[{"data":1,"prerenderedAt":6340},["ShallowReactive",2],{"tv-globals-deep":3,"tv-shows":33,"tv-episodes":121},{"og":4,"featured":5},"8e09c925-ad12-455c-b4fc-f05893034b5c",{"id":6,"slug":7,"vimeo_id":8,"description":9,"tile":10,"length":11,"resources":12,"people":12,"episode_number":13,"published":14,"title":15,"video_transcript_html":16,"video_transcript_text":17,"content":12,"seo":18,"status":19,"episode_people":20,"recommendations":22,"season":23},"637aafa2-b323-4ad0-adf0-ba52328bb798","data-management","1007701870","Discover how Directus empowers you to manage data, create collections, customize fields, and streamline content workflows with features like Explore, Editor, and multilingual support.","367c893f-d7d3-43d7-bd5e-233d30dc9a92",8,null,1,"2024-09-09","Data Management","\u003Cp>Speaker 0: Directus can be used to manage both the data in your collections and the collection data models themselves, which includes setting up new collections, fields, and defining the relationships between them. Any changes made directly in your database will be recognized and reflected by directors. While collections in a directors project map to your database tables, they are not directly equivalent. Collections are your tables, plus the additional metadata required for Directus to operate correctly. There are 2 types of collections.\u003C\u002Fp>\n\u003Cp>All Directus specific settings and data are held in system collections created and managed by Directus and never in your existing database tables. This includes user related data, including sessions, data around our automation features, activity and revision logging, and more. As an aside, it means that you can effectively remove Directus if desired with no Directus specific data left behind. We believe it's important to put you in control of your data. And while we hope you'll use directors for months years to come, you can jump out at any time.\u003C\u002Fp>\n\u003Cp>To stop running the application, delete the tables that start with directors underscore, and it's like we were never there. User collections are created directly in your database or via directors. They describe your project's data models. Let's talk about what goes into a collection. Each collection has a unique name and strategy for creating a primary key for each item, which includes an auto incrementing number, a UUID, or a manually entered string.\u003C\u002Fp>\n\u003Cp>At the time of creating a collection, directors can also set up some common optional fields, including status, thought, created date, created by, and so on. Next is fields. Like collections are to tables, directors fields map to fields in your database, but contain additional metadata about how they are shown in the editor within layout and in relational fields. When creating a field, the first thing to choose is the interface, which you can think of as the form input type. Directus has many built in interfaces, including a rich text editor, a map, a file picker, and more.\u003C\u002Fp>\n\u003Cp>And you can download additional ones from the Directus marketplace. Directus also supports all standard relationship types, as well as a few more of its own compound types, which are custom tailored to make common but complex tasks easier. When creating a many to many or what we call a many to any builder relationship, directors will create the junction collection for you automatically. There are a whole set of display and validation options available for each field, which you can then configure directly from the data model settings. A quick aside that Director supports bringing an existing database, there are some first time setup needed in the data model settings to configure them with the additional details needed to turn them into directors collections.\u003C\u002Fp>\n\u003Cp>This is all done in the Data Studio data model settings and is similar to when you create new collections. Directus Explore allows you to display and query items in a collection. Data is represented as it is stored in your database with display templates allowing custom representation with inline text, image thumbnails, dates, numbers, or anything else. Data can be displayed in any of the default or installed layouts, each with their own configuration options. Layouts include a table, Kanban board, gallery, calendar, and map, with more available in the Directus marketplace.\u003C\u002Fp>\n\u003Cp>Now, at the top of layouts, Directus Explore has a search, which uses all top level string, text, number, and UUID fields, but also has a very powerful filter interface. You can add single filters or multiple using the and and all groups, which can then be nested. You can use the provided variables to refer to the current user or roles. Once you have everything set up the way you like, you can create a preset. Presets are saved views that contain a search, filters, sorts, visible fields, and layout information.\u003C\u002Fp>\n\u003Cp>They can be saved for all users or specific roles or users. This allows you to create views that are more relevant to your different teams and stakeholders. Director's editor allows you to display, edit, and preview items in your collections. This page also includes options for archiving, reverting, versioning, and commenting on an item. In the editor, the field settings from when you configure the data model are applied.\u003C\u002Fp>\n\u003Cp>This includes their chosen interfaces, position, and size, validations, conditional visibility, and more. There is a live preview that can show your changes in your real application as you save them. So you know what it will look like once you publish or rebuild your site. To use live preview, you need to have correctly configured your external application, but we have guides in our docs to help you with this. Content versioning allows teams to create and manage different versions of their content.\u003C\u002Fp>\n\u003Cp>There are several reasons to use content versioning, including drafting content without publishing it and more ways to collaborate with others. At any point, a content version can be created and worked on in isolation. Then when ready, a content version can be promoted to become the new main version. We have a UI to select which edited field should be promoted, if not all of them. And all changes made on an item are stored in the revision history along with who made them, which is great for both accountability and also reverting changes if needed.\u003C\u002Fp>\n\u003Cp>Directus has a lot of flexibility around translations, but it's important to note that there are 2 distinct types of translation. 1, within the data studio web application that serves your team, and 2, in the content and data that you actually author. Many parts of the Director's Data Studio are already translated into over 30 languages. This includes all UI elements and descriptions. We support community contributions to both increase the coverage for existing languages and to support new ones.\u003C\u002Fp>\n\u003Cp>When creating collections and fields, you can set translations of names, which will be reflected if the user changes their language Data Studio. And in any other field across Directus, for example, in field descriptions, you can utilize translation strings, which can be set up in your project settings. For content translations, Directus has a dedicated translations interface to configure and author multilingual content. And that's a very quick summary of data management inside of Directus. If you have any questions, please feel free to reach out, and we'll see you in the next video.\u003C\u002Fp>","Directors can be used to manage both the data in your collections and the collection data models themselves, which includes setting up new collections, fields, and defining the relationships between them. Any changes made directly in your database will be recognized and reflected by directors. While collections in a directors project map to your database tables, they are not directly equivalent. Collections are your tables, plus the additional metadata required for Directus to operate correctly. There are 2 types of collections. All Directus specific settings and data are held in system collections created and managed by Directus and never in your existing database tables. This includes user related data, including sessions, data around our automation features, activity and revision logging, and more. As an aside, it means that you can effectively remove Directus if desired with no Directus specific data left behind. We believe it's important to put you in control of your data. And while we hope you'll use directors for months years to come, you can jump out at any time. To stop running the application, delete the tables that start with directors underscore, and it's like we were never there. User collections are created directly in your database or via directors. They describe your project's data models. Let's talk about what goes into a collection. Each collection has a unique name and strategy for creating a primary key for each item, which includes an auto incrementing number, a UUID, or a manually entered string. At the time of creating a collection, directors can also set up some common optional fields, including status, thought, created date, created by, and so on. Next is fields. Like collections are to tables, directors fields map to fields in your database, but contain additional metadata about how they are shown in the editor within layout and in relational fields. When creating a field, the first thing to choose is the interface, which you can think of as the form input type. Directus has many built in interfaces, including a rich text editor, a map, a file picker, and more. And you can download additional ones from the Directus marketplace. Directus also supports all standard relationship types, as well as a few more of its own compound types, which are custom tailored to make common but complex tasks easier. When creating a many to many or what we call a many to any builder relationship, directors will create the junction collection for you automatically. There are a whole set of display and validation options available for each field, which you can then configure directly from the data model settings. A quick aside that Director supports bringing an existing database, there are some first time setup needed in the data model settings to configure them with the additional details needed to turn them into directors collections. This is all done in the Data Studio data model settings and is similar to when you create new collections. Directus Explore allows you to display and query items in a collection. Data is represented as it is stored in your database with display templates allowing custom representation with inline text, image thumbnails, dates, numbers, or anything else. Data can be displayed in any of the default or installed layouts, each with their own configuration options. Layouts include a table, Kanban board, gallery, calendar, and map, with more available in the Directus marketplace. Now, at the top of layouts, Directus Explore has a search, which uses all top level string, text, number, and UUID fields, but also has a very powerful filter interface. You can add single filters or multiple using the and and all groups, which can then be nested. You can use the provided variables to refer to the current user or roles. Once you have everything set up the way you like, you can create a preset. Presets are saved views that contain a search, filters, sorts, visible fields, and layout information. They can be saved for all users or specific roles or users. This allows you to create views that are more relevant to your different teams and stakeholders. Director's editor allows you to display, edit, and preview items in your collections. This page also includes options for archiving, reverting, versioning, and commenting on an item. In the editor, the field settings from when you configure the data model are applied. This includes their chosen interfaces, position, and size, validations, conditional visibility, and more. There is a live preview that can show your changes in your real application as you save them. So you know what it will look like once you publish or rebuild your site. To use live preview, you need to have correctly configured your external application, but we have guides in our docs to help you with this. Content versioning allows teams to create and manage different versions of their content. There are several reasons to use content versioning, including drafting content without publishing it and more ways to collaborate with others. At any point, a content version can be created and worked on in isolation. Then when ready, a content version can be promoted to become the new main version. We have a UI to select which edited field should be promoted, if not all of them. And all changes made on an item are stored in the revision history along with who made them, which is great for both accountability and also reverting changes if needed. Directus has a lot of flexibility around translations, but it's important to note that there are 2 distinct types of translation. 1, within the data studio web application that serves your team, and 2, in the content and data that you actually author. Many parts of the Director's Data Studio are already translated into over 30 languages. This includes all UI elements and descriptions. We support community contributions to both increase the coverage for existing languages and to support new ones. When creating collections and fields, you can set translations of names, which will be reflected if the user changes their language Data Studio. And in any other field across Directus, for example, in field descriptions, you can utilize translation strings, which can be set up in your project settings. For content translations, Directus has a dedicated translations interface to configure and author multilingual content. And that's a very quick summary of data management inside of Directus. If you have any questions, please feel free to reach out, and we'll see you in the next video.","958be259-c514-4549-9588-22a140f23427","published",[21],"d180947c-749e-4607-83c9-88b8a59ab770",[],{"show":24},{"id":25,"title":26,"logo":27,"cover":28,"tile":29,"announcement_text":12,"description":30,"slug":31,"one_liner":32,"card_text":12,"status":19,"sort":12},"3a4b9e97-449f-4e24-b607-9ad1baee2441","Directus Academy","82d9be2b-1154-40b5-a118-a5e615b64429","6eca7da2-0dbb-4a2d-a96f-6a9c2a614cce","bd5e0d7c-fd4f-4225-af71-dedb154be371","In this series, we will introduce you to many parts of the Directus platform. We won't go into a huge amount of depth on each tool, but will ensure that you understand what it is capable of, when you can use it in your projects, and know where to look for more information.","directus-academy","Learn about each of Directus' building blocks in this tour of the platform.",[34,36,38,40,42,44,46,48,49,51,53,55,57,59,61,63,65,67,69,71,73,75,77,79,81,83,85,87,89,91,93,95,97,99,101,103,105,107,109,111,113,115,117,119],{"id":35},"0589509a-8e0a-4640-a3ca-1e6604b3e5f3",{"id":37},"100ece4f-6a6b-4e33-b6b3-114924af66f5",{"id":39},"120b0b36-6171-451a-9fe6-92fe0ea666cd",{"id":41},"1e9d2e3a-7688-4615-a4c9-457bc1982d1b",{"id":43},"24008997-801f-48ef-9c21-30a478419242",{"id":45},"325db7c3-f62a-484d-883e-b49f08738e2b",{"id":47},"3961133b-e16b-4351-903e-0d8d6ac0ff4c",{"id":25},{"id":50},"3e7a0899-2219-4b9d-98fd-ee1a1b73a8be",{"id":52},"541d8c8c-142d-4915-bd42-8aa9b0aaec34",{"id":54},"563b467f-a99a-49e3-8612-ca25d6829182",{"id":56},"5938933b-ddff-4283-ae4c-e0460e85112a",{"id":58},"7f15853d-146d-4bc2-8be4-2ff4b5f0ac0c",{"id":60},"8056fe95-ffe5-47d3-b325-efc637ae00b1",{"id":62},"80b58974-59af-4108-870e-6bef37e45ab0",{"id":64},"8422d0cf-3adb-4dc6-93c6-0e6fafebac43",{"id":66},"8cf3e543-24db-4bc7-b6bb-f8b988528405",{"id":68},"8cf6395d-10fb-4df9-9e47-67d1ee4549eb",{"id":70},"91136fb9-7a6e-48cd-a789-e824eeb01cac",{"id":72},"94965a81-5c52-4059-95a7-5e9b9e467e5b",{"id":74},"9530c399-7261-4a1a-94bf-7c71969795fe",{"id":76},"95b9e28b-c72e-473c-a99c-41f83f4a62ff",{"id":78},"9f10d27e-0df8-4574-a0c8-34bbd33f0943",{"id":80},"9f848853-05b6-463e-96de-3fef244613b5",{"id":82},"a4a62fc4-fdc7-47ae-8b47-190abc5cfde9",{"id":84},"aa913777-6d1e-4cb5-9cd4-1a3111576fa5",{"id":86},"b3b0f87f-8572-4940-b611-a58fd2423a65",{"id":88},"ba555b5a-0190-4595-bb73-bad34dc78a21",{"id":90},"bdee500e-382c-4eeb-bf5b-1c4d3c39d35a",{"id":92},"bdefc4e8-45fb-4cdd-a0e8-1fd4ac654bb4",{"id":94},"c248c0ef-2c20-4465-a2fb-54a805f13ee1",{"id":96},"c266c576-a3d2-4e32-9411-3dfe86bb3a5e",{"id":98},"c5fdfb27-4949-4b06-85b3-0020a06ca3a8",{"id":100},"c688c2d8-afc0-4175-864c-1c31c1338ccd",{"id":102},"d3f716a9-466c-4f77-a747-b4c4b0f591df",{"id":104},"d769481b-7993-41cc-bf28-13b7f631455c",{"id":106},"d9e33574-a24f-4f18-b7f5-088b50a63010",{"id":108},"dbc9f017-73c1-41d8-92e1-0c282b4aef71",{"id":110},"dcc3b569-c1c2-4aa0-a011-476cca3c24bc",{"id":112},"e076c58d-81ee-461f-a67e-7b6374953292",{"id":114},"e47319b6-40fb-405f-bb35-dd8856dabcd8",{"id":116},"ecbc473b-0b19-4770-9f31-144d5a183e1e",{"id":118},"f592985a-aadd-4573-94da-7778b758d78a",{"id":120},"fb9d1e83-52ea-4ab2-9ef9-23309f9b955d",[122,153,171,189,207,225,242,258,276,299,324,342,359,385,414,429,445,460,475,492,506,522,537,552,567,583,600,615,638,653,668,698,718,735,753,780,795,810,825,842,857,872,885,904,924,947,962,988,1008,1029,1045,1073,1089,1105,1120,1136,1151,1167,1182,1197,1212,1230,1248,1271,1286,1301,1316,1335,1359,1374,1389,1404,1419,1444,1458,1473,1488,1503,1518,1544,1564,1586,1625,1646,1667,1688,1710,1729,1748,1767,1804,1824,1851,1877,1892,1907,1922,1937,1955,1970,1985,2000,2015,2021,2037,2057,2079,2097,2118,2134,2163,2179,2195,2211,2227,2243,2259,2275,2297,2323,2345,2365,2398,2416,2441,2457,2475,2499,2517,2535,2561,2576,2591,2606,2621,2643,2658,2673,2691,2714,2730,2762,2779,2797,2813,2831,2847,2863,2881,2901,2917,2933,2948,2964,2979,2995,3025,3040,3055,3070,3085,3100,3120,3135,3150,3165,3180,3197,3213,3228,3244,3261,3285,3300,3316,3349,3382,3395,3411,3426,3443,3457,3470,3489,3504,3520,3540,3554,3567,3582,3608,3630,3643,3656,3675,3690,3707,3727,3741,3753,3771,3788,3812,3826,3839,3858,3875,3892,3912,3926,3939,3963,4000,4032,4049,4074,4088,4104,4117,4136,4175,4191,4220,4236,4252,4265,4289,4305,4326,4353,4373,4389,4405,4421,4439,4457,4473,4489,4514,4546,4563,4589,4615,4639,4656,4688,4705,4722,4738,4761,4778,4807,4831,4847,4860,4885,4902,4919,4943,4957,4970,4989,5006,5027,5050,5064,5077,5110,5127,5144,5168,5182,5195,5216,5233,5254,5277,5291,5304,5321,5342,5356,5369,5394,5411,5436,5458,5475,5491,5507,5518,5544,5557,5570,5594,5614,5627,5644,5661,5690,5716,5736,5755,5779,5799,5815,5831,5847,5866,5883,5905,5922,5941,5959,5981,5998,6015,6033,6054,6071,6088,6110,6137,6163,6195,6214,6232,6250,6268,6286,6304,6322],{"id":123,"slug":124,"vimeo_id":125,"description":126,"tile":127,"length":128,"resources":12,"people":12,"episode_number":129,"published":130,"title":131,"video_transcript_html":132,"video_transcript_text":133,"content":12,"seo":134,"status":19,"episode_people":135,"recommendations":137,"season":138},"37e28ea2-bec3-40bd-8b09-b9fbb47c1759","introducing-builders","1177392929","A new program for the community helping to push Directus forward.\n\n","3ac7b48f-2f4a-4f6c-b1d2-038893bc61e3",21,7,"2026-03-27","Introducing: Builders","\u003Cp>Speaker 0: Hello everyone. Welcome to the builder segment as part of Leap Week. I'm Beth. I run community ops here at Directus, and I have the absolute pleasure of talking to you about a brand new community program we launched in the last month called Directus Builders. Builders is a community champion program for people who use Directus, want to share what they're building, and contribute to the community.\u003C\u002Fp>\u003Cp>Whether you're interested in sharing technical insights and receiving amplification from our social channels, joining a network of other DirectUs users, or getting our support for your own community initiatives, this program is for you if you are using directors to build. By joining, you'll enter a private community with other experience builders and our team. It's open to contributors, customers, partners, users, really anyone who uses Directus to build something useful. You don't need to be building something huge, you just need to be building something real. If you're the kind of person who likes helping others figure things out, sharing what you've been learning, or creating something cool, we want to hear from you.\u003C\u002Fp>\u003Cp>Hopefully, that gives you some indication about the types of activities you can be doing as part of directors builders. We've already seen so much enthusiasm from the new joiners, so thank you so much. We have participants from over 13 different countries which is super exciting to see and that is only growing. Next up we have some of our new builders to showcase what they are working on.\u003C\u002Fp>\u003Cp>Speaker 1: Hi. My name is Craig Harmon, and I'm from Perth, Western Australia. I'm a developer, and I've been using for about, five or six years now. And I wanted to show you a project I've been working on, called Nuxtus. Now Nuxtus started life as a boilerplate just for myself to get up and running very quickly with Directus and Nuxt.\u003C\u002Fp>\u003Cp>But it's kind of grown bigger than that now, and it can do automate a lot of processes, for me to get up and running really quickly, with a front end in Nuxt being driven by Directus. So I wanted to show it to you very quickly today. So I'm just on the Nuxt website here. We can see it's very easy to get going. There's an m p x create command.\u003C\u002Fp>\u003Cp>So if we run that in the terminal, what's going to happen now is we're going to run through the standard director setup. So we'll just use local host, give a password. We'll use an SQLite database so we can get up and running nice and quickly. Quickly. And now what's gonna happen is the installer is gonna go away, download and set up Directus just like it normally would if you installed Directus yourself manually.\u003C\u002Fp>\u003Cp>But it's also going to create a client project, with Nuxt and Tailwind and the Directus SDK setup. But above and beyond that, it's also got some other little, packages, which will help us when working specifically with Directus and Nuxt. If you don't wanna use the whole of Nuxtus, you don't have to. Each of the packages are available separately. If you go to the nuxtus.com website and then go to the GitHub page, you'll see that each individual packages can be downloaded and used in your own projects without having to use Nuxtus.\u003C\u002Fp>\u003Cp>But Nuxtus is just a nice, easy, quick way to get a new project up and running. So we just wait for Directus to finish setting up here. Perfect. So we can see now that Directus and Nuxt are all set up. So if we go into our project directory, and if we run npm start, we'll see our front end Nuxt website is ready to go.\u003C\u002Fp>\u003Cp>There we go. Also, what's happened is we've got our Directus set up. If we log in to Directus, this is all now good to go. Now you could stop here. You can start writing your manual code if you wanted to, But Nuxtus also has some other really, nice features that you might wanna take, advantage of.\u003C\u002Fp>\u003Cp>So let's create a new collection in Directus. And let's say, just for the sake of a demo, we're gonna create a blog. Let's use all of the optional fields, and we'll also create a blog title and a main content area for the blog. Great. So we've added them in.\u003C\u002Fp>\u003Cp>That's all ready to go. One more more thing we're gonna do because we want to make this blog available on the front end is we're also gonna need to go to, user settings and let's make the blog publicly readable. And now that we've done that, now now this first demo is not going to be too exciting, but we'll show where we can go from there. So I haven't written any code yet, but if I go to blog, it's going to tell me that I've got no blog articles found because Nuxtus has picked up that I've created a collection called blog, but I haven't put any data in there yet. Now if I go back to my blog content and create an item, This is some content.\u003C\u002Fp>\u003Cp>We've now got some content. Let's go back to our blog and refresh. We're now given the index number of that blog. And if we click it, we get the blog collection name and all of the data about that blog. Okay.\u003C\u002Fp>\u003Cp>Now that's not overly useful, but let's see what's happening in the background. If we bring up our editor. So in server is all of our direct us package. You can see there's a direct us extension here, but we can largely leave that alone. Let's have a look at the client because that's where things get interesting.\u003C\u002Fp>\u003Cp>So we've got a standard Nuxt configuration. We've got Tailwind installed. But if we go into pages, automatically, Nuxtus has created this blog folder with two files in it. The index, which is the list of all the blog articles and also a details page, which at the moment is just echoing out a blog item. But it's more than that.\u003C\u002Fp>\u003Cp>This blog that we've created is now a fully typed object. So it's getting its type information from the director schema. So we can go down here and say, well, we don't want to display the ID of the blog to, list our blog items. We wanna show the title and that's fully typed in TypeScript already. So we save that.\u003C\u002Fp>\u003Cp>We'll see straight away in the background here. We're now using the title to list our blog items and we can go through again. And there's our our blog article that we created. Likewise, in the blog article itself, we don't want to show just the whole object. We want to show the title and then we want to have another div where we show the content.\u003C\u002Fp>\u003Cp>And again, blog is fully typed, so we know what we can get pull out from the object. And there we go. There's our change. So we've now got the name of the collection, the name of the blog article that we're looking at and the content of it and our routing all set up for us with doing very minimal code. So if you're interested in having a look at Nuxtus, please do go to the website, nuxtus.com, and, feel free to have it installed and let me know how you go.\u003C\u002Fp>\u003Cp>Thank you very much.\u003C\u002Fp>\u003Cp>Speaker 2: Today, I would like to show you how to supercharge AI batch processing using Directus MCP with a self learning note taking workflow. Batch processing is really effective for, for situations where you need raw speed, but it kinda breaks down when you need to make creative decisions on individual items and on the batch as a whole. You can't really do that without a human element. AI models, I found, are great for those sorts of tasks, but they break down when you give them a whole bunch of data. And they also struggle with finding the right information to pull in to make those creative decisions.\u003C\u002Fp>\u003Cp>So the solution I found is to teach AI to take notes. By implementing a workflow that lets Directus MCP take notes directly on whatever it's working on, we gain the ability to do things like long running task tracking because the model can pick up where it left off and and, start fresh with a new context window. And we also get things like self improving database access. You take a note on all the different things that you ran into difficulty with and what you did to solve them and how it worked out. And it speeds up future runs because it can read that note and go, okay.\u003C\u002Fp>\u003Cp>So don't do this. Do this, and I'll be able to just continue with the task I was working on. And then by letting it take notes, we can consolidate a whole bunch of data that was stored in the database. Say, for example, you wanted to pull out all of the aliases for your articles that are related to rabbits and you also want the ID for each one. Well, well, you can just dump that in a note and analyze it in future runs.\u003C\u002Fp>\u003Cp>So in our case, Directus is the knowledge backbone. Every note is just a key value pair. I'll go ahead and show you the notes table here. It's a very simple setup. We have a key, which is just a string, and this is what the AI model uses to kind of categorize what the different notes are about.\u003C\u002Fp>\u003Cp>And we have the value, and that's just a big markdown, field. It's a very simple setup, but it's surprisingly powerful. By using semantic keys and explicit instructions on how to record and review notes, we give the AI models a very quick and effective way to find the context they need. So this database has a problem. We have a whole bunch of articles.\u003C\u002Fp>\u003Cp>We have a 124 articles. The titles don't really make any sense. Oh, the bodies are in Latin. And I I can't work with this. So I've created a prompt for Claude to read the article titles and come up with a concept proposal for each one.\u003C\u002Fp>\u003Cp>What could this article actually be about? So let's look at the system prompt here. You can see that there's two things that it's informing the AI model about. That there's an AI notes table in the rough structure of that table and that it needs to read and record its database insights, especially how it solved problems it ran into and just update that note on every run. This gives it the self learning ability we were talking about earlier.\u003C\u002Fp>\u003Cp>It's able to figure out what it ran into last time and fix it on the next run. Now back to the task. I've told the AI model it's a skilled content analyst and writer. We have a huge collection of articles. All the bodies are full of nonsense.\u003C\u002Fp>\u003Cp>All that. Alright. So its job is to record the concepts that it comes up with in an article concepts note. It's not sure what to do with an article based on the title. Be creative.\u003C\u002Fp>\u003Cp>Find a wacky concept. Then we have some very specific instructions. We say always read the template and template article concepts, this is a note, to determine how to structure your output. Always create a new article concepts note for each run. Always give it the exact name.\u003C\u002Fp>\u003Cp>Always process 30 articles. Always review the last note you created before starting to make sure you don't duplicate any work. This template lets us make sure that we get consistent output on every run. Alright. Let's go ahead and run this a few times and see what its output looks like.\u003C\u002Fp>\u003Cp>I turn on auto refresh, and then I'm gonna go into Claude, add a prompt from Directus, and examine articles and propose concepts. If you want to know how to use this feature, go ahead and take a look at the Directus MCP documentation, and then we're just gonna send it. Now one of the fun things you can do with read and write access direct to Directus is if the model makes a mistake, you can ask it to update the prompt to fix its mistake in future runs. Article concepts. It has identified directly that just about every article appears to be lorem ipsum gibberish and occasional test content.\u003C\u002Fp>\u003Cp>Alright. So came up with a bunch of different articles on a bunch of different ideas. In a bit, we're gonna use this to actually write all these different article concepts. But for now, I just wanna keep going through the batch process to show you how it's able to pick up where it left off and continue. On this run, if you notice, it actually picked up that it needed to look up the translations and didn't have to figure that out from the schema this time.\u003C\u002Fp>\u003Cp>Alright, it is a new day. My cloud usage limits have reset, and we are ready to continue with step two, which is generating the articles based on the concepts that we've put together. All of the, article concepts have now been saved in these, concept notes. They take the article title. They try to figure out what on earth, the article should be about and propose a concept.\u003C\u002Fp>\u003Cp>So now we have the next step of the process, which is to generate a actual article for each of the articles we generate concepts on in both English and German. We're gonna go in batches of 10 and see how well that works. We'll take this prompt and it should automatically read over all of our article concept notes and start filling in articles for those. Let's go. So we're gonna use the turn concepts into full fledged articles prompt.\u003C\u002Fp>\u003Cp>That's gonna go and read through all of our article concept notes and start filling in those articles, and I'll show you those articles as it writes them. I have no idea if these articles are gonna make any sense. This is gonna be fun. I'm having fun reading through these articles. I have no idea how helpful any of this is, but it at least sounds very convincing.\u003C\u002Fp>\u003Cp>Now the key advantage to this approach is that if we were generating concepts and writing articles at the same time, we'd have to use much smaller batches. But because we split the process into two steps, we've saved a ton of context window, and we're able to work in larger batches. And as a bonus, we can do multiple things with those article summaries, while we're working on generating those articles from those summaries. Now this isn't the most efficient workflow for generating articles. There's way better workflows for that.\u003C\u002Fp>\u003Cp>We're just demonstrating this concept. But a nice thing about this approach is that you can perform multiple tasks at the same time. For example, while we were working on these articles, we could also be working on something completely different using the notes that we're generating the articles from. I have this prompt here, categorize article concepts, which will allow Claude to suggest an article taxonomy based on the summaries it's already generated. It'll just read all the article concept notes and create a new note suggesting that new taxonomy.\u003C\u002Fp>\u003Cp>I like the way it describes these articles as a fascinating collection of technical and business focused article concepts with creative jargon filled titles that have been transformed into practical valuable content ideas. I'm not quite sure I'd be so positive about it, but Nate gets the idea across. Alright. Let's take a look at the categories that it generated and the opportunities that identified. So here's some content gaps, AI and ethics, sustainable technology operations, human centered digital transformation, all sorts of other different things.\u003C\u002Fp>\u003Cp>And here are some of the categories it's come up with. System architecture, it's put a bunch of different articles in that category, ecommerce, digital business, data management and analytics, user experience and interface design, business operations or business optimization management, automation and AI systems, project management and collaboration. So it's basically taken all of those articles that we put together and categorized them according to common themes in those articles. This could be really useful if you have an existing content collection that you're trying to build a new categorization system for. And what's great is you could just take this and create another prompt to actually apply that taxonomy to articles, and create categories and all sorts of different things for that in Directus.\u003C\u002Fp>\u003Cp>So by the end of this run, here's what the AI model has built. Concept proposals for every article in our database, full body content for many of those articles in English and German, a whole new content categorization system for the articles it wrote, progress logs so that it doesn't lose its place, and database access hints that documents how the challenges were solved so that on future runs, it could do even better. Instead of treating AI like a disposable worker or one that can infinitely stuff and stuff and stuff and work on something until it gets all confused, We're treating it like a teammate that tracks and follows up on what it's done across multiple days, multiple runs, starting with a clean slate on each task and only pulling in the information that it needs. We turned a human in the loop batch processing task into an AI creative workflow by splitting it into discrete steps and letting it kind of pick up where it left off. We gave the model a way to track progress.\u003C\u002Fp>\u003Cp>We built templates to make the results more consistent and reusable, and we recorded information about the database to help the AI model get smarter over time. I've had a ton of fun nailing down this workflow and testing it on all sorts of tasks. For my work, we're actually using it to do things similar to this where we're analyzing a huge amount of content and trying to figure out how to organize and categorize and and do all sorts of things, and it's it's doing really well. The ability to take notes, record what it's done, and just kind of start over and create its own context has been incredibly helpful with turning a workflow that works really well for one or two articles into something that works well across thousands. So So thank you for your time.\u003C\u002Fp>\u003Cp>I really look forward to seeing what everyone does with Directus and Directus MCP going forward, especially as Directus MCP evolves, AI models get more capable, and just the overall core gets stronger. The future is gonna be fun.\u003C\u002Fp>\u003Cp>Speaker 3: Hey. I'm Matt from Morley. We're a directors partner agency based in The UK. And today, I want to give you a quick look at what we've been building, both for our clients and for the directors community. So one of the things we built for directors that we're most proud of is the NUXT directors module.\u003C\u002Fp>\u003Cp>It's an open source NUXT module that wraps the official directors SDK and gives you proper first class integration with NUXT. The idea is simple, you add your module, point it at your Directus instance and that's it, you're up and running. You get session based authentication with cross domain support out of the box, automatic type generation from your directors collections, type safe websockets, Nux images automatically configured for your directors assets, and we even embed the directors admin panel right into the Nux dev tools. So let me show you how to get up and running with that. So I'll just open up Visual Studio Code.\u003C\u002Fp>\u003Cp>The first thing you need to do is go into your Nux config, add the Nux Direct SDK, install it in whatever package manager you like, then go to your environment variable, add your direct to your URL, and if you want to use things like, admin server endpoints or automatic type generation then just add in a Nux, a direct to admin token as well and that will be automatically handled for you. Don't worry nothing will be passed to the front end it's all completely safe. And that's it, when you start the server you get access to everything you'd expect from your Direct SDK automatically imported. So as you can see here on my blog page I've got the directors all I do is call use directors and then things like read items automatically imported and it's fully type safe. You can see all of my collections.\u003C\u002Fp>\u003Cp>I get errors if it's a if it doesn't actually exist in the director's schema and I can do things that you'd expect. Fully typed filtering, fully typed nested fields, and again I will get errors if something doesn't exist, and fully typed sort. That's it. It's up, it's running. You can do, there's even help methods for things like getting your file URLs, where you can pass in options around your thumbnails, whether you want to download it, what you want to call it, all that is handled automatically for the NUXT Directus SDK.\u003C\u002Fp>\u003Cp>We built this because we kept solving the same problems across client projects and rather than copying and pasting boilerplate we packaged it up properly and made available on NPM. It's MIT licensed and we're just about to release version five which targets Nux four and the latest Direct SDK. Here's some of the work that we built using Directus and the Nux Directus SDK. So that's a quick snapshot of what we're building at Rolla. If you're building anything with Nux and Directus, feel free to give the Nux Directus SDK a look.\u003C\u002Fp>\u003Cp>Otherwise, if you want to chat about anything Nux, directors or even potentially a future project please do reach out at rolli. Io. Thank you very much.\u003C\u002Fp>\u003Cp>Speaker 0: Thank Thank you so much for the showcases and to everyone who's already joined and showed enthusiasm. We are really excited about bringing the community together in this way and to see all the possibilities that can come out of it and, yeah, hopefully, we have done enough to convince you to join. That's it from me. If you have any questions, do head over to the community forum at community.directors.io and have a great rest of leap week, everyone. See you soon.\u003C\u002Fp>\u003Cp>Bye.\u003C\u002Fp>","Hello everyone. Welcome to the builder segment as part of Leap Week. I'm Beth. I run community ops here at Directus, and I have the absolute pleasure of talking to you about a brand new community program we launched in the last month called Directus Builders. Builders is a community champion program for people who use Directus, want to share what they're building, and contribute to the community. Whether you're interested in sharing technical insights and receiving amplification from our social channels, joining a network of other DirectUs users, or getting our support for your own community initiatives, this program is for you if you are using directors to build. By joining, you'll enter a private community with other experience builders and our team. It's open to contributors, customers, partners, users, really anyone who uses Directus to build something useful. You don't need to be building something huge, you just need to be building something real. If you're the kind of person who likes helping others figure things out, sharing what you've been learning, or creating something cool, we want to hear from you. Hopefully, that gives you some indication about the types of activities you can be doing as part of directors builders. We've already seen so much enthusiasm from the new joiners, so thank you so much. We have participants from over 13 different countries which is super exciting to see and that is only growing. Next up we have some of our new builders to showcase what they are working on. Hi. My name is Craig Harmon, and I'm from Perth, Western Australia. I'm a developer, and I've been using for about, five or six years now. And I wanted to show you a project I've been working on, called Nuxtus. Now Nuxtus started life as a boilerplate just for myself to get up and running very quickly with Directus and Nuxt. But it's kind of grown bigger than that now, and it can do automate a lot of processes, for me to get up and running really quickly, with a front end in Nuxt being driven by Directus. So I wanted to show it to you very quickly today. So I'm just on the Nuxt website here. We can see it's very easy to get going. There's an m p x create command. So if we run that in the terminal, what's going to happen now is we're going to run through the standard director setup. So we'll just use local host, give a password. We'll use an SQLite database so we can get up and running nice and quickly. Quickly. And now what's gonna happen is the installer is gonna go away, download and set up Directus just like it normally would if you installed Directus yourself manually. But it's also going to create a client project, with Nuxt and Tailwind and the Directus SDK setup. But above and beyond that, it's also got some other little, packages, which will help us when working specifically with Directus and Nuxt. If you don't wanna use the whole of Nuxtus, you don't have to. Each of the packages are available separately. If you go to the nuxtus.com website and then go to the GitHub page, you'll see that each individual packages can be downloaded and used in your own projects without having to use Nuxtus. But Nuxtus is just a nice, easy, quick way to get a new project up and running. So we just wait for Directus to finish setting up here. Perfect. So we can see now that Directus and Nuxt are all set up. So if we go into our project directory, and if we run npm start, we'll see our front end Nuxt website is ready to go. There we go. Also, what's happened is we've got our Directus set up. If we log in to Directus, this is all now good to go. Now you could stop here. You can start writing your manual code if you wanted to, But Nuxtus also has some other really, nice features that you might wanna take, advantage of. So let's create a new collection in Directus. And let's say, just for the sake of a demo, we're gonna create a blog. Let's use all of the optional fields, and we'll also create a blog title and a main content area for the blog. Great. So we've added them in. That's all ready to go. One more more thing we're gonna do because we want to make this blog available on the front end is we're also gonna need to go to, user settings and let's make the blog publicly readable. And now that we've done that, now now this first demo is not going to be too exciting, but we'll show where we can go from there. So I haven't written any code yet, but if I go to blog, it's going to tell me that I've got no blog articles found because Nuxtus has picked up that I've created a collection called blog, but I haven't put any data in there yet. Now if I go back to my blog content and create an item, This is some content. We've now got some content. Let's go back to our blog and refresh. We're now given the index number of that blog. And if we click it, we get the blog collection name and all of the data about that blog. Okay. Now that's not overly useful, but let's see what's happening in the background. If we bring up our editor. So in server is all of our direct us package. You can see there's a direct us extension here, but we can largely leave that alone. Let's have a look at the client because that's where things get interesting. So we've got a standard Nuxt configuration. We've got Tailwind installed. But if we go into pages, automatically, Nuxtus has created this blog folder with two files in it. The index, which is the list of all the blog articles and also a details page, which at the moment is just echoing out a blog item. But it's more than that. This blog that we've created is now a fully typed object. So it's getting its type information from the director schema. So we can go down here and say, well, we don't want to display the ID of the blog to, list our blog items. We wanna show the title and that's fully typed in TypeScript already. So we save that. We'll see straight away in the background here. We're now using the title to list our blog items and we can go through again. And there's our our blog article that we created. Likewise, in the blog article itself, we don't want to show just the whole object. We want to show the title and then we want to have another div where we show the content. And again, blog is fully typed, so we know what we can get pull out from the object. And there we go. There's our change. So we've now got the name of the collection, the name of the blog article that we're looking at and the content of it and our routing all set up for us with doing very minimal code. So if you're interested in having a look at Nuxtus, please do go to the website, nuxtus.com, and, feel free to have it installed and let me know how you go. Thank you very much. Today, I would like to show you how to supercharge AI batch processing using Directus MCP with a self learning note taking workflow. Batch processing is really effective for, for situations where you need raw speed, but it kinda breaks down when you need to make creative decisions on individual items and on the batch as a whole. You can't really do that without a human element. AI models, I found, are great for those sorts of tasks, but they break down when you give them a whole bunch of data. And they also struggle with finding the right information to pull in to make those creative decisions. So the solution I found is to teach AI to take notes. By implementing a workflow that lets Directus MCP take notes directly on whatever it's working on, we gain the ability to do things like long running task tracking because the model can pick up where it left off and and, start fresh with a new context window. And we also get things like self improving database access. You take a note on all the different things that you ran into difficulty with and what you did to solve them and how it worked out. And it speeds up future runs because it can read that note and go, okay. So don't do this. Do this, and I'll be able to just continue with the task I was working on. And then by letting it take notes, we can consolidate a whole bunch of data that was stored in the database. Say, for example, you wanted to pull out all of the aliases for your articles that are related to rabbits and you also want the ID for each one. Well, well, you can just dump that in a note and analyze it in future runs. So in our case, Directus is the knowledge backbone. Every note is just a key value pair. I'll go ahead and show you the notes table here. It's a very simple setup. We have a key, which is just a string, and this is what the AI model uses to kind of categorize what the different notes are about. And we have the value, and that's just a big markdown, field. It's a very simple setup, but it's surprisingly powerful. By using semantic keys and explicit instructions on how to record and review notes, we give the AI models a very quick and effective way to find the context they need. So this database has a problem. We have a whole bunch of articles. We have a 124 articles. The titles don't really make any sense. Oh, the bodies are in Latin. And I I can't work with this. So I've created a prompt for Claude to read the article titles and come up with a concept proposal for each one. What could this article actually be about? So let's look at the system prompt here. You can see that there's two things that it's informing the AI model about. That there's an AI notes table in the rough structure of that table and that it needs to read and record its database insights, especially how it solved problems it ran into and just update that note on every run. This gives it the self learning ability we were talking about earlier. It's able to figure out what it ran into last time and fix it on the next run. Now back to the task. I've told the AI model it's a skilled content analyst and writer. We have a huge collection of articles. All the bodies are full of nonsense. All that. Alright. So its job is to record the concepts that it comes up with in an article concepts note. It's not sure what to do with an article based on the title. Be creative. Find a wacky concept. Then we have some very specific instructions. We say always read the template and template article concepts, this is a note, to determine how to structure your output. Always create a new article concepts note for each run. Always give it the exact name. Always process 30 articles. Always review the last note you created before starting to make sure you don't duplicate any work. This template lets us make sure that we get consistent output on every run. Alright. Let's go ahead and run this a few times and see what its output looks like. I turn on auto refresh, and then I'm gonna go into Claude, add a prompt from Directus, and examine articles and propose concepts. If you want to know how to use this feature, go ahead and take a look at the Directus MCP documentation, and then we're just gonna send it. Now one of the fun things you can do with read and write access direct to Directus is if the model makes a mistake, you can ask it to update the prompt to fix its mistake in future runs. Article concepts. It has identified directly that just about every article appears to be lorem ipsum gibberish and occasional test content. Alright. So came up with a bunch of different articles on a bunch of different ideas. In a bit, we're gonna use this to actually write all these different article concepts. But for now, I just wanna keep going through the batch process to show you how it's able to pick up where it left off and continue. On this run, if you notice, it actually picked up that it needed to look up the translations and didn't have to figure that out from the schema this time. Alright, it is a new day. My cloud usage limits have reset, and we are ready to continue with step two, which is generating the articles based on the concepts that we've put together. All of the, article concepts have now been saved in these, concept notes. They take the article title. They try to figure out what on earth, the article should be about and propose a concept. So now we have the next step of the process, which is to generate a actual article for each of the articles we generate concepts on in both English and German. We're gonna go in batches of 10 and see how well that works. We'll take this prompt and it should automatically read over all of our article concept notes and start filling in articles for those. Let's go. So we're gonna use the turn concepts into full fledged articles prompt. That's gonna go and read through all of our article concept notes and start filling in those articles, and I'll show you those articles as it writes them. I have no idea if these articles are gonna make any sense. This is gonna be fun. I'm having fun reading through these articles. I have no idea how helpful any of this is, but it at least sounds very convincing. Now the key advantage to this approach is that if we were generating concepts and writing articles at the same time, we'd have to use much smaller batches. But because we split the process into two steps, we've saved a ton of context window, and we're able to work in larger batches. And as a bonus, we can do multiple things with those article summaries, while we're working on generating those articles from those summaries. Now this isn't the most efficient workflow for generating articles. There's way better workflows for that. We're just demonstrating this concept. But a nice thing about this approach is that you can perform multiple tasks at the same time. For example, while we were working on these articles, we could also be working on something completely different using the notes that we're generating the articles from. I have this prompt here, categorize article concepts, which will allow Claude to suggest an article taxonomy based on the summaries it's already generated. It'll just read all the article concept notes and create a new note suggesting that new taxonomy. I like the way it describes these articles as a fascinating collection of technical and business focused article concepts with creative jargon filled titles that have been transformed into practical valuable content ideas. I'm not quite sure I'd be so positive about it, but Nate gets the idea across. Alright. Let's take a look at the categories that it generated and the opportunities that identified. So here's some content gaps, AI and ethics, sustainable technology operations, human centered digital transformation, all sorts of other different things. And here are some of the categories it's come up with. System architecture, it's put a bunch of different articles in that category, ecommerce, digital business, data management and analytics, user experience and interface design, business operations or business optimization management, automation and AI systems, project management and collaboration. So it's basically taken all of those articles that we put together and categorized them according to common themes in those articles. This could be really useful if you have an existing content collection that you're trying to build a new categorization system for. And what's great is you could just take this and create another prompt to actually apply that taxonomy to articles, and create categories and all sorts of different things for that in Directus. So by the end of this run, here's what the AI model has built. Concept proposals for every article in our database, full body content for many of those articles in English and German, a whole new content categorization system for the articles it wrote, progress logs so that it doesn't lose its place, and database access hints that documents how the challenges were solved so that on future runs, it could do even better. Instead of treating AI like a disposable worker or one that can infinitely stuff and stuff and stuff and work on something until it gets all confused, We're treating it like a teammate that tracks and follows up on what it's done across multiple days, multiple runs, starting with a clean slate on each task and only pulling in the information that it needs. We turned a human in the loop batch processing task into an AI creative workflow by splitting it into discrete steps and letting it kind of pick up where it left off. We gave the model a way to track progress. We built templates to make the results more consistent and reusable, and we recorded information about the database to help the AI model get smarter over time. I've had a ton of fun nailing down this workflow and testing it on all sorts of tasks. For my work, we're actually using it to do things similar to this where we're analyzing a huge amount of content and trying to figure out how to organize and categorize and and do all sorts of things, and it's it's doing really well. The ability to take notes, record what it's done, and just kind of start over and create its own context has been incredibly helpful with turning a workflow that works really well for one or two articles into something that works well across thousands. So So thank you for your time. I really look forward to seeing what everyone does with Directus and Directus MCP going forward, especially as Directus MCP evolves, AI models get more capable, and just the overall core gets stronger. The future is gonna be fun. Hey. I'm Matt from Morley. We're a directors partner agency based in The UK. And today, I want to give you a quick look at what we've been building, both for our clients and for the directors community. So one of the things we built for directors that we're most proud of is the NUXT directors module. It's an open source NUXT module that wraps the official directors SDK and gives you proper first class integration with NUXT. The idea is simple, you add your module, point it at your Directus instance and that's it, you're up and running. You get session based authentication with cross domain support out of the box, automatic type generation from your directors collections, type safe websockets, Nux images automatically configured for your directors assets, and we even embed the directors admin panel right into the Nux dev tools. So let me show you how to get up and running with that. So I'll just open up Visual Studio Code. The first thing you need to do is go into your Nux config, add the Nux Direct SDK, install it in whatever package manager you like, then go to your environment variable, add your direct to your URL, and if you want to use things like, admin server endpoints or automatic type generation then just add in a Nux, a direct to admin token as well and that will be automatically handled for you. Don't worry nothing will be passed to the front end it's all completely safe. And that's it, when you start the server you get access to everything you'd expect from your Direct SDK automatically imported. So as you can see here on my blog page I've got the directors all I do is call use directors and then things like read items automatically imported and it's fully type safe. You can see all of my collections. I get errors if it's a if it doesn't actually exist in the director's schema and I can do things that you'd expect. Fully typed filtering, fully typed nested fields, and again I will get errors if something doesn't exist, and fully typed sort. That's it. It's up, it's running. You can do, there's even help methods for things like getting your file URLs, where you can pass in options around your thumbnails, whether you want to download it, what you want to call it, all that is handled automatically for the NUXT Directus SDK. We built this because we kept solving the same problems across client projects and rather than copying and pasting boilerplate we packaged it up properly and made available on NPM. It's MIT licensed and we're just about to release version five which targets Nux four and the latest Direct SDK. Here's some of the work that we built using Directus and the Nux Directus SDK. So that's a quick snapshot of what we're building at Rolla. If you're building anything with Nux and Directus, feel free to give the Nux Directus SDK a look. Otherwise, if you want to chat about anything Nux, directors or even potentially a future project please do reach out at rolli. Io. Thank you very much. Thank Thank you so much for the showcases and to everyone who's already joined and showed enthusiasm. We are really excited about bringing the community together in this way and to see all the possibilities that can come out of it and, yeah, hopefully, we have done enough to convince you to join. That's it from me. If you have any questions, do head over to the community forum at community.directors.io and have a great rest of leap week, everyone. See you soon. Bye.","aed3883f-f0df-4ae7-8c85-5df7e2c86161",[136],"912b46f7-c6c2-4876-a81c-1060ff910431",[],{"year":139,"number":140,"id":141,"episodes":142,"show":150},"2026",59,"289f6534-7fdd-46df-8c00-89a75469fe41",[143,144,145,146,147,148,123,149],"bbaa3063-6fbe-4d96-bbc7-e50672f9a308","f885409e-0ace-41e5-aca3-faf4dcd7659b","7271f0be-33fd-4cea-b7bd-9c63e74969e1","0baede33-974c-4343-abad-3cea928c8112","a10f99c3-6b45-46e6-b703-64366f150c57","1310befc-e361-4e19-848f-d685c19dddef","68536266-9502-4df8-a295-ef082dfe6fd0",{"title":151,"slug":152},"Leap Week","leap-week",{"id":146,"slug":154,"vimeo_id":155,"description":156,"tile":157,"length":158,"resources":12,"people":12,"episode_number":159,"published":130,"title":160,"video_transcript_html":161,"video_transcript_text":162,"content":12,"seo":163,"status":19,"episode_people":164,"recommendations":167,"season":168},"from-zero-to-production","1176293308","Spin up a backend, connect a frontend, and deploy to production - all in one session. Featuring Railway.\n\n","14919273-9a84-4b2a-a651-20a11144d00d",38,4,"From Zero to Production","\u003Cp>Speaker 0: Hi, everyone, and welcome to Leap Week. My name is Lindsay, and I'm an engineer on the direct us team where I mostly work on integrations and developer experience. For day two of Leap Week, the theme is build it, run it, sell it. The idea is to show how quickly you can go from an idea to a real production application using modern tools. We'll be focusing on the first part of that story, build it.\u003C\u002Fp>\u003Cp>For this session, we're going to build a fictional AI automation agency. The idea is that this agency helps companies implement AI workflows for things like marketing, automation, research, sales outreach, and internal knowledge assistance. So by the end of the session, we'll have a working back end for this agency, generate a front end website for it, and deploy the whole thing live. But before we can build anything, we need somewhere to run our back end and infrastructure. I'm joined today by someone from the Railway team.\u003C\u002Fp>\u003Cp>I'll let them introduce themselves and tell us a little bit about Railway before we start building.\u003C\u002Fp>\u003Cp>Speaker 1: Awesome. So, thank you so much for having me. My name is Mohammed. I'm a general engineer at Railway. And at Railway is I would say in a nutshell, it's a all in one intelligent cloud provider.\u003C\u002Fp>\u003Cp>You can deploy pretty much anything, databases, back ends, front ends, queues, whatever. Like, you can just deploy it. You can just run it. And, yeah, essentially, we allow you to deploy anything, and we really focus on making it easy for you to go from zero to deploy it as easy as possible. Yeah.\u003C\u002Fp>\u003Cp>So excited about what we'll build today today.\u003C\u002Fp>\u003Cp>Speaker 0: Awesome. So the first step in getting any project off the ground is gonna be your infrastructure. And instead of spending hours setting up servers and databases, Railways gonna let us start from a working template. For this demo, we have a template we've already created. It's a pretty simple one.\u003C\u002Fp>\u003Cp>It's gonna have everything you need to get started. We do actually have another version as well that launches with CMS collections already started if you want a starting point. But for this, we are going to use our blank one. So I'll go ahead and launch that template here.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So a little bit of context. Templates essentially encapsulate a set of infrastructure components, all the configuration. And here, we essentially have, like, a database set up, post press red as a bucket for object storage, as well as directives, which runs our back end. So, like, directives here would be, a server that we actually use.\u003C\u002Fp>\u003Cp>And now, like, everything here is neatly on the railway canvas. You can see everything grouped under, like, the directors group. And, essentially, like, here, you have a project, and a project is where you can have essentially everything deployed. So, like, in this example here, you have practice running. Maybe you will have, like, the front end, so that will be a separate service.\u003C\u002Fp>\u003Cp>And that's pretty much all we need to get started. So, like, all you need to do is you can sign up for free, and then you can go to the link to deploy the template. You click deploy now. You can either deploy to a new project or an existing project. If you choose, like, the path we're going through right now is deploying to a new project, this is the experience you'll get.\u003C\u002Fp>\u003Cp>And now you can see, actually, it's like we have the difference for versus, they're being deployed. So, like, Post JS, that's the database. And then we have Redis and as well, we have, DirectThis, which is also just cute. So, like, one thing after another will be deployed. So, like, in a, hopefully, a minute or two, everything should be ready, configured.\u003C\u002Fp>\u003Cp>You don't even need to, like, touch an editor, and it just works. So that's pretty much yeah. That's pretty much the overview for templates.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So it looks like\u003C\u002Fp>\u003Cp>Speaker 1: Like, this is also deploying I see it's so this one is actually deploying from a Docker image, and this is the official one, for directors. I assume this is the latest version. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yep. This is our latest version.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. We make sure\u003C\u002Fp>\u003Cp>Speaker 0: to keep this updated.\u003C\u002Fp>\u003Cp>Speaker 1: That's awesome. But, yeah, if, let's say, for example, you deploy and you want to get the latest version, of, let's say, Directus. If you actually go to settings, you'll be able to see the source image. There's like automatic updates. You'll actually be able to see if there's an automatic update.\u003C\u002Fp>\u003Cp>You just click apply. You deploy it. It just works if you want like the latest version. So it should be very easy to set up, but so far we can now we see, we have the database ready. Red is ready.\u003C\u002Fp>\u003Cp>The bucket is created, and now the last component direct list is being deployed, which hopefully should be ready Yep. Very soon. And we're live.\u003C\u002Fp>\u003Cp>Speaker 0: We'll click that. So in just a couple minutes, we've gone from nothing to a running back end with a database, API, and admin interface. So as part of our setup process, we'll create our first admin user here. Give it a password.\u003C\u002Fp>\u003Cp>Speaker 1: So I assume this happens as the first initial step. Is this correct?\u003C\u002Fp>\u003Cp>Speaker 0: Yes. Yep. Okay. When you first set up, it'll ask you to create your admin user. And here we go.\u003C\u002Fp>\u003Cp>We're in our, UI here. And to get started, we are gonna do a little bit of, setup for our AI assistant because we are going to be using AI to create most of this. So Directus has a built in AI assistant that can help you. It can generate schema content, and it has knowledge of what's going on in your instance. So if you go into settings here, we'll give it a key.\u003C\u002Fp>\u003Cp>Oops. Let me grab my key.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think you just said\u003C\u002Fp>\u003Cp>Speaker 0: One password. Gotta have that security.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. And then we are also going to set up our MCP for later because we'll be using that when we do our front end.\u003C\u002Fp>\u003Cp>Speaker 1: So here, this is, like, the Directus MCP or, like, this is an MCP server for our deployed direct the Directus instance?\u003C\u002Fp>\u003Cp>Speaker 0: Yes. It is. Yep. So our cursor, which we'll use to do the front end, will be able to access all of our data here. And it'll depend on the permissions we give it, what it will be able to do.\u003C\u002Fp>\u003Cp>But we also have this in app AI assistant here. So we're gonna use that to generate our schema. So I already have some\u003C\u002Fp>\u003Cp>Speaker 1: So quick question. For someone who's not a 100 to 100% familiar, let's say, with all of the features, essentially, what we're doing is now we have a like, our complete back end deployed running. And part of this back end, you can actually expose an MTP server, and then I can connect to this MTP server via call code, code x cursor, whatever coding agent of my choice, and then just tell it to do stuff for me. That'll be reflected in my live deployed instance, and now it's just that's it.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. We'll see that a little bit later, but we're gonna do something similar here. We also have our AI assistant that's in the UI here, and we are gonna use that to build our back end, our collections, our schema. So right now, we have no collections created.\u003C\u002Fp>\u003Cp>We don't have any data tables, basically. So I'm gonna go ahead and prompt it with a prompt that I created earlier, and it's going to create the structure of our back end.\u003C\u002Fp>\u003Cp>Speaker 1: And the idea is, like, a collection would be, like, an object, right, for something.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So it's gonna be, like, your data tables. So it'll have, like, an overlying day data table where we'll have one called site settings, and that's gonna be the settings for the website. We'll have one for case studies.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: That's gonna have case studies for our AI agency. Actually, I can show you the prompt here a little closer. So we've we're creating our collections, and we're giving it different fields. So we're giving it, like, a tagline, a headline. We're gonna give it different services that this AI agency does, give it some fields there, and we're gonna create playbooks and case studies out of those, so things you might need in an AI agency marketing website.\u003C\u002Fp>\u003Cp>And the AI on our end is going to do that work for us. So we've told it in our prompt what we want. We didn't really tell it what type of fields they need to be, if they're strings or numbs or any of that. It'll infer that if we want. And, actually, one nice thing is if you don't know what schema you want, you can talk it through with the AI assistant.\u003C\u002Fp>\u003Cp>It can give you suggestions of things that you might wanna add for a data table. So it's gonna ask questions too back to the user. So it's saying it's ready to create these, and it's saying, do we wanna proceed with its defaults? Do we wanna review each one and decide? Or do we wanna have, like, it be super minimal?\u003C\u002Fp>\u003Cp>I'm gonna say just create it. Let's see what it comes up with. It's asking how we should store the icon. So let's just say a single file upload. How do we wanna store the pricing start?\u003C\u002Fp>\u003Cp>We don't really care about that. We'll just pick something.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: There's a\u003C\u002Fp>\u003Cp>Speaker 1: there's a recommendation. So I think we can\u003C\u002Fp>\u003Cp>Speaker 0: go with it. Yeah. It's collaborative. It's gonna ask you how you wanna do things. It's not gonna just bowl over your user and, like, do everything for you.\u003C\u002Fp>\u003Cp>It's it's not just an AI tool that's gonna do it for you. It is a human in the loop type of experience where you can make sure it's doing something you actually want it to do. And it's gonna tell you everything it's doing while it's doing it. So you can see here. It's planning it out.\u003C\u002Fp>\u003Cp>You can see the thinking if you wanna know, like, what it's thinking while it does it. And you can do the different models if you're you have a different one. We're currently just doing, GPT five. In those settings we set up earlier, you can change that out. Like, if you want something different going on, you can say which ones are allowed, because this UI is actually not just for your admin.\u003C\u002Fp>\u003Cp>You can allow other people here. So you can have other people with different access levels able to come in and modify your content once it gets created or your data tables. So you can let them use the AI assistant, and you can set what permission levels they have. So it's gonna ask for different tools like creating the collections if it can do it before it does it. You'll see here's our data models now.\u003C\u002Fp>\u003Cp>It's created a couple of them. We've got our case studies one. It's starting to add the fields into these. In the past, you would have to do this all manually and create collections. Now you can just use the AI to do it without having to worry too much.\u003C\u002Fp>\u003Cp>So it's a lot less manual work.\u003C\u002Fp>\u003Cp>Speaker 1: But we could have done the same thing by using the MCP thing. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yep. You can. Yeah. We just wanted to give people an in app way to do it a little faster and simpler for people who maybe aren't don't have a data model or AI model they like to use outside of here.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Definitely makes sense. It's just like in my mind, I really like the flow of using some like, I use Claude a lot. So when I use Claude, I'm like, okay. I want a complete end to end flow.\u003C\u002Fp>\u003Cp>Like, what what would it look like if I don't leave cloth? Can I actually do the stuff I wanna do? And it seems like the answer is yeah. But there's also And\u003C\u002Fp>\u003Cp>Speaker 0: that's actually that's what I like to do as well as I'll go in cursor, and you could start the whole front and back end directly in cursor using the direct SMCP, and it can create it in here for you. We're gonna do the front end in Cursor just so we're seeing that too a little bit, but I wanted to use a little of the AI assistant as well.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. I'm actually in the process of making sure that our experience of getting started to be super seamless. So we already have, like, a regular CLI. We have, like, Asian skills, and, we're going to essentially keep shipping more, features through the CLI.\u003C\u002Fp>\u003Cp>So, like, it should be able to you say, like, oh, I wanna deploy direct us. It should be able to go through the templates marketplace, find the most up to date template deployed for you, and then it should be able to also figure out, like, the deployment is finished. Let me continue. And then you just have, like, a complete workflow where you don't even have to go to a templates page. So that's kind of, like, what's\u003C\u002Fp>\u003Cp>Speaker 0: That's awesome.\u003C\u002Fp>\u003Cp>Speaker 1: In the works. But yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I'm excited for that. That'll be good.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's I think it asked a question at the end.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. So now it is done. It's gonna, like if you've used this model, it constantly asks you other things it wants to do, but it is done. It's created the fields. So we've got that part done.\u003C\u002Fp>\u003Cp>Now we're gonna give it some fake content in these fields. So that's the database table. And now we're gonna fill these tables with actual, like, fake data. So I'm gonna paste that in. So when we go into here, you'll see no content yet, and it's gonna start filling those in.\u003C\u002Fp>\u003Cp>But you can see, like, these are the fields. So if a nontechnical user came in and wanted to give it a new name or a new tagline for their website, they can do it in here without having to touch the code at all. So it's a nice way to work with both your front end team and your back end team and your content team and not have, people who are super technical not able to get things done. While this is doing it, we are actually gonna get finished setting up for our MCP, actually. So we're gonna go here in our docs.\u003C\u002Fp>\u003Cp>We have a little section for setting up the MCP in different tools. We're gonna use cursor. So I'm gonna click add to cursor here. Nice easy button, and it will set us up into our settings. And we are going to go grab our URL from here.\u003C\u002Fp>\u003Cp>So this is our directest instance URL, and we'll pop that in here for our MCP setup. So it's just your instance URL slash MCP, and then you're gonna wanna set up a token. So let's make sure it has the permissions it needs. When you're connecting to external tools like cursor, it's always a good idea to scope your permissions so that it's not able to completely nuke your whole setup. You know, AI likes to be overeager.\u003C\u002Fp>\u003Cp>Speaker 1: So Yeah. Yeah. It's like, let's start all over.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. We need a fresh start. We we recommend setting up a token for it specifically and not using your admin token. So you wanna go to your user roles here. You'll create a new role, name it whatever you want, and you'll add a policy.\u003C\u002Fp>\u003Cp>We're gonna give it view only policy. So you'll add your collections, give it read only access. That way it can't, like, update or delete or anything when it's in the MCP. If you want the MCP to do that, you can totally give it permissions to create, read, and update, and do whatever you want with it. But I recommend giving it, like, the lowest level of access at the start so that, you know, it's not gonna go in and break your whole setup here.\u003C\u002Fp>\u003Cp>Mhmm. So we've got our policy. I'm gonna create a new user too to grab a token from. So if we scroll down to our tokens, we're gonna generate it and save. And then we'll go back in here, and that's where you put in this bear here, authorization code.\u003C\u002Fp>\u003Cp>You put that token, click install, and that will install our direct as MCP encursors. So now we're ready to go for that when this is finished. So let's go check on it.\u003C\u002Fp>\u003Cp>Speaker 1: So what we've done is create a role, and then the role has the token?\u003C\u002Fp>\u003Cp>Speaker 0: Yes. So the the user is what who has the token. So this MCP user has the role that we created. I created it all within the same page, but this person has the role, and then they have the token. Mhmm.\u003C\u002Fp>\u003Cp>So if you go into our settings here and look at the MCP role, we've got our view only policy that we set up Mhmm. For our different collections. And the users in the role is that MCP guy. So now it looks like it did finish the content. If we go back into our content page, now you can see it's filled it in with our sample data.\u003C\u002Fp>\u003Cp>I gave it a few guidelines of the type of things I wanted it to have, but, otherwise, it just kinda came up with this on its own, sample content. So it's given us a starting point. So now that we've got some sample content, we are going to go back in to cursor. I'm gonna grab this really long prompt I've created telling it basically we are an AI agency. We're creating our website.\u003C\u002Fp>\u003Cp>I gave it I told it to use next. I also made sure to tell it to use the Directus MCP server. This might take a minute, so I'm gonna go ahead and get it started. We are in a completely empty folder here, so I'm gonna have it do all of the coding for me. Let it create what we need here.\u003C\u002Fp>\u003Cp>It's gonna make sure to look at our schema via the MCP. So we want it to not guess on what structure we have of our data. We want it to actually look at it. Like we said, we could create the data directly from Cursor via the MCP. You would have to make sure you give it, the right permissions.\u003C\u002Fp>\u003Cp>So that that might have to be an admin token in order to generate new schema with your MCP. So if you're comfortable doing that, you totally can.\u003C\u002Fp>\u003Cp>Speaker 1: Interesting. So once that's done, we'll have something that's working like a front end running locally that's fully functional, pulling data from our deployed direct us instance. Correct?\u003C\u002Fp>\u003Cp>Speaker 0: Yep.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. And any changes you make in direct us would be reflected in your local front end that you've got here. And then as the final step, we are going to do to deploy this front end to railway so that you have a full production application front end and back end.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I have cursor set up, so I could just automatically runs everything. I don't, like, manually approve stuff. I just\u003C\u002Fp>\u003Cp>Speaker 0: I don't trust it. Yeah. I gotta make sure I know what it's doing. I've been burned before.\u003C\u002Fp>\u003Cp>Speaker 1: I don't know. The the models are they keep getting better and it's like, I'm like, I would've done the same thing. So I don't know. I feel like it's just faster and you can just like, you know, sit back, relax, grab your favorite drink while it gets to work. You come back to it and you're like, woah, this thing actually works.\u003C\u002Fp>\u003Cp>So Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: I think I'm a bit of a control freak. I can't let it just just\u003C\u002Fp>\u003Cp>Speaker 1: go. I'm telling you, like, once you let go and you see, like, it actually works and it's like, I think, I think you'll be plenty surprised. Then you're like, wow, that's actually a really fun. Cause like now, you know, if let's say for whatever reason, someone brings you on Slack, you can play for guys. Like, you come back to us, like, oh, I forgot to approve.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That it's done.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. I I I think, it's a much more fun experience when you just let it kinda do its own thing without you steering it. But I do understand the, appeal of, you know, wanting to see what it does. But also, like, we're starting from scratch, so there's little, like, room for error, I would say.\u003C\u002Fp>\u003Cp>But yeah.\u003C\u002Fp>\u003Cp>Speaker 0: There's nothing for it to break. It's creating it from scratch. So\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: We're pretty comfortable letting it just do what it wants to do here. So it looks like it actually has gotten the types if you look. So it pulled this from our MCP. It checked in, Directus, and it got our back end and what types we have there. So that's good.\u003C\u002Fp>\u003Cp>Looks like it's I did tell it also to create a little connection a net little connector to direct us here, a fetcher.\u003C\u002Fp>\u003Cp>Speaker 1: So it\u003C\u002Fp>\u003Cp>Speaker 0: looks like it's done that, getting different gets to get the data.\u003C\u002Fp>\u003Cp>Speaker 1: And it will be able to pull in our deployed, like, like, URL and actually set it as an environment variable?\u003C\u002Fp>\u003Cp>Speaker 0: Yes. It will. So I it might make me make the e n v, but I've got it. It's supposed to use the direct URL here, and it's supposed to use the token that we created. So, we'll see.\u003C\u002Fp>\u003Cp>I've done this a couple times just to see how it would do, and it it doesn't always know to grab the URL. If it's really smart, it can grab it from the cursor settings, and it can get the token and the URL, but it doesn't always manage that. You'll notice I'm in auto mode, so it's a different model each time, so it might not be a very smart model. But we at the end, we'll give it the proper e n v variables, and it'll be able to connect to our deployed instance.\u003C\u002Fp>\u003Cp>Speaker 1: Excellent. Yeah. Honestly, again, I I like my to choose my own models, You should use, like, a, you know, g p three five point four or, like, an Opus 4.6 and because I don't know. I don't like that, I would say I wouldn't trust the auto. Like, which one would affect?\u003C\u002Fp>\u003Cp>Like, I wanna be I want a consistent experience. So then if, like, anything starts feeling weird, then I can blame the model. I was like, oh, you know, like, Opus is acting really dumb lately, that sort of thing. So\u003C\u002Fp>\u003Cp>Speaker 0: It it does just feel like from one day to the next, though, even if you have a model that you know is good, it it doesn't always behave exactly the same.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Sometimes it feels like, you know, you're you're just rolling the dice. Sometimes it's like, woah. This is it. Like, this is the next big thing.\u003C\u002Fp>\u003Cp>I was like cause like, sometimes I, I, I asked it a question and I wanted it to check against like, you know, our internal, GitHub repo. And it's like, Hey, do we support this thing? It was like, yes, you do. And I told her like, oh, show me the code. It's like, after checking, apparently, this is not supported.\u003C\u002Fp>\u003Cp>I was like, come. Yeah. And this is like the smart and I told it, like, oh, you know, UltraThink, you know, work really hard, but it's like, I guess sometimes it's just, you know, not that type of day. But I think here it's like it's it's it's working well, based on what I'm saying.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It looks like it's getting something.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: It's messing around with the configs now.\u003C\u002Fp>\u003Cp>Speaker 1: Interesting. What is what's it doing to the Next. Js config if we open it up? Just out of curiosity. Technically, you know, we're we're not supposed to look at the code, you know, and, because, you know, I was just shipping it, but I'm also curious.\u003C\u002Fp>\u003Cp>Okay. It's setting, like, the assets URL, I assume, for, like, files and stuff. That's pretty awesome.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. And while it does that, I'm actually gonna grab my last prompt, which is a prompt to have it, push to GitHub so that we can get it ready for deploying the front end. I'm gonna grab that. Go back in here. I think it's almost done.\u003C\u002Fp>\u003Cp>It feels like it's it's doing the read me. So\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Probably at the end. But yeah. I would say, there is actually a like, you you can definitely, you know, start with this workflow. It's totally fine.\u003C\u002Fp>\u003Cp>But if you have, like, the AOS CLI setup, it could just deploy it to your existing project. And this way you don't even have to worry about git being installed, get, having a GitHub repo or anything. But when you have it with git, well, the benefit is, anytime you make, changes through the front end, it will just automatically deploy. So it's like it's it's all trade offs based on, you know, how, structured and organized you wanna be. But yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That makes sense. Alright. We did not create our E and V, so we're gonna create that real quick. And\u003C\u002Fp>\u003Cp>Speaker 1: I would say that's the responsible thing for it to do.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. I don't didn't really like that it created its own ENV and, like, grabbed my things from the MCP. That was a little, uncomfortable. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It then gets better this way.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. Look at our URL. Alright. And then we're gonna run it locally just to see it working.\u003C\u002Fp>\u003Cp>Speaker 1: What does the static token do?\u003C\u002Fp>\u003Cp>Speaker 0: So that's the one we created with for the MCP. We're using it here too. Okay. So that is the permissions that we allow in Directus. So if you give it, like, no read permission, it won't be able to see the content.\u003C\u002Fp>\u003Cp>If you give it access to read, like what we did, we gave it access to read all of these collections, then it can read the data. So that's where, like, you probably want a separate one from your MCP. We're just in the interest of time having it Yeah. One that is view all for all of our collections because we don't need to make changes with the MCP. But you probably would have permission to update and delete with your MCP as well.\u003C\u002Fp>\u003Cp>So you probably want wanted a separate one for your front end. So you'd probably wanna create a user role for your front end that gives it different permissions. Otherwise, you can have it set up to use your public permissions, which controls what the API data is, available without authenticating. But it's safer if you do give it a token so that your front end is only able to grab what you want, not, like, everything. So let's see if it worked.\u003C\u002Fp>\u003Cp>Speaker 1: Moment of truth.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. There it is. Looks like some images are broken, but we've got a full website here. You can navigate around. It's got some prompts.\u003C\u002Fp>\u003Cp>It's got a button to get in touch.\u003C\u002Fp>\u003Cp>Speaker 1: We've got a\u003C\u002Fp>\u003Cp>Speaker 0: full website here. Obviously, you'd wanna iterate on it, fix the images, and, do a little honestly, it looks great. It doesn't need too much design or anything even. So that's a good starting point. We are going to go ahead and say good enough.\u003C\u002Fp>\u003Cp>The images can get fixed later. We are going to deploy this. And, of course, I copied other things, so I need to go back and get my prompt. So like you said, there's other ways to do this, but we are gonna have it pushed to a GitHub repo. We're pretending we have other people who wanna manage this code, and we want it in some place that we would have access to it as a team.\u003C\u002Fp>\u003Cp>So we've have a GitHub repo we've already set up. It's empty right now, and we're gonna push it into here. So I'm having it create a readme that'll tell users, our other team members, basically, is in our example here. We're deploying to Railway. Here's what you'll need.\u003C\u002Fp>\u003Cp>It'll mention the EMV variables we created here, because we're gonna need those. I'm actually gonna copy them now, so I have them ready. And then it it's gonna make sure everything's building properly with the prompt I gave it, and then it will push to GitHub for us.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And I noticed it said, like, even if you don't have, like, the environment variable set up initially, like, it should still deploy. It should still work. But once we actually set the these values, we'll be able to actually fetch the thing and have the same result we have locally, but actually have it live and deployed on a URL.\u003C\u002Fp>\u003Cp>Speaker 0: Yep.\u003C\u002Fp>\u003Cp>Speaker 1: It's updating the ReadMe.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. There's our ReadMe.\u003C\u002Fp>\u003Cp>Speaker 1: I remember the times where I had you know, I used to write stuff by hand.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Now I just be like, you know, just instructions, just lines, like update, read me, and that's it. You know? Just just figure it out the rest.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. I think it's gonna commit.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think it's already I believe I ran already a built. So, yeah, we see, like, the dot next directory. Maybe the build was under there. Yeah.\u003C\u002Fp>\u003Cp>It's running get the commands. Okay. I think it has committed everything here. So now\u003C\u002Fp>\u003Cp>Speaker 0: What did I do?\u003C\u002Fp>\u003Cp>Speaker 1: I think it's just the wrong link.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It did. Okay. So we are gonna now go in here. We're gonna add a service.\u003C\u002Fp>\u003Cp>Speaker 1: Yep.\u003C\u002Fp>\u003Cp>Speaker 0: GitHub repository. And what did I call it?\u003C\u002Fp>\u003Cp>Speaker 1: Was it like AI agents or something like that?\u003C\u002Fp>\u003Cp>Speaker 0: AI agents. Not sure. Oh, boy. Hold on. I pasted it down here.\u003C\u002Fp>\u003Cp>Speaker 1: You should be able to see, like, all repos, but maybe you have too many. So, like\u003C\u002Fp>\u003Cp>Speaker 0: I have so many. I really need to clean it up.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. That's me.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Hold on. I'm gonna\u003C\u002Fp>\u003Cp>Speaker 1: Give me to refresh. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Just don't wanna have my mega list of repos up on the screen so one's.\u003C\u002Fp>\u003Cp>Speaker 1: But I think you might need to refresh the, so I tried to push the code. Right? Oh, it didn't.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I don't know if it\u003C\u002Fp>\u003Cp>Speaker 1: pushed. I mean, I guess you need to stop. Yeah. See\u003C\u002Fp>\u003Cp>Speaker 0: It's getting me with the permission things. You're right.\u003C\u002Fp>\u003Cp>Speaker 1: I should\u003C\u002Fp>\u003Cp>Speaker 0: just let it do whatever it wants.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. But now it's I don't know if it's pushing it, and then it should be good. So now if you go to right way, I would say if, if you refresh, you should be able to see it. Yeah. We have code.\u003C\u002Fp>\u003Cp>Speaker 0: The code is now here.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. Now when you add, we might need to like refer, I think it should just fetch, but you can try.\u003C\u002Fp>\u003Cp>Speaker 0: Does it not like my\u003C\u002Fp>\u003Cp>Speaker 1: Maybe just try refreshing, like, the, entire Page. Dashboard. Yeah. Yeah. Just in case.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Okay. And Okay. Let's try again.\u003C\u002Fp>\u003Cp>Speaker 1: Interesting. I mean, if you want, what you could do is maybe it's just an issue with, like, get up not pulling. So, like, you can copy the full URL. And when you add, you can just paste that URL.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. The it looks like it found it like that. So let's\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. So that is how you connect it, and then\u003C\u002Fp>\u003Cp>Speaker 1: we're gonna just need to hit deploy. Yep.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. But I wanna put it up by the other ones first. Yeah. And that was\u003C\u002Fp>\u003Cp>Speaker 1: in there.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. So I wanna also add our e and v variables before we get started.\u003C\u002Fp>\u003Cp>Speaker 1: So they're actually automatically inferred, like, at the bottom. Oh. You can see them. You were right. Suggest variables, and then you can update these values and click add, then you're good to go.\u003C\u002Fp>\u003Cp>Speaker 0: There you go. So let me grab where I pasted them over here.\u003C\u002Fp>\u003Cp>Speaker 1: You can also have, like, go the route you were going through, like the raw editor, copy and paste that other thing, but just just wanted to point it out. Because, like, to me, it's like, it's one of those things that when you don't know it, you're like, oh, that's neat. That's kinda how I always felt about it the first time I tried it.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. You're right. That is cool that it already kind of pulled up what we needed.\u003C\u002Fp>\u003Cp>Speaker 1: But,\u003C\u002Fp>\u003Cp>Speaker 0: yeah, you could also open raw editor and just paste in the whole EMV file that we had ready.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. And then you can click add to add everything.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. So these this is the token we have in our local one, and then this is the URL of the same thing in Railway. You guys can also do internal URLs. Right? Is that something we could use here if we wanted to use the local URL\u003C\u002Fp>\u003Cp>Speaker 1: of this? So you can do, like, a dollar sign, two curly braces, and then you should get auto complete for, like, the URL. So, actually, you will be able to see a line where they're both connected if you wanna try it out. And then what will happen is, now whenever if the URL changes, it will just automatically update the other one as well and redeploy it.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So that's like using the internal\u003C\u002Fp>\u003Cp>Speaker 1: URL reference variable. So I I believe you the way it would work is you just do, so the on directives and the variables themselves, like, if you go to variables. I believe what's the yeah. I guess it's public URL is the value that we will want. So wait.\u003C\u002Fp>\u003Cp>If you go now to the AI agency service and then go to the next public directors URL, because that's the URL value you wanna set. Right? So that more menu and then edit. And then if you do, dollar sign and then double curly braces, you'll be able to actually pull in so I believe the name of the service is directed. So you just do direct us, and then it's gonna be dot yep.\u003C\u002Fp>\u003Cp>Do you see? And then you just choose the public URL Yep. From this list. Got it. So now when you choose it, it's just gonna automatically give you the right URL and it will just be able to read it.\u003C\u002Fp>\u003Cp>So if the other one updates, you just do it. But for this, you're gonna need to hit redeploy again. So it will take some time. So I'd say we can do this after you can cancel. But yeah.\u003C\u002Fp>\u003Cp>And I would say there's actually another cool feature because might as well, if you go to settings, like, okay. It's done. So we can actually test the thing. But we can update, like, the domain to, like, a actual domain because you can now buy domains on railway.\u003C\u002Fp>\u003Cp>Speaker 0: Nice. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. So this will give to you just a domain for, the front end itself, and it will have, like, a .up.railway.app at the end. You'll need to specify a port, which I'm not sure what it is on. I I think it should be 3,000. Like, 3,000 should just work.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: And if we visit it, might be the incorrect port. You could try eighty eighty as well because that's what I was suggesting in the beginning. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: There we go.\u003C\u002Fp>\u003Cp>Speaker 1: There we go.\u003C\u002Fp>\u003Cp>Speaker 0: So there is our front end deployed.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. And we have a live app.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Awesome. So that was pretty fast. We went from nothing to a fully deployed front and back end here. Let's see.\u003C\u002Fp>\u003Cp>Was there anything else we should know about Railway and getting this set up here?\u003C\u002Fp>\u003Cp>Speaker 1: I mean, if you hit deploy now, it will just redeploy, and you'll actually be able to see on the canvas. There's, like, an arrow, and this, service, like our front end is, referencing the direct us. And that's how you have, like, a connection between the two. And, yeah, other than that, it's like, you can just have everything in one place. Again, like, if you go now to the AI agency service and you click on settings and you also go to networking, from the right.\u003C\u002Fp>\u003Cp>Yeah. And then you can actually choose a custom domain. So when you click on it, you should actually be able to buy a domain straight from here. So like, if you come up with that domain, you can just do it from here as well. Or if you go to like ferry.com\u002Fdomains, you can do it.\u003C\u002Fp>\u003Cp>But yeah, I would say that's kinda like you now have fully like everything in one place, but you know, took us not a lot of time, and we have, like, a fully, fully deployed working back end, front end. Just, yeah, everything is in one place.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So why don't I recap what we did? We started with a simple idea launching a fictional AI automation agency. We launched infrastructure on Railway. We generated a structured back end with Directus.\u003C\u002Fp>\u003Cp>We populated it with fake content. We generated the front end using Cursor. We deployed the whole thing back to Railway. That's the build it part of today's theme. The key takeaway is that when interfaces can be generated quickly, the back end data model becomes the strategic layer.\u003C\u002Fp>\u003Cp>If someone watching wants to try this themselves, what's just the easiest way to get started on the railway side of things?\u003C\u002Fp>\u003Cp>Speaker 1: The easiest way, I would say going to the template you shared would be the easiest place. And if they, you know, wanna use direct this. And also, by the way, I forgot to mention. So if you actually, like, close the, service here, there's like an agent in the top right corner as well, where you can actually also ask questions about your stuff, like actually ask it to make changes and it should be able to make changes, directly. So I believe if you just say like, oh, update, make this update, it should figure things out on its own.\u003C\u002Fp>\u003Cp>Speaker 0: Awesome. So you guys got some AI in here too?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. In case someone wants everything in the dashboard, they can, but also if they want, the, to use whatever their coding agent of choice, they should be able to do it. But yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Cool. Well, thank you for joining us. I hope everybody has a good rest of your Leap Week.\u003C\u002Fp>\u003Cp>Speaker 1: Sounds great. Thank you so much for having me.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Thank you for joining us.\u003C\u002Fp>","Hi, everyone, and welcome to Leap Week. My name is Lindsay, and I'm an engineer on the direct us team where I mostly work on integrations and developer experience. For day two of Leap Week, the theme is build it, run it, sell it. The idea is to show how quickly you can go from an idea to a real production application using modern tools. We'll be focusing on the first part of that story, build it. For this session, we're going to build a fictional AI automation agency. The idea is that this agency helps companies implement AI workflows for things like marketing, automation, research, sales outreach, and internal knowledge assistance. So by the end of the session, we'll have a working back end for this agency, generate a front end website for it, and deploy the whole thing live. But before we can build anything, we need somewhere to run our back end and infrastructure. I'm joined today by someone from the Railway team. I'll let them introduce themselves and tell us a little bit about Railway before we start building. Awesome. So, thank you so much for having me. My name is Mohammed. I'm a general engineer at Railway. And at Railway is I would say in a nutshell, it's a all in one intelligent cloud provider. You can deploy pretty much anything, databases, back ends, front ends, queues, whatever. Like, you can just deploy it. You can just run it. And, yeah, essentially, we allow you to deploy anything, and we really focus on making it easy for you to go from zero to deploy it as easy as possible. Yeah. So excited about what we'll build today today. Awesome. So the first step in getting any project off the ground is gonna be your infrastructure. And instead of spending hours setting up servers and databases, Railways gonna let us start from a working template. For this demo, we have a template we've already created. It's a pretty simple one. It's gonna have everything you need to get started. We do actually have another version as well that launches with CMS collections already started if you want a starting point. But for this, we are going to use our blank one. So I'll go ahead and launch that template here. Yeah. So a little bit of context. Templates essentially encapsulate a set of infrastructure components, all the configuration. And here, we essentially have, like, a database set up, post press red as a bucket for object storage, as well as directives, which runs our back end. So, like, directives here would be, a server that we actually use. And now, like, everything here is neatly on the railway canvas. You can see everything grouped under, like, the directors group. And, essentially, like, here, you have a project, and a project is where you can have essentially everything deployed. So, like, in this example here, you have practice running. Maybe you will have, like, the front end, so that will be a separate service. And that's pretty much all we need to get started. So, like, all you need to do is you can sign up for free, and then you can go to the link to deploy the template. You click deploy now. You can either deploy to a new project or an existing project. If you choose, like, the path we're going through right now is deploying to a new project, this is the experience you'll get. And now you can see, actually, it's like we have the difference for versus, they're being deployed. So, like, Post JS, that's the database. And then we have Redis and as well, we have, DirectThis, which is also just cute. So, like, one thing after another will be deployed. So, like, in a, hopefully, a minute or two, everything should be ready, configured. You don't even need to, like, touch an editor, and it just works. So that's pretty much yeah. That's pretty much the overview for templates. Yeah. So it looks like Like, this is also deploying I see it's so this one is actually deploying from a Docker image, and this is the official one, for directors. I assume this is the latest version. Right? Yep. This is our latest version. Yep. We make sure to keep this updated. That's awesome. But, yeah, if, let's say, for example, you deploy and you want to get the latest version, of, let's say, Directus. If you actually go to settings, you'll be able to see the source image. There's like automatic updates. You'll actually be able to see if there's an automatic update. You just click apply. You deploy it. It just works if you want like the latest version. So it should be very easy to set up, but so far we can now we see, we have the database ready. Red is ready. The bucket is created, and now the last component direct list is being deployed, which hopefully should be ready Yep. Very soon. And we're live. We'll click that. So in just a couple minutes, we've gone from nothing to a running back end with a database, API, and admin interface. So as part of our setup process, we'll create our first admin user here. Give it a password. So I assume this happens as the first initial step. Is this correct? Yes. Yep. Okay. When you first set up, it'll ask you to create your admin user. And here we go. We're in our, UI here. And to get started, we are gonna do a little bit of, setup for our AI assistant because we are going to be using AI to create most of this. So Directus has a built in AI assistant that can help you. It can generate schema content, and it has knowledge of what's going on in your instance. So if you go into settings here, we'll give it a key. Oops. Let me grab my key. Yeah. I think you just said One password. Gotta have that security. Yeah. Alright. And then we are also going to set up our MCP for later because we'll be using that when we do our front end. So here, this is, like, the Directus MCP or, like, this is an MCP server for our deployed direct the Directus instance? Yes. It is. Yep. So our cursor, which we'll use to do the front end, will be able to access all of our data here. And it'll depend on the permissions we give it, what it will be able to do. But we also have this in app AI assistant here. So we're gonna use that to generate our schema. So I already have some So quick question. For someone who's not a 100 to 100% familiar, let's say, with all of the features, essentially, what we're doing is now we have a like, our complete back end deployed running. And part of this back end, you can actually expose an MTP server, and then I can connect to this MTP server via call code, code x cursor, whatever coding agent of my choice, and then just tell it to do stuff for me. That'll be reflected in my live deployed instance, and now it's just that's it. Yeah. Yeah. We'll see that a little bit later, but we're gonna do something similar here. We also have our AI assistant that's in the UI here, and we are gonna use that to build our back end, our collections, our schema. So right now, we have no collections created. We don't have any data tables, basically. So I'm gonna go ahead and prompt it with a prompt that I created earlier, and it's going to create the structure of our back end. And the idea is, like, a collection would be, like, an object, right, for something. Yeah. So it's gonna be, like, your data tables. So it'll have, like, an overlying day data table where we'll have one called site settings, and that's gonna be the settings for the website. We'll have one for case studies. Mhmm. That's gonna have case studies for our AI agency. Actually, I can show you the prompt here a little closer. So we've we're creating our collections, and we're giving it different fields. So we're giving it, like, a tagline, a headline. We're gonna give it different services that this AI agency does, give it some fields there, and we're gonna create playbooks and case studies out of those, so things you might need in an AI agency marketing website. And the AI on our end is going to do that work for us. So we've told it in our prompt what we want. We didn't really tell it what type of fields they need to be, if they're strings or numbs or any of that. It'll infer that if we want. And, actually, one nice thing is if you don't know what schema you want, you can talk it through with the AI assistant. It can give you suggestions of things that you might wanna add for a data table. So it's gonna ask questions too back to the user. So it's saying it's ready to create these, and it's saying, do we wanna proceed with its defaults? Do we wanna review each one and decide? Or do we wanna have, like, it be super minimal? I'm gonna say just create it. Let's see what it comes up with. It's asking how we should store the icon. So let's just say a single file upload. How do we wanna store the pricing start? We don't really care about that. We'll just pick something. Yeah. Yeah. There's a there's a recommendation. So I think we can go with it. Yeah. It's collaborative. It's gonna ask you how you wanna do things. It's not gonna just bowl over your user and, like, do everything for you. It's it's not just an AI tool that's gonna do it for you. It is a human in the loop type of experience where you can make sure it's doing something you actually want it to do. And it's gonna tell you everything it's doing while it's doing it. So you can see here. It's planning it out. You can see the thinking if you wanna know, like, what it's thinking while it does it. And you can do the different models if you're you have a different one. We're currently just doing, GPT five. In those settings we set up earlier, you can change that out. Like, if you want something different going on, you can say which ones are allowed, because this UI is actually not just for your admin. You can allow other people here. So you can have other people with different access levels able to come in and modify your content once it gets created or your data tables. So you can let them use the AI assistant, and you can set what permission levels they have. So it's gonna ask for different tools like creating the collections if it can do it before it does it. You'll see here's our data models now. It's created a couple of them. We've got our case studies one. It's starting to add the fields into these. In the past, you would have to do this all manually and create collections. Now you can just use the AI to do it without having to worry too much. So it's a lot less manual work. But we could have done the same thing by using the MCP thing. Right? Yep. You can. Yeah. We just wanted to give people an in app way to do it a little faster and simpler for people who maybe aren't don't have a data model or AI model they like to use outside of here. Yeah. Definitely makes sense. It's just like in my mind, I really like the flow of using some like, I use Claude a lot. So when I use Claude, I'm like, okay. I want a complete end to end flow. Like, what what would it look like if I don't leave cloth? Can I actually do the stuff I wanna do? And it seems like the answer is yeah. But there's also And that's actually that's what I like to do as well as I'll go in cursor, and you could start the whole front and back end directly in cursor using the direct SMCP, and it can create it in here for you. We're gonna do the front end in Cursor just so we're seeing that too a little bit, but I wanted to use a little of the AI assistant as well. Yeah. Yeah. I'm actually in the process of making sure that our experience of getting started to be super seamless. So we already have, like, a regular CLI. We have, like, Asian skills, and, we're going to essentially keep shipping more, features through the CLI. So, like, it should be able to you say, like, oh, I wanna deploy direct us. It should be able to go through the templates marketplace, find the most up to date template deployed for you, and then it should be able to also figure out, like, the deployment is finished. Let me continue. And then you just have, like, a complete workflow where you don't even have to go to a templates page. So that's kind of, like, what's That's awesome. In the works. But yeah. Yeah. I'm excited for that. That'll be good. Yeah. That's I think it asked a question at the end. Yep. So now it is done. It's gonna, like if you've used this model, it constantly asks you other things it wants to do, but it is done. It's created the fields. So we've got that part done. Now we're gonna give it some fake content in these fields. So that's the database table. And now we're gonna fill these tables with actual, like, fake data. So I'm gonna paste that in. So when we go into here, you'll see no content yet, and it's gonna start filling those in. But you can see, like, these are the fields. So if a nontechnical user came in and wanted to give it a new name or a new tagline for their website, they can do it in here without having to touch the code at all. So it's a nice way to work with both your front end team and your back end team and your content team and not have, people who are super technical not able to get things done. While this is doing it, we are actually gonna get finished setting up for our MCP, actually. So we're gonna go here in our docs. We have a little section for setting up the MCP in different tools. We're gonna use cursor. So I'm gonna click add to cursor here. Nice easy button, and it will set us up into our settings. And we are going to go grab our URL from here. So this is our directest instance URL, and we'll pop that in here for our MCP setup. So it's just your instance URL slash MCP, and then you're gonna wanna set up a token. So let's make sure it has the permissions it needs. When you're connecting to external tools like cursor, it's always a good idea to scope your permissions so that it's not able to completely nuke your whole setup. You know, AI likes to be overeager. So Yeah. Yeah. It's like, let's start all over. Yeah. We need a fresh start. We we recommend setting up a token for it specifically and not using your admin token. So you wanna go to your user roles here. You'll create a new role, name it whatever you want, and you'll add a policy. We're gonna give it view only policy. So you'll add your collections, give it read only access. That way it can't, like, update or delete or anything when it's in the MCP. If you want the MCP to do that, you can totally give it permissions to create, read, and update, and do whatever you want with it. But I recommend giving it, like, the lowest level of access at the start so that, you know, it's not gonna go in and break your whole setup here. Mhmm. So we've got our policy. I'm gonna create a new user too to grab a token from. So if we scroll down to our tokens, we're gonna generate it and save. And then we'll go back in here, and that's where you put in this bear here, authorization code. You put that token, click install, and that will install our direct as MCP encursors. So now we're ready to go for that when this is finished. So let's go check on it. So what we've done is create a role, and then the role has the token? Yes. So the the user is what who has the token. So this MCP user has the role that we created. I created it all within the same page, but this person has the role, and then they have the token. Mhmm. So if you go into our settings here and look at the MCP role, we've got our view only policy that we set up Mhmm. For our different collections. And the users in the role is that MCP guy. So now it looks like it did finish the content. If we go back into our content page, now you can see it's filled it in with our sample data. I gave it a few guidelines of the type of things I wanted it to have, but, otherwise, it just kinda came up with this on its own, sample content. So it's given us a starting point. So now that we've got some sample content, we are going to go back in to cursor. I'm gonna grab this really long prompt I've created telling it basically we are an AI agency. We're creating our website. I gave it I told it to use next. I also made sure to tell it to use the Directus MCP server. This might take a minute, so I'm gonna go ahead and get it started. We are in a completely empty folder here, so I'm gonna have it do all of the coding for me. Let it create what we need here. It's gonna make sure to look at our schema via the MCP. So we want it to not guess on what structure we have of our data. We want it to actually look at it. Like we said, we could create the data directly from Cursor via the MCP. You would have to make sure you give it, the right permissions. So that that might have to be an admin token in order to generate new schema with your MCP. So if you're comfortable doing that, you totally can. Interesting. So once that's done, we'll have something that's working like a front end running locally that's fully functional, pulling data from our deployed direct us instance. Correct? Yep. Okay. Yep. And any changes you make in direct us would be reflected in your local front end that you've got here. And then as the final step, we are going to do to deploy this front end to railway so that you have a full production application front end and back end. Yeah. I have cursor set up, so I could just automatically runs everything. I don't, like, manually approve stuff. I just I don't trust it. Yeah. I gotta make sure I know what it's doing. I've been burned before. I don't know. The the models are they keep getting better and it's like, I'm like, I would've done the same thing. So I don't know. I feel like it's just faster and you can just like, you know, sit back, relax, grab your favorite drink while it gets to work. You come back to it and you're like, woah, this thing actually works. So Yeah. I think I'm a bit of a control freak. I can't let it just just go. I'm telling you, like, once you let go and you see, like, it actually works and it's like, I think, I think you'll be plenty surprised. Then you're like, wow, that's actually a really fun. Cause like now, you know, if let's say for whatever reason, someone brings you on Slack, you can play for guys. Like, you come back to us, like, oh, I forgot to approve. Yeah. That it's done. Yeah. Yeah. I I I think, it's a much more fun experience when you just let it kinda do its own thing without you steering it. But I do understand the, appeal of, you know, wanting to see what it does. But also, like, we're starting from scratch, so there's little, like, room for error, I would say. But yeah. There's nothing for it to break. It's creating it from scratch. So Yeah. Yeah. We're pretty comfortable letting it just do what it wants to do here. So it looks like it actually has gotten the types if you look. So it pulled this from our MCP. It checked in, Directus, and it got our back end and what types we have there. So that's good. Looks like it's I did tell it also to create a little connection a net little connector to direct us here, a fetcher. So it looks like it's done that, getting different gets to get the data. And it will be able to pull in our deployed, like, like, URL and actually set it as an environment variable? Yes. It will. So I it might make me make the e n v, but I've got it. It's supposed to use the direct URL here, and it's supposed to use the token that we created. So, we'll see. I've done this a couple times just to see how it would do, and it it doesn't always know to grab the URL. If it's really smart, it can grab it from the cursor settings, and it can get the token and the URL, but it doesn't always manage that. You'll notice I'm in auto mode, so it's a different model each time, so it might not be a very smart model. But we at the end, we'll give it the proper e n v variables, and it'll be able to connect to our deployed instance. Excellent. Yeah. Honestly, again, I I like my to choose my own models, You should use, like, a, you know, g p three five point four or, like, an Opus 4.6 and because I don't know. I don't like that, I would say I wouldn't trust the auto. Like, which one would affect? Like, I wanna be I want a consistent experience. So then if, like, anything starts feeling weird, then I can blame the model. I was like, oh, you know, like, Opus is acting really dumb lately, that sort of thing. So It it does just feel like from one day to the next, though, even if you have a model that you know is good, it it doesn't always behave exactly the same. Yeah. Sometimes it feels like, you know, you're you're just rolling the dice. Sometimes it's like, woah. This is it. Like, this is the next big thing. I was like cause like, sometimes I, I, I asked it a question and I wanted it to check against like, you know, our internal, GitHub repo. And it's like, Hey, do we support this thing? It was like, yes, you do. And I told her like, oh, show me the code. It's like, after checking, apparently, this is not supported. I was like, come. Yeah. And this is like the smart and I told it, like, oh, you know, UltraThink, you know, work really hard, but it's like, I guess sometimes it's just, you know, not that type of day. But I think here it's like it's it's it's working well, based on what I'm saying. Yeah. It looks like it's getting something. Yeah. It's messing around with the configs now. Interesting. What is what's it doing to the Next. Js config if we open it up? Just out of curiosity. Technically, you know, we're we're not supposed to look at the code, you know, and, because, you know, I was just shipping it, but I'm also curious. Okay. It's setting, like, the assets URL, I assume, for, like, files and stuff. That's pretty awesome. Yep. And while it does that, I'm actually gonna grab my last prompt, which is a prompt to have it, push to GitHub so that we can get it ready for deploying the front end. I'm gonna grab that. Go back in here. I think it's almost done. It feels like it's it's doing the read me. So Yeah. Probably at the end. But yeah. I would say, there is actually a like, you you can definitely, you know, start with this workflow. It's totally fine. But if you have, like, the AOS CLI setup, it could just deploy it to your existing project. And this way you don't even have to worry about git being installed, get, having a GitHub repo or anything. But when you have it with git, well, the benefit is, anytime you make, changes through the front end, it will just automatically deploy. So it's like it's it's all trade offs based on, you know, how, structured and organized you wanna be. But yeah. Yeah. That makes sense. Alright. We did not create our E and V, so we're gonna create that real quick. And I would say that's the responsible thing for it to do. Yes. I don't didn't really like that it created its own ENV and, like, grabbed my things from the MCP. That was a little, uncomfortable. Yeah. Yeah. It then gets better this way. Yes. Look at our URL. Alright. And then we're gonna run it locally just to see it working. What does the static token do? So that's the one we created with for the MCP. We're using it here too. Okay. So that is the permissions that we allow in Directus. So if you give it, like, no read permission, it won't be able to see the content. If you give it access to read, like what we did, we gave it access to read all of these collections, then it can read the data. So that's where, like, you probably want a separate one from your MCP. We're just in the interest of time having it Yeah. One that is view all for all of our collections because we don't need to make changes with the MCP. But you probably would have permission to update and delete with your MCP as well. So you probably want wanted a separate one for your front end. So you'd probably wanna create a user role for your front end that gives it different permissions. Otherwise, you can have it set up to use your public permissions, which controls what the API data is, available without authenticating. But it's safer if you do give it a token so that your front end is only able to grab what you want, not, like, everything. So let's see if it worked. Moment of truth. Okay. There it is. Looks like some images are broken, but we've got a full website here. You can navigate around. It's got some prompts. It's got a button to get in touch. We've got a full website here. Obviously, you'd wanna iterate on it, fix the images, and, do a little honestly, it looks great. It doesn't need too much design or anything even. So that's a good starting point. We are going to go ahead and say good enough. The images can get fixed later. We are going to deploy this. And, of course, I copied other things, so I need to go back and get my prompt. So like you said, there's other ways to do this, but we are gonna have it pushed to a GitHub repo. We're pretending we have other people who wanna manage this code, and we want it in some place that we would have access to it as a team. So we've have a GitHub repo we've already set up. It's empty right now, and we're gonna push it into here. So I'm having it create a readme that'll tell users, our other team members, basically, is in our example here. We're deploying to Railway. Here's what you'll need. It'll mention the EMV variables we created here, because we're gonna need those. I'm actually gonna copy them now, so I have them ready. And then it it's gonna make sure everything's building properly with the prompt I gave it, and then it will push to GitHub for us. Yeah. And I noticed it said, like, even if you don't have, like, the environment variable set up initially, like, it should still deploy. It should still work. But once we actually set the these values, we'll be able to actually fetch the thing and have the same result we have locally, but actually have it live and deployed on a URL. Yep. It's updating the ReadMe. Yep. There's our ReadMe. I remember the times where I had you know, I used to write stuff by hand. Yeah. Now I just be like, you know, just instructions, just lines, like update, read me, and that's it. You know? Just just figure it out the rest. Alright. I think it's gonna commit. Yeah. I think it's already I believe I ran already a built. So, yeah, we see, like, the dot next directory. Maybe the build was under there. Yeah. It's running get the commands. Okay. I think it has committed everything here. So now What did I do? I think it's just the wrong link. Yeah. It did. Okay. So we are gonna now go in here. We're gonna add a service. Yep. GitHub repository. And what did I call it? Was it like AI agents or something like that? AI agents. Not sure. Oh, boy. Hold on. I pasted it down here. You should be able to see, like, all repos, but maybe you have too many. So, like I have so many. I really need to clean it up. Yeah. Yeah. That's me. Okay. Hold on. I'm gonna Give me to refresh. Yeah. Yeah. Just don't wanna have my mega list of repos up on the screen so one's. But I think you might need to refresh the, so I tried to push the code. Right? Oh, it didn't. Yeah. I don't know if it pushed. I mean, I guess you need to stop. Yeah. See It's getting me with the permission things. You're right. I should just let it do whatever it wants. Yeah. But now it's I don't know if it's pushing it, and then it should be good. So now if you go to right way, I would say if, if you refresh, you should be able to see it. Yeah. We have code. The code is now here. Yep. Now when you add, we might need to like refer, I think it should just fetch, but you can try. Does it not like my Maybe just try refreshing, like, the, entire Page. Dashboard. Yeah. Yeah. Just in case. Yeah. Okay. And Okay. Let's try again. Interesting. I mean, if you want, what you could do is maybe it's just an issue with, like, get up not pulling. So, like, you can copy the full URL. And when you add, you can just paste that URL. Okay. The it looks like it found it like that. So let's Yeah. Yeah. Alright. So that is how you connect it, and then we're gonna just need to hit deploy. Yep. Yes. But I wanna put it up by the other ones first. Yeah. And that was in there. Yes. So I wanna also add our e and v variables before we get started. So they're actually automatically inferred, like, at the bottom. Oh. You can see them. You were right. Suggest variables, and then you can update these values and click add, then you're good to go. There you go. So let me grab where I pasted them over here. You can also have, like, go the route you were going through, like the raw editor, copy and paste that other thing, but just just wanted to point it out. Because, like, to me, it's like, it's one of those things that when you don't know it, you're like, oh, that's neat. That's kinda how I always felt about it the first time I tried it. Yeah. You're right. That is cool that it already kind of pulled up what we needed. But, yeah, you could also open raw editor and just paste in the whole EMV file that we had ready. Yep. And then you can click add to add everything. Yep. So these this is the token we have in our local one, and then this is the URL of the same thing in Railway. You guys can also do internal URLs. Right? Is that something we could use here if we wanted to use the local URL of this? So you can do, like, a dollar sign, two curly braces, and then you should get auto complete for, like, the URL. So, actually, you will be able to see a line where they're both connected if you wanna try it out. And then what will happen is, now whenever if the URL changes, it will just automatically update the other one as well and redeploy it. Okay. So that's like using the internal URL reference variable. So I I believe you the way it would work is you just do, so the on directives and the variables themselves, like, if you go to variables. I believe what's the yeah. I guess it's public URL is the value that we will want. So wait. If you go now to the AI agency service and then go to the next public directors URL, because that's the URL value you wanna set. Right? So that more menu and then edit. And then if you do, dollar sign and then double curly braces, you'll be able to actually pull in so I believe the name of the service is directed. So you just do direct us, and then it's gonna be dot yep. Do you see? And then you just choose the public URL Yep. From this list. Got it. So now when you choose it, it's just gonna automatically give you the right URL and it will just be able to read it. So if the other one updates, you just do it. But for this, you're gonna need to hit redeploy again. So it will take some time. So I'd say we can do this after you can cancel. But yeah. And I would say there's actually another cool feature because might as well, if you go to settings, like, okay. It's done. So we can actually test the thing. But we can update, like, the domain to, like, a actual domain because you can now buy domains on railway. Nice. Okay. Yep. So this will give to you just a domain for, the front end itself, and it will have, like, a .up.railway.app at the end. You'll need to specify a port, which I'm not sure what it is on. I I think it should be 3,000. Like, 3,000 should just work. Yeah. Yeah. And if we visit it, might be the incorrect port. You could try eighty eighty as well because that's what I was suggesting in the beginning. Yeah. There we go. There we go. So there is our front end deployed. Yep. And we have a live app. Yeah. Awesome. So that was pretty fast. We went from nothing to a fully deployed front and back end here. Let's see. Was there anything else we should know about Railway and getting this set up here? I mean, if you hit deploy now, it will just redeploy, and you'll actually be able to see on the canvas. There's, like, an arrow, and this, service, like our front end is, referencing the direct us. And that's how you have, like, a connection between the two. And, yeah, other than that, it's like, you can just have everything in one place. Again, like, if you go now to the AI agency service and you click on settings and you also go to networking, from the right. Yeah. And then you can actually choose a custom domain. So when you click on it, you should actually be able to buy a domain straight from here. So like, if you come up with that domain, you can just do it from here as well. Or if you go to like ferry.com\u002Fdomains, you can do it. But yeah, I would say that's kinda like you now have fully like everything in one place, but you know, took us not a lot of time, and we have, like, a fully, fully deployed working back end, front end. Just, yeah, everything is in one place. Yeah. So why don't I recap what we did? We started with a simple idea launching a fictional AI automation agency. We launched infrastructure on Railway. We generated a structured back end with Directus. We populated it with fake content. We generated the front end using Cursor. We deployed the whole thing back to Railway. That's the build it part of today's theme. The key takeaway is that when interfaces can be generated quickly, the back end data model becomes the strategic layer. If someone watching wants to try this themselves, what's just the easiest way to get started on the railway side of things? The easiest way, I would say going to the template you shared would be the easiest place. And if they, you know, wanna use direct this. And also, by the way, I forgot to mention. So if you actually, like, close the, service here, there's like an agent in the top right corner as well, where you can actually also ask questions about your stuff, like actually ask it to make changes and it should be able to make changes, directly. So I believe if you just say like, oh, update, make this update, it should figure things out on its own. Awesome. So you guys got some AI in here too? Yeah. Yeah. In case someone wants everything in the dashboard, they can, but also if they want, the, to use whatever their coding agent of choice, they should be able to do it. But yeah. Cool. Well, thank you for joining us. I hope everybody has a good rest of your Leap Week. Sounds great. Thank you so much for having me. Yeah. Thank you for joining us.","3d92d748-e9d2-4d03-8a5b-cc78b8273739",[165,166],"533d89f7-b567-47f2-8dbf-940e4ff396e0","37090185-0684-40ef-97c0-e860f2b2df77",[],{"year":139,"number":140,"id":141,"episodes":169,"show":170},[143,144,145,146,147,148,123,149],{"title":151,"slug":152},{"id":144,"slug":172,"vimeo_id":173,"description":174,"tile":175,"length":176,"resources":12,"people":12,"episode_number":177,"published":130,"title":178,"video_transcript_html":179,"video_transcript_text":180,"content":12,"seo":12,"status":19,"episode_people":181,"recommendations":185,"season":186},"the-build-off","1176279072","Three builders. Three AI tools. One backend. Who can ship the best frontend in 30 minutes?","4059a3df-317a-4665-9a4a-9f5f2548ca07",47,2,"The Build-Off","\u003Cp>Speaker 0: What's up, everybody? It is great to see you, to meet you virtually here. My name is Matt. I'm on the marketing team here at Directus. And if you're watching this, you're in for a treat because we have quite the repertoire of guests here today, also from the Directus team, and we're gonna be doing something pretty fun.\u003C\u002Fp>\u003Cp>But first, let me introduce each guest. You know him. You love him. You've seen his face all over the place. It's John Daniels.\u003C\u002Fp>\u003Cp>Speaker 1: John Daniels.\u003C\u002Fp>\u003Cp>Speaker 2: What's up? Glad that you\u003C\u002Fp>\u003Cp>Speaker 0: started with me.\u003C\u002Fp>\u003Cp>Speaker 2: I was worried you were gonna say Brian. So hello.\u003C\u002Fp>\u003Cp>Speaker 1: Your face is all over the place right here.\u003C\u002Fp>\u003Cp>Speaker 0: I don't think anybody really knows who Bryant is. So, no. No. No. No.\u003C\u002Fp>\u003Cp>Of course, we have the man himself, mister Bry. Bryant Gillespie, who's one of our esteemed developer advocate type folks, I guess, on the team. You know? He kinda does everything.\u003C\u002Fp>\u003Cp>Speaker 1: Type folks? Yes. Happy to be here. Very excited for this. Look.\u003C\u002Fp>\u003Cp>I'm just gonna start this off with saying whatever we're doing in this competition, I'm gonna mop the floor with you guys. That's it.\u003C\u002Fp>\u003Cp>Speaker 0: Strong start. I was gonna do this not in the spirit of competition, but in Oh. In in friendliness, but I guess that's what we're gonna do now. Alright. And if anybody doesn't know, John is one of our AEs here at Directus, so the most competitive of of everyone here.\u003C\u002Fp>\u003Cp>So I'm sure pretty sure that'll awaken a fire in a moment. So, yeah. Brian, what are we doing today? I\u003C\u002Fp>\u003Cp>Speaker 1: this is your show, my friend. You tell me what we're doing. Clearly, it's something to do with an AI skills directory because that is what's on the screen.\u003C\u002Fp>\u003Cp>Speaker 0: That is what's on the screen. Alright. I'll give you the brief rundown. So today, we are gonna have a build off. Everybody's been talking about front ends and UIs, and the errors are shifting.\u003C\u002Fp>\u003Cp>And there's so many tools out there. I'm a user of Cloud Code for a lot of the marketing stuff that we do here at Directus, including building the Leap Week landing page. Our team uses codex. Our team uses a lot of the out of the box, like, harnesses, like cursor. And we also use things like Lovable.\u003C\u002Fp>\u003Cp>So it's a good mix of things, but that's a lot of subscriptions add up. So we thought, why not put the skills to the test and see which we can use best for building the front end on a Directus back end, which would be a skills directory. And one of the reasons we wanna do a skills directory is because there's a lot of shady skills directories out there and there's a lot of mistrust in a lot of them. So our developer team has a lot of, skills that they share, that we thought would be cool to build something along with that, as well as our go to market team uses a lot of things too. So, yeah, that's really the gist.\u003C\u002Fp>\u003Cp>Did I\u003C\u002Fp>\u003Cp>Speaker 1: What are the what are the stipulations for this, man? We're building a skills directory front end. We're gonna need a back end for that, which we'll do, but what are the rules?\u003C\u002Fp>\u003Cp>Speaker 0: There are no rules. There's only one rule. And that is\u003C\u002Fp>\u003Cp>Speaker 1: that's details?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. There's gonna be one rule where we have to create the back end and direct us. And then whatever tool we're gonna use, we are going to use the same back end for that front end. So running down the list of tools that we will use, I will be using Lovable. It's something I've used in the past.\u003C\u002Fp>\u003Cp>I've I've not actually used it in quite a bit, because I've been using, Cloud Code. But, Brian, what are you gonna be using?\u003C\u002Fp>\u003Cp>Speaker 1: I'm gonna be using Cloud Code.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. John, what you got?\u003C\u002Fp>\u003Cp>Speaker 2: I use my old dependable cursor.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Is cursor what you built? The, garage inventory?\u003C\u002Fp>\u003Cp>Speaker 2: It is. Yeah. Yeah. And I already have a I already have an idea.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, boy. Oh, no.\u003C\u002Fp>\u003Cp>Speaker 2: Retro. It's a lot yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Front end directory.\u003C\u002Fp>\u003Cp>Speaker 2: It's a long\u003C\u002Fp>\u003Cp>Speaker 1: Here we go.\u003C\u002Fp>\u003Cp>Speaker 0: I'm doing that maybe more than, like, moving forward in my chair.\u003C\u002Fp>\u003Cp>Speaker 1: Cool.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Well So Right. To kick things off, I'm assuming we'll kick we've started.\u003C\u002Fp>\u003Cp>Speaker 1: We we got a totally blank data model here. We'll just call it skills directory. Right? John is just banging on the keyboard, man. Alright.\u003C\u002Fp>\u003Cp>Totally empty. It's like you know, we'll we'll do this 100 app style. Like, what what what do we even need on a skills directory? Right? You're gonna need some skills, but what what is the what are the other stuff that you're planning on tracking here?\u003C\u002Fp>\u003Cp>What do you need for the back end?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That's a good question. When you say skills, what does that encompass? Is that like the title and the description and the actual I mean, skills are basically just a fancy word for markdown files. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I mean, that's, yeah, that's the the funny thing about all of this stuff to me is it's all just markdown. Like, been been dealing with markdown since I was 12. A week ago. Yeah.\u003C\u002Fp>\u003Cp>So we got a name, a description for the skill. You've got the, what, the actual skill content. I don't I don't\u003C\u002Fp>\u003Cp>Speaker 0: know.\u003C\u002Fp>\u003Cp>Speaker 1: Are you I'm assuming you're gonna have, like, users who submit the skills?\u003C\u002Fp>\u003Cp>Speaker 0: Mhmm. We'll have I think, at some point, if we wanna\u003C\u002Fp>\u003Cp>Speaker 1: Submit a bug.\u003C\u002Fp>\u003Cp>Speaker 0: Public facing, but for our internal team makes sense. You probably want, some sort of, like, tagging system. So if it's engineering skill or if it's\u003C\u002Fp>\u003Cp>Speaker 1: So the tag name. Okay. What else you got?\u003C\u002Fp>\u003Cp>Speaker 0: Option for an image, if you wanna add that to the front end potentially.\u003C\u002Fp>\u003Cp>Speaker 1: Image? Okay. You're getting away from the markdown stuff now. I'm gonna just draw the arrows for fun here\u003C\u002Fp>\u003Cp>Speaker 0: at this point. Here we go.\u003C\u002Fp>\u003Cp>Speaker 1: Boom. Alright. So you got skills. You got tags. What else do you need?\u003C\u002Fp>\u003Cp>Speaker 0: I mean,\u003C\u002Fp>\u003Cp>Speaker 1: I Repository. GitHub repository. Is that where they're coming from?\u003C\u002Fp>\u003Cp>Speaker 0: Could you just host it within Directus itself, like, as a file?\u003C\u002Fp>\u003Cp>Speaker 1: I mean, we can. Yeah. Like, do you want it linked to somewhere?\u003C\u002Fp>\u003Cp>Speaker 2: I think\u003C\u002Fp>\u003Cp>Speaker 0: I wanna keep it, like, self like, contained within the one\u003C\u002Fp>\u003Cp>Speaker 1: thing. Okay. Alright. Cool. Is this anything else?\u003C\u002Fp>\u003Cp>Or is it we're just gonna roll with this.\u003C\u002Fp>\u003Cp>Speaker 0: Just keep it simple. Unless you got anything to do. Wait.\u003C\u002Fp>\u003Cp>Speaker 2: No. Keep it simple, stupid.\u003C\u002Fp>\u003Cp>Speaker 1: Which one is stupid, man?\u003C\u002Fp>\u003Cp>Speaker 2: You probably said it.\u003C\u002Fp>\u003Cp>Speaker 0: Alright.\u003C\u002Fp>\u003Cp>Speaker 1: I've got this. So, hopefully, I've got some secret sauce here. Been working on a CLI tool. So I'm gonna tell Claude code here. Can you guys see this?\u003C\u002Fp>\u003Cp>Should I zoom way in? Alright. Run d six s agents. It's our CLI for connecting to direct us.\u003C\u002Fp>\u003Cp>Speaker 0: D six s. It's the first time seeing the CLI in action right here.\u003C\u002Fp>\u003Cp>Speaker 1: Very nerdy. You know, the Kubernetes deal. What is this thing doing? Why is it not running this command? It's reading four files, which d success.\u003C\u002Fp>\u003Cp>Just run the command, dude. What are you doing?\u003C\u002Fp>\u003Cp>Speaker 2: Chose the wrong tool. Maybe cursor's been\u003C\u002Fp>\u003Cp>Speaker 1: Maybe you chose the wrong tool. Yeah. There it is. Okay. So, basically, that command gives it a little context about this.\u003C\u002Fp>\u003Cp>Cool. And now this thing should be primed to go. Agents. Right. Cool.\u003C\u002Fp>\u003Cp>Alright. So now we're gonna kick it over into plan mode. I'm gonna paste that image that I had. Right? And we're just gonna say, hey.\u003C\u002Fp>\u003Cp>We're building an AI skills directory. Here's a screenshot of a data model I'm competing with John and Matt. We need the back end in Directus first before we build the front end. Can you plan out the Directus back end?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Alright.\u003C\u002Fp>\u003Cp>Speaker 1: I've been using Let's see what happens here. Okay. Right here. Alright. It is going to alright.\u003C\u002Fp>\u003Cp>This is where I tell you to do as I say, not as I do, because I'm just gonna turn this thing loose. Right? I should have actually set dangerously set permissions here. But\u003C\u002Fp>\u003Cp>Speaker 2: I don't even have it ask for permission anymore. I just let it go.\u003C\u002Fp>\u003Cp>Speaker 1: Go, baby. Go.\u003C\u002Fp>\u003Cp>Speaker 0: I have to leave this a lot. That's probably why.\u003C\u002Fp>\u003Cp>Speaker 1: If you can't leave that out, security infraction.\u003C\u002Fp>\u003Cp>Speaker 2: Security. Spent hundreds of dollars in tokens last month.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. It's going to write this plan. Are you so are are you not using cloud code at all? You're still using cursor every day?\u003C\u002Fp>\u003Cp>Speaker 2: I use both. Depends on what I'm doing.\u003C\u002Fp>\u003Cp>Speaker 0: So what so what you're you're basically just doing what the MCP used to do just from the terminal\u003C\u002Fp>\u003Cp>Speaker 1: So is the why the CLI? Right? Well, the reason why is because context. The context window. So MCP uses up a lot of context, for comparison.\u003C\u002Fp>\u003Cp>Right? Like, if you have all the tools enabled, MCP or our AI assistant right now could use up to thirty, forty thousand tokens to have all the tools enabled. The CLI, by default, doesn't load anything into the context, and the agent is or like CloudCo, these agent harnesses are pretty good at navigating, like, CLIs just because they're they're trained on a lot of bash scripts and stuff like that. So, you know, it basically you keep the context window clean until you need it, and, yeah, you follow this progressive discovery pattern. I'm yeah.\u003C\u002Fp>\u003Cp>I've been talking. I'm not even looking at this plan. Hopefully, this thing actually does what we wanted to do. We shall see. Right?\u003C\u002Fp>\u003Cp>Actually, use recipes. D success recipes. This is should be a shorthand. Again, this is all highly experimental, so failed to log the plug in, so it's not gonna use that at all. Plug ins.\u003C\u002Fp>\u003Cp>Index. Oh, good. Alright. Well, just push forward. We may be kicking back to the MCP for this.\u003C\u002Fp>\u003Cp>Speaker 0: Hey.\u003C\u002Fp>\u003Cp>Speaker 2: Alright. Is what I when I type in the when it when MCP goes wrong for me, I say, just do it, and it'll it'll figure it out.\u003C\u002Fp>\u003Cp>Speaker 1: Just do it. Figure it out funny, man.\u003C\u002Fp>\u003Cp>Speaker 0: Turn off permissions and just go.\u003C\u002Fp>\u003Cp>Speaker 1: What is this thing doing? Your token doesn't have admin access. I do have admin access.\u003C\u002Fp>\u003Cp>Speaker 0: No. You're wrong. You said, highly experimental. Correct?\u003C\u002Fp>\u003Cp>Speaker 1: I mean, it it was working well. Works on my machine until you start recording. Right? Right. So we'll just go back to the old tried and true here.\u003C\u002Fp>\u003Cp>Both collections exist, but I have no schema. Oh my gosh. This thing is hopelessly lost.\u003C\u002Fp>\u003Cp>Speaker 2: Lost. Hey. There's two collections in there now.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. They're actually just folder collections because they don't exist. Alright. Skills. When I say tried and true, I mean me.\u003C\u002Fp>\u003Cp>Alright. What do we have there? We have name, description, content. Name.\u003C\u002Fp>\u003Cp>Speaker 0: I've never seen anyone move this fast inside of the.\u003C\u002Fp>\u003Cp>Speaker 1: Content. Just markdown. It's just markdown. There you go. Alright.\u003C\u002Fp>\u003Cp>You got tags. Yeah?\u003C\u002Fp>\u003Cp>Speaker 0: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 1: Cool. Tags. I have a name for the tags. Tag name. Cool.\u003C\u002Fp>\u003Cp>And then we need a skill tags. We need a junction table. Cool. I'm taking the long way around here. Skills.\u003C\u002Fp>\u003Cp>Now I'm gonna go to the mini mini relationship inside directions. It's been a minute since I've done this, man. Don't you miss it? Always I do miss it. Yeah.\u003C\u002Fp>\u003Cp>It took me an hour to do half of this stuff that I could now do in, like, five minutes using the AI tools when they work, mind you. Skills ID, tags ID. Let's just call it skill. Let's call it tag. Skills.\u003C\u002Fp>\u003Cp>Cool. Give it a sort field. Boom. Boom. Boom.\u003C\u002Fp>\u003Cp>Speaker 0: Boom. You know, it's worth that messing up to watch you do this. It's like watching Boom.\u003C\u002Fp>\u003Cp>Speaker 1: That's it. There's the back end. You're good. Golden. Very simple.\u003C\u002Fp>\u003Cp>Very easy. Now what?\u003C\u002Fp>\u003Cp>Speaker 2: Now we need some data.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm. Maybe it can do that. Look, I I set it all up. Nice. Thanks for nothing.\u003C\u002Fp>\u003Cp>Now I need you to create some sample data for us, though. I mean, I'm a make sure you rub it in the face of the thing. Right? Talk to me well, I this might take a minute, but,\u003C\u002Fp>\u003Cp>Speaker 2: like, what are you guys Like, when you\u003C\u002Fp>\u003Cp>Speaker 1: are are we supposed to talk about what we're gonna build? Like, how the front end is gonna look, or is this gonna be, like, a secret thing?\u003C\u002Fp>\u003Cp>Speaker 0: We'll talk about, you know, how things are going. I'm still I'm between a couple ideas on what I want this front end to look like. I I want it to not look like a generic directory because there's, like, directories of directories out there. I already\u003C\u002Fp>\u003Cp>Speaker 2: have an idea, so don't even don't even worry about Wait.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Was it one that we talked about earlier, or is this one this is completely new?\u003C\u002Fp>\u003Cp>Speaker 2: I got a new idea.\u003C\u002Fp>\u003Cp>Speaker 0: Interesting. Okay. I think I'll have an idea.\u003C\u002Fp>\u003Cp>Speaker 1: You think you'll have an idea? Alright. Well, it's got there's eight tags. There's six skills. Let's add 10 more skills.\u003C\u002Fp>\u003Cp>Create a few sample users as well, give them a different role. Just the ability to view skills and update the ones they've uploaded. Alright. So now we're gonna move to the the front end stuff. Yeah?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I think so. So we're all operating off of the same direct us back end, and you just created a few users. I'm assuming that's to give us access to that data.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. So you guys have your own users in there. Looks like it's created\u003C\u002Fp>\u003Cp>Speaker 2: Only one guy cares about a photo.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Oh my gosh. Alright, dude. Fine. Fine.\u003C\u002Fp>\u003Cp>Fine. Fine.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Boom. I'll update my You\u003C\u002Fp>\u003Cp>Speaker 1: look you look rather young in that one,\u003C\u002Fp>\u003Cp>Speaker 2: John. Thanks.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So what do I dig at all.\u003C\u002Fp>\u003Cp>Speaker 0: Thank you. Alright. So each of us is gonna have our own role, and you will provide that role\u003C\u002Fp>\u003Cp>Speaker 1: to us.\u003C\u002Fp>\u003Cp>Speaker 2: If you refresh real quick. Sorry. Refresh bright. There.\u003C\u002Fp>\u003Cp>Speaker 0: Of course.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. We're reading we're watching this. You're certainly not watching this on Saint Patrick's Day. We're recording it on Saint Patrick's Day, though.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Yeah. Yeah. Well done.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That one.\u003C\u002Fp>\u003Cp>Speaker 2: Sorry. Continue, Matt.\u003C\u002Fp>\u003Cp>Speaker 0: How do you have access to this already? I\u003C\u002Fp>\u003Cp>Speaker 1: I already gave him access. I put it in the chat. I sent you guys up already. Your direct us email and your password. What's the password?\u003C\u002Fp>\u003Cp>The password is Password. Now, hopefully, one of us will remember to have removed that by the time this recording goes live.\u003C\u002Fp>\u003Cp>Speaker 2: I'll tell him how to change\u003C\u002Fp>\u003Cp>Speaker 0: it. In the chat. Must be what is this\u003C\u002Fp>\u003Cp>Speaker 1: thing doing. Okay. I was creating, like, 35. Why can't I not just accept all here? Good lord.\u003C\u002Fp>\u003Cp>Speaker 0: Shoot me the shoot me the URL in the chat. I don't see it in there.\u003C\u002Fp>\u003Cp>Speaker 1: It's a imigos.directus.app.\u003C\u002Fp>\u003Cp>Speaker 0: Got it now. Alright. Alright. I'm in.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. Alright. Alright. Alright. Invalid input.\u003C\u002Fp>\u003Cp>Sarah Chen. Why does it keep trying to I don't know. It really likes the name Sarah for whatever reason.\u003C\u002Fp>\u003Cp>Speaker 0: Hey. I got a crush.\u003C\u002Fp>\u003Cp>Speaker 1: Marcus, Priya, Sarah. Those are the folks who are uploading the other skills. Alright. So we're good then. Yeah.\u003C\u002Fp>\u003Cp>There's gonna be some skills that populate in there, and then everybody is jamming on the back end. How long are we do we have on the back or on the front end? Right?\u003C\u002Fp>\u003Cp>Speaker 0: I set a timer. We have, like, two minutes now, but, if we wanna take ten minutes, you know, and\u003C\u002Fp>\u003Cp>Speaker 1: do what we Ten minutes to do a whole back end or a front end?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah, man. I mean, it's the UI error is changing, so they say.\u003C\u002Fp>\u003Cp>Speaker 1: I at least, like, 15. Yeah?\u003C\u002Fp>\u003Cp>Speaker 0: 20 to 30.\u003C\u002Fp>\u003Cp>Speaker 1: We could do, like, a supercut.\u003C\u002Fp>\u003Cp>Speaker 0: We'll extend this session a little bit. We've got some time.\u003C\u002Fp>\u003Cp>Speaker 1: Do a supercut. Yeah. Alright. Twenty minutes then. That's it.\u003C\u002Fp>\u003Cp>Twenty minutes. No more. No less.\u003C\u002Fp>\u003Cp>Speaker 0: I think You\u003C\u002Fp>\u003Cp>Speaker 1: guys better not be starting with the back end already or front end already.\u003C\u002Fp>\u003Cp>Speaker 0: Sure John's already done. I've been Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: He's already done. Hearing him. Alright.\u003C\u002Fp>\u003Cp>Speaker 2: I'm just right now, I'm just setting up the m I'm setting up my MCP and cursor.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. Well, you guys just tell me when you're ready to go.\u003C\u002Fp>\u003Cp>Speaker 0: I'm I'm pretty confident\u003C\u002Fp>\u003Cp>Speaker 1: in my skills.\u003C\u002Fp>\u003Cp>Speaker 0: Let's say we're using, like, you know, lovable or something. I've got my user here. I create It's got admin policy, create a new token, save said token, and should be good to pull data from that directly.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And just lovable have FCP?\u003C\u002Fp>\u003Cp>Speaker 0: I don't know. It's a good question.\u003C\u002Fp>\u003Cp>Speaker 1: Personal connectors. There you go, dude.\u003C\u002Fp>\u003Cp>Speaker 0: I see it. I sees it.\u003C\u002Fp>\u003Cp>Speaker 1: Here's the directisio docs. I'm gonna just point you in the right direction there. Connect AI, AI, MCP server. This is the page that you need from the documentation. I'm gonna paste that in the chat.\u003C\u002Fp>\u003Cp>Good luck to you, sir. You guys just all the skill the back end is done. The back end is locked. Don't even think about changing the back end.\u003C\u002Fp>\u003Cp>Speaker 2: Alright.\u003C\u002Fp>\u003Cp>Speaker 1: When you guys say go, I'm gonna hit go. Hit go. There it is. Alright. Twenty minutes, boys.\u003C\u002Fp>\u003Cp>Speaker 0: I'm I've gotta upgrade my lovable account because, apparently, Lovable doesn't allow custom MCPs unless it's It\u003C\u002Fp>\u003Cp>Speaker 2: sounds like an excuse, bro.\u003C\u002Fp>\u003Cp>Speaker 1: Already an excuse.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. One pro. I'm gonna spend this whole time setting up my custom MCP.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, no. Why can't I see the text here? Oh, that's not good. I'm going react on this one. Why can't I see this, though?\u003C\u002Fp>\u003Cp>I just got this new Mac, man. Still, if you guys beat me, that will be b y since we're just throwing excuses out there.\u003C\u002Fp>\u003Cp>Speaker 0: I am still signing a still I'm still going there by a sign up for a year.\u003C\u002Fp>\u003Cp>Speaker 1: I can't even get the I can't get the tan stack start script here from the their website.\u003C\u002Fp>\u003Cp>Speaker 2: Start.\u003C\u002Fp>\u003Cp>Speaker 0: Connection failed. Gotta love it. Alright. Well, mobile mobile is not really communicating with me right now. I'm having trouble.\u003C\u002Fp>\u003Cp>I hope you guys are having fun. I'm having a blast.\u003C\u002Fp>\u003Cp>Speaker 2: Matt, is your keyboard connected yet?\u003C\u002Fp>\u003Cp>Speaker 0: Yes, John.\u003C\u002Fp>\u003Cp>Speaker 2: Alright. Let's see. I got my build plan ready. Let's see if this looks good.\u003C\u002Fp>\u003Cp>Speaker 0: No. I don't think Lovable's gonna be the the move. Not great, Lovable. Not great.\u003C\u002Fp>\u003Cp>Speaker 2: Alright, guys. I've clicked build. We're gonna we're gonna get v one running. What if we finish in ten minutes? Does that give us bonus points?\u003C\u002Fp>\u003Cp>Speaker 0: I should be done setting up my MCB by then. So\u003C\u002Fp>\u003Cp>Speaker 2: Matt, why don't you have a rabbit in your background? Brian and I both have a rabbit in the background.\u003C\u002Fp>\u003Cp>Speaker 1: I have several.\u003C\u002Fp>\u003Cp>Speaker 0: Because I like to have my rabbits in my point of view. So not in the background. Alright. Well, local's not gonna work with me on the MCP, so I'm gonna have to roll.\u003C\u002Fp>\u003Cp>Speaker 1: You just just gonna have to roll. Can I just copy, Jace? You could just tell it to use the SDK. I think you\u003C\u002Fp>\u003Cp>Speaker 2: can copy.\u003C\u002Fp>\u003Cp>Speaker 1: I think\u003C\u002Fp>\u003Cp>Speaker 2: you can also just give it your API token and just give it access. I think I've done that before. You'd be like, hey. I'm an API token. Didn't know we'd get excuses so quick out the gate, though.\u003C\u002Fp>\u003Cp>Speaker 0: You know, usually, I'm using, like, whisper for this. Go for it. Okay. Then we\u003C\u002Fp>\u003Cp>Speaker 1: know what you're gonna build. Right?\u003C\u002Fp>\u003Cp>Speaker 2: Just meet your just meet yourself while you mouth it and then unmute.\u003C\u002Fp>\u003Cp>Speaker 1: I need a front end that looks great. Make no mistakes.\u003C\u002Fp>\u003Cp>Speaker 2: Make it look beautiful.\u003C\u002Fp>\u003Cp>Speaker 1: I I hope John's idea is something that looked like a pot of gold and a leprechaun.\u003C\u002Fp>\u003Cp>Speaker 2: No. But that would have been good. Dang. I think mine'll be good, though.\u003C\u002Fp>\u003Cp>Speaker 0: I I have no idea what this output of this prompt is gonna mean. I'm actually kind of terrified. I\u003C\u002Fp>\u003Cp>Speaker 1: know. I'm just reading my typing, and it's freaking terrible.\u003C\u002Fp>\u003Cp>Speaker 0: That's why he's was\u003C\u002Fp>\u003Cp>Speaker 2: The good news is AI doesn't care about typos.\u003C\u002Fp>\u003Cp>Speaker 0: What were the tags we put in here, by the way?\u003C\u002Fp>\u003Cp>Speaker 1: Research writing, design, automation, productivity, coding, marketing, and data analysis.\u003C\u002Fp>\u003Cp>Speaker 2: We should upload these to to, like, Vercel or Netlify or something afterwards so people can play with them, the front ends.\u003C\u002Fp>\u003Cp>Speaker 1: That should be that should be solid.\u003C\u002Fp>\u003Cp>Speaker 2: Here's the file being created right now. It's called bang effect dot JSX.\u003C\u002Fp>\u003Cp>Speaker 1: Bang effect. You may win this, man. I think you guys had more time to think about the ideas here.\u003C\u002Fp>\u003Cp>Speaker 2: Don't\u003C\u002Fp>\u003Cp>Speaker 0: yeah. I\u003C\u002Fp>\u003Cp>Speaker 1: don't have a solid idea.\u003C\u002Fp>\u003Cp>Speaker 0: Lost trying to set up the MCP server in Lovable.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, that's a risky run. You've got what what did we got on the clock? Thirteen twenty one.\u003C\u002Fp>\u003Cp>Speaker 0: Plenty of time.\u003C\u002Fp>\u003Cp>Speaker 1: Just just come on, Claude. Don't film me now. I like you, dawg. We got this. We got this.\u003C\u002Fp>\u003Cp>Speaker 2: Oh, first preview. It looks good.\u003C\u002Fp>\u003Cp>Speaker 0: Did you one shot it? Oh,\u003C\u002Fp>\u003Cp>Speaker 2: damn. This is okay. You guys are gonna lose.\u003C\u002Fp>\u003Cp>Speaker 1: He's blowing smoke. I know.\u003C\u002Fp>\u003Cp>Speaker 0: He's he doesn't have anything. It's just a blank screen.\u003C\u002Fp>\u003Cp>Speaker 2: I wish you guys could see this. No.\u003C\u002Fp>\u003Cp>Speaker 0: I'm trusting lovable with my lunch.\u003C\u002Fp>\u003Cp>Speaker 2: The sound effects are amazing.\u003C\u002Fp>\u003Cp>Speaker 1: You have sound effects?\u003C\u002Fp>\u003Cp>Speaker 2: The only problem is I can tell the words are backwards. I'm like, why are these words backwards?\u003C\u002Fp>\u003Cp>Speaker 0: What, wait. Tags. One, two, three.\u003C\u002Fp>\u003Cp>Speaker 2: I like how there's, like, duplicate tags.\u003C\u002Fp>\u003Cp>Speaker 0: Lovable has gotten a lot better on the user interface since all of us use it. Like, it quite looks nice, except for the whole connecting and MCP thing.\u003C\u002Fp>\u003Cp>Speaker 2: I wish you guys could hear these sound effects.\u003C\u002Fp>\u003Cp>Speaker 1: I think I can hear a few.\u003C\u002Fp>\u003Cp>Speaker 0: Did you do freaking lasers?\u003C\u002Fp>\u003Cp>Speaker 2: Oh, it's looking so much better now. Oh my gosh. This looks so good.\u003C\u002Fp>\u003Cp>Speaker 0: It's a risk. It's poker face.\u003C\u002Fp>\u003Cp>Speaker 2: No. You know it's a good a good project when you are playing the game that you've built.\u003C\u002Fp>\u003Cp>Speaker 1: I'm still waiting for dependencies to resign. Alright. You know me, Ben. If you enjoy watching people slide by on the skin of their teeth, please tune in to Directus TV for where yours truly embarrasses himself twenty four seven by trying to build apps. Oh god.\u003C\u002Fp>\u003Cp>This is going horribly wrong. I should not have should not have done React that is using way outdated stuff. We're just I I don't know. Tam, if you watch this, man, I don't know what happened to the website today when I was recording this, but\u003C\u002Fp>\u003Cp>Speaker 0: I think this is gonna be\u003C\u002Fp>\u003Cp>Speaker 1: Oh, there it is. Finally. It's okay.\u003C\u002Fp>\u003Cp>Speaker 0: You got something?\u003C\u002Fp>\u003Cp>Speaker 1: Must have been I the site must have been broken, man. I don't I don't know.\u003C\u002Fp>\u003Cp>Speaker 2: Okay. Now now it's playing its playing its game on its own. This is where where it's fun to watch Cursor play its own game.\u003C\u002Fp>\u003Cp>Speaker 0: You got your AI playing an AI? K. What are you doing over there?\u003C\u002Fp>\u003Cp>Speaker 2: Who? Me? Just kicking ass?\u003C\u002Fp>\u003Cp>Speaker 0: I'm gonna have to rely on a one shot here.\u003C\u002Fp>\u003Cp>Speaker 2: I think I might have this with a one shot. We'll see. It's still it's finishing up the build.\u003C\u002Fp>\u003Cp>Speaker 0: This is building out the comp I'm building the character select and move list UI right now, so we're gonna have to wait a a second. Did you create an RPG, John?\u003C\u002Fp>\u003Cp>Speaker 2: No. I created a a video game. Do you wanna know what its theme theme is?\u003C\u002Fp>\u003Cp>Speaker 0: I wanna wait and see.\u003C\u002Fp>\u003Cp>Speaker 2: Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Leprechauns?\u003C\u002Fp>\u003Cp>Speaker 2: No.\u003C\u002Fp>\u003Cp>Speaker 0: Pecauns. I think I you know, if I had more time, it would be cool to do, like, an RPG style directory where you have to, like, level up to get the better prompts. I wonder if anybody would spend time or not. Probably not.\u003C\u002Fp>\u003Cp>Speaker 1: I've been looking at this thing, and I'm just like, I don't even know.\u003C\u002Fp>\u003Cp>Speaker 0: Do you have anything? Do you have a UI at all, Brian?\u003C\u002Fp>\u003Cp>Speaker 1: I I have nothing on the screen.\u003C\u002Fp>\u003Cp>Speaker 0: That's not good. How much time do we have left?\u003C\u002Fp>\u003Cp>Speaker 1: Six minutes. Oh, man.\u003C\u002Fp>\u003Cp>Speaker 2: Guys, this you might as well just stop. I'm telling you, you're gonna lose. I know Matt basically has nothing. He's gonna show a white screen. It's gonna be like index.html.\u003C\u002Fp>\u003Cp>Speaker 0: We're gonna wait and ask I\u003C\u002Fp>\u003Cp>Speaker 1: have a header.\u003C\u002Fp>\u003Cp>Speaker 0: You have a header?\u003C\u002Fp>\u003Cp>Speaker 1: I have a header.\u003C\u002Fp>\u003Cp>Speaker 2: Match this line to us.\u003C\u002Fp>\u003Cp>Speaker 0: Have have I been in the roost this whole time? Who knows?\u003C\u002Fp>\u003Cp>Speaker 1: I feel like I do miss the old days where I just good old fashioned handwriting code. I probably yeah. On a 100 apps, hundred hours, like, the days of all, I woulda had something already that looked nice. I'm not super happy with what I've got here.\u003C\u002Fp>\u003Cp>Speaker 0: I do only have a white screen.\u003C\u002Fp>\u003Cp>Speaker 2: K. I put in one more prompt. I I'm three shotting it, and I think three shots might be it.\u003C\u002Fp>\u003Cp>Speaker 0: I think my idea was here, but the execution is lacking.\u003C\u002Fp>\u003Cp>Speaker 2: Oh, it even gave it a little tab icon. Nice.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. I think we got something.\u003C\u002Fp>\u003Cp>Speaker 2: Matt, are you past the home page yet? Did you get logged in yet?\u003C\u002Fp>\u003Cp>Speaker 0: Been spending all this time on the initial\u003C\u002Fp>\u003Cp>Speaker 2: You spent fifteen minutes of the twenty minutes just upgrading to pro. I did.\u003C\u002Fp>\u003Cp>Speaker 0: I might have credit card information floating out there, so remind me to cancel my cards. Alright. We got something working. Yeah. Without the MCP, I'm I'm not really able to pull the.\u003C\u002Fp>\u003Cp>Speaker 2: Here's what here's what my cursor just said so you know what it's doing. The buttons are noticeably bigger now. Let me shoot a couple skills first, then test the collected view and detail pop up. That's a little hit into what I've built.\u003C\u002Fp>\u003Cp>Speaker 0: Are we talking legend of Zelda or something? What? Dangerous to go\u003C\u002Fp>\u003Cp>Speaker 1: alone. Sweet mother.\u003C\u002Fp>\u003Cp>Speaker 0: He's still in there, or is that going too?\u003C\u002Fp>\u003Cp>Speaker 1: I still got it. Started off on the wrong foot, man. Should not have, should not have tried to go React. Should just you gotta stick with what you know, man.\u003C\u002Fp>\u003Cp>Speaker 0: Talk with what you know. Why did you go with React? You were trying to get fancy?\u003C\u002Fp>\u003Cp>Speaker 1: I I've been using, I I got a project, a personal project where I've used, TanStack start lately. I'm trying to learn a little more about React so that I'm not one dimensional. Mhmm. Did not pay off today. Did not pay off today.\u003C\u002Fp>\u003Cp>Mhmm. Thirty seconds. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: This is I've just spent all the time troubleshooting. Lovable doesn't seem to really work well with custom MCPs, so if anybody's curious.\u003C\u002Fp>\u003Cp>Speaker 1: Fifteen, fourteen, 13. Who are you who's who gets to go first?\u003C\u002Fp>\u003Cp>Speaker 0: Well, I\u003C\u002Fp>\u003Cp>Speaker 1: mean, clearly, it sounds like John.\u003C\u002Fp>\u003Cp>Speaker 0: Like, John's face means that\u003C\u002Fp>\u003Cp>Speaker 1: he may have something really good. He might have. Yeah. Time's up.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Time's up. It's just finishing it's just finishing its build, but I've just been I only needed three prompts to get it to some pretty amazing.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. I mean, I've got something. It's not,\u003C\u002Fp>\u003Cp>Speaker 2: Sounds like you have nothing.\u003C\u002Fp>\u003Cp>Speaker 1: It's basically You go then. Me?\u003C\u002Fp>\u003Cp>Speaker 0: Let me it's still finishing this moment. The junction table I faced. Alright. I just refreshed. We'll see.\u003C\u002Fp>\u003Cp>Speaker 1: I I think that is I'll go then. I'll go.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Let's see what you got, Bryant. I'm excited to see this\u003C\u002Fp>\u003Cp>Speaker 1: header. Screen. I didn't have time to work on the design. We have a skills directory. I was going for, like, the brutalist theme.\u003C\u002Fp>\u003Cp>It didn't really get. I I had literally no time to worry on the design. So the the tags do work, which is nice. We can search. So we search for SEO.\u003C\u002Fp>\u003Cp>That works. We can load up the skill. We see it being loaded here. You know, you got the nice little subtle detail, the little arrows instead of the standard bullet points. You got the the fake install.\u003C\u002Fp>\u003Cp>And then the coup d'etat. Is that what the I don't know. I don't know. Matt has surely lost.\u003C\u002Fp>\u003Cp>Speaker 2: Nice.\u003C\u002Fp>\u003Cp>Speaker 1: Stakes. Pretty badly.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, so you can submit skills from the front. That's pretty good. Pretty good.\u003C\u002Fp>\u003Cp>Speaker 1: Oh. No.\u003C\u002Fp>\u003Cp>Speaker 0: Failure.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I did get a chance to to fix that. If I had, like, two more minutes, like, whatever it is that you're doing right now while I'm demoing mine.\u003C\u002Fp>\u003Cp>Speaker 2: I'm not doing anything.\u003C\u002Fp>\u003Cp>Speaker 1: This looks good. I mean talking to Matt.\u003C\u002Fp>\u003Cp>Speaker 0: Design wise? What like, I mean, I think it looks solid. I would probably call it\u003C\u002Fp>\u003Cp>Speaker 1: I I wouldn't have gone, like, the dark mode design. Right? I would have gone light mode, and I would've, like, just had it be very in your face. Would have been nice to have, like, the login to be able to submit this, but, again,\u003C\u002Fp>\u003Cp>Speaker 0: just good\u003C\u002Fp>\u003Cp>Speaker 1: to me. Wrestle with it too much.\u003C\u002Fp>\u003Cp>Speaker 2: It's more than I was expecting. It's it looks good.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Call it Hill Skills. And\u003C\u002Fp>\u003Cp>Speaker 1: it's got,\u003C\u002Fp>\u003Cp>Speaker 0: like, kind of a nineties vibe to it. I dig it. Alright. I guess I'll go next. We'll see what happens here.\u003C\u002Fp>\u003Cp>But, alright. This is called, AI skills pro skater presented by AI Amigos. So the concept\u003C\u002Fp>\u003Cp>Speaker 2: I wanna say I'm impressed that you have something. I thought you were gonna show us nothing.\u003C\u002Fp>\u003Cp>Speaker 0: Maybe maybe I was in Parker Face the whole time. But, anyways, the concept here is you start by tag. So you pick your, it's not really skateboarding, but the concept was Tony Hawk's Pro Skater. But, it was supposed to be whoever like, I wanted to do it by people in an organization. So engineers would wanna probably see the engineering skills, marketers, that sort of thing.\u003C\u002Fp>\u003Cp>So starting with tags, which is something I haven't seen before. And then in true Tony Hawk fashion, ideally, what I was trying to get was to unlock the skill, you have to do the, like, combo thing with the keyboard. And, it would do a little trick sort of thing. If I had more time, this would be like a full Tony Hawk style game where you can unlock skills that way. But, have all of the ones here.\u003C\u002Fp>\u003Cp>It just randomly put some, like, easy hard sick, grade on here, but I still pulled in all the overview platform tips and stuff. Not as nice as I was, like, hoping to get, but\u003C\u002Fp>\u003Cp>Speaker 1: Where's the skateboards?\u003C\u002Fp>\u003Cp>Speaker 0: I know. There is no skateboards.\u003C\u002Fp>\u003Cp>Speaker 2: I was I I\u003C\u002Fp>\u003Cp>Speaker 0: I was disappointed.\u003C\u002Fp>\u003Cp>Speaker 2: I think you've got the beginnings of something. I think this is cool. I think this is a good idea. I think it's cool.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I was trying to go for the most non directory directory type, but, yeah. That's what that's what I\u003C\u002Fp>\u003Cp>Speaker 1: That's cool. Yeah. This part, I like, man. This looks this looks super nice.\u003C\u002Fp>\u003Cp>Speaker 2: It's totally\u003C\u002Fp>\u003Cp>Speaker 1: It's even got the\u003C\u002Fp>\u003Cp>Speaker 2: Like, I like that they're even floating.\u003C\u002Fp>\u003Cp>Speaker 1: It's like the the CRT Yeah. Tube TV effect to it. Mhmm. Yeah. I've been lately.\u003C\u002Fp>\u003Cp>Speaker 0: But yeah. So that's the end. That's AI skills for a skater.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think it's for a skater.\u003C\u002Fp>\u003Cp>Speaker 0: Alright, John.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. Let's see what you got, John boy.\u003C\u002Fp>\u003Cp>Speaker 2: Alright. Let's do this. I went you know, if you've if anyone's followed me on LinkedIn, I did the whole Nintendo theme garage inventory. So I kinda kept it Nintendo themed, and I went with Duck Hunt, although Skills Hunt. So you come here and you you shoot the little you you can't hear it or maybe you can't.\u003C\u002Fp>\u003Cp>There's little noises. There's a little creature down there, and then you can you can view the collections that you've got. You've got a little the only thing I would do is make the fonts bigger. You can see all of these things here. But you can go back to the game and you shoot the little you shoot the skills that you want.\u003C\u002Fp>\u003Cp>Speaker 1: The dog is So there there\u003C\u002Fp>\u003Cp>Speaker 2: is noises. I don't think you can hear them, but maybe the recording will show them. But there is noises when you're shooting things, and that's what I did. You got your skills there on the right that you've collected. You've got your skills here, and then you can view what those skills are.\u003C\u002Fp>\u003Cp>Speaker 0: Alright.\u003C\u002Fp>\u003Cp>Speaker 2: So, yeah, shoot for\u003C\u002Fp>\u003Cp>Speaker 1: ask questions later.\u003C\u002Fp>\u003Cp>Speaker 2: Actual game I already got that one. Damn. I need a new skill. There we go. Bang.\u003C\u002Fp>\u003Cp>Speaker 0: I love gamifying the skills. Yeah. You can't just get the skills. You gotta work.\u003C\u002Fp>\u003Cp>Speaker 2: You gotta work for it, man. Anyways, that's what I\u003C\u002Fp>\u003Cp>Speaker 1: know. Alright.\u003C\u002Fp>\u003Cp>Speaker 0: I mean, all skill one. Honestly, I think way better than all of us anticipated, at least for for me.\u003C\u002Fp>\u003Cp>Speaker 2: I mean, we did all of those Friday It's a working game. Twenty minutes.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Very nice.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It was twenty minutes on the clock and mostly fighting with my, connection and react, and John was his master of vibes over there. So\u003C\u002Fp>\u003Cp>Speaker 2: Master of vibes.\u003C\u002Fp>\u003Cp>Speaker 0: I mean, if\u003C\u002Fp>\u003Cp>Speaker 1: I had to speak Does this go to a vote between us,\u003C\u002Fp>\u003Cp>Speaker 0: or does it does it go to the audience? I mean, I I think it's a good question. Right.\u003C\u002Fp>\u003Cp>Speaker 2: I think we all know who won this one here internally.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: As much as it pains me to\u003C\u002Fp>\u003Cp>Speaker 0: say it, very creative. Very creative. Mhmm. I like shoes.\u003C\u002Fp>\u003Cp>Speaker 2: I think they're all good. I think they're\u003C\u002Fp>\u003Cp>Speaker 1: all\u003C\u002Fp>\u003Cp>Speaker 2: great, dare I say. They need all they all need adjustments, obviously, if you were to move this to production, but I think they were all great ideas in different manners.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And then you combine them all. And that's I mean, I think the best thing is that, like, I'm imagining you have, like, a full team of folks operating off the same like, you like, at companies I've worked at, they've they've always been hackathons. But, like, opening up a hackathon to the entire team to be able to work off the same base set of data, I think, is just the really cool thing here. It's, like, everybody can bring ideas to the table now.\u003C\u002Fp>\u003Cp>But, yeah, overall, pretty cool.\u003C\u002Fp>\u003Cp>Speaker 1: Well, cool, guys. I think that's\u003C\u002Fp>\u003Cp>Speaker 0: about it for this session. I appreciate everybody hanging in there with us. It sounded like if you're just listening to this, it was awful, but, you know, the end result has been solid.\u003C\u002Fp>","What's up, everybody? It is great to see you, to meet you virtually here. My name is Matt. I'm on the marketing team here at Directus. And if you're watching this, you're in for a treat because we have quite the repertoire of guests here today, also from the Directus team, and we're gonna be doing something pretty fun. But first, let me introduce each guest. You know him. You love him. You've seen his face all over the place. It's John Daniels. John Daniels. What's up? Glad that you started with me. I was worried you were gonna say Brian. So hello. Your face is all over the place right here. I don't think anybody really knows who Bryant is. So, no. No. No. No. Of course, we have the man himself, mister Bry. Bryant Gillespie, who's one of our esteemed developer advocate type folks, I guess, on the team. You know? He kinda does everything. Type folks? Yes. Happy to be here. Very excited for this. Look. I'm just gonna start this off with saying whatever we're doing in this competition, I'm gonna mop the floor with you guys. That's it. Strong start. I was gonna do this not in the spirit of competition, but in Oh. In in friendliness, but I guess that's what we're gonna do now. Alright. And if anybody doesn't know, John is one of our AEs here at Directus, so the most competitive of of everyone here. So I'm sure pretty sure that'll awaken a fire in a moment. So, yeah. Brian, what are we doing today? I this is your show, my friend. You tell me what we're doing. Clearly, it's something to do with an AI skills directory because that is what's on the screen. That is what's on the screen. Alright. I'll give you the brief rundown. So today, we are gonna have a build off. Everybody's been talking about front ends and UIs, and the errors are shifting. And there's so many tools out there. I'm a user of Cloud Code for a lot of the marketing stuff that we do here at Directus, including building the Leap Week landing page. Our team uses codex. Our team uses a lot of the out of the box, like, harnesses, like cursor. And we also use things like Lovable. So it's a good mix of things, but that's a lot of subscriptions add up. So we thought, why not put the skills to the test and see which we can use best for building the front end on a Directus back end, which would be a skills directory. And one of the reasons we wanna do a skills directory is because there's a lot of shady skills directories out there and there's a lot of mistrust in a lot of them. So our developer team has a lot of, skills that they share, that we thought would be cool to build something along with that, as well as our go to market team uses a lot of things too. So, yeah, that's really the gist. Did I What are the what are the stipulations for this, man? We're building a skills directory front end. We're gonna need a back end for that, which we'll do, but what are the rules? There are no rules. There's only one rule. And that is that's details? Yeah. There's gonna be one rule where we have to create the back end and direct us. And then whatever tool we're gonna use, we are going to use the same back end for that front end. So running down the list of tools that we will use, I will be using Lovable. It's something I've used in the past. I've I've not actually used it in quite a bit, because I've been using, Cloud Code. But, Brian, what are you gonna be using? I'm gonna be using Cloud Code. Alright. John, what you got? I use my old dependable cursor. Yeah. Is cursor what you built? The, garage inventory? It is. Yeah. Yeah. And I already have a I already have an idea. Oh, boy. Oh, no. Retro. It's a lot yeah. Front end directory. It's a long Here we go. I'm doing that maybe more than, like, moving forward in my chair. Cool. Yeah. Well So Right. To kick things off, I'm assuming we'll kick we've started. We we got a totally blank data model here. We'll just call it skills directory. Right? John is just banging on the keyboard, man. Alright. Totally empty. It's like you know, we'll we'll do this 100 app style. Like, what what what do we even need on a skills directory? Right? You're gonna need some skills, but what what is the what are the other stuff that you're planning on tracking here? What do you need for the back end? Yeah. That's a good question. When you say skills, what does that encompass? Is that like the title and the description and the actual I mean, skills are basically just a fancy word for markdown files. Right? Yeah. I mean, that's, yeah, that's the the funny thing about all of this stuff to me is it's all just markdown. Like, been been dealing with markdown since I was 12. A week ago. Yeah. So we got a name, a description for the skill. You've got the, what, the actual skill content. I don't I don't know. Are you I'm assuming you're gonna have, like, users who submit the skills? Mhmm. We'll have I think, at some point, if we wanna Submit a bug. Public facing, but for our internal team makes sense. You probably want, some sort of, like, tagging system. So if it's engineering skill or if it's So the tag name. Okay. What else you got? Option for an image, if you wanna add that to the front end potentially. Image? Okay. You're getting away from the markdown stuff now. I'm gonna just draw the arrows for fun here at this point. Here we go. Boom. Alright. So you got skills. You got tags. What else do you need? I mean, I Repository. GitHub repository. Is that where they're coming from? Could you just host it within Directus itself, like, as a file? I mean, we can. Yeah. Like, do you want it linked to somewhere? I think I wanna keep it, like, self like, contained within the one thing. Okay. Alright. Cool. Is this anything else? Or is it we're just gonna roll with this. Just keep it simple. Unless you got anything to do. Wait. No. Keep it simple, stupid. Which one is stupid, man? You probably said it. Alright. I've got this. So, hopefully, I've got some secret sauce here. Been working on a CLI tool. So I'm gonna tell Claude code here. Can you guys see this? Should I zoom way in? Alright. Run d six s agents. It's our CLI for connecting to direct us. D six s. It's the first time seeing the CLI in action right here. Very nerdy. You know, the Kubernetes deal. What is this thing doing? Why is it not running this command? It's reading four files, which d success. Just run the command, dude. What are you doing? Chose the wrong tool. Maybe cursor's been Maybe you chose the wrong tool. Yeah. There it is. Okay. So, basically, that command gives it a little context about this. Cool. And now this thing should be primed to go. Agents. Right. Cool. Alright. So now we're gonna kick it over into plan mode. I'm gonna paste that image that I had. Right? And we're just gonna say, hey. We're building an AI skills directory. Here's a screenshot of a data model I'm competing with John and Matt. We need the back end in Directus first before we build the front end. Can you plan out the Directus back end? Yeah. Alright. I've been using Let's see what happens here. Okay. Right here. Alright. It is going to alright. This is where I tell you to do as I say, not as I do, because I'm just gonna turn this thing loose. Right? I should have actually set dangerously set permissions here. But I don't even have it ask for permission anymore. I just let it go. Go, baby. Go. I have to leave this a lot. That's probably why. If you can't leave that out, security infraction. Security. Spent hundreds of dollars in tokens last month. Alright. It's going to write this plan. Are you so are are you not using cloud code at all? You're still using cursor every day? I use both. Depends on what I'm doing. So what so what you're you're basically just doing what the MCP used to do just from the terminal So is the why the CLI? Right? Well, the reason why is because context. The context window. So MCP uses up a lot of context, for comparison. Right? Like, if you have all the tools enabled, MCP or our AI assistant right now could use up to thirty, forty thousand tokens to have all the tools enabled. The CLI, by default, doesn't load anything into the context, and the agent is or like CloudCo, these agent harnesses are pretty good at navigating, like, CLIs just because they're they're trained on a lot of bash scripts and stuff like that. So, you know, it basically you keep the context window clean until you need it, and, yeah, you follow this progressive discovery pattern. I'm yeah. I've been talking. I'm not even looking at this plan. Hopefully, this thing actually does what we wanted to do. We shall see. Right? Actually, use recipes. D success recipes. This is should be a shorthand. Again, this is all highly experimental, so failed to log the plug in, so it's not gonna use that at all. Plug ins. Index. Oh, good. Alright. Well, just push forward. We may be kicking back to the MCP for this. Hey. Alright. Is what I when I type in the when it when MCP goes wrong for me, I say, just do it, and it'll it'll figure it out. Just do it. Figure it out funny, man. Turn off permissions and just go. What is this thing doing? Your token doesn't have admin access. I do have admin access. No. You're wrong. You said, highly experimental. Correct? I mean, it it was working well. Works on my machine until you start recording. Right? Right. So we'll just go back to the old tried and true here. Both collections exist, but I have no schema. Oh my gosh. This thing is hopelessly lost. Lost. Hey. There's two collections in there now. Yep. They're actually just folder collections because they don't exist. Alright. Skills. When I say tried and true, I mean me. Alright. What do we have there? We have name, description, content. Name. I've never seen anyone move this fast inside of the. Content. Just markdown. It's just markdown. There you go. Alright. You got tags. Yeah? Mhmm. Cool. Tags. I have a name for the tags. Tag name. Cool. And then we need a skill tags. We need a junction table. Cool. I'm taking the long way around here. Skills. Now I'm gonna go to the mini mini relationship inside directions. It's been a minute since I've done this, man. Don't you miss it? Always I do miss it. Yeah. It took me an hour to do half of this stuff that I could now do in, like, five minutes using the AI tools when they work, mind you. Skills ID, tags ID. Let's just call it skill. Let's call it tag. Skills. Cool. Give it a sort field. Boom. Boom. Boom. Boom. You know, it's worth that messing up to watch you do this. It's like watching Boom. That's it. There's the back end. You're good. Golden. Very simple. Very easy. Now what? Now we need some data. Mhmm. Maybe it can do that. Look, I I set it all up. Nice. Thanks for nothing. Now I need you to create some sample data for us, though. I mean, I'm a make sure you rub it in the face of the thing. Right? Talk to me well, I this might take a minute, but, like, what are you guys Like, when you are are we supposed to talk about what we're gonna build? Like, how the front end is gonna look, or is this gonna be, like, a secret thing? We'll talk about, you know, how things are going. I'm still I'm between a couple ideas on what I want this front end to look like. I I want it to not look like a generic directory because there's, like, directories of directories out there. I already have an idea, so don't even don't even worry about Wait. Alright. Was it one that we talked about earlier, or is this one this is completely new? I got a new idea. Interesting. Okay. I think I'll have an idea. You think you'll have an idea? Alright. Well, it's got there's eight tags. There's six skills. Let's add 10 more skills. Create a few sample users as well, give them a different role. Just the ability to view skills and update the ones they've uploaded. Alright. So now we're gonna move to the the front end stuff. Yeah? Yeah. I think so. So we're all operating off of the same direct us back end, and you just created a few users. I'm assuming that's to give us access to that data. Yep. So you guys have your own users in there. Looks like it's created Only one guy cares about a photo. Yeah. Oh my gosh. Alright, dude. Fine. Fine. Fine. Fine. Yeah. Boom. I'll update my You look you look rather young in that one, John. Thanks. Alright. So what do I dig at all. Thank you. Alright. So each of us is gonna have our own role, and you will provide that role to us. If you refresh real quick. Sorry. Refresh bright. There. Of course. Yeah. We're reading we're watching this. You're certainly not watching this on Saint Patrick's Day. We're recording it on Saint Patrick's Day, though. Yeah. Yeah. Yeah. Well done. Yeah. That one. Sorry. Continue, Matt. How do you have access to this already? I I already gave him access. I put it in the chat. I sent you guys up already. Your direct us email and your password. What's the password? The password is Password. Now, hopefully, one of us will remember to have removed that by the time this recording goes live. I'll tell him how to change it. In the chat. Must be what is this thing doing. Okay. I was creating, like, 35. Why can't I not just accept all here? Good lord. Shoot me the shoot me the URL in the chat. I don't see it in there. It's a imigos.directus.app. Got it now. Alright. Alright. I'm in. Alright. Alright. Alright. Alright. Invalid input. Sarah Chen. Why does it keep trying to I don't know. It really likes the name Sarah for whatever reason. Hey. I got a crush. Marcus, Priya, Sarah. Those are the folks who are uploading the other skills. Alright. So we're good then. Yeah. There's gonna be some skills that populate in there, and then everybody is jamming on the back end. How long are we do we have on the back or on the front end? Right? I set a timer. We have, like, two minutes now, but, if we wanna take ten minutes, you know, and do what we Ten minutes to do a whole back end or a front end? Yeah, man. I mean, it's the UI error is changing, so they say. I at least, like, 15. Yeah? 20 to 30. We could do, like, a supercut. We'll extend this session a little bit. We've got some time. Do a supercut. Yeah. Alright. Twenty minutes then. That's it. Twenty minutes. No more. No less. I think You guys better not be starting with the back end already or front end already. Sure John's already done. I've been Yeah. He's already done. Hearing him. Alright. I'm just right now, I'm just setting up the m I'm setting up my MCP and cursor. Alright. Well, you guys just tell me when you're ready to go. I'm I'm pretty confident in my skills. Let's say we're using, like, you know, lovable or something. I've got my user here. I create It's got admin policy, create a new token, save said token, and should be good to pull data from that directly. Yeah. And just lovable have FCP? I don't know. It's a good question. Personal connectors. There you go, dude. I see it. I sees it. Here's the directisio docs. I'm gonna just point you in the right direction there. Connect AI, AI, MCP server. This is the page that you need from the documentation. I'm gonna paste that in the chat. Good luck to you, sir. You guys just all the skill the back end is done. The back end is locked. Don't even think about changing the back end. Alright. When you guys say go, I'm gonna hit go. Hit go. There it is. Alright. Twenty minutes, boys. I'm I've gotta upgrade my lovable account because, apparently, Lovable doesn't allow custom MCPs unless it's It sounds like an excuse, bro. Already an excuse. Alright. One pro. I'm gonna spend this whole time setting up my custom MCP. Oh, no. Why can't I see the text here? Oh, that's not good. I'm going react on this one. Why can't I see this, though? I just got this new Mac, man. Still, if you guys beat me, that will be b y since we're just throwing excuses out there. I am still signing a still I'm still going there by a sign up for a year. I can't even get the I can't get the tan stack start script here from the their website. Start. Connection failed. Gotta love it. Alright. Well, mobile mobile is not really communicating with me right now. I'm having trouble. I hope you guys are having fun. I'm having a blast. Matt, is your keyboard connected yet? Yes, John. Alright. Let's see. I got my build plan ready. Let's see if this looks good. No. I don't think Lovable's gonna be the the move. Not great, Lovable. Not great. Alright, guys. I've clicked build. We're gonna we're gonna get v one running. What if we finish in ten minutes? Does that give us bonus points? I should be done setting up my MCB by then. So Matt, why don't you have a rabbit in your background? Brian and I both have a rabbit in the background. I have several. Because I like to have my rabbits in my point of view. So not in the background. Alright. Well, local's not gonna work with me on the MCP, so I'm gonna have to roll. You just just gonna have to roll. Can I just copy, Jace? You could just tell it to use the SDK. I think you can copy. I think you can also just give it your API token and just give it access. I think I've done that before. You'd be like, hey. I'm an API token. Didn't know we'd get excuses so quick out the gate, though. You know, usually, I'm using, like, whisper for this. Go for it. Okay. Then we know what you're gonna build. Right? Just meet your just meet yourself while you mouth it and then unmute. I need a front end that looks great. Make no mistakes. Make it look beautiful. I I hope John's idea is something that looked like a pot of gold and a leprechaun. No. But that would have been good. Dang. I think mine'll be good, though. I I have no idea what this output of this prompt is gonna mean. I'm actually kind of terrified. I know. I'm just reading my typing, and it's freaking terrible. That's why he's was The good news is AI doesn't care about typos. What were the tags we put in here, by the way? Research writing, design, automation, productivity, coding, marketing, and data analysis. We should upload these to to, like, Vercel or Netlify or something afterwards so people can play with them, the front ends. That should be that should be solid. Here's the file being created right now. It's called bang effect dot JSX. Bang effect. You may win this, man. I think you guys had more time to think about the ideas here. Don't yeah. I don't have a solid idea. Lost trying to set up the MCP server in Lovable. Oh, that's a risky run. You've got what what did we got on the clock? Thirteen twenty one. Plenty of time. Just just come on, Claude. Don't film me now. I like you, dawg. We got this. We got this. Oh, first preview. It looks good. Did you one shot it? Oh, damn. This is okay. You guys are gonna lose. He's blowing smoke. I know. He's he doesn't have anything. It's just a blank screen. I wish you guys could see this. No. I'm trusting lovable with my lunch. The sound effects are amazing. You have sound effects? The only problem is I can tell the words are backwards. I'm like, why are these words backwards? What, wait. Tags. One, two, three. I like how there's, like, duplicate tags. Lovable has gotten a lot better on the user interface since all of us use it. Like, it quite looks nice, except for the whole connecting and MCP thing. I wish you guys could hear these sound effects. I think I can hear a few. Did you do freaking lasers? Oh, it's looking so much better now. Oh my gosh. This looks so good. It's a risk. It's poker face. No. You know it's a good a good project when you are playing the game that you've built. I'm still waiting for dependencies to resign. Alright. You know me, Ben. If you enjoy watching people slide by on the skin of their teeth, please tune in to Directus TV for where yours truly embarrasses himself twenty four seven by trying to build apps. Oh god. This is going horribly wrong. I should not have should not have done React that is using way outdated stuff. We're just I I don't know. Tam, if you watch this, man, I don't know what happened to the website today when I was recording this, but I think this is gonna be Oh, there it is. Finally. It's okay. You got something? Must have been I the site must have been broken, man. I don't I don't know. Okay. Now now it's playing its playing its game on its own. This is where where it's fun to watch Cursor play its own game. You got your AI playing an AI? K. What are you doing over there? Who? Me? Just kicking ass? I'm gonna have to rely on a one shot here. I think I might have this with a one shot. We'll see. It's still it's finishing up the build. This is building out the comp I'm building the character select and move list UI right now, so we're gonna have to wait a a second. Did you create an RPG, John? No. I created a a video game. Do you wanna know what its theme theme is? I wanna wait and see. Okay. Leprechauns? No. Pecauns. I think I you know, if I had more time, it would be cool to do, like, an RPG style directory where you have to, like, level up to get the better prompts. I wonder if anybody would spend time or not. Probably not. I've been looking at this thing, and I'm just like, I don't even know. Do you have anything? Do you have a UI at all, Brian? I I have nothing on the screen. That's not good. How much time do we have left? Six minutes. Oh, man. Guys, this you might as well just stop. I'm telling you, you're gonna lose. I know Matt basically has nothing. He's gonna show a white screen. It's gonna be like index.html. We're gonna wait and ask I have a header. You have a header? I have a header. Match this line to us. Have have I been in the roost this whole time? Who knows? I feel like I do miss the old days where I just good old fashioned handwriting code. I probably yeah. On a 100 apps, hundred hours, like, the days of all, I woulda had something already that looked nice. I'm not super happy with what I've got here. I do only have a white screen. K. I put in one more prompt. I I'm three shotting it, and I think three shots might be it. I think my idea was here, but the execution is lacking. Oh, it even gave it a little tab icon. Nice. Alright. I think we got something. Matt, are you past the home page yet? Did you get logged in yet? Been spending all this time on the initial You spent fifteen minutes of the twenty minutes just upgrading to pro. I did. I might have credit card information floating out there, so remind me to cancel my cards. Alright. We got something working. Yeah. Without the MCP, I'm I'm not really able to pull the. Here's what here's what my cursor just said so you know what it's doing. The buttons are noticeably bigger now. Let me shoot a couple skills first, then test the collected view and detail pop up. That's a little hit into what I've built. Are we talking legend of Zelda or something? What? Dangerous to go alone. Sweet mother. He's still in there, or is that going too? I still got it. Started off on the wrong foot, man. Should not have, should not have tried to go React. Should just you gotta stick with what you know, man. Talk with what you know. Why did you go with React? You were trying to get fancy? I I've been using, I I got a project, a personal project where I've used, TanStack start lately. I'm trying to learn a little more about React so that I'm not one dimensional. Mhmm. Did not pay off today. Did not pay off today. Mhmm. Thirty seconds. Yeah. This is I've just spent all the time troubleshooting. Lovable doesn't seem to really work well with custom MCPs, so if anybody's curious. Fifteen, fourteen, 13. Who are you who's who gets to go first? Well, I mean, clearly, it sounds like John. Like, John's face means that he may have something really good. He might have. Yeah. Time's up. Yeah. Time's up. It's just finishing it's just finishing its build, but I've just been I only needed three prompts to get it to some pretty amazing. Okay. I mean, I've got something. It's not, Sounds like you have nothing. It's basically You go then. Me? Let me it's still finishing this moment. The junction table I faced. Alright. I just refreshed. We'll see. I I think that is I'll go then. I'll go. Yeah. Let's see what you got, Bryant. I'm excited to see this header. Screen. I didn't have time to work on the design. We have a skills directory. I was going for, like, the brutalist theme. It didn't really get. I I had literally no time to worry on the design. So the the tags do work, which is nice. We can search. So we search for SEO. That works. We can load up the skill. We see it being loaded here. You know, you got the nice little subtle detail, the little arrows instead of the standard bullet points. You got the the fake install. And then the coup d'etat. Is that what the I don't know. I don't know. Matt has surely lost. Nice. Stakes. Pretty badly. Oh, so you can submit skills from the front. That's pretty good. Pretty good. Oh. No. Failure. Yeah. I did get a chance to to fix that. If I had, like, two more minutes, like, whatever it is that you're doing right now while I'm demoing mine. I'm not doing anything. This looks good. I mean talking to Matt. Design wise? What like, I mean, I think it looks solid. I would probably call it I I wouldn't have gone, like, the dark mode design. Right? I would have gone light mode, and I would've, like, just had it be very in your face. Would have been nice to have, like, the login to be able to submit this, but, again, just good to me. Wrestle with it too much. It's more than I was expecting. It's it looks good. Yeah. Call it Hill Skills. And it's got, like, kind of a nineties vibe to it. I dig it. Alright. I guess I'll go next. We'll see what happens here. But, alright. This is called, AI skills pro skater presented by AI Amigos. So the concept I wanna say I'm impressed that you have something. I thought you were gonna show us nothing. Maybe maybe I was in Parker Face the whole time. But, anyways, the concept here is you start by tag. So you pick your, it's not really skateboarding, but the concept was Tony Hawk's Pro Skater. But, it was supposed to be whoever like, I wanted to do it by people in an organization. So engineers would wanna probably see the engineering skills, marketers, that sort of thing. So starting with tags, which is something I haven't seen before. And then in true Tony Hawk fashion, ideally, what I was trying to get was to unlock the skill, you have to do the, like, combo thing with the keyboard. And, it would do a little trick sort of thing. If I had more time, this would be like a full Tony Hawk style game where you can unlock skills that way. But, have all of the ones here. It just randomly put some, like, easy hard sick, grade on here, but I still pulled in all the overview platform tips and stuff. Not as nice as I was, like, hoping to get, but Where's the skateboards? I know. There is no skateboards. I was I I I was disappointed. I think you've got the beginnings of something. I think this is cool. I think this is a good idea. I think it's cool. Yeah. I was trying to go for the most non directory directory type, but, yeah. That's what that's what I That's cool. Yeah. This part, I like, man. This looks this looks super nice. It's totally It's even got the Like, I like that they're even floating. It's like the the CRT Yeah. Tube TV effect to it. Mhmm. Yeah. I've been lately. But yeah. So that's the end. That's AI skills for a skater. Yeah. I think it's for a skater. Alright, John. Alright. Let's see what you got, John boy. Alright. Let's do this. I went you know, if you've if anyone's followed me on LinkedIn, I did the whole Nintendo theme garage inventory. So I kinda kept it Nintendo themed, and I went with Duck Hunt, although Skills Hunt. So you come here and you you shoot the little you you can't hear it or maybe you can't. There's little noises. There's a little creature down there, and then you can you can view the collections that you've got. You've got a little the only thing I would do is make the fonts bigger. You can see all of these things here. But you can go back to the game and you shoot the little you shoot the skills that you want. The dog is So there there is noises. I don't think you can hear them, but maybe the recording will show them. But there is noises when you're shooting things, and that's what I did. You got your skills there on the right that you've collected. You've got your skills here, and then you can view what those skills are. Alright. So, yeah, shoot for ask questions later. Actual game I already got that one. Damn. I need a new skill. There we go. Bang. I love gamifying the skills. Yeah. You can't just get the skills. You gotta work. You gotta work for it, man. Anyways, that's what I know. Alright. I mean, all skill one. Honestly, I think way better than all of us anticipated, at least for for me. I mean, we did all of those Friday It's a working game. Twenty minutes. Yeah. Very nice. Yeah. It was twenty minutes on the clock and mostly fighting with my, connection and react, and John was his master of vibes over there. So Master of vibes. I mean, if I had to speak Does this go to a vote between us, or does it does it go to the audience? I mean, I I think it's a good question. Right. I think we all know who won this one here internally. Yeah. Yeah. Yeah. Yeah. As much as it pains me to say it, very creative. Very creative. Mhmm. I like shoes. I think they're all good. I think they're all great, dare I say. They need all they all need adjustments, obviously, if you were to move this to production, but I think they were all great ideas in different manners. Yeah. And then you combine them all. And that's I mean, I think the best thing is that, like, I'm imagining you have, like, a full team of folks operating off the same like, you like, at companies I've worked at, they've they've always been hackathons. But, like, opening up a hackathon to the entire team to be able to work off the same base set of data, I think, is just the really cool thing here. It's, like, everybody can bring ideas to the table now. But, yeah, overall, pretty cool. Well, cool, guys. I think that's about it for this session. I appreciate everybody hanging in there with us. It sounded like if you're just listening to this, it was awful, but, you know, the end result has been solid.",[182,183,184],"d7e6adc3-560c-4e06-8e32-ae37949193c0","5abcac60-2f3d-472c-9976-e013163d7a80","5576f5a9-383a-4f43-80c0-6356e6aeaf79",[],{"year":139,"number":140,"id":141,"episodes":187,"show":188},[143,144,145,146,147,148,123,149],{"title":151,"slug":152},{"id":148,"slug":190,"vimeo_id":191,"description":192,"tile":193,"length":194,"resources":12,"people":12,"episode_number":195,"published":130,"title":196,"video_transcript_html":197,"video_transcript_text":198,"content":12,"seo":199,"status":19,"episode_people":200,"recommendations":203,"season":204},"personalization-at-scale","1176544348","Learn how to use enrichment data to build hyper-personalized landing pages at scale. Featuring Clay.\n\n","f736efe0-fb5d-4453-8cde-408ba3643b8c",27,6,"Personalization at Scale","\u003Cp>Speaker 0: Hey. What's up, everybody? Matt here from the direct us marketing team. I am super excited today to be showing off, what I think is a really cool session personally. Kinda set the scene a little bit.\u003C\u002Fp>\u003Cp>Obviously, I said marketing, and that's probably terrifying for a lot of people. But, I I like the I I truly believe the gap between, like, technical and nontechnical is getting, you know, smaller. So Mhmm. That's why I'm super excited to have Clay and my friend Mohawk Desai here. Mohawk, how are you, man?\u003C\u002Fp>\u003Cp>Speaker 1: Hey. How's it going, Matt? Thanks for bringing me on. Appreciate it.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Absolutely. So most of our audience is probably not familiar with Clay. Yeah.\u003C\u002Fp>\u003Cp>I will say, like, I I think Clay was kind of the first step for me to get to become more technical, with a lot of enrichment and sort of stuff, but like that. But maybe you could give kind of a a brief Yeah. Overview of what Clay is for the folks.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Absolutely. So at a high level, Clay is a a GTM orchestration and data enrichment tool. And I think a good analogy is I would view Clay as almost a development environment, except not for technical folks, not for coders, but for GTM nerds, like me and like Matt. So, yeah, Clay is if you look at Clay, and we'll jump into the product in a second, but it looks like Excel.\u003C\u002Fp>\u003Cp>There's a lot of AI formulas. There's a lot of, sort sort of technical concepts like webhooks and HTTP APIs. But it is really built for you know, right now, one of our biggest personas is, like, rev ops people at companies, but that's kind of expanding now into marketing folks, into mark ops, and to other personas down the road as well, including, like, sales. But it's really for, like, systems thinkers who wanna build their sort of GTM engine. Clay is kind of the perfect middle ground for all of that, where you can have your entire tech stack flow into Clay.\u003C\u002Fp>\u003Cp>We integrate with 90 plus providers, for data, but as well as sort of GTM execution tools like Gong. And so Clay is kind of like the glue between all your different tools, as well as a place where you could secure data, the things like emails and phone numbers. So, really, it's sort of a one stop shop for GTM orchestration. It's probably the best way I can describe Clay.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Wow. That's a you're reading off like a prompter there. That was awesome. No.\u003C\u002Fp>\u003Cp>The way that you yeah. The way you said, like, it looks like a spreadsheet when you first get into it, that was, like, my first reaction when I first started testing it. You know, I was like, what? This just looks like another spreadsheet. And then when you start looking at the integrations and the possibilities with, like, enrichment waterfalls, that's what, like, had my holy But speaking of integrations, you know, Directus is, you know, we're we're we've always been kind of a content management system for a lot of folks.\u003C\u002Fp>\u003Cp>So all of the pages for your website, it could be the back end for applications. There's a lot of different use cases. And as we've been getting Clay and Directus closer together, our team has found a lot of really cool use cases that you can really dive into, with that enrichment data. One of which I'm really excited to talk about today. And I feel like was this, like, Clay's, like, bread and butter early on, this sort of, like, personalization type play?\u003C\u002Fp>\u003Cp>Speaker 1: I would say not early on. I think, actually, early on in the moat was this the data enrichment piece. On the back end to find emails and phone numbers, Clay takes a waterfall approach where we don't just check one database, but we'll cycle through 90, again, 90 plus providers to find an email or phone number. And so that was the note early on was a higher coverage rate and higher accuracy for data versus under other individual vendors. But, Matt, to your point, I think in the past year or so, a lot of smart rev ops people and marketing people are realizing that Clay's a really good place to do ABM at scale, so account based marketing at scale.\u003C\u002Fp>\u003Cp>And one of them is, yes, starting to create personalized landing pages at scale for your target accounts. But this can also leak into other cool use cases like you can create Google Slides at scale that do the same thing. We have a direct integration of Google Slides, and and so on and so forth. And now in the in the age of AI with all the tools available, we're trying to be a little bit more open to to tools that will allow you to do this. But Directus is, like, a perfect, perfect example of a great integration where we can, really customize our outreach and get those target accounts in our book.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. And when you say ABM, I'm sure the the audience is like, another acronym, like marketing and some of the acronyms. But, for folks that don't know, so ABM is is kind of an approach to marketing and sales that, in my opinion, is the way to do it because it's way less intrusive than just, like, shotgun blasting a bunch of people that have no idea who you are. There's a lot of research and, personalization that goes into identifying, you know, 50 accounts, a 100 accounts that are assigned specifically to, like, your go to market team or your reps.\u003C\u002Fp>\u003Cp>So they get to build relationships and really understand, like, the pains and and things within those those companies. And that kinda sets the scene for today, which is, we're gonna do a workflow from beginning to end and and show you how we built it, basically, where it's not like those shitty emails you'll get that's like, hey. Like, I saw you play volleyball, and now let's get on a fifteen minute call. Like, it that's not personalization. Like, personalization is looking at, like, who a company is hiring, you know, what teams are growing, what is probably because of that, some of the struggles they have right now that your product or solution could maybe solve for, and then building out pages and stories around that.\u003C\u002Fp>\u003Cp>But that's hard to do without automation, without things like Clay for the data enrichment. So that's what I'm excited to talk about today.\u003C\u002Fp>\u003Cp>Speaker 1: Amazing. Let's do it.\u003C\u002Fp>\u003Cp>Speaker 0: Awesome. Alright. So let's hop in. I hope everything is gonna break. I know as soon as I start sharing stuff.\u003C\u002Fp>\u003Cp>It always I don't know why. It always happens. But, let's start with, like, kind of what we're gonna have at the end. That that's kind of our our end goal. We'll start there and work backwards.\u003C\u002Fp>\u003Cp>So, this is just a quick landing page that I spun up, like, ten minutes before this, with clogged code. UIs, you know, you you can spin up a UI really fast now. But the goal is to have this fake AI agency that we just made up called Cottontail, then AI operations team. So they'll handle a lot of the, the manual processes and things like that. So we're gonna have these customized landing pages where it's like, this is the typical personalization you see.\u003C\u002Fp>\u003Cp>Like, all your company name, your, you know, subheader. But what I'm really excited about is focusing in on this section, which is, like, we're gonna use Clay to basically pull out, you know, who are the the number the highest number of jobs, what teams are really growing, and run a couple of, you know, flows on, what does that mean? Like, what are those struggles, the problems they probably have because of that? And then how can our solution fix that? So, basically, a mirror image of what I just said, but this is what it looks like in practice.\u003C\u002Fp>\u003Cp>Any thoughts on, like you know, obviously, working at Clay, like Yeah. Clay, in my opinion, is one of the best marketing. They're I they're like the shining pinnacle right now, everything that y'all do. Mhmm. And I wanna say ABM is a big part of that.\u003C\u002Fp>\u003Cp>Right?\u003C\u002Fp>\u003Cp>Speaker 1: Or it could be cool\u003C\u002Fp>\u003Cp>Speaker 0: at all.\u003C\u002Fp>\u003Cp>Speaker 1: No. Absolutely. As you mentioned, ABM is becoming even a a larger and larger segment of a use case at Clay. I think typically, customers will come in and start with basic enrichment, basic, automated outbound or automated inbound, but those really savvy, you know, companies that know they have sort of a target list of a hundred, two hundred, you know, large companies or or target accounts. ABM is really, really effective at breaking in when everything else doesn't work.\u003C\u002Fp>\u003Cp>And, our CEO, Kareem, has a very cool, analogy or a sort of phrase that encapsulates what ABM should look like, and that's surround sound. You're surround sound of your customers, in in a way that feels so personalized that they they feel obligated to sort of respond or at least curious enough to alright. What do these guys have to say to us? So ABM is like there's a lot that goes into it. One of them is this use case we're gonna jump into, which are these, like, landing pages.\u003C\u002Fp>\u003Cp>Matt, you're correct. This is becoming a very popular thing amongst advanced GTM teams. But outside of just custom landing pages, there's a lot of other things that kind of fold up into ABM. One of them is signals. So taking a signal based approach and having your reps work off intent based data rather than static third party data.\u003C\u002Fp>\u003Cp>So that's one piece. And then the third piece could also be ads. Clay also just launched a new ads tool, but in general, ads is just another way to sort of, again, surround sound, your your personas at these companies. So now imagine a head of marketing at one of your target accounts is getting brilliant, custom landing pages, great emails from your sales teams, are are getting, you know, timely outreach based on some funding events or relevant events to your company, and then you're also seeing ads on LinkedIn and Meta, it's gonna be hard to not be curious about what you do. So that's ABM in a nutshell.\u003C\u002Fp>\u003Cp>And, Matt, I think you're right that that this is picking up a lot in the market.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. That's that's awesome. And when you start folding in, like, billboards and buses with your Yes. Exactly.\u003C\u002Fp>\u003Cp>It's inescapable, man.\u003C\u002Fp>\u003Cp>Speaker 1: Yes. Inescapable. Exactly. Exactly.\u003C\u002Fp>\u003Cp>Speaker 0: But in a, like, in a way, I think it's it's better than just kind of blindfold just going out there, like, blasting you know, pulling a list of a 100,000 contacts and, like, spray and pray method.\u003C\u002Fp>\u003Cp>Speaker 1: Spray. Exactly.\u003C\u002Fp>\u003Cp>Speaker 0: So, so yeah. So that's what we're gonna do today is really focus on on building these pages out, which I think is really the foundation of it. This is not only something for the customers, but also, you know, for your team members as they start to do the research having, like it's basically like a slide deck in in a lot of ways. So just to kind of show you how I've set up direct us in the back end, so we've got, Cottontail here. I've created two collections.\u003C\u002Fp>\u003Cp>So we've got a landing pages collection. This is where all of the stuff we're gonna do in clay is gonna push, right here. So looking at the actual data model schema for this, it's it's basically everything I showed on that that front end page. So Mhmm. Company name, we're gonna have slug, so this will, like, auto generate the pages when they they come in.\u003C\u002Fp>\u003Cp>Everything is powered by by Railway, which is really the one of the easiest ways to get started with Directus and and build front ends on. We got industry. We got a little testimonial thing. We'll see if we have enough time. I I definitely wanna show how the mini to one, like, data schema stuff works.\u003C\u002Fp>\u003Cp>And then we'll have the personalization content kind of backfilled here. So, like, as developers or technical people are watching this, like, this is how you can knock something out. Like, if you do this for your marketing team, it would be, like, you're you're the hero for for months just giving them something like this. But, also, if you're building applications or products, like, this is a great way to get it into the market and start, you know, marketing the stuff that you're building. But yeah.\u003C\u002Fp>\u003Cp>So that's all built out. Just to show you what it would look like if you were to manually fill this out, yeah, just like a a typical form here. This is just how I have it laid out, but you can do it a lot of different ways. But, but, yeah, I that's really all I had for the director side. Like, as a back end, it's not sexy.\u003C\u002Fp>\u003Cp>It's just like we need to push all this data here, but I'm really excited to show what we've got for Clay. So, Mohawk, I'll actually turn it over to you.\u003C\u002Fp>\u003Cp>Speaker 1: Amazing. Thank you, Matt. Great intro. Great context. So now what I'm gonna do is share my screen, and go into Clay.\u003C\u002Fp>\u003Cp>So for folks that have never seen Clay, welcome to Clay. This is the spreadsheet looking tool that you keep hearing about, and so this is what it looks like. So as a starting point for this example, we've uploaded, as our first step, a list of company domains, just their websites. In Clay, though, you can import these company domains from a bunch of other sources. So we have our own company search where you can filter, you know, by, industry, location, revenue count, etcetera.\u003C\u002Fp>\u003Cp>So if you don't already have a list of target accounts in your CRM or in a CSV, you can use Clay to find those target accounts based on your filters. And, as I mentioned, you can upload a CSV as well or connect your CRM and have a live sync between Salesforce or HubSpot, and and Clay. But, again, zooming back out, we are starting with a list of company domains that we have randomly chosen and put into this table. That's the only input we're putting in. That's the I think the key point of all this is that all these other columns you see that have been set up, you set them up once, and anytime you add a new input, it'll automatically run for, for that new input.\u003C\u002Fp>\u003Cp>So, actually, Matt, I I I'm gonna go ahead and sort of enter a new domain here. And, this is my favorite company in the world. So Apple, we're gonna put that a bit second to play. And so I've put this in, and as you can see, it's already autofilled. And I'll kind of now go column by column and break down what we did here to get to that landing page endpoint that Matt showed.\u003C\u002Fp>\u003Cp>So the the first thing is we ran this action called enrich company, and that essentially, in in a JSON array, will return a bunch of data about the company that could be useful. So things like their employee count, their revenue range, their, again, their their, country of origin, things like that. And so the the relevant data points we wanted to pull out for that landing page for each of these companies are really just the company name, the slug, the employee count, and the industry. What we also got, which is pretty cool, and this is kind of taking customization to that, like, final level, is we have URLs to the company's logo as well as a screenshot of their website. So now we've imported their their brand, what they look like, what they feel like, and that can be used if you'd like in your custom landing page, that you're sending to these target accounts.\u003C\u002Fp>\u003Cp>Then from there, we look into jobs. Now jobs in general, I think, are very underrated data point that GTM team should be indexing on because jobs tell you a lot about the company. Tells you what their tech stack is for specific roles. Right? Like, what Mhmm.\u003C\u002Fp>\u003Cp>In that little section that says you should have experience with these technologies, it tells you what they're using. It tells you about their sort of their mission, what they're up to, kind of everything that a candidate would need to know happens to be a lot of the information that we wanna know as sellers and marketers. So, we went ahead and ran an action called find active job openings at this company. And, all all we really needed here was the domain as the input. Again, we're working off of this one input.\u003C\u002Fp>\u003Cp>It's really all we need. And so once you put your domain into this action, that we, you know, we set sort of our filters and we run it, it'll return how many jobs are sort of posted for this as well as the link to each job, which is wonderful because we can then run an AI column on this, active job opening column to kind of analyze those job postings and extract the key insights that we want, like I mentioned. And so we're able to find, which teams are growing fastest, which functions appear to be manual or understaffed. And imagine replacing all of this with the pain points you wanna extract about these companies from their job postings. And so this is this is where we start going into, like, combining the power of AI with the power of of Clay's customization, and then finally that that final layer of direct is coming in as that that content layer.\u003C\u002Fp>\u003Cp>So we've extracted into columns, sort of these different pain points slash operational insights. I shouldn't say pain point, actually. These are specifically how is the team operating, and they're stored in columns, which we'll use to upload that into those, different fields that Matt showed at the beginning. So great. We have our operational insight columns built out.\u003C\u002Fp>\u003Cp>We also use AI to develop, like, personalized headers and sub headers for that landing page. So, essentially, all the inputs we're putting into Directus are all developed in Clay call my call. And so we got personalized sub headers, which are really cool. And then we finally got to those pain points, which I was a little too excited to talk about. But, if we go here to generate, you'll kinda see the prompt, but we're giving it business context.\u003C\u002Fp>\u003Cp>We're giving it, what we do as an agency, as a fake agency. And then it we tell the AI simply what we would tell maybe a new intern or a new hire company, and and we kind of talk to the AI exactly like that, and we get great results. So we're saying, right, three pain points on their operations or go to market team using the inputs from these outputs that we found before. And, for each one kind of return just like a a quick one liner that we can upload to the website. And so this is an example of the final final result here.\u003C\u002Fp>\u003Cp>So that's all the customization we've done in Clay, and the final step to return this data back to Directus is using HTTP API call. Now when I first joined Clay, I'll be honest, I had no idea what this was. And it sounds complicated and scary, but it is a very we try to make it as easy as possible in Clay, and we literally tell you what to copy and paste in here. But this is essentially just a way to share data between the Clay table and Directus. And so in this case, there's a few methods, to get it's it's pretty self explanatory, actually.\u003C\u002Fp>\u003Cp>If you're getting data, that means you're returning data into this table. But in this case, we're posting data, sort of pushing data out. So you selected that as our method. Matt provided me an endpoint from Directus that we can then paste into here. And then, in our body, this was actually, Matt, you wanna Matt actually set this column up.\u003C\u002Fp>\u003Cp>Matt, how did you generate this this sort of body?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I used AI.\u003C\u002Fp>\u003Cp>Speaker 1: So, like I was I was about to say that. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Perfect. Yeah. No. Yeah. I mean, one of the that this was actually my first time setting this up, and, our, integrations engineer, Lynn Lindsay, has actually built a Directus specific Directus CMS HTTP API.\u003C\u002Fp>\u003Cp>So if you go to, like, set up an enrichment column, just search Directus and it'll pop up, and there's some instructions in there. But I we also have some docs, which I'll show once we hop over from here, of where you can find those. We also talk about how to get it set, set up on our side as well.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: So the item from get create update. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Exactly. So it's kind of alluding to all the hard work Matt did to set up this column. He's made it super simple and saved this as a public template that you'll be able to access. If you search up direct as HTTP in the clay search bar, you'll see this exact enrichment here, which is the get item from CMS collection.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Great. And, basically, to to to for the, for the JSON, for this, you know, be able to pull this into Directus, what I did when I say I used AI, like, I literally just took a screenshot of the data model from the Directus side that I showed earlier and then just dropped it in a cloud and was like, can you create, like, a JSON placeholder for Glay? And then Beautiful.\u003C\u002Fp>\u003Cp>My my favorite part actually is, like, mapping the actual columns to like, for some reason, it's just like it's like Legos. Like, I just love Yeah. Yeah. Slash then just, like, go through and pick it. Yeah.\u003C\u002Fp>\u003Cp>It's very satisfying.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's by the way, Legos for adults or Minecraft for adults is the analogy I use when I think about clay. A big reason why I joined the company. I get to work with Legos every day now. Yeah.\u003C\u002Fp>\u003Cp>Cool. So that that's that's the last step of the Clay workflow. All this data now, because of Matt's hard work with this HTTP API call, flows back in direct us. I will now pass it off back to Matt to show the final product.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Did, did you run those columns again? All five.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. All all six, actually. So Apple should have run as well. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Let me see.\u003C\u002Fp>\u003Cp>Speaker 1: So I'll you want me to rerun them real quick?\u003C\u002Fp>\u003Cp>Speaker 0: Toss it over. Toss the screen over. Okay. I'll just share my screen and see, and we'll just, like Cool.\u003C\u002Fp>\u003Cp>Speaker 1: Cut out as necessary. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. We'll, like, we'll do it live. Alright. So we have our direct distance here. If I refresh, everything went according to plan.\u003C\u002Fp>\u003Cp>So it looks like Apple got pushed over. So if you, on your side, run that Yeah. HTTP\u003C\u002Fp>\u003Cp>Speaker 1: Call call. Gotcha.\u003C\u002Fp>\u003Cp>Speaker 0: Again. Yes. So those should come in one by one. And while you're doing that so like I said, all of the data is automatically pushed over. I I didn't have to lift a finger for this.\u003C\u002Fp>\u003Cp>Where it gets interesting now is, like, we have the logo URL, so we can build, you know, like a we have, flows and directives, which I haven't really talked about much, but you can set up, automations from the data inside here. So we can run a flow that's like, hey. Like, take that URL, go and fetch the the logo, and then you'll have, like, you know, all of that to personalize on as well. So, it looks like everything is in here. Yeah.\u003C\u002Fp>\u003Cp>So it looks like all of our other ones got pushed as well. Cool. I mentioned I'll just show you really quick. So if you go to our docs and with that HTTP API that we set up. So we have in here, integrations page.\u003C\u002Fp>\u003Cp>So you can just come in and and search search search Clay, and you should be able to find, yeah, quick and easy setup, how to use, like, the templates and stuff here. And we tell you, everything pretty well. Shout out to Lindsay for for her work on all this. But cool. Alright.\u003C\u002Fp>\u003Cp>So let's see if it works. That's probably Yeah. Probably the most important part of this. Yeah. Alright.\u003C\u002Fp>\u003Cp>So we've got this. We've got the slug, which should auto apply, to the pages. I am gonna pop in. So I I mentioned this earlier, but we have, like the cool thing about the data in Directus is when you create these collections, you can do, like, many to one so you can associate separate collections. So you have a team that just has access to collections with, like, well based access control, and you can have a team that just has access to landing pages.\u003C\u002Fp>\u003Cp>Speaker 1: But they\u003C\u002Fp>\u003Cp>Speaker 0: can Nice. Their work can, like, counter, like, counteract. So or in a good way, not not in a bad way. But, like, for Apple, right, like, I don't know if we have I guess we can pull a SaaS in. So we would wanna pull in, like, the specific SaaS testimonial here, which you could probably trigger with another flow, but I'm not that advanced.\u003C\u002Fp>\u003Cp>But I also like to do some manual, manual work around this. So let's see. Moment of truth. So coming over to our front end here.\u003C\u002Fp>\u003Cp>Speaker 1: Got\u003C\u002Fp>\u003Cp>Speaker 0: basic page. Type in apple.\u003C\u002Fp>\u003Cp>Speaker 1: There you go.\u003C\u002Fp>\u003Cp>Speaker 0: Apple. Beautiful. Ops team. Wow. So we've got our subheader, All of our specific problems where it looks like they're hiring a lot of engineering, so we can for our products, you know, intake and routing workflows, that support the onboarding, that sort of stuff.\u003C\u002Fp>\u003Cp>Yeah. And then our our SaaS testimonial here. So it's like I mean, if if you take out our talking, what? This is probably, like, a two to five minute workflow if you have, like, all of the basic pieces, and you can scale ABN that fast, which with, like, personalization. That's actually good personalization.\u003C\u002Fp>\u003Cp>I like that that's what has always, like, impressed me with this is just how granular you can get and refine and, like, now you have this. Keep iterating, testing, see what resonates.\u003C\u002Fp>\u003Cp>Speaker 1: Yes. And I think, Matt, I'll say one thing here. With great power comes great responsibility. I think it's still on the person that's developing the workflow to have good taste. Good taste in what type of personalization do you do versus what you don't.\u003C\u002Fp>\u003Cp>Matt alluded to a great point earlier. Maybe don't mention their kids' Facebook, you know, profile or something random about them that that they're not gonna care about. It sounds contrived. People are tired of getting AI sloppy emails and over personalized messages. So there's a fine line of personalizing tastefully with something like this versus sort of being, having something contrived that's not gonna work.\u003C\u002Fp>\u003Cp>But, nonetheless, the technology is here. It's exciting, and now we're we're really happy to put this in in your hands.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. And that's like you just said, like, alluding back to when I said, like, when I said I'm in marketing and people probably cringed and run out, like, the garlic and holy water.\u003C\u002Fp>\u003Cp>Speaker 1: Like,\u003C\u002Fp>\u003Cp>Speaker 0: we we try to do it the right way. Like, we're we're trying to be there's, like, good marketing and bad marketing. And I tell, like, our engineering team all the time, like, I'm I'm trying to change the way that people think about it. Because a lot of, like Mhmm. A lot of it has been just, like, those quick growth hack things that I hate.\u003C\u002Fp>\u003Cp>But, when you do things like this and you actually start to build relationships and understand and show that, like, you are doing the work to learn about Mayo Clinic and, you know, some of the pains and struggles they have. Like, that's it's not contact level. You don't have to worry about PII. You're scraping all the available data you can find on the site. So, yeah, I I think this is you know, we keep alluding to it, but the way forward and, it's cool to see that Clay and Directus, like, just as a stack can can do it.\u003C\u002Fp>\u003Cp>Yeah. Obviously, AI helping a lot of things, but, I say it all the time. Everybody's becoming a builder, and you're probably seeing this over at Clay too. Like Yep. Them and moat.\u003C\u002Fp>\u003Cp>Like, this used to be, like, for all the developers in this call, probably a pain in the butt because you had marketing asking, like, can you create this page for me? Can you create this state? Like, how do I get this data here? And like I said, I've opened terminal before I worked here at Directus probably twice in my life, and now using cloud code. Being able to do all this myself and not have to bother anybody, that's the way things are shifting for for us as, you know, go to market team.\u003C\u002Fp>\u003Cp>And I'm I'm really excited to see, you know, where we land with it.\u003C\u002Fp>\u003Cp>Speaker 1: Absolutely.\u003C\u002Fp>\u003Cp>Speaker 0: Well, cool, man. Mohawk, I really appreciate you coming on today, giving us\u003C\u002Fp>\u003Cp>Speaker 1: Likewise.\u003C\u002Fp>\u003Cp>Speaker 0: You know, a quick view of Clay, how everything works. I'm so glad everything worked out. We didn't have any technical issues on either end. But, for everybody watching, like, all of the prompts that, were shared, I'll make sure that it's in, like, a accessible place. So if you wanna just grab those and test it yourself.\u003C\u002Fp>\u003Cp>Anything else from this call that is some, like, shareable assets or anything, I'll definitely be, sharing that as well. But, yeah, any anything you wanna add, Mohawk?\u003C\u002Fp>\u003Cp>Speaker 1: I I think that's all. This was great. I appreciate you, Matt, bringing me on to be able to show the power of Clay and direct us together. And I just hope that, you know, if you're watching this right now, you have the site, you have an idea. Hopefully, the idea juices are flowing right now, and we'll try to make it as easy as possible for you guys to get this kind of up and running.\u003C\u002Fp>\u003Cp>I will say, Matt, to your point, lead to speed is everything now, and I think speed of execution is everything now, right, in the AI age. So, this hopefully will save a lot of time, but also kind of enable you to do things that you were not were not possible before. I think that's the most exciting part for me. But yeah. Again, thank you, Matt.\u003C\u002Fp>\u003Cp>Appreciate the time.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Awesome.\u003C\u002Fp>","Hey. What's up, everybody? Matt here from the direct us marketing team. I am super excited today to be showing off, what I think is a really cool session personally. Kinda set the scene a little bit. Obviously, I said marketing, and that's probably terrifying for a lot of people. But, I I like the I I truly believe the gap between, like, technical and nontechnical is getting, you know, smaller. So Mhmm. That's why I'm super excited to have Clay and my friend Mohawk Desai here. Mohawk, how are you, man? Hey. How's it going, Matt? Thanks for bringing me on. Appreciate it. Yeah. Yeah. Absolutely. So most of our audience is probably not familiar with Clay. Yeah. I will say, like, I I think Clay was kind of the first step for me to get to become more technical, with a lot of enrichment and sort of stuff, but like that. But maybe you could give kind of a a brief Yeah. Overview of what Clay is for the folks. Yeah. Absolutely. So at a high level, Clay is a a GTM orchestration and data enrichment tool. And I think a good analogy is I would view Clay as almost a development environment, except not for technical folks, not for coders, but for GTM nerds, like me and like Matt. So, yeah, Clay is if you look at Clay, and we'll jump into the product in a second, but it looks like Excel. There's a lot of AI formulas. There's a lot of, sort sort of technical concepts like webhooks and HTTP APIs. But it is really built for you know, right now, one of our biggest personas is, like, rev ops people at companies, but that's kind of expanding now into marketing folks, into mark ops, and to other personas down the road as well, including, like, sales. But it's really for, like, systems thinkers who wanna build their sort of GTM engine. Clay is kind of the perfect middle ground for all of that, where you can have your entire tech stack flow into Clay. We integrate with 90 plus providers, for data, but as well as sort of GTM execution tools like Gong. And so Clay is kind of like the glue between all your different tools, as well as a place where you could secure data, the things like emails and phone numbers. So, really, it's sort of a one stop shop for GTM orchestration. It's probably the best way I can describe Clay. Yeah. Wow. That's a you're reading off like a prompter there. That was awesome. No. The way that you yeah. The way you said, like, it looks like a spreadsheet when you first get into it, that was, like, my first reaction when I first started testing it. You know, I was like, what? This just looks like another spreadsheet. And then when you start looking at the integrations and the possibilities with, like, enrichment waterfalls, that's what, like, had my holy But speaking of integrations, you know, Directus is, you know, we're we're we've always been kind of a content management system for a lot of folks. So all of the pages for your website, it could be the back end for applications. There's a lot of different use cases. And as we've been getting Clay and Directus closer together, our team has found a lot of really cool use cases that you can really dive into, with that enrichment data. One of which I'm really excited to talk about today. And I feel like was this, like, Clay's, like, bread and butter early on, this sort of, like, personalization type play? I would say not early on. I think, actually, early on in the moat was this the data enrichment piece. On the back end to find emails and phone numbers, Clay takes a waterfall approach where we don't just check one database, but we'll cycle through 90, again, 90 plus providers to find an email or phone number. And so that was the note early on was a higher coverage rate and higher accuracy for data versus under other individual vendors. But, Matt, to your point, I think in the past year or so, a lot of smart rev ops people and marketing people are realizing that Clay's a really good place to do ABM at scale, so account based marketing at scale. And one of them is, yes, starting to create personalized landing pages at scale for your target accounts. But this can also leak into other cool use cases like you can create Google Slides at scale that do the same thing. We have a direct integration of Google Slides, and and so on and so forth. And now in the in the age of AI with all the tools available, we're trying to be a little bit more open to to tools that will allow you to do this. But Directus is, like, a perfect, perfect example of a great integration where we can, really customize our outreach and get those target accounts in our book. Yeah. Yeah. And when you say ABM, I'm sure the the audience is like, another acronym, like marketing and some of the acronyms. But, for folks that don't know, so ABM is is kind of an approach to marketing and sales that, in my opinion, is the way to do it because it's way less intrusive than just, like, shotgun blasting a bunch of people that have no idea who you are. There's a lot of research and, personalization that goes into identifying, you know, 50 accounts, a 100 accounts that are assigned specifically to, like, your go to market team or your reps. So they get to build relationships and really understand, like, the pains and and things within those those companies. And that kinda sets the scene for today, which is, we're gonna do a workflow from beginning to end and and show you how we built it, basically, where it's not like those shitty emails you'll get that's like, hey. Like, I saw you play volleyball, and now let's get on a fifteen minute call. Like, it that's not personalization. Like, personalization is looking at, like, who a company is hiring, you know, what teams are growing, what is probably because of that, some of the struggles they have right now that your product or solution could maybe solve for, and then building out pages and stories around that. But that's hard to do without automation, without things like Clay for the data enrichment. So that's what I'm excited to talk about today. Amazing. Let's do it. Awesome. Alright. So let's hop in. I hope everything is gonna break. I know as soon as I start sharing stuff. It always I don't know why. It always happens. But, let's start with, like, kind of what we're gonna have at the end. That that's kind of our our end goal. We'll start there and work backwards. So, this is just a quick landing page that I spun up, like, ten minutes before this, with clogged code. UIs, you know, you you can spin up a UI really fast now. But the goal is to have this fake AI agency that we just made up called Cottontail, then AI operations team. So they'll handle a lot of the, the manual processes and things like that. So we're gonna have these customized landing pages where it's like, this is the typical personalization you see. Like, all your company name, your, you know, subheader. But what I'm really excited about is focusing in on this section, which is, like, we're gonna use Clay to basically pull out, you know, who are the the number the highest number of jobs, what teams are really growing, and run a couple of, you know, flows on, what does that mean? Like, what are those struggles, the problems they probably have because of that? And then how can our solution fix that? So, basically, a mirror image of what I just said, but this is what it looks like in practice. Any thoughts on, like you know, obviously, working at Clay, like Yeah. Clay, in my opinion, is one of the best marketing. They're I they're like the shining pinnacle right now, everything that y'all do. Mhmm. And I wanna say ABM is a big part of that. Right? Or it could be cool at all. No. Absolutely. As you mentioned, ABM is becoming even a a larger and larger segment of a use case at Clay. I think typically, customers will come in and start with basic enrichment, basic, automated outbound or automated inbound, but those really savvy, you know, companies that know they have sort of a target list of a hundred, two hundred, you know, large companies or or target accounts. ABM is really, really effective at breaking in when everything else doesn't work. And, our CEO, Kareem, has a very cool, analogy or a sort of phrase that encapsulates what ABM should look like, and that's surround sound. You're surround sound of your customers, in in a way that feels so personalized that they they feel obligated to sort of respond or at least curious enough to alright. What do these guys have to say to us? So ABM is like there's a lot that goes into it. One of them is this use case we're gonna jump into, which are these, like, landing pages. Matt, you're correct. This is becoming a very popular thing amongst advanced GTM teams. But outside of just custom landing pages, there's a lot of other things that kind of fold up into ABM. One of them is signals. So taking a signal based approach and having your reps work off intent based data rather than static third party data. So that's one piece. And then the third piece could also be ads. Clay also just launched a new ads tool, but in general, ads is just another way to sort of, again, surround sound, your your personas at these companies. So now imagine a head of marketing at one of your target accounts is getting brilliant, custom landing pages, great emails from your sales teams, are are getting, you know, timely outreach based on some funding events or relevant events to your company, and then you're also seeing ads on LinkedIn and Meta, it's gonna be hard to not be curious about what you do. So that's ABM in a nutshell. And, Matt, I think you're right that that this is picking up a lot in the market. Yeah. Yeah. That's that's awesome. And when you start folding in, like, billboards and buses with your Yes. Exactly. It's inescapable, man. Yes. Inescapable. Exactly. Exactly. But in a, like, in a way, I think it's it's better than just kind of blindfold just going out there, like, blasting you know, pulling a list of a 100,000 contacts and, like, spray and pray method. Spray. Exactly. So, so yeah. So that's what we're gonna do today is really focus on on building these pages out, which I think is really the foundation of it. This is not only something for the customers, but also, you know, for your team members as they start to do the research having, like it's basically like a slide deck in in a lot of ways. So just to kind of show you how I've set up direct us in the back end, so we've got, Cottontail here. I've created two collections. So we've got a landing pages collection. This is where all of the stuff we're gonna do in clay is gonna push, right here. So looking at the actual data model schema for this, it's it's basically everything I showed on that that front end page. So Mhmm. Company name, we're gonna have slug, so this will, like, auto generate the pages when they they come in. Everything is powered by by Railway, which is really the one of the easiest ways to get started with Directus and and build front ends on. We got industry. We got a little testimonial thing. We'll see if we have enough time. I I definitely wanna show how the mini to one, like, data schema stuff works. And then we'll have the personalization content kind of backfilled here. So, like, as developers or technical people are watching this, like, this is how you can knock something out. Like, if you do this for your marketing team, it would be, like, you're you're the hero for for months just giving them something like this. But, also, if you're building applications or products, like, this is a great way to get it into the market and start, you know, marketing the stuff that you're building. But yeah. So that's all built out. Just to show you what it would look like if you were to manually fill this out, yeah, just like a a typical form here. This is just how I have it laid out, but you can do it a lot of different ways. But, but, yeah, I that's really all I had for the director side. Like, as a back end, it's not sexy. It's just like we need to push all this data here, but I'm really excited to show what we've got for Clay. So, Mohawk, I'll actually turn it over to you. Amazing. Thank you, Matt. Great intro. Great context. So now what I'm gonna do is share my screen, and go into Clay. So for folks that have never seen Clay, welcome to Clay. This is the spreadsheet looking tool that you keep hearing about, and so this is what it looks like. So as a starting point for this example, we've uploaded, as our first step, a list of company domains, just their websites. In Clay, though, you can import these company domains from a bunch of other sources. So we have our own company search where you can filter, you know, by, industry, location, revenue count, etcetera. So if you don't already have a list of target accounts in your CRM or in a CSV, you can use Clay to find those target accounts based on your filters. And, as I mentioned, you can upload a CSV as well or connect your CRM and have a live sync between Salesforce or HubSpot, and and Clay. But, again, zooming back out, we are starting with a list of company domains that we have randomly chosen and put into this table. That's the only input we're putting in. That's the I think the key point of all this is that all these other columns you see that have been set up, you set them up once, and anytime you add a new input, it'll automatically run for, for that new input. So, actually, Matt, I I I'm gonna go ahead and sort of enter a new domain here. And, this is my favorite company in the world. So Apple, we're gonna put that a bit second to play. And so I've put this in, and as you can see, it's already autofilled. And I'll kind of now go column by column and break down what we did here to get to that landing page endpoint that Matt showed. So the the first thing is we ran this action called enrich company, and that essentially, in in a JSON array, will return a bunch of data about the company that could be useful. So things like their employee count, their revenue range, their, again, their their, country of origin, things like that. And so the the relevant data points we wanted to pull out for that landing page for each of these companies are really just the company name, the slug, the employee count, and the industry. What we also got, which is pretty cool, and this is kind of taking customization to that, like, final level, is we have URLs to the company's logo as well as a screenshot of their website. So now we've imported their their brand, what they look like, what they feel like, and that can be used if you'd like in your custom landing page, that you're sending to these target accounts. Then from there, we look into jobs. Now jobs in general, I think, are very underrated data point that GTM team should be indexing on because jobs tell you a lot about the company. Tells you what their tech stack is for specific roles. Right? Like, what Mhmm. In that little section that says you should have experience with these technologies, it tells you what they're using. It tells you about their sort of their mission, what they're up to, kind of everything that a candidate would need to know happens to be a lot of the information that we wanna know as sellers and marketers. So, we went ahead and ran an action called find active job openings at this company. And, all all we really needed here was the domain as the input. Again, we're working off of this one input. It's really all we need. And so once you put your domain into this action, that we, you know, we set sort of our filters and we run it, it'll return how many jobs are sort of posted for this as well as the link to each job, which is wonderful because we can then run an AI column on this, active job opening column to kind of analyze those job postings and extract the key insights that we want, like I mentioned. And so we're able to find, which teams are growing fastest, which functions appear to be manual or understaffed. And imagine replacing all of this with the pain points you wanna extract about these companies from their job postings. And so this is this is where we start going into, like, combining the power of AI with the power of of Clay's customization, and then finally that that final layer of direct is coming in as that that content layer. So we've extracted into columns, sort of these different pain points slash operational insights. I shouldn't say pain point, actually. These are specifically how is the team operating, and they're stored in columns, which we'll use to upload that into those, different fields that Matt showed at the beginning. So great. We have our operational insight columns built out. We also use AI to develop, like, personalized headers and sub headers for that landing page. So, essentially, all the inputs we're putting into Directus are all developed in Clay call my call. And so we got personalized sub headers, which are really cool. And then we finally got to those pain points, which I was a little too excited to talk about. But, if we go here to generate, you'll kinda see the prompt, but we're giving it business context. We're giving it, what we do as an agency, as a fake agency. And then it we tell the AI simply what we would tell maybe a new intern or a new hire company, and and we kind of talk to the AI exactly like that, and we get great results. So we're saying, right, three pain points on their operations or go to market team using the inputs from these outputs that we found before. And, for each one kind of return just like a a quick one liner that we can upload to the website. And so this is an example of the final final result here. So that's all the customization we've done in Clay, and the final step to return this data back to Directus is using HTTP API call. Now when I first joined Clay, I'll be honest, I had no idea what this was. And it sounds complicated and scary, but it is a very we try to make it as easy as possible in Clay, and we literally tell you what to copy and paste in here. But this is essentially just a way to share data between the Clay table and Directus. And so in this case, there's a few methods, to get it's it's pretty self explanatory, actually. If you're getting data, that means you're returning data into this table. But in this case, we're posting data, sort of pushing data out. So you selected that as our method. Matt provided me an endpoint from Directus that we can then paste into here. And then, in our body, this was actually, Matt, you wanna Matt actually set this column up. Matt, how did you generate this this sort of body? Yeah. I used AI. So, like I was I was about to say that. Yeah. Perfect. Yeah. No. Yeah. I mean, one of the that this was actually my first time setting this up, and, our, integrations engineer, Lynn Lindsay, has actually built a Directus specific Directus CMS HTTP API. So if you go to, like, set up an enrichment column, just search Directus and it'll pop up, and there's some instructions in there. But I we also have some docs, which I'll show once we hop over from here, of where you can find those. We also talk about how to get it set, set up on our side as well. Yep. Yeah. So the item from get create update. Yeah. Yeah. Exactly. So it's kind of alluding to all the hard work Matt did to set up this column. He's made it super simple and saved this as a public template that you'll be able to access. If you search up direct as HTTP in the clay search bar, you'll see this exact enrichment here, which is the get item from CMS collection. Yeah. Yeah. Great. And, basically, to to to for the, for the JSON, for this, you know, be able to pull this into Directus, what I did when I say I used AI, like, I literally just took a screenshot of the data model from the Directus side that I showed earlier and then just dropped it in a cloud and was like, can you create, like, a JSON placeholder for Glay? And then Beautiful. My my favorite part actually is, like, mapping the actual columns to like, for some reason, it's just like it's like Legos. Like, I just love Yeah. Yeah. Slash then just, like, go through and pick it. Yeah. It's very satisfying. Yeah. That's by the way, Legos for adults or Minecraft for adults is the analogy I use when I think about clay. A big reason why I joined the company. I get to work with Legos every day now. Yeah. Cool. So that that's that's the last step of the Clay workflow. All this data now, because of Matt's hard work with this HTTP API call, flows back in direct us. I will now pass it off back to Matt to show the final product. Yeah. Did, did you run those columns again? All five. Yeah. All all six, actually. So Apple should have run as well. Yeah. Let me see. So I'll you want me to rerun them real quick? Toss it over. Toss the screen over. Okay. I'll just share my screen and see, and we'll just, like Cool. Cut out as necessary. Yeah. Yeah. We'll, like, we'll do it live. Alright. So we have our direct distance here. If I refresh, everything went according to plan. So it looks like Apple got pushed over. So if you, on your side, run that Yeah. HTTP Call call. Gotcha. Again. Yes. So those should come in one by one. And while you're doing that so like I said, all of the data is automatically pushed over. I I didn't have to lift a finger for this. Where it gets interesting now is, like, we have the logo URL, so we can build, you know, like a we have, flows and directives, which I haven't really talked about much, but you can set up, automations from the data inside here. So we can run a flow that's like, hey. Like, take that URL, go and fetch the the logo, and then you'll have, like, you know, all of that to personalize on as well. So, it looks like everything is in here. Yeah. So it looks like all of our other ones got pushed as well. Cool. I mentioned I'll just show you really quick. So if you go to our docs and with that HTTP API that we set up. So we have in here, integrations page. So you can just come in and and search search search Clay, and you should be able to find, yeah, quick and easy setup, how to use, like, the templates and stuff here. And we tell you, everything pretty well. Shout out to Lindsay for for her work on all this. But cool. Alright. So let's see if it works. That's probably Yeah. Probably the most important part of this. Yeah. Alright. So we've got this. We've got the slug, which should auto apply, to the pages. I am gonna pop in. So I I mentioned this earlier, but we have, like the cool thing about the data in Directus is when you create these collections, you can do, like, many to one so you can associate separate collections. So you have a team that just has access to collections with, like, well based access control, and you can have a team that just has access to landing pages. But they can Nice. Their work can, like, counter, like, counteract. So or in a good way, not not in a bad way. But, like, for Apple, right, like, I don't know if we have I guess we can pull a SaaS in. So we would wanna pull in, like, the specific SaaS testimonial here, which you could probably trigger with another flow, but I'm not that advanced. But I also like to do some manual, manual work around this. So let's see. Moment of truth. So coming over to our front end here. Got basic page. Type in apple. There you go. Apple. Beautiful. Ops team. Wow. So we've got our subheader, All of our specific problems where it looks like they're hiring a lot of engineering, so we can for our products, you know, intake and routing workflows, that support the onboarding, that sort of stuff. Yeah. And then our our SaaS testimonial here. So it's like I mean, if if you take out our talking, what? This is probably, like, a two to five minute workflow if you have, like, all of the basic pieces, and you can scale ABN that fast, which with, like, personalization. That's actually good personalization. I like that that's what has always, like, impressed me with this is just how granular you can get and refine and, like, now you have this. Keep iterating, testing, see what resonates. Yes. And I think, Matt, I'll say one thing here. With great power comes great responsibility. I think it's still on the person that's developing the workflow to have good taste. Good taste in what type of personalization do you do versus what you don't. Matt alluded to a great point earlier. Maybe don't mention their kids' Facebook, you know, profile or something random about them that that they're not gonna care about. It sounds contrived. People are tired of getting AI sloppy emails and over personalized messages. So there's a fine line of personalizing tastefully with something like this versus sort of being, having something contrived that's not gonna work. But, nonetheless, the technology is here. It's exciting, and now we're we're really happy to put this in in your hands. Yeah. Yeah. And that's like you just said, like, alluding back to when I said, like, when I said I'm in marketing and people probably cringed and run out, like, the garlic and holy water. Like, we we try to do it the right way. Like, we're we're trying to be there's, like, good marketing and bad marketing. And I tell, like, our engineering team all the time, like, I'm I'm trying to change the way that people think about it. Because a lot of, like Mhmm. A lot of it has been just, like, those quick growth hack things that I hate. But, when you do things like this and you actually start to build relationships and understand and show that, like, you are doing the work to learn about Mayo Clinic and, you know, some of the pains and struggles they have. Like, that's it's not contact level. You don't have to worry about PII. You're scraping all the available data you can find on the site. So, yeah, I I think this is you know, we keep alluding to it, but the way forward and, it's cool to see that Clay and Directus, like, just as a stack can can do it. Yeah. Obviously, AI helping a lot of things, but, I say it all the time. Everybody's becoming a builder, and you're probably seeing this over at Clay too. Like Yep. Them and moat. Like, this used to be, like, for all the developers in this call, probably a pain in the butt because you had marketing asking, like, can you create this page for me? Can you create this state? Like, how do I get this data here? And like I said, I've opened terminal before I worked here at Directus probably twice in my life, and now using cloud code. Being able to do all this myself and not have to bother anybody, that's the way things are shifting for for us as, you know, go to market team. And I'm I'm really excited to see, you know, where we land with it. Absolutely. Well, cool, man. Mohawk, I really appreciate you coming on today, giving us Likewise. You know, a quick view of Clay, how everything works. I'm so glad everything worked out. We didn't have any technical issues on either end. But, for everybody watching, like, all of the prompts that, were shared, I'll make sure that it's in, like, a accessible place. So if you wanna just grab those and test it yourself. Anything else from this call that is some, like, shareable assets or anything, I'll definitely be, sharing that as well. But, yeah, any anything you wanna add, Mohawk? I I think that's all. This was great. I appreciate you, Matt, bringing me on to be able to show the power of Clay and direct us together. And I just hope that, you know, if you're watching this right now, you have the site, you have an idea. Hopefully, the idea juices are flowing right now, and we'll try to make it as easy as possible for you guys to get this kind of up and running. I will say, Matt, to your point, lead to speed is everything now, and I think speed of execution is everything now, right, in the AI age. So, this hopefully will save a lot of time, but also kind of enable you to do things that you were not were not possible before. I think that's the most exciting part for me. But yeah. Again, thank you, Matt. Appreciate the time. Yeah. Awesome.","9af7ccb4-866a-402c-90e9-28339beda884",[201,202],"75435596-a393-420e-9b1f-1761ced507bf","801b909b-3062-459c-a6bb-a8d67285d1c3",[],{"year":139,"number":140,"id":141,"episodes":205,"show":206},[143,144,145,146,147,148,123,149],{"title":151,"slug":152},{"id":147,"slug":208,"vimeo_id":209,"description":210,"tile":211,"length":212,"resources":12,"people":12,"episode_number":213,"published":130,"title":214,"video_transcript_html":215,"video_transcript_text":216,"content":12,"seo":217,"status":19,"episode_people":218,"recommendations":221,"season":222},"web-to-backend","1176299027","The web is full of data. We'll show you how to extract it for AI, automation, or whatever you're building.","d8202d8b-9701-4cf8-bee1-b4f595512685",43,5,"Web to Backend","\u003Cp>Speaker 0: Alright, folks. Welcome to another session here at Directus Leap Week. I am joined by Leo Gregorio from Fire Crawl. We've got some exciting stuff for you today. We're gonna be taming unstructured data.\u003C\u002Fp>\u003Cp>Right? Turning the wild web into ready APIs using FireCrawl and Directus, to to serve those APIs. I am Brian Gillespie, a staff product engineer here at Directus. And, again, joined by special guest, Leo Gregorio from FireCrawl. Leo, maybe maybe I'll kick it over to you for a quick intro for everyone.\u003C\u002Fp>\u003Cp>Speaker 1: It's a pleasure to be here, Brian. I've used Directus for quite a long time, and I'm stoked to be here.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I'm super excited to have you, man. Yeah. I've messed around with FireCrawl a little bit before this. I agree.\u003C\u002Fp>\u003Cp>I'm I'm no way an expert. I'm glad to hear that you've messed with Directus before. But for the folks who are new to FireCrawl, you know, can you take a few moments just to explain what it what the tool is? Like, what do you guys do? What do you enable for developers?\u003C\u002Fp>\u003Cp>Speaker 1: Sure. Over at FireCrawl, we specialize at providing web data to agents. Not only agents, we can also have endpoints where you can build your data, data web fetcher upon those APIs. But we mainly focus on providing data over to agents in a way that, it's it's basically giving years' eyes over to your cloud code instance to wherever you're really building, to fetch for data. You should be thinking of FireCrawl to do that.\u003C\u002Fp>\u003Cp>Speaker 0: Sick. What are, like, some of the, like, specifics within that? Like, what what capabilities does FireCrawl have?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. To fetch for data, I like to compare with our manual way of fetching data. So if you want to fetch a specific website, what you would do is search for something. So we have that specific endpoint on searching. You can then use scrape to scrape that specific URL.\u003C\u002Fp>\u003Cp>And from the scrape endpoint, we have a bunch of different formats that we will likely be exploring through this presentation, which are screenshot. You can actually screenshot the entire page. There's a bunch of different ways to take that screenshot. It can be exactly what we're seeing right now. It can be of the entire page, mobile.\u003C\u002Fp>\u003Cp>So that's just an example of one of our formats. We can fetch for summary. We allow the agents to execute and interact with the page, and that helps a lot with context, for example. So you're not only fetching pure HTML. You can fetch clean markdown.\u003C\u002Fp>\u003Cp>You can fetch exactly what you need from that given page, and that's that's the beauty of it. That's what we try to optimize to. And, yeah, we we've been been pretty successful, while doing so.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah, man. I I've watched the growth of the last couple years, so kudos on that. It's it's a really interesting tool. And as I understand it, like, you guys have an agent thing on your endpoint as well. So you could you could basically call an agent your agent from a different agent.\u003C\u002Fp>\u003Cp>Yes. And and get really Yes. Metal with it, which is nice. I I love the ability to, again, just throw in a URL in here and get structured data out of the other side because it Oh, yeah. Complements perfectly with Directus.\u003C\u002Fp>\u003Cp>And what we do is, like, if if you give us structured data, we'll give you ready to go APIs and permissions and and all of that. So, super excited for this. Alright. Let's get down to brass tacks. Right?\u003C\u002Fp>\u003Cp>What are we gonna build today? So the scenario that I've got, basically, I've got a directory of different software. This is on my own site. So this is a a Better Sign Shop. I love the print and sign industry.\u003C\u002Fp>\u003Cp>I can't get out of it even though I've tried. Software comes up a lot. You know, I I guess it does in any industry these days. But, for sign and print, you know, how do they manage their business? There's a bunch of different tools for that.\u003C\u002Fp>\u003Cp>And we've got a directory here that, is sorely unmaintained. Right? All this data was collected manually some years ago, and we've got things like features of a particular software. On on some pages, we have pricing information. But, you know, it's a lot to manage all this personally.\u003C\u002Fp>\u003Cp>It it would be a lot to put on the vendor of the software to maintain. It'd be a lot for anybody on my side to say, okay. Keep all this up to date. Does this seem like a good use case for Firegirl?\u003C\u002Fp>\u003Cp>Speaker 1: It's a perfect use case. Since we provide structured data, you can fetch for that. There's plenty of ways we can fetch for that. So if you already have the specific URL, you can use scrape or you can, like, use the agent that you mentioned. And by the way, the design of that website is pretty neat.\u003C\u002Fp>\u003Cp>Okay. Thank you, man. Yeah. If my, pretty neat.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Thank you, man. Yeah. My, I I've got three daughters. They love pink and purple.\u003C\u002Fp>\u003Cp>So, hey, like, anything that I do personally, like, it has to have pink in it. Right? Alright. So let's let's talk through maybe how we're gonna build this real quick, and then we will dive into actually, like, going through it. Right?\u003C\u002Fp>\u003Cp>So we've got Okay. We've got a software collection. You know, mostly, I wanna have, like, pricing data. We've got, like, a name. Let's just call it, like, a summary.\u003C\u002Fp>\u003Cp>We're providing a URL. What else do we have? We got pricing data has to have a specific format, which should be good. I'm yeah. Maybe pricing is the the best place to start.\u003C\u002Fp>\u003Cp>So a like, that is going to be I don't know the pricing URLs for this, but I do have the domain. So, like, walk me through what what this is gonna look\u003C\u002Fp>\u003Cp>Speaker 1: like on the FireCrawl side. So from your website, it seems like you already have specific URLs. It doesn't seem like the idea is having a bunch of different URLs. It seems like it's focused on quality. Mhmm.\u003C\u002Fp>\u003Cp>Therefore, if we were to have the specific pricing page, it would be better just because we can send off just one endpoint. It would be just scrape. So if we have that in the database, we can just search for that specific URL and get everything, all the structure, the price, we can check track if the the price changed, everything. If we don't, if the idea is scaling this, so for example, if your website were to have anyone add, any, type of software there Mhmm. This would scale up.\u003C\u002Fp>\u003Cp>And we wouldn't have control over what is the URL paid, the the pricing page, the URL for everything. Right. And then we'd have to create some kind of system where we search and then actually find that that the the pricing page. We can either do that with crawl, because inside of crawl, we have an LLM, or we can use agents to grab everything.\u003C\u002Fp>\u003Cp>Speaker 0: I I got you. What do you recommend in this situation? I'll just go back to FireCrawl here and go to my dashboard.\u003C\u002Fp>\u003Cp>Speaker 1: Because we I'd recommend going for for the pricing page, and we can test that inside of the playground for fire crawl. Let me send the link over.\u003C\u002Fp>\u003Cp>Speaker 0: I will look here in the chat. Here it is. Alright.\u003C\u002Fp>\u003Cp>Speaker 1: So grab any pricing page that we already have for one of those software queries.\u003C\u002Fp>\u003Cp>Speaker 0: Aware of. Okay. I think I think this is the right one. Let me make sure. Pricing.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. There we go.\u003C\u002Fp>\u003Cp>Speaker 1: Nice. So\u003C\u002Fp>\u003Cp>Speaker 0: we're just gonna throw this\u003C\u002Fp>\u003Cp>Speaker 1: in. Perfect. As for the format, you can select, let's see. Jason.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Let me zoom back out just a little bit. Alright. So we're gonna\u003C\u002Fp>\u003Cp>Speaker 1: hit edit options. Perfect. And that's basically an LLM that understands what it's seeing inside of the page, and you can prompt it to fetch only the pricing data from that specific page.\u003C\u002Fp>\u003Cp>Speaker 0: Gotcha. Okay. So let me pull this up over here. Oh, format JSON. Alright.\u003C\u002Fp>\u003Cp>So we're gonna edit our options. Right? And here is just basically going to be what I want from the pricing data. Right? So we are going to find this particular one in here.\u003C\u002Fp>\u003Cp>We're gonna look at this and alright. So Perfect.\u003C\u002Fp>\u003Cp>Speaker 1: You already have the schema. Like a\u003C\u002Fp>\u003Cp>Speaker 0: Like a can I just paste JSON here, and it'll pick this up Yep?\u003C\u002Fp>\u003Cp>Speaker 1: Or no? I don't think you can, that would be all It's gotta be, it's gotta be\u003C\u002Fp>\u003Cp>Speaker 0: JSON schema. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: But that's a nice idea. We should add a way to get grab an example and produce the schema. That's that's something.\u003C\u002Fp>\u003Cp>Speaker 0: Starting price, that's gonna be a number. Actually, it's a string.\u003C\u002Fp>\u003Cp>Speaker 1: I I think the first one should be plans, and and it should be an array. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: It should be right. Nested dot. Yeah. Got you.\u003C\u002Fp>\u003Cp>Speaker 1: And then it's an array of objects.\u003C\u002Fp>\u003Cp>Speaker 0: Objects. Perfect. Starting price. And we want pricing\u003C\u002Fp>\u003Cp>Speaker 1: period. Oh, no. For each Actually, inside of the the array of objects, it would be, like, the the plants. Oh, yeah. I think you I think you were go going right, and I yeah.\u003C\u002Fp>\u003Cp>We reversed it. I was just, like, the array.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Sorry. No. All good. I I started typing it, and I realized it as well, man.\u003C\u002Fp>\u003Cp>No worries. It's usually me, like, fat fingering typos on these things instead of, like, actually typing something completely wrong. But, billing cycle. Cool. Billing period.\u003C\u002Fp>\u003Cp>And then we have features, which is gonna be an array itself, array of strings. I got highlight, but I don't think we really need highlight in this case. Okay. Hit save options. Okay.\u003C\u002Fp>\u003Cp>Yeah. Cool. So anything else that I need to That's a checkbox.\u003C\u002Fp>\u003Cp>Speaker 1: It. We can hit start scraping, and let's see what we get. Depending on what we get, maybe the website has a heavy JavaScript, and that might require us to wait a couple seconds before applying the scrape. So that's where the other options comes in. Sick.\u003C\u002Fp>\u003Cp>Alright. There it is.\u003C\u002Fp>\u003Cp>Speaker 0: Price. Sick. Okay. Shopbox Express. Features.\u003C\u002Fp>\u003Cp>Yeah. So we could just it's probably wise to pull this up side by side just to verify what's what's coming in. One zero nine plus 29 user a month. The billing cycle is monthly. We have features, pricing tools.\u003C\u002Fp>\u003Cp>Yeah. This is this is pretty much one to one. I like that the thing truncated, like, some of this data because it's not really necessary.\u003C\u002Fp>\u003Cp>Speaker 1: The show can specify a bit more because the pricing, depending on the the the the column, maybe we don't want exactly the same way the the pricing is there. But\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And is that done just via the prompt or, like, in the actual JSON structure? Or\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. We we can decide either if, because we can have, like, a strategy where the price could be a number. Right? And then it would be forced to place only a single price and then have, like, optional prices, right Yeah. I get it.\u003C\u002Fp>\u003Cp>Yeah. So it's really it's really up to whatever we're really building with that.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So it looks like the, like, the the way I've got the front end for this particular thing structured. Right? We're relying on let's take a look at what the pricing plans look like. Right?\u003C\u002Fp>\u003Cp>So we've we've got this really specific, like, format here of, like, we need a number there that's large. Then we've got, like, some additional stuff, and then we have, like, the month. So\u003C\u002Fp>\u003Cp>Speaker 1: let let's have a price, which is a number, a float. Right? And then, additional pricing information. How about that?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So we got price. That's a number. Alright. Let's just go back to direct us.\u003C\u002Fp>\u003Cp>Cool. Price. Alright. So would we just specify this, like, up in the prompt of, like, billing cycle should be something like slash month or slash is this the best way to inform it?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That that's that's one way that should work pretty fine.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. In this case, we'll say billing period just captures additional per user or extra cost. Alright. Let's see what that does. Let's see.\u003C\u002Fp>\u003Cp>That gives us what we're looking for. And if not, we'll just work on actually integrating this into Directus instead of actually pulling. K. 29. Okay.\u003C\u002Fp>\u003Cp>Price slash per month. 29 per user per month. Yeah. So this is already looking much better. Sick.\u003C\u002Fp>\u003Cp>Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. I think that's that's better for the front end.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. So if I hit go get code here, this is gonna give me what I need to integrate this inside Directus.\u003C\u002Fp>\u003Cp>Speaker 1: Exactly. You can get a JavaScript code on on on on the right.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Okay. Yeah. And I could also, like, get curls. There's a couple different ways to do this.\u003C\u002Fp>\u003Cp>Like, we could easily, like, write an extension inside Directus that could could do this for us. Since this seems pretty lightweight, we could just use direct as flows for this as well. And I I think we've even got, like, a fire crawl extension in the marketplace for this, but let's just let's just figure out how to do this, through flows. That might be the the easiest, quickest way here so we don't have to worry about distributing an extension anywhere. Alright.\u003C\u002Fp>\u003Cp>So Let's go. We will set up a new flow. I think we probably just, like, manually trigger this, and then we could set it up on a later. Alright. So we're gonna say fetch pricing data.\u003C\u002Fp>\u003Cp>Sick. Alright. We're gonna manually trigger this as a hook. We are looking for this is always whenever you do a demo, you always find flaws in your own software. We'd need a we'd need a search for this for sure.\u003C\u002Fp>\u003Cp>Alright. So we're gonna manually trigger this on the software collection. Next, we would just do what we call a HTTP webhook requesting URL. That's the operation. We'll say call fire crawl.\u003C\u002Fp>\u003Cp>And I'm gonna definitely have to roll the token after this. But\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I was just about to mention.\u003C\u002Fp>\u003Cp>Speaker 0: Luckily, this is also a local host as well. Alright. So we're just gonna add a header, authorization. Bear no. I could also, like, throw this in the EMV, but we're not gonna bother with that for now.\u003C\u002Fp>\u003Cp>Authorization. There's those typos that I spoke about. What else we need? Content type. Application.\u003C\u002Fp>\u003Cp>JSON. Sick. Okay. And then here's the data. So that's what we're sending in the body, except the only thing that we're gonna have to change here, right, is the URL that we're we're triggering this from.\u003C\u002Fp>\u003Cp>So Exactly. In this case, Directus has this functionality where we could do something like this, where we add, like, a a mustache syntax, and we can say trigger dot body dot, I don't know what we're gonna call this pricing URL. I I think it may be payload. I don't know. It will we'll test this in a moment.\u003C\u002Fp>\u003Cp>So, basically, what this will do is populate that from what we call the the trigger. And I'm just gonna go in and because we're not tracking the URL, maybe we just we do that pricing URL right here. Pricing page URL stream. Cool. Hit save.\u003C\u002Fp>\u003Cp>Alright. And before we add anything else, alright, all I'm gonna do is go in. We're gonna test this out. Fetch pricing data. Excited for this.\u003C\u002Fp>\u003Cp>Why didn't it show? It's probably fetching data, but it is not did I forget to save it? Requires selection. Oh, I guess I did forget to save it. Pricing URL.\u003C\u002Fp>\u003Cp>That happens. Alright. Sick. There we go. Got an input.\u003C\u002Fp>\u003Cp>Save. Okay. Now alright. This failed anyway. Right?\u003C\u002Fp>\u003Cp>Bad request. So I had to look at that. Let's see. But now if we go into ShopVox, here we go. It fetch pricing data.\u003C\u002Fp>\u003Cp>We've got the URL somewhere. Run flow. Cool. That triggered successfully. I can just go back to the flow.\u003C\u002Fp>\u003Cp>Normally, I would have these, like, set up differently. But alright. So we've got the body. No. It's not under payload.\u003C\u002Fp>\u003Cp>That's why it failed. Okay. So here's the pricing URL. It's triggered. So, basically, direct as flow's easy way to create these automations.\u003C\u002Fp>\u003Cp>Whenever you run a flow, each op each step or operations as we call them, they append their data, to an object that you could pull from. So in this case, the trigger has a special one where it has a dollar sign in front of it, and it's gonna be trigger.body or, well, it should be payload.body, I thought. Pricing URL. It should be that data. Here's what we passed in, but, yeah, we could see it's it's undefined right here.\u003C\u002Fp>\u003Cp>So that's probably where the issue lies. Trigger that body.\u003C\u002Fp>\u003Cp>Speaker 1: A column? An additional column at the end, maybe?\u003C\u002Fp>\u003Cp>Speaker 0: Trigger that body.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, no.\u003C\u002Fp>\u003Cp>Speaker 0: No. No. This should be trigger.payload. Here's the options passed. Trigger.payload.body.pricing URL.\u003C\u002Fp>\u003Cp>That should be it. Let's just add what we can do is add an intermediate step in here. I'm not sure why this is not populating correctly. Triggered. Let's try I just wanna see what payload comes back with.\u003C\u002Fp>\u003Cp>Do you have to pass HTTPS for your for the API, or will it\u003C\u002Fp>\u003Cp>Speaker 1: pick up without HTTPS? It it should be able to pick up without HTTPS.\u003C\u002Fp>\u003Cp>Speaker 0: Okay.\u003C\u002Fp>\u003Cp>Speaker 1: It should just add it there.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Let's see. Still showing undefined. Trigger. Why are we not getting this information that we need?\u003C\u002Fp>\u003Cp>Alright. Let's put in an intermediate step here. Just gonna basically run some JavaScript. We have a way to do that. We're just gonna use run script.\u003C\u002Fp>\u003Cp>I'm just gonna call this format. And here, what it does, it it receives all the data up to this operation that's already ran. And I just wanna return data dot trigger. And I'm I'm just gonna see I'm gonna wire this up, make sure we're getting that data properly. Flows.\u003C\u002Fp>\u003Cp>Dropbox. HTTPS. Run flow. Fetch our pricing data. Here we go.\u003C\u002Fp>\u003Cp>Unexpected token. What are we even doing here? The demos. The the demo gremlins. Unexpected dot.\u003C\u002Fp>\u003Cp>Why is it not liking that? Oh, yeah. Duh. Return data. Let's do it this way.\u003C\u002Fp>\u003Cp>Alright. Now open a new window. Get a little smarter here. Close. Fetch pricing data.\u003C\u002Fp>\u003Cp>URL. K. Refresh. What do we get? Okay.\u003C\u002Fp>\u003Cp>Cool. There we go. We can see the data. I don't know, again, why it wasn't liking that, but we'll just wire this up now. Let's see if this actually works.\u003C\u002Fp>\u003Cp>We're gonna call formats. And, basically, in this case, let's just return dot payload body. Alright. We can see we're getting oh, okay. I don't see a payload.\u003C\u002Fp>\u003Cp>That's probably where we were going wrong. Should just be body. That's my fault. Alright. So let's test one more time.\u003C\u002Fp>\u003Cp>Test. Here we go. Okay. There we go. Data.\u003C\u002Fp>\u003Cp>Alright.\u003C\u002Fp>\u003Cp>Speaker 1: There it is.\u003C\u002Fp>\u003Cp>Speaker 0: This is gonna this is gonna work this time. 100%. Here we go. Let's go. We are going to actually now we're gonna fetch that from format.payload.\u003C\u002Fp>\u003Cp>Actually, it's not, is it? Body.pricing URL. Nope. It's just nobody. There we go.\u003C\u002Fp>\u003Cp>Format .pricing URL should be good Or did I data dot pricing URL. Gotta help us. Format dot data. Alright. It is going to work this time.\u003C\u002Fp>\u003Cp>We are going to alright. So now we could see. Right? This is, we could we could actually set this up to be asynchronous as we wanted, but it is waiting for that to come back. And now bada bing bada boom.\u003C\u002Fp>\u003Cp>What do we have? This is the this is the data structure that we want. Right? Yeah. Exactly.\u003C\u002Fp>\u003Cp>Sick. Okay. Alright. So now we just need to basically wire that up to direct us. And in this case, it's going to be, let me make this full screen again.\u003C\u002Fp>\u003Cp>Speaker 1: And we'll Additionally, just to make this workflow, more reliable and just continue applying changes if something was actually changed from the pricing page, we can use the change tracking. So this would be just an additional format that that would instruct our workflow, yeah, something changed, right, instead of always fetching. Yeah. But that's Yeah. There's plenty more things that we we can we can add just to make sure everything's on point.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Okay. Well, it let me let's make sure we can update the software first, and then we will then we'll tackle that, man. Alright. So we've got software.\u003C\u002Fp>\u003Cp>That's our collection. We are gonna I'm just gonna give this full access for now. And what we need to do, this should work here. We're gonna do something like trigger dot keys dot zero. So it trigger dot keys.\u003C\u002Fp>\u003Cp>This is just an array of the the primary key for, whatever we're triggering on. I'll hit enter there. And then the payload that we're going to send I'm gonna save this real quick. We're gonna look at what we received back. So it's just Navigate the tree.\u003C\u002Fp>\u003Cp>Speaker 1: Dot JSON, basically.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So it should be payload\u003C\u002Fp>\u003Cp>Speaker 1: Actually, dot dot data dot data dot data. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Dot data dot\u003C\u002Fp>\u003Cp>Speaker 1: Are there the q dot data there? Oh, oh,\u003C\u002Fp>\u003Cp>Speaker 0: I see JSON. Yeah. The it's like it's it's nested because the operation itself returns it in a data, and this is the fetch call that gets returned. So it's so it is data dot data dot JSON. JSON.\u003C\u002Fp>\u003Cp>Is what we want. Yep. Alright. So we should be able to do this where we have we're just gonna check the key here. I call this call fire crawl.\u003C\u002Fp>\u003Cp>I can always adjust these keys and that's, again, that's what it's gonna pin to. So it should be able to do something simple like this where we say firecrawl.data.data.json. And this is the pricing that we're going to update. I think it's surprising. What did we we'll open this up.\u003C\u002Fp>\u003Cp>What did I what did I call it here? Pricing data. Let me look at the actual data model real quick. Pricing data. That's that is indeed what it needs to be.\u003C\u002Fp>\u003Cp>Pricing underscore data. Alright. Now go back to the side by side, get that lovely shot of fire crawl. And now this is actually gonna work. Right?\u003C\u002Fp>\u003Cp>Yep. If the demo gods are on our side.\u003C\u002Fp>\u003Cp>Speaker 1: Always in the demo.\u003C\u002Fp>\u003Cp>Speaker 0: Here we go. Let's save this just just to make sure. Alright. Run flow. Now we should see this pricing change if everything works okay.\u003C\u002Fp>\u003Cp>Updated two fields. Something I'm assuming something failed. So it wasn't fire crawl. It was just me. BSS software.\u003C\u002Fp>\u003Cp>Keys undefined. Alright. Body dot keys. Okay. That's what it is.\u003C\u002Fp>\u003Cp>Operator error again.\u003C\u002Fp>\u003Cp>Speaker 1: Body dot keys.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. One more time. Fetch pricing data. Oh, no. You're gonna use all my FireCrawl credits before we even use them, actually.\u003C\u002Fp>\u003Cp>Yeah. Sick. Okay. Now we could see it actually worked this time. So amazing.\u003C\u002Fp>\u003Cp>Right? Now we've got this wired up where it will update the pricing data for us on on this schedule, or well, actually, not schedule. It's totally manual. Right? That's still not what we want.\u003C\u002Fp>\u003Cp>Talk to me about the the change tracking bids.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Change tracking is it's a format that you'll specify just like we specify JSON as a format, we can add chain tracking. And inside of that chain tracking, like we fetched to JSON, there'll be the chain tracking object that will tell us, hey. Something changed in the website or, no. Nothing changed.\u003C\u002Fp>\u003Cp>So you don't really need to update anything. And that's just that's an add on. We don't we don't really need to implement this in here. But it it's nice that you mentioned about wasting your Firepower credits because the reason why we went for scraping, which, by the way, we can use batch scraping to do everything, all at once, do everything in parallel. Every scrape you do to to every single page wastes one single credit.\u003C\u002Fp>\u003Cp>Gotcha. And the more you abstract Yeah. Yeah. Yeah. And the the the more you abstract, the more, like, for example, if you use crawl, crawl is a combination of map with scrape, then, you'll like, the credits will, be higher for that just because you're fetching the different pages.\u003C\u002Fp>\u003Cp>If you then abstract away to using agents, since the agent executes everything for us, you don't really need to think about searching, scraping, mapping, or anything. It should, it will be a bit more expensive just because it's handling a bunch of things that you really don't need to decide. So Gotcha. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: So it's like a sliding scale of what your what your your tolerance is of, like, hey. I know the exact workflow that I need. Exactly. You could you could be very surgical or, like, the agent Yes. Would just be, like, here's the URL.\u003C\u002Fp>\u003Cp>Here's some guidance. Here's the data that I want. Yes. Just work until you find that. Yes.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And and we can get creative with that. So, while you were implementing, I thought we could use agents to fetch for the pricing page or for whatever pages from that specific software, and then just place that in over back in Directus, place it in the database, and then always have Fireflies scrape for that given URL. So we would just be using agent to populate the URLs that will be scraped, not needing to use agent every single time. So we can get really creative with this, to ensure that we're only scraping at the times that we really need to.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Okay. Sick. Let's let's see how we do that. Right?\u003C\u002Fp>\u003Cp>I'm in the the fire crawl dashboard here. Is this something that's better done here, or is it Yeah. Like, in\u003C\u002Fp>\u003Cp>Speaker 1: code or It's it's it's a nice place for us to test it.\u003C\u002Fp>\u003Cp>Speaker 0: Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Alright. In there, you can actually just type in it could be a very high level. So example, just get the name of the you don't even need to place the URL, to be honest. You can just place Okay.\u003C\u002Fp>\u003Cp>Place in the name of the software and say, fetch the pricing for this software.\u003C\u002Fp>\u003Cp>Speaker 0: For let's see what we got. Let's do a different one. Let's say, core bridge here. First, the pricing for CoreBridge software.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: We'll just say sign industry in case they've got there's multiple. Right? So what's the the difference between Spark Mini and Spark Pro?\u003C\u002Fp>\u003Cp>Speaker 1: Spark Mini is so it's lightweight. It it's like the the comparison between using a Haiku model and a Claude or an op opus model. So one will be faster and the other will be just more efficient.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Yeah. So here we are, Leo. We've seen, we've got this thing is it's fetching the pricing, and now it's asking for the details here. Is that, like, a failure for for me No.\u003C\u002Fp>\u003Cp>Speaker 1: It's actually instructing this? Yeah. It's a default just to fetch for more information. Usually, we don't give enough context to LLMs. So it's just asking for that context, but let's we can skip that.\u003C\u002Fp>\u003Cp>There's a button to just skip and immediately so down there Okay. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: I see it.\u003C\u002Fp>\u003Cp>Speaker 1: We could just skip through it. It'll generate a specific schema that it should use. So it's already defining that schema. We always always try to output structured data. Yeah.\u003C\u002Fp>\u003Cp>Sick. And then now we and then Yes. Exactly. And then you can just run the agent, and it should fetch everything that we need. Since it it it runs asynchronously Uh-huh.\u003C\u002Fp>\u003Cp>You can ask for for it to alert us back whenever it's done.\u003C\u002Fp>\u003Cp>Speaker 0: So let's see what it's come up with. Right? It's got core bridge starter, SMB, core bridge plus. It's it's came back with, like, eight plans.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, yeah. Let let's see if it's even, like, previous pricing for them.\u003C\u002Fp>\u003Cp>Speaker 0: No. It looks like there are that many plans. Annual. Okay. So it's divided into monthly and annual.\u003C\u002Fp>\u003Cp>Okay. Six. So, you know, I have $1.99, $3.99, or $1.29. I'm sorry. 549 and 899.\u003C\u002Fp>\u003Cp>Yeah. This is so the data checks out, man. I think, you know, that's a a concern with everybody these days, especially on, like, the the agents. Do you what's the the secret sauce? Is there yeah.\u003C\u002Fp>\u003Cp>Do you care to share? Like, how do you guys get accurate results on this stuff?\u003C\u002Fp>\u003Cp>Speaker 1: Oh, it's it's a mixture of system prompting using the best LLMs, understanding the flow of scraping. So Mhmm. Just having a fallback to when you need to interact with data, understanding, yeah, caching is is a part of it as well. So, yeah, it it's a bunch of I I I I feel like it's a bunch of little things that when summed up gets a pretty nice result like this.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Nice, man. Nice. Well, I know we're we're probably coming up on time. So, like, I won't go through the the trouble of integrating, like, the agent, but, you know, the fact that it would be, like, super simple here\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Again, it's just a v two slash agent, and then we'll It would be\u003C\u002Fp>\u003Cp>Speaker 1: the same process as before.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Actually, it may not be that bad. Alright. So we go back to our flow.\u003C\u002Fp>\u003Cp>You know, I would probably change this as well, right, where instead of a manual trigger, we could go into, like, a cron, and I could trigger this, like, once a week or once a month or something like that. We'll we'll just keep it manual for now. But let's let's try this out and see. So we're gonna change the\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Change it over to agent.\u003C\u002Fp>\u003Cp>Speaker 0: And then it's just gonna\u003C\u002Fp>\u003Cp>Speaker 1: be need to add a prompt and a a schema?\u003C\u002Fp>\u003Cp>Speaker 0: Do we have the we got the prompt already? Right? And then the rest of this, we just\u003C\u002Fp>\u003Cp>Speaker 1: remove? Nope. Yeah. I I think\u003C\u002Fp>\u003Cp>Speaker 0: she can actually\u003C\u002Fp>\u003Cp>Speaker 1: remove everything since the prompt was specific to the URL it was given.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. We just need the the schema. Right? Alright. So we have prompts.\u003C\u002Fp>\u003Cp>Yep. Fetch the pricing for company name. Let's do the oh, I don't know how this is gonna work out. We'll see.\u003C\u002Fp>\u003Cp>Speaker 1: And we can actually explore even more. I I mean, we don't have enough enough time to do so, but every single schema from your website, not only pricing, but could be handled from the agent. So we could have many different arrays and and objects to explore all the sections of that.\u003C\u002Fp>\u003Cp>Speaker 0: Got you. Yeah. So it doesn't have to be, like, pricing. We could go in and basically, we're saying we could do the entirety of this and have Yes. Like, agent just continue.\u003C\u002Fp>\u003Cp>Yeah. Like like, attributes or any of that. Everything. Yep. Oh, man.\u003C\u002Fp>\u003Cp>That would be amazing. Exactly. Yeah. Sick. Okay.\u003C\u002Fp>\u003Cp>Well, hey. This is probably a good stopping point then. You know, on on fire crawl, man, like, you know, this is just as obviously, like, one simple workflow that we've touched on. Yeah. What's what's next for you guys?\u003C\u002Fp>\u003Cp>Or, like, what, is there anything you wanted to highlight out of the out of this?\u003C\u002Fp>\u003Cp>Speaker 1: Let's see. I believe I believe FireCrawl. I think from this simple demo, it shows implementing something as simple as this. You get you get a a lot. Right?\u003C\u002Fp>\u003Cp>So from just one endpoint or one tool from Firegral, you can explore, the entire web. And there is so many things that we can build with this. Because before before AI, before all this wave of AI, scraping was something really difficult. Scraping was something that, you'd have to handle everything. You'd have to try to treat and filter out specific words, specific sessions.\u003C\u002Fp>\u003Cp>And now with FireCrawl, you have an LLM handling everything for you. It just allows us to build a lot more. And not only to build with FireCrawl because we we showed an example where we're using the FireCrawl API inside of our app. Right? Yeah.\u003C\u002Fp>\u003Cp>But even coding, and I I'm seeing directors implementing more and more AI tools. Eventually We are then. Yeah. Eventually, you could integrate Firewall into those agents as well and have Firewall have information, have much more context because whoever uses AI, enough understands that context is king. Right?\u003C\u002Fp>\u003Cp>And if you parrot it with the best web scraper out there, then you'll you'll have a lot of, pretty much best results.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah, man.\u003C\u002Fp>\u003Cp>Speaker 1: But yeah. I mean Can we file or you guys\u003C\u002Fp>\u003Cp>Speaker 0: have got the CLI now as well. I see. Right? Yes. That could be the next step for me is, like, oh, let's mess with this.\u003C\u002Fp>\u003Cp>Right? Because they like, my day to day right now is a a lot of quad code certainly.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And if the agent uses a CLI, it it can do a lot more. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Sick. Okay. Yeah.\u003C\u002Fp>\u003Cp>So yeah. Another follow-up session then then for sure where we're gonna take the direct us MCP, and then we will add the fire crawl CLI, and then we will just turn cloud code loose. Because everything that we did here today, like, you could do that via, the direct SMTP. So I could have Claude build a flow that just calls fire crawl agent and, you know, we could simplify all this work. But Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: It would be a matter of five minutes for it to build the entire page.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah, man. Sick. Sick. Cool. Well, Leo, man.\u003C\u002Fp>\u003Cp>And thanks for the the session. I I really appreciate you guys joining.\u003C\u002Fp>\u003Cp>Speaker 1: Thanks. Thanks for for having me. Just one more just an additional tip. Follow our YouTube channel.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Our\u003C\u002Fp>\u003Cp>Speaker 1: YouTube channel, they're we're we're posting everything there. We launch a lot. So to keep up with the scraping industry, feel free to subscribe there.\u003C\u002Fp>\u003Cp>Speaker 0: Fire crawl YouTube. At fire crawl underscore dev. There you go. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Sick. I see your face. There you are, man. Awesome, dude. Well, again, thanks for joining.\u003C\u002Fp>\u003Cp>This has been a fun session, and we could definitely follow-up again with a a run this back, man.\u003C\u002Fp>\u003Cp>Speaker 1: Absolutely. Alright. Excited to do it again.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. See you. Thanks, everybody.\u003C\u002Fp>","Alright, folks. Welcome to another session here at Directus Leap Week. I am joined by Leo Gregorio from Fire Crawl. We've got some exciting stuff for you today. We're gonna be taming unstructured data. Right? Turning the wild web into ready APIs using FireCrawl and Directus, to to serve those APIs. I am Brian Gillespie, a staff product engineer here at Directus. And, again, joined by special guest, Leo Gregorio from FireCrawl. Leo, maybe maybe I'll kick it over to you for a quick intro for everyone. It's a pleasure to be here, Brian. I've used Directus for quite a long time, and I'm stoked to be here. Yeah. I'm super excited to have you, man. Yeah. I've messed around with FireCrawl a little bit before this. I agree. I'm I'm no way an expert. I'm glad to hear that you've messed with Directus before. But for the folks who are new to FireCrawl, you know, can you take a few moments just to explain what it what the tool is? Like, what do you guys do? What do you enable for developers? Sure. Over at FireCrawl, we specialize at providing web data to agents. Not only agents, we can also have endpoints where you can build your data, data web fetcher upon those APIs. But we mainly focus on providing data over to agents in a way that, it's it's basically giving years' eyes over to your cloud code instance to wherever you're really building, to fetch for data. You should be thinking of FireCrawl to do that. Sick. What are, like, some of the, like, specifics within that? Like, what what capabilities does FireCrawl have? Yeah. To fetch for data, I like to compare with our manual way of fetching data. So if you want to fetch a specific website, what you would do is search for something. So we have that specific endpoint on searching. You can then use scrape to scrape that specific URL. And from the scrape endpoint, we have a bunch of different formats that we will likely be exploring through this presentation, which are screenshot. You can actually screenshot the entire page. There's a bunch of different ways to take that screenshot. It can be exactly what we're seeing right now. It can be of the entire page, mobile. So that's just an example of one of our formats. We can fetch for summary. We allow the agents to execute and interact with the page, and that helps a lot with context, for example. So you're not only fetching pure HTML. You can fetch clean markdown. You can fetch exactly what you need from that given page, and that's that's the beauty of it. That's what we try to optimize to. And, yeah, we we've been been pretty successful, while doing so. Yeah, man. I I've watched the growth of the last couple years, so kudos on that. It's it's a really interesting tool. And as I understand it, like, you guys have an agent thing on your endpoint as well. So you could you could basically call an agent your agent from a different agent. Yes. And and get really Yes. Metal with it, which is nice. I I love the ability to, again, just throw in a URL in here and get structured data out of the other side because it Oh, yeah. Complements perfectly with Directus. And what we do is, like, if if you give us structured data, we'll give you ready to go APIs and permissions and and all of that. So, super excited for this. Alright. Let's get down to brass tacks. Right? What are we gonna build today? So the scenario that I've got, basically, I've got a directory of different software. This is on my own site. So this is a a Better Sign Shop. I love the print and sign industry. I can't get out of it even though I've tried. Software comes up a lot. You know, I I guess it does in any industry these days. But, for sign and print, you know, how do they manage their business? There's a bunch of different tools for that. And we've got a directory here that, is sorely unmaintained. Right? All this data was collected manually some years ago, and we've got things like features of a particular software. On on some pages, we have pricing information. But, you know, it's a lot to manage all this personally. It it would be a lot to put on the vendor of the software to maintain. It'd be a lot for anybody on my side to say, okay. Keep all this up to date. Does this seem like a good use case for Firegirl? It's a perfect use case. Since we provide structured data, you can fetch for that. There's plenty of ways we can fetch for that. So if you already have the specific URL, you can use scrape or you can, like, use the agent that you mentioned. And by the way, the design of that website is pretty neat. Okay. Thank you, man. Yeah. If my, pretty neat. Okay. Thank you, man. Yeah. My, I I've got three daughters. They love pink and purple. So, hey, like, anything that I do personally, like, it has to have pink in it. Right? Alright. So let's let's talk through maybe how we're gonna build this real quick, and then we will dive into actually, like, going through it. Right? So we've got Okay. We've got a software collection. You know, mostly, I wanna have, like, pricing data. We've got, like, a name. Let's just call it, like, a summary. We're providing a URL. What else do we have? We got pricing data has to have a specific format, which should be good. I'm yeah. Maybe pricing is the the best place to start. So a like, that is going to be I don't know the pricing URLs for this, but I do have the domain. So, like, walk me through what what this is gonna look like on the FireCrawl side. So from your website, it seems like you already have specific URLs. It doesn't seem like the idea is having a bunch of different URLs. It seems like it's focused on quality. Mhmm. Therefore, if we were to have the specific pricing page, it would be better just because we can send off just one endpoint. It would be just scrape. So if we have that in the database, we can just search for that specific URL and get everything, all the structure, the price, we can check track if the the price changed, everything. If we don't, if the idea is scaling this, so for example, if your website were to have anyone add, any, type of software there Mhmm. This would scale up. And we wouldn't have control over what is the URL paid, the the pricing page, the URL for everything. Right. And then we'd have to create some kind of system where we search and then actually find that that the the pricing page. We can either do that with crawl, because inside of crawl, we have an LLM, or we can use agents to grab everything. I I got you. What do you recommend in this situation? I'll just go back to FireCrawl here and go to my dashboard. Because we I'd recommend going for for the pricing page, and we can test that inside of the playground for fire crawl. Let me send the link over. I will look here in the chat. Here it is. Alright. So grab any pricing page that we already have for one of those software queries. Aware of. Okay. I think I think this is the right one. Let me make sure. Pricing. Okay. Yep. There we go. Nice. So we're just gonna throw this in. Perfect. As for the format, you can select, let's see. Jason. Yeah. Let me zoom back out just a little bit. Alright. So we're gonna hit edit options. Perfect. And that's basically an LLM that understands what it's seeing inside of the page, and you can prompt it to fetch only the pricing data from that specific page. Gotcha. Okay. So let me pull this up over here. Oh, format JSON. Alright. So we're gonna edit our options. Right? And here is just basically going to be what I want from the pricing data. Right? So we are going to find this particular one in here. We're gonna look at this and alright. So Perfect. You already have the schema. Like a Like a can I just paste JSON here, and it'll pick this up Yep? Or no? I don't think you can, that would be all It's gotta be, it's gotta be JSON schema. Okay. But that's a nice idea. We should add a way to get grab an example and produce the schema. That's that's something. Starting price, that's gonna be a number. Actually, it's a string. I I think the first one should be plans, and and it should be an array. Okay. It should be right. Nested dot. Yeah. Got you. And then it's an array of objects. Objects. Perfect. Starting price. And we want pricing period. Oh, no. For each Actually, inside of the the array of objects, it would be, like, the the plants. Oh, yeah. I think you I think you were go going right, and I yeah. We reversed it. I was just, like, the array. Yeah. Sorry. No. All good. I I started typing it, and I realized it as well, man. No worries. It's usually me, like, fat fingering typos on these things instead of, like, actually typing something completely wrong. But, billing cycle. Cool. Billing period. And then we have features, which is gonna be an array itself, array of strings. I got highlight, but I don't think we really need highlight in this case. Okay. Hit save options. Okay. Yeah. Cool. So anything else that I need to That's a checkbox. It. We can hit start scraping, and let's see what we get. Depending on what we get, maybe the website has a heavy JavaScript, and that might require us to wait a couple seconds before applying the scrape. So that's where the other options comes in. Sick. Alright. There it is. Price. Sick. Okay. Shopbox Express. Features. Yeah. So we could just it's probably wise to pull this up side by side just to verify what's what's coming in. One zero nine plus 29 user a month. The billing cycle is monthly. We have features, pricing tools. Yeah. This is this is pretty much one to one. I like that the thing truncated, like, some of this data because it's not really necessary. The show can specify a bit more because the pricing, depending on the the the the column, maybe we don't want exactly the same way the the pricing is there. But Yeah. And is that done just via the prompt or, like, in the actual JSON structure? Or Yeah. We we can decide either if, because we can have, like, a strategy where the price could be a number. Right? And then it would be forced to place only a single price and then have, like, optional prices, right Yeah. I get it. Yeah. So it's really it's really up to whatever we're really building with that. Yeah. So it looks like the, like, the the way I've got the front end for this particular thing structured. Right? We're relying on let's take a look at what the pricing plans look like. Right? So we've we've got this really specific, like, format here of, like, we need a number there that's large. Then we've got, like, some additional stuff, and then we have, like, the month. So let let's have a price, which is a number, a float. Right? And then, additional pricing information. How about that? Yeah. So we got price. That's a number. Alright. Let's just go back to direct us. Cool. Price. Alright. So would we just specify this, like, up in the prompt of, like, billing cycle should be something like slash month or slash is this the best way to inform it? Yeah. That that's that's one way that should work pretty fine. Okay. In this case, we'll say billing period just captures additional per user or extra cost. Alright. Let's see what that does. Let's see. That gives us what we're looking for. And if not, we'll just work on actually integrating this into Directus instead of actually pulling. K. 29. Okay. Price slash per month. 29 per user per month. Yeah. So this is already looking much better. Sick. Yeah. Okay. I think that's that's better for the front end. Alright. So if I hit go get code here, this is gonna give me what I need to integrate this inside Directus. Exactly. You can get a JavaScript code on on on on the right. Yeah. Okay. Yeah. And I could also, like, get curls. There's a couple different ways to do this. Like, we could easily, like, write an extension inside Directus that could could do this for us. Since this seems pretty lightweight, we could just use direct as flows for this as well. And I I think we've even got, like, a fire crawl extension in the marketplace for this, but let's just let's just figure out how to do this, through flows. That might be the the easiest, quickest way here so we don't have to worry about distributing an extension anywhere. Alright. So Let's go. We will set up a new flow. I think we probably just, like, manually trigger this, and then we could set it up on a later. Alright. So we're gonna say fetch pricing data. Sick. Alright. We're gonna manually trigger this as a hook. We are looking for this is always whenever you do a demo, you always find flaws in your own software. We'd need a we'd need a search for this for sure. Alright. So we're gonna manually trigger this on the software collection. Next, we would just do what we call a HTTP webhook requesting URL. That's the operation. We'll say call fire crawl. And I'm gonna definitely have to roll the token after this. But Yeah. I was just about to mention. Luckily, this is also a local host as well. Alright. So we're just gonna add a header, authorization. Bear no. I could also, like, throw this in the EMV, but we're not gonna bother with that for now. Authorization. There's those typos that I spoke about. What else we need? Content type. Application. JSON. Sick. Okay. And then here's the data. So that's what we're sending in the body, except the only thing that we're gonna have to change here, right, is the URL that we're we're triggering this from. So Exactly. In this case, Directus has this functionality where we could do something like this, where we add, like, a a mustache syntax, and we can say trigger dot body dot, I don't know what we're gonna call this pricing URL. I I think it may be payload. I don't know. It will we'll test this in a moment. So, basically, what this will do is populate that from what we call the the trigger. And I'm just gonna go in and because we're not tracking the URL, maybe we just we do that pricing URL right here. Pricing page URL stream. Cool. Hit save. Alright. And before we add anything else, alright, all I'm gonna do is go in. We're gonna test this out. Fetch pricing data. Excited for this. Why didn't it show? It's probably fetching data, but it is not did I forget to save it? Requires selection. Oh, I guess I did forget to save it. Pricing URL. That happens. Alright. Sick. There we go. Got an input. Save. Okay. Now alright. This failed anyway. Right? Bad request. So I had to look at that. Let's see. But now if we go into ShopVox, here we go. It fetch pricing data. We've got the URL somewhere. Run flow. Cool. That triggered successfully. I can just go back to the flow. Normally, I would have these, like, set up differently. But alright. So we've got the body. No. It's not under payload. That's why it failed. Okay. So here's the pricing URL. It's triggered. So, basically, direct as flow's easy way to create these automations. Whenever you run a flow, each op each step or operations as we call them, they append their data, to an object that you could pull from. So in this case, the trigger has a special one where it has a dollar sign in front of it, and it's gonna be trigger.body or, well, it should be payload.body, I thought. Pricing URL. It should be that data. Here's what we passed in, but, yeah, we could see it's it's undefined right here. So that's probably where the issue lies. Trigger that body. A column? An additional column at the end, maybe? Trigger that body. Oh, no. No. No. This should be trigger.payload. Here's the options passed. Trigger.payload.body.pricing URL. That should be it. Let's just add what we can do is add an intermediate step in here. I'm not sure why this is not populating correctly. Triggered. Let's try I just wanna see what payload comes back with. Do you have to pass HTTPS for your for the API, or will it pick up without HTTPS? It it should be able to pick up without HTTPS. Okay. It should just add it there. Alright. Let's see. Still showing undefined. Trigger. Why are we not getting this information that we need? Alright. Let's put in an intermediate step here. Just gonna basically run some JavaScript. We have a way to do that. We're just gonna use run script. I'm just gonna call this format. And here, what it does, it it receives all the data up to this operation that's already ran. And I just wanna return data dot trigger. And I'm I'm just gonna see I'm gonna wire this up, make sure we're getting that data properly. Flows. Dropbox. HTTPS. Run flow. Fetch our pricing data. Here we go. Unexpected token. What are we even doing here? The demos. The the demo gremlins. Unexpected dot. Why is it not liking that? Oh, yeah. Duh. Return data. Let's do it this way. Alright. Now open a new window. Get a little smarter here. Close. Fetch pricing data. URL. K. Refresh. What do we get? Okay. Cool. There we go. We can see the data. I don't know, again, why it wasn't liking that, but we'll just wire this up now. Let's see if this actually works. We're gonna call formats. And, basically, in this case, let's just return dot payload body. Alright. We can see we're getting oh, okay. I don't see a payload. That's probably where we were going wrong. Should just be body. That's my fault. Alright. So let's test one more time. Test. Here we go. Okay. There we go. Data. Alright. There it is. This is gonna this is gonna work this time. 100%. Here we go. Let's go. We are going to actually now we're gonna fetch that from format.payload. Actually, it's not, is it? Body.pricing URL. Nope. It's just nobody. There we go. Format .pricing URL should be good Or did I data dot pricing URL. Gotta help us. Format dot data. Alright. It is going to work this time. We are going to alright. So now we could see. Right? This is, we could we could actually set this up to be asynchronous as we wanted, but it is waiting for that to come back. And now bada bing bada boom. What do we have? This is the this is the data structure that we want. Right? Yeah. Exactly. Sick. Okay. Alright. So now we just need to basically wire that up to direct us. And in this case, it's going to be, let me make this full screen again. And we'll Additionally, just to make this workflow, more reliable and just continue applying changes if something was actually changed from the pricing page, we can use the change tracking. So this would be just an additional format that that would instruct our workflow, yeah, something changed, right, instead of always fetching. Yeah. But that's Yeah. There's plenty more things that we we can we can add just to make sure everything's on point. Yeah. Okay. Well, it let me let's make sure we can update the software first, and then we will then we'll tackle that, man. Alright. So we've got software. That's our collection. We are gonna I'm just gonna give this full access for now. And what we need to do, this should work here. We're gonna do something like trigger dot keys dot zero. So it trigger dot keys. This is just an array of the the primary key for, whatever we're triggering on. I'll hit enter there. And then the payload that we're going to send I'm gonna save this real quick. We're gonna look at what we received back. So it's just Navigate the tree. Dot JSON, basically. Yeah. So it should be payload Actually, dot dot data dot data dot data. Yeah. Dot data dot Are there the q dot data there? Oh, oh, I see JSON. Yeah. The it's like it's it's nested because the operation itself returns it in a data, and this is the fetch call that gets returned. So it's so it is data dot data dot JSON. JSON. Is what we want. Yep. Alright. So we should be able to do this where we have we're just gonna check the key here. I call this call fire crawl. I can always adjust these keys and that's, again, that's what it's gonna pin to. So it should be able to do something simple like this where we say firecrawl.data.data.json. And this is the pricing that we're going to update. I think it's surprising. What did we we'll open this up. What did I what did I call it here? Pricing data. Let me look at the actual data model real quick. Pricing data. That's that is indeed what it needs to be. Pricing underscore data. Alright. Now go back to the side by side, get that lovely shot of fire crawl. And now this is actually gonna work. Right? Yep. If the demo gods are on our side. Always in the demo. Here we go. Let's save this just just to make sure. Alright. Run flow. Now we should see this pricing change if everything works okay. Updated two fields. Something I'm assuming something failed. So it wasn't fire crawl. It was just me. BSS software. Keys undefined. Alright. Body dot keys. Okay. That's what it is. Operator error again. Body dot keys. Alright. One more time. Fetch pricing data. Oh, no. You're gonna use all my FireCrawl credits before we even use them, actually. Yeah. Sick. Okay. Now we could see it actually worked this time. So amazing. Right? Now we've got this wired up where it will update the pricing data for us on on this schedule, or well, actually, not schedule. It's totally manual. Right? That's still not what we want. Talk to me about the the change tracking bids. Yeah. Change tracking is it's a format that you'll specify just like we specify JSON as a format, we can add chain tracking. And inside of that chain tracking, like we fetched to JSON, there'll be the chain tracking object that will tell us, hey. Something changed in the website or, no. Nothing changed. So you don't really need to update anything. And that's just that's an add on. We don't we don't really need to implement this in here. But it it's nice that you mentioned about wasting your Firepower credits because the reason why we went for scraping, which, by the way, we can use batch scraping to do everything, all at once, do everything in parallel. Every scrape you do to to every single page wastes one single credit. Gotcha. And the more you abstract Yeah. Yeah. Yeah. And the the the more you abstract, the more, like, for example, if you use crawl, crawl is a combination of map with scrape, then, you'll like, the credits will, be higher for that just because you're fetching the different pages. If you then abstract away to using agents, since the agent executes everything for us, you don't really need to think about searching, scraping, mapping, or anything. It should, it will be a bit more expensive just because it's handling a bunch of things that you really don't need to decide. So Gotcha. Yeah. So it's like a sliding scale of what your what your your tolerance is of, like, hey. I know the exact workflow that I need. Exactly. You could you could be very surgical or, like, the agent Yes. Would just be, like, here's the URL. Here's some guidance. Here's the data that I want. Yes. Just work until you find that. Yes. Yeah. And and we can get creative with that. So, while you were implementing, I thought we could use agents to fetch for the pricing page or for whatever pages from that specific software, and then just place that in over back in Directus, place it in the database, and then always have Fireflies scrape for that given URL. So we would just be using agent to populate the URLs that will be scraped, not needing to use agent every single time. So we can get really creative with this, to ensure that we're only scraping at the times that we really need to. Yeah. Okay. Sick. Let's let's see how we do that. Right? I'm in the the fire crawl dashboard here. Is this something that's better done here, or is it Yeah. Like, in code or It's it's it's a nice place for us to test it. Okay. Yeah. Alright. In there, you can actually just type in it could be a very high level. So example, just get the name of the you don't even need to place the URL, to be honest. You can just place Okay. Place in the name of the software and say, fetch the pricing for this software. For let's see what we got. Let's do a different one. Let's say, core bridge here. First, the pricing for CoreBridge software. Yeah. We'll just say sign industry in case they've got there's multiple. Right? So what's the the difference between Spark Mini and Spark Pro? Spark Mini is so it's lightweight. It it's like the the comparison between using a Haiku model and a Claude or an op opus model. So one will be faster and the other will be just more efficient. Alright. Yeah. So here we are, Leo. We've seen, we've got this thing is it's fetching the pricing, and now it's asking for the details here. Is that, like, a failure for for me No. It's actually instructing this? Yeah. It's a default just to fetch for more information. Usually, we don't give enough context to LLMs. So it's just asking for that context, but let's we can skip that. There's a button to just skip and immediately so down there Okay. Yeah. I see it. We could just skip through it. It'll generate a specific schema that it should use. So it's already defining that schema. We always always try to output structured data. Yeah. Sick. And then now we and then Yes. Exactly. And then you can just run the agent, and it should fetch everything that we need. Since it it it runs asynchronously Uh-huh. You can ask for for it to alert us back whenever it's done. So let's see what it's come up with. Right? It's got core bridge starter, SMB, core bridge plus. It's it's came back with, like, eight plans. Oh, yeah. Let let's see if it's even, like, previous pricing for them. No. It looks like there are that many plans. Annual. Okay. So it's divided into monthly and annual. Okay. Six. So, you know, I have $1.99, $3.99, or $1.29. I'm sorry. 549 and 899. Yeah. This is so the data checks out, man. I think, you know, that's a a concern with everybody these days, especially on, like, the the agents. Do you what's the the secret sauce? Is there yeah. Do you care to share? Like, how do you guys get accurate results on this stuff? Oh, it's it's a mixture of system prompting using the best LLMs, understanding the flow of scraping. So Mhmm. Just having a fallback to when you need to interact with data, understanding, yeah, caching is is a part of it as well. So, yeah, it it's a bunch of I I I I feel like it's a bunch of little things that when summed up gets a pretty nice result like this. Yeah. Nice, man. Nice. Well, I know we're we're probably coming up on time. So, like, I won't go through the the trouble of integrating, like, the agent, but, you know, the fact that it would be, like, super simple here Yeah. Again, it's just a v two slash agent, and then we'll It would be the same process as before. Yeah. Yeah. Actually, it may not be that bad. Alright. So we go back to our flow. You know, I would probably change this as well, right, where instead of a manual trigger, we could go into, like, a cron, and I could trigger this, like, once a week or once a month or something like that. We'll we'll just keep it manual for now. But let's let's try this out and see. So we're gonna change the Yeah. Change it over to agent. And then it's just gonna be need to add a prompt and a a schema? Do we have the we got the prompt already? Right? And then the rest of this, we just remove? Nope. Yeah. I I think she can actually remove everything since the prompt was specific to the URL it was given. Yeah. We just need the the schema. Right? Alright. So we have prompts. Yep. Fetch the pricing for company name. Let's do the oh, I don't know how this is gonna work out. We'll see. And we can actually explore even more. I I mean, we don't have enough enough time to do so, but every single schema from your website, not only pricing, but could be handled from the agent. So we could have many different arrays and and objects to explore all the sections of that. Got you. Yeah. So it doesn't have to be, like, pricing. We could go in and basically, we're saying we could do the entirety of this and have Yes. Like, agent just continue. Yeah. Like like, attributes or any of that. Everything. Yep. Oh, man. That would be amazing. Exactly. Yeah. Sick. Okay. Well, hey. This is probably a good stopping point then. You know, on on fire crawl, man, like, you know, this is just as obviously, like, one simple workflow that we've touched on. Yeah. What's what's next for you guys? Or, like, what, is there anything you wanted to highlight out of the out of this? Let's see. I believe I believe FireCrawl. I think from this simple demo, it shows implementing something as simple as this. You get you get a a lot. Right? So from just one endpoint or one tool from Firegral, you can explore, the entire web. And there is so many things that we can build with this. Because before before AI, before all this wave of AI, scraping was something really difficult. Scraping was something that, you'd have to handle everything. You'd have to try to treat and filter out specific words, specific sessions. And now with FireCrawl, you have an LLM handling everything for you. It just allows us to build a lot more. And not only to build with FireCrawl because we we showed an example where we're using the FireCrawl API inside of our app. Right? Yeah. But even coding, and I I'm seeing directors implementing more and more AI tools. Eventually We are then. Yeah. Eventually, you could integrate Firewall into those agents as well and have Firewall have information, have much more context because whoever uses AI, enough understands that context is king. Right? And if you parrot it with the best web scraper out there, then you'll you'll have a lot of, pretty much best results. Yeah, man. But yeah. I mean Can we file or you guys have got the CLI now as well. I see. Right? Yes. That could be the next step for me is, like, oh, let's mess with this. Right? Because they like, my day to day right now is a a lot of quad code certainly. Yeah. And if the agent uses a CLI, it it can do a lot more. Right? Yeah. Yeah. Sick. Okay. Yeah. So yeah. Another follow-up session then then for sure where we're gonna take the direct us MCP, and then we will add the fire crawl CLI, and then we will just turn cloud code loose. Because everything that we did here today, like, you could do that via, the direct SMTP. So I could have Claude build a flow that just calls fire crawl agent and, you know, we could simplify all this work. But Yeah. It would be a matter of five minutes for it to build the entire page. Yeah, man. Sick. Sick. Cool. Well, Leo, man. And thanks for the the session. I I really appreciate you guys joining. Thanks. Thanks for for having me. Just one more just an additional tip. Follow our YouTube channel. Yeah. Our YouTube channel, they're we're we're posting everything there. We launch a lot. So to keep up with the scraping industry, feel free to subscribe there. Fire crawl YouTube. At fire crawl underscore dev. There you go. Yeah. Yeah. Sick. I see your face. There you are, man. Awesome, dude. Well, again, thanks for joining. This has been a fun session, and we could definitely follow-up again with a a run this back, man. Absolutely. Alright. Excited to do it again. Alright. See you. Thanks, everybody.","b984e4a4-f9d7-4e77-9200-906ac7f71e2d",[219,220],"739d3849-5a61-4b70-9022-97abe785813f","73deee8d-d24b-4cf9-a11f-a2adac9d0202",[],{"year":139,"number":140,"id":141,"episodes":223,"show":224},[143,144,145,146,147,148,123,149],{"title":151,"slug":152},{"id":149,"slug":226,"vimeo_id":227,"description":228,"tile":229,"length":230,"resources":12,"people":12,"episode_number":11,"published":130,"title":231,"video_transcript_html":232,"video_transcript_text":233,"content":12,"seo":234,"status":19,"episode_people":235,"recommendations":238,"season":239},"what-next-for-directus","1176305680","A deep-dive look at what's shipping, what's coming, and where the product is heading.\n\n","5795d393-226c-4831-8d38-f47e25419dd5",32,"What's Next for Directus","\u003Cp>Speaker 0: Alright. And with that, we've made it to the last session of the Leap Week. I really hope you enjoyed all the great announcements so far. But before we wrap up today, we wanted to take a beat and talk a little bit about what we've shipped recently and what is next for the Directus platform as a whole. I'm Greg Consonzo, the CTO and cofounder here at Directus, and with me is James.\u003C\u002Fp>\u003Cp>Speaker 1: Thank you very much. Yeah. I'm James. Working at Directus as a staff product manager. Thank you for the intro, Rick.\u003C\u002Fp>\u003Cp>Speaker 0: So first and foremost, some of you might have seen that on GitHub discussions, we put a big banner, and we we posted a couple of comments about our new road map. So we moved platforms over to roadmap.direct.io. You might have seen it. Why why did we make this change, James?\u003C\u002Fp>\u003Cp>Speaker 1: If we look at the old mechanism for feature requests, we used to manage this through GitHub discussions. Honestly, it became a a place for things to kind of go away and die, unfortunately, through no, you know, no means on our part or no deliberate means, but, it wasn't so integrated with, you know, linear and the development workflows of the teams. So it's kinda had this, you know, disjointed journey between the feature requests and then how they get actioned and then making that visible to the community as well. So we've started to use a new a new product. And like Wrike said, you'll have seen on some of the discussion threads, that, you know, we've moved over now.\u003C\u002Fp>\u003Cp>So some of the threads have been locked. Some of them we've kept open because we wanna keep the discussion going. Right? And we don't want that to stop on some of that particularly some of the ones that have been upvoted heavily. But what you will find is that, you know, these have been ported over to our our new road map, which you can find at roadmap.directus.io\u002Froadmap.\u003C\u002Fp>\u003Cp>You can go in here, search for existing discussions, and we've maintained that link back to the GitHub thread as well, because there's lots of useful information there. So, yeah, that's that's kinda how things look. I don't know if you've got anything to add, Ryke.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I'll say it's it's one of those interesting points where it feels very obvious to keep it all centralized on the repo. I mean, GitHub already has discussions. They already have upvotes. They already have, like, a, submitted by status.\u003C\u002Fp>\u003Cp>They already have the whole it's it's, approved or denied or completed and all that kind of stuff. So it it might be a little weird to just see that move over. But at the end of the day, we kinda just outgrew the tooling that was available. Right? So even right now, when we noticed, oh, we wanna move over, there's no way for us to to prevent new ones from being open on GitHub without shutting down all of the old ones, which is very unfortunate because then people can't find them.\u003C\u002Fp>\u003Cp>So at the end of the day, you know, this is a great, great upgrade, to at least be able to manage these feature requests and and also help our team ship, the things that are truly important here to the community. With that as well, any request that you put into this product line right now goes straight to our product team. So you're also straight talking to the source and and the folks in charge of that roadmap. You're truly in James in this case, which is a lot nicer line of communication in that regard. So just as before, you know, please do take a look at the road map.\u003C\u002Fp>\u003Cp>If you see something that you agree with or you're aligned with, please hit that upvote button. If you have a different request that nobody else has requested yet on this particular list, hit the new request button, and it will fly straight to us. And we'll we'll reach out, for more information.\u003C\u002Fp>\u003Cp>Speaker 1: That's right.\u003C\u002Fp>\u003Cp>Speaker 0: Give it to me. Plus one. Any updates?\u003C\u002Fp>\u003Cp>Speaker 1: Direct line of communication, like you said. And Exactly. Feel free to add an extra layer of detail. If you see something that, you know, you see yourself in in the request, but you wanna add, you know, additional information because that comes directly to me and Mike.\u003C\u002Fp>\u003Cp>Speaker 0: Exactly. And with that being said, I think let's take a look at this road map sort of going in in reverse chronological order maybe for this one. Let's take a look at some of the stuff that we've recently completed. So we'll we'll group this roughly by release, instead of chronological order. Well, I guess it's kinda the same if you think about it.\u003C\u002Fp>\u003Cp>But, you know, we we plan these things out on the road map over time. But starting with with 11 dot 16 release, we have the global draft versions, which is a a part of a bigger new project. James, what what is that project all about?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So I think there's been a a perception that, the content versioning features inside Directus have been kind of a advanced feature. They're a little bit tucked away. They work a little bit like GitHub branches, funny enough today. But for, you know, content editors, that need to version content, that workflow for how you work with the draft, how that gets promoted or published into the, you know, the the the live item has been not super intuitive.\u003C\u002Fp>\u003Cp>So we started on this track to make that a lot more native. And the latest update brings, you know, a global draft version to any item that has content versioning enabled. So the moment you switch that on, you're gonna have a draft version inside that that item. That was something you'd have to create yourself before. And then with that, we've brought that into the visual editor as well.\u003C\u002Fp>\u003Cp>So if you're looking at the visual editor and you want to preview draft content, you can now do that, via the visual editor. So it's part of a foundational part to making all of these content editing workflows much more intuitive for you for your business folk who are managing content. And we'll see, you know, extensions of that over the next couple of releases, for sure, particularly in the v 12 release, to make that feel much more native, that whole publishing workflow of content.\u003C\u002Fp>\u003Cp>Speaker 0: So practically, that means that today, the moment you like you said, the moment you like the moment you open an item, it's straight into draft. So then it's it's way more of that sort of one, two step. You make your changes through draft. You hit publish by the time you're ready. You don't have to manually switch, back and forth ahead of time for that to work.\u003C\u002Fp>\u003Cp>Now that allows us to start working towards way more powerful capabilities in the future as well, of course. So we're thinking about things like dedicated permissions for versioning or potentially approval workflows even or releases of multiple of these content items together. So this this first draft change is is one in a step of many, many changes all related to what we're calling the CEW project or the content editorial workflow project, internally.\u003C\u002Fp>\u003Cp>Speaker 1: Awesome. That's right.\u003C\u002Fp>\u003Cp>Speaker 0: Then another thing we shipped recently that fits really well in this sort of one two punch of you make a bunch of changes and you ship it out. Can you guess what it is? It's the deployments module. So we just shipped a new module directly in the studio integrated with Vercel, quickly followed up by Netlify by a community member. Thank you for that.\u003C\u002Fp>\u003Cp>Thank you. What what is that deployments module all about?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So it's it's something we we've even struggled with internally, our internal marketing teams that use Directus. You know, the sites are deployed on on Vercel or Netlify or, you know, other other hosting providers. And let's say you're managing some content inside Directus, you then have to leave Directus, you know, assuming you have access to that third party solution, to then go and deploy your site to reflect the the changes that you've made in your CMS. Now that's a little bit nuanced, whether you want all of your content editors to have access to that system, which is primarily about deployments, potentially not.\u003C\u002Fp>\u003Cp>But maybe you wanna give them the flexibility to still rebuild the site on demand. So this is really about bringing that functionality natively into director's core so that you can make those changes. And then, you know, you can go in and you can hit, trigger a build in in Vercel or Netlify. And then we'll give you the, you know, the the deployment status, the build logs, give you a link out to see the the the, the the site once it's built. So, yeah, I mean, all all covered by RBAC.\u003C\u002Fp>\u003Cp>So, you know, if you you want to delegate responsibility beyond the admin in terms of who can trigger deployments, that's all kind of covered by the existing, role based access control settings inside Directus. That's what it's all about. I mean, I think we'd love to extend this beyond Vercel and Netlify as well. So you probably got some ideas.\u003C\u002Fp>\u003Cp>Speaker 0: Gonna say, like, with with most everything we've built in Direct is we've tried to do this in as most of an abstract that way as we could, which is why we saw Netlify flying relatively quickly. Now this is this is too good of a a segue not to use it. If people wanna request which one we should add as a third, where where did they do that?\u003C\u002Fp>\u003Cp>Speaker 1: Well, I I guess you could come come on to here. Right? And you could still add the feedback here, just so it's, you know, tightly related to this project. But this is a completed project, so maybe the best thing to do would be to hit new request. Tell us that you want Cloudflare pages or something like that.\u003C\u002Fp>\u003Cp>And then, that's gonna come right to us. And, obviously, that's gonna be something that we're but we're already thinking about it. Right?\u003C\u002Fp>\u003Cp>Speaker 0: For sure. For sure. Yeah. Alright. Onto a very different topic then.\u003C\u002Fp>\u003Cp>And this is a feature request that I opened back on GitHub maybe four years ago, if not five at this point. The ability to potentially filter within JSON fields. It is one that we've sort of toyed with. We've tried it out. We've done a PR in the past that didn't quite land, but there's updates on that front.\u003C\u002Fp>\u003Cp>James, what's the latest?\u003C\u002Fp>\u003Cp>Speaker 1: There is. And they're currently showing is in progress, but, you know, part of this was actually released in in, like you said, the 11\u002F2016. But, yeah, this is really about bringing support to interact with, you know, JSON data in your database, at the API. So the first piece that we delivered in 11\u002F2016 allows you to select fields within a JSON blob very simply. So you don't have to bring the whole thing, you know, client side and deal with it yourself.\u003C\u002Fp>\u003Cp>That is nice, but it's clearly not the end goal. The end goal is to to bring full support, in the API for filtering, sorting, aliasing, across all of the different database vendors, of course, and then extend it, you know, to to the SDK as well. Make sure it's not just supported in the rest API. So we'll see a big big chunk of that, particularly the filtering drop in the next release. And I think that, you know, in subsequent releases, there might be additional things just to to get us to that final state.\u003C\u002Fp>\u003Cp>But, yeah, like you said, I think the original GitHub discussion maybe we can have a look. Got a feeling this was yeah. A little while ago. But lots of community support for this, so really happy to be able to to work on this. Shout out to Tim internally who's been working on this.\u003C\u002Fp>\u003Cp>It was actually a bit of a hackathon project that ended up getting rolled out, into the core. So\u003C\u002Fp>\u003Cp>Speaker 0: It it was something we've all been wanting to have for a long time, so I'm glad we're getting to it now.\u003C\u002Fp>\u003Cp>Speaker 1: Awesome. Yeah. Super exciting. What about something that's probably quite close to your involvement, which is we've had a bit of an AI related work stream over the last few months. Things have developed not just not just in the market, but also for directors.\u003C\u002Fp>\u003Cp>So maybe you wanna give us a few, a few thoughts on how\u003C\u002Fp>\u003Cp>Speaker 0: how things develop. I might use this as a little bit of a a sneaky personal pitch, but if you happen to have caught my my recent keynotes at the at Vue JS Amsterdam conference or coming up at Vue JS Atlanta, one thing that we have been heavily focused on is to see how can we use the AI technology stacks, so LLMs and generative images and things like that, within the product in a way that actually makes sense for our users. Right? So the thing I'm personally always scared of is shipping a bunch of gimmicks that nobody then cares about, which, you know, hot takes, but it happens all over the place. So how do we use that technology then in a way that actually makes sense?\u003C\u002Fp>\u003Cp>So the first thing we did is implement that MCP endpoint that shipped, I wanna say, in 11\u002F13 or even '12. It's a little while back now. It's been out for a minute. We did it as a standalone package first to test the waters. That went really incredibly well.\u003C\u002Fp>\u003Cp>Then we moved it into core as a native feature that is just there by default. So that allows LLMs outside of the product to use the content and data within and but also take other system actions, like doing the data modeling or even setting up permissions. Although, make sure you confirm what it's doing. The second big step for us there was to move that experience into the product itself. So right now, we have that AI chat sidebar, natively in the product.\u003C\u002Fp>\u003Cp>So it's always available in the bottom right, and it knows the context of the page. Where are you? What tools are available? What can it, you know, what can it do both on the client and server side? So on the server side, we've implemented the same MCP tools that you had before from external LLMs.\u003C\u002Fp>\u003Cp>So that is things like changing the data model, reading or writing items, retrieving files, and some other system operations like that. The second big thing we started developing is sort of the client side tooling integration of that. And what that means is that the LLM chat sidebar can take actions in the studio on your behalf. So not just on the API, but in the studio as well. We're continuing to explore what that looks like.\u003C\u002Fp>\u003Cp>So right now, we're focusing heavily on exactly the e d assets and translations workflow. So that is really focusing on, you know, helping you do the content creation in various languages that you might not be native in on the fly, in line in the studio rather than doing it, you know, hidden away in the background. So a lot of very exciting stuff on that front. We're also doing a little bit of r and d in this sort of more agentic workflows in the background and background processes, but, you know, that is a little too early to to share too much of that.\u003C\u002Fp>\u003Cp>Speaker 1: Nice. And I think since we released some of these capabilities, particularly the the embedded AI assistant, lot of feedback we got was around extending support. Right? I think we went live with Anthropic and OpenAI, but maybe you can add a bit on how we've looked at support beyond that and and what people are still asking for.\u003C\u002Fp>\u003Cp>Speaker 0: For sure. Yeah. I mean, as as anybody who's been anybody who who stumbled into Twitter in in the recent week or so, I mean, you see these models, they they evolve so, so, so rapidly that it is near impossible to just pick one vendor and and be happy with it for, you know, years to come. So as with most everything else at Directus, like we just saw with, the other features we talked about as well around deployment, we wanna make sure we built this in a an agnostic a way as we possibly can. So we'd indeed shipped it with an abstraction with just on top of OpenAI and anthropic to start with.\u003C\u002Fp>\u003Cp>But then immediately, people started mentioning. It's like, oh, but what about Gemini? Or what about Grok? God forbid. Or what about an OpenAI endpoint that I self host, which is very interesting.\u003C\u002Fp>\u003Cp>So we started shipping, some of the big other vendors that you'd expect. So I think we we, well, not I think I know we added support for for Gemini pretty quickly after, but are now also really focusing in on, like, okay. What are the nuances of, like, an open AI that runs on Azure that you have full control over, or what happens if you run a Mistral AI model that you have self hosted? To really tie back into that data sovereignty play. Right?\u003C\u002Fp>\u003Cp>Because at the end of the day, the technology stack is really cool and exciting, but you might not be comfortable with sending, you know, that private data from your database over to some external company that you may or may not trust. So this is a really interesting next angle here for the way we treat AI within Directus that I think fits in really well in our sort of overall self hosted, data sovereignty play.\u003C\u002Fp>\u003Cp>Speaker 1: Very nice. Very nice. So, yeah, you can you can add add an open AI API compatible, that's a hell of a mouthful, provider inside the director settings today if you wanna use the the AI assistant, with an alternative provider. Let's come back to the completed section. I know we're going jumping all over the place as we go, but I think that's okay.\u003C\u002Fp>\u003Cp>Yeah. I mean, this was an interesting one internally for the team, at least. I think collaborative editing was a was a capability that was that was developed suddenly before I joined Directus, but originally built as a extension. Is that right?\u003C\u002Fp>\u003Cp>Speaker 0: That's right. And that was really to make sure that we, just as with the MCP endpoint, had a way to experiment with what that needed to look and feel like. So as you might imagine, with something as broadly used as direct as in terms of use cases, with that comes a lot of different hopes and dreams about how that collaborative nature of the thing works. Is everybody working on the same draft state that we just talked about for the CEW project, or are you working all locally, and does it sync up only when you hit save? And how does that then do do merge resolutions and and complicated things like that?\u003C\u002Fp>\u003Cp>So we shipped it as a plug in first to test the waters. That went really well. We learned a lot from that. Then by the time we were like, okay. This feels now like we know what this needs to look like in the product for real, we were able to move it back into core.\u003C\u002Fp>\u003Cp>And luckily, because, you know, extensions are built as very modular pieces, that are built in the same way that we built the core product, it was actually relatively straightforward to to move that in here as well.\u003C\u002Fp>\u003Cp>Speaker 1: Nice. I think that one was added in in 11\u002F2015 maybe. But, yeah, that's now basically a core a core capability. So if you want to enable collaborative editing, you could do that in the project settings. Once you enable that, you're gonna see, you know, multiple avatars if you're collaborating over the same same item inside the studio.\u003C\u002Fp>\u003Cp>And you get things like field locking behavior as well. So if somebody's working in a field, obviously, you can't edit it while they're editing it. So, yeah, really nice, feature for for content folks, who want to collaborate together. So highly recommend switching it on and, of course, giving us feedback, if you experience anything that you think could be improved or anything like that.\u003C\u002Fp>\u003Cp>Speaker 0: If you know where to find us now. Also, with that quick shout out, you know, we we often do use extensions as the the vessel to do experimental work. So if you're you're curious to see what is, you know, the newest of the new, do take a look at the extensions that are out there, both from us and and the community. We post a lot of experimental work on the Directus Labs GitHub organization that I think is worth, a quick look.\u003C\u002Fp>\u003Cp>Speaker 1: Awesome. Maybe that's a good segue into something which we're gonna have to comment on because it's it's sitting right at the top. Certainly, the thing that caught my attention since I joined Directus, right, which is this one. And the reason I say that is because you you mentioned extensions, and you mentioned, you know, at the moment, this is something where I think people who are looking for a solution to configure as code or or gen more generally speaking, environment synchronization, they are having to look to third party extensions, and there are some good ones out there. I think that's\u003C\u002Fp>\u003Cp>Speaker 0: For sure.\u003C\u002Fp>\u003Cp>Speaker 1: That's something we can say for sure. So a bit of context on this one today. I think schema snapshot is all all or nothing. Right? You can synchronize, one environment to another, but it's all or nothing.\u003C\u002Fp>\u003Cp>And I think that that's not really providing the granular control that people need. I think they need to be able to do it resource specific, be able to exclude, include certain collections, and, of course, support things beyond the schema itself, roles, permissions, etcetera. Maybe we could comment here. I mean, I think I think this is a really big priority for us this year, and we wanna give visibility that this is top of mind. What I would say is at the moment, if you're struggling with this, look to the third party extensions.\u003C\u002Fp>\u003Cp>Some of the the third party extensions here, you know, the the director sync, underneath Traktor. Really good, fully modular, decomposes the schema into discrete files. You know, highly recommend that, if you need kind of selective syncing capabilities in absence of anything native. The migration bundle is actually a Directus Labs one. This is very UI driven, allows you to select resource types, push them to different environments.\u003C\u002Fp>\u003Cp>And then the Directus schema sync as well is another one that I'm aware of that that allows for sort of event driven auto export as well. So there are some great solutions out there. Right? But I think that one of the concerns is is some of these are, you know, they're they're not native solutions, and so we're really looking to bring kind of a first party experience here as well. Don't know if you you wanna add anything on this one, Reich.\u003C\u002Fp>\u003Cp>Speaker 0: Well, I think the fascinating thing here the question is is I'm sure on top of everybody's mind is, what took you so long? Like, why has this thing been open for such a long time? It's been the number one or two. At least top three upvote of feature requests for, you know, as long as this project has been live. And I think it's interesting to dive into that context a little bit because there's there's two feature requests that come to mind that have been open and upvoted a lot.\u003C\u002Fp>\u003Cp>The second one, we'll circle back to, you know, I I would say put in the comments if you can guess which it is. But config as code, it's one of the ones where there's just such a tremendous wide range of opinions and hopes and dreams. Right? Even different use cases as well. Some people wanna use it as a sort of way to move from a dev environment to a prod environment, which is very valid.\u003C\u002Fp>\u003Cp>Some people wanna use it more as a sort of backup and restore approach that you can go back in time to a previous state of your project. Some people have been telling us they wanna use it as a sort of template for new projects. Like, if you run an agency, you just wanna, you know, spin up multiple copies of the same underlying template. And all of those different use cases, they all kind of require the same solution with a twist. So they're all slightly different, and they all have their own requirements and and differences, which is why this has been in in research phase for a long, long time while we just capture and ingest all of that information.\u003C\u002Fp>\u003Cp>So over the now years, we've been talking to a lot of different people about what this means, and what those different use cases are and and outline how it all comes together. And we have a pretty good plan now into how this, this could actually look like for real as a native feature. So that is super exciting and something, you know, I look forward to having.\u003C\u002Fp>\u003Cp>Speaker 1: Cool. Have we covered the completed? Do we want to look at other things on here that are either in progress or coming up?\u003C\u002Fp>\u003Cp>Speaker 0: I mean, when it comes to the completed list, there's there's a lot more to look at here on the road map itself. In the interest of time, you know, I don't wanna spend the rest of the whole day or even this week just going through them item by item. If you do have questions about any of these other stuff, by the way, a, you know, where to find this now on the road map itself, but, also, we have the community, of course. Right? So we have community.directus.io.\u003C\u002Fp>\u003Cp>If you have questions about anything you hear today, anything here on the road map, feel free to to put a post up there as well. We we actively monitor that. I'm more than happy to chat with you, about any anything and everything, frankly. So find us there. I think, you know, a couple of small things that are probably worth calling out on the to do list is the MIME type restrictions on file interfaces, relatively tiny thing, but it's something, you know, people have been asking for, for quite some time.\u003C\u002Fp>\u003Cp>So we're very glad that we've been able to to to put that in. We did a we did a revision to revisions, an update to how, you know, the comparison looks like if you if you look, to compare the current state of your item with a a previous state. So we we changed that from from the little sidebar you might have seen, with the red green changes into more of a side by side view that is a lot more in-depth and allows you to explore, you know, the things that changed. Other than that, we did also finally add bulk file and folder downloading. So in the file library, you could select multiple things at once.\u003C\u002Fp>\u003Cp>Just download them all as a as a folder instead of having to go one by one. It's the little things. It's the little things that are huge quality of life unlocks here for the platform. You know? So oftentimes, things like that, they're a little bit too small to mention anything like this, but it's definitely worth shouting out, I think.\u003C\u002Fp>\u003Cp>Speaker 1: For sure. The the other one that comes to mind is the, external third party JWT support. I think this is something we saw a lot of feedback around how to manage that, how to validate a third party JWT translated into direct us accountability object. And I think we exposed some u utils there to to make that much easier, and there's a there's a really great reference guide. So if you are looking to, you know, integrate sort of for machine to machine purposes with Okta and things like that, then, you know, there's a there's a great reference tutorial, which hopefully is, like, flashing up on screen or something right now, that that I'd recommend people check out as well.\u003C\u002Fp>\u003Cp>Speaker 0: And it's another one of those things. Right? It's like, from the outside looking in, it's it's just hard to really know what that means and what that unlocks. But that external JWT validation means that you can have your users log in using something like Okta or Auth0 and then use that same token you already have from that external service as the token indirect is, which is, you know, not everybody might need that, but when you do, you really do, and there's really no other workaround. So, again, I'm very glad that our extension service combined with some of these utilities that we provided are now allowing you to to unlock that that otherwise blocker.\u003C\u002Fp>\u003Cp>Speaker 1: Good stuff. I think beyond that, looking at kind of, you know, what's in progress now, I think we've we've touched upon a lot of these already. Like we said, the the global draft was the kind of foundation towards this draft and publishing workflow, having that really intuitive experience, and incorporating things within that, like auto save when you're editing a form, which kind of relates to this one. Right? You know, you wanna save and stay.\u003C\u002Fp>\u003Cp>Well, if you have auto save, well, that kind of becomes somewhat redundant. So looking to solve some of these problems that exist in the to do with, you know, some of the solutions that are also in progress right now.\u003C\u002Fp>\u003Cp>Speaker 0: Absolutely. I I teased it a little bit before when I said top three, most upvoted things. Save and stay button, definitely one of the three, but we we have to touch on the elephant in the room, which is renaming collections and fields. You want it. We want it.\u003C\u002Fp>\u003Cp>We all want it. Everybody has wanted it from from a long time. This is one where it's not so much the yeah. There it is. Triple digit.\u003C\u002Fp>\u003Cp>There it is. The big one, the the light will of this team. The the, where where the config is code or even the save and state default state is one of the ones that takes a lot of research because there's so many different opinions and so many different ways of approaching it. Renaming collections of fields feels like a very dumb one in comparison. Right?\u003C\u002Fp>\u003Cp>It's like you have it or you don't. There's really not a lot to figure out for that. So you might be wondering, like, what the hell takes you so long to to do that for the same reason. And this is quite frankly, you know, system design thing from from back in the day. So we have, when we make that change in the database well, of course, it's a very destructive change in the database that is connected, so there's a third party tooling problem potentially.\u003C\u002Fp>\u003Cp>But, But, also, there's a lot of internal references that need to be updated, some of which, can be a bit under the hood, for things like layout options and and other references that are otherwise hidden. So we've we've seen, quite a few different experiments on this topic, recently in in extensions, which, again, thank you for that. It's it's great inspiration. Make sure to check those out. But we also recently started experimenting internally with, like, okay.\u003C\u002Fp>\u003Cp>How could this come together in a way that doesn't immediately break everything? Right? So how can we work on API aliasing, for example, to unlock some of the the requirements for this without breaking the connected database and its connected services that we might not know about. Alternatives as well, of course. Like, what what are the options that we do all of those updates in the system tables as one giant transaction in case, you know, something goes wrong and you have to undo it?\u003C\u002Fp>\u003Cp>All of that is to say is do know that there's not a day that I wake up and not think about renaming collections of fields. You you you find me in person at conferences and it's the first thing you bring up and and do know that I've I've heard you loud and clear. And it's something that's very, very top of mind.\u003C\u002Fp>\u003Cp>Speaker 1: Good stuff. So we expect to hear more about this one, you think, this year?\u003C\u002Fp>\u003Cp>Speaker 0: For sure. I mean, you're gonna hear me bring it up every single time, if if anything. So you're gonna hear about it. You're gonna hear about it. Now we're we have some some very active experiments in flight now to see how we can bring this to life in a way that doesn't completely break everything or is a huge breaking change to migrate into and all that that kind of stuff.\u003C\u002Fp>\u003Cp>Because, of course, you know, it's it's it's one thing to say we now allow you to rename everything, but the migration into the new version takes, you know, a week to do by hand. That's not good enough for us either. So bear with, you know, there's quite a lot of technical work to be figured out here, but do know that we're we're actively talking about it.\u003C\u002Fp>\u003Cp>Speaker 1: It's front of mind.\u003C\u002Fp>\u003Cp>Speaker 0: Front of mind.\u003C\u002Fp>\u003Cp>Speaker 1: Good stuff. Beyond or behind all of this. Right? We've spoken a lot about a lot of stuff today, but maybe to think, beyond just what's visible on the road map here. Few rumors on forums, few things being hinted at over the last year.\u003C\u002Fp>\u003Cp>People are naturally gonna be curious about what that means. If you know, you know. If you've seen things in the forums, you might know what we're speaking about. But can you tell us anything about that, right, is there anything we can let people know about?\u003C\u002Fp>\u003Cp>Speaker 0: Well, I can say we have been teasing and and sometimes leaking, you know, who you are. Some of these some of the information behind this. But, yes, the the rumors there are true. We have been hard at work behind the scenes on something brand new as well. So this is gonna be a separate thing from Directus altogether with more of a back end as a service focus than we've had in the past.\u003C\u002Fp>\u003Cp>It's his own image, inspired by Directus and using the learnings that we have, but aimed at a way sort of larger scale, multi data source, deployment paradigm. I don't wanna overshare too much yet before people get mad at me, but we are putting out a a sign up link if you wanna learn more. So we're gonna do some early sign ups for alpha release for a sort of private testing phase. We're very excited about this, and I wish I could just share, you know, a lot more than that. But be assured, we're gonna be sharing a lot more in the next league week, about this project.\u003C\u002Fp>\u003Cp>Speaker 1: Very nice. Very nice. I think that's a small small teaser for now. And like I said, there should be a resource for people to go and, sign up and and and and get onto some kind of wait list for something. Look forward to hearing more about that.\u003C\u002Fp>\u003Cp>Alright.\u003C\u002Fp>\u003Cp>Speaker 0: With that all being said, I I know we mentioned it a couple of times today, but if you have any other feature requests or ideas about what this road map should look like or what you find important or don't, that's also very valuable information. Know where to find us at roadmap.tractors.io. That gets you straight in touch with James and yours truly. For everything else, any questions, any concerns, any other teasers, find your way to community.directus.io. We have a wonderful discourse server over there, that we actively moderate and manage, and that is incredibly active within the community.\u003C\u002Fp>\u003Cp>So I highly recommend find your way to those two resources, and we'll see you on the Internet.\u003C\u002Fp>","Alright. And with that, we've made it to the last session of the Leap Week. I really hope you enjoyed all the great announcements so far. But before we wrap up today, we wanted to take a beat and talk a little bit about what we've shipped recently and what is next for the Directus platform as a whole. I'm Greg Consonzo, the CTO and cofounder here at Directus, and with me is James. Thank you very much. Yeah. I'm James. Working at Directus as a staff product manager. Thank you for the intro, Rick. So first and foremost, some of you might have seen that on GitHub discussions, we put a big banner, and we we posted a couple of comments about our new road map. So we moved platforms over to roadmap.direct.io. You might have seen it. Why why did we make this change, James? If we look at the old mechanism for feature requests, we used to manage this through GitHub discussions. Honestly, it became a a place for things to kind of go away and die, unfortunately, through no, you know, no means on our part or no deliberate means, but, it wasn't so integrated with, you know, linear and the development workflows of the teams. So it's kinda had this, you know, disjointed journey between the feature requests and then how they get actioned and then making that visible to the community as well. So we've started to use a new a new product. And like Wrike said, you'll have seen on some of the discussion threads, that, you know, we've moved over now. So some of the threads have been locked. Some of them we've kept open because we wanna keep the discussion going. Right? And we don't want that to stop on some of that particularly some of the ones that have been upvoted heavily. But what you will find is that, you know, these have been ported over to our our new road map, which you can find at roadmap.directus.io\u002Froadmap. You can go in here, search for existing discussions, and we've maintained that link back to the GitHub thread as well, because there's lots of useful information there. So, yeah, that's that's kinda how things look. I don't know if you've got anything to add, Ryke. Yeah. I'll say it's it's one of those interesting points where it feels very obvious to keep it all centralized on the repo. I mean, GitHub already has discussions. They already have upvotes. They already have, like, a, submitted by status. They already have the whole it's it's, approved or denied or completed and all that kind of stuff. So it it might be a little weird to just see that move over. But at the end of the day, we kinda just outgrew the tooling that was available. Right? So even right now, when we noticed, oh, we wanna move over, there's no way for us to to prevent new ones from being open on GitHub without shutting down all of the old ones, which is very unfortunate because then people can't find them. So at the end of the day, you know, this is a great, great upgrade, to at least be able to manage these feature requests and and also help our team ship, the things that are truly important here to the community. With that as well, any request that you put into this product line right now goes straight to our product team. So you're also straight talking to the source and and the folks in charge of that roadmap. You're truly in James in this case, which is a lot nicer line of communication in that regard. So just as before, you know, please do take a look at the road map. If you see something that you agree with or you're aligned with, please hit that upvote button. If you have a different request that nobody else has requested yet on this particular list, hit the new request button, and it will fly straight to us. And we'll we'll reach out, for more information. That's right. Give it to me. Plus one. Any updates? Direct line of communication, like you said. And Exactly. Feel free to add an extra layer of detail. If you see something that, you know, you see yourself in in the request, but you wanna add, you know, additional information because that comes directly to me and Mike. Exactly. And with that being said, I think let's take a look at this road map sort of going in in reverse chronological order maybe for this one. Let's take a look at some of the stuff that we've recently completed. So we'll we'll group this roughly by release, instead of chronological order. Well, I guess it's kinda the same if you think about it. But, you know, we we plan these things out on the road map over time. But starting with with 11 dot 16 release, we have the global draft versions, which is a a part of a bigger new project. James, what what is that project all about? Yeah. So I think there's been a a perception that, the content versioning features inside Directus have been kind of a advanced feature. They're a little bit tucked away. They work a little bit like GitHub branches, funny enough today. But for, you know, content editors, that need to version content, that workflow for how you work with the draft, how that gets promoted or published into the, you know, the the the live item has been not super intuitive. So we started on this track to make that a lot more native. And the latest update brings, you know, a global draft version to any item that has content versioning enabled. So the moment you switch that on, you're gonna have a draft version inside that that item. That was something you'd have to create yourself before. And then with that, we've brought that into the visual editor as well. So if you're looking at the visual editor and you want to preview draft content, you can now do that, via the visual editor. So it's part of a foundational part to making all of these content editing workflows much more intuitive for you for your business folk who are managing content. And we'll see, you know, extensions of that over the next couple of releases, for sure, particularly in the v 12 release, to make that feel much more native, that whole publishing workflow of content. So practically, that means that today, the moment you like you said, the moment you like the moment you open an item, it's straight into draft. So then it's it's way more of that sort of one, two step. You make your changes through draft. You hit publish by the time you're ready. You don't have to manually switch, back and forth ahead of time for that to work. Now that allows us to start working towards way more powerful capabilities in the future as well, of course. So we're thinking about things like dedicated permissions for versioning or potentially approval workflows even or releases of multiple of these content items together. So this this first draft change is is one in a step of many, many changes all related to what we're calling the CEW project or the content editorial workflow project, internally. Awesome. That's right. Then another thing we shipped recently that fits really well in this sort of one two punch of you make a bunch of changes and you ship it out. Can you guess what it is? It's the deployments module. So we just shipped a new module directly in the studio integrated with Vercel, quickly followed up by Netlify by a community member. Thank you for that. Thank you. What what is that deployments module all about? Yeah. So it's it's something we we've even struggled with internally, our internal marketing teams that use Directus. You know, the sites are deployed on on Vercel or Netlify or, you know, other other hosting providers. And let's say you're managing some content inside Directus, you then have to leave Directus, you know, assuming you have access to that third party solution, to then go and deploy your site to reflect the the changes that you've made in your CMS. Now that's a little bit nuanced, whether you want all of your content editors to have access to that system, which is primarily about deployments, potentially not. But maybe you wanna give them the flexibility to still rebuild the site on demand. So this is really about bringing that functionality natively into director's core so that you can make those changes. And then, you know, you can go in and you can hit, trigger a build in in Vercel or Netlify. And then we'll give you the, you know, the the deployment status, the build logs, give you a link out to see the the the, the the site once it's built. So, yeah, I mean, all all covered by RBAC. So, you know, if you you want to delegate responsibility beyond the admin in terms of who can trigger deployments, that's all kind of covered by the existing, role based access control settings inside Directus. That's what it's all about. I mean, I think we'd love to extend this beyond Vercel and Netlify as well. So you probably got some ideas. Gonna say, like, with with most everything we've built in Direct is we've tried to do this in as most of an abstract that way as we could, which is why we saw Netlify flying relatively quickly. Now this is this is too good of a a segue not to use it. If people wanna request which one we should add as a third, where where did they do that? Well, I I guess you could come come on to here. Right? And you could still add the feedback here, just so it's, you know, tightly related to this project. But this is a completed project, so maybe the best thing to do would be to hit new request. Tell us that you want Cloudflare pages or something like that. And then, that's gonna come right to us. And, obviously, that's gonna be something that we're but we're already thinking about it. Right? For sure. For sure. Yeah. Alright. Onto a very different topic then. And this is a feature request that I opened back on GitHub maybe four years ago, if not five at this point. The ability to potentially filter within JSON fields. It is one that we've sort of toyed with. We've tried it out. We've done a PR in the past that didn't quite land, but there's updates on that front. James, what's the latest? There is. And they're currently showing is in progress, but, you know, part of this was actually released in in, like you said, the 11\u002F2016. But, yeah, this is really about bringing support to interact with, you know, JSON data in your database, at the API. So the first piece that we delivered in 11\u002F2016 allows you to select fields within a JSON blob very simply. So you don't have to bring the whole thing, you know, client side and deal with it yourself. That is nice, but it's clearly not the end goal. The end goal is to to bring full support, in the API for filtering, sorting, aliasing, across all of the different database vendors, of course, and then extend it, you know, to to the SDK as well. Make sure it's not just supported in the rest API. So we'll see a big big chunk of that, particularly the filtering drop in the next release. And I think that, you know, in subsequent releases, there might be additional things just to to get us to that final state. But, yeah, like you said, I think the original GitHub discussion maybe we can have a look. Got a feeling this was yeah. A little while ago. But lots of community support for this, so really happy to be able to to work on this. Shout out to Tim internally who's been working on this. It was actually a bit of a hackathon project that ended up getting rolled out, into the core. So It it was something we've all been wanting to have for a long time, so I'm glad we're getting to it now. Awesome. Yeah. Super exciting. What about something that's probably quite close to your involvement, which is we've had a bit of an AI related work stream over the last few months. Things have developed not just not just in the market, but also for directors. So maybe you wanna give us a few, a few thoughts on how how things develop. I might use this as a little bit of a a sneaky personal pitch, but if you happen to have caught my my recent keynotes at the at Vue JS Amsterdam conference or coming up at Vue JS Atlanta, one thing that we have been heavily focused on is to see how can we use the AI technology stacks, so LLMs and generative images and things like that, within the product in a way that actually makes sense for our users. Right? So the thing I'm personally always scared of is shipping a bunch of gimmicks that nobody then cares about, which, you know, hot takes, but it happens all over the place. So how do we use that technology then in a way that actually makes sense? So the first thing we did is implement that MCP endpoint that shipped, I wanna say, in 11\u002F13 or even '12. It's a little while back now. It's been out for a minute. We did it as a standalone package first to test the waters. That went really incredibly well. Then we moved it into core as a native feature that is just there by default. So that allows LLMs outside of the product to use the content and data within and but also take other system actions, like doing the data modeling or even setting up permissions. Although, make sure you confirm what it's doing. The second big step for us there was to move that experience into the product itself. So right now, we have that AI chat sidebar, natively in the product. So it's always available in the bottom right, and it knows the context of the page. Where are you? What tools are available? What can it, you know, what can it do both on the client and server side? So on the server side, we've implemented the same MCP tools that you had before from external LLMs. So that is things like changing the data model, reading or writing items, retrieving files, and some other system operations like that. The second big thing we started developing is sort of the client side tooling integration of that. And what that means is that the LLM chat sidebar can take actions in the studio on your behalf. So not just on the API, but in the studio as well. We're continuing to explore what that looks like. So right now, we're focusing heavily on exactly the e d assets and translations workflow. So that is really focusing on, you know, helping you do the content creation in various languages that you might not be native in on the fly, in line in the studio rather than doing it, you know, hidden away in the background. So a lot of very exciting stuff on that front. We're also doing a little bit of r and d in this sort of more agentic workflows in the background and background processes, but, you know, that is a little too early to to share too much of that. Nice. And I think since we released some of these capabilities, particularly the the embedded AI assistant, lot of feedback we got was around extending support. Right? I think we went live with Anthropic and OpenAI, but maybe you can add a bit on how we've looked at support beyond that and and what people are still asking for. For sure. Yeah. I mean, as as anybody who's been anybody who who stumbled into Twitter in in the recent week or so, I mean, you see these models, they they evolve so, so, so rapidly that it is near impossible to just pick one vendor and and be happy with it for, you know, years to come. So as with most everything else at Directus, like we just saw with, the other features we talked about as well around deployment, we wanna make sure we built this in a an agnostic a way as we possibly can. So we'd indeed shipped it with an abstraction with just on top of OpenAI and anthropic to start with. But then immediately, people started mentioning. It's like, oh, but what about Gemini? Or what about Grok? God forbid. Or what about an OpenAI endpoint that I self host, which is very interesting. So we started shipping, some of the big other vendors that you'd expect. So I think we we, well, not I think I know we added support for for Gemini pretty quickly after, but are now also really focusing in on, like, okay. What are the nuances of, like, an open AI that runs on Azure that you have full control over, or what happens if you run a Mistral AI model that you have self hosted? To really tie back into that data sovereignty play. Right? Because at the end of the day, the technology stack is really cool and exciting, but you might not be comfortable with sending, you know, that private data from your database over to some external company that you may or may not trust. So this is a really interesting next angle here for the way we treat AI within Directus that I think fits in really well in our sort of overall self hosted, data sovereignty play. Very nice. Very nice. So, yeah, you can you can add add an open AI API compatible, that's a hell of a mouthful, provider inside the director settings today if you wanna use the the AI assistant, with an alternative provider. Let's come back to the completed section. I know we're going jumping all over the place as we go, but I think that's okay. Yeah. I mean, this was an interesting one internally for the team, at least. I think collaborative editing was a was a capability that was that was developed suddenly before I joined Directus, but originally built as a extension. Is that right? That's right. And that was really to make sure that we, just as with the MCP endpoint, had a way to experiment with what that needed to look and feel like. So as you might imagine, with something as broadly used as direct as in terms of use cases, with that comes a lot of different hopes and dreams about how that collaborative nature of the thing works. Is everybody working on the same draft state that we just talked about for the CEW project, or are you working all locally, and does it sync up only when you hit save? And how does that then do do merge resolutions and and complicated things like that? So we shipped it as a plug in first to test the waters. That went really well. We learned a lot from that. Then by the time we were like, okay. This feels now like we know what this needs to look like in the product for real, we were able to move it back into core. And luckily, because, you know, extensions are built as very modular pieces, that are built in the same way that we built the core product, it was actually relatively straightforward to to move that in here as well. Nice. I think that one was added in in 11\u002F2015 maybe. But, yeah, that's now basically a core a core capability. So if you want to enable collaborative editing, you could do that in the project settings. Once you enable that, you're gonna see, you know, multiple avatars if you're collaborating over the same same item inside the studio. And you get things like field locking behavior as well. So if somebody's working in a field, obviously, you can't edit it while they're editing it. So, yeah, really nice, feature for for content folks, who want to collaborate together. So highly recommend switching it on and, of course, giving us feedback, if you experience anything that you think could be improved or anything like that. If you know where to find us now. Also, with that quick shout out, you know, we we often do use extensions as the the vessel to do experimental work. So if you're you're curious to see what is, you know, the newest of the new, do take a look at the extensions that are out there, both from us and and the community. We post a lot of experimental work on the Directus Labs GitHub organization that I think is worth, a quick look. Awesome. Maybe that's a good segue into something which we're gonna have to comment on because it's it's sitting right at the top. Certainly, the thing that caught my attention since I joined Directus, right, which is this one. And the reason I say that is because you you mentioned extensions, and you mentioned, you know, at the moment, this is something where I think people who are looking for a solution to configure as code or or gen more generally speaking, environment synchronization, they are having to look to third party extensions, and there are some good ones out there. I think that's For sure. That's something we can say for sure. So a bit of context on this one today. I think schema snapshot is all all or nothing. Right? You can synchronize, one environment to another, but it's all or nothing. And I think that that's not really providing the granular control that people need. I think they need to be able to do it resource specific, be able to exclude, include certain collections, and, of course, support things beyond the schema itself, roles, permissions, etcetera. Maybe we could comment here. I mean, I think I think this is a really big priority for us this year, and we wanna give visibility that this is top of mind. What I would say is at the moment, if you're struggling with this, look to the third party extensions. Some of the the third party extensions here, you know, the the director sync, underneath Traktor. Really good, fully modular, decomposes the schema into discrete files. You know, highly recommend that, if you need kind of selective syncing capabilities in absence of anything native. The migration bundle is actually a Directus Labs one. This is very UI driven, allows you to select resource types, push them to different environments. And then the Directus schema sync as well is another one that I'm aware of that that allows for sort of event driven auto export as well. So there are some great solutions out there. Right? But I think that one of the concerns is is some of these are, you know, they're they're not native solutions, and so we're really looking to bring kind of a first party experience here as well. Don't know if you you wanna add anything on this one, Reich. Well, I think the fascinating thing here the question is is I'm sure on top of everybody's mind is, what took you so long? Like, why has this thing been open for such a long time? It's been the number one or two. At least top three upvote of feature requests for, you know, as long as this project has been live. And I think it's interesting to dive into that context a little bit because there's there's two feature requests that come to mind that have been open and upvoted a lot. The second one, we'll circle back to, you know, I I would say put in the comments if you can guess which it is. But config as code, it's one of the ones where there's just such a tremendous wide range of opinions and hopes and dreams. Right? Even different use cases as well. Some people wanna use it as a sort of way to move from a dev environment to a prod environment, which is very valid. Some people wanna use it more as a sort of backup and restore approach that you can go back in time to a previous state of your project. Some people have been telling us they wanna use it as a sort of template for new projects. Like, if you run an agency, you just wanna, you know, spin up multiple copies of the same underlying template. And all of those different use cases, they all kind of require the same solution with a twist. So they're all slightly different, and they all have their own requirements and and differences, which is why this has been in in research phase for a long, long time while we just capture and ingest all of that information. So over the now years, we've been talking to a lot of different people about what this means, and what those different use cases are and and outline how it all comes together. And we have a pretty good plan now into how this, this could actually look like for real as a native feature. So that is super exciting and something, you know, I look forward to having. Cool. Have we covered the completed? Do we want to look at other things on here that are either in progress or coming up? I mean, when it comes to the completed list, there's there's a lot more to look at here on the road map itself. In the interest of time, you know, I don't wanna spend the rest of the whole day or even this week just going through them item by item. If you do have questions about any of these other stuff, by the way, a, you know, where to find this now on the road map itself, but, also, we have the community, of course. Right? So we have community.directus.io. If you have questions about anything you hear today, anything here on the road map, feel free to to put a post up there as well. We we actively monitor that. I'm more than happy to chat with you, about any anything and everything, frankly. So find us there. I think, you know, a couple of small things that are probably worth calling out on the to do list is the MIME type restrictions on file interfaces, relatively tiny thing, but it's something, you know, people have been asking for, for quite some time. So we're very glad that we've been able to to to put that in. We did a we did a revision to revisions, an update to how, you know, the comparison looks like if you if you look, to compare the current state of your item with a a previous state. So we we changed that from from the little sidebar you might have seen, with the red green changes into more of a side by side view that is a lot more in-depth and allows you to explore, you know, the things that changed. Other than that, we did also finally add bulk file and folder downloading. So in the file library, you could select multiple things at once. Just download them all as a as a folder instead of having to go one by one. It's the little things. It's the little things that are huge quality of life unlocks here for the platform. You know? So oftentimes, things like that, they're a little bit too small to mention anything like this, but it's definitely worth shouting out, I think. For sure. The the other one that comes to mind is the, external third party JWT support. I think this is something we saw a lot of feedback around how to manage that, how to validate a third party JWT translated into direct us accountability object. And I think we exposed some u utils there to to make that much easier, and there's a there's a really great reference guide. So if you are looking to, you know, integrate sort of for machine to machine purposes with Okta and things like that, then, you know, there's a there's a great reference tutorial, which hopefully is, like, flashing up on screen or something right now, that that I'd recommend people check out as well. And it's another one of those things. Right? It's like, from the outside looking in, it's it's just hard to really know what that means and what that unlocks. But that external JWT validation means that you can have your users log in using something like Okta or Auth0 and then use that same token you already have from that external service as the token indirect is, which is, you know, not everybody might need that, but when you do, you really do, and there's really no other workaround. So, again, I'm very glad that our extension service combined with some of these utilities that we provided are now allowing you to to unlock that that otherwise blocker. Good stuff. I think beyond that, looking at kind of, you know, what's in progress now, I think we've we've touched upon a lot of these already. Like we said, the the global draft was the kind of foundation towards this draft and publishing workflow, having that really intuitive experience, and incorporating things within that, like auto save when you're editing a form, which kind of relates to this one. Right? You know, you wanna save and stay. Well, if you have auto save, well, that kind of becomes somewhat redundant. So looking to solve some of these problems that exist in the to do with, you know, some of the solutions that are also in progress right now. Absolutely. I I teased it a little bit before when I said top three, most upvoted things. Save and stay button, definitely one of the three, but we we have to touch on the elephant in the room, which is renaming collections and fields. You want it. We want it. We all want it. Everybody has wanted it from from a long time. This is one where it's not so much the yeah. There it is. Triple digit. There it is. The big one, the the light will of this team. The the, where where the config is code or even the save and state default state is one of the ones that takes a lot of research because there's so many different opinions and so many different ways of approaching it. Renaming collections of fields feels like a very dumb one in comparison. Right? It's like you have it or you don't. There's really not a lot to figure out for that. So you might be wondering, like, what the hell takes you so long to to do that for the same reason. And this is quite frankly, you know, system design thing from from back in the day. So we have, when we make that change in the database well, of course, it's a very destructive change in the database that is connected, so there's a third party tooling problem potentially. But, But, also, there's a lot of internal references that need to be updated, some of which, can be a bit under the hood, for things like layout options and and other references that are otherwise hidden. So we've we've seen, quite a few different experiments on this topic, recently in in extensions, which, again, thank you for that. It's it's great inspiration. Make sure to check those out. But we also recently started experimenting internally with, like, okay. How could this come together in a way that doesn't immediately break everything? Right? So how can we work on API aliasing, for example, to unlock some of the the requirements for this without breaking the connected database and its connected services that we might not know about. Alternatives as well, of course. Like, what what are the options that we do all of those updates in the system tables as one giant transaction in case, you know, something goes wrong and you have to undo it? All of that is to say is do know that there's not a day that I wake up and not think about renaming collections of fields. You you you find me in person at conferences and it's the first thing you bring up and and do know that I've I've heard you loud and clear. And it's something that's very, very top of mind. Good stuff. So we expect to hear more about this one, you think, this year? For sure. I mean, you're gonna hear me bring it up every single time, if if anything. So you're gonna hear about it. You're gonna hear about it. Now we're we have some some very active experiments in flight now to see how we can bring this to life in a way that doesn't completely break everything or is a huge breaking change to migrate into and all that that kind of stuff. Because, of course, you know, it's it's it's one thing to say we now allow you to rename everything, but the migration into the new version takes, you know, a week to do by hand. That's not good enough for us either. So bear with, you know, there's quite a lot of technical work to be figured out here, but do know that we're we're actively talking about it. It's front of mind. Front of mind. Good stuff. Beyond or behind all of this. Right? We've spoken a lot about a lot of stuff today, but maybe to think, beyond just what's visible on the road map here. Few rumors on forums, few things being hinted at over the last year. People are naturally gonna be curious about what that means. If you know, you know. If you've seen things in the forums, you might know what we're speaking about. But can you tell us anything about that, right, is there anything we can let people know about? Well, I can say we have been teasing and and sometimes leaking, you know, who you are. Some of these some of the information behind this. But, yes, the the rumors there are true. We have been hard at work behind the scenes on something brand new as well. So this is gonna be a separate thing from Directus altogether with more of a back end as a service focus than we've had in the past. It's his own image, inspired by Directus and using the learnings that we have, but aimed at a way sort of larger scale, multi data source, deployment paradigm. I don't wanna overshare too much yet before people get mad at me, but we are putting out a a sign up link if you wanna learn more. So we're gonna do some early sign ups for alpha release for a sort of private testing phase. We're very excited about this, and I wish I could just share, you know, a lot more than that. But be assured, we're gonna be sharing a lot more in the next league week, about this project. Very nice. Very nice. I think that's a small small teaser for now. And like I said, there should be a resource for people to go and, sign up and and and and get onto some kind of wait list for something. Look forward to hearing more about that. Alright. With that all being said, I I know we mentioned it a couple of times today, but if you have any other feature requests or ideas about what this road map should look like or what you find important or don't, that's also very valuable information. Know where to find us at roadmap.tractors.io. That gets you straight in touch with James and yours truly. For everything else, any questions, any concerns, any other teasers, find your way to community.directus.io. We have a wonderful discourse server over there, that we actively moderate and manage, and that is incredibly active within the community. So I highly recommend find your way to those two resources, and we'll see you on the Internet.","0a212673-7f3c-4be5-8bb2-cf71df4a9aea",[236,237],"ab4d84a8-5507-4a27-a392-52c6af41a007","b02696e6-fb11-47cf-bcc4-b7378353b257",[],{"year":139,"number":140,"id":141,"episodes":240,"show":241},[143,144,145,146,147,148,123,149],{"title":151,"slug":152},{"id":143,"slug":243,"vimeo_id":244,"description":245,"tile":246,"length":247,"resources":12,"people":12,"episode_number":13,"published":130,"title":248,"video_transcript_html":249,"video_transcript_text":250,"content":12,"seo":251,"status":19,"episode_people":252,"recommendations":254,"season":255},"ui-era-shifting","1176272144","Hear from Directus CEO & Co-Founder Ben Haynes on software is shifting from UI-first to data-first. When everyone can build an interface, what is actually valuable?\n\n","08f64249-c879-4294-92f1-b73a3ea82750",14,"The UI Era is Shifting","\u003Cp>Speaker 0: So let's kick things off. This is Ben, founder and CEO at Directus. And today, I need to talk to you about the end of the UI era. A little sensational, but let's dive in so that I can explain. First off, you might be thinking, don't we have better tools, more people than ever building all these front ends, all these UIs?\u003C\u002Fp>\u003Cp>Yes, absolutely. But when everything is special, nothing is. The front ends have been, commoditized. So let's explore what actually makes software magic, and specifically where the value of software has shifted to and where it's heading next. So since software was created, the interface has been mistaken for the product itself.\u003C\u002Fp>\u003Cp>Users click through, buttons and interfaces and they said, Oh, this is the magic. They saw a dashboard and they said, Oh, that's the application. The Shiny part got the attention for most users. But engineers, you know, for those who are out there, you know better. The visible part of the software is the tip of the iceberg.\u003C\u002Fp>\u003Cp>What lives underneath is the expensive, the fragile part. The part that takes years, decades, to get right. AI has made this a really big deal. Anyone can spin up a decent looking UI, a form, with solid UX, dashboards, components, all of that. They can stitch it together and you can get something that looks just like software.\u003C\u002Fp>\u003Cp>That's amazing. But it's also misleading and maybe even a little dangerous. It creates the illusion that software got easy, but it did not. The front end got easier. The back end maybe even got harder.\u003C\u002Fp>\u003Cp>We have so many builders out there. The back end is the tricky part. And the reason for that is you don't know what you don't know. So while most non engineers can definitely dream up an interface, you are a non technical person. You probably don't know what is needed behind the scenes to actually build this out, make it scalable, make it resilient.\u003C\u002Fp>\u003Cp>So you can Vibe code a UI in a day, but you cannot Vibe code trust. So maybe we just delete the backend. Cursor a little while back actually deleted their headless CMS. As an example. They replaced it in a couple days for a couple $100 worth of tokens.\u003C\u002Fp>\u003Cp>And that really works for a company like theirs. A small team, highly technical, very capable. But that's not going to work for other organizations. You can delete the CMS, but you can't delete the problems that the CMS solves. They come back and when they do, you're either gonna be building from scratch or you're gonna be drowning.\u003C\u002Fp>\u003Cp>So switching gears, let's talk about the back end. You can't one shot your way into production resilience. You can't fake your way into scalable architecture, granular RBAC or access control, reliable APIs, multi tenancy observability, governance rollback strategy, operational maturity. All of this is earned the hard way. Cursor, Lovable, Replit, Bolt and others, they've all made it really easy to create a front end in minutes.\u003C\u002Fp>\u003Cp>And it works. But without a back end, really what you've created is a clickable prototype. The front end and back end distinction gets really blurry because of the excitement around, you know, the generation of this of this front end. And I I love metaphors, so I'll kinda take a a second here. You wouldn't build a house starting with the doorbell, or something like that.\u003C\u002Fp>\u003Cp>You know, the the fresh paint, the beautiful fixtures, the interior design, none of that matters if you've got plumbing, leaking behind the walls, if you've got electricals, you know, sparking fires behind the sheetrock, a foundation that's not even poured and the house is just sinking into the mud. That's just not how things work. Anyone can build these demos, these facades, but far fewer people can build something that actually survives success. You just you can't vibe code trust. So if the front end is becoming free, where does the value go?\u003C\u002Fp>\u003Cp>It goes deeper but not just deeper technically, deeper organizationally. The thing that makes software software wasn't really the pixels. It's not the front end. It's the layer underneath where your whole organization goes, to work together. It's the logic, the APIs, the permissions, the 99.999%, uptime.\u003C\u002Fp>\u003Cp>Your performance under load. You know, does that work? Auditability. Maintainability. The stability that people don't notice until things break.\u003C\u002Fp>\u003Cp>It's not what you see, it's what you feel. You feel when your app goes down. When the data is suddenly corrupted or truncated unexpectedly or deleted. Permissions break. Traffic spikes and things just fall apart.\u003C\u002Fp>\u003Cp>The front end may be what people admire, but the back end again is what people trust. That's what really matters here. So let's first clarify what do we mean by back end. There's the Supabase Lovable partnership and people would refer to Supabase as the backend. It's a database.\u003C\u002Fp>\u003Cp>It's got infrastructure, an API. But really the backend is more than that. The backend is a collaboration layer. It's where technical and non technical users can go to actually access, to browse, manage, and visualize data. It's where data is governed, and used.\u003C\u002Fp>\u003Cp>Not just by engineers, but by anybody in the team. AI accelerates what individuals can do. Our own engineering team uses Cloud Code every day and the velocity gains are very much real. But more complex use cases require humans working together across teams. Engineers with PMs and ops, marketing, legal.\u003C\u002Fp>\u003Cp>AI doesn't solve the collaboration problem. It actually makes it larger. The winners won't be those that can create the prettiest screen the fastest, but those that combine the speed of AI built interfaces with the true discipline of back end engineering. And then democratizing that across the entire organization, technical users and non technical. That's how a hobby project becomes an actual product, or in some cases, how a product becomes an actual company.\u003C\u002Fp>\u003Cp>Okay. So let's break down the back end into three pillars. The first one we'll call governance, how you collaborate safely. The data is there. It's in the database behind every application, that's that's out there.\u003C\u002Fp>\u003Cp>But it's locked behind the doors of IT, as it were, for every report, every change of the data that's needed. If you're non technical, you're probably submitting a support ticket. We need tools that unlock the database for the technical users. Technical users are about 3% of an org, typically. And so we need something like a database administration tool but that's simple, safe, and intuitive enough for anyone to use.\u003C\u002Fp>\u003Cp>It's not about locking things down. It's about opening them up, responsibly. How many one shot AI applications are actually considering RBAC and granular permissions and all that in their system? I would guess not many. So we need to browse, manage, and visualize our data, in an intuitive way.\u003C\u002Fp>\u003Cp>We also need docs, API references, specifications. Those are needed as your team grows, more people join, you need integrations with other services. Those are boilerplate. Let the subject matter experts do their work. We all have seen, you know, a game of telephone and how that can degrade things.\u003C\u002Fp>\u003Cp>You can't pass these requests along. You need people to do that work directly. And so we need interfaces that work for everyone. The second one is scale, or how we take that collaboration and we make it survive the complexity, of the real world. You could also consider that to be production efficiency, resilience.\u003C\u002Fp>\u003Cp>There are a lot of things to consider, consider here. Anyone can build a v one. The problem is when you get to v two, v three, v four, v n, when you go from beta testers out to actual users, in the real world, you bring more people onto your team, internal users. Eventually, you can't just throw hardware at the problem to get by. You actually need to have a proper back end that supports growing your application and whatever it is that you're building.\u003C\u002Fp>\u003Cp>And the third pillar is ownership, or taking all of this and making sure you own what you're building. Again, the value of what you're building is not the front end. That's been commoditized. It's not your cloud infrastructure, your hosting, that's a utility bill, and it's not even really your data that can be scraped, pretty easily these days. So the value is your back end.\u003C\u002Fp>\u003Cp>It's the architecture, the logic and your team, that's, you know, back there moving faster through this collaboration. It's the trust that you create, again, through scale and governance and all of that. It's the value that you unlock from your data with all of this. So you want to pull these systems closer. That's your fence.\u003C\u002Fp>\u003Cp>That's your moat. Self hosting, data sovereignty, open systems. This is where you take control back. So these are the three things that are needed for a proper Backend. But even for the modern organizations that check off all of these boxes, they're still seeing ten, twenty, 30 plus, of these back ends, you know, all over the place.\u003C\u002Fp>\u003Cp>Disparate systems, each with their own API. Maybe it's REST or GraphQL, webhooks. You know, they're all different integrations, different interfaces, different logins. They don't talk to each other. Data is siloed, and so is collaboration.\u003C\u002Fp>\u003Cp>So we're looking at something that has no federation, no unified layer, no consistency and no strategy. And for AI apps, that's even worse. They typically don't even have a backend at all, you know? So here we are. The backend era has begun.\u003C\u002Fp>\u003Cp>So what do we do now? What do we need? We need a unified layer, a single pane of glass that you can put across these disparate systems and bring them together, leaving the data in situ, leaving the data where it is, but federating it and bringing it together. We need to wrap all of that with the granular access control, the rule based permissions, that allow humans and other integrations and agents all alike to have CRUD, to have create, read, update, and delete control, so they're not deleting the data so that you gain more of that trust. You need to bake the tools into this platform that give it the scale and resilience that we were talking about.\u003C\u002Fp>\u003Cp>Not just the API throughput and the data pipelines, but also scaling your team. So again, you need to be able to have everyone working together. And you need to make this a model that is completely yours. So deploying where you need to deploy, whether that's cloud or on prem, you wanna make sure that you are working with an open core, that it's source available. This is the value.\u003C\u002Fp>\u003Cp>So you want to keep all of this really safe. This is actually what we've been building at Directus, and it works. We have 45,000,000 Docker downloads, 35,000 stars on GitHub, You know, over a thousand customers. It started back in 2004, twenty two years ago. My agency in Brooklyn.\u003C\u002Fp>\u003Cp>We were building for Google, Prada, Snapchat, SoulCycled US government, AT and T. And behind every project that we built, there was a database. And we made something for that database that was simple, safe, and intuitive enough for the business user, this collaborative back end. Back then, my team was human, obviously. And we were creating those innovative front ends and now here we are two decades later and it's even more important.\u003C\u002Fp>\u003Cp>Everyone's a builder. Everyone has these interfaces that they're generating. Alright. So I will leave you with this one last bit, for today. Our Directus platform powers production applications for very large brands, around the world.\u003C\u002Fp>\u003Cp>One of these global pizza delivery app, brands is actually handling 9,000 requests per second, sustained. This is not something that you just one shot your way into. You're not going to vibe code your way, in, you know, the hour, the day, the week for these mission critical production scale applications. The front end may be what most people admire this exciting, you know, glamorous thing that we're seeing, very, very recently come out of all these AI app building tools. And it's phenomenal, but the backend is what they trust.\u003C\u002Fp>\u003Cp>So invest accordingly. From me and everyone at Directus, thank you so much and happy building.\u003C\u002Fp>","So let's kick things off. This is Ben, founder and CEO at Directus. And today, I need to talk to you about the end of the UI era. A little sensational, but let's dive in so that I can explain. First off, you might be thinking, don't we have better tools, more people than ever building all these front ends, all these UIs? Yes, absolutely. But when everything is special, nothing is. The front ends have been, commoditized. So let's explore what actually makes software magic, and specifically where the value of software has shifted to and where it's heading next. So since software was created, the interface has been mistaken for the product itself. Users click through, buttons and interfaces and they said, Oh, this is the magic. They saw a dashboard and they said, Oh, that's the application. The Shiny part got the attention for most users. But engineers, you know, for those who are out there, you know better. The visible part of the software is the tip of the iceberg. What lives underneath is the expensive, the fragile part. The part that takes years, decades, to get right. AI has made this a really big deal. Anyone can spin up a decent looking UI, a form, with solid UX, dashboards, components, all of that. They can stitch it together and you can get something that looks just like software. That's amazing. But it's also misleading and maybe even a little dangerous. It creates the illusion that software got easy, but it did not. The front end got easier. The back end maybe even got harder. We have so many builders out there. The back end is the tricky part. And the reason for that is you don't know what you don't know. So while most non engineers can definitely dream up an interface, you are a non technical person. You probably don't know what is needed behind the scenes to actually build this out, make it scalable, make it resilient. So you can Vibe code a UI in a day, but you cannot Vibe code trust. So maybe we just delete the backend. Cursor a little while back actually deleted their headless CMS. As an example. They replaced it in a couple days for a couple $100 worth of tokens. And that really works for a company like theirs. A small team, highly technical, very capable. But that's not going to work for other organizations. You can delete the CMS, but you can't delete the problems that the CMS solves. They come back and when they do, you're either gonna be building from scratch or you're gonna be drowning. So switching gears, let's talk about the back end. You can't one shot your way into production resilience. You can't fake your way into scalable architecture, granular RBAC or access control, reliable APIs, multi tenancy observability, governance rollback strategy, operational maturity. All of this is earned the hard way. Cursor, Lovable, Replit, Bolt and others, they've all made it really easy to create a front end in minutes. And it works. But without a back end, really what you've created is a clickable prototype. The front end and back end distinction gets really blurry because of the excitement around, you know, the generation of this of this front end. And I I love metaphors, so I'll kinda take a a second here. You wouldn't build a house starting with the doorbell, or something like that. You know, the the fresh paint, the beautiful fixtures, the interior design, none of that matters if you've got plumbing, leaking behind the walls, if you've got electricals, you know, sparking fires behind the sheetrock, a foundation that's not even poured and the house is just sinking into the mud. That's just not how things work. Anyone can build these demos, these facades, but far fewer people can build something that actually survives success. You just you can't vibe code trust. So if the front end is becoming free, where does the value go? It goes deeper but not just deeper technically, deeper organizationally. The thing that makes software software wasn't really the pixels. It's not the front end. It's the layer underneath where your whole organization goes, to work together. It's the logic, the APIs, the permissions, the 99.999%, uptime. Your performance under load. You know, does that work? Auditability. Maintainability. The stability that people don't notice until things break. It's not what you see, it's what you feel. You feel when your app goes down. When the data is suddenly corrupted or truncated unexpectedly or deleted. Permissions break. Traffic spikes and things just fall apart. The front end may be what people admire, but the back end again is what people trust. That's what really matters here. So let's first clarify what do we mean by back end. There's the Supabase Lovable partnership and people would refer to Supabase as the backend. It's a database. It's got infrastructure, an API. But really the backend is more than that. The backend is a collaboration layer. It's where technical and non technical users can go to actually access, to browse, manage, and visualize data. It's where data is governed, and used. Not just by engineers, but by anybody in the team. AI accelerates what individuals can do. Our own engineering team uses Cloud Code every day and the velocity gains are very much real. But more complex use cases require humans working together across teams. Engineers with PMs and ops, marketing, legal. AI doesn't solve the collaboration problem. It actually makes it larger. The winners won't be those that can create the prettiest screen the fastest, but those that combine the speed of AI built interfaces with the true discipline of back end engineering. And then democratizing that across the entire organization, technical users and non technical. That's how a hobby project becomes an actual product, or in some cases, how a product becomes an actual company. Okay. So let's break down the back end into three pillars. The first one we'll call governance, how you collaborate safely. The data is there. It's in the database behind every application, that's that's out there. But it's locked behind the doors of IT, as it were, for every report, every change of the data that's needed. If you're non technical, you're probably submitting a support ticket. We need tools that unlock the database for the technical users. Technical users are about 3% of an org, typically. And so we need something like a database administration tool but that's simple, safe, and intuitive enough for anyone to use. It's not about locking things down. It's about opening them up, responsibly. How many one shot AI applications are actually considering RBAC and granular permissions and all that in their system? I would guess not many. So we need to browse, manage, and visualize our data, in an intuitive way. We also need docs, API references, specifications. Those are needed as your team grows, more people join, you need integrations with other services. Those are boilerplate. Let the subject matter experts do their work. We all have seen, you know, a game of telephone and how that can degrade things. You can't pass these requests along. You need people to do that work directly. And so we need interfaces that work for everyone. The second one is scale, or how we take that collaboration and we make it survive the complexity, of the real world. You could also consider that to be production efficiency, resilience. There are a lot of things to consider, consider here. Anyone can build a v one. The problem is when you get to v two, v three, v four, v n, when you go from beta testers out to actual users, in the real world, you bring more people onto your team, internal users. Eventually, you can't just throw hardware at the problem to get by. You actually need to have a proper back end that supports growing your application and whatever it is that you're building. And the third pillar is ownership, or taking all of this and making sure you own what you're building. Again, the value of what you're building is not the front end. That's been commoditized. It's not your cloud infrastructure, your hosting, that's a utility bill, and it's not even really your data that can be scraped, pretty easily these days. So the value is your back end. It's the architecture, the logic and your team, that's, you know, back there moving faster through this collaboration. It's the trust that you create, again, through scale and governance and all of that. It's the value that you unlock from your data with all of this. So you want to pull these systems closer. That's your fence. That's your moat. Self hosting, data sovereignty, open systems. This is where you take control back. So these are the three things that are needed for a proper Backend. But even for the modern organizations that check off all of these boxes, they're still seeing ten, twenty, 30 plus, of these back ends, you know, all over the place. Disparate systems, each with their own API. Maybe it's REST or GraphQL, webhooks. You know, they're all different integrations, different interfaces, different logins. They don't talk to each other. Data is siloed, and so is collaboration. So we're looking at something that has no federation, no unified layer, no consistency and no strategy. And for AI apps, that's even worse. They typically don't even have a backend at all, you know? So here we are. The backend era has begun. So what do we do now? What do we need? We need a unified layer, a single pane of glass that you can put across these disparate systems and bring them together, leaving the data in situ, leaving the data where it is, but federating it and bringing it together. We need to wrap all of that with the granular access control, the rule based permissions, that allow humans and other integrations and agents all alike to have CRUD, to have create, read, update, and delete control, so they're not deleting the data so that you gain more of that trust. You need to bake the tools into this platform that give it the scale and resilience that we were talking about. Not just the API throughput and the data pipelines, but also scaling your team. So again, you need to be able to have everyone working together. And you need to make this a model that is completely yours. So deploying where you need to deploy, whether that's cloud or on prem, you wanna make sure that you are working with an open core, that it's source available. This is the value. So you want to keep all of this really safe. This is actually what we've been building at Directus, and it works. We have 45,000,000 Docker downloads, 35,000 stars on GitHub, You know, over a thousand customers. It started back in 2004, twenty two years ago. My agency in Brooklyn. We were building for Google, Prada, Snapchat, SoulCycled US government, AT and T. And behind every project that we built, there was a database. And we made something for that database that was simple, safe, and intuitive enough for the business user, this collaborative back end. Back then, my team was human, obviously. And we were creating those innovative front ends and now here we are two decades later and it's even more important. Everyone's a builder. Everyone has these interfaces that they're generating. Alright. So I will leave you with this one last bit, for today. Our Directus platform powers production applications for very large brands, around the world. One of these global pizza delivery app, brands is actually handling 9,000 requests per second, sustained. This is not something that you just one shot your way into. You're not going to vibe code your way, in, you know, the hour, the day, the week for these mission critical production scale applications. The front end may be what most people admire this exciting, you know, glamorous thing that we're seeing, very, very recently come out of all these AI app building tools. And it's phenomenal, but the backend is what they trust. So invest accordingly. From me and everyone at Directus, thank you so much and happy building.","d312a30e-6db1-4a83-8dea-ae5c75e3e179",[253],"02c14164-7bd1-470f-aa71-bd2a16d33a86",[],{"year":139,"number":140,"id":141,"episodes":256,"show":257},[143,144,145,146,147,148,123,149],{"title":151,"slug":152},{"id":145,"slug":259,"vimeo_id":260,"description":261,"tile":262,"length":263,"resources":12,"people":12,"episode_number":264,"published":130,"title":265,"video_transcript_html":266,"video_transcript_text":267,"content":12,"seo":12,"status":19,"episode_people":268,"recommendations":272,"season":273},"ai-in-the-enterprise","1176284514","How are enterprises giving teams access to AI while managing governance, data privacy, and risk?","ccb9efaf-5355-49a8-a6b4-5088bd5200a7",45,3,"AI in the Enterprise","\u003Cp>Speaker 0: Excellent. Today, we have another episode of Bridging Bytes. It's been a minute, since we did our last one, but I'm extremely excited, to have two folks here to run through some exciting, topics around AI in the enterprise. So today, I'm joined with, with Holger Hammel, who joins us from HelloFresh. I actually think the intro came from Emma, our VP of marketing, from your time at Ivan.\u003C\u002Fp>\u003Cp>But, really excited to have you here, Holger. And then also Peter Bell, CTO, founder, head, of AI over at Gather dot dev. And I think you're actually a little bit closer, Peter. You're in Absolutely. In New York, in the city?\u003C\u002Fp>\u003Cp>Speaker 1: New York City. Just north of the city in Westchester.\u003C\u002Fp>\u003Cp>Speaker 0: Nice. And, Holger, you're you're over in Berlin. Right?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Correct.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. We have slightly different time zones and, and lighting probably in the background. But, Holger, do you wanna give a quick intro in terms of, like, what you're doing over there at HelloFresh?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I like to. So first, thanks a lot for having me. It's a super interesting topic, of course. And so I'm VP engineering at HelloFresh.\u003C\u002Fp>\u003Cp>I'm leading there the consumer alliance. So it's, like, about three hundred three hundred engineers across different tribes and squads, and we're covering the, client side applications, right, the web and the app page, and first layer of back end, aggregation layer, and data science, data engineering a little bit, and customer care. And, basically, you know HelloFresh. Right? We have, we have, the meal kits in, The US and in European countries, 18 countries overall.\u003C\u002Fp>\u003Cp>And we deliver fresh meals to everybody at home, and we have as well ready to eat meals. And it's a it's an interesting, setup where we have, physical products delivered to to customers, right, really, like, food products with their own kind of challenges in a sense, right, how to do this. And then we have the digital product on top, that we work on. And my key part here is to really, solve for personalization, right, to really create a digital experience that brings the best customer experience. And, and as well, and of course, the topic for today is, like, how to make this very effectively, and kind of make best use of AI internally for engineering departments, but as well of of introducing it into our products, to, for\u003C\u002Fp>\u003Cp>Speaker 0: Love that. Very, very relevant. It's always good to hear, you know, anything on the hardware or the physical side, with what you're doing at HelloFresh. It's that that's the moat these days, in terms of, in terms of AI, and everything that that it's it's gobbling up. Thank you, Holger.\u003C\u002Fp>\u003Cp>Peter, could you maybe tell us a little bit about Gather Dev, and what and everything else that you're you're doing on your side?\u003C\u002Fp>\u003Cp>Speaker 1: Absolutely. I was an IC, wrote software for many years, then I became an engineering leader, CTO of a bunch of startups. I ran engineering at general assembly, built teams up to about 50, so fairly small scale. These days, I'm doing two things. I am writing the book, Scaling AI Adoption and Engineering.\u003C\u002Fp>\u003Cp>Basically, AI for CTOs. And it sure. We're gonna cover software factories and verifications and all the tech, but the hard part is how do you manage stakeholder expectations? How do you do the change management? How do you teach people when the curriculum changes every three days?\u003C\u002Fp>\u003Cp>Like, how do you do that at scale? And so we have these niche in person and online communities for founding CTOs. You're like an individual contributor or a team lead. You got a team of less than 10. Startup CTOs venture back to a scaling from 10 to about a 100, like, how do I hire my first DMs, and how do I do performance management, and how do I create a consistent hiring, process, things like that.\u003C\u002Fp>\u003Cp>And then VPs and CTOs at scale who are running also, like, a 100 up to a couple thousand where it's generally good news is you have the systems in place. Bad news is they are now what is stopping you from getting the work done, that and the people. And so I spend a lot of time talking with engineering leaders about how do you scale adoption, not only what are the good technical practices, but how do you actually do the change management, which will be the hard part.\u003C\u002Fp>\u003Cp>Speaker 0: Love that. Yeah. I mean, that is super relevant for some of the questions we'll get into today. I I, you know, built teams to 50 sounds small maybe, but, that's still where we are, and that's our whole team, over here at Directus. But, and I think you also do CTO hour, right, part of O'Reilly's Yep.\u003C\u002Fp>\u003Cp>Speaker 1: So couple of the things I'll do is I do a quarterly CTO hour for O'Reilly. The most recent one, I got to interview Camille Fournier, the author of Manager's Path, and Ali Adasan, who's the CTO at Dropbox. It was a lot of fun. And then twice a year, I get to, go to KubeCon, the Kubernetes conference. And for CNCF, I get to facilitate the executive summit along with Kelsey Hightower.\u003C\u002Fp>\u003Cp>So that's always a blast.\u003C\u002Fp>\u003Cp>Speaker 0: I love that. Is it KubeCon or KubeCon? Because it seems like Kubernetes, it'd be KubeCon.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. You know what? I don't even know how to pronounce it, but with my accent, people always think I'm saying Q con, which is great, but a whole other thing. I I was at QCon AI recently presented something there, but so I I just try to make it clear, which one I'm talking about because my my computer always gets it wrong for sure when I'm dictating.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Well, say it with confidence, and I guess it doesn't matter too much. So let's kick it off. I think sort of like just table stakes, you know, setting the foundation here. In terms of day to day AI, Holger, maybe you can kick this one off, with your thoughts.\u003C\u002Fp>\u003Cp>Where where is your organization today with AI? Like, what does it actually look like for your employees, for your teams? You know, how's how's that actually operate?\u003C\u002Fp>\u003Cp>Speaker 2: Yes. So I think we are in a crossroad. Right? In a we're in a in a in a in a situation where maybe some of the teams are as well, but, we are, very good, I think, already in adoption of AI on an individual level. So we have all engineers, most of, you know, using some form of AI.\u003C\u002Fp>\u003Cp>We have everybody using some form of AI to kind of improve the documentation, their workflows, and so on. What we recently, what we started, beginning of, the year or a little bit before as introducing our framework for the whole product development life cycle. And that's the interesting part. That's where we wanna go to, right, is, really understanding how we solve for not just cycle time, like, making one engineer faster, but, like, getting the flow for the whole squad to a complete new level. Right?\u003C\u002Fp>\u003Cp>It doesn't really make only sense to have one engineer being four times faster. Right? Not bad, but it's not enough. Right? And I think we're in the middle of we we as organization understood this.\u003C\u002Fp>\u003Cp>We are in the middle of transforming this. I really look at this as an AI transformation. We had DevOps transformation. We have AI transformation. Now we are in the in all of that on steroids, right, which is the AI transformation, and really trying to understand the cultural, the technical, aspects of this is something we're in the middle of right now.\u003C\u002Fp>\u003Cp>And it's something. Right? So you see engineers having, like, tons of agents running. You have designers creating pull requests. All of this is happening.\u003C\u002Fp>\u003Cp>Right? Just really bringing it together and super effective, on scale is where we are at and trying to solve for.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Is there is there a specific team or department that was know, doing something different than you expected in terms of, you know, that day to day? You know, may either moving faster, maybe they're moving slower, or just kind of a different different vibe altogether?\u003C\u002Fp>\u003Cp>Speaker 2: I think it was what what was striking was that we have a few, like, smaller businesses that, basically, we have, like, HelloFresh and and Factor, the established big businesses, if you want. And then we have smaller, separately managed, startups, kind of. Right? So GoodJob and Pets Table. And they are organized a bit more lean in a sense.\u003C\u002Fp>\u003Cp>Right? They kind of kept the startup vibe, and they you saw picking up. They saw we saw them picking up very fast. Right? Because it's just out of pure necessity in a sense.\u003C\u002Fp>\u003Cp>Right? So resources are scarce. So to get anything done, right, and they couldn't ask for, like, five more engineers, and they had to solve it. And they start solving it with AI, and they went very fast. Right?\u003C\u002Fp>\u003Cp>And they were the first one adopting it. And there was maybe less red band as well. Right? So they just, you know, did it. Right?\u003C\u002Fp>\u003Cp>And so they kind of act as the as the template or as the as the as the leaders down. Right? And where we got the inspire the whole organization are inspired by that speed and the that results.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I mean, that's a huge network effect to be able to inspire other teams, you know, by seeing how fast that you can move and, like, maybe be more efficient. Obviously, that startup mentality is huge right now. We're seeing a lot of, you know, constriction across headcount in different orgs, you know, huge orgs that are saying like, oh, we can not just do things more efficiently, but, let's kind of adopt this more agile, you know, startup way of thinking. I think that's good across the board, you know, and, of course, sort of, sped up by by AI.\u003C\u002Fp>\u003Cp>Speaker 2: And the interesting thought I was just want to mention is really is the the constraints. Right? So the the the constraints of limited resources and still an ambitious motivated team that wants to run this, this was a key cut cut cultural aspect. Right? Because if you then have a big organization, very distributed, decentralized, like everybody does their part, this might not be there.\u003C\u002Fp>\u003Cp>Right? And so thinking about how to create this urgency, is a big part of that transformation, I feel.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. I mean, the only you have these, like, enormous organizations, and it's almost like you you can only shrink the overall headcount so much. Like, how do you actually get that startup speed when you have hundreds, thousands, tens of thousands plus of, employees? And I think, you you know, that sort of had you eat an elephant, you know, one bite at a time.\u003C\u002Fp>\u003Cp>If you kind of break up your teams and kind of make them very autonomous and give them that that skill individually, they can operate like many different, smaller agencies or smaller smaller, startups within a larger org. That's I love that. Peter, similar question to you. I guess, you know, what what does that AI look like day to day for you? And maybe more specifically, like, how has that changed, you know, over the past year or so?\u003C\u002Fp>\u003Cp>Speaker 1: So for me personally, it's a very different thing. As an individual, I'm a solopreneur, and it's great. I have a team of eight named agents running twenty four seven. I built my own little orchestrator. I'm creating my own custom memory system, which, puts skills.\u003C\u002Fp>\u003Cp>It puts, prompts and agents into a GitHub repo, and some high value shared informally structured context also goes there. Everything else goes into a Postgres database, which allows me to have things like I can have agents doing message passing through the database so that I can have multiple agents collaborating without having to go through a pull push cycle that I would have to if all of my tickets or work to be done was in some kind of Git based system. So personally, it's great. I'm just starting to dig into software generation. I'm looking to spin up in the next two weeks my own clone of monday.com so that I can basically have an interface for playbooks for running the business and projects for either running experiments, building new playbooks, or incrementally improving playbooks.\u003C\u002Fp>\u003Cp>And the idea is deterministic workflows with small model and human of the loop steps. So that way I can firstly reduce the cost latency of the models by the like, I just need a simple classifier. I don't need to be running Opus four six to do that. And then on the other side, for the human in the loop steps, every single interaction is captured in a fresh session. So we keep them in the smart zone, very small percentage of context used, and it means that then I run a compounding loop so that every night, every agent reviews what it did and updates its own instructions and skills in a way that it would be able to do it a little better the next day.\u003C\u002Fp>\u003Cp>And I'm already this system I'm building, I I started two weeks ago, and it's already running the entire business. And now I'm digging into software factories and the verification and looking at the Dan Shapiro, looking at what OpenAI did internally, looking at what Harper Reid is talking about about building rich software factories. When I see people who are blessed and cursed with 200 or 500 or 2,000 engineers, it's a slightly different story. The first thing I see is that, the widespread thing we're still seeing is augmented rather than agentic development, cursor in IDE mode or Copilot. It's fine.\u003C\u002Fp>\u003Cp>And if you wanna be 30% quicker doing 10% of your job, which is actually writing the code, you're gonna get some incremental improvement there. I think the next step to go is agentic. So maybe you've got six or eight small agents doing that you trust to write code or review code. That's fine too, and that you know, maybe you can be one and a half, two x as fast depending upon the code base, how well you prompt it, how much context you provide it. It starts to get interesting then you need to compound.\u003C\u002Fp>\u003Cp>I see people saying, oh, you know, like, I keep trying to do this, and it creates bad code. I'm like, have you told it what good looks like? Have you told it to remember what good looks like? And then if there's too much context where you're blowing the entire context window with all the rules, have you decomposed it so that you have one agent writing the code, one reviewing it? Maybe you have one reviewer that's looking for cyclomatic complexity, quality of naming, and architecture, and another one that's looking for security because you don't want one agent to have to solve for seven different dimensions in a single pass.\u003C\u002Fp>\u003Cp>It starts to get dumb. And once you so that then becomes interesting both on an individual level, even I'm getting compounding out of my small, agentic army. But once you start to do interesting things, you generally have a DevEx or platform team that owns the repo with the skills, agents, and prompts, and the key context, what you then do is you have this mechanism where other people within the org can fork that, tweak it, try to make it better, and then if, say, three people in the org give it a thumbs up, you then say, okay. This now gets owned by the head of AI, the platform team, the DevX team, and then you can share good practices across the organization without a priority knowing what the good practices are gonna be. Because Hochul is absolutely right.\u003C\u002Fp>\u003Cp>This is exactly the same as a cloud migration. You don't just give people a book on Kubernetes and say, are we there yet? Right? You actually want to have a program manager, and you want to have dashboards, and you want to have KPIs, and you want to have lunch and learns, and you want to have training sessions and Slack channels. You want to elevate people in your in your all hands.\u003C\u002Fp>\u003Cp>But at the same time, while you're doing all of that stuff, the curriculum changes every day, so you actually need to have a bottom up mechanism to get the best ideas from whenever they come. The one other thing I would say is, create a little bit of space for solo players. You can run way faster with this stuff solo. And if you have a small number of fire breathers, especially if they're building something that's not it's not the main way you charge all your customers. Right?\u003C\u002Fp>\u003Cp>This is the admin dashboard or this is the internal dev tooling where it goes down for an hour. It's not the worst thing. Those people will learn the practices that can then broadly be shared to the majority of your team. There's three buckets, and I promise to stop talking. The first bucket is the people who can't wait to do this more.\u003C\u002Fp>\u003Cp>They play with Steve Yegi's Gastown on the weekend. They are killing it. They're like, if I don't have 60 agents running, if I'm not blowing through 12 max plans at $200 each, personally, I'm not doing it right. Then there's the vast then there's some people who are honestly like, this is destroying the planet. This is destroying my job.\u003C\u002Fp>\u003Cp>This is miserable. I shall never do this. AI doesn't know how to write code as well as I do because I'm a Java developer. And they're gonna have a hard time. Hopefully, they will get AI infected like we got test infected for TDD.\u003C\u002Fp>\u003Cp>And the vast majority of people are somewhere in the middle saying, dude, I would do this, but you're telling me to read 50 blogs. Don't I have, like, features to shit? Tell me how to do it, and I'll give it a shot. Yeah. And you need to figure out how you deal with each one of those populations.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. No. And that's that's huge. And it's interesting, you know, hearing about, you know, minimum spend, like, you know, on on AI. Like, is that a motivator?\u003C\u002Fp>\u003Cp>You know, you just kinda say, like, you're not spending enough. It's also interesting, you know, hearing the comparison of, like, a small team, a big org down to small teams or solopreneur. You know, at the end of the day, you can ramp up the the minions as it were, and get the the team that you need quickly. But you mentioned sort of like piping your data into a database, you know, into Postgres. Like, whether that's you doing that with with the minions or it's a bunch of teams, I think that kind of bridges into the next the next topic pretty well, which is either way, we're connecting data up to these these services, up to the tools that we're building, whether it's internal and maybe, like you said, it goes down for a second, Peter, and maybe that's not the end of the world.\u003C\u002Fp>\u003Cp>You know, maybe it is, depending on the usage. But in terms of governance, like, how how do we find that balance? Holger, I'm gonna throw this back over to you. When we think about people moving fast, you know, getting that agility, we want people building things and experimenting, optimizing their workflows. How do you think about guardrails?\u003C\u002Fp>\u003Cp>Because, obviously, you know, people are just kind of out there, you know, building, but, you know, data is is crucial. Data is the backbone of all of this. And if that gets leaked, if that, you know, isn't know, given given the proper RBAC or permissions, in these quick systems that are being built, you know, how do you how do you speak to that across your org?\u003C\u002Fp>\u003Cp>Speaker 2: That's a very interesting question. And there's probably no final answer, but I think one, of course, we we have a high responsibility for for the data of our customers and our employees, and that's not gonna be sacrificed or, like, a change. Right? So we need central governance for for data, and we have it. So it's basically centralized through the existing teams that we have, like security or data privacy teams, reliability teams, ops teams.\u003C\u002Fp>\u003Cp>And we have a central centralized Gen AI team, basically, infrastructure team that kind of, is owning owning those things. Having said this, traditionally, you have guard rates around cost or maybe, I don't know, who can who can access to this. And and this, we we deliberately said we wanna, while protecting all the all the, PII data and relevant data very clearly, we want to open up basically everything else and and make people just try it out. Like, remove every red tape that we can. Right?\u003C\u002Fp>\u003Cp>And I think, you know, we were not looking at, should we have Corsa and Cloud Code and Gemini and whatnot. Right? We just take it all, cut it out, and see what sticks. Right? And if there's a new kid on the blog, we probably take this on as well and then see later, how we decommission it.\u003C\u002Fp>\u003Cp>It's just more important to get people excited, to get people working, and then, there will be a time for consolidation. And, you know, it's it's kind of ongoing. It's ready. We know more about, like, how how to manage context, you know, how to have a memory that is not, tool dependent and agnostic and stuff like that. So that helps.\u003C\u002Fp>\u003Cp>But I think that's the key part here. Right? It's removing red tape where we can while, while protecting the the data that we have to.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I mean and there's also I mean, shadow IT, obviously, and now we have, you know, shadow AI. Is that an issue for you? Like, people just bypassing, like, you know, the the research shows that, you know, when leadership is actually shaping all of this, AI governance, you're getting better value for your company. But you're still gonna have your ICs, your, you know, pretty much anybody go out and say, I'm gonna use my personal account, my personal service.\u003C\u002Fp>\u003Cp>You know, is what is your solution to that? You know, is that that that's, like, sort of a sidecar risk. You know, do you just lock that down? You just make it easier to use the approved, like, internal services? Or, you know, how do you avoid that sort of issue where people are using things that just aren't even approved and they're just kinda going rogue for better or worse?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. So the honest answer is you cannot fully fully, kind of mitigate this, I would say. Right? And I think, I think we have a very compelling offer for people. Right?\u003C\u002Fp>\u003Cp>The you know, you have, you have, plans for for, for Copilot, for Gemini. There are. Right? So I think, if people now still choose to copy paste something in their own, Chativity, you cannot really, do much about it, I guess. But I think it's it's about training.\u003C\u002Fp>\u003Cp>Right? Kind of, we have a policy for for JNI. Right? We very early on had a had a policy and a got got guidelines, of course. Right?\u003C\u002Fp>\u003Cp>So that that people understand this. And I think especially if you're not in engineering, we want everybody to become a builder. Right? But not everybody in outside of engineering might have the context and might be security, sensitive also. So I think it's a big part of, like, training and making people aware of the risks and, what IP means, and, you know, what are the difference between private and enterprise accounts and stuff like that.\u003C\u002Fp>\u003Cp>So that is very, very important. But I think we tend to be more encouraging of using the tools than kind of limiting for now.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Well, Well, I would hope that everybody's being security sensitive. You know, that's that's obviously the name of the game. It it only takes one incident, one issue, and everything goes down quickly.\u003C\u002Fp>\u003Cp>Speaker 2: Wonderful.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Peter, I guess, you know, different different sort of, route to, you know, think about this. But, you know, are you doing are you kind of baking that into your process and how you're thinking of you know, if if we're saving data into a database, are you just piping it straight in? Are you piping it straight out? Is there are there any sort of things that you're thinking of when you're you're building out these these systems?\u003C\u002Fp>\u003Cp>Speaker 1: So one of the things I like is that, in many ways, I can build, an engineering infrastructure that feels like a a bigger company. And in fact, I need to. I find that the guidance, the onboarding, the design systems, the less decisions an agent is allowed to make, the more processing it can bring to those decisions, and the less likely is it it is to make random bad ones. I I was just chatting with a a few CTOs running larger teams at breakfast yesterday, and one of the common threads was how can we go more heavily into design systems, into standard patterns, into processes, into minimizing the number of decent technical decisions that need to be made so that the agents don't get don't don't have too too much space to keep getting it wrong. So I I think, managing the number of decisions for the agents, I think being very thoughtful about data, you need to think through I think we're gonna see a lot more about agentic roles and permissions.\u003C\u002Fp>\u003Cp>It turns out that if you just have a prompt that says never, never, never merge your work domain, It works almost all the time. If, however, you literally, I know somebody who has 50 agents and each one has their own GitHub account. And the nice thing is that you just put branch based permissions on main, and they are unable to merge it in until either a human or some other agent has done it. So I think you need to be very thoughtful. Don't assume that the agents will do what you say or even something reasonably close to what you say and have the guardrails.\u003C\u002Fp>\u003Cp>And I think it also comes back to all the classic good engineering practices. You know, you should decouple release from production by using feature flags so that you can canary rollout. You can load test things. You can roll things back. All of these are important.\u003C\u002Fp>\u003Cp>And I think to the biggest story is, like, whether it's shadow IT or, like, how you use this, I recommend picking a lane. If you think about it, technology adoption life cycle, crossing the chasm is like a 30 year old book, and it's still true. You can have innovators, early adopters, early late majority, and laggard. And, you know, that's okay. If you run a bunch of gyms, if you run grocery stores like physical plant, if you run ski resorts, maybe you can just wait till Microsoft figures it out and just tell everyone to go use Copilot.\u003C\u002Fp>\u003Cp>And in a year or two, you'll be a little bit faster. That's actually perfectly okay. You're gonna lose anybody who wants to work with AI, so it's gonna be a negative selection in terms of the team you get, but you're gonna keep most of the people who know how your systems work, and it'll keep them happy. And shadow IT is gonna be a problem for a while, but, honestly, all the people who wanna use shadow AI are gonna leave your company anyway. So it just is what it is.\u003C\u002Fp>\u003Cp>And then you can go to the other extreme. You can be like a Toby at Shopify. Right? You can be like, hey. Not only do I want to say we're AI first very early on, there was one time where he got the head of his, AI team to say, I want the 20 people using the most tokens.\u003C\u002Fp>\u003Cp>Promote them. Is that a good business decision where those tokens being used usefully? Doesn't matter. That is a cultural concept that that creates a sense of we want to be innovators. We want the people who wanna be innovators, and that's what we wanna attract.\u003C\u002Fp>\u003Cp>And if that costs, you know, 500 k in bonuses, it was totally worth it. So I think you need to figure out where you play on that. But then even if you're an f aider or early adopter, you still need education and enablement. Make it as easy as possible, like Holger said, for them to use. If you're just like, what do you mean we do AI?\u003C\u002Fp>\u003Cp>We have Copilot licenses, and you can request one. You're gonna get a lot of shadow AI and lose a lot of great people.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: If you work hard but have reasonable red lines that explain why sending PII to service in China may not be in your customers or your business's best interest, That kinda makes sense. And providing you teach people what's going on and especially for the nontechnical folks in the org, that you give them an understanding of why the rules exist and good ways to be a good corporate citizen and still actually get stuff done.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Oh, absolutely. It's interesting thing. I've never even heard of, you know, just finding who's using the most tokens and promote them. Like, I can imagine people starting to use their agents just to run other agents just to ramp up the token spend.\u003C\u002Fp>\u003Cp>Over here, you know, building AI into our platform, it's all about, you know, how do we optimize this and get the tokens down. But I guess different different strokes for different folks. You had mentioned sort of nightly builds and sort of feature gating, you know, behind the flag. That's sort of kinda leads us into the next topic here, which is, you know, we we're seeing a million apps, you know, flood the market. We're seeing all these cool POCs and pilots and experimental, you know, things internal and external.\u003C\u002Fp>\u003Cp>Like, what what gets that into production? Like, what makes that a viable application or something that, you know, you kinda mentioned it works almost all the time, you know, and, you know, we're gonna wrap it in the secure prompt, like, never ever ever do this, and it still does it, you know, on occasion. That doesn't fly in production when you're dealing with mission critical systems, when you're, you know, really building for the customer, and externally. Beyond sort of, like, the the point that you mentioned, Peter, maybe you can kinda is there anything else that you think is is critical? Like, we talked on governance and sort of maybe permissions.\u003C\u002Fp>\u003Cp>But what else helps get you to that production scale and resilience?\u003C\u002Fp>\u003Cp>Speaker 1: So there's a lot of things. A really good starting point is to remember we already have nondeterministic systems building software. They're called humans. There's just slightly different parameters when you deal with these new nondeterministic systems that are building software. It's it's an intern, and by the time you get to the end of the context window, it's an intern who's been on Adderall for two nights and is starting to forget stuff.\u003C\u002Fp>\u003Cp>So you have to be thoughtful about how you engage with your guardrails. My assumption that to take the extreme example is that my only job is to provide training for my future robot overlords. Do I believe that's true? Do I believe there's no rule for humanity? Hopefully not.\u003C\u002Fp>\u003Cp>But if I use that as an operating assumption, then there is no part of there's no time that I interact with an agent that I'm not doing it in a thoughtful way to improve their ability to give me a better outcome next time. So the first thing is you just rinse and repeat. You, like, you wanna build an entire software factory, right, where it goes from you just say the vision and it's in production with no human no required human gates between the two. All you do is you tell an agent to write some code, you tell it what was wrong, you make sure that it captures that context, and you tell it to go again. And you keep repeating that loop time and time and time again until you build more and more validators, adversarial reviews, and other elements that will reduce the likelihood.\u003C\u002Fp>\u003Cp>And you know what? I've taken that in production database in my life. Like, humans get it wrong too, but it's much less likely the more revs you do around that cycle to figure out what does good mean, what does bad mean, and how do we put, both tooling and and also just prompts. You've got the prompts, so you've got the tooling, you've got the context, but also you do need, I think, permissioning systems. You need to think about the blast radius.\u003C\u002Fp>\u003Cp>And then all the good stuff we've always been doing, observability, monitoring, alerting, I think there's great opportunities now for or self healing software is a fancy word for incident response where the LLM knows enough about the system to propose a a move forward. I think that's going to become mainstream. And, also, just stuff that we're doing fifteen years ago, game daying, resilience engineering. How can you make it so if your cart goes down, at least you can still see the products? If your payment provider goes down, it can still save your cart and send you an email once it goes up.\u003C\u002Fp>\u003Cp>Assume that stuff's going to break more and create more resilient systems with less moving pieces so that even when things go wrong, the blast radius is constrained.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: One final thing I'd point is and the only other part is you can also we're not gonna review every line of code at some point that's going away. However, you can take a statistical process control approach to that, which is it's like ball bearings. You don't check everyone, but but if one of them is out of tolerance, you start checking them more frequently. And you do it risk adjusted, you're probably gonna test a smaller percentage of the PRs for your admin dashboard than you are for your main financial production flow.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That's that's amazing. I I love that. It's interesting. You kinda bring up sort of like, oh, when this stops working, you know, you have this, like, graceful degradation, process.\u003C\u002Fp>\u003Cp>I my first thought is, you know, you don't know what you don't know. And you have these sort of non engineers building out systems, and the system might work. But if they don't know to prompt in, you know, hey, let's let's make sure that we have this, you know, progressive enhancement, graceful degradation, then that just won't be included there, which is which is interesting to think of. Like, the creator of, you know, the software still needs to be architecturally aware of, you know, software engine, design and architecture.\u003C\u002Fp>\u003Cp>Speaker 1: Two possible wonders I've seen for that. One model which I think is is good for now, which is I think we're going to see, you know, the two pizza team is not always going to be six to eight people now. I think you're gonna find triads in groups of four being able to get a lot of move a very long way and very fast when you have teams of twenty, fifty, 60 agents working with them. But there's going to be a combination of product and engineering skills. Somebody needs to know what we're building and why.\u003C\u002Fp>\u003Cp>Somebody needs to be thoughtful about, wait a second, latency, queuing, retime, split brain problems, all this stuff we deal with with large distributed systems. Those awarenesses need to exist. And then you might have some design skills or some data skills depending upon where the group is in your org and what it's trying to do. So I think in the early days, you make sure there's an engineer in the room before something you care about goes to production. I think, eventually, if you build a sufficiently rich software factory, you build those architectural concepts into the review process.\u003C\u002Fp>\u003Cp>Whether we get there and how close we get there, I don't know. So for now, I'm gonna keep a staff plus engineer having to look over the code just to be sure.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. How and how quickly we get there. Exactly. Holger, I'm I'm really curious to hear your sort of, like, take on this. You know?\u003C\u002Fp>\u003Cp>Obviously, Peter mentioned a lot of things that do get sort of these pilots up into production. But, you know, our platform direct us, for instance, you know, we power, you know, mission critical software where we're pumping out, you know, bandwidth at, like, nine nine thousand pizzas per second, you know, for for certain customers or whatever applications they might be, building. And it's funny to me thinking of, like, that that is, like, production grade, like, at scale. You can't just one shot, you know, or Vibe code your way to that. Like, there's there's a process.\u003C\u002Fp>\u003Cp>So I'm curious, you know, when you take sort of that equivalent at, you know, HelloFresh, like, what are you what are you doing? What do you layer on top to make sure, that your applications, you know, if you're if you're building in that way, you know, through AI, that it can handle that.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. That's a fantastic question. I think we, so so some some of the stuff, you mentioned, we do have to be one of the things, like, with the higher throughput, right, of of agentic AI or just, you know, teams speeding up. We see more, pull request reviews coming up with a new problem. Right?\u003C\u002Fp>\u003Cp>So so we need to find more, ways of, I think it's a really good idea, right, to kind of, find another abstraction level, basically, to look at, you know, risk management and and reviews and and and in a sense. Right? So that's quite interesting. So I think there we need to invest more. Right?\u003C\u002Fp>\u003Cp>This is something we need to do. What we have been doing is we have brought a few things to production, like AI products to production, which is kind of interesting. So we've been, maybe the most obvious case. We did go first as well. This is like customer care and having chatbots right there and experimenting there, a lot.\u003C\u002Fp>\u003Cp>And there, it's it's really it was we were kind of mid mid class last year or so. Right? So we were learning how to do this as we go. Right? And then managing, basically, the technology was not so much of the problem.\u003C\u002Fp>\u003Cp>It's more like managing the, really the the quality of it. Right? So all the different ways of of of how how a conversation with a human, the one deterministic, entity, right, can go, is, is, you know, not to underestimate. Right? And there's a lot of things that can go go wrong and how to how to design communication, the getting the intent right, and and keeping it safe, right, and and correct, is an is an ongoing task.\u003C\u002Fp>\u003Cp>And we, as we invested into, you know, the teams, that build up this messaging and then as well, the the automatic test. We experiment with AI as judged. Right? So, basically, having LLMs checking those those those flows as well. I think this this is very, very promising.\u003C\u002Fp>\u003Cp>Right? So this is one pattern that we see more is that you'll introduce, AI, maybe even different models, either for pull request reviews, having iterations on them, basically watching the manual testing themselves. Right? Like, having an eye look at, at how how it looks like and then doing pull requests by themselves. This actually works pretty, pretty well and has a very has a very high showed very high impact.\u003C\u002Fp>\u003Cp>So these are a few examples of where I can see. Right? So I think my my main concern right now is really getting the the the review part out of the way and and and on the other side, getting more, I don't know, engaging more UX and so on to create more experiments. So that's the other thing. I would like to have more, more variants basically tested earlier with the idea of having just already a more winning variant, basically, created into production, have higher higher signal, higher, success with more expensive AB testing in production.\u003C\u002Fp>\u003Cp>But why not having, like, 10 we we see this already, but I think we can do more 10 variations, test the prototypes, high high fidelity, bring it into the building, having tested before or with synthetic audiences. Right? Checking out quantitative analysis even on on scale if a business model works with a new hypothesis. So these are the things we're currently on and want to invest more into. So to get it on the front.\u003C\u002Fp>\u003Cp>Right? So getting better better signals earlier in the process, having the winning variance going to production faster and solving the PR revenue bottleneck problem. There are a few topics that we're currently working on. And then a few other things about personalization, improving the models, and as well creation from automatic creation of videos and and images, and we're creating a lot of, like, meals. And we have some AI support tooling that helps menu chefs, right, the creators of recipes to improve the process of getting this from idea to production, significantly faster than before.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Well, it it that's I I love that. How how big is your org, Holger?\u003C\u002Fp>\u003Cp>Speaker 2: So my org is about 300, people right now. The overall HelloTech org. So how this is how we call this either, combination of all, like, tech technical staff, if you want, is, is about 1,000 people.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And I I it's interesting. Like, all this only happens if we actually you know, the governance, you know, getting to production, you still have to have the people to make this happen. You know, going back to early, like, we can't just make the agents have the other agents work. Like, there's still human in a loop as Peter keeps putting it.\u003C\u002Fp>\u003Cp>It. You know, we think of, you know, Jack Dorsey and and kinda what happened over at Square. Like, switching to, like, the people side of this, you know, with an org of that size, like, how do you actually get the workforce to adopt, to use, to, you know, to actually have this happen in the first place. You know? Is I'm I'm assuming there's a fear piece.\u003C\u002Fp>\u003Cp>You know, we've talked a little bit about, I think, Peter, you had mentioned sort of building your for the future AI overlords. You know? There's a perception, I think, across some people that, like, oh, am I just digging my own grave and sort of, like, going out and and sort of building these these systems? But, you know, we have to lean in. You know, we can't just ignore it.\u003C\u002Fp>\u003Cp>So, you know, Holger, within your org, like, how does that work? Like, how do you make that happen?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. So I like to to frame it in a way that, I think HelloFresh is a really great way to experiment with this, learn this, apply it on scale, and just make yourself, stay relevant if you want. Right? And and be on on on top of the game there. Right?\u003C\u002Fp>\u003Cp>So we we really try we we try to make this work. We support people. We support the teams. And that's kind of the positive way of framing it on if you wanna play it a bit differently. If you don't do this, right, the gap between, what the what the what the industry demands or what where the where the top level is and and everybody else, it's getting bigger very fast.\u003C\u002Fp>\u003Cp>Right? So you you you risk of falling behind in a sense, and that's not limited to engineering at all at all. Right? It's like every function and software engineering in and around it at least is, has the same kind of, opportunities and and challenges in a sense. But I don't, like, underestimate the, those factors.\u003C\u002Fp>\u003Cp>They are they are real. Right? So and I think there's a system of incentivizing, helping people, making it, you know, as we talked about it, easy to figure to try out things. Right? But then as well, set up clear expectations, right, that, that we expect every team to onboard into the new process and to to adopt to this agentic AI, for instance, use cases or to to have more variance in production now that we can do it, right, fairly easily.\u003C\u002Fp>\u003Cp>So there will be, the expectations grow steadily as well, to keep up with this. So that's the both of the side. Right? Enabling people, but as well-being very clear about expectations.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Oh, 100%. And it did like, Deloitte's latest report, I think, said something like only 20% of companies of orgs, actually, their their teams are actually ready, and prepared for AI. So, you know, getting that number up, hopefully, HelloFresh, you know, is in that 20%. But, but we're come we're coming up on time here.\u003C\u002Fp>\u003Cp>So I wanna kind of at least be a little bit more forward looking, with with sort of wrapping up here in terms of what's next. Peter, maybe I'll send it over to you. If you're looking out the next, like, say, one, one and a half years, like, where do you think the most change is gonna happen within AI? With an enterprise AI? I I think you go anything beyond that.\u003C\u002Fp>\u003Cp>I mean, even if you go out six months, the question mark start piling up so high. But if you were just kinda, you know, behind the sky, where do you think things are are heading?\u003C\u002Fp>\u003Cp>Speaker 1: Within the engineering org, I think it's pretty straight this will be I'm not gonna put a timeline on it. I've got a very good friend, senior CTO. He's like, you can make whatever prognostications prognostications, but don't put a timeline on it. You you'll thank me later. And I I think he's right.\u003C\u002Fp>\u003Cp>I have no idea. It will be much faster at smaller companies in greenfield environments, in places where you happen to have, an engineer that's just super passionate about this and pulls the team forward. It should happen more quickly in SaaS companies and in companies that are potentially have an existential threat from AI. As I said, you're on a chain of gyms, maybe it doesn't matter. So there's going to be uneven, speed of change, but I think, eventually, we're going to see a flattening.\u003C\u002Fp>\u003Cp>What the year of efficiency continues to resonate many years after. You could imagine a world where you have triads or maybe teams of three to four doing something similar to what a two pizza team does now so you can split them out and have more of them capturing more initiatives. I I think it's possible you might have five to 15 of those reporting straight up to a senior director or a VP where you have a team lead in each. I think we're gonna continue to see the collapsing of that, and I think the VP is gonna be spending a lot of time on the keyboard reviewing detailed specifications, verifications, harness improvements, as well as research and product elements. So I think we're going to see leaner engineering orgs.\u003C\u002Fp>\u003Cp>The good news is we need a 100 x the software, and, I think there are lots of opportunities for anybody who is passionate about either. And I think we need to be happy with this. Right now, we're all excited about AI engineers. And for me, that's basically harness engineers, people who are thinking about prompts and verifications and steps and gates. We need 8% of those.\u003C\u002Fp>\u003Cp>That's our platform or DevX team. Everyone else is going to become a product engineer who gets better at proposing experiments, writing specifications, and creating rich verifications while also being thoughtful architecturally. And I think those are the two roles that are gonna continue to grow and be incredibly impactful in the future.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Oh, I love that. Slightly different lens, hold on. I wanna hear sort of your your sort of take on where things are heading. But, on the enterprise, something that's very sort of near and dear to my, my heart, my job is sort of the commodification of the front end.\u003C\u002Fp>\u003Cp>Like, we're we're seeing that happen actively with the AI app builders. The value of software that's being built, where where does that value shift? As as the front end, as the facade becomes commoditized and and everybody can build that, where does it shift? I mean, down the stack to, you know, all the way back to the database or somewhere in between. Where do you feel, that's that's heading over the next year or so?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. It's a it's a it's a very good question. So basically, it's, I I still think, a a creativity and and being able to create a mode, the connection to your customers, building up this relationship is probably more important than ever. So you can be if you double speed on the wrong side of the highway, right, it it will not help you necessarily. So, so and I think that the the productivity gains that we have, that we're seeing now and that are super exciting, they will become a commodity at some point.\u003C\u002Fp>\u003Cp>I'm not putting a timeline on it either. But, you know, you you will have your great engineering teams, and product engineering teams. They will they will work on a completely different level. You still need to figure out what really works for a customer. And now the levels the field is, we're closer.\u003C\u002Fp>\u003Cp>Right? So I cannot rely on, say, a company like HelloFresh being faster, to production with some features than a small start up. So I need to be really, really smart about this. Right? I need to be have the most creative people writing the best specs.\u003C\u002Fp>\u003Cp>Right? So and, we have a lot of customers. We have a lot of customer relationship that is that is super valuable. That is not easy to copy, right, even with AI. So building on top of this and making sure that it stays this way and we stay on top of the innovation, that is what, I think, what still matters the most.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I mean, bring it back to the people, you know, the as a designer, you know, focusing on the creative. You know, you can always it's really exciting right now because AI is able to take, you know, this massive context and, like, pipe it in and make everybody creative, make everyone a developer. But at the end of the day, you know, you have to be able to break outside of, like, what's already there and sort of just rearranging those things. So I love hearing the answer kind of always coming back to the human, to the core of, you know, creativity and, you know, even going back to earlier.\u003C\u002Fp>\u003Cp>You know, you can throw the the kill switch in there. You can do all the right things and still, AI will kinda find its way to work around it. So, it's really important to have people that understand the the proper architecture of of what we're building and and why those things are important. So, hopefully, we don't stray too far from from those first principles. Holger, Peter, it was really, really exciting, getting to just chat through, you know, and the enterprise with both of you.\u003C\u002Fp>\u003Cp>You know, hopefully, we'll get to chat again soon, maybe again on a bridging bites episode. But for now, anything that you guys wanted to sign off with before we wrap this episode up?\u003C\u002Fp>\u003Cp>Speaker 2: No. Just thanks a lot for for for having me. And, yeah. It was a pleasure talking to you. And, it's just exciting times, I must say.\u003C\u002Fp>\u003Cp>Speaker 1: Likewise, Ben. Thank you so much for the invite, Holger. It was wonderful to hear your insights. So much fun and great conversations. Lots for all of us to learn.\u003C\u002Fp>\u003Cp>Speaker 0: Awesome. Yeah. Well, absolutely. Thank you both for being here, and we'll see everybody, soon on the next episode. Alright.\u003C\u002Fp>\u003Cp>Thanks, guys.\u003C\u002Fp>","Excellent. Today, we have another episode of Bridging Bytes. It's been a minute, since we did our last one, but I'm extremely excited, to have two folks here to run through some exciting, topics around AI in the enterprise. So today, I'm joined with, with Holger Hammel, who joins us from HelloFresh. I actually think the intro came from Emma, our VP of marketing, from your time at Ivan. But, really excited to have you here, Holger. And then also Peter Bell, CTO, founder, head, of AI over at Gather dot dev. And I think you're actually a little bit closer, Peter. You're in Absolutely. In New York, in the city? New York City. Just north of the city in Westchester. Nice. And, Holger, you're you're over in Berlin. Right? Yeah. Correct. Yeah. We have slightly different time zones and, and lighting probably in the background. But, Holger, do you wanna give a quick intro in terms of, like, what you're doing over there at HelloFresh? Yeah. I like to. So first, thanks a lot for having me. It's a super interesting topic, of course. And so I'm VP engineering at HelloFresh. I'm leading there the consumer alliance. So it's, like, about three hundred three hundred engineers across different tribes and squads, and we're covering the, client side applications, right, the web and the app page, and first layer of back end, aggregation layer, and data science, data engineering a little bit, and customer care. And, basically, you know HelloFresh. Right? We have, we have, the meal kits in, The US and in European countries, 18 countries overall. And we deliver fresh meals to everybody at home, and we have as well ready to eat meals. And it's a it's an interesting, setup where we have, physical products delivered to to customers, right, really, like, food products with their own kind of challenges in a sense, right, how to do this. And then we have the digital product on top, that we work on. And my key part here is to really, solve for personalization, right, to really create a digital experience that brings the best customer experience. And, and as well, and of course, the topic for today is, like, how to make this very effectively, and kind of make best use of AI internally for engineering departments, but as well of of introducing it into our products, to, for Love that. Very, very relevant. It's always good to hear, you know, anything on the hardware or the physical side, with what you're doing at HelloFresh. It's that that's the moat these days, in terms of, in terms of AI, and everything that that it's it's gobbling up. Thank you, Holger. Peter, could you maybe tell us a little bit about Gather Dev, and what and everything else that you're you're doing on your side? Absolutely. I was an IC, wrote software for many years, then I became an engineering leader, CTO of a bunch of startups. I ran engineering at general assembly, built teams up to about 50, so fairly small scale. These days, I'm doing two things. I am writing the book, Scaling AI Adoption and Engineering. Basically, AI for CTOs. And it sure. We're gonna cover software factories and verifications and all the tech, but the hard part is how do you manage stakeholder expectations? How do you do the change management? How do you teach people when the curriculum changes every three days? Like, how do you do that at scale? And so we have these niche in person and online communities for founding CTOs. You're like an individual contributor or a team lead. You got a team of less than 10. Startup CTOs venture back to a scaling from 10 to about a 100, like, how do I hire my first DMs, and how do I do performance management, and how do I create a consistent hiring, process, things like that. And then VPs and CTOs at scale who are running also, like, a 100 up to a couple thousand where it's generally good news is you have the systems in place. Bad news is they are now what is stopping you from getting the work done, that and the people. And so I spend a lot of time talking with engineering leaders about how do you scale adoption, not only what are the good technical practices, but how do you actually do the change management, which will be the hard part. Love that. Yeah. I mean, that is super relevant for some of the questions we'll get into today. I I, you know, built teams to 50 sounds small maybe, but, that's still where we are, and that's our whole team, over here at Directus. But, and I think you also do CTO hour, right, part of O'Reilly's Yep. So couple of the things I'll do is I do a quarterly CTO hour for O'Reilly. The most recent one, I got to interview Camille Fournier, the author of Manager's Path, and Ali Adasan, who's the CTO at Dropbox. It was a lot of fun. And then twice a year, I get to, go to KubeCon, the Kubernetes conference. And for CNCF, I get to facilitate the executive summit along with Kelsey Hightower. So that's always a blast. I love that. Is it KubeCon or KubeCon? Because it seems like Kubernetes, it'd be KubeCon. Yeah. You know what? I don't even know how to pronounce it, but with my accent, people always think I'm saying Q con, which is great, but a whole other thing. I I was at QCon AI recently presented something there, but so I I just try to make it clear, which one I'm talking about because my my computer always gets it wrong for sure when I'm dictating. Yeah. Well, say it with confidence, and I guess it doesn't matter too much. So let's kick it off. I think sort of like just table stakes, you know, setting the foundation here. In terms of day to day AI, Holger, maybe you can kick this one off, with your thoughts. Where where is your organization today with AI? Like, what does it actually look like for your employees, for your teams? You know, how's how's that actually operate? Yes. So I think we are in a crossroad. Right? In a we're in a in a in a in a situation where maybe some of the teams are as well, but, we are, very good, I think, already in adoption of AI on an individual level. So we have all engineers, most of, you know, using some form of AI. We have everybody using some form of AI to kind of improve the documentation, their workflows, and so on. What we recently, what we started, beginning of, the year or a little bit before as introducing our framework for the whole product development life cycle. And that's the interesting part. That's where we wanna go to, right, is, really understanding how we solve for not just cycle time, like, making one engineer faster, but, like, getting the flow for the whole squad to a complete new level. Right? It doesn't really make only sense to have one engineer being four times faster. Right? Not bad, but it's not enough. Right? And I think we're in the middle of we we as organization understood this. We are in the middle of transforming this. I really look at this as an AI transformation. We had DevOps transformation. We have AI transformation. Now we are in the in all of that on steroids, right, which is the AI transformation, and really trying to understand the cultural, the technical, aspects of this is something we're in the middle of right now. And it's something. Right? So you see engineers having, like, tons of agents running. You have designers creating pull requests. All of this is happening. Right? Just really bringing it together and super effective, on scale is where we are at and trying to solve for. Yeah. Is there is there a specific team or department that was know, doing something different than you expected in terms of, you know, that day to day? You know, may either moving faster, maybe they're moving slower, or just kind of a different different vibe altogether? I think it was what what was striking was that we have a few, like, smaller businesses that, basically, we have, like, HelloFresh and and Factor, the established big businesses, if you want. And then we have smaller, separately managed, startups, kind of. Right? So GoodJob and Pets Table. And they are organized a bit more lean in a sense. Right? They kind of kept the startup vibe, and they you saw picking up. They saw we saw them picking up very fast. Right? Because it's just out of pure necessity in a sense. Right? So resources are scarce. So to get anything done, right, and they couldn't ask for, like, five more engineers, and they had to solve it. And they start solving it with AI, and they went very fast. Right? And they were the first one adopting it. And there was maybe less red band as well. Right? So they just, you know, did it. Right? And so they kind of act as the as the template or as the as the as the leaders down. Right? And where we got the inspire the whole organization are inspired by that speed and the that results. Yeah. I mean, that's a huge network effect to be able to inspire other teams, you know, by seeing how fast that you can move and, like, maybe be more efficient. Obviously, that startup mentality is huge right now. We're seeing a lot of, you know, constriction across headcount in different orgs, you know, huge orgs that are saying like, oh, we can not just do things more efficiently, but, let's kind of adopt this more agile, you know, startup way of thinking. I think that's good across the board, you know, and, of course, sort of, sped up by by AI. And the interesting thought I was just want to mention is really is the the constraints. Right? So the the the constraints of limited resources and still an ambitious motivated team that wants to run this, this was a key cut cut cultural aspect. Right? Because if you then have a big organization, very distributed, decentralized, like everybody does their part, this might not be there. Right? And so thinking about how to create this urgency, is a big part of that transformation, I feel. Yeah. Yeah. I mean, the only you have these, like, enormous organizations, and it's almost like you you can only shrink the overall headcount so much. Like, how do you actually get that startup speed when you have hundreds, thousands, tens of thousands plus of, employees? And I think, you you know, that sort of had you eat an elephant, you know, one bite at a time. If you kind of break up your teams and kind of make them very autonomous and give them that that skill individually, they can operate like many different, smaller agencies or smaller smaller, startups within a larger org. That's I love that. Peter, similar question to you. I guess, you know, what what does that AI look like day to day for you? And maybe more specifically, like, how has that changed, you know, over the past year or so? So for me personally, it's a very different thing. As an individual, I'm a solopreneur, and it's great. I have a team of eight named agents running twenty four seven. I built my own little orchestrator. I'm creating my own custom memory system, which, puts skills. It puts, prompts and agents into a GitHub repo, and some high value shared informally structured context also goes there. Everything else goes into a Postgres database, which allows me to have things like I can have agents doing message passing through the database so that I can have multiple agents collaborating without having to go through a pull push cycle that I would have to if all of my tickets or work to be done was in some kind of Git based system. So personally, it's great. I'm just starting to dig into software generation. I'm looking to spin up in the next two weeks my own clone of monday.com so that I can basically have an interface for playbooks for running the business and projects for either running experiments, building new playbooks, or incrementally improving playbooks. And the idea is deterministic workflows with small model and human of the loop steps. So that way I can firstly reduce the cost latency of the models by the like, I just need a simple classifier. I don't need to be running Opus four six to do that. And then on the other side, for the human in the loop steps, every single interaction is captured in a fresh session. So we keep them in the smart zone, very small percentage of context used, and it means that then I run a compounding loop so that every night, every agent reviews what it did and updates its own instructions and skills in a way that it would be able to do it a little better the next day. And I'm already this system I'm building, I I started two weeks ago, and it's already running the entire business. And now I'm digging into software factories and the verification and looking at the Dan Shapiro, looking at what OpenAI did internally, looking at what Harper Reid is talking about about building rich software factories. When I see people who are blessed and cursed with 200 or 500 or 2,000 engineers, it's a slightly different story. The first thing I see is that, the widespread thing we're still seeing is augmented rather than agentic development, cursor in IDE mode or Copilot. It's fine. And if you wanna be 30% quicker doing 10% of your job, which is actually writing the code, you're gonna get some incremental improvement there. I think the next step to go is agentic. So maybe you've got six or eight small agents doing that you trust to write code or review code. That's fine too, and that you know, maybe you can be one and a half, two x as fast depending upon the code base, how well you prompt it, how much context you provide it. It starts to get interesting then you need to compound. I see people saying, oh, you know, like, I keep trying to do this, and it creates bad code. I'm like, have you told it what good looks like? Have you told it to remember what good looks like? And then if there's too much context where you're blowing the entire context window with all the rules, have you decomposed it so that you have one agent writing the code, one reviewing it? Maybe you have one reviewer that's looking for cyclomatic complexity, quality of naming, and architecture, and another one that's looking for security because you don't want one agent to have to solve for seven different dimensions in a single pass. It starts to get dumb. And once you so that then becomes interesting both on an individual level, even I'm getting compounding out of my small, agentic army. But once you start to do interesting things, you generally have a DevEx or platform team that owns the repo with the skills, agents, and prompts, and the key context, what you then do is you have this mechanism where other people within the org can fork that, tweak it, try to make it better, and then if, say, three people in the org give it a thumbs up, you then say, okay. This now gets owned by the head of AI, the platform team, the DevX team, and then you can share good practices across the organization without a priority knowing what the good practices are gonna be. Because Hochul is absolutely right. This is exactly the same as a cloud migration. You don't just give people a book on Kubernetes and say, are we there yet? Right? You actually want to have a program manager, and you want to have dashboards, and you want to have KPIs, and you want to have lunch and learns, and you want to have training sessions and Slack channels. You want to elevate people in your in your all hands. But at the same time, while you're doing all of that stuff, the curriculum changes every day, so you actually need to have a bottom up mechanism to get the best ideas from whenever they come. The one other thing I would say is, create a little bit of space for solo players. You can run way faster with this stuff solo. And if you have a small number of fire breathers, especially if they're building something that's not it's not the main way you charge all your customers. Right? This is the admin dashboard or this is the internal dev tooling where it goes down for an hour. It's not the worst thing. Those people will learn the practices that can then broadly be shared to the majority of your team. There's three buckets, and I promise to stop talking. The first bucket is the people who can't wait to do this more. They play with Steve Yegi's Gastown on the weekend. They are killing it. They're like, if I don't have 60 agents running, if I'm not blowing through 12 max plans at $200 each, personally, I'm not doing it right. Then there's the vast then there's some people who are honestly like, this is destroying the planet. This is destroying my job. This is miserable. I shall never do this. AI doesn't know how to write code as well as I do because I'm a Java developer. And they're gonna have a hard time. Hopefully, they will get AI infected like we got test infected for TDD. And the vast majority of people are somewhere in the middle saying, dude, I would do this, but you're telling me to read 50 blogs. Don't I have, like, features to shit? Tell me how to do it, and I'll give it a shot. Yeah. And you need to figure out how you deal with each one of those populations. Yeah. No. And that's that's huge. And it's interesting, you know, hearing about, you know, minimum spend, like, you know, on on AI. Like, is that a motivator? You know, you just kinda say, like, you're not spending enough. It's also interesting, you know, hearing the comparison of, like, a small team, a big org down to small teams or solopreneur. You know, at the end of the day, you can ramp up the the minions as it were, and get the the team that you need quickly. But you mentioned sort of like piping your data into a database, you know, into Postgres. Like, whether that's you doing that with with the minions or it's a bunch of teams, I think that kind of bridges into the next the next topic pretty well, which is either way, we're connecting data up to these these services, up to the tools that we're building, whether it's internal and maybe, like you said, it goes down for a second, Peter, and maybe that's not the end of the world. You know, maybe it is, depending on the usage. But in terms of governance, like, how how do we find that balance? Holger, I'm gonna throw this back over to you. When we think about people moving fast, you know, getting that agility, we want people building things and experimenting, optimizing their workflows. How do you think about guardrails? Because, obviously, you know, people are just kind of out there, you know, building, but, you know, data is is crucial. Data is the backbone of all of this. And if that gets leaked, if that, you know, isn't know, given given the proper RBAC or permissions, in these quick systems that are being built, you know, how do you how do you speak to that across your org? That's a very interesting question. And there's probably no final answer, but I think one, of course, we we have a high responsibility for for the data of our customers and our employees, and that's not gonna be sacrificed or, like, a change. Right? So we need central governance for for data, and we have it. So it's basically centralized through the existing teams that we have, like security or data privacy teams, reliability teams, ops teams. And we have a central centralized Gen AI team, basically, infrastructure team that kind of, is owning owning those things. Having said this, traditionally, you have guard rates around cost or maybe, I don't know, who can who can access to this. And and this, we we deliberately said we wanna, while protecting all the all the, PII data and relevant data very clearly, we want to open up basically everything else and and make people just try it out. Like, remove every red tape that we can. Right? And I think, you know, we were not looking at, should we have Corsa and Cloud Code and Gemini and whatnot. Right? We just take it all, cut it out, and see what sticks. Right? And if there's a new kid on the blog, we probably take this on as well and then see later, how we decommission it. It's just more important to get people excited, to get people working, and then, there will be a time for consolidation. And, you know, it's it's kind of ongoing. It's ready. We know more about, like, how how to manage context, you know, how to have a memory that is not, tool dependent and agnostic and stuff like that. So that helps. But I think that's the key part here. Right? It's removing red tape where we can while, while protecting the the data that we have to. Yeah. I mean and there's also I mean, shadow IT, obviously, and now we have, you know, shadow AI. Is that an issue for you? Like, people just bypassing, like, you know, the the research shows that, you know, when leadership is actually shaping all of this, AI governance, you're getting better value for your company. But you're still gonna have your ICs, your, you know, pretty much anybody go out and say, I'm gonna use my personal account, my personal service. You know, is what is your solution to that? You know, is that that that's, like, sort of a sidecar risk. You know, do you just lock that down? You just make it easier to use the approved, like, internal services? Or, you know, how do you avoid that sort of issue where people are using things that just aren't even approved and they're just kinda going rogue for better or worse? Yeah. So the honest answer is you cannot fully fully, kind of mitigate this, I would say. Right? And I think, I think we have a very compelling offer for people. Right? The you know, you have, you have, plans for for, for Copilot, for Gemini. There are. Right? So I think, if people now still choose to copy paste something in their own, Chativity, you cannot really, do much about it, I guess. But I think it's it's about training. Right? Kind of, we have a policy for for JNI. Right? We very early on had a had a policy and a got got guidelines, of course. Right? So that that people understand this. And I think especially if you're not in engineering, we want everybody to become a builder. Right? But not everybody in outside of engineering might have the context and might be security, sensitive also. So I think it's a big part of, like, training and making people aware of the risks and, what IP means, and, you know, what are the difference between private and enterprise accounts and stuff like that. So that is very, very important. But I think we tend to be more encouraging of using the tools than kind of limiting for now. Yeah. Well, Well, I would hope that everybody's being security sensitive. You know, that's that's obviously the name of the game. It it only takes one incident, one issue, and everything goes down quickly. Wonderful. Yeah. Peter, I guess, you know, different different sort of, route to, you know, think about this. But, you know, are you doing are you kind of baking that into your process and how you're thinking of you know, if if we're saving data into a database, are you just piping it straight in? Are you piping it straight out? Is there are there any sort of things that you're thinking of when you're you're building out these these systems? So one of the things I like is that, in many ways, I can build, an engineering infrastructure that feels like a a bigger company. And in fact, I need to. I find that the guidance, the onboarding, the design systems, the less decisions an agent is allowed to make, the more processing it can bring to those decisions, and the less likely is it it is to make random bad ones. I I was just chatting with a a few CTOs running larger teams at breakfast yesterday, and one of the common threads was how can we go more heavily into design systems, into standard patterns, into processes, into minimizing the number of decent technical decisions that need to be made so that the agents don't get don't don't have too too much space to keep getting it wrong. So I I think, managing the number of decisions for the agents, I think being very thoughtful about data, you need to think through I think we're gonna see a lot more about agentic roles and permissions. It turns out that if you just have a prompt that says never, never, never merge your work domain, It works almost all the time. If, however, you literally, I know somebody who has 50 agents and each one has their own GitHub account. And the nice thing is that you just put branch based permissions on main, and they are unable to merge it in until either a human or some other agent has done it. So I think you need to be very thoughtful. Don't assume that the agents will do what you say or even something reasonably close to what you say and have the guardrails. And I think it also comes back to all the classic good engineering practices. You know, you should decouple release from production by using feature flags so that you can canary rollout. You can load test things. You can roll things back. All of these are important. And I think to the biggest story is, like, whether it's shadow IT or, like, how you use this, I recommend picking a lane. If you think about it, technology adoption life cycle, crossing the chasm is like a 30 year old book, and it's still true. You can have innovators, early adopters, early late majority, and laggard. And, you know, that's okay. If you run a bunch of gyms, if you run grocery stores like physical plant, if you run ski resorts, maybe you can just wait till Microsoft figures it out and just tell everyone to go use Copilot. And in a year or two, you'll be a little bit faster. That's actually perfectly okay. You're gonna lose anybody who wants to work with AI, so it's gonna be a negative selection in terms of the team you get, but you're gonna keep most of the people who know how your systems work, and it'll keep them happy. And shadow IT is gonna be a problem for a while, but, honestly, all the people who wanna use shadow AI are gonna leave your company anyway. So it just is what it is. And then you can go to the other extreme. You can be like a Toby at Shopify. Right? You can be like, hey. Not only do I want to say we're AI first very early on, there was one time where he got the head of his, AI team to say, I want the 20 people using the most tokens. Promote them. Is that a good business decision where those tokens being used usefully? Doesn't matter. That is a cultural concept that that creates a sense of we want to be innovators. We want the people who wanna be innovators, and that's what we wanna attract. And if that costs, you know, 500 k in bonuses, it was totally worth it. So I think you need to figure out where you play on that. But then even if you're an f aider or early adopter, you still need education and enablement. Make it as easy as possible, like Holger said, for them to use. If you're just like, what do you mean we do AI? We have Copilot licenses, and you can request one. You're gonna get a lot of shadow AI and lose a lot of great people. Yeah. If you work hard but have reasonable red lines that explain why sending PII to service in China may not be in your customers or your business's best interest, That kinda makes sense. And providing you teach people what's going on and especially for the nontechnical folks in the org, that you give them an understanding of why the rules exist and good ways to be a good corporate citizen and still actually get stuff done. Yeah. Oh, absolutely. It's interesting thing. I've never even heard of, you know, just finding who's using the most tokens and promote them. Like, I can imagine people starting to use their agents just to run other agents just to ramp up the token spend. Over here, you know, building AI into our platform, it's all about, you know, how do we optimize this and get the tokens down. But I guess different different strokes for different folks. You had mentioned sort of nightly builds and sort of feature gating, you know, behind the flag. That's sort of kinda leads us into the next topic here, which is, you know, we we're seeing a million apps, you know, flood the market. We're seeing all these cool POCs and pilots and experimental, you know, things internal and external. Like, what what gets that into production? Like, what makes that a viable application or something that, you know, you kinda mentioned it works almost all the time, you know, and, you know, we're gonna wrap it in the secure prompt, like, never ever ever do this, and it still does it, you know, on occasion. That doesn't fly in production when you're dealing with mission critical systems, when you're, you know, really building for the customer, and externally. Beyond sort of, like, the the point that you mentioned, Peter, maybe you can kinda is there anything else that you think is is critical? Like, we talked on governance and sort of maybe permissions. But what else helps get you to that production scale and resilience? So there's a lot of things. A really good starting point is to remember we already have nondeterministic systems building software. They're called humans. There's just slightly different parameters when you deal with these new nondeterministic systems that are building software. It's it's an intern, and by the time you get to the end of the context window, it's an intern who's been on Adderall for two nights and is starting to forget stuff. So you have to be thoughtful about how you engage with your guardrails. My assumption that to take the extreme example is that my only job is to provide training for my future robot overlords. Do I believe that's true? Do I believe there's no rule for humanity? Hopefully not. But if I use that as an operating assumption, then there is no part of there's no time that I interact with an agent that I'm not doing it in a thoughtful way to improve their ability to give me a better outcome next time. So the first thing is you just rinse and repeat. You, like, you wanna build an entire software factory, right, where it goes from you just say the vision and it's in production with no human no required human gates between the two. All you do is you tell an agent to write some code, you tell it what was wrong, you make sure that it captures that context, and you tell it to go again. And you keep repeating that loop time and time and time again until you build more and more validators, adversarial reviews, and other elements that will reduce the likelihood. And you know what? I've taken that in production database in my life. Like, humans get it wrong too, but it's much less likely the more revs you do around that cycle to figure out what does good mean, what does bad mean, and how do we put, both tooling and and also just prompts. You've got the prompts, so you've got the tooling, you've got the context, but also you do need, I think, permissioning systems. You need to think about the blast radius. And then all the good stuff we've always been doing, observability, monitoring, alerting, I think there's great opportunities now for or self healing software is a fancy word for incident response where the LLM knows enough about the system to propose a a move forward. I think that's going to become mainstream. And, also, just stuff that we're doing fifteen years ago, game daying, resilience engineering. How can you make it so if your cart goes down, at least you can still see the products? If your payment provider goes down, it can still save your cart and send you an email once it goes up. Assume that stuff's going to break more and create more resilient systems with less moving pieces so that even when things go wrong, the blast radius is constrained. Yeah. One final thing I'd point is and the only other part is you can also we're not gonna review every line of code at some point that's going away. However, you can take a statistical process control approach to that, which is it's like ball bearings. You don't check everyone, but but if one of them is out of tolerance, you start checking them more frequently. And you do it risk adjusted, you're probably gonna test a smaller percentage of the PRs for your admin dashboard than you are for your main financial production flow. Yeah. That's that's amazing. I I love that. It's interesting. You kinda bring up sort of like, oh, when this stops working, you know, you have this, like, graceful degradation, process. I my first thought is, you know, you don't know what you don't know. And you have these sort of non engineers building out systems, and the system might work. But if they don't know to prompt in, you know, hey, let's let's make sure that we have this, you know, progressive enhancement, graceful degradation, then that just won't be included there, which is which is interesting to think of. Like, the creator of, you know, the software still needs to be architecturally aware of, you know, software engine, design and architecture. Two possible wonders I've seen for that. One model which I think is is good for now, which is I think we're going to see, you know, the two pizza team is not always going to be six to eight people now. I think you're gonna find triads in groups of four being able to get a lot of move a very long way and very fast when you have teams of twenty, fifty, 60 agents working with them. But there's going to be a combination of product and engineering skills. Somebody needs to know what we're building and why. Somebody needs to be thoughtful about, wait a second, latency, queuing, retime, split brain problems, all this stuff we deal with with large distributed systems. Those awarenesses need to exist. And then you might have some design skills or some data skills depending upon where the group is in your org and what it's trying to do. So I think in the early days, you make sure there's an engineer in the room before something you care about goes to production. I think, eventually, if you build a sufficiently rich software factory, you build those architectural concepts into the review process. Whether we get there and how close we get there, I don't know. So for now, I'm gonna keep a staff plus engineer having to look over the code just to be sure. Yeah. How and how quickly we get there. Exactly. Holger, I'm I'm really curious to hear your sort of, like, take on this. You know? Obviously, Peter mentioned a lot of things that do get sort of these pilots up into production. But, you know, our platform direct us, for instance, you know, we power, you know, mission critical software where we're pumping out, you know, bandwidth at, like, nine nine thousand pizzas per second, you know, for for certain customers or whatever applications they might be, building. And it's funny to me thinking of, like, that that is, like, production grade, like, at scale. You can't just one shot, you know, or Vibe code your way to that. Like, there's there's a process. So I'm curious, you know, when you take sort of that equivalent at, you know, HelloFresh, like, what are you what are you doing? What do you layer on top to make sure, that your applications, you know, if you're if you're building in that way, you know, through AI, that it can handle that. Yeah. That's a fantastic question. I think we, so so some some of the stuff, you mentioned, we do have to be one of the things, like, with the higher throughput, right, of of agentic AI or just, you know, teams speeding up. We see more, pull request reviews coming up with a new problem. Right? So so we need to find more, ways of, I think it's a really good idea, right, to kind of, find another abstraction level, basically, to look at, you know, risk management and and reviews and and and in a sense. Right? So that's quite interesting. So I think there we need to invest more. Right? This is something we need to do. What we have been doing is we have brought a few things to production, like AI products to production, which is kind of interesting. So we've been, maybe the most obvious case. We did go first as well. This is like customer care and having chatbots right there and experimenting there, a lot. And there, it's it's really it was we were kind of mid mid class last year or so. Right? So we were learning how to do this as we go. Right? And then managing, basically, the technology was not so much of the problem. It's more like managing the, really the the quality of it. Right? So all the different ways of of of how how a conversation with a human, the one deterministic, entity, right, can go, is, is, you know, not to underestimate. Right? And there's a lot of things that can go go wrong and how to how to design communication, the getting the intent right, and and keeping it safe, right, and and correct, is an is an ongoing task. And we, as we invested into, you know, the teams, that build up this messaging and then as well, the the automatic test. We experiment with AI as judged. Right? So, basically, having LLMs checking those those those flows as well. I think this this is very, very promising. Right? So this is one pattern that we see more is that you'll introduce, AI, maybe even different models, either for pull request reviews, having iterations on them, basically watching the manual testing themselves. Right? Like, having an eye look at, at how how it looks like and then doing pull requests by themselves. This actually works pretty, pretty well and has a very has a very high showed very high impact. So these are a few examples of where I can see. Right? So I think my my main concern right now is really getting the the the review part out of the way and and and on the other side, getting more, I don't know, engaging more UX and so on to create more experiments. So that's the other thing. I would like to have more, more variants basically tested earlier with the idea of having just already a more winning variant, basically, created into production, have higher higher signal, higher, success with more expensive AB testing in production. But why not having, like, 10 we we see this already, but I think we can do more 10 variations, test the prototypes, high high fidelity, bring it into the building, having tested before or with synthetic audiences. Right? Checking out quantitative analysis even on on scale if a business model works with a new hypothesis. So these are the things we're currently on and want to invest more into. So to get it on the front. Right? So getting better better signals earlier in the process, having the winning variance going to production faster and solving the PR revenue bottleneck problem. There are a few topics that we're currently working on. And then a few other things about personalization, improving the models, and as well creation from automatic creation of videos and and images, and we're creating a lot of, like, meals. And we have some AI support tooling that helps menu chefs, right, the creators of recipes to improve the process of getting this from idea to production, significantly faster than before. Yeah. Yeah. Well, it it that's I I love that. How how big is your org, Holger? So my org is about 300, people right now. The overall HelloTech org. So how this is how we call this either, combination of all, like, tech technical staff, if you want, is, is about 1,000 people. Yeah. And I I it's interesting. Like, all this only happens if we actually you know, the governance, you know, getting to production, you still have to have the people to make this happen. You know, going back to early, like, we can't just make the agents have the other agents work. Like, there's still human in a loop as Peter keeps putting it. It. You know, we think of, you know, Jack Dorsey and and kinda what happened over at Square. Like, switching to, like, the people side of this, you know, with an org of that size, like, how do you actually get the workforce to adopt, to use, to, you know, to actually have this happen in the first place. You know? Is I'm I'm assuming there's a fear piece. You know, we've talked a little bit about, I think, Peter, you had mentioned sort of building your for the future AI overlords. You know? There's a perception, I think, across some people that, like, oh, am I just digging my own grave and sort of, like, going out and and sort of building these these systems? But, you know, we have to lean in. You know, we can't just ignore it. So, you know, Holger, within your org, like, how does that work? Like, how do you make that happen? Yeah. So I like to to frame it in a way that, I think HelloFresh is a really great way to experiment with this, learn this, apply it on scale, and just make yourself, stay relevant if you want. Right? And and be on on on top of the game there. Right? So we we really try we we try to make this work. We support people. We support the teams. And that's kind of the positive way of framing it on if you wanna play it a bit differently. If you don't do this, right, the gap between, what the what the what the industry demands or what where the where the top level is and and everybody else, it's getting bigger very fast. Right? So you you you risk of falling behind in a sense, and that's not limited to engineering at all at all. Right? It's like every function and software engineering in and around it at least is, has the same kind of, opportunities and and challenges in a sense. But I don't, like, underestimate the, those factors. They are they are real. Right? So and I think there's a system of incentivizing, helping people, making it, you know, as we talked about it, easy to figure to try out things. Right? But then as well, set up clear expectations, right, that, that we expect every team to onboard into the new process and to to adopt to this agentic AI, for instance, use cases or to to have more variance in production now that we can do it, right, fairly easily. So there will be, the expectations grow steadily as well, to keep up with this. So that's the both of the side. Right? Enabling people, but as well-being very clear about expectations. Yeah. Oh, 100%. And it did like, Deloitte's latest report, I think, said something like only 20% of companies of orgs, actually, their their teams are actually ready, and prepared for AI. So, you know, getting that number up, hopefully, HelloFresh, you know, is in that 20%. But, but we're come we're coming up on time here. So I wanna kind of at least be a little bit more forward looking, with with sort of wrapping up here in terms of what's next. Peter, maybe I'll send it over to you. If you're looking out the next, like, say, one, one and a half years, like, where do you think the most change is gonna happen within AI? With an enterprise AI? I I think you go anything beyond that. I mean, even if you go out six months, the question mark start piling up so high. But if you were just kinda, you know, behind the sky, where do you think things are are heading? Within the engineering org, I think it's pretty straight this will be I'm not gonna put a timeline on it. I've got a very good friend, senior CTO. He's like, you can make whatever prognostications prognostications, but don't put a timeline on it. You you'll thank me later. And I I think he's right. I have no idea. It will be much faster at smaller companies in greenfield environments, in places where you happen to have, an engineer that's just super passionate about this and pulls the team forward. It should happen more quickly in SaaS companies and in companies that are potentially have an existential threat from AI. As I said, you're on a chain of gyms, maybe it doesn't matter. So there's going to be uneven, speed of change, but I think, eventually, we're going to see a flattening. What the year of efficiency continues to resonate many years after. You could imagine a world where you have triads or maybe teams of three to four doing something similar to what a two pizza team does now so you can split them out and have more of them capturing more initiatives. I I think it's possible you might have five to 15 of those reporting straight up to a senior director or a VP where you have a team lead in each. I think we're gonna continue to see the collapsing of that, and I think the VP is gonna be spending a lot of time on the keyboard reviewing detailed specifications, verifications, harness improvements, as well as research and product elements. So I think we're going to see leaner engineering orgs. The good news is we need a 100 x the software, and, I think there are lots of opportunities for anybody who is passionate about either. And I think we need to be happy with this. Right now, we're all excited about AI engineers. And for me, that's basically harness engineers, people who are thinking about prompts and verifications and steps and gates. We need 8% of those. That's our platform or DevX team. Everyone else is going to become a product engineer who gets better at proposing experiments, writing specifications, and creating rich verifications while also being thoughtful architecturally. And I think those are the two roles that are gonna continue to grow and be incredibly impactful in the future. Yeah. Oh, I love that. Slightly different lens, hold on. I wanna hear sort of your your sort of take on where things are heading. But, on the enterprise, something that's very sort of near and dear to my, my heart, my job is sort of the commodification of the front end. Like, we're we're seeing that happen actively with the AI app builders. The value of software that's being built, where where does that value shift? As as the front end, as the facade becomes commoditized and and everybody can build that, where does it shift? I mean, down the stack to, you know, all the way back to the database or somewhere in between. Where do you feel, that's that's heading over the next year or so? Yeah. It's a it's a it's a very good question. So basically, it's, I I still think, a a creativity and and being able to create a mode, the connection to your customers, building up this relationship is probably more important than ever. So you can be if you double speed on the wrong side of the highway, right, it it will not help you necessarily. So, so and I think that the the productivity gains that we have, that we're seeing now and that are super exciting, they will become a commodity at some point. I'm not putting a timeline on it either. But, you know, you you will have your great engineering teams, and product engineering teams. They will they will work on a completely different level. You still need to figure out what really works for a customer. And now the levels the field is, we're closer. Right? So I cannot rely on, say, a company like HelloFresh being faster, to production with some features than a small start up. So I need to be really, really smart about this. Right? I need to be have the most creative people writing the best specs. Right? So and, we have a lot of customers. We have a lot of customer relationship that is that is super valuable. That is not easy to copy, right, even with AI. So building on top of this and making sure that it stays this way and we stay on top of the innovation, that is what, I think, what still matters the most. Yeah. I mean, bring it back to the people, you know, the as a designer, you know, focusing on the creative. You know, you can always it's really exciting right now because AI is able to take, you know, this massive context and, like, pipe it in and make everybody creative, make everyone a developer. But at the end of the day, you know, you have to be able to break outside of, like, what's already there and sort of just rearranging those things. So I love hearing the answer kind of always coming back to the human, to the core of, you know, creativity and, you know, even going back to earlier. You know, you can throw the the kill switch in there. You can do all the right things and still, AI will kinda find its way to work around it. So, it's really important to have people that understand the the proper architecture of of what we're building and and why those things are important. So, hopefully, we don't stray too far from from those first principles. Holger, Peter, it was really, really exciting, getting to just chat through, you know, and the enterprise with both of you. You know, hopefully, we'll get to chat again soon, maybe again on a bridging bites episode. But for now, anything that you guys wanted to sign off with before we wrap this episode up? No. Just thanks a lot for for for having me. And, yeah. It was a pleasure talking to you. And, it's just exciting times, I must say. Likewise, Ben. Thank you so much for the invite, Holger. It was wonderful to hear your insights. So much fun and great conversations. Lots for all of us to learn. Awesome. Yeah. Well, absolutely. Thank you both for being here, and we'll see everybody, soon on the next episode. Alright. Thanks, guys.",[269,270,271],"d8526b3c-a789-4204-b7c8-ae43f353d0e2","5501ad58-a882-4675-a4ee-9be9901db2da","c8de2a57-26d9-4da7-8b01-f2bfdaecb2d6",[],{"year":139,"number":140,"id":141,"episodes":274,"show":275},[143,144,145,146,147,148,123,149],{"title":151,"slug":152},{"id":277,"slug":278,"vimeo_id":279,"description":280,"tile":281,"length":282,"resources":12,"people":12,"episode_number":13,"published":283,"title":284,"video_transcript_html":285,"video_transcript_text":286,"content":12,"seo":287,"status":19,"episode_people":288,"recommendations":291,"season":292},"6f059d81-f200-4dc7-88db-cb29239b3979","February-2026","1168199994","Join us for The Changelog, taking you through the month’s Directus updates including product updates, new content and community contribution highlights. This month's show includes an AI update from Bryant and a new community program to get involved with from Beth.\n\n","253fe0a0-a0f9-4d82-9e60-5e38f3d8bed4",33,"2026-03-04","February 2026","\u003Cp>Speaker 0: Hello everyone, welcome to the changelog from Directus for February. I'm Beth and we have a really great show for you coming up. We've got a product update from James, an AI update from Brian, I'm around with a brand new community program to get involved with and we have a fresh one app ten minute episode with some brand new directors faces, so do stick around if you can. Whether you are joining us for the first time or you are a regular, hi hello, thank you for spending some time with us, and without further ado, let's kick it off with James and a product update for you.\u003C\u002Fp>\u003Cp>Speaker 1: Hey. This is James from Directus, and I'm gonna take you through some of the highlights in the 11 dot 15 release. Now first up, our AI assistant is now in GA, and it is coming with some very tasty updates. We've added multi provider support with Google and OpenAI compatible providers. So now you can use our AI assistant with Olauma Mistral AI, extending on prior support for exclusively anthropic and OpenAI.\u003C\u002Fp>\u003Cp>We've also made the AI assistant native across all of the interfaces in the director studio, meaning you can even use the AI assistant in the visual editor now. Now with new power comes new responsibility. And to use this feature, you will need to update the Director's visual editing library to v 1.2.0 plus on your website. We've also added a new deployments module inside Director's. This allows you to connect your Director's instance with Vercel to centrally manage deployments, monitor build status and control your front end projects all without leaving Directus.\u003C\u002Fp>\u003Cp>We've added support for Vercel first, but Netlify and others are sure to come soon. Let's have a look at how it works. You'll find the Deployments module inside the settings and you'll need to enable this first. Once you've enabled that you're going to get the Deployments module in the sidebar of Directus. Let's take a look.\u003C\u002Fp>\u003Cp>Let's have a go at configuring Vercel. What you'll need is your personal access token from Vercel, and here's one I have from earlier. Once you add that, you'll see the projects listed from your Vercel account. You can choose to bring one or more of these into directors. So let's bring in a couple.\u003C\u002Fp>\u003Cp>Now you see the projects listed in the project listing. And if we click into one of these, we're able to hit deploy and start building our site from inside directors. So let's assume that we've made some content changes. Patch we've updated, you know, the price of an item on our website. And as a result, we need our site on Vercel to be rebuilt.\u003C\u002Fp>\u003Cp>So I'd come into the deployments module after making that content change and I'd hit deploy. Now the other great thing here is we can monitor the deployment status as that is building. So in case that fails, I'm gonna be able to see the reasons for the site failing. And when it's successful, I'm actually gonna have the link up at the top right to be able to visit the end result. So we'll just give that a second while it's building.\u003C\u002Fp>\u003Cp>Awesome. Now I can see the status is ready. And if I hit refresh, I'm gonna see this link up here which allows me to visit the end result. Now if I come back into here, I can see I can go back and I can see the deployment listing. Now one thing to call out is you're only gonna see deployments triggered from Directus inside the listing today.\u003C\u002Fp>\u003Cp>So all of your deployments made from Vercel will not appear here at the moment. One last call out is at the moment, the deployments module is only accessible for admins. However, we do plan to add, RBAC support so you can open this up to more users in the next release. We've also brought collaborative editing into core. Now this was previously built as an extension, but we wanted to bring it into core to make some performance improvements, reduce the amount of setup, and make sure that this is a native capability.\u003C\u002Fp>\u003Cp>Now under the hood, this runs on WebSocket connections for real time sync, so you do need to have this enabled on your project. It also plugs into the existing Director's permissions so users can only collaborate on records they have access to. Let's have a look at how to enable this in 11.15. You'll find this new setting in the project settings in your Directus instance. And once enabled, this will enable the real time sync.\u003C\u002Fp>\u003Cp>So let's take a look at a record in our content space. We've got a collection of products, and let's assume that two people are working on the denim jacket. And I will just there we go. We can see that both myself, James, and Michael Matthews are now working inside, the Product Datastem. Now let's assume that somebody is working inside a specific field.\u003C\u002Fp>\u003Cp>You'll see that that field lock comes into play, and that stops people overwriting each other's changes. So that's collaborative editing, and that's now available in the core. Now we've also made some improvements to how you can review view revisions inside the studio. So let's assume that we're updating the price of our denim jacket. And let's come back in to look at the revisions.\u003C\u002Fp>\u003Cp>Now previously when you viewed a revision, we were always comparing the revision you open to the latest revision of that item. But we've made some changes to make this a little bit more intuitive. So if I update my latest revision, you'll see I have that revision on the right hand side and I'm always comparing it to the previous revision now so that we can see the granular change from a 100 dot 99 to 50.99 in this case. Now we've also maintained some flexibility for you to compare a previous revision. Let's choose a much older one and see how that currently compares to the latest revision.\u003C\u002Fp>\u003Cp>Now this is advantageous in the case for restoration, in case you wanted to restore an older version but you want to understand exactly what it's going to update on the latest version of that item. So you just toggle this pill and you can switch between what you're comparing inside the revisions comparison model. So we've been through all of the main items inside the 11 dot 15 release. But as usual, you can go to the release notes on GitHub if you want to view every granular change, that got made inside 11 dot 15.\u003C\u002Fp>\u003Cp>Speaker 2: Hey, guys. Brian here. And I'm gonna showcase some of the exciting new features we shipped to the AI assistant in v 11 dot 15. Alright. First and foremost, it's an absolute banger.\u003C\u002Fp>\u003Cp>Not only can you now use the visual editor right alongside the form inside the live preview pane, but I can use the AI assistant right alongside the visual editor. I just click the magic AI button here. And now the AI assistant has this visual editor element into our context. And we'll just ask it to, let's punch up the copy a bit for this headline. Cool.\u003C\u002Fp>\u003Cp>So it understands where that is at on the page, what's going on. And now we can see once we approve that tool call, that gets updated in line. All right, that's just the start, right? That is a heavy hitter in this case. You can also add context to the AI assistant now.\u003C\u002Fp>\u003Cp>So, I can update pages that I'm not currently on. Let's say I've got this test page. Please update the slug and title for the test page to slash about. Right? And because we are injecting that into the context, it knows what page to update.\u003C\u002Fp>\u003Cp>And if we just go back to our pages list, we could see now that is updated. Last but not least, you can also reuse the AI prompts, those templated prompts from the MCP. So if you go to your AI settings, make sure that the MCP is enabled and that you've got the AI prompts collection. And then you can reuse these prompts over and over again. So if there are dynamic variables, Directus will ask you to fill those out.\u003C\u002Fp>\u003Cp>Or you know, if you don't have any variables, you could just\u003C\u002Fp>\u003Cp>Speaker 3: do this and say, hey,\u003C\u002Fp>\u003Cp>Speaker 2: tell me a nice dad joke. Alright. We'll see what it comes up with. Guy walks into a library, books about paranoia. She whispered, they're right behind you.\u003C\u002Fp>\u003Cp>Perfect. Alright. Now, onto some of the other items that you guys asked for, and I wanted to make sure that we delivered here. So now you can also control which models for the three major providers, OpenAI, Anthropic, and Google, so you can lock those down. The other big rock out of this release is going to be the OpenAI compatible provider.\u003C\u002Fp>\u003Cp>So as long as you have an endpoint that is OpenAI compatible, you can now go in here and set your base URL, add your API keys, you could set up your different models. Make sure you include the context limit, the output limit. You can also pass custom provider options if needed. And then you can use Ollama or, any other self hosted models, any other, OpenAI compatible models. Let's say, hey from Ollama.\u003C\u002Fp>\u003Cp>And this might be just a little bit slow because my MacBook is absolutely screaming at me right now. We'll fast forward. Alright. So now you can see that we've got the text back. Your mileage is gonna vary with the self hosted models, but, you know, if you've got Azure OpenAI or some other open open AI compatible endpoint that you're using, this is a great solution for you.\u003C\u002Fp>\u003Cp>And as always, keep the feedback coming. We love to iterate on these features, and we want to deliver real value instead of just the usual AI hype. That's it for me. Back to you, Beth.\u003C\u002Fp>\u003Cp>Speaker 0: I'm here to talk through a brand new community program we're launching called Directus Builders. Builders is a community champion program for people who use Directus, want to share what they're building, and contribute to the community. Whether you're interested in sharing technical insights and receiving amplification from our social channels, joining a network of other directors users, or getting our support for your own community initiatives, this program is for you if you are using directors to build. By joining, you'll enter a private community with other experienced builders and our team. It's open to contributors, customers, partners, users, really anyone who uses Directus to build something useful.\u003C\u002Fp>\u003Cp>You don't need to be building something huge, you just need to be building something real. If you're the person who likes helping others figure things out, sharing what you've been learning, or creating something cool, we want to hear from you. Applications to join the first cohort are now open. If you've got any questions or you have an idea that you think might work as part of this programme, we're all ears, we want to hear it. There's a couple of ways you can get in touch with us: submit via the application form, send an email to devreldirectus.\u003C\u002Fp>\u003Cp>Io, or post on the community forum. All of those work and we can get talking from there. We're really excited about launching this program, we hope you will also share the excitement and want to join and we're really looking to shape the future of the program collaboratively with the builders into something that works for everyone. So if you do have ideas, thoughts, questions, please do let us\u003C\u002Fp>\u003Cp>Speaker 3: know. Alright, viewers. Welcome to, yet another episode of 100 app, 100 oh, no. No. No.\u003C\u002Fp>\u003Cp>No. One app in ten minutes. Right? We are doing the remix version today where we have ten minutes to build and plan plan and build an amazing app clone, crazy suggestion, and I have no idea what we're gonna do. So the rules.\u003C\u002Fp>\u003Cp>Right? Ten minutes to plan and build. No more, no less. How we're gonna do that? We are going to use some, amazing tools that we have built into Directus.\u003C\u002Fp>\u003Cp>And then, rule number two, the anti rule. Use whatever you've got at your disposal. Today, I've got two awesome dudes at my disposal, mister Alvaro and Mark from our team here at Directus. No strangers to the Vue community. Welcome to the show, gents.\u003C\u002Fp>\u003Cp>Speaker 4: Thanks for having us, Bryant.\u003C\u002Fp>\u003Cp>Speaker 5: Thank you very much for the nice intro. Happy, to be here.\u003C\u002Fp>\u003Cp>Speaker 3: Yeah. Yeah. No. I'm super excited. Have you guys given any thought to what we're what we're gonna build?\u003C\u002Fp>\u003Cp>Speaker 5: I think, Mark, you have some idea though.\u003C\u002Fp>\u003Cp>Speaker 4: Yeah. So yesterday, we talked a little bit. I talked with Ava what we could build and, I don't know if if I showed it to you, Brian, but on my website I have a, instead of new year's solutions, I have new year's bingo cards. So you have five by five grid of stuff I want to do in the year. And if I get at least one in a row, so diagonal or horizontal or vertical, I already have bingo and it's a success.\u003C\u002Fp>\u003Cp>So I don't have to do all of them. And if you go to mark.dev\u002Fbingo\u003C\u002Fp>\u003Cp>Speaker 3: Okay. Let's check it out, guys.\u003C\u002Fp>\u003Cp>Speaker 4: You can it's still since it's just well, now February, not a lot has happened there. But\u003C\u002Fp>\u003Cp>Speaker 5: it's a it's a really nice way to actually do some of the New Year's resolution. I always get the press at the end of the year like I have done, like, a quarter of them.\u003C\u002Fp>\u003Cp>Speaker 3: Yeah. I love it. Alright. So alright. This is neat, man.\u003C\u002Fp>\u003Cp>I I miss Yep.\u003C\u002Fp>\u003Cp>Speaker 4: And each of them can be either, like, you did it or you didn't do it or it can be progressive. Like, read six books and you are, like, one books, two books, three books in. And I think I also have, like, sub tasks. If we can make that work, like, if one one, let's say, one bingo item has a few sub items as well. Like, don't have an example now, but that would also be cool.\u003C\u002Fp>\u003Cp>Speaker 3: Gotcha. Okay. New Year's resolution. Bingo card generator. Alright.\u003C\u002Fp>\u003Cp>That's what we're doing. This is gonna be amazing. This should be fun. What color are you guys feeling? Purple, pink?\u003C\u002Fp>\u003Cp>Speaker 5: I go I go purple. Blue. Or purple?\u003C\u002Fp>\u003Cp>Speaker 3: Purple. There we go.\u003C\u002Fp>\u003Cp>Speaker 4: Direct is purple. Nice.\u003C\u002Fp>\u003Cp>Speaker 3: Direct is purple. Alright, guys. Alright. So I'm sure you've seen the show. We're gonna start the clock.\u003C\u002Fp>\u003Cp>We got ten minutes to plan and build this thing. Let's do it. Alright. So the first thing I usually do here is cover requirements. Right?\u003C\u002Fp>\u003Cp>So what are the requirements we need out of this? Right? We need to generate bingo cards. Like, what do you what were you calling those?\u003C\u002Fp>\u003Cp>Speaker 4: Like, items probably or\u003C\u002Fp>\u003Cp>Speaker 3: Okay.\u003C\u002Fp>\u003Cp>Speaker 4: Goals. Yeah. Items.\u003C\u002Fp>\u003Cp>Speaker 5: Yeah. Like a grid of of of items. Mhmm.\u003C\u002Fp>\u003Cp>Speaker 3: Yeah. Alright. So we got some goals. Those are what kinda fields are you tracking on those? Just the name of the goal?\u003C\u002Fp>\u003Cp>Speaker 4: Yeah. A name description and then the status.\u003C\u002Fp>\u003Cp>Speaker 3: Status of the goal. Progress. Progress. Is it are you status and progress interchangeable?\u003C\u002Fp>\u003Cp>Speaker 4: Yeah. I guess if you like the if the progress is under percent, the status\u003C\u002Fp>\u003Cp>Speaker 3: Ah, okay. Yeah. Yeah. Yeah. Got it.\u003C\u002Fp>\u003Cp>Okay. And then we've got if you got goals, you got what? Items underneath the goals? We want, like, subtasks, like, if it's\u003C\u002Fp>\u003Cp>Speaker 4: You you can have subtasks. Let's see if there's one that has subtasks. I don't remember.\u003C\u002Fp>\u003Cp>Speaker 3: Task. It's called test.\u003C\u002Fp>\u003Cp>Speaker 4: Alright. So that the task would play into into progress as well, I guess.\u003C\u002Fp>\u003Cp>Speaker 3: Into goal. And then the task completed increases progress. Cool. Alright. And task needs what?\u003C\u002Fp>\u003Cp>Name? Description? No. Just name? Date date probably.\u003C\u002Fp>\u003Cp>Speaker 4: Maybe the, the item can have a a completed ad. Yeah. They completed as well for the task for the, item on top. Yeah.\u003C\u002Fp>\u003Cp>Speaker 3: Alright. And then we we wanna try to get a front end set up for this as well. Yeah. Alright. And we we need a front end to display the pingo cards.\u003C\u002Fp>\u003Cp>Alright. This could be a stretch in seven minutes now. Let's see how we do. Alright. So what are we using today?\u003C\u002Fp>\u003Cp>Right? We've got a blank directus project. We've got Claude code over here. Let's dive into it. Alright?\u003C\u002Fp>\u003Cp>I'm going to I'm not sure what you guys have been coding with. I've been using Super Whisper. I dig it. Alright. How are you doing?\u003C\u002Fp>\u003Cp>Alright, guys. We are building a New Year's resolution bingo card generator. I'm gonna copy and paste the data model that we want. You have access to a direct assistance. I want you to create our schema for that.\u003C\u002Fp>\u003Cp>We're also going to be building a front end to display the bingo cards. Let me know what questions you have. Let's create a plan. Alright. So this is crunching the transcript for that right now.\u003C\u002Fp>\u003Cp>Cool. There we go. I'll just, copy and paste this. Hopefully, we'll get some something good out of it. And we're gonna ask Claude Coad to plan.\u003C\u002Fp>\u003Cp>Speaker 5: Alright.\u003C\u002Fp>\u003Cp>Speaker 3: So now we've got the schema. So we've got the direct us MCP connected to this thing. And I I think you guys have had a chance to try this out already. Right? Yeah.\u003C\u002Fp>\u003Cp>Speaker 4: I think Avro has. I haven't.\u003C\u002Fp>\u003Cp>Speaker 5: Yeah. Play with it in the morning. It's gonna create the collections, the scheme is for you.\u003C\u002Fp>\u003Cp>Speaker 3: Yeah. Alright. So it's got a fresh direction. No custom collections. Alright.\u003C\u002Fp>\u003Cp>And I can zoom in just a little bit more so we could see this. What is the plan? And this is probably one of my favorite parts about this thing where it will prompt you for questions. Direct us flow, that's what we wanna do there. Vanilla JS.\u003C\u002Fp>\u003Cp>Yeah. That's what we'll do. What do you guys think? Five by five grid? Four by four?\u003C\u002Fp>\u003Cp>Speaker 4: We we can do also four by four so we don't have to come up with 25 things.\u003C\u002Fp>\u003Cp>Speaker 3: Amazing. Right? We got five minutes left.\u003C\u002Fp>\u003Cp>Speaker 5: You you can say to the MCP, hey, cloud, get, your twenty twenty six, bingo\u003C\u002Fp>\u003Cp>Speaker 4: Oh, that was cool.\u003C\u002Fp>\u003Cp>Speaker 3: Yeah. Yeah. Alright. Public read, that's fine. Anyone can view those.\u003C\u002Fp>\u003Cp>Cool. Alright. And now, hopefully, this thing should have a plan.\u003C\u002Fp>\u003Cp>Speaker 5: I wonder which resolutions Cloud Cove could have.\u003C\u002Fp>\u003Cp>Speaker 3: I don't know. Let's see. We'll we'll spin that up in an in a new find out. Alright. Cool.\u003C\u002Fp>\u003Cp>Right? Here's the direct to schema. There's our it's gonna create a flow. It's gonna create the front end. Sounds good.\u003C\u002Fp>\u003Cp>Let's let's roll with it. Right? I don't know what we're actually doing other than just talking this through here, but, I'm curious to see just how this thing works. I've you know, of course, like, spent a ton of time testing and building the MCP, but I've not spent a ton of time using it with the the latest Opus four five model. Alright.\u003C\u002Fp>\u003Cp>So it is checked the existing schema. Now we are it should start implementing. Yes. Please just start jamming on here. And if I refresh, now we should see some collections start to come in to the direct instance.\u003C\u002Fp>\u003Cp>We should see some collections. Start to come into the direct instance. There we go. Okay. Alright.\u003C\u002Fp>\u003Cp>Oh, nice. I was just worried that I did something wrong. So we got our goals. We got our tasks. Amazing.\u003C\u002Fp>\u003Cp>Right? Now I could go in. We could potentially create some new ones if we need. One of the things that I like about this is it, like it seems like the anthropic models do a better job of, like, actually putting together a cohesive form than than, like, the OpenAI ones. So it's going through creating relations and fields.\u003C\u002Fp>\u003Cp>Alright, guys. So in this other one, create, some New Year's resolutions for yourself, Claude. Alright. You guys have any more guidance for this thing?\u003C\u002Fp>\u003Cp>Speaker 4: They should follow this the smart principle, probably.\u003C\u002Fp>\u003Cp>Speaker 3: Follow the smart principle. What's the smart principle?\u003C\u002Fp>\u003Cp>Speaker 4: Now you got me. So it's like measurable, achievable.\u003C\u002Fp>\u003Cp>Speaker 3: I know what you're talking about now. Yeah. The smart goals.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 3: And include the add them to the goals and tasks inside.\u003C\u002Fp>\u003Cp>Speaker 4: For the for me, the most important one is always measurable. You have to be able to measure what you do. If not, you lose yourself.\u003C\u002Fp>\u003Cp>Speaker 5: You lose yourself. That's so funny.\u003C\u002Fp>\u003Cp>Speaker 3: That is very poetic. I love it, man. Alright. So it looks like okay. Yeah.\u003C\u002Fp>\u003Cp>I was just making sure we've got the relationship created correctly there. Alright. It is going to so we got two claws going. We got two minutes here. Let's see.\u003C\u002Fp>\u003Cp>I can see their goals and tasks.\u003C\u002Fp>\u003Cp>Speaker 5: Alright. This is the next development, man. Right? This is\u003C\u002Fp>\u003Cp>Speaker 3: the next development. Yeah. This thing is going to yeah. I need to enter YOLO mode so we can actually have this thing not stop to do these calls. But, behind the scenes, right, it is building this progress calculator flow.\u003C\u002Fp>\u003Cp>And and flows are Sure. A a nice piece of functionality. It can be a little time consuming to set up, like, complex flows via the UI. So having direct us put these together, is, yeah, definitely time saving. Right?\u003C\u002Fp>\u003Cp>That's probably, like, five, ten steps there. Yes. Create those items. Alright. Let's see what we've got.\u003C\u002Fp>\u003Cp>Are we gonna get to the front end for this thing? I don't know if we are, man.\u003C\u002Fp>\u003Cp>Speaker 4: I should've had Bryant.\u003C\u002Fp>\u003Cp>Speaker 3: Should've had, Claude do that first. It's connecting the operations. Claude, you need to go faster, my friend. Alright. So what are the what are the goals that Claude set for itself?\u003C\u002Fp>\u003Cp>This should be interesting.\u003C\u002Fp>\u003Cp>Speaker 5: Put that description statement.\u003C\u002Fp>\u003Cp>Speaker 4: I'll reduce\u003C\u002Fp>\u003Cp>Speaker 3: average response latency by 20%. Achieve 95% task completion rate without clarification. What an interesting goal. Here's the the individual tasks. And, oh,\u003C\u002Fp>\u003Cp>Speaker 5: and that was done there.\u003C\u002Fp>\u003Cp>Speaker 3: The HTML.\u003C\u002Fp>\u003Cp>Speaker 5: The front end.\u003C\u002Fp>\u003Cp>Speaker 3: Now it's doing it. No. Let me open this test project up. Is it going to have enough time? Yes.\u003C\u002Fp>\u003Cp>Proceed. New Year's resolution. Bingo. Oh, no. We ran out of time.\u003C\u002Fp>\u003Cp>It's so close. MCP connection should have access. No need to set up. I think, you know, this was so close, guys. I'm just going to it's against the rules, but you know what?\u003C\u002Fp>\u003Cp>We can make up our own rules here. I am just going to give access here to see and see if this will actually finish. Of course. There it is, man. The API permissions got us.\u003C\u002Fp>\u003Cp>We could see the bingo card here. There's the individual task. Ten minutes, full working back end with permissions, so close to a working front end.\u003C\u002Fp>\u003Cp>Speaker 4: It did pretty cool.\u003C\u002Fp>\u003Cp>Speaker 3: This is this is very cool. Right?\u003C\u002Fp>\u003Cp>Speaker 5: Even even with the subtask because that that wasn't an extra thing. Like, now it's the only iteration. Like, put the progress in the front end and\u003C\u002Fp>\u003Cp>Speaker 3: Yeah. I'm very curious to see. Right? It's already got it looks like it maybe did it miss some of the flows? Right?\u003C\u002Fp>\u003Cp>So the thing to take away here is obviously, like, you could build incredibly quickly with Directus and MCP, and this is not loading, probably because of my computer. Just hates running all these Docker containers locally. What is going on?\u003C\u002Fp>\u003Cp>Speaker 5: How many do you have?\u003C\u002Fp>\u003Cp>Speaker 3: There's probably, like, five or 10 running at the moment, like, different instances. And I'm sure if I, like, killed the camera, it would probably stop doing this. I don't I don't know what's going on here. Local host 8055. I at least want to end this episode on a high note and show something.\u003C\u002Fp>\u003Cp>Come on. Alright. So we could see the flows. Did they yeah. It actually connected the flow.\u003C\u002Fp>\u003Cp>So I'm just curious. Right. Just wanting to see. Right? Build a mastering five new programming frameworks.\u003C\u002Fp>\u003Cp>Let's say we completed this right now. Does this flow actually work? And So it it\u003C\u002Fp>\u003Cp>Speaker 5: it could increase the progress of the task of the goal.\u003C\u002Fp>\u003Cp>Speaker 3: It should. It should. And, of course, doing a hard refresh here is not not a great idea.\u003C\u002Fp>\u003Cp>Speaker 2: Alright. Well, gents,\u003C\u002Fp>\u003Cp>Speaker 3: you know, I'm not sure whether to put a, like, a thumbs up stamp on this one. Thumbs down stamp. So we just do I think yeah. This was, we we got most of the functionality\u003C\u002Fp>\u003Cp>Speaker 5: here. We\u003C\u002Fp>\u003Cp>Speaker 3: just didn't get, the front end all the way there.\u003C\u002Fp>\u003Cp>Speaker 4: Oh, Brian, you are lagging quite\u003C\u002Fp>\u003Cp>Speaker 3: Of course, I did.\u003C\u002Fp>\u003Cp>Speaker 5: Yeah.\u003C\u002Fp>\u003Cp>Speaker 3: Because it does I think you\u003C\u002Fp>\u003Cp>Speaker 4: you get a a thumbs up, Brian, because it we got a working thing at the end, and you had the the grid showing everything with the progress. So I think you get a thumbs up.\u003C\u002Fp>\u003Cp>Speaker 3: Yeah. Alright, guys. My computer is struggling. So we are going to sign off for this episode. Mark Alvaro, I've heard a little rumor that there might be a podcast coming up, so I'm super excited for that.\u003C\u002Fp>\u003Cp>Thanks for joining me for this episode of one app in ten minutes.\u003C\u002Fp>\u003Cp>Speaker 0: We want to take a moment towards the end of the change log for thanking our amazing community contributors who give their time to improve the director's project. In January, we had 14 contributors, and so we'd like to say a huge thank you to Oscar for removing the deprecated webhooks functionality across the stack, Abdullah for removing the comment tab from the activities page, Thomas for adding concurrency control for file uploads via a new files max upload concurrency environment variable, 'kiki' for fixing an issue that would cause some draw header icons from being displayed too large, 'pancaj' for fixing incorrect initial slider fill position when the midpoint is not a valid stepped value, and for fixing markdown editor layout when a minimum input height is applied. VDR for fixing sticky column background in many to many list interface. Fan for improving system permissions collection picker to support easier multi selection. Ty for replacing the local use local storage composable with the view use equivalent, Daniel for disabling text highlighting for druggable view elements in Chrome and Firefox, Clint for fixing permission cache to respect cache system TTL, Bruno for fixing conversion of fields from object notation to dot syntax in SDK subscription queries, Arthur for fixing an issue where the Supabase storage driver would fail if the root folder is the empty string, and Joseph for adding support for specifying a KMS key ID in s three storage when using AWS KMS server side encryption.\u003C\u002Fp>\u003Cp>Thank you again to these individuals. You can see their specific pull inside of the full release notes on GitHub. Lastly, we also want to take the time to thank our GitHub Sponsors of January who financially contribute to Directus' development. Thank you to Wayfan, Mike, Fergus, Omar, Marcus, Mission Control, Utomic, Steven, James, Manuel, Andreas, John, Burb, Adam, Jason, Yuya, Valentino, Jens and Wayne. The money we are given from our GitHub sponsors goes straight back to community members who build tooling and extensions for the director's ecosystem.\u003C\u002Fp>\u003Cp>Thank you again for being part of that. Alright, that is it for this month's changelog, if you are still here still watching thank you very much for spending the time with us. If you have any questions head on over to the directors forum, have a great rest of your day and see you soon.\u003C\u002Fp>","Hello everyone, welcome to the changelog from Directus for February. I'm Beth and we have a really great show for you coming up. We've got a product update from James, an AI update from Brian, I'm around with a brand new community program to get involved with and we have a fresh one app ten minute episode with some brand new directors faces, so do stick around if you can. Whether you are joining us for the first time or you are a regular, hi hello, thank you for spending some time with us, and without further ado, let's kick it off with James and a product update for you. Hey. This is James from Directus, and I'm gonna take you through some of the highlights in the 11 dot 15 release. Now first up, our AI assistant is now in GA, and it is coming with some very tasty updates. We've added multi provider support with Google and OpenAI compatible providers. So now you can use our AI assistant with Olauma Mistral AI, extending on prior support for exclusively anthropic and OpenAI. We've also made the AI assistant native across all of the interfaces in the director studio, meaning you can even use the AI assistant in the visual editor now. Now with new power comes new responsibility. And to use this feature, you will need to update the Director's visual editing library to v 1.2.0 plus on your website. We've also added a new deployments module inside Director's. This allows you to connect your Director's instance with Vercel to centrally manage deployments, monitor build status and control your front end projects all without leaving Directus. We've added support for Vercel first, but Netlify and others are sure to come soon. Let's have a look at how it works. You'll find the Deployments module inside the settings and you'll need to enable this first. Once you've enabled that you're going to get the Deployments module in the sidebar of Directus. Let's take a look. Let's have a go at configuring Vercel. What you'll need is your personal access token from Vercel, and here's one I have from earlier. Once you add that, you'll see the projects listed from your Vercel account. You can choose to bring one or more of these into directors. So let's bring in a couple. Now you see the projects listed in the project listing. And if we click into one of these, we're able to hit deploy and start building our site from inside directors. So let's assume that we've made some content changes. Patch we've updated, you know, the price of an item on our website. And as a result, we need our site on Vercel to be rebuilt. So I'd come into the deployments module after making that content change and I'd hit deploy. Now the other great thing here is we can monitor the deployment status as that is building. So in case that fails, I'm gonna be able to see the reasons for the site failing. And when it's successful, I'm actually gonna have the link up at the top right to be able to visit the end result. So we'll just give that a second while it's building. Awesome. Now I can see the status is ready. And if I hit refresh, I'm gonna see this link up here which allows me to visit the end result. Now if I come back into here, I can see I can go back and I can see the deployment listing. Now one thing to call out is you're only gonna see deployments triggered from Directus inside the listing today. So all of your deployments made from Vercel will not appear here at the moment. One last call out is at the moment, the deployments module is only accessible for admins. However, we do plan to add, RBAC support so you can open this up to more users in the next release. We've also brought collaborative editing into core. Now this was previously built as an extension, but we wanted to bring it into core to make some performance improvements, reduce the amount of setup, and make sure that this is a native capability. Now under the hood, this runs on WebSocket connections for real time sync, so you do need to have this enabled on your project. It also plugs into the existing Director's permissions so users can only collaborate on records they have access to. Let's have a look at how to enable this in 11.15. You'll find this new setting in the project settings in your Directus instance. And once enabled, this will enable the real time sync. So let's take a look at a record in our content space. We've got a collection of products, and let's assume that two people are working on the denim jacket. And I will just there we go. We can see that both myself, James, and Michael Matthews are now working inside, the Product Datastem. Now let's assume that somebody is working inside a specific field. You'll see that that field lock comes into play, and that stops people overwriting each other's changes. So that's collaborative editing, and that's now available in the core. Now we've also made some improvements to how you can review view revisions inside the studio. So let's assume that we're updating the price of our denim jacket. And let's come back in to look at the revisions. Now previously when you viewed a revision, we were always comparing the revision you open to the latest revision of that item. But we've made some changes to make this a little bit more intuitive. So if I update my latest revision, you'll see I have that revision on the right hand side and I'm always comparing it to the previous revision now so that we can see the granular change from a 100 dot 99 to 50.99 in this case. Now we've also maintained some flexibility for you to compare a previous revision. Let's choose a much older one and see how that currently compares to the latest revision. Now this is advantageous in the case for restoration, in case you wanted to restore an older version but you want to understand exactly what it's going to update on the latest version of that item. So you just toggle this pill and you can switch between what you're comparing inside the revisions comparison model. So we've been through all of the main items inside the 11 dot 15 release. But as usual, you can go to the release notes on GitHub if you want to view every granular change, that got made inside 11 dot 15. Hey, guys. Brian here. And I'm gonna showcase some of the exciting new features we shipped to the AI assistant in v 11 dot 15. Alright. First and foremost, it's an absolute banger. Not only can you now use the visual editor right alongside the form inside the live preview pane, but I can use the AI assistant right alongside the visual editor. I just click the magic AI button here. And now the AI assistant has this visual editor element into our context. And we'll just ask it to, let's punch up the copy a bit for this headline. Cool. So it understands where that is at on the page, what's going on. And now we can see once we approve that tool call, that gets updated in line. All right, that's just the start, right? That is a heavy hitter in this case. You can also add context to the AI assistant now. So, I can update pages that I'm not currently on. Let's say I've got this test page. Please update the slug and title for the test page to slash about. Right? And because we are injecting that into the context, it knows what page to update. And if we just go back to our pages list, we could see now that is updated. Last but not least, you can also reuse the AI prompts, those templated prompts from the MCP. So if you go to your AI settings, make sure that the MCP is enabled and that you've got the AI prompts collection. And then you can reuse these prompts over and over again. So if there are dynamic variables, Directus will ask you to fill those out. Or you know, if you don't have any variables, you could just do this and say, hey, tell me a nice dad joke. Alright. We'll see what it comes up with. Guy walks into a library, books about paranoia. She whispered, they're right behind you. Perfect. Alright. Now, onto some of the other items that you guys asked for, and I wanted to make sure that we delivered here. So now you can also control which models for the three major providers, OpenAI, Anthropic, and Google, so you can lock those down. The other big rock out of this release is going to be the OpenAI compatible provider. So as long as you have an endpoint that is OpenAI compatible, you can now go in here and set your base URL, add your API keys, you could set up your different models. Make sure you include the context limit, the output limit. You can also pass custom provider options if needed. And then you can use Ollama or, any other self hosted models, any other, OpenAI compatible models. Let's say, hey from Ollama. And this might be just a little bit slow because my MacBook is absolutely screaming at me right now. We'll fast forward. Alright. So now you can see that we've got the text back. Your mileage is gonna vary with the self hosted models, but, you know, if you've got Azure OpenAI or some other open open AI compatible endpoint that you're using, this is a great solution for you. And as always, keep the feedback coming. We love to iterate on these features, and we want to deliver real value instead of just the usual AI hype. That's it for me. Back to you, Beth. I'm here to talk through a brand new community program we're launching called Directus Builders. Builders is a community champion program for people who use Directus, want to share what they're building, and contribute to the community. Whether you're interested in sharing technical insights and receiving amplification from our social channels, joining a network of other directors users, or getting our support for your own community initiatives, this program is for you if you are using directors to build. By joining, you'll enter a private community with other experienced builders and our team. It's open to contributors, customers, partners, users, really anyone who uses Directus to build something useful. You don't need to be building something huge, you just need to be building something real. If you're the person who likes helping others figure things out, sharing what you've been learning, or creating something cool, we want to hear from you. Applications to join the first cohort are now open. If you've got any questions or you have an idea that you think might work as part of this programme, we're all ears, we want to hear it. There's a couple of ways you can get in touch with us: submit via the application form, send an email to devreldirectus. Io, or post on the community forum. All of those work and we can get talking from there. We're really excited about launching this program, we hope you will also share the excitement and want to join and we're really looking to shape the future of the program collaboratively with the builders into something that works for everyone. So if you do have ideas, thoughts, questions, please do let us know. Alright, viewers. Welcome to, yet another episode of 100 app, 100 oh, no. No. No. No. One app in ten minutes. Right? We are doing the remix version today where we have ten minutes to build and plan plan and build an amazing app clone, crazy suggestion, and I have no idea what we're gonna do. So the rules. Right? Ten minutes to plan and build. No more, no less. How we're gonna do that? We are going to use some, amazing tools that we have built into Directus. And then, rule number two, the anti rule. Use whatever you've got at your disposal. Today, I've got two awesome dudes at my disposal, mister Alvaro and Mark from our team here at Directus. No strangers to the Vue community. Welcome to the show, gents. Thanks for having us, Bryant. Thank you very much for the nice intro. Happy, to be here. Yeah. Yeah. No. I'm super excited. Have you guys given any thought to what we're what we're gonna build? I think, Mark, you have some idea though. Yeah. So yesterday, we talked a little bit. I talked with Ava what we could build and, I don't know if if I showed it to you, Brian, but on my website I have a, instead of new year's solutions, I have new year's bingo cards. So you have five by five grid of stuff I want to do in the year. And if I get at least one in a row, so diagonal or horizontal or vertical, I already have bingo and it's a success. So I don't have to do all of them. And if you go to mark.dev\u002Fbingo Okay. Let's check it out, guys. You can it's still since it's just well, now February, not a lot has happened there. But it's a it's a really nice way to actually do some of the New Year's resolution. I always get the press at the end of the year like I have done, like, a quarter of them. Yeah. I love it. Alright. So alright. This is neat, man. I I miss Yep. And each of them can be either, like, you did it or you didn't do it or it can be progressive. Like, read six books and you are, like, one books, two books, three books in. And I think I also have, like, sub tasks. If we can make that work, like, if one one, let's say, one bingo item has a few sub items as well. Like, don't have an example now, but that would also be cool. Gotcha. Okay. New Year's resolution. Bingo card generator. Alright. That's what we're doing. This is gonna be amazing. This should be fun. What color are you guys feeling? Purple, pink? I go I go purple. Blue. Or purple? Purple. There we go. Direct is purple. Nice. Direct is purple. Alright, guys. Alright. So I'm sure you've seen the show. We're gonna start the clock. We got ten minutes to plan and build this thing. Let's do it. Alright. So the first thing I usually do here is cover requirements. Right? So what are the requirements we need out of this? Right? We need to generate bingo cards. Like, what do you what were you calling those? Like, items probably or Okay. Goals. Yeah. Items. Yeah. Like a grid of of of items. Mhmm. Yeah. Alright. So we got some goals. Those are what kinda fields are you tracking on those? Just the name of the goal? Yeah. A name description and then the status. Status of the goal. Progress. Progress. Is it are you status and progress interchangeable? Yeah. I guess if you like the if the progress is under percent, the status Ah, okay. Yeah. Yeah. Yeah. Got it. Okay. And then we've got if you got goals, you got what? Items underneath the goals? We want, like, subtasks, like, if it's You you can have subtasks. Let's see if there's one that has subtasks. I don't remember. Task. It's called test. Alright. So that the task would play into into progress as well, I guess. Into goal. And then the task completed increases progress. Cool. Alright. And task needs what? Name? Description? No. Just name? Date date probably. Maybe the, the item can have a a completed ad. Yeah. They completed as well for the task for the, item on top. Yeah. Alright. And then we we wanna try to get a front end set up for this as well. Yeah. Alright. And we we need a front end to display the pingo cards. Alright. This could be a stretch in seven minutes now. Let's see how we do. Alright. So what are we using today? Right? We've got a blank directus project. We've got Claude code over here. Let's dive into it. Alright? I'm going to I'm not sure what you guys have been coding with. I've been using Super Whisper. I dig it. Alright. How are you doing? Alright, guys. We are building a New Year's resolution bingo card generator. I'm gonna copy and paste the data model that we want. You have access to a direct assistance. I want you to create our schema for that. We're also going to be building a front end to display the bingo cards. Let me know what questions you have. Let's create a plan. Alright. So this is crunching the transcript for that right now. Cool. There we go. I'll just, copy and paste this. Hopefully, we'll get some something good out of it. And we're gonna ask Claude Coad to plan. Alright. So now we've got the schema. So we've got the direct us MCP connected to this thing. And I I think you guys have had a chance to try this out already. Right? Yeah. I think Avro has. I haven't. Yeah. Play with it in the morning. It's gonna create the collections, the scheme is for you. Yeah. Alright. So it's got a fresh direction. No custom collections. Alright. And I can zoom in just a little bit more so we could see this. What is the plan? And this is probably one of my favorite parts about this thing where it will prompt you for questions. Direct us flow, that's what we wanna do there. Vanilla JS. Yeah. That's what we'll do. What do you guys think? Five by five grid? Four by four? We we can do also four by four so we don't have to come up with 25 things. Amazing. Right? We got five minutes left. You you can say to the MCP, hey, cloud, get, your twenty twenty six, bingo Oh, that was cool. Yeah. Yeah. Alright. Public read, that's fine. Anyone can view those. Cool. Alright. And now, hopefully, this thing should have a plan. I wonder which resolutions Cloud Cove could have. I don't know. Let's see. We'll we'll spin that up in an in a new find out. Alright. Cool. Right? Here's the direct to schema. There's our it's gonna create a flow. It's gonna create the front end. Sounds good. Let's let's roll with it. Right? I don't know what we're actually doing other than just talking this through here, but, I'm curious to see just how this thing works. I've you know, of course, like, spent a ton of time testing and building the MCP, but I've not spent a ton of time using it with the the latest Opus four five model. Alright. So it is checked the existing schema. Now we are it should start implementing. Yes. Please just start jamming on here. And if I refresh, now we should see some collections start to come in to the direct instance. We should see some collections. Start to come into the direct instance. There we go. Okay. Alright. Oh, nice. I was just worried that I did something wrong. So we got our goals. We got our tasks. Amazing. Right? Now I could go in. We could potentially create some new ones if we need. One of the things that I like about this is it, like it seems like the anthropic models do a better job of, like, actually putting together a cohesive form than than, like, the OpenAI ones. So it's going through creating relations and fields. Alright, guys. So in this other one, create, some New Year's resolutions for yourself, Claude. Alright. You guys have any more guidance for this thing? They should follow this the smart principle, probably. Follow the smart principle. What's the smart principle? Now you got me. So it's like measurable, achievable. I know what you're talking about now. Yeah. The smart goals. Yeah. Yeah. Yeah. And include the add them to the goals and tasks inside. For the for me, the most important one is always measurable. You have to be able to measure what you do. If not, you lose yourself. You lose yourself. That's so funny. That is very poetic. I love it, man. Alright. So it looks like okay. Yeah. I was just making sure we've got the relationship created correctly there. Alright. It is going to so we got two claws going. We got two minutes here. Let's see. I can see their goals and tasks. Alright. This is the next development, man. Right? This is the next development. Yeah. This thing is going to yeah. I need to enter YOLO mode so we can actually have this thing not stop to do these calls. But, behind the scenes, right, it is building this progress calculator flow. And and flows are Sure. A a nice piece of functionality. It can be a little time consuming to set up, like, complex flows via the UI. So having direct us put these together, is, yeah, definitely time saving. Right? That's probably, like, five, ten steps there. Yes. Create those items. Alright. Let's see what we've got. Are we gonna get to the front end for this thing? I don't know if we are, man. I should've had Bryant. Should've had, Claude do that first. It's connecting the operations. Claude, you need to go faster, my friend. Alright. So what are the what are the goals that Claude set for itself? This should be interesting. Put that description statement. I'll reduce average response latency by 20%. Achieve 95% task completion rate without clarification. What an interesting goal. Here's the the individual tasks. And, oh, and that was done there. The HTML. The front end. Now it's doing it. No. Let me open this test project up. Is it going to have enough time? Yes. Proceed. New Year's resolution. Bingo. Oh, no. We ran out of time. It's so close. MCP connection should have access. No need to set up. I think, you know, this was so close, guys. I'm just going to it's against the rules, but you know what? We can make up our own rules here. I am just going to give access here to see and see if this will actually finish. Of course. There it is, man. The API permissions got us. We could see the bingo card here. There's the individual task. Ten minutes, full working back end with permissions, so close to a working front end. It did pretty cool. This is this is very cool. Right? Even even with the subtask because that that wasn't an extra thing. Like, now it's the only iteration. Like, put the progress in the front end and Yeah. I'm very curious to see. Right? It's already got it looks like it maybe did it miss some of the flows? Right? So the thing to take away here is obviously, like, you could build incredibly quickly with Directus and MCP, and this is not loading, probably because of my computer. Just hates running all these Docker containers locally. What is going on? How many do you have? There's probably, like, five or 10 running at the moment, like, different instances. And I'm sure if I, like, killed the camera, it would probably stop doing this. I don't I don't know what's going on here. Local host 8055. I at least want to end this episode on a high note and show something. Come on. Alright. So we could see the flows. Did they yeah. It actually connected the flow. So I'm just curious. Right. Just wanting to see. Right? Build a mastering five new programming frameworks. Let's say we completed this right now. Does this flow actually work? And So it it it could increase the progress of the task of the goal. It should. It should. And, of course, doing a hard refresh here is not not a great idea. Alright. Well, gents, you know, I'm not sure whether to put a, like, a thumbs up stamp on this one. Thumbs down stamp. So we just do I think yeah. This was, we we got most of the functionality here. We just didn't get, the front end all the way there. Oh, Brian, you are lagging quite Of course, I did. Yeah. Because it does I think you you get a a thumbs up, Brian, because it we got a working thing at the end, and you had the the grid showing everything with the progress. So I think you get a thumbs up. Yeah. Alright, guys. My computer is struggling. So we are going to sign off for this episode. Mark Alvaro, I've heard a little rumor that there might be a podcast coming up, so I'm super excited for that. Thanks for joining me for this episode of one app in ten minutes. We want to take a moment towards the end of the change log for thanking our amazing community contributors who give their time to improve the director's project. In January, we had 14 contributors, and so we'd like to say a huge thank you to Oscar for removing the deprecated webhooks functionality across the stack, Abdullah for removing the comment tab from the activities page, Thomas for adding concurrency control for file uploads via a new files max upload concurrency environment variable, 'kiki' for fixing an issue that would cause some draw header icons from being displayed too large, 'pancaj' for fixing incorrect initial slider fill position when the midpoint is not a valid stepped value, and for fixing markdown editor layout when a minimum input height is applied. VDR for fixing sticky column background in many to many list interface. Fan for improving system permissions collection picker to support easier multi selection. Ty for replacing the local use local storage composable with the view use equivalent, Daniel for disabling text highlighting for druggable view elements in Chrome and Firefox, Clint for fixing permission cache to respect cache system TTL, Bruno for fixing conversion of fields from object notation to dot syntax in SDK subscription queries, Arthur for fixing an issue where the Supabase storage driver would fail if the root folder is the empty string, and Joseph for adding support for specifying a KMS key ID in s three storage when using AWS KMS server side encryption. Thank you again to these individuals. You can see their specific pull inside of the full release notes on GitHub. Lastly, we also want to take the time to thank our GitHub Sponsors of January who financially contribute to Directus' development. Thank you to Wayfan, Mike, Fergus, Omar, Marcus, Mission Control, Utomic, Steven, James, Manuel, Andreas, John, Burb, Adam, Jason, Yuya, Valentino, Jens and Wayne. The money we are given from our GitHub sponsors goes straight back to community members who build tooling and extensions for the director's ecosystem. Thank you again for being part of that. Alright, that is it for this month's changelog, if you are still here still watching thank you very much for spending the time with us. If you have any questions head on over to the directors forum, have a great rest of your day and see you soon.","73bc207d-4411-4b34-8a99-3ace5581711e",[289,290],"ca7b298d-cfce-4a0a-a467-352f31bd3140","12d14e99-9340-4084-9bce-25e042471e7d",[],{"year":139,"number":293,"id":294,"episodes":295,"show":296},57,"e1a5a496-3320-4bb4-8267-8fec1c7c5f57",[277],{"title":297,"slug":298},"The Changelog","the-changelog",{"id":300,"slug":301,"vimeo_id":302,"description":303,"tile":304,"length":305,"resources":12,"people":12,"episode_number":177,"published":306,"title":307,"video_transcript_html":308,"video_transcript_text":309,"content":12,"seo":310,"status":19,"episode_people":311,"recommendations":314,"season":315},"8213071c-6d2a-4564-b058-f177900dcad1","workout-app","1163990620","Bryant is joined by Matt with the goal of building a workout fitness app in just 10 minutes using the MCP.","54b6f2d5-ab15-4465-9cbe-1ddba413084f",18,"2026-03-02","Workout App","\u003Cp>Speaker 0: Alright. Alright. Alright. Welcome back to another episode of one app ten minutes. I'm your host Brian Gillespie.\u003C\u002Fp>\u003Cp>Today, we have got an extra special guest that I will introduce in a moment. But if you're new to the show, let's cover the rules. Ten minutes to plan and build and no more, no less. You animals couldn't sit for an hour, so here we are trying to build apps in ten minutes. Successfully or not, we will build the app or die trying.\u003C\u002Fp>\u003Cp>As for a special guest, right, mister Matt Miner from our marketing team at Directus. Matt, how are you, sir? Happy to have you on the show.\u003C\u002Fp>\u003Cp>Speaker 1: Well, I was good. In the intro, you said I was very special, and then you just said I was special. So which is it? Very special or just a special?\u003C\u002Fp>\u003Cp>Speaker 0: Very special?\u003C\u002Fp>\u003Cp>Speaker 1: Good. Extra special. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: How special do you wanna be? Just How\u003C\u002Fp>\u003Cp>Speaker 1: special do you wanna be? Enough. You know? No. It's great to be here.\u003C\u002Fp>\u003Cp>Unlike other guests, I had a little heads up, so you couldn't catch me off guard with a random meeting. So, I'm\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I mean, that's that's fun for me, certainly, to put people on the spot. The other thing that gives me a bit of anxiety is if you have had time to think on what we're actually gonna build Yeah. I certainly have no idea. You can thank Beth on our team for that.\u003C\u002Fp>\u003Cp>Thank you, Beth. I don't know if you're gonna watch this afterwards or in post production, but I appreciate you for putting me on the spot. Yeah. So, without further ado, you know, tell me what we're building so I can put it up here on the board.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. We're gonna build. Little backstory. Since having our second kid, my son, I can count on the number I can count the number of times we've slept through the night on, like, one hand. And for one for one and a half years, he he turns two in June.\u003C\u002Fp>\u003Cp>So, for, you know, obvious reasons, like our I've not been to the gym. You know, I've not been working out. I get winded when I walk up the stairs. So I've been one of the New Year's resolutions this year has been getting back\u003C\u002Fp>\u003Cp>Speaker 0: to the gym.\u003C\u002Fp>\u003Cp>Speaker 1: I need some sort of, like, fitness app, workout app sort of thing, to track workouts and things like that, as well as keep, like, kind of a running diary. And, if we wanna get, like, crazy advanced with it, I have a lot of ideas for it. But, yeah, hopefully, this is something you can build and and I can sell on the side. So that's my goal today.\u003C\u002Fp>\u003Cp>Speaker 0: Jeez. Okay. I think a native app would be very difficult in ten minutes. We could certainly cook something up with Directus. So just to to prove to the folks there's no hand wavy magic here.\u003C\u002Fp>\u003Cp>Right? We've got a blank Directus back in. I do have cloud code set up for us with the direct Us MCP already connected, which is, hopefully going to help us move faster than the speed of light. But you know the drill. We're gonna start the clock, and then we'll plan this thing out and see how far we can get in ten minutes.\u003C\u002Fp>\u003Cp>Are you ready?\u003C\u002Fp>\u003Cp>Speaker 1: I think I'm ready.\u003C\u002Fp>\u003Cp>Speaker 0: He's born ready. Here we go. Alright. Ten minutes. Here we go.\u003C\u002Fp>\u003Cp>What sort of functionality do you want out of this thing?\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So, basically, I need to be able to, like, put workouts in ahead of ahead of the, when I actually go work out. So I'm thinking, like, Monday, Wednesday, Friday, I'll go to the gym. I'll do, like, a push day on Monday, pull day on Wednesday, and, like, a legs day on Friday. Right?\u003C\u002Fp>\u003Cp>Within that, like, just a basic thirty minute workout, so it'll be kind of repeatable with the exercises. And then just having a way to log, like, the weight that I do. And then if if we wanna get extra fancy with it, like, implementing some sort of, like, AI increase of, like, you know, a couple percentage points of the weight. So, like, next time when I go, there's a little bit more weight. I have to be able to rate it.\u003C\u002Fp>\u003Cp>So, like, if I can hit all of the reps during the workout workout, and if so, then, like, increase it by, like, 5% or something weight wise. Yeah. Basic functionality is that. Logging, you know, planning the workout, with sets and reps in the workout and then logging, like, easy, medium, hard for each one.\u003C\u002Fp>\u003Cp>Speaker 0: Holy cow. Okay. Alright. No short order. Right?\u003C\u002Fp>\u003Cp>I'm not sure if you're using this tool yet or is it variety of tools? I've got this thing called Super Whisperer. I'm just gonna enable this because I suck at typing. Alright. And then I'm gonna talk.\u003C\u002Fp>\u003Cp>Hi, Claude. We are going to build a workout fitness app sort of thing for my friend, Matt. I'm gonna paste some notes on our data model, what the workout should look like and the different things that we need to, have inside the app. And I want you to quickly, as fast as you can, create a plan to add that to our direct us instance, using the MCP. Alright.\u003C\u002Fp>\u003Cp>So, we got a transcript here that would have taken me forever to type. I'm gonna paste this in. Oops. Let me make sure I copy this. Cool.\u003C\u002Fp>\u003Cp>And then let's go. Right? Now, normally, I would kick this over in plan mode, but we are extremely on the clock here. We got seven minutes and thirty seconds left. It should pick up the schema and existing collections from Directus.\u003C\u002Fp>\u003Cp>Speaker 1: For one shotting.\u003C\u002Fp>\u003Cp>Speaker 0: Is this We're we're just gonna have to one shot this. You know, I'm asking it to plan first, though. Right? I did say that. Quickly create a plan.\u003C\u002Fp>\u003Cp>So here we go. We got a plan. Matt's workout app. Alright. You got exercises.\u003C\u002Fp>\u003Cp>Cool. You got workouts, day of the week, target duration, workout exercises, junction table, workout logs, sets completed, exercise, default sets, default reps. Okay. So I see reps and stuff in there. We got the exercise progress.\u003C\u002Fp>\u003Cp>Exercise logs, that's where your your rating comes in, I guess. Optionally, create a flow. Alright. So let's answer these questions. Right?\u003C\u002Fp>\u003Cp>We want, LBs, an American LBs.\u003C\u002Fp>\u003Cp>Speaker 1: In our annoying metric system.\u003C\u002Fp>\u003Cp>Speaker 0: User User specific shared templates.\u003C\u002Fp>\u003Cp>Speaker 1: Context to that would be what? If, like, I'd go to the gym with my wife, we could share, like, the same workout.\u003C\u002Fp>\u003Cp>Speaker 0: The same workout app? Yeah. Maybe. User off, I guess. Let's do that.\u003C\u002Fp>\u003Cp>Specific exercises to prepopulate, come up with a list of exercises to populate. And, actually, we can have Claude do that separately. Rating scale, one to five or thumbs up or thumbs down?\u003C\u002Fp>\u003Cp>Speaker 1: I would say easy, medium, hard. So, like, if easy, progress weight. If medium, keep weight. If hard, reduce weight or reps.\u003C\u002Fp>\u003Cp>Speaker 0: If easy, increase, medium, increase?\u003C\u002Fp>\u003Cp>Speaker 1: Medium, keep the same. And hard, reduce.\u003C\u002Fp>\u003Cp>Speaker 0: Reduce reps. Alright. Five minutes. Let's see what it comes up with. It's gonna update the plan for us, and then, Has\u003C\u002Fp>\u003Cp>Speaker 1: it already been five minutes?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. This this goes quickly. Yes. Implement my guy. Go forth.\u003C\u002Fp>\u003Cp>What do you mean? So you could see already, like, the I I know how to click all these buttons and direct us, certainly. Right? I could go through, and I could do all this. And I've done it pretty quickly.\u003C\u002Fp>\u003Cp>But, AI is such a I it gives us so much leverage in this equation.\u003C\u002Fp>\u003Cp>Speaker 1: That's the thing, though. Right? Like, does anybody actually enjoy building out the data models, like, on the back end now? Like, this it's it's kind of fun, I guess, like, putting it's like putting a puzzle together, but, like, it feels better if you just get a foundation and then be able to, like, tweak on it. Right?\u003C\u002Fp>\u003Cp>I guess, posing that question to you, like, do you like building out the back end or do you care about the front end?\u003C\u002Fp>\u003Cp>Speaker 0: You know, I am I'm a results guy, man. Yes. You are. Do you know? I\u003C\u002Fp>\u003Cp>Speaker 1: You better get results.\u003C\u002Fp>\u003Cp>Speaker 0: Like the ship stuff. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: I I I need this.\u003C\u002Fp>\u003Cp>Speaker 0: On the spot. Like, I you know, I I am kind of sad on some level of, like, hey. It's not me doing the work anymore, but, you know, I've come to realize that, like, the the strength that I have is not actually writing the code or clicking the buttons. Right? It is putting the the stuff together.\u003C\u002Fp>\u003Cp>So we are at three forty three. What is Directus doing here? What is claw doing? Right? You can see it's creating fields for all the different relationships.\u003C\u002Fp>\u003Cp>It's adding those relationships. Now it's seeding the exercises. Yes. Let's create the default workout templates and create the flow to auto progress. So, again, this is this is a terrible way to build something for actual production in that, I've just got this set up in yellow mode, and, basically, I'm barely even reading these things.\u003C\u002Fp>\u003Cp>Yeah. We're I didn't even give you a chance to to comment on this. Right. But the the principles here are sound. Right?\u003C\u002Fp>\u003Cp>And that you've got a thinking partner in in something like Cloud Code or, you know, it could just be Cloud or Chad GPT. It doesn't really matter which AI you're using. It's It's helpful for thinking through these things. So if we take a look, right, now we've got our data model. We've got our exercise progress.\u003C\u002Fp>\u003Cp>We've got your different workouts with your different exercises already created here. Alright. You got your workout logs, which I'm assuming here's your workout.\u003C\u002Fp>\u003Cp>Speaker 1: So Monday. Yeah. And that's the planning of it. Right? It's like yeah.\u003C\u002Fp>\u003Cp>Monday. Well, probably\u003C\u002Fp>\u003Cp>Speaker 0: Oh, this is this is your yeah. The workout log. Workouts would be this is does this have your weight? No. It doesn't have the weights on it, does it?\u003C\u002Fp>\u003Cp>Yeah. So that part of it we're missing. Right? Maybe that is\u003C\u002Fp>\u003Cp>Speaker 1: And I guess we aren't specific about that. Yeah. Well, there's weight at least in the logs.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So let's set up the Monday. Yesterday, Matt did this. Totally did it.\u003C\u002Fp>\u003Cp>Speaker 1: Totally did it.\u003C\u002Fp>\u003Cp>Speaker 0: I swear. Alright. So, you create your exercise log. You got a bench press, etcetera. Right?\u003C\u002Fp>\u003Cp>You completed five sets of five reps at two twenty five. Mhmm. Mhmm. Beast. Mhmm.\u003C\u002Fp>\u003Cp>Alright. So there you go. You got your exercise log. Let's check on what we're doing. Right?\u003C\u002Fp>\u003Cp>It is creating some operations now. So we should see, like, a flow in here. Should be creating these operations for it. Okay. We got a minute and twelve seconds left.\u003C\u002Fp>\u003Cp>Speaker 1: I should have not said my sob story at the beginning. We'd have more time. But, I mean, this looks great. Right? Like, I think what's really interesting about it is that, you know, not even using Directus as, like, the interface for this, like, just slapping together a lovable front end or something that's much easier and accessible, like, from my mobile device than just, like, while I'm there, just blah blah blah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Alright. So we're gonna go in, and we're gonna create this workout log again. It's Wednesday, pool day. Except you did it on Tuesday.\u003C\u002Fp>\u003Cp>It is we're gonna do hard incline. Oh, what's the pull? Lat pull downs. There you go. You had 20 sets.\u003C\u002Fp>\u003Cp>20 reps. Crushed it. 110 pounds.\u003C\u002Fp>\u003Cp>Speaker 1: I had my creosote\u003C\u002Fp>\u003Cp>Speaker 0: right there. Let's see what happens. User exercise progress. Did we did we get an error on the flow? Oh, we got five seconds left.\u003C\u002Fp>\u003Cp>Know. Out of time. No. Run scripts. Cannot read properties.\u003C\u002Fp>\u003Cp>Okay. So, you know, Claude screwed up a bit on our run script somewhere in here where, you know, it's it's wrote some JavaScript code, which is nice. One of the nice things about the flows inside Directus is you can just write arbitrary JavaScript and run that. But there's a a few debugging issues. Right?\u003C\u002Fp>\u003Cp>So ten minutes in this case. And, you know, let's let's take a look. Right? Let's go to the tally. We've got got the workout exercises.\u003C\u002Fp>\u003Cp>We got the reps. We got your Monday, Wednesday, Friday, and we got the ability to log the workout. We got the rating the workouts. What we did not get is the automatic increase. So that logic, we did not get.\u003C\u002Fp>\u003Cp>Speaker 1: And I would say that that So instead of the way it's interesting how I applied it to the full workout instead of the individual, like, exercises, which is probably why it got funky because it was probably waiting for us to put in all of the\u003C\u002Fp>\u003Cp>Speaker 0: like, hey. Was this individual exercise hard or not? That's a good insight. Right? So it does show you I I I think I'm impressed with how far you can get into\u003C\u002Fp>\u003Cp>Speaker 1: I mean\u003C\u002Fp>\u003Cp>Speaker 0: that's right.\u003C\u002Fp>\u003Cp>Speaker 1: Man, this is I mean, this is really the basis. You see a lot of apps out there like Fitbot or, like, Fitness AI, which are solid, but, you know, they're not customized to you. And being able to just, like, do this, get it custom, share it, you know, with whoever I'm working out with, it's powerful.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Another ten, fifteen minutes, we could have had, you know, nano banana generate images of these or we could have copied images. Like, diagrams of how how to do the exercises. Right? Yeah.\u003C\u002Fp>\u003Cp>Could create workout templates. Again, like, you could have, like, auto progression or auto calculation on there. And then even with the AI chat inside Directus. Right? Mhmm.\u003C\u002Fp>\u003Cp>Hey, man. Plan me a new workout. Right? Even if you let's say you you haven't built that, that front end yet Yeah. The native app that we're gonna build in ten minutes.\u003C\u002Fp>\u003Cp>Yo. You could still interact with this on your phone\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Because it is all mobile responsive. Right? And I can open up the AI chat inside here, add an OpenAI API key, and, you know, I've got somebody that can log all my workouts or create new workouts for me as well right inside Directus. I don't have to code anything.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It's wild.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Well Well You know? I mean I\u003C\u002Fp>\u003Cp>Speaker 1: we got far. We got farther than it would have taken me if I was gonna do this myself.\u003C\u002Fp>\u003Cp>Speaker 0: Well, we'll give this, like, the the fair to Midland. Like, meh, like\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: It's okay.\u003C\u002Fp>\u003Cp>Speaker 1: What is it? Gladiator? Don't know if it's up or down yet. I'll just give it this side. Don't know whether to kill it or let it live.\u003C\u002Fp>\u003Cp>Speaker 0: There you go. Yeah. The alright. Yeah. Well, Matt, thanks for joining me.\u003C\u002Fp>\u003Cp>This is fun. Yeah. I do like, this is something that I I need myself personally, so I do kinda wanna keep working on it on the slide.\u003C\u002Fp>\u003Cp>Speaker 1: Hey, man.\u003C\u002Fp>\u003Cp>Speaker 0: This is fun. Great idea, man.\u003C\u002Fp>\u003Cp>Speaker 1: Everybody's stealing my ideas lately. I'm just kidding. Give me access to this. I I wanna see you know what? We can also build a scoreboard so we can see, like, who can how much more I work with, bro.\u003C\u002Fp>\u003Cp>Speaker 0: Bro, I used to say\u003C\u002Fp>\u003Cp>Speaker 1: power I know. I I can't. I've seen you in person. There's no way. You're you got me beat.\u003C\u002Fp>\u003Cp>Speaker 0: Short and squat, dude.\u003C\u002Fp>\u003Cp>Speaker 1: See them. Alright. Cool. Well, thanks, man. Appreciate it.\u003C\u002Fp>\u003Cp>And, yeah, this is awesome.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Thoroughly enjoyed it. Alright. Everybody out there, that is it for this episode of one app ten minutes. Thanks for joining.\u003C\u002Fp>\u003Cp>Stay tuned for more episodes.\u003C\u002Fp>","Alright. Alright. Alright. Welcome back to another episode of one app ten minutes. I'm your host Brian Gillespie. Today, we have got an extra special guest that I will introduce in a moment. But if you're new to the show, let's cover the rules. Ten minutes to plan and build and no more, no less. You animals couldn't sit for an hour, so here we are trying to build apps in ten minutes. Successfully or not, we will build the app or die trying. As for a special guest, right, mister Matt Miner from our marketing team at Directus. Matt, how are you, sir? Happy to have you on the show. Well, I was good. In the intro, you said I was very special, and then you just said I was special. So which is it? Very special or just a special? Very special? Good. Extra special. Yeah. How special do you wanna be? Just How special do you wanna be? Enough. You know? No. It's great to be here. Unlike other guests, I had a little heads up, so you couldn't catch me off guard with a random meeting. So, I'm Yeah. I mean, that's that's fun for me, certainly, to put people on the spot. The other thing that gives me a bit of anxiety is if you have had time to think on what we're actually gonna build Yeah. I certainly have no idea. You can thank Beth on our team for that. Thank you, Beth. I don't know if you're gonna watch this afterwards or in post production, but I appreciate you for putting me on the spot. Yeah. So, without further ado, you know, tell me what we're building so I can put it up here on the board. Alright. We're gonna build. Little backstory. Since having our second kid, my son, I can count on the number I can count the number of times we've slept through the night on, like, one hand. And for one for one and a half years, he he turns two in June. So, for, you know, obvious reasons, like our I've not been to the gym. You know, I've not been working out. I get winded when I walk up the stairs. So I've been one of the New Year's resolutions this year has been getting back to the gym. I need some sort of, like, fitness app, workout app sort of thing, to track workouts and things like that, as well as keep, like, kind of a running diary. And, if we wanna get, like, crazy advanced with it, I have a lot of ideas for it. But, yeah, hopefully, this is something you can build and and I can sell on the side. So that's my goal today. Jeez. Okay. I think a native app would be very difficult in ten minutes. We could certainly cook something up with Directus. So just to to prove to the folks there's no hand wavy magic here. Right? We've got a blank Directus back in. I do have cloud code set up for us with the direct Us MCP already connected, which is, hopefully going to help us move faster than the speed of light. But you know the drill. We're gonna start the clock, and then we'll plan this thing out and see how far we can get in ten minutes. Are you ready? I think I'm ready. He's born ready. Here we go. Alright. Ten minutes. Here we go. What sort of functionality do you want out of this thing? Alright. So, basically, I need to be able to, like, put workouts in ahead of ahead of the, when I actually go work out. So I'm thinking, like, Monday, Wednesday, Friday, I'll go to the gym. I'll do, like, a push day on Monday, pull day on Wednesday, and, like, a legs day on Friday. Right? Within that, like, just a basic thirty minute workout, so it'll be kind of repeatable with the exercises. And then just having a way to log, like, the weight that I do. And then if if we wanna get extra fancy with it, like, implementing some sort of, like, AI increase of, like, you know, a couple percentage points of the weight. So, like, next time when I go, there's a little bit more weight. I have to be able to rate it. So, like, if I can hit all of the reps during the workout workout, and if so, then, like, increase it by, like, 5% or something weight wise. Yeah. Basic functionality is that. Logging, you know, planning the workout, with sets and reps in the workout and then logging, like, easy, medium, hard for each one. Holy cow. Okay. Alright. No short order. Right? I'm not sure if you're using this tool yet or is it variety of tools? I've got this thing called Super Whisperer. I'm just gonna enable this because I suck at typing. Alright. And then I'm gonna talk. Hi, Claude. We are going to build a workout fitness app sort of thing for my friend, Matt. I'm gonna paste some notes on our data model, what the workout should look like and the different things that we need to, have inside the app. And I want you to quickly, as fast as you can, create a plan to add that to our direct us instance, using the MCP. Alright. So, we got a transcript here that would have taken me forever to type. I'm gonna paste this in. Oops. Let me make sure I copy this. Cool. And then let's go. Right? Now, normally, I would kick this over in plan mode, but we are extremely on the clock here. We got seven minutes and thirty seconds left. It should pick up the schema and existing collections from Directus. For one shotting. Is this We're we're just gonna have to one shot this. You know, I'm asking it to plan first, though. Right? I did say that. Quickly create a plan. So here we go. We got a plan. Matt's workout app. Alright. You got exercises. Cool. You got workouts, day of the week, target duration, workout exercises, junction table, workout logs, sets completed, exercise, default sets, default reps. Okay. So I see reps and stuff in there. We got the exercise progress. Exercise logs, that's where your your rating comes in, I guess. Optionally, create a flow. Alright. So let's answer these questions. Right? We want, LBs, an American LBs. In our annoying metric system. User User specific shared templates. Context to that would be what? If, like, I'd go to the gym with my wife, we could share, like, the same workout. The same workout app? Yeah. Maybe. User off, I guess. Let's do that. Specific exercises to prepopulate, come up with a list of exercises to populate. And, actually, we can have Claude do that separately. Rating scale, one to five or thumbs up or thumbs down? I would say easy, medium, hard. So, like, if easy, progress weight. If medium, keep weight. If hard, reduce weight or reps. If easy, increase, medium, increase? Medium, keep the same. And hard, reduce. Reduce reps. Alright. Five minutes. Let's see what it comes up with. It's gonna update the plan for us, and then, Has it already been five minutes? Yeah. This this goes quickly. Yes. Implement my guy. Go forth. What do you mean? So you could see already, like, the I I know how to click all these buttons and direct us, certainly. Right? I could go through, and I could do all this. And I've done it pretty quickly. But, AI is such a I it gives us so much leverage in this equation. That's the thing, though. Right? Like, does anybody actually enjoy building out the data models, like, on the back end now? Like, this it's it's kind of fun, I guess, like, putting it's like putting a puzzle together, but, like, it feels better if you just get a foundation and then be able to, like, tweak on it. Right? I guess, posing that question to you, like, do you like building out the back end or do you care about the front end? You know, I am I'm a results guy, man. Yes. You are. Do you know? I You better get results. Like the ship stuff. Yeah. I I I need this. On the spot. Like, I you know, I I am kind of sad on some level of, like, hey. It's not me doing the work anymore, but, you know, I've come to realize that, like, the the strength that I have is not actually writing the code or clicking the buttons. Right? It is putting the the stuff together. So we are at three forty three. What is Directus doing here? What is claw doing? Right? You can see it's creating fields for all the different relationships. It's adding those relationships. Now it's seeding the exercises. Yes. Let's create the default workout templates and create the flow to auto progress. So, again, this is this is a terrible way to build something for actual production in that, I've just got this set up in yellow mode, and, basically, I'm barely even reading these things. Yeah. We're I didn't even give you a chance to to comment on this. Right. But the the principles here are sound. Right? And that you've got a thinking partner in in something like Cloud Code or, you know, it could just be Cloud or Chad GPT. It doesn't really matter which AI you're using. It's It's helpful for thinking through these things. So if we take a look, right, now we've got our data model. We've got our exercise progress. We've got your different workouts with your different exercises already created here. Alright. You got your workout logs, which I'm assuming here's your workout. So Monday. Yeah. And that's the planning of it. Right? It's like yeah. Monday. Well, probably Oh, this is this is your yeah. The workout log. Workouts would be this is does this have your weight? No. It doesn't have the weights on it, does it? Yeah. So that part of it we're missing. Right? Maybe that is And I guess we aren't specific about that. Yeah. Well, there's weight at least in the logs. Yeah. So let's set up the Monday. Yesterday, Matt did this. Totally did it. Totally did it. I swear. Alright. So, you create your exercise log. You got a bench press, etcetera. Right? You completed five sets of five reps at two twenty five. Mhmm. Mhmm. Beast. Mhmm. Alright. So there you go. You got your exercise log. Let's check on what we're doing. Right? It is creating some operations now. So we should see, like, a flow in here. Should be creating these operations for it. Okay. We got a minute and twelve seconds left. I should have not said my sob story at the beginning. We'd have more time. But, I mean, this looks great. Right? Like, I think what's really interesting about it is that, you know, not even using Directus as, like, the interface for this, like, just slapping together a lovable front end or something that's much easier and accessible, like, from my mobile device than just, like, while I'm there, just blah blah blah. Yeah. Alright. So we're gonna go in, and we're gonna create this workout log again. It's Wednesday, pool day. Except you did it on Tuesday. It is we're gonna do hard incline. Oh, what's the pull? Lat pull downs. There you go. You had 20 sets. 20 reps. Crushed it. 110 pounds. I had my creosote right there. Let's see what happens. User exercise progress. Did we did we get an error on the flow? Oh, we got five seconds left. Know. Out of time. No. Run scripts. Cannot read properties. Okay. So, you know, Claude screwed up a bit on our run script somewhere in here where, you know, it's it's wrote some JavaScript code, which is nice. One of the nice things about the flows inside Directus is you can just write arbitrary JavaScript and run that. But there's a a few debugging issues. Right? So ten minutes in this case. And, you know, let's let's take a look. Right? Let's go to the tally. We've got got the workout exercises. We got the reps. We got your Monday, Wednesday, Friday, and we got the ability to log the workout. We got the rating the workouts. What we did not get is the automatic increase. So that logic, we did not get. And I would say that that So instead of the way it's interesting how I applied it to the full workout instead of the individual, like, exercises, which is probably why it got funky because it was probably waiting for us to put in all of the like, hey. Was this individual exercise hard or not? That's a good insight. Right? So it does show you I I I think I'm impressed with how far you can get into I mean that's right. Man, this is I mean, this is really the basis. You see a lot of apps out there like Fitbot or, like, Fitness AI, which are solid, but, you know, they're not customized to you. And being able to just, like, do this, get it custom, share it, you know, with whoever I'm working out with, it's powerful. Yeah. Another ten, fifteen minutes, we could have had, you know, nano banana generate images of these or we could have copied images. Like, diagrams of how how to do the exercises. Right? Yeah. Could create workout templates. Again, like, you could have, like, auto progression or auto calculation on there. And then even with the AI chat inside Directus. Right? Mhmm. Hey, man. Plan me a new workout. Right? Even if you let's say you you haven't built that, that front end yet Yeah. The native app that we're gonna build in ten minutes. Yo. You could still interact with this on your phone Yeah. Because it is all mobile responsive. Right? And I can open up the AI chat inside here, add an OpenAI API key, and, you know, I've got somebody that can log all my workouts or create new workouts for me as well right inside Directus. I don't have to code anything. Yeah. It's wild. Yeah. Well Well You know? I mean I we got far. We got farther than it would have taken me if I was gonna do this myself. Well, we'll give this, like, the the fair to Midland. Like, meh, like Yeah. It's okay. What is it? Gladiator? Don't know if it's up or down yet. I'll just give it this side. Don't know whether to kill it or let it live. There you go. Yeah. The alright. Yeah. Well, Matt, thanks for joining me. This is fun. Yeah. I do like, this is something that I I need myself personally, so I do kinda wanna keep working on it on the slide. Hey, man. This is fun. Great idea, man. Everybody's stealing my ideas lately. I'm just kidding. Give me access to this. I I wanna see you know what? We can also build a scoreboard so we can see, like, who can how much more I work with, bro. Bro, I used to say power I know. I I can't. I've seen you in person. There's no way. You're you got me beat. Short and squat, dude. See them. Alright. Cool. Well, thanks, man. Appreciate it. And, yeah, this is awesome. Yeah. Thoroughly enjoyed it. Alright. Everybody out there, that is it for this episode of one app ten minutes. Thanks for joining. Stay tuned for more episodes.","ba70a505-d112-4005-961a-306fd9c963e7",[312,313],"2d942561-61b4-4734-98e8-fb0093fc9799","54a3d149-4b85-4dcd-9a86-60157885f576",[],{"year":139,"number":316,"id":317,"episodes":318,"show":321},58,"9095d401-66bc-416a-997b-f038365ce5ed",[319,300,320],"2c5b1e3e-c12f-4dc5-aef8-52082d41c192","6352abf0-f785-4887-9ee8-44553ea3b55e",{"title":322,"slug":323},"100 Apps In 100 Hours","100-apps-100-hours",{"id":320,"slug":325,"vimeo_id":326,"description":327,"tile":328,"length":329,"resources":12,"people":12,"episode_number":264,"published":306,"title":330,"video_transcript_html":331,"video_transcript_text":332,"content":12,"seo":333,"status":19,"episode_people":334,"recommendations":338,"season":339},"book-rating-app","1163987834","Bryant is joined by Vicky and Beth with the goal of building a book rating app in just 10 minutes using the MCP.","702ee809-e53c-43db-a8ee-6c63901ef22d",16,"Book Rating App","\u003Cp>Speaker 0: Welcome back to yet another episode of one app, ten minutes, your favorite show where we build apps, clones, whatever. Interesting stuff in ten minutes or less. Used to be I had an hour to do these, but you guys have low attention spans now. So we're down to ten minutes. Super excited for this episode.\u003C\u002Fp>\u003Cp>Very, very excited. We've got, a couple of rules before we dive in. Ten minutes to plan and build. So no more, no less. And then number two, the anti rule.\u003C\u002Fp>\u003Cp>Use whatever you have at your disposal. And I have got two amazing people from our team at Directus at at my disposal today, Beth and Vicky. Welcome to the show, guys.\u003C\u002Fp>\u003Cp>Speaker 1: Thanks for asking.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. Yes. This is very exciting. Have you guys given any thought to what we're actually going to build today? I think You have a plan?\u003C\u002Fp>\u003Cp>We got a plan. Okay. That's good because I have a little idea what we're building, and that is, terrifying for me not knowing what we're actually going to build. So do you guys wanna lay it on me?\u003C\u002Fp>\u003Cp>Speaker 2: Yes. So we want to build a book rating app, because\u003C\u002Fp>\u003Cp>Speaker 1: in the\u003C\u002Fp>\u003Cp>Speaker 2: new year, we want to read more books.\u003C\u002Fp>\u003Cp>Speaker 0: And Book. Rating app. If I can ask, how did you guys come up with a a book rating app?\u003C\u002Fp>\u003Cp>Speaker 1: We're big on goals this year. So something Vicky and I have been talking about is, you know, just pull our goals out for the year. You gotta have a goal. You gotta have a purpose. You gotta have a track.\u003C\u002Fp>\u003Cp>You gotta gotta track your track your goals.\u003C\u002Fp>\u003Cp>Speaker 0: So is is book reading is, like, reading more books, like, near the top of the list? What what else is in consideration?\u003C\u002Fp>\u003Cp>Speaker 1: Oh, it's it's at the top of the list, for sure, for me at least. Vicky, have you got any other any other goals at the top of the list?\u003C\u002Fp>\u003Cp>Speaker 2: Lots of goals, lots of New Year's resolution, but this one's been trying to read more books for, like, the past ten years now. But\u003C\u002Fp>\u003Cp>Speaker 0: And today, we're gonna build the app that makes you read more books. I love it.\u003C\u002Fp>\u003Cp>Speaker 2: Keeps me accountable.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Alright. Well, you guys know how the show works. I'm going to start the timer. Basically, what we've got here is a totally blank instance of Directus.\u003C\u002Fp>\u003Cp>I've got, our Directus MCP connected so that we can, leverage some AI tools, but I'm gonna hit go. Are you guys ready?\u003C\u002Fp>\u003Cp>Speaker 1: Are you ready?\u003C\u002Fp>\u003Cp>Speaker 0: I I was born in. Sync. I know. That is, that's a good thing.\u003C\u002Fp>\u003Cp>Speaker 1: I'm jealous.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. So when it comes to our book rating app, what kind of functionality do you guys wanna see out of this thing?\u003C\u002Fp>\u003Cp>Speaker 1: So we need some book information. So that would be title, genre, author\u003C\u002Fp>\u003Cp>Speaker 0: Okay.\u003C\u002Fp>\u003Cp>Speaker 1: And then the name of genre.\u003C\u002Fp>\u003Cp>Speaker 0: You guys are going too fast for me. Alright. We need books. We need genres. We need author, which clearly I can't spell.\u003C\u002Fp>\u003Cp>Probably a good thing we're gonna use AI. And then we like, are you guys are gonna share this app? Like, are you going to compete for who reads the most books?\u003C\u002Fp>\u003Cp>Speaker 1: Sure. It'd be the leaderboard. Would\u003C\u002Fp>\u003Cp>Speaker 0: be good. Writing, comments, notes. We wanna build a\u003C\u002Fp>\u003Cp>Speaker 1: Live it.\u003C\u002Fp>\u003Cp>Speaker 0: Book leaderboard. Leaderboard. Book leaderboard. Okay. Genre, author.\u003C\u002Fp>\u003Cp>Cool.\u003C\u002Fp>\u003Cp>Speaker 1: Maybe some notes. Yeah. You've got comments. Perfect.\u003C\u002Fp>\u003Cp>Speaker 0: Comments, notes. Notes is fine. We'll change that up. Alright. So, yeah, this is just the the functionality that we need.\u003C\u002Fp>\u003Cp>The first thing I'm gonna do is hop into Claude, and I've I've got this tool called Super Whisperer that I've been using that just transcribes my voice. We're pressed for time. So here we go. Hi, Claude. I want you to help us build a book rating app for my two amazing colleagues.\u003C\u002Fp>\u003Cp>They wanna read more books in the new year. I'm going to paste the outline of a Directus schema. I want you to fill that out, and then I want you to, plan with me quickly and then add it to our directus instance for us. Alright. So click a button.\u003C\u002Fp>\u003Cp>This thing transcribes everything for me with nice punctuation and no misspellings. You know, I've got these these sausage fingers I complain about all the time. Alright. So we could see that the MCP is now looking at the direct to schema, which is, should be blank in this case. And now Claude should oh, I gotta give it the actual outline, though, don't I?\u003C\u002Fp>\u003Cp>That makes sense. There we go.\u003C\u002Fp>\u003Cp>Speaker 1: It's very polite about it.\u003C\u002Fp>\u003Cp>Speaker 0: It is very polite. Yeah. I do I I'm not sure if if you're a person who who does the please and thank you, but, you know, I do as well. I'm like, hey. This could end in April.\u003C\u002Fp>\u003Cp>I don't I don't wanna be terminated. So Awesome. Alright. So we got a couple of questions, guys. Genre collections.\u003C\u002Fp>\u003Cp>Let's do a separate genre collection. I'll make that call for you. As far as the rating scale, do we wanna do one to five stars, one to ten, one to a 100?\u003C\u002Fp>\u003Cp>Speaker 1: One to five stars, please. One to five stars.\u003C\u002Fp>\u003Cp>Speaker 0: One to five stars. Ratings should track which user for sure. Leaderboard leaderboard. I'll set up. Do you guys care about cover image, publication year?\u003C\u002Fp>\u003Cp>Let's\u003C\u002Fp>\u003Cp>Speaker 1: give me to description\u003C\u002Fp>\u003Cp>Speaker 0: for the book. Cool. Alright. Go, Claude. Go.\u003C\u002Fp>\u003Cp>We've got, six minutes and\u003C\u002Fp>\u003Cp>Speaker 1: We're doing well on time. I\u003C\u002Fp>\u003Cp>Speaker 0: think we are. Yeah. Certainly. Ready. Go for it.\u003C\u002Fp>\u003Cp>Alright. So the native MCP has access to create collections, relationships, and also fields inside your direct assistance. So we could see here here, this is using Claude. Of course, if you're not using Claude code, which is, you know, more technical, you know, the terminal UI is maybe not the best for some of this stuff. If you're using cursor or chat GPT or just the cloud AI, you can do these same operations.\u003C\u002Fp>\u003Cp>Right? The other thing I'm doing here is just enabling yellow mode, which, Jonathan, if you're watching this one, please don't get mad at me. This is just a test instance. Never recommend that in production because, you know, any schema changes, you can lose your data. Alright.\u003C\u002Fp>\u003Cp>So what do we see? How are we looking, Beth? I think authors good. Book ratings, date created, the user. Cool.\u003C\u002Fp>\u003Cp>Books. Alright. Let's see if this actually works. Cool. Okay.\u003C\u002Fp>\u003Cp>What's the what's the first one\u003C\u002Fp>\u003Cp>Speaker 1: you're looking? Listening to an audiobook called Thinking in Bets.\u003C\u002Fp>\u003Cp>Speaker 0: Thinking in Bets. Does it\u003C\u002Fp>\u003Cp>Speaker 1: making smart decisions without all the data. It was actually, recommended by another one of our colleagues.\u003C\u002Fp>\u003Cp>Speaker 0: There we go. Alright. Who is the author?\u003C\u002Fp>\u003Cp>Speaker 1: I have my phone ready because I knew this was coming. Annie Duke.\u003C\u002Fp>\u003Cp>Speaker 0: Annie Duke? Annie Duke?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Beautiful.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. It is, I don't I don't know the genre, but, are you far enough along to are you far enough along to give it a rating?\u003C\u002Fp>\u003Cp>Speaker 1: So far, I like it. I think it's it's hovering around, like, like, a three or four. Let's see four for now. It starts off. I like it.\u003C\u002Fp>\u003Cp>I like it.\u003C\u002Fp>\u003Cp>Speaker 0: It's interesting. There we go. Alright. So there we go. We've got a book.\u003C\u002Fp>\u003Cp>We've got some book ratings. We've got an author. We've got a genre. We could see all that being created. Let's just hop in here and, like, take Claude, populate our data with some books and genres plus authors.\u003C\u002Fp>\u003Cp>So while it does that, we got three minutes and fifty six seconds. You know, I could certainly give you time. A plethora. That's a great way to describe it. Alright.\u003C\u002Fp>\u003Cp>Let's let's work on creating a dashboard so we could compare you guys. Right? Who is going to win. So book ratings, we we see the user there. I've it's locked to myself.\u003C\u002Fp>\u003Cp>Okay. I'm just gonna quickly change that to be not read only so I can change it. And I'm gonna create you guys as users real quick. Obviously, you're not gonna be able to log in, but I wanna have you guys in here. Alright.\u003C\u002Fp>\u003Cp>This is best book rating. Alright. Vicky, do you or what book are you reading?\u003C\u002Fp>\u003Cp>Speaker 2: Well, I watched the housemaid, the movie, but I realized it was a book too. So I'm starting to read that to compare to the movie itself. Sorry. I'm a movie first.\u003C\u002Fp>\u003Cp>Speaker 1: It's good because then you can, like, picture them.\u003C\u002Fp>\u003Cp>Speaker 0: Who is the who's the author?\u003C\u002Fp>\u003Cp>Speaker 2: The author is Freda McFadden. So f r e I d a, and then last name Freda\u003C\u002Fp>\u003Cp>Speaker 0: McFadden. Alright. Look at those typing skills. And that is I don't know what the genre is.\u003C\u002Fp>\u003Cp>Speaker 2: Like, thriller suspense.\u003C\u002Fp>\u003Cp>Speaker 0: Thriller.\u003C\u002Fp>\u003Cp>Speaker 1: Thriller is good.\u003C\u002Fp>\u003Cp>Speaker 0: Not to be confused with the Michael Jackson song. Okay. Alright. So you both have two ratings in here. Let's create the dashboard.\u003C\u002Fp>\u003Cp>Dashboard. We'll just call it dashboard, which bragging on my spelling, that is totally misspelled. Alright. So we are going to add a dashboard. Let's check-in on Claude.\u003C\u002Fp>\u003Cp>Go ahead. Create a lot. Fire away, my friend. Create some book ratings for the users, Beth and Vicky. Let's see if it'll do that while we're doing this.\u003C\u002Fp>\u003Cp>Alright. So we wanna see who has read the most books.\u003C\u002Fp>\u003Cp>Speaker 1: Beth.\u003C\u002Fp>\u003Cp>Speaker 0: Let's just do a simple metric, book rating. What is the field that we're gonna count? Let's just count the ID of the book. So we're gonna count distinct IDs, and we're gonna add a filter where the user first name contains Beth. Alright.\u003C\u002Fp>\u003Cp>This should give us that. We'll add a header. This is best books. Bath pick a color. 55.\u003C\u002Fp>\u003Cp>No pressure.\u003C\u002Fp>\u003Cp>Speaker 1: Orange. Orange. Orange. Orange. Thank you.\u003C\u002Fp>\u003Cp>Speaker 0: GraphQL validation error. Why does it matter like that? Field. Oh, I didn't select a field. Best books.\u003C\u002Fp>\u003Cp>Alright. We're gonna duplicate this. And now we got Vicky. Vicky, pick a color.\u003C\u002Fp>\u003Cp>Speaker 2: Green.\u003C\u002Fp>\u003Cp>Speaker 0: Green. Vicky's books. There you go. You got zero. Looks like Beth wins.\u003C\u002Fp>\u003Cp>Can't query the user's collection directly. Do we have some books in here? Let's alright. Quickly, five seconds left. Come on.\u003C\u002Fp>\u003Cp>Let's help Vicky out. Book pick a book. Oh, Vicky. Of course. There you go.\u003C\u002Fp>\u003Cp>Oh, no. The rating is required. Vicky likes it. It is a five. There you go.\u003C\u002Fp>\u003Cp>And boom. No. Refresh. Still doesn't like you. What happened?\u003C\u002Fp>\u003Cp>Speaker 1: I think you didn't assign the user to be away from\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It's been a preset me. So Yeah. It looks like I read more books than both of of you guys in this scenario. But alright.\u003C\u002Fp>\u003Cp>It will end on a one to one draw. This is the book rating app. You know, I think it would have been fun to maybe put together, like, a front end for this, but, the fact that we could put this together super quickly with you guys in in ten minutes, I think that's a win. Do we\u003C\u002Fp>\u003Cp>Speaker 1: Definitely a win. I feel like we should insert the win, little little frame. I think it would be really good if we had, you know, they had do, like, the top 100 books ever or, like, the best 500 books ever. You could so easily, like, include all of those books and just tick them off and rate it that way.\u003C\u002Fp>\u003Cp>Speaker 0: Is that what you guys are gonna do? Like No. Probably not.\u003C\u002Fp>\u003Cp>Speaker 1: I have I have my collection here and in the library a lot these days. So\u003C\u002Fp>\u003Cp>Speaker 0: That's very ambitious. 500 books this year.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, no. Absolutely not. I I said 52 books this year, one a week.\u003C\u002Fp>\u003Cp>Speaker 0: 52 a week. 52 a week.\u003C\u002Fp>\u003Cp>Speaker 1: Really? I'm I'm I'm aiming for a 100, but I don't want to I don't want two a week. Because it's also, like, audiobooks and short books and poetry books. Less screen time for me is the aim. Vicky, have you got a number in mind?\u003C\u002Fp>\u003Cp>Speaker 2: I was trying to aim for 5 a year, but\u003C\u002Fp>\u003Cp>Speaker 1: it's so soft. It's good.\u003C\u002Fp>\u003Cp>Speaker 0: $5.05 a year. Yeah. I'd look if you're trying to hit 52, I've got a bunch of kids' books that I can like, my kids have exhausted. I can send them don't just use. You could you could blow through them, like, three, four, five a day.\u003C\u002Fp>\u003Cp>No no big deal. Beautiful.\u003C\u002Fp>\u003Cp>Speaker 2: There's no rules to that leaderboard, so I might as well take it.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. No validation. It's no no doctor Seuss. No no big deal. Anyway, alright, guys.\u003C\u002Fp>\u003Cp>Thank you so\u003C\u002Fp>\u003Cp>Speaker 1: much, Brian.\u003C\u002Fp>\u003Cp>Speaker 0: I've I've really enjoyed this. I hope you guys come back on. We can expand the book rating app. And the next next challenge we'll do is actually making you guys read the book somehow. So I I don't know how to build the software to do that yet, but next time.\u003C\u002Fp>\u003Cp>Speaker 1: We'll get we'll get AI to read it for us and take it to digest them and to feed them back to us. It'll be fine.\u003C\u002Fp>\u003Cp>Speaker 0: There you go. That's an idea. Alright. Thanks for coming on. Really enjoyed it, and stay tuned for the next episode of one app, ten minutes.\u003C\u002Fp>\u003Cp>Speaker 1: Bye. See you guys.\u003C\u002Fp>","Welcome back to yet another episode of one app, ten minutes, your favorite show where we build apps, clones, whatever. Interesting stuff in ten minutes or less. Used to be I had an hour to do these, but you guys have low attention spans now. So we're down to ten minutes. Super excited for this episode. Very, very excited. We've got, a couple of rules before we dive in. Ten minutes to plan and build. So no more, no less. And then number two, the anti rule. Use whatever you have at your disposal. And I have got two amazing people from our team at Directus at at my disposal today, Beth and Vicky. Welcome to the show, guys. Thanks for asking. Yes. Yes. This is very exciting. Have you guys given any thought to what we're actually going to build today? I think You have a plan? We got a plan. Okay. That's good because I have a little idea what we're building, and that is, terrifying for me not knowing what we're actually going to build. So do you guys wanna lay it on me? Yes. So we want to build a book rating app, because in the new year, we want to read more books. And Book. Rating app. If I can ask, how did you guys come up with a a book rating app? We're big on goals this year. So something Vicky and I have been talking about is, you know, just pull our goals out for the year. You gotta have a goal. You gotta have a purpose. You gotta have a track. You gotta gotta track your track your goals. So is is book reading is, like, reading more books, like, near the top of the list? What what else is in consideration? Oh, it's it's at the top of the list, for sure, for me at least. Vicky, have you got any other any other goals at the top of the list? Lots of goals, lots of New Year's resolution, but this one's been trying to read more books for, like, the past ten years now. But And today, we're gonna build the app that makes you read more books. I love it. Keeps me accountable. Okay. Alright. Well, you guys know how the show works. I'm going to start the timer. Basically, what we've got here is a totally blank instance of Directus. I've got, our Directus MCP connected so that we can, leverage some AI tools, but I'm gonna hit go. Are you guys ready? Are you ready? I I was born in. Sync. I know. That is, that's a good thing. I'm jealous. Alright. So when it comes to our book rating app, what kind of functionality do you guys wanna see out of this thing? So we need some book information. So that would be title, genre, author Okay. And then the name of genre. You guys are going too fast for me. Alright. We need books. We need genres. We need author, which clearly I can't spell. Probably a good thing we're gonna use AI. And then we like, are you guys are gonna share this app? Like, are you going to compete for who reads the most books? Sure. It'd be the leaderboard. Would be good. Writing, comments, notes. We wanna build a Live it. Book leaderboard. Leaderboard. Book leaderboard. Okay. Genre, author. Cool. Maybe some notes. Yeah. You've got comments. Perfect. Comments, notes. Notes is fine. We'll change that up. Alright. So, yeah, this is just the the functionality that we need. The first thing I'm gonna do is hop into Claude, and I've I've got this tool called Super Whisperer that I've been using that just transcribes my voice. We're pressed for time. So here we go. Hi, Claude. I want you to help us build a book rating app for my two amazing colleagues. They wanna read more books in the new year. I'm going to paste the outline of a Directus schema. I want you to fill that out, and then I want you to, plan with me quickly and then add it to our directus instance for us. Alright. So click a button. This thing transcribes everything for me with nice punctuation and no misspellings. You know, I've got these these sausage fingers I complain about all the time. Alright. So we could see that the MCP is now looking at the direct to schema, which is, should be blank in this case. And now Claude should oh, I gotta give it the actual outline, though, don't I? That makes sense. There we go. It's very polite about it. It is very polite. Yeah. I do I I'm not sure if if you're a person who who does the please and thank you, but, you know, I do as well. I'm like, hey. This could end in April. I don't I don't wanna be terminated. So Awesome. Alright. So we got a couple of questions, guys. Genre collections. Let's do a separate genre collection. I'll make that call for you. As far as the rating scale, do we wanna do one to five stars, one to ten, one to a 100? One to five stars, please. One to five stars. One to five stars. Ratings should track which user for sure. Leaderboard leaderboard. I'll set up. Do you guys care about cover image, publication year? Let's give me to description for the book. Cool. Alright. Go, Claude. Go. We've got, six minutes and We're doing well on time. I think we are. Yeah. Certainly. Ready. Go for it. Alright. So the native MCP has access to create collections, relationships, and also fields inside your direct assistance. So we could see here here, this is using Claude. Of course, if you're not using Claude code, which is, you know, more technical, you know, the terminal UI is maybe not the best for some of this stuff. If you're using cursor or chat GPT or just the cloud AI, you can do these same operations. Right? The other thing I'm doing here is just enabling yellow mode, which, Jonathan, if you're watching this one, please don't get mad at me. This is just a test instance. Never recommend that in production because, you know, any schema changes, you can lose your data. Alright. So what do we see? How are we looking, Beth? I think authors good. Book ratings, date created, the user. Cool. Books. Alright. Let's see if this actually works. Cool. Okay. What's the what's the first one you're looking? Listening to an audiobook called Thinking in Bets. Thinking in Bets. Does it making smart decisions without all the data. It was actually, recommended by another one of our colleagues. There we go. Alright. Who is the author? I have my phone ready because I knew this was coming. Annie Duke. Annie Duke? Annie Duke? Yeah. Beautiful. Alright. It is, I don't I don't know the genre, but, are you far enough along to are you far enough along to give it a rating? So far, I like it. I think it's it's hovering around, like, like, a three or four. Let's see four for now. It starts off. I like it. I like it. It's interesting. There we go. Alright. So there we go. We've got a book. We've got some book ratings. We've got an author. We've got a genre. We could see all that being created. Let's just hop in here and, like, take Claude, populate our data with some books and genres plus authors. So while it does that, we got three minutes and fifty six seconds. You know, I could certainly give you time. A plethora. That's a great way to describe it. Alright. Let's let's work on creating a dashboard so we could compare you guys. Right? Who is going to win. So book ratings, we we see the user there. I've it's locked to myself. Okay. I'm just gonna quickly change that to be not read only so I can change it. And I'm gonna create you guys as users real quick. Obviously, you're not gonna be able to log in, but I wanna have you guys in here. Alright. This is best book rating. Alright. Vicky, do you or what book are you reading? Well, I watched the housemaid, the movie, but I realized it was a book too. So I'm starting to read that to compare to the movie itself. Sorry. I'm a movie first. It's good because then you can, like, picture them. Who is the who's the author? The author is Freda McFadden. So f r e I d a, and then last name Freda McFadden. Alright. Look at those typing skills. And that is I don't know what the genre is. Like, thriller suspense. Thriller. Thriller is good. Not to be confused with the Michael Jackson song. Okay. Alright. So you both have two ratings in here. Let's create the dashboard. Dashboard. We'll just call it dashboard, which bragging on my spelling, that is totally misspelled. Alright. So we are going to add a dashboard. Let's check-in on Claude. Go ahead. Create a lot. Fire away, my friend. Create some book ratings for the users, Beth and Vicky. Let's see if it'll do that while we're doing this. Alright. So we wanna see who has read the most books. Beth. Let's just do a simple metric, book rating. What is the field that we're gonna count? Let's just count the ID of the book. So we're gonna count distinct IDs, and we're gonna add a filter where the user first name contains Beth. Alright. This should give us that. We'll add a header. This is best books. Bath pick a color. 55. No pressure. Orange. Orange. Orange. Orange. Thank you. GraphQL validation error. Why does it matter like that? Field. Oh, I didn't select a field. Best books. Alright. We're gonna duplicate this. And now we got Vicky. Vicky, pick a color. Green. Green. Vicky's books. There you go. You got zero. Looks like Beth wins. Can't query the user's collection directly. Do we have some books in here? Let's alright. Quickly, five seconds left. Come on. Let's help Vicky out. Book pick a book. Oh, Vicky. Of course. There you go. Oh, no. The rating is required. Vicky likes it. It is a five. There you go. And boom. No. Refresh. Still doesn't like you. What happened? I think you didn't assign the user to be away from Yeah. It's been a preset me. So Yeah. It looks like I read more books than both of of you guys in this scenario. But alright. It will end on a one to one draw. This is the book rating app. You know, I think it would have been fun to maybe put together, like, a front end for this, but, the fact that we could put this together super quickly with you guys in in ten minutes, I think that's a win. Do we Definitely a win. I feel like we should insert the win, little little frame. I think it would be really good if we had, you know, they had do, like, the top 100 books ever or, like, the best 500 books ever. You could so easily, like, include all of those books and just tick them off and rate it that way. Is that what you guys are gonna do? Like No. Probably not. I have I have my collection here and in the library a lot these days. So That's very ambitious. 500 books this year. Oh, no. Absolutely not. I I said 52 books this year, one a week. 52 a week. 52 a week. Really? I'm I'm I'm aiming for a 100, but I don't want to I don't want two a week. Because it's also, like, audiobooks and short books and poetry books. Less screen time for me is the aim. Vicky, have you got a number in mind? I was trying to aim for 5 a year, but it's so soft. It's good. $5.05 a year. Yeah. I'd look if you're trying to hit 52, I've got a bunch of kids' books that I can like, my kids have exhausted. I can send them don't just use. You could you could blow through them, like, three, four, five a day. No no big deal. Beautiful. There's no rules to that leaderboard, so I might as well take it. Yeah. No validation. It's no no doctor Seuss. No no big deal. Anyway, alright, guys. Thank you so much, Brian. I've I've really enjoyed this. I hope you guys come back on. We can expand the book rating app. And the next next challenge we'll do is actually making you guys read the book somehow. So I I don't know how to build the software to do that yet, but next time. We'll get we'll get AI to read it for us and take it to digest them and to feed them back to us. It'll be fine. There you go. That's an idea. Alright. Thanks for coming on. Really enjoyed it, and stay tuned for the next episode of one app, ten minutes. Bye. See you guys.","9e7335b9-89fb-4faf-9cbe-11d2216e51f0",[335,336,337],"db77dc81-834c-4fbe-99b4-c17d40fc7bf1","2c87cbd3-9b86-4473-ad16-859d4dc73cb4","9f92db51-afe2-421f-847e-8a71cc526ce8",[],{"year":139,"number":316,"id":317,"episodes":340,"show":341},[319,300,320],{"title":322,"slug":323},{"id":319,"slug":343,"vimeo_id":344,"description":345,"tile":346,"length":305,"resources":12,"people":12,"episode_number":13,"published":306,"title":347,"video_transcript_html":348,"video_transcript_text":349,"content":12,"seo":350,"status":19,"episode_people":351,"recommendations":355,"season":356},"new-years-resolution-bingo-generator","1163992739","Bryant is joined by Marc and Alvaro with the goal of building a goal bingo app in just 10 minutes using the MCP.","1e2aef3d-30ec-4672-be1e-f439efe7e045","New Years Resolution Bingo Generator","\u003Cp>Speaker 0: Alright, viewers. Welcome to, yet another episode of 100 app, 100 I don't know. No. No. No.\u003C\u002Fp>\u003Cp>One app in ten minutes. Right? We are doing the remix version today where we have ten minutes to build and plan plan and build an amazing app clone, crazy suggestion, and I have no idea what we're gonna do. So the rules. Right?\u003C\u002Fp>\u003Cp>Ten minutes to plan and build. No more, no less. How we're gonna do that? We are going to use some, amazing tools that we have built into Directus. And then, rule number two, the anti rule.\u003C\u002Fp>\u003Cp>Use whatever you've got at your disposal. Today, I've got two awesome dudes at my disposal, mister Alvaro and Mark from our team here at Directus. No strangers to the VUE community. Welcome to the show, gents.\u003C\u002Fp>\u003Cp>Speaker 1: Thanks for having us, Bryant.\u003C\u002Fp>\u003Cp>Speaker 2: Thank you very much for the nice intro. Happy, to be here.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. No. I'm super excited. Have you guys given any thought to what we're what we're gonna build?\u003C\u002Fp>\u003Cp>Speaker 2: I think Mark has some idea though.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So yesterday, we talked a little bit. I talked with Ava what we could build. And, I don't know if if I showed it to you, Brian, but on my website, I have a, instead of new year for solutions, I have new year's bingo cards. So you have five by five grid of stuff I want to do in the year.\u003C\u002Fp>\u003Cp>And if I get at least one in a row, so diagonal or horizontal or vertical, I already have bingo and it's a success. So I don't have to do all of them. And if you go to mark.dev\u002Fbingo\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Let's check it out, guys.\u003C\u002Fp>\u003Cp>Speaker 1: You can it's still since it's just well, now February, not a lot has happened there. But\u003C\u002Fp>\u003Cp>Speaker 2: But it's a it's a really nice way to actually do some of the New Year's resolution. I always get the press at the end of the year like I have done, like, a quarter of them.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I love it. Alright. So hey. This is neat, man.\u003C\u002Fp>\u003Cp>I I miss Yep.\u003C\u002Fp>\u003Cp>Speaker 1: And each of them can be either, like, you did it or you didn't do it or it can be progressive. Like, read six books and you are, like, one books, two books, three books in. And I think I also have, like, subtasks if we can make that work. Like, if one one, let's say, one bingo item has a few sub items as well. Like, don't have an example now, but that would also\u003C\u002Fp>\u003Cp>Speaker 0: be cool. Gotcha. Okay. New Year's resolution. Bingo card generator.\u003C\u002Fp>\u003Cp>Alright. That's what we're doing. This is gonna be amazing. This should be fun. What color are you guys feeling?\u003C\u002Fp>\u003Cp>Purple, pink?\u003C\u002Fp>\u003Cp>Speaker 2: I go I go purple. Blue. Oh, purple.\u003C\u002Fp>\u003Cp>Speaker 0: Purple. There we go.\u003C\u002Fp>\u003Cp>Speaker 1: Direct is purple. Nice.\u003C\u002Fp>\u003Cp>Speaker 0: Direct is purple. Alright, guys. Alright. So I'm sure you've seen the show. We're gonna start the clock.\u003C\u002Fp>\u003Cp>We got ten minutes to plan and build this thing. Let's do it. Alright. So, the first thing I usually do here is cover requirements. Right?\u003C\u002Fp>\u003Cp>So what are the requirements we need out of this? Right? We need to generate bingo cards. Like, what do you what were you calling those?\u003C\u002Fp>\u003Cp>Speaker 1: Like, items probably or\u003C\u002Fp>\u003Cp>Speaker 0: Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Goals. Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: Items. Yeah. Like a grid of of items.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. So we got some goals. Those are what kinda fields are you tracking on those? Just the name of the goal?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. A name description and then the status.\u003C\u002Fp>\u003Cp>Speaker 0: Status of the goal. Progress. Progress. Is it are you status and progress interchangeable?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I guess if you like the if the progress is under percent, the status\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Yeah. Yeah. Yeah. Got it.\u003C\u002Fp>\u003Cp>Okay. And then we've got we've got goals. You've got what? Items underneath the goals? What do you do?\u003C\u002Fp>\u003Cp>We want, like, subtasks, like, if it's\u003C\u002Fp>\u003Cp>Speaker 1: You you can have subtasks. Let's see if there's one that has subtasks. I don't remember.\u003C\u002Fp>\u003Cp>Speaker 0: Tasks. It's called test. Alright. So the tasks would\u003C\u002Fp>\u003Cp>Speaker 1: play into into progress as well. I guess.\u003C\u002Fp>\u003Cp>Speaker 0: Into goal. And then the task completed increases progress. Cool. Alright. And task needs what?\u003C\u002Fp>\u003Cp>Name? Description? No. Just name? Date date probably.\u003C\u002Fp>\u003Cp>Speaker 1: Maybe the, the item can have a a completed ad. Yeah. They've completed as well for the task for the, item on top. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. And then we we wanna try to get a front end set up for this as well. Yeah. Alright. And we need a front end to display the bingo cards.\u003C\u002Fp>\u003Cp>Alright. This could be a stretch in seven minutes now. Let's see how we do. Alright. So what are we using today?\u003C\u002Fp>\u003Cp>Right? We've got a blank directus project. We've got Claude Code over here. Let's dive into it. Alright?\u003C\u002Fp>\u003Cp>I'm going to I'm not sure what you guys have been coding with. I've been using Super Whisper. I dig it. Hi. How are you doing?\u003C\u002Fp>\u003Cp>Alright, guys. We are building a New Year's resolution bingo card generator. I'm gonna copy and paste the data model that we want. You have access to a direct assistance. I want you to create our schema for that.\u003C\u002Fp>\u003Cp>We're also going to be building a front end to display the bingo cards. Let me know what questions you have. Let's create a plan. Alright. So this is crunching the transcript for that right now.\u003C\u002Fp>\u003Cp>Cool. There we go. I'll just, copy and paste this. Hopefully, we'll get some something good out of it. And we're gonna ask Claude Code to plan.\u003C\u002Fp>\u003Cp>Alright. So now we've got the schema. So we've got the direct us MCP connected to this thing. And I I think you guys have had a chance to try this out already. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think Avro has. I haven't.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Play with it in the morning. It's gonna create the collections, the schemas for you.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Alright. So it's got a fresh direction. No custom collections. Alright.\u003C\u002Fp>\u003Cp>And I can zoom in just a little bit more so we could see this. What is the plan? And this is probably one of my favorite parts about this thing where it will prompt you for questions. Direct us flow, that's what we wanna do there. Vanilla JS.\u003C\u002Fp>\u003Cp>Yeah. That's what we'll do. What do you guys think? Five by five grid? Four by four?\u003C\u002Fp>\u003Cp>Speaker 1: We we can do also four by four so we don't have to come up with 25 things.\u003C\u002Fp>\u003Cp>Speaker 0: Amazing. Right? We got five minutes left.\u003C\u002Fp>\u003Cp>Speaker 2: You you can say to the MCP, hey, cloud, get, your twenty twenty six, bingo.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, that was cool.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Alright. Public read, that's fine. Anyone can view those.\u003C\u002Fp>\u003Cp>Cool. Alright. And now, hopefully, this thing should have a plan.\u003C\u002Fp>\u003Cp>Speaker 2: I wonder which resolutions Cloud Code could have.\u003C\u002Fp>\u003Cp>Speaker 0: I don't know. Let's see. We'll we'll spin that up in an in a\u003C\u002Fp>\u003Cp>Speaker 1: new find out.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Cool. Right? Here's the direct to schema. There's our it's gonna create a flow.\u003C\u002Fp>\u003Cp>It's gonna create the front end. Sounds good. Let's let's roll with it. Right? I don't know what we're actually doing other than just talking this through here, but, I'm curious to see just how this thing works.\u003C\u002Fp>\u003Cp>I've you know, of course, like, spent a ton of time testing and building the MCP, but I've not spent a ton of time using it with, the latest Opus four five model. Alright. So it is checked the existing schema. Now we are it should start implementing. Yes.\u003C\u002Fp>\u003Cp>Please just start jamming on here. And if I refresh, now we should see some collections start to come in to the direct instance. We should see some collections. Start to come into the direct assistance. There we go.\u003C\u002Fp>\u003Cp>Okay. Alright. Oh, nice. I was just worried that I did something wrong. So we got our goals.\u003C\u002Fp>\u003Cp>We got our tasks. Amazing. Right? Now I could go in. We could potentially create some new ones if we need.\u003C\u002Fp>\u003Cp>One of the things that I like about this is it, like it seems like the anthropic models do a better job of, like, actually putting together a cohesive form than than, like, the OpenAI wants. So it's going through creating relations and fields. Alright, guys. So in this other one, create, some New Year's resolutions for yourself, Claude. Alright.\u003C\u002Fp>\u003Cp>You guys have any more guidance for this thing?\u003C\u002Fp>\u003Cp>Speaker 1: They should follow this the smart principle, probably.\u003C\u002Fp>\u003Cp>Speaker 0: Follow the smart principle. What's the smart principle?\u003C\u002Fp>\u003Cp>Speaker 1: Now you got me. So it's like measurable, achievable.\u003C\u002Fp>\u003Cp>Speaker 0: I know what you're talking about now. Yeah. The smart goals. Yeah. Yeah.\u003C\u002Fp>\u003Cp>And include the add them to the goals and tasks inside.\u003C\u002Fp>\u003Cp>Speaker 1: For the for me, the most important one is always measurable. You have to be able to measure what you do. If not, you lose yourself.\u003C\u002Fp>\u003Cp>Speaker 2: You lose yourself. That's so funny. It's okay.\u003C\u002Fp>\u003Cp>Speaker 0: That is very poetic. I love it, man. Alright. So it looks like okay. Yeah.\u003C\u002Fp>\u003Cp>I was just making sure we've got the relationship created correctly there. Alright. It is going to so we got two claws going. We got two minutes here. Let's see.\u003C\u002Fp>\u003Cp>I can see their goals and tasks.\u003C\u002Fp>\u003Cp>Speaker 1: Alright.\u003C\u002Fp>\u003Cp>Speaker 2: This is the next development, man. Right? Resting to\u003C\u002Fp>\u003Cp>Speaker 0: the next development. Yeah. This thing is going to yeah. I need to enter YOLO mode so we can actually, have this thing not stop to do these calls. But, behind the scenes, right, it is building this progress calculator flow.\u003C\u002Fp>\u003Cp>And and flows are\u003C\u002Fp>\u003Cp>Speaker 2: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: A a nice piece of functionality. It can be a little time consuming to set up, like, complex flows via the UI. So having direct us put these together, is, yeah, definitely time saving. Right? That's probably, like, five, ten steps there.\u003C\u002Fp>\u003Cp>Yes. Create those items. Alright. Let's see what we've got. Are we gonna get to the front end for this thing?\u003C\u002Fp>\u003Cp>I don't know if we are, man. I should've had Bryant. Should've had, Claude do that first. It's connecting the operations. Claude,\u003C\u002Fp>\u003Cp>Speaker 1: you need\u003C\u002Fp>\u003Cp>Speaker 0: to go faster, my friend. Alright. So what are the what are the goals that Claude set for itself? This should be interesting.\u003C\u002Fp>\u003Cp>Speaker 2: Put that description, Steven.\u003C\u002Fp>\u003Cp>Speaker 0: I'll reduce average response latency by 20%, Achieve 95% task completion rate without clarification. What an interesting goal. Here's the the individual tasks. And, I'll\u003C\u002Fp>\u003Cp>Speaker 2: And that was the end there. The front end.\u003C\u002Fp>\u003Cp>Speaker 0: Now it's doing it. No. Let me open this test project up. Is it going to have enough time? Yes.\u003C\u002Fp>\u003Cp>Proceed. New Year's resolution. Bingo. Oh, no. We ran out of time.\u003C\u002Fp>\u003Cp>So close. MCP connection should have access. No need to set up. I think, you know, this was so close, guys. I'm just going to it's against the rules, but you know what?\u003C\u002Fp>\u003Cp>We can make up our own rules here. I am just going to give access here to see and see if this will actually finish. Of course. There it is, man. The API permissions got us.\u003C\u002Fp>\u003Cp>We could see the bingo card here. There's the individual tasks. Ten minutes, full working back end with permissions, so close to a working front end. It did\u003C\u002Fp>\u003Cp>Speaker 1: pretty cool.\u003C\u002Fp>\u003Cp>Speaker 0: This is this is very cool. Right?\u003C\u002Fp>\u003Cp>Speaker 2: Even even with the subtask because that that wasn't an extra thing. Like, now it's the only iteration. Like, put the progress in the front end and\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I'm very curious to see. Right? It's already got, it looks like it maybe did it miss some of the flows? Right?\u003C\u002Fp>\u003Cp>So the thing to take away here is obviously, like, you could build incredibly quickly with Directus and MCP, and this is not loading, probably because of my computer. Just hates running all these Docker containers locally. What is going on?\u003C\u002Fp>\u003Cp>Speaker 2: How many do you have?\u003C\u002Fp>\u003Cp>Speaker 0: There's probably, like, five or 10 running at the moment, like, different instances. And I'm sure if I, like, killed the camera, it would probably stop doing this. I don't I don't know what's going on here. Local host, 8055. I at least wanna end this episode on a high note and show something.\u003C\u002Fp>\u003Cp>Come on.\u003C\u002Fp>\u003Cp>Speaker 1: Are you you're, are you running open claw? That's\u003C\u002Fp>\u003Cp>Speaker 0: I'm not yet. No. No. Not yet. I did test that thing out.\u003C\u002Fp>\u003Cp>It's, it's definitely an interesting one. I'm not sure that it is, it's not all there yet. Alright. It it's my testing was, like, most things you're gonna have to oh, great. Now I'm locked out of the actual instance as well.\u003C\u002Fp>\u003Cp>Hey. What is going on? Local host. 8055. Yeah.\u003C\u002Fp>\u003Cp>My open claw testing was basically, is it you're just gonna have to invest a lot of time into it to get something amazing out of it.\u003C\u002Fp>\u003Cp>Speaker 2: You you saw it. Right? Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Alright. So we can see the flows. Did they yeah. It actually connected the flow.\u003C\u002Fp>\u003Cp>So I'm just curious. Right. Just wanting to see. Right? Build a mastering five new programming frameworks.\u003C\u002Fp>\u003Cp>Let's say we completed this right now. Does this flow actually work? And So it it it\u003C\u002Fp>\u003Cp>Speaker 2: could increase the progress of the task of the goal.\u003C\u002Fp>\u003Cp>Speaker 0: It should. And, of course, doing a hard refresh here is not not a great idea. Alright. Well, gents, you know, I'm not sure whether to put a, like, a thumbs up stamp on this one. Thumbs up stamp.\u003C\u002Fp>\u003Cp>We can just do I think. Yeah. This was, I think we got most of the functionality\u003C\u002Fp>\u003Cp>Speaker 2: there. Good at dive.\u003C\u002Fp>\u003Cp>Speaker 0: We just didn't get, the front end all the way there.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, Brian, you are lagging quite.\u003C\u002Fp>\u003Cp>Speaker 0: Of course, it did.\u003C\u002Fp>\u003Cp>Speaker 1: Is it done? I think you you get a a thumbs up, Brian, because it we got a working thing at the end, and you had the the grid showing everything with the progress. So I think you get a thumbs up.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Alright, guys. My computer is struggling, so we are going to sign off for this episode. Mark Alvaro, I've heard a little rumor that there might be a podcast coming up, so I'm super excited for that. Thanks for joining me for this episode of one app in ten minutes.\u003C\u002Fp>","Alright, viewers. Welcome to, yet another episode of 100 app, 100 I don't know. No. No. No. One app in ten minutes. Right? We are doing the remix version today where we have ten minutes to build and plan plan and build an amazing app clone, crazy suggestion, and I have no idea what we're gonna do. So the rules. Right? Ten minutes to plan and build. No more, no less. How we're gonna do that? We are going to use some, amazing tools that we have built into Directus. And then, rule number two, the anti rule. Use whatever you've got at your disposal. Today, I've got two awesome dudes at my disposal, mister Alvaro and Mark from our team here at Directus. No strangers to the VUE community. Welcome to the show, gents. Thanks for having us, Bryant. Thank you very much for the nice intro. Happy, to be here. Yeah. Yeah. No. I'm super excited. Have you guys given any thought to what we're what we're gonna build? I think Mark has some idea though. Yeah. So yesterday, we talked a little bit. I talked with Ava what we could build. And, I don't know if if I showed it to you, Brian, but on my website, I have a, instead of new year for solutions, I have new year's bingo cards. So you have five by five grid of stuff I want to do in the year. And if I get at least one in a row, so diagonal or horizontal or vertical, I already have bingo and it's a success. So I don't have to do all of them. And if you go to mark.dev\u002Fbingo Okay. Let's check it out, guys. You can it's still since it's just well, now February, not a lot has happened there. But But it's a it's a really nice way to actually do some of the New Year's resolution. I always get the press at the end of the year like I have done, like, a quarter of them. Yeah. I love it. Alright. So hey. This is neat, man. I I miss Yep. And each of them can be either, like, you did it or you didn't do it or it can be progressive. Like, read six books and you are, like, one books, two books, three books in. And I think I also have, like, subtasks if we can make that work. Like, if one one, let's say, one bingo item has a few sub items as well. Like, don't have an example now, but that would also be cool. Gotcha. Okay. New Year's resolution. Bingo card generator. Alright. That's what we're doing. This is gonna be amazing. This should be fun. What color are you guys feeling? Purple, pink? I go I go purple. Blue. Oh, purple. Purple. There we go. Direct is purple. Nice. Direct is purple. Alright, guys. Alright. So I'm sure you've seen the show. We're gonna start the clock. We got ten minutes to plan and build this thing. Let's do it. Alright. So, the first thing I usually do here is cover requirements. Right? So what are the requirements we need out of this? Right? We need to generate bingo cards. Like, what do you what were you calling those? Like, items probably or Okay. Goals. Yeah. Items. Yeah. Like a grid of of items. Mhmm. Yeah. Alright. So we got some goals. Those are what kinda fields are you tracking on those? Just the name of the goal? Yeah. A name description and then the status. Status of the goal. Progress. Progress. Is it are you status and progress interchangeable? Yeah. I guess if you like the if the progress is under percent, the status Okay. Yeah. Yeah. Yeah. Got it. Okay. And then we've got we've got goals. You've got what? Items underneath the goals? What do you do? We want, like, subtasks, like, if it's You you can have subtasks. Let's see if there's one that has subtasks. I don't remember. Tasks. It's called test. Alright. So the tasks would play into into progress as well. I guess. Into goal. And then the task completed increases progress. Cool. Alright. And task needs what? Name? Description? No. Just name? Date date probably. Maybe the, the item can have a a completed ad. Yeah. They've completed as well for the task for the, item on top. Yeah. Alright. And then we we wanna try to get a front end set up for this as well. Yeah. Alright. And we need a front end to display the bingo cards. Alright. This could be a stretch in seven minutes now. Let's see how we do. Alright. So what are we using today? Right? We've got a blank directus project. We've got Claude Code over here. Let's dive into it. Alright? I'm going to I'm not sure what you guys have been coding with. I've been using Super Whisper. I dig it. Hi. How are you doing? Alright, guys. We are building a New Year's resolution bingo card generator. I'm gonna copy and paste the data model that we want. You have access to a direct assistance. I want you to create our schema for that. We're also going to be building a front end to display the bingo cards. Let me know what questions you have. Let's create a plan. Alright. So this is crunching the transcript for that right now. Cool. There we go. I'll just, copy and paste this. Hopefully, we'll get some something good out of it. And we're gonna ask Claude Code to plan. Alright. So now we've got the schema. So we've got the direct us MCP connected to this thing. And I I think you guys have had a chance to try this out already. Right? Yeah. I think Avro has. I haven't. Yeah. Play with it in the morning. It's gonna create the collections, the schemas for you. Yeah. Alright. So it's got a fresh direction. No custom collections. Alright. And I can zoom in just a little bit more so we could see this. What is the plan? And this is probably one of my favorite parts about this thing where it will prompt you for questions. Direct us flow, that's what we wanna do there. Vanilla JS. Yeah. That's what we'll do. What do you guys think? Five by five grid? Four by four? We we can do also four by four so we don't have to come up with 25 things. Amazing. Right? We got five minutes left. You you can say to the MCP, hey, cloud, get, your twenty twenty six, bingo. Oh, that was cool. Yeah. Yeah. Alright. Public read, that's fine. Anyone can view those. Cool. Alright. And now, hopefully, this thing should have a plan. I wonder which resolutions Cloud Code could have. I don't know. Let's see. We'll we'll spin that up in an in a new find out. Alright. Cool. Right? Here's the direct to schema. There's our it's gonna create a flow. It's gonna create the front end. Sounds good. Let's let's roll with it. Right? I don't know what we're actually doing other than just talking this through here, but, I'm curious to see just how this thing works. I've you know, of course, like, spent a ton of time testing and building the MCP, but I've not spent a ton of time using it with, the latest Opus four five model. Alright. So it is checked the existing schema. Now we are it should start implementing. Yes. Please just start jamming on here. And if I refresh, now we should see some collections start to come in to the direct instance. We should see some collections. Start to come into the direct assistance. There we go. Okay. Alright. Oh, nice. I was just worried that I did something wrong. So we got our goals. We got our tasks. Amazing. Right? Now I could go in. We could potentially create some new ones if we need. One of the things that I like about this is it, like it seems like the anthropic models do a better job of, like, actually putting together a cohesive form than than, like, the OpenAI wants. So it's going through creating relations and fields. Alright, guys. So in this other one, create, some New Year's resolutions for yourself, Claude. Alright. You guys have any more guidance for this thing? They should follow this the smart principle, probably. Follow the smart principle. What's the smart principle? Now you got me. So it's like measurable, achievable. I know what you're talking about now. Yeah. The smart goals. Yeah. Yeah. And include the add them to the goals and tasks inside. For the for me, the most important one is always measurable. You have to be able to measure what you do. If not, you lose yourself. You lose yourself. That's so funny. It's okay. That is very poetic. I love it, man. Alright. So it looks like okay. Yeah. I was just making sure we've got the relationship created correctly there. Alright. It is going to so we got two claws going. We got two minutes here. Let's see. I can see their goals and tasks. Alright. This is the next development, man. Right? Resting to the next development. Yeah. This thing is going to yeah. I need to enter YOLO mode so we can actually, have this thing not stop to do these calls. But, behind the scenes, right, it is building this progress calculator flow. And and flows are Yeah. A a nice piece of functionality. It can be a little time consuming to set up, like, complex flows via the UI. So having direct us put these together, is, yeah, definitely time saving. Right? That's probably, like, five, ten steps there. Yes. Create those items. Alright. Let's see what we've got. Are we gonna get to the front end for this thing? I don't know if we are, man. I should've had Bryant. Should've had, Claude do that first. It's connecting the operations. Claude, you need to go faster, my friend. Alright. So what are the what are the goals that Claude set for itself? This should be interesting. Put that description, Steven. I'll reduce average response latency by 20%, Achieve 95% task completion rate without clarification. What an interesting goal. Here's the the individual tasks. And, I'll And that was the end there. The front end. Now it's doing it. No. Let me open this test project up. Is it going to have enough time? Yes. Proceed. New Year's resolution. Bingo. Oh, no. We ran out of time. So close. MCP connection should have access. No need to set up. I think, you know, this was so close, guys. I'm just going to it's against the rules, but you know what? We can make up our own rules here. I am just going to give access here to see and see if this will actually finish. Of course. There it is, man. The API permissions got us. We could see the bingo card here. There's the individual tasks. Ten minutes, full working back end with permissions, so close to a working front end. It did pretty cool. This is this is very cool. Right? Even even with the subtask because that that wasn't an extra thing. Like, now it's the only iteration. Like, put the progress in the front end and Yeah. I'm very curious to see. Right? It's already got, it looks like it maybe did it miss some of the flows? Right? So the thing to take away here is obviously, like, you could build incredibly quickly with Directus and MCP, and this is not loading, probably because of my computer. Just hates running all these Docker containers locally. What is going on? How many do you have? There's probably, like, five or 10 running at the moment, like, different instances. And I'm sure if I, like, killed the camera, it would probably stop doing this. I don't I don't know what's going on here. Local host, 8055. I at least wanna end this episode on a high note and show something. Come on. Are you you're, are you running open claw? That's I'm not yet. No. No. Not yet. I did test that thing out. It's, it's definitely an interesting one. I'm not sure that it is, it's not all there yet. Alright. It it's my testing was, like, most things you're gonna have to oh, great. Now I'm locked out of the actual instance as well. Hey. What is going on? Local host. 8055. Yeah. My open claw testing was basically, is it you're just gonna have to invest a lot of time into it to get something amazing out of it. You you saw it. Right? Yeah. Yeah. Alright. So we can see the flows. Did they yeah. It actually connected the flow. So I'm just curious. Right. Just wanting to see. Right? Build a mastering five new programming frameworks. Let's say we completed this right now. Does this flow actually work? And So it it it could increase the progress of the task of the goal. It should. And, of course, doing a hard refresh here is not not a great idea. Alright. Well, gents, you know, I'm not sure whether to put a, like, a thumbs up stamp on this one. Thumbs up stamp. We can just do I think. Yeah. This was, I think we got most of the functionality there. Good at dive. We just didn't get, the front end all the way there. Oh, Brian, you are lagging quite. Of course, it did. Is it done? I think you you get a a thumbs up, Brian, because it we got a working thing at the end, and you had the the grid showing everything with the progress. So I think you get a thumbs up. Yeah. Alright, guys. My computer is struggling, so we are going to sign off for this episode. Mark Alvaro, I've heard a little rumor that there might be a podcast coming up, so I'm super excited for that. Thanks for joining me for this episode of one app in ten minutes.","0878a4f4-5836-4a78-b897-a8b4789c9e7c",[352,353,354],"851de303-b434-4092-8b15-fb6720b1e4c2","010213cf-c50a-4f60-b264-afb8fcf3e4b3","487b9dd3-5c6e-4894-bf34-a2bb1f6d2b64",[],{"year":139,"number":316,"id":317,"episodes":357,"show":358},[319,300,320],{"title":322,"slug":323},{"id":360,"slug":361,"vimeo_id":362,"description":363,"tile":364,"length":12,"resources":12,"people":12,"episode_number":264,"published":365,"title":366,"video_transcript_html":367,"video_transcript_text":368,"content":369,"seo":370,"status":19,"episode_people":371,"recommendations":372,"season":373},"5506f9ca-3a72-4447-8b50-ce4c821e3d1d","mcp-claude-code","1129629455","Set up the native Directus MCP Server with Claude Code for powerful command-line AI workflows. Learn how to configure the MCP integration to enable direct content management and schema operations from your terminal with Claude's autonomous coding capabilities.","cecbd794-70ec-43fa-abab-5e8d97cc4192","2025-11-20","Connect to Claude Code","\u003Cp>Speaker 0: Welcome, friends. Brian here from Directus. In this video, we're gonna show you how to connect the Directus native MCP to Claude code. So we're gonna start our journey inside our Directus instance, and we're gonna go to the AI settings to make sure that we have enabled the MCP server. We're gonna go ahead and add our AI prompts collection because we've already loaded that from a template.\u003C\u002Fp>\u003Cp>If you don't have an AI prompts collection, it will prompt you to create one. So we're gonna save those settings, and the next thing we're gonna do is go to our users. It's highly recommended we create a new user for this. In this case, I'm probably gonna wanna work with the schema locally in my development environment, so I'm gonna make sure that we give it, admin role access as well in my direct assistance. When you go to production, highly recommend stripping that out, and using a more scoped access policy, with these AI actors.\u003C\u002Fp>\u003Cp>Alright. So with that out of the way, we've got everything we need. Then we're just gonna pop over to the Directus documentation, and I'm just gonna copy this one liner and we're gonna go in and change our Directus URL. So this is gonna be HTTP, HTTP local host eight zero five five, and then I'm gonna go in and we're gonna copy paste that token that we generated. So I'm just gonna paste that right there.\u003C\u002Fp>\u003Cp>We'll hit, cool. Added MCP server and now we can start up cloud code just by running the cloud command. Cool. And now we're ready. Right?\u003C\u002Fp>\u003Cp>If I look for MCP, we could see we've got our different prompts here that are coming from the prompts collection inside our Directus instance. So I can look there. We could see those three prompts. Or I can just ask it, you know, can you tell me a little about my Directus schema? Alright.\u003C\u002Fp>\u003Cp>This should pick up on the tools that are available through the MCP server and ask to run them for us. You know, we can choose to always allow. You know, here we go. We could see, some of that. Cool.\u003C\u002Fp>\u003Cp>And now we could see it actually calling the schema tool so we can start getting some of the information about the Directus instance. Alright. So here it goes. It comes back with a summary of that. One of the other nice ways I can use this, you know, maybe we've got a page and we wanna add a new field to it.\u003C\u002Fp>\u003Cp>Add a new field to our pages collection for an AI generated summary. Cool. So one of the nice things about the MCP inside Directus is it does have schema tools, so we can create new fields, relationships, and even new collections inside our direct instance, not just read those things. Alright. So here's the information it's gonna send.\u003C\u002Fp>\u003Cp>Cool. We'll allow it to do so, and then once it's done, I should just be able to hit refresh over here on the left, and we'll see that new AI summary field. There are lots more capabilities baked into the Directus MCP, but that is it for this video on how to connect to Cloud Code. Stay tuned for more videos in this series.\u003C\u002Fp>","Welcome, friends. Brian here from Directus. In this video, we're gonna show you how to connect the Directus native MCP to Claude code. So we're gonna start our journey inside our Directus instance, and we're gonna go to the AI settings to make sure that we have enabled the MCP server. We're gonna go ahead and add our AI prompts collection because we've already loaded that from a template. If you don't have an AI prompts collection, it will prompt you to create one. So we're gonna save those settings, and the next thing we're gonna do is go to our users. It's highly recommended we create a new user for this. In this case, I'm probably gonna wanna work with the schema locally in my development environment, so I'm gonna make sure that we give it, admin role access as well in my direct assistance. When you go to production, highly recommend stripping that out, and using a more scoped access policy, with these AI actors. Alright. So with that out of the way, we've got everything we need. Then we're just gonna pop over to the Directus documentation, and I'm just gonna copy this one liner and we're gonna go in and change our Directus URL. So this is gonna be HTTP, HTTP local host eight zero five five, and then I'm gonna go in and we're gonna copy paste that token that we generated. So I'm just gonna paste that right there. We'll hit, cool. Added MCP server and now we can start up cloud code just by running the cloud command. Cool. And now we're ready. Right? If I look for MCP, we could see we've got our different prompts here that are coming from the prompts collection inside our Directus instance. So I can look there. We could see those three prompts. Or I can just ask it, you know, can you tell me a little about my Directus schema? Alright. This should pick up on the tools that are available through the MCP server and ask to run them for us. You know, we can choose to always allow. You know, here we go. We could see, some of that. Cool. And now we could see it actually calling the schema tool so we can start getting some of the information about the Directus instance. Alright. So here it goes. It comes back with a summary of that. One of the other nice ways I can use this, you know, maybe we've got a page and we wanna add a new field to it. Add a new field to our pages collection for an AI generated summary. Cool. So one of the nice things about the MCP inside Directus is it does have schema tools, so we can create new fields, relationships, and even new collections inside our direct instance, not just read those things. Alright. So here's the information it's gonna send. Cool. We'll allow it to do so, and then once it's done, I should just be able to hit refresh over here on the left, and we'll see that new AI summary field. There are lots more capabilities baked into the Directus MCP, but that is it for this video on how to connect to Cloud Code. Stay tuned for more videos in this series.","\u003Cp>In this episode, we'll show you how to connect the native Directus MCP Server to Claude Code for powerful command-line AI workflows. You'll learn how to:\u003C\u002Fp>\u003Cul>\u003Cli>Set up the Directus MCP server configuration\u003C\u002Fli>\u003Cli>Generate and configure your access token for Claude Code\u003C\u002Fli>\u003Cli>Connect your Directus instance to Claude Code's terminal interface\u003C\u002Fli>\u003Cli>Use autonomous AI coding capabilities with direct Directus access\u003C\u002Fli>\u003Cli>Execute schema operations and content management from the command line\u003C\u002Fli>\u003C\u002Ful>\u003Cp>Claude Code brings Anthropic's powerful AI directly to your terminal with autonomous task execution. This integration enables you to delegate complex coding and content management tasks to Claude while maintaining direct access to your Directus instance. Ideal for developers who prefer working in the terminal and want to automate content operations alongside their development workflow.\u003C\u002Fp>","128c78b8-31f6-45cb-b8b3-70c67e9f7cf6",[],[],{"year":374,"number":176,"id":375,"episodes":376,"show":382},"Native MCP","9781e3ca-7d76-4ebc-8c35-511681cd9394",[377,378,360,379,380,381],"23e7f108-2cb4-4ab2-95b3-62429ce0b697","32408843-f937-47d4-bbcb-65fad031548b","1e3cca6c-b7f7-4f79-b0c8-428e58624785","4afe2385-df4a-45f0-9409-75bb149b62be","86f0031b-9bf0-44cb-aa66-b3bd03fa89b0",{"title":383,"slug":384},"Directus MCP Server","directus-mcp-server",{"id":386,"slug":387,"vimeo_id":388,"description":389,"tile":390,"length":264,"resources":12,"people":12,"episode_number":159,"published":365,"title":391,"video_transcript_html":392,"video_transcript_text":393,"content":12,"seo":394,"status":19,"episode_people":395,"recommendations":397,"season":398},"f1c9f6ee-8760-4719-8834-e4da28f9b3eb","collaborative-cms-mcp","1138969642","Christina talks through what we mean at Directus by 'collaborative CMS' and how the MCP is part of the story.","3407ff52-ee9a-47c3-966b-e3ebd4933606","What is a Collaborative CMS?","\u003Cp>Speaker 0: Hi. I'm Christina. I'm director of product marketing at Directus. And I'm here to clear up one question. So what does collaborative CMS mean?\u003C\u002Fp>\u003Cp>The problem with CMS is has always been that any given product is geared either to developers who are building a CMS for their colleagues or their clients, or for people who are the nontechnical end users of the CMS that the devs are building. So marketing tends to think mainly in campaigns, narratives, and speed to market. Engineering tends to think mainly in data structures, validation rules, and system integrity. So this isn't a skills gap between them. It's something else.\u003C\u002Fp>\u003Cp>The result is that developer focused platforms give you power and flexibility, but they're opaque to non platforms give you power and flexibility, but they're opaque to non technical users. User friendly platforms make content editing accessible, but they constrain what the developers can build. Every time you build a CMS, you have to pick your poison and choose an architectural trade off. What we're exploring though with our new MCP tool at Directus and what we've implemented natively is bridging that divide. And here's what it looks like.\u003C\u002Fp>\u003Cp>A developer can build and change collections through the natural language interface of the MCP so that they can ship faster. You can add and delete fields, insert data into your collections, build automations like flows, edit existing collections, and create multiple new collections while still configuring all the kinds of fields that are added, like making them non nullable or adding conditions, setting up validation rules, and of course, creating relationships. Meanwhile, a marketer can describe what they need in their own language. They can use the MCP to completely change the brand tone of voice and pieces of writing, edit length, shorten content, generate SEO metadata, and alter slugs. Of course, they can update items and blog posts at scale and like you'd expect, they can create new content in bulk.\u003C\u002Fp>\u003Cp>So whichever user is tasked with doing something like importing the entire product catalog along with pricing tiers and regional variations, the AI understands this request and the underlying data schema creating properly structured entries with the right relationships and validation that developers have previously established. So this is the bridge between what developers and marketers traditionally do independently. The MCP allows either person to handle that middle work, and that speeds up both teams. And this is what we mean by collaborative CMS. And the MCP becomes a natural language interface for people to do all kinds of work together.\u003C\u002Fp>\u003Cp>Both technical and non technical users working in the same system, speaking in their own language without needing to translate their needs into another context or language. And what we're seeing so far is development teams setting up robust data models once. Marketing teams operating within those guardrails at their natural velocity. There's no tickets, no waiting, no translation errors, and it's an end to all the rework. So could natural language interfaces remove communication friction between teams?\u003C\u002Fp>\u003Cp>We think so. We're seeing it happen with our MCP integration. Companies are already using it to change how their teams work together. We're excited for you to try it. Let us know what you think.\u003C\u002Fp>","Hi. I'm Christina. I'm director of product marketing at Directus. And I'm here to clear up one question. So what does collaborative CMS mean? The problem with CMS is has always been that any given product is geared either to developers who are building a CMS for their colleagues or their clients, or for people who are the nontechnical end users of the CMS that the devs are building. So marketing tends to think mainly in campaigns, narratives, and speed to market. Engineering tends to think mainly in data structures, validation rules, and system integrity. So this isn't a skills gap between them. It's something else. The result is that developer focused platforms give you power and flexibility, but they're opaque to non platforms give you power and flexibility, but they're opaque to non technical users. User friendly platforms make content editing accessible, but they constrain what the developers can build. Every time you build a CMS, you have to pick your poison and choose an architectural trade off. What we're exploring though with our new MCP tool at Directus and what we've implemented natively is bridging that divide. And here's what it looks like. A developer can build and change collections through the natural language interface of the MCP so that they can ship faster. You can add and delete fields, insert data into your collections, build automations like flows, edit existing collections, and create multiple new collections while still configuring all the kinds of fields that are added, like making them non nullable or adding conditions, setting up validation rules, and of course, creating relationships. Meanwhile, a marketer can describe what they need in their own language. They can use the MCP to completely change the brand tone of voice and pieces of writing, edit length, shorten content, generate SEO metadata, and alter slugs. Of course, they can update items and blog posts at scale and like you'd expect, they can create new content in bulk. So whichever user is tasked with doing something like importing the entire product catalog along with pricing tiers and regional variations, the AI understands this request and the underlying data schema creating properly structured entries with the right relationships and validation that developers have previously established. So this is the bridge between what developers and marketers traditionally do independently. The MCP allows either person to handle that middle work, and that speeds up both teams. And this is what we mean by collaborative CMS. And the MCP becomes a natural language interface for people to do all kinds of work together. Both technical and non technical users working in the same system, speaking in their own language without needing to translate their needs into another context or language. And what we're seeing so far is development teams setting up robust data models once. Marketing teams operating within those guardrails at their natural velocity. There's no tickets, no waiting, no translation errors, and it's an end to all the rework. So could natural language interfaces remove communication friction between teams? We think so. We're seeing it happen with our MCP integration. Companies are already using it to change how their teams work together. We're excited for you to try it. Let us know what you think.","bad4717c-1b52-4ec0-a2e7-75ba94a65b42",[396],"8b786101-b7a5-498f-b45b-bf9450894079",[],{"year":399,"number":400,"id":401,"episodes":402,"show":411},"2025",39,"49af1154-dd24-49c1-a1fd-fb2da0a4484b",[403,404,405,386,406,407,408,409,410],"f1b56b45-9398-41c1-becd-1d08d15e593d","629c73cb-a886-4db1-9e45-a604316de145","3bf8ea55-3ff8-4dc8-937b-093ca713b8a9","7df39746-4a7e-4916-ba9a-71c1fa291944","437dea6b-a229-449c-9437-3f6620c2898d","95ed81c1-d6f4-4f12-817c-ca7aaeb9ac85","3ac5e6eb-111e-4bcf-b2ab-66e70712b3a8","1848dd2c-dbff-4361-9571-eb64a629c46d",{"title":412,"slug":413},"MCP Showcase","mcp-showcase",{"id":377,"slug":415,"vimeo_id":416,"description":417,"tile":418,"length":12,"resources":12,"people":12,"episode_number":13,"published":365,"title":419,"video_transcript_html":420,"video_transcript_text":421,"content":422,"seo":423,"status":19,"episode_people":424,"recommendations":425,"season":426},"mcp-chatgpt","1129585453","Connect Directus to ChatGPT using the native Directus MCP Server. This episode demonstrates how to set up the MCP integration with ChatGPT, enabling AI-powered content management and data operations directly from your ChatGPT conversations.","b3dc0c24-b5ec-46da-8598-37a731de4b35","Connect to ChatGPT","\u003Cp>Speaker 0: Hi there. Bryant here for Directus. In this video, we are gonna connect the Directus MCP to ChatGPT. Amazing. All the acronyms we have these days.\u003C\u002Fp>\n\u003Cp>Alright. So the first thing that we're going to do is basically go into the AI settings inside Directus and make sure that MCP is enabled. So we'll just click the MCP server and, you know, this template, which you can load in Directus cloud or self hosted, has everything you need to get started with CMS. It also has this reusable template prompts collection already enabled. So we're gonna make sure that is connected.\u003C\u002Fp>\n\u003Cp>The next step in the process is basically, we're we're just gonna go in and create a new user. So we'll call this chat GPT. Now you could use your existing user. We don't recommend it, simply for security reasons. And in this case, I wanna work with the full set of tools.\u003C\u002Fp>\n\u003Cp>You know, maybe we're gonna adjust the schema in this CMS instance, so I'm just going to add them as an administrator. So the next thing we're gonna do, we will create a access token. I'm just gonna copy that because we're gonna need it, and I'm gonna hit save. So now with that done, we're gonna go over to chat g p t, and we're gonna go into our settings down here. Inside the settings, we're gonna go to the apps and connectors.\u003C\u002Fp>\n\u003Cp>We're gonna click on advanced settings. And to use the Directus MCP inside chat GPT, again, with all the acronyms, you have to set up and enable developer mode, which is currently in beta. Alright. So with that, we're gonna go back up to the top. We're gonna hit create a custom connector and we're just gonna call this Directus CMS.\u003C\u002Fp>\n\u003Cp>It's magic. And then we're gonna take our MCP URL. So this is gonna be the URL of your Directus instance. My simple cmsstarter.directus.app. And then we're gonna add slash m c p to the end, and where it says o auth, we're gonna hit no authentication.\u003C\u002Fp>\n\u003Cp>Now the last piece here, we're gonna add a question mark access underscore token, and then we're gonna paste that token that we generated. So this is our static access token. Cool. Now custom MCP servers may introduce risk. Obviously, you you should be aware of that going into this.\u003C\u002Fp>\n\u003Cp>But once you do that, you hit, create. There we go. And now we can see we've got our Directus CMS. We can see all the different tools that are available to us, then we can go out, And now it shows we are in developer mode. Right?\u003C\u002Fp>\n\u003Cp>So now I can start working with chatGPT. What does my Directus schema look like? And this should go out, connect to our direct assistance, and pull back the, available schema. So it's gonna show, hey, we're gonna call the tool here, look for the direct to schema tool, which should return a list of raw collections that we could see here. This is just all the collections in our system, and then it's going to summarize that for us.\u003C\u002Fp>\n\u003Cp>Right? Pretty easy. We got different blocks. We got a website. Here's our primary collection.\u003C\u002Fp>\n\u003Cp>Cool. Can you create a new blog post for us about how fun it is to keep pet rabbits. Alright. So we're just gonna hit stop here. There we go.\u003C\u002Fp>\n\u003Cp>And now it is going to call some more tools. And we can see it's trying to create a new post inside the post collection. And there's our data, the joy of keeping pet rabbits. Right? So I can remember for this individual conversation to allow ChatGPT to call these tools over again, or I can simply just allow it for that one post.\u003C\u002Fp>\n\u003Cp>Alright. So now we can click here, open this link, and oh. I didn't get the URL correct, but there we go. We can see we have the joy of keeping pet rabbits. Here's a super short blog post.\u003C\u002Fp>\n\u003Cp>Now there are tons of things you could do with the Directus MCP. This is just scratching the surface, but that is how you connect it to chat GPT. Stay tuned for the next video.\u003C\u002Fp>","Hi there. Bryant here for Directus. In this video, we are gonna connect the Directus MCP to ChatGPT. Amazing. All the acronyms we have these days. Alright. So the first thing that we're going to do is basically go into the AI settings inside Directus and make sure that MCP is enabled. So we'll just click the MCP server and, you know, this template, which you can load in Directus cloud or self hosted, has everything you need to get started with CMS. It also has this reusable template prompts collection already enabled. So we're gonna make sure that is connected. The next step in the process is basically, we're we're just gonna go in and create a new user. So we'll call this chat GPT. Now you could use your existing user. We don't recommend it, simply for security reasons. And in this case, I wanna work with the full set of tools. You know, maybe we're gonna adjust the schema in this CMS instance, so I'm just going to add them as an administrator. So the next thing we're gonna do, we will create a access token. I'm just gonna copy that because we're gonna need it, and I'm gonna hit save. So now with that done, we're gonna go over to chat g p t, and we're gonna go into our settings down here. Inside the settings, we're gonna go to the apps and connectors. We're gonna click on advanced settings. And to use the Directus MCP inside chat GPT, again, with all the acronyms, you have to set up and enable developer mode, which is currently in beta. Alright. So with that, we're gonna go back up to the top. We're gonna hit create a custom connector and we're just gonna call this Directus CMS. It's magic. And then we're gonna take our MCP URL. So this is gonna be the URL of your Directus instance. My simple cmsstarter.directus.app. And then we're gonna add slash m c p to the end, and where it says o auth, we're gonna hit no authentication. Now the last piece here, we're gonna add a question mark access underscore token, and then we're gonna paste that token that we generated. So this is our static access token. Cool. Now custom MCP servers may introduce risk. Obviously, you you should be aware of that going into this. But once you do that, you hit, create. There we go. And now we can see we've got our Directus CMS. We can see all the different tools that are available to us, then we can go out, And now it shows we are in developer mode. Right? So now I can start working with chatGPT. What does my Directus schema look like? And this should go out, connect to our direct assistance, and pull back the, available schema. So it's gonna show, hey, we're gonna call the tool here, look for the direct to schema tool, which should return a list of raw collections that we could see here. This is just all the collections in our system, and then it's going to summarize that for us. Right? Pretty easy. We got different blocks. We got a website. Here's our primary collection. Cool. Can you create a new blog post for us about how fun it is to keep pet rabbits. Alright. So we're just gonna hit stop here. There we go. And now it is going to call some more tools. And we can see it's trying to create a new post inside the post collection. And there's our data, the joy of keeping pet rabbits. Right? So I can remember for this individual conversation to allow ChatGPT to call these tools over again, or I can simply just allow it for that one post. Alright. So now we can click here, open this link, and oh. I didn't get the URL correct, but there we go. We can see we have the joy of keeping pet rabbits. Here's a super short blog post. Now there are tons of things you could do with the Directus MCP. This is just scratching the surface, but that is how you connect it to chat GPT. Stay tuned for the next video.","\u003Cp>In this episode, we walk you through connecting the native Directus MCP Server to ChatGPT. You'll learn how to:\u003C\u002Fp>\u003Cul>\u003Cli>Enable the MCP server in your Directus AI settings\u003C\u002Fli>\u003Cli>Set up the AI prompts collection for reusable workflows\u003C\u002Fli>\u003Cli>Create a dedicated ChatGPT user with appropriate permissions\u003C\u002Fli>\u003Cli>Generate a static access token for secure authentication\u003C\u002Fli>\u003Cli>Enable developer mode in ChatGPT\u003C\u002Fli>\u003Cli>Create a custom MCP connector with your Directus instance URL\u003C\u002Fli>\u003Cli>Configure authentication using your access token\u003C\u002Fli>\u003Cli>Test the connection by querying your schema and creating content\u003C\u002Fli>\u003C\u002Ful>\u003Cp>Watch as we demonstrate the complete setup process, from configuring Directus to creating a blog post entirely through ChatGPT. This native integration enables you to work with your CMS through natural conversation with ChatGPT.\u003C\u002Fp>","88db55bc-2985-47ef-b38c-bda138cd5ca7",[],[],{"year":374,"number":176,"id":375,"episodes":427,"show":428},[377,378,360,379,380,381],{"title":383,"slug":384},{"id":404,"slug":430,"vimeo_id":431,"description":432,"tile":433,"length":213,"resources":12,"people":12,"episode_number":177,"published":365,"title":434,"video_transcript_html":435,"video_transcript_text":436,"content":12,"seo":437,"status":19,"episode_people":438,"recommendations":441,"season":442},"setting-up-mcp","1138989005","Join Bryant and Lindsey as they show you how to get started with the MCP.","1420d770-1835-4ddb-85c4-f293d92e775b","Setting up MCP: Start Building Today","\u003Cp>Speaker 0: Alright. I'm here with Lindsay from our team. And in this segment, we're gonna show you how to connect the Directus Native MCP to your LLM or your AI tools of choice. Lindsey, do you want to walk us through the process?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So the first step is enabling it. So you'll wanna go to your Directus instance, go into settings, and then navigate to the AI section. And here's where you'll set up your MCP server. So you'll just click enable.\u003C\u002Fp>\u003Cp>If you want the prompt collection, you can turn it on here, or if you already have it, you can find it and tell us what it is. And then make sure you click that save at the top. Decide if you wanna allow deleting as well.\u003C\u002Fp>\u003Cp>Speaker 0: And what is our next step after we've enabled the MCP?\u003C\u002Fp>\u003Cp>Speaker 1: Yes. So now this is important. You'll wanna set up a new user so that you can set up a clean token with the access permissions you need for your MCP. So we're gonna give it administrator right now, but you might want it to give it slightly tighter permissions if you wanna keep it from doing everything. We're gonna do this because we're gonna have it messing with the schema.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. So now we've got our token. We are ready to connect to, our tools of choice. What are we gonna start with first, Lindsay?\u003C\u002Fp>\u003Cp>Speaker 1: So let's do cursor. So that's one of our easiest ones. If you go to our docs, we actually have a one click button to get you started there. So find the cursor section in our docs, scroll down, and click a button. It's gonna open up your cursor, and you'll just fill in the settings here.\u003C\u002Fp>\u003Cp>So you'll wanna put in your URL for your instance, and then this is where you'll grab that token that you created from your user.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. And I think we'll just hit install.\u003C\u002Fp>\u003Cp>Speaker 1: And there you go. You're good\u003C\u002Fp>\u003Cp>Speaker 0: to go. And we we can see we are connected, and then we are ready to chat. And as you can see, the Directus MCP is connected to cursor, and we are actually picking up the schema. Alright. So that is how you connect the native MCP to Cursor.\u003C\u002Fp>\u003Cp>Super easy to get started. Make sure you check it out.\u003C\u002Fp>\u003Cp>Speaker 1: But what about marketers? They typically use chat g p t five or something similar.\u003C\u002Fp>\u003Cp>Speaker 0: That's a a great question, Lizzie. So let me show you just how easy it is to connect to ChatGPT. Again, once you've got that token, all you need to do is log in to ChatGPT. In the bottom, you'll go to your specific settings. You'll look for apps and connectors, and you have to be in developer mode, which sounds scary.\u003C\u002Fp>\u003Cp>It's really just, a beta feature that they have for protection, and security. So once you activate developer mode, then you can go in and add your own MCP. They're called custom connectors. So we'll just add Directus. We'll give it our URL here.\u003C\u002Fp>\u003Cp>There we go. Simplecmsstarter.directus.app\u002Fmcp. And then we're going to add a question mark access underscore token, and then you're going to paste that same access token that you have. We're going to choose no authentication, and then we just hit create. There we go.\u003C\u002Fp>\u003Cp>And now I should be able to chat. So we'll just ask it what our Directus schema looks like. It should pick up the available tools through the MCP, and it'd be able to understand my full schema inside Directus. Again, super simple to connect with ChatGPT, and happy MCP ing.\u003C\u002Fp>\u003Cp>Speaker 1: If you have any feedback for us or any questions, head over to community.directus.io and let us know.\u003C\u002Fp>","Alright. I'm here with Lindsay from our team. And in this segment, we're gonna show you how to connect the Directus Native MCP to your LLM or your AI tools of choice. Lindsey, do you want to walk us through the process? Yeah. So the first step is enabling it. So you'll wanna go to your Directus instance, go into settings, and then navigate to the AI section. And here's where you'll set up your MCP server. So you'll just click enable. If you want the prompt collection, you can turn it on here, or if you already have it, you can find it and tell us what it is. And then make sure you click that save at the top. Decide if you wanna allow deleting as well. And what is our next step after we've enabled the MCP? Yes. So now this is important. You'll wanna set up a new user so that you can set up a clean token with the access permissions you need for your MCP. So we're gonna give it administrator right now, but you might want it to give it slightly tighter permissions if you wanna keep it from doing everything. We're gonna do this because we're gonna have it messing with the schema. Alright. So now we've got our token. We are ready to connect to, our tools of choice. What are we gonna start with first, Lindsay? So let's do cursor. So that's one of our easiest ones. If you go to our docs, we actually have a one click button to get you started there. So find the cursor section in our docs, scroll down, and click a button. It's gonna open up your cursor, and you'll just fill in the settings here. So you'll wanna put in your URL for your instance, and then this is where you'll grab that token that you created from your user. Alright. And I think we'll just hit install. And there you go. You're good to go. And we we can see we are connected, and then we are ready to chat. And as you can see, the Directus MCP is connected to cursor, and we are actually picking up the schema. Alright. So that is how you connect the native MCP to Cursor. Super easy to get started. Make sure you check it out. But what about marketers? They typically use chat g p t five or something similar. That's a a great question, Lizzie. So let me show you just how easy it is to connect to ChatGPT. Again, once you've got that token, all you need to do is log in to ChatGPT. In the bottom, you'll go to your specific settings. You'll look for apps and connectors, and you have to be in developer mode, which sounds scary. It's really just, a beta feature that they have for protection, and security. So once you activate developer mode, then you can go in and add your own MCP. They're called custom connectors. So we'll just add Directus. We'll give it our URL here. There we go. Simplecmsstarter.directus.app\u002Fmcp. And then we're going to add a question mark access underscore token, and then you're going to paste that same access token that you have. We're going to choose no authentication, and then we just hit create. There we go. And now I should be able to chat. So we'll just ask it what our Directus schema looks like. It should pick up the available tools through the MCP, and it'd be able to understand my full schema inside Directus. Again, super simple to connect with ChatGPT, and happy MCP ing. If you have any feedback for us or any questions, head over to community.directus.io and let us know.","8478d1a2-218b-43df-9764-f61c177d8dc3",[439,440],"5e4704dd-c8d2-4efe-8162-0b029f34259a","5ed24299-b070-4b50-91f3-c32623700d24",[],{"year":399,"number":400,"id":401,"episodes":443,"show":444},[403,404,405,386,406,407,408,409,410],{"title":412,"slug":413},{"id":405,"slug":446,"vimeo_id":447,"description":448,"tile":449,"length":177,"resources":12,"people":12,"episode_number":264,"published":365,"title":450,"video_transcript_html":451,"video_transcript_text":452,"content":12,"seo":453,"status":19,"episode_people":454,"recommendations":456,"season":457},"ai-in-directus","1138986971","Rijk shared how we're approaching AI at Directus.","b2888cf3-877e-439a-a153-62e152f40260","Our Perspective on AI in Directus","\u003Cp>Speaker 0: Hello. It's Reik van Zonthe, cofounder and CTO here at Directus. Very excited with this MCP launch. Very excited to see what you're gonna integrate it with and what new use cases you might find. For this segment, I was asked to go in a little bit deeper on the philosophy of artificial intelligence here at Directus, how we treat it right now, how we're planning on using it now and in the future, and what it means for our end users.\u003C\u002Fp>\u003Cp>So over time, over the last months and years, we've seen a lot of different directions that AI can take, a lot of possibilities that were unlocked. Some of them very exciting and real. Some of them a little bit silver bullet magic. At the end of the day, for us, it's all about helping our users achieve their goals in the most efficient and easy to use way as possible. We're not here to just replace a bunch of tasks that a person might have to do.\u003C\u002Fp>\u003Cp>We're here to help you out get to your results faster. With that in mind, we're really treating this more as a collaboration tool rather than an automation tool. Christina will do a little segment on that later, so keep watching. We found that LLMs are pretty great at helping unblock users in tasks they're taking, but do wanna make sure that we offer those LLM tools when and where they make sense. So for our very first deep dive here into AI, we started with MCP as the focus area.\u003C\u002Fp>\u003Cp>That allows our users to be unblocked when using third party tools like Claw, JetGPT, Recursor, or any other tool that supports MCP, and allow them to use their content, their data within Directus from those tools directly. When it comes to AI use, we obviously have to make sure that we have some very strict control about what an LM is allowed to read, what it is allowed to do with your data. And in general, this this fits in neatly with DirectUs' permission system because we, of course, through policies in our RBAC system, we have some very strict access control in place. The same applies for your MCP, usage. In general, we're implementing AI here as a carefully considered approach with that real user value rather than gimmicky for the sake of it.\u003C\u002Fp>\u003Cp>I'm incredibly excited to see what you will integrate our MCP endpoint with. Please do let me know as well. As this technology progresses, we're seeing a lot more interesting use cases, things we might have never even thought of on how to use it. So please let us know all of your thoughts, feelings, concerns, cool experiments, and everything else on community.directus.ao. And keep your eyes peeled because we have a lot more cool AI stuff coming in the near future.\u003C\u002Fp>\u003Cp>Thank you.\u003C\u002Fp>","Hello. It's Reik van Zonthe, cofounder and CTO here at Directus. Very excited with this MCP launch. Very excited to see what you're gonna integrate it with and what new use cases you might find. For this segment, I was asked to go in a little bit deeper on the philosophy of artificial intelligence here at Directus, how we treat it right now, how we're planning on using it now and in the future, and what it means for our end users. So over time, over the last months and years, we've seen a lot of different directions that AI can take, a lot of possibilities that were unlocked. Some of them very exciting and real. Some of them a little bit silver bullet magic. At the end of the day, for us, it's all about helping our users achieve their goals in the most efficient and easy to use way as possible. We're not here to just replace a bunch of tasks that a person might have to do. We're here to help you out get to your results faster. With that in mind, we're really treating this more as a collaboration tool rather than an automation tool. Christina will do a little segment on that later, so keep watching. We found that LLMs are pretty great at helping unblock users in tasks they're taking, but do wanna make sure that we offer those LLM tools when and where they make sense. So for our very first deep dive here into AI, we started with MCP as the focus area. That allows our users to be unblocked when using third party tools like Claw, JetGPT, Recursor, or any other tool that supports MCP, and allow them to use their content, their data within Directus from those tools directly. When it comes to AI use, we obviously have to make sure that we have some very strict control about what an LM is allowed to read, what it is allowed to do with your data. And in general, this this fits in neatly with DirectUs' permission system because we, of course, through policies in our RBAC system, we have some very strict access control in place. The same applies for your MCP, usage. In general, we're implementing AI here as a carefully considered approach with that real user value rather than gimmicky for the sake of it. I'm incredibly excited to see what you will integrate our MCP endpoint with. Please do let me know as well. As this technology progresses, we're seeing a lot more interesting use cases, things we might have never even thought of on how to use it. So please let us know all of your thoughts, feelings, concerns, cool experiments, and everything else on community.directus.ao. And keep your eyes peeled because we have a lot more cool AI stuff coming in the near future. Thank you.","019e56b8-a87f-4f7a-9d65-e362c204d375",[455],"40d5c649-a40e-4a79-950d-505597ed81dc",[],{"year":399,"number":400,"id":401,"episodes":458,"show":459},[403,404,405,386,406,407,408,409,410],{"title":412,"slug":413},{"id":403,"slug":461,"vimeo_id":462,"description":463,"tile":464,"length":264,"resources":12,"people":12,"episode_number":13,"published":365,"title":465,"video_transcript_html":466,"video_transcript_text":467,"content":12,"seo":468,"status":19,"episode_people":469,"recommendations":471,"season":472},"mcp-101","1138979993","Join Bryant as he talks through the details of the Directus MCP.","305db0ef-78f3-4f8c-b646-c2edd912da3c","MCP 101: What It Actually Does","\u003Cp>Speaker 0: Hey, everyone. Brian here. I gotta say, I'm super excited for this event. But let's get the intros out of the way, and then we'll dive in. If you're new to Directus, it's a headless CMS and back end that lets you build and manage any kind of data or content.\u003C\u002Fp>\u003Cp>You design your own data model or connect your existing SQL database. Directus will generate production ready APIs for you, and you get a complete admin app to manage everything on top of that. So let's move on to the main course. Right? This is what you're all here for.\u003C\u002Fp>\u003Cp>What is MCP? Why is it so freaking hyped? Let's talk about it. Model context protocol or MCP is basically just a way for your AI assistance like Claude or ChatGPT or your IDE like Cursor or Versus Code to connect directly to your tools and data sources. Instead of copying and pasting between ChatGPT and your CMS, the AI, it can talk to Directus directly, and we've built MCP support right into Directus Core.\u003C\u002Fp>\u003Cp>You enable it in the AI settings, generate an access token for a new user or existing user, connect your favorite AI tools, and you're done. That's it. No middleware, no extra servers to manage, no nothing to deploy. Sounds great. Right?\u003C\u002Fp>\u003Cp>But what does this actually do, Brian? Well, hey. That's where it gets interesting. Right? There are multiple different use cases depending on your role.\u003C\u002Fp>\u003Cp>So for content teams, you can import blog posts straight from Google Docs with proper field mapping and not having to waste an hour reformatting the copy. You You can organize hundreds of images that are all named image underscore whatever. Add metadata to the entire asset library without clicking through each file individually, spending a ton of time. For developers, you can quickly prototype data models just by describing what you need. Hey, AI.\u003C\u002Fp>\u003Cp>Build me a CRM with organizations, contacts, and deals. It will go through and create the collections, set up the relationships for you, the whole thing. Or you can modify your schema on the fly. Hey, I AI. I need a reading time field for all my blog posts and calculate it for my existing articles, set up an automation for that.\u003C\u002Fp>\u003Cp>Your AI tools work through the existing Directus permissions. So the beautiful thing about this is it can only do what you've allowed it to do. That keeps everything nice and secure, and everything shows up in your activity log. It's basically your normal direct us workflow, just faster, which brings us to what we're showing today. In this showcase, we're going to walk through some real examples, content creation, data modeling, automation, the stuff that people are actually using this for in production.\u003C\u002Fp>\u003Cp>We're gonna show you what works well, what to watch out for, and how to get the most out of combining Directus with AI. And you'll probably also hear a lot of rabbit puns. So with that, down the rabbit hole we go.\u003C\u002Fp>","Hey, everyone. Brian here. I gotta say, I'm super excited for this event. But let's get the intros out of the way, and then we'll dive in. If you're new to Directus, it's a headless CMS and back end that lets you build and manage any kind of data or content. You design your own data model or connect your existing SQL database. Directus will generate production ready APIs for you, and you get a complete admin app to manage everything on top of that. So let's move on to the main course. Right? This is what you're all here for. What is MCP? Why is it so freaking hyped? Let's talk about it. Model context protocol or MCP is basically just a way for your AI assistance like Claude or ChatGPT or your IDE like Cursor or Versus Code to connect directly to your tools and data sources. Instead of copying and pasting between ChatGPT and your CMS, the AI, it can talk to Directus directly, and we've built MCP support right into Directus Core. You enable it in the AI settings, generate an access token for a new user or existing user, connect your favorite AI tools, and you're done. That's it. No middleware, no extra servers to manage, no nothing to deploy. Sounds great. Right? But what does this actually do, Brian? Well, hey. That's where it gets interesting. Right? There are multiple different use cases depending on your role. So for content teams, you can import blog posts straight from Google Docs with proper field mapping and not having to waste an hour reformatting the copy. You You can organize hundreds of images that are all named image underscore whatever. Add metadata to the entire asset library without clicking through each file individually, spending a ton of time. For developers, you can quickly prototype data models just by describing what you need. Hey, AI. Build me a CRM with organizations, contacts, and deals. It will go through and create the collections, set up the relationships for you, the whole thing. Or you can modify your schema on the fly. Hey, I AI. I need a reading time field for all my blog posts and calculate it for my existing articles, set up an automation for that. Your AI tools work through the existing Directus permissions. So the beautiful thing about this is it can only do what you've allowed it to do. That keeps everything nice and secure, and everything shows up in your activity log. It's basically your normal direct us workflow, just faster, which brings us to what we're showing today. In this showcase, we're going to walk through some real examples, content creation, data modeling, automation, the stuff that people are actually using this for in production. We're gonna show you what works well, what to watch out for, and how to get the most out of combining Directus with AI. And you'll probably also hear a lot of rabbit puns. So with that, down the rabbit hole we go.","7f40dfe7-1149-467b-85e9-20e38e08c07e",[470],"c8f4c795-4fb6-46ca-b7d2-00c019ffb026",[],{"year":399,"number":400,"id":401,"episodes":473,"show":474},[403,404,405,386,406,407,408,409,410],{"title":412,"slug":413},{"id":406,"slug":476,"vimeo_id":477,"description":478,"tile":479,"length":480,"resources":12,"people":12,"episode_number":213,"published":365,"title":481,"video_transcript_html":482,"video_transcript_text":483,"content":12,"seo":484,"status":19,"episode_people":485,"recommendations":488,"season":489},"10-mins-tiktok-clone","1138963281","Bryant and Ben try to build the project of Ben's dreams using the MCP, in 10 minutes or less!","95397771-2625-415b-a579-1335736d00ce",12,"Building in 10 Minutes or Less: TikTok Clone","\u003Cp>Speaker 0: Alright. Welcome back to another episode of one app in ten minutes. This is a extra special season or, extra special show. Normally, this is 100 apps, one hundred hours. Today, we're doing everything in ten minutes.\u003C\u002Fp>\u003Cp>That comes with a lot of hype and A\u003C\u002Fp>\u003Cp>Speaker 1: lot of pressure.\u003C\u002Fp>\u003Cp>Speaker 0: This extra special episode. We've got mystery app with Ben. So mystery app on two levels. One, Ben, our CEO, had no idea he was being pulled into this. And two, I've got no idea what he wants to build, but we're gonna see if we could do it in ten minutes.\u003C\u002Fp>\u003Cp>Ben, welcome to the show.\u003C\u002Fp>\u003Cp>Speaker 1: Thank you, Brian. I'm very excited to mysteriously be here.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. Alright. So the name of the game today, ten minutes to plan and build, no more, no less. You might wanna be thinking about what app we're gonna be building now. I'm gonna tell everybody else how we're making this possible.\u003C\u002Fp>\u003Cp>So I've got a blank instance of Directus set up. I've also got our Directus MCP server connected to Claude AI, and that is the the secret sauce that we're gonna be using today to try and go much faster. So, alright, what do you what do you wanna build today, Ben?\u003C\u002Fp>\u003Cp>Speaker 1: My my first thought is, like, a content like a like a TikTok.\u003C\u002Fp>\u003Cp>Speaker 0: TikTok.\u003C\u002Fp>\u003Cp>Speaker 1: It's just, like, an easy Look at that. TikTok clone. TikTok clone.\u003C\u002Fp>\u003Cp>Speaker 0: With the owner. This CEO. Alright. 10 on the clock. Alright.\u003C\u002Fp>\u003Cp>Next up, you know how this goes. I know you've seen the show. We're gonna start the clock. We're gonna talk about what functionality we need, and then we're gonna go. Alright.\u003C\u002Fp>\u003Cp>Clock is running. Alright, Ben. What do we need out of, a TikTok clone?\u003C\u002Fp>\u003Cp>Speaker 1: Oof. We need to we need a feed, like a FYP, like a little just getting through getting through the content, getting through the videos. Need to be able to interact with those, so I guess give it a thumbs up. So, like, a little a favorites toggle.\u003C\u002Fp>\u003Cp>Speaker 0: Like a likes. Mhmm.\u003C\u002Fp>\u003Cp>Speaker 1: And then comments. I don't know what else is\u003C\u002Fp>\u003Cp>Speaker 0: Comments and probably like a post. Right? The Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: I guess the content itself is like a video, the author. So I guess they should all be attached relationally to an author, and then a description, a time stamp, posted time stamp.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Alright. Let's let's give this a shot. Right? We're gonna just copy paste this.\u003C\u002Fp>\u003Cp>We're gonna go to our trusty AI. You know, excited to be able to maybe separate fact and hype here. So, let's build a TikTok clone inside Directus. We want the following functionality. Personalized feeds, posts with likes and comments, and let's just use what?\u003C\u002Fp>\u003Cp>The direct Us users collection. Yeah. Mhmm. Let's use I was absolutely driving Reich crazy on another episode with typos. So let's use\u003C\u002Fp>\u003Cp>Speaker 1: Is it apostrophe missing a typo?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. There we go. Let's use direct users as the user's collection and reuse off there. Alright. We're gonna hit go, and we're gonna see what happens.\u003C\u002Fp>\u003Cp>Speaker 1: I can I can vouch for the fact that I had no clue what this is gonna be, so you definitely didn't? So there's\u003C\u002Fp>\u003Cp>Speaker 0: no Oh. Oh, no. Is this a oh, boy. Alright. We're just gonna add to the difficulty level here.\u003C\u002Fp>\u003Cp>It looks like I wasn't originally connected to the MCP. So we'll just refresh and go again. How we doing on time? Seven minutes forty seven seconds. Alright.\u003C\u002Fp>\u003Cp>We\u003C\u002Fp>\u003Cp>Speaker 1: burned through a lot on that on that hour.\u003C\u002Fp>\u003Cp>Speaker 0: No pressure. Right? If if one hour wasn't enough drama for you, let's do it all in ten minutes. So My water's\u003C\u002Fp>\u003Cp>Speaker 1: empty. We're still over there.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. So let's see. We've got the direct assist assistant system prompt here. It's fetching the schema. Obviously, this is a blank instance, and then it's looking for the directest users collection.\u003C\u002Fp>\u003Cp>Alright. So now we can see that the AI is trying to go in and and create these collections inside direct us. And it looks like we've got posts, we've got likes, we've got comments, follows, hashtags for post discovery.\u003C\u002Fp>\u003Cp>Speaker 1: Relational\u003C\u002Fp>\u003Cp>Speaker 0: hashtags. And it looks like it's got a junction table for posts and hashtags. Mhmm. Cool. And, I I don't think you've had a ton of experience with the MCP yet other than our like, the video updates.\u003C\u002Fp>\u003Cp>Only my content. So the it it approaches this very systematically, and then it will go in and create the collections. And it looks like it has only created one collection at a time. Alright. So this is new behavior to me.\u003C\u002Fp>\u003Cp>Previously, it's done all the collections and then the relations, but we we should be getting the same effect.\u003C\u002Fp>\u003Cp>Speaker 1: Like that time excuse there, Brian.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Absolutely. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. So Oh, wow. So we've already got the likes. We could see the posts. Got a caption, a video file, a thumbnail, user ID.\u003C\u002Fp>\u003Cp>Is this all does this line up with what you would expect out of out of this bin?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I mean, my design brain is is, like, thinking of, like, a little bit of form layout, but I'm impressed that it's doing, like, half in full width, and it's kinda doing a really good first pass.\u003C\u002Fp>\u003Cp>Speaker 0: Here's our awesome video.\u003C\u002Fp>\u003Cp>Speaker 1: And, I don't have any. So let's see. That was my first concern is, like, you're not gonna have any, like, MP fours at the ready.\u003C\u002Fp>\u003Cp>Speaker 0: We've got tons. Recdus MCP assets. What do we got? Blog post workflow. We could test the the TUS upload protocol at the same time.\u003C\u002Fp>\u003Cp>Speaker 1: So, Meta, are you uploading an m c p p video for the m c p video?\u003C\u002Fp>\u003Cp>Speaker 0: I was trying to. It looks like, some I'm getting some errors. So that's part of the fun of this. We just keep refreshing and and seeing more content or more data model show up. So we got post.\u003C\u002Fp>\u003Cp>Let's try this again. I'm gonna link this to my admin user. Here's our awesome video, And let's upload a video from device, see what it got. MCP assets. Do I have a video that is actually small enough?\u003C\u002Fp>\u003Cp>It looks like 30 megabytes is all I've got here.\u003C\u002Fp>\u003Cp>Speaker 1: It feels like that that can work. Except I'm getting a issue.\u003C\u002Fp>\u003Cp>Speaker 0: Oh. Oh. No stress. Alright. Let's just try an image for now.\u003C\u002Fp>\u003Cp>Oh, I've done something wrong with the images as well.\u003C\u002Fp>\u003Cp>Speaker 1: I think TikTok has images, doesn't it?\u003C\u002Fp>\u003Cp>Speaker 0: It it it does. It looks like something has gone wrong. I had reversed relationships. Okay. So now it's filling in the other relationships.\u003C\u002Fp>\u003Cp>And, unfortunately, I don't think it's gonna allow us to do this without a file, but we could solve for that really quickly.\u003C\u002Fp>\u003Cp>Speaker 1: I know. I'm googling sample m p four file. I like cityscapes and all that on the mind I've seen. I think it is something that I've done here. Thirty seconds, 1.5 megs.\u003C\u002Fp>\u003Cp>1.5 megs. Schema.\u003C\u002Fp>\u003Cp>Speaker 0: It's like Add to proceed.\u003C\u002Fp>\u003Cp>Speaker 1: Does this not know we are on a\u003C\u002Fp>\u003Cp>Speaker 0: okay. So I spotted the issue, and that's a good point to bring up that this is really cool functionality to be able to prototype this quickly, but it's not a it's not a silver bullet. It looks like the MCP had tried to tag the wrong folder that did not exist inside the instance, and that's where the issue was coming from. So we could try this again. Here's our test post.\u003C\u002Fp>\u003Cp>Let's add admin user. Just going to I\u003C\u002Fp>\u003Cp>Speaker 1: Slacked your URL if you wanna try doing it from URL. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. We'll try it from the URL. There we go. We've got the video file uploaded. We can set this to publish.\u003C\u002Fp>\u003Cp>We will save our post. And now that the MCP has done its thing, we got two minutes and forty seconds Infinite. To, let's just create create some test posts and sample content for each collection. Cool. Alright.\u003C\u002Fp>\u003Cp>In the meantime, Ben, what is great about Directus and how fast you could prototype like this?\u003C\u002Fp>\u003Cp>Speaker 1: I mean, this is this is insane. Like, with all the things that we're encountering here I mean, even just the boilerplate stuff of getting the prompt right, trying to find just a sample video file. Like, I you just I feel like you can just, like, mess around. And the worst case scenario, you just command z it and then start again, or at least get, like, a good 80% of the way there.\u003C\u002Fp>\u003Cp>Speaker 0: We got one minute and thirty six seconds. I think one of the the coolest things to showcase here is that we have a ready to go API. Right? And not just, I need, like, one API. We've got all the APIs for what we need on the back end of this thing.\u003C\u002Fp>\u003Cp>Speaker 1: I love that it's doing all the colors and iconography too. Like, that to me is it just takes so much time. Like, oh, okay. What what, like, material symbol icon should I use for this or that? Like, that plus the colors and all the form layout.\u003C\u002Fp>\u003Cp>Like, that's\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So let's go in and create a a like here. Here's our post. Cool. They created should be populated automatically.\u003C\u002Fp>\u003Cp>We got follows. We got comments. We can add a comment to the post. Test comment. Alright.\u003C\u002Fp>\u003Cp>User ID. Commenting on my own\u003C\u002Fp>\u003Cp>Speaker 1: I got a user. Who said who said test comment?\u003C\u002Fp>\u003Cp>Speaker 0: There we go. Alright. So thirty seconds left. I I feel like, you know, we didn't obviously do anything on the front end here, but now we we've got these APIs with a a little bit more time. We could probably slap together a front end, and have something up and running in under an hour, which, you know, the namesake of the show is is insane to me that you could do so much with DirectUs in an hour.\u003C\u002Fp>\u003Cp>But being able to get here in ten minutes, The entire back end is,\u003C\u002Fp>\u003Cp>Speaker 1: like, the the schema. We've got, like, everything sort of configured the right way. We got the full API, REST, GraphQL.\u003C\u002Fp>\u003Cp>Speaker 0: That's it. Ten minutes is up. Well, Ben, any kind of final words on Directus and AI before we we wrap this episode up?\u003C\u002Fp>\u003Cp>Speaker 1: I mean, it's just it's super exciting. Like, it's the confluence of two big things. Like, AI is just, like, variable, ephemeral, like, crazy new emergent technology with all this potential, but it's kind of a little errant with all the craziness that can get thrown into the mix. And then you've got this, like, super stable, reliable, resilient back end platform of directors. I think, like, at the intersection of those two things is is, as Jonathan would say, there there's magic there.\u003C\u002Fp>\u003Cp>Yeah. No. This is this is awesome.\u003C\u002Fp>\u003Cp>Speaker 0: And with that, we will end this episode. Thanks for joining us, and stay tuned for more.\u003C\u002Fp>\u003Cp>Speaker 1: Thank you, Brian.\u003C\u002Fp>","Alright. Welcome back to another episode of one app in ten minutes. This is a extra special season or, extra special show. Normally, this is 100 apps, one hundred hours. Today, we're doing everything in ten minutes. That comes with a lot of hype and A lot of pressure. This extra special episode. We've got mystery app with Ben. So mystery app on two levels. One, Ben, our CEO, had no idea he was being pulled into this. And two, I've got no idea what he wants to build, but we're gonna see if we could do it in ten minutes. Ben, welcome to the show. Thank you, Brian. I'm very excited to mysteriously be here. Yes. Alright. So the name of the game today, ten minutes to plan and build, no more, no less. You might wanna be thinking about what app we're gonna be building now. I'm gonna tell everybody else how we're making this possible. So I've got a blank instance of Directus set up. I've also got our Directus MCP server connected to Claude AI, and that is the the secret sauce that we're gonna be using today to try and go much faster. So, alright, what do you what do you wanna build today, Ben? My my first thought is, like, a content like a like a TikTok. TikTok. It's just, like, an easy Look at that. TikTok clone. TikTok clone. With the owner. This CEO. Alright. 10 on the clock. Alright. Next up, you know how this goes. I know you've seen the show. We're gonna start the clock. We're gonna talk about what functionality we need, and then we're gonna go. Alright. Clock is running. Alright, Ben. What do we need out of, a TikTok clone? Oof. We need to we need a feed, like a FYP, like a little just getting through getting through the content, getting through the videos. Need to be able to interact with those, so I guess give it a thumbs up. So, like, a little a favorites toggle. Like a likes. Mhmm. And then comments. I don't know what else is Comments and probably like a post. Right? The Yeah. I guess the content itself is like a video, the author. So I guess they should all be attached relationally to an author, and then a description, a time stamp, posted time stamp. Okay. Alright. Let's let's give this a shot. Right? We're gonna just copy paste this. We're gonna go to our trusty AI. You know, excited to be able to maybe separate fact and hype here. So, let's build a TikTok clone inside Directus. We want the following functionality. Personalized feeds, posts with likes and comments, and let's just use what? The direct Us users collection. Yeah. Mhmm. Let's use I was absolutely driving Reich crazy on another episode with typos. So let's use Is it apostrophe missing a typo? Yeah. There we go. Let's use direct users as the user's collection and reuse off there. Alright. We're gonna hit go, and we're gonna see what happens. I can I can vouch for the fact that I had no clue what this is gonna be, so you definitely didn't? So there's no Oh. Oh, no. Is this a oh, boy. Alright. We're just gonna add to the difficulty level here. It looks like I wasn't originally connected to the MCP. So we'll just refresh and go again. How we doing on time? Seven minutes forty seven seconds. Alright. We burned through a lot on that on that hour. No pressure. Right? If if one hour wasn't enough drama for you, let's do it all in ten minutes. So My water's empty. We're still over there. Alright. So let's see. We've got the direct assist assistant system prompt here. It's fetching the schema. Obviously, this is a blank instance, and then it's looking for the directest users collection. Alright. So now we can see that the AI is trying to go in and and create these collections inside direct us. And it looks like we've got posts, we've got likes, we've got comments, follows, hashtags for post discovery. Relational hashtags. And it looks like it's got a junction table for posts and hashtags. Mhmm. Cool. And, I I don't think you've had a ton of experience with the MCP yet other than our like, the video updates. Only my content. So the it it approaches this very systematically, and then it will go in and create the collections. And it looks like it has only created one collection at a time. Alright. So this is new behavior to me. Previously, it's done all the collections and then the relations, but we we should be getting the same effect. Like that time excuse there, Brian. Yeah. Yeah. Yeah. Yeah. Absolutely. Yeah. Yeah. Alright. So Oh, wow. So we've already got the likes. We could see the posts. Got a caption, a video file, a thumbnail, user ID. Is this all does this line up with what you would expect out of out of this bin? Yeah. I mean, my design brain is is, like, thinking of, like, a little bit of form layout, but I'm impressed that it's doing, like, half in full width, and it's kinda doing a really good first pass. Here's our awesome video. And, I don't have any. So let's see. That was my first concern is, like, you're not gonna have any, like, MP fours at the ready. We've got tons. Recdus MCP assets. What do we got? Blog post workflow. We could test the the TUS upload protocol at the same time. So, Meta, are you uploading an m c p p video for the m c p video? I was trying to. It looks like, some I'm getting some errors. So that's part of the fun of this. We just keep refreshing and and seeing more content or more data model show up. So we got post. Let's try this again. I'm gonna link this to my admin user. Here's our awesome video, And let's upload a video from device, see what it got. MCP assets. Do I have a video that is actually small enough? It looks like 30 megabytes is all I've got here. It feels like that that can work. Except I'm getting a issue. Oh. Oh. No stress. Alright. Let's just try an image for now. Oh, I've done something wrong with the images as well. I think TikTok has images, doesn't it? It it it does. It looks like something has gone wrong. I had reversed relationships. Okay. So now it's filling in the other relationships. And, unfortunately, I don't think it's gonna allow us to do this without a file, but we could solve for that really quickly. I know. I'm googling sample m p four file. I like cityscapes and all that on the mind I've seen. I think it is something that I've done here. Thirty seconds, 1.5 megs. 1.5 megs. Schema. It's like Add to proceed. Does this not know we are on a okay. So I spotted the issue, and that's a good point to bring up that this is really cool functionality to be able to prototype this quickly, but it's not a it's not a silver bullet. It looks like the MCP had tried to tag the wrong folder that did not exist inside the instance, and that's where the issue was coming from. So we could try this again. Here's our test post. Let's add admin user. Just going to I Slacked your URL if you wanna try doing it from URL. Okay. Alright. We'll try it from the URL. There we go. We've got the video file uploaded. We can set this to publish. We will save our post. And now that the MCP has done its thing, we got two minutes and forty seconds Infinite. To, let's just create create some test posts and sample content for each collection. Cool. Alright. In the meantime, Ben, what is great about Directus and how fast you could prototype like this? I mean, this is this is insane. Like, with all the things that we're encountering here I mean, even just the boilerplate stuff of getting the prompt right, trying to find just a sample video file. Like, I you just I feel like you can just, like, mess around. And the worst case scenario, you just command z it and then start again, or at least get, like, a good 80% of the way there. We got one minute and thirty six seconds. I think one of the the coolest things to showcase here is that we have a ready to go API. Right? And not just, I need, like, one API. We've got all the APIs for what we need on the back end of this thing. I love that it's doing all the colors and iconography too. Like, that to me is it just takes so much time. Like, oh, okay. What what, like, material symbol icon should I use for this or that? Like, that plus the colors and all the form layout. Like, that's Yeah. So let's go in and create a a like here. Here's our post. Cool. They created should be populated automatically. We got follows. We got comments. We can add a comment to the post. Test comment. Alright. User ID. Commenting on my own I got a user. Who said who said test comment? There we go. Alright. So thirty seconds left. I I feel like, you know, we didn't obviously do anything on the front end here, but now we we've got these APIs with a a little bit more time. We could probably slap together a front end, and have something up and running in under an hour, which, you know, the namesake of the show is is insane to me that you could do so much with DirectUs in an hour. But being able to get here in ten minutes, The entire back end is, like, the the schema. We've got, like, everything sort of configured the right way. We got the full API, REST, GraphQL. That's it. Ten minutes is up. Well, Ben, any kind of final words on Directus and AI before we we wrap this episode up? I mean, it's just it's super exciting. Like, it's the confluence of two big things. Like, AI is just, like, variable, ephemeral, like, crazy new emergent technology with all this potential, but it's kind of a little errant with all the craziness that can get thrown into the mix. And then you've got this, like, super stable, reliable, resilient back end platform of directors. I think, like, at the intersection of those two things is is, as Jonathan would say, there there's magic there. Yeah. No. This is this is awesome. And with that, we will end this episode. Thanks for joining us, and stay tuned for more. Thank you, Brian.","31c14d2e-45e1-4e6a-be3a-381f23d2362c",[486,487],"c1d5015e-188b-4fe4-9595-3f83f2e59241","c0d5cd0b-e341-4b10-8f91-d0efe6dc19a3",[],{"year":399,"number":400,"id":401,"episodes":490,"show":491},[403,404,405,386,406,407,408,409,410],{"title":412,"slug":413},{"id":409,"slug":493,"vimeo_id":494,"description":495,"tile":496,"length":159,"resources":12,"people":12,"episode_number":11,"published":365,"title":497,"video_transcript_html":498,"video_transcript_text":499,"content":12,"seo":500,"status":19,"episode_people":501,"recommendations":502,"season":503},"mcp-build-content-handling","1138950576","Wayne Eldridge talks through using the MCP for content handling.","fdc35657-28da-49fd-b023-ec3de32261c3","Community MCP Build: Content Handling","\u003Cp>Speaker 0: My name is Wayne Eldridge. I run a company called Anamica in sunny Cape Town, South Africa. We build everything from mobile apps to custom business internal facing apps, primarily indirectus, and that's how we discovered directus at the back end. When the directors MCP server got announced, we were very excited. We've done a whole internal shift, from programming line by line to, obviously, tab completions to agentic, coding and agentic software development, with the human in the loop.\u003C\u002Fp>\u003Cp>So everything's still verified by a human, but we have a whole system of, feedback loops loops, error logging, everything like that. So when the the director's MCP server, arrived, we were in the process of porting our website from Webflow, to Next. Js. Webflow did have its own built in CMS. However, we needed the customization of Directus, and we were very familiar with the product.\u003C\u002Fp>\u003Cp>So when the Directus MCP server was announced, we saw a great opportunity to port all of our blogs and content, from the, web flow CMS, into directors, specifically using, autonomous agents. Typically, what we would do is self host direct us. However, you can use Directus Cloud, to initiate this as a general blog post engine itself. We used Next. Js in the front end and, obviously, Directus in the back end.\u003C\u002Fp>\u003Cp>We would use Cursor and Sonnet four, whatever model that you wish to select in Cursor to take the content from, Webflow, via the Webflow MCP server, use cursor and sonnet to connect to the director's MCP server, choose an updated, updated director's blog post, and associate that content to that blog post category. We then discovered that whilst we ported several of our blogs, we still had a whole bunch of links of interesting, tech articles that we stored in Notion, and we thought ourselves, you know, we could share these links and specific content extremely quickly with the director's MCP server. We dropped in one or two links into Cursor, asked the, agent in Cursor to write a blog post about it, and send it to the MCP server associating the blog post category to, these content of the article. This happened very quickly and very seamlessly between the directors, MCP server, our agent, and the links sitting locally on our machine. We then decided to port all our links of interesting tech that we store internally at Dynamic and, put them on the websites under our resource hub.\u003C\u002Fp>\u003Cp>We can also receive articles written by our copywriters, put them within our code base, and ask the director's MCP server to, post the content onto our website. And along with that, update the website, which is written in Next. Js with any SEO, requirements that has been picked up by the content of the article itself, or without leaving cursor, linked to a director's MCP, and directors either sitting on a self hosted instance or, if you wish, in the directors cloud.\u003C\u002Fp>","My name is Wayne Eldridge. I run a company called Anamica in sunny Cape Town, South Africa. We build everything from mobile apps to custom business internal facing apps, primarily indirectus, and that's how we discovered directus at the back end. When the directors MCP server got announced, we were very excited. We've done a whole internal shift, from programming line by line to, obviously, tab completions to agentic, coding and agentic software development, with the human in the loop. So everything's still verified by a human, but we have a whole system of, feedback loops loops, error logging, everything like that. So when the the director's MCP server, arrived, we were in the process of porting our website from Webflow, to Next. Js. Webflow did have its own built in CMS. However, we needed the customization of Directus, and we were very familiar with the product. So when the Directus MCP server was announced, we saw a great opportunity to port all of our blogs and content, from the, web flow CMS, into directors, specifically using, autonomous agents. Typically, what we would do is self host direct us. However, you can use Directus Cloud, to initiate this as a general blog post engine itself. We used Next. Js in the front end and, obviously, Directus in the back end. We would use Cursor and Sonnet four, whatever model that you wish to select in Cursor to take the content from, Webflow, via the Webflow MCP server, use cursor and sonnet to connect to the director's MCP server, choose an updated, updated director's blog post, and associate that content to that blog post category. We then discovered that whilst we ported several of our blogs, we still had a whole bunch of links of interesting, tech articles that we stored in Notion, and we thought ourselves, you know, we could share these links and specific content extremely quickly with the director's MCP server. We dropped in one or two links into Cursor, asked the, agent in Cursor to write a blog post about it, and send it to the MCP server associating the blog post category to, these content of the article. This happened very quickly and very seamlessly between the directors, MCP server, our agent, and the links sitting locally on our machine. We then decided to port all our links of interesting tech that we store internally at Dynamic and, put them on the websites under our resource hub. We can also receive articles written by our copywriters, put them within our code base, and ask the director's MCP server to, post the content onto our website. And along with that, update the website, which is written in Next. Js with any SEO, requirements that has been picked up by the content of the article itself, or without leaving cursor, linked to a director's MCP, and directors either sitting on a self hosted instance or, if you wish, in the directors cloud.","ee3f18af-3840-4cf7-bb81-794869789eb5",[],[],{"year":399,"number":400,"id":401,"episodes":504,"show":505},[403,404,405,386,406,407,408,409,410],{"title":412,"slug":413},{"id":407,"slug":507,"vimeo_id":508,"description":509,"tile":510,"length":329,"resources":12,"people":12,"episode_number":195,"published":365,"title":511,"video_transcript_html":512,"video_transcript_text":513,"content":12,"seo":514,"status":19,"episode_people":515,"recommendations":518,"season":519},"10-mins-menu-app","1138952700","Bryant and Rijk try to build the project of Rijk's dreams using the MCP, in 10 minutes or less!","9019e6ad-9dd3-42ab-9840-b39bf58fc5b7","Building in 10 Minutes or Less: Menu App","\u003Cp>Speaker 0: Alright. Welcome back to an extra special episode of one app in ten minutes. I'm your host, Brian Gillespie, for Directus. Today, I honestly like, I I hope I'm not gonna get fired here, but, we are going to be building a mystery app with our CTO here at Directus, Reich Van Zanten. And I call this mystery app, and I say, hey.\u003C\u002Fp>\u003Cp>I hope I don't get fired in that, I've invited Reich to a call, but he has no idea he's gonna be on the show. And I call it mystery app because I have no idea what idea he's gonna throw out there or if it's even possible. Previously, the show was called 100 apps, hundred hours for this season or this spin off here. It is one app in ten minutes. The rules are we have ten minutes to plan and build an app.\u003C\u002Fp>\u003Cp>No more, no less. In this case, we're gonna be using what we do have at our disposal, which is Directus, claw.ai, and, Directus MCP connected. Welcome to one app ten minutes, Rick.\u003C\u002Fp>\u003Cp>Speaker 1: One app ten minutes.\u003C\u002Fp>\u003Cp>Speaker 0: For the audience, we are recording live. Did you have any idea that you're gonna be on this show today?\u003C\u002Fp>\u003Cp>Speaker 1: I had no clue. But here we\u003C\u002Fp>\u003Cp>Speaker 0: are. Amazing. Amazing. Alright. So I think you're already aware of 100 apps, hundred hours, one app, ten minutes, shortened time frame, leveraging AI.\u003C\u002Fp>\u003Cp>I've got mystery app here with Wrike. The world wants to know what what app are we gonna build today?\u003C\u002Fp>\u003Cp>Speaker 1: In ten minutes?\u003C\u002Fp>\u003Cp>Speaker 0: In ten minutes.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, boy. Well, I could spend ten minutes thinking about an app, but that's not what I wanna do.\u003C\u002Fp>\u003Cp>Speaker 0: The most incredible cold opened ever, ladies and gentlemen. What a what a do you thought you were coming into, an afternoon meeting? Amazing.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, one of many. One of many. Oh, this is I'm woefully ill prepared for this. Okay. We have ten minutes.\u003C\u002Fp>\u003Cp>Well, the time hasn't started yet,\u003C\u002Fp>\u003Cp>Speaker 0: has it? No. No. No. You got we gotta decide the app, then we got ten minutes to plan and build the thing, which is it is very ambitious in and of itself.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I will say. Okay. Ten minutes. That makes it so much trickier.\u003C\u002Fp>\u003Cp>I was gonna say, I was recently working oh, maybe we can do a part of that. I was gonna say, I was recently working on sort of, like, a quizzing app.\u003C\u002Fp>\u003Cp>Speaker 0: A quizzing app. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Nights and weekends for, you know, getting, sending people a link. You could sign in. You can you can do a little quiz, and then we could check out the leaderboard. That is not a ten minute I don't ever at all. I'll tell you that.\u003C\u002Fp>\u003Cp>Oh. But maybe it is. Let's see here. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: No pressure.\u003C\u002Fp>\u003Cp>Speaker 1: None whatsoever.\u003C\u002Fp>\u003Cp>Speaker 0: To give you context, we've already done a lot of the former 100 apps, hundred hours. So Expensify clone, Airbnb, Netflix. Like, already done all these things. Multi site CMS. All the all the expected fruit is is already picked off the tree.\u003C\u002Fp>\u003Cp>Speaker 1: You're not making it any easier. Wait. If this is a if if we're gonna be using AI to build this stuff, why don't we use AI to come up with what we're building in the first place?\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Alright. Alright. I've got\u003C\u002Fp>\u003Cp>Speaker 1: I've got ten minutes.\u003C\u002Fp>\u003Cp>Speaker 0: Rike Van Zanten, our CTO, on an episode of one app, ten minutes. What should we build? Bum bum bum. I'm Curious to see if it calls the actual direct SMTP here, but I I don't think so. Real time dashboard builder.\u003C\u002Fp>\u003Cp>Oh, it it knows you. You're a known quantity. Clearly. Schema visualizer, webhook, ecommerce, headless ecommerce, QR code menu generator, team mood tracker, meeting room optimizer.\u003C\u002Fp>\u003Cp>Speaker 1: I think the QR menu one is actually not too bad of an idea. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Because this is a restaurant.\u003C\u002Fp>\u003Cp>Speaker 1: Thing or you're going in, you're sitting down at the table, you have to scan a little QR code, you pull up a menu on your phone. So very remote. Where does this menu come from? Where does it go? Could be something.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Restaurant menu. QR code is doing everything.\u003C\u002Fp>\u003Cp>Speaker 1: Ten minutes where I create a new startup on the fly.\u003C\u002Fp>\u003Cp>Speaker 0: Restaurant menu, QR code, generator thingy. There we go. Alright. We're ready? We're starting the clock.\u003C\u002Fp>\u003Cp>What do we need out of this thing? Obviously Well,\u003C\u002Fp>\u003Cp>Speaker 1: we need menus. We need things on the menu. So we need menu items. We need ideally some sort of, like, allergy information for every menu item. So that is, like, ingredients maybe, or, like, allergy information, or components.\u003C\u002Fp>\u003Cp>Like, sometimes on menus, you see, you know, the menu item and then individual parts of what's in it. Right? Yep. So yesterday night, at least four times of what is in the Negroni. Just kidding.\u003C\u002Fp>\u003Cp>What else do we need? We need well, we're gonna be generating a QR code for every menu, I guess, because we're rendering out each menu on a separate page. So that makes sense.\u003C\u002Fp>\u003Cp>Speaker 0: That QR code?\u003C\u002Fp>\u003Cp>Speaker 1: Oh, we need a a sort of category for the menu item. Like, is it an entree, or is it an appetizer, or is it a dessert? And then on the menu itself, we probably need a similar thing for, is it a lunch menu, or a dinner menu, or some special event? You know, this is like a Christmas day, menu or something.\u003C\u002Fp>\u003Cp>Speaker 0: So what would that be?\u003C\u002Fp>\u003Cp>Speaker 1: I was already thinking Yeah. Type a menu. Yeah. Holiday special.\u003C\u002Fp>\u003Cp>Speaker 0: Holiday. Alright. Now, I've already set the audience up for this, but what we've got already is I've got a blank direct to sentence. We've got the Claude AI connected via the direct to MCP, and let's build the menu system. So I'm just gonna dump this in here.\u003C\u002Fp>\u003Cp>Again, we're on the clock. So, you know, we're just gonna oh, gosh. Okay. That's\u003C\u002Fp>\u003Cp>Speaker 1: Oh, no.\u003C\u002Fp>\u003Cp>Speaker 0: That's not even formatted properly. Okay. Allergy category for the menu. Flow for during QR code. Alright.\u003C\u002Fp>\u003Cp>This is the magic moment. Can you one shot a brand new startup in seven minutes and fifty five seconds? We'll see.\u003C\u002Fp>\u003Cp>Speaker 1: Well, in no context either. I mean, you're just you're giving it nothing. You're truly giving it nothing.\u003C\u002Fp>\u003Cp>Speaker 0: Absolutely nothing. Right? So the Directed MCP, we've got a bunch of tools that are preprogrammed into this thing. And you'll see, like, the system prompt here, which is basically just some information about, how the assistant should act. Then we have a tool that allows it to introspect the direct to schema or or not even introspect, just see the direct to schema.\u003C\u002Fp>\u003Cp>And here it's already gone to work. So I'm just gonna hit refresh over here on the left. And by the power of AI combined, we now have some stuff. So we can start creating a new menu. This is, Reich's special lunch menu.\u003C\u002Fp>\u003Cp>And is this, is this something that we would expect to see? I mean, how would you grade this? So we got the type of lunch. That's pretty solid. Is this active?\u003C\u002Fp>\u003Cp>There's the QR code for the menu. Uh-oh. Doesn't like that, does it?\u003C\u002Fp>\u003Cp>Speaker 1: Is it still create? Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: It is still adding our relationships. So maybe that's where\u003C\u002Fp>\u003Cp>Speaker 1: I would I would do it a second until it's done, frankly.\u003C\u002Fp>\u003Cp>Speaker 0: Trying to get ahead of ourselves.\u003C\u002Fp>\u003Cp>Speaker 1: Because we only have so we only have so many seconds. But\u003C\u002Fp>\u003Cp>Speaker 0: There we go. Alright. So one thing that that I really like about this is just seeing, like, the volume of work that I would have to to do myself. Now it like, it's not that much work to spin up a back end with Directus. And the the visual, like, drag and drop the data model part of it is beautiful.\u003C\u002Fp>\u003Cp>But when you're watching a machine do all of this work for you, man, that's it's even better.\u003C\u002Fp>\u003Cp>Speaker 1: And, man, what can I say? What what is better than doing work is just not doing work.\u003C\u002Fp>\u003Cp>Speaker 0: Not doing any work. Alright. So we're gonna call this, like, special lunch menu. Now let's see if this actually works this time around. There we go.\u003C\u002Fp>\u003Cp>Alright. So we've got a menu. Looks like it's already added some menu items. How do you feel\u003C\u002Fp>\u003Cp>Speaker 1: about be seeing eating jumbo shrimp for lunch or Atlantic salmon for that, man.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. We've got our ingredients. Looks like we've got an allergens. Okay. We got a preset there.\u003C\u002Fp>\u003Cp>And then we got our categories for appetizers, mains, desserts, beverages. What is this thing doing? Okay. It's\u003C\u002Fp>\u003Cp>Speaker 1: looking like I think it's building the front end for it now too.\u003C\u002Fp>\u003Cp>Speaker 0: Wow. It's actually building the front end. I didn't even ask that. But let's just let's check on the the flow progress. Right?\u003C\u002Fp>\u003Cp>We got five minutes left, which is, actually, I'm I'm surprised we're even here at this point.\u003C\u002Fp>\u003Cp>Speaker 1: But I'll say stop the clock because I'm seeing a menu on the screen right now.\u003C\u002Fp>\u003Cp>Speaker 0: Right. Is it is it actually calling Directus too? Oh, no. Of course. Yeah.\u003C\u002Fp>\u003Cp>Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Okay. It's just gonna send that be too good to be true. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So let's talk easy enough to do, though. This is one SDK call and you're in in business.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So let's toggle this and see. Generate QR code for the menu. Okay. So we've generated the QR code.\u003C\u002Fp>\u003Cp>Looks like it might have missed a few steps. We generated a QR code. Is this even valid? What is the QR server?\u003C\u002Fp>\u003Cp>Speaker 1: I don't think I've ever seen this. Is that a thing? QR is a dummy. I think that's just a dummy, isn't it?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. QR code, API. Can we get something really quickly? Yeah.\u003C\u002Fp>\u003Cp>Let's see here\u003C\u002Fp>\u003Cp>Speaker 1: another thing. We can oh, yeah. Maybe.\u003C\u002Fp>\u003Cp>Speaker 0: We're just gonna straight up vibe code. Gotta go back. Gotta go back. Gotta go back. Fix the flow to import the generated.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, yeah. Stop making typos, Brian. We're on the clock. Come on.\u003C\u002Fp>\u003Cp>Speaker 0: The QR code generated QR code. Here's the service. What's the endpoint? What's the endpoint?\u003C\u002Fp>\u003Cp>Speaker 1: I can find it. I'm sure I can do it.\u003C\u002Fp>\u003Cp>Speaker 0: This is so much more fun with a partner.\u003C\u002Fp>\u003Cp>Speaker 1: Brian, let's go. Let's go. Let's go. Let's go. Come on.\u003C\u002Fp>\u003Cp>Come on.\u003C\u002Fp>\u003Cp>Speaker 0: Files. Come on. Import. Files import.\u003C\u002Fp>\u003Cp>Speaker 1: If we do as an import file operation, is that a thing? Is that even exist?\u003C\u002Fp>\u003Cp>Speaker 0: We do not have that.\u003C\u002Fp>\u003Cp>Speaker 1: We should.\u003C\u002Fp>\u003Cp>Speaker 0: Can you\u003C\u002Fp>\u003Cp>Speaker 1: build that in ten minutes? Same. We could.\u003C\u002Fp>\u003Cp>Speaker 0: Import file URL. Hit point. Oh, it's still building. Okay. It's reconnecting.\u003C\u002Fp>\u003Cp>Reconnecting. Alright. So it's doing some some type of work on our behalf here. QR code. Oh, no.\u003C\u002Fp>\u003Cp>Dun dun dun. Two minutes still. Alright. What do we what do we got here? Okay.\u003C\u002Fp>\u003Cp>Still not generating the QR code. Okay. Well, I think one thing we can agree on here\u003C\u002Fp>\u003Cp>Speaker 1: I mean, a QR code, you can easily generate on the client side. There's a bunch of libraries for that kind of stuff. So that that's the least of my concerns, frankly.\u003C\u002Fp>\u003Cp>Speaker 0: Least of the concerns. Alright. What other concerns do we have here?\u003C\u002Fp>\u003Cp>Speaker 1: Well, let's just quickly check on the data model. Right? Because we have we have menus, we have items, we have ingredients, we have the allergens on the ingredients. So all this is coming together really, really nicely.\u003C\u002Fp>\u003Cp>Speaker 0: Shall we, like, look at the API calls? Because, obviously, you're gonna render this on the front end.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Did you give it a, like, public access or is it already?\u003C\u002Fp>\u003Cp>Speaker 0: I've just logged in. So we can make this let's let's yeah. Maybe showcase that really quickly. Alright. I can make all this publicly accessible just by going to our access policies and adding all these collections.\u003C\u002Fp>\u003Cp>I need faster fingers. There we go. Alright. We go back. Items, menus, fields.\u003C\u002Fp>\u003Cp>There we go. We've got our lunch menu. Oh, I forgot to add the the items to the menu. Can we do this? Where's the menu?\u003C\u002Fp>\u003Cp>Menu. Right? Special lunch menu. Add our existing items. Where we at?\u003C\u002Fp>\u003Cp>Twenty five seconds.\u003C\u002Fp>\u003Cp>Speaker 1: Hit save.\u003C\u002Fp>\u003Cp>Speaker 0: Items, menus, fields. Okay. I don't see the menu items. We need menu items dot asterisk.\u003C\u002Fp>\u003Cp>Speaker 1: There we go.\u003C\u002Fp>\u003Cp>Speaker 0: Junction table.\u003C\u002Fp>\u003Cp>Speaker 1: Items. Double star it. Double star.\u003C\u002Fp>\u003Cp>Speaker 0: Double star. No. Well, that's it. That's the ten minutes. We have built a menu generating app thingy.\u003C\u002Fp>\u003Cp>Ten minutes or less. How do we feel about this?\u003C\u002Fp>\u003Cp>Speaker 1: It's pretty cool. I will say. My favorite is the icons and the colors for the for the collection.\u003C\u002Fp>\u003Cp>Speaker 0: I so that again, like, this is all AI, you know, there's a lot of hype out there. But one thing that that I appreciate about the MCP that we built here is there's real value to this, in my opinion, of how quick can you prototype, how fast can you iterate on this. In ten minutes or less, completely fresh. No idea. There it is.\u003C\u002Fp>\u003Cp>Restaurant menu app down. Reich, anything, anything you'd like to add as we we close this episode, this chaotic episode, if you didn't need enough drama and one app in ten minutes bringing in somebody totally unexpectedly. Well, Reich, thank you for joining me for this. You know, again, I think this is a lot of fun, but again, it just goes to show how far and how fast you could get when you combine Directus and AI. That's it, folks.\u003C\u002Fp>\u003Cp>Make sure you stay tuned for more episodes of one app, ten minutes.\u003C\u002Fp>","Alright. Welcome back to an extra special episode of one app in ten minutes. I'm your host, Brian Gillespie, for Directus. Today, I honestly like, I I hope I'm not gonna get fired here, but, we are going to be building a mystery app with our CTO here at Directus, Reich Van Zanten. And I call this mystery app, and I say, hey. I hope I don't get fired in that, I've invited Reich to a call, but he has no idea he's gonna be on the show. And I call it mystery app because I have no idea what idea he's gonna throw out there or if it's even possible. Previously, the show was called 100 apps, hundred hours for this season or this spin off here. It is one app in ten minutes. The rules are we have ten minutes to plan and build an app. No more, no less. In this case, we're gonna be using what we do have at our disposal, which is Directus, claw.ai, and, Directus MCP connected. Welcome to one app ten minutes, Rick. One app ten minutes. For the audience, we are recording live. Did you have any idea that you're gonna be on this show today? I had no clue. But here we are. Amazing. Amazing. Alright. So I think you're already aware of 100 apps, hundred hours, one app, ten minutes, shortened time frame, leveraging AI. I've got mystery app here with Wrike. The world wants to know what what app are we gonna build today? In ten minutes? In ten minutes. Oh, boy. Well, I could spend ten minutes thinking about an app, but that's not what I wanna do. The most incredible cold opened ever, ladies and gentlemen. What a what a do you thought you were coming into, an afternoon meeting? Amazing. Oh, one of many. One of many. Oh, this is I'm woefully ill prepared for this. Okay. We have ten minutes. Well, the time hasn't started yet, has it? No. No. No. You got we gotta decide the app, then we got ten minutes to plan and build the thing, which is it is very ambitious in and of itself. Yeah. I will say. Okay. Ten minutes. That makes it so much trickier. I was gonna say, I was recently working oh, maybe we can do a part of that. I was gonna say, I was recently working on sort of, like, a quizzing app. A quizzing app. Okay. Nights and weekends for, you know, getting, sending people a link. You could sign in. You can you can do a little quiz, and then we could check out the leaderboard. That is not a ten minute I don't ever at all. I'll tell you that. Oh. But maybe it is. Let's see here. Okay. No pressure. None whatsoever. To give you context, we've already done a lot of the former 100 apps, hundred hours. So Expensify clone, Airbnb, Netflix. Like, already done all these things. Multi site CMS. All the all the expected fruit is is already picked off the tree. You're not making it any easier. Wait. If this is a if if we're gonna be using AI to build this stuff, why don't we use AI to come up with what we're building in the first place? Alright. Alright. Alright. I've got I've got ten minutes. Rike Van Zanten, our CTO, on an episode of one app, ten minutes. What should we build? Bum bum bum. I'm Curious to see if it calls the actual direct SMTP here, but I I don't think so. Real time dashboard builder. Oh, it it knows you. You're a known quantity. Clearly. Schema visualizer, webhook, ecommerce, headless ecommerce, QR code menu generator, team mood tracker, meeting room optimizer. I think the QR menu one is actually not too bad of an idea. Right? Because this is a restaurant. Thing or you're going in, you're sitting down at the table, you have to scan a little QR code, you pull up a menu on your phone. So very remote. Where does this menu come from? Where does it go? Could be something. Alright. Restaurant menu. QR code is doing everything. Ten minutes where I create a new startup on the fly. Restaurant menu, QR code, generator thingy. There we go. Alright. We're ready? We're starting the clock. What do we need out of this thing? Obviously Well, we need menus. We need things on the menu. So we need menu items. We need ideally some sort of, like, allergy information for every menu item. So that is, like, ingredients maybe, or, like, allergy information, or components. Like, sometimes on menus, you see, you know, the menu item and then individual parts of what's in it. Right? Yep. So yesterday night, at least four times of what is in the Negroni. Just kidding. What else do we need? We need well, we're gonna be generating a QR code for every menu, I guess, because we're rendering out each menu on a separate page. So that makes sense. That QR code? Oh, we need a a sort of category for the menu item. Like, is it an entree, or is it an appetizer, or is it a dessert? And then on the menu itself, we probably need a similar thing for, is it a lunch menu, or a dinner menu, or some special event? You know, this is like a Christmas day, menu or something. So what would that be? I was already thinking Yeah. Type a menu. Yeah. Holiday special. Holiday. Alright. Now, I've already set the audience up for this, but what we've got already is I've got a blank direct to sentence. We've got the Claude AI connected via the direct to MCP, and let's build the menu system. So I'm just gonna dump this in here. Again, we're on the clock. So, you know, we're just gonna oh, gosh. Okay. That's Oh, no. That's not even formatted properly. Okay. Allergy category for the menu. Flow for during QR code. Alright. This is the magic moment. Can you one shot a brand new startup in seven minutes and fifty five seconds? We'll see. Well, in no context either. I mean, you're just you're giving it nothing. You're truly giving it nothing. Absolutely nothing. Right? So the Directed MCP, we've got a bunch of tools that are preprogrammed into this thing. And you'll see, like, the system prompt here, which is basically just some information about, how the assistant should act. Then we have a tool that allows it to introspect the direct to schema or or not even introspect, just see the direct to schema. And here it's already gone to work. So I'm just gonna hit refresh over here on the left. And by the power of AI combined, we now have some stuff. So we can start creating a new menu. This is, Reich's special lunch menu. And is this, is this something that we would expect to see? I mean, how would you grade this? So we got the type of lunch. That's pretty solid. Is this active? There's the QR code for the menu. Uh-oh. Doesn't like that, does it? Is it still create? Yeah. It is still adding our relationships. So maybe that's where I would I would do it a second until it's done, frankly. Trying to get ahead of ourselves. Because we only have so we only have so many seconds. But There we go. Alright. So one thing that that I really like about this is just seeing, like, the volume of work that I would have to to do myself. Now it like, it's not that much work to spin up a back end with Directus. And the the visual, like, drag and drop the data model part of it is beautiful. But when you're watching a machine do all of this work for you, man, that's it's even better. And, man, what can I say? What what is better than doing work is just not doing work. Not doing any work. Alright. So we're gonna call this, like, special lunch menu. Now let's see if this actually works this time around. There we go. Alright. So we've got a menu. Looks like it's already added some menu items. How do you feel about be seeing eating jumbo shrimp for lunch or Atlantic salmon for that, man. Alright. We've got our ingredients. Looks like we've got an allergens. Okay. We got a preset there. And then we got our categories for appetizers, mains, desserts, beverages. What is this thing doing? Okay. It's looking like I think it's building the front end for it now too. Wow. It's actually building the front end. I didn't even ask that. But let's just let's check on the the flow progress. Right? We got five minutes left, which is, actually, I'm I'm surprised we're even here at this point. But I'll say stop the clock because I'm seeing a menu on the screen right now. Right. Is it is it actually calling Directus too? Oh, no. Of course. Yeah. Yeah. Yeah. Okay. It's just gonna send that be too good to be true. Yeah. Yeah. Alright. So let's talk easy enough to do, though. This is one SDK call and you're in in business. Yeah. So let's toggle this and see. Generate QR code for the menu. Okay. So we've generated the QR code. Looks like it might have missed a few steps. We generated a QR code. Is this even valid? What is the QR server? I don't think I've ever seen this. Is that a thing? QR is a dummy. I think that's just a dummy, isn't it? Yeah. Yeah. QR code, API. Can we get something really quickly? Yeah. Let's see here another thing. We can oh, yeah. Maybe. We're just gonna straight up vibe code. Gotta go back. Gotta go back. Gotta go back. Fix the flow to import the generated. Oh, yeah. Stop making typos, Brian. We're on the clock. Come on. The QR code generated QR code. Here's the service. What's the endpoint? What's the endpoint? I can find it. I'm sure I can do it. This is so much more fun with a partner. Brian, let's go. Let's go. Let's go. Let's go. Come on. Come on. Files. Come on. Import. Files import. If we do as an import file operation, is that a thing? Is that even exist? We do not have that. We should. Can you build that in ten minutes? Same. We could. Import file URL. Hit point. Oh, it's still building. Okay. It's reconnecting. Reconnecting. Alright. So it's doing some some type of work on our behalf here. QR code. Oh, no. Dun dun dun. Two minutes still. Alright. What do we what do we got here? Okay. Still not generating the QR code. Okay. Well, I think one thing we can agree on here I mean, a QR code, you can easily generate on the client side. There's a bunch of libraries for that kind of stuff. So that that's the least of my concerns, frankly. Least of the concerns. Alright. What other concerns do we have here? Well, let's just quickly check on the data model. Right? Because we have we have menus, we have items, we have ingredients, we have the allergens on the ingredients. So all this is coming together really, really nicely. Shall we, like, look at the API calls? Because, obviously, you're gonna render this on the front end. Yeah. Did you give it a, like, public access or is it already? I've just logged in. So we can make this let's let's yeah. Maybe showcase that really quickly. Alright. I can make all this publicly accessible just by going to our access policies and adding all these collections. I need faster fingers. There we go. Alright. We go back. Items, menus, fields. There we go. We've got our lunch menu. Oh, I forgot to add the the items to the menu. Can we do this? Where's the menu? Menu. Right? Special lunch menu. Add our existing items. Where we at? Twenty five seconds. Hit save. Items, menus, fields. Okay. I don't see the menu items. We need menu items dot asterisk. There we go. Junction table. Items. Double star it. Double star. Double star. No. Well, that's it. That's the ten minutes. We have built a menu generating app thingy. Ten minutes or less. How do we feel about this? It's pretty cool. I will say. My favorite is the icons and the colors for the for the collection. I so that again, like, this is all AI, you know, there's a lot of hype out there. But one thing that that I appreciate about the MCP that we built here is there's real value to this, in my opinion, of how quick can you prototype, how fast can you iterate on this. In ten minutes or less, completely fresh. No idea. There it is. Restaurant menu app down. Reich, anything, anything you'd like to add as we we close this episode, this chaotic episode, if you didn't need enough drama and one app in ten minutes bringing in somebody totally unexpectedly. Well, Reich, thank you for joining me for this. You know, again, I think this is a lot of fun, but again, it just goes to show how far and how fast you could get when you combine Directus and AI. That's it, folks. Make sure you stay tuned for more episodes of one app, ten minutes.","7ea951b9-ee5d-456e-8e38-ea63fc3010f0",[516,517],"6dcc99c9-1ea6-46e3-a22e-8b730be473a3","8e8ddf61-cf6f-4a94-99fd-57c07f3b26b8",[],{"year":399,"number":400,"id":401,"episodes":520,"show":521},[403,404,405,386,406,407,408,409,410],{"title":412,"slug":413},{"id":408,"slug":523,"vimeo_id":524,"description":525,"tile":526,"length":527,"resources":12,"people":12,"episode_number":129,"published":365,"title":528,"video_transcript_html":529,"video_transcript_text":530,"content":12,"seo":531,"status":19,"episode_people":532,"recommendations":533,"season":534},"mcp-build-note-app","1138970580","Joshua Bemendorfer talks through his project: building a note-taking app.","51d2f9d1-b2b2-420f-bf86-e7c7dba6926e",9,"Community MCP Build: Note-Taking App","\u003Cp>Speaker 0: Today, I would like to show you how to supercharge AI batch processing using Directus MCP with a self learning note taking workflow. Batch processing is really effective for, for situations where you need raw speed, but it kinda breaks down when you need to make creative decisions on individual items and on the batch as a whole. You can't really do that without a human element. AI models, I found, are great for those sorts of tasks, but they break down when you give them a whole bunch of data. And they also struggle with finding the right information to pull in to make those creative decisions.\u003C\u002Fp>\u003Cp>So the solution I found is to teach AI to take notes. By implementing a workflow that lets Directus MCP take notes directly on whatever it's working on, we gain the ability to do things like long running task tracking because the model can pick up where it left off and and, start fresh with a new context window. And we also get things like self improving database access. You take a note on all the different things that you ran into difficulty with and what you did to solve them and how it worked out. And it speeds up future runs because it can read that note and go, okay.\u003C\u002Fp>\u003Cp>So don't do this. Do this, and I'll be able to just continue with the task I was working on. And then by letting it take notes, we can consolidate a whole bunch of data that was stored in the database. Say, Say, for example, you wanted to pull out all of the aliases for your articles that are related to rabbits and you also want the ID for each one. Well, you can just dump that in a note and analyze it in future runs.\u003C\u002Fp>\u003Cp>So in our case, Directus is the knowledge backbone. Every note is just a key value pair. I'll go ahead and show you the notes table here. It's a very simple setup. We have a key, which is just a string, and this is what the AI model uses to kind of categorize what the different notes are about.\u003C\u002Fp>\u003Cp>And we have the value, and that's just a big markdown, field. It's a very simple setup, but it's surprisingly powerful. By using semantic keys and explicit instructions on how to record and review notes, we give the AI models a very quick and effective way to find the context they need. So this database has a problem. We have a whole bunch of articles.\u003C\u002Fp>\u003Cp>We have a 124 articles. The titles don't really make any sense. Oh, the bodies are in Latin. And I I can't work with this. So I've created a prompt for Claude to read the article titles and come up with a concept proposal for each one.\u003C\u002Fp>\u003Cp>What could this article actually be about? So let's look at the system prompt here. You can see that there's two things that it's informing the AI model about. That there's an AI notes table in the rough structure of that table and that it needs to read and record its database insights, especially how it solved problems it ran into and just update that note on every run. This gives it the self learning ability we were talking about earlier.\u003C\u002Fp>\u003Cp>It's able to figure out what it ran into last time and fix it on the next run. Now back to the task. I've told the AI model it's a skilled content analyst and writer. We have a huge collection of articles. All the bodies are full of nonsense, all that.\u003C\u002Fp>\u003Cp>Alright. So its job is to record the concepts that it comes up with in an article concepts note. It's not sure what to do with an article based on the title. Be creative. Find a wacky concept.\u003C\u002Fp>\u003Cp>Then we have some very specific instructions. We say always read the template and template article concepts, this is a note, to determine how to structure your output. Always create a new article concepts note for each run. Always give it the exact name. Always process 30 articles.\u003C\u002Fp>\u003Cp>Always review the last note you created before starting to make sure you don't duplicate any work. This template lets us make sure that we get consistent output on every run. Alright. Let's go ahead and run this a few times and see what its output looks like. I turn on auto refresh, and then I'm gonna go into Claude, add a prompt from Directus, and examine articles and propose concepts.\u003C\u002Fp>\u003Cp>If you want to know how to use this feature, go ahead and take a look at the Directus MCP documentation, and then we're just gonna send it. Now one of the fun things you can do with read and write access direct to Directus is if the model makes a mistake, you can ask it to update the prompt to fix its mistake in future runs. Article concepts. It has identified directly that just about every article appears to be lorem ipsum gibberish and occasional test content. Alright.\u003C\u002Fp>\u003Cp>So came up with a bunch of different articles on a bunch of different ideas. In a bit, we're gonna use this to actually write all these different article concepts. But for now, I just wanna keep going through the batch process to show you how it's able to pick up where it left off and continue. On this run, if you notice, it actually picked up that it needed to look up the translations and didn't have to figure that out from the schema this time. Alright.\u003C\u002Fp>\u003Cp>It is a new day. My cloud usage limits have reset, and we are ready to continue with step two, which is generating the articles based on the concepts that we've put together. All of the, article concepts have now been saved in these, concept notes. They take the article title. They try to figure out what on earth, the article should be about and propose a concept.\u003C\u002Fp>\u003Cp>So now we have the next step of the process, which is to generate a actual article for each of the articles we generate concepts on in both English and German. And we're gonna go in batches of 10 and see how well that works. We'll take this prompt and it should automatically read over all of our article concept notes and start filling in articles for those. Let's go. So we're going to use the turn concepts into full fledged articles prompt.\u003C\u002Fp>\u003Cp>That's going to go and read through all of our article concept notes and start filling in those articles, and I'll show you those articles as it writes them. I have no idea if these articles are going to make any sense. This is going to be fun. I'm having fun reading through these articles. I have no idea how helpful any of this is, but it at least sounds very convincing.\u003C\u002Fp>\u003Cp>Now, the key advantage to this approach is that if we were generating concepts and writing articles at the same time, we'd have to use much smaller batches. But because we split the process into two steps, we've saved a ton of context window, and we're able to work in larger batches. And as a bonus, we can do multiple things with those article summaries, while we're working on generating those articles from those summaries. Now, this isn't the most efficient workflow for generating articles. There's way better workflows for that.\u003C\u002Fp>\u003Cp>We're just demonstrating this concept. But a nice thing about this approach is that you can perform multiple tasks at the same time. For example, while we were working on these articles, we could also be working on something completely different using the notes that we're generating the articles from. I have this prompt here, categorize article concepts, which will allow Claude to suggest an article taxonomy based on the summaries it's already generated. It'll just read all the article concept notes and create a new note suggesting that new taxonomy.\u003C\u002Fp>\u003Cp>I like the way it describes these articles as a fascinating collection of technical and business focused article concepts with creative jargon filled titles that have been transformed into practical valuable content ideas. I'm not quite sure I'd be so positive about it, but it gets the idea across. Alright. Let's take a look at the categories that it generated and the opportunities that identified. So here's some content gaps, AI and ethics, sustainable technology operations, human centered digital transformation, all sorts of other different things.\u003C\u002Fp>\u003Cp>And here are some of the categories that's come up with. System architecture, it's put a bunch of different articles in that category, Ecommerce, digital business, data management and analytics, user experience and interface design, business operations or business optimization management, automation and AI systems, project management and collaboration. So it's basically taken all of those articles that we put together and categorized them according to common themes in those articles. This could be really useful if you have an existing content collection that you're trying to build a new categorization system for. And what's great is you could just take this and create another prompt to actually apply that taxonomy to articles, and create categories and all sorts of different things for that in Directus.\u003C\u002Fp>\u003Cp>So by the end of this run, here's what the AI model has built: concept proposals for every article in our database, full body content for many of those articles in English and German, a whole new content categorization system for the articles it wrote, progress logs so that it doesn't lose its place, and database access hints that documents how the challenges were solved so that on future runs, it could do even better. Instead of treating AI like a disposable worker or one that can infinitely stuff and stuff and stuff and work on something until it gets all confused, we're treating it like a teammate that tracks and follows up on what it's done across multiple days, multiple runs, starting with a clean slate on each task and only pulling in the information that it needs. We turned a human in the loop batch processing task into an AI creative workflow by splitting it into discrete steps and letting it kind of pick up where it left off. We gave the model a way to track progress. We built templates to make the results more consistent and reusable, and we recorded information about the database to help the AI model get smarter over time.\u003C\u002Fp>\u003Cp>I've had a ton of fun nailing down this workflow and testing it on all sorts of tasks. For my work, we're actually using it to do things similar to this where we're analyzing a huge amount of content and trying to figure out how to organize and categorize and and do all sorts of things, and it's it's doing really well. The ability to take notes, record what it's done, and just kind of start over and create its own context has been incredibly helpful with turning a workflow that works really well for one or two articles into something that works well across thousands. So thank you for your time. I really look forward to seeing what everyone does with Directus and Directus MCP going forward, especially as Directus MCP evolves, AI models get more capable, and just the overall core gets stronger.\u003C\u002Fp>\u003Cp>The future is gonna be fun.\u003C\u002Fp>","Today, I would like to show you how to supercharge AI batch processing using Directus MCP with a self learning note taking workflow. Batch processing is really effective for, for situations where you need raw speed, but it kinda breaks down when you need to make creative decisions on individual items and on the batch as a whole. You can't really do that without a human element. AI models, I found, are great for those sorts of tasks, but they break down when you give them a whole bunch of data. And they also struggle with finding the right information to pull in to make those creative decisions. So the solution I found is to teach AI to take notes. By implementing a workflow that lets Directus MCP take notes directly on whatever it's working on, we gain the ability to do things like long running task tracking because the model can pick up where it left off and and, start fresh with a new context window. And we also get things like self improving database access. You take a note on all the different things that you ran into difficulty with and what you did to solve them and how it worked out. And it speeds up future runs because it can read that note and go, okay. So don't do this. Do this, and I'll be able to just continue with the task I was working on. And then by letting it take notes, we can consolidate a whole bunch of data that was stored in the database. Say, Say, for example, you wanted to pull out all of the aliases for your articles that are related to rabbits and you also want the ID for each one. Well, you can just dump that in a note and analyze it in future runs. So in our case, Directus is the knowledge backbone. Every note is just a key value pair. I'll go ahead and show you the notes table here. It's a very simple setup. We have a key, which is just a string, and this is what the AI model uses to kind of categorize what the different notes are about. And we have the value, and that's just a big markdown, field. It's a very simple setup, but it's surprisingly powerful. By using semantic keys and explicit instructions on how to record and review notes, we give the AI models a very quick and effective way to find the context they need. So this database has a problem. We have a whole bunch of articles. We have a 124 articles. The titles don't really make any sense. Oh, the bodies are in Latin. And I I can't work with this. So I've created a prompt for Claude to read the article titles and come up with a concept proposal for each one. What could this article actually be about? So let's look at the system prompt here. You can see that there's two things that it's informing the AI model about. That there's an AI notes table in the rough structure of that table and that it needs to read and record its database insights, especially how it solved problems it ran into and just update that note on every run. This gives it the self learning ability we were talking about earlier. It's able to figure out what it ran into last time and fix it on the next run. Now back to the task. I've told the AI model it's a skilled content analyst and writer. We have a huge collection of articles. All the bodies are full of nonsense, all that. Alright. So its job is to record the concepts that it comes up with in an article concepts note. It's not sure what to do with an article based on the title. Be creative. Find a wacky concept. Then we have some very specific instructions. We say always read the template and template article concepts, this is a note, to determine how to structure your output. Always create a new article concepts note for each run. Always give it the exact name. Always process 30 articles. Always review the last note you created before starting to make sure you don't duplicate any work. This template lets us make sure that we get consistent output on every run. Alright. Let's go ahead and run this a few times and see what its output looks like. I turn on auto refresh, and then I'm gonna go into Claude, add a prompt from Directus, and examine articles and propose concepts. If you want to know how to use this feature, go ahead and take a look at the Directus MCP documentation, and then we're just gonna send it. Now one of the fun things you can do with read and write access direct to Directus is if the model makes a mistake, you can ask it to update the prompt to fix its mistake in future runs. Article concepts. It has identified directly that just about every article appears to be lorem ipsum gibberish and occasional test content. Alright. So came up with a bunch of different articles on a bunch of different ideas. In a bit, we're gonna use this to actually write all these different article concepts. But for now, I just wanna keep going through the batch process to show you how it's able to pick up where it left off and continue. On this run, if you notice, it actually picked up that it needed to look up the translations and didn't have to figure that out from the schema this time. Alright. It is a new day. My cloud usage limits have reset, and we are ready to continue with step two, which is generating the articles based on the concepts that we've put together. All of the, article concepts have now been saved in these, concept notes. They take the article title. They try to figure out what on earth, the article should be about and propose a concept. So now we have the next step of the process, which is to generate a actual article for each of the articles we generate concepts on in both English and German. And we're gonna go in batches of 10 and see how well that works. We'll take this prompt and it should automatically read over all of our article concept notes and start filling in articles for those. Let's go. So we're going to use the turn concepts into full fledged articles prompt. That's going to go and read through all of our article concept notes and start filling in those articles, and I'll show you those articles as it writes them. I have no idea if these articles are going to make any sense. This is going to be fun. I'm having fun reading through these articles. I have no idea how helpful any of this is, but it at least sounds very convincing. Now, the key advantage to this approach is that if we were generating concepts and writing articles at the same time, we'd have to use much smaller batches. But because we split the process into two steps, we've saved a ton of context window, and we're able to work in larger batches. And as a bonus, we can do multiple things with those article summaries, while we're working on generating those articles from those summaries. Now, this isn't the most efficient workflow for generating articles. There's way better workflows for that. We're just demonstrating this concept. But a nice thing about this approach is that you can perform multiple tasks at the same time. For example, while we were working on these articles, we could also be working on something completely different using the notes that we're generating the articles from. I have this prompt here, categorize article concepts, which will allow Claude to suggest an article taxonomy based on the summaries it's already generated. It'll just read all the article concept notes and create a new note suggesting that new taxonomy. I like the way it describes these articles as a fascinating collection of technical and business focused article concepts with creative jargon filled titles that have been transformed into practical valuable content ideas. I'm not quite sure I'd be so positive about it, but it gets the idea across. Alright. Let's take a look at the categories that it generated and the opportunities that identified. So here's some content gaps, AI and ethics, sustainable technology operations, human centered digital transformation, all sorts of other different things. And here are some of the categories that's come up with. System architecture, it's put a bunch of different articles in that category, Ecommerce, digital business, data management and analytics, user experience and interface design, business operations or business optimization management, automation and AI systems, project management and collaboration. So it's basically taken all of those articles that we put together and categorized them according to common themes in those articles. This could be really useful if you have an existing content collection that you're trying to build a new categorization system for. And what's great is you could just take this and create another prompt to actually apply that taxonomy to articles, and create categories and all sorts of different things for that in Directus. So by the end of this run, here's what the AI model has built: concept proposals for every article in our database, full body content for many of those articles in English and German, a whole new content categorization system for the articles it wrote, progress logs so that it doesn't lose its place, and database access hints that documents how the challenges were solved so that on future runs, it could do even better. Instead of treating AI like a disposable worker or one that can infinitely stuff and stuff and stuff and work on something until it gets all confused, we're treating it like a teammate that tracks and follows up on what it's done across multiple days, multiple runs, starting with a clean slate on each task and only pulling in the information that it needs. We turned a human in the loop batch processing task into an AI creative workflow by splitting it into discrete steps and letting it kind of pick up where it left off. We gave the model a way to track progress. We built templates to make the results more consistent and reusable, and we recorded information about the database to help the AI model get smarter over time. I've had a ton of fun nailing down this workflow and testing it on all sorts of tasks. For my work, we're actually using it to do things similar to this where we're analyzing a huge amount of content and trying to figure out how to organize and categorize and and do all sorts of things, and it's it's doing really well. The ability to take notes, record what it's done, and just kind of start over and create its own context has been incredibly helpful with turning a workflow that works really well for one or two articles into something that works well across thousands. So thank you for your time. I really look forward to seeing what everyone does with Directus and Directus MCP going forward, especially as Directus MCP evolves, AI models get more capable, and just the overall core gets stronger. The future is gonna be fun.","61b97ecd-ad99-4e38-9799-fd3fbfe95e44",[],[],{"year":399,"number":400,"id":401,"episodes":535,"show":536},[403,404,405,386,406,407,408,409,410],{"title":412,"slug":413},{"id":379,"slug":538,"vimeo_id":539,"description":540,"tile":541,"length":12,"resources":12,"people":12,"episode_number":159,"published":365,"title":542,"video_transcript_html":543,"video_transcript_text":544,"content":545,"seo":546,"status":19,"episode_people":547,"recommendations":548,"season":549},"mcp-claude-ai","1129654788","Connect the native Directus MCP Server to Claude AI Desktop for streamlined content management conversations. This episode guides you through the setup process to bring Directus data and operations directly into your Claude Desktop chat experience.","b0aff196-aeb4-4807-9283-4a279ca81114","Connect to Claude AI \u002F Desktop","\u003Cp>Speaker 0: Alright. Alright. Brian here for Directus. In this video, we are going to show you how to connect the Directus native MCP to Claude AI. Alright.\u003C\u002Fp>\u003Cp>So we're gonna start our journey inside the Directus instance. We're gonna go to the AI settings, and we're gonna make sure that our MCP server is enabled. You could set that up to allow deletes, which will, allow the MCP tools to delete files, which is helpful. You're also gonna wanna make sure that you've got an AI prompts collection set up. This allows you to build reusable workflows using these templated prompts that you can control inside your Directus instance.\u003C\u002Fp>\u003Cp>The next step of the process, we're just gonna go into the user directory, and we are going to create a new user. This is the recommended way to interact with AI just to keep things nice and secure. So we'll create a new user for Claude AI. For now, I'm gonna give them a administrator access, but before I go to production, I would definitely scope that back. The next step will generate a static access token for it to use with the Directus API, and then we'll hit save.\u003C\u002Fp>\u003Cp>So with that out of the way, maybe we dive into one of our posts, and then we come into Claude and set up our custom connector. So MCP, it's called connectors under the Claude settings. We're just gonna go in and add a new custom connector, we'll call it Directus CMS, and I'm going to copy and paste my Directus URL. We're gonna go to slash m c p, and then I'm gonna add query params to this, just access underscore token, and I'm gonna make sure I put an equal sign, and then I'm gonna paste that token that we generated after this. So the full directus URL slash m c p question mark access underscore token equals your directus static access token.\u003C\u002Fp>\u003Cp>You hit add, and this will then connect to that MCP. So you can hit configure here to see all the different tools. You can turn these on or off. Alright. So let's hop in and open up a new chat, and then we'll just ask it something simple.\u003C\u002Fp>\u003Cp>What does our Directus schema look like inside our instance? Alright. So those keywords should be picked up by Claude, and then it should start calling the tools inside our direct us MCP. Okay. It has ran our system prompt.\u003C\u002Fp>\u003Cp>Now it's going to call our schema tool, which should fetch all the different collections inside Directus, and now it is going to summarize that for us. We can see what it returned here. And, you know, maybe we want to add a new field to our post collection for an AI generated summary. Right? Let's add a new field to our blog post collection for an AI summary.\u003C\u002Fp>\u003Cp>Alright. And this should call the fields tool inside Directus. It looks like it's going to fetch more information about the post collection first, which is good, and then it will ask to create a new field inside the post collection. Once that's done, all I do is hit refresh over here on the left side, and now we can see this new field inside our Directus instance. This is just barely scratching the surface of the capabilities inside the Directus MCP and partnering up with something like Claude.\u003C\u002Fp>\u003Cp>Stay tuned for more videos in this series.\u003C\u002Fp>","Alright. Alright. Brian here for Directus. In this video, we are going to show you how to connect the Directus native MCP to Claude AI. Alright. So we're gonna start our journey inside the Directus instance. We're gonna go to the AI settings, and we're gonna make sure that our MCP server is enabled. You could set that up to allow deletes, which will, allow the MCP tools to delete files, which is helpful. You're also gonna wanna make sure that you've got an AI prompts collection set up. This allows you to build reusable workflows using these templated prompts that you can control inside your Directus instance. The next step of the process, we're just gonna go into the user directory, and we are going to create a new user. This is the recommended way to interact with AI just to keep things nice and secure. So we'll create a new user for Claude AI. For now, I'm gonna give them a administrator access, but before I go to production, I would definitely scope that back. The next step will generate a static access token for it to use with the Directus API, and then we'll hit save. So with that out of the way, maybe we dive into one of our posts, and then we come into Claude and set up our custom connector. So MCP, it's called connectors under the Claude settings. We're just gonna go in and add a new custom connector, we'll call it Directus CMS, and I'm going to copy and paste my Directus URL. We're gonna go to slash m c p, and then I'm gonna add query params to this, just access underscore token, and I'm gonna make sure I put an equal sign, and then I'm gonna paste that token that we generated after this. So the full directus URL slash m c p question mark access underscore token equals your directus static access token. You hit add, and this will then connect to that MCP. So you can hit configure here to see all the different tools. You can turn these on or off. Alright. So let's hop in and open up a new chat, and then we'll just ask it something simple. What does our Directus schema look like inside our instance? Alright. So those keywords should be picked up by Claude, and then it should start calling the tools inside our direct us MCP. Okay. It has ran our system prompt. Now it's going to call our schema tool, which should fetch all the different collections inside Directus, and now it is going to summarize that for us. We can see what it returned here. And, you know, maybe we want to add a new field to our post collection for an AI generated summary. Right? Let's add a new field to our blog post collection for an AI summary. Alright. And this should call the fields tool inside Directus. It looks like it's going to fetch more information about the post collection first, which is good, and then it will ask to create a new field inside the post collection. Once that's done, all I do is hit refresh over here on the left side, and now we can see this new field inside our Directus instance. This is just barely scratching the surface of the capabilities inside the Directus MCP and partnering up with something like Claude. Stay tuned for more videos in this series.","\u003Cp>In this episode, we show you how to connect the native Directus MCP Server to Claude AI Desktop. You'll learn how to:\u003C\u002Fp>\u003Cul>\u003Cli>Enable the MCP server in your Directus AI settings\u003C\u002Fli>\u003Cli>Configure the allow deletes option for file management\u003C\u002Fli>\u003Cli>Set up the AI prompts collection for reusable workflows\u003C\u002Fli>\u003Cli>Create a dedicated Claude AI user with appropriate permissions\u003C\u002Fli>\u003Cli>Generate a static access token for secure API access\u003C\u002Fli>\u003Cli>Add a custom connector in Claude Desktop settings\u003C\u002Fli>\u003Cli>Configure the connection URL with your Directus instance and access token\u003C\u002Fli>\u003Cli>Test the connection by querying your schema\u003C\u002Fli>\u003Cli>Add new fields to collections through natural conversation\u003C\u002Fli>\u003C\u002Ful>\u003Cp>Watch as we demonstrate the complete setup process, from configuring Directus to adding an AI summary field to a blog post collection. We show how Claude can fetch your schema, understand your collections, and make changes to your Directus instance through conversational AI. This integration brings powerful content management capabilities directly into your Claude Desktop experience.\u003C\u002Fp>","f00ef473-3a0a-4a02-a3dc-d53aeecd12d8",[],[],{"year":374,"number":176,"id":375,"episodes":550,"show":551},[377,378,360,379,380,381],{"title":383,"slug":384},{"id":410,"slug":553,"vimeo_id":554,"description":555,"tile":556,"length":177,"resources":12,"people":12,"episode_number":527,"published":365,"title":557,"video_transcript_html":558,"video_transcript_text":559,"content":12,"seo":560,"status":19,"episode_people":561,"recommendations":563,"season":564},"mcp-hackathon-challenges","1138984257","Bryant talks through the MCP Hackathon, giving details on the challenges, prizes and entry information.","e4f205b0-a2f5-41f3-9847-202fc5de6289","MCP Hackathon: Challenges, Prizes, and How to Enter","\u003Cp>Speaker 0: A few examples of what you can do with the Directus MCP. Now is your chance to actually build something with it, and we're gonna make it worth your while. So I'm here with the official announcement. We are running a Directus MCP hackathon for the next few weeks, and man, it's gonna be loads of fun. There are two challenges, and yes, you can enter both.\u003C\u002Fp>\u003Cp>Challenge number one is to build a cool Directus project using our native MCP. It's completely open ended. No rules, no limits. You can build a tool that solves a real problem or build something freaking wild. To enter, just provide a short overview and a quick five minute demo video showing us and the community how awesome it is.\u003C\u002Fp>\u003Cp>Challenge number two is to create a community template. You'll build a ready to go direct to starter that other developers can fire up right away. Think an AI powered blog or maybe a smart product catalog. Something that is helpful for the community and showcases what's possible when you build with the Directus MCP. All you need is a public GitHub repo with the template included.\u003C\u002Fp>\u003Cp>And, look, don't worry. We've got some tooling and a sample repo to make this process easy peasy for you. Winners in each challenge will get a cool thousand bucks USD, and even cooler than that is that every team member gets an eight bit dough retro mechanical keyboard. Yes, those are the ones that look like the old Nintendo, and yes, I will be jealous of you. Winners also get some sweet Directus swag.\u003C\u002Fp>\u003Cp>So get yourself registered at directus.i0\u002Fhackathons. Registering also gets you into our mentor hours sessions where you can ask questions and get help from other members of the core team and myself. Again, check out the official rules and submission details at directus.i0\u002Fhackathons. If you've been looking for an excuse to build something with AI and Directus, now's the time. I'm super excited to see what you build.\u003C\u002Fp>","A few examples of what you can do with the Directus MCP. Now is your chance to actually build something with it, and we're gonna make it worth your while. So I'm here with the official announcement. We are running a Directus MCP hackathon for the next few weeks, and man, it's gonna be loads of fun. There are two challenges, and yes, you can enter both. Challenge number one is to build a cool Directus project using our native MCP. It's completely open ended. No rules, no limits. You can build a tool that solves a real problem or build something freaking wild. To enter, just provide a short overview and a quick five minute demo video showing us and the community how awesome it is. Challenge number two is to create a community template. You'll build a ready to go direct to starter that other developers can fire up right away. Think an AI powered blog or maybe a smart product catalog. Something that is helpful for the community and showcases what's possible when you build with the Directus MCP. All you need is a public GitHub repo with the template included. And, look, don't worry. We've got some tooling and a sample repo to make this process easy peasy for you. Winners in each challenge will get a cool thousand bucks USD, and even cooler than that is that every team member gets an eight bit dough retro mechanical keyboard. Yes, those are the ones that look like the old Nintendo, and yes, I will be jealous of you. Winners also get some sweet Directus swag. So get yourself registered at directus.i0\u002Fhackathons. Registering also gets you into our mentor hours sessions where you can ask questions and get help from other members of the core team and myself. Again, check out the official rules and submission details at directus.i0\u002Fhackathons. If you've been looking for an excuse to build something with AI and Directus, now's the time. I'm super excited to see what you build.","4152664e-dc82-49fb-a1a2-54ca097c5c39",[562],"ee3c6dd7-e0a8-453e-99ae-ebc7fc869805",[],{"year":399,"number":400,"id":401,"episodes":565,"show":566},[403,404,405,386,406,407,408,409,410],{"title":412,"slug":413},{"id":381,"slug":568,"vimeo_id":569,"description":570,"tile":571,"length":329,"resources":12,"people":12,"episode_number":195,"published":365,"title":572,"video_transcript_html":573,"video_transcript_text":574,"content":575,"seo":576,"status":19,"episode_people":577,"recommendations":579,"season":580},"mcp-for-marketers","1130344643","Explore powerful content workflows for marketers using the native Directus MCP Server with ChatGPT and Claude. This episode demonstrates real-world marketing use cases including importing formatted content from Google Docs, creating landing pages with AI, automating translations, and generating images.","bfcdd8a5-e5dd-48c0-8105-fc8de6f2ed33","Directus MCP for Marketers","\u003Cp>Speaker 0: Alright. Alright. Alright. Welcome, marketing folks. This is the video for you.\u003C\u002Fp>\n\u003Cp>I'm Brian from Directus. In this video, we're gonna cover content workflows with the Directus native MCP and ChatGPT and Claude. So this is gonna be exciting. We're gonna step through a couple different content workflows that you're dealing with on your day to day basis and give you a couple ideas for how you can improve that. Right?\u003C\u002Fp>\n\u003Cp>First one on the list, developers versus content teams. No. We've got content that we have inside a Google Doc. This has been written by a guest author. It has specific formatting, And anytime you've tried to copy and paste this inside your CMS, you know what a challenge it is.\u003C\u002Fp>\n\u003Cp>Alright. So let's tackle it. Right? We're going to go inside our CMS, and I'm just gonna first create a new user for our AI. This is just best practice as far as security goes.\u003C\u002Fp>\n\u003Cp>We don't need to do anything else other than scroll down. We're gonna give them a team member role and we're gonna generate what we call a static access token. That's really all we need to do here. We're gonna hit save, and now we've got our AI user. Make sure that you, copy paste that token somewhere because we're gonna reuse it in a moment.\u003C\u002Fp>\n\u003Cp>For this, let's go to ChatGPT. We're gonna go to our apps and connectors, and make sure you go to the advanced settings. Now you're gonna have to be a plus member of ChatGPT or better. Make sure you enable developer mode and we're just gonna create a new connector. Sounds scary, it is not.\u003C\u002Fp>\n\u003Cp>We're just gonna call this Directus CMS and what we're gonna do here, we're gonna copy our Directus base URL, I'm gonna paste it here, I'm gonna do a big slash, m c p, question mark, access, underscore, token, and then we're going to take that, static access token that we generated and just stick it at the end of that URL. Now we get a big scary warning here. The nice part about the Directus MCP is we have guardrails in place, so anything that you can't do via that user's permissions, the LLM is not allowed to either, and that's an important part of the Directus MCP. So we hit create. This should create our custom connector and connect to the direct assistance and sometimes that takes a little longer in chat g p t.\u003C\u002Fp>\n\u003Cp>Cool. Alright. So now we can see that. We've got access. It is connected.\u003C\u002Fp>\n\u003Cp>We can see all the different tools. Cool. Alright. So next up on the list, right, we've got our blog post here and we want to get it here. Cool.\u003C\u002Fp>\n\u003Cp>But, now, one of the things that guest writers often forget is we have a lot of extra fields like SEO, meta titles, and descriptions. We've got content. But again, if I copy paste this, it's not gonna work great. So we're just gonna add this from Google Drive, find our article, here it is, Devs versus Marketers, and we'll say please add this to our Directus blog posts collection with all the proper fields for our schema. Cool.\u003C\u002Fp>\n\u003Cp>Alright. With that, we'll hit go. And what this should do is call the Directus MCP and start working through this document. Alright. So it's reading the different documents.\u003C\u002Fp>\n\u003Cp>We could see the train of thought over here. Just my own personal experience with chat GPT version five is, it's a little bit slower. We can, you know, crank that up if we want to, but this should call the direct to schema, and then it will hopefully, pick up our post structure as well so it knows what fields that we need to send. It's doing a bit of thinking here, and we could turn that down if we want to. But there we go.\u003C\u002Fp>\n\u003Cp>We could see it's picking up the post schema from the direct instance. And after that, it should start formatting this into, an article for our post. Alright? Need to create a post using the uploaded docx file. I'll extract the title.\u003C\u002Fp>\n\u003Cp>There's a slug. Set the publish state to in review as null. Perhaps add an AI generated summary. Okay. So now we can see here that it is going to call a tool to create this post force.\u003C\u002Fp>\n\u003Cp>Let's take a look at the results. So if we hit refresh, it doesn't look like it's quite done yet. What do we get here? Are we done? Successfully created the item.\u003C\u002Fp>\n\u003Cp>All I have to do is hit refresh over here, and now we can see our article. Right? It's populated the title, populated the slug. We've got all of our formatting and AI generated summary. And if I open up the code here, there is no crazy formatting.\u003C\u002Fp>\n\u003Cp>These are just standard HTML tags, instead of the Google Docs nightmare. Right? So that's all done. Amazing. Right?\u003C\u002Fp>\n\u003Cp>Now let's take a look at how to go further with this. Right? Alright. So now let's switch over to Claude and just show that workflow as well of how to connect if you're using that. I find that Claude's sonnet is usually a better writer than, ChatGPT in in my own experience.\u003C\u002Fp>\n\u003Cp>Now they they got a little better with GPT five, but SONNET remains, great at writing. So we're gonna go into the settings. We're gonna hit add custom connector. It's a very similar setup. We'll just call it direct to CMS.\u003C\u002Fp>\n\u003Cp>We're going to copy and paste our URL, again, slash m c p question mark access underscore token, and then we're gonna paste that token that we generated, which I believe is this one, I think. And then we'll hit add. That should connect, and we'll know it's connected here because then we can click configure. Now with Claude, we've got some stuff that we don't have in ChatGPT, and that's why I wanted to show this to you as well. You've got the ability to load prompts from Directus.\u003C\u002Fp>\n\u003Cp>So inside your Directus instance, if you're using our CMS starter, you already have this baked in or, you know, it you could create this collection really easily. You can use these templated prompts essentially to build powerful workflows. You know, this one is a dad joke. We've got a system prompt here, etcetera. But, you know, we've got samples in here like humanize the content or create a landing page, which is what we're going to do now.\u003C\u002Fp>\n\u003Cp>Right? So I wanna create a landing page for the Directus MCP. We'll get super meta here. And what I'm gonna do is click add and hit landing page, and here is a series of prompts. Right?\u003C\u002Fp>\n\u003Cp>So the Directus MCP server is the product we're promoting. We want, we want folks to try it with Claude and chat GPT. Reduces busy work. This is gonna be marketers as our audience and, clicking around a bunch and wasting three hours of your life to create a blog post. Cool.\u003C\u002Fp>\n\u003Cp>Is this too technical? Maybe. Alright. So we're gonna add our prompt here. Failed to get the prompt.\u003C\u002Fp>\n\u003Cp>Why did you fail to get the prompt? Test. There we go. Okay. Sometimes that happens.\u003C\u002Fp>\n\u003Cp>Alright. Create a landing page for the Directus MCP. Cool. So what should happen next? Right?\u003C\u002Fp>\n\u003Cp>This should call a series of the Directus MCP tools. First is gonna get the system prompt, and I could choose to allow once or always allow. The system prompt is pretty harmless. It just basically has some instructions for the LLM. Then it's going to fetch our direct to schema so it knows how the landing pages look, and it's going to fetch the available blocks.\u003C\u002Fp>\n\u003Cp>So as a marketer, you don't want to hit up the development team every time you wanna launch a page. Directus CMS is beautiful in that regard because you can build pages, really detailed pages from, just dynamic blocks here. So you could see I've got a hero. I've got rich text. We've got some blog posts.\u003C\u002Fp>\n\u003Cp>We've got pricing plans. And then we have our form. Right? So each one of these blocks has its own setup, different style of fields. Here's all our different images, etcetera.\u003C\u002Fp>\n\u003Cp>But it can be time consuming to scaffold out some of those pages. Now you could duplicate pages or, we can, in this case, have our LLM friends create some of this content for us. So now you can see it's actually going through and building out the landing page for us. So we've got a block hero. Here's the copy for that.\u003C\u002Fp>\n\u003Cp>It is creating the buttons for the hero. Now it's updating the hero with the button group. Now it's gonna create the problem solution section. And, you know, if we take a look at the prompt that we had here, basically, I'm telling it that, hey. You're Harry Drey, the world class copywriter.\u003C\u002Fp>\n\u003Cp>You are amazing at generating landing pages that actually convert. You examine the direct to schema to see what blocks are available. So basically, all this is just templated prompt so that you know, you're gonna get predictable or somewhat predictable results out of this thing if you use it over and over again. And you can create those prompts here inside Directus. Whatever works for you, save them, share them with your team.\u003C\u002Fp>\n\u003Cp>It's definitely a very nice workflow. So it's going through and creating all the different sections for us, and then once it's done, it should come back and put them all together. Now I'm going to just fast forward for your sake through this process, and then we'll come back. And we're back. Okay.\u003C\u002Fp>\n\u003Cp>So it has completed this landing page for us. I'm a little scared to open it. Obviously, these things are never scripted here, but we could see our landing page that it created for us. If I want to take a look at the live preview there, great. Stop clicking around for three hours, start creating content in minutes.\u003C\u002Fp>\n\u003Cp>And and as far as the design of this starter page here, it's a starter. We keep it relatively simple. So there's nothing crazy going on as far as the design, but we've got a ton of copy here that we could then potentially go in and easily update inside Directus, either, you know, through the visual editor or, you know, right inside the form. We've got a lot of different options there. But now let's take a look at one of the other use cases that I wanna cover in this video.\u003C\u002Fp>\n\u003Cp>Alright. So let's go back to our blog post that we created through ChatGPT, this developers versus content teams. I'm gonna start a new chat over in Claude's sonnet, and we're just gonna have it actually run some translations for us. If you're based in Europe or even in The US, content needs to be localized into each language, which can be a challenge. Now Directus has a great interface for this where I could see my different translations side by side and compare those, but the act of actually translating the copy takes a while.\u003C\u002Fp>\n\u003Cp>Newsflash, LLMs are incredibly good at translations because of all the the data they've been trained on. So let's put that to use. Right? We have this article inside our directus CMS, I'd like you to generate all the translations for that post in all the different languages we support in the languages collection. Alright.\u003C\u002Fp>\n\u003Cp>So let's just see if the LLM could get this right. You know, I intentionally misled it a bit because the collection inside Directus is called post. Right? It is not called articles, so, you know, this is my attempt to confuse it a bit and see what results we get. Right?\u003C\u002Fp>\n\u003Cp>So it's gonna call the system prompt. Amazing. That's our expected behavior. It should then fetch the schema, and that's going to return a list of all the collections. We could see those there.\u003C\u002Fp>\n\u003Cp>And then it's going to be fetching the posts and the languages collection. Alright. So now I could see the post has a translations field. Let me find the specific article. Okay.\u003C\u002Fp>\n\u003Cp>So it looks like it's located the article. It's gonna actually check our translation schema to see what fields that we need. And now it is going to check on the actual relationship. Direct us relations. Okay.\u003C\u002Fp>\n\u003Cp>I think this is because I've activated the skills, which is a relatively new cloud feature inside my accounts. Don't have access to do this. Okay. So here, we have hit a snag. Right?\u003C\u002Fp>\n\u003Cp>Let's just pause. This looks like it's a permissions issue because this post translation is a relatively new thing. Look. LLMs are not perfect. This is not a rehearsed demo, so sometimes you're gonna run into interesting bits.\u003C\u002Fp>\n\u003Cp>Right? So let's go in and for a team member inside here, we'll just add some access to create, read, update post translations. That should solve that issue. And now I'm just gonna start a new chat. You know, one thing that I've found, sometimes it's better just to start a new chat when things get squirrelly instead of trying to actually, course correct.\u003C\u002Fp>\n\u003Cp>These LLMs, they pick up steam. It's kinda like gravity, a ball going downhill. Alright. So we're gonna go through the same progression here. It should check on the schema.\u003C\u002Fp>\n\u003Cp>It's gonna find the languages. Now it's actually picking up on the post translations. We'll get all the available languages. Cool. See you have eight languages.\u003C\u002Fp>\n\u003Cp>Let me check if there are any existing translations. So it's going through this in a a smart manner. Now it's going to generate all the translations for the non English languages. Excellent. Alright.\u003C\u002Fp>\n\u003Cp>So it it understands the schema. It knows what each of our translations, look like behind the scenes, and it should do all of this work for me. Looking nice. Is it going to make all of these in a single call? Let's kinda monitor what it's going to do.\u003C\u002Fp>\n\u003Cp>Great. K. We could see it's still translating this. So, again, in order to save you guys some time, I'll just hit fast forward. The last time was about sixty seconds.\u003C\u002Fp>\n\u003Cp>We'll see how long it takes here. Alright. And we're back. So Claude has told us it's successfully created all the different translations for our article. So let's just pop back in here.\u003C\u002Fp>\n\u003Cp>We'll go to the translations tab, and let's do this side by side. So we could see the Arabic translation, if we wanna look at French versus the Italian. I am terrible at both of these languages, so I couldn't tell you if these are accurate or not. But I can tell you that it is amazing not to have to do all of this work manually myself. So we could see those side by side.\u003C\u002Fp>\n\u003Cp>You could close that out if you want to. Just drop that there. We could filter through these and see which translations are actually completed versus not. It has done all those fields for us, which is great. And that is it for translating content.\u003C\u002Fp>\n\u003Cp>Right? I wanna show you just one more thing before we go. Directus has built in automations. These things are called flows. Flows are just simple or complex low code, no code automations that, the MCP can create for you or your developers can create to enable you.\u003C\u002Fp>\n\u003Cp>Think sending email notifications or pulling data from other systems into Directus. Now you can call these flows, at least the manual once, through the MCP, but also you can run those through the Directus UI. So the last piece of the puzzle, right, we're missing a beautiful image for this. Let's just pop open our flow. We've got a generate image flow and, create a kind of developers versus, generate an image of a developer fighting a marketer inside a boxing ring, ultra realistic, four k, high def, whatever.\u003C\u002Fp>\n\u003Cp>Right? If I could actually spell high def. Alright. So we'll do this in, what, sixteen nine, run this flow. We'll get a notification that this is and image generation has kicked off.\u003C\u002Fp>\n\u003Cp>And in just a moment, there we go, we see our image to use. Looking great. And then we could just go back in and quickly add this to our blog post. We'll look for all files. There we go.\u003C\u002Fp>\n\u003Cp>Let's set a publish date on this. We'll make that for today. We've got, the author, the webmaster, and then we can see this image in its full glory. Well, the image is not loading, but, why is that? It's probably because it needs to be moved to the public folder to be visible.\u003C\u002Fp>\n\u003Cp>Again, no perfect demos, but this is, you know, this is a great workflow, I believe. Alright. So there we go. We can see our blog post. I can go in.\u003C\u002Fp>\n\u003Cp>And if I do need to change something here, we've got the visual editor inside Directus. Maybe I want to tweak this and use that em dash that everyone is avoiding. Cool. So that is it for this video and content editing workflows in Directus. I hope you, mister marketer, missus marketer, miss marketer, will find a ton of value in the Directus MCP.\u003C\u002Fp>\n\u003Cp>Stay tuned for more videos in this series.\u003C\u002Fp>","Alright. Alright. Alright. Welcome, marketing folks. This is the video for you. I'm Brian from Directus. In this video, we're gonna cover content workflows with the Directus native MCP and ChatGPT and Claude. So this is gonna be exciting. We're gonna step through a couple different content workflows that you're dealing with on your day to day basis and give you a couple ideas for how you can improve that. Right? First one on the list, developers versus content teams. No. We've got content that we have inside a Google Doc. This has been written by a guest author. It has specific formatting, And anytime you've tried to copy and paste this inside your CMS, you know what a challenge it is. Alright. So let's tackle it. Right? We're going to go inside our CMS, and I'm just gonna first create a new user for our AI. This is just best practice as far as security goes. We don't need to do anything else other than scroll down. We're gonna give them a team member role and we're gonna generate what we call a static access token. That's really all we need to do here. We're gonna hit save, and now we've got our AI user. Make sure that you, copy paste that token somewhere because we're gonna reuse it in a moment. For this, let's go to ChatGPT. We're gonna go to our apps and connectors, and make sure you go to the advanced settings. Now you're gonna have to be a plus member of ChatGPT or better. Make sure you enable developer mode and we're just gonna create a new connector. Sounds scary, it is not. We're just gonna call this Directus CMS and what we're gonna do here, we're gonna copy our Directus base URL, I'm gonna paste it here, I'm gonna do a big slash, m c p, question mark, access, underscore, token, and then we're going to take that, static access token that we generated and just stick it at the end of that URL. Now we get a big scary warning here. The nice part about the Directus MCP is we have guardrails in place, so anything that you can't do via that user's permissions, the LLM is not allowed to either, and that's an important part of the Directus MCP. So we hit create. This should create our custom connector and connect to the direct assistance and sometimes that takes a little longer in chat g p t. Cool. Alright. So now we can see that. We've got access. It is connected. We can see all the different tools. Cool. Alright. So next up on the list, right, we've got our blog post here and we want to get it here. Cool. But, now, one of the things that guest writers often forget is we have a lot of extra fields like SEO, meta titles, and descriptions. We've got content. But again, if I copy paste this, it's not gonna work great. So we're just gonna add this from Google Drive, find our article, here it is, Devs versus Marketers, and we'll say please add this to our Directus blog posts collection with all the proper fields for our schema. Cool. Alright. With that, we'll hit go. And what this should do is call the Directus MCP and start working through this document. Alright. So it's reading the different documents. We could see the train of thought over here. Just my own personal experience with chat GPT version five is, it's a little bit slower. We can, you know, crank that up if we want to, but this should call the direct to schema, and then it will hopefully, pick up our post structure as well so it knows what fields that we need to send. It's doing a bit of thinking here, and we could turn that down if we want to. But there we go. We could see it's picking up the post schema from the direct instance. And after that, it should start formatting this into, an article for our post. Alright? Need to create a post using the uploaded docx file. I'll extract the title. There's a slug. Set the publish state to in review as null. Perhaps add an AI generated summary. Okay. So now we can see here that it is going to call a tool to create this post force. Let's take a look at the results. So if we hit refresh, it doesn't look like it's quite done yet. What do we get here? Are we done? Successfully created the item. All I have to do is hit refresh over here, and now we can see our article. Right? It's populated the title, populated the slug. We've got all of our formatting and AI generated summary. And if I open up the code here, there is no crazy formatting. These are just standard HTML tags, instead of the Google Docs nightmare. Right? So that's all done. Amazing. Right? Now let's take a look at how to go further with this. Right? Alright. So now let's switch over to Claude and just show that workflow as well of how to connect if you're using that. I find that Claude's sonnet is usually a better writer than, ChatGPT in in my own experience. Now they they got a little better with GPT five, but SONNET remains, great at writing. So we're gonna go into the settings. We're gonna hit add custom connector. It's a very similar setup. We'll just call it direct to CMS. We're going to copy and paste our URL, again, slash m c p question mark access underscore token, and then we're gonna paste that token that we generated, which I believe is this one, I think. And then we'll hit add. That should connect, and we'll know it's connected here because then we can click configure. Now with Claude, we've got some stuff that we don't have in ChatGPT, and that's why I wanted to show this to you as well. You've got the ability to load prompts from Directus. So inside your Directus instance, if you're using our CMS starter, you already have this baked in or, you know, it you could create this collection really easily. You can use these templated prompts essentially to build powerful workflows. You know, this one is a dad joke. We've got a system prompt here, etcetera. But, you know, we've got samples in here like humanize the content or create a landing page, which is what we're going to do now. Right? So I wanna create a landing page for the Directus MCP. We'll get super meta here. And what I'm gonna do is click add and hit landing page, and here is a series of prompts. Right? So the Directus MCP server is the product we're promoting. We want, we want folks to try it with Claude and chat GPT. Reduces busy work. This is gonna be marketers as our audience and, clicking around a bunch and wasting three hours of your life to create a blog post. Cool. Is this too technical? Maybe. Alright. So we're gonna add our prompt here. Failed to get the prompt. Why did you fail to get the prompt? Test. There we go. Okay. Sometimes that happens. Alright. Create a landing page for the Directus MCP. Cool. So what should happen next? Right? This should call a series of the Directus MCP tools. First is gonna get the system prompt, and I could choose to allow once or always allow. The system prompt is pretty harmless. It just basically has some instructions for the LLM. Then it's going to fetch our direct to schema so it knows how the landing pages look, and it's going to fetch the available blocks. So as a marketer, you don't want to hit up the development team every time you wanna launch a page. Directus CMS is beautiful in that regard because you can build pages, really detailed pages from, just dynamic blocks here. So you could see I've got a hero. I've got rich text. We've got some blog posts. We've got pricing plans. And then we have our form. Right? So each one of these blocks has its own setup, different style of fields. Here's all our different images, etcetera. But it can be time consuming to scaffold out some of those pages. Now you could duplicate pages or, we can, in this case, have our LLM friends create some of this content for us. So now you can see it's actually going through and building out the landing page for us. So we've got a block hero. Here's the copy for that. It is creating the buttons for the hero. Now it's updating the hero with the button group. Now it's gonna create the problem solution section. And, you know, if we take a look at the prompt that we had here, basically, I'm telling it that, hey. You're Harry Drey, the world class copywriter. You are amazing at generating landing pages that actually convert. You examine the direct to schema to see what blocks are available. So basically, all this is just templated prompt so that you know, you're gonna get predictable or somewhat predictable results out of this thing if you use it over and over again. And you can create those prompts here inside Directus. Whatever works for you, save them, share them with your team. It's definitely a very nice workflow. So it's going through and creating all the different sections for us, and then once it's done, it should come back and put them all together. Now I'm going to just fast forward for your sake through this process, and then we'll come back. And we're back. Okay. So it has completed this landing page for us. I'm a little scared to open it. Obviously, these things are never scripted here, but we could see our landing page that it created for us. If I want to take a look at the live preview there, great. Stop clicking around for three hours, start creating content in minutes. And and as far as the design of this starter page here, it's a starter. We keep it relatively simple. So there's nothing crazy going on as far as the design, but we've got a ton of copy here that we could then potentially go in and easily update inside Directus, either, you know, through the visual editor or, you know, right inside the form. We've got a lot of different options there. But now let's take a look at one of the other use cases that I wanna cover in this video. Alright. So let's go back to our blog post that we created through ChatGPT, this developers versus content teams. I'm gonna start a new chat over in Claude's sonnet, and we're just gonna have it actually run some translations for us. If you're based in Europe or even in The US, content needs to be localized into each language, which can be a challenge. Now Directus has a great interface for this where I could see my different translations side by side and compare those, but the act of actually translating the copy takes a while. Newsflash, LLMs are incredibly good at translations because of all the the data they've been trained on. So let's put that to use. Right? We have this article inside our directus CMS, I'd like you to generate all the translations for that post in all the different languages we support in the languages collection. Alright. So let's just see if the LLM could get this right. You know, I intentionally misled it a bit because the collection inside Directus is called post. Right? It is not called articles, so, you know, this is my attempt to confuse it a bit and see what results we get. Right? So it's gonna call the system prompt. Amazing. That's our expected behavior. It should then fetch the schema, and that's going to return a list of all the collections. We could see those there. And then it's going to be fetching the posts and the languages collection. Alright. So now I could see the post has a translations field. Let me find the specific article. Okay. So it looks like it's located the article. It's gonna actually check our translation schema to see what fields that we need. And now it is going to check on the actual relationship. Direct us relations. Okay. I think this is because I've activated the skills, which is a relatively new cloud feature inside my accounts. Don't have access to do this. Okay. So here, we have hit a snag. Right? Let's just pause. This looks like it's a permissions issue because this post translation is a relatively new thing. Look. LLMs are not perfect. This is not a rehearsed demo, so sometimes you're gonna run into interesting bits. Right? So let's go in and for a team member inside here, we'll just add some access to create, read, update post translations. That should solve that issue. And now I'm just gonna start a new chat. You know, one thing that I've found, sometimes it's better just to start a new chat when things get squirrelly instead of trying to actually, course correct. These LLMs, they pick up steam. It's kinda like gravity, a ball going downhill. Alright. So we're gonna go through the same progression here. It should check on the schema. It's gonna find the languages. Now it's actually picking up on the post translations. We'll get all the available languages. Cool. See you have eight languages. Let me check if there are any existing translations. So it's going through this in a a smart manner. Now it's going to generate all the translations for the non English languages. Excellent. Alright. So it it understands the schema. It knows what each of our translations, look like behind the scenes, and it should do all of this work for me. Looking nice. Is it going to make all of these in a single call? Let's kinda monitor what it's going to do. Great. K. We could see it's still translating this. So, again, in order to save you guys some time, I'll just hit fast forward. The last time was about sixty seconds. We'll see how long it takes here. Alright. And we're back. So Claude has told us it's successfully created all the different translations for our article. So let's just pop back in here. We'll go to the translations tab, and let's do this side by side. So we could see the Arabic translation, if we wanna look at French versus the Italian. I am terrible at both of these languages, so I couldn't tell you if these are accurate or not. But I can tell you that it is amazing not to have to do all of this work manually myself. So we could see those side by side. You could close that out if you want to. Just drop that there. We could filter through these and see which translations are actually completed versus not. It has done all those fields for us, which is great. And that is it for translating content. Right? I wanna show you just one more thing before we go. Directus has built in automations. These things are called flows. Flows are just simple or complex low code, no code automations that, the MCP can create for you or your developers can create to enable you. Think sending email notifications or pulling data from other systems into Directus. Now you can call these flows, at least the manual once, through the MCP, but also you can run those through the Directus UI. So the last piece of the puzzle, right, we're missing a beautiful image for this. Let's just pop open our flow. We've got a generate image flow and, create a kind of developers versus, generate an image of a developer fighting a marketer inside a boxing ring, ultra realistic, four k, high def, whatever. Right? If I could actually spell high def. Alright. So we'll do this in, what, sixteen nine, run this flow. We'll get a notification that this is and image generation has kicked off. And in just a moment, there we go, we see our image to use. Looking great. And then we could just go back in and quickly add this to our blog post. We'll look for all files. There we go. Let's set a publish date on this. We'll make that for today. We've got, the author, the webmaster, and then we can see this image in its full glory. Well, the image is not loading, but, why is that? It's probably because it needs to be moved to the public folder to be visible. Again, no perfect demos, but this is, you know, this is a great workflow, I believe. Alright. So there we go. We can see our blog post. I can go in. And if I do need to change something here, we've got the visual editor inside Directus. Maybe I want to tweak this and use that em dash that everyone is avoiding. Cool. So that is it for this video and content editing workflows in Directus. I hope you, mister marketer, missus marketer, miss marketer, will find a ton of value in the Directus MCP. Stay tuned for more videos in this series.","\u003Cp>In this episode, we walk through essential content workflows for marketers using the native Directus MCP Server with ChatGPT and Claude. You'll learn how to:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Set up secure MCP connections for both ChatGPT and Claude Desktop\u003C\u002Fli>\n\u003Cli>Import guest articles from Google Docs with proper formatting and SEO fields\u003C\u002Fli>\n\u003Cli>Use custom prompts from Directus for repeatable workflows\u003C\u002Fli>\n\u003Cli>Create complete landing pages with dynamic blocks through conversation\u003C\u002Fli>\n\u003Cli>Generate multilingual translations automatically across all supported languages\u003C\u002Fli>\n\u003Cli>Trigger Directus Flows for image generation and other automations\u003C\u002Fli>\n\u003Cli>Work with the visual editor to refine AI-generated content\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Watch as we demonstrate real marketing scenarios from start to finish. We cover importing a guest blog post from Google Docs and automatically populating all metadata fields, creating a full landing page for the Directus MCP using templated prompts, translating an entire article into eight languages, and generating hero images using automated Directus Flows. This episode showcases practical patterns for content teams to reduce busy work and focus on strategy and creativity. Perfect for marketers and content creators who want to streamline their content operations without constantly relying on developers.\u003C\u002Fp>","8b7b0c05-87fc-4d6f-a4a5-dc7eb4bf716c",[578],"81920482-b404-4edf-9214-8cd967972137",[],{"year":374,"number":176,"id":375,"episodes":581,"show":582},[377,378,360,379,380,381],{"title":383,"slug":384},{"id":380,"slug":584,"vimeo_id":585,"description":586,"tile":587,"length":588,"resources":12,"people":12,"episode_number":213,"published":365,"title":589,"video_transcript_html":590,"video_transcript_text":591,"content":592,"seo":593,"status":19,"episode_people":594,"recommendations":596,"season":597},"mcp-for-developers","1130318289","Discover powerful developer workflows using the native Directus MCP Server with Claude Code. This episode demonstrates practical use cases including rapid data modeling, automated flow creation, sample data generation, and script writing to accelerate your development process.","f6b21e46-3a64-4047-a53f-a4bd36090dc7",23,"Directus MCP For Developers","\u003Cp>Speaker 0: Alright. Alright. Welcome, devs. Brian here for Directus. And in this video, we are going to show you some developer workflows when working with the Directus native MCP and your IDE.\u003C\u002Fp>\u003Cp>So no matter where you sit on the Vibe coding spectrum, love it, hate it, don't like it, cautiously optimistic. Hopefully, we're gonna get a few workflows out of here that you can try and take away for your own use. Let's dive in. First stop is just gonna be into the AI settings page of Directus to make sure that the MCP is enabled for this instance. We're gonna allow deletes, which is okay.\u003C\u002Fp>\u003Cp>In our development instance, you may want to disable that in production. We've got our AI prompts collection, which is helpful for all the marketers in your life. They get reusable prompt templates they can use with things like Claude or other clients that support it, and we can change the system prompt behavior. With that enabled, what we're going to do next is just go in and create a new user here. So I'm gonna name this Claude Code, you're very right to be security concerned when it comes to LLM and prompt injection and all those bad things that we hear about, and that's why we advocate creating separate users for the MCP, so you could tightly scope those permission settings.\u003C\u002Fp>\u003Cp>And, in this case, we're gonna give it admin access because I wanna show you guys some of the schema tools. But do not do this in production. Make sure you scope it down either by the access policy or by the role itself, and make sure you give it the least permissions possible for the task. Right? Alright, so we're going to create a static access token here, we're going to save this user, and now I'm just going to pull this up.\u003C\u002Fp>\u003Cp>I've got a little snippet somewhere on my clipboard. I'm going to grab that token that we just created. So basically, this is the command for adding cloud code, adding the MCP server to that. I'm just gonna open up another terminal here inside cursor. I've been jamming a lot with cloud code lately, especially as part of developing this MCP.\u003C\u002Fp>\u003Cp>So, that's what we'll cover today. Of course, there's other videos on how to connect it to Cursor or other clients. This is just one workflow that we're gonna show you. Alright. So our first workflow that we're gonna cover, right, is conceptual data models.\u003C\u002Fp>\u003Cp>Right. Maybe I've got a new project and, you know, a a Directus makes it super easy to data model anything, really. I could go through and visually map out whatever, and then getting the instant APIs on top of that is just icing on the cake. But in this case, I I wanna move faster. Right?\u003C\u002Fp>\u003Cp>So let's open up Cloud Code, and let's just talk to it, add a a con a new data model to Directus. Let's create a new data model for a CRM inside my directus instance. Keep it lightweight for now. Just standard CRM stuff. Let's brainstorm together first before you add it though.\u003C\u002Fp>\u003Cp>Alright. Cool. So we will take a look at what happens. Hopefully, this should be a signal to the MCP server that it should go in and run a couple tools and just see what we have inside Directus First, but we'll see how Claude Code reacts. Right?\u003C\u002Fp>\u003Cp>It is doing some thinking. We can toggle that with command o and see kind of what we what we got. Organizations, contacts, deals, activities, tasks. Okay. Questions.\u003C\u002Fp>\u003Cp>Cool. Let me close this out. Let's skip the extra collections. This looks pretty good for now. Activities, let's use m two o fields versus the m two a.\u003C\u002Fp>\u003Cp>Deal pipeline stages, we want those to be a separate collection. Should be a separate collection. Nothing industry specific for now. Alright. So, you know, I appreciate the back and forth here.\u003C\u002Fp>\u003Cp>You know, hopefully, now that we've stressed got this planned out, let's just hit continue with the plan and see what Claude can do via the MCP. Alright. We can see it doing some thinking. And and, again, like, I could do this via the UI. We're just gonna see how much quicker it is to do it via the MCP.\u003C\u002Fp>\u003Cp>It's going to ask us to call the system prompt. Basically, this just gives some more information about the role. Cool. It looks like the next one it is trying to call is the schema tool, which is good. We'll give it full access.\u003C\u002Fp>\u003Cp>That way, it can see the existing schema inside our direct assistance. And I guess I could make this full screen. There we go. Zoom in even further. And I could probably even close the terminal down here.\u003C\u002Fp>\u003Cp>Alright. So we could see it's trying to run the collections command. Cool. This should create the collection inside Directus for us. And, again, I'm just gonna give it full access to continue.\u003C\u002Fp>\u003Cp>You know, again, be very careful when you're using these tools. Make sure you're inspecting all of the payloads first. You know, especially when it's touching the schema, you might want to export the schema. There is an API endpoint for that. Check the docs at Directus to to verify.\u003C\u002Fp>\u003Cp>Alright. Now it's gonna go in and create the different relationships for us, and it looks like it's hit an error. Right? Now the MCP inside Directus, there's two validation layers. Like, we first validate, you know, a a light validation against the input schema.\u003C\u002Fp>\u003Cp>And then before we actually perform the operation inside Directus, like creating a field or a relationship, goes through a a much more strict validation, which helps catch some of these errors like this. And you could see it successfully created these relationships right now. Alright. So if I do a hard refresh over here, we close this, we should start to see some of these things get populated, like our contacts and our organizations, the pipeline stages. Right?\u003C\u002Fp>\u003Cp>We're still probably waiting on the deals, I would assume. Right. But now I can start entering the data for this. Right? Bryant Gillespie.\u003C\u002Fp>\u003Cp>Probably shouldn't put my full email address in here. And I could create a new organization, call that direct us. Great. There we go. And we could see that it it has already got all these relationships set up for us as well, which is is super nice.\u003C\u002Fp>\u003Cp>Right? We've got deals, we've got contacts, we can see the title of the deal, we've got a slider for the probability of this thing closing, we've got a link to our stage, let's call this new deal, give it a color, title, of course title is required so it's already added some validation for us. New deal, Brian's deal, Brian's new deal, sounds like a presidential thing. Alright. So that is, just one use case for the MCP.\u003C\u002Fp>\u003Cp>Right? And you can see this thing is still going to town. It's adding activities, etcetera. In this case, I'm I'm just gonna go ahead and stop this, And let's start a new thread and look at another potential use case for this. Right?\u003C\u002Fp>\u003Cp>So one of the other nice pieces of functionality that is in this iteration of the MCP is the ability to create direct as flows. And you're probably asking me, hey. What are direct as flows? Right? They are low code, no code automations that you can build logic in whenever things happen.\u003C\u002Fp>\u003Cp>You can make requests. You can send emails. Right? As a developer, how many times have you had to put in code for a simple email notification when a certain event happens? Directus as it flows are your best friend in that scenario.\u003C\u002Fp>\u003Cp>We don't have to submit a PR, wait for three days for somebody to review that change, and the email copy, we could just quickly model it inside flows. But a lot of folks in the community expressed interest in being able to create flows with LLMs. So let's do that. Right? Whenever let's plan a new direct us flow.\u003C\u002Fp>\u003Cp>Whenever a contact is created, Send a notification in app to the owner of that contact. Let's plan the flow first and then create it. We confirm. Okay. Alright.\u003C\u002Fp>\u003Cp>So, basically, we take a a super simple prompt, and I I could be more specific here to get better results. But using the thinking mode inside Cloud Code, the the latest Sonnet model seems to work really well. And this is kind of the the workflow that I've adopted for myself. Let's get a detailed plan. Let's pick up all the information that we need first, and then, then we send, it off on its task.\u003C\u002Fp>\u003Cp>Right? So here's the flow plan. We've got a new contact notification. If the owner is null or assigned, let's add a check for no owner. Use a condition to filter that.\u003C\u002Fp>\u003Cp>We don't want to send a notification if there's no owner. Alright. For the message text message text, just use the contact details. Execute the plan. Again, LLMs aren't particularly worried by typos, in some context maybe, but it should go through and then it will actually start building the flow for us.\u003C\u002Fp>\u003Cp>So there's two components to flows. We have the flow itself and then the operations within the flow, and it should build both of those forming and then actually combine those together, and make sure everything is linked up properly. So we can see this happening here in real time. Directus flow, creating the operation. Not sure how long this will take.\u003C\u002Fp>\u003Cp>And it looks like we're done. Right? A more complex flow would probably take a little bit longer for the LLM to map out, but let's just go in. I'm gonna refresh. We're gonna take a look.\u003C\u002Fp>\u003Cp>Here's our new flow that it has created. Whenever an item is created in the contacts collection, we're gonna check the payload for the owner, and then we're going to update the user. We'll send them a notification. I do spot one issue here. Right?\u003C\u002Fp>\u003Cp>The trigger dot payload dot ID is, not what we're gonna get. It's actually gonna be a trigger dot key. So, you know, this thing is not infallible, but for thirty five seconds to have this flow is pretty good. Could save me a lot of time. Right?\u003C\u002Fp>\u003Cp>Scaffolding this out, and then I'll just dial it in. But let's test this out. We're just gonna send a new contact into the database. I'm gonna make sure that I set myself as the user and boom, in the notifications there we could see new contact has been assigned. I could go in and navigate directly to that contact.\u003C\u002Fp>\u003Cp>Alright. Let's move on to the next workflow. So one of the other workflows that I find myself using quite a bit is just adding sample data, just small sets of sample data. MCP is a rather expensive protocol, so you you don't wanna use this to seed a database. But, you know, if we're developing a, a front end, like a a website, right, Typically, you might want to have a kitchen sink page.\u003C\u002Fp>\u003Cp>You know, if we take a look at our our block based builder, right, it can be very time consuming to scaffold out a full page with all the different options so that when you are building this, right, you can visually see what's happening. Right? MCP makes that super easy. So this is another workflow. Let's go in with let's create a kitchen sink page inside Directus with all the different block combinations so we can better work on the styling and front end components.\u003C\u002Fp>\u003Cp>Alright. So that's the prop we're gonna kick things off with. And it's for whatever reason, this thing is still showing what we previously had. But let's see what happens. Right?\u003C\u002Fp>\u003Cp>It's gonna run the direct to schema. It will get all the existing collections that we have. And if I wanna just pull this up side by side in my browser, you know, here's kind of what we've got as far as the home page, what that kinda looks like, But let's have it create this kitchen sink page. So we're just gonna go back and okay. So it looks like it has picked up all the different block types.\u003C\u002Fp>\u003Cp>We can see that here. Let's use the schema tool to understand what all of those different options are. And now it should go in and start creating this page for us. Right? Has it created the page?\u003C\u002Fp>\u003Cp>It looks like it's gonna go through and create the individual blocks first. So if I show my hidden collections inside Directus, we can go through and see probably some of these different things that it's creating for us. Block hero. Right. This is a block hero.\u003C\u002Fp>\u003Cp>Looks like it's doing a bunch of those. Just, block hero with the image positioned on the right side of the content. Cool. So it's gonna go through this process. I'll just speed this up for the sake of the video, and then we'll come back.\u003C\u002Fp>\u003Cp>And scene. Now we're back. One of the things that I do have toggled when I'm working with Cloud Code, I I use the thinking mode quite a bit. It seems to help the output a lot. But now we've got our kitchen sink page.\u003C\u002Fp>\u003Cp>All we have to do is hit refresh. We could see that it's added quite a few different blocks for us here, which is nice. Looks like it missed one of the form blocks. You missed the form block in our kitchen sink page. Alright.\u003C\u002Fp>\u003Cp>So while we're doing that, let's just open this up. We can see the live preview here, which is probably not working. Maybe we just set this to publish for now. It'll be a a different issue for a different video. But let's open up this kitchen sink page, local host 3,000 kitchen sink, and see what we got.\u003C\u002Fp>\u003Cp>Alright. This doesn't appear to be working. Let me just go back quickly. I'm gonna add a publish date to this. Again, we'll sort out the versioning later.\u003C\u002Fp>\u003Cp>Okay. So now I could see I've got our kitchen sink page. We've got a bunch of different hero layouts. And one of the things that I noticed right away is, you know, if we've got more than two or three buttons, this kinda this design fails. Right?\u003C\u002Fp>\u003Cp>It it goes off the the reservation, and this is kinda helpful for fixing that. Right? We may go in, let's just shrink this up a bit, and then we look at our button group. Cool. There's our button group.\u003C\u002Fp>\u003Cp>We've got our container classes. Let's just add flex wrap here. And now we can see it's super nice to have these kitchen sink kind of components, but one of the great use cases here is just populating the sample data. Right? Is it our CRM?\u003C\u002Fp>\u003Cp>We could have it populate a few sample records for us, or we could even have it generate a seed script for us, which puts us back into the next workflow for this. Alright. So one of the other workflows that is super nice with the MCP and your LLM is having it write scripts for you. This could be migrating from one CMS to another, in this case, Directus. That's a pattern that that our team has used locally with tools like Cloud Code or Cursor.\u003C\u002Fp>\u003Cp>You know, there's a lot of things that need to be migrated. And MCP is is good at understanding and, you know, the context around the data, especially when you've given it the schema, but having an LLM move all the data one by one by brick by brick, token by token, is super inefficient. So, let's just have it write a script to load up a bunch of blog posts for us. Let's write a node script to use the Falso library is one that I like to generate a bunch of fake contacts and companies inside our directus instance. Alright.\u003C\u002Fp>\u003Cp>So now what I'm gonna do, I'm just gonna copy the URL for Falso here or, gonna do that. Cool. And down here in the actual terminal, I'm just gonna go in and go ahead and install this library. Just get a little jump start. Cool.\u003C\u002Fp>\u003Cp>Alright. So it looks like it is reading the package JSON. We should see that. It's gonna pick up our different collections. And what else?\u003C\u002Fp>\u003Cp>Contacts and organizations is reading the schema for that, which is great. And we'll see if we can get it to output this script for us. Again, I this is a a more efficient way. It's more repeatable. So if you do need, like, highly deterministic output, this is, a one pattern that's worked really well for us.\u003C\u002Fp>\u003Cp>Right? It looks like it's also doing a e n v example. Check if the direct Us SDK is installed. Cool. There's the seed data script.\u003C\u002Fp>\u003Cp>We're probably gonna need to install the Directus SDK in the root as well. Looks like it's already taken care of that for us. Cool. We'll allow it to make those changes. We'll go ahead and run the installation script, And then we are going to need to do what?\u003C\u002Fp>\u003Cp>Then we'll need to do the e and v variables. Right? PM PMI install. Let's see is what it's got for us as far as the seed data. Alright.\u003C\u002Fp>\u003Cp>So different industries. We got the number of organizations, three organizations per contact. Okay. So it's actually picking this up from the directus.e n v. So we shouldn't really need to do anything other than just actually run this.\u003C\u002Fp>\u003Cp>Right? Nodeseeddata.js. Created orgs is not iterable. Where is that coming from? Again, if you're gonna go straight vibe code on this, make sure that you are doing everything properly.\u003C\u002Fp>\u003Cp>Let's fix the script. Yes. Alright. Looks like it is trying to make sure we're we're actually adding authentication, which is a good thing. Created orgs is not an iterable.\u003C\u002Fp>\u003Cp>Okay. Just waiting on you, Claude. Just waiting on you. It is not getting the what what are we doing here? Try running it again.\u003C\u002Fp>\u003Cp>There we go. Okay. So it said it has exceeded this successfully. We're gonna take a look, and boom. There's our seed data.\u003C\u002Fp>\u003Cp>Right? So that was stupid quick, stupid easy to go in and add this data. And this is based on actual utility. Right? There's not a ton of crazy hype here.\u003C\u002Fp>\u003Cp>These are actually just useful workflows in your day to day as you're building with Directus. You make sure you look to the community for more workflows. That is it for this video. Stay tuned for more in the MCP series.\u003C\u002Fp>","Alright. Alright. Welcome, devs. Brian here for Directus. And in this video, we are going to show you some developer workflows when working with the Directus native MCP and your IDE. So no matter where you sit on the Vibe coding spectrum, love it, hate it, don't like it, cautiously optimistic. Hopefully, we're gonna get a few workflows out of here that you can try and take away for your own use. Let's dive in. First stop is just gonna be into the AI settings page of Directus to make sure that the MCP is enabled for this instance. We're gonna allow deletes, which is okay. In our development instance, you may want to disable that in production. We've got our AI prompts collection, which is helpful for all the marketers in your life. They get reusable prompt templates they can use with things like Claude or other clients that support it, and we can change the system prompt behavior. With that enabled, what we're going to do next is just go in and create a new user here. So I'm gonna name this Claude Code, you're very right to be security concerned when it comes to LLM and prompt injection and all those bad things that we hear about, and that's why we advocate creating separate users for the MCP, so you could tightly scope those permission settings. And, in this case, we're gonna give it admin access because I wanna show you guys some of the schema tools. But do not do this in production. Make sure you scope it down either by the access policy or by the role itself, and make sure you give it the least permissions possible for the task. Right? Alright, so we're going to create a static access token here, we're going to save this user, and now I'm just going to pull this up. I've got a little snippet somewhere on my clipboard. I'm going to grab that token that we just created. So basically, this is the command for adding cloud code, adding the MCP server to that. I'm just gonna open up another terminal here inside cursor. I've been jamming a lot with cloud code lately, especially as part of developing this MCP. So, that's what we'll cover today. Of course, there's other videos on how to connect it to Cursor or other clients. This is just one workflow that we're gonna show you. Alright. So our first workflow that we're gonna cover, right, is conceptual data models. Right. Maybe I've got a new project and, you know, a a Directus makes it super easy to data model anything, really. I could go through and visually map out whatever, and then getting the instant APIs on top of that is just icing on the cake. But in this case, I I wanna move faster. Right? So let's open up Cloud Code, and let's just talk to it, add a a con a new data model to Directus. Let's create a new data model for a CRM inside my directus instance. Keep it lightweight for now. Just standard CRM stuff. Let's brainstorm together first before you add it though. Alright. Cool. So we will take a look at what happens. Hopefully, this should be a signal to the MCP server that it should go in and run a couple tools and just see what we have inside Directus First, but we'll see how Claude Code reacts. Right? It is doing some thinking. We can toggle that with command o and see kind of what we what we got. Organizations, contacts, deals, activities, tasks. Okay. Questions. Cool. Let me close this out. Let's skip the extra collections. This looks pretty good for now. Activities, let's use m two o fields versus the m two a. Deal pipeline stages, we want those to be a separate collection. Should be a separate collection. Nothing industry specific for now. Alright. So, you know, I appreciate the back and forth here. You know, hopefully, now that we've stressed got this planned out, let's just hit continue with the plan and see what Claude can do via the MCP. Alright. We can see it doing some thinking. And and, again, like, I could do this via the UI. We're just gonna see how much quicker it is to do it via the MCP. It's going to ask us to call the system prompt. Basically, this just gives some more information about the role. Cool. It looks like the next one it is trying to call is the schema tool, which is good. We'll give it full access. That way, it can see the existing schema inside our direct assistance. And I guess I could make this full screen. There we go. Zoom in even further. And I could probably even close the terminal down here. Alright. So we could see it's trying to run the collections command. Cool. This should create the collection inside Directus for us. And, again, I'm just gonna give it full access to continue. You know, again, be very careful when you're using these tools. Make sure you're inspecting all of the payloads first. You know, especially when it's touching the schema, you might want to export the schema. There is an API endpoint for that. Check the docs at Directus to to verify. Alright. Now it's gonna go in and create the different relationships for us, and it looks like it's hit an error. Right? Now the MCP inside Directus, there's two validation layers. Like, we first validate, you know, a a light validation against the input schema. And then before we actually perform the operation inside Directus, like creating a field or a relationship, goes through a a much more strict validation, which helps catch some of these errors like this. And you could see it successfully created these relationships right now. Alright. So if I do a hard refresh over here, we close this, we should start to see some of these things get populated, like our contacts and our organizations, the pipeline stages. Right? We're still probably waiting on the deals, I would assume. Right. But now I can start entering the data for this. Right? Bryant Gillespie. Probably shouldn't put my full email address in here. And I could create a new organization, call that direct us. Great. There we go. And we could see that it it has already got all these relationships set up for us as well, which is is super nice. Right? We've got deals, we've got contacts, we can see the title of the deal, we've got a slider for the probability of this thing closing, we've got a link to our stage, let's call this new deal, give it a color, title, of course title is required so it's already added some validation for us. New deal, Brian's deal, Brian's new deal, sounds like a presidential thing. Alright. So that is, just one use case for the MCP. Right? And you can see this thing is still going to town. It's adding activities, etcetera. In this case, I'm I'm just gonna go ahead and stop this, And let's start a new thread and look at another potential use case for this. Right? So one of the other nice pieces of functionality that is in this iteration of the MCP is the ability to create direct as flows. And you're probably asking me, hey. What are direct as flows? Right? They are low code, no code automations that you can build logic in whenever things happen. You can make requests. You can send emails. Right? As a developer, how many times have you had to put in code for a simple email notification when a certain event happens? Directus as it flows are your best friend in that scenario. We don't have to submit a PR, wait for three days for somebody to review that change, and the email copy, we could just quickly model it inside flows. But a lot of folks in the community expressed interest in being able to create flows with LLMs. So let's do that. Right? Whenever let's plan a new direct us flow. Whenever a contact is created, Send a notification in app to the owner of that contact. Let's plan the flow first and then create it. We confirm. Okay. Alright. So, basically, we take a a super simple prompt, and I I could be more specific here to get better results. But using the thinking mode inside Cloud Code, the the latest Sonnet model seems to work really well. And this is kind of the the workflow that I've adopted for myself. Let's get a detailed plan. Let's pick up all the information that we need first, and then, then we send, it off on its task. Right? So here's the flow plan. We've got a new contact notification. If the owner is null or assigned, let's add a check for no owner. Use a condition to filter that. We don't want to send a notification if there's no owner. Alright. For the message text message text, just use the contact details. Execute the plan. Again, LLMs aren't particularly worried by typos, in some context maybe, but it should go through and then it will actually start building the flow for us. So there's two components to flows. We have the flow itself and then the operations within the flow, and it should build both of those forming and then actually combine those together, and make sure everything is linked up properly. So we can see this happening here in real time. Directus flow, creating the operation. Not sure how long this will take. And it looks like we're done. Right? A more complex flow would probably take a little bit longer for the LLM to map out, but let's just go in. I'm gonna refresh. We're gonna take a look. Here's our new flow that it has created. Whenever an item is created in the contacts collection, we're gonna check the payload for the owner, and then we're going to update the user. We'll send them a notification. I do spot one issue here. Right? The trigger dot payload dot ID is, not what we're gonna get. It's actually gonna be a trigger dot key. So, you know, this thing is not infallible, but for thirty five seconds to have this flow is pretty good. Could save me a lot of time. Right? Scaffolding this out, and then I'll just dial it in. But let's test this out. We're just gonna send a new contact into the database. I'm gonna make sure that I set myself as the user and boom, in the notifications there we could see new contact has been assigned. I could go in and navigate directly to that contact. Alright. Let's move on to the next workflow. So one of the other workflows that I find myself using quite a bit is just adding sample data, just small sets of sample data. MCP is a rather expensive protocol, so you you don't wanna use this to seed a database. But, you know, if we're developing a, a front end, like a a website, right, Typically, you might want to have a kitchen sink page. You know, if we take a look at our our block based builder, right, it can be very time consuming to scaffold out a full page with all the different options so that when you are building this, right, you can visually see what's happening. Right? MCP makes that super easy. So this is another workflow. Let's go in with let's create a kitchen sink page inside Directus with all the different block combinations so we can better work on the styling and front end components. Alright. So that's the prop we're gonna kick things off with. And it's for whatever reason, this thing is still showing what we previously had. But let's see what happens. Right? It's gonna run the direct to schema. It will get all the existing collections that we have. And if I wanna just pull this up side by side in my browser, you know, here's kind of what we've got as far as the home page, what that kinda looks like, But let's have it create this kitchen sink page. So we're just gonna go back and okay. So it looks like it has picked up all the different block types. We can see that here. Let's use the schema tool to understand what all of those different options are. And now it should go in and start creating this page for us. Right? Has it created the page? It looks like it's gonna go through and create the individual blocks first. So if I show my hidden collections inside Directus, we can go through and see probably some of these different things that it's creating for us. Block hero. Right. This is a block hero. Looks like it's doing a bunch of those. Just, block hero with the image positioned on the right side of the content. Cool. So it's gonna go through this process. I'll just speed this up for the sake of the video, and then we'll come back. And scene. Now we're back. One of the things that I do have toggled when I'm working with Cloud Code, I I use the thinking mode quite a bit. It seems to help the output a lot. But now we've got our kitchen sink page. All we have to do is hit refresh. We could see that it's added quite a few different blocks for us here, which is nice. Looks like it missed one of the form blocks. You missed the form block in our kitchen sink page. Alright. So while we're doing that, let's just open this up. We can see the live preview here, which is probably not working. Maybe we just set this to publish for now. It'll be a a different issue for a different video. But let's open up this kitchen sink page, local host 3,000 kitchen sink, and see what we got. Alright. This doesn't appear to be working. Let me just go back quickly. I'm gonna add a publish date to this. Again, we'll sort out the versioning later. Okay. So now I could see I've got our kitchen sink page. We've got a bunch of different hero layouts. And one of the things that I noticed right away is, you know, if we've got more than two or three buttons, this kinda this design fails. Right? It it goes off the the reservation, and this is kinda helpful for fixing that. Right? We may go in, let's just shrink this up a bit, and then we look at our button group. Cool. There's our button group. We've got our container classes. Let's just add flex wrap here. And now we can see it's super nice to have these kitchen sink kind of components, but one of the great use cases here is just populating the sample data. Right? Is it our CRM? We could have it populate a few sample records for us, or we could even have it generate a seed script for us, which puts us back into the next workflow for this. Alright. So one of the other workflows that is super nice with the MCP and your LLM is having it write scripts for you. This could be migrating from one CMS to another, in this case, Directus. That's a pattern that that our team has used locally with tools like Cloud Code or Cursor. You know, there's a lot of things that need to be migrated. And MCP is is good at understanding and, you know, the context around the data, especially when you've given it the schema, but having an LLM move all the data one by one by brick by brick, token by token, is super inefficient. So, let's just have it write a script to load up a bunch of blog posts for us. Let's write a node script to use the Falso library is one that I like to generate a bunch of fake contacts and companies inside our directus instance. Alright. So now what I'm gonna do, I'm just gonna copy the URL for Falso here or, gonna do that. Cool. And down here in the actual terminal, I'm just gonna go in and go ahead and install this library. Just get a little jump start. Cool. Alright. So it looks like it is reading the package JSON. We should see that. It's gonna pick up our different collections. And what else? Contacts and organizations is reading the schema for that, which is great. And we'll see if we can get it to output this script for us. Again, I this is a a more efficient way. It's more repeatable. So if you do need, like, highly deterministic output, this is, a one pattern that's worked really well for us. Right? It looks like it's also doing a e n v example. Check if the direct Us SDK is installed. Cool. There's the seed data script. We're probably gonna need to install the Directus SDK in the root as well. Looks like it's already taken care of that for us. Cool. We'll allow it to make those changes. We'll go ahead and run the installation script, And then we are going to need to do what? Then we'll need to do the e and v variables. Right? PM PMI install. Let's see is what it's got for us as far as the seed data. Alright. So different industries. We got the number of organizations, three organizations per contact. Okay. So it's actually picking this up from the directus.e n v. So we shouldn't really need to do anything other than just actually run this. Right? Nodeseeddata.js. Created orgs is not iterable. Where is that coming from? Again, if you're gonna go straight vibe code on this, make sure that you are doing everything properly. Let's fix the script. Yes. Alright. Looks like it is trying to make sure we're we're actually adding authentication, which is a good thing. Created orgs is not an iterable. Okay. Just waiting on you, Claude. Just waiting on you. It is not getting the what what are we doing here? Try running it again. There we go. Okay. So it said it has exceeded this successfully. We're gonna take a look, and boom. There's our seed data. Right? So that was stupid quick, stupid easy to go in and add this data. And this is based on actual utility. Right? There's not a ton of crazy hype here. These are actually just useful workflows in your day to day as you're building with Directus. You make sure you look to the community for more workflows. That is it for this video. Stay tuned for more in the MCP series.","\u003Cp>In this episode, we explore real-world developer workflows using the native Directus MCP Server with Claude Code. You'll learn how to:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Set up secure MCP access with dedicated users and scoped permissions\u003C\u002Fli>\n\u003Cli>Create complete data models through conversation (demo: building a CRM)\u003C\u002Fli>\n\u003Cli>Generate Directus Flows for automated notifications and workflows\u003C\u002Fli>\n\u003Cli>Build kitchen sink pages with sample content for front-end development\u003C\u002Fli>\n\u003Cli>Write custom seed scripts using the Falso library for realistic test data\u003C\u002Fli>\n\u003Cli>Debug and iterate on LLM-generated code in real-time\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Watch as we demonstrate multiple development scenarios from initial setup through execution. We cover conceptual data modeling for a lightweight CRM, creating automated notification flows, generating comprehensive test pages with all block combinations, and writing efficient seed scripts for bulk data creation. This episode showcases practical patterns for integrating AI into your daily development workflow, whether you love vibe coding or prefer more traditional approaches. Perfect for developers looking to accelerate your Directus projects with AI assistance.\u003C\u002Fp>","bd023627-472e-4f97-94e6-4d89927d16b6",[595],"9fc7c7bf-dd0e-45ac-8f03-a90ab85ef56c",[],{"year":374,"number":176,"id":375,"episodes":598,"show":599},[377,378,360,379,380,381],{"title":383,"slug":384},{"id":378,"slug":601,"vimeo_id":602,"description":603,"tile":604,"length":12,"resources":12,"people":12,"episode_number":177,"published":365,"title":605,"video_transcript_html":606,"video_transcript_text":607,"content":608,"seo":609,"status":19,"episode_people":610,"recommendations":611,"season":612},"mcp-cursor","1129619572","Integrate the native Directus MCP Server with Cursor IDE for enhanced AI-powered development workflows. This episode shows you how to configure the MCP connection in Cursor, enabling seamless content management and database operations while you code.","33f6bfdd-1d11-4976-912d-a56d5b4b10c1","Connect to Cursor","\u003Cp>Speaker 0: What's up, friends? Brian here for Directus. And in this video, we're gonna show you how to connect the Directus native MCP to your cursor IDE. Love all the acronyms. Alright.\u003C\u002Fp>\u003Cp>The first thing we're gonna do is go into the AI settings inside your Directus Instance. Now I've got this Instance running locally. You know, you could connect either way to a local Instance or to a cloud based instance. So we're also going to enable our AI prompts collection, which it comes out of the gate with the CMS template. And cool.\u003C\u002Fp>\u003Cp>We'll make sure we save that. We can allow deletes. So next up on the list, we're gonna go in and I can create a new user here or I could just grab the token for our admin user. Great. We're gonna do that.\u003C\u002Fp>\u003Cp>I'll hit save. Amazing. Okay. Next, what we're going to do is just gonna pop over to the documentation, and we're gonna look for the AI guides. We got a quick setup on the MCP.\u003C\u002Fp>\u003Cp>We'll scroll down. And I do this because we have this one click install to cursor, or I could just copy paste this. We'll just open up cursor, and we can see here's the Directus MCP. I'm just gonna change this to my URL, which in this case is just local host eight zero five five, and I'm gonna paste in that token that we created. Now once I hit install, we can see now we've got six tools.\u003C\u002Fp>\u003Cp>I can go in and enable all the other tools. I've also got three prompts. That's coming from the AI prompts collection inside this Directus instance. Now we're ready to go. Right?\u003C\u002Fp>\u003Cp>Locally here, I've got a Next. Js website running. This is, again, just a template that you could get access to through the cloud or self hosted. But now we're ready to fire up a conversation. Right?\u003C\u002Fp>\u003Cp>So I'm gonna open up the cursor agent. We'll just make this larger. You know, let's use one of the the newer models, ClaudeSonic. And let's start this conversation with the agent. What can you tell me about my Directus schema?\u003C\u002Fp>\u003Cp>So if you're vibe coding anything up, this is a super helpful tool, the schema tool that's within the native MCP. It is going to run our system prompt. You could see it run the schema. We can get all the information about the collections. It's gonna grab some extra information about that, and we could see here that it is summarizing what's going on.\u003C\u002Fp>\u003Cp>Right? Maybe we want to add a new collection or or change a field within an existing collection. Right? Let's take a look at our blog post. Maybe we want an AI summary.\u003C\u002Fp>\u003Cp>Can you add an AI summary field to our blog post? Cool. So now we're working with the MCP. Do we want to allow it to run this field tool? Yeah.\u003C\u002Fp>\u003Cp>We'll enable that. And now we could see that it has added an AI summary field for us, which we could see right there. There's lots more that you can do with the Directus MCP inside cursor, but that's it for this video. Right? Stay tuned for more in this MCP series.\u003C\u002Fp>","What's up, friends? Brian here for Directus. And in this video, we're gonna show you how to connect the Directus native MCP to your cursor IDE. Love all the acronyms. Alright. The first thing we're gonna do is go into the AI settings inside your Directus Instance. Now I've got this Instance running locally. You know, you could connect either way to a local Instance or to a cloud based instance. So we're also going to enable our AI prompts collection, which it comes out of the gate with the CMS template. And cool. We'll make sure we save that. We can allow deletes. So next up on the list, we're gonna go in and I can create a new user here or I could just grab the token for our admin user. Great. We're gonna do that. I'll hit save. Amazing. Okay. Next, what we're going to do is just gonna pop over to the documentation, and we're gonna look for the AI guides. We got a quick setup on the MCP. We'll scroll down. And I do this because we have this one click install to cursor, or I could just copy paste this. We'll just open up cursor, and we can see here's the Directus MCP. I'm just gonna change this to my URL, which in this case is just local host eight zero five five, and I'm gonna paste in that token that we created. Now once I hit install, we can see now we've got six tools. I can go in and enable all the other tools. I've also got three prompts. That's coming from the AI prompts collection inside this Directus instance. Now we're ready to go. Right? Locally here, I've got a Next. Js website running. This is, again, just a template that you could get access to through the cloud or self hosted. But now we're ready to fire up a conversation. Right? So I'm gonna open up the cursor agent. We'll just make this larger. You know, let's use one of the the newer models, ClaudeSonic. And let's start this conversation with the agent. What can you tell me about my Directus schema? So if you're vibe coding anything up, this is a super helpful tool, the schema tool that's within the native MCP. It is going to run our system prompt. You could see it run the schema. We can get all the information about the collections. It's gonna grab some extra information about that, and we could see here that it is summarizing what's going on. Right? Maybe we want to add a new collection or or change a field within an existing collection. Right? Let's take a look at our blog post. Maybe we want an AI summary. Can you add an AI summary field to our blog post? Cool. So now we're working with the MCP. Do we want to allow it to run this field tool? Yeah. We'll enable that. And now we could see that it has added an AI summary field for us, which we could see right there. There's lots more that you can do with the Directus MCP inside cursor, but that's it for this video. Right? Stay tuned for more in this MCP series.","\u003Cp>In this episode, we guide you through connecting the native Directus MCP Server to Cursor IDE. You'll learn how to:\u003C\u002Fp>\u003Cul>\u003Cli>Enable the MCP server and AI prompts collection in Directus\u003C\u002Fli>\u003Cli>Generate a static access token from your Directus user\u003C\u002Fli>\u003Cli>Use the one-click install button from the Directus documentation\u003C\u002Fli>\u003Cli>Configure the MCP connection in Cursor with your instance URL and token\u003C\u002Fli>\u003Cli>Enable all available MCP tools in Cursor\u003C\u002Fli>\u003Cli>Access custom prompts from your Directus AI prompts collection\u003C\u002Fli>\u003Cli>Query your Directus schema using the schema tool\u003C\u002Fli>\u003Cli>Add new fields to collections through AI conversation\u003C\u002Fli>\u003C\u002Ful>\u003Cp>Watch as we demonstrate connecting a local Directus instance to Cursor, then use the Cursor agent with Claude Sonnet to query the schema and add an AI summary field to a blog post collection. This integration brings powerful AI capabilities directly into your development environment for seamless content management while coding.\u003C\u002Fp>","6768d099-6f72-42cd-ba02-024b3c7af62e",[],[],{"year":374,"number":176,"id":375,"episodes":613,"show":614},[377,378,360,379,380,381],{"title":383,"slug":384},{"id":616,"slug":617,"vimeo_id":618,"description":619,"tile":620,"length":195,"resources":12,"people":12,"episode_number":264,"published":621,"title":622,"video_transcript_html":623,"video_transcript_text":624,"content":12,"seo":625,"status":19,"episode_people":626,"recommendations":628,"season":629},"dc17b7f3-af98-40a4-b205-4cda6af48c71","building-fronted-cms-within-directus","1126261892","Join Bryant as he answers a community question \"Is building frontend CMS within Directus possible?\".","a4ebec91-f105-41a5-b68d-46e76eb58354","2025-10-13","Building Frontend CMS within Directus","\u003Cp>Speaker 0: And we're back with another episode of the community hotline. Welcome. Today, we have Wylus, Wylies. Sorry if I get the name wrong. Welcome to the community.\u003C\u002Fp>\u003Cp>Glad to have you, friend. Alright. So our question today, is building a front end CMS within direct as possible? So we'll tackle that in a moment. Let's read the rest of this.\u003C\u002Fp>\u003Cp>Good day, gurus. I'm looking for a powerful tool to support the following. SaaS, hosting multiple tenant within one project, creating pages if possible, but how could I render it within Directus? I saw tutorials, but only using Directus as a back end. How can we make this work potentially?\u003C\u002Fp>\u003Cp>Alright. So first and foremost, right, Directus is set up to be a headless CMS and back end. So that paradigm is very important to understand. WordPress is, has a headless option, but it's ideally a monolithic kind of solution where you've got your CMS, and you've got your front end, and the templating, and all that is mixed together. Whereas, Directus is headless and that's the way it's designed.\u003C\u002Fp>\u003Cp>So you can, send that data to multiple sources. Maybe you've got a a front end, maybe you've got a mobile application, maybe you've got a kiosk or a display that you need to send data to or fetch data, from a back end. Directus is great for that. When it comes to building a front end CMS within Directus, basically, you're talking about the Directus App Studio. Right?\u003C\u002Fp>\u003Cp>I can not only just add my own team members to this, but I could potentially create a multi tenant CMS out of the data studio itself. So, if you take a look at our CMS starter, this is available, you know, through the website. If you go to directives.io, you hit this command line, or the script here. Just pop this into your terminal, select the CMS option, or if you go to our cloud, log in, you spin up a new project inside cloud, just pick the CMS template and you can get access to what I'm gonna show you here today. Right?\u003C\u002Fp>\u003Cp>So this comes out of the box. This is a a CMS. It's exactly what you would expect out of a CMS. We have pages, we have posts, etcetera. Now if I wanted to make this multi tenant, how would I go about it?\u003C\u002Fp>\u003Cp>Well, first, I would create a tenant collection. Maybe that's sites or, properties or whatever we wanna call it. And then for each collection within this, I add a relationship, many to one, back to that site's collection, back to that tenant collection. And then within our access policies, you've got everything you need to basically scope access down. So you can filter based on the site, so that one tenant cannot read the, data from another.\u003C\u002Fp>\u003Cp>Now, there's an specific episode of 100 apps, one hundred hours where we cover this exact thing. So just look for multi site. That mission is season three episode two. You can follow right along with this and see how to set up a multi tenant CMS using Directus. Now to your other question.\u003C\u002Fp>\u003Cp>Right? How can I create pages or render within Directus? Now Directus itself is extremely flexible, and this is where our extensions come into play. And a good example of that, if we just go to directus.io and we look at our sandbox demo, there's a little extra module in here that is not, baked into directus core. This is just an extension that we built to give you a welcome page that does get rendered inside Directus.\u003C\u002Fp>\u003Cp>Now this is coming from a separate Directus instance, but again, all of Directus is modular and extensible. So this is actually a module extension. But if we just go to our docs, we look for our guides, we go to the extensions section, You can learn all about extensions. So you can customize the way that you interact with data inside the forms. Those are interfaces.\u003C\u002Fp>\u003Cp>You can control the way that data is displayed in the different layouts throughout the studio. That is a display. You have layouts themselves, so you can control the listing of these items. You know, they think tables or kanban, views or maps, whatever. We can add panels to the dashboards and kind of the overall level where you've got total control over what gets rendered on the page, you have a module.\u003C\u002Fp>\u003Cp>So if you want flexibility on how things are rendered inside Directus, Maybe you wanna add some extra pages to it or you just want something, a blank canvas where you could totally experiment with whatever you want. Custom modules are what you're looking at. So, with that in mind, I hope that is enough to answer your question. Wireless. Wireless.\u003C\u002Fp>\u003Cp>Sorry. Again, I hate if I get it wrong. But that's it for this episode of Community Hopeline. If you want me to answer one of your questions on the next change log or or on the next community hop line, make sure you hop into the Directus community, community.directus.io, and I'll see you around.\u003C\u002Fp>","And we're back with another episode of the community hotline. Welcome. Today, we have Wylus, Wylies. Sorry if I get the name wrong. Welcome to the community. Glad to have you, friend. Alright. So our question today, is building a front end CMS within direct as possible? So we'll tackle that in a moment. Let's read the rest of this. Good day, gurus. I'm looking for a powerful tool to support the following. SaaS, hosting multiple tenant within one project, creating pages if possible, but how could I render it within Directus? I saw tutorials, but only using Directus as a back end. How can we make this work potentially? Alright. So first and foremost, right, Directus is set up to be a headless CMS and back end. So that paradigm is very important to understand. WordPress is, has a headless option, but it's ideally a monolithic kind of solution where you've got your CMS, and you've got your front end, and the templating, and all that is mixed together. Whereas, Directus is headless and that's the way it's designed. So you can, send that data to multiple sources. Maybe you've got a a front end, maybe you've got a mobile application, maybe you've got a kiosk or a display that you need to send data to or fetch data, from a back end. Directus is great for that. When it comes to building a front end CMS within Directus, basically, you're talking about the Directus App Studio. Right? I can not only just add my own team members to this, but I could potentially create a multi tenant CMS out of the data studio itself. So, if you take a look at our CMS starter, this is available, you know, through the website. If you go to directives.io, you hit this command line, or the script here. Just pop this into your terminal, select the CMS option, or if you go to our cloud, log in, you spin up a new project inside cloud, just pick the CMS template and you can get access to what I'm gonna show you here today. Right? So this comes out of the box. This is a a CMS. It's exactly what you would expect out of a CMS. We have pages, we have posts, etcetera. Now if I wanted to make this multi tenant, how would I go about it? Well, first, I would create a tenant collection. Maybe that's sites or, properties or whatever we wanna call it. And then for each collection within this, I add a relationship, many to one, back to that site's collection, back to that tenant collection. And then within our access policies, you've got everything you need to basically scope access down. So you can filter based on the site, so that one tenant cannot read the, data from another. Now, there's an specific episode of 100 apps, one hundred hours where we cover this exact thing. So just look for multi site. That mission is season three episode two. You can follow right along with this and see how to set up a multi tenant CMS using Directus. Now to your other question. Right? How can I create pages or render within Directus? Now Directus itself is extremely flexible, and this is where our extensions come into play. And a good example of that, if we just go to directus.io and we look at our sandbox demo, there's a little extra module in here that is not, baked into directus core. This is just an extension that we built to give you a welcome page that does get rendered inside Directus. Now this is coming from a separate Directus instance, but again, all of Directus is modular and extensible. So this is actually a module extension. But if we just go to our docs, we look for our guides, we go to the extensions section, You can learn all about extensions. So you can customize the way that you interact with data inside the forms. Those are interfaces. You can control the way that data is displayed in the different layouts throughout the studio. That is a display. You have layouts themselves, so you can control the listing of these items. You know, they think tables or kanban, views or maps, whatever. We can add panels to the dashboards and kind of the overall level where you've got total control over what gets rendered on the page, you have a module. So if you want flexibility on how things are rendered inside Directus, Maybe you wanna add some extra pages to it or you just want something, a blank canvas where you could totally experiment with whatever you want. Custom modules are what you're looking at. So, with that in mind, I hope that is enough to answer your question. Wireless. Wireless. Sorry. Again, I hate if I get it wrong. But that's it for this episode of Community Hopeline. If you want me to answer one of your questions on the next change log or or on the next community hop line, make sure you hop into the Directus community, community.directus.io, and I'll see you around.","5d2ee344-765f-4c79-ba18-539cb6470e5f",[627],"4ddd0967-d465-42f9-8093-dc2f01a5f3c5",[],{"year":399,"number":630,"id":631,"episodes":632,"show":635},19,"8e34d235-6112-474d-8ae9-5506e9322a93",[633,634,616],"77fddac4-c3c3-4752-9ce7-ad0d149f5896","3090013b-39e2-415b-849c-01dd93c7b831",{"title":636,"slug":637},"The Directus Community Hopline","community-hopline",{"id":633,"slug":639,"vimeo_id":640,"description":641,"tile":642,"length":213,"resources":12,"people":12,"episode_number":13,"published":621,"title":643,"video_transcript_html":644,"video_transcript_text":645,"content":12,"seo":646,"status":19,"episode_people":647,"recommendations":649,"season":650},"generate-api-docs-custom-collections","1126260982","Join Bryant as he answers a community question \"is there an automated way to generate API docs for my custom collections inside Directus?\"","e26b41e8-88a7-4d2f-b9a7-237b45c4bad2","Generate API Docs for Custom Collections in Directus","\u003Cp>Speaker 0: Alright. Alright. Welcome back. We are live on the Directus community hotline. I'm your host, Brian Gillespie.\u003C\u002Fp>\n\u003Cp>Today, we are answering a question from community member RHB. RHB, what's up, sir? Glad to have you inside the community. Let's get to your question. Thanks.\u003C\u002Fp>\n\u003Cp>But is there an automated way to generate API docs for my custom collections inside Directus? The open API spec that generates that Directus generates is massive, and 99% of it is system stuff I don't need. I just want clean docs for my own collections like products, customers, services, etcetera. Anyone know of a tool or script that could filter the OpenAPI spec to only include my collections? Or maybe there's a better approach entirely.\u003C\u002Fp>\n\u003Cp>Well, I'm not sure of a tool or a script, but I can give you a better approach. Let's dive into it. So, this is familiar to probably everyone. This is like the swagger spec. This is kind of the out of the box API documentation.\u003C\u002Fp>\n\u003Cp>This is just their pet store demo. But, one of the nice things about Directus, I know I say this a lot in videos, is the ability to generate this open API spec. So I could just come to server slash specs slash o s and get the full open API spec. It's beautiful. Now, it is very comprehensive in that every single API endpoint that the current user has access to will be shown here.\u003C\u002Fp>\n\u003Cp>So we can kinda see which way we're leading here. The easiest way to get a filtered list for your API spec is just to use a user to grab the API spec that has lowered permissions, restricted permissions, permissions just to the collections that we want in the spec. So how can we do that? We're gonna go into Directus settings, and I'm just gonna create an access policy for this. Now technically, again, just use any user that has the correct permissions that you want and generate the open API spec.\u003C\u002Fp>\n\u003Cp>But to make this super clear, we're just gonna generate a new policy called API spec, and I'm gonna add two collections to this. Just pages will give all CRUD access. Not trying to print the page. We'll do posts. Great.\u003C\u002Fp>\n\u003Cp>We'll give all CRUD. We'll take away the shares. I don't typically use those a lot. Okay. So now we'll we've got this policy.\u003C\u002Fp>\n\u003Cp>We're gonna go in and create a user, And I'm just gonna call this one, surprise surprise, API spec user. Great. What do we need to actually pull this off? Right? We don't have to add a role.\u003C\u002Fp>\n\u003Cp>Directus 11 was really nice in that, we got permission or we got support for access policies. So we can, roll up policies up to the role level or I could just add them directly to specific users in this case. So we're just gonna add API spec. I'm gonna generate a token. And now I'm gonna hop over to Swagger and I'm gonna add my directus URL.\u003C\u002Fp>\n\u003Cp>I'm going to go to this endpoint, server slash specs slash o a s, and I'm gonna add a query param for access token. Now this is definitely, do what I say, don't do what I do. Adding the access token as a query param is not safe, doing it just for demonstration purposes here. Make sure, you know, you're passing that as an authorization header, for the bearer token. So I'm just gonna hit explore and server specs OAS.\u003C\u002Fp>\n\u003Cp>Let's try again. Grabbed the token. Got the API spec. Save. Explore.\u003C\u002Fp>\n\u003Cp>Server spec. Forgot an s. Gotta get the s in there. Okay. So now we're going to see, we'll see the authentication endpoints just because those are always available.\u003C\u002Fp>\n\u003Cp>Right? The server health endpoints. Okay. Great. But if I take a look now, I don't see a giant list of endpoints.\u003C\u002Fp>\n\u003Cp>I just see pages and I just see posts. And, again, I can use Swagger to test those out, but this is the best way to filter down that list, without running extra scripts or extra processing on it. So RHB, I hope this is helpful for you, man. The other thing that I wanna show you, if you go into the marketplace, we've got several extensions that could get the job done for you as well potentially. So if you just search rapid in the marketplace, there's an API viewer extension, a rapid docus extension that allows you to view that open API spec without ever leaving the Directus App Studio.\u003C\u002Fp>\n\u003Cp>If you would like your question to be featured on Community Hopeline or you just have Directus questions in general, hop into our community at community.directus.io. Post your question and you may be featured on the next episode. We'll see you.\u003C\u002Fp>","Alright. Alright. Welcome back. We are live on the Directus community hotline. I'm your host, Brian Gillespie. Today, we are answering a question from community member RHB. RHB, what's up, sir? Glad to have you inside the community. Let's get to your question. Thanks. But is there an automated way to generate API docs for my custom collections inside Directus? The open API spec that generates that Directus generates is massive, and 99% of it is system stuff I don't need. I just want clean docs for my own collections like products, customers, services, etcetera. Anyone know of a tool or script that could filter the OpenAPI spec to only include my collections? Or maybe there's a better approach entirely. Well, I'm not sure of a tool or a script, but I can give you a better approach. Let's dive into it. So, this is familiar to probably everyone. This is like the swagger spec. This is kind of the out of the box API documentation. This is just their pet store demo. But, one of the nice things about Directus, I know I say this a lot in videos, is the ability to generate this open API spec. So I could just come to server slash specs slash o s and get the full open API spec. It's beautiful. Now, it is very comprehensive in that every single API endpoint that the current user has access to will be shown here. So we can kinda see which way we're leading here. The easiest way to get a filtered list for your API spec is just to use a user to grab the API spec that has lowered permissions, restricted permissions, permissions just to the collections that we want in the spec. So how can we do that? We're gonna go into Directus settings, and I'm just gonna create an access policy for this. Now technically, again, just use any user that has the correct permissions that you want and generate the open API spec. But to make this super clear, we're just gonna generate a new policy called API spec, and I'm gonna add two collections to this. Just pages will give all CRUD access. Not trying to print the page. We'll do posts. Great. We'll give all CRUD. We'll take away the shares. I don't typically use those a lot. Okay. So now we'll we've got this policy. We're gonna go in and create a user, And I'm just gonna call this one, surprise surprise, API spec user. Great. What do we need to actually pull this off? Right? We don't have to add a role. Directus 11 was really nice in that, we got permission or we got support for access policies. So we can, roll up policies up to the role level or I could just add them directly to specific users in this case. So we're just gonna add API spec. I'm gonna generate a token. And now I'm gonna hop over to Swagger and I'm gonna add my directus URL. I'm going to go to this endpoint, server slash specs slash o a s, and I'm gonna add a query param for access token. Now this is definitely, do what I say, don't do what I do. Adding the access token as a query param is not safe, doing it just for demonstration purposes here. Make sure, you know, you're passing that as an authorization header, for the bearer token. So I'm just gonna hit explore and server specs OAS. Let's try again. Grabbed the token. Got the API spec. Save. Explore. Server spec. Forgot an s. Gotta get the s in there. Okay. So now we're going to see, we'll see the authentication endpoints just because those are always available. Right? The server health endpoints. Okay. Great. But if I take a look now, I don't see a giant list of endpoints. I just see pages and I just see posts. And, again, I can use Swagger to test those out, but this is the best way to filter down that list, without running extra scripts or extra processing on it. So RHB, I hope this is helpful for you, man. The other thing that I wanna show you, if you go into the marketplace, we've got several extensions that could get the job done for you as well potentially. So if you just search rapid in the marketplace, there's an API viewer extension, a rapid docus extension that allows you to view that open API spec without ever leaving the Directus App Studio. If you would like your question to be featured on Community Hopeline or you just have Directus questions in general, hop into our community at community.directus.io. Post your question and you may be featured on the next episode. We'll see you.","d6795cbb-2206-419d-b87c-ade078226051",[648],"161af416-59a8-448e-8a15-ac2db27cd5ae",[],{"year":399,"number":630,"id":631,"episodes":651,"show":652},[633,634,616],{"title":636,"slug":637},{"id":634,"slug":654,"vimeo_id":655,"description":656,"tile":657,"length":247,"resources":12,"people":12,"episode_number":177,"published":621,"title":658,"video_transcript_html":659,"video_transcript_text":660,"content":12,"seo":661,"status":19,"episode_people":662,"recommendations":664,"season":665},"lightspeed-retail-api","1126261338","Join Bryant as he answers a community question \"I'm integrating Directus with the Lightspeed Retail API [...] has anyone successfully implemented a setup like this?\".","81e3ce98-0159-4ba1-a239-9df8d625d240","Lightspeed Retail API","\u003Cp>Speaker 0: And welcome back to the community hotline. I'm your host Brian Gillespie from Directus. Today, we are answering a question from Directus community member, Daryl Morley. Daryl, how are you, sir? Welcome to the community.\u003C\u002Fp>\u003Cp>Happy to have you. I'm gonna answer your question today in detail. Alright. So the question from Daryl. I'm integrating Directus with Lightspeed retail API.\u003C\u002Fp>\u003Cp>Not heard Lightspeed name in a long time. That's awesome though. Would like to use, for example, the category ID from Lightspeed as the primary key in my categories collection within Directus. Here's the flow I'm aiming for. One, a the first step.\u003C\u002Fp>\u003Cp>A user creates a new category from the Directus admin UI. A before create hook fires that's not actually what it's called in directus, but I got you. It creates a category inside Lightspeed via their API. We receive the category ID back from Lightspeed, and it sets that ID as the primary key of the new Directus record. The issue I'm running into is if the ID field is not auto generated, Directus requires you to provide an ID.\u003C\u002Fp>\u003Cp>Yeah. You gotta have that primary key for sure. But I can't know the ID until I call the Lightspeed API inside the hook. Prevents me from using the Lightspeed category ID as a directus primary key. I could potentially store light speed ID in a separate external category ID field, also an option, but then I run into difficulties linking my items to categories as, the directus only sports relationships using the primary key.\u003C\u002Fp>\u003Cp>You could establish relationships on that that other field as well, but I totally understand where you're coming from with this. And Daryl's pleading because anyone successfully implemented a setup like this or found a clean workaround, keeps everything in sync. Alright. So we are going to dive into this, Daryl. We are gonna solve this problem together right now.\u003C\u002Fp>\u003Cp>Alright. So I've got a blank Directus instance. We're gonna go in and let's create a new collection. I use plurals. We're gonna call it categories.\u003C\u002Fp>\u003Cp>Now every collection has to have a primary key. Typically, I just leave this set to ID for simplicity's sake, And usually I go with a generated UUID. We automatically generate that. In this case, where we want that to come from a third party, we are going to use a manually entered string. Now optional fields, I usually add these a 100% of the time.\u003C\u002Fp>\u003Cp>Not gonna do that here. I'm just gonna click finish setup, and what we're gonna do is just hit name. Let's add one more field for the name of this category, and this is our categories. Right? So now the challenge is if I go in and this is a new category, just as you said, if I try to save this using command s or just hitting the save button here, requires a value.\u003C\u002Fp>\u003Cp>Well, that sucks. That's not what I wanna do. So how do we fix this? There's two ways I can solve this. I'm gonna show you flows first, and then we'll talk about hooks.\u003C\u002Fp>\u003Cp>Underneath the hood, they use the same event system. But, one way to do this inside the studio without writing much code or creating a custom extension that you've got to bundle and deploy, you could just do this in a flow. And if the logic is simple enough, it's it's easy to maintain. So we're gonna call this create category is the name of this flow cool in external API. Now I'm not actually gonna use the light speed API here just to keep this high level, but, yeah.\u003C\u002Fp>\u003Cp>You'll you'll figure that part of it out. Got faith in you. Got confidence. Alright. So we can trigger these flows a variety of different ways.\u003C\u002Fp>\u003Cp>The one we're looking for is event hook, and then we're immediately asked the type of event hook. So there's two, obviously, that's on the screen. There's filter, which is, they said we say blocking here because it blocks that event from actually performing. This is the one that we want because we get a chance to it runs before the item gets saved in the database or before the item gets updated. We'll talk about that in a moment.\u003C\u002Fp>\u003Cp>Actions happen after the event is settled, so after the category has been created. In this case, we want to intercept that category as it's being created and before it's saved into the database, and we're gonna alter the payload. So that's what we're doing here. The scope, we just want items create. Right?\u003C\u002Fp>\u003Cp>Updated items should already have an ID, and we don't wanna complicate this. The response body is going to be our altered payload with this. So this is the payload that we're gonna save to the database. Now you can choose between all data or the data of the last operation. In this case, I'm just gonna keep this simple and choose data of the last operation to remove as many potential issues.\u003C\u002Fp>\u003Cp>Alright. So now we've got our trigger. The next thing that we wanna do is call the external API. Now, I could go in and just choose webhook request URL. I'm gonna hit call external API here.\u003C\u002Fp>\u003Cp>This is our step. In this case, it's gonna be a post. And I've just set up, like, a a mock endpoint, from some online free service that when we send this request, it should just send us an ID back. Cool. Alright.\u003C\u002Fp>\u003Cp>And now the last step, and, you know, and probably, like, that stuff you're getting back from Lightspeed, you're gonna wanna transform that payload. There's two ways to do that. And let's just call this payload. Right? I could use the run script operation where I've got the full power of JavaScript.\u003C\u002Fp>\u003Cp>Now you can't import anything from NPM or use any libraries here, but you you've got, you know, the standard JavaScript library available to you here, and you can run, you know, just basic little functions there. Actually, you don't have the standard JavaScript library. This runs in an isolate, but you got all the basic JavaScript stuff that you're gonna need to transform a payload. The other way to do this is via the transform payload operation, which basically just allows you to adjust what is gonna come out of this thing. And you'll see we're using mustache syntax here for a reason.\u003C\u002Fp>\u003Cp>Every operation in a flow appends its data to a key that you can then access. So there's two that are special, which are accountability and dot trigger. That's why they have or I'm sorry. Dollar sign trigger. That's why they have that dollar sign in front of them.\u003C\u002Fp>\u003Cp>But the rest of them, we could just do like this. So it'll be call external API. That's the key. And then we're just gonna get the ID. Right?\u003C\u002Fp>\u003Cp>Cool. Hit save. This is our flow. Super complicated, super messy. No.\u003C\u002Fp>\u003Cp>It's not. It's easy. And then we're gonna go in, and now we're gonna create a new category. So we hit new category, And, oh, undefined. That's not good.\u003C\u002Fp>\u003Cp>What is happening here? Call external API. That's my key. Did we get some records back? Okay.\u003C\u002Fp>\u003Cp>Yep. So that's the issue. Right? Now I incorrectly formatted this. The thing that we're looking for is dot data dot ID.\u003C\u002Fp>\u003Cp>Alright. Always gotta do a bit of troubleshooting. I was hoping I was just gonna one shot that without any AI assistance, but, hey, it happens. And I also saw that we're missing the name. So in this case, you know, I'm just going to call the name.\u003C\u002Fp>\u003Cp>So we'll do trigger dot payload dot name. I think that's gonna be correct. Now, again, this is probably where you might wanna use something like JavaScript. But we can see here in our trigger, we have a payload. We have a name for the payload.\u003C\u002Fp>\u003Cp>Awesome. Alright. So now, fingers crossed, this should work or it's gonna error out because we can't have two categories with undefined. This is new category. Hit save, And voila, we've got an ID that has come from the ether.\u003C\u002Fp>\u003Cp>Now the other approach to do this, you know, if you prefer to express your logic as code and you wanna, you know, check that into Git, what you can do is just build an extension that does the same thing. Right? How do I do that? Well, go to our extension docs. So just go to guides, go to extensions, go to quick start, copy this code.\u003C\u002Fp>\u003Cp>I'm just gonna open up my terminal. We are going to put this here, APX direct us extension latest, and we're just gonna write a custom hook. And I'm going to even cheat and just use something that I've got in, another project. Alright. Choose a name for the extension.\u003C\u002Fp>\u003Cp>External ID, maybe. Sounds good. Cool. We're gonna use TypeScript auto installer dependencies, and now I've got this external ID extension. It actually needs to be in my extensions folder here inside Directus.\u003C\u002Fp>\u003Cp>So, let's just blow this whole thing up and do that. My Directus project CD extensions slash external ID. Why does that not work? CD extensions. What is my directory?\u003C\u002Fp>\u003Cp>Oh, somehow I'm inside a Directus. I gotta do that. CD extensions, external ID. Okay. And if I open this up, it's just an index dot ts file whenever the item is created.\u003C\u002Fp>\u003Cp>Let's pull up just something from my clipboard. Where are you? Okay. Cool. So this is a pattern that I've used quite a bit before.\u003C\u002Fp>\u003Cp>We're just gonna call this categories. Right? And we're gonna erase nano ID. We don't really need that. Cool.\u003C\u002Fp>\u003Cp>And we'll say, what, fetch? We could do a fetch call in here. Alright. Constant ID. You know what?\u003C\u002Fp>\u003Cp>Let's just let's keep it simple. Nano ID. Cool. So what this does, right, we define this hook, and we'll go into we see this filter that's on items dot create, and then we're passing in the payload, our schema from the direct us. And, we've got our accountability object, which we're not using at all here.\u003C\u002Fp>\u003Cp>But then we've got this constant that says, okay. These collections have to have a public ID. And in this case, I could just change it to ID. And all we're doing here is updating the payload and returning the payload. Payload has no ID.\u003C\u002Fp>\u003Cp>Alright. I'm gonna hit NPM build. Directus CD extensions. External ID. Cool.\u003C\u002Fp>\u003Cp>There we go. PMPM build. Build my extension. I just wanna show you one way that can get this done. And then I've got my directus instance.\u003C\u002Fp>\u003Cp>We're gonna need to restart that bad boy. So we'll hit restart. It should pick up that new extension. And fingers crossed. If we go to extensions, we could see that extension.\u003C\u002Fp>\u003Cp>And then I can deactivate. Let's not delete it entirely. So we'll just deactivate this flow, change the status to inactive. Cool. Got it.\u003C\u002Fp>\u003Cp>And now if I do this again, say test category. Fingers crossed. I know this is not gonna work. Oh, it does work. There we go.\u003C\u002Fp>\u003Cp>Test category. I was gonna say, I don't think I have Nano ID in installed, but maybe we're using that somewhere inside Directus itself. Cool. There it is. Couple different ways to achieve it.\u003C\u002Fp>\u003Cp>Hope this answers your question. Daryl, my man, thanks for joining the community. Appreciate you. Keep the questions coming. Keep the feedback coming.\u003C\u002Fp>\u003Cp>We love it. I'll see you next time on the community hotline.\u003C\u002Fp>","And welcome back to the community hotline. I'm your host Brian Gillespie from Directus. Today, we are answering a question from Directus community member, Daryl Morley. Daryl, how are you, sir? Welcome to the community. Happy to have you. I'm gonna answer your question today in detail. Alright. So the question from Daryl. I'm integrating Directus with Lightspeed retail API. Not heard Lightspeed name in a long time. That's awesome though. Would like to use, for example, the category ID from Lightspeed as the primary key in my categories collection within Directus. Here's the flow I'm aiming for. One, a the first step. A user creates a new category from the Directus admin UI. A before create hook fires that's not actually what it's called in directus, but I got you. It creates a category inside Lightspeed via their API. We receive the category ID back from Lightspeed, and it sets that ID as the primary key of the new Directus record. The issue I'm running into is if the ID field is not auto generated, Directus requires you to provide an ID. Yeah. You gotta have that primary key for sure. But I can't know the ID until I call the Lightspeed API inside the hook. Prevents me from using the Lightspeed category ID as a directus primary key. I could potentially store light speed ID in a separate external category ID field, also an option, but then I run into difficulties linking my items to categories as, the directus only sports relationships using the primary key. You could establish relationships on that that other field as well, but I totally understand where you're coming from with this. And Daryl's pleading because anyone successfully implemented a setup like this or found a clean workaround, keeps everything in sync. Alright. So we are going to dive into this, Daryl. We are gonna solve this problem together right now. Alright. So I've got a blank Directus instance. We're gonna go in and let's create a new collection. I use plurals. We're gonna call it categories. Now every collection has to have a primary key. Typically, I just leave this set to ID for simplicity's sake, And usually I go with a generated UUID. We automatically generate that. In this case, where we want that to come from a third party, we are going to use a manually entered string. Now optional fields, I usually add these a 100% of the time. Not gonna do that here. I'm just gonna click finish setup, and what we're gonna do is just hit name. Let's add one more field for the name of this category, and this is our categories. Right? So now the challenge is if I go in and this is a new category, just as you said, if I try to save this using command s or just hitting the save button here, requires a value. Well, that sucks. That's not what I wanna do. So how do we fix this? There's two ways I can solve this. I'm gonna show you flows first, and then we'll talk about hooks. Underneath the hood, they use the same event system. But, one way to do this inside the studio without writing much code or creating a custom extension that you've got to bundle and deploy, you could just do this in a flow. And if the logic is simple enough, it's it's easy to maintain. So we're gonna call this create category is the name of this flow cool in external API. Now I'm not actually gonna use the light speed API here just to keep this high level, but, yeah. You'll you'll figure that part of it out. Got faith in you. Got confidence. Alright. So we can trigger these flows a variety of different ways. The one we're looking for is event hook, and then we're immediately asked the type of event hook. So there's two, obviously, that's on the screen. There's filter, which is, they said we say blocking here because it blocks that event from actually performing. This is the one that we want because we get a chance to it runs before the item gets saved in the database or before the item gets updated. We'll talk about that in a moment. Actions happen after the event is settled, so after the category has been created. In this case, we want to intercept that category as it's being created and before it's saved into the database, and we're gonna alter the payload. So that's what we're doing here. The scope, we just want items create. Right? Updated items should already have an ID, and we don't wanna complicate this. The response body is going to be our altered payload with this. So this is the payload that we're gonna save to the database. Now you can choose between all data or the data of the last operation. In this case, I'm just gonna keep this simple and choose data of the last operation to remove as many potential issues. Alright. So now we've got our trigger. The next thing that we wanna do is call the external API. Now, I could go in and just choose webhook request URL. I'm gonna hit call external API here. This is our step. In this case, it's gonna be a post. And I've just set up, like, a a mock endpoint, from some online free service that when we send this request, it should just send us an ID back. Cool. Alright. And now the last step, and, you know, and probably, like, that stuff you're getting back from Lightspeed, you're gonna wanna transform that payload. There's two ways to do that. And let's just call this payload. Right? I could use the run script operation where I've got the full power of JavaScript. Now you can't import anything from NPM or use any libraries here, but you you've got, you know, the standard JavaScript library available to you here, and you can run, you know, just basic little functions there. Actually, you don't have the standard JavaScript library. This runs in an isolate, but you got all the basic JavaScript stuff that you're gonna need to transform a payload. The other way to do this is via the transform payload operation, which basically just allows you to adjust what is gonna come out of this thing. And you'll see we're using mustache syntax here for a reason. Every operation in a flow appends its data to a key that you can then access. So there's two that are special, which are accountability and dot trigger. That's why they have or I'm sorry. Dollar sign trigger. That's why they have that dollar sign in front of them. But the rest of them, we could just do like this. So it'll be call external API. That's the key. And then we're just gonna get the ID. Right? Cool. Hit save. This is our flow. Super complicated, super messy. No. It's not. It's easy. And then we're gonna go in, and now we're gonna create a new category. So we hit new category, And, oh, undefined. That's not good. What is happening here? Call external API. That's my key. Did we get some records back? Okay. Yep. So that's the issue. Right? Now I incorrectly formatted this. The thing that we're looking for is dot data dot ID. Alright. Always gotta do a bit of troubleshooting. I was hoping I was just gonna one shot that without any AI assistance, but, hey, it happens. And I also saw that we're missing the name. So in this case, you know, I'm just going to call the name. So we'll do trigger dot payload dot name. I think that's gonna be correct. Now, again, this is probably where you might wanna use something like JavaScript. But we can see here in our trigger, we have a payload. We have a name for the payload. Awesome. Alright. So now, fingers crossed, this should work or it's gonna error out because we can't have two categories with undefined. This is new category. Hit save, And voila, we've got an ID that has come from the ether. Now the other approach to do this, you know, if you prefer to express your logic as code and you wanna, you know, check that into Git, what you can do is just build an extension that does the same thing. Right? How do I do that? Well, go to our extension docs. So just go to guides, go to extensions, go to quick start, copy this code. I'm just gonna open up my terminal. We are going to put this here, APX direct us extension latest, and we're just gonna write a custom hook. And I'm going to even cheat and just use something that I've got in, another project. Alright. Choose a name for the extension. External ID, maybe. Sounds good. Cool. We're gonna use TypeScript auto installer dependencies, and now I've got this external ID extension. It actually needs to be in my extensions folder here inside Directus. So, let's just blow this whole thing up and do that. My Directus project CD extensions slash external ID. Why does that not work? CD extensions. What is my directory? Oh, somehow I'm inside a Directus. I gotta do that. CD extensions, external ID. Okay. And if I open this up, it's just an index dot ts file whenever the item is created. Let's pull up just something from my clipboard. Where are you? Okay. Cool. So this is a pattern that I've used quite a bit before. We're just gonna call this categories. Right? And we're gonna erase nano ID. We don't really need that. Cool. And we'll say, what, fetch? We could do a fetch call in here. Alright. Constant ID. You know what? Let's just let's keep it simple. Nano ID. Cool. So what this does, right, we define this hook, and we'll go into we see this filter that's on items dot create, and then we're passing in the payload, our schema from the direct us. And, we've got our accountability object, which we're not using at all here. But then we've got this constant that says, okay. These collections have to have a public ID. And in this case, I could just change it to ID. And all we're doing here is updating the payload and returning the payload. Payload has no ID. Alright. I'm gonna hit NPM build. Directus CD extensions. External ID. Cool. There we go. PMPM build. Build my extension. I just wanna show you one way that can get this done. And then I've got my directus instance. We're gonna need to restart that bad boy. So we'll hit restart. It should pick up that new extension. And fingers crossed. If we go to extensions, we could see that extension. And then I can deactivate. Let's not delete it entirely. So we'll just deactivate this flow, change the status to inactive. Cool. Got it. And now if I do this again, say test category. Fingers crossed. I know this is not gonna work. Oh, it does work. There we go. Test category. I was gonna say, I don't think I have Nano ID in installed, but maybe we're using that somewhere inside Directus itself. Cool. There it is. Couple different ways to achieve it. Hope this answers your question. Daryl, my man, thanks for joining the community. Appreciate you. Keep the questions coming. Keep the feedback coming. We love it. I'll see you next time on the community hotline.","27bbae9c-2dcf-4b93-a5f3-af1d69caf24f",[663],"36cfc426-c09f-4a0f-8967-008272477fb7",[],{"year":399,"number":630,"id":631,"episodes":666,"show":667},[633,634,616],{"title":636,"slug":637},{"id":669,"slug":670,"vimeo_id":671,"description":672,"tile":673,"length":674,"resources":12,"people":12,"episode_number":675,"published":676,"title":677,"video_transcript_html":678,"video_transcript_text":679,"content":12,"seo":680,"status":19,"episode_people":681,"recommendations":685,"season":686},"3d916baf-bb4c-4fa7-8d0d-a7beb07945ff","september-2025","1121526894","Join us for The Changelog, taking you through the month’s Directus updates including product updates, new content and community contribution highlights.","0010da3d-a46b-4022-9f42-01cccc9d706c",37,13,"2025-09-26","September 2025","\u003Cp>Speaker 0: Hello everyone! I hope you're having a wonderful day. Welcome to September's version of the changelog. If you're new here I'm Beth and I'm gonna be taking you through what we've got in store for you for this month. If you are joining us live, do let us know along the way if you've got any questions.\u003C\u002Fp>\u003Cp>And if you are joining us later, there'll be multiple places that you can ask your questions. If you're on LinkedIn, if you're on YouTube, ask there. If you are finding this somewhere else, community.directors.i0 is the place to go in the meantime, and I'm kicking it off with what we have new with product. Hello, everyone. It's been a little while since we last had our product update here with us being away from the change log in August, so let's not waste any time and let's jump into what's been going on with Directus since we last spoke.\u003C\u002Fp>\u003Cp>For Directus 11.1, there's some potentially breaking changes. Firstly is snapshots behavior. We now exclude database only tables from snapshots, meaning tables not tracked in Directus collections. The second breaking change affects typescript extension developers. Services exposed to API extensions are now fully typed instead of any which means you might see new type errors when building extensions.\u003C\u002Fp>\u003Cp>Things like item service constructor now expect strict string types and methods like read one and read many expect specific types for primary keys. WYSIWYG adds through improvements. We've also added a code tool to the WYSIWYG text editor which gives you more flexibility when editing content plus various accessibility improvements for anchors iframes and labels. Flows updates. There's a new error operation in flows and we've added support for private key JWT auth method in the open id driver.\u003C\u002Fp>\u003Cp>Other notable updates. We've upgraded the extensions SDK with the latest versions of rollup and vite, though this does raise the minimum node. Js version to 20.19.0 plus the usual batch of bug fixes and optimizations throughout the platform. On to directus 11.11, which has some potential breaking changes around content versioning. However, since I'm also gonna be talking about directors 11.12 right now and next up, I think I'll leave talking about content versioning until it's the most up to date version.\u003C\u002Fp>\u003Cp>But if you are wanting more information about content versioning and how it affects 11.11 check out the breaking change docs on github. Within 11.11 we improved the wysiwyg editor with proper link styling and fixed some code mode issues. We also upgraded esbuild, write, and updated nodemailer to AWS sesv2. And we added a new field to conditions for clearing hidden fields on save. Short and sweet, moving on to 11.12.\u003C\u002Fp>\u003Cp>For directors 11.12, there's some potentially breaking changes related to content versioning. We fixed how user created, user updated, date created, and date updated values work in content versioning. These fields now correctly reflect the actual user and timestamp of creation or last update rather than the user and date of promotion. Also requesting a non existent version will now return a forbidden error. Beyond those potentially breaking changes we've got some pretty significant improvements for content versioning.\u003C\u002Fp>\u003Cp>We've completely rewritten how content versioning handles relational data and query parameters. This was a complex undertaking but the result is a much more robust and predictable versioning system. Content versions now support all the same query parameters as regular content. Filters, sorting, field selection, and aggregation all work just like you'd expect. We've also got proper relational data merging so versioned content with relationships now merges correctly giving you accurate representation of your content at any point in time.\u003C\u002Fp>\u003Cp>And the new implementation is significantly faster when working with complex relational structures. This is just one step towards improving our content versioning experience. We have lots more planned so stay tuned. Right to left language support improvements. We've also expanded our studio accessibility with some important improvements for localization.\u003C\u002Fp>\u003Cp>Context menus now properly align and display for RTL languages and we've got better handling of directional layouts across the data studio interface. These improvements make directors much more accessible for teams working in RTL languages including Arabic, Hebrew, Farsi, and Urdu just to name a few. MCP support and technical improvements. On the technical side, we've also got some MCP support rolling out. This is still in beta so we'll have much more to share on that front soon.\u003C\u002Fp>\u003Cp>We fixed OAuth flow to allow for 2FA setup for users without passwords, where previously if you did not have a password linked to your account you couldn't set up 2FA, now you can. Bug fixes note: there are also a bunch of smaller improvements and bug fixes. If you'd like to see the full release notes they're available on GitHub. And that's it for 11\u002F2012. As always let us know if you've got any questions the best place to do that is community.directus.io.\u003C\u002Fp>\u003Cp>We also just generally love hearing about how these updates improve your work with Directus. Thank you so much for taking the time and we'll catch you on the next release. Alright and next up we have both Brian and Ryke getting together to talk about the marketplace updates.\u003C\u002Fp>\u003Cp>Speaker 1: Thanks for the intro, Beth. Ryke and I are here, and we're both super excited to announce the public marketplace. Ryke, what are we talking about when we say public marketplace?\u003C\u002Fp>\u003Cp>Speaker 2: We're talking about the marketplace on the website this time. So if you recall, the marketplace started life as internal tooling to solve, you know, extension distribution. How do people get their hands on extensions? Both for the ones that we built, but also the ones that other developers like to share with our large user base. So we've had the marketplace in the studio of Directus itself for a little while.\u003C\u002Fp>\u003Cp>And I'm happy to announce that starting today, that is no longer in beta as well. But we're here to announce another great update to that marketplace, which is the public marketplace. So the public marketplace is a version of the marketplace. You see the same extensions available on our website. So the rest of the internet can explore it as well.\u003C\u002Fp>\u003Cp>Brian, you wanna give a little demo of what that looks like?\u003C\u002Fp>\u003Cp>Speaker 1: Absolutely. Thank you, sir. Alright. So the public marketplace is all about solving discoverability. Unless you had a Directus instance, you were unable to view the amazing extensions that our community has put together.\u003C\u002Fp>\u003Cp>That changes today. So there are three components to the marketplace. You can find it at directus.io\u002Fextensions, and you could see all the different extensions that are available to install inside your directus instance. We've got filtering capabilities, so you could sort by or filter by the different types of extensions. Super easy.\u003C\u002Fp>\u003Cp>We can search for extensions. This is typo tolerant search as well. So if you're like me, quick with the the fingers, this is, a great search experience. Some of the new capabilities that we have in the public marketplace are the ability to see trending extensions over the past week or the past thirty days. So you could see who's installing the most popular extensions over a certain period of time.\u003C\u002Fp>\u003Cp>When you click into a given extension, you'll see a full overview of the extension. You can see all the statistics, which versions of Directus it works with, how many total downloads, even report issues for that. And then once you're ready to install that extension, all you have to do is click install extension, enter in your Directus URL, like so, and hit install extension, and that will take you right inside your Directus instance to that extension ready for you to install. Alright. Other components to the marketplace include integrations.\u003C\u002Fp>\u003Cp>These are basically roll ups of the different extensions. So if you are looking to see if Directus integrates with HubSpot, for instance, we've got the HubSpot integration page where you can see all the different extensions that, interface with HubSpot, as well as general overviews. So we can quickly see our AI extensions or integrations here. And if we're looking at OpenAI, there's a lot of different extensions that use OpenAI, and you can quickly find all of them. Last but not least, we have templates.\u003C\u002Fp>\u003Cp>So templates are starters for your next Directus project. We have some of our own starters as well as community contributed starters like the adventure business toolkit by Mimi Paul. And here you can get started with a fully baked Directus project, ready to customize in no time at all. That's it for the demo of the public marketplace. Reich, any last words before we kick it back over to Beth?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. This is awesome, Brian. Thank you very much. And another another step in in sort of our extension story and how to customize Directus. Where'd you say people could find this again?\u003C\u002Fp>\u003Cp>Speaker 1: It is directus.io\u002Fextensions. Awesome.\u003C\u002Fp>\u003Cp>Speaker 2: Directus.io\u002Fextensions. And with that, back to you, Beth.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. And in very exciting news, this is not the last of Ryke and Brian that you will see in this show. So for now, I'm gonna send it over to one of my I say this every single time. It's my favorite segment that we do. It is the community showcase.\u003C\u002Fp>\u003Cp>And for the very first time, we have someone coming back that has previously been on this show. So we have Josh with the note taking project.\u003C\u002Fp>\u003Cp>Speaker 3: Today, I would like to show you how to supercharge AI batch processing using Directus MCP with a self learning note taking workflow. Batch processing is really effective for, for situations where you need raw speed, but it kinda breaks down when you need to make creative decisions on individual items and on the batch as a whole. You can't really do that without a human element. AI models, I found, are great for those sorts of tasks, but they break down when you give them a whole bunch of data. And they also struggle with finding the right information to pull in to make those creative decisions.\u003C\u002Fp>\u003Cp>So the solution I found is to teach AI to take notes. By implementing a workflow that lets Directus MCP take notes directly on whatever it's working on, we gain the ability to do things like long running task tracking because the model can pick up where it left off and and, start fresh with a new context window. And we also get things like self improving database access. You take a note on all the different things that you ran into difficulty with and what you did to solve them and how it worked out. And it speeds up future runs because it can read that note and go, okay.\u003C\u002Fp>\u003Cp>So don't do this. Do this, and I'll be able to just continue with the task I was working on. And then by letting it take notes, we can consolidate a whole bunch of data that was stored in the database. Say, for example, you wanted to pull out all of the aliases for your articles that are related to rabbits and you also want the ID for each one. Well, you can just dump that in a note and analyze it in future runs.\u003C\u002Fp>\u003Cp>So in our case, Directus is the knowledge backbone. Every note is just a key value pair. I'll go ahead and show you the notes table here. It's a very simple setup. We have a key, which is just a string, and this is what the AI model uses to kind of categorize what the different notes are about.\u003C\u002Fp>\u003Cp>And we have the value, and that's just a big markdown, field. It's a very simple setup, but it's surprisingly powerful. By using semantic keys and explicit instructions on how to record and review notes, we give the AI models a very quick and effective way to find the context they need. So this database has a problem. We have a whole bunch of articles.\u003C\u002Fp>\u003Cp>We have a 124 articles, and the titles don't really make any sense. Oh, the bodies are in Latin. And I I can't work with this. So I've created a prompt for Claude to read the article titles and come up with a concept proposal for each one. What could this article actually be about?\u003C\u002Fp>\u003Cp>So let's look at the system prompt here. You can see that there's two things that it's informing the AI model about. That there's an AI notes table in the rough structure of that table and that it needs to read and record its database insights, especially how it solved problems it ran into and just update that note on every run. This gives it the self learning ability we were talking about earlier. It's able to figure out what it ran into last time and fix it on the next run.\u003C\u002Fp>\u003Cp>Now back to the task. I've told the AI model it's a skilled content analyst and writer. We have a huge collection of articles. All the bodies are full of nonsense, all that. Alright.\u003C\u002Fp>\u003Cp>So its job is to record the concepts that it comes up with in an article concepts note. It's not sure what to do with an article based on the title. Be creative. Find a wacky concept. Then we have some very specific instructions.\u003C\u002Fp>\u003Cp>We say always read the template and template article concepts, this is a note, to determine how to structure your output. Always create a new article concepts note for each run. Always give it the exact name. Always process 30 articles. Always review the last note you created before starting to make sure you don't duplicate any work.\u003C\u002Fp>\u003Cp>This template lets us make sure that we get consistent output on every run. Alright. Let's go ahead and run this a few times and see what its output looks like. I turn on auto refresh, and then I'm gonna go into Claude, add a prompt from Directus, and examine articles and propose concepts. If you want to know how to use this feature, go ahead and take a look at the Directus MCP documentation, and then we're just going to send it.\u003C\u002Fp>\u003Cp>Now, one of the fun things you can do with read and write access to Directus is if the model makes a mistake, you can ask it to update the prompt to fix its mistake in future runs. Article concepts. It has identified directly that just about every article appears to be lorem ipsum gibberish and occasional test content. Alright. So came up with a bunch of different articles on a bunch of different ideas.\u003C\u002Fp>\u003Cp>In a bit, we're gonna use this to actually write all these different article concepts. But for now, I just wanna keep going through the batch process to show you how it's able to pick up where it left off and continue. On this run, if you notice, it actually picked up that it needed to look up the translations and didn't have to figure that out from the schema this time. Alright. It is a new day.\u003C\u002Fp>\u003Cp>My cloud usage limits have reset, and we are ready to continue with step two, which is generating the articles based on the concepts that we've put together. All of the, article concepts have now been saved in these, concept notes. They take the article title. They try to figure out what on earth, the article should be about and propose a concept. So now we have the next step of the process, which is to generate a actual article for each of the articles we generate concepts on in both English and German.\u003C\u002Fp>\u003Cp>And we're gonna go in batches of 10 and see how well that works. We'll take this prompt and it should automatically read over all of our article concept notes and start filling in articles for those. Let's go. So we're going to use the turn concepts in the full fledged articles prompt. That's going to go and read through all of our article concept notes and start filling in those articles, and I'll show you those articles as it writes them.\u003C\u002Fp>\u003Cp>I have no idea if these articles are gonna make any sense. This is gonna be fun. I'm having fun reading through these articles. I have no idea how helpful any of this is, but it at least sounds very convincing. Now, the key advantage to this approach is that if we were generating concepts and writing articles at the same time, we'd have to use much smaller batches.\u003C\u002Fp>\u003Cp>But because we split the process into two steps, we've saved a ton of context window, and we're able to work in larger batches. And as a bonus, we can do multiple things with those article summaries, while we're working on generating those articles from those summaries. Now this isn't the most efficient workflow for generating articles. There's way better workflows for that. We're just demonstrating this concept.\u003C\u002Fp>\u003Cp>But a nice thing about this approach is that you can perform multiple tasks at the same time. For example, while we were working on these articles, we could also be working on something completely different using the notes that we're generating the articles from. I have this prompt here, categorize article concepts, which will allow Claude to suggest an article taxonomy based on the summaries it's already generated. It'll just read all the article concept notes and create a new note suggesting that new taxonomy. I like the way it describes these articles as a fascinating collection of technical and business focused article concepts with creative jargon filled titles that have been transformed into practical valuable content ideas.\u003C\u002Fp>\u003Cp>I'm not quite sure I'd be so positive about it, but Nate gets the idea across. Alright. Let's take a look at the categories that are generated and the opportunities that identified. So here's some content gaps, AI and ethics, sustainable technology operations, human centered digital transformation, all sorts of other different things. And here are some of the categories that's come up with.\u003C\u002Fp>\u003Cp>System architecture, it's put a bunch of different articles in that category, Ecommerce, digital business, data management and analytics, user experience and interface design, business operations or business optimization management, automation and AI systems, project management and collaboration. So So it's basically taken all of those articles that we put together and categorized them according to common themes in those articles. This could be really useful if you have an existing content collection that you're trying to build a new categorization system for. And what's great is you could just take this and create another prompt to actually apply that taxonomy to articles, and create categories and all sorts of different things for that in Directus. So by the end of this run, here's what the AI model has built: concept proposals for every article in our database, full body content for many of those articles in English and German, a whole new content categorization system for the articles it wrote, progress logs so that it doesn't lose its place, and database access hints that documents how the challenges were solved so that on future runs, it could do even better.\u003C\u002Fp>\u003Cp>Instead of treating AI like a disposable worker or one that can infinitely stuff and stuff and stuff and work on something until it gets all confused, we're treating it like a teammate that tracks and follows up on what it's done across multiple days, multiple runs, starting with a clean slate on each task and only pulling in the information that it needs. We turned a human in the loop batch processing task into an AI creative workflow by splitting it into discrete steps and letting it kind of pick up where it left off. We gave the model a way to track progress. We built templates to make the results more consistent and reusable, and we recorded information about the database to help the AI model get smarter over time. I've had a ton of fun nailing down this workflow and testing it on all sorts of tasks.\u003C\u002Fp>\u003Cp>For my work, we're actually using it to do things similar to this where we're analyzing a huge amount of content and trying to figure out how to organize and categorize and and do all sorts of things, and it's it's doing really well. The ability to take notes, record what it's done, and just kind of start over and create its own context has been incredibly helpful with turning a workflow that works really well for one or two articles into something that works well across thousands. So thank you for your time. I really look forward to seeing what everyone does with Directus and Directus MCP going forward, especially as Directus MCP evolves, AI models get more capable, and just the overall\u003C\u002Fp>\u003Cp>Speaker 0: core gets stronger. The future is gonna be fun. Thank you once again to Josh for sending in that amazing showcase. This actually started because it was a post on the community forum, so do if you have been inspired and you've got any questions for Josh, he is around at community.directors.io. And, speaking of the MCP and AI, I gave a bit of a challenge to Bryant and Reich with an AI update.\u003C\u002Fp>\u003Cp>I was like, please can we have an AI update for the change log? And I gave them no more context, and they delivered. So here you have an AI update from Brian and Reich.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. And we're back. So in this segment, Beth has challenged Wrike and I to have a conversation on AI and Directus. Where that conversation is gonna go, let's see. Right?\u003C\u002Fp>\u003Cp>But Wrike, you know, what's what is our take on AI at Directus? We'll just What is\u003C\u002Fp>\u003Cp>Speaker 2: our take? That is a very six hour long answer in a five minute segment. So let's let's start\u003C\u002Fp>\u003Cp>Speaker 0: at the\u003C\u002Fp>\u003Cp>Speaker 2: beginning. Well, as you know, most everybody else in the industry, we've been using AI a lot internally to try to figure out and get a feel for what works, what doesn't, where are the opportunities, where are the challenges that to then figure out what do we want to do with AI in the context of Directus. Right? So the way we see it right now, there's basically two major touch points for artificial intelligence in the world of Directus. It is one, what can external tools do with Directus on the user's behalf?\u003C\u002Fp>\u003Cp>So think about a a cloud desktop or a JetGPT. And secondly, what can we do using LLM technology within the studio and API to enrich the user experience of Directus itself? So Brian, you wanna start with the with the first?\u003C\u002Fp>\u003Cp>Speaker 1: Yes. Absolutely. So what can external LLMs do with the data inside Directus? Right? We've already previously shipped a local MCP server, and we've, we've gotten a ton of great feedback with that.\u003C\u002Fp>\u003Cp>Now, what we are currently in development with is an MCP server inside Directus. So you'll be able to connect remotely to your Directus instance and use the tools that are available to add content, update blog posts, build landing pages. But more than that, you will be able to edit your actual data models. Your update, all of your schema, add new fields, improve, the, the user experience for your content editors. So that really comes together nicely as a complete picture.\u003C\u002Fp>\u003Cp>But, on the other side of the coin, maybe you wanna talk about the the second piece. Right? What we plan to do with AI inside Direct Us.\u003C\u002Fp>\u003Cp>Speaker 2: On the internal side, we as a team really strongly believe in shipping features that add actual value. So we have seen, you know, some uses before, I I won't name names, that are a little gimmicky and not really customer friendly or actually useful in a day to day. So right now we're we have some solid plans and some early stuff in flight, that add some real value into Direct to Studio. Both using generative AI for generating and helping, optimize content and other data, but also in a way to sort of help some of the more click ops heavy configuration pieces of Directus itself. Right?\u003C\u002Fp>\u003Cp>So think about things like, flows or data model and things like that. On the first part though, I know we're flipping back and forth a little bit, but both both tied together. On the MCP front, one thing you mentioned there, Brian, is like it can do data modeling, it can do, you know, data access. Is is that just a a free for all agent that is just gonna go rogue on my database? Or how do we how do we protect that?\u003C\u002Fp>\u003Cp>Speaker 1: I I'm glad you mentioned that because that is one of the scariest pieces of AI in my mind right now. There's other MCP servers where you can connect directly to a Postgres database and basically run any raw SQL queries against that. So you and I both, probably break out in a sweat when we talk about something like that. The MCP server inside Directus is beautiful because it is scoped by the permissions that we make, available through the API. So any, user connecting via the MCP, you can only perform the different operations that that user has access to, which means, improved security compared to the, maybe some other MCP implementations.\u003C\u002Fp>\u003Cp>And also we prevent you from taking super destructive actions that, could, result in a lot of data loss or just general heartache for both content editors and developers.\u003C\u002Fp>\u003Cp>Speaker 2: Phew. Thank God. Yeah. Because to your point that, that does make me break out in, in sweat. And the same goes for for the work that we're doing with LMS within the studio at the moment.\u003C\u002Fp>\u003Cp>Right? I think it's important to mention. So where, in in our testing with the MCP server, we find, you know, a lot of the LMS as of September 2025 because the stuff change all the time. It is oftentimes a little trial and error. Right?\u003C\u002Fp>\u003Cp>You see an LOM tries something, it fails, it gets an error, tries again, it fails, it tries again, and it succeeds, and sometimes it gets there, and sometimes it doesn't. We spend quite a lot of time, really optimizing the system prompts to get that reliability up and up and up and it's in a spot where I'm like, this is actually fairly reliable. Like, as in as reliable as we can get it from an L. M. But by by integrating the L.\u003C\u002Fp>\u003Cp>M. Technology straight into the studio piece of it, we have the opportunity to do an extra layer of validation as well. Which means that we can make sure that we know what the l l m is about to do or what it wants to do and we can allow the user to validate and verify that before actually running some of these pieces. You know, akin to what you might have seen in a in a cursor or Copilot right before it runs terminal commands, things like that. So anyways, we'll we'll leave it at that because I don't wanna, you know, tease too much before before it's ready for the prime time.\u003C\u002Fp>\u003Cp>But we have some very exciting stuff coming. So I believe the MCP server, will ship somewhere in this month. We're sort of in the final testing phases. If you're interested, please do check out the PR if this goes out before it's merged. Otherwise, you'll see it on the main branch and in the upcoming release.\u003C\u002Fp>\u003Cp>For the internal AI piece, like I said, it's very much research and development early testing. Just feel free to ping me anywhere on the internet if you're interested to sort of learn more about that. We're always happy to chat and learn from, you know, what people actually wanna use this for. But expect more on that in the near future. So stay tuned for that.\u003C\u002Fp>\u003Cp>And with that, back again to Beth.\u003C\u002Fp>\u003Cp>Speaker 0: Alrighty thank you once again to Brian and Reich especially since I gave them not much context I was like I'm just gonna bring you into a recording studio and we're gonna talk about some things. So that was really helpful and I hope you're all excited if again if you've got any questions, do let us know. We're now gonna get over to Brian with a section for the community hotline.\u003C\u002Fp>\u003Cp>Speaker 1: And we're back with another episode of the community hotline. Welcome. Today, we have Wylus, Wylies. Sorry if I get the name wrong. Welcome to the community.\u003C\u002Fp>\u003Cp>Glad to have you, friend. Alright. So our question today, is building a front end CMS within Directus possible? So we'll tackle that in a moment. Let's read the rest of this.\u003C\u002Fp>\u003Cp>Good day, gurus. I'm looking for a powerful tool to support the following. SaaS, hold hosting multiple tenant within one project, creating pages if possible, but how could I render it within Directus? I saw tutorials, but only using Directus as a back end. How can we make this work potentially?\u003C\u002Fp>\u003Cp>Alright. So first and foremost, right, Directus is set up to be a headless CMS and back end. So that paradigm is very important to understand. WordPress is, has a headless option, but it's ideally a monolithic kind of solution where you've got your CMS and you've got your front end and the templating and all that is mixed together, whereas Directus is headless and that's the way it's designed. So you can, send that data to multiple sources.\u003C\u002Fp>\u003Cp>Maybe you've got a a front end, maybe you've got a mobile application, maybe you've got a kiosk or a display that you need to send data to or fetch data, from a back end. Directus is great for that. When it comes to building a front end CMS within Directus, basically, you're talking about the Directus App Studio. Right? I can not only just add my own team members to this, but I could potentially create a multi tenant CMS out of the Data Studio itself.\u003C\u002Fp>\u003Cp>So, if you take a look at our CMS starter, this is available, you know, through the website. If you go to directives.io, you hit this command line, or this script here. Just pop this into your terminal, select the CMS option, or if you go to our cloud, log in, you spin up a new project inside cloud, just pick the CMS template and you can get access to what I'm gonna show you here today. Right? So this comes out of the box.\u003C\u002Fp>\u003Cp>This is a a CMS. It's exactly what you would expect out of a CMS. We have pages, we have posts, etcetera. Now if I wanted to make this multi tenant, how would I go about it? Well, first, I would create a tenant collection.\u003C\u002Fp>\u003Cp>Maybe that's sites or, properties or whatever we wanna call it. And then for each collection within this, I add a relationship, many to one, back to that site's collection, back to that tenant collection. And then within our access policies, you've got everything you need to basically scope access down. So you can filter based on the site, so that one tenant cannot read the, data from another. Now, there's an specific episode of 100 apps, one hundred hours where we cover this exact thing.\u003C\u002Fp>\u003Cp>So just look for multi site. That mission is season three episode two. You can follow right along with this and see how to set up a multi tenant CMS using Directus. Now to your other question. Right?\u003C\u002Fp>\u003Cp>How can I create pages or render within Directus? Now Directus itself is extremely flexible, and this is where our extensions come into play. And a good example of that, if we just go to directus.io and we look at our sandbox demo, there's a little extra module in here that is not, baked into directus core. This is just an extension that we built to give you a welcome page that does get rendered inside Directus. Now this is coming from a separate Directus instance, but again, all of Directus is modular and extensible.\u003C\u002Fp>\u003Cp>So this is actually a module extension. But if we just go to our docs, we look for our guides, we go to the extensions section, you can learn all about extensions. So you can customize the way that you interact with data inside the forms. Those are interfaces. You can control the way that data is displayed in the different layouts throughout the studio.\u003C\u002Fp>\u003Cp>That is a display. You have layouts themselves so you can control the listing of these items. You know, I think tables or kanban, views or maps, whatever. We can add panels to the dashboards and kind of the overall level where you've got total control over what gets rendered on the page, you have a module. So if you want flexibility on how things are rendered inside Directus, Maybe you wanna add some extra pages to it or you just want something, a blank canvas where you could totally experiment with whatever you want.\u003C\u002Fp>\u003Cp>Custom modules are what you're looking at. So, with that in mind, I hope that is enough to answer your question. Wireless. Wireless. Sorry.\u003C\u002Fp>\u003Cp>Again, I hate if I get it wrong. But that's it for this episode of Community Hopeline. If you want me to answer one of your questions on the next change log or or on the next community hotline, make sure you hop into the Directus community, community.directus.io, and I'll see you around.\u003C\u002Fp>\u003Cp>Speaker 0: We want to take a moment towards the end of the changelog for thanking our community contributors who give their time to improve the director's project. Since last changelog, there have been 11 contributors. Thank you to Josh for fixing require selection check for manual trigger flows, Klayvo for adding a message property to the SDK error object, Amos for removing duplicate code in fields read all, Gerard for fixing a bug that was preventing translations from displaying in the calendar layout, Danton for fixing a bug that prevented pop ups from working in the WYSIWYG interface when opened in a drawer, Hughes for adding WebSocket authenticate filter hook, Abdullah for fixing the code tool to the WYSIWYG text editor and for fixing links in WYSIWYG missing underline and pointer cursor styling, Matt for adding TypeScript support for services within the extension context and for fixing an issue for empty states not being centered in RTL languages, Tim for standardizing batch mode for raw group fields, Gloria for enabling text selection in the studio, studio, Jens for adding the ability to override the email from property. Thank you again to everyone mentioned. You can see their specific full request inside of the full release notes on GitHub.\u003C\u002Fp>\u003Cp>Lastly, we also want to take the time to thank our GitHub Sponsors of July and August who financially contribute to Directus' development. A huge thank you to Weifan, Jens, Mike, Fergus, Omar, Marcus, Mission Control, Utomic, Steven, James, Nonlinear, Andreas, John, Wayne, Burb, Adam, Jason, Yuya, Vincent, c k, Valentino, and Hadi. The money we are given from our GitHub sponsors goes straight back to community members who build tooling and extensions for the director's ecosystem. Thank you again for being part of that. Alright.\u003C\u002Fp>\u003Cp>And that is it for the change log of September 13. Thank you if you are still with us and you've made it to the end. You're my favorite people. No matter whether you are new to this changelog or you've been returning each time, we hope to see you again. As we've said multiple times within this changelog, if you do have any questions, let us know.\u003C\u002Fp>\u003Cp>And, yeah, that's it. Have a great rest of your day, your week, and your month, and we hope to see you really soon. Have a good one, everyone. Take care. Bye.\u003C\u002Fp>","Hello everyone! I hope you're having a wonderful day. Welcome to September's version of the changelog. If you're new here I'm Beth and I'm gonna be taking you through what we've got in store for you for this month. If you are joining us live, do let us know along the way if you've got any questions. And if you are joining us later, there'll be multiple places that you can ask your questions. If you're on LinkedIn, if you're on YouTube, ask there. If you are finding this somewhere else, community.directus.io is the place to go in the meantime, and I'm kicking it off with what we have new with product. Hello, everyone. It's been a little while since we last had our product update here with us being away from the change log in August, so let's not waste any time and let's jump into what's been going on with Directus since we last spoke. For Directus 11.1, there's some potentially breaking changes. Firstly is snapshots behavior. We now exclude database only tables from snapshots, meaning tables not tracked in Directus collections. The second breaking change affects typescript extension developers. Services exposed to API extensions are now fully typed instead of any which means you might see new type errors when building extensions. Things like item service constructor now expect strict string types and methods like read one and read many expect specific types for primary keys. WYSIWYG adds through improvements. We've also added a code tool to the WYSIWYG text editor which gives you more flexibility when editing content plus various accessibility improvements for anchors iframes and labels. Flows updates. There's a new error operation in flows and we've added support for private key JWT auth method in the open id driver. Other notable updates. We've upgraded the extensions SDK with the latest versions of rollup and vite, though this does raise the minimum node. Js version to 20.19.0 plus the usual batch of bug fixes and optimizations throughout the platform. On to directus 11.11, which has some potential breaking changes around content versioning. However, since I'm also gonna be talking about directors 11.12 right now and next up, I think I'll leave talking about content versioning until it's the most up to date version. But if you are wanting more information about content versioning and how it affects 11.11 check out the breaking change docs on github. Within 11.11 we improved the wysiwyg editor with proper link styling and fixed some code mode issues. We also upgraded esbuild, write, and updated nodemailer to AWS sesv2. And we added a new field to conditions for clearing hidden fields on save. Short and sweet, moving on to 11.12. For directors 11.12, there's some potentially breaking changes related to content versioning. We fixed how user created, user updated, date created, and date updated values work in content versioning. These fields now correctly reflect the actual user and timestamp of creation or last update rather than the user and date of promotion. Also requesting a non existent version will now return a forbidden error. Beyond those potentially breaking changes we've got some pretty significant improvements for content versioning. We've completely rewritten how content versioning handles relational data and query parameters. This was a complex undertaking but the result is a much more robust and predictable versioning system. Content versions now support all the same query parameters as regular content. Filters, sorting, field selection, and aggregation all work just like you'd expect. We've also got proper relational data merging so versioned content with relationships now merges correctly giving you accurate representation of your content at any point in time. And the new implementation is significantly faster when working with complex relational structures. This is just one step towards improving our content versioning experience. We have lots more planned so stay tuned. Right to left language support improvements. We've also expanded our studio accessibility with some important improvements for localization. Context menus now properly align and display for RTL languages and we've got better handling of directional layouts across the data studio interface. These improvements make directors much more accessible for teams working in RTL languages including Arabic, Hebrew, Farsi, and Urdu just to name a few. MCP support and technical improvements. On the technical side, we've also got some MCP support rolling out. This is still in beta so we'll have much more to share on that front soon. We fixed OAuth flow to allow for 2FA setup for users without passwords, where previously if you did not have a password linked to your account you couldn't set up 2FA, now you can. Bug fixes note: there are also a bunch of smaller improvements and bug fixes. If you'd like to see the full release notes they're available on GitHub. And that's it for 11\u002F2012. As always let us know if you've got any questions the best place to do that is community.directus.io. We also just generally love hearing about how these updates improve your work with Directus. Thank you so much for taking the time and we'll catch you on the next release. Alright and next up we have both Brian and Ryke getting together to talk about the marketplace updates. Thanks for the intro, Beth. Ryke and I are here, and we're both super excited to announce the public marketplace. Ryke, what are we talking about when we say public marketplace? We're talking about the marketplace on the website this time. So if you recall, the marketplace started life as internal tooling to solve, you know, extension distribution. How do people get their hands on extensions? Both for the ones that we built, but also the ones that other developers like to share with our large user base. So we've had the marketplace in the studio of Directus itself for a little while. And I'm happy to announce that starting today, that is no longer in beta as well. But we're here to announce another great update to that marketplace, which is the public marketplace. So the public marketplace is a version of the marketplace. You see the same extensions available on our website. So the rest of the internet can explore it as well. Brian, you wanna give a little demo of what that looks like? Absolutely. Thank you, sir. Alright. So the public marketplace is all about solving discoverability. Unless you had a Directus instance, you were unable to view the amazing extensions that our community has put together. That changes today. So there are three components to the marketplace. You can find it at directus.io\u002Fextensions, and you could see all the different extensions that are available to install inside your directus instance. We've got filtering capabilities, so you could sort by or filter by the different types of extensions. Super easy. We can search for extensions. This is typo tolerant search as well. So if you're like me, quick with the the fingers, this is, a great search experience. Some of the new capabilities that we have in the public marketplace are the ability to see trending extensions over the past week or the past thirty days. So you could see who's installing the most popular extensions over a certain period of time. When you click into a given extension, you'll see a full overview of the extension. You can see all the statistics, which versions of Directus it works with, how many total downloads, even report issues for that. And then once you're ready to install that extension, all you have to do is click install extension, enter in your Directus URL, like so, and hit install extension, and that will take you right inside your Directus instance to that extension ready for you to install. Alright. Other components to the marketplace include integrations. These are basically roll ups of the different extensions. So if you are looking to see if Directus integrates with HubSpot, for instance, we've got the HubSpot integration page where you can see all the different extensions that, interface with HubSpot, as well as general overviews. So we can quickly see our AI extensions or integrations here. And if we're looking at OpenAI, there's a lot of different extensions that use OpenAI, and you can quickly find all of them. Last but not least, we have templates. So templates are starters for your next Directus project. We have some of our own starters as well as community contributed starters like the adventure business toolkit by Mimi Paul. And here you can get started with a fully baked Directus project, ready to customize in no time at all. That's it for the demo of the public marketplace. Reich, any last words before we kick it back over to Beth? Yeah. This is awesome, Brian. Thank you very much. And another another step in in sort of our extension story and how to customize Directus. Where'd you say people could find this again? It is directus.io\u002Fextensions. Awesome. Directus.io\u002Fextensions. And with that, back to you, Beth. Alright. And in very exciting news, this is not the last of Ryke and Brian that you will see in this show. So for now, I'm gonna send it over to one of my I say this every single time. It's my favorite segment that we do. It is the community showcase. And for the very first time, we have someone coming back that has previously been on this show. So we have Josh with the note taking project. Today, I would like to show you how to supercharge AI batch processing using Directus MCP with a self learning note taking workflow. Batch processing is really effective for, for situations where you need raw speed, but it kinda breaks down when you need to make creative decisions on individual items and on the batch as a whole. You can't really do that without a human element. AI models, I found, are great for those sorts of tasks, but they break down when you give them a whole bunch of data. And they also struggle with finding the right information to pull in to make those creative decisions. So the solution I found is to teach AI to take notes. By implementing a workflow that lets Directus MCP take notes directly on whatever it's working on, we gain the ability to do things like long running task tracking because the model can pick up where it left off and and, start fresh with a new context window. And we also get things like self improving database access. You take a note on all the different things that you ran into difficulty with and what you did to solve them and how it worked out. And it speeds up future runs because it can read that note and go, okay. So don't do this. Do this, and I'll be able to just continue with the task I was working on. And then by letting it take notes, we can consolidate a whole bunch of data that was stored in the database. Say, for example, you wanted to pull out all of the aliases for your articles that are related to rabbits and you also want the ID for each one. Well, you can just dump that in a note and analyze it in future runs. So in our case, Directus is the knowledge backbone. Every note is just a key value pair. I'll go ahead and show you the notes table here. It's a very simple setup. We have a key, which is just a string, and this is what the AI model uses to kind of categorize what the different notes are about. And we have the value, and that's just a big markdown, field. It's a very simple setup, but it's surprisingly powerful. By using semantic keys and explicit instructions on how to record and review notes, we give the AI models a very quick and effective way to find the context they need. So this database has a problem. We have a whole bunch of articles. We have a 124 articles, and the titles don't really make any sense. Oh, the bodies are in Latin. And I I can't work with this. So I've created a prompt for Claude to read the article titles and come up with a concept proposal for each one. What could this article actually be about? So let's look at the system prompt here. You can see that there's two things that it's informing the AI model about. That there's an AI notes table in the rough structure of that table and that it needs to read and record its database insights, especially how it solved problems it ran into and just update that note on every run. This gives it the self learning ability we were talking about earlier. It's able to figure out what it ran into last time and fix it on the next run. Now back to the task. I've told the AI model it's a skilled content analyst and writer. We have a huge collection of articles. All the bodies are full of nonsense, all that. Alright. So its job is to record the concepts that it comes up with in an article concepts note. It's not sure what to do with an article based on the title. Be creative. Find a wacky concept. Then we have some very specific instructions. We say always read the template and template article concepts, this is a note, to determine how to structure your output. Always create a new article concepts note for each run. Always give it the exact name. Always process 30 articles. Always review the last note you created before starting to make sure you don't duplicate any work. This template lets us make sure that we get consistent output on every run. Alright. Let's go ahead and run this a few times and see what its output looks like. I turn on auto refresh, and then I'm gonna go into Claude, add a prompt from Directus, and examine articles and propose concepts. If you want to know how to use this feature, go ahead and take a look at the Directus MCP documentation, and then we're just going to send it. Now, one of the fun things you can do with read and write access to Directus is if the model makes a mistake, you can ask it to update the prompt to fix its mistake in future runs. Article concepts. It has identified directly that just about every article appears to be lorem ipsum gibberish and occasional test content. Alright. So came up with a bunch of different articles on a bunch of different ideas. In a bit, we're gonna use this to actually write all these different article concepts. But for now, I just wanna keep going through the batch process to show you how it's able to pick up where it left off and continue. On this run, if you notice, it actually picked up that it needed to look up the translations and didn't have to figure that out from the schema this time. Alright. It is a new day. My cloud usage limits have reset, and we are ready to continue with step two, which is generating the articles based on the concepts that we've put together. All of the, article concepts have now been saved in these, concept notes. They take the article title. They try to figure out what on earth, the article should be about and propose a concept. So now we have the next step of the process, which is to generate a actual article for each of the articles we generate concepts on in both English and German. And we're gonna go in batches of 10 and see how well that works. We'll take this prompt and it should automatically read over all of our article concept notes and start filling in articles for those. Let's go. So we're going to use the turn concepts in the full fledged articles prompt. That's going to go and read through all of our article concept notes and start filling in those articles, and I'll show you those articles as it writes them. I have no idea if these articles are gonna make any sense. This is gonna be fun. I'm having fun reading through these articles. I have no idea how helpful any of this is, but it at least sounds very convincing. Now, the key advantage to this approach is that if we were generating concepts and writing articles at the same time, we'd have to use much smaller batches. But because we split the process into two steps, we've saved a ton of context window, and we're able to work in larger batches. And as a bonus, we can do multiple things with those article summaries, while we're working on generating those articles from those summaries. Now this isn't the most efficient workflow for generating articles. There's way better workflows for that. We're just demonstrating this concept. But a nice thing about this approach is that you can perform multiple tasks at the same time. For example, while we were working on these articles, we could also be working on something completely different using the notes that we're generating the articles from. I have this prompt here, categorize article concepts, which will allow Claude to suggest an article taxonomy based on the summaries it's already generated. It'll just read all the article concept notes and create a new note suggesting that new taxonomy. I like the way it describes these articles as a fascinating collection of technical and business focused article concepts with creative jargon filled titles that have been transformed into practical valuable content ideas. I'm not quite sure I'd be so positive about it, but Nate gets the idea across. Alright. Let's take a look at the categories that are generated and the opportunities that identified. So here's some content gaps, AI and ethics, sustainable technology operations, human centered digital transformation, all sorts of other different things. And here are some of the categories that's come up with. System architecture, it's put a bunch of different articles in that category, Ecommerce, digital business, data management and analytics, user experience and interface design, business operations or business optimization management, automation and AI systems, project management and collaboration. So So it's basically taken all of those articles that we put together and categorized them according to common themes in those articles. This could be really useful if you have an existing content collection that you're trying to build a new categorization system for. And what's great is you could just take this and create another prompt to actually apply that taxonomy to articles, and create categories and all sorts of different things for that in Directus. So by the end of this run, here's what the AI model has built: concept proposals for every article in our database, full body content for many of those articles in English and German, a whole new content categorization system for the articles it wrote, progress logs so that it doesn't lose its place, and database access hints that documents how the challenges were solved so that on future runs, it could do even better. Instead of treating AI like a disposable worker or one that can infinitely stuff and stuff and stuff and work on something until it gets all confused, we're treating it like a teammate that tracks and follows up on what it's done across multiple days, multiple runs, starting with a clean slate on each task and only pulling in the information that it needs. We turned a human in the loop batch processing task into an AI creative workflow by splitting it into discrete steps and letting it kind of pick up where it left off. We gave the model a way to track progress. We built templates to make the results more consistent and reusable, and we recorded information about the database to help the AI model get smarter over time. I've had a ton of fun nailing down this workflow and testing it on all sorts of tasks. For my work, we're actually using it to do things similar to this where we're analyzing a huge amount of content and trying to figure out how to organize and categorize and and do all sorts of things, and it's it's doing really well. The ability to take notes, record what it's done, and just kind of start over and create its own context has been incredibly helpful with turning a workflow that works really well for one or two articles into something that works well across thousands. So thank you for your time. I really look forward to seeing what everyone does with Directus and Directus MCP going forward, especially as Directus MCP evolves, AI models get more capable, and just the overall core gets stronger. The future is gonna be fun. Thank you once again to Josh for sending in that amazing showcase. This actually started because it was a post on the community forum, so do if you have been inspired and you've got any questions for Josh, he is around at community.directors.io. And, speaking of the MCP and AI, I gave a bit of a challenge to Bryant and Reich with an AI update. I was like, please can we have an AI update for the change log? And I gave them no more context, and they delivered. So here you have an AI update from Brian and Reich. Alright. And we're back. So in this segment, Beth has challenged Wrike and I to have a conversation on AI and Directus. Where that conversation is gonna go, let's see. Right? But Wrike, you know, what's what is our take on AI at Directus? We'll just What is our take? That is a very six hour long answer in a five minute segment. So let's let's start at the beginning. Well, as you know, most everybody else in the industry, we've been using AI a lot internally to try to figure out and get a feel for what works, what doesn't, where are the opportunities, where are the challenges that to then figure out what do we want to do with AI in the context of Directus. Right? So the way we see it right now, there's basically two major touch points for artificial intelligence in the world of Directus. It is one, what can external tools do with Directus on the user's behalf? So think about a a cloud desktop or a JetGPT. And secondly, what can we do using LLM technology within the studio and API to enrich the user experience of Directus itself? So Brian, you wanna start with the with the first? Yes. Absolutely. So what can external LLMs do with the data inside Directus? Right? We've already previously shipped a local MCP server, and we've, we've gotten a ton of great feedback with that. Now, what we are currently in development with is an MCP server inside Directus. So you'll be able to connect remotely to your Directus instance and use the tools that are available to add content, update blog posts, build landing pages. But more than that, you will be able to edit your actual data models. Your update, all of your schema, add new fields, improve, the, the user experience for your content editors. So that really comes together nicely as a complete picture. But, on the other side of the coin, maybe you wanna talk about the the second piece. Right? What we plan to do with AI inside Direct Us. On the internal side, we as a team really strongly believe in shipping features that add actual value. So we have seen, you know, some uses before, I I won't name names, that are a little gimmicky and not really customer friendly or actually useful in a day to day. So right now we're we have some solid plans and some early stuff in flight, that add some real value into Direct to Studio. Both using generative AI for generating and helping, optimize content and other data, but also in a way to sort of help some of the more click ops heavy configuration pieces of Directus itself. Right? So think about things like, flows or data model and things like that. On the first part though, I know we're flipping back and forth a little bit, but both both tied together. On the MCP front, one thing you mentioned there, Brian, is like it can do data modeling, it can do, you know, data access. Is is that just a a free for all agent that is just gonna go rogue on my database? Or how do we how do we protect that? I I'm glad you mentioned that because that is one of the scariest pieces of AI in my mind right now. There's other MCP servers where you can connect directly to a Postgres database and basically run any raw SQL queries against that. So you and I both, probably break out in a sweat when we talk about something like that. The MCP server inside Directus is beautiful because it is scoped by the permissions that we make, available through the API. So any, user connecting via the MCP, you can only perform the different operations that that user has access to, which means, improved security compared to the, maybe some other MCP implementations. And also we prevent you from taking super destructive actions that, could, result in a lot of data loss or just general heartache for both content editors and developers. Phew. Thank God. Yeah. Because to your point that, that does make me break out in, in sweat. And the same goes for for the work that we're doing with LMS within the studio at the moment. Right? I think it's important to mention. So where, in in our testing with the MCP server, we find, you know, a lot of the LMS as of September 2025 because the stuff change all the time. It is oftentimes a little trial and error. Right? You see an LOM tries something, it fails, it gets an error, tries again, it fails, it tries again, and it succeeds, and sometimes it gets there, and sometimes it doesn't. We spend quite a lot of time, really optimizing the system prompts to get that reliability up and up and up and it's in a spot where I'm like, this is actually fairly reliable. Like, as in as reliable as we can get it from an L. M. But by by integrating the L. M. Technology straight into the studio piece of it, we have the opportunity to do an extra layer of validation as well. Which means that we can make sure that we know what the l l m is about to do or what it wants to do and we can allow the user to validate and verify that before actually running some of these pieces. You know, akin to what you might have seen in a in a cursor or Copilot right before it runs terminal commands, things like that. So anyways, we'll we'll leave it at that because I don't wanna, you know, tease too much before before it's ready for the prime time. But we have some very exciting stuff coming. So I believe the MCP server, will ship somewhere in this month. We're sort of in the final testing phases. If you're interested, please do check out the PR if this goes out before it's merged. Otherwise, you'll see it on the main branch and in the upcoming release. For the internal AI piece, like I said, it's very much research and development early testing. Just feel free to ping me anywhere on the internet if you're interested to sort of learn more about that. We're always happy to chat and learn from, you know, what people actually wanna use this for. But expect more on that in the near future. So stay tuned for that. And with that, back again to Beth. Alrighty thank you once again to Brian and Reich especially since I gave them not much context I was like I'm just gonna bring you into a recording studio and we're gonna talk about some things. So that was really helpful and I hope you're all excited if again if you've got any questions, do let us know. We're now gonna get over to Brian with a section for the community hotline. And we're back with another episode of the community hotline. Welcome. Today, we have Wylus, Wylies. Sorry if I get the name wrong. Welcome to the community. Glad to have you, friend. Alright. So our question today, is building a front end CMS within Directus possible? So we'll tackle that in a moment. Let's read the rest of this. Good day, gurus. I'm looking for a powerful tool to support the following. SaaS, hold hosting multiple tenant within one project, creating pages if possible, but how could I render it within Directus? I saw tutorials, but only using Directus as a back end. How can we make this work potentially? Alright. So first and foremost, right, Directus is set up to be a headless CMS and back end. So that paradigm is very important to understand. WordPress is, has a headless option, but it's ideally a monolithic kind of solution where you've got your CMS and you've got your front end and the templating and all that is mixed together, whereas Directus is headless and that's the way it's designed. So you can, send that data to multiple sources. Maybe you've got a a front end, maybe you've got a mobile application, maybe you've got a kiosk or a display that you need to send data to or fetch data, from a back end. Directus is great for that. When it comes to building a front end CMS within Directus, basically, you're talking about the Directus App Studio. Right? I can not only just add my own team members to this, but I could potentially create a multi tenant CMS out of the Data Studio itself. So, if you take a look at our CMS starter, this is available, you know, through the website. If you go to directives.io, you hit this command line, or this script here. Just pop this into your terminal, select the CMS option, or if you go to our cloud, log in, you spin up a new project inside cloud, just pick the CMS template and you can get access to what I'm gonna show you here today. Right? So this comes out of the box. This is a a CMS. It's exactly what you would expect out of a CMS. We have pages, we have posts, etcetera. Now if I wanted to make this multi tenant, how would I go about it? Well, first, I would create a tenant collection. Maybe that's sites or, properties or whatever we wanna call it. And then for each collection within this, I add a relationship, many to one, back to that site's collection, back to that tenant collection. And then within our access policies, you've got everything you need to basically scope access down. So you can filter based on the site, so that one tenant cannot read the, data from another. Now, there's an specific episode of 100 apps, one hundred hours where we cover this exact thing. So just look for multi site. That mission is season three episode two. You can follow right along with this and see how to set up a multi tenant CMS using Directus. Now to your other question. Right? How can I create pages or render within Directus? Now Directus itself is extremely flexible, and this is where our extensions come into play. And a good example of that, if we just go to directus.io and we look at our sandbox demo, there's a little extra module in here that is not, baked into directus core. This is just an extension that we built to give you a welcome page that does get rendered inside Directus. Now this is coming from a separate Directus instance, but again, all of Directus is modular and extensible. So this is actually a module extension. But if we just go to our docs, we look for our guides, we go to the extensions section, you can learn all about extensions. So you can customize the way that you interact with data inside the forms. Those are interfaces. You can control the way that data is displayed in the different layouts throughout the studio. That is a display. You have layouts themselves so you can control the listing of these items. You know, I think tables or kanban, views or maps, whatever. We can add panels to the dashboards and kind of the overall level where you've got total control over what gets rendered on the page, you have a module. So if you want flexibility on how things are rendered inside Directus, Maybe you wanna add some extra pages to it or you just want something, a blank canvas where you could totally experiment with whatever you want. Custom modules are what you're looking at. So, with that in mind, I hope that is enough to answer your question. Wireless. Wireless. Sorry. Again, I hate if I get it wrong. But that's it for this episode of Community Hopeline. If you want me to answer one of your questions on the next change log or or on the next community hotline, make sure you hop into the Directus community, community.directus.io, and I'll see you around. We want to take a moment towards the end of the changelog for thanking our community contributors who give their time to improve the director's project. Since last changelog, there have been 11 contributors. Thank you to Josh for fixing require selection check for manual trigger flows, Klayvo for adding a message property to the SDK error object, Amos for removing duplicate code in fields read all, Gerard for fixing a bug that was preventing translations from displaying in the calendar layout, Danton for fixing a bug that prevented pop ups from working in the WYSIWYG interface when opened in a drawer, Hughes for adding WebSocket authenticate filter hook, Abdullah for fixing the code tool to the WYSIWYG text editor and for fixing links in WYSIWYG missing underline and pointer cursor styling, Matt for adding TypeScript support for services within the extension context and for fixing an issue for empty states not being centered in RTL languages, Tim for standardizing batch mode for raw group fields, Gloria for enabling text selection in the studio, studio, Jens for adding the ability to override the email from property. Thank you again to everyone mentioned. You can see their specific full request inside of the full release notes on GitHub. Lastly, we also want to take the time to thank our GitHub Sponsors of July and August who financially contribute to Directus' development. A huge thank you to Weifan, Jens, Mike, Fergus, Omar, Marcus, Mission Control, Utomic, Steven, James, Nonlinear, Andreas, John, Wayne, Burb, Adam, Jason, Yuya, Vincent, c k, Valentino, and Hadi. The money we are given from our GitHub sponsors goes straight back to community members who build tooling and extensions for the director's ecosystem. Thank you again for being part of that. Alright. And that is it for the change log of September 13. Thank you if you are still with us and you've made it to the end. You're my favorite people. No matter whether you are new to this changelog or you've been returning each time, we hope to see you again. As we've said multiple times within this changelog, if you do have any questions, let us know. And, yeah, that's it. Have a great rest of your day, your week, and your month, and we hope to see you really soon. Have a good one, everyone. Take care. Bye.","77bf786f-8146-4b70-9b81-5ed46eb4333e",[682,683,684],"f8974511-d9e9-4907-9df1-9b5ba335564a","72df55fe-a262-4ba1-9794-bc73ab064378","43ed1773-7a33-4e11-b399-5742b57e74a1",[],{"year":399,"number":687,"id":688,"episodes":689,"show":697},53,"8d55b0f7-e337-475c-99c7-3b65612fbcff",[690,691,692,693,694,695,696,669],"b730c9d0-30fb-4eff-b4b6-5be61826c8c0","c14eb0dd-301c-412e-b15f-a81dfe7c1265","0ed97d3a-f55b-497e-a5c1-5812814a841e","89e3526f-dcfc-4280-96bb-126465f340f3","24ba631d-1e9f-4c47-b4eb-3f72e60dd0cd","7dd74ad6-eca6-4193-851e-8e4322847794","c181631f-45fb-4190-9f78-760fdf735bd6",{"title":297,"slug":298},{"id":699,"slug":700,"vimeo_id":701,"description":702,"tile":703,"length":12,"resources":12,"people":12,"episode_number":13,"published":704,"title":705,"video_transcript_html":706,"video_transcript_text":707,"content":12,"seo":708,"status":19,"episode_people":709,"recommendations":711,"season":712},"0d7df81b-38ad-4a28-add2-347887ad2fff","case-study-content-with-ai","1105040672","Matt turns a customer interview into a published case study in under an hour. Watch him go from raw transcript to live website using his method that cuts production time from 5 hours to 1. No more staring at blank pages or endless formatting! ","56abc428-f378-4ed8-963e-4edef2eb3557","2025-09-05","Shipping Case Study Content 5x Faster with AI","\u003Cp>Speaker 0: Hey. What's up, everyone? My name is Matt. I am a, marketer by trade. A few weeks ago, I posted on LinkedIn if anybody would be interested in me kinda showing off the process behind how I build case studies now with AI as opposed to how I used to write them, the old brute force classic way of actually writing them.\u003C\u002Fp>\n\u003Cp>You know, luckily, we don't have to do that anymore, and I'm able to create case studies at a much higher clip rate, much more rapidly as a team of one. So, what I figured I would do in this video is show you, kind of the four pillars, quote, unquote, of how, I approach case studies now. One, I'm gonna give you a just glimpse at the stack that I use for this. That includes all the AI tooling, some of the other tools like Notion, as well as, you know, my company is Directus, so shameless plug. I use us for our own CMS for our website.\u003C\u002Fp>\n\u003Cp>And being able to use the MCP has dramatically helped, the time to getting these case studies actually staged and published on our website. So I'll run through that. That should be really quick. Then I'll talk through some of the prework that gets done. So, there is a big part of this that is still a human component, which is, you know, AI can't run the case study for you, and I don't know if I would trust it to ever do that.\u003C\u002Fp>\n\u003Cp>So I've got a list of questions that I typically ask, and I've got a transcript I've already done. So, by the way, this will be me live building out a real case study that, has been on my list for a lot longer than it should have been with other stuff, but we're getting it done now. I'll also run you through some of the prompts that I use. You really have to prime the AI, to get it exactly how you want it to be. So we'll do that as well.\u003C\u002Fp>\n\u003Cp>Then we'll run through the actual content creation. It is extremely fast. There is a lot of editing and things that has to go into effect to make it actually, like, good and not super, like I'm trying to think of the phrase that AI typically uses, like, ever evolving, ever changing world of case studies. You gotta remove all that sort of stuff. So and then at the end, I'm gonna show you how I publish it.\u003C\u002Fp>\n\u003Cp>And this is actually the coolest part, which it takes no time at all with our new MCP. I still can't wrap my head around the potential with it, and I think you'll like it too. So make sure you stick around for that part as well. Just as caveat, like, anytime I watch these videos, like, I always find myself with a thousand screenshots on my desktop, so don't worry about screenshots. I'm gonna share a link to the notion that has, you know, this I don't know if it's a slide, but this picture, all of the prompts, all of the, like, even the case study questions I ask, and anything else that is relevant here, it'll all be there and available for download.\u003C\u002Fp>\n\u003Cp>So, I might gate it just because I gotta gate it, but, you know, we won't chase you with the SDRs. It's just nice to have couple emails so I can take it to the team and be like, look what I generated from this. Life of a marketer. Right? Anyways, okay.\u003C\u002Fp>\n\u003Cp>Let's jump into the stack. So, this is my stack. This is what I use. So for Notion, that's really replaced Google Docs for me. And I just like the layout, like the editing, the way that you can write in it.\u003C\u002Fp>\n\u003Cp>Just it just comes more naturally at this point. You can also type notion.new like you can in docs.new and get a whole new Notion doc. So convenient. For the call recording, which I've already done, don't worry. I'm not gonna record a call on this call.\u003C\u002Fp>\n\u003Cp>I use Evoma. Evoma is awesome. It gives you the transcript, which is really what you need from that conversation to prime, Claude so you can actually, you know, use it. That you can use any call recorder as long as it gives you a transcript, but, you need to have that part. That's kind of the whole reason of doing this exercise.\u003C\u002Fp>\n\u003Cp>Then I use perplexity for just kind of the organization, like, researching them. I like to do priming with, like, from an external tool, external AI, like, perplexity before I create the content because it just I don't know. I I just feel like it goes better, plus I just love spending $20 on every AI tool for some reason. And then for the bulk of the content, I use Claude, and I'll run you through actually how I prompt it and how I talk to it and yell at it and compliment it and all those good things on this on this video. And then at the end, shameless plug again, Directus.\u003C\u002Fp>\n\u003Cp>That's what we use. We eat our own dog food, as they say, for our content management. So all of the case studies and all the blogs and everything we have on our website, that all goes through Directus. And I really wanna show you the MCP, just because I it still blows my mind. Cool.\u003C\u002Fp>\n\u003Cp>So to start, case study questions. Right? Like, you this is the only part that will always be manual. There's no AI involved here. These are the questions that I ask.\u003C\u002Fp>\n\u003Cp>I take into every case study call. Obviously, it's not like a q and a session. The way that I treat these case study calls is more of just like a podcast, like conversation. These are really just like boilerplate, kinda give a road map of the actual conversation. But, what I find is they typically go, you know, off tangent in places, and that's when the best stuff comes out.\u003C\u002Fp>\n\u003Cp>So this case study that I'm actually working on is with one of our, you know, really good, really big clients, FEG. They're a, Fortuna. They're like a gaming, iGaming slash casino, like, online gambling sort of company. So, that's what this video will be about. Like, super meta is me building that case study for Directus as a marketing tactic for Directus, I guess, is what this is.\u003C\u002Fp>\n\u003Cp>But, really, I think this will be valuable for you. So, the you probably have case study questions. If you don't, like, I will link these in the actual notion that will be part of that. This getting so meta already. But you'll have access to these.\u003C\u002Fp>\n\u003Cp>You can just replace, like, how are you using Directus today with your own company, and, you know, customize it as needed. So with that in mind, let's hop over to the first bit of prompting in the prework, which is, finding out information about the company. So, this is, like, the easiest part, which is, I just say, like, give me a comprehensive summary about FEG or whatever company you have interviewed. And why I like perplexity is I feel like it has the most comprehensive, like, online search, and it really gives, like, a really good overview. And, you can dial in a little bit more if you want to get more specific about things.\u003C\u002Fp>\n\u003Cp>These are none of the FEG. FEG. Let's see. FEG. Right.\u003C\u002Fp>\n\u003Cp>Like this. So there was a bunch of FEGs out there. So I'll just say Fortuna, specifically. And it is not Fortuna mining. Yeah.\u003C\u002Fp>\n\u003Cp>Just waiting for this to go. We'll edit this bit out. We'll start over. Alright. So let's hop over to Perplexity for our first step in our prework slash prompting.\u003C\u002Fp>\n\u003Cp>I use Perplexity, because I feel like it is the most comprehensive search for a company. I don't really need, like, the deep research, like, a 100 pages of research. Like, I just wanna get really a good brief overview of the company, you know, what they do, just to give context to the content as well. So, what I'll ask is I typically just ask, like, give me a comprehensive summary about blank. And then, actually, what you can do is come in here.\u003C\u002Fp>\n\u003Cp>Fortuna. None of those. Fortuna f e g. Here we go. Fortuna Entertainment Group.\u003C\u002Fp>\n\u003Cp>Really awesome website, which I think is actually powered by Directus. Wink wink. But, yeah, I'll just do that, feg.eu, and I'll let it, just give me the overview. It gives really good, like, core activities. I like how it does, you know, the milestones here as well.\u003C\u002Fp>\n\u003Cp>This is all really, really good prompting, primer, I guess. Prime priming for the prompt. I've gotta figure out how to say that the right way. But, and a nice little summary here. So easy peasy.\u003C\u002Fp>\n\u003Cp>I think this is perfect based on what I know of them, in that conversation. This is exactly, like, what I would need. So I just copy and paste that over in this other Notion doc I've got on this other page. And then next, I am gonna hop over to the actual Claude instance I've got. So I have hopped over here to, Claude by Anthropic.\u003C\u002Fp>\n\u003Cp>I use this for pretty much all of my content writing. I don't know. I just feel like it is it just writes better to me. I've yet to use Grok. Some things I use ChatGPT for.\u003C\u002Fp>\n\u003Cp>I feel like that's gotten a lot better recently as well. I don't know. These things it feels like they change quality wise, like, every week or so, and it really depends on when you're doing it. They could be at, like, high bandwidth and just the output if you use them as much as I do, you will start to realize, like, this for this and this for that and that sort of thing. But, for long form content, I really do like Claude.\u003C\u002Fp>\n\u003Cp>Okay. So where to start? Right? For the initial prompt, what I like to do is give it before I give it anything about the actual case study or or who I wanna write about, I like to do this prompt, which is kinda priming it on the background of what I want from it. So the way that I've structured this is, like, you know, you're world class class copywriting expert.\u003C\u002Fp>\n\u003Cp>It really loves those, like, role play type of things, and that specializes that interning interviews and case studies. So, I'm gonna take the transcript after this and upload it, but first, I'm gonna prime it with five case studies that I already have. I think you can do three. I just do five just to cover all the bases. And this is really important here, which is, like, don't start doing anything until I confirm with you, that it's time to do that.\u003C\u002Fp>\n\u003Cp>So, really, all of these prompts are just gonna end with, like, the assistant coming back with, like, I I confirm. I understand. We'll see. Yep. Understand exactly.\u003C\u002Fp>\n\u003Cp>And to be honest, what I think I'm gonna do is actually try to turn this into the style I want as concise because I wanna use fewer tokens here. It can get, like, really verbal and really wordy, and I think that's why, like, the new Grok is really good is that it's just very concise with its answers, and you use less tokens when it has to refer back to things. So quick little pro tip. Cool. So it's asking me to share those examples.\u003C\u002Fp>\n\u003Cp>I picked these just because I really like kind of the way that they're framed. So I've got this one, which is mighty HQ. I've got a COPPA case study, which I think turned out really well. Actually, you know what I'm gonna do is you could upload these as text, but what I will actually test and see if this is just as good is pulling the actual URLs here because I do believe that Web search is connected. Yeah.\u003C\u002Fp>\n\u003Cp>So, I've attached two. I will share links for the others. So Prusa is our most recent one. That's the one I really like the formatting style of, and I would probably use that. Ripley is another good one.\u003C\u002Fp>\n\u003Cp>And, let's see. Yeah. Copa, that one. And I'll use Lucid as the last one because I really like this one as well. Cool.\u003C\u002Fp>\n\u003Cp>And if you're starting from scratch, like, you don't need to do these at all, but, these, I think, give it a really good sense of, you know, how we've done this before. They understand the narrative and, like, how we tell the story and that sort of thing. So, depends on, like, how you like to do your case studies here. If you don't have any, then just you can prompt it with, like, this is what I want the output to be. Yeah.\u003C\u002Fp>\n\u003Cp>I I I prefer to tell kind of the the stories and and that sort of thing. So, excellent. So it's read them. It understands, from those what everything should be. Let me pull up my next prompt here.\u003C\u002Fp>\n\u003Cp>And, actually, before I upload the transcript, I wanna drop this one in here, which is, you know, what are the common characteristics? Like, what do you think makes them great? And the tone of voice. It touched on it a little bit, but I really wanna, extract that from those so it understands, like, this is exactly the common thread of all of these case studies we've done, like, the the voice, the structure, and all of that. So it's it's fetching all that now.\u003C\u002Fp>\n\u003Cp>We'll see. Yeah. Classic narrative, pain point, search, the implementation, and the transformation. We do have kind of a conversational tone. You know, I I don't like to be super dry, but totally up to to you how it it really depends on who you sell to.\u003C\u002Fp>\n\u003Cp>Right? Like, we primarily work with developers, and they don't really like all the jargon and talk and all that stuff. So, this is another thing also is, like, I am a former journalist, so I love to pull out quotes, and I prefer the interviewee to tell the story as opposed to us telling the story on their behalf. So, classic journalism is like, you know, it's full of quotes, and you just kinda connect the dots in between, like, little like, a little puzzle. Right?\u003C\u002Fp>\n\u003Cp>And just make sure everything kinda makes sense and flows well. So, great. So now is when I feel good about that. What I'm gonna do is upload that information that we pulled from Perplexity here so it understands who we're writing about. It's a lot of copy and pasting.\u003C\u002Fp>\n\u003Cp>And one day, we'll have agents that replace this whole process, but, and I'm sure there's some purists out there that aren't too, like, fond of this, which I totally understand. Right? If I had time in between, you know, all the other work stuff to, like, sit down and build, like, really good human written case studies and all the research and all that, like, I would love to do it. But I just feel like I don't really have the time anymore, and I think this is a great way to do to supplement kind of the the work that you're doing. So, excellent.\u003C\u002Fp>\n\u003Cp>So it understands, the style, you know, who we're writing about, which is, FEG. And now I will share the transcript, which I do not have offhand, but I am going to, go grab that really quick. And this is another one of those, like, I want you to read it, get a feel for how we've worked together, you know, their initial intentions and needs, and anything else you think is relevant to create kind of a unique narrative arc that we wanna tell. And, again, I like to end it with, like, just make sure that you've analyzed it. Like, don't start doing anything yet.\u003C\u002Fp>\n\u003Cp>So we'll see how long this takes. Confirmed. Loved it. Yeah. So there.\u003C\u002Fp>\n\u003Cp>You pull out already the challenge it has and really all of the metrics, that we wanna tell and really highlight here, which is, you know, five to six hour deployment times, you know, six months in advanced promotional content. It had to be planned. Our contact, Darko, when he found us at the conference and what he really liked about it and the things that stuck out to him, the transformation, which is, like, the big, like, whatever. I forgot. I used I took a theater arts class back in the day, but the best stories have that, that moment of transformation that, you know, Luke Skywalker and the swamp.\u003C\u002Fp>\n\u003Cp>Yeah. I don't know. I'm terrible at analogies, but you understand, I think. Anyways okay. So now it's read it.\u003C\u002Fp>\n\u003Cp>It understands it. It's tell me what it's learned. So I'm actually gonna skip the part where I say, like, tell me what you've learned because I think it's done a great job of that already. Now before we dive into the content, I wanna start with an outline. So using the previous case studies I've shared, like, as a reference, give me an outline for how we can structure this, which I think it sort of had it in the previous one.\u003C\u002Fp>\n\u003Cp>But the way that it creates, like, an artifact here is just much easier to to work with, and, like, just start working in the artifacts themselves. Yeah. I mean, couldn't have said it better myself, I guess. Perfect. Challenge, the implementation, the results, you know, future plans, kind of a a wrap up of, you know, why they chose Directus and then a conclusion of what all this means.\u003C\u002Fp>\n\u003Cp>So amazing. My next prompt. And the next few prompts are really just gonna be me giving it a little bit more information of what I want as the output. So this is a really big one. I will just copy this over, but I'll talk through this a little bit because there's a lot of stuff in here.\u003C\u002Fp>\n\u003Cp>Alright. So nice little compliment, which everybody loves. I don't have to add spaces here. But, yeah. So the way that I write the case studies is that I don't have it spit out everything at once.\u003C\u002Fp>\n\u003Cp>I like to do it section by section, based on kind of the previous ones we've done. I like to do the journal journalistic style, which is, you know, line breaks for every quote. I don't like to have quotes on the same thing as, you know, paragraphs, that we write on our side. I wanna keep paragraph short, easily scannable, and readable, so three to four sentences max. This is the big one.\u003C\u002Fp>\n\u003Cp>And if you wanna just take this out of this whole video, you're more than welcome to do it. But I love to avoid the AI terminology that seems to seep into all of these, which is, like, words like, delve and ever evolving and revolutionize and streamline. I I just I cannot stand those words or when companies use those words. They just feel like filler to me. They're not really, like, powerful.\u003C\u002Fp>\n\u003Cp>So, like, where you could use the term use and you use the term utilize instead, like, why? Why don't you just say use? Right? Like, talk like a normal person having a conversation like I'm talking here. So, and then I have just these that I've built over time, which is, like, start with the broader industry context, you know, use the short things, incorporate a lot of quotes in there, specific challenges, briefly explain if you do mention technical features, avoid again, I like to double down.\u003C\u002Fp>\n\u003Cp>Like, avoid the buzzwords and the hyperbole. And don't this is a big one. Like, don't make claims without the supporting evidence or examples. We're trying to cut down on the hallucination. It still works its way into this, but trying to nip it in the butt here.\u003C\u002Fp>\n\u003Cp>But that's what the editing is for at the end anyways. So, yeah. No. I think I'll add this at the end just to see if there's anything else that it recommends in addition to these. Yeah.\u003C\u002Fp>\n\u003Cp>Love that. Practical business benefit along with those, like, technical specifics, and really emphasizing the pain points. Perfect. Now this is where we actually write it. So let's start with the introduction.\u003C\u002Fp>\n\u003Cp>Now that we've done all this, right, that that's most of the work is getting the priming and the prompting right. I can already tell, like, I'll probably have to edit the intro here. Seamless worked its way in there. It is what it is. But, yeah, the, the tone here isn't ideal for me, which is okay.\u003C\u002Fp>\n\u003Cp>Right? Like, I think the benefit of this is I'm not staring at a blank page and having to write it from there. And we'll come to that at the end when I do the editing and stuff. But, Yeah. Here, I would say, great.\u003C\u002Fp>\n\u003Cp>Next section. And then we'll just do this, like, ad nauseum till the end. Right? Because it already has the outline. It knows what we're gonna write.\u003C\u002Fp>\n\u003Cp>I love how it's adding in, you know, the actual quotes from the case study we ran. Yeah. Great. Next section. So we did the challenge.\u003C\u002Fp>\n\u003Cp>The next section, if I believe yeah. That's when they came across Directus, kind of the story about that. I I don't know why, but I just love more of the stories than I do just like the here's the x y z benefit. Here's what happened. That sort of thing.\u003C\u002Fp>\n\u003Cp>I don't know. I'm just kind of a story nerd, I guess. Great. Next section. And, like, I wanna reiterate, like, I'm not gonna copy and paste this into the wet or our CMS and just hit publish.\u003C\u002Fp>\n\u003Cp>Right? Like, there is a lot of editing that's gonna have to go into this still. But it's just like having, like, an outsourced content writer that I'd find through, like, Fiverr or something that gives me kind of the baseline. And then it's a lot less work for me to go in and and edit and move things around and change things, add quotes, take out quotes, that sort of thing. And the other reason I like doing it section by section is, when you do it altogether and try to get it to spit out this, like, really big piece of content at once, like, it tends to override itself and, like, mention things it's it already has mentioned.\u003C\u002Fp>\n\u003Cp>When you're doing it section by section, it's taking each previous section into context instead of, like, trying to write it all at once. And I found that this, like, this is gonna be a call out quote, I think, for this, like, in the, like, the marketing and and, you know, things that that we'll post out on social, which is, like, you know, one of the or even the best rollouts in Fortuna history. No outages, really smooth, straightforward. I mean, come on. That's that was dark, though.\u003C\u002Fp>\n\u003Cp>That was an incredible quote, and it really depends on the input you get or the output you get from a customer to put into here, the quality of this that comes out. So, again, reiterating that initial customer conversation and making sure you have that good podcast like conversation with them really pulls out this this stuff. So, cool results. And I wanna say we got two more sections for this, if I remember correctly. Nice.\u003C\u002Fp>\n\u003Cp>I know this isn't the most thrilling thing to see, But now we're kinda talking about the future, which most of our well, all of our case studies kinda end with, like, you know, this is the problem that's been solved. You know, looking ahead, how can you maybe deploy Directus in in future chats or something? Just kinda keeping the conversation going. And then we've got the final section. And then just a nice little compliment so far.\u003C\u002Fp>\n\u003Cp>Because why not? Right? If they do end up taking over the world, I think they'll remember when I said great job and gave them a smiley face. So now that we have this, I feel good about it. Right?\u003C\u002Fp>\n\u003Cp>Like, I I think this is so far just, like, kinda breezing through it. It looks good. Like I said, there's gonna be some heavy edits. You know? Maybe the intro is gonna need some changes.\u003C\u002Fp>\n\u003Cp>One thing that you definitely need to do is verify the quotes, and the hallucination typically happens on the quotes themselves. In one of my prompts earlier, I think I was like, you know, double check. Like, don't I forgot what I had said, but, you know, don't make things up. Like, make sure you have, context and everything you have for said case study. But I like it.\u003C\u002Fp>\n\u003Cp>Now just to knock out some of the other additional stuff before I go to the edit is I wanna get sort of the metadata and the title done. For this one, like, I really like how it did for our Ripley's one where we caught out the metrics, short description using the jobs to be done framework. And then I will top it off with, like, let's get a good title for this five killer titles, like, make sure we add a metric in there, make sure it's a scroll stopper. And, I mean, legacy CMS held to 70% speed games is pretty badass, but I don't know if we can actually post that on our website. Maybe we can.\u003C\u002Fp>\n\u003Cp>No. These are all good. Like, I I would probably lean towards one because it calls it out, who it is, you know, what the actual thing was, that they achieved, like the big metric showstopper banner callout, and then also across five European gaming markets is pretty big. But these other ones could be useful in other situations. Maybe, you know, if we do some redistribution of this content.\u003C\u002Fp>\n\u003Cp>But, yeah, I'll say let's go with one. Excellent. So I have got my case study now. Next, I'm gonna hop back over to Notion, and then I'll copy this here. I'll move this kind of older title.\u003C\u002Fp>\n\u003Cp>Yeah. Too big. Turn that back to text. Yeah. You like, this is just thick paragraphs that I wanna avoid, but, bam.\u003C\u002Fp>\n\u003Cp>Killer title. I've got my subheader here, learn how Fortuna Entertainment Group used direct us to achieve. I won't call out the metric here. I'll just say faster content deployment, unify web and mobile management, and deliver one of the best rollouts in company history. That is solid, and I'm just gonna italicize that.\u003C\u002Fp>\n\u003Cp>Cool. Alright. So this is the human component. Right now, we have a case study that is workable with all of this information in it. And I'm actually gonna stop recording, while I go through and edit this.\u003C\u002Fp>\n\u003Cp>This will probably take me thirty to forty five minutes to do it. I also wanna double check to make sure that the quotes are right. And if they aren't, then I will use that same cloud prompt we have where it has the context of the conversation, and I'm gonna go back and tell it, hey. Like, double check. Like, I think some of these quotes are wrong.\u003C\u002Fp>\n\u003Cp>The previous time I did this, there was probably three or four direct quotes that weren't right, or just it had kinda mixed up some of the different quotes together. But, yeah, it's it's helpful if you, like, reprompted. So I'm gonna stop sharing. I'm gonna pause this video, and I'll be right back with the final version. Alright.\u003C\u002Fp>\n\u003Cp>So I've done quite a bit of editing. That was actually quicker than I thought. Took about ten minutes. I'm actually blown away by the output here. I just wanna give you kind of a a scope of, you know, what does it look like when I edit it and, like, after I've done edited and, you know, when I go to, the next step, which is getting it ready for publication.\u003C\u002Fp>\n\u003Cp>How I like to operate is what I'll do is go through and just loosely format it a little bit, like, how I like it to look. It's just easier for me. So we have that great, title that it spit out, and then I've got kind of the subtext. This will be included, like, on the, you know, the the OG image and stuff, like, when you share it out. I ended up liking I know I said that I wanted to take this out, but it's kind of a cool intro.\u003C\u002Fp>\n\u003Cp>Right? Like, I think it reminds me a lot of, like, kings and, like, cards and that sort of thing. So, decided to leave that in there. I a lot of the work was breaking up a lot of the, paragraphs, so they weren't just, like, big blocks. We've got I try to do, like, rhythmic when I do writing.\u003C\u002Fp>\n\u003Cp>So there's, like, some lines with one line and then, you know, some shorter sentences and that sort of thing, which was actually surprising as opposed to last time, what really stood out to me is that these quotes are actually, like, literally from the transcript. So, like, it it even has, like, some of the like like, Darko, you know, is not like you know, he he speaks very good English, but, you know, there's some sort of broken English in some of these. And there's a lot of, like, you know and, like, that sort of thing. So it literally pulled out the exact quotes, which is awesome. And now, you know, I like, this first one.\u003C\u002Fp>\n\u003Cp>Right? I like to just go in and kinda clean them up. I'll, add, like, specific context to, like, the journalistic style of doing it that way, just to clean them up a little bit, make them easier to read and, like, just better in the context of where they're at in the story. But and the the really the only changes I made were just, editing some of the things. You know, I took out a few sentences here and there.\u003C\u002Fp>\n\u003Cp>I will reexplore these, like, chapter headers, because I think they can be better. I took out specific references to, like, other tooling or, like, you know, the tools that were getting replaced. I just don't want it's, like, petty to have that stuff in there. So, replace it just with, like, legacy, the platform providers and that sort of thing. But, overall, like, I I think this is great.\u003C\u002Fp>\n\u003Cp>And so next steps for me from from now that I've got this edited and I've got all of the quotes pulled out here, I highlighted all the things in red that I do wanna go back and change and tweak and clean up a little bit. But other than that, like, I mean, this is solid. So what I'll probably do is take another ten minutes and just kinda formulate some ideas for these headers, clean up some of these quotes, and, yeah, then we'll start the exciting part, which is actually getting this published. And that actually might be the quickest part of this whole video. So, be right back after I do this final cleanup here.\u003C\u002Fp>\n\u003Cp>Alright. I'm back. I'm really, really happy with how this case study turned out. You know, all in all, I started about an hour ago, which previously, this would probably take me, like, you know, four to five hours to do. But, I was getting couple Slack pings I jumped over on.\u003C\u002Fp>\n\u003Cp>But, overall, like, this, I would say, is actually our one of our strongest ones when it comes to case studies. Yeah. So just quick touch on some of the things that I edited. So, went through, really smoothed out those quotes so they make sense in the context of of the narrative slash story. I added a little bit more of, like, a narrative driven header here instead of, like, the classic, like, the challenge, the thing.\u003C\u002Fp>\n\u003Cp>Something that's more like, I don't know, click baity maybe. But, yeah, I I feel good about this the story that's told here and kind of what the resolution is. But yeah. So now that we have this done, I'm super happy with it. So next step is just getting this loaded into the actual CMS.\u003C\u002Fp>\n\u003Cp>There is a interesting part here. So previously, working with any I've I've worked with, I've worked with WordPress. I've worked with HubSpot. I've worked with, a couple others that I don't know if I can name, but you probably know them, before I started working here at Directus, and Directus kinda became the defacto tool that we use for our website and all that stuff. But, before even just using Directus, right, there has always been a pain for me when it comes to, like I just spent, you know, an hour previously before I had AI.\u003C\u002Fp>\n\u003Cp>It would take a lot longer to do this. But, I mean, this is an hour worth of work, like, going through doing all the editing and all that stuff. I I'm in a really happy place with it. And this would be the part of the story where I was just like I feel like, oh god. I hate this, which is actually getting this into a, CMS to get it published.\u003C\u002Fp>\n\u003Cp>And that's because, like, I just hated doing more copy and pasting over to the actual document. You know, just it was it was pain in the butt. Always has been. A few months ago, I wanna say May, my coworker, Bryant, you know, was he'd been reading about MCPs, which if you don't know, it's model context protocol, which is I I think that's what it is. But, essentially, what it is is, kind of an API that connects to your AI or your cloud instance that can be, like, connected with any any tool you use is if that tool has, like, an open API or connector.\u003C\u002Fp>\n\u003Cp>Right? So what he did is he built a MCP for Directus that connects directly to cloud. Originally, the intention, I think, that I that I'd expected out of it was, you know, just something that could generate content because that's what, you know, AI does for marketers. It's like it generates content, and then you go edit it. What I did not expect was it to actually build out the pages for me if I handed it over the content.\u003C\u002Fp>\n\u003Cp>And I wanna show you live in this call because it just blows my mind every time I use it. So, now that I've got this really solid, case study, super happy with it. I am actually going to I'm gonna stop sharing for a second to make sure I can get all of my cloud instance and get my login and all that stuff for Directus, which I'm just gonna hop into now. So if you've never seen Directus, I will show you in one moment. But yeah.\u003C\u002Fp>\n\u003Cp>So let me show you how we have it set up at least so you can get kind of an idea of what this looks like. So, here in Directus, we've got all of our, resources. So, like, each one of these tabs is, like, just something different that we've either experimented with or we've been building and and testing. There's a lot of, like, just really cool stuff we can do here. But to give you an idea of how it's laid out for our, resources, like case studies things, is, all of our blogs and case studies come from this single collection, which is resources, and this is how we've structured this.\u003C\u002Fp>\n\u003Cp>So for our recent SOC two announcement, which hooray, very official, the when I inputted and made the blog slash, you know, PR announcement, this is how I I built it out, basically. You know? Just like title slug. If if you use WordPress or anything like that, you probably know what this is, from that understanding. But, yeah, and then we have a block editor.\u003C\u002Fp>\n\u003Cp>Right? So, like, we have these prebuilt blocks that we go in, and we can add, like, header, you know, call to actions. We can have, like, pop outs, which we call block paper internally, separators, dividers, all that stuff. So what it actually comes down to and I'll I'll go to the last case study we did, which was Prusa. What I really like doing is creating and I'll hop over to direct us here so you can see from the front end what this actually looks like.\u003C\u002Fp>\n\u003Cp>I need to go to our case studies. And I like this format because, like I said earlier, like, I I really like to involve quotes and have, like, the customers actually tell the story as opposed to us. So I added a lot of these blocks in in between. How I add it's nice to have images and things in here too just to kind of break up the monotony of the text. Just makes it I I I just think it looks better.\u003C\u002Fp>\n\u003Cp>It's just easier to read. So we had the actual customer case study quotes in here, which felt good. Right? Like, I I think this is solid. It's it's a good narrative.\u003C\u002Fp>\n\u003Cp>The pain in the butt was actually creating the different, quote slash testimonial blocks here because, from the back end, what it looks like is, like, I had each of those quotes as a different block, and then I had, the rich text kinda in between. So this was pre MCP life. This took me, like, longer than it should've, to build it out, but that's okay. Right? Now with the MCP, like alright.\u003C\u002Fp>\n\u003Cp>I'm gonna show you. I'm gonna make sure I've got all my ducks in it right here and make sure I pull up the right thing. Okay. So I have a project here, which is Direct to SMTP. Like, this is so if you've ever used projects in cloud, it's like you can basically, like, have a set, like, like, knowledge that you give it.\u003C\u002Fp>\n\u003Cp>So for this one, I put in this prompt of, like, you you know, you're a world class marketing content, technical professional, because I use this a lot for, you know, just getting the content. I can create landing pages really fast, that sort of thing. The this gives it the context that it's working within Directus. And then what I would do is it's connected. So, I will take like, I'll say I want to create a new case study with the same format slash layout as our last one, which was Prusa three d.\u003C\u002Fp>\n\u003Cp>It is in the resources folder or resources collection. Can you please review that and let me know when you have understood the context and layout. I'll just reiterate that. So this should be connected, I think, hopefully. Yeah.\u003C\u002Fp>\n\u003Cp>Alright. So now it's prompting the system. Right? It's going in, and it's it's getting what I had told it. It's examining the three d that's actually within Directus.\u003C\u002Fp>\n\u003Cp>So it's understanding the format. It's going through and reading all of the blocks that exist there, and gonna get, like, an idea for, you know, how to actually structure this new case study now that I've got all of these, you know, quotes in here. And we'll Looks like it's still looking to find it. Ah, found it. Perfect.\u003C\u002Fp>\n\u003Cp>Okay. Which is just awesome, by the way, that it goes through and it actually thinks and keeps you updated, like, as it's trying to find it. This is a read option thing. It's looking at the specific blocks, which is how it was built. Now it understands case study structure like I showed you.\u003C\u002Fp>\n\u003Cp>Right? It alternates between that block quote and that block rich text. So exactly how I wanna lay this out. Yeah. Now it's ready to create a new case study.\u003C\u002Fp>\n\u003Cp>So what I'm gonna do is come over to where I've got my case study written out. I'm going to hop back over here. Perfect. Here's the case study information. So I'm gonna have title is that awesome one we had figured out.\u003C\u002Fp>\n\u003Cp>I'm gonna have subtitle, which was this, And then I'm just gonna drop in the actual I'm copy and pasting this whole thing. Here's the case study content and then copied it. Alright. We're doing this on the fly. Like, f it.\u003C\u002Fp>\n\u003Cp>We're doing it live. Let's see what happens. So far so good. I mean, it understands what the context is, and it's exactly what I showed you. Now remember, like, when I showed you in the resources, there there is no existing thing for this.\u003C\u002Fp>\n\u003Cp>Like, I didn't even create a new case study, like, placeholder or anything. This is gonna do all of that for me. So I can literally just sit back and, I don't know, watch Netflix for five minutes. If anybody from my company is watching, I don't actually watch Netflix. But, if you if that was your prerogative.\u003C\u002Fp>\n\u003Cp>You know? If it's late in on the weekend, you're getting some weekend work done, you can go do whatever. I'm just chugging Red Bulls right now and getting hyped for Friday evening, which is, like, I yeah. I don't know. I, like, I don't know what to do with my hands now at this point, like Ricky Bobby, while it's going through and creating these blocks.\u003C\u002Fp>\n\u003Cp>Right? So it created the new resource using a create item thing. What will probably have to I I haven't uploaded any, like, media. Right? So I need to find Darko's headshot.\u003C\u002Fp>\n\u003Cp>I need to put that in there. We have an FEG logo in there, which I'm really curious to see if it's gonna be able to find it. I believe it's labeled right in the files, but, I'll also need to create, like, a primary banner image, which is, like, our typical, like, Directus logo plus the logo of the person, or the logo of the company that is the main feature, as you'll find is, like, our typical format for it. But yeah. Awesome.\u003C\u002Fp>\n\u003Cp>So it's alternating between the blocks. Right? So, it created the initial resource. It's creating all the remaining blocks, rich text. There's the content that I had written.\u003C\u002Fp>\n\u003Cp>I love that it's doing, like, the formatting already. So all of the h twos that I had pulled in, the different paragraphs, still creating the blocks. I mean, I I could do this all day. Like, I almost feel guilty guilty, like, it's doing this, but, if I wasn't on camera here and talking, like, I would probably be back in Slack or I would be in another tab doing something else while this happened. But I just have to talk to fill up the space, which is so weird, like, doing that.\u003C\u002Fp>\n\u003Cp>I don't know how my coworker, Bryant, does it for all his videos, which is a nice little plug. Like, go to directis.0.i0\u002Ftv, watch 100 apps hundred hours. He does this, but he builds actual applications out, which is just unreal to me. But, yeah, I am, I'm interested to see how this pans out. Looks like it's still going.\u003C\u002Fp>\n\u003Cp>It was a lot of content. Let's see. Couldn't connect. Lost network connection. Well, that's interesting.\u003C\u002Fp>\n\u003Cp>Okay. Well, I know that it did create some stuff in here. So what I'm actually gonna do is go back in to the Directus instance and see what we've got. So this was our previous one. If I refresh let's see.\u003C\u002Fp>\n\u003Cp>It should have created the resource. Interesting. You know how things go when you just create new stuff. Right? It's never perfect.\u003C\u002Fp>\n\u003Cp>Alright. Well, didn't overwrite any of that, which is solid. We have it set to so it doesn't delete or overwrite anything, but okay. Well, not as perfect as we thought. Of course, it's not gonna go perfect on the first go around.\u003C\u002Fp>\n\u003Cp>You know how that goes. Right? Okay. All good. Let's try again, see what happens.\u003C\u002Fp>\n\u003Cp>I'll share something else. I'll come back here, and we'll try again. You know what might have happened? And I noticed that there was maybe somebody else in there. Maybe something to look for, which is interesting.\u003C\u002Fp>\n\u003Cp>But in any case, you know, it's finding a placeholder. Reusing the case study image. Resources created. You know, it'll build the alternating block structure. I'm having deja vu.\u003C\u002Fp>\n\u003Cp>You know? Some things just don't go perfectly the first go around, and that's okay. Still, I am about two hours saved at this point of just doing the formatting and that sort of thing. So, yeah, we'll see if it works this way. Continue building the blocks, creating several blocks at once, and then linking them to the resource.\u003C\u002Fp>\n\u003Cp>Let's see. Well, I'm gonna let this run. If it messes up again, I will come back. But when it's done, instead of me talking to fill dead air, I will, just skip forward to the good part. And I'm back.\u003C\u002Fp>\n\u003Cp>And it just took a second time. Right? Like, I don't know if anybody here remembers the old Nintendo games or Sega games where you had to just, like, got dusty. He had to blow it out, just blow on the cartridge and put it back in. That is basically what happened with my Claude instance.\u003C\u002Fp>\n\u003Cp>So, yeah. So the second go around, I haven't actually checked the actual thing yet, but based on what I'm seeing here, says it's created it. I went through and just double checked, and it looks like the the block quotes were correct. The structure that it added all in there, there was 15 alternating blocks based on the rich text and the quotes. All the quotes were from Darko.\u003C\u002Fp>\n\u003Cp>Again, I'll probably have to upload, his assets and the other thing. But cool. So I'm gonna go back to our marketing instance for. Share something else. This has been totally live, by the way.\u003C\u002Fp>\n\u003Cp>I have not, other than a little bit of prep here and there, this has been yeah. So what I'll do is add date published. I'm gonna sort by descending. It's not actually published yet, so I need to double check where that would be, which is probably why it's not in here because it's not actually published. And open that.\u003C\u002Fp>\n\u003Cp>Open the link. Let's try this. Fingers crossed. Nice. Okay.\u003C\u002Fp>\n\u003Cp>Well, it's here. It's in resources. It looks like date published was back January 18, so it pulled the same publish date. So I'm gonna set this to now just to have it updated to, be at the top of the list. 70% faster gaming, content deployment.\u003C\u002Fp>\n\u003Cp>Actually, I think this title is wrong, which the title was supposed to be how Fortuna Entertainment achieved. See, can't trust AI for everything. You know? That's actually a really long I'll say FEG. Shorten that title a little bit.\u003C\u002Fp>\n\u003Cp>You know? Changing it on the fly. Let's just make sure our, yeah, summary, change that. It's case study. It's part of the category.\u003C\u002Fp>\n\u003Cp>The slug is how we do it. It's just what the company's name is part of the slug, and use this placeholder for the image, which I'll update. And then as we go down here, looks good. Right? So I think there's a few little missteps here where there's a couple quotes down here that will probably just have to get added to the right spot.\u003C\u002Fp>\n\u003Cp>But, you know, if I go in and, I'm gonna save this as it stands just to get an idea for what it looks like. I'm actually gonna use our live preview here to see what this looks like. Alright. It's up. So we've got how for twenty entertainment group achieved 70% faster.\u003C\u002Fp>\n\u003Cp>We have just a placeholder image here. Yeah. It exists. Subdued sort to. I'm gonna have to make a few changes to this just because for some reason they're not showing up here.\u003C\u002Fp>\n\u003Cp>But in any case, doing stuff on the fly. So the MCP did not go off as exciting as I hoped it would. But just from the perspective of, like, hey. This is this content's in here already. Like, I didn't have to create these new blocks.\u003C\u002Fp>\n\u003Cp>I didn't have to copy and paste this stuff in here. Now it's just going in and ensuring that this is, like, built the right way. I'm just gonna refresh real quick. Looks like the same here. And if I actually pop this image out yeah.\u003C\u002Fp>\n\u003Cp>Image can't be null, so I'll have to pull one of our images from the library. We'll use this placeholder, see if that changes things for a live preview. Alright. Well, I'll have to go investigate this, obviously. But like I said, typically, this works out a lot easier than it does here, a lot cleaner.\u003C\u002Fp>\n\u003Cp>Every time I've done it, but when I'm not recording, it happened, it happens to go off really well. So, it's just because I'm recording, because you're watching, and I was trying to hype it up. But like I said, any case, it's just really cool, I think, that we have been able to capture all of this case study content in here. It has reduced the the need to go through and actually, like, copy and paste that content over. And, yeah, now there's just a little bit of cleanup left and gotta create the assets, and I gotta, like, double check and make sure that it's not a quote that's, like, kinda throwing stuff off.\u003C\u002Fp>\n\u003Cp>And I think we're we're good to go. I solved the problem. Alright. So I figured out what it was. So what had happened was, the initial run of the MCP when it was creating that that first batch of content, it actually created, like, two thirds of the entire thing.\u003C\u002Fp>\n\u003Cp>So when it stopped, that was still associated with a instance, and it still had the same slug. So there was a small bug slash error on my part where I didn't recognize that there had been two versions of the same instance built up. So, all is well that ends well. You know, after realizing the issue, what I did was, delete the initial one that was created, and that's all I needed to do. So I came back in, and here's the content.\u003C\u002Fp>\n\u003Cp>It's all here. Amazing. It's got our quotes. I'm gonna have to obviously clean up because I had quotes in the original one, and our quote template has this here. But, yeah, the case study is here.\u003C\u002Fp>\n\u003Cp>Amazing. So I'll just have to readjust some of the blocks. I noticed that some of them, you know, are are out of order, but that's totally fine as long as I don't have to create them from scratch. One of the cool things I think I'm gonna do now is I'm gonna add the FEG logo here, as part of this. So I'm gonna come back here, and I'm gonna say, amazing.\u003C\u002Fp>\n\u003Cp>For the quote testimonial blocks, can you find the FEG most recent G logo we uploaded and add that to all of the blocks, please. Now we're getting tricky with it. So instead of me going through and manually adding each one, and I if this works, hopefully, it does, and we get it correctly added. So found two. We'll use the first one, and update all the quote blocks with that company logo efficiently, just so you know.\u003C\u002Fp>\n\u003Cp>It's doing this concisely and efficiently. Great. So while that is cooking, I'm just gonna keep an eye here. I'm gonna go over. I'm gonna adjust these images, but I'll have, this done at, you know, 04:45, 05:00 on a Friday.\u003C\u002Fp>\n\u003Cp>Nice little end cap to the week is a really cool case study. Really good story. And, yeah, so that's it. I didn't have to turn this into a whole big series. I mean, we got this done in under an hour, despite the hiccups with the MCP part, but, that would have been an an additional whole other hour to actually put that content in there.\u003C\u002Fp>\n\u003Cp>So I'm glad that that got figured out. So that's it. That is the process for k creating a case study from scratch to finish product within an hour. You know, obviously, the process that I have and kind of the way that I approach case studies is gonna be wildly different from how you would or how anybody else would, but, I think there's the process here is applicable to anything. So, you know, other use cases for for case studies could be, you know, taking, like, small testimonials or walls of love and creating, like, quick little one hitter, you know, many case studies on your site.\u003C\u002Fp>\n\u003Cp>You can obviously do the same process for that. Yeah. It's it's really up to you. The world is your oyster when it comes to AI plus something like Directus and an MCP, to put it all on your site. So, like I said, I will make sure to put together a really nice little Notion doc, that everyone can have access to, which will include, those initial case study questions that I talked about.\u003C\u002Fp>\n\u003Cp>I will include, the prompts, so all of the prompts that I copy and paste it in there. I'll try to include, like, a little bit of the process of actually using those prompts, like, what to look for, the steps afterwards, and the things they'll look for. So during that editing phase, making sure that the quotes are right. And then I'm actually gonna link, guides to direct us on how to use the MCP because, you know, it's worth even just, like, trying it. Right?\u003C\u002Fp>\n\u003Cp>I I think your mind would be blown if you start using it and see what the possibilities are. I've just barely, like, scratched the surface on it. So here, it says it update updated all the FEG logos. So fingers crossed. Let's see if it actually worked.\u003C\u002Fp>\n\u003Cp>But go in here and refresh. There we are. Bam. There you go. FEG logo on each one.\u003C\u002Fp>\n\u003Cp>And then I just need to add, you know, Darko's headshot. I'm gonna do the same process for all of those. But, yeah, I'm I'm super excited for how this turned out, all in all. And, again, I did it completely on the fly. So I I don't know what I expected coming into this, but, hopefully, this was helpful.\u003C\u002Fp>\n\u003Cp>I know there was a lot of rambling and stuff, but, yeah. Be sure to download the guide. I'll make sure that gets posted here as well. If there's any other like, I I'd like doing this sort of thing and building out content. Content is, like, you know, my my favorite thing to do and using AI, especially in the process of creating content.\u003C\u002Fp>\n\u003Cp>If there's any other specific things you'd like to see, like, just hit me up on LinkedIn or, you know, anywhere you can find me. I love to nerd out about this stuff. But, yeah, I appreciate you following along and, you know, watching, probably skipping through. But, Yeah. I I hope this was helpful for you.\u003C\u002Fp>\n\u003Cp>So, till the next one, I guess. Till then, take it easy, and keep marketing. Keep contenting. I don't know. I need a catchphrase.\u003C\u002Fp>\n\u003Cp>We'll get there. Alright. Bye.\u003C\u002Fp>","Hey. What's up, everyone? My name is Matt. I am a, marketer by trade. A few weeks ago, I posted on LinkedIn if anybody would be interested in me kinda showing off the process behind how I build case studies now with AI as opposed to how I used to write them, the old brute force classic way of actually writing them. You know, luckily, we don't have to do that anymore, and I'm able to create case studies at a much higher clip rate, much more rapidly as a team of one. So, what I figured I would do in this video is show you, kind of the four pillars, quote, unquote, of how, I approach case studies now. One, I'm gonna give you a just glimpse at the stack that I use for this. That includes all the AI tooling, some of the other tools like Notion, as well as, you know, my company is Directus, so shameless plug. I use us for our own CMS for our website. And being able to use the MCP has dramatically helped, the time to getting these case studies actually staged and published on our website. So I'll run through that. That should be really quick. Then I'll talk through some of the prework that gets done. So, there is a big part of this that is still a human component, which is, you know, AI can't run the case study for you, and I don't know if I would trust it to ever do that. So I've got a list of questions that I typically ask, and I've got a transcript I've already done. So, by the way, this will be me live building out a real case study that, has been on my list for a lot longer than it should have been with other stuff, but we're getting it done now. I'll also run you through some of the prompts that I use. You really have to prime the AI, to get it exactly how you want it to be. So we'll do that as well. Then we'll run through the actual content creation. It is extremely fast. There is a lot of editing and things that has to go into effect to make it actually, like, good and not super, like I'm trying to think of the phrase that AI typically uses, like, ever evolving, ever changing world of case studies. You gotta remove all that sort of stuff. So and then at the end, I'm gonna show you how I publish it. And this is actually the coolest part, which it takes no time at all with our new MCP. I still can't wrap my head around the potential with it, and I think you'll like it too. So make sure you stick around for that part as well. Just as caveat, like, anytime I watch these videos, like, I always find myself with a thousand screenshots on my desktop, so don't worry about screenshots. I'm gonna share a link to the notion that has, you know, this I don't know if it's a slide, but this picture, all of the prompts, all of the, like, even the case study questions I ask, and anything else that is relevant here, it'll all be there and available for download. So, I might gate it just because I gotta gate it, but, you know, we won't chase you with the SDRs. It's just nice to have couple emails so I can take it to the team and be like, look what I generated from this. Life of a marketer. Right? Anyways, okay. Let's jump into the stack. So, this is my stack. This is what I use. So for Notion, that's really replaced Google Docs for me. And I just like the layout, like the editing, the way that you can write in it. Just it just comes more naturally at this point. You can also type notion.new like you can in docs.new and get a whole new Notion doc. So convenient. For the call recording, which I've already done, don't worry. I'm not gonna record a call on this call. I use Evoma. Evoma is awesome. It gives you the transcript, which is really what you need from that conversation to prime, Claude so you can actually, you know, use it. That you can use any call recorder as long as it gives you a transcript, but, you need to have that part. That's kind of the whole reason of doing this exercise. Then I use perplexity for just kind of the organization, like, researching them. I like to do priming with, like, from an external tool, external AI, like, perplexity before I create the content because it just I don't know. I I just feel like it goes better, plus I just love spending $20 on every AI tool for some reason. And then for the bulk of the content, I use Claude, and I'll run you through actually how I prompt it and how I talk to it and yell at it and compliment it and all those good things on this on this video. And then at the end, shameless plug again, Directus. That's what we use. We eat our own dog food, as they say, for our content management. So all of the case studies and all the blogs and everything we have on our website, that all goes through Directus. And I really wanna show you the MCP, just because I it still blows my mind. Cool. So to start, case study questions. Right? Like, you this is the only part that will always be manual. There's no AI involved here. These are the questions that I ask. I take into every case study call. Obviously, it's not like a q and a session. The way that I treat these case study calls is more of just like a podcast, like conversation. These are really just like boilerplate, kinda give a road map of the actual conversation. But, what I find is they typically go, you know, off tangent in places, and that's when the best stuff comes out. So this case study that I'm actually working on is with one of our, you know, really good, really big clients, FEG. They're a, Fortuna. They're like a gaming, iGaming slash casino, like, online gambling sort of company. So, that's what this video will be about. Like, super meta is me building that case study for Directus as a marketing tactic for Directus, I guess, is what this is. But, really, I think this will be valuable for you. So, the you probably have case study questions. If you don't, like, I will link these in the actual notion that will be part of that. This getting so meta already. But you'll have access to these. You can just replace, like, how are you using Directus today with your own company, and, you know, customize it as needed. So with that in mind, let's hop over to the first bit of prompting in the prework, which is, finding out information about the company. So, this is, like, the easiest part, which is, I just say, like, give me a comprehensive summary about FEG or whatever company you have interviewed. And why I like perplexity is I feel like it has the most comprehensive, like, online search, and it really gives, like, a really good overview. And, you can dial in a little bit more if you want to get more specific about things. These are none of the FEG. FEG. Let's see. FEG. Right. Like this. So there was a bunch of FEGs out there. So I'll just say Fortuna, specifically. And it is not Fortuna mining. Yeah. Just waiting for this to go. We'll edit this bit out. We'll start over. Alright. So let's hop over to Perplexity for our first step in our prework slash prompting. I use Perplexity, because I feel like it is the most comprehensive search for a company. I don't really need, like, the deep research, like, a 100 pages of research. Like, I just wanna get really a good brief overview of the company, you know, what they do, just to give context to the content as well. So, what I'll ask is I typically just ask, like, give me a comprehensive summary about blank. And then, actually, what you can do is come in here. Fortuna. None of those. Fortuna f e g. Here we go. Fortuna Entertainment Group. Really awesome website, which I think is actually powered by Directus. Wink wink. But, yeah, I'll just do that, feg.eu, and I'll let it, just give me the overview. It gives really good, like, core activities. I like how it does, you know, the milestones here as well. This is all really, really good prompting, primer, I guess. Prime priming for the prompt. I've gotta figure out how to say that the right way. But, and a nice little summary here. So easy peasy. I think this is perfect based on what I know of them, in that conversation. This is exactly, like, what I would need. So I just copy and paste that over in this other Notion doc I've got on this other page. And then next, I am gonna hop over to the actual Claude instance I've got. So I have hopped over here to, Claude by Anthropic. I use this for pretty much all of my content writing. I don't know. I just feel like it is it just writes better to me. I've yet to use Grok. Some things I use ChatGPT for. I feel like that's gotten a lot better recently as well. I don't know. These things it feels like they change quality wise, like, every week or so, and it really depends on when you're doing it. They could be at, like, high bandwidth and just the output if you use them as much as I do, you will start to realize, like, this for this and this for that and that sort of thing. But, for long form content, I really do like Claude. Okay. So where to start? Right? For the initial prompt, what I like to do is give it before I give it anything about the actual case study or or who I wanna write about, I like to do this prompt, which is kinda priming it on the background of what I want from it. So the way that I've structured this is, like, you know, you're world class class copywriting expert. It really loves those, like, role play type of things, and that specializes that interning interviews and case studies. So, I'm gonna take the transcript after this and upload it, but first, I'm gonna prime it with five case studies that I already have. I think you can do three. I just do five just to cover all the bases. And this is really important here, which is, like, don't start doing anything until I confirm with you, that it's time to do that. So, really, all of these prompts are just gonna end with, like, the assistant coming back with, like, I I confirm. I understand. We'll see. Yep. Understand exactly. And to be honest, what I think I'm gonna do is actually try to turn this into the style I want as concise because I wanna use fewer tokens here. It can get, like, really verbal and really wordy, and I think that's why, like, the new Grok is really good is that it's just very concise with its answers, and you use less tokens when it has to refer back to things. So quick little pro tip. Cool. So it's asking me to share those examples. I picked these just because I really like kind of the way that they're framed. So I've got this one, which is mighty HQ. I've got a COPPA case study, which I think turned out really well. Actually, you know what I'm gonna do is you could upload these as text, but what I will actually test and see if this is just as good is pulling the actual URLs here because I do believe that Web search is connected. Yeah. So, I've attached two. I will share links for the others. So Prusa is our most recent one. That's the one I really like the formatting style of, and I would probably use that. Ripley is another good one. And, let's see. Yeah. Copa, that one. And I'll use Lucid as the last one because I really like this one as well. Cool. And if you're starting from scratch, like, you don't need to do these at all, but, these, I think, give it a really good sense of, you know, how we've done this before. They understand the narrative and, like, how we tell the story and that sort of thing. So, depends on, like, how you like to do your case studies here. If you don't have any, then just you can prompt it with, like, this is what I want the output to be. Yeah. I I I prefer to tell kind of the the stories and and that sort of thing. So, excellent. So it's read them. It understands, from those what everything should be. Let me pull up my next prompt here. And, actually, before I upload the transcript, I wanna drop this one in here, which is, you know, what are the common characteristics? Like, what do you think makes them great? And the tone of voice. It touched on it a little bit, but I really wanna, extract that from those so it understands, like, this is exactly the common thread of all of these case studies we've done, like, the the voice, the structure, and all of that. So it's it's fetching all that now. We'll see. Yeah. Classic narrative, pain point, search, the implementation, and the transformation. We do have kind of a conversational tone. You know, I I don't like to be super dry, but totally up to to you how it it really depends on who you sell to. Right? Like, we primarily work with developers, and they don't really like all the jargon and talk and all that stuff. So, this is another thing also is, like, I am a former journalist, so I love to pull out quotes, and I prefer the interviewee to tell the story as opposed to us telling the story on their behalf. So, classic journalism is like, you know, it's full of quotes, and you just kinda connect the dots in between, like, little like, a little puzzle. Right? And just make sure everything kinda makes sense and flows well. So, great. So now is when I feel good about that. What I'm gonna do is upload that information that we pulled from Perplexity here so it understands who we're writing about. It's a lot of copy and pasting. And one day, we'll have agents that replace this whole process, but, and I'm sure there's some purists out there that aren't too, like, fond of this, which I totally understand. Right? If I had time in between, you know, all the other work stuff to, like, sit down and build, like, really good human written case studies and all the research and all that, like, I would love to do it. But I just feel like I don't really have the time anymore, and I think this is a great way to do to supplement kind of the the work that you're doing. So, excellent. So it understands, the style, you know, who we're writing about, which is, FEG. And now I will share the transcript, which I do not have offhand, but I am going to, go grab that really quick. And this is another one of those, like, I want you to read it, get a feel for how we've worked together, you know, their initial intentions and needs, and anything else you think is relevant to create kind of a unique narrative arc that we wanna tell. And, again, I like to end it with, like, just make sure that you've analyzed it. Like, don't start doing anything yet. So we'll see how long this takes. Confirmed. Loved it. Yeah. So there. You pull out already the challenge it has and really all of the metrics, that we wanna tell and really highlight here, which is, you know, five to six hour deployment times, you know, six months in advanced promotional content. It had to be planned. Our contact, Darko, when he found us at the conference and what he really liked about it and the things that stuck out to him, the transformation, which is, like, the big, like, whatever. I forgot. I used I took a theater arts class back in the day, but the best stories have that, that moment of transformation that, you know, Luke Skywalker and the swamp. Yeah. I don't know. I'm terrible at analogies, but you understand, I think. Anyways okay. So now it's read it. It understands it. It's tell me what it's learned. So I'm actually gonna skip the part where I say, like, tell me what you've learned because I think it's done a great job of that already. Now before we dive into the content, I wanna start with an outline. So using the previous case studies I've shared, like, as a reference, give me an outline for how we can structure this, which I think it sort of had it in the previous one. But the way that it creates, like, an artifact here is just much easier to to work with, and, like, just start working in the artifacts themselves. Yeah. I mean, couldn't have said it better myself, I guess. Perfect. Challenge, the implementation, the results, you know, future plans, kind of a a wrap up of, you know, why they chose Directus and then a conclusion of what all this means. So amazing. My next prompt. And the next few prompts are really just gonna be me giving it a little bit more information of what I want as the output. So this is a really big one. I will just copy this over, but I'll talk through this a little bit because there's a lot of stuff in here. Alright. So nice little compliment, which everybody loves. I don't have to add spaces here. But, yeah. So the way that I write the case studies is that I don't have it spit out everything at once. I like to do it section by section, based on kind of the previous ones we've done. I like to do the journal journalistic style, which is, you know, line breaks for every quote. I don't like to have quotes on the same thing as, you know, paragraphs, that we write on our side. I wanna keep paragraph short, easily scannable, and readable, so three to four sentences max. This is the big one. And if you wanna just take this out of this whole video, you're more than welcome to do it. But I love to avoid the AI terminology that seems to seep into all of these, which is, like, words like, delve and ever evolving and revolutionize and streamline. I I just I cannot stand those words or when companies use those words. They just feel like filler to me. They're not really, like, powerful. So, like, where you could use the term use and you use the term utilize instead, like, why? Why don't you just say use? Right? Like, talk like a normal person having a conversation like I'm talking here. So, and then I have just these that I've built over time, which is, like, start with the broader industry context, you know, use the short things, incorporate a lot of quotes in there, specific challenges, briefly explain if you do mention technical features, avoid again, I like to double down. Like, avoid the buzzwords and the hyperbole. And don't this is a big one. Like, don't make claims without the supporting evidence or examples. We're trying to cut down on the hallucination. It still works its way into this, but trying to nip it in the butt here. But that's what the editing is for at the end anyways. So, yeah. No. I think I'll add this at the end just to see if there's anything else that it recommends in addition to these. Yeah. Love that. Practical business benefit along with those, like, technical specifics, and really emphasizing the pain points. Perfect. Now this is where we actually write it. So let's start with the introduction. Now that we've done all this, right, that that's most of the work is getting the priming and the prompting right. I can already tell, like, I'll probably have to edit the intro here. Seamless worked its way in there. It is what it is. But, yeah, the, the tone here isn't ideal for me, which is okay. Right? Like, I think the benefit of this is I'm not staring at a blank page and having to write it from there. And we'll come to that at the end when I do the editing and stuff. But, Yeah. Here, I would say, great. Next section. And then we'll just do this, like, ad nauseum till the end. Right? Because it already has the outline. It knows what we're gonna write. I love how it's adding in, you know, the actual quotes from the case study we ran. Yeah. Great. Next section. So we did the challenge. The next section, if I believe yeah. That's when they came across Directus, kind of the story about that. I I don't know why, but I just love more of the stories than I do just like the here's the x y z benefit. Here's what happened. That sort of thing. I don't know. I'm just kind of a story nerd, I guess. Great. Next section. And, like, I wanna reiterate, like, I'm not gonna copy and paste this into the wet or our CMS and just hit publish. Right? Like, there is a lot of editing that's gonna have to go into this still. But it's just like having, like, an outsourced content writer that I'd find through, like, Fiverr or something that gives me kind of the baseline. And then it's a lot less work for me to go in and and edit and move things around and change things, add quotes, take out quotes, that sort of thing. And the other reason I like doing it section by section is, when you do it altogether and try to get it to spit out this, like, really big piece of content at once, like, it tends to override itself and, like, mention things it's it already has mentioned. When you're doing it section by section, it's taking each previous section into context instead of, like, trying to write it all at once. And I found that this, like, this is gonna be a call out quote, I think, for this, like, in the, like, the marketing and and, you know, things that that we'll post out on social, which is, like, you know, one of the or even the best rollouts in Fortuna history. No outages, really smooth, straightforward. I mean, come on. That's that was dark, though. That was an incredible quote, and it really depends on the input you get or the output you get from a customer to put into here, the quality of this that comes out. So, again, reiterating that initial customer conversation and making sure you have that good podcast like conversation with them really pulls out this this stuff. So, cool results. And I wanna say we got two more sections for this, if I remember correctly. Nice. I know this isn't the most thrilling thing to see, But now we're kinda talking about the future, which most of our well, all of our case studies kinda end with, like, you know, this is the problem that's been solved. You know, looking ahead, how can you maybe deploy Directus in in future chats or something? Just kinda keeping the conversation going. And then we've got the final section. And then just a nice little compliment so far. Because why not? Right? If they do end up taking over the world, I think they'll remember when I said great job and gave them a smiley face. So now that we have this, I feel good about it. Right? Like, I I think this is so far just, like, kinda breezing through it. It looks good. Like I said, there's gonna be some heavy edits. You know? Maybe the intro is gonna need some changes. One thing that you definitely need to do is verify the quotes, and the hallucination typically happens on the quotes themselves. In one of my prompts earlier, I think I was like, you know, double check. Like, don't I forgot what I had said, but, you know, don't make things up. Like, make sure you have, context and everything you have for said case study. But I like it. Now just to knock out some of the other additional stuff before I go to the edit is I wanna get sort of the metadata and the title done. For this one, like, I really like how it did for our Ripley's one where we caught out the metrics, short description using the jobs to be done framework. And then I will top it off with, like, let's get a good title for this five killer titles, like, make sure we add a metric in there, make sure it's a scroll stopper. And, I mean, legacy CMS held to 70% speed games is pretty badass, but I don't know if we can actually post that on our website. Maybe we can. No. These are all good. Like, I I would probably lean towards one because it calls it out, who it is, you know, what the actual thing was, that they achieved, like the big metric showstopper banner callout, and then also across five European gaming markets is pretty big. But these other ones could be useful in other situations. Maybe, you know, if we do some redistribution of this content. But, yeah, I'll say let's go with one. Excellent. So I have got my case study now. Next, I'm gonna hop back over to Notion, and then I'll copy this here. I'll move this kind of older title. Yeah. Too big. Turn that back to text. Yeah. You like, this is just thick paragraphs that I wanna avoid, but, bam. Killer title. I've got my subheader here, learn how Fortuna Entertainment Group used direct us to achieve. I won't call out the metric here. I'll just say faster content deployment, unify web and mobile management, and deliver one of the best rollouts in company history. That is solid, and I'm just gonna italicize that. Cool. Alright. So this is the human component. Right now, we have a case study that is workable with all of this information in it. And I'm actually gonna stop recording, while I go through and edit this. This will probably take me thirty to forty five minutes to do it. I also wanna double check to make sure that the quotes are right. And if they aren't, then I will use that same cloud prompt we have where it has the context of the conversation, and I'm gonna go back and tell it, hey. Like, double check. Like, I think some of these quotes are wrong. The previous time I did this, there was probably three or four direct quotes that weren't right, or just it had kinda mixed up some of the different quotes together. But, yeah, it's it's helpful if you, like, reprompted. So I'm gonna stop sharing. I'm gonna pause this video, and I'll be right back with the final version. Alright. So I've done quite a bit of editing. That was actually quicker than I thought. Took about ten minutes. I'm actually blown away by the output here. I just wanna give you kind of a a scope of, you know, what does it look like when I edit it and, like, after I've done edited and, you know, when I go to, the next step, which is getting it ready for publication. How I like to operate is what I'll do is go through and just loosely format it a little bit, like, how I like it to look. It's just easier for me. So we have that great, title that it spit out, and then I've got kind of the subtext. This will be included, like, on the, you know, the the OG image and stuff, like, when you share it out. I ended up liking I know I said that I wanted to take this out, but it's kind of a cool intro. Right? Like, I think it reminds me a lot of, like, kings and, like, cards and that sort of thing. So, decided to leave that in there. I a lot of the work was breaking up a lot of the, paragraphs, so they weren't just, like, big blocks. We've got I try to do, like, rhythmic when I do writing. So there's, like, some lines with one line and then, you know, some shorter sentences and that sort of thing, which was actually surprising as opposed to last time, what really stood out to me is that these quotes are actually, like, literally from the transcript. So, like, it it even has, like, some of the like like, Darko, you know, is not like you know, he he speaks very good English, but, you know, there's some sort of broken English in some of these. And there's a lot of, like, you know and, like, that sort of thing. So it literally pulled out the exact quotes, which is awesome. And now, you know, I like, this first one. Right? I like to just go in and kinda clean them up. I'll, add, like, specific context to, like, the journalistic style of doing it that way, just to clean them up a little bit, make them easier to read and, like, just better in the context of where they're at in the story. But and the the really the only changes I made were just, editing some of the things. You know, I took out a few sentences here and there. I will reexplore these, like, chapter headers, because I think they can be better. I took out specific references to, like, other tooling or, like, you know, the tools that were getting replaced. I just don't want it's, like, petty to have that stuff in there. So, replace it just with, like, legacy, the platform providers and that sort of thing. But, overall, like, I I think this is great. And so next steps for me from from now that I've got this edited and I've got all of the quotes pulled out here, I highlighted all the things in red that I do wanna go back and change and tweak and clean up a little bit. But other than that, like, I mean, this is solid. So what I'll probably do is take another ten minutes and just kinda formulate some ideas for these headers, clean up some of these quotes, and, yeah, then we'll start the exciting part, which is actually getting this published. And that actually might be the quickest part of this whole video. So, be right back after I do this final cleanup here. Alright. I'm back. I'm really, really happy with how this case study turned out. You know, all in all, I started about an hour ago, which previously, this would probably take me, like, you know, four to five hours to do. But, I was getting couple Slack pings I jumped over on. But, overall, like, this, I would say, is actually our one of our strongest ones when it comes to case studies. Yeah. So just quick touch on some of the things that I edited. So, went through, really smoothed out those quotes so they make sense in the context of of the narrative slash story. I added a little bit more of, like, a narrative driven header here instead of, like, the classic, like, the challenge, the thing. Something that's more like, I don't know, click baity maybe. But, yeah, I I feel good about this the story that's told here and kind of what the resolution is. But yeah. So now that we have this done, I'm super happy with it. So next step is just getting this loaded into the actual CMS. There is a interesting part here. So previously, working with any I've I've worked with, I've worked with WordPress. I've worked with HubSpot. I've worked with, a couple others that I don't know if I can name, but you probably know them, before I started working here at Directus, and Directus kinda became the defacto tool that we use for our website and all that stuff. But, before even just using Directus, right, there has always been a pain for me when it comes to, like I just spent, you know, an hour previously before I had AI. It would take a lot longer to do this. But, I mean, this is an hour worth of work, like, going through doing all the editing and all that stuff. I I'm in a really happy place with it. And this would be the part of the story where I was just like I feel like, oh god. I hate this, which is actually getting this into a, CMS to get it published. And that's because, like, I just hated doing more copy and pasting over to the actual document. You know, just it was it was pain in the butt. Always has been. A few months ago, I wanna say May, my coworker, Bryant, you know, was he'd been reading about MCPs, which if you don't know, it's model context protocol, which is I I think that's what it is. But, essentially, what it is is, kind of an API that connects to your AI or your cloud instance that can be, like, connected with any any tool you use is if that tool has, like, an open API or connector. Right? So what he did is he built a MCP for Directus that connects directly to cloud. Originally, the intention, I think, that I that I'd expected out of it was, you know, just something that could generate content because that's what, you know, AI does for marketers. It's like it generates content, and then you go edit it. What I did not expect was it to actually build out the pages for me if I handed it over the content. And I wanna show you live in this call because it just blows my mind every time I use it. So, now that I've got this really solid, case study, super happy with it. I am actually going to I'm gonna stop sharing for a second to make sure I can get all of my cloud instance and get my login and all that stuff for Directus, which I'm just gonna hop into now. So if you've never seen Directus, I will show you in one moment. But yeah. So let me show you how we have it set up at least so you can get kind of an idea of what this looks like. So, here in Directus, we've got all of our, resources. So, like, each one of these tabs is, like, just something different that we've either experimented with or we've been building and and testing. There's a lot of, like, just really cool stuff we can do here. But to give you an idea of how it's laid out for our, resources, like case studies things, is, all of our blogs and case studies come from this single collection, which is resources, and this is how we've structured this. So for our recent SOC two announcement, which hooray, very official, the when I inputted and made the blog slash, you know, PR announcement, this is how I I built it out, basically. You know? Just like title slug. If if you use WordPress or anything like that, you probably know what this is, from that understanding. But, yeah, and then we have a block editor. Right? So, like, we have these prebuilt blocks that we go in, and we can add, like, header, you know, call to actions. We can have, like, pop outs, which we call block paper internally, separators, dividers, all that stuff. So what it actually comes down to and I'll I'll go to the last case study we did, which was Prusa. What I really like doing is creating and I'll hop over to direct us here so you can see from the front end what this actually looks like. I need to go to our case studies. And I like this format because, like I said earlier, like, I I really like to involve quotes and have, like, the customers actually tell the story as opposed to us. So I added a lot of these blocks in in between. How I add it's nice to have images and things in here too just to kind of break up the monotony of the text. Just makes it I I I just think it looks better. It's just easier to read. So we had the actual customer case study quotes in here, which felt good. Right? Like, I I think this is solid. It's it's a good narrative. The pain in the butt was actually creating the different, quote slash testimonial blocks here because, from the back end, what it looks like is, like, I had each of those quotes as a different block, and then I had, the rich text kinda in between. So this was pre MCP life. This took me, like, longer than it should've, to build it out, but that's okay. Right? Now with the MCP, like alright. I'm gonna show you. I'm gonna make sure I've got all my ducks in it right here and make sure I pull up the right thing. Okay. So I have a project here, which is Direct to SMTP. Like, this is so if you've ever used projects in cloud, it's like you can basically, like, have a set, like, like, knowledge that you give it. So for this one, I put in this prompt of, like, you you know, you're a world class marketing content, technical professional, because I use this a lot for, you know, just getting the content. I can create landing pages really fast, that sort of thing. The this gives it the context that it's working within Directus. And then what I would do is it's connected. So, I will take like, I'll say I want to create a new case study with the same format slash layout as our last one, which was Prusa three d. It is in the resources folder or resources collection. Can you please review that and let me know when you have understood the context and layout. I'll just reiterate that. So this should be connected, I think, hopefully. Yeah. Alright. So now it's prompting the system. Right? It's going in, and it's it's getting what I had told it. It's examining the three d that's actually within Directus. So it's understanding the format. It's going through and reading all of the blocks that exist there, and gonna get, like, an idea for, you know, how to actually structure this new case study now that I've got all of these, you know, quotes in here. And we'll Looks like it's still looking to find it. Ah, found it. Perfect. Okay. Which is just awesome, by the way, that it goes through and it actually thinks and keeps you updated, like, as it's trying to find it. This is a read option thing. It's looking at the specific blocks, which is how it was built. Now it understands case study structure like I showed you. Right? It alternates between that block quote and that block rich text. So exactly how I wanna lay this out. Yeah. Now it's ready to create a new case study. So what I'm gonna do is come over to where I've got my case study written out. I'm going to hop back over here. Perfect. Here's the case study information. So I'm gonna have title is that awesome one we had figured out. I'm gonna have subtitle, which was this, And then I'm just gonna drop in the actual I'm copy and pasting this whole thing. Here's the case study content and then copied it. Alright. We're doing this on the fly. Like, f it. We're doing it live. Let's see what happens. So far so good. I mean, it understands what the context is, and it's exactly what I showed you. Now remember, like, when I showed you in the resources, there there is no existing thing for this. Like, I didn't even create a new case study, like, placeholder or anything. This is gonna do all of that for me. So I can literally just sit back and, I don't know, watch Netflix for five minutes. If anybody from my company is watching, I don't actually watch Netflix. But, if you if that was your prerogative. You know? If it's late in on the weekend, you're getting some weekend work done, you can go do whatever. I'm just chugging Red Bulls right now and getting hyped for Friday evening, which is, like, I yeah. I don't know. I, like, I don't know what to do with my hands now at this point, like Ricky Bobby, while it's going through and creating these blocks. Right? So it created the new resource using a create item thing. What will probably have to I I haven't uploaded any, like, media. Right? So I need to find Darko's headshot. I need to put that in there. We have an FEG logo in there, which I'm really curious to see if it's gonna be able to find it. I believe it's labeled right in the files, but, I'll also need to create, like, a primary banner image, which is, like, our typical, like, Directus logo plus the logo of the person, or the logo of the company that is the main feature, as you'll find is, like, our typical format for it. But yeah. Awesome. So it's alternating between the blocks. Right? So, it created the initial resource. It's creating all the remaining blocks, rich text. There's the content that I had written. I love that it's doing, like, the formatting already. So all of the h twos that I had pulled in, the different paragraphs, still creating the blocks. I mean, I I could do this all day. Like, I almost feel guilty guilty, like, it's doing this, but, if I wasn't on camera here and talking, like, I would probably be back in Slack or I would be in another tab doing something else while this happened. But I just have to talk to fill up the space, which is so weird, like, doing that. I don't know how my coworker, Bryant, does it for all his videos, which is a nice little plug. Like, go to directis.0.i0\u002Ftv, watch 100 apps hundred hours. He does this, but he builds actual applications out, which is just unreal to me. But, yeah, I am, I'm interested to see how this pans out. Looks like it's still going. It was a lot of content. Let's see. Couldn't connect. Lost network connection. Well, that's interesting. Okay. Well, I know that it did create some stuff in here. So what I'm actually gonna do is go back in to the Directus instance and see what we've got. So this was our previous one. If I refresh let's see. It should have created the resource. Interesting. You know how things go when you just create new stuff. Right? It's never perfect. Alright. Well, didn't overwrite any of that, which is solid. We have it set to so it doesn't delete or overwrite anything, but okay. Well, not as perfect as we thought. Of course, it's not gonna go perfect on the first go around. You know how that goes. Right? Okay. All good. Let's try again, see what happens. I'll share something else. I'll come back here, and we'll try again. You know what might have happened? And I noticed that there was maybe somebody else in there. Maybe something to look for, which is interesting. But in any case, you know, it's finding a placeholder. Reusing the case study image. Resources created. You know, it'll build the alternating block structure. I'm having deja vu. You know? Some things just don't go perfectly the first go around, and that's okay. Still, I am about two hours saved at this point of just doing the formatting and that sort of thing. So, yeah, we'll see if it works this way. Continue building the blocks, creating several blocks at once, and then linking them to the resource. Let's see. Well, I'm gonna let this run. If it messes up again, I will come back. But when it's done, instead of me talking to fill dead air, I will, just skip forward to the good part. And I'm back. And it just took a second time. Right? Like, I don't know if anybody here remembers the old Nintendo games or Sega games where you had to just, like, got dusty. He had to blow it out, just blow on the cartridge and put it back in. That is basically what happened with my Claude instance. So, yeah. So the second go around, I haven't actually checked the actual thing yet, but based on what I'm seeing here, says it's created it. I went through and just double checked, and it looks like the the block quotes were correct. The structure that it added all in there, there was 15 alternating blocks based on the rich text and the quotes. All the quotes were from Darko. Again, I'll probably have to upload, his assets and the other thing. But cool. So I'm gonna go back to our marketing instance for. Share something else. This has been totally live, by the way. I have not, other than a little bit of prep here and there, this has been yeah. So what I'll do is add date published. I'm gonna sort by descending. It's not actually published yet, so I need to double check where that would be, which is probably why it's not in here because it's not actually published. And open that. Open the link. Let's try this. Fingers crossed. Nice. Okay. Well, it's here. It's in resources. It looks like date published was back January 18, so it pulled the same publish date. So I'm gonna set this to now just to have it updated to, be at the top of the list. 70% faster gaming, content deployment. Actually, I think this title is wrong, which the title was supposed to be how Fortuna Entertainment achieved. See, can't trust AI for everything. You know? That's actually a really long I'll say FEG. Shorten that title a little bit. You know? Changing it on the fly. Let's just make sure our, yeah, summary, change that. It's case study. It's part of the category. The slug is how we do it. It's just what the company's name is part of the slug, and use this placeholder for the image, which I'll update. And then as we go down here, looks good. Right? So I think there's a few little missteps here where there's a couple quotes down here that will probably just have to get added to the right spot. But, you know, if I go in and, I'm gonna save this as it stands just to get an idea for what it looks like. I'm actually gonna use our live preview here to see what this looks like. Alright. It's up. So we've got how for twenty entertainment group achieved 70% faster. We have just a placeholder image here. Yeah. It exists. Subdued sort to. I'm gonna have to make a few changes to this just because for some reason they're not showing up here. But in any case, doing stuff on the fly. So the MCP did not go off as exciting as I hoped it would. But just from the perspective of, like, hey. This is this content's in here already. Like, I didn't have to create these new blocks. I didn't have to copy and paste this stuff in here. Now it's just going in and ensuring that this is, like, built the right way. I'm just gonna refresh real quick. Looks like the same here. And if I actually pop this image out yeah. Image can't be null, so I'll have to pull one of our images from the library. We'll use this placeholder, see if that changes things for a live preview. Alright. Well, I'll have to go investigate this, obviously. But like I said, typically, this works out a lot easier than it does here, a lot cleaner. Every time I've done it, but when I'm not recording, it happened, it happens to go off really well. So, it's just because I'm recording, because you're watching, and I was trying to hype it up. But like I said, any case, it's just really cool, I think, that we have been able to capture all of this case study content in here. It has reduced the the need to go through and actually, like, copy and paste that content over. And, yeah, now there's just a little bit of cleanup left and gotta create the assets, and I gotta, like, double check and make sure that it's not a quote that's, like, kinda throwing stuff off. And I think we're we're good to go. I solved the problem. Alright. So I figured out what it was. So what had happened was, the initial run of the MCP when it was creating that that first batch of content, it actually created, like, two thirds of the entire thing. So when it stopped, that was still associated with a instance, and it still had the same slug. So there was a small bug slash error on my part where I didn't recognize that there had been two versions of the same instance built up. So, all is well that ends well. You know, after realizing the issue, what I did was, delete the initial one that was created, and that's all I needed to do. So I came back in, and here's the content. It's all here. Amazing. It's got our quotes. I'm gonna have to obviously clean up because I had quotes in the original one, and our quote template has this here. But, yeah, the case study is here. Amazing. So I'll just have to readjust some of the blocks. I noticed that some of them, you know, are are out of order, but that's totally fine as long as I don't have to create them from scratch. One of the cool things I think I'm gonna do now is I'm gonna add the FEG logo here, as part of this. So I'm gonna come back here, and I'm gonna say, amazing. For the quote testimonial blocks, can you find the FEG most recent G logo we uploaded and add that to all of the blocks, please. Now we're getting tricky with it. So instead of me going through and manually adding each one, and I if this works, hopefully, it does, and we get it correctly added. So found two. We'll use the first one, and update all the quote blocks with that company logo efficiently, just so you know. It's doing this concisely and efficiently. Great. So while that is cooking, I'm just gonna keep an eye here. I'm gonna go over. I'm gonna adjust these images, but I'll have, this done at, you know, 04:45, 05:00 on a Friday. Nice little end cap to the week is a really cool case study. Really good story. And, yeah, so that's it. I didn't have to turn this into a whole big series. I mean, we got this done in under an hour, despite the hiccups with the MCP part, but, that would have been an an additional whole other hour to actually put that content in there. So I'm glad that that got figured out. So that's it. That is the process for k creating a case study from scratch to finish product within an hour. You know, obviously, the process that I have and kind of the way that I approach case studies is gonna be wildly different from how you would or how anybody else would, but, I think there's the process here is applicable to anything. So, you know, other use cases for for case studies could be, you know, taking, like, small testimonials or walls of love and creating, like, quick little one hitter, you know, many case studies on your site. You can obviously do the same process for that. Yeah. It's it's really up to you. The world is your oyster when it comes to AI plus something like Directus and an MCP, to put it all on your site. So, like I said, I will make sure to put together a really nice little Notion doc, that everyone can have access to, which will include, those initial case study questions that I talked about. I will include, the prompts, so all of the prompts that I copy and paste it in there. I'll try to include, like, a little bit of the process of actually using those prompts, like, what to look for, the steps afterwards, and the things they'll look for. So during that editing phase, making sure that the quotes are right. And then I'm actually gonna link, guides to direct us on how to use the MCP because, you know, it's worth even just, like, trying it. Right? I I think your mind would be blown if you start using it and see what the possibilities are. I've just barely, like, scratched the surface on it. So here, it says it update updated all the FEG logos. So fingers crossed. Let's see if it actually worked. But go in here and refresh. There we are. Bam. There you go. FEG logo on each one. And then I just need to add, you know, Darko's headshot. I'm gonna do the same process for all of those. But, yeah, I'm I'm super excited for how this turned out, all in all. And, again, I did it completely on the fly. So I I don't know what I expected coming into this, but, hopefully, this was helpful. I know there was a lot of rambling and stuff, but, yeah. Be sure to download the guide. I'll make sure that gets posted here as well. If there's any other like, I I'd like doing this sort of thing and building out content. Content is, like, you know, my my favorite thing to do and using AI, especially in the process of creating content. If there's any other specific things you'd like to see, like, just hit me up on LinkedIn or, you know, anywhere you can find me. I love to nerd out about this stuff. But, yeah, I appreciate you following along and, you know, watching, probably skipping through. But, Yeah. I I hope this was helpful for you. So, till the next one, I guess. Till then, take it easy, and keep marketing. Keep contenting. I don't know. I need a catchphrase. We'll get there. Alright. Bye.","952ad43a-abb4-4b2f-a17f-294ff2322eb4",[710],"338d03b2-37a1-4b37-8e3c-98478aa1c35d",[],{"year":399,"number":305,"id":713,"episodes":714,"show":715},"a9558b2c-d408-47fb-8586-076724dc2d8e",[699],{"title":716,"slug":717},"Content Power Hour","content-power-hour",{"id":696,"slug":719,"vimeo_id":720,"description":672,"tile":721,"length":722,"resources":12,"people":12,"episode_number":480,"published":723,"title":724,"video_transcript_html":725,"video_transcript_text":726,"content":12,"seo":727,"status":19,"episode_people":728,"recommendations":731,"season":732},"July-25","1099308991","e9982a61-0c03-4dbe-b009-5f57f6e0ae78",26,"2025-07-09","July 2025","\u003Cp>Speaker 0: Alright. Hello, everyone. Welcome to July's version of the changelog from Directus. I'm Beth, and I'm gonna be taking you through what we've got in store for you today. Whether you are joining us live or you're catching up to this later, hi.\u003C\u002Fp>\u003Cp>Hello. Thank you for joining us. Welcome. Welcome if you are new. Thanks for joining us again if you are returning.\u003C\u002Fp>\u003Cp>If If you are joining us live and you've got any questions, do pop them in the chat. And if you are on YouTube or anything else like that, we will catch up with them later on. But for now, I am gonna kick us off by starting with the product updates.\u003C\u002Fp>\u003Cp>Speaker 1: Let's talk through some of the highlights for our most recent release, directors 11.9. There's some potential breaking changes to be aware of. We've added support for LDAP login and enhanced parameter consistency for refresh and logout commands. There's allowed overriding the mode in refresh and logout commands to be inline with login, and the login method now accepts a payload object instead of separate email and password parameters. We've added anonymous project ID for improved telemetry.\u003C\u002Fp>\u003Cp>We've also added backlink query parameter to exclude back relations when expanding queried fields. You can see the full release notes on GitHub.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Something else that's new is collaborative editing. So we've got Bryant talking you through that next.\u003C\u002Fp>\u003Cp>Speaker 2: It's officially time to stop, collaborate, and collaborate? Brian here from Directus. And today, super excited to show you collaborative editing, which has been one of the most requested features since I've been here at Directus. And it's big on the hit list. It's awesome.\u003C\u002Fp>\u003Cp>Let's take a look. So you could see I've got, two different browsers side by side, and I'm logged in, as myself over here and as, dramatic chipmunk over here on the right. And I'm just gonna open up one of my pages here, the home page. So I'm gonna start editing my home page. You know, maybe I wanna change the permalink.\u003C\u002Fp>\u003Cp>And, of course, I could see my own user avatar here, but, as dramatic chipmunk logs into this page, we could see now, I could see he is editing this, and, he's locked out of editing any fields that I'm working on. And likewise, on this side of it, I am locked out of editing any fields that he is working on. All these changes are happening in real time, and it does support relationships. So this is the best part. Right?\u003C\u002Fp>\u003Cp>Directus allows you to create relationships super easily, and then, you know, the relationships like many to any page builder are great for dynamic pages. So now I can work on a landing page collaboratively with my teammates. So you could see here any changes I make are reflected over here. And likewise, any changes you make are reflected there. Now that is collaborative editing.\u003C\u002Fp>\u003Cp>Super simple, super smooth. How do we actually enable this? This is an extension, so make sure you follow the instructions in the docs, download, and install the extension into your Directus instance. The next thing that you'll do is go to your settings. So I'm gonna go over here.\u003C\u002Fp>\u003Cp>Let's just go back to full screen. We're gonna go to the project settings. Go to the module bar. Once you have that extension installed, make sure that real time collaboration is checked, and that will add a new icon to the navigation. And you can enable this globally, which is the default.\u003C\u002Fp>\u003Cp>It's the easiest way to do it. It will work in the file library or in the user directory. Or if you prefer a more selective approach, maybe you've got some sensitive data that you don't want to, work collaboratively on, what you can do is go into the individual data model. So pick a collection and just search for the collaboration interface. You add that to the specific collection that you want, and then collaboration will only be enabled for that specific collection or collections that you enable it for.\u003C\u002Fp>\u003Cp>So that's it for collaborative editing. That's it for me. This thing is awesome. I hope you'll find it as awesome as I do. Kudos to our team and thank you for watching.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. And if you wanna hear more or you've got any questions, we have a post on community.directors.io from Matt with lots of other info and some links which I will drop in the chat as well. Speaking of the community platform, the next segment is a community hotline.\u003C\u002Fp>\u003Cp>Speaker 2: And welcome back to the community hotline. I'm your host Brian Gillespie from Directus. Today, we are answering a question from Directus community member, Daryl Morley. Daryl, how are you, sir? Welcome to the community.\u003C\u002Fp>\u003Cp>Happy to have you. I'm gonna answer your question today in detail. Alright. So the question from Daryl. I'm integrating Directus with Lightspeed retail API.\u003C\u002Fp>\u003Cp>Not heard Lightspeed name in a long time. That's awesome though. Would like to use, for example, the category ID from Lightspeed as the primary key in my categories collection within Directus. Here's the flow I'm aiming for. One, a the first step, a user creates a new category from the Directus admin UI.\u003C\u002Fp>\u003Cp>A before create hook fires that's not actually what it's called in Directus, but I got you. It creates a category inside Lightspeed via their API. We receive the category ID back from Lightspeed, and it sets that ID as the primary key of the new Directus record. The issue I'm running into is if the ID field is not auto generated, Directus requires you to provide an ID. Yeah.\u003C\u002Fp>\u003Cp>You gotta have that primary key for sure. But I can't know the ID until I call the Lightspeed API inside the hook. Prevents me from using the Lightspeed category ID as a Directus primary key. I could potentially store Lightspeed ID in a separate external category ID field, also an option. But then I run into difficulties linking my items to categories as, the directus only supports relationships using the primary key.\u003C\u002Fp>\u003Cp>You could establish relationships on that that other field as well, but I totally understand where you're coming from with this. And Daryl's pleading. Because anyone successfully implemented a setup like this or found a clean workaround, keeps everything in sync. Alright. So we are going to dive into this, Daryl.\u003C\u002Fp>\u003Cp>We are gonna solve this problem together right now. Alright. So I've got a blank direct us instance. We're gonna go in and let's create a new collection. I use plurals.\u003C\u002Fp>\u003Cp>We're gonna call it categories. Now every collection has to have a primary key. Typically, I just leave this set to ID for simplicity's sake, and usually I go with a generated UUID. We automatically generate that. In this case, where we want that to come from a third party, we are going to use a manually entered string.\u003C\u002Fp>\u003Cp>Now optional fields, I usually add these a 100% of the time. Not gonna do that here. I'm just gonna click finish setup, and what we're gonna do is just hit name. Let's add one more field for the name of this category, and this is our categories. Right?\u003C\u002Fp>\u003Cp>So now the challenge is if I go in, and this is a new category, just as you said, if I try to save this using command s or just hitting the save button here, requires a value. Well, that sucks. That's not what I wanna do. So how do we fix this? There's two ways I can solve this.\u003C\u002Fp>\u003Cp>I'm gonna show you flows first, and then we'll talk about hooks. Underneath the hood, they use the same event system. But, one way to do this inside the studio without writing much code or creating a custom extension that you've got to bundle and deploy, you could just do this in a flow. And if the logic is simple enough, it's it's easy to maintain. So we're gonna call this create category is the name of this flow, cool, in external API.\u003C\u002Fp>\u003Cp>Now I'm not actually gonna use the light speed API here just to keep this high level, but, yeah, you'll you'll figure that part of it out. Got faith in you. Got confidence. Alright. So we can trigger these flows a variety of different ways.\u003C\u002Fp>\u003Cp>Well, the one we're looking for is event hook, and then we're immediately asked the type of event hook. So there's two, obviously, that's on the screen. There's filter, which is, they said we say blocking here because it blocks that event from actually performing. This is the one that we want because we get a chance to it runs before the item gets saved in the database or before the item gets updated. We'll talk about that in a moment.\u003C\u002Fp>\u003Cp>Actions happen after the event is settled, so after the category has been created. In this case, we want to intercept that category as it's being created and before it's saved into the database, and we're gonna alter the payload. So that's what we're doing here. The scope, we just want items create. Right?\u003C\u002Fp>\u003Cp>Updated items should already have an ID, and we don't wanna complicate this. The response body is going to be our altered payload with this. So this is the payload that we're gonna save to the database. Now you can choose between all data or the data of the last operation. In this case, I'm just gonna keep this simple and choose data of the last operation to remove as many potential issues.\u003C\u002Fp>\u003Cp>Alright. So now we've got our trigger. The next thing that we wanna do is call the external API. Now, I could go in and just choose webhook request URL. I'm gonna hit call external API here.\u003C\u002Fp>\u003Cp>This is our step. In this case, it's gonna be a post. And I've just set up, like, a a mock endpoint, from some online free service that when we send this request, it should just send us an ID back. Cool. Alright.\u003C\u002Fp>\u003Cp>And now the last step, you know, and probably, like, that stuff you're getting back from Lightspeed, you're gonna wanna transform that payload. There's two ways to do that. And let's just call this payload. Right? I could use the run script operation where I've got the full power of JavaScript.\u003C\u002Fp>\u003Cp>Now you can import anything from NPM or use any libraries here, but you you've got, you know, the standard JavaScript library available to you here, and you can run, you know, just basic little functions there. Actually, you don't have the standard JavaScript library. This runs in an isolate, but you got all the basic JavaScript stuff that you're gonna need to transform a payload. The other way to do this is via the transform payload operation, which basically just allows you to adjust what is gonna come out of this thing. And you'll see we're using mustache syntax here for a reason.\u003C\u002Fp>\u003Cp>Every operation in a flow appends its data to a key that you can then access. So there's two that are special, which are accountability and dot trigger. That's why they have or I'm sorry, dollar sign trigger. That's why they have that dollar sign in front of them. But the rest of them, we could just do like this.\u003C\u002Fp>\u003Cp>So it'll be call external API, that's the key, and then we're just gonna get the ID. Right? Cool. Hit save. This is our flow.\u003C\u002Fp>\u003Cp>Super complicated, super messy. No. It's not. It's easy. And then we're gonna go in, and now we're gonna create a new category.\u003C\u002Fp>\u003Cp>So we hit new category. And oh, undefined. That's not good. What is happening here? Call external API.\u003C\u002Fp>\u003Cp>That's my key. Did we get some records back? Okay. Yep. So that's the issue.\u003C\u002Fp>\u003Cp>Right? Now I incorrectly formatted this. The thing that we're looking for is dot data dot ID. Alright. Always gotta do a bit of troubleshooting.\u003C\u002Fp>\u003Cp>I was hoping I was just gonna one shot that without any AI assistance, but, hey, it happens. And I also saw that we're missing the name. So in this case, you know, I'm just going to call the name. So we'll do trigger dot payload dot name. I think that's gonna be correct.\u003C\u002Fp>\u003Cp>Now, again, this is probably where you might wanna use something like JavaScript. But we can see here in our trigger, we have a payload. We have a name for the payload. Awesome. Alright.\u003C\u002Fp>\u003Cp>So now, fingers crossed, this should work or it's gonna error out because we can't have two categories with undefined. This is new category. Hit save, and voila, we've got an ID that has come from the ether. Now the other approach to do this, you know, if you prefer to express your logic as code and you wanna, you know, check that into Git, what you can do is just build an extension that does the same thing. Right?\u003C\u002Fp>\u003Cp>How do I do that? Well, go to our extension docs. So just go to guides, go to extensions, go to quick start, copy this code. I'm just gonna open up my terminal. We are going to put this here, APX direct us extension latest, and we're just gonna write a custom hook.\u003C\u002Fp>\u003Cp>And I'm going to even cheat and just use something that I've got in, another project. Alright. Choose a name for the extension. External ID, maybe. Sounds good.\u003C\u002Fp>\u003Cp>Cool. We're gonna use TypeScript, auto installer dependencies, and now I've got this external ID extension. It actually needs to be in my extensions folder here inside Directus. So, let's just blow this whole thing up and do that. My Directus project CD extensions slash external ID.\u003C\u002Fp>\u003Cp>Why does that not work? CD extensions. What is my directory? Oh, somehow I'm inside Directus. I could do that.\u003C\u002Fp>\u003Cp>CD extensions, external ID. Okay. And if I open this up, it's just an index dot ts file whenever the item is created. Let's pull up just something from my clipboard. Where are you?\u003C\u002Fp>\u003Cp>Okay. Cool. So this is a pattern that I've used quite a bit before. We're just gonna call this categories. Right?\u003C\u002Fp>\u003Cp>And we're gonna erase nano ID. We don't really need that. Cool. And we'll say, what, fetch? We could do a fetch call in here.\u003C\u002Fp>\u003Cp>Alright. Constant ID. You know what? Let's just let's keep it simple. Nano ID.\u003C\u002Fp>\u003Cp>Cool. So what this does, right, we define this hook, and we'll go into we see this filter that's on items dot create, and then we're passing in the payload, our schema from the direct us. And, we've got our accountability object, which we're not using at all here. But then we've got this constant that says, okay. These collections have to have a public ID.\u003C\u002Fp>\u003Cp>And in this case, I could just change it to ID. And all we're doing here is updating the payload and returning the payload. Payload has no ID. Alright. I'm gonna hit NPM build.\u003C\u002Fp>\u003Cp>Directus CD extensions. External ID. Cool. There we go. PMPM build.\u003C\u002Fp>\u003Cp>Build my extension. I just wanna show you one way that can get this done. And then I've got my directus instance. We're gonna need to restart that bad boy. So we'll hit restart.\u003C\u002Fp>\u003Cp>It should pick up that new extension. And fingers crossed. If we go to extensions, we could see that extension. And then I can deactivate. Let's not delete it entirely.\u003C\u002Fp>\u003Cp>So we'll just deactivate this flow, change the status to inactive. Cool. Got it. And now if I do this again, say test category. Fingers crossed.\u003C\u002Fp>\u003Cp>I know this is not gonna work. Oh, it does work. There we go. Test category. I was gonna say, I don't think I have Nano ID in installed, but maybe we're using that somewhere inside Directus itself.\u003C\u002Fp>\u003Cp>Cool. There it is. Couple different ways to achieve it. Hope this answers your question. Daryl, my man, thanks for joining the community.\u003C\u002Fp>\u003Cp>Appreciate you. Keep the questions coming. Keep the feedback coming. We love it. I'll see you next time on the community hotline.\u003C\u002Fp>\u003Cp>Hey, guys. Brian here for Directus. And in this video, I'm gonna show you how to use the Directus Content MCP server to organize and manage all of your assets inside your CMS. Now, this is a huge problem for any CMS, all the way back to the before WordPress times. Right?\u003C\u002Fp>\u003Cp>We've got a bunch of images that somebody uploads. I'll just copy paste this one in. And to beat it all, I'm just gonna rename this one image dot JPEG. Right? We're gonna download a nice little bunny that we're gonna paste in here.\u003C\u002Fp>\u003Cp>So we'll throw our bunny in there. Cool. Got the bunny. And let's do one more, maybe some ice cream. My kids are into ice cream, so that's what we'll do.\u003C\u002Fp>\u003Cp>And, again, like, one of the other nice things about Directus, I can always import from a URL instead of a, actually, a file. So there we go, right? And here in lies the problem with every asset management inside of CMS is we end up with a bunch of stuff that makes no sense, has no metadata, basically impossible to search. So q, the direct as content MCP server, and Claude. So I am going to use my prompt here, and it says add from Directus.\u003C\u002Fp>\u003Cp>And I'm gonna click this one that says organize images. So these prompts basically allow us to add additional details, that gets replaced using variables. And then I'm just gonna say, please organize the assets in the needs organized folder. Sorry for the slow typing. So I hit add prompt, and basically, this has just generated this.\u003C\u002Fp>\u003Cp>It has added my prompt here that I gave it at the end, and we've got some instructions for it already. And we'll just hit go. So what is Claude going to do? It should pull the schema from Directus. It should look at the system prompt, and then it should analyze these files, actually.\u003C\u002Fp>\u003Cp>So it has the ability because of the ClaudeSonic model has vision capability. It can analyze these raw images and actually analyze the content. So you could see it here. It's making a request with the raw true. That's gonna download the file.\u003C\u002Fp>\u003Cp>It should analyze that. And now it should go in and update the metadata for each one of these images. Now somewhere, someone in Anthropic is screaming about all of the calls that are happening here, but this is truly awesome. Right? So we could see it is updating all the metadata for me.\u003C\u002Fp>\u003Cp>I haven't had to go through and classify these. Now moving forward, as I just refresh the screen here and we could see what it's done for us, I've got a full title, I've got a description, I've got tags for all of these. This is gonna be easy to find this content moving forward versus just a bunch of image dot JPEG or image dot PNG or random strings of characters inside our assets. Amazing. So this is just one workflow that is possible with the direct to content MCP.\u003C\u002Fp>\u003Cp>Stay tuned for more.\u003C\u002Fp>\u003Cp>Speaker 1: We want to take a moment towards the end of the changelog for thanking our amazing community contributors who give their time to improve the director's project. Since last month, there have been three A huge thank you to Jonas for fixing logging of invalid URL in isurlallowed, to SunGroll for adding support for non array group claims in OpenID and OAuth2, and to ABDON for updating the extensions SDK to Rollup v4. Thank you again. You can see the specific pull requests inside of the full release notes on GitHub. Lastly, we also want to take the time to thank our GitHub Sponsors of June, who kindly financially contribute to Directus' development.\u003C\u002Fp>\u003Cp>A huge thank you to Wayfan, Entle, Mike, Fergus, Omar, Marcus, Mission Control, Utomic, Steven, James, Nonlinear, Andreas, John, Jamiluddin, Birb, Adam, Jason, Yuya, Vincent, CK, Valentino, Jens, and Wayne. The money we are given from our GitHub sponsors goes straight back to community members who build tooling and extensions for the director's ecosystem. Thank you again for being part of that.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. And that is everything we have for you this month. If you are still here, thank you so much for taking the time to join us. We really appreciate it. If you've got any feedback on anything you'd like to see or any questions you have, community.directors.io, and we'll hope to see you next month.\u003C\u002Fp>\u003Cp>Thanks so much for joining. Have a good one, everyone. Bye.\u003C\u002Fp>","Alright. Hello, everyone. Welcome to July's version of the changelog from Directus. I'm Beth, and I'm gonna be taking you through what we've got in store for you today. Whether you are joining us live or you're catching up to this later, hi. Hello. Thank you for joining us. Welcome. Welcome if you are new. Thanks for joining us again if you are returning. If If you are joining us live and you've got any questions, do pop them in the chat. And if you are on YouTube or anything else like that, we will catch up with them later on. But for now, I am gonna kick us off by starting with the product updates. Let's talk through some of the highlights for our most recent release, directors 11.9. There's some potential breaking changes to be aware of. We've added support for LDAP login and enhanced parameter consistency for refresh and logout commands. There's allowed overriding the mode in refresh and logout commands to be inline with login, and the login method now accepts a payload object instead of separate email and password parameters. We've added anonymous project ID for improved telemetry. We've also added backlink query parameter to exclude back relations when expanding queried fields. You can see the full release notes on GitHub. Alright. Something else that's new is collaborative editing. So we've got Bryant talking you through that next. It's officially time to stop, collaborate, and collaborate? Brian here from Directus. And today, super excited to show you collaborative editing, which has been one of the most requested features since I've been here at Directus. And it's big on the hit list. It's awesome. Let's take a look. So you could see I've got, two different browsers side by side, and I'm logged in, as myself over here and as, dramatic chipmunk over here on the right. And I'm just gonna open up one of my pages here, the home page. So I'm gonna start editing my home page. You know, maybe I wanna change the permalink. And, of course, I could see my own user avatar here, but, as dramatic chipmunk logs into this page, we could see now, I could see he is editing this, and, he's locked out of editing any fields that I'm working on. And likewise, on this side of it, I am locked out of editing any fields that he is working on. All these changes are happening in real time, and it does support relationships. So this is the best part. Right? Directus allows you to create relationships super easily, and then, you know, the relationships like many to any page builder are great for dynamic pages. So now I can work on a landing page collaboratively with my teammates. So you could see here any changes I make are reflected over here. And likewise, any changes you make are reflected there. Now that is collaborative editing. Super simple, super smooth. How do we actually enable this? This is an extension, so make sure you follow the instructions in the docs, download, and install the extension into your Directus instance. The next thing that you'll do is go to your settings. So I'm gonna go over here. Let's just go back to full screen. We're gonna go to the project settings. Go to the module bar. Once you have that extension installed, make sure that real time collaboration is checked, and that will add a new icon to the navigation. And you can enable this globally, which is the default. It's the easiest way to do it. It will work in the file library or in the user directory. Or if you prefer a more selective approach, maybe you've got some sensitive data that you don't want to, work collaboratively on, what you can do is go into the individual data model. So pick a collection and just search for the collaboration interface. You add that to the specific collection that you want, and then collaboration will only be enabled for that specific collection or collections that you enable it for. So that's it for collaborative editing. That's it for me. This thing is awesome. I hope you'll find it as awesome as I do. Kudos to our team and thank you for watching. Alright. And if you wanna hear more or you've got any questions, we have a post on community.directors.io from Matt with lots of other info and some links which I will drop in the chat as well. Speaking of the community platform, the next segment is a community hotline. And welcome back to the community hotline. I'm your host Brian Gillespie from Directus. Today, we are answering a question from Directus community member, Daryl Morley. Daryl, how are you, sir? Welcome to the community. Happy to have you. I'm gonna answer your question today in detail. Alright. So the question from Daryl. I'm integrating Directus with Lightspeed retail API. Not heard Lightspeed name in a long time. That's awesome though. Would like to use, for example, the category ID from Lightspeed as the primary key in my categories collection within Directus. Here's the flow I'm aiming for. One, a the first step, a user creates a new category from the Directus admin UI. A before create hook fires that's not actually what it's called in Directus, but I got you. It creates a category inside Lightspeed via their API. We receive the category ID back from Lightspeed, and it sets that ID as the primary key of the new Directus record. The issue I'm running into is if the ID field is not auto generated, Directus requires you to provide an ID. Yeah. You gotta have that primary key for sure. But I can't know the ID until I call the Lightspeed API inside the hook. Prevents me from using the Lightspeed category ID as a Directus primary key. I could potentially store Lightspeed ID in a separate external category ID field, also an option. But then I run into difficulties linking my items to categories as, the directus only supports relationships using the primary key. You could establish relationships on that that other field as well, but I totally understand where you're coming from with this. And Daryl's pleading. Because anyone successfully implemented a setup like this or found a clean workaround, keeps everything in sync. Alright. So we are going to dive into this, Daryl. We are gonna solve this problem together right now. Alright. So I've got a blank direct us instance. We're gonna go in and let's create a new collection. I use plurals. We're gonna call it categories. Now every collection has to have a primary key. Typically, I just leave this set to ID for simplicity's sake, and usually I go with a generated UUID. We automatically generate that. In this case, where we want that to come from a third party, we are going to use a manually entered string. Now optional fields, I usually add these a 100% of the time. Not gonna do that here. I'm just gonna click finish setup, and what we're gonna do is just hit name. Let's add one more field for the name of this category, and this is our categories. Right? So now the challenge is if I go in, and this is a new category, just as you said, if I try to save this using command s or just hitting the save button here, requires a value. Well, that sucks. That's not what I wanna do. So how do we fix this? There's two ways I can solve this. I'm gonna show you flows first, and then we'll talk about hooks. Underneath the hood, they use the same event system. But, one way to do this inside the studio without writing much code or creating a custom extension that you've got to bundle and deploy, you could just do this in a flow. And if the logic is simple enough, it's it's easy to maintain. So we're gonna call this create category is the name of this flow, cool, in external API. Now I'm not actually gonna use the light speed API here just to keep this high level, but, yeah, you'll you'll figure that part of it out. Got faith in you. Got confidence. Alright. So we can trigger these flows a variety of different ways. Well, the one we're looking for is event hook, and then we're immediately asked the type of event hook. So there's two, obviously, that's on the screen. There's filter, which is, they said we say blocking here because it blocks that event from actually performing. This is the one that we want because we get a chance to it runs before the item gets saved in the database or before the item gets updated. We'll talk about that in a moment. Actions happen after the event is settled, so after the category has been created. In this case, we want to intercept that category as it's being created and before it's saved into the database, and we're gonna alter the payload. So that's what we're doing here. The scope, we just want items create. Right? Updated items should already have an ID, and we don't wanna complicate this. The response body is going to be our altered payload with this. So this is the payload that we're gonna save to the database. Now you can choose between all data or the data of the last operation. In this case, I'm just gonna keep this simple and choose data of the last operation to remove as many potential issues. Alright. So now we've got our trigger. The next thing that we wanna do is call the external API. Now, I could go in and just choose webhook request URL. I'm gonna hit call external API here. This is our step. In this case, it's gonna be a post. And I've just set up, like, a a mock endpoint, from some online free service that when we send this request, it should just send us an ID back. Cool. Alright. And now the last step, you know, and probably, like, that stuff you're getting back from Lightspeed, you're gonna wanna transform that payload. There's two ways to do that. And let's just call this payload. Right? I could use the run script operation where I've got the full power of JavaScript. Now you can import anything from NPM or use any libraries here, but you you've got, you know, the standard JavaScript library available to you here, and you can run, you know, just basic little functions there. Actually, you don't have the standard JavaScript library. This runs in an isolate, but you got all the basic JavaScript stuff that you're gonna need to transform a payload. The other way to do this is via the transform payload operation, which basically just allows you to adjust what is gonna come out of this thing. And you'll see we're using mustache syntax here for a reason. Every operation in a flow appends its data to a key that you can then access. So there's two that are special, which are accountability and dot trigger. That's why they have or I'm sorry, dollar sign trigger. That's why they have that dollar sign in front of them. But the rest of them, we could just do like this. So it'll be call external API, that's the key, and then we're just gonna get the ID. Right? Cool. Hit save. This is our flow. Super complicated, super messy. No. It's not. It's easy. And then we're gonna go in, and now we're gonna create a new category. So we hit new category. And oh, undefined. That's not good. What is happening here? Call external API. That's my key. Did we get some records back? Okay. Yep. So that's the issue. Right? Now I incorrectly formatted this. The thing that we're looking for is dot data dot ID. Alright. Always gotta do a bit of troubleshooting. I was hoping I was just gonna one shot that without any AI assistance, but, hey, it happens. And I also saw that we're missing the name. So in this case, you know, I'm just going to call the name. So we'll do trigger dot payload dot name. I think that's gonna be correct. Now, again, this is probably where you might wanna use something like JavaScript. But we can see here in our trigger, we have a payload. We have a name for the payload. Awesome. Alright. So now, fingers crossed, this should work or it's gonna error out because we can't have two categories with undefined. This is new category. Hit save, and voila, we've got an ID that has come from the ether. Now the other approach to do this, you know, if you prefer to express your logic as code and you wanna, you know, check that into Git, what you can do is just build an extension that does the same thing. Right? How do I do that? Well, go to our extension docs. So just go to guides, go to extensions, go to quick start, copy this code. I'm just gonna open up my terminal. We are going to put this here, APX direct us extension latest, and we're just gonna write a custom hook. And I'm going to even cheat and just use something that I've got in, another project. Alright. Choose a name for the extension. External ID, maybe. Sounds good. Cool. We're gonna use TypeScript, auto installer dependencies, and now I've got this external ID extension. It actually needs to be in my extensions folder here inside Directus. So, let's just blow this whole thing up and do that. My Directus project CD extensions slash external ID. Why does that not work? CD extensions. What is my directory? Oh, somehow I'm inside Directus. I could do that. CD extensions, external ID. Okay. And if I open this up, it's just an index dot ts file whenever the item is created. Let's pull up just something from my clipboard. Where are you? Okay. Cool. So this is a pattern that I've used quite a bit before. We're just gonna call this categories. Right? And we're gonna erase nano ID. We don't really need that. Cool. And we'll say, what, fetch? We could do a fetch call in here. Alright. Constant ID. You know what? Let's just let's keep it simple. Nano ID. Cool. So what this does, right, we define this hook, and we'll go into we see this filter that's on items dot create, and then we're passing in the payload, our schema from the direct us. And, we've got our accountability object, which we're not using at all here. But then we've got this constant that says, okay. These collections have to have a public ID. And in this case, I could just change it to ID. And all we're doing here is updating the payload and returning the payload. Payload has no ID. Alright. I'm gonna hit NPM build. Directus CD extensions. External ID. Cool. There we go. PMPM build. Build my extension. I just wanna show you one way that can get this done. And then I've got my directus instance. We're gonna need to restart that bad boy. So we'll hit restart. It should pick up that new extension. And fingers crossed. If we go to extensions, we could see that extension. And then I can deactivate. Let's not delete it entirely. So we'll just deactivate this flow, change the status to inactive. Cool. Got it. And now if I do this again, say test category. Fingers crossed. I know this is not gonna work. Oh, it does work. There we go. Test category. I was gonna say, I don't think I have Nano ID in installed, but maybe we're using that somewhere inside Directus itself. Cool. There it is. Couple different ways to achieve it. Hope this answers your question. Daryl, my man, thanks for joining the community. Appreciate you. Keep the questions coming. Keep the feedback coming. We love it. I'll see you next time on the community hotline. Hey, guys. Brian here for Directus. And in this video, I'm gonna show you how to use the Directus Content MCP server to organize and manage all of your assets inside your CMS. Now, this is a huge problem for any CMS, all the way back to the before WordPress times. Right? We've got a bunch of images that somebody uploads. I'll just copy paste this one in. And to beat it all, I'm just gonna rename this one image dot JPEG. Right? We're gonna download a nice little bunny that we're gonna paste in here. So we'll throw our bunny in there. Cool. Got the bunny. And let's do one more, maybe some ice cream. My kids are into ice cream, so that's what we'll do. And, again, like, one of the other nice things about Directus, I can always import from a URL instead of a, actually, a file. So there we go, right? And here in lies the problem with every asset management inside of CMS is we end up with a bunch of stuff that makes no sense, has no metadata, basically impossible to search. So q, the direct as content MCP server, and Claude. So I am going to use my prompt here, and it says add from Directus. And I'm gonna click this one that says organize images. So these prompts basically allow us to add additional details, that gets replaced using variables. And then I'm just gonna say, please organize the assets in the needs organized folder. Sorry for the slow typing. So I hit add prompt, and basically, this has just generated this. It has added my prompt here that I gave it at the end, and we've got some instructions for it already. And we'll just hit go. So what is Claude going to do? It should pull the schema from Directus. It should look at the system prompt, and then it should analyze these files, actually. So it has the ability because of the ClaudeSonic model has vision capability. It can analyze these raw images and actually analyze the content. So you could see it here. It's making a request with the raw true. That's gonna download the file. It should analyze that. And now it should go in and update the metadata for each one of these images. Now somewhere, someone in Anthropic is screaming about all of the calls that are happening here, but this is truly awesome. Right? So we could see it is updating all the metadata for me. I haven't had to go through and classify these. Now moving forward, as I just refresh the screen here and we could see what it's done for us, I've got a full title, I've got a description, I've got tags for all of these. This is gonna be easy to find this content moving forward versus just a bunch of image dot JPEG or image dot PNG or random strings of characters inside our assets. Amazing. So this is just one workflow that is possible with the direct to content MCP. Stay tuned for more. We want to take a moment towards the end of the changelog for thanking our amazing community contributors who give their time to improve the director's project. Since last month, there have been three A huge thank you to Jonas for fixing logging of invalid URL in isurlallowed, to SunGroll for adding support for non array group claims in OpenID and OAuth2, and to ABDON for updating the extensions SDK to Rollup v4. Thank you again. You can see the specific pull requests inside of the full release notes on GitHub. Lastly, we also want to take the time to thank our GitHub Sponsors of June, who kindly financially contribute to Directus' development. A huge thank you to Wayfan, Entle, Mike, Fergus, Omar, Marcus, Mission Control, Utomic, Steven, James, Nonlinear, Andreas, John, Jamiluddin, Birb, Adam, Jason, Yuya, Vincent, CK, Valentino, Jens, and Wayne. The money we are given from our GitHub sponsors goes straight back to community members who build tooling and extensions for the director's ecosystem. Thank you again for being part of that. Alright. And that is everything we have for you this month. If you are still here, thank you so much for taking the time to join us. We really appreciate it. If you've got any feedback on anything you'd like to see or any questions you have, community.directors.io, and we'll hope to see you next month. Thanks so much for joining. Have a good one, everyone. Bye.","c5f71639-2dcd-4bb2-afb0-fbdd97bd9c22",[729,730],"0e958051-a92d-4102-b399-e2958fd1f67f","b0c26bb1-d654-4dd1-9240-fea0bc4187e5",[],{"year":399,"number":687,"id":688,"episodes":733,"show":734},[690,691,692,693,694,695,696,669],{"title":297,"slug":298},{"id":695,"slug":736,"vimeo_id":737,"description":672,"tile":738,"length":739,"resources":12,"people":12,"episode_number":740,"published":741,"title":742,"video_transcript_html":743,"video_transcript_text":744,"content":12,"seo":745,"status":19,"episode_people":746,"recommendations":749,"season":750},"June-2025","1090433878","0486cf9a-d86d-467c-9907-c097982adfb8",28,11,"2025-06-12","June 2025","\u003Cp>Speaker 0: Hello, everyone. Welcome to June's version of the changelog. If you're new, I'm Beth, and I'm gonna be taking you through what we've got in store for you today. Whether you are joining us live or on demand, do let us know if you've got any questions. If you are live, then we've got the chat running.\u003C\u002Fp>\u003Cp>And, otherwise, it's community.directus.io. And if you do have any questions along the way, we'll do our best to get them answered for you. I hope you're having a great start to your day, week, and month, and we're gonna be kicking off with product updates from me this month. So I'm gonna be kicking that off now. Alright.\u003C\u002Fp>\u003Cp>For Directus 11.8 released last month, there are a bunch of smaller improvements and bug fixes, so we're not gonna spend too much time going through it, but here are a couple of highlights. We've added the ability to have a toggle variable input for the in and in filters. We fixed relation creation to files and added filters to files and image interfaces. And finally, IP, user agent, and origin are now tracked in the activity records for WebSocket activity. That's a really short overview of some of the things we've been up to this month.\u003C\u002Fp>\u003Cp>If you'd like to see the full release notes, they're available on GitHub. This month, we have six new tutorials across two themes, submitting forms using Directus and integrating the Directus visual editor. And you can find those in Next. Js, Nuxt, and SvelteKit. Go check them out.\u003C\u002Fp>\u003Cp>They're in the docs at directors.i0\u002Fdocs\u002Ftutorials. Alright. And something else that's new with us this month is the directors MCP server, which we've got a demo from Brian coming next.\u003C\u002Fp>\u003Cp>Speaker 1: Hey. What's up, guys? Brian here. And in this video, I'm gonna show you how to use AI tools like Cloud Desktop to quickly build landing pages inside Directus CMS. Alright.\u003C\u002Fp>\u003Cp>So I've got this prompt up, and this is the time that I've actually tried this prompt, so we'll see how it goes. Basically, I want Cloud Desktop to create a landing page inside my Directus instance for me. Now this is a complicated endeavor, because, one of the the huge benefits of Directus is the ability to create pages dynamically using our many to any relationships. Basically, page blocks. Right?\u003C\u002Fp>\u003Cp>So a page can be made up of a bunch of different blocks and each block with its own schema, its own data model, and its own presentation on the front end of the website. So it makes it really easy. I can also edit these things visually. But, still scaffolding out these pages, can be time consuming. There's no way around it.\u003C\u002Fp>\u003Cp>Like, we've gotta rearrange blocks on a page. So let's use Claude and this prompt that I've got set up. So I'm just gonna go here to my prompt section. We're gonna create a landing page. And the way this prompt works, basically, I've got a bunch of variables to fill in and the direct us content MCP picks up those variables that I've included in either the message or the system prompt and prompts me to fill those out.\u003C\u002Fp>\u003Cp>So, draft a landing page for a specific audience based on an idea or topic. So we're selling Directus the AI ready CMS. Cool. Desired action is going to be sign up for cloud instance. You could check this out on cloud.\u003C\u002Fp>\u003Cp>There's also a command that you can run-in our documentation to get this full CMS set up locally. Value proposition. AI plus Directus eliminates busy work and death by a thousand cuts. Descriptive tone, we wanna use professional but friendly and confident, super relatable. Alright.\u003C\u002Fp>\u003Cp>The audience that we're aiming for, technical marketers and developers. Specific pain points. Where's the pain point? Other solutions, too slow, too complicated, don't do enough. Specific solution outcome, Directus CMS is the best.\u003C\u002Fp>\u003Cp>Level of knowledge, mildly aware. Alright. This is a super detailed prompt, so I'm expecting the results to be amazing. Don't want to switch systems, migration pane, costs, etcetera. Alright.\u003C\u002Fp>\u003Cp>So this is our prompt. Basically, it has filled out all those variables for us. It's gonna send that to Claude, and we're gonna start building out this landing page. Right? So you're seeing this the time, same as me.\u003C\u002Fp>\u003Cp>What's happening behind the scenes? The Directus Content MCP server is feeding these tools to Claude. Claude is calling its system prompt, that we've set up. So, hey. You're a Directus assistant.\u003C\u002Fp>\u003Cp>Here's what you've got access to. It has understood our schema. So it understands the specific data model that it's working with. Right? And now it is working its way through the landing page.\u003C\u002Fp>\u003Cp>And here we could see it's already created the page. Right? So if I just refresh, Directus, the AI ready CMS, eliminate busy work forever. Right? So here's our page, and we don't see any blocks though.\u003C\u002Fp>\u003Cp>Right? Of course, this is also still a draft. But what's happening here is because the MCP and, by consequence of the LLM is now aware of my specific data model in Directus, it can go through and create these blocks for me that are a 100% on brand, that are a 100% correct to our data model. And let's see what it comes up with. Right?\u003C\u002Fp>\u003Cp>We got a CTA button group. It's adding buttons within that. See the live demo. Start a free cloud instance. That's our main, callout that we said.\u003C\u002Fp>\u003Cp>It's even picked up the Directus cloud registration URL just somewhere in the ether, problem agitation section. So it's doing a ton of work on my behalf here and actually creating these blocks inside Directus, and then it's gonna come back and actually add those blocks to the page. Now if you were to scaffold this out yourself, maybe you're super efficient. Yeah. It's still gonna take you a while and you're gonna have to fill out a lot of these fields.\u003C\u002Fp>\u003Cp>So now it's going to add all this for us and the end result is gonna be something that we can come back and just edit manually. Right? So here I see all the different sections. It is creating all those for us. And this is what it's all about.\u003C\u002Fp>\u003Cp>Right? I don't know about you, but in sci fi, when I was a little kid, we were promised this dream of computers doing a lot of work for us, taking our ideas and and helping us achieve the result. So here's our landing page. I'm just gonna hit refresh, And maybe we wanna actually open this in the Directus visual editor so I can start going through this personally. Alright?\u003C\u002Fp>\u003Cp>So stop fighting your CMS. Start building. Other CMSs are too slow, too complicated, and don't do enough. Get back to what matters. Creating amazing digital experiences.\u003C\u002Fp>\u003Cp>So there's our headline. Here's kind of the problem agitation. Traditional CMSs weren't built for AI powered workflows. What if your CMS actually worked with you? We've got a pricing block, and it looks like it did miss some of the format here for this, which I'm curious about.\u003C\u002Fp>\u003Cp>But, we've got this landing page all scaffolded out. I could go in and manually adjust any of this that I need. Adjust any of this content. Command s for save and see those changes live. But this, wow, that is using the Directus Content MCP to build your landing pages out for you so you can go in and tweak them so they're just perfect.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. It's super exciting. And if you want more information, Bryant has done a great post on our forum for having much more explanation than that. And if you have the tag on AI, if you're searching for it, you can find it, or I've just put the link to the post, over in comments on directors.i0\u002Ftv\u002Flive if you are joining us live. Speaking of the community forum, our next segment is again Brian with a special edition of community hotline.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. Alright. Welcome back. We are live on the Directus community hotline. I'm your host Brian Gillespie.\u003C\u002Fp>\u003Cp>Today, we are answering a question from community member, RHB. RHB, what's up, sir? Glad to have you inside the community. Let's get to your question. Thanks.\u003C\u002Fp>\u003Cp>But is there an automated way to generate API docs for my custom collections inside Directus? The open API spec that generates that Directus generates is massive, and 99% of it is system stuff I don't need. I just want clean docs for my own collections like products, customers, services, etcetera. Anyone know of a tool or script that could filter the open API spec to only include my collections? Or maybe there's a better approach entirely.\u003C\u002Fp>\u003Cp>Well, I'm not sure of a tool or a script, but I can give you a better approach. Let's dive into it. So, this is familiar to probably everyone. This is like the swagger spec. This is kind of the out of the box API documentation.\u003C\u002Fp>\u003Cp>This is just their pet store demo. But, one of the nice things about Directus, I know I say this a lot in videos, is the ability to generate this open API spec. So I could just come to server slash specs slash o a s and get the full open API spec. It's beautiful. Now, it is very comprehensive in that every single API endpoint that the current user has access to will be shown here.\u003C\u002Fp>\u003Cp>So we can kinda see which way we're leading here. The easiest way to get a filtered list for your API spec is just to use a user to grab the API spec that has lowered permissions, restricted permissions. Permissions just to the collections that we want in the spec. So how can we do that? We're gonna go into Directus settings, and I'm just gonna create an access policy for this.\u003C\u002Fp>\u003Cp>Now technically, again, just use any user that has the correct permissions that you want and generate the open API spec. But to make this super clear, we're just gonna generate a new policy called API spec, and I'm gonna add two collections to this. Just pages. We'll give all crud access. Not trying to print the page.\u003C\u002Fp>\u003Cp>We'll do posts. Great. We'll give all crud. We'll take away the shares. I don't typically use those a lot.\u003C\u002Fp>\u003Cp>Okay. So now we'll we've got this policy. We're gonna go in and create a user, and I'm just gonna call this one, surprise surprise, API spec user. Great. What do we need to actually pull this off?\u003C\u002Fp>\u003Cp>Right? We don't have to add a role. Directus 11 was really nice in that, we got permission or we got support for access policies. So we can, roll up policies up to the role level or I could just add them directly to specific users in this case. So we're just gonna add API spec.\u003C\u002Fp>\u003Cp>I'm gonna generate a token. And now I'm gonna hop over to Swagger and I'm gonna add my direct us URL. I'm going to go to this endpoint, server slash specs slash o s, and I'm gonna add a query param for access token. Now this is definitely, do what I say, don't do what I do. Adding the access token as a query param is not safe, doing it just for demonstration purposes here.\u003C\u002Fp>\u003Cp>Make sure, you know, you're passing that is an authorization header, for the bearer token. So I'm just gonna hit explore and server specs OAS. Let's try again. Grab the token, got the API spec, save, explore. Server spec, forgot an s.\u003C\u002Fp>\u003Cp>Gotta get the s in there. Okay. So now we're going to see, we'll see the authentication endpoints just because those are always available. Right? The server health endpoints.\u003C\u002Fp>\u003Cp>Okay. Great. But if I take a look now, I don't see a giant list of endpoints. I just see pages, and I just see posts. And, again, I can use Swagger to test those out, but this is the best way to filter down that list, without running extra scripts or extra processing on it.\u003C\u002Fp>\u003Cp>So RHB, I hope this is helpful for you, man. The other thing that I wanna show you, if you go into the marketplace, we've got several extensions that could get the job done for you as well potentially. So if you just search rapid in the marketplace, there's an API viewer extension, a rapid docus extension that allows you to view that open API spec without ever leaving the Directus App Studio. If you would like your question to be featured on Community Hopeline or you just have Directus questions in general, hop into our community at community.directus.io, post your question, and you may be featured on the next episode. We'll see you.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Thank you so much to Brian for both of those amazing segments. It's always a treat. As we're approaching the end of the show or towards the end, we have our final segment, which is from Carmen, and it is an episode of Translation Station.\u003C\u002Fp>\u003Cp>Speaker 2: Carmen. Suite developer educator share direct translation station. Translation station. Alright. Back to English.\u003C\u002Fp>\u003Cp>So here we've got a Directus project dedicated for storing my blog posts. Now amongst the collections, we can see posts. And I'd like to take a minute to take a look at the structure or the data model of these posts. Gonna hop over to the settings module and take a look at posts itself. So we can see a variety of fields here.\u003C\u002Fp>\u003Cp>And the ones I want to highlight are slug, which will help us build a URL to find our post in our application. Next, we've got the title and the content. Another field I want to highlight is translations. Now I've gone ahead and created some translations for our blog posts. If you want to find out how to translate your content in the data studio, check out our data studio content translations episode of Translation Station.\u003C\u002Fp>\u003Cp>Back in our data model, I also want to take a look at post translations, which is a hidden field that holds the translation data for each of our posts. You can see we've got the title and the content. Let's now take a look at the post itself. We've got the ultimate guide to rabbits, which is written in both English. Scroll down here.\u003C\u002Fp>\u003Cp>As well as French. So I'm going to show you how you can build an application to retrieve translations of your blog posts. So what we have here is an application written in Nuxt, which uses three ways of accessing our translated blog posts. One is using the Directus SDK. One is using the REST API of our Directus project.\u003C\u002Fp>\u003Cp>And the last is using the GraphQL endpoint of our Directus project. I'm gonna give you a short tour of this Nuxt application. we set up our environment, which will connect to our Directus project and proxy accordingly to go to local host 3,000 slash Directus so that we can access our data. So let's start with the SDK. I've set up a Nuxt plugin here, which instantiates a Directus client and makes it available to my Nuxt application.\u003C\u002Fp>\u003Cp>By going to SDK slash the language code of the content slash the slug of the blog post, we're going to retrieve and display the translated data for our blog post. Let's walk through this real quick. we instantiate our client and set up the retrieval of the parameters and a reference for the post itself. Then we asynchronously use the direct client to read several posts. Using the deep and filter parameters, we're going to grab the blog post that corresponds to the slug given in our URL as well as the translation itself for that locale.\u003C\u002Fp>\u003Cp>We then make available the data of the translations into the result and limit to one just for good measure. Once we have that, we grab the translation itself from that payload and display it accordingly. Let's take a look at it in action. So here we are. We've gone to local host 3,000 in my running Nuxt application slash sdk slash en u s, that's for English, slash the ultimate guide to rabbits.\u003C\u002Fp>\u003Cp>And that gives us my blog post. To view it in French, I just have to change my URL to frfr. Check it out. Now it's in French. Fantastic.\u003C\u002Fp>\u003Cp>Let's see how it's done with REST now. All right. So we won't be using our SDK client and instead we'll be using the fetch API in order to retrieve the posts that match the slug as well as the locale itself. And then retrieving as fields, the translation title, content, and the language code. Limited to one for good measure.\u003C\u002Fp>\u003Cp>Then we retrieve the translation with the correct language code from our locale. And then just like before, if there's a post, we display its content. Let's see it in action. Back in my browser, I'm going to go to rest slash en dash us slash the ultimate guide to rabbits. Access that, and there's our English blog post.\u003C\u002Fp>\u003Cp>And for French, f r dash f r. And there we go. All good and in French. One more. Let's look at GraphQL.\u003C\u002Fp>\u003Cp>So just like with the REST API, we'll be using fetch, grab the GraphQL data. we'll build our query, which will filter out the posts by slug and specifically filter out the translations by the language code alongside that translation's title and content. Then we make a request to the GraphQL endpoint of our Directus project, and then set that post value to have that translation data. Then just like before, if we have a post, we go ahead and display it. Let's have a look.\u003C\u002Fp>\u003Cp>All right. Back in our browser, let's go to graph QL slash en dash us slash the ultimate guide to rabbits. And there we go. There it is in English. Now let's try it in French.\u003C\u002Fp>\u003Cp>Wonderful. So that's the three ways we can access our translated data from our direct Us projects API. Alright. Well, there you have it. How to access your translated content from Directus using our APIs and SDKs.\u003C\u002Fp>\u003Cp>But that's not the only stop on this translation station journey we're on. If you wanna learn how to translate Directus directly or where it gets translated, how to use translation strings, or how to translate your content, check out the other episodes of Translation Station. So I hope this was helpful, and I'll see you on the next stop of Translation Station. Bye for now.\u003C\u002Fp>\u003Cp>Speaker 0: We want to take a moment towards the end of the changelog for thanking our community contributors who give their time and expertise to improve the directors project. Since last month there have been five contributors. A huge thank you to Bruno for ensuring the configured display format is applied to the Kanban layout, Mehdi for enhancing the appearance of the flow trigger buttons in the sidebar by applying their custom colors, a FAQ for ensuring the flow logs with color indications for success and failure and adding a new filter to view only failed logs, AMOS for fixing permissions handling in the files module and for adding new action events for extensions, Nico for fixing admin password, admin token, key, and secret to always be interpreted as strings. A huge thank you once again. You can see their specific pull requests inside of the full release notes on GitHub.\u003C\u002Fp>\u003Cp>Lastly, we also want to take the time to thank our GitHub sponsors of May who kindly financially contribute to Directus' development. A huge thank you to Wayfam, Hintle, Mike, Fergus, Omar, Marcus, Mission Control, Peter, Utomic, Steven, James, nonlinear, Andreas, Reza, John, Jamilinin, Barb, Adam, Jason, Barker, Yuya, Vincent, CK, Valentino, Jens, Wayne, and Avi. The money we are given from our GitHub sponsors go straight back to community members who build tooling and extensions for the direct to see ecosystem. Thank Thank you again for being part of that. Alright.\u003C\u002Fp>\u003Cp>And that is the show for this month. As always, if you've got any feedback about things you would like to see in upcoming shows, do let us know. But other than that, thank you so much for taking the time to spend with us. We really appreciate it. We hope you found this useful.\u003C\u002Fp>\u003Cp>And, yeah, take care, and we'll see you next month.\u003C\u002Fp>","Hello, everyone. Welcome to June's version of the changelog. If you're new, I'm Beth, and I'm gonna be taking you through what we've got in store for you today. Whether you are joining us live or on demand, do let us know if you've got any questions. If you are live, then we've got the chat running. And, otherwise, it's community.directus.io. And if you do have any questions along the way, we'll do our best to get them answered for you. I hope you're having a great start to your day, week, and month, and we're gonna be kicking off with product updates from me this month. So I'm gonna be kicking that off now. Alright. For Directus 11.8 released last month, there are a bunch of smaller improvements and bug fixes, so we're not gonna spend too much time going through it, but here are a couple of highlights. We've added the ability to have a toggle variable input for the in and in filters. We fixed relation creation to files and added filters to files and image interfaces. And finally, IP, user agent, and origin are now tracked in the activity records for WebSocket activity. That's a really short overview of some of the things we've been up to this month. If you'd like to see the full release notes, they're available on GitHub. This month, we have six new tutorials across two themes, submitting forms using Directus and integrating the Directus visual editor. And you can find those in Next. Js, Nuxt, and SvelteKit. Go check them out. They're in the docs at directors.i0\u002Fdocs\u002Ftutorials. Alright. And something else that's new with us this month is the directors MCP server, which we've got a demo from Brian coming next. Hey. What's up, guys? Brian here. And in this video, I'm gonna show you how to use AI tools like Cloud Desktop to quickly build landing pages inside Directus CMS. Alright. So I've got this prompt up, and this is the time that I've actually tried this prompt, so we'll see how it goes. Basically, I want Cloud Desktop to create a landing page inside my Directus instance for me. Now this is a complicated endeavor, because, one of the the huge benefits of Directus is the ability to create pages dynamically using our many to any relationships. Basically, page blocks. Right? So a page can be made up of a bunch of different blocks and each block with its own schema, its own data model, and its own presentation on the front end of the website. So it makes it really easy. I can also edit these things visually. But, still scaffolding out these pages, can be time consuming. There's no way around it. Like, we've gotta rearrange blocks on a page. So let's use Claude and this prompt that I've got set up. So I'm just gonna go here to my prompt section. We're gonna create a landing page. And the way this prompt works, basically, I've got a bunch of variables to fill in and the direct us content MCP picks up those variables that I've included in either the message or the system prompt and prompts me to fill those out. So, draft a landing page for a specific audience based on an idea or topic. So we're selling Directus the AI ready CMS. Cool. Desired action is going to be sign up for cloud instance. You could check this out on cloud. There's also a command that you can run-in our documentation to get this full CMS set up locally. Value proposition. AI plus Directus eliminates busy work and death by a thousand cuts. Descriptive tone, we wanna use professional but friendly and confident, super relatable. Alright. The audience that we're aiming for, technical marketers and developers. Specific pain points. Where's the pain point? Other solutions, too slow, too complicated, don't do enough. Specific solution outcome, Directus CMS is the best. Level of knowledge, mildly aware. Alright. This is a super detailed prompt, so I'm expecting the results to be amazing. Don't want to switch systems, migration pane, costs, etcetera. Alright. So this is our prompt. Basically, it has filled out all those variables for us. It's gonna send that to Claude, and we're gonna start building out this landing page. Right? So you're seeing this the time, same as me. What's happening behind the scenes? The Directus Content MCP server is feeding these tools to Claude. Claude is calling its system prompt, that we've set up. So, hey. You're a Directus assistant. Here's what you've got access to. It has understood our schema. So it understands the specific data model that it's working with. Right? And now it is working its way through the landing page. And here we could see it's already created the page. Right? So if I just refresh, Directus, the AI ready CMS, eliminate busy work forever. Right? So here's our page, and we don't see any blocks though. Right? Of course, this is also still a draft. But what's happening here is because the MCP and, by consequence of the LLM is now aware of my specific data model in Directus, it can go through and create these blocks for me that are a 100% on brand, that are a 100% correct to our data model. And let's see what it comes up with. Right? We got a CTA button group. It's adding buttons within that. See the live demo. Start a free cloud instance. That's our main, callout that we said. It's even picked up the Directus cloud registration URL just somewhere in the ether, problem agitation section. So it's doing a ton of work on my behalf here and actually creating these blocks inside Directus, and then it's gonna come back and actually add those blocks to the page. Now if you were to scaffold this out yourself, maybe you're super efficient. Yeah. It's still gonna take you a while and you're gonna have to fill out a lot of these fields. So now it's going to add all this for us and the end result is gonna be something that we can come back and just edit manually. Right? So here I see all the different sections. It is creating all those for us. And this is what it's all about. Right? I don't know about you, but in sci fi, when I was a little kid, we were promised this dream of computers doing a lot of work for us, taking our ideas and and helping us achieve the result. So here's our landing page. I'm just gonna hit refresh, And maybe we wanna actually open this in the Directus visual editor so I can start going through this personally. Alright? So stop fighting your CMS. Start building. Other CMSs are too slow, too complicated, and don't do enough. Get back to what matters. Creating amazing digital experiences. So there's our headline. Here's kind of the problem agitation. Traditional CMSs weren't built for AI powered workflows. What if your CMS actually worked with you? We've got a pricing block, and it looks like it did miss some of the format here for this, which I'm curious about. But, we've got this landing page all scaffolded out. I could go in and manually adjust any of this that I need. Adjust any of this content. Command s for save and see those changes live. But this, wow, that is using the Directus Content MCP to build your landing pages out for you so you can go in and tweak them so they're just perfect. Alright. It's super exciting. And if you want more information, Bryant has done a great post on our forum for having much more explanation than that. And if you have the tag on AI, if you're searching for it, you can find it, or I've just put the link to the post, over in comments on directors.i0\u002Ftv\u002Flive if you are joining us live. Speaking of the community forum, our next segment is again Brian with a special edition of community hotline. Alright. Alright. Welcome back. We are live on the Directus community hotline. I'm your host Brian Gillespie. Today, we are answering a question from community member, RHB. RHB, what's up, sir? Glad to have you inside the community. Let's get to your question. Thanks. But is there an automated way to generate API docs for my custom collections inside Directus? The open API spec that generates that Directus generates is massive, and 99% of it is system stuff I don't need. I just want clean docs for my own collections like products, customers, services, etcetera. Anyone know of a tool or script that could filter the open API spec to only include my collections? Or maybe there's a better approach entirely. Well, I'm not sure of a tool or a script, but I can give you a better approach. Let's dive into it. So, this is familiar to probably everyone. This is like the swagger spec. This is kind of the out of the box API documentation. This is just their pet store demo. But, one of the nice things about Directus, I know I say this a lot in videos, is the ability to generate this open API spec. So I could just come to server slash specs slash o a s and get the full open API spec. It's beautiful. Now, it is very comprehensive in that every single API endpoint that the current user has access to will be shown here. So we can kinda see which way we're leading here. The easiest way to get a filtered list for your API spec is just to use a user to grab the API spec that has lowered permissions, restricted permissions. Permissions just to the collections that we want in the spec. So how can we do that? We're gonna go into Directus settings, and I'm just gonna create an access policy for this. Now technically, again, just use any user that has the correct permissions that you want and generate the open API spec. But to make this super clear, we're just gonna generate a new policy called API spec, and I'm gonna add two collections to this. Just pages. We'll give all crud access. Not trying to print the page. We'll do posts. Great. We'll give all crud. We'll take away the shares. I don't typically use those a lot. Okay. So now we'll we've got this policy. We're gonna go in and create a user, and I'm just gonna call this one, surprise surprise, API spec user. Great. What do we need to actually pull this off? Right? We don't have to add a role. Directus 11 was really nice in that, we got permission or we got support for access policies. So we can, roll up policies up to the role level or I could just add them directly to specific users in this case. So we're just gonna add API spec. I'm gonna generate a token. And now I'm gonna hop over to Swagger and I'm gonna add my direct us URL. I'm going to go to this endpoint, server slash specs slash o s, and I'm gonna add a query param for access token. Now this is definitely, do what I say, don't do what I do. Adding the access token as a query param is not safe, doing it just for demonstration purposes here. Make sure, you know, you're passing that is an authorization header, for the bearer token. So I'm just gonna hit explore and server specs OAS. Let's try again. Grab the token, got the API spec, save, explore. Server spec, forgot an s. Gotta get the s in there. Okay. So now we're going to see, we'll see the authentication endpoints just because those are always available. Right? The server health endpoints. Okay. Great. But if I take a look now, I don't see a giant list of endpoints. I just see pages, and I just see posts. And, again, I can use Swagger to test those out, but this is the best way to filter down that list, without running extra scripts or extra processing on it. So RHB, I hope this is helpful for you, man. The other thing that I wanna show you, if you go into the marketplace, we've got several extensions that could get the job done for you as well potentially. So if you just search rapid in the marketplace, there's an API viewer extension, a rapid docus extension that allows you to view that open API spec without ever leaving the Directus App Studio. If you would like your question to be featured on Community Hopeline or you just have Directus questions in general, hop into our community at community.directus.io, post your question, and you may be featured on the next episode. We'll see you. Alright. Thank you so much to Brian for both of those amazing segments. It's always a treat. As we're approaching the end of the show or towards the end, we have our final segment, which is from Carmen, and it is an episode of Translation Station. Carmen. Suite developer educator share direct translation station. Translation station. Alright. Back to English. So here we've got a Directus project dedicated for storing my blog posts. Now amongst the collections, we can see posts. And I'd like to take a minute to take a look at the structure or the data model of these posts. Gonna hop over to the settings module and take a look at posts itself. So we can see a variety of fields here. And the ones I want to highlight are slug, which will help us build a URL to find our post in our application. Next, we've got the title and the content. Another field I want to highlight is translations. Now I've gone ahead and created some translations for our blog posts. If you want to find out how to translate your content in the data studio, check out our data studio content translations episode of Translation Station. Back in our data model, I also want to take a look at post translations, which is a hidden field that holds the translation data for each of our posts. You can see we've got the title and the content. Let's now take a look at the post itself. We've got the ultimate guide to rabbits, which is written in both English. Scroll down here. As well as French. So I'm going to show you how you can build an application to retrieve translations of your blog posts. So what we have here is an application written in Nuxt, which uses three ways of accessing our translated blog posts. One is using the Directus SDK. One is using the REST API of our Directus project. And the last is using the GraphQL endpoint of our Directus project. I'm gonna give you a short tour of this Nuxt application. we set up our environment, which will connect to our Directus project and proxy accordingly to go to local host 3,000 slash Directus so that we can access our data. So let's start with the SDK. I've set up a Nuxt plugin here, which instantiates a Directus client and makes it available to my Nuxt application. By going to SDK slash the language code of the content slash the slug of the blog post, we're going to retrieve and display the translated data for our blog post. Let's walk through this real quick. we instantiate our client and set up the retrieval of the parameters and a reference for the post itself. Then we asynchronously use the direct client to read several posts. Using the deep and filter parameters, we're going to grab the blog post that corresponds to the slug given in our URL as well as the translation itself for that locale. We then make available the data of the translations into the result and limit to one just for good measure. Once we have that, we grab the translation itself from that payload and display it accordingly. Let's take a look at it in action. So here we are. We've gone to local host 3,000 in my running Nuxt application slash sdk slash en u s, that's for English, slash the ultimate guide to rabbits. And that gives us my blog post. To view it in French, I just have to change my URL to frfr. Check it out. Now it's in French. Fantastic. Let's see how it's done with REST now. All right. So we won't be using our SDK client and instead we'll be using the fetch API in order to retrieve the posts that match the slug as well as the locale itself. And then retrieving as fields, the translation title, content, and the language code. Limited to one for good measure. Then we retrieve the translation with the correct language code from our locale. And then just like before, if there's a post, we display its content. Let's see it in action. Back in my browser, I'm going to go to rest slash en dash us slash the ultimate guide to rabbits. Access that, and there's our English blog post. And for French, f r dash f r. And there we go. All good and in French. One more. Let's look at GraphQL. So just like with the REST API, we'll be using fetch, grab the GraphQL data. we'll build our query, which will filter out the posts by slug and specifically filter out the translations by the language code alongside that translation's title and content. Then we make a request to the GraphQL endpoint of our Directus project, and then set that post value to have that translation data. Then just like before, if we have a post, we go ahead and display it. Let's have a look. All right. Back in our browser, let's go to graph QL slash en dash us slash the ultimate guide to rabbits. And there we go. There it is in English. Now let's try it in French. Wonderful. So that's the three ways we can access our translated data from our direct Us projects API. Alright. Well, there you have it. How to access your translated content from Directus using our APIs and SDKs. But that's not the only stop on this translation station journey we're on. If you wanna learn how to translate Directus directly or where it gets translated, how to use translation strings, or how to translate your content, check out the other episodes of Translation Station. So I hope this was helpful, and I'll see you on the next stop of Translation Station. Bye for now. We want to take a moment towards the end of the changelog for thanking our community contributors who give their time and expertise to improve the directors project. Since last month there have been five contributors. A huge thank you to Bruno for ensuring the configured display format is applied to the Kanban layout, Mehdi for enhancing the appearance of the flow trigger buttons in the sidebar by applying their custom colors, a FAQ for ensuring the flow logs with color indications for success and failure and adding a new filter to view only failed logs, AMOS for fixing permissions handling in the files module and for adding new action events for extensions, Nico for fixing admin password, admin token, key, and secret to always be interpreted as strings. A huge thank you once again. You can see their specific pull requests inside of the full release notes on GitHub. Lastly, we also want to take the time to thank our GitHub sponsors of May who kindly financially contribute to Directus' development. A huge thank you to Wayfam, Hintle, Mike, Fergus, Omar, Marcus, Mission Control, Peter, Utomic, Steven, James, nonlinear, Andreas, Reza, John, Jamilinin, Barb, Adam, Jason, Barker, Yuya, Vincent, CK, Valentino, Jens, Wayne, and Avi. The money we are given from our GitHub sponsors go straight back to community members who build tooling and extensions for the direct to see ecosystem. Thank Thank you again for being part of that. Alright. And that is the show for this month. As always, if you've got any feedback about things you would like to see in upcoming shows, do let us know. But other than that, thank you so much for taking the time to spend with us. We really appreciate it. We hope you found this useful. And, yeah, take care, and we'll see you next month.","206ccef0-bd71-4595-aaec-4bd213a3653b",[747,748],"4c96fc0e-9390-4421-bf97-af82e87fe07f","aa6dfd6c-f2b9-4620-a427-952b2c0e42b1",[],{"year":399,"number":687,"id":688,"episodes":751,"show":752},[690,691,692,693,694,695,696,669],{"title":297,"slug":298},{"id":754,"slug":755,"vimeo_id":756,"description":757,"tile":758,"length":12,"resources":12,"people":12,"episode_number":213,"published":759,"title":760,"video_transcript_html":761,"video_transcript_text":762,"content":763,"seo":12,"status":19,"episode_people":764,"recommendations":766,"season":767},"ddb03a95-6ef0-4232-9f57-7c3e454ef41f","publishing-blog-post-from-google-docs","1088809284","Master AI-assisted blog post creation with the Directus MCP Server. Learn how to streamline your content creation process, from initial concept to published post, using AI collaboration to maintain quality while saving time.","d3381a42-8c83-4adf-9985-5575f30719c8","2025-05-29","Publishing Blog Posts from Google Docs","\u003Cp>Speaker 0: In this video, I'm going to show you how to use the Directus Content MCP server to upload blog posts to your CMS without any human intervention. This is gonna be so nice. Alright. So if you've ever been in content operations, you'll know that at some point, all roads lead to, from, around, in between Google Docs. Somebody drafts a piece of content, it winds up in a Google Doc, this is how we do all of our editing, suggesting, all of that.\u003C\u002Fp>\u003Cp>Now, lots of problems with this workflow though. This has gotta make its way into a CMS at some point. So, somebody, probably you, gets tasked with filling out a bunch of different fields that you've got set up in your CMS and pasting this content. Now you've been here before. This sucks because Google Docs is adding a bunch of HTML formatting that we don't really need and it's gonna take me longer to clean up than actually to copy and paste this stuff.\u003C\u002Fp>\u003Cp>So what's the better way, Bryant? Well, I'm about to show you. Let's take a look. I've got the Directus MCP server set up with Cloud Desktop. I've got the Google Drive integration already added to Cloud Desktop, super easy to add.\u003C\u002Fp>\u003Cp>And we're just gonna lean on our friend, Cloud here. Hey. Will you please, always say please, add this as a draft post in my Directus CMS. Alright. So what is happening behind the scenes?\u003C\u002Fp>\u003Cp>Claude should have access to my Directus instance. It's going to, call a system prompt and get the information that it needs to, help us. Then it should reach out and read the schema of our direct assistance. So no matter what schema you have, whether you have six different fields here, maybe yours is called the name of this post versus the title. The MCP server picks that up directly from your Directus instance, communicates that back to Claude.\u003C\u002Fp>\u003Cp>So now we can see that he is putting together a post collection. Here's a draft post for our Google Doc. Cool. Looks like he is thinking about this, and we'll just, speed this up a little bit while we wait. Alright.\u003C\u002Fp>\u003Cp>So now we can see that Claude is generating an item here, and we could see we've got a title. We've got a slug. We've got a description. We've got, a draft status. Then we've got our actual content, which it has correctly set up inside, HTML tags.\u003C\u002Fp>\u003Cp>Despite, whatever format came out of Google Docs, right, we don't see any stray formatting that is going to mess up our page. So once he gets done with all of this, we should see a post inside our CMS. So here's the summary. I can click on this and open this link up. Now we can see we have this post.\u003C\u002Fp>\u003Cp>We can see our content, and because I've got Directus set up with Live Preview, I can actually preview my content before it goes live, which is nice. But I also see that an image is missing, so, let's ask Claude to look for one that's appropriate. Can you please look through our directus assets and find an appropriate image. Gosh. I can't spell.\u003C\u002Fp>\u003Cp>Alright. So now, again, one of the tools that the Directus Content MCP server has is the ability to access your files. Here are the most relevant options. We got UI design wireframing, a modern UI dashboard. K.\u003C\u002Fp>\u003Cp>Let's go with that. Alright. And cool. So now it's updated our blog post for us with the image. Cool.\u003C\u002Fp>\u003Cp>So there's our blog post with our content and our featured image, and that is just one workflow that is possible with the Directus Content MCP. Stay tuned for more videos in this series. Seriously, don't miss these. We'll see you next.\u003C\u002Fp>","In this video, I'm going to show you how to use the Directus Content MCP server to upload blog posts to your CMS without any human intervention. This is gonna be so nice. Alright. So if you've ever been in content operations, you'll know that at some point, all roads lead to, from, around, in between Google Docs. Somebody drafts a piece of content, it winds up in a Google Doc, this is how we do all of our editing, suggesting, all of that. Now, lots of problems with this workflow though. This has gotta make its way into a CMS at some point. So, somebody, probably you, gets tasked with filling out a bunch of different fields that you've got set up in your CMS and pasting this content. Now you've been here before. This sucks because Google Docs is adding a bunch of HTML formatting that we don't really need and it's gonna take me longer to clean up than actually to copy and paste this stuff. So what's the better way, Bryant? Well, I'm about to show you. Let's take a look. I've got the Directus MCP server set up with Cloud Desktop. I've got the Google Drive integration already added to Cloud Desktop, super easy to add. And we're just gonna lean on our friend, Cloud here. Hey. Will you please, always say please, add this as a draft post in my Directus CMS. Alright. So what is happening behind the scenes? Claude should have access to my Directus instance. It's going to, call a system prompt and get the information that it needs to, help us. Then it should reach out and read the schema of our direct assistance. So no matter what schema you have, whether you have six different fields here, maybe yours is called the name of this post versus the title. The MCP server picks that up directly from your Directus instance, communicates that back to Claude. So now we can see that he is putting together a post collection. Here's a draft post for our Google Doc. Cool. Looks like he is thinking about this, and we'll just, speed this up a little bit while we wait. Alright. So now we can see that Claude is generating an item here, and we could see we've got a title. We've got a slug. We've got a description. We've got, a draft status. Then we've got our actual content, which it has correctly set up inside, HTML tags. Despite, whatever format came out of Google Docs, right, we don't see any stray formatting that is going to mess up our page. So once he gets done with all of this, we should see a post inside our CMS. So here's the summary. I can click on this and open this link up. Now we can see we have this post. We can see our content, and because I've got Directus set up with Live Preview, I can actually preview my content before it goes live, which is nice. But I also see that an image is missing, so, let's ask Claude to look for one that's appropriate. Can you please look through our directus assets and find an appropriate image. Gosh. I can't spell. Alright. So now, again, one of the tools that the Directus Content MCP server has is the ability to access your files. Here are the most relevant options. We got UI design wireframing, a modern UI dashboard. K. Let's go with that. Alright. And cool. So now it's updated our blog post for us with the image. Cool. So there's our blog post with our content and our featured image, and that is just one workflow that is possible with the Directus Content MCP. Stay tuned for more videos in this series. Seriously, don't miss these. We'll see you next.","\u003Cp>Discover how to revolutionize your blog post creation workflow with AI assistance. This episode covers:\u003C\u002Fp>\u003Cul>\u003Cli>Setting up blog post templates and prompts in Directus\u003C\u002Fli>\u003Cli>Using AI to generate initial content drafts\u003C\u002Fli>\u003Cli>Collaborative editing and refinement techniques\u003C\u002Fli>\u003Cli>Automating metadata and SEO optimization\u003C\u002Fli>\u003Cli>Publishing workflows and content review processes\u003C\u002Fli>\u003C\u002Ful>\u003Cp>Transform your content creation process from hours to minutes while maintaining editorial quality and brand consistency.\u003C\u002Fp>",[765],"edadb3e5-ac11-462b-a055-556904de744f",[],{"year":768,"number":769,"id":770,"episodes":771,"show":779},"Local MCP",20,"e6fd55a1-6730-40ee-b3c8-a7820506f0f6",[772,773,774,775,754,776,777,778],"788bb059-2be8-4b97-a1cc-533d4b0eebad","284d3147-4ba3-4f84-a036-1eb8558eaa15","9f382043-e5a9-49db-b891-46125c0d4daa","f7c2f24f-6a07-43c7-91fc-dfafa6624d4d","60fd26d0-634d-4709-9ff7-aa1021aaf3c2","29419e22-2c4e-4713-991b-ceb1ef021dc6","ad48fa0e-e2fc-479b-b720-633051d21ab4",{"title":383,"slug":384},{"id":775,"slug":781,"vimeo_id":782,"description":783,"tile":784,"length":213,"resources":12,"people":12,"episode_number":159,"published":759,"title":785,"video_transcript_html":786,"video_transcript_text":787,"content":788,"seo":12,"status":19,"episode_people":789,"recommendations":791,"season":792},"raycast-installation","1088793378","Connect Directus to Raycast using the MCP Server for quick AI-powered content management from your macOS launcher. Learn how to install, configure, and use the Directus MCP Server with Raycast for efficient workflow automation.","0c0b3712-9357-452f-81e0-0dcaf76f606d","Raycast Installation","\u003Cp>Speaker 0: Hey. What's up, guys? Brian here from Directus. In this video, I'm gonna show you how to install the Directus Content MCP server inside Raycast. So Raycast, recently added support for MCP servers and a couple things that we're gonna wanna do.\u003C\u002Fp>\u003Cp>Right? If you don't have Node. Js already installed on your computer, you're gonna wanna do that. So if you're a non developer Node. Js is just a JavaScript runtime that allows you to run these MCP servers locally, which is the safest way to do so as of the time of this video, and you will need it to actually run the MCP servers.\u003C\u002Fp>\u003Cp>So you can go to nodejs.org\u002Fdownload or just search that up in Google. Make sure you download and install Node. Now next, if we look at Raycast, their documentation has a nice little pro tip. So I'm just gonna go to our Directus MCP server documentation. I'm gonna copy this code, and I'm gonna open up Raycast and hit install server.\u003C\u002Fp>\u003Cp>Now because I've already got this JSON copied, it is basically picking that up inside Raycast, and all I have to do is go to my Directus instance and create a token and grab the URL. So I'm gonna open that up, and I go into my Directus Instance. If you're on Directus Cloud, it will look like this, .directus.app. If you are self hosting Directus, obviously, it's gonna be at your URL. So I'm just gonna go in.\u003C\u002Fp>\u003Cp>I'm gonna hit, create a token under my specific user, and I'm gonna make sure that I save that. And then I'm just gonna open Raycast up again, paste that token in here, and then I'm also gonna copy and paste just the base URL of my Directus instance. We'll hit that, and then I'm gonna do command enter to install that MCP server. And now once that's done can I actually drag this to the middle? Is there a way to I don't guess I can make that any bigger.\u003C\u002Fp>\u003Cp>But, what I could do now is just hit at Directus, and I can have a conversation with my Directus instance. You know, how many files are in my Directus CMS? Now using this on Raycast, it will often prompt you to allow tool access. You'll have to use, like, the command key, command enter to work, But you can see here it is calling read files, and we can see there's 44 files in our CMS. So you're probably going to want to experiment with all the different models that are available under Raycast, but one tip that I can give you is go to the Raycast settings, and under your extensions, make sure you search for MCP.\u003C\u002Fp>\u003Cp>And where it says Ask Directus once you've installed this, add a custom instruction that says, make sure you always call the System Prompt tool first. This is just based on my own testing with the Raycast MCP. You know, using clawed desktop, this tool, like, it does better with the tool calling than I've observed here. So if I go in and I make this change, typically, we'll get the behavior that we want now. Or we say, okay.\u003C\u002Fp>\u003Cp>How many blog posts are there? Okay. So now we could see it is running that system prompt, and it's prompt us for the ability to run that tool. And this is kind of the logical progression we should see where it says system prompt, then it reads the collections, and then it will pick up the schema from that, and then we can read our items. So how many of those are about rabbits?\u003C\u002Fp>\u003Cp>I think most of them are, but we'll see what this comes up with. There are three blog posts about rabbits. Right? Excellent. Alright.\u003C\u002Fp>\u003Cp>So that is the Directus MCP connecting that with Raycast. Make sure you stay tuned for more videos on the Directus MCP server so so you can figure out the best use cases and, the ways to use this tool. We'll catch you on the next video.\u003C\u002Fp>","Hey. What's up, guys? Brian here from Directus. In this video, I'm gonna show you how to install the Directus Content MCP server inside Raycast. So Raycast, recently added support for MCP servers and a couple things that we're gonna wanna do. Right? If you don't have Node. Js already installed on your computer, you're gonna wanna do that. So if you're a non developer Node. Js is just a JavaScript runtime that allows you to run these MCP servers locally, which is the safest way to do so as of the time of this video, and you will need it to actually run the MCP servers. So you can go to nodejs.org\u002Fdownload or just search that up in Google. Make sure you download and install Node. Now next, if we look at Raycast, their documentation has a nice little pro tip. So I'm just gonna go to our Directus MCP server documentation. I'm gonna copy this code, and I'm gonna open up Raycast and hit install server. Now because I've already got this JSON copied, it is basically picking that up inside Raycast, and all I have to do is go to my Directus instance and create a token and grab the URL. So I'm gonna open that up, and I go into my Directus Instance. If you're on Directus Cloud, it will look like this, .directus.app. If you are self hosting Directus, obviously, it's gonna be at your URL. So I'm just gonna go in. I'm gonna hit, create a token under my specific user, and I'm gonna make sure that I save that. And then I'm just gonna open Raycast up again, paste that token in here, and then I'm also gonna copy and paste just the base URL of my Directus instance. We'll hit that, and then I'm gonna do command enter to install that MCP server. And now once that's done can I actually drag this to the middle? Is there a way to I don't guess I can make that any bigger. But, what I could do now is just hit at Directus, and I can have a conversation with my Directus instance. You know, how many files are in my Directus CMS? Now using this on Raycast, it will often prompt you to allow tool access. You'll have to use, like, the command key, command enter to work, But you can see here it is calling read files, and we can see there's 44 files in our CMS. So you're probably going to want to experiment with all the different models that are available under Raycast, but one tip that I can give you is go to the Raycast settings, and under your extensions, make sure you search for MCP. And where it says Ask Directus once you've installed this, add a custom instruction that says, make sure you always call the System Prompt tool first. This is just based on my own testing with the Raycast MCP. You know, using clawed desktop, this tool, like, it does better with the tool calling than I've observed here. So if I go in and I make this change, typically, we'll get the behavior that we want now. Or we say, okay. How many blog posts are there? Okay. So now we could see it is running that system prompt, and it's prompt us for the ability to run that tool. And this is kind of the logical progression we should see where it says system prompt, then it reads the collections, and then it will pick up the schema from that, and then we can read our items. So how many of those are about rabbits? I think most of them are, but we'll see what this comes up with. There are three blog posts about rabbits. Right? Excellent. Alright. So that is the Directus MCP connecting that with Raycast. Make sure you stay tuned for more videos on the Directus MCP server so so you can figure out the best use cases and, the ways to use this tool. We'll catch you on the next video.","\u003Cp>Learn how to set up the Directus MCP Server with Raycast for lightning-fast content management. This episode demonstrates:\u003C\u002Fp>\u003Cul>\u003Cli>Installing Raycast on macOS\u003C\u002Fli>\u003Cli>Adding the Directus MCP Server to Raycast\u003C\u002Fli>\u003Cli>Configuring connection settings and authentication\u003C\u002Fli>\u003Cli>Using @directus commands for quick content operations\u003C\u002Fli>\u003Cli>Custom instructions for optimal performance\u003C\u002Fli>\u003C\u002Ful>\u003Cp>Ideal for macOS users who want instant access to Directus content management through their system launcher.\u003C\u002Fp>",[790],"8cbdbcc3-36ad-47c0-adfc-ba5c8725b83d",[],{"year":768,"number":769,"id":770,"episodes":793,"show":794},[772,773,774,775,754,776,777,778],{"title":383,"slug":384},{"id":774,"slug":796,"vimeo_id":797,"description":798,"tile":799,"length":159,"resources":12,"people":12,"episode_number":264,"published":759,"title":800,"video_transcript_html":801,"video_transcript_text":802,"content":803,"seo":12,"status":19,"episode_people":804,"recommendations":806,"season":807},"cursor-installation","1088793312","Set up the Directus MCP Server with Cursor IDE for AI-powered development workflows. This episode covers the complete installation process and configuration steps to integrate Directus with Cursor for enhanced coding and content management.","79677377-c185-43c9-8d1d-25086361c6ef","Cursor Installation","\u003Cp>Speaker 0: Hey. What's up, guys? Brian here at Directus. In this video, I'm gonna show you how to install the Directus Content MCP into Cursor. So Cursor supports two different ways to install MCP servers, and we're gonna show you what I actually prefer.\u003C\u002Fp>\u003Cp>Now before we start, you're gonna obviously make sure that you have Node. Js installed on your computer. If you're already running Cursor, there's a good chance you already have Node installed, but, again, just to run these MCP servers locally, you will wanna make sure that you've got that installed. You could go to nodejs.org slash download, just to pick that up. So, if we take a look, the, configuration locations for Cursor.\u003C\u002Fp>\u003Cp>I highly recommend the project configuration which, basically we create a Cursor directory and add m c p dot json. Now you could do globally, but if you are working with several different Directus projects, you're probably gonna want that to be, per project. So we'll create a new folder in cursor. We'll just call it dot cursor and we'll create a new file called MCP dot JSON. Now the next thing that we're gonna do is just load up the standard configuration here.\u003C\u002Fp>\u003Cp>I'm pulling this from the repo. You can grab it from the docs. We'll copy the configuration. We've got MCP servers, then we have Directus inside here. We've got our commands and our arguments.\u003C\u002Fp>\u003Cp>And then the two pieces that you are going to supply is your Directus URL and your Directus token. You can also use username and password here. All that is detailed in the documentation or the read me. So for this I'm just going to add my directus URL, simplecmsstarter.directus.app. If you're on directus cloud, you will have this format, directus.app.\u003C\u002Fp>\u003Cp>If you are self hosting, obviously, that's gonna be your own URL. The next piece of the puzzle here, I'm gonna go in to my specific user. I'm gonna scroll down the page to admin token. I'm gonna generate a static token to use here. Hit save.\u003C\u002Fp>\u003Cp>Make sure you save. That's important. And I'm just going to paste that token here. So now once I'm done, hit save. This should enable the MCP server, and now I can go in over here when I chat with this.\u003C\u002Fp>\u003Cp>Make sure you're on agent mode, And I don't know the specific models. Let's just try Claude Sonnet three five. What does my Directus schema look like? Alright. So fingers crossed, this should, prompt some tool calls like the system prompt to get information about it, and then it will call read collections to pick up the schema.\u003C\u002Fp>\u003Cp>Alright. And away we go. So, you know, you could potentially use this to update content, but the content MCP server does not have destructive actions enabled like creating collections or dropping collections or, anything that could harm your data. Alright. That's it for installing one of the other things that you may want to look at inside the cursor settings.\u003C\u002Fp>\u003Cp>So if we go to cursor settings, we go to features, there is a protection for, chat, like agents running tools. Where is this at? MC tool MCP Tools Protection, you may want to enable this, you know, in case, you don't want it to automatically run these tools. So by default, I think this is checked by default, maybe it's not. Make sure that you look for this if you want to be aware and approve all the individual tool calls.\u003C\u002Fp>\u003Cp>Again, the Content MCP does not have any destructive actions enabled by default, but could be an extra piece of protection that you look at. So that is it for this video. Stay tuned for more on the MCP.\u003C\u002Fp>","Hey. What's up, guys? Brian here at Directus. In this video, I'm gonna show you how to install the Directus Content MCP into Cursor. So Cursor supports two different ways to install MCP servers, and we're gonna show you what I actually prefer. Now before we start, you're gonna obviously make sure that you have Node. Js installed on your computer. If you're already running Cursor, there's a good chance you already have Node installed, but, again, just to run these MCP servers locally, you will wanna make sure that you've got that installed. You could go to nodejs.org slash download, just to pick that up. So, if we take a look, the, configuration locations for Cursor. I highly recommend the project configuration which, basically we create a Cursor directory and add m c p dot json. Now you could do globally, but if you are working with several different Directus projects, you're probably gonna want that to be, per project. So we'll create a new folder in cursor. We'll just call it dot cursor and we'll create a new file called MCP dot JSON. Now the next thing that we're gonna do is just load up the standard configuration here. I'm pulling this from the repo. You can grab it from the docs. We'll copy the configuration. We've got MCP servers, then we have Directus inside here. We've got our commands and our arguments. And then the two pieces that you are going to supply is your Directus URL and your Directus token. You can also use username and password here. All that is detailed in the documentation or the read me. So for this I'm just going to add my directus URL, simplecmsstarter.directus.app. If you're on directus cloud, you will have this format, directus.app. If you are self hosting, obviously, that's gonna be your own URL. The next piece of the puzzle here, I'm gonna go in to my specific user. I'm gonna scroll down the page to admin token. I'm gonna generate a static token to use here. Hit save. Make sure you save. That's important. And I'm just going to paste that token here. So now once I'm done, hit save. This should enable the MCP server, and now I can go in over here when I chat with this. Make sure you're on agent mode, And I don't know the specific models. Let's just try Claude Sonnet three five. What does my Directus schema look like? Alright. So fingers crossed, this should, prompt some tool calls like the system prompt to get information about it, and then it will call read collections to pick up the schema. Alright. And away we go. So, you know, you could potentially use this to update content, but the content MCP server does not have destructive actions enabled like creating collections or dropping collections or, anything that could harm your data. Alright. That's it for installing one of the other things that you may want to look at inside the cursor settings. So if we go to cursor settings, we go to features, there is a protection for, chat, like agents running tools. Where is this at? MC tool MCP Tools Protection, you may want to enable this, you know, in case, you don't want it to automatically run these tools. So by default, I think this is checked by default, maybe it's not. Make sure that you look for this if you want to be aware and approve all the individual tool calls. Again, the Content MCP does not have any destructive actions enabled by default, but could be an extra piece of protection that you look at. So that is it for this video. Stay tuned for more on the MCP.","\u003Cp>Discover how to install and configure the Directus MCP Server with Cursor IDE. This tutorial covers:\u003C\u002Fp>\u003Cul>\u003Cli>Installing Cursor IDE\u003C\u002Fli>\u003Cli>Creating the MCP configuration file\u003C\u002Fli>\u003Cli>Setting up environment variables for Directus connection\u003C\u002Fli>\u003Cli>Verifying the server connection status\u003C\u002Fli>\u003Cli>Testing AI-assisted development workflows\u003C\u002Fli>\u003C\u002Ful>\u003Cp>Perfect for developers who want to leverage AI assistance directly in their code editor while working with Directus projects.\u003C\u002Fp>",[805],"0c11261c-4d40-4119-9556-3e7e1ecd36ba",[],{"year":768,"number":769,"id":770,"episodes":808,"show":809},[772,773,774,775,754,776,777,778],{"title":383,"slug":384},{"id":773,"slug":811,"vimeo_id":812,"description":813,"tile":814,"length":213,"resources":12,"people":12,"episode_number":177,"published":759,"title":815,"video_transcript_html":816,"video_transcript_text":817,"content":818,"seo":12,"status":19,"episode_people":819,"recommendations":821,"season":822},"claude-desktop-mcp-installation","1088793423","Learn how to install and configure the Directus MCP Server with Claude Desktop. This episode walks through the complete setup process, from getting your Directus credentials to configuring the MCP server for seamless AI-powered content management.","1e0065a8-611f-416e-8aeb-c04bfb6d7c12","Claude Desktop Installation","\u003Cp>Speaker 0: Hey. What's up, guys? Brian here from Directus. In this video, I'm gonna show you how to set up the Directus MCP server with clawed desktop. So first and foremost, before we can start working with our direct assistance using the MCP server, we have to install cloud desktop.\u003C\u002Fp>\u003Cp>So if you don't have that already, you're gonna wanna download the cloud desktop version. This doesn't work with the website. It has to be desktop. So cloud.ai\u002Fdownload, get that set up, and then come back. The next thing that you're gonna want to have installed is Node JS.\u003C\u002Fp>\u003Cp>This is a JavaScript runtime that basically allows the MCP server to run locally on your computer, which is the most secure way to run MCP servers at this time. So if you go to nodejs.org\u002Fen or your language code slash download or just Google install Node, you should get this page, and it should be preconfigured for your specific computer, whether you're on Mac or Windows, but make sure that you've got this thing installed. If you don't, hit pause, go install it, come back to the video. Now with both of those pieces installed, the next thing you're gonna do is open up Claude desktop and go to the settings from the file menu. That's gonna give you a dialogue that looks something like this.\u003C\u002Fp>\u003Cp>You'll see we have general, we've got give feedback, etcetera. You might see something like cloud settings here, but what we're looking for is the developer tab, and then I'm gonna hit edit config. So this is gonna give me a pointer to this file called claudesktopconfig dot json. And if you're not a developer, don't worry, json might look scary but it is not, I assure you. All we're gonna do here is just tell Claude about our Directus MCP server.\u003C\u002Fp>\u003Cp>So for now let's just open this up and I'll put these side by side. Any text editor will do. What we're gonna do, we're just gonna copy and paste this code from the documentation for our Directus MCP server. So you could see we've got MCP server here, which is just a a key. Inside that object, we are going to copy Directus MCP.\u003C\u002Fp>\u003Cp>Cool. We're just gonna paste that in there. Now there's a a couple things that you'll see breaking this down. We got the command and then we have the arguments. This is just to run the actual MCP server.\u003C\u002Fp>\u003Cp>And then we have this e n v thing. So this is where we set up our specific configuration. And what we're gonna do here, we're gonna now open up our directus instance and I'm gonna go to my user account and I'm going to create a token. So I'll go down to admin options, I'm gonna hit token, I'm gonna create this token, and then I'm going to save the user. Make sure you copy that token, paste it into the configuration here, And then the last thing that you're gonna do is grab the URL for your Directus instance.\u003C\u002Fp>\u003Cp>So if you're on Directus cloud, like our hosting, this will look like .directus.app. If you are self hosting directus, this is obviously gonna be hosted at your own domain. Now I'm just gonna hit save or command s or, you know, I can go to file, save as here, but I'm gonna save that file. And The next thing I'm gonna do, I'm just gonna quit Claw Desktop and I'm gonna reopen Claw Desktop. And if I've done all those steps properly, I can open up the tool menu and I now see Directus and it has all the tools that I have access to.\u003C\u002Fp>\u003Cp>And if I have prompts enabled, I can surely see those from here as well. So the MCP server does have some extra configuration that you can add. If you want to enable the prompts collection, which allows you to store and reuse prompts in Cloud Desktop, there is configuration here. Just copy and paste that in from the actual documentation. Follow the same procedure.\u003C\u002Fp>\u003Cp>Close Cloud, open Cloud, and we should see our tools there. And now I can see some prompts that we can reuse over and over again for different work. So now that we've got everything connected, we could see all of our tools, leverage our different prompts. Let's just actually test this out. Right?\u003C\u002Fp>\u003Cp>How many files are in my Directus instance? This should make a tool call and query the individual files. One of the nice things that I like about Cloud Desktop is that the, tool calls do show the request and the response, so you could see that. And then we get the summary here. So that is connecting Cloud Desktop to the Directus MCP server.\u003C\u002Fp>\u003Cp>Make sure you stay tuned for more videos on the MCP of how to actually use this in your day to day workflows.\u003C\u002Fp>","Hey. What's up, guys? Brian here from Directus. In this video, I'm gonna show you how to set up the Directus MCP server with clawed desktop. So first and foremost, before we can start working with our direct assistance using the MCP server, we have to install cloud desktop. So if you don't have that already, you're gonna wanna download the cloud desktop version. This doesn't work with the website. It has to be desktop. So cloud.ai\u002Fdownload, get that set up, and then come back. The next thing that you're gonna want to have installed is Node JS. This is a JavaScript runtime that basically allows the MCP server to run locally on your computer, which is the most secure way to run MCP servers at this time. So if you go to nodejs.org\u002Fen or your language code slash download or just Google install Node, you should get this page, and it should be preconfigured for your specific computer, whether you're on Mac or Windows, but make sure that you've got this thing installed. If you don't, hit pause, go install it, come back to the video. Now with both of those pieces installed, the next thing you're gonna do is open up Claude desktop and go to the settings from the file menu. That's gonna give you a dialogue that looks something like this. You'll see we have general, we've got give feedback, etcetera. You might see something like cloud settings here, but what we're looking for is the developer tab, and then I'm gonna hit edit config. So this is gonna give me a pointer to this file called claudesktopconfig dot json. And if you're not a developer, don't worry, json might look scary but it is not, I assure you. All we're gonna do here is just tell Claude about our Directus MCP server. So for now let's just open this up and I'll put these side by side. Any text editor will do. What we're gonna do, we're just gonna copy and paste this code from the documentation for our Directus MCP server. So you could see we've got MCP server here, which is just a a key. Inside that object, we are going to copy Directus MCP. Cool. We're just gonna paste that in there. Now there's a a couple things that you'll see breaking this down. We got the command and then we have the arguments. This is just to run the actual MCP server. And then we have this e n v thing. So this is where we set up our specific configuration. And what we're gonna do here, we're gonna now open up our directus instance and I'm gonna go to my user account and I'm going to create a token. So I'll go down to admin options, I'm gonna hit token, I'm gonna create this token, and then I'm going to save the user. Make sure you copy that token, paste it into the configuration here, And then the last thing that you're gonna do is grab the URL for your Directus instance. So if you're on Directus cloud, like our hosting, this will look like .directus.app. If you are self hosting directus, this is obviously gonna be hosted at your own domain. Now I'm just gonna hit save or command s or, you know, I can go to file, save as here, but I'm gonna save that file. And The next thing I'm gonna do, I'm just gonna quit Claw Desktop and I'm gonna reopen Claw Desktop. And if I've done all those steps properly, I can open up the tool menu and I now see Directus and it has all the tools that I have access to. And if I have prompts enabled, I can surely see those from here as well. So the MCP server does have some extra configuration that you can add. If you want to enable the prompts collection, which allows you to store and reuse prompts in Cloud Desktop, there is configuration here. Just copy and paste that in from the actual documentation. Follow the same procedure. Close Cloud, open Cloud, and we should see our tools there. And now I can see some prompts that we can reuse over and over again for different work. So now that we've got everything connected, we could see all of our tools, leverage our different prompts. Let's just actually test this out. Right? How many files are in my Directus instance? This should make a tool call and query the individual files. One of the nice things that I like about Cloud Desktop is that the, tool calls do show the request and the response, so you could see that. And then we get the summary here. So that is connecting Cloud Desktop to the Directus MCP server. Make sure you stay tuned for more videos on the MCP of how to actually use this in your day to day workflows.","\u003Cp>In this episode, we'll guide you through installing the Directus MCP Server with Claude Desktop. You'll learn how to:\u003C\u002Fp>\u003Cul>\u003Cli>Download and set up Claude Desktop\u003C\u002Fli>\u003Cli>Get your Directus credentials and static access token\u003C\u002Fli>\u003Cli>Configure the MCP server in Claude Desktop settings\u003C\u002Fli>\u003Cli>Test the connection and verify everything is working\u003C\u002Fli>\u003C\u002Ful>\u003Cp>By the end of this tutorial, you'll have Claude Desktop connected to your Directus instance, ready to help with content creation and management tasks.\u003C\u002Fp>",[820],"fdb7848e-d7f3-44ec-92a6-ae82ed12d81e",[],{"year":768,"number":769,"id":770,"episodes":823,"show":824},[772,773,774,775,754,776,777,778],{"title":383,"slug":384},{"id":772,"slug":826,"vimeo_id":827,"description":828,"tile":829,"length":830,"resources":12,"people":12,"episode_number":13,"published":759,"title":831,"video_transcript_html":832,"video_transcript_text":833,"content":834,"seo":12,"status":19,"episode_people":835,"recommendations":838,"season":839},"real-life-ai-content-workflows-directus-mcp","1088750836","Join this comprehensive workshop exploring real-world AI content workflows with the Directus MCP Server. See practical examples, best practices, and advanced techniques for integrating AI into your daily content management processes.","a2c6a2b0-b199-4ce9-a4be-ad480e2e4cf3",48,"Real-Life AI Content Workflows with Directus MCP Workshop","\u003Cp>Speaker 0: We are live.\u003C\u002Fp>\u003Cp>Speaker 1: Hang on. I got something for this. Sorry.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Let's let's kick this thing off, baby.\u003C\u002Fp>\u003Cp>Speaker 1: Nice. Got a lot of folks in here.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Well, Matt, you wanna do the honors? You want me to? What do we how are we kicking this thing off?\u003C\u002Fp>\u003Cp>Speaker 1: Oh, yeah. Alright. We're going straight into it. Love it. Yeah.\u003C\u002Fp>\u003Cp>So hey, everybody. Welcome to our, Real Live Content AI workflows, directed some TP event. My name is Matt. Bryant is sitting in the copilot chair. I guess you're more of the pilot because you're controlling really everything.\u003C\u002Fp>\u003Cp>I'm just gonna sit here. But, yeah, great to see everybody. You might be seeing a couple people drop in where they're from. That's kind of the classic session workshop standby to open things. So cool to see, like, international representation in there.\u003C\u002Fp>\u003Cp>Cool. You wanna kick things off, Brian? Yeah. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: So, over the last, I don't know. It's been six weeks at this point. We've, like, been working on this MCP. This is the launch event for that. But as we kick things off, definitely let us know in the chat who you are, where you're from, what you're building.\u003C\u002Fp>\u003Cp>We've got, like, a a few short slides we're gonna dive into, and then we will showcase these actual workflows that you can take and use right away. So I'm super excited for that. I'm sure we'll forget something along the way, but we'll have some fun with this. And, the whole goal here is to, again, give you value that you could put to use right away right after this call. So, Matt, with that?\u003C\u002Fp>\u003Cp>Yeah. Alrighty, man.\u003C\u002Fp>\u003Cp>Speaker 1: Let's roll. Alright. Well, like I said, good morning, good afternoon, good evening, everybody, where you're from. My name is Matt. I've I'm obviously from Directus.\u003C\u002Fp>\u003Cp>I'm on the marketing team here, so I head up a lot of the content initiatives and that sort of thing. So primarily the non technical and non doc stuff that you'll find on our blog and our case studies and all that stuff. So I figured it would make sense for me to kind of kick this off because content is near and dear to my heart. I've been doing it my entire career and I know the pains and struggles of it throughout my entire career too. So just on the agenda today, just so you know what we're going to cover, we did the introduction, so checkbox there.\u003C\u002Fp>\u003Cp>Hopefully, you know who we are by now. I'm going to talk a little bit about kind of the bigger problem of what the MCP actually solves and kind of how we're thinking about AI, generally speaking there. Collaboration, beyond just the content. So this is a very like small microcosm use case. So I just want to set the scene correctly.\u003C\u002Fp>\u003Cp>So you understand like what the MCP is, what it can actually solve for your business, or your, your clients, and then we'll hop into the live demo. So I'll get all the boring stuff out of the way first, with the story typical marketer. And then we'll hop into, like, kind of a q and a. So to kick things off, when I think about content, I always think of the hours I lose on kind of the stupid mundane tasks. And I saw a stat that content teams only spend about 45% of their time on real value generating work for the business, right?\u003C\u002Fp>\u003Cp>The rest that remaining 55% is consumed by the operational administrative tasks and that sort of thing. That's how much time the average content content team spends on work that isn't actually creating content, right? It's like copy and pasting from Google Docs, fixing broken formatting, building page structures manually, tagging assets one by one, and then losing all the assets. 55% of your team's creative energy goes to busy work. Imagine if they spent that time creating, not copy and pasting.\u003C\u002Fp>\u003Cp>Trust me when I say that I've tried like every AI out there from cursor to Jasper in the early days to Claude perplexity. Now you name it. I've tried it. Most AI tools out there today will help you write a better headline or generate some copy. And that's useful, right?\u003C\u002Fp>\u003Cp>That's good. But it's not really transformative. You still have to move that content. You still got to format it. You still got to publish it.\u003C\u002Fp>\u003Cp>You still got to manage it. That's the 55% we're talking about. So what we built with this MCP is different. It's not just another GPT wrapper. Instead of AI that suggests what the headline or what the content should be, we decided to build this MCP to connect it with your CMS and handle all of that busy work as well.\u003C\u002Fp>\u003Cp>So Yeah, in the next forty five minutes you're gonna see content move from Google docs to a live site without touching copy paste landing pages through conversation, analyze and organize which libraries automatically. And I just wanna stress, like, this isn't a prototype or a concept. Like, it exists right now and you're gonna get access to it after this. So, yeah, it's the direct SMTP. We're excited to give everybody access to it.\u003C\u002Fp>\u003Cp>We just got to get through kind of the live demo just to show you how how it works first. Brian, if you can scroll up just a few to the\u003C\u002Fp>\u003Cp>Speaker 0: This one? Yeah. Down. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: It said before we dive in, I just wanna zoom out for so what you're about to see with the content team is really just the beginning. Right? Like all of those mundane tasks that 55% that is lost. Content is just a perfect microcosm where AI is heading across every company. It's the same problems the content teams face face I know firsthand, that really everybody faces.\u003C\u002Fp>\u003Cp>Right? It's disconnected tools. It's the AI that suggests but doesn't execute. It's happening everywhere. I think we got a few sales folks on here too.\u003C\u002Fp>\u003Cp>Sales teams are copying new data from forms and other CRM, sending the email tools, and then the reporting dashboards, same date. You got developers. I think most of you writing the same credit operations, right? Like building the same admin interfaces, creating the same API endpoints, handling the same data validation logic on every single project and then maintaining it all separately. And then you got leaders are asking like, what's the status, from their teams and getting different answers because everyone's working in different systems that don't really talk to each other.\u003C\u002Fp>\u003Cp>So all of that to say, content teams aren't unique in these struggles. They're just the clearest example of a universal problem. And what you're about to see really is a glimpse of how that whole disconnect can be solved, not just for content teams, but you know, the context made sense. So zooming in again. So most AI tools you probably know today are, like, having a really smart intern or a really dumb intern, depending on how you use it, who could only give advice.\u003C\u002Fp>\u003Cp>Like, here's what you should do. But then you still have to actually go and do it. The real revolution here, and I hate the term revolution, I guess, evolution is that AI that actually does the work right? Ai that connects to your systems, understands your processes and executes within your business rules. You've probably heard about AI agents, right?\u003C\u002Fp>\u003Cp>If you have, this sounds pretty familiar territory to you. If you have it, it's basically autonomous AI that can plan and execute complex tasks across multiple systems. Like that's exactly like exactly where the industry, every industry, all businesses heading. But here's what most people miss is that agents actually need infrastructure to work. They need secure access to the data.\u003C\u002Fp>\u003Cp>They need permission systems. They need to understand your business rules and they need APIs that actually connect to your workflows. Most companies are trying to build agents on top of disconnected systems and that's like building a highway with no on ramps. What we're going to show you today, the MCP connecting AI to your content workflows. This is really the foundation for what makes AI agents possible.\u003C\u002Fp>\u003Cp>You can't have intelligent automation without intelligent infrastructure. And I just wanna caveat this first by saying, like, we're not at all advocating to replace humans. I think one of our competitors came out yesterday and was talking about like automating 10% of The US labor force. Like it's kind of messed up. I don't want to talk about that.\u003C\u002Fp>\u003Cp>I hate when people talk about AI taking people's jobs. That's not what the focus is. The focus is really creating a true collaboration between your team and the AI so they can be better and 10 x their output. You bring the strategy, creativity, and judgment and let the AI handle all the execution and consistency and scale. So, when that collaboration works, you don't really just get productivity gains.\u003C\u002Fp>\u003Cp>You get entirely new capabilities, right? It takes your business to the next level. Everybody wants to be valuable to the business, and this is the clearest way cutting out that mundane work and focusing on strategy and creativity. I probably sound like I'm two X right now. I think somebody mentioned with my, talking, but, yeah.\u003C\u002Fp>\u003Cp>All right. So to wrap it up, just this concept of the collaboration layer is, this is what's stopping most things now. Companies build AI features inside of existing tools and we think the future really is like kind of the the infrastructure outside of it. It's the collaboration layer that sits between AI and all your business systems. That's what this MCP is like.\u003C\u002Fp>\u003Cp>You've got your presentation layer, your front end, your websites, your apps on top, and you've got kind of the bottom foundational thing, database back end systems. But what's missing is this middle layer, the collaboration layer. It's kind of the connective tissue that lets AI work with your CMS and your database and your workflows and your team processes all through one interface. That's what direct is sort of becoming, you know, it's not just a CMS with AI features. It's a collaboration layer that lets AI work with your tech stack.\u003C\u002Fp>\u003Cp>Content management really is just a first use case and a lot of people use it for that. And it's really good at that. And the architecture we're building, I think, works for any workflow, any team, any business process. So I just want to emphasize, like, this is a small glimpse into what's possible when you start using this MCP. And I think, you know, it's, it's massive for, and I hopefully like you leave this with some ideas of like, this is applicable for a lot of different use cases and, for my clients and business and that sort of thing.\u003C\u002Fp>\u003Cp>Speaker 0: Cool.\u003C\u002Fp>\u003Cp>Speaker 1: That's the future. So I'll, I'll stop talking, get all the business stuff out of the way. Ryan, I'd love for you to take over and actually show them, show the final couple.\u003C\u002Fp>\u003Cp>Speaker 0: Show show the people. No. Hey. Hey. You they perfectly said that.\u003C\u002Fp>\u003Cp>Again, like, to me, the promise of AI is is not about taking jobs or automating 10% of the labor force. Right? It's automating the stuff that sucks, like the headaches, the stresses, all of that stuff. Alright. So before we dive in, let's just cover, like, a couple of MCP concepts just like, I'm sure a lot of you are already familiar with this.\u003C\u002Fp>\u003Cp>But, for those who are are not super familiar, I haven't dove into the deep end on this. MCP is just a a standard that is, becoming widely adopted or is already widely adopted for helping AI tools communicate with, your different applications and services that you use, like Directus. So model context protocol, and that's the long form, basically just sits in the middle, and it standardizes the way you communicate with LLMs from all the different services. We won't go into the nitty gritty on that. That's not why you're here.\u003C\u002Fp>\u003Cp>So what is the direct Us content MCP, and why is it the Content MCP? So this release is mostly focused on working with your existing data inside your Directus instance. So it it a lot of it is centered around content. Like, the tools can be used to publish blog posts, fix links, build landing pages, organize assets, update menus, and I'm sure you'll come up with a thousand other use cases for this after this call. But it does not include potentially destructive things like, deleting tables or collections from Directus.\u003C\u002Fp>\u003Cp>Potentially looking at that in future releases, but, mostly, we wanna give you the tools to work with your existing data and not potentially blow up your Directus instance. Alright. Cool. Everybody still with me? Alright.\u003C\u002Fp>\u003Cp>So how do we get access to this thing? How do you work with it? Depends on the LLM or the AI tools that you're using. There are three ones that are pretty popular right now. Raycast is, kind of a command bar tool that you could see here on my screen.\u003C\u002Fp>\u003Cp>But for this demo, we're gonna be using Cloud Desktop. It is the best one that I've found for these content workflows. So, we've got documentation. We've got all of that you can take a look at after this, but let's just take a look at how you install this thing and work with it. Alright.\u003C\u002Fp>\u003Cp>So the first thing you'll need, you'll need to download Cloud Desktop if you don't already have this. ChatGPT does not have MCP support yet, but some of their APIs do. So download this, and then there is a configuration that you'll need to do. So we'll open up Claude. We'll go to the settings.\u003C\u002Fp>\u003Cp>You'll see something like this. You click over to the developer tab, and then you're gonna click this edit config. Now, for any non developers here, this might look scary, but, let me just comment out my token here. We're just gonna pop in some JSON config. The two pieces that we really need here are the directus URL and a directus token, which you can grab from your directus instance.\u003C\u002Fp>\u003Cp>If you don't have a Directus instance, would love for you to try us out on Directus cloud or, you know, we'll give you the resources that you need to set up a a self hosted instance of this exact setup, at the end of this thing. So this is just the command to connect to the Directus MCP server. Right now, this uses the standard IO format. So, if you do any research on MCP, you'll see that there's a a lot of confusion on security and privacy, and there's a lot of different attacks. Right now, this runs the MCP server locally on your computer.\u003C\u002Fp>\u003Cp>So, we circumvent a lot of those things. And one of the beautiful things is this respects all the permissions that you have inside Directus. Alright. So, onto the fun stuff. And I'm sure Matt, I think he's did you mute yourself, Matt?\u003C\u002Fp>\u003Cp>Yeah. Probably so. Anyway, this is this is a workflow that you know well. Right, my friend? Publishing content.\u003C\u002Fp>\u003Cp>And just about every content workflow. Right? This is how it ends up. So you know that all roads start and intersect through Google Docs at some point. We've got articles that we want to publish here.\u003C\u002Fp>\u003Cp>The suggest, edit, all that functionality that's built into Google Docs makes this hey. It's just a easy tool. Right? And Directus is about working with the tools that you've got and instead of trying to force you into editing your blog posts or content outside of that. So the first workflow that we're gonna take a look at is how to get this blog post that we've got into our direct us CMS.\u003C\u002Fp>\u003Cp>Alright. So here's our blog post. The standard format looks like this. We go in. We open up a new blog post, and then I'm immediately greeted by a lot of different fields that I have to fill out to make sure this gets published correctly.\u003C\u002Fp>\u003Cp>And then you play this game where we copy paste into the rich text editor, and you'll see that Google Docs has all this formatting and all this extra stuff that I'm now gonna have to go through and and account for. This is gonna eat up a lot of time. Not only that, like our SEO meta descriptions, the short summaries, the content writer left all that out of the Google Doc conveniently. So now I've got to go in and write 60 characters that make make this make sense in a search pane. Let's let's just make that workflow a lot easier though.\u003C\u002Fp>\u003Cp>Right? So we are going to go into Claude. Once you've got that Directus MCP server connected, you'll see the available tools here, and I, you know, will explain some of these as we go through. But I'm just going to add this article from Google Docs. So this is a built in integration, and we're gonna say, hey.\u003C\u002Fp>\u003Cp>Please, always say please, add this article as a draft post inside Directus. Alright. So once I do that, what is going to happen behind the scenes? Right? This MCP server is going to connect to my direct instance, and you could see Claude here is calling the system prompt that we've got set up for it, which is basically some instructions of how to interact with the CMS.\u003C\u002Fp>\u003Cp>It is going to pick up the exact schema from my direct assistance. So this is, understanding all of the individual fields that I've got set up inside my direct Us instance, the data model, all of that so that the LLM has the proper context to do this job for me. So now you can see it's figuring out there's a post collection. It is automatically written a description for this piece of content, and you could see it's currently in the process of taking all of that ugly Google Docs formatting and just doing standard HTML elements that are going to look on brand, look amazing inside our, website. So this has finished this workflow.\u003C\u002Fp>\u003Cp>I can just click on, that. Or if I refresh over here inside Directus, Now we can see we've got our blog post. Right? And I can show this in the live preview here. I can also open this up in our visual editor.\u003C\u002Fp>\u003Cp>So this is that collaboration that we're talking about where AI gets me most of the way, and then I can go in and tweak things here, like, visually inside Directus, or through the the form space approach. Right? Now, the next workflow that I wanna show you guys. Right? This is pretty impressive.\u003C\u002Fp>\u003Cp>Feels like we're getting closer to the promise of AI. But I want to talk about assets for a moment. So this blog post needs a featured image. Right? And any content editor, any any CMS user has always been is is a % been through this pain of a file library that looks like this, image dot PNG, image zero zero zero one, random UUIDs.\u003C\u002Fp>\u003Cp>Right? So let's just take a look. I'm gonna go to Unsplash, and I'm just gonna download some random images here. So this is a sunset. Great.\u003C\u002Fp>\u003Cp>There's a penguin. Not sure if John from our team is on this. I know he's a big fan of penguins. And we'll just grab one more image. Right?\u003C\u002Fp>\u003Cp>So, also going to showcase the ability to just import from a URL inside Directus, but, also just gonna drop these images that we've got. We've got them here. Cool. We've got these images, but no metadata. Like, if someone is looking for an image of a penguin inside our CMS, So if I just go to my file library and I search penguin, oh, it looks like I've got a penguin already in here.\u003C\u002Fp>\u003Cp>But I digress. Right? This is a problem with every CMS. Now, what I can do with this MCP is automatically organize these assets. And I can't overstate how huge this is, just as a person who has a ton of unorganized assets in the CMS.\u003C\u002Fp>\u003Cp>Alright. So two capabilities we're gonna showcase here, the ability to load props inside, something like Cloud Desktop. So I've got this prompt where it is, organize images, and I'm gonna tell it to organize all the images in the needs organized folder. Alright. So I'm gonna add this prompt.\u003C\u002Fp>\u003Cp>Where does this come from? What what is happening behind the scenes? Right? So inside Directus, we've got just a AI prompts collection, and I could store these to allow all the users of this MCP server or anybody who has access to it to recall these prompts and, build really robust workflows that are super easy to use. So, basically, it's just taking this.\u003C\u002Fp>\u003Cp>It's added some of the context. These are instructions we're gonna use over and over again. I'm going to send this, and the Directus Content MCP server should go about organizing these assets for us. So it is going to, look for a folder that has needs organized, then it will read all the files within the folder. And one of the tools that we have available is the ability to read the raw image content.\u003C\u002Fp>\u003Cp>So it's actually looking at the the binary image data using the vision tools inside Claude, and actually analyzing that image. And it's letting us know, yes. Hey. I've analyzed all three of these images. Let me update these for you.\u003C\u002Fp>\u003Cp>The king penguin portrait. Great. And we could see it has the title. It has the description. It has the tags.\u003C\u002Fp>\u003Cp>And one of the other nice things, especially for a front end, Directus has support for focal points. So whenever an image gets cropped or transformed dynamically, we wanna make sure that the the subject, the the focus stays in frame. Alright? So we can see here that Directus has organized all these assets for us, and now we are able to search these and and use these in our our blog posts, in our workflows. Alright.\u003C\u002Fp>\u003Cp>Next one. It's hard doing this with with, nobody else on the on the line here, but maybe we cue that laugh track at some point. But, next on the list. Right? Building landing pages.\u003C\u002Fp>\u003Cp>And I say this a lot if you've ever caught any of my YouTube videos. One of my favorite features of Directus is the ability to build dynamic pages using blocks. So if you're a developer, you could set up different blocks for marketers, for content editors, and they can dynamically build pages based on those blocks. With the recent visual editor release, I can control these blocks dynamically, edit them right in line. Huge.\u003C\u002Fp>\u003Cp>But scaffolding out these blocks is still fairly tedious. Right? It is a a lot of forms to fill out, and just a a lot of back and forth to to get to something where I can tweak to to what I need it to be. So I'm just gonna start a new chat here, and let's do something more complex. Right?\u003C\u002Fp>\u003Cp>So we're gonna build a landing page. And I'm a little embarrassed that I didn't prepare for this one. I haven't had time to actually run this prompt through the system. It is, one I created right before this thing. So we'll see how it goes.\u003C\u002Fp>\u003Cp>Hopefully, it has fun. But just a a costar prompt that says, okay. Here's how I want you to structure the landing page, what goes into it. And I am going to call this from the cloud desktop. So we got create landing page, and we're gonna do this for direct to CMS.\u003C\u002Fp>\u003Cp>So direct to CMS desired action. We want someone to sign up for a cloud trial. The value proposition, a better CMS, headless, not brainless. That's my key value proposition. Who is our audience?\u003C\u002Fp>\u003Cp>Right? We want, technical marketers and developers. Ship faster is the specific pain point that we're solving. The specific solution and outcome is better workflow, if I can actually spell, less stress, cost, time to implement, migration. Alright.\u003C\u002Fp>\u003Cp>Cool. So now I've got my prompt finished. We're just gonna add this to our workflow. I'm gonna jam this into Claude. And, it again, it's gonna go through this progression where it understands the role that we've assigned it.\u003C\u002Fp>\u003Cp>It's going to pick up the direct to schema. And now it knows the available blocks that it can use to build a page for us. Right? So it is going to go through this whole progression of, creating a page. So we could see the page that it has created here.\u003C\u002Fp>\u003Cp>No content in that yet. Right? So it's actually going through and systematically creating each one of these individual blocks for the page. I can now go away, have a coffee, walk the dog, come back in just a minute here, and have a fully scaffolded out landing page for me to customize and tweak and and get ready to publish. Alright.\u003C\u002Fp>\u003Cp>Gotta get that product placement in there. So we can see this thing is working through. And, you know, obviously, like, the the benefit here, again, like, it knows your direct to schema. And so, you know, you can get access to this CMS setup that we've got. I'll just, go to the website here while it's we're waiting on this to do its thing.\u003C\u002Fp>\u003Cp>Directus.io. If you're a developer, you can pull this down. If you're not a developer, just click get started free, and you can actually get this exact template. But if you are not operating with this exact template, right, it pulls that schema and data model from your instance so it knows exactly what the fields that you have are and what belongs in that content. Alright.\u003C\u002Fp>\u003Cp>So, hopefully, this thing will get finished in just one moment. Let's take a look at some of the questions. Do we have any? Mister Matt, do we have any major questions while we're we're waiting on our friend to to do his work here?\u003C\u002Fp>\u003Cp>Speaker 1: No. We got some good ones. Yeah. Keep the questions and stuff coming in. We'll we'll try to get most of them, at the end.\u003C\u002Fp>\u003Cp>But, yeah, I was just responding to Kent who's got a deadline tomorrow to convince his boss to create new blocks. So, Kent, if you need, like, a doctor's note, we'll, we'll go ahead and handle that for you. Juan had a good point about, you know, the the user experience is really nice, but, what about the time it takes for a large inventory? Right? Like, lots of files, lot of lot of images when you're going through and, like, doing the organization and that sort of thing.\u003C\u002Fp>\u003Cp>Does it scale up if needed? I think, you know, it depends on the LLM or the model you're using. Obviously, like, the new ones, like, the deep thinking ones will take a lot longer, but, on your, you know, early stuff, it could be quicker.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Certainly. Like, you could, I you know, like, Claude Haiku, I think, is, like, their fastest model. I believe it has vision. Don't quote me on that.\u003C\u002Fp>\u003Cp>It just basically depends on the the actual model that you're working with. You know, I'm using, like, the latest and greatest from Claude here because I I'm not only asking it to publish the content. I'm asking it to, you know, do some massaging and and actually help prepare the content in our voice. And I found that Claude's on it is just hands down, like, the best rider for that. Again, like, this is the MCP server is just a protocol for connecting to different AI tools, and Cloud Desktop is is just the use case we're featuring here.\u003C\u002Fp>\u003Cp>But you could certainly, connect to Cursor or Raycast or, any other tools that support this protocol.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. So it looks like it has done the work here, and we've got our landing page ready to go. So let's just open this thing up. We'll take a look. We could see we've got a full landing page here.\u003C\u002Fp>\u003Cp>I'm just gonna open this up in the visual editor. And now we can go through this thing and see that, hey. This is got everything scaffolded out for us, you know, really nicely. Right? And I'm in a position now where I can go back in and just quickly edit whatever I need here.\u003C\u002Fp>\u003Cp>Hit save. You see that change and update.\u003C\u002Fp>\u003Cp>Speaker 1: I I can't stress, like, how much of a game changer this is. Like, you know, it typically takes days just for the scaffolding and there's, like, you know, writer's block and you just don't know what blocks to put. And this one is just, like, bam. Like, it's all there. And then when you combine it with the visual editing and hand it off to the content team, it's\u003C\u002Fp>\u003Cp>Speaker 0: again, collaboration. Right? So using the AI tools, also being able to visually edit and quickly tweak that. You know, this is not about completely eliminating. It is about streamlining, about taking the stress out of it, about getting something that I can then take and improve.\u003C\u002Fp>\u003Cp>The last but not least, workflow that I wanna show here is, publishing and automations. So, I love this meme, but picture automated AI agents, in this case, Claude, that are calling automated workflows that you have set up inside Directus. So Directus supports, low code, no code automations depending on what you're trying to do. Is a feature is called flows. So it is a visual automation builder, and I could do complex stuff.\u003C\u002Fp>\u003Cp>I could do really simple stuff. In this case, let's take a look and hey. This looks great. This looks great. Change it to published, and then publish the entire site using flows.\u003C\u002Fp>\u003Cp>Alright. And and just say, like, best practices here. Obviously, like, the better you prompts that you have, like, what you're putting in, the better results you're getting out. And I'm basically playing, dumb here a bit and giving it, like, simple instructions. But, obviously, the the more detailed you can be, the better the results.\u003C\u002Fp>\u003Cp>So what's happening here? Right? It is updated the status of this page and added the the current date and time stamp for me. Page is now published. But the next step here, you could see that it is reading through the flows.\u003C\u002Fp>\u003Cp>It has found a flow called publish website, and it's actually triggered this flow. So if I go over to our, Vercel account, you can see that this is actually going through and rebuilding the site. And, you know, in fifty four seconds, this content will be live on on the main site.\u003C\u002Fp>\u003Cp>Speaker 1: Wow.\u003C\u002Fp>\u003Cp>Speaker 0: And, and, Vercel trying to get the the extra the dough there, upgrade to pro. Alright. So that is the demo portion of this thing. You know, we have it basically speed ran this thing, I think. I hope it is, it just said, like, a good showcase of of what's available through the ContentMCP.\u003C\u002Fp>\u003Cp>There are, like, more tools in this, that are available. One of like, I hope this serves as inspiration to you all of what you can actually do with this thing. And I can't wait to see the workflows that everybody cooks up for the MCP. Definitely share with us. Keep the feedback coming.\u003C\u002Fp>\u003Cp>I that's all. That's all I've got. Matt.\u003C\u002Fp>\u003Cp>Speaker 1: That's awesome.\u003C\u002Fp>\u003Cp>Speaker 0: Back to you.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I've I've I love watching it every time. Cool. I'm just kinda running through some of the, the comments. There's a lot of stuff, a lot of questions around kind of the costs credit situation when you start using obviously, when you start using, like, LOMs with yourself and especially if you give access to, like, the marketing team, for instance, who tend to run through a lot of credits, not speaking from personal experience.\u003C\u002Fp>\u003Cp>But, anything on that that just, like, you know, in terms of maybe that landing page. Right? Like, how many credits or whatever the cost could be for that?\u003C\u002Fp>\u003Cp>Speaker 0: So as as far as the credits, like, the demo that I've got here is using, like, a I've got a paid subscription to Cloud Desktop. So, you know, I'm sure somebody in Tropic is maybe screaming about, you know, especially all the testing I've been doing over the last couple weeks, especially on some of the the top tier models, but that's included in the subscription. You know, depending on which models you're using, it's not really much different than than using these via API or whatever. Like, you know, there's always a balance that you try to strike between how effective it is and how, like, how much you're actually spending. And I you know, the same balance here.\u003C\u002Fp>\u003Cp>I don't I I don't see any any, like, specific stuff that you wouldn't encounter using AI in in in the same fashion.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. I think, yeah, we've we've never run into any credits issues with this stuff. But actually, we've been talking with Century. If you're familiar who's actually piloting they've got a beta out, I think, of a, it's like a LLM monitoring, so they can track, like, how many credits are being used.\u003C\u002Fp>\u003Cp>So it could be a good thing to test with this. We're currently talking to them about, like, a potential, you know, co co workshop for something on that. So we'll definitely keep everyone posted if that moves forward. But that could be a great thing to pair with with this so, you know, you're not going, bananas with your credits. Cool.\u003C\u002Fp>\u003Cp>Speaker 0: So one other question I saw here. Does the MCP support having multiple direct Us environments? Like, one instance of the MCP does not support multiple environments at at this point. Right? But there's nothing stopping you from potentially, and I I don't I'm not sure that I've actually tested this, to be honest.\u003C\u002Fp>\u003Cp>But, you know, I don't think there's anything stopping you from trying this out where I could say, hey. This is Directus non prod, something like this, and creating multiple instances of the server. I just worry about it getting confused depending on, like, the model and the tool that you're using. So try it out, report back, see what you come up with. It's the best advice I could give.\u003C\u002Fp>\u003Cp>Speaker 1: Paul, shout out Ridiculous Engineering. How's it going, Paul? Good to see you. This is actually a great idea, about, you know, some sort of exchange, MCP kinda workflows, prompts, marketplace. What do you think, Brian?\u003C\u002Fp>\u003Cp>Speaker 0: I would love to see that. One thing that we're gonna do is, like, all this, say, like, the slide deck, this FigJam, like, all this, we're gonna send to everybody who's registered for this event. We'll make sure you get access to that. All the prompts that I've used today, we'll include those. I you know, for now, I would say we're just gonna direct those to the community.\u003C\u002Fp>\u003Cp>So if you haven't already, sign up for our community. It's just community.directus.io. We've got an MCP server thread. You know, maybe we start a thread on MCP workflows and prompts so that that we could share and, you know, prosper together. Like, everybody take advantage of of AI.\u003C\u002Fp>\u003Cp>Right?\u003C\u002Fp>\u003Cp>Speaker 1: Let me just drop that that here. So, this this you can get access to it, on our new community platform here. Right? You were just saying? We've got it linked.\u003C\u002Fp>\u003Cp>Speaker 0: Amazing. Yep.\u003C\u002Fp>\u003Cp>Speaker 1: Cool.\u003C\u002Fp>\u003Cp>Speaker 0: What else do we have?\u003C\u002Fp>\u003Cp>Speaker 1: Here you go. Nice little shout out. No explosion. For the demos in in one.\u003C\u002Fp>\u003Cp>Speaker 0: It's Obviously smoothly. I love it, man. Obviously, George has been on some of my other demos before where I've got, like, multiple Docker containers running, you know, like, three different Nuxt applications and, yeah. No. This is a good one.\u003C\u002Fp>\u003Cp>Thank you. Appreciate you, George. What else? I I see this one's from Jonathan, but it is, Jonathan's on our team, the magic man. But, I probably in response to another question, the current MCP release.\u003C\u002Fp>\u003Cp>Again, if we just take a look. MCP. I'll share my screen again. This is the actual repository for it. It just got, like, a list of tools somewhere on here where we can just quickly run through these things.\u003C\u002Fp>\u003Cp>Like, we can read the current user information, read collections, schema, create items, update items, read files, import files. So all this to say, like, all the admin functions that are available through the Directus API, we didn't make available in this release. We wanted to make this one very focused on working with content inside your Directus instance. And, you know, Directus has got robust permissions so that, you know, a content editor who doesn't have access to collection a can't do that through the MCP. But also one of the concerns with production data is security not blowing up, and then since not accidentally dropping a table from the database and losing a 50 pieces of content or 3,000 pieces of content.\u003C\u002Fp>\u003Cp>So we didn't include those more destructive tools in this release for that very reason. Now we are exploring what that looks like in future releases and how to add some more of those powerful tools, especially if you're, let's say, building a new instance for a client or for an internal team of, you know, maybe modeling out some of your schema for you, like, creating relationships, configuring permissions, all that stuff that is is necessary. But, you know, maybe not, like, the best use of your time to a point and click and set all these things up.\u003C\u002Fp>\u003Cp>Speaker 1: Thanks. Here's one.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, I killed it. Sorry. Just trying to\u003C\u002Fp>\u003Cp>Speaker 1: creating collections on the\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. There there it is. Yeah. That's the exact question. Yeah.\u003C\u002Fp>\u003Cp>So right now, no. It cannot create collections at this point.\u003C\u002Fp>\u003Cp>Speaker 1: Damn. Alright. Just kinda scrolling through.\u003C\u002Fp>\u003Cp>Speaker 0: Anything else?\u003C\u002Fp>\u003Cp>Speaker 1: Soft deletes would be great.\u003C\u002Fp>\u003Cp>Speaker 0: Soft deletes would be great. Yes. And that is like a per collection setting inside your direct to CMS where you can archive. I I don't think I've got that set up on this demo instance, but it's totally doable.\u003C\u002Fp>\u003Cp>Speaker 1: Cool.\u003C\u002Fp>\u003Cp>Speaker 0: Any other final questions we need to answer?\u003C\u002Fp>\u003Cp>Speaker 1: I think there was one earlier about, you know, can this MCP be used to create extensions or kind of start to kind of expanding the use case out of just, like, kind of the the rudimentary content stuff?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. So certainly, you know, again, like, looking at a a future release where we have some more available tools, like, a builder MCP, if we're calling this content MCP, don't don't hold me to any of those things. Still experimenting with a lot of that. There's a lot of promise.\u003C\u002Fp>\u003Cp>I will say that. But this is, again, this is mostly focused on content teams. Depending on what content you've got in Directus, you know, there's a lot of possibilities there. But if you're a developer working with Directus, you're setting up Directus for clients, just stay tuned. We've got we've got some fun stuff coming for you.\u003C\u002Fp>\u003Cp>I promise.\u003C\u002Fp>\u003Cp>Speaker 1: Cool. Alright. Well, I guess we can wrap up early. I do wanna pull up one thing. If you could just pull up that, photo, like, the image, asset manager from the direct to search lens.\u003C\u002Fp>\u003Cp>If you've got quick access.\u003C\u002Fp>\u003Cp>Speaker 0: So the actual Yes. Like instance? Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Within the Directus instance. And just, make that penguin picture bigger. Bam. Alright. Michael said that's one judgmental penguin and, like, I just glanced at it.\u003C\u002Fp>\u003Cp>And I was like, yeah. That's the most judgmental penguin I've ever seen. So, yeah. Just wanted to call that out. And I don't know how to know.\u003C\u002Fp>\u003Cp>You know?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Alright, guys. Matt, thanks for putting together this event, doing the outline. For everybody in the audience, thanks for attending. Like, again, I run with this thing, build cool stuff.\u003C\u002Fp>\u003Cp>Make sure you share with us. Like, everything that we build is a % for you guys. Like, we wanna solve real problems, with these tools and and not automate 10% of The US labor force. Again, to get access to this, you can go to directus.i0\u002Fdocs. There's an AI section in our documentation.\u003C\u002Fp>\u003Cp>Again, we'll send out links to all this. It will give you installation instructions. There's videos of of me walking you through that installation process. You'll be freaking tired of my voice, by the time you're through. But then we also have, just sample workflows that we showed today, and information about the other configurations, like setting up the prompt system and all the other good goodies that are in here.\u003C\u002Fp>\u003Cp>And if you want the simple CMS template, again, get started free. Choose the CMS template when you go through the cloud creation process. Or if you want that locally, just copy this command, run that, and you'll be up and running in less than five minutes.\u003C\u002Fp>\u003Cp>Speaker 1: Cool. Yeah. And like Brian mentioned, we'll be sending out the, attendee resource kit with links to the repo, community.directus.io, and everything else you need to really get started on this today. So, yeah, I'm excited to see what comes of it. And I love that idea of a marketplace potentially.\u003C\u002Fp>\u003Cp>So, we'll definitely have chats about that. But yeah, thanks. Thanks everybody. Appreciate the time today.\u003C\u002Fp>\u003Cp>Speaker 0: We'll catch you on the next one.\u003C\u002Fp>","We are live. Hang on. I got something for this. Sorry. Alright. Let's let's kick this thing off, baby. Nice. Got a lot of folks in here. Alright. Well, Matt, you wanna do the honors? You want me to? What do we how are we kicking this thing off? Oh, yeah. Alright. We're going straight into it. Love it. Yeah. So hey, everybody. Welcome to our, Real Live Content AI workflows, directed some TP event. My name is Matt. Bryant is sitting in the copilot chair. I guess you're more of the pilot because you're controlling really everything. I'm just gonna sit here. But, yeah, great to see everybody. You might be seeing a couple people drop in where they're from. That's kind of the classic session workshop standby to open things. So cool to see, like, international representation in there. Cool. You wanna kick things off, Brian? Yeah. Yeah. Yeah. Yeah. So, over the last, I don't know. It's been six weeks at this point. We've, like, been working on this MCP. This is the launch event for that. But as we kick things off, definitely let us know in the chat who you are, where you're from, what you're building. We've got, like, a a few short slides we're gonna dive into, and then we will showcase these actual workflows that you can take and use right away. So I'm super excited for that. I'm sure we'll forget something along the way, but we'll have some fun with this. And, the whole goal here is to, again, give you value that you could put to use right away right after this call. So, Matt, with that? Yeah. Alrighty, man. Let's roll. Alright. Well, like I said, good morning, good afternoon, good evening, everybody, where you're from. My name is Matt. I've I'm obviously from Directus. I'm on the marketing team here, so I head up a lot of the content initiatives and that sort of thing. So primarily the non technical and non doc stuff that you'll find on our blog and our case studies and all that stuff. So I figured it would make sense for me to kind of kick this off because content is near and dear to my heart. I've been doing it my entire career and I know the pains and struggles of it throughout my entire career too. So just on the agenda today, just so you know what we're going to cover, we did the introduction, so checkbox there. Hopefully, you know who we are by now. I'm going to talk a little bit about kind of the bigger problem of what the MCP actually solves and kind of how we're thinking about AI, generally speaking there. Collaboration, beyond just the content. So this is a very like small microcosm use case. So I just want to set the scene correctly. So you understand like what the MCP is, what it can actually solve for your business, or your, your clients, and then we'll hop into the live demo. So I'll get all the boring stuff out of the way first, with the story typical marketer. And then we'll hop into, like, kind of a q and a. So to kick things off, when I think about content, I always think of the hours I lose on kind of the stupid mundane tasks. And I saw a stat that content teams only spend about 45% of their time on real value generating work for the business, right? The rest that remaining 55% is consumed by the operational administrative tasks and that sort of thing. That's how much time the average content content team spends on work that isn't actually creating content, right? It's like copy and pasting from Google Docs, fixing broken formatting, building page structures manually, tagging assets one by one, and then losing all the assets. 55% of your team's creative energy goes to busy work. Imagine if they spent that time creating, not copy and pasting. Trust me when I say that I've tried like every AI out there from cursor to Jasper in the early days to Claude perplexity. Now you name it. I've tried it. Most AI tools out there today will help you write a better headline or generate some copy. And that's useful, right? That's good. But it's not really transformative. You still have to move that content. You still got to format it. You still got to publish it. You still got to manage it. That's the 55% we're talking about. So what we built with this MCP is different. It's not just another GPT wrapper. Instead of AI that suggests what the headline or what the content should be, we decided to build this MCP to connect it with your CMS and handle all of that busy work as well. So Yeah, in the next forty five minutes you're gonna see content move from Google docs to a live site without touching copy paste landing pages through conversation, analyze and organize which libraries automatically. And I just wanna stress, like, this isn't a prototype or a concept. Like, it exists right now and you're gonna get access to it after this. So, yeah, it's the direct SMTP. We're excited to give everybody access to it. We just got to get through kind of the live demo just to show you how how it works first. Brian, if you can scroll up just a few to the This one? Yeah. Down. Okay. It said before we dive in, I just wanna zoom out for so what you're about to see with the content team is really just the beginning. Right? Like all of those mundane tasks that 55% that is lost. Content is just a perfect microcosm where AI is heading across every company. It's the same problems the content teams face face I know firsthand, that really everybody faces. Right? It's disconnected tools. It's the AI that suggests but doesn't execute. It's happening everywhere. I think we got a few sales folks on here too. Sales teams are copying new data from forms and other CRM, sending the email tools, and then the reporting dashboards, same date. You got developers. I think most of you writing the same credit operations, right? Like building the same admin interfaces, creating the same API endpoints, handling the same data validation logic on every single project and then maintaining it all separately. And then you got leaders are asking like, what's the status, from their teams and getting different answers because everyone's working in different systems that don't really talk to each other. So all of that to say, content teams aren't unique in these struggles. They're just the clearest example of a universal problem. And what you're about to see really is a glimpse of how that whole disconnect can be solved, not just for content teams, but you know, the context made sense. So zooming in again. So most AI tools you probably know today are, like, having a really smart intern or a really dumb intern, depending on how you use it, who could only give advice. Like, here's what you should do. But then you still have to actually go and do it. The real revolution here, and I hate the term revolution, I guess, evolution is that AI that actually does the work right? Ai that connects to your systems, understands your processes and executes within your business rules. You've probably heard about AI agents, right? If you have, this sounds pretty familiar territory to you. If you have it, it's basically autonomous AI that can plan and execute complex tasks across multiple systems. Like that's exactly like exactly where the industry, every industry, all businesses heading. But here's what most people miss is that agents actually need infrastructure to work. They need secure access to the data. They need permission systems. They need to understand your business rules and they need APIs that actually connect to your workflows. Most companies are trying to build agents on top of disconnected systems and that's like building a highway with no on ramps. What we're going to show you today, the MCP connecting AI to your content workflows. This is really the foundation for what makes AI agents possible. You can't have intelligent automation without intelligent infrastructure. And I just wanna caveat this first by saying, like, we're not at all advocating to replace humans. I think one of our competitors came out yesterday and was talking about like automating 10% of The US labor force. Like it's kind of messed up. I don't want to talk about that. I hate when people talk about AI taking people's jobs. That's not what the focus is. The focus is really creating a true collaboration between your team and the AI so they can be better and 10 x their output. You bring the strategy, creativity, and judgment and let the AI handle all the execution and consistency and scale. So, when that collaboration works, you don't really just get productivity gains. You get entirely new capabilities, right? It takes your business to the next level. Everybody wants to be valuable to the business, and this is the clearest way cutting out that mundane work and focusing on strategy and creativity. I probably sound like I'm two X right now. I think somebody mentioned with my, talking, but, yeah. All right. So to wrap it up, just this concept of the collaboration layer is, this is what's stopping most things now. Companies build AI features inside of existing tools and we think the future really is like kind of the the infrastructure outside of it. It's the collaboration layer that sits between AI and all your business systems. That's what this MCP is like. You've got your presentation layer, your front end, your websites, your apps on top, and you've got kind of the bottom foundational thing, database back end systems. But what's missing is this middle layer, the collaboration layer. It's kind of the connective tissue that lets AI work with your CMS and your database and your workflows and your team processes all through one interface. That's what direct is sort of becoming, you know, it's not just a CMS with AI features. It's a collaboration layer that lets AI work with your tech stack. Content management really is just a first use case and a lot of people use it for that. And it's really good at that. And the architecture we're building, I think, works for any workflow, any team, any business process. So I just want to emphasize, like, this is a small glimpse into what's possible when you start using this MCP. And I think, you know, it's, it's massive for, and I hopefully like you leave this with some ideas of like, this is applicable for a lot of different use cases and, for my clients and business and that sort of thing. Cool. That's the future. So I'll, I'll stop talking, get all the business stuff out of the way. Ryan, I'd love for you to take over and actually show them, show the final couple. Show show the people. No. Hey. Hey. You they perfectly said that. Again, like, to me, the promise of AI is is not about taking jobs or automating 10% of the labor force. Right? It's automating the stuff that sucks, like the headaches, the stresses, all of that stuff. Alright. So before we dive in, let's just cover, like, a couple of MCP concepts just like, I'm sure a lot of you are already familiar with this. But, for those who are are not super familiar, I haven't dove into the deep end on this. MCP is just a a standard that is, becoming widely adopted or is already widely adopted for helping AI tools communicate with, your different applications and services that you use, like Directus. So model context protocol, and that's the long form, basically just sits in the middle, and it standardizes the way you communicate with LLMs from all the different services. We won't go into the nitty gritty on that. That's not why you're here. So what is the direct Us content MCP, and why is it the Content MCP? So this release is mostly focused on working with your existing data inside your Directus instance. So it it a lot of it is centered around content. Like, the tools can be used to publish blog posts, fix links, build landing pages, organize assets, update menus, and I'm sure you'll come up with a thousand other use cases for this after this call. But it does not include potentially destructive things like, deleting tables or collections from Directus. Potentially looking at that in future releases, but, mostly, we wanna give you the tools to work with your existing data and not potentially blow up your Directus instance. Alright. Cool. Everybody still with me? Alright. So how do we get access to this thing? How do you work with it? Depends on the LLM or the AI tools that you're using. There are three ones that are pretty popular right now. Raycast is, kind of a command bar tool that you could see here on my screen. But for this demo, we're gonna be using Cloud Desktop. It is the best one that I've found for these content workflows. So, we've got documentation. We've got all of that you can take a look at after this, but let's just take a look at how you install this thing and work with it. Alright. So the first thing you'll need, you'll need to download Cloud Desktop if you don't already have this. ChatGPT does not have MCP support yet, but some of their APIs do. So download this, and then there is a configuration that you'll need to do. So we'll open up Claude. We'll go to the settings. You'll see something like this. You click over to the developer tab, and then you're gonna click this edit config. Now, for any non developers here, this might look scary, but, let me just comment out my token here. We're just gonna pop in some JSON config. The two pieces that we really need here are the directus URL and a directus token, which you can grab from your directus instance. If you don't have a Directus instance, would love for you to try us out on Directus cloud or, you know, we'll give you the resources that you need to set up a a self hosted instance of this exact setup, at the end of this thing. So this is just the command to connect to the Directus MCP server. Right now, this uses the standard IO format. So, if you do any research on MCP, you'll see that there's a a lot of confusion on security and privacy, and there's a lot of different attacks. Right now, this runs the MCP server locally on your computer. So, we circumvent a lot of those things. And one of the beautiful things is this respects all the permissions that you have inside Directus. Alright. So, onto the fun stuff. And I'm sure Matt, I think he's did you mute yourself, Matt? Yeah. Probably so. Anyway, this is this is a workflow that you know well. Right, my friend? Publishing content. And just about every content workflow. Right? This is how it ends up. So you know that all roads start and intersect through Google Docs at some point. We've got articles that we want to publish here. The suggest, edit, all that functionality that's built into Google Docs makes this hey. It's just a easy tool. Right? And Directus is about working with the tools that you've got and instead of trying to force you into editing your blog posts or content outside of that. So the first workflow that we're gonna take a look at is how to get this blog post that we've got into our direct us CMS. Alright. So here's our blog post. The standard format looks like this. We go in. We open up a new blog post, and then I'm immediately greeted by a lot of different fields that I have to fill out to make sure this gets published correctly. And then you play this game where we copy paste into the rich text editor, and you'll see that Google Docs has all this formatting and all this extra stuff that I'm now gonna have to go through and and account for. This is gonna eat up a lot of time. Not only that, like our SEO meta descriptions, the short summaries, the content writer left all that out of the Google Doc conveniently. So now I've got to go in and write 60 characters that make make this make sense in a search pane. Let's let's just make that workflow a lot easier though. Right? So we are going to go into Claude. Once you've got that Directus MCP server connected, you'll see the available tools here, and I, you know, will explain some of these as we go through. But I'm just going to add this article from Google Docs. So this is a built in integration, and we're gonna say, hey. Please, always say please, add this article as a draft post inside Directus. Alright. So once I do that, what is going to happen behind the scenes? Right? This MCP server is going to connect to my direct instance, and you could see Claude here is calling the system prompt that we've got set up for it, which is basically some instructions of how to interact with the CMS. It is going to pick up the exact schema from my direct assistance. So this is, understanding all of the individual fields that I've got set up inside my direct Us instance, the data model, all of that so that the LLM has the proper context to do this job for me. So now you can see it's figuring out there's a post collection. It is automatically written a description for this piece of content, and you could see it's currently in the process of taking all of that ugly Google Docs formatting and just doing standard HTML elements that are going to look on brand, look amazing inside our, website. So this has finished this workflow. I can just click on, that. Or if I refresh over here inside Directus, Now we can see we've got our blog post. Right? And I can show this in the live preview here. I can also open this up in our visual editor. So this is that collaboration that we're talking about where AI gets me most of the way, and then I can go in and tweak things here, like, visually inside Directus, or through the the form space approach. Right? Now, the next workflow that I wanna show you guys. Right? This is pretty impressive. Feels like we're getting closer to the promise of AI. But I want to talk about assets for a moment. So this blog post needs a featured image. Right? And any content editor, any any CMS user has always been is is a % been through this pain of a file library that looks like this, image dot PNG, image zero zero zero one, random UUIDs. Right? So let's just take a look. I'm gonna go to Unsplash, and I'm just gonna download some random images here. So this is a sunset. Great. There's a penguin. Not sure if John from our team is on this. I know he's a big fan of penguins. And we'll just grab one more image. Right? So, also going to showcase the ability to just import from a URL inside Directus, but, also just gonna drop these images that we've got. We've got them here. Cool. We've got these images, but no metadata. Like, if someone is looking for an image of a penguin inside our CMS, So if I just go to my file library and I search penguin, oh, it looks like I've got a penguin already in here. But I digress. Right? This is a problem with every CMS. Now, what I can do with this MCP is automatically organize these assets. And I can't overstate how huge this is, just as a person who has a ton of unorganized assets in the CMS. Alright. So two capabilities we're gonna showcase here, the ability to load props inside, something like Cloud Desktop. So I've got this prompt where it is, organize images, and I'm gonna tell it to organize all the images in the needs organized folder. Alright. So I'm gonna add this prompt. Where does this come from? What what is happening behind the scenes? Right? So inside Directus, we've got just a AI prompts collection, and I could store these to allow all the users of this MCP server or anybody who has access to it to recall these prompts and, build really robust workflows that are super easy to use. So, basically, it's just taking this. It's added some of the context. These are instructions we're gonna use over and over again. I'm going to send this, and the Directus Content MCP server should go about organizing these assets for us. So it is going to, look for a folder that has needs organized, then it will read all the files within the folder. And one of the tools that we have available is the ability to read the raw image content. So it's actually looking at the the binary image data using the vision tools inside Claude, and actually analyzing that image. And it's letting us know, yes. Hey. I've analyzed all three of these images. Let me update these for you. The king penguin portrait. Great. And we could see it has the title. It has the description. It has the tags. And one of the other nice things, especially for a front end, Directus has support for focal points. So whenever an image gets cropped or transformed dynamically, we wanna make sure that the the subject, the the focus stays in frame. Alright? So we can see here that Directus has organized all these assets for us, and now we are able to search these and and use these in our our blog posts, in our workflows. Alright. Next one. It's hard doing this with with, nobody else on the on the line here, but maybe we cue that laugh track at some point. But, next on the list. Right? Building landing pages. And I say this a lot if you've ever caught any of my YouTube videos. One of my favorite features of Directus is the ability to build dynamic pages using blocks. So if you're a developer, you could set up different blocks for marketers, for content editors, and they can dynamically build pages based on those blocks. With the recent visual editor release, I can control these blocks dynamically, edit them right in line. Huge. But scaffolding out these blocks is still fairly tedious. Right? It is a a lot of forms to fill out, and just a a lot of back and forth to to get to something where I can tweak to to what I need it to be. So I'm just gonna start a new chat here, and let's do something more complex. Right? So we're gonna build a landing page. And I'm a little embarrassed that I didn't prepare for this one. I haven't had time to actually run this prompt through the system. It is, one I created right before this thing. So we'll see how it goes. Hopefully, it has fun. But just a a costar prompt that says, okay. Here's how I want you to structure the landing page, what goes into it. And I am going to call this from the cloud desktop. So we got create landing page, and we're gonna do this for direct to CMS. So direct to CMS desired action. We want someone to sign up for a cloud trial. The value proposition, a better CMS, headless, not brainless. That's my key value proposition. Who is our audience? Right? We want, technical marketers and developers. Ship faster is the specific pain point that we're solving. The specific solution and outcome is better workflow, if I can actually spell, less stress, cost, time to implement, migration. Alright. Cool. So now I've got my prompt finished. We're just gonna add this to our workflow. I'm gonna jam this into Claude. And, it again, it's gonna go through this progression where it understands the role that we've assigned it. It's going to pick up the direct to schema. And now it knows the available blocks that it can use to build a page for us. Right? So it is going to go through this whole progression of, creating a page. So we could see the page that it has created here. No content in that yet. Right? So it's actually going through and systematically creating each one of these individual blocks for the page. I can now go away, have a coffee, walk the dog, come back in just a minute here, and have a fully scaffolded out landing page for me to customize and tweak and and get ready to publish. Alright. Gotta get that product placement in there. So we can see this thing is working through. And, you know, obviously, like, the the benefit here, again, like, it knows your direct to schema. And so, you know, you can get access to this CMS setup that we've got. I'll just, go to the website here while it's we're waiting on this to do its thing. Directus.io. If you're a developer, you can pull this down. If you're not a developer, just click get started free, and you can actually get this exact template. But if you are not operating with this exact template, right, it pulls that schema and data model from your instance so it knows exactly what the fields that you have are and what belongs in that content. Alright. So, hopefully, this thing will get finished in just one moment. Let's take a look at some of the questions. Do we have any? Mister Matt, do we have any major questions while we're we're waiting on our friend to to do his work here? No. We got some good ones. Yeah. Keep the questions and stuff coming in. We'll we'll try to get most of them, at the end. But, yeah, I was just responding to Kent who's got a deadline tomorrow to convince his boss to create new blocks. So, Kent, if you need, like, a doctor's note, we'll, we'll go ahead and handle that for you. Juan had a good point about, you know, the the user experience is really nice, but, what about the time it takes for a large inventory? Right? Like, lots of files, lot of lot of images when you're going through and, like, doing the organization and that sort of thing. Does it scale up if needed? I think, you know, it depends on the LLM or the model you're using. Obviously, like, the new ones, like, the deep thinking ones will take a lot longer, but, on your, you know, early stuff, it could be quicker. Yeah. Certainly. Like, you could, I you know, like, Claude Haiku, I think, is, like, their fastest model. I believe it has vision. Don't quote me on that. It just basically depends on the the actual model that you're working with. You know, I'm using, like, the latest and greatest from Claude here because I I'm not only asking it to publish the content. I'm asking it to, you know, do some massaging and and actually help prepare the content in our voice. And I found that Claude's on it is just hands down, like, the best rider for that. Again, like, this is the MCP server is just a protocol for connecting to different AI tools, and Cloud Desktop is is just the use case we're featuring here. But you could certainly, connect to Cursor or Raycast or, any other tools that support this protocol. Yeah. Alright. So it looks like it has done the work here, and we've got our landing page ready to go. So let's just open this thing up. We'll take a look. We could see we've got a full landing page here. I'm just gonna open this up in the visual editor. And now we can go through this thing and see that, hey. This is got everything scaffolded out for us, you know, really nicely. Right? And I'm in a position now where I can go back in and just quickly edit whatever I need here. Hit save. You see that change and update. I I can't stress, like, how much of a game changer this is. Like, you know, it typically takes days just for the scaffolding and there's, like, you know, writer's block and you just don't know what blocks to put. And this one is just, like, bam. Like, it's all there. And then when you combine it with the visual editing and hand it off to the content team, it's again, collaboration. Right? So using the AI tools, also being able to visually edit and quickly tweak that. You know, this is not about completely eliminating. It is about streamlining, about taking the stress out of it, about getting something that I can then take and improve. The last but not least, workflow that I wanna show here is, publishing and automations. So, I love this meme, but picture automated AI agents, in this case, Claude, that are calling automated workflows that you have set up inside Directus. So Directus supports, low code, no code automations depending on what you're trying to do. Is a feature is called flows. So it is a visual automation builder, and I could do complex stuff. I could do really simple stuff. In this case, let's take a look and hey. This looks great. This looks great. Change it to published, and then publish the entire site using flows. Alright. And and just say, like, best practices here. Obviously, like, the better you prompts that you have, like, what you're putting in, the better results you're getting out. And I'm basically playing, dumb here a bit and giving it, like, simple instructions. But, obviously, the the more detailed you can be, the better the results. So what's happening here? Right? It is updated the status of this page and added the the current date and time stamp for me. Page is now published. But the next step here, you could see that it is reading through the flows. It has found a flow called publish website, and it's actually triggered this flow. So if I go over to our, Vercel account, you can see that this is actually going through and rebuilding the site. And, you know, in fifty four seconds, this content will be live on on the main site. Wow. And, and, Vercel trying to get the the extra the dough there, upgrade to pro. Alright. So that is the demo portion of this thing. You know, we have it basically speed ran this thing, I think. I hope it is, it just said, like, a good showcase of of what's available through the ContentMCP. There are, like, more tools in this, that are available. One of like, I hope this serves as inspiration to you all of what you can actually do with this thing. And I can't wait to see the workflows that everybody cooks up for the MCP. Definitely share with us. Keep the feedback coming. I that's all. That's all I've got. Matt. That's awesome. Back to you. Yeah. I've I've I love watching it every time. Cool. I'm just kinda running through some of the, the comments. There's a lot of stuff, a lot of questions around kind of the costs credit situation when you start using obviously, when you start using, like, LOMs with yourself and especially if you give access to, like, the marketing team, for instance, who tend to run through a lot of credits, not speaking from personal experience. But, anything on that that just, like, you know, in terms of maybe that landing page. Right? Like, how many credits or whatever the cost could be for that? So as as far as the credits, like, the demo that I've got here is using, like, a I've got a paid subscription to Cloud Desktop. So, you know, I'm sure somebody in Tropic is maybe screaming about, you know, especially all the testing I've been doing over the last couple weeks, especially on some of the the top tier models, but that's included in the subscription. You know, depending on which models you're using, it's not really much different than than using these via API or whatever. Like, you know, there's always a balance that you try to strike between how effective it is and how, like, how much you're actually spending. And I you know, the same balance here. I don't I I don't see any any, like, specific stuff that you wouldn't encounter using AI in in in the same fashion. Yeah. Yeah. I think, yeah, we've we've never run into any credits issues with this stuff. But actually, we've been talking with Century. If you're familiar who's actually piloting they've got a beta out, I think, of a, it's like a LLM monitoring, so they can track, like, how many credits are being used. So it could be a good thing to test with this. We're currently talking to them about, like, a potential, you know, co co workshop for something on that. So we'll definitely keep everyone posted if that moves forward. But that could be a great thing to pair with with this so, you know, you're not going, bananas with your credits. Cool. So one other question I saw here. Does the MCP support having multiple direct Us environments? Like, one instance of the MCP does not support multiple environments at at this point. Right? But there's nothing stopping you from potentially, and I I don't I'm not sure that I've actually tested this, to be honest. But, you know, I don't think there's anything stopping you from trying this out where I could say, hey. This is Directus non prod, something like this, and creating multiple instances of the server. I just worry about it getting confused depending on, like, the model and the tool that you're using. So try it out, report back, see what you come up with. It's the best advice I could give. Paul, shout out Ridiculous Engineering. How's it going, Paul? Good to see you. This is actually a great idea, about, you know, some sort of exchange, MCP kinda workflows, prompts, marketplace. What do you think, Brian? I would love to see that. One thing that we're gonna do is, like, all this, say, like, the slide deck, this FigJam, like, all this, we're gonna send to everybody who's registered for this event. We'll make sure you get access to that. All the prompts that I've used today, we'll include those. I you know, for now, I would say we're just gonna direct those to the community. So if you haven't already, sign up for our community. It's just community.directus.io. We've got an MCP server thread. You know, maybe we start a thread on MCP workflows and prompts so that that we could share and, you know, prosper together. Like, everybody take advantage of of AI. Right? Let me just drop that that here. So, this this you can get access to it, on our new community platform here. Right? You were just saying? We've got it linked. Amazing. Yep. Cool. What else do we have? Here you go. Nice little shout out. No explosion. For the demos in in one. It's Obviously smoothly. I love it, man. Obviously, George has been on some of my other demos before where I've got, like, multiple Docker containers running, you know, like, three different Nuxt applications and, yeah. No. This is a good one. Thank you. Appreciate you, George. What else? I I see this one's from Jonathan, but it is, Jonathan's on our team, the magic man. But, I probably in response to another question, the current MCP release. Again, if we just take a look. MCP. I'll share my screen again. This is the actual repository for it. It just got, like, a list of tools somewhere on here where we can just quickly run through these things. Like, we can read the current user information, read collections, schema, create items, update items, read files, import files. So all this to say, like, all the admin functions that are available through the Directus API, we didn't make available in this release. We wanted to make this one very focused on working with content inside your Directus instance. And, you know, Directus has got robust permissions so that, you know, a content editor who doesn't have access to collection a can't do that through the MCP. But also one of the concerns with production data is security not blowing up, and then since not accidentally dropping a table from the database and losing a 50 pieces of content or 3,000 pieces of content. So we didn't include those more destructive tools in this release for that very reason. Now we are exploring what that looks like in future releases and how to add some more of those powerful tools, especially if you're, let's say, building a new instance for a client or for an internal team of, you know, maybe modeling out some of your schema for you, like, creating relationships, configuring permissions, all that stuff that is is necessary. But, you know, maybe not, like, the best use of your time to a point and click and set all these things up. Thanks. Here's one. Oh, I killed it. Sorry. Just trying to creating collections on the Yeah. There there it is. Yeah. That's the exact question. Yeah. So right now, no. It cannot create collections at this point. Damn. Alright. Just kinda scrolling through. Anything else? Soft deletes would be great. Soft deletes would be great. Yes. And that is like a per collection setting inside your direct to CMS where you can archive. I I don't think I've got that set up on this demo instance, but it's totally doable. Cool. Any other final questions we need to answer? I think there was one earlier about, you know, can this MCP be used to create extensions or kind of start to kind of expanding the use case out of just, like, kind of the the rudimentary content stuff? Yeah. Yeah. So certainly, you know, again, like, looking at a a future release where we have some more available tools, like, a builder MCP, if we're calling this content MCP, don't don't hold me to any of those things. Still experimenting with a lot of that. There's a lot of promise. I will say that. But this is, again, this is mostly focused on content teams. Depending on what content you've got in Directus, you know, there's a lot of possibilities there. But if you're a developer working with Directus, you're setting up Directus for clients, just stay tuned. We've got we've got some fun stuff coming for you. I promise. Cool. Alright. Well, I guess we can wrap up early. I do wanna pull up one thing. If you could just pull up that, photo, like, the image, asset manager from the direct to search lens. If you've got quick access. So the actual Yes. Like instance? Yeah. Within the Directus instance. And just, make that penguin picture bigger. Bam. Alright. Michael said that's one judgmental penguin and, like, I just glanced at it. And I was like, yeah. That's the most judgmental penguin I've ever seen. So, yeah. Just wanted to call that out. And I don't know how to know. You know? Yeah. Alright, guys. Matt, thanks for putting together this event, doing the outline. For everybody in the audience, thanks for attending. Like, again, I run with this thing, build cool stuff. Make sure you share with us. Like, everything that we build is a % for you guys. Like, we wanna solve real problems, with these tools and and not automate 10% of The US labor force. Again, to get access to this, you can go to directus.i0\u002Fdocs. There's an AI section in our documentation. Again, we'll send out links to all this. It will give you installation instructions. There's videos of of me walking you through that installation process. You'll be freaking tired of my voice, by the time you're through. But then we also have, just sample workflows that we showed today, and information about the other configurations, like setting up the prompt system and all the other good goodies that are in here. And if you want the simple CMS template, again, get started free. Choose the CMS template when you go through the cloud creation process. Or if you want that locally, just copy this command, run that, and you'll be up and running in less than five minutes. Cool. Yeah. And like Brian mentioned, we'll be sending out the, attendee resource kit with links to the repo, community.directus.io, and everything else you need to really get started on this today. So, yeah, I'm excited to see what comes of it. And I love that idea of a marketplace potentially. So, we'll definitely have chats about that. But yeah, thanks. Thanks everybody. Appreciate the time today. We'll catch you on the next one.","\u003Cp>This comprehensive workshop brings together everything you've learned about AI-powered content workflows. Experience real-world scenarios including:\u003C\u002Fp>\u003Cul>\u003Cli>Live demonstrations of complex content creation workflows\u003C\u002Fli>\u003Cli>Advanced prompt engineering techniques for consistent results\u003C\u002Fli>\u003Cli>Integration strategies for existing content operations\u003C\u002Fli>\u003Cli>Troubleshooting common workflow challenges\u003C\u002Fli>\u003Cli>Best practices for team collaboration with AI assistance\u003C\u002Fli>\u003Cli>Future-proofing your content operations\u003C\u002Fli>\u003C\u002Ful>\u003Cp>Perfect for content teams, marketers, and developers ready to implement AI-assisted workflows at scale. This workshop provides practical, actionable insights you can apply immediately to your content operations.\u003C\u002Fp>",[836,837],"c25d56f8-7a20-4bca-9494-a3d26cf45c32","af44262d-58af-4837-9a73-a42df31c1978",[],{"year":768,"number":769,"id":770,"episodes":840,"show":841},[772,773,774,775,754,776,777,778],{"title":383,"slug":384},{"id":778,"slug":843,"vimeo_id":844,"description":845,"tile":846,"length":675,"resources":12,"people":12,"episode_number":11,"published":759,"title":847,"video_transcript_html":848,"video_transcript_text":849,"content":850,"seo":12,"status":19,"episode_people":851,"recommendations":853,"season":854},"how-we-used-mcp-for-this-series","1088806996","See the Directus MCP Server in action as we document the actual workflow used to create this entire series. Watch real-time AI collaboration transform a simple list of videos into a complete, structured content series with full metadata, relationships, and automation.","087a8f2f-a2c2-48b9-b4cc-20be0d4740a9","How We Used MCP for this Series","\u003Cp>Speaker 0: Hey, guys. Brian here for Directus. And in this video, I'm gonna give you, like, a behind the scenes demo of the Directus content MCP. Can't stress this enough. We just wrapped our MCP event.\u003C\u002Fp>\u003Cp>Same shirt, same same hat, all that. This is not scripted. I've not practiced any of this stuff. You know, so many of the AI demos and tools that I see are these polished scripted videos that have been practiced to the gills and it's hey, here's what you could do with the tools. Look at this.\u003C\u002Fp>\u003Cp>You can potentially do this. I'm gonna show you something that you could do right now with the Directus Content MCP and go. Okay. So, we have a platform called Directus TV where we, have a a different series of shows on all sorts of different topics inside Directus, like translations or mapping or authentication or theming if you're into that. Don't know who that guy is.\u003C\u002Fp>\u003Cp>But, we put all the recordings of our workshops into, something called enter the workshop. But this Directus MCP, we've got seven or eight videos. We're gonna create a new series. So this is behind the scenes. This is our actual instance.\u003C\u002Fp>\u003Cp>I'm gonna set this up. I'm gonna go in. Just gonna log in to my account, create a token. Cool. I'm gonna hop over to my MCP server setup, and I'm gonna plug in the TV URL.\u003C\u002Fp>\u003Cp>Hopefully, I blur this out. And I'm gonna take my token. So I'm just gonna paste that here, quickly move that off screen. Obviously, gonna have to roll that one after this video. And then once I'm done with that, I'm just gonna restart claw desktop.\u003C\u002Fp>\u003Cp>Awesome. Alright. And can you check my directus schema for me? Now I'll be super happy when Claude desktop supports, like a voice mail or something. But let's see what we've got here.\u003C\u002Fp>\u003Cp>So it's loading our system prompt. Cool. Let me examine your direct to schema. Sending a request. Appears to be a content management platform for TV video platform with episodes.\u003C\u002Fp>\u003Cp>Awesome. Alright. So now, like, how to go about this. Right? Here's our organization structure.\u003C\u002Fp>\u003Cp>We have a show. Each show has seasons. And within each season, there are episodes. So I'm just going to try to do this in the most lazy possible way. How can I copy all these names of the videos?\u003C\u002Fp>\u003Cp>Direct us NCP. I want to create a new show for the Directus NCP server. Please create the show, and I'll send you the links to the videos. So there are videos that are hosted at Vimeo. And if we take a look at the actual structure and you could get this exact same setup inside Directus plus.\u003C\u002Fp>\u003Cp>But you can see here's the structure. We've got, a season. There's an episode number. There's a slug. There's a title.\u003C\u002Fp>\u003Cp>There's a Vimeo ID, and that's how we store this. And then we have episode people and lots of kind of complex relationships. There's also flows that are set up to generate transcripts. So, Vimeo videos. Alright.\u003C\u002Fp>\u003Cp>So I'll just give it more context here. We're gonna say, hey. Here's the ones I have. I have, Claude desktop MCP installation, cursor installation, Raycast installation. What do we have?\u003C\u002Fp>\u003Cp>We have a blog post workflow. Workflow. Real life AI content workflows workshop. And what? Landing page workflow.\u003C\u002Fp>\u003Cp>And what's the last one? Organize assets. Alright. So I don't know how well this is gonna do. I'll just copy paste the videos.\u003C\u002Fp>\u003Cp>Link. That's called desktop installation. This is cursor MCP installation, Raycast installation, blog post workflow, landing page workflow. And if I had to do this a lot, I would obviously try to optimize this even further. Landing page workflow, real life content workflows.\u003C\u002Fp>\u003Cp>Okay. Alright. So I'm not telling it anything about the structure. Right? It's already picked up the schema.\u003C\u002Fp>\u003Cp>And let's give it some more context. Here's the docs for the MCP server for additional context. Alright. So I'm gonna just go here to directus.i0\u002Fdocs. And there's, like, a a lot of descriptions and and things like that that go into this.\u003C\u002Fp>\u003Cp>I'm just gonna paste these docs, tools, prompts, workflows. Alright. So that's all the docs that I've got. I'm just gonna trust and and fire this off that in the episodes that this is actually gonna work out based on info below. Now look, I'm, again, not a prompt genius.\u003C\u002Fp>\u003Cp>This is all stuff that you can do right now. And if you learn a little bit about prompting more than me, you will get even further than this. So let's just see what happens. Is going to create the show for me. There's the description.\u003C\u002Fp>\u003Cp>This is gonna be published. It is a AI powered content management with Directus and MCP. Alright. So now it's creating a new season for that specific show. Looks like it is creating the individual episodes.\u003C\u002Fp>\u003Cp>Alright. So now if I go in here, we search for MCP. Right? I can see this. There's the card text, published, status, images.\u003C\u002Fp>\u003Cp>So we we need some images. I don't have to generate those, outside of this workflow. But if I go in, we look at the season, I could see that it's starting to populate the episodes for me. And is it done? Let's create the workflow episodes.\u003C\u002Fp>\u003Cp>I don't know if it was actually done or not. Alright. Organize assets. The workshop episode. Organize assets.\u003C\u002Fp>\u003Cp>Workflow. I'll just save and update that. So, again, hey. Like, on this workshop, we covered this collaboration model where, you you know, hey. I'm actually working alongside this, and I I think this is a great just a great showcase of that.\u003C\u002Fp>\u003Cp>Right? This is the MCP server. Now all I need to do is basically just test this out. Right? I've got the show.\u003C\u002Fp>\u003Cp>I've got the season for the show. I've got the episodes for the show, and all the descriptions are there. It looks like it's missing the episode people. So can you add what am I set up as? Is there Brian in here?\u003C\u002Fp>\u003Cp>There's also Brian Ross. I don't know who that guy is. Can you add Bryant Gillespie to the episode people for all the episodes and add Matt Miner for the last one, the workshop. Alright. So this is, you know, a fairly complex relationship.\u003C\u002Fp>\u003Cp>Like, this is a many to many relationship. And okay. It should so it's going to go in and query all the TV people to see if they exist. Then it's gonna go into add Brian Gillespie to all seven episodes and Matt Miner to the workshop episode. Now, again, this wouldn't be terribly difficult.\u003C\u002Fp>\u003Cp>I could go in and do this manually. But, again, let's take advantage of the AI where we can. I'll just hit refresh. We'll see what it's coming up with. Okay.\u003C\u002Fp>\u003Cp>So it's got the first one. Second one is good. Is it done with the episodes people for Ed Miner Matt Miner to the workshop episode? Added all the people to the episodes. Alright.\u003C\u002Fp>\u003Cp>Right. So Matt Miner, Brian Gillespie, there's the workshop. All I need are the images, and we're not gonna generate those via via this workflow. Now what I also am going to do here, right, now normally what I would have to do is go through each individual episode. So if I look for show contains MCP, that's gonna be case specific.\u003C\u002Fp>\u003Cp>So I would have to go in and generate the transcripts for these, but can you generate transcripts for all those episodes using flows? Let's see what this comes up with. And, again, look, I've this is my first time doing in this. I've not practiced this at all. We'll see what's gonna happen.\u003C\u002Fp>\u003Cp>There's a generate transcript flow that's designed to fetch Vimeo files and send them to Deepgram for transcription. This is exactly what we need. So it's gonna go through and call these flows. So now if I, like, go through and I look at the generate transcript, I can see that this has been triggered for that, for that episode. It's gonna continue with the rest of the episodes, and, that's a wrap.\u003C\u002Fp>\u003Cp>Right? So what I'm able to do here is just take a a literal ton of busy work out of the equation for me. And now all I gotta do is hop in Figma, design some images for this, and I could publish seven videos in record time. So that's it for this video. I guess this will be the eighth video that we upload to this MCP.\u003C\u002Fp>\u003Cp>But I can't wait to see what you guys build with this. Please share your feedback. Send us questions, comments, ideas that you have for, Directus and AI. I'll catch you on the next one. See you.\u003C\u002Fp>","Hey, guys. Brian here for Directus. And in this video, I'm gonna give you, like, a behind the scenes demo of the Directus content MCP. Can't stress this enough. We just wrapped our MCP event. Same shirt, same same hat, all that. This is not scripted. I've not practiced any of this stuff. You know, so many of the AI demos and tools that I see are these polished scripted videos that have been practiced to the gills and it's hey, here's what you could do with the tools. Look at this. You can potentially do this. I'm gonna show you something that you could do right now with the Directus Content MCP and go. Okay. So, we have a platform called Directus TV where we, have a a different series of shows on all sorts of different topics inside Directus, like translations or mapping or authentication or theming if you're into that. Don't know who that guy is. But, we put all the recordings of our workshops into, something called enter the workshop. But this Directus MCP, we've got seven or eight videos. We're gonna create a new series. So this is behind the scenes. This is our actual instance. I'm gonna set this up. I'm gonna go in. Just gonna log in to my account, create a token. Cool. I'm gonna hop over to my MCP server setup, and I'm gonna plug in the TV URL. Hopefully, I blur this out. And I'm gonna take my token. So I'm just gonna paste that here, quickly move that off screen. Obviously, gonna have to roll that one after this video. And then once I'm done with that, I'm just gonna restart claw desktop. Awesome. Alright. And can you check my directus schema for me? Now I'll be super happy when Claude desktop supports, like a voice mail or something. But let's see what we've got here. So it's loading our system prompt. Cool. Let me examine your direct to schema. Sending a request. Appears to be a content management platform for TV video platform with episodes. Awesome. Alright. So now, like, how to go about this. Right? Here's our organization structure. We have a show. Each show has seasons. And within each season, there are episodes. So I'm just going to try to do this in the most lazy possible way. How can I copy all these names of the videos? Direct us NCP. I want to create a new show for the Directus NCP server. Please create the show, and I'll send you the links to the videos. So there are videos that are hosted at Vimeo. And if we take a look at the actual structure and you could get this exact same setup inside Directus plus. But you can see here's the structure. We've got, a season. There's an episode number. There's a slug. There's a title. There's a Vimeo ID, and that's how we store this. And then we have episode people and lots of kind of complex relationships. There's also flows that are set up to generate transcripts. So, Vimeo videos. Alright. So I'll just give it more context here. We're gonna say, hey. Here's the ones I have. I have, Claude desktop MCP installation, cursor installation, Raycast installation. What do we have? We have a blog post workflow. Workflow. Real life AI content workflows workshop. And what? Landing page workflow. And what's the last one? Organize assets. Alright. So I don't know how well this is gonna do. I'll just copy paste the videos. Link. That's called desktop installation. This is cursor MCP installation, Raycast installation, blog post workflow, landing page workflow. And if I had to do this a lot, I would obviously try to optimize this even further. Landing page workflow, real life content workflows. Okay. Alright. So I'm not telling it anything about the structure. Right? It's already picked up the schema. And let's give it some more context. Here's the docs for the MCP server for additional context. Alright. So I'm gonna just go here to directus.i0\u002Fdocs. And there's, like, a a lot of descriptions and and things like that that go into this. I'm just gonna paste these docs, tools, prompts, workflows. Alright. So that's all the docs that I've got. I'm just gonna trust and and fire this off that in the episodes that this is actually gonna work out based on info below. Now look, I'm, again, not a prompt genius. This is all stuff that you can do right now. And if you learn a little bit about prompting more than me, you will get even further than this. So let's just see what happens. Is going to create the show for me. There's the description. This is gonna be published. It is a AI powered content management with Directus and MCP. Alright. So now it's creating a new season for that specific show. Looks like it is creating the individual episodes. Alright. So now if I go in here, we search for MCP. Right? I can see this. There's the card text, published, status, images. So we we need some images. I don't have to generate those, outside of this workflow. But if I go in, we look at the season, I could see that it's starting to populate the episodes for me. And is it done? Let's create the workflow episodes. I don't know if it was actually done or not. Alright. Organize assets. The workshop episode. Organize assets. Workflow. I'll just save and update that. So, again, hey. Like, on this workshop, we covered this collaboration model where, you you know, hey. I'm actually working alongside this, and I I think this is a great just a great showcase of that. Right? This is the MCP server. Now all I need to do is basically just test this out. Right? I've got the show. I've got the season for the show. I've got the episodes for the show, and all the descriptions are there. It looks like it's missing the episode people. So can you add what am I set up as? Is there Brian in here? There's also Brian Ross. I don't know who that guy is. Can you add Bryant Gillespie to the episode people for all the episodes and add Matt Miner for the last one, the workshop. Alright. So this is, you know, a fairly complex relationship. Like, this is a many to many relationship. And okay. It should so it's going to go in and query all the TV people to see if they exist. Then it's gonna go into add Brian Gillespie to all seven episodes and Matt Miner to the workshop episode. Now, again, this wouldn't be terribly difficult. I could go in and do this manually. But, again, let's take advantage of the AI where we can. I'll just hit refresh. We'll see what it's coming up with. Okay. So it's got the first one. Second one is good. Is it done with the episodes people for Ed Miner Matt Miner to the workshop episode? Added all the people to the episodes. Alright. Right. So Matt Miner, Brian Gillespie, there's the workshop. All I need are the images, and we're not gonna generate those via via this workflow. Now what I also am going to do here, right, now normally what I would have to do is go through each individual episode. So if I look for show contains MCP, that's gonna be case specific. So I would have to go in and generate the transcripts for these, but can you generate transcripts for all those episodes using flows? Let's see what this comes up with. And, again, look, I've this is my first time doing in this. I've not practiced this at all. We'll see what's gonna happen. There's a generate transcript flow that's designed to fetch Vimeo files and send them to Deepgram for transcription. This is exactly what we need. So it's gonna go through and call these flows. So now if I, like, go through and I look at the generate transcript, I can see that this has been triggered for that, for that episode. It's gonna continue with the rest of the episodes, and, that's a wrap. Right? So what I'm able to do here is just take a a literal ton of busy work out of the equation for me. And now all I gotta do is hop in Figma, design some images for this, and I could publish seven videos in record time. So that's it for this video. I guess this will be the eighth video that we upload to this MCP. But I can't wait to see what you guys build with this. Please share your feedback. Send us questions, comments, ideas that you have for, Directus and AI. I'll catch you on the next one. See you.","\u003Cp>Go behind the scenes to see the Directus MCP Server in action! This episode documents the actual workflow used to create this entire series, demonstrating:\u003C\u002Fp>\u003Cul>\u003Cli>Real-time AI collaboration for content creation\u003C\u002Fli>\u003Cli>Using MCP to analyze schema and create structured content\u003C\u002Fli>\u003Cli>Bulk episode creation and metadata management\u003C\u002Fli>\u003Cli>Adding people relationships and triggering automation flows\u003C\u002Fli>\u003Cli>The complete end-to-end workflow from concept to published series\u003C\u002Fli>\u003C\u002Ful>\u003Cp>This meta episode showcases the true power of AI-assisted content management - you'll see exactly how we went from a list of Vimeo IDs to a complete, structured series in minutes rather than hours.\u003C\u002Fp>\u003Cp>\u003Cstrong>Perfect for:\u003C\u002Fstrong> Content managers, developers, and anyone interested in seeing practical AI workflows in action. This is the \"show your work\" episode that demonstrates the real-world efficiency gains possible with the Directus MCP Server.\u003C\u002Fp>",[852],"ad33d1be-e902-4b48-9ae8-ef6d5326642e",[],{"year":768,"number":769,"id":770,"episodes":855,"show":856},[772,773,774,775,754,776,777,778],{"title":383,"slug":384},{"id":777,"slug":858,"vimeo_id":859,"description":860,"tile":861,"length":12,"resources":12,"people":12,"episode_number":129,"published":759,"title":862,"video_transcript_html":863,"video_transcript_text":864,"content":865,"seo":12,"status":19,"episode_people":866,"recommendations":868,"season":869},"building-landing-pages-with-ai","1088791484","Create compelling landing pages with AI assistance using the Directus MCP Server. Learn how to build high-converting pages by leveraging AI for content creation, structure, and optimization while maintaining design consistency.","dfe3565e-6009-460a-812a-80ea8ca1c7c1","Building Landing Pages with AI","\u003Cp>Speaker 0: Hey. What's up, guys? Brian here. And in this video, I'm gonna show you how to use AI tools like Cloud Desktop to quickly build landing pages inside Direct to CMS. Alright.\u003C\u002Fp>\u003Cp>So I've got this prompt up, and this is the first time that I've actually tried this prompt, so we'll see how it goes. Basically, I want Cloud Desktop to create a landing page inside my Directus instance for me. Now this is a complicated endeavor, because, one of the the huge benefits of Directus is the ability to create pages dynamically using our many to any relationships. Basically, page blocks. Right?\u003C\u002Fp>\u003Cp>So a page can be made up of a bunch of different blocks and each block with its own schema, its own data model, and its own presentation on the front end of the website. So it makes it really easy. I can also edit these things visually. But, still scaffolding out these pages, can be time consuming. There's no way around it.\u003C\u002Fp>\u003Cp>Like, we've gotta rearrange blocks on a page. So let's use Claude and this prompt that I've got set up. So I'm just gonna go here to my prompt section. We're gonna create a landing page. And the way this prompt works, basically, I've got a bunch of variables to fill in and the direct as content MCP picks up those variables that I've included in either the message or the system prompt and prompts me to fill those out.\u003C\u002Fp>\u003Cp>So, draft a landing page for a specific audience based on an idea or topic. So we're selling Directus the AI ready CMS. Cool. Desired action is going to be sign up for cloud instance. You could check this out on cloud.\u003C\u002Fp>\u003Cp>There's also a command that you can run-in our documentation to get this full CMS set up locally. Value proposition. AI plus Directus eliminates busy work and death by a thousand cuts. Descriptive tone, we wanna use professional but friendly and confident, super relatable. Alright.\u003C\u002Fp>\u003Cp>The audience that we're aiming for, technical marketers and developers. Specific pain points. Where's the pain point? Other solutions, too slow, too complicated, don't do enough. Specific solution outcome, Directus CMS is the best.\u003C\u002Fp>\u003Cp>Level of knowledge, mildly aware. Alright. This is a super detailed prompt, so I'm expecting the results to be amazing. Don't want to switch systems, migration pain, costs, etcetera. Alright.\u003C\u002Fp>\u003Cp>So this is our prompt. Basically, it has filled out all those variables for us. It's gonna send that to Claude, and we're gonna start building out this landing page. Right? So you're seeing this the first time, same as me.\u003C\u002Fp>\u003Cp>What's happening behind the scenes? The Directus Content MCP server is feeding these tools to Claude. Claude is calling its system prompt, that we've set up. So, hey, you're a direct assistant. Here's what you've got access to.\u003C\u002Fp>\u003Cp>It has understood our schema. So it understands the specific data model that it's working with. Right? And now it is working its way through the landing page. And here we could see it's already created the page.\u003C\u002Fp>\u003Cp>Right? So if I just refresh, Directus, the AI ready CMS, eliminate busy work forever. Right? So here's our page. And we don't see any blocks though.\u003C\u002Fp>\u003Cp>Right? Of course, this is also still a draft. But what's happening here is because the MCP and, by consequence of the LLM is now aware of my specific data model in Directus, it can go through and create these blocks for me that are a % on brand, that are a % correct to our data model. And let's see what it comes up with. Right?\u003C\u002Fp>\u003Cp>We got a CTA button group. It's adding buttons within that. See the live demo. Start a free cloud instance. That's our main, call out that we said.\u003C\u002Fp>\u003Cp>It's even picked up the Directus cloud registration URL just somewhere in the ether. Problem agitation section. So it's doing a ton of work on my behalf here and actually creating these blocks inside Directus, and then it's gonna come back and actually add those blocks to the page. Now if you were to scaffold this out yourself, maybe you're super efficient. Yeah.\u003C\u002Fp>\u003Cp>It's still gonna take you a while and you're gonna have to fill out a lot of these fields. So now it's going to add all this for us and the end result is gonna be something that we can come back and just edit manually. Right? So here I see all the different sections. It is creating all those for us.\u003C\u002Fp>\u003Cp>And this is what it's all about. Right? I don't know about you, but in sci fi, when I was a little kid, we were promised this dream of computers doing a lot of work for us, taking our ideas and and helping us achieve the result. So here's our landing page. I'm just gonna hit refresh.\u003C\u002Fp>\u003Cp>And maybe we wanna actually open this in the direct as visual editor so I can start going through this personally. Alright? So stop fighting your CMS. Start building. Other CMSs are too slow, too complicated, and don't do enough.\u003C\u002Fp>\u003Cp>Get back to what matters. Creating amazing digital experiences. So there's our headline. Here's kind of the problem agitation. Traditional CMSs weren't built for AI powered workflows.\u003C\u002Fp>\u003Cp>What if your CMS actually worked with you? We've got a pricing block, and it looks like it did miss some of the format here for this, which I'm curious about. But, we've got this landing page all scaffolded out. I could go in and manually adjust any of this that I need. Adjust any of this content.\u003C\u002Fp>\u003Cp>Command s for save and see those changes live. But this, wow, that is using the Directus Content MCP to build your landing pages out for you so you can go in and tweak them so they're just perfect. That's it for this video. Stay tuned for others in the MCP.\u003C\u002Fp>","Hey. What's up, guys? Brian here. And in this video, I'm gonna show you how to use AI tools like Cloud Desktop to quickly build landing pages inside Direct to CMS. Alright. So I've got this prompt up, and this is the first time that I've actually tried this prompt, so we'll see how it goes. Basically, I want Cloud Desktop to create a landing page inside my Directus instance for me. Now this is a complicated endeavor, because, one of the the huge benefits of Directus is the ability to create pages dynamically using our many to any relationships. Basically, page blocks. Right? So a page can be made up of a bunch of different blocks and each block with its own schema, its own data model, and its own presentation on the front end of the website. So it makes it really easy. I can also edit these things visually. But, still scaffolding out these pages, can be time consuming. There's no way around it. Like, we've gotta rearrange blocks on a page. So let's use Claude and this prompt that I've got set up. So I'm just gonna go here to my prompt section. We're gonna create a landing page. And the way this prompt works, basically, I've got a bunch of variables to fill in and the direct as content MCP picks up those variables that I've included in either the message or the system prompt and prompts me to fill those out. So, draft a landing page for a specific audience based on an idea or topic. So we're selling Directus the AI ready CMS. Cool. Desired action is going to be sign up for cloud instance. You could check this out on cloud. There's also a command that you can run-in our documentation to get this full CMS set up locally. Value proposition. AI plus Directus eliminates busy work and death by a thousand cuts. Descriptive tone, we wanna use professional but friendly and confident, super relatable. Alright. The audience that we're aiming for, technical marketers and developers. Specific pain points. Where's the pain point? Other solutions, too slow, too complicated, don't do enough. Specific solution outcome, Directus CMS is the best. Level of knowledge, mildly aware. Alright. This is a super detailed prompt, so I'm expecting the results to be amazing. Don't want to switch systems, migration pain, costs, etcetera. Alright. So this is our prompt. Basically, it has filled out all those variables for us. It's gonna send that to Claude, and we're gonna start building out this landing page. Right? So you're seeing this the first time, same as me. What's happening behind the scenes? The Directus Content MCP server is feeding these tools to Claude. Claude is calling its system prompt, that we've set up. So, hey, you're a direct assistant. Here's what you've got access to. It has understood our schema. So it understands the specific data model that it's working with. Right? And now it is working its way through the landing page. And here we could see it's already created the page. Right? So if I just refresh, Directus, the AI ready CMS, eliminate busy work forever. Right? So here's our page. And we don't see any blocks though. Right? Of course, this is also still a draft. But what's happening here is because the MCP and, by consequence of the LLM is now aware of my specific data model in Directus, it can go through and create these blocks for me that are a % on brand, that are a % correct to our data model. And let's see what it comes up with. Right? We got a CTA button group. It's adding buttons within that. See the live demo. Start a free cloud instance. That's our main, call out that we said. It's even picked up the Directus cloud registration URL just somewhere in the ether. Problem agitation section. So it's doing a ton of work on my behalf here and actually creating these blocks inside Directus, and then it's gonna come back and actually add those blocks to the page. Now if you were to scaffold this out yourself, maybe you're super efficient. Yeah. It's still gonna take you a while and you're gonna have to fill out a lot of these fields. So now it's going to add all this for us and the end result is gonna be something that we can come back and just edit manually. Right? So here I see all the different sections. It is creating all those for us. And this is what it's all about. Right? I don't know about you, but in sci fi, when I was a little kid, we were promised this dream of computers doing a lot of work for us, taking our ideas and and helping us achieve the result. So here's our landing page. I'm just gonna hit refresh. And maybe we wanna actually open this in the direct as visual editor so I can start going through this personally. Alright? So stop fighting your CMS. Start building. Other CMSs are too slow, too complicated, and don't do enough. Get back to what matters. Creating amazing digital experiences. So there's our headline. Here's kind of the problem agitation. Traditional CMSs weren't built for AI powered workflows. What if your CMS actually worked with you? We've got a pricing block, and it looks like it did miss some of the format here for this, which I'm curious about. But, we've got this landing page all scaffolded out. I could go in and manually adjust any of this that I need. Adjust any of this content. Command s for save and see those changes live. But this, wow, that is using the Directus Content MCP to build your landing pages out for you so you can go in and tweak them so they're just perfect. That's it for this video. Stay tuned for others in the MCP.","\u003Cp>Build powerful landing pages with AI-powered workflows. This comprehensive tutorial demonstrates:\u003C\u002Fp>\u003Cul>\u003Cli>Analyzing existing page structures and components\u003C\u002Fli>\u003Cli>Using AI to generate persuasive copy and calls-to-action\u003C\u002Fli>\u003Cli>Creating structured content with proper components\u003C\u002Fli>\u003Cli>Optimizing for conversion and user experience\u003C\u002Fli>\u003Cli>A\u002FB testing content variations with AI assistance\u003C\u002Fli>\u003C\u002Ful>\u003Cp>Perfect for marketers and content creators who want to accelerate landing page development without sacrificing quality or conversion potential.\u003C\u002Fp>",[867],"02b7f777-2c1d-4365-92da-138082985cec",[],{"year":768,"number":769,"id":770,"episodes":870,"show":871},[772,773,774,775,754,776,777,778],{"title":383,"slug":384},{"id":776,"slug":873,"vimeo_id":874,"description":875,"tile":876,"length":129,"resources":12,"people":12,"episode_number":195,"published":759,"title":877,"video_transcript_html":12,"video_transcript_text":12,"content":878,"seo":12,"status":19,"episode_people":879,"recommendations":881,"season":882},"ai-organize-assets-directus-mcp","1088809355","Transform your asset management with AI-powered organization. Learn how to use the Directus MCP Server to automatically analyze, categorize, and optimize your media library for better discoverability and workflow efficiency.","b711cdc8-8eb0-402a-95de-ccfde56bcc60","Organizing Assets","\u003Cp>Revolutionize your asset management workflow with AI assistance. This episode shows you how to:\u003C\u002Fp>\u003Cul>\u003Cli>Analyze image content automatically using AI vision capabilities\u003C\u002Fli>\u003Cli>Generate descriptive titles and alt text based on visual analysis\u003C\u002Fli>\u003Cli>Bulk update asset metadata and tags for better organization\u003C\u002Fli>\u003Cli>Create smart categorization systems\u003C\u002Fli>\u003Cli>Implement automated asset selection for content projects\u003C\u002Fli>\u003C\u002Ful>\u003Cp>Say goodbye to generic filenames and missing metadata. Learn how to build a well-organized, searchable asset library that saves time and improves content quality.\u003C\u002Fp>",[880],"548a3585-8a93-401e-b8fc-f2e19c181ac8",[],{"year":768,"number":769,"id":770,"episodes":883,"show":884},[772,773,774,775,754,776,777,778],{"title":383,"slug":384},{"id":694,"slug":886,"vimeo_id":887,"description":672,"tile":888,"length":400,"resources":12,"people":12,"episode_number":889,"published":890,"title":891,"video_transcript_html":892,"video_transcript_text":893,"content":12,"seo":894,"status":19,"episode_people":895,"recommendations":900,"season":901},"may-2025","1082491904","69658fd3-a1f8-4fc9-b9c8-37f004731e7d",10,"2025-05-14","May 2025","\u003Cp>Speaker 0: Alright. Hello, everyone. I hope you're all having a wonderful day. Welcome to the May changelog. And if you are joining us live or you're watching later on, hi.\u003C\u002Fp>\u003Cp>Hello. Thank you for joining us. I'm Beth, and I'm gonna be taking you through today's show. We have a lot of Brian today. It's very exciting.\u003C\u002Fp>\u003Cp>I, however, am gonna be kicking you off by taking you to Carmen with the product\u003C\u002Fp>\u003Cp>Speaker 1: updates. Thanks, Beth. And hey, everybody. Carmen here to tell you about what we've shipped in the last month or so with Directus eleven point seven point o through to eleven point seven point two. Now we've launched a couple of exciting new features, some bug fixes, and just one potential breaking change that you should know about.\u003C\u002Fp>\u003Cp>Now let's start with some potential breaking changes. As of Directus 11.7 o, we will no longer be supporting MySQL 5.7 in the API, so keep an eye out for that. One of the biggest improvements we've made is in the UX and UI of the visual editor module, so go check it out. Lastly, we've published the Directus schema builder package for mocking database schemas. So that's an overview of what we've been working on over the last month.\u003C\u002Fp>\u003Cp>Be sure to check out further details of the visual editor module improvements later on.\u003C\u002Fp>\u003Cp>Speaker 2: Hey. What's up? Brian here. I'm gonna tell you about some updates to, one of our more popular extensions that we've released to the Directus Labs program, the superheader. So the superheader is, basically a powerful header component that gives you a couple different pieces inside one box.\u003C\u002Fp>\u003Cp>So you get a a a title that uses dynamic values. You get a subtitle, so you can add things like published and, you know, additional metadata. You get contextual help, and you get an action bar where you can add links or you can run flows right from the form instead of opening in the right sidebar. So this has been pretty popular since we launched it, but we also got a slew of feedback from you guys. So keep the feedback coming.\u003C\u002Fp>\u003Cp>Love it. We have fixed, quite a few of the bugs that are were happening on opening different modal windows within modals. So that is resolved. And let's take a look at the new functionality. So one of the pieces that is brand new is the ability to open the help content in a modal instead of inline, as a like an accordion type experience.\u003C\u002Fp>\u003Cp>For me, I think this is a better experience, but we still support the inline help if you prefer that as well. Next, we have fixed some of the issues with, like, loading internal links. So you can see here, I can open this up in\u003C\u002Fp>\u003Cp>Speaker 3: the visual\u003C\u002Fp>\u003Cp>Speaker 2: editor. That does not have to spin up the app or open in a new window, so we've solved that. And then when opening flows or running flows, now we have the capability to, properly manage the overlay so that if you open something else inside your flow, which you can do let's take a look at this, where we wanna pick up the direct Us users. This is no longer showing behind that. So, just quality of life improvements.\u003C\u002Fp>\u003Cp>Now, onto the two biggest improvements. Right? The ability to translate the help content, which was a huge one for, anybody outside The US. And, initially, I wasn't super sure there was an easy way to get the this done, but we managed to do that. So here is how you enable translation support within the superheader.\u003C\u002Fp>\u003Cp>So we're here inside the data model. We go into our interface, and we scroll down, and now we have this enable help translations. So the help translations here, this uses the custom translation strings, and you'll have to set these up just like you would anywhere else, that you're leveraging those for fields. And what this will do, whatever you set for these translation strings will override whatever the the help content is here. So let's just take a look at what that experience looks like.\u003C\u002Fp>\u003Cp>I'm gonna go in. And if I look at my custom translations, you could see I've got two different values for this key. I've got one in English, and this is just HTML content. That still works just fine. And then I have the same thing in French.\u003C\u002Fp>\u003Cp>So now go to the home page. We could see, okay, this is English. If I go to my specific user, we just change the default language to French, and we go back. You know? So we see some of this content.\u003C\u002Fp>\u003Cp>Obviously, it doesn't have translation strings set up for it. It. But if we load our help, now we've got that help content localized for our user. So that just works out of the box with the standard setup. Now I'll go back in, just quickly set this back.\u003C\u002Fp>\u003Cp>And then the last piece of the puzzle is relational item support. So when we first launched this, we could not, fetch relationships. Now you can. So if you have, like, a site set up where I've got a site that this belongs to. We wanna show the name of the site or even inside, like, our actions where we want to leverage a, a domain from another collection or a field from a many to one relationship, we can do that now, which is great.\u003C\u002Fp>\u003Cp>I know some of you have multi tenant setups, and this enables that workflow. So now I can go in. I could see the site name. This is linked to Bryant's site if we open that up or Bryant's front end. And then if I hit view on website, that will load that link for me.\u003C\u002Fp>\u003Cp>So that is the update for the superheader. Hope you guys enjoy this. Super powerful. Got an update for you on our SEO plugin. So this was a a plug in that we launched not too long ago.\u003C\u002Fp>\u003Cp>We've added, a lot of nice updates to it. So, if you've already used it before, this should look familiar to you. The plug in is all about managing your SEO metadata in a simple, easy to use interface. So, out of the box, you kinda get page titles and meta descriptions, which is basically the the bare minimum that you want on the SEO side. We show you a a quick little search preview of what that would look like.\u003C\u002Fp>\u003Cp>You could populate this with a template from the page title, and we give you just some contextual help of, you know, how many characters you should have for the page title or the meta description. And if I go over those, you could see that that's gonna be truncated in our search preview. We've also added a preview for your social media links or OG images. So here, I can, upload my social images or my OG image and actually get a quick preview of what it's gonna look like if it's shared on different platforms, and what that link will actually look like. Just, again, helpful little context.\u003C\u002Fp>\u003Cp>But now we have added even more powerful features to this, based on feedback that we received. So, we can enable an advanced controls like search engine for, like, no index, no follow so that it doesn't follow links or this particular page doesn't get indexed. You can add site map controls if you want to change the frequency, which the crawlers look at your site, or the SEO fields. Those were, like, custom SEO fields where I can define, like, canonical URLs or basically any other metadata that I want to pass to my front end. But the new thing here is our focus key phrase.\u003C\u002Fp>\u003Cp>So we can actually analyze your content. So I'm gonna go into our pages collection, and let's set up the SEO plug in for our blog posts. So So I'm just gonna add this focus key phrase, and when I enable that, it asks me for a slug field and content fields. So where are we actually keeping the content that we're gonna render on the page? Now if you've ever u if you're coming from WordPress, this is very similar to something like Yoast or or some of those other SEO tools where the it's just static analysis of our content.\u003C\u002Fp>\u003Cp>So if I pull this up, we go over to our SEO tab, and here we've got the option to add our main key phrase. So if I add CMS as our main key phrase, you could see this analysis will happen almost immediately. It says, hey. Your focus key phrase does not appear in the content. So this keyword isn't showing up in my content.\u003C\u002Fp>\u003Cp>You know, maybe bunny would be a better one for this. Cool. And then it shows us, hey. Your subheadings, like your h ones, h twos, h threes, those do not contain that focus key phrase. So it gives us improvements, good results.\u003C\u002Fp>\u003Cp>Just a quick, nice little check on your content before you publish to know that you're covering your SEO basics. So that is the update for the SEO plugin. I I hope you'll find this helpful. Keep the feedback coming, please. We love it.\u003C\u002Fp>\u003Cp>Speaker 1: So what's new in content this month? We've been hard at work and published eight new articles all about CMSs or content management systems. Be sure to check them out. We've got articles on how to create a CMS and how to generate dynamic pages for CMSs in Astro, Nuxt, Next. Js, and SvelteKit.\u003C\u002Fp>\u003Cp>Don't miss them.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. And as a reminder, you can find the extensions in github.com\u002Fdirectors-labs\u002Fextensions and also the tutorials on directors.i0\u002Fdocs\u002Ftutorials. Next up, we have more of Brian. I told you we were super lucky today. He is gonna be talking us through the visual editor updates.\u003C\u002Fp>\u003Cp>Speaker 2: Super excited for this one. This is a huge feature release for us. Visual editing. It's here. It's real.\u003C\u002Fp>\u003Cp>It's freaking awesome. And, yeah, I don't get excite well, I get very excited about a lot of things, but I'm super excited about this one, and you should be too. This is gonna change the game. Right? The problem with most headless CMS experiences, great for us as developers, we love the structured data.\u003C\u002Fp>\u003Cp>Structured data is great for LLMs and AI. But, when you go headless, you kinda miss that that standard see what I'm editing experience. Right? You're relying on the front end to interpret a lot of that. So in the past, we've had live preview.\u003C\u002Fp>\u003Cp>That works great. But what if we take this a step further and I just allow my content editors not to have to do this in a form, but as they're browsing the page and they could see the edits they're making right in line. That is visual editing. It's here. Let's take a look at it.\u003C\u002Fp>\u003Cp>So I'm just gonna open this page up. This is our super header extension, but I'm just gonna open this page in our visual editor. So here I could see, we've got our content, and you'll notice as I start mousing over things, because I've enabled visual editor and it's, visual editor is two pieces. Right? There's a module inside Directus, and then there's a front end library that you install on your website.\u003C\u002Fp>\u003Cp>Works with any of your favorite frameworks. So Nuxt, Next, Astro, Svelte. We've actually got starter kits for you. % ready to go with this integrated, so you don't really have to do a lot of work to get started with this, but super powerful. Right up here at the top, we'll see our different URLs that are available to navigate to.\u003C\u002Fp>\u003Cp>I can also do this where I just toggle the navigation and hide that. And basically from here, it is like a full on browsing experience. I can jump between different pages and I can hover over an element, go in and edit that. Edit this form. Great.\u003C\u002Fp>\u003Cp>All the keyboard shortcuts that you would expect are here. So I can hit command save to save that if I'm opening this up. I can hit escape to close. So it's a very nice experience. Up at the top, I can also toggle all the editable elements.\u003C\u002Fp>\u003Cp>So I can see everything that I can edit in one one go. Super easy. Right? I can go in and update all of my navigation items. I could add new items to my navigation.\u003C\u002Fp>\u003Cp>And, basically, this is, all just using the data model that you've got set up in Directus. On the front end, you're enabling these fields and the behavior that you want through data attributes. Couldn't be simpler to integrate, works with any deployment target, whether that's Netlify or Vercel, just a regular node, long running server. Whatever you've got, you can make this work. And this is baked into core as well, which, you know, it's not hidden behind some enterprise plan, which is huge.\u003C\u002Fp>\u003Cp>So I'll I'll just go in and let's navigate to our page at the bottom here. This is integrated into our front end where you can see I can now just drag and drop these blocks. And instead of doing this in a form and hitting save and waiting for the changes to happen, now I can see this happen directly in line. So this is content editing, and this is freaking awesome. So number one, your content editors are going to love this.\u003C\u002Fp>\u003Cp>You know, definitely check this out. I'm gonna show you inside our docs where this is located. First and foremost, like I said, we've got starters ready to go for you. There's a command that you can just copy here, n p x directus dash template dash cli at latest init. Copy this.\u003C\u002Fp>\u003Cp>Run this. It will pull down a example of our CMS template that you see here, and then it will also prompt you to install your favorite front end framework, which, again, we've got it all set up. But if you go to our docs, go to the guide section, and under content, look for the visual editor. So this is a a quick preview of what it looks like. We've got documentation on the front end library.\u003C\u002Fp>\u003Cp>Again, that works through data attributes. You just install this, do a little bit of config, and boom. Your content editors will love you. They'll be 10 times more efficient, 10 times more productive, and I this is just an amazing feature all around. I I'm super excited about it.\u003C\u002Fp>\u003Cp>I hope you are too.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Thank you for those updates, Brian. If you are regular here at the changelog, first of all, thank you. You are my favorite people. And secondly, you will know that the community showcase is my all time favorite part of this show, and we are back, and I'm so excited for it.\u003C\u002Fp>\u003Cp>So we have Jochem talking through his currency extension next.\u003C\u002Fp>\u003Cp>Speaker 4: Hello, everybody. My name is Jochem Gourdes, and I'm a software developer from The Netherlands. In my day to day job, I have a lot of focus on building software for the app, mainly in the front end space. My go to back end to use with my front end applications and the one that I really enjoy using is Directus. And one of the things I really like about Directus is the extensibility via custom extensions.\u003C\u002Fp>\u003Cp>And that's also the topic of my video today. I recently built and released an extension called simple currency fields. The main goal of this extension is to provide a custom editor interface and an inline display for the Data Studio to enter and view currency values the same convenient way as we're used to in the Director Studio. And why currency values? Well, for example, to store prices for products in an ecommerce store.\u003C\u002Fp>\u003Cp>The reason I wanted to build this extension was because the default input field field for the decimal type didn't give me enough flexibility for safely handling user inputs and correctly displaying a currency value. And as a technical side note, I did want to extend upon the decimal type for the fields because of its accuracy in the database. So the extension will actually only work with fields that have decimal as their type. I've prepared a tiny demo within a local direct instance on my computer and they'll be using a custom product collection I've prepared. So let's have a look.\u003C\u002Fp>\u003Cp>So let's have a look at our product collection. As you can see, we have a couple of pretty common, fields, status, image, name, and price fields. Interface size, also price, and a couple of other fields fields which are not important for this demo. For the inputs, for the interface side, we have a euro symbol. And for our display portion, we also have a euro symbol.\u003C\u002Fp>\u003Cp>And if we are going to our product data model, we can see that price field is configured as a decimal field with precision 19 and scale of two, which means we can store pretty big, numbers in our database for these fields. And in the interface tab, you can see how I configured the euro sign as icons for the in for the interface and euro sign as prefix that's being used by the display. I've also enabled auto formatting for this field, and I think this is a pretty common way how you normally would configure currency fields in directors. So what I want to do next is change my system settings, my language settings to Dutch. Let's do that.\u003C\u002Fp>\u003Cp>And let's go back to our product collection. And one of the problems I was talking about should become visible now because the way the price is presented here is not common for, the Dutch language. For instance, it should be using a comma as a decimal separator, but instead it's still showing a dot for it. If we now go into one of the products and edit it as we and edit it, we can see the dot here as well. Important to note here though is that users should also use a document changing the value here.\u003C\u002Fp>\u003Cp>And this is probably not convenient for a lot of users and maybe even worse if I copy a valid ditch price into the fields, that will just change it into a comma here, we will get a very crisp and clear error message. At least I hope so. Well, not really. Most technical users would be able to figure out what's going on here, but most probably would not. So these are a couple of the things I want to solve with the extension.\u003C\u002Fp>\u003Cp>A simple way to input and view currency values preferably in the local of the user handling the content. So let's solve that. For this, we can go to our extensions and we can see that we have no extensions installed yet. And if you go to the marketplace, we can search for the simple fields, simple currency field, extension. We can install it.\u003C\u002Fp>\u003Cp>We have to reload the page and then we can go back to our collection to the price fields. And let's start with the display, now with the interface. And let's change the input to currency. And let's configure our currency to euro. Save it.\u003C\u002Fp>\u003Cp>And if we go back to our product listing and to a product, we can now see properly formatted currency value. In fact, I can type whatever I want Unless it's a number, it will not accept it as input. What's also need to show here is that as soon as you leave the field, the field will automatically be formatted into the correct logo for the user. So it also supports rounding. So if I add a six, it should be become 96, and that will also work.\u003C\u002Fp>\u003Cp>But you can also add in bigger numbers to get a thousand separator like this. And if you leave the field, it will automatically, probably format it. If you save it to the same thing and you can also, copy in numbers with a thousand separator in it. Like this, for instance, as an example. If I copy that into the fields, it will automatically use the proper inputting format.\u003C\u002Fp>\u003Cp>And as soon as I leave it, it will use the proper display formatting. And yeah. Let's also enable it for the display. So if you go back to our collection, go to the display settings, remove the configured version and pick currency. Save that.\u003C\u002Fp>\u003Cp>The currency symbol is automatically derived from the interface settings. Let's go back to our collection and we can see it's properly formatting here as well. One last thing to show here, I think, is that, instead of changing, current fields, you can also easily add new currency fields to any of your collections. For instance, let's imagine we need a subtotal field in our product collection. We can easily do so by selecting the currency, field here, which is just a placeholder for the input field with types at two decimal.\u003C\u002Fp>\u003Cp>Let's name that subtotal. Let's also give that the same, currency symbol. And let's go to the advanced, options in order to set precision and scale to be the same as the price field. Save it. Move it and let's go back to our collection.\u003C\u002Fp>\u003Cp>And here you can see now that we can use the field with the with the extension. So that was my demo. With this extension, I hope to make handling currency values in Directus not only easier, but also more intuitive and user friendly, especially for those working in different logos. If you would like to try this extension yourself, you can find it on the Directus marketplace and on my GitHub profile. You can also find me on Directus Discord server if you want to chat.\u003C\u002Fp>\u003Cp>And if you have any feedback, suggestions, or ideas for improvements, feel free to reach out. I'm always open for to suggestions and collaboration. Thank you for watching this demo, and I look forward to see how the community makes use of the simple currency field extension. Have a great day. Bye.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. And there's actually since Yohum very kindly, recorded that video, there is a new post on our new community platform, which I will link here as well. And that brings us to our next section, which is that Ryke and I had a little chat about just that. Welcome. We have Raik with us who is do you want to introduce yourself in case people don't know?\u003C\u002Fp>\u003Cp>Maybe they're finding this on a different platform.\u003C\u002Fp>\u003Cp>Speaker 3: Yes, please. I'm the CTO and cofounder\u003C\u002Fp>\u003Cp>Speaker 0: And you're here with exciting news, which you may have already heard if you are in our Discord. But what is the exciting news, Royce?\u003C\u002Fp>\u003Cp>Speaker 3: We have a new community platform that is very exciting news.\u003C\u002Fp>\u003Cp>Speaker 0: What is the new platform? Where is it?\u003C\u002Fp>\u003Cp>Speaker 3: New platform is community.directors.ao, and it's a discourse forum.\u003C\u002Fp>\u003Cp>Speaker 0: Are we about to battle it out between Discord and discourse right now?\u003C\u002Fp>\u003Cp>Speaker 3: I think we should. Shouldn't we?\u003C\u002Fp>\u003Cp>Speaker 0: I think we should. I think we should. So we're moving from Discord to Discourse. Why? Why are we why are we doing that?\u003C\u002Fp>\u003Cp>Speaker 3: Right. So we've been on Discord for, gosh, years now. Right? We started it as an open community server, and it kinda turned into a bit of a, you know, q and a type of thing, a chat with contributors. But over time, we realized as that community grew, we just hit a couple of roadblocks, really.\u003C\u002Fp>\u003Cp>It's like the search kinda didn't work, so people have they have to ask the same question multiple times. You know, the the questions don't show up on the Internet. So if you Google for stuff, you're not gonna find any of the questions that were asked on Discord. And generally speaking, you know, it it started to you know, there's some cracks showed up. There were some cracks at that skill for sure.\u003C\u002Fp>\u003Cp>So the the main points that we realized we wanted to solve is a, just that findability. Right? Make it accessible, make it accessible to the Internet, make it accessible and therefore and then and then the second part is make sure that it's in a in a structured format that is a little bit of QA rather than just check messages that are all, you know, intertwined like that. So, yeah, we we chose Discourse as sort of the tool for the job, and that's now accessible.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. It is. So you can view it without a, an account or you can make an account and you can start posting and liking things. What's happening to the current Discord? Was is it completely going?\u003C\u002Fp>\u003Cp>Are we are we shutting it down? Oh, no.\u003C\u002Fp>\u003Cp>Speaker 3: No. No. No. No. No.\u003C\u002Fp>\u003Cp>So what Discord is really good for is active chatting, obviously. I mean, it's a chat platform. Right? So we're gonna keep it around for chatting with the community and just talking to each other, talking with contributors, talking to, you know, folks in the community in general. But we are explicitly saying, hey.\u003C\u002Fp>\u003Cp>Discourse is just the better tool for the job when it comes to q and a. So if you have any questions around Directus or how to use it or you're running into something, Discourse is gonna be the place to go. Right? Community.directus.ao. And for everything else, if you just, you know, wanna chat, wanna hang out, Discord is gonna stick around.\u003C\u002Fp>\u003Cp>Speaker 0: I'm gonna give you a very quick tour of the new community platform. If you head over to community.directors.io, you'll land here, which has all of the top posts. You can also filter from all time, year, quarter, month, week or on today. There's also the ability to see the latest activity on posts. You can also filter with tags or in categories.\u003C\u002Fp>\u003Cp>So for example, if you want to see the showcases, here you go. You have the ability to sign up with your Google, your GitHub, your Discord or your email address. Once you've signed in 'here I am is definitely not Beth' you'll be able to post topics, so you can pick whichever category is the right one for your post and any tags that relate to it as well as a title and a body of text. You can also interact with other people's posts by liking, you've got a few choice of different emojis, as well as replying. If you've got any questions about the new platform or what's happening with Discord or anything else, let us know.\u003C\u002Fp>\u003Cp>The general chat in Discord is still available or make a new topic on our community platform.\u003C\u002Fp>\u003Cp>Speaker 2: Welcome back to another episode of Short Hops. I'm your host Brian Gillespie. And in this episode, I'm gonna give you one of the coolest tips and tricks that I've probably ever shared here on Short Hops. So stick with me on this one. It's really cool.\u003C\u002Fp>\u003Cp>Alright. So what are we focused on today? We're gonna build a better user experience using filters on our relational interfaces. That is a mouthful, kind of underwhelming title. But let's dive into an example project and I'll show you what we've got going on.\u003C\u002Fp>\u003Cp>So this is a sample Directus instance. This is my agency OS project that we put out under the Directus Labs organization. Be sure to check that out. But I wanna show you the scenario. So I'm creating a project in here and we've got an organization and we could add multiple contacts to this.\u003C\u002Fp>\u003Cp>So a a project belongs to an organization. Let's go with Tesla, in this case, pretty ambiguous name. And then I want to add an existing contact to this. But you could see here here's my problem. If I take a look, I get all the contacts for this.\u003C\u002Fp>\u003Cp>So, you know, I'm only wanting to be able to select contacts that are at Tesla. So this is kind of a a dependency here. How do we manage something like this? So I'm just gonna open the settings. We're gonna go into our data model.\u003C\u002Fp>\u003Cp>I'm gonna look for the projects collection. And here inside organization, we've got contacts right beside it. What I'm looking for, you know, we could see the relationship tab and how all these things come together. But when I go into the interface, and this is a mini to mini collection or relationship, so you'll have to be mindful of how you've got your relationship set up. But if I want to set that up dependent upon a field that we already have, I could do something like this.\u003C\u002Fp>\u003Cp>Down here in my filter, you know, I could go in and and like hard code a value of, you know, show me all the contacts where the name contains Ashley. But we want this to be dynamic, right? So I'm gonna drill into our organizations. This is a many to many relationship. And I'm gonna say organizations dot ID equals, and I could do this.\u003C\u002Fp>\u003Cp>So I'm gonna do mustache brackets here, hold on, and And I'm gonna do organization, which is the name of that other field inside our data model. Alright. Great. So let's take a look at this and we'll go back. We'll create a new project.\u003C\u002Fp>\u003Cp>Right now if I click add existing, no contact show up. How come? Right? So what I could do here, we'll say direct us test. That's the company we wanna do this project for.\u003C\u002Fp>\u003Cp>And now I only see the contacts who are part of that organization. Right? Likewise, if I change this to Tesla, I open it up and I see I've got Elon Musk here as the only option to select. So that's it. Take a look at the filter on any of the relational interfaces that you've set up.\u003C\u002Fp>\u003Cp>This is a a nice little technique to use in certain scenarios like this one. That's it for this episode. Stay tuned for the next one.\u003C\u002Fp>\u003Cp>Speaker 0: We want to take a moment towards the end of the change log for thanking our amazing community contributors who give their time to improve the director's project. Since last month, there have been three contributors. A huge thank you to Osman for fixing duplicate values for is one off filter, Gevelium for ensuring that the context menu of a field in the data model settings appear at full height, and to Ziles for adding data collection and field name attributes to parent wrapper for all form elements. Thank you again. You can see their specific pull requests inside of the full release notes on GitHub.\u003C\u002Fp>\u003Cp>Lastly, we also want to take the time to thank our GitHub Sponsors of April who financially contribute to their Exodus development. A huge thank you to Weifan, Entel, Mike, Fergus, Omar, Marcus, Mission Control, Peter, Utomic, Steven, James, Nonlinear, Andreas, Khan, John, Jamiluddin, Burb, Adam, Jason, Birka, Yuya, Vincent, CK, Valentino, Jens, Wayne, and Yannis. The money we are given from our GitHub sponsors goes straight back to community members who build tooling and extensions for the director's ecosystem. Thank you again for being part of that. Alright.\u003C\u002Fp>\u003Cp>That is everything for this month's changelog. Thank you so much. If you've made it to this point, you are incredible, and I appreciate you spending the time with us. I hope to see you next month. In the meantime, do go check out the new community platform.\u003C\u002Fp>\u003Cp>And if you've got any questions, you've got two choices now. You can either go into the Discord or you can be top tier and go to our new community platform. So please go see you over there and let go and check out more of what's been going on. Have a great rest of your day, your week, and your month, everyone, and I will see you again shortly.\u003C\u002Fp>","Alright. Hello, everyone. I hope you're all having a wonderful day. Welcome to the May changelog. And if you are joining us live or you're watching later on, hi. Hello. Thank you for joining us. I'm Beth, and I'm gonna be taking you through today's show. We have a lot of Brian today. It's very exciting. I, however, am gonna be kicking you off by taking you to Carmen with the product updates. Thanks, Beth. And hey, everybody. Carmen here to tell you about what we've shipped in the last month or so with Directus eleven point seven point o through to eleven point seven point two. Now we've launched a couple of exciting new features, some bug fixes, and just one potential breaking change that you should know about. Now let's start with some potential breaking changes. As of Directus 11.7 o, we will no longer be supporting MySQL 5.7 in the API, so keep an eye out for that. One of the biggest improvements we've made is in the UX and UI of the visual editor module, so go check it out. Lastly, we've published the Directus schema builder package for mocking database schemas. So that's an overview of what we've been working on over the last month. Be sure to check out further details of the visual editor module improvements later on. Hey. What's up? Brian here. I'm gonna tell you about some updates to, one of our more popular extensions that we've released to the Directus Labs program, the superheader. So the superheader is, basically a powerful header component that gives you a couple different pieces inside one box. So you get a a a title that uses dynamic values. You get a subtitle, so you can add things like published and, you know, additional metadata. You get contextual help, and you get an action bar where you can add links or you can run flows right from the form instead of opening in the right sidebar. So this has been pretty popular since we launched it, but we also got a slew of feedback from you guys. So keep the feedback coming. Love it. We have fixed, quite a few of the bugs that are were happening on opening different modal windows within modals. So that is resolved. And let's take a look at the new functionality. So one of the pieces that is brand new is the ability to open the help content in a modal instead of inline, as a like an accordion type experience. For me, I think this is a better experience, but we still support the inline help if you prefer that as well. Next, we have fixed some of the issues with, like, loading internal links. So you can see here, I can open this up in the visual editor. That does not have to spin up the app or open in a new window, so we've solved that. And then when opening flows or running flows, now we have the capability to, properly manage the overlay so that if you open something else inside your flow, which you can do let's take a look at this, where we wanna pick up the direct Us users. This is no longer showing behind that. So, just quality of life improvements. Now, onto the two biggest improvements. Right? The ability to translate the help content, which was a huge one for, anybody outside The US. And, initially, I wasn't super sure there was an easy way to get the this done, but we managed to do that. So here is how you enable translation support within the superheader. So we're here inside the data model. We go into our interface, and we scroll down, and now we have this enable help translations. So the help translations here, this uses the custom translation strings, and you'll have to set these up just like you would anywhere else, that you're leveraging those for fields. And what this will do, whatever you set for these translation strings will override whatever the the help content is here. So let's just take a look at what that experience looks like. I'm gonna go in. And if I look at my custom translations, you could see I've got two different values for this key. I've got one in English, and this is just HTML content. That still works just fine. And then I have the same thing in French. So now go to the home page. We could see, okay, this is English. If I go to my specific user, we just change the default language to French, and we go back. You know? So we see some of this content. Obviously, it doesn't have translation strings set up for it. It. But if we load our help, now we've got that help content localized for our user. So that just works out of the box with the standard setup. Now I'll go back in, just quickly set this back. And then the last piece of the puzzle is relational item support. So when we first launched this, we could not, fetch relationships. Now you can. So if you have, like, a site set up where I've got a site that this belongs to. We wanna show the name of the site or even inside, like, our actions where we want to leverage a, a domain from another collection or a field from a many to one relationship, we can do that now, which is great. I know some of you have multi tenant setups, and this enables that workflow. So now I can go in. I could see the site name. This is linked to Bryant's site if we open that up or Bryant's front end. And then if I hit view on website, that will load that link for me. So that is the update for the superheader. Hope you guys enjoy this. Super powerful. Got an update for you on our SEO plugin. So this was a a plug in that we launched not too long ago. We've added, a lot of nice updates to it. So, if you've already used it before, this should look familiar to you. The plug in is all about managing your SEO metadata in a simple, easy to use interface. So, out of the box, you kinda get page titles and meta descriptions, which is basically the the bare minimum that you want on the SEO side. We show you a a quick little search preview of what that would look like. You could populate this with a template from the page title, and we give you just some contextual help of, you know, how many characters you should have for the page title or the meta description. And if I go over those, you could see that that's gonna be truncated in our search preview. We've also added a preview for your social media links or OG images. So here, I can, upload my social images or my OG image and actually get a quick preview of what it's gonna look like if it's shared on different platforms, and what that link will actually look like. Just, again, helpful little context. But now we have added even more powerful features to this, based on feedback that we received. So, we can enable an advanced controls like search engine for, like, no index, no follow so that it doesn't follow links or this particular page doesn't get indexed. You can add site map controls if you want to change the frequency, which the crawlers look at your site, or the SEO fields. Those were, like, custom SEO fields where I can define, like, canonical URLs or basically any other metadata that I want to pass to my front end. But the new thing here is our focus key phrase. So we can actually analyze your content. So I'm gonna go into our pages collection, and let's set up the SEO plug in for our blog posts. So So I'm just gonna add this focus key phrase, and when I enable that, it asks me for a slug field and content fields. So where are we actually keeping the content that we're gonna render on the page? Now if you've ever u if you're coming from WordPress, this is very similar to something like Yoast or or some of those other SEO tools where the it's just static analysis of our content. So if I pull this up, we go over to our SEO tab, and here we've got the option to add our main key phrase. So if I add CMS as our main key phrase, you could see this analysis will happen almost immediately. It says, hey. Your focus key phrase does not appear in the content. So this keyword isn't showing up in my content. You know, maybe bunny would be a better one for this. Cool. And then it shows us, hey. Your subheadings, like your h ones, h twos, h threes, those do not contain that focus key phrase. So it gives us improvements, good results. Just a quick, nice little check on your content before you publish to know that you're covering your SEO basics. So that is the update for the SEO plugin. I I hope you'll find this helpful. Keep the feedback coming, please. We love it. So what's new in content this month? We've been hard at work and published eight new articles all about CMSs or content management systems. Be sure to check them out. We've got articles on how to create a CMS and how to generate dynamic pages for CMSs in Astro, Nuxt, Next. Js, and SvelteKit. Don't miss them. Alright. And as a reminder, you can find the extensions in github.com\u002Fdirectors-labs\u002Fextensions and also the tutorials on directors.i0\u002Fdocs\u002Ftutorials. Next up, we have more of Brian. I told you we were super lucky today. He is gonna be talking us through the visual editor updates. Super excited for this one. This is a huge feature release for us. Visual editing. It's here. It's real. It's freaking awesome. And, yeah, I don't get excite well, I get very excited about a lot of things, but I'm super excited about this one, and you should be too. This is gonna change the game. Right? The problem with most headless CMS experiences, great for us as developers, we love the structured data. Structured data is great for LLMs and AI. But, when you go headless, you kinda miss that that standard see what I'm editing experience. Right? You're relying on the front end to interpret a lot of that. So in the past, we've had live preview. That works great. But what if we take this a step further and I just allow my content editors not to have to do this in a form, but as they're browsing the page and they could see the edits they're making right in line. That is visual editing. It's here. Let's take a look at it. So I'm just gonna open this page up. This is our super header extension, but I'm just gonna open this page in our visual editor. So here I could see, we've got our content, and you'll notice as I start mousing over things, because I've enabled visual editor and it's, visual editor is two pieces. Right? There's a module inside Directus, and then there's a front end library that you install on your website. Works with any of your favorite frameworks. So Nuxt, Next, Astro, Svelte. We've actually got starter kits for you. % ready to go with this integrated, so you don't really have to do a lot of work to get started with this, but super powerful. Right up here at the top, we'll see our different URLs that are available to navigate to. I can also do this where I just toggle the navigation and hide that. And basically from here, it is like a full on browsing experience. I can jump between different pages and I can hover over an element, go in and edit that. Edit this form. Great. All the keyboard shortcuts that you would expect are here. So I can hit command save to save that if I'm opening this up. I can hit escape to close. So it's a very nice experience. Up at the top, I can also toggle all the editable elements. So I can see everything that I can edit in one one go. Super easy. Right? I can go in and update all of my navigation items. I could add new items to my navigation. And, basically, this is, all just using the data model that you've got set up in Directus. On the front end, you're enabling these fields and the behavior that you want through data attributes. Couldn't be simpler to integrate, works with any deployment target, whether that's Netlify or Vercel, just a regular node, long running server. Whatever you've got, you can make this work. And this is baked into core as well, which, you know, it's not hidden behind some enterprise plan, which is huge. So I'll I'll just go in and let's navigate to our page at the bottom here. This is integrated into our front end where you can see I can now just drag and drop these blocks. And instead of doing this in a form and hitting save and waiting for the changes to happen, now I can see this happen directly in line. So this is content editing, and this is freaking awesome. So number one, your content editors are going to love this. You know, definitely check this out. I'm gonna show you inside our docs where this is located. First and foremost, like I said, we've got starters ready to go for you. There's a command that you can just copy here, n p x directus dash template dash cli at latest init. Copy this. Run this. It will pull down a example of our CMS template that you see here, and then it will also prompt you to install your favorite front end framework, which, again, we've got it all set up. But if you go to our docs, go to the guide section, and under content, look for the visual editor. So this is a a quick preview of what it looks like. We've got documentation on the front end library. Again, that works through data attributes. You just install this, do a little bit of config, and boom. Your content editors will love you. They'll be 10 times more efficient, 10 times more productive, and I this is just an amazing feature all around. I I'm super excited about it. I hope you are too. Alright. Thank you for those updates, Brian. If you are regular here at the changelog, first of all, thank you. You are my favorite people. And secondly, you will know that the community showcase is my all time favorite part of this show, and we are back, and I'm so excited for it. So we have Jochem talking through his currency extension next. Hello, everybody. My name is Jochem Gourdes, and I'm a software developer from The Netherlands. In my day to day job, I have a lot of focus on building software for the app, mainly in the front end space. My go to back end to use with my front end applications and the one that I really enjoy using is Directus. And one of the things I really like about Directus is the extensibility via custom extensions. And that's also the topic of my video today. I recently built and released an extension called simple currency fields. The main goal of this extension is to provide a custom editor interface and an inline display for the Data Studio to enter and view currency values the same convenient way as we're used to in the Director Studio. And why currency values? Well, for example, to store prices for products in an ecommerce store. The reason I wanted to build this extension was because the default input field field for the decimal type didn't give me enough flexibility for safely handling user inputs and correctly displaying a currency value. And as a technical side note, I did want to extend upon the decimal type for the fields because of its accuracy in the database. So the extension will actually only work with fields that have decimal as their type. I've prepared a tiny demo within a local direct instance on my computer and they'll be using a custom product collection I've prepared. So let's have a look. So let's have a look at our product collection. As you can see, we have a couple of pretty common, fields, status, image, name, and price fields. Interface size, also price, and a couple of other fields fields which are not important for this demo. For the inputs, for the interface side, we have a euro symbol. And for our display portion, we also have a euro symbol. And if we are going to our product data model, we can see that price field is configured as a decimal field with precision 19 and scale of two, which means we can store pretty big, numbers in our database for these fields. And in the interface tab, you can see how I configured the euro sign as icons for the in for the interface and euro sign as prefix that's being used by the display. I've also enabled auto formatting for this field, and I think this is a pretty common way how you normally would configure currency fields in directors. So what I want to do next is change my system settings, my language settings to Dutch. Let's do that. And let's go back to our product collection. And one of the problems I was talking about should become visible now because the way the price is presented here is not common for, the Dutch language. For instance, it should be using a comma as a decimal separator, but instead it's still showing a dot for it. If we now go into one of the products and edit it as we and edit it, we can see the dot here as well. Important to note here though is that users should also use a document changing the value here. And this is probably not convenient for a lot of users and maybe even worse if I copy a valid ditch price into the fields, that will just change it into a comma here, we will get a very crisp and clear error message. At least I hope so. Well, not really. Most technical users would be able to figure out what's going on here, but most probably would not. So these are a couple of the things I want to solve with the extension. A simple way to input and view currency values preferably in the local of the user handling the content. So let's solve that. For this, we can go to our extensions and we can see that we have no extensions installed yet. And if you go to the marketplace, we can search for the simple fields, simple currency field, extension. We can install it. We have to reload the page and then we can go back to our collection to the price fields. And let's start with the display, now with the interface. And let's change the input to currency. And let's configure our currency to euro. Save it. And if we go back to our product listing and to a product, we can now see properly formatted currency value. In fact, I can type whatever I want Unless it's a number, it will not accept it as input. What's also need to show here is that as soon as you leave the field, the field will automatically be formatted into the correct logo for the user. So it also supports rounding. So if I add a six, it should be become 96, and that will also work. But you can also add in bigger numbers to get a thousand separator like this. And if you leave the field, it will automatically, probably format it. If you save it to the same thing and you can also, copy in numbers with a thousand separator in it. Like this, for instance, as an example. If I copy that into the fields, it will automatically use the proper inputting format. And as soon as I leave it, it will use the proper display formatting. And yeah. Let's also enable it for the display. So if you go back to our collection, go to the display settings, remove the configured version and pick currency. Save that. The currency symbol is automatically derived from the interface settings. Let's go back to our collection and we can see it's properly formatting here as well. One last thing to show here, I think, is that, instead of changing, current fields, you can also easily add new currency fields to any of your collections. For instance, let's imagine we need a subtotal field in our product collection. We can easily do so by selecting the currency, field here, which is just a placeholder for the input field with types at two decimal. Let's name that subtotal. Let's also give that the same, currency symbol. And let's go to the advanced, options in order to set precision and scale to be the same as the price field. Save it. Move it and let's go back to our collection. And here you can see now that we can use the field with the with the extension. So that was my demo. With this extension, I hope to make handling currency values in Directus not only easier, but also more intuitive and user friendly, especially for those working in different logos. If you would like to try this extension yourself, you can find it on the Directus marketplace and on my GitHub profile. You can also find me on Directus Discord server if you want to chat. And if you have any feedback, suggestions, or ideas for improvements, feel free to reach out. I'm always open for to suggestions and collaboration. Thank you for watching this demo, and I look forward to see how the community makes use of the simple currency field extension. Have a great day. Bye. Alright. And there's actually since Yohum very kindly, recorded that video, there is a new post on our new community platform, which I will link here as well. And that brings us to our next section, which is that Ryke and I had a little chat about just that. Welcome. We have Raik with us who is do you want to introduce yourself in case people don't know? Maybe they're finding this on a different platform. Yes, please. I'm the CTO and cofounder And you're here with exciting news, which you may have already heard if you are in our Discord. But what is the exciting news, Royce? We have a new community platform that is very exciting news. What is the new platform? Where is it? New platform is community.directors.ao, and it's a discourse forum. Are we about to battle it out between Discord and discourse right now? I think we should. Shouldn't we? I think we should. I think we should. So we're moving from Discord to Discourse. Why? Why are we why are we doing that? Right. So we've been on Discord for, gosh, years now. Right? We started it as an open community server, and it kinda turned into a bit of a, you know, q and a type of thing, a chat with contributors. But over time, we realized as that community grew, we just hit a couple of roadblocks, really. It's like the search kinda didn't work, so people have they have to ask the same question multiple times. You know, the the questions don't show up on the Internet. So if you Google for stuff, you're not gonna find any of the questions that were asked on Discord. And generally speaking, you know, it it started to you know, there's some cracks showed up. There were some cracks at that skill for sure. So the the main points that we realized we wanted to solve is a, just that findability. Right? Make it accessible, make it accessible to the Internet, make it accessible and therefore and then and then the second part is make sure that it's in a in a structured format that is a little bit of QA rather than just check messages that are all, you know, intertwined like that. So, yeah, we we chose Discourse as sort of the tool for the job, and that's now accessible. Yes. It is. So you can view it without a, an account or you can make an account and you can start posting and liking things. What's happening to the current Discord? Was is it completely going? Are we are we shutting it down? Oh, no. No. No. No. No. No. So what Discord is really good for is active chatting, obviously. I mean, it's a chat platform. Right? So we're gonna keep it around for chatting with the community and just talking to each other, talking with contributors, talking to, you know, folks in the community in general. But we are explicitly saying, hey. Discourse is just the better tool for the job when it comes to q and a. So if you have any questions around Directus or how to use it or you're running into something, Discourse is gonna be the place to go. Right? Community.directus.ao. And for everything else, if you just, you know, wanna chat, wanna hang out, Discord is gonna stick around. I'm gonna give you a very quick tour of the new community platform. If you head over to community.directors.io, you'll land here, which has all of the top posts. You can also filter from all time, year, quarter, month, week or on today. There's also the ability to see the latest activity on posts. You can also filter with tags or in categories. So for example, if you want to see the showcases, here you go. You have the ability to sign up with your Google, your GitHub, your Discord or your email address. Once you've signed in 'here I am is definitely not Beth' you'll be able to post topics, so you can pick whichever category is the right one for your post and any tags that relate to it as well as a title and a body of text. You can also interact with other people's posts by liking, you've got a few choice of different emojis, as well as replying. If you've got any questions about the new platform or what's happening with Discord or anything else, let us know. The general chat in Discord is still available or make a new topic on our community platform. Welcome back to another episode of Short Hops. I'm your host Brian Gillespie. And in this episode, I'm gonna give you one of the coolest tips and tricks that I've probably ever shared here on Short Hops. So stick with me on this one. It's really cool. Alright. So what are we focused on today? We're gonna build a better user experience using filters on our relational interfaces. That is a mouthful, kind of underwhelming title. But let's dive into an example project and I'll show you what we've got going on. So this is a sample Directus instance. This is my agency OS project that we put out under the Directus Labs organization. Be sure to check that out. But I wanna show you the scenario. So I'm creating a project in here and we've got an organization and we could add multiple contacts to this. So a a project belongs to an organization. Let's go with Tesla, in this case, pretty ambiguous name. And then I want to add an existing contact to this. But you could see here here's my problem. If I take a look, I get all the contacts for this. So, you know, I'm only wanting to be able to select contacts that are at Tesla. So this is kind of a a dependency here. How do we manage something like this? So I'm just gonna open the settings. We're gonna go into our data model. I'm gonna look for the projects collection. And here inside organization, we've got contacts right beside it. What I'm looking for, you know, we could see the relationship tab and how all these things come together. But when I go into the interface, and this is a mini to mini collection or relationship, so you'll have to be mindful of how you've got your relationship set up. But if I want to set that up dependent upon a field that we already have, I could do something like this. Down here in my filter, you know, I could go in and and like hard code a value of, you know, show me all the contacts where the name contains Ashley. But we want this to be dynamic, right? So I'm gonna drill into our organizations. This is a many to many relationship. And I'm gonna say organizations dot ID equals, and I could do this. So I'm gonna do mustache brackets here, hold on, and And I'm gonna do organization, which is the name of that other field inside our data model. Alright. Great. So let's take a look at this and we'll go back. We'll create a new project. Right now if I click add existing, no contact show up. How come? Right? So what I could do here, we'll say direct us test. That's the company we wanna do this project for. And now I only see the contacts who are part of that organization. Right? Likewise, if I change this to Tesla, I open it up and I see I've got Elon Musk here as the only option to select. So that's it. Take a look at the filter on any of the relational interfaces that you've set up. This is a a nice little technique to use in certain scenarios like this one. That's it for this episode. Stay tuned for the next one. We want to take a moment towards the end of the change log for thanking our amazing community contributors who give their time to improve the director's project. Since last month, there have been three contributors. A huge thank you to Osman for fixing duplicate values for is one off filter, Gevelium for ensuring that the context menu of a field in the data model settings appear at full height, and to Ziles for adding data collection and field name attributes to parent wrapper for all form elements. Thank you again. You can see their specific pull requests inside of the full release notes on GitHub. Lastly, we also want to take the time to thank our GitHub Sponsors of April who financially contribute to their Exodus development. A huge thank you to Weifan, Entel, Mike, Fergus, Omar, Marcus, Mission Control, Peter, Utomic, Steven, James, Nonlinear, Andreas, Khan, John, Jamiluddin, Burb, Adam, Jason, Birka, Yuya, Vincent, CK, Valentino, Jens, Wayne, and Yannis. The money we are given from our GitHub sponsors goes straight back to community members who build tooling and extensions for the director's ecosystem. Thank you again for being part of that. Alright. That is everything for this month's changelog. Thank you so much. If you've made it to this point, you are incredible, and I appreciate you spending the time with us. I hope to see you next month. In the meantime, do go check out the new community platform. And if you've got any questions, you've got two choices now. You can either go into the Discord or you can be top tier and go to our new community platform. So please go see you over there and let go and check out more of what's been going on. Have a great rest of your day, your week, and your month, everyone, and I will see you again shortly.","457acc4a-f193-4bd7-b6d3-127bf4d616c4",[896,897,898,899],"24edf573-77d1-4b4c-a73e-95d3b309791f","6b0b7e92-d84a-427f-987e-060483ae26fc","c080c42f-3987-4338-9b04-f7c36fdbd5f1","e3c418a9-d6dc-4a25-96ee-ab0debfc4c2e",[],{"year":399,"number":687,"id":688,"episodes":902,"show":903},[690,691,692,693,694,695,696,669],{"title":297,"slug":298},{"id":693,"slug":905,"vimeo_id":906,"description":907,"tile":908,"length":909,"resources":12,"people":12,"episode_number":527,"published":910,"title":911,"video_transcript_html":912,"video_transcript_text":913,"content":12,"seo":914,"status":19,"episode_people":915,"recommendations":920,"season":921},"april-2025","1072484710","Join us for The Changelog, taking you through the month’s Directus updates including product updates, new content and community contribution highlights. This month includes new extensions such as an Algolia Operation, a GitHub Operation and an Elasticsearch Operation, a look into the new Visual Editor beta module and an episode of the Joy of Theming with Bry Ross.","567bc407-9a1a-4f35-98c4-4e2b9e18348c",36,"2025-04-08","April 2025","\u003Cp>Speaker 0: Hello, everyone. Happy April. I'm Beth, and I'm gonna be taking you through the change log. I hope you're all having a great start to the month. If you are new here or you've been here before, welcome.\u003C\u002Fp>\u003Cp>Welcome. Thank you so much for spending your time with us. About what we've got going on. If you've got any questions, do let us know in chat if you're live or in Discord later. And with that, I'm gonna send you over to Carmen with the product updates for the month.\u003C\u002Fp>\u003Cp>Speaker 1: Thank you, Beth. And hey, everyone. Carmen here to tell you about what we've shipped in Directus in the last month or so with 11.6 o and eleven point six point one. And not only have we added numerous small improvements and bug fixes, we've got two highlights that I'd love to draw to your attention today. So first, I wanted to bring your attention to a potential breaking change.\u003C\u002Fp>\u003Cp>In the latest version of Directus, we've introduced a fix for flows. This is going to affect how action hooks pass on final payloads. Now, in the past, there was a bug that original payloads would be provided in the action hook. However, moving forward, this behavior has been rectified so that the final payload, that is after the filter hook and preset changes, is now passed down to the action hook. Next, and something we feel is quite exciting, is we've added the visual editor module beta.\u003C\u002Fp>\u003Cp>Florian is going to join the change log later on to tell you more about that. Next, let's hear about extensions from Mike.\u003C\u002Fp>\u003Cp>Speaker 2: Hi. I'm Mike, and I'm here to talk you through the extensions for this changelog. These are from the Director's Labs. Director's Labs is a GitHub org where we publish new extensions with additional functionality or experiments for you to try in your projects. This extension is the Elasticsearch operation.\u003C\u002Fp>\u003Cp>As you can see here, we have a create and delete flow. We also have an elastic instance with no indices currently. So firstly, in the create flow, we have an action which is on every create items in the collection posts, then it's gonna forward to our Elasticsearch operation. Here, you'll notice we've configured the remote Elasticsearch instance with the API key. I will be deleting those afterwards just in case.\u003C\u002Fp>\u003Cp>You can also define the collection dynamically, so the index created within Elasticsearch will be dynamically created. So each collection can have its own. You can have them prefix, suffix, or you can have a shared index for multiple collections. In this instance, we haven't we're using the create action so that it's going to add additional documents to the index with our key and the constructed data we wanna send over. This allows us to define what information we do or do not want to share from any given item to the index.\u003C\u002Fp>\u003Cp>So just to test, we are going to go and add another document. So here we're gonna go, hello world new search in Elasticsearch. What is Elasticsearch and what's the data sent? I'm gonna just put it as published because I can. And again adding a image from our library.\u003C\u002Fp>\u003Cp>Let's pick the Fusion Lead Director's logo actually, and save. So we have now saved our item. We're gonna go over to our index, our post index with one new document. If we go in, we can then see that we have our document with its title and its content, but because we didn't send over the hero image, it doesn't contain it. And we can also do the same with delete.\u003C\u002Fp>\u003Cp>So we delete the item from here. There we go. We have deleted the item, and that should delete from our index, which is brilliant. We can also update and document within the index as and when it happens. This is the Algolia operation.\u003C\u002Fp>\u003Cp>As you can see here, currently we have no indexes within Algolia. If we go over to our directors instance, currently looking at the flow for search indexes and we're going to be doing one for the creation of a item within the index. With this, we've got our event hook, which is an action on any time we create a update within the post collection. We could increase that to all other collections as with any other. And if we go inside of our creation, we can see we provide it with the application ID and the API key for Algolia.\u003C\u002Fp>\u003Cp>We can also define the index itself either dynamically as a collection. We can prefix, suffix, or have a step set index for all information being sent over to Algolia, say, as a centralized index. We also get to do an upsert as well as a patch update, delete, retrieve, and a search operation. In this case, we're going to be doing post creation rather than an upsert just for simplicity. And in this instance, we have our post collection with a title, content, and hero image, but we're only sending over the two item types.\u003C\u002Fp>\u003Cp>If we create a new post, so this is an item for Algolia. Algolia is very powerful for search within a site. We save this, and it should send over to Algolia. And we can now see that we have our posts index, and we sent over the information as is from our instance. This also means it's within any of the Algo linear interfaces we decide to put on the front end.\u003C\u002Fp>\u003Cp>Now we also have our delete flow, and within this case, we are setting the same information, but we can delete multiple items at once. So it has to be the keys rather than key as the attribute for the object ID, just how our Golia references the information. I'm going to clear changes, discard changes, go back, Delete our object. So we go over to our index, refresh it, And we should have an index, but with no records available. This is the open search operation.\u003C\u002Fp>\u003Cp>Much like with the other operations, this allows us to trigger a response like any other flow. So we have our event hook here where an action, a create item from the collections post, it is going to send over to OpenSearch with this host based upon the collection as the index and using the key for the item as the item ID for managing update creation reads on the open search side. So we will quickly just discard changes. Open search. Open search powers.\u003C\u002Fp>\u003Cp>The AWS search functionality. And we're gonna sit may as well, just for the sake of it, publish, save. And if we check open discover the dashboard for OpenSearch, even with the index post, we haven't got anything currently. Reload it, and we should have our object here with our content. OpenSearch powers the AWS search functionality of search title.\u003C\u002Fp>\u003Cp>I've then got a matching flow for deleting. So if we delete, it then takes the keys for all the objects that are being manipulated and sends that over as a delete type action. Evola. No more content. This is our new GitHub operation.\u003C\u002Fp>\u003Cp>It's designed for operating with GitHub actions and GitHub workflows. As you can see here, we have a trigger with the action for creating, updating, and I will actually open it up to see. We are multiple different types. Any form of change to items within it. We're monitoring the post collection as well as the director's collections for if we add any new collections to it.\u003C\u002Fp>\u003Cp>We obviously if we wanted, detect changes within those collections, we'll want to add these collections here. And the actual GitHub operation itself, we are providing it with the owner and repository, a fine grained access token from within the developer settings of the repo in GitHub. We're providing with a event type so that it's associated with a very specific workflow. As you can see on the right hand side, my workflow has the repository dispatch type, which is how it triggers from Webhooks, and a matching content build key so that when the hook is, triggered from Directus, it triggers the correct workflow. Yeah.\u003C\u002Fp>\u003Cp>Otherwise, it's a very simple flow for me just to show it off. And if we save this actually, we don't need to save because it's already saved. Clear changes. If we go back to our page and we and we see here, I have plenty of them running from just me testing. But if I create a new one, a new page, and we do, does this trigger GitHub actions, and we save it, we should momentarily get a new GitHub action, which will start.\u003C\u002Fp>\u003Cp>So I'm just gonna refresh to see it. There we go. Here is our latest one. It is queued up, and it's running the build state. We're laid off.\u003C\u002Fp>\u003Cp>This one doesn't work because I'm running a very outdated version of Next. Js on this project, but we have triggered the correct response. Thanks very much for checking out these extensions. If you have any other questions about these extensions that we've shipped, extensions in general, or how to build them, or if you're having problems with, anything you're trying to build yourself, please feel free to come to the Discord and you can find help in the extensions channel.\u003C\u002Fp>\u003Cp>Speaker 1: Let's talk about content. Now in the last month, we've introduced eight new tutorials for working with Directus using different web frameworks. You'll be able to find our tutorials on building dynamic forms and building real time chat applications using Directus in combination with Astro, Next. Js, Nuxt, and SvelteKit. We're always adding more tutorials, so keep an eye out.\u003C\u002Fp>\u003Cp>Speaker 0: Amazing. And I've put the link to each of the extensions mentioned in the directors TV live chat. They're also, once again, just in case you missed it, in the DirectorsLabsorg in the extensions repo on GitHub. So amazing. Thank you so much to Carmen and Mike for taking us through those updates.\u003C\u002Fp>\u003Cp>As Carmen mentioned, next up, we have Florian going more into detail about the very exciting visual editor.\u003C\u002Fp>\u003Cp>Speaker 3: Hey, everyone. I'm Florian, a front end engineer at Directus. I'm here to talk to you about the visual editor which came out in beta with the 11 dot six dot zero release. This new module allows you to visually edit the content of your website directly in place. Now you can edit without ever leaving the visual of what you're working on.\u003C\u002Fp>\u003Cp>You can work right in it. Here's a demo as I talk you through the details. Once the visual editor is enabled and a URL is provided, it will appear in the module bar. The visual editor behaves similarly to live preview in that it renders content in an iframe, but with a few important differences. Users can navigate through the website freely and edit any linked element on a page without navigating to the applicable collection.\u003C\u002Fp>\u003Cp>Clicking an edit button next to an editable element opens an editor in either a drawer, a modal, or a popover. This editing mode as well as the number and the selection of fields that appear in such an editor can be set using the vision editing library, which web developers need to implement in their website. The library sets up the connection between your website and your director's instance. The studio module is then used to render your website within director studio, make changes in place, and then render those changes as they are made. This is Visual Editor, and it's just the beginning.\u003C\u002Fp>\u003Cp>We have big plans for it. If you'd like to give feedback, you can share it in the discord or the visual editing repository.\u003C\u002Fp>\u003Cp>Speaker 0: Amazing. Thank you so much to Florian for taking the time to show us more around it. And it is super exciting, we've already seen some love but if you do have things as Florian said that you'd like to give feedback we're here to hear it, we are here to that's such a loop in my brain, we're here to hear it so yeah do let us know if you are intending to use it and if you've got any questions, of course. Now for this month's changelog director's TV episode, we are going into the archive with a joy of theming with Bry Ross who, I cannot stress enough, is definitely definitely not Brian in a wig. So enjoy.\u003C\u002Fp>\u003Cp>Speaker 4: Hi, friends. Welcome back to the show. I'm your host, Brian Ross. And today, we're gonna take an extra special trip back to the nineties with you, our friends and friends. Alright.\u003C\u002Fp>\u003Cp>So we're gonna be painting a beautiful Central Perk Coffee Company theme. Alright. So let's dive into our theme. First, gonna pull up our project settings and we're going to adjust those. Just a quick little project name and description.\u003C\u002Fp>\u003Cp>Your friend's coffee shop. What could be more beautiful for a project descriptor? Alright. So we're gonna paste the URL in there, and we can see that project settings, our name, and descriptor up there at the top. I like to pronounce it descriptor.\u003C\u002Fp>\u003Cp>Makes it sound like Thor. Very powerful. But the effect that themes can have are are very powerful, especially on your users. Alright. So let's pull up our color and we're just gonna for our project color, we're gonna grab that green from the Central Perk logo.\u003C\u002Fp>\u003Cp>Now that's a good project color. Next, let's add a public foreground and public background. So we'll just look for Central Perk and see if we can just grab a good looking image from from all of our friends here. We're just gonna spread that across the entire background and for the public foreground, we'll just use the same logo. I think that should still look really nice on the login page, don't you?\u003C\u002Fp>\u003Cp>Yeah. Great. Now all these techniques that you see me using, you can use these yourself. There's nothing holding you back. You've got the tools.\u003C\u002Fp>\u003Cp>Just take the first step. Alright. So we're gonna paint in that first note, that public note. We've got our favicon and now we're ready to start theming. Alright, so I really like this walnut light walnut brown this almond beige color.\u003C\u002Fp>\u003Cp>Let's use that for our modules background so we'll just paint that in there at the top. One thing I do see though, I want I want this to match all the way up and down. So I'm just gonna paint a couple strokes there. We're just gonna look for our background color. I see that's the project color.\u003C\u002Fp>\u003Cp>We need to fix that, so we're going to go into our custom CSS and just add a little dot module dash bar dash logo class. Make that background transparent. Now you see it. Now you don't. Boom.\u003C\u002Fp>\u003Cp>Looks great. Alright, so now for our icons in that module bar. What I want to do, I don't want to be heavy handed. I'm still going to use light strokes for this, but I am going to try and pick up a, like, a darker brown. I'm almost thinking they've got one here on the website, but, I just don't see it.\u003C\u002Fp>\u003Cp>Alright. Let's just try a black and see how that works. So we'll just go paint in a black. It's it's too much contrast. It doesn't give me those warm feelings of a of a coffee shop that I'm looking for.\u003C\u002Fp>\u003Cp>So let's pull this brown from this wood image. Now one of the other tools that I use, ColorSlurp, terrible name, great product though. Being able to just copy those hex codes for for everything all you see on your screen. Great tool. Stores all those hex codes as well, and then you can quickly bang those in your keyboard.\u003C\u002Fp>\u003Cp>Alright. So next, what we wanna do is we want to control our list, our sidebar here. So let's add a background to this. And for this I'm going to use a little tool that I like. We're going to look for the Essential Perk couch color.\u003C\u002Fp>\u003Cp>Because if you remember inside this coffee shop there's a big couch that just takes up all the area. So we're going to lift this orange couch and make it our background. Is it weird? Maybe a little bit, but for you folks who didn't live through\u003C\u002Fp>\u003Cp>Speaker 0: the\u003C\u002Fp>\u003Cp>Speaker 4: nineties, they were a weird time as well. So let's let's lean into it and and embrace the journey, see where this theme takes us. A little unorthodox mixing colors like this, but with your tools at your side and joy in your heart, you can make any theme work. That looks good. So now we've got a nice separation there.\u003C\u002Fp>\u003Cp>Let's resolve the icons here. So again, having those set to the project color of green might not be the best idea. So I'm just going to take that detail brush and paint in some foreground Just a white foreground Let's go in and let's try to make this look like a cloth texture as much as we can So I'm just gonna use a little pattern monster here. We're gonna find a pattern. Just gonna keep keep looking through our patterns until we find something that we like.\u003C\u002Fp>\u003Cp>Just just browse those patterns. Do we find a pattern that we like? This adjoined diamond pattern reminds me of my grandmother's couch and her kitchen floor at the same time. Amazing. Alright, so let's use that same orange base color and we're just going to dial back the opacity here and then also on the other overlay, let's just switch that from white to black.\u003C\u002Fp>\u003Cp>And, again, we're gonna dial in that opacity. Now I think this is a subtle effect that kinda gives a texture that reminds us of the nineties, or at the very least, your grandmother's house. What wonderful memories I'm sure you have there. Alright. So we're just gonna paint the rest of that in and, okay.\u003C\u002Fp>\u003Cp>We'll dial back that opacity a little bit just to give this a little more subtle effect. And now I'm gonna pull out that palette knife, and we're just gonna paste this in. We're gonna pull up that CSS. We're gonna copy the URL portion so we've got our data URI. We're just going to paste that into the background and just bring that all the way down.\u003C\u002Fp>\u003Cp>Alright. So now we've got this orange symbolic couch in the middle of our theme. Alright. So if we go into our background, we want to add a subtle background, that stark contrast between the white and the orange. There's opportunity there.\u003C\u002Fp>\u003Cp>So we're just gonna dial that in a little bit. Again, we're gonna use our color mix. Just paint that in. We're going to blend that almond with 80% white and we get this soft beige. Just invites you in like a warm cup of coffee in the morning.\u003C\u002Fp>\u003Cp>Now the last thing that we want to do is dial in our fonts. We're just going to pull this up. We've got a custom font here that looks like a scribble. I quite like this effect. So we're just going to go in to our font face, add that there, and then we're just going to paint this on.\u003C\u002Fp>\u003Cp>Paint in a little font family. We're going to call it friends. We're going to call it friends. Yeah, that's nice. Alright.\u003C\u002Fp>\u003Cp>Use a light touch. Very important. You want to have a handy hand heavy hand in this. Alright. So then we're gonna go into our fonts under display, and we're just gonna go friends.\u003C\u002Fp>\u003Cp>Just gonna add friends in there. Now we can see that at the top and just adjust it a little bit. There we go. Voila. And now we have our coffee shop theme.\u003C\u002Fp>\u003Cp>We have our friends and the couch in the middle, and everything is right, Just like it was in the nineteen nineties. Alright. Let's take a look at our public sign in page. And voila. We have our Central Perk theme.\u003C\u002Fp>\u003Cp>That's it for this episode of The Joy of Theming. Thank you for joining me. It's through your support that we can continue teaching techniques to help everyone theme. Each of us has a perfect theme inside of us. It's time to let yours out.\u003C\u002Fp>\u003Cp>We'll see you.\u003C\u002Fp>\u003Cp>Speaker 0: We want to take a moment towards the end of the changelog for thanking our amazing community contributors who give their time to improve the director's project. Since last month, there have been five contributors. A huge thank you to Subtire Lou for updating utility sort type signature to allow the use of both string and number. Thank you to Collier for adding an implicit for one to many and many to any relations Thank you to Jacob for improving invalid asset transformation parameter value error handling and adding an omit key option to get config from env Thank you to Son for updating the translation key input field overflow And thank you to Dan for adding the ability to map OAuth claim groups to directors roles Thank you again and you can see their specific pull requests inside of the full release notes on GitHub. Lastly, we also want to take the time to thank our GitHub Sponsors of March who financially contribute to Directus's development a huge thank you to Wayfam, Entel, Mike, Fergus, Omar, Marcus a special thank you to Mission Control for coming on board as an advocate level Peter, Utomic, Steven, Robson, Nonlinear, Andreas, John, Jamaleddin, Burb, Adam, Jason, Birka, Vincent, CK, Valentino, Jens, Wayne and Yannis.\u003C\u002Fp>\u003Cp>Thank you again the money we are given from our GitHub sponsors goes straight back to community members who build tooling and extensions for the Director's ecosystem So thank you again for being part of that. Alright. I am such a fan of Bry Ross. And if you also enjoyed that as much as I did, there are lots of other episodes with different themes such as Pampers, American Express. We've got the Simpsons, Home Depot.\u003C\u002Fp>\u003Cp>Some say there is nothing, no theme that Bry Ross cannot do. He's a true artist. And, yes, if you haven't been checking out directorstv, directors I 0 Directors.i0\u002Ftv, do check it out. There are tons of episodes, across lots of different, topics. If you've not checked it out, there is a lot for you to catch up on so do, I recommend going and seeing it too.\u003C\u002Fp>\u003Cp>Thank you so much for joining us. You have reached the end. If you are still here with us, thank you for spending the time with us. We appreciate you. Hopefully, we'll see you next month and have a great month in between.\u003C\u002Fp>\u003Cp>Bye everyone.\u003C\u002Fp>","Hello, everyone. Happy April. I'm Beth, and I'm gonna be taking you through the change log. I hope you're all having a great start to the month. If you are new here or you've been here before, welcome. Welcome. Thank you so much for spending your time with us. About what we've got going on. If you've got any questions, do let us know in chat if you're live or in Discord later. And with that, I'm gonna send you over to Carmen with the product updates for the month. Thank you, Beth. And hey, everyone. Carmen here to tell you about what we've shipped in Directus in the last month or so with 11.6 o and eleven point six point one. And not only have we added numerous small improvements and bug fixes, we've got two highlights that I'd love to draw to your attention today. So first, I wanted to bring your attention to a potential breaking change. In the latest version of Directus, we've introduced a fix for flows. This is going to affect how action hooks pass on final payloads. Now, in the past, there was a bug that original payloads would be provided in the action hook. However, moving forward, this behavior has been rectified so that the final payload, that is after the filter hook and preset changes, is now passed down to the action hook. Next, and something we feel is quite exciting, is we've added the visual editor module beta. Florian is going to join the change log later on to tell you more about that. Next, let's hear about extensions from Mike. Hi. I'm Mike, and I'm here to talk you through the extensions for this changelog. These are from the Director's Labs. Director's Labs is a GitHub org where we publish new extensions with additional functionality or experiments for you to try in your projects. This extension is the Elasticsearch operation. As you can see here, we have a create and delete flow. We also have an elastic instance with no indices currently. So firstly, in the create flow, we have an action which is on every create items in the collection posts, then it's gonna forward to our Elasticsearch operation. Here, you'll notice we've configured the remote Elasticsearch instance with the API key. I will be deleting those afterwards just in case. You can also define the collection dynamically, so the index created within Elasticsearch will be dynamically created. So each collection can have its own. You can have them prefix, suffix, or you can have a shared index for multiple collections. In this instance, we haven't we're using the create action so that it's going to add additional documents to the index with our key and the constructed data we wanna send over. This allows us to define what information we do or do not want to share from any given item to the index. So just to test, we are going to go and add another document. So here we're gonna go, hello world new search in Elasticsearch. What is Elasticsearch and what's the data sent? I'm gonna just put it as published because I can. And again adding a image from our library. Let's pick the Fusion Lead Director's logo actually, and save. So we have now saved our item. We're gonna go over to our index, our post index with one new document. If we go in, we can then see that we have our document with its title and its content, but because we didn't send over the hero image, it doesn't contain it. And we can also do the same with delete. So we delete the item from here. There we go. We have deleted the item, and that should delete from our index, which is brilliant. We can also update and document within the index as and when it happens. This is the Algolia operation. As you can see here, currently we have no indexes within Algolia. If we go over to our directors instance, currently looking at the flow for search indexes and we're going to be doing one for the creation of a item within the index. With this, we've got our event hook, which is an action on any time we create a update within the post collection. We could increase that to all other collections as with any other. And if we go inside of our creation, we can see we provide it with the application ID and the API key for Algolia. We can also define the index itself either dynamically as a collection. We can prefix, suffix, or have a step set index for all information being sent over to Algolia, say, as a centralized index. We also get to do an upsert as well as a patch update, delete, retrieve, and a search operation. In this case, we're going to be doing post creation rather than an upsert just for simplicity. And in this instance, we have our post collection with a title, content, and hero image, but we're only sending over the two item types. If we create a new post, so this is an item for Algolia. Algolia is very powerful for search within a site. We save this, and it should send over to Algolia. And we can now see that we have our posts index, and we sent over the information as is from our instance. This also means it's within any of the Algo linear interfaces we decide to put on the front end. Now we also have our delete flow, and within this case, we are setting the same information, but we can delete multiple items at once. So it has to be the keys rather than key as the attribute for the object ID, just how our Golia references the information. I'm going to clear changes, discard changes, go back, Delete our object. So we go over to our index, refresh it, And we should have an index, but with no records available. This is the open search operation. Much like with the other operations, this allows us to trigger a response like any other flow. So we have our event hook here where an action, a create item from the collections post, it is going to send over to OpenSearch with this host based upon the collection as the index and using the key for the item as the item ID for managing update creation reads on the open search side. So we will quickly just discard changes. Open search. Open search powers. The AWS search functionality. And we're gonna sit may as well, just for the sake of it, publish, save. And if we check open discover the dashboard for OpenSearch, even with the index post, we haven't got anything currently. Reload it, and we should have our object here with our content. OpenSearch powers the AWS search functionality of search title. I've then got a matching flow for deleting. So if we delete, it then takes the keys for all the objects that are being manipulated and sends that over as a delete type action. Evola. No more content. This is our new GitHub operation. It's designed for operating with GitHub actions and GitHub workflows. As you can see here, we have a trigger with the action for creating, updating, and I will actually open it up to see. We are multiple different types. Any form of change to items within it. We're monitoring the post collection as well as the director's collections for if we add any new collections to it. We obviously if we wanted, detect changes within those collections, we'll want to add these collections here. And the actual GitHub operation itself, we are providing it with the owner and repository, a fine grained access token from within the developer settings of the repo in GitHub. We're providing with a event type so that it's associated with a very specific workflow. As you can see on the right hand side, my workflow has the repository dispatch type, which is how it triggers from Webhooks, and a matching content build key so that when the hook is, triggered from Directus, it triggers the correct workflow. Yeah. Otherwise, it's a very simple flow for me just to show it off. And if we save this actually, we don't need to save because it's already saved. Clear changes. If we go back to our page and we and we see here, I have plenty of them running from just me testing. But if I create a new one, a new page, and we do, does this trigger GitHub actions, and we save it, we should momentarily get a new GitHub action, which will start. So I'm just gonna refresh to see it. There we go. Here is our latest one. It is queued up, and it's running the build state. We're laid off. This one doesn't work because I'm running a very outdated version of Next. Js on this project, but we have triggered the correct response. Thanks very much for checking out these extensions. If you have any other questions about these extensions that we've shipped, extensions in general, or how to build them, or if you're having problems with, anything you're trying to build yourself, please feel free to come to the Discord and you can find help in the extensions channel. Let's talk about content. Now in the last month, we've introduced eight new tutorials for working with Directus using different web frameworks. You'll be able to find our tutorials on building dynamic forms and building real time chat applications using Directus in combination with Astro, Next. Js, Nuxt, and SvelteKit. We're always adding more tutorials, so keep an eye out. Amazing. And I've put the link to each of the extensions mentioned in the directors TV live chat. They're also, once again, just in case you missed it, in the DirectorsLabsorg in the extensions repo on GitHub. So amazing. Thank you so much to Carmen and Mike for taking us through those updates. As Carmen mentioned, next up, we have Florian going more into detail about the very exciting visual editor. Hey, everyone. I'm Florian, a front end engineer at Directus. I'm here to talk to you about the visual editor which came out in beta with the 11 dot six dot zero release. This new module allows you to visually edit the content of your website directly in place. Now you can edit without ever leaving the visual of what you're working on. You can work right in it. Here's a demo as I talk you through the details. Once the visual editor is enabled and a URL is provided, it will appear in the module bar. The visual editor behaves similarly to live preview in that it renders content in an iframe, but with a few important differences. Users can navigate through the website freely and edit any linked element on a page without navigating to the applicable collection. Clicking an edit button next to an editable element opens an editor in either a drawer, a modal, or a popover. This editing mode as well as the number and the selection of fields that appear in such an editor can be set using the vision editing library, which web developers need to implement in their website. The library sets up the connection between your website and your director's instance. The studio module is then used to render your website within director studio, make changes in place, and then render those changes as they are made. This is Visual Editor, and it's just the beginning. We have big plans for it. If you'd like to give feedback, you can share it in the discord or the visual editing repository. Amazing. Thank you so much to Florian for taking the time to show us more around it. And it is super exciting, we've already seen some love but if you do have things as Florian said that you'd like to give feedback we're here to hear it, we are here to that's such a loop in my brain, we're here to hear it so yeah do let us know if you are intending to use it and if you've got any questions, of course. Now for this month's changelog director's TV episode, we are going into the archive with a joy of theming with Bry Ross who, I cannot stress enough, is definitely definitely not Brian in a wig. So enjoy. Hi, friends. Welcome back to the show. I'm your host, Brian Ross. And today, we're gonna take an extra special trip back to the nineties with you, our friends and friends. Alright. So we're gonna be painting a beautiful Central Perk Coffee Company theme. Alright. So let's dive into our theme. First, gonna pull up our project settings and we're going to adjust those. Just a quick little project name and description. Your friend's coffee shop. What could be more beautiful for a project descriptor? Alright. So we're gonna paste the URL in there, and we can see that project settings, our name, and descriptor up there at the top. I like to pronounce it descriptor. Makes it sound like Thor. Very powerful. But the effect that themes can have are are very powerful, especially on your users. Alright. So let's pull up our color and we're just gonna for our project color, we're gonna grab that green from the Central Perk logo. Now that's a good project color. Next, let's add a public foreground and public background. So we'll just look for Central Perk and see if we can just grab a good looking image from from all of our friends here. We're just gonna spread that across the entire background and for the public foreground, we'll just use the same logo. I think that should still look really nice on the login page, don't you? Yeah. Great. Now all these techniques that you see me using, you can use these yourself. There's nothing holding you back. You've got the tools. Just take the first step. Alright. So we're gonna paint in that first note, that public note. We've got our favicon and now we're ready to start theming. Alright, so I really like this walnut light walnut brown this almond beige color. Let's use that for our modules background so we'll just paint that in there at the top. One thing I do see though, I want I want this to match all the way up and down. So I'm just gonna paint a couple strokes there. We're just gonna look for our background color. I see that's the project color. We need to fix that, so we're going to go into our custom CSS and just add a little dot module dash bar dash logo class. Make that background transparent. Now you see it. Now you don't. Boom. Looks great. Alright, so now for our icons in that module bar. What I want to do, I don't want to be heavy handed. I'm still going to use light strokes for this, but I am going to try and pick up a, like, a darker brown. I'm almost thinking they've got one here on the website, but, I just don't see it. Alright. Let's just try a black and see how that works. So we'll just go paint in a black. It's it's too much contrast. It doesn't give me those warm feelings of a of a coffee shop that I'm looking for. So let's pull this brown from this wood image. Now one of the other tools that I use, ColorSlurp, terrible name, great product though. Being able to just copy those hex codes for for everything all you see on your screen. Great tool. Stores all those hex codes as well, and then you can quickly bang those in your keyboard. Alright. So next, what we wanna do is we want to control our list, our sidebar here. So let's add a background to this. And for this I'm going to use a little tool that I like. We're going to look for the Essential Perk couch color. Because if you remember inside this coffee shop there's a big couch that just takes up all the area. So we're going to lift this orange couch and make it our background. Is it weird? Maybe a little bit, but for you folks who didn't live through the nineties, they were a weird time as well. So let's let's lean into it and and embrace the journey, see where this theme takes us. A little unorthodox mixing colors like this, but with your tools at your side and joy in your heart, you can make any theme work. That looks good. So now we've got a nice separation there. Let's resolve the icons here. So again, having those set to the project color of green might not be the best idea. So I'm just going to take that detail brush and paint in some foreground Just a white foreground Let's go in and let's try to make this look like a cloth texture as much as we can So I'm just gonna use a little pattern monster here. We're gonna find a pattern. Just gonna keep keep looking through our patterns until we find something that we like. Just just browse those patterns. Do we find a pattern that we like? This adjoined diamond pattern reminds me of my grandmother's couch and her kitchen floor at the same time. Amazing. Alright, so let's use that same orange base color and we're just going to dial back the opacity here and then also on the other overlay, let's just switch that from white to black. And, again, we're gonna dial in that opacity. Now I think this is a subtle effect that kinda gives a texture that reminds us of the nineties, or at the very least, your grandmother's house. What wonderful memories I'm sure you have there. Alright. So we're just gonna paint the rest of that in and, okay. We'll dial back that opacity a little bit just to give this a little more subtle effect. And now I'm gonna pull out that palette knife, and we're just gonna paste this in. We're gonna pull up that CSS. We're gonna copy the URL portion so we've got our data URI. We're just going to paste that into the background and just bring that all the way down. Alright. So now we've got this orange symbolic couch in the middle of our theme. Alright. So if we go into our background, we want to add a subtle background, that stark contrast between the white and the orange. There's opportunity there. So we're just gonna dial that in a little bit. Again, we're gonna use our color mix. Just paint that in. We're going to blend that almond with 80% white and we get this soft beige. Just invites you in like a warm cup of coffee in the morning. Now the last thing that we want to do is dial in our fonts. We're just going to pull this up. We've got a custom font here that looks like a scribble. I quite like this effect. So we're just going to go in to our font face, add that there, and then we're just going to paint this on. Paint in a little font family. We're going to call it friends. We're going to call it friends. Yeah, that's nice. Alright. Use a light touch. Very important. You want to have a handy hand heavy hand in this. Alright. So then we're gonna go into our fonts under display, and we're just gonna go friends. Just gonna add friends in there. Now we can see that at the top and just adjust it a little bit. There we go. Voila. And now we have our coffee shop theme. We have our friends and the couch in the middle, and everything is right, Just like it was in the nineteen nineties. Alright. Let's take a look at our public sign in page. And voila. We have our Central Perk theme. That's it for this episode of The Joy of Theming. Thank you for joining me. It's through your support that we can continue teaching techniques to help everyone theme. Each of us has a perfect theme inside of us. It's time to let yours out. We'll see you. We want to take a moment towards the end of the changelog for thanking our amazing community contributors who give their time to improve the director's project. Since last month, there have been five contributors. A huge thank you to Subtire Lou for updating utility sort type signature to allow the use of both string and number. Thank you to Collier for adding an implicit for one to many and many to any relations Thank you to Jacob for improving invalid asset transformation parameter value error handling and adding an omit key option to get config from env Thank you to Son for updating the translation key input field overflow And thank you to Dan for adding the ability to map OAuth claim groups to directors roles Thank you again and you can see their specific pull requests inside of the full release notes on GitHub. Lastly, we also want to take the time to thank our GitHub Sponsors of March who financially contribute to Directus's development a huge thank you to Wayfam, Entel, Mike, Fergus, Omar, Marcus a special thank you to Mission Control for coming on board as an advocate level Peter, Utomic, Steven, Robson, Nonlinear, Andreas, John, Jamaleddin, Burb, Adam, Jason, Birka, Vincent, CK, Valentino, Jens, Wayne and Yannis. Thank you again the money we are given from our GitHub sponsors goes straight back to community members who build tooling and extensions for the Director's ecosystem So thank you again for being part of that. Alright. I am such a fan of Bry Ross. And if you also enjoyed that as much as I did, there are lots of other episodes with different themes such as Pampers, American Express. We've got the Simpsons, Home Depot. Some say there is nothing, no theme that Bry Ross cannot do. He's a true artist. And, yes, if you haven't been checking out directorstv, directors I 0 Directors.i0\u002Ftv, do check it out. There are tons of episodes, across lots of different, topics. If you've not checked it out, there is a lot for you to catch up on so do, I recommend going and seeing it too. Thank you so much for joining us. You have reached the end. If you are still here with us, thank you for spending the time with us. We appreciate you. Hopefully, we'll see you next month and have a great month in between. Bye everyone.","d4cc3ee3-081e-4865-aee1-f130124acc8a",[916,917,918,919],"eb645c44-4d6d-41b7-8813-223e01e50cb4","b5fa8a76-5798-48b9-87d5-26d146216ce5","6594f8ea-630f-40ec-8678-00aab5c43fc7","965569fc-a343-4438-bd12-a6f3bce609a5",[],{"year":399,"number":687,"id":688,"episodes":922,"show":923},[690,691,692,693,694,695,696,669],{"title":297,"slug":298},{"id":925,"slug":926,"vimeo_id":927,"description":928,"tile":929,"length":740,"resources":12,"people":12,"episode_number":264,"published":930,"title":931,"video_transcript_html":932,"video_transcript_text":933,"content":12,"seo":934,"status":19,"episode_people":935,"recommendations":937,"season":938},"592bb1b1-59a1-4c28-9690-1752ccf2d7e2","geocoding-using-the-mapboxapi","1068453287","Directus Automate allows you to integrate with numerous services and automatically perform actions on specific triggers. In this episode, Carmen will walk you through creating an application that integrates with the Mapbox API to automatically retrieve addresses and locations.","6fc1451a-dd7d-4b02-8f49-5a28cc1ea340","2025-03-27","Geocoding using the Mapbox API and Directus Automate","\u003Cp>Speaker 0: Alright. Here we are in a brand new Directus project. Now, in this episode of uncharted territory, I'm gonna show you how to build an application that autocompletes addresses and automatically retrieves GeoJSON data using the Mapbox API. Now, in order to go through this project, you will need to sign up for a free Mapbox account and get an API key. So, let's create our collection for this project.\u003C\u002Fp>\u003Cp>Now, this application is going to contain a collection of cafes complete with their address, location, and rating. So, finish the setup. And let's build our first field. The first field is going to be an autocomplete input for the address. Now to work with autocomplete inputs in Directus, we need to have a URL to query for the list of results.\u003C\u002Fp>\u003Cp>This is going to take the shape of our URL. This is going to come directly from the Mapbox API. So we've got our URL, and our first query parameter is the value. You can notice here in the curly brackets, this is going to come from what we type into the autocomplete input field. The next query parameter is our access token.\u003C\u002Fp>\u003Cp>The results path here is the array or list of results. From the Mapbox API, I know this to be features. Next, we're going to set the text path and the value path. Now the text path is going to be what is displayed for our users. This is properties dot full underscore address.\u003C\u002Fp>\u003Cp>Again, this comes directly from the Mapbox API. And for the value path, that is what we're going to store in our collection, it's going to be the same thing. Properties. Full address. Wonderful.\u003C\u002Fp>\u003Cp>Well, guess what? We're ready to save that and try it out. Let's go over to our content and create a new item. We're gonna look up one of my favorite cafes, Cafe Mozart in Vienna. I know that sounds typical.\u003C\u002Fp>\u003Cp>And I know the address to be Albertina Platz 2 Or 2. So here we go. Perfect. And we can see that it autocompletes, gives us suggestions, and now I can go ahead and save that. But before I do that, I would actually like this to save on a map or retrieve its geolocation data.\u003C\u002Fp>\u003Cp>So, I'm not actually gonna save this cafe because next, I want to save the location. Let's go over to our collection again and add the remaining two fields. First is the rating, which is a slider. I'm gonna call it rating, minimum value zero, maximum value five, and the step interval is going to be one. Fantastic.\u003C\u002Fp>\u003Cp>Next, we're going to add a map field. We'll call it location. In another episode of uncharted territory, we covered the different types of geolocation data. Check it out. For this, we're going to save the raw JSON data.\u003C\u002Fp>\u003Cp>Perfect. Let's go ahead and save that. Now, my aim is to have Directus automatically set the location when I add the address. And, we're going to do this using flows. Flows allow me to automate different aspects of my Directus project.\u003C\u002Fp>\u003Cp>We don't have any flows in our Directus project yet, so we're gonna add our first one. And this one's going to be called geolocate cafe. We're going to set up a trigger for it, which is an event hook. This is going to be a filter type, which means that it will block, as in the action that we take, in this case, creating a new cafe or editing a cafe, is going to be blocked until this flow completes. We'll choose filter type.\u003C\u002Fp>\u003Cp>The scope will be for creating an item and updating an item because we want to automatically get that geolocation when we create or edit a cafe. Speaking of which, our collection is going to be cafes. The response body is going to be the data of the last operation. Let's go ahead and save that setup. And now we're ready to work on our flow.\u003C\u002Fp>\u003Cp>So let's go ahead and add our first operation. In order for this flow to run, we want to make sure that the address has been set. So it's going to be a condition. That is, we're going to make sure that the address was submitted. Let's write in our condition.\u003C\u002Fp>\u003Cp>We're going to make sure that our flows trigger data, which contains a payload of data. We want to make sure that the payload of data contains an address. Not just contains an address, but we wanna make sure that it has been submitted, that is, that the payload of data we're submitting contains an address. So, we wanna make sure that the address has been submitted here. We'll go ahead and save that.\u003C\u002Fp>\u003Cp>And now, we have a condition. As you can see, it splits off in two directions. Great. Let's add the operations that take place if the address has been set. This is called the happy path.\u003C\u002Fp>\u003Cp>Now for this, we're going to make a web request or webhook operation. And it's going to go to the Mapbox API and request the geolocation data of the cafe given an address. So in the URL field, I'm going to paste in my request. So let's have a look here. Using the Mapbox API, we're going to have two query parameters.\u003C\u002Fp>\u003Cp>First is the address itself. So you can see here, we're retrieving using the curly bracket syntax the trigger's payload's address. So we're using the address that was submitted when we created our cafe item. Next, we're adding as a second query parameter, our access token. And that's it.\u003C\u002Fp>\u003Cp>Next, we're going to add in an operation for when this web request was successful. And we're going to use the run scripts operation. So we're going to replace the contents of the script with my own one. And what we're doing here is returning an object that is going to be saved to the database. Now you'll see that it contains the payload itself, that is the data of the cafe when we created it, as well as the location, which is now going to be saved into this item, into this cafe, with the data from that web request.\u003C\u002Fp>\u003Cp>So, data dot dollar sign last, which is the data that came in from that last operation, which was the web request. And then, using our knowledge of the Mapbox API, we go into data, the first feature, that is the first result, dot geometry. And this is GeoJSON data, which Directus understands and recognizes. So we'll go ahead and save that. Great.\u003C\u002Fp>\u003Cp>So we're almost done. The last thing we need to do is implement the unhappy path of what happens if no address has been set. Because if we create a cafe without an address, this flow will still run. And because it errors out, the cafe will not be created with any data. So we will add an operation here for what happens if the address has not been set.\u003C\u002Fp>\u003Cp>We don't want our data to be lost. So we're going to add a transform payload operation and leave the data as is. That is, we will return only what has been given to us. That is, the trigger's payload. That is, which data the user has entered when creating a cafe.\u003C\u002Fp>\u003Cp>So we'll save that operation, and we're done with our flow. So I'm gonna save that flow, and we if I go back, we can see our flow is active and we're ready to take it out for a spin. Great. So we have our cafe here. We'll give it a default rating of five because I love the cafe Mozart.\u003C\u002Fp>\u003Cp>Now we're going to set the address, Albertina Platz 5 Or 2 for the Cafe Mozart. Now, let's go ahead and save our cafe. Now, we can see a location was indeed saved. Now, wouldn't it be exciting to see what that is? So, let's go ahead and click on our item, our cafe, and we can see look at that.\u003C\u002Fp>\u003Cp>Look at that. We scroll in. Venus, you know, German for Vienna. Scroll, scroll, scroll. Look at that.\u003C\u002Fp>\u003Cp>It's the cafe Mozart. How exciting. In another episode of uncharted territory, you can also see how to use different layouts to display your data. Check it out. Now we can always see here our cafe Mozart.\u003C\u002Fp>\u003Cp>But this isn't very exciting, is it? Let's use the display item for the address. Much better. So that's how you can use geolocation data using the Mapbox API to automate your usage of geolocation data using Directus. See you next time.\u003C\u002Fp>","Alright. Here we are in a brand new Directus project. Now, in this episode of uncharted territory, I'm gonna show you how to build an application that autocompletes addresses and automatically retrieves GeoJSON data using the Mapbox API. Now, in order to go through this project, you will need to sign up for a free Mapbox account and get an API key. So, let's create our collection for this project. Now, this application is going to contain a collection of cafes complete with their address, location, and rating. So, finish the setup. And let's build our first field. The first field is going to be an autocomplete input for the address. Now to work with autocomplete inputs in Directus, we need to have a URL to query for the list of results. This is going to take the shape of our URL. This is going to come directly from the Mapbox API. So we've got our URL, and our first query parameter is the value. You can notice here in the curly brackets, this is going to come from what we type into the autocomplete input field. The next query parameter is our access token. The results path here is the array or list of results. From the Mapbox API, I know this to be features. Next, we're going to set the text path and the value path. Now the text path is going to be what is displayed for our users. This is properties dot full underscore address. Again, this comes directly from the Mapbox API. And for the value path, that is what we're going to store in our collection, it's going to be the same thing. Properties. Full address. Wonderful. Well, guess what? We're ready to save that and try it out. Let's go over to our content and create a new item. We're gonna look up one of my favorite cafes, Cafe Mozart in Vienna. I know that sounds typical. And I know the address to be Albertina Platz 2 Or 2. So here we go. Perfect. And we can see that it autocompletes, gives us suggestions, and now I can go ahead and save that. But before I do that, I would actually like this to save on a map or retrieve its geolocation data. So, I'm not actually gonna save this cafe because next, I want to save the location. Let's go over to our collection again and add the remaining two fields. First is the rating, which is a slider. I'm gonna call it rating, minimum value zero, maximum value five, and the step interval is going to be one. Fantastic. Next, we're going to add a map field. We'll call it location. In another episode of uncharted territory, we covered the different types of geolocation data. Check it out. For this, we're going to save the raw JSON data. Perfect. Let's go ahead and save that. Now, my aim is to have Directus automatically set the location when I add the address. And, we're going to do this using flows. Flows allow me to automate different aspects of my Directus project. We don't have any flows in our Directus project yet, so we're gonna add our first one. And this one's going to be called geolocate cafe. We're going to set up a trigger for it, which is an event hook. This is going to be a filter type, which means that it will block, as in the action that we take, in this case, creating a new cafe or editing a cafe, is going to be blocked until this flow completes. We'll choose filter type. The scope will be for creating an item and updating an item because we want to automatically get that geolocation when we create or edit a cafe. Speaking of which, our collection is going to be cafes. The response body is going to be the data of the last operation. Let's go ahead and save that setup. And now we're ready to work on our flow. So let's go ahead and add our first operation. In order for this flow to run, we want to make sure that the address has been set. So it's going to be a condition. That is, we're going to make sure that the address was submitted. Let's write in our condition. We're going to make sure that our flows trigger data, which contains a payload of data. We want to make sure that the payload of data contains an address. Not just contains an address, but we wanna make sure that it has been submitted, that is, that the payload of data we're submitting contains an address. So, we wanna make sure that the address has been submitted here. We'll go ahead and save that. And now, we have a condition. As you can see, it splits off in two directions. Great. Let's add the operations that take place if the address has been set. This is called the happy path. Now for this, we're going to make a web request or webhook operation. And it's going to go to the Mapbox API and request the geolocation data of the cafe given an address. So in the URL field, I'm going to paste in my request. So let's have a look here. Using the Mapbox API, we're going to have two query parameters. First is the address itself. So you can see here, we're retrieving using the curly bracket syntax the trigger's payload's address. So we're using the address that was submitted when we created our cafe item. Next, we're adding as a second query parameter, our access token. And that's it. Next, we're going to add in an operation for when this web request was successful. And we're going to use the run scripts operation. So we're going to replace the contents of the script with my own one. And what we're doing here is returning an object that is going to be saved to the database. Now you'll see that it contains the payload itself, that is the data of the cafe when we created it, as well as the location, which is now going to be saved into this item, into this cafe, with the data from that web request. So, data dot dollar sign last, which is the data that came in from that last operation, which was the web request. And then, using our knowledge of the Mapbox API, we go into data, the first feature, that is the first result, dot geometry. And this is GeoJSON data, which Directus understands and recognizes. So we'll go ahead and save that. Great. So we're almost done. The last thing we need to do is implement the unhappy path of what happens if no address has been set. Because if we create a cafe without an address, this flow will still run. And because it errors out, the cafe will not be created with any data. So we will add an operation here for what happens if the address has not been set. We don't want our data to be lost. So we're going to add a transform payload operation and leave the data as is. That is, we will return only what has been given to us. That is, the trigger's payload. That is, which data the user has entered when creating a cafe. So we'll save that operation, and we're done with our flow. So I'm gonna save that flow, and we if I go back, we can see our flow is active and we're ready to take it out for a spin. Great. So we have our cafe here. We'll give it a default rating of five because I love the cafe Mozart. Now we're going to set the address, Albertina Platz 5 Or 2 for the Cafe Mozart. Now, let's go ahead and save our cafe. Now, we can see a location was indeed saved. Now, wouldn't it be exciting to see what that is? So, let's go ahead and click on our item, our cafe, and we can see look at that. Look at that. We scroll in. Venus, you know, German for Vienna. Scroll, scroll, scroll. Look at that. It's the cafe Mozart. How exciting. In another episode of uncharted territory, you can also see how to use different layouts to display your data. Check it out. Now we can always see here our cafe Mozart. But this isn't very exciting, is it? Let's use the display item for the address. Much better. So that's how you can use geolocation data using the Mapbox API to automate your usage of geolocation data using Directus. See you next time.","7e0d72ce-9fe3-415b-b986-5349b3e1dfeb",[936],"ea90b9ce-5baa-4914-9fc0-7568a751995a",[],{"year":399,"number":939,"id":940,"episodes":941,"show":944},15,"bdfe9ee7-796d-474d-87fe-b2e38c6576e9",[942,943,925],"85aaab3f-4674-44b4-a307-77b095228eea","481c2654-a4d8-415d-bb1f-ea25ea3d27d0",{"title":945,"slug":946},"Uncharted Territory","uncharted-territory",{"id":943,"slug":948,"vimeo_id":949,"description":950,"tile":951,"length":195,"resources":12,"people":12,"episode_number":177,"published":930,"title":952,"video_transcript_html":953,"video_transcript_text":954,"content":12,"seo":955,"status":19,"episode_people":956,"recommendations":958,"season":959},"using-the-map-layout-in-directus","1068453361","Directus allows you to display geospatial data in the content module. In this episode, Carmen will walk you through how to use the Map Layout in Directus.","780f385f-e060-44e8-be9f-2460ac20f570","Using the Map Layout in Directus","\u003Cp>Speaker 0: Hey there. Carmen here from Directus. Let's look at how to use the map layout with geospatial data. Here we are in a brand new Directus project.\u003C\u002Fp>\u003Cp>Speaker 1: Let's go ahead and create a brand new collection. This is going to be for my road trips I'm gonna go ahead and click on next\u003C\u002Fp>\u003Cp>Speaker 0: and leave all of these fields blank now the collection is created but I don't have any fields yet This collection is gonna have two fields. One for the date when my journey started and a map field for all of the places I visited. Go ahead and click on create field and then on date time. Now,\u003C\u002Fp>\u003Cp>Speaker 1: the key for the date time is gonna be just date and the type is gonna be a date.\u003C\u002Fp>\u003Cp>Speaker 0: Save that. And next, I'm gonna create another field of type map called places.\u003C\u002Fp>\u003Cp>Speaker 1: Now in another episode of uncharted territory, we explore the different types of map fields. For this one, I'm gonna go ahead and pick line string. That's because I want to save a series of places that I visited during a road trip. I'm gonna go ahead and save that.\u003C\u002Fp>\u003Cp>Speaker 0: Now my collection is ready to go. Let's add some data. I'm gonna go ahead and click on create item, set my date to be the beginning of this month,\u003C\u002Fp>\u003Cp>Speaker 1: and my first trip is gonna be along Chile.\u003C\u002Fp>\u003Cp>Speaker 0: We start at Antofagasta, go down to Atacama, move along down to Valparaiso, that's where I'm from, and next to Santiago, the capital,\u003C\u002Fp>\u003Cp>Speaker 1: do a stopover at Temuco. And let's go ahead and make our next stop be, How about the aisle of Chile?\u003C\u002Fp>\u003Cp>Speaker 0: We'll end our road trip over at Puerto Mont. Great. That's done. I'm going to go ahead and save that. Now, by default, when we look at our map data, it looks like a collection of coordinates.\u003C\u002Fp>\u003Cp>Not ideal. Let's add one more road trip before we do something about it. Gonna click on create item. My next trip is gonna be the previous month, start of February,\u003C\u002Fp>\u003Cp>Speaker 1: and it's gonna take place in Europe. Let's go ahead and start in Vienna, capital of Austria. That's where I live.\u003C\u002Fp>\u003Cp>Speaker 0: Let's go over to Graz then to Klagenfurt.\u003C\u002Fp>\u003Cp>Speaker 1: Then how about Ljubljana in Slovenia, which I'm told is beautiful, down to Zagreb in Croatia, and we'll end\u003C\u002Fp>\u003Cp>Speaker 0: in Belgrade in Serbia.\u003C\u002Fp>\u003Cp>Speaker 1: Fantastic. Let's go ahead and save that. And once again, collection of coordinates.\u003C\u002Fp>\u003Cp>Speaker 0: Now, let's talk about layouts. In Directus, we can use layouts to display our data in different ways. Calendar cards, which look like this. We've got Kanban, which is currently empty. Calendar for our we could use our dates for that.\u003C\u002Fp>\u003Cp>But\u003C\u002Fp>\u003Cp>Speaker 1: we're gonna use the map layout. And here, we can see our road trips show up.\u003C\u002Fp>\u003Cp>Speaker 0: We can see our trip from Chile from March 2. And up here, we can see our road trip from February 2.\u003C\u002Fp>\u003Cp>Speaker 1: Over here, we can use see our base map, which is set by default to open street map, our geospatial field, which if we have more than one, we can set it accordingly, and our display template. And what's cool about this is we can write it however we like. How about road trip from space and then we can pick it and then we can pick a date and check it out. Now it says road trip from 02\u002F02\u002F2025.\u003C\u002Fp>\u003Cp>Speaker 0: Furthermore, if\u003C\u002Fp>\u003Cp>Speaker 1: we go back to our settings and we change our dates display to be relative,\u003C\u002Fp>\u003Cp>Speaker 0: Check this out.\u003C\u002Fp>\u003Cp>Speaker 1: Let's set our layout to be the map again, road trip from,\u003C\u002Fp>\u003Cp>Speaker 0: and now we can see we have\u003C\u002Fp>\u003Cp>Speaker 1: a road trip from about one month ago. So that's the use of the map layout in Directus.\u003C\u002Fp>","Hey there. Carmen here from Directus. Let's look at how to use the map layout with geospatial data. Here we are in a brand new Directus project. Let's go ahead and create a brand new collection. This is going to be for my road trips I'm gonna go ahead and click on next and leave all of these fields blank now the collection is created but I don't have any fields yet This collection is gonna have two fields. One for the date when my journey started and a map field for all of the places I visited. Go ahead and click on create field and then on date time. Now, the key for the date time is gonna be just date and the type is gonna be a date. Save that. And next, I'm gonna create another field of type map called places. Now in another episode of uncharted territory, we explore the different types of map fields. For this one, I'm gonna go ahead and pick line string. That's because I want to save a series of places that I visited during a road trip. I'm gonna go ahead and save that. Now my collection is ready to go. Let's add some data. I'm gonna go ahead and click on create item, set my date to be the beginning of this month, and my first trip is gonna be along Chile. We start at Antofagasta, go down to Atacama, move along down to Valparaiso, that's where I'm from, and next to Santiago, the capital, do a stopover at Temuco. And let's go ahead and make our next stop be, How about the aisle of Chile? We'll end our road trip over at Puerto Mont. Great. That's done. I'm going to go ahead and save that. Now, by default, when we look at our map data, it looks like a collection of coordinates. Not ideal. Let's add one more road trip before we do something about it. Gonna click on create item. My next trip is gonna be the previous month, start of February, and it's gonna take place in Europe. Let's go ahead and start in Vienna, capital of Austria. That's where I live. Let's go over to Graz then to Klagenfurt. Then how about Ljubljana in Slovenia, which I'm told is beautiful, down to Zagreb in Croatia, and we'll end in Belgrade in Serbia. Fantastic. Let's go ahead and save that. And once again, collection of coordinates. Now, let's talk about layouts. In Directus, we can use layouts to display our data in different ways. Calendar cards, which look like this. We've got Kanban, which is currently empty. Calendar for our we could use our dates for that. But we're gonna use the map layout. And here, we can see our road trips show up. We can see our trip from Chile from March 2. And up here, we can see our road trip from February 2. Over here, we can use see our base map, which is set by default to open street map, our geospatial field, which if we have more than one, we can set it accordingly, and our display template. And what's cool about this is we can write it however we like. How about road trip from space and then we can pick it and then we can pick a date and check it out. Now it says road trip from 02\u002F02\u002F2025. Furthermore, if we go back to our settings and we change our dates display to be relative, Check this out. Let's set our layout to be the map again, road trip from, and now we can see we have a road trip from about one month ago. So that's the use of the map layout in Directus.","1613c71d-e8d2-460f-9085-4dffa0b30885",[957],"5624226b-1e21-4d4b-b134-5d6739a6c5a7",[],{"year":399,"number":939,"id":940,"episodes":960,"show":961},[942,943,925],{"title":945,"slug":946},{"id":963,"slug":964,"vimeo_id":965,"description":966,"tile":967,"length":12,"resources":12,"people":12,"episode_number":159,"published":968,"title":969,"video_transcript_html":970,"video_transcript_text":971,"content":12,"seo":972,"status":19,"episode_people":973,"recommendations":976,"season":977},"45133ec4-b8c7-4989-83a3-b0b46c20835c","advanced-content-workflows-inngest","1067367779","Join us and our friends at Inngest to learn all about building advanced content workflows and orchestrating automated localization for content in different languages. ","f0228f3a-bc39-4151-b6fd-55594af0f637","2025-03-20","Build Advanced Content Workflows: A Deep Dive with Directus + Inngest","\u003Cp>Speaker 0: With Directus and Inngest. I'm your host on the Directus side, Bryant Gillespie, developer advocate, growth engineer, professional hack and slash developer over here at Directus. Super excited for today. Got a special guest that I'll introduce in just a moment. We should have a good time.\u003C\u002Fp>\n\u003Cp>We kinda reworked the slide deck a bit last minute, as I tend to do. But before we dive into introductions, let's just take a look at what's on the agenda. Alright. So we'll go through some super awkward introductions in just a moment. We'll deep dive into what is ingest, what powers ingest behind the scenes, why you would wanna use ingest, then we'll get into why Directus and ingest are a great pair together.\u003C\u002Fp>\n\u003Cp>And then we're gonna run through this advanced content workflow after we've, walked through this integration of invincible automatic localization, for your content, which is always a beast. So with that, I want to kick over to mister Dan. Dan, how are you, sir?\u003C\u002Fp>\n\u003Cp>Speaker 1: Great. Great, Brian. Thanks for thanks for having me. Stoked about stoked about doing this. Thanks everyone for joining as well.\u003C\u002Fp>\n\u003Cp>My name is Dan, and I'm the CTO and cofounder at a company called Jest. And we'll, we'll get into a little bit more about what that is and, what we're doing today and, why you've joined. But, yeah, just thanks for joining, and I'm stoked about the stuff that Brian, Brian kicked off today.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Yeah. And, Abel, like, for for some context and backstory, like, I first found ingest, we were working on a project we were calling Event OS. And, Direct has made it a breeze to set this up, get some APIs to work with inside a Nuxt application, but it's not dissimilar from the platform you use to register for this event. One of the the main challenges was, like, how do we send notifications and, like, how do we set up reminders an hour before an event and still have that be robust, like, cancellable in case we move the event, you know, and all sorts of things that we're gonna cover today.\u003C\u002Fp>\n\u003Cp>So that was my introduction to ingest, and frankly, I was kinda blown away. I I don't think if Matt is here in the chat, I was like, man, we gotta get these guys on a partner webinar, and voila, here we are. So with that, we'll just drag the little slide deck over. And what's up next is what is ingest? So I'll kick it over to you, Dan, and, you know, let you dive into ingest a little bit deeper for those who are unfamiliar.\u003C\u002Fp>\n\u003Cp>Speaker 1: Great. Thanks, Brian. Appreciate it. So I'm gonna give you a little high level overview today, and we're a technical audience, so have a little bit more of a a technical variant on slides today. So, if anyone needs things a little bit bigger, tell me to bump it up in the chat.\u003C\u002Fp>\n\u003Cp>I'll try to respond if there's anything. So yeah. So, basically, what is Ingest? Okay. What you can basically think about it as the at the high level is that Ingest is a workflow orchestration platform that enables you to run reliable asynchronous jobs anywhere.\u003C\u002Fp>\n\u003Cp>We're gonna get come back to that in just a just a minute or two. So why would you use ingest for this this task, this need? What ingest allows you to do is to define these step functions. So you're creating workflows directly in your existing code base, use our SDKs, and any logic that you're used to. There's no DAGs or really obscure complex DSLs to learn to build these, powerful functions using regular code.\u003C\u002Fp>\n\u003Cp>So they're also reliable by default. So everything that you run with ingest automatically is retries. I think we've probably a lot of people here have built systems where, things go wrong. An API goes down for a few minutes, there's a blip, there's a networking thing, there's a race condition that happens in your database, and all simple retry a few seconds later would solve your problem. So ingest does that by default, and it also includes, something that is called durable execution.\u003C\u002Fp>\n\u003Cp>You may have heard this term, and if you haven't, you can think of this as your code is designed to be fault tolerant and survive catastrophic failovers, basically. So I won't get too deep into that topic today, but there's plenty of resources that we can send you after if you are curious. The last thing is that we've built in queuing and flow control right into ingest so that you can go to production confidently. What that means is that there are concurrency management built into the system that you don't need to have run a a worker or a queue or anything like that. You just define it in your code very simply.\u003C\u002Fp>\n\u003Cp>You also have controls for multi tenant support, which is another advanced topic we won't get into yet today. But you can just kind of anything that you might have built into some sort of, asynchronous system that you might need is there at your fingertips with easy configuration like throttling, rate limiting, delaying the start of jobs, debouncing, batch processing, dynamically prioritize prioritizing certain jobs, as well as running work in parallel or fanning out to multiple jobs. So that's at the high level, but realistically, okay, what can I build with ingest? So let's just you know, at the highest level in general thing is ingest is a general purpose tool, and you can run any background task. Something simple, just one function, one operation to something that is that is very complex and has to orchestrate between different process.\u003C\u002Fp>\n\u003Cp>So you also have the control in your hands. You can control the flow of the logic with if statements, not DAGs, and you can kinda build whatever you want. So I'll give you a little taste of, you know, with this audience, which is technical, and I'm sure a lot of y'all are interested or already users of Directus. You know, what might work in or make sense in your in your situation or what you might be building? So one of the first things that's very hot, sure a lot of you are dabbling or building already, pushing these things to production is AI workflows.\u003C\u002Fp>\n\u003Cp>So you can handle the complexity of things like flaky LOM API calls that might fail, things like chaining, building agent loops, calling tools if you're getting that advanced, or do things like human in the loop flows. You know, you're you're proving the work that that, LLM did for you. So this might be a research AI workflow. Go out and get all this data and pull it back and store it somewhere, or you might be creating content. That kinda leads me into content processing, which is where you might be pre or post processing some content.\u003C\u002Fp>\n\u003Cp>It might be a blog post or could be static assets that are in your system like images or videos. You might be doing things like translating, resizing, transcribing more to those assets. You might need to kind of create a complex pipeline. So one example here is that you you might be taking data, from your, your Directus system and you might be piping that into maybe a React SVG library and generating visual assets on the fly for your content and then uploading it to where you're storing, your assets. You know, another thing there, like, kind of beyond content is is user journey automation.\u003C\u002Fp>\n\u003Cp>Brian said at the top something about scheduling emails with events and and and and that and and whatnot. Within just, sorry, Brian.\u003C\u002Fp>\n\u003Cp>Speaker 0: No. No. That was a huge one for me, man. That was, that was, like, my introduction to it, and I think that was, like, one of the one of the first case studies or something I ran across in the docs of, like, hey. Just something as simple as let me sign a user up and then wait a day and send them something Yeah.\u003C\u002Fp>\n\u003Cp>Incredibly convoluted in other systems.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. And and you know what? You might have this thing where you could some systems might allow you to schedule an email in the future, or you might have a cron based system. But what if you could just, you know, prepare something, do some operation maybe like you're sending somebody you know, you're sending someone an invite, and then that invite email goes out. And then twenty four hours before the event, you might wanna schedule and say, let's render this email.\u003C\u002Fp>\n\u003Cp>Might be using, say, React email, which is awesome, and, pulling in data from different sources and sending that person a personalized email twenty four hours ahead. Or you might wanna customize, hey. This user is in this time stamp. So you might wanna, like, have more control sorry, time, time stamp, time zone. So you might wanna have, like, a time aware thing.\u003C\u002Fp>\n\u003Cp>So you could use something like another complex tool or, whatever is within an event platform that you're doing or something like Intercom or HubSpot, but that can be either not very controllable. You can't bring in custom logic and code. It's very static. They're crazy expensive, for those purposes. So you kinda do whatever you want here.\u003C\u002Fp>\n\u003Cp>Build flows that are scheduling delays, Slack notification, even like things like provisioning resources or provisioning access. So, that's a fun kinda use case. You can kinda build anything triggered with these vents. I'll go through a couple of these other ones because these are kinda secondary. But, you know, if you're integrating your system with a third party webhook, maybe something like integrating Stripe payments somehow into in into your system, or maybe you're using a system like Mux, and you're uploading your video assets to Mux, but you need to do something in your Directus CMS after those videos are uploaded, you can use a webhook.\u003C\u002Fp>\n\u003Cp>And you can those events can go to ingest and trigger your functions to run your Directus instance. So you can kind of build more advanced integrations with more control that you might need. And the last thing that happens in a lot of systems is there's always data from one source to another to another place. So you might have just data import jobs. Kind of think about it like you're building ETL jobs or just pipelines or synchronization, but you don't, you know, you you don't need anything like a complex data science tool or, something that a data engineer might use like an airflow or something like that.\u003C\u002Fp>\n\u003Cp>So you can build kind of anything in this, but I hope these are kind of ideas that might work with, what you're already doing with Directus. So I'll go from there, and I'll, I'll get to that question in a little bit. We have some time at the end as well. And, I'll go and just show, like, a little bit of functions. You this again is a technical audience, so let's just show some code about how you build an Ingest function and how it works.\u003C\u002Fp>\n\u003Cp>So I'll run through this. Basically, what you're doing here is you're creating the Ingest function. It's pretty simple. You define it in your existing code base along with your directus functions, your directus logic and whatnot, your whole system. You can do things like define queuing or flow control right at the top of the function very easily.\u003C\u002Fp>\n\u003Cp>So if, for example, you just wanted to run this job, at most 30 times in sixty seconds and you wanted to max out at three retries or 15 retries, you can control these things very easily and the ingest system will just do it automatically for you. You don't need to battle queues and infrastructure for that. You also declaratively define the event that will trigger your function. So in this case, when a blog post is drafted, run this code. And then bay very simply, you define a function right here, which is a handler that has all your code.\u003C\u002Fp>\n\u003Cp>If you're thinking about what I talked about before which is workflows, steps, and whatnot, when you're defining ingest functions, all you need to do is learn a couple building blocks which are steps. The main one is step dot run. Basically, you can encapsulate some sort of business logic, some sort of side effect, and you can perform an operation in there. Each time you run code within one of these, it's automatically retried if it fails as per your retry policy. And if this does succeed, if there's any failures later in the function, it won't be retried.\u003C\u002Fp>\n\u003Cp>We cache those results and save them. So it makes your function a little durable if you're familiar with that term or fault tolerant, you know, that's the the a la the, durable execution side of things. So, basically what you're doing is you're defining steps and multiple steps to build out your workflow. You can use normal if logic. You can dynamically define steps on the fly.\u003C\u002Fp>\n\u003Cp>You do not need to predefine them, which is really great. And that's the building blocks. But what's interesting is that there's more power to these these these basic primitives of steps that you have. So here's one example that might tie back to what we were just talking about, which are that you can pause workflows and wait for approval. So you can or wait for other data to happen, other events.\u003C\u002Fp>\n\u003Cp>So you could build things that run, process some, you know, some data, and then wait for maybe an approval or maybe something else in your system to be updated, like a draft or publish status or something, and you can resume your function. So ingest your code will not be running. Ingest basically holds on to the state of your function. And then when it gets that event, it resumes and restarts your function from where it stopped, doing some different kind of magic and whatnot. So then again, you can use that approval to dynamically, you know, traverse and and run different steps.\u003C\u002Fp>\n\u003Cp>You can also do things that are a little bit, you know, kind of outside of this this this thing that are that are kind of extending the power, which are making reliable calls to LLMs by offloading those AI calls to ingest servers. You can also do things like what Brian talked about, which are basically use other types of steps like sleeps or delays. So you can delay to an exact time and then resume the code then, or you can delay to an arbitrary number of days or hours or weeks, and then your code resumes. So these are kinda just the the basic building blocks. There's a couple more that I didn't get into.\u003C\u002Fp>\n\u003Cp>But if you can learn these couple primitives, you can build a lot of complex logic that also is very reliable outside of box. So, you know, going from here, I'll just take a step back and look at the architecture. And, Brian, how are we doing on time right now?\u003C\u002Fp>\n\u003Cp>Speaker 0: We are we're no. We're golden.\u003C\u002Fp>\n\u003Cp>Speaker 1: Can you see? Okay.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Yeah. No. I just yeah. No.\u003C\u002Fp>\n\u003Cp>We're beautiful. No worries. I I I did just wanna say, like, the hey, like, the the background jobs and and, like, some of the the queuing stuff was what got me in the door. But, like, the the durable execution piece, especially when you're working with, like, the LLM stuff, after you've you've used, like, one of these big models where you could just literally watch credits evaporate when a function fails is, has been a, like, a nice piece for my workflow. So, kudos there.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Thank you. Thank you. Appreciate that. And, yes, to the to the, to the audience, yes.\u003C\u002Fp>\n\u003Cp>Using, step dot a I dot infer, if you're running in serverless because with something like Vercel, it can offload it to our servers. So your function stops running. It's not consuming any any compute and it offloads. So that was an easy one to jump in there. Thanks for that question.\u003C\u002Fp>\n\u003Cp>Yeah. I'll jump into the architecture because I wanna show you how it works with Directus and how the system is. So basically, at at the high level, this is a little, you know, you don't need to know, like, know everything in detail. I'll try to zoom and see if I can pinch in a little bit. There we go.\u003C\u002Fp>\n\u003Cp>So the ingest system comprises a bunch of things that you might typically build or need to to execute this and do this yourself. So and there's much more beyond that. But the fundamental thing is there's an event API that receives data from anywhere, could be from your direct assistance or the webhook, And there's, something that consumes those events and basically, creates new jobs and enqueues them. It stores the state of those jobs. It stores the history of those jobs in case there's a problem or anything or you wanna observe that later, which we'll get to, then it executes those jobs.\u003C\u002Fp>\n\u003Cp>And what happens is the ingest knows, okay, I'm running this function and it toasted on your direct instance. Your code is sitting in your direct instance, and it invokes it via HTTP. So it securely just calls it when it needs to, invokes the minimum number of work, and then returns to ingest. And then if it needs to resume or continue, it will make other calls as necessary. So if you sleep, it goes back to ingest, then it calls your server again later.\u003C\u002Fp>\n\u003Cp>So this is the basic kind of principle of how all the systems work together and how there's a queue built into the system that is basically pushing work to your system. And that's why it's really key to have the flow controls. You know, you don't want to you wanna set something like throttling because you don't wanna overwhelm and knock down your system or your database in case you're processing, say, a huge backlog of 10,000 or 10,000,000, jobs or tasks that you need to do. So this is really key and also just shows that the code that you write is hosted on your, your machine. It's where you have it.\u003C\u002Fp>\n\u003Cp>You keep it along with your existing, direct as code base, which is really great. So I'll, I'll show you a little bit about, like, why do we have a database, what is this dashboard UI. So when you're running these long jobs that are happening behind the scenes, they're often hard to observe. I'm sure that some of you have worked in systems where you're parsing just logs on CloudWatch or something like that. You're trying to figure out what's the status of this job.\u003C\u002Fp>\n\u003Cp>Okay. Now we need to chuck it into a database so we can observe this. And you're trying to piece together what happened and where things failed. So we get to that one question in one second. So what we have is we have a dashboard that gives you visibility into the system.\u003C\u002Fp>\n\u003Cp>So this is the ingest dashboard and ingest cloud where you can see the the output or status of all the different workflows and functions that you've run-in your system, and then you can expand these to see all the different steps and what might have failed, what have might retried, the outputs of those steps, some metadata that you might pull through like, say, tokens in the model that you might have used to make an AI call or the time stamp when something started or ended. You also can look at inputs, say, okay. What piece of content triggered this? You can debug what actually happened and understand, did this work? You can easily cancel and retry these things or retry them in bulk.\u003C\u002Fp>\n\u003Cp>There's a lot of operations you could do, but this is the core of, like, this observability that you have into the system. So in also at the, like, the highest level, you can see broad system level metrics, like what functions have failed. Is my import data pipeline failing at a high rate? What what's happening? I need to look into this.\u003C\u002Fp>\n\u003Cp>So it gives you this kind of overall picture of how the system is go is going where it's it might be doing things always in the background. It's it aren't always automatically visible. And you can run many functions and have all different types of, functions and workflows like generating video transcripts or creating chat completions or daily digest. You you just get the single pane of glass of, like, understanding what's running in my system. And so the point of concurrent runs that, was asked in the, in the, in the chat, what happens is basically, like, ingest, can can execute multiple things at a given time.\u003C\u002Fp>\n\u003Cp>Depending on your plan and your configuration, you can limit how much concurrency you want. So you can control I only wanna execute 1,000 things at a time, or you might have multiple servers that you've horizontally scaled to handle more load. You can control individually what that is. You can also with the multi tenant controls can can control how many concurrent jobs a given user gets. So you can limit it like that in case you're building that type of system.\u003C\u002Fp>\n\u003Cp>But, ingest handles that because it it it does the it actually processes the the work off the queue internally. It can you basically declaratively say, this is the concurrent number of like, amount of work that I wanna run. So that's a that's a great question though. So, you know, basically, to summarize, like, this part, there's why are we here? We're talking about in ingest and direct us.\u003C\u002Fp>\n\u003Cp>Right? So tie back a couple things that I that I went over. It's the same code base that you're already running. It's the same server. You get to take right.\u003C\u002Fp>\n\u003Cp>Use the ingest SDK to define the functions right in the same code base with everything else that you're using with Directus, and there's no additional compute necessary. And, and, Brian, you got some? No.\u003C\u002Fp>\n\u003Cp>Speaker 0: No. I was just jumping back in as we transition.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. I'll, I'll I'll, Brian's gonna go in a lot deeper about this, which I'm which I'm stoked for all y'all to see because he's built some really cool things. And and some of the things again, this is fault tolerance, the async workflows, you know, so you get the confidence, automatic retries, and control over these processes. And remember that steps encapsulate these retryable logic. You can bring existing logic, import new libraries, whatever the heck you want, write any workflow.\u003C\u002Fp>\n\u003Cp>And one thing that Brian will go deep on this, I think, is super cool about how it works together is Directus, hooks are super cool because it kinda provides this nice link between the direct to system and ingest functions, but I won't spoil anything there. I'll, I'll pass over to Brian to kinda take it from here. But, and then we can go to if there's any other questions, we'd probably go to the the QA afterwards so we can get through some of these things.\u003C\u002Fp>\n\u003Cp>Speaker 0: Some of\u003C\u002Fp>\n\u003Cp>Speaker 1: the questions might be answered.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Definitely. So I pop the questions into the chat. We'll get to them if we can. I'll take the screen back, and we'll briefly cover what is Directus.\u003C\u002Fp>\n\u003Cp>For those, who are joining, who are not familiar, Directus is I I like to call it LEGO for developers where, like, everything that you need to build a digital experience, whether it's an app, a website, automatic content translation, which we're gonna do today, it it's all there for you. And you're just stacking these bricks together a lot like, what Dan was mentioning with ingest of, you know, these primitives that you have, and you could use them to build really powerful stuff. So Directus will give you intuitive UI, which we'll look at in a moment, a nice, like, admin CMS layer. You get instant rest and GraphQL APIs on top of any SQL database, and this is all self hostable. You can also run-in our cloud as well.\u003C\u002Fp>\n\u003Cp>And just a little tiny, fine print there of any, should be like most SQL databases. Right? Alright. So why Directus? Why ingest?\u003C\u002Fp>\n\u003Cp>Why together? A couple things. Like, Dan already mentioned, you know, the hooks that we have in Directus make this super easy to communicate with ingest. But more than that, ingest gives you that durable execution. To say being able to build these powerful workflows in code is tremendous.\u003C\u002Fp>\n\u003Cp>So the first question that, I was asked when I was putting this webinar together, and and this is from our team was, hey. Why why ingest when we already have automations inside Directus? So if you're still learning Directus, we have an automations module called Directus flows, and it is allows you to define a low code, no code automations. You know, when something happens, do this. These flows are great for short lived automations.\u003C\u002Fp>\n\u003Cp>Some of the things that that Dan showed in the previous slide where we have the syntax of, like, the step function, Directus doesn't have the ability inside a flow to wait for thirty days and send an email or to, wait for another event inside of a flow, without some complicated logic tying it all together. So flows are great for short lived automations or automations that that don't have super complex logic, and ingest is a great complement to flows. And, you know, in the future, I could even see us having an extension to trigger ingest, functions inside a flow. But, as we get into flows versus hooks versus extensions, as as Dan mentioned, Directus flows underneath the hood once you pull off the the mask here, the the Scooby Doo, I kinda mean, is just using Directus hooks. So, Directus hooks, if we pull up my browser, which is always dangerous since I'm using Arc, Directus hooks are just code that fires whenever certain events occur, and you've got a lot of different hooks at your disposal.\u003C\u002Fp>\n\u003Cp>This is using custom extensions, and we'll kinda cover that in just a minute. But at the core, hooks are allowing you to say, hey. When this event happens at Directus, do this. And, we'll we're leveraging these hooks extensively in this integration. So there's filter hooks, which happen before the event is emitted.\u003C\u002Fp>\n\u003Cp>So if I wanna run some code to either modify the payload before an item gets created or potentially stop that from occurring, I could do that via a filter hook. And then the ones that we're leveraging today in this workflow are gonna be our action hooks. So when after an item gets created, we wanna do something. In this case, it's going to be sending those events to ingest. Alright.\u003C\u002Fp>\n\u003Cp>Any questions on hooks before we kinda dive into this? We've also got, like, an endpoint that we're gonna use, and I I just wanna take a moment to touch on one of the most important pieces of Directus is this extensibility. So if you go into our documentation, which is a a great starting point after this webinar, take a look at the extensions overview. You could customize every piece of Directus. So you could customize the interfaces, like how the data is displayed inside the studio.\u003C\u002Fp>\n\u003Cp>But the parts that we're leveraging today are API extensions. The hooks, endpoints, operations are within flows, and, of course, bundles are how we put all these together and distribute them. Alright. So how do we make Directus and Ingest play nice? And this was the part that, I did the heavy lifting on with some help from Dan and his team.\u003C\u002Fp>\n\u003Cp>You're still blown away by how easy it is to write ingest functions. It really just kinda gets out of the way. And and once you guys go through this process and set this up and and don't worry. Wait. There's more type of moment here.\u003C\u002Fp>\n\u003Cp>We're we're gonna give you the whole code base as the bonus after this, so don't worry about taking notes or screenshots or anything like that. But, once you've got this set up, then you can build really incredible stuff, with the incredible pace, I would say. Alright, so I'm going to open up my code editor here and we're gonna take a look at the first piece of the puzzle which is our environment setup. And for Directus projects this almost always starts with a Docker composed file. So this is the pretty standard boilerplate Docker composed from, our documentation.\u003C\u002Fp>\n\u003Cp>I've I've done just a little bit of cleanup here and extracted some of these things out to environment variables versus, just inlining them. And a couple things that I want to note, when you spin up Directus, especially, like, using our standard Docker Compose, which we're using Postgres for the database, Directus will create this, this folder structure for you with the database, the extensions. This template is is just for applying the schema that we've got for this example, but, it will create all of that for you. And for a cleaner DX, I've basically just extracted this custom Directus extension out to the root in a queue folder. So this entire code base, I've I've gone through the trouble of trying to make comments, to to add a little bit of what I was thinking as we went through here.\u003C\u002Fp>\n\u003Cp>But all the the rest of this is pretty standard with the exception of a different port. I'm sure Dan can relate to this as you're developing. You've got, like, 35 instances of your product running on your local machine at one given time, so you gotta switch these ports over. The only addition here is the ingest dev server. So the ingest dev server, they've got a a docker image that you could pull.\u003C\u002Fp>\n\u003Cp>You can also run this via NPM too. Right? Dan, a node?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Yeah. You can. You can install the binary via MPX or NPM.\u003C\u002Fp>\n\u003Cp>Speaker 0: Perfect. Perfect. Yeah. So, you know, we highly recommend Docker if you're working with Directus, but, you know, you could run this, ingest dev server using NPM if you prefer. But I just wanted to point this out like we've got it pointing to our direct assistance, and this is running on port eight two eight eight.\u003C\u002Fp>\n\u003Cp>The dev server is amazing for running locally. It's almost, identical to the ingest cloud dashboard, with a few exceptions. And, you know, one thing I do wanna call out, and I think Dan already iterated on it, is your ingest functions, unless you're doing, like, the the LLM infer that that Dan mentioned, these are all running on your same Directus instance whether you're using the dev server or ingest cloud. So I just wanted to call that piece out. Now for the environment that we've got set up here, you could see when you go to production and just has event signing keys or event keys and signing keys so you could keep, things very secure.\u003C\u002Fp>\n\u003Cp>For purposes here, we're just running, on the dev mode, keep it lightweight, make it easy to iterate. One thing to note, when we get into the translation workflow, we are using DeepL, so I've just got that environment variable. So when we are configuring a Directus extension, Directus has a extensions SDK that makes this process super easy. I just bang in this command into my terminal, n p x create direct us extension. Make sure you use the at latest tag to to pull that.\u003C\u002Fp>\n\u003Cp>And what you're going to initialize, I'll just show you kinda what this looks like, is a bundle extension. MPX Directus create extension at latest. This will ask you to install, and then we will go through and choose a bundle. And this is where my Internet just totally craps the bed apparently. Too many connections at once.\u003C\u002Fp>\n\u003Cp>Okay. Yep. So we scroll down, we hit a bundle, and a bundle is just a collection of these extensions. Now once you initialize that, it will look something like this. Where are we?\u003C\u002Fp>\n\u003Cp>So I've I've got this in our queue set up. You could see here we've got our Directus extension. It is a type of bundle, and then to add extensions to that bundle you run npm add or npm run add or p npm add, whichever package manager that you're using. So as far as the ingest setup here, let's dive into what that actually looks like. And, again, we're gonna give you access to all of this code.\u003C\u002Fp>\n\u003Cp>So at the high level here, we've got a function folder that stores all of our functions. We've got some hooks that we'll take a look at, and then we have this ingest client. So how do we set this up? How do we make sure it connects securely and plays nice with Directus? We are pulling in from the ingest package, and we're gonna skip this little middleware piece, but this create ingest client basically just returning a new ingest client that we can use to invoke these functions.\u003C\u002Fp>\n\u003Cp>Now the middleware piece here is rather important. Basically, all we're doing here is some dependency injection to use direct to services, like creating an item, updating an item, sending a notification to our user. So that's an important piece of the puzzle. There's a a couple things like a helper function in here to set the directest context so we can leverage that. And then we're just basically using a singleton pattern for this ingest client so that, you know, we're we're using the same client over and over.\u003C\u002Fp>\n\u003Cp>Setting this up and, you know, ingest needs to be able to serve those functions. So, in that we've got a endpoint that we define. So this is a custom direct us endpoint. You know, you can see some some standard, imports here. This is where we're actually going to import those functions.\u003C\u002Fp>\n\u003Cp>We'll show that in in just a few, but we're basically defining an endpoint. The ID here is gonna be the route that this gets served on. So, I'm using ingest. If I go to local host eight zero eight eight slash ingest, that's where my functions will be served from. And you can see here when we're defining an endpoint, we get the express router instance, and then we get the direct us context.\u003C\u002Fp>\n\u003Cp>And that is what we are calling here or or what we're passing when we set the directest context so that ingest has access to those directest services. Very important piece. If you're setting this up on your own, don't, don't forget that part of it. Last but not least, we'll basically just serve the ingest client here. You can see the syntax we're passing in the client that we've set up, and then we're giving it the functions to serve.\u003C\u002Fp>\n\u003Cp>Now the last piece of making these actually talk to each other is the direct us hooks. Right? So, we've got our hooks set up. The famous handler. I see.\u003C\u002Fp>\n\u003Cp>Not sure what you mean by that, Bazar, but, I we can unpack that in the q and a for sure. Alright. So hooks. Right? Again, we're just defining these actions that we want to run whenever a certain event occurs.\u003C\u002Fp>\n\u003Cp>And I was I was using I I won't say I was using this wrong, but Dan definitely gave me a level up, while we were prepping for this webinar. So, breaking these down. Right? We're using the action hooks inside Directus. So I only want to invoke a ingest function after a certain thing occurs, not not necessarily before.\u003C\u002Fp>\n\u003Cp>So if we break this down, whenever a post so we'll get into our data model in a moment, but whenever our post gets updated, we are going to send, ingest an event that our posts were updated and we're gonna pass the the event from Directus and this accountability object from our context, which is basically this is the user permissions and, like, the user ID and things like that. Is this user going to have access to actually update that post, after we do the translations that we take a look at. So\u003C\u002Fp>\n\u003Cp>Speaker 1: one\u003C\u002Fp>\n\u003Cp>Speaker 0: of the things that I I do wanna recommend as you go through this, mirror the event names inside your ingest functions from your direct us, events. So you could see this syntax here. Basically, I've just added direct us in case we've got some other service that we want to send events to ingest from. But here, I've just mirrored the syntax and the reason why, and Dan schooled me on this, is it makes it easier to debug and trace these things through your application, especially if you want to trigger multiple ingest functions on a post update or when a post gets created. Alright.\u003C\u002Fp>\n\u003Cp>So the last piece of our puzzle. Right? We've added hooks. We will create our ingest functions, and we'll take a look at that. We'll share this amazing slide deck at the end of this as well.\u003C\u002Fp>\n\u003Cp>But we've got a full guide if you've already got a direct us project and you just wanna walk through integrating this. We've got a full guide that we've written on our documentation for you to check out as well. Alright. So, Dan, I'm gonna bring you back. This was your diagram.\u003C\u002Fp>\n\u003Cp>You know, maybe maybe kinda touch on how this works a bit.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Yeah. So I think, Brent really laid it out really well here in that. He just showed where with the extension and direct us hooks, that's where we're we're hooking to these actions and just broadcasting basically events over to ingest. So So those events, are basically declaring what happened within the direct to simple, system.\u003C\u002Fp>\n\u003Cp>And then our ingest functions, will declare when they run. Right? We we saw that before when I walked through some code. You declare when something happens, this is when I wanna run. So instead of directly invoking jobs, this allows you to decouple and also build things independently and do things like fan out, as Brian alluded to.\u003C\u002Fp>\n\u003Cp>So the ingest system basically understands what your function, your workflows, want to do when, and it basically routes them through back to to, to your server. So at the highest level, that's how we're stitching together the hooks with events ingest and then back to the back to the system to to call your code.\u003C\u002Fp>\n\u003Cp>Speaker 0: Beautiful. Beautiful. I I love the love the diagram, and it like, this was a a big reason why we shifted to this format to make it feel a little more interactive, a little more fun. So all that said. Right?\u003C\u002Fp>\n\u003Cp>Now to the main event of, like, hey. We we've got this set up. These two are talking together. How are we going to build this advanced workflow? What are we gonna build?\u003C\u002Fp>\n\u003Cp>So we're gonna show you guys content translations that are automatic and, of course, durable, and I will say totally unbreakable because I know I've written a lot of the code, but but very, very durable, and, like, an incredible workflow for, anybody on your content team that needs to handle translations. So if you've worked with content translations and other systems, this GIF probably holds true. It is so much fun, probably involving, like, a lot of spreadsheets, a lot of working back and forth with either different contractors or different team members. Directus makes that whole process a lot easier. We've got a beautiful interface for it, and, I think Matt on our team is gonna kill me for saying beautiful intuitive interface.\u003C\u002Fp>\n\u003Cp>But let's dive into why this why this sucks. Right? We're gonna do this manually as kind of the first step that usually involves those spreadsheets, then we can graduate to APIs. The one we're using today is DeepL, but, you know, Chat GPT and some of the other LLMs have gotten incredibly good at translations. Kind of a toss-up there between speed, DeepL, I've found is is really quick, and, it seems to be highly accurate.\u003C\u002Fp>\n\u003Cp>But as you get into that, especially when you're translating a lot of content, you run into, hey. This is gonna take a while. And as Dan alluded to, I've already ran into this, like, 35 times through the, building of this thing is, oops, we hit the rate limit of the APIs or one of those API calls fails. What happens? Right?\u003C\u002Fp>\n\u003Cp>Then we lose not only our data, so, you know, we just hope and pray that there's some logs that we can go back and get some of that data, from their side or, you know, just evaporates. So, this is what we seek to fix inside this workflow. And before I show you Directus and Ingest, I want to take a look at our our data model at a high level. Now, this is a simplified example, and I guarantee you when you go to production, you've got a lot of content that you're gonna translate that it probably has a more complex structure than this. But, I do wanna give you this, like, again, we're gonna give you the whole code base.\u003C\u002Fp>\n\u003Cp>You could take this and run with it, and so this starting point will make it easy for you to adapt to those, those more complex models. At the high level, we've got a post. The post has a title, a slug, and some content. Now, there's also a languages collection inside the Directus instance. Whenever you use our translations interface, we will create that for you if you don't have it already.\u003C\u002Fp>\n\u003Cp>The format is pretty standard. We have a code like in US, the name English direction, and then one of the additions I've added here is just a DeepL translation code because their API, doesn't necessarily follow the standard ISO codes. And then for each, post that we're gonna translate we have a relationship to a collection or a SQL table called post translations. So within that we have a pointer back to our language and then we have our title, slug, and content. So at a high level that's the data model we're working with.\u003C\u002Fp>\n\u003Cp>Let's kind of pull this up and and see what this actually looks like. Alright. Dan's gonna laugh at me here. I'm sure to mess up Arc here. Still haven't mastered the side by side in Arc.\u003C\u002Fp>\n\u003Cp>So over here on the left, I've got our, ingest dev server up and running. If I go in here, you could see I I don't have any runs yet, But over here on the right is our special themed version of Directus just for the ingest webinar. I really dig the the black and green vibes that you guys have on the website, Dan, by the way.\u003C\u002Fp>\n\u003Cp>Speaker 1: Same. Okay.\u003C\u002Fp>\n\u003Cp>Speaker 0: Nice. Alright. So, if we take a look at this, right, this is a beautiful gosh. I gotta stop saying beautiful, Matt. Intuitive interface for all of our content editors.\u003C\u002Fp>\n\u003Cp>We could see the default language up here at the top. So we're writing this in English. We're gonna pass that to the DeepL API. When we send this translation over, you know, I could set this and and write in whatever default language that I want, and we'll handle the translations. Directus gives you this beautiful side by side view for your translations, and we can see that all of these different languages in our system are fully translated.\u003C\u002Fp>\n\u003Cp>So, the flow that we're gonna set up, and I'll I'll walk through this in a minute, is we're gonna take any post anytime a a change happens or we create a new post, we're gonna fetch all the languages that we have that we wanna translate content for, and we're gonna go and actually translate that. Beautiful. Alright. So let's take a look at this invincible translation workflow. And usually I do this kinda in code, at the top of my document.\u003C\u002Fp>\n\u003Cp>I break this down. Since we did this format, I wanted to make it a little more visual, and I'll still walk you through the actual code for this. So at the start of this, what will happen and what I left off of this diagram is the user event. Right? User creates a new post, and then that kicks off the process here.\u003C\u002Fp>\n\u003Cp>So we're gonna send the post ID, we call it a key, to this workflow. And the first step is gonna be normalizing these event keys because the Directus, event emitter, like if we do post dot update we get an array of keys versus like post dot create we just get a string for the key. So we're gonna normalize those keys. We'll check and see if relevant fields have changed. So if nothing has changed on an update, doesn't make any sense to actually build these translations again.\u003C\u002Fp>\n\u003Cp>If something has changed, we're going to retrieve all the translations for that existing post, and that's an important piece. Again, you know, we don't wanna translate content if we don't have to, if it hasn't changed. This is gonna be using, Directus item service, so we'll dive into the code in just a moment. We have, we'll get all of our available languages via the direct Us item service again. We'll use both of those to build a list of translation items.\u003C\u002Fp>\n\u003Cp>So what do we actually need to translate? We'll fire those off using, the ingest step functions to the DeepL API and we're gonna do that in parallel and we'll also loop over those so we get that nice tracking and observability. We'll log any errors and then when we get that back we're going to upsert into the database and potentially notify the user. So that's the flow at a high level. Let's take a look at the actual code.\u003C\u002Fp>\n\u003Cp>And, Dan, if I gloss over something or I miss something on the ingest side, definitely call me out for it.\u003C\u002Fp>\n\u003Cp>Speaker 1: Will do.\u003C\u002Fp>\n\u003Cp>Speaker 0: Perfect. Alright. So, again, you can see here, this is, these are the two hooks that we're using to actually manage this workflow. So whenever an item gets updated, we send ingest, hey. We updated this, or we say, hey.\u003C\u002Fp>\n\u003Cp>We created this. And then our function looks like this. And, this is not the shortest function I've ever written, but definitely not the longest. I'll say that. So, again, an outline of the translate workflow, what I found is super helpful for me is just to quickly outline the logic at the top of each one of these.\u003C\u002Fp>\n\u003Cp>And if we go through, we could see that we're importing that ingest client. We've got the DeepL API, just their node client that we're gonna use, and we've got some types that we're pulling in to, make the TypeScript compiler TypeScript gods happy. So, we've got some translatable fields here. Again, it just defining some constants. These are the only fields that we wanna translate.\u003C\u002Fp>\n\u003Cp>You could easily set this up to be dynamic, and just defining some of the the DeepL params that we're gonna use in our API call. So when we get into the meat of the ingest function here, you can see we've got an ID. We've got our name just describing what this is. And then, you can trigger an ingest function, on any number of events. So the standard syntax here is an object with an event property, but if you want to have multiple triggers for this function, you could just pass an array of objects.\u003C\u002Fp>\n\u003Cp>Great. So onto our handler function, we're getting the event and the step, which is standard syntax and Jest is giving us that. And then we're also pulling out this direct us context that we added through that middleware. So through that, we're gonna get our services, we're gonna get our schema, our EMV variables, which is gonna give us the DeepL API key. So the first step in this function is normalizing those keys.\u003C\u002Fp>\n\u003Cp>You could see all of that code here. What's notably missing is the step functions that we saw earlier. And originally, I had these wrapped, but, a a nice little tip that Dan gave me is if this, the code that you're running doesn't actually mutate external state or depend on external state, you don't necessarily have to wrap it in a in one of the ingest step functions. Next up, we'll get our payload from the event. We'll check that to see if we have any translatable content included.\u003C\u002Fp>\n\u003Cp>And if we don't have any translated translatable content, then we can just return early. Right? Next, we'll get our translator. So this is the DeepL client. We'll get our schema from Directus, and we're gonna init these item services.\u003C\u002Fp>\n\u003Cp>So this is how we talk to the database, on the Directus side of it. There's just a little helper function down at the bottom of this file that that makes that a little less verbose, and then we run into our first step function. So here, we're going to get the current post, and this is just a, a service call by the post service. So we're gonna read by the query. We're gonna look for the post with the ID that we were passed in the event and return not just the root level fields, but also the translations that are attached to that post.\u003C\u002Fp>\n\u003Cp>So if we look at that in, like, the Directus UI to give you an example, we're not only gonna fetch this information, we're gonna fetch all the individual translations. And that is one of my favorite features of Directus is, being able to fetch the data that I need in a single API call. So I could go deeper into this if I wanted to, you know, three, four, five different levels. Eventually, you'll reach a max where you you don't wanna go, but, depending on the data that you've got, using these asterisks as a wildcard is incredibly helpful for local development. Now, going further, again, we're just going to have a step function that, will cancel this whole thing.\u003C\u002Fp>\n\u003Cp>If we can't get the languages from Directus, we don't know what to translate. And this was a a last minute addition. And, Dan, could you talk about, like, the retry logic just a little bit?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. As Ingest handles, errors automatically and does retries, some errors you might anticipate and say, this is a non retryable setup. So if I'm missing the API key, might as well not retry it because it's just not gonna work. So in that sense, you, ingest allows, includes a custom function, basically, which allows ingest to say, you know what? Let's stop here and not retry anything else.\u003C\u002Fp>\n\u003Cp>So that's what Brian is using here where this is a nonrecoverable error kinda situation. So but, typically, you can throw errors to customers and whatnot, and, those will all be retried automatically. You can even catch them and handle them however you want as well.\u003C\u002Fp>\n\u003Cp>Speaker 0: Beautiful. Beautiful. Thank you, Diane. Alright. Let me try to find where we were at.\u003C\u002Fp>\n\u003Cp>Okay. So we've got our languages, then we move on to the next step, which is actually building our translation list. Excuse me, guys. Dealing with six not six kids, but sick kids here at the house. Always a struggle.\u003C\u002Fp>\n\u003Cp>So here in this step function, we're basically just building up a list of the translations that we want. Right? So we're looping through all of those, posts that we've got, making sure, you know, we've got the fields that we wanna translate, and then we're basically building an array for those things that we'll we'll pass to the next step as we scroll down, which will be actually translating all these items in parallel. So, here, we're using promise dot all to fire these all at once. And, again, I think hey.\u003C\u002Fp>\n\u003Cp>Dan, you mentioned this, like, it like, defining a unique ID for these was not not strictly necessary, but, maybe talk about that for a minute if you don't mind.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Yeah. When you're executing in a loop, it's or in something like here where you're paralyzing with promise dot all. You don't need to. Ingest basically takes this and under like, understands, like, internally.\u003C\u002Fp>\n\u003Cp>You could check out though the SDK as well if you're curious about how it works. Basically, it takes the step ID and and and appends some sort of, iterator and creates a hash. So, automatically, it, make sure that if two steps have the same ID, it is it will, like, not they won't overwrite each other conflict. But for the sake of debugging, which I think is a great idea of what Brian's done here is you can dynamically set these keys, in your in your loop, which makes it easier for what Brian will show in just a minute, in the UI. Yeah.\u003C\u002Fp>\n\u003Cp>Great.\u003C\u002Fp>\n\u003Cp>Speaker 0: Alright. So we go through we send all these to DeepL. We get all of those things back, and then, the final two steps here, we're basically, again, using promises to do all of these upserts. So, when you're using these services in, like, inside the actual directest, like, API endpoints or hooks, there is this upsert, which, if anybody on the Directus team is listening, would love to have the upsert on the SDK. Just put in a nod for that one.\u003C\u002Fp>\n\u003Cp>But, this makes it super easy to, upstart content. Super simple. Like, hey. If this ID doesn't exist, we're gonna create the translation here. And then last but not least, we're we want to notify the user, hey.\u003C\u002Fp>\n\u003Cp>Your translations are done. So you can go check them out, because this is running in the background. Awesome. So that's the flow. Let's take a look at the UI.\u003C\u002Fp>\n\u003Cp>How does this work? Let's, what do we what are we gonna translate? Dan, do you have any thoughts?\u003C\u002Fp>\n\u003Cp>Speaker 1: I don't know. You hit someone good yesterday. It was just, like, hello from\u003C\u002Fp>\n\u003Cp>Speaker 0: Hello from Dan and Bryant. Alright. So we're going to throw this in. What do we have? This is an amazing webinar.\u003C\u002Fp>\n\u003Cp>This is not me. This is somebody in the chat. We're not saying we're amazing. But alright. Here we go.\u003C\u002Fp>\n\u003Cp>So now what will happen as soon as I save this, over on the left, we should see the event being fired to ingest, unless I have done something totally wrong. And and just to prove that I'm not pulling any switcheroo on you guys, we don't see any translations there. So immediately after I've created that new post, now we could see, the ingest dev server and I get all this observability, all these steps that ran within the actual flow. So we get all of those steps broken down and you can see here we've got the individual steps within that loop that we use those specific IDs for. And as I go through here, you could see the output for each one of these.\u003C\u002Fp>\n\u003Cp>So, I don't speak Russian. Not sure if you do, Dan, or not. No. So there we go. So we could see here's the actual content that's that's getting translated.\u003C\u002Fp>\n\u003Cp>There's the slug. There's the title, etcetera, in all the different languages that we have set up. And this it's like seeing this together was, like, when it really hooked for me of, like, okay. Great. All this is running in the background.\u003C\u002Fp>\n\u003Cp>I get all the observability. So, like, when something inevitably screws up, which for me is often, if you catch any of the hundred apps hundred hours episodes. But and, like, having all this at your fingertips is incredibly powerful as a developer. And being a a developer that has all this and is able to build a flow like this that will do all the translations for your team automatically, turns you into a hero, %. So that is the flow.\u003C\u002Fp>\n\u003Cp>Dan, any anything to add before we kinda jump into queue?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. And what's really nice is I think just, you set it up earlier with Docker Compose, but with everything running on your your machine, you can work and iterate quickly on this flow and not have to worry about, like, conflicting with, you know, bumping into shared resources. Like, if you're using something like SQS or something like that on Amazon, you need to provision those things. It becomes a little bit of a nightmare. And what also is nice here is, like, you know, Brian's code works perfectly as we see.\u003C\u002Fp>\n\u003Cp>It's all green. But if there was an error, you'll be able to see that span go red. And what's nice about the dev server flow is that it saves the input of your function. So if Brian were to go back to his code base, fix that bug, save it, the dev server would basically you know, the his direct to server would would refresh, reload, and you could click rerun in this dev server, and it would just rerun the function again. So if he, you know, if you hit rerun live, it should just it should just work.\u003C\u002Fp>\n\u003Cp>And so this gives you, like, this fast feedback loop. So you're, like, in this kind of hot reload situation where I'm working on my I'm tweaking I'm tweaking. So instead of you having to, like, go to the right, manually click a bunch of buttons in the in the Directus UI, you can have a fast feedback loop. So, like, do it once in there, keep going, you know, tweak the output, look at things, tweak maybe prompts or different things that you might be using, to to create this. So at least, like, this allows you to kind of, hopefully move a lot faster when you're building these these things that can be complex.\u003C\u002Fp>\n\u003Cp>Speaker 0: But, yeah, I can't stress that enough. Like, the the speed at which you could iterate with the dev server and, direct us being able to, you know, go in and quickly model a feature and idea, and then also being able to, like, prepare that for scale using Ingest is, again, a great pair. Just, works really well together. Alright, guys. So if we move to our amazing slide deck, it is now time for Q and AO.\u003C\u002Fp>\n\u003Cp>If you guys have any questions in the chat that we wanna take a look at, Dan, do you spot any that we need to\u003C\u002Fp>\n\u003Cp>Speaker 1: I did see a couple questions I could talk to. The first thing I could do was a couple questions on self hosting for Jess side because we we know we can, self host direct us. Right? And I'm sure a lot of people do that as well.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Self hosting is incredibly popular for Directus.\u003C\u002Fp>\n\u003Cp>Speaker 1: So I'm sure I'm sure there's a lot of people in the audience that are very curious about self hosting ingest as well. And you can self host ingest. The code that, Brian showed that runs in the DevServer, that binary is the exact same binary that you could self host. You can also you can run it in a very lightweight version or you can offload. Ingest has queuing and state history involved like that it that is backed by.\u003C\u002Fp>\n\u003Cp>So when it's running locally, it just runs in memory because it's low volume and it's very simple. But when you self host it and you deploy it into your own cloud or wherever you want, you can hook it up to an existing, instance of Postgres or you can also, plug it out and and connect it to a dedicated Redis, maybe running in another container or something like that. It can handle a little bit more scale and handle, like, you know, restarts of your ingest system. So, you know, you can self host. There are certain things that aren't in self host yet, like, some observable observability and metrics.\u003C\u002Fp>\n\u003Cp>A lot of those systems were built in ingest cloud. We're gonna be, you know, kind of bringing some of those things down to, to open source as well. But now, you know, all the key features, all the throttling, flow control, defining functions, are all are all there. So you can run that wherever you want and and and self host.\u003C\u002Fp>\n\u003Cp>Speaker 0: Amazing. Got it. So I on the directed side of it, of course, like, you could self host, we've got a BSL license, which basically, a free to run for anybody under under $5,000,000 in total finances or revenue. So So if you have questions on the license, definitely reach out to our team about that. You can do that through the website.\u003C\u002Fp>\n\u003Cp>What other questions do we have? I think there was one that I saw that, was super helpful. I like, comparing I like, when I first came into Ingest, I'd heard of Temporal, worked with it a bit. I can't find this one in the chat now, but, like, how do you guys stack up against temporal, Dan?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Yeah. We that's a I think it's a great question. You know, especially with the term durable execution, temporal's, you know, in its essence, describes itself as a durable execution engine. So it is dead focused on a, what we believe is just like the durable execution of the function.\u003C\u002Fp>\n\u003Cp>Actually, the logic. Right? Is when something fails, it does checkpoints and it and it retries. So in that sense, there there is similarities. Right?\u003C\u002Fp>\n\u003Cp>But we consider that durable execution is just like a means to an end. Right? It is it is a feature. It is not the whole platform. So what ingest really, layers on is a couple things.\u003C\u002Fp>\n\u003Cp>We have an event based approach as Brian showed with the hooks, so you can fan out and you can replay and do more things, have a little bit more flexibility with events. And if you're someone who likes events, like, I'm sure that that resonates. And if you haven't, give it a try. And all the flow control and advanced queuing is one of the things that is unique to ingest and, is is in this self self hosted open source version as well is all this reliable flow control. So when people are building these systems, often you don't just wanna execute a job and run it to completion.\u003C\u002Fp>\n\u003Cp>You need to manage how fast it's processing, this job, you know, how many times per minute you might run something. Maybe I wanna delay, I wanna debounce something, I wanna rate limit this job, run things in batch processing. Maybe just dynamically say, when there's 20 posts that have been published, let's execute this batch instead of saying, you know, let's just push these 20 items in one big blob. So there's a a a lot of differences in that sense of, like, what we've built around. And I think one of the things that also is true with the ingest SDK is it doesn't mess with the runtime.\u003C\u002Fp>\n\u003Cp>If you have used temporal, you in the TypeScript or JavaScript, you know, SDK, temporal does something where they kind of, like, wrap your logic and certain things like random doesn't work. So there's some gotchas that's like, I don't know what's going on with this runtime or it's going to wrap some of your things and you might not it might just not be native code, but we've fundamentally chosen to build ingest, say, anything that you're using works. It's very easy to look in the source and see where things are running. It's very it aims to be a very thin layer, so there's no weird kind of, like, gotchas and things that I need to know. And, generally, like, you know, ingest is very dynamic and defining steps and everything is very fluid.\u003C\u002Fp>\n\u003Cp>So there aren't, there's a lot of friction to, like, the rigidity that you might find with other solutions. So I those are just a few things. There's many more, that you could check out, like, on our site and whatnot, but or ping me afterwards if you if you if you're curious.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. And I'm not sure that you could say this, but, like, the the syntax that that I found, like, writing the same sort of thing in in jest is dramatically, easier, and it just jives with the way that my brain works versus, like, some of the verbosity and, like, just how temporal structures things. So, do we have any other questions? I I guess that's gonna sting a bit if we have temporal onto one of the partner webinars as well. But\u003C\u002Fp>\n\u003Cp>Speaker 1: I, what is called? I'll mention one someone asked a question about, retries. What happens when something hits hits max retries? The function will be declared as failed. And what, ingest also allows you to do is basically say, you know, if there's a complete outage, say, DeepL's down for twenty four hours or or an hour, all your functions are failing and all the retries are exhausted, you can use ingest to say, select, you know, between these two time stamps, anything that failed, replay them so you can do bulk retrace, retries, we call replays of, of those functions.\u003C\u002Fp>\n\u003Cp>So you can recover from systems because we persist all the inputs, you know, if that's helpful.\u003C\u002Fp>\n\u003Cp>Speaker 0: Definitely. And then, I think probably the last question before we wrap up is, can you run webhooks in the ingest server, the dev server?\u003C\u002Fp>\n\u003Cp>Speaker 1: You, you can. You'll have to configure your webhooks in in in in I guess it depends on, like, how you're how you're running things. So, Ingest Cloud, it has webhooks and and transforms. And locally, you just need to write a little logic to just, like, wrap that transform to simulate what, what is happening. We'll be bringing in in the future a synchronization, some thing with Cloud to make that a little bit more easy and bring them into the DevServer.\u003C\u002Fp>\n\u003Cp>But fundamentally, the webhooks are just the same API endpoint that ingest dot send is using, that Brian showed in the in those, in those direct hooks. So it's just sending JSON payloads, and that's what, what webhooks primarily are. So, it's, it's pretty easy to to utilize it and build around. There's also a few different docs on our website about that if you're curious about webhooks.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Awesome. I don't I don't see any other questions from the team. We're a little bit over, Dan, but I I I man, I appreciate you coming on. Like, before we kinda get into the awkward outro, I do want to just reiterate, like, for anybody still here, if you've registered for this, we'll hit you with an email with all the links to the repo, this amazing slide deck that we put together.\u003C\u002Fp>\n\u003Cp>So don't worry about that. That'll be coming in the next, couple hours, so just be patient. But wrapping this thing up, we we hope this was helpful for you guys. Please send us your feedback. We really enjoy doing these webinars, showcasing other tools and, things that that help you build faster.\u003C\u002Fp>\n\u003Cp>And, Dan, thank you for joining. Really appreciate the, you know, the collaborative effort on this thing. Learned a ton about it. And, for everybody else who like, next steps for you guys, what what does that look like? You know, you wanna sign off a little bit?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Yeah. Thanks for thanks for having me, and, thanks for doing all the leg work. You wrote all the code. You built all of it.\u003C\u002Fp>\n\u003Cp>So it's, it was pretty awesome to see this, and I think it's a great example. I think if you really wanna figure it out and you want some more, there'll be the follow-up email. Brian also has a really great tutorial that he put together, which has a lot of detail. It's pretty incredible, but definitely check that out. I'm sure that'll be in the follow-up email.\u003C\u002Fp>\n\u003Cp>And, then also, I'd say, like, if you just wanna tinker a little bit, go with ingest, go check out, one of the quick starts on our docs and just go tinker a little bit with the dev server, build some different things with some dummy code, and then kind of, that's easy way to just kinda get started, and then you can kind of dive in for the in-depth stuff with Directus using, using Brian's tutorial that he wrote. So thanks for everybody joining. And if you ever have any questions, we, we do have a Discord community. You can find the link on our site, or you can always reach out to us, contact us anytime with ingest dot com. That's 2, by the way.\u003C\u002Fp>\n\u003Cp>Just always remember that one.\u003C\u002Fp>\n\u003Cp>Speaker 0: Two n's. Yeah. Perfect. Alright, Dan. Thank you.\u003C\u002Fp>\n\u003Cp>Thanks for the audience. That's a wrap. We'll see you all.\u003C\u002Fp>\n\u003Cp>Speaker 1: Thank you.\u003C\u002Fp>","With Directus and Inngest. I'm your host on the Directus side, Brian Gillespie, developer advocate, growth engineer, professional hack and slash developer over here at Directus. Super excited for today. Got a special guest that I'll introduce in just a moment. We should have a good time. We kinda reworked the slide deck a bit last minute, as I tend to do. But before we dive into introductions, let's just take a look at what's on the agenda. Alright. So we'll go through some super awkward introductions in just a moment. We'll deep dive into what is ingest, what powers ingest behind the scenes, why you would wanna use ingest, then we'll get into why Directus and ingest are a great pair together. And then we're gonna run through this advanced content workflow after we've, walked through this integration of invincible automatic localization, for your content, which is always a beast. So with that, I want to kick over to mister Dan. Dan, how are you, sir? Great. Great, Brian. Thanks for thanks for having me. Stoked about stoked about doing this. Thanks everyone for joining as well. My name is Dan, and I'm the CTO and cofounder at a company called Jest. And we'll, we'll get into a little bit more about what that is and, what we're doing today and, why you've joined. But, yeah, just thanks for joining, and I'm stoked about the stuff that Brian, Brian kicked off today. Yeah. Yeah. And, Abel, like, for for some context and backstory, like, I first found ingest, we were working on a project we were calling Event OS. And, Direct has made it a breeze to set this up, get some APIs to work with inside a Nuxt application, but it's not dissimilar from the platform you use to register for this event. One of the the main challenges was, like, how do we send notifications and, like, how do we set up reminders an hour before an event and still have that be robust, like, cancellable in case we move the event, you know, and all sorts of things that we're gonna cover today. So that was my introduction to ingest, and frankly, I was kinda blown away. I I don't think if Matt is here in the chat, I was like, man, we gotta get these guys on a partner webinar, and voila, here we are. So with that, we'll just drag the little slide deck over. And what's up next is what is ingest? So I'll kick it over to you, Dan, and, you know, let you dive into ingest a little bit deeper for those who are unfamiliar. Great. Thanks, Brian. Appreciate it. So I'm gonna give you a little high level overview today, and we're a technical audience, so have a little bit more of a a technical variant on slides today. So, if anyone needs things a little bit bigger, tell me to bump it up in the chat. I'll try to respond if there's anything. So yeah. So, basically, what is Ingest? Okay. What you can basically think about it as the at the high level is that Ingest is a workflow orchestration platform that enables you to run reliable asynchronous jobs anywhere. We're gonna get come back to that in just a just a minute or two. So why would you use ingest for this this task, this need? What ingest allows you to do is to define these step functions. So you're creating workflows directly in your existing code base, use our SDKs, and any logic that you're used to. There's no DAGs or really obscure complex DSLs to learn to build these, powerful functions using regular code. So they're also reliable by default. So everything that you run with ingest automatically is retries. I think we've probably a lot of people here have built systems where, things go wrong. An API goes down for a few minutes, there's a blip, there's a networking thing, there's a race condition that happens in your database, and all simple retry a few seconds later would solve your problem. So ingest does that by default, and it also includes, something that is called durable execution. You may have heard this term, and if you haven't, you can think of this as your code is designed to be fault tolerant and survive catastrophic failovers, basically. So I won't get too deep into that topic today, but there's plenty of resources that we can send you after if you are curious. The last thing is that we've built in queuing and flow control right into ingest so that you can go to production confidently. What that means is that there are concurrency management built into the system that you don't need to have run a a worker or a queue or anything like that. You just define it in your code very simply. You also have controls for multi tenant support, which is another advanced topic we won't get into yet today. But you can just kind of anything that you might have built into some sort of, asynchronous system that you might need is there at your fingertips with easy configuration like throttling, rate limiting, delaying the start of jobs, debouncing, batch processing, dynamically prioritize prioritizing certain jobs, as well as running work in parallel or fanning out to multiple jobs. So that's at the high level, but realistically, okay, what can I build with ingest? So let's just you know, at the highest level in general thing is ingest is a general purpose tool, and you can run any background task. Something simple, just one function, one operation to something that is that is very complex and has to orchestrate between different process. So you also have the control in your hands. You can control the flow of the logic with if statements, not DAGs, and you can kinda build whatever you want. So I'll give you a little taste of, you know, with this audience, which is technical, and I'm sure a lot of y'all are interested or already users of Directus. You know, what might work in or make sense in your in your situation or what you might be building? So one of the first things that's very hot, sure a lot of you are dabbling or building already, pushing these things to production is AI workflows. So you can handle the complexity of things like flaky LOM API calls that might fail, things like chaining, building agent loops, calling tools if you're getting that advanced, or do things like human in the loop flows. You know, you're you're proving the work that that, LLM did for you. So this might be a research AI workflow. Go out and get all this data and pull it back and store it somewhere, or you might be creating content. That kinda leads me into content processing, which is where you might be pre or post processing some content. It might be a blog post or could be static assets that are in your system like images or videos. You might be doing things like translating, resizing, transcribing more to those assets. You might need to kind of create a complex pipeline. So one example here is that you you might be taking data, from your, your Directus system and you might be piping that into maybe a React SVG library and generating visual assets on the fly for your content and then uploading it to where you're storing, your assets. You know, another thing there, like, kind of beyond content is is user journey automation. Brian said at the top something about scheduling emails with events and and and and that and and whatnot. Within just, sorry, Brian. No. No. That was a huge one for me, man. That was, that was, like, my introduction to it, and I think that was, like, one of the one of the first case studies or something I ran across in the docs of, like, hey. Just something as simple as let me sign a user up and then wait a day and send them something Yeah. Incredibly convoluted in other systems. Yeah. And and you know what? You might have this thing where you could some systems might allow you to schedule an email in the future, or you might have a cron based system. But what if you could just, you know, prepare something, do some operation maybe like you're sending somebody you know, you're sending someone an invite, and then that invite email goes out. And then twenty four hours before the event, you might wanna schedule and say, let's render this email. Might be using, say, React email, which is awesome, and, pulling in data from different sources and sending that person a personalized email twenty four hours ahead. Or you might wanna customize, hey. This user is in this time stamp. So you might wanna, like, have more control sorry, time, time stamp, time zone. So you might wanna have, like, a time aware thing. So you could use something like another complex tool or, whatever is within an event platform that you're doing or something like Intercom or HubSpot, but that can be either not very controllable. You can't bring in custom logic and code. It's very static. They're crazy expensive, for those purposes. So you kinda do whatever you want here. Build flows that are scheduling delays, Slack notification, even like things like provisioning resources or provisioning access. So, that's a fun kinda use case. You can kinda build anything triggered with these vents. I'll go through a couple of these other ones because these are kinda secondary. But, you know, if you're integrating your system with a third party webhook, maybe something like integrating Stripe payments somehow into in into your system, or maybe you're using a system like Mux, and you're uploading your video assets to Mux, but you need to do something in your Directus CMS after those videos are uploaded, you can use a webhook. And you can those events can go to ingest and trigger your functions to run your Directus instance. So you can kind of build more advanced integrations with more control that you might need. And the last thing that happens in a lot of systems is there's always data from one source to another to another place. So you might have just data import jobs. Kind of think about it like you're building ETL jobs or just pipelines or synchronization, but you don't, you know, you you don't need anything like a complex data science tool or, something that a data engineer might use like an airflow or something like that. So you can build kind of anything in this, but I hope these are kind of ideas that might work with, what you're already doing with Directus. So I'll go from there, and I'll, I'll get to that question in a little bit. We have some time at the end as well. And, I'll go and just show, like, a little bit of functions. You this again is a technical audience, so let's just show some code about how you build an Ingest function and how it works. So I'll run through this. Basically, what you're doing here is you're creating the Ingest function. It's pretty simple. You define it in your existing code base along with your directus functions, your directus logic and whatnot, your whole system. You can do things like define queuing or flow control right at the top of the function very easily. So if, for example, you just wanted to run this job, at most 30 times in sixty seconds and you wanted to max out at three retries or 15 retries, you can control these things very easily and the ingest system will just do it automatically for you. You don't need to battle queues and infrastructure for that. You also declaratively define the event that will trigger your function. So in this case, when a blog post is drafted, run this code. And then bay very simply, you define a function right here, which is a handler that has all your code. If you're thinking about what I talked about before which is workflows, steps, and whatnot, when you're defining ingest functions, all you need to do is learn a couple building blocks which are steps. The main one is step dot run. Basically, you can encapsulate some sort of business logic, some sort of side effect, and you can perform an operation in there. Each time you run code within one of these, it's automatically retried if it fails as per your retry policy. And if this does succeed, if there's any failures later in the function, it won't be retried. We cache those results and save them. So it makes your function a little durable if you're familiar with that term or fault tolerant, you know, that's the the a la the, durable execution side of things. So, basically what you're doing is you're defining steps and multiple steps to build out your workflow. You can use normal if logic. You can dynamically define steps on the fly. You do not need to predefine them, which is really great. And that's the building blocks. But what's interesting is that there's more power to these these these basic primitives of steps that you have. So here's one example that might tie back to what we were just talking about, which are that you can pause workflows and wait for approval. So you can or wait for other data to happen, other events. So you could build things that run, process some, you know, some data, and then wait for maybe an approval or maybe something else in your system to be updated, like a draft or publish status or something, and you can resume your function. So ingest your code will not be running. Ingest basically holds on to the state of your function. And then when it gets that event, it resumes and restarts your function from where it stopped, doing some different kind of magic and whatnot. So then again, you can use that approval to dynamically, you know, traverse and and run different steps. You can also do things that are a little bit, you know, kind of outside of this this this thing that are that are kind of extending the power, which are making reliable calls to LLMs by offloading those AI calls to ingest servers. You can also do things like what Brian talked about, which are basically use other types of steps like sleeps or delays. So you can delay to an exact time and then resume the code then, or you can delay to an arbitrary number of days or hours or weeks, and then your code resumes. So these are kinda just the the basic building blocks. There's a couple more that I didn't get into. But if you can learn these couple primitives, you can build a lot of complex logic that also is very reliable outside of box. So, you know, going from here, I'll just take a step back and look at the architecture. And, Brian, how are we doing on time right now? We are we're no. We're golden. Can you see? Okay. Yeah. Yeah. No. I just yeah. No. We're beautiful. No worries. I I I did just wanna say, like, the hey, like, the the background jobs and and, like, some of the the queuing stuff was what got me in the door. But, like, the the durable execution piece, especially when you're working with, like, the LLM stuff, after you've you've used, like, one of these big models where you could just literally watch credits evaporate when a function fails is, has been a, like, a nice piece for my workflow. So, kudos there. Yeah. Thank you. Thank you. Appreciate that. And, yes, to the to the, to the audience, yes. Using, step dot a I dot infer, if you're running in serverless because with something like Vercel, it can offload it to our servers. So your function stops running. It's not consuming any any compute and it offloads. So that was an easy one to jump in there. Thanks for that question. Yeah. I'll jump into the architecture because I wanna show you how it works with Directus and how the system is. So basically, at at the high level, this is a little, you know, you don't need to know, like, know everything in detail. I'll try to zoom and see if I can pinch in a little bit. There we go. So the ingest system comprises a bunch of things that you might typically build or need to to execute this and do this yourself. So and there's much more beyond that. But the fundamental thing is there's an event API that receives data from anywhere, could be from your direct assistance or the webhook, And there's, something that consumes those events and basically, creates new jobs and enqueues them. It stores the state of those jobs. It stores the history of those jobs in case there's a problem or anything or you wanna observe that later, which we'll get to, then it executes those jobs. And what happens is the ingest knows, okay, I'm running this function and it toasted on your direct instance. Your code is sitting in your direct instance, and it invokes it via HTTP. So it securely just calls it when it needs to, invokes the minimum number of work, and then returns to ingest. And then if it needs to resume or continue, it will make other calls as necessary. So if you sleep, it goes back to ingest, then it calls your server again later. So this is the basic kind of principle of how all the systems work together and how there's a queue built into the system that is basically pushing work to your system. And that's why it's really key to have the flow controls. You know, you don't want to you wanna set something like throttling because you don't wanna overwhelm and knock down your system or your database in case you're processing, say, a huge backlog of 10,000 or 10,000,000, jobs or tasks that you need to do. So this is really key and also just shows that the code that you write is hosted on your, your machine. It's where you have it. You keep it along with your existing, direct as code base, which is really great. So I'll, I'll show you a little bit about, like, why do we have a database, what is this dashboard UI. So when you're running these long jobs that are happening behind the scenes, they're often hard to observe. I'm sure that some of you have worked in systems where you're parsing just logs on CloudWatch or something like that. You're trying to figure out what's the status of this job. Okay. Now we need to chuck it into a database so we can observe this. And you're trying to piece together what happened and where things failed. So we get to that one question in one second. So what we have is we have a dashboard that gives you visibility into the system. So this is the ingest dashboard and ingest cloud where you can see the the output or status of all the different workflows and functions that you've run-in your system, and then you can expand these to see all the different steps and what might have failed, what have might retried, the outputs of those steps, some metadata that you might pull through like, say, tokens in the model that you might have used to make an AI call or the time stamp when something started or ended. You also can look at inputs, say, okay. What piece of content triggered this? You can debug what actually happened and understand, did this work? You can easily cancel and retry these things or retry them in bulk. There's a lot of operations you could do, but this is the core of, like, this observability that you have into the system. So in also at the, like, the highest level, you can see broad system level metrics, like what functions have failed. Is my import data pipeline failing at a high rate? What what's happening? I need to look into this. So it gives you this kind of overall picture of how the system is go is going where it's it might be doing things always in the background. It's it aren't always automatically visible. And you can run many functions and have all different types of, functions and workflows like generating video transcripts or creating chat completions or daily digest. You you just get the single pane of glass of, like, understanding what's running in my system. And so the point of concurrent runs that, was asked in the, in the, in the chat, what happens is basically, like, ingest, can can execute multiple things at a given time. Depending on your plan and your configuration, you can limit how much concurrency you want. So you can control I only wanna execute 1,000 things at a time, or you might have multiple servers that you've horizontally scaled to handle more load. You can control individually what that is. You can also with the multi tenant controls can can control how many concurrent jobs a given user gets. So you can limit it like that in case you're building that type of system. But, ingest handles that because it it it does the it actually processes the the work off the queue internally. It can you basically declaratively say, this is the concurrent number of like, amount of work that I wanna run. So that's a that's a great question though. So, you know, basically, to summarize, like, this part, there's why are we here? We're talking about in ingest and direct us. Right? So tie back a couple things that I that I went over. It's the same code base that you're already running. It's the same server. You get to take right. Use the ingest SDK to define the functions right in the same code base with everything else that you're using with Directus, and there's no additional compute necessary. And, and, Brian, you got some? No. No. I was just jumping back in as we transition. Yeah. I'll, I'll I'll, Brian's gonna go in a lot deeper about this, which I'm which I'm stoked for all y'all to see because he's built some really cool things. And and some of the things again, this is fault tolerance, the async workflows, you know, so you get the confidence, automatic retries, and control over these processes. And remember that steps encapsulate these retryable logic. You can bring existing logic, import new libraries, whatever the heck you want, write any workflow. And one thing that Brian will go deep on this, I think, is super cool about how it works together is Directus, hooks are super cool because it kinda provides this nice link between the direct to system and ingest functions, but I won't spoil anything there. I'll, I'll pass over to Brian to kinda take it from here. But, and then we can go to if there's any other questions, we'd probably go to the the QA afterwards so we can get through some of these things. Some of the questions might be answered. Yeah. Definitely. So I pop the questions into the chat. We'll get to them if we can. I'll take the screen back, and we'll briefly cover what is Directus. For those, who are joining, who are not familiar, Directus is I I like to call it LEGO for developers where, like, everything that you need to build a digital experience, whether it's an app, a website, automatic content translation, which we're gonna do today, it it's all there for you. And you're just stacking these bricks together a lot like, what Dan was mentioning with ingest of, you know, these primitives that you have, and you could use them to build really powerful stuff. So Directus will give you intuitive UI, which we'll look at in a moment, a nice, like, admin CMS layer. You get instant rest and GraphQL APIs on top of any SQL database, and this is all self hostable. You can also run-in our cloud as well. And just a little tiny, fine print there of any, should be like most SQL databases. Right? Alright. So why Directus? Why ingest? Why together? A couple things. Like, Dan already mentioned, you know, the hooks that we have in Directus make this super easy to communicate with ingest. But more than that, ingest gives you that durable execution. To say being able to build these powerful workflows in code is tremendous. So the first question that, I was asked when I was putting this webinar together, and and this is from our team was, hey. Why why ingest when we already have automations inside Directus? So if you're still learning Directus, we have an automations module called Directus flows, and it is allows you to define a low code, no code automations. You know, when something happens, do this. These flows are great for short lived automations. Some of the things that that Dan showed in the previous slide where we have the syntax of, like, the step function, Directus doesn't have the ability inside a flow to wait for thirty days and send an email or to, wait for another event inside of a flow, without some complicated logic tying it all together. So flows are great for short lived automations or automations that that don't have super complex logic, and ingest is a great complement to flows. And, you know, in the future, I could even see us having an extension to trigger ingest, functions inside a flow. But, as we get into flows versus hooks versus extensions, as as Dan mentioned, Directus flows underneath the hood once you pull off the the mask here, the the Scooby Doo, I kinda mean, is just using Directus hooks. So, Directus hooks, if we pull up my browser, which is always dangerous since I'm using Arc, Directus hooks are just code that fires whenever certain events occur, and you've got a lot of different hooks at your disposal. This is using custom extensions, and we'll kinda cover that in just a minute. But at the core, hooks are allowing you to say, hey. When this event happens at Directus, do this. And, we'll we're leveraging these hooks extensively in this integration. So there's filter hooks, which happen before the event is emitted. So if I wanna run some code to either modify the payload before an item gets created or potentially stop that from occurring, I could do that via a filter hook. And then the ones that we're leveraging today in this workflow are gonna be our action hooks. So when after an item gets created, we wanna do something. In this case, it's going to be sending those events to ingest. Alright. Any questions on hooks before we kinda dive into this? We've also got, like, an endpoint that we're gonna use, and I I just wanna take a moment to touch on one of the most important pieces of Directus is this extensibility. So if you go into our documentation, which is a a great starting point after this webinar, take a look at the extensions overview. You could customize every piece of Directus. So you could customize the interfaces, like how the data is displayed inside the studio. But the parts that we're leveraging today are API extensions. The hooks, endpoints, operations are within flows, and, of course, bundles are how we put all these together and distribute them. Alright. So how do we make Directus and Ingest play nice? And this was the part that, I did the heavy lifting on with some help from Dan and his team. You're still blown away by how easy it is to write ingest functions. It really just kinda gets out of the way. And and once you guys go through this process and set this up and and don't worry. Wait. There's more type of moment here. We're we're gonna give you the whole code base as the bonus after this, so don't worry about taking notes or screenshots or anything like that. But, once you've got this set up, then you can build really incredible stuff, with the incredible pace, I would say. Alright, so I'm going to open up my code editor here and we're gonna take a look at the first piece of the puzzle which is our environment setup. And for Directus projects this almost always starts with a Docker composed file. So this is the pretty standard boilerplate Docker composed from, our documentation. I've I've done just a little bit of cleanup here and extracted some of these things out to environment variables versus, just inlining them. And a couple things that I want to note, when you spin up Directus, especially, like, using our standard Docker Compose, which we're using Postgres for the database, Directus will create this, this folder structure for you with the database, the extensions. This template is is just for applying the schema that we've got for this example, but, it will create all of that for you. And for a cleaner DX, I've basically just extracted this custom Directus extension out to the root in a queue folder. So this entire code base, I've I've gone through the trouble of trying to make comments, to to add a little bit of what I was thinking as we went through here. But all the the rest of this is pretty standard with the exception of a different port. I'm sure Dan can relate to this as you're developing. You've got, like, 35 instances of your product running on your local machine at one given time, so you gotta switch these ports over. The only addition here is the ingest dev server. So the ingest dev server, they've got a a docker image that you could pull. You can also run this via NPM too. Right? Dan, a node? Yeah. Yeah. You can. You can install the binary via MPX or NPM. Perfect. Perfect. Yeah. So, you know, we highly recommend Docker if you're working with Directus, but, you know, you could run this, ingest dev server using NPM if you prefer. But I just wanted to point this out like we've got it pointing to our direct assistance, and this is running on port eight two eight eight. The dev server is amazing for running locally. It's almost, identical to the ingest cloud dashboard, with a few exceptions. And, you know, one thing I do wanna call out, and I think Dan already iterated on it, is your ingest functions, unless you're doing, like, the the LLM infer that that Dan mentioned, these are all running on your same Directus instance whether you're using the dev server or ingest cloud. So I just wanted to call that piece out. Now for the environment that we've got set up here, you could see when you go to production and just has event signing keys or event keys and signing keys so you could keep, things very secure. For purposes here, we're just running, on the dev mode, keep it lightweight, make it easy to iterate. One thing to note, when we get into the translation workflow, we are using DeepL, so I've just got that environment variable. So when we are configuring a Directus extension, Directus has a extensions SDK that makes this process super easy. I just bang in this command into my terminal, n p x create direct us extension. Make sure you use the at latest tag to to pull that. And what you're going to initialize, I'll just show you kinda what this looks like, is a bundle extension. MPX Directus create extension at latest. This will ask you to install, and then we will go through and choose a bundle. And this is where my Internet just totally craps the bed apparently. Too many connections at once. Okay. Yep. So we scroll down, we hit a bundle, and a bundle is just a collection of these extensions. Now once you initialize that, it will look something like this. Where are we? So I've I've got this in our queue set up. You could see here we've got our Directus extension. It is a type of bundle, and then to add extensions to that bundle you run npm add or npm run add or p npm add, whichever package manager that you're using. So as far as the ingest setup here, let's dive into what that actually looks like. And, again, we're gonna give you access to all of this code. So at the high level here, we've got a function folder that stores all of our functions. We've got some hooks that we'll take a look at, and then we have this ingest client. So how do we set this up? How do we make sure it connects securely and plays nice with Directus? We are pulling in from the ingest package, and we're gonna skip this little middleware piece, but this create ingest client basically just returning a new ingest client that we can use to invoke these functions. Now the middleware piece here is rather important. Basically, all we're doing here is some dependency injection to use direct to services, like creating an item, updating an item, sending a notification to our user. So that's an important piece of the puzzle. There's a a couple things like a helper function in here to set the directest context so we can leverage that. And then we're just basically using a singleton pattern for this ingest client so that, you know, we're we're using the same client over and over. Setting this up and, you know, ingest needs to be able to serve those functions. So, in that we've got a endpoint that we define. So this is a custom direct us endpoint. You know, you can see some some standard, imports here. This is where we're actually going to import those functions. We'll show that in in just a few, but we're basically defining an endpoint. The ID here is gonna be the route that this gets served on. So, I'm using ingest. If I go to local host eight zero eight eight slash ingest, that's where my functions will be served from. And you can see here when we're defining an endpoint, we get the express router instance, and then we get the direct us context. And that is what we are calling here or or what we're passing when we set the directest context so that ingest has access to those directest services. Very important piece. If you're setting this up on your own, don't, don't forget that part of it. Last but not least, we'll basically just serve the ingest client here. You can see the syntax we're passing in the client that we've set up, and then we're giving it the functions to serve. Now the last piece of making these actually talk to each other is the direct us hooks. Right? So, we've got our hooks set up. The famous handler. I see. Not sure what you mean by that, Bazar, but, I we can unpack that in the q and a for sure. Alright. So hooks. Right? Again, we're just defining these actions that we want to run whenever a certain event occurs. And I was I was using I I won't say I was using this wrong, but Dan definitely gave me a level up, while we were prepping for this webinar. So, breaking these down. Right? We're using the action hooks inside Directus. So I only want to invoke a ingest function after a certain thing occurs, not not necessarily before. So if we break this down, whenever a post so we'll get into our data model in a moment, but whenever our post gets updated, we are going to send, ingest an event that our posts were updated and we're gonna pass the the event from Directus and this accountability object from our context, which is basically this is the user permissions and, like, the user ID and things like that. Is this user going to have access to actually update that post, after we do the translations that we take a look at. So one of the things that I I do wanna recommend as you go through this, mirror the event names inside your ingest functions from your direct us, events. So you could see this syntax here. Basically, I've just added direct us in case we've got some other service that we want to send events to ingest from. But here, I've just mirrored the syntax and the reason why, and Dan schooled me on this, is it makes it easier to debug and trace these things through your application, especially if you want to trigger multiple ingest functions on a post update or when a post gets created. Alright. So the last piece of our puzzle. Right? We've added hooks. We will create our ingest functions, and we'll take a look at that. We'll share this amazing slide deck at the end of this as well. But we've got a full guide if you've already got a direct us project and you just wanna walk through integrating this. We've got a full guide that we've written on our documentation for you to check out as well. Alright. So, Dan, I'm gonna bring you back. This was your diagram. You know, maybe maybe kinda touch on how this works a bit. Yeah. Yeah. So I think, Brent really laid it out really well here in that. He just showed where with the extension and direct us hooks, that's where we're we're hooking to these actions and just broadcasting basically events over to ingest. So So those events, are basically declaring what happened within the direct to simple, system. And then our ingest functions, will declare when they run. Right? We we saw that before when I walked through some code. You declare when something happens, this is when I wanna run. So instead of directly invoking jobs, this allows you to decouple and also build things independently and do things like fan out, as Brian alluded to. So the ingest system basically understands what your function, your workflows, want to do when, and it basically routes them through back to to, to your server. So at the highest level, that's how we're stitching together the hooks with events ingest and then back to the back to the system to to call your code. Beautiful. Beautiful. I I love the love the diagram, and it like, this was a a big reason why we shifted to this format to make it feel a little more interactive, a little more fun. So all that said. Right? Now to the main event of, like, hey. We we've got this set up. These two are talking together. How are we going to build this advanced workflow? What are we gonna build? So we're gonna show you guys content translations that are automatic and, of course, durable, and I will say totally unbreakable because I know I've written a lot of the code, but but very, very durable, and, like, an incredible workflow for, anybody on your content team that needs to handle translations. So if you've worked with content translations and other systems, this GIF probably holds true. It is so much fun, probably involving, like, a lot of spreadsheets, a lot of working back and forth with either different contractors or different team members. Directus makes that whole process a lot easier. We've got a beautiful interface for it, and, I think Matt on our team is gonna kill me for saying beautiful intuitive interface. But let's dive into why this why this sucks. Right? We're gonna do this manually as kind of the first step that usually involves those spreadsheets, then we can graduate to APIs. The one we're using today is DeepL, but, you know, Chat GPT and some of the other LLMs have gotten incredibly good at translations. Kind of a toss-up there between speed, DeepL, I've found is is really quick, and, it seems to be highly accurate. But as you get into that, especially when you're translating a lot of content, you run into, hey. This is gonna take a while. And as Dan alluded to, I've already ran into this, like, 35 times through the, building of this thing is, oops, we hit the rate limit of the APIs or one of those API calls fails. What happens? Right? Then we lose not only our data, so, you know, we just hope and pray that there's some logs that we can go back and get some of that data, from their side or, you know, just evaporates. So, this is what we seek to fix inside this workflow. And before I show you Directus and Ingest, I want to take a look at our our data model at a high level. Now, this is a simplified example, and I guarantee you when you go to production, you've got a lot of content that you're gonna translate that it probably has a more complex structure than this. But, I do wanna give you this, like, again, we're gonna give you the whole code base. You could take this and run with it, and so this starting point will make it easy for you to adapt to those, those more complex models. At the high level, we've got a post. The post has a title, a slug, and some content. Now, there's also a languages collection inside the Directus instance. Whenever you use our translations interface, we will create that for you if you don't have it already. The format is pretty standard. We have a code like in US, the name English direction, and then one of the additions I've added here is just a DeepL translation code because their API, doesn't necessarily follow the standard ISO codes. And then for each, post that we're gonna translate we have a relationship to a collection or a SQL table called post translations. So within that we have a pointer back to our language and then we have our title, slug, and content. So at a high level that's the data model we're working with. Let's kind of pull this up and and see what this actually looks like. Alright. Dan's gonna laugh at me here. I'm sure to mess up Arc here. Still haven't mastered the side by side in Arc. So over here on the left, I've got our, ingest dev server up and running. If I go in here, you could see I I don't have any runs yet, But over here on the right is our special themed version of Directus just for the ingest webinar. I really dig the the black and green vibes that you guys have on the website, Dan, by the way. Same. Okay. Nice. Alright. So, if we take a look at this, right, this is a beautiful gosh. I gotta stop saying beautiful, Matt. Intuitive interface for all of our content editors. We could see the default language up here at the top. So we're writing this in English. We're gonna pass that to the DeepL API. When we send this translation over, you know, I could set this and and write in whatever default language that I want, and we'll handle the translations. Directus gives you this beautiful side by side view for your translations, and we can see that all of these different languages in our system are fully translated. So, the flow that we're gonna set up, and I'll I'll walk through this in a minute, is we're gonna take any post anytime a a change happens or we create a new post, we're gonna fetch all the languages that we have that we wanna translate content for, and we're gonna go and actually translate that. Beautiful. Alright. So let's take a look at this invincible translation workflow. And usually I do this kinda in code, at the top of my document. I break this down. Since we did this format, I wanted to make it a little more visual, and I'll still walk you through the actual code for this. So at the start of this, what will happen and what I left off of this diagram is the user event. Right? User creates a new post, and then that kicks off the process here. So we're gonna send the post ID, we call it a key, to this workflow. And the first step is gonna be normalizing these event keys because the Directus, event emitter, like if we do post dot update we get an array of keys versus like post dot create we just get a string for the key. So we're gonna normalize those keys. We'll check and see if relevant fields have changed. So if nothing has changed on an update, doesn't make any sense to actually build these translations again. If something has changed, we're going to retrieve all the translations for that existing post, and that's an important piece. Again, you know, we don't wanna translate content if we don't have to, if it hasn't changed. This is gonna be using, Directus item service, so we'll dive into the code in just a moment. We have, we'll get all of our available languages via the direct Us item service again. We'll use both of those to build a list of translation items. So what do we actually need to translate? We'll fire those off using, the ingest step functions to the DeepL API and we're gonna do that in parallel and we'll also loop over those so we get that nice tracking and observability. We'll log any errors and then when we get that back we're going to upsert into the database and potentially notify the user. So that's the flow at a high level. Let's take a look at the actual code. And, Dan, if I gloss over something or I miss something on the ingest side, definitely call me out for it. Will do. Perfect. Alright. So, again, you can see here, this is, these are the two hooks that we're using to actually manage this workflow. So whenever an item gets updated, we send ingest, hey. We updated this, or we say, hey. We created this. And then our function looks like this. And, this is not the shortest function I've ever written, but definitely not the longest. I'll say that. So, again, an outline of the translate workflow, what I found is super helpful for me is just to quickly outline the logic at the top of each one of these. And if we go through, we could see that we're importing that ingest client. We've got the DeepL API, just their node client that we're gonna use, and we've got some types that we're pulling in to, make the TypeScript compiler TypeScript gods happy. So, we've got some translatable fields here. Again, it just defining some constants. These are the only fields that we wanna translate. You could easily set this up to be dynamic, and just defining some of the the DeepL params that we're gonna use in our API call. So when we get into the meat of the ingest function here, you can see we've got an ID. We've got our name just describing what this is. And then, you can trigger an ingest function, on any number of events. So the standard syntax here is an object with an event property, but if you want to have multiple triggers for this function, you could just pass an array of objects. Great. So onto our handler function, we're getting the event and the step, which is standard syntax and Jest is giving us that. And then we're also pulling out this direct us context that we added through that middleware. So through that, we're gonna get our services, we're gonna get our schema, our EMV variables, which is gonna give us the DeepL API key. So the first step in this function is normalizing those keys. You could see all of that code here. What's notably missing is the step functions that we saw earlier. And originally, I had these wrapped, but, a a nice little tip that Dan gave me is if this, the code that you're running doesn't actually mutate external state or depend on external state, you don't necessarily have to wrap it in a in one of the ingest step functions. Next up, we'll get our payload from the event. We'll check that to see if we have any translatable content included. And if we don't have any translated translatable content, then we can just return early. Right? Next, we'll get our translator. So this is the DeepL client. We'll get our schema from Directus, and we're gonna init these item services. So this is how we talk to the database, on the Directus side of it. There's just a little helper function down at the bottom of this file that that makes that a little less verbose, and then we run into our first step function. So here, we're going to get the current post, and this is just a, a service call by the post service. So we're gonna read by the query. We're gonna look for the post with the ID that we were passed in the event and return not just the root level fields, but also the translations that are attached to that post. So if we look at that in, like, the Directus UI to give you an example, we're not only gonna fetch this information, we're gonna fetch all the individual translations. And that is one of my favorite features of Directus is, being able to fetch the data that I need in a single API call. So I could go deeper into this if I wanted to, you know, three, four, five different levels. Eventually, you'll reach a max where you you don't wanna go, but, depending on the data that you've got, using these asterisks as a wildcard is incredibly helpful for local development. Now, going further, again, we're just going to have a step function that, will cancel this whole thing. If we can't get the languages from Directus, we don't know what to translate. And this was a a last minute addition. And, Dan, could you talk about, like, the retry logic just a little bit? Yeah. As Ingest handles, errors automatically and does retries, some errors you might anticipate and say, this is a non retryable setup. So if I'm missing the API key, might as well not retry it because it's just not gonna work. So in that sense, you, ingest allows, includes a custom function, basically, which allows ingest to say, you know what? Let's stop here and not retry anything else. So that's what Brian is using here where this is a nonrecoverable error kinda situation. So but, typically, you can throw errors to customers and whatnot, and, those will all be retried automatically. You can even catch them and handle them however you want as well. Beautiful. Beautiful. Thank you, Diane. Alright. Let me try to find where we were at. Okay. So we've got our languages, then we move on to the next step, which is actually building our translation list. Excuse me, guys. Dealing with six not six kids, but sick kids here at the house. Always a struggle. So here in this step function, we're basically just building up a list of the translations that we want. Right? So we're looping through all of those, posts that we've got, making sure, you know, we've got the fields that we wanna translate, and then we're basically building an array for those things that we'll we'll pass to the next step as we scroll down, which will be actually translating all these items in parallel. So, here, we're using promise dot all to fire these all at once. And, again, I think hey. Dan, you mentioned this, like, it like, defining a unique ID for these was not not strictly necessary, but, maybe talk about that for a minute if you don't mind. Yeah. Yeah. When you're executing in a loop, it's or in something like here where you're paralyzing with promise dot all. You don't need to. Ingest basically takes this and under like, understands, like, internally. You could check out though the SDK as well if you're curious about how it works. Basically, it takes the step ID and and and appends some sort of, iterator and creates a hash. So, automatically, it, make sure that if two steps have the same ID, it is it will, like, not they won't overwrite each other conflict. But for the sake of debugging, which I think is a great idea of what Brian's done here is you can dynamically set these keys, in your in your loop, which makes it easier for what Brian will show in just a minute, in the UI. Yeah. Great. Alright. So we go through we send all these to DeepL. We get all of those things back, and then, the final two steps here, we're basically, again, using promises to do all of these upserts. So, when you're using these services in, like, inside the actual directest, like, API endpoints or hooks, there is this upsert, which, if anybody on the Directus team is listening, would love to have the upsert on the SDK. Just put in a nod for that one. But, this makes it super easy to, upstart content. Super simple. Like, hey. If this ID doesn't exist, we're gonna create the translation here. And then last but not least, we're we want to notify the user, hey. Your translations are done. So you can go check them out, because this is running in the background. Awesome. So that's the flow. Let's take a look at the UI. How does this work? Let's, what do we what are we gonna translate? Dan, do you have any thoughts? I don't know. You hit someone good yesterday. It was just, like, hello from Hello from Dan and Bryant. Alright. So we're going to throw this in. What do we have? This is an amazing webinar. This is not me. This is somebody in the chat. We're not saying we're amazing. But alright. Here we go. So now what will happen as soon as I save this, over on the left, we should see the event being fired to ingest, unless I have done something totally wrong. And and just to prove that I'm not pulling any switcheroo on you guys, we don't see any translations there. So immediately after I've created that new post, now we could see, the ingest dev server and I get all this observability, all these steps that ran within the actual flow. So we get all of those steps broken down and you can see here we've got the individual steps within that loop that we use those specific IDs for. And as I go through here, you could see the output for each one of these. So, I don't speak Russian. Not sure if you do, Dan, or not. No. So there we go. So we could see here's the actual content that's that's getting translated. There's the slug. There's the title, etcetera, in all the different languages that we have set up. And this it's like seeing this together was, like, when it really hooked for me of, like, okay. Great. All this is running in the background. I get all the observability. So, like, when something inevitably screws up, which for me is often, if you catch any of the hundred apps hundred hours episodes. But and, like, having all this at your fingertips is incredibly powerful as a developer. And being a a developer that has all this and is able to build a flow like this that will do all the translations for your team automatically, turns you into a hero, %. So that is the flow. Dan, any anything to add before we kinda jump into queue? Yeah. And what's really nice is I think just, you set it up earlier with Docker Compose, but with everything running on your your machine, you can work and iterate quickly on this flow and not have to worry about, like, conflicting with, you know, bumping into shared resources. Like, if you're using something like SQS or something like that on Amazon, you need to provision those things. It becomes a little bit of a nightmare. And what also is nice here is, like, you know, Brian's code works perfectly as we see. It's all green. But if there was an error, you'll be able to see that span go red. And what's nice about the dev server flow is that it saves the input of your function. So if Brian were to go back to his code base, fix that bug, save it, the dev server would basically you know, the his direct to server would would refresh, reload, and you could click rerun in this dev server, and it would just rerun the function again. So if he, you know, if you hit rerun live, it should just it should just work. And so this gives you, like, this fast feedback loop. So you're, like, in this kind of hot reload situation where I'm working on my I'm tweaking I'm tweaking. So instead of you having to, like, go to the right, manually click a bunch of buttons in the in the Directus UI, you can have a fast feedback loop. So, like, do it once in there, keep going, you know, tweak the output, look at things, tweak maybe prompts or different things that you might be using, to to create this. So at least, like, this allows you to kind of, hopefully move a lot faster when you're building these these things that can be complex. But, yeah, I can't stress that enough. Like, the the speed at which you could iterate with the dev server and, direct us being able to, you know, go in and quickly model a feature and idea, and then also being able to, like, prepare that for scale using Ingest is, again, a great pair. Just, works really well together. Alright, guys. So if we move to our amazing slide deck, it is now time for Q and AO. If you guys have any questions in the chat that we wanna take a look at, Dan, do you spot any that we need to I did see a couple questions I could talk to. The first thing I could do was a couple questions on self hosting for Jess side because we we know we can, self host direct us. Right? And I'm sure a lot of people do that as well. Yeah. Self hosting is incredibly popular for Directus. So I'm sure I'm sure there's a lot of people in the audience that are very curious about self hosting ingest as well. And you can self host ingest. The code that, Brian showed that runs in the DevServer, that binary is the exact same binary that you could self host. You can also you can run it in a very lightweight version or you can offload. Ingest has queuing and state history involved like that it that is backed by. So when it's running locally, it just runs in memory because it's low volume and it's very simple. But when you self host it and you deploy it into your own cloud or wherever you want, you can hook it up to an existing, instance of Postgres or you can also, plug it out and and connect it to a dedicated Redis, maybe running in another container or something like that. It can handle a little bit more scale and handle, like, you know, restarts of your ingest system. So, you know, you can self host. There are certain things that aren't in self host yet, like, some observable observability and metrics. A lot of those systems were built in ingest cloud. We're gonna be, you know, kind of bringing some of those things down to, to open source as well. But now, you know, all the key features, all the throttling, flow control, defining functions, are all are all there. So you can run that wherever you want and and and self host. Amazing. Got it. So I on the directed side of it, of course, like, you could self host, we've got a BSL license, which basically, a free to run for anybody under under $5,000,000 in total finances or revenue. So So if you have questions on the license, definitely reach out to our team about that. You can do that through the website. What other questions do we have? I think there was one that I saw that, was super helpful. I like, comparing I like, when I first came into Ingest, I'd heard of Temporal, worked with it a bit. I can't find this one in the chat now, but, like, how do you guys stack up against temporal, Dan? Yeah. Yeah. We that's a I think it's a great question. You know, especially with the term durable execution, temporal's, you know, in its essence, describes itself as a durable execution engine. So it is dead focused on a, what we believe is just like the durable execution of the function. Actually, the logic. Right? Is when something fails, it does checkpoints and it and it retries. So in that sense, there there is similarities. Right? But we consider that durable execution is just like a means to an end. Right? It is it is a feature. It is not the whole platform. So what ingest really, layers on is a couple things. We have an event based approach as Brian showed with the hooks, so you can fan out and you can replay and do more things, have a little bit more flexibility with events. And if you're someone who likes events, like, I'm sure that that resonates. And if you haven't, give it a try. And all the flow control and advanced queuing is one of the things that is unique to ingest and, is is in this self self hosted open source version as well is all this reliable flow control. So when people are building these systems, often you don't just wanna execute a job and run it to completion. You need to manage how fast it's processing, this job, you know, how many times per minute you might run something. Maybe I wanna delay, I wanna debounce something, I wanna rate limit this job, run things in batch processing. Maybe just dynamically say, when there's 20 posts that have been published, let's execute this batch instead of saying, you know, let's just push these 20 items in one big blob. So there's a a a lot of differences in that sense of, like, what we've built around. And I think one of the things that also is true with the ingest SDK is it doesn't mess with the runtime. If you have used temporal, you in the TypeScript or JavaScript, you know, SDK, temporal does something where they kind of, like, wrap your logic and certain things like random doesn't work. So there's some gotchas that's like, I don't know what's going on with this runtime or it's going to wrap some of your things and you might not it might just not be native code, but we've fundamentally chosen to build ingest, say, anything that you're using works. It's very easy to look in the source and see where things are running. It's very it aims to be a very thin layer, so there's no weird kind of, like, gotchas and things that I need to know. And, generally, like, you know, ingest is very dynamic and defining steps and everything is very fluid. So there aren't, there's a lot of friction to, like, the rigidity that you might find with other solutions. So I those are just a few things. There's many more, that you could check out, like, on our site and whatnot, but or ping me afterwards if you if you if you're curious. Yeah. And I'm not sure that you could say this, but, like, the the syntax that that I found, like, writing the same sort of thing in in jest is dramatically, easier, and it just jives with the way that my brain works versus, like, some of the verbosity and, like, just how temporal structures things. So, do we have any other questions? I I guess that's gonna sting a bit if we have temporal onto one of the partner webinars as well. But I, what is called? I'll mention one someone asked a question about, retries. What happens when something hits hits max retries? The function will be declared as failed. And what, ingest also allows you to do is basically say, you know, if there's a complete outage, say, DeepL's down for twenty four hours or or an hour, all your functions are failing and all the retries are exhausted, you can use ingest to say, select, you know, between these two time stamps, anything that failed, replay them so you can do bulk retrace, retries, we call replays of, of those functions. So you can recover from systems because we persist all the inputs, you know, if that's helpful. Definitely. And then, I think probably the last question before we wrap up is, can you run webhooks in the ingest server, the dev server? You, you can. You'll have to configure your webhooks in in in in I guess it depends on, like, how you're how you're running things. So, Ingest Cloud, it has webhooks and and transforms. And locally, you just need to write a little logic to just, like, wrap that transform to simulate what, what is happening. We'll be bringing in in the future a synchronization, some thing with Cloud to make that a little bit more easy and bring them into the DevServer. But fundamentally, the webhooks are just the same API endpoint that ingest dot send is using, that Brian showed in the in those, in those direct hooks. So it's just sending JSON payloads, and that's what, what webhooks primarily are. So, it's, it's pretty easy to to utilize it and build around. There's also a few different docs on our website about that if you're curious about webhooks. Yeah. Awesome. I don't I don't see any other questions from the team. We're a little bit over, Dan, but I I I man, I appreciate you coming on. Like, before we kinda get into the awkward outro, I do want to just reiterate, like, for anybody still here, if you've registered for this, we'll hit you with an email with all the links to the repo, this amazing slide deck that we put together. So don't worry about that. That'll be coming in the next, couple hours, so just be patient. But wrapping this thing up, we we hope this was helpful for you guys. Please send us your feedback. We really enjoy doing these webinars, showcasing other tools and, things that that help you build faster. And, Dan, thank you for joining. Really appreciate the, you know, the collaborative effort on this thing. Learned a ton about it. And, for everybody else who like, next steps for you guys, what what does that look like? You know, you wanna sign off a little bit? Yeah. Yeah. Thanks for thanks for having me, and, thanks for doing all the leg work. You wrote all the code. You built all of it. So it's, it was pretty awesome to see this, and I think it's a great example. I think if you really wanna figure it out and you want some more, there'll be the follow-up email. Brian also has a really great tutorial that he put together, which has a lot of detail. It's pretty incredible, but definitely check that out. I'm sure that'll be in the follow-up email. And, then also, I'd say, like, if you just wanna tinker a little bit, go with ingest, go check out, one of the quick starts on our docs and just go tinker a little bit with the dev server, build some different things with some dummy code, and then kind of, that's easy way to just kinda get started, and then you can kind of dive in for the in-depth stuff with Directus using, using Brian's tutorial that he wrote. So thanks for everybody joining. And if you ever have any questions, we, we do have a Discord community. You can find the link on our site, or you can always reach out to us, contact us anytime with ingest dot com. That's 2, by the way. Just always remember that one. Two n's. Yeah. Perfect. Alright, Dan. Thank you. Thanks for the audience. That's a wrap. We'll see you all. Thank you.","019ddedf-e66c-4156-89ff-380d9f7e15a4",[974,975],"42e32b28-830e-4966-b849-176902ae12f7","c812e8dd-f9fe-4ee6-bef4-19be62f3427d",[],{"year":978,"number":979,"id":980,"episodes":981,"show":985},"2024",42,"181a77a7-65c5-46b3-9e4f-474acc00436a",[982,983,984,963],"bb508a73-7947-4be9-8493-a226861cfe7c","b0159a9d-73b5-432e-b2a4-b3f606f8ba96","65956c5e-17ae-467d-8ae8-c2dd8cfcc2ab",{"title":986,"slug":987},"Enter the Workshop","enter-the-workshop",{"id":692,"slug":989,"vimeo_id":990,"description":991,"tile":992,"length":993,"resources":12,"people":12,"episode_number":11,"published":994,"title":995,"video_transcript_html":996,"video_transcript_text":997,"content":12,"seo":998,"status":19,"episode_people":999,"recommendations":1004,"season":1005},"march-2025","1063229947","Join us for The Changelog, taking you through the month’s Directus updates including product updates, new content and community contribution highlights. This month includes new extensions such as an SEO plugin and field comments module, a new episode of Unchartered Territory and What's in your Dock from Bryant.","f6439d36-d8f8-45f9-91f1-8dd032f99b32",34,"2025-03-12","March 2025","\u003Cp>Speaker 0: Alright. Hello, everyone. Hopefully, you are having a great day. If you're new here, I'm Beth, and I am here to take you through what is new with Directus in March. We have a really great show for you today.\u003C\u002Fp>\n\u003Cp>We have a lot of Bryant. We're very lucky. He will be taking us through what his working setup is in a special edition of what's in your dock. We also have a brand new episode one of uncharted territory, a brand new director's TV show. But first, I have an invite for you.\u003C\u002Fp>\n\u003Cp>So if you are free on the March 20, we are hosting a workshop with Ingest called building advanced content workflows. And in it, you can learn how to build an automated translation system using direct us in ingest that you can implement immediately. It is on the March 20. And if you are in our Discord, you can see it with the little events tab on the there. First of all though I'm gonna take you through to Carmen who has got the product updates for you, for this month.\u003C\u002Fp>\n\u003Cp>Speaker 1: Hey, everyone. Carmen here to tell you about what we've shipped in Directus in the last month or so with eleven point five point zero and eleven point five point one. We've added numerous small improvements and bug fixes. And in this section, I'm gonna highlight two of these to draw your attention. Firstly and importantly, we have a breaking change to bring to your attention.\u003C\u002Fp>\n\u003Cp>There is a changed error message when a flow condition operation fails, And we've added partial Prometheus supports for your monitoring needs. Currently, we support the pull method via the metrics endpoint. Metrics generated can be controlled by a CSV of services to observe using the metric services environment variable. So that's what we've released in the last month. Be sure to keep up with releases on our documentation at directus.i0\u002Fdocs.\u003C\u002Fp>\n\u003Cp>And if you have any questions or feedback, be sure to join us on Discord. This month, we've added two different tutorials to our documentation in four frameworks each, Next. Js, Astro, SvelteKit, and Nuxt. So, yeah, we've got eight new articles. These are on implementing multilingual content using Directus and the framework and rendering dynamic blocks using framework.\u003C\u002Fp>\n\u003Cp>You can check them out at directus.i0\u002Fdocs forward \u002Ftutorials forward slash getting dash started.\u003C\u002Fp>\n\u003Cp>Speaker 2: Hello. I'm Mike, and I'm from the marketing team. I'm gonna be here to talk to you about all the latest extensions that have been released in the Director's Labs org on GitHub. Just as a reminder, this org is where we publish regularly new experimental extensions with quality of life or feature improvements that could be used. First, we're gonna be talking about the field comments module.\u003C\u002Fp>\n\u003Cp>First, what we need to do is connect it to a collection. In this case, I'm gonna attach it to our employees collection. We can enable it on all fields by default, or we can select specific fields we wish to be able to comment against. When we add an official record, so for this, I'll be adding myself. I am the developer experience engineer.\u003C\u002Fp>\n\u003Cp>I do things with extensions. You'll notice how there's no additional information or anything here. Once the record is created, we can go back in, and you'll notice how there are now additional comments sections that we can add. So in this case, at admin user, is this person actually the dev ex engineer? Once we submit this, we'll be had a comment to that field, and it'll tag accordingly how many comments are available.\u003C\u002Fp>\n\u003Cp>This also uses the existing commenting, feature of directors. So if you tag somebody, etcetera, they will receive a notification about it. And as an aside, any field level comment is completely isolated to its field and doesn't appear in the item level comments. The next extension we're talking about is the SEO plugin. We can configure this quickly with templates for both the title, the meta description, and we can enable additional functionality such as sitemap controls for how often things are going to be updating our sitemap, search engine controls for things such as no follow and no index, as well as adding the social media image for graphs.\u003C\u002Fp>\n\u003Cp>I'm going to just quickly skip that one so I don't have to attach an image. Add this to\u003C\u002Fp>\n\u003Cp>Speaker 1: our data model. Once we are\u003C\u002Fp>\n\u003Cp>Speaker 2: back in our data model, you'll be able to see that we have our content as well as our new fields for SEO. So, okay. Alsmore is the DevX engineer. Mike those things with extensions for directors. And you'll notice how we're now getting a lovely preview with some help controllers, so we don't want this to be followed, but we do want it to be indexed.\u003C\u002Fp>\n\u003Cp>And now we can save this. This extension that we'll be looking at is the WYSIWYG add edit file link. So if we create an item, this is the selected field. There is a new add edit file link button. So if we have some content and we want to insert a file, for example, we can grab off my desktop a text file, add it, the transcript of the meeting, and we can have it open in a new field.\u003C\u002Fp>\n\u003Cp>This image, preview doesn't work as it's not an image, but we add this, and we'll have the transcript to the new meeting as a file. We can also then go in, revise it, and change anything. The complete just as if we were editing the normal link, we can also and just to double check, we can also upload an image. Director's logo, which will have the preview and allow us to insert it with the download option. And they look like so, with the complete URL through to the assets library locally.\u003C\u002Fp>\n\u003Cp>The final module for this month is going to be the migrate module. Here you can see I have the migrate modules interface, and the Directus instance I'm going to send it to. This is, my local instance, and this is a remote cloud instance. As you can see here, there is no content, no collections available, but we have a collection available here. So I'm just gonna quickly grab a static token, as the module requires a authorization token to interact with the client.\u003C\u002Fp>\n\u003Cp>Don't worry. I'll recycle this afterwards so that nobody can use it. Save that. Going to grab our domain for the client. Now we can really check to see if it is compatible.\u003C\u002Fp>\n\u003Cp>And you'll see here that there there is a difference. My local is running 11.4, and the remote is 11.51. We can force this through. I'm going to now. Or we can use this as an opportunity to run any upgrades, etcetera, to make sure everything is aligned, especially if there is a major difference like version 10 or any of the larger schema changes internally.\u003C\u002Fp>\n\u003Cp>So we're gonna force this, and we don't want to copy over the users because I already have an admin user over in my remote. So we are now going to start the dry run, and it's going to create a snapshot and create collection options. This may take a moment. We're also able to preview every one of the files created as part of the migration, all the individual components that make it up. Now to actually do the migration, we apply our key and our domain.\u003C\u002Fp>\n\u003Cp>We are once again gonna have to force it through, and we remove the driver and tag. This way, it's not just building it and running locally. It is pushing up to the remote instance. So just to validate again, we don't have a collection here. I'm gonna start the migration process.\u003C\u002Fp>\n\u003Cp>And now we can see that the migration is complete, but it has skipped the users as we already have an admin user. Now if I reload the content, we will have our employee collection with me, the developer experience engineer, with all the relevant information internally. The only thing that's missing is this instance will now need to be configured with the correct extensions for it to operate as expected.\u003C\u002Fp>\n\u003Cp>Speaker 0: Alright. Hopefully, you found those interesting. Mike, I don't know if you're in the chat, but what is your keyboard? It's clicky, it's a chef's kiss. Very nice.\u003C\u002Fp>\n\u003Cp>It's very satisfying. If you are planning on using anything that was just mentioned or you have any questions, let us know. A few of us are hanging around in the chat so we can see them and answer your questions. Or if you are catching this on demand, let us know in Discord, and we'll see what we can do. It is time for the first of Brian's sections.\u003C\u002Fp>\n\u003Cp>So he is gonna be talking us through what is in your doc. And if you haven't caught that on the director's TV already, we have a bunch of cool people with very interesting jobs tell us what they are using in their day to day setup. And so here is Bryant's. Hey.\u003C\u002Fp>\n\u003Cp>Speaker 3: Hey. Bryant here from Directus, and I I guess today I'm actually breaking down what's in my doc. Now, our CTO, Ryke, did one of these videos not too long ago and there's a lot of overlap in our setups. So I'll try to cover some of the apps that he didn't mention, but if you haven't checked out his video, definitely give it a watch to see what he's running. Alright.\u003C\u002Fp>\n\u003Cp>So let's dive in. Right. Onto, like, my main squeezes, the daily drivers. First up has got to be Missive. Hands down, the only email app you'll ever need.\u003C\u002Fp>\n\u003Cp>I've been using it for years. Now what makes it special? Well, I can manage both work and personal inboxes in a single app. That's the biggest thing. No more bouncing between Gmail tabs or different applications.\u003C\u002Fp>\n\u003Cp>And, you know, if you're working with a team, the collaboration features are killer, especially if you're just sharing inboxes with said team. Alright. Next up is gonna be Raycast. I use that for navigating and launching apps, and I was an Alford user for a lot of years. Even built a few plugins for that, but honestly Raycast blows it out of the water.\u003C\u002Fp>\n\u003Cp>Quick app switching, the clipboard history is probably what I use most. It's just become one of those tools that I can't imagine working without anymore. I do pay for the Pro plan, but, honestly, I don't use the AI features a ton because the experience is just a little clunky compared to some of the dedicated apps. For API work I'm using Bruno. If you've caught any of my 100 Apps one hundred Hours episodes, you've probably seen me using it to make API calls.\u003C\u002Fp>\n\u003Cp>It's a great open source alternative to Postman. The interface is a lot cleaner, and it's been super reliable for me. I've also recently switched to Orb Stack as a replacement for Docker Desktop. It seems to be a lot lighter on resources, it starts up a lot faster for me, and it it really just feels more native. So definitely give it a look.\u003C\u002Fp>\n\u003Cp>Now, on to videos, which is what I get asked about most. So, like, my setup, I get questions on all the time on the YouTube channel, both privately in the Discord community and sometimes via email. We do a lot of different styles of videos, so I'm gonna do a quick breakdown here of what I'm using. As far as the video apps, Loom is my go to for sharing quick updates with the Directus Core team or within the Directus community. If you need to explain a bug or show how something works, just click Record, boom, I get a link to share and then I'm done.\u003C\u002Fp>\n\u003Cp>That's it. For those UI focused recordings where you see me zipping around the screen, with all the slick animations, I'm using Screen Studio. It automatically adds those smooth zooms and highlights, and it makes just UI demos way more engaging. 99% of the time audio is off for those videos. When I need something more robust for longer form content, I'm using ScreenFlow.\u003C\u002Fp>\n\u003Cp>The editing capabilities within it are super quick and lightweight. It's perfect for me to put out, quick but yet still polished videos. Most of the time though, I'm exporting for our main man, Nat, who is our editor. And, Nat, you should probably edit yourself in here taking a battle somewhere because you are freaking amazing. And then the little mouse highlight pointy thing that everybody asks about, that's Mouse Pose.\u003C\u002Fp>\n\u003Cp>It's a nice simple little tool with a hot key. It makes following those cursor movements so much easier for viewers and I've been using it for so long, like, using it to explain stuff has become second nature to me. As far as the video gear that I'm using, on the hardware side, my mic is the Rode VideoMic NTG. It's a bit older, maybe like five, six years old, but the sound quality is incredible for the price point, and I can use it both on my camera or mounted on my desktop here. The headphones that you always see me in are Sony WH-1000XM5s.\u003C\u002Fp>\n\u003Cp>I recently upgraded because my dog got a hold of the old pair. These are pretty pricey, but, I've got three little girls so the noise cancellation is definitely worth it to me. Everything plugs into a CalDigit TS USB hub. I've got the TS three plus. There's a newer version available, but this one has worked for me for years.\u003C\u002Fp>\n\u003Cp>I still can't get to that one cable nirvana though because I've got two of these, LG five ks displays and my MacBook Pro doesn't run all of that through a single cable. For the camera, I've got a Sony a 6,400 with a Sigma sixteen millimeter lens. That's what gives me that nice bokeh effect that you see in the background. All that is connected through an Elgato Cam Link four k, which converts the HDMI signal to USB and lets me plug that into the computer. The mood lights in the background, those are Govee light bars.\u003C\u002Fp>\n\u003Cp>I think that's how you pronounce it. I have their iPhone app so I can just change the vibe depending on what I'm recording or, you know, sometimes my mood that particular day. If it's Christmas, I can turn on Christmas lights. Amazing. Alright.\u003C\u002Fp>\n\u003Cp>So for a little more spicier territory, AI. Right? You've probably seen me use AI in some of the hundred apps, hundred hours episodes, if you've caught any. But here's what I'm actually using day to day. Claude has become my daily driver.\u003C\u002Fp>\n\u003Cp>It's part of my workflow in some form just about every single day. I could be using it for, like, creating ASCII art for a rabbit that I need for a CLI app. Don't judge me. Or summarizing documents or content that I don't have time to read. Recently used it for, like, health insurance research.\u003C\u002Fp>\n\u003Cp>Or, you know, work related. I'm writing meta descriptions for blog posts or, Directus TV episodes. I'm also using Cursor for coding, which, you know, I have a love hate relationship with at the moment. Auto completion works amazingly well probably 75, 80 percent of the time for what I use it for. But most of the LLMs still have the older Directus SDK syntax memorized, so you have to prompt it a bit to pull that out of it.\u003C\u002Fp>\n\u003Cp>The agent feature in cursor is is interesting. You know, I've used it on some greenfield projects or some new features. With existing code bases, though, it's kinda been a bit of a mixed bag for me. Also AI wise, I've been playing a lot with replicate. I don't have a ton of time.\u003C\u002Fp>\n\u003Cp>I've got three little girls. So, like, setting up all these models and trying to run them locally for me is is not an option or just something I'm not willing to commit to. So, for image generation, I've been playing around with the Flux models. So if you've seen any weird AI generated images, from me, that's where they're coming from. I like Replicate because it's quick and easy to experiment, and I've even run a couple of fine tunes using their platform.\u003C\u002Fp>\n\u003Cp>So that's it. You know, the tools I use mostly just to help me get the job done quickly. I'll see you in the community and on Directus TV. I'm definitely gonna invoke rule number two here. And I'm going to go into Directus Cloud and you can call this cheating, you can call this taking advantage of what we've already built previously, but we are going to do a new project using our simple CMS template.\u003C\u002Fp>\n\u003Cp>If I bring up my little mouse pointer tool here, it's called Mouse Pose by the way. I think that is like 90% of modern JavaScript development is wrestling with dependencies and bundlers and config. Why go with four? To me, I I hate giving people a middle ground. Right?\u003C\u002Fp>\n\u003Cp>So if we have bad or, let's say, the worst, bad, and then good and great, there's no middle ground. Right? So you gotta get off the fence. Hey. That's the way the cookie crumbles sometimes.\u003C\u002Fp>\n\u003Cp>I'm not sure what I am doing wrong with this. Feel free to steal this password here because this project should be turned off by the time we get to airing this actual episode. I'm just gonna create a new component. We'll call it feedback. Should we call it widget?\u003C\u002Fp>\n\u003Cp>Love widgets. Who doesn't love widgets? This is the personalized content engine. Website personalization engine. Badabing badaboom.\u003C\u002Fp>\n\u003Cp>This is beautiful. Right? I've got no idea whether we're actually going to be able to, like, push this one across the finish line or not. So that should be exciting for this episode. Filnaughty.\u003C\u002Fp>\n\u003Cp>Filnaughty just typing that out. And then we have the nice list. Create a strong password. Why do I have to create a strong password? Come on, dog.\u003C\u002Fp>\n\u003Cp>Why is it asking me to do this? I'm not sure. I'm sure somebody could figure out how to get access to my local host. I know we got a lot of really great hackers out there in the community. This is gonna be Site B.\u003C\u002Fp>\n\u003Cp>Alright. The other site. The site that I don't like. Site B. Don't even care about it enough to give it a nice name.\u003C\u002Fp>\n\u003Cp>Everything is installed. PMPM dev. I think we need like a blooper reel, but maybe that would be every episode that resulted in just non stop bloopers. We'll delete all of these fields that are no longer global in our data model. And today's exes episode is is really just a, a big exercise in data modeling.\u003C\u002Fp>\n\u003Cp>Alright. Delete. Delete. Delete. And this is how you know that these things aren't scripted because if it was, I would be way more organized than this.\u003C\u002Fp>\n\u003Cp>Boom. I've got a REST API. Magic happens. Sparkles fall from the sky, etcetera. Sometimes you win, sometimes you lose.\u003C\u002Fp>\n\u003Cp>Just gotta keep rolling the dice. Alright. That's it for this episode of hundred apps, hundred hours. We'll catch you on the next one. Thanks for joining.\u003C\u002Fp>\n\u003Cp>Speaker 0: Amazing. And all 10 episodes of 100 apps one hundred hours season three. So there's over 30, episodes for you at directorsi0\u002Ftv, and it's the first one. I made it the first one, when I uploaded it, so you should have no worries finding it. But thank you so much for Brian for taking us through both what is in his doc and also, the full a hundred apps, a hundred hours season three.\u003C\u002Fp>\n\u003Cp>Do go check them out. He's a very entertaining person. It's it's gonna be good. So, next up, we have episode one of another, brand new director's TV, show, which is uncharted territory, and that is we're learning how to use geospatial data with Directus. So I'm gonna hand it over to Carmen to take you through episode one.\u003C\u002Fp>\n\u003Cp>Speaker 1: This is my Directus project for tracking all of my hiking activity. And as you can see, I've got a collection called hikes, which is currently empty. Let's take a look at its structure by going to settings, data model, and hikes. Now, here we can see we've got two fields in our hikes collection. But I'd love to show on a map where my hike took place.\u003C\u002Fp>\n\u003Cp>So what we're gonna do is create a new field, make it of type map, and call it location. And we can see right now that the type is set to point. But as it turns out, there's lots of types of map data. And in this episode, we're gonna cover each and every one of them. So let's start with point.\u003C\u002Fp>\n\u003Cp>I'm gonna save this field and then go and create a hike. Gonna click on create item. Date set to today, and I'll set my location to be in this part of Vienna. So that's what the point type is. It lets us set a single point on a map.\u003C\u002Fp>\n\u003Cp>If we take a look at our collection, we'll see we have our type set to point and our coordinates set to these coordinates. Of course, this doesn't look as nice, but we'll cover the layout thereof in another episode. But a point is nice, but a hike is more of a, you know, a journey along a line. What I'm gonna do is create that map type location again, but this time set the type to line string. Go ahead and save that.\u003C\u002Fp>\n\u003Cp>And let's try again. Set the date to today. And go for a hike somewhere in France. And what I can do is by setting a certain number of points, create this line that indicates my hiking journey. So now we can see that we have a series of coordinates, which is good.\u003C\u002Fp>\n\u003Cp>But what about the other types? Let's once again create that location field and this time set the type to be a polygon. Let's go to Slovakia. So what I can do here is once again, by clicking around, I can now set a polygon to denote which area I've covered with my hike. Go ahead and save that.\u003C\u002Fp>\n\u003Cp>And we can see once again, the coordinates are set accordingly. Now let's try the multi point type. Go ahead and create a new hike. Now with multi point, I can set a variety of points. Next let's try the multiline string.\u003C\u002Fp>\n\u003Cp>Let's go to The United States. Now with a multiline string, similar to the line string itself, I can now have several strings. The multi polygon location allows us to create multiple polygons on a map. Let's give that a try. Let's go to Argentina.\u003C\u002Fp>\n\u003Cp>And as you can see, I can create multiple polygons in a map. Let's try the geometry all option. I'm gonna save this and create a new hike. Now what the geography all option lets you do is mark all kinds of points. So we can have points, we can have polygons, and we can have lines.\u003C\u002Fp>\n\u003Cp>Let's cover what the rest of the types look like. First, there's json. And json looks exactly like a geometry all object, but stored as json. A string is also a geometry all object stored as a string of characters, a text. You guessed it.\u003C\u002Fp>\n\u003Cp>Also a geometry all object, but stored as text. And CSV is a geometry object, but stored as CSV or comma separated values. How can I best store my hike? Now, as we said towards the beginning of this, the line string is really going to be my best bet because it's a series of points that I follow during my hike. So these are the different types in which we can store that GeoJSON mapping data in DirectUs.\u003C\u002Fp>\n\u003Cp>Speaker 4: We want to take a moment towards the end of the change log for thanking our amazing community contributors who give their time to improve the director's project. Since last month, there have been two contributors. A huge thank you to Chiyako for fixing a marketplace extension detail page back button and Gerard for removing a duplicated key validation logic from update by query. Thank you again. You can see the specific pull requests inside of the full release notes on GitHub.\u003C\u002Fp>\n\u003Cp>Lastly, we also want to take the time to thank our GitHub sponsors of February who financially contribute to Directsys development. A huge thank you to Wayfan, Entel, Mike, Fergus, Omar, Marcus, Peter, Utomic, Steven, Robson, Nonlinear, Andreas, John, Khan, Biab, Adam, Jason, Birka, Vincent, CK, Valentino, and Jens. The money we are given from our GitHub Sponsors goes straight back to community members who build tooling and extensions for the directors ecosystem. Thank you again for being part of that. For this month's reading list, we're following on from changing up a bit like last month with the audio special and are coming to you with video recommendations coming from FODSTEM, the free and open source software developers European meeting that took place last month.\u003C\u002Fp>\n\u003Cp>There are recordings from lots of different tracks including community, data analytics, inclusive web, security, tool the docs and so many others. To call out three of the recordings, there's alternative text for images text anyway from Mike Gifford, lessons learned open sourcing The UK's COVID tracing app from Terence Eden, and beyond the readme, crafting a better developer Experience for Open Source Projects from Lorna Mitchell. There's a seriously impressive volume of resources available to watch at fosdem.org, so we recommend you go check them out.\u003C\u002Fp>\n\u003Cp>Speaker 0: Alright. That is it for March's the changelog. If you are still here, thank you so much for taking the time to hang out with us. Hopefully, you found something useful and something that excites you for the day. Have a great rest of your day.\u003C\u002Fp>\n\u003Cp>In the meantime, as I said at the beginning, if you are free on the March 20 and want to join our workshop with Ingest, I have just posted the link in the chat. If you're not, in the Direct to CV chat live, you can also find it in the events tab on Discord. Have a great rest of your day, and hopefully see you for the next one. Take care, everyone. Have a good one.\u003C\u002Fp>\n\u003Cp>Bye.\u003C\u002Fp>","Alright. Hello, everyone. Hopefully, you are having a great day. If you're new here, I'm Beth, and I am here to take you through what is new with Directus in March. We have a really great show for you today. We have a lot of Brian. We're very lucky. He will be taking us through what his working setup is in a special edition of what's in your dock. We also have a brand new episode one of uncharted territory, a brand new director's TV show. But first, I have an invite for you. So if you are free on the March 20, we are hosting a workshop with Ingest called building advanced content workflows. And in it, you can learn how to build an automated translation system using direct us in ingest that you can implement immediately. It is on the March 20. And if you are in our Discord, you can see it with the little events tab on the there. First of all though I'm gonna take you through to Carmen who has got the product updates for you, for this month. Hey, everyone. Carmen here to tell you about what we've shipped in Directus in the last month or so with eleven point five point zero and eleven point five point one. We've added numerous small improvements and bug fixes. And in this section, I'm gonna highlight two of these to draw your attention. Firstly and importantly, we have a breaking change to bring to your attention. There is a changed error message when a flow condition operation fails, And we've added partial Prometheus supports for your monitoring needs. Currently, we support the pull method via the metrics endpoint. Metrics generated can be controlled by a CSV of services to observe using the metric services environment variable. So that's what we've released in the last month. Be sure to keep up with releases on our documentation at directus.i0\u002Fdocs. And if you have any questions or feedback, be sure to join us on Discord. This month, we've added two different tutorials to our documentation in four frameworks each, Next. Js, Astro, SvelteKit, and Nuxt. So, yeah, we've got eight new articles. These are on implementing multilingual content using Directus and the framework and rendering dynamic blocks using framework. You can check them out at directus.i0\u002Fdocs forward \u002Ftutorials forward slash getting dash started. Hello. I'm Mike, and I'm from the marketing team. I'm gonna be here to talk to you about all the latest extensions that have been released in the Director's Labs org on GitHub. Just as a reminder, this org is where we publish regularly new experimental extensions with quality of life or feature improvements that could be used. First, we're gonna be talking about the field comments module. First, what we need to do is connect it to a collection. In this case, I'm gonna attach it to our employees collection. We can enable it on all fields by default, or we can select specific fields we wish to be able to comment against. When we add an official record, so for this, I'll be adding myself. I am the developer experience engineer. I do things with extensions. You'll notice how there's no additional information or anything here. Once the record is created, we can go back in, and you'll notice how there are now additional comments sections that we can add. So in this case, at admin user, is this person actually the dev ex engineer? Once we submit this, we'll be had a comment to that field, and it'll tag accordingly how many comments are available. This also uses the existing commenting, feature of directors. So if you tag somebody, etcetera, they will receive a notification about it. And as an aside, any field level comment is completely isolated to its field and doesn't appear in the item level comments. The next extension we're talking about is the SEO plugin. We can configure this quickly with templates for both the title, the meta description, and we can enable additional functionality such as sitemap controls for how often things are going to be updating our sitemap, search engine controls for things such as no follow and no index, as well as adding the social media image for graphs. I'm going to just quickly skip that one so I don't have to attach an image. Add this to our data model. Once we are back in our data model, you'll be able to see that we have our content as well as our new fields for SEO. So, okay. Alsmore is the DevX engineer. Mike those things with extensions for directors. And you'll notice how we're now getting a lovely preview with some help controllers, so we don't want this to be followed, but we do want it to be indexed. And now we can save this. This extension that we'll be looking at is the WYSIWYG add edit file link. So if we create an item, this is the selected field. There is a new add edit file link button. So if we have some content and we want to insert a file, for example, we can grab off my desktop a text file, add it, the transcript of the meeting, and we can have it open in a new field. This image, preview doesn't work as it's not an image, but we add this, and we'll have the transcript to the new meeting as a file. We can also then go in, revise it, and change anything. The complete just as if we were editing the normal link, we can also and just to double check, we can also upload an image. Director's logo, which will have the preview and allow us to insert it with the download option. And they look like so, with the complete URL through to the assets library locally. The final module for this month is going to be the migrate module. Here you can see I have the migrate modules interface, and the Directus instance I'm going to send it to. This is, my local instance, and this is a remote cloud instance. As you can see here, there is no content, no collections available, but we have a collection available here. So I'm just gonna quickly grab a static token, as the module requires a authorization token to interact with the client. Don't worry. I'll recycle this afterwards so that nobody can use it. Save that. Going to grab our domain for the client. Now we can really check to see if it is compatible. And you'll see here that there there is a difference. My local is running 11.4, and the remote is 11.51. We can force this through. I'm going to now. Or we can use this as an opportunity to run any upgrades, etcetera, to make sure everything is aligned, especially if there is a major difference like version 10 or any of the larger schema changes internally. So we're gonna force this, and we don't want to copy over the users because I already have an admin user over in my remote. So we are now going to start the dry run, and it's going to create a snapshot and create collection options. This may take a moment. We're also able to preview every one of the files created as part of the migration, all the individual components that make it up. Now to actually do the migration, we apply our key and our domain. We are once again gonna have to force it through, and we remove the driver and tag. This way, it's not just building it and running locally. It is pushing up to the remote instance. So just to validate again, we don't have a collection here. I'm gonna start the migration process. And now we can see that the migration is complete, but it has skipped the users as we already have an admin user. Now if I reload the content, we will have our employee collection with me, the developer experience engineer, with all the relevant information internally. The only thing that's missing is this instance will now need to be configured with the correct extensions for it to operate as expected. Alright. Hopefully, you found those interesting. Mike, I don't know if you're in the chat, but what is your keyboard? It's clicky, it's a chef's kiss. Very nice. It's very satisfying. If you are planning on using anything that was just mentioned or you have any questions, let us know. A few of us are hanging around in the chat so we can see them and answer your questions. Or if you are catching this on demand, let us know in Discord, and we'll see what we can do. It is time for the first of Brian's sections. So he is gonna be talking us through what is in your doc. And if you haven't caught that on the director's TV already, we have a bunch of cool people with very interesting jobs tell us what they are using in their day to day setup. And so here is Bryant's. Hey. Hey. Bryant here from Directus, and I I guess today I'm actually breaking down what's in my doc. Now, our CTO, Ryke, did one of these videos not too long ago and there's a lot of overlap in our setups. So I'll try to cover some of the apps that he didn't mention, but if you haven't checked out his video, definitely give it a watch to see what he's running. Alright. So let's dive in. Right. Onto, like, my main squeezes, the daily drivers. First up has got to be Missive. Hands down, the only email app you'll ever need. I've been using it for years. Now what makes it special? Well, I can manage both work and personal inboxes in a single app. That's the biggest thing. No more bouncing between Gmail tabs or different applications. And, you know, if you're working with a team, the collaboration features are killer, especially if you're just sharing inboxes with said team. Alright. Next up is gonna be Raycast. I use that for navigating and launching apps, and I was an Alford user for a lot of years. Even built a few plugins for that, but honestly Raycast blows it out of the water. Quick app switching, the clipboard history is probably what I use most. It's just become one of those tools that I can't imagine working without anymore. I do pay for the Pro plan, but, honestly, I don't use the AI features a ton because the experience is just a little clunky compared to some of the dedicated apps. For API work I'm using Bruno. If you've caught any of my 100 Apps one hundred Hours episodes, you've probably seen me using it to make API calls. It's a great open source alternative to Postman. The interface is a lot cleaner, and it's been super reliable for me. I've also recently switched to Orb Stack as a replacement for Docker Desktop. It seems to be a lot lighter on resources, it starts up a lot faster for me, and it it really just feels more native. So definitely give it a look. Now, on to videos, which is what I get asked about most. So, like, my setup, I get questions on all the time on the YouTube channel, both privately in the Discord community and sometimes via email. We do a lot of different styles of videos, so I'm gonna do a quick breakdown here of what I'm using. As far as the video apps, Loom is my go to for sharing quick updates with the Directus Core team or within the Directus community. If you need to explain a bug or show how something works, just click Record, boom, I get a link to share and then I'm done. That's it. For those UI focused recordings where you see me zipping around the screen, with all the slick animations, I'm using Screen Studio. It automatically adds those smooth zooms and highlights, and it makes just UI demos way more engaging. 99% of the time audio is off for those videos. When I need something more robust for longer form content, I'm using ScreenFlow. The editing capabilities within it are super quick and lightweight. It's perfect for me to put out, quick but yet still polished videos. Most of the time though, I'm exporting for our main man, Nat, who is our editor. And, Nat, you should probably edit yourself in here taking a battle somewhere because you are freaking amazing. And then the little mouse highlight pointy thing that everybody asks about, that's Mouse Pose. It's a nice simple little tool with a hot key. It makes following those cursor movements so much easier for viewers and I've been using it for so long, like, using it to explain stuff has become second nature to me. As far as the video gear that I'm using, on the hardware side, my mic is the Rode VideoMic NTG. It's a bit older, maybe like five, six years old, but the sound quality is incredible for the price point, and I can use it both on my camera or mounted on my desktop here. The headphones that you always see me in are Sony WH-1000XM5s. I recently upgraded because my dog got a hold of the old pair. These are pretty pricey, but, I've got three little girls so the noise cancellation is definitely worth it to me. Everything plugs into a CalDigit TS USB hub. I've got the TS three plus. There's a newer version available, but this one has worked for me for years. I still can't get to that one cable nirvana though because I've got two of these, LG five ks displays and my MacBook Pro doesn't run all of that through a single cable. For the camera, I've got a Sony a 6,400 with a Sigma sixteen millimeter lens. That's what gives me that nice bokeh effect that you see in the background. All that is connected through an Elgato Cam Link four k, which converts the HDMI signal to USB and lets me plug that into the computer. The mood lights in the background, those are Govee light bars. I think that's how you pronounce it. I have their iPhone app so I can just change the vibe depending on what I'm recording or, you know, sometimes my mood that particular day. If it's Christmas, I can turn on Christmas lights. Amazing. Alright. So for a little more spicier territory, AI. Right? You've probably seen me use AI in some of the hundred apps, hundred hours episodes, if you've caught any. But here's what I'm actually using day to day. Claude has become my daily driver. It's part of my workflow in some form just about every single day. I could be using it for, like, creating ASCII art for a rabbit that I need for a CLI app. Don't judge me. Or summarizing documents or content that I don't have time to read. Recently used it for, like, health insurance research. Or, you know, work related. I'm writing meta descriptions for blog posts or, Directus TV episodes. I'm also using Cursor for coding, which, you know, I have a love hate relationship with at the moment. Auto completion works amazingly well probably 75, 80 percent of the time for what I use it for. But most of the LLMs still have the older Directus SDK syntax memorized, so you have to prompt it a bit to pull that out of it. The agent feature in cursor is is interesting. You know, I've used it on some greenfield projects or some new features. With existing code bases, though, it's kinda been a bit of a mixed bag for me. Also AI wise, I've been playing a lot with replicate. I don't have a ton of time. I've got three little girls. So, like, setting up all these models and trying to run them locally for me is is not an option or just something I'm not willing to commit to. So, for image generation, I've been playing around with the Flux models. So if you've seen any weird AI generated images, from me, that's where they're coming from. I like Replicate because it's quick and easy to experiment, and I've even run a couple of fine tunes using their platform. So that's it. You know, the tools I use mostly just to help me get the job done quickly. I'll see you in the community and on Directus TV. I'm definitely gonna invoke rule number two here. And I'm going to go into Directus Cloud and you can call this cheating, you can call this taking advantage of what we've already built previously, but we are going to do a new project using our simple CMS template. If I bring up my little mouse pointer tool here, it's called Mouse Pose by the way. I think that is like 90% of modern JavaScript development is wrestling with dependencies and bundlers and config. Why go with four? To me, I I hate giving people a middle ground. Right? So if we have bad or, let's say, the worst, bad, and then good and great, there's no middle ground. Right? So you gotta get off the fence. Hey. That's the way the cookie crumbles sometimes. I'm not sure what I am doing wrong with this. Feel free to steal this password here because this project should be turned off by the time we get to airing this actual episode. I'm just gonna create a new component. We'll call it feedback. Should we call it widget? Love widgets. Who doesn't love widgets? This is the personalized content engine. Website personalization engine. Badabing badaboom. This is beautiful. Right? I've got no idea whether we're actually going to be able to, like, push this one across the finish line or not. So that should be exciting for this episode. Filnaughty. Filnaughty just typing that out. And then we have the nice list. Create a strong password. Why do I have to create a strong password? Come on, dog. Why is it asking me to do this? I'm not sure. I'm sure somebody could figure out how to get access to my local host. I know we got a lot of really great hackers out there in the community. This is gonna be Site B. Alright. The other site. The site that I don't like. Site B. Don't even care about it enough to give it a nice name. Everything is installed. PMPM dev. I think we need like a blooper reel, but maybe that would be every episode that resulted in just non stop bloopers. We'll delete all of these fields that are no longer global in our data model. And today's exes episode is is really just a, a big exercise in data modeling. Alright. Delete. Delete. Delete. And this is how you know that these things aren't scripted because if it was, I would be way more organized than this. Boom. I've got a REST API. Magic happens. Sparkles fall from the sky, etcetera. Sometimes you win, sometimes you lose. Just gotta keep rolling the dice. Alright. That's it for this episode of hundred apps, hundred hours. We'll catch you on the next one. Thanks for joining. Amazing. And all 10 episodes of 100 apps one hundred hours season three. So there's over 30, episodes for you at directorsi0\u002Ftv, and it's the first one. I made it the first one, when I uploaded it, so you should have no worries finding it. But thank you so much for Brian for taking us through both what is in his doc and also, the full a hundred apps, a hundred hours season three. Do go check them out. He's a very entertaining person. It's it's gonna be good. So, next up, we have episode one of another, brand new director's TV, show, which is uncharted territory, and that is we're learning how to use geospatial data with Directus. So I'm gonna hand it over to Carmen to take you through episode one. This is my Directus project for tracking all of my hiking activity. And as you can see, I've got a collection called hikes, which is currently empty. Let's take a look at its structure by going to settings, data model, and hikes. Now, here we can see we've got two fields in our hikes collection. But I'd love to show on a map where my hike took place. So what we're gonna do is create a new field, make it of type map, and call it location. And we can see right now that the type is set to point. But as it turns out, there's lots of types of map data. And in this episode, we're gonna cover each and every one of them. So let's start with point. I'm gonna save this field and then go and create a hike. Gonna click on create item. Date set to today, and I'll set my location to be in this part of Vienna. So that's what the point type is. It lets us set a single point on a map. If we take a look at our collection, we'll see we have our type set to point and our coordinates set to these coordinates. Of course, this doesn't look as nice, but we'll cover the layout thereof in another episode. But a point is nice, but a hike is more of a, you know, a journey along a line. What I'm gonna do is create that map type location again, but this time set the type to line string. Go ahead and save that. And let's try again. Set the date to today. And go for a hike somewhere in France. And what I can do is by setting a certain number of points, create this line that indicates my hiking journey. So now we can see that we have a series of coordinates, which is good. But what about the other types? Let's once again create that location field and this time set the type to be a polygon. Let's go to Slovakia. So what I can do here is once again, by clicking around, I can now set a polygon to denote which area I've covered with my hike. Go ahead and save that. And we can see once again, the coordinates are set accordingly. Now let's try the multi point type. Go ahead and create a new hike. Now with multi point, I can set a variety of points. Next let's try the multiline string. Let's go to The United States. Now with a multiline string, similar to the line string itself, I can now have several strings. The multi polygon location allows us to create multiple polygons on a map. Let's give that a try. Let's go to Argentina. And as you can see, I can create multiple polygons in a map. Let's try the geometry all option. I'm gonna save this and create a new hike. Now what the geography all option lets you do is mark all kinds of points. So we can have points, we can have polygons, and we can have lines. Let's cover what the rest of the types look like. First, there's json. And json looks exactly like a geometry all object, but stored as json. A string is also a geometry all object stored as a string of characters, a text. You guessed it. Also a geometry all object, but stored as text. And CSV is a geometry object, but stored as CSV or comma separated values. How can I best store my hike? Now, as we said towards the beginning of this, the line string is really going to be my best bet because it's a series of points that I follow during my hike. So these are the different types in which we can store that GeoJSON mapping data in DirectUs. We want to take a moment towards the end of the change log for thanking our amazing community contributors who give their time to improve the director's project. Since last month, there have been two contributors. A huge thank you to Chiyako for fixing a marketplace extension detail page back button and Gerard for removing a duplicated key validation logic from update by query. Thank you again. You can see the specific pull requests inside of the full release notes on GitHub. Lastly, we also want to take the time to thank our GitHub sponsors of February who financially contribute to Directsys development. A huge thank you to Wayfan, Entel, Mike, Fergus, Omar, Marcus, Peter, Utomic, Steven, Robson, Nonlinear, Andreas, John, Khan, Biab, Adam, Jason, Birka, Vincent, CK, Valentino, and Jens. The money we are given from our GitHub Sponsors goes straight back to community members who build tooling and extensions for the directors ecosystem. Thank you again for being part of that. For this month's reading list, we're following on from changing up a bit like last month with the audio special and are coming to you with video recommendations coming from FODSTEM, the free and open source software developers European meeting that took place last month. There are recordings from lots of different tracks including community, data analytics, inclusive web, security, tool the docs and so many others. To call out three of the recordings, there's alternative text for images text anyway from Mike Gifford, lessons learned open sourcing The UK's COVID tracing app from Terence Eden, and beyond the readme, crafting a better developer Experience for Open Source Projects from Lorna Mitchell. There's a seriously impressive volume of resources available to watch at fosdem.org, so we recommend you go check them out. Alright. That is it for March's the changelog. If you are still here, thank you so much for taking the time to hang out with us. Hopefully, you found something useful and something that excites you for the day. Have a great rest of your day. In the meantime, as I said at the beginning, if you are free on the March 20 and want to join our workshop with Ingest, I have just posted the link in the chat. If you're not, in the Direct to CV chat live, you can also find it in the events tab on Discord. Have a great rest of your day, and hopefully see you for the next one. Take care, everyone. Have a good one. Bye.","7a174565-9bb1-48a3-9fe6-0409d03b3829",[1000,1001,1002,1003],"066bb544-334d-4408-a34d-60c4120f8dd5","d3910456-5949-4910-b4e2-53c20f6ddd1b","ff03f68f-3289-47f8-ad51-bf2caee18bbb","7873b288-47b7-42e2-ba27-c1f73f25f5a6",[],{"year":399,"number":687,"id":688,"episodes":1006,"show":1007},[690,691,692,693,694,695,696,669],{"title":297,"slug":298},{"id":1009,"slug":1010,"vimeo_id":1011,"description":1012,"tile":1013,"length":11,"resources":12,"people":12,"episode_number":177,"published":994,"title":1014,"video_transcript_html":1015,"video_transcript_text":1016,"content":12,"seo":1017,"status":19,"episode_people":1018,"recommendations":1020,"season":1021},"292a3e94-c8b3-4ed9-b214-60264d089232","what's-in-your-dock-bryant","1065051930","Bryant Gillespie, Growth Engineer at Directus takes us through his most used apps and websites.","17792489-5d2e-4082-be35-6650885aeda6","What's in your Dock, Bryant?","\u003Cp>Speaker 0: Hey, hey. Brian here from Directus and I I guess today I'm actually breaking down what's in my dock. Now our CTO, Wrike, did one of these videos not too long ago and there's a lot of overlap in our setups. So I'll try to cover some of the apps that he didn't mention, but if you haven't checked out his video, definitely give it a watch to see what he's running. Alright, so let's dive in.\u003C\u002Fp>\u003Cp>Right? Onto like my main squeezes, the daily drivers. First up has got to be Missive. Hands down, the only email app you'll ever need. I've been using it for years.\u003C\u002Fp>\u003Cp>Now what makes it special? Well, I can manage both work and personal inboxes in a single app. That's the biggest thing. No more bouncing between Gmail tabs or different applications. And, you know, if you're working with a team, the collaboration features are killer, especially if you're just sharing inboxes with said team.\u003C\u002Fp>\u003Cp>Alright. Next up is gonna be Raycast. I use that for navigating and launching apps, and I was an Alford user for a lot of years. Even built a few plugins for that, but, honestly, Raycast blows it out of the water. Quick app switching, the clipboard history is probably what I use most.\u003C\u002Fp>\u003Cp>It's just become one of those tools that I can't imagine working without anymore. I do pay for the Pro plan, but honestly, I don't use the AI features a ton because the experience is just a little clunky compared to some of the dedicated apps. For API work, I'm using Bruno. If you've caught any of my 100 Apps one hundred Hours episodes, you've probably seen me using it to make API calls. It's a great open source alternative to Postman, the interface is a lot cleaner, and it's been super reliable for me.\u003C\u002Fp>\u003Cp>I've also recently switched to Orb Stack as a replacement for Docker Desktop. It seems to be a lot lighter on resources, it starts up a lot faster for me, and it it really just feels more native. So definitely give it a look. Now, on to videos, which is what I get asked about most. So, like my setup, I get questions on all the time on the YouTube channel, both privately in the Discord community and sometimes via email.\u003C\u002Fp>\u003Cp>We do a lot of different styles of videos, so I'm gonna do a quick breakdown here of what I'm using. As far as the video apps, Loom is my go to for sharing quick updates with the Direct Discord team or within the Directus community. If you need to explain a bug or show how something works, just click Record, boom, I get a link to share and then I'm done. That's it. For those UI focused recordings where you see me zipping around the screen, with all the slick animations, I'm using Screen Studio.\u003C\u002Fp>\u003Cp>It automatically adds those smooth zooms and highlights, and it makes just UI demos way more engaging. 99% of the time audio is off for those videos. When I need something more robust for longer form content, I'm using ScreenFlow. The editing capabilities within it are super quick and lightweight. It's perfect for me to put out, quick, but yet still polished videos.\u003C\u002Fp>\u003Cp>Most of the time though, I'm exporting for our main man, Nat, who is our editor. And Nat, you should probably edit yourself in here taking a battle somewhere because you are freaking amazing. And then the little mouse highlight pointy thing that everybody asks about, that's Mouse Pose. It's a nice simple little tool with a hot key, it makes following those cursor movements so much easier for viewers, and I've been using it for so long, like, using it to explain stuff has become second nature to me. As far as the video gear that I'm using, on the hardware side, my mic is the Rode VideoMic NTG.\u003C\u002Fp>\u003Cp>It's a bit older, maybe like five, six years old, but the sound quality is incredible for the price point, and I can use it both on my camera or mounted on my desktop here. The headphones that you always see me in are Sony WH-one thousand XM5s. I recently upgraded because my dog got a hold of the old pair. These are pretty pricey, but, I've got three little girls so the noise cancellation is definitely worth it to me. Everything plugs into a CalDigit TS USB hub.\u003C\u002Fp>\u003Cp>I've got the TS three plus. There's a newer version available, but this one has worked for me for years. I still can't get to that one cable nirvana though because I've got two of these, LG five k displays and my MacBook Pro doesn't run all of that through a single cable. For the camera, I've got a Sony a 6,400 with a Sigma sixteen millimeter lens. That's what gives me that nice bokeh effect that you see in the background.\u003C\u002Fp>\u003Cp>All that is connected through an Elgato Cam Link four k, which converts the HDMI signal to USB and lets me plug that into the computer. The mood lights in the background, those are Govee light bars. I think that's how you pronounce it. I have their iPhone app so I can just change the vibe depending on what I'm recording or, you know, sometimes my mood that particular day. If it's Christmas, I can turn on Christmas lights.\u003C\u002Fp>\u003Cp>Amazing. Alright. So for a little more spicier territory, AI. Right? You've probably seen me use AI in some of the hundred apps, hundred hours episodes if you've caught any.\u003C\u002Fp>\u003Cp>But here's what I'm actually using day to day. Claude has become my daily driver. It's part of my workflow in some form just about every single day. I could be using it for, like, creating ASCII art for a rabbit that I need for a CLI app. Don't judge me.\u003C\u002Fp>\u003Cp>Or summarizing documents or content that I don't have time to read. Recently used it for, like, health insurance research. Or, you know, work related. I'm writing meta descriptions for blog posts or, Directus TV episodes. I'm also using Cursor for coding, which, you know, I have a love hate relationship with at the moment.\u003C\u002Fp>\u003Cp>Auto completion works amazingly well probably 75, 80 percent of the time for what I use it for. But most of the LLMs still have the older Directus SDK syntax memorized, so you have to prompt it a bit to pull that out of it. The agent feature in cursor is is interesting. You know, I've used it on some greenfield projects or some new features. With existing code bases though, it's kinda been, a bit of a mixed bag for me.\u003C\u002Fp>\u003Cp>Also AI wise, I've been playing a lot with replicate. I don't have a ton of time. I've got three little girls. So, like, setting up all these models and trying to run them locally for me is is not an option or just something I'm not willing to commit to. So, for image generation, I've been playing around with the Flux models.\u003C\u002Fp>\u003Cp>So if you've seen any weird AI generated images, from me that's where they're coming from. I like replicate because it's quick and easy to experiment, and I've even run a couple of fine tunes using their platform. So that's it. You know, the tools I use mostly just to help me get the job done quickly. I'll see you in the community and on Directus TV.\u003C\u002Fp>","Hey, hey. Brian here from Directus and I I guess today I'm actually breaking down what's in my dock. Now our CTO, Wrike, did one of these videos not too long ago and there's a lot of overlap in our setups. So I'll try to cover some of the apps that he didn't mention, but if you haven't checked out his video, definitely give it a watch to see what he's running. Alright, so let's dive in. Right? Onto like my main squeezes, the daily drivers. First up has got to be Missive. Hands down, the only email app you'll ever need. I've been using it for years. Now what makes it special? Well, I can manage both work and personal inboxes in a single app. That's the biggest thing. No more bouncing between Gmail tabs or different applications. And, you know, if you're working with a team, the collaboration features are killer, especially if you're just sharing inboxes with said team. Alright. Next up is gonna be Raycast. I use that for navigating and launching apps, and I was an Alford user for a lot of years. Even built a few plugins for that, but, honestly, Raycast blows it out of the water. Quick app switching, the clipboard history is probably what I use most. It's just become one of those tools that I can't imagine working without anymore. I do pay for the Pro plan, but honestly, I don't use the AI features a ton because the experience is just a little clunky compared to some of the dedicated apps. For API work, I'm using Bruno. If you've caught any of my 100 Apps one hundred Hours episodes, you've probably seen me using it to make API calls. It's a great open source alternative to Postman, the interface is a lot cleaner, and it's been super reliable for me. I've also recently switched to Orb Stack as a replacement for Docker Desktop. It seems to be a lot lighter on resources, it starts up a lot faster for me, and it it really just feels more native. So definitely give it a look. Now, on to videos, which is what I get asked about most. So, like my setup, I get questions on all the time on the YouTube channel, both privately in the Discord community and sometimes via email. We do a lot of different styles of videos, so I'm gonna do a quick breakdown here of what I'm using. As far as the video apps, Loom is my go to for sharing quick updates with the Direct Discord team or within the Directus community. If you need to explain a bug or show how something works, just click Record, boom, I get a link to share and then I'm done. That's it. For those UI focused recordings where you see me zipping around the screen, with all the slick animations, I'm using Screen Studio. It automatically adds those smooth zooms and highlights, and it makes just UI demos way more engaging. 99% of the time audio is off for those videos. When I need something more robust for longer form content, I'm using ScreenFlow. The editing capabilities within it are super quick and lightweight. It's perfect for me to put out, quick, but yet still polished videos. Most of the time though, I'm exporting for our main man, Nat, who is our editor. And Nat, you should probably edit yourself in here taking a battle somewhere because you are freaking amazing. And then the little mouse highlight pointy thing that everybody asks about, that's Mouse Pose. It's a nice simple little tool with a hot key, it makes following those cursor movements so much easier for viewers, and I've been using it for so long, like, using it to explain stuff has become second nature to me. As far as the video gear that I'm using, on the hardware side, my mic is the Rode VideoMic NTG. It's a bit older, maybe like five, six years old, but the sound quality is incredible for the price point, and I can use it both on my camera or mounted on my desktop here. The headphones that you always see me in are Sony WH-one thousand XM5s. I recently upgraded because my dog got a hold of the old pair. These are pretty pricey, but, I've got three little girls so the noise cancellation is definitely worth it to me. Everything plugs into a CalDigit TS USB hub. I've got the TS three plus. There's a newer version available, but this one has worked for me for years. I still can't get to that one cable nirvana though because I've got two of these, LG five k displays and my MacBook Pro doesn't run all of that through a single cable. For the camera, I've got a Sony a 6,400 with a Sigma sixteen millimeter lens. That's what gives me that nice bokeh effect that you see in the background. All that is connected through an Elgato Cam Link four k, which converts the HDMI signal to USB and lets me plug that into the computer. The mood lights in the background, those are Govee light bars. I think that's how you pronounce it. I have their iPhone app so I can just change the vibe depending on what I'm recording or, you know, sometimes my mood that particular day. If it's Christmas, I can turn on Christmas lights. Amazing. Alright. So for a little more spicier territory, AI. Right? You've probably seen me use AI in some of the hundred apps, hundred hours episodes if you've caught any. But here's what I'm actually using day to day. Claude has become my daily driver. It's part of my workflow in some form just about every single day. I could be using it for, like, creating ASCII art for a rabbit that I need for a CLI app. Don't judge me. Or summarizing documents or content that I don't have time to read. Recently used it for, like, health insurance research. Or, you know, work related. I'm writing meta descriptions for blog posts or, Directus TV episodes. I'm also using Cursor for coding, which, you know, I have a love hate relationship with at the moment. Auto completion works amazingly well probably 75, 80 percent of the time for what I use it for. But most of the LLMs still have the older Directus SDK syntax memorized, so you have to prompt it a bit to pull that out of it. The agent feature in cursor is is interesting. You know, I've used it on some greenfield projects or some new features. With existing code bases though, it's kinda been, a bit of a mixed bag for me. Also AI wise, I've been playing a lot with replicate. I don't have a ton of time. I've got three little girls. So, like, setting up all these models and trying to run them locally for me is is not an option or just something I'm not willing to commit to. So, for image generation, I've been playing around with the Flux models. So if you've seen any weird AI generated images, from me that's where they're coming from. I like replicate because it's quick and easy to experiment, and I've even run a couple of fine tunes using their platform. So that's it. You know, the tools I use mostly just to help me get the job done quickly. I'll see you in the community and on Directus TV.","3a335283-9ab7-4216-85a7-510fee56b9d8",[1019],"c466ccdb-4b81-451d-9e90-162e9b6b7689",[],{"year":978,"number":1022,"id":1023,"episodes":1024,"show":1026},49,"3310777b-2774-4ba9-80d5-7c96c566aa10",[1025,1009],"493e8e9a-433c-4c33-9d79-cec59ec20391",{"title":1027,"slug":1028},"What's In Your Dock","whats-in-your-dock",{"id":942,"slug":1030,"vimeo_id":1031,"description":1032,"tile":1033,"length":213,"resources":12,"people":12,"episode_number":13,"published":1034,"title":1035,"video_transcript_html":1036,"video_transcript_text":1037,"content":12,"seo":1038,"status":19,"episode_people":1039,"recommendations":1041,"season":1042},"geojson-fields","1063283504","Directus comes with numerous types of geospatial data presentation. In this episode, Carmen will walk you through the different fields and how to use them.","bce5fad1-991c-4201-93f1-860f390e90cc","2025-03-11","Using GeoJSON fields in Directus","\u003Cp>Speaker 0: This is my Directus project for tracking all of my hiking activity. And as you can see, I've got a collection called hikes, which is currently empty. Let's take a look at its structure by going to settings, data model, and hikes. Now here we can see we've got two fields in our hikes collection, but I'd love to show on a map where my hike took place. So what we're going to do is create a new field, make it of type map, and call it location.\u003C\u002Fp>\u003Cp>And we can see right now that the type is set to point. But as it turns out, there's lots of types of map data. And in this episode, we're gonna cover each and every one of them. So let's start with point. I'm gonna save this field and then go and create a hike.\u003C\u002Fp>\u003Cp>Gonna click Gonna click on create item. Date set to today. And I'll set my location to be in this part of Vienna. So that's what the point type is. It lets us set a single point on a map.\u003C\u002Fp>\u003Cp>If we take a look at our collection, we'll see we have our type set to point and our coordinates set to these coordinates. Of course, this doesn't look as nice, but we'll cover the layout thereof in another episode. But a point is nice, but a hike is more of a, you know, a journey along a line. What I'm gonna do is create that map type location again, but this time set the type to line string. Go ahead and save that.\u003C\u002Fp>\u003Cp>And let's try again. Set the date to today and go for a hike somewhere in France. And what I can do is by setting a certain number of points, create this line that indicates my hiking journey. So now we can see that we have a series of coordinates, which is good. But what about the other types?\u003C\u002Fp>\u003Cp>Let's once again create that location field and this time set the type to be a polygon. Let's go to Slovakia. So what I can do here is once again, by clicking around, I can now set a polygon to denote which area I've covered with my hike. Go ahead and save that. And we can see once again, the coordinates are set accordingly.\u003C\u002Fp>\u003Cp>Now let's try the multi point type. Go ahead and create a new hike. Now with multi point, I can set a variety of points. Next, let's try the multiline string. Let's go to The United States.\u003C\u002Fp>\u003Cp>Now with a multiline string similar to the line string itself I can now have several strings. The multi polygon location allows us to create multiple polygons on a map. Let's give that a try. Let's go to Argentina. And as you can see, I can create multiple polygons in a map.\u003C\u002Fp>\u003Cp>Let's try the geometry all option. I'm going to save this and create a new hike. Now what the geography all option lets you do is mark all kinds of points. So we can have points, we can have polygons, and we can have lines. Let's cover what the rest of the types look like.\u003C\u002Fp>\u003Cp>First, there's json. And json looks exactly like a geometry all object, but stored as json. A string is also a geometry all object stored as a string of characters, a text. You guessed it. Also a geometry all object, but stored as text.\u003C\u002Fp>\u003Cp>And CSV is a geometry object but stored as CSV or comma separated values. How can I best store my hike? Now, as we said towards the beginning of this, the line string is really going to be my best bet because it's a series of points that I follow during my hike. So these are the different types in which we can store that GeoJSON mapping data in DirectUs.\u003C\u002Fp>","This is my Directus project for tracking all of my hiking activity. And as you can see, I've got a collection called hikes, which is currently empty. Let's take a look at its structure by going to settings, data model, and hikes. Now here we can see we've got two fields in our hikes collection, but I'd love to show on a map where my hike took place. So what we're going to do is create a new field, make it of type map, and call it location. And we can see right now that the type is set to point. But as it turns out, there's lots of types of map data. And in this episode, we're gonna cover each and every one of them. So let's start with point. I'm gonna save this field and then go and create a hike. Gonna click Gonna click on create item. Date set to today. And I'll set my location to be in this part of Vienna. So that's what the point type is. It lets us set a single point on a map. If we take a look at our collection, we'll see we have our type set to point and our coordinates set to these coordinates. Of course, this doesn't look as nice, but we'll cover the layout thereof in another episode. But a point is nice, but a hike is more of a, you know, a journey along a line. What I'm gonna do is create that map type location again, but this time set the type to line string. Go ahead and save that. And let's try again. Set the date to today and go for a hike somewhere in France. And what I can do is by setting a certain number of points, create this line that indicates my hiking journey. So now we can see that we have a series of coordinates, which is good. But what about the other types? Let's once again create that location field and this time set the type to be a polygon. Let's go to Slovakia. So what I can do here is once again, by clicking around, I can now set a polygon to denote which area I've covered with my hike. Go ahead and save that. And we can see once again, the coordinates are set accordingly. Now let's try the multi point type. Go ahead and create a new hike. Now with multi point, I can set a variety of points. Next, let's try the multiline string. Let's go to The United States. Now with a multiline string similar to the line string itself I can now have several strings. The multi polygon location allows us to create multiple polygons on a map. Let's give that a try. Let's go to Argentina. And as you can see, I can create multiple polygons in a map. Let's try the geometry all option. I'm going to save this and create a new hike. Now what the geography all option lets you do is mark all kinds of points. So we can have points, we can have polygons, and we can have lines. Let's cover what the rest of the types look like. First, there's json. And json looks exactly like a geometry all object, but stored as json. A string is also a geometry all object stored as a string of characters, a text. You guessed it. Also a geometry all object, but stored as text. And CSV is a geometry object but stored as CSV or comma separated values. How can I best store my hike? Now, as we said towards the beginning of this, the line string is really going to be my best bet because it's a series of points that I follow during my hike. So these are the different types in which we can store that GeoJSON mapping data in DirectUs.","402feb0f-7f71-4848-80e8-49b37f893bea",[1040],"00297cfc-fe79-4e31-bf92-6f51250f2b1f",[],{"year":399,"number":939,"id":940,"episodes":1043,"show":1044},[942,943,925],{"title":945,"slug":946},{"id":1046,"slug":1047,"vimeo_id":1048,"description":1049,"tile":1050,"length":687,"resources":12,"people":12,"episode_number":213,"published":1051,"title":1052,"video_transcript_html":1053,"video_transcript_text":1054,"content":12,"seo":1055,"status":19,"episode_people":1056,"recommendations":1058,"season":1059},"fee34ecb-a4b6-4218-ac25-d15052c7604d","feedback-widget","1059435866","Bryant builds an embeddable feedback widget for documentation sites that captures user ratings and comments with a decisive 4-point scale. Watch as he creates a complete system with slick animations and wraps it in an iframe-friendly package, then builds a dashboard to visualize feedback metrics with conditional styling.","c0378c84-fc0c-427c-94d2-89cc3bbe7b8b","2025-03-10","Mission: Feedback Widget","\u003Cp>Speaker 0: And we're back with another episode of 100 apps, one hundred hours. I'm your host Brian Gillespie here for Directus. And today, we are building a feedback widget. And you know what they say, if it quacks like a duck, it's probably a duck. So we'll see what the duck is going on today.\u003C\u002Fp>\u003Cp>I've got this amazing hockey jersey that, my brother-in-law gifted me a couple years ago. Rocking that today. So what are we rocking on the build today? We're rocking a feedback widget and I don't often, like, carry over builds from episode to episode. But if you watched the last episode, you probably watched me fail to try and massage a help center template into something that pulls the data remotely, pulls the content remotely from Directus as a CMS instead of local markdown files.\u003C\u002Fp>\u003Cp>Rectified that issue a little bit off scene. And today, we're gonna carry that same theme. We're gonna be build a feedback widget for the documentation site. So if this is your first go with hundred apps hundred hours, you might wanna go back and watch me fail miserably at that one. Maybe not.\u003C\u002Fp>\u003Cp>You'll still get value out of this one. You'll be able to follow along. You don't have to watch that one. But the rules are, we have sixty minutes to plan, sixty minutes to build. Well, it's just sixty minutes total.\u003C\u002Fp>\u003Cp>Plan and build. No more, no less. And then we're gonna use whatever we have at our disposal, which in this case happens to be the last project I was working on. Alright. So let's start the clock and discuss this feedback widget.\u003C\u002Fp>\u003Cp>Right? What are we actually trying to achieve with this thing? Let's discuss the functionality for this. Right? So functionality wise, basically, I want to track feedback on articles within our help center or documentation.\u003C\u002Fp>\u003Cp>And can we make this iframeable or, like, a custom element, I dare say? We'll probably start with iframe. Right. So we basically wanna manage our feedback on the articles within our help center or documentation. And to do that, obviously, we're gonna need a back end.\u003C\u002Fp>\u003Cp>Right? We can statically deploy a website. We could do all of that, but we need somewhere to store this feedback. Directus is gonna make that super simple for us. So what do we have for this specific one?\u003C\u002Fp>\u003Cp>Actually, let's let's talk about our data model before I dive into that. Right? Always helps to kinda map that out. So we already have an articles collection from the last episode. It has a title.\u003C\u002Fp>\u003Cp>Each article has a slug, and it has some content. We have categories for those articles as well. I'm not super worried about the categories. What we are going to add to the mix here is a feedback collection or ratings or what I I don't even know. You could call this a million things.\u003C\u002Fp>\u003Cp>Right? So we wanna give a an objective score, so an actual rating. We wanna have comments. Great. We need to track the article.\u003C\u002Fp>\u003Cp>Right? So we need to have a connection there. Basically, the nope. That's weird. Okay.\u003C\u002Fp>\u003Cp>Left is right and right is left. So we had the rating, we had the comments, we got the article. I think that's pretty much all we need or what we're looking for there. Could be wrong. We probably wanna track, like, the URL as well.\u003C\u002Fp>\u003Cp>Cool. So that's kinda what our data model looks like for this. It shouldn't be anything crazy. We could build, like, a dashboard out of this thing. You know, let's dive in.\u003C\u002Fp>\u003Cp>Right? So going back to the application, if we take a look, we've got this direct to since then it has categories. Great. Categories contain articles. Each article has a title, a slug, some content.\u003C\u002Fp>\u003Cp>You know, we can apply tags to it. We didn't really mess around with tags much on the last episode because never got there. Really struggled to adapt that template. But, hey, this is hundred apps, hundred hours. There's only so much you could do in an hour.\u003C\u002Fp>\u003Cp>And if it goes wrong, it goes wrong. Right? So what are we gonna do first? Let's start working on our back end. You know, you could see this amazing front end I've got here.\u003C\u002Fp>\u003Cp>Basically, this is our look. This is a really beautiful help center. If you don't agree with me, I'm sorry. But this is this thing is amazing. So we got an article called accessing items.\u003C\u002Fp>\u003Cp>There's a header for it. There's a title. Here's the markdown editor. We can actually see what that content looks like side by side. Ayo.\u003C\u002Fp>\u003Cp>It's a markdown editor. This is beautiful. Right? Hit save. Refresh.\u003C\u002Fp>\u003Cp>Boom. There it is. You know, I probably need to add some padding there. Let's just quickly do that. The designer OCD is kicking in.\u003C\u002Fp>\u003Cp>Let's just add a little bit of padding here. Of Of course, I'm a huge Tailwind guy. Tailwind nut. Okay. There we go.\u003C\u002Fp>\u003Cp>Great. Or we could even probably add a divider there. Get a little more separation. View divider or is it u? I'm using the Nuxt UI library.\u003C\u002Fp>\u003Cp>Yeah. There we go. Alright. So there we go. It's beautiful.\u003C\u002Fp>\u003Cp>What we wanna do is plop a big giant feedback widget down here at the bottom of this particular article. And I don't know why I took a screenshot of that, but that's what we're gonna do. First thing we wanna do though, at least on the back end, let's separate these tabs. We'll make this giant yeah, I could call this ratings. I'm inclined to just call it feedback.\u003C\u002Fp>\u003Cp>And, usually, I'm breaking my own convention here where I use, like, plurals for the table or collection names, but, hey, it's feedback. So the this was created on. We could call this the timestamp or, you know, created at. Let's say updated at. K.\u003C\u002Fp>\u003Cp>Status sort. We don't really need any of that. Do we really care the user that this was created by? You know, if you had users that were logged in on the Directus documentation, you know, maybe you do. Oh, I got those backwards created by created at or updated by.\u003C\u002Fp>\u003Cp>Gah. Updated by. Gosh. Alright. Not enough coffee this morning, actually.\u003C\u002Fp>\u003Cp>Alright. So we'll finish the setup of that one. We'll give this a rating, icon, rate. We give it a star, half star, save. Cool.\u003C\u002Fp>\u003Cp>Now, let's add a rating value. Right? We've got a rating. What do we want to give as far as our ratings? You know, do we wanna go three, four, five?\u003C\u002Fp>\u003Cp>I'm more inclined to do, like like, four as as far as the rating. Right? So let's set up a new input here. Maybe this is better served as something like a what do we have for this? We have a slider component.\u003C\u002Fp>\u003Cp>Slider. Slider. There we go. Alright. So the minimum value will say one, the maximum value is four, default value is null.\u003C\u002Fp>\u003Cp>Do we wanna always show the value? Yes. The step interval is one. We can open advanced field creation mode if we want. And then on the display side of it, we can visualize this as a number of stars.\u003C\u002Fp>\u003Cp>We could show it in a simple format, or we could just show the actual stars. So we'll do that. Why go with four? To me, I I hate giving people a middle ground. Right?\u003C\u002Fp>\u003Cp>So if we have bad or let's say, the worst, bad, and then good and great, there's no middle ground. Right? So you gotta get off the fence. We want to add some comments to this. So we'll use just like a text area.\u003C\u002Fp>\u003Cp>It's a great way to see that content, you know, maybe trim the start and end of that, and save. Right? Now what we need is let's just do the URL. We'll track that. I can add a little link input to the icon to the box, actually.\u003C\u002Fp>\u003Cp>And then we'll do what else are we gonna do here? We wanna do a relationship back to the article. Alright. So if we look at this feedback, we got like a rating system. Okay.\u003C\u002Fp>\u003Cp>We can have some comments here. There's a URL. But we probably wanna be able to, you know, see the feedback per article, instead of just by the URL. So what we're gonna do, we'll go to our feedback, and I could do this one of two ways. I can create the relationship inside the feedback, and that's a many to one relationship, or I can go into articles and create a one to many relationship there.\u003C\u002Fp>\u003Cp>You know, if you are creating the many to one and you do want that, inverse relationship, just make sure you check the boxes appropriately. I'm gonna go to articles. We're just gonna use the one to many relationship here. Let's call this feedback the feedback collection. The foreign key is gonna be article.\u003C\u002Fp>\u003Cp>So all the feedback, every single rating is gonna belong to an article, a single article. Like, we can't give the same feedback on multiple articles, basically. And we're gonna do the rating, and maybe we show, like, the comments as well for this. We show a link, and maybe this would be better as actually, like, a table as well. So we see the comments when this was created at and any comments.\u003C\u002Fp>\u003Cp>Okay. We will sort by the ratings. We could sort ascending. So, yes, let's show the worst ones first. Amazing.\u003C\u002Fp>\u003Cp>Alright. So now we've got feedback. If I go to the feedback collection, you could see we've got the article that it's related to there. And anytime you create that one to many relationship inside Directus, it's going to hide the corresponding mini to one relationship. So we're we're just going to, add that back.\u003C\u002Fp>\u003Cp>Great. And we probably wanna track, like, the URL here, like a fully qualified URL. This would be great for something like, hey, I've got development and I have production, where obviously you're gonna have different URLs for that. So you could filter that out even though it may be on like the same article locally. Or you could get around that a couple different ways by, you know, not even storing development feedback or, you know, some other type of system.\u003C\u002Fp>\u003Cp>But now if we go here, I should be able to go in and create feedback on this. You know, hey. This article is not beautiful, sir. And that is gonna be what? API HTTP local host 3,000 API slash accessing, is it two?\u003C\u002Fp>\u003Cp>Two c's? Accessing items. Right? Great. I've added some feedback.\u003C\u002Fp>\u003Cp>There it is. Boom. I can see that feedback now. I can see the URL. I can see the article.\u003C\u002Fp>\u003Cp>We got all of that. Great. But we don't want people to log in to Directus to actually interact with this feedback or leave us feedback. There's our ratings, the stars. Looks that looks really nice.\u003C\u002Fp>\u003Cp>Right? So what are we gonna do? We're gonna build that on the front end. Right? Cool.\u003C\u002Fp>\u003Cp>So if we are to interact with our API now, right, I could go in and basically, I could see the articles. I could see feedback and, you you know, I could do something like this in my params where I call fields and I do feedback.asterisk. So this first asterisk will give me all the root level fields. The second one will give me all the second level or the the root level fields for the feedback collection. If I actually save it there actually make that call, feedback.star.\u003C\u002Fp>\u003Cp>Great. So here I could see the actual ratings. Right? But if I were to actually try to post a rating or anything like that, at least publicly. Right?\u003C\u002Fp>\u003Cp>If I copy this same URL great. Open an incognito window. I am not even actually getting the info. Alright. I can't see the ratings.\u003C\u002Fp>\u003Cp>You know, previously, I had set up public permissions for the articles. I can't see the ratings for the feedback. And the reason for that is bada bing bada boom. Quack, quack, quack. Because we have, restrictive permissions, basically.\u003C\u002Fp>\u003Cp>Whenever you create new collections inside Directus, we don't add permissions for those by default. So we keep that data secure. So what we're gonna do, we'll go into our access policy here and, you know, this might not necessarily be what I would go to production with, but I'm gonna let people create feedback here and, you know, maybe I let them read the ID of the feedback. So as far as field permissions, I don't want them to be able to read our feedback. Maybe just get the ID after they've submitted that.\u003C\u002Fp>\u003Cp>Right? So we'll let them create feedback and then they can read the feedback of the ID they've created. Now a couple other ways we could do this, you know, if this was like a a static site, you could use like Vercel or Netlify functions that that you could potentially, you know, proxy to the direct us back end. You know, just a a a ton of different ways you could do this and you could have like a a separate role that controls the communication to the direct assistance. But for now, this will work.\u003C\u002Fp>\u003Cp>This will be fine. We're just gonna let anybody create that feedback. Right? So now let's start in on our feedback component. I'm just gonna create a new component.\u003C\u002Fp>\u003Cp>We'll call it feedback. Should we call it widget? Love widgets. Who doesn't love widgets? Alright.\u003C\u002Fp>\u003Cp>So I'm a script script tag first guy. Not sure if you are or not, but alright. So what do we actually need to define our you know, let's let's start with, like, props. Right? What what do we want for our props?\u003C\u002Fp>\u003Cp>The props are going to be define props. We do want an article ID. That's a that's a great prop to have. We do want to have the URL or should we we should be able to get that, actually. So I think all we actually need here as far as props is an article ID, and then we can get the current URL.\u003C\u002Fp>\u003Cp>This should be like use request URL from Nuxt. Use request URL. Let's just check their documentation. Use request URL. There's a helper function that returns the URL object working on both client side and user get request URL.\u003C\u002Fp>\u003Cp>Okay. So there we've got the request URL. Cool. And then, yeah, maybe we'll wanna define our ratings. Right?\u003C\u002Fp>\u003Cp>Const ratings, ratings map, or something like that. So we'll give a label. I I like using emojis for this. Let's do mad. Maybe we just use a trash can, like value trash.\u003C\u002Fp>\u003Cp>No. I'm using cursor here as the IDE. I I I like the auto completion for this, basically. Sometimes it's very finicky. Sometimes it's not.\u003C\u002Fp>\u003Cp>Yeah. It seems like we've come up with a good scale here. Cool. There's our ratings map. What else do we need here?\u003C\u002Fp>\u003Cp>We probably wanna track some comments. Comments equals ref string. No. That's just gonna be a string. We'll just, leave that as an empty string for now.\u003C\u002Fp>\u003Cp>And then let's go in and just like build a comments component. Right? And since that we're using cursor, let's just see what the sync can actually do. I'm just gonna hit command l and let's say build a feedback widget with the ratings map. Make it look beautiful.\u003C\u002Fp>\u003Cp>This is not how I've been prompting this thing, but build the, let's say, the template portion of the feedback widget with the ratings map. Make it look beautiful. Alright. So we can see here it's going through creating some code for us. I think it's using Claude three point five Sonnet here.\u003C\u002Fp>\u003Cp>There's our submit button. And I've got the Tailwind UI library here. So, you know, we could probably let's let's just see what it comes up with first. Feedback widget, app widget. Oh, no.\u003C\u002Fp>\u003Cp>That is not the right one. We want to what is going on here? Okay. Maybe we just copy paste this code here. It seems like I need to update the index for this or something like that.\u003C\u002Fp>\u003Cp>So we'll just copy paste. Great. There's our feedback widget. It's doing something. Obviously, styled with Tailwind.\u003C\u002Fp>\u003Cp>It's probably not gonna match what we've got, but we'll go to our slug. So here's our actual page. We've got some messy, messy stuff going on here. We are actually using the, Nuxt MDC library to just render this markdown content. And then we're gonna add a new separator.\u003C\u002Fp>\u003Cp>We'll add this feedback widget, article ID, this page dot ID. Did we even get the page dot ID? Yeah. We should have that page ID. Alright.\u003C\u002Fp>\u003Cp>So is this beautiful? Yes. Looks beautiful ish. Alright. We could do better.\u003C\u002Fp>\u003Cp>We could do better than this. Alright. Let's zoom out to, like, hundred percent. Yeah. I just kinda cut off here, the shadows, etcetera.\u003C\u002Fp>\u003Cp>We don't really want BG whites. And I think the Nuxt UI library has this uCard we could even drop this thing into. There we go. Let's remove all of this. K.\u003C\u002Fp>\u003Cp>How helpful was this article? Great. So now this is full width. Then we have new buttons. We can remove all of this.\u003C\u002Fp>\u003Cp>The title here, let's change that to label or no. We'll leave that. U buttons. What happens if we click this? Right?\u003C\u002Fp>\u003Cp>What? View button. Key equals rating value. We probably need a rating as well. Right?\u003C\u002Fp>\u003Cp>Rating. Ref number. We'll just leave that set to null to begin with. Number or null. K.\u003C\u002Fp>\u003Cp>And when we click that yeah. There you go. Cursor. We wanna set the rating equal to the rating dot value. Solid.\u003C\u002Fp>\u003Cp>There's the rating. Let's see what we get. Refresh. Something broken? Why are we not seeing anything good here?\u003C\u002Fp>\u003Cp>U buttons. Oh, yeah. U button. Dum dum. There we go.\u003C\u002Fp>\u003Cp>Alright. So now I could see there's our buttons. Those are not the great looking buttons that I want. So let's do, like, a ghost. Okay.\u003C\u002Fp>\u003Cp>There we go. And if I just wanted to show this, right, we could do rating. There's our rating. Is that actually gonna show the rating? Rating, rating, rating, rating.\u003C\u002Fp>\u003Cp>P p tag. Rating is null. Rating is we're using maybe conflicting rating value. There we go. Alright.\u003C\u002Fp>\u003Cp>So now we can see one, two, three, or four. Great. That's helpful. And then we have our extra comments. You know, let's make this giant text five XL.\u003C\u002Fp>\u003Cp>Great. We'll give these some padding too. Glass. P two. P four.\u003C\u002Fp>\u003Cp>Is this gonna give us some padding? Yeah. There we go. Cool. Alright.\u003C\u002Fp>\u003Cp>Looking great. This is trash. This is great. And let's do some additional cleanup here. So then we have a u form field, I think, is the name of this component, that wraps this, that provides us like a label.\u003C\u002Fp>\u003Cp>Additional comments. Okay. Looking great. And then we can do a u text area. V model, there's our comments, feedback comments.\u003C\u002Fp>\u003Cp>We don't need any of this. And then do we actually need we want this to be is block a prop here? It is not. So let's do width equals width full full width. Okay.\u003C\u002Fp>\u003Cp>Cool. Looking great. And then let's ditch this blue button. That'll be you button. Yeah.\u003C\u002Fp>\u003Cp>We can leave the type as submit. We'll just clear this out. Let's make that a block level button. Block and size equals x l. Make it a big button.\u003C\u002Fp>\u003Cp>Alright. And then in this case, I don't know why the overflow is being hidden here. Probably something to do with, like, one of my layouts. Overflow hidden. Yeah.\u003C\u002Fp>\u003Cp>There it is. Let's just solve for that. Right? Okay. So now we're good here.\u003C\u002Fp>\u003Cp>Let's add, like, a ring or something if this is selected. And let's do a little bit of cleanup. Right? So if there's no rating, the if if there's a rating value, we're gonna show the additional comments. So by default, this won't show and then it should expand.\u003C\u002Fp>\u003Cp>Now one of the other libraries that I love to use in my Nuxt projects is called form kit auto animate, auto animate dot form kit. It basically gives us, like, a nice animations and basically like a single line of code. Right? So you add this to your project. They've got a Nuxt module for this.\u003C\u002Fp>\u003Cp>And now if we go up to our card component, so that's where this is at, and I add this directive, v auto animate watch this. Watch this. What's v if if I got this inside a why is this not working as intended? Maybe because of the structure of the card component. And let's just wrap the inside of this in a div and see if that fixes it.\u003C\u002Fp>\u003Cp>Div. V auto animate. I don't wanna make a liar out of myself. Oh, there. Yeah.\u003C\u002Fp>\u003Cp>There we go. Now we got, like, some slick animations. Okay. V f rating dot value. You chose maybe we show, like, the yeah.\u003C\u002Fp>\u003Cp>You chose four out of four. You know, we could potentially even wrap these. And flex, justify between items baseline. Let's see what that looks like. It shows four out of four.\u003C\u002Fp>\u003Cp>Great. And then let's add, like, a conditional class. BG gray. No. I I wanna add a ring to this.\u003C\u002Fp>\u003Cp>Ring two, ring primary, 500. Right. There we go. We've got some additional comets. This is looking nice.\u003C\u002Fp>\u003Cp>Yeah. Solid. Cool. Looks good. We can submit feedback.\u003C\u002Fp>\u003Cp>If we actually we're not showing the additional comments, and then we submit the feedback. Right? Right now, this does absolutely nothing. So what we're gonna do, let's add a click. We're gonna submit the feedback, and then we'll go up and actually add a submit feedback function.\u003C\u002Fp>\u003Cp>Right? So we'll call this an async function because we're gonna be calling the directus API with this. We'll call it submit feedback. And, yeah, we don't really need to pass it any props. We can just pick those up from this widget.\u003C\u002Fp>\u003Cp>We got the current URL. Cool. Alright. So here's where we're gonna come back and use our direct us SDK. We're gonna do import create item from the Directus SDK.\u003C\u002Fp>\u003Cp>Great. And we're also going to pull in this Directus plugin from Nuxt app. So, you missed that part on the last episode where we set this up. Basically, I've got this simple Nuxt plug in. It pulls the Directus URL from the Nuxt runtime config, creates a rest client.\u003C\u002Fp>\u003Cp>There's a bunch of commented stuff here to deal with authentication that we're not handling, and then we provide that back to the directus application. So whenever the, like, the first visit occurs, like, this client gets created, and then we can use that same client throughout the entire Nuxt app here, simple integration with the SDK. Alright. So what we're gonna do, we'll wait for the response. You know, maybe we wanna show like a loading state as well.\u003C\u002Fp>\u003Cp>So we'll do loading ref equals false. We'll set loading dot value is equal to true. And then we can see that, like, direct us is our cursor here is trying to auto complete this, which I don't like. I don't like that auto complete. We will do this.\u003C\u002Fp>\u003Cp>So we're gonna call direct us dot request, and then we're gonna pass the method into here. So we're gonna create an item. We're gonna create an item in the feedback collection, and then we're gonna pass the body of that item. So we're gonna pass the URL, which is gonna be current URL. I don't know if that's gonna be value or, you know, let's make sure that's a string.\u003C\u002Fp>\u003Cp>Then we're gonna pass the rating, which is gonna be rating value dot value. And then we have comments dot value. And we could, you know, potentially wrap this and try catch, just so we could do error handling if we want to. Create item feedback. Yeah.\u003C\u002Fp>\u003Cp>Okay. Great. And then finally, set the loading value to false. Now I can also pass that loading prop here, And we could even use, like, the view shorthand syntax as well. Okay.\u003C\u002Fp>\u003Cp>So, let's say disabled unless there's a rating value. If there's no rating value, then this is disabled. Right? So if we haven't picked anything, we can't click the button. Cool.\u003C\u002Fp>\u003Cp>Alright. So now if we click the button, we could do that. We can add some comments. If I watch the network request tab here, we submit that. We see some data coming back.\u003C\u002Fp>\u003Cp>We got the ID. There's the payload. Then we can go in and actually check and see whether we got our feedback or not. Cool. Alright.\u003C\u002Fp>\u003Cp>So what did we miss? We got the URL. We got the rating. The rating sucks. We didn't add any comments.\u003C\u002Fp>\u003Cp>Okay? We forgot to pass the article. Right? So the article equals the props. Article ID, and that should be required.\u003C\u002Fp>\u003Cp>Cool. Let's try this one more time. Right? So this is an amazing article. This is amazing, Bryant.\u003C\u002Fp>\u003Cp>Alright. So there we go. We can see how quick that is. Obviously, that's local. There's our feedback we submitted.\u003C\u002Fp>\u003Cp>Let's go back to our feedback collection. This is amazing. Bryant, here's our comments. Here's our rating. This looks great.\u003C\u002Fp>\u003Cp>Alright? There is our feedback widget. We could do a lot to improve this, obviously. But what if we wanted to, like, maybe embed this? Right?\u003C\u002Fp>\u003Cp>Could we iframe this? You know, Vue is a, like, a good framework for web components. I've not really dove into a lot of, like, the custom elements and, like, web component stuff with Vue. I don't really wanna tackle it on this. That could probably be its own episode.\u003C\u002Fp>\u003Cp>But what if we put this feedback widget on its own routes and we just try to embed it into other pages? So let's do this. We will create a new route. Let's call it feedback.view. Cool.\u003C\u002Fp>\u003Cp>We'll initiate, like, the base setup here. And you think I would set up, like, my snippets correctly at this point, but I have not. And then what are we gonna do? We're gonna use the routes, and we're gonna get the article ID. Actually, is it like a use query or we could just call query from this, but I don't think Nuxt provides like a use query.\u003C\u002Fp>\u003Cp>Let's call the article ID equals route.query. Article ID. K. And then we're gonna just drop this feedback widget in here. It's almost like talking out loud.\u003C\u002Fp>\u003Cp>This thing knows what I wanna do. And then we should probably pass the blank layout for this because otherwise, if I go here and I go to feedback, we're gonna get the rest of this, like my header and everything else. Right? So then I could go in here and const, no. It's actually define page meta meta layout equals blank.\u003C\u002Fp>\u003Cp>Does that give us what we want? Cool. Awesome. Alright. Now let's make sure v f article ID to v else, no article ID provided.\u003C\u002Fp>\u003Cp>So there's no article ID. And then if we do something like this, article ID equals test. Okay. So now we got that. Maybe we want to pass the current URL.\u003C\u002Fp>\u003Cp>Can an iframe get the current URL? Great question for, great question for AI, I think. Can the can an iframe get the URL of the host? Using JavaScript only works with the same origin policy blah blah blah. Yeah.\u003C\u002Fp>\u003Cp>So maybe we wanna pass, like, the URL. Cool. Alright. And then that means we may need to adjust our feedback widget just a little bit. So feedback widget.\u003C\u002Fp>\u003Cp>We can add a URL prop. This could be an optional prop. That'll be a string. And equals URL or props dot URL if that exists or we're gonna use the request URL. Cool.\u003C\u002Fp>\u003Cp>Cool. Great. Yeah. So now if I try this, this should throw an error coming back from Directus because I'm not using a proper ID. Right?\u003C\u002Fp>\u003Cp>Unexpected error occurred, and that's because the article ID that I passed here is not correct. Right, because there is no existing article ID there. So we could, you know, do something like making that optional or making that null if there is no article ID, I guess. But, you know, that's not really what we're after here. We just wanna stick this in an iframe.\u003C\u002Fp>\u003Cp>Alright. Cool. So now let's just open up a new can we do, like, HTML? Yeah. There we go.\u003C\u002Fp>\u003Cp>Div id app h one. Here's a you know what? Why am I not just relying on AI? Create a sample web page about hundred apps, hundred hours. Not a a Vue app, just a standard HTML five page.\u003C\u002Fp>\u003Cp>And it is using Tailwind as well, of course. Yes. We'll just copy this guy. There we go. Alright.\u003C\u002Fp>\u003Cp>This is a weather dashboard. We're just gonna drop this on my desktop, index. HTML, along for the days of index HTML sometimes. Alright. And then how do we get this to open up?\u003C\u002Fp>\u003Cp>Here's our weather dashboard. Yeah. Who knows? This this is amazing. Now, right, what if we try to add this iframe?\u003C\u002Fp>\u003Cp>Iframe local host feedback article URL ID blah blah blah. Is this actually gonna work? Okay. That's great. We've got, like, this.\u003C\u002Fp>\u003Cp>I wish probably do we wanna set a height for this? Do we need to set a height? Height equals, I don't know, 300 px. Okay. So the styling is not necessarily what we need out of this.\u003C\u002Fp>\u003Cp>MTE four. Refresh. Okay. Cool. In the feedback widget, let's say BG transparent.\u003C\u002Fp>\u003Cp>Where is that coming from? Where is the white coming from? Where are you coming from, friend? Is that just coming from, like, app dot view or something? No.\u003C\u002Fp>\u003Cp>The default layout blank. Style dot, let's just do HTML and body, background color equals transparent. There we go. Okay. That gets what we want for the blank.\u003C\u002Fp>\u003Cp>Now this is looking good. And we can actually embed this. Right? Now, let's set the height to maybe 600 pixels. Give this enough room to expand.\u003C\u002Fp>\u003Cp>Okay. So now we've got this iframed in here. Will this actually work when we submit feedback? Feedback local host errors are expected. Right?\u003C\u002Fp>\u003Cp>Because we don't have the proper ID, it's not sending that to direct us correctly. So, you know, maybe we update this widget. Blah blah blah. Undefined props. Article ID or undefined.\u003C\u002Fp>\u003Cp>Feedback. What are we trying to send? Article is one. Okay. So we are sending an article there.\u003C\u002Fp>\u003Cp>So if we go into our index.HTML, maybe we just remove this. K. We pass our URL. Page not found. Feedback.\u003C\u002Fp>\u003Cp>Oh, there you go. No article ID provided. Feedback. Let's just remove that piece. We don't really need that.\u003C\u002Fp>\u003Cp>Okay. Cool. There it is. There's our widget. We can submit feedback on this.\u003C\u002Fp>\u003Cp>Cool. There it is. Is this coming through okay? Local host. Yeah.\u003C\u002Fp>\u003Cp>So there we go. We've got our URL there. Cool. What's what's next? Right?\u003C\u002Fp>\u003Cp>We need, like, a success state for this thing, and then we could probably call this one gravy. Right? Let's add just like a success state. Success ref equals false. And then success equals true.\u003C\u002Fp>\u003Cp>And then we go down here and, all of our submit stuff. Right? We're just gonna wrap this in a template. V if no success, v else. Thanks for your feedback.\u003C\u002Fp>\u003Cp>And you can see why I like cursor. Like, once you get enough of your logic in there, it it does start to, you know, be very helpful as far as auto completion. This is a pretty cool episode. Right? Quack.\u003C\u002Fp>\u003Cp>Quack. Quack. There we go. We'll hit submit. There you go.\u003C\u002Fp>\u003Cp>Thanks for your feedback. We're good. You know, we could get even more sophisticated with this as I roll away. We could, you know, potentially, like, add a cookie or something to track whether they submitted this. And if they've already submitted feedback on the article, not let them submit again.\u003C\u002Fp>\u003Cp>I don't think that's really necessary. Right? Let's use the remaining time that we have to just, like, quickly build, like, a dashboard to see this feedback. So we'll go in, let's call this our feedback dashboard or help center dashboard. Cool.\u003C\u002Fp>\u003Cp>We'll add insights for this. So we'll just create a new panel for it. Let's show just a list of, like, the total feedback. Right? The field is gonna be ID.\u003C\u002Fp>\u003Cp>We'll count these. What is the filter gonna be? Do we actually need a filter? No. We don't.\u003C\u002Fp>\u003Cp>We'll just say total feedback submissions. Great. And boom. Now we get a metric panel that shows us the total number of feedback submissions. Let's add a new one, where we track, like, the average rating.\u003C\u002Fp>\u003Cp>Right? So I could do this in a metric. Let's see if we can get this meter component, what this will actually show, what it'll look like. So we got the primary field here is gonna be rating or the the field that we're summarizing. We want to average that.\u003C\u002Fp>\u003Cp>And the max is gonna be four. And there's our color, Filter, Stroke, Medium, Conditional Styles. Yeah. If the is less than or equal to two is red. Let's do, like, yellow for less than or equal to three.\u003C\u002Fp>\u003Cp>And then, otherwise, maybe this is green? I'm not sure what this is gonna show. Average rating, 60%. Okay. So this is showing, like, a percentage of this.\u003C\u002Fp>\u003Cp>Right? And that's conditional. So if I go in and I were to I'm not saying you should do this ever, and you'd probably wanna lock down the permissions for this to make it noneditable after the fact. Alright. I go into our help center.\u003C\u002Fp>\u003Cp>Now we see 85. So I'm guessing the value here Where's that coming in at? Oh, this should be green. Right. Cool.\u003C\u002Fp>\u003Cp>So, you know, 85%. Probably, the meter is not the best function for that. We should just use the average here. Sort field. We don't really need a sort field for that.\u003C\u002Fp>\u003Cp>Maximum decimals. Let's go to, like, one decimal. And average rating. Let's see what that comes up of. There we go.\u003C\u002Fp>\u003Cp>So average rating is 3.4. I can open this up over here. Or can I? Let's do this. There we go.\u003C\u002Fp>\u003Cp>And then I can blast some feedback. Test. Oh, yeah. You need to actually do an article, though, don't you, Brian? API slash accessing items.\u003C\u002Fp>\u003Cp>Alright. So now we could blast some feedback on this. This sucks. Boom. Just refresh.\u003C\u002Fp>\u003Cp>I don't suggest spamming your own help center. But then we could see that score change, obviously, because there are eight total submissions for this thing. Let's keep building out. Right? Maybe we wanna see a list of the individual articles and their feedback.\u003C\u002Fp>\u003Cp>Can we do that? Like a list of, here's the feedback. And we give a list. We want to show the URL. We wanna show the rating.\u003C\u002Fp>\u003Cp>And we want to, like, comments are gonna get a bit tricky there, but we can sort by when they were created at. Makes sense to me. We'll sort by descending, so we'll see the new ones first. List of feedback. There it is.\u003C\u002Fp>\u003Cp>Cool. Again, that designer OCD is kicking in, so let's make this same width. And one of the the finer points about the dashboards here is when you slide them together, the border radius, snaps. So here we can see all the feedback. Great.\u003C\u002Fp>\u003Cp>I can open this up and see the actual ratings. Let's go in and just solve that. That's going to aggravate me. Like, what I can do here is, I can disable editing for this. So this will control editing from the Directus Studio here.\u003C\u002Fp>\u003Cp>Now if I wanted to control, like, whether you can update this or not via the API, that would be in the permission settings. You'd probably wanna set that up to where, like, certain team members cannot edit this value. But, now you can see, like, all this is locked down when I open it up inside the studio. And, you know, if I open it here through the dashboard, it's not gonna show either. So it's a great way to do that.\u003C\u002Fp>\u003Cp>One of the last things that I just wanna showcase here is the ability to add, like, a global variables and global relational variables. So let's just create a global variable called time or date from, and let's make this a time stamp. We'll use, like, a default value of, I don't know, November 1. Daytime, 24 value. DateFrom.\u003C\u002Fp>\u003Cp>Okay. So we define a key here. Right? This is cool. Define the key.\u003C\u002Fp>\u003Cp>And obviously I created all these today so I'm not sure how impressive this is going to be. But what I could do here is when I set up like this list of feedback, I could go in and define my filters. So I wanna do createdAt is greater than or is in between, let's say, date from, and let's do dollar sign now. Cool. Dollar sign now.\u003C\u002Fp>\u003Cp>Okay. If we do November 27, December third as the date from, you know, if I go here, you could see that that is filtered out. And, likewise, I can apply the same filter. Copy raw value. Paste raw value.\u003C\u002Fp>\u003Cp>Paste raw value. Paste raw value. Paste raw value. And you can see this is just the, these are the standard filter rules inside Directus. You could find all those in the documentation.\u003C\u002Fp>\u003Cp>And we go back through November. Right. I see a list of all the items, and, obviously, that updates all the different metrics. Right? The other cool thing that I can do is add a relational.\u003C\u002Fp>\u003Cp>I was trying to combine relational and variable there. So let's call this the the actual article. Right? So I pick an article and let's call this the, let's use the title of the article in our display template for this. Pick an Article.\u003C\u002Fp>\u003Cp>K. Cool. And this will give us a drop down where we can pick an article. Cool. Great.\u003C\u002Fp>\u003Cp>There's accessing items. Doesn't do anything though. Right? So we're gonna do the same thing where we add a filter for this. So the article, the should be like article ID equals article.\u003C\u002Fp>\u003Cp>So because we've defined that that variable here, this is accessible anywhere on this dashboard. Alright. And as soon as I start to add that you can see Article that it adjusts the values that we're seeing. Article ID equals Article. Awesome.\u003C\u002Fp>\u003Cp>So now I'm only seeing the data that is in this date range and through this specific article. So if I pick WIP, there's nothing there for that one because that one is not published. And now I could see, boom, we have the feedback for our specific article. That's it. This is our amazing feedback widget episode on 100 apps, one hundred hours.\u003C\u002Fp>\u003Cp>What's more to say? I think this is a a great tutorial, something that, you guys can take away and, could be helpful in a lot of different contexts. So let's call that a win. Eight minutes and thirty seconds left on the clock. Roll the beautiful bean footage as they say.\u003C\u002Fp>\u003Cp>We'll catch you on the next episode of 100 apps, one hundred hours. We'll see you.\u003C\u002Fp>","And we're back with another episode of 100 apps, one hundred hours. I'm your host Brian Gillespie here for Directus. And today, we are building a feedback widget. And you know what they say, if it quacks like a duck, it's probably a duck. So we'll see what the duck is going on today. I've got this amazing hockey jersey that, my brother-in-law gifted me a couple years ago. Rocking that today. So what are we rocking on the build today? We're rocking a feedback widget and I don't often, like, carry over builds from episode to episode. But if you watched the last episode, you probably watched me fail to try and massage a help center template into something that pulls the data remotely, pulls the content remotely from Directus as a CMS instead of local markdown files. Rectified that issue a little bit off scene. And today, we're gonna carry that same theme. We're gonna be build a feedback widget for the documentation site. So if this is your first go with hundred apps hundred hours, you might wanna go back and watch me fail miserably at that one. Maybe not. You'll still get value out of this one. You'll be able to follow along. You don't have to watch that one. But the rules are, we have sixty minutes to plan, sixty minutes to build. Well, it's just sixty minutes total. Plan and build. No more, no less. And then we're gonna use whatever we have at our disposal, which in this case happens to be the last project I was working on. Alright. So let's start the clock and discuss this feedback widget. Right? What are we actually trying to achieve with this thing? Let's discuss the functionality for this. Right? So functionality wise, basically, I want to track feedback on articles within our help center or documentation. And can we make this iframeable or, like, a custom element, I dare say? We'll probably start with iframe. Right. So we basically wanna manage our feedback on the articles within our help center or documentation. And to do that, obviously, we're gonna need a back end. Right? We can statically deploy a website. We could do all of that, but we need somewhere to store this feedback. Directus is gonna make that super simple for us. So what do we have for this specific one? Actually, let's let's talk about our data model before I dive into that. Right? Always helps to kinda map that out. So we already have an articles collection from the last episode. It has a title. Each article has a slug, and it has some content. We have categories for those articles as well. I'm not super worried about the categories. What we are going to add to the mix here is a feedback collection or ratings or what I I don't even know. You could call this a million things. Right? So we wanna give a an objective score, so an actual rating. We wanna have comments. Great. We need to track the article. Right? So we need to have a connection there. Basically, the nope. That's weird. Okay. Left is right and right is left. So we had the rating, we had the comments, we got the article. I think that's pretty much all we need or what we're looking for there. Could be wrong. We probably wanna track, like, the URL as well. Cool. So that's kinda what our data model looks like for this. It shouldn't be anything crazy. We could build, like, a dashboard out of this thing. You know, let's dive in. Right? So going back to the application, if we take a look, we've got this direct to since then it has categories. Great. Categories contain articles. Each article has a title, a slug, some content. You know, we can apply tags to it. We didn't really mess around with tags much on the last episode because never got there. Really struggled to adapt that template. But, hey, this is hundred apps, hundred hours. There's only so much you could do in an hour. And if it goes wrong, it goes wrong. Right? So what are we gonna do first? Let's start working on our back end. You know, you could see this amazing front end I've got here. Basically, this is our look. This is a really beautiful help center. If you don't agree with me, I'm sorry. But this is this thing is amazing. So we got an article called accessing items. There's a header for it. There's a title. Here's the markdown editor. We can actually see what that content looks like side by side. Ayo. It's a markdown editor. This is beautiful. Right? Hit save. Refresh. Boom. There it is. You know, I probably need to add some padding there. Let's just quickly do that. The designer OCD is kicking in. Let's just add a little bit of padding here. Of Of course, I'm a huge Tailwind guy. Tailwind nut. Okay. There we go. Great. Or we could even probably add a divider there. Get a little more separation. View divider or is it u? I'm using the Nuxt UI library. Yeah. There we go. Alright. So there we go. It's beautiful. What we wanna do is plop a big giant feedback widget down here at the bottom of this particular article. And I don't know why I took a screenshot of that, but that's what we're gonna do. First thing we wanna do though, at least on the back end, let's separate these tabs. We'll make this giant yeah, I could call this ratings. I'm inclined to just call it feedback. And, usually, I'm breaking my own convention here where I use, like, plurals for the table or collection names, but, hey, it's feedback. So the this was created on. We could call this the timestamp or, you know, created at. Let's say updated at. K. Status sort. We don't really need any of that. Do we really care the user that this was created by? You know, if you had users that were logged in on the Directus documentation, you know, maybe you do. Oh, I got those backwards created by created at or updated by. Gah. Updated by. Gosh. Alright. Not enough coffee this morning, actually. Alright. So we'll finish the setup of that one. We'll give this a rating, icon, rate. We give it a star, half star, save. Cool. Now, let's add a rating value. Right? We've got a rating. What do we want to give as far as our ratings? You know, do we wanna go three, four, five? I'm more inclined to do, like like, four as as far as the rating. Right? So let's set up a new input here. Maybe this is better served as something like a what do we have for this? We have a slider component. Slider. Slider. There we go. Alright. So the minimum value will say one, the maximum value is four, default value is null. Do we wanna always show the value? Yes. The step interval is one. We can open advanced field creation mode if we want. And then on the display side of it, we can visualize this as a number of stars. We could show it in a simple format, or we could just show the actual stars. So we'll do that. Why go with four? To me, I I hate giving people a middle ground. Right? So if we have bad or let's say, the worst, bad, and then good and great, there's no middle ground. Right? So you gotta get off the fence. We want to add some comments to this. So we'll use just like a text area. It's a great way to see that content, you know, maybe trim the start and end of that, and save. Right? Now what we need is let's just do the URL. We'll track that. I can add a little link input to the icon to the box, actually. And then we'll do what else are we gonna do here? We wanna do a relationship back to the article. Alright. So if we look at this feedback, we got like a rating system. Okay. We can have some comments here. There's a URL. But we probably wanna be able to, you know, see the feedback per article, instead of just by the URL. So what we're gonna do, we'll go to our feedback, and I could do this one of two ways. I can create the relationship inside the feedback, and that's a many to one relationship, or I can go into articles and create a one to many relationship there. You know, if you are creating the many to one and you do want that, inverse relationship, just make sure you check the boxes appropriately. I'm gonna go to articles. We're just gonna use the one to many relationship here. Let's call this feedback the feedback collection. The foreign key is gonna be article. So all the feedback, every single rating is gonna belong to an article, a single article. Like, we can't give the same feedback on multiple articles, basically. And we're gonna do the rating, and maybe we show, like, the comments as well for this. We show a link, and maybe this would be better as actually, like, a table as well. So we see the comments when this was created at and any comments. Okay. We will sort by the ratings. We could sort ascending. So, yes, let's show the worst ones first. Amazing. Alright. So now we've got feedback. If I go to the feedback collection, you could see we've got the article that it's related to there. And anytime you create that one to many relationship inside Directus, it's going to hide the corresponding mini to one relationship. So we're we're just going to, add that back. Great. And we probably wanna track, like, the URL here, like a fully qualified URL. This would be great for something like, hey, I've got development and I have production, where obviously you're gonna have different URLs for that. So you could filter that out even though it may be on like the same article locally. Or you could get around that a couple different ways by, you know, not even storing development feedback or, you know, some other type of system. But now if we go here, I should be able to go in and create feedback on this. You know, hey. This article is not beautiful, sir. And that is gonna be what? API HTTP local host 3,000 API slash accessing, is it two? Two c's? Accessing items. Right? Great. I've added some feedback. There it is. Boom. I can see that feedback now. I can see the URL. I can see the article. We got all of that. Great. But we don't want people to log in to Directus to actually interact with this feedback or leave us feedback. There's our ratings, the stars. Looks that looks really nice. Right? So what are we gonna do? We're gonna build that on the front end. Right? Cool. So if we are to interact with our API now, right, I could go in and basically, I could see the articles. I could see feedback and, you you know, I could do something like this in my params where I call fields and I do feedback.asterisk. So this first asterisk will give me all the root level fields. The second one will give me all the second level or the the root level fields for the feedback collection. If I actually save it there actually make that call, feedback.star. Great. So here I could see the actual ratings. Right? But if I were to actually try to post a rating or anything like that, at least publicly. Right? If I copy this same URL great. Open an incognito window. I am not even actually getting the info. Alright. I can't see the ratings. You know, previously, I had set up public permissions for the articles. I can't see the ratings for the feedback. And the reason for that is bada bing bada boom. Quack, quack, quack. Because we have, restrictive permissions, basically. Whenever you create new collections inside Directus, we don't add permissions for those by default. So we keep that data secure. So what we're gonna do, we'll go into our access policy here and, you know, this might not necessarily be what I would go to production with, but I'm gonna let people create feedback here and, you know, maybe I let them read the ID of the feedback. So as far as field permissions, I don't want them to be able to read our feedback. Maybe just get the ID after they've submitted that. Right? So we'll let them create feedback and then they can read the feedback of the ID they've created. Now a couple other ways we could do this, you know, if this was like a a static site, you could use like Vercel or Netlify functions that that you could potentially, you know, proxy to the direct us back end. You know, just a a a ton of different ways you could do this and you could have like a a separate role that controls the communication to the direct assistance. But for now, this will work. This will be fine. We're just gonna let anybody create that feedback. Right? So now let's start in on our feedback component. I'm just gonna create a new component. We'll call it feedback. Should we call it widget? Love widgets. Who doesn't love widgets? Alright. So I'm a script script tag first guy. Not sure if you are or not, but alright. So what do we actually need to define our you know, let's let's start with, like, props. Right? What what do we want for our props? The props are going to be define props. We do want an article ID. That's a that's a great prop to have. We do want to have the URL or should we we should be able to get that, actually. So I think all we actually need here as far as props is an article ID, and then we can get the current URL. This should be like use request URL from Nuxt. Use request URL. Let's just check their documentation. Use request URL. There's a helper function that returns the URL object working on both client side and user get request URL. Okay. So there we've got the request URL. Cool. And then, yeah, maybe we'll wanna define our ratings. Right? Const ratings, ratings map, or something like that. So we'll give a label. I I like using emojis for this. Let's do mad. Maybe we just use a trash can, like value trash. No. I'm using cursor here as the IDE. I I I like the auto completion for this, basically. Sometimes it's very finicky. Sometimes it's not. Yeah. It seems like we've come up with a good scale here. Cool. There's our ratings map. What else do we need here? We probably wanna track some comments. Comments equals ref string. No. That's just gonna be a string. We'll just, leave that as an empty string for now. And then let's go in and just like build a comments component. Right? And since that we're using cursor, let's just see what the sync can actually do. I'm just gonna hit command l and let's say build a feedback widget with the ratings map. Make it look beautiful. This is not how I've been prompting this thing, but build the, let's say, the template portion of the feedback widget with the ratings map. Make it look beautiful. Alright. So we can see here it's going through creating some code for us. I think it's using Claude three point five Sonnet here. There's our submit button. And I've got the Tailwind UI library here. So, you know, we could probably let's let's just see what it comes up with first. Feedback widget, app widget. Oh, no. That is not the right one. We want to what is going on here? Okay. Maybe we just copy paste this code here. It seems like I need to update the index for this or something like that. So we'll just copy paste. Great. There's our feedback widget. It's doing something. Obviously, styled with Tailwind. It's probably not gonna match what we've got, but we'll go to our slug. So here's our actual page. We've got some messy, messy stuff going on here. We are actually using the, Nuxt MDC library to just render this markdown content. And then we're gonna add a new separator. We'll add this feedback widget, article ID, this page dot ID. Did we even get the page dot ID? Yeah. We should have that page ID. Alright. So is this beautiful? Yes. Looks beautiful ish. Alright. We could do better. We could do better than this. Alright. Let's zoom out to, like, hundred percent. Yeah. I just kinda cut off here, the shadows, etcetera. We don't really want BG whites. And I think the Nuxt UI library has this uCard we could even drop this thing into. There we go. Let's remove all of this. K. How helpful was this article? Great. So now this is full width. Then we have new buttons. We can remove all of this. The title here, let's change that to label or no. We'll leave that. U buttons. What happens if we click this? Right? What? View button. Key equals rating value. We probably need a rating as well. Right? Rating. Ref number. We'll just leave that set to null to begin with. Number or null. K. And when we click that yeah. There you go. Cursor. We wanna set the rating equal to the rating dot value. Solid. There's the rating. Let's see what we get. Refresh. Something broken? Why are we not seeing anything good here? U buttons. Oh, yeah. U button. Dum dum. There we go. Alright. So now I could see there's our buttons. Those are not the great looking buttons that I want. So let's do, like, a ghost. Okay. There we go. And if I just wanted to show this, right, we could do rating. There's our rating. Is that actually gonna show the rating? Rating, rating, rating, rating. P p tag. Rating is null. Rating is we're using maybe conflicting rating value. There we go. Alright. So now we can see one, two, three, or four. Great. That's helpful. And then we have our extra comments. You know, let's make this giant text five XL. Great. We'll give these some padding too. Glass. P two. P four. Is this gonna give us some padding? Yeah. There we go. Cool. Alright. Looking great. This is trash. This is great. And let's do some additional cleanup here. So then we have a u form field, I think, is the name of this component, that wraps this, that provides us like a label. Additional comments. Okay. Looking great. And then we can do a u text area. V model, there's our comments, feedback comments. We don't need any of this. And then do we actually need we want this to be is block a prop here? It is not. So let's do width equals width full full width. Okay. Cool. Looking great. And then let's ditch this blue button. That'll be you button. Yeah. We can leave the type as submit. We'll just clear this out. Let's make that a block level button. Block and size equals x l. Make it a big button. Alright. And then in this case, I don't know why the overflow is being hidden here. Probably something to do with, like, one of my layouts. Overflow hidden. Yeah. There it is. Let's just solve for that. Right? Okay. So now we're good here. Let's add, like, a ring or something if this is selected. And let's do a little bit of cleanup. Right? So if there's no rating, the if if there's a rating value, we're gonna show the additional comments. So by default, this won't show and then it should expand. Now one of the other libraries that I love to use in my Nuxt projects is called form kit auto animate, auto animate dot form kit. It basically gives us, like, a nice animations and basically like a single line of code. Right? So you add this to your project. They've got a Nuxt module for this. And now if we go up to our card component, so that's where this is at, and I add this directive, v auto animate watch this. Watch this. What's v if if I got this inside a why is this not working as intended? Maybe because of the structure of the card component. And let's just wrap the inside of this in a div and see if that fixes it. Div. V auto animate. I don't wanna make a liar out of myself. Oh, there. Yeah. There we go. Now we got, like, some slick animations. Okay. V f rating dot value. You chose maybe we show, like, the yeah. You chose four out of four. You know, we could potentially even wrap these. And flex, justify between items baseline. Let's see what that looks like. It shows four out of four. Great. And then let's add, like, a conditional class. BG gray. No. I I wanna add a ring to this. Ring two, ring primary, 500. Right. There we go. We've got some additional comets. This is looking nice. Yeah. Solid. Cool. Looks good. We can submit feedback. If we actually we're not showing the additional comments, and then we submit the feedback. Right? Right now, this does absolutely nothing. So what we're gonna do, let's add a click. We're gonna submit the feedback, and then we'll go up and actually add a submit feedback function. Right? So we'll call this an async function because we're gonna be calling the directus API with this. We'll call it submit feedback. And, yeah, we don't really need to pass it any props. We can just pick those up from this widget. We got the current URL. Cool. Alright. So here's where we're gonna come back and use our direct us SDK. We're gonna do import create item from the Directus SDK. Great. And we're also going to pull in this Directus plugin from Nuxt app. So, you missed that part on the last episode where we set this up. Basically, I've got this simple Nuxt plug in. It pulls the Directus URL from the Nuxt runtime config, creates a rest client. There's a bunch of commented stuff here to deal with authentication that we're not handling, and then we provide that back to the directus application. So whenever the, like, the first visit occurs, like, this client gets created, and then we can use that same client throughout the entire Nuxt app here, simple integration with the SDK. Alright. So what we're gonna do, we'll wait for the response. You know, maybe we wanna show like a loading state as well. So we'll do loading ref equals false. We'll set loading dot value is equal to true. And then we can see that, like, direct us is our cursor here is trying to auto complete this, which I don't like. I don't like that auto complete. We will do this. So we're gonna call direct us dot request, and then we're gonna pass the method into here. So we're gonna create an item. We're gonna create an item in the feedback collection, and then we're gonna pass the body of that item. So we're gonna pass the URL, which is gonna be current URL. I don't know if that's gonna be value or, you know, let's make sure that's a string. Then we're gonna pass the rating, which is gonna be rating value dot value. And then we have comments dot value. And we could, you know, potentially wrap this and try catch, just so we could do error handling if we want to. Create item feedback. Yeah. Okay. Great. And then finally, set the loading value to false. Now I can also pass that loading prop here, And we could even use, like, the view shorthand syntax as well. Okay. So, let's say disabled unless there's a rating value. If there's no rating value, then this is disabled. Right? So if we haven't picked anything, we can't click the button. Cool. Alright. So now if we click the button, we could do that. We can add some comments. If I watch the network request tab here, we submit that. We see some data coming back. We got the ID. There's the payload. Then we can go in and actually check and see whether we got our feedback or not. Cool. Alright. So what did we miss? We got the URL. We got the rating. The rating sucks. We didn't add any comments. Okay? We forgot to pass the article. Right? So the article equals the props. Article ID, and that should be required. Cool. Let's try this one more time. Right? So this is an amazing article. This is amazing, Bryant. Alright. So there we go. We can see how quick that is. Obviously, that's local. There's our feedback we submitted. Let's go back to our feedback collection. This is amazing. Bryant, here's our comments. Here's our rating. This looks great. Alright? There is our feedback widget. We could do a lot to improve this, obviously. But what if we wanted to, like, maybe embed this? Right? Could we iframe this? You know, Vue is a, like, a good framework for web components. I've not really dove into a lot of, like, the custom elements and, like, web component stuff with Vue. I don't really wanna tackle it on this. That could probably be its own episode. But what if we put this feedback widget on its own routes and we just try to embed it into other pages? So let's do this. We will create a new route. Let's call it feedback.view. Cool. We'll initiate, like, the base setup here. And you think I would set up, like, my snippets correctly at this point, but I have not. And then what are we gonna do? We're gonna use the routes, and we're gonna get the article ID. Actually, is it like a use query or we could just call query from this, but I don't think Nuxt provides like a use query. Let's call the article ID equals route.query. Article ID. K. And then we're gonna just drop this feedback widget in here. It's almost like talking out loud. This thing knows what I wanna do. And then we should probably pass the blank layout for this because otherwise, if I go here and I go to feedback, we're gonna get the rest of this, like my header and everything else. Right? So then I could go in here and const, no. It's actually define page meta meta layout equals blank. Does that give us what we want? Cool. Awesome. Alright. Now let's make sure v f article ID to v else, no article ID provided. So there's no article ID. And then if we do something like this, article ID equals test. Okay. So now we got that. Maybe we want to pass the current URL. Can an iframe get the current URL? Great question for, great question for AI, I think. Can the can an iframe get the URL of the host? Using JavaScript only works with the same origin policy blah blah blah. Yeah. So maybe we wanna pass, like, the URL. Cool. Alright. And then that means we may need to adjust our feedback widget just a little bit. So feedback widget. We can add a URL prop. This could be an optional prop. That'll be a string. And equals URL or props dot URL if that exists or we're gonna use the request URL. Cool. Cool. Great. Yeah. So now if I try this, this should throw an error coming back from Directus because I'm not using a proper ID. Right? Unexpected error occurred, and that's because the article ID that I passed here is not correct. Right, because there is no existing article ID there. So we could, you know, do something like making that optional or making that null if there is no article ID, I guess. But, you know, that's not really what we're after here. We just wanna stick this in an iframe. Alright. Cool. So now let's just open up a new can we do, like, HTML? Yeah. There we go. Div id app h one. Here's a you know what? Why am I not just relying on AI? Create a sample web page about hundred apps, hundred hours. Not a a Vue app, just a standard HTML five page. And it is using Tailwind as well, of course. Yes. We'll just copy this guy. There we go. Alright. This is a weather dashboard. We're just gonna drop this on my desktop, index. HTML, along for the days of index HTML sometimes. Alright. And then how do we get this to open up? Here's our weather dashboard. Yeah. Who knows? This this is amazing. Now, right, what if we try to add this iframe? Iframe local host feedback article URL ID blah blah blah. Is this actually gonna work? Okay. That's great. We've got, like, this. I wish probably do we wanna set a height for this? Do we need to set a height? Height equals, I don't know, 300 px. Okay. So the styling is not necessarily what we need out of this. MTE four. Refresh. Okay. Cool. In the feedback widget, let's say BG transparent. Where is that coming from? Where is the white coming from? Where are you coming from, friend? Is that just coming from, like, app dot view or something? No. The default layout blank. Style dot, let's just do HTML and body, background color equals transparent. There we go. Okay. That gets what we want for the blank. Now this is looking good. And we can actually embed this. Right? Now, let's set the height to maybe 600 pixels. Give this enough room to expand. Okay. So now we've got this iframed in here. Will this actually work when we submit feedback? Feedback local host errors are expected. Right? Because we don't have the proper ID, it's not sending that to direct us correctly. So, you know, maybe we update this widget. Blah blah blah. Undefined props. Article ID or undefined. Feedback. What are we trying to send? Article is one. Okay. So we are sending an article there. So if we go into our index.HTML, maybe we just remove this. K. We pass our URL. Page not found. Feedback. Oh, there you go. No article ID provided. Feedback. Let's just remove that piece. We don't really need that. Okay. Cool. There it is. There's our widget. We can submit feedback on this. Cool. There it is. Is this coming through okay? Local host. Yeah. So there we go. We've got our URL there. Cool. What's what's next? Right? We need, like, a success state for this thing, and then we could probably call this one gravy. Right? Let's add just like a success state. Success ref equals false. And then success equals true. And then we go down here and, all of our submit stuff. Right? We're just gonna wrap this in a template. V if no success, v else. Thanks for your feedback. And you can see why I like cursor. Like, once you get enough of your logic in there, it it does start to, you know, be very helpful as far as auto completion. This is a pretty cool episode. Right? Quack. Quack. Quack. There we go. We'll hit submit. There you go. Thanks for your feedback. We're good. You know, we could get even more sophisticated with this as I roll away. We could, you know, potentially, like, add a cookie or something to track whether they submitted this. And if they've already submitted feedback on the article, not let them submit again. I don't think that's really necessary. Right? Let's use the remaining time that we have to just, like, quickly build, like, a dashboard to see this feedback. So we'll go in, let's call this our feedback dashboard or help center dashboard. Cool. We'll add insights for this. So we'll just create a new panel for it. Let's show just a list of, like, the total feedback. Right? The field is gonna be ID. We'll count these. What is the filter gonna be? Do we actually need a filter? No. We don't. We'll just say total feedback submissions. Great. And boom. Now we get a metric panel that shows us the total number of feedback submissions. Let's add a new one, where we track, like, the average rating. Right? So I could do this in a metric. Let's see if we can get this meter component, what this will actually show, what it'll look like. So we got the primary field here is gonna be rating or the the field that we're summarizing. We want to average that. And the max is gonna be four. And there's our color, Filter, Stroke, Medium, Conditional Styles. Yeah. If the is less than or equal to two is red. Let's do, like, yellow for less than or equal to three. And then, otherwise, maybe this is green? I'm not sure what this is gonna show. Average rating, 60%. Okay. So this is showing, like, a percentage of this. Right? And that's conditional. So if I go in and I were to I'm not saying you should do this ever, and you'd probably wanna lock down the permissions for this to make it noneditable after the fact. Alright. I go into our help center. Now we see 85. So I'm guessing the value here Where's that coming in at? Oh, this should be green. Right. Cool. So, you know, 85%. Probably, the meter is not the best function for that. We should just use the average here. Sort field. We don't really need a sort field for that. Maximum decimals. Let's go to, like, one decimal. And average rating. Let's see what that comes up of. There we go. So average rating is 3.4. I can open this up over here. Or can I? Let's do this. There we go. And then I can blast some feedback. Test. Oh, yeah. You need to actually do an article, though, don't you, Brian? API slash accessing items. Alright. So now we could blast some feedback on this. This sucks. Boom. Just refresh. I don't suggest spamming your own help center. But then we could see that score change, obviously, because there are eight total submissions for this thing. Let's keep building out. Right? Maybe we wanna see a list of the individual articles and their feedback. Can we do that? Like a list of, here's the feedback. And we give a list. We want to show the URL. We wanna show the rating. And we want to, like, comments are gonna get a bit tricky there, but we can sort by when they were created at. Makes sense to me. We'll sort by descending, so we'll see the new ones first. List of feedback. There it is. Cool. Again, that designer OCD is kicking in, so let's make this same width. And one of the the finer points about the dashboards here is when you slide them together, the border radius, snaps. So here we can see all the feedback. Great. I can open this up and see the actual ratings. Let's go in and just solve that. That's going to aggravate me. Like, what I can do here is, I can disable editing for this. So this will control editing from the Directus Studio here. Now if I wanted to control, like, whether you can update this or not via the API, that would be in the permission settings. You'd probably wanna set that up to where, like, certain team members cannot edit this value. But, now you can see, like, all this is locked down when I open it up inside the studio. And, you know, if I open it here through the dashboard, it's not gonna show either. So it's a great way to do that. One of the last things that I just wanna showcase here is the ability to add, like, a global variables and global relational variables. So let's just create a global variable called time or date from, and let's make this a time stamp. We'll use, like, a default value of, I don't know, November 1. Daytime, 24 value. DateFrom. Okay. So we define a key here. Right? This is cool. Define the key. And obviously I created all these today so I'm not sure how impressive this is going to be. But what I could do here is when I set up like this list of feedback, I could go in and define my filters. So I wanna do createdAt is greater than or is in between, let's say, date from, and let's do dollar sign now. Cool. Dollar sign now. Okay. If we do November 27, December third as the date from, you know, if I go here, you could see that that is filtered out. And, likewise, I can apply the same filter. Copy raw value. Paste raw value. Paste raw value. Paste raw value. Paste raw value. And you can see this is just the, these are the standard filter rules inside Directus. You could find all those in the documentation. And we go back through November. Right. I see a list of all the items, and, obviously, that updates all the different metrics. Right? The other cool thing that I can do is add a relational. I was trying to combine relational and variable there. So let's call this the the actual article. Right? So I pick an article and let's call this the, let's use the title of the article in our display template for this. Pick an Article. K. Cool. And this will give us a drop down where we can pick an article. Cool. Great. There's accessing items. Doesn't do anything though. Right? So we're gonna do the same thing where we add a filter for this. So the article, the should be like article ID equals article. So because we've defined that that variable here, this is accessible anywhere on this dashboard. Alright. And as soon as I start to add that you can see Article that it adjusts the values that we're seeing. Article ID equals Article. Awesome. So now I'm only seeing the data that is in this date range and through this specific article. So if I pick WIP, there's nothing there for that one because that one is not published. And now I could see, boom, we have the feedback for our specific article. That's it. This is our amazing feedback widget episode on 100 apps, one hundred hours. What's more to say? I think this is a a great tutorial, something that, you guys can take away and, could be helpful in a lot of different contexts. So let's call that a win. Eight minutes and thirty seconds left on the clock. Roll the beautiful bean footage as they say. We'll catch you on the next episode of 100 apps, one hundred hours. We'll see you.","9c2ff38d-5ab4-4e8b-955a-a083c80f88e4",[1057],"2ba92584-506d-452f-8b3b-ac72790ab0cb",[],{"year":399,"number":1060,"id":1061,"episodes":1062,"show":1072},56,"d6b229fe-38fc-495b-ba0c-c574ebfea38f",[1063,1064,1065,1066,1046,1067,1068,1069,1070,1071],"ec88bef1-fffd-43eb-9d93-3123dc381b97","ab550907-1a5a-4fc0-8208-764465a1864f","97806b9d-063a-447f-9dca-617217ae5879","2a920d49-91e0-4237-bbd7-12e1181ad02a","1ac7647d-0f09-4698-8ca0-e4448a4fe0e9","54789ca9-ca1d-4c25-a4d7-31c32de53d5d","09a64df3-79d6-4ede-a394-30a2499c5fee","bfb4b33c-1494-4111-8f33-d59bfde9df65","36839053-13e7-4eb6-8e73-efb73bf61fb1",{"title":322,"slug":323},{"id":1067,"slug":1074,"vimeo_id":1075,"description":1076,"tile":1077,"length":1078,"resources":12,"people":12,"episode_number":195,"published":1051,"title":1079,"video_transcript_html":1080,"video_transcript_text":1081,"content":12,"seo":1082,"status":19,"episode_people":1083,"recommendations":1085,"season":1086},"internal-knowledge-base","1059436262","Bryant builds a Guru-inspired knowledge base with bite-sized information cards accessible through a Chrome extension. Watch as he creates a Directus backend with verification flows, then builds a browser extension interface that makes company knowledge searchable and editable on the fly—despite a tricky bug that nearly derails the entire project.","900c0ff2-b753-449c-bc21-59ec9cc3f342",66,"Mission: Internal Knowledgebase","\u003Cp>Speaker 0: And we are back with another episode of 100 apps, one hundred hours. I'm your host, Brian Gillespie. Welcome back. I'm here for Directus, by Directus something something. I I don't know.\u003C\u002Fp>\u003Cp>I'm trying to be fancy here. Not my forte, obviously. Dad of three little girls, gave up being cool a long time ago. But today, we have got a cool episode for you. We are going to be tackling an internal knowledge base.\u003C\u002Fp>\u003Cp>I'll explain what that is in a moment, but if you're new to 100 apps, one hundred hours, there are two basic rules. Number one, we have sixty minutes to plan and build your app, your clone, your idea, whatever this is. You get no more, no less. And rule number two, the anti rule. The no holds barred WrestleMania action, use whatever you have at your disposal.\u003C\u002Fp>\u003Cp>This is a no holds barred cage match, basically. So let's talk about internal knowledge base for a moment. When I used to run a customer success team, at a previous company, there was this product called Guru that was basically an internal knowledge base. You know, we used it for a lot of our chat support team. Also our CSMs, you know, if they had a question, we could easily document this.\u003C\u002Fp>\u003Cp>And the cool thing that they did have was this Chrome extension that would basically give you access to all the knowledge base. And, you know, the structure was very bite sized pieces of information, and that was the whole idea behind it. So we're gonna try and replicate something similar. You know, anytime you have something like an internal knowledge base, if it's not in everyone's face, the knowledge goes stale. If it's a pain in the butt to edit, it goes stale.\u003C\u002Fp>\u003Cp>So let's tackle that challenge. We're gonna put sixty minutes on the clock. Let's let's ride. Let's wrassle. Alright.\u003C\u002Fp>\u003Cp>So, functionality for this specific app. Right? And this is me probably biting off a lot more than I can actually chew in sixty minutes. But what we're gonna do, we're gonna build a back end for the internal knowledge base, And then we're going to build a Chrome extension to search search articles, edit articles, and that's probably the extent of maybe what we'll get done in an hour. I don't even know if we'll get this done.\u003C\u002Fp>\u003Cp>Right? But, we need to kinda flush this out further. Let's go in and map some data model. Map this out. Right?\u003C\u002Fp>\u003Cp>So at a high level, we're we're gonna have users of this application, so we'll have some authentication, etcetera. You know, they'll be able to log in. As far as the actual knowledge itself, I like the term cards for this. Instead of articles, that's what we're gonna do. And the reason why is these are bite sized pieces of internal knowledge.\u003C\u002Fp>\u003Cp>I think that's a helpful paradigm. As far as the fields that we want, we're gonna have a title for the card. Maybe I won't even get into, like, AI generated summary of this. We may use some AI stuff to, like, dive into this. Let's have an owner of this card.\u003C\u002Fp>\u003Cp>Like, who owns this knowledge internally? We've got the actual content of the card for sure. You know, maybe we got some tags for the cards as well. And then, of course, we have tags. Let's say there's a title for the tag and then a topic or an area.\u003C\u002Fp>\u003Cp>Right? I think that's a good structure for tags. Like, in in the case of Directus, that may be, like, features or frameworks or something like that. And then we'll have users. So and a user owns a card.\u003C\u002Fp>\u003Cp>Tags are connected to cards in some form or fashion shape, form, or fashion to quote the West Virginia governor here. This is a many to many relationship between cards and tags. Right? A card could have multiple tags. A tag could have multiple cards or a single tag could be applied to multiple cards.\u003C\u002Fp>\u003Cp>So that is the data model setup we are going to roll with. You know, if you've watched this before, I've got, like, a standard Nuxt starter that I use for this. But, on the front end, we're gonna try this Chrome extension Vite starter from, mister Fayezara. Fayez Fayez Ahmed. I think that's his name.\u003C\u002Fp>\u003Cp>Yep. Fayez. Hopefully, I'm pronouncing that right. If I'm not, I apologize. But, let's focus on that back end first.\u003C\u002Fp>\u003Cp>Right? We are going to zoom way in and let's create our first collection. Right? Let's call this cards, and I'm gonna give these a generated UUID. We'll say when the card was created, who created it, when it was updated.\u003C\u002Fp>\u003Cp>What's the status of the card? You know, this could be helpful if we have, like, cards that are draft or not approved or not verified, I would say. That should be like the one of the first states that we have for this. And then we'll add a a sort order, I guess. Why not?\u003C\u002Fp>\u003Cp>Let's see if we could find a card. So this is a looks like a memory card, a SIM card. It's a good icon for this. Great. I'm just gonna hit command s to save this and stay.\u003C\u002Fp>\u003Cp>And let's start with the title. So straight input field string. We do wanna require a value for this. Right? Great.\u003C\u002Fp>\u003Cp>You know, we could potentially index this field if we want to inside the database. That's what that's for. Do we need to trim? We don't need this to be URL safe, so we're gonna roll with that. And then what else do we have?\u003C\u002Fp>\u003Cp>We had an some content for this. Let's go with the WYSIWYG editor. We'll have some content, you know, and for, like, AI heavy stuff, we might even, like, set this to be markdown. Basically, behind the scenes, these use the same type for the column inside the database. And what Directus is doing behind the scenes whenever I add this field is basically just creating a new column inside the cards table inside our Postgres database.\u003C\u002Fp>\u003Cp>So for now, let's, let's go to the markdown interface just to appease our developer friends. And let's say we'll auto format the display for this. Cool. Great. Gravy.\u003C\u002Fp>\u003Cp>Amazing. What else do we need? Right? We wanna assign ownership of this card. And, you know, maybe we even want to have, like, a date verified.\u003C\u002Fp>\u003Cp>So let's take care of the ownership first. Right? I'm gonna add a many to one relationship here. So I'm gonna just call this the owner of this card, the related collection. Whenever you spin up a Directus instance, it gives us, some system collections that it prefixes with Directus so that all of your your SQL stays pure, which is nice.\u003C\u002Fp>\u003Cp>And then we're just going to add the Directus users collection here. So this is what the instance uses for authentication, you know, into the studio and via the API. So we'll just piggyback on that. Right? We'll assign a user to this.\u003C\u002Fp>\u003Cp>And for the display, cool, great, everything looks nice. We'll display should be an option to display the user. We could show the user in a circle. That will look great. And let's move that owner beside this.\u003C\u002Fp>\u003Cp>We want to show the status. Date updated. So there's the status title owner. And maybe we want to switch that around. Basically, what I'm doing here is just controlling the form for anybody using the back end of this.\u003C\u002Fp>\u003Cp>So let's make the title full width just to give that proper weight, proper gravitas. Here's the status. Maybe we wanna update that from draft to you know, it's not necessarily like we're concerned about whether this is published or not. We just want it to be verified. So, like, internally, you wanna make sure that that content is totally verified.\u003C\u002Fp>\u003Cp>So that's what we'll do. We'll call it verified. This is archived. Cool. We could copy these choices and just paste those into our labels.\u003C\u002Fp>\u003Cp>And I don't wanna show as a dot. I just wanna show each actual label. Alright. So next, maybe we add a time field, date time. Let's do this as time stamps.\u003C\u002Fp>\u003Cp>We'll just follow the same convention, date verified. Cool. Awesome. Alright. And we'll come back to that in a moment.\u003C\u002Fp>\u003Cp>This is our cards collection. Right? Let's call this direct us features. I'm gonna assign this to me, the admin user, and we will here's all the amazing features of Directus. Great.\u003C\u002Fp>\u003Cp>REST APIs, GraphQL APIs, APIS APIs. There we go. Insights, permissions, whatever. Right? Great.\u003C\u002Fp>\u003Cp>There's our preview for the markdown. You know, if we decide to do some AI, you should later this is gonna be a a friendlier format for that. Great. We've got a card. Cool.\u003C\u002Fp>\u003Cp>We can move this around. You know, let's add just a a hook to this using some flows to automatically populate the date verified. Right? So, anytime I verify a card, let's say a card is updated, updated or created, card is updated, created. You know, we could be add verification stamp.\u003C\u002Fp>\u003Cp>Alright. And basically, anytime a card is created or, you know, let's just say updated in this point because the default should be like draft, the state for all of the cards. Anytime a card is updated, let's go in and let's make sure check the status. Check status. Alright.\u003C\u002Fp>\u003Cp>We're just gonna create a condition here and this uses the standard, filter rules inside Directus. What I'm gonna do is just copy our template here. We're gonna adjust this a little bit. Payload dot status. We'll drill in one more layer And if the status is equal to verified, we're gonna run some action after this, right.\u003C\u002Fp>\u003Cp>So inside Flows is a great way to add simple automations to your back end And there is flow control already built into flows. Shocker. Right? But if this condition passes, we're going to take action on it. If it doesn't pass, meaning the status is not updated or it's not there, it will, you know, follow the rejection path.\u003C\u002Fp>\u003Cp>So in this case, you know, I I could log something to the console. In this case, I'm just going to, run a little script. Get current time. Right. We're gonna return, what's new date?\u003C\u002Fp>\u003Cp>Is that gonna be two ISO? Is it two ISO string like that? New date to ISO string. Is that what we're looking for? New date to string to ISO string.\u003C\u002Fp>\u003Cp>That should be what I think that's what we're looking for. So we're just gonna return the current time. Great. And then I'm going to update the data. Update card.\u003C\u002Fp>\u003Cp>Cool. So we'll do cards. We can use the permissions from the trigger. And I'm gonna say this should be dollar sign trigger dot keys, the first item in that array. Not a % sure, but, date verified.\u003C\u002Fp>\u003Cp>And then we'll do something like this. We'll just use this mustache syntax, git underscore current time. Great. Cool. Let's see if this actually works.\u003C\u002Fp>\u003Cp>Alright. So this is good. These are all amazing features of Directus. I'm gonna verify this. I hit save.\u003C\u002Fp>\u003Cp>And voila. Now we could see the exact date and time that I verified this thing, December 3 '10 '14 AM. Amazing. Sweet. Right?\u003C\u002Fp>\u003Cp>Alright. Let's go back and continue our March onto our data model here. So let's set up some tags. I'm not really concerned about when these were updated, etcetera, blah blah blah. You know, maybe we wanna sort the tags, but, let's find a tag.\u003C\u002Fp>\u003Cp>We could use the hashtag. That's what tags are for. This is a title. Cool. Title of the tag.\u003C\u002Fp>\u003Cp>That's required. And we'll keep keep updating. We don't wanna change just yet, my friend. And then the topic. Right?\u003C\u002Fp>\u003Cp>What are we gonna call this? Area topic? I think I like topic for this. I still wanna keep the string. I don't think there's a need for extra topics, but what we'll do, we'll just pick the options from a drop down.\u003C\u002Fp>\u003Cp>Right? So in the case of Directus, you know, this could be features features of Directus. Makes sense. Frameworks, like next Nuxt, etcetera. Frameworks.\u003C\u002Fp>\u003Cp>Can we get another f one? What else is this gonna be? API, API, pricing, you know, the pricing of direct you know, these could be infinite, and maybe that's, you know, not not a great one because that could be just like a a side effect. Cool. So now we have tags.\u003C\u002Fp>\u003Cp>We want to create this relationship between the two, and it doesn't really matter where I start here. All roads lead to the many to many relationship. If I'm on the cards collection, I'm gonna create a new field. We're gonna call it keys or I'm sorry. Call the key is gonna be tags.\u003C\u002Fp>\u003Cp>I need more coffee today. The related collection is gonna be tags. We don't wanna allow duplicate tags. We wanna show a link to this item. But what I wanna do here, I'm gonna open up the advanced field creation mode, and you could see what's going on behind the scenes.\u003C\u002Fp>\u003Cp>Directus is creating a junction collection for us. I'm gonna change the names of these just for my own anxiety, I guess. So we're gonna call this card tags. So these are tags on a card. The junction field or the the foreign key is card for the card, tag for the tag, and then we're gonna add this corresponding field to tags as well.\u003C\u002Fp>\u003Cp>We're gonna call those cards, and then the sort is gonna be sort. I'll just set these to delete the junction table item in case we deselect a tag from any of these. There's no reason to keep, the extra info in this junction collection. Great. We'll display some related values and away we go.\u003C\u002Fp>\u003Cp>Right? Now I'm gonna set like a default display template here just in case we're using that in some of the views. We'll do the same for the tags. We'll give that a title. Maybe we wanna show the actual topic.\u003C\u002Fp>\u003Cp>Cool. Topic. Great. Boom. Boom.\u003C\u002Fp>\u003Cp>Boom. Boom. Boom. We've got all the relationships. We've got everything configured.\u003C\u002Fp>\u003Cp>This is a feature tag. We don't have a tag for features. Well, we have a topic for features. What is this gonna be? This is, REST APIs.\u003C\u002Fp>\u003Cp>Cool. Directus REST API. It's awesome. Cool. Alright.\u003C\u002Fp>\u003Cp>So there we go. Now we have some tags. We have title. We have some content. We have the tags here.\u003C\u002Fp>\u003Cp>I can see all the cards within that. This is looking freaking sweet as far as the back end. Like, if I want to fetch all my cards, I could just open up the direct assistance. I'll swap the admin side of this. I'll go to items cards.\u003C\u002Fp>\u003Cp>Here I can see all of that. And one of my favorite features of the REST API is the ability to do stuff like this where I can say, hey, I want all the root level fields. That's what this wild card is for. And then I also want all the fields for the tags. So you could see here, this is I'm actually going through the junction collection.\u003C\u002Fp>\u003Cp>So I probably wanna go one step further and do this. So I can actually see, okay. Here's my tags. Here's the tag. What's the title of the tag?\u003C\u002Fp>\u003Cp>What's the topic? Etcetera. And I, you know, that's not really recommended for production use. Right? I would probably wanna be more specific so that you don't end up with, like, six levels of recursion because you could see the cards start to show here.\u003C\u002Fp>\u003Cp>Tags dot topic. Cool. There we go. There's the tag. There's the title.\u003C\u002Fp>\u003Cp>Topic. You know, I could go in and keep going with this. You get the idea. You get the picture. Alright.\u003C\u002Fp>\u003Cp>Let's bring that back. So I've still got access to my direct as admin. And then we are going to fire this thing up. Is this gonna go well? I do not know.\u003C\u002Fp>\u003Cp>So we're gonna pull this up, and I'm probably better off, like, starting a new directory for this. CD sites. Cool. We're gonna, like, what? Git clone?\u003C\u002Fp>\u003Cp>Can I page? Git clone. The extension. Okay. CD, the extension, and code dot.\u003C\u002Fp>\u003Cp>Is that the, that's not gonna open it in cursor. We're gonna cheat. Is it really cheating? I don't think so. We're gonna use cursor today.\u003C\u002Fp>\u003Cp>Alright. So looks like what do we have? We got PMPMI, kinda your standard. So we'll wait for this terminal to pop up. PMPMI.\u003C\u002Fp>\u003Cp>Okay. Come on. Come on. Core pack. Do I have to enable core pack?\u003C\u002Fp>\u003Cp>Core pack enable? Core pack enabler. That's what I am. User local core pack target in this. What is going on here?\u003C\u002Fp>\u003Cp>CD my extension, cloned to local. PMPMI. What is going on here? Unexpected identifier core pack. Is this just like a bad terminal instance?\u003C\u002Fp>\u003Cp>PPMI. Yeah. Wild. Okay. I guess the shell wasn't loaded yet before I fired this thing up.\u003C\u002Fp>\u003Cp>Alright. So now we'll do p m p m dev. Never used this thing before, so it should be fun watching me sweat. What are we gonna do here? Then load the extension in the browser within the extension folder.\u003C\u002Fp>\u003Cp>Okay. So, I am using Arc. I'm gonna switch over to Chrome for this. I'm gonna drag this out of the way. You know what?\u003C\u002Fp>\u003Cp>I I do like Arc. There are some frustrations with Arc, but, you know, it is what it is. Let's go to where is it? Slash extensions? Extensions?\u003C\u002Fp>\u003Cp>We'll just manage our extensions here. Let's load and unpack extension. You can see I've got, like, Vue already installed on this one. Extension select. Failed to load extension.\u003C\u002Fp>\u003Cp>What are my docs saying? Extension extension dev, I guess. Let's try the dev folder. Okay. We got v extension.\u003C\u002Fp>\u003Cp>Cool. What is this gonna do? Can we pin to the toolbar? Tim the toolbar, Taylor. Okay.\u003C\u002Fp>\u003Cp>Cool. So now we have this cool little extension. Alright. Dark mode, airplane mode, VPN. I don't know what any of these settings are doing, but, this looks cool.\u003C\u002Fp>\u003Cp>This is our little pop up widget. Right? I'm assuming somewhere in the source folder here we can find what we need for this. Alright. So this is kinda structured.\u003C\u002Fp>\u003Cp>I I've worked on Chrome extensions a little bit in the past. Let's see. We've got the pages. We got an app. So we're showing a router view, which is nice.\u003C\u002Fp>\u003Cp>So we can navigate between these specific things. We've got pages, index.HTML. We have the module. That's the specific pop up. You know, we've got, like, content scripts and things like that.\u003C\u002Fp>\u003Cp>I'm assuming if I open up this, this will say hello from content script. Let's actually for now, let's just focus on the pop up side of things. Right? I don't so much care about the settings here or the about. Where's the about page?\u003C\u002Fp>\u003Cp>That's the profile page, I'm assuming. Profile page. Put your own content here. Cool. Alright.\u003C\u002Fp>\u003Cp>How are we gonna get started with this thing? Let's see if we can actually just fetch a list of our articles, and we're gonna take over the home page here. The first thing I'm gonna do is move this script setup to the top. We'll do lang t s typescripts. Great.\u003C\u002Fp>\u003Cp>This has already got Tailwind installed, so I'm cruising there. Let's call this the we'll give this a little header actions. Is it articles, cards? Directus knowledge cards. That sounds great.\u003C\u002Fp>\u003Cp>Knowledge cards. Okay. Div. Great. Yeah.\u003C\u002Fp>\u003Cp>We'll do flex call. Okay. I'm I'm gonna roll with you there. Now as far as the components, let's not over engineer first. Okay.\u003C\u002Fp>\u003Cp>So now we get into, like, our first kinda inflection point here. We want to PMPM install the Directus SDK. Awesome. We'll put the SDK in here. We'll import where am I gonna stick the SDK clients?\u003C\u002Fp>\u003Cp>Util services, you know, we could call it that. Services. We will create a directus.ts file. We're gonna import the rest. We're gonna create Directus from the Directus SDK.\u003C\u002Fp>\u003Cp>Read items, read item, update item. I'm just gonna import the whole house here. Right? Create an item, delete an item. And one of the nice things about the Directus SDK is how modular it is.\u003C\u002Fp>\u003Cp>Right? So I can only I only have to import the items that I actually need. And I'm just gonna hard code the app URL here. That's our Directus URL, 8055. Create Directus.\u003C\u002Fp>\u003Cp>Okay. And then we can export Directus. Do we want that to be you know, let's call it export cons. So I could still, you know, access Directus. But also let's, let's carry this further and just get like a a function.\u003C\u002Fp>\u003Cp>Export async function. Get cards. Get cards. Cool. Yeah.\u003C\u002Fp>\u003Cp>Maybe we wanna potentially pass like a like params for that. We're gonna commit typescript no nos here. We'll just slap any on there. And what we're gonna do here, we'll say cards. We're going to what?\u003C\u002Fp>\u003Cp>Await. Directus. That's not that's not a good auto completion cursor. Alright. So we're gonna say read items, knowledge cards, params.\u003C\u002Fp>\u003Cp>We're gonna return the cards. Get card. Yeah. There we go. Okay.\u003C\u002Fp>\u003Cp>Now it's starting to get the picture. Right? This is one of the reasons why I have adopted cursor is because once it zeros in on, like, the logic for how you do some of this stuff, it is pretty good about actually taking care of it for you. Right? It's not gonna be knowledge cards though.\u003C\u002Fp>\u003Cp>And that could be solved by actually giving it, you know, like a TypeScript definition of what our types are, which you could potentially grab from the Directus open API spec. Alright. So let's go back to the pop up. Now we've got the service. We're going to import get cards from at services.\u003C\u002Fp>\u003Cp>I hope that is the actual alias for this. We'll we're about to find out, honestly. Do we wanna do cards? Why would we do cards as a reference? Cards equals await get cards.\u003C\u002Fp>\u003Cp>And are we gonna pass any params? Let's just get all the fields for the cards. It failed. Services from Directus. Yeah.\u003C\u002Fp>\u003Cp>I kinda figured that was gonna be the case. Okay. Now we've got that. Let's just go in. Oh, we've got this.\u003C\u002Fp>\u003Cp>We could format this a little bit. One of the first things I like to do just to make sure I'm getting the data that I want is just output this thing. So we'll save. Refresh. Can I actually see the network requests that are occurring through this thing?\u003C\u002Fp>\u003Cp>Inspect? What does that do? Does that give me okay. So now we're here inside the oh, as soon as you throw the pop up away, it disappears. Inspect.\u003C\u002Fp>\u003Cp>Is there a way to save this? The pop up is gonna disappear on me. So if I go to here so wait. Get cards. Are we actually finding that?\u003C\u002Fp>\u003Cp>Pages pop up. Dot.services.directus.js. Is that where how we should import this thing? I don't see nothing. What's happening?\u003C\u002Fp>\u003Cp>Maybe we should actually wrap this in, try catch so we could see some errors. Right? Try return cars. We'll just log the errors out. And now if we pop open the console, right, should we see some errors happening?\u003C\u002Fp>\u003Cp>Console failed to load. This is forbidden forbidden. We can't access this. Why is that happening? I kinda thought this would be the case.\u003C\u002Fp>\u003Cp>Right? So by default, whenever I create new collections, Directus does not provide any access to those collections. Right? So if I look at the Public Access Policy, I got nothing coming from that. We got thirty minutes on the clock, so for now let's, let's just grant full access to this.\u003C\u002Fp>\u003Cp>We are working locally. You know, if I was going to basically go to production with this, obviously, I would implement authentication. Maybe we can circle back to that. But, you know, the core functionality here is being able to update, edit, and rearrange these cards. Alright.\u003C\u002Fp>\u003Cp>So now with those permissions populated, we save this again. Can we actually see what's going on here? Console component, setup component. I don't see, get cards. Is there a way to actually sticky the inspection window?\u003C\u002Fp>\u003Cp>How do I do that? Focus page. I thought there was a way to to keep this up regardless, but maybe not for the pop up window. Settings, type error, node. Let's just try to restart.\u003C\u002Fp>\u003Cp>Let's see what we got. Do we have to reload this extension every single time? Refresh? I sure hope not, but we will if we have to. Now I don't see any.\u003C\u002Fp>\u003Cp>So we should be able to, like, console log the cards. Here's the cards. Right? Should be able to see some kind of output from that. Inspect.\u003C\u002Fp>\u003Cp>Console. Scheduler flush view internals. It's not liking that for whatever reason. Right? Maybe we want to do on on mounted.\u003C\u002Fp>\u003Cp>Cause cards. Response. Cards dot value equals response. Cards dot value.\u003C\u002Fp>\u003Cp>Speaker 1: Hey. Okay. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: It doesn't like fetching when the component is created for whatever reason. Not sure why that is, but there we go. We've got our cards. We could see the cards here. Cool.\u003C\u002Fp>\u003Cp>I can also do what we talked about where I can say, okay. I wanna get the tag. And where I'm using the SDK, I can also do something like this where we have tags, and then I can provide, like, an object like structure. So within that, we have, tag, and then we have the title. Let's fetch the ID of the tag and then the maybe even the topic.\u003C\u002Fp>\u003Cp>Tags. Tag. Okay. So now we could see all the info we want. That's working correctly.\u003C\u002Fp>\u003Cp>Let's, let's actually render out these cards. Right? So we'll do, like, a div. V four in cars key card dot ID. What are you gonna come up with cursor?\u003C\u002Fp>\u003Cp>Title.subscription. No. There's not gonna be a description for end. Maybe we show the tags within that. So span b four tag in card dot tags tags.\u003C\u002Fp>\u003Cp>Cool. Rest knowledge card, v API. Class. Let's give them some padding. We'll add a light border, round, large, rounded, l g.\u003C\u002Fp>\u003Cp>See what that looks like. Okay. And this is actually gonna be is this gonna be router link? Router link is gonna be missing two, a car dot ID. Okay.\u003C\u002Fp>\u003Cp>And then we're gonna need pages. So what does our actual router look like? Right? Okay. Looks like cursor is doing its thing for me here.\u003C\u002Fp>\u003Cp>So the path dot card dot ID pages card. So we're gonna create a new card page. Cool. Card dot view. I probably need to import that as well.\u003C\u002Fp>\u003Cp>Card view. Oh, it is dynamically importing those. Okay. That's fine. We can stick with that.\u003C\u002Fp>\u003Cp>Vbase TS setup. Move my script up to the top. And then we can, like, nab this card thing as well. But for now, this should solve the challenge. Okay.\u003C\u002Fp>\u003Cp>So now we can see the card. There's a direct us REST API. Card is looking nice. Let's go back to this. Maybe we give the title a little styling.\u003C\u002Fp>\u003Cp>Text large, font bold. Okay. We add a little margin to the top of this, give it a border, give a little padding to the top. Okay. So now we got our cards.\u003C\u002Fp>\u003Cp>We're probably gonna want a, like, a search input as well. Search input. If I do something like this, what is cursor gonna do for me? Input, placeholder, search. We could search our cards.\u003C\u002Fp>\u003Cp>Cool. And then, you know, we might even wanna do something like this where we have a query, and then we're gonna add, like, a watch to this. Watch search new val. What are we gonna do as far as the fetching? It's actually await get cards unmounted.\u003C\u002Fp>\u003Cp>And what we could do on the new value, we are going to if new value, cars have we don't wanna search cars that way. We could just use the directus API for this. So let's set up the watch, just to make sure that watcher is actually working here. And I could at least do this on the Directus documentation so we got something nice to kinda look at. Right?\u003C\u002Fp>\u003Cp>If I change the search here, it should be updating watch search. It's not console logging that new value for the search. Why is that? Because I don't have a v model on the search input. Let's try this again.\u003C\u002Fp>\u003Cp>Console. Why is the search are we outputting the sir okay. So now we can start seeing that. Great. Does this thing have something like view use already plugged in for, like, a debounce?\u003C\u002Fp>\u003Cp>I don't see that. But alright. We're we're not gonna worry too much about the debounce right now, and we're just gonna ignore this. Like, let's get to the actual card view for now. And, god, I really wanna change that router, that footer.\u003C\u002Fp>\u003Cp>We'll just drop the footer off of this. Cool. Alright. So now we can see the cards. Here's the settings.\u003C\u002Fp>\u003Cp>Cool. Great. How much time are we cruising out? We got twenty two minutes left. Let's see if we can get to actually editing these cards.\u003C\u002Fp>\u003Cp>So, if we take some of our logic here from the actual home page, we can probably just reuse a lot of this. We could get card, card. We don't need a search. Get card. And, again, I'm not totally sure why this thing is not wanting to card dot value equals response.\u003C\u002Fp>\u003Cp>Why it's not wanting to fetch properly? You know, if we're using script setup, it should do that for us, but, I digress. It's at a h one. Text two XL border b. P b four.\u003C\u002Fp>\u003Cp>Cool. And does it sound like tailwind typography in it? I don't know. We're just gonna do div class. Text large.\u003C\u002Fp>\u003Cp>Oh, we can add a prose class in there just to see. And one of the main reasons I love Tailwind is just the ability to, like, quickly do stuff like this. Alright. Git card. We're gonna git card.\u003C\u002Fp>\u003Cp>That is in the services.directus.js. Git card. Right? Search is not defined. Why do I need search?\u003C\u002Fp>\u003Cp>Oh, because we're watching that value. We don't need to watch that business. Alright. Go to home. So refresh this.\u003C\u002Fp>\u003Cp>We go to the direct API. Why is the ID is not defined. Oh, okay. Yep. We need to pass the ID.\u003C\u002Fp>\u003Cp>So that's gonna be what? Const route equals use route params dot ID. Are we passing that? Yep. We do have that.\u003C\u002Fp>\u003Cp>Cool. So now we should be getting that ID.\u003C\u002Fp>\u003Cp>Speaker 1: What is the oh, we\u003C\u002Fp>\u003Cp>Speaker 0: had to import that. Forgot I'm not working in Nuxt. Nuxt does all the fancy auto imports for us. So we refresh. There it is.\u003C\u002Fp>\u003Cp>Directus REST API. We can go home. We can go back to that card. Cool. Alright.\u003C\u002Fp>\u003Cp>Let's do const is editing. Ref equals false. Does this have auto imports? I'm assuming it does because it is. Let's see.\u003C\u002Fp>\u003Cp>Auto import, components auto importing, feed plugin components. We'll just import ref to be safe. Right? Obviously, that's already working, but let's give this some padding as well, make this look a little nicer, and we'll add a little margin to the top of this bad boy. Okay.\u003C\u002Fp>\u003Cp>Alright. So now we have our content. We could view this. Let's add a button to edit this thing. Right?\u003C\u002Fp>\u003Cp>Flex items center gap for I'll just say justify between, and then we're gonna add a button. Okay. We can edit this card. That's cool. The border b will move here.\u003C\u002Fp>\u003Cp>Cool. Alright. And if we edit this, what are we gonna use for the editor? View markdown editor. View markdown editor.\u003C\u002Fp>\u003Cp>What do we have here? What do we have? This is, again, risky business. Markdown editor in v three. Oh, no.\u003C\u002Fp>\u003Cp>Alright. This is it's not markdown. Base editor. Where are the demos? Import?\u003C\u002Fp>\u003Cp>Base editor? Oh, I guess we're this just doesn't have, like, some nice syntax. Right? Code mirror editor? Yeah.\u003C\u002Fp>\u003Cp>This looks way is this too cumbersome for what we're looking for? I probably should have snuffed this out earlier. Right? View editor, MD editor for view. This one's got 1,300 stars.\u003C\u002Fp>\u003Cp>I don't necessarily want to, like, show the information. We just need, like, a nice editor. Right? And this might be, like, something where we throw throw it back on, like, not markdown, but HTML. Right?\u003C\u002Fp>\u003Cp>View three markdown editor. View three markdown editor. Should probably just drop something like tip tap into this. Yeah. Let's just go for it.\u003C\u002Fp>\u003Cp>Okay. So PMPMI, MD editor MD editor v three. Here's what the usage looks like. Let's try to import all of this. This will be the content, And we're gonna what?\u003C\u002Fp>\u003Cp>Clone that content. Okay. Is editing. So this will be v m d editor. Is that what it is?\u003C\u002Fp>\u003Cp>V model content. And then we're gonna have a save function. Async function. Save. Await update card, which is a new one that we do not have.\u003C\u002Fp>\u003Cp>Update card. ID and content. Is editing value. Cool. I don't think there is a save function.\u003C\u002Fp>\u003Cp>Alright. So we're let's wrap this. Div. V f is editing, and we'll do a button to save. Now is this actually going to go off?\u003C\u002Fp>\u003Cp>I don't know. Vite server did not start. Something bad is oh, you gotta actually fire up the dev server after you install the thing, Brian. That would actually make sense. Fifteen minutes left.\u003C\u002Fp>\u003Cp>Can how far can we get on this internal knowledge base tool? Alright. So here we go. Search cards. We can we're not getting the card.\u003C\u002Fp>\u003Cp>We're throwing an issue. Use ID. Can I use list is outdated? Where do we get in here? A day card ID equals rent route top params.ID.\u003C\u002Fp>\u003Cp>Error when updating dependencies. What is the view version of this? Okay. So let's do PMPM. I think it's v view 3.5 view at latest.\u003C\u002Fp>\u003Cp>Hopefully, that gets us the use ID composable that we're talking about. PMPM dev. Okay. Syntax error does not provide an export. Alright.\u003C\u002Fp>\u003Cp>So that is obviously a problem. We will export update card. There's the data. Update the card, return the card. Bada bing badda boom.\u003C\u002Fp>\u003Cp>We are going to update the card with the value as well. Constant response, card dot value equals response. Or we can just console log this. And you could see Cursor is doing a lot of the heavy lifting for me here. What else are we getting?\u003C\u002Fp>\u003Cp>Service does not provide. It does provide an update card. Export async function. Services pages card. It should provide if we can get the card, why can't we update the card?\u003C\u002Fp>\u003Cp>Alright. So we hit edit. There we go. We can hit save. Card dot value equals response.\u003C\u002Fp>\u003Cp>V equals true. Shallow false. Update card. Why is the card Card. Well, that doesn't look right.\u003C\u002Fp>\u003Cp>On mounted card, await card response card dot value equals response. Why is this a why was this an array to begin with? Right? Extensions reload. Okay.\u003C\u002Fp>\u003Cp>Something is going wrong here. What are we doing wrong? The card dot contents, get card returns the card, read item cards dot ID. What is the actual response that we're getting? Inspects cards.\u003C\u002Fp>\u003Cp>There's the response. Card dot proxy. So why is it not showing the actual card content anymore? Car dot value. Oh, what is going on here, man?\u003C\u002Fp>\u003Cp>Do I have something else going on? But I define card way too many times. Response get card. So we get the card. I could see the response.\u003C\u002Fp>\u003Cp>The response looks good to me. Right? Go here. Go home. You see the the response.\u003C\u002Fp>\u003Cp>There's the response. Content. Tags. There's the card value. Maybe we set this to content dot value equals card dot value dot content.\u003C\u002Fp>\u003Cp>Card. Card dot title. What in the world is going on? Dep. Why are we loading?\u003C\u002Fp>\u003Cp>I'm not comprehending something here. VF card. VF card. We are seeing the car. Cannot read.\u003C\u002Fp>\u003Cp>VF card. How is the card dot title this is what I'm struggling to understand. Cards. There's the cards. There's the card.\u003C\u002Fp>\u003Cp>Card dot value. We can see the title right there, card dot value. Unref. Guard. We are running out of time.\u003C\u002Fp>\u003Cp>What is going on here, man? Where did we go wrong? Is it when we brought in the MD editor business? Alright. So this works.\u003C\u002Fp>\u003Cp>Yeah? Or it should. Some new, like, quirks with Vue. What is going on? I pulled in the latest version of Vue, and now we are, like, dying here.\u003C\u002Fp>\u003Cp>Dying on the vine. Car dot value equals response. Is there, like, a weird thing that I'm not picking up what I'm putting down here? Why does this work? And that doesn't.\u003C\u002Fp>\u003Cp>Cards ref. This is the exact same logic from the home. On mounted async, we get the card, Cards dot value dot response. Is this storing is like a lots of fun all the time. But cards.\u003C\u002Fp>\u003Cp>Click the card. See the card. There's the card. It is an object as it should be. Card dot value equals response.\u003C\u002Fp>\u003Cp>I don't understand. V f card has a value. Do we have to specifically say the value dot title? No. You shouldn't have to.\u003C\u002Fp>\u003Cp>Not in the template. Why do I keep getting this error? This would be a good question for AI, I assume. Why do I keep getting this as my content. You shouldn't have to do that inside the the template, though.\u003C\u002Fp>\u003Cp>Yeah. That's what I thought. You shouldn't have to unref in the template. Right? This is the case where AI, yeah, Check that response on your unmounted callback.\u003C\u002Fp>\u003Cp>Yeah. Huge help here. Some kinda, like, proxy issue with view, I'm assuming. I don't know. I don't know what the deal is.\u003C\u002Fp>\u003Cp>Read item cards. Card equals response. Blah blah blah blah blah blah blah blah. Okay. Some funky business going down.\u003C\u002Fp>\u003Cp>Ref car. Value. Yeah. There should be no need to actually I swear this is like a hot module reloading thing. B is ref true.\u003C\u002Fp>\u003Cp>What is the actual card, Joe? So if that's the card, why do we have? This is baffling me, man. So there's my card data. If that's the value of the card, should I not be able to access the card dot title?\u003C\u002Fp>\u003Cp>Card dot content. Oh. Oh my gosh. So that is the freaking problem when I updated and saved this card. This is like the king of deep oh, look.\u003C\u002Fp>\u003Cp>Sometimes. So I accidentally saved the value as that. We had everything going for us. I blew it all away just to goof this thing up big time. Hey.\u003C\u002Fp>\u003Cp>That's how we roll here on 100 apps. There was an issue with the save function that called caused the problem here. So let's say we got content equals ref. That'll be nothing for now. The content dot value should equal the response dot content.\u003C\u002Fp>\u003Cp>So that's the content we're gonna submit. Is editing. God. What an idiot. Boom.\u003C\u002Fp>\u003Cp>Explosion. There we go. This is one that I man, I really just have to, like, finish this one for my own sake. Failed this mission totally because I got hung up and not actually paying attention. That is the fun of this thing.\u003C\u002Fp>\u003Cp>Right? So here we go. We got an async function for save. Let's call it save. We're gonna update the card with the content.\u003C\u002Fp>\u003Cp>We'll set is editing to false. Const response response equals. We'll set the card dot value to the response. Cool. And then let's do this where we say v if is editing, v else model.\u003C\u002Fp>\u003Cp>We're gonna do wrap that in a div. V if is editing, and then we're gonna add a save button. Class p two blue, etcetera. Click save equals content dot value. Do we have a button?\u003C\u002Fp>\u003Cp>We forgot to add our edit button. And let's remove this at the top. Give this some padding. V is editing equals false. Vhtmlcard.content.\u003C\u002Fp>\u003Cp>Here's our content for this card. This is amazing content. Open this back up. There's our REST API. We hit edit.\u003C\u002Fp>\u003Cp>This shows this freaking thing. There's our editor. Test. Me. I failed the submission.\u003C\u002Fp>\u003Cp>Great. If we hit save, what does that do? Console update card is not defined. Obviously, we didn't import that. Update card.\u003C\u002Fp>\u003Cp>We'll try this again. Test, test, test. Save. Of course, there it is. There's the test, test, test content.\u003C\u002Fp>\u003Cp>Too little, too late, friends. And that's the way it rolls on 100 apps, one hundred hours. That's it for the internal knowledge base episode. Careful of how you say values, man. I never, for the life of me, thought, okay.\u003C\u002Fp>\u003Cp>Why am I getting this? Surely there would have been an error, boneheaded mistake. That's the way it goes. We'll see you on the next one.\u003C\u002Fp>","And we are back with another episode of 100 apps, one hundred hours. I'm your host, Brian Gillespie. Welcome back. I'm here for Directus, by Directus something something. I I don't know. I'm trying to be fancy here. Not my forte, obviously. Dad of three little girls, gave up being cool a long time ago. But today, we have got a cool episode for you. We are going to be tackling an internal knowledge base. I'll explain what that is in a moment, but if you're new to 100 apps, one hundred hours, there are two basic rules. Number one, we have sixty minutes to plan and build your app, your clone, your idea, whatever this is. You get no more, no less. And rule number two, the anti rule. The no holds barred WrestleMania action, use whatever you have at your disposal. This is a no holds barred cage match, basically. So let's talk about internal knowledge base for a moment. When I used to run a customer success team, at a previous company, there was this product called Guru that was basically an internal knowledge base. You know, we used it for a lot of our chat support team. Also our CSMs, you know, if they had a question, we could easily document this. And the cool thing that they did have was this Chrome extension that would basically give you access to all the knowledge base. And, you know, the structure was very bite sized pieces of information, and that was the whole idea behind it. So we're gonna try and replicate something similar. You know, anytime you have something like an internal knowledge base, if it's not in everyone's face, the knowledge goes stale. If it's a pain in the butt to edit, it goes stale. So let's tackle that challenge. We're gonna put sixty minutes on the clock. Let's let's ride. Let's wrassle. Alright. So, functionality for this specific app. Right? And this is me probably biting off a lot more than I can actually chew in sixty minutes. But what we're gonna do, we're gonna build a back end for the internal knowledge base, And then we're going to build a Chrome extension to search search articles, edit articles, and that's probably the extent of maybe what we'll get done in an hour. I don't even know if we'll get this done. Right? But, we need to kinda flush this out further. Let's go in and map some data model. Map this out. Right? So at a high level, we're we're gonna have users of this application, so we'll have some authentication, etcetera. You know, they'll be able to log in. As far as the actual knowledge itself, I like the term cards for this. Instead of articles, that's what we're gonna do. And the reason why is these are bite sized pieces of internal knowledge. I think that's a helpful paradigm. As far as the fields that we want, we're gonna have a title for the card. Maybe I won't even get into, like, AI generated summary of this. We may use some AI stuff to, like, dive into this. Let's have an owner of this card. Like, who owns this knowledge internally? We've got the actual content of the card for sure. You know, maybe we got some tags for the cards as well. And then, of course, we have tags. Let's say there's a title for the tag and then a topic or an area. Right? I think that's a good structure for tags. Like, in in the case of Directus, that may be, like, features or frameworks or something like that. And then we'll have users. So and a user owns a card. Tags are connected to cards in some form or fashion shape, form, or fashion to quote the West Virginia governor here. This is a many to many relationship between cards and tags. Right? A card could have multiple tags. A tag could have multiple cards or a single tag could be applied to multiple cards. So that is the data model setup we are going to roll with. You know, if you've watched this before, I've got, like, a standard Nuxt starter that I use for this. But, on the front end, we're gonna try this Chrome extension Vite starter from, mister Fayezara. Fayez Fayez Ahmed. I think that's his name. Yep. Fayez. Hopefully, I'm pronouncing that right. If I'm not, I apologize. But, let's focus on that back end first. Right? We are going to zoom way in and let's create our first collection. Right? Let's call this cards, and I'm gonna give these a generated UUID. We'll say when the card was created, who created it, when it was updated. What's the status of the card? You know, this could be helpful if we have, like, cards that are draft or not approved or not verified, I would say. That should be like the one of the first states that we have for this. And then we'll add a a sort order, I guess. Why not? Let's see if we could find a card. So this is a looks like a memory card, a SIM card. It's a good icon for this. Great. I'm just gonna hit command s to save this and stay. And let's start with the title. So straight input field string. We do wanna require a value for this. Right? Great. You know, we could potentially index this field if we want to inside the database. That's what that's for. Do we need to trim? We don't need this to be URL safe, so we're gonna roll with that. And then what else do we have? We had an some content for this. Let's go with the WYSIWYG editor. We'll have some content, you know, and for, like, AI heavy stuff, we might even, like, set this to be markdown. Basically, behind the scenes, these use the same type for the column inside the database. And what Directus is doing behind the scenes whenever I add this field is basically just creating a new column inside the cards table inside our Postgres database. So for now, let's, let's go to the markdown interface just to appease our developer friends. And let's say we'll auto format the display for this. Cool. Great. Gravy. Amazing. What else do we need? Right? We wanna assign ownership of this card. And, you know, maybe we even want to have, like, a date verified. So let's take care of the ownership first. Right? I'm gonna add a many to one relationship here. So I'm gonna just call this the owner of this card, the related collection. Whenever you spin up a Directus instance, it gives us, some system collections that it prefixes with Directus so that all of your your SQL stays pure, which is nice. And then we're just going to add the Directus users collection here. So this is what the instance uses for authentication, you know, into the studio and via the API. So we'll just piggyback on that. Right? We'll assign a user to this. And for the display, cool, great, everything looks nice. We'll display should be an option to display the user. We could show the user in a circle. That will look great. And let's move that owner beside this. We want to show the status. Date updated. So there's the status title owner. And maybe we want to switch that around. Basically, what I'm doing here is just controlling the form for anybody using the back end of this. So let's make the title full width just to give that proper weight, proper gravitas. Here's the status. Maybe we wanna update that from draft to you know, it's not necessarily like we're concerned about whether this is published or not. We just want it to be verified. So, like, internally, you wanna make sure that that content is totally verified. So that's what we'll do. We'll call it verified. This is archived. Cool. We could copy these choices and just paste those into our labels. And I don't wanna show as a dot. I just wanna show each actual label. Alright. So next, maybe we add a time field, date time. Let's do this as time stamps. We'll just follow the same convention, date verified. Cool. Awesome. Alright. And we'll come back to that in a moment. This is our cards collection. Right? Let's call this direct us features. I'm gonna assign this to me, the admin user, and we will here's all the amazing features of Directus. Great. REST APIs, GraphQL APIs, APIS APIs. There we go. Insights, permissions, whatever. Right? Great. There's our preview for the markdown. You know, if we decide to do some AI, you should later this is gonna be a a friendlier format for that. Great. We've got a card. Cool. We can move this around. You know, let's add just a a hook to this using some flows to automatically populate the date verified. Right? So, anytime I verify a card, let's say a card is updated, updated or created, card is updated, created. You know, we could be add verification stamp. Alright. And basically, anytime a card is created or, you know, let's just say updated in this point because the default should be like draft, the state for all of the cards. Anytime a card is updated, let's go in and let's make sure check the status. Check status. Alright. We're just gonna create a condition here and this uses the standard, filter rules inside Directus. What I'm gonna do is just copy our template here. We're gonna adjust this a little bit. Payload dot status. We'll drill in one more layer And if the status is equal to verified, we're gonna run some action after this, right. So inside Flows is a great way to add simple automations to your back end And there is flow control already built into flows. Shocker. Right? But if this condition passes, we're going to take action on it. If it doesn't pass, meaning the status is not updated or it's not there, it will, you know, follow the rejection path. So in this case, you know, I I could log something to the console. In this case, I'm just going to, run a little script. Get current time. Right. We're gonna return, what's new date? Is that gonna be two ISO? Is it two ISO string like that? New date to ISO string. Is that what we're looking for? New date to string to ISO string. That should be what I think that's what we're looking for. So we're just gonna return the current time. Great. And then I'm going to update the data. Update card. Cool. So we'll do cards. We can use the permissions from the trigger. And I'm gonna say this should be dollar sign trigger dot keys, the first item in that array. Not a % sure, but, date verified. And then we'll do something like this. We'll just use this mustache syntax, git underscore current time. Great. Cool. Let's see if this actually works. Alright. So this is good. These are all amazing features of Directus. I'm gonna verify this. I hit save. And voila. Now we could see the exact date and time that I verified this thing, December 3 '10 '14 AM. Amazing. Sweet. Right? Alright. Let's go back and continue our March onto our data model here. So let's set up some tags. I'm not really concerned about when these were updated, etcetera, blah blah blah. You know, maybe we wanna sort the tags, but, let's find a tag. We could use the hashtag. That's what tags are for. This is a title. Cool. Title of the tag. That's required. And we'll keep keep updating. We don't wanna change just yet, my friend. And then the topic. Right? What are we gonna call this? Area topic? I think I like topic for this. I still wanna keep the string. I don't think there's a need for extra topics, but what we'll do, we'll just pick the options from a drop down. Right? So in the case of Directus, you know, this could be features features of Directus. Makes sense. Frameworks, like next Nuxt, etcetera. Frameworks. Can we get another f one? What else is this gonna be? API, API, pricing, you know, the pricing of direct you know, these could be infinite, and maybe that's, you know, not not a great one because that could be just like a a side effect. Cool. So now we have tags. We want to create this relationship between the two, and it doesn't really matter where I start here. All roads lead to the many to many relationship. If I'm on the cards collection, I'm gonna create a new field. We're gonna call it keys or I'm sorry. Call the key is gonna be tags. I need more coffee today. The related collection is gonna be tags. We don't wanna allow duplicate tags. We wanna show a link to this item. But what I wanna do here, I'm gonna open up the advanced field creation mode, and you could see what's going on behind the scenes. Directus is creating a junction collection for us. I'm gonna change the names of these just for my own anxiety, I guess. So we're gonna call this card tags. So these are tags on a card. The junction field or the the foreign key is card for the card, tag for the tag, and then we're gonna add this corresponding field to tags as well. We're gonna call those cards, and then the sort is gonna be sort. I'll just set these to delete the junction table item in case we deselect a tag from any of these. There's no reason to keep, the extra info in this junction collection. Great. We'll display some related values and away we go. Right? Now I'm gonna set like a default display template here just in case we're using that in some of the views. We'll do the same for the tags. We'll give that a title. Maybe we wanna show the actual topic. Cool. Topic. Great. Boom. Boom. Boom. Boom. Boom. We've got all the relationships. We've got everything configured. This is a feature tag. We don't have a tag for features. Well, we have a topic for features. What is this gonna be? This is, REST APIs. Cool. Directus REST API. It's awesome. Cool. Alright. So there we go. Now we have some tags. We have title. We have some content. We have the tags here. I can see all the cards within that. This is looking freaking sweet as far as the back end. Like, if I want to fetch all my cards, I could just open up the direct assistance. I'll swap the admin side of this. I'll go to items cards. Here I can see all of that. And one of my favorite features of the REST API is the ability to do stuff like this where I can say, hey, I want all the root level fields. That's what this wild card is for. And then I also want all the fields for the tags. So you could see here, this is I'm actually going through the junction collection. So I probably wanna go one step further and do this. So I can actually see, okay. Here's my tags. Here's the tag. What's the title of the tag? What's the topic? Etcetera. And I, you know, that's not really recommended for production use. Right? I would probably wanna be more specific so that you don't end up with, like, six levels of recursion because you could see the cards start to show here. Tags dot topic. Cool. There we go. There's the tag. There's the title. Topic. You know, I could go in and keep going with this. You get the idea. You get the picture. Alright. Let's bring that back. So I've still got access to my direct as admin. And then we are going to fire this thing up. Is this gonna go well? I do not know. So we're gonna pull this up, and I'm probably better off, like, starting a new directory for this. CD sites. Cool. We're gonna, like, what? Git clone? Can I page? Git clone. The extension. Okay. CD, the extension, and code dot. Is that the, that's not gonna open it in cursor. We're gonna cheat. Is it really cheating? I don't think so. We're gonna use cursor today. Alright. So looks like what do we have? We got PMPMI, kinda your standard. So we'll wait for this terminal to pop up. PMPMI. Okay. Come on. Come on. Core pack. Do I have to enable core pack? Core pack enable? Core pack enabler. That's what I am. User local core pack target in this. What is going on here? CD my extension, cloned to local. PMPMI. What is going on here? Unexpected identifier core pack. Is this just like a bad terminal instance? PPMI. Yeah. Wild. Okay. I guess the shell wasn't loaded yet before I fired this thing up. Alright. So now we'll do p m p m dev. Never used this thing before, so it should be fun watching me sweat. What are we gonna do here? Then load the extension in the browser within the extension folder. Okay. So, I am using Arc. I'm gonna switch over to Chrome for this. I'm gonna drag this out of the way. You know what? I I do like Arc. There are some frustrations with Arc, but, you know, it is what it is. Let's go to where is it? Slash extensions? Extensions? We'll just manage our extensions here. Let's load and unpack extension. You can see I've got, like, Vue already installed on this one. Extension select. Failed to load extension. What are my docs saying? Extension extension dev, I guess. Let's try the dev folder. Okay. We got v extension. Cool. What is this gonna do? Can we pin to the toolbar? Tim the toolbar, Taylor. Okay. Cool. So now we have this cool little extension. Alright. Dark mode, airplane mode, VPN. I don't know what any of these settings are doing, but, this looks cool. This is our little pop up widget. Right? I'm assuming somewhere in the source folder here we can find what we need for this. Alright. So this is kinda structured. I I've worked on Chrome extensions a little bit in the past. Let's see. We've got the pages. We got an app. So we're showing a router view, which is nice. So we can navigate between these specific things. We've got pages, index.HTML. We have the module. That's the specific pop up. You know, we've got, like, content scripts and things like that. I'm assuming if I open up this, this will say hello from content script. Let's actually for now, let's just focus on the pop up side of things. Right? I don't so much care about the settings here or the about. Where's the about page? That's the profile page, I'm assuming. Profile page. Put your own content here. Cool. Alright. How are we gonna get started with this thing? Let's see if we can actually just fetch a list of our articles, and we're gonna take over the home page here. The first thing I'm gonna do is move this script setup to the top. We'll do lang t s typescripts. Great. This has already got Tailwind installed, so I'm cruising there. Let's call this the we'll give this a little header actions. Is it articles, cards? Directus knowledge cards. That sounds great. Knowledge cards. Okay. Div. Great. Yeah. We'll do flex call. Okay. I'm I'm gonna roll with you there. Now as far as the components, let's not over engineer first. Okay. So now we get into, like, our first kinda inflection point here. We want to PMPM install the Directus SDK. Awesome. We'll put the SDK in here. We'll import where am I gonna stick the SDK clients? Util services, you know, we could call it that. Services. We will create a directus.ts file. We're gonna import the rest. We're gonna create Directus from the Directus SDK. Read items, read item, update item. I'm just gonna import the whole house here. Right? Create an item, delete an item. And one of the nice things about the Directus SDK is how modular it is. Right? So I can only I only have to import the items that I actually need. And I'm just gonna hard code the app URL here. That's our Directus URL, 8055. Create Directus. Okay. And then we can export Directus. Do we want that to be you know, let's call it export cons. So I could still, you know, access Directus. But also let's, let's carry this further and just get like a a function. Export async function. Get cards. Get cards. Cool. Yeah. Maybe we wanna potentially pass like a like params for that. We're gonna commit typescript no nos here. We'll just slap any on there. And what we're gonna do here, we'll say cards. We're going to what? Await. Directus. That's not that's not a good auto completion cursor. Alright. So we're gonna say read items, knowledge cards, params. We're gonna return the cards. Get card. Yeah. There we go. Okay. Now it's starting to get the picture. Right? This is one of the reasons why I have adopted cursor is because once it zeros in on, like, the logic for how you do some of this stuff, it is pretty good about actually taking care of it for you. Right? It's not gonna be knowledge cards though. And that could be solved by actually giving it, you know, like a TypeScript definition of what our types are, which you could potentially grab from the Directus open API spec. Alright. So let's go back to the pop up. Now we've got the service. We're going to import get cards from at services. I hope that is the actual alias for this. We'll we're about to find out, honestly. Do we wanna do cards? Why would we do cards as a reference? Cards equals await get cards. And are we gonna pass any params? Let's just get all the fields for the cards. It failed. Services from Directus. Yeah. I kinda figured that was gonna be the case. Okay. Now we've got that. Let's just go in. Oh, we've got this. We could format this a little bit. One of the first things I like to do just to make sure I'm getting the data that I want is just output this thing. So we'll save. Refresh. Can I actually see the network requests that are occurring through this thing? Inspect? What does that do? Does that give me okay. So now we're here inside the oh, as soon as you throw the pop up away, it disappears. Inspect. Is there a way to save this? The pop up is gonna disappear on me. So if I go to here so wait. Get cards. Are we actually finding that? Pages pop up. Dot.services.directus.js. Is that where how we should import this thing? I don't see nothing. What's happening? Maybe we should actually wrap this in, try catch so we could see some errors. Right? Try return cars. We'll just log the errors out. And now if we pop open the console, right, should we see some errors happening? Console failed to load. This is forbidden forbidden. We can't access this. Why is that happening? I kinda thought this would be the case. Right? So by default, whenever I create new collections, Directus does not provide any access to those collections. Right? So if I look at the Public Access Policy, I got nothing coming from that. We got thirty minutes on the clock, so for now let's, let's just grant full access to this. We are working locally. You know, if I was going to basically go to production with this, obviously, I would implement authentication. Maybe we can circle back to that. But, you know, the core functionality here is being able to update, edit, and rearrange these cards. Alright. So now with those permissions populated, we save this again. Can we actually see what's going on here? Console component, setup component. I don't see, get cards. Is there a way to actually sticky the inspection window? How do I do that? Focus page. I thought there was a way to to keep this up regardless, but maybe not for the pop up window. Settings, type error, node. Let's just try to restart. Let's see what we got. Do we have to reload this extension every single time? Refresh? I sure hope not, but we will if we have to. Now I don't see any. So we should be able to, like, console log the cards. Here's the cards. Right? Should be able to see some kind of output from that. Inspect. Console. Scheduler flush view internals. It's not liking that for whatever reason. Right? Maybe we want to do on on mounted. Cause cards. Response. Cards dot value equals response. Cards dot value. Hey. Okay. Yeah. It doesn't like fetching when the component is created for whatever reason. Not sure why that is, but there we go. We've got our cards. We could see the cards here. Cool. I can also do what we talked about where I can say, okay. I wanna get the tag. And where I'm using the SDK, I can also do something like this where we have tags, and then I can provide, like, an object like structure. So within that, we have, tag, and then we have the title. Let's fetch the ID of the tag and then the maybe even the topic. Tags. Tag. Okay. So now we could see all the info we want. That's working correctly. Let's, let's actually render out these cards. Right? So we'll do, like, a div. V four in cars key card dot ID. What are you gonna come up with cursor? Title.subscription. No. There's not gonna be a description for end. Maybe we show the tags within that. So span b four tag in card dot tags tags. Cool. Rest knowledge card, v API. Class. Let's give them some padding. We'll add a light border, round, large, rounded, l g. See what that looks like. Okay. And this is actually gonna be is this gonna be router link? Router link is gonna be missing two, a car dot ID. Okay. And then we're gonna need pages. So what does our actual router look like? Right? Okay. Looks like cursor is doing its thing for me here. So the path dot card dot ID pages card. So we're gonna create a new card page. Cool. Card dot view. I probably need to import that as well. Card view. Oh, it is dynamically importing those. Okay. That's fine. We can stick with that. Vbase TS setup. Move my script up to the top. And then we can, like, nab this card thing as well. But for now, this should solve the challenge. Okay. So now we can see the card. There's a direct us REST API. Card is looking nice. Let's go back to this. Maybe we give the title a little styling. Text large, font bold. Okay. We add a little margin to the top of this, give it a border, give a little padding to the top. Okay. So now we got our cards. We're probably gonna want a, like, a search input as well. Search input. If I do something like this, what is cursor gonna do for me? Input, placeholder, search. We could search our cards. Cool. And then, you know, we might even wanna do something like this where we have a query, and then we're gonna add, like, a watch to this. Watch search new val. What are we gonna do as far as the fetching? It's actually await get cards unmounted. And what we could do on the new value, we are going to if new value, cars have we don't wanna search cars that way. We could just use the directus API for this. So let's set up the watch, just to make sure that watcher is actually working here. And I could at least do this on the Directus documentation so we got something nice to kinda look at. Right? If I change the search here, it should be updating watch search. It's not console logging that new value for the search. Why is that? Because I don't have a v model on the search input. Let's try this again. Console. Why is the search are we outputting the sir okay. So now we can start seeing that. Great. Does this thing have something like view use already plugged in for, like, a debounce? I don't see that. But alright. We're we're not gonna worry too much about the debounce right now, and we're just gonna ignore this. Like, let's get to the actual card view for now. And, god, I really wanna change that router, that footer. We'll just drop the footer off of this. Cool. Alright. So now we can see the cards. Here's the settings. Cool. Great. How much time are we cruising out? We got twenty two minutes left. Let's see if we can get to actually editing these cards. So, if we take some of our logic here from the actual home page, we can probably just reuse a lot of this. We could get card, card. We don't need a search. Get card. And, again, I'm not totally sure why this thing is not wanting to card dot value equals response. Why it's not wanting to fetch properly? You know, if we're using script setup, it should do that for us, but, I digress. It's at a h one. Text two XL border b. P b four. Cool. And does it sound like tailwind typography in it? I don't know. We're just gonna do div class. Text large. Oh, we can add a prose class in there just to see. And one of the main reasons I love Tailwind is just the ability to, like, quickly do stuff like this. Alright. Git card. We're gonna git card. That is in the services.directus.js. Git card. Right? Search is not defined. Why do I need search? Oh, because we're watching that value. We don't need to watch that business. Alright. Go to home. So refresh this. We go to the direct API. Why is the ID is not defined. Oh, okay. Yep. We need to pass the ID. So that's gonna be what? Const route equals use route params dot ID. Are we passing that? Yep. We do have that. Cool. So now we should be getting that ID. What is the oh, we had to import that. Forgot I'm not working in Nuxt. Nuxt does all the fancy auto imports for us. So we refresh. There it is. Directus REST API. We can go home. We can go back to that card. Cool. Alright. Let's do const is editing. Ref equals false. Does this have auto imports? I'm assuming it does because it is. Let's see. Auto import, components auto importing, feed plugin components. We'll just import ref to be safe. Right? Obviously, that's already working, but let's give this some padding as well, make this look a little nicer, and we'll add a little margin to the top of this bad boy. Okay. Alright. So now we have our content. We could view this. Let's add a button to edit this thing. Right? Flex items center gap for I'll just say justify between, and then we're gonna add a button. Okay. We can edit this card. That's cool. The border b will move here. Cool. Alright. And if we edit this, what are we gonna use for the editor? View markdown editor. View markdown editor. What do we have here? What do we have? This is, again, risky business. Markdown editor in v three. Oh, no. Alright. This is it's not markdown. Base editor. Where are the demos? Import? Base editor? Oh, I guess we're this just doesn't have, like, some nice syntax. Right? Code mirror editor? Yeah. This looks way is this too cumbersome for what we're looking for? I probably should have snuffed this out earlier. Right? View editor, MD editor for view. This one's got 1,300 stars. I don't necessarily want to, like, show the information. We just need, like, a nice editor. Right? And this might be, like, something where we throw throw it back on, like, not markdown, but HTML. Right? View three markdown editor. View three markdown editor. Should probably just drop something like tip tap into this. Yeah. Let's just go for it. Okay. So PMPMI, MD editor MD editor v three. Here's what the usage looks like. Let's try to import all of this. This will be the content, And we're gonna what? Clone that content. Okay. Is editing. So this will be v m d editor. Is that what it is? V model content. And then we're gonna have a save function. Async function. Save. Await update card, which is a new one that we do not have. Update card. ID and content. Is editing value. Cool. I don't think there is a save function. Alright. So we're let's wrap this. Div. V f is editing, and we'll do a button to save. Now is this actually going to go off? I don't know. Vite server did not start. Something bad is oh, you gotta actually fire up the dev server after you install the thing, Brian. That would actually make sense. Fifteen minutes left. Can how far can we get on this internal knowledge base tool? Alright. So here we go. Search cards. We can we're not getting the card. We're throwing an issue. Use ID. Can I use list is outdated? Where do we get in here? A day card ID equals rent route top params.ID. Error when updating dependencies. What is the view version of this? Okay. So let's do PMPM. I think it's v view 3.5 view at latest. Hopefully, that gets us the use ID composable that we're talking about. PMPM dev. Okay. Syntax error does not provide an export. Alright. So that is obviously a problem. We will export update card. There's the data. Update the card, return the card. Bada bing badda boom. We are going to update the card with the value as well. Constant response, card dot value equals response. Or we can just console log this. And you could see Cursor is doing a lot of the heavy lifting for me here. What else are we getting? Service does not provide. It does provide an update card. Export async function. Services pages card. It should provide if we can get the card, why can't we update the card? Alright. So we hit edit. There we go. We can hit save. Card dot value equals response. V equals true. Shallow false. Update card. Why is the card Card. Well, that doesn't look right. On mounted card, await card response card dot value equals response. Why is this a why was this an array to begin with? Right? Extensions reload. Okay. Something is going wrong here. What are we doing wrong? The card dot contents, get card returns the card, read item cards dot ID. What is the actual response that we're getting? Inspects cards. There's the response. Card dot proxy. So why is it not showing the actual card content anymore? Car dot value. Oh, what is going on here, man? Do I have something else going on? But I define card way too many times. Response get card. So we get the card. I could see the response. The response looks good to me. Right? Go here. Go home. You see the the response. There's the response. Content. Tags. There's the card value. Maybe we set this to content dot value equals card dot value dot content. Card. Card dot title. What in the world is going on? Dep. Why are we loading? I'm not comprehending something here. VF card. VF card. We are seeing the car. Cannot read. VF card. How is the card dot title this is what I'm struggling to understand. Cards. There's the cards. There's the card. Card dot value. We can see the title right there, card dot value. Unref. Guard. We are running out of time. What is going on here, man? Where did we go wrong? Is it when we brought in the MD editor business? Alright. So this works. Yeah? Or it should. Some new, like, quirks with Vue. What is going on? I pulled in the latest version of Vue, and now we are, like, dying here. Dying on the vine. Car dot value equals response. Is there, like, a weird thing that I'm not picking up what I'm putting down here? Why does this work? And that doesn't. Cards ref. This is the exact same logic from the home. On mounted async, we get the card, Cards dot value dot response. Is this storing is like a lots of fun all the time. But cards. Click the card. See the card. There's the card. It is an object as it should be. Card dot value equals response. I don't understand. V f card has a value. Do we have to specifically say the value dot title? No. You shouldn't have to. Not in the template. Why do I keep getting this error? This would be a good question for AI, I assume. Why do I keep getting this as my content. You shouldn't have to do that inside the the template, though. Yeah. That's what I thought. You shouldn't have to unref in the template. Right? This is the case where AI, yeah, Check that response on your unmounted callback. Yeah. Huge help here. Some kinda, like, proxy issue with view, I'm assuming. I don't know. I don't know what the deal is. Read item cards. Card equals response. Blah blah blah blah blah blah blah blah. Okay. Some funky business going down. Ref car. Value. Yeah. There should be no need to actually I swear this is like a hot module reloading thing. B is ref true. What is the actual card, Joe? So if that's the card, why do we have? This is baffling me, man. So there's my card data. If that's the value of the card, should I not be able to access the card dot title? Card dot content. Oh. Oh my gosh. So that is the freaking problem when I updated and saved this card. This is like the king of deep oh, look. Sometimes. So I accidentally saved the value as that. We had everything going for us. I blew it all away just to goof this thing up big time. Hey. That's how we roll here on 100 apps. There was an issue with the save function that called caused the problem here. So let's say we got content equals ref. That'll be nothing for now. The content dot value should equal the response dot content. So that's the content we're gonna submit. Is editing. God. What an idiot. Boom. Explosion. There we go. This is one that I man, I really just have to, like, finish this one for my own sake. Failed this mission totally because I got hung up and not actually paying attention. That is the fun of this thing. Right? So here we go. We got an async function for save. Let's call it save. We're gonna update the card with the content. We'll set is editing to false. Const response response equals. We'll set the card dot value to the response. Cool. And then let's do this where we say v if is editing, v else model. We're gonna do wrap that in a div. V if is editing, and then we're gonna add a save button. Class p two blue, etcetera. Click save equals content dot value. Do we have a button? We forgot to add our edit button. And let's remove this at the top. Give this some padding. V is editing equals false. Vhtmlcard.content. Here's our content for this card. This is amazing content. Open this back up. There's our REST API. We hit edit. This shows this freaking thing. There's our editor. Test. Me. I failed the submission. Great. If we hit save, what does that do? Console update card is not defined. Obviously, we didn't import that. Update card. We'll try this again. Test, test, test. Save. Of course, there it is. There's the test, test, test content. Too little, too late, friends. And that's the way it rolls on 100 apps, one hundred hours. That's it for the internal knowledge base episode. Careful of how you say values, man. I never, for the life of me, thought, okay. Why am I getting this? Surely there would have been an error, boneheaded mistake. That's the way it goes. We'll see you on the next one.","236f7d6a-3c0e-4965-9534-a8ca7de26efd",[1084],"81e81b72-9749-40e1-a9ea-6698718110af",[],{"year":399,"number":1060,"id":1061,"episodes":1087,"show":1088},[1063,1064,1065,1066,1046,1067,1068,1069,1070,1071],{"title":322,"slug":323},{"id":1071,"slug":1090,"vimeo_id":1091,"description":1092,"tile":1093,"length":1094,"resources":12,"people":12,"episode_number":889,"published":1051,"title":1095,"video_transcript_html":1096,"video_transcript_text":1097,"content":12,"seo":1098,"status":19,"episode_people":1099,"recommendations":1101,"season":1102},"ai-copilot","1059438523","Bryant takes on his most ambitious challenge yet: building an AI Copilot that integrates directly with Directus data. Watch as he races against the clock to create a custom chat interface that can query your database, implement tool calling functionality, and (attempt to) persist conversations. Things get meta when Bryant uses AI to help build his AI assistant—with some hilariously chaotic results along the way.","b22143bc-51f9-4460-9c1e-45b2b9735375",62,"Mission: AI Copilot","\u003Cp>Speaker 0: Welcome back to another episode of 100 apps, one hundred hours. I'm your host, Brian Gillespie, for Directus. And today is a very ambitious hour. I've got no clue how this is actually gonna go, but we are going to try and build an AI copilot that sits right alongside our data, inside our back end, which is Directus. Alright.\u003C\u002Fp>\n\u003Cp>If you're new to the show, 100 apps, one hundred hours, we have sixty minutes to plan and build an application, a clone of Airbnb, did some something we can actually ship and pat ourselves on the back at the end of the day. Or, you know, more often times than not, we struggle and then we, fail publicly. You know, sometimes that stinks, but it's all in good fun. The second rule of 100 apps, one hundred hours is use whatever you have at your disposal. So, AI tools, past projects, you know, if I my kids could code at this point, I'd probably leverage them.\u003C\u002Fp>\n\u003Cp>But, it should be a fun episode today. Let's dive in and try and figure out what we're actually doing. Let's put sixty minutes on the clock. Boom. AI Copilot go.\u003C\u002Fp>\n\u003Cp>So, at this stage of the game, AI is very impossible to ignore, and, you know, chatbots are everywhere, very commonplace. You know, lots of folks using Claude, chat GPT on a daily basis. Even my kids are aware of what it is. So, what we're gonna do is try to build an AI Copilot into our Directus instance. So I've got this blank Directus instance.\u003C\u002Fp>\n\u003Cp>I'm imagining this as a module. Directus is super flexible as a back end in the CMS because we got full control over what's going on. I'm imagining it as a a module with, like, a standard chat interface. I don't know if we'll get there or not, but let's just discuss functionality. Right?\u003C\u002Fp>\n\u003Cp>We want to be able to chat with AI models, specifically LLM models. Cat. You have to actually learn to spell Bryant. Alright. We wanna be able to chat with the LLMs.\u003C\u002Fp>\n\u003Cp>We want to be able to use tool calling. I think that's the technical term to fetch Directus data. Data stored in Directus. Data stored in Directus. We wanna do this do all this through a beautiful chat interface.\u003C\u002Fp>\n\u003Cp>Alright. That's our functionality. Right? We're gonna probably have as far as our data model, we'll have, like, a conversations to, like, persist these. We've also got our data, which I'm going to leverage some of our existing, like, starter templates.\u003C\u002Fp>\n\u003Cp>Great. Okay. So there's the arrows drawn in the sand. Let's let's dive into this thing. The first thing I'm gonna do, I'm gonna load up some sample data.\u003C\u002Fp>\n\u003Cp>So what I wanna do is go in here and create a token. I could just use my standard password as well, but we're just gonna copy that out. This is set up at local host 8055. I'm gonna come into the terminal, open up a new terminal instance here, and once that spins up, I'm gonna do directus m p x. I guess I can make this big so we can actually see it.\u003C\u002Fp>\n\u003Cp>Right? Directus template CLI at latest. Apply is the command I'm gonna run, and this should load up some templates for me or the ability to load a template. Fetching. Okay.\u003C\u002Fp>\n\u003Cp>So we're gonna apply a community template. Looks great. Let's do let's just do our simple CRM for this one. You know, I could the simple website CMS, super excited about that template. I love that one.\u003C\u002Fp>\n\u003Cp>For, like, a assistant, maybe CRM makes more sense. So we're defaulting to eight zero five five. I'm just gonna add my token in there, and you could probably steal this token. This instance is gonna be destroyed, and I'm not sure I'm I'm sure somebody could figure out how to get access to my local host. I know we got a lot of really great hackers out there in the community.\u003C\u002Fp>\n\u003Cp>That template was applied successfully. And now I'm just gonna hit reload, and the first thing I see is Michael Scott there. Alright. So now I've got some sample data to at least work with. We got some organizations.\u003C\u002Fp>\n\u003Cp>Great. So let's dive into how we're actually gonna pull this off. If I map this out, basically, what we're gonna take advantage of are extensions in Directus. Extensions, extensions, extensions. Two types specifically.\u003C\u002Fp>\n\u003Cp>Right? So for secure communications to our LLMs, we want to have a API proxy endpoint. So we're gonna set up an endpoint, and I'm pretty confident we can get this done. The actual chat UI, I'm not sure that will be what we call a module. So a module is basically just whatever functionality that you want.\u003C\u002Fp>\n\u003Cp>We give you tools like composables to access data inside the Directus instance, and then we give you free reign. So, like, a good example of a module is our command bar or command pallet. This is an extension that you can install from the marketplace. What it does is it adds a command k search to every bit of Directus so I can quickly search no matter where I'm at, whether I'm on contacts and I wanna search organizations, blah blah blah. It also comes with, like, a a settings page, and it's MIT licensed.\u003C\u002Fp>\n\u003Cp>So, you know, if you are looking at building, like, a custom module, this is a good one to copy because it has a ton of functionality and, it had a small hand in it. Kudos to Hannes from our team for, dude, just making this freaking wicked awesome one. Alright, but back to the task at hand, we're going to create a custom endpoint for this. So locally, I'm not sure if you can see this or not, we've got our databases here and then we've just got this Extensions folder. There's the Registry, which is when you install extensions from the marketplace.\u003C\u002Fp>\n\u003Cp>We're just going to create a new extension for this. So I'm going to open up this inside the terminal somewhere here, get this back to a reasonable level where I can actually develop, and I'm gonna do npx create create directus extensionlatest. Just look at the screen, I'm terrible at narrating. We'll go ahead and install this. It's been a minute since I've done this.\u003C\u002Fp>\n\u003Cp>And we offer another type of extension where it is, which is called a bundle. So a bundle is basically wrapper, like we're building a ChatGPT wrapper. A bundle is essentially a wrapper where we can, like, bundle some extensions together. That's a bundle. Right, we can distribute an endpoint and a module within a bundle, that's what we're gonna do here.\u003C\u002Fp>\n\u003Cp>So I'm gonna scroll all the way down, we're gonna do bundle, we're gonna call this Directus, let's just call it AI Copilot, that's what we'll call it. Yes. We wanna auto install dependencies. And now inside our extensions folder, you can see Directus is fleshing this out for us. We've got the Directus Extensions SDK, and then we can see there's nothing else in here.\u003C\u002Fp>\n\u003Cp>Right? So the next thing that we wanna do is CD Copilot, and we're going to hit npm run add so that we can add an extension to this bundle. Yeah. The if you're not using a bundle, like, it should automatically initiate the extension type for you. But in this case, we want to do an endpoint.\u003C\u002Fp>\n\u003Cp>We're going to call this Chat Endpoint. Let's use TypeScript and this will create a new Directus endpoint for us. Very simple, You'll notice a Request Response here behind the scenes, just an Express app. And next, we're going to talk about what we're actually going to use. Giving myself a seizure here, switching back and forth.\u003C\u002Fp>\n\u003Cp>So, I've been experimenting in other projects with the AI SDK from Vercel. That's what we're gonna use here and try to get as far as we can and try to make this really nice in a short period of time. So let's fire this up, right? Docs. What do we need to install first?\u003C\u002Fp>\n\u003Cp>Right? Do I want to use OpenAI or Anthropic? I've got I think I've got an Anthropic API key locally already. Do I do I not do, EMP? Yes.\u003C\u002Fp>\n\u003Cp>I do have a API key locally. Great. So let's do that. Right? We are going to do an overview.\u003C\u002Fp>\n\u003Cp>We want to install PMPMIAI. We're gonna use Anthropic as a provider, AI SDK Anthropic. And because we're using Express here, we're gonna wanna import Express as well, or install Express. We're probably also going to wanna have Zod. Right, if we get into, like, the tool calling, Zod is a big one, so we'll add Zod.\u003C\u002Fp>\n\u003Cp>That should give us the tools that we need to work on this, and let's just look at our custom endpoint now, right. The next thing I'm going to do is hit pnpm dev inside this. This will set up a watcher for our extension, and I'm just going to stop our container and restart just so it picks up that extension now that we've built it. And what this is gonna do, it should now be available at slash chat endpoint. Let's see.\u003C\u002Fp>\n\u003Cp>Local host. So if I refresh, we should see that extension. There's our chat endpoint. Let's hunt down the URL for this. Chat endpoint.\u003C\u002Fp>\n\u003Cp>Hello, world. There it is. Boom. Boom. Boom.\u003C\u002Fp>\n\u003Cp>Great. So how can we control that if we want? Set up some of this documentation here. Alright. So the the next thing that we're going to want to specify, I could specify, like, an endpoint or an ID for this when we define the endpoint.\u003C\u002Fp>\n\u003Cp>Right? If I put these up side by side, export defaults. We're gonna call this the chat. Chat. We're gonna have a handler for this.\u003C\u002Fp>\n\u003Cp>And in the handler, we'll provide the router and the context. Alright. Now we save. This should reload because I've got that set. And now if we load this again, chat endpoint doesn't exist, but now chat will.\u003C\u002Fp>\n\u003Cp>Right? Great. So now we can control what we want that endpoint to show up as. Awesome. Alright.\u003C\u002Fp>\n\u003Cp>What's next? Right? We're going to actually pull this in and set this up. Alright, so a couple of things that we're going to do that you probably noticed here, right? If we want to pull the API key from our environment, we're gonna want to actually pull that out, right.\u003C\u002Fp>\n\u003Cp>So let's do Destructure This. We're also gonna do we actually need that? No. Let's just keep it simple for now. Let's look at this AI SDK, and we're gonna import createanthropic, and we're going to create an anthropic client.\u003C\u002Fp>\n\u003Cp>Create an anthropic API key. We're gonna leave the base URL the same. Great. I'm not sure why we're duplicating that. Right.\u003C\u002Fp>\n\u003Cp>We're gonna add a router. This is gonna be a post method. So let's now take a look at was it did I have a I have an express option in here? Okay. So here's what a typical express setup might look like.\u003C\u002Fp>\n\u003Cp>So we're gonna import z from za. We'll go ahead and import za. Let's also do stream text from AI. We're gonna use router dot post, and let's see what AI comes up with. Message request dot body stream twenty twenty.\u003C\u002Fp>\n\u003Cp>Let's see the newer model there. Provider instance, anthropic provider. Does this list out their models? Okay. Yeah.\u003C\u002Fp>\n\u003Cp>Here's the latest model for Claude SONNET. That's what we're gonna use in this case. And we got messages, role, user content, message. We're just gonna send the stream. What is the is there a helper inside here for this?\u003C\u002Fp>\n\u003Cp>Result dot stream. Results dot pipe stream to response. Let's call this the result. Alright. And let's see if this is actually going to give us what we want.\u003C\u002Fp>\n\u003Cp>Alright. So we post a message. This should send something to the AI. I'm just gonna go into hundred apps. We're gonna create a new request.\u003C\u002Fp>\n\u003Cp>We'll say HTTP local host eight zero five five slash chat. Oh, that's the name and not the actual URL. Chat. Alright. We got a body.\u003C\u002Fp>\n\u003Cp>We're gonna create a JSON body. It's gonna have a message. What is two plus two? Send HTTP request wrong version number or prototype blah blah blah blah blah blah. Maybe we should wrap this in a try catch, see what we got.\u003C\u002Fp>\n\u003Cp>Oh, clean that up for me. Oh, well, no. No. No. No.\u003C\u002Fp>\n\u003Cp>No. Try catch console .log dot error. Do we have formatting? I do have some formatting enabled in this. Let's just try to send the results.\u003C\u002Fp>\n\u003Cp>Pres that's in result. Extension's reloaded. What is this showing? StreamText. Create anthropic API key.\u003C\u002Fp>\n\u003Cp>Console dot log. Do we have the actual ENV? We do have the ENV. Did I name it the correct thing? Oh, god.\u003C\u002Fp>\n\u003Cp>What an idiot. HTTPS should be HTTP base stream, send result. I don't know that the actual Bruno here will, like, handle streaming. What if we just tried, like, generate text? Generate text.\u003C\u002Fp>\n\u003Cp>Awaits. Let's just make sure this is gonna actually work first before we get crazy with it. Generate text results dot send. Result. Alright.\u003C\u002Fp>\n\u003Cp>Let's pull chat over here. What's two plus two? We hit send. Okay. What is two plus two?\u003C\u002Fp>\n\u003Cp>Finished reason, two plus two equals four. There's the text we get back. Okay. So now we've got, like, some type of AI proxy going on. Right?\u003C\u002Fp>\n\u003Cp>Let's jump to, like, tool calling at this point. We have, like, forty minutes left. You know, I read through the documentation for this. Not sure we're gonna get this correct, but alright. This may be where we just lean on AI to build AI and call it good.\u003C\u002Fp>\n\u003Cp>What I'm gonna do let's just copy all of this. Right? And this wouldn't be fun if we didn't, like, make it super meta and have AI create AI. Let's say something like this. Here's the documentation for tool calling with Vercel AI SDK in Node.\u003C\u002Fp>\n\u003Cp>Create a tool for fetching data from the Directus instance using the items service. Alright. So when it comes to, like, custom endpoints in Directus, one of the things that you do have access to is all the services that we use internally as in Directus, and, hopefully, this is gonna do a somewhat decent job of that. We'll kinda see what it comes up with. But if I take a look at, like, the extensions docs and we go to extension services, like, how do I access items?\u003C\u002Fp>\n\u003Cp>Right? Whenever I define a custom endpoint, we receive the, you know, router instance that we can plug into, and then we get this context. So the context has the EMV, has our services, it has this helper function for getting the schema. And then we define a new item service based on the collection that we're gonna call. Right?\u003C\u002Fp>\n\u003Cp>Let's see what we come up with. So I'm just gonna hit apply. We'll kinda go through this. We got stream text, generate text, item service, this looks okay. We're also gonna need schema, services get schema.\u003C\u002Fp>\n\u003Cp>We're gonna need that as well. Alright. So what is this actually doing? It's creating tools, multiple rounds, query from a a Directus collection. What's the filter?\u003C\u002Fp>\n\u003Cp>Object equals await get dot schema. Request dot accountability. Can we just leave that part off? That will be read by query, filter, success, return the items, the count, accept the file, await generate text. Something is off here.\u003C\u002Fp>\n\u003Cp>We got, like, one too many. Where are we going wrong? There we go. Okay. Alright.\u003C\u002Fp>\n\u003Cp>So, hopefully, if we invoke this tool, there's a description. Now let's try a new message, and let's just ask it let's pull up Directus. Right? Simple question. Right?\u003C\u002Fp>\n\u003Cp>Like, how many organizations do we have? How many organizations are in our Directus instance? There are six organizations in the Directus instance. These are Health Plus, Technovus, Solartec, Tesla. Boom.\u003C\u002Fp>\n\u003Cp>So nailed it spot on. We can see kind of what's going on here. There's the context that that we provided, I guess. I could see, like, the answers there. That's great.\u003C\u002Fp>\n\u003Cp>I'll help you. Okay. So there's the actual organizations. Cool. So now we've got kind of this tool calling functionality into this endpoint, which is pretty nice.\u003C\u002Fp>\n\u003Cp>Do we want to make it aware of the schema of Directus? I'm not sure, like, is this giving it enough information? Or tools, query collection. What does our actual schema look like? Right?\u003C\u002Fp>\n\u003Cp>Console log, await. Console schema equals await git schema. Oh, it's gonna be a async handler. Hansel dot log schema. We'll just stop logging that.\u003C\u002Fp>\n\u003Cp>Directus flows, schema, deals, activities, deal contacts. Yeah. So the other thing to note that this could get expensive pretty quickly. Alright. Okay.\u003C\u002Fp>\n\u003Cp>So now we are cooking at least with something. Right? Let's try to go in and focus on, like, this chat interface. Right? Now how are we going to do that?\u003C\u002Fp>\n\u003Cp>We're going to go back to our extension. I'm going to disable this for now. We're going to hit npm run add. We want to add a module for this. So this will give us a, we'll call this a Copilot module.\u003C\u002Fp>\n\u003Cp>Chat module, whatever. There we go. So this is gonna add another folder within our AI Copilot. So now we got the chat endpoint. We've got the Copilot module.\u003C\u002Fp>\n\u003Cp>And within this module, now if we just build this, we should see this module becoming available in our settings for the project. Custom. Custom module. There we go. So I can actually define this.\u003C\u002Fp>\n\u003Cp>Right? So each module or a lot of the interface extensions have this index dot ts. We're gonna call this our Copilot module. Let's call it Directus AI Copilot Magic. Is it gonna be Magic?\u003C\u002Fp>\n\u003Cp>Oh, there's not a Magic icon. What do we have as far as icons? Designer OCD gets me every time. A recovering designer. What is that one called?\u003C\u002Fp>\n\u003Cp>Magic button. Magic button. See if that gets us where we wanna be. Discard. Refresh.\u003C\u002Fp>\n\u003Cp>There we go. Alright. So I'm gonna enable this. We're gonna move it up to the very tippy top. There's our module.\u003C\u002Fp>\n\u003Cp>Here's our custom content. Boom. Didi boom. It's just a simple view component. Right?\u003C\u002Fp>\n\u003Cp>We can get as complex or as detailed as we want. On the client side here, we have access to some composables. Right? So on the back end, like the the Node. Js side, we've got, like, this item service we can call.\u003C\u002Fp>\n\u003Cp>On our other extensions where's the extension types? There we go. Like interfaces, for example, accessing internal systems, we can access, like, stores through this. We can also access, like, the used API or the SDK. Great.\u003C\u002Fp>\n\u003Cp>We'll just pull in the API. And now let's look at the documentation. So by no means am I an expert here, and this is probably where things are gonna get pretty hairy. Like, if we recap, we've got a proxy endpoint. We've implemented just a tool to fetch Directus data and include that in our Copilot.\u003C\u002Fp>\n\u003Cp>Now we need to actually build this, like, this chat UI that we're so used to seeing, right? And Directus is using Vue, which I love Vue. Vue is amazing. Most of these AI, like, pre built chat UIs are React based. So if you know of a good one that's for Vue, for building chatbots really quickly, let me know.\u003C\u002Fp>\n\u003Cp>So let's go here to SDK UI. Right? They do have support for, like, Vue JS. I see that here, this use chat function. We're gonna need that AI SDK view, so we might as well go ahead and install that as well.\u003C\u002Fp>\n\u003Cp>AI SDK view. Okay. There's the API endpoints, conversational interface. Is there, like, a simple recipe for this? Stream checks with a chat prompt.\u003C\u002Fp>\n\u003Cp>Alright. So, again, this might be a great thing to lean on AI for using here's an re a React example from our the Vercel, SDK. Or, actually, let's just piggyback off that last chat. Let's see. Here's a React example from for chat UI from Vercel AI SDK.\u003C\u002Fp>\n\u003Cp>Oh, actually, I have to show that. Build a view version of this that works with our chat endpoint. There we go. See what AI comes up with. So it's creating another composable.\u003C\u002Fp>\n\u003Cp>Why are we just use the built in used chat from Vue AI SDK. How do I go back to that? Chatbot AI SDK, chatbots. That was what? Under the API reference?\u003C\u002Fp>\n\u003Cp>Use chat view. And I just yeah. SDK view. Okay. And now we're getting somewhere.\u003C\u002Fp>\n\u003Cp>We got some messages. We got v button, v text area. No. Alright. Let's let's try and look at what's going on here.\u003C\u002Fp>\n\u003Cp>Handle submit, use chat, use API, AI Copilot, message content. So here's our messages. The v text area, v button, these are built in components within Directus. It is using Tailwind, which we we don't use Tailwind to Directus. So, use vanilla CSS instead, And then we can hit save and see if this is actually gonna work.\u003C\u002Fp>\n\u003Cp>Right? Now, I will tell you, like, I I've been using Cursor a lot recording this season of a hundred apps, hundred hours. I like it a lot, especially for prototyping. I found myself definitely suspect of the code sometimes, so don't just tap, tap, tap, accept. Yeah.\u003C\u002Fp>\n\u003Cp>Be very deliberate. And before you ship anything to production, make sure you test all this and go back and understand what's going on. This is not going to work, obviously. I just know our variable syntax, all of these have dash theme, dash dash in there. So hopefully, this solves that, but those are the things that you gotta watch and sometimes where, like, the AI Copilot stuff can get in the way.\u003C\u002Fp>\n\u003Cp>Right? How many orgs, organizations are in Directus? There is no icon button here. What would that be? Send.\u003C\u002Fp>\n\u003Cp>Google material symbols. Is there a send? There is a send. We don't have a icon prop here as well, so we're gonna do something like this, like v icon name equals send. That should give us the icon that we're looking for.\u003C\u002Fp>\n\u003Cp>Yeah. There we go. Okay. How many organizations are in Directus? Alright.\u003C\u002Fp>\n\u003Cp>So is this actually gonna work? Who knows? We'll see. Chat is not coming back. Internal server error.\u003C\u002Fp>\n\u003Cp>There's the messages. I don't think we're gonna need to pass the ID of this. Right? So if we look for a unique identifier for the chat, let's not use that. We got slash chat.\u003C\u002Fp>\n\u003Cp>Where is the local host slash chat? Alright. If we look at our endpoint, right, we need to adjust this. Right? We're gonna do StreamText, and we want to do the result for that.\u003C\u002Fp>\n\u003Cp>So we're gonna pipe that stream. Messages, content dot messages. What is the example for this look like? Is there where's that node? Like, so much of development for me is diving through documentation and just bouncing back and forth.\u003C\u002Fp>\n\u003Cp>Express. You can set up an Express server, stream, generate text, stream text with a chat prompt. Stream object, stream stream text. You probably just wanna look at it like the actual APIs here. Stream text.\u003C\u002Fp>\n\u003Cp>What are we passing to stream text? We've got messages that we're gonna pass. So those are just gonna be coming from the body. Right? Those are gonna be messages.\u003C\u002Fp>\n\u003Cp>We'll just pass those messages. And is that gonna get us what we want? Tools is already there. Do we need, like, a max tokens? No.\u003C\u002Fp>\n\u003Cp>Alright. Let's just see how far this gets us. How many organizations are in Directus? Okay. There's the so we're seeing the response.\u003C\u002Fp>\n\u003Cp>I can see the actual response there, but I'm not seeing it show up in our actual module here. Right? Messages. Alright. Use chat.\u003C\u002Fp>\n\u003Cp>Append. Let's just test what they're doing. Yeah. So that's kind of what we want there. Append content inputs.\u003C\u002Fp>\n\u003Cp>Result to data stream response, system helpful assistance, messages, I don't know why we have handle input change. Set appends. Let's see what's actually going on. This is where things get tricky. Right?\u003C\u002Fp>\n\u003Cp>Is there a handle input change inside the use chat model? Use chat. Initial message, initial input on. Handle submit. What does this return?\u003C\u002Fp>\n\u003Cp>Handle submit. I don't think there is a handle input change. Right? So, again, this is where, like, things get dangerous with AI, where you take 10 giant steps forward, and then you take, like, six giant steps backwards just trying to figure out what's actually happening here. So, chatbots.\u003C\u002Fp>\n\u003Cp>Where's the core StreamText. Well, actually, let's look at use chat on this side. Right? Initial input, initial messages on tool call, on response. If set to stream protocol, stream protocol should be text, I guess.\u003C\u002Fp>\n\u003Cp>Let's see how many orgs are in Directus. Oh, there we go. Okay. Now we're getting somewhere. Right?\u003C\u002Fp>\n\u003Cp>Okay. So now we're seeing the text being streamed in. If I wanted to select this, I can't because of what we've got set up in Directus. But, eighteen minutes in, like, we've got chat with AI models. We've got tool calling.\u003C\u002Fp>\n\u003Cp>We are somewhat through. Like, maybe we strike through half of that. We do all this through a chat interface. Alright. Now alright.\u003C\u002Fp>\n\u003Cp>Let's dive into, like, persistence. What do you think of those names? What do you think of those names? Okay. Yeah.\u003C\u002Fp>\n\u003Cp>We should probably have some type of, like, markdown formatting here as well. Is that what's coming back from the actual API? Is it just markdown? Yes. Okay.\u003C\u002Fp>\n\u003Cp>So do we have do we have a markdown helper inside Directus? Directus dash Directus issues. Cisco markdown. Directives markdown. Can we access this directive?\u003C\u002Fp>\n\u003Cp>The markdown. The markdown. Or we're buying that as MD? BMD? BMD.\u003C\u002Fp>\n\u003Cp>Okay. So can we do this? VMD equals message.content. Obviously, we're gonna lose that. Hey, o.\u003C\u002Fp>\n\u003Cp>Yeah. There we go. Now we're in some formatting. Looking nice. Looking nice.\u003C\u002Fp>\n\u003Cp>Okay. Get specific fields from a collection. Here to help you work with direct to collections. What do we have inside the database? Alright.\u003C\u002Fp>\n\u003Cp>Contacts. Who is the coolest contact in our CRM? Each of these contacts has their own interesting qualities. Without knowing your specific criteria for coolness, it is hard to definitively say who is the coolest. Edon kind of sounds like Elon.\u003C\u002Fp>\n\u003Cp>This might be a playful reference. Who knows? Who knows? Alright. So this is super interesting.\u003C\u002Fp>\n\u003Cp>Right? Let's work on persistence. Let's work on persistence to our directus database, what would the best data model be knowing the structure for messages. So if we look at stream stream object, stream text, And, like, our endpoint here is just streaming back the pipe text stream two response. Is there, like, a pipe data stream?\u003C\u002Fp>\n\u003Cp>Yeah. There you go. That's how we could get the actual messages. Pipe data stream to response. Send message data object.\u003C\u002Fp>\n\u003Cp>Okay. And then we should be able to like, inside our module, we can use the stream protocol. Let's just set this and see see if this is gonna work as well. Hey, o. Okay.\u003C\u002Fp>\n\u003Cp>But now instead, what we're getting back is actually the message data as well, which is probably what we're gonna want. Alright. What did you come up with, friend? So we've got messages, conversations, messages. So this is actually trying to give us a direct to schema, which kinda scares me a bit.\u003C\u002Fp>\n\u003Cp>So we got a user conversations. We got an ID. We got a title. We got a message schema. Messages, ID, conversation, role, content, tool calls, tool results.\u003C\u002Fp>\n\u003Cp>Okay. So this is the type of response we're getting back. I'm just gonna save that. Right? Alright.\u003C\u002Fp>\n\u003Cp>That was the payload that we sent. What is the response that we got? Okay. And now we got twelve minutes left just to, like, try to round this out and see if we can get this persistence part of it. Alright.\u003C\u002Fp>\n\u003Cp>So we're gonna create conversations. This is gonna be a UUID for the ID, created at for the time stamp, created by, updated at, updated by I will just go with what AI said here. I really don't wanna think too much about this. We'll do a mini to one relationship here. We'll call that the user.\u003C\u002Fp>\n\u003Cp>The related collection we're gonna use is directus underscore users. It'll show a link to the item. Great. And then we're gonna go in and create, what, messages. Messages.\u003C\u002Fp>\n\u003Cp>These are gonna be what did it say there? That's a UUID. Yeah. UUID. Conversation?\u003C\u002Fp>\n\u003Cp>Yes. I'll link those together. Content. Oh, not created at. Created at for the time stamp.\u003C\u002Fp>\n\u003Cp>Created at. K. We're gonna have a role for that. Role is gonna be what? That could be a drop down, so we could set that up as a drop down if we wanted to.\u003C\u002Fp>\n\u003Cp>User, that's gonna be user, or assistant. Too many a's. Assistant. Too many s's. Too many s's.\u003C\u002Fp>\n\u003Cp>Alright. And then we have the message content, which I I think is gonna be marked down, content. We could say tool calls, tool calls, Tool results. I don't know if this is actually gonna be it or not. Tool results, ID, conversation.\u003C\u002Fp>\n\u003Cp>Then we're just gonna link this together. I add a mini to one relationship here. Conversation. The related collection is gonna be conversations. We're gonna show a link.\u003C\u002Fp>\n\u003Cp>And what I'm gonna do, I almost always go into the advanced field relationships or the advanced, advanced mode when creating fields. I'm gonna add the reverse in here. So I'm gonna add all the messages to this conversation. Great. And okay.\u003C\u002Fp>\n\u003Cp>So now create conversation, load conversation, save a message, use chat persistence. Okay. Yeah. Alright. Let's just roll with it.\u003C\u002Fp>\n\u003Cp>This is gonna create a new composable. Use chat persistence. Sometimes it's fun just to shut your brain off and totally, just not think about it. It's fun that this show, honestly. Like, how fast can we turn something?\u003C\u002Fp>\n\u003Cp>So let's go back to our module. We're gonna import this. The actual import is just gonna be from use chat persistence. And we're not there is no handle input change, right? Handle submit.\u003C\u002Fp>\n\u003Cp>Load conversations. Handle submit. Use chat. Stream protocol. Save message.\u003C\u002Fp>\n\u003Cp>V model input, we do have inputs. We need to import on mounted watch from view. Use chat persistence. What's that gonna be? Dot JS.\u003C\u002Fp>\n\u003Cp>Or is that just like a default? Nope. Export function, use chat persistence. Why is it not picking that up? Oh, because it's in the same directory.\u003C\u002Fp>\n\u003Cp>Duh. Alright. Seven minutes remaining. Let's see what happens now. AI on top of AI, building AI.\u003C\u002Fp>\n\u003Cp>How can you help me? Alright. So we'll just watch the network request here. That's finished. I don't see another network request.\u003C\u002Fp>\n\u003Cp>Oh, maybe you gotta refresh first. How can you help me? Oh, boy. I'm not sure if you can see what's going on here, but we are going to be bricking this instance very quickly if we continue that. So, conversations, right, what do we do here?\u003C\u002Fp>\n\u003Cp>We've created some type of infinite loop. Thank you, AI. Love it. We're gonna load conversations on that. Watch conversations.\u003C\u002Fp>\n\u003Cp>Where are we getting the ID? Oh, why are we passing it? Yeah. Why do we want we don't want that. Is that what it is?\u003C\u002Fp>\n\u003Cp>The save message Is that where the problem came in at? I don't know. It's saving the same ID test. Nope. Still there.\u003C\u002Fp>\n\u003Cp>Alright. So you messed up, bro. You introduced an infinite loop. Why did you do that? Is it somewhere in our composed load message?\u003C\u002Fp>\n\u003Cp>Handle submit function calls itself recursively. Yeah. Okay. Yeah. Okay.\u003C\u002Fp>\n\u003Cp>Okay. Well, what did you change? Handle submit, await, append using Alright. Now is it possible to nuke all of these? Let's just nuke all these messages.\u003C\u002Fp>\n\u003Cp>Right? I'm gonna go straight to the database. Boom. Boom. Boom.\u003C\u002Fp>\n\u003Cp>This is fun. I like messing around with this AI stuff. We're just gonna nuke all of these conversations. There's 80,000,000 conversations as well. Maybe now this will actually work.\u003C\u002Fp>\n\u003Cp>Oh, some type of error handling there. It just disappears. Hey. Okay. So now we should be getting back to where we need to be.\u003C\u002Fp>\n\u003Cp>Messages. There we go. It's not saving the conversations correctly. Conversation. Current conversation.\u003C\u002Fp>\n\u003Cp>What is the current conversation? Current conversations dot ref. Well, no. There we go. We're creating a conversation.\u003C\u002Fp>\n\u003Cp>So current conversation dot value, save message, current conversation dot value. So why isn't it creating a conversation? Let's say create conversation. So we're gonna create a conversation when we load this. And maybe that'll fix the persistence issue.\u003C\u002Fp>\n\u003Cp>There's the conversation. Test message. Some type of stream issue going on there. But if we now go to conversations. No.\u003C\u002Fp>\n\u003Cp>Gremlins in the AI. This is why you don't have AI do your coding for you. Super helpful, though. Right? No chance I would have gotten here as far as we have in an hour without this tool.\u003C\u002Fp>\n\u003Cp>Obviously, lots of bugs and interface issues to squash before that. What's how many deals do we have outstanding? Let's try a simpler query. Deal stage ID. Are there deals?\u003C\u002Fp>\n\u003Cp>I thought there were deals. And maybe it doesn't know enough about the it just doesn't know enough about it. I know I've introduced some bugs. I kinda wanna end on a high note here, but we're running out of time. Right?\u003C\u002Fp>\n\u003Cp>We're gonna call it good. Let's end on a good one. What kind of collections do we have here? Organizations deals. You could probably feed it, like, the current user as well.\u003C\u002Fp>\n\u003Cp>That's gonna be, like, accountability, maybe. I don't know. How many open task activities activities are there? Structure of the activities collection. Man, I would just ruin this.\u003C\u002Fp>\n\u003Cp>How many orgs are there? I can't even add another working example. Sometimes that's how these things go. How many organizations are there? Yeah.\u003C\u002Fp>\n\u003Cp>We're now getting, like, crazy mad errors that we broke something. Alright. So there it is. Some type of weird infinite recursion glitch that I have introduced by just leveraging AI and tabbing and accepting. So now there's a lot of debugging involved in this, but this is a great example of the power of Directus, the power of AI, Vercel AI SDK, whoever's working on this, if you watch this, amazing work on this to make, this sort of stuff really easy and accessible and quick to pick up.\u003C\u002Fp>\n\u003Cp>Boom. Goes the dynamite. That's it for this episode of 100 apps, one hundred hours. I hope you will stay tuned for the next episode. Ciao.\u003C\u002Fp>","Welcome back to another episode of 100 apps, one hundred hours. I'm your host, Brian Gillespie, for Directus. And today is a very ambitious hour. I've got no clue how this is actually gonna go, but we are going to try and build an AI copilot that sits right alongside our data, inside our back end, which is Directus. Alright. If you're new to the show, 100 apps, one hundred hours, we have sixty minutes to plan and build an application, a clone of Airbnb, did some something we can actually ship and pat ourselves on the back at the end of the day. Or, you know, more often times than not, we struggle and then we, fail publicly. You know, sometimes that stinks, but it's all in good fun. The second rule of 100 apps, one hundred hours is use whatever you have at your disposal. So, AI tools, past projects, you know, if I my kids could code at this point, I'd probably leverage them. But, it should be a fun episode today. Let's dive in and try and figure out what we're actually doing. Let's put sixty minutes on the clock. Boom. AI Copilot go. So, at this stage of the game, AI is very impossible to ignore, and, you know, chatbots are everywhere, very commonplace. You know, lots of folks using Claude, chat GPT on a daily basis. Even my kids are aware of what it is. So, what we're gonna do is try to build an AI Copilot into our Directus instance. So I've got this blank Directus instance. I'm imagining this as a module. Directus is super flexible as a back end in the CMS because we got full control over what's going on. I'm imagining it as a a module with, like, a standard chat interface. I don't know if we'll get there or not, but let's just discuss functionality. Right? We want to be able to chat with AI models, specifically LLM models. Cat. You have to actually learn to spell Bryant. Alright. We wanna be able to chat with the LLMs. We want to be able to use tool calling. I think that's the technical term to fetch Directus data. Data stored in Directus. Data stored in Directus. We wanna do this do all this through a beautiful chat interface. Alright. That's our functionality. Right? We're gonna probably have as far as our data model, we'll have, like, a conversations to, like, persist these. We've also got our data, which I'm going to leverage some of our existing, like, starter templates. Great. Okay. So there's the arrows drawn in the sand. Let's let's dive into this thing. The first thing I'm gonna do, I'm gonna load up some sample data. So what I wanna do is go in here and create a token. I could just use my standard password as well, but we're just gonna copy that out. This is set up at local host 8055. I'm gonna come into the terminal, open up a new terminal instance here, and once that spins up, I'm gonna do directus m p x. I guess I can make this big so we can actually see it. Right? Directus template CLI at latest. Apply is the command I'm gonna run, and this should load up some templates for me or the ability to load a template. Fetching. Okay. So we're gonna apply a community template. Looks great. Let's do let's just do our simple CRM for this one. You know, I could the simple website CMS, super excited about that template. I love that one. For, like, a assistant, maybe CRM makes more sense. So we're defaulting to eight zero five five. I'm just gonna add my token in there, and you could probably steal this token. This instance is gonna be destroyed, and I'm not sure I'm I'm sure somebody could figure out how to get access to my local host. I know we got a lot of really great hackers out there in the community. That template was applied successfully. And now I'm just gonna hit reload, and the first thing I see is Michael Scott there. Alright. So now I've got some sample data to at least work with. We got some organizations. Great. So let's dive into how we're actually gonna pull this off. If I map this out, basically, what we're gonna take advantage of are extensions in Directus. Extensions, extensions, extensions. Two types specifically. Right? So for secure communications to our LLMs, we want to have a API proxy endpoint. So we're gonna set up an endpoint, and I'm pretty confident we can get this done. The actual chat UI, I'm not sure that will be what we call a module. So a module is basically just whatever functionality that you want. We give you tools like composables to access data inside the Directus instance, and then we give you free reign. So, like, a good example of a module is our command bar or command pallet. This is an extension that you can install from the marketplace. What it does is it adds a command k search to every bit of Directus so I can quickly search no matter where I'm at, whether I'm on contacts and I wanna search organizations, blah blah blah. It also comes with, like, a a settings page, and it's MIT licensed. So, you know, if you are looking at building, like, a custom module, this is a good one to copy because it has a ton of functionality and, it had a small hand in it. Kudos to Hannes from our team for, dude, just making this freaking wicked awesome one. Alright, but back to the task at hand, we're going to create a custom endpoint for this. So locally, I'm not sure if you can see this or not, we've got our databases here and then we've just got this Extensions folder. There's the Registry, which is when you install extensions from the marketplace. We're just going to create a new extension for this. So I'm going to open up this inside the terminal somewhere here, get this back to a reasonable level where I can actually develop, and I'm gonna do npx create create directus extensionlatest. Just look at the screen, I'm terrible at narrating. We'll go ahead and install this. It's been a minute since I've done this. And we offer another type of extension where it is, which is called a bundle. So a bundle is basically wrapper, like we're building a ChatGPT wrapper. A bundle is essentially a wrapper where we can, like, bundle some extensions together. That's a bundle. Right, we can distribute an endpoint and a module within a bundle, that's what we're gonna do here. So I'm gonna scroll all the way down, we're gonna do bundle, we're gonna call this Directus, let's just call it AI Copilot, that's what we'll call it. Yes. We wanna auto install dependencies. And now inside our extensions folder, you can see Directus is fleshing this out for us. We've got the Directus Extensions SDK, and then we can see there's nothing else in here. Right? So the next thing that we wanna do is CD Copilot, and we're going to hit npm run add so that we can add an extension to this bundle. Yeah. The if you're not using a bundle, like, it should automatically initiate the extension type for you. But in this case, we want to do an endpoint. We're going to call this Chat Endpoint. Let's use TypeScript and this will create a new Directus endpoint for us. Very simple, You'll notice a Request Response here behind the scenes, just an Express app. And next, we're going to talk about what we're actually going to use. Giving myself a seizure here, switching back and forth. So, I've been experimenting in other projects with the AI SDK from Vercel. That's what we're gonna use here and try to get as far as we can and try to make this really nice in a short period of time. So let's fire this up, right? Docs. What do we need to install first? Right? Do I want to use OpenAI or Anthropic? I've got I think I've got an Anthropic API key locally already. Do I do I not do, EMP? Yes. I do have a API key locally. Great. So let's do that. Right? We are going to do an overview. We want to install PMPMIAI. We're gonna use Anthropic as a provider, AI SDK Anthropic. And because we're using Express here, we're gonna wanna import Express as well, or install Express. We're probably also going to wanna have Zod. Right, if we get into, like, the tool calling, Zod is a big one, so we'll add Zod. That should give us the tools that we need to work on this, and let's just look at our custom endpoint now, right. The next thing I'm going to do is hit pnpm dev inside this. This will set up a watcher for our extension, and I'm just going to stop our container and restart just so it picks up that extension now that we've built it. And what this is gonna do, it should now be available at slash chat endpoint. Let's see. Local host. So if I refresh, we should see that extension. There's our chat endpoint. Let's hunt down the URL for this. Chat endpoint. Hello, world. There it is. Boom. Boom. Boom. Great. So how can we control that if we want? Set up some of this documentation here. Alright. So the the next thing that we're going to want to specify, I could specify, like, an endpoint or an ID for this when we define the endpoint. Right? If I put these up side by side, export defaults. We're gonna call this the chat. Chat. We're gonna have a handler for this. And in the handler, we'll provide the router and the context. Alright. Now we save. This should reload because I've got that set. And now if we load this again, chat endpoint doesn't exist, but now chat will. Right? Great. So now we can control what we want that endpoint to show up as. Awesome. Alright. What's next? Right? We're going to actually pull this in and set this up. Alright, so a couple of things that we're going to do that you probably noticed here, right? If we want to pull the API key from our environment, we're gonna want to actually pull that out, right. So let's do Destructure This. We're also gonna do we actually need that? No. Let's just keep it simple for now. Let's look at this AI SDK, and we're gonna import createanthropic, and we're going to create an anthropic client. Create an anthropic API key. We're gonna leave the base URL the same. Great. I'm not sure why we're duplicating that. Right. We're gonna add a router. This is gonna be a post method. So let's now take a look at was it did I have a I have an express option in here? Okay. So here's what a typical express setup might look like. So we're gonna import z from za. We'll go ahead and import za. Let's also do stream text from AI. We're gonna use router dot post, and let's see what AI comes up with. Message request dot body stream twenty twenty. Let's see the newer model there. Provider instance, anthropic provider. Does this list out their models? Okay. Yeah. Here's the latest model for Claude SONNET. That's what we're gonna use in this case. And we got messages, role, user content, message. We're just gonna send the stream. What is the is there a helper inside here for this? Result dot stream. Results dot pipe stream to response. Let's call this the result. Alright. And let's see if this is actually going to give us what we want. Alright. So we post a message. This should send something to the AI. I'm just gonna go into hundred apps. We're gonna create a new request. We'll say HTTP local host eight zero five five slash chat. Oh, that's the name and not the actual URL. Chat. Alright. We got a body. We're gonna create a JSON body. It's gonna have a message. What is two plus two? Send HTTP request wrong version number or prototype blah blah blah blah blah blah. Maybe we should wrap this in a try catch, see what we got. Oh, clean that up for me. Oh, well, no. No. No. No. No. Try catch console .log dot error. Do we have formatting? I do have some formatting enabled in this. Let's just try to send the results. Pres that's in result. Extension's reloaded. What is this showing? StreamText. Create anthropic API key. Console dot log. Do we have the actual ENV? We do have the ENV. Did I name it the correct thing? Oh, god. What an idiot. HTTPS should be HTTP base stream, send result. I don't know that the actual Bruno here will, like, handle streaming. What if we just tried, like, generate text? Generate text. Awaits. Let's just make sure this is gonna actually work first before we get crazy with it. Generate text results dot send. Result. Alright. Let's pull chat over here. What's two plus two? We hit send. Okay. What is two plus two? Finished reason, two plus two equals four. There's the text we get back. Okay. So now we've got, like, some type of AI proxy going on. Right? Let's jump to, like, tool calling at this point. We have, like, forty minutes left. You know, I read through the documentation for this. Not sure we're gonna get this correct, but alright. This may be where we just lean on AI to build AI and call it good. What I'm gonna do let's just copy all of this. Right? And this wouldn't be fun if we didn't, like, make it super meta and have AI create AI. Let's say something like this. Here's the documentation for tool calling with Vercel AI SDK in Node. Create a tool for fetching data from the Directus instance using the items service. Alright. So when it comes to, like, custom endpoints in Directus, one of the things that you do have access to is all the services that we use internally as in Directus, and, hopefully, this is gonna do a somewhat decent job of that. We'll kinda see what it comes up with. But if I take a look at, like, the extensions docs and we go to extension services, like, how do I access items? Right? Whenever I define a custom endpoint, we receive the, you know, router instance that we can plug into, and then we get this context. So the context has the EMV, has our services, it has this helper function for getting the schema. And then we define a new item service based on the collection that we're gonna call. Right? Let's see what we come up with. So I'm just gonna hit apply. We'll kinda go through this. We got stream text, generate text, item service, this looks okay. We're also gonna need schema, services get schema. We're gonna need that as well. Alright. So what is this actually doing? It's creating tools, multiple rounds, query from a a Directus collection. What's the filter? Object equals await get dot schema. Request dot accountability. Can we just leave that part off? That will be read by query, filter, success, return the items, the count, accept the file, await generate text. Something is off here. We got, like, one too many. Where are we going wrong? There we go. Okay. Alright. So, hopefully, if we invoke this tool, there's a description. Now let's try a new message, and let's just ask it let's pull up Directus. Right? Simple question. Right? Like, how many organizations do we have? How many organizations are in our Directus instance? There are six organizations in the Directus instance. These are Health Plus, Technovus, Solartec, Tesla. Boom. So nailed it spot on. We can see kind of what's going on here. There's the context that that we provided, I guess. I could see, like, the answers there. That's great. I'll help you. Okay. So there's the actual organizations. Cool. So now we've got kind of this tool calling functionality into this endpoint, which is pretty nice. Do we want to make it aware of the schema of Directus? I'm not sure, like, is this giving it enough information? Or tools, query collection. What does our actual schema look like? Right? Console log, await. Console schema equals await git schema. Oh, it's gonna be a async handler. Hansel dot log schema. We'll just stop logging that. Directus flows, schema, deals, activities, deal contacts. Yeah. So the other thing to note that this could get expensive pretty quickly. Alright. Okay. So now we are cooking at least with something. Right? Let's try to go in and focus on, like, this chat interface. Right? Now how are we going to do that? We're going to go back to our extension. I'm going to disable this for now. We're going to hit npm run add. We want to add a module for this. So this will give us a, we'll call this a Copilot module. Chat module, whatever. There we go. So this is gonna add another folder within our AI Copilot. So now we got the chat endpoint. We've got the Copilot module. And within this module, now if we just build this, we should see this module becoming available in our settings for the project. Custom. Custom module. There we go. So I can actually define this. Right? So each module or a lot of the interface extensions have this index dot ts. We're gonna call this our Copilot module. Let's call it Directus AI Copilot Magic. Is it gonna be Magic? Oh, there's not a Magic icon. What do we have as far as icons? Designer OCD gets me every time. A recovering designer. What is that one called? Magic button. Magic button. See if that gets us where we wanna be. Discard. Refresh. There we go. Alright. So I'm gonna enable this. We're gonna move it up to the very tippy top. There's our module. Here's our custom content. Boom. Didi boom. It's just a simple view component. Right? We can get as complex or as detailed as we want. On the client side here, we have access to some composables. Right? So on the back end, like the the Node. Js side, we've got, like, this item service we can call. On our other extensions where's the extension types? There we go. Like interfaces, for example, accessing internal systems, we can access, like, stores through this. We can also access, like, the used API or the SDK. Great. We'll just pull in the API. And now let's look at the documentation. So by no means am I an expert here, and this is probably where things are gonna get pretty hairy. Like, if we recap, we've got a proxy endpoint. We've implemented just a tool to fetch Directus data and include that in our Copilot. Now we need to actually build this, like, this chat UI that we're so used to seeing, right? And Directus is using Vue, which I love Vue. Vue is amazing. Most of these AI, like, pre built chat UIs are React based. So if you know of a good one that's for Vue, for building chatbots really quickly, let me know. So let's go here to SDK UI. Right? They do have support for, like, Vue JS. I see that here, this use chat function. We're gonna need that AI SDK view, so we might as well go ahead and install that as well. AI SDK view. Okay. There's the API endpoints, conversational interface. Is there, like, a simple recipe for this? Stream checks with a chat prompt. Alright. So, again, this might be a great thing to lean on AI for using here's an re a React example from our the Vercel, SDK. Or, actually, let's just piggyback off that last chat. Let's see. Here's a React example from for chat UI from Vercel AI SDK. Oh, actually, I have to show that. Build a view version of this that works with our chat endpoint. There we go. See what AI comes up with. So it's creating another composable. Why are we just use the built in used chat from Vue AI SDK. How do I go back to that? Chatbot AI SDK, chatbots. That was what? Under the API reference? Use chat view. And I just yeah. SDK view. Okay. And now we're getting somewhere. We got some messages. We got v button, v text area. No. Alright. Let's let's try and look at what's going on here. Handle submit, use chat, use API, AI Copilot, message content. So here's our messages. The v text area, v button, these are built in components within Directus. It is using Tailwind, which we we don't use Tailwind to Directus. So, use vanilla CSS instead, And then we can hit save and see if this is actually gonna work. Right? Now, I will tell you, like, I I've been using Cursor a lot recording this season of a hundred apps, hundred hours. I like it a lot, especially for prototyping. I found myself definitely suspect of the code sometimes, so don't just tap, tap, tap, accept. Yeah. Be very deliberate. And before you ship anything to production, make sure you test all this and go back and understand what's going on. This is not going to work, obviously. I just know our variable syntax, all of these have dash theme, dash dash in there. So hopefully, this solves that, but those are the things that you gotta watch and sometimes where, like, the AI Copilot stuff can get in the way. Right? How many orgs, organizations are in Directus? There is no icon button here. What would that be? Send. Google material symbols. Is there a send? There is a send. We don't have a icon prop here as well, so we're gonna do something like this, like v icon name equals send. That should give us the icon that we're looking for. Yeah. There we go. Okay. How many organizations are in Directus? Alright. So is this actually gonna work? Who knows? We'll see. Chat is not coming back. Internal server error. There's the messages. I don't think we're gonna need to pass the ID of this. Right? So if we look for a unique identifier for the chat, let's not use that. We got slash chat. Where is the local host slash chat? Alright. If we look at our endpoint, right, we need to adjust this. Right? We're gonna do StreamText, and we want to do the result for that. So we're gonna pipe that stream. Messages, content dot messages. What is the example for this look like? Is there where's that node? Like, so much of development for me is diving through documentation and just bouncing back and forth. Express. You can set up an Express server, stream, generate text, stream text with a chat prompt. Stream object, stream stream text. You probably just wanna look at it like the actual APIs here. Stream text. What are we passing to stream text? We've got messages that we're gonna pass. So those are just gonna be coming from the body. Right? Those are gonna be messages. We'll just pass those messages. And is that gonna get us what we want? Tools is already there. Do we need, like, a max tokens? No. Alright. Let's just see how far this gets us. How many organizations are in Directus? Okay. There's the so we're seeing the response. I can see the actual response there, but I'm not seeing it show up in our actual module here. Right? Messages. Alright. Use chat. Append. Let's just test what they're doing. Yeah. So that's kind of what we want there. Append content inputs. Result to data stream response, system helpful assistance, messages, I don't know why we have handle input change. Set appends. Let's see what's actually going on. This is where things get tricky. Right? Is there a handle input change inside the use chat model? Use chat. Initial message, initial input on. Handle submit. What does this return? Handle submit. I don't think there is a handle input change. Right? So, again, this is where, like, things get dangerous with AI, where you take 10 giant steps forward, and then you take, like, six giant steps backwards just trying to figure out what's actually happening here. So, chatbots. Where's the core StreamText. Well, actually, let's look at use chat on this side. Right? Initial input, initial messages on tool call, on response. If set to stream protocol, stream protocol should be text, I guess. Let's see how many orgs are in Directus. Oh, there we go. Okay. Now we're getting somewhere. Right? Okay. So now we're seeing the text being streamed in. If I wanted to select this, I can't because of what we've got set up in Directus. But, eighteen minutes in, like, we've got chat with AI models. We've got tool calling. We are somewhat through. Like, maybe we strike through half of that. We do all this through a chat interface. Alright. Now alright. Let's dive into, like, persistence. What do you think of those names? What do you think of those names? Okay. Yeah. We should probably have some type of, like, markdown formatting here as well. Is that what's coming back from the actual API? Is it just markdown? Yes. Okay. So do we have do we have a markdown helper inside Directus? Directus dash Directus issues. Cisco markdown. Directives markdown. Can we access this directive? The markdown. The markdown. Or we're buying that as MD? BMD? BMD. Okay. So can we do this? VMD equals message.content. Obviously, we're gonna lose that. Hey, o. Yeah. There we go. Now we're in some formatting. Looking nice. Looking nice. Okay. Get specific fields from a collection. Here to help you work with direct to collections. What do we have inside the database? Alright. Contacts. Who is the coolest contact in our CRM? Each of these contacts has their own interesting qualities. Without knowing your specific criteria for coolness, it is hard to definitively say who is the coolest. Edon kind of sounds like Elon. This might be a playful reference. Who knows? Who knows? Alright. So this is super interesting. Right? Let's work on persistence. Let's work on persistence to our directus database, what would the best data model be knowing the structure for messages. So if we look at stream stream object, stream text, And, like, our endpoint here is just streaming back the pipe text stream two response. Is there, like, a pipe data stream? Yeah. There you go. That's how we could get the actual messages. Pipe data stream to response. Send message data object. Okay. And then we should be able to like, inside our module, we can use the stream protocol. Let's just set this and see see if this is gonna work as well. Hey, o. Okay. But now instead, what we're getting back is actually the message data as well, which is probably what we're gonna want. Alright. What did you come up with, friend? So we've got messages, conversations, messages. So this is actually trying to give us a direct to schema, which kinda scares me a bit. So we got a user conversations. We got an ID. We got a title. We got a message schema. Messages, ID, conversation, role, content, tool calls, tool results. Okay. So this is the type of response we're getting back. I'm just gonna save that. Right? Alright. That was the payload that we sent. What is the response that we got? Okay. And now we got twelve minutes left just to, like, try to round this out and see if we can get this persistence part of it. Alright. So we're gonna create conversations. This is gonna be a UUID for the ID, created at for the time stamp, created by, updated at, updated by I will just go with what AI said here. I really don't wanna think too much about this. We'll do a mini to one relationship here. We'll call that the user. The related collection we're gonna use is directus underscore users. It'll show a link to the item. Great. And then we're gonna go in and create, what, messages. Messages. These are gonna be what did it say there? That's a UUID. Yeah. UUID. Conversation? Yes. I'll link those together. Content. Oh, not created at. Created at for the time stamp. Created at. K. We're gonna have a role for that. Role is gonna be what? That could be a drop down, so we could set that up as a drop down if we wanted to. User, that's gonna be user, or assistant. Too many a's. Assistant. Too many s's. Too many s's. Alright. And then we have the message content, which I I think is gonna be marked down, content. We could say tool calls, tool calls, Tool results. I don't know if this is actually gonna be it or not. Tool results, ID, conversation. Then we're just gonna link this together. I add a mini to one relationship here. Conversation. The related collection is gonna be conversations. We're gonna show a link. And what I'm gonna do, I almost always go into the advanced field relationships or the advanced, advanced mode when creating fields. I'm gonna add the reverse in here. So I'm gonna add all the messages to this conversation. Great. And okay. So now create conversation, load conversation, save a message, use chat persistence. Okay. Yeah. Alright. Let's just roll with it. This is gonna create a new composable. Use chat persistence. Sometimes it's fun just to shut your brain off and totally, just not think about it. It's fun that this show, honestly. Like, how fast can we turn something? So let's go back to our module. We're gonna import this. The actual import is just gonna be from use chat persistence. And we're not there is no handle input change, right? Handle submit. Load conversations. Handle submit. Use chat. Stream protocol. Save message. V model input, we do have inputs. We need to import on mounted watch from view. Use chat persistence. What's that gonna be? Dot JS. Or is that just like a default? Nope. Export function, use chat persistence. Why is it not picking that up? Oh, because it's in the same directory. Duh. Alright. Seven minutes remaining. Let's see what happens now. AI on top of AI, building AI. How can you help me? Alright. So we'll just watch the network request here. That's finished. I don't see another network request. Oh, maybe you gotta refresh first. How can you help me? Oh, boy. I'm not sure if you can see what's going on here, but we are going to be bricking this instance very quickly if we continue that. So, conversations, right, what do we do here? We've created some type of infinite loop. Thank you, AI. Love it. We're gonna load conversations on that. Watch conversations. Where are we getting the ID? Oh, why are we passing it? Yeah. Why do we want we don't want that. Is that what it is? The save message Is that where the problem came in at? I don't know. It's saving the same ID test. Nope. Still there. Alright. So you messed up, bro. You introduced an infinite loop. Why did you do that? Is it somewhere in our composed load message? Handle submit function calls itself recursively. Yeah. Okay. Yeah. Okay. Okay. Well, what did you change? Handle submit, await, append using Alright. Now is it possible to nuke all of these? Let's just nuke all these messages. Right? I'm gonna go straight to the database. Boom. Boom. Boom. This is fun. I like messing around with this AI stuff. We're just gonna nuke all of these conversations. There's 80,000,000 conversations as well. Maybe now this will actually work. Oh, some type of error handling there. It just disappears. Hey. Okay. So now we should be getting back to where we need to be. Messages. There we go. It's not saving the conversations correctly. Conversation. Current conversation. What is the current conversation? Current conversations dot ref. Well, no. There we go. We're creating a conversation. So current conversation dot value, save message, current conversation dot value. So why isn't it creating a conversation? Let's say create conversation. So we're gonna create a conversation when we load this. And maybe that'll fix the persistence issue. There's the conversation. Test message. Some type of stream issue going on there. But if we now go to conversations. No. Gremlins in the AI. This is why you don't have AI do your coding for you. Super helpful, though. Right? No chance I would have gotten here as far as we have in an hour without this tool. Obviously, lots of bugs and interface issues to squash before that. What's how many deals do we have outstanding? Let's try a simpler query. Deal stage ID. Are there deals? I thought there were deals. And maybe it doesn't know enough about the it just doesn't know enough about it. I know I've introduced some bugs. I kinda wanna end on a high note here, but we're running out of time. Right? We're gonna call it good. Let's end on a good one. What kind of collections do we have here? Organizations deals. You could probably feed it, like, the current user as well. That's gonna be, like, accountability, maybe. I don't know. How many open task activities activities are there? Structure of the activities collection. Man, I would just ruin this. How many orgs are there? I can't even add another working example. Sometimes that's how these things go. How many organizations are there? Yeah. We're now getting, like, crazy mad errors that we broke something. Alright. So there it is. Some type of weird infinite recursion glitch that I have introduced by just leveraging AI and tabbing and accepting. So now there's a lot of debugging involved in this, but this is a great example of the power of Directus, the power of AI, Vercel AI SDK, whoever's working on this, if you watch this, amazing work on this to make, this sort of stuff really easy and accessible and quick to pick up. Boom. Goes the dynamite. That's it for this episode of 100 apps, one hundred hours. I hope you will stay tuned for the next episode. Ciao.","6ad08c15-e97a-4887-9cf2-8b0d5b78d137",[1100],"031a705b-7238-4184-a19a-e3408398dff0",[],{"year":399,"number":1060,"id":1061,"episodes":1103,"show":1104},[1063,1064,1065,1066,1046,1067,1068,1069,1070,1071],{"title":322,"slug":323},{"id":1066,"slug":1106,"vimeo_id":1107,"description":1108,"tile":1109,"length":1094,"resources":12,"people":12,"episode_number":159,"published":1051,"title":1110,"video_transcript_html":1111,"video_transcript_text":1112,"content":12,"seo":1113,"status":19,"episode_people":1114,"recommendations":1116,"season":1117},"help-center","1059435433","Bryant tackles the challenge of building a documentation site that lets content editors manage help articles through a CMS instead of markdown files. Watch as he sets up a backend with Directus—creating categories, articles, and tags relationships—before diving into a frontend template to bring it all together in his race against the clock.","866ba612-4d80-485a-ad0e-7f438ec602a9","Mission: Help Center","\u003Cp>Speaker 0: What's up peeps? Welcome back to another episode of 100 apps, one hundred hours. I'm your host, Brian Gillespie for Directus. Today, we've got a pretty cool episode, I think. Yeah.\u003C\u002Fp>\u003Cp>I've seen this come up time and time again in our community and some of the questions. You know, most documentation sites or help centers, at least in the developer community, they're all static markdown files, which is great. As a developer, I love markdown plain text formatting in a nice easy to read way, easy for me to author. Content editors, they don't necessarily love markdown. So today, we're going to be building a help center.\u003C\u002Fp>\u003Cp>If you're new to 100 apps, one hundred hours, there are two rules. We have sixty minutes to plan and build an application, an idea, a clone, whatever, no more, no less. And number two, use whatever we have at our disposal. So with that, let's fire sixty minutes on the clock and go. So I usually start every episode just trying to figure out what type of functionality we're looking for here.\u003C\u002Fp>\u003Cp>So I want a documentation site, a help center slash documentation site where let's do this on a new line, where content editors can edit within a CMS and not necessarily text files or markdown or whatever. Right? Those are the main requirements here. If we were to put down, like, a stretch goal on this, I would say, like, IT and IN support could be a we'll call it a stretch goal. Sounds good?\u003C\u002Fp>\u003Cp>Sounds great. Alright. So as far as what we want out of our data model here. Right? When I think of a help center, I think of categories.\u003C\u002Fp>\u003Cp>So I think of what section do these belong in, and I think of articles. So category has what? It has a name. It has articles within it. So there's a relationship there.\u003C\u002Fp>\u003Cp>The articles belong to a category. Could there be multiple categories or multiple could an article live in multiple categories? I would say not. I would say that's probably the realm for our friend tags. Right?\u003C\u002Fp>\u003Cp>Or topics or something like that. Right? I can have a mini to mini relationship. So we'll do some nice looking arrows. Great.\u003C\u002Fp>\u003Cp>Great looking arrows. So we have multiple tags on an article, but an article belongs to a single category. Right? So we have a title for the article. We have a slug and then we have content.\u003C\u002Fp>\u003Cp>Now this could be markdown if you prefer that. It could be HTML from a WYSIWYG editor. However we wanna do that, we'll tackle that as we dive in. Alright. So before we even do the front end, let's start on this inside Directus.\u003C\u002Fp>\u003Cp>Now I've got a nice kind of front end template picked out for this, but, let's start back end first with our data. Right? The first thing we're gonna create, let's let's tackle categories. Right? So we'll do categories.\u003C\u002Fp>\u003Cp>We could call these collections. We call a table inside direct us a collection, so that could be confusing. Let's just call it a category. Great. We'll give it a UUID and we'll hit save.\u003C\u002Fp>\u003Cp>Do I want any of these other things? We probably want sorts for these. You know, I could decide not to publish an entire category or not. I guess that's okay. Let's give this a name.\u003C\u002Fp>\u003Cp>Category name, makes sense. Article title, I am we could go with title across the board. I guess that makes more sense. Alright. So let's go ahead and start cleaning up the form a bit.\u003C\u002Fp>\u003Cp>I like title, status. Do we have a slug for the category? Probably. Forgot to put that in there. We can even add nice little icons in Directus.\u003C\u002Fp>\u003Cp>Makes it super simple. We'll just add a little link. I'm gonna open up the interface for this and just Slugify this. Make my Slugify option using my little mouse Pose tool here. We'll trim the start and end in case there are white spaces, and let's make this mono space font.\u003C\u002Fp>\u003Cp>Yeah. This is gonna look amazing. Right? We'll use mono space font for the display. That just controls how it looks in the different directus layouts.\u003C\u002Fp>\u003Cp>We have a title, a slug. We have a category. What else do we want? We wanna have an icon. Right?\u003C\u002Fp>\u003Cp>Cool. Icon for this category. Can't get rid of the designer OCD stuff that I still hang on to. Cool. That looks great for categories.\u003C\u002Fp>\u003Cp>Now let's set up our articles. It doesn't look great. Forgot an icon for the actual category itself and a display template. Great. Okay.\u003C\u002Fp>\u003Cp>So now we have categories. I could go in and create a new category. Let's call this API reference. Right? API.\u003C\u002Fp>\u003Cp>And do we have an API? Yeah. Your standard API icon. We'll just go ahead and set this to published. Great.\u003C\u002Fp>\u003Cp>We have a category. I could query this on the front end. Items slash categories. Boom. I've got a REST API.\u003C\u002Fp>\u003Cp>Magic happens. Sparkles fall from the sky, etcetera. Alright. So we've got categories. Let's add articles now.\u003C\u002Fp>\u003Cp>So again, I'm going to caution against using something like slug here as the primary key field because if you ever need to change that slug, you cannot change the primary key value for a record because of all the relationships that are attached to it. Could get very messy. Right? So, what we'll do here, let's add created on, created by. These system fields here or these optional system fields are helpful.\u003C\u002Fp>\u003Cp>Created by you can also rename them as you see me doing here. Updated at, created at, updated at, updated by that'll be the user. Cool. You can use them as is. You can leave these out.\u003C\u002Fp>\u003Cp>You can add them back later. It's just a shortcut for you. Alright. So let's pick the article icon. Is there an article icon?\u003C\u002Fp>\u003Cp>What do you got for me? Directus. That looks pretty good for an article. We'll command s to save and stay. And let's start fiddling around with this, right?\u003C\u002Fp>\u003Cp>Again, we'll use a title for the article. Cool. Gravy. One of the other shortcuts that you can use in Directus, if you have a field that you want from another collection per se inside your data model, I can just go to duplicate. So I've already got the slug set up how I want it.\u003C\u002Fp>\u003Cp>We'll just dupe that across to the articles collection. Alright. And categories will probably be above that. So here I can see my slug field. We'll just set that to half width.\u003C\u002Fp>\u003Cp>What else did we have here? We had content. Right? So how are we gonna set up content? Do we want WYSIWYG?\u003C\u002Fp>\u003Cp>Do we want Markdown? It could go either way. Let's lean Markdown first just to see what this looks like. Hit save. And basically behind the scenes, this is just creating a text column inside the database.\u003C\u002Fp>\u003Cp>So, you know, if I wanted to go back later and adjust like the actual interface, what the user interacts with, I can change that from markdown to WYSIWYG very easily. Or if you're like me, you might use even plug something like Tiptap in here through our directus marketplace. Great. K. Now we've got tags.\u003C\u002Fp>\u003Cp>Let's go in and create our tags. Great. Generate a UUID. What is the tag? Do we have a sort for the tags?\u003C\u002Fp>\u003Cp>We don't really have any of this tags. We'll use a hashtag. What's the tag? Name of the tag, title of the tag? We'll just use title.\u003C\u002Fp>\u003Cp>Again, naming things is always the hardest thing. I think there's like a receipt option that kinda looks like a tag. Does that bookmark maybe? Kinda looks like a tag. Kinda also looks a lot like a bookmark.\u003C\u002Fp>\u003Cp>This does not look like a tag. Oh, that's a Pentagon. Pin tag on. Yeah. Cool.\u003C\u002Fp>\u003Cp>Let's use that. Sounds great. Alright. So now we have a tag. I don't know that we need a a slug on this.\u003C\u002Fp>\u003Cp>You know, you might have, like, grouping for tags later. But that's kind of the basic of this data model. Right? Now let's let's add an article to this, and we can quickly see what we're working with. Accessing items.\u003C\u002Fp>\u003Cp>Items. Accessing items. So there's our slug. We can publish this guy. We can go in and add some markdown.\u003C\u002Fp>\u003Cp>Hey, yo. This is a markdown editor. It edits markdown. Wild, I know, right? And then I could preview that.\u003C\u002Fp>\u003Cp>We could save this. Again, we can just, like, look at this URL. We can replace items with the admin. So here we can see this data. This is because we're actually logged in.\u003C\u002Fp>\u003Cp>When we send this request, it is sending our token in the cookie. If I were to do this in, like, a incognito window, you'll see that I I get no access. No access to that. We could solve that by going in and updating our access policies and and or our roles. So, by default, Directus loads two roles.\u003C\u002Fp>\u003Cp>There's a public and an administrator role. We've got our access policies here which are, more granular. Right? I can have I can attach 35 different access policies to a single role if I want to. But if we look here, the data that is available without authentication is absolutely zero.\u003C\u002Fp>\u003Cp>Absolutely nothing because we want you to be secure. So we'll just go in, we'll add tags. We're probably gonna want files if we choose to upload them. So we'll add read access to files, add read access to tags, add read access to categories and articles. The one restriction I'm gonna put on articles is that we only wanna show published articles.\u003C\u002Fp>\u003Cp>Pretty straightforward. I guess we could do this to categories as well because we do we have a status? Yes. We only wanna show published categories. Now if I were to test that out, we just create a this is unpublished article.\u003C\u002Fp>\u003Cp>This is a work in progress. That's what we'll call it. This is WIP. Great. I got two articles now.\u003C\u002Fp>\u003Cp>If I open up that incognito window, paste that same URL except we drop the ID, you could see I'm only getting a single article that is published. If I were to change the WIP article to published, boom, boom, fetch. Yeah. There we go. I can now see that WIP article.\u003C\u002Fp>\u003Cp>Great. Permissions works. We have a REST API. We have what we need to proceed. So, the thing we are missing here is our relationships.\u003C\u002Fp>\u003Cp>Right? We don't have a relationship between articles and tags and categories and vice versa. So let's solve for that. Right? First thing, we need to define kinda the way those relationships work.\u003C\u002Fp>\u003Cp>There is a one to many relationship between categories and articles and a many to many relationship between our tags. So I could do this. I could go into our categories. We'll look for that one to many relationship. We'll call this articles.\u003C\u002Fp>\u003Cp>The related collection is also gonna be called articles. So the key is the name of the field inside the categories collection, the related collection here, and then we have a foreign key, which will be the category, probably a a nice naming convention here, singular, plural, fun stuff. So the category is only going to be a pointer back to the categories collection. And for the displayed template, we could show the title. Great.\u003C\u002Fp>\u003Cp>Maybe we wanted to do show if this is actually published or not. I think that's a good way to handle it. And we'll show a link to this item. And if I wanted to drill into this, I can go open the advanced field and we could see here's the related collection. You know, maybe I wanna add that sort field.\u003C\u002Fp>\u003Cp>We'll use that to, control the order of the articles. You know, we're not in documentation or like a help center or something. You don't necessarily wanna do like chronological order based on date. You wanna do, an order that you specifically set. So I do that.\u003C\u002Fp>\u003Cp>That should have also created the corresponding field inside articles, so now we have that set up. And then what I'm gonna do now that we're here in articles, we'll create that many to many relationship. I Was trying to look for another name there, wasn't I? Let's call this tags. The related collection will be tags, and we don't wanna allow duplicate tags.\u003C\u002Fp>\u003Cp>We'll show a link to this. There's a lot of other settings that we control here. You know, how many different items show in a page when you're looking at a lot of them. You shouldn't have probably more than 15 tags per page, but if you do, kudos. Alright.\u003C\u002Fp>\u003Cp>For our relationship tab here, I always open up advanced so I can see what's going on. You could see now that Directus is gonna create this junction collection for you. I could scroll down, it'll tell you exactly what fields are gonna be created, but I'm gonna adjust that. I'm a little OCD about how I name things. So this is gonna be article tags.\u003C\u002Fp>\u003Cp>The key for the article, I'm just gonna call that article and then I'm gonna call this tag instead of tags underscore ID. You can let Directus autofill that. You don't have to add it this way. Totally up to you. Whatever you prefer.\u003C\u002Fp>\u003Cp>And then I'm gonna add this corresponding field. If I do ever want to filter articles by tags, I could go straight to the tag and get a list of articles that way as well if I add this field. And we'll add a sort just just for fun. Great. There's our interface.\u003C\u002Fp>\u003Cp>There's our display. We'll display the related values. Looking good. And And we can see that Directus has created that article tags collection for us. It's hidden by default.\u003C\u002Fp>\u003Cp>We don't necessarily need to see it. And what does this look like now? Right? So hey yo. This is the markdown editor.\u003C\u002Fp>\u003Cp>We have tags. We don't see a category. So I do wanna be able to edit that category. We'll just unhead that guy and boom. There we go.\u003C\u002Fp>\u003Cp>So now we can see a title. Here's the status. And maybe we move that category up here to the top besides status. That makes sense to me. Again, the beautiful thing here is we are updating the form that our team members are actually gonna interact with, which I think is freaking awesome.\u003C\u002Fp>\u003Cp>I don't wanna hide that. I just wanna make it half width. Got a little carried away there. Move some of these other ones out of my way. Alright.\u003C\u002Fp>\u003Cp>So now we've got category. Got a status, category, title, slug, content, and then tags if we wanna apply those. What does this actually look like? Looks pretty good. There's our API.\u003C\u002Fp>\u003Cp>Cool. That's the category it's in. Here's the title. Here's the slug. There's the tags.\u003C\u002Fp>\u003Cp>We can create a new tag. Let's call this, reference or feature. We can call this items. That makes sense. Great.\u003C\u002Fp>\u003Cp>We'll hit save and stay. This is not looking nice. I don't like this. This is the UUID for the tag. So let's just go into here and I can fix this two ways.\u003C\u002Fp>\u003Cp>I can go into my tag and add a default display template. I think that should take care of it. We'll maybe do the same here for our articles. Just anytime we're using this, it should default. Okay.\u003C\u002Fp>\u003Cp>So now I can see the tags. We have items there. Looks much better. Much, much better. I still see, like, category here.\u003C\u002Fp>\u003Cp>We probably need to fix that as well just because it's gonna drive me crazy. So that is our display template here. We just wanna make sure we're displaying related values. If I don't populate that field, it should show, like, the default. Why is it not doing that?\u003C\u002Fp>\u003Cp>Okay. I'll just fix it. Just let me fix it. Alright. We want the title here.\u003C\u002Fp>\u003Cp>Let's add the title there. Make a liar out of me. Okay. So now I can see the category. I can rearrange the fields up here.\u003C\u002Fp>\u003Cp>This looks good. There's the category this belongs to. Maybe that's second, third, etcetera. Yeah. I could filter those by sending value, whatever I wanted.\u003C\u002Fp>\u003Cp>Cool. If I open up the category, I could see all the articles within it and this may be the best way to manage my different articles. Right? So eighteen minutes in, we've got the skeleton of our help center back end. Right?\u003C\u002Fp>\u003Cp>This looks nice. We've got the ability for someone to edit this content, in a nice editor and riggy jig, away we go. Right? So the other component to this, we need a front end. Right?\u003C\u002Fp>\u003Cp>And I found this beautiful docs template, from mister Tony Tony Zhang. Tony, have you ever watched this? This is a pretty cool ShadCn view docs template that you've created. It looks great. So you've got, you know, kind of the standard docs vibe going on.\u003C\u002Fp>\u003Cp>You get a lot of components already built into this thing. So let's just go to GitHub. And take a big risk here on hundred apps, hundred hours. I have not messed with this. It looks like you've got, like, a starter here, but we're gonna, like, change some of the nuts and bolts of this.\u003C\u002Fp>\u003Cp>So, we're gonna abandon my usual Nuxt starter application that I have. And let's just open this up. We're gonna go to the terminal, git clone, shad c n docs nuxt. I think I'm in the right spot. Maybe not.\u003C\u002Fp>\u003Cp>Maybe I wanna do git clone, CD and then git clone. Let's back up one directory. Looks like it was already in the help center directory. Did that what did we get here? Trash.\u003C\u002Fp>\u003Cp>Oh, that's in the trash. Let's open a new terminal instance and solve for that problem. Git clone. Okay. So now we have shad c n docs nuxt.\u003C\u002Fp>\u003Cp>We'll open this up in the terminal. P n p m I. Let's get this thing fired up. And let's just kind of inspect what's going on here. I see an app dot vue file.\u003C\u002Fp>\u003Cp>That's kind of standard for a Nuxt app. We have an app folder that controls router options. Looks like that is, like, controlling for, like, a sticky header or something like that. This is cool. And then when you see, like, we have oh, that's my own app.\u003C\u002Fp>\u003Cp>Come on, guy. That's your standard Nuxt app that you have. Right? Why are you doing that? So then we have something like pages.\u003C\u002Fp>\u003Cp>Cool. So this is using Nuxt Content. Nuxt Content is a really cool, headless kind of content. So basically, hey. We can use markdown, and allows us to use Git as a CMS, basically.\u003C\u002Fp>\u003Cp>File based CMS. That's what I was struggling with. They've got this MDC format though, which is kind of interesting where I can, like, use a component like this, like hero, and it will generate this. It'll actually generate stuff like cards for you using kind of a markdown familiar syntax, which, again, pretty cool for developers. You know, nice for content editors that they can create like a complex like looking card or something in here.\u003C\u002Fp>\u003Cp>Could get confusing, maybe not. Let's run with it and see. But we've got this thing installed. Let's just fire up the dev server and see what we can get done with this thing. Alright.\u003C\u002Fp>\u003Cp>So we'll go to local host. And we're developing. We're developing. Looking good. Zoom out and direct us a little bit.\u003C\u002Fp>\u003Cp>Oh, that's not what we want. Come on, baby. Come on. That first time's a little bit slow. Don't be shy.\u003C\u002Fp>\u003Cp>Okay. So this looks pretty good. We've got an index here. This is a content renderer. I don't know where this is picking up the content from.\u003C\u002Fp>\u003Cp>I'm assuming it's coming from this index.md thing here, and you can kinda see what's going on. This is the home page. We've got effortless and beautiful. We'll just change this. Bryant's hundred apps, hundred hours, starter, help center.\u003C\u002Fp>\u003Cp>Using cursor here. It's got nice auto complete. I I that's probably the main reason that I love it. It's a little more intelligent than, like, the Copilot for stuff that I'm already gonna be typing. This is a help center, but with a hundred apps, hundred hours starter.\u003C\u002Fp>\u003Cp>Great. We don't need this iframe. Cool. We're gonna get started. There it is.\u003C\u002Fp>\u003Cp>Great. We could leave the buttons. Cool. This is gonna be what? To where do we wanna navigate to?\u003C\u002Fp>\u003Cp>Let's go to slash, what was our doc that we had? Right? We've got accessing items that is that's what we'll do. Right? Accessing items.\u003C\u002Fp>\u003Cp>If we remove this link to GitHub, we save it. Something broken. I broke something. Why is this not working right? Actions.\u003C\u002Fp>\u003Cp>Okay. There we go. That's gonna take us to accessing items. That's a four zero four. Bummer.\u003C\u002Fp>\u003Cp>No big deal. Alright. So this got this content directory that we're pulling from. Okay. Great.\u003C\u002Fp>\u003Cp>We can see it's using that that Nuxt content here. Nice feature. Great. I'm just gonna like slap, I'm gonna copy the index file, honestly, from my other starter that I have here and I'm just gonna stick this in index because I'm lazy. Alright.\u003C\u002Fp>\u003Cp>So now you can see this is my standard starter index file that we'll see. I'll just remove that. Hundred apps, hundred hours. NUC starter. Boom.\u003C\u002Fp>\u003Cp>There it is. Great. Cool. But I should probably have a button actually to go to one of the individual pages, though. Right?\u003C\u002Fp>\u003Cp>Let's keep that. Accessing items. Get started. And I'm doing this because I don't want the actual Nuxt content pieces of this. It's okay.\u003C\u002Fp>\u003Cp>Nuxt u button. What? Nuxt link? Or should it be UI button? UI button.\u003C\u002Fp>\u003Cp>Where is our where is our UI button? There's our UI button. What other props do we have as a button? As a as Nuxt link? Can we do that with this thing?\u003C\u002Fp>\u003Cp>Will that actually work? Is it gonna like that? Atrial? Why is it not it's not liking that at all. I don't know.\u003C\u002Fp>\u003Cp>I don't know why. What if we just do a regular a? Okay. There's our tag. Obviously, that's gonna hit us with like a full page reload, but no worries.\u003C\u002Fp>\u003Cp>Alright. So what we're gonna do, I'm gonna create a Directus plug in for this, Directus.ts. And to save myself time, invoking rule number two, use whatever you have at your disposal. I've already got a sample Directus plug in in my starter. So we'll just pull this across to plug ins.\u003C\u002Fp>\u003Cp>Alright. Now if I can go full screen on this. I should put it in the wrong window, Alright. So first off, we need to install the Directus SDK. I'm not gonna use cookies here.\u003C\u002Fp>\u003Cp>So do we really need authentication? No. We don't need that from the SDK. Let's not even use real time or read me. We're just gonna use the REST API.\u003C\u002Fp>\u003Cp>Gravy. I'm gonna get rid of this bit about authentication and this bit about that. And all we're gonna do here, pretty easy, pretty simple, we are fetching a direct as public URL from the Nuxt runtime config. We are creating a directus client and then we're providing that back to the Nuxt application so we can access that in multiple places. So now we're gonna install directus SDK.\u003C\u002Fp>\u003Cp>Solid. And I'm gonna find the Nuxt config for this file. And then we're gonna add, probably need an env variable. Is there a dot env file already? There's not.\u003C\u002Fp>\u003Cp>We'll call this Directus URL. That's gonna be HTTP local host 8055. That's just what I'm running on here. Great. And then I'm gonna find what's looking for our runtime config.\u003C\u002Fp>\u003Cp>Public, direct us URL. You can see this is already picking this stuff up for me. Another nice benefit of cursor. And then so we've stripped out. We didn't really strip it out, but let's strip this out.\u003C\u002Fp>\u003Cp>We don't need that. For our slug, what do we wanna do? What do we wanna do? We've got, why is script set up not at the top? This is again, just my OCD kicking in.\u003C\u002Fp>\u003Cp>There's some lint rules set up here. E s lint config. Delete. View block order. Oh, let's change that guy.\u003C\u002Fp>\u003Cp>Alright. Is that gonna fix this now? Script setup. Script. Okay.\u003C\u002Fp>\u003Cp>Alright. Changing some rules there. We're we're cooking with gas now. Alright. So we could see what's going on here.\u003C\u002Fp>\u003Cp>Like, this is like pulling this content in from markdown files itself. That's I don't wanna do that. I'm not gonna do it. But now we got this nice directus plugin that we can use and that'll be directus URL. It should be a string.\u003C\u002Fp>\u003Cp>Let's just fire this up. There's one other thing that I'm going to add to this and it is the I I know because I've worked with this thing before. It's the Nuxt MDC package, which is basically doing some of the heavy lifting under the hood as far as, like, the taking the MDC format and, you know, generating that AST that we can then use to render with. So, we'll p m p m I at Nuxt MDC. And you can see here there is like this parsed markdown or parse markdown function that we could call.\u003C\u002Fp>\u003Cp>Then we can render that markdown like the body and the data format. Alright. So we've got those two things. Let's fire up this dev server one more time and we'll see what we could do as far as a slug. Right?\u003C\u002Fp>\u003Cp>So now we're gonna do something like this where I'm gonna import read items from the direct Us SDK and I'm also gonna pull in that Directus client from Nuxt app. So the SDK is very modular, so I don't have to import all the methods that I'm not actually using. Right? And Nuxt will do some fancy, tree shaking magic for us based on the the individual routes. So should be all great.\u003C\u002Fp>\u003Cp>What we're gonna do here, we'll do something like this. We'll do constant data and then we're gonna do await, use async data. Okay. So we've got this async data composable from Nuxt. We're gonna give this, we want let's also pull in the route for this as well.\u003C\u002Fp>\u003Cp>So route equals use route. We're gonna pass this a key to use. Alright. So this will be page this will be article let's use a back tick here. Article dash route dot params.slug.join.\u003C\u002Fp>\u003Cp>Maybe. If that's an actual hooray. Okay. Here we go. This is not right at all.\u003C\u002Fp>\u003Cp>This is one of those times that you may not want to totally trust. We're gonna return read item no. We're not gonna return that. We're gonna return directives dot request. Read items.\u003C\u002Fp>\u003Cp>We're gonna get the articles. And then we can define a query. Right? So our query is gonna look like this. We got the filter, and then the slug is what we wanna filter on.\u003C\u002Fp>\u003Cp>And we want that to be equal to route dot params dot slug dot join. I'm I'm not even sure we actually need that. I can't remember the format that Nuxt is gonna give this to us in. We could call this actual data page. And then somewhere in here, this is where this is read items, we want to do a transform on that async data call.\u003C\u002Fp>\u003Cp>Transform. Data, data zero. So we're gonna get the first item in that array. This looks good. Directus, directus dot request.\u003C\u002Fp>\u003Cp>Data page. Script setup tag. What am I missing? Did I goo something? Directus request would read items.\u003C\u002Fp>\u003Cp>Oh, I didn't miss something. There we go. I think that gets us where we wanna be. Alright. So now let's just open this up, test this out.\u003C\u002Fp>\u003Cp>Okay. Unexpected token, define page components. Let's just ignore all of this baloney right now. Got page, invalid URL. Well, that's not great.\u003C\u002Fp>\u003Cp>E n v, direct as URL. K. Nux config, direct as URL. I don't need all of that. Direct as URL.\u003C\u002Fp>\u003Cp>Oh, that's in the wrong config file. Let's just copy that. Nuxt config, shad c n docs. Cool. Why is that not showing as directories URL as string?\u003C\u002Fp>\u003Cp>I Wonder why that's not showing an error here. TypeScript error. Nux config. Wrong Nux config. Unexpected use of the global variable.\u003C\u002Fp>\u003Cp>Why is that unexpected use? Fix with AI. What does it come back with? Import dot Neta dot e m v. That's not gonna be right.\u003C\u002Fp>\u003Cp>PNP m dev. Maybe that's where we didn't have that the first time we started the dev server. Not a %. Articles, filters, slug, param. And we can even do let's just console log the route dot params dot slug.\u003C\u002Fp>\u003Cp>Slug. Okay. So now we could start to see what the problem is here. Slug needs to be does need to be joined. Slug.join.\u003C\u002Fp>\u003Cp>Dodger, hon. Slug accessing items. Why can't we fetch that page? What's going on? There's our slug.\u003C\u002Fp>\u003Cp>We could see the slug. Great. Why can't we fetch this actual page, right? If we navigate, we go back. We should see, like, a fetch request here from Directus.\u003C\u002Fp>\u003Cp>Twenty minutes. How are we doing? How are we doing? We're not doing great, Brian. Not great.\u003C\u002Fp>\u003Cp>Alright. We're we should see a call to direct us somewhere. Local host. 8055. Not seeing it.\u003C\u002Fp>\u003Cp>Alright. Accessing items. Oh, it's because we're not using a link on that index page. Server side rendering. Fun stuff.\u003C\u002Fp>\u003Cp>So is there a link in this? Links button link button link button link to okay. So we should use button link, content button link. What component does that get us? Does that get us where we wanna go?\u003C\u002Fp>\u003Cp>Kind of? Kind of not? So we see a lot of stuff coming from, like, the Nuxt content API. We don't really want any of that business. Don't want that smoke.\u003C\u002Fp>\u003Cp>How can we strip all this out? Right? Can I just is this gonna can all this? This is gonna break a bunch of stuff, isn't it? Umami client, I don't want that either.\u003C\u002Fp>\u003Cp>Slug. Okay. If I just trash all of content, what happens if I disable Nuxt content entirely? Nuxt image, you use Nuxt. Chat c n Nuxt.\u003C\u002Fp>\u003Cp>This could go way wrong. It's always fun, though. Right? Let's just remove content. You where are we using use content helpers at?\u003C\u002Fp>\u003Cp>Right? I guess I should be looking at these individual things going on here. Use content helpers. Where's that at? Aside.\u003C\u002Fp>\u003Cp>So we gotta decide. We don't even care about that. What does our where are our layouts? Do we have a layout? Components, where where are you?\u003C\u002Fp>\u003Cp>App dot view. Layout header, layout aside. Let's just remove the aside. What what's in the header? Layout header, Use config.\u003C\u002Fp>\u003Cp>Okay. Main setup. Use content headers. Search dialogue. Come on.\u003C\u002Fp>\u003Cp>Why did you do that, Brian? That doesn't make sense. Use config. It's gonna break. Header, main, new key from value path.\u003C\u002Fp>\u003Cp>Page dot value dot main. Now if we just strip that out, what what is left? So many fun things here. This is a good reason not to use your own template, or this is a good reason to use your own template. Where else are you use content helpers?\u003C\u002Fp>\u003Cp>Don't want you. Where is search dialogue being used at? Search button layout, search dialogue. Search button. We'll just remove that.\u003C\u002Fp>\u003Cp>Can I actually get something loaded? Stressful. Stressful. Stressful. What are we working with?\u003C\u002Fp>\u003Cp>Nineteen, twenty minutes. So we're back to this slug catch all route. Can we even console log the page? Let's see what we're getting back from the actual page here. Looks like we're struggling to not render any of this, so I'll just fall back on one of my popular patterns that you'll see in a bunch of other episodes of 100 apps, one hundred hours.\u003C\u002Fp>\u003Cp>We'll just do this. Div. Okay. Cool. So now we can actually see that we are getting some content.\u003C\u002Fp>\u003Cp>It's just that we're not rendering the content properly. Bada bing bada boom. That's a problem. Right? Alright.\u003C\u002Fp>\u003Cp>So now let's hop into this Nux MDC business where, hey, we're getting some markdown here from something. Can we just use this n d c component to render this markdown? Alright. So I'll go here. This will be page dot it's not body.\u003C\u002Fp>\u003Cp>It's page content. What will that actually render, if anything? View, hydration, mix, match, can't find smart icon, can't resolve, KBD. Do we see a MDC component? No.\u003C\u002Fp>\u003Cp>I don't. And why? Because we did not add this to that. So we want at nuxt js slash mdc. I think that's correct.\u003C\u002Fp>\u003Cp>Nuxt mdc module. We're gonna add that to our modules. P m p m dev. We'll restart this guy. Oh my gosh.\u003C\u002Fp>\u003Cp>Did I put this? Okay. Don't do what I did. Do not put this in the same directory as your normal stuff. Ugh, I feel like an idiot.\u003C\u002Fp>\u003Cp>Okay. There we go. Fire this up. PnPM dev, we'll run it again. We'll wait for Nuxt to run this out.\u003C\u002Fp>\u003Cp>And is this actually going to work the way we want it to once it spins all of this up? We are cruising on seventeen minutes. Can we actually show these files? Bada bing, badda boom. Cannot find compose.\u003C\u002Fp>\u003Cp>Where is this coming from? Cannot find module composer. Shadcian YAML. Shadcian docs YAML. Package YAML.\u003C\u002Fp>\u003Cp>Types index, run model v runner. What are we doing? Theme, CSS, directory. Nuxt instance is unavailable. P m p m I.\u003C\u002Fp>\u003Cp>When all else fails, just blow away all of your Nuxt stuff. What have happens if we do the node modules? PMPMI. Let's try this one more time. Or it could just be that I've totally broken this thing by stripping away the Nuxt content piece.\u003C\u002Fp>\u003Cp>Probably would have been better off just starting with our Nuxt boilerplate that I already had. Not saying this is not a good piece of kit, this Shadc and Nuxt template. Just saying if you're gonna do this, don't try to learn that and do this at the same time. Trying to think of what this would even be. That's the Nux icon TS config base URL.\u003C\u002Fp>\u003Cp>Hopefully, this thing will spin up now. Hundred apps, hundred hours, starter. There's that. It's not finding a smart icon. I don't know what the smart icon is.\u003C\u002Fp>\u003Cp>Not really super concerned about it. Alright. So here's the page. We could see this is the value that we're getting from the page. Failed to load the resource.\u003C\u002Fp>\u003Cp>So it's not actually rendering anything. Right? So that part of it sucks. Don't like that. Let's actually try this import parse markdown.\u003C\u002Fp>\u003Cp>So if we get the content, we'll do page dot value dot content. Should we even use, like, a computed prop for that? You shouldn't put side effects in in that. Right? Page value, content, slugs.\u003C\u002Fp>\u003Cp>Let's try console dot log content. What if we do console dot log page dot value? Just see what we're getting here. We want to await that. Wait that.\u003C\u002Fp>\u003Cp>What is this? Man. TS config. Build transpile. I'm not gonna I'm not even gonna use Shiki, but it's probably gonna throw a fit because we've got some components inside this thing that are using it.\u003C\u002Fp>\u003Cp>The fun of using someone else's template. Why? Where is this even coming from? If we search all inside here, YAML, Nux config, app config. So there's a lot of this in the app config.\u003C\u002Fp>\u003Cp>It require Nuxt YAML. Where is YAML? Is that it? Composer dot JS. Module helpers, vite node, types node, use config, editor config.\u003C\u002Fp>\u003Cp>Yeah. This is this is wack, man. Alright. Pivot on the fly. Bad Chad c n Docs Nuxt.\u003C\u002Fp>\u003Cp>Just delete you, sir. Now, again, this looks like a a great template. I just don't have enough time. I've got eleven minutes here. So we are getting pretty desperate at this point.\u003C\u002Fp>\u003Cp>We are gonna do I guess I shoulda took what I had before we just ripped it all out. Right? You live, you learn. V base script. I want script setup lang t s.\u003C\u002Fp>\u003Cp>Alright. Now alright. So we go to this slug. That's gonna be inside the pages directory. We don't really need auth, but let's go back to what we had.\u003C\u002Fp>\u003Cp>Pnpmi at nuxt js slash mdc. That should get us what we need here. I've already mistakenly put that into the Nuxt config. And here we're gonna use routes. Use routes.\u003C\u002Fp>\u003Cp>Import. Read items. Read items from direct us Nuxt. Then we'll use constant data of the page. We will await use async data.\u003C\u002Fp>\u003Cp>Give this a page, page routes dot params.slug dot join slash. Great. Okay. Then we've got the actual function we're gonna return. Return, read items, pages, filters, slug, not even close.\u003C\u002Fp>\u003Cp>Directus dot request, read items, pages. Still not even close. Boys, come on. Cursor is not not savvy on this. Filter.\u003C\u002Fp>\u003Cp>It's slug. You're just getting in the way at this point, aren't you, friend? And then we will pull in direct us from Nuxt app. We hit page. This gives us what we need.\u003C\u002Fp>\u003Cp>P m p m dev. Parse markdown. Pull that in. Alright. So now we want to parse the markdown content.\u003C\u002Fp>\u003Cp>Console log page. Console log content. We'll just do pre content. What is that actually gonna render, if anything? Oh my gosh.\u003C\u002Fp>\u003Cp>Failed to resolve import blah blah blah. Does this actually work? Gosh. Fix your template, homie. Fix your template.\u003C\u002Fp>\u003Cp>Alright. I'm sure you guys are having fun following along with this disaster show here. PMPMI. Delete. PNPM dev.\u003C\u002Fp>\u003Cp>This is probably like a type error, I'm assuming. Fingers crossed. Or we might just have to hang it up on this one. I hate to be defeated. I really just wanna show some of this content, and I wanted to try the, like, the actual components where you can do MDC in here.\u003C\u002Fp>\u003Cp>Oh my god. Failed to load. BMPM lock. Delete. Here's what we'll do.\u003C\u002Fp>\u003Cp>If this doesn't work, I'm done. Nuxt. Node modules. Delete. Nuxt.\u003C\u002Fp>\u003Cp>Do we have the MDC in here? Nuxt MDC. Where are you? There it is. Nuxt MDC.\u003C\u002Fp>\u003Cp>Alright. Let me fire this up one last time. We got this. You got this next. Come on, baby.\u003C\u002Fp>\u003Cp>We've already got our plug in here that should be working for us. We can strip all of this out, just comment it out, basically. We don't need that. Don't need any of that. Okay.\u003C\u002Fp>\u003Cp>Everything's installed. PMPM dev. I think we need, like, a blooper reel, but maybe that would be every episode that resulted in just nonstop bloopers. Okay. So far so good.\u003C\u002Fp>\u003Cp>I don't see any major errors coming through. Cannot resolve Tailwind CSS. Are you freaking kidding me? PMPMI, Tailwind CSS at next. Sometimes it just doesn't work out in your favor.\u003C\u002Fp>\u003Cp>I think that is like 90% of modern JavaScript development is wrestling with dependencies and bundlers and config. Honestly, I spend way more time doing that than I do actually developing stuff. Non pojos. Does this have to have Nuxt Content installed? PMI Nuxt content.\u003C\u002Fp>\u003Cp>What is the name of this package? Content dot Nuxt. At Nuxt content, I'm assuming, releases. You can start with a fresh I wanna start add to a project, add Nuxt content. I swear if this is it, I'm gonna be so mad that you just have to install this thing.\u003C\u002Fp>\u003Cp>I thought I was being clever showing you guys this Nux MDC stuff. I've messed with this before. I've got it working previously. Obviously, a little little rusty. Alright.\u003C\u002Fp>\u003Cp>So let's say we do wanna do, like, this alert thing. What is this thing doing, man? What? Where is Nuxt YAML? Where the hell is this coming from?\u003C\u002Fp>\u003Cp>Can I find Compose? Compose.js. Where where is this even at, man? Composer requires to CJS loader YAML, feet runner. I'm not loading any YAML.\u003C\u002Fp>\u003Cp>YAML. Do I have something in the content directory? No. Freaking no, man. If I remove this parse markdown, does that fix it?\u003C\u002Fp>\u003Cp>Page dot contents. Okay. Yeah. So it renders something here. Console log.\u003C\u002Fp>\u003Cp>Page is undefined. That could be why. Not even getting a page. That's why you don't trust, don't trust AI, friends. Page dot it's not even page.\u003C\u002Fp>\u003Cp>It shouldn't be page. But page return request direct us articles filter slug equals route dot params .slug.join. Okay. So now we can see the page. We need to go back and add our transform option.\u003C\u002Fp>\u003Cp>Transform. Get our data. Hey, yo. There's our content. What do we got?\u003C\u002Fp>\u003Cp>We got a minute and forty seven seconds left. Amazing. Just freaking amazing, honestly. H one page dot title. Class text three x l, font bold.\u003C\u002Fp>\u003Cp>Let's add a container to this. Right? That'll give us our page. Amazing. Is that really what it was?\u003C\u002Fp>\u003Cp>Now is this actually gonna work? Content. What if we show, like, MDC render value equals content. No. It's it's still not gonna shove that down, is it?\u003C\u002Fp>\u003Cp>Well, well, well, well, mister Bryant, it does not like you. Anyway, we got remotely close. Nah. We really didn't. We've got the help center, like, part of it set up.\u003C\u002Fp>\u003Cp>Didn't even get into any of the, like, fun MDC component stuff. Roll credits, guys. Yeah. Recapping this, it was a bit ambitious to dive into a template that I had never used before and was totally set up for local markdown files. And I think I could wrestle that in an hour and actually show you how to render Vue components from content, delivered via the CMS.\u003C\u002Fp>\u003Cp>But, hey, sometimes you win, sometimes you lose. Just gotta keep rolling the dice. Alright. That's it for this episode of hundred apps, hundred hours. We'll catch you on the next one.\u003C\u002Fp>\u003Cp>Thanks for joining.\u003C\u002Fp>","What's up peeps? Welcome back to another episode of 100 apps, one hundred hours. I'm your host, Brian Gillespie for Directus. Today, we've got a pretty cool episode, I think. Yeah. I've seen this come up time and time again in our community and some of the questions. You know, most documentation sites or help centers, at least in the developer community, they're all static markdown files, which is great. As a developer, I love markdown plain text formatting in a nice easy to read way, easy for me to author. Content editors, they don't necessarily love markdown. So today, we're going to be building a help center. If you're new to 100 apps, one hundred hours, there are two rules. We have sixty minutes to plan and build an application, an idea, a clone, whatever, no more, no less. And number two, use whatever we have at our disposal. So with that, let's fire sixty minutes on the clock and go. So I usually start every episode just trying to figure out what type of functionality we're looking for here. So I want a documentation site, a help center slash documentation site where let's do this on a new line, where content editors can edit within a CMS and not necessarily text files or markdown or whatever. Right? Those are the main requirements here. If we were to put down, like, a stretch goal on this, I would say, like, IT and IN support could be a we'll call it a stretch goal. Sounds good? Sounds great. Alright. So as far as what we want out of our data model here. Right? When I think of a help center, I think of categories. So I think of what section do these belong in, and I think of articles. So category has what? It has a name. It has articles within it. So there's a relationship there. The articles belong to a category. Could there be multiple categories or multiple could an article live in multiple categories? I would say not. I would say that's probably the realm for our friend tags. Right? Or topics or something like that. Right? I can have a mini to mini relationship. So we'll do some nice looking arrows. Great. Great looking arrows. So we have multiple tags on an article, but an article belongs to a single category. Right? So we have a title for the article. We have a slug and then we have content. Now this could be markdown if you prefer that. It could be HTML from a WYSIWYG editor. However we wanna do that, we'll tackle that as we dive in. Alright. So before we even do the front end, let's start on this inside Directus. Now I've got a nice kind of front end template picked out for this, but, let's start back end first with our data. Right? The first thing we're gonna create, let's let's tackle categories. Right? So we'll do categories. We could call these collections. We call a table inside direct us a collection, so that could be confusing. Let's just call it a category. Great. We'll give it a UUID and we'll hit save. Do I want any of these other things? We probably want sorts for these. You know, I could decide not to publish an entire category or not. I guess that's okay. Let's give this a name. Category name, makes sense. Article title, I am we could go with title across the board. I guess that makes more sense. Alright. So let's go ahead and start cleaning up the form a bit. I like title, status. Do we have a slug for the category? Probably. Forgot to put that in there. We can even add nice little icons in Directus. Makes it super simple. We'll just add a little link. I'm gonna open up the interface for this and just Slugify this. Make my Slugify option using my little mouse Pose tool here. We'll trim the start and end in case there are white spaces, and let's make this mono space font. Yeah. This is gonna look amazing. Right? We'll use mono space font for the display. That just controls how it looks in the different directus layouts. We have a title, a slug. We have a category. What else do we want? We wanna have an icon. Right? Cool. Icon for this category. Can't get rid of the designer OCD stuff that I still hang on to. Cool. That looks great for categories. Now let's set up our articles. It doesn't look great. Forgot an icon for the actual category itself and a display template. Great. Okay. So now we have categories. I could go in and create a new category. Let's call this API reference. Right? API. And do we have an API? Yeah. Your standard API icon. We'll just go ahead and set this to published. Great. We have a category. I could query this on the front end. Items slash categories. Boom. I've got a REST API. Magic happens. Sparkles fall from the sky, etcetera. Alright. So we've got categories. Let's add articles now. So again, I'm going to caution against using something like slug here as the primary key field because if you ever need to change that slug, you cannot change the primary key value for a record because of all the relationships that are attached to it. Could get very messy. Right? So, what we'll do here, let's add created on, created by. These system fields here or these optional system fields are helpful. Created by you can also rename them as you see me doing here. Updated at, created at, updated at, updated by that'll be the user. Cool. You can use them as is. You can leave these out. You can add them back later. It's just a shortcut for you. Alright. So let's pick the article icon. Is there an article icon? What do you got for me? Directus. That looks pretty good for an article. We'll command s to save and stay. And let's start fiddling around with this, right? Again, we'll use a title for the article. Cool. Gravy. One of the other shortcuts that you can use in Directus, if you have a field that you want from another collection per se inside your data model, I can just go to duplicate. So I've already got the slug set up how I want it. We'll just dupe that across to the articles collection. Alright. And categories will probably be above that. So here I can see my slug field. We'll just set that to half width. What else did we have here? We had content. Right? So how are we gonna set up content? Do we want WYSIWYG? Do we want Markdown? It could go either way. Let's lean Markdown first just to see what this looks like. Hit save. And basically behind the scenes, this is just creating a text column inside the database. So, you know, if I wanted to go back later and adjust like the actual interface, what the user interacts with, I can change that from markdown to WYSIWYG very easily. Or if you're like me, you might use even plug something like Tiptap in here through our directus marketplace. Great. K. Now we've got tags. Let's go in and create our tags. Great. Generate a UUID. What is the tag? Do we have a sort for the tags? We don't really have any of this tags. We'll use a hashtag. What's the tag? Name of the tag, title of the tag? We'll just use title. Again, naming things is always the hardest thing. I think there's like a receipt option that kinda looks like a tag. Does that bookmark maybe? Kinda looks like a tag. Kinda also looks a lot like a bookmark. This does not look like a tag. Oh, that's a Pentagon. Pin tag on. Yeah. Cool. Let's use that. Sounds great. Alright. So now we have a tag. I don't know that we need a a slug on this. You know, you might have, like, grouping for tags later. But that's kind of the basic of this data model. Right? Now let's let's add an article to this, and we can quickly see what we're working with. Accessing items. Items. Accessing items. So there's our slug. We can publish this guy. We can go in and add some markdown. Hey, yo. This is a markdown editor. It edits markdown. Wild, I know, right? And then I could preview that. We could save this. Again, we can just, like, look at this URL. We can replace items with the admin. So here we can see this data. This is because we're actually logged in. When we send this request, it is sending our token in the cookie. If I were to do this in, like, a incognito window, you'll see that I I get no access. No access to that. We could solve that by going in and updating our access policies and and or our roles. So, by default, Directus loads two roles. There's a public and an administrator role. We've got our access policies here which are, more granular. Right? I can have I can attach 35 different access policies to a single role if I want to. But if we look here, the data that is available without authentication is absolutely zero. Absolutely nothing because we want you to be secure. So we'll just go in, we'll add tags. We're probably gonna want files if we choose to upload them. So we'll add read access to files, add read access to tags, add read access to categories and articles. The one restriction I'm gonna put on articles is that we only wanna show published articles. Pretty straightforward. I guess we could do this to categories as well because we do we have a status? Yes. We only wanna show published categories. Now if I were to test that out, we just create a this is unpublished article. This is a work in progress. That's what we'll call it. This is WIP. Great. I got two articles now. If I open up that incognito window, paste that same URL except we drop the ID, you could see I'm only getting a single article that is published. If I were to change the WIP article to published, boom, boom, fetch. Yeah. There we go. I can now see that WIP article. Great. Permissions works. We have a REST API. We have what we need to proceed. So, the thing we are missing here is our relationships. Right? We don't have a relationship between articles and tags and categories and vice versa. So let's solve for that. Right? First thing, we need to define kinda the way those relationships work. There is a one to many relationship between categories and articles and a many to many relationship between our tags. So I could do this. I could go into our categories. We'll look for that one to many relationship. We'll call this articles. The related collection is also gonna be called articles. So the key is the name of the field inside the categories collection, the related collection here, and then we have a foreign key, which will be the category, probably a a nice naming convention here, singular, plural, fun stuff. So the category is only going to be a pointer back to the categories collection. And for the displayed template, we could show the title. Great. Maybe we wanted to do show if this is actually published or not. I think that's a good way to handle it. And we'll show a link to this item. And if I wanted to drill into this, I can go open the advanced field and we could see here's the related collection. You know, maybe I wanna add that sort field. We'll use that to, control the order of the articles. You know, we're not in documentation or like a help center or something. You don't necessarily wanna do like chronological order based on date. You wanna do, an order that you specifically set. So I do that. That should have also created the corresponding field inside articles, so now we have that set up. And then what I'm gonna do now that we're here in articles, we'll create that many to many relationship. I Was trying to look for another name there, wasn't I? Let's call this tags. The related collection will be tags, and we don't wanna allow duplicate tags. We'll show a link to this. There's a lot of other settings that we control here. You know, how many different items show in a page when you're looking at a lot of them. You shouldn't have probably more than 15 tags per page, but if you do, kudos. Alright. For our relationship tab here, I always open up advanced so I can see what's going on. You could see now that Directus is gonna create this junction collection for you. I could scroll down, it'll tell you exactly what fields are gonna be created, but I'm gonna adjust that. I'm a little OCD about how I name things. So this is gonna be article tags. The key for the article, I'm just gonna call that article and then I'm gonna call this tag instead of tags underscore ID. You can let Directus autofill that. You don't have to add it this way. Totally up to you. Whatever you prefer. And then I'm gonna add this corresponding field. If I do ever want to filter articles by tags, I could go straight to the tag and get a list of articles that way as well if I add this field. And we'll add a sort just just for fun. Great. There's our interface. There's our display. We'll display the related values. Looking good. And And we can see that Directus has created that article tags collection for us. It's hidden by default. We don't necessarily need to see it. And what does this look like now? Right? So hey yo. This is the markdown editor. We have tags. We don't see a category. So I do wanna be able to edit that category. We'll just unhead that guy and boom. There we go. So now we can see a title. Here's the status. And maybe we move that category up here to the top besides status. That makes sense to me. Again, the beautiful thing here is we are updating the form that our team members are actually gonna interact with, which I think is freaking awesome. I don't wanna hide that. I just wanna make it half width. Got a little carried away there. Move some of these other ones out of my way. Alright. So now we've got category. Got a status, category, title, slug, content, and then tags if we wanna apply those. What does this actually look like? Looks pretty good. There's our API. Cool. That's the category it's in. Here's the title. Here's the slug. There's the tags. We can create a new tag. Let's call this, reference or feature. We can call this items. That makes sense. Great. We'll hit save and stay. This is not looking nice. I don't like this. This is the UUID for the tag. So let's just go into here and I can fix this two ways. I can go into my tag and add a default display template. I think that should take care of it. We'll maybe do the same here for our articles. Just anytime we're using this, it should default. Okay. So now I can see the tags. We have items there. Looks much better. Much, much better. I still see, like, category here. We probably need to fix that as well just because it's gonna drive me crazy. So that is our display template here. We just wanna make sure we're displaying related values. If I don't populate that field, it should show, like, the default. Why is it not doing that? Okay. I'll just fix it. Just let me fix it. Alright. We want the title here. Let's add the title there. Make a liar out of me. Okay. So now I can see the category. I can rearrange the fields up here. This looks good. There's the category this belongs to. Maybe that's second, third, etcetera. Yeah. I could filter those by sending value, whatever I wanted. Cool. If I open up the category, I could see all the articles within it and this may be the best way to manage my different articles. Right? So eighteen minutes in, we've got the skeleton of our help center back end. Right? This looks nice. We've got the ability for someone to edit this content, in a nice editor and riggy jig, away we go. Right? So the other component to this, we need a front end. Right? And I found this beautiful docs template, from mister Tony Tony Zhang. Tony, have you ever watched this? This is a pretty cool ShadCn view docs template that you've created. It looks great. So you've got, you know, kind of the standard docs vibe going on. You get a lot of components already built into this thing. So let's just go to GitHub. And take a big risk here on hundred apps, hundred hours. I have not messed with this. It looks like you've got, like, a starter here, but we're gonna, like, change some of the nuts and bolts of this. So, we're gonna abandon my usual Nuxt starter application that I have. And let's just open this up. We're gonna go to the terminal, git clone, shad c n docs nuxt. I think I'm in the right spot. Maybe not. Maybe I wanna do git clone, CD and then git clone. Let's back up one directory. Looks like it was already in the help center directory. Did that what did we get here? Trash. Oh, that's in the trash. Let's open a new terminal instance and solve for that problem. Git clone. Okay. So now we have shad c n docs nuxt. We'll open this up in the terminal. P n p m I. Let's get this thing fired up. And let's just kind of inspect what's going on here. I see an app dot vue file. That's kind of standard for a Nuxt app. We have an app folder that controls router options. Looks like that is, like, controlling for, like, a sticky header or something like that. This is cool. And then when you see, like, we have oh, that's my own app. Come on, guy. That's your standard Nuxt app that you have. Right? Why are you doing that? So then we have something like pages. Cool. So this is using Nuxt Content. Nuxt Content is a really cool, headless kind of content. So basically, hey. We can use markdown, and allows us to use Git as a CMS, basically. File based CMS. That's what I was struggling with. They've got this MDC format though, which is kind of interesting where I can, like, use a component like this, like hero, and it will generate this. It'll actually generate stuff like cards for you using kind of a markdown familiar syntax, which, again, pretty cool for developers. You know, nice for content editors that they can create like a complex like looking card or something in here. Could get confusing, maybe not. Let's run with it and see. But we've got this thing installed. Let's just fire up the dev server and see what we can get done with this thing. Alright. So we'll go to local host. And we're developing. We're developing. Looking good. Zoom out and direct us a little bit. Oh, that's not what we want. Come on, baby. Come on. That first time's a little bit slow. Don't be shy. Okay. So this looks pretty good. We've got an index here. This is a content renderer. I don't know where this is picking up the content from. I'm assuming it's coming from this index.md thing here, and you can kinda see what's going on. This is the home page. We've got effortless and beautiful. We'll just change this. Bryant's hundred apps, hundred hours, starter, help center. Using cursor here. It's got nice auto complete. I I that's probably the main reason that I love it. It's a little more intelligent than, like, the Copilot for stuff that I'm already gonna be typing. This is a help center, but with a hundred apps, hundred hours starter. Great. We don't need this iframe. Cool. We're gonna get started. There it is. Great. We could leave the buttons. Cool. This is gonna be what? To where do we wanna navigate to? Let's go to slash, what was our doc that we had? Right? We've got accessing items that is that's what we'll do. Right? Accessing items. If we remove this link to GitHub, we save it. Something broken. I broke something. Why is this not working right? Actions. Okay. There we go. That's gonna take us to accessing items. That's a four zero four. Bummer. No big deal. Alright. So this got this content directory that we're pulling from. Okay. Great. We can see it's using that that Nuxt content here. Nice feature. Great. I'm just gonna like slap, I'm gonna copy the index file, honestly, from my other starter that I have here and I'm just gonna stick this in index because I'm lazy. Alright. So now you can see this is my standard starter index file that we'll see. I'll just remove that. Hundred apps, hundred hours. NUC starter. Boom. There it is. Great. Cool. But I should probably have a button actually to go to one of the individual pages, though. Right? Let's keep that. Accessing items. Get started. And I'm doing this because I don't want the actual Nuxt content pieces of this. It's okay. Nuxt u button. What? Nuxt link? Or should it be UI button? UI button. Where is our where is our UI button? There's our UI button. What other props do we have as a button? As a as Nuxt link? Can we do that with this thing? Will that actually work? Is it gonna like that? Atrial? Why is it not it's not liking that at all. I don't know. I don't know why. What if we just do a regular a? Okay. There's our tag. Obviously, that's gonna hit us with like a full page reload, but no worries. Alright. So what we're gonna do, I'm gonna create a Directus plug in for this, Directus.ts. And to save myself time, invoking rule number two, use whatever you have at your disposal. I've already got a sample Directus plug in in my starter. So we'll just pull this across to plug ins. Alright. Now if I can go full screen on this. I should put it in the wrong window, Alright. So first off, we need to install the Directus SDK. I'm not gonna use cookies here. So do we really need authentication? No. We don't need that from the SDK. Let's not even use real time or read me. We're just gonna use the REST API. Gravy. I'm gonna get rid of this bit about authentication and this bit about that. And all we're gonna do here, pretty easy, pretty simple, we are fetching a direct as public URL from the Nuxt runtime config. We are creating a directus client and then we're providing that back to the Nuxt application so we can access that in multiple places. So now we're gonna install directus SDK. Solid. And I'm gonna find the Nuxt config for this file. And then we're gonna add, probably need an env variable. Is there a dot env file already? There's not. We'll call this Directus URL. That's gonna be HTTP local host 8055. That's just what I'm running on here. Great. And then I'm gonna find what's looking for our runtime config. Public, direct us URL. You can see this is already picking this stuff up for me. Another nice benefit of cursor. And then so we've stripped out. We didn't really strip it out, but let's strip this out. We don't need that. For our slug, what do we wanna do? What do we wanna do? We've got, why is script set up not at the top? This is again, just my OCD kicking in. There's some lint rules set up here. E s lint config. Delete. View block order. Oh, let's change that guy. Alright. Is that gonna fix this now? Script setup. Script. Okay. Alright. Changing some rules there. We're we're cooking with gas now. Alright. So we could see what's going on here. Like, this is like pulling this content in from markdown files itself. That's I don't wanna do that. I'm not gonna do it. But now we got this nice directus plugin that we can use and that'll be directus URL. It should be a string. Let's just fire this up. There's one other thing that I'm going to add to this and it is the I I know because I've worked with this thing before. It's the Nuxt MDC package, which is basically doing some of the heavy lifting under the hood as far as, like, the taking the MDC format and, you know, generating that AST that we can then use to render with. So, we'll p m p m I at Nuxt MDC. And you can see here there is like this parsed markdown or parse markdown function that we could call. Then we can render that markdown like the body and the data format. Alright. So we've got those two things. Let's fire up this dev server one more time and we'll see what we could do as far as a slug. Right? So now we're gonna do something like this where I'm gonna import read items from the direct Us SDK and I'm also gonna pull in that Directus client from Nuxt app. So the SDK is very modular, so I don't have to import all the methods that I'm not actually using. Right? And Nuxt will do some fancy, tree shaking magic for us based on the the individual routes. So should be all great. What we're gonna do here, we'll do something like this. We'll do constant data and then we're gonna do await, use async data. Okay. So we've got this async data composable from Nuxt. We're gonna give this, we want let's also pull in the route for this as well. So route equals use route. We're gonna pass this a key to use. Alright. So this will be page this will be article let's use a back tick here. Article dash route dot params.slug.join. Maybe. If that's an actual hooray. Okay. Here we go. This is not right at all. This is one of those times that you may not want to totally trust. We're gonna return read item no. We're not gonna return that. We're gonna return directives dot request. Read items. We're gonna get the articles. And then we can define a query. Right? So our query is gonna look like this. We got the filter, and then the slug is what we wanna filter on. And we want that to be equal to route dot params dot slug dot join. I'm I'm not even sure we actually need that. I can't remember the format that Nuxt is gonna give this to us in. We could call this actual data page. And then somewhere in here, this is where this is read items, we want to do a transform on that async data call. Transform. Data, data zero. So we're gonna get the first item in that array. This looks good. Directus, directus dot request. Data page. Script setup tag. What am I missing? Did I goo something? Directus request would read items. Oh, I didn't miss something. There we go. I think that gets us where we wanna be. Alright. So now let's just open this up, test this out. Okay. Unexpected token, define page components. Let's just ignore all of this baloney right now. Got page, invalid URL. Well, that's not great. E n v, direct as URL. K. Nux config, direct as URL. I don't need all of that. Direct as URL. Oh, that's in the wrong config file. Let's just copy that. Nuxt config, shad c n docs. Cool. Why is that not showing as directories URL as string? I Wonder why that's not showing an error here. TypeScript error. Nux config. Wrong Nux config. Unexpected use of the global variable. Why is that unexpected use? Fix with AI. What does it come back with? Import dot Neta dot e m v. That's not gonna be right. PNP m dev. Maybe that's where we didn't have that the first time we started the dev server. Not a %. Articles, filters, slug, param. And we can even do let's just console log the route dot params dot slug. Slug. Okay. So now we could start to see what the problem is here. Slug needs to be does need to be joined. Slug.join. Dodger, hon. Slug accessing items. Why can't we fetch that page? What's going on? There's our slug. We could see the slug. Great. Why can't we fetch this actual page, right? If we navigate, we go back. We should see, like, a fetch request here from Directus. Twenty minutes. How are we doing? How are we doing? We're not doing great, Brian. Not great. Alright. We're we should see a call to direct us somewhere. Local host. 8055. Not seeing it. Alright. Accessing items. Oh, it's because we're not using a link on that index page. Server side rendering. Fun stuff. So is there a link in this? Links button link button link button link to okay. So we should use button link, content button link. What component does that get us? Does that get us where we wanna go? Kind of? Kind of not? So we see a lot of stuff coming from, like, the Nuxt content API. We don't really want any of that business. Don't want that smoke. How can we strip all this out? Right? Can I just is this gonna can all this? This is gonna break a bunch of stuff, isn't it? Umami client, I don't want that either. Slug. Okay. If I just trash all of content, what happens if I disable Nuxt content entirely? Nuxt image, you use Nuxt. Chat c n Nuxt. This could go way wrong. It's always fun, though. Right? Let's just remove content. You where are we using use content helpers at? Right? I guess I should be looking at these individual things going on here. Use content helpers. Where's that at? Aside. So we gotta decide. We don't even care about that. What does our where are our layouts? Do we have a layout? Components, where where are you? App dot view. Layout header, layout aside. Let's just remove the aside. What what's in the header? Layout header, Use config. Okay. Main setup. Use content headers. Search dialogue. Come on. Why did you do that, Brian? That doesn't make sense. Use config. It's gonna break. Header, main, new key from value path. Page dot value dot main. Now if we just strip that out, what what is left? So many fun things here. This is a good reason not to use your own template, or this is a good reason to use your own template. Where else are you use content helpers? Don't want you. Where is search dialogue being used at? Search button layout, search dialogue. Search button. We'll just remove that. Can I actually get something loaded? Stressful. Stressful. Stressful. What are we working with? Nineteen, twenty minutes. So we're back to this slug catch all route. Can we even console log the page? Let's see what we're getting back from the actual page here. Looks like we're struggling to not render any of this, so I'll just fall back on one of my popular patterns that you'll see in a bunch of other episodes of 100 apps, one hundred hours. We'll just do this. Div. Okay. Cool. So now we can actually see that we are getting some content. It's just that we're not rendering the content properly. Bada bing bada boom. That's a problem. Right? Alright. So now let's hop into this Nux MDC business where, hey, we're getting some markdown here from something. Can we just use this n d c component to render this markdown? Alright. So I'll go here. This will be page dot it's not body. It's page content. What will that actually render, if anything? View, hydration, mix, match, can't find smart icon, can't resolve, KBD. Do we see a MDC component? No. I don't. And why? Because we did not add this to that. So we want at nuxt js slash mdc. I think that's correct. Nuxt mdc module. We're gonna add that to our modules. P m p m dev. We'll restart this guy. Oh my gosh. Did I put this? Okay. Don't do what I did. Do not put this in the same directory as your normal stuff. Ugh, I feel like an idiot. Okay. There we go. Fire this up. PnPM dev, we'll run it again. We'll wait for Nuxt to run this out. And is this actually going to work the way we want it to once it spins all of this up? We are cruising on seventeen minutes. Can we actually show these files? Bada bing, badda boom. Cannot find compose. Where is this coming from? Cannot find module composer. Shadcian YAML. Shadcian docs YAML. Package YAML. Types index, run model v runner. What are we doing? Theme, CSS, directory. Nuxt instance is unavailable. P m p m I. When all else fails, just blow away all of your Nuxt stuff. What have happens if we do the node modules? PMPMI. Let's try this one more time. Or it could just be that I've totally broken this thing by stripping away the Nuxt content piece. Probably would have been better off just starting with our Nuxt boilerplate that I already had. Not saying this is not a good piece of kit, this Shadc and Nuxt template. Just saying if you're gonna do this, don't try to learn that and do this at the same time. Trying to think of what this would even be. That's the Nux icon TS config base URL. Hopefully, this thing will spin up now. Hundred apps, hundred hours, starter. There's that. It's not finding a smart icon. I don't know what the smart icon is. Not really super concerned about it. Alright. So here's the page. We could see this is the value that we're getting from the page. Failed to load the resource. So it's not actually rendering anything. Right? So that part of it sucks. Don't like that. Let's actually try this import parse markdown. So if we get the content, we'll do page dot value dot content. Should we even use, like, a computed prop for that? You shouldn't put side effects in in that. Right? Page value, content, slugs. Let's try console dot log content. What if we do console dot log page dot value? Just see what we're getting here. We want to await that. Wait that. What is this? Man. TS config. Build transpile. I'm not gonna I'm not even gonna use Shiki, but it's probably gonna throw a fit because we've got some components inside this thing that are using it. The fun of using someone else's template. Why? Where is this even coming from? If we search all inside here, YAML, Nux config, app config. So there's a lot of this in the app config. It require Nuxt YAML. Where is YAML? Is that it? Composer dot JS. Module helpers, vite node, types node, use config, editor config. Yeah. This is this is wack, man. Alright. Pivot on the fly. Bad Chad c n Docs Nuxt. Just delete you, sir. Now, again, this looks like a a great template. I just don't have enough time. I've got eleven minutes here. So we are getting pretty desperate at this point. We are gonna do I guess I shoulda took what I had before we just ripped it all out. Right? You live, you learn. V base script. I want script setup lang t s. Alright. Now alright. So we go to this slug. That's gonna be inside the pages directory. We don't really need auth, but let's go back to what we had. Pnpmi at nuxt js slash mdc. That should get us what we need here. I've already mistakenly put that into the Nuxt config. And here we're gonna use routes. Use routes. Import. Read items. Read items from direct us Nuxt. Then we'll use constant data of the page. We will await use async data. Give this a page, page routes dot params.slug dot join slash. Great. Okay. Then we've got the actual function we're gonna return. Return, read items, pages, filters, slug, not even close. Directus dot request, read items, pages. Still not even close. Boys, come on. Cursor is not not savvy on this. Filter. It's slug. You're just getting in the way at this point, aren't you, friend? And then we will pull in direct us from Nuxt app. We hit page. This gives us what we need. P m p m dev. Parse markdown. Pull that in. Alright. So now we want to parse the markdown content. Console log page. Console log content. We'll just do pre content. What is that actually gonna render, if anything? Oh my gosh. Failed to resolve import blah blah blah. Does this actually work? Gosh. Fix your template, homie. Fix your template. Alright. I'm sure you guys are having fun following along with this disaster show here. PMPMI. Delete. PNPM dev. This is probably like a type error, I'm assuming. Fingers crossed. Or we might just have to hang it up on this one. I hate to be defeated. I really just wanna show some of this content, and I wanted to try the, like, the actual components where you can do MDC in here. Oh my god. Failed to load. BMPM lock. Delete. Here's what we'll do. If this doesn't work, I'm done. Nuxt. Node modules. Delete. Nuxt. Do we have the MDC in here? Nuxt MDC. Where are you? There it is. Nuxt MDC. Alright. Let me fire this up one last time. We got this. You got this next. Come on, baby. We've already got our plug in here that should be working for us. We can strip all of this out, just comment it out, basically. We don't need that. Don't need any of that. Okay. Everything's installed. PMPM dev. I think we need, like, a blooper reel, but maybe that would be every episode that resulted in just nonstop bloopers. Okay. So far so good. I don't see any major errors coming through. Cannot resolve Tailwind CSS. Are you freaking kidding me? PMPMI, Tailwind CSS at next. Sometimes it just doesn't work out in your favor. I think that is like 90% of modern JavaScript development is wrestling with dependencies and bundlers and config. Honestly, I spend way more time doing that than I do actually developing stuff. Non pojos. Does this have to have Nuxt Content installed? PMI Nuxt content. What is the name of this package? Content dot Nuxt. At Nuxt content, I'm assuming, releases. You can start with a fresh I wanna start add to a project, add Nuxt content. I swear if this is it, I'm gonna be so mad that you just have to install this thing. I thought I was being clever showing you guys this Nux MDC stuff. I've messed with this before. I've got it working previously. Obviously, a little little rusty. Alright. So let's say we do wanna do, like, this alert thing. What is this thing doing, man? What? Where is Nuxt YAML? Where the hell is this coming from? Can I find Compose? Compose.js. Where where is this even at, man? Composer requires to CJS loader YAML, feet runner. I'm not loading any YAML. YAML. Do I have something in the content directory? No. Freaking no, man. If I remove this parse markdown, does that fix it? Page dot contents. Okay. Yeah. So it renders something here. Console log. Page is undefined. That could be why. Not even getting a page. That's why you don't trust, don't trust AI, friends. Page dot it's not even page. It shouldn't be page. But page return request direct us articles filter slug equals route dot params .slug.join. Okay. So now we can see the page. We need to go back and add our transform option. Transform. Get our data. Hey, yo. There's our content. What do we got? We got a minute and forty seven seconds left. Amazing. Just freaking amazing, honestly. H one page dot title. Class text three x l, font bold. Let's add a container to this. Right? That'll give us our page. Amazing. Is that really what it was? Now is this actually gonna work? Content. What if we show, like, MDC render value equals content. No. It's it's still not gonna shove that down, is it? Well, well, well, well, mister Bryant, it does not like you. Anyway, we got remotely close. Nah. We really didn't. We've got the help center, like, part of it set up. Didn't even get into any of the, like, fun MDC component stuff. Roll credits, guys. Yeah. Recapping this, it was a bit ambitious to dive into a template that I had never used before and was totally set up for local markdown files. And I think I could wrestle that in an hour and actually show you how to render Vue components from content, delivered via the CMS. But, hey, sometimes you win, sometimes you lose. Just gotta keep rolling the dice. Alright. That's it for this episode of hundred apps, hundred hours. We'll catch you on the next one. Thanks for joining.","3dde94eb-edb1-47e2-b894-8261adc507c7",[1115],"83401e22-c391-4741-b6e7-6d47ec52de87",[],{"year":399,"number":1060,"id":1061,"episodes":1118,"show":1119},[1063,1064,1065,1066,1046,1067,1068,1069,1070,1071],{"title":322,"slug":323},{"id":1068,"slug":1121,"vimeo_id":1122,"description":1123,"tile":1124,"length":1125,"resources":12,"people":12,"episode_number":129,"published":1051,"title":1126,"video_transcript_html":1127,"video_transcript_text":1128,"content":12,"seo":1129,"status":19,"episode_people":1130,"recommendations":1132,"season":1133},"website-personalization-engine","1059436796","Bryant builds a content personalization system that shows different variations of web content based on visitor segments. Watch as he uses Directus to create a flexible backend for managing segment-specific content variations, then implements client-side detection to serve the right content to the right visitors.","40c50d05-f056-49ff-a4e8-cb7db67cbdc1",61,"Mission: Website Personalization Engine","\u003Cp>Speaker 0: Welcome back to another episode of 100 apps, one hundred hours. I'm your host, Brian Gillespie, here from Directus. And today should be well, I think it's gonna be an exciting show. My confidence level on this one, just to put that out there, is fairly lower than normal. But today, we are going to be building a website personalization engine.\u003C\u002Fp>\u003Cp>I went through several different titles for this one. I don't know if that's actually gonna stick when this goes on Directus TV or not. But, what are we trying to do? Well, first, let's explain the rules. If you're new to 100 apps, one hundred hours, the rules are simple.\u003C\u002Fp>\u003Cp>Basically, we have sixty minutes to plan and build a clone, an application, a website. So that is plan and build, nothing more, nothing less. And then the only other rule, which is the I call it the anti rule, is use whatever you have at your disposal, which I will probably be leveraging, some AI today because I am wading into uncharted territory for me. So website personalization engine, sixty minutes. Let's do this together.\u003C\u002Fp>\u003Cp>Or it'll, like, publicly laugh at Bryant. Try. Alright. So we're gonna put sixty minutes on the clock, and let's cover this. Alright.\u003C\u002Fp>\u003Cp>So website personalization, you know, originally, this idea was for, like, personalized landing pages. Like, I know a specific company or target account that I want. Let's create a specific landing page for them. But in the light research that I did before this, which is basically, like, ten minutes of Google, basically, when it comes to personalization, it's more of a segment based approach. So that's what we're gonna sketch out here.\u003C\u002Fp>\u003Cp>You know, we wanna bucket visitors into a segment and then display certain content for that spec segment to personalize it. So, let's plan out our kinda data model. And, you know, I could leverage, like, some of our direct to CMS starters here. We're just gonna keep this stupid simple super simple. I tell my kids not to say stupid, so I probably shouldn't say that either.\u003C\u002Fp>\u003Cp>But alright. So as far as our data model, let's flesh out the application. Maybe we do just wanna, like, flesh out what are the actual features that we want out of this specific application and make that less huge. Alright. So we want to have a piece of content.\u003C\u002Fp>\u003Cp>Great. Directus will manage that for us pretty easily. And then we want to show variations of that content based on traits of a visitor or segments segments that a visitor belongs to. Now, something like this or or even when you get into, like, AB testing, you know, it's fairly easy. I won't well, I won't say fairly easy, but it's easy enough to set up, like, an AB testing code.\u003C\u002Fp>\u003Cp>If this, do that. If this if else, do that. But, like, the intersection of, like, handing this off to a content or a marketing team and setting it up in code and, like, managing all of that to work together for front end, back end, and making it cohesive, really difficult task. Right? So this is really all I'm trying to achieve today.\u003C\u002Fp>\u003Cp>We could make this fancier maybe down the the line, but let's let's discuss the data model for this. What are we gonna set up? So I could just call this content. Maybe it's, let's just have a pages collection. You know, this could be like a post or something like that.\u003C\u002Fp>\u003Cp>And then we're going to have what else? We're probably gonna have some segments. Like, here's the different segments that we want. And, you know, if you were doing, like, a proper system, you'd probably want something like events. Not super concerned with that.\u003C\u002Fp>\u003Cp>You know, the segments probably have, like, some rules for each segment or yeah. What are the rules? This sort of thing. I don't even know if we'll get this far. Let's just try to wade through this and and show something on the page.\u003C\u002Fp>\u003Cp>Alright. So let's get into what I've got set up. This is, just a Nuxt application. This is my standard starter for, you know, like, if I was just YOLO ing into a hackathon, this would be my starter. You could see a bunch of commented out code.\u003C\u002Fp>\u003Cp>But basically, I've got a blank Directus instance, if I can actually get logged into this bad boy. Directus is gonna be our CMS. It's gonna handle all of the back end for us. Dear lord. Why can I not get logged into this thing?\u003C\u002Fp>\u003Cp>Boy. Boy, oh, boy. Admin example. Password is the password. It's not even up, but that would be why that would be why I couldn't get into the application.\u003C\u002Fp>\u003Cp>Alright. So I've got Directus set up, in a Docker Compose, just a standard local setup kind of format. And like I said, Directus is gonna be our back end, our CMS. Okay. Great.\u003C\u002Fp>\u003Cp>Blank slate. Nothing fancy here. We do have templates available for stuff like CMS if if that's something that you're into. And then we have a Nuxt website. Alright.\u003C\u002Fp>\u003Cp>So let's start building some functionality. And inside the Nuxt website, we've got, like, an index page. So that's what we're staring at here. Alright, so let's add some pages content. Great.\u003C\u002Fp>\u003Cp>Go in, we'll create a new collection inside pages. For those of you who are new to Directus, what's happening behind the scenes here is Directus is connected to my Postgres database and it is basically gonna mirror whatever changes I make here to that SQL database. And I could connect like a an existing SQL database to this as well. So we have some pages. Great.\u003C\u002Fp>\u003Cp>Got a collection that's gonna show up here. When I hit new, there's nothing shows up. So we need to add some fields to our pages. And again, like, we're modeling our data here. We're generating an API and we're building a form for our content editors all in one place.\u003C\u002Fp>\u003Cp>So page needs a title. Great. A page needs a slug. So we're just gonna create a new field called slug. I'm gonna go into the interface settings and make sure this is URL safe.\u003C\u002Fp>\u003Cp>Do I have my little pointer? Yeah. I do. That's a cool little tool. Occasionally, I get questions on it.\u003C\u002Fp>\u003Cp>It is called mouse pose, Mac only, I think. Alright. So what else are we gonna have on our pages? We got a title. We got a slug.\u003C\u002Fp>\u003Cp>Maybe we'll keep this simple. Right? We've got a headline. That could be some text. Cool.\u003C\u002Fp>\u003Cp>And because I am totally gonna make it easy on myself, we're just gonna call this content. One big rich HTML rich text content block. This probably looks more akin to, like, a blog post than a page. Directus does have that amazing many to any like page builder scenario that's covered in some of our templates but we gotta crawl before we can walk. Alright so we got our pages, lets go ahead and let's just create a page.\u003C\u002Fp>\u003Cp>Right? This is Brian's page. Brian's page. Cool. Hey, yo.\u003C\u002Fp>\u003Cp>Why am I talking to myself? Great. Alright. So we got some content. Now if I want to query that via the API, I can go to the directus URL, go to items, pages, and boom.\u003C\u002Fp>\u003Cp>I could see I have a page here. There's my content. API is ready to go. This is already I've I've got the Nuxt application wired up with, like, a simple Directus SDK client. So we're creating Directus, we're using REST, and then we're providing that to the Nuxt application.\u003C\u002Fp>\u003Cp>So So if I go in and I do something like this, where we're just gonna create a new dynamic route inside Nuxt and do I have, like, a v t s? Yep. Script set up. Really need to work on my snippets to save time here. And my script at the top guy, when I'm working with Nuxt review.\u003C\u002Fp>\u003Cp>Alright. So now we want to fetch some of this page content. Right? Because what happens now, I've got this. If I go to Bryant's page, nothing shows.\u003C\u002Fp>\u003Cp>Yeah. That sucks. We need to resolve that. Alright. So what we're gonna do, we will do const can't type today.\u003C\u002Fp>\u003Cp>You'll notice I am using cursor here. Directus equals use Nuxt app. And, okay, we've got some auto completions that are not necessarily gonna give us what we want. Alright, so we're gonna get the data. Nuxt has the use async data function or the composable that we're going to use.\u003C\u002Fp>\u003Cp>If I can actually type that, great. We're gonna give this a key. So, the key, if we grab the routes, is going to be the slug, route. Params. Slug page.\u003C\u002Fp>\u003Cp>Let's just tag this as a literal. There we go. Thank you. Alright. And then we are going to return directus dot request, we're gonna do read items pages.\u003C\u002Fp>\u003Cp>Pages. K. And we're gonna filter where the slug, that's right, is equal to route dot params dot slug. K. So we have direct us there.\u003C\u002Fp>\u003Cp>We're gonna import read items from our SDK. You know, I could potentially, like, import that here and provide that as well if I wanted to, but we'll just do it this way, keep it nice and easy. And then let's see if we can just actually get the data. It'd been a minute since I messed with this. Is this actually going to work?\u003C\u002Fp>\u003Cp>Are we making a network call? Oh, wait. Use async data. There's that. There's the call that we're returning.\u003C\u002Fp>\u003Cp>Read items pages. Let's check our network requests. Are we seeing any actual network requests? It is server rendering these. So we are not seeing that.\u003C\u002Fp>\u003Cp>Probably want to add some error handling. So we'll just add that. If there's an error dot value, we are going to console dot error, error dot value. Great. Do I have any formatting set on this?\u003C\u002Fp>\u003Cp>Got a little bit of formatting. Alright. Can we see the error? H three error create error. What is going on there?\u003C\u002Fp>\u003Cp>There's an error. Error dot value dot message. What's the format for this? Create error. We're throwing an error.\u003C\u002Fp>\u003Cp>Hey. I'm guessing though, just from past experience, this is because we have not enabled any access to that page's collection. Right? So when I do this, I am still using the session token from Directus. But if I open this up in a new browser, we could see, hey.\u003C\u002Fp>\u003Cp>I don't have any permissions. So I'm just gonna enable permissions for pages. Let's do read permissions, see what we got. Boom. We can actually get some data and wrap this in a nice pre tag to see what we've got.\u003C\u002Fp>\u003Cp>Okay. Cool. Now let's take this data that we have and we'll add an h one. This will be the page dot headline. And then we are going to check and see what all I do have in this application.\u003C\u002Fp>\u003Cp>I don't have, like, a pros component. So we'll just do something like this where we have VHtml content. Now if I refresh if I refresh if I refresh, why is it that actually showing? Where's my content? Page dot headline.\u003C\u002Fp>\u003Cp>How is it that I could see the page content? Oh, I do have it here. Page dot headline. Why isn't that actually showing on the page, though? Interesting question.\u003C\u002Fp>\u003Cp>Interesting. Definitely. Let's just center this up. Maybe let's max auto, max width, four x up. Add some padding.\u003C\u002Fp>\u003Cp>Why isn't my content displaying via page? Do we need to wait on the page? Am I really not smart enough to figure this part of it out? Is there some type of error that we're getting paged out headline? I could clearly see oh, yes.\u003C\u002Fp>\u003Cp>That's why, Brian. You are getting an array here, so we need to transform that. So async data. I think it's here that we can do a transform. Data.\u003C\u002Fp>\u003Cp>Data. Just the first item. There we go. Alright. There we go.\u003C\u002Fp>\u003Cp>Hey, yo. Why am I talking to myself? Make sure that you understand what your data looks like before you start trying to mess around with it. We're gonna make that giant. And if we slap a pros tag onto this, pros l g.\u003C\u002Fp>\u003Cp>I think I've got Telen typography included here so we could see this data. Okay. So we're rendering something out to the page. Great. Now, this is super fancy.\u003C\u002Fp>\u003Cp>Right? Let's just grab something from the Directus blog to use as content. Here's a post that I wrote. I'm just gonna copy this entire thing. We're gonna go in and throw that here.\u003C\u002Fp>\u003Cp>Great. Just to get something on the page. Okay. We'll call this context switching sucks for devs. Cool.\u003C\u002Fp>\u003Cp>Alright. So now we are showing a basic thing on the page. Hallelujah this is amazing this is the coolest thing we've ever built. How are we doing on time? We got about forty five minutes left.\u003C\u002Fp>\u003Cp>All right so we have a piece of content now we want to show variations of that content for an actual visitor. So how are we going to achieve this sort of thing? And, you know, if we boil this down to the basics, we've got a page. We're going to have variations of page content. Yeah.\u003C\u002Fp>\u003Cp>We could call this personalizations or page variations. That's gonna be linked to our segments, and this is not right. I do have a PhD in drawing arrows, just not in Figma. This is still a challenge for me. Alright.\u003C\u002Fp>\u003Cp>So let's create this and I think what we can do is hijack some of the functionality that already exists in Directus for this sort of thing. Going off label here, off brand. So, we will go into our data model. What I'm gonna do, I'm just gonna create a page variations. Yeah.\u003C\u002Fp>\u003Cp>Naming stuff is probably the hardest challenge in development. You know, we could call this personalizations, but that might make somebody mad. Yeah. I'll just do created at, created by. I don't necessarily need this information.\u003C\u002Fp>\u003Cp>These are just helpers that you can add. Directus, like, prefix or, like, sets these up for you. So whenever, you save this item or create a new item, it obviously records the user, etcetera. Just some shortcuts. Alright.\u003C\u002Fp>\u003Cp>So we have page variations. We've got two different collections here. Great. How are we gonna tie these things together? Now, what I can do and what I'm gonna steal is our translations feature.\u003C\u002Fp>\u003Cp>So if we go to the docs actually, let's go to the new docs. These things are in beta as of now. We're gonna look for translations. Great. We have the ability to translate content and manage all those translations for you.\u003C\u002Fp>\u003Cp>And if I open this in a new tab, we get, like, this beautiful side by side interface where I could see, okay, here's English versus Spanish or French. And this is like, when we boil it down, like, when I think about it, I'm just now coming to me through my head here, is it basically, translation is a form of personalization anyway. So why not leverage this existing structure? You know, short of, like, the icons and maybe some of the other stuff that is baked in there, I I think it should work. So I'm also going to like, normally, if we were doing translations, you would create, like, a languages collection where you would store all the languages that you wanna translate your content into.\u003C\u002Fp>\u003Cp>In this case, what we're gonna do is just use, I'm gonna create a new collection. I'm gonna call it segments. You know, we could manually generate the IDs for these or we could use, like, a generated UUID. You know, if I was trying to scale this out, I would definitely be using, like, generated UUIDs, but let's just call this key we're gonna manually enter a string for this. So whenever we create a new segment, we're gonna manually enter in the key.\u003C\u002Fp>\u003Cp>That'll help us keep things a little bit clearer. Updated at updated by just to keep track. And do we want this segment to be active or not? You know, maybe we set that up on a a Boolean toggle. Alright.\u003C\u002Fp>\u003Cp>So we got a key for it. Maybe we want, like, a proper name. So we add a name field and drag this down. And what I'm doing here is basically just configuring the actual form that we're gonna use to set this stuff up. Do we have a segment?\u003C\u002Fp>\u003Cp>Do we have a category? Yep. There we go. Yeah. Again, naming things is hard.\u003C\u002Fp>\u003Cp>Let's add some icons. Just the designer OCD for me kicking in. Doc. Yep. There we go.\u003C\u002Fp>\u003Cp>Edit document. And I'm just gonna like actually hide these page variations. So now we have pages, we have segments. Let's create a new segment. Right, I want to speak to developers.\u003C\u002Fp>\u003Cp>We could say these are for developers, that's our nice name. Directus is also helpful for content editors, so I'll do content editors. Yeah. I wish I was doing this live so I could say, hey, hyphenate or underscore, but let's just go with a hyphen. Fine.\u003C\u002Fp>\u003Cp>Fine. Fine. You know, I could keep adding these as much as I want. Right? So now we've got some segments, we've got some pages.\u003C\u002Fp>\u003Cp>We wanna generate these variations. So again, we're gonna reach for the Directus translations interface. And I hope this works out like I think it will or like I hope it will. You can see that we're pre setting the languages collection. Directus is smart enough to know, like, when you're doing translations, you need languages, we can create that for you.\u003C\u002Fp>\u003Cp>But what I'm gonna do here, I'm gonna open up advanced field mode instead, and I'm gonna go in and I'm just gonna do all of this mapping myself. So we try to be smart and create junction collections and everything for you, but you can take total control of this as well. So here, this is the collection. So we're gonna call this page variations. I'm just gonna use page and we're gonna call this the segment and the collection that we're targeting is segments.\u003C\u002Fp>\u003Cp>So, I'm gonna set these all up to cascade, so if we delete a page variation or deselect one, we delete that content because we're not going to use these variations across different collections or pages. Alright, so the language indicator field. Now normally this is what shows here right up here. Right? Just the indicator of what this is.\u003C\u002Fp>\u003Cp>We could use the key. We could use the name. Let's use the name. Language direction. We're gonna ignore that.\u003C\u002Fp>\u003Cp>Default language is the primary key. Let's let's say I wanna default to developers. And we're gonna start this split. So we'll start open like this. Great.\u003C\u002Fp>\u003Cp>Okay. So now we create this, and we could show related values. You know, you can even preview the translation content. And I already see one thing that's going to bug the crap out of me as I called it translations. I forgot to rename this.\u003C\u002Fp>\u003Cp>Alright. So we're gonna call this variations. So it's not translations, it's variations. We're just piggybacking off of functionality. Alright.\u003C\u002Fp>\u003Cp>Pages. Cool. This is gonna be the what? Key? No.\u003C\u002Fp>\u003Cp>Segment. We're gonna call that segment. That's the segment. The collection is segment. And I gotta set up this nice cascade again.\u003C\u002Fp>\u003Cp>Okay. Go through the exact same process all over again. Love shooting myself in the foot Just to make sure we have variations. Is variations spelled correctly? At this point, we don't know.\u003C\u002Fp>\u003Cp>Alright. So I probably goofed something up already. Did I? Did I not? Page variations.\u003C\u002Fp>\u003Cp>Page variations. Page page is not found. Oh. Yep. Alright.\u003C\u002Fp>\u003Cp>So love it when I goof up. Cause more work for myself. We're just gonna start this process all over. Alright. Try it one more time.\u003C\u002Fp>\u003Cp>Page, variations, Generate the UID. I'm not even gonna go for that. At this point, we're gonna say translations interface. Great. This is gonna be segments.\u003C\u002Fp>\u003Cp>We're going to use the segment name. We're gonna start with the split open view. And let's control this. Right? Page variations.\u003C\u002Fp>\u003Cp>This is gonna be page. Segment. Delete. Alright. So definitely don't do what I have done.\u003C\u002Fp>\u003Cp>Let's try this. See if that works. Doesn't have field page and doesn't have a relationship. And what in the world field page in collections doesn't have a relationship? Why doesn't it it should have a relationship.\u003C\u002Fp>\u003Cp>Pages, segments, translations, start open. Why doesn't this have a relationship? Well, let's just see what happens when we do this in orally. Translations already has a relationship. Alright.\u003C\u002Fp>\u003Cp>So we can see here by adding this we get a languages and then we get a pages translations, there's a pages ID, there's an ID. This should work out okay. I don't know what is going on. Maybe I didn't refresh the data model. Key name, segments, page variations.\u003C\u002Fp>\u003Cp>Why am I not getting this to work correctly? Let's just take a look at our database and see what's happening. 53Edit. 5 3 2, test, Connect. Alright.\u003C\u002Fp>\u003Cp>So now we're behind the scenes into the database trying to figure out what I goofed up to begin with and it looks like there's some relationships here that got left over that didn't get fixed that are causing problems. Alright. So now with that out of the way, let's try this again. Page variations, we're gonna generate an ID for those. I'm just gonna skip all the fanciness.\u003C\u002Fp>\u003Cp>And now I'm gonna go in and do translations. We're gonna call this variations. Instead of languages we're going to use segments, we're going to use the name of that. We are then going to set up to use page variations, page segments, segment. Okay.\u003C\u002Fp>\u003Cp>Now is that going to work out how we want fingers crossed. Let's log in and see what have we got. Okay. So now I get this side by side interface of content editors and developers. Right?\u003C\u002Fp>\u003Cp>But I don't see anything inside the actual form. So inside this junction table is where we're gonna go in and create our content that is actually going to vary. So in this case, it's just basically going to be our headline and our content. And I can quickly duplicate these. So we'll go to page variations.\u003C\u002Fp>\u003Cp>I'm gonna say copy headline. We're gonna copy the content. Just make sure you change that copy that we prepend at the end so that you don't, you know, get confused. But with this information set up, now I can get an interface that looks like this where we can say, hey. Great.\u003C\u002Fp>\u003Cp>Now we've got developers. We've got content editors. Cool. And I can be able to store different contexts here. Context switching sucks.\u003C\u002Fp>\u003Cp>Great. That's what we'll change the default headline to. We're gonna change this for devs, Context switching sucks for content editors. This content is for editors. This content is for devs.\u003C\u002Fp>\u003Cp>Alright. So now thinking through our mental model, we've got our default content. And then if we identify a segment, like if the visitor falls into the content editor segment or they fall into the developer segment, we're going to show them different content. Now, what is this actually going to look like? So if I go and like we look at the API now, got my API request, you could see those variations.\u003C\u002Fp>\u003Cp>One of the beauty, beautiful things of Directus is the ability to use the rest API in a GraphQL like manner. So I could do something like this where I just say, hey. Give me all of the root level fields and also give me all the fields within our variations. So now I can see here's my variations. The segment here is developers.\u003C\u002Fp>\u003Cp>The, segment here is content editors and presumably on the Nuxt side of it. And, you know, I could rig something up via the API to do this swap for me, but depending on how I'm rendering my site, you know, let's say I'm statically generating the site, if I want to still offer this, I've got to pre generate the payload, you know, pre generate our JSON and store that so I can use it when rendering it. So we're gonna try to do this on the client side. Inside our page level data here, right, we're still getting the same result. And if I add the data back, you know, now we should be seeing our variations, but hey, lo and behold we're not because again we need to go back into our access policies and set that up.\u003C\u002Fp>\u003Cp>So we're going to allow read access for our page variations and for our segments just so we could see those things. You know, when we're going to production, we'd probably make sure there's some kind of status on these so that, you know we're only showing published pages etcetera, but outside the scope. So now I can see my variations. We're gonna go here and let's just specify our fields, right. It's not content, it is variations of this content.\u003C\u002Fp>\u003Cp>And now we could see that. Great. Alright. So now we need to translate this content or personalize it in this case. Right?\u003C\u002Fp>\u003Cp>How do we assign a segment? You know, like, writing a rules engine here would be, kinda challenging with the twenty seven minutes we have left, I would say. But, you know, at the core, we we've gotta have some way to assign the segment. What I'm going to do I I think we're just gonna use a cookie, for this. So Nuxt has a use cookie composable.\u003C\u002Fp>\u003Cp>Let's just call this the segment key. We're gonna use cookie or you call that segment. Great. And I'm just gonna make this stupid simple. We'll add a div.\u003C\u002Fp>\u003Cp>We'll flex these, add some gap, make sure that's in the middle, etcetera. And then we're gonna add some buttons. I've got the Nuxt UI library in here. Segment key equals developers. No.\u003C\u002Fp>\u003Cp>We're not gonna do designers, but, we're gonna do content editors. Content editor. And then maybe we just add I am a great. Alright. Maybe we wrap this whole thing in.\u003C\u002Fp>\u003Cp>I think it's a u container from the Nuxt UI library. Okay. Yep. So we get a little bit of spacing there, And we can add, just some space between these space y eight. Okay.\u003C\u002Fp>\u003Cp>Great. And this is probably all part of the same section. Okay. So I am a developer. I am a content editor.\u003C\u002Fp>\u003Cp>I am a developer's. Great. Again, naming is crucial here. But basically, like, what I should be able to see now if I go into, like, my cookie storage, I can see that segment as well. Right?\u003C\u002Fp>\u003Cp>So based on this, I'm tagging that person. You know, and we're just explicitly asking. But, you know, we could set up, like, some kind of client side rule engine or, you know, you could potentially do this on the server side as well. Whatever pages you visit, weight those against each other and assign a, you know, if I've got five articles for developers and you look at three of those, then I'm probably safe to say that you're a developer, versus, you know, reading the content editor pages. Alright.\u003C\u002Fp>\u003Cp>So how do we actually translate this? Right? We probably want, like, a helper function that takes our page data and then evaluates the segment and then, you know, spits out the proper content. So let's just see how awesome AI has become here and see if this is actually worthwhile or I should just probably write it out. But, this wouldn't be fun if we weren't using something like cursor and, you know, something super opinionated or where everybody has an opinion about AI at this moment.\u003C\u002Fp>\u003Cp>Here we go. Write a let's just describe this. Right? Personalize the page content based on the segment key. Where is this gonna come up with?\u003C\u002Fp>\u003Cp>Personalize content, page value dot variations. I okay. You know, maybe this would come back. Let's see. Helper function to get the personalized content.\u003C\u002Fp>\u003Cp>Page variations return page dot that's not really content. Right? We just wanna return yeah. Okay. So I'm probably not prompting right.\u003C\u002Fp>\u003Cp>Let's see what we can do. Take in the page and the segment key and return a merged page with the proper variation based on the segment key. That's better, still not great. Right? You would probably want, like, a personalized function where it, just basically takes in, the page content and the different segments and the current segment and returns that.\u003C\u002Fp>\u003Cp>But nevertheless we got our personalized page or we should, right. Great. We can see our variations, we can see the content. If I check-in view dev tools, we go to our slug page. Do we have the personalized page?\u003C\u002Fp>\u003Cp>Great. That doesn't really Context switching sucks. No. But that doesn't look right to me. Right?\u003C\u002Fp>\u003Cp>So we do personalized page dot headline, personalized page dot content. Context switching sucks. I am currently in the developer side of things, so it should be showing should be toggling this. Right? So there's something wrong here.\u003C\u002Fp>\u003Cp>Page dot value dot variation. Yeah. So, basically, what we wanna do is loop through the trying to to do this the AI way and not get frustrated. Loop through the page variations and merge them with the page based on the segment key. Still showing the same thing.\u003C\u002Fp>\u003Cp>Personalized page. Yeah. Okay. Yeah. Alright.\u003C\u002Fp>\u003Cp>So this is the final page. Let's define that. The final page. Okay. Then we are going to is this getting smarter now?\u003C\u002Fp>\u003Cp>For constant of variations, if segment dot key, final page, return final page. What does that do? Why is it why is it still not doing what we want? Page.valuepage.value.variations.developers. Oh, yeah.\u003C\u002Fp>\u003Cp>That's why. Basically, we need to use, like, a file. Constant variations equals variations. Selected Constant variation is going to be we basically need to look through this array. Variations dot find.\u003C\u002Fp>\u003Cp>We'll look for the variation. And there we go. Okay. Yeah. So we're getting that array.\u003C\u002Fp>\u003Cp>That's the problem. So now if I remove this blah blah blah page, Now I have personalized content for folks. Amazing. This is the personalized content engine, website personalization engine. Badabing badaboom.\u003C\u002Fp>\u003Cp>This is beautiful. Right? So now I can define all of these variations within our actual CMS. Like here's the segments that we want to create content for. You know, I could potentially set up some type of rules engine for this.\u003C\u002Fp>\u003Cp>And then, you know, on our individual pages, then I can go through and define this content. Now is this a real world scenario? Probably not. You would take this and use something like our mini to any builder where you're building these dynamic pages and within each section you have personalized content for that. But at the very least, hey, this is now we've got, a pretty robust I won't say robust.\u003C\u002Fp>\u003Cp>This is not robust at all. But it is a a pretty it's a start to a personalization engine basically. That's it. That's all. Now where do we go from here?\u003C\u002Fp>\u003Cp>Right? I this would be extracted out into a, like, a helper function, so that, like, each page or each block, we would we would call this function, return the content if there is a variation, like, if we have a segment key. Yeah. And we could even go as far as, like, creating a rule engine for this. So let's just continue to beat on AI, which is, honestly not been great so far for this.\u003C\u002Fp>\u003Cp>Let's say extract this out to out to a helper function that is more robust, maybe. I don't know. Let's see. I'm gonna take a sip of coffee while it's trying to choke that down. The auto completions are not great, but here we go.\u003C\u002Fp>\u003Cp>Let's see what it's come up with. Personalization. This looks to be a composable. Use personalized page. Here's the page.\u003C\u002Fp>\u003Cp>Segment key. Update the page component. Here's how we're gonna use that. Personalize page. Use a page.\u003C\u002Fp>\u003Cp>There is a segment key. Okay. AI. I will bite. Alright.\u003C\u002Fp>\u003Cp>App utils. Personalization. And I don't know why it's using the is there let's see, like, an actual it's using the composable format for view, but import type maybe rest. Return computed. I mean, to me, this is a I guess this is a this is a composable.\u003C\u002Fp>\u003Cp>I'm not sure why it's doing that. So we'll stick that there. This is use personalization. This is the name of it. Use personalization.\u003C\u002Fp>\u003Cp>Again, probably not a strong choice for, like a composable here because this is probably something that you might want to extract out and use, like, across different projects. Use personalization. Use personalization. Okay. Cursor.\u003C\u002Fp>\u003Cp>Okay. Now that you use this helper, can we have it apply this automatically? Yes. But knowing Nuxt, we should automatically use this. Use personalization.\u003C\u002Fp>\u003Cp>There's our page content. There's the segment key. Does this still get us what we want? So far, looks like it does. Right?\u003C\u002Fp>\u003Cp>And the nice thing here is because we're using a cookie, and even though, like, this would be server side rendered, I'm still getting the content that we want. Right? Because it's automatically running a this cookie first before we actually display the content. Cool. Now let's do we have time?\u003C\u002Fp>\u003Cp>What kind of time do we have here? We got fifteen minutes left. Where do we take this further? Right? Where would we go from here?\u003C\u002Fp>\u003Cp>How do we set this up into, like, something like blocks, right, where we want to build a page out of the blocks? So for that, we can use the mini to any builder inside Directus. We've got this mini to any builder. We're gonna call this blocks. We don't have any related items yet.\u003C\u002Fp>\u003Cp>So first thing we're gonna do here is create a hero block. Call it block hero because I like to be obtuse. Alright. The hero is gonna have a headline. It's gonna have a description.\u003C\u002Fp>\u003Cp>Great. Okay. And then we have a block CTA. Cool. And that is going to have a call out and a button text.\u003C\u002Fp>\u003Cp>Great. Okay. Now we're gonna put those together with the many to any builder. These are gonna be our page blocks. We're gonna say block CTA, block hero.\u003C\u002Fp>\u003Cp>Great. Basically, this many to any relationship, it it doesn't necessarily exist, inside, like, standard SQL. Right? So Directus is doing a bit of API magic here. Basically, what what's happening, and we'll just hide, like, what we've got already.\u003C\u002Fp>\u003Cp>Alright. Hide this field. Don't destroy. Just hide. So we can see what's happening.\u003C\u002Fp>\u003Cp>We've created these separate collections, and then there's a junction collection here that is storing the page's ID and then we're storing the ID for the item. And then we also have a string for the collection that we're pulling this from. So it's, you know, basically some API magic to string these things together. But now if I go in and I update the page, we could see we've got a CTA that we'd say, this is you should try Directus. Do it now.\u003C\u002Fp>\u003Cp>Great. There's our CTA. There's our hero. Brian is cool. Well, he's not.\u003C\u002Fp>\u003Cp>There's our description. Alright. And let's hit save and stay. Let's just look at our page content now that we're getting back. Right.\u003C\u002Fp>\u003Cp>We're showing two different blocks here, but again, we need to go in and set our permissions. Directus keeps you secure by default. So we're gonna add just read permissions for all this. Again, for production, we wouldn't do this, but can we get to personalized blocks in twelve minutes or less? I need to get my ass in gear.\u003C\u002Fp>\u003Cp>Alright. Alright so we're gonna go back to our slug here. We're gonna change this up where we have our blocks. And actually we could use like a object syntax as well. Blocks, we're going to have here.\u003C\u002Fp>\u003Cp>We're going to get the we could get that. And then we're probably also going to have, like, variations within the blocks themselves. Well, this is getting messy. Right? Okay.\u003C\u002Fp>\u003Cp>So now let's just go back to the page. Variations blocks. I don't wanna do variations. We wanna do blocks. Alright.\u003C\u002Fp>\u003Cp>I'm gonna pull this back in. For our page render, we're just gonna have to pull that out. And now we could see we got, like, some data for each block. Right? Blocks, items, we're gonna wanna grab the item for each block.\u003C\u002Fp>\u003Cp>I don't need it. Fields comma. What do we got here? Okay. Well, yes, no, maybe so.\u003C\u002Fp>\u003Cp>Something is off. We got one too many. How nested did we go here? This one needs okay. Did we get it right this time?\u003C\u002Fp>\u003Cp>We still did not get this right. Direct as requests. AI is totally screwing this up, and I've had too much coffee to fix this properly. Alright. So we got the block.\u003C\u002Fp>\u003Cp>We're within the block, we're going to have, the items items, and then we're gonna grab the variations. Where okay. Backup. Backup. Backup.\u003C\u002Fp>\u003Cp>Love all the formatting. Items. Oh, yeah. This is not going well for me, is it? Fields.\u003C\u002Fp>\u003Cp>We're gonna grab the collection field. Great. Okay. Now, we got item. If I change this to item, we should see the item text.\u003C\u002Fp>\u003Cp>Great. And then also we're gonna have variations on that same item. Cool. Alright. But we don't have those yet.\u003C\u002Fp>\u003Cp>Alright. So let's go in and add those. So now the same way that I created that translation function on, let's say, the page level, we're gonna go through and we're gonna do this speed run style in less than eight minutes. We're gonna call this variations. Great.\u003C\u002Fp>\u003Cp>This is gonna be segments. This is the collection there. And what are we gonna call this? We're gonna call this the segments. Block hero ID, block hero variations.\u003C\u002Fp>\u003Cp>Great. Primary key is gonna be the or the primary indicator is gonna be that. We'll set this up. Alright, so now on our block hero I should be able to set up like a variation here except I don't have that content yet. So we'll go to our block CTA.\u003C\u002Fp>\u003Cp>Oh, no. Block hero. We're gonna copy that headline and description down into the next collection, right, to for our variations so that we can have that specific content. Block hero variations, duplicate. Great.\u003C\u002Fp>\u003Cp>Now, I go in, we've got our block hero I'm gonna stick up at the top. We have a headline for Brian's cool to editors. Brian is cool to developers. Great. Okay.\u003C\u002Fp>\u003Cp>And we're gonna create a hero component. View, create a hero component with Tailwind that has two props, headline and description. Let's see what it comes up with. Outsourcing the work here. Six minutes.\u003C\u002Fp>\u003Cp>You know, we could declare this one good, but, yeah, let's make it fun. Alright. We got the hero component. Okay. So now within the like this page section, I guess, Let's go and call that hero component with the page headline and description.\u003C\u002Fp>\u003Cp>Context switching sucks. Do we have a oh, no. We don't have that at the page level. Right? We're gonna loop through all of our blocks.\u003C\u002Fp>\u003Cp>So the, let's do template v four page blocks within the blocks. If the block collection equals the hero to block hero, basically, block underscore hero, that's the name of our collection, we're going to render that out. But now we also need to use the personalized page, page dot blocks, personalized page dot blocks. But low and behold, we're still not changing that content based on that because our personalization is what? At the page level.\u003C\u002Fp>\u003Cp>Right? So AI, help us in our moment of need. Five minutes left. Change this to well, accept a what a to accept content and recursively merge if there is a variations key within The array within the objects. There's a variations key.\u003C\u002Fp>\u003Cp>Date. And task. Objects. Let's see what this comes with back. What if just curious.\u003C\u002Fp>\u003Cp>Alright. Merge variations, current segment, matching variations. Is this to me, like, obviously highlights the dangers of AI? I am on a crunch here. Right?\u003C\u002Fp>\u003Cp>I don't have a clue. I don't have enough time to, like, figure out if this is actually going to do what I wanted to do anyway. So, you know, great that you can I I mean, I love the fact that I can just, like, quickly POC this, but, you know, it's like a what are we coming back with here? Personalized page. Let's just see what it comes back with.\u003C\u002Fp>\u003Cp>Blocks zero. So, again, like, is yeah. See, that's goofed up. Oh, and I'm not even giving the personalization content, though, am I? Why is that?\u003C\u002Fp>\u003Cp>I should be fetching it here. Items, personalizations. We gotta go back into our permissions, block hero variations, access. Save. Are we now at least getting that?\u003C\u002Fp>\u003Cp>Yes. Okay. So AI did save the day here, basically. You know, before I shipped any of this, I would need to go through and, like, study this in detail to figure out is this actually doing doing right. It appears to be doing right, but are there gonna be, like, dangerous side effects?\u003C\u002Fp>\u003Cp>But here I could see Brian is cool to developers. You know, maybe we go back in and within this, do we have a does don't like him? Alright. No. He's not.\u003C\u002Fp>\u003Cp>Neither do content editors. So there is that. You know, the one thing I noticed is we should have, like, a fallback and it should take that fallback into account. So it's not doing, like, a deep merge, but now we could see that content. We could clean this up just to show it out.\u003C\u002Fp>\u003Cp>And boom. So now we've got, like, page block level data where I could go through and build a page and have segments and personalize that data, for each individual segment. So I'm I'm gonna call that a win. Right? We got one minute fifty six seconds left.\u003C\u002Fp>\u003Cp>Would I have been able to achieve this without cursor? %. Would it have taken me more than an hour? One hundred percent. Is this still cool and fancy?\u003C\u002Fp>\u003Cp>Probably not. This is the start to something incredibly cool where, you know, I could go in and on the client side have some type of tracking and some type of rule engine that just, like, consistently con like, creates these different segments or groups of visitor into segments. And if you keep that on the client side, it could be privacy friendly as well. But I hope this was an interesting episode, an exciting episode for you. I've had a good time.\u003C\u002Fp>\u003Cp>I'm probably gonna figure out a way for us to leverage this somehow in the future. That's it for this episode of 100 apps, one hundred hours. Thanks for joining me. I do roll like the success message. See you guys next time.\u003C\u002Fp>","Welcome back to another episode of 100 apps, one hundred hours. I'm your host, Brian Gillespie, here from Directus. And today should be well, I think it's gonna be an exciting show. My confidence level on this one, just to put that out there, is fairly lower than normal. But today, we are going to be building a website personalization engine. I went through several different titles for this one. I don't know if that's actually gonna stick when this goes on Directus TV or not. But, what are we trying to do? Well, first, let's explain the rules. If you're new to 100 apps, one hundred hours, the rules are simple. Basically, we have sixty minutes to plan and build a clone, an application, a website. So that is plan and build, nothing more, nothing less. And then the only other rule, which is the I call it the anti rule, is use whatever you have at your disposal, which I will probably be leveraging, some AI today because I am wading into uncharted territory for me. So website personalization engine, sixty minutes. Let's do this together. Or it'll, like, publicly laugh at Bryant. Try. Alright. So we're gonna put sixty minutes on the clock, and let's cover this. Alright. So website personalization, you know, originally, this idea was for, like, personalized landing pages. Like, I know a specific company or target account that I want. Let's create a specific landing page for them. But in the light research that I did before this, which is basically, like, ten minutes of Google, basically, when it comes to personalization, it's more of a segment based approach. So that's what we're gonna sketch out here. You know, we wanna bucket visitors into a segment and then display certain content for that spec segment to personalize it. So, let's plan out our kinda data model. And, you know, I could leverage, like, some of our direct to CMS starters here. We're just gonna keep this stupid simple super simple. I tell my kids not to say stupid, so I probably shouldn't say that either. But alright. So as far as our data model, let's flesh out the application. Maybe we do just wanna, like, flesh out what are the actual features that we want out of this specific application and make that less huge. Alright. So we want to have a piece of content. Great. Directus will manage that for us pretty easily. And then we want to show variations of that content based on traits of a visitor or segments segments that a visitor belongs to. Now, something like this or or even when you get into, like, AB testing, you know, it's fairly easy. I won't well, I won't say fairly easy, but it's easy enough to set up, like, an AB testing code. If this, do that. If this if else, do that. But, like, the intersection of, like, handing this off to a content or a marketing team and setting it up in code and, like, managing all of that to work together for front end, back end, and making it cohesive, really difficult task. Right? So this is really all I'm trying to achieve today. We could make this fancier maybe down the the line, but let's let's discuss the data model for this. What are we gonna set up? So I could just call this content. Maybe it's, let's just have a pages collection. You know, this could be like a post or something like that. And then we're going to have what else? We're probably gonna have some segments. Like, here's the different segments that we want. And, you know, if you were doing, like, a proper system, you'd probably want something like events. Not super concerned with that. You know, the segments probably have, like, some rules for each segment or yeah. What are the rules? This sort of thing. I don't even know if we'll get this far. Let's just try to wade through this and and show something on the page. Alright. So let's get into what I've got set up. This is, just a Nuxt application. This is my standard starter for, you know, like, if I was just YOLO ing into a hackathon, this would be my starter. You could see a bunch of commented out code. But basically, I've got a blank Directus instance, if I can actually get logged into this bad boy. Directus is gonna be our CMS. It's gonna handle all of the back end for us. Dear lord. Why can I not get logged into this thing? Boy. Boy, oh, boy. Admin example. Password is the password. It's not even up, but that would be why that would be why I couldn't get into the application. Alright. So I've got Directus set up, in a Docker Compose, just a standard local setup kind of format. And like I said, Directus is gonna be our back end, our CMS. Okay. Great. Blank slate. Nothing fancy here. We do have templates available for stuff like CMS if if that's something that you're into. And then we have a Nuxt website. Alright. So let's start building some functionality. And inside the Nuxt website, we've got, like, an index page. So that's what we're staring at here. Alright, so let's add some pages content. Great. Go in, we'll create a new collection inside pages. For those of you who are new to Directus, what's happening behind the scenes here is Directus is connected to my Postgres database and it is basically gonna mirror whatever changes I make here to that SQL database. And I could connect like a an existing SQL database to this as well. So we have some pages. Great. Got a collection that's gonna show up here. When I hit new, there's nothing shows up. So we need to add some fields to our pages. And again, like, we're modeling our data here. We're generating an API and we're building a form for our content editors all in one place. So page needs a title. Great. A page needs a slug. So we're just gonna create a new field called slug. I'm gonna go into the interface settings and make sure this is URL safe. Do I have my little pointer? Yeah. I do. That's a cool little tool. Occasionally, I get questions on it. It is called mouse pose, Mac only, I think. Alright. So what else are we gonna have on our pages? We got a title. We got a slug. Maybe we'll keep this simple. Right? We've got a headline. That could be some text. Cool. And because I am totally gonna make it easy on myself, we're just gonna call this content. One big rich HTML rich text content block. This probably looks more akin to, like, a blog post than a page. Directus does have that amazing many to any like page builder scenario that's covered in some of our templates but we gotta crawl before we can walk. Alright so we got our pages, lets go ahead and let's just create a page. Right? This is Brian's page. Brian's page. Cool. Hey, yo. Why am I talking to myself? Great. Alright. So we got some content. Now if I want to query that via the API, I can go to the directus URL, go to items, pages, and boom. I could see I have a page here. There's my content. API is ready to go. This is already I've I've got the Nuxt application wired up with, like, a simple Directus SDK client. So we're creating Directus, we're using REST, and then we're providing that to the Nuxt application. So So if I go in and I do something like this, where we're just gonna create a new dynamic route inside Nuxt and do I have, like, a v t s? Yep. Script set up. Really need to work on my snippets to save time here. And my script at the top guy, when I'm working with Nuxt review. Alright. So now we want to fetch some of this page content. Right? Because what happens now, I've got this. If I go to Bryant's page, nothing shows. Yeah. That sucks. We need to resolve that. Alright. So what we're gonna do, we will do const can't type today. You'll notice I am using cursor here. Directus equals use Nuxt app. And, okay, we've got some auto completions that are not necessarily gonna give us what we want. Alright, so we're gonna get the data. Nuxt has the use async data function or the composable that we're going to use. If I can actually type that, great. We're gonna give this a key. So, the key, if we grab the routes, is going to be the slug, route. Params. Slug page. Let's just tag this as a literal. There we go. Thank you. Alright. And then we are going to return directus dot request, we're gonna do read items pages. Pages. K. And we're gonna filter where the slug, that's right, is equal to route dot params dot slug. K. So we have direct us there. We're gonna import read items from our SDK. You know, I could potentially, like, import that here and provide that as well if I wanted to, but we'll just do it this way, keep it nice and easy. And then let's see if we can just actually get the data. It'd been a minute since I messed with this. Is this actually going to work? Are we making a network call? Oh, wait. Use async data. There's that. There's the call that we're returning. Read items pages. Let's check our network requests. Are we seeing any actual network requests? It is server rendering these. So we are not seeing that. Probably want to add some error handling. So we'll just add that. If there's an error dot value, we are going to console dot error, error dot value. Great. Do I have any formatting set on this? Got a little bit of formatting. Alright. Can we see the error? H three error create error. What is going on there? There's an error. Error dot value dot message. What's the format for this? Create error. We're throwing an error. Hey. I'm guessing though, just from past experience, this is because we have not enabled any access to that page's collection. Right? So when I do this, I am still using the session token from Directus. But if I open this up in a new browser, we could see, hey. I don't have any permissions. So I'm just gonna enable permissions for pages. Let's do read permissions, see what we got. Boom. We can actually get some data and wrap this in a nice pre tag to see what we've got. Okay. Cool. Now let's take this data that we have and we'll add an h one. This will be the page dot headline. And then we are going to check and see what all I do have in this application. I don't have, like, a pros component. So we'll just do something like this where we have VHtml content. Now if I refresh if I refresh if I refresh, why is it that actually showing? Where's my content? Page dot headline. How is it that I could see the page content? Oh, I do have it here. Page dot headline. Why isn't that actually showing on the page, though? Interesting question. Interesting. Definitely. Let's just center this up. Maybe let's max auto, max width, four x up. Add some padding. Why isn't my content displaying via page? Do we need to wait on the page? Am I really not smart enough to figure this part of it out? Is there some type of error that we're getting paged out headline? I could clearly see oh, yes. That's why, Brian. You are getting an array here, so we need to transform that. So async data. I think it's here that we can do a transform. Data. Data. Just the first item. There we go. Alright. There we go. Hey, yo. Why am I talking to myself? Make sure that you understand what your data looks like before you start trying to mess around with it. We're gonna make that giant. And if we slap a pros tag onto this, pros l g. I think I've got Telen typography included here so we could see this data. Okay. So we're rendering something out to the page. Great. Now, this is super fancy. Right? Let's just grab something from the Directus blog to use as content. Here's a post that I wrote. I'm just gonna copy this entire thing. We're gonna go in and throw that here. Great. Just to get something on the page. Okay. We'll call this context switching sucks for devs. Cool. Alright. So now we are showing a basic thing on the page. Hallelujah this is amazing this is the coolest thing we've ever built. How are we doing on time? We got about forty five minutes left. All right so we have a piece of content now we want to show variations of that content for an actual visitor. So how are we going to achieve this sort of thing? And, you know, if we boil this down to the basics, we've got a page. We're going to have variations of page content. Yeah. We could call this personalizations or page variations. That's gonna be linked to our segments, and this is not right. I do have a PhD in drawing arrows, just not in Figma. This is still a challenge for me. Alright. So let's create this and I think what we can do is hijack some of the functionality that already exists in Directus for this sort of thing. Going off label here, off brand. So, we will go into our data model. What I'm gonna do, I'm just gonna create a page variations. Yeah. Naming stuff is probably the hardest challenge in development. You know, we could call this personalizations, but that might make somebody mad. Yeah. I'll just do created at, created by. I don't necessarily need this information. These are just helpers that you can add. Directus, like, prefix or, like, sets these up for you. So whenever, you save this item or create a new item, it obviously records the user, etcetera. Just some shortcuts. Alright. So we have page variations. We've got two different collections here. Great. How are we gonna tie these things together? Now, what I can do and what I'm gonna steal is our translations feature. So if we go to the docs actually, let's go to the new docs. These things are in beta as of now. We're gonna look for translations. Great. We have the ability to translate content and manage all those translations for you. And if I open this in a new tab, we get, like, this beautiful side by side interface where I could see, okay, here's English versus Spanish or French. And this is like, when we boil it down, like, when I think about it, I'm just now coming to me through my head here, is it basically, translation is a form of personalization anyway. So why not leverage this existing structure? You know, short of, like, the icons and maybe some of the other stuff that is baked in there, I I think it should work. So I'm also going to like, normally, if we were doing translations, you would create, like, a languages collection where you would store all the languages that you wanna translate your content into. In this case, what we're gonna do is just use, I'm gonna create a new collection. I'm gonna call it segments. You know, we could manually generate the IDs for these or we could use, like, a generated UUID. You know, if I was trying to scale this out, I would definitely be using, like, generated UUIDs, but let's just call this key we're gonna manually enter a string for this. So whenever we create a new segment, we're gonna manually enter in the key. That'll help us keep things a little bit clearer. Updated at updated by just to keep track. And do we want this segment to be active or not? You know, maybe we set that up on a a Boolean toggle. Alright. So we got a key for it. Maybe we want, like, a proper name. So we add a name field and drag this down. And what I'm doing here is basically just configuring the actual form that we're gonna use to set this stuff up. Do we have a segment? Do we have a category? Yep. There we go. Yeah. Again, naming things is hard. Let's add some icons. Just the designer OCD for me kicking in. Doc. Yep. There we go. Edit document. And I'm just gonna like actually hide these page variations. So now we have pages, we have segments. Let's create a new segment. Right, I want to speak to developers. We could say these are for developers, that's our nice name. Directus is also helpful for content editors, so I'll do content editors. Yeah. I wish I was doing this live so I could say, hey, hyphenate or underscore, but let's just go with a hyphen. Fine. Fine. Fine. You know, I could keep adding these as much as I want. Right? So now we've got some segments, we've got some pages. We wanna generate these variations. So again, we're gonna reach for the Directus translations interface. And I hope this works out like I think it will or like I hope it will. You can see that we're pre setting the languages collection. Directus is smart enough to know, like, when you're doing translations, you need languages, we can create that for you. But what I'm gonna do here, I'm gonna open up advanced field mode instead, and I'm gonna go in and I'm just gonna do all of this mapping myself. So we try to be smart and create junction collections and everything for you, but you can take total control of this as well. So here, this is the collection. So we're gonna call this page variations. I'm just gonna use page and we're gonna call this the segment and the collection that we're targeting is segments. So, I'm gonna set these all up to cascade, so if we delete a page variation or deselect one, we delete that content because we're not going to use these variations across different collections or pages. Alright, so the language indicator field. Now normally this is what shows here right up here. Right? Just the indicator of what this is. We could use the key. We could use the name. Let's use the name. Language direction. We're gonna ignore that. Default language is the primary key. Let's let's say I wanna default to developers. And we're gonna start this split. So we'll start open like this. Great. Okay. So now we create this, and we could show related values. You know, you can even preview the translation content. And I already see one thing that's going to bug the crap out of me as I called it translations. I forgot to rename this. Alright. So we're gonna call this variations. So it's not translations, it's variations. We're just piggybacking off of functionality. Alright. Pages. Cool. This is gonna be the what? Key? No. Segment. We're gonna call that segment. That's the segment. The collection is segment. And I gotta set up this nice cascade again. Okay. Go through the exact same process all over again. Love shooting myself in the foot Just to make sure we have variations. Is variations spelled correctly? At this point, we don't know. Alright. So I probably goofed something up already. Did I? Did I not? Page variations. Page variations. Page page is not found. Oh. Yep. Alright. So love it when I goof up. Cause more work for myself. We're just gonna start this process all over. Alright. Try it one more time. Page, variations, Generate the UID. I'm not even gonna go for that. At this point, we're gonna say translations interface. Great. This is gonna be segments. We're going to use the segment name. We're gonna start with the split open view. And let's control this. Right? Page variations. This is gonna be page. Segment. Delete. Alright. So definitely don't do what I have done. Let's try this. See if that works. Doesn't have field page and doesn't have a relationship. And what in the world field page in collections doesn't have a relationship? Why doesn't it it should have a relationship. Pages, segments, translations, start open. Why doesn't this have a relationship? Well, let's just see what happens when we do this in orally. Translations already has a relationship. Alright. So we can see here by adding this we get a languages and then we get a pages translations, there's a pages ID, there's an ID. This should work out okay. I don't know what is going on. Maybe I didn't refresh the data model. Key name, segments, page variations. Why am I not getting this to work correctly? Let's just take a look at our database and see what's happening. 53Edit. 5 3 2, test, Connect. Alright. So now we're behind the scenes into the database trying to figure out what I goofed up to begin with and it looks like there's some relationships here that got left over that didn't get fixed that are causing problems. Alright. So now with that out of the way, let's try this again. Page variations, we're gonna generate an ID for those. I'm just gonna skip all the fanciness. And now I'm gonna go in and do translations. We're gonna call this variations. Instead of languages we're going to use segments, we're going to use the name of that. We are then going to set up to use page variations, page segments, segment. Okay. Now is that going to work out how we want fingers crossed. Let's log in and see what have we got. Okay. So now I get this side by side interface of content editors and developers. Right? But I don't see anything inside the actual form. So inside this junction table is where we're gonna go in and create our content that is actually going to vary. So in this case, it's just basically going to be our headline and our content. And I can quickly duplicate these. So we'll go to page variations. I'm gonna say copy headline. We're gonna copy the content. Just make sure you change that copy that we prepend at the end so that you don't, you know, get confused. But with this information set up, now I can get an interface that looks like this where we can say, hey. Great. Now we've got developers. We've got content editors. Cool. And I can be able to store different contexts here. Context switching sucks. Great. That's what we'll change the default headline to. We're gonna change this for devs, Context switching sucks for content editors. This content is for editors. This content is for devs. Alright. So now thinking through our mental model, we've got our default content. And then if we identify a segment, like if the visitor falls into the content editor segment or they fall into the developer segment, we're going to show them different content. Now, what is this actually going to look like? So if I go and like we look at the API now, got my API request, you could see those variations. One of the beauty, beautiful things of Directus is the ability to use the rest API in a GraphQL like manner. So I could do something like this where I just say, hey. Give me all of the root level fields and also give me all the fields within our variations. So now I can see here's my variations. The segment here is developers. The, segment here is content editors and presumably on the Nuxt side of it. And, you know, I could rig something up via the API to do this swap for me, but depending on how I'm rendering my site, you know, let's say I'm statically generating the site, if I want to still offer this, I've got to pre generate the payload, you know, pre generate our JSON and store that so I can use it when rendering it. So we're gonna try to do this on the client side. Inside our page level data here, right, we're still getting the same result. And if I add the data back, you know, now we should be seeing our variations, but hey, lo and behold we're not because again we need to go back into our access policies and set that up. So we're going to allow read access for our page variations and for our segments just so we could see those things. You know, when we're going to production, we'd probably make sure there's some kind of status on these so that, you know we're only showing published pages etcetera, but outside the scope. So now I can see my variations. We're gonna go here and let's just specify our fields, right. It's not content, it is variations of this content. And now we could see that. Great. Alright. So now we need to translate this content or personalize it in this case. Right? How do we assign a segment? You know, like, writing a rules engine here would be, kinda challenging with the twenty seven minutes we have left, I would say. But, you know, at the core, we we've gotta have some way to assign the segment. What I'm going to do I I think we're just gonna use a cookie, for this. So Nuxt has a use cookie composable. Let's just call this the segment key. We're gonna use cookie or you call that segment. Great. And I'm just gonna make this stupid simple. We'll add a div. We'll flex these, add some gap, make sure that's in the middle, etcetera. And then we're gonna add some buttons. I've got the Nuxt UI library in here. Segment key equals developers. No. We're not gonna do designers, but, we're gonna do content editors. Content editor. And then maybe we just add I am a great. Alright. Maybe we wrap this whole thing in. I think it's a u container from the Nuxt UI library. Okay. Yep. So we get a little bit of spacing there, And we can add, just some space between these space y eight. Okay. Great. And this is probably all part of the same section. Okay. So I am a developer. I am a content editor. I am a developer's. Great. Again, naming is crucial here. But basically, like, what I should be able to see now if I go into, like, my cookie storage, I can see that segment as well. Right? So based on this, I'm tagging that person. You know, and we're just explicitly asking. But, you know, we could set up, like, some kind of client side rule engine or, you know, you could potentially do this on the server side as well. Whatever pages you visit, weight those against each other and assign a, you know, if I've got five articles for developers and you look at three of those, then I'm probably safe to say that you're a developer, versus, you know, reading the content editor pages. Alright. So how do we actually translate this? Right? We probably want, like, a helper function that takes our page data and then evaluates the segment and then, you know, spits out the proper content. So let's just see how awesome AI has become here and see if this is actually worthwhile or I should just probably write it out. But, this wouldn't be fun if we weren't using something like cursor and, you know, something super opinionated or where everybody has an opinion about AI at this moment. Here we go. Write a let's just describe this. Right? Personalize the page content based on the segment key. Where is this gonna come up with? Personalize content, page value dot variations. I okay. You know, maybe this would come back. Let's see. Helper function to get the personalized content. Page variations return page dot that's not really content. Right? We just wanna return yeah. Okay. So I'm probably not prompting right. Let's see what we can do. Take in the page and the segment key and return a merged page with the proper variation based on the segment key. That's better, still not great. Right? You would probably want, like, a personalized function where it, just basically takes in, the page content and the different segments and the current segment and returns that. But nevertheless we got our personalized page or we should, right. Great. We can see our variations, we can see the content. If I check-in view dev tools, we go to our slug page. Do we have the personalized page? Great. That doesn't really Context switching sucks. No. But that doesn't look right to me. Right? So we do personalized page dot headline, personalized page dot content. Context switching sucks. I am currently in the developer side of things, so it should be showing should be toggling this. Right? So there's something wrong here. Page dot value dot variation. Yeah. So, basically, what we wanna do is loop through the trying to to do this the AI way and not get frustrated. Loop through the page variations and merge them with the page based on the segment key. Still showing the same thing. Personalized page. Yeah. Okay. Yeah. Alright. So this is the final page. Let's define that. The final page. Okay. Then we are going to is this getting smarter now? For constant of variations, if segment dot key, final page, return final page. What does that do? Why is it why is it still not doing what we want? Page.valuepage.value.variations.developers. Oh, yeah. That's why. Basically, we need to use, like, a file. Constant variations equals variations. Selected Constant variation is going to be we basically need to look through this array. Variations dot find. We'll look for the variation. And there we go. Okay. Yeah. So we're getting that array. That's the problem. So now if I remove this blah blah blah page, Now I have personalized content for folks. Amazing. This is the personalized content engine, website personalization engine. Badabing badaboom. This is beautiful. Right? So now I can define all of these variations within our actual CMS. Like here's the segments that we want to create content for. You know, I could potentially set up some type of rules engine for this. And then, you know, on our individual pages, then I can go through and define this content. Now is this a real world scenario? Probably not. You would take this and use something like our mini to any builder where you're building these dynamic pages and within each section you have personalized content for that. But at the very least, hey, this is now we've got, a pretty robust I won't say robust. This is not robust at all. But it is a a pretty it's a start to a personalization engine basically. That's it. That's all. Now where do we go from here? Right? I this would be extracted out into a, like, a helper function, so that, like, each page or each block, we would we would call this function, return the content if there is a variation, like, if we have a segment key. Yeah. And we could even go as far as, like, creating a rule engine for this. So let's just continue to beat on AI, which is, honestly not been great so far for this. Let's say extract this out to out to a helper function that is more robust, maybe. I don't know. Let's see. I'm gonna take a sip of coffee while it's trying to choke that down. The auto completions are not great, but here we go. Let's see what it's come up with. Personalization. This looks to be a composable. Use personalized page. Here's the page. Segment key. Update the page component. Here's how we're gonna use that. Personalize page. Use a page. There is a segment key. Okay. AI. I will bite. Alright. App utils. Personalization. And I don't know why it's using the is there let's see, like, an actual it's using the composable format for view, but import type maybe rest. Return computed. I mean, to me, this is a I guess this is a this is a composable. I'm not sure why it's doing that. So we'll stick that there. This is use personalization. This is the name of it. Use personalization. Again, probably not a strong choice for, like a composable here because this is probably something that you might want to extract out and use, like, across different projects. Use personalization. Use personalization. Okay. Cursor. Okay. Now that you use this helper, can we have it apply this automatically? Yes. But knowing Nuxt, we should automatically use this. Use personalization. There's our page content. There's the segment key. Does this still get us what we want? So far, looks like it does. Right? And the nice thing here is because we're using a cookie, and even though, like, this would be server side rendered, I'm still getting the content that we want. Right? Because it's automatically running a this cookie first before we actually display the content. Cool. Now let's do we have time? What kind of time do we have here? We got fifteen minutes left. Where do we take this further? Right? Where would we go from here? How do we set this up into, like, something like blocks, right, where we want to build a page out of the blocks? So for that, we can use the mini to any builder inside Directus. We've got this mini to any builder. We're gonna call this blocks. We don't have any related items yet. So first thing we're gonna do here is create a hero block. Call it block hero because I like to be obtuse. Alright. The hero is gonna have a headline. It's gonna have a description. Great. Okay. And then we have a block CTA. Cool. And that is going to have a call out and a button text. Great. Okay. Now we're gonna put those together with the many to any builder. These are gonna be our page blocks. We're gonna say block CTA, block hero. Great. Basically, this many to any relationship, it it doesn't necessarily exist, inside, like, standard SQL. Right? So Directus is doing a bit of API magic here. Basically, what what's happening, and we'll just hide, like, what we've got already. Alright. Hide this field. Don't destroy. Just hide. So we can see what's happening. We've created these separate collections, and then there's a junction collection here that is storing the page's ID and then we're storing the ID for the item. And then we also have a string for the collection that we're pulling this from. So it's, you know, basically some API magic to string these things together. But now if I go in and I update the page, we could see we've got a CTA that we'd say, this is you should try Directus. Do it now. Great. There's our CTA. There's our hero. Brian is cool. Well, he's not. There's our description. Alright. And let's hit save and stay. Let's just look at our page content now that we're getting back. Right. We're showing two different blocks here, but again, we need to go in and set our permissions. Directus keeps you secure by default. So we're gonna add just read permissions for all this. Again, for production, we wouldn't do this, but can we get to personalized blocks in twelve minutes or less? I need to get my ass in gear. Alright. Alright so we're gonna go back to our slug here. We're gonna change this up where we have our blocks. And actually we could use like a object syntax as well. Blocks, we're going to have here. We're going to get the we could get that. And then we're probably also going to have, like, variations within the blocks themselves. Well, this is getting messy. Right? Okay. So now let's just go back to the page. Variations blocks. I don't wanna do variations. We wanna do blocks. Alright. I'm gonna pull this back in. For our page render, we're just gonna have to pull that out. And now we could see we got, like, some data for each block. Right? Blocks, items, we're gonna wanna grab the item for each block. I don't need it. Fields comma. What do we got here? Okay. Well, yes, no, maybe so. Something is off. We got one too many. How nested did we go here? This one needs okay. Did we get it right this time? We still did not get this right. Direct as requests. AI is totally screwing this up, and I've had too much coffee to fix this properly. Alright. So we got the block. We're within the block, we're going to have, the items items, and then we're gonna grab the variations. Where okay. Backup. Backup. Backup. Love all the formatting. Items. Oh, yeah. This is not going well for me, is it? Fields. We're gonna grab the collection field. Great. Okay. Now, we got item. If I change this to item, we should see the item text. Great. And then also we're gonna have variations on that same item. Cool. Alright. But we don't have those yet. Alright. So let's go in and add those. So now the same way that I created that translation function on, let's say, the page level, we're gonna go through and we're gonna do this speed run style in less than eight minutes. We're gonna call this variations. Great. This is gonna be segments. This is the collection there. And what are we gonna call this? We're gonna call this the segments. Block hero ID, block hero variations. Great. Primary key is gonna be the or the primary indicator is gonna be that. We'll set this up. Alright, so now on our block hero I should be able to set up like a variation here except I don't have that content yet. So we'll go to our block CTA. Oh, no. Block hero. We're gonna copy that headline and description down into the next collection, right, to for our variations so that we can have that specific content. Block hero variations, duplicate. Great. Now, I go in, we've got our block hero I'm gonna stick up at the top. We have a headline for Brian's cool to editors. Brian is cool to developers. Great. Okay. And we're gonna create a hero component. View, create a hero component with Tailwind that has two props, headline and description. Let's see what it comes up with. Outsourcing the work here. Six minutes. You know, we could declare this one good, but, yeah, let's make it fun. Alright. We got the hero component. Okay. So now within the like this page section, I guess, Let's go and call that hero component with the page headline and description. Context switching sucks. Do we have a oh, no. We don't have that at the page level. Right? We're gonna loop through all of our blocks. So the, let's do template v four page blocks within the blocks. If the block collection equals the hero to block hero, basically, block underscore hero, that's the name of our collection, we're going to render that out. But now we also need to use the personalized page, page dot blocks, personalized page dot blocks. But low and behold, we're still not changing that content based on that because our personalization is what? At the page level. Right? So AI, help us in our moment of need. Five minutes left. Change this to well, accept a what a to accept content and recursively merge if there is a variations key within The array within the objects. There's a variations key. Date. And task. Objects. Let's see what this comes with back. What if just curious. Alright. Merge variations, current segment, matching variations. Is this to me, like, obviously highlights the dangers of AI? I am on a crunch here. Right? I don't have a clue. I don't have enough time to, like, figure out if this is actually going to do what I wanted to do anyway. So, you know, great that you can I I mean, I love the fact that I can just, like, quickly POC this, but, you know, it's like a what are we coming back with here? Personalized page. Let's just see what it comes back with. Blocks zero. So, again, like, is yeah. See, that's goofed up. Oh, and I'm not even giving the personalization content, though, am I? Why is that? I should be fetching it here. Items, personalizations. We gotta go back into our permissions, block hero variations, access. Save. Are we now at least getting that? Yes. Okay. So AI did save the day here, basically. You know, before I shipped any of this, I would need to go through and, like, study this in detail to figure out is this actually doing doing right. It appears to be doing right, but are there gonna be, like, dangerous side effects? But here I could see Brian is cool to developers. You know, maybe we go back in and within this, do we have a does don't like him? Alright. No. He's not. Neither do content editors. So there is that. You know, the one thing I noticed is we should have, like, a fallback and it should take that fallback into account. So it's not doing, like, a deep merge, but now we could see that content. We could clean this up just to show it out. And boom. So now we've got, like, page block level data where I could go through and build a page and have segments and personalize that data, for each individual segment. So I'm I'm gonna call that a win. Right? We got one minute fifty six seconds left. Would I have been able to achieve this without cursor? %. Would it have taken me more than an hour? One hundred percent. Is this still cool and fancy? Probably not. This is the start to something incredibly cool where, you know, I could go in and on the client side have some type of tracking and some type of rule engine that just, like, consistently con like, creates these different segments or groups of visitor into segments. And if you keep that on the client side, it could be privacy friendly as well. But I hope this was an interesting episode, an exciting episode for you. I've had a good time. I'm probably gonna figure out a way for us to leverage this somehow in the future. That's it for this episode of 100 apps, one hundred hours. Thanks for joining me. I do roll like the success message. See you guys next time.","7fed7f62-8460-4a82-b086-4943b7f0d6fa",[1131],"9118d9ce-5497-44cf-a504-95bf6a70dd9d",[],{"year":399,"number":1060,"id":1061,"episodes":1134,"show":1135},[1063,1064,1065,1066,1046,1067,1068,1069,1070,1071],{"title":322,"slug":323},{"id":1070,"slug":1137,"vimeo_id":1138,"description":1139,"tile":1140,"length":1125,"resources":12,"people":12,"episode_number":527,"published":1051,"title":1141,"video_transcript_html":1142,"video_transcript_text":1143,"content":12,"seo":1144,"status":19,"episode_people":1145,"recommendations":1147,"season":1148},"ai-personalized-landing","1059438000","Watch Bryant create a system that generates custom landing pages for target companies by scraping their websites and using AI to craft personalized sales pitches. With a mix of Directus hooks, FireCrawl for content scraping, and the Anthropic API for copywriting, he builds a complete workflow that transforms company data into engaging sales pages for his fictional developer seat cushion business.","8b694159-4108-4801-8254-3a42720143fe","Mission: AI Personalized Landing Page","\u003Cp>Speaker 0: Alright. Alright. Welcome back to, yet another episode of 100 apps, one hundred hours. I'm your host, Brian Gillespie. And in this episode, we're reaching back into the AI personalization bucket, and we're gonna build some personalized landing pages.\u003C\u002Fp>\u003Cp>If this is your first time on 100 apps one hundred hours, there are only two rules to the show. We have sixty minutes to plan and build an application, a clone of one of your favorite apps, or some totally random idea. No more, no less. And then number two is use whatever we have at our disposal. Could be past projects, could be AI, could be all sorts of crazy things that we might get into today.\u003C\u002Fp>\u003Cp>I've got no idea whether we're actually going to be able to, like, push this one across the finish line or not. So that should be exciting for this episode. Alright. So AI personalized landing pages. You know, there's another episode we've done in this season, like, content personalization.\u003C\u002Fp>\u003Cp>This time around, I wanna get very, very specific and say, hey, I have a target account or a a company that I wanna do business with. I wanna use AI to generate a landing page for them. So let's dive right in. We're gonna put sixty minutes on the clock, and let's go. Here we are.\u003C\u002Fp>\u003Cp>Alright. So I always like to start these by just discussing what are the features that we actually want out of this. Right? So as far as our functionality, we'll just make this freaking huge. Oh, not that big.\u003C\u002Fp>\u003Cp>Alright. So as far as the functionality we want out of this, here's how I'm thinking this is actually going to flow. We're going to add an organization to our back end, which is gonna be powered by Directus. And then a the AI is going to scrape the website. We want to use that content to generate and use content found to shift return.\u003C\u002Fp>\u003Cp>There we go. That's what I needed to generate a personalized landing page for that specific org. Okay? Display that using a front end. That's it.\u003C\u002Fp>\u003Cp>I would love to be able to do this on the fly, this part of it. Right? But, unfortunately, like, the LLM models are not super fast or well, if you use something like GROC, maybe. But the this step is gonna have to be done asynchronously. So we'll sort through that.\u003C\u002Fp>\u003Cp>Let's think through, like, our our actual data model now. So let's throw this up on the board. We're gonna have organization. And, honestly, like, I I could have a separate one for pages. You know, we've got, like, the page builder set up inside Directus, which is really nice.\u003C\u002Fp>\u003Cp>Something that we've showed in previous episodes where I can build a dynamic page based on predefined blocks. In this case, we might just keep it super simple. We've got an organization that I wanna target. Within that, we're gonna have a name. I need a website URL to scrape.\u003C\u002Fp>\u003Cp>And then the rest of it would be generated. Right? You know, let's call it blocks, I guess, generated blocks. Maybe we got a hero. I wanna have, like, a feature section.\u003C\u002Fp>\u003Cp>Call to action. Call to action, CTA. Man, I have outgrown my box here. We'll just add that up there. Alright.\u003C\u002Fp>\u003Cp>So this seems pretty solid, I guess. I what's our plan of attack? Right? What are we going to sell? What are we going to pitch?\u003C\u002Fp>\u003Cp>You know, what is our organization that we are setting up? Alright. So, as far as what I've got in the can already, I've got a Nuxt application. I've got a direct instance. So if we load up local host 3,000, I should see my Nuxt instance, my Nuxt application, just the same starter I use in all of these.\u003C\u002Fp>\u003Cp>And if I go to Logos eight zero five five, I should see a blank directus instance. That is not the one that I wanted to see. Let's control some of these Docker containers here, and then we'll fire this back up to see if we get what we want. Boop boop boop. Should be a blank instance.\u003C\u002Fp>\u003Cp>Great. Okay. Alright. So now I'm gonna log into this blank Directus instance. Directus is gonna give me all the back end functionality that I want and or need.\u003C\u002Fp>\u003Cp>And you could see it is totally blank. Right? So let's attack this from the back end perspective first, and then we'll switch to the front end, and then we'll switch to the back end, and we'll switch back and forth and back and forth until we all get confused with how many tabs I have open. Alright. Create a new collection.\u003C\u002Fp>\u003Cp>So let's create a new collection for our organizations. Right? As far as table names go, I prefer plural. Some people prefer singular. Totally up to you.\u003C\u002Fp>\u003Cp>We're just gonna add some of these specific default fields that Directus makes available to us. Like, hey. Is this published? Is there a sort order to this? Obviously, date created here will be populated with a timestamp of when this record gets created.\u003C\u002Fp>\u003Cp>Great. So now we have an organization. I'm gonna make this look very pretty. We will call it we'll search for maybe business. Forget I'm talking to myself here.\u003C\u002Fp>\u003Cp>I was about to ask you a question of which icon to use, but, still just talking to myself. Alright. So as far as our first field, right, we're going to add a the name of the organization. Pretty simple. Let's keep editing.\u003C\u002Fp>\u003Cp>I can make this shorter. We'll just go half width. So I am controlling the field, like the the actual form that we'll be interacting with as well as my data model and my APIs. So next, we will save this and let's add a website or URL. Let's just call it website.\u003C\u002Fp>\u003Cp>And what are we gonna do here? Let's go to advanced field creation. We could see that this is allowing a null value, but let's make sure that we set that to required. So when I fill out this form, it is going to make me input that. Now if I keep this as nullable, that means we could still potentially pass that value into the database as null, using the API.\u003C\u002Fp>\u003Cp>I could turn that off if I wanted to. I could also make sure this is unique as well, so that we don't generate this a million times. And for the display, we'll just show the raw value. Right? Is that it?\u003C\u002Fp>\u003Cp>Let's add a little icon just to make this a little fancy. This will be a link. Great. And now we have a website. And let's go ahead and make name required as well.\u003C\u002Fp>\u003Cp>We're gonna fill both of those things out. Boom. Amazing. I can go in. I can now add a name like Directus and a website, Directus.io.\u003C\u002Fp>\u003Cp>Save. Great. Notice that perfect little icon right there. It looks great. Right?\u003C\u002Fp>\u003Cp>I can even oh, no. I was gonna say I could point to it. I can point to it. There it is. Alright.\u003C\u002Fp>\u003Cp>So now we have this, like, data model. It's amazing. There's two fields here. This is really underwhelming as Bryant. Alright.\u003C\u002Fp>\u003Cp>So we've got an organization in our back end. Let's switch round to the front end, and let's start, like, fleshing something out. And this is where I will say, like, POC generation, AI has got an incredibly great for this, especially with, like, UI libraries like Shad CN and Tailwind for styling. It's gotten really good at creating components and things like that. So, what are we going to what are we gonna generate here?\u003C\u002Fp>\u003Cp>Like, what are we pitching? What are we what are we selling? Coffee mugs. Let's do something super developy developer ish. Maybe like a a seat cushion.\u003C\u002Fp>\u003Cp>Maybe we're gonna sell seat cushions. I'm hopefully, I look younger than I feel, but I've got three little girls. I've got a back that aches every day when I wake up. So maybe that's what we'll do here. We're gonna generate some seat cushions.\u003C\u002Fp>\u003Cp>Alright. So, when I think of the front end here, let's create a page. So let's create a new route for the org. Organization. I'm gonna wrap this in brackets there.\u003C\u002Fp>\u003Cp>Great. We'll do v base setup, script setup. Let's change that to length. Yes. And this is giant text, so that's a good thing.\u003C\u002Fp>\u003Cp>Alright. We got an organization. We're gonna fetch that organizational data. And behind the scenes, I've used this in so many videos. I'd never take the time to explain it.\u003C\u002Fp>\u003Cp>Basically, I'm using the Directus SDK. I've just got a quick little plug in as far as Nuxt goes. We're gonna pull the direct as URL from the configuration. We're gonna create a really simple direct as client, and we're gonna provide that to the Nuxt application. So the first time this application spins up or, you know, when it loads, it creates that instance for us and makes it globally available through something like this where we could say, Directus use Nuxt app.\u003C\u002Fp>\u003Cp>The other thing that I'm gonna do here, I'm gonna import read items from the Directus SDK and then we're gonna fetch this data. So we'll say data, that's gonna be organization. No thank you AI. Wait. We're gonna use the async data composable from Nuxt, just a handy little helper for caching.\u003C\u002Fp>\u003Cp>We're gonna call this organization organization. We're gonna get that organization from our routes, route.params.organization. No. We don't want that. We're going to do it this way, route.params.organization.\u003C\u002Fp>\u003Cp>We're gonna do direct us dot request. Yes. Direct us request. Read items, and we're just gonna wrap that. And do I have some nice formatting?\u003C\u002Fp>\u003Cp>Great. We got some formatting. Okay. So what are we doing here? We're looking for a slug.\u003C\u002Fp>\u003Cp>We don't really have a slug set up yet. So let's just do an ID. And maybe we do wanna set up a slug, maybe we don't. Route dot params ID. You'll notice this filter syntax here.\u003C\u002Fp>\u003Cp>Directus has a ton of different filter rules that I can use to create deeply nested or crazy queries where I can fetch everything I need in a single API call. And then I'm just gonna go in and log this out on the page. Is this possible? Let's go into Directus and just make this process easy for me as well. There is a button links option here which is just presentational.\u003C\u002Fp>\u003Cp>And we're gonna say view on website. Give it a label, arrow up link. Do it is there like an external link? Arrow. Let's just go arrow caret ray.\u003C\u002Fp>\u003Cp>Okay. And we're gonna say HTTP, we want the URL local host 3,000 is what we're running on. And then we're gonna run this at the ID. Great. Well, it's safe.\u003C\u002Fp>\u003Cp>Now I should get a button on this page where I can view on the website. And lo and behold, it looks amazing. This is the most beautiful page ever. We're just gonna go back in and edit our permissions. So by default, Directus keeps everything secure, keeps a tight lid on everything.\u003C\u002Fp>\u003Cp>So this public policy that we have doesn't have any access to our data. So let's go in and add read access for organizations and let's let's set files as well in case we decide to upload some pictures of our amazing seat cushions that we're going to sell. And now if I refresh the page, we could see, great, I have a name, I have a website. Alright. That's kinda underwhelming.\u003C\u002Fp>\u003Cp>Not great. So let's have it design. Let's retray, here. We are going to create I'm gonna use Claude's sonnet, and let's create a landing page. You are let's let's back up a minute.\u003C\u002Fp>\u003Cp>What do we want our landing page to look like actually? Right? So we probably got like a hero. We've got a features for our seat cushions, seat cushions, hero section. And then we've got like a like a buy button.\u003C\u002Fp>\u003Cp>Buy button. Buy now. CTA. Alright. So that's gonna be the structure for our page.\u003C\u002Fp>\u003Cp>Here we go. We are going to flesh this out. You know, we probably want to you are are we gonna, like, one shot this? Let's see. Well, if I do this, you are a expert I I don't think you really need to do this because it's picking up up whatever I have, but create a hero section component for a online store that sells seat cushions to developers.\u003C\u002Fp>\u003Cp>A beautiful, well designed view three component using alright. So one of the features I've been experimenting with in cursor is this documentation feature. This starter has like the Nuxt UI library built into it. Which version am I actually running? Did I upgrade this to the latest one?\u003C\u002Fp>\u003Cp>Yeah. I'm running the alpha version. So we're gonna use those docs and tell when CSS. We'll see what we come up with here. Let's create a beautiful new component.\u003C\u002Fp>\u003Cp>You page hero. I don't know I don't know that I have any of these actually. Just you don't use our Nuxt UI components after all. Okay. So we can see what is coming up with hero section.\u003C\u002Fp>\u003Cp>We got a feature, ergonomic design. Yeah. I'm kinda curious to see what it comes up with as far as, like, the data model around this as well. We'll just go ahead and flush this out for it here while component section, features. Oh, this is running kinda slow.\u003C\u002Fp>\u003Cp>New collection, blah blah blah, right column. Yeah. It would probably even be easier just to go into, like, Tailwind UI and find some of these components here. They've got some nice ecommerce components. Let's look at it like a hero section.\u003C\u002Fp>\u003Cp>Alright. Let's just we'll just see what it came up with first and accept this. We'll go back to our organization. We'll insert this component in here. This is our hero section.\u003C\u002Fp>\u003Cp>Great. Cushion. JPEG. Is there a cushion dot JPEG? Hero section.\u003C\u002Fp>\u003Cp>Where is this cushion dot jpeg cushion hero dot jpeg. Alright. Let's bring in one of our first AI big guns here. We'll go to replicate, and I'm just gonna use our flux model here. It will create a product image of a futuristic seat cushion for developers if I could actually spell, that'd be dangerous.\u003C\u002Fp>\u003Cp>Developer's seat cushion, developer's desk chair. I don't think punctuation is hugely important here. Let's see what we come up with. For 6¢ an image. What are we gonna get out of this?\u003C\u002Fp>\u003Cp>That looks pretty pretty wicked awesome there. Right? So we're gonna download that. Let's dump this into our assets folder, and we'll call it seat cushion dot JPEG, and we'll just update that here. Alright.\u003C\u002Fp>\u003Cp>So this should be assets, images slash seat cushion. And oh, what have we got here? Something weird is going on. Better comfort for better code. There we go.\u003C\u002Fp>\u003Cp>Alright. Now we're getting somewhere, except this looks really odd with our price badge. Something's going on there. Let's just comment that out. There we go.\u003C\u002Fp>\u003Cp>We got our seat cushion. This is great. Alright. Now let's have it designed something else. Right?\u003C\u002Fp>\u003Cp>Let me just shrink this down just a little bit. Feature alright. So we want this to be our headline. Right? So let's flush this out a little bit more.\u003C\u002Fp>\u003Cp>This is gonna be our headline. This will be our description. I'm just trying to think through like our what our props are gonna look like for this. Define props, features as a feature. Great.\u003C\u002Fp>\u003Cp>Learn more, shop now. Better comfort for better code. This is a new collection. Badge. Alright.\u003C\u002Fp>\u003Cp>So we'll just stick that in our props. Now we got badge, got features. Do we actually want the icons we can. I think I've got the Nux icon library built into this so we could simplify this, like hero icons. There we go.\u003C\u002Fp>\u003Cp>I don't know what other hero cons we have here. Shopping bag, check circle. There we go. And then we'll, like, flesh this out. Where is that icon at?\u003C\u002Fp>\u003Cp>Icon. Feature dot icon. Yeah. So we could ditch all of this and make this a little bit cleaner. We'll keep that.\u003C\u002Fp>\u003Cp>Right? You icon. Name equals feature dot icon. We'll just ditch this, ditch that, clean this up a bit, and we should still have our icons. Great.\u003C\u002Fp>\u003Cp>And then we're gonna move out features out of here, we'll put that into this. So for now, we'll just say here's our data, our data. We'll drop our features here. Sometimes it is frustrating as I'll get out to properly just navigate using these AI tools. The tab tab tab is it's nice when it works well.\u003C\u002Fp>\u003Cp>When you have all these extensions included, it does get a little bit messy. Let's check on time. Where is Figma? Thirty seven minutes. Alright.\u003C\u002Fp>\u003Cp>Better get to it, g. Alright. If I hit refresh, are we actually going to let's call this data, and then we'll go back into our hero section. We'll just wrap all this up into data. There we go.\u003C\u002Fp>\u003Cp>There we go. Data dot badge, data dot headline, shop now, learn more, data dot features. Okay. So maybe that is a little long as far as a headline. What do we have previously?\u003C\u002Fp>\u003Cp>Yeah. Alright. Comfort for better code. There we go. Okay.\u003C\u002Fp>\u003Cp>Now, this is our data. Right? This is our data structure. Maybe we tell this to generate. Alright.\u003C\u002Fp>\u003Cp>So how are we gonna generate the actual code here? Like, how are we gonna generate the personalization of this specific page? Let's just ditch the code part. There's our website. Looks great.\u003C\u002Fp>\u003Cp>How are we gonna personalize this? So for that, let's kick off a Directus extension, basically. Basically. Yeah. Nothing basic about it.\u003C\u002Fp>\u003Cp>So what I'm thinking we'll do here, we'll set up a custom Directus extension so that whenever I am adding a new organization, it will create that data for us. So let's also do this where we'll go in and we'll call this just page data. Page data. Page content. It doesn't really matter what we call it.\u003C\u002Fp>\u003Cp>I'm just gonna set it to be JSON. That's what we're gonna ask back from the API. Alright. Let's kick off this extension. So I'm inside my Directus directory.\u003C\u002Fp>\u003Cp>We're probably gonna go into CD extensions within that. And then I'm gonna go n p x create direct us or create dash direct us dash extension latest. And I am going to create a let's do a custom hook. This is generate personalized page, custom landing page, AI landing page. We use TypeScripts.\u003C\u002Fp>\u003Cp>We'll go ahead and install the dependencies. And one of the things that I've got set up here inside my Docker Compose for this, obviously, like, I'm I'm I'm creating a volume for the extensions here so we could pull those in. But I'm also somewhere in here. I have extensions auto reload set so that whenever we build this extension, Directus is going to look for that and reload this extension or at least it should. So we'll go into CD AI landing page, npm dev.\u003C\u002Fp>\u003Cp>Alright. So let's actually open this bad boy up and see what we've got here. Items dot create. Okay. So now I'm just quickly going to cheat a bit and pull up the direct to stocks.\u003C\u002Fp>\u003Cp>Hooks, custom API hooks. So what we're looking for here is after this item is created, and I think the structure for a collection itself is a collection name dot items dot create. This is what we're gonna do. So our filter will look something like the or our action here will look something like this. We don't necessarily need this.\u003C\u002Fp>\u003Cp>Alright. So we're gonna say organizations dot items dot create, organization created, and this will be async. And what this is going to receive should be alright. We're gonna get the context here. We can see what that context is gonna look like.\u003C\u002Fp>\u003Cp>That's how we're gonna initiate some services. What does the action receive? Right? The action receives the event name and a callback function that is executed whenever the thing is submitted. Alright.\u003C\u002Fp>\u003Cp>Extensions guys. Do we have one on hooks? Use hooks to validate phone numbers. Items that create input collection. So we're gonna get the payload, I'm assuming, and the content, item context.\u003C\u002Fp>\u003Cp>Let's see just kinda what we get here. Extension's changed. Okay. Test httpstest.co. If I check my Docker logs, I cannot import.\u003C\u002Fp>\u003Cp>Let's just stop and spin up the Docker container one more time. Make sure we're actually getting that. Okay. I could see that extension being initialized. Oh my gosh.\u003C\u002Fp>\u003Cp>That gets ugly, doesn't it? Alright. Let's try this one more time. We'll delete testco, test Co. Are we seeing any console log?\u003C\u002Fp>\u003Cp>I'm not seeing any logs here. Async, define hook. Extensions reloaded. Organization oh, duh. You gotta use the right right syntax, Brian.\u003C\u002Fp>\u003Cp>Frustrating to be under the clock. Testtest.co. Hit save. Okay. So now we're seeing some stuff here.\u003C\u002Fp>\u003Cp>Great. Alright. There's our holy moly. Alright. So that is our there's the context that we're getting there.\u003C\u002Fp>\u003Cp>Organization created. There's the payload that we're receiving. And then we're getting the item context. Okay. Great.\u003C\u002Fp>\u003Cp>Alright. So what we're gonna do, what do we want to actually happen when this is going to pop off? There's a third party tool that we're gonna use for this called FireCrawl that I've been testing. Right? So let's try this.\u003C\u002Fp>\u003Cp>We're going to add Firecrawl. Js. Alright. So we're gonna go to our extension that we're running. I'm gonna install this library from FireCrawl.\u003C\u002Fp>\u003Cp>And we're gonna go ahead and one of the other things that I'm gonna do, we're gonna call this the organization's service. We're going to extract that out of the context. Const request. No. It's gonna be the services out of the context, then the item service through the services.\u003C\u002Fp>\u003Cp>And we're gonna call new item service. Great. And this is gonna give us the ability to update. Right? So we're gonna initialize that service.\u003C\u002Fp>\u003Cp>We've got that installed. Now let's copy our code here. I'm hoping this is my live API. New FireCrawl app. We need to import that.\u003C\u002Fp>\u003Cp>Scrape a single web page. Got it. We'll move that down below our item service, and we're gonna get the scrape result. And for now, let's just oh, I can already see the typo happening here. That's gonna be frustrating for me.\u003C\u002Fp>\u003Cp>And we're gonna say await updated org equals await organizationservice.update.one. Data, does that need to be wrapped in data? I don't know. Scrape content. Alright.\u003C\u002Fp>\u003Cp>We're gonna return the updated org, and let's wrap this in a try catch as well. Try catch. Console dot error. See if we get some formatting. Nice.\u003C\u002Fp>\u003Cp>Okay. Let's fire up the dev server for this and cross our fingers. Right? The other thing I need to update here, we're gonna have to go in and figure out which tab has all of my browsers in it. Maybe we just move that to a single browser for now.\u003C\u002Fp>\u003Cp>And we're gonna add a new field for this. We're gonna call it, scraped result. Scraped result. I don't know what this is gonna be. I'm assuming scraped content.\u003C\u002Fp>\u003Cp>I'm assuming this is gonna be JSON data that we're gonna get back. I guess I could look and see. Success data. You know, we could also just store the markdown. That might be easier.\u003C\u002Fp>\u003Cp>Where do we go? Scrape content. Let's delete that. Where does this store the markdown? Scraped.\u003C\u002Fp>\u003Cp>Scraped content. Alright. So according to FireCrawl, if we do this correctly, we're gonna get something like this. So with the scrape result dot data dot markdown. Fingers crossed.\u003C\u002Fp>\u003Cp>That's what we're gonna get. We could also just console log that to verify scrape content, scrape result. Alright. Let's go for it and see one last thing that I wanna do here. This is not, using, like, any of our accountability, so do not do this in production.\u003C\u002Fp>\u003Cp>But we'll just allow the public to create and update all the content on the underlying instance. Great. Love how secure I am being. I'm sure everybody on my team would be thrilled. Alright.\u003C\u002Fp>\u003Cp>So let's just go to Directus as the example. HTTPS, Directus.io. That's our website. Oh, I've already got directis.io hard coded in there as well, so we're probably gonna want to pick that up out of the payload as well. Can we still see that payload?\u003C\u002Fp>\u003Cp>No. Let's do this. We'll see the payload.website URL. We'll see if this actually works. Fingers crossed that it does.\u003C\u002Fp>\u003Cp>Scrape content. Save. What happens on this side of it? Around URL must be a top level domain or valid path. So I'm getting that error there.\u003C\u002Fp>\u003Cp>It's fine. Should set this up on update or create. Directus.io. Save and stay. Do we see what we get back?\u003C\u002Fp>\u003Cp>Is this thing still running in the background? What are we doing here? I got no visibility into what's happening here. Let's try it again. Directus.\u003C\u002Fp>\u003Cp>Directus. Io. Hit save. Must be a failed to scrape URL. URL must oh, duh, dummy.\u003C\u002Fp>\u003Cp>This is where it helps to define types so you don't make these same silly mistakes as I have. So I've called this field website. I was calling it website URL here, so not actually passing any website. Cannot read properties of collections. So there's our scrape results.\u003C\u002Fp>\u003Cp>Do we see the markdown? There's our markdown. Cannot update one. What am I doing wrong here? We're not updating the schema.\u003C\u002Fp>\u003Cp>We need to extension service, access items. Yes. Here we go. This is what I actually need to see. Alright.\u003C\u002Fp>\u003Cp>So we got our context. We're gonna do services get schema. We need that schema. And we're also gonna need the accountability schema. So in our item service definition, we don't wanna do this.\u003C\u002Fp>\u003Cp>We're gonna do this. Equals await. No AI. No. We're gonna await git schema.\u003C\u002Fp>\u003Cp>So we're gonna get the schema from our context and on the accountability side, accountability. That'll be accountability. If we just destructure that, I think that gives us what we need. Let's go back and try it again. Love hacking this stuff together on the fly.\u003C\u002Fp>\u003Cp>No pressure. Directus directus dot I o. Hit save and stay. Let's just watch over here and see what happens. Fire crawl seems to take a little bit, so that is forbidden.\u003C\u002Fp>\u003Cp>I don't know what we're getting over here. Can I just leave off the accountability object? How many times am I gonna create this before I just do update? Directus Directus dot I o. Save.\u003C\u002Fp>\u003Cp>Probably burning through these fire crawl credits as we speak as well. That that that that that, forbidden. You don't have permission to access this. Why is that not the case? What is the alright.\u003C\u002Fp>\u003Cp>So let's stop loading that. Let's just say console dot log the payload. Actually, I think it's I goofed up, didn't I? What are we gonna be receiving here? It's not gonna be it'll be payload dot key.\u003C\u002Fp>\u003Cp>Payload dot let's just log the whole payload again and see. Just comment out this. Comment out. Directus Directus dot I o slash payload key. Okay.\u003C\u002Fp>\u003Cp>Duh. You're smarter than that, Brian. Alright. So we need this key. That's what we're getting is the key payload dot key.\u003C\u002Fp>\u003Cp>So here, payload dot key. There's the event. There's the key. There's the organization of the collection we already got. Alright.\u003C\u002Fp>\u003Cp>Let's keep rolling and maybe try to actually complete this one. Directus. Directus dot I o. Save. Can we actually get the scrape content?\u003C\u002Fp>\u003Cp>Can we actually complete this on time? Can we do any of these things in any sort of actual working fashion? Is this actually gonna work? Will we know? How do I know?\u003C\u002Fp>\u003Cp>Console.logupdatedorg. Directus. Directus I o. Save. There's the payload.\u003C\u002Fp>\u003Cp>Fire crawl really takes a bit. Status code, 200. Updated org. Is this actually gonna see what we want? No.\u003C\u002Fp>\u003Cp>Of course not. It's not gonna update the right one. Data dot scraped content. I'm sure this is what we need to do here. Right?\u003C\u002Fp>\u003Cp>Update one. Let's look at the services. Update an item. Yep. It's not inside data.\u003C\u002Fp>\u003Cp>So I don't need that to wrap. Gosh. I'm just eating up all the time. I wish I spent more time with hook extensions. Super powerful because you're basically just updating the underlying Express app instance anyway.\u003C\u002Fp>\u003Cp>But alright. So now let's reach for anthropic. If we hit refresh, do we ah, still not getting the data that I want. Right? It's saying that it's updated that org.\u003C\u002Fp>\u003Cp>Scrape result. Scrape result scrape content. So they tricked me. It's not it's not as it shows. It's actually not there's actually no data attribute there.\u003C\u002Fp>\u003Cp>It's basically just scrape result dot markdown. Alright. Last freaking time for the scraped content, Directus Directus dot I o. I was not planning on doing this much troubleshooting. Behind the scenes here, I'm gonna go in and just create a API key for anthropic.\u003C\u002Fp>\u003Cp>Hundred apps. There's my API key. Updated org. Did we actually get what we want? Holy moly.\u003C\u002Fp>\u003Cp>We got some scraped content in here. Okay. Alright. So now the next step in the puzzle is going to be call the Anthropic API to generate a landing page for the organization with the JSON schema, page data. Alright.\u003C\u002Fp>\u003Cp>So now this is one of the things where I've been leaning on the Vercel API, or AI SDK. So I'm just gonna install it here, p m p I. This is just AI. And they've got a way. The last time I checked, the Anthropic does really well.\u003C\u002Fp>\u003Cp>Like, SONNET does really well at copywriting. But it doesn't have the JSON mode that the ones like OpenAI have. So I'm just gonna go in and look for provider management providers. Where are my providers? Providers and models.\u003C\u002Fp>\u003Cp>Looks for the anthropic provider. I'm gonna add it as well. And let's see what we can get here. Import anthropic. So I'm gonna import this guy.\u003C\u002Fp>\u003Cp>And then we're also going to do what? Create anthropic. We'll do that here. Now what I would normally do is this context object should have an e and v variables as well. API key.\u003C\u002Fp>\u003Cp>I'm not gonna do that here because I am worthless, basically. No. We're eating up a lot of time. Base URL, API key, generate text. We want to generate objects.\u003C\u002Fp>\u003Cp>So there's our anthropic client that we're creating. Don't mind that API key. Now let's define what do we need? We need Zod as well, p m Zod. And we're going to use was it generate object in here?\u003C\u002Fp>\u003Cp>See if we can find it. Generate object. Alright. Generate object from AI. Okay?\u003C\u002Fp>\u003Cp>Now what we're trying to do is just basically, we're gonna define a Zod schema of the object that we want, and that's gonna be our hero section. Right? Zod schema. We're gonna lean on the props. There we go.\u003C\u002Fp>\u003Cp>Okay. So now I'm just gonna copy this wholesale. We're gonna stick this in our custom hook. Again, not being careful at all about separation of concerns or cleanup. So there's our Zod schema.\u003C\u002Fp>\u003Cp>We want the object to return hero section schema. Alright. So now let's look up what we actually want this to how we're gonna work this. Generate object. We don't want testing.\u003C\u002Fp>\u003Cp>There's gotta be a better during text, during structured data. Alright. So our model okay. So we want the object equals await generate object. I'm hoping this is right.\u003C\u002Fp>\u003Cp>Fingers crossed with AI. Generate a landing page for the following organization with the content. Okay. For the organization payload. Name, the following content.\u003C\u002Fp>\u003Cp>You are an expert copywriter, and your job is to generate amazing landing pages that are personalized for our target clients. We sell the best desk chair cushions in the world for developers. Schema. There's our schema. That's our Zod model.\u003C\u002Fp>\u003Cp>Okay. When we get that back, what are we gonna do with the object? We are going to update the org, updated org two. It's fancy, fancy, fancy. Okay.\u003C\u002Fp>\u003Cp>Alright. How we doing on time? We got nine minutes to try and figure this out. Let's just PM, PM, dev this bad boy up and see if this is actually gonna come out at all. We're rebuilding this extension.\u003C\u002Fp>\u003Cp>Dun dun dun dun dun. I'm gonna nuke this entirely. Let's try it again. Let's see if it works for directus.io. We hit save.\u003C\u002Fp>\u003Cp>I could check the logs on Docker. Create anthropic is not defined. Anthropic. Response. Anthropic.\u003C\u002Fp>\u003Cp>Where is that? Providers and models. Anthropic provider. Create anthropic. Import the default provider.\u003C\u002Fp>\u003Cp>Oh, no. We could just import the what am I doing? Create create anthropic. Organizations. Oh, no.\u003C\u002Fp>\u003Cp>Directus. Duh. Directus .I o. Hit save. There's the payload.\u003C\u002Fp>\u003Cp>Obviously, it would be great to have visibility into this. And one tool that I've been using as of late is ingest. Great tool for something like this, generated hero section. This is gonna be nice, doc. Okay.\u003C\u002Fp>\u003Cp>Alright. So now before we reveal what it generated, let's just try to make sure this is actually gonna work. So instead of the data here, we're just gonna use the organization dot data dot page data. Organization dot page data. Generated hero section.\u003C\u002Fp>\u003Cp>That looks nice. Organization dot page data. Dun dun dun, the big reveal. Close that guy out. No.\u003C\u002Fp>\u003Cp>Status message. What is the status message? Did we actually get the page data generated? Okay. We got the page data generated.\u003C\u002Fp>\u003Cp>It looks like it's coming back with data here as well. So we should probably fix that in our hook. Page data page data is what? Object dot data. But for now, what I'm gonna do, I'm gonna just strip this out.\u003C\u002Fp>\u003Cp>Right? Great. And we refresh. We refresh. We refresh.\u003C\u002Fp>\u003Cp>No. What is going on? Organization. I'm excited for this, and I'm hoping we could get this to work. Right?\u003C\u002Fp>\u003Cp>Why am I not getting any of my data? Organizations. Access policies. We can see that organize oh, that's because it's not the right organization. Alright.\u003C\u002Fp>\u003Cp>I was using the old organization. We blew that one away like a hundred times. Right? This is probably where you'd wanna slug. Five minutes left.\u003C\u002Fp>\u003Cp>What do we have on the clock, Johnny? No. It's not working. Boy. Alright.\u003C\u002Fp>\u003Cp>There's our page data, organization dot page data. There's the scrape content. Why are we not seeing that? Right? Organization.\u003C\u002Fp>\u003Cp>Let's try it now. Status code. What is the error that we're receiving? V if organization dot page data. I could see the page data, man.\u003C\u002Fp>\u003Cp>Organization. Oh, duh. Gosh, man. What an idiot. Alright.\u003C\u002Fp>\u003Cp>So I I've done this on another episode as well. We're gonna transform this to get data. Where is this at? Is it here? Transform data.\u003C\u002Fp>\u003Cp>I don't know if this is how it functions. Error results, organization. I'm not sure the exact syntax, and I'm not gonna mess around with it. So we're gonna do this. It's gonna be organization.\u003C\u002Fp>\u003Cp>Boom. Bam. Directus plus Comfy Desk revolutionize your development experience, Seamless integration. Integrate Directus' flexible back end with your favorite front end while sitting comfortably on comfy desk cushions. Tailor your with adjustable cushions.\u003C\u002Fp>\u003Cp>Perfect. Alright. So now we have landing pages that are personalized for a specific client within, like an AI workflow. Right? So let's try this with some other company.\u003C\u002Fp>\u003Cp>Let's say OpenAI. I guess just to to pit these guys against each other here is this OpenAI.com. OpenAI Com. We'll hit save. And hopefully, this all runs, works nicely on the back end.\u003C\u002Fp>\u003Cp>There's the scrape result. You know, if I wanted to get really fancy, obviously, I could take this to the extreme here. But let's look at OpenAI. We'll view this on the website. What is going on?\u003C\u002Fp>\u003Cp>For noose proper next string, OpenAI. Okay. This one is not super strong. Right? Not sure what the scrape data result was.\u003C\u002Fp>\u003Cp>It looks like Firecrawl didn't do a great job with their website. Right? Let's try Apple.apple.com. Save. And we'll see what it comes back with.\u003C\u002Fp>\u003Cp>There's the scrape content. And, again, this is just coming back with markdown. You know, I might have better luck with, like, actual JavaScript parsing or something like that. But boom. Elevate your comfort coding experience with Apple inspired comfort, the iCushion Pro.\u003C\u002Fp>\u003Cp>Anyway, that's it for this episode. We've achieved this. I'm pushing a stop button on it. One minute left, we have basically fleshed out this personalized landing page where we add an organization, we use AI to scrape it, we then we generate content using LLMs, based on a specific schema, and then boom, we've got a personalized landing page that we could share with them. As far as next steps for me would probably be, like, setting this up on, like, a pretty URL or a slug, you know, adding this to some type of outbound, like, outreach campaign potentially.\u003C\u002Fp>\u003Cp>That's it though. It's amazing what you can put together in just an hour with tools like Cursor, Directus, Nuxt, Anthropic, all these different things come together to create stuff that's fun to build. I'll catch you on the next episode. Peace.\u003C\u002Fp>","Alright. Alright. Welcome back to, yet another episode of 100 apps, one hundred hours. I'm your host, Brian Gillespie. And in this episode, we're reaching back into the AI personalization bucket, and we're gonna build some personalized landing pages. If this is your first time on 100 apps one hundred hours, there are only two rules to the show. We have sixty minutes to plan and build an application, a clone of one of your favorite apps, or some totally random idea. No more, no less. And then number two is use whatever we have at our disposal. Could be past projects, could be AI, could be all sorts of crazy things that we might get into today. I've got no idea whether we're actually going to be able to, like, push this one across the finish line or not. So that should be exciting for this episode. Alright. So AI personalized landing pages. You know, there's another episode we've done in this season, like, content personalization. This time around, I wanna get very, very specific and say, hey, I have a target account or a a company that I wanna do business with. I wanna use AI to generate a landing page for them. So let's dive right in. We're gonna put sixty minutes on the clock, and let's go. Here we are. Alright. So I always like to start these by just discussing what are the features that we actually want out of this. Right? So as far as our functionality, we'll just make this freaking huge. Oh, not that big. Alright. So as far as the functionality we want out of this, here's how I'm thinking this is actually going to flow. We're going to add an organization to our back end, which is gonna be powered by Directus. And then a the AI is going to scrape the website. We want to use that content to generate and use content found to shift return. There we go. That's what I needed to generate a personalized landing page for that specific org. Okay? Display that using a front end. That's it. I would love to be able to do this on the fly, this part of it. Right? But, unfortunately, like, the LLM models are not super fast or well, if you use something like GROC, maybe. But the this step is gonna have to be done asynchronously. So we'll sort through that. Let's think through, like, our our actual data model now. So let's throw this up on the board. We're gonna have organization. And, honestly, like, I I could have a separate one for pages. You know, we've got, like, the page builder set up inside Directus, which is really nice. Something that we've showed in previous episodes where I can build a dynamic page based on predefined blocks. In this case, we might just keep it super simple. We've got an organization that I wanna target. Within that, we're gonna have a name. I need a website URL to scrape. And then the rest of it would be generated. Right? You know, let's call it blocks, I guess, generated blocks. Maybe we got a hero. I wanna have, like, a feature section. Call to action. Call to action, CTA. Man, I have outgrown my box here. We'll just add that up there. Alright. So this seems pretty solid, I guess. I what's our plan of attack? Right? What are we going to sell? What are we going to pitch? You know, what is our organization that we are setting up? Alright. So, as far as what I've got in the can already, I've got a Nuxt application. I've got a direct instance. So if we load up local host 3,000, I should see my Nuxt instance, my Nuxt application, just the same starter I use in all of these. And if I go to Logos eight zero five five, I should see a blank directus instance. That is not the one that I wanted to see. Let's control some of these Docker containers here, and then we'll fire this back up to see if we get what we want. Boop boop boop. Should be a blank instance. Great. Okay. Alright. So now I'm gonna log into this blank Directus instance. Directus is gonna give me all the back end functionality that I want and or need. And you could see it is totally blank. Right? So let's attack this from the back end perspective first, and then we'll switch to the front end, and then we'll switch to the back end, and we'll switch back and forth and back and forth until we all get confused with how many tabs I have open. Alright. Create a new collection. So let's create a new collection for our organizations. Right? As far as table names go, I prefer plural. Some people prefer singular. Totally up to you. We're just gonna add some of these specific default fields that Directus makes available to us. Like, hey. Is this published? Is there a sort order to this? Obviously, date created here will be populated with a timestamp of when this record gets created. Great. So now we have an organization. I'm gonna make this look very pretty. We will call it we'll search for maybe business. Forget I'm talking to myself here. I was about to ask you a question of which icon to use, but, still just talking to myself. Alright. So as far as our first field, right, we're going to add a the name of the organization. Pretty simple. Let's keep editing. I can make this shorter. We'll just go half width. So I am controlling the field, like the the actual form that we'll be interacting with as well as my data model and my APIs. So next, we will save this and let's add a website or URL. Let's just call it website. And what are we gonna do here? Let's go to advanced field creation. We could see that this is allowing a null value, but let's make sure that we set that to required. So when I fill out this form, it is going to make me input that. Now if I keep this as nullable, that means we could still potentially pass that value into the database as null, using the API. I could turn that off if I wanted to. I could also make sure this is unique as well, so that we don't generate this a million times. And for the display, we'll just show the raw value. Right? Is that it? Let's add a little icon just to make this a little fancy. This will be a link. Great. And now we have a website. And let's go ahead and make name required as well. We're gonna fill both of those things out. Boom. Amazing. I can go in. I can now add a name like Directus and a website, Directus.io. Save. Great. Notice that perfect little icon right there. It looks great. Right? I can even oh, no. I was gonna say I could point to it. I can point to it. There it is. Alright. So now we have this, like, data model. It's amazing. There's two fields here. This is really underwhelming as Bryant. Alright. So we've got an organization in our back end. Let's switch round to the front end, and let's start, like, fleshing something out. And this is where I will say, like, POC generation, AI has got an incredibly great for this, especially with, like, UI libraries like Shad CN and Tailwind for styling. It's gotten really good at creating components and things like that. So, what are we going to what are we gonna generate here? Like, what are we pitching? What are we what are we selling? Coffee mugs. Let's do something super developy developer ish. Maybe like a a seat cushion. Maybe we're gonna sell seat cushions. I'm hopefully, I look younger than I feel, but I've got three little girls. I've got a back that aches every day when I wake up. So maybe that's what we'll do here. We're gonna generate some seat cushions. Alright. So, when I think of the front end here, let's create a page. So let's create a new route for the org. Organization. I'm gonna wrap this in brackets there. Great. We'll do v base setup, script setup. Let's change that to length. Yes. And this is giant text, so that's a good thing. Alright. We got an organization. We're gonna fetch that organizational data. And behind the scenes, I've used this in so many videos. I'd never take the time to explain it. Basically, I'm using the Directus SDK. I've just got a quick little plug in as far as Nuxt goes. We're gonna pull the direct as URL from the configuration. We're gonna create a really simple direct as client, and we're gonna provide that to the Nuxt application. So the first time this application spins up or, you know, when it loads, it creates that instance for us and makes it globally available through something like this where we could say, Directus use Nuxt app. The other thing that I'm gonna do here, I'm gonna import read items from the Directus SDK and then we're gonna fetch this data. So we'll say data, that's gonna be organization. No thank you AI. Wait. We're gonna use the async data composable from Nuxt, just a handy little helper for caching. We're gonna call this organization organization. We're gonna get that organization from our routes, route.params.organization. No. We don't want that. We're going to do it this way, route.params.organization. We're gonna do direct us dot request. Yes. Direct us request. Read items, and we're just gonna wrap that. And do I have some nice formatting? Great. We got some formatting. Okay. So what are we doing here? We're looking for a slug. We don't really have a slug set up yet. So let's just do an ID. And maybe we do wanna set up a slug, maybe we don't. Route dot params ID. You'll notice this filter syntax here. Directus has a ton of different filter rules that I can use to create deeply nested or crazy queries where I can fetch everything I need in a single API call. And then I'm just gonna go in and log this out on the page. Is this possible? Let's go into Directus and just make this process easy for me as well. There is a button links option here which is just presentational. And we're gonna say view on website. Give it a label, arrow up link. Do it is there like an external link? Arrow. Let's just go arrow caret ray. Okay. And we're gonna say HTTP, we want the URL local host 3,000 is what we're running on. And then we're gonna run this at the ID. Great. Well, it's safe. Now I should get a button on this page where I can view on the website. And lo and behold, it looks amazing. This is the most beautiful page ever. We're just gonna go back in and edit our permissions. So by default, Directus keeps everything secure, keeps a tight lid on everything. So this public policy that we have doesn't have any access to our data. So let's go in and add read access for organizations and let's let's set files as well in case we decide to upload some pictures of our amazing seat cushions that we're going to sell. And now if I refresh the page, we could see, great, I have a name, I have a website. Alright. That's kinda underwhelming. Not great. So let's have it design. Let's retray, here. We are going to create I'm gonna use Claude's sonnet, and let's create a landing page. You are let's let's back up a minute. What do we want our landing page to look like actually? Right? So we probably got like a hero. We've got a features for our seat cushions, seat cushions, hero section. And then we've got like a like a buy button. Buy button. Buy now. CTA. Alright. So that's gonna be the structure for our page. Here we go. We are going to flesh this out. You know, we probably want to you are are we gonna, like, one shot this? Let's see. Well, if I do this, you are a expert I I don't think you really need to do this because it's picking up up whatever I have, but create a hero section component for a online store that sells seat cushions to developers. A beautiful, well designed view three component using alright. So one of the features I've been experimenting with in cursor is this documentation feature. This starter has like the Nuxt UI library built into it. Which version am I actually running? Did I upgrade this to the latest one? Yeah. I'm running the alpha version. So we're gonna use those docs and tell when CSS. We'll see what we come up with here. Let's create a beautiful new component. You page hero. I don't know I don't know that I have any of these actually. Just you don't use our Nuxt UI components after all. Okay. So we can see what is coming up with hero section. We got a feature, ergonomic design. Yeah. I'm kinda curious to see what it comes up with as far as, like, the data model around this as well. We'll just go ahead and flush this out for it here while component section, features. Oh, this is running kinda slow. New collection, blah blah blah, right column. Yeah. It would probably even be easier just to go into, like, Tailwind UI and find some of these components here. They've got some nice ecommerce components. Let's look at it like a hero section. Alright. Let's just we'll just see what it came up with first and accept this. We'll go back to our organization. We'll insert this component in here. This is our hero section. Great. Cushion. JPEG. Is there a cushion dot JPEG? Hero section. Where is this cushion dot jpeg cushion hero dot jpeg. Alright. Let's bring in one of our first AI big guns here. We'll go to replicate, and I'm just gonna use our flux model here. It will create a product image of a futuristic seat cushion for developers if I could actually spell, that'd be dangerous. Developer's seat cushion, developer's desk chair. I don't think punctuation is hugely important here. Let's see what we come up with. For 6¢ an image. What are we gonna get out of this? That looks pretty pretty wicked awesome there. Right? So we're gonna download that. Let's dump this into our assets folder, and we'll call it seat cushion dot JPEG, and we'll just update that here. Alright. So this should be assets, images slash seat cushion. And oh, what have we got here? Something weird is going on. Better comfort for better code. There we go. Alright. Now we're getting somewhere, except this looks really odd with our price badge. Something's going on there. Let's just comment that out. There we go. We got our seat cushion. This is great. Alright. Now let's have it designed something else. Right? Let me just shrink this down just a little bit. Feature alright. So we want this to be our headline. Right? So let's flush this out a little bit more. This is gonna be our headline. This will be our description. I'm just trying to think through like our what our props are gonna look like for this. Define props, features as a feature. Great. Learn more, shop now. Better comfort for better code. This is a new collection. Badge. Alright. So we'll just stick that in our props. Now we got badge, got features. Do we actually want the icons we can. I think I've got the Nux icon library built into this so we could simplify this, like hero icons. There we go. I don't know what other hero cons we have here. Shopping bag, check circle. There we go. And then we'll, like, flesh this out. Where is that icon at? Icon. Feature dot icon. Yeah. So we could ditch all of this and make this a little bit cleaner. We'll keep that. Right? You icon. Name equals feature dot icon. We'll just ditch this, ditch that, clean this up a bit, and we should still have our icons. Great. And then we're gonna move out features out of here, we'll put that into this. So for now, we'll just say here's our data, our data. We'll drop our features here. Sometimes it is frustrating as I'll get out to properly just navigate using these AI tools. The tab tab tab is it's nice when it works well. When you have all these extensions included, it does get a little bit messy. Let's check on time. Where is Figma? Thirty seven minutes. Alright. Better get to it, g. Alright. If I hit refresh, are we actually going to let's call this data, and then we'll go back into our hero section. We'll just wrap all this up into data. There we go. There we go. Data dot badge, data dot headline, shop now, learn more, data dot features. Okay. So maybe that is a little long as far as a headline. What do we have previously? Yeah. Alright. Comfort for better code. There we go. Okay. Now, this is our data. Right? This is our data structure. Maybe we tell this to generate. Alright. So how are we gonna generate the actual code here? Like, how are we gonna generate the personalization of this specific page? Let's just ditch the code part. There's our website. Looks great. How are we gonna personalize this? So for that, let's kick off a Directus extension, basically. Basically. Yeah. Nothing basic about it. So what I'm thinking we'll do here, we'll set up a custom Directus extension so that whenever I am adding a new organization, it will create that data for us. So let's also do this where we'll go in and we'll call this just page data. Page data. Page content. It doesn't really matter what we call it. I'm just gonna set it to be JSON. That's what we're gonna ask back from the API. Alright. Let's kick off this extension. So I'm inside my Directus directory. We're probably gonna go into CD extensions within that. And then I'm gonna go n p x create direct us or create dash direct us dash extension latest. And I am going to create a let's do a custom hook. This is generate personalized page, custom landing page, AI landing page. We use TypeScripts. We'll go ahead and install the dependencies. And one of the things that I've got set up here inside my Docker Compose for this, obviously, like, I'm I'm I'm creating a volume for the extensions here so we could pull those in. But I'm also somewhere in here. I have extensions auto reload set so that whenever we build this extension, Directus is going to look for that and reload this extension or at least it should. So we'll go into CD AI landing page, npm dev. Alright. So let's actually open this bad boy up and see what we've got here. Items dot create. Okay. So now I'm just quickly going to cheat a bit and pull up the direct to stocks. Hooks, custom API hooks. So what we're looking for here is after this item is created, and I think the structure for a collection itself is a collection name dot items dot create. This is what we're gonna do. So our filter will look something like the or our action here will look something like this. We don't necessarily need this. Alright. So we're gonna say organizations dot items dot create, organization created, and this will be async. And what this is going to receive should be alright. We're gonna get the context here. We can see what that context is gonna look like. That's how we're gonna initiate some services. What does the action receive? Right? The action receives the event name and a callback function that is executed whenever the thing is submitted. Alright. Extensions guys. Do we have one on hooks? Use hooks to validate phone numbers. Items that create input collection. So we're gonna get the payload, I'm assuming, and the content, item context. Let's see just kinda what we get here. Extension's changed. Okay. Test httpstest.co. If I check my Docker logs, I cannot import. Let's just stop and spin up the Docker container one more time. Make sure we're actually getting that. Okay. I could see that extension being initialized. Oh my gosh. That gets ugly, doesn't it? Alright. Let's try this one more time. We'll delete testco, test Co. Are we seeing any console log? I'm not seeing any logs here. Async, define hook. Extensions reloaded. Organization oh, duh. You gotta use the right right syntax, Brian. Frustrating to be under the clock. Testtest.co. Hit save. Okay. So now we're seeing some stuff here. Great. Alright. There's our holy moly. Alright. So that is our there's the context that we're getting there. Organization created. There's the payload that we're receiving. And then we're getting the item context. Okay. Great. Alright. So what we're gonna do, what do we want to actually happen when this is going to pop off? There's a third party tool that we're gonna use for this called FireCrawl that I've been testing. Right? So let's try this. We're going to add Firecrawl. Js. Alright. So we're gonna go to our extension that we're running. I'm gonna install this library from FireCrawl. And we're gonna go ahead and one of the other things that I'm gonna do, we're gonna call this the organization's service. We're going to extract that out of the context. Const request. No. It's gonna be the services out of the context, then the item service through the services. And we're gonna call new item service. Great. And this is gonna give us the ability to update. Right? So we're gonna initialize that service. We've got that installed. Now let's copy our code here. I'm hoping this is my live API. New FireCrawl app. We need to import that. Scrape a single web page. Got it. We'll move that down below our item service, and we're gonna get the scrape result. And for now, let's just oh, I can already see the typo happening here. That's gonna be frustrating for me. And we're gonna say await updated org equals await organizationservice.update.one. Data, does that need to be wrapped in data? I don't know. Scrape content. Alright. We're gonna return the updated org, and let's wrap this in a try catch as well. Try catch. Console dot error. See if we get some formatting. Nice. Okay. Let's fire up the dev server for this and cross our fingers. Right? The other thing I need to update here, we're gonna have to go in and figure out which tab has all of my browsers in it. Maybe we just move that to a single browser for now. And we're gonna add a new field for this. We're gonna call it, scraped result. Scraped result. I don't know what this is gonna be. I'm assuming scraped content. I'm assuming this is gonna be JSON data that we're gonna get back. I guess I could look and see. Success data. You know, we could also just store the markdown. That might be easier. Where do we go? Scrape content. Let's delete that. Where does this store the markdown? Scraped. Scraped content. Alright. So according to FireCrawl, if we do this correctly, we're gonna get something like this. So with the scrape result dot data dot markdown. Fingers crossed. That's what we're gonna get. We could also just console log that to verify scrape content, scrape result. Alright. Let's go for it and see one last thing that I wanna do here. This is not, using, like, any of our accountability, so do not do this in production. But we'll just allow the public to create and update all the content on the underlying instance. Great. Love how secure I am being. I'm sure everybody on my team would be thrilled. Alright. So let's just go to Directus as the example. HTTPS, Directus.io. That's our website. Oh, I've already got directis.io hard coded in there as well, so we're probably gonna want to pick that up out of the payload as well. Can we still see that payload? No. Let's do this. We'll see the payload.website URL. We'll see if this actually works. Fingers crossed that it does. Scrape content. Save. What happens on this side of it? Around URL must be a top level domain or valid path. So I'm getting that error there. It's fine. Should set this up on update or create. Directus.io. Save and stay. Do we see what we get back? Is this thing still running in the background? What are we doing here? I got no visibility into what's happening here. Let's try it again. Directus. Directus. Io. Hit save. Must be a failed to scrape URL. URL must oh, duh, dummy. This is where it helps to define types so you don't make these same silly mistakes as I have. So I've called this field website. I was calling it website URL here, so not actually passing any website. Cannot read properties of collections. So there's our scrape results. Do we see the markdown? There's our markdown. Cannot update one. What am I doing wrong here? We're not updating the schema. We need to extension service, access items. Yes. Here we go. This is what I actually need to see. Alright. So we got our context. We're gonna do services get schema. We need that schema. And we're also gonna need the accountability schema. So in our item service definition, we don't wanna do this. We're gonna do this. Equals await. No AI. No. We're gonna await git schema. So we're gonna get the schema from our context and on the accountability side, accountability. That'll be accountability. If we just destructure that, I think that gives us what we need. Let's go back and try it again. Love hacking this stuff together on the fly. No pressure. Directus directus dot I o. Hit save and stay. Let's just watch over here and see what happens. Fire crawl seems to take a little bit, so that is forbidden. I don't know what we're getting over here. Can I just leave off the accountability object? How many times am I gonna create this before I just do update? Directus Directus dot I o. Save. Probably burning through these fire crawl credits as we speak as well. That that that that that, forbidden. You don't have permission to access this. Why is that not the case? What is the alright. So let's stop loading that. Let's just say console dot log the payload. Actually, I think it's I goofed up, didn't I? What are we gonna be receiving here? It's not gonna be it'll be payload dot key. Payload dot let's just log the whole payload again and see. Just comment out this. Comment out. Directus Directus dot I o slash payload key. Okay. Duh. You're smarter than that, Brian. Alright. So we need this key. That's what we're getting is the key payload dot key. So here, payload dot key. There's the event. There's the key. There's the organization of the collection we already got. Alright. Let's keep rolling and maybe try to actually complete this one. Directus. Directus dot I o. Save. Can we actually get the scrape content? Can we actually complete this on time? Can we do any of these things in any sort of actual working fashion? Is this actually gonna work? Will we know? How do I know? Console.logupdatedorg. Directus. Directus I o. Save. There's the payload. Fire crawl really takes a bit. Status code, 200. Updated org. Is this actually gonna see what we want? No. Of course not. It's not gonna update the right one. Data dot scraped content. I'm sure this is what we need to do here. Right? Update one. Let's look at the services. Update an item. Yep. It's not inside data. So I don't need that to wrap. Gosh. I'm just eating up all the time. I wish I spent more time with hook extensions. Super powerful because you're basically just updating the underlying Express app instance anyway. But alright. So now let's reach for anthropic. If we hit refresh, do we ah, still not getting the data that I want. Right? It's saying that it's updated that org. Scrape result. Scrape result scrape content. So they tricked me. It's not it's not as it shows. It's actually not there's actually no data attribute there. It's basically just scrape result dot markdown. Alright. Last freaking time for the scraped content, Directus Directus dot I o. I was not planning on doing this much troubleshooting. Behind the scenes here, I'm gonna go in and just create a API key for anthropic. Hundred apps. There's my API key. Updated org. Did we actually get what we want? Holy moly. We got some scraped content in here. Okay. Alright. So now the next step in the puzzle is going to be call the Anthropic API to generate a landing page for the organization with the JSON schema, page data. Alright. So now this is one of the things where I've been leaning on the Vercel API, or AI SDK. So I'm just gonna install it here, p m p I. This is just AI. And they've got a way. The last time I checked, the Anthropic does really well. Like, SONNET does really well at copywriting. But it doesn't have the JSON mode that the ones like OpenAI have. So I'm just gonna go in and look for provider management providers. Where are my providers? Providers and models. Looks for the anthropic provider. I'm gonna add it as well. And let's see what we can get here. Import anthropic. So I'm gonna import this guy. And then we're also going to do what? Create anthropic. We'll do that here. Now what I would normally do is this context object should have an e and v variables as well. API key. I'm not gonna do that here because I am worthless, basically. No. We're eating up a lot of time. Base URL, API key, generate text. We want to generate objects. So there's our anthropic client that we're creating. Don't mind that API key. Now let's define what do we need? We need Zod as well, p m Zod. And we're going to use was it generate object in here? See if we can find it. Generate object. Alright. Generate object from AI. Okay? Now what we're trying to do is just basically, we're gonna define a Zod schema of the object that we want, and that's gonna be our hero section. Right? Zod schema. We're gonna lean on the props. There we go. Okay. So now I'm just gonna copy this wholesale. We're gonna stick this in our custom hook. Again, not being careful at all about separation of concerns or cleanup. So there's our Zod schema. We want the object to return hero section schema. Alright. So now let's look up what we actually want this to how we're gonna work this. Generate object. We don't want testing. There's gotta be a better during text, during structured data. Alright. So our model okay. So we want the object equals await generate object. I'm hoping this is right. Fingers crossed with AI. Generate a landing page for the following organization with the content. Okay. For the organization payload. Name, the following content. You are an expert copywriter, and your job is to generate amazing landing pages that are personalized for our target clients. We sell the best desk chair cushions in the world for developers. Schema. There's our schema. That's our Zod model. Okay. When we get that back, what are we gonna do with the object? We are going to update the org, updated org two. It's fancy, fancy, fancy. Okay. Alright. How we doing on time? We got nine minutes to try and figure this out. Let's just PM, PM, dev this bad boy up and see if this is actually gonna come out at all. We're rebuilding this extension. Dun dun dun dun dun. I'm gonna nuke this entirely. Let's try it again. Let's see if it works for directus.io. We hit save. I could check the logs on Docker. Create anthropic is not defined. Anthropic. Response. Anthropic. Where is that? Providers and models. Anthropic provider. Create anthropic. Import the default provider. Oh, no. We could just import the what am I doing? Create create anthropic. Organizations. Oh, no. Directus. Duh. Directus .I o. Hit save. There's the payload. Obviously, it would be great to have visibility into this. And one tool that I've been using as of late is ingest. Great tool for something like this, generated hero section. This is gonna be nice, doc. Okay. Alright. So now before we reveal what it generated, let's just try to make sure this is actually gonna work. So instead of the data here, we're just gonna use the organization dot data dot page data. Organization dot page data. Generated hero section. That looks nice. Organization dot page data. Dun dun dun, the big reveal. Close that guy out. No. Status message. What is the status message? Did we actually get the page data generated? Okay. We got the page data generated. It looks like it's coming back with data here as well. So we should probably fix that in our hook. Page data page data is what? Object dot data. But for now, what I'm gonna do, I'm gonna just strip this out. Right? Great. And we refresh. We refresh. We refresh. No. What is going on? Organization. I'm excited for this, and I'm hoping we could get this to work. Right? Why am I not getting any of my data? Organizations. Access policies. We can see that organize oh, that's because it's not the right organization. Alright. I was using the old organization. We blew that one away like a hundred times. Right? This is probably where you'd wanna slug. Five minutes left. What do we have on the clock, Johnny? No. It's not working. Boy. Alright. There's our page data, organization dot page data. There's the scrape content. Why are we not seeing that? Right? Organization. Let's try it now. Status code. What is the error that we're receiving? V if organization dot page data. I could see the page data, man. Organization. Oh, duh. Gosh, man. What an idiot. Alright. So I I've done this on another episode as well. We're gonna transform this to get data. Where is this at? Is it here? Transform data. I don't know if this is how it functions. Error results, organization. I'm not sure the exact syntax, and I'm not gonna mess around with it. So we're gonna do this. It's gonna be organization. Boom. Bam. Directus plus Comfy Desk revolutionize your development experience, Seamless integration. Integrate Directus' flexible back end with your favorite front end while sitting comfortably on comfy desk cushions. Tailor your with adjustable cushions. Perfect. Alright. So now we have landing pages that are personalized for a specific client within, like an AI workflow. Right? So let's try this with some other company. Let's say OpenAI. I guess just to to pit these guys against each other here is this OpenAI.com. OpenAI Com. We'll hit save. And hopefully, this all runs, works nicely on the back end. There's the scrape result. You know, if I wanted to get really fancy, obviously, I could take this to the extreme here. But let's look at OpenAI. We'll view this on the website. What is going on? For noose proper next string, OpenAI. Okay. This one is not super strong. Right? Not sure what the scrape data result was. It looks like Firecrawl didn't do a great job with their website. Right? Let's try Apple.apple.com. Save. And we'll see what it comes back with. There's the scrape content. And, again, this is just coming back with markdown. You know, I might have better luck with, like, actual JavaScript parsing or something like that. But boom. Elevate your comfort coding experience with Apple inspired comfort, the iCushion Pro. Anyway, that's it for this episode. We've achieved this. I'm pushing a stop button on it. One minute left, we have basically fleshed out this personalized landing page where we add an organization, we use AI to scrape it, we then we generate content using LLMs, based on a specific schema, and then boom, we've got a personalized landing page that we could share with them. As far as next steps for me would probably be, like, setting this up on, like, a pretty URL or a slug, you know, adding this to some type of outbound, like, outreach campaign potentially. That's it though. It's amazing what you can put together in just an hour with tools like Cursor, Directus, Nuxt, Anthropic, all these different things come together to create stuff that's fun to build. I'll catch you on the next episode. Peace.","e7c271ec-4424-48b6-9063-d3afa4ac3fe4",[1146],"6f802ad1-c2aa-47f4-8770-e73bc9a5fc41",[],{"year":399,"number":1060,"id":1061,"episodes":1149,"show":1150},[1063,1064,1065,1066,1046,1067,1068,1069,1070,1071],{"title":322,"slug":323},{"id":1069,"slug":1152,"vimeo_id":1153,"description":1154,"tile":1155,"length":1156,"resources":12,"people":12,"episode_number":11,"published":1051,"title":1157,"video_transcript_html":1158,"video_transcript_text":1159,"content":12,"seo":1160,"status":19,"episode_people":1161,"recommendations":1163,"season":1164},"company-intranet","1059437289","Bryant takes on the challenge of building a company intranet portal for fictional company \"Initech\" (complete with Office Space branding). Watch as he implements Google single sign-on, creates team structures with reporting hierarchies, sets up permission-based content access, and designs an announcement system. Follow along as he transforms Directus into a fully-functional corporate portal where employees see only what they're supposed to—TPS reports and all.","d63b665c-8bce-4c54-99fe-2783f1b44a56",63,"Mission: Company Intranet","\u003Cp>Speaker 0: Welcome back to yet another episode of 100 apps, one hundred hours. I'm your host, Brian Gillespie. This is the show where we try to build or rebuild some of your favorite apps in one hour or less or get publicly shamed trying. Alright. So if you're new here, there are only two rules.\u003C\u002Fp>\u003Cp>Sixty minutes to plan and build, which always results in fun and chaos. And the second rule, use whatever you have at your disposal, AI tools, past projects, random packages off of the Internet, whatever. It's fair game. Alright. Let's dive in.\u003C\u002Fp>\u003Cp>Today, we are going to be building a company intranet portal thing. Listen. This was honestly a suggestion from, some of the AEs on our team. Love those guys. They're amazing.\u003C\u002Fp>\u003Cp>The spec for this, very fuzzy. Right? I I don't even know what is in a company Internet portal sort of thing these days. I do know excuse me. I do know that I don't think I ever enjoyed using one.\u003C\u002Fp>\u003Cp>So we're gonna try to create something in an hour or less that is helpful and actually decent to use. Let's dive in and hit the clock. Alright. Sixty minutes. Usually, let's kick this off with functionality.\u003C\u002Fp>\u003Cp>Right? What type of functionality do we want out of an intranet portal? And we're gonna do single sign on for sure. Right? If we're using something internal, we want single sign on maybe through Google or Microsoft or something like that.\u003C\u002Fp>\u003Cp>We're gonna set that up. Yeah. We wanna be able to track team members and show org structure, you know, maybe provide helpful links. I feel like maybe that could be like a Google Doc, but, you know, we're we're doing the fancy version of this. And then what else do we're gonna have?\u003C\u002Fp>\u003Cp>You know, maybe I wanna be able to send messages to entire team. Great. Okay. So that is what we're looking at as far as functionality. Let's kind of discuss our data model.\u003C\u002Fp>\u003Cp>Right? We're going to need honestly, there's new shapes there. Figma, come on now. Okay. I don't I don't need a tour on the new shapes.\u003C\u002Fp>\u003Cp>Thank you, though. Alright. Getting derailed by the tools already. We're, like, two minutes into this. Alright.\u003C\u002Fp>\u003Cp>So, obviously, we are using Directus on the back end here. That's gonna give me a lot out of the box, like, user permissions, like users. Right? So we're gonna have users of the application. Those are also gonna double as team members.\u003C\u002Fp>\u003Cp>So one shot, one kill here. You know, users comes with emails, passwords, first name, last name, job title. We're gonna probably want to add, like, reporting structure, direct reports, reports to, something like that so I can establish those relationships. We probably want, like, a team. So we got different teams within the organization, so that'll be a separate one.\u003C\u002Fp>\u003Cp>Why is that text so small? Oh, too big. Too big. Okay. Alright.\u003C\u002Fp>\u003Cp>So we got teams. We got users. What else do we have? You know, maybe, like, pages or resources. I guess really fuzzy.\u003C\u002Fp>\u003Cp>Right? And this is the part of the show where I draw these amazing arrows that that FigJam allows for. So k. Looks great. You know?\u003C\u002Fp>\u003Cp>And then maybe there's a relationship here where some team members have access to certain pages and some don't, that sort of thing. Let's see what we can get done. Right? Alright. So what have I got set up already?\u003C\u002Fp>\u003Cp>Basically, absolutely nothing. I've got a example project or a a Docker Directus instance running here. I've got, you know, just kind of like a standard Docker Compose setup. Don't have much config in this. No collections setup whatsoever.\u003C\u002Fp>\u003Cp>Alright. So let's tackle first thing on the list. Right? I want to set up single sign on. So if I log out of this instance, I see email and password, log in.\u003C\u002Fp>\u003Cp>I wanna do single sign on for, like, Google, for instance. Alright. So how are we going to configure that? Well, inside our documentation, we've got a list of, like, the configuration that we could set up. So we're gonna have, like, an auth providers.\u003C\u002Fp>\u003Cp>So that's a comma comma separated list of auth providers. The driver that we're gonna use and then a provider mode, we'll probably use session. And then for Google, I think the recommendation from our team is to use OpenID. So we've got, like, an OpenID setup here. Now do we have, like, a Google SSO single sign on?\u003C\u002Fp>\u003Cp>This this is the single sign on dock. Do we have a, like, standard config? Somewhere where we could see, like, a standard config for this. Yeah. There we go.\u003C\u002Fp>\u003Cp>Alright. So we could set up multiple auth providers, but this looks like the the pretty standard config for, Google. So I'm just gonna copy this wholesale. Right? I'm gonna go inside my Docker Compose, you know, or I could stick this in my ENV file if I wanted to.\u003C\u002Fp>\u003Cp>I'm not gonna open that because I've got some other secrets in there. As far as the IDE I'm using, this is Cursor. I've been using that this entire season. I found it pretty helpful and especially for quick POCs like this. Alright.\u003C\u002Fp>\u003Cp>So now we got some config in here. We're gonna have to go and create a client inside Google as well. So let's go to Google Google SSO single sign on, console. Cloud. Google, I think, is the let's see what we got here.\u003C\u002Fp>\u003Cp>Log in as Directus. Alright. Let's get started configuring. I've got my first project, so you're probably gonna have to create a project if you don't have one. Let's get started.\u003C\u002Fp>\u003Cp>We're gonna call this 100 apps, one hundred hours. The user support email, that's gonna be me, Brian at Directus. This will be an internal audience. So, my knowledge of Google auth is very minimal, But, obviously, internal is gonna be users only within our organization, which for this works well. Right?\u003C\u002Fp>\u003Cp>When I'm at directus dot I o. Great. I agree. We're going to spin this up. Okay.\u003C\u002Fp>\u003Cp>Nice. Nice. Okay. We're gonna create an OAuth client. This is a web application.\u003C\u002Fp>\u003Cp>Let's call this Directus. Alright. So what do we need to do as far as, like, our JavaScript origins and our authorized redirect URLs? So I think we're gonna have to do something like this where we have local host 8055 as far as our origin. Let's see if we can search our doc single sign on.\u003C\u002Fp>\u003Cp>Here's a entire guide on this. So you could just search that up. Right? Internal authorized domains. Alright.\u003C\u002Fp>\u003Cp>So we're gonna authorize JavaScript origins. That's gonna be it's not required, but that's gonna be our directus server address. And then the authorized callback is gonna be slash auth slash login slash Google slash callback. And that's because of the auth provider key that we're providing to the auth provider is Google. Alright?\u003C\u002Fp>\u003Cp>So that's how we reference this in our actual config. Alright. So, let's add that. That'll be HTTP local host 8 0 5 5. Great.\u003C\u002Fp>\u003Cp>We're gonna hit create. I certainly hope it doesn't take as long as what it's saying. Alright. So if I expand this, over here we're gonna get a client ID. We're gonna copy this client ID.\u003C\u002Fp>\u003Cp>That's gonna go here. Great. Then we got a secret. Don't steal my secrets. We're gonna paste that here.\u003C\u002Fp>\u003Cp>And what else do we need? Let's add an auth Google mode. Google mode. We're gonna make that session. The identifier key is email.\u003C\u002Fp>\u003Cp>I'm also gonna do allow public registration. Google allow public registration. So we want anybody who comes through our organization to be able to register for this. And one of the other things that we could do, I'm just gonna copy and paste. We probably wanna add, like, a default role setup as well.\u003C\u002Fp>\u003Cp>We'll get to that in a moment. Default role ID. Let's just actually dive into that right now. Alright. So great.\u003C\u002Fp>\u003Cp>We are going to sign in. If you remember the admin password, that is okay. So now we got this. Let's go in and create we've got two roles by default. These are out of the box for Directus.\u003C\u002Fp>\u003Cp>We've got public. This role controls, you know, like, what is available without authentication. And there's also a public access policy. So the access policies are, you know, just groups of different permissions, and then you can add many different access to a role. I love the granularity here, and it's super easy to maintain, like, a complex permission system between the both of these.\u003C\u002Fp>\u003Cp>What we do wanna do here is basically let's create a new role. We're gonna call this a team member role. Great. Okay. Alright.\u003C\u002Fp>\u003Cp>So we'll just save that. I'm gonna grab the primary key for this. That's what I'm gonna stick here for the role ID. That's the role that we want folks to have. As far as the policies that we're gonna give them, for now, let's just give them full admin access.\u003C\u002Fp>\u003Cp>They could change anything. We're gonna scope that down eventually, though. Alright. So the next thing I'm gonna do to test this out, right, I want to I I just want to stop my container. I'm gonna spin that container back up, and I'm gonna open this in.\u003C\u002Fp>\u003Cp>Let's do an incognito window with the admin login. Admin and example. Password. Great. K.\u003C\u002Fp>\u003Cp>And now if I log out of this one because I'm I'm already signed in to Google for everything here. Alright. If I hit bryant at directus, create a strong password. Why do I have to create a strong password? Come on, dawg.\u003C\u002Fp>\u003Cp>Why is it asking me to do this? Use 20 characters or more. Come on, guys. What is it? What are we doing?\u003C\u002Fp>\u003Cp>I'm gonna do this off screen here. I don't I don't know why this is happening here. Google. Let's create a new password. 20 characters or more.\u003C\u002Fp>\u003Cp>Directus dot I o. Okay. Oh my gosh. Let's see if this is actually gonna work. Sign in to 100 apps, one hundred hours.\u003C\u002Fp>\u003Cp>The request cannot be processed because it is malformed, which is not good. Let's try it again. What are we doing wrong here? So we go back to our Google where's our Google application? Yeah.\u003C\u002Fp>\u003Cp>Sign in. Yes. It's me. Love it when all of this nice security tech gets in our way. What are we missing?\u003C\u002Fp>\u003Cp>Right? HTTP local host 8055 slash login slash google\u002Fcallback. Okay. See, this is nice because I've tried to log in to a different account, but I should be able to log in to there we go. So now I am in.\u003C\u002Fp>\u003Cp>Right? And we can see that Bryant Gillespie is created. This is this is really nice. Right? We could see the auth provider here is Google.\u003C\u002Fp>\u003Cp>There's the external modifier or identifier, not modifier. Now we've got single sign on. Right? And one of the other things that I could potentially do I think there's config for this if we take a look. Go into auth and SSO.\u003C\u002Fp>\u003Cp>I could disable the default provider as well if I wanted to so that you could only log in with single sign on. So if we do that, let's see what we've got. Auth, disable default, true. And then I'm gonna restart my container. Let's just log back out.\u003C\u002Fp>\u003Cp>And now there's only Google to log in. There is no email and password auth options. This will disable it at the API level as well. Super nice thing to do. Right?\u003C\u002Fp>\u003Cp>If if all you want to allow is single sign on. So, with that done, right, let's let's go in and brand this thing because we obviously need a nice brand. Huge fan of office space. If you're younger and you're watching this, you know, this is an older movie. It pains me to say that.\u003C\u002Fp>\u003Cp>But, let's use Inatec as the company here. So I'm gonna go into our appearance settings, the project logo. So I could just copy the image address, take full advantage of this a lot of times. So I'll just import the file from a URL. If I hit save, we could see that Inatec logo up there.\u003C\u002Fp>\u003Cp>I'm gonna use the color picker. Let's pick up, like, a Inateq blue, maybe, I guess we could call that. Right? We got, like, this pale blue color. I'm gonna set up the Directus default theme, and in the namesake of the show, I'm gonna steal from our simple CMS template.\u003C\u002Fp>\u003Cp>I've got, like, a custom theme that I really like, super happy with. So I'm just gonna steal that paste raw value. There we go. We might wanna swap this out. And one of the nice things about the theming in Directus is, you know, I've got all these nice keys for theming, but I also have the ability just to add custom CSS and do whatever I want.\u003C\u002Fp>\u003Cp>So in this case, we'll do that as well. And what else are we gonna do? I'm gonna fix this just because it's gonna aggravate me. Alright. So this is where that CSS comes in.\u003C\u002Fp>\u003Cp>I've got the module bar logo. I'm gonna do something like this where we have dot module bar logo, background. Let's just do white. And we're gonna stick important on that. There we go.\u003C\u002Fp>\u003Cp>Now we're cooking. And maybe we wanna add, like, a subtle subtle border there. Let's see. What is that gonna be? That'll be our navigation.\u003C\u002Fp>\u003Cp>That'll be the modules. That'll be the border color, which would be I don't know. Let let's use color mix in sRGB. We'll use the var theme. We're gonna use the primary color and maybe 20% white.\u003C\u002Fp>\u003Cp>F f f f. And let's set, like, a two pixel border just so we can see it. Alright. There we go. So now we got a little little border action to keep things separated.\u003C\u002Fp>\u003Cp>I mean, this is not like a super nice mouse over active color, but I'm not gonna get too far into it. Right? This is gonna be the Inatec company portal. And one of the things that's gonna aggravate me is just our font choice here. Like, DM Sans is a newer font.\u003C\u002Fp>\u003Cp>Maybe we just set this to Arial. Do I have that installed? Would it be Arial? Yeah. I think it is one way or the other.\u003C\u002Fp>\u003Cp>And then we can set, like, our, let's let's pick something, like, super corporate here. Yeah, Roboto feels pretty corporate to me at this point. We'll do Roboto. So if I just put this in quotation marks, this will go ahead and pull those font files from Google for you. So any Google font, good, bad, ugly, whatever you wanna do, you can include those when you're customizing these themes.\u003C\u002Fp>\u003Cp>Alright. So now we got Inatek. We've got Roboto. This feels very robotic. This is a sad company worked for fictionally.\u003C\u002Fp>\u003Cp>So now if I refresh our login screen, we've got this kind of action going on. Looks okay. Maybe we wanna spice this up a bit. Right? So we're just gonna put Bill Lemberg on the login page.\u003C\u002Fp>\u003Cp>So we'll go to appearance. The public background here, we're gonna import Lemberg. Yeah. We need those TPS reports, and, you know, I will just leave the Favicon blank. There it is.\u003C\u002Fp>\u003Cp>There we go. Right? So this is the it's beginning to shape up as a perfect company portal. Alright. And one thing I'm gonna do, I'm gonna turn this off, or we're not gonna be able to test, like, our permissions.\u003C\u002Fp>\u003Cp>So disable default. Let me just spin this back up so we can get, like, an email and password. Log in one more time. Sign out. Okay.\u003C\u002Fp>\u003Cp>So now I've got my email and password back. Let's dive into the next item on our list, single not single sign on. We just completed that one. We're gonna dive into tracking team members and showing, like, an org structure, providing helpful links, and maybe try to send messages to the entire team. Alright.\u003C\u002Fp>\u003Cp>So, we've already got users through Directus. Right? We've got our user library here. We could see that Brian logged in, etcetera, blah blah blah. Looking great.\u003C\u002Fp>\u003Cp>Let's add this team functionality. Right? And we probably want the ability to create new teams and adjust teams and etcetera. So let's just create a collection for this. I could also just adjust the users collection, which we'll do in a few.\u003C\u002Fp>\u003Cp>But for this case, I'm gonna create teams. So each team, we need a primary key field. I could use an auto incremented integer. We can use a manually entered string. You know, I mean, like, in this case, maybe we use a manually entered string because I'm not expecting to have a a ton of teams, and, you know, the way we reference those is probably not gonna change much.\u003C\u002Fp>\u003Cp>Alright. Do we need any of these default fields? I'm not really sure. I don't think so. So we'll just skip that.\u003C\u002Fp>\u003Cp>So we got a team. We're gonna give a team name. Pretty simple. We'll make sure that is required. Great.\u003C\u002Fp>\u003Cp>The ID, we can make sure this is like URL safe, so we'll just enable Slugify through the interface settings. And we've got a team, we've got a name. Now what we're gonna do is set up a relationship between we probably got, like, a a parent and child relationship between teams as well. Right? One team could roll up to another team, could roll up to etcetera.\u003C\u002Fp>\u003Cp>You know, we could classify things like a team lead, and then we're gonna have, like, an organizational structure where we have direct reports for each user as well. So let's set up that first one. Right? Each team needs a leader. We'll just call this team lead.\u003C\u002Fp>\u003Cp>So here, I'm using the direct us relationships. I'm gonna set up a many to one because I only want one leader within the team. Could certainly set up multiple leaders if I wanted to, but for the related collection here, I'm gonna use direct us users. And as soon as I hit on the right one, you'll see that kind of turn blue and lock in. I can also just go over here and hit the system collections and do it that way as well.\u003C\u002Fp>\u003Cp>We're gonna show a link, and I'm gonna open up Advanced Field Creation Mode just so we could see this. What I'm also gonna do here, I can create the corresponding relationship at the same time. And this is where data modeling becomes very important. Right? Because, do we necessarily want this relationship here?\u003C\u002Fp>\u003Cp>Because I could be leading, you know, I guess I could lead multiple teams. So we will add this relationship here, but, you know, if you need, like, multiple team leads, obviously, you're looking at, like, many to many. We're gonna display the direct as user, show the user in a circle. We'll set that up. So there's the team lead.\u003C\u002Fp>\u003Cp>And now if I were to go and check our data model for users, right, I should also have a Teams option here. Right? There's our Teams. So we can specify the do I actually want that? The we're gonna have Teams as another thing, like, teams that we're a part of.\u003C\u002Fp>\u003Cp>Let's delete this, recreate this relationship, one to many, and teams leading. It's not t lead teams leading. Yeah. Leads, teams, teams leading, I guess. Naming stuff is I say this on every episode, I feel like, but naming stuff is the hardest thing in programming without a doubt.\u003C\u002Fp>\u003Cp>Alright. There we go. We got the display table, and we're gonna show the name. Cool. Team lead already has an associated relationship.\u003C\u002Fp>\u003Cp>What are we doing here? Have I bricked this whole thing? Let's see what we've got. Can I get access to the relationships here? If I reload what is going on?\u003C\u002Fp>\u003Cp>Have I totally bricked Directus with this relationship? What have I done? People, what have I done? Permissions, policies, fields, direct us users, team leads. Now if I refresh, did this sort it out?\u003C\u002Fp>\u003Cp>What have I done? Alright. Teams. Team lead. I don't understand.\u003C\u002Fp>\u003Cp>Teams users leading does not exist. Let's just try down and up. I goofed up our relationship somehow. I think we can get this back. We can solve this problem.\u003C\u002Fp>\u003Cp>Now I would be remiss not to take this opportunity to talk about the data mirroring inside Directus. So, you can install Directus onto any existing SQL database, whether that's Postgres, SQLite, MySQL. Directus, like, bootstraps these name space collections as you see here, but everything else is just standard SQL. Right? There's nothing direct as specific about our team teams collection here.\u003C\u002Fp>\u003Cp>And as I go through and build these out, let's try this one more time. One to many relationship or actually a many to one here. We're gonna have a team lead. We'll pick our direct to users collection, and then in the relationship settings, I'm gonna add that one, teams leading. I hate that, but that's what we're rolling with for now.\u003C\u002Fp>\u003Cp>We're gonna create this relationship. Right? Now, we could see that that change is being mirrored here. There's my team lead. Amazing.\u003C\u002Fp>\u003Cp>Right? So I could go in. We could create a new team. We could do team America. Let's just call it bride's team or let's do marketing, I guess.\u003C\u002Fp>\u003Cp>Marketing. Great. Mister admin user can be our team lead, and then I can go in and add additional teams. Right? Product.\u003C\u002Fp>\u003Cp>Got the product team. Sales. Great. You know, we could get really fancy and add icons for all these teams. Let's Let's just check how we're doing on time.\u003C\u002Fp>\u003Cp>We got thirty minutes left. Let's keep cruising. Right? Alright. So we got team leads.\u003C\u002Fp>\u003Cp>Seems like enough. Let's go in and add our team members. Right? Teams members. Maybe add an icon, though.\u003C\u002Fp>\u003Cp>Team. Is there a group? This is a collective. There we go. Group.\u003C\u002Fp>\u003Cp>Looks great. Amazing. Teams. Okay. So now I want to you know, I could potentially sit in multiple teams, I guess.\u003C\u002Fp>\u003Cp>You know, obviously, you would hope that that you would have a, like, a one to one relationship within the team, but, yeah, I again, hey. Like, data modeling issue, do could a team user belong to multiple teams? I I'm gonna say yes in this case just because I don't know. You know, I maybe, like, you wanna use this as, like, a project team as well. So that's that's how we'll play this.\u003C\u002Fp>\u003Cp>Alright. Teams, we're gonna call this team members. Okay. I'm just gonna create a junction collection manually here. Like, Directus will create this for you if you want.\u003C\u002Fp>\u003Cp>I just get in the habit of wanting total total control over everything. I'm just gonna hide this guy. Alright. So now we're gonna link members to our team. And for that, we're gonna reach for our many to many relationships inside Directus.\u003C\u002Fp>\u003Cp>We're gonna call this members. The related collection is gonna be users, Directus users. Great. And I'm gonna go to the relationship tab. So on almost every episode, you'll see me jump into the advanced setup.\u003C\u002Fp>\u003Cp>Once you get into the basics with Directus, highly recommend it, because it gives me total control over the fields that are created in the junction collection. Right? Instead of using team ID, I wanna use team, and I'm just gonna use user here because I like to be OCD about certain things. Right? So now we're gonna add this teams field to direct us users so I can, you know, potentially use that in our permissions.\u003C\u002Fp>\u003Cp>And on all of these relational triggers, I'm gonna set these to cascade, right, because there's no sense in keeping this relationship in there. Alright. We could set up our interfaces, display related values. Great. There we go.\u003C\u002Fp>\u003Cp>We got our team members. I can go in and set, like, a first name, last name. I could even go in and add, like, an avatar here if I want to. Oh, avatar. There's like a little thumbnail helper.\u003C\u002Fp>\u003Cp>Oops. Let me just edit the raw value. You could see this is just the mustache syntax to populate these values, but inside the interface it shows very nicely. Alright. So now we got our team.\u003C\u002Fp>\u003Cp>Let's add some members to the team. I'm gonna add Bryant and Tess Tess to the marketing team. Great. There's our team members. I'm gonna give I gotta upload a photo here just to make sure I got something.\u003C\u002Fp>\u003Cp>Right? There we go. There's my ugly mug. There we go. So now I'm not seeing myself in there.\u003C\u002Fp>\u003Cp>We need to fix that as well. Here's where I can just copy these values. So I'll hit copy raw value. I'm just gonna paste that in the interface display template. So the difference between interface and display, the interface shows inside the form.\u003C\u002Fp>\u003Cp>Right? This is the interface. Here inside, like, the teams, if I were to look at the members, that's our display. So I wanna show the same thing. I could see those members.\u003C\u002Fp>\u003Cp>There's our team members. There's the team lead. Great. You know, I can prevent editing on all of this. Amazing.\u003C\u002Fp>\u003Cp>Solid. Right? Alright. So now, you know, we've got team members. Yeah.\u003C\u002Fp>\u003Cp>Let's add, like, our reporting structure as well with would that be within a team? This is where we get into, like, splitting errors. Right? In this case, I'm gonna add this to the user level. I guess you could add it at the the team member level, but, you know, let's not do that.\u003C\u002Fp>\u003Cp>So, anyway, I'm gonna go into our system collections here. We're going to create some new fields here. So you can already see I've got two fields. The system fields are locked down. I can't change those, but, I can add new fields to this collection, to this table in the database.\u003C\u002Fp>\u003Cp>So we're gonna add a, like, a recursive relationship here. So for that, let's just use the one to many, and this is gonna be our direct reports. Right? This is gonna be direct as users, and the foreign key would be reports two. Again, you know, naming structure here is hard.\u003C\u002Fp>\u003Cp>It would be great to have help on that side. But, alas, this is not live. I am just talking to myself. Alright. So there's our display template.\u003C\u002Fp>\u003Cp>We're gonna show a link to the item. If I want to, I can just, like, show this relationship. This is gonna be created. Reports to, direct reports. Pretty pretty straightforward.\u003C\u002Fp>\u003Cp>Right? Direct report. So you could see that it's created, like, two two or two fields here. And if I look at, like, my and, like, here's the teams leading. Here's the teams I'm a part of.\u003C\u002Fp>\u003Cp>And let's say, mister I this is gonna get confusing as I'll get a test direct report. Test manager. Okay. Alright. So I got two users here.\u003C\u002Fp>\u003Cp>I have I've got a test direct report. I don't see the other field, right, who I report to, so we can go in and edit that as well. I think by default, we hide that. Maybe I just wanna show that here. So we're gonna show reports to go in, adjust this.\u003C\u002Fp>\u003Cp>So those are my direct reports. Here is reporting to the test manager. And now if I go into, let's say, the test manager, let's take a look at that, there's the direct reports. I can also make this like a tree view as well. So if I go into users, on these recursive relationships, we can go in and set this up to use our tree view so we have this recursive relationship.\u003C\u002Fp>\u003Cp>I'm just gonna copy this, we've got tree view, paste that there. Now if I go to mister test manager, I can see my direct reports, but I can also see I'm the test manager in this scenario. I'm not Brian Gillespie. I can see the direct reports of that person and, like, all the way down the chain potentially. Right?\u003C\u002Fp>\u003Cp>So if I have mister CEO, the test manager rolls up to mister CEO, I can drill all the way down the tree, the tree, as they say. Alright. So now that we've got those relationships set up, you know, it would be nice to show, like, an org chart or something there. Let's focus on on bigger challenges at this point. Like, how can we send messages to all these folks?\u003C\u002Fp>\u003Cp>Right? We wanna provide some helpful leaks, send messages. You know, maybe we want to have a specific page. You know, as far as, like, a portal, you probably got, hey, like, pages, you know, like, struggle with what to name. Like, here's just, like, helpful content that we want to create.\u003C\u002Fp>\u003Cp>Right? So we're just gonna add these status, is this published or not? Great. Let's give this a title. And because this is internal, we we don't really need anything like a slug.\u003C\u002Fp>\u003Cp>You know, I could potentially add it if I wanted to, but, this is the page title. Here's the markdown content. Right. Great. This is our content, page contents.\u003C\u002Fp>\u003Cp>And then trying to think of the best structure here. Like, will we set up, like, different categories? I mean, this is kinda getting into, like, knowledge base level. You know, I basically, just trying to set up a structure here. Let's say marketing team should have access to that post.\u003C\u002Fp>\u003Cp>Let's say let's create another one. Marketing should not have access. Right. Alright. And let's kind of get into let's step into this as well.\u003C\u002Fp>\u003Cp>Right? Tested example, password. They've got our team member role. Cool, test at example, password. Alright, so this is what our other user is gonna see, And right now, the team member role, as we specified, has admin access, which let's go ahead and lock that down.\u003C\u002Fp>\u003Cp>Right? So let's create a new policy, team member I'm just gonna call this app access. So we're gonna give app access to the team member, and that should hide stuff like settings that we don't want them to configure. How we doing on time? Nineteen minutes remaining.\u003C\u002Fp>\u003Cp>Cruising. Alright. Team member app. Let's go back to our team member role. We're gonna remove this administrator policy.\u003C\u002Fp>\u003Cp>Alright. We don't wanna give total access. We want to give team member app access. Great. And I think I just locked myself out of the system.\u003C\u002Fp>\u003Cp>Fun for everyone. Right? But if I refresh over here where I'm logged in as our test direct report, now I don't see any of our content. I don't see any of the settings. You know, I can access the dashboard, see the file library, etcetera.\u003C\u002Fp>\u003Cp>I'm gonna dive back into table plus here. We could see there's our team members. I'm just gonna go in and quickly fix this goof up that I just made where I removed myself as an administrator. There we go. Alright.\u003C\u002Fp>\u003Cp>So now if I reload that, I've got access back again. And again, you can see this is all just underlying SQL. Nothing nothing fancy here. Right? Alright.\u003C\u002Fp>\u003Cp>Marketing should have access to this. Marketing should not have access. You know, I would probably go one layer deep if I had, like, a a lot of these and say, okay. You know, I'll group these by categories. And, you know, like, that way you could restrict access, but let's just do it this way.\u003C\u002Fp>\u003Cp>We're going to create another mini to mini teams with access to this content. Yeah. What else could we do? A Couple other ways we could do this. Right?\u003C\u002Fp>\u003Cp>Yeah. Let's just do the many to many to keep it relational. Teams. Team access. Teams access.\u003C\u002Fp>\u003Cp>Teams. Great. Show a link to the item. Alright. And we could see page, team access.\u003C\u002Fp>\u003Cp>That's gonna be the page. That's gonna be the team. This is pages that they have access to, and I'll show you why this is gonna matter in a moment. Alright. So title content, marketing should have access.\u003C\u002Fp>\u003Cp>We can say marketing. Great. Pages marketing should not have access to. That's gonna be product and sales should have access to those. Okay.\u003C\u002Fp>\u003Cp>Now by default, no permissions are set for collections. Right? So if I go into team member app policy, I've just got the system collections that they need. This is why I love the access policies because I can also keep those separate. And I could say can access pages or read pages or something like that.\u003C\u002Fp>\u003Cp>Right? I don't have to do any of these other options. I can just go in and say pages, pages team access. So by default, let's set these up first. Maybe we wanna be able to read the teams.\u003C\u002Fp>\u003Cp>That's fine enough, we can see all the team members. But when it comes to page access, right, what we want is to restrict that based on teamaccess.ID. No. Teamaccess.team equals or is one of current user. So this is a dynamic variable inside Directus, and, basically, we can go through the tree here.\u003C\u002Fp>\u003Cp>So this will get us our current user, like, who's logged in. We could go through teams and then Teams.team. I believe Teamsteam. Is that gonna be our junction table collection? Should be.\u003C\u002Fp>\u003Cp>Right? Here's our team members. There's the team. Alright. Let's just test this out and see.\u003C\u002Fp>\u003Cp>I don't see any nothing. Right? Well, we created that access policy, but we didn't apply it to our team member role. So here's our policies. Right?\u003C\u002Fp>\u003Cp>Team member app. They can just log in to the app. And now we're gonna give them access to pages. Hit save and say. And if I did my homework or if I did everything correctly, this should be all set up.\u003C\u002Fp>\u003Cp>Right? So there's marketing. I could see these. I can't edit any of them. And as far as the pages go, marketing should have access to this, but they should not have access to that.\u003C\u002Fp>\u003Cp>Bada bing, badda boom. That's all done. Right? Amazing. It just works out of the box.\u003C\u002Fp>\u003Cp>And the beautiful part about this is not only is this through the UI, right, where I'm logged in, this is respected throughout the API as well. So if I, like, do this API call, oh, I don't need to add API in front of it. Used to working with Nuxt. Items dot pages, there's that specific page. But if I just fetch items pages, again, I'm only seeing the one page.\u003C\u002Fp>\u003Cp>Whereas if I do it over here in this browser, you could see I've got two pages, because I am logged in as an admin, and admins have full permissions. Great. Alright. I'm gonna check this off as the helpful links. You know, couple ways we could send messages to our users.\u003C\u002Fp>\u003Cp>We've obviously got, like, an email. I don't think we have a phone field in here. We could add a field to track phone numbers for our team members. We could, you know, message them via Twilio without, say, exposing, phone numbers or private information to other members of the team. Right?\u003C\u002Fp>\u003Cp>Do we have enough time for something like Twilio? Let's just let's cover the email use case. Right? I want to craft a message to, let's say, team announcements. Right?\u003C\u002Fp>\u003Cp>And I want to draft this message. I wanna send it out to all of our team members. Too many n's in there. Thank you. Spell check.\u003C\u002Fp>\u003Cp>Date created. Cool. We'll generate a UID. Got it. And we got the message body, and we want to do what?\u003C\u002Fp>\u003Cp>Message body is just gonna be WYSIWYG. That's our content. We could just call it content, or, yeah, you might even do text. Fine. Keep it simple.\u003C\u002Fp>\u003Cp>Alright. Alright. And then we're gonna do, like, a mini to mini for our teams that we wanna message. Right? Maybe I wanna message individual teams.\u003C\u002Fp>\u003Cp>So we'll do the related collection. And, again, like, behind the scenes right now, Directus Us is crew will create those junction tables for me. Again, I just won't have the control unless I go into this team announcement teams, team announcement teams, team announcement. Let's just call it announcement. We're gonna call that team.\u003C\u002Fp>\u003Cp>Alright. And this is like a, an insane way to do this, but I I could really just create, like, a a flow. But maybe I wanted to, like, schedule this as well. Right? Okay.\u003C\u002Fp>\u003Cp>So here's our team announcements. I want to add a announcement for the marketing team. And, hey. This is an announcement from me. Alright.\u003C\u002Fp>\u003Cp>So, you know, maybe I'm, like, drafting this. You know, maybe I don't wanna send this right away. Maybe we track, like, a another time stamp for date sent, date scheduled, that sort of thing. But we've got this announcement. Right?\u003C\u002Fp>\u003Cp>I there's a couple of ways that we could do this as well, but let's create a flow for this. Right? So I'm gonna go in, let's say, send announcement, and we're just gonna manually trigger this. So once we have the announcement, we wanna send it out. We're gonna require a confirmation.\u003C\u002Fp>\u003Cp>Send announcement. K. And I could actually use, like, this flow to, you could build forms into this, right, which is another nice thing. I could say, hey, form field. This is a field.\u003C\u002Fp>\u003Cp>So when you're doing these manual flows inside Directus, it's always nice to have this ability to just stick a form field in there. And from here, hit send announcement. I can add something to this field, run this flow, and that will give me some data. Right? So here I can see the body.\u003C\u002Fp>\u003Cp>Here's the keys that we're receiving. So this is the actual announcement. And we could do something like this, where we're going to, what are we going to do? We're going to read the data. So we're going to get the announcement.\u003C\u002Fp>\u003Cp>Announcements. That's going to be your Team Announcements. The IDs here are gonna be trigger dot body dot keys. I'm just gonna hit enter to save that. That.\u003C\u002Fp>\u003Cp>I don't need to run a filter on that. And then, also, I'm gonna get the team members. Right? So if I look at my payload one more time oh, no. We'll get the we'll get the team members from the actual announcements.\u003C\u002Fp>\u003Cp>Right? So here, I'm gonna do something like this where I have fields. Fields allows you to fetch what you need in a GraphQL like manner even though we're using REST. So I don't necessarily have to reach for the complexity of GraphQL if I don't need it. So I'm gonna get all the root level fields with an asterisk, then I am going to get teams, and I want teams dot member dot star.\u003C\u002Fp>\u003Cp>I'm just gonna test that out and see. I think it's depending on how we set up that. We've got teams, team members. Oh, it's gonna be user, not member. Alright.\u003C\u002Fp>\u003Cp>Let's test this out. Teams dot user teams dot user dot star. We'll save that. Alright. Now, what do we got?\u003C\u002Fp>\u003Cp>Six minutes left. Let's crush this here. We're going to send the announcement. Send that out. Hit flows.\u003C\u002Fp>\u003Cp>We get send announcements. There's our payload error, UUID. Some kind of issue here. Oh, we're passing an array. Let's not pass an array.\u003C\u002Fp>\u003Cp>Let's just pass the array of arrays. Let's just pass the single array. We're gonna hit send announcement here. Test that out. Get our flow.\u003C\u002Fp>\u003Cp>Send announcement. This is an announcement. Okay. So we got teams. I'm not getting the actual team members that we need here because we need, like, teams.\u003C\u002Fp>\u003Cp>Teams. I should be getting the team data. Right? And I should be getting that. Teams.\u003C\u002Fp>\u003Cp>Why am I not getting that data that I want? Let's try it again. Send flows. Send announcement. We're gonna take a look at this.\u003C\u002Fp>\u003Cp>Okay. Okay. Team.team.marketing. And then can I get through users from there? Teams.starTeam.star.\u003C\u002Fp>\u003Cp>Team Members Star. Love waiting through a good data model here. Let's see what we get now. Test, flows, send announcement. Okay.\u003C\u002Fp>\u003Cp>So now we're getting closer. Right? Here's our team. There's our members. We got the users.\u003C\u002Fp>\u003Cp>Those are what we're trying to get to. Teams.user.star. This is gonna give me the emails of the team members. Once I get that, send announcement. Flows.\u003C\u002Fp>\u003Cp>Is this gonna give us the data of what we want? Yes. There we go. Alright. And, really, all I need here is the email.\u003C\u002Fp>\u003Cp>This is where I'm going to turn to AI for, like, three minutes worth here. Let's just open cursor. Write a function, a JS function that returns the array of emails for this sample data. There we go. First, we need to okay.\u003C\u002Fp>\u003Cp>Spit something out, friend. Data dot teams, that flat map. Okay. So there's a simple this is writing TypeScript, which we're not gonna be able to use in flows, but you do have the ability to run just JavaScript. We'll call this transform.\u003C\u002Fp>\u003Cp>Let's just pop in that function. We'll remove the types. Data data dot teams. Alright. So we're gonna do this.\u003C\u002Fp>\u003Cp>We're going to get the, what did we call that last one? This is just announcement. We're gonna get the announcement data. So here's what we're gonna do. We'll do, this doesn't need to be async.\u003C\u002Fp>\u003Cp>We're gonna say const, actually, we're just gonna return get emails, and the data is gonna be dot data dot announcement. So as you go through a flow, like, each step will append the result under the key. So this data object that we pass, I can access the announcement data for that step through this. And then the final step here is gonna be sending an email, which I don't think I've actually I can't remember if I got emails hooked up or not. We're gonna get this transform.\u003C\u002Fp>\u003Cp>We're going to reference that. So I'm gonna send the email. We're going to say transform, transform. And I'm gonna make sure that that is that should be passing an array. So we're just gonna use the raw value.\u003C\u002Fp>\u003Cp>I don't have a this is an important announcement. And then for the WYSIWYG content, we are going to do announcements dot text. Alright. We got forty five seconds. We're only gonna get one go at this probably.\u003C\u002Fp>\u003Cp>Let's see if this is actually gonna work. Run this flow. Send announcement. Is it actually going to run the flow to brian@directus.io? Did I configure this?\u003C\u002Fp>\u003Cp>Yes or no? Come on, baby. Come on. It shows my email is disconnected. Gmail.\u003C\u002Fp>\u003Cp>Come on. Come on, Gmail. Fifteen seconds. Are we gonna get an announcement? Are we gonna get an announcement?\u003C\u002Fp>\u003Cp>Are we gonna get an announcement? No. I don't think I've got email connected inside this actual Docker Compose file, to be honest with you. Too bad. Too bad.\u003C\u002Fp>\u003Cp>We didn't get to the last one. That sucks. Sending messages to the entire team, it should go through. And if I were on a Directus cloud instance, it probably would go through, but I don't have the actual email drivers conf like, the config set up for that. So, like, no emails are going through there.\u003C\u002Fp>\u003Cp>You know, obviously, I would wanna take the time as a next step to just go ahead and do that. So, if I go to, like, the direct us documentation and I look for email, you know, I would want to set up, like, my either my SMTP settings or Mailgun or SES, to actually send those emails out. But I will I I don't know if we're gonna call this a win or not because it did run this flow. It did give us the emails. We're not getting any errors there.\u003C\u002Fp>\u003Cp>We're just, yeah. I don't know. Let's let's just do the explosion anyway. That's my favorite animation of this whole series. So that is building a company Internet internal portal.\u003C\u002Fp>\u003Cp>Hope this was a helpful look at just how powerful Directus is. I will catch you on the next episode of 100 apps, one hundred hours.\u003C\u002Fp>","Welcome back to yet another episode of 100 apps, one hundred hours. I'm your host, Brian Gillespie. This is the show where we try to build or rebuild some of your favorite apps in one hour or less or get publicly shamed trying. Alright. So if you're new here, there are only two rules. Sixty minutes to plan and build, which always results in fun and chaos. And the second rule, use whatever you have at your disposal, AI tools, past projects, random packages off of the Internet, whatever. It's fair game. Alright. Let's dive in. Today, we are going to be building a company intranet portal thing. Listen. This was honestly a suggestion from, some of the AEs on our team. Love those guys. They're amazing. The spec for this, very fuzzy. Right? I I don't even know what is in a company Internet portal sort of thing these days. I do know excuse me. I do know that I don't think I ever enjoyed using one. So we're gonna try to create something in an hour or less that is helpful and actually decent to use. Let's dive in and hit the clock. Alright. Sixty minutes. Usually, let's kick this off with functionality. Right? What type of functionality do we want out of an intranet portal? And we're gonna do single sign on for sure. Right? If we're using something internal, we want single sign on maybe through Google or Microsoft or something like that. We're gonna set that up. Yeah. We wanna be able to track team members and show org structure, you know, maybe provide helpful links. I feel like maybe that could be like a Google Doc, but, you know, we're we're doing the fancy version of this. And then what else do we're gonna have? You know, maybe I wanna be able to send messages to entire team. Great. Okay. So that is what we're looking at as far as functionality. Let's kind of discuss our data model. Right? We're going to need honestly, there's new shapes there. Figma, come on now. Okay. I don't I don't need a tour on the new shapes. Thank you, though. Alright. Getting derailed by the tools already. We're, like, two minutes into this. Alright. So, obviously, we are using Directus on the back end here. That's gonna give me a lot out of the box, like, user permissions, like users. Right? So we're gonna have users of the application. Those are also gonna double as team members. So one shot, one kill here. You know, users comes with emails, passwords, first name, last name, job title. We're gonna probably want to add, like, reporting structure, direct reports, reports to, something like that so I can establish those relationships. We probably want, like, a team. So we got different teams within the organization, so that'll be a separate one. Why is that text so small? Oh, too big. Too big. Okay. Alright. So we got teams. We got users. What else do we have? You know, maybe, like, pages or resources. I guess really fuzzy. Right? And this is the part of the show where I draw these amazing arrows that that FigJam allows for. So k. Looks great. You know? And then maybe there's a relationship here where some team members have access to certain pages and some don't, that sort of thing. Let's see what we can get done. Right? Alright. So what have I got set up already? Basically, absolutely nothing. I've got a example project or a a Docker Directus instance running here. I've got, you know, just kind of like a standard Docker Compose setup. Don't have much config in this. No collections setup whatsoever. Alright. So let's tackle first thing on the list. Right? I want to set up single sign on. So if I log out of this instance, I see email and password, log in. I wanna do single sign on for, like, Google, for instance. Alright. So how are we going to configure that? Well, inside our documentation, we've got a list of, like, the configuration that we could set up. So we're gonna have, like, an auth providers. So that's a comma comma separated list of auth providers. The driver that we're gonna use and then a provider mode, we'll probably use session. And then for Google, I think the recommendation from our team is to use OpenID. So we've got, like, an OpenID setup here. Now do we have, like, a Google SSO single sign on? This this is the single sign on dock. Do we have a, like, standard config? Somewhere where we could see, like, a standard config for this. Yeah. There we go. Alright. So we could set up multiple auth providers, but this looks like the the pretty standard config for, Google. So I'm just gonna copy this wholesale. Right? I'm gonna go inside my Docker Compose, you know, or I could stick this in my ENV file if I wanted to. I'm not gonna open that because I've got some other secrets in there. As far as the IDE I'm using, this is Cursor. I've been using that this entire season. I found it pretty helpful and especially for quick POCs like this. Alright. So now we got some config in here. We're gonna have to go and create a client inside Google as well. So let's go to Google Google SSO single sign on, console. Cloud. Google, I think, is the let's see what we got here. Log in as Directus. Alright. Let's get started configuring. I've got my first project, so you're probably gonna have to create a project if you don't have one. Let's get started. We're gonna call this 100 apps, one hundred hours. The user support email, that's gonna be me, Brian at Directus. This will be an internal audience. So, my knowledge of Google auth is very minimal, But, obviously, internal is gonna be users only within our organization, which for this works well. Right? When I'm at directus dot I o. Great. I agree. We're going to spin this up. Okay. Nice. Nice. Okay. We're gonna create an OAuth client. This is a web application. Let's call this Directus. Alright. So what do we need to do as far as, like, our JavaScript origins and our authorized redirect URLs? So I think we're gonna have to do something like this where we have local host 8055 as far as our origin. Let's see if we can search our doc single sign on. Here's a entire guide on this. So you could just search that up. Right? Internal authorized domains. Alright. So we're gonna authorize JavaScript origins. That's gonna be it's not required, but that's gonna be our directus server address. And then the authorized callback is gonna be slash auth slash login slash Google slash callback. And that's because of the auth provider key that we're providing to the auth provider is Google. Alright? So that's how we reference this in our actual config. Alright. So, let's add that. That'll be HTTP local host 8 0 5 5. Great. We're gonna hit create. I certainly hope it doesn't take as long as what it's saying. Alright. So if I expand this, over here we're gonna get a client ID. We're gonna copy this client ID. That's gonna go here. Great. Then we got a secret. Don't steal my secrets. We're gonna paste that here. And what else do we need? Let's add an auth Google mode. Google mode. We're gonna make that session. The identifier key is email. I'm also gonna do allow public registration. Google allow public registration. So we want anybody who comes through our organization to be able to register for this. And one of the other things that we could do, I'm just gonna copy and paste. We probably wanna add, like, a default role setup as well. We'll get to that in a moment. Default role ID. Let's just actually dive into that right now. Alright. So great. We are going to sign in. If you remember the admin password, that is okay. So now we got this. Let's go in and create we've got two roles by default. These are out of the box for Directus. We've got public. This role controls, you know, like, what is available without authentication. And there's also a public access policy. So the access policies are, you know, just groups of different permissions, and then you can add many different access to a role. I love the granularity here, and it's super easy to maintain, like, a complex permission system between the both of these. What we do wanna do here is basically let's create a new role. We're gonna call this a team member role. Great. Okay. Alright. So we'll just save that. I'm gonna grab the primary key for this. That's what I'm gonna stick here for the role ID. That's the role that we want folks to have. As far as the policies that we're gonna give them, for now, let's just give them full admin access. They could change anything. We're gonna scope that down eventually, though. Alright. So the next thing I'm gonna do to test this out, right, I want to I I just want to stop my container. I'm gonna spin that container back up, and I'm gonna open this in. Let's do an incognito window with the admin login. Admin and example. Password. Great. K. And now if I log out of this one because I'm I'm already signed in to Google for everything here. Alright. If I hit bryant at directus, create a strong password. Why do I have to create a strong password? Come on, dawg. Why is it asking me to do this? Use 20 characters or more. Come on, guys. What is it? What are we doing? I'm gonna do this off screen here. I don't I don't know why this is happening here. Google. Let's create a new password. 20 characters or more. Directus dot I o. Okay. Oh my gosh. Let's see if this is actually gonna work. Sign in to 100 apps, one hundred hours. The request cannot be processed because it is malformed, which is not good. Let's try it again. What are we doing wrong here? So we go back to our Google where's our Google application? Yeah. Sign in. Yes. It's me. Love it when all of this nice security tech gets in our way. What are we missing? Right? HTTP local host 8055 slash login slash google\u002Fcallback. Okay. See, this is nice because I've tried to log in to a different account, but I should be able to log in to there we go. So now I am in. Right? And we can see that Bryant Gillespie is created. This is this is really nice. Right? We could see the auth provider here is Google. There's the external modifier or identifier, not modifier. Now we've got single sign on. Right? And one of the other things that I could potentially do I think there's config for this if we take a look. Go into auth and SSO. I could disable the default provider as well if I wanted to so that you could only log in with single sign on. So if we do that, let's see what we've got. Auth, disable default, true. And then I'm gonna restart my container. Let's just log back out. And now there's only Google to log in. There is no email and password auth options. This will disable it at the API level as well. Super nice thing to do. Right? If if all you want to allow is single sign on. So, with that done, right, let's let's go in and brand this thing because we obviously need a nice brand. Huge fan of office space. If you're younger and you're watching this, you know, this is an older movie. It pains me to say that. But, let's use Inatec as the company here. So I'm gonna go into our appearance settings, the project logo. So I could just copy the image address, take full advantage of this a lot of times. So I'll just import the file from a URL. If I hit save, we could see that Inatec logo up there. I'm gonna use the color picker. Let's pick up, like, a Inateq blue, maybe, I guess we could call that. Right? We got, like, this pale blue color. I'm gonna set up the Directus default theme, and in the namesake of the show, I'm gonna steal from our simple CMS template. I've got, like, a custom theme that I really like, super happy with. So I'm just gonna steal that paste raw value. There we go. We might wanna swap this out. And one of the nice things about the theming in Directus is, you know, I've got all these nice keys for theming, but I also have the ability just to add custom CSS and do whatever I want. So in this case, we'll do that as well. And what else are we gonna do? I'm gonna fix this just because it's gonna aggravate me. Alright. So this is where that CSS comes in. I've got the module bar logo. I'm gonna do something like this where we have dot module bar logo, background. Let's just do white. And we're gonna stick important on that. There we go. Now we're cooking. And maybe we wanna add, like, a subtle subtle border there. Let's see. What is that gonna be? That'll be our navigation. That'll be the modules. That'll be the border color, which would be I don't know. Let let's use color mix in sRGB. We'll use the var theme. We're gonna use the primary color and maybe 20% white. F f f f. And let's set, like, a two pixel border just so we can see it. Alright. There we go. So now we got a little little border action to keep things separated. I mean, this is not like a super nice mouse over active color, but I'm not gonna get too far into it. Right? This is gonna be the Inatec company portal. And one of the things that's gonna aggravate me is just our font choice here. Like, DM Sans is a newer font. Maybe we just set this to Arial. Do I have that installed? Would it be Arial? Yeah. I think it is one way or the other. And then we can set, like, our, let's let's pick something, like, super corporate here. Yeah, Roboto feels pretty corporate to me at this point. We'll do Roboto. So if I just put this in quotation marks, this will go ahead and pull those font files from Google for you. So any Google font, good, bad, ugly, whatever you wanna do, you can include those when you're customizing these themes. Alright. So now we got Inatek. We've got Roboto. This feels very robotic. This is a sad company worked for fictionally. So now if I refresh our login screen, we've got this kind of action going on. Looks okay. Maybe we wanna spice this up a bit. Right? So we're just gonna put Bill Lemberg on the login page. So we'll go to appearance. The public background here, we're gonna import Lemberg. Yeah. We need those TPS reports, and, you know, I will just leave the Favicon blank. There it is. There we go. Right? So this is the it's beginning to shape up as a perfect company portal. Alright. And one thing I'm gonna do, I'm gonna turn this off, or we're not gonna be able to test, like, our permissions. So disable default. Let me just spin this back up so we can get, like, an email and password. Log in one more time. Sign out. Okay. So now I've got my email and password back. Let's dive into the next item on our list, single not single sign on. We just completed that one. We're gonna dive into tracking team members and showing, like, an org structure, providing helpful links, and maybe try to send messages to the entire team. Alright. So, we've already got users through Directus. Right? We've got our user library here. We could see that Brian logged in, etcetera, blah blah blah. Looking great. Let's add this team functionality. Right? And we probably want the ability to create new teams and adjust teams and etcetera. So let's just create a collection for this. I could also just adjust the users collection, which we'll do in a few. But for this case, I'm gonna create teams. So each team, we need a primary key field. I could use an auto incremented integer. We can use a manually entered string. You know, I mean, like, in this case, maybe we use a manually entered string because I'm not expecting to have a a ton of teams, and, you know, the way we reference those is probably not gonna change much. Alright. Do we need any of these default fields? I'm not really sure. I don't think so. So we'll just skip that. So we got a team. We're gonna give a team name. Pretty simple. We'll make sure that is required. Great. The ID, we can make sure this is like URL safe, so we'll just enable Slugify through the interface settings. And we've got a team, we've got a name. Now what we're gonna do is set up a relationship between we probably got, like, a a parent and child relationship between teams as well. Right? One team could roll up to another team, could roll up to etcetera. You know, we could classify things like a team lead, and then we're gonna have, like, an organizational structure where we have direct reports for each user as well. So let's set up that first one. Right? Each team needs a leader. We'll just call this team lead. So here, I'm using the direct us relationships. I'm gonna set up a many to one because I only want one leader within the team. Could certainly set up multiple leaders if I wanted to, but for the related collection here, I'm gonna use direct us users. And as soon as I hit on the right one, you'll see that kind of turn blue and lock in. I can also just go over here and hit the system collections and do it that way as well. We're gonna show a link, and I'm gonna open up Advanced Field Creation Mode just so we could see this. What I'm also gonna do here, I can create the corresponding relationship at the same time. And this is where data modeling becomes very important. Right? Because, do we necessarily want this relationship here? Because I could be leading, you know, I guess I could lead multiple teams. So we will add this relationship here, but, you know, if you need, like, multiple team leads, obviously, you're looking at, like, many to many. We're gonna display the direct as user, show the user in a circle. We'll set that up. So there's the team lead. And now if I were to go and check our data model for users, right, I should also have a Teams option here. Right? There's our Teams. So we can specify the do I actually want that? The we're gonna have Teams as another thing, like, teams that we're a part of. Let's delete this, recreate this relationship, one to many, and teams leading. It's not t lead teams leading. Yeah. Leads, teams, teams leading, I guess. Naming stuff is I say this on every episode, I feel like, but naming stuff is the hardest thing in programming without a doubt. Alright. There we go. We got the display table, and we're gonna show the name. Cool. Team lead already has an associated relationship. What are we doing here? Have I bricked this whole thing? Let's see what we've got. Can I get access to the relationships here? If I reload what is going on? Have I totally bricked Directus with this relationship? What have I done? People, what have I done? Permissions, policies, fields, direct us users, team leads. Now if I refresh, did this sort it out? What have I done? Alright. Teams. Team lead. I don't understand. Teams users leading does not exist. Let's just try down and up. I goofed up our relationship somehow. I think we can get this back. We can solve this problem. Now I would be remiss not to take this opportunity to talk about the data mirroring inside Directus. So, you can install Directus onto any existing SQL database, whether that's Postgres, SQLite, MySQL. Directus, like, bootstraps these name space collections as you see here, but everything else is just standard SQL. Right? There's nothing direct as specific about our team teams collection here. And as I go through and build these out, let's try this one more time. One to many relationship or actually a many to one here. We're gonna have a team lead. We'll pick our direct to users collection, and then in the relationship settings, I'm gonna add that one, teams leading. I hate that, but that's what we're rolling with for now. We're gonna create this relationship. Right? Now, we could see that that change is being mirrored here. There's my team lead. Amazing. Right? So I could go in. We could create a new team. We could do team America. Let's just call it bride's team or let's do marketing, I guess. Marketing. Great. Mister admin user can be our team lead, and then I can go in and add additional teams. Right? Product. Got the product team. Sales. Great. You know, we could get really fancy and add icons for all these teams. Let's Let's just check how we're doing on time. We got thirty minutes left. Let's keep cruising. Right? Alright. So we got team leads. Seems like enough. Let's go in and add our team members. Right? Teams members. Maybe add an icon, though. Team. Is there a group? This is a collective. There we go. Group. Looks great. Amazing. Teams. Okay. So now I want to you know, I could potentially sit in multiple teams, I guess. You know, obviously, you would hope that that you would have a, like, a one to one relationship within the team, but, yeah, I again, hey. Like, data modeling issue, do could a team user belong to multiple teams? I I'm gonna say yes in this case just because I don't know. You know, I maybe, like, you wanna use this as, like, a project team as well. So that's that's how we'll play this. Alright. Teams, we're gonna call this team members. Okay. I'm just gonna create a junction collection manually here. Like, Directus will create this for you if you want. I just get in the habit of wanting total total control over everything. I'm just gonna hide this guy. Alright. So now we're gonna link members to our team. And for that, we're gonna reach for our many to many relationships inside Directus. We're gonna call this members. The related collection is gonna be users, Directus users. Great. And I'm gonna go to the relationship tab. So on almost every episode, you'll see me jump into the advanced setup. Once you get into the basics with Directus, highly recommend it, because it gives me total control over the fields that are created in the junction collection. Right? Instead of using team ID, I wanna use team, and I'm just gonna use user here because I like to be OCD about certain things. Right? So now we're gonna add this teams field to direct us users so I can, you know, potentially use that in our permissions. And on all of these relational triggers, I'm gonna set these to cascade, right, because there's no sense in keeping this relationship in there. Alright. We could set up our interfaces, display related values. Great. There we go. We got our team members. I can go in and set, like, a first name, last name. I could even go in and add, like, an avatar here if I want to. Oh, avatar. There's like a little thumbnail helper. Oops. Let me just edit the raw value. You could see this is just the mustache syntax to populate these values, but inside the interface it shows very nicely. Alright. So now we got our team. Let's add some members to the team. I'm gonna add Bryant and Tess Tess to the marketing team. Great. There's our team members. I'm gonna give I gotta upload a photo here just to make sure I got something. Right? There we go. There's my ugly mug. There we go. So now I'm not seeing myself in there. We need to fix that as well. Here's where I can just copy these values. So I'll hit copy raw value. I'm just gonna paste that in the interface display template. So the difference between interface and display, the interface shows inside the form. Right? This is the interface. Here inside, like, the teams, if I were to look at the members, that's our display. So I wanna show the same thing. I could see those members. There's our team members. There's the team lead. Great. You know, I can prevent editing on all of this. Amazing. Solid. Right? Alright. So now, you know, we've got team members. Yeah. Let's add, like, our reporting structure as well with would that be within a team? This is where we get into, like, splitting errors. Right? In this case, I'm gonna add this to the user level. I guess you could add it at the the team member level, but, you know, let's not do that. So, anyway, I'm gonna go into our system collections here. We're going to create some new fields here. So you can already see I've got two fields. The system fields are locked down. I can't change those, but, I can add new fields to this collection, to this table in the database. So we're gonna add a, like, a recursive relationship here. So for that, let's just use the one to many, and this is gonna be our direct reports. Right? This is gonna be direct as users, and the foreign key would be reports two. Again, you know, naming structure here is hard. It would be great to have help on that side. But, alas, this is not live. I am just talking to myself. Alright. So there's our display template. We're gonna show a link to the item. If I want to, I can just, like, show this relationship. This is gonna be created. Reports to, direct reports. Pretty pretty straightforward. Right? Direct report. So you could see that it's created, like, two two or two fields here. And if I look at, like, my and, like, here's the teams leading. Here's the teams I'm a part of. And let's say, mister I this is gonna get confusing as I'll get a test direct report. Test manager. Okay. Alright. So I got two users here. I have I've got a test direct report. I don't see the other field, right, who I report to, so we can go in and edit that as well. I think by default, we hide that. Maybe I just wanna show that here. So we're gonna show reports to go in, adjust this. So those are my direct reports. Here is reporting to the test manager. And now if I go into, let's say, the test manager, let's take a look at that, there's the direct reports. I can also make this like a tree view as well. So if I go into users, on these recursive relationships, we can go in and set this up to use our tree view so we have this recursive relationship. I'm just gonna copy this, we've got tree view, paste that there. Now if I go to mister test manager, I can see my direct reports, but I can also see I'm the test manager in this scenario. I'm not Brian Gillespie. I can see the direct reports of that person and, like, all the way down the chain potentially. Right? So if I have mister CEO, the test manager rolls up to mister CEO, I can drill all the way down the tree, the tree, as they say. Alright. So now that we've got those relationships set up, you know, it would be nice to show, like, an org chart or something there. Let's focus on on bigger challenges at this point. Like, how can we send messages to all these folks? Right? We wanna provide some helpful leaks, send messages. You know, maybe we want to have a specific page. You know, as far as, like, a portal, you probably got, hey, like, pages, you know, like, struggle with what to name. Like, here's just, like, helpful content that we want to create. Right? So we're just gonna add these status, is this published or not? Great. Let's give this a title. And because this is internal, we we don't really need anything like a slug. You know, I could potentially add it if I wanted to, but, this is the page title. Here's the markdown content. Right. Great. This is our content, page contents. And then trying to think of the best structure here. Like, will we set up, like, different categories? I mean, this is kinda getting into, like, knowledge base level. You know, I basically, just trying to set up a structure here. Let's say marketing team should have access to that post. Let's say let's create another one. Marketing should not have access. Right. Alright. And let's kind of get into let's step into this as well. Right? Tested example, password. They've got our team member role. Cool, test at example, password. Alright, so this is what our other user is gonna see, And right now, the team member role, as we specified, has admin access, which let's go ahead and lock that down. Right? So let's create a new policy, team member I'm just gonna call this app access. So we're gonna give app access to the team member, and that should hide stuff like settings that we don't want them to configure. How we doing on time? Nineteen minutes remaining. Cruising. Alright. Team member app. Let's go back to our team member role. We're gonna remove this administrator policy. Alright. We don't wanna give total access. We want to give team member app access. Great. And I think I just locked myself out of the system. Fun for everyone. Right? But if I refresh over here where I'm logged in as our test direct report, now I don't see any of our content. I don't see any of the settings. You know, I can access the dashboard, see the file library, etcetera. I'm gonna dive back into table plus here. We could see there's our team members. I'm just gonna go in and quickly fix this goof up that I just made where I removed myself as an administrator. There we go. Alright. So now if I reload that, I've got access back again. And again, you can see this is all just underlying SQL. Nothing nothing fancy here. Right? Alright. Marketing should have access to this. Marketing should not have access. You know, I would probably go one layer deep if I had, like, a a lot of these and say, okay. You know, I'll group these by categories. And, you know, like, that way you could restrict access, but let's just do it this way. We're going to create another mini to mini teams with access to this content. Yeah. What else could we do? A Couple other ways we could do this. Right? Yeah. Let's just do the many to many to keep it relational. Teams. Team access. Teams access. Teams. Great. Show a link to the item. Alright. And we could see page, team access. That's gonna be the page. That's gonna be the team. This is pages that they have access to, and I'll show you why this is gonna matter in a moment. Alright. So title content, marketing should have access. We can say marketing. Great. Pages marketing should not have access to. That's gonna be product and sales should have access to those. Okay. Now by default, no permissions are set for collections. Right? So if I go into team member app policy, I've just got the system collections that they need. This is why I love the access policies because I can also keep those separate. And I could say can access pages or read pages or something like that. Right? I don't have to do any of these other options. I can just go in and say pages, pages team access. So by default, let's set these up first. Maybe we wanna be able to read the teams. That's fine enough, we can see all the team members. But when it comes to page access, right, what we want is to restrict that based on teamaccess.ID. No. Teamaccess.team equals or is one of current user. So this is a dynamic variable inside Directus, and, basically, we can go through the tree here. So this will get us our current user, like, who's logged in. We could go through teams and then Teams.team. I believe Teamsteam. Is that gonna be our junction table collection? Should be. Right? Here's our team members. There's the team. Alright. Let's just test this out and see. I don't see any nothing. Right? Well, we created that access policy, but we didn't apply it to our team member role. So here's our policies. Right? Team member app. They can just log in to the app. And now we're gonna give them access to pages. Hit save and say. And if I did my homework or if I did everything correctly, this should be all set up. Right? So there's marketing. I could see these. I can't edit any of them. And as far as the pages go, marketing should have access to this, but they should not have access to that. Bada bing, badda boom. That's all done. Right? Amazing. It just works out of the box. And the beautiful part about this is not only is this through the UI, right, where I'm logged in, this is respected throughout the API as well. So if I, like, do this API call, oh, I don't need to add API in front of it. Used to working with Nuxt. Items dot pages, there's that specific page. But if I just fetch items pages, again, I'm only seeing the one page. Whereas if I do it over here in this browser, you could see I've got two pages, because I am logged in as an admin, and admins have full permissions. Great. Alright. I'm gonna check this off as the helpful links. You know, couple ways we could send messages to our users. We've obviously got, like, an email. I don't think we have a phone field in here. We could add a field to track phone numbers for our team members. We could, you know, message them via Twilio without, say, exposing, phone numbers or private information to other members of the team. Right? Do we have enough time for something like Twilio? Let's just let's cover the email use case. Right? I want to craft a message to, let's say, team announcements. Right? And I want to draft this message. I wanna send it out to all of our team members. Too many n's in there. Thank you. Spell check. Date created. Cool. We'll generate a UID. Got it. And we got the message body, and we want to do what? Message body is just gonna be WYSIWYG. That's our content. We could just call it content, or, yeah, you might even do text. Fine. Keep it simple. Alright. Alright. And then we're gonna do, like, a mini to mini for our teams that we wanna message. Right? Maybe I wanna message individual teams. So we'll do the related collection. And, again, like, behind the scenes right now, Directus Us is crew will create those junction tables for me. Again, I just won't have the control unless I go into this team announcement teams, team announcement teams, team announcement. Let's just call it announcement. We're gonna call that team. Alright. And this is like a, an insane way to do this, but I I could really just create, like, a a flow. But maybe I wanted to, like, schedule this as well. Right? Okay. So here's our team announcements. I want to add a announcement for the marketing team. And, hey. This is an announcement from me. Alright. So, you know, maybe I'm, like, drafting this. You know, maybe I don't wanna send this right away. Maybe we track, like, a another time stamp for date sent, date scheduled, that sort of thing. But we've got this announcement. Right? I there's a couple of ways that we could do this as well, but let's create a flow for this. Right? So I'm gonna go in, let's say, send announcement, and we're just gonna manually trigger this. So once we have the announcement, we wanna send it out. We're gonna require a confirmation. Send announcement. K. And I could actually use, like, this flow to, you could build forms into this, right, which is another nice thing. I could say, hey, form field. This is a field. So when you're doing these manual flows inside Directus, it's always nice to have this ability to just stick a form field in there. And from here, hit send announcement. I can add something to this field, run this flow, and that will give me some data. Right? So here I can see the body. Here's the keys that we're receiving. So this is the actual announcement. And we could do something like this, where we're going to, what are we going to do? We're going to read the data. So we're going to get the announcement. Announcements. That's going to be your Team Announcements. The IDs here are gonna be trigger dot body dot keys. I'm just gonna hit enter to save that. That. I don't need to run a filter on that. And then, also, I'm gonna get the team members. Right? So if I look at my payload one more time oh, no. We'll get the we'll get the team members from the actual announcements. Right? So here, I'm gonna do something like this where I have fields. Fields allows you to fetch what you need in a GraphQL like manner even though we're using REST. So I don't necessarily have to reach for the complexity of GraphQL if I don't need it. So I'm gonna get all the root level fields with an asterisk, then I am going to get teams, and I want teams dot member dot star. I'm just gonna test that out and see. I think it's depending on how we set up that. We've got teams, team members. Oh, it's gonna be user, not member. Alright. Let's test this out. Teams dot user teams dot user dot star. We'll save that. Alright. Now, what do we got? Six minutes left. Let's crush this here. We're going to send the announcement. Send that out. Hit flows. We get send announcements. There's our payload error, UUID. Some kind of issue here. Oh, we're passing an array. Let's not pass an array. Let's just pass the array of arrays. Let's just pass the single array. We're gonna hit send announcement here. Test that out. Get our flow. Send announcement. This is an announcement. Okay. So we got teams. I'm not getting the actual team members that we need here because we need, like, teams. Teams. I should be getting the team data. Right? And I should be getting that. Teams. Why am I not getting that data that I want? Let's try it again. Send flows. Send announcement. We're gonna take a look at this. Okay. Okay. Team.team.marketing. And then can I get through users from there? Teams.starTeam.star. Team Members Star. Love waiting through a good data model here. Let's see what we get now. Test, flows, send announcement. Okay. So now we're getting closer. Right? Here's our team. There's our members. We got the users. Those are what we're trying to get to. Teams.user.star. This is gonna give me the emails of the team members. Once I get that, send announcement. Flows. Is this gonna give us the data of what we want? Yes. There we go. Alright. And, really, all I need here is the email. This is where I'm going to turn to AI for, like, three minutes worth here. Let's just open cursor. Write a function, a JS function that returns the array of emails for this sample data. There we go. First, we need to okay. Spit something out, friend. Data dot teams, that flat map. Okay. So there's a simple this is writing TypeScript, which we're not gonna be able to use in flows, but you do have the ability to run just JavaScript. We'll call this transform. Let's just pop in that function. We'll remove the types. Data data dot teams. Alright. So we're gonna do this. We're going to get the, what did we call that last one? This is just announcement. We're gonna get the announcement data. So here's what we're gonna do. We'll do, this doesn't need to be async. We're gonna say const, actually, we're just gonna return get emails, and the data is gonna be dot data dot announcement. So as you go through a flow, like, each step will append the result under the key. So this data object that we pass, I can access the announcement data for that step through this. And then the final step here is gonna be sending an email, which I don't think I've actually I can't remember if I got emails hooked up or not. We're gonna get this transform. We're going to reference that. So I'm gonna send the email. We're going to say transform, transform. And I'm gonna make sure that that is that should be passing an array. So we're just gonna use the raw value. I don't have a this is an important announcement. And then for the WYSIWYG content, we are going to do announcements dot text. Alright. We got forty five seconds. We're only gonna get one go at this probably. Let's see if this is actually gonna work. Run this flow. Send announcement. Is it actually going to run the flow to brian@directus.io? Did I configure this? Yes or no? Come on, baby. Come on. It shows my email is disconnected. Gmail. Come on. Come on, Gmail. Fifteen seconds. Are we gonna get an announcement? Are we gonna get an announcement? Are we gonna get an announcement? No. I don't think I've got email connected inside this actual Docker Compose file, to be honest with you. Too bad. Too bad. We didn't get to the last one. That sucks. Sending messages to the entire team, it should go through. And if I were on a Directus cloud instance, it probably would go through, but I don't have the actual email drivers conf like, the config set up for that. So, like, no emails are going through there. You know, obviously, I would wanna take the time as a next step to just go ahead and do that. So, if I go to, like, the direct us documentation and I look for email, you know, I would want to set up, like, my either my SMTP settings or Mailgun or SES, to actually send those emails out. But I will I I don't know if we're gonna call this a win or not because it did run this flow. It did give us the emails. We're not getting any errors there. We're just, yeah. I don't know. Let's let's just do the explosion anyway. That's my favorite animation of this whole series. So that is building a company Internet internal portal. Hope this was a helpful look at just how powerful Directus is. I will catch you on the next episode of 100 apps, one hundred hours.","6926779e-6ca8-4901-89ee-7e1b754fc47b",[1162],"a8a10989-2021-40d3-b730-b1111150fcf1",[],{"year":399,"number":1060,"id":1061,"episodes":1165,"show":1166},[1063,1064,1065,1066,1046,1067,1068,1069,1070,1071],{"title":322,"slug":323},{"id":1065,"slug":1168,"vimeo_id":1169,"description":1170,"tile":1171,"length":316,"resources":12,"people":12,"episode_number":264,"published":1051,"title":1172,"video_transcript_html":1173,"video_transcript_text":1174,"content":12,"seo":1175,"status":19,"episode_people":1176,"recommendations":1178,"season":1179},"rabbitar-directory","1059434943","Hop along with Bryant as he attempts to build a management system for AI-generated rabbit avatars (\"Rabbitars\"). Watch him tackle importing hundreds of images, setting up trait relationships, and creating an automated flow to generate new characters with DALL-E—all in just 60 minutes.","0a355b7b-d425-4db4-b7c2-4ad9fd964a6e","Mission: Rabbitar Directory","\u003Cp>Speaker 0: Alright. Alright. Alright. Welcome back to yet another episode of 100 apps, one hundred hours. I'm your host Brian Gillespie, and this is the show where we try to build or rebuild some of your favorite apps or even crazy off the wall app ideas in one hour or less.\u003C\u002Fp>\u003Cp>Today, we've got a fun episode for you, an AI Rabatar directory, and I'll explain what that means in just a moment. But, the rules, if you're new to the show, you have sixty minutes to plan and build an application, no more, no less, and we're gonna use whatever tools we have at our disposal, which for me lately has, been a bit of, like, Cursor AI, the, AI enabled IDE. Been using it. I like it a lot for auto completion and things like that. Also, we've got some pieces for this one from a previous project.\u003C\u002Fp>\u003Cp>So the AI Ravitar directory. Let's take a look at at what we've got. Right? So this is a concept that we had in our Leap Week registration system. So for Leap Week three, back in the summertime, you could go in and when you registered, you could generate an AI Ravatar, that filled your astronaut suit.\u003C\u002Fp>\u003Cp>This was a space theme. You pick your traits, and it would generate a custom Ravatar for you that we then use in, like, the OG images on the the actual ticket pages. So lots of cool stuff there. But, what we're gonna do today is take this and transform it into a directory of these Ravatars with their traits that we could potentially reuse somewhere else. So with that, let's start the clock and dig into this thing.\u003C\u002Fp>\u003Cp>What is the actual functionality that we want out of this? And, you know, this is usually where I like to start anytime I'm building an application. We're just gonna do this. And did we yep. Okay.\u003C\u002Fp>\u003Cp>Alright. So the functionality that we want out of this. Right? We want a list of well, basically, manage a lot of rabbitars. So we wanna be able to, you know, edit those, generate them.\u003C\u002Fp>\u003Cp>Let's say generate new rabbitars based on a prompt, and, you know, manage traits. Manage a lot of rabbitars. You know? Do we get potentially, like, serve random rabbitars? I don't know.\u003C\u002Fp>\u003Cp>Serve random Ravitar based on a seed. Alright. So what is our data model for this thing looks like? Hey. This is one that I'm kind of building on the fly here.\u003C\u002Fp>\u003Cp>I haven't really thought much about this. You know, some of the other ones, I think a a little more about before we pop in here, but, obviously, we're gonna have our rabbitars as the top level. If we drill down into that, what does that actually look like? We've got, a relationship to traits that we'll have. We probably got a title for the Ravitar.\u003C\u002Fp>\u003Cp>We probably have a description. If I remember this right, maybe we call it, like, prompt because that's what we had from our Leap Week setup. And then we've got a list of traits, and we can make those relational. So we'll have that. We'll just connect the dots there.\u003C\u002Fp>\u003Cp>That'll be a mini to mini relationship. Right? Mini to mini relationship between traits and Ravatars, and this will just have a trait. And, you know, maybe we can add a description to that if we want to. No big deal.\u003C\u002Fp>\u003Cp>No big deal either way. This looks pretty good. You know, if you watched the show before, I've I've got, like, a Nuxt app starter that I use from time to time. I've downloaded a list of the files and prompts from our Leap Week system and, you know, anonymize this data so that it is secure. And then we've just created a blank project on Directus cloud.\u003C\u002Fp>\u003Cp>You can see there's a totally blank instance. So let's dive right in. Right? The first thing we're gonna do, let's create our Ravatar collection. Right?\u003C\u002Fp>\u003Cp>So we're gonna call this Ravatars. For the primary key, let's use a generated UUID for this, and we'll have a created at. We can say created by, or we could just use the naming convention that we have here. This is one of my favorite features inside Directus. Like, you can adjust these, but, basically, this will give you, you know, the standard time stamps, the standard relationship to the user that created these things.\u003C\u002Fp>\u003Cp>We don't really need a status for these. Right? Once they're created, they're created. We don't need a sort. So let's just add that.\u003C\u002Fp>\u003Cp>There we go. We've got our avatars and now we're gonna create these extra fields like title, prompt, and then we'll go through and do the traits. Right? So let's actually change this to a name. We'll give each one of these Ravatars a name somehow.\u003C\u002Fp>\u003Cp>Maybe we could use AI to do that since this is an AI generated episode. Great. Cool. Well, now we've got a name. Pretty simple.\u003C\u002Fp>\u003Cp>Just a string input. What else do we have? We have a prompt. You know, what was the actual prompt that we used to generate this avatar? That could be handy information.\u003C\u002Fp>\u003Cp>What else? And then we need the actual avatar. So we need the file. Forgot to do that one, didn't I? So let's do the file.\u003C\u002Fp>\u003Cp>I could use file or image here. It's the same kind of relationship. It just depends on what type it is. So if it's an image, which we are doing images, I would choose the image interface here because it will show, like, a nice thumbnail preview. So we're just gonna call this the file for the avatar or ravitar, and we can add padding to this if we need to.\u003C\u002Fp>\u003Cp>Great. Now let's work on traits. Right? So traits is gonna be a separate collection for me just because I want to be able to add new traits easily and, you know, be able to query the number of rabbitars based on a trait. And that gets a little trickier to do if you're using, you know, traits here as a a field in, like, CSV format.\u003C\u002Fp>\u003Cp>So I'm just gonna create a new table. We're gonna call it traits. And instead of the primary key field being ID with, like, an auto incremented integer here, I'm just gonna do this. Let's call the primary key field trait, and we're gonna use a manually entered string. That'll be nice.\u003C\u002Fp>\u003Cp>You know, created at, created by, updated at. I really don't need this, but we'll add it anyway. Great. And now we've got a trait field or trait collection, I should say. And, you know, I could go through and add a description to this if I wanted to, you know, just extra info, really.\u003C\u002Fp>\u003Cp>Alright. And and I could even go as far as, like, using a label. So where I'm using this as the primary key, you know, I might want to, like, slugify that or make sure that it everything is, like, in camel case or something like that. But cool. So you see how easy it is to add these extra fields to direct us.\u003C\u002Fp>\u003Cp>Let's do, cruelty free. Yeah. That's my favorite icon. I don't know why this is called cruelty free, but that gets us the bunny that we're looking for. Awesome.\u003C\u002Fp>\u003Cp>And then we'll add traits. I don't know if there's an icon for this or not. Traits attributes. Looks good enough. Attributes.\u003C\u002Fp>\u003Cp>Where are you? There we go. Alright. Just a little little touch of OCD there. Cool.\u003C\u002Fp>\u003Cp>So now we've got avatars. We can see we got a name. We got a prompt. We got a file. We got traits.\u003C\u002Fp>\u003Cp>But these two are not linked together. Right? So, what we want to do next is just link these two together. That is going to be using a many to many relationship inside Directus and I'm going to call this field traits. So, you can see I'm in the Ravatars collection.\u003C\u002Fp>\u003Cp>I'm going to go in and add a new field called traits and the related collection is going to be traits. And what I'm going to do here, we'll show a link to that, but for now what I'm going to do is continue in advanced field creation mode. And we're gonna go to the relationship. So here, we can see that Directus is gonna create a junction table for us. Let's call it ravitar traits, and I'm gonna change the fields inside that collection.\u003C\u002Fp>\u003Cp>So this will be called ravatar. This will be called trait. And we can add that corresponding field to the traits collection as well. That way I can look at any of the ravatars that have a trait. And I guess we can add a sort field here.\u003C\u002Fp>\u003Cp>So the relational triggers, if I bring up my little mouse pointer tool here, it's called mouse posay, by the way. If you are interested in this little thing that you've you've caught me use a couple times, Mouse Pose if you're on a Mac. Pretty neat little piece of software for creating videos or doing training. Alright. Back to the lecture at hand.\u003C\u002Fp>\u003Cp>So we will go in and these relational triggers are just whenever these certain events happen. And, basically, if I deselect a trait on a Ravatar or I delete a trait, what happens? And more times than not, when I'm using many to many relationships, I set these up so that, it will delete the junction table item because I I still have the data in each individual collection. Cool. So with that, let's go ahead and create this mini to mini relationship.\u003C\u002Fp>\u003Cp>And now I can see the traits here. I've got traits there. And you can see inside the data model that junction table that was created, ravitar traits. So, you know, if I open this up and I create a a new ravitar, right, we can add existing traits, add new traits, blah blah blah blah blah blah blah blah blah. Alright.\u003C\u002Fp>\u003Cp>So now what I've got working with here is this should be set up correctly for this kind of data model. Right? And this is ripped out of our Leap Week project that I was talking about that I won't showcase here, but you can get access to something similar inside our Directus plus subscription. It's called the event registration kit, and it comes with a a NUCs front end with the, like, the detailed badges and things like that. But we got the file.\u003C\u002Fp>\u003Cp>We've got the prompt. We've got the individual traits that should have the correct keys to a trait. I wanna say that will create the trait, but we'll take a look once we actually figure that out or not. And this may throw an error when we try to import it. But for now, like, what I'm concerned with is this file.\u003C\u002Fp>\u003Cp>Right? If I look inside this project, I don't have any files. So what I've done in another directory here, I've used our template CLI tool to just export all the files and assets from that project. So here we go. I've got all the actual rabbitars here.\u003C\u002Fp>\u003Cp>We could see what all those fine fellows look like and and ladies. Let's try to use the Directus template CLI tool to get those into this project. So we got rabbitars.directus.app. We're gonna go in to my users here. I'm gonna create a token for this.\u003C\u002Fp>\u003Cp>We'll save that token. And what do I wanna do? We're gonna go to c d desktop \u002Fleapweek. Do we have something there? We got templates.\u003C\u002Fp>\u003Cp>Okay. Good. Let's c d into templates. We got the Leap Week template. And now we're gonna do, let's actually just pull this thing up so we can see what's going on.\u003C\u002Fp>\u003Cp>Template CLI. Open that up in a new tab. Got this going on. Alright. So the latest version of this does have, like, a programmatic mode where I can apply partials.\u003C\u002Fp>\u003Cp>So I'm gonna just copy this into my editor. We're gonna go h t t p s, what is this? Ravatars.directus.app. I'm gonna paste my token. Reminder to self roll this token before we do this.\u003C\u002Fp>\u003Cp>My template is leap week. Template type equals local. I'm gonna do a partial, and we're gonna do files. So that flag should get us, oh, and I think partial is, like, the p part of it as well. Is that what that stands for?\u003C\u002Fp>\u003Cp>P is a partial? Partial apply p. Okay. So we wanna do files only. Fingers crossed if this is actually gonna work or not.\u003C\u002Fp>\u003Cp>That's the beauty of this sort of thing. Alright. We hit go. It's logged in as the admin user. This doesn't look like it's doing what we want.\u003C\u002Fp>\u003Cp>Row row, Raggy. Maybe loading the files, and then we may be having to do some cleanup work after the fact. So I probably lied about a flag here or something. Well, let's just open it up and see. It's always fun.\u003C\u002Fp>\u003Cp>What else has this done for us? We'll just make this full screen again. Rabbitars. And it is uploading the files here, so we're probably getting a bit of that. And you could see now we do have events.\u003C\u002Fp>\u003Cp>We we've got this entire Leap Week template, which, is grand. So we'll we'll let this process these files, I guess, before we try to do anything else. This is gonna eat up some time on the clock, I'm afraid. I don't know how we're going here. One of the the nice things that we have recently added in Directus is the ability to see the logs.\u003C\u002Fp>\u003Cp>So these are coming in real time, and I could see, all the logs that are coming in. And you could see that we've got files here. So it should be uploading these files. And if I open this tab, you know, we should start to see some of those files uploading. But this is just a handy way to check on what's happening inside the direct us instance in real time.\u003C\u002Fp>\u003Cp>Now the template CLI tool that I've got here, it has a rate limit of, like, 10 requests a second in here. So that's why you're seeing, like, these things being staggered, which is helpful to not kind of overload. And these are not small files either. So this is a lot of bandwidth that I'm eating up here. But now you can start to see some of the the actual avatars.\u003C\u002Fp>\u003Cp>We just hope that the import of the JSON goes well once this is done. Now, maybe we try to work on some other piece of this while this is loading. I can't imagine how long this is gonna take. There's 924. We're doing 10 a second times 924 divided by 10.\u003C\u002Fp>\u003Cp>That gives us a 92 divided by 60. Minute and a half. It's taking a little longer than that, isn't it? Anyway, let's dive in and I don't think it's going to mess up if we start cleaning this up, but it's a risky run. Right?\u003C\u002Fp>\u003Cp>I'm gonna start deleting these collections that I did not actually need in this case. And then I'm gonna go back to the drawing board at some point after this and figure out why that partial apply did not work. Still loading files, people, ravatar. We don't need any of these blocks. We're just gonna clean this up.\u003C\u002Fp>\u003Cp>And again, what Directus is doing underneath the hood here, it has basically added these tables to my SQL database. But once I, you know, delete these or add new collections or any changes that I make through Directus through the studio, the underlying Directus APIs are making those changes to the SQL database in real time, which is nice. Right? So if I were to take a look now, like, all these tables would be in my SQL database, but we're just gonna clean these up. Who said this is not fun?\u003C\u002Fp>\u003Cp>Make sure we're not removing the fields that we actually need for this. Still loading files. Still loading files. Alright. Delete.\u003C\u002Fp>\u003Cp>Delete. Delete. And this is how you know that these things aren't scripted because if it was, I would be way more organized than this. Alright. So now we've got our collections back where we need it.\u003C\u002Fp>\u003Cp>Are we we're still getting all the files in here? Files. Files. Files. Files.\u003C\u002Fp>\u003Cp>Files. Just posting away. Anyway, let's try to figure out what we're gonna do next. Right? We've got the av Ravatars in there.\u003C\u002Fp>\u003Cp>We're gonna wanna generate new Ravatars at some point. How can we do that? Right? For that, let's go into the marketplace. And I believe we have an AI image generation option.\u003C\u002Fp>\u003Cp>Yes. Or operation here. So this is part of the Directus AI pack that we have. We've got the AI image generation option which calls DALL E three to generate an image. Right?\u003C\u002Fp>\u003Cp>So let's install this guy. We'll refresh. That will pull up that. Are we done with the files? No.\u003C\u002Fp>\u003Cp>We are not. There's another one that I'm gonna pull in here, the AI writer operation. So this is basically just a shortcut for the different AI LLM providers like OpenAI, Anthropic, Meta, Llama, all those different ones. And you can use a couple different options here. So we're just gonna install this too in case we need that.\u003C\u002Fp>\u003Cp>And let's talk about, like, creating new Revatars. We'll refresh. Still plugging away on the images. Man, how many rabbitars worth this in in in total? Let's let's just see.\u003C\u002Fp>\u003Cp>924 Ravatars. We're awaiting more logs. Are we done? Post. Post.\u003C\u002Fp>\u003Cp>I don't see oh, there we go again. Coming back. We're running more images. But, yeah. You can actually see the scalability here.\u003C\u002Fp>\u003Cp>Like, we're just hammering this thing with images and everything else is running a okay. Alright. So let's tackle this Gravatar generation piece. Right? If I want to create a new Ravitar, right, we're probably going to pass it some traits.\u003C\u002Fp>\u003Cp>And maybe we give it a name. Right? So let's organize this just a little bit because the two things that I want are gonna be up here. Like, I'm not gonna give this a prompt each time. I just want to store the prompt that was used to generate that.\u003C\u002Fp>\u003Cp>And we may even have something like this, like a metadata field. I I use this pattern a lot as well where I just store some JSON data that comes back from these. Alright. So basically, we'll give it a name, we'll add a few traits, and then we will store the rest of this info. And we could even go in and add, like, a group to this, like a detail group.\u003C\u002Fp>\u003Cp>Yeah. We'll call this generation, AI generation. Great. Do we have, like, a little robot icon? Perfect.\u003C\u002Fp>\u003Cp>Cool. So now we can just stuff all these in here, Get a little bit of organization. And we might want to even like update a status or something like that. Right? I didn't think we needed a status field here, but maybe we do wanna have a status for, like, generating or generation.\u003C\u002Fp>\u003Cp>Maybe we won't mess with that at this moment. Alright. Okay. So it looks like it's got all the files. That's all we need.\u003C\u002Fp>\u003Cp>I'm just gonna cancel that. Do we have 924 files? Is that what it shows? Let's see. All files.\u003C\u002Fp>\u003Cp>Refresh. We're done sending files through here. Boom. Boom. Boom.\u003C\u002Fp>\u003Cp>Boom. Oh, have I somehow logged myself out? No. Okay. I don't know what happened there.\u003C\u002Fp>\u003Cp>Alright. So we do have 924 files. Great. We'll take a detour again from this. But now we've got, like, this nice AI generation thing that, you know, we could see, like, the actual file.\u003C\u002Fp>\u003Cp>Maybe we wanna pull a file back out of this. K. I'm sorry. I'm a bit scattered here. I haven't had enough coffee yet this morning.\u003C\u002Fp>\u003Cp>Alright. So now I've got this JSON file that I exported from, the the directory, the Leap Week. Let's go in and actually try to import this file. We'll go to Directus, hundred apps, Nuxt starter. I've got output dot JSON.\u003C\u002Fp>\u003Cp>Let's open this up and see if we can get this imported. It says invalid foreign key. That's not good. Should we run this type of script again? Trait.\u003C\u002Fp>\u003Cp>Let's just go in and create a rabbitar. Let's create this first one. File. Trait. Where's our file?\u003C\u002Fp>\u003Cp>Oh, I'm inside traits. Did I import inside the trait directory? Maybe that could be the problem. We'll just edit the raw value. Let's add the file there.\u003C\u002Fp>\u003Cp>That should find the file. We don't have any of these traits, so we'll create one. Sunglasses. Masculine. Serious.\u003C\u002Fp>\u003Cp>Great. Beard and mustache. And if I add this prompt there we go. Alright. So I'm just going to delete this guy from our file since we're adding that now.\u003C\u002Fp>\u003Cp>We'll hit save and stay. Now we've got our our actual file here. We've got the list of prompts or traits. Serious rabbit face. Mcrabbit.\u003C\u002Fp>\u003Cp>Serious rabbit face Mcrabbit. There's our directory of, this guy. We can see we've got the traits now, and we're showing, like, the actual rabbitars that we have here. And there's the list of traits. Great.\u003C\u002Fp>\u003Cp>Cool. Makes sense. And then we could even clean that up a bit if we wanted to, but let's now let me try to import this again just to see. I think I may have had the wrong directory. Invalid foreign key.\u003C\u002Fp>\u003Cp>Alright. Let's see. Let's try this again. We'll hit start import. What are we getting back from the for the trait collection, Ravitar traits?\u003C\u002Fp>\u003Cp>Invalid foreign key in Ravitar traits. It's always always fun. Alright. So what I'm gonna do, I'm gonna just open up this inside a new tab. I'm gonna wanna wanna see the data we're getting back from the API.\u003C\u002Fp>\u003Cp>Right? So here I could see this is the actual data. Let's do something like this where we add an asterisk, we get fields, we get traits, and I just wanna get all the root level fields for traits. There's the trait. I guess, do we have to go through and get all the individual traits?\u003C\u002Fp>\u003Cp>Is that the problem? It should be this should be the right syntax. Alright. I got this script. Where is this guy?\u003C\u002Fp>\u003Cp>So we're transforming the snake case. Trait. Trait. We shouldn't have to do that, should we? Alright.\u003C\u002Fp>\u003Cp>Let's think about this a little bit further. Do we need to stuff this into a create option, or is it just the fact that we need to get the traits? Alright. This is where things like cursor come in handy. Alright.\u003C\u002Fp>\u003Cp>Write another script that will find all the unique traits and output into a JSON array. Extract unique traits, blah blah blah. Let's just call this traits dot j s. And then you can even go as far as, like, how what command to run this. No transform.\u003C\u002Fp>\u003Cp>Blah blah blah. Maybe that didn't help much, but node traits dot j s. Was that output dot JSON or no? This is, this is gonna pull the oh, that's gonna grab my actual other one. What is this thing?\u003C\u002Fp>\u003Cp>Transform. There we go. Traits dot j s, and then we'll do this as what? Oh, traits. Where are we at here?\u003C\u002Fp>\u003Cp>I'm in the wrong directory. Output traits. Traits dot j s. FS is not defined. Man.\u003C\u002Fp>\u003Cp>Just having fun with this all day here. Alright. Two snake cases not defined, of course. Should've just copied this entire file over. Alright.\u003C\u002Fp>\u003Cp>So if we got the traits alright. So we'll do this the old fashioned way. Sunglasses, serious, smiling, great, short hair, eyeglasses, Bald. Black hair. Hat.\u003C\u002Fp>\u003Cp>Blonde hair. How fast can you type, Bryant? There we go. Alright. How are we doing on time?\u003C\u002Fp>\u003Cp>We got, like, thirty minutes left. We got feminine, lipstick. What else we got? Necklace. Neutral.\u003C\u002Fp>\u003Cp>Red hair. Gray hair. And because this is the primary key, if I try to do one like sunglasses that we already have, it should throw this out. Yeah. Value has to be unique.\u003C\u002Fp>\u003Cp>Okay. So I I think we've got them all. Did we get smiling? I don't know if we got that one or not. Smiling.\u003C\u002Fp>\u003Cp>We did get smiling. Okay. Alright. Let's try to import this again now that we have all those existing traits. If not, like, we could just use the create, add new syntax, invalid foreign key.\u003C\u002Fp>\u003Cp>Yep. Alright. Let's go back to our script that I wrote here just to get this data correct. Traits, we are going to add this in a we're gonna wrap this in this syntax. So create, update, delete.\u003C\u002Fp>\u003Cp>And then we're gonna pull this. So this will basically give us this syntax, if I pull it up in the docs. Right? Traits. Not traits.\u003C\u002Fp>\u003Cp>Create, update, delete. Is that gonna show? Basically, whenever you're creating items in here if we make this full screen, Relational data. So Directus allows you to create relationships or relational data through a specific collection. Sometimes it's better to use the detailed changes syntax.\u003C\u002Fp>\u003Cp>So, basically, you say, okay. Here's the records we wanna create. Here's the ones we want to update. Here's the ones we want to delete. And that should populate everything that we need.\u003C\u002Fp>\u003Cp>We're just gonna run this one again. Node transform. Output JSON. Alright. So this should create a record in the junction collection.\u003C\u002Fp>\u003Cp>Let's try it again. Output dot JSON. Start import. Invalid foreign key. Don't tell me that, sir.\u003C\u002Fp>\u003Cp>Do not tell me that. We need those traits. Start import. In the collection, Ravitar traits. Invalid foreign key for the field trait.\u003C\u002Fp>\u003Cp>That is the foreign key. Did I goof that up? Ravitar traits. Trait, Ravitar. Create trait sunglasses.\u003C\u002Fp>\u003Cp>Let's just shorten this up. We'll try it with a different file. Yeah. Can we get one of these to actually import? Import one dot JSON.\u003C\u002Fp>\u003Cp>Still not sure what I'm doing wrong, but that's the beauty of doing this live. For trait value is required for the trait. There is a trait. Import one dot JSON. File prompt.\u003C\u002Fp>\u003Cp>Oh, I'm inside the trait. Start import. Import one. Okay. So we got one to import.\u003C\u002Fp>\u003Cp>I don't understand what we're doing here. Why isn't the full file working? Eyeglasses, brown hair. It should be working. Right.\u003C\u002Fp>\u003Cp>Now we can see that works. Was I just in the wrong one again? Not paying attention? No. Is there one where trait is empty?\u003C\u002Fp>\u003Cp>No. Feminine red. Where's our output traits again? Traits. Alright.\u003C\u002Fp>\u003Cp>Let's just do this. We'll do a little sort. Sort lines ascending. Bald beard, black hair, blonde hair. They're missing brown hair.\u003C\u002Fp>\u003Cp>Yeah. So if I was smart, I would have, figured out the way to make these traits actually auto populate. But, again, that's the beauty of doing these things live. Import one. Okay.\u003C\u002Fp>\u003Cp>That works. So we'll just delete these guys out. Cool. Let's try the actual output file. We're still missing one, aren't we?\u003C\u002Fp>\u003Cp>Bald, brown, black, earrings, eyeglasses, feminine, gray hair, hat, lipstick, masculine, long hair. Long hair. Attention to detail matters, Brian. Long hair, masculine mustache, necklace, neutral, red hair, serious, short hair, smiling sunglasses. Okay.\u003C\u002Fp>\u003Cp>Now we have them all. We've just seriously burnt a lot of time on this, and that is gonna put us in a time crunch. But no stress. Right? So this should now take all of that data, import our avatars, make sure the files are linked up, and this may take a moment just because it is, like, digesting all those relationships as well.\u003C\u002Fp>\u003Cp>What do we got left? Twenty three minutes. Where are we at? We've got a lot of rabbitars that we can manage. We can now manage our traits as well, which is nice.\u003C\u002Fp>\u003Cp>How do we generate new rabbitars? Right? Cool. If I wanted to just update these a bit, I could go in and show a tiny image preview here just so we could see what that rabbitar actually looks like. And then I would probably go in here and update my interface to show, like, the trait name as well.\u003C\u002Fp>\u003Cp>And we'll display related values. We'll show the trait name. So now I can see, like, the actual traits here. Smiling brown here. I don't have a name for those.\u003C\u002Fp>\u003Cp>We probably not gonna get to that portion of it. I guess we could use AI to generate a list of names for these, and you could probably even pass it. Like, some of the ones like OpenAI, you could pass it the the actual image of this and have it give it a name. But here, I don't even really need the prompt. Right?\u003C\u002Fp>\u003Cp>I just wanna see the individual traits. Cool. Neutral brown hair. There's that. And I can make this a little more cozy, or what I might wanna do is just add this to a card's layout.\u003C\u002Fp>\u003Cp>Traits. Trait. We're under pressure trying to generate too many files, maybe. Bump bump bump. There we go.\u003C\u002Fp>\u003Cp>Oh, that's a good one there. Yeah. So now I get to see these actual Ravatars, and you'd be able to navigate these through the file library as well. It's just nice to be able to see them through the lens of the the associated traits as well. Right?\u003C\u002Fp>\u003Cp>So if I went here and I went to trait, it should be showing those. I don't know why it's not. But anyway, there we go. We want to be able to generate new rabbitars as well. Let's go through and create that step.\u003C\u002Fp>\u003Cp>Right? So anytime a new rabbitar is generated, generate new rabbitars. Generate new rabbitar. Let's do this. Is there a new icon?\u003C\u002Fp>\u003Cp>Perfect. You know, I love icons. Alright. So anytime we create a new Ravitar entry, after that's created, let's go in and call that OpenAPI a OpenAI API. I always get hung up on that.\u003C\u002Fp>\u003Cp>So here's our collection. So anytime an item is created in the Ravitar collection, we're going to run this flow. And I could test that just by doing something like this. We go into Ravitar and create a new one. We're going to call this Doctor.\u003C\u002Fp>\u003Cp>Phil the Ravitar. What traits is he going to have? Bald, gray hair, eyeglasses, Sirius. Cool. There's our traits.\u003C\u002Fp>\u003Cp>Alright, we hit Save and Stay. Cool. And there's Doctor. Phil, right? So that should have actually triggered this flow to run.\u003C\u002Fp>\u003Cp>And we can see that in the logs and it should pass us the actual data. So we can see here, here's the individual traits. Great. Cool. Now what?\u003C\u002Fp>\u003Cp>Now we are going to generate an image. Alright. Generate image, and that is gonna be using the AI image generation. And now we're gonna need an open API key, platform.openai.com. Log in.\u003C\u002Fp>\u003Cp>How are we doing on time? We got 19 left, man. We're cooking now. Let's get logged in. Alright.\u003C\u002Fp>\u003Cp>Where are you? Dashboard. API keys. We're gonna create a new secret key. Direct to sleep week.\u003C\u002Fp>\u003Cp>Okay. Yeah. We'll do that. This is what we call rabbitars. Create the secret key.\u003C\u002Fp>\u003Cp>Copy. Let's throw this in here. That's the secret. Alright. Do I have these are part of the rules.\u003C\u002Fp>\u003Cp>Right? Do I have the prompt that we use to actually generate these bad boys? That would be handy. That would save us a bit of time. Wouldn't it, Brian?\u003C\u002Fp>\u003Cp>Where are you? Leap week. Leap week. Leap week. Leap week.\u003C\u002Fp>\u003Cp>What is this? Leap week? Leap week. No. That's the actual template, isn't it?\u003C\u002Fp>\u003Cp>Oh, boy. Alright. Directus. Where are you? Lots of projects in here.\u003C\u002Fp>\u003Cp>Event OS. Virtual events, I think, is what this is. Server API. Is is this the one? Let's see.\u003C\u002Fp>\u003Cp>There we go. Okay. So there's my prompt. Photorealistic head on headshot of a single rabbit set against a back black background with detailed fur and realistic texture, and then we're gonna pass it the attributes that we want. Let's give it a name, traits.\u003C\u002Fp>\u003Cp>And we'll do standard quality square as the size. Alright. So now what I'm gonna do here is just add a little connector, and we could see, like, the data that we're getting back. I just wanna clean this up. We'll just copy this over here, and we'll make this half screen.\u003C\u002Fp>\u003Cp>So I'm gonna do a little run script operation. Cleanup is what we'll call it. And we're gonna take the data coming from this. So we'll do here's the, let's just call it the payload. And that will be under the data key, we'll find trigger because it's coming from the trigger.\u003C\u002Fp>\u003Cp>That will be the payload, and then we're gonna return traits. Right? So we want const traits equals payload dot create dot map, trait. And we want to return trait dot trait. Return trait.\u003C\u002Fp>\u003Cp>We just wanna return that, don't we? Trait dot trait. Traits. Okay. The name will be payload dot name, if it exists.\u003C\u002Fp>\u003Cp>And no. Actually, that one should exist. Payload dot name. Name. Payload dot name.\u003C\u002Fp>\u003Cp>I need a comma and traits. And I could paste this in here just to verify that this is actual test dot j s. Is this actually properly formatted, or are we we're throwing an error there. What am I missing? Oh, we're missing a there we go.\u003C\u002Fp>\u003Cp>No. Return. Okay. Concentrates. I think that should be proper now, except we don't need that.\u003C\u002Fp>\u003Cp>We got the return in the wrong place. Okeydoke. I think this is gonna work. And, you know, always test first. So we'll just go in and create another avatar.\u003C\u002Fp>\u003Cp>Be Jeezy. He is going to have, what, long hair. How we doing on time? Fifteen minutes left. Super masculine sunglasses.\u003C\u002Fp>\u003Cp>Save. What do we get coming back? Are we seeing this the way we want it to be? Cannot read payload dot map. Payload dot create.\u003C\u002Fp>\u003Cp>What was the actual payload? Traits dot create. Payload. Data dot trigger dot payload. That doesn't need to be asynchronous.\u003C\u002Fp>\u003Cp>Data dot trigger dot payload data dot trigger. Was there not a payload? I could've swore there was. Yeah. Trigger dot payload.\u003C\u002Fp>\u003Cp>Turn trait. That'll loop through these. We should have a trait dot trait. Create dot map. Create traits out.\u003C\u002Fp>\u003Cp>Yep. There you go. Sometimes you miss a option there. Let's try it again. BG.\u003C\u002Fp>\u003Cp>Sunglasses. Masculine. Long hair. I just have this up in a bun right now, by the way. Jiraiya new Ravitar.\u003C\u002Fp>\u003Cp>Cool. Now we get the traits. Can we just return the actual list of trait? Treat dot treat dot treat dot treat. Okay.\u003C\u002Fp>\u003Cp>Cool. Flows. We have new flows. There we go. Okay.\u003C\u002Fp>\u003Cp>So this is what we're gonna pass to open AI API. Cool. And traits is gonna be cleanup dot traits. And the name, cleanup dot traits. Cool.\u003C\u002Fp>\u003Cp>And then we will pass cleanup dot name. K. And then the next thing that we're gonna wanna do, we should get something back from this, but then we'll have to import a file as well. But let's test this out. We could try that doctor Phil one again.\u003C\u002Fp>\u003Cp>That's great. Beard, gray hair, bald, serious. Woah. Save. And I could even open this up in a new tab if we wanted to do some fanciness here.\u003C\u002Fp>\u003Cp>Just flows. Alright. We see this flow less than a minute ago. Okay. This has given us this is the actual URL.\u003C\u002Fp>\u003Cp>Okay. So there's the Rabatar that was generated. There it is. Square standard. Here's the URL.\u003C\u002Fp>\u003Cp>Right? So now we need to import this URL into Directus. Right? So for that, let's go to the docs. And let's look at the API reference.\u003C\u002Fp>\u003Cp>So we're going to go to files accessing files. We don't want to access we want to create files. Importing a file. Alright, so using the REST API we can import a file. And what we're going to do here is basically just call this thing.\u003C\u002Fp>\u003Cp>Import file. Cool. We will use the webhook request. This is what? A post request to ravatars dot directus dot app slash files slash import.\u003C\u002Fp>\u003Cp>And we're gonna pass it two things. Right? We're gonna pass it a URL. URL is what? What's the URL?\u003C\u002Fp>\u003Cp>What did I call that last step? Generate image. So we're gonna access that data through this key, and I I think it'll actually just be generate image because it looks like all we're getting back from them is a string. And then we're gonna pass an authorization header here. Or what I could do instead is just allow anybody the ability to upload a file here, which if we save that, I can go into our access policies, go to the public one, and allow anybody to create a file.\u003C\u002Fp>\u003Cp>That's what I'm gonna do here. This is not recommended for production use. Obviously, you don't want anybody in the general public with the ability to upload files. Generate a new rabbitar. Cool.\u003C\u002Fp>\u003Cp>And this should the last step of this one is going to be do we get the actual ID of the item? Right? There's our key. So that's gonna be trigger dot key. What we're gonna do is update this.\u003C\u002Fp>\u003Cp>We're gonna update that rabbitar. Update rabbitar. From the collection there. We're going to do full access just to make sure we've got it. Then we're going to do this.\u003C\u002Fp>\u003Cp>We'll do the trigger. Key. And then the payload, we're gonna have the file that we get back from that last operation. So file is gonna be import oh my gosh. Is it just called import?\u003C\u002Fp>\u003Cp>It's just called import. Alright. So we'll do file. So that's the inside the Ravitar collection, that's where we're storing the relationship to the file. And that will be import dot data dot id.\u003C\u002Fp>\u003Cp>I wanna say that's what it should be. Import.data.id. If it's not, it's not. Okeydoke. Alright.\u003C\u002Fp>\u003Cp>So let's try this again. We're gonna do Bry Guy, Bry Ross. Cool. Bry Ross has red hair, smiling, eyeglasses, long hair, very masculine, and a beard. K.\u003C\u002Fp>\u003Cp>We'll hit save and stay. And I think I had I had Directus pulling it up at at one point here, didn't I? Let's just go in. We'll check our flows. Less than a minute ago.\u003C\u002Fp>\u003Cp>Update Ravitar. Invalid syntax for type UUID. Okay. So we got this back. Data.data.ID is what we need.\u003C\u002Fp>\u003Cp>So we didn't quite get the updated one. Data dot data dot ID. Alright. But I should be able to see that Ravitar in there. Right?\u003C\u002Fp>\u003Cp>Yeah. There we go. Alright. So let's try this one more time with six minutes left on the clock. What do we have here?\u003C\u002Fp>\u003Cp>Test. Bry Ross. We'll just pick a bunch of these eyeglasses. Red hair. Cool.\u003C\u002Fp>\u003Cp>Beard. Save and stay. And I'm gonna copy this link. We'll go over here. No.\u003C\u002Fp>\u003Cp>Not that one. Got arced again. Test. Bry. There it is.\u003C\u002Fp>\u003Cp>Right? So we can see here we created this fourteen seconds later. There we go. We have our AI Rabatar that is generated for this one. Bada bing badda boom.\u003C\u002Fp>\u003Cp>We generated new rabbitars. Now we can generate those on the fly, which is great. And because this is a direct us flow, even if I created these via the API, like passing traits, it would call that OpenAI API for me. Do we have enough time? We got five minutes.\u003C\u002Fp>\u003Cp>How can we generate a random avatar based on or how can we return a random avatar based on a seed? I I'm not even gonna attempt this one, to be honest with you, because, normally, I would wanna set this up on, like, a probably, like, a custom extension, instead of, like, a flow or something like that. But it it is doable. I wanna end on a high note. So just recapping.\u003C\u002Fp>\u003Cp>Right? We have generated a directory full of AI Ravatars that we can then manage and query and do all sorts of things with. We can manage traits, and we can see all the Ravatars that are within those as well. You know, we might even clean this up a little bit. So I can see for the interface, I wanna show the Ravitar and I wanna show the file for that Ravitar.\u003C\u002Fp>\u003Cp>So now if I take a look at these, right, we should be seeing, like, a little list of those Ravatars there. That's a particularly ugly one there. I'm not sure what happened with this one, but, there we go. That is generating an AI Gravatar directory. More goodness to come in the next episodes of 100 apps, one hundred hours.\u003C\u002Fp>\u003Cp>Join me. We'll see you. You.\u003C\u002Fp>","Alright. Alright. Alright. Welcome back to yet another episode of 100 apps, one hundred hours. I'm your host Brian Gillespie, and this is the show where we try to build or rebuild some of your favorite apps or even crazy off the wall app ideas in one hour or less. Today, we've got a fun episode for you, an AI Rabatar directory, and I'll explain what that means in just a moment. But, the rules, if you're new to the show, you have sixty minutes to plan and build an application, no more, no less, and we're gonna use whatever tools we have at our disposal, which for me lately has, been a bit of, like, Cursor AI, the, AI enabled IDE. Been using it. I like it a lot for auto completion and things like that. Also, we've got some pieces for this one from a previous project. So the AI Ravitar directory. Let's take a look at at what we've got. Right? So this is a concept that we had in our Leap Week registration system. So for Leap Week three, back in the summertime, you could go in and when you registered, you could generate an AI Ravatar, that filled your astronaut suit. This was a space theme. You pick your traits, and it would generate a custom Ravatar for you that we then use in, like, the OG images on the the actual ticket pages. So lots of cool stuff there. But, what we're gonna do today is take this and transform it into a directory of these Ravatars with their traits that we could potentially reuse somewhere else. So with that, let's start the clock and dig into this thing. What is the actual functionality that we want out of this? And, you know, this is usually where I like to start anytime I'm building an application. We're just gonna do this. And did we yep. Okay. Alright. So the functionality that we want out of this. Right? We want a list of well, basically, manage a lot of rabbitars. So we wanna be able to, you know, edit those, generate them. Let's say generate new rabbitars based on a prompt, and, you know, manage traits. Manage a lot of rabbitars. You know? Do we get potentially, like, serve random rabbitars? I don't know. Serve random Ravitar based on a seed. Alright. So what is our data model for this thing looks like? Hey. This is one that I'm kind of building on the fly here. I haven't really thought much about this. You know, some of the other ones, I think a a little more about before we pop in here, but, obviously, we're gonna have our rabbitars as the top level. If we drill down into that, what does that actually look like? We've got, a relationship to traits that we'll have. We probably got a title for the Ravitar. We probably have a description. If I remember this right, maybe we call it, like, prompt because that's what we had from our Leap Week setup. And then we've got a list of traits, and we can make those relational. So we'll have that. We'll just connect the dots there. That'll be a mini to mini relationship. Right? Mini to mini relationship between traits and Ravatars, and this will just have a trait. And, you know, maybe we can add a description to that if we want to. No big deal. No big deal either way. This looks pretty good. You know, if you watched the show before, I've I've got, like, a Nuxt app starter that I use from time to time. I've downloaded a list of the files and prompts from our Leap Week system and, you know, anonymize this data so that it is secure. And then we've just created a blank project on Directus cloud. You can see there's a totally blank instance. So let's dive right in. Right? The first thing we're gonna do, let's create our Ravatar collection. Right? So we're gonna call this Ravatars. For the primary key, let's use a generated UUID for this, and we'll have a created at. We can say created by, or we could just use the naming convention that we have here. This is one of my favorite features inside Directus. Like, you can adjust these, but, basically, this will give you, you know, the standard time stamps, the standard relationship to the user that created these things. We don't really need a status for these. Right? Once they're created, they're created. We don't need a sort. So let's just add that. There we go. We've got our avatars and now we're gonna create these extra fields like title, prompt, and then we'll go through and do the traits. Right? So let's actually change this to a name. We'll give each one of these Ravatars a name somehow. Maybe we could use AI to do that since this is an AI generated episode. Great. Cool. Well, now we've got a name. Pretty simple. Just a string input. What else do we have? We have a prompt. You know, what was the actual prompt that we used to generate this avatar? That could be handy information. What else? And then we need the actual avatar. So we need the file. Forgot to do that one, didn't I? So let's do the file. I could use file or image here. It's the same kind of relationship. It just depends on what type it is. So if it's an image, which we are doing images, I would choose the image interface here because it will show, like, a nice thumbnail preview. So we're just gonna call this the file for the avatar or ravitar, and we can add padding to this if we need to. Great. Now let's work on traits. Right? So traits is gonna be a separate collection for me just because I want to be able to add new traits easily and, you know, be able to query the number of rabbitars based on a trait. And that gets a little trickier to do if you're using, you know, traits here as a a field in, like, CSV format. So I'm just gonna create a new table. We're gonna call it traits. And instead of the primary key field being ID with, like, an auto incremented integer here, I'm just gonna do this. Let's call the primary key field trait, and we're gonna use a manually entered string. That'll be nice. You know, created at, created by, updated at. I really don't need this, but we'll add it anyway. Great. And now we've got a trait field or trait collection, I should say. And, you know, I could go through and add a description to this if I wanted to, you know, just extra info, really. Alright. And and I could even go as far as, like, using a label. So where I'm using this as the primary key, you know, I might want to, like, slugify that or make sure that it everything is, like, in camel case or something like that. But cool. So you see how easy it is to add these extra fields to direct us. Let's do, cruelty free. Yeah. That's my favorite icon. I don't know why this is called cruelty free, but that gets us the bunny that we're looking for. Awesome. And then we'll add traits. I don't know if there's an icon for this or not. Traits attributes. Looks good enough. Attributes. Where are you? There we go. Alright. Just a little little touch of OCD there. Cool. So now we've got avatars. We can see we got a name. We got a prompt. We got a file. We got traits. But these two are not linked together. Right? So, what we want to do next is just link these two together. That is going to be using a many to many relationship inside Directus and I'm going to call this field traits. So, you can see I'm in the Ravatars collection. I'm going to go in and add a new field called traits and the related collection is going to be traits. And what I'm going to do here, we'll show a link to that, but for now what I'm going to do is continue in advanced field creation mode. And we're gonna go to the relationship. So here, we can see that Directus is gonna create a junction table for us. Let's call it ravitar traits, and I'm gonna change the fields inside that collection. So this will be called ravatar. This will be called trait. And we can add that corresponding field to the traits collection as well. That way I can look at any of the ravatars that have a trait. And I guess we can add a sort field here. So the relational triggers, if I bring up my little mouse pointer tool here, it's called mouse posay, by the way. If you are interested in this little thing that you've you've caught me use a couple times, Mouse Pose if you're on a Mac. Pretty neat little piece of software for creating videos or doing training. Alright. Back to the lecture at hand. So we will go in and these relational triggers are just whenever these certain events happen. And, basically, if I deselect a trait on a Ravatar or I delete a trait, what happens? And more times than not, when I'm using many to many relationships, I set these up so that, it will delete the junction table item because I I still have the data in each individual collection. Cool. So with that, let's go ahead and create this mini to mini relationship. And now I can see the traits here. I've got traits there. And you can see inside the data model that junction table that was created, ravitar traits. So, you know, if I open this up and I create a a new ravitar, right, we can add existing traits, add new traits, blah blah blah blah blah blah blah blah blah. Alright. So now what I've got working with here is this should be set up correctly for this kind of data model. Right? And this is ripped out of our Leap Week project that I was talking about that I won't showcase here, but you can get access to something similar inside our Directus plus subscription. It's called the event registration kit, and it comes with a a NUCs front end with the, like, the detailed badges and things like that. But we got the file. We've got the prompt. We've got the individual traits that should have the correct keys to a trait. I wanna say that will create the trait, but we'll take a look once we actually figure that out or not. And this may throw an error when we try to import it. But for now, like, what I'm concerned with is this file. Right? If I look inside this project, I don't have any files. So what I've done in another directory here, I've used our template CLI tool to just export all the files and assets from that project. So here we go. I've got all the actual rabbitars here. We could see what all those fine fellows look like and and ladies. Let's try to use the Directus template CLI tool to get those into this project. So we got rabbitars.directus.app. We're gonna go in to my users here. I'm gonna create a token for this. We'll save that token. And what do I wanna do? We're gonna go to c d desktop \u002Fleapweek. Do we have something there? We got templates. Okay. Good. Let's c d into templates. We got the Leap Week template. And now we're gonna do, let's actually just pull this thing up so we can see what's going on. Template CLI. Open that up in a new tab. Got this going on. Alright. So the latest version of this does have, like, a programmatic mode where I can apply partials. So I'm gonna just copy this into my editor. We're gonna go h t t p s, what is this? Ravatars.directus.app. I'm gonna paste my token. Reminder to self roll this token before we do this. My template is leap week. Template type equals local. I'm gonna do a partial, and we're gonna do files. So that flag should get us, oh, and I think partial is, like, the p part of it as well. Is that what that stands for? P is a partial? Partial apply p. Okay. So we wanna do files only. Fingers crossed if this is actually gonna work or not. That's the beauty of this sort of thing. Alright. We hit go. It's logged in as the admin user. This doesn't look like it's doing what we want. Row row, Raggy. Maybe loading the files, and then we may be having to do some cleanup work after the fact. So I probably lied about a flag here or something. Well, let's just open it up and see. It's always fun. What else has this done for us? We'll just make this full screen again. Rabbitars. And it is uploading the files here, so we're probably getting a bit of that. And you could see now we do have events. We we've got this entire Leap Week template, which, is grand. So we'll we'll let this process these files, I guess, before we try to do anything else. This is gonna eat up some time on the clock, I'm afraid. I don't know how we're going here. One of the the nice things that we have recently added in Directus is the ability to see the logs. So these are coming in real time, and I could see, all the logs that are coming in. And you could see that we've got files here. So it should be uploading these files. And if I open this tab, you know, we should start to see some of those files uploading. But this is just a handy way to check on what's happening inside the direct us instance in real time. Now the template CLI tool that I've got here, it has a rate limit of, like, 10 requests a second in here. So that's why you're seeing, like, these things being staggered, which is helpful to not kind of overload. And these are not small files either. So this is a lot of bandwidth that I'm eating up here. But now you can start to see some of the the actual avatars. We just hope that the import of the JSON goes well once this is done. Now, maybe we try to work on some other piece of this while this is loading. I can't imagine how long this is gonna take. There's 924. We're doing 10 a second times 924 divided by 10. That gives us a 92 divided by 60. Minute and a half. It's taking a little longer than that, isn't it? Anyway, let's dive in and I don't think it's going to mess up if we start cleaning this up, but it's a risky run. Right? I'm gonna start deleting these collections that I did not actually need in this case. And then I'm gonna go back to the drawing board at some point after this and figure out why that partial apply did not work. Still loading files, people, ravatar. We don't need any of these blocks. We're just gonna clean this up. And again, what Directus is doing underneath the hood here, it has basically added these tables to my SQL database. But once I, you know, delete these or add new collections or any changes that I make through Directus through the studio, the underlying Directus APIs are making those changes to the SQL database in real time, which is nice. Right? So if I were to take a look now, like, all these tables would be in my SQL database, but we're just gonna clean these up. Who said this is not fun? Make sure we're not removing the fields that we actually need for this. Still loading files. Still loading files. Alright. Delete. Delete. Delete. And this is how you know that these things aren't scripted because if it was, I would be way more organized than this. Alright. So now we've got our collections back where we need it. Are we we're still getting all the files in here? Files. Files. Files. Files. Files. Just posting away. Anyway, let's try to figure out what we're gonna do next. Right? We've got the av Ravatars in there. We're gonna wanna generate new Ravatars at some point. How can we do that? Right? For that, let's go into the marketplace. And I believe we have an AI image generation option. Yes. Or operation here. So this is part of the Directus AI pack that we have. We've got the AI image generation option which calls DALL E three to generate an image. Right? So let's install this guy. We'll refresh. That will pull up that. Are we done with the files? No. We are not. There's another one that I'm gonna pull in here, the AI writer operation. So this is basically just a shortcut for the different AI LLM providers like OpenAI, Anthropic, Meta, Llama, all those different ones. And you can use a couple different options here. So we're just gonna install this too in case we need that. And let's talk about, like, creating new Revatars. We'll refresh. Still plugging away on the images. Man, how many rabbitars worth this in in in total? Let's let's just see. 924 Ravatars. We're awaiting more logs. Are we done? Post. Post. I don't see oh, there we go again. Coming back. We're running more images. But, yeah. You can actually see the scalability here. Like, we're just hammering this thing with images and everything else is running a okay. Alright. So let's tackle this Gravatar generation piece. Right? If I want to create a new Ravitar, right, we're probably going to pass it some traits. And maybe we give it a name. Right? So let's organize this just a little bit because the two things that I want are gonna be up here. Like, I'm not gonna give this a prompt each time. I just want to store the prompt that was used to generate that. And we may even have something like this, like a metadata field. I I use this pattern a lot as well where I just store some JSON data that comes back from these. Alright. So basically, we'll give it a name, we'll add a few traits, and then we will store the rest of this info. And we could even go in and add, like, a group to this, like a detail group. Yeah. We'll call this generation, AI generation. Great. Do we have, like, a little robot icon? Perfect. Cool. So now we can just stuff all these in here, Get a little bit of organization. And we might want to even like update a status or something like that. Right? I didn't think we needed a status field here, but maybe we do wanna have a status for, like, generating or generation. Maybe we won't mess with that at this moment. Alright. Okay. So it looks like it's got all the files. That's all we need. I'm just gonna cancel that. Do we have 924 files? Is that what it shows? Let's see. All files. Refresh. We're done sending files through here. Boom. Boom. Boom. Boom. Oh, have I somehow logged myself out? No. Okay. I don't know what happened there. Alright. So we do have 924 files. Great. We'll take a detour again from this. But now we've got, like, this nice AI generation thing that, you know, we could see, like, the actual file. Maybe we wanna pull a file back out of this. K. I'm sorry. I'm a bit scattered here. I haven't had enough coffee yet this morning. Alright. So now I've got this JSON file that I exported from, the the directory, the Leap Week. Let's go in and actually try to import this file. We'll go to Directus, hundred apps, Nuxt starter. I've got output dot JSON. Let's open this up and see if we can get this imported. It says invalid foreign key. That's not good. Should we run this type of script again? Trait. Let's just go in and create a rabbitar. Let's create this first one. File. Trait. Where's our file? Oh, I'm inside traits. Did I import inside the trait directory? Maybe that could be the problem. We'll just edit the raw value. Let's add the file there. That should find the file. We don't have any of these traits, so we'll create one. Sunglasses. Masculine. Serious. Great. Beard and mustache. And if I add this prompt there we go. Alright. So I'm just going to delete this guy from our file since we're adding that now. We'll hit save and stay. Now we've got our our actual file here. We've got the list of prompts or traits. Serious rabbit face. Mcrabbit. Serious rabbit face Mcrabbit. There's our directory of, this guy. We can see we've got the traits now, and we're showing, like, the actual rabbitars that we have here. And there's the list of traits. Great. Cool. Makes sense. And then we could even clean that up a bit if we wanted to, but let's now let me try to import this again just to see. I think I may have had the wrong directory. Invalid foreign key. Alright. Let's see. Let's try this again. We'll hit start import. What are we getting back from the for the trait collection, Ravitar traits? Invalid foreign key in Ravitar traits. It's always always fun. Alright. So what I'm gonna do, I'm gonna just open up this inside a new tab. I'm gonna wanna wanna see the data we're getting back from the API. Right? So here I could see this is the actual data. Let's do something like this where we add an asterisk, we get fields, we get traits, and I just wanna get all the root level fields for traits. There's the trait. I guess, do we have to go through and get all the individual traits? Is that the problem? It should be this should be the right syntax. Alright. I got this script. Where is this guy? So we're transforming the snake case. Trait. Trait. We shouldn't have to do that, should we? Alright. Let's think about this a little bit further. Do we need to stuff this into a create option, or is it just the fact that we need to get the traits? Alright. This is where things like cursor come in handy. Alright. Write another script that will find all the unique traits and output into a JSON array. Extract unique traits, blah blah blah. Let's just call this traits dot j s. And then you can even go as far as, like, how what command to run this. No transform. Blah blah blah. Maybe that didn't help much, but node traits dot j s. Was that output dot JSON or no? This is, this is gonna pull the oh, that's gonna grab my actual other one. What is this thing? Transform. There we go. Traits dot j s, and then we'll do this as what? Oh, traits. Where are we at here? I'm in the wrong directory. Output traits. Traits dot j s. FS is not defined. Man. Just having fun with this all day here. Alright. Two snake cases not defined, of course. Should've just copied this entire file over. Alright. So if we got the traits alright. So we'll do this the old fashioned way. Sunglasses, serious, smiling, great, short hair, eyeglasses, Bald. Black hair. Hat. Blonde hair. How fast can you type, Bryant? There we go. Alright. How are we doing on time? We got, like, thirty minutes left. We got feminine, lipstick. What else we got? Necklace. Neutral. Red hair. Gray hair. And because this is the primary key, if I try to do one like sunglasses that we already have, it should throw this out. Yeah. Value has to be unique. Okay. So I I think we've got them all. Did we get smiling? I don't know if we got that one or not. Smiling. We did get smiling. Okay. Alright. Let's try to import this again now that we have all those existing traits. If not, like, we could just use the create, add new syntax, invalid foreign key. Yep. Alright. Let's go back to our script that I wrote here just to get this data correct. Traits, we are going to add this in a we're gonna wrap this in this syntax. So create, update, delete. And then we're gonna pull this. So this will basically give us this syntax, if I pull it up in the docs. Right? Traits. Not traits. Create, update, delete. Is that gonna show? Basically, whenever you're creating items in here if we make this full screen, Relational data. So Directus allows you to create relationships or relational data through a specific collection. Sometimes it's better to use the detailed changes syntax. So, basically, you say, okay. Here's the records we wanna create. Here's the ones we want to update. Here's the ones we want to delete. And that should populate everything that we need. We're just gonna run this one again. Node transform. Output JSON. Alright. So this should create a record in the junction collection. Let's try it again. Output dot JSON. Start import. Invalid foreign key. Don't tell me that, sir. Do not tell me that. We need those traits. Start import. In the collection, Ravitar traits. Invalid foreign key for the field trait. That is the foreign key. Did I goof that up? Ravitar traits. Trait, Ravitar. Create trait sunglasses. Let's just shorten this up. We'll try it with a different file. Yeah. Can we get one of these to actually import? Import one dot JSON. Still not sure what I'm doing wrong, but that's the beauty of doing this live. For trait value is required for the trait. There is a trait. Import one dot JSON. File prompt. Oh, I'm inside the trait. Start import. Import one. Okay. So we got one to import. I don't understand what we're doing here. Why isn't the full file working? Eyeglasses, brown hair. It should be working. Right. Now we can see that works. Was I just in the wrong one again? Not paying attention? No. Is there one where trait is empty? No. Feminine red. Where's our output traits again? Traits. Alright. Let's just do this. We'll do a little sort. Sort lines ascending. Bald beard, black hair, blonde hair. They're missing brown hair. Yeah. So if I was smart, I would have, figured out the way to make these traits actually auto populate. But, again, that's the beauty of doing these things live. Import one. Okay. That works. So we'll just delete these guys out. Cool. Let's try the actual output file. We're still missing one, aren't we? Bald, brown, black, earrings, eyeglasses, feminine, gray hair, hat, lipstick, masculine, long hair. Long hair. Attention to detail matters, Brian. Long hair, masculine mustache, necklace, neutral, red hair, serious, short hair, smiling sunglasses. Okay. Now we have them all. We've just seriously burnt a lot of time on this, and that is gonna put us in a time crunch. But no stress. Right? So this should now take all of that data, import our avatars, make sure the files are linked up, and this may take a moment just because it is, like, digesting all those relationships as well. What do we got left? Twenty three minutes. Where are we at? We've got a lot of rabbitars that we can manage. We can now manage our traits as well, which is nice. How do we generate new rabbitars? Right? Cool. If I wanted to just update these a bit, I could go in and show a tiny image preview here just so we could see what that rabbitar actually looks like. And then I would probably go in here and update my interface to show, like, the trait name as well. And we'll display related values. We'll show the trait name. So now I can see, like, the actual traits here. Smiling brown here. I don't have a name for those. We probably not gonna get to that portion of it. I guess we could use AI to generate a list of names for these, and you could probably even pass it. Like, some of the ones like OpenAI, you could pass it the the actual image of this and have it give it a name. But here, I don't even really need the prompt. Right? I just wanna see the individual traits. Cool. Neutral brown hair. There's that. And I can make this a little more cozy, or what I might wanna do is just add this to a card's layout. Traits. Trait. We're under pressure trying to generate too many files, maybe. Bump bump bump. There we go. Oh, that's a good one there. Yeah. So now I get to see these actual Ravatars, and you'd be able to navigate these through the file library as well. It's just nice to be able to see them through the lens of the the associated traits as well. Right? So if I went here and I went to trait, it should be showing those. I don't know why it's not. But anyway, there we go. We want to be able to generate new rabbitars as well. Let's go through and create that step. Right? So anytime a new rabbitar is generated, generate new rabbitars. Generate new rabbitar. Let's do this. Is there a new icon? Perfect. You know, I love icons. Alright. So anytime we create a new Ravitar entry, after that's created, let's go in and call that OpenAPI a OpenAI API. I always get hung up on that. So here's our collection. So anytime an item is created in the Ravitar collection, we're going to run this flow. And I could test that just by doing something like this. We go into Ravitar and create a new one. We're going to call this Doctor. Phil the Ravitar. What traits is he going to have? Bald, gray hair, eyeglasses, Sirius. Cool. There's our traits. Alright, we hit Save and Stay. Cool. And there's Doctor. Phil, right? So that should have actually triggered this flow to run. And we can see that in the logs and it should pass us the actual data. So we can see here, here's the individual traits. Great. Cool. Now what? Now we are going to generate an image. Alright. Generate image, and that is gonna be using the AI image generation. And now we're gonna need an open API key, platform.openai.com. Log in. How are we doing on time? We got 19 left, man. We're cooking now. Let's get logged in. Alright. Where are you? Dashboard. API keys. We're gonna create a new secret key. Direct to sleep week. Okay. Yeah. We'll do that. This is what we call rabbitars. Create the secret key. Copy. Let's throw this in here. That's the secret. Alright. Do I have these are part of the rules. Right? Do I have the prompt that we use to actually generate these bad boys? That would be handy. That would save us a bit of time. Wouldn't it, Brian? Where are you? Leap week. Leap week. Leap week. Leap week. What is this? Leap week? Leap week. No. That's the actual template, isn't it? Oh, boy. Alright. Directus. Where are you? Lots of projects in here. Event OS. Virtual events, I think, is what this is. Server API. Is is this the one? Let's see. There we go. Okay. So there's my prompt. Photorealistic head on headshot of a single rabbit set against a back black background with detailed fur and realistic texture, and then we're gonna pass it the attributes that we want. Let's give it a name, traits. And we'll do standard quality square as the size. Alright. So now what I'm gonna do here is just add a little connector, and we could see, like, the data that we're getting back. I just wanna clean this up. We'll just copy this over here, and we'll make this half screen. So I'm gonna do a little run script operation. Cleanup is what we'll call it. And we're gonna take the data coming from this. So we'll do here's the, let's just call it the payload. And that will be under the data key, we'll find trigger because it's coming from the trigger. That will be the payload, and then we're gonna return traits. Right? So we want const traits equals payload dot create dot map, trait. And we want to return trait dot trait. Return trait. We just wanna return that, don't we? Trait dot trait. Traits. Okay. The name will be payload dot name, if it exists. And no. Actually, that one should exist. Payload dot name. Name. Payload dot name. I need a comma and traits. And I could paste this in here just to verify that this is actual test dot j s. Is this actually properly formatted, or are we we're throwing an error there. What am I missing? Oh, we're missing a there we go. No. Return. Okay. Concentrates. I think that should be proper now, except we don't need that. We got the return in the wrong place. Okeydoke. I think this is gonna work. And, you know, always test first. So we'll just go in and create another avatar. Be Jeezy. He is going to have, what, long hair. How we doing on time? Fifteen minutes left. Super masculine sunglasses. Save. What do we get coming back? Are we seeing this the way we want it to be? Cannot read payload dot map. Payload dot create. What was the actual payload? Traits dot create. Payload. Data dot trigger dot payload. That doesn't need to be asynchronous. Data dot trigger dot payload data dot trigger. Was there not a payload? I could've swore there was. Yeah. Trigger dot payload. Turn trait. That'll loop through these. We should have a trait dot trait. Create dot map. Create traits out. Yep. There you go. Sometimes you miss a option there. Let's try it again. BG. Sunglasses. Masculine. Long hair. I just have this up in a bun right now, by the way. Jiraiya new Ravitar. Cool. Now we get the traits. Can we just return the actual list of trait? Treat dot treat dot treat dot treat. Okay. Cool. Flows. We have new flows. There we go. Okay. So this is what we're gonna pass to open AI API. Cool. And traits is gonna be cleanup dot traits. And the name, cleanup dot traits. Cool. And then we will pass cleanup dot name. K. And then the next thing that we're gonna wanna do, we should get something back from this, but then we'll have to import a file as well. But let's test this out. We could try that doctor Phil one again. That's great. Beard, gray hair, bald, serious. Woah. Save. And I could even open this up in a new tab if we wanted to do some fanciness here. Just flows. Alright. We see this flow less than a minute ago. Okay. This has given us this is the actual URL. Okay. So there's the Rabatar that was generated. There it is. Square standard. Here's the URL. Right? So now we need to import this URL into Directus. Right? So for that, let's go to the docs. And let's look at the API reference. So we're going to go to files accessing files. We don't want to access we want to create files. Importing a file. Alright, so using the REST API we can import a file. And what we're going to do here is basically just call this thing. Import file. Cool. We will use the webhook request. This is what? A post request to ravatars dot directus dot app slash files slash import. And we're gonna pass it two things. Right? We're gonna pass it a URL. URL is what? What's the URL? What did I call that last step? Generate image. So we're gonna access that data through this key, and I I think it'll actually just be generate image because it looks like all we're getting back from them is a string. And then we're gonna pass an authorization header here. Or what I could do instead is just allow anybody the ability to upload a file here, which if we save that, I can go into our access policies, go to the public one, and allow anybody to create a file. That's what I'm gonna do here. This is not recommended for production use. Obviously, you don't want anybody in the general public with the ability to upload files. Generate a new rabbitar. Cool. And this should the last step of this one is going to be do we get the actual ID of the item? Right? There's our key. So that's gonna be trigger dot key. What we're gonna do is update this. We're gonna update that rabbitar. Update rabbitar. From the collection there. We're going to do full access just to make sure we've got it. Then we're going to do this. We'll do the trigger. Key. And then the payload, we're gonna have the file that we get back from that last operation. So file is gonna be import oh my gosh. Is it just called import? It's just called import. Alright. So we'll do file. So that's the inside the Ravitar collection, that's where we're storing the relationship to the file. And that will be import dot data dot id. I wanna say that's what it should be. Import.data.id. If it's not, it's not. Okeydoke. Alright. So let's try this again. We're gonna do Bry Guy, Bry Ross. Cool. Bry Ross has red hair, smiling, eyeglasses, long hair, very masculine, and a beard. K. We'll hit save and stay. And I think I had I had Directus pulling it up at at one point here, didn't I? Let's just go in. We'll check our flows. Less than a minute ago. Update Ravitar. Invalid syntax for type UUID. Okay. So we got this back. Data.data.ID is what we need. So we didn't quite get the updated one. Data dot data dot ID. Alright. But I should be able to see that Ravitar in there. Right? Yeah. There we go. Alright. So let's try this one more time with six minutes left on the clock. What do we have here? Test. Bry Ross. We'll just pick a bunch of these eyeglasses. Red hair. Cool. Beard. Save and stay. And I'm gonna copy this link. We'll go over here. No. Not that one. Got arced again. Test. Bry. There it is. Right? So we can see here we created this fourteen seconds later. There we go. We have our AI Rabatar that is generated for this one. Bada bing badda boom. We generated new rabbitars. Now we can generate those on the fly, which is great. And because this is a direct us flow, even if I created these via the API, like passing traits, it would call that OpenAI API for me. Do we have enough time? We got five minutes. How can we generate a random avatar based on or how can we return a random avatar based on a seed? I I'm not even gonna attempt this one, to be honest with you, because, normally, I would wanna set this up on, like, a probably, like, a custom extension, instead of, like, a flow or something like that. But it it is doable. I wanna end on a high note. So just recapping. Right? We have generated a directory full of AI Ravatars that we can then manage and query and do all sorts of things with. We can manage traits, and we can see all the Ravatars that are within those as well. You know, we might even clean this up a little bit. So I can see for the interface, I wanna show the Ravitar and I wanna show the file for that Ravitar. So now if I take a look at these, right, we should be seeing, like, a little list of those Ravatars there. That's a particularly ugly one there. I'm not sure what happened with this one, but, there we go. That is generating an AI Gravatar directory. More goodness to come in the next episodes of 100 apps, one hundred hours. Join me. We'll see you. You.","2685fdc9-9197-4838-b93b-5e4610dcdddb",[1177],"0c98dc96-64e5-43c7-81dd-d74f5e324ed9",[],{"year":399,"number":1060,"id":1061,"episodes":1180,"show":1181},[1063,1064,1065,1066,1046,1067,1068,1069,1070,1071],{"title":322,"slug":323},{"id":1063,"slug":1183,"vimeo_id":1184,"description":1185,"tile":1186,"length":140,"resources":12,"people":12,"episode_number":13,"published":1051,"title":1187,"video_transcript_html":1188,"video_transcript_text":1189,"content":12,"seo":1190,"status":19,"episode_people":1191,"recommendations":1193,"season":1194},"ai-letters-to-santa","1059428648","Bryant builds a holiday-themed app that generates personalized letters from \"Open Source Santa\" based on GitHub profiles. Watch as he creates a system that analyzes developers' repositories, determines whether they're on the open source naughty or nice list, and generates snarky, sarcastic letters from Santa — complete with festive styling and holiday cheer.","6209314e-e6ee-4a2d-9e97-11eedd08595a","Mission: AI Letters to Santa","\u003Cp>Speaker 0: Alright. Alright. Alright. We are back with the Christmas edition of 100 apps, one hundred hours. Today, we are going to be building AI letters from Santa.\u003C\u002Fp>\u003Cp>I've got my lumberjack style on today. My wife called this lumberjack Jesus earlier, but I digress. We're back for more. The rules of 100 apps, one hundred hours. If this is your first show, we have sixty minutes to plan and build an application, a website, a portal, whatever.\u003C\u002Fp>\u003Cp>Whatever we're building, sixty minutes, no more, no less. And rule number two, the anti rule, use whatever you have at your disposal. And since this is an AI Christmas special, I'm gonna pull out all the stops. So let's dive right in. We're gonna hit the clock here.\u003C\u002Fp>\u003Cp>Fire it up. Sixty minutes on the timer. Go. Alright. So AI letters from Santa.\u003C\u002Fp>\u003Cp>What do we actually want out of this? So I have to admit, I cheated a little bit because I thought about this with my team, and I knew we wanted to do this. I've seen things in the past where you write a letter to Santa, you get something back in the mail, etcetera, etcetera. With AI, we could take this up a notch. So combining two ideas.\u003C\u002Fp>\u003Cp>A while back, I saw a GitHub roast page where you enter in your GitHub profile and it, basically will scrape that and give you a roast of how well you're actually not doing in GitHub. So we're gonna combine that with a letter from Santa. And basically, what we wanna do is, enter a GitHub profile. We're gonna scrape that profile. We're going to send that to AI.\u003C\u002Fp>\u003Cp>So we want LLM analysis of the profile. I'm not sure what we're gonna call that. And then I'm going to bucket people on the open source naughty or nice list. So score naughty or nice list. And then we're gonna generate a letter from open source Santa.\u003C\u002Fp>\u003Cp>Generate a letter from open source Santa to that GitHub profile to that profile. Alright. So as far as that functionality, this looks pretty good. Right? What are the tools that we're gonna use of the trade today?\u003C\u002Fp>\u003Cp>I've got a Directus Docker container up and running locally. Directus is obviously the back end we're using to store all of these things. And if everything works as intended p m p m g. I guess, sometimes things don't work as you intend. I've got a Nuxt application that we are going to try and use here.\u003C\u002Fp>\u003Cp>I'm not sure what's going on, but let's hop into the Directus instance. So I'm just gonna pull up Chrome. We'll log in to 8055, and I should be able to pull up my back end. So great. Got Directus running.\u003C\u002Fp>\u003Cp>You could see this is a pretty blank instance of Directus. This is just the boilerplate I use now. There are a couple extensions installed that I was testing, just messing around with. But, let's make sure. What are we doing here?\u003C\u002Fp>\u003Cp>For MPMI. Sometimes these things never go as planned. Okay. So maybe now we can get this Nuxt application up and running that will be served at local host 3,000, and we'll just basically use it to scaffold out our communications. As far as what I'm using, I've upgraded this boilerplate that I've used for 100 apps to, the Nuxt UI v three alpha, just to play around with Tailwind four and, you know, some of these nice new components that are coming from, like, Radix view.\u003C\u002Fp>\u003Cp>So alright. Let's actually model this thing out. Right? What do we need as far as our data models? I think we just need, like, a maybe like a profiles.\u003C\u002Fp>\u003Cp>So under profiles, we would have, what, our username, letter from Santa, letter from Santa, list, you know, are you naughty or nice? Great. And what else do we need? Let's let's jam on that. So we'll, just set up the back end for this.\u003C\u002Fp>\u003Cp>I'm just gonna create a new collection. We're going to call this profiles is the name of it. And why can't I zoom way in? There we go. That's maybe too far, but all good.\u003C\u002Fp>\u003Cp>Let's do created at, updated at. K. Status sort not needed. Who this was created by, I'm not super concerned with. So now we have a profile.\u003C\u002Fp>\u003Cp>We're gonna do the username. Great. That's where we'll store the GitHub profile. What else do we need? What else did we have here?\u003C\u002Fp>\u003Cp>We've got the letter from Santa. What is that gonna be in letter from let's just call it letter. Great. We'll use the WYSIWYG editor inside Directus so we could just store, I'm assuming, HTML content for that. And then we've got the list.\u003C\u002Fp>\u003Cp>So that's basically gonna be a string. We can, you know, make this look nice inside the directus admin. We'll just give it a naughty. Feel naughty just typing that out, and then we have the nice list. Great.\u003C\u002Fp>\u003Cp>There we go. And I'm just going to go on record that we're probably as soon as we start typing naughty into the AI stuff, we'll probably get some some things back. Like a I probably set off the content alarms or something like that. So there we go. We've got a username.\u003C\u002Fp>\u003Cp>We've got a letter. We've got a list. You know, I could potentially put that in here. What I'm gonna do now, I'm just gonna let's just we're just using Directus to store this at the moment. Right?\u003C\u002Fp>\u003Cp>There's a lot of different ways I could go with how to actually generate the application here. But Directus allows me to create custom extensions. What I'm gonna do here is just start, working on this from the Nuxt side of it. So we're gonna input the, actual form here. Let's add a profile.\u003C\u002Fp>\u003Cp>What are we gonna call this? Let's just call this letters dot view. We'll get a view component set up. Lang equals TS. Great.\u003C\u002Fp>\u003Cp>I need to work on my little macros here. Okay. Alright. The other thing that you'll notice here is that I am using cursor. So cursor I recently started testing this thing out.\u003C\u002Fp>\u003Cp>Really enjoying the actual auto completions for this thing. So, I don't have it usually generate like a a giant list of code, but the automations, or the auto completions are are pretty nice for this thing. So let's start with, what, step one. It'd be enter GitHub profile info. GitHub username.\u003C\u002Fp>\u003Cp>Alright. So the only thing here, sometimes it gets a little wonky with the okay. So we use the you form from Nuxt UI. Good question, Brian. The new one, the alpha, they changed some of the conventions.\u003C\u002Fp>\u003Cp>So I've got a form with a schema. I've got a form field instead of a form group, and then I've got an input. Okay. So we've got the form, new form field, and input GitHub username. Let's just see what that gets us on the front end.\u003C\u002Fp>\u003Cp>We're gonna go to this page, which is letters. Okay. Alright. So let's go ahead and just center this up. I think there's actually a container component we can use.\u003C\u002Fp>\u003Cp>Great. Cool. Okay. So now we have a GitHub username, and let's add a submit button. New button, click handle submit, and boom.\u003C\u002Fp>\u003Cp>We have a GitHub username, blah blah blah. Hit submit. Supposedly does something. What it's gonna do right now? Absolutely nothing.\u003C\u002Fp>\u003Cp>Alright. So the next thing that we wanna do, let's kick this thing off. We want to have the form state, we use reactive for that. Great. GitHub username.\u003C\u002Fp>\u003Cp>Okay. And then we're gonna write a function to handle submit. Thank you. Yeah. Great.\u003C\u002Fp>\u003Cp>We'll just, console log that. Right? Boom. There we go. We can see the GitHub username, yada yada yada.\u003C\u002Fp>\u003Cp>Alright. This is actually gonna be an async function. Great. Okay. So now what do we wanna do with this?\u003C\u002Fp>\u003Cp>Right? We have to think about our application structure. And what I'm gonna do here is just basically add a Nuxt server route. So if we break this down, in this server route, what we're gonna do, call the GitHub API, call GitHub API. We're gonna wanna grab a couple pieces of information like the user profile, or any other repos, and maybe, like, their their public read me.\u003C\u002Fp>\u003Cp>I guess we could loop through the actual repos and, you know, pick up more information there, but, let's see what we can get done with that piece. Alright. So let's just go here. We're gonna set up a new route. Let's call it, roast route.\u003C\u002Fp>\u003Cp>We'll do post, and just gonna copy the event handler here. K. So now whenever we hit this route with a post, it should return hello world. We can just check and see if that's actually gonna work. So, we will do the I'm trying to think if that's gonna return.\u003C\u002Fp>\u003Cp>Nope. So we got the response. We're gonna do await. We can use the regular fetch or the dollar sign fetch, which is the Nuxt specific version. And just test this out, see what we get back in the console.\u003C\u002Fp>\u003Cp>Where are you? Okay. Yeah. So we can see the request going out. We can see hello world coming back.\u003C\u002Fp>\u003Cp>Great. Cool. Alright. So now what we're gonna do, right, let's just scaffold this out. We are going to pick up the body.\u003C\u002Fp>\u003Cp>There's a wait read body. Great. So that is going to have the GitHub username in there. And, how did we spell that? Yep.\u003C\u002Fp>\u003Cp>Great. Alright. So we're gonna say GitHub username, and then we've got, like, this git roast function. I'm not really sure where some of these auto completions are coming from. But, what we're gonna do next, let's call yep.\u003C\u002Fp>\u003Cp>There we go. That's a good one. API users, GitHub username. Is that the correct one? Let's just test that.\u003C\u002Fp>\u003Cp>All the developers on my team are screaming and crying at the moment, watching all these AI auto completions. So that seems fair. Great. And let's actually use the Nuxt equivalent. Just this is using OFETCH, which does some automated data transformation and should automatically throw errors for you as well, which is nice.\u003C\u002Fp>\u003Cp>So this is gonna be the let's call this a profile. Alright. And then if we take a look at the profile, we probably wanna get the actual repos for that user as well. Alright. So we'll get the repos.\u003C\u002Fp>\u003Cp>Great. And let's just take a look at the data we're getting from the actual repos. Okay. So what do we actually concern ourself with here? Do we actually want all of this information?\u003C\u002Fp>\u003Cp>What do we actually care about from these? So stargazers, watchers counts, maybe those properties. You know what? Let's just jam it all in there and see what comes out of it. Right?\u003C\u002Fp>\u003Cp>And then let's get the profile readme. GitHub user content, GitHub username, repos dot name, Repos dot name. No. That's not gonna cut it. I think it's gonna be, what, GitHub username.\u003C\u002Fp>\u003Cp>GitHub username. Somebody who's already done this before. Give me the structure. And then main. Let's just see if we can find that.\u003C\u002Fp>\u003Cp>Read me will just populate my name. I don't even know if I have a actually have a read me. Yeah. There we go. Okay.\u003C\u002Fp>\u003Cp>So that is the structure. Great. That is what we needed to confirm. And now let's just actually return this and see what we'll get back. Alright.\u003C\u002Fp>\u003Cp>Brian Gillespie. Now I'm gonna fire this away. Roast. No. Nothing found.\u003C\u002Fp>\u003Cp>Well, that's a little concerning. GitHub username equals body. Read the body. We have fetched the user's GitHub username. Let's just console log the username.\u003C\u002Fp>\u003Cp>API dot GitHub users. I don't see the actual username coming back. GitHub. That's always fun. Alright.\u003C\u002Fp>\u003Cp>What did I do wrong? GitHub underscore username. Okay. Oh, duh. Are we actually passing that in the body of the form?\u003C\u002Fp>\u003Cp>Form. Console dot log response. Request payload. API slash roast dot post. What are we getting back here?\u003C\u002Fp>\u003Cp>GitHub username form dot GitHub username. Oh, that's right. We are missing a state variable here. So is it actually submitting the form? No?\u003C\u002Fp>\u003Cp>No. Okay, friends. What do we do from here? We have handle submit. We're going to use fetch await.\u003C\u002Fp>\u003Cp>Wait that fetch request. We should have already got this back. Of course. There it is. What a dumb dumb.\u003C\u002Fp>\u003Cp>Forgot to actually fix the v model there. So that's what that is. Sometimes, these are not great to do at the end of the day. But okay. Where we at as far as time?\u003C\u002Fp>\u003Cp>We've got forty two minutes remaining. I feel pretty confident on this one. Alright. Now with our roast, we can remove this. We should be able to get that information.\u003C\u002Fp>\u003Cp>Now let's make sure that we're getting what we want back from that API. Great. There's the profile. There's the repos, and there's the profile. Read me.\u003C\u002Fp>\u003Cp>Great. Alright. So what are we gonna do with these now? Right? The next step in this process would be to, pass the profile to LLM and ask it to summarize for us.\u003C\u002Fp>\u003Cp>What do we want this to return? It should return something that looks like this. We want a letter from Santa. Letter from Santa in HTML. And then we're gonna want the the list, naughty or nice.\u003C\u002Fp>\u003Cp>And that should be all we really need to return. Alright. LLM returns JSON. Cool. So what is the LLM we're gonna use?\u003C\u002Fp>\u003Cp>You know, typically, I use OpenAI for a lot of the stuff that I do here at Directus. I've been messing around a lot with, Claude locally. So we're just going to try this out. Santa letters. So we're gonna use anthropic.\u003C\u002Fp>\u003Cp>There's my API key. We're going to drop that in our ENV file, if I can actually get there. Jeez. There we go. I'm just gonna call it Claude ABI key.\u003C\u002Fp>\u003Cp>Okay. Great. And by the time you've watched this, hopefully, I've disabled that key. So, don't stop the video and try to figure that out. Alright.\u003C\u002Fp>\u003Cp>Let's pull up our docs for the API. We need to get the API reference. And let's define this prompt. Prompt. You are a letter writing AI.\u003C\u002Fp>\u003Cp>Alright. Analyze the following GitHub profile. You are the open source Santa Claus. You determine whose open source contributions are naughty or nice, analyze the following GitHub profile, Return a JSON object with the following fields, a letter from Santa and HTML. Set a really high bar for the nice list.\u003C\u002Fp>\u003Cp>What else do we need as far as a prompt? And, yeah, here is the data, profile, readme, json, stringify. Wonder why it's doing that. But okay. Nevertheless, there we go.\u003C\u002Fp>\u003Cp>Turn a JSON object instead of really write the letter in a snarky sarcastic tone. Cool. Alright. And now we're going to send that to Anthropic. Alright.\u003C\u002Fp>\u003Cp>So if we look at their oh, looks like we could just use their JavaScript SDK. That's great. Let's go ahead and open this up. We'll fire that up, install this thing. Import anthropic.\u003C\u002Fp>\u003Cp>Great. And then we're going to create that message. Alright. Constant AI response equals anthropic messages dot create Claude Sonnet. Okay.\u003C\u002Fp>\u003Cp>Messages user role, content prompt. Do we wanna set, like, max tokens? What is the what's the default for max tokens? Where do we actually pass this API key? Getting started authentication, x API key.\u003C\u002Fp>\u003Cp>API key equals process e n v. And, again, like, you could start to see why I really like using cursor because it has, like, this sixth sense for a lot of this stuff that I'm actually trying to do. Sometimes it gets that wrong, but a lot of times it gets it right. So alright, AI response messages. Do we wanna set a max tokens?\u003C\u002Fp>\u003Cp>Body messages, max tokens required. Let's give some more parameters. Write a short letter in a short in a snarky sarcastic tone. That is 500 words or less. And then for the tokens, if we look at Sonnet, we've got like a context window of like 200,000, so maybe a hundred thousand tokens.\u003C\u002Fp>\u003Cp>Oh, no. That's the output. Max output is eight nine one two. That's fine. Max tokens.\u003C\u002Fp>\u003Cp>Great. And let's return. Actually, what we're gonna do next is save that to the Directus database. Right? So we've got this collection for our profile.\u003C\u002Fp>\u003Cp>What I've also done, I've got a utility set up here. This is just using the Directus SDK. And one of the nice things about Nuxt, I say that a lot, is, the ability to it will auto import this for me. So I don't have to import it. I should just be able to call Directus server right here.\u003C\u002Fp>\u003Cp>So let's call it Directus response equals await directus server dot request create item. That's going to be in the profile. And we'll do the GitHub username. That's actually going to be username. Letter response, content dot text.\u003C\u002Fp>\u003Cp>I don't actually know what we're gonna get back directly. Return only a JSON object. And maybe we wanna add something like this for let's just do code. We'll set this up. And I'm just gonna add a field for, let's call it metadata or something where I'm just gonna store the entire response.\u003C\u002Fp>\u003Cp>And honestly, let's just do that to begin with. Metadata, AI response, content dot text. So if we take a look at the API reference, we go back to messages here. I'm kinda curious as to what we're gonna get back. The content text.\u003C\u002Fp>\u003Cp>Okay. Type text something. We'll get back something from the system. Let's just even do it this way. We'll say content direct us response, and then we're going to return direct us response.\u003C\u002Fp>\u003Cp>See what that gives us. Now let's go in. Where's our app? We'll switch back to Chrome. I do like Arc.\u003C\u002Fp>\u003Cp>I've found it to be lacking for development because it's just not super fast. Alright. So fingers crossed that this actually does what it should do. And, let's make this even nicer. And we'll add, like, a loading state, constant loading, ref equals false.\u003C\u002Fp>\u003Cp>We'll add loading dot value equals true. Loading dot value equals false. Great. And what else do we want to do? Is there a loading state on the actual form?\u003C\u002Fp>\u003Cp>Let's take a look. So Nuxt UI state, there is not a loading state on that. There should be on the button though. So just update that. Okay.\u003C\u002Fp>\u003Cp>And let's test this bad way out. Submit. Alright. We're waiting. We're waiting.\u003C\u002Fp>\u003Cp>We're waiting. We're waiting. We're waiting. This could take a minute. So, you know, we might even want to, like, potentially set up a oh, okay.\u003C\u002Fp>\u003Cp>So we're not getting anything back. We see a request error. So let's go into our roast, and we should probably do some error handling. Alright. Catch error, console error.\u003C\u002Fp>\u003Cp>Return, or we could just throw the error. What do we got here? Format. Alright. Let's refresh.\u003C\u002Fp>\u003Cp>I'll try this again and see what kind of error we're getting and why. Pending. Invalid user credentials for Directus. Okay. Great.\u003C\u002Fp>\u003Cp>So, just wasting tokens there, throwing them into the void. One of the things that you'll notice, I do have this direct as URL set up, but, my server token is probably a % not correct. So I'm gonna go in and create a token for this. We'll just create a new token. We'll call this the server token.\u003C\u002Fp>\u003Cp>And I wanna make sure in my utility that I have that set as server token, direct us URL. Okay. Let's try this thing again. PPM dev. I will restart the dev server, pull in that new ENV, though I think Nuxt may automatically update that for us.\u003C\u002Fp>\u003Cp>How we doing on time? We got twenty nine minutes left, so I'm feeling pretty confident that we can get something out of this. Let's go ahead and try it again. Bryant Gillespie. Submit.\u003C\u002Fp>\u003Cp>K. K. Roast. You do not have permission to access this. Okay.\u003C\u002Fp>\u003Cp>Can anybody spot the error? It is because I left off a s. We have profiles, and this is profile. So again, if I I don't know if I you can actually see the logs for anthropic. Okay.\u003C\u002Fp>\u003Cp>Yeah. We could see here's the actual logs. It's probably not showing what we've got there. But anyway alright. We'll try this one more time.\u003C\u002Fp>\u003Cp>Let's just clean this up a bit. And away we go. Dun dun dun. I don't like the looks of this, actually. Let's just reset.\u003C\u002Fp>\u003Cp>Try this again. AI response. We got the prompt. Got the profile. Dun dun dun.\u003C\u002Fp>\u003Cp>The moment of truth. Are we actually gonna be able to get this thing to work? Brig Gillespie. Submit. Obviously, this would probably be better as, like, a background job or something like that.\u003C\u002Fp>\u003Cp>Alright. So we refresh, and we have something here. Okay. Yeah. So we're getting some text back.\u003C\u002Fp>\u003Cp>It looks like we need to parse the JSON. The letter is going to be text parsed response, text, parse response dot list, and then we get metadata, which would just be the parse response, I'm assuming. Alright. We're gonna delete this out. Let's run this again.\u003C\u002Fp>\u003Cp>And hopefully I'm not burning through all these credits that I loaded up. Okay. So now we're looking great. Okay. So we have our username.\u003C\u002Fp>\u003Cp>We've got our letter. Ho ho ho. What do we have here? Another developer thinking they can impress Santa with a few measly repositories. I've seen l's with more impressive profiles.\u003C\u002Fp>\u003Cp>I got made it to the naughty list. Great. Amazing. Alright. So now that's working as intended.\u003C\u002Fp>\u003Cp>Let's let's make this pretty. Right? The form, we're going to do max width. Maybe we set this to Excel. Move that form to the somewhat in the center of the page.\u003C\u002Fp>\u003Cp>And let's just lean on AI here. Right? This is already pretty cool. One of the other things I wanna do is maybe we set up a route where we actually surface this letter. Right?\u003C\u002Fp>\u003Cp>So if we do let's do letters as a directory inside pages. And we're gonna do the username in brackets. So just take this username, make that in brackets, and then I'm gonna put letters inside here, and we'll change the name of this to the index route. Alright. So let's just clean this up a bit, wrap this up, and console the error loading.\u003C\u002Fp>\u003Cp>Actually, we could do that in finally. Great. And what we're gonna do, if the response is good, we could navigate to the username page. Cool. And that way, you know, basically, like, this could get very expensive if if you made this thing public.\u003C\u002Fp>\u003Cp>Right? You don't want people generating like 35 letters to Santa. So we can add a check to the database if we've already got that GitHub username and just return the letter that we we already have. Right? Okay.\u003C\u002Fp>\u003Cp>So on the response, as long as there's no error, we're going to navigate dot to form. Github username. And this would be await. Navigate to. Cool.\u003C\u002Fp>\u003Cp>Alright. Now let's just lean on AI and see what we could do. Add some Christmas theming to this. Let's see what this actually will do. Add some Christmas thinging, ho ho ho.\u003C\u002Fp>\u003Cp>Looks like it's generating some random messages. Code review letters to Santa, random message, decorative elements. Great. Love decorative elements. Now, with cursor, I'm just gonna click apply here.\u003C\u002Fp>\u003Cp>It should go through and run through this actual code. I can close this out and see, you know, in kind of a preview way what it's gonna change. And if we hit reload oh, what we got going on here? Letters index. Is that because I changed the route?\u003C\u002Fp>\u003Cp>Okay. Yeah. Now we're looking very festive here. This looks this looks great. AI, what can you do?\u003C\u002Fp>\u003Cp>Alright. The other thing I see, maybe we want this to be block. Will that get it done? Block. Class.\u003C\u002Fp>\u003Cp>Let's just make the width full. Width full. Okay. And then let's shrink this actual form a bit. Yeah.\u003C\u002Fp>\u003Cp>MD. There we go. Alright. We're deep in the Christmas cheer now. And, while we wait, let's well, not while we wait.\u003C\u002Fp>\u003Cp>Let's actually go in and now we're gonna work on this letter. Alright. So, this does have a Nux plug in. This is just my boilerplate where I can go in and actually request the information from Directus on the client side, or, you know, I could set up a route for this on the server side in Nuxt. Both of those ways are are totally valid depending on your application.\u003C\u002Fp>\u003Cp>Obviously, totally up to you. We will just, let's let's keep it the same theme. We're going to, like, fetch roast, or we could do, like, a roast.git.ts. And what are we gonna pass? Do we want to pass the username as a param, or we'll just pass it as a query parameter?\u003C\u002Fp>\u003Cp>Okay. So in this one, what we're gonna do, we will call the profiles endpoint inside Directus. So we'll just go const, response equals await Directus server. And, you know, sometimes you wanna make requests on the server side. That's why I've got this set up, this way.\u003C\u002Fp>\u003Cp>We're gonna do read item, and I gave this a UUID. We could've used the actual profile as the primary key. But, what we're gonna do, read profiles, and we're gonna set up a query for this. So we'll do a filter parameter, the username. So that's the field.\u003C\u002Fp>\u003Cp>We're gonna drop down again. This will be equal to the username. So first we're gonna have to get the username equals get router param. Nope. We're gonna do git query, and that would just be the query.\u003C\u002Fp>\u003Cp>Great. Username. We could destructure this if we wanted to. Return username equals username, and we're gonna return that response. Great.\u003C\u002Fp>\u003Cp>Cool. So now we do this. And on this one, what we can do is use the use fetch composable from Nuxt. So this will be we've got some data. We're gonna use fetch, and we're gonna call API slash roast.\u003C\u002Fp>\u003Cp>And the is it params? I believe. See what we got. And let's add the so the same festivities, I guess. Festiveness.\u003C\u002Fp>\u003Cp>Perfect. Alright. Decorative elements, blah blah blah, random messages. We're gonna put that up here in the script. Okay.\u003C\u002Fp>\u003Cp>Code review letters to Santa. And instead of the form, right, we're gonna replace this with data. Alright. So now if I do this, what's gonna happen? Route is not defined.\u003C\u002Fp>\u003Cp>Okay. So we just need to call use route to fetch that route. And do we actually get the stuff that we need here? We could test this API as well. Letters API roast username equals Brian Gillespie.\u003C\u002Fp>\u003Cp>Okay. Yeah. So that's getting us what we want from direct us except is it query? What is the use fetch? This is where, like, Nuxt documentation comes in handy.\u003C\u002Fp>\u003Cp>Use fetch. Where we at? We got sixteen minutes remaining. We got data use fetch. What are the URL query?\u003C\u002Fp>\u003Cp>Okay. Alias for query. That's what I thought. Root params username. Oh, data.\u003C\u002Fp>\u003Cp>Are we actually let's jump into the view dev tools. We'll hit the username route. And I see the data here. Here's the issue. Right?\u003C\u002Fp>\u003Cp>It is returning an array. So inside our routes, we could, you know, do something like this where we're just picking off the first item. I could also do that transform that on the the Nuxt side if I wanted to. Here's our letter from Santa. Cool.\u003C\u002Fp>\u003Cp>Code review letters from Santa. What I'm gonna do, let's use the pros class from Tailwind to get styling for this. We'll make the text dark green. Great. That's fine.\u003C\u002Fp>\u003Cp>And then the interior of this, we're just going to use v HTML. So we get this. Do I not have Tailwind typography into this? At plug in Tailwind typography. Okay.\u003C\u002Fp>\u003Cp>Yeah. So there we go. Now we've got the letter from Santa Claus. This is looking really nice. Perfect.\u003C\u002Fp>\u003Cp>Let's add like a cursive font. Right? Font family cursive. And this is Tailwind four, where all the config is basically CSS variables. So, really enjoying that Nuxt module, playing around with it.\u003C\u002Fp>\u003Cp>Let's find a handwritten font. Okay. Caveat. Looks nice. Nuxt has also added a a like this font amazing thing where you just throw your fonts in the CSS and it will actually download these things for you.\u003C\u002Fp>\u003Cp>So let's take a look at this. Right? I'm just gonna change this to font cursive and bada bing bada boom, we get what we want. So let's put, like, pros XL to XL. And there we go.\u003C\u002Fp>\u003Cp>So dear Bryant, what do we have here? Blah blah blah, etcetera. We have got thirteen minutes left on the clock. What can we do for fun? Let's go back and actually test this thing out.\u003C\u002Fp>\u003Cp>I'm just gonna refresh. There we go. I'm gonna do our fearless leader here at Directus, mister Ben Haines. We're gonna send this to Santa, and something bad happened. We could not find okay.\u003C\u002Fp>\u003Cp>So it looks like this one is not finding Ben's profile. Haynes, Maine. And Haynes Haynes Haynes Haynes Haynes. Would that be at, like, Master branch maybe? Where's our roast?\u003C\u002Fp>\u003Cp>Roast.post, profile read me. Try. I bet it's at master. I'm just gonna do this the quick and dirty way. Alright.\u003C\u002Fp>\u003Cp>So we go back. Let's try this again. Mister Ben Haines, we're about to roast you, sir. Alright. So we're checking the list twice.\u003C\u002Fp>\u003Cp>And eleven minutes on the clock. We've got the letter to Ben Haines from Ben Haines. Why are we not seeing the actual letter? There it is. I'm dreaming of a Nuxt application that actually works.\u003C\u002Fp>\u003Cp>What is going on with this? Letters, username, data dot name. I'm assuming because there is no name. Username. I'm running this on a sour note here.\u003C\u002Fp>\u003Cp>Ben Haines. That's kinda weird. Ben Haines. Why is it doing that? API roast username.\u003C\u002Fp>\u003Cp>What is going on here? Get async data. API roast. Why does it work for me and not for mister Haynes? What are we actually doing wrong here?\u003C\u002Fp>\u003Cp>Did I spell the name wrong? GitHub username. Alright. E pipe. Use fetch roast.\u003C\u002Fp>\u003Cp>Can't find the username? Profiles get username, get query. Is it read query? No. It's get query.\u003C\u002Fp>\u003Cp>Return query. API API roast. Ben Haines. So why aren't we why isn't this working? So it's not actually finding the username for that, which is odd because I have the username right there.\u003C\u002Fp>\u003Cp>Filter contains. Okay. I don't understand it, but we're gonna roll with it. Great. Some type of encoding or something maybe.\u003C\u002Fp>\u003Cp>Not sure. Booyah. Ben, I'm gonna read this to you. Dear Ben, ho ho ho. Well, isn't this embarrassing?\u003C\u002Fp>\u003Cp>I've been reviewing your GitHub profile, and I must say I'm thoroughly underwhelmed. 20 whole repositories, you must have been super busy this century. Meanwhile, Santa's got billions of believers worldwide. Look, I'm not saying you're on the naughty list because your contributions are lackluster. I'm saying if you were open source for coal, you barely have enough to heat a dollhouse.\u003C\u002Fp>\u003Cp>That is brutal. So let's call that a win. This is AI letters with Santa. Do we wanna do one more just for fun? Just for giggles?\u003C\u002Fp>\u003Cp>Let's let's test this out. Directus Directus. I forget Reich's actual, GitHub profile. There it is. Okay.\u003C\u002Fp>\u003Cp>So we're gonna throw mister Reich Van Zanten in there, our CTO, see what comes out of this thing. Hopefully, we got everything we need. It will do its thing. And and, that's not gonna pick on Wrike. Yeah.\u003C\u002Fp>\u003Cp>I don't know what's going on with this thing. Potentially some kind of caching issue. Don't know. Anyway, response zero. Down to the wire, five minutes, four minutes, three minutes, two minutes, no minutes.\u003C\u002Fp>\u003Cp>Is the server running? Use async data. And what if we just use fetch? Response. It's gonna be response dot letter.\u003C\u002Fp>\u003Cp>Oh, boy. Response fetch, browse, params, username. Come on. Failed to stringify the server logs. What is going on?\u003C\u002Fp>\u003Cp>This feels like a crappy way to end this one. It should be, like, festive with holiday cheer. I don't understand what is going on with Nox. Oh, duh, dummy. You have to wait the promise.\u003C\u002Fp>\u003Cp>Is that getting us what we need? Still not getting us what we need. Data is not defined on the instance. Where else am I getting the data at? Fested messages, data.\u003C\u002Fp>\u003Cp>Oh, if response. We're not even getting a response. SSR, undefined, undefined. Hey. That's the way the cookie crumbles sometimes.\u003C\u002Fp>\u003Cp>I'm not sure what I am doing wrong with this. I'm sure it'll come to me right after I get done with this. Is it like a key? Cash. Cash, no cash.\u003C\u002Fp>\u003Cp>Cache. No cache. No cache. Well, at least Ben's works. At least mine works.\u003C\u002Fp>\u003Cp>Not entirely sure what's going on with this little API that I've written, why it is caching this. But, hey, that's AI letters to Santa. That's the way it goes. This has been a hundred apps, hundred hours. Thanks for joining me.\u003C\u002Fp>\u003Cp>We'll catch you on the next episode. See you.\u003C\u002Fp>","Alright. Alright. Alright. We are back with the Christmas edition of 100 apps, one hundred hours. Today, we are going to be building AI letters from Santa. I've got my lumberjack style on today. My wife called this lumberjack Jesus earlier, but I digress. We're back for more. The rules of 100 apps, one hundred hours. If this is your first show, we have sixty minutes to plan and build an application, a website, a portal, whatever. Whatever we're building, sixty minutes, no more, no less. And rule number two, the anti rule, use whatever you have at your disposal. And since this is an AI Christmas special, I'm gonna pull out all the stops. So let's dive right in. We're gonna hit the clock here. Fire it up. Sixty minutes on the timer. Go. Alright. So AI letters from Santa. What do we actually want out of this? So I have to admit, I cheated a little bit because I thought about this with my team, and I knew we wanted to do this. I've seen things in the past where you write a letter to Santa, you get something back in the mail, etcetera, etcetera. With AI, we could take this up a notch. So combining two ideas. A while back, I saw a GitHub roast page where you enter in your GitHub profile and it, basically will scrape that and give you a roast of how well you're actually not doing in GitHub. So we're gonna combine that with a letter from Santa. And basically, what we wanna do is, enter a GitHub profile. We're gonna scrape that profile. We're going to send that to AI. So we want LLM analysis of the profile. I'm not sure what we're gonna call that. And then I'm going to bucket people on the open source naughty or nice list. So score naughty or nice list. And then we're gonna generate a letter from open source Santa. Generate a letter from open source Santa to that GitHub profile to that profile. Alright. So as far as that functionality, this looks pretty good. Right? What are the tools that we're gonna use of the trade today? I've got a Directus Docker container up and running locally. Directus is obviously the back end we're using to store all of these things. And if everything works as intended p m p m g. I guess, sometimes things don't work as you intend. I've got a Nuxt application that we are going to try and use here. I'm not sure what's going on, but let's hop into the Directus instance. So I'm just gonna pull up Chrome. We'll log in to 8055, and I should be able to pull up my back end. So great. Got Directus running. You could see this is a pretty blank instance of Directus. This is just the boilerplate I use now. There are a couple extensions installed that I was testing, just messing around with. But, let's make sure. What are we doing here? For MPMI. Sometimes these things never go as planned. Okay. So maybe now we can get this Nuxt application up and running that will be served at local host 3,000, and we'll just basically use it to scaffold out our communications. As far as what I'm using, I've upgraded this boilerplate that I've used for 100 apps to, the Nuxt UI v three alpha, just to play around with Tailwind four and, you know, some of these nice new components that are coming from, like, Radix view. So alright. Let's actually model this thing out. Right? What do we need as far as our data models? I think we just need, like, a maybe like a profiles. So under profiles, we would have, what, our username, letter from Santa, letter from Santa, list, you know, are you naughty or nice? Great. And what else do we need? Let's let's jam on that. So we'll, just set up the back end for this. I'm just gonna create a new collection. We're going to call this profiles is the name of it. And why can't I zoom way in? There we go. That's maybe too far, but all good. Let's do created at, updated at. K. Status sort not needed. Who this was created by, I'm not super concerned with. So now we have a profile. We're gonna do the username. Great. That's where we'll store the GitHub profile. What else do we need? What else did we have here? We've got the letter from Santa. What is that gonna be in letter from let's just call it letter. Great. We'll use the WYSIWYG editor inside Directus so we could just store, I'm assuming, HTML content for that. And then we've got the list. So that's basically gonna be a string. We can, you know, make this look nice inside the directus admin. We'll just give it a naughty. Feel naughty just typing that out, and then we have the nice list. Great. There we go. And I'm just going to go on record that we're probably as soon as we start typing naughty into the AI stuff, we'll probably get some some things back. Like a I probably set off the content alarms or something like that. So there we go. We've got a username. We've got a letter. We've got a list. You know, I could potentially put that in here. What I'm gonna do now, I'm just gonna let's just we're just using Directus to store this at the moment. Right? There's a lot of different ways I could go with how to actually generate the application here. But Directus allows me to create custom extensions. What I'm gonna do here is just start, working on this from the Nuxt side of it. So we're gonna input the, actual form here. Let's add a profile. What are we gonna call this? Let's just call this letters dot view. We'll get a view component set up. Lang equals TS. Great. I need to work on my little macros here. Okay. Alright. The other thing that you'll notice here is that I am using cursor. So cursor I recently started testing this thing out. Really enjoying the actual auto completions for this thing. So, I don't have it usually generate like a a giant list of code, but the automations, or the auto completions are are pretty nice for this thing. So let's start with, what, step one. It'd be enter GitHub profile info. GitHub username. Alright. So the only thing here, sometimes it gets a little wonky with the okay. So we use the you form from Nuxt UI. Good question, Brian. The new one, the alpha, they changed some of the conventions. So I've got a form with a schema. I've got a form field instead of a form group, and then I've got an input. Okay. So we've got the form, new form field, and input GitHub username. Let's just see what that gets us on the front end. We're gonna go to this page, which is letters. Okay. Alright. So let's go ahead and just center this up. I think there's actually a container component we can use. Great. Cool. Okay. So now we have a GitHub username, and let's add a submit button. New button, click handle submit, and boom. We have a GitHub username, blah blah blah. Hit submit. Supposedly does something. What it's gonna do right now? Absolutely nothing. Alright. So the next thing that we wanna do, let's kick this thing off. We want to have the form state, we use reactive for that. Great. GitHub username. Okay. And then we're gonna write a function to handle submit. Thank you. Yeah. Great. We'll just, console log that. Right? Boom. There we go. We can see the GitHub username, yada yada yada. Alright. This is actually gonna be an async function. Great. Okay. So now what do we wanna do with this? Right? We have to think about our application structure. And what I'm gonna do here is just basically add a Nuxt server route. So if we break this down, in this server route, what we're gonna do, call the GitHub API, call GitHub API. We're gonna wanna grab a couple pieces of information like the user profile, or any other repos, and maybe, like, their their public read me. I guess we could loop through the actual repos and, you know, pick up more information there, but, let's see what we can get done with that piece. Alright. So let's just go here. We're gonna set up a new route. Let's call it, roast route. We'll do post, and just gonna copy the event handler here. K. So now whenever we hit this route with a post, it should return hello world. We can just check and see if that's actually gonna work. So, we will do the I'm trying to think if that's gonna return. Nope. So we got the response. We're gonna do await. We can use the regular fetch or the dollar sign fetch, which is the Nuxt specific version. And just test this out, see what we get back in the console. Where are you? Okay. Yeah. So we can see the request going out. We can see hello world coming back. Great. Cool. Alright. So now what we're gonna do, right, let's just scaffold this out. We are going to pick up the body. There's a wait read body. Great. So that is going to have the GitHub username in there. And, how did we spell that? Yep. Great. Alright. So we're gonna say GitHub username, and then we've got, like, this git roast function. I'm not really sure where some of these auto completions are coming from. But, what we're gonna do next, let's call yep. There we go. That's a good one. API users, GitHub username. Is that the correct one? Let's just test that. All the developers on my team are screaming and crying at the moment, watching all these AI auto completions. So that seems fair. Great. And let's actually use the Nuxt equivalent. Just this is using OFETCH, which does some automated data transformation and should automatically throw errors for you as well, which is nice. So this is gonna be the let's call this a profile. Alright. And then if we take a look at the profile, we probably wanna get the actual repos for that user as well. Alright. So we'll get the repos. Great. And let's just take a look at the data we're getting from the actual repos. Okay. So what do we actually concern ourself with here? Do we actually want all of this information? What do we actually care about from these? So stargazers, watchers counts, maybe those properties. You know what? Let's just jam it all in there and see what comes out of it. Right? And then let's get the profile readme. GitHub user content, GitHub username, repos dot name, Repos dot name. No. That's not gonna cut it. I think it's gonna be, what, GitHub username. GitHub username. Somebody who's already done this before. Give me the structure. And then main. Let's just see if we can find that. Read me will just populate my name. I don't even know if I have a actually have a read me. Yeah. There we go. Okay. So that is the structure. Great. That is what we needed to confirm. And now let's just actually return this and see what we'll get back. Alright. Brian Gillespie. Now I'm gonna fire this away. Roast. No. Nothing found. Well, that's a little concerning. GitHub username equals body. Read the body. We have fetched the user's GitHub username. Let's just console log the username. API dot GitHub users. I don't see the actual username coming back. GitHub. That's always fun. Alright. What did I do wrong? GitHub underscore username. Okay. Oh, duh. Are we actually passing that in the body of the form? Form. Console dot log response. Request payload. API slash roast dot post. What are we getting back here? GitHub username form dot GitHub username. Oh, that's right. We are missing a state variable here. So is it actually submitting the form? No? No. Okay, friends. What do we do from here? We have handle submit. We're going to use fetch await. Wait that fetch request. We should have already got this back. Of course. There it is. What a dumb dumb. Forgot to actually fix the v model there. So that's what that is. Sometimes, these are not great to do at the end of the day. But okay. Where we at as far as time? We've got forty two minutes remaining. I feel pretty confident on this one. Alright. Now with our roast, we can remove this. We should be able to get that information. Now let's make sure that we're getting what we want back from that API. Great. There's the profile. There's the repos, and there's the profile. Read me. Great. Alright. So what are we gonna do with these now? Right? The next step in this process would be to, pass the profile to LLM and ask it to summarize for us. What do we want this to return? It should return something that looks like this. We want a letter from Santa. Letter from Santa in HTML. And then we're gonna want the the list, naughty or nice. And that should be all we really need to return. Alright. LLM returns JSON. Cool. So what is the LLM we're gonna use? You know, typically, I use OpenAI for a lot of the stuff that I do here at Directus. I've been messing around a lot with, Claude locally. So we're just going to try this out. Santa letters. So we're gonna use anthropic. There's my API key. We're going to drop that in our ENV file, if I can actually get there. Jeez. There we go. I'm just gonna call it Claude ABI key. Okay. Great. And by the time you've watched this, hopefully, I've disabled that key. So, don't stop the video and try to figure that out. Alright. Let's pull up our docs for the API. We need to get the API reference. And let's define this prompt. Prompt. You are a letter writing AI. Alright. Analyze the following GitHub profile. You are the open source Santa Claus. You determine whose open source contributions are naughty or nice, analyze the following GitHub profile, Return a JSON object with the following fields, a letter from Santa and HTML. Set a really high bar for the nice list. What else do we need as far as a prompt? And, yeah, here is the data, profile, readme, json, stringify. Wonder why it's doing that. But okay. Nevertheless, there we go. Turn a JSON object instead of really write the letter in a snarky sarcastic tone. Cool. Alright. And now we're going to send that to Anthropic. Alright. So if we look at their oh, looks like we could just use their JavaScript SDK. That's great. Let's go ahead and open this up. We'll fire that up, install this thing. Import anthropic. Great. And then we're going to create that message. Alright. Constant AI response equals anthropic messages dot create Claude Sonnet. Okay. Messages user role, content prompt. Do we wanna set, like, max tokens? What is the what's the default for max tokens? Where do we actually pass this API key? Getting started authentication, x API key. API key equals process e n v. And, again, like, you could start to see why I really like using cursor because it has, like, this sixth sense for a lot of this stuff that I'm actually trying to do. Sometimes it gets that wrong, but a lot of times it gets it right. So alright, AI response messages. Do we wanna set a max tokens? Body messages, max tokens required. Let's give some more parameters. Write a short letter in a short in a snarky sarcastic tone. That is 500 words or less. And then for the tokens, if we look at Sonnet, we've got like a context window of like 200,000, so maybe a hundred thousand tokens. Oh, no. That's the output. Max output is eight nine one two. That's fine. Max tokens. Great. And let's return. Actually, what we're gonna do next is save that to the Directus database. Right? So we've got this collection for our profile. What I've also done, I've got a utility set up here. This is just using the Directus SDK. And one of the nice things about Nuxt, I say that a lot, is, the ability to it will auto import this for me. So I don't have to import it. I should just be able to call Directus server right here. So let's call it Directus response equals await directus server dot request create item. That's going to be in the profile. And we'll do the GitHub username. That's actually going to be username. Letter response, content dot text. I don't actually know what we're gonna get back directly. Return only a JSON object. And maybe we wanna add something like this for let's just do code. We'll set this up. And I'm just gonna add a field for, let's call it metadata or something where I'm just gonna store the entire response. And honestly, let's just do that to begin with. Metadata, AI response, content dot text. So if we take a look at the API reference, we go back to messages here. I'm kinda curious as to what we're gonna get back. The content text. Okay. Type text something. We'll get back something from the system. Let's just even do it this way. We'll say content direct us response, and then we're going to return direct us response. See what that gives us. Now let's go in. Where's our app? We'll switch back to Chrome. I do like Arc. I've found it to be lacking for development because it's just not super fast. Alright. So fingers crossed that this actually does what it should do. And, let's make this even nicer. And we'll add, like, a loading state, constant loading, ref equals false. We'll add loading dot value equals true. Loading dot value equals false. Great. And what else do we want to do? Is there a loading state on the actual form? Let's take a look. So Nuxt UI state, there is not a loading state on that. There should be on the button though. So just update that. Okay. And let's test this bad way out. Submit. Alright. We're waiting. We're waiting. We're waiting. We're waiting. We're waiting. This could take a minute. So, you know, we might even want to, like, potentially set up a oh, okay. So we're not getting anything back. We see a request error. So let's go into our roast, and we should probably do some error handling. Alright. Catch error, console error. Return, or we could just throw the error. What do we got here? Format. Alright. Let's refresh. I'll try this again and see what kind of error we're getting and why. Pending. Invalid user credentials for Directus. Okay. Great. So, just wasting tokens there, throwing them into the void. One of the things that you'll notice, I do have this direct as URL set up, but, my server token is probably a % not correct. So I'm gonna go in and create a token for this. We'll just create a new token. We'll call this the server token. And I wanna make sure in my utility that I have that set as server token, direct us URL. Okay. Let's try this thing again. PPM dev. I will restart the dev server, pull in that new ENV, though I think Nuxt may automatically update that for us. How we doing on time? We got twenty nine minutes left, so I'm feeling pretty confident that we can get something out of this. Let's go ahead and try it again. Bryant Gillespie. Submit. K. K. Roast. You do not have permission to access this. Okay. Can anybody spot the error? It is because I left off a s. We have profiles, and this is profile. So again, if I I don't know if I you can actually see the logs for anthropic. Okay. Yeah. We could see here's the actual logs. It's probably not showing what we've got there. But anyway alright. We'll try this one more time. Let's just clean this up a bit. And away we go. Dun dun dun. I don't like the looks of this, actually. Let's just reset. Try this again. AI response. We got the prompt. Got the profile. Dun dun dun. The moment of truth. Are we actually gonna be able to get this thing to work? Brig Gillespie. Submit. Obviously, this would probably be better as, like, a background job or something like that. Alright. So we refresh, and we have something here. Okay. Yeah. So we're getting some text back. It looks like we need to parse the JSON. The letter is going to be text parsed response, text, parse response dot list, and then we get metadata, which would just be the parse response, I'm assuming. Alright. We're gonna delete this out. Let's run this again. And hopefully I'm not burning through all these credits that I loaded up. Okay. So now we're looking great. Okay. So we have our username. We've got our letter. Ho ho ho. What do we have here? Another developer thinking they can impress Santa with a few measly repositories. I've seen l's with more impressive profiles. I got made it to the naughty list. Great. Amazing. Alright. So now that's working as intended. Let's let's make this pretty. Right? The form, we're going to do max width. Maybe we set this to Excel. Move that form to the somewhat in the center of the page. And let's just lean on AI here. Right? This is already pretty cool. One of the other things I wanna do is maybe we set up a route where we actually surface this letter. Right? So if we do let's do letters as a directory inside pages. And we're gonna do the username in brackets. So just take this username, make that in brackets, and then I'm gonna put letters inside here, and we'll change the name of this to the index route. Alright. So let's just clean this up a bit, wrap this up, and console the error loading. Actually, we could do that in finally. Great. And what we're gonna do, if the response is good, we could navigate to the username page. Cool. And that way, you know, basically, like, this could get very expensive if if you made this thing public. Right? You don't want people generating like 35 letters to Santa. So we can add a check to the database if we've already got that GitHub username and just return the letter that we we already have. Right? Okay. So on the response, as long as there's no error, we're going to navigate dot to form. Github username. And this would be await. Navigate to. Cool. Alright. Now let's just lean on AI and see what we could do. Add some Christmas theming to this. Let's see what this actually will do. Add some Christmas thinging, ho ho ho. Looks like it's generating some random messages. Code review letters to Santa, random message, decorative elements. Great. Love decorative elements. Now, with cursor, I'm just gonna click apply here. It should go through and run through this actual code. I can close this out and see, you know, in kind of a preview way what it's gonna change. And if we hit reload oh, what we got going on here? Letters index. Is that because I changed the route? Okay. Yeah. Now we're looking very festive here. This looks this looks great. AI, what can you do? Alright. The other thing I see, maybe we want this to be block. Will that get it done? Block. Class. Let's just make the width full. Width full. Okay. And then let's shrink this actual form a bit. Yeah. MD. There we go. Alright. We're deep in the Christmas cheer now. And, while we wait, let's well, not while we wait. Let's actually go in and now we're gonna work on this letter. Alright. So, this does have a Nux plug in. This is just my boilerplate where I can go in and actually request the information from Directus on the client side, or, you know, I could set up a route for this on the server side in Nuxt. Both of those ways are are totally valid depending on your application. Obviously, totally up to you. We will just, let's let's keep it the same theme. We're going to, like, fetch roast, or we could do, like, a roast.git.ts. And what are we gonna pass? Do we want to pass the username as a param, or we'll just pass it as a query parameter? Okay. So in this one, what we're gonna do, we will call the profiles endpoint inside Directus. So we'll just go const, response equals await Directus server. And, you know, sometimes you wanna make requests on the server side. That's why I've got this set up, this way. We're gonna do read item, and I gave this a UUID. We could've used the actual profile as the primary key. But, what we're gonna do, read profiles, and we're gonna set up a query for this. So we'll do a filter parameter, the username. So that's the field. We're gonna drop down again. This will be equal to the username. So first we're gonna have to get the username equals get router param. Nope. We're gonna do git query, and that would just be the query. Great. Username. We could destructure this if we wanted to. Return username equals username, and we're gonna return that response. Great. Cool. So now we do this. And on this one, what we can do is use the use fetch composable from Nuxt. So this will be we've got some data. We're gonna use fetch, and we're gonna call API slash roast. And the is it params? I believe. See what we got. And let's add the so the same festivities, I guess. Festiveness. Perfect. Alright. Decorative elements, blah blah blah, random messages. We're gonna put that up here in the script. Okay. Code review letters to Santa. And instead of the form, right, we're gonna replace this with data. Alright. So now if I do this, what's gonna happen? Route is not defined. Okay. So we just need to call use route to fetch that route. And do we actually get the stuff that we need here? We could test this API as well. Letters API roast username equals Brian Gillespie. Okay. Yeah. So that's getting us what we want from direct us except is it query? What is the use fetch? This is where, like, Nuxt documentation comes in handy. Use fetch. Where we at? We got sixteen minutes remaining. We got data use fetch. What are the URL query? Okay. Alias for query. That's what I thought. Root params username. Oh, data. Are we actually let's jump into the view dev tools. We'll hit the username route. And I see the data here. Here's the issue. Right? It is returning an array. So inside our routes, we could, you know, do something like this where we're just picking off the first item. I could also do that transform that on the the Nuxt side if I wanted to. Here's our letter from Santa. Cool. Code review letters from Santa. What I'm gonna do, let's use the pros class from Tailwind to get styling for this. We'll make the text dark green. Great. That's fine. And then the interior of this, we're just going to use v HTML. So we get this. Do I not have Tailwind typography into this? At plug in Tailwind typography. Okay. Yeah. So there we go. Now we've got the letter from Santa Claus. This is looking really nice. Perfect. Let's add like a cursive font. Right? Font family cursive. And this is Tailwind four, where all the config is basically CSS variables. So, really enjoying that Nuxt module, playing around with it. Let's find a handwritten font. Okay. Caveat. Looks nice. Nuxt has also added a a like this font amazing thing where you just throw your fonts in the CSS and it will actually download these things for you. So let's take a look at this. Right? I'm just gonna change this to font cursive and bada bing bada boom, we get what we want. So let's put, like, pros XL to XL. And there we go. So dear Bryant, what do we have here? Blah blah blah, etcetera. We have got thirteen minutes left on the clock. What can we do for fun? Let's go back and actually test this thing out. I'm just gonna refresh. There we go. I'm gonna do our fearless leader here at Directus, mister Ben Haines. We're gonna send this to Santa, and something bad happened. We could not find okay. So it looks like this one is not finding Ben's profile. Haynes, Maine. And Haynes Haynes Haynes Haynes Haynes. Would that be at, like, Master branch maybe? Where's our roast? Roast.post, profile read me. Try. I bet it's at master. I'm just gonna do this the quick and dirty way. Alright. So we go back. Let's try this again. Mister Ben Haines, we're about to roast you, sir. Alright. So we're checking the list twice. And eleven minutes on the clock. We've got the letter to Ben Haines from Ben Haines. Why are we not seeing the actual letter? There it is. I'm dreaming of a Nuxt application that actually works. What is going on with this? Letters, username, data dot name. I'm assuming because there is no name. Username. I'm running this on a sour note here. Ben Haines. That's kinda weird. Ben Haines. Why is it doing that? API roast username. What is going on here? Get async data. API roast. Why does it work for me and not for mister Haynes? What are we actually doing wrong here? Did I spell the name wrong? GitHub username. Alright. E pipe. Use fetch roast. Can't find the username? Profiles get username, get query. Is it read query? No. It's get query. Return query. API API roast. Ben Haines. So why aren't we why isn't this working? So it's not actually finding the username for that, which is odd because I have the username right there. Filter contains. Okay. I don't understand it, but we're gonna roll with it. Great. Some type of encoding or something maybe. Not sure. Booyah. Ben, I'm gonna read this to you. Dear Ben, ho ho ho. Well, isn't this embarrassing? I've been reviewing your GitHub profile, and I must say I'm thoroughly underwhelmed. 20 whole repositories, you must have been super busy this century. Meanwhile, Santa's got billions of believers worldwide. Look, I'm not saying you're on the naughty list because your contributions are lackluster. I'm saying if you were open source for coal, you barely have enough to heat a dollhouse. That is brutal. So let's call that a win. This is AI letters with Santa. Do we wanna do one more just for fun? Just for giggles? Let's let's test this out. Directus Directus. I forget Reich's actual, GitHub profile. There it is. Okay. So we're gonna throw mister Reich Van Zanten in there, our CTO, see what comes out of this thing. Hopefully, we got everything we need. It will do its thing. And and, that's not gonna pick on Wrike. Yeah. I don't know what's going on with this thing. Potentially some kind of caching issue. Don't know. Anyway, response zero. Down to the wire, five minutes, four minutes, three minutes, two minutes, no minutes. Is the server running? Use async data. And what if we just use fetch? Response. It's gonna be response dot letter. Oh, boy. Response fetch, browse, params, username. Come on. Failed to stringify the server logs. What is going on? This feels like a crappy way to end this one. It should be, like, festive with holiday cheer. I don't understand what is going on with Nox. Oh, duh, dummy. You have to wait the promise. Is that getting us what we need? Still not getting us what we need. Data is not defined on the instance. Where else am I getting the data at? Fested messages, data. Oh, if response. We're not even getting a response. SSR, undefined, undefined. Hey. That's the way the cookie crumbles sometimes. I'm not sure what I am doing wrong with this. I'm sure it'll come to me right after I get done with this. Is it like a key? Cash. Cash, no cash. Cache. No cache. No cache. Well, at least Ben's works. At least mine works. Not entirely sure what's going on with this little API that I've written, why it is caching this. But, hey, that's AI letters to Santa. That's the way it goes. This has been a hundred apps, hundred hours. Thanks for joining me. We'll catch you on the next episode. See you.","ec46771b-85fb-4967-9a7e-81102f96cf74",[1192],"cc9262db-1ae2-4bc1-a1f9-7d2fc51a396d",[],{"year":399,"number":1060,"id":1061,"episodes":1195,"show":1196},[1063,1064,1065,1066,1046,1067,1068,1069,1070,1071],{"title":322,"slug":323},{"id":1064,"slug":1198,"vimeo_id":1199,"description":1200,"tile":1201,"length":1060,"resources":12,"people":12,"episode_number":177,"published":1051,"title":1202,"video_transcript_html":1203,"video_transcript_text":1204,"content":12,"seo":1205,"status":19,"episode_people":1206,"recommendations":1208,"season":1209},"multisite-cms","1059434419","Amid the WordPress drama, Bryant builds a multi-site CMS system that lets you manage content for multiple websites from a single Directus instance. Watch as he sets up relationships between sites, pages, and users, then configures advanced permissions to ensure content editors can only access sites they're assigned to—all while creating a secure API structure for your frontend applications.","5e7c1362-710f-4604-a812-93290e4bdeba","Mission: Multisite CMS","\u003Cp>Speaker 0: Welcome back to the next episode of 100 apps, one hundred hours. I'm your host, Brian Gillespie. And today, we are going to be building a multi site CMS. It is been impossible to ignore the WordPress drama over the last month or two, and that has left a lot of folks wondering, hey, should I be switching our CMS from WordPress? WordPress has the multi site mode, so we're going to walk through how to build that today with Directus.\u003C\u002Fp>\u003Cp>If you are new to 100 apps one hundred hours, there are only two rules. Number one, sixty minutes to plan and build, nothing more, nothing less. Everyone knows the rules. And, the second rule, the anti rule, one that I'm definitely going to invoke today, use whatever you have at your disposal. So let's dive right in.\u003C\u002Fp>\u003Cp>We're gonna start the clock. Sixty minutes on the timer. Let's talk about multi site CMS. So what are we trying to do here? We want to manage content for multiple sites.\u003C\u002Fp>\u003Cp>That could be two, that could be three, it could be 25, it could be 50 from a single CMS instance. So we will share some of our data models amongst those sites, but the actual content will be different. So, as far as the functionality that we're looking for out of the system, let's just mock that up. Functionality. Right?\u003C\u002Fp>\u003Cp>We want to be able to manage pages and posts and other types of content for multiple sites within one CMS. We wanna make sure that there, are users can only edit and access data for sites that they are assigned to, owners of, assigned to, etcetera. That's the main functionality that we are looking for. Right? So one of the other things that I always do on these is basically map out our data model.\u003C\u002Fp>\u003Cp>Right? And if we're going after WordPress here, we have, like, pages and we typically have posts. Those are kind of the two pieces of content that, you know, when I think of WordPress, I think of, okay, here's what's inside, the data model for that. But we're also gonna need to go up to a higher level. Right?\u003C\u002Fp>\u003Cp>Because each page, each post is going to belong to a certain site. Now in this scenario, I'm thinking each site or each page only belongs to a single site and if you need to duplicate a page for another site, you just create a a entire copy of it. But, you know, you could potentially set this up if you are sharing the same page across multiple sites. You could model that relationship as well. You know, that's one of the advantages of Directus as a CMS is that you can model your data however you want, and your table structure inside your database will follow suit.\u003C\u002Fp>\u003Cp>Directus just makes those edits for you. And then, you know, we're going to need a relationship to users. Right? So users are going to be kind of a mini to mini relationship. Right?\u003C\u002Fp>\u003Cp>Love drawing these arrows. We need an arrow over here. And, let's just say mini to mini. Right? One user could be working on three, four, five sites or could be a single site, and, you know, we could have many users working on the same site.\u003C\u002Fp>\u003Cp>Alright. So as far as our data model, that's that's pretty good. You know, we'll dive into the nitty gritty details in just a moment, but I'm definitely gonna invoke rule number two here, and I'm going to go into Directus Cloud. And you can call this cheating. You can call this taking advantage of what we've already built previously.\u003C\u002Fp>\u003Cp>But we are going to do a new project using our simple CMS template because it gives us most of the functionality we need right out of the gate. So we're gonna call this multi site CMS test or 100 apps. That's good. See how long we can get the URL for this particular project. And then I'm just gonna click simple CMS here.\u003C\u002Fp>\u003Cp>I'm gonna switch this to monthly, and I'm gonna check out. And this will spin up our project for us. So, if we think more along the lines here of of what we actually need to do, You know, this simple CMS template also has forms. It has navigation, and I'll show you that in just a moment after we get it fired up. And then we have a global settings collection, inside there, which is actually, we're gonna have to do a bit of work to that.\u003C\u002Fp>\u003Cp>Can't get my arrows straight here. Jeez. Okay. There we go. Alright.\u003C\u002Fp>\u003Cp>So those all roll up to the sites. There are many users, many sites. We're just gonna wait for this Directus instance to spin up. And I'm assuming I've probably already got an email with this thing. Great.\u003C\u002Fp>\u003Cp>We'll just copy this. And feel free to steal this password here because this project should be turned off by the time we get to airing this actual episode. Alright. So we're just waiting for the CDN. Are we ready yet?\u003C\u002Fp>\u003Cp>We are ready. Okay. So great. Now we've got our login. Let's just go ahead and fire this up.\u003C\u002Fp>\u003Cp>We'll get logged into our simple CMS instance. And let's just kind of go through this actual CMS. I'll zoom way in and we can kind of dive through the content here. So we've already got some pages. We've got posts.\u003C\u002Fp>\u003Cp>We have forms, form submissions, navigation. So within pages, we can see we've got standard stuff, title, a permalink, what the status is. We've got some blocks here, of different content. So this is using the mini to any relationships inside Directus. I can drag and drop this to build a page dynamically, which is nice for our content editors or the marketing folk that are actually working on the content for the site.\u003C\u002Fp>\u003Cp>Then we have our blog posts. So lots of bunny related content here. We've got slugs. We've got status. Is this published?\u003C\u002Fp>\u003Cp>Yes or no? We got description, author, actual content. There's some AI features baked into this template. It is, a really nice starter kit for any Directus project if that's what you're looking to do. We got form fields.\u003C\u002Fp>\u003Cp>Great. There's a newsletter sign up, we need a first name, we need an email address, etcetera. And then, you know, every site needs a menu. So here we have some navigation, pretty cut and dry. And then there's the globals collection where we're storing settings that are going to be per site in our specific use case like logo, favicon, favicon, five icon.\u003C\u002Fp>\u003Cp>Great. Never know how to say that one. And we will dive in. Right? So we've got pages.\u003C\u002Fp>\u003Cp>We've got, you know, I could go in and query this particular content. So if I just drop this admin off the URL and I go to pages, we could query this content. If I wanted to see the individual content of the blocks, I can add a fields param to this and just do an asterisk, which will essentially act as a wildcard. It'll give me all the root level fields, and then I'm gonna say blocks, and I could do blocks dot collection, blocks dot item, And I can see I can start to fetch all the content within, and and I could keep drilling into this if I wanted to. But that doesn't cover our use case of multisite.\u003C\u002Fp>\u003Cp>Right? So the big glaring problem with all of this is we don't have sites. So let's go in, we're going to create our sites collection. We'll go to the settings, we'll just create sites. I'm going to use a generated UUID for this.\u003C\u002Fp>\u003Cp>We can add some of these in the canned system fields that add functionality like timestamps and users automatically. I'm just gonna leave this blank for now. And we're gonna give this site a name. And let's add a little hint for the user here. So I'm going to go to Advanced Field Creation Mode.\u003C\u002Fp>\u003Cp>I'm going to go to the Field tab and we're just going to say the internal name for this site. Great. That'll give us a hint. And voila, now we have sites. I'm gonna give this a nice looking icon, maybe a globe.\u003C\u002Fp>\u003Cp>That stands for, like, website. Right? We'll drag this up to the very tippy top. I've got globals as well. Maybe we need to change the icon.\u003C\u002Fp>\u003Cp>Let's do, like, settings or something. I used to be a designer. I like to refer to myself as a recovering designer, so, icons and user experience and small little details like that matter a lot to me. So let's go ahead and create a new site. Right?\u003C\u002Fp>\u003Cp>We're gonna do Agency OS. That's one of our other, open source projects that we released. And this is gonna be site b. Right? The other site.\u003C\u002Fp>\u003Cp>The site that I don't like. Site b. Don't even care about it enough to give it a nice name. Alright, so now what do we do with these specific sites? Right?\u003C\u002Fp>\u003Cp>Now that we have a site, maybe we want to go in and establish the relationship between all the different pages and things. Let's start with our globals though. Alright? These are settings, like the title, the URL, the logo, the favicon. Some of these we're gonna use for SEO.\u003C\u002Fp>\u003Cp>Some of them, maybe in the the footer like social links, things like that. We want to have these per site. Alright? So one of the nice things about Directus is this visual data modeling. I can come in and I can copy these fields across collections.\u003C\u002Fp>\u003Cp>So we will go to sites. We're gonna create a duplicate of that title field. I'm gonna duplicate the URL. I could even get fancy if I wanted to and potentially create, like, a flow to duplicate these things. You'll notice here on the ones that are relational, like, this is based on a many to one relationship.\u003C\u002Fp>\u003Cp>I cannot duplicate those fields just because the underlying schema changes that happen. But just regular any other fields that are not relational, I can easily duplicate. So we'll do that. We'll add our description. Copy that over.\u003C\u002Fp>\u003Cp>And let's do our social links. Now you'll also see there's this credentials group, like our open AI API key. Gosh. That's a mouthful. I wish, I wish their name was a little bit different because I'm dealing with these API keys in a lot of episodes, but I digress.\u003C\u002Fp>\u003Cp>So these things are probably gonna be they're gonna stay global. Right? We don't actually need to remove those. So here, let's just remove title. Let's remove URL now that we've copied it.\u003C\u002Fp>\u003Cp>The logo is just a nice little divider there. We'll remove the favicon. We'll delete all of these fields that are no longer global in our data model. And today's exes episode is is really just a, a big exercise in data modeling. Alright.\u003C\u002Fp>\u003Cp>So now if I go to the globals, all we have is the direct Us URL. So we're gonna serve all the the content for those sites out of a single instance. That's good. You know, they would probably share an API key or, you know, we could set it up for where that API key is per site if we wanted to. But now we can see we have these things, like, agency OS, the best operating system for your agency.\u003C\u002Fp>\u003Cp>Alright. We'll give this a URL, agencyos.dev. Run your business better. Alright. We can add social links.\u003C\u002Fp>\u003Cp>Great. Cool. Gravy. Right? And now, I could go in and, you know, I just got this option here to copy the API URL, and now I could see that individual site.\u003C\u002Fp>\u003Cp>So this is just my directus URL slash items slash site slash ID, or I can get a list of all the sites. What I don't see are my pages. Right? So how do we set up ownership of the pages and posts and things like that? So we want to use the relationships inside Directus.\u003C\u002Fp>\u003Cp>We're gonna set these up. We're gonna go to our sites. And again inside our data model that we spec'd out, pages belong to a single site. So one page belongs to one site. Same thing for posts and all the way down.\u003C\u002Fp>\u003Cp>So what we're going to do here is use the one to many field inside Directus. We're going to call this pages. The key is the name of the field inside the sites collection. We're gonna pick our related collection, which is gonna be pages, and then the foreign key will be the field that holds the site ID in the pages collection. So, you know, this might be site ID.\u003C\u002Fp>\u003Cp>I prefer just calling it site just because of the way that you can grab that relational data in a single API call. Alright, so the next thing we want to do is make sure we set up our display templates. And in this case, you know, I could go in and pick the fields that we wanna display, but what I'm gonna do here, I'm gonna use the table layout instead. So I could show multiple columns here and kinda get like a a standard table view that I'm looking for. So we'll add the title of the page, the status of the page, and do we need any more than that?\u003C\u002Fp>\u003Cp>Maybe not. Maybe we want permalink just because that could be dramatically different than the title. We will sort those by our sort order, and we're just gonna enable these other options to enable searching and to show a specific link. Okay. So now we've established a relationship to our pages.\u003C\u002Fp>\u003Cp>What does that actually look like? Alright. Let's scroll down. I can already see that the UI is getting a little bit, messy as far as user experience. We'll tackle that in just a moment.\u003C\u002Fp>\u003Cp>But here, now I can see my pages. I can create a new page directly from this site, or I can go in and add existing pages to that site. So I could say, okay. Hey. This site has a home page.\u003C\u002Fp>\u003Cp>It has a privacy policy page. I can go ahead and add these other pages to this specific site. There we go. I can save and stay, and then I've got, you know, the ability to edit these pages. I can also navigate directly to the page from there.\u003C\u002Fp>\u003Cp>So this is looking nice. One of the other things that I wanna do here though, because I care a lot about what my content editors are working with inside the CMS, we're gonna clean this up a bit. And I'm sure if you're watching this, you care as well. So there's an extension that I love to use quite a bit. It is called the group tabs group tabs interface.\u003C\u002Fp>\u003Cp>I've already got this installed in the simple website CMS template. It is by mister Hannes Kuttner, which is a a member of our core team. I think he actually built this before he joined the core team, though. Great little extension. Not sure why my images aren't displaying here.\u003C\u002Fp>\u003Cp>But basically, it gives me a nice tabbed interface. So what we're gonna do, we're gonna go to our sites data model, and I'm gonna look for the tab group option. Now this has got an option to actually fill the width of the page, so we'll take advantage of that, and I'm gonna save this. So this basically gives me a container where I can start dragging and dropping these other fields within, and we're gonna clean up this UI a bit. So let's see what that actually looks like.\u003C\u002Fp>\u003Cp>Boom. Now I've got title, I've got URL, I've got tagline. Except, you know, maybe I want to nest all of these fields under one single tab, right, instead of having, like, 35 different tabs. So what I can do here, I'm gonna reach for another field type here called the raw group. This doesn't actually create any columns in the database or anything like that.\u003C\u002Fp>\u003Cp>We're just gonna call this group branding, for instance. That's gonna be a key that we need to add, and then that gives us another container. And I could do things like this, where now I've got the title, I've got the URL, got the tagline, description, social links, and maybe we put pages up here ahead of those. So basically what I'm doing now is constructing the interface. So now we have pages.\u003C\u002Fp>\u003Cp>We have all these fields that show up under group branding. That doesn't doesn't seem right to me. Right? That looks kinda funky. When somebody who's adding this, they're gonna say group branding.\u003C\u002Fp>\u003Cp>What does that mean? So how can we control that? Right? The developer in me wants to say, hey. I need everything nicely name spaced and organized just for my own sanity, especially when I add more people to this.\u003C\u002Fp>\u003Cp>But the designer, the UX person in me says, hey. This this doesn't make sense. So what we can do is go into edit that specific field, and under the field settings, there are the field name translations. This is one of the most underrated features in Directus, I think. So I could just give this a translation.\u003C\u002Fp>\u003Cp>Right? And I pick the language. Of course, my user is set to the English language, but you could create translations for as many different languages as you have users. And Directus will display the correct one because when they log in, they can set up their default language on their profile. So I can work in English.\u003C\u002Fp>\u003Cp>Alexander on our team, he can work in French. Some of our fine folks from Germany, they can work in German, and so forth. Whatever it whatever we need. Alright. So as soon as I edit that, nothing changes here.\u003C\u002Fp>\u003Cp>But when I go back to the sites, now you can see that tab has a nice name that more aligns with what we're trying to achieve. Alright. So now that we have this relationship between pages, right, we keep moving on down the line. I'll go back to sites, and we're gonna add another one to many relationship for posts. That will be the related collection of posts, the foreign key, we're gonna use site.\u003C\u002Fp>\u003Cp>We'll add a table for this. And let's say we want the title of the post, so we want the status of the post, and we want when this was published. You know, typically, blog posts are rolling through in chronological order. So now for our sort field, instead of, like, an actual sort field, we're probably gonna wanna do published at. And maybe we do that by descending order so that we show, the most recent posts first.\u003C\u002Fp>\u003Cp>I can set how many pages or how many items per page that I wanna show in the table. And away we go, we can hit save. Great. So we'll add posts. Let's go back and hit one to many again.\u003C\u002Fp>\u003Cp>We're gonna do forms. So we'll do related collection forms. We will do the foreign key of site. Keep with that same theme. For the forms, do we actually need the table view?\u003C\u002Fp>\u003Cp>Yeah. Maybe we wanna add that. Right? So we've got title. We've got, the number of fields for the form.\u003C\u002Fp>\u003Cp>Maybe we wanna show that information. The number of fields, what fields we have. Over the fields we go. Laughing all the way. How many submissions maybe?\u003C\u002Fp>\u003Cp>Looks great. Cool. And now we'll do that. Now I know that we're gonna run into an issue with navigation because I'm the one who built this website CMS template and let me show you why. So when constructing this, the Symbol CMS template is made for a single website, not multi site, which we're building here.\u003C\u002Fp>\u003Cp>So the problem is the primary key is set up for a single site. Basically this is just a string. Right? Because I wanted to be able to do something like this for our actual content where I could say, hey, item slash navigation, and instead of calling, like, a random UUID, I could just call main and I can get my main navigation. And again, using that fields parameter, I could go in and get all the underlying items for that navigation.\u003C\u002Fp>\u003Cp>And I could go even further and do items dot children dot asterisk and get all of the children or even deeper. Right? Items.children.page. And then I can get the details of the actual page that we're linking to, like the permalink, so that I don't have to create brittle links that are gonna break as soon as soon as someone changes a permalink or a slug. Right?\u003C\u002Fp>\u003Cp>But in my haste in doing that, I kind of limited us here as far as what we can do with this structure. Right? Because if this is called footer, right, I would venture to guess that a lot of the sites that we're working on are also gonna have a footer. So then we have to worry about creating unique names. So what we're gonna do here, I'm just gonna trash this entire navigation collection.\u003C\u002Fp>\u003Cp>Boom. Away it goes. I could go into the actual SQL and update this, but for this purpose we'll just keep it nice and lightweight. We'll just create this collection again, And instead of using a string as the primary key, we're just going to use UUID. Perfect.\u003C\u002Fp>\u003Cp>We'll give this a key. And let's make this and give it a key field. Yeah, just to be clever. Alright. We can even set this field to be indexed for database performance.\u003C\u002Fp>\u003Cp>And maybe we want to slugify this thing as well so that it is friendly in URLs. The internal key for this navigation menu for this menu, I don't think we need to be super verbose here. I think there was an is active option there, so we'll just set that. Is this menu actually active for the site or not? And then I'm gonna go back and just like we did on sites, I'm gonna create a one to many relationship to our navigation items.\u003C\u002Fp>\u003Cp>So the items are gonna be the individual links. The navigation is basically our entire menu. So navigation will be main menu or header or footer, and then within that we'll have the navigation items. Great. Alright.\u003C\u002Fp>\u003Cp>For the foreign key is gonna be navigation. That's our parent collection. And you can always kinda see you can already see kind of the conventions that I follow where we have a singular and then a plural here for the the child relationship, just to try to keep things clean. Again, you could follow whatever convention that you want here. Totally up to you.\u003C\u002Fp>\u003Cp>So then we'll have a title for those items. We'll show a link. We'll sort by the actual sort field that's an integer. And if I expand this a little bit, you know, we can go in and set up our display templates, which we'll wanna do just to make sure that, when we're working inside, like, a a layout, which is like the table or the kanban or whatever layout you prefer to edit this, that that information shows, the actual title instead of random UUIDs. Alright.\u003C\u002Fp>\u003Cp>So, we're getting close there. Let's just add a menu icon for this and put this back up into our list. We'll put navigation items below that. Great. And now we can resume what we were doing.\u003C\u002Fp>\u003Cp>Go back to sites. We'll do a one to many relationship here. This will be navigation. Could call it menus. That's the hardest part of doing any of these things is what to call the names.\u003C\u002Fp>\u003Cp>But we'll just follow the same convention. Navigation. There's the site. Here's the menus. We're gonna show the key.\u003C\u002Fp>\u003Cp>Maybe we wanna show the number of items there. Great. We're gonna sort by nothing. Great. Alright.\u003C\u002Fp>\u003Cp>Now let's take a look at our progress. I'm kinda curious where we're at on the timer. We're about half an hour in. Still got a good way to go. But as far as what we're working with, and I can already see, right, there's our display templates that we need to set up.\u003C\u002Fp>\u003Cp>So I'm seeing basically the UUID of all the pages, and that's not what I wanna see. Right? So I'm gonna just go to pages here. You can see I've got my interface, which is the actual form detail. That's where these things show.\u003C\u002Fp>\u003Cp>The display shows up in that layout, so the the list of all the data. Right? We're gonna choose to show the related values, and I can add whatever fields here I want from pages. So maybe the title and the status of that page. Great.\u003C\u002Fp>\u003Cp>We could do the same thing for our blog posts. We wanna show the title and the status. Maybe the same thing for the form. We just wanna show the title of the form. And for our navigation, maybe we want to show the key and the number of items.\u003C\u002Fp>\u003Cp>Let's see how that looks. Right. Now we get something that's a little more manageable. We can see, hey, here's all the pages. I can bounce out to a specific page right from this view.\u003C\u002Fp>\u003Cp>I can go into the actual site. There's the name for this site. We can see we've got pages. We've got posts. We've got forms.\u003C\u002Fp>\u003Cp>We've got navigation. We've got branding. This is starting to look wonderful. Right? We'll probably need a header for this site.\u003C\u002Fp>\u003Cp>Let's see if we can copy some of these existing items. That's great. Let's create a new one. We'll call this the footer. Add some of these other items that we did not copy, contact us, privacy policy, blog, etcetera.\u003C\u002Fp>\u003Cp>Great. Make that active. We'll make the header active. Great. We hit save and stay.\u003C\u002Fp>\u003Cp>We can see okay. Here's the items within each one of those. Perfect. Everything's looking nice. Great.\u003C\u002Fp>\u003Cp>So now we have this site structure. Right? And if I were to go to my Directus URL, I go to items slash sites, I can get all the info for the site. Site b is looking pretty lonely in this case. We'll sort that in a moment.\u003C\u002Fp>\u003Cp>But here, there's lots of ways that I could get access to the information for this specific site. Again, I could go through and use the fields params here to fetch all the pages for the site. So pages dot asterisk, and I could see here's the same content I was getting at the pages endpoint. Now I could also, like if I were to grab the ID of this specific site, right, I could go through and get all the pages. So, we can see here there's a a site field where we're storing an actual ID.\u003C\u002Fp>\u003Cp>That's great. Let's go in and how do we get back to the actual admin? Admoine. Sausage fingers get me every single time. But let's go in and add a page to site b.\u003C\u002Fp>\u003Cp>Right? So we have site b. We're gonna create a new page for site b. Site b page. Cool.\u003C\u002Fp>\u003Cp>We'll just go ahead and pretend like this is a great page. We'll publish it. Cool. And now if I go back and I refresh, right, we're seeing this page for site b. So how can I filter this out?\u003C\u002Fp>\u003Cp>I could use our filter params for that. So I could do, like, filter. We're gonna do the site, we're gonna use underscore equals, which is our syntax for that. And oh, I lost my fancy stuff. So we're gonna run that again.\u003C\u002Fp>\u003Cp>Filter site. Just wrap these in brackets equals this. And now you can see I only see the items for the agency OS site, and I don't see that page that I just created. Now this is great for us in a multi site CMS setup. Right?\u003C\u002Fp>\u003Cp>I can have all this content, I can query it by the individual site, but let's talk about the problem here. So the problem is how do we lock down this content? So I'm just gonna copy this. Right? I'm gonna pull this up in another window, an incognito window.\u003C\u002Fp>\u003Cp>We're gonna discard the changes here for site b. And I'm gonna go in and add our first user. So the scene is set. We've got our sites. We want our users to be able to start editing the content.\u003C\u002Fp>\u003Cp>Alright. So this is gonna be our agency OS content editor. Great. This will be agencyOS@example.com. We're gonna do a real secure password of password.\u003C\u002Fp>\u003Cp>Alright. So I'm gonna create this new user. I could also invite users. I'm gonna give them the role of team member, which has already has a couple policies, attached to it. So if we just look at that, we see that this person can they'll have access to the direct to studio.\u003C\u002Fp>\u003Cp>They'll have, edit capabilities for our specific content. Right? So we'll just create this new user, and then I should be able to log in as that user over here. Example.com. Password.\u003C\u002Fp>\u003Cp>Badda bing badda boom. And now I can see all of these pages. Right? And you're probably already picking up on the problem. We could just make it more visible here.\u003C\u002Fp>\u003Cp>If we drag site over, that's gonna aggravate me. So we're just gonna go ahead and fix that in the pages section. Alright. I can go to site. I wanna display the related values.\u003C\u002Fp>\u003Cp>I wanna show the name of the site. In the mini to one, we're gonna show the name of the site. Great. Great. Beautiful.\u003C\u002Fp>\u003Cp>I can unhide this field on the pages if I want to. I'll show you what that looks like in just a moment. But now if I just refresh over here, you can see the problem. Well, number one, I don't have access to see any of the sites, but I can see the content for site b. And I can edit this even though I am not the content editor for site b.\u003C\u002Fp>\u003Cp>So let's just go in and fix these permissions. Looks like I've got a few that we need to fix. We'll just add site access. And we'll just set all of these up for all right now just to get back to, healthy baseline of this. We don't want folks to create sites.\u003C\u002Fp>\u003Cp>We don't want them to delete sites. We're gonna leave that for the admin. And then I'll just hit save and stay. Alright. So now I'll be able to see sites up here.\u003C\u002Fp>\u003Cp>And again, you can see the problem is that I could go in and I could change the title of this to Site B Sucks. Not cool. So how do we actually lock this down from being edited? Right? Directus gives us a great way to do that with custom permissions.\u003C\u002Fp>\u003Cp>And this is a little complicated the first time that you encounter it, but once you understand what's happening, it is incredibly powerful. So here we are on our access policy for content editors. Right? Anybody with this policy attached to their user directly or to their user role will have edit access for any of these things that we've set up. And these are just the individual collections.\u003C\u002Fp>\u003Cp>Right. And one way that Directus protects your data when you add a new collection, we don't give access rights to any of the those new collections by default. We like to keep things secure. But, the AgencyOS folks should not be able to see content for Site B. So what do we need to do?\u003C\u002Fp>\u003Cp>Right? We need to establish a relationship between site and the user. So to do that, we're gonna go back to our sites, and in this case we're gonna use that many to many relationship that we already talked about. We're gonna create a new field inside the sites collection called users. The related collection will be direct us users.\u003C\u002Fp>\u003Cp>We don't want to allow duplicates. We're gonna show a link. Great. We'll just go ahead and create this. Actually, I'm gonna delete that back up.\u003C\u002Fp>\u003Cp>Forgot to explain what's actually going on. In that many to many relationship, basically it creates a junction table for us. So let's try again. We'll do sites, our users, direct us users is the related collection. And instead of hitting save here, I'm gonna open up this advanced mode, and now you can see what's going on underneath the hood.\u003C\u002Fp>\u003Cp>Right? There's a junction collection that's being created inside our database. I'm gonna call this siteusers. By default this will get auto populated. You can go in and change it.\u003C\u002Fp>\u003Cp>Right. So I'm gonna clean up this convention just a little bit. So we've got the junction table will be siteusers, the site ID will be stored under site, the directus user ID will be stored as user, and then one important piece here is to add this corresponding field to the directus users collection as well, because we're gonna need to use that in our permissions. So we'll add a sort field, and the relational triggers here just let us set up, actions based on what happens to this specific thing, right. If we deselect the site users or we delete the sites, we delete one of the directest users, we probably wanna keep this thing clean and delete the record inside the junction table.\u003C\u002Fp>\u003Cp>Great. Alright. So what did we actually achieve here? So if I go into sites, we go to AgencyOS, I can now add that AgencyOS content editor to that specific site. Okay.\u003C\u002Fp>\u003Cp>Great, Bryant. Why can he still edit all of the content in site b? That's where our access policies come into play. Right? We're running on twenty minutes here.\u003C\u002Fp>\u003Cp>We still got plenty of time to set this up. What we're going to do now is restrict access to all of our content. So we've got all the relationships set up. We're ready to lock this down. What we're gonna do, we're gonna go into our sites.\u003C\u002Fp>\u003Cp>Right? The first thing that we wanna lock down if you're not part of site b, you shouldn't be seeing site b. Right? So we'll go into our read access. This has full read access.\u003C\u002Fp>\u003Cp>We're gonna use custom permissions to scope this down a bit. So we're gonna drill into our users, and this is our junction table, right? So the user, users dot users equals dollar sign underscore current user. So basically what we're saying is if the user is part of this site and they are then able to read information from that site. So why is that not showing up?\u003C\u002Fp>\u003Cp>Oh, we probably didn't set up permissions for the junction table. There we go. Okay. We don't want anybody to be able to adjust that. Okay.\u003C\u002Fp>\u003Cp>So now I can see all the users within a certain site for only sites that I am a part of. Right? So I can no longer see or search or find Site B for this example user that's logged in. Right? The AgencyOS Content Editor, they cannot see Site B.\u003C\u002Fp>\u003Cp>Now if I go into site b and I give them access to site b and adding them as a user of site b, refresh over here, boom, site b shows up. I remove them. You can now not get access to site b. Right? One problem though, while I don't have access to the actual site, I can still see the pages for site b.\u003C\u002Fp>\u003Cp>So I here's where a bit of the tedious part comes in, where I just go in and actually edit our permissions for all of these different items. Right? So we wanna be able to edit, create new pages, but when it comes to reading and updating pages, we want to make sure that those are scoped properly. So the site, we're gonna go back to that junction table. Right?\u003C\u002Fp>\u003Cp>Site users, user equals current user. So because we have a relationship to the site from pages, we are now able to check all the users within the site and make sure that the ID for those includes the current user ID. And what I'm gonna do here, I'm gonna just copy this rule, and I'm gonna go through and paste this rule into the item's permissions item permissions. And I could go through and do that for other things as well, like our post collection. Where are you posts?\u003C\u002Fp>\u003Cp>Right? Copy paste this rule. Again, we'll add the rule there. We don't want them to be able to delete pages that don't belong to their site. Same thing for posts.\u003C\u002Fp>\u003Cp>And, you know, depending on your scenario, right, whether you're setting up multi site for different clients or different teams within a larger organization or, yeah, some combination thereof, you've got total control of this by creating different access policies, different roles. So the access policies I love, because I can get really granular about what what content I want to make available to read, update, create, etcetera. Alright. So for the sake of time, I'm not gonna go through and adjust all of these things, but if I just hit save and stay, now I should not be able to edit any content for site b or even see it unless I'm part of that site. And that applies for the API as well, right.\u003C\u002Fp>\u003Cp>So if I go in and I do items slash pages, we make this pretty print, I don't see any of those pages. Right? And just to prove this to you, we've got a page here that belongs to site b. If I copy the ID of this, and remember I'm logged in as the content editor for site, AgencyOS over here, right, I'm gonna get a error that I do not have permission to access this specific page. So great.\u003C\u002Fp>\u003Cp>That works really nicely, Bryant. Cool. How do we actually implement this on a, like, front end scenario? Right? How should we set this up?\u003C\u002Fp>\u003Cp>And one pattern that I follow quite a bit is creating a bot user for my front end. Right? So now that we've got this data model set up, everything is flowing permission wise the way that we want it, right we would go through and create our front ends. So you could have your front ends all in Next. Js, Nuxt, whatever front end framework you prefer.\u003C\u002Fp>\u003Cp>You can even have different front end frameworks. You're gonna wanna fetch that content and either generate your front end statically, dynamically, server side rendered, incremental static regeneration, all the different ways of rendering your content, you know, all available to you via the API. But one pattern that I like, I will go in. I'm just gonna create a new role. I'm gonna call it bot.\u003C\u002Fp>\u003Cp>And this is just a role for our website front ends to access the API. Alright, so within roles we are going to add content policies. Right? So we don't want our front end to actually be able to edit the content, we just want it to read content. Potentially we might want to let it upload files or submit forms.\u003C\u002Fp>\u003Cp>But for now, let's just keep this simple. We are going to let it read content. And one of the other roles that comes with Directus out of the box is the public role. So the public role controls what API data is available without authentication. Now usually I will take advantage of the public role, and you can see we have different policies here.\u003C\u002Fp>\u003Cp>So I can let the general public read all the published content. That's okay. In this multi tenant scenario, you might want to lock that down. Right? So I'll just remove all access for the public role.\u003C\u002Fp>\u003Cp>And if I get really crazy with this, go to Safari, we go to items, we go to pages, right, now I don't get access to any of the content. Right? I cannot have access to pages. But, if I am still logged in over here, we can get access to our pages. Right.\u003C\u002Fp>\u003Cp>Right. And, again, we're only seeing the page data for agent c o s. Cool. So we've created this bot user or this bot role. Let's take a look at our read policies.\u003C\u002Fp>\u003Cp>We'll just go ahead and fix all of these other collections that we would need to render on the front end. Probably the navigation. Great sites. Cool. And again, you can see some of these are already, kind of partial.\u003C\u002Fp>\u003Cp>50% opacity here just highlighted where it says partial read access. Let's take a look at, like, pages for instance. I'm gonna save this really quickly though. So let's take a look at pages. Right?\u003C\u002Fp>\u003Cp>The rule for pages is that it has to be published, and the published date has to be now or before now. Makes sense, right? But we wanna add one more stipulation to this that any of these bot users can only access the sites that they have access to. So again, we can use that same setup where we go into the Junction collection, we use the current user. And with these bot users, I would highly recommend just creating one bot per website.\u003C\u002Fp>\u003Cp>So if you've got 50 websites, you create 50 bot users, that will be able to give you a token that will only, be able to access data for that specific site. And that way you don't have to keep passing something like site ID equals x y z in the parameters or every single call. Because that can get tedious, honestly. So here we're gonna adjust posts to do the same thing. We want the site, we want the underscore users user equal current user.\u003C\u002Fp>\u003Cp>And again, this is just a dynamic variable. Directus will populate that user ID for you there. Did we do that for pages as well? Let's do it for pages. Where are you?\u003C\u002Fp>\u003Cp>Sites. Tabs, users. Current user. Great. And if I want to, like, copy and paste these entire rules, these are just the standard filter rule syntax.\u003C\u002Fp>\u003Cp>If you check out docs.directus.io, you can find all of these things. Right. Great. Okay. So now we've got that locked down.\u003C\u002Fp>\u003Cp>Let's go in and create a new bot. Right? Let's say bot, site, site b bot. Crack myself up sometimes. And here, like, basically, we don't need an email and password.\u003C\u002Fp>\u003Cp>These folks aren't gonna log in to the data studio. Right? This is just gonna be a user for our front end to access the data. So in this case, we're gonna add that bot role. Cool.\u003C\u002Fp>\u003Cp>So I can know how many of these bots that we have. One other thing to note is that I don't even have to create a separate role. I could just give them read access, through the policies. So you've got a lot of flexibility there, and, you know, maybe you've got that one content editor who needs to do a little bit more than the others. Instead of having to create a separate role for them, just give them the policy that they need.\u003C\u002Fp>\u003Cp>What I'm gonna do here is create a static token. This just allows us to access it and we're gonna add this bot to Site B. Alright. Cool. Great.\u003C\u002Fp>\u003Cp>Save. Now we've got this user. And, cool. So now, what are we gonna do? I want to just demonstrate this.\u003C\u002Fp>\u003Cp>I don't think we're gonna have enough time. We've got like ten minutes before the hour's up. We're probably not gonna be able to dive into anything front end for that'll be tremendously helpful. Right? But let's just pretend like we're doing this on the front end.\u003C\u002Fp>\u003Cp>Documents. Great. We'll create a multi site collection here. I'm just using this HTTP client called Bruno. Usually, we do not talk about Bruno, but in this case, we are talking about Bruno.\u003C\u002Fp>\u003Cp>So I will grab the URL here from our API, and I'm gonna pretend to be the site b front end here. So we're gonna try to fetch our pages for site b. If I just make this full screen, I should get an error. Right? We do not have permission to access this.\u003C\u002Fp>\u003Cp>Rightfully so, we disabled all public access. But now if I go in and I add my token, right, now without having to add a param for site or or any of this mess. Right? I am only getting the content for site b because of the relationships and the permissions that I set up. So this bot user is a part of site b, and we can fix that to actually display this.\u003C\u002Fp>\u003Cp>So we'll go into direct us users. We'll go to sites. We want to display the site name. We'll display the site name in our display templates. Great.\u003C\u002Fp>\u003Cp>So now if we go back to bot for site b, alright, we can see the sites that they're available to. Now, if you're creating a lot of sites, you know, in this process of creating bots for each site are kind of cumbersome. Anytime you create a new site you could potentially run a flow that creates this bot user automatically, etcetera, so that you don't have to mess with this. It just depends on how many different sites that you're managing and what that setup kinda looks like. But here you can already see, let's go in and, you know, if I add the bot to the AgencyOS site, I save that.\u003C\u002Fp>\u003Cp>And if I rerun this API call, you can see now I'm getting all of those pages for the AgencyOS site as well, which is not what we want in this case. But, that way we can lock down the data that each one of these folks have access to. And this could be an unlimited number of front ends. So I hope that is it for this one. That is creating a multi site CMS.\u003C\u002Fp>\u003Cp>Right? Just to recap, we've got a sites collection that we created. We shifted all of our settings for the site into the actual sites collection themselves. Then we have relationships for pages, posts, etcetera, and we set up a bot user to access that for our front end. Right?\u003C\u002Fp>\u003Cp>On the front end, we would then just set up an API call to the direct us API. Not gonna get into that. That is gonna be opening a can of worms with five minutes left, and I don't wanna disappoint myself. So with that, multisite CMS, we're calling that a wrap. That's a win for this episode of 100 apps, one hundred hours.\u003C\u002Fp>\u003Cp>I hope you'll tune in to the next one, and make sure you check out our documentation for more insight on how to set some of these things up. I'll see you.\u003C\u002Fp>","Welcome back to the next episode of 100 apps, one hundred hours. I'm your host, Brian Gillespie. And today, we are going to be building a multi site CMS. It is been impossible to ignore the WordPress drama over the last month or two, and that has left a lot of folks wondering, hey, should I be switching our CMS from WordPress? WordPress has the multi site mode, so we're going to walk through how to build that today with Directus. If you are new to 100 apps one hundred hours, there are only two rules. Number one, sixty minutes to plan and build, nothing more, nothing less. Everyone knows the rules. And, the second rule, the anti rule, one that I'm definitely going to invoke today, use whatever you have at your disposal. So let's dive right in. We're gonna start the clock. Sixty minutes on the timer. Let's talk about multi site CMS. So what are we trying to do here? We want to manage content for multiple sites. That could be two, that could be three, it could be 25, it could be 50 from a single CMS instance. So we will share some of our data models amongst those sites, but the actual content will be different. So, as far as the functionality that we're looking for out of the system, let's just mock that up. Functionality. Right? We want to be able to manage pages and posts and other types of content for multiple sites within one CMS. We wanna make sure that there, are users can only edit and access data for sites that they are assigned to, owners of, assigned to, etcetera. That's the main functionality that we are looking for. Right? So one of the other things that I always do on these is basically map out our data model. Right? And if we're going after WordPress here, we have, like, pages and we typically have posts. Those are kind of the two pieces of content that, you know, when I think of WordPress, I think of, okay, here's what's inside, the data model for that. But we're also gonna need to go up to a higher level. Right? Because each page, each post is going to belong to a certain site. Now in this scenario, I'm thinking each site or each page only belongs to a single site and if you need to duplicate a page for another site, you just create a a entire copy of it. But, you know, you could potentially set this up if you are sharing the same page across multiple sites. You could model that relationship as well. You know, that's one of the advantages of Directus as a CMS is that you can model your data however you want, and your table structure inside your database will follow suit. Directus just makes those edits for you. And then, you know, we're going to need a relationship to users. Right? So users are going to be kind of a mini to mini relationship. Right? Love drawing these arrows. We need an arrow over here. And, let's just say mini to mini. Right? One user could be working on three, four, five sites or could be a single site, and, you know, we could have many users working on the same site. Alright. So as far as our data model, that's that's pretty good. You know, we'll dive into the nitty gritty details in just a moment, but I'm definitely gonna invoke rule number two here, and I'm going to go into Directus Cloud. And you can call this cheating. You can call this taking advantage of what we've already built previously. But we are going to do a new project using our simple CMS template because it gives us most of the functionality we need right out of the gate. So we're gonna call this multi site CMS test or 100 apps. That's good. See how long we can get the URL for this particular project. And then I'm just gonna click simple CMS here. I'm gonna switch this to monthly, and I'm gonna check out. And this will spin up our project for us. So, if we think more along the lines here of of what we actually need to do, You know, this simple CMS template also has forms. It has navigation, and I'll show you that in just a moment after we get it fired up. And then we have a global settings collection, inside there, which is actually, we're gonna have to do a bit of work to that. Can't get my arrows straight here. Jeez. Okay. There we go. Alright. So those all roll up to the sites. There are many users, many sites. We're just gonna wait for this Directus instance to spin up. And I'm assuming I've probably already got an email with this thing. Great. We'll just copy this. And feel free to steal this password here because this project should be turned off by the time we get to airing this actual episode. Alright. So we're just waiting for the CDN. Are we ready yet? We are ready. Okay. So great. Now we've got our login. Let's just go ahead and fire this up. We'll get logged into our simple CMS instance. And let's just kind of go through this actual CMS. I'll zoom way in and we can kind of dive through the content here. So we've already got some pages. We've got posts. We have forms, form submissions, navigation. So within pages, we can see we've got standard stuff, title, a permalink, what the status is. We've got some blocks here, of different content. So this is using the mini to any relationships inside Directus. I can drag and drop this to build a page dynamically, which is nice for our content editors or the marketing folk that are actually working on the content for the site. Then we have our blog posts. So lots of bunny related content here. We've got slugs. We've got status. Is this published? Yes or no? We got description, author, actual content. There's some AI features baked into this template. It is, a really nice starter kit for any Directus project if that's what you're looking to do. We got form fields. Great. There's a newsletter sign up, we need a first name, we need an email address, etcetera. And then, you know, every site needs a menu. So here we have some navigation, pretty cut and dry. And then there's the globals collection where we're storing settings that are going to be per site in our specific use case like logo, favicon, favicon, five icon. Great. Never know how to say that one. And we will dive in. Right? So we've got pages. We've got, you know, I could go in and query this particular content. So if I just drop this admin off the URL and I go to pages, we could query this content. If I wanted to see the individual content of the blocks, I can add a fields param to this and just do an asterisk, which will essentially act as a wildcard. It'll give me all the root level fields, and then I'm gonna say blocks, and I could do blocks dot collection, blocks dot item, And I can see I can start to fetch all the content within, and and I could keep drilling into this if I wanted to. But that doesn't cover our use case of multisite. Right? So the big glaring problem with all of this is we don't have sites. So let's go in, we're going to create our sites collection. We'll go to the settings, we'll just create sites. I'm going to use a generated UUID for this. We can add some of these in the canned system fields that add functionality like timestamps and users automatically. I'm just gonna leave this blank for now. And we're gonna give this site a name. And let's add a little hint for the user here. So I'm going to go to Advanced Field Creation Mode. I'm going to go to the Field tab and we're just going to say the internal name for this site. Great. That'll give us a hint. And voila, now we have sites. I'm gonna give this a nice looking icon, maybe a globe. That stands for, like, website. Right? We'll drag this up to the very tippy top. I've got globals as well. Maybe we need to change the icon. Let's do, like, settings or something. I used to be a designer. I like to refer to myself as a recovering designer, so, icons and user experience and small little details like that matter a lot to me. So let's go ahead and create a new site. Right? We're gonna do Agency OS. That's one of our other, open source projects that we released. And this is gonna be site b. Right? The other site. The site that I don't like. Site b. Don't even care about it enough to give it a nice name. Alright, so now what do we do with these specific sites? Right? Now that we have a site, maybe we want to go in and establish the relationship between all the different pages and things. Let's start with our globals though. Alright? These are settings, like the title, the URL, the logo, the favicon. Some of these we're gonna use for SEO. Some of them, maybe in the the footer like social links, things like that. We want to have these per site. Alright? So one of the nice things about Directus is this visual data modeling. I can come in and I can copy these fields across collections. So we will go to sites. We're gonna create a duplicate of that title field. I'm gonna duplicate the URL. I could even get fancy if I wanted to and potentially create, like, a flow to duplicate these things. You'll notice here on the ones that are relational, like, this is based on a many to one relationship. I cannot duplicate those fields just because the underlying schema changes that happen. But just regular any other fields that are not relational, I can easily duplicate. So we'll do that. We'll add our description. Copy that over. And let's do our social links. Now you'll also see there's this credentials group, like our open AI API key. Gosh. That's a mouthful. I wish, I wish their name was a little bit different because I'm dealing with these API keys in a lot of episodes, but I digress. So these things are probably gonna be they're gonna stay global. Right? We don't actually need to remove those. So here, let's just remove title. Let's remove URL now that we've copied it. The logo is just a nice little divider there. We'll remove the favicon. We'll delete all of these fields that are no longer global in our data model. And today's exes episode is is really just a, a big exercise in data modeling. Alright. So now if I go to the globals, all we have is the direct Us URL. So we're gonna serve all the the content for those sites out of a single instance. That's good. You know, they would probably share an API key or, you know, we could set it up for where that API key is per site if we wanted to. But now we can see we have these things, like, agency OS, the best operating system for your agency. Alright. We'll give this a URL, agencyos.dev. Run your business better. Alright. We can add social links. Great. Cool. Gravy. Right? And now, I could go in and, you know, I just got this option here to copy the API URL, and now I could see that individual site. So this is just my directus URL slash items slash site slash ID, or I can get a list of all the sites. What I don't see are my pages. Right? So how do we set up ownership of the pages and posts and things like that? So we want to use the relationships inside Directus. We're gonna set these up. We're gonna go to our sites. And again inside our data model that we spec'd out, pages belong to a single site. So one page belongs to one site. Same thing for posts and all the way down. So what we're going to do here is use the one to many field inside Directus. We're going to call this pages. The key is the name of the field inside the sites collection. We're gonna pick our related collection, which is gonna be pages, and then the foreign key will be the field that holds the site ID in the pages collection. So, you know, this might be site ID. I prefer just calling it site just because of the way that you can grab that relational data in a single API call. Alright, so the next thing we want to do is make sure we set up our display templates. And in this case, you know, I could go in and pick the fields that we wanna display, but what I'm gonna do here, I'm gonna use the table layout instead. So I could show multiple columns here and kinda get like a a standard table view that I'm looking for. So we'll add the title of the page, the status of the page, and do we need any more than that? Maybe not. Maybe we want permalink just because that could be dramatically different than the title. We will sort those by our sort order, and we're just gonna enable these other options to enable searching and to show a specific link. Okay. So now we've established a relationship to our pages. What does that actually look like? Alright. Let's scroll down. I can already see that the UI is getting a little bit, messy as far as user experience. We'll tackle that in just a moment. But here, now I can see my pages. I can create a new page directly from this site, or I can go in and add existing pages to that site. So I could say, okay. Hey. This site has a home page. It has a privacy policy page. I can go ahead and add these other pages to this specific site. There we go. I can save and stay, and then I've got, you know, the ability to edit these pages. I can also navigate directly to the page from there. So this is looking nice. One of the other things that I wanna do here though, because I care a lot about what my content editors are working with inside the CMS, we're gonna clean this up a bit. And I'm sure if you're watching this, you care as well. So there's an extension that I love to use quite a bit. It is called the group tabs group tabs interface. I've already got this installed in the simple website CMS template. It is by mister Hannes Kuttner, which is a a member of our core team. I think he actually built this before he joined the core team, though. Great little extension. Not sure why my images aren't displaying here. But basically, it gives me a nice tabbed interface. So what we're gonna do, we're gonna go to our sites data model, and I'm gonna look for the tab group option. Now this has got an option to actually fill the width of the page, so we'll take advantage of that, and I'm gonna save this. So this basically gives me a container where I can start dragging and dropping these other fields within, and we're gonna clean up this UI a bit. So let's see what that actually looks like. Boom. Now I've got title, I've got URL, I've got tagline. Except, you know, maybe I want to nest all of these fields under one single tab, right, instead of having, like, 35 different tabs. So what I can do here, I'm gonna reach for another field type here called the raw group. This doesn't actually create any columns in the database or anything like that. We're just gonna call this group branding, for instance. That's gonna be a key that we need to add, and then that gives us another container. And I could do things like this, where now I've got the title, I've got the URL, got the tagline, description, social links, and maybe we put pages up here ahead of those. So basically what I'm doing now is constructing the interface. So now we have pages. We have all these fields that show up under group branding. That doesn't doesn't seem right to me. Right? That looks kinda funky. When somebody who's adding this, they're gonna say group branding. What does that mean? So how can we control that? Right? The developer in me wants to say, hey. I need everything nicely name spaced and organized just for my own sanity, especially when I add more people to this. But the designer, the UX person in me says, hey. This this doesn't make sense. So what we can do is go into edit that specific field, and under the field settings, there are the field name translations. This is one of the most underrated features in Directus, I think. So I could just give this a translation. Right? And I pick the language. Of course, my user is set to the English language, but you could create translations for as many different languages as you have users. And Directus will display the correct one because when they log in, they can set up their default language on their profile. So I can work in English. Alexander on our team, he can work in French. Some of our fine folks from Germany, they can work in German, and so forth. Whatever it whatever we need. Alright. So as soon as I edit that, nothing changes here. But when I go back to the sites, now you can see that tab has a nice name that more aligns with what we're trying to achieve. Alright. So now that we have this relationship between pages, right, we keep moving on down the line. I'll go back to sites, and we're gonna add another one to many relationship for posts. That will be the related collection of posts, the foreign key, we're gonna use site. We'll add a table for this. And let's say we want the title of the post, so we want the status of the post, and we want when this was published. You know, typically, blog posts are rolling through in chronological order. So now for our sort field, instead of, like, an actual sort field, we're probably gonna wanna do published at. And maybe we do that by descending order so that we show, the most recent posts first. I can set how many pages or how many items per page that I wanna show in the table. And away we go, we can hit save. Great. So we'll add posts. Let's go back and hit one to many again. We're gonna do forms. So we'll do related collection forms. We will do the foreign key of site. Keep with that same theme. For the forms, do we actually need the table view? Yeah. Maybe we wanna add that. Right? So we've got title. We've got, the number of fields for the form. Maybe we wanna show that information. The number of fields, what fields we have. Over the fields we go. Laughing all the way. How many submissions maybe? Looks great. Cool. And now we'll do that. Now I know that we're gonna run into an issue with navigation because I'm the one who built this website CMS template and let me show you why. So when constructing this, the Symbol CMS template is made for a single website, not multi site, which we're building here. So the problem is the primary key is set up for a single site. Basically this is just a string. Right? Because I wanted to be able to do something like this for our actual content where I could say, hey, item slash navigation, and instead of calling, like, a random UUID, I could just call main and I can get my main navigation. And again, using that fields parameter, I could go in and get all the underlying items for that navigation. And I could go even further and do items dot children dot asterisk and get all of the children or even deeper. Right? Items.children.page. And then I can get the details of the actual page that we're linking to, like the permalink, so that I don't have to create brittle links that are gonna break as soon as soon as someone changes a permalink or a slug. Right? But in my haste in doing that, I kind of limited us here as far as what we can do with this structure. Right? Because if this is called footer, right, I would venture to guess that a lot of the sites that we're working on are also gonna have a footer. So then we have to worry about creating unique names. So what we're gonna do here, I'm just gonna trash this entire navigation collection. Boom. Away it goes. I could go into the actual SQL and update this, but for this purpose we'll just keep it nice and lightweight. We'll just create this collection again, And instead of using a string as the primary key, we're just going to use UUID. Perfect. We'll give this a key. And let's make this and give it a key field. Yeah, just to be clever. Alright. We can even set this field to be indexed for database performance. And maybe we want to slugify this thing as well so that it is friendly in URLs. The internal key for this navigation menu for this menu, I don't think we need to be super verbose here. I think there was an is active option there, so we'll just set that. Is this menu actually active for the site or not? And then I'm gonna go back and just like we did on sites, I'm gonna create a one to many relationship to our navigation items. So the items are gonna be the individual links. The navigation is basically our entire menu. So navigation will be main menu or header or footer, and then within that we'll have the navigation items. Great. Alright. For the foreign key is gonna be navigation. That's our parent collection. And you can always kinda see you can already see kind of the conventions that I follow where we have a singular and then a plural here for the the child relationship, just to try to keep things clean. Again, you could follow whatever convention that you want here. Totally up to you. So then we'll have a title for those items. We'll show a link. We'll sort by the actual sort field that's an integer. And if I expand this a little bit, you know, we can go in and set up our display templates, which we'll wanna do just to make sure that, when we're working inside, like, a a layout, which is like the table or the kanban or whatever layout you prefer to edit this, that that information shows, the actual title instead of random UUIDs. Alright. So, we're getting close there. Let's just add a menu icon for this and put this back up into our list. We'll put navigation items below that. Great. And now we can resume what we were doing. Go back to sites. We'll do a one to many relationship here. This will be navigation. Could call it menus. That's the hardest part of doing any of these things is what to call the names. But we'll just follow the same convention. Navigation. There's the site. Here's the menus. We're gonna show the key. Maybe we wanna show the number of items there. Great. We're gonna sort by nothing. Great. Alright. Now let's take a look at our progress. I'm kinda curious where we're at on the timer. We're about half an hour in. Still got a good way to go. But as far as what we're working with, and I can already see, right, there's our display templates that we need to set up. So I'm seeing basically the UUID of all the pages, and that's not what I wanna see. Right? So I'm gonna just go to pages here. You can see I've got my interface, which is the actual form detail. That's where these things show. The display shows up in that layout, so the the list of all the data. Right? We're gonna choose to show the related values, and I can add whatever fields here I want from pages. So maybe the title and the status of that page. Great. We could do the same thing for our blog posts. We wanna show the title and the status. Maybe the same thing for the form. We just wanna show the title of the form. And for our navigation, maybe we want to show the key and the number of items. Let's see how that looks. Right. Now we get something that's a little more manageable. We can see, hey, here's all the pages. I can bounce out to a specific page right from this view. I can go into the actual site. There's the name for this site. We can see we've got pages. We've got posts. We've got forms. We've got navigation. We've got branding. This is starting to look wonderful. Right? We'll probably need a header for this site. Let's see if we can copy some of these existing items. That's great. Let's create a new one. We'll call this the footer. Add some of these other items that we did not copy, contact us, privacy policy, blog, etcetera. Great. Make that active. We'll make the header active. Great. We hit save and stay. We can see okay. Here's the items within each one of those. Perfect. Everything's looking nice. Great. So now we have this site structure. Right? And if I were to go to my Directus URL, I go to items slash sites, I can get all the info for the site. Site b is looking pretty lonely in this case. We'll sort that in a moment. But here, there's lots of ways that I could get access to the information for this specific site. Again, I could go through and use the fields params here to fetch all the pages for the site. So pages dot asterisk, and I could see here's the same content I was getting at the pages endpoint. Now I could also, like if I were to grab the ID of this specific site, right, I could go through and get all the pages. So, we can see here there's a a site field where we're storing an actual ID. That's great. Let's go in and how do we get back to the actual admin? Admoine. Sausage fingers get me every single time. But let's go in and add a page to site b. Right? So we have site b. We're gonna create a new page for site b. Site b page. Cool. We'll just go ahead and pretend like this is a great page. We'll publish it. Cool. And now if I go back and I refresh, right, we're seeing this page for site b. So how can I filter this out? I could use our filter params for that. So I could do, like, filter. We're gonna do the site, we're gonna use underscore equals, which is our syntax for that. And oh, I lost my fancy stuff. So we're gonna run that again. Filter site. Just wrap these in brackets equals this. And now you can see I only see the items for the agency OS site, and I don't see that page that I just created. Now this is great for us in a multi site CMS setup. Right? I can have all this content, I can query it by the individual site, but let's talk about the problem here. So the problem is how do we lock down this content? So I'm just gonna copy this. Right? I'm gonna pull this up in another window, an incognito window. We're gonna discard the changes here for site b. And I'm gonna go in and add our first user. So the scene is set. We've got our sites. We want our users to be able to start editing the content. Alright. So this is gonna be our agency OS content editor. Great. This will be agencyOS@example.com. We're gonna do a real secure password of password. Alright. So I'm gonna create this new user. I could also invite users. I'm gonna give them the role of team member, which has already has a couple policies, attached to it. So if we just look at that, we see that this person can they'll have access to the direct to studio. They'll have, edit capabilities for our specific content. Right? So we'll just create this new user, and then I should be able to log in as that user over here. Example.com. Password. Badda bing badda boom. And now I can see all of these pages. Right? And you're probably already picking up on the problem. We could just make it more visible here. If we drag site over, that's gonna aggravate me. So we're just gonna go ahead and fix that in the pages section. Alright. I can go to site. I wanna display the related values. I wanna show the name of the site. In the mini to one, we're gonna show the name of the site. Great. Great. Beautiful. I can unhide this field on the pages if I want to. I'll show you what that looks like in just a moment. But now if I just refresh over here, you can see the problem. Well, number one, I don't have access to see any of the sites, but I can see the content for site b. And I can edit this even though I am not the content editor for site b. So let's just go in and fix these permissions. Looks like I've got a few that we need to fix. We'll just add site access. And we'll just set all of these up for all right now just to get back to, healthy baseline of this. We don't want folks to create sites. We don't want them to delete sites. We're gonna leave that for the admin. And then I'll just hit save and stay. Alright. So now I'll be able to see sites up here. And again, you can see the problem is that I could go in and I could change the title of this to Site B Sucks. Not cool. So how do we actually lock this down from being edited? Right? Directus gives us a great way to do that with custom permissions. And this is a little complicated the first time that you encounter it, but once you understand what's happening, it is incredibly powerful. So here we are on our access policy for content editors. Right? Anybody with this policy attached to their user directly or to their user role will have edit access for any of these things that we've set up. And these are just the individual collections. Right. And one way that Directus protects your data when you add a new collection, we don't give access rights to any of the those new collections by default. We like to keep things secure. But, the AgencyOS folks should not be able to see content for Site B. So what do we need to do? Right? We need to establish a relationship between site and the user. So to do that, we're gonna go back to our sites, and in this case we're gonna use that many to many relationship that we already talked about. We're gonna create a new field inside the sites collection called users. The related collection will be direct us users. We don't want to allow duplicates. We're gonna show a link. Great. We'll just go ahead and create this. Actually, I'm gonna delete that back up. Forgot to explain what's actually going on. In that many to many relationship, basically it creates a junction table for us. So let's try again. We'll do sites, our users, direct us users is the related collection. And instead of hitting save here, I'm gonna open up this advanced mode, and now you can see what's going on underneath the hood. Right? There's a junction collection that's being created inside our database. I'm gonna call this siteusers. By default this will get auto populated. You can go in and change it. Right. So I'm gonna clean up this convention just a little bit. So we've got the junction table will be siteusers, the site ID will be stored under site, the directus user ID will be stored as user, and then one important piece here is to add this corresponding field to the directus users collection as well, because we're gonna need to use that in our permissions. So we'll add a sort field, and the relational triggers here just let us set up, actions based on what happens to this specific thing, right. If we deselect the site users or we delete the sites, we delete one of the directest users, we probably wanna keep this thing clean and delete the record inside the junction table. Great. Alright. So what did we actually achieve here? So if I go into sites, we go to AgencyOS, I can now add that AgencyOS content editor to that specific site. Okay. Great, Bryant. Why can he still edit all of the content in site b? That's where our access policies come into play. Right? We're running on twenty minutes here. We still got plenty of time to set this up. What we're going to do now is restrict access to all of our content. So we've got all the relationships set up. We're ready to lock this down. What we're gonna do, we're gonna go into our sites. Right? The first thing that we wanna lock down if you're not part of site b, you shouldn't be seeing site b. Right? So we'll go into our read access. This has full read access. We're gonna use custom permissions to scope this down a bit. So we're gonna drill into our users, and this is our junction table, right? So the user, users dot users equals dollar sign underscore current user. So basically what we're saying is if the user is part of this site and they are then able to read information from that site. So why is that not showing up? Oh, we probably didn't set up permissions for the junction table. There we go. Okay. We don't want anybody to be able to adjust that. Okay. So now I can see all the users within a certain site for only sites that I am a part of. Right? So I can no longer see or search or find Site B for this example user that's logged in. Right? The AgencyOS Content Editor, they cannot see Site B. Now if I go into site b and I give them access to site b and adding them as a user of site b, refresh over here, boom, site b shows up. I remove them. You can now not get access to site b. Right? One problem though, while I don't have access to the actual site, I can still see the pages for site b. So I here's where a bit of the tedious part comes in, where I just go in and actually edit our permissions for all of these different items. Right? So we wanna be able to edit, create new pages, but when it comes to reading and updating pages, we want to make sure that those are scoped properly. So the site, we're gonna go back to that junction table. Right? Site users, user equals current user. So because we have a relationship to the site from pages, we are now able to check all the users within the site and make sure that the ID for those includes the current user ID. And what I'm gonna do here, I'm gonna just copy this rule, and I'm gonna go through and paste this rule into the item's permissions item permissions. And I could go through and do that for other things as well, like our post collection. Where are you posts? Right? Copy paste this rule. Again, we'll add the rule there. We don't want them to be able to delete pages that don't belong to their site. Same thing for posts. And, you know, depending on your scenario, right, whether you're setting up multi site for different clients or different teams within a larger organization or, yeah, some combination thereof, you've got total control of this by creating different access policies, different roles. So the access policies I love, because I can get really granular about what what content I want to make available to read, update, create, etcetera. Alright. So for the sake of time, I'm not gonna go through and adjust all of these things, but if I just hit save and stay, now I should not be able to edit any content for site b or even see it unless I'm part of that site. And that applies for the API as well, right. So if I go in and I do items slash pages, we make this pretty print, I don't see any of those pages. Right? And just to prove this to you, we've got a page here that belongs to site b. If I copy the ID of this, and remember I'm logged in as the content editor for site, AgencyOS over here, right, I'm gonna get a error that I do not have permission to access this specific page. So great. That works really nicely, Bryant. Cool. How do we actually implement this on a, like, front end scenario? Right? How should we set this up? And one pattern that I follow quite a bit is creating a bot user for my front end. Right? So now that we've got this data model set up, everything is flowing permission wise the way that we want it, right we would go through and create our front ends. So you could have your front ends all in Next. Js, Nuxt, whatever front end framework you prefer. You can even have different front end frameworks. You're gonna wanna fetch that content and either generate your front end statically, dynamically, server side rendered, incremental static regeneration, all the different ways of rendering your content, you know, all available to you via the API. But one pattern that I like, I will go in. I'm just gonna create a new role. I'm gonna call it bot. And this is just a role for our website front ends to access the API. Alright, so within roles we are going to add content policies. Right? So we don't want our front end to actually be able to edit the content, we just want it to read content. Potentially we might want to let it upload files or submit forms. But for now, let's just keep this simple. We are going to let it read content. And one of the other roles that comes with Directus out of the box is the public role. So the public role controls what API data is available without authentication. Now usually I will take advantage of the public role, and you can see we have different policies here. So I can let the general public read all the published content. That's okay. In this multi tenant scenario, you might want to lock that down. Right? So I'll just remove all access for the public role. And if I get really crazy with this, go to Safari, we go to items, we go to pages, right, now I don't get access to any of the content. Right? I cannot have access to pages. But, if I am still logged in over here, we can get access to our pages. Right. Right. And, again, we're only seeing the page data for agent c o s. Cool. So we've created this bot user or this bot role. Let's take a look at our read policies. We'll just go ahead and fix all of these other collections that we would need to render on the front end. Probably the navigation. Great sites. Cool. And again, you can see some of these are already, kind of partial. 50% opacity here just highlighted where it says partial read access. Let's take a look at, like, pages for instance. I'm gonna save this really quickly though. So let's take a look at pages. Right? The rule for pages is that it has to be published, and the published date has to be now or before now. Makes sense, right? But we wanna add one more stipulation to this that any of these bot users can only access the sites that they have access to. So again, we can use that same setup where we go into the Junction collection, we use the current user. And with these bot users, I would highly recommend just creating one bot per website. So if you've got 50 websites, you create 50 bot users, that will be able to give you a token that will only, be able to access data for that specific site. And that way you don't have to keep passing something like site ID equals x y z in the parameters or every single call. Because that can get tedious, honestly. So here we're gonna adjust posts to do the same thing. We want the site, we want the underscore users user equal current user. And again, this is just a dynamic variable. Directus will populate that user ID for you there. Did we do that for pages as well? Let's do it for pages. Where are you? Sites. Tabs, users. Current user. Great. And if I want to, like, copy and paste these entire rules, these are just the standard filter rule syntax. If you check out docs.directus.io, you can find all of these things. Right. Great. Okay. So now we've got that locked down. Let's go in and create a new bot. Right? Let's say bot, site, site b bot. Crack myself up sometimes. And here, like, basically, we don't need an email and password. These folks aren't gonna log in to the data studio. Right? This is just gonna be a user for our front end to access the data. So in this case, we're gonna add that bot role. Cool. So I can know how many of these bots that we have. One other thing to note is that I don't even have to create a separate role. I could just give them read access, through the policies. So you've got a lot of flexibility there, and, you know, maybe you've got that one content editor who needs to do a little bit more than the others. Instead of having to create a separate role for them, just give them the policy that they need. What I'm gonna do here is create a static token. This just allows us to access it and we're gonna add this bot to Site B. Alright. Cool. Great. Save. Now we've got this user. And, cool. So now, what are we gonna do? I want to just demonstrate this. I don't think we're gonna have enough time. We've got like ten minutes before the hour's up. We're probably not gonna be able to dive into anything front end for that'll be tremendously helpful. Right? But let's just pretend like we're doing this on the front end. Documents. Great. We'll create a multi site collection here. I'm just using this HTTP client called Bruno. Usually, we do not talk about Bruno, but in this case, we are talking about Bruno. So I will grab the URL here from our API, and I'm gonna pretend to be the site b front end here. So we're gonna try to fetch our pages for site b. If I just make this full screen, I should get an error. Right? We do not have permission to access this. Rightfully so, we disabled all public access. But now if I go in and I add my token, right, now without having to add a param for site or or any of this mess. Right? I am only getting the content for site b because of the relationships and the permissions that I set up. So this bot user is a part of site b, and we can fix that to actually display this. So we'll go into direct us users. We'll go to sites. We want to display the site name. We'll display the site name in our display templates. Great. So now if we go back to bot for site b, alright, we can see the sites that they're available to. Now, if you're creating a lot of sites, you know, in this process of creating bots for each site are kind of cumbersome. Anytime you create a new site you could potentially run a flow that creates this bot user automatically, etcetera, so that you don't have to mess with this. It just depends on how many different sites that you're managing and what that setup kinda looks like. But here you can already see, let's go in and, you know, if I add the bot to the AgencyOS site, I save that. And if I rerun this API call, you can see now I'm getting all of those pages for the AgencyOS site as well, which is not what we want in this case. But, that way we can lock down the data that each one of these folks have access to. And this could be an unlimited number of front ends. So I hope that is it for this one. That is creating a multi site CMS. Right? Just to recap, we've got a sites collection that we created. We shifted all of our settings for the site into the actual sites collection themselves. Then we have relationships for pages, posts, etcetera, and we set up a bot user to access that for our front end. Right? On the front end, we would then just set up an API call to the direct us API. Not gonna get into that. That is gonna be opening a can of worms with five minutes left, and I don't wanna disappoint myself. So with that, multisite CMS, we're calling that a wrap. That's a win for this episode of 100 apps, one hundred hours. I hope you'll tune in to the next one, and make sure you check out our documentation for more insight on how to set some of these things up. I'll see you.","68ed164f-6d7d-4aba-adab-50be7e5b7b09",[1207],"febb5a0f-decf-49bc-a161-899e94abf23c",[],{"year":399,"number":1060,"id":1061,"episodes":1210,"show":1211},[1063,1064,1065,1066,1046,1067,1068,1069,1070,1071],{"title":322,"slug":323},{"id":984,"slug":1213,"vimeo_id":1214,"description":1215,"tile":1216,"length":1217,"resources":12,"people":12,"episode_number":264,"published":1218,"title":1219,"video_transcript_html":1220,"video_transcript_text":1221,"content":12,"seo":1222,"status":19,"episode_people":1223,"recommendations":1226,"season":1227},"setting-up-ab-testing-posthog","1060881589","Join us and our friends at PostHog to learn all about building A\u002FB testing infrastructure in your CMS with their killer A\u002FB testing functionality.","68305008-77bb-46a0-9ce0-4e94b8667fea",65,"2025-02-27","Building A\u002FB Testing in Your CMS: A Deep Dive with Directus & PostHog","\u003Cp>Speaker 0: And we are live. Alright. Welcome. Welcome. Welcome.\u003C\u002Fp>\u003Cp>Super excited to kick off this webinar. It's been a long time in the making for me. I have been knee deep in AB testing over the last couple weeks, so super excited. We are going to be covering how to build AB testing inside your CMS with Posthog and Directus. As everybody trickles in, if you are in the chat, let us know where you are from.\u003C\u002Fp>\u003Cp>Hop in the chat. Let us know. Awesome. I am Brian Gillespie from Directus. I see a few of you in the chat already know me.\u003C\u002Fp>\u003Cp>We also have Yurai from Posthog. Yurai, nice to have you.\u003C\u002Fp>\u003Cp>Speaker 1: Hey, everybody. Nice to meet you. I'm Yorai. I live in Amsterdam, Netherlands, and I've been at Posthog for about a year and a half. And for the last year or so, I've been, working hard, on our AB testing tool, which I'm hoping to, demo you a bit today.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. I could I I'm excited because I'm gonna learn a bit on this one as well. Obviously, I've done a lot of the technical implementation for not just our own use case at Directus, but, for this amazing bonus that we got for everyone at the conclusion of this. But, I, you know, I I haven't messed around with all of the the the, like, the metrics and, like, the testing and it just like, all of the config inside post hoc is is tremendously powerful. So I looking to, I guess, forward to seeing what, you know, how that is cooked up on your end.\u003C\u002Fp>\u003Cp>Alright. Let's, let's cover the agenda, and then we'll kinda give a brief overview of, both direct us and post hoc just because I I saw some questions in the sign up of the people that weren't familiar with either one. So this is obviously the awkward introduction phase. Hi. Okay.\u003C\u002Fp>\u003Cp>I see everybody. Canada, Tampa, Florida, Texas. Amazing. Nashville. Next, we're going to kick it over to Yurai where they'll cover he'll cover, basically, a demo of post hoc and how you set up experiments, how you run AB testing, what's a feature flag, what's not, what you should be thinking about as you're testing.\u003C\u002Fp>\u003Cp>And then we're going to, basically do a live jam sesh of how to connect Directus and Posthog using this, the starter kit that we've created. And we'll open this up for q and a at the end of this. And I'll show you guys how to get this amazing bonus with, the working source code, fingers crossed. Right? So you don't have to invest the time and headache that I have invested over the last couple weeks.\u003C\u002Fp>\u003Cp>But with that, Yirai, maybe you want to talk a little bit about post hoc for those who are unfamiliar with the tool.\u003C\u002Fp>\u003Cp>Speaker 1: Sure. I guess it'll be easier if I share my screen right away. Yeah. Go for it. Let me do that.\u003C\u002Fp>\u003Cp>Alright. Well, so post hoc, started as, as a product analytics platform, but we've really evolved into kind of, like, an all in one solution which allows you to build great products. So, besides product analytics, we have today more tools such as session replay, feature flags, surveys, you know, data warehouse, and, of course, experiments, which we'll talk about today. So experiments, basically allow you to, test, like, variations on your website, test different changes, and see if those changes lead to some kind of improvements in the behavior of your users, which is then, of course, visible in the metrics that you are tracking. So the way it works is that you you use post hoc's product or feature called feature flags.\u003C\u002Fp>\u003Cp>And feature flags basically assign different variations of your website to your users. And usually, by default, you will be testing two variations, on your website. The variations will be normally called control and test. And let's say user a will get the variation control, user b gets the variation test. They will both see something different on your website, and then, you know, PulseHawk will track the behavior of your users on the website by capturing events.\u003C\u002Fp>\u003Cp>And then we aggregate those events on our site, and we basically calculate results for you, which tell you whether a particular variation is better than some other variation. So like I said, every experiment is is backed by a feature flag, but, actually, you don't really need to know, much about feature flags at all. The feature flag will be created for you when you set up an experiment. So, like, all you have to do is basically create the experiment, and I believe Brian will later show you how to do that via direct us.\u003C\u002Fp>\u003Cp>Speaker 0: We will. And,\u003C\u002Fp>\u003Cp>Speaker 1: basically, it all kind of, like, happens under under the hood. And, basically, all you need to then do to analyze your experiment is to just let it go to your experiments that impose HOG, and you will be able to analyze your results there. So I'm going to open an example experiment to show you what a results analysis might look like. So over here, I have a running experiment open. You can see that it was started, like, two months ago, which will be, like, a pretty long running experiment, but that's just because, like, it's it's some test data.\u003C\u002Fp>\u003Cp>But, basically, the the core of each experiment is the metrics that you are tracking. Right? It's it's these metrics that tell you, like, what what exact changes in behavior your your changes in in your content or in your experience are producing. And in this particular experiment, I'm tracking, six different metrics, three primary and three secondary. And the difference between the primary and secondary is just that it's just like the way of of organizing your metrics.\u003C\u002Fp>\u003Cp>So the primary metrics are something which actually inform whether your experiment is successful or not. And secondary metrics are kind of like guardrails. So it's it's something that you don't really want to regress. It's it's maybe not like a like a metric directly tied to your experiment, but it could be anything from, let's say, like, a session length or or any kind of, like, in interaction maybe indirectly linked to your experiment, but you still may want to track that to make sure that, that you don't get some some other part of your of your product kind of, like, regressed.\u003C\u002Fp>\u003Cp>Speaker 0: Cool. That's a I I like that for sure. Hey. That's that's one of the confusing things for me. It was, like, what goes in primary, what goes in secondary?\u003C\u002Fp>\u003Cp>Just, so it sounds more of, like, secondary would be like, hey. This is a great guard against unwanted side effects. Like, hey. Exactly. Increased conversion, but, you know, like, the time on the page or or, like, session time or, bounce rate or something rose versus, the actual event that we wanted.\u003C\u002Fp>\u003Cp>Speaker 1: That's exactly right. Yeah. So it's just like a way of organizing things. And but other than that, there is really, like, no difference under the hood between, like, a primary metric or a secondary metric. Basically, always, like, at a at a very low level, what we do at Boss Hog is capturing events, and, a metric is like a way of counting those events in a in a certain way.\u003C\u002Fp>\u003Cp>So let's have a look at at one of these metrics. So let's let's take a look at the first one. I'm going to click at this on this edit button, and here is my metric definition. So this is a funnel metric which measures, the conversion rate between two events. So the first conversion so so the first event here is called sign up started.\u003C\u002Fp>\u003Cp>The second event is sign up completed. And what this funnel metric measures is the conversion between these two events. So you can see that I have 3,000 persons who did the first event, but only 815 persons who, triggered the second event. And so the the difference between these two events is basically your conversion rate, which in this case is twenty seven percent. And what you actually see here so this is like the the metric definition form.\u003C\u002Fp>\u003Cp>This is not your experiment result. This is just kinda like a preview, which shows you well, is the data actually there, right, in the in the system? Like, are actually people sending these events? And that kind of tells you that, okay. Like, this is, like, a valid metric to experiment on.\u003C\u002Fp>\u003Cp>Like, our instrumentation is set up properly so we can actually go ahead and and save that metric. So once I save the metric and, well, once the experiment is running, I will start seeing my results once sort of, like, a minimum criteria are met, which is that, you know, you need to have some sort some number of events that have been ingested. You need to have events for both control and test variants. And once all of these are met, you will start seeing results. And the way we present results is kind of like an industry standard way to present results of, you know, AB experiments, which is that we show you this chart, which is called a delta chart.\u003C\u002Fp>\u003Cp>And what you see here is that for each variant, you will see this bar. And this bar is basically a credible interval. What it shows you is the the actual or let me actually start start start, like this. So so each each bar shows you, the actual difference between that given variant and the control variant. Right?\u003C\u002Fp>\u003Cp>The the black bar in the middle is the delta between the variant and and the and the control variant. So you can see that in case of the test one variant, we actually have a regression here. So the control is at 0%, and the test one variant is kind of like minus 13%. So that's bad. Right?\u003C\u002Fp>\u003Cp>Like, that's a that's a regression. We have worse conversion rate for the test one variant compared to the control variant. Now for the test two variant, we actually see an improvement. So the delta here is plus 6.92 compared to control, and that's why this bar is in is in green because it's actually gonna be improvement. So that's what the the the black vertical bar tells you.\u003C\u002Fp>\u003Cp>And now now onto the edges onto the edges of the actual bar. So this is like a credible interval. So what this bar tells you is actually the uncertainty that you have because, in any kind of statistical testing, there is some sort of uncertainty. And this like, the the the outer boundaries of these, credible intervals tells you, what kind of range in the actual results you may expect. And this basically tells you that, this credible interval, goes from minus 3% to plus 70%.\u003C\u002Fp>\u003Cp>And that means that in ninety five percent of the cases, because this is a so called 95% credible interval, you can expect the true value to lie between, like, this range. So there is still some sort of small probability that there will be a regression, for the test two variant even though, it's kind of like there's a high probability that it will be, some some sort of improvement. The narrower a credible interval is, the higher certainty you have. Right? Because, it's kind of like a tighter range of values where the actual value may lie.\u003C\u002Fp>\u003Cp>The wider it is, then it's kind of like more more uncertainty. And oftentimes, as you as you collect more data and you, like, keep refreshing results, you can you can kind of, like, observe the variance getting narrower and narrower every day as you kind of gather more data and get gather more more certainty. So that's what these credible intervals bars tell you. It's calculated separately for each of the metrics. And, at post hoc, we use a so called Bayesian statistical methodology, and the two main outputs of the methodology is the credible interval itself, which tells you the the uncertainty of the result.\u003C\u002Fp>\u003Cp>And kind of like the main output is is what we call win probability. So in this case, for this variant, there is an almost 83% probability that this test two variant is actually better than control. And then we show you, kind of like the the significance banner over here. And at post hoc, the the criterion that we use to tell you whether you should roll out a variant or shouldn't roll out a variant is that the win probability needs to be higher than 90% for the best variant. And in this case, you can see that it's actually less than 90%.\u003C\u002Fp>\u003Cp>And that's why for this particular metric, we declare it as not significant because it's less than 90%, which is what this tool tip also tells you.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So now you've just answered, like, my own specific like, this is the biggest question I've had on, like, the experiments that we've ran of, like, what's how do you measure the significance? You know? Because we we've ran several tests, and, I'll get into, like, the specific results here in just a bit of one of our tests. But, like, some of the tests have been like, we saw, like, a it what looked like a positive improvement, but it was marked not significant.\u003C\u002Fp>\u003Cp>So it's like, like, it you know, can we be confident in that result or not?\u003C\u002Fp>\u003Cp>Speaker 1: Right. Yeah. Yeah. That's a good point. And, we actually keep, improving this UI, and we want to actually, like, make it clearer as to, like, what all of these numbers mean and when you can expect significant.\u003C\u002Fp>\u003Cp>And it's like why something is significant, why something isn't significant. Just, like, make make all of this kind of decision making clear. So that's that's definitely a a valid point.\u003C\u002Fp>\u003Cp>Speaker 0: I I I'll just stick you in the UI explaining it, man. Take it like you flawlessly. You did I was\u003C\u002Fp>\u003Cp>Speaker 1: gonna ask that again. I I didn't hear that. I I\u003C\u002Fp>\u003Cp>Speaker 0: was gonna say, just stick a video of you inside the UI because you, you you did flawlessly.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, nice. Yeah. We might actually do that. That's a that's a great Yeah. Cool.\u003C\u002Fp>\u003Cp>Am I still sharing? Because I think the, Oh, we lost the screen share. Yeah. Oh, okay. I'll reshare.\u003C\u002Fp>\u003Cp>Okay. And then, so just like continuing to the second metric, it's it's exactly the same principle. So each metric is is evaluated in exactly the same way. I mean, the the like, on our back end, there are some differences as to how, different metric types are evaluated. So for example, the second metrics the second metric is a different metric type.\u003C\u002Fp>\u003Cp>Right? So, like, in in the first case, we had a we were measuring funnel conversion. In the second metric, we are actually just measuring the role click count. And, of course, there are some statistical differences as to how this should be evaluated, like, on the back end. And we make sure that, like, we do this properly.\u003C\u002Fp>\u003Cp>But for you as a user, there's really no difference. You basically just look at, the movement of these bars relative to the control variant, and you look at the the win probability. And then the banner will will tell you whether a particular metric is significant or not. You can also dive deeper into any particular metrics. So if you click on details, you will see the actual counts for, for each variant.\u003C\u002Fp>\u003Cp>You can see that the test variant is strongest. Right? So it makes sense that it has the highest count over here. And this is like a cumulative chart, so the the the counts actually stay the same after we we stopped collecting the data for this experiment. Nice.\u003C\u002Fp>\u003Cp>Yeah. You can also see things like exposures for each variant, their means, the delta, things like that. There's also like a like a small cool feature, which is that you can actually view recordings for any particular variant. So the the the power of the POSIX platform is really that we offer multiple products, and they are kind of, like, interlinked. So you can can actually, like, click on on this particular variant and see the recordings, like, of those persons that that that actually, like, sold that variant.\u003C\u002Fp>\u003Cp>And I don't see any recordings here because I'm on my local instance and just, like, using all the data so you can know from actual users. But in Yeah. On actual dashboard, you would actually see recordings, of users where you could actually see how they how they interact with your with your website.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And and, like, having that all in one has been, like, a significant help for us at Directus. You know, I would it's not like for us, it's it's not stack overflow. It's it's like stack overload. Like, I, you know, I the thought of adding, like, six more tools to your tech stack for your website, is just like a a mess.\u003C\u002Fp>\u003Cp>It's it's like a pain for us. I I don't wanna do it. So and, like, when we integrated post hoc, like, the the analytics for us was, like, one of the one of the first things that we got a lot of value out of, and then we we started diving into the AB testing. You know, as we we kinda shift gears, like, do you have any best practices, your eye on, like like, what to test? You know, obviously, like, you've built this thing.\u003C\u002Fp>\u003Cp>You probably worked closely with with some clients at Posthog. Like, what are people testing? Do you have any best practices to share?\u003C\u002Fp>\u003Cp>Speaker 1: Sure. So I would say if you if you are just starting out with experiments, start with something very small. So things like, you know, small changes to your landing page. That will really allow you to, just, like, kind of, like, get, like, how how the whole things works. And, maybe also kind of, like, circumvent some of the gotchas, like, like, why why while you are still starting out.\u003C\u002Fp>\u003Cp>Like, there are, like, several things that you should be aware of if you are implementing AB testing. I'm not sure if if, like I would say, like, maybe there can, like, be on the scope of this of this webinar, but we have, like, section with some troubleshooting and FAQs and, some best practices, when when implementing experiments. To summarize very quickly, I would say the like, one important thing is to make sure that your tracking is set up correctly. So, like, in your code, whenever a user performs a given action, you do actually capture that action so that Pulsar receives that event because, obviously, if we don't receive the correct events, we cannot, provide correct analysis. Now in terms of some in terms of, like, some likely more actionable AB testing advice, I would say start with small changes.\u003C\u002Fp>\u003Cp>Also make sure that, you are testing perhaps only, like, one or two changes at once. Because if you change, like, too many things, let's say, on your on your on your landing page, and then the test is showing, you know, significant outcome, like, you don't really know which which one of those a changes that you've made is actually leading to the improvement. Whereas, if you just like there's, like, small incremental changes, then you would be able to to tell that, kind of, like, more reliably. Another kind of, like, important technical detail is that you should probably use a reverse proxy on your post hoc setup to make sure that the, like, ad blockers are not, like, blocking capturing of the events, which is, like, a common issue, that can be really easily, circumvented with this. We also have, like, proper documentation, on this.\u003C\u002Fp>\u003Cp>Like, in general, if if you have, basically, for for any questions, you can use our search functionality in our documentation, for example, for the reverse proxy. That will explain to you exactly what you you should do to to set up also correctly to to be able to circumvent, ad blockers. Another useful tip is to learn how to actually estimate, your sample size properly. So one thing I I haven't explained yet is that we have this data collection section over here. And what this allows you to do is to is to answer the question how long you should run your experiment for.\u003C\u002Fp>\u003Cp>And, so I'm actually going to show you how this works. So if I click on edit over here, I have this slider which says minimum detectable effect. And this basically says, well, what kind of change in my metric am I trying to measure? And there's, like, a trade off to be made here because, if you are the the way the way sample sizes and experimentation work is that the larger the change you are trying to measure, the smaller the sample size you need. It may sound kind of counterintuitive.\u003C\u002Fp>\u003Cp>Speaker 0: It it definitely. Definitely. Because I've seen this, and I'm like, like, hey. Why why do we need less people for this?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And, actually, right now, we are actually completely rebuilding this component to to do, like, a better job, explaining all this. But, basically, what this means is that if there is a huge change in your metric, you don't really need, like, a sensitive test for it. Right? You you don't need, like, a, like, a huge sample size because, if there is, like, a huge effect, that effect will already be apparent in, like, a relatively small sample size.\u003C\u002Fp>\u003Cp>But if you are trying to measure something much smaller, like, let's say, I'm I'm just going to move this slider from 10% to 2%, I need, like, a much more sensitive test, which means, like, a much higher sample size. Right? So, like, I I basically need, much bigger sample size to be able to reliably say, this 2% change is not just due some sort of chance. It's actually due to the actual change in the in the underlying behavior. But I definitely need, like, a larger sample size for this.\u003C\u002Fp>\u003Cp>So Right. The consideration some some some key considerations here is that are that, first of all, what is the sample size you can actually get? Right? If you if you are a startup and you're just, like, getting your first users, it might be actually difficult to get a sample size of 10,000 persons. So in that case, you are basically restricted to much smaller sample sizes.\u003C\u002Fp>\u003Cp>And\u003C\u002Fp>\u003Cp>Speaker 0: Right.\u003C\u002Fp>\u003Cp>Speaker 1: That actually means that your tests would actually would probably have to target, you know, just like large changes.\u003C\u002Fp>\u003Cp>Speaker 0: You wanna go big or go home at that point.\u003C\u002Fp>\u003Cp>Speaker 1: Exactly. Yeah. But as soon as you have some larger user base, you can start going after, you know, incremental small changes, that perhaps produce, smaller effects on your metrics. But you can you can run, like, many of such experiments and, you know, even incremental changes or or of one or 2% over time. They they really add up to a lot.\u003C\u002Fp>\u003Cp>So, yeah, there's, like, a trade off to be made here, when it comes to this, so it's it's it's good to be aware of that.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Well, awesome. Yeah. Thanks for the best practices, man. Thank you.\u003C\u002Fp>\u003Cp>Like, yeah, I'm I'm learning just as much on this as as the audience, so I appreciate that. We'll jump into, you know, kind of, like, our own experience a little bit, and then I'll, kinda run through the steps of of integrating post hoc and direct us and and, again, show you guys the the source code. We'll we'll dive into it. We won't write code. That didn't work out well for me the last time I, I did one of these live sessions.\u003C\u002Fp>\u003Cp>But, as far as our own, like, experience with posthog, you know, recently, we rolled out a brand new version of our home page. And I'm gonna can we see this? Oh, yeah. There we go. So this was a big change for us.\u003C\u002Fp>\u003Cp>And, you know, this one, if I shrink it down, it's probably a little better. But this was a this new homepage was a big shift for us. And one of the things that we wanted to do was we wanted to test first to make sure that, number one, the messaging was wasn't causing a a decrease in conversions. Number two, you know, we wanted to make sure that that this was performing better than our our old home page. You know, we've got this interactive carousel component that basically links into, what we call our directus pizza demo, which is just a a live working instance of directus.\u003C\u002Fp>\u003Cp>So folks can hop in and and poke around inside one of the templates. Before we we shifted all that traffic, right, we wanted to make sure that this was actually worthwhile. So our own results that we saw got a fancy slide up here somewhere. Boom. Yeah.\u003C\u002Fp>\u003Cp>So, the conversions were were relatively the same. And, again, that goes back to kinda your eyes point about, like, what metrics are, you know, how do we measure significant change? But some of the big results that we saw was, like, a 30% decrease in bounce rate on the site, which is huge. And, obviously, that correlates with a, like, a larger session time, most likely because people are getting in the demo of Directus, at least that's our hypothesis, and and actually poking around, which is which is what we want. And and I know you guys at Posthog, you're I, you guys are are kind of following that same methodology of, like, you know, let's let's skip all of the, fluffy marketing stuff and actually get you into the product, so we can actually dive in and and learn.\u003C\u002Fp>\u003Cp>Alright. So let's actually dive into a this integration. And I put together just, like, this a really nice visual for how we've been doing AB testing with post hoc at Directus. And that is kind of the the concept behind behind this setup. We've done tests at at two levels, and I call it the block level, which is basically testing within the same page, which is, you know, hey.\u003C\u002Fp>\u003Cp>We wanna test a different headline on the homepage, or we wanna test, a different pricing component or a a different pricing tier. So that would be like a block level test. And then, I I was calling this a page level test, which is basically testing between different pages. You could call it a split test. You guys are calling it redirect testing inside the documentation dry.\u003C\u002Fp>\u003Cp>But it basically like, we we take a URL. We want to redirect some percentage of the traffic. You know, usually, if it's just two variants you're testing, you you probably split fifty fifty. But but that's the way that we've been doing testing at Directus. And now I'm going to show you how this all comes together.\u003C\u002Fp>\u003Cp>So, this is post hoc, and we laid a special theme, special post hoc theme on top of a direct us instance just for this webinar. But this is our CMS starter template with a little bit of magic sprinkled into it. So, if we take a look at what I call the checklist where did that guy go? Supposed to have a production person here, Matt. Not calling you out, but I'm calling you out.\u003C\u002Fp>\u003Cp>The post hoc checklist, can we see that live? Do I have to stop screen sharing to see that? Yeah. There we go. Alright.\u003C\u002Fp>\u003Cp>So this is the AB testing checklist, as far as integrating with posthog. I'm gonna show you how to create a a project in posthog. We're going to dive into, like, creating a personal API key to power this little automation that we've got. We're gonna walk through the directest data model. We'll adjust some permissions.\u003C\u002Fp>\u003Cp>I'll show you the flow that's involved, and we'll talk through, like, this Next. Js front end, and how that is integrated. Alright. So let's get back to the screen share, and we'll do this together. What I'm going to do, and this is a little crazy to do.\u003C\u002Fp>\u003Cp>We are, like, maxed out as far as projects. So I'm gonna delete this test project. This is, sketchy on a demo on a webinar, but, that's what we're gonna do. Alright. So I'm in post all the first thing we've gotta do.\u003C\u002Fp>\u003Cp>Right? We're we're gonna create a project. Just go through this. This is gonna be the AB testing webinar project. Great.\u003C\u002Fp>\u003Cp>Alright. So we've got our project. I'm gonna need two things. I need the project ID. So I can find that up here in the URL, or, let me get my fancy mouse pointer going here.\u003C\u002Fp>\u003Cp>I can find that from these settings as well. So I'm gonna grab the project ID, and, yes, we will send the recording of this. I promise. Alright. So now in the Directus instance, which you are going to get total unrestricted access to you at the end of this, we've got some global set up.\u003C\u002Fp>\u003Cp>So global's inside Directus, are basically, just a what we call a singleton collection. So globals are are typically things like, social links or favicons, logos, stuff that you're gonna use across your entire site. So we're gonna add our project ID. And then the other thing that I'm gonna add, I I don't necessarily need this project API key for the directive side of things. You'll need this on the Next.\u003C\u002Fp>\u003Cp>Js integration. You'll wanna copy it to your clipboard, stick it into your text editor, so that you've got that. But what we're gonna do, we need to go into our personal settings. And the reason why is inside this Directus instance, there's an a nice little automation that will show. Alright.\u003C\u002Fp>\u003Cp>So I'm gonna log in, reauthenticate for security, and we're gonna look for a personal API key. So I'm just gonna create a new key. This is our a b testing webinar key. We want a specific project that's gonna be our AB testing webinar. Whenever you create keys, whether that's in post hoc or GitHub, please be very specific.\u003C\u002Fp>\u003Cp>So we're gonna do right access on experiments and feature flags, and I think this should be all we need. Am I am I correct in that assumption, your eye?\u003C\u002Fp>\u003Cp>Speaker 1: Looking good to me. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Perfect. Alright. So I'm gonna grab this key. I'm gonna go in, and I'm gonna post that inside this direct us instance.\u003C\u002Fp>\u003Cp>Amazing. Magic. Right? So let's let's talk through the changes inside this Directus instance. Again, this is our simple CMS template.\u003C\u002Fp>\u003Cp>Like, if you go to Directus.io, you go to get started for free, you create a cloud account, You get logged in. You can get the starting point for this, just by clicking CMS, or you can also get it through our template CLI tool. We'll button up all these resources. But this already has what we call the many to any relationship. It's basically a dynamic page builder that is set up inside your CMS.\u003C\u002Fp>\u003Cp>So if I open up my live preview pane, we can see that this page is made up of blocks. Right? And this paradigm lends itself to that block test that I was talking about. So that is kind of the setup here. The, extra collections that we've added to this direct to census, which are very minimal, are are just two pieces.\u003C\u002Fp>\u003Cp>Right? We have added experiments and experiment variants. And the reason why we add those inside direct us, we need to be able to link the content inside the CMS to the post hoc experiment. And this is it gets back into the why we created this. So we want to empower our marketing team, our content editors, to run tests, right, without code, without bothering the developer, without it being blocked by the developer.\u003C\u002Fp>\u003Cp>Right? We want marketers or well, this is my personal mission. I want developers and marketers to get along well. And if you are waiting, on information from marketing to set up the actual code for an AB test, not great. Likewise, if they have to bug you every time they want to test a new variant, that's gonna frustrate you as well.\u003C\u002Fp>\u003Cp>So, what we do, we've created a a experiments collection. And inside that, pretty simple. We've got a name for the experiment. We've got a feature flag key that you'll see we actually need, inside post hub. We've got a a short description.\u003C\u002Fp>\u003Cp>We've added a type of test. You know, is it a block or is it a page level test? And then we have our variants. So the variants are a relationship to that experiment variants, and this is pretty simple as well. We've got a key for the variant.\u003C\u002Fp>\u003Cp>Each experiment has to have a control variant as your eye talked about. And if you're doing a page level experiment or a redirect test, you need to have a URL. So on the front end, running a test is as simple as this. Right? With those pieces put together, and I'm sure Matt is crossing his fingers behind the scenes right now.\u003C\u002Fp>\u003Cp>Let's do a block level test. Right? So I'm inside Directus. I want to test a new headline. New headline.\u003C\u002Fp>\u003Cp>Home page. There he is. I see I see him in the chat. New headline for homepage. Alright.\u003C\u002Fp>\u003Cp>I stole this placeholder copy directly from you guys, Uriah. We want to let's see if this new headline improves conversion. So, hey, this doesn't totally replace post hoc. This is just a a slick integration to work together with the two. So we're gonna pick the test type.\u003C\u002Fp>\u003Cp>This is gonna be our block level test. We wanna test within the same page. And we'll add the control, and we'll add, just like this new headline variant. Great. Okay.\u003C\u002Fp>\u003Cp>Now with that out of the way, we save. What happens behind the scenes? There is an automation. I love automation. Direct as flows is a great way to build these automations.\u003C\u002Fp>\u003Cp>This is what this automation looks like, and I'll walk you through it really quickly. So whenever I go to create an item inside experiments, we've run this series of operations. We grab our global settings, so that API key, that project ID, and then we format a payload for post hoc. We create a new experiment inside post hoc using their API. Did we lose audio?\u003C\u002Fp>\u003Cp>Can you hear me okay, Yuri?\u003C\u002Fp>\u003Cp>Speaker 1: I can hear you, Brian. Yes.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Okay. Alright. I just wanted to make sure. Hopefully, it'll all be in the recording as well.\u003C\u002Fp>\u003Cp>But, then we've got a another it just it little piece of JavaScript here that formats a feature flag payload, and that's helpful for our redirect test that we're doing. And then basically, we stuff all that into post dog. And at the end of this, we return a payload that gets saved inside Directus. So the effect that we've get is a experiment that gets created inside Posthog. We've got a experiment here inside Directus now that we can actually link to, a piece of content.\u003C\u002Fp>\u003Cp>So if I go into post dog, we go to experiments. Check installation, skip installation. Skip or no? Skip. I did not remember that part of the creating a project.\u003C\u002Fp>\u003Cp>Alright. So we could see this experiment here inside post hoc. Let's see. There it is. This is all set up.\u003C\u002Fp>\u003Cp>But now let's go and link this to a piece of content. Right. So we're gonna go back to our home page, and we've got our hero block. So this is the control block. I'm just gonna go down to the bottom.\u003C\u002Fp>\u003Cp>And, basically, we've got a a relationship from this block to our experiment, and then we're gonna pick the variant that it belongs to. Except something wasn't quite right. It wouldn't be a demo that I was doing if everything worked smoothly. Why isn't my variant showing up? Clear filters.\u003C\u002Fp>\u003Cp>Experiment. There is the alright. You got me. Let's clean this up just a bit. I've got tried to get fancy, and I've got, I don't know what level of fancy I got here.\u003C\u002Fp>\u003Cp>But, okay. We'll try this again. Now I'm gonna link this to our new homepage headline experiment. We're going to add this to our control, and now we're gonna add another headline. This is the new headline.\u003C\u002Fp>\u003Cp>Amazing. It's gonna look beautiful. We're gonna link it to that same experiment, except now I'm gonna link this to our new headline variant. So all I'm doing behind the scenes here, nothing fancy. I'm just linking this piece of content to the posthog experiment.\u003C\u002Fp>\u003Cp>On the Next. Js front end, we're making a call. We get this content, And, because we've got posthog integrated, we get something like this. If I hit refresh, right, I don't see two hero images or two hero blocks here. I just see one.\u003C\u002Fp>\u003Cp>Right? And that is because of the post hoc SDK that's set up that is handling all the magic, and you could probably understand why I don't want to do all that magic myself. Now let's see if I can actually trigger you're gonna have to show me, like, a a trick to, like, force some type of, visitor into a variant sometime, your eye.\u003C\u002Fp>\u003Cp>Speaker 1: Sure. We can do that.\u003C\u002Fp>\u003Cp>Speaker 0: Let's see. This should be put in. Yeah. It's it doesn't seem like I can actually trigger the not triggering the variant here for some reason, through this. But, this is how this is actually integrated.\u003C\u002Fp>\u003Cp>This is like a a block level test. Now, you know, if I swap\u003C\u002Fp>\u003Cp>Speaker 1: Can you, can you open your web tools? Maybe we can try to override a flag, for this particular page. Yeah. I I Oh, we don't have to do that. That was up to you.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Okay. So there we go. So if you know, now you could see if I swap the control Right?\u003C\u002Fp>\u003Cp>If I make the headline the control, we could see the difference here. And, basically, the post hoc integration is is pulling that all together. So that is, like, the setup inside Directus. Like, if I wanted to run a page, a a reader direct level test, let's say I wanted to have a a new pricing page. Right?\u003C\u002Fp>\u003Cp>If I go to pricing, we've got pricing to fit every budget here. Maybe I wanna change this. We have new pricing. So we'll just create a new page. Pricing to fit no one's budget, and we'll just raise the prices by quite a bit here.\u003C\u002Fp>\u003Cp>Amazing. Alright. So now I'm gonna hit save as copy in this template, and now I've got I've got two new pages or or, well, one new page, but that's our page. I'm gonna go in. And now if I wanna do a redirect level test, I have new pricing page.\u003C\u002Fp>\u003Cp>Pricing page. We'll do a redirect. So the control, here, we're gonna add this URL. So that'll just be slash pricing. And when you set up the control experiment or the control variant, again, that is the URL that you're testing.\u003C\u002Fp>\u003Cp>It's an important distinction to make. Next, we will add the new pricing page. Great. That's gonna be new pricing. And, again, our direct us flow automation, like, will will bring this home for us and basically create this experiment inside post dog.\u003C\u002Fp>\u003Cp>So we'll just hit refresh. We've got our new pricing experiment, and I can click in and and see the the variance here. And as Yurai showed, there's just a feature flag that backs these. Now what we're doing on the page level tests is we're using the post hog feature flag payloads, to avoid making a extra call to direct us for this information. So if we I think I can get a better view if I click edit here, maybe shrink this back a bit.\u003C\u002Fp>\u003Cp>You can kinda see what's going on here. We've got an experiment type. It's a page level test. We've got a control path, so that's our pricing. And then we have a path that we're going to redirect to.\u003C\u002Fp>\u003Cp>And what happens on the front end if we go to local host 3,000. Now if I try to navigate to pricing, I'm either going to get the control did I I guess we may have to dive into the actual tools here. We'll we'll work that out in a moment. Love giving these demos on the fly. Alright.\u003C\u002Fp>\u003Cp>What is next on the agenda? We'll just look at that really quickly. Alright. Our feature flag test. Alright.\u003C\u002Fp>\u003Cp>So we've got the director side of things. We've nailed that piece. And then let's take a look at, like, the Next. Js side. Right?\u003C\u002Fp>\u003Cp>We want to, like, walk through how this is actually set up and integrated. So a a couple important pieces that you need as far as, like, setting this up within Next. Js. Let's pull this up. Alright.\u003C\u002Fp>\u003Cp>Can everybody see this? Okay. Let me try to close close the terminal a bit here. I I can shrink the the size of the make make the font just a little bit bigger. Alright.\u003C\u002Fp>\u003Cp>So, again, once you download this repo, you know, feel free to to browse through it on GitHub. We'll, again, we'll we'll send you all of this. But let's let's start on the direct side of things. Inside this Next. Js application, there are our fetchers.\u003C\u002Fp>\u003Cp>So we're just using these two. Close. Shrink that. Okay. There we go.\u003C\u002Fp>\u003Cp>Alright. So these fetchers are are basically just communicating using the Directus SDK. What we've got here and the only change that we've made from our standard Next. Js template is just making sure we grab the experiment data and the experiment variant that we've linked to a page block. So this all comes together on our, like, our page builder setup that we've got.\u003C\u002Fp>\u003Cp>And one of the other things that you'll have to do inside Directus, you can fetch that data, but you need to be able to add the data to your permissions. So you've got to make sure that your experiment variance and your experiments are enabled underneath your permissions to make this work inside Directus. That is just as far as getting into best practices, that's one of my Directus best practices. At 99% of my errors are because I didn't set permissions. Right?\u003C\u002Fp>\u003Cp>But we have to add that experiment variant there. And then inside what we call the page builder, there's a a bit of logic here that basically, filters out the blocks. So, this template is set up to run Next. Js server components, so we don't get this flash of content, whenever we enroll someone into a variant. But, basically, we're just checking to see, is this block attached to a variant in an experiment?\u003C\u002Fp>\u003Cp>If it is, we get the feature flag from the post halt client, which we'll look at in a moment. And should we add this block? So if the feature flag is found and the block is the control variant, we'll add that control variant. If the feature flag is found and it's not the control variant, we'll make sure we add that to the, to that block. So, not a huge shift in in the logic as far as working with Directus, just simply matching those up.\u003C\u002Fp>\u003Cp>On the post hoc side of it, and this is all we it's just standard boilerplate from the post hoc documentation. You need to have a post hoc provider. So we just set this up using use client here because this provider is going to go into a shared, like, a layout inside this Next. Js application. One of the important bits, especially for, like, server side rendering is the bootstrapping.\u003C\u002Fp>\u003Cp>So, basically, we're getting all the feature flags on the server side from post hoc and making sure we pass that when we initialize the the post hoc JS client on the the client side. And, Yurai, do you have anything to to kinda add on that bootstrapping side? You know? I I know this was, like, one point that was, a a little bit of where I ran into some issues when I was implementing this.\u003C\u002Fp>\u003Cp>Speaker 1: No. Not really. I would just say that this this is really the preferred way how to how to get a feature flex to your client, for a couple of reasons. Because the the other alternative is to actually fetch the feature flags directly from the client, but there is always some delay there. So, you know, you you you may get some usage events being sent without correct future flag information if you do it that way.\u003C\u002Fp>\u003Cp>Right? But if you if you bootstrap your flags, that means you always evaluate the flags on the server, which is actually faster because, the POSOC library actually evaluates, the flags there without having to actually go to the POSOC server. So it's actually faster. And once the the web content is served, you already get the feature flags kind of, like, basically already bootstrapped to it. So, this is is actually what what we always recommend, for our users to do.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Makes sense. Now how you do the bootstrapping, it depends on your specific application. The way that we chose to do it in this specific one.\u003C\u002Fp>\u003Cp>So we've got this post hoc provider. There's a there's a shared layout that I'll I'll show you. This is just how we we use this provider. So inside our root layout component, and this is using the the Next. Js app router setup, we are actually, like, sending this bootstrap data in a header from a Next JS middleware.\u003C\u002Fp>\u003Cp>So we get that here. We pass it to our provider, that sends it down through the client. But the middleware is an important piece. Now you you could do this via a, like, a server component and, like, if you're not doing the redirect testing, I found that that worked pretty well. But, as far as, like, the redirects, you probably wanna do this in the Next.\u003C\u002Fp>\u003Cp>Js middleware. Just this is the best way that I've found to do it. So what we've done inside the middleware, if we get to the actual function here. Right? We get the path name that you're sending, you know, what we're navigating to, and then we're basically getting a distinct ID.\u003C\u002Fp>\u003Cp>So this is just a helper that is somewhere, maybe in a where is that guy? Distinct ID. Yep. So a post log gives stores a cookie. We will try to get the distinct ID for that visitor, that user via that cookie.\u003C\u002Fp>\u003Cp>If we can't find it, we're just gonna create one. Right? And then we will look for some cache data inside the cookie. So we've got, like, a a bootstrap cookie, where we're we're caching this data. But, basically, what we're doing to enhance performance and and make sure that you're not, like, delaying rendering every single time, we've got a flag route set up on the API side, which is somewhere.\u003C\u002Fp>\u003Cp>Posthog flags. So, basically, there's a Node. Js posthog client. We pass that distinct ID to it. We go get all the flags and the payloads, and then we're caching that for sixty seconds.\u003C\u002Fp>\u003Cp>So as the user navigates, this middleware gets triggered, and, you know, we're we bootstrap that data. And then we also use this to handle our redirect, at the page level. So we've got a check for redirect function, which basically looks at that flag data that we have here. So once we fetch all of those flags from post hoc, we're iterating through those and saying, okay. Are any of these redirects that we've set up matching this experiment?\u003C\u002Fp>\u003Cp>If so, then we we send them through. You know, there's a a little bit more fancy stuff behind the scenes, but I I know we're coming up on time with this. Is there I I'm trying to think if there's any other, like, important pieces that I wanted to cover before we turn everybody just totally loose on this thing. I don't think so. Let's see.\u003C\u002Fp>\u003Cp>Where's my checklist? Redirect. We've configured the provider. And I saw where is was it Jobchum? Yes.\u003C\u002Fp>\u003Cp>So the that was a good spot. I figured out why this is not working. Thanks to JobChomp. It the public post hoc API key is from my previous project. So that's where I told everybody to take this down, make sure you copy it, but I forgot to stick it into my EMV.\u003C\u002Fp>\u003Cp>And, yeah, that's why we were having issues. Always love it on the demos. That's that's always fun. Alright. That's it.\u003C\u002Fp>\u003Cp>Let's, we'll open it up for q and a. You're I, you know, while we're waiting on questions to come up, I just wanted to say thank you for for jumping on with us and, you know, at least teaching me how to get get more use out of out of the post hoc side of things.\u003C\u002Fp>\u003Cp>Speaker 1: Of course. Yeah. It's a it's a it's a pleasure. It's it's a very nice integration that you that you build there. And, we actually have our own kind of, like, no code experimentation tool.\u003C\u002Fp>\u003Cp>It's still very, like, very much in beta. But, we'll I'd love to get access\u003C\u002Fp>\u003Cp>Speaker 0: to that.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Whoever whoever uses, like, just Bosak can actually, like, already try it out. It's it's, like, not nearly as powerful as as as, like, what direct Directus allows right now as in kind of, like, rearranging blocks and, like like, doing all that. It's basically just for, like, simple simple style changes. But, yeah, perhaps there is also something for us to to learn here.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. You know, I like, hopefully, like, coming out of this, we'll have, like, a, like, a how to dev blog post to to put this together. But, you know, one of the things that I I just I struggle to find any, like, linking to CMS examples. So, now that we've got one, this is how an integration could work.\u003C\u002Fp>\u003Cp>Let's let's take some questions here from Steven. Do you have a guide on what kind of traffic numbers you need to do effective testing?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So maybe I can answer that. So like I said, we we have this sample size calculator, which basically tells you exactly that. Now, that calculation is always tied to a particular metric. Right?\u003C\u002Fp>\u003Cp>So if you are tracking five metrics, but each of those metrics has kind of, like, different, like, usage numbers, as in, like, different number of persons that actually generate that metric. To be to be really statistically rigorous, you actually have to take the metric with, kinda like the smallest traffic and make sure that you actually get enough traffic for that metric. Right? Other than that, it's it's it's already like what I mentioned. The the the larger the change you are targeting, the smaller the sample size.\u003C\u002Fp>\u003Cp>But the smaller the change you are targeting means that, you kind of, like you need to have, like, a more sensitive test, and you need a you need a large larger sample size.\u003C\u002Fp>\u003Cp>Speaker 0: That makes sense. Alright. One other question I see from Stefan. How would you set up an AB test for global components across multiple pages like a header? Will it be, like, a new type of test that needs to be set up first?\u003C\u002Fp>\u003Cp>It it depends. Like, any good development oriented question, the answer is it depends. But, you know, if you've got the let me just pull up direct us real quick, and then we'll I'll give the bonus link in just a moment. Where's this at? Alright.\u003C\u002Fp>\u003Cp>So, you know, a basically, inside the direct us instance that that we've shown here, on the page block level, we we just added a relationship to the variance. And, you know, we've got the corresponding logic inside the Next. Js application that basically just says, hey. Post all, give me the variance and then assigns one of those. But you could add the same relationship to other pieces of the website if you wanted to, whether that was, you know, your navigation, like, your navigation items, within the setup.\u003C\u002Fp>\u003Cp>So this, this CMS setup has navigation already built into it. You know, you could potentially link it there. You know, you could do, like, a a hybrid approach inside the code where you, you know, you hard code some of these tests, which are, like, a lot of the examples in post hoc just for simplicity's sake are are there. In our own experience, it's like I I tryna I kind of, like, look at it through the lens of, like, hey. Is this something we're we're gonna do often?\u003C\u002Fp>\u003Cp>Like, do I wanna test header elements often? If so, you know, it might make sense to enable your content editors to be able to do that. If it's, you know, like, a one and done test, you might just, add it to the code and and move on. So, hopefully, that's helpful. Let me throw up the well, I'll just post it here in the chat, if I can.\u003C\u002Fp>\u003Cp>What's going on? The screen share is stuck. Why is this not working? Something's going on. Okay.\u003C\u002Fp>\u003Cp>I can't post the link here in the chat. Matt, if you're around, post this link in the chat for me. My screen is fouling up as it often does on these demos. We'll we'll definitely send this out in a newsletter after the webinar as well, but, there's a repo where you can get all the source code. If you have any questions, feel free to follow-up with us.\u003C\u002Fp>\u003Cp>On the Directus side of things, we are also offering a special little promo. And I can't get this to yeah. Hey. The screen share thing is just spinning for me. So that's where we're at with it.\u003C\u002Fp>\u003Cp>Uriah, thanks for joining, man. I really enjoyed this. You know, this has been a fun project, and I I appreciate your support and your help along the way.\u003C\u002Fp>\u003Cp>Speaker 1: Likewise.\u003C\u002Fp>\u003Cp>Speaker 0: Excellent. We'll have a recording out for everyone. And with that, thank you, and good night.\u003C\u002Fp>\u003Cp>Speaker 1: Thank you, everybody. Good luck. Bye bye.\u003C\u002Fp>","And we are live. Alright. Welcome. Welcome. Welcome. Super excited to kick off this webinar. It's been a long time in the making for me. I have been knee deep in AB testing over the last couple weeks, so super excited. We are going to be covering how to build AB testing inside your CMS with Posthog and Directus. As everybody trickles in, if you are in the chat, let us know where you are from. Hop in the chat. Let us know. Awesome. I am Brian Gillespie from Directus. I see a few of you in the chat already know me. We also have Yurai from Posthog. Yurai, nice to have you. Hey, everybody. Nice to meet you. I'm Yorai. I live in Amsterdam, Netherlands, and I've been at Posthog for about a year and a half. And for the last year or so, I've been, working hard, on our AB testing tool, which I'm hoping to, demo you a bit today. Yes. I could I I'm excited because I'm gonna learn a bit on this one as well. Obviously, I've done a lot of the technical implementation for not just our own use case at Directus, but, for this amazing bonus that we got for everyone at the conclusion of this. But, I, you know, I I haven't messed around with all of the the the, like, the metrics and, like, the testing and it just like, all of the config inside post hoc is is tremendously powerful. So I looking to, I guess, forward to seeing what, you know, how that is cooked up on your end. Alright. Let's, let's cover the agenda, and then we'll kinda give a brief overview of, both direct us and post hoc just because I I saw some questions in the sign up of the people that weren't familiar with either one. So this is obviously the awkward introduction phase. Hi. Okay. I see everybody. Canada, Tampa, Florida, Texas. Amazing. Nashville. Next, we're going to kick it over to Yurai where they'll cover he'll cover, basically, a demo of post hoc and how you set up experiments, how you run AB testing, what's a feature flag, what's not, what you should be thinking about as you're testing. And then we're going to, basically do a live jam sesh of how to connect Directus and Posthog using this, the starter kit that we've created. And we'll open this up for q and a at the end of this. And I'll show you guys how to get this amazing bonus with, the working source code, fingers crossed. Right? So you don't have to invest the time and headache that I have invested over the last couple weeks. But with that, Yirai, maybe you want to talk a little bit about post hoc for those who are unfamiliar with the tool. Sure. I guess it'll be easier if I share my screen right away. Yeah. Go for it. Let me do that. Alright. Well, so post hoc, started as, as a product analytics platform, but we've really evolved into kind of, like, an all in one solution which allows you to build great products. So, besides product analytics, we have today more tools such as session replay, feature flags, surveys, you know, data warehouse, and, of course, experiments, which we'll talk about today. So experiments, basically allow you to, test, like, variations on your website, test different changes, and see if those changes lead to some kind of improvements in the behavior of your users, which is then, of course, visible in the metrics that you are tracking. So the way it works is that you you use post hoc's product or feature called feature flags. And feature flags basically assign different variations of your website to your users. And usually, by default, you will be testing two variations, on your website. The variations will be normally called control and test. And let's say user a will get the variation control, user b gets the variation test. They will both see something different on your website, and then, you know, PulseHawk will track the behavior of your users on the website by capturing events. And then we aggregate those events on our site, and we basically calculate results for you, which tell you whether a particular variation is better than some other variation. So like I said, every experiment is is backed by a feature flag, but, actually, you don't really need to know, much about feature flags at all. The feature flag will be created for you when you set up an experiment. So, like, all you have to do is basically create the experiment, and I believe Brian will later show you how to do that via direct us. We will. And, basically, it all kind of, like, happens under under the hood. And, basically, all you need to then do to analyze your experiment is to just let it go to your experiments that impose HOG, and you will be able to analyze your results there. So I'm going to open an example experiment to show you what a results analysis might look like. So over here, I have a running experiment open. You can see that it was started, like, two months ago, which will be, like, a pretty long running experiment, but that's just because, like, it's it's some test data. But, basically, the the core of each experiment is the metrics that you are tracking. Right? It's it's these metrics that tell you, like, what what exact changes in behavior your your changes in in your content or in your experience are producing. And in this particular experiment, I'm tracking, six different metrics, three primary and three secondary. And the difference between the primary and secondary is just that it's just like the way of of organizing your metrics. So the primary metrics are something which actually inform whether your experiment is successful or not. And secondary metrics are kind of like guardrails. So it's it's something that you don't really want to regress. It's it's maybe not like a like a metric directly tied to your experiment, but it could be anything from, let's say, like, a session length or or any kind of, like, in interaction maybe indirectly linked to your experiment, but you still may want to track that to make sure that, that you don't get some some other part of your of your product kind of, like, regressed. Cool. That's a I I like that for sure. Hey. That's that's one of the confusing things for me. It was, like, what goes in primary, what goes in secondary? Just, so it sounds more of, like, secondary would be like, hey. This is a great guard against unwanted side effects. Like, hey. Exactly. Increased conversion, but, you know, like, the time on the page or or, like, session time or, bounce rate or something rose versus, the actual event that we wanted. That's exactly right. Yeah. So it's just like a way of organizing things. And but other than that, there is really, like, no difference under the hood between, like, a primary metric or a secondary metric. Basically, always, like, at a at a very low level, what we do at Boss Hog is capturing events, and, a metric is like a way of counting those events in a in a certain way. So let's have a look at at one of these metrics. So let's let's take a look at the first one. I'm going to click at this on this edit button, and here is my metric definition. So this is a funnel metric which measures, the conversion rate between two events. So the first conversion so so the first event here is called sign up started. The second event is sign up completed. And what this funnel metric measures is the conversion between these two events. So you can see that I have 3,000 persons who did the first event, but only 815 persons who, triggered the second event. And so the the difference between these two events is basically your conversion rate, which in this case is twenty seven percent. And what you actually see here so this is like the the metric definition form. This is not your experiment result. This is just kinda like a preview, which shows you well, is the data actually there, right, in the in the system? Like, are actually people sending these events? And that kind of tells you that, okay. Like, this is, like, a valid metric to experiment on. Like, our instrumentation is set up properly so we can actually go ahead and and save that metric. So once I save the metric and, well, once the experiment is running, I will start seeing my results once sort of, like, a minimum criteria are met, which is that, you know, you need to have some sort some number of events that have been ingested. You need to have events for both control and test variants. And once all of these are met, you will start seeing results. And the way we present results is kind of like an industry standard way to present results of, you know, AB experiments, which is that we show you this chart, which is called a delta chart. And what you see here is that for each variant, you will see this bar. And this bar is basically a credible interval. What it shows you is the the actual or let me actually start start start, like this. So so each each bar shows you, the actual difference between that given variant and the control variant. Right? The the black bar in the middle is the delta between the variant and and the and the control variant. So you can see that in case of the test one variant, we actually have a regression here. So the control is at 0%, and the test one variant is kind of like minus 13%. So that's bad. Right? Like, that's a that's a regression. We have worse conversion rate for the test one variant compared to the control variant. Now for the test two variant, we actually see an improvement. So the delta here is plus 6.92 compared to control, and that's why this bar is in is in green because it's actually gonna be improvement. So that's what the the the black vertical bar tells you. And now now onto the edges onto the edges of the actual bar. So this is like a credible interval. So what this bar tells you is actually the uncertainty that you have because, in any kind of statistical testing, there is some sort of uncertainty. And this like, the the the outer boundaries of these, credible intervals tells you, what kind of range in the actual results you may expect. And this basically tells you that, this credible interval, goes from minus 3% to plus 70%. And that means that in ninety five percent of the cases, because this is a so called 95% credible interval, you can expect the true value to lie between, like, this range. So there is still some sort of small probability that there will be a regression, for the test two variant even though, it's kind of like there's a high probability that it will be, some some sort of improvement. The narrower a credible interval is, the higher certainty you have. Right? Because, it's kind of like a tighter range of values where the actual value may lie. The wider it is, then it's kind of like more more uncertainty. And oftentimes, as you as you collect more data and you, like, keep refreshing results, you can you can kind of, like, observe the variance getting narrower and narrower every day as you kind of gather more data and get gather more more certainty. So that's what these credible intervals bars tell you. It's calculated separately for each of the metrics. And, at post hoc, we use a so called Bayesian statistical methodology, and the two main outputs of the methodology is the credible interval itself, which tells you the the uncertainty of the result. And kind of like the main output is is what we call win probability. So in this case, for this variant, there is an almost 83% probability that this test two variant is actually better than control. And then we show you, kind of like the the significance banner over here. And at post hoc, the the criterion that we use to tell you whether you should roll out a variant or shouldn't roll out a variant is that the win probability needs to be higher than 90% for the best variant. And in this case, you can see that it's actually less than 90%. And that's why for this particular metric, we declare it as not significant because it's less than 90%, which is what this tool tip also tells you. Okay. So now you've just answered, like, my own specific like, this is the biggest question I've had on, like, the experiments that we've ran of, like, what's how do you measure the significance? You know? Because we we've ran several tests, and, I'll get into, like, the specific results here in just a bit of one of our tests. But, like, some of the tests have been like, we saw, like, a it what looked like a positive improvement, but it was marked not significant. So it's like, like, it you know, can we be confident in that result or not? Right. Yeah. Yeah. That's a good point. And, we actually keep, improving this UI, and we want to actually, like, make it clearer as to, like, what all of these numbers mean and when you can expect significant. And it's like why something is significant, why something isn't significant. Just, like, make make all of this kind of decision making clear. So that's that's definitely a a valid point. I I I'll just stick you in the UI explaining it, man. Take it like you flawlessly. You did I was gonna ask that again. I I didn't hear that. I I was gonna say, just stick a video of you inside the UI because you, you you did flawlessly. Oh, nice. Yeah. We might actually do that. That's a that's a great Yeah. Cool. Am I still sharing? Because I think the, Oh, we lost the screen share. Yeah. Oh, okay. I'll reshare. Okay. And then, so just like continuing to the second metric, it's it's exactly the same principle. So each metric is is evaluated in exactly the same way. I mean, the the like, on our back end, there are some differences as to how, different metric types are evaluated. So for example, the second metrics the second metric is a different metric type. Right? So, like, in in the first case, we had a we were measuring funnel conversion. In the second metric, we are actually just measuring the role click count. And, of course, there are some statistical differences as to how this should be evaluated, like, on the back end. And we make sure that, like, we do this properly. But for you as a user, there's really no difference. You basically just look at, the movement of these bars relative to the control variant, and you look at the the win probability. And then the banner will will tell you whether a particular metric is significant or not. You can also dive deeper into any particular metrics. So if you click on details, you will see the actual counts for, for each variant. You can see that the test variant is strongest. Right? So it makes sense that it has the highest count over here. And this is like a cumulative chart, so the the the counts actually stay the same after we we stopped collecting the data for this experiment. Nice. Yeah. You can also see things like exposures for each variant, their means, the delta, things like that. There's also like a like a small cool feature, which is that you can actually view recordings for any particular variant. So the the the power of the POSIX platform is really that we offer multiple products, and they are kind of, like, interlinked. So you can can actually, like, click on on this particular variant and see the recordings, like, of those persons that that that actually, like, sold that variant. And I don't see any recordings here because I'm on my local instance and just, like, using all the data so you can know from actual users. But in Yeah. On actual dashboard, you would actually see recordings, of users where you could actually see how they how they interact with your with your website. Yeah. And and, like, having that all in one has been, like, a significant help for us at Directus. You know, I would it's not like for us, it's it's not stack overflow. It's it's like stack overload. Like, I, you know, I the thought of adding, like, six more tools to your tech stack for your website, is just like a a mess. It's it's like a pain for us. I I don't wanna do it. So and, like, when we integrated post hoc, like, the the analytics for us was, like, one of the one of the first things that we got a lot of value out of, and then we we started diving into the AB testing. You know, as we we kinda shift gears, like, do you have any best practices, your eye on, like like, what to test? You know, obviously, like, you've built this thing. You probably worked closely with with some clients at Posthog. Like, what are people testing? Do you have any best practices to share? Sure. So I would say if you if you are just starting out with experiments, start with something very small. So things like, you know, small changes to your landing page. That will really allow you to, just, like, kind of, like, get, like, how how the whole things works. And, maybe also kind of, like, circumvent some of the gotchas, like, like, why why while you are still starting out. Like, there are, like, several things that you should be aware of if you are implementing AB testing. I'm not sure if if, like I would say, like, maybe there can, like, be on the scope of this of this webinar, but we have, like, section with some troubleshooting and FAQs and, some best practices, when when implementing experiments. To summarize very quickly, I would say the like, one important thing is to make sure that your tracking is set up correctly. So, like, in your code, whenever a user performs a given action, you do actually capture that action so that Pulsar receives that event because, obviously, if we don't receive the correct events, we cannot, provide correct analysis. Now in terms of some in terms of, like, some likely more actionable AB testing advice, I would say start with small changes. Also make sure that, you are testing perhaps only, like, one or two changes at once. Because if you change, like, too many things, let's say, on your on your on your landing page, and then the test is showing, you know, significant outcome, like, you don't really know which which one of those a changes that you've made is actually leading to the improvement. Whereas, if you just like there's, like, small incremental changes, then you would be able to to tell that, kind of, like, more reliably. Another kind of, like, important technical detail is that you should probably use a reverse proxy on your post hoc setup to make sure that the, like, ad blockers are not, like, blocking capturing of the events, which is, like, a common issue, that can be really easily, circumvented with this. We also have, like, proper documentation, on this. Like, in general, if if you have, basically, for for any questions, you can use our search functionality in our documentation, for example, for the reverse proxy. That will explain to you exactly what you you should do to to set up also correctly to to be able to circumvent, ad blockers. Another useful tip is to learn how to actually estimate, your sample size properly. So one thing I I haven't explained yet is that we have this data collection section over here. And what this allows you to do is to is to answer the question how long you should run your experiment for. And, so I'm actually going to show you how this works. So if I click on edit over here, I have this slider which says minimum detectable effect. And this basically says, well, what kind of change in my metric am I trying to measure? And there's, like, a trade off to be made here because, if you are the the way the way sample sizes and experimentation work is that the larger the change you are trying to measure, the smaller the sample size you need. It may sound kind of counterintuitive. It it definitely. Definitely. Because I've seen this, and I'm like, like, hey. Why why do we need less people for this? Yeah. And, actually, right now, we are actually completely rebuilding this component to to do, like, a better job, explaining all this. But, basically, what this means is that if there is a huge change in your metric, you don't really need, like, a sensitive test for it. Right? You you don't need, like, a, like, a huge sample size because, if there is, like, a huge effect, that effect will already be apparent in, like, a relatively small sample size. But if you are trying to measure something much smaller, like, let's say, I'm I'm just going to move this slider from 10% to 2%, I need, like, a much more sensitive test, which means, like, a much higher sample size. Right? So, like, I I basically need, much bigger sample size to be able to reliably say, this 2% change is not just due some sort of chance. It's actually due to the actual change in the in the underlying behavior. But I definitely need, like, a larger sample size for this. So Right. The consideration some some some key considerations here is that are that, first of all, what is the sample size you can actually get? Right? If you if you are a startup and you're just, like, getting your first users, it might be actually difficult to get a sample size of 10,000 persons. So in that case, you are basically restricted to much smaller sample sizes. And Right. That actually means that your tests would actually would probably have to target, you know, just like large changes. You wanna go big or go home at that point. Exactly. Yeah. But as soon as you have some larger user base, you can start going after, you know, incremental small changes, that perhaps produce, smaller effects on your metrics. But you can you can run, like, many of such experiments and, you know, even incremental changes or or of one or 2% over time. They they really add up to a lot. So, yeah, there's, like, a trade off to be made here, when it comes to this, so it's it's it's good to be aware of that. Yeah. Well, awesome. Yeah. Thanks for the best practices, man. Thank you. Like, yeah, I'm I'm learning just as much on this as as the audience, so I appreciate that. We'll jump into, you know, kind of, like, our own experience a little bit, and then I'll, kinda run through the steps of of integrating post hoc and direct us and and, again, show you guys the the source code. We'll we'll dive into it. We won't write code. That didn't work out well for me the last time I, I did one of these live sessions. But, as far as our own, like, experience with posthog, you know, recently, we rolled out a brand new version of our home page. And I'm gonna can we see this? Oh, yeah. There we go. So this was a big change for us. And, you know, this one, if I shrink it down, it's probably a little better. But this was a this new homepage was a big shift for us. And one of the things that we wanted to do was we wanted to test first to make sure that, number one, the messaging was wasn't causing a a decrease in conversions. Number two, you know, we wanted to make sure that that this was performing better than our our old home page. You know, we've got this interactive carousel component that basically links into, what we call our directus pizza demo, which is just a a live working instance of directus. So folks can hop in and and poke around inside one of the templates. Before we we shifted all that traffic, right, we wanted to make sure that this was actually worthwhile. So our own results that we saw got a fancy slide up here somewhere. Boom. Yeah. So, the conversions were were relatively the same. And, again, that goes back to kinda your eyes point about, like, what metrics are, you know, how do we measure significant change? But some of the big results that we saw was, like, a 30% decrease in bounce rate on the site, which is huge. And, obviously, that correlates with a, like, a larger session time, most likely because people are getting in the demo of Directus, at least that's our hypothesis, and and actually poking around, which is which is what we want. And and I know you guys at Posthog, you're I, you guys are are kind of following that same methodology of, like, you know, let's let's skip all of the, fluffy marketing stuff and actually get you into the product, so we can actually dive in and and learn. Alright. So let's actually dive into a this integration. And I put together just, like, this a really nice visual for how we've been doing AB testing with post hoc at Directus. And that is kind of the the concept behind behind this setup. We've done tests at at two levels, and I call it the block level, which is basically testing within the same page, which is, you know, hey. We wanna test a different headline on the homepage, or we wanna test, a different pricing component or a a different pricing tier. So that would be like a block level test. And then, I I was calling this a page level test, which is basically testing between different pages. You could call it a split test. You guys are calling it redirect testing inside the documentation dry. But it basically like, we we take a URL. We want to redirect some percentage of the traffic. You know, usually, if it's just two variants you're testing, you you probably split fifty fifty. But but that's the way that we've been doing testing at Directus. And now I'm going to show you how this all comes together. So, this is post hoc, and we laid a special theme, special post hoc theme on top of a direct us instance just for this webinar. But this is our CMS starter template with a little bit of magic sprinkled into it. So, if we take a look at what I call the checklist where did that guy go? Supposed to have a production person here, Matt. Not calling you out, but I'm calling you out. The post hoc checklist, can we see that live? Do I have to stop screen sharing to see that? Yeah. There we go. Alright. So this is the AB testing checklist, as far as integrating with posthog. I'm gonna show you how to create a a project in posthog. We're going to dive into, like, creating a personal API key to power this little automation that we've got. We're gonna walk through the directest data model. We'll adjust some permissions. I'll show you the flow that's involved, and we'll talk through, like, this Next. Js front end, and how that is integrated. Alright. So let's get back to the screen share, and we'll do this together. What I'm going to do, and this is a little crazy to do. We are, like, maxed out as far as projects. So I'm gonna delete this test project. This is, sketchy on a demo on a webinar, but, that's what we're gonna do. Alright. So I'm in post all the first thing we've gotta do. Right? We're we're gonna create a project. Just go through this. This is gonna be the AB testing webinar project. Great. Alright. So we've got our project. I'm gonna need two things. I need the project ID. So I can find that up here in the URL, or, let me get my fancy mouse pointer going here. I can find that from these settings as well. So I'm gonna grab the project ID, and, yes, we will send the recording of this. I promise. Alright. So now in the Directus instance, which you are going to get total unrestricted access to you at the end of this, we've got some global set up. So global's inside Directus, are basically, just a what we call a singleton collection. So globals are are typically things like, social links or favicons, logos, stuff that you're gonna use across your entire site. So we're gonna add our project ID. And then the other thing that I'm gonna add, I I don't necessarily need this project API key for the directive side of things. You'll need this on the Next. Js integration. You'll wanna copy it to your clipboard, stick it into your text editor, so that you've got that. But what we're gonna do, we need to go into our personal settings. And the reason why is inside this Directus instance, there's an a nice little automation that will show. Alright. So I'm gonna log in, reauthenticate for security, and we're gonna look for a personal API key. So I'm just gonna create a new key. This is our a b testing webinar key. We want a specific project that's gonna be our AB testing webinar. Whenever you create keys, whether that's in post hoc or GitHub, please be very specific. So we're gonna do right access on experiments and feature flags, and I think this should be all we need. Am I am I correct in that assumption, your eye? Looking good to me. Yeah. Okay. Perfect. Alright. So I'm gonna grab this key. I'm gonna go in, and I'm gonna post that inside this direct us instance. Amazing. Magic. Right? So let's let's talk through the changes inside this Directus instance. Again, this is our simple CMS template. Like, if you go to Directus.io, you go to get started for free, you create a cloud account, You get logged in. You can get the starting point for this, just by clicking CMS, or you can also get it through our template CLI tool. We'll button up all these resources. But this already has what we call the many to any relationship. It's basically a dynamic page builder that is set up inside your CMS. So if I open up my live preview pane, we can see that this page is made up of blocks. Right? And this paradigm lends itself to that block test that I was talking about. So that is kind of the setup here. The, extra collections that we've added to this direct to census, which are very minimal, are are just two pieces. Right? We have added experiments and experiment variants. And the reason why we add those inside direct us, we need to be able to link the content inside the CMS to the post hoc experiment. And this is it gets back into the why we created this. So we want to empower our marketing team, our content editors, to run tests, right, without code, without bothering the developer, without it being blocked by the developer. Right? We want marketers or well, this is my personal mission. I want developers and marketers to get along well. And if you are waiting, on information from marketing to set up the actual code for an AB test, not great. Likewise, if they have to bug you every time they want to test a new variant, that's gonna frustrate you as well. So, what we do, we've created a a experiments collection. And inside that, pretty simple. We've got a name for the experiment. We've got a feature flag key that you'll see we actually need, inside post hub. We've got a a short description. We've added a type of test. You know, is it a block or is it a page level test? And then we have our variants. So the variants are a relationship to that experiment variants, and this is pretty simple as well. We've got a key for the variant. Each experiment has to have a control variant as your eye talked about. And if you're doing a page level experiment or a redirect test, you need to have a URL. So on the front end, running a test is as simple as this. Right? With those pieces put together, and I'm sure Matt is crossing his fingers behind the scenes right now. Let's do a block level test. Right? So I'm inside Directus. I want to test a new headline. New headline. Home page. There he is. I see I see him in the chat. New headline for homepage. Alright. I stole this placeholder copy directly from you guys, Uriah. We want to let's see if this new headline improves conversion. So, hey, this doesn't totally replace post hoc. This is just a a slick integration to work together with the two. So we're gonna pick the test type. This is gonna be our block level test. We wanna test within the same page. And we'll add the control, and we'll add, just like this new headline variant. Great. Okay. Now with that out of the way, we save. What happens behind the scenes? There is an automation. I love automation. Direct as flows is a great way to build these automations. This is what this automation looks like, and I'll walk you through it really quickly. So whenever I go to create an item inside experiments, we've run this series of operations. We grab our global settings, so that API key, that project ID, and then we format a payload for post hoc. We create a new experiment inside post hoc using their API. Did we lose audio? Can you hear me okay, Yuri? I can hear you, Brian. Yes. Okay. Okay. Alright. I just wanted to make sure. Hopefully, it'll all be in the recording as well. But, then we've got a another it just it little piece of JavaScript here that formats a feature flag payload, and that's helpful for our redirect test that we're doing. And then basically, we stuff all that into post dog. And at the end of this, we return a payload that gets saved inside Directus. So the effect that we've get is a experiment that gets created inside Posthog. We've got a experiment here inside Directus now that we can actually link to, a piece of content. So if I go into post dog, we go to experiments. Check installation, skip installation. Skip or no? Skip. I did not remember that part of the creating a project. Alright. So we could see this experiment here inside post hoc. Let's see. There it is. This is all set up. But now let's go and link this to a piece of content. Right. So we're gonna go back to our home page, and we've got our hero block. So this is the control block. I'm just gonna go down to the bottom. And, basically, we've got a a relationship from this block to our experiment, and then we're gonna pick the variant that it belongs to. Except something wasn't quite right. It wouldn't be a demo that I was doing if everything worked smoothly. Why isn't my variant showing up? Clear filters. Experiment. There is the alright. You got me. Let's clean this up just a bit. I've got tried to get fancy, and I've got, I don't know what level of fancy I got here. But, okay. We'll try this again. Now I'm gonna link this to our new homepage headline experiment. We're going to add this to our control, and now we're gonna add another headline. This is the new headline. Amazing. It's gonna look beautiful. We're gonna link it to that same experiment, except now I'm gonna link this to our new headline variant. So all I'm doing behind the scenes here, nothing fancy. I'm just linking this piece of content to the posthog experiment. On the Next. Js front end, we're making a call. We get this content, And, because we've got posthog integrated, we get something like this. If I hit refresh, right, I don't see two hero images or two hero blocks here. I just see one. Right? And that is because of the post hoc SDK that's set up that is handling all the magic, and you could probably understand why I don't want to do all that magic myself. Now let's see if I can actually trigger you're gonna have to show me, like, a a trick to, like, force some type of, visitor into a variant sometime, your eye. Sure. We can do that. Let's see. This should be put in. Yeah. It's it doesn't seem like I can actually trigger the not triggering the variant here for some reason, through this. But, this is how this is actually integrated. This is like a a block level test. Now, you know, if I swap Can you, can you open your web tools? Maybe we can try to override a flag, for this particular page. Yeah. I I Oh, we don't have to do that. That was up to you. Yeah. Yeah. Okay. So there we go. So if you know, now you could see if I swap the control Right? If I make the headline the control, we could see the difference here. And, basically, the post hoc integration is is pulling that all together. So that is, like, the setup inside Directus. Like, if I wanted to run a page, a a reader direct level test, let's say I wanted to have a a new pricing page. Right? If I go to pricing, we've got pricing to fit every budget here. Maybe I wanna change this. We have new pricing. So we'll just create a new page. Pricing to fit no one's budget, and we'll just raise the prices by quite a bit here. Amazing. Alright. So now I'm gonna hit save as copy in this template, and now I've got I've got two new pages or or, well, one new page, but that's our page. I'm gonna go in. And now if I wanna do a redirect level test, I have new pricing page. Pricing page. We'll do a redirect. So the control, here, we're gonna add this URL. So that'll just be slash pricing. And when you set up the control experiment or the control variant, again, that is the URL that you're testing. It's an important distinction to make. Next, we will add the new pricing page. Great. That's gonna be new pricing. And, again, our direct us flow automation, like, will will bring this home for us and basically create this experiment inside post dog. So we'll just hit refresh. We've got our new pricing experiment, and I can click in and and see the the variance here. And as Yurai showed, there's just a feature flag that backs these. Now what we're doing on the page level tests is we're using the post hog feature flag payloads, to avoid making a extra call to direct us for this information. So if we I think I can get a better view if I click edit here, maybe shrink this back a bit. You can kinda see what's going on here. We've got an experiment type. It's a page level test. We've got a control path, so that's our pricing. And then we have a path that we're going to redirect to. And what happens on the front end if we go to local host 3,000. Now if I try to navigate to pricing, I'm either going to get the control did I I guess we may have to dive into the actual tools here. We'll we'll work that out in a moment. Love giving these demos on the fly. Alright. What is next on the agenda? We'll just look at that really quickly. Alright. Our feature flag test. Alright. So we've got the director side of things. We've nailed that piece. And then let's take a look at, like, the Next. Js side. Right? We want to, like, walk through how this is actually set up and integrated. So a a couple important pieces that you need as far as, like, setting this up within Next. Js. Let's pull this up. Alright. Can everybody see this? Okay. Let me try to close close the terminal a bit here. I I can shrink the the size of the make make the font just a little bit bigger. Alright. So, again, once you download this repo, you know, feel free to to browse through it on GitHub. We'll, again, we'll we'll send you all of this. But let's let's start on the direct side of things. Inside this Next. Js application, there are our fetchers. So we're just using these two. Close. Shrink that. Okay. There we go. Alright. So these fetchers are are basically just communicating using the Directus SDK. What we've got here and the only change that we've made from our standard Next. Js template is just making sure we grab the experiment data and the experiment variant that we've linked to a page block. So this all comes together on our, like, our page builder setup that we've got. And one of the other things that you'll have to do inside Directus, you can fetch that data, but you need to be able to add the data to your permissions. So you've got to make sure that your experiment variance and your experiments are enabled underneath your permissions to make this work inside Directus. That is just as far as getting into best practices, that's one of my Directus best practices. At 99% of my errors are because I didn't set permissions. Right? But we have to add that experiment variant there. And then inside what we call the page builder, there's a a bit of logic here that basically, filters out the blocks. So, this template is set up to run Next. Js server components, so we don't get this flash of content, whenever we enroll someone into a variant. But, basically, we're just checking to see, is this block attached to a variant in an experiment? If it is, we get the feature flag from the post halt client, which we'll look at in a moment. And should we add this block? So if the feature flag is found and the block is the control variant, we'll add that control variant. If the feature flag is found and it's not the control variant, we'll make sure we add that to the, to that block. So, not a huge shift in in the logic as far as working with Directus, just simply matching those up. On the post hoc side of it, and this is all we it's just standard boilerplate from the post hoc documentation. You need to have a post hoc provider. So we just set this up using use client here because this provider is going to go into a shared, like, a layout inside this Next. Js application. One of the important bits, especially for, like, server side rendering is the bootstrapping. So, basically, we're getting all the feature flags on the server side from post hoc and making sure we pass that when we initialize the the post hoc JS client on the the client side. And, Yurai, do you have anything to to kinda add on that bootstrapping side? You know? I I know this was, like, one point that was, a a little bit of where I ran into some issues when I was implementing this. No. Not really. I would just say that this this is really the preferred way how to how to get a feature flex to your client, for a couple of reasons. Because the the other alternative is to actually fetch the feature flags directly from the client, but there is always some delay there. So, you know, you you you may get some usage events being sent without correct future flag information if you do it that way. Right? But if you if you bootstrap your flags, that means you always evaluate the flags on the server, which is actually faster because, the POSOC library actually evaluates, the flags there without having to actually go to the POSOC server. So it's actually faster. And once the the web content is served, you already get the feature flags kind of, like, basically already bootstrapped to it. So, this is is actually what what we always recommend, for our users to do. Yeah. Yeah. Makes sense. Now how you do the bootstrapping, it depends on your specific application. The way that we chose to do it in this specific one. So we've got this post hoc provider. There's a there's a shared layout that I'll I'll show you. This is just how we we use this provider. So inside our root layout component, and this is using the the Next. Js app router setup, we are actually, like, sending this bootstrap data in a header from a Next JS middleware. So we get that here. We pass it to our provider, that sends it down through the client. But the middleware is an important piece. Now you you could do this via a, like, a server component and, like, if you're not doing the redirect testing, I found that that worked pretty well. But, as far as, like, the redirects, you probably wanna do this in the Next. Js middleware. Just this is the best way that I've found to do it. So what we've done inside the middleware, if we get to the actual function here. Right? We get the path name that you're sending, you know, what we're navigating to, and then we're basically getting a distinct ID. So this is just a helper that is somewhere, maybe in a where is that guy? Distinct ID. Yep. So a post log gives stores a cookie. We will try to get the distinct ID for that visitor, that user via that cookie. If we can't find it, we're just gonna create one. Right? And then we will look for some cache data inside the cookie. So we've got, like, a a bootstrap cookie, where we're we're caching this data. But, basically, what we're doing to enhance performance and and make sure that you're not, like, delaying rendering every single time, we've got a flag route set up on the API side, which is somewhere. Posthog flags. So, basically, there's a Node. Js posthog client. We pass that distinct ID to it. We go get all the flags and the payloads, and then we're caching that for sixty seconds. So as the user navigates, this middleware gets triggered, and, you know, we're we bootstrap that data. And then we also use this to handle our redirect, at the page level. So we've got a check for redirect function, which basically looks at that flag data that we have here. So once we fetch all of those flags from post hoc, we're iterating through those and saying, okay. Are any of these redirects that we've set up matching this experiment? If so, then we we send them through. You know, there's a a little bit more fancy stuff behind the scenes, but I I know we're coming up on time with this. Is there I I'm trying to think if there's any other, like, important pieces that I wanted to cover before we turn everybody just totally loose on this thing. I don't think so. Let's see. Where's my checklist? Redirect. We've configured the provider. And I saw where is was it Jobchum? Yes. So the that was a good spot. I figured out why this is not working. Thanks to JobChomp. It the public post hoc API key is from my previous project. So that's where I told everybody to take this down, make sure you copy it, but I forgot to stick it into my EMV. And, yeah, that's why we were having issues. Always love it on the demos. That's that's always fun. Alright. That's it. Let's, we'll open it up for q and a. You're I, you know, while we're waiting on questions to come up, I just wanted to say thank you for for jumping on with us and, you know, at least teaching me how to get get more use out of out of the post hoc side of things. Of course. Yeah. It's a it's a it's a pleasure. It's it's a very nice integration that you that you build there. And, we actually have our own kind of, like, no code experimentation tool. It's still very, like, very much in beta. But, we'll I'd love to get access to that. Yeah. Whoever whoever uses, like, just Bosak can actually, like, already try it out. It's it's, like, not nearly as powerful as as as, like, what direct Directus allows right now as in kind of, like, rearranging blocks and, like like, doing all that. It's basically just for, like, simple simple style changes. But, yeah, perhaps there is also something for us to to learn here. Yeah. Yeah. You know, I like, hopefully, like, coming out of this, we'll have, like, a, like, a how to dev blog post to to put this together. But, you know, one of the things that I I just I struggle to find any, like, linking to CMS examples. So, now that we've got one, this is how an integration could work. Let's let's take some questions here from Steven. Do you have a guide on what kind of traffic numbers you need to do effective testing? Yeah. So maybe I can answer that. So like I said, we we have this sample size calculator, which basically tells you exactly that. Now, that calculation is always tied to a particular metric. Right? So if you are tracking five metrics, but each of those metrics has kind of, like, different, like, usage numbers, as in, like, different number of persons that actually generate that metric. To be to be really statistically rigorous, you actually have to take the metric with, kinda like the smallest traffic and make sure that you actually get enough traffic for that metric. Right? Other than that, it's it's it's already like what I mentioned. The the the larger the change you are targeting, the smaller the sample size. But the smaller the change you are targeting means that, you kind of, like you need to have, like, a more sensitive test, and you need a you need a large larger sample size. That makes sense. Alright. One other question I see from Stefan. How would you set up an AB test for global components across multiple pages like a header? Will it be, like, a new type of test that needs to be set up first? It it depends. Like, any good development oriented question, the answer is it depends. But, you know, if you've got the let me just pull up direct us real quick, and then we'll I'll give the bonus link in just a moment. Where's this at? Alright. So, you know, a basically, inside the direct us instance that that we've shown here, on the page block level, we we just added a relationship to the variance. And, you know, we've got the corresponding logic inside the Next. Js application that basically just says, hey. Post all, give me the variance and then assigns one of those. But you could add the same relationship to other pieces of the website if you wanted to, whether that was, you know, your navigation, like, your navigation items, within the setup. So this, this CMS setup has navigation already built into it. You know, you could potentially link it there. You know, you could do, like, a a hybrid approach inside the code where you, you know, you hard code some of these tests, which are, like, a lot of the examples in post hoc just for simplicity's sake are are there. In our own experience, it's like I I tryna I kind of, like, look at it through the lens of, like, hey. Is this something we're we're gonna do often? Like, do I wanna test header elements often? If so, you know, it might make sense to enable your content editors to be able to do that. If it's, you know, like, a one and done test, you might just, add it to the code and and move on. So, hopefully, that's helpful. Let me throw up the well, I'll just post it here in the chat, if I can. What's going on? The screen share is stuck. Why is this not working? Something's going on. Okay. I can't post the link here in the chat. Matt, if you're around, post this link in the chat for me. My screen is fouling up as it often does on these demos. We'll we'll definitely send this out in a newsletter after the webinar as well, but, there's a repo where you can get all the source code. If you have any questions, feel free to follow-up with us. On the Directus side of things, we are also offering a special little promo. And I can't get this to yeah. Hey. The screen share thing is just spinning for me. So that's where we're at with it. Uriah, thanks for joining, man. I really enjoyed this. You know, this has been a fun project, and I I appreciate your support and your help along the way. Likewise. Excellent. We'll have a recording out for everyone. And with that, thank you, and good night. Thank you, everybody. Good luck. Bye bye.","70c8471c-7ad1-49ad-bcde-86e4370fc40b",[1224,1225],"f885efad-ff0f-404d-a475-fc8f0319742d","be9d26d5-180f-4a33-8907-1353e1a713e1",[],{"year":978,"number":979,"id":980,"episodes":1228,"show":1229},[982,983,984,963],{"title":986,"slug":987},{"id":691,"slug":1231,"vimeo_id":1232,"description":1233,"tile":1234,"length":722,"resources":12,"people":12,"episode_number":129,"published":1235,"title":1236,"video_transcript_html":1237,"video_transcript_text":1238,"content":12,"seo":1239,"status":19,"episode_people":1240,"recommendations":1244,"season":1245},"February-2025","1053401939","Join us for The Changelog, taking you through the month’s Directus updates including product updates, new content and community contribution highlights. This month's show includes a documentation update, Labs extension showcase, a template preview from Lindsey Zylstra and an episode of a new show on Directus TV called Translation Station.","b6280603-6b9a-4a4b-a17d-5f9b03d9c1ec","2025-02-12","February 2025","\u003Cp>Speaker 0: Alright. Hello, everyone. I hope you're having a great day. I'm Beth, and it is my pleasure to welcome you to the February change log. As usual, if you are live, do let us know in the chat where you are joining us from.\u003C\u002Fp>\u003Cp>We have a great set of updates for you today. We have some product updates. We've got some new extensions, some tutorials coming for you very soon. We have a new episode of a brand new director's TV show that has launched today, and we have Lindsay with a starter's preview. So do stick around if you are able to.\u003C\u002Fp>\u003Cp>And I'm gonna kick us off by sending this over to Kevin for some product updates.\u003C\u002Fp>\u003Cp>Speaker 1: Hello. Kevin here with an update on what's new in Directus version eleven point four point one. This is a nice small release with some quality of life improvements. Firstly, we have standardized how delete buttons work across the Data Studio with a new component. They have a nice little confirmation dialogue so you don't mistakenly delete things in future expecting it to be there when it wasn't always.\u003C\u002Fp>\u003Cp>The translation store is now available to app extension authors, which makes it easier to create multi language app extensions for the Data Studio. Directus now supports rendering many to any items in related value displays, for example, in layouts. And the send email operation now supports cc, bcc, and reply to fields, which like everything in Directus Automate can take in dynamic values. There's also been a whole bunch of little fixes which are rooted in consistency and standardization across Directus. You can check the full release notes in GitHub.\u003C\u002Fp>\u003Cp>You can download Directus eleven point four point one from Docker Hub and use these new additions in Directus Cloud right now. Thank you so much for joining me, and we'll see you next time.\u003C\u002Fp>\u003Cp>Speaker 2: Hi. I'm Mike, and I'm from the developer experience team. I'm going to demonstrate each of the new extensions in Directus Labs this month. As a reminder, Directus Labs is our team's experimental org on GitHub, which regularly publishes new extensions. The first extension we'll go through is going to be the YouTube embed, which is listed here, and I've put it as video cast.\u003C\u002Fp>\u003Cp>If we open up the item, we can add URL, and then we'll have the actual interface itself. This is configured using the Google Console API and the channel specifically you want to look at. So I'm just going to randomly select something so I know it makes sense. Copy that. Add that there just for a little bit of context for myself.\u003C\u002Fp>\u003Cp>Look into YouTube and then look up sell. Copy the item. Remember to put this into the code of the content of from the WYSIWYG if necessary. And\u003C\u002Fp>\u003Cp>Speaker 3: we are\u003C\u002Fp>\u003Cp>Speaker 2: good. And that is the YouTube interface. The next extension we'll show is the AI researcher bundle. With this one, we can configure it with anthropic or open AI models. All you require is your API key to add to it.\u003C\u002Fp>\u003Cp>So in this case, I have my PhD thesis, and I want to ask something about quarks. What is a quark? It'll go away and ask the model and return the answer. And it does it in great depth. How many types are there?\u003C\u002Fp>\u003Cp>And since we've been asking separate questions and we want to keep the context of the conversation, the entire conversation is available in this pop up whilst still maintaining the main value. So we can close that and move on. And here we have our final extension of the day. This is the inline repeater. As you can see, it has elements that can be expanded and include multiple items as part of them.\u003C\u002Fp>\u003Cp>So here we have the service and the URL for a podcast. I'm gonna add a new one here. And we're gonna go to Spotify and just grab the URL. Add that in. And we'll we'll have multiple inline elements containing the different objects.\u003C\u002Fp>\u003Cp>We can add as many as we like as part of this repeater. Thanks.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Thank you so much to Kevin and Mike for those updates. If you're planning on using anything that they've mentioned or if you've got any questions about it, do let us know here in the chat or on discord and we'll do our best to get you your answers. And next up we are very excited to have Lindsay talking about starters preview. So here is Lindsay.\u003C\u002Fp>\u003Cp>Speaker 3: Hi. I'm Lindsay Zylstra. Today, I'm excited to introduce you to our new Next. Js simple CMS starter. This starter is designed to work with our simple CMS back end template.\u003C\u002Fp>\u003Cp>It gives you a powerful starting point to build off of or modify to meet your unique needs so you can style and customize quickly without getting bogged down with the setup. But what exactly is this starter? Essentially, it's a ready to use framework specific front end that connects seamlessly with our simple CMS back end to help you build full CMS web applications. With this starter, you benefit from a robust Directus integration that simplifies content management, along with Next. Js App Router that effortlessly handles dynamic routing and layouts.\u003C\u002Fp>\u003Cp>We've we've also incorporated Tailwind CSS and ShadCn components to offer rapid, utility first styling. Plus, it comes with built in examples that show you how to implement live previews, add customized blocks, set up dynamic form fields, all of this giving you a complete toolkit to kick start your projects. Let me switch over to my screen for a quick demo. First, I'm gonna show you our Directus integration in Versus Code. Here you can see we're using the Directus SDK along with a fetchers file here that gives a great example of how to retrieve data from Directus seamlessly.\u003C\u002Fp>\u003Cp>We've also included a command line tool to generate Directus types automatically, saving you the hassle of creating them manually. Now if we look over here at our direct Us pages content, you can see our starter pages build dynamically from direct Us data. This dynamic approach allows you to add blocks and construct your pages, and we've integrated site map generation and SEO in order to give you a head start on making your site more discoverable. We've left most of the styling up to you with only minimal opinionated design choices. However, we've included useful features like dark mode and the ability to set an accent color So you can easily tailor the look to match your brand.\u003C\u002Fp>\u003Cp>Moving on to dynamic forms. Our dynamic forms are designed to offer flexibility. You can quickly create forms with fields defined in Directus. For example, here's a contact us form. You can set customized success messages as well as change your submit labels and add in any type of field you'd like to see.\u003C\u002Fp>\u003Cp>We manage validations through Zod, schema validation. This setup makes it straightforward to add any additional validations you might need. Here's the contact us form on the page. Finally, we'll take a look at our global search. It provides a basic search right out of the box.\u003C\u002Fp>\u003Cp>You have the capability to expand it for deeper and more comprehensive searches as your project grows. Each of these features work together to create a flexible, developer friendly starter that empowers you to build full CMS powered web applications without unnecessary overhead. Our primary goal was to create a developer friendly experience that tackles common challenges head on. We focused on ensuring smooth live preview, flexible content management, and intuitive setup, whether you're running Directus in the cloud or locally. So how do you get access to this?\u003C\u002Fp>\u003Cp>You can find this code in our Directus Labs repo under starters. Dive right in by following our detailed setup instructions available in the ReadMe files. This will get you up and running with all the features we've discussed. As a sneak peek, we're also working on a command line tool that'll be out in beta soon. This will simplify the installation and configuration even further so you can focus on building and less on setup.\u003C\u002Fp>\u003Cp>That's our quick overview of our Next. Js simple CMS starter with Directus integration. I hope you enjoyed this walk through. In the coming months, we plan to release more of these starters for other popular frameworks as well as more that will pair with our other back end templates. So be sure to check back in.\u003C\u002Fp>\u003Cp>If you're interested in the command line tool or any of our future starters that will be released, make sure to subscribe to our newsletter. Thank you for watching. I can't wait to see what you build with this tool. Have a great day.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Thank you so much to Lindsay, and we're hoping that you're as excited as we are to be able to share that. Before I move us on to the episode of Translation Station, I wanna highlight that we've got some, exciting tutorials coming your way to the new docs very soon. We have implementing direct us live preview and using direct us off, in Next, Nuxt, Astro, and SvelteKit, and they'll be available very soon. So do check back, if that sounds good to you.\u003C\u002Fp>\u003Cp>Next, we have episode one of translation station, which is a four part new directors TV series. It's exploring the opportunities of localization in directors. And as a series, it covers topics from translating the director's code base, translation strings, translating your content, and accessing your translated content via the director's APIs. Have I said translation one too many times? I think you get the idea that it's all about translations, so here is episode one.\u003C\u002Fp>\u003Cp>Speaker 4: Carmen, developer, educator, and translation station. Translation station. Alright. Back to English. So here we've got a direct us project that I'm running for my blog.\u003C\u002Fp>\u003Cp>And, yeah, just one blog post for now. We'll be filling it up with time. I wanted to show you in the data studio the collections I've got. I've got some authors, I've got some languages, and I've got some posts. So, as you can see, my computer's default language is English.\u003C\u002Fp>\u003Cp>Now, my Directus project is also running by default in English. But what if I wanna make it available in another language? What I can do is go over to the settings module and then the settings page itself where amongst other settings, I'm gonna find the default language. I'm gonna open this up and show you just how many languages we have available here. And not just languages, but languages specific to a region.\u003C\u002Fp>\u003Cp>So, I'm gonna scroll down being Chilean myself, I'm gonna set my language to Latin American Spanish. Gina's there too, but I wanna encompass all of this continent. Click on that and I'm going to save my changes. You can see now that a bunch of things have changed already. I'm in the setting in the modules instead of settings and so on and so forth.\u003C\u002Fp>\u003Cp>What's really key to know here is the Directus project language has now changed. But what happens to the rest of our things? For example, in our data model, or modelo de depos, we're gonna see that the names of our collections haven't changed. Or, if we go to content or contenido as it is now, we're gonna see that my blog post stays the same, even the fields on the blog post themselves. But that's to be expected because our project is now in Spanish, but the content itself and the data model are still in English because that's how it was when I created it.\u003C\u002Fp>\u003Cp>But what I want to focus on here is the language of the Directus project itself. Let's go back to our settings module real quick. You might be wondering where do the translations for these come from and how can I change or contribute to them? Now given that Directus is open source under the BSL license, we actually get these translations as open source contributions. That's right.\u003C\u002Fp>\u003Cp>You can contribute your own translations to Directus. Next, I'm gonna show you how to do that. Over in the Directus documentation, under Resources, Community, and contribution translations, we're gonna find the instructions for how to contribute localizations to the Directus Data Studio, and this is done via platform called Crowdin. So over in Crowdin, we can see there's a bunch of translations that exist, which I love to see. We've got Catalan, Dutch, Finnish, Estonian, and a whole bunch of other languages.\u003C\u002Fp>\u003Cp>So, how can I contribute to a language, say, if I find a typo? First thing I need to do is log in to Crowded, which is via a free account. Now that I'm logged in, I can go ahead and look for my language and start contributing to it. For example, let's look for Spanish and specifically focus on that Spanish Latin America. Now on this page, I'm gonna click on translate all.\u003C\u002Fp>\u003Cp>This will give me a list of all of the strings or pieces of text that can be translated in Directus. Now here, under editing preset, I can see the string editor predefinido. But because this is in the current test we're currently editing, it should actually be editando predefinido. So what I'm gonna do is make that suggestion and save it. But because there's already an approved translation, what I'm gonna do is notify a proofreader.\u003C\u002Fp>\u003Cp>Cool. So now my suggestion is made to improve this Latin American Spanish translation of Directus. But what happens next? So our team over at Directus is going to get a pull request from Crowdin with the change that I've made. Now you might be thinking, well, why don't I just make the pull request if it's open source?\u003C\u002Fp>\u003Cp>Bear in mind that crowding's automatic synchronization is going to overwrite any changes that don't correspond with crowding itself. So it's best to make those translations directly through crowding. Now, the last question is, when am I gonna get that new string? It'll be merged into Directus itself and will be available in the next release. So, now we just gotta sit tight.\u003C\u002Fp>\u003Cp>Alright. Well, there you have it. How to configure your Directus project in another language, which strings are translated directly, and how to contribute your own translations to the greater Directus ecosystem. I hope this was helpful, but there's more stops on this translation station journey we're going on. We'll also be learning about translation strings, translating content in the data studio, and accessing translated content in your own application.\u003C\u002Fp>\u003Cp>So all aboard for the next station, and off we go. See you next time.\u003C\u002Fp>\u003Cp>Speaker 5: We want to take a moment towards the end of the change log for thanking our amazing community contributors who give their time to improve the director's project. Since last month, there have been three contributors. A huge thank you to Nicholas for fixing conditional interface options incorrectly replacing choices, High Vibes only for adding support for CC, BCC, and reply to fields to the email operation, iodele for fixing a grammar error in the live preview documentation. Thank you again, and you can see their specific pull requests inside of the full release notes on GitHub. Lastly, we also want to take the time to thank our GitHub Sponsors of January who financially contribute to Directus' development.\u003C\u002Fp>\u003Cp>A huge thank you to Wei Fan, Entel, Jamiluddin, Fergus, Omar, Marcus, c k, Peter, Utomic, Steven, Robson, nonlinear, Andreas, Valentino, John, Wayne, Burb, Adam, Jason, Birka, Jens, Vincent, Mike, Khan, and Lassie. The money we are given from our GitHub Sponsors goes straight back to community members who build tooling and extensions for the director's ecosystem. For this month's reading list, we're going off script and sharing an audio special of our favorite tech and tech adjacent podcasts. Firstly, one that you've likely heard of, Syntax, a podcast about web development covering topics including full stack web development, the latest frameworks, databases, and lots more. There's over 800 episodes, so lots to check out.\u003C\u002Fp>\u003Cp>Next, we have Waveform, a weekly podcast aimed at gadget lovers covering trends and product reviews. Next is Darknet Diaries, which explores topics from the dark web like cybercrime, hacking, and other less than legal activities, as well as the people fighting back against them. Deep Questions with Cal Newport focuses on productivity and the struggle to work and live deeply in a world with lots of digital distractions. Decoder is a new show from The Verge that members of our team have been enjoying for its interviews with technology leaders on their take on technology and the future of it all. The DevTools podcast does what it says on the tin.\u003C\u002Fp>\u003Cp>It's a podcast about developer tools and the people who make them, with lots of interesting guests from companies you're probably using. Hiking humans looks behind the social engineering scams, phishing schemes, and criminal exploits that are making headlines and taking a heavy toll on organizations around the world. What the Shell, aside from having an exceptional name, provides an accessible look into major hacks and vulnerabilities through looking at incidents and news. And lastly, but in no means least, open observability talks discuss harnessing the power of open source to advance observability initiatives with guests that range from open source project creators, end users, and thought leaders. We hope you enjoy checking out some of the podcasts.\u003C\u002Fp>\u003Cp>If you've got your own recommendations, do share them with us. We'd love to hear what you're listening to.\u003C\u002Fp>\u003Cp>Speaker 0: Alright and that is it for February's the changelog. If you have any questions about anything you've seen and whether you're joining us live or, watching on demand do get over to discord and we'll be able to help help you out over there. Do check out Director TV. We are always adding new shows. You should be able to find translation station.\u003C\u002Fp>\u003Cp>It will be right at the top, so you shouldn't be able to miss it. As always, if you've got any feedback about things you'd like to see within the changelog, very searchable feedback. We'd love to hear your thoughts. Thank you so much if you have made it this far for sticking with us and spending your time with us. We really appreciate it.\u003C\u002Fp>\u003Cp>And please head over to Discord for the next info on the change log for March. Have a great day, everyone, and I'll chat with you soon. Bye.\u003C\u002Fp>","Alright. Hello, everyone. I hope you're having a great day. I'm Beth, and it is my pleasure to welcome you to the February change log. As usual, if you are live, do let us know in the chat where you are joining us from. We have a great set of updates for you today. We have some product updates. We've got some new extensions, some tutorials coming for you very soon. We have a new episode of a brand new director's TV show that has launched today, and we have Lindsay with a starter's preview. So do stick around if you are able to. And I'm gonna kick us off by sending this over to Kevin for some product updates. Hello. Kevin here with an update on what's new in Directus version eleven point four point one. This is a nice small release with some quality of life improvements. Firstly, we have standardized how delete buttons work across the Data Studio with a new component. They have a nice little confirmation dialogue so you don't mistakenly delete things in future expecting it to be there when it wasn't always. The translation store is now available to app extension authors, which makes it easier to create multi language app extensions for the Data Studio. Directus now supports rendering many to any items in related value displays, for example, in layouts. And the send email operation now supports cc, bcc, and reply to fields, which like everything in Directus Automate can take in dynamic values. There's also been a whole bunch of little fixes which are rooted in consistency and standardization across Directus. You can check the full release notes in GitHub. You can download Directus eleven point four point one from Docker Hub and use these new additions in Directus Cloud right now. Thank you so much for joining me, and we'll see you next time. Hi. I'm Mike, and I'm from the developer experience team. I'm going to demonstrate each of the new extensions in Directus Labs this month. As a reminder, Directus Labs is our team's experimental org on GitHub, which regularly publishes new extensions. The first extension we'll go through is going to be the YouTube embed, which is listed here, and I've put it as video cast. If we open up the item, we can add URL, and then we'll have the actual interface itself. This is configured using the Google Console API and the channel specifically you want to look at. So I'm just going to randomly select something so I know it makes sense. Copy that. Add that there just for a little bit of context for myself. Look into YouTube and then look up sell. Copy the item. Remember to put this into the code of the content of from the WYSIWYG if necessary. And we are good. And that is the YouTube interface. The next extension we'll show is the AI researcher bundle. With this one, we can configure it with anthropic or open AI models. All you require is your API key to add to it. So in this case, I have my PhD thesis, and I want to ask something about quarks. What is a quark? It'll go away and ask the model and return the answer. And it does it in great depth. How many types are there? And since we've been asking separate questions and we want to keep the context of the conversation, the entire conversation is available in this pop up whilst still maintaining the main value. So we can close that and move on. And here we have our final extension of the day. This is the inline repeater. As you can see, it has elements that can be expanded and include multiple items as part of them. So here we have the service and the URL for a podcast. I'm gonna add a new one here. And we're gonna go to Spotify and just grab the URL. Add that in. And we'll we'll have multiple inline elements containing the different objects. We can add as many as we like as part of this repeater. Thanks. Alright. Thank you so much to Kevin and Mike for those updates. If you're planning on using anything that they've mentioned or if you've got any questions about it, do let us know here in the chat or on discord and we'll do our best to get you your answers. And next up we are very excited to have Lindsay talking about starters preview. So here is Lindsay. Hi. I'm Lindsay Zylstra. Today, I'm excited to introduce you to our new Next. Js simple CMS starter. This starter is designed to work with our simple CMS back end template. It gives you a powerful starting point to build off of or modify to meet your unique needs so you can style and customize quickly without getting bogged down with the setup. But what exactly is this starter? Essentially, it's a ready to use framework specific front end that connects seamlessly with our simple CMS back end to help you build full CMS web applications. With this starter, you benefit from a robust Directus integration that simplifies content management, along with Next. Js App Router that effortlessly handles dynamic routing and layouts. We've we've also incorporated Tailwind CSS and ShadCn components to offer rapid, utility first styling. Plus, it comes with built in examples that show you how to implement live previews, add customized blocks, set up dynamic form fields, all of this giving you a complete toolkit to kick start your projects. Let me switch over to my screen for a quick demo. First, I'm gonna show you our Directus integration in Versus Code. Here you can see we're using the Directus SDK along with a fetchers file here that gives a great example of how to retrieve data from Directus seamlessly. We've also included a command line tool to generate Directus types automatically, saving you the hassle of creating them manually. Now if we look over here at our direct Us pages content, you can see our starter pages build dynamically from direct Us data. This dynamic approach allows you to add blocks and construct your pages, and we've integrated site map generation and SEO in order to give you a head start on making your site more discoverable. We've left most of the styling up to you with only minimal opinionated design choices. However, we've included useful features like dark mode and the ability to set an accent color So you can easily tailor the look to match your brand. Moving on to dynamic forms. Our dynamic forms are designed to offer flexibility. You can quickly create forms with fields defined in Directus. For example, here's a contact us form. You can set customized success messages as well as change your submit labels and add in any type of field you'd like to see. We manage validations through Zod, schema validation. This setup makes it straightforward to add any additional validations you might need. Here's the contact us form on the page. Finally, we'll take a look at our global search. It provides a basic search right out of the box. You have the capability to expand it for deeper and more comprehensive searches as your project grows. Each of these features work together to create a flexible, developer friendly starter that empowers you to build full CMS powered web applications without unnecessary overhead. Our primary goal was to create a developer friendly experience that tackles common challenges head on. We focused on ensuring smooth live preview, flexible content management, and intuitive setup, whether you're running Directus in the cloud or locally. So how do you get access to this? You can find this code in our Directus Labs repo under starters. Dive right in by following our detailed setup instructions available in the ReadMe files. This will get you up and running with all the features we've discussed. As a sneak peek, we're also working on a command line tool that'll be out in beta soon. This will simplify the installation and configuration even further so you can focus on building and less on setup. That's our quick overview of our Next. Js simple CMS starter with Directus integration. I hope you enjoyed this walk through. In the coming months, we plan to release more of these starters for other popular frameworks as well as more that will pair with our other back end templates. So be sure to check back in. If you're interested in the command line tool or any of our future starters that will be released, make sure to subscribe to our newsletter. Thank you for watching. I can't wait to see what you build with this tool. Have a great day. Alright. Thank you so much to Lindsay, and we're hoping that you're as excited as we are to be able to share that. Before I move us on to the episode of Translation Station, I wanna highlight that we've got some, exciting tutorials coming your way to the new docs very soon. We have implementing direct us live preview and using direct us off, in Next, Nuxt, Astro, and SvelteKit, and they'll be available very soon. So do check back, if that sounds good to you. Next, we have episode one of translation station, which is a four part new directors TV series. It's exploring the opportunities of localization in directors. And as a series, it covers topics from translating the director's code base, translation strings, translating your content, and accessing your translated content via the director's APIs. Have I said translation one too many times? I think you get the idea that it's all about translations, so here is episode one. Carmen, developer, educator, and translation station. Translation station. Alright. Back to English. So here we've got a direct us project that I'm running for my blog. And, yeah, just one blog post for now. We'll be filling it up with time. I wanted to show you in the data studio the collections I've got. I've got some authors, I've got some languages, and I've got some posts. So, as you can see, my computer's default language is English. Now, my Directus project is also running by default in English. But what if I wanna make it available in another language? What I can do is go over to the settings module and then the settings page itself where amongst other settings, I'm gonna find the default language. I'm gonna open this up and show you just how many languages we have available here. And not just languages, but languages specific to a region. So, I'm gonna scroll down being Chilean myself, I'm gonna set my language to Latin American Spanish. Gina's there too, but I wanna encompass all of this continent. Click on that and I'm going to save my changes. You can see now that a bunch of things have changed already. I'm in the setting in the modules instead of settings and so on and so forth. What's really key to know here is the Directus project language has now changed. But what happens to the rest of our things? For example, in our data model, or modelo de depos, we're gonna see that the names of our collections haven't changed. Or, if we go to content or contenido as it is now, we're gonna see that my blog post stays the same, even the fields on the blog post themselves. But that's to be expected because our project is now in Spanish, but the content itself and the data model are still in English because that's how it was when I created it. But what I want to focus on here is the language of the Directus project itself. Let's go back to our settings module real quick. You might be wondering where do the translations for these come from and how can I change or contribute to them? Now given that Directus is open source under the BSL license, we actually get these translations as open source contributions. That's right. You can contribute your own translations to Directus. Next, I'm gonna show you how to do that. Over in the Directus documentation, under Resources, Community, and contribution translations, we're gonna find the instructions for how to contribute localizations to the Directus Data Studio, and this is done via platform called Crowdin. So over in Crowdin, we can see there's a bunch of translations that exist, which I love to see. We've got Catalan, Dutch, Finnish, Estonian, and a whole bunch of other languages. So, how can I contribute to a language, say, if I find a typo? First thing I need to do is log in to Crowded, which is via a free account. Now that I'm logged in, I can go ahead and look for my language and start contributing to it. For example, let's look for Spanish and specifically focus on that Spanish Latin America. Now on this page, I'm gonna click on translate all. This will give me a list of all of the strings or pieces of text that can be translated in Directus. Now here, under editing preset, I can see the string editor predefinido. But because this is in the current test we're currently editing, it should actually be editando predefinido. So what I'm gonna do is make that suggestion and save it. But because there's already an approved translation, what I'm gonna do is notify a proofreader. Cool. So now my suggestion is made to improve this Latin American Spanish translation of Directus. But what happens next? So our team over at Directus is going to get a pull request from Crowdin with the change that I've made. Now you might be thinking, well, why don't I just make the pull request if it's open source? Bear in mind that crowding's automatic synchronization is going to overwrite any changes that don't correspond with crowding itself. So it's best to make those translations directly through crowding. Now, the last question is, when am I gonna get that new string? It'll be merged into Directus itself and will be available in the next release. So, now we just gotta sit tight. Alright. Well, there you have it. How to configure your Directus project in another language, which strings are translated directly, and how to contribute your own translations to the greater Directus ecosystem. I hope this was helpful, but there's more stops on this translation station journey we're going on. We'll also be learning about translation strings, translating content in the data studio, and accessing translated content in your own application. So all aboard for the next station, and off we go. See you next time. We want to take a moment towards the end of the change log for thanking our amazing community contributors who give their time to improve the director's project. Since last month, there have been three contributors. A huge thank you to Nicholas for fixing conditional interface options incorrectly replacing choices, High Vibes only for adding support for CC, BCC, and reply to fields to the email operation, iodele for fixing a grammar error in the live preview documentation. Thank you again, and you can see their specific pull requests inside of the full release notes on GitHub. Lastly, we also want to take the time to thank our GitHub Sponsors of January who financially contribute to Directus' development. A huge thank you to Wei Fan, Entel, Jamiluddin, Fergus, Omar, Marcus, c k, Peter, Utomic, Steven, Robson, nonlinear, Andreas, Valentino, John, Wayne, Burb, Adam, Jason, Birka, Jens, Vincent, Mike, Khan, and Lassie. The money we are given from our GitHub Sponsors goes straight back to community members who build tooling and extensions for the director's ecosystem. For this month's reading list, we're going off script and sharing an audio special of our favorite tech and tech adjacent podcasts. Firstly, one that you've likely heard of, Syntax, a podcast about web development covering topics including full stack web development, the latest frameworks, databases, and lots more. There's over 800 episodes, so lots to check out. Next, we have Waveform, a weekly podcast aimed at gadget lovers covering trends and product reviews. Next is Darknet Diaries, which explores topics from the dark web like cybercrime, hacking, and other less than legal activities, as well as the people fighting back against them. Deep Questions with Cal Newport focuses on productivity and the struggle to work and live deeply in a world with lots of digital distractions. Decoder is a new show from The Verge that members of our team have been enjoying for its interviews with technology leaders on their take on technology and the future of it all. The DevTools podcast does what it says on the tin. It's a podcast about developer tools and the people who make them, with lots of interesting guests from companies you're probably using. Hiking humans looks behind the social engineering scams, phishing schemes, and criminal exploits that are making headlines and taking a heavy toll on organizations around the world. What the Shell, aside from having an exceptional name, provides an accessible look into major hacks and vulnerabilities through looking at incidents and news. And lastly, but in no means least, open observability talks discuss harnessing the power of open source to advance observability initiatives with guests that range from open source project creators, end users, and thought leaders. We hope you enjoy checking out some of the podcasts. If you've got your own recommendations, do share them with us. We'd love to hear what you're listening to. Alright and that is it for February's the changelog. If you have any questions about anything you've seen and whether you're joining us live or, watching on demand do get over to discord and we'll be able to help help you out over there. Do check out Director TV. We are always adding new shows. You should be able to find translation station. It will be right at the top, so you shouldn't be able to miss it. As always, if you've got any feedback about things you'd like to see within the changelog, very searchable feedback. We'd love to hear your thoughts. Thank you so much if you have made it this far for sticking with us and spending your time with us. We really appreciate it. And please head over to Discord for the next info on the change log for March. Have a great day, everyone, and I'll chat with you soon. Bye.","dec97d4f-8fd6-431d-b8bb-96f80bb292c1",[1241,1242,1243],"aef5c5fc-6394-4c0f-8633-89193d5d1e63","32d28229-73f2-4ffe-939c-c28f99b68ca6","89bb5a15-8430-4acc-85c6-6ccb20c33349",[],{"year":399,"number":687,"id":688,"episodes":1246,"show":1247},[690,691,692,693,694,695,696,669],{"title":297,"slug":298},{"id":1249,"slug":1250,"vimeo_id":1251,"description":1252,"tile":1253,"length":11,"resources":12,"people":12,"episode_number":264,"published":1254,"title":1255,"video_transcript_html":1256,"video_transcript_text":1257,"content":12,"seo":1258,"status":19,"episode_people":1259,"recommendations":1261,"season":1262},"0e2eaf09-d3e4-4cca-97c8-e38e833c731f","content-translations-in-directus-data-studio","1055321748","Directus allows you to set up your content to be translated into a bevy of languages. In this episode, Carmen will show you how to activate and use content translations.","fc130184-64bb-481a-bef3-d628cf84bf6d","2025-02-11","Content Translations in Directus: Data Studio","\u003Cp>Speaker 0: Translation station. Alright. Back to English. So here we are in our data studio working on our blog application. And as you can see, we have a collection of posts.\u003C\u002Fp>\u003Cp>So let's take a look at the data model for our posts. By going over to the settings module and over to data model, we can see we have a collection called posts. And now we can see a number of fields here, including ID, the slug, which helps us construct a URL so that we can access the blog post on our application, the title of the blog post, the content of the blog post, as well as the image, publishing date, the author, and whether the blog post is published or not. So that's our data model. Let's go back to our content and take a look at the sample post I've prepared here, the ultimate guide to rabbits.\u003C\u002Fp>\u003Cp>And we can see the content is already filled in and in English. But our author, Gerance, also speaks French. So what we're going to be doing in this episode is enabling folks to be able to write their content in several languages. This is going to be achieved by going back to the settings module and adding a new field to our posts collection. So I'm going to open up our post collection and click on create field.\u003C\u002Fp>\u003Cp>Now Directus has a plethora of fields that we can add and even one tailored specifically for content translations. By scrolling down over to the relational section, we're going to find the translations field, which is exactly what we need. So I'm going to click on that. And I'm going to walk us through a couple of things that are going to happen. First of all, the key is filled in for us automatically called translations.\u003C\u002Fp>\u003Cp>The languages collection is also filled in for us. Now you may think, well, the languages collection doesn't exist yet, but that's fine because what's going to happen is that it is going to be created automatically. We'll see that in just a moment. The last part I want to fill in is the default language of our translations. And you saw already, I had pre written this blog post in English.\u003C\u002Fp>\u003Cp>So the default language for our post is going to be, is going to use the language code, which is en US. Now I'm going to go ahead and save. And a number of things just happened. The first thing you'll notice is that we now have our field of translations, which is great. But what else changed?\u003C\u002Fp>\u003Cp>Let's have a look. I'm going to go back to my data model. You're going to see a couple of new collections. First off is languages. And this is the collection that I mentioned earlier would be automatically created for us.\u003C\u002Fp>\u003Cp>So I'm going to open the languages collection. We're going to see it has three fields, the code, which is that en US, the name, which is English United States, and the direction, which in this case is going to be whether you read from left to right, top to bottom, right to left, and so on and so forth. But there's one more collection that got created and is also invisible. That is, you won't be able to see it when adding content. And this is the post's translations pivot table.\u003C\u002Fp>\u003Cp>Now a pivot table is used to connect a post to a language because many posts can be written in many languages. So let's open it up real quick. If I go over to post translations, we're going to see we have three fields here. The ID of the post translation itself, the ID of the post, and the code of the language. That is the identifier, in this case, EMUS, of the language.\u003C\u002Fp>\u003Cp>So does that mean we can go ahead and start translating our content? Well, let's hop over to our content module, open up our ultimate guide to rabbits, and you're going to see that by scrolling down, we do have the option of adding translations, but it says empty form. And the reason for this is because, yes, we do have translations, but we have no fields that we can translate. So how do we fix that? Well, let's go back to our settings module and open up that post translations collection.\u003C\u002Fp>\u003Cp>Now I'm going to click on create field and add two fields. First is going to be the title. Go ahead and create that. And now the content, which is going to be a WYSIWYG or what you see is what you get field. Alrighty.\u003C\u002Fp>\u003Cp>I'm going to click on save. Now, it might not seem like I changed a lot, but check this out. Soon as I go back to my content module and open up my guide once again, I'm going to scroll down real quick. Now we have translations. So we can already start by filling in our title and content.\u003C\u002Fp>\u003Cp>So I'm going to go ahead and do that from our English content. Perfect. And now we can add translations for our content. So given Guerance speaks French, she has written this article in French. So I'm going to look up French, and I'm going to add in that content.\u003C\u002Fp>\u003Cp>Here we go. There's our title. And now our content. Wonderful. Once this is done, I can go ahead and save my item.\u003C\u002Fp>\u003Cp>And now I have a translated blog post. If I take a look, French is there. Wonderful. So this is how I can translate my content using Directus. In another episode of the show, we're going to see how we can access these translations in our applications using the Directus SDK or REST API or GraphQL API.\u003C\u002Fp>\u003Cp>Alright. Well, there you have it. How to translate your content in the Data Studio using Directus. But that's not the only stop on our translation station journey. If you wanna know how to translate Directus itself, how to use translation strings, or how to access your translated content using our APIs and SDK, well, tune in for other episodes of Translation Station.\u003C\u002Fp>\u003Cp>So all aboard, and I'll see you next time.\u003C\u002Fp>","Translation station. Alright. Back to English. So here we are in our data studio working on our blog application. And as you can see, we have a collection of posts. So let's take a look at the data model for our posts. By going over to the settings module and over to data model, we can see we have a collection called posts. And now we can see a number of fields here, including ID, the slug, which helps us construct a URL so that we can access the blog post on our application, the title of the blog post, the content of the blog post, as well as the image, publishing date, the author, and whether the blog post is published or not. So that's our data model. Let's go back to our content and take a look at the sample post I've prepared here, the ultimate guide to rabbits. And we can see the content is already filled in and in English. But our author, Gerance, also speaks French. So what we're going to be doing in this episode is enabling folks to be able to write their content in several languages. This is going to be achieved by going back to the settings module and adding a new field to our posts collection. So I'm going to open up our post collection and click on create field. Now Directus has a plethora of fields that we can add and even one tailored specifically for content translations. By scrolling down over to the relational section, we're going to find the translations field, which is exactly what we need. So I'm going to click on that. And I'm going to walk us through a couple of things that are going to happen. First of all, the key is filled in for us automatically called translations. The languages collection is also filled in for us. Now you may think, well, the languages collection doesn't exist yet, but that's fine because what's going to happen is that it is going to be created automatically. We'll see that in just a moment. The last part I want to fill in is the default language of our translations. And you saw already, I had pre written this blog post in English. So the default language for our post is going to be, is going to use the language code, which is en US. Now I'm going to go ahead and save. And a number of things just happened. The first thing you'll notice is that we now have our field of translations, which is great. But what else changed? Let's have a look. I'm going to go back to my data model. You're going to see a couple of new collections. First off is languages. And this is the collection that I mentioned earlier would be automatically created for us. So I'm going to open the languages collection. We're going to see it has three fields, the code, which is that en US, the name, which is English United States, and the direction, which in this case is going to be whether you read from left to right, top to bottom, right to left, and so on and so forth. But there's one more collection that got created and is also invisible. That is, you won't be able to see it when adding content. And this is the post's translations pivot table. Now a pivot table is used to connect a post to a language because many posts can be written in many languages. So let's open it up real quick. If I go over to post translations, we're going to see we have three fields here. The ID of the post translation itself, the ID of the post, and the code of the language. That is the identifier, in this case, EMUS, of the language. So does that mean we can go ahead and start translating our content? Well, let's hop over to our content module, open up our ultimate guide to rabbits, and you're going to see that by scrolling down, we do have the option of adding translations, but it says empty form. And the reason for this is because, yes, we do have translations, but we have no fields that we can translate. So how do we fix that? Well, let's go back to our settings module and open up that post translations collection. Now I'm going to click on create field and add two fields. First is going to be the title. Go ahead and create that. And now the content, which is going to be a WYSIWYG or what you see is what you get field. Alrighty. I'm going to click on save. Now, it might not seem like I changed a lot, but check this out. Soon as I go back to my content module and open up my guide once again, I'm going to scroll down real quick. Now we have translations. So we can already start by filling in our title and content. So I'm going to go ahead and do that from our English content. Perfect. And now we can add translations for our content. So given Guerance speaks French, she has written this article in French. So I'm going to look up French, and I'm going to add in that content. Here we go. There's our title. And now our content. Wonderful. Once this is done, I can go ahead and save my item. And now I have a translated blog post. If I take a look, French is there. Wonderful. So this is how I can translate my content using Directus. In another episode of the show, we're going to see how we can access these translations in our applications using the Directus SDK or REST API or GraphQL API. Alright. Well, there you have it. How to translate your content in the Data Studio using Directus. But that's not the only stop on our translation station journey. If you wanna know how to translate Directus itself, how to use translation strings, or how to access your translated content using our APIs and SDK, well, tune in for other episodes of Translation Station. So all aboard, and I'll see you next time.","4e93b898-70f8-4cc4-8275-fa038baa4eba",[1260],"f956a739-7124-4186-923f-4141a87e5f57",[],{"year":399,"number":247,"id":1263,"episodes":1264,"show":1268},"d516a222-02bd-4ee7-bad2-2b1cc31650e7",[1265,1266,1249,1267],"0ecac717-cbf2-4dbd-9d2b-aec232c10b0a","b588e6c5-d031-4be6-aef4-e4f421f10cd5","e82dc6ab-1cab-4b37-af9f-9c1dcb4c075b",{"title":1269,"slug":1270},"Translation Station","translation-station",{"id":1265,"slug":1272,"vimeo_id":1273,"description":1274,"tile":1275,"length":129,"resources":12,"people":12,"episode_number":13,"published":1254,"title":1276,"video_transcript_html":1277,"video_transcript_text":1278,"content":12,"seo":1279,"status":19,"episode_people":1280,"recommendations":1282,"season":1283},"data-studio-translations-in-directus","1054965103","Directus comes in several languages translated kindly by our community. In this episode, Carmen will show you how to contribute your own translations using Crowdin.","63af0d22-a137-4add-973b-ffe428bd0ae5","Data Studio Translations in Directus","\u003Cp>Speaker 0: Carmen is so developer, educator, and direct translation station. Is translation station. Alright. Back to English. So here we've got a direct us project that I'm running for my blog.\u003C\u002Fp>\n\u003Cp>And, yeah, just one blog post for now. We'll be filling it up with time. But I wanted to show you in the data studio the collections I've got. I've got some authors, I've got some languages, and I've got some posts. So, as you can see, my computer's default language is English.\u003C\u002Fp>\n\u003Cp>Now, my Directus project is also running by default in English. But what if I wanna make it available in another language? What I can do is go over to the settings module and then the settings page itself, where amongst other settings, I'm gonna find the default language. I'm gonna open this up and show you just how many languages we have available here and not just languages, but languages specific to a region. So I'm going to scroll down being Chilean myself.\u003C\u002Fp>\n\u003Cp>I'm going to set my language to Latin American Spanish. Gina's there too, but I wanna encompass all of this continent. Click on that and I'm going to save my changes. You can see now that a bunch of things have changed already. I'm in the setting in the modules instead of settings and so on and so forth.\u003C\u002Fp>\n\u003Cp>What's really key to know here is the Directus project language has now changed. But what happens to the rest of our things? For example, in our data model or modelo de datos, we're gonna see that the names of our collections haven't changed. Or if we go to content or as it is now, we're gonna see that my blog post stays the same. Even the fields on the blog post themselves.\u003C\u002Fp>\n\u003Cp>But that's to be expected because our project is now in Spanish, but the content itself and the data model are still in English because that's how it was when I created it. But what I want to focus on here is the language of the Directus project itself. Let's go back to our settings module real quick. You might be wondering where do the translations for these come from and how can I change or contribute to them? Now given that Directus is open source under the BSL license, we actually get these translations as open source contributions.\u003C\u002Fp>\n\u003Cp>That's right. You can contribute your own translations to Directus. Next, I'm gonna show you how to do that. Over in the Directus documentation, under resources, community, and contribution translations, we're gonna find the instructions for how to contribute localizations to the Directus Data Studio. And this is done by a platform called Crowdin.\u003C\u002Fp>\n\u003Cp>So over in Crowdin, we can see there's a bunch of translations that exist, which I love to see. Got Catalan, Dutch, Finnish, Estonian, and a whole bunch of other languages. So how can I contribute to a language, say, if I find a typo? First thing I need to do is log into Crowdin, which is via free account. Now that I'm logged in, I can go ahead and look for my language and start contributing to it.\u003C\u002Fp>\n\u003Cp>For example, let's look for Spanish and specifically focus on that Spanish Latin America. Now on this page, I'm gonna click on translate all. This will give me a list of all of the strings or pieces of text that can be translated in Directus. Now here, under editing preset, I can see the string editor predefinido, but because this is in the current test we're currently editing, it should actually be editando predefinido. So what I'm gonna do is make that suggestion and save it.\u003C\u002Fp>\n\u003Cp>But because there's already an approved translation, what I'm gonna do is notify a proofreader. Cool. So now my suggestion is made to improve this Latin American Spanish translation of Directus, but what happens next? So our team over at Directus is going to get a pull request from Croudon with the change that I've made. Now you might be thinking, well, why don't I just make the pull request if it's open source?\u003C\u002Fp>\n\u003Cp>Bear in mind that crowding's automatic synchronization is going to overwrite any changes that don't correspond with crowding itself. So it's best to make those translations directly through crowding. Now, the last question is, when am I gonna get that new string? It'll be merged into Directus itself and will be available in the next release. So now we just gotta sit tight.\u003C\u002Fp>\n\u003Cp>Alright. Well, there you have it. How to configure your Directus project in another language, which strings are translated directly, and how to contribute your own translations to the greater Directus ecosystem. I hope this was helpful. But there's more stops on this Translation Station journey we're going on.\u003C\u002Fp>\n\u003Cp>We'll also be learning about translation strings, translating content in the data studio, and accessing translated content in your own application. So all aboard for the next station, and off we go. See you next time.\u003C\u002Fp>","Carmen is so developer, educator, and direct translation station. Is translation station. Alright. Back to English. So here we've got a direct us project that I'm running for my blog. And, yeah, just one blog post for now. We'll be filling it up with time. But I wanted to show you in the data studio the collections I've got. I've got some authors, I've got some languages, and I've got some posts. So, as you can see, my computer's default language is English. Now, my Directus project is also running by default in English. But what if I wanna make it available in another language? What I can do is go over to the settings module and then the settings page itself, where amongst other settings, I'm gonna find the default language. I'm gonna open this up and show you just how many languages we have available here and not just languages, but languages specific to a region. So I'm going to scroll down being Chilean myself. I'm going to set my language to Latin American Spanish. Gina's there too, but I wanna encompass all of this continent. Click on that and I'm going to save my changes. You can see now that a bunch of things have changed already. I'm in the setting in the modules instead of settings and so on and so forth. What's really key to know here is the Directus project language has now changed. But what happens to the rest of our things? For example, in our data model or modelo de datos, we're gonna see that the names of our collections haven't changed. Or if we go to content or as it is now, we're gonna see that my blog post stays the same. Even the fields on the blog post themselves. But that's to be expected because our project is now in Spanish, but the content itself and the data model are still in English because that's how it was when I created it. But what I want to focus on here is the language of the Directus project itself. Let's go back to our settings module real quick. You might be wondering where do the translations for these come from and how can I change or contribute to them? Now given that Directus is open source under the BSL license, we actually get these translations as open source contributions. That's right. You can contribute your own translations to Directus. Next, I'm gonna show you how to do that. Over in the Directus documentation, under resources, community, and contribution translations, we're gonna find the instructions for how to contribute localizations to the Directus Data Studio. And this is done by a platform called Crowdin. So over in Crowdin, we can see there's a bunch of translations that exist, which I love to see. Got Catalan, Dutch, Finnish, Estonian, and a whole bunch of other languages. So how can I contribute to a language, say, if I find a typo? First thing I need to do is log into Crowdin, which is via free account. Now that I'm logged in, I can go ahead and look for my language and start contributing to it. For example, let's look for Spanish and specifically focus on that Spanish Latin America. Now on this page, I'm gonna click on translate all. This will give me a list of all of the strings or pieces of text that can be translated in Directus. Now here, under editing preset, I can see the string editor predefinido, but because this is in the current test we're currently editing, it should actually be editando predefinido. So what I'm gonna do is make that suggestion and save it. But because there's already an approved translation, what I'm gonna do is notify a proofreader. Cool. So now my suggestion is made to improve this Latin American Spanish translation of Directus, but what happens next? So our team over at Directus is going to get a pull request from Croudon with the change that I've made. Now you might be thinking, well, why don't I just make the pull request if it's open source? Bear in mind that crowding's automatic synchronization is going to overwrite any changes that don't correspond with crowding itself. So it's best to make those translations directly through crowding. Now, the last question is, when am I gonna get that new string? It'll be merged into Directus itself and will be available in the next release. So now we just gotta sit tight. Alright. Well, there you have it. How to configure your Directus project in another language, which strings are translated directly, and how to contribute your own translations to the greater Directus ecosystem. I hope this was helpful. But there's more stops on this Translation Station journey we're going on. We'll also be learning about translation strings, translating content in the data studio, and accessing translated content in your own application. So all aboard for the next station, and off we go. See you next time.","0b334619-c239-44e3-8b32-89dca63264d0",[1281],"b3f9d8ad-c52b-4a7a-afe9-a29cfdf4782a",[],{"year":399,"number":247,"id":1263,"episodes":1284,"show":1285},[1265,1266,1249,1267],{"title":1269,"slug":1270},{"id":1267,"slug":1287,"vimeo_id":1288,"description":1289,"tile":1290,"length":11,"resources":12,"people":12,"episode_number":159,"published":1254,"title":1291,"video_transcript_html":1292,"video_transcript_text":1293,"content":12,"seo":1294,"status":19,"episode_people":1295,"recommendations":1297,"season":1298},"content-translations-in-directus-sdk","1055321900","You can use your Directus project’s API to access your translated content. In this episode, Carmen will show you how to build an application to display translated content using the Directus SDK, REST and GraphQL APIs.","d7485f2c-f7ae-4d7d-aa36-a071c084eeda","Content Translations in Directus: SDK","\u003Cp>Speaker 0: Carmen. Developer educator. Translation station. So here we've got a Directus project dedicated for storing my blog posts. Now, amongst the collections, we can see posts.\u003C\u002Fp>\u003Cp>And I'd like to take a minute to take a look at the structure or the data model of these posts. Gonna hop over to the settings module and take a look at posts itself. So we can see a variety of fields here. And the ones I want to highlight are slug, which will help us build a URL to find our post in our application. Next, we've got the title and the content.\u003C\u002Fp>\u003Cp>Another field I want to highlight is translations. Now I've gone ahead and created some translations for our blog posts. If you want to find out how to translate your content in the data studio, check out our data studio content translations episode of Translation Station. Back in our data model, I also want to take a look at post translations, which is a hidden field that holds the translation data for each of our posts. You can see we've got the title and the content.\u003C\u002Fp>\u003Cp>Let's now take a look at the post itself. We've got the ultimate guide to rabbits, which is written in both English. Scroll down here. As well as French. So I'm going to show you how you can build an application to retrieve translations of your blog posts.\u003C\u002Fp>\u003Cp>So what we have here is an application written in Nuxt, which uses three ways of accessing our translated blog posts. One is using the Directus SDK. One is using the REST API of our Directus project. And the last is using the GraphQL endpoint of our Directus project. I'm gonna give you a short tour of this Nuxt application.\u003C\u002Fp>\u003Cp>First, we set up our environment, which will connect to our Directus project and proxy accordingly to go to local host 3,000 slash Directus so that we can access our data. So let's start with the SDK. I've set up a Nuxt plug in here, which instantiates a direct Us client and makes it available to my Nuxt application. By going to SDK slash the language code of the content slash the slug of the blog post, we're going to retrieve and display the translated data for our blog post. Let's walk through this real quick.\u003C\u002Fp>\u003Cp>First, we instantiate our client and set up the retrieval of the parameters and a reference for the post itself. Then we asynchronously use the direct client to read several posts. Using the deep and filter parameters, we're going to grab the blog post that corresponds to the slug given in our URL, as well as the translation itself for that locale. We then make available the data of the translations into the result and limit to one just for good measure. Once we have that, we grab the translation itself from that payload and display it accordingly.\u003C\u002Fp>\u003Cp>Let's take a look at it in action. So here we are. We've gone to local host 3,000 in my running Nuxt application slash SDK slash enus, that's for English, slash the ultimate guide to rabbits. And that gives us my blog post. To view it in French, I just have to change my URL to frfr.\u003C\u002Fp>\u003Cp>Check it out. Now it's in French. Fantastic. Let's see how it's done with REST now. All right.\u003C\u002Fp>\u003Cp>So we won't be using our SDK client, and instead, we'll be using the fetch API in order to retrieve the post that match the slug as well as the locale itself. And then retrieving as fields the translation title, content, and the language code, limited to one for good measure. Then we retrieve the translation with the correct language code from our locale. And then just like before, if there's a post, we display its content. Let's see it in action.\u003C\u002Fp>\u003Cp>Back in my browser, I'm going to go to rest slash en dash us slash the ultimate guide to rabbits. Access that, and there's our English blog post. And for French, fr dash fr. And there we go. All good and in French.\u003C\u002Fp>\u003Cp>One more. Let's look at GraphQL. So just like with the REST API, we'll be using fetch grab the GraphQL data. First, we'll build our query, which will filter out the posts by slug and specifically filter out the translations by the language code alongside that translation's title and content. Then we make a request to the GraphQL endpoint of our Directus project and then set that post value to have that translation data.\u003C\u002Fp>\u003Cp>Then just like before, if we have a post, we go ahead and display it. Let's have a look. All right. Back in our browser, let's go to graphql en us the ultimate guide to rabbits. And there we go.\u003C\u002Fp>\u003Cp>There it is in English. Now let's try it in French. Wonderful. So that's the three ways we can access our translated data from our Directus projects API. Alright.\u003C\u002Fp>\u003Cp>Well, there you have it. How to access your translated content from Directus using our APIs and SDKs. But that's not the only stop on this translation station journey we're on. If you wanna learn how to translate Directus directly or where it gets translated, how to use translation strings, or how to translate your content, check out the other episodes of Translation Station. So I hope this was helpful, and I'll see you on the next stop of Translation Station.\u003C\u002Fp>\u003Cp>Bye for now.\u003C\u002Fp>","Carmen. Developer educator. Translation station. So here we've got a Directus project dedicated for storing my blog posts. Now, amongst the collections, we can see posts. And I'd like to take a minute to take a look at the structure or the data model of these posts. Gonna hop over to the settings module and take a look at posts itself. So we can see a variety of fields here. And the ones I want to highlight are slug, which will help us build a URL to find our post in our application. Next, we've got the title and the content. Another field I want to highlight is translations. Now I've gone ahead and created some translations for our blog posts. If you want to find out how to translate your content in the data studio, check out our data studio content translations episode of Translation Station. Back in our data model, I also want to take a look at post translations, which is a hidden field that holds the translation data for each of our posts. You can see we've got the title and the content. Let's now take a look at the post itself. We've got the ultimate guide to rabbits, which is written in both English. Scroll down here. As well as French. So I'm going to show you how you can build an application to retrieve translations of your blog posts. So what we have here is an application written in Nuxt, which uses three ways of accessing our translated blog posts. One is using the Directus SDK. One is using the REST API of our Directus project. And the last is using the GraphQL endpoint of our Directus project. I'm gonna give you a short tour of this Nuxt application. First, we set up our environment, which will connect to our Directus project and proxy accordingly to go to local host 3,000 slash Directus so that we can access our data. So let's start with the SDK. I've set up a Nuxt plug in here, which instantiates a direct Us client and makes it available to my Nuxt application. By going to SDK slash the language code of the content slash the slug of the blog post, we're going to retrieve and display the translated data for our blog post. Let's walk through this real quick. First, we instantiate our client and set up the retrieval of the parameters and a reference for the post itself. Then we asynchronously use the direct client to read several posts. Using the deep and filter parameters, we're going to grab the blog post that corresponds to the slug given in our URL, as well as the translation itself for that locale. We then make available the data of the translations into the result and limit to one just for good measure. Once we have that, we grab the translation itself from that payload and display it accordingly. Let's take a look at it in action. So here we are. We've gone to local host 3,000 in my running Nuxt application slash SDK slash enus, that's for English, slash the ultimate guide to rabbits. And that gives us my blog post. To view it in French, I just have to change my URL to frfr. Check it out. Now it's in French. Fantastic. Let's see how it's done with REST now. All right. So we won't be using our SDK client, and instead, we'll be using the fetch API in order to retrieve the post that match the slug as well as the locale itself. And then retrieving as fields the translation title, content, and the language code, limited to one for good measure. Then we retrieve the translation with the correct language code from our locale. And then just like before, if there's a post, we display its content. Let's see it in action. Back in my browser, I'm going to go to rest slash en dash us slash the ultimate guide to rabbits. Access that, and there's our English blog post. And for French, fr dash fr. And there we go. All good and in French. One more. Let's look at GraphQL. So just like with the REST API, we'll be using fetch grab the GraphQL data. First, we'll build our query, which will filter out the posts by slug and specifically filter out the translations by the language code alongside that translation's title and content. Then we make a request to the GraphQL endpoint of our Directus project and then set that post value to have that translation data. Then just like before, if we have a post, we go ahead and display it. Let's have a look. All right. Back in our browser, let's go to graphql en us the ultimate guide to rabbits. And there we go. There it is in English. Now let's try it in French. Wonderful. So that's the three ways we can access our translated data from our Directus projects API. Alright. Well, there you have it. How to access your translated content from Directus using our APIs and SDKs. But that's not the only stop on this translation station journey we're on. If you wanna learn how to translate Directus directly or where it gets translated, how to use translation strings, or how to translate your content, check out the other episodes of Translation Station. So I hope this was helpful, and I'll see you on the next stop of Translation Station. Bye for now.","2627e703-7919-4414-b5a8-981a3fd300a6",[1296],"e9f4c135-99d8-40ef-a868-8507cc8b064b",[],{"year":399,"number":247,"id":1263,"episodes":1299,"show":1300},[1265,1266,1249,1267],{"title":1269,"slug":1270},{"id":1266,"slug":1302,"vimeo_id":1303,"description":1304,"tile":1305,"length":129,"resources":12,"people":12,"episode_number":177,"published":1254,"title":1306,"video_transcript_html":1307,"video_transcript_text":1308,"content":12,"seo":1309,"status":19,"episode_people":1310,"recommendations":1312,"season":1313},"string-translations-in-directus","1055321591","Did you know you can individually translate labels, notes, and other aspects of your Directus project? In this episode, Carmen will show you how to use Directus’ translation strings.","b4569fb1-a314-4563-bb9c-a0f5069565a5","String Translations in Directus","\u003Cp>Speaker 0: Hey, everybody. My name is Carmen, and I'm a Developer Educator at Directus. Now, don't worry. Your TV settings aren't wrong. Those are indeed Spanish subtitles you see under me.\u003C\u002Fp>\u003Cp>But why Spanish? Well, because this is Translation Station, the show all about localization and translations using Directus. In this episode, I'll be showing you how to use translation strings, multilingual key value pairs that you can use throughout your Directus project for things like drop downs, placeholder text, field notes, system collections, and more. So get ready, everybody, because next up is Translation Station. Alright.\u003C\u002Fp>\u003Cp>Here we are in my Directus project, which is dedicated to my blog. You can see we have a collection here called posts, which is the one we're going to be working with during this episode. Let's go ahead and take a look at its data model. I go to settings and then click on posts. We're going to see we have, amongst other fields, an ID, a slug for accessing our blog with an application, the title of the blog post, the content of the blog post, as well as author, publishing date, the image, and whether it's published or not.\u003C\u002Fp>\u003Cp>Now, one thing I'd like to do is add a field to specify the language that our blog post is written in. So I'm gonna go ahead and click on create field and then look for the dropdown. So we're gonna call this language. It's gotta be a string. And we need to add a few choices for this.\u003C\u002Fp>\u003Cp>So I'm gonna go ahead and click on create new. First one is going to be English. The value will be en west. And I'm gonna go ahead and do the same for the remaining languages. German.\u003C\u002Fp>\u003Cp>And finally, French. Perfect. Let's go ahead and save that field. Now we can go into our content and specify the language of our blog post. So given this post is written in English, I'm going to set the language here to English.\u003C\u002Fp>\u003Cp>So this is great. But what happens if I set my DirectUs project to another language? What if, for example, I set it to French? Looks good. But notice what happens when I look at the language dropdown itself.\u003C\u002Fp>\u003Cp>Everything else is perfectly in French, but the language dropdown is still in English. The way we can achieve having these different values in different languages is using translation strings, which is what we're going to be adding next. So back in our settings model, we've got hold on. Let me set this back to English for a sec. Lovely.\u003C\u002Fp>\u003Cp>Back in our settings module, there's a tab here called Translations. And you can see I've been hard at work creating translation strings for us. So for German, for example, we've got English, Spanish, Deutsch, We've got English, German, Spanish, and French for English. We've got English, Aleman, Francis, and Espanol for Spanish, and English, Francais, Espanol. Hold on.\u003C\u002Fp>\u003Cp>We still need German. Let's go ahead and add that. Then click here on the plus button. So the key needs to match the same key for every language. So English is going to be English across all languages.\u003C\u002Fp>\u003Cp>In this case, we want to specify German. We want to specify the language to be French. And the value is going to be the name of German in French, which in this case is Aleman. Go ahead and save that. Fantastic.\u003C\u002Fp>\u003Cp>Now we've got all of our translation strings ready. So now we just need to integrate them into our data model. This can be achieved by going back to the data model, going back to posts, going back to our language dropdown. And then for each of these, check it out. This translation button here allows us to specify a translation string.\u003C\u002Fp>\u003Cp>So now I can go ahead and set these to be the translation strings. And when I change my language, it's going to display the correct language name. So that's all saved. What happens now? Let's go look at my data model again.\u003C\u002Fp>\u003Cp>So we can still see English, Spanish, German, and French, which is great. But let's now change the language of our directus instance to be French. And now let's see what happens. So directus is in French. And if I go over to our guide once again, scroll down, and now we can see the correct language is displayed.\u003C\u002Fp>\u003Cp>Now you might be wondering hold on. Put this back in English one more time. There we go. Cool. Now you might be wondering, where else can I use these translation strings?\u003C\u002Fp>\u003Cp>Well, really this can be used anywhere where you saw that translation button. For example, the notes, a placeholder, and a variety of other ways to do this. For example, if you wanted to have displayed different names for your system collections, you can add a custom dropdown field for that translation key. Just an idea. But all in all, that's how you use translation strings to integrate multiple languages into your Directus project.\u003C\u002Fp>\u003Cp>Alright. Well, there you have it. As you can see, we can use translation strings in a multitude of ways to make it more accessible for folks speaking other languages to work on our Directus project. But that's not the only stop on our journey at Translation Station. If you wanna know how to contribute to Directus' localization itself, how to translate your content in the data studio, or how to access your translated content from our SDK or other APIs, check out the other episodes.\u003C\u002Fp>\u003Cp>So get ready everybody because next stop after this is Translation Station. See you soon.\u003C\u002Fp>","Hey, everybody. My name is Carmen, and I'm a Developer Educator at Directus. Now, don't worry. Your TV settings aren't wrong. Those are indeed Spanish subtitles you see under me. But why Spanish? Well, because this is Translation Station, the show all about localization and translations using Directus. In this episode, I'll be showing you how to use translation strings, multilingual key value pairs that you can use throughout your Directus project for things like drop downs, placeholder text, field notes, system collections, and more. So get ready, everybody, because next up is Translation Station. Alright. Here we are in my Directus project, which is dedicated to my blog. You can see we have a collection here called posts, which is the one we're going to be working with during this episode. Let's go ahead and take a look at its data model. I go to settings and then click on posts. We're going to see we have, amongst other fields, an ID, a slug for accessing our blog with an application, the title of the blog post, the content of the blog post, as well as author, publishing date, the image, and whether it's published or not. Now, one thing I'd like to do is add a field to specify the language that our blog post is written in. So I'm gonna go ahead and click on create field and then look for the dropdown. So we're gonna call this language. It's gotta be a string. And we need to add a few choices for this. So I'm gonna go ahead and click on create new. First one is going to be English. The value will be en west. And I'm gonna go ahead and do the same for the remaining languages. German. And finally, French. Perfect. Let's go ahead and save that field. Now we can go into our content and specify the language of our blog post. So given this post is written in English, I'm going to set the language here to English. So this is great. But what happens if I set my DirectUs project to another language? What if, for example, I set it to French? Looks good. But notice what happens when I look at the language dropdown itself. Everything else is perfectly in French, but the language dropdown is still in English. The way we can achieve having these different values in different languages is using translation strings, which is what we're going to be adding next. So back in our settings model, we've got hold on. Let me set this back to English for a sec. Lovely. Back in our settings module, there's a tab here called Translations. And you can see I've been hard at work creating translation strings for us. So for German, for example, we've got English, Spanish, Deutsch, We've got English, German, Spanish, and French for English. We've got English, Aleman, Francis, and Espanol for Spanish, and English, Francais, Espanol. Hold on. We still need German. Let's go ahead and add that. Then click here on the plus button. So the key needs to match the same key for every language. So English is going to be English across all languages. In this case, we want to specify German. We want to specify the language to be French. And the value is going to be the name of German in French, which in this case is Aleman. Go ahead and save that. Fantastic. Now we've got all of our translation strings ready. So now we just need to integrate them into our data model. This can be achieved by going back to the data model, going back to posts, going back to our language dropdown. And then for each of these, check it out. This translation button here allows us to specify a translation string. So now I can go ahead and set these to be the translation strings. And when I change my language, it's going to display the correct language name. So that's all saved. What happens now? Let's go look at my data model again. So we can still see English, Spanish, German, and French, which is great. But let's now change the language of our directus instance to be French. And now let's see what happens. So directus is in French. And if I go over to our guide once again, scroll down, and now we can see the correct language is displayed. Now you might be wondering hold on. Put this back in English one more time. There we go. Cool. Now you might be wondering, where else can I use these translation strings? Well, really this can be used anywhere where you saw that translation button. For example, the notes, a placeholder, and a variety of other ways to do this. For example, if you wanted to have displayed different names for your system collections, you can add a custom dropdown field for that translation key. Just an idea. But all in all, that's how you use translation strings to integrate multiple languages into your Directus project. Alright. Well, there you have it. As you can see, we can use translation strings in a multitude of ways to make it more accessible for folks speaking other languages to work on our Directus project. But that's not the only stop on our journey at Translation Station. If you wanna know how to contribute to Directus' localization itself, how to translate your content in the data studio, or how to access your translated content from our SDK or other APIs, check out the other episodes. So get ready everybody because next stop after this is Translation Station. See you soon.","3d40d358-4e62-409c-8c0b-8e052eab752a",[1311],"78f472a9-e478-4ec7-8ef6-cb6fb949582b",[],{"year":399,"number":247,"id":1263,"episodes":1314,"show":1315},[1265,1266,1249,1267],{"title":1269,"slug":1270},{"id":690,"slug":1317,"vimeo_id":1318,"description":1319,"tile":1320,"length":909,"resources":12,"people":12,"episode_number":195,"published":1321,"title":1322,"video_transcript_html":1323,"video_transcript_text":1324,"content":12,"seo":1325,"status":19,"episode_people":1326,"recommendations":1331,"season":1332},"january-2025","1048503294","Join us for The Changelog, taking you through the month’s Directus updates including product updates, new content and community contribution highlights. This month's show includes a documentation update, a community showcase from Shruti showing a real estate app, a salty santa recap from Bryant and an episode of a new show on Directus TV called Authentication Avenue.","6749d6aa-31b7-46bf-bfc1-74509a42440d","2025-01-23","January 2025","\u003Cp>Speaker 0: Hello everyone and welcome to the first change log of 2025. I hope you're all having an amazing start to your new year. You deserve it. As usual if you are joining us live do let us know in the chat where you're joining us from. We love to see, all the different places people are tuning in from.\u003C\u002Fp>\u003Cp>We have a really great show for you planned for today. We have a docs update from Carmen, a salty Santa recap from Bryant, a community showcase of a real estate app, and a new director's TV show based on authentication. So do stick around if you are able. Any questions in the meantime, let us know in the chat, and we'll, aim to help you. And I am going to start today off by sending you over to Kevin who will be giving you a product update.\u003C\u002Fp>\u003Cp>Speaker 1: Hey, everyone. Kevin here to tell you about what we've shipped in Directus in the last month or so, including our latest release last week, 11.4. Recent releases have included a lot of small optimizations, improvements, and bug fixes, but here's the highlights. It's only January. There's already a lot to talk about, so let's jump straight in.\u003C\u002Fp>\u003Cp>We finally support new versions of Node. Js, upgrading our runtime version to Node 22. We had some dependencies that we weren't sure were compatible beyond Node 18, but we've checked and resolved all known issues so you can continue to run directives on current long term support versions of Node. We've also added the ability to toggle translation interface items to make them deletable while ensuring things like defaults, conditions, and validations continue to work as expected. This is a really nice quality of life improvement for projects with multilingual content.\u003C\u002Fp>\u003Cp>Our Kanban layout has also received an update, now allowing you to use relational data in titles that appear on the front of cards. Version 11.3.5 and 11.4.0 have introduced a new language, Murathi, and got it all the way to a 100% coverage. This makes it the 30th language with 70% coverage or higher with over 2 thirds of those over 90%. We've also added activity tracking for field deletion, which is really useful for both accountability and visibility into what has changed in your project. You can continue to find the activity log at the bottom of your sidebar on the right hand side of the data studio.\u003C\u002Fp>\u003Cp>And finally, one for extension authors and a first contribution from our new core engineer, Mike. The latest version of the CLI that ships with the director's extensions SDK has a new validate command that performs common checks to determine that your extension will run and has key qualities that we look for. In future, we're planning to tie this to the marketplace, But for now, it's a lovely local convenience tool to help you ship better extensions. You can run it now with npx create directors extension validate. And that's all from me.\u003C\u002Fp>\u003Cp>Lots of updates to talk about. I'm gonna kick it back over to Beth, and I'll see you next time. Bye for now.\u003C\u002Fp>\u003Cp>Speaker 0: I'm going to demonstrate each of the new extensions in Directors Labs this month. As a reminder, Directors Labs is our team's experimental org on GitHub, which regularly publishes new extensions. Firstly, we have the tree view table layout which when selected from the layout options we have the option to select the parent. Here we have team members' names, who they report to, and whether or not they are warm enough at their desks. Having selected the tree view table as the layout option, we can now drag and select who reports to who out of the list of team member names.\u003C\u002Fp>\u003Cp>This then is reflected in, for example, Jack having the team members Astrid and Kate. The card select interface shown here with that information about records, adds image card select interfaces for single and multi selected use cases. You also have the ability to add via search and you can use inline SVGs via copy and paste, icons from material symbols library, or an image for the card select. Once selected these are reflected in the raw value. Another new extension is the switch interface which provides a nice alternative to the toggle interface which is default.\u003C\u002Fp>\u003Cp>Another part of this is the optional description you can add to the field. Here if we go into the interface we can add a description of 'wow, an optional description, how helpful' and once saved that will be back into the switch interface. Lastly, we have the super header interface which is a really nice way of adding more context along with actions. Here we've got information about a concert and we've got the venue and we want to change the superheader into something a bit more contextually helpful. So, if we go in to the super header title, we can alter the title to be 'location', we can add an icon, so because it's a location I think the pin makes more sense and we can choose a color using the colour picker, RVDB or HSL.\u003C\u002Fp>\u003Cp>I'm just going to pre select blue. You can also have a subtitle, so this is where the concert is' and we can add in helpful information to guide users so entry details will be given on the 1st of Feb. We also have the ability to add the action and that is a flow or a link. For this, I am going to say that when the button is clicked, I want us to navigate to search of concert venues. This extension gives the ability to use route level fields as variables in title subtitle or link URL and once we have changed that you can see that the super header has changed to show location and provide more context if it's needed.\u003C\u002Fp>\u003Cp>Speaker 2: Hey. What's up, changelog crew? Bryant here from Directus, and I just wanted to walk you through the salty open source Santa. Alright. This is probably more of a recap seeing as Christmas is now over, but, this was a fun little app, and you may get some use out of it as well.\u003C\u002Fp>\u003Cp>So Salty Open Source Santa is a Nuxt and Directus application that, basically, is the open source naughty or nice list. So you log in to GitHub, write a letter to Santa, or you can also write a letter to Santa on behalf of a friend or colleague. But, basically, we write a letter to Santa. Santa then checks your GitHub profile to see your open source contributions for the year. So behind the scenes, we're calling the GitHub API, actually using the GraphQL API.\u003C\u002Fp>\u003Cp>We get your contributions, your commits, your pull requests, your reviews that you've given on pull requests, if you've sponsored anybody. And we put all that through a scoring algorithm and determine whether you are on the naughty or nice list. And then regardless, Santa writes you a roast style letter back. You can see Santa is particularly rough on me here. Been watching you, Brian, and let me tell you, your GitHub profile is about as impressive as a melted snowman.\u003C\u002Fp>\u003Cp>So some of these takes are spicy. Some of them are a little more tame, but, lots of cool little features built into this application. One of them is the spiciness meter here. So, this is just how we determine whether a take or a roast letter is particularly spicy. And, if we take a look at Santa's list, we could see the spiciness level of each one of these.\u003C\u002Fp>\u003Cp>So we've got a 155 folks on the naughty list, 70 on the nice list. So that's kind of, in place of a like button. You could actually roast users or organizations with this thing. And then one of the other really fun bits that we we included in this, we're using 11 Labs here to actually read the letter to you from Santa. So you can have Santa read this out loud to you, which is awesome.\u003C\u002Fp>\u003Cp>A member of the core team, Pedro Pizarro, has got real chops for voice over. He's a musician as well, but he sent us the sample. We cloned the voice very quickly inside 11 Labs, and it calls their API and actually generates this for you. So if you want to check out this application, if you want to remix it yourself, go to the Directus Labs repository, look for the OS Santa repo sorry, the Directus Labs organization, and look for the OS Santa repo. You can see the full source code here.\u003C\u002Fp>\u003Cp>Everything is MIT licensed, so feel free to grab it, do whatever you want with it. Hopefully, it's helpful in your own applications. I know I had a lot of fun building it, and we got some great feedback from a lot of folks around Christmas time. So that's it for me. Back to you guys.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Hopefully, you are, excited to try that out for yourself if you haven't already. We have added all of the links to the various things mentioned in the chat, so do try it out and let us know what something says about you. Hopefully, you're on the nice list. Next up, we have Carmen with a documentation update and I believe Carmen's in the chat as well, so if you do have any questions whilst they're talking through, let us know, and we'll do the best to, have a chat with you there as well.\u003C\u002Fp>\u003Cp>Speaker 3: Hey, everyone. My name is Carmen. I'm a developer educator at Directus and part of my role is documentation. Over the last few weeks, we went ahead and launched our brand new documentation platform rebuilt from the ground up. And I am so excited to walk you through the new structure and also tell you about our motivations.\u003C\u002Fp>\u003Cp>So let's talk about content. We've been building our documentation content over several years, starting from a minimal set of requirements. It's seen a lot of input, and we wanted to take this opportunity to consolidate everything into one voice. So, every word, every sentence, every paragraph has been looked over carefully, adapted, or rewritten. This has been a group effort, not just by myself, but other educators at Directus.\u003C\u002Fp>\u003Cp>As for structure, we've also carefully considered how to best lead the reader towards what they need. On the new homepage, the first thing you'll see is how to get up and running with Directus as quickly as possible with a demo. Moving lower down, you'll find a full fledged guide on getting started using Directus as well as a link to our Directus Academy video series. Let's go over our new start section. We've assembled a set of short essentials for running a Directus project from creating your data model to authentication to using real time data.\u003C\u002Fp>\u003Cp>Next, I wanna show you how we've created detailed reference guides for using each of Directus' features. For example, if you need to configure authentication on your Directus project, you'll find everything you need here. Our API reference has also seen a major overhaul. It's now directly generated from our new open API specification. This is fully open source.\u003C\u002Fp>\u003Cp>So, if you find anything, please feel free to open a pull request. We've also added a section dedicated to using Directus Cloud. How to self host your Directus project and finally restructured our configuration documentation to make your project much easier to configure. Over in resources, we've added a dedicated community section where you can find our contribution, authoring, and documenting guidelines. You'll also find our community maintained tooling.\u003C\u002Fp>\u003Cp>If you're working on an open source tooling for Directus, please let us know so we can add it to this section on the home page. Any release changes to Directus are logged here as well. So, keep an eye out for those as they come in. Finally, we're bringing in a set of tutorials to work with Directus. If you're looking to fetch data from Directus using a specific framework, how to build specific projects with Directus, some advanced tips and tricks, migrating from other platforms to Directus, or building your very own extensions, you'll find it all here.\u003C\u002Fp>\u003Cp>And there's more coming, so stay tuned. As you can see, a lot has changed and for the better, we think. You'll find this new documentation platform over at directus. Iodocs. This is an ongoing project on our end and I'd love to ask you to please have a look over the new documentation, give us feedback, and tell us what we can do to improve.\u003C\u002Fp>\u003Cp>You'll find a little feedback widget on every page and our team will read all of your feedback. You can also open issues in our new directus\u002Fdocs repository on GitHub. Well, that's it for me. Thank you so much for listening, and I'll see you soon.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Thank you so much, Carmen. That is all looking great. And as mentioned before, if you've got any questions, do let us know. Next up, and I do say this every month, but it remains to be true, is my favorite segment of the show.\u003C\u002Fp>\u003Cp>We have the community showcase. And this month, we have Shruti Kapoor building a real estate app. So sending over to Shruti.\u003C\u002Fp>\u003Cp>Speaker 4: Hi, everyone. My name is Shruti Kapoor, and I'm going to do a quick demo for you to show you what I built with Directus. I'm a staff engineer currently working at Slack, and I love making coding tutorials for folks and help them get into front end. You can find all of my content up on YouTube at shruti kapoor 08. Now let's get into the demo.\u003C\u002Fp>\u003Cp>Here's a demo of an app that I built with Directus, OpenAI, Landchain, Node. Js, React, and Tailwind. It's a lot of stack, but let me simplify this demo for you. So here's a place where we can search for properties. I built this pro I built this app when I was looking for real estate in Seattle, and I was sick of entering fields in Zillow and having to manually type all of the things over and over again.\u003C\u002Fp>\u003Cp>So here's an app that I built. In this app, you can simply write an English language sentence like, I am looking for a 4 bedroom house with backyard, swimming pool, front yard, nice school district in the range of 2,000,000, 2,000,000, how many zeros are there in 1,000,000? 25,000,000. Let's just say I have this money. And if I hit search, that's all I have to do to look for a house and it's gonna magically look up for houses that match that preference in Seattle.\u003C\u002Fp>\u003Cp>So this is what I built. Also, this is also the starting point of building a recommendation engine. I built this like and dislike button. So next so let's say that I want to build a recommendation engine where I want to see more properties that match the preferences that I like. I can hit like on a property like this one, which I feel like is very pretty, or this one, which I feel like is too crowded.\u003C\u002Fp>\u003Cp>So I'm gonna dislike this. And now if I head over to my director's database, give it a reload, and see at the bottom that this property, 320 Northwest 75th Street, which was at 805, is marked as disliked in my database. And that's the grounds of setting up a recommendation engine. I can use the preferences that I set, yes or no, to build a recommendation engine that values the properties that I've already liked and tries to show you more properties like that, which will be the next part of this demo. By the way, I'll walk through this code very briefly to show you how I'm integrating with OpenAI.\u003C\u002Fp>\u003Cp>But if you're interested in a detailed walk through of this app, here is a video I've built on my channel which you can watch, Building an AI powered real estate app. But for now, let me show you how I integrate with LMM. So I've got this file called llm. Js, and this is all I'm providing my LMM. Okay?\u003C\u002Fp>\u003Cp>K. I'm creating an open AI, NLM, just providing it my API key. Then I'm setting a parser. I'm using the structured output parser and giving it a ZOD object that I want to create. The ZOD object that I want to create is price ending, which is gonna be a budget, which provides us a budget range of what my property should be, number of bedrooms for the house that I'm looking for, number of bathrooms.\u003C\u002Fp>\u003Cp>I can also provide a default value. For example, 1, if no bathroom was provided and a nice to have, which in my case would be backyard, swimming pool, front yard, front yard, nice school district, things like that. And, again, I can provide if there is nothing provided, return backyard because that's the kind of person I am. I love backyard. That's all I need to create is that object.\u003C\u002Fp>\u003Cp>And with this parser, I can then invoke my chain, which is how I call LLM. I'm gonna provide it a template. I'm gonna say, parse the description provided by user, and that is the text that I'm entering here at the top. And I'm gonna say pass the description provided by the user to extract information about real estate preferences. Which information?\u003C\u002Fp>\u003Cp>Exactly this information that I'm defining up here. And then all I need to do is invoke my chain with the description that the user has provided and the format instructions that I just generated over here. And that's how you call LLM. I'm saving all of this data in the database, which is direct us, and this will be helpful in creating a recommendation engine in the future. I can also save all of the properties that I've liked in the database.\u003C\u002Fp>\u003Cp>Thank you for watching my demo.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Thank you so much once again to Shruti for that, and I will post the link to her full video shortly. We have a new Directors TV episode next. It came out today. It's one of 5 episodes focused on authentication.\u003C\u002Fp>\u003Cp>So we're going to send in the authentication avenue next.\u003C\u002Fp>\u003Cp>Speaker 1: Hey there, developers, and welcome to authentication avenue. Ever wonder how your favorite apps know it's really you trying to log in? Well, today we're diving into authentication, the digital world's way of checking your ID. Let's imagine we're at our local library. When you want to borrow books, you need a library card.\u003C\u002Fp>\u003Cp>But getting that card isn't as simple as just walking in and saying, hey, I'm Kevin. Here's what actually happens. 1st, you fill out an application with your information. That's you claiming who you are. Then you show your ID or proof of address.\u003C\u002Fp>\u003Cp>That's you proving that you are really you. And only after verifying your identity does the librarian give you your very own library card. Now, every time you come back to borrow books, you show that library card. The librarian can check their system and confirm it's a valid card that actually belongs to you. And that's exactly how authentication works in the digital world.\u003C\u002Fp>\u003Cp>When you log in to your favorite app, you don't just type in your email and poof, you're in. Just like at the library, you need something to prove it's really you with something only you would know, your password. Now in the developer world, authentication is like that library card system. When users try to access their private data or personal settings, authentication verifies their identity using something they know, like their password, something they have, like their phone or verification codes, or something they are like their fingerprint. We call all of these authentication factors, and they're the digital equivalent of that library card and ID check.\u003C\u002Fp>\u003Cp>Now, once you've proven who you are, you need a way to show it with each request, like carrying your library card. In the digital world, we have a few ways of doing this. Most commonly, you will use what is known as a bearer token. It's like carrying an ID card that says, I've already proved who I am. You include this token in a special part of your request called the header.\u003C\u002Fp>\u003Cp>Or you might use cookies. These are like invisible name tags that your browser automatically shows to websites you've logged into before. Super convenient. And while you technically can include your credentials as query parameters in a URL, for example, example.com\u002F, or question mark token equals 12345, we don't really recommend this because sometimes URLs can be logged. And that's like writing your password on a sticky note where anyone can see it.\u003C\u002Fp>\u003Cp>So remember, authentication isn't just about saying who you are but it's about proving it. Next up, we'll see how this works in practice with Directus, but first, let's return our library card. Here we have a posts collection in a Directus project, but this collection is not publicly accessible, so we will need to authenticate as a user with the requisite permissions in order to access this data. And we see here when we just try and access it, when we try and list the posts, that we get an error. You don't have permission to access the collection posts or it does not exist, which is correct because it doesn't know who we are.\u003C\u002Fp>\u003Cp>We have not authenticated. Now as mentioned, there's a few ways to authenticate. One of the most common is by passing in what is known as an authorization header. So the headers get sent along with our request. The value is bearer, space, and then a valid access token of a user that has permissions.\u003C\u002Fp>\u003Cp>And when we hit send, we will get this data back. As I mentioned, there is also another way of doing this, which is via a query parameter to your request. So you can include access token equals value, and, again, that will successfully authenticate. The reason I'm showing you this is to actually dissuade you from using this approach because this full URL, you can type it in your browser here, will return data, but this full URL, including your access token, could be logged by your browser history, by your browser extensions, by your Internet service provider, your corporate VPN provider, and so on. And so to keep it secure, we tend to not use this approach too much.\u003C\u002Fp>\u003Cp>I do also wanna show you how to get an access token and authenticate using the Directus SDK. So here we have just a JavaScript file. We are initializing a new Directus SDK instance, and what we're going to do is import the authentication composable. And then we are going to initialize the client with the composable. This gives us a brand new function.\u003C\u002Fp>\u003Cp>We can now type in directus dot login and provide our email and our password, these values here. And this client will now be authenticated. So now we can go ahead and actually query our post's data. Let's import rest and read items, which we'll need to make this request, and add the rest composable to our client. And now we can go ahead and make a query, directors dot request read items posts.\u003C\u002Fp>\u003Cp>And then we'll console log the items, and this line here, specifically, is what is authenticating us. Let's see if that works, and we should see, promise pending. Absolutely. We just need to put in await there, and we will now see that once the data is returned, it is displayed here. So this is how we authenticate using the SDK.\u003C\u002Fp>\u003Cp>Join me in the next episode of authentication avenue where we will cover a brand new topic.\u003C\u002Fp>\u003Cp>Speaker 0: We want to take a moment towards the end of the changelog for thanking our community contributors who give their time to develop the Directors Project. Since last month, there have been 16 contributors. A huge thank you to Johannes for adding ability to map OIDC groups to OIDC roles, Junhong for ensuring floating view will not scroll top after route change and adding internationalization to rich text editor toolbar, Dominic for fixing the alignment of the 'select all' checkbox in the notifications drawer, and adding system argument for the clear cache function in the SDK, Robson for enhancing vertical alignment of SSO icons on login page, Osman for fixing an issue with unintentional sorting after editing items in relational interfaces, Gavelium for improving the default user created display template by using the avatar field, Gloria for increasing raw editor max height, Nick for adding assigning admin policy to a new role created via command line when needed, Paulius for adding ID token to provide a payload in auth create and auth update filter hooks, gene for improving the performance of relational lookups, DATNA for adding a discriminated union scenario to m to a query type resolution, Matthew for adding support for title and text fields to display related values in the kanban layout, culdip for adding Morathi to the list of translations, ben for clearing login error message on successful credentials when TFA is enabled, amateo for improving flow notification messages to differentiate between async and sync flow execution, Jason for fixing typo in 3 files.\u003C\u002Fp>\u003Cp>Thank you again to all our contributors. You can see their specific pull requests inside the full release notes on GitHub. Lastly, we want to take the time to thank our GitHub Sponsors of December who financially contribute to their riches' development. Thank you to Tommy, Entle, Jamilid, Fergus, Omar, Marcus, CK, Peter, Utomic, Steven, Robson, Nonlinear, Andrea, Valentino, John, Wayne, Bjorn, Adam, Jason, Birka, Jens, Vincent, Mike, Khan, Lessey, and Wayfair. The money we are given from our GitHub Sponsors goes straight back to community members who build tooling and extensions for the director's ecosystem.\u003C\u002Fp>\u003Cp>Thank you again for being part of that. Alright. And that is the January change log. Thank you so much to everyone for sticking around if you are still here. I hope you have enjoyed and been, educated by what we are showing.\u003C\u002Fp>\u003Cp>If you do have any questions in the meantime after this change log, we're always around in Discord to, hear what your thoughts are. And, additionally, if you have anything you'd like to see as part of the change log, always keen on feedback here, so do let us know. Thank you so much for joining us, and join us next time. The information will be around the discord events tab. So, yeah, keep an eye out for that.\u003C\u002Fp>\u003Cp>And thank you so much for joining us. Have a great rest of your day, week, and see you soon. Bye everyone.\u003C\u002Fp>","Hello everyone and welcome to the first change log of 2025. I hope you're all having an amazing start to your new year. You deserve it. As usual if you are joining us live do let us know in the chat where you're joining us from. We love to see, all the different places people are tuning in from. We have a really great show for you planned for today. We have a docs update from Carmen, a salty Santa recap from Bryant, a community showcase of a real estate app, and a new director's TV show based on authentication. So do stick around if you are able. Any questions in the meantime, let us know in the chat, and we'll, aim to help you. And I am going to start today off by sending you over to Kevin who will be giving you a product update. Hey, everyone. Kevin here to tell you about what we've shipped in Directus in the last month or so, including our latest release last week, 11.4. Recent releases have included a lot of small optimizations, improvements, and bug fixes, but here's the highlights. It's only January. There's already a lot to talk about, so let's jump straight in. We finally support new versions of Node. Js, upgrading our runtime version to Node 22. We had some dependencies that we weren't sure were compatible beyond Node 18, but we've checked and resolved all known issues so you can continue to run directives on current long term support versions of Node. We've also added the ability to toggle translation interface items to make them deletable while ensuring things like defaults, conditions, and validations continue to work as expected. This is a really nice quality of life improvement for projects with multilingual content. Our Kanban layout has also received an update, now allowing you to use relational data in titles that appear on the front of cards. Version 11.3.5 and 11.4.0 have introduced a new language, Murathi, and got it all the way to a 100% coverage. This makes it the 30th language with 70% coverage or higher with over 2 thirds of those over 90%. We've also added activity tracking for field deletion, which is really useful for both accountability and visibility into what has changed in your project. You can continue to find the activity log at the bottom of your sidebar on the right hand side of the data studio. And finally, one for extension authors and a first contribution from our new core engineer, Mike. The latest version of the CLI that ships with the director's extensions SDK has a new validate command that performs common checks to determine that your extension will run and has key qualities that we look for. In future, we're planning to tie this to the marketplace, But for now, it's a lovely local convenience tool to help you ship better extensions. You can run it now with npx create directors extension validate. And that's all from me. Lots of updates to talk about. I'm gonna kick it back over to Beth, and I'll see you next time. Bye for now. I'm going to demonstrate each of the new extensions in Directors Labs this month. As a reminder, Directors Labs is our team's experimental org on GitHub, which regularly publishes new extensions. Firstly, we have the tree view table layout which when selected from the layout options we have the option to select the parent. Here we have team members' names, who they report to, and whether or not they are warm enough at their desks. Having selected the tree view table as the layout option, we can now drag and select who reports to who out of the list of team member names. This then is reflected in, for example, Jack having the team members Astrid and Kate. The card select interface shown here with that information about records, adds image card select interfaces for single and multi selected use cases. You also have the ability to add via search and you can use inline SVGs via copy and paste, icons from material symbols library, or an image for the card select. Once selected these are reflected in the raw value. Another new extension is the switch interface which provides a nice alternative to the toggle interface which is default. Another part of this is the optional description you can add to the field. Here if we go into the interface we can add a description of 'wow, an optional description, how helpful' and once saved that will be back into the switch interface. Lastly, we have the super header interface which is a really nice way of adding more context along with actions. Here we've got information about a concert and we've got the venue and we want to change the superheader into something a bit more contextually helpful. So, if we go in to the super header title, we can alter the title to be 'location', we can add an icon, so because it's a location I think the pin makes more sense and we can choose a color using the colour picker, RVDB or HSL. I'm just going to pre select blue. You can also have a subtitle, so this is where the concert is' and we can add in helpful information to guide users so entry details will be given on the 1st of Feb. We also have the ability to add the action and that is a flow or a link. For this, I am going to say that when the button is clicked, I want us to navigate to search of concert venues. This extension gives the ability to use route level fields as variables in title subtitle or link URL and once we have changed that you can see that the super header has changed to show location and provide more context if it's needed. Hey. What's up, changelog crew? Bryant here from Directus, and I just wanted to walk you through the salty open source Santa. Alright. This is probably more of a recap seeing as Christmas is now over, but, this was a fun little app, and you may get some use out of it as well. So Salty Open Source Santa is a Nuxt and Directus application that, basically, is the open source naughty or nice list. So you log in to GitHub, write a letter to Santa, or you can also write a letter to Santa on behalf of a friend or colleague. But, basically, we write a letter to Santa. Santa then checks your GitHub profile to see your open source contributions for the year. So behind the scenes, we're calling the GitHub API, actually using the GraphQL API. We get your contributions, your commits, your pull requests, your reviews that you've given on pull requests, if you've sponsored anybody. And we put all that through a scoring algorithm and determine whether you are on the naughty or nice list. And then regardless, Santa writes you a roast style letter back. You can see Santa is particularly rough on me here. Been watching you, Brian, and let me tell you, your GitHub profile is about as impressive as a melted snowman. So some of these takes are spicy. Some of them are a little more tame, but, lots of cool little features built into this application. One of them is the spiciness meter here. So, this is just how we determine whether a take or a roast letter is particularly spicy. And, if we take a look at Santa's list, we could see the spiciness level of each one of these. So we've got a 155 folks on the naughty list, 70 on the nice list. So that's kind of, in place of a like button. You could actually roast users or organizations with this thing. And then one of the other really fun bits that we we included in this, we're using 11 Labs here to actually read the letter to you from Santa. So you can have Santa read this out loud to you, which is awesome. A member of the core team, Pedro Pizarro, has got real chops for voice over. He's a musician as well, but he sent us the sample. We cloned the voice very quickly inside 11 Labs, and it calls their API and actually generates this for you. So if you want to check out this application, if you want to remix it yourself, go to the Directus Labs repository, look for the OS Santa repo sorry, the Directus Labs organization, and look for the OS Santa repo. You can see the full source code here. Everything is MIT licensed, so feel free to grab it, do whatever you want with it. Hopefully, it's helpful in your own applications. I know I had a lot of fun building it, and we got some great feedback from a lot of folks around Christmas time. So that's it for me. Back to you guys. Alright. Hopefully, you are, excited to try that out for yourself if you haven't already. We have added all of the links to the various things mentioned in the chat, so do try it out and let us know what something says about you. Hopefully, you're on the nice list. Next up, we have Carmen with a documentation update and I believe Carmen's in the chat as well, so if you do have any questions whilst they're talking through, let us know, and we'll do the best to, have a chat with you there as well. Hey, everyone. My name is Carmen. I'm a developer educator at Directus and part of my role is documentation. Over the last few weeks, we went ahead and launched our brand new documentation platform rebuilt from the ground up. And I am so excited to walk you through the new structure and also tell you about our motivations. So let's talk about content. We've been building our documentation content over several years, starting from a minimal set of requirements. It's seen a lot of input, and we wanted to take this opportunity to consolidate everything into one voice. So, every word, every sentence, every paragraph has been looked over carefully, adapted, or rewritten. This has been a group effort, not just by myself, but other educators at Directus. As for structure, we've also carefully considered how to best lead the reader towards what they need. On the new homepage, the first thing you'll see is how to get up and running with Directus as quickly as possible with a demo. Moving lower down, you'll find a full fledged guide on getting started using Directus as well as a link to our Directus Academy video series. Let's go over our new start section. We've assembled a set of short essentials for running a Directus project from creating your data model to authentication to using real time data. Next, I wanna show you how we've created detailed reference guides for using each of Directus' features. For example, if you need to configure authentication on your Directus project, you'll find everything you need here. Our API reference has also seen a major overhaul. It's now directly generated from our new open API specification. This is fully open source. So, if you find anything, please feel free to open a pull request. We've also added a section dedicated to using Directus Cloud. How to self host your Directus project and finally restructured our configuration documentation to make your project much easier to configure. Over in resources, we've added a dedicated community section where you can find our contribution, authoring, and documenting guidelines. You'll also find our community maintained tooling. If you're working on an open source tooling for Directus, please let us know so we can add it to this section on the home page. Any release changes to Directus are logged here as well. So, keep an eye out for those as they come in. Finally, we're bringing in a set of tutorials to work with Directus. If you're looking to fetch data from Directus using a specific framework, how to build specific projects with Directus, some advanced tips and tricks, migrating from other platforms to Directus, or building your very own extensions, you'll find it all here. And there's more coming, so stay tuned. As you can see, a lot has changed and for the better, we think. You'll find this new documentation platform over at directus. Iodocs. This is an ongoing project on our end and I'd love to ask you to please have a look over the new documentation, give us feedback, and tell us what we can do to improve. You'll find a little feedback widget on every page and our team will read all of your feedback. You can also open issues in our new directus\u002Fdocs repository on GitHub. Well, that's it for me. Thank you so much for listening, and I'll see you soon. Alright. Thank you so much, Carmen. That is all looking great. And as mentioned before, if you've got any questions, do let us know. Next up, and I do say this every month, but it remains to be true, is my favorite segment of the show. We have the community showcase. And this month, we have Shruti Kapoor building a real estate app. So sending over to Shruti. Hi, everyone. My name is Shruti Kapoor, and I'm going to do a quick demo for you to show you what I built with Directus. I'm a staff engineer currently working at Slack, and I love making coding tutorials for folks and help them get into front end. You can find all of my content up on YouTube at shruti kapoor 08. Now let's get into the demo. Here's a demo of an app that I built with Directus, OpenAI, Landchain, Node. Js, React, and Tailwind. It's a lot of stack, but let me simplify this demo for you. So here's a place where we can search for properties. I built this pro I built this app when I was looking for real estate in Seattle, and I was sick of entering fields in Zillow and having to manually type all of the things over and over again. So here's an app that I built. In this app, you can simply write an English language sentence like, I am looking for a 4 bedroom house with backyard, swimming pool, front yard, nice school district in the range of 2,000,000, 2,000,000, how many zeros are there in 1,000,000? 25,000,000. Let's just say I have this money. And if I hit search, that's all I have to do to look for a house and it's gonna magically look up for houses that match that preference in Seattle. So this is what I built. Also, this is also the starting point of building a recommendation engine. I built this like and dislike button. So next so let's say that I want to build a recommendation engine where I want to see more properties that match the preferences that I like. I can hit like on a property like this one, which I feel like is very pretty, or this one, which I feel like is too crowded. So I'm gonna dislike this. And now if I head over to my director's database, give it a reload, and see at the bottom that this property, 320 Northwest 75th Street, which was at 805, is marked as disliked in my database. And that's the grounds of setting up a recommendation engine. I can use the preferences that I set, yes or no, to build a recommendation engine that values the properties that I've already liked and tries to show you more properties like that, which will be the next part of this demo. By the way, I'll walk through this code very briefly to show you how I'm integrating with OpenAI. But if you're interested in a detailed walk through of this app, here is a video I've built on my channel which you can watch, Building an AI powered real estate app. But for now, let me show you how I integrate with LMM. So I've got this file called llm. Js, and this is all I'm providing my LMM. Okay? K. I'm creating an open AI, NLM, just providing it my API key. Then I'm setting a parser. I'm using the structured output parser and giving it a ZOD object that I want to create. The ZOD object that I want to create is price ending, which is gonna be a budget, which provides us a budget range of what my property should be, number of bedrooms for the house that I'm looking for, number of bathrooms. I can also provide a default value. For example, 1, if no bathroom was provided and a nice to have, which in my case would be backyard, swimming pool, front yard, front yard, nice school district, things like that. And, again, I can provide if there is nothing provided, return backyard because that's the kind of person I am. I love backyard. That's all I need to create is that object. And with this parser, I can then invoke my chain, which is how I call LLM. I'm gonna provide it a template. I'm gonna say, parse the description provided by user, and that is the text that I'm entering here at the top. And I'm gonna say pass the description provided by the user to extract information about real estate preferences. Which information? Exactly this information that I'm defining up here. And then all I need to do is invoke my chain with the description that the user has provided and the format instructions that I just generated over here. And that's how you call LLM. I'm saving all of this data in the database, which is direct us, and this will be helpful in creating a recommendation engine in the future. I can also save all of the properties that I've liked in the database. Thank you for watching my demo. Alright. Thank you so much once again to Shruti for that, and I will post the link to her full video shortly. We have a new Directors TV episode next. It came out today. It's one of 5 episodes focused on authentication. So we're going to send in the authentication avenue next. Hey there, developers, and welcome to authentication avenue. Ever wonder how your favorite apps know it's really you trying to log in? Well, today we're diving into authentication, the digital world's way of checking your ID. Let's imagine we're at our local library. When you want to borrow books, you need a library card. But getting that card isn't as simple as just walking in and saying, hey, I'm Kevin. Here's what actually happens. 1st, you fill out an application with your information. That's you claiming who you are. Then you show your ID or proof of address. That's you proving that you are really you. And only after verifying your identity does the librarian give you your very own library card. Now, every time you come back to borrow books, you show that library card. The librarian can check their system and confirm it's a valid card that actually belongs to you. And that's exactly how authentication works in the digital world. When you log in to your favorite app, you don't just type in your email and poof, you're in. Just like at the library, you need something to prove it's really you with something only you would know, your password. Now in the developer world, authentication is like that library card system. When users try to access their private data or personal settings, authentication verifies their identity using something they know, like their password, something they have, like their phone or verification codes, or something they are like their fingerprint. We call all of these authentication factors, and they're the digital equivalent of that library card and ID check. Now, once you've proven who you are, you need a way to show it with each request, like carrying your library card. In the digital world, we have a few ways of doing this. Most commonly, you will use what is known as a bearer token. It's like carrying an ID card that says, I've already proved who I am. You include this token in a special part of your request called the header. Or you might use cookies. These are like invisible name tags that your browser automatically shows to websites you've logged into before. Super convenient. And while you technically can include your credentials as query parameters in a URL, for example, example.com\u002F, or question mark token equals 12345, we don't really recommend this because sometimes URLs can be logged. And that's like writing your password on a sticky note where anyone can see it. So remember, authentication isn't just about saying who you are but it's about proving it. Next up, we'll see how this works in practice with Directus, but first, let's return our library card. Here we have a posts collection in a Directus project, but this collection is not publicly accessible, so we will need to authenticate as a user with the requisite permissions in order to access this data. And we see here when we just try and access it, when we try and list the posts, that we get an error. You don't have permission to access the collection posts or it does not exist, which is correct because it doesn't know who we are. We have not authenticated. Now as mentioned, there's a few ways to authenticate. One of the most common is by passing in what is known as an authorization header. So the headers get sent along with our request. The value is bearer, space, and then a valid access token of a user that has permissions. And when we hit send, we will get this data back. As I mentioned, there is also another way of doing this, which is via a query parameter to your request. So you can include access token equals value, and, again, that will successfully authenticate. The reason I'm showing you this is to actually dissuade you from using this approach because this full URL, you can type it in your browser here, will return data, but this full URL, including your access token, could be logged by your browser history, by your browser extensions, by your Internet service provider, your corporate VPN provider, and so on. And so to keep it secure, we tend to not use this approach too much. I do also wanna show you how to get an access token and authenticate using the Directus SDK. So here we have just a JavaScript file. We are initializing a new Directus SDK instance, and what we're going to do is import the authentication composable. And then we are going to initialize the client with the composable. This gives us a brand new function. We can now type in directus dot login and provide our email and our password, these values here. And this client will now be authenticated. So now we can go ahead and actually query our post's data. Let's import rest and read items, which we'll need to make this request, and add the rest composable to our client. And now we can go ahead and make a query, directors dot request read items posts. And then we'll console log the items, and this line here, specifically, is what is authenticating us. Let's see if that works, and we should see, promise pending. Absolutely. We just need to put in await there, and we will now see that once the data is returned, it is displayed here. So this is how we authenticate using the SDK. Join me in the next episode of authentication avenue where we will cover a brand new topic. We want to take a moment towards the end of the changelog for thanking our community contributors who give their time to develop the Directors Project. Since last month, there have been 16 contributors. A huge thank you to Johannes for adding ability to map OIDC groups to OIDC roles, Junhong for ensuring floating view will not scroll top after route change and adding internationalization to rich text editor toolbar, Dominic for fixing the alignment of the 'select all' checkbox in the notifications drawer, and adding system argument for the clear cache function in the SDK, Robson for enhancing vertical alignment of SSO icons on login page, Osman for fixing an issue with unintentional sorting after editing items in relational interfaces, Gavelium for improving the default user created display template by using the avatar field, Gloria for increasing raw editor max height, Nick for adding assigning admin policy to a new role created via command line when needed, Paulius for adding ID token to provide a payload in auth create and auth update filter hooks, gene for improving the performance of relational lookups, DATNA for adding a discriminated union scenario to m to a query type resolution, Matthew for adding support for title and text fields to display related values in the kanban layout, culdip for adding Morathi to the list of translations, ben for clearing login error message on successful credentials when TFA is enabled, amateo for improving flow notification messages to differentiate between async and sync flow execution, Jason for fixing typo in 3 files. Thank you again to all our contributors. You can see their specific pull requests inside the full release notes on GitHub. Lastly, we want to take the time to thank our GitHub Sponsors of December who financially contribute to their riches' development. Thank you to Tommy, Entle, Jamilid, Fergus, Omar, Marcus, CK, Peter, Utomic, Steven, Robson, Nonlinear, Andrea, Valentino, John, Wayne, Bjorn, Adam, Jason, Birka, Jens, Vincent, Mike, Khan, Lessey, and Wayfair. The money we are given from our GitHub Sponsors goes straight back to community members who build tooling and extensions for the director's ecosystem. Thank you again for being part of that. Alright. And that is the January change log. Thank you so much to everyone for sticking around if you are still here. I hope you have enjoyed and been, educated by what we are showing. If you do have any questions in the meantime after this change log, we're always around in Discord to, hear what your thoughts are. And, additionally, if you have anything you'd like to see as part of the change log, always keen on feedback here, so do let us know. Thank you so much for joining us, and join us next time. The information will be around the discord events tab. So, yeah, keep an eye out for that. And thank you so much for joining us. Have a great rest of your day, week, and see you soon. Bye everyone.","ee99b59e-52e3-4401-a57f-10ab14f69344",[1327,1328,1329,1330],"42d869b7-2872-4c42-9c21-964b9c6aa34d","6cd7f001-85b5-4ae5-ace8-6aafa9d9cd37","07b7288f-5b49-4bf2-aa61-daf78c5ccd94","3f30c283-367a-4535-ab32-083047861140",[],{"year":399,"number":687,"id":688,"episodes":1333,"show":1334},[690,691,692,693,694,695,696,669],{"title":297,"slug":298},{"id":1336,"slug":1337,"vimeo_id":1338,"description":1339,"tile":1340,"length":129,"resources":12,"people":12,"episode_number":159,"published":1341,"title":1342,"video_transcript_html":1343,"video_transcript_text":1344,"content":12,"seo":1345,"status":19,"episode_people":1346,"recommendations":1348,"season":1349},"ae9ce140-b94f-455f-8e94-e623e3d4da76","what-are-static-tokens","1048670289","Sometimes you need to give out a master key that never changes. In this jog down Authentication Avenue, Kevin answers \"What are static tokens?\".","ab367ceb-0852-46de-bcbe-3326246b30d9","2025-01-21","What are static tokens?","\u003Cp>Speaker 0: Hey, developers. And welcome to another episode of Authentication Avenue. Ever needed a way to access your application that never expires and never changes? Today, we're talking about static tokens, the digital equivalent of having a master key to your house. My friendly neighborhood mail carrier is called Alex.\u003C\u002Fp>\u003Cp>Every morning, Alex delivers packages to, our building. And now most residents to our building get a new digital key card every week. The building manager says it's safer that way. But imagine if Alex had to visit the manager's office every single week for a new key card. They'd spend more times collecting new keys than delivering mail.\u003C\u002Fp>\u003Cp>So what is the solution? Alex gets a special permanent key. It never expires, it never needs updating, and it lets them deliver packages efficiently every single day. Now in the world of software, static tokens are just like Alex's permanent key. While regular users might need their access credentials refreshed frequently, like those weekly key cards, some situations call for a permanent, unchanging key.\u003C\u002Fp>\u003Cp>Maybe it's an automated email system that needs to check for new messages or a weather service that needs to fetch updates every hour. These are our digital mail carriers needing reliable and constant access. But here's the thing, just like the building manager only gives out a few permanent keys, static tokens are special. They're powerful tools that we use carefully and only when we really need them. If someone gets Alex's key, that would be a problem, so we need to make sure that they are kept safe.\u003C\u002Fp>\u003Cp>In directives, each user can have one static token. Think of it as their personal master key. Unlike regular access tokens that expire and need refreshing, this static token stays the same until you manually change it, provides continuous access to the API, and can be used for automated tasks or system integrations. It also has all the permissions of the user that it belongs to. However, remember with great power comes great responsibility.\u003C\u002Fp>\u003Cp>Since these tokens don't expire, you need to keep them extra secure and only use them when necessary. So there you have it. Static tokens are your always on access pass. Perfect for automation and system integration, but handle with care. Let's see how we can get a static token inside of Directus and use it when building our projects.\u003C\u002Fp>\u003Cp>As mentioned, each user can have one static access token, which can be used to authenticate as them in context where you can't constantly be providing a username or password or changing the key that you have provided. This is really common in very small personal applications and in any form of automation or integration with a third party. Now over in your user profile or if you are an admin in other user profiles, you can scroll down to the bottom and notice this token area here. You generate the token with the plus button, take note of it, copying it to your clipboard, and then make sure that you save or this token will not be active. This is the token that you can now provide when, when authenticating with Directus.\u003C\u002Fp>\u003Cp>If you go back in, take note that this is stored as a string in your database. However, you can't access it easily via the UI here. It's obfuscated. And if you regenerate the token, the old token will no longer work, so we only show it one time, so be sure to copy it. Now when you're using the SDK, take note that we have a static token function, which you can, which you can import from the SDK.\u003C\u002Fp>\u003Cp>And when you're initializing your client, you can pass in the static token like so. Oh, it helps if you actually do something with that console log items, like so. 3rd time lucky. There's some data there. You can also provide a static token for one off requests using different functions that are available within the SDK, and you can take a look at the SDK documentation to learn more.\u003C\u002Fp>\u003Cp>So, yeah, that's how you use static tokens, really good for prototyping small personal projects, and sometimes necessary in order to set up integrations. Thank you so much for joining me, and I'll see you in the next episode.\u003C\u002Fp>","Hey, developers. And welcome to another episode of Authentication Avenue. Ever needed a way to access your application that never expires and never changes? Today, we're talking about static tokens, the digital equivalent of having a master key to your house. My friendly neighborhood mail carrier is called Alex. Every morning, Alex delivers packages to, our building. And now most residents to our building get a new digital key card every week. The building manager says it's safer that way. But imagine if Alex had to visit the manager's office every single week for a new key card. They'd spend more times collecting new keys than delivering mail. So what is the solution? Alex gets a special permanent key. It never expires, it never needs updating, and it lets them deliver packages efficiently every single day. Now in the world of software, static tokens are just like Alex's permanent key. While regular users might need their access credentials refreshed frequently, like those weekly key cards, some situations call for a permanent, unchanging key. Maybe it's an automated email system that needs to check for new messages or a weather service that needs to fetch updates every hour. These are our digital mail carriers needing reliable and constant access. But here's the thing, just like the building manager only gives out a few permanent keys, static tokens are special. They're powerful tools that we use carefully and only when we really need them. If someone gets Alex's key, that would be a problem, so we need to make sure that they are kept safe. In directives, each user can have one static token. Think of it as their personal master key. Unlike regular access tokens that expire and need refreshing, this static token stays the same until you manually change it, provides continuous access to the API, and can be used for automated tasks or system integrations. It also has all the permissions of the user that it belongs to. However, remember with great power comes great responsibility. Since these tokens don't expire, you need to keep them extra secure and only use them when necessary. So there you have it. Static tokens are your always on access pass. Perfect for automation and system integration, but handle with care. Let's see how we can get a static token inside of Directus and use it when building our projects. As mentioned, each user can have one static access token, which can be used to authenticate as them in context where you can't constantly be providing a username or password or changing the key that you have provided. This is really common in very small personal applications and in any form of automation or integration with a third party. Now over in your user profile or if you are an admin in other user profiles, you can scroll down to the bottom and notice this token area here. You generate the token with the plus button, take note of it, copying it to your clipboard, and then make sure that you save or this token will not be active. This is the token that you can now provide when, when authenticating with Directus. If you go back in, take note that this is stored as a string in your database. However, you can't access it easily via the UI here. It's obfuscated. And if you regenerate the token, the old token will no longer work, so we only show it one time, so be sure to copy it. Now when you're using the SDK, take note that we have a static token function, which you can, which you can import from the SDK. And when you're initializing your client, you can pass in the static token like so. Oh, it helps if you actually do something with that console log items, like so. 3rd time lucky. There's some data there. You can also provide a static token for one off requests using different functions that are available within the SDK, and you can take a look at the SDK documentation to learn more. So, yeah, that's how you use static tokens, really good for prototyping small personal projects, and sometimes necessary in order to set up integrations. Thank you so much for joining me, and I'll see you in the next episode.","8840ef88-bda1-463e-8e76-e9c9cb0f6d6f",[1347],"97aaa6b6-31b8-48cd-980b-b3d2aa3219b2",[],{"year":399,"number":675,"id":1350,"episodes":1351,"show":1356},"ff7886c3-8457-49e6-a2c7-f7d526d518ce",[1352,1353,1354,1336,1355],"0df4af9a-442f-4fd5-97b5-3c38e6644e75","707a5d2a-1db7-4b64-a371-cb332d72b16f","c6491b5a-5afa-4ce8-8958-d15ecf4a3369","6d68c3bc-e3b4-49f2-81fb-6bd7ddb45995",{"title":1357,"slug":1358},"Authentication Avenue","authentication-ave",{"id":1355,"slug":1360,"vimeo_id":1361,"description":1362,"tile":1363,"length":159,"resources":12,"people":12,"episode_number":213,"published":1341,"title":1364,"video_transcript_html":1365,"video_transcript_text":1366,"content":12,"seo":1367,"status":19,"episode_people":1368,"recommendations":1370,"season":1371},"what-are-cookies","1048670378","No, not those kind of cookies! In this comfortable sit on a bench at the end of Authentication Avenue, Kevin answers \"What are cookies?\" and shows you how to get them.","6c79160f-53b5-49c7-b6fc-5289010dc045","What are cookies?","\u003Cp>Speaker 0: Hey, developers. Welcome back to authentication avenue. Ever wondered how websites seem to magically remember you as you click from page to page? Today, we're talking about cookies, tiny digital notes that follow you around automatically. Imagine you're shopping at a busy department store.\u003C\u002Fp>\u003Cp>When you first walk in, the greeter hands you a small numbered tag. Now here's the cool part, as you move through the different departments, you never have to explain who you are or what you're doing. The fitting room attendant scans your tag and knows which clothes you're trying. The cafe counter sees your tag and knows about your coffee loyalty points. Your personal shopper spots your tag and pulls up your size preferences, and the gift wrapping station automatically knows which items are yours.\u003C\u002Fp>\u003Cp>You never have to take the tag out and show it to anyone. Every department scanner just picks it up automatically when it's in your pocket. But here's the thing, this tag only works in this store. If you walk into the store next door that tag means nothing to them. Web cookies work exactly like this, when you visit a website it gives your browser a cookie, like that number tag.\u003C\u002Fp>\u003Cp>Every time you click to a new page on that same website, your browser automatically shows this cookie. You don't have to do anything, it just happens in the background. But just like that store tag, cookies only work on the website that created them. In web applications, cookies are small text files that your browser stores securely, automatically includes in every request to the same website, keeps separate for different websites, and expire after a certain time. In Directus, when you log in with cookies enabled, it sets a cookie in your browser.\u003C\u002Fp>\u003Cp>After that, every request you make to Directus automatically includes this cookie. That's how it knows it's still you, page after page, request after request. So remember, cookies are like that magical shopping loyalty card. They work automatically in the background, making sure that websites remember who you are with every click. Let's see how cookies work in Directus.\u003C\u002Fp>\u003Cp>Very simple web page that includes a button, and when the button is clicked, we will run this function. All the function does is send a post request to our login endpoint, with the email, the password, and a mode of session, which tells directors to send a cookie in response, not a JSON payload with our access token. Once successful, it will log cookie set. So here is the web page here, and we click log in, and we see that the request goes out and comes back. And the data that comes back, it doesn't have much in the payload at all.\u003C\u002Fp>\u003Cp>It comes back with expiry time, but it also sends back a cookie with the value. So we see that here. Now session cookies are interesting in that they also include the refresh portion of the of the, access token all as a single token. So once it expires, that's it. You can no longer use it, but you can refresh it before it expires, using the single key.\u003C\u002Fp>\u003Cp>So here we have a cookie and we see over in application cookie settings that that cookie has been saved to our browser. Now in any subsequent requests, this cookie will be automatically included in requests without us needing to authenticate separately. So that's just a really, really surface level understanding of how cookies work and where you can find them inside of your browser dev tools. Thank you so much for joining me for this episode of Authentication Avenue.\u003C\u002Fp>","Hey, developers. Welcome back to authentication avenue. Ever wondered how websites seem to magically remember you as you click from page to page? Today, we're talking about cookies, tiny digital notes that follow you around automatically. Imagine you're shopping at a busy department store. When you first walk in, the greeter hands you a small numbered tag. Now here's the cool part, as you move through the different departments, you never have to explain who you are or what you're doing. The fitting room attendant scans your tag and knows which clothes you're trying. The cafe counter sees your tag and knows about your coffee loyalty points. Your personal shopper spots your tag and pulls up your size preferences, and the gift wrapping station automatically knows which items are yours. You never have to take the tag out and show it to anyone. Every department scanner just picks it up automatically when it's in your pocket. But here's the thing, this tag only works in this store. If you walk into the store next door that tag means nothing to them. Web cookies work exactly like this, when you visit a website it gives your browser a cookie, like that number tag. Every time you click to a new page on that same website, your browser automatically shows this cookie. You don't have to do anything, it just happens in the background. But just like that store tag, cookies only work on the website that created them. In web applications, cookies are small text files that your browser stores securely, automatically includes in every request to the same website, keeps separate for different websites, and expire after a certain time. In Directus, when you log in with cookies enabled, it sets a cookie in your browser. After that, every request you make to Directus automatically includes this cookie. That's how it knows it's still you, page after page, request after request. So remember, cookies are like that magical shopping loyalty card. They work automatically in the background, making sure that websites remember who you are with every click. Let's see how cookies work in Directus. Very simple web page that includes a button, and when the button is clicked, we will run this function. All the function does is send a post request to our login endpoint, with the email, the password, and a mode of session, which tells directors to send a cookie in response, not a JSON payload with our access token. Once successful, it will log cookie set. So here is the web page here, and we click log in, and we see that the request goes out and comes back. And the data that comes back, it doesn't have much in the payload at all. It comes back with expiry time, but it also sends back a cookie with the value. So we see that here. Now session cookies are interesting in that they also include the refresh portion of the of the, access token all as a single token. So once it expires, that's it. You can no longer use it, but you can refresh it before it expires, using the single key. So here we have a cookie and we see over in application cookie settings that that cookie has been saved to our browser. Now in any subsequent requests, this cookie will be automatically included in requests without us needing to authenticate separately. So that's just a really, really surface level understanding of how cookies work and where you can find them inside of your browser dev tools. Thank you so much for joining me for this episode of Authentication Avenue.","90d34055-1ae9-4c0a-976a-7b43222ee509",[1369],"8d0a1b2d-7958-4a56-a967-a099adf9c590",[],{"year":399,"number":675,"id":1350,"episodes":1372,"show":1373},[1352,1353,1354,1336,1355],{"title":1357,"slug":1358},{"id":1352,"slug":1375,"vimeo_id":1376,"description":1377,"tile":1378,"length":129,"resources":12,"people":12,"episode_number":13,"published":1341,"title":1379,"video_transcript_html":1380,"video_transcript_text":1381,"content":12,"seo":1382,"status":19,"episode_people":1383,"recommendations":1385,"season":1386},"what-does-authentication-mean","1048669894","Authentication happens many times a day without us even realizing. In this walk down Authentication Avenue, Kevin answers \"What does authentication mean?\"","cb41e733-5cf5-4a65-bf50-92407827e19a","What does authentication mean?","\u003Cp>Speaker 0: Hey there, developers, and welcome to authentication avenue. Ever wonder how your favorite apps know it's really you trying to log in? Well, today we're diving into authentication, the digital world's way of checking your ID. Let's imagine we're at our local library. When you want to borrow books, you need a library card.\u003C\u002Fp>\u003Cp>But getting that card isn't as simple as just walking in and saying, hey, I'm Kevin. Here's what actually happens. First, you fill out an application with your information. That's you claiming who you are. Then you show your ID or proof of address.\u003C\u002Fp>\u003Cp>That's you proving that you are really you. And only after verifying your identity does the librarian give you your very own library card. Now, every time you come back to borrow books, you show that library card. The librarian can check their system and confirm it's a valid card that actually belongs to you. And that's exactly how authentication works in the digital world.\u003C\u002Fp>\u003Cp>When you log in to your favorite app, you don't just type in your email and poof, you're in. Just like at the library, you need something to prove it's really you with something only you would know, your password. Now, in the developer world, authentication is like that library card system. When users try to access their private data or personal settings, authentication verifies their identity using something they know, like their password, something they have, like their phone or verification codes, or something they are, like their fingerprint. We call all of these authentication factors, and they're the digital equivalent of that library card and ID check.\u003C\u002Fp>\u003Cp>Now, once you've proven who you are, you need a way to show it with each request, like carrying your library card. In the digital world, we have a few ways of doing this. Most commonly, you will use what is known as a bearer token. It's like carrying an ID card that says, I've already proved who I am. You include this token in a special part of your request called the header.\u003C\u002Fp>\u003Cp>Or you might use cookies. These are like invisible name tags that your browser automatically shows to websites you've logged into before. Super convenient. And while you technically can include your credentials as query parameters in a URL, for example, example.com\u002Fquestionmarktoken equals 12345, we don't really recommend this because sometimes URLs can be logged and that's like writing your password on a sticky note where anyone can see it. So remember, authentication isn't just about saying who you are but it's about proving it.\u003C\u002Fp>\u003Cp>Next up, we'll see how this works in practice with Directus. But first, let's return our library card. Here we have a posts collection in a Directus project, but this collection is not publicly accessible, so we will need to authenticate as a user with the requisite permissions in order to access this data. And we see here when we just try and access it, when we try and list the posts, that we get an error. You don't have permission to access the collection posts or it does not exist, which is correct because it doesn't know who we are.\u003C\u002Fp>\u003Cp>We have not authenticated. Now as mentioned, there's a few ways to authenticate. One of the most common is by passing in what is known as an authorization header. So the headers get sent along with our request. The value is bearer space and then a valid access token of a user that has permissions, and when we hit send, we will get this data back.\u003C\u002Fp>\u003Cp>As I mentioned, there is also another way of doing this, which is via a query parameter to your request. So you can include access token equals value, and, again, that will successfully authenticate. The reason I'm showing you this is to actually dissuade you from using this approach because this full URL, you can type it in your browser here, will return data, but this full URL, including your access token, could be logged by your browser history, by your browser extensions, by your Internet service provider, your corporate VPN provider, and so on. And so to keep it secure, we tend to not use this approach too much. I do also wanna show you how to get an access token and authenticate using the Directus SDK.\u003C\u002Fp>\u003Cp>So here we have just a JavaScript file. We are initializing a new Directus SDK instance, and what we're going to do is import the authentication composable. And then we are going to initialize the client with the composable. This gives us a brand new function. We can now type in directives.login and provide our email and our password, these values here.\u003C\u002Fp>\u003Cp>And this client will now be authenticated, so now we can go ahead and actually query our post data. Let's import rest and read items, which we'll need to make this request, and add the rest composable to our client. And now we can go ahead and make a query, directus dot request read items posts. And then we'll console log the items. And this line here specifically is what is authenticating us.\u003C\u002Fp>\u003Cp>Let's see if that works, and we should see app promise pending. Absolutely. We just need to put in a wait there, and we will now see that once the data is returned, it is displayed here. So this is how we authenticate using the SDK. Join me in the next episode of Authentication Avenue where we will cover a brand new topic.\u003C\u002Fp>","Hey there, developers, and welcome to authentication avenue. Ever wonder how your favorite apps know it's really you trying to log in? Well, today we're diving into authentication, the digital world's way of checking your ID. Let's imagine we're at our local library. When you want to borrow books, you need a library card. But getting that card isn't as simple as just walking in and saying, hey, I'm Kevin. Here's what actually happens. First, you fill out an application with your information. That's you claiming who you are. Then you show your ID or proof of address. That's you proving that you are really you. And only after verifying your identity does the librarian give you your very own library card. Now, every time you come back to borrow books, you show that library card. The librarian can check their system and confirm it's a valid card that actually belongs to you. And that's exactly how authentication works in the digital world. When you log in to your favorite app, you don't just type in your email and poof, you're in. Just like at the library, you need something to prove it's really you with something only you would know, your password. Now, in the developer world, authentication is like that library card system. When users try to access their private data or personal settings, authentication verifies their identity using something they know, like their password, something they have, like their phone or verification codes, or something they are, like their fingerprint. We call all of these authentication factors, and they're the digital equivalent of that library card and ID check. Now, once you've proven who you are, you need a way to show it with each request, like carrying your library card. In the digital world, we have a few ways of doing this. Most commonly, you will use what is known as a bearer token. It's like carrying an ID card that says, I've already proved who I am. You include this token in a special part of your request called the header. Or you might use cookies. These are like invisible name tags that your browser automatically shows to websites you've logged into before. Super convenient. And while you technically can include your credentials as query parameters in a URL, for example, example.com\u002Fquestionmarktoken equals 12345, we don't really recommend this because sometimes URLs can be logged and that's like writing your password on a sticky note where anyone can see it. So remember, authentication isn't just about saying who you are but it's about proving it. Next up, we'll see how this works in practice with Directus. But first, let's return our library card. Here we have a posts collection in a Directus project, but this collection is not publicly accessible, so we will need to authenticate as a user with the requisite permissions in order to access this data. And we see here when we just try and access it, when we try and list the posts, that we get an error. You don't have permission to access the collection posts or it does not exist, which is correct because it doesn't know who we are. We have not authenticated. Now as mentioned, there's a few ways to authenticate. One of the most common is by passing in what is known as an authorization header. So the headers get sent along with our request. The value is bearer space and then a valid access token of a user that has permissions, and when we hit send, we will get this data back. As I mentioned, there is also another way of doing this, which is via a query parameter to your request. So you can include access token equals value, and, again, that will successfully authenticate. The reason I'm showing you this is to actually dissuade you from using this approach because this full URL, you can type it in your browser here, will return data, but this full URL, including your access token, could be logged by your browser history, by your browser extensions, by your Internet service provider, your corporate VPN provider, and so on. And so to keep it secure, we tend to not use this approach too much. I do also wanna show you how to get an access token and authenticate using the Directus SDK. So here we have just a JavaScript file. We are initializing a new Directus SDK instance, and what we're going to do is import the authentication composable. And then we are going to initialize the client with the composable. This gives us a brand new function. We can now type in directives.login and provide our email and our password, these values here. And this client will now be authenticated, so now we can go ahead and actually query our post data. Let's import rest and read items, which we'll need to make this request, and add the rest composable to our client. And now we can go ahead and make a query, directus dot request read items posts. And then we'll console log the items. And this line here specifically is what is authenticating us. Let's see if that works, and we should see app promise pending. Absolutely. We just need to put in a wait there, and we will now see that once the data is returned, it is displayed here. So this is how we authenticate using the SDK. Join me in the next episode of Authentication Avenue where we will cover a brand new topic.","46deecaf-afd5-459a-bdb1-5dc472da7510",[1384],"d615326f-0878-4ff7-992f-18b7d87de16b",[],{"year":399,"number":675,"id":1350,"episodes":1387,"show":1388},[1352,1353,1354,1336,1355],{"title":1357,"slug":1358},{"id":1353,"slug":1390,"vimeo_id":1391,"description":1392,"tile":1393,"length":195,"resources":12,"people":12,"episode_number":177,"published":1341,"title":1394,"video_transcript_html":1395,"video_transcript_text":1396,"content":12,"seo":1397,"status":19,"episode_people":1398,"recommendations":1400,"season":1401},"what-is-access-control","1048670023","It's not just enough to give someone access, but sometimes you need to say what they can do. In this stroll down Authentication Avenue, Kevin answers \"What is access control?\"","f02244b4-07b6-413d-b32c-941ca188cc79","What is access control?","\u003Cp>Speaker 0: Hey, developers. And welcome to authentication Avenue. Today, we're going to explore access control. How systems decide what different users can and can't do. It's like having a clever set of rules that keep everything organized and secure.\u003C\u002Fp>\u003Cp>Imagine you're at a huge museum. When you enter, everyone gets a different colored badge. Red badges are for the curators who can do everything. Blue badges are for the tour guides who can enter public areas. Green badges are for the cleaning staff who can access maintenance areas.\u003C\u002Fp>\u003Cp>And yellow badges are for visitors who can only view exhibits. But it gets even more specific because each badge has special symbols that show exactly what you can do. For example, looking at exhibits, that is the read permission. Creating new displays, that's the create permission. Rearranging exhibits, that's the update permission.\u003C\u002Fp>\u003Cp>And finally, removing displays, that's the delete permission. And here's where it gets interesting. Some staff members might only be allowed to change exhibits that they created themselves. Like a junior curator who can only modify their own displays, but not anyone else's. In the tech world, we call this ownership based access.\u003C\u002Fp>\u003Cp>Now in Directus, this system is handled through roles, permissions, and policies. Roles are like your colored badges, the broad category you belong to. Permissions are a single rule. For example, what collections you can access or what CRUD operations, that's create, read, update, and delete operations, you can perform. Custom permissions like only view published articles and whether you can only work with items you created, and policies are bundles of permissions that can be applied to either roles or individual users.\u003C\u002Fp>\u003Cp>Think of them as rule packages. For example, a content editor role might have policies that allow them to read all articles, create new articles, but only update their own articles, and a content editor might never delete anything or not have the permissions to delete anything. What's cool about policies is their flexibility. You can create one policy, like basic content management, and apply it to both the junior editor role and specific users who need those permissions. It's like creating a standardized set of rules once and then using them wherever you need them.\u003C\u002Fp>\u003Cp>So, remember, access control isn't just about yes or no, it's about who can do what, where, and under what conditions. Next up, we'll see how to configure these permissions in Directus. Here we have a posts collection with a field denoting the author. This is a many to one relationship with the director's users collection, and what this means is that each one of these represents a user in our project that could authenticate with this project. So here we see this one is authored by Beth, 2 by me, and 1 by Carmen.\u003C\u002Fp>\u003Cp>Now we have an access policy here, which denotes how users can interact with the posts collection. Now remember, the public do not have access to this collection. Instead, we say that users who have this policy applied to them can partially read. We say that they can only read items where they are the author, and we do that using this dynamic variable here called current user, and the value of this will change depending on the currently authenticated user. Exactly the same with update and the same with delete.\u003C\u002Fp>\u003Cp>This policy is attached to the member role, which in turn is assigned to Beth, Carmen, and Mike. My user is an admin. So let's see how this works in practice. So here we see we are trying to access the posts collection with no authentication, and we hit send. And as expected, we get a 403 error, which means forbidden, you don't have access to this, or it does not exist.\u003C\u002Fp>\u003Cp>As an admin, if I put in an admin token here, we see that we can access all posts. We see there is an author here beginning with 18b and an author here beginning with be8, and that is because admins have full CRUD access, create, read, update, and delete over all collections in the project. This is the exact same request, but with Beth's token, and we see here that if we hit send, we now only get one item returned. In turn, Beth can only update this item or delete this item, but not those created by other users. This is really nice because it means access control is set up and controlled by directus, which means you cannot inadvertently access data that you're not allowed to in your own application because it's all handled at a direct us level.\u003C\u002Fp>\u003Cp>So that's just a quick example of how access control works inside of Directus. I will see you next time on Authentication Avenue.\u003C\u002Fp>","Hey, developers. And welcome to authentication Avenue. Today, we're going to explore access control. How systems decide what different users can and can't do. It's like having a clever set of rules that keep everything organized and secure. Imagine you're at a huge museum. When you enter, everyone gets a different colored badge. Red badges are for the curators who can do everything. Blue badges are for the tour guides who can enter public areas. Green badges are for the cleaning staff who can access maintenance areas. And yellow badges are for visitors who can only view exhibits. But it gets even more specific because each badge has special symbols that show exactly what you can do. For example, looking at exhibits, that is the read permission. Creating new displays, that's the create permission. Rearranging exhibits, that's the update permission. And finally, removing displays, that's the delete permission. And here's where it gets interesting. Some staff members might only be allowed to change exhibits that they created themselves. Like a junior curator who can only modify their own displays, but not anyone else's. In the tech world, we call this ownership based access. Now in Directus, this system is handled through roles, permissions, and policies. Roles are like your colored badges, the broad category you belong to. Permissions are a single rule. For example, what collections you can access or what CRUD operations, that's create, read, update, and delete operations, you can perform. Custom permissions like only view published articles and whether you can only work with items you created, and policies are bundles of permissions that can be applied to either roles or individual users. Think of them as rule packages. For example, a content editor role might have policies that allow them to read all articles, create new articles, but only update their own articles, and a content editor might never delete anything or not have the permissions to delete anything. What's cool about policies is their flexibility. You can create one policy, like basic content management, and apply it to both the junior editor role and specific users who need those permissions. It's like creating a standardized set of rules once and then using them wherever you need them. So, remember, access control isn't just about yes or no, it's about who can do what, where, and under what conditions. Next up, we'll see how to configure these permissions in Directus. Here we have a posts collection with a field denoting the author. This is a many to one relationship with the director's users collection, and what this means is that each one of these represents a user in our project that could authenticate with this project. So here we see this one is authored by Beth, 2 by me, and 1 by Carmen. Now we have an access policy here, which denotes how users can interact with the posts collection. Now remember, the public do not have access to this collection. Instead, we say that users who have this policy applied to them can partially read. We say that they can only read items where they are the author, and we do that using this dynamic variable here called current user, and the value of this will change depending on the currently authenticated user. Exactly the same with update and the same with delete. This policy is attached to the member role, which in turn is assigned to Beth, Carmen, and Mike. My user is an admin. So let's see how this works in practice. So here we see we are trying to access the posts collection with no authentication, and we hit send. And as expected, we get a 403 error, which means forbidden, you don't have access to this, or it does not exist. As an admin, if I put in an admin token here, we see that we can access all posts. We see there is an author here beginning with 18b and an author here beginning with be8, and that is because admins have full CRUD access, create, read, update, and delete over all collections in the project. This is the exact same request, but with Beth's token, and we see here that if we hit send, we now only get one item returned. In turn, Beth can only update this item or delete this item, but not those created by other users. This is really nice because it means access control is set up and controlled by directus, which means you cannot inadvertently access data that you're not allowed to in your own application because it's all handled at a direct us level. So that's just a quick example of how access control works inside of Directus. I will see you next time on Authentication Avenue.","a0b96fa2-f5e3-4b0b-8083-80a555a7aa49",[1399],"b32df2cb-479f-4aba-9aac-cd2933d6448d",[],{"year":399,"number":675,"id":1350,"episodes":1402,"show":1403},[1352,1353,1354,1336,1355],{"title":1357,"slug":1358},{"id":1354,"slug":1405,"vimeo_id":1406,"description":1407,"tile":1408,"length":129,"resources":12,"people":12,"episode_number":264,"published":1341,"title":1409,"video_transcript_html":1410,"video_transcript_text":1411,"content":12,"seo":1412,"status":19,"episode_people":1413,"recommendations":1415,"season":1416},"why-do-tokens-expire","1048670160","You can't have access forever. In this skip down Authentication Avenue, Kevin answers \"Why do tokens expire?\" and shows you how to get new ones.","f1fa8472-da39-4d05-ad8d-8e891e8b1f55","Why do tokens expire?","\u003Cp>Speaker 0: Hey, developers. Welcome to authentication avenue. You know how spies in movies have secret codes that expire after a single use? Well, in modern applications, we do something similar with access tokens. Today, we're exploring why these digital passes have expiration dates and the clever system that keeps users logged in securely.\u003C\u002Fp>\u003Cp>Imagine you're visiting a big amusement park. When you arrive you get 2 things, a ride pass that's good for just one day, that's like your access token, and a special VIP card that's good for the whole year, That's like your refresh token. Now when your day pass expires, you don't need to go back to the ticket counter and buy a new ticket. Instead, you can show your VIP card at the special kiosk, and it will print you a fresh day pass on your next visit. And this is exactly how modern applications handle access.\u003C\u002Fp>\u003Cp>Your access token is like that day pass. It lets you go on rides or in our case make API requests, but it expires quite quickly. The refresh token is like the VIP card. It's valid for much longer and lets you get new access tokens without logging in again. But why not just make the access token last forever?\u003C\u002Fp>\u003Cp>Well, imagine if someone stole your day pass, they can use it till the end of the day. But, if they stole a much longer pass, say for the whole year, they could keep using it forever. And that's why we keep access tokens short lived, it's safer that way. In Directus, here is how it works. When you log in, you get a package of data containing an access token, usually valid for 15 minutes, although you can configure that, A refresh token, valid for much longer, and an expiration time stamp.\u003C\u002Fp>\u003Cp>Now when your access token expires, you call the refresh endpoint with your refresh token, and then get a fresh package of tokens. And if you want to log out, you can destroy these tokens, just like shredding your park passes so nobody else can use them. This is especially important when using shared computers or if you think your tokens might be compromised. So there you have it. Token expiration isn't just an inconvenience, it's a crucial security feature that helps keep your application safe.\u003C\u002Fp>\u003Cp>Let's see how it works in Directus. I wanna show you how to interact with Directus' authentication endpoints via API, and we're going to start with the login endpoint. So this is a post request to \u002Fauth\u002Flogin. In the body, we provide the email and password, and when we send that, we receive a packet of data back. We what?\u003C\u002Fp>\u003Cp>Oh, there we go. We have an an access token, which we can use in order to make requests. We have a refresh token and an expiry time. This is 30,000 milliseconds or 30 seconds. I've set it to be quite short in this project to demonstrate my point.\u003C\u002Fp>\u003Cp>Now we can use this access token here in order to authenticate requests and retrieve data, but as you might remember, we've only set this to 30 seconds before expiry. So if I try and hit send again, we see that the token has expired. This whole token is no longer usable. So what do we do? We won't always be able to send off an email and password because we may no longer have access to them.\u003C\u002Fp>\u003Cp>You don't necessarily want to store them, so we use the refresh token. Now the refresh token also expires, but a much, much longer time. I believe the default is 7 days, but it is configurable just like the access token is, which I set to 30 seconds here. So we have this refresh token, and we also have a refresh endpoint, post slash auth slash refresh. We provide our refresh token, and what we receive back is the same data packet, an access token, a refresh token, and the amount of time until it expires.\u003C\u002Fp>\u003Cp>You can now use this new access token in order to authenticate requests. And once again, this refresh token is now spent, it no longer works. You can now use the new refresh token in order to receive a new packet of data. And once you're done and you want to log out, you can hit \u002Fauth\u002Flogoutpost with your refresh token, that is not the refresh token, with your refresh token, and you will receive a 204, which is a successful request, but with no content returned. And now the tokens, including the refresh token, is, invalidated, and you can no longer access it.\u003C\u002Fp>\u003Cp>Now when you're working with the SDK by comparison, the SDK handles all of this for you. It will handle the storage of your tokens, it will handle the refreshing of your tokens for you as soon as you log in. However, we do also provide explicit, methods, request, refresh, here it is, and log out, a refresh function and a log out method in order to run those same commands as the API. But in most use cases, you won't need these. You log in, and as you make requests, the SDK will handle the refreshing of tokens on your behalf.\u003C\u002Fp>\u003Cp>So hopefully, that helps explain the relationship between access tokens, refresh tokens, expiry time, and how you actually work with the API in order to generate and regenerate them. Thank you for joining me on Authentication Avenue, and I'll see you next time.\u003C\u002Fp>","Hey, developers. Welcome to authentication avenue. You know how spies in movies have secret codes that expire after a single use? Well, in modern applications, we do something similar with access tokens. Today, we're exploring why these digital passes have expiration dates and the clever system that keeps users logged in securely. Imagine you're visiting a big amusement park. When you arrive you get 2 things, a ride pass that's good for just one day, that's like your access token, and a special VIP card that's good for the whole year, That's like your refresh token. Now when your day pass expires, you don't need to go back to the ticket counter and buy a new ticket. Instead, you can show your VIP card at the special kiosk, and it will print you a fresh day pass on your next visit. And this is exactly how modern applications handle access. Your access token is like that day pass. It lets you go on rides or in our case make API requests, but it expires quite quickly. The refresh token is like the VIP card. It's valid for much longer and lets you get new access tokens without logging in again. But why not just make the access token last forever? Well, imagine if someone stole your day pass, they can use it till the end of the day. But, if they stole a much longer pass, say for the whole year, they could keep using it forever. And that's why we keep access tokens short lived, it's safer that way. In Directus, here is how it works. When you log in, you get a package of data containing an access token, usually valid for 15 minutes, although you can configure that, A refresh token, valid for much longer, and an expiration time stamp. Now when your access token expires, you call the refresh endpoint with your refresh token, and then get a fresh package of tokens. And if you want to log out, you can destroy these tokens, just like shredding your park passes so nobody else can use them. This is especially important when using shared computers or if you think your tokens might be compromised. So there you have it. Token expiration isn't just an inconvenience, it's a crucial security feature that helps keep your application safe. Let's see how it works in Directus. I wanna show you how to interact with Directus' authentication endpoints via API, and we're going to start with the login endpoint. So this is a post request to \u002Fauth\u002Flogin. In the body, we provide the email and password, and when we send that, we receive a packet of data back. We what? Oh, there we go. We have an an access token, which we can use in order to make requests. We have a refresh token and an expiry time. This is 30,000 milliseconds or 30 seconds. I've set it to be quite short in this project to demonstrate my point. Now we can use this access token here in order to authenticate requests and retrieve data, but as you might remember, we've only set this to 30 seconds before expiry. So if I try and hit send again, we see that the token has expired. This whole token is no longer usable. So what do we do? We won't always be able to send off an email and password because we may no longer have access to them. You don't necessarily want to store them, so we use the refresh token. Now the refresh token also expires, but a much, much longer time. I believe the default is 7 days, but it is configurable just like the access token is, which I set to 30 seconds here. So we have this refresh token, and we also have a refresh endpoint, post slash auth slash refresh. We provide our refresh token, and what we receive back is the same data packet, an access token, a refresh token, and the amount of time until it expires. You can now use this new access token in order to authenticate requests. And once again, this refresh token is now spent, it no longer works. You can now use the new refresh token in order to receive a new packet of data. And once you're done and you want to log out, you can hit \u002Fauth\u002Flogoutpost with your refresh token, that is not the refresh token, with your refresh token, and you will receive a 204, which is a successful request, but with no content returned. And now the tokens, including the refresh token, is, invalidated, and you can no longer access it. Now when you're working with the SDK by comparison, the SDK handles all of this for you. It will handle the storage of your tokens, it will handle the refreshing of your tokens for you as soon as you log in. However, we do also provide explicit, methods, request, refresh, here it is, and log out, a refresh function and a log out method in order to run those same commands as the API. But in most use cases, you won't need these. You log in, and as you make requests, the SDK will handle the refreshing of tokens on your behalf. So hopefully, that helps explain the relationship between access tokens, refresh tokens, expiry time, and how you actually work with the API in order to generate and regenerate them. Thank you for joining me on Authentication Avenue, and I'll see you next time.","a9f13359-7b06-4f50-a2c3-da41d60e927c",[1414],"5b464654-0785-4256-9c30-9e61ee7f8205",[],{"year":399,"number":675,"id":1350,"episodes":1417,"show":1418},[1352,1353,1354,1336,1355],{"title":1357,"slug":1358},{"id":1420,"slug":1421,"vimeo_id":1422,"description":1423,"tile":1424,"length":159,"resources":12,"people":12,"episode_number":213,"published":1425,"title":1426,"video_transcript_html":1427,"video_transcript_text":1428,"content":12,"seo":1429,"status":19,"episode_people":1430,"recommendations":1432,"season":1433},"d4f2017e-da21-4e9d-ae37-efd26d36c845","bobby-tail-book-return-machine","1041539655","Bobby Tail learns about the automation concepts of flows, triggers and operations. Read by Bryant Gillespie.\n","ff26449c-2dc4-44e5-8445-4486e55cfd31","2024-12-25","Bobby Tail and the Book Return Machine","\u003Cp>Speaker 0: Bobby Tale and the Book Return Machine, read by Bryant Gillespie. Bobby Tale had a problem. The tree library's return box was overflowing. Books were getting mixed up and some were even going missing. There must be a better way, he said, watching another stack of books tumble over.\u003C\u002Fp>\u003Cp>Just then, one book slid down a branch and landed perfectly on its shelf. Bobby's eyes lit up. What if we could make all the books find their way home? The Loop Squirrel sisters bounced with excitement when Bobby shared his idea. We could use slides, Lily squeaked.\u003C\u002Fp>\u003Cp>And please, Luna added. Together, they drew plans for a magical machine that would sort and return books automatically. Binary Badger adjusted his pocket watch and nodded approvingly. Every machine needs a trigger to start, he explained, just like my watch needs winding. The bug brothers arrived to help test each part.\u003C\u002Fp>\u003Cp>1st, we need a special door, said syntax Cricket, wearing his green scarf. Logic Cricket, in blue, added, when a book touches the door, that's our trigger. Run time cricket in Red started his stopwatch. Then the magic begins. The Array Mole family popped up next, each wearing their numbered sweaters.\u003C\u002Fp>\u003Cp>Different books need different homes, they squeaked in unison. They built special chutes, one for storybooks, one for picture books, and a special golden chute for magical books. Each path glowed with its own special color. Finally, Daisy debug duck walked over with her magnifying glass. Time to test everything, she declared.\u003C\u002Fp>\u003Cp>They dropped a story book into the return slot. It slid down, triggered a small bell, activated a pulley, and rode down a spiral slide. And then, plop, it landed in the wrong place. Back to work, Daisy quacked cheerfully. The best machines need plenty of testing.\u003C\u002Fp>\u003Cp>Each friend added something special to help the machine work better. Variable vol brought measuring spoons to balance the pulleys. The Loop sisters adjusted the slides until they curved just right. Binary badger added tiny bells to signal when each book passed by. Soon, the machine was humming with magical efficiency.\u003C\u002Fp>\u003Cp>At last, the book machine was complete. Bobby Tell watched in delight as books danced through the sorting system and landed gently in their proper places. It's like magic, he whispered, but better because we built it together. The tree library had never been so organized, and Bobby had never been so proud. This story covers the automation concepts of flows, triggers, and automations.\u003C\u002Fp>","Bobby Tale and the Book Return Machine, read by Bryant Gillespie. Bobby Tale had a problem. The tree library's return box was overflowing. Books were getting mixed up and some were even going missing. There must be a better way, he said, watching another stack of books tumble over. Just then, one book slid down a branch and landed perfectly on its shelf. Bobby's eyes lit up. What if we could make all the books find their way home? The Loop Squirrel sisters bounced with excitement when Bobby shared his idea. We could use slides, Lily squeaked. And please, Luna added. Together, they drew plans for a magical machine that would sort and return books automatically. Binary Badger adjusted his pocket watch and nodded approvingly. Every machine needs a trigger to start, he explained, just like my watch needs winding. The bug brothers arrived to help test each part. 1st, we need a special door, said syntax Cricket, wearing his green scarf. Logic Cricket, in blue, added, when a book touches the door, that's our trigger. Run time cricket in Red started his stopwatch. Then the magic begins. The Array Mole family popped up next, each wearing their numbered sweaters. Different books need different homes, they squeaked in unison. They built special chutes, one for storybooks, one for picture books, and a special golden chute for magical books. Each path glowed with its own special color. Finally, Daisy debug duck walked over with her magnifying glass. Time to test everything, she declared. They dropped a story book into the return slot. It slid down, triggered a small bell, activated a pulley, and rode down a spiral slide. And then, plop, it landed in the wrong place. Back to work, Daisy quacked cheerfully. The best machines need plenty of testing. Each friend added something special to help the machine work better. Variable vol brought measuring spoons to balance the pulleys. The Loop sisters adjusted the slides until they curved just right. Binary badger added tiny bells to signal when each book passed by. Soon, the machine was humming with magical efficiency. At last, the book machine was complete. Bobby Tell watched in delight as books danced through the sorting system and landed gently in their proper places. It's like magic, he whispered, but better because we built it together. The tree library had never been so organized, and Bobby had never been so proud. This story covers the automation concepts of flows, triggers, and automations.","d1d7a9a7-ec34-4d9b-84b6-c1606ce12cd7",[1431],"026b94c5-41af-44b0-9450-ecbbe968c22f",[],{"year":978,"number":480,"id":1434,"episodes":1435,"show":1441},"abd92d57-b704-43fb-9627-960c5bb8bafb",[1436,1437,1438,1439,1420,1440],"dc7374e7-0222-4b3d-af7e-861b0b539f2f","688f81cf-5ec4-4e0e-9d5c-6b1402e2e7c7","4aede222-b326-4fc4-938a-2bf7dcfb1bf0","dd5c7534-2330-4383-b352-f99be0c75f2d","c8db927f-574a-4816-bfae-01fea3c0fa12",{"title":1442,"slug":1443},"Bobby Tail's Little Library","bobby-tails-little-library",{"id":1439,"slug":1445,"vimeo_id":1446,"description":1447,"tile":1448,"length":159,"resources":12,"people":12,"episode_number":159,"published":1425,"title":1449,"video_transcript_html":1450,"video_transcript_text":1451,"content":12,"seo":1452,"status":19,"episode_people":1453,"recommendations":1454,"season":1455},"bobby-tail-always-awake-library","1041539753","Bobby Tail learns about the concepts of Realtime, WebSockets and GraphQL. Read by Daniel Roe.\n","5fb298d6-94ad-4696-ad18-595bed991f05","Bobby Tail and the Always-Awake Library","\u003Cp>Speaker 0: Bobbie tale and the always awake library, read by Daniel Roe. Bobbie tale was arranging new books on the tree library shelves when he heard a disappointed sigh. Oh, if only I'd known you had butterfly books yesterday, said a young mouse. I walked all the way here for flower books instead. Bobby's ears drooped.\u003C\u002Fp>\u003Cp>Just then, he noticed professor owl Turing, watching thoughtfully from his favorite perch. What we need? Professor Owl hooted wisely, adjusting his spectacles, is a way to make the library always away. When something changes here, everyone should know right away. Binary badger nodded, his pocket watch ticking.\u003C\u002Fp>\u003Cp>Like how my watch always shows the exact time everywhere you look at it. The loop squirrel sisters bounced with excitement. We can carry messages, Lily chirped. Faster than fast, Luna added. They began stringing magical ribbons between all the library windows.\u003C\u002Fp>\u003Cp>When the new book arrives, they explained together, these ribbons will shimmer and show its picture everywhere at once. Daisy debug duck waddled over, magnifying glass and wing. But what if someone wants to know about specific books? She asked. The array mole family popped up wearing their numbered sweaters.\u003C\u002Fp>\u003Cp>We can make magical mirrors, they squeaked. Just whisper what you're looking for and the mirror will show you exactly where to find it. Variable before pulled special tools from her patchwork vest. Crystal bells to alert readers of changes. Tiny mirrors for specific questions, and silver thread to connect everything together.\u003C\u002Fp>\u003Cp>Now, we can make sure everyone knows about new books right away, she explained, her vest pockets glowing with magic. Soon, the tree library was more alive than ever. When Bobby placed a new butterfly book on the shelf, all the magical ribbons sparkled with butterfly pictures. Woodland creatures could ask the mirrors about specific stories and instantly see where to find them. No more disappointed sighs, everyone knew about new books right away.\u003C\u002Fp>\u003Cp>The library truly never sleeps now, Bobby said proudly, watching a young rabbit discover a new story through one of the magical mirrors. Professor Owl nodded approvingly from his perch. Thanks to everyone's help, the tree library had become a place where books and readers could find each other instantly, any time of day. This story covers the concepts of real time WebSockets and GraphQL.\u003C\u002Fp>","Bobbie tale and the always awake library, read by Daniel Roe. Bobbie tale was arranging new books on the tree library shelves when he heard a disappointed sigh. Oh, if only I'd known you had butterfly books yesterday, said a young mouse. I walked all the way here for flower books instead. Bobby's ears drooped. Just then, he noticed professor owl Turing, watching thoughtfully from his favorite perch. What we need? Professor Owl hooted wisely, adjusting his spectacles, is a way to make the library always away. When something changes here, everyone should know right away. Binary badger nodded, his pocket watch ticking. Like how my watch always shows the exact time everywhere you look at it. The loop squirrel sisters bounced with excitement. We can carry messages, Lily chirped. Faster than fast, Luna added. They began stringing magical ribbons between all the library windows. When the new book arrives, they explained together, these ribbons will shimmer and show its picture everywhere at once. Daisy debug duck waddled over, magnifying glass and wing. But what if someone wants to know about specific books? She asked. The array mole family popped up wearing their numbered sweaters. We can make magical mirrors, they squeaked. Just whisper what you're looking for and the mirror will show you exactly where to find it. Variable before pulled special tools from her patchwork vest. Crystal bells to alert readers of changes. Tiny mirrors for specific questions, and silver thread to connect everything together. Now, we can make sure everyone knows about new books right away, she explained, her vest pockets glowing with magic. Soon, the tree library was more alive than ever. When Bobby placed a new butterfly book on the shelf, all the magical ribbons sparkled with butterfly pictures. Woodland creatures could ask the mirrors about specific stories and instantly see where to find them. No more disappointed sighs, everyone knew about new books right away. The library truly never sleeps now, Bobby said proudly, watching a young rabbit discover a new story through one of the magical mirrors. Professor Owl nodded approvingly from his perch. Thanks to everyone's help, the tree library had become a place where books and readers could find each other instantly, any time of day. This story covers the concepts of real time WebSockets and GraphQL.","61d7e070-037a-44f0-a06e-3a8ef1e3a85e",[],[],{"year":978,"number":480,"id":1434,"episodes":1456,"show":1457},[1436,1437,1438,1439,1420,1440],{"title":1442,"slug":1443},{"id":1440,"slug":1459,"vimeo_id":1460,"description":1461,"tile":1462,"length":264,"resources":12,"people":12,"episode_number":195,"published":1425,"title":1463,"video_transcript_html":1464,"video_transcript_text":1465,"content":12,"seo":1466,"status":19,"episode_people":1467,"recommendations":1469,"season":1470},"bobby-tail-winter-reading-rush","1041539857","Bobby Tail learns about Directus insights, dashboards, panels and charts. Read by Mike Elsmore.","a79a994c-9f34-433d-92e5-e2940805c919","Bobby Tail and the Winter Reading Rush","\u003Cp>Speaker 0: Bobby Tail and the Winter Reading Rush. Read by Mike Ellsmore. The first frost that dusted the tree library's leaves with silver when Bobby Tail noticed something strange. Long lines of woodland creatures stretched from every door. Winter's coming, whispered a sleepy hedgehog, and we all need books to read during the cold days.\u003C\u002Fp>\n\u003Cp>Bobby's ears twitched with worry. How could he make sure everyone got enough books for winter? Barnaby Badger arrived, his pocket watch frosted with ice. We need a special system, he declared. With a wave of his watch, different magical tapestries appeared in the air.\u003C\u002Fp>\n\u003Cp>One showed floating snowflakes that counted available books, another had glowing icicles measuring return times, and a third created a rainbow arch showing different types of winter stories. Each panel tells us something different about our library, Bobby exclaimed excitedly. The arraymowl family popped up, their numbered sweaters now topped with warm scarves. Look what we can build this week, creating more magical visual templates. They stacked shivering acorns into climbing towers to show how many books each animal group needed.\u003C\u002Fp>\n\u003Cp>There were strands of sparkling web between related books, making a beautiful pattern that showed which stories went together. Finally, they created a timeline of twinkling stars showing when books would return to the library. Lily and Luna loop squirrels swung down from their branches. We can track the most popular winter stories Lily began, and make sure everyone gets their favourite kind, Luna finished. They created swirling paths of light showing how books could be shared between different groups of readers.\u003C\u002Fp>\n\u003Cp>Daisy Debook Duck waddled in from the snow, her magnifying glass catching snowflakes. Let's check its fur, she quacked, examining their magical blackboard. Together, they adjusted the glowing displays until every creature would get just the right number of books to last until spring. As the first snow began to fall, Bobbietail watched their tapestry of information spring to life. Crystal timers showed when books would return, floating acorns tracked what was available, and glowing paths helped every creature find what they needed.\u003C\u002Fp>\n\u003Cp>The tree library had never been so organized. That winter was the tree library's busiest ever, but thanks to the magical dashboard, every creature had plenty to read. Bears cuddled up with tall tales for their long sleep, squirrels had nutty adventures to enjoy in their nests, and even the smallest mice had tiny tails to keep them cozy. Bobbie Tail smiled, watching happy readers heading home with through the snow. The concepts in this book include director's insights, dashboards, panels, and charts.\u003C\u002Fp>","Bobbie Tail and the Winter Reading Rush. Read by Mike Ellsmore. The first frost that dusted the tree library's leaves with silver when Bobbie Tail noticed something strange. Long lines of woodland creatures stretched from every door. Winter's coming, whispered a sleepy hedgehog, and we all need books to read during the cold days. Bobby's ears twitched with worry. How could he make sure everyone got enough books for winter? Barnaby Badger arrived, his pocket watch frosted with ice. We need a special system, he declared. With a wave of his watch, different magical tapestries appeared in the air. One showed floating snowflakes that counted available books, another had glowing icicles measuring return times, and a third created a rainbow arch showing different types of winter stories. Each panel tells us something different about our library, Bobby exclaimed excitedly. The arraymowl family popped up, their numbered sweaters now topped with warm scarves. Look what we can build this week, creating more magical visual templates. They stacked shivering acorns into climbing towers to show how many books each animal group needed. There were strands of sparkling web between related books, making a beautiful pattern that showed which stories went together. Finally, they created a timeline of twinkling stars showing when books would return to the library. Lily and Luna loop squirrels swung down from their branches. We can track the most popular winter stories Lily began, and make sure everyone gets their favourite kind, Luna finished. They created swirling paths of light showing how books could be shared between different groups of readers. Daisy Debook Duck waddled in from the snow, her magnifying glass catching snowflakes. Let's check its fur, she quacked, examining their magical blackboard. Together, they adjusted the glowing displays until every creature would get just the right number of books to last until spring. As the first snow began to fall, Bobbietail watched their tapestry of information spring to life. Crystal timers showed when books would return, floating acorns tracked what was available, and glowing paths helped every creature find what they needed. The tree library had never been so organized. That winter was the tree library's busiest ever, but thanks to the magical dashboard, every creature had plenty to read. Bears cuddled up with tall tales for their long sleep, squirrels had nutty adventures to enjoy in their nests, and even the smallest mice had tiny tails to keep them cozy. Bobbie Tail smiled, watching happy readers heading home with through the snow. The concepts in this book include director's insights, dashboards, panels, and charts.","1d75926e-5a36-46e2-9a45-ece7639da564",[1468],"6b4b2e80-4586-4ea8-872b-62aaa2fba092",[],{"year":978,"number":480,"id":1434,"episodes":1471,"show":1472},[1436,1437,1438,1439,1420,1440],{"title":1442,"slug":1443},{"id":1438,"slug":1474,"vimeo_id":1475,"description":1476,"tile":1477,"length":264,"resources":12,"people":12,"episode_number":264,"published":1478,"title":1479,"video_transcript_html":1480,"video_transcript_text":1481,"content":12,"seo":1482,"status":19,"episode_people":1483,"recommendations":1484,"season":1485},"bobby-tail-library-construction-crew","1037458018","Bobby Tail learns about the backend concepts of data modeling, APIs and file management. Read by Cathrin Steiner.","5f911a0e-13b5-4636-adb2-704ac49a41e2","2024-12-10","Bobby Tail and the Library Construction Crew","\u003Cp>Speaker 0: Bobby Tail and the library construction crew, read by Catherin Steiner. Bobby Tail had a big problem. The tree library was running out of space. Books were stacked in every corner, and new stories arrived each day. We need to build a new wing, he said, adjusting his blue vest.\u003C\u002Fp>\n\u003Cp>But building a library wasn't like building a regular treehouse. Everything needed to be perfectly organized. Binary badger arrived first, pocket watch ticking. Before we build, he said, we need a proper blueprint. Just like a tree needs strong roots, a library needs a strong foundation.\u003C\u002Fp>\n\u003Cp>Together, they drew plans for different rooms, one for storybooks, another for pictures, and a special vault for magical scrolls. The Raymould family popped up from their tunnels, each wearing their numbered sweaters. We'll help organize everything, they squeaked excitedly. Each book needs its special place, just like we have our numbered homes. That way, we'll always know where to find things.\u003C\u002Fp>\n\u003Cp>Lily and Luna, the loop scroll sisters, swished their tails as they tested different bridges between the library sections. We need magical pathways, Lily began, so everyone can find exactly what they're looking for, Luna finished. They created special rope bridges that connected every room to every other room. Daisy Debuck Duck waddled in with her magnifying glass. 'Let's test everything,' she declared.\u003C\u002Fp>\n\u003Cp>She checked each bridge, each shelf, and each room. We must make sure every friend can find their books easily, she explained, marking items off her checklist with her wing. Variable Vole arrived with her magical bag. Now for the special items, she announced. Together, they created cozy reading nooks, picture frame holders, and scroll cases.\u003C\u002Fp>\n\u003Cp>Everything had its perfect spot, ready for all kinds of woodland treasures. Finally, the new library wing was complete. Bobby Tayl stood proudly with his construction crew. The Arraymalls organized the books, the Loop Sisters bridges connected everything perfectly, and Daisy Duck had tested every path. It was more than just a library, it was a magical place, where every piece of knowledge had a whole.\u003C\u002Fp>\n\u003Cp>This story introduces the back end concepts of data modeling, APIs, and file management.\u003C\u002Fp>","Bobby Tail and the library construction crew, read by Catherin Steiner. Bobby Tail had a big problem. The tree library was running out of space. Books were stacked in every corner, and new stories arrived each day. We need to build a new wing, he said, adjusting his blue vest. But building a library wasn't like building a regular treehouse. Everything needed to be perfectly organized. Binary badger arrived first, pocket watch ticking. Before we build, he said, we need a proper blueprint. Just like a tree needs strong roots, a library needs a strong foundation. Together, they drew plans for different rooms, one for storybooks, another for pictures, and a special vault for magical scrolls. The Raymould family popped up from their tunnels, each wearing their numbered sweaters. We'll help organize everything, they squeaked excitedly. Each book needs its special place, just like we have our numbered homes. That way, we'll always know where to find things. Lily and Luna, the loop scroll sisters, swished their tails as they tested different bridges between the library sections. We need magical pathways, Lily began, so everyone can find exactly what they're looking for, Luna finished. They created special rope bridges that connected every room to every other room. Daisy Debuck Duck waddled in with her magnifying glass. 'Let's test everything,' she declared. She checked each bridge, each shelf, and each room. We must make sure every friend can find their books easily, she explained, marking items off her checklist with her wing. Variable Vole arrived with her magical bag. Now for the special items, she announced. Together, they created cozy reading nooks, picture frame holders, and scroll cases. Everything had its perfect spot, ready for all kinds of woodland treasures. Finally, the new library wing was complete. Bobby Tayl stood proudly with his construction crew. The Arraymalls organized the books, the Loop Sisters bridges connected everything perfectly, and Daisy Duck had tested every path. It was more than just a library, it was a magical place, where every piece of knowledge had a whole. This story introduces the back end concepts of data modeling, APIs, and file management.","550cb7eb-e9b6-4bfb-8687-4c5d671aecca",[],[],{"year":978,"number":480,"id":1434,"episodes":1486,"show":1487},[1436,1437,1438,1439,1420,1440],{"title":1442,"slug":1443},{"id":1436,"slug":1489,"vimeo_id":1490,"description":1491,"tile":1492,"length":213,"resources":12,"people":12,"episode_number":13,"published":1478,"title":1493,"video_transcript_html":1494,"video_transcript_text":1495,"content":12,"seo":1496,"status":19,"episode_people":1497,"recommendations":1499,"season":1500},"bobby-tail-digital-library","1037457756","Bobby Tail learns about Directus and its many capabilities, including the Data Studio and Data Engine. Read by Beth Loft.","d7484c80-e880-4ae8-90cc-43873f7d391a","Bobby Tail and the Digital Library","\u003Cp>Speaker 0: Bobby Tail and the Digital Library, read by Beth Watt. Bobby Tail peered into his notebook. His brow furrowed with confusion. There must be a better way to keep track of all of these wonderful discoveries, he mumbled, looking at the pages filled with scribbled notes, drawings, and these papers threatening to fall out. His blue vest pockets were bulging with scattered information, and he knew something had to change.\u003C\u002Fp>\u003Cp>Just then, professor Owl Turing swooped it down from his library in the tall tree, adjusting his wire rimmed spectacles. Bobby, he hooted softly, I see you're still struggling with organizing your discoveries. What if I told you there's a magical way to keep all your information neat, safe, and easy to find? Professor Owl explained how data platforms were like enormous magical libraries where information could live. Imagine a place, he said, where you could store pictures, notes, numbers, and stories, all organized perfectly and accessible from anywhere.\u003C\u002Fp>\u003Cp>Bobby's ears packed up with excitement, his imagination spinning with possibilities. But how does it work? Bobby asked pulling out his notebook. Daisy debuggeduck waddled up, her magnifying glass at the ready. Let me show you, she quacked.\u003C\u002Fp>\u003Cp>Data platforms are like magical sorting systems. They can take information from anywhere and organize it perfectly, no matter what kind of information it is. The Loop squirrel sisters, Lily and Luna, joined the explanation. Think of it like our family's organized tunnel system, Lily explained. Each piece of information has its own special place, But we can move things around super quickly, Luna finished, and we can find anything in seconds.\u003C\u002Fp>\u003Cp>Binary Badger stepped forward, his black and white fur highlighting his precise nature. These platforms aren't just about storing data, he explained checking his pocket watch. They're about making that data work for you. You can ask questions, get reports, and connect different kinds of information instantly. But what makes these platforms so special?\u003C\u002Fp>\u003Cp>Bobby asked. Variable pulled out his magical bag. They're flexible! He exclaimed. Unlike all systems that were rigid, these platforms can grow and change.\u003C\u002Fp>\u003Cp>You can add new types of information, connect to different tools, and always keep everything safe. The string swan gracefully glided into the conversation. And the best part? Security, she explained, her alphabet bead necklace sparkling. These platforms protect your information, making sure only the right creatures can see the right data.\u003C\u002Fp>\u003Cp>It's like a super smart lock on a very special diary. So these platforms could help everyone? Bobby asked. Bullion Bear, with his binary pattern scarf, nodded. From tiny businesses to giant organizations, from artists to scientists, everyone can use these magical systems to make their work easier and more connected.\u003C\u002Fp>\u003Cp>Bobby Tail closed his notebook, now organized and neat. He understood now. Data platforms weren't just about storing information. They were about connecting ideas, protecting knowledge, and making the world a little bit more magical. I can't wait to tell everyone, he exclaimed, his vest pockets now perfectly arranged.\u003C\u002Fp>\u003Cp>This story introduces Directus and its many capabilities, including the Data Studio and Data Engine.\u003C\u002Fp>","Bobby Tail and the Digital Library, read by Beth Watt. Bobby Tail peered into his notebook. His brow furrowed with confusion. There must be a better way to keep track of all of these wonderful discoveries, he mumbled, looking at the pages filled with scribbled notes, drawings, and these papers threatening to fall out. His blue vest pockets were bulging with scattered information, and he knew something had to change. Just then, professor Owl Turing swooped it down from his library in the tall tree, adjusting his wire rimmed spectacles. Bobby, he hooted softly, I see you're still struggling with organizing your discoveries. What if I told you there's a magical way to keep all your information neat, safe, and easy to find? Professor Owl explained how data platforms were like enormous magical libraries where information could live. Imagine a place, he said, where you could store pictures, notes, numbers, and stories, all organized perfectly and accessible from anywhere. Bobby's ears packed up with excitement, his imagination spinning with possibilities. But how does it work? Bobby asked pulling out his notebook. Daisy debuggeduck waddled up, her magnifying glass at the ready. Let me show you, she quacked. Data platforms are like magical sorting systems. They can take information from anywhere and organize it perfectly, no matter what kind of information it is. The Loop squirrel sisters, Lily and Luna, joined the explanation. Think of it like our family's organized tunnel system, Lily explained. Each piece of information has its own special place, But we can move things around super quickly, Luna finished, and we can find anything in seconds. Binary Badger stepped forward, his black and white fur highlighting his precise nature. These platforms aren't just about storing data, he explained checking his pocket watch. They're about making that data work for you. You can ask questions, get reports, and connect different kinds of information instantly. But what makes these platforms so special? Bobby asked. Variable pulled out his magical bag. They're flexible! He exclaimed. Unlike all systems that were rigid, these platforms can grow and change. You can add new types of information, connect to different tools, and always keep everything safe. The string swan gracefully glided into the conversation. And the best part? Security, she explained, her alphabet bead necklace sparkling. These platforms protect your information, making sure only the right creatures can see the right data. It's like a super smart lock on a very special diary. So these platforms could help everyone? Bobby asked. Bullion Bear, with his binary pattern scarf, nodded. From tiny businesses to giant organizations, from artists to scientists, everyone can use these magical systems to make their work easier and more connected. Bobby Tail closed his notebook, now organized and neat. He understood now. Data platforms weren't just about storing information. They were about connecting ideas, protecting knowledge, and making the world a little bit more magical. I can't wait to tell everyone, he exclaimed, his vest pockets now perfectly arranged. This story introduces Directus and its many capabilities, including the Data Studio and Data Engine.","9c73586e-f812-45f8-9ef2-f6b4bf882dd2",[1498],"836d0156-3198-43b7-b3ab-551196f9eac6",[],{"year":978,"number":480,"id":1434,"episodes":1501,"show":1502},[1436,1437,1438,1439,1420,1440],{"title":1442,"slug":1443},{"id":1437,"slug":1504,"vimeo_id":1505,"description":1506,"tile":1507,"length":264,"resources":12,"people":12,"episode_number":177,"published":1478,"title":1508,"video_transcript_html":1509,"video_transcript_text":1510,"content":12,"seo":1511,"status":19,"episode_people":1512,"recommendations":1514,"season":1515},"bobby-tail-magical-guest-list","1037457584","Bobby Tail learns about the authentication concepts of usernames, password, two-factor authentication, access control, and single sign-on. Read by Carmen Huidobro.","9e3772a9-8390-49c8-ac4a-be663f19dd6b","Bobby Tail and the Magical Guest List","\u003Cp>Speaker 0: Bobby Taylor and the magical guest list, read by Carmen Huidobro. Bobby Taylor was planning something extraordinary, a surprise party for professor owl Turing's birthday in the tree library. But Bobby knew he couldn't just let anyone wander in. I need a magical way to make sure only the right friends can enter, he thought, adjusting the gold buttons on his blue vest. Daisy debugged duck, Bobby called.\u003C\u002Fp>\n\u003Cp>I need help creating a magical guest list that only invited friends can see. Daisy waddled over her oversized magnifying glass swinging from her wing. We will use authentication, she explained. It's like creating a magical key that only the right friends can use. The Loop squirrel sisters, Lily and Luna, bounced into the conversation.\u003C\u002Fp>\n\u003Cp>We can help create user accounts, they tripped in unison. Each friend will get a special username and password just like the secret handshake that lets them enter the party. Binary Badger stepped forward, adjusting his pocket watch. We'll add an extra layer of protection, he explained carefully. 2 factor authentication is like having 2 special locks.\u003C\u002Fp>\n\u003Cp>First, a password, then a magical code that changes every time, making sure only the true friends can enter. Shh, Bobby Taylor whispered to his friends. Professor Owl can't know about the party. Binary badger nodded. Each friend will have a special job, he explained.\u003C\u002Fp>\n\u003Cp>Some will hang decorations, some will hide, and some will keep watch just like a big fun secret mission. We can even send magical invitations, explained variable vol, pulling scrolls from her patchwork vest. Each invitation will have a special link that helps friends enter the party. We'll invite friends from all other kingdoms. They can just bring their included passes and show them to us.\u003C\u002Fp>\n\u003Cp>Finally, the day arrived. Bobbie Tail's authentication magic worked perfectly. Only the invited friends could enter the surprise party. Professor Owl was amazed, and everyone celebrated Bobby's clever way of keeping the party safe and special. This story introduces the authentication concepts of usernames, passwords, two factor authentication, access control, and single sign on.\u003C\u002Fp>","Bobby Taylor and the magical guest list, read by Carmen Huidobro. Bobby Taylor was planning something extraordinary, a surprise party for professor owl Turing's birthday in the tree library. But Bobby knew he couldn't just let anyone wander in. I need a magical way to make sure only the right friends can enter, he thought, adjusting the gold buttons on his blue vest. Daisy debugged duck, Bobby called. I need help creating a magical guest list that only invited friends can see. Daisy waddled over her oversized magnifying glass swinging from her wing. We will use authentication, she explained. It's like creating a magical key that only the right friends can use. The Loop squirrel sisters, Lily and Luna, bounced into the conversation. We can help create user accounts, they tripped in unison. Each friend will get a special username and password just like the secret handshake that lets them enter the party. Binary Badger stepped forward, adjusting his pocket watch. We'll add an extra layer of protection, he explained carefully. 2 factor authentication is like having 2 special locks. First, a password, then a magical code that changes every time, making sure only the true friends can enter. Shh, Bobby Taylor whispered to his friends. Professor Owl can't know about the party. Binary badger nodded. Each friend will have a special job, he explained. Some will hang decorations, some will hide, and some will keep watch just like a big fun secret mission. We can even send magical invitations, explained variable vol, pulling scrolls from her patchwork vest. Each invitation will have a special link that helps friends enter the party. We'll invite friends from all other kingdoms. They can just bring their included passes and show them to us. Finally, the day arrived. Bobbie Tail's authentication magic worked perfectly. Only the invited friends could enter the surprise party. Professor Owl was amazed, and everyone celebrated Bobby's clever way of keeping the party safe and special. This story introduces the authentication concepts of usernames, passwords, two factor authentication, access control, and single sign on.","bab43b17-698a-4cd3-9c7f-61e305b73198",[1513],"54f729c3-9c98-44cf-a617-99d5d9386602",[],{"year":978,"number":480,"id":1434,"episodes":1516,"show":1517},[1436,1437,1438,1439,1420,1440],{"title":1442,"slug":1443},{"id":1519,"slug":1520,"vimeo_id":1521,"description":1522,"tile":1523,"length":400,"resources":12,"people":12,"episode_number":213,"published":1478,"title":1524,"video_transcript_html":1525,"video_transcript_text":1526,"content":12,"seo":1527,"status":19,"episode_people":1528,"recommendations":1534,"season":1535},"11513676-0e94-4407-bee0-387ae2bca2ac","5-december-2024","1037759355","Join us for The Changelog, taking you through the month’s Directus updates including product updates, new content and community contribution highlights. This month's show includes a year in review recap, a community showcase from Petros showing a universal translator extension, an episode of a new show on Directus TV called Bobby Tail's Little Library and more...","aec34c4a-1d8e-4e5c-acb3-a3a5cd456af2","December 2024","\u003Cp>Speaker 0: Alright. Hello. Hello, everyone. Welcome to December's version of the change log. Hope you're all having an amazing day.\u003C\u002Fp>\u003Cp>If you are joining us live, welcome. Let us know in the chat where you are joining us from as usual. We love to see it. We've got a really great show for you today. We have the usual product and extension updates.\u003C\u002Fp>\u003Cp>We've got the year in review for you, a community showcase, and a new show that we launched on director's TV today. So stick around if you are able to. We'd love to be able to chat with you along the way. Hello, everyone. So great for joining.\u003C\u002Fp>\u003Cp>Hello. Hello. Yeah. Let us know in the chat if you are joining. Oh, Canada.\u003C\u002Fp>\u003Cp>Hello. Welcome. Morning time, early morning time. Welcome. Glad for you to be joining us.\u003C\u002Fp>\u003Cp>Florida, super exciting. Much more exciting than London, personally. I think so at least. But, yeah, great to have you all here with us. So I'm kicking off with Alex who's gonna take you through the product updates for December.\u003C\u002Fp>\u003Cp>Speaker 1: Hey, folks. Alex here, director of web engineering, and I'm going to be taking you through the core updates that have happened since the last changelog. There's been a few releases, but here are the notable things to be aware of. In 11.2.2, we made s three connection settings configurable via environment variables. This includes things like connection timeout.\u003C\u002Fp>\u003Cp>In 11.3.0, we introduced new retention settings for activities, revisions, and flow logs. These 3 system tables can become quite large, and now you can change how long they will be kept for being preened. Earlier this year, we committed to providing security updates to Directus 10 until the end of the year. Make sure you upgrade to 10.13.4 if you're still on the version 10 family. The same patch has been included in the Directus 11 patch as well.\u003C\u002Fp>\u003Cp>That's all for me. Handing back over to Beth now.\u003C\u002Fp>\u003Cp>Speaker 2: I'm going to demonstrate each of the extensions new in Directors Labs this month. As a reminder, Directors Labs is our team's experimental org in GitHub, which regularly publishes new extensions. Here's a demo project to showcase what's new. The address completion interface integrates Google Places auto complete data API directly into the director's editor. This improves data quality and stores data in a standard GeoJSON feature format.\u003C\u002Fp>\u003Cp>It also enhances user experience by speeding up address entry. You can swap between map tiles or, when setting up the interface, skip the map altogether. The tour group interface is one we are super excited about. You can set up guided tours around director's editor to help your content authors better understand your configuration. You can set up multiple with their own labels, but we've set up one here to demonstrate.\u003C\u002Fp>\u003Cp>As you see, it's a multi step tour which moves between any of the interfaces in the UI. If we go to the interface settings, here, you can see it's a group interface, so items which are children can be included in the tour. You can add any number of tour steps. We have to first specify any element to select. This extension injects a couple of classes for each field, one for the whole field including label and one just for the output.\u003C\u002Fp>\u003Cp>You can specify a title and description and other metadata for display. This is gonna be so helpful when handing off your data project to end users. This is a choropleth panel. You've likely seen something like this before where countries are highlighted more intensely based on the higher values. Here, we have a views collection and a field called country, which contains a 2 letter ISO code.\u003C\u002Fp>\u003Cp>You can also use 3 letter or m 49 country codes. Like many other panels, pick an aggregated field and function. Here, we are counting the number of items with the country. We think this will be useful for understanding visitors and customer data based on country. The HubSpot API operation provides a way to interact directly with the Hub Spot API from Directors Automate.\u003C\u002Fp>\u003Cp>Once you provide your API key with correct access, you can interact with a load of endpoints like contacts, deals, and emails. Once you select an endpoint, you have a number of suitable actions available. Here, you can create, read, update, delete, or list your contacts. So this could be used to update a record whenever data changes in Directus, for example. We're really happy to be offering this extension to speed up integrations with your existing data sources.\u003C\u002Fp>\u003Cp>Speaker 0: Alright and I have just now put the link to check out all the extensions in the chat so if you are excited, as we hope you are, about those extensions do let us know, check them out yourself. Before I put us over to the year in review, I do also want to shout out a new project Brian has created called salty open source Santa. You write a letter to Santa including your GitHub username and Santa tells you if you are on the naughty or nice list. It's all in good fun but I also am really excited that Bryant has put through a blog post and a video talking through how he created the project as well. It's well worth checking out, so I will also include the links to those in the chat as well.\u003C\u002Fp>\u003Cp>Right now, I'm sending you over to Wrike to kick us off for the 2024 director's year in review.\u003C\u002Fp>\u003Cp>Speaker 3: Welcome to our 2024 recap where we walk through all of the cool and exciting things we added to Directus this year. I'm Reik, the CTO and cofounder of Directus. I'm going to be kicking this off by taking you through what we shipped in new releases to the core Directus project. Seeing that we shipped over a 1,000 things, I won't be listing all of them, but here's the top ones. Opening with a small but impactful feature, we added vocal point support, which allows you to set a vocal point coordinate via the API or the data studio when you're cropping images.\u003C\u002Fp>\u003Cp>That way you can ensure that the important parts of your image remain visible no matter what sizes you're generating in the API. In March, we released the beta of the marketplace, launching our extensions registry, enabling the distribution and installation of extensions in any Directus project. This was also a great way for us to be able to ship new features via extensions, which Beth will talk about later. We also brought upgrades to content versioning and live preview so you can check out what content will look like in your project without needing to publish it to the API. In version 10 dot 11, we introduced public registration, simplifying the process of allowing any user to create new accounts.\u003C\u002Fp>\u003Cp>This is available both in the Studio and via our new registration and verification APIs. To further extend public registration, we also introduced configurable user limits so you can control the number of users in your project, giving you more control over how your project runs. Over the summer, we released a new major version of Directus version 11. This introduced policies, changing the way you can compose access control throughout your project. We also used this opportunity to announce our first ever extended security update period for a version of Directus for those who weren't quite ready to migrate to the new version yet.\u003C\u002Fp>\u003Cp>In your project settings, you can now also find streaming system logs inside of the data studio for greater visibility into what's going on in your API and debug any problems along the way. For more information on this or any of the other things I just mentioned, do check out the detailed release notes on GitHub. Next up, we have Kevin taking you through our cloud recap.\u003C\u002Fp>\u003Cp>Speaker 4: Thanks, Reich. Now I have the pleasure of recapping everything that's happened this year for directors cloud. If you've not heard of it, directors cloud is the way to host directors projects managed by the team who build it. You, of course, can still self host Directus, and we make that as easy as possible. But if you want hardware maintenance, upgrades and migrations, and storage handled, Directus Cloud is for you.\u003C\u002Fp>\u003Cp>We provision a database, asset storage, caching, and more. Just a few button presses in the cloud dashboard, and you're ready to go. Now 2024 added a lot of requested features and capabilities to Directus Cloud. Probably the biggest change is to our pricing and the introduction of our new starter tier. This lowers the barrier even more to getting started, and it starts at just $15 a month.\u003C\u002Fp>\u003Cp>Remember, that's not only Directus, but a database, asset storage, caching, and all of the work required to keep an application online and secure. We also introduced a new self serve business tier that sits above our existing professional tier, but for customers who aren't quite ready for enterprise. This year, we also introduced auto scaling to cloud projects to make sure they always stay online. There was actually a significant amount of infrastructure work in the background to enable all of these changes, so shout out to our infra team for making it happen. In Director's Cloud, you can now add custom domains and set advanced project configuration that allows the editing of key environment variables.\u003C\u002Fp>\u003Cp>This is a big deal. It was one of the key pieces of feedback we had from cloud customers, and we're happy to have addressed it. We've picked important variables and a safe kind of pre picked allowed values to ensure that all projects on directors cloud continue to run smoothly. But if you want to see more, just reach out and let us know what you'd like to see. Lastly, we hear that launching a directors project for the first time can be a bit daunting, as you have a powerful but ultimately blank canvas to set up.\u003C\u002Fp>\u003Cp>When you set up a cloud project, we now offer the ability to set it up with one of our cloud templates, one for CMS, one for CRM, and one for ecommerce use cases. That's a lot of new stuff in Director's Cloud this year. Now we're reaching the end of the year, and quite frankly, I need a nap. So I'm gonna kick it back over to Beth to talk to you about all of the new extensions we shipped in 2024. Thank you so much for coming with me on this journey this year, and I'll see you next time.\u003C\u002Fp>\u003Cp>Bye for now.\u003C\u002Fp>\u003Cp>Speaker 2: Thanks, Kevin. Hello again, everyone. In every change log we have, we talk through the new extensions we release. There've been quite a few, so I'm here to take you through a short recap of some of our favorites. Starting off with directors dotai.\u003C\u002Fp>\u003Cp>Directors dotai is a group of 11 AI based extensions, which brought new capabilities to directors automate. We've integrated with best in class AI platforms to allow you to transcribe audio, generate images, write text, and scrape web pages. There's a whole lot in Director's AI, and we have a whole series on Directus TV to talk you through each of the extensions. We've added a bunch of new interfaces for Directus Editor. Rich interfaces for video, audio, PDF files, and address auto completion Collaborate with our new whiteboard and Wherebycalls interfaces Allow users to navigate multi selections via API with the multi level Autocomplete API interface One of our most upvoted feature requests, calculated fields, is also now available as an interface It supports loads of functions and dynamic data from your items.\u003C\u002Fp>\u003Cp>And our new tool group, releasing today, allows you to create guided tools around the editor to support your content authors. We've added some really powerful new layouts. The spreadsheet layout and the Gantt chart layout. Directors Insights has also received lots of love with a load of new panels, a more detailed table that you can drop in, Lots of new charts: scatterplot, funnel, tree map, timeline, and a choropleth map. And a new flow trigger panel, so you can execute automations directly from your dashboards.\u003C\u002Fp>\u003Cp>Finally, new service integrations. As well as Whereby Video calls that we mentioned earlier, we now have integrations with Flausible Analytics, resend's email service, and the HubSpot CRM. Moving away from extensions, I also want to briefly talk about templates, which is something we've also added this year that can help you get building with directors faster. This year, we launched a template directory, with templates built by both our core team and community members. Currently, there are templates for building a content management system, a customer relationship manager, an ecommerce store, an agency operating system, an event operating system, and an adventure business kit comprising a website, blog, and CMS.\u003C\u002Fp>\u003Cp>These are all available to browse on directors. Io \u002F templates. I'm gonna hand over to Carmen now for taking you through the content recap.\u003C\u002Fp>\u003Cp>Speaker 5: Thanks, Beth. I'm Carmen, developer educator here at Directus. This time last year, we launched Directus TV. Between then and now, we've added over 250 episodes across nearly 40 shows. A huge amount of entertaining and educational resources.\u003C\u002Fp>\u003Cp>Some highlights from this year include new seasons of 100 Apps in 100 Hours where Bryant races against the clock to create his own versions of a customer relationship manager, a remote job board, and a social media platform to name only a few. A new season of Trace Talks from John and Pedro interviewing senior engineering leaders such as David Zimmer from Netflix and Dana Lawson at Netlify on their journeys and experiences in the tech industry. Also, a new season of Short Hops continuing to take you through quick ways of getting more from Directus, the 20 24 episodes, including transformation presets, focal points, and version control. We've also launched many new shows including what's in your dock, where we ask guests to show us their most used hardware and software, Ready, Set, Battlesnake, where Andrew and Kevin build a capable snake inside of a Directus project. As well as the joy of theming where Bry Ross shows how to make theming masterpieces.\u003C\u002Fp>\u003Cp>Democratizing data got to grips with using open data and Directus to build new applications around data like the trees of London and meteorite landings. Bridging Bytes brought together technology leaders to discuss the future of sustainable open source and how agencies innovate. Enter the workshop, invited friends from Twilio and Deepgram to build extensions live on air. And my new show, Sharp Focus, hosted by me as I show you how to use image transformation as part of Directus files. Keep an eye out for this and many new shows at directus.i0\u002Ftv.\u003C\u002Fp>\u003Cp>This year, we've also published over 50 new tutorials to help you get the most from Directus. We've published getting started guides for Sveltekit, ASTRO, Next, Nuxt, and more. Developers in other languages, we got you. There's loads including posts for Django and Flask devs, those who use Flutter, Kotlin, and Swift, and for the Laravel community too. You can now find loads of Directus automate tutorials showed how to integrate with Clarify's image recognition APIs, GitHub and OpenAPI.\u003C\u002Fp>\u003Cp>We believe you should be able to host Directus anywhere. So we wrote new content to help you deploy Directus on Azure, Google Cloud Platform, and AWS. There are some really cool project write ups as well, including building ecommerce projects with Stripe integrations, using Directus as a baby health monitor with Opsgenie, and building event registration systems. We also have new migration posts, including one for WordPress and integration with different indexing systems like Algolia and Elasticsearch. This is not all and we encourage you to take a look at our tutorials in our docs to explore our whole collection.\u003C\u002Fp>\u003Cp>That's all for me. We hope you've enjoyed this recap. And as always, if you've got any questions, let us know on Discord.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. So that was a recap of some of the highlights for 2024. Whether you have been with us the whole year or you are new to directors this year, we'd love to hear what your year has looked like with directors, whether anything has changed, whether you've brought in anything new that we've talked about. But, yeah, it's been great to spend a second to just look back rather than constantly looking forward, and put together the year in review for you all. So we hope you enjoyed it.\u003C\u002Fp>\u003Cp>Next up is my personal favorite. I say this every month and every month it is true. It is the community showcase, and this month, we have Petrus Diaz talking through the universal translate flow operation.\u003C\u002Fp>\u003Cp>Speaker 6: Hi, everyone. My name is Petros Diaz, and I'm here to demo, Directus flow operation extension that we've built called a universal translator. And the main task of the universal translators is to translate anything that you throw at it, be it a string or a JSON object or an array. Let's have a quick look of how that works. You just place the universal translator within your flow and the only thing you need to do is provide the input translate which can be a direct string or a variable and choose the language which can be based on the user language that the user has selected, or you can choose from the drop down.\u003C\u002Fp>\u003Cp>We haven't included the entire language collection here, which we can do so in the future. Let's just leave it to auto for now. And just let's, let's run it real quick so you can see the results. I have a ready made flow here. So this flow creates a PDF, for the user in the language they've selected.\u003C\u002Fp>\u003Cp>And let's have a look at the result. Here it is. So you can't really tell which are the translated fields here but status, author, process, location, approved all of these are translated fields. And let's look at them in the actual record and here they are. So approved becomes approved and horizontal processes becomes horizontal processes but let's also have a look in another language just to make sure that everything works as described.\u003C\u002Fp>\u003Cp>And what we want to do is just run it again And here it is. Here you can see that, all of these fields have been translated to the, corresponding translations from the, directors collection, Directus translation. So the way this works, it's really simple. The operation just looks for a mustache type translation field which can be like this. So anything you want translated should go in the mustache syntax which is this here along with the closing bracket.\u003C\u002Fp>\u003Cp>So, let's make it quite big so it's visible. Let's hit save and try that again. And you can see here that our mustache syntax has been translated to the to the translation from the internal collection. It's a really simple, extension, and let's have a look under the hood. So this is the actual code for the extension.\u003C\u002Fp>\u003Cp>It's very short code as you can see. That's it. Just 79 lines of code, nothing remarkable. And you can see where the database pulls the, well the extension pulls language selected for the user from the database. And that's pretty much it.\u003C\u002Fp>\u003Cp>The rest is just for the options and it's a very simple extension. Now why did we create this? Because we have various users using our platform in a number of languages and all of these fields, would need to be translated. So we figured a very simple way to do that, which can be used and reused. And you can even use it for sending emails using the internal emailing service.\u003C\u002Fp>\u003Cp>We were considering other solutions as well, translating the fields internally through code or, just having a number of selections in all languages. But we figured that's not really viable as a solution because we are looking to expand a number of languages, and we really needed the localization for all of our all of our users. The, the challenge the biggest challenge in creating this, I think was pretty much creating the extension itself because we didn't really have experience. But using the provided guide from the director's documentation, it was actually pretty straightforward to build this, this extension. That wasn't so difficult.\u003C\u002Fp>\u003Cp>I guess it would be good to expand this to make sure that it works properly in any kind of, input. It doesn't miss any nested translations, which is very important that it does a recursive search of the translations that it actually executes. We do need to replace the choices with the actual languages from the collection. This doesn't need to be hard coded. It was just for our demo.\u003C\u002Fp>\u003Cp>And, I think this is pretty much it for something that needs to be translated. We don't really need to get into, more functionality or more detail, But if anybody would, be willing to help with expanding even just a little bit and, fixing our code just here and there, I would be grateful and, you know, you just make it available for everybody else as well. So, our add on is not yet our extension is not yet available on the marketplace, but you can download it from our GitHub repo, build it, and deploy it yourself, test it out. And if you have any comments, just open and, an issue in the GitHub and we'll see if we can, help out in resolving it. That was it.\u003C\u002Fp>\u003Cp>Thank you for watching. And I hope you can watch any other videos we'll make in the future for any of our other extensions.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Thank you once again to Petros for taking the time to record it for us and talk us through it. We always really appreciate it. If you'd like to see more about different projects that are being built with directors, highly recommend the I made this channel on discord. It's especially great because every now and then people give us updates and so you get to see kind of as it's built along, which I think is really sweet.\u003C\u002Fp>\u003Cp>Next, we have an episode of Bobbie Tail's Little Library, which is a brand new show we've added to director's TV today, and it is a narrated storybook. So we have a short episode for you from that series.\u003C\u002Fp>\u003Cp>Speaker 7: Bobby Tail and the library construction crew, read by Catherine Steiner. Bobby Tail had a big problem. The tree library was running out of space. Books were stacked in every corner, and new stories arrived each day. We need to build a new wing, he said, adjusting his blue vest.\u003C\u002Fp>\u003Cp>But building a library wasn't like building a regular treehouse, everything needed to be perfectly organized. Binary Badger arrived first, pocket watch ticking. Before we build, he said, we need a proper blueprint. Just like a tree needs strong roots, a library needs a strong foundation.' Together, they drew plans for different rooms. 1 for storybooks, another for pictures, and a special vault for magical scrolls.\u003C\u002Fp>\u003Cp>The Raymould family popped up from their tunnels, each wearing their numbered sweaters. We'll help organize everything, they squeaked excitedly. Each book needs its special place, just like we have our numbered homes. That way, we'll always know where to find things. Lily and Luna, the loop scroll sisters, swished their tails as they tested different bridges between the library sections.\u003C\u002Fp>\u003Cp>We need magical pathways, Lily began, so everyone can find exactly what they're looking for, Luna finished. They created special rope bridges that connected every room to every other room. Daisy Debock Duck waddled in with her magnifying glass. 'Let's test everything,' she declared. She checked each bridge, each shelf, and each room.\u003C\u002Fp>\u003Cp>We must make sure every friend can find their books easily,' she explained. Marking items off her checklist with her wing. Variable goal arrived with her magical bag. Now for the special items, she announced. Together, they created cozy reading nooks, picture frame holders, and scroll cases.\u003C\u002Fp>\u003Cp>Everything had its perfect spot, ready for all kinds of woodland treasures. Finally, the new library wing was complete. Bobby Tayl stood proudly with his construction crew. The Arraymalls organized the books, the Loop sisters bridges connected everything perfectly, and Daisy Duck had tested every path. It was more than just a library, it was a magical place, where every piece of knowledge had a hole.\u003C\u002Fp>\u003Cp>This story introduces the back end concepts of data modeling, APIs, and file management.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Hopefully, you like that as much as we do. I am putting the link to the show for for the rest of the episodes. If you or anyone you know would like to check that out, we highly recommend. We also really enjoyed putting it together.\u003C\u002Fp>\u003Cp>So glad to see you're already, liking it. Someone said, just landed. What am I watching? You were watching a new director's TV episode of Bobbie Tail's Little Library. So welcome, and glad you're here.\u003C\u002Fp>\u003Cp>We have mixed up the reading list, so I'm gonna send it myself there.\u003C\u002Fp>\u003Cp>Speaker 2: We put together a reading list based on resources shared by the director's core team on things they found interesting, educational, or entertaining this month, and we'd like to share it with you now. Firstly, we have an interactive study of queuing strategies from Encore, comparing 1st in, 1st out, last in, 1st out, and priority queues on wait time, drop results, and process time outs. Next, we have an interesting approach to testing query results for different database vendors. This allows you to test identical SQL results across database drivers or dialects for the same queries in a semi automated fashion. Next, there's an interesting write up on AI assisted coding from Adi Osmani talking about the hidden cost of AI speed brought about by edge case handling, error handling, and questioning output, and how AI tools help experienced developers more than beginners.\u003C\u002Fp>\u003Cp>Lastly, this article is about making focus outlines look better. They're the glows that appear around form inputs in focus in the browser. We particularly like how there's some real world examples in this post as well. That's all from the reading list this month. As always, if you have your own reading list suggestions, we'd love you to share them in Discord with us.\u003C\u002Fp>\u003Cp>We want to take\u003C\u002Fp>\u003Cp>Speaker 0: a moment towards the end of\u003C\u002Fp>\u003Cp>Speaker 2: the changelog for thanking our community contributors, who give their time to improve the director's project. Since last month, there have been 5 releases, directors 11.2.2 to directors 11.3.2 and 7 contributors. Thank you to Robson for fixing a HTML validity issue in the table component, Alex for fixing SSO GitHub sign up for users without public emails, George for allowing the login timeout for open ID SSO login to be adjusted, Nicholas for fixing typos in relational MD, Matthew for fixing WYSIWYG file upload extension to use filename disk, Ziles for fixing specifications TS to generate required array in open API schema, and Nick for adding select all and delete buttons for notification straw. Thank you again. You can see their specific pull requests inside of the full release notes on GitHub.\u003C\u002Fp>\u003Cp>Lastly, we also want to take the time to thank our GitHub Sponsors of November who financially contribute to Directus' development. A huge thank you to Tessent, Entle, dmathams, Fergus, Omar, Marcus, c k, Peter, Utomit, Steven, Robson, nonlinear, Andrea Valentino, John, Wayne, Bjorn, Adam, Jason, Birka, Jens, Vincent, Mike, Khan, Lassie, and Wayfan. The money we are given from our GitHub Sponsors goes straight back to community members who build tooling and extensions for the director's ecosystem. Thank you again for being part of that.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. And that was the change log for December. Thank you if you are still here for joining us along the whole way. If you randomly entered into Bobby Tales and you got very confused as to what was going on, welcome and thank you for joining us the whole way too. Any questions after this is over, please put them in Discord.\u003C\u002Fp>\u003Cp>We love hearing from you along the way as well. And if you have any 2025 changelog or not wishlist items that you'd like to see from us do let us know in discord too, as we are super interested in hearing how we can make this better and more interesting and useful for you all. It is for you after all. So if there is anything you'd like, this is actually I'd like to see this. Let us know.\u003C\u002Fp>\u003Cp>We'll no promises, but we will see what we can do. Thank you so much once again for joining us and, yeah, have a great rest of your day, your week, and your 2024, everyone. Bye.\u003C\u002Fp>","Alright. Hello. Hello, everyone. Welcome to December's version of the change log. Hope you're all having an amazing day. If you are joining us live, welcome. Let us know in the chat where you are joining us from as usual. We love to see it. We've got a really great show for you today. We have the usual product and extension updates. We've got the year in review for you, a community showcase, and a new show that we launched on director's TV today. So stick around if you are able to. We'd love to be able to chat with you along the way. Hello, everyone. So great for joining. Hello. Hello. Yeah. Let us know in the chat if you are joining. Oh, Canada. Hello. Welcome. Morning time, early morning time. Welcome. Glad for you to be joining us. Florida, super exciting. Much more exciting than London, personally. I think so at least. But, yeah, great to have you all here with us. So I'm kicking off with Alex who's gonna take you through the product updates for December. Hey, folks. Alex here, director of web engineering, and I'm going to be taking you through the core updates that have happened since the last changelog. There's been a few releases, but here are the notable things to be aware of. In 11.2.2, we made s three connection settings configurable via environment variables. This includes things like connection timeout. In 11.3.0, we introduced new retention settings for activities, revisions, and flow logs. These 3 system tables can become quite large, and now you can change how long they will be kept for being preened. Earlier this year, we committed to providing security updates to Directus 10 until the end of the year. Make sure you upgrade to 10.13.4 if you're still on the version 10 family. The same patch has been included in the Directus 11 patch as well. That's all for me. Handing back over to Beth now. I'm going to demonstrate each of the extensions new in Directors Labs this month. As a reminder, Directors Labs is our team's experimental org in GitHub, which regularly publishes new extensions. Here's a demo project to showcase what's new. The address completion interface integrates Google Places auto complete data API directly into the director's editor. This improves data quality and stores data in a standard GeoJSON feature format. It also enhances user experience by speeding up address entry. You can swap between map tiles or, when setting up the interface, skip the map altogether. The tour group interface is one we are super excited about. You can set up guided tours around director's editor to help your content authors better understand your configuration. You can set up multiple with their own labels, but we've set up one here to demonstrate. As you see, it's a multi step tour which moves between any of the interfaces in the UI. If we go to the interface settings, here, you can see it's a group interface, so items which are children can be included in the tour. You can add any number of tour steps. We have to first specify any element to select. This extension injects a couple of classes for each field, one for the whole field including label and one just for the output. You can specify a title and description and other metadata for display. This is gonna be so helpful when handing off your data project to end users. This is a choropleth panel. You've likely seen something like this before where countries are highlighted more intensely based on the higher values. Here, we have a views collection and a field called country, which contains a 2 letter ISO code. You can also use 3 letter or m 49 country codes. Like many other panels, pick an aggregated field and function. Here, we are counting the number of items with the country. We think this will be useful for understanding visitors and customer data based on country. The HubSpot API operation provides a way to interact directly with the Hub Spot API from Directors Automate. Once you provide your API key with correct access, you can interact with a load of endpoints like contacts, deals, and emails. Once you select an endpoint, you have a number of suitable actions available. Here, you can create, read, update, delete, or list your contacts. So this could be used to update a record whenever data changes in Directus, for example. We're really happy to be offering this extension to speed up integrations with your existing data sources. Alright and I have just now put the link to check out all the extensions in the chat so if you are excited, as we hope you are, about those extensions do let us know, check them out yourself. Before I put us over to the year in review, I do also want to shout out a new project Brian has created called salty open source Santa. You write a letter to Santa including your GitHub username and Santa tells you if you are on the naughty or nice list. It's all in good fun but I also am really excited that Bryant has put through a blog post and a video talking through how he created the project as well. It's well worth checking out, so I will also include the links to those in the chat as well. Right now, I'm sending you over to Wrike to kick us off for the 2024 director's year in review. Welcome to our 2024 recap where we walk through all of the cool and exciting things we added to Directus this year. I'm Reik, the CTO and cofounder of Directus. I'm going to be kicking this off by taking you through what we shipped in new releases to the core Directus project. Seeing that we shipped over a 1,000 things, I won't be listing all of them, but here's the top ones. Opening with a small but impactful feature, we added vocal point support, which allows you to set a vocal point coordinate via the API or the data studio when you're cropping images. That way you can ensure that the important parts of your image remain visible no matter what sizes you're generating in the API. In March, we released the beta of the marketplace, launching our extensions registry, enabling the distribution and installation of extensions in any Directus project. This was also a great way for us to be able to ship new features via extensions, which Beth will talk about later. We also brought upgrades to content versioning and live preview so you can check out what content will look like in your project without needing to publish it to the API. In version 10 dot 11, we introduced public registration, simplifying the process of allowing any user to create new accounts. This is available both in the Studio and via our new registration and verification APIs. To further extend public registration, we also introduced configurable user limits so you can control the number of users in your project, giving you more control over how your project runs. Over the summer, we released a new major version of Directus version 11. This introduced policies, changing the way you can compose access control throughout your project. We also used this opportunity to announce our first ever extended security update period for a version of Directus for those who weren't quite ready to migrate to the new version yet. In your project settings, you can now also find streaming system logs inside of the data studio for greater visibility into what's going on in your API and debug any problems along the way. For more information on this or any of the other things I just mentioned, do check out the detailed release notes on GitHub. Next up, we have Kevin taking you through our cloud recap. Thanks, Reich. Now I have the pleasure of recapping everything that's happened this year for directors cloud. If you've not heard of it, directors cloud is the way to host directors projects managed by the team who build it. You, of course, can still self host Directus, and we make that as easy as possible. But if you want hardware maintenance, upgrades and migrations, and storage handled, Directus Cloud is for you. We provision a database, asset storage, caching, and more. Just a few button presses in the cloud dashboard, and you're ready to go. Now 2024 added a lot of requested features and capabilities to Directus Cloud. Probably the biggest change is to our pricing and the introduction of our new starter tier. This lowers the barrier even more to getting started, and it starts at just $15 a month. Remember, that's not only Directus, but a database, asset storage, caching, and all of the work required to keep an application online and secure. We also introduced a new self serve business tier that sits above our existing professional tier, but for customers who aren't quite ready for enterprise. This year, we also introduced auto scaling to cloud projects to make sure they always stay online. There was actually a significant amount of infrastructure work in the background to enable all of these changes, so shout out to our infra team for making it happen. In Director's Cloud, you can now add custom domains and set advanced project configuration that allows the editing of key environment variables. This is a big deal. It was one of the key pieces of feedback we had from cloud customers, and we're happy to have addressed it. We've picked important variables and a safe kind of pre picked allowed values to ensure that all projects on directors cloud continue to run smoothly. But if you want to see more, just reach out and let us know what you'd like to see. Lastly, we hear that launching a directors project for the first time can be a bit daunting, as you have a powerful but ultimately blank canvas to set up. When you set up a cloud project, we now offer the ability to set it up with one of our cloud templates, one for CMS, one for CRM, and one for ecommerce use cases. That's a lot of new stuff in Director's Cloud this year. Now we're reaching the end of the year, and quite frankly, I need a nap. So I'm gonna kick it back over to Beth to talk to you about all of the new extensions we shipped in 2024. Thank you so much for coming with me on this journey this year, and I'll see you next time. Bye for now. Thanks, Kevin. Hello again, everyone. In every change log we have, we talk through the new extensions we release. There've been quite a few, so I'm here to take you through a short recap of some of our favorites. Starting off with directors dotai. Directors dotai is a group of 11 AI based extensions, which brought new capabilities to directors automate. We've integrated with best in class AI platforms to allow you to transcribe audio, generate images, write text, and scrape web pages. There's a whole lot in Director's AI, and we have a whole series on Directus TV to talk you through each of the extensions. We've added a bunch of new interfaces for Directus Editor. Rich interfaces for video, audio, PDF files, and address auto completion Collaborate with our new whiteboard and Wherebycalls interfaces Allow users to navigate multi selections via API with the multi level Autocomplete API interface One of our most upvoted feature requests, calculated fields, is also now available as an interface It supports loads of functions and dynamic data from your items. And our new tool group, releasing today, allows you to create guided tools around the editor to support your content authors. We've added some really powerful new layouts. The spreadsheet layout and the Gantt chart layout. Directors Insights has also received lots of love with a load of new panels, a more detailed table that you can drop in, Lots of new charts: scatterplot, funnel, tree map, timeline, and a choropleth map. And a new flow trigger panel, so you can execute automations directly from your dashboards. Finally, new service integrations. As well as Whereby Video calls that we mentioned earlier, we now have integrations with Flausible Analytics, resend's email service, and the HubSpot CRM. Moving away from extensions, I also want to briefly talk about templates, which is something we've also added this year that can help you get building with directors faster. This year, we launched a template directory, with templates built by both our core team and community members. Currently, there are templates for building a content management system, a customer relationship manager, an ecommerce store, an agency operating system, an event operating system, and an adventure business kit comprising a website, blog, and CMS. These are all available to browse on directors. Io \u002F templates. I'm gonna hand over to Carmen now for taking you through the content recap. Thanks, Beth. I'm Carmen, developer educator here at Directus. This time last year, we launched Directus TV. Between then and now, we've added over 250 episodes across nearly 40 shows. A huge amount of entertaining and educational resources. Some highlights from this year include new seasons of 100 Apps in 100 Hours where Bryant races against the clock to create his own versions of a customer relationship manager, a remote job board, and a social media platform to name only a few. A new season of Trace Talks from John and Pedro interviewing senior engineering leaders such as David Zimmer from Netflix and Dana Lawson at Netlify on their journeys and experiences in the tech industry. Also, a new season of Short Hops continuing to take you through quick ways of getting more from Directus, the 20 24 episodes, including transformation presets, focal points, and version control. We've also launched many new shows including what's in your dock, where we ask guests to show us their most used hardware and software, Ready, Set, Battlesnake, where Andrew and Kevin build a capable snake inside of a Directus project. As well as the joy of theming where Bry Ross shows how to make theming masterpieces. Democratizing data got to grips with using open data and Directus to build new applications around data like the trees of London and meteorite landings. Bridging Bytes brought together technology leaders to discuss the future of sustainable open source and how agencies innovate. Enter the workshop, invited friends from Twilio and Deepgram to build extensions live on air. And my new show, Sharp Focus, hosted by me as I show you how to use image transformation as part of Directus files. Keep an eye out for this and many new shows at directus.i0\u002Ftv. This year, we've also published over 50 new tutorials to help you get the most from Directus. We've published getting started guides for Sveltekit, ASTRO, Next, Nuxt, and more. Developers in other languages, we got you. There's loads including posts for Django and Flask devs, those who use Flutter, Kotlin, and Swift, and for the Laravel community too. You can now find loads of Directus automate tutorials showed how to integrate with Clarify's image recognition APIs, GitHub and OpenAPI. We believe you should be able to host Directus anywhere. So we wrote new content to help you deploy Directus on Azure, Google Cloud Platform, and AWS. There are some really cool project write ups as well, including building ecommerce projects with Stripe integrations, using Directus as a baby health monitor with Opsgenie, and building event registration systems. We also have new migration posts, including one for WordPress and integration with different indexing systems like Algolia and Elasticsearch. This is not all and we encourage you to take a look at our tutorials in our docs to explore our whole collection. That's all for me. We hope you've enjoyed this recap. And as always, if you've got any questions, let us know on Discord. Alright. So that was a recap of some of the highlights for 2024. Whether you have been with us the whole year or you are new to directors this year, we'd love to hear what your year has looked like with directors, whether anything has changed, whether you've brought in anything new that we've talked about. But, yeah, it's been great to spend a second to just look back rather than constantly looking forward, and put together the year in review for you all. So we hope you enjoyed it. Next up is my personal favorite. I say this every month and every month it is true. It is the community showcase, and this month, we have Petrus Diaz talking through the universal translate flow operation. Hi, everyone. My name is Petros Diaz, and I'm here to demo, Directus flow operation extension that we've built called a universal translator. And the main task of the universal translators is to translate anything that you throw at it, be it a string or a JSON object or an array. Let's have a quick look of how that works. You just place the universal translator within your flow and the only thing you need to do is provide the input translate which can be a direct string or a variable and choose the language which can be based on the user language that the user has selected, or you can choose from the drop down. We haven't included the entire language collection here, which we can do so in the future. Let's just leave it to auto for now. And just let's, let's run it real quick so you can see the results. I have a ready made flow here. So this flow creates a PDF, for the user in the language they've selected. And let's have a look at the result. Here it is. So you can't really tell which are the translated fields here but status, author, process, location, approved all of these are translated fields. And let's look at them in the actual record and here they are. So approved becomes approved and horizontal processes becomes horizontal processes but let's also have a look in another language just to make sure that everything works as described. And what we want to do is just run it again And here it is. Here you can see that, all of these fields have been translated to the, corresponding translations from the, directors collection, Directus translation. So the way this works, it's really simple. The operation just looks for a mustache type translation field which can be like this. So anything you want translated should go in the mustache syntax which is this here along with the closing bracket. So, let's make it quite big so it's visible. Let's hit save and try that again. And you can see here that our mustache syntax has been translated to the to the translation from the internal collection. It's a really simple, extension, and let's have a look under the hood. So this is the actual code for the extension. It's very short code as you can see. That's it. Just 79 lines of code, nothing remarkable. And you can see where the database pulls the, well the extension pulls language selected for the user from the database. And that's pretty much it. The rest is just for the options and it's a very simple extension. Now why did we create this? Because we have various users using our platform in a number of languages and all of these fields, would need to be translated. So we figured a very simple way to do that, which can be used and reused. And you can even use it for sending emails using the internal emailing service. We were considering other solutions as well, translating the fields internally through code or, just having a number of selections in all languages. But we figured that's not really viable as a solution because we are looking to expand a number of languages, and we really needed the localization for all of our all of our users. The, the challenge the biggest challenge in creating this, I think was pretty much creating the extension itself because we didn't really have experience. But using the provided guide from the director's documentation, it was actually pretty straightforward to build this, this extension. That wasn't so difficult. I guess it would be good to expand this to make sure that it works properly in any kind of, input. It doesn't miss any nested translations, which is very important that it does a recursive search of the translations that it actually executes. We do need to replace the choices with the actual languages from the collection. This doesn't need to be hard coded. It was just for our demo. And, I think this is pretty much it for something that needs to be translated. We don't really need to get into, more functionality or more detail, But if anybody would, be willing to help with expanding even just a little bit and, fixing our code just here and there, I would be grateful and, you know, you just make it available for everybody else as well. So, our add on is not yet our extension is not yet available on the marketplace, but you can download it from our GitHub repo, build it, and deploy it yourself, test it out. And if you have any comments, just open and, an issue in the GitHub and we'll see if we can, help out in resolving it. That was it. Thank you for watching. And I hope you can watch any other videos we'll make in the future for any of our other extensions. Alright. Thank you once again to Petros for taking the time to record it for us and talk us through it. We always really appreciate it. If you'd like to see more about different projects that are being built with directors, highly recommend the I made this channel on discord. It's especially great because every now and then people give us updates and so you get to see kind of as it's built along, which I think is really sweet. Next, we have an episode of Bobbie Tail's Little Library, which is a brand new show we've added to director's TV today, and it is a narrated storybook. So we have a short episode for you from that series. Bobby Tail and the library construction crew, read by Catherine Steiner. Bobby Tail had a big problem. The tree library was running out of space. Books were stacked in every corner, and new stories arrived each day. We need to build a new wing, he said, adjusting his blue vest. But building a library wasn't like building a regular treehouse, everything needed to be perfectly organized. Binary Badger arrived first, pocket watch ticking. Before we build, he said, we need a proper blueprint. Just like a tree needs strong roots, a library needs a strong foundation.' Together, they drew plans for different rooms. 1 for storybooks, another for pictures, and a special vault for magical scrolls. The Raymould family popped up from their tunnels, each wearing their numbered sweaters. We'll help organize everything, they squeaked excitedly. Each book needs its special place, just like we have our numbered homes. That way, we'll always know where to find things. Lily and Luna, the loop scroll sisters, swished their tails as they tested different bridges between the library sections. We need magical pathways, Lily began, so everyone can find exactly what they're looking for, Luna finished. They created special rope bridges that connected every room to every other room. Daisy Debock Duck waddled in with her magnifying glass. 'Let's test everything,' she declared. She checked each bridge, each shelf, and each room. We must make sure every friend can find their books easily,' she explained. Marking items off her checklist with her wing. Variable goal arrived with her magical bag. Now for the special items, she announced. Together, they created cozy reading nooks, picture frame holders, and scroll cases. Everything had its perfect spot, ready for all kinds of woodland treasures. Finally, the new library wing was complete. Bobby Tayl stood proudly with his construction crew. The Arraymalls organized the books, the Loop sisters bridges connected everything perfectly, and Daisy Duck had tested every path. It was more than just a library, it was a magical place, where every piece of knowledge had a hole. This story introduces the back end concepts of data modeling, APIs, and file management. Alright. Hopefully, you like that as much as we do. I am putting the link to the show for for the rest of the episodes. If you or anyone you know would like to check that out, we highly recommend. We also really enjoyed putting it together. So glad to see you're already, liking it. Someone said, just landed. What am I watching? You were watching a new director's TV episode of Bobbie Tail's Little Library. So welcome, and glad you're here. We have mixed up the reading list, so I'm gonna send it myself there. We put together a reading list based on resources shared by the director's core team on things they found interesting, educational, or entertaining this month, and we'd like to share it with you now. Firstly, we have an interactive study of queuing strategies from Encore, comparing 1st in, 1st out, last in, 1st out, and priority queues on wait time, drop results, and process time outs. Next, we have an interesting approach to testing query results for different database vendors. This allows you to test identical SQL results across database drivers or dialects for the same queries in a semi automated fashion. Next, there's an interesting write up on AI assisted coding from Adi Osmani talking about the hidden cost of AI speed brought about by edge case handling, error handling, and questioning output, and how AI tools help experienced developers more than beginners. Lastly, this article is about making focus outlines look better. They're the glows that appear around form inputs in focus in the browser. We particularly like how there's some real world examples in this post as well. That's all from the reading list this month. As always, if you have your own reading list suggestions, we'd love you to share them in Discord with us. We want to take a moment towards the end of the changelog for thanking our community contributors, who give their time to improve the director's project. Since last month, there have been 5 releases, directors 11.2.2 to directors 11.3.2 and 7 contributors. Thank you to Robson for fixing a HTML validity issue in the table component, Alex for fixing SSO GitHub sign up for users without public emails, George for allowing the login timeout for open ID SSO login to be adjusted, Nicholas for fixing typos in relational MD, Matthew for fixing WYSIWYG file upload extension to use filename disk, Ziles for fixing specifications TS to generate required array in open API schema, and Nick for adding select all and delete buttons for notification straw. Thank you again. You can see their specific pull requests inside of the full release notes on GitHub. Lastly, we also want to take the time to thank our GitHub Sponsors of November who financially contribute to Directus' development. A huge thank you to Tessent, Entle, dmathams, Fergus, Omar, Marcus, c k, Peter, Utomit, Steven, Robson, nonlinear, Andrea Valentino, John, Wayne, Bjorn, Adam, Jason, Birka, Jens, Vincent, Mike, Khan, Lassie, and Wayfan. The money we are given from our GitHub Sponsors goes straight back to community members who build tooling and extensions for the director's ecosystem. Thank you again for being part of that. Alright. And that was the change log for December. Thank you if you are still here for joining us along the whole way. If you randomly entered into Bobby Tales and you got very confused as to what was going on, welcome and thank you for joining us the whole way too. Any questions after this is over, please put them in Discord. We love hearing from you along the way as well. And if you have any 2025 changelog or not wishlist items that you'd like to see from us do let us know in discord too, as we are super interested in hearing how we can make this better and more interesting and useful for you all. It is for you after all. So if there is anything you'd like, this is actually I'd like to see this. Let us know. We'll no promises, but we will see what we can do. Thank you so much once again for joining us and, yeah, have a great rest of your day, your week, and your 2024, everyone. Bye.","01804104-119d-470b-b225-e6a8d5d2d2a0",[1529,1530,1531,1532,1533],"d7f68adc-7027-4475-aa53-55f8d0342b44","ad08e78f-597e-4ba7-9fab-ad8e0d47a247","0af255dc-eb15-47a1-911e-f2b51d47848b","6913fad7-24b4-4c51-9f90-963969ce745d","69391df2-bd67-4f9f-8d6a-4fce37455245",[],{"year":978,"number":1536,"id":1537,"episodes":1538,"show":1543},17,"093d2e2b-8006-4f05-a00c-22f124332e56",[1539,1540,1541,1542,1519],"07591f9d-8187-47fd-9904-92eda024043a","61e8a740-8f60-40e8-9fc3-d9bcda53d16b","a5361f29-432a-4602-ae17-57da36b48d19","cfcf16eb-9630-4423-9f83-0fe27ef57f85",{"title":297,"slug":298},{"id":1545,"slug":1546,"vimeo_id":1547,"description":1548,"tile":1549,"length":159,"resources":12,"people":12,"episode_number":264,"published":1550,"title":1551,"video_transcript_html":12,"video_transcript_text":12,"content":12,"seo":1552,"status":19,"episode_people":1553,"recommendations":1555,"season":1556},"40e22453-86d8-4e4f-8da2-54955b5505ba","q3-2024","1033207890","Director of Customer Success Will gives a rundown of the latest news, product updates, and more from Q3 of 2024. ","e5e8630f-0b47-4d45-a7f0-80be2b03c077","2024-11-27","Q3 2024","ed05d276-2f3f-49f2-8e56-40bd180b482f",[1554],"a7df9647-ec47-44fb-a8a5-0408f12b7639",[],{"year":978,"number":740,"id":1557,"episodes":1558,"show":1561},"a27cacbb-d746-415c-a4c7-8146af11e835",[1559,1560,1545],"fbe534d0-514e-4383-817d-17d6b6fc2ebc","1b99667b-fbef-48f9-8562-e44f88d75e9a",{"title":1562,"slug":1563},"Quarterly Customer Q&A","quarterly-customer-qna",{"id":1565,"slug":1566,"vimeo_id":1567,"description":1568,"tile":1569,"length":12,"resources":12,"people":12,"episode_number":13,"published":1570,"title":1571,"video_transcript_html":1572,"video_transcript_text":1573,"content":12,"seo":1574,"status":19,"episode_people":1575,"recommendations":1579,"season":1580},"6120f682-7d17-4a06-9f18-3bbf1e26faae","seans-dead-plants","1030834662","Sean loves plants, but he can't stop killing them. Can our crack team of experts save him from his murderous streak?","baa0b0e1-a43e-43a1-a64f-627752c9c688","2024-11-19","Sean's Dead Plants","\u003Cp>Speaker 0: Hello, and welcome to Automate My Life. In this show, we help regular people using Directus.\u003C\u002Fp>\n\u003Cp>Speaker 1: And today, we are in sunny London with our first person in need, Sean. He has lots of problems. We can't help with most of them, but we certainly can help with 1. Over to Sean. So I'm here with Sean who has called in the Automate My Life team with a problem.\u003C\u002Fp>\n\u003Cp>Sean, what can we help you with?\u003C\u002Fp>\n\u003Cp>Speaker 2: Hi, Kevin. Thanks for coming. I have a problem. Tell me more. Look at this plant.\u003C\u002Fp>\n\u003Cp>What do you notice about it?\u003C\u002Fp>\n\u003Cp>Speaker 1: Sean, this plant is dead.\u003C\u002Fp>\n\u003Cp>Speaker 2: That is the problem. Okay. Yep. See, I have plants and they are not dead, but then no matter what I do, they become dead. Despite your best efforts.\u003C\u002Fp>\n\u003Cp>Despite my best and sometimes your mediocre efforts as well.\u003C\u002Fp>\n\u003Cp>Speaker 1: Okay. I understand. Well, I think this is something we might be able to help with over in the workshop.\u003C\u002Fp>\n\u003Cp>Speaker 2: Marvelous. Automate my life, Directus team. Over to Beth.\u003C\u002Fp>\n\u003Cp>Speaker 0: Okay, folks. You've heard what Sean's problem is. We've brought you here as the best of the best to work out a solution for Sean. The future is plants relies on you. What have you got?\u003C\u002Fp>\n\u003Cp>Speaker 3: Hey, Sean. Welcome to the Directus workshopping team. We're all about automating our lives every day. I have a similar problem. My wife my partner, has a pepper garden.\u003C\u002Fp>\n\u003Cp>She's an avid gardener. She loves to garden. This year, she actually picked up a little pH temperature monitor or soil tester, to give you the soil quality and quantity and make sure that she could amend the soil properly for those kinds of things. But as I'm thinking about this now, I'd actually like to work with her, and we'll get a I I think next year, I'm actually going to do this particular project. But in the meantime, we'll talk about some of the ways we could solve this problem.\u003C\u002Fp>\n\u003Cp>There are both DIY options and aftermarket options. I think I'll opt for the DIY, And that will be using something along the lines of an e s ESP 32 Wi Fi sensor. There's a nice Directus blog that kind of goes through a use case for this. But we're gonna talk a little bit more in detail about some specific sensors and capabilities that we'll need to do for this. So there's an ESP 32.\u003C\u002Fp>\n\u003Cp>This has built in WiFi, the ability to integrate that with your sensors and plug that into your sensors, so we can collect data, soil temp moisture contents and, temperatures. So the key thing is to making sure that your plants grow successfully. Now in order to make this all work together to get you alerts and notifications, I'm gonna hand you off to my main automation guru, mister Bryant Gillespie.\u003C\u002Fp>\n\u003Cp>Speaker 4: Sorry, Jonathan. I, had these thinking glasses on to consider this problem. I just wanna say this is an amazing use case for direct us flows. We are going to be able to send automated notifications whenever these values are out of bounds. So here's what I'm thinking.\u003C\u002Fp>\n\u003Cp>Whenever a new item is created in the logging collection we trigger a flow. That flow does some magic and it will run some conditional operations to determine whether those values for moisture, for pH, for temperature are within the bounds that we set. If they are within those bounds nothing happens. You're a okay Sean. As soon as those values step out of bounds like they're on the court, boom you get a message via SMS through the Twilio API.\u003C\u002Fp>\n\u003Cp>And that's a wrap. Now to kick it over to my main man, AvDV, our head of insights.\u003C\u002Fp>\n\u003Cp>Speaker 5: Hey, Sean. If you're anything like me and you've got a lot of plants, I can imagine all these sensors are gonna be bringing in a ton of data into your into your director's project. So what better way to use insights or our dashboards to build up, targeted, insights, which give you a summary of all the information in one page. We have, time series charts that you can start to track the pH, the moisture, and the temperature over time. And with built in filters as well, you can you can filter by room, by particular plants, or even by the season if you wanna track, the growth or the moisture over the seasons.\u003C\u002Fp>\n\u003Cp>There's a ton more use cases that we could build in, but those are just good. Some 3 quick summaries of what you could build with the direct us insights.\u003C\u002Fp>\n\u003Cp>Speaker 0: Wow. This all sounds great. I'm gonna leave you in the workshop to make this real. So, Sean, we built you a soil monitor. There's sensors in here that every hour monitor the status of the plant.\u003C\u002Fp>\n\u003Cp>And if it's in need, we'll let you know via text messages exactly what's wrong.\u003C\u002Fp>\n\u003Cp>Speaker 2: Wow. That's amazing. I'll never kill again. Thanks, directors.\u003C\u002Fp>\n\u003Cp>Speaker 1: Thank you so much. What a wonderful first episode of Automate My Life. Thank you for taking part. If you have an idea for a future episode, you can probably keep it to yourself. This is probably a one and done.\u003C\u002Fp>\n\u003Cp>Thank you, and bye for now.&nbsp;\u003C\u002Fp>","Hello, and welcome to Automate My Life. In this show, we help regular people using Directus. And today, we are in sunny London with our first person in need, Sean. He has lots of problems. We can't help with most of them, but we certainly can help with 1. Over to Sean. So I'm here with Sean who has called in the Automate My Life team with a problem. Sean, what can we help you with? Hi, Keir. Thanks for coming. I have a problem. Tell me more. Look at this plant. What do you notice about it? Sean, this plant is dead. That is the problem. Okay. Yep. See, I have plants and they are not dead, but then no matter what I do, they become dead. Despite your best efforts. Despite my best and sometimes your mediocre efforts as well. Okay. I understand. Well, I think this is something we might be able to help with over in the workshop. Marvelous. Automate my life, directors team. Over to Beth. Okay, folks. You've heard what Sean's problem is. We've brought you here as the best of the best to work out a solution for Sean. The future is plants relies on you. What have you got? Hey, Sean. Welcome to the Directus workshopping team. We're all about automating our lives every day. I have a similar problem. My wife my partner, has a pepper garden. She's an avid gardener. She loves to garden. This year, she actually picked up a little pH temperature monitor or soil tester, to give you the soil quality and quantity and make sure that she could amend the soil properly for those kinds of things. But as I'm thinking about this now, I'd actually like to work with her, and we'll get a I I think next year, I'm actually going to do this particular project. But in the meantime, we'll talk about some of the ways we could solve this problem. There are both DIY options and aftermarket options. I think I'll opt for the DIY, And that will be using something along the lines of an e s ESP 32 Wi Fi sensor. There's a nice Directus blog that kind of goes through a use case for this. But we're gonna talk a little bit more in detail about some specific sensors and capabilities that we'll need to do for this. So there's an ESP 32. This has built in WiFi, the ability to integrate that with your sensors and plug that into your sensors, so we can collect data, soil temp moisture contents and, temperatures. So the key thing is to making sure that your plants grow successfully. Now in order to make this all work together to get you alerts and notifications, I'm gonna hand you off to my main automation guru, mister Bryant Gillespie. Sorry, Jonathan. I, had these thinking glasses on to consider this problem. I just wanna say this is an amazing use case for direct us flows. We are going to be able to send automated notifications whenever these values are out of bounds. So here's what I'm thinking. Whenever a new item is created in the logging collection we trigger a flow. That flow does some magic and it will run some conditional operations to determine whether those values for moisture, for pH, for temperature are within the bounds that we set. If they are within those bounds nothing happens. You're a okay Sean. As soon as those values step out of bounds like they're on the court, boom you get a message via SMS through the Twilio API. And that's a wrap. Now to kick it over to my main man, AvDV, our head of insights. Hey, Sean. If you're anything like me and you've got a lot of plants, I can imagine all these sensors are gonna be bringing in a ton of data into your into your director's project. So what better way to use insights or our dashboards to build up, targeted, insights, which give you a summary of all the information in one page. We have, time series charts that you can start to track the pH, the moisture, and the temperature over time. And with built in filters as well, you can you can filter by room, by particular plants, or even by the season if you wanna track, the growth or the moisture over the seasons. There's a ton more use cases that we could build in, but those are just good. Some 3 quick summaries of what you could build with the direct us insights. Wow. This all sounds great. I'm gonna leave you in the workshop to make this real. So, Sean, we built you a soil monitor. There's sensors in here that every hour monitor the status of the plant. And if it's in need, we'll let you know via text messages exactly what's wrong. Wow. That's amazing. I'll never kill again. Thanks, directors. Thank you so much. What a wonderful first episode of Automate My Life. Thank you for taking part. If you have an idea for a future episode, you can probably keep it to yourself. This is probably a one and done. Thank you, and bye for now. And that's something we can help you with. I certainly hope so. I think we can handle discus team. Wasn't there anybody there? Director. Director. Best best day in this one, 100%. Luke was real coming to a YouTube video.","1e89a2ee-cbe4-48d9-a6fa-250b8907ad9a",[1576,1577,1578],"718079f5-11af-4dff-b8c5-f9ddd3e905af","154d91af-0a0b-4641-b356-05bfa39f846d","3ed13642-d112-4e0c-8a8c-02ebac65e362",[],{"year":978,"number":889,"id":1581,"episodes":1582,"show":1583},"1ba3139f-9a4c-4b4e-b310-b5d23ca72aba",[1565],{"title":1584,"slug":1585},"Automate My Life!","automate-my-life",{"id":1587,"slug":1588,"vimeo_id":1589,"description":1590,"tile":1591,"length":213,"resources":1592,"people":12,"episode_number":213,"published":1570,"title":1608,"video_transcript_html":1609,"video_transcript_text":1610,"content":12,"seo":1611,"status":19,"episode_people":1612,"recommendations":1614,"season":1615},"731d8184-1ba4-4fa7-99e4-571492d1c552","preset-transformations","1030679756","Join Carmen as we assign our transformations to presets that we can apply to multiple images.","1c5f6ce6-3c69-408a-8aad-5257f7711fba",[1593,1596,1599,1602,1605],{"name":1594,"url":1595},"Photo credit: Torres del paine: photo by Karla Robinson on Unsplash","https:\u002F\u002Funsplash.com\u002Fes\u002Ffotos\u002Fun-lago-rodeado-de-montanas-HT2jDxe6UF4?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash",{"name":1597,"url":1598},"Photo credit: Osorno: photo by Willian Justen de Vasconcellos on Unsplash","https:\u002F\u002Funsplash.com\u002Fes\u002Ffotos\u002Fmonte-fuji-vRLMbO3Wpjw?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash",{"name":1600,"url":1601},"Photo credit: Geysers del Tatio: photo by Jorge Fernández Salas on Unsplash","https:\u002F\u002Funsplash.com\u002Fes\u002Ffotos\u002Ffoto-de-primer-plano-de-la-montana-01pVoT_YINk?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash",{"name":1603,"url":1604},"Photo credit: Rapa nui: photo by Sophie Laurent on Unsplash","https:\u002F\u002Funsplash.com\u002Fes\u002Ffotos\u002Fformacion-rocosa-gris-bajo-nubes-blancas-y-cielo-azul-durante-el-dia-BNdjmu-wLFk?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash",{"name":1606,"url":1607},"Photo credit: Marble Cathedral of lake General Carrera, Chilean Patagonia","https:\u002F\u002Fstock.adobe.com\u002Fat\u002Fcontributor\u002F200869569\u002Fnoradoa?load_type=author&prev_url=detail","Preset Transformations","\u003Cp>Speaker 0: Hello, everybody. My name is Carmen, and welcome to Sharp Focus, the show where we create a beautiful framing of how Directus allows you to transform your images and truly make them shine. Now in the last 4 episodes, gosh, they've gone by so quickly, we've seen how we can manipulate, create optimizations for, and apply advanced transformations of all of our images in direct Us. But getting to do so becomes a little bit cumbersome if you have to repeat yourself all the time. So in this final episode, we're gonna look at how you can create presets in Direct Us and just quickly grab those keys up and apply them to your images all in one go.\u003C\u002Fp>\u003Cp>And it's gonna be a lot of fun. So I hope you've got your cameras handy. I've got mine. Let's look into how we can apply those presets in Directus. Here we are in my Directus project dedicated to my photography, and we'll be looking at the final photo from my trip to Chile, La Capilla de Marmol or Marble Chapels located in the south of Chile in the Lake General Carrera, which is just beautiful, but I digress.\u003C\u002Fp>\u003Cp>So as much power and control as these custom and advanced transformations offer us, having to specify them every time we want to retrieve an image can become cumbersome. So what we can do is save presets that we can then later reference and think of this as a collection of transformations that we can then specify and apply at once. Now Directus actually comes with one of these built in, which I would like to show you now. In my code for my Nuxt app, you can see that I'm specifying a key and system large contain. And this is a built in preset that then shows the image in a nice large way that is contained.\u003C\u002Fp>\u003Cp>Fantastic. But how can we build our own? Let's go over to our settings module and its according settings section. And we're gonna scroll down here to our transformation presets. And we're gonna go ahead and click on create new.\u003C\u002Fp>\u003Cp>Now let's call this cool thumbnail. Naming was never really my thing. And now you're going to recognize a lot of these transformations that we've been using throughout this series. So let's go with contain, a width of 300, a height of 500. We will leave the upscaling as is, and our format is gonna be JPEG.\u003C\u002Fp>\u003Cp>We're not gonna do any advanced transformations for now using Sharp, and we're gonna go ahead and save that. Now that that's done and saved, let's go back to our application and start using it. So we've got our ID specified already, and we're gonna replace our key with, as said, cool thumbnail. Go ahead and save that. And now we're going to see, We've applied our preset all in one go.\u003C\u002Fp>\u003Cp>And now the great thing about this is that these transformations can be applied not just as the SDK level, but of course at a rest level. So if we wanted to go to that URL, put in our ID, and specify as a query parameter the key cool thumbnail, there's our cropped and resized image. That's how we can create and utilize presets of several transformations in one go. Alright, folks. Well, that's it.\u003C\u002Fp>\u003Cp>The last photoshoot is done. It's like we've taken everything we've learned in the last 4 episodes and culminated into one big preset that we can use time and time again without having to write out all of these transformations. So now we can optimize, we can manipulate, and we can apply advanced transformations to our images using Directus. So that ends series 1 of Sharp Focus. Thank you so much for watching.\u003C\u002Fp>\u003Cp>If you have any lingering doubts or wanna explore what else you can do with Directus, be sure to check out our documentation and check out the Directus community. But that's it for now. I will say, once again, thanks for watching. Remember, keep those cameras handy, and I will see you in the next episode. Bye for now.\u003C\u002Fp>","Hello, everybody. My name is Carmen, and welcome to Sharp Focus, the show where we create a beautiful framing of how Directus allows you to transform your images and truly make them shine. Now in the last 4 episodes, gosh, they've gone by so quickly, we've seen how we can manipulate, create optimizations for, and apply advanced transformations of all of our images in direct Us. But getting to do so becomes a little bit cumbersome if you have to repeat yourself all the time. So in this final episode, we're gonna look at how you can create presets in Direct Us and just quickly grab those keys up and apply them to your images all in one go. And it's gonna be a lot of fun. So I hope you've got your cameras handy. I've got mine. Let's look into how we can apply those presets in Directus. Here we are in my Directus project dedicated to my photography, and we'll be looking at the final photo from my trip to Chile, La Capilla de Marmol or Marble Chapels located in the south of Chile in the Lake General Carrera, which is just beautiful, but I digress. So as much power and control as these custom and advanced transformations offer us, having to specify them every time we want to retrieve an image can become cumbersome. So what we can do is save presets that we can then later reference and think of this as a collection of transformations that we can then specify and apply at once. Now Directus actually comes with one of these built in, which I would like to show you now. In my code for my Nuxt app, you can see that I'm specifying a key and system large contain. And this is a built in preset that then shows the image in a nice large way that is contained. Fantastic. But how can we build our own? Let's go over to our settings module and its according settings section. And we're gonna scroll down here to our transformation presets. And we're gonna go ahead and click on create new. Now let's call this cool thumbnail. Naming was never really my thing. And now you're going to recognize a lot of these transformations that we've been using throughout this series. So let's go with contain, a width of 300, a height of 500. We will leave the upscaling as is, and our format is gonna be JPEG. We're not gonna do any advanced transformations for now using Sharp, and we're gonna go ahead and save that. Now that that's done and saved, let's go back to our application and start using it. So we've got our ID specified already, and we're gonna replace our key with, as said, cool thumbnail. Go ahead and save that. And now we're going to see, We've applied our preset all in one go. And now the great thing about this is that these transformations can be applied not just as the SDK level, but of course at a rest level. So if we wanted to go to that URL, put in our ID, and specify as a query parameter the key cool thumbnail, there's our cropped and resized image. That's how we can create and utilize presets of several transformations in one go. Alright, folks. Well, that's it. The last photoshoot is done. It's like we've taken everything we've learned in the last 4 episodes and culminated into one big preset that we can use time and time again without having to write out all of these transformations. So now we can optimize, we can manipulate, and we can apply advanced transformations to our images using Directus. So that ends series 1 of Sharp Focus. Thank you so much for watching. If you have any lingering doubts or wanna explore what else you can do with Directus, be sure to check out our documentation and check out the Directus community. But that's it for now. I will say, once again, thanks for watching. Remember, keep those cameras handy, and I will see you in the next episode. Bye for now.","ebdc4bff-8de2-4616-9c87-b178bbdcd517",[1613],"a35f3518-cde8-42bc-be27-4b8d4aeea910",[],{"year":978,"number":527,"id":1616,"episodes":1617,"show":1622},"4726ce9e-9ed5-4d26-b76e-47d74381bc91",[1618,1619,1620,1621,1587],"3fd6dfb4-644b-43d0-9aef-5a6e5488efa8","954941eb-b737-412f-967b-387295df72bf","59b18d30-080b-42cf-84ef-fdca7542388d","1c8eb8c1-f7eb-4b24-9cac-e742a70a9da1",{"title":1623,"slug":1624},"Sharp Focus","sharp-focus",{"id":1620,"slug":1626,"vimeo_id":1627,"description":1628,"tile":1629,"length":213,"resources":1630,"people":12,"episode_number":264,"published":1570,"title":1636,"video_transcript_html":1637,"video_transcript_text":1638,"content":12,"seo":1639,"status":19,"episode_people":1640,"recommendations":1642,"season":1643},"size-and-performance","1030681873","Join Carmen as we transform our images to be more performant and format-conscious.","9534ee88-2302-4352-a715-391969efc88f",[1631,1632,1633,1634,1635],{"name":1594,"url":1595},{"name":1597,"url":1598},{"name":1600,"url":1601},{"name":1603,"url":1604},{"name":1606,"url":1607},"Size and Performance","\u003Cp>Speaker 0: Hello, everybody. My name is Carmen, and welcome to Sharp Focus, the show where we zoom in on how Directus allows you to transform your images and truly make them shine. Now in previous episodes, we've seen how Directus stores your images and how you can grant permission and access to them. And we've also seen how you can manipulate your images width and height and fit using the Directus SDK and REST API. So what's next?\u003C\u002Fp>\u003Cp>In this episode, we're going to focus on size and performance. If you're working with a lot of images, you wanna keep some consistency and focus on the user experience, making sure that the images are consistently sized, but more importantly, load quickly. So let's find out how Directus does that for you. Let me just grab my camera, and let's hop right in. All right.\u003C\u002Fp>\u003Cp>Here we are in my directest project dedicated to my photography. And once again, we're going to be looking at my photos from my trip to Chile. In this episode, we're going to be working with the photos of the geysers del Tapio, which are located in the Andes mountains of Northern Chile. And these are beautiful geysers that we're going to be working with. So I said in this episode, we're going to be focusing on the size and performance of transforming our images.\u003C\u002Fp>\u003Cp>Let's again, look at our Nuxt app, which has now been modified to also show the size of our image here, clocking in at 46 kilobytes. Now over in our code, we've got our width, our height, and our fit as we saw in the image manipulations of episode 2. Our first parameter that we're gonna be working with is quality, which can take a value of 0 to 100. The higher the value, the higher the quality, but let's see what happens when we go lower in quality. I'm gonna start with 50%.\u003C\u002Fp>\u003Cp>So we can see the file has been reduced to 26 kilobytes and the image looks a little bit rougher, but not too bad. Let's bring it a little lower, half that to 25. We're seeing a file size of 16 kilobytes and the image looks a little bit grainier. If I bring that down to 5%, we're gonna see the image gets even lower in quality. It becomes a little difficult to see our beautiful geysers, but the image size is 4 kilobytes.\u003C\u002Fp>\u003Cp>And if I bring it all the way to a 100%, we're gonna see the image looks beautiful, but the size is a 153 kilobytes. So let's leave it at that 80% sweet spot. Next, we'll look at the format attribute. Its value will modify the format of the image. So we have the options of auto, JPEG, PNG, webp, and tiff.\u003C\u002Fp>\u003Cp>So if we start with auto, which is a default value, directives will try to format it in webp or avif depending on browser support. Otherwise, it'll fall back to JPEG. Let's look at JPEG PNG. We'll see that our image now has a size of a 111 kilobytes. If we try and web peak, we'll see 43 kilobytes.\u003C\u002Fp>\u003Cp>Let's leave that with auto by default. And finally, we'll have a look at the without enlargement property, which defaults to true. So if we set that to false, Directus will not upscale the image. So now I have a lot more granular control over the sizing of my images when I use them in my applications. All right.\u003C\u002Fp>\u003Cp>Well, there you have it folks. We've seen how we can use the direct to SDK to manipulate the quality and the format and the upscaling of our images. And as you can see, it's super straightforward and split fast. So what's next in the next episode, we're really going to dive into the sharp API and get into all of the nitty gritty advanced transformation. This one's super exciting folks.\u003C\u002Fp>\u003Cp>So I'm looking forward to seeing you there. Just remember, keep your camera handy and I'll see you in the next one.\u003C\u002Fp>","Hello, everybody. My name is Carmen, and welcome to Sharp Focus, the show where we zoom in on how Directus allows you to transform your images and truly make them shine. Now in previous episodes, we've seen how Directus stores your images and how you can grant permission and access to them. And we've also seen how you can manipulate your images width and height and fit using the Directus SDK and REST API. So what's next? In this episode, we're going to focus on size and performance. If you're working with a lot of images, you wanna keep some consistency and focus on the user experience, making sure that the images are consistently sized, but more importantly, load quickly. So let's find out how Directus does that for you. Let me just grab my camera, and let's hop right in. All right. Here we are in my directest project dedicated to my photography. And once again, we're going to be looking at my photos from my trip to Chile. In this episode, we're going to be working with the photos of the geysers del Tapio, which are located in the Andes mountains of Northern Chile. And these are beautiful geysers that we're going to be working with. So I said in this episode, we're going to be focusing on the size and performance of transforming our images. Let's again, look at our Nuxt app, which has now been modified to also show the size of our image here, clocking in at 46 kilobytes. Now over in our code, we've got our width, our height, and our fit as we saw in the image manipulations of episode 2. Our first parameter that we're gonna be working with is quality, which can take a value of 0 to 100. The higher the value, the higher the quality, but let's see what happens when we go lower in quality. I'm gonna start with 50%. So we can see the file has been reduced to 26 kilobytes and the image looks a little bit rougher, but not too bad. Let's bring it a little lower, half that to 25. We're seeing a file size of 16 kilobytes and the image looks a little bit grainier. If I bring that down to 5%, we're gonna see the image gets even lower in quality. It becomes a little difficult to see our beautiful geysers, but the image size is 4 kilobytes. And if I bring it all the way to a 100%, we're gonna see the image looks beautiful, but the size is a 153 kilobytes. So let's leave it at that 80% sweet spot. Next, we'll look at the format attribute. Its value will modify the format of the image. So we have the options of auto, JPEG, PNG, webp, and tiff. So if we start with auto, which is a default value, directives will try to format it in webp or avif depending on browser support. Otherwise, it'll fall back to JPEG. Let's look at JPEG PNG. We'll see that our image now has a size of a 111 kilobytes. If we try and web peak, we'll see 43 kilobytes. Let's leave that with auto by default. And finally, we'll have a look at the without enlargement property, which defaults to true. So if we set that to false, Directus will not upscale the image. So now I have a lot more granular control over the sizing of my images when I use them in my applications. All right. Well, there you have it folks. We've seen how we can use the direct to SDK to manipulate the quality and the format and the upscaling of our images. And as you can see, it's super straightforward and split fast. So what's next in the next episode, we're really going to dive into the sharp API and get into all of the nitty gritty advanced transformation. This one's super exciting folks. So I'm looking forward to seeing you there. Just remember, keep your camera handy and I'll see you in the next one.","96fbce59-099e-498d-beeb-c07e5088e62b",[1641],"8479d2d3-dd74-41d6-96c8-4e3027228d25",[],{"year":978,"number":527,"id":1616,"episodes":1644,"show":1645},[1618,1619,1620,1621,1587],{"title":1623,"slug":1624},{"id":1619,"slug":1647,"vimeo_id":1648,"description":1649,"tile":1650,"length":195,"resources":1651,"people":12,"episode_number":177,"published":1570,"title":1657,"video_transcript_html":1658,"video_transcript_text":1659,"content":12,"seo":1660,"status":19,"episode_people":1661,"recommendations":1663,"season":1664},"image-manipulation","1030682505","Join Carmen as we apply focal points and other manipulations to our images.","ba3d5e9b-9c0c-49bc-9619-44608195a7a9",[1652,1653,1654,1655,1656],{"name":1594,"url":1595},{"name":1597,"url":1598},{"name":1600,"url":1601},{"name":1603,"url":1604},{"name":1606,"url":1607},"Image Manipulation","\u003Cp>Speaker 0: Hello, everybody. My name is Carmen, and welcome to Sharp Focus, the show where we create an aperture into how Directus helps you transform your images and truly make them shine. Now\u003C\u002Fp>\u003Cp>Speaker 1: in\u003C\u002Fp>\u003Cp>Speaker 0: the previous episode, we looked into how Directus lets you manage your images and grant access permissions publicly, which is going to come in super handy for this next episode, because we'll be working with the Directus SDK to apply some of our first image manipulations. So without further ado, let's grab our cameras and our code editor and find out how we can manage our images and manipulate them using Directus. So here we are in my Directus project dedicated\u003C\u002Fp>\u003Cp>Speaker 1: to my photography, and we're gonna be looking at another one of my photos from my trip to Chile. This time, we'll\u003C\u002Fp>\u003Cp>Speaker 0: be looking at the Osorno volcano, which is located in the northern border of the Patagonia region of Chile. Now I've developed a little Nuxt application that I will use to show those photos, which I've got running on another tab here. Now let's take a look. And gosh, as you can see, it's quite blown out. I can, oh, there it is.\u003C\u002Fp>\u003Cp>There's a volcano.\u003C\u002Fp>\u003Cp>Speaker 1: Now, what I would like\u003C\u002Fp>\u003Cp>Speaker 0: to do for my application is manipulate the image using Directus in order to show it\u003C\u002Fp>\u003Cp>Speaker 1: as a thumbnail. So let's take a moment to look at the code. So in\u003C\u002Fp>\u003Cp>Speaker 0: this example, we'll be using the Directus SDK to retrieve images. You can see on line 4, I've got the file ID. On line 5, I'm instantiating that Directus instance with my PhotographyDirectus local instance. And the important part, on line 9,\u003C\u002Fp>\u003Cp>Speaker 1: I'm retrieving that asset as a readable stream, which I then convert into a little URL that I\u003C\u002Fp>\u003Cp>Speaker 0: can display here on line\u003C\u002Fp>\u003Cp>Speaker 1: 21. So what we're doing on line 10 is reading that asset raw, just getting all of the image data. But\u003C\u002Fp>\u003Cp>Speaker 0: as we saw in the previous video, we can pass in some parameters. Now the way this works in the direct to SDK is this is passed as an object. So what I can do here, for example, is pass in a width. Go with 300. I'm gonna go ahead and save that.\u003C\u002Fp>\u003Cp>Now let's go back to application and see how that looks. Marvelous. Much better. Now we can see the volcano there in its full glory. But say I wanted to have a little bit more of a fancy grid and manipulate the size\u003C\u002Fp>\u003Cp>Speaker 1: a little bit more. For this, we can also use a height attribute. Now, what I'm gonna do next is have my editor and my browser side by side. Now, when I add a high attribute, what directives will do is automatically crop the image so that we can have\u003C\u002Fp>\u003Cp>Speaker 0: the volcano in its full glory. But what if I wanted to have a different part of the image displayed when it's cropped?\u003C\u002Fp>\u003Cp>Speaker 1: This is where focal points come in. So let's go back to Directus real quick. Now here, we can go ahead and click on the edit button to change different aspects about our image, crop it\u003C\u002Fp>\u003Cp>Speaker 0: and other stuff, flip it if we want to. What I wanna focus on here is the focal point tool. So what I'm gonna do is let's say I wanna actually focus on this little cloud over\u003C\u002Fp>\u003Cp>Speaker 1: here. I'm gonna drag and drop it there. Hit save. Go back to my application. And I would actually like to maybe crop it\u003C\u002Fp>\u003Cp>Speaker 0: a little bit differently. Let's go with 300 by 600. Go ahead and save that and check it out. Now this little cloud is in full display, which is all well and good, but of course I really want my volcano back. So I'm just gonna go ahead and make that the focal point.\u003C\u002Fp>\u003Cp>Maybe actually do keep that at 500. By 300.\u003C\u002Fp>\u003Cp>Speaker 1: Much better. So we've got our height, we've got our width and our focal point.\u003C\u002Fp>\u003Cp>Speaker 0: The last thing we're going\u003C\u002Fp>\u003Cp>Speaker 1: to cover here is the fit. Now the\u003C\u002Fp>\u003Cp>Speaker 0: fit parameter dictates how the image is going to get manipulated depending on the width and height that we provide. And by default, this is set to be cover. We'll notice not much changes when we set the fit to be cover. But we actually have a couple of other options. For example,\u003C\u002Fp>\u003Cp>Speaker 1: we have contained, which as we'll see allows us to\u003C\u002Fp>\u003Cp>Speaker 0: see the entire image and just add some letter boxing where necessary. If I were to switch the width and the height around, then it would add letter boxing accordingly.\u003C\u002Fp>\u003Cp>Speaker 1: But okay. Let's look at what else we've got. If we go with the inside fit,\u003C\u002Fp>\u003Cp>Speaker 0: what Directus is going to do is resize the image so that the dimensions are less than or equal to the requested widths and height, keeping the entirety of the image in there. And conversely, outside fit will resize it to be greater than or equal to the requested width and height. So in this case, for example, we're keeping the 500\u003C\u002Fp>\u003Cp>Speaker 1: height. So to wrap up, I'm\u003C\u002Fp>\u003Cp>Speaker 0: gonna go ahead and keep that with cover because I'd like to have the images displayed next to each other, sort of like this gallery we see here. And now I know that with focal points, I can select my favorite parts of the photos to be seen when cropped. All right, folks. Well, there you have it. We've learned how to manipulate images with height and fit using Directus SDK.\u003C\u002Fp>\u003Cp>But don't worry. There's much more in store. So stay tuned for the next episode where we'll learn how to work with size and performance of our images using Directus. I cannot wait to show you what beautiful thumbnails we can create. So stay tuned and see you next episode.\u003C\u002Fp>\u003Cp>Keep those cameras handy.\u003C\u002Fp>","Hello, everybody. My name is Carmen, and welcome to Sharp Focus, the show where we create an aperture into how Directus helps you transform your images and truly make them shine. Now in the previous episode, we looked into how Directus lets you manage your images and grant access permissions publicly, which is going to come in super handy for this next episode, because we'll be working with the Directus SDK to apply some of our first image manipulations. So without further ado, let's grab our cameras and our code editor and find out how we can manage our images and manipulate them using Directus. So here we are in my Directus project dedicated to my photography, and we're gonna be looking at another one of my photos from my trip to Chile. This time, we'll be looking at the Osorno volcano, which is located in the northern border of the Patagonia region of Chile. Now I've developed a little Nuxt application that I will use to show those photos, which I've got running on another tab here. Now let's take a look. And gosh, as you can see, it's quite blown out. I can, oh, there it is. There's a volcano. Now, what I would like to do for my application is manipulate the image using Directus in order to show it as a thumbnail. So let's take a moment to look at the code. So in this example, we'll be using the Directus SDK to retrieve images. You can see on line 4, I've got the file ID. On line 5, I'm instantiating that Directus instance with my PhotographyDirectus local instance. And the important part, on line 9, I'm retrieving that asset as a readable stream, which I then convert into a little URL that I can display here on line 21. So what we're doing on line 10 is reading that asset raw, just getting all of the image data. But as we saw in the previous video, we can pass in some parameters. Now the way this works in the direct to SDK is this is passed as an object. So what I can do here, for example, is pass in a width. Go with 300. I'm gonna go ahead and save that. Now let's go back to application and see how that looks. Marvelous. Much better. Now we can see the volcano there in its full glory. But say I wanted to have a little bit more of a fancy grid and manipulate the size a little bit more. For this, we can also use a height attribute. Now, what I'm gonna do next is have my editor and my browser side by side. Now, when I add a high attribute, what directives will do is automatically crop the image so that we can have the volcano in its full glory. But what if I wanted to have a different part of the image displayed when it's cropped? This is where focal points come in. So let's go back to Directus real quick. Now here, we can go ahead and click on the edit button to change different aspects about our image, crop it and other stuff, flip it if we want to. What I wanna focus on here is the focal point tool. So what I'm gonna do is let's say I wanna actually focus on this little cloud over here. I'm gonna drag and drop it there. Hit save. Go back to my application. And I would actually like to maybe crop it a little bit differently. Let's go with 300 by 600. Go ahead and save that and check it out. Now this little cloud is in full display, which is all well and good, but of course I really want my volcano back. So I'm just gonna go ahead and make that the focal point. Maybe actually do keep that at 500. By 300. Much better. So we've got our height, we've got our width and our focal point. The last thing we're going to cover here is the fit. Now the fit parameter dictates how the image is going to get manipulated depending on the width and height that we provide. And by default, this is set to be cover. We'll notice not much changes when we set the fit to be cover. But we actually have a couple of other options. For example, we have contained, which as we'll see allows us to see the entire image and just add some letter boxing where necessary. If I were to switch the width and the height around, then it would add letter boxing accordingly. But okay. Let's look at what else we've got. If we go with the inside fit, what Directus is going to do is resize the image so that the dimensions are less than or equal to the requested widths and height, keeping the entirety of the image in there. And conversely, outside fit will resize it to be greater than or equal to the requested width and height. So in this case, for example, we're keeping the 500 height. So to wrap up, I'm gonna go ahead and keep that with cover because I'd like to have the images displayed next to each other, sort of like this gallery we see here. And now I know that with focal points, I can select my favorite parts of the photos to be seen when cropped. All right, folks. Well, there you have it. We've learned how to manipulate images with height and fit using Directus SDK. But don't worry. There's much more in store. So stay tuned for the next episode where we'll learn how to work with size and performance of our images using Directus. I cannot wait to show you what beautiful thumbnails we can create. So stay tuned and see you next episode. Keep those cameras handy.","83f7e0c8-c1b1-4b4c-a55f-2ca7a6648e86",[1662],"9d8a980e-b165-4e19-8e08-33c33f467776",[],{"year":978,"number":527,"id":1616,"episodes":1665,"show":1666},[1618,1619,1620,1621,1587],{"title":1623,"slug":1624},{"id":1621,"slug":1668,"vimeo_id":1669,"description":1670,"tile":1671,"length":213,"resources":1672,"people":12,"episode_number":159,"published":1570,"title":1678,"video_transcript_html":1679,"video_transcript_text":1680,"content":12,"seo":1681,"status":19,"episode_people":1682,"recommendations":1684,"season":1685},"advanced-transformations","1030680517","Join Carmen as we apply advanced transformations to our images leveraging the Sharp API.","3e734707-3997-41c3-9e81-543f7d27c4a3",[1673,1674,1675,1676,1677],{"name":1594,"url":1595},{"name":1597,"url":1598},{"name":1600,"url":1601},{"name":1603,"url":1604},{"name":1606,"url":1607},"Advanced Transformations","\u003Cp>Speaker 0: Hello, everybody. My name is Carmen, and welcome to Sharp Focus, the show where we truly capture how it is that allows you to transform your images and truly make them shine. Now in previous episodes, we've seen how Directus manages your images for you, how you can set permissions, how you can use the SDK to manipulate the width, the height, the fit, the quality, the performance, the format of your images, and much more. But in this episode, we're gonna get into some advanced stuff, my friends. We're gonna be looking at how Directus exposes the Node JS Sharp API to allow you to do some truly wild transformations.\u003C\u002Fp>\u003Cp>I'm talking things like rotating images, resizing them, shifting them, tilting them, applying color tints, negation. Oh gosh. There's so much. But don't take my word for it. Let's just hop into some code.\u003C\u002Fp>\u003Cp>Let me just grab my camera here and let's get right into it. See you there. Here we are in my directus project dedicated to my photography, and we'll be taking a look at one of my photos from my recent trip to Chile. In this episode, we'll be working with this photo from Rapa Nui or Easter Island. And as said, we'll be working with advanced transformations.\u003C\u002Fp>\u003Cp>In order to have advanced control over your files, Directus exposes the full Sharp Node JS API, which has a bevy of options to allow us to work with our images. I'm talking things like rotating, flipping images, flopping images, sharpening, blurring, flattening, negate, color negation, color manipulation, channel manipulation, and many other options. So let's play around with some of these. We'll be working once again with our Nuxt applications, applying some of these transformations. Now you can see that I've applied the width attribute, but we can expand on this using the transforms property, which is an array of Sharp API calls.\u003C\u002Fp>\u003Cp>And these are applied as array pairs of property and value. So for example, I'm gonna use the resize property, which allows me to resize my image and make it 1,000 pixels wide. Thereby replacing the width property. And I'm just showing this as something that's possible to do. We're not gonna see much of a difference yet, but let's make things a little bit more interesting.\u003C\u002Fp>\u003Cp>Let's add another advanced transformation. How about rotation? Let's rotate it by 90 degrees. Fabulous. Let's try some other stuff and you can see, I can concatenate several transformations.\u003C\u002Fp>\u003Cp>For example, let's make it blurry. Let's blur it by 45%. Yep. That's a bit much. Let's blur it by about 15%.\u003C\u002Fp>\u003Cp>Cool. Let's make it 5%. Just give it a little bit of an enigma here. Fantastic. Finally, we'll do something just a touch more enigmatic, which is to use the negate transformation.\u003C\u002Fp>\u003Cp>We'll negate it. And you see didn't pass any properties and it already reversed the colors. Yep. It's totally possible to concatenate several operations and you can apply them in different orders for different effects. For example, now that I've resized it to be a 1000 pixels wide after I've rotated, you can see the image is wider.\u003C\u002Fp>\u003Cp>But what if we wanted to have that height back? Well, the resize transformation does take several parameters. For example, let's make it 500 pixels high. Cool. But, yeah, as said, advanced transformations and other manipulations can be applied in combination.\u003C\u002Fp>\u003Cp>So what you can do, for example, is say I want to reduce the quality of the ultimate image. Let's make it 25%. Save that. And now I've got a blurry negated rotated image. Alrighty.\u003C\u002Fp>\u003Cp>Well, I don't know about the rest of y'all, but I'm always so impressed with how much you can do using these advanced transformations. And as you can see, it can get pretty cool, all powered by Directus' API and SDK. So what's next in store? What we're gonna cover in the next and final episode is how you can apply all of the stuff we can been doing, but with only presets, which means not having to repeat yourself. And as software developers love to keep stuff D R Y, don't repeat yourself.\u003C\u002Fp>\u003Cp>So let's find out how we'll do that in the next episode. But until then, keep those cameras handy, and I'll see you next time.\u003C\u002Fp>","Hello, everybody. My name is Carmen, and welcome to Sharp Focus, the show where we truly capture how it is that allows you to transform your images and truly make them shine. Now in previous episodes, we've seen how Directus manages your images for you, how you can set permissions, how you can use the SDK to manipulate the width, the height, the fit, the quality, the performance, the format of your images, and much more. But in this episode, we're gonna get into some advanced stuff, my friends. We're gonna be looking at how Directus exposes the Node JS Sharp API to allow you to do some truly wild transformations. I'm talking things like rotating images, resizing them, shifting them, tilting them, applying color tints, negation. Oh gosh. There's so much. But don't take my word for it. Let's just hop into some code. Let me just grab my camera here and let's get right into it. See you there. Here we are in my directus project dedicated to my photography, and we'll be taking a look at one of my photos from my recent trip to Chile. In this episode, we'll be working with this photo from Rapa Nui or Easter Island. And as said, we'll be working with advanced transformations. In order to have advanced control over your files, Directus exposes the full Sharp Node JS API, which has a bevy of options to allow us to work with our images. I'm talking things like rotating, flipping images, flopping images, sharpening, blurring, flattening, negate, color negation, color manipulation, channel manipulation, and many other options. So let's play around with some of these. We'll be working once again with our Nuxt applications, applying some of these transformations. Now you can see that I've applied the width attribute, but we can expand on this using the transforms property, which is an array of Sharp API calls. And these are applied as array pairs of property and value. So for example, I'm gonna use the resize property, which allows me to resize my image and make it 1,000 pixels wide. Thereby replacing the width property. And I'm just showing this as something that's possible to do. We're not gonna see much of a difference yet, but let's make things a little bit more interesting. Let's add another advanced transformation. How about rotation? Let's rotate it by 90 degrees. Fabulous. Let's try some other stuff and you can see, I can concatenate several transformations. For example, let's make it blurry. Let's blur it by 45%. Yep. That's a bit much. Let's blur it by about 15%. Cool. Let's make it 5%. Just give it a little bit of an enigma here. Fantastic. Finally, we'll do something just a touch more enigmatic, which is to use the negate transformation. We'll negate it. And you see didn't pass any properties and it already reversed the colors. Yep. It's totally possible to concatenate several operations and you can apply them in different orders for different effects. For example, now that I've resized it to be a 1000 pixels wide after I've rotated, you can see the image is wider. But what if we wanted to have that height back? Well, the resize transformation does take several parameters. For example, let's make it 500 pixels high. Cool. But, yeah, as said, advanced transformations and other manipulations can be applied in combination. So what you can do, for example, is say I want to reduce the quality of the ultimate image. Let's make it 25%. Save that. And now I've got a blurry negated rotated image. Alrighty. Well, I don't know about the rest of y'all, but I'm always so impressed with how much you can do using these advanced transformations. And as you can see, it can get pretty cool, all powered by Directus' API and SDK. So what's next in store? What we're gonna cover in the next and final episode is how you can apply all of the stuff we can been doing, but with only presets, which means not having to repeat yourself. And as software developers love to keep stuff D R Y, don't repeat yourself. So let's find out how we'll do that in the next episode. But until then, keep those cameras handy, and I'll see you next time.","ba4fb8f5-d77e-492b-9ee2-07449c5e10af",[1683],"4daa5c56-f887-4c28-8002-4ea130548114",[],{"year":978,"number":527,"id":1616,"episodes":1686,"show":1687},[1618,1619,1620,1621,1587],{"title":1623,"slug":1624},{"id":1618,"slug":1689,"vimeo_id":1690,"description":1691,"tile":1692,"length":129,"resources":1693,"people":12,"episode_number":13,"published":1570,"title":1700,"video_transcript_html":1701,"video_transcript_text":1702,"content":12,"seo":1703,"status":19,"episode_people":1704,"recommendations":1706,"season":1707},"image-transformations","1030681299","Join Carmen as we learn what image transformations are and how you can assign permissions to them in Directus.","88c07dac-56dd-4ea7-9cbf-a0cef89b3fbe",[1694,1695,1696,1697,1698],{"name":1594,"url":1595},{"name":1597,"url":1598},{"name":1600,"url":1601},{"name":1603,"url":1604},{"name":1699,"url":1607},"Photo credit: Marble Cathedral of lake General Carrera, Chilean Patagonia ","What are Image Transformations","\u003Cp>Speaker 0: Hello, everybody, and welcome to Sharp Focus, the show where we shine a lens on how Directus helps you transform your images and truly make them shine. My name is Carmen, and I will be your guide over the next 5 episodes where we'll learn how images can be transformed, how they work under the hood, how they can be manipulated, how they can be resized to be more performant, how you can use advanced transformations, and how you can use presets to take all of the above and apply it in one fell swoop. Now as it turns out, Directus offers a bevy of options for working with images out of the box, powered by the sharp API from node JS. And we're going to be taking a look at how to do that in a way that's going to be super fun and super approachable. So won't you join me in grabbing our cameras and let's dive right into finding out how Directus works with images.\u003C\u002Fp>\u003Cp>So here we are in my very own Directus project running locally dedicated to my photography. Right now, we are in the file library module where you can see that I have a folder over here on the left sidebar. This folder contains photos from my latest trip to my home country, Chile. Now I'm going to use Directus to work with these images for different applications, thumbnails, that sort of thing. But as you can see, a lot of these images are of varying sizes and qualities.\u003C\u002Fp>\u003Cp>But that's okay. We can use directives to transform these images to create some consistency and even add some cool effects, which we'll be doing in this series. But first, let's take a little look at what makes these images tick. Now I've already uploaded these to Directus, so this gives me a couple of options to work with them. If I click on one of the images, for example, the photo of my trip to the national park, Torres del Paine, we will see that we can do things like change the title, add a description, some tags, location, if we wish, some focal points, more on that later, and also have access to its asset ID.\u003C\u002Fp>\u003Cp>But that's not all. We can even edit that image. Now we're not going to be doing that in this series except for using it to create focal points. But again, more on that later. Now, if I want to access that image from say my browser, I can take that asset ID and navigate to my instances URL \u002Fassets\u002Ftheid of that image, which is fantastic.\u003C\u002Fp>\u003Cp>But one thing to bear in mind, I'm logged into Directus as an admin. But if I wanted to access this image from an application, I would need to assign that application the correct permissions, which we can do here in Directus. When I navigate to the settings module and then go over to access policies, we can see that I already have some permissions and roles set for administrators, which is my logged in user. But I want to make these images fully public. So what I can do is click here on public and under permissions, click on add collection and select the Directus Files system collection.\u003C\u002Fp>\u003Cp>I will be able to assign different permissions for folks accessing that from anywhere. But I only want to give folks the ability to read. So we'll click here on read access and then assign all access. The last thing to do is to save that permission. Fantastic.\u003C\u002Fp>\u003Cp>Now I can access this image from a browser, from an application, from anywhere. So let's go back and look at that image. Now you remember that these images are of different sizes and qualities. So in order to create some consistencies, we're going to be applying different image transformations, but also giving them a little bit of pizzazz with some advanced transformations. It's gonna be super cool.\u003C\u002Fp>\u003Cp>Before we wrap up this video, I'd like to show you a couple of the types of transformations we can do. In the next video, we'll be looking at image manipulation where we can use query parameters like width, like height, and fit to manipulate our image. And in this case, make it a consistent 300 by 300 pixels. But that's not all. In our 3rd episode, we'll be looking at how we can manipulate the size and performance of our images.\u003C\u002Fp>\u003Cp>For example, here we're applying a quality of 5% and a format of webP to be used in our applications. Next. Now bear with me. We're going to be doing some advanced transformations. Now this looks a little wild, but you'll see that we're applying some powerful transforms with that sharp API.\u003C\u002Fp>\u003Cp>So we're applying a blur, a tint, we're negating colors. All of this is done with direct us, which is super cool. And finally, in our last episode, we'll talk about preset transformation where we can assign presets to a key. And that way we don't have to type out all of those transformations, but rather place them in one consistent place in Directus. Finally, let's talk about that processing power.\u003C\u002Fp>\u003Cp>You might think it'd be pretty cumbersome if every time I reload this image, it reprocesses all of those transformations. And I have good news. That transformation is done once and then saved in Directus, and you don't have to apply it every time. That means that you can reload that image worry free of any extra processing power done. So there you have it.\u003C\u002Fp>\u003Cp>Now we've seen how direct us works with images out of the box and how we can apply access permissions, which is going to be super handy because in the next 4 episodes, we're going to be working with a new Nuxt application to work with these images and display them in a variety of ways using transformations. So what's next? In episode 2, we'll be looking at how to resize our images using width, height, and other parameters, and I can't wait to show you all how it works. So till next time, keep those cameras ready.\u003C\u002Fp>","Hello, everybody, and welcome to Sharp Focus, the show where we shine a lens on how Directus helps you transform your images and truly make them shine. My name is Carmen, and I will be your guide over the next 5 episodes where we'll learn how images can be transformed, how they work under the hood, how they can be manipulated, how they can be resized to be more performant, how you can use advanced transformations, and how you can use presets to take all of the above and apply it in one fell swoop. Now as it turns out, Directus offers a bevy of options for working with images out of the box, powered by the sharp API from node JS. And we're going to be taking a look at how to do that in a way that's going to be super fun and super approachable. So won't you join me in grabbing our cameras and let's dive right into finding out how Directus works with images. So here we are in my very own Directus project running locally dedicated to my photography. Right now, we are in the file library module where you can see that I have a folder over here on the left sidebar. This folder contains photos from my latest trip to my home country, Chile. Now I'm going to use Directus to work with these images for different applications, thumbnails, that sort of thing. But as you can see, a lot of these images are of varying sizes and qualities. But that's okay. We can use directives to transform these images to create some consistency and even add some cool effects, which we'll be doing in this series. But first, let's take a little look at what makes these images tick. Now I've already uploaded these to Directus, so this gives me a couple of options to work with them. If I click on one of the images, for example, the photo of my trip to the national park, Torres del Paine, we will see that we can do things like change the title, add a description, some tags, location, if we wish, some focal points, more on that later, and also have access to its asset ID. But that's not all. We can even edit that image. Now we're not going to be doing that in this series except for using it to create focal points. But again, more on that later. Now, if I want to access that image from say my browser, I can take that asset ID and navigate to my instances URL \u002Fassets\u002Ftheid of that image, which is fantastic. But one thing to bear in mind, I'm logged into Directus as an admin. But if I wanted to access this image from an application, I would need to assign that application the correct permissions, which we can do here in Directus. When I navigate to the settings module and then go over to access policies, we can see that I already have some permissions and roles set for administrators, which is my logged in user. But I want to make these images fully public. So what I can do is click here on public and under permissions, click on add collection and select the Directus Files system collection. I will be able to assign different permissions for folks accessing that from anywhere. But I only want to give folks the ability to read. So we'll click here on read access and then assign all access. The last thing to do is to save that permission. Fantastic. Now I can access this image from a browser, from an application, from anywhere. So let's go back and look at that image. Now you remember that these images are of different sizes and qualities. So in order to create some consistencies, we're going to be applying different image transformations, but also giving them a little bit of pizzazz with some advanced transformations. It's gonna be super cool. Before we wrap up this video, I'd like to show you a couple of the types of transformations we can do. In the next video, we'll be looking at image manipulation where we can use query parameters like width, like height, and fit to manipulate our image. And in this case, make it a consistent 300 by 300 pixels. But that's not all. In our 3rd episode, we'll be looking at how we can manipulate the size and performance of our images. For example, here we're applying a quality of 5% and a format of webP to be used in our applications. Next. Now bear with me. We're going to be doing some advanced transformations. Now this looks a little wild, but you'll see that we're applying some powerful transforms with that sharp API. So we're applying a blur, a tint, we're negating colors. All of this is done with direct us, which is super cool. And finally, in our last episode, we'll talk about preset transformation where we can assign presets to a key. And that way we don't have to type out all of those transformations, but rather place them in one consistent place in Directus. Finally, let's talk about that processing power. You might think it'd be pretty cumbersome if every time I reload this image, it reprocesses all of those transformations. And I have good news. That transformation is done once and then saved in Directus, and you don't have to apply it every time. That means that you can reload that image worry free of any extra processing power done. So there you have it. Now we've seen how direct us works with images out of the box and how we can apply access permissions, which is going to be super handy because in the next 4 episodes, we're going to be working with a new Nuxt application to work with these images and display them in a variety of ways using transformations. So what's next? In episode 2, we'll be looking at how to resize our images using width, height, and other parameters, and I can't wait to show you all how it works. So till next time, keep those cameras ready.","560a8148-6c64-4016-9729-cf5ba5a4cb56",[1705],"b446d476-774a-4918-85d6-174bca22b2a6",[],{"year":978,"number":527,"id":1616,"episodes":1708,"show":1709},[1618,1619,1620,1621,1587],{"title":1623,"slug":1624},{"id":1542,"slug":1711,"vimeo_id":1712,"description":1713,"tile":1714,"length":263,"resources":12,"people":12,"episode_number":159,"published":1715,"title":1716,"video_transcript_html":1717,"video_transcript_text":1718,"content":12,"seo":1719,"status":19,"episode_people":1720,"recommendations":1725,"season":1726},"4-november-2024","1030913964","Join us for The Changelog, taking you through the month’s Directus updates including product updates, new content and community contribution highlights. This month's show includes a community showcase of a note taking system from Josh, Carmen taking you the first episode of Sharp Focus, Kevin with the new guest Author program and more...","9bde0965-1d3e-4d2e-b5c1-c3e11711200b","2024-11-18","November 2024","\u003Cp>Speaker 0: All right. Hello. Hello everyone. Hopefully you can all hear me and if you can I would love to know where you are joining us from today? 1, to say that\u003C\u002Fp>\u003Cp>Speaker 1: you can hear me and, 2, because I'm nosy and\u003C\u002Fp>\u003Cp>Speaker 0: I wanna know where everyone is. Last time everyone was all over and it was really great to hear. Personally, I am joining from London where we had the first snow of the fall this morning. It was quite rainy snow like it wasn't magical magical but it counted and that's what matters so that is where I am from. Excellent, Carmen, thank\u003C\u002Fp>\u003Cp>Speaker 1: you so much for telling me you can hear me, I appreciate it. Vienna, Austria, we've got Sweden, USA, Finland, amazing, welcome. I hope you're all having such an amazing day. Devon, Germany, very cool, Netherlands as well. We have some very fun and also educational, material coming up for you for the rest of this show.\u003C\u002Fp>\u003Cp>If you've got any questions throughout, we've got\u003C\u002Fp>\u003Cp>Speaker 0: the chat open, and there's a couple of people from our team in it as well. So please do let us know if you have any questions. But we are starting off, the November change log with a product update from Bryant. So I'm gonna send you over to him.\u003C\u002Fp>\u003Cp>Speaker 2: Thanks, Beth. Bryant here for Directus, and here's a quick rundown of the core changes that have happened since the last changelog. Version 11.2.0 brought TUS resumable file upload protocol support to the Supabase, Azure, Cloudinary, and GCS storage adapters. And we already had tough support for the AWS s three compatible and local asset storage adapters. Which basically means, you are now fully covered when uploading large files, like videos to Directus.\u003C\u002Fp>\u003Cp>In version 11 dot 1 dot 2, we introduced a migration for Directus comments. There's now a dedicated directus underscore comments collection, which will make it much much easier for you to use comments in your front end projects via the API. We previously stored comments in the directus underscore activity collection, which admittedly was a bit messy because of all the other activity logs we stored alongside those comments. So this welcome change brings new API endpoints for comments, but the existing endpoints are still 100% totally functional. One note though, primary keys are now UUIDs instead of integers.\u003C\u002Fp>\u003Cp>So that could impact any custom type checking implementations you set up. The Directus SDK's internal comment endpoints have also been updated to reflect this change. And I know you're all for avoiding errors, so just ensure your Directus version is compatible with the latest SDK when you're using those comment functions. And there's just one more change with version 11.1.2. We've made some improvements to content versioning.\u003C\u002Fp>\u003Cp>Internally, we stored every change to a content version separately in the directus_revisions collection. And then we merge those together when promoting a version. In this release, we've added a new delta field to directus_versions collection, and that combines all the revisions into a single field. So all that to say, this means you'll be able to prune your revisions, which are the individual changes to a piece of content, without losing your versions. And with that, back over to you, Beth.\u003C\u002Fp>\u003Cp>Speaker 0: I'm going to demonstrate each of the new extensions in Director's Labs this month. As a reminder, Director's Labs is our team's experimental organization on GitHub. Typically, these extensions aren't maintained the same way that the main directors project is, but we have now committed to maintaining 4 of them. They are the spreadsheet layout, the Gantt chart layout, the Command Palette module, and the calculated fields bundle. You can see all these extensions, including which extensions are maintained, over on the extensions repo.\u003C\u002Fp>\u003Cp>Onto the demos. Directors AI is a collection of extensions that allows you to leverage the best AI tools from within Directors Automate. Today, we have 2 announcements, a brand new extension and an improvement. Let's start with a brand new extension, the AI web scraper. This allows you to scrape web pages and receive structured data back.\u003C\u002Fp>\u003Cp>We're using FireCrawl to perform the scraping, so you'll need an API key from them. You put a web page or a link to a PDF. Here, we're using directors. Io. If we go into the actions before scraping, here you can get the scraper to perform actions on the web page and add additional metadata about the action.\u003C\u002Fp>\u003Cp>For example, which element to click on or how far to scroll. For the data to extract, here's where you tell it what data you wanted to extract, and it will use AI to extract it. In the property description, we're going to put permission, string as data type, and we're going to enable making it a requirement. Optionally, there are additional formats, so you can also specify how you want the pages content to be presented back to you. They've also just had their launch week and we've implemented their brand new features such as the country, language, and mobile options.\u003C\u002Fp>\u003Cp>The other announcement is an improvement to the AI writer. The biggest change here is that before you could only use OpenAI's models and now we're extending the option to use multiple AI providers such as Anthropic, as well as different AI models. Other than that, everything else works as it did before, and this expansion was done thanks to an issue that was opened on the Directors Labs Extensions repo. So please do keep bringing your feedback for these. That's what's new for Directors AI We often hear from people that they love Directors insights, but want more ways to visualise their data.\u003C\u002Fp>\u003Cp>So we've added 4 new panels, which you can install now via the marketplace. 1st, we have the tree map chart. Select a collection, optionally a filter, which field you want to use for the labels and the value, and any aggregation you would like to perform. For example, here we are summing the value of all deals per company, so each company has its own cell at the size of all values related to that company. Next, we have funnel chart.\u003C\u002Fp>\u003Cp>Now, the funnel chart works exactly the same way, but visualises data differently. So depending on your needs might be more appropriate for you. For example, here we have a typical sales funnel. Funnel charts excel when people, or entities, follow a linear path and you want to see how many convert between stages. Next is the scatterplot.\u003C\u002Fp>\u003Cp>If you've never seen one before, this is a very typical two axis chart where values are plotted as dots. You can optionally add axis labels and hover over any of the dots to see the values. Finally, we have the timeline chart. This is a sibling to the Gantt chart layout that we recently released last month. We have less space to work with in a panel, so it's a simplified version, but it can be really powerful to include for project management.\u003C\u002Fp>\u003Cp>This relies on having 2 date fields, a start and an end, and then a label for the axes on the left and some display text for the item itself. We hope you enjoy our new panels. We certainly think that people are going to get a lot of use out of them. Extension authors often want to extend the functionality of our built in interfaces, And a few months ago, we released the WYSIWYG and block editor as boilerplates to build on top of. Today, we are releasing our first layout boilerplate, the table layout.\u003C\u002Fp>\u003Cp>This was already the basis for our spreadsheet layout extension, and now you can build on top of it and extend it too. You can find this in our Director's Labs extension repo, And we hope that this supercharges your extension building.\u003C\u002Fp>\u003Cp>Speaker 3: Hello there. Today, we are announcing our brand new and improved guest author program. The directors community is filled with people who use directors in so many different ways, so many different languages, frameworks, use cases, and this program is all about bringing those experiences to the tutorial section in our new documentation to help new directors users and existing directors users be as successful as possible. So we invite you to apply to be part of our brand new guest author program. Previously, our guest author program worked where you submitted your own ideas.\u003C\u002Fp>\u003Cp>In this program, once accepted, we have identified a huge number of tutorials that we think would be hugely impact for our users, and we invite you to write them and get paid doing it. We will support you through that whole process reviewing content and making sure what you create, is the best it can possibly be. So if you are an experienced technical writer and you know how to use directors, this program is for you. It's about educating others about how to be successful and make the most of directors. We are going to open applications a few times a year.\u003C\u002Fp>\u003Cp>This first open application starts today, and it will be open through the end of November, to kick off shortly after. So we really look forward to seeing your applications and the amazing tutorials that you can write to help others.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Hopefully, I am back. I'm super excited about those announcements. I did put in the chat the links for applications to become a guest author and also the Derick's Labs extensions, URL as well, so do check those out if you haven't already seen them. I'm super interested in hearing about what you are excited about from those announcements or if you're using any extensions.\u003C\u002Fp>\u003Cp>We've got a couple\u003C\u002Fp>\u003Cp>Speaker 1: of minutes in the chat to, have a talk with you about them So if you have seen any that you're like, this is gonna be particularly useful to me, we'd love some instant feedback on what is getting you excited, what you might be using them for. I know and I think I said this last month to you about the Gantt chart, but anything project management, super exciting. Like, just to be able to see a timeline, like a crisp timeline is super exciting for me. Also, the funnel, we love to see it. So, yeah, if you do have any thoughts, keep them coming in the chat whilst we are talking through them.\u003C\u002Fp>\u003Cp>Speaker 0: And if not, we have the Discord throughout the month, if you've got any questions about anything at all. I'm gonna\u003C\u002Fp>\u003Cp>Speaker 1: give it a couple more seconds. If you are typing, type quickly, before I move us on.\u003C\u002Fp>\u003Cp>Speaker 0: But, yeah, just, as we said in there, lots of feedback coming in through, which is super helpful in working out the directions as well. So that is all good, and we're excited to keep things coming and keep telling you about them in the change log. Alright. I am going to Ben says, I'd really like some more documentation on using real time stuff in Next. Js.\u003C\u002Fp>\u003Cp>Carmen, I believe you are in the chat\u003C\u002Fp>\u003Cp>Speaker 1: as well, so that, hopefully, you are able to give some insight on what is upcoming. Josh says I\u003C\u002Fp>\u003Cp>Speaker 0: found your software, and I'm using cool. Excellent. Very excited to see it. So I'm gonna move us on to the next segment, which is slightly cringey. So you have been warned.\u003C\u002Fp>\u003Cp>We had a lot of fun putting this together, and it's time for a completely new segment in\u003C\u002Fp>\u003Cp>Speaker 1: the change log. We do try and keep things, new and fresh, so we haven't done this before. This is a segment called Automate My Life, and we really hope you enjoy it.\u003C\u002Fp>\u003Cp>Speaker 0: Hello, and welcome to Automate My Life. In this show, we help regular people using Directus.\u003C\u002Fp>\u003Cp>Speaker 3: And today, we are in sunny London with our 1st person in need, Sean. He has lots of problems. We can't help with most of them, but we certainly can help with 1. Over to Sean. So I'm here with Sean who has called in the Automate My Life team with a problem.\u003C\u002Fp>\u003Cp>Sean, what can we help you with? Hi, Keir. Thanks for coming. I have a problem. Tell me more.\u003C\u002Fp>\u003Cp>Look at this plant. What do you notice about it? Sean, this plant is dead. That is the problem. Okay.\u003C\u002Fp>\u003Cp>Yep. See, I have plants and they are not dead. But then no matter what I do, they become dead. Despite your best effort. Despite my best and sometimes your mediocre efforts as well.\u003C\u002Fp>\u003Cp>Okay. I understand. Well, I think this is something we might be able to help with over in the workshop. Marvelous. Automate my life, director's team.\u003C\u002Fp>\u003Cp>Over to Beth.\u003C\u002Fp>\u003Cp>Speaker 0: Okay, folks. You've heard what Sean's problem is. We've brought you here as the best of the best to work out a solution for Sean. The future of his plants relies on you. What have you got?\u003C\u002Fp>\u003Cp>Speaker 4: Hey, Sean. Welcome to the Directus workshopping team. We're all about automating our lives every day. I have a similar problem. My wife my partner, has a pepper garden.\u003C\u002Fp>\u003Cp>She's an avid gardener. She loves to garden. This year, she actually picked up a little pH temperature monitor or soil tester, to give you the soil quality and quantity and make sure that she could amend the soil properly for those kinds of things. But as I'm thinking about this now, I'd actually like to work with her, and we'll get a I I think next year, I'm actually going to do this particular project. But in the meantime, we'll talk about some of the ways we could solve this problem.\u003C\u002Fp>\u003Cp>There are both DIY options and aftermarket options. I think I'll opt for the DIY, and that will be using something along the lines of an e s ESP 32 Wi Fi Sensor. There's a nice Directus blog that kind of goes through a use case for this, but we're gonna talk a little bit more in detail about some specific sensors and capabilities that we'll need to do for this. So there's an ESP 32. This has built in Wi Fi.\u003C\u002Fp>\u003Cp>The ability to integrate that with your sensors and plug that into your sensors, so we can collect data, soil temp moisture contents, and, temperatures. So the key thing is to making sure that your plants grow successfully. Now, in order to make this all work together to get you alerts and notifications, I'm gonna hand you off to my main automation guru, mister Bryant Gillespie.\u003C\u002Fp>\u003Cp>Speaker 2: Sorry, Jonathan. I, had these thinking glasses on to consider this problem. I just want to say this is an amazing use case for Directus Flows. We are going to be able to send automated notifications whenever these values are out of bounds. So here's what I'm thinking.\u003C\u002Fp>\u003Cp>Whenever a new item is created in the logging collection we trigger a flow. That flow does some magic and it will run some conditional operations to determine whether those values for moisture, for pH, for temperature are within the bounds that we set. If they are within those bounds, nothing happens. You're a okay Sean. As soon as those values step out of bounds like they're on the court, boom, you get a message via SMS through the Twilio API.\u003C\u002Fp>\u003Cp>And that's a wrap. Now to kick it over to my main man, AvDV, our head of insights.\u003C\u002Fp>\u003Cp>Speaker 5: Hey, Sean. If you're anything like me and you've got a lot of plants, I can imagine all these sensors are gonna be bringing in a ton of data into your into your director's project. So what better way to use insights or our dashboards to build up, targeted, insights which give you a summary of all the information in one page. We have, time series charts that you can start to track the pH, the moisture and the temperature over time. And with built in filters as well, you can you can filter by room, by particular plant, or even by the season if you want to track, the growth or the moisture over the seasons.\u003C\u002Fp>\u003Cp>There's a ton more use cases that we could build in, but those are just good. Some 3 quick summaries of what you could build with the Director's insights.\u003C\u002Fp>\u003Cp>Speaker 0: Wow. This all sounds great. I'm gonna leave you in the workshop to make this real. So Sean, we built you a soil monitor. There's sensors in here that every hour monitor the status of the planet.\u003C\u002Fp>\u003Cp>And if it's in need, we'll let you know via text messages exactly what's wrong.\u003C\u002Fp>\u003Cp>Speaker 3: Wow. That's amazing. I'll never kill again. Thanks, directors. Thank you so much.\u003C\u002Fp>\u003Cp>What a wonderful first episode of Automate My Life. Thank you for taking part. If you have an idea for a future episode, you can probably keep it to yourself. This is probably a one and done. Thank you, and bye for now.\u003C\u002Fp>\u003Cp>And that's something we can help you with. I certainly hope so.\u003C\u002Fp>\u003Cp>Speaker 6: I think we can handle discus team.\u003C\u002Fp>\u003Cp>Speaker 3: What's the new company again? Director. Director.\u003C\u002Fp>\u003Cp>Speaker 1: Wow. That was quite something, I hope you enjoyed. And I'm also very glad that the bloopers reel did in fact stay in because it was a lot of fun to record. There were some great ones. So, next up is sorry.\u003C\u002Fp>\u003Cp>I've gotta compose myself. It was so cringey. I could still feel it, like, in my cheeks. So next up, we have what I always say is my favorite part\u003C\u002Fp>\u003Cp>Speaker 0: of the change log, and that is the community showcase. This month, we have Josh talking through the note taking system he built, so I'm gonna send you over to him.\u003C\u002Fp>\u003Cp>Speaker 6: Hi there. I'm Joshua Behmendurfer, a software developer and IT manager at a Christian nonprofit. I have a tendency to build odd solutions like this one to solve mildly frustrating problems. Today, I'm showing off an offline first portable note taking system that integrates with Directus. It uses the Tiletec's awesome hackberry pi 0 for the hardware.\u003C\u002Fp>\u003Cp>This little doohickey with a physical keyboard, And Directus to store and retrieve data. Here's how it works. 1st, in Directus, I'm going to create a new collection called to do with a manual primary key which we'll set to the current date from the note taking system. Then we'll add a single simple markdown field that we'll use for our task list. And you can do more complicated stuff.\u003C\u002Fp>\u003Cp>I'm just going with the markdown field. Then I'm going to add a, assign to field that references a contact so that I can show how, relationships work with the note taking system. And then we should be good to go. Alright. Now let's take a look at how to create and edit notes on the device.\u003C\u002Fp>\u003Cp>I'm gonna use my Android phone here because it's pretty hard to take a recording of the Hackberry without my fingers getting in the way. I've got my editor open with a new text file. This file contains commands that interface with the Directus API. Let's skip the current date so that we can refer to it when creating today's to do and then get the ID of my contact entry so that the to do could be assigned to me. I'm using the Directus search and field parameters to find just my contact ID.\u003C\u002Fp>\u003Cp>Alright. Now we've got that ID. Let's make a view to do for today. We could use the, dollar sign to refer to the output of previous commands. Like, you were setting the ID to the current date.\u003C\u002Fp>\u003Cp>And then I'm going to assign that contact to my that, to do to myself. We're on it. Now let's add some tasks for some things I need to do today. Well, let's see. We could take out the trash.\u003C\u002Fp>\u003Cp>I should probably do that. We should do the laundry. That definitely needs to get done. What else? I should probably record this video.\u003C\u002Fp>\u003Cp>Alright. And then when I hit control r, these changes sync to Directus. And then they're also saved locally in this file for offline editing. I can make changes and they will get synced to my Directus instance. So let's take a look at what has been saved.\u003C\u002Fp>\u003Cp>As you can see, the tasks, assignment, and date are all here. I built this system because I forget things pretty easily and I'm always remembering them at the wrong time. So, to try and capture those fleeting memories, I tried to take notes on the go. Especially at Berk when I'm needing to walk between different people's offices and just catch up with all sorts of things people are saying while I'm, away from my desk. So, I started out with all sorts of notebooks and even designed several variations of reusable note cards, but I could never find the right note card at the right time and digitizing them constantly to make them searchable turned out to be quite a chore.\u003C\u002Fp>\u003Cp>To make the switch to digital notes, I started with Obsidian. It worked pretty well, but I never had the flexibility in data structures and the querying capabilities that I wanted. The text editing experience and syncing proved finicky at times as well. It was close to what I wanted, but not quite right. I solved the structured data organization problem using Directus.\u003C\u002Fp>\u003Cp>The Data Studio makes it a breeze to set up all the collections and fields I need to organize my data the way I want to and it backs it with the full power of a SQLite database. Anything it doesn't support out of the box, I'm a software developer, I can build on to it. But, I had two main issues with the vanilla Directus setup. Number 1, it doesn't work offline and I was always forgetting to hit the save button in my notes And trying to switch between other apps on my phone and direct us while in the middle of a bunch of other things just put too much friction in the note taking process. So, when I saw the Hackberry Pi come up for sale, it seemed like the perfect crazy gadget for taking notes.\u003C\u002Fp>\u003Cp>My, fat thumbs make typing on a touchscreen a pain, so a device with a physical keyboard was perfect. And, it's still small enough to fit in my shirt pocket. I snagged one once they were in stock and while waiting for it to ship from Germany I threw together some simple software to communicate with Directus. This system uses the micro text editor, which is a convenient, easy to use command line text editor that also happens to be extensible with Lua. And I combine it with an indentation based syntax for encoding JSON as the small scripts do things up to direct us.\u003C\u002Fp>\u003Cp>It's not pretty and it does have all sorts of interesting ways of crashing but it works very well for my use case. When I need to take notes, I press the hotkey on the Hackberry, the note I want to edit comes up, then I just type and sync to Directus once I'm back online. I doubt this exact system will be of much use for anyone else but it just goes to show the creative things you can do with Directus to Scratch, personal or work related itches. There's all sorts of wonderful ways you can wire things up. Thank you for your time.\u003C\u002Fp>\u003Cp>Goodbye.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Thank you so much to you, Joshua, for taking the time to, record the video and share\u003C\u002Fp>\u003Cp>Speaker 1: with us as we always love to see what people are doing with directors. I see\u003C\u002Fp>\u003Cp>Speaker 0: that a couple of people have got a video blur, we are on it and we're trying to fix it and see what\u003C\u002Fp>\u003Cp>Speaker 1: we can do. Hopefully it comes back to normal. I don't think it's been that way the whole time so fingers crossed it's just\u003C\u002Fp>\u003Cp>Speaker 0: a bit of a rough patch and it will be fine again going forward. I'm gonna send you over to Carmen who has the first video of Sharp Focus and I'm gonna allow her to talk through exactly what that series is and the rest of the series is also out now on director's TV. So I'll find the link for you in the chat as well. Alright. Over to Carmen.\u003C\u002Fp>\u003Cp>Speaker 7: Hello, everybody, and welcome to Sharp Focus, the show where we shine a lens on how Directus helps you transform your images and truly make them shine. My name is Carmen, and I will be your guide over the next 5 episodes where we'll learn how images can be transformed, how they work under the hood, how they can be manipulated, how they can be resized to be more performant, how you can use advanced transformations, and how you can use presets to take all of the above and apply it in one fell swoop. Now as it turns out, Directus offers a bevy of options for working with images out of the box powered by the sharp API from Node JS. And we're going to be taking a look at how to do that in a way that's going to be super fun and super approachable. So won't you join me in grabbing our cameras and let's dive right into finding out how Directus works with images.\u003C\u002Fp>\u003Cp>So here we are in my very own Directus project running locally dedicated to my photography. Right now, we are in the file library module where you can see that I have a folder over here on the left sidebar. This folder contains photos from my latest trip to my home country Chile. Now, I'm going to use Directus to work with these images for different applications, thumbnails, that sort of thing. But, as you can see, a lot of these images are of varying sizes and qualities.\u003C\u002Fp>\u003Cp>But that's okay. We can use to transform these images to create some consistency and even add some cool effects, which we'll be doing in this series. But first, let's take a little look at what makes these images tick. Now I've already uploaded these to Directus, so this gives me a couple of options to work with them. If I click on one of the images, for example, the photo of my trip to the national park, Torres del Paine, we will see that we can do things like change the title, add a description, some tags, location, if we wish, some focal points, more on that later, and also have access to its asset ID.\u003C\u002Fp>\u003Cp>But that's not all. We can even edit that image. Now we're not going to be doing that in this series, except for using it to create focal points. But again, more on that later. Now, if I want to access that image from say my browser, I can take that asset ID and navigate to my instances URL\u002Fassets\u002Fthe ID of that image, which is fantastic.\u003C\u002Fp>\u003Cp>But one thing to bear in mind, I'm logged into Directus as an admin. But if I wanted to access this image from an application, I would need to assign that application the correct permissions, which we can do here in Directus. When I navigate to the settings module and then go over to access policies, we can see that I already have some permissions and roles set for administrators, which is my logged in user. But I wanna make these images fully public. So what I can do is click here on public and under permissions, click on add collection and select the Directus Files system collection.\u003C\u002Fp>\u003Cp>I will be able to assign different permissions for folks accessing that from anywhere. But I only want to give folks the ability to read. So we'll click here on read access and then assign all access. The last thing to do is to save that permission. Fantastic.\u003C\u002Fp>\u003Cp>Now I can access this image from a browser, from an application, from anywhere. So let's go back and look at that image. Now you remember that these images are of different sizes and qualities. So in order to create some consistencies, we're going to be applying different image transformations, but also giving them a little bit of pizzazz with some advanced transformations. It's gonna be super cool.\u003C\u002Fp>\u003Cp>Before we wrap up this video, I'd like to show you a couple of the types of transformations we can do. In the next video, we'll be looking at image manipulation where we can use query parameters like width, like height, and fit to manipulate our image and in this case make it a consistent 300 by 300 pixels. But that's not all. In our 3rd episode, we'll be looking at how we can manipulate the size and performance of our images. For example, here we're applying a quality of 5% and a format of web P to be used in our applications.\u003C\u002Fp>\u003Cp>Next. Now bear with me. We're going to be doing some advanced transformations. Now this looks a little wild, but you'll see that we're applying some powerful transforms with that sharp API. So we're applying a blur, a tint, we're negating colors.\u003C\u002Fp>\u003Cp>All of this is done with direct us, which is super cool. And finally, in our last episode, we'll talk about preset transformation where we can assign presets to a key, and that way we don't have to type out all of those transformations, but rather place them in one consistent place in Directus. Finally, let's talk about that processing power. You might think it'd be pretty cumbersome if every time I reload this image, it reprocesses all of those transformations. And I have good news.\u003C\u002Fp>\u003Cp>That transformation is done once and then saved in Directus, and you don't have to apply that every time. That means that you can reload that image worry free of any extra processing power done. So there you have it. Now we've seen how direct us works with images out of the box and how we can apply access permissions, which is gonna be super handy because in the next 4 episodes, we're gonna be working with a new Nuxt application to work with these images and display them in a variety of ways using transformations. So what's next?\u003C\u002Fp>\u003Cp>In episode 2, we'll be looking at how to resize our images using width, height, and other parameters, and I can't wait to show you all how it works. So till next time, keep those cameras ready.\u003C\u002Fp>\u003Cp>Speaker 0: We've put together a reading list from suggestions within the director's team of resources they found interesting, educational, or entertaining this month. Firstly, we have how we built a powerful JSON data type for ClickHouse. They talk about some of the challenges they face, such as true column oriented storage, dynamically changing data without type unification, and prevention of avalanche of column data files on disk. Next, a conflict free replicated data type, also known as a CRDT, is a data structure that lets multiple users or applications make changes to the same piece of data. This blog post from BlockSuite, called Building Document Centric CRDT Native Editors, explores the evolution of collaborative document editors, including the transitioning from the traditional editor centric approach to the document centric approach.\u003C\u002Fp>\u003Cp>Next, not a blog post, but an interesting project, pdlight. Run a full Postgres database, locally in WASM, with reactivity and live sync As opposed to running it in the application, you can run this in the browser, dependency free And unlike previous Postgres in the browser projects, pdlight does not use a Linux virtual machine. It is simply Postgres in WASM. Finally, there are some roles that rely on you being able to work with a lot of people from different teams where sometimes that's not always possible time wise and they become a blocker. This post from Kirk Campbell addresses that challenge by looking at how to utilise AI, specifically Claude Projects, in absence of people being able to give you their time and how to bridge that gap.\u003C\u002Fp>\u003Cp>Those are 4 really solid recommendations from the team that we hope you enjoy. We want to take a moment towards the end of the changelog to thank our amazing community contributors who give their time and expertise to improving the director's project. Since last month, there have been 3 releases and 8 contributors. A massive thank you to Gerard for adding support for batch editing in many to many and one to many table interfaces. Thank you to Jun Hong for fixing a display error when selecting Kanban layout for the first time, adding condition to only show the create dashboard if the user has the correct permission, fixing tag interfaces to correctly handle reset white space option, ensuring new operation can reuse same key as previously deleted one in current flow editing session, fixing an issue where the WYSIWYG interface would reload with every keystroke if custom formats are given, and fixing query time logging, leaking memory for failed queries.\u003C\u002Fp>\u003Cp>Thank you to Ayush for ensuring Elements login page are correctly truncated when not enough space is available. Thank you to Chris for adding save options to role and policy item pages. Thank you to Shay for fixing an issue where the mail sender is not displayed correctly for instances that use special characters in project name. Thank you to Shane for ensuring failing GraphQL requests are properly handled by throwing an error and exposing errors and data information. Thank you to Osman for fixing disabled state of the add file empty state button for users without permissions.\u003C\u002Fp>\u003Cp>Thank you again to all our contributors, and you can see their specific pull requests inside of the full release notes on GitHub. Lastly, we also want to take the time to thank the GitHub Sponsors of October for financially contributing to Directus' development. A huge thank you to Clement, that's DLXCLM, for sponsoring our backer tier of $100 a month. A huge thank you also to Entle, d Mathams, Fergus, Omar, Marcus, c k, Tommy, Perissa, Utomic, Steven, Kadir, nonlinear, Andreas, Valentino, Anthony, John, Wayne, Adam, Jason, Birka, Jens, Marcel, Vincent, Mike, Weifan, Lassie, Pedro, and Gemma Liddin. Thank you so much once again.\u003C\u002Fp>\u003Cp>The money we are given from our GitHub Sponsors go straight back to community members who build tooling and extensions for the director's ecosystem. Thank you again for being part of that. Alright. That is the end of this month's changelog. If you've made it this far, thank you so much for staying with us and watching till the end.\u003C\u002Fp>\u003Cp>We really appreciate it. All of these recordings will be available on director's TV as well as all 5 episodes of sharp focus. If you are wanting to see anything new from, us as part of the change log, this, would be a great time to tell us. We'll be sticking around in the chat for a couple more minutes. If you've got any questions that do come up throughout the month, Discord general, Discord help are the places to go.\u003C\u002Fp>\u003Cp>We will be back with December's version of the change log on December 10th at 3 PM, GMT or wherever that is your time. Discord handily tells you in your current, time zone, which is great. I've put all of the reading list recommendation URLs in the chat. If anyone's got any questions, as I said, we will be hanging around for a couple more minutes. But other than that, thank you so much once again.\u003C\u002Fp>\u003Cp>I hope you have a great week, a great month, and hopefully see you next time for December's change log. Have a great day, everyone.\u003C\u002Fp>","All right. Hello. Hello everyone. Hopefully you can all hear me and if you can I would love to know where you are joining us from today? 1, to say that you can hear me and, 2, because I'm nosy and I wanna know where everyone is. Last time everyone was all over and it was really great to hear. Personally, I am joining from London where we had the first snow of the fall this morning. It was quite rainy snow like it wasn't magical magical but it counted and that's what matters so that is where I am from. Excellent, Carmen, thank you so much for telling me you can hear me, I appreciate it. Vienna, Austria, we've got Sweden, USA, Finland, amazing, welcome. I hope you're all having such an amazing day. Devon, Germany, very cool, Netherlands as well. We have some very fun and also educational, material coming up for you for the rest of this show. If you've got any questions throughout, we've got the chat open, and there's a couple of people from our team in it as well. So please do let us know if you have any questions. But we are starting off, the November change log with a product update from Bryant. So I'm gonna send you over to him. Thanks, Beth. Bryant here for Directus, and here's a quick rundown of the core changes that have happened since the last changelog. Version 11.2.0 brought TUS resumable file upload protocol support to the Supabase, Azure, Cloudinary, and GCS storage adapters. And we already had tough support for the AWS s three compatible and local asset storage adapters. Which basically means, you are now fully covered when uploading large files, like videos to Directus. In version 11 dot 1 dot 2, we introduced a migration for Directus comments. There's now a dedicated directus underscore comments collection, which will make it much much easier for you to use comments in your front end projects via the API. We previously stored comments in the directus underscore activity collection, which admittedly was a bit messy because of all the other activity logs we stored alongside those comments. So this welcome change brings new API endpoints for comments, but the existing endpoints are still 100% totally functional. One note though, primary keys are now UUIDs instead of integers. So that could impact any custom type checking implementations you set up. The Directus SDK's internal comment endpoints have also been updated to reflect this change. And I know you're all for avoiding errors, so just ensure your Directus version is compatible with the latest SDK when you're using those comment functions. And there's just one more change with version 11.1.2. We've made some improvements to content versioning. Internally, we stored every change to a content version separately in the directus_revisions collection. And then we merge those together when promoting a version. In this release, we've added a new delta field to directus_versions collection, and that combines all the revisions into a single field. So all that to say, this means you'll be able to prune your revisions, which are the individual changes to a piece of content, without losing your versions. And with that, back over to you, Beth. I'm going to demonstrate each of the new extensions in Director's Labs this month. As a reminder, Director's Labs is our team's experimental organization on GitHub. Typically, these extensions aren't maintained the same way that the main directors project is, but we have now committed to maintaining 4 of them. They are the spreadsheet layout, the Gantt chart layout, the Command Palette module, and the calculated fields bundle. You can see all these extensions, including which extensions are maintained, over on the extensions repo. Onto the demos. Directors AI is a collection of extensions that allows you to leverage the best AI tools from within Directors Automate. Today, we have 2 announcements, a brand new extension and an improvement. Let's start with a brand new extension, the AI web scraper. This allows you to scrape web pages and receive structured data back. We're using FireCrawl to perform the scraping, so you'll need an API key from them. You put a web page or a link to a PDF. Here, we're using directors. Io. If we go into the actions before scraping, here you can get the scraper to perform actions on the web page and add additional metadata about the action. For example, which element to click on or how far to scroll. For the data to extract, here's where you tell it what data you wanted to extract, and it will use AI to extract it. In the property description, we're going to put permission, string as data type, and we're going to enable making it a requirement. Optionally, there are additional formats, so you can also specify how you want the pages content to be presented back to you. They've also just had their launch week and we've implemented their brand new features such as the country, language, and mobile options. The other announcement is an improvement to the AI writer. The biggest change here is that before you could only use OpenAI's models and now we're extending the option to use multiple AI providers such as Anthropic, as well as different AI models. Other than that, everything else works as it did before, and this expansion was done thanks to an issue that was opened on the Directors Labs Extensions repo. So please do keep bringing your feedback for these. That's what's new for Directors AI We often hear from people that they love Directors insights, but want more ways to visualise their data. So we've added 4 new panels, which you can install now via the marketplace. 1st, we have the tree map chart. Select a collection, optionally a filter, which field you want to use for the labels and the value, and any aggregation you would like to perform. For example, here we are summing the value of all deals per company, so each company has its own cell at the size of all values related to that company. Next, we have funnel chart. Now, the funnel chart works exactly the same way, but visualises data differently. So depending on your needs might be more appropriate for you. For example, here we have a typical sales funnel. Funnel charts excel when people, or entities, follow a linear path and you want to see how many convert between stages. Next is the scatterplot. If you've never seen one before, this is a very typical two axis chart where values are plotted as dots. You can optionally add axis labels and hover over any of the dots to see the values. Finally, we have the timeline chart. This is a sibling to the Gantt chart layout that we recently released last month. We have less space to work with in a panel, so it's a simplified version, but it can be really powerful to include for project management. This relies on having 2 date fields, a start and an end, and then a label for the axes on the left and some display text for the item itself. We hope you enjoy our new panels. We certainly think that people are going to get a lot of use out of them. Extension authors often want to extend the functionality of our built in interfaces, And a few months ago, we released the WYSIWYG and block editor as boilerplates to build on top of. Today, we are releasing our first layout boilerplate, the table layout. This was already the basis for our spreadsheet layout extension, and now you can build on top of it and extend it too. You can find this in our Director's Labs extension repo, And we hope that this supercharges your extension building. Hello there. Today, we are announcing our brand new and improved guest author program. The directors community is filled with people who use directors in so many different ways, so many different languages, frameworks, use cases, and this program is all about bringing those experiences to the tutorial section in our new documentation to help new directors users and existing directors users be as successful as possible. So we invite you to apply to be part of our brand new guest author program. Previously, our guest author program worked where you submitted your own ideas. In this program, once accepted, we have identified a huge number of tutorials that we think would be hugely impact for our users, and we invite you to write them and get paid doing it. We will support you through that whole process reviewing content and making sure what you create, is the best it can possibly be. So if you are an experienced technical writer and you know how to use directors, this program is for you. It's about educating others about how to be successful and make the most of directors. We are going to open applications a few times a year. This first open application starts today, and it will be open through the end of November, to kick off shortly after. So we really look forward to seeing your applications and the amazing tutorials that you can write to help others. Alright. Hopefully, I am back. I'm super excited about those announcements. I did put in the chat the links for applications to become a guest author and also the Derick's Labs extensions, URL as well, so do check those out if you haven't already seen them. I'm super interested in hearing about what you are excited about from those announcements or if you're using any extensions. We've got a couple of minutes in the chat to, have a talk with you about them So if you have seen any that you're like, this is gonna be particularly useful to me, we'd love some instant feedback on what is getting you excited, what you might be using them for. I know and I think I said this last month to you about the Gantt chart, but anything project management, super exciting. Like, just to be able to see a timeline, like a crisp timeline is super exciting for me. Also, the funnel, we love to see it. So, yeah, if you do have any thoughts, keep them coming in the chat whilst we are talking through them. And if not, we have the Discord throughout the month, if you've got any questions about anything at all. I'm gonna give it a couple more seconds. If you are typing, type quickly, before I move us on. But, yeah, just, as we said in there, lots of feedback coming in through, which is super helpful in working out the directions as well. So that is all good, and we're excited to keep things coming and keep telling you about them in the change log. Alright. I am going to Ben says, I'd really like some more documentation on using real time stuff in Next. Js. Carmen, I believe you are in the chat as well, so that, hopefully, you are able to give some insight on what is upcoming. Josh says I found your software, and I'm using cool. Excellent. Very excited to see it. So I'm gonna move us on to the next segment, which is slightly cringey. So you have been warned. We had a lot of fun putting this together, and it's time for a completely new segment in the change log. We do try and keep things, new and fresh, so we haven't done this before. This is a segment called Automate My Life, and we really hope you enjoy it. Hello, and welcome to Automate My Life. In this show, we help regular people using Directus. And today, we are in sunny London with our 1st person in need, Sean. He has lots of problems. We can't help with most of them, but we certainly can help with 1. Over to Sean. So I'm here with Sean who has called in the Automate My Life team with a problem. Sean, what can we help you with? Hi, Keir. Thanks for coming. I have a problem. Tell me more. Look at this plant. What do you notice about it? Sean, this plant is dead. That is the problem. Okay. Yep. See, I have plants and they are not dead. But then no matter what I do, they become dead. Despite your best effort. Despite my best and sometimes your mediocre efforts as well. Okay. I understand. Well, I think this is something we might be able to help with over in the workshop. Marvelous. Automate my life, director's team. Over to Beth. Okay, folks. You've heard what Sean's problem is. We've brought you here as the best of the best to work out a solution for Sean. The future of his plants relies on you. What have you got? Hey, Sean. Welcome to the Directus workshopping team. We're all about automating our lives every day. I have a similar problem. My wife my partner, has a pepper garden. She's an avid gardener. She loves to garden. This year, she actually picked up a little pH temperature monitor or soil tester, to give you the soil quality and quantity and make sure that she could amend the soil properly for those kinds of things. But as I'm thinking about this now, I'd actually like to work with her, and we'll get a I I think next year, I'm actually going to do this particular project. But in the meantime, we'll talk about some of the ways we could solve this problem. There are both DIY options and aftermarket options. I think I'll opt for the DIY, and that will be using something along the lines of an e s ESP 32 Wi Fi Sensor. There's a nice Directus blog that kind of goes through a use case for this, but we're gonna talk a little bit more in detail about some specific sensors and capabilities that we'll need to do for this. So there's an ESP 32. This has built in Wi Fi. The ability to integrate that with your sensors and plug that into your sensors, so we can collect data, soil temp moisture contents, and, temperatures. So the key thing is to making sure that your plants grow successfully. Now, in order to make this all work together to get you alerts and notifications, I'm gonna hand you off to my main automation guru, mister Bryant Gillespie. Sorry, Jonathan. I, had these thinking glasses on to consider this problem. I just want to say this is an amazing use case for Directus Flows. We are going to be able to send automated notifications whenever these values are out of bounds. So here's what I'm thinking. Whenever a new item is created in the logging collection we trigger a flow. That flow does some magic and it will run some conditional operations to determine whether those values for moisture, for pH, for temperature are within the bounds that we set. If they are within those bounds, nothing happens. You're a okay Sean. As soon as those values step out of bounds like they're on the court, boom, you get a message via SMS through the Twilio API. And that's a wrap. Now to kick it over to my main man, AvDV, our head of insights. Hey, Sean. If you're anything like me and you've got a lot of plants, I can imagine all these sensors are gonna be bringing in a ton of data into your into your director's project. So what better way to use insights or our dashboards to build up, targeted, insights which give you a summary of all the information in one page. We have, time series charts that you can start to track the pH, the moisture and the temperature over time. And with built in filters as well, you can you can filter by room, by particular plant, or even by the season if you want to track, the growth or the moisture over the seasons. There's a ton more use cases that we could build in, but those are just good. Some 3 quick summaries of what you could build with the Director's insights. Wow. This all sounds great. I'm gonna leave you in the workshop to make this real. So Sean, we built you a soil monitor. There's sensors in here that every hour monitor the status of the planet. And if it's in need, we'll let you know via text messages exactly what's wrong. Wow. That's amazing. I'll never kill again. Thanks, directors. Thank you so much. What a wonderful first episode of Automate My Life. Thank you for taking part. If you have an idea for a future episode, you can probably keep it to yourself. This is probably a one and done. Thank you, and bye for now. And that's something we can help you with. I certainly hope so. I think we can handle discus team. What's the new company again? Director. Director. Wow. That was quite something, I hope you enjoyed. And I'm also very glad that the bloopers reel did in fact stay in because it was a lot of fun to record. There were some great ones. So, next up is sorry. I've gotta compose myself. It was so cringey. I could still feel it, like, in my cheeks. So next up, we have what I always say is my favorite part of the change log, and that is the community showcase. This month, we have Josh talking through the note taking system he built, so I'm gonna send you over to him. Hi there. I'm Joshua Behmendurfer, a software developer and IT manager at a Christian nonprofit. I have a tendency to build odd solutions like this one to solve mildly frustrating problems. Today, I'm showing off an offline first portable note taking system that integrates with Directus. It uses the Tiletec's awesome hackberry pi 0 for the hardware. This little doohickey with a physical keyboard, And Directus to store and retrieve data. Here's how it works. 1st, in Directus, I'm going to create a new collection called to do with a manual primary key which we'll set to the current date from the note taking system. Then we'll add a single simple markdown field that we'll use for our task list. And you can do more complicated stuff. I'm just going with the markdown field. Then I'm going to add a, assign to field that references a contact so that I can show how, relationships work with the note taking system. And then we should be good to go. Alright. Now let's take a look at how to create and edit notes on the device. I'm gonna use my Android phone here because it's pretty hard to take a recording of the Hackberry without my fingers getting in the way. I've got my editor open with a new text file. This file contains commands that interface with the Directus API. Let's skip the current date so that we can refer to it when creating today's to do and then get the ID of my contact entry so that the to do could be assigned to me. I'm using the Directus search and field parameters to find just my contact ID. Alright. Now we've got that ID. Let's make a view to do for today. We could use the, dollar sign to refer to the output of previous commands. Like, you were setting the ID to the current date. And then I'm going to assign that contact to my that, to do to myself. We're on it. Now let's add some tasks for some things I need to do today. Well, let's see. We could take out the trash. I should probably do that. We should do the laundry. That definitely needs to get done. What else? I should probably record this video. Alright. And then when I hit control r, these changes sync to Directus. And then they're also saved locally in this file for offline editing. I can make changes and they will get synced to my Directus instance. So let's take a look at what has been saved. As you can see, the tasks, assignment, and date are all here. I built this system because I forget things pretty easily and I'm always remembering them at the wrong time. So, to try and capture those fleeting memories, I tried to take notes on the go. Especially at Berk when I'm needing to walk between different people's offices and just catch up with all sorts of things people are saying while I'm, away from my desk. So, I started out with all sorts of notebooks and even designed several variations of reusable note cards, but I could never find the right note card at the right time and digitizing them constantly to make them searchable turned out to be quite a chore. To make the switch to digital notes, I started with Obsidian. It worked pretty well, but I never had the flexibility in data structures and the querying capabilities that I wanted. The text editing experience and syncing proved finicky at times as well. It was close to what I wanted, but not quite right. I solved the structured data organization problem using Directus. The Data Studio makes it a breeze to set up all the collections and fields I need to organize my data the way I want to and it backs it with the full power of a SQLite database. Anything it doesn't support out of the box, I'm a software developer, I can build on to it. But, I had two main issues with the vanilla Directus setup. Number 1, it doesn't work offline and I was always forgetting to hit the save button in my notes And trying to switch between other apps on my phone and direct us while in the middle of a bunch of other things just put too much friction in the note taking process. So, when I saw the Hackberry Pi come up for sale, it seemed like the perfect crazy gadget for taking notes. My, fat thumbs make typing on a touchscreen a pain, so a device with a physical keyboard was perfect. And, it's still small enough to fit in my shirt pocket. I snagged one once they were in stock and while waiting for it to ship from Germany I threw together some simple software to communicate with Directus. This system uses the micro text editor, which is a convenient, easy to use command line text editor that also happens to be extensible with Lua. And I combine it with an indentation based syntax for encoding JSON as the small scripts do things up to direct us. It's not pretty and it does have all sorts of interesting ways of crashing but it works very well for my use case. When I need to take notes, I press the hotkey on the Hackberry, the note I want to edit comes up, then I just type and sync to Directus once I'm back online. I doubt this exact system will be of much use for anyone else but it just goes to show the creative things you can do with Directus to Scratch, personal or work related itches. There's all sorts of wonderful ways you can wire things up. Thank you for your time. Goodbye. Alright. Thank you so much to you, Joshua, for taking the time to, record the video and share with us as we always love to see what people are doing with directors. I see that a couple of people have got a video blur, we are on it and we're trying to fix it and see what we can do. Hopefully it comes back to normal. I don't think it's been that way the whole time so fingers crossed it's just a bit of a rough patch and it will be fine again going forward. I'm gonna send you over to Carmen who has the first video of Sharp Focus and I'm gonna allow her to talk through exactly what that series is and the rest of the series is also out now on director's TV. So I'll find the link for you in the chat as well. Alright. Over to Carmen. Hello, everybody, and welcome to Sharp Focus, the show where we shine a lens on how Directus helps you transform your images and truly make them shine. My name is Carmen, and I will be your guide over the next 5 episodes where we'll learn how images can be transformed, how they work under the hood, how they can be manipulated, how they can be resized to be more performant, how you can use advanced transformations, and how you can use presets to take all of the above and apply it in one fell swoop. Now as it turns out, Directus offers a bevy of options for working with images out of the box powered by the sharp API from Node JS. And we're going to be taking a look at how to do that in a way that's going to be super fun and super approachable. So won't you join me in grabbing our cameras and let's dive right into finding out how Directus works with images. So here we are in my very own Directus project running locally dedicated to my photography. Right now, we are in the file library module where you can see that I have a folder over here on the left sidebar. This folder contains photos from my latest trip to my home country Chile. Now, I'm going to use Directus to work with these images for different applications, thumbnails, that sort of thing. But, as you can see, a lot of these images are of varying sizes and qualities. But that's okay. We can use to transform these images to create some consistency and even add some cool effects, which we'll be doing in this series. But first, let's take a little look at what makes these images tick. Now I've already uploaded these to Directus, so this gives me a couple of options to work with them. If I click on one of the images, for example, the photo of my trip to the national park, Torres del Paine, we will see that we can do things like change the title, add a description, some tags, location, if we wish, some focal points, more on that later, and also have access to its asset ID. But that's not all. We can even edit that image. Now we're not going to be doing that in this series, except for using it to create focal points. But again, more on that later. Now, if I want to access that image from say my browser, I can take that asset ID and navigate to my instances URL\u002Fassets\u002Fthe ID of that image, which is fantastic. But one thing to bear in mind, I'm logged into Directus as an admin. But if I wanted to access this image from an application, I would need to assign that application the correct permissions, which we can do here in Directus. When I navigate to the settings module and then go over to access policies, we can see that I already have some permissions and roles set for administrators, which is my logged in user. But I wanna make these images fully public. So what I can do is click here on public and under permissions, click on add collection and select the Directus Files system collection. I will be able to assign different permissions for folks accessing that from anywhere. But I only want to give folks the ability to read. So we'll click here on read access and then assign all access. The last thing to do is to save that permission. Fantastic. Now I can access this image from a browser, from an application, from anywhere. So let's go back and look at that image. Now you remember that these images are of different sizes and qualities. So in order to create some consistencies, we're going to be applying different image transformations, but also giving them a little bit of pizzazz with some advanced transformations. It's gonna be super cool. Before we wrap up this video, I'd like to show you a couple of the types of transformations we can do. In the next video, we'll be looking at image manipulation where we can use query parameters like width, like height, and fit to manipulate our image and in this case make it a consistent 300 by 300 pixels. But that's not all. In our 3rd episode, we'll be looking at how we can manipulate the size and performance of our images. For example, here we're applying a quality of 5% and a format of web P to be used in our applications. Next. Now bear with me. We're going to be doing some advanced transformations. Now this looks a little wild, but you'll see that we're applying some powerful transforms with that sharp API. So we're applying a blur, a tint, we're negating colors. All of this is done with direct us, which is super cool. And finally, in our last episode, we'll talk about preset transformation where we can assign presets to a key, and that way we don't have to type out all of those transformations, but rather place them in one consistent place in Directus. Finally, let's talk about that processing power. You might think it'd be pretty cumbersome if every time I reload this image, it reprocesses all of those transformations. And I have good news. That transformation is done once and then saved in Directus, and you don't have to apply that every time. That means that you can reload that image worry free of any extra processing power done. So there you have it. Now we've seen how direct us works with images out of the box and how we can apply access permissions, which is gonna be super handy because in the next 4 episodes, we're gonna be working with a new Nuxt application to work with these images and display them in a variety of ways using transformations. So what's next? In episode 2, we'll be looking at how to resize our images using width, height, and other parameters, and I can't wait to show you all how it works. So till next time, keep those cameras ready. We've put together a reading list from suggestions within the director's team of resources they found interesting, educational, or entertaining this month. Firstly, we have how we built a powerful JSON data type for ClickHouse. They talk about some of the challenges they face, such as true column oriented storage, dynamically changing data without type unification, and prevention of avalanche of column data files on disk. Next, a conflict free replicated data type, also known as a CRDT, is a data structure that lets multiple users or applications make changes to the same piece of data. This blog post from BlockSuite, called Building Document Centric CRDT Native Editors, explores the evolution of collaborative document editors, including the transitioning from the traditional editor centric approach to the document centric approach. Next, not a blog post, but an interesting project, pdlight. Run a full Postgres database, locally in WASM, with reactivity and live sync As opposed to running it in the application, you can run this in the browser, dependency free And unlike previous Postgres in the browser projects, pdlight does not use a Linux virtual machine. It is simply Postgres in WASM. Finally, there are some roles that rely on you being able to work with a lot of people from different teams where sometimes that's not always possible time wise and they become a blocker. This post from Kirk Campbell addresses that challenge by looking at how to utilise AI, specifically Claude Projects, in absence of people being able to give you their time and how to bridge that gap. Those are 4 really solid recommendations from the team that we hope you enjoy. We want to take a moment towards the end of the changelog to thank our amazing community contributors who give their time and expertise to improving the director's project. Since last month, there have been 3 releases and 8 contributors. A massive thank you to Gerard for adding support for batch editing in many to many and one to many table interfaces. Thank you to Jun Hong for fixing a display error when selecting Kanban layout for the first time, adding condition to only show the create dashboard if the user has the correct permission, fixing tag interfaces to correctly handle reset white space option, ensuring new operation can reuse same key as previously deleted one in current flow editing session, fixing an issue where the WYSIWYG interface would reload with every keystroke if custom formats are given, and fixing query time logging, leaking memory for failed queries. Thank you to Ayush for ensuring Elements login page are correctly truncated when not enough space is available. Thank you to Chris for adding save options to role and policy item pages. Thank you to Shay for fixing an issue where the mail sender is not displayed correctly for instances that use special characters in project name. Thank you to Shane for ensuring failing GraphQL requests are properly handled by throwing an error and exposing errors and data information. Thank you to Osman for fixing disabled state of the add file empty state button for users without permissions. Thank you again to all our contributors, and you can see their specific pull requests inside of the full release notes on GitHub. Lastly, we also want to take the time to thank the GitHub Sponsors of October for financially contributing to Directus' development. A huge thank you to Clement, that's DLXCLM, for sponsoring our backer tier of $100 a month. A huge thank you also to Entle, d Mathams, Fergus, Omar, Marcus, c k, Tommy, Perissa, Utomic, Steven, Kadir, nonlinear, Andreas, Valentino, Anthony, John, Wayne, Adam, Jason, Birka, Jens, Marcel, Vincent, Mike, Weifan, Lassie, Pedro, and Gemma Liddin. Thank you so much once again. The money we are given from our GitHub Sponsors go straight back to community members who build tooling and extensions for the director's ecosystem. Thank you again for being part of that. Alright. That is the end of this month's changelog. If you've made it this far, thank you so much for staying with us and watching till the end. We really appreciate it. All of these recordings will be available on director's TV as well as all 5 episodes of sharp focus. If you are wanting to see anything new from, us as part of the change log, this, would be a great time to tell us. We'll be sticking around in the chat for a couple more minutes. If you've got any questions that do come up throughout the month, Discord general, Discord help are the places to go. We will be back with December's version of the change log on December 10th at 3 PM, GMT or wherever that is your time. Discord handily tells you in your current, time zone, which is great. I've put all of the reading list recommendation URLs in the chat. If anyone's got any questions, as I said, we will be hanging around for a couple more minutes. But other than that, thank you so much once again. I hope you have a great week, a great month, and hopefully see you next time for December's change log. Have a great day, everyone.","61f4c782-d881-4b7c-8ac6-3214b0ee3cdd",[1721,1722,1723,1724],"3ff4cb13-ce14-41b6-b91f-9ae28a68a5c6","27249462-20d7-46cc-8035-4d85b310b61f","8fba3cc4-9a25-4280-aa0d-3ff816d0abd9","182a93e5-058a-478b-bf9f-99c6e5978950",[],{"year":978,"number":1536,"id":1537,"episodes":1727,"show":1728},[1539,1540,1541,1542,1519],{"title":297,"slug":298},{"id":1730,"slug":1731,"vimeo_id":1732,"description":1733,"tile":1734,"length":889,"resources":12,"people":12,"episode_number":13,"published":1735,"title":1733,"video_transcript_html":1736,"video_transcript_text":1737,"content":12,"seo":1738,"status":19,"episode_people":1739,"recommendations":1741,"season":1742},"979db4da-a870-4120-94ee-bd80789f411c","firecrawl","1026203173","Integrating Firecrawl with Directus","d0a87153-8475-433f-aca0-dea9802caf03","2024-10-16","\u003Cp>Speaker 0: Hello there. I'm really excited about this tutorial. So on Directus TV, we already have a show called Quick Connect, which shows you how to integrate third party services with Directus using Directus Automate and Flows. And in the spirit of that show, today, I'm gonna show you how to integrate FireCrawl with Directus. Now here they say that they turn websites into LLM ready data.\u003C\u002Fp>\u003Cp>And what that means in practice is you can feed it a URL, provide some options if you want, and it will go and take a look at that web page and return some structured data for you like so. This is their scrape endpoint, which will take a single web page and scrape some data from it. They also have a couple of other endpoints, crawl and map, but today, we're gonna use scrape. Now I've already logged into FireCrawl Cloud and generated an API key, which I'll copy for later. You can also self host FireCrawl, but for ease, I'm just gonna use their cloud product here.\u003C\u002Fp>\u003Cp>Now I have this directors project over here with a new empty collection called companies. In this collection, there are a few fields. URL, a name, a description, mission, and a boolean, a true force value, is it open source. And our goal will be to provide the URL and then have FireCrawl automatically populate the rest with flows. So let's go ahead and create a new flow.\u003C\u002Fp>\u003Cp>So this is our automation builder if you've not seen it before. I'll call this one get company data, I guess. And we are going to use a manual trigger, which will add a button to the side of collection and item pages. So we're going to say we'll run this on the company's collection. What else matters here?\u003C\u002Fp>\u003Cp>We're going to, not require selection, so the button always works. And we're going to require confirmation, which will pop up a modal. And in that modal, we will just add a URL. We'll make it a string input, and we'll make it full widths. And I think that's all we need to do here.\u003C\u002Fp>\u003Cp>So just to see what happens here, if I go back to the, to the company's collection, we now see this button here, this manual flow, trigger. I click that. It pulls up the box, and we'll put in a URL and hit run flow. So now if we go back to our flow, we should immediately see that there is one log. And in here, there is a body, and the URL is the value that we typed.\u003C\u002Fp>\u003Cp>Fantastic. Now we need to actually do something with it. So, let's go ahead and add a new operation here. And, honestly, fire crawl is pretty sick. You can just make one web request.\u003C\u002Fp>\u003Cp>Let's take a look at their docs. We're gonna use the LLM extract, endpoint here, and let's just take a look at the kind of construction of this API call. It's a post request to this URL. We're gonna pass in our, our API key here as an authorization header, and then they give us this kind of JSON payload here. Here, it's telling it to go ahead and extract specifically these four fields, the company mission, does it support SSO, is it open source, and is it in Y Combinator?\u003C\u002Fp>\u003Cp>And it's saying you must go get all four of these. So let's actually just turn this straight into a flow request, request URL, operation. So we're gonna do a post request to this URL, post request to this URL. I'm gonna go and copy my API key again here, and at the end of this I'll, I'll destroy the key. Authorization authorization, bearer API key, save, And then there's the request body.\u003C\u002Fp>\u003Cp>And, honestly, it contains a little more than we need, but this contains everything we need. So we'll just pop that in there. The only thing we wanna do, of course, is pass in the URL that we put in the box. So we'll replace this with trigger.body. URL.\u003C\u002Fp>\u003Cp>Fantastic. Let's save that and see what happens if we go over to content, press the button, and type in directors.io. We see that's running. That's running. That's a good sign.\u003C\u002Fp>\u003Cp>It means it's going off and making the request, waiting for the request. And then we see there is a second log. And we get some data back. There was a 200, so it was successful. Inside of data, there is a property called data, and then there is this value called extract.\u003C\u002Fp>\u003Cp>Extract contains all of those custom keys we asked for, company mission, supports SSO, is open source, and is in YC. And then always when you scrape, you get this metadata object, title, description, language, Open Graph data, source URL, and so on. So, really, all we wanna do here now is we wanna take this data and create a new company from it. So let's add a new, let's create a new operation on the resolve path of that web request. Let's call it create data.\u003C\u002Fp>\u003Cp>We're gonna create something in the company's collection. We'll give it full access, and then we just need to provide a payload. So let's go ahead and do that. We have an object here. We have a name.\u003C\u002Fp>\u003Cp>Oh, we have a name, and we're gonna pass in the value of the last operation dot data.data.metadata dot title. Then I, for one, am I'm just gonna copy this and edit it each time. So we have name, URL. So that's last .data.data.metadata. Source URL.\u003C\u002Fp>\u003Cp>We could, of course, just take it from the trigger body URL, but this is properly formatted. You'll notice I typed in directus dot I o, but when it came back in the payload, it came back with the with the, with the protocol HTTPS and so on. So we have name. We have URL. We have a description.\u003C\u002Fp>\u003Cp>Now this one is also from the metadata description. We have the mission. Now this was a custom piece of data we asked to be extracted. Company mission is what we called it. And finally, we have open, open underscore source.\u003C\u002Fp>\u003Cp>Last data, data extract and then is underscore open underscore source and then remove that trailing comma. So I believe that's the name of all of the fields. We'll figure it out in a moment when when it inevitably doesn't work. We'll hit the button again, directors.io, and hit run flow. Once again, that's going off to fire crawl using their endpoint and there we see there we see it straight here.\u003C\u002Fp>\u003Cp>URL name, description, mission, and the boolean is open source. Let's, let's try that once more. Let's go in here and say firefirecrawl.devrun flow. So let's see. And in theory, we should just give that a moment, and there it is.\u003C\u002Fp>\u003Cp>So now you can go ahead and grab more data. Now, of course, if we take a look at this endpoint here, you can provide custom properties, and it will try its best to get data out from that. They have a couple of other interesting things which I'll draw your attention to even if I don't think it works in this context. They have extracting without a schema. So this extract here was us creating a a schema.\u003C\u002Fp>\u003Cp>Right? You can give it just a text. You can give it a prompt. Extract the company mission from the page. But the thing I don't like about that is you're not explicitly saying what the name of the key is, so you don't necessarily know what it's gonna be at the end.\u003C\u002Fp>\u003Cp>I like creating a schema personally. They do something else that's kinda interesting. If I take a look at where is I think it's in their API API reference here inside of scrape. They have this interesting thing called actions. So you can get it to wait, to take a screenshot, to click, write text, press a key, and scroll.\u003C\u002Fp>\u003Cp>And the combination of clicking and writing text means you can get it to interact with your web page. You see it here, actions, wait two milliseconds. You could get it to, like, sign into things perhaps, perform search terms. I think it's super interesting. And then take screenshots, of course, and upload those to directors if you fancy.\u003C\u002Fp>\u003Cp>So there's a lot of flexibility in this. Having seen kinda how easy this API is, I think I'll go ahead and turn this into, an extension some point in the next few weeks, which we can release as part of Directus AI. But, yeah, that's how to integrate FireCrawl with Directus using Directus Automate. Hope you found this interesting, and by all means, if you have questions, just reach out.\u003C\u002Fp>","Hello there. I'm really excited about this tutorial. So on Directus TV, we already have a show called Quick Connect, which shows you how to integrate third party services with Directus using Directus Automate and Flows. And in the spirit of that show, today, I'm gonna show you how to integrate FireCrawl with Directus. Now here they say that they turn websites into LLM ready data. And what that means in practice is you can feed it a URL, provide some options if you want, and it will go and take a look at that web page and return some structured data for you like so. This is their scrape endpoint, which will take a single web page and scrape some data from it. They also have a couple of other endpoints, crawl and map, but today, we're gonna use scrape. Now I've already logged into FireCrawl Cloud and generated an API key, which I'll copy for later. You can also self host FireCrawl, but for ease, I'm just gonna use their cloud product here. Now I have this directors project over here with a new empty collection called companies. In this collection, there are a few fields. URL, a name, a description, mission, and a boolean, a true force value, is it open source. And our goal will be to provide the URL and then have FireCrawl automatically populate the rest with flows. So let's go ahead and create a new flow. So this is our automation builder if you've not seen it before. I'll call this one get company data, I guess. And we are going to use a manual trigger, which will add a button to the side of collection and item pages. So we're going to say we'll run this on the company's collection. What else matters here? We're going to, not require selection, so the button always works. And we're going to require confirmation, which will pop up a modal. And in that modal, we will just add a URL. We'll make it a string input, and we'll make it full widths. And I think that's all we need to do here. So just to see what happens here, if I go back to the, to the company's collection, we now see this button here, this manual flow, trigger. I click that. It pulls up the box, and we'll put in a URL and hit run flow. So now if we go back to our flow, we should immediately see that there is one log. And in here, there is a body, and the URL is the value that we typed. Fantastic. Now we need to actually do something with it. So, let's go ahead and add a new operation here. And, honestly, fire crawl is pretty sick. You can just make one web request. Let's take a look at their docs. We're gonna use the LLM extract, endpoint here, and let's just take a look at the kind of construction of this API call. It's a post request to this URL. We're gonna pass in our, our API key here as an authorization header, and then they give us this kind of JSON payload here. Here, it's telling it to go ahead and extract specifically these four fields, the company mission, does it support SSO, is it open source, and is it in Y Combinator? And it's saying you must go get all four of these. So let's actually just turn this straight into a flow request, request URL, operation. So we're gonna do a post request to this URL, post request to this URL. I'm gonna go and copy my API key again here, and at the end of this I'll, I'll destroy the key. Authorization authorization, bearer API key, save, And then there's the request body. And, honestly, it contains a little more than we need, but this contains everything we need. So we'll just pop that in there. The only thing we wanna do, of course, is pass in the URL that we put in the box. So we'll replace this with trigger.body. URL. Fantastic. Let's save that and see what happens if we go over to content, press the button, and type in directors.io. We see that's running. That's running. That's a good sign. It means it's going off and making the request, waiting for the request. And then we see there is a second log. And we get some data back. There was a 200, so it was successful. Inside of data, there is a property called data, and then there is this value called extract. Extract contains all of those custom keys we asked for, company mission, supports SSO, is open source, and is in YC. And then always when you scrape, you get this metadata object, title, description, language, Open Graph data, source URL, and so on. So, really, all we wanna do here now is we wanna take this data and create a new company from it. So let's add a new, let's create a new operation on the resolve path of that web request. Let's call it create data. We're gonna create something in the company's collection. We'll give it full access, and then we just need to provide a payload. So let's go ahead and do that. We have an object here. We have a name. Oh, we have a name, and we're gonna pass in the value of the last operation dot data.data.metadata dot title. Then I, for one, am I'm just gonna copy this and edit it each time. So we have name, URL. So that's last .data.data.metadata. Source URL. We could, of course, just take it from the trigger body URL, but this is properly formatted. You'll notice I typed in directus dot I o, but when it came back in the payload, it came back with the with the, with the protocol HTTPS and so on. So we have name. We have URL. We have a description. Now this one is also from the metadata description. We have the mission. Now this was a custom piece of data we asked to be extracted. Company mission is what we called it. And finally, we have open, open underscore source. Last data, data extract and then is underscore open underscore source and then remove that trailing comma. So I believe that's the name of all of the fields. We'll figure it out in a moment when when it inevitably doesn't work. We'll hit the button again, directors.io, and hit run flow. Once again, that's going off to fire crawl using their endpoint and there we see there we see it straight here. URL name, description, mission, and the boolean is open source. Let's, let's try that once more. Let's go in here and say firefirecrawl.devrun flow. So let's see. And in theory, we should just give that a moment, and there it is. So now you can go ahead and grab more data. Now, of course, if we take a look at this endpoint here, you can provide custom properties, and it will try its best to get data out from that. They have a couple of other interesting things which I'll draw your attention to even if I don't think it works in this context. They have extracting without a schema. So this extract here was us creating a a schema. Right? You can give it just a text. You can give it a prompt. Extract the company mission from the page. But the thing I don't like about that is you're not explicitly saying what the name of the key is, so you don't necessarily know what it's gonna be at the end. I like creating a schema personally. They do something else that's kinda interesting. If I take a look at where is I think it's in their API API reference here inside of scrape. They have this interesting thing called actions. So you can get it to wait, to take a screenshot, to click, write text, press a key, and scroll. And the combination of clicking and writing text means you can get it to interact with your web page. You see it here, actions, wait two milliseconds. You could get it to, like, sign into things perhaps, perform search terms. I think it's super interesting. And then take screenshots, of course, and upload those to directors if you fancy. So there's a lot of flexibility in this. Having seen kinda how easy this API is, I think I'll go ahead and turn this into, an extension some point in the next few weeks, which we can release as part of Directus AI. But, yeah, that's how to integrate FireCrawl with Directus using Directus Automate. Hope you found this interesting, and by all means, if you have questions, just reach out.","9c575c49-4fb1-4eed-9731-e1e1acb55da3",[1740],"4830ae03-22cd-4399-82f2-5767bf5cf0cc",[],{"year":978,"number":830,"id":1743,"episodes":1744,"show":1745},"cf7a056d-fa10-4bc5-8cc3-c2b9ef59b684",[1730],{"title":1746,"slug":1747},"Quick Connect","quick-connect",{"id":1541,"slug":1749,"vimeo_id":1750,"description":1751,"tile":1752,"length":12,"resources":12,"people":12,"episode_number":264,"published":1735,"title":1753,"video_transcript_html":1754,"video_transcript_text":1755,"content":1756,"seo":1757,"status":19,"episode_people":1758,"recommendations":1763,"season":1764},"3-october-2024","1019714535","Join us for The Changelog, taking you through the month’s Directus updates including product updates, new content and community contribution highlights. This month's show includes a community showcase of a form builder from David, Carmen taking you through a docs preview and Kevin with a tutorial on AI web scraping API with flows.\n\n","f3b9ac10-251f-4708-ae07-51a29d1868b6","October 2024","\u003Cp>Speaker 0: Alright. Hello, everyone. Hopefully, you can all hear me, and I am gonna take a pause and just check that that is the case. So if someone could let me know, that would be great. Oh, I was excited.\u003C\u002Fp>\n\u003Cp>Carmen's saying hello, and I'm still gonna wait. Loud and clear. Perfect. Thank you so much, Kevin. Welcome to October's version of the change log from Directus.\u003C\u002Fp>\n\u003Cp>If you're new here or if you've, joined us before, welcome. I hope you're having the most perfect day. I hope your day is not too hot, not too cold. It's like the crispest, best version of the day because that is what you deserve for joining us. Thank you for for being here.\u003C\u002Fp>\n\u003Cp>We have a great show for you planned. We've got some updates from Brian and myself from product and extensions, we've got Carmen with a docs update for what is upcoming and that's super exciting, we have Kevin with a quick connect tutorial. We've got the community showcase, and we've got a whole bunch of different things. I am joining from London. I would love to know, where you are all joining from.\u003C\u002Fp>\n\u003Cp>I can see that the chat is already busy. That is great. Please continue. We have team members in there answering all your questions. So, yeah, if you've got anything that you are thinking about, let us know based on anything we are saying.\u003C\u002Fp>\n\u003Cp>We also have the general chat in Discord for afterwards if it comes to you and you're like oh I should have said that feel free to keep the conversation moving there as well. Did anyone say where they're joining from so far? I see a lot of hellos. Oh, Netherlands, North Germany. Hello.\u003C\u002Fp>\n\u003Cp>Welcome. Welcome. Granada, Spain, Ukraine, Austria, Egypt, Sardinia, Nigeria. Amazing. Welcome, France too.\u003C\u002Fp>\n\u003Cp>Hello. Hello. Super happy for you all to be joining us. I am gonna hand over to you, Bryant, with some product updates for the first start of the show. So hope you enjoy them.\u003C\u002Fp>\n\u003Cp>Speaker 1: Brian here for Directus, and I've got 2 quick updates for you. Version 11.1.1 dropped last week with a load of bug fixes, optimizations, and a few dependency updates. I just wanted to give a quick shout out to our engineering team and the community members who are on fire at improving the Directus experience for you, for me, and the entire Directus community. There is however one breaking change. The underlying SendGrid package we were using is no longer maintained.\u003C\u002Fp>\n\u003Cp>So rather than risk your security or some type of crazy exploit, we've removed it. Now if you're using SendGrid as your direct to email provider, you'll want to switch up your config to use SMTP instead of their API. For the full list of fixes and updates, check the release notes in the Directus repository. Now on to my favorite update, probably because I had a little hand in shipping these. There's no bias here.\u003C\u002Fp>\n\u003Cp>I'm super excited to announce Directus cloud templates. These are handcrafted Directus instances with data models, permissions, flows, all the niceties already built out for you. Now, whenever you spin up a new project for your organization or your client inside Directus Cloud, you can get to 80% done by clicking one button. There are currently 3 templates to choose from. Simple CMS, which is great for your next website build.\u003C\u002Fp>\n\u003Cp>It's configured with the dynamic page builder, blog posts, menus, and even dynamic forms. Your content editors are going to love this one. Next up, we have simple CRM, which is the perfect starting point for your own custom Salesforce or HubSpot. It includes the standard fare, like organizations and contacts, deals and activities, and a configurable pipeline. Last but not least, we have simple ecommerce, which showcases a small online apparel or t shirt shop build out.\u003C\u002Fp>\n\u003Cp>The data model includes products, orders, customers, and it even handles more complex scenarios like product variance, which account for all the color and size combinations automatically. So hop on to Directus Cloud, spin up an instance, check them out, and as always, send us your feedback because we'll be releasing more of these templates in the coming months. And back over to you, Beth.\u003C\u002Fp>\n\u003Cp>Speaker 0: I'm going to demonstrate each of the new extensions in Directus Labs this month. As a reminder Directus Labs is our team's experimental org in GitHub which regularly publishes new extensions we think are cool. The Gantt chart layout displays items in a collection in a Gantt chart helping those of you who use Directus for project management and task management. When configuring the layout specify a label that you want displayed on each task, a start date and an end date, and optionally a dependency field which will draw dependency lines in a chart. You can specify the zoom in as granular as an hour and as broad as a year.\u003C\u002Fp>\n\u003Cp>You can download it now in the marketplace. A calculated fields interface is the most upvoted open feature request in the directors project and today we are releasing it as an extension. It allows you to write a formula and the value of the interface will be automatically computed and shown. It supports the full set of functions provided by formula. Js and the majority of JavaScript operators that work for numbers and strings.\u003C\u002Fp>\n\u003Cp>It also supports relational fields and we pass formulas to ensure they are only running allowed functions which is important for security. A couple of things that are important to note values here are only visible in the interface and not in API responses. We've had this extension in the works for a while, but just a couple of days ago, a community member, Judah Sullivan, released the Directus AutoGen Extension which you might also want to check out as it's similar Both are available in the marketplace The API metric panel can be used to display a value from an external API, for example, that could be the number of docker downloads or sales or followers on social media platforms As you can see here we are making a web request to get docker metrics. We could also provide custom headers or a request body if required. Then you specify the path of the value you want to display.\u003C\u002Fp>\n\u003Cp>It's a nice responsive panel that allows you to get an overview of your business. You can download this extension directly from GitHub or NPM. Lastly, one of our core team members, Tim, in his spare time released a pair of flow operations which allows authentication and permission checking. The first operation allows you to check whether the flow was triggered by a user with admin access, user access, or public access. The operation will return true if authenticated or else throw an error.\u003C\u002Fp>\n\u003Cp>The second operation allows you to select a collection and it will return true if a user has read permissions in that collection. You can check this out over on github and npm we hope you enjoy using all these extensions Gantt chart layout, calculated fields interface, the API metric panel and tims authentication and permission checks let's bring my camera back up and I'll see how you're doing. I brought my camera back up hopefully you can see me again. Yeah, as I said we are super excited for everything we've just announced. Out of the extensions we've just talked about or if there are any other extensions that you regularly use in the marketplace, now is a good time to let us know in the chat.\u003C\u002Fp>\n\u003Cp>I mean for me as someone who really really likes project management efficiency the Gantt chart layout is super exciting and Kevin has just put in the chat as well the extension link for you to check them out for yourself. Tim, I did see you. Brace look also same person, same super ability. We are very excited about the operations as well. So, yeah, if you have any, extensions that you think you're like, this has really changed the game for me, this has changed the way I use direct us, or if you're just like, this is cool and I'm excited to hear more about it, let us know in the chat, and we can have a chat about this right now.\u003C\u002Fp>\n\u003Cp>Kevin is saying that Florian's flexible editor interface is the one. Yes. I agree. That one is great too. And, yeah, as we said, shout out to you, Judah.\u003C\u002Fp>\n\u003Cp>I'm not sure if you are in the chat, but we were talking about this in Discord earlier, too. So do make sure to check out their contribution as well. Yeah. So those are the, four ones for the extensions this month. Every month, we aim to have more.\u003C\u002Fp>\n\u003Cp>And if you are looking at where, to go, the extensions link is the best place. We do also have the extensions channel in Discord too, and the director's extension field, actions from Atomic Media is also a great one. I'm gonna move us along to the next part of the changelog, but feel free to keep the conversation going, via the chats whilst we're there. So I'm handing over to Carmen with a docs preview, which is super exciting. So I will send over to her now.\u003C\u002Fp>\n\u003Cp>Speaker 2: Hey, everyone. My name is Carmen. I'm a developer educator at Directus, and part of my role is documentation and that's what I'm going to be showing you today. We are about to launch a beta of our new documentation and I just wanna show you a little bit about what our motivations are, what we've done, and what's next. And, in order to do that, I'm gonna tell you a little bit about what three aspects of our documentation we're aiming to tackle.\u003C\u002Fp>\n\u003Cp>1 is content, the other is structure, and finally, the platform. So let's talk about the content. Over the years, starting from a very minimal set of documentation that served our needs, not only have we gotten a lot of feedback and requests for new pieces of documentation, but we've also seen lots of our team, lots of hands touch that documentation. And over time there have been different voices, there have been different tones and we just wanted to completely rewrite that. So everything that you will see in the new documentation, whether it's been brought over or adapted or written from scratch, will have been touched not just by me but by other educators.\u003C\u002Fp>\n\u003Cp>Next is structure. And so what we've done is sort of split it into 2 areas. 1 is a reference manual where you're looking at the exact parts of the documentation that you need just so that you can use Directus. But there's also an implementation side of it. You're not just using Directus in isolation.\u003C\u002Fp>\n\u003Cp>So we are also bringing in a set of tutorials so that you can work with directives, for example, with a specific framework, with a specific use case, maybe with a specific SSO provider. What we're doing here is hoping that we can make this sort of separation bring a lot of clarity to our documentation. Finally, there's our platform. When we started out, we were using VitePress which fulfilled so many of our needs. It came with a template that we could use and when we hit those limitations, we would extend them, make those parts our own, and adapt them as needed.\u003C\u002Fp>\n\u003Cp>But, with the opportunity of rewriting our documentation for scratch, we also wanted to have a brand new platform. We are rewriting our documentation using Nuxt. And I just wanna give a little shout out to one of our engineers, Connor, who's been spearheading this effort in a magnificent way. And finally, let's be honest, maintaining an API reference all in markdown can be quite cumbersome when manually written. So what we've set out to do is to have these API references also be automatically generated from code.\u003C\u002Fp>\n\u003Cp>So cool. Those are the 3 parts that we're addressing with our new documentation. But what's next? As I mentioned earlier, later this month, we'll be launching the 1st beta of the documentation. And this is where y'all come in.\u003C\u002Fp>\n\u003Cp>I'd love to ask you to please look over them, give us feedback, and tell us what we can do to improve this documentation for when we launch towards the end of the year. The other thing that we're going to be doing as well is to really look over all of our existing tutorials and audit them and bring them over. That does not mean that our old documentation is going to hide them our way. We're not gonna be hiding that old documentation or our tutorials. We're just going to be systematically bringing them over, making sure that they are correct, relevant, that they work with our current version of Directus and making sure that those tutorials stay in lockstep with new releases and new documentation.\u003C\u002Fp>\n\u003Cp>So that's it for me. Thank you so much for listening and I'll see you later.\u003C\u002Fp>\n\u003Cp>Speaker 0: Alright. As Kevin said in the chat, we are releasing that in the next couple of weeks. He's also put the link where you can find it. Super exciting. And I am also gonna be the 3rd person to say a massive big up to Connor and also a massive thank you to Carmen as well.\u003C\u002Fp>\n\u003Cp>We've now come to the my personal favorite part of the changelog which is the community showcase. Love seeing what everyone is building and how they are making directors their own And this month, we have a community showcase from David who is gonna be talking about the form builder project they're currently working on.\u003C\u002Fp>\n\u003Cp>Speaker 3: Hi. I'm David from. We are small digital studio based in Montreal. We've been working with directors for the last 2 years. And today, I'm going to talk to you about a small form builder extension that we've built for Dericus and that we're going to open source soon, hopefully.\u003C\u002Fp>\n\u003Cp>So let's start. So, the main feature that our clients have been missing since we migrated away from WordPress has been the form builder. Not sure if you're familiar with gravity forms, which is one form, complex form builder that is labeled in their WordPress ecosystem, that allow end users to configure forms, with conditions, with advanced fields, with notification, even integrations with external systems. So all those portions are quite easy to build with Directus apart from the advanced form builder. We were able to build a basic form builder using native features from directives, but they were not as easy for a end user to configure.\u003C\u002Fp>\n\u003Cp>In creating a complex form builders with conditions and wizards like features, is quite a big endeavor. So that's why we chose to, we actually started looking into open source project that could help us. And that's where we came across a library called survey gs. Survey so basically, survey gs has 2 main comp 2 main components. One is called survey creator.\u003C\u002Fp>\n\u003Cp>The survey creator is what the end user use to create the form or survey. It's quite powerful. And the other important portion is form library, or should I say what libraries? Now form libraries, they basically take in the JSON coming from the survey creator to render a form. They have library for Angular, Vue.\u003C\u002Fp>\n\u003Cp>Js, React, vanilla JavaScript, and probably other frameworks that for oh, yeah, and backbone, if they're still user of backbones. But anyway, yeah. So the only when we found that we found that it was basically doing everything we needed from a foreign perspective. So the only thing left was to integrate with Directus. And that's where we created our first extension, actually, our first or should I say first public extension for Directus?\u003C\u002Fp>\n\u003Cp>So let me jump into that. I'll do a quick demo of what we've built. The main features that we wanted to implement were, well, of course, integrating the survey creator, the ability to save forms or the form data in any collection, and finally send notification to either internal users or external users after form was filled. So here, as you can see, we have our small form module. So I'll jump into contact us form.\u003C\u002Fp>\n\u003Cp>I'll start with the form builder portion. Here, we have the basic form, 2 columns, first name, last name, or email. We have a drop down with some choices, subject of the the form. In here, we have our first condition. So, for example, I have a condition here that say if the subject is product information, then we will display what specific product information are you looking for.\u003C\u002Fp>\n\u003Cp>If it shows customer support, then instead, it's going to be what's your issue. I want this I won't demo all the features of, sorry. Js. There's a lot of documentation. You can look it up on their website.\u003C\u002Fp>\n\u003Cp>But, yeah, it's fairly powerful from builder. Then afterwards, we have the actual integration with Directus. So if you I go into the configuration of the form. Let me hide that for now. So, yeah, what we do have is the schema that was generated by the form builder.\u003C\u002Fp>\n\u003Cp>Technically, you could edit the schema itself, but it's easier to use the form builder instead. Please note, though, that the form builder of Suri. Js does require a license. We we do have the configuration to input your license in the extension, so that's good. But just so you're aware, it does require a license from, Suri.\u003C\u002Fp>\n\u003Cp>Js to use a survey creator. Anyway, so what I was saying is here, we have the configuration. So we could we can see that, it's submitting data to form submission collection. You can choose any collection that you want. You can create a new one.\u003C\u002Fp>\n\u003Cp>You can have multiple forms submitting to the same collection. It's really depend on your requirements, basically. We also have notification templates. I'll show I'll explain how notification templates work afterwards. And did I forget anything?\u003C\u002Fp>\n\u003Cp>No. I think that's pretty much it. So, yeah, actually, let's jump in into notification template first. So if somebody submit or fill in that form, as I mentioned, you can configure 1 or more notification templates. So let's jump into it.\u003C\u002Fp>\n\u003Cp>Form templates use like just flows. You can use any fields that was part of the form as as the recipient. For example, if you want them to have more than 1, you can add other recipient. In this case, we're using the field work email. But you could also add an hard coded one for, suppose, someone on your team.\u003C\u002Fp>\n\u003Cp>The template is itself is pure HTML, but same thing. There's basic templating, so you can use, variables from the from the form. In this case, the contact us form. So let's try and submit 13. So I'm gonna go back to my form, contact us form.\u003C\u002Fp>\n\u003Cp>And I didn't build my preview. Here in this case, it's an XGS application. So it's a React component that we built that is basically reading the schema here and passing the schema to the to the sorry. Js library. And once you're done submitting the form, it's going to call a webhook from Dactors to save the data.\u003C\u002Fp>\n\u003Cp>So let's try that. So of course, we do have validation. So let me fill the form. We based in North America. So we want to chat about product information, customer support.\u003C\u002Fp>\n\u003Cp>Yes. I'm not sure if this form is going to actually send email. Yeah. This field is not required. So I don't not gonna fill it.\u003C\u002Fp>\n\u003Cp>I'm just gonna complete the form. Thanks. No problem. So if we go into our contact us entries, we can see my entry, all the fields that were filled filled in with the data that was passed. And if we look at our notifications that were generated, we can see here we do have the new notification, the email that it was sent to, the actual content of the email.\u003C\u002Fp>\n\u003Cp>So you have a and a status, so you know if it it was fully sent or not. It does link back to the entry if needed that generate that that generated that notification. And once the form is submitted, we can see in my email that I received the confirmation template. Yes. So yeah.\u003C\u002Fp>\n\u003Cp>So that's pretty much how the, extension work. If we look at our flows, there's a couple of flows that need to be configured, but it's not that complex. Most of the features are built in feature from directors or survey GS. So most of the extension is gluing together the the 2 great products, basically. We do have some configuration that you can pass to the survey JS extension.\u003C\u002Fp>\n\u003Cp>So, for example, if you want to show the designer tab, the JSON tab, depending on your users, you might not want them to be able to see the JSON. So first of all, you can disable that, save the settings. And if you go back to the actual form, you see I don't have access to the JSON editor here. So we made that we made sure that we could configure every settings from, survey survey creator directly from directors. So, hopefully, it's an extension that, some of you will find useful.\u003C\u002Fp>\n\u003Cp>We're hoping to release the extension in the next month. We just wanna make sure that it come with clear installation instruction. But, yeah, it was the main feature for us that was missing from Directus. Everything else has been great. We're really happy having migrating from WordPress to Directus.\u003C\u002Fp>\n\u003Cp>And with that last missing piece of the puzzle, we have we now have a complete toolkit to, finalize our projects. So thank you very much.\u003C\u002Fp>\n\u003Cp>Speaker 0: Alright. Thank you so much to you, David, for taking, the time to take us through that. I'm super excited for it to become available hopefully soon. I think it will make a great addition and if you're keen for more community showcases I highly recommend checking out the 'I made this channel'. Specifically people post updates on their projects and how they're going along and I think that's really great to see exactly how things are changing all in the same thread which is super interesting.\u003C\u002Fp>\n\u003Cp>Next up, we have Kevin with a quick connect tutorial on AI web scraping API with flows. So I'm gonna hand over to him.\u003C\u002Fp>\n\u003Cp>Speaker 4: Hello there. I'm really excited about this tutorial. So on Directus TV, we already have a show called Quick Connect, which shows you how to integrate third party services with Directus using Directus Automate and Flows. And in the spirit of that show, today, I'm gonna show you how to integrate FireCrawl with Directus. Now here they say that they turn websites into LLM ready data, And what that means in practice is you can feed it a URL, provide some options if you want, and it will go and take a look at that web page and return some structured data for you like so.\u003C\u002Fp>\n\u003Cp>This is their scrape endpoint, which will take a single web page and scrape some data from it. They also have a couple of other endpoints, crawl and map, but today we're gonna use scrape. Now I've already logged into Firecrawl Cloud and generated an API key, which I'll copy for later. You can also self host Firecrawl, but for ease, I'm just gonna use their cloud product here. Now I have this directors project over here with a new empty collection called companies.\u003C\u002Fp>\n\u003Cp>In this collection, there are a few fields, URL, a name, a description, a mission, and a boolean, a true force value, is it open source. And our goal will be to provide the URL and then have FireCrawl automatically populate the rest with flows. So let's go ahead and create a new flow. So this is our automation builder if you've not seen it before. I'll call this one get company data, I guess.\u003C\u002Fp>\n\u003Cp>And we are going to use a manual trigger which will add a button to the side of collection and item pages. So we're going to say we'll run this on the company's collection. What else matters here? We're going to not require selection, so the button always works, and we're going to require confirmation which will pop up a modal, and in that modal we will just add a URL. We'll make it a string input and we'll make it full width, and I think that's all we need to do here.\u003C\u002Fp>\n\u003Cp>So just to see what happens here, if I go back to the, to the company's collection, we now see this button here, this manual flow, trigger. I click that. It pulls up the box, and we'll put in a URL and hit run flow. So, now, if we go back to our flow, we should immediately see that there is one log. And in here, there is a body and the URL is the value that we typed.\u003C\u002Fp>\n\u003Cp>Fantastic. Now we need to actually do something with it. So, let's go ahead and add a new operation here. And, honestly, fire crawl is pretty sick. You can just make one web request.\u003C\u002Fp>\n\u003Cp>Let's take a look at their docs. We're gonna use the llm extract endpoint here, and let's just take a look at the kind of construction of this API call. It's a post request to this URL. We're gonna pass in our our API key here as an authorization header, and then they give us this kind of JSON payload here. Here, it's telling it to go ahead and extract specifically these four fields, the company mission, does it support SSO, is it open source, and is it in Y Combinator?\u003C\u002Fp>\n\u003Cp>And it's saying you must go get all 4 of these. So let's actually just turn this straight into a flow request request URL operation. So we're gonna do a post request to this URL, post request to this URL. I'm gonna go and copy my API key again here, and at the end of this, I'll, I'll destroy the key. Authorization authorization, bearer API key, save, and then there's the request body.\u003C\u002Fp>\n\u003Cp>And, honestly, it contains a little more than we need but this contains everything we need so we'll just pop that in there. The only thing we wanna do, of course, is pass in the URL that we put in the box. So we'll replace this with trigger dot body dot URL. Fantastic. Let's save that and see what happens if we go over to content, press the button, and type in directors dot io.\u003C\u002Fp>\n\u003Cp>We see that's running. That's running. That's a good sign. It means it's going off and making the request, waiting for the request. And then we see there is a second log.\u003C\u002Fp>\n\u003Cp>And we get some data back. There was a 200, so it was successful. Inside of data, there is a property called data, and then there is this value called extract. Extract contains all of those custom keys we asked for, company mission, supports SSO, is open source, and is in YC. And then always when you scrape, you get this metadata object, title, description, language, Open Graph data, source URL, and so on.\u003C\u002Fp>\n\u003Cp>So really all we want to do here now is we want to take this data and create a new company from it. So let's add a new, let's create a new operation on the resolve path of that web request. Let's call it create data. We're gonna create something in the company's collection, we'll give it full access, and then we just need to provide a payload. So let's go ahead and do that.\u003C\u002Fp>\n\u003Cp>We have an object here, we have a name, we have a name, and we're gonna pass in the value of the last operation dotdata.data.metadata.title. Then I, for 1, am I'm just gonna copy this and edit it each time. So we have name, URL. So that's last.data.data.metadata.sourceURL. We could, of course, just take it from the trigger body URL, but this is properly formatted.\u003C\u002Fp>\n\u003Cp>You'll notice I typed in directus.io, but when it came back in the payload, it came back with the with the, with the protocol HTTPS and so on. So we have name. We have URL. We have a description. Now this one is also from the metadata Description.\u003C\u002Fp>\n\u003Cp>We have the mission. Now this was a custom piece of data we asked to be extracted. Company mission is what we called it. And, finally, we have open, open underscore source. Last data, data extract and then is underscore open underscore source and then remove that trailing comma.\u003C\u002Fp>\n\u003Cp>So I believe that's the name of all of the fields. We'll figure it out in a moment when when it inevitably doesn't work. We'll hit the button again, directors dot io and hit run flow. Once again that's going off to Firecrawl using their endpoint and there we see there we see it straight here. URL name, description, mission and the boolean is open source.\u003C\u002Fp>\n\u003Cp>Let's, let's try that once more. Let's go in here and say fire fire crawl dot dev run flow. So let's see. And in theory we should just give that a moment and there it is. So now you can go ahead and grab more data.\u003C\u002Fp>\n\u003Cp>Now, of course, if we take a look at this endpoint here, you can provide custom properties and it will try its best to get data out from that. They have a couple of other interesting things which I'll draw your attention to even if I don't think it works in this context. They have, attention to even if I don't think it works in this context. They have extracting without a schema. So this extract here was us creating a a schema.\u003C\u002Fp>\n\u003Cp>Right. You can give it just a text. You can give it a prompt. Extract the company mission from the page, but the thing I don't like about that is you're not explicitly saying what the name of the key is, so you don't necessarily know what it's going to be at the end. I like creating a schema personally.\u003C\u002Fp>\n\u003Cp>They do something else that's kind of interesting. If I take a look at where is it, I think it's in their API reference here inside of scrape. They have this interesting thing called actions. So you can get it to wait, to take a screenshot, to click, write text, press a key and scroll and the combination of clicking and writing text means you can get it to interact with your web page. You see it here, actions, wait 2 milliseconds.\u003C\u002Fp>\n\u003Cp>You could get it to, like, sign in to things perhaps, perform search terms. I think it's super interesting and then take screenshots, of course, and upload those to directors if you fancy. So there's a lot of flexibility in this. Having seen kind of how easy this API is, I think I'll go ahead and turn this into, an extension some point in the next few weeks which we can release as part of Directus AI. But, yeah, that's how to integrate FireCrawl with Directus using Directus Automate.\u003C\u002Fp>\n\u003Cp>Hope you found this interesting, and by all means, if you have questions just reach out. Alright. Over to Beth.\u003C\u002Fp>\n\u003Cp>Speaker 0: Welcome to The Reading List. Internally, we have a number of Slack channels where we share links from all around that we found educational and entertaining and we'd really like to share some of our favorites this month with you. First up, we have EIAIO games on scaling 1,000,000 checkboxes. This was a web experiment where every time anyone globally checks a box that change is reflected across everyone's browser and it goes in-depth into the scaling challenges of the unexpected popularity of the website leading to 650,000,000 checked boxes. Next, we have a recorded YouTube video from wasm.\u003C\u002Fp>\n\u003Cp>Io24 featuring Luke Wagner giving a talk on a stream of consciousness on the future of async in the component model. This is a great talk from one of the original designers of WebAssembly on how async will work in WASI 0.3. Next, we have introducing Netflix's key value data abstraction layer on the Netflix tech blog. This takes us through some of their global backend infrastructure design powering one of the most popular streaming platforms around and their use of multiple online distributed databases such as Apache Cassandra. Next up, we have an interesting read introducing the tool Cambria which is used to help you describe in a structured way how schemas change and why they change.\u003C\u002Fp>\n\u003Cp>This is from an independent research lab called Inc and Switch, definitely one for folks interested in the more academic side of things. The data visualization catalog provides a list of charts categorized by their data visualization functions or by what you want a chart to communicate to an audience. It's a really useful guide for selecting charts based on your analysis or communication needs. It starts with what you're trying to communicate and recommends which chart or tools to use. Lastly, a nice little tip from Vic instead of storing states as booleans, so like true or false, you could consider a date stamp so you can see when a state was updated with null meaning false and any date value meaning true.\u003C\u002Fp>\n\u003Cp>Those are just a few of the links that we shared internally this month. If you've got your own interesting links we would absolutely love to see them. We would recommend you share them within the discord server so you can see them and you never know they might even be in next month's changelog! We like to take a moment towards the end of each changelog to thank our contributors and sponsors who help make Directus as great as it can be. A huge thank you to Florian for dropping support for the old SendGrid transport option in favor of SMTP directly.\u003C\u002Fp>\n\u003Cp>Thank you to Shane for adding support for disabling terminal screen clearing on rebuild in watch mode. Thank you to Junhong for fixing SVG truncation issue in image interface preview, for fixing an issue that would cause custom role names to not be translated in the role navigation sidebar, for fixing WYSIWYG interface to be updated when interface options are changed via conditions, for improving behavior of the translations interface when setting use users language option, for ensuring data in sidebar is re fetched when switching pages while a sidebar pane is already opened, and for ensuring errors occurred within the card layout are always shown. Thank you to renovate for updating dependencies across multiple packages. Thank you to Joshua for fixing an issue that would cause log streaming to fail on directors installations that use a sub path in the URL. Thank you to for ensuring users are redirected to the intended page after login via LDAP.\u003C\u002Fp>\n\u003Cp>Thank you to CASE for adding the optional ignore rules option to schema apply CLI, and thank you to Anton for making the output types of SDK requests better readable in IDEs. Thank you again to each individual you can see their specific pull requests inside of the full release notes on GitHub. Lastly, we also want to take the time to thank our GitHub Sponsors of September who financially contribute to Directus' development specifically a huge thank you to Clement that's dlxclem for sponsoring as our backer tier of $100 a month. The money we are given from our Github sponsors goes straight back to the community members who build tooling and extensions for the directors ecosystem. Thank you again for being part of that.\u003C\u002Fp>\n\u003Cp>Alright, that is the last of the segments that we have for this month. If you are still watching thank you so much for sticking around we appreciate you And if you've watched the whole thing we'd love to know which segments you particularly like. That helps us plan for the next few months and to show, what you'd like to see so do let us know any feedback about the change log. The next change log is November 12th at 3 pm the same time and I do hope that it's going to stay the same time in discord because we are changing from British summer time to the non British summer time which is Greenwich Mean Time So if you see a different, time, it'll still be 3 PM GMT BST. It's all gonna be there.\u003C\u002Fp>\n\u003Cp>It's fine. Yes, Kevin. Not quite connect. Weird presenter in my opinion. Anyway, thank you so much again for tuning in.\u003C\u002Fp>\n\u003Cp>Have a great rest of your day, and hopefully catch you for the next one. Bye, everyone.\u003C\u002Fp>","Alright. Hello, everyone. Hopefully, you can all hear me, and I am gonna take a pause and just check that that is the case. So if someone could let me know, that would be great. Oh, I was excited. Carmen's saying hello, and I'm still gonna wait. Loud and clear. Perfect. Thank you so much, Kevin. Welcome to October's version of the change log from Directus. If you're new here or if you've, joined us before, welcome. I hope you're having the most perfect day. I hope your day is not too hot, not too cold. It's like the crispest, best version of the day because that is what you deserve for joining us. Thank you for for being here. We have a great show for you planned. We've got some updates from Brian and myself from product and extensions, we've got Carmen with a docs update for what is upcoming and that's super exciting, we have Kevin with a quick connect tutorial. We've got the community showcase, and we've got a whole bunch of different things. I am joining from London. I would love to know, where you are all joining from. I can see that the chat is already busy. That is great. Please continue. We have team members in there answering all your questions. So, yeah, if you've got anything that you are thinking about, let us know based on anything we are saying. We also have the general chat in Discord for afterwards if it comes to you and you're like oh I should have said that feel free to keep the conversation moving there as well. Did anyone say where they're joining from so far? I see a lot of hellos. Oh, Netherlands, North Germany. Hello. Welcome. Welcome. Granada, Spain, Ukraine, Austria, Egypt, Sardinia, Nigeria. Amazing. Welcome, France too. Hello. Hello. Super happy for you all to be joining us. I am gonna hand over to you, Bryant, with some product updates for the first start of the show. So hope you enjoy them. Brian here for Directus, and I've got 2 quick updates for you. Version 11.1.1 dropped last week with a load of bug fixes, optimizations, and a few dependency updates. I just wanted to give a quick shout out to our engineering team and the community members who are on fire at improving the Directus experience for you, for me, and the entire Directus community. There is however one breaking change. The underlying SendGrid package we were using is no longer maintained. So rather than risk your security or some type of crazy exploit, we've removed it. Now if you're using SendGrid as your direct to email provider, you'll want to switch up your config to use SMTP instead of their API. For the full list of fixes and updates, check the release notes in the Directus repository. Now on to my favorite update, probably because I had a little hand in shipping these. There's no bias here. I'm super excited to announce Directus cloud templates. These are handcrafted Directus instances with data models, permissions, flows, all the niceties already built out for you. Now, whenever you spin up a new project for your organization or your client inside Directus Cloud, you can get to 80% done by clicking one button. There are currently 3 templates to choose from. Simple CMS, which is great for your next website build. It's configured with the dynamic page builder, blog posts, menus, and even dynamic forms. Your content editors are going to love this one. Next up, we have simple CRM, which is the perfect starting point for your own custom Salesforce or HubSpot. It includes the standard fare, like organizations and contacts, deals and activities, and a configurable pipeline. Last but not least, we have simple ecommerce, which showcases a small online apparel or t shirt shop build out. The data model includes products, orders, customers, and it even handles more complex scenarios like product variance, which account for all the color and size combinations automatically. So hop on to Directus Cloud, spin up an instance, check them out, and as always, send us your feedback because we'll be releasing more of these templates in the coming months. And back over to you, Beth. I'm going to demonstrate each of the new extensions in Directus Labs this month. As a reminder Directus Labs is our team's experimental org in GitHub which regularly publishes new extensions we think are cool. The Gantt chart layout displays items in a collection in a Gantt chart helping those of you who use Directus for project management and task management. When configuring the layout specify a label that you want displayed on each task, a start date and an end date, and optionally a dependency field which will draw dependency lines in a chart. You can specify the zoom in as granular as an hour and as broad as a year. You can download it now in the marketplace. A calculated fields interface is the most upvoted open feature request in the directors project and today we are releasing it as an extension. It allows you to write a formula and the value of the interface will be automatically computed and shown. It supports the full set of functions provided by formula. Js and the majority of JavaScript operators that work for numbers and strings. It also supports relational fields and we pass formulas to ensure they are only running allowed functions which is important for security. A couple of things that are important to note values here are only visible in the interface and not in API responses. We've had this extension in the works for a while, but just a couple of days ago, a community member, Judah Sullivan, released the Directus AutoGen Extension which you might also want to check out as it's similar Both are available in the marketplace The API metric panel can be used to display a value from an external API, for example, that could be the number of docker downloads or sales or followers on social media platforms As you can see here we are making a web request to get docker metrics. We could also provide custom headers or a request body if required. Then you specify the path of the value you want to display. It's a nice responsive panel that allows you to get an overview of your business. You can download this extension directly from GitHub or NPM. Lastly, one of our core team members, Tim, in his spare time released a pair of flow operations which allows authentication and permission checking. The first operation allows you to check whether the flow was triggered by a user with admin access, user access, or public access. The operation will return true if authenticated or else throw an error. The second operation allows you to select a collection and it will return true if a user has read permissions in that collection. You can check this out over on github and npm we hope you enjoy using all these extensions Gantt chart layout, calculated fields interface, the API metric panel and tims authentication and permission checks let's bring my camera back up and I'll see how you're doing. I brought my camera back up hopefully you can see me again. Yeah, as I said we are super excited for everything we've just announced. Out of the extensions we've just talked about or if there are any other extensions that you regularly use in the marketplace, now is a good time to let us know in the chat. I mean for me as someone who really really likes project management efficiency the Gantt chart layout is super exciting and Kevin has just put in the chat as well the extension link for you to check them out for yourself. Tim, I did see you. Brace look also same person, same super ability. We are very excited about the operations as well. So, yeah, if you have any, extensions that you think you're like, this has really changed the game for me, this has changed the way I use direct us, or if you're just like, this is cool and I'm excited to hear more about it, let us know in the chat, and we can have a chat about this right now. Kevin is saying that Florian's flexible editor interface is the one. Yes. I agree. That one is great too. And, yeah, as we said, shout out to you, Judah. I'm not sure if you are in the chat, but we were talking about this in Discord earlier, too. So do make sure to check out their contribution as well. Yeah. So those are the, four ones for the extensions this month. Every month, we aim to have more. And if you are looking at where, to go, the extensions link is the best place. We do also have the extensions channel in Discord too, and the director's extension field, actions from Atomic Media is also a great one. I'm gonna move us along to the next part of the changelog, but feel free to keep the conversation going, via the chats whilst we're there. So I'm handing over to Carmen with a docs preview, which is super exciting. So I will send over to her now. Hey, everyone. My name is Carmen. I'm a developer educator at Directus, and part of my role is documentation and that's what I'm going to be showing you today. We are about to launch a beta of our new documentation and I just wanna show you a little bit about what our motivations are, what we've done, and what's next. And, in order to do that, I'm gonna tell you a little bit about what three aspects of our documentation we're aiming to tackle. 1 is content, the other is structure, and finally, the platform. So let's talk about the content. Over the years, starting from a very minimal set of documentation that served our needs, not only have we gotten a lot of feedback and requests for new pieces of documentation, but we've also seen lots of our team, lots of hands touch that documentation. And over time there have been different voices, there have been different tones and we just wanted to completely rewrite that. So everything that you will see in the new documentation, whether it's been brought over or adapted or written from scratch, will have been touched not just by me but by other educators. Next is structure. And so what we've done is sort of split it into 2 areas. 1 is a reference manual where you're looking at the exact parts of the documentation that you need just so that you can use Directus. But there's also an implementation side of it. You're not just using Directus in isolation. So we are also bringing in a set of tutorials so that you can work with directives, for example, with a specific framework, with a specific use case, maybe with a specific SSO provider. What we're doing here is hoping that we can make this sort of separation bring a lot of clarity to our documentation. Finally, there's our platform. When we started out, we were using VitePress which fulfilled so many of our needs. It came with a template that we could use and when we hit those limitations, we would extend them, make those parts our own, and adapt them as needed. But, with the opportunity of rewriting our documentation for scratch, we also wanted to have a brand new platform. We are rewriting our documentation using Nuxt. And I just wanna give a little shout out to one of our engineers, Connor, who's been spearheading this effort in a magnificent way. And finally, let's be honest, maintaining an API reference all in markdown can be quite cumbersome when manually written. So what we've set out to do is to have these API references also be automatically generated from code. So cool. Those are the 3 parts that we're addressing with our new documentation. But what's next? As I mentioned earlier, later this month, we'll be launching the 1st beta of the documentation. And this is where y'all come in. I'd love to ask you to please look over them, give us feedback, and tell us what we can do to improve this documentation for when we launch towards the end of the year. The other thing that we're going to be doing as well is to really look over all of our existing tutorials and audit them and bring them over. That does not mean that our old documentation is going to hide them our way. We're not gonna be hiding that old documentation or our tutorials. We're just going to be systematically bringing them over, making sure that they are correct, relevant, that they work with our current version of Directus and making sure that those tutorials stay in lockstep with new releases and new documentation. So that's it for me. Thank you so much for listening and I'll see you later. Alright. As Kevin said in the chat, we are releasing that in the next couple of weeks. He's also put the link where you can find it. Super exciting. And I am also gonna be the 3rd person to say a massive big up to Connor and also a massive thank you to Carmen as well. We've now come to the my personal favorite part of the changelog which is the community showcase. Love seeing what everyone is building and how they are making directors their own And this month, we have a community showcase from David who is gonna be talking about the form builder project they're currently working on. Hi. I'm David from. We are small digital studio based in Montreal. We've been working with directors for the last 2 years. And today, I'm going to talk to you about a small form builder extension that we've built for Dericus and that we're going to open source soon, hopefully. So let's start. So, the main feature that our clients have been missing since we migrated away from WordPress has been the form builder. Not sure if you're familiar with gravity forms, which is one form, complex form builder that is labeled in their WordPress ecosystem, that allow end users to configure forms, with conditions, with advanced fields, with notification, even integrations with external systems. So all those portions are quite easy to build with Directus apart from the advanced form builder. We were able to build a basic form builder using native features from directives, but they were not as easy for a end user to configure. In creating a complex form builders with conditions and wizards like features, is quite a big endeavor. So that's why we chose to, we actually started looking into open source project that could help us. And that's where we came across a library called survey gs. Survey so basically, survey gs has 2 main comp 2 main components. One is called survey creator. The survey creator is what the end user use to create the form or survey. It's quite powerful. And the other important portion is form library, or should I say what libraries? Now form libraries, they basically take in the JSON coming from the survey creator to render a form. They have library for Angular, Vue. Js, React, vanilla JavaScript, and probably other frameworks that for oh, yeah, and backbone, if they're still user of backbones. But anyway, yeah. So the only when we found that we found that it was basically doing everything we needed from a foreign perspective. So the only thing left was to integrate with Directus. And that's where we created our first extension, actually, our first or should I say first public extension for Directus? So let me jump into that. I'll do a quick demo of what we've built. The main features that we wanted to implement were, well, of course, integrating the survey creator, the ability to save forms or the form data in any collection, and finally send notification to either internal users or external users after form was filled. So here, as you can see, we have our small form module. So I'll jump into contact us form. I'll start with the form builder portion. Here, we have the basic form, 2 columns, first name, last name, or email. We have a drop down with some choices, subject of the the form. In here, we have our first condition. So, for example, I have a condition here that say if the subject is product information, then we will display what specific product information are you looking for. If it shows customer support, then instead, it's going to be what's your issue. I want this I won't demo all the features of, sorry. Js. There's a lot of documentation. You can look it up on their website. But, yeah, it's fairly powerful from builder. Then afterwards, we have the actual integration with Directus. So if you I go into the configuration of the form. Let me hide that for now. So, yeah, what we do have is the schema that was generated by the form builder. Technically, you could edit the schema itself, but it's easier to use the form builder instead. Please note, though, that the form builder of Suri. Js does require a license. We we do have the configuration to input your license in the extension, so that's good. But just so you're aware, it does require a license from, Suri. Js to use a survey creator. Anyway, so what I was saying is here, we have the configuration. So we could we can see that, it's submitting data to form submission collection. You can choose any collection that you want. You can create a new one. You can have multiple forms submitting to the same collection. It's really depend on your requirements, basically. We also have notification templates. I'll show I'll explain how notification templates work afterwards. And did I forget anything? No. I think that's pretty much it. So, yeah, actually, let's jump in into notification template first. So if somebody submit or fill in that form, as I mentioned, you can configure 1 or more notification templates. So let's jump into it. Form templates use like just flows. You can use any fields that was part of the form as as the recipient. For example, if you want them to have more than 1, you can add other recipient. In this case, we're using the field work email. But you could also add an hard coded one for, suppose, someone on your team. The template is itself is pure HTML, but same thing. There's basic templating, so you can use, variables from the from the form. In this case, the contact us form. So let's try and submit 13. So I'm gonna go back to my form, contact us form. And I didn't build my preview. Here in this case, it's an XGS application. So it's a React component that we built that is basically reading the schema here and passing the schema to the to the sorry. Js library. And once you're done submitting the form, it's going to call a webhook from Dactors to save the data. So let's try that. So of course, we do have validation. So let me fill the form. We based in North America. So we want to chat about product information, customer support. Yes. I'm not sure if this form is going to actually send email. Yeah. This field is not required. So I don't not gonna fill it. I'm just gonna complete the form. Thanks. No problem. So if we go into our contact us entries, we can see my entry, all the fields that were filled filled in with the data that was passed. And if we look at our notifications that were generated, we can see here we do have the new notification, the email that it was sent to, the actual content of the email. So you have a and a status, so you know if it it was fully sent or not. It does link back to the entry if needed that generate that that generated that notification. And once the form is submitted, we can see in my email that I received the confirmation template. Yes. So yeah. So that's pretty much how the, extension work. If we look at our flows, there's a couple of flows that need to be configured, but it's not that complex. Most of the features are built in feature from directors or survey GS. So most of the extension is gluing together the the 2 great products, basically. We do have some configuration that you can pass to the survey JS extension. So, for example, if you want to show the designer tab, the JSON tab, depending on your users, you might not want them to be able to see the JSON. So first of all, you can disable that, save the settings. And if you go back to the actual form, you see I don't have access to the JSON editor here. So we made that we made sure that we could configure every settings from, survey survey creator directly from directors. So, hopefully, it's an extension that, some of you will find useful. We're hoping to release the extension in the next month. We just wanna make sure that it come with clear installation instruction. But, yeah, it was the main feature for us that was missing from Directus. Everything else has been great. We're really happy having migrating from WordPress to Directus. And with that last missing piece of the puzzle, we have we now have a complete toolkit to, finalize our projects. So thank you very much. Alright. Thank you so much to you, David, for taking, the time to take us through that. I'm super excited for it to become available hopefully soon. I think it will make a great addition and if you're keen for more community showcases I highly recommend checking out the 'I made this channel'. Specifically people post updates on their projects and how they're going along and I think that's really great to see exactly how things are changing all in the same thread which is super interesting. Next up, we have Kevin with a quick connect tutorial on AI web scraping API with flows. So I'm gonna hand over to him. Hello there. I'm really excited about this tutorial. So on Directus TV, we already have a show called Quick Connect, which shows you how to integrate third party services with Directus using Directus Automate and Flows. And in the spirit of that show, today, I'm gonna show you how to integrate FireCrawl with Directus. Now here they say that they turn websites into LLM ready data, And what that means in practice is you can feed it a URL, provide some options if you want, and it will go and take a look at that web page and return some structured data for you like so. This is their scrape endpoint, which will take a single web page and scrape some data from it. They also have a couple of other endpoints, crawl and map, but today we're gonna use scrape. Now I've already logged into Firecrawl Cloud and generated an API key, which I'll copy for later. You can also self host Firecrawl, but for ease, I'm just gonna use their cloud product here. Now I have this directors project over here with a new empty collection called companies. In this collection, there are a few fields, URL, a name, a description, a mission, and a boolean, a true force value, is it open source. And our goal will be to provide the URL and then have FireCrawl automatically populate the rest with flows. So let's go ahead and create a new flow. So this is our automation builder if you've not seen it before. I'll call this one get company data, I guess. And we are going to use a manual trigger which will add a button to the side of collection and item pages. So we're going to say we'll run this on the company's collection. What else matters here? We're going to not require selection, so the button always works, and we're going to require confirmation which will pop up a modal, and in that modal we will just add a URL. We'll make it a string input and we'll make it full width, and I think that's all we need to do here. So just to see what happens here, if I go back to the, to the company's collection, we now see this button here, this manual flow, trigger. I click that. It pulls up the box, and we'll put in a URL and hit run flow. So, now, if we go back to our flow, we should immediately see that there is one log. And in here, there is a body and the URL is the value that we typed. Fantastic. Now we need to actually do something with it. So, let's go ahead and add a new operation here. And, honestly, fire crawl is pretty sick. You can just make one web request. Let's take a look at their docs. We're gonna use the llm extract endpoint here, and let's just take a look at the kind of construction of this API call. It's a post request to this URL. We're gonna pass in our our API key here as an authorization header, and then they give us this kind of JSON payload here. Here, it's telling it to go ahead and extract specifically these four fields, the company mission, does it support SSO, is it open source, and is it in Y Combinator? And it's saying you must go get all 4 of these. So let's actually just turn this straight into a flow request request URL operation. So we're gonna do a post request to this URL, post request to this URL. I'm gonna go and copy my API key again here, and at the end of this, I'll, I'll destroy the key. Authorization authorization, bearer API key, save, and then there's the request body. And, honestly, it contains a little more than we need but this contains everything we need so we'll just pop that in there. The only thing we wanna do, of course, is pass in the URL that we put in the box. So we'll replace this with trigger dot body dot URL. Fantastic. Let's save that and see what happens if we go over to content, press the button, and type in directors dot io. We see that's running. That's running. That's a good sign. It means it's going off and making the request, waiting for the request. And then we see there is a second log. And we get some data back. There was a 200, so it was successful. Inside of data, there is a property called data, and then there is this value called extract. Extract contains all of those custom keys we asked for, company mission, supports SSO, is open source, and is in YC. And then always when you scrape, you get this metadata object, title, description, language, Open Graph data, source URL, and so on. So really all we want to do here now is we want to take this data and create a new company from it. So let's add a new, let's create a new operation on the resolve path of that web request. Let's call it create data. We're gonna create something in the company's collection, we'll give it full access, and then we just need to provide a payload. So let's go ahead and do that. We have an object here, we have a name, we have a name, and we're gonna pass in the value of the last operation dotdata.data.metadata.title. Then I, for 1, am I'm just gonna copy this and edit it each time. So we have name, URL. So that's last.data.data.metadata.sourceURL. We could, of course, just take it from the trigger body URL, but this is properly formatted. You'll notice I typed in directus.io, but when it came back in the payload, it came back with the with the, with the protocol HTTPS and so on. So we have name. We have URL. We have a description. Now this one is also from the metadata Description. We have the mission. Now this was a custom piece of data we asked to be extracted. Company mission is what we called it. And, finally, we have open, open underscore source. Last data, data extract and then is underscore open underscore source and then remove that trailing comma. So I believe that's the name of all of the fields. We'll figure it out in a moment when when it inevitably doesn't work. We'll hit the button again, directors dot io and hit run flow. Once again that's going off to Firecrawl using their endpoint and there we see there we see it straight here. URL name, description, mission and the boolean is open source. Let's, let's try that once more. Let's go in here and say fire fire crawl dot dev run flow. So let's see. And in theory we should just give that a moment and there it is. So now you can go ahead and grab more data. Now, of course, if we take a look at this endpoint here, you can provide custom properties and it will try its best to get data out from that. They have a couple of other interesting things which I'll draw your attention to even if I don't think it works in this context. They have, attention to even if I don't think it works in this context. They have extracting without a schema. So this extract here was us creating a a schema. Right. You can give it just a text. You can give it a prompt. Extract the company mission from the page, but the thing I don't like about that is you're not explicitly saying what the name of the key is, so you don't necessarily know what it's going to be at the end. I like creating a schema personally. They do something else that's kind of interesting. If I take a look at where is it, I think it's in their API reference here inside of scrape. They have this interesting thing called actions. So you can get it to wait, to take a screenshot, to click, write text, press a key and scroll and the combination of clicking and writing text means you can get it to interact with your web page. You see it here, actions, wait 2 milliseconds. You could get it to, like, sign in to things perhaps, perform search terms. I think it's super interesting and then take screenshots, of course, and upload those to directors if you fancy. So there's a lot of flexibility in this. Having seen kind of how easy this API is, I think I'll go ahead and turn this into, an extension some point in the next few weeks which we can release as part of Directus AI. But, yeah, that's how to integrate FireCrawl with Directus using Directus Automate. Hope you found this interesting, and by all means, if you have questions just reach out. Alright. Over to Beth. Welcome to The Reading List. Internally, we have a number of Slack channels where we share links from all around that we found educational and entertaining and we'd really like to share some of our favorites this month with you. First up, we have EIAIO games on scaling 1,000,000 checkboxes. This was a web experiment where every time anyone globally checks a box that change is reflected across everyone's browser and it goes in-depth into the scaling challenges of the unexpected popularity of the website leading to 650,000,000 checked boxes. Next, we have a recorded YouTube video from wasm. Io24 featuring Luke Wagner giving a talk on a stream of consciousness on the future of async in the component model. This is a great talk from one of the original designers of WebAssembly on how async will work in WASI 0.3. Next, we have introducing Netflix's key value data abstraction layer on the Netflix tech blog. This takes us through some of their global backend infrastructure design powering one of the most popular streaming platforms around and their use of multiple online distributed databases such as Apache Cassandra. Next up, we have an interesting read introducing the tool Cambria which is used to help you describe in a structured way how schemas change and why they change. This is from an independent research lab called Inc and Switch, definitely one for folks interested in the more academic side of things. The data visualization catalog provides a list of charts categorized by their data visualization functions or by what you want a chart to communicate to an audience. It's a really useful guide for selecting charts based on your analysis or communication needs. It starts with what you're trying to communicate and recommends which chart or tools to use. Lastly, a nice little tip from Vic instead of storing states as booleans, so like true or false, you could consider a date stamp so you can see when a state was updated with null meaning false and any date value meaning true. Those are just a few of the links that we shared internally this month. If you've got your own interesting links we would absolutely love to see them. We would recommend you share them within the discord server so you can see them and you never know they might even be in next month's changelog! We like to take a moment towards the end of each changelog to thank our contributors and sponsors who help make Directus as great as it can be. A huge thank you to Florian for dropping support for the old SendGrid transport option in favor of SMTP directly. Thank you to Shane for adding support for disabling terminal screen clearing on rebuild in watch mode. Thank you to Junhong for fixing SVG truncation issue in image interface preview, for fixing an issue that would cause custom role names to not be translated in the role navigation sidebar, for fixing WYSIWYG interface to be updated when interface options are changed via conditions, for improving behavior of the translations interface when setting use users language option, for ensuring data in sidebar is re fetched when switching pages while a sidebar pane is already opened, and for ensuring errors occurred within the card layout are always shown. Thank you to renovate for updating dependencies across multiple packages. Thank you to Joshua for fixing an issue that would cause log streaming to fail on directors installations that use a sub path in the URL. Thank you to for ensuring users are redirected to the intended page after login via LDAP. Thank you to CASE for adding the optional ignore rules option to schema apply CLI, and thank you to Anton for making the output types of SDK requests better readable in IDEs. Thank you again to each individual you can see their specific pull requests inside of the full release notes on GitHub. Lastly, we also want to take the time to thank our GitHub Sponsors of September who financially contribute to Directus' development specifically a huge thank you to Clement that's dlxclem for sponsoring as our backer tier of $100 a month. The money we are given from our Github sponsors goes straight back to the community members who build tooling and extensions for the directors ecosystem. Thank you again for being part of that. Alright, that is the last of the segments that we have for this month. If you are still watching thank you so much for sticking around we appreciate you And if you've watched the whole thing we'd love to know which segments you particularly like. That helps us plan for the next few months and to show, what you'd like to see so do let us know any feedback about the change log. The next change log is November 12th at 3 pm the same time and I do hope that it's going to stay the same time in discord because we are changing from British summer time to the non British summer time which is Greenwich Mean Time So if you see a different, time, it'll still be 3 PM GMT BST. It's all gonna be there. It's fine. Yes, Kevin. Not quite connect. Weird presenter in my opinion. Anyway, thank you so much again for tuning in. Have a great rest of your day, and hopefully catch you for the next one. Bye, everyone.","\u003Cp>Join us for The Changelog, taking you through the month&rsquo;s Directus updates including product updates, new content and community contribution highlights. This month's show includes a community showcase of a form builder from David, Carmen taking us through a docs preview and Kevin with a tutorial on AI web scraping API with flows.\u003C\u002Fp>","e5fcb8f2-0efc-45bd-a936-5b6133c1c0ce",[1759,1760,1761,1762],"8735bb07-70ba-4859-8691-f8f73fbbd4b6","01efa3c2-0d0a-4cfa-9977-98491f1fb5e4","a29b02f4-9587-4342-83cc-4e81821a0286","dd8ab2f5-3e33-4655-9469-411840a590d4",[],{"year":978,"number":1536,"id":1537,"episodes":1765,"show":1766},[1539,1540,1541,1542,1519],{"title":297,"slug":298},{"id":1768,"slug":1769,"vimeo_id":1770,"description":1771,"tile":1772,"length":263,"resources":1773,"people":12,"episode_number":480,"published":1777,"title":1778,"video_transcript_html":1779,"video_transcript_text":1780,"content":12,"seo":1781,"status":19,"episode_people":1782,"recommendations":1785,"season":1786},"84c7b3ac-fd85-4539-8f39-3247118bcbf2","17853","1008857977","In this recording of our live event on September 12th 2024, Nils, Jonathan, and Rijk discuss Optionally Delete Unused Files.","0a141eae-c91d-4519-bec4-1faf19b7ef37",[1774],{"name":1775,"url":1776},"Discussion","https:\u002F\u002Fgithub.com\u002Fdirectus\u002Fdirectus\u002Fdiscussions\u002F17853","2024-10-07","Optionally Delete Unused Files","\u003Cp>Speaker 0: Welcome back, everybody. Welcome to another request review. We have a very special guest here today. Welcome, Niels. He's 7 of our usual host, Daniel of the day.\u003C\u002Fp>\u003Cp>Jonathan, what are we gonna be talking about?\u003C\u002Fp>\u003Cp>Speaker 1: Today, we are going to talk about discussion number 17, 853. That is going to be about\u003C\u002Fp>\u003Cp>Speaker 0: My favorite.\u003C\u002Fp>\u003Cp>Speaker 1: File deletion and management. So this is primarily around the relational, file deletes. If you delete a file from a record or you delete a file from the file system or from the direct to files library, what are some better options for managing that relational delete. So currently, we do a do not delete on the file. It's kind of the default configuration.\u003C\u002Fp>\u003Cp>There are some cascading rules and things you can configure in some cases. But because files can be associated with so many different collections and records, this particular request is around, are there some additional options or features that we could add around improving that file management? Right. So that's the that's the general I'll drop a link just in case people didn't get it.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So to kick that out right away, the, the important thing to note here, of course, is that direct to files, you both have this sort of, you know, physical file on disk, and you have a row in direct to files, the system table that contains additional metadata we cannot store on the file itself. Right? So think about, we store information about, the some of the exit data for easier retrieval. We store, things like width and height, file duration for for movies, I think, and audio files.\u003C\u002Fp>\u003Cp>We have some additional fields, focal points. Yeah. Exactly. Some location text, some optional metadata that's human configurable. And, of course, some information about what direct this user created, updated, and modified it, whatever.\u003C\u002Fp>\u003Cp>So we have a record in the relational database in the system stuff for for more information. So it also means that when it comes to automatically deleting those files, what we're talking about here is effectively a, you know, sort of cascade delete or a cascade update whenever that relational record is getting destroyed. Right? So from a usual database management perspective, this is a very standard task thing where it's like, okay. The ID of the file gets removed.\u003C\u002Fp>\u003Cp>I want there any record that uses it to be destroyed as well. Let's do the other way around. Right? The the foreign key gets removed, destroy the file. Now in this feature request, what which way is it going?\u003C\u002Fp>\u003Cp>Because I believe it's saying that they wanna delete the file when they delete the record where it's used rather than the other way around. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. In this case, yes. They've mentioned it both directions or it's mentioned somewhere in here. But if you're deselecting or removing an item from a a record association. Right?\u003C\u002Fp>\u003Cp>So if you've got something simple like articles with a file associated, one of the option requests discussed here is if I delete or remove the file from here, that the user either automatically deletes the file from the file system, removes it from direct to files, ideally cleans it off the drive. But alternate options were to actually prompt the user for, do you wanna delete the file? And if yes, go ahead. Yeah. Lots of complexities that come with all of that.\u003C\u002Fp>\u003Cp>Right? Because file the file could be in use by another record. The other big thing with files, because files can be embedded inside of things like WYSIWYSIWYSIWYGs and block editors as relational. Right now, and this isn't this isn't necessarily a direct to specific issue. This is a SQL issue.\u003C\u002Fp>\u003Cp>Right? Unless you're maintaining dual relationals all over the place with everything related back into the file system or some mechanism to track because files can be associated with any number of collections and be embedded inside of rich text fields. How do we manage or understand those kinds of things as well? So there's a a number of factors, I think, that make this complex. But for now, at least what they're asking for is if I delete the file from the record, automatically delete the file or prompt the user, with an option to keep or delete the file.\u003C\u002Fp>\u003Cp>Speaker 0: So it's an interesting one there because the so so both those points, the first one to your point, this is this is a problem that has been as old as any sort of content management system on the planet is if you have a link to a file in a piece of text, is that a relational record? No. Is that a relationship in a database you can easily track? Also, no. So now on file delete, are you gonna be scanning through all of those blobs of text all over the place to sort of pseudo find relationships that is hella inefficient?\u003C\u002Fp>\u003Cp>Right? So how do you deal with that? That's that's a good one. The second one that is interesting is that this is kind of the going the wrong way, so to speak, if you compare it to how regular relationships work in a database. Right?\u003C\u002Fp>\u003Cp>So usually, when you say when you do a cascade delete, it would be the moment you delete the file, you delete all of the rows where it has been used in a foreign key. Am I saying that right? Or am I making this up? I was getting too confused, to be honest.\u003C\u002Fp>\u003Cp>Speaker 1: I think it goes the other way because the foreign key, at least in that case, like in my article case, the foreign key reference is actually here on this record. So we actually store the foreign key. So I believe the cascade is actually going the other way. So if I delete it from here and I tell it to cascade, it should cascade and delete the file. And I believe that actually does.\u003C\u002Fp>\u003Cp>So we can actually double check the settings, article because\u003C\u002Fp>\u003Cp>Speaker 0: we do some article in both ways in direct as I was just trying to remember if I wrote like myself which way the default is.\u003C\u002Fp>\u003Cp>Speaker 1: So the relationship in this case is directional from direct as follows. Right?\u003C\u002Fp>\u003Cp>Speaker 0: It was the rate that I said. It's like the moment you delete the file, you get to choose what you wanna do, with the forward key field. Right? Do you wanna nullify it, or do you wanna destroy the whole row that uses that forwarding key? So so that's why I meant with sort of like the feature request going the opposite way.\u003C\u002Fp>\u003Cp>Right? Because by a database and default, you're saying, okay, the moment you delete the file, delete everything where the file is used. But it feels like this feature request is going the other way. Right? They're saying the moment I deselect the file from a gallery post or something, I also want to trigger a deletion of the related file.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. Key.\u003C\u002Fp>\u003Cp>Speaker 1: So now you're actually you are actually correct. It does go the other way. So if you delete the file, it's actually cleaning up the foreign key, right, is what we're technically do. Yep. My mistake.\u003C\u002Fp>\u003Cp>Speaker 0: So this and this is welcome back to the beauty of request review. If you haven't been here before, we always like to diverge as much as we can before we come back down to the notes at the top of the hour. The immediate question to me here becomes, is this a file specific thing, or is this just a directed data thing? Right? Is this something that you should be able to set up for everything where you're like, okay.\u003C\u002Fp>\u003Cp>The moment I I delete this record or deselect this relationship, I wanna be able to remove the thing on the other end. Is that a thing that we just wanna support as a more generic option or is this something that is inherently specific to files? Because it kinda sounds like that this is that this person was mentioning files just because that's the use case for this particular person and this website that they're building. But I could see this as be a relationship thing. Yep.\u003C\u002Fp>\u003Cp>Speaker 2: At least implementation wise, it's like equal, I would say. There should be no diff like, no technical difficulties to make it more advanced in a sense or more capable.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. No. I actually like that. I I think, especially as we move towards, you know, our our our future concepts of we want to support non sequel kinds of things. This means, to some degree, direct us needs to understand where things are being used and keep track of that so that otherwise, we can end up with a, you know, an even bigger mess.\u003C\u002Fp>\u003Cp>Right? Sync beyond files to records to data and content. I don't I've been trying to think about this because this comes up quite a bit. There's there's also things like one of the things that I've seen a number of times recently is, can I tell what the file is associated? Can it can can you show me what else you know, where is this file being used?\u003C\u002Fp>\u003Cp>Right? So that comes back to in particular, this is problematic for things like where it's embedded as a text, you know, inside of a rich text. My brain goes, how do we we would have to, in some way, keep track of the relationals as part of Directus. Because I did some searches on, like, SQL databases. How can you do this in c e?\u003C\u002Fp>\u003Cp>Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: It's not really you you would have to pull up what are all of the foreign key constraints that go back to this table and then go search through all of those tables to find foreign keys with the ID, I guess. That's really the only way to do it.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It is. And you can do some view things. You can do but you have to you know, it's it's complex. It's not something that's just simple to say, where is this?\u003C\u002Fp>\u003Cp>But if Directus in some way was actually keeping track in some way of, you know, think of some kind of an indexing or a mechanism of keeping track that when it gets associated, we track that it got associated in some way. Right? Call it a table. Call it a\u003C\u002Fp>\u003Cp>Speaker 0: The the difficulty there, of course, is that data could be live updating through all sorts of sources that's not direct us. So how do we keep that index up to date? Or is that something that we have to periodically re scrape to regenerate, which sounds tricky, or tricky to keep that, you know, consistent. Keep that keep that data integrity alive. This is also a feature request that immediately reminds me of be because of this connection, it reminds me of the one around, how do we warn people, about where stuff is used when you delete it?\u003C\u002Fp>\u003Cp>Because right now, you can set up the form keys to nullify a file, but if somebody deletes a file, they might not know where they're gonna be nullifying things automatically. Right? It's a very similar, where is this being used question except during delete.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. And that's the that's the common request is I you know, at least I would like to know where this, you know, where this thing is where all this thing is being used. You know, what what all is it associated with in that sense\u003C\u002Fp>\u003Cp>Speaker 2: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: For tracking it down? Because if I yeah. You know, I wanna understand the impacts of me deleting a file or me deleting a record, what are the Right. Relational impacts?\u003C\u002Fp>\u003Cp>Speaker 0: Now looking up all those foreign keys is a pretty heavy thing. But in Directus, we do store that relational information already. So we should have the ability to say what are the relationships that have this collection in it, and then do a sort of aggregate search that says, okay. What is the count of items that have a foreign key to this item? Which should be a relatively e like, not easy, but a cheap operation to do rather.\u003C\u002Fp>\u003Cp>I mean, it depends on table size. There's always an asterisk, but we could, you know, do a what's the kind of items that are using the current primary key as a foreign key in the, related tables and then present that as well, right, where we say, okay. We know, this collection is being used in well, in this example's case, direct this files is being used in, photo albums. And therefore, we can say, when you delete this file, you're gonna set it to null in these in 3 different photo album records. Right?\u003C\u002Fp>\u003Cp>Is this something you wanna do? Yes or no? So at least be a little bit more helpful about that.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I really like the idea of, like, having a Oh, sorry.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, no. Go ahead. Go ahead, please, dude.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I really like the idea of having, like, a graph view. Like, just in general, that would show you, okay, this would be deleted. This would be, like, nullified. This would be this this in-depth, that happened with on a delete.\u003C\u002Fp>\u003Cp>And, like, the data is already there in the API in the app. We just have to then visualize it with, like, a graph view or a list of things that get deleted, a list of things that get nullified. We aren't we kinda doing something with creating when creating, for example, relational stuff, we already say what we create. Like, do you know this yellow text list that we have on the data data model creation? We can just do the same for the deletion process, basically.\u003C\u002Fp>\u003Cp>If you delete an item, go through the whole schema of relations and see what links to what, and that's basically just recursively checking against each nested collection. And the fancy part then would be how to visualize this. And in in terms of, like, do we run it in a list? Do we run it in a fancy graph where it's like, this collection links to that collection, and this field in that collection would then delete another collection or something like that? Or would we just run it as a list very stupidly?\u003C\u002Fp>\u003Cp>Speaker 0: And to make that one slightly more complicated, what do you do if there is user input required? So, for example, if the default cascade is make it null, but the field is not nullable, then what? Right? Are we gonna Then\u003C\u002Fp>\u003Cp>Speaker 2: you get an escape.\u003C\u002Fp>\u003Cp>Speaker 0: Are we gonna tell them you cannot delete it because it's in use, Or are we gonna tell them, what do you wanna do with the 5 places where you're using this? You wanna replace it with something else? And is that something we wanna do in line? Or is it enough if we sell if we just say you cannot delete it right now, go fix the other stuff first? Like, which is also, you know, a flow that would be enough.\u003C\u002Fp>\u003Cp>I don't know if it's as useful.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think at least indicated that it exists somewhere and then saying, go clean it up before you can actually delete the file. That seems okay. You could give the option to say nullify where I'm at or place with something else where I'm at. Interesting.\u003C\u002Fp>\u003Cp>Interesting.\u003C\u002Fp>\u003Cp>Speaker 2: I'm I'm also wondering, is it is it is it not really, like, configuration error if you set the relation to nullify something that can't be null? That sounds like somebody has messed up things in that case, which we should also protect people from doing. But\u003C\u002Fp>\u003Cp>Speaker 0: Fair enough. Yeah. That that is that is fair. But the the default behavior for foreign key like that is to prevent the deletion, I think. So even if you don't set it to set nil, but you just leave it at the default, which is prevent the deletion, it'll just throw an error.\u003C\u002Fp>\u003Cp>Because I throw right now, we don't throw a SQL error that says I cannot delete form keeps Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: Exactly. And then we should probably even, like, protect when setting up the relations that you shouldn't be able to set it to nullify something it can't be nullified to.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. But I think it's all the settings. Even the the mistake setup and the the default case. There's a case to be made that we wanna have some sort of user input moment in during the delete that says, okay. We're gonna be either nullifying if that's the setting, or we're gonna try to nullify if that's the setting, across 15 items in your your 12 collections.\u003C\u002Fp>\u003Cp>Or if it is a, prevent the deletion, the question could be, do you wanna replace where this is used with something else before you can delete it? Right? That would be a pretty helpful thing. I know we're getting a bit of trend for this particular feature request, but these are all kind of part of the bigger part of the same, sort of questions and user stories.\u003C\u002Fp>\u003Cp>Speaker 1: Agreed. And, again, I think he's got he's got some of those cases covered. They they've got some of those cases covered here. Right? So they know to delete the file, keep the file, prompt the user.\u003C\u002Fp>\u003Cp>I must\u003C\u002Fp>\u003Cp>Speaker 0: delete it\u003C\u002Fp>\u003Cp>Speaker 1: in the collection.\u003C\u002Fp>\u003Cp>Speaker 0: Well, to to get it back on track for this particular feature request though Yeah. Because the what we just described is, you know, if you wanna follow just a regular foreign key constraint, and we wanna inform the user of what's about to happen, that's part 1. We wanna allow them to take some action if they have to make some changes for it to go through. But then the third one, and that's the new thing in this feature, requested the question, do we wanna do it the other way around as well? Where unlike in a database, we allow a secondary foreign key constraint type of thing where we say, if you delete the the the record that holds the foreign key, delete the related thing.\u003C\u002Fp>\u003Cp>And that then has a sort of recursive compound effect where it's like, do we now show any other foreign keys that might exist on the thing that you're deleting through the foreign key that you just deleted? What?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: I think that's probably one of the reasons why, you know, databases don't sort of recursive, like, have that way of doing it both ways because that really feels like, you know, you're you're quickly ending in a in a in a sort of infinite loop type of deal.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And that might be where maybe you don't show them that. You just simply know that is still being so if they choose, say, the delete option to say, you know, I do wanna delete the file, or the the record in this case, then you could provide an indication that there are existing relationships. Are you really sure? And then it'll affect those other you know, it will affect other things, you know, and number of other things or just the fact that it exists, that it is actually related to other active items.\u003C\u002Fp>\u003Cp>So instead of and not try to show them what all because that could be a large number of things. I don't know that you'd want to try and show that in a dialogue box, but you could show this is related to any number of other things or this is related to other things. And therefore, you know, this could have catastrophic effects. Yes or no? And maybe even making an option that, you know, a toggle config flag or something on the settings of that says, if this is still related to other things, then they're not allowed to do that from a permissioning standpoint kind of thing.\u003C\u002Fp>\u003Cp>Then, you know, the option is, no, you can't delete this because this is related to other things. Go to the source record and clean it up and do whatever. Right? The whatever workflow. So I think you can go that route, maybe.\u003C\u002Fp>\u003Cp>Speaker 2: On another note, I'm also wondering if we should that it's it goes in a similar direction, but, like, protecting the users from making mistakes, would be like a feature where you have a protected collection, that basically says, okay, you have to, for example, enter the, the name or ID or some type of validation before you can actually delete the, the item in the collection. For example, if you're managing users and you don't want to mess up have, your managers mess up the user's collection because that could be catastrophic or something similar, where they would have to then, for example, enter something. And that would also tie into this, like, what happens on the delete part where it's like, okay. This collection is related to other collection that is protected, and we should also, in this collection, mind you that you're deleting something from another collection that is protected. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yes. Yes. Yes. Yes. This old man now this makes me think from a technical perspective.\u003C\u002Fp>\u003Cp>We're gonna dive a little deep here. But from a technical perspective, this makes me wonder right now, We rely on the database foreign key constraints for all this. Right? That we say, okay. You said it's a set null.\u003C\u002Fp>\u003Cp>That is literally a database foreign key index with a cascade set null. Right? Yeah. But that also means that from a Directus perspective, we're not triggering any logic or hooks or or anything necessarily on the Directus side for those nested deletes because we're just relying on the database to do it. Right?\u003C\u002Fp>\u003Cp>Which is a technically performant way, but at the same time, that does raise newer questions around, you know, if you have a flow that handles something on file, delete to clean something up. Right? And that file gets deleted through. Well, I guess a file is actually a bad example, ironically enough, because it's on the disk and not on the database. But if you have a flow that cleans up something, on a database change, but it is changed directly from a foreign key constraint that changes it.\u003C\u002Fp>\u003Cp>We're not triggering it as an event. Right?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I think that was also one of the problems a lot of people had with working with flows that it wasn't, like, cascading these, like, deviate events to, to flows, basically, I think, which would also solve that problem in a sense, I think. Although, I don't remember exactly what the problem there was in detail because it's quite a long time ago, which where I had read through these issues. But I do still like the idea of, like, this this type of protected collection. I I do think that could I'm not sure.\u003C\u002Fp>\u003Cp>I haven't seen many press on that idea, but I do think it could be a nice enhancement just from on the side of, like, making sure you don't do anything. But, I mean, aren't we doing something sim I guess, no. We're not. But, for example, on the director's collection, you could technically delete those from the app. Right?\u003C\u002Fp>\u003Cp>But we somehow oh, oh, at least Yeah. No. In a sense, we also do some sort of protection there because, like, yeah, sure. You can do that technically, but you really shouldn't.\u003C\u002Fp>\u003Cp>Speaker 0: No. No. I'm pretty sure we're we're preventing any any direct to stable modification from the product itself. Yeah. Yeah.\u003C\u002Fp>\u003Cp>I mean, you can add custom fields and remove those, but you cannot mess with the the system ones, which I think is a separate feature request, actually, funnily enough. I don't know if we we I forgot if we did one of those, an episode of one of those, but there's a feature request to be able to update, you know, the locked system fields because some people are rightfully so saying, I don't use a description field on files. Can I just delete it? And I'm like, no. Right.\u003C\u002Fp>\u003Cp>Not at the moment.\u003C\u002Fp>\u003Cp>Speaker 1: Well, I think it was that was actually a permissioning thing. They wanted to be able to not show it. Right? Be able to deselect that as a visible option to the user.\u003C\u002Fp>\u003Cp>Speaker 0: That should be fine.\u003C\u002Fp>\u003Cp>Speaker 1: So that there there were some key things there. That was actually related to the permissioning. I remember that one because that was I was like, oh, you can't. Different description. There\u003C\u002Fp>\u003Cp>Speaker 2: are there\u003C\u002Fp>\u003Cp>Speaker 1: are things that you can't remove from the user view. Right? So, again, we do lock the fields, and I think we should we should allow it to have these. But on the access policy side for that particular one, it was, you know, if I'm in the content manager policy here that\u003C\u002Fp>\u003Cp>Speaker 0: called app access permission set has you can see your own user, but then that c includes some of those system fields that you may or may not care about. That's Yep.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. Yep. So that was all related to this of, you know, if I'm in my directest users on the permissioning side of the app and those kinds of things in the read permissions here. Yeah. Custom coming down into the field permission, I can't deselect.\u003C\u002Fp>\u003Cp>So if I don't want to show, you know, the location of the title or the description, because we're not using them or whatever. There's only certain things you can deselect from the key fields.\u003C\u002Fp>\u003Cp>Speaker 0: So Yeah. No. That makes sense. That makes sense. That was a little bit.\u003C\u002Fp>\u003Cp>Speaker 2: Let's not get I\u003C\u002Fp>\u003Cp>Speaker 0: I recognize how bug big of his detour this is from what we're talking about. But\u003C\u002Fp>\u003Cp>Speaker 1: I guess the other thing that would be nice, the the one thing that our our locks currently prevent is, like, reordering the fields. Right? So, like, we stick the roll and some of the configuration details at the very bottom. Well, what if I wanted to roll up at the top in the item form to see it Yeah. In in a certain way?\u003C\u002Fp>\u003Cp>Speaker 0: I think our founders sells the topic for the next one of these.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Sometimes. But back to file delete. What was I taking notes on? Larger configuration.\u003C\u002Fp>\u003Cp>Speaker 0: Now here's another very real question. I know there's, of course, enough upvotes for us to warn discussing this, but there's also a very real question of, is this something we want? Like, the whole flow of warning people for a cascade delete and doing it one way as per the database the way we do it now. Sure. That that feels like an obvious one.\u003C\u002Fp>\u003Cp>That that part, obvious to me. But this one is interesting where it's like I deselect a file from a relationship. Do I wanna be able to delete the file, or is that confusing?\u003C\u002Fp>\u003Cp>Speaker 1: For content user for the user experience, yes, I would like that option because the user doesn't want to the the user workflow wouldn't necessarily be I now have to go to the direct to file library, find that file. Once again, how do I find the file? Do I remember the file name? Do I do those things? How do I go and find and now delete that file?\u003C\u002Fp>\u003Cp>That I think is the the key thing from a content editors perspective. Yes or no?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I'm I'm thinking about 80, 20\u003C\u002Fp>\u003Cp>Speaker 1: here we have to think about, but from a content editors experience, right, we find it we all find it very annoying if I have to go do 3 more steps to go delete something. Right? If I have to go now, remember to go back to the file library, go search and find the file, and then delete the file, only to find out that the file can't be deleted because it is being used. Or Right. Accidentally now not know that deleting the file has now removed it from some place that I didn't know about or didn't even have access to.\u003C\u002Fp>\u003Cp>That's the other permissioning adds another level of complexity to this. Right? So my ability to delete that file, well, it might be in use somewhere else. And that's another reason I think we shouldn't show the items or try to show items necessarily because they may not have access or permissions to where that file is being used. Mhmm.\u003C\u002Fp>\u003Cp>And so you get back to the concept of, well, how do I, you know, how do we deal with that? What do we what do we allow or not allow? I think it's a nice workflow, because commonly, right, it's not so much that we're I I think in most of these kind this this particular use case. Right? The file is only being used in the record that it was uploaded for, you know, you know, it's a gallery.\u003C\u002Fp>\u003Cp>It's something that they're doing in the, you know, the content users adding that for that specific thing. And then when they're done, they just want it cleaned up. We don't wanna have gigabytes of files laying around in our file storage and in our file systems that we're not using. Mhmm. Two questions.\u003C\u002Fp>\u003Cp>Just a thought.\u003C\u002Fp>\u003Cp>Speaker 0: One one thought. Two questions. One technical interesting difficulty is nested relational staged changes. As in, if I'm on an item where the file field is required and I choose to delete the file instead of deselect, is that file immediately deleted, or is that file only deleted the moment I hit save on the parent that I'm clicking the delete on? I think that is that is a question.\u003C\u002Fp>\u003Cp>And that is mostly around if you're in content versioning, for example. Right? And you make a new version of an existing thing and it uses the same file. If I click delete the file in the version, I now destroy the published one. You know what I mean?\u003C\u002Fp>\u003Cp>So that file should only be deleted the moment I publish the staged versions item. I think that's an interesting interesting little problem. But then how is that presented in the UI? Because if you have marked the file as deleted and then you select a different one, do we keep that as a state? Do we show a little indicator that you have 5 things scheduled to be deleted?\u003C\u002Fp>\u003Cp>Or how does that work? I think that's a difficult question. And then\u003C\u002Fp>\u003Cp>Speaker 1: That's how we're doing it. So that's how we do it today. Right? So we do show that indicator. Right?\u003C\u002Fp>\u003Cp>So if you go to I have that will work for that. On most of our relational kinds of things, some of the relational interfaces, we do indicate that you are deleting and adding. Right? So we're removing this. Right?\u003C\u002Fp>\u003Cp>We have that indicator there. We don't actually delete until you commit the record. Right? We don't actually propagate any of the changes until we\u003C\u002Fp>\u003Cp>Speaker 0: I'm I'm supposed to think of it right in a in a file picker, a deselect and a and a delete is very similar. You deselect with the intent to select a different one. Right? But if I deleted with the intent of selecting a different one, now I'm effectively presented with 2 changes that I made. We don't really have a UI pattern for that, yet, which is something what was it to think about when I left the ad.\u003C\u002Fp>\u003Cp>It's just like a regular drop down. It's all the same same problem. The second thing that popped to mind, and this is a bit of a bigger just think out loud type of thing, would it make more sense for a use case like this to make your own custom direct these files collection where there is no relationship to direct to files. There's a relationship just directly to the file. So you would have to store some of the required fields for that to work, like where is it on disk, what disk is it on.\u003C\u002Fp>\u003Cp>Is that a thing? Because technically speaking for direct test files, the only required columns to turn that into file management would be what adapter is this on and what is the the file path to find it. Everything else is is metadata that we use to enrich the platform, but not necessarily required to do file management.\u003C\u002Fp>\u003Cp>Speaker 1: Good. Then you've got to handle I mean, again, some of the key issues that we run into, right, with how does the file when and where does the file get uploaded? How is that managed? Or are you assuming that the file exists on that file system? Right?\u003C\u002Fp>\u003Cp>The the file interfaces do a whole lot of or the APIs. Right? The direct to these APIs do a whole lot of things for you. And we've, you know, some of those new enhancements around file streaming and restarting where you left off. And, you know, these capabilities, when you're dealing with files, it gets, you know, there's a lot of complexity there.\u003C\u002Fp>\u003Cp>And I don't think a lot of folks are gonna want to take that on as a I when I wanted to manage a custom extension that's doing these things for me or, you know, replicating the capabilities of the direct to SAP application. Flows is an option. Right? So that is something that there's actually a link. Someone else put a link in here on how they kind of configured a flow to do some of this.\u003C\u002Fp>\u003Cp>Speaker 0: Right. Right.\u003C\u002Fp>\u003Cp>Speaker 1: Down here. So this thread here, there's actually an example of a flow. Right? So we do have on hook. You can action based on file upload, based on item update, create, delete.\u003C\u002Fp>\u003Cp>So you can apply whatever custom logic. I think the main issue there is you don't get a user prompt per se in those cases.\u003C\u002Fp>\u003Cp>Speaker 2: I have I have another probably probably a bit of side of like, side topic idea. That like, not not side topic, but, what is it called? Going outside of the the main road. We could have something like reference counted tiles where it's like, okay. This file exists as long as there are any uses of it.\u003C\u002Fp>\u003Cp>And when there are no uses of it anymore, it automatically gets deleted, which then we could also look into something where it's like, okay. If I upload the same image twice, we do, like, a hash of the image to compare if we already have that file somewhere, and some fancy fancy things, which in the end should just make it easier to manage all these files. Like, with 500 people uploading separate files, you get the files collection in, like, a giant amount of people having stuff there. And, like, a reference counter thing could just make it a lot easier to manage all these different file or, like, deduplicate files and get rid of, like, unused ones because that's a thing I've run into even by just myself using directors.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm. Yeah. When you don't have direct file store access, which many times in SAS slash cloud slash whatever.\u003C\u002Fp>\u003Cp>Speaker 0: Very interesting to start. So my brain is immediately in 12 places with that comment.\u003C\u002Fp>\u003Cp>Speaker 2: But it's the\u003C\u002Fp>\u003Cp>Speaker 0: an additional piece of functionality around relational data files and otherwise is the show me where the stuff that is not used anywhere.\u003C\u002Fp>\u003Cp>Speaker 2: I think it's a very Yeah. That's a good point. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Right. And this is something on on a layout level where it's like, okay, filter down by stuff that is not used anywhere else, which is, it does make me think of a comment that was on the chat a little earlier from our our own team that was like, hey. I'm already looking forward to the technical complexity of tracking references within text and JSON blobs in a barrel. Right? Which is very, very, very, very, very, very, very, very, pretty true.\u003C\u002Fp>\u003Cp>But\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. And in that case, it definitely has to be restrained to things that, again, we can, like, check where it's, like, on JSON blobs or, like, repeat us. That will be very, very difficult and probably not feasible. But at least we, like, at least at least we make the option to if you want to have a feature like reference counted files, then there is a way to have it easily and without any manual labor, basically. You could still do a repeater, but then you won't have the fancy it's basically like the idea of a repeater.\u003C\u002Fp>\u003Cp>A repeater, you can slap on really quickly to have just any list of data, but it's nothing fancy. You can't fancy query that. I mean, now you can, but a long time\u003C\u002Fp>\u003Cp>Speaker 0: you couldn't.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. Are we approaching a what do we think is actually achievable? What we would actually like it to do?\u003C\u002Fp>\u003Cp>Speaker 0: Oh, what do you want? I love it. I'm I'm still a little bit in two minds of this whole going doing doing for keeping strange whole place. And and the main reason there is because it kinda strays away from the database as the source of truth for this stuff, which could be a good thing because it unlocks other things, but it's quite a big breaking change in how we handle relationships just in general. Right?\u003C\u002Fp>\u003Cp>Because right now, foreign keys and direct relations are 1 to 1 the exact same. And we're reaching a point now with this where we're saying, well, we'll use foreign key constraints on the database for performance reasons because they're they're indexed, but we will not rely on them for this sort of behavior on foreign key changes. So, like, for things like, I wanna delete the related thing when I deselect the current thing, that is not a database thing anymore. That's the directest thing. We're just fine.\u003C\u002Fp>\u003Cp>It's just a little bigger change.\u003C\u002Fp>\u003Cp>Speaker 2: Or not, don't we have to anyways implement it in the future on the server side and not the database side on these constraints? Because when no supporting relations between databases, we can't have that anymore anyways. So we\u003C\u002Fp>\u003Cp>Speaker 0: Great.\u003C\u002Fp>\u003Cp>Speaker 2: Have to implement it ourselves anyway. So it's just on the road anyway. Yeah. Just a thing you have to pick up and then do, which is not as easy as it sounds. But Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: So with that in mind, I think some of the stuff that we talked about around, you know, warning and informing the user when a form key constraint is about to delete a bunch of stuff or set null to a bunch of stuff. I think that's an obvious thing. Definitely, it's something we wanna have. That would be a big quality of life improvement. I think doing the relationship the other way around, I think if something happened, it'd probably be a longer term thing just because it is a very big breaking change and how we handle relationships today that I wanna do a bit carefully and just more more planned out.\u003C\u002Fp>\u003Cp>The whole idea around custom tables for direct to files, I'm not feeling that one. I think that was a oh, you know, a bit of a brain fart. Just thinking that wrong. That's what these specials are for, but it doesn't feel like the right move right here. Because of all the the system logic that's going on under the hood, we we have to keep that in in control.\u003C\u002Fp>\u003Cp>What else was on the was Jonathan? You've been taking notes furiously. What am I missing?\u003C\u002Fp>\u003Cp>Speaker 1: I'm trying to keep up some of the workflow options, relationship existence, tracking kind of graph style or being able to understand the that was one thing that Nils mentioned just from a UX perspective. I think that's that almost feels more like the admin side and the data model having ERD style visibility and understanding that, oh, there is a relationship to files from x number of collections and being aware of those things. Taking it away from, you know, embedded inside of rich text or repeaters or weird things, but just the actual ERD, the understanding the graph of what is actually related to my content. From an admin perspective, that would be useful. I do really like the idea of at least understanding files is one of the most common things that I see around this, but we can also think, I was talking to Alex and, Antonio on our team this morning.\u003C\u002Fp>\u003Cp>They mentioned that, you know, many to any is another one that has this kind of weird thing where there's not an actual relationship. We do manage that in app. It applies to many to any though in the sense of I can go delete a component that might actually be in use somewhere and you don't get a warning of, hey, this is, you know, you you're you're impacting other things, or impacting something.\u003C\u002Fp>\u003Cp>Speaker 0: Deleting it from the relationship. You're not deleting the related item as of today rather. Like, that would be a similar to your point though. That's a similar thing with this feature request where if we add delete as a proper delete the related thing, then you're in a whole new world of how do you display that and how does that work? Does that happen to you sync or do the hood or yeah.\u003C\u002Fp>\u003Cp>Speaker 1: And I think a lot of this is probably considerations that need to be handled as part of, you know, the new abstraction layers and things where, as Nils mentioned. Right? Longer term, this is not going to you know, as we move to data federation next year, we're gonna need to be able to handle these things as Directus. Right? Understanding that now it's not just it's data in 2 different databases, right, that we've created a virtual relation for.\u003C\u002Fp>\u003Cp>We need to understand and track, I think, against I I think as you've said, isn't just a files thing. This really becomes a more kind of global direct us handling logic around how content can be connected to one another. Right? I'll take relationship out. It's just it's connected.\u003C\u002Fp>\u003Cp>It's there's there's some form of tracking that needs to be maintained. And I think as as you said, one of the problems that we have today is, well, if someone's doing stuff direct sequel, you know, we try to support all of that and make sure that we're aware of that. But I think as we move towards, you know, as you move towards something like this, well, if you choose to create or do things under the hood, you may not get them. It's the same thing that we do with activity and revision logging. You're not gonna get that.\u003C\u002Fp>\u003Cp>If you're doing something outside of the APIs, you don't get those benefits that exist within the Directus application. Now it could be that there's ways to crawl and find that they've done something and add that or indicate that there's something that then needs to be configured. But I think as we move towards Directus doing some of this logic in the abstraction layers, it's going to require that we handle these use cases in a in a comprehensive way.\u003C\u002Fp>\u003Cp>Speaker 0: Mhmm. Cool. Well, I think that probably concludes our our little discussion here around feature request 17 853. Any other last closing thoughts here, Neil's, Jonathan? Nope.\u003C\u002Fp>\u003Cp>Awesome. Well, cool. Well, thanks everybody for tuning in. As per usual, this will be up on direct us that a o slash TV. I hope you tune in next time about something else.\u003C\u002Fp>\u003Cp>Interesting, I'm sure. But until then, thank you for watching. Like and subscribe.\u003C\u002Fp>\u003Cp>Speaker 1: Subscribe. Thanks, team. Get in. Cheers.\u003C\u002Fp>","Welcome back, everybody. Welcome to another request review. We have a very special guest here today. Welcome, Niels. He's 7 of our usual host, Daniel of the day. Jonathan, what are we gonna be talking about? Today, we are going to talk about discussion number 17, 853. That is going to be about My favorite. File deletion and management. So this is primarily around the relational, file deletes. If you delete a file from a record or you delete a file from the file system or from the direct to files library, what are some better options for managing that relational delete. So currently, we do a do not delete on the file. It's kind of the default configuration. There are some cascading rules and things you can configure in some cases. But because files can be associated with so many different collections and records, this particular request is around, are there some additional options or features that we could add around improving that file management? Right. So that's the that's the general I'll drop a link just in case people didn't get it. Yeah. So to kick that out right away, the, the important thing to note here, of course, is that direct to files, you both have this sort of, you know, physical file on disk, and you have a row in direct to files, the system table that contains additional metadata we cannot store on the file itself. Right? So think about, we store information about, the some of the exit data for easier retrieval. We store, things like width and height, file duration for for movies, I think, and audio files. We have some additional fields, focal points. Yeah. Exactly. Some location text, some optional metadata that's human configurable. And, of course, some information about what direct this user created, updated, and modified it, whatever. So we have a record in the relational database in the system stuff for for more information. So it also means that when it comes to automatically deleting those files, what we're talking about here is effectively a, you know, sort of cascade delete or a cascade update whenever that relational record is getting destroyed. Right? So from a usual database management perspective, this is a very standard task thing where it's like, okay. The ID of the file gets removed. I want there any record that uses it to be destroyed as well. Let's do the other way around. Right? The the foreign key gets removed, destroy the file. Now in this feature request, what which way is it going? Because I believe it's saying that they wanna delete the file when they delete the record where it's used rather than the other way around. Right? Yeah. In this case, yes. They've mentioned it both directions or it's mentioned somewhere in here. But if you're deselecting or removing an item from a a record association. Right? So if you've got something simple like articles with a file associated, one of the option requests discussed here is if I delete or remove the file from here, that the user either automatically deletes the file from the file system, removes it from direct to files, ideally cleans it off the drive. But alternate options were to actually prompt the user for, do you wanna delete the file? And if yes, go ahead. Yeah. Lots of complexities that come with all of that. Right? Because file the file could be in use by another record. The other big thing with files, because files can be embedded inside of things like WYSIWYSIWYSIWYGs and block editors as relational. Right now, and this isn't this isn't necessarily a direct to specific issue. This is a SQL issue. Right? Unless you're maintaining dual relationals all over the place with everything related back into the file system or some mechanism to track because files can be associated with any number of collections and be embedded inside of rich text fields. How do we manage or understand those kinds of things as well? So there's a a number of factors, I think, that make this complex. But for now, at least what they're asking for is if I delete the file from the record, automatically delete the file or prompt the user, with an option to keep or delete the file. So it's an interesting one there because the so so both those points, the first one to your point, this is this is a problem that has been as old as any sort of content management system on the planet is if you have a link to a file in a piece of text, is that a relational record? No. Is that a relationship in a database you can easily track? Also, no. So now on file delete, are you gonna be scanning through all of those blobs of text all over the place to sort of pseudo find relationships that is hella inefficient? Right? So how do you deal with that? That's that's a good one. The second one that is interesting is that this is kind of the going the wrong way, so to speak, if you compare it to how regular relationships work in a database. Right? So usually, when you say when you do a cascade delete, it would be the moment you delete the file, you delete all of the rows where it has been used in a foreign key. Am I saying that right? Or am I making this up? I was getting too confused, to be honest. I think it goes the other way because the foreign key, at least in that case, like in my article case, the foreign key reference is actually here on this record. So we actually store the foreign key. So I believe the cascade is actually going the other way. So if I delete it from here and I tell it to cascade, it should cascade and delete the file. And I believe that actually does. So we can actually double check the settings, article because we do some article in both ways in direct as I was just trying to remember if I wrote like myself which way the default is. So the relationship in this case is directional from direct as follows. Right? It was the rate that I said. It's like the moment you delete the file, you get to choose what you wanna do, with the forward key field. Right? Do you wanna nullify it, or do you wanna destroy the whole row that uses that forwarding key? So so that's why I meant with sort of like the feature request going the opposite way. Right? Because by a database and default, you're saying, okay, the moment you delete the file, delete everything where the file is used. But it feels like this feature request is going the other way. Right? They're saying the moment I deselect the file from a gallery post or something, I also want to trigger a deletion of the related file. Yeah. Yes. Key. So now you're actually you are actually correct. It does go the other way. So if you delete the file, it's actually cleaning up the foreign key, right, is what we're technically do. Yep. My mistake. So this and this is welcome back to the beauty of request review. If you haven't been here before, we always like to diverge as much as we can before we come back down to the notes at the top of the hour. The immediate question to me here becomes, is this a file specific thing, or is this just a directed data thing? Right? Is this something that you should be able to set up for everything where you're like, okay. The moment I I delete this record or deselect this relationship, I wanna be able to remove the thing on the other end. Is that a thing that we just wanna support as a more generic option or is this something that is inherently specific to files? Because it kinda sounds like that this is that this person was mentioning files just because that's the use case for this particular person and this website that they're building. But I could see this as be a relationship thing. Yep. At least implementation wise, it's like equal, I would say. There should be no diff like, no technical difficulties to make it more advanced in a sense or more capable. Yeah. No. I actually like that. I I think, especially as we move towards, you know, our our our future concepts of we want to support non sequel kinds of things. This means, to some degree, direct us needs to understand where things are being used and keep track of that so that otherwise, we can end up with a, you know, an even bigger mess. Right? Sync beyond files to records to data and content. I don't I've been trying to think about this because this comes up quite a bit. There's there's also things like one of the things that I've seen a number of times recently is, can I tell what the file is associated? Can it can can you show me what else you know, where is this file being used? Right? So that comes back to in particular, this is problematic for things like where it's embedded as a text, you know, inside of a rich text. My brain goes, how do we we would have to, in some way, keep track of the relationals as part of Directus. Because I did some searches on, like, SQL databases. How can you do this in c e? Yeah. It's not really you you would have to pull up what are all of the foreign key constraints that go back to this table and then go search through all of those tables to find foreign keys with the ID, I guess. That's really the only way to do it. Yeah. It is. And you can do some view things. You can do but you have to you know, it's it's complex. It's not something that's just simple to say, where is this? But if Directus in some way was actually keeping track in some way of, you know, think of some kind of an indexing or a mechanism of keeping track that when it gets associated, we track that it got associated in some way. Right? Call it a table. Call it a The the difficulty there, of course, is that data could be live updating through all sorts of sources that's not direct us. So how do we keep that index up to date? Or is that something that we have to periodically re scrape to regenerate, which sounds tricky, or tricky to keep that, you know, consistent. Keep that keep that data integrity alive. This is also a feature request that immediately reminds me of be because of this connection, it reminds me of the one around, how do we warn people, about where stuff is used when you delete it? Because right now, you can set up the form keys to nullify a file, but if somebody deletes a file, they might not know where they're gonna be nullifying things automatically. Right? It's a very similar, where is this being used question except during delete. Yep. And that's the that's the common request is I you know, at least I would like to know where this, you know, where this thing is where all this thing is being used. You know, what what all is it associated with in that sense Yeah. For tracking it down? Because if I yeah. You know, I wanna understand the impacts of me deleting a file or me deleting a record, what are the Right. Relational impacts? Now looking up all those foreign keys is a pretty heavy thing. But in Directus, we do store that relational information already. So we should have the ability to say what are the relationships that have this collection in it, and then do a sort of aggregate search that says, okay. What is the count of items that have a foreign key to this item? Which should be a relatively e like, not easy, but a cheap operation to do rather. I mean, it depends on table size. There's always an asterisk, but we could, you know, do a what's the kind of items that are using the current primary key as a foreign key in the, related tables and then present that as well, right, where we say, okay. We know, this collection is being used in well, in this example's case, direct this files is being used in, photo albums. And therefore, we can say, when you delete this file, you're gonna set it to null in these in 3 different photo album records. Right? Is this something you wanna do? Yes or no? So at least be a little bit more helpful about that. Yeah. I really like the idea of, like, having a Oh, sorry. Oh, no. Go ahead. Go ahead, please, dude. Yeah. I really like the idea of having, like, a graph view. Like, just in general, that would show you, okay, this would be deleted. This would be, like, nullified. This would be this this in-depth, that happened with on a delete. And, like, the data is already there in the API in the app. We just have to then visualize it with, like, a graph view or a list of things that get deleted, a list of things that get nullified. We aren't we kinda doing something with creating when creating, for example, relational stuff, we already say what we create. Like, do you know this yellow text list that we have on the data data model creation? We can just do the same for the deletion process, basically. If you delete an item, go through the whole schema of relations and see what links to what, and that's basically just recursively checking against each nested collection. And the fancy part then would be how to visualize this. And in in terms of, like, do we run it in a list? Do we run it in a fancy graph where it's like, this collection links to that collection, and this field in that collection would then delete another collection or something like that? Or would we just run it as a list very stupidly? And to make that one slightly more complicated, what do you do if there is user input required? So, for example, if the default cascade is make it null, but the field is not nullable, then what? Right? Are we gonna Then you get an escape. Are we gonna tell them you cannot delete it because it's in use, Or are we gonna tell them, what do you wanna do with the 5 places where you're using this? You wanna replace it with something else? And is that something we wanna do in line? Or is it enough if we sell if we just say you cannot delete it right now, go fix the other stuff first? Like, which is also, you know, a flow that would be enough. I don't know if it's as useful. Yeah. I think at least indicated that it exists somewhere and then saying, go clean it up before you can actually delete the file. That seems okay. You could give the option to say nullify where I'm at or place with something else where I'm at. Interesting. Interesting. I'm I'm also wondering, is it is it is it not really, like, configuration error if you set the relation to nullify something that can't be null? That sounds like somebody has messed up things in that case, which we should also protect people from doing. But Fair enough. Yeah. That that is that is fair. But the the default behavior for foreign key like that is to prevent the deletion, I think. So even if you don't set it to set nil, but you just leave it at the default, which is prevent the deletion, it'll just throw an error. Because I throw right now, we don't throw a SQL error that says I cannot delete form keeps Yeah. Exactly. And then we should probably even, like, protect when setting up the relations that you shouldn't be able to set it to nullify something it can't be nullified to. Yeah. But I think it's all the settings. Even the the mistake setup and the the default case. There's a case to be made that we wanna have some sort of user input moment in during the delete that says, okay. We're gonna be either nullifying if that's the setting, or we're gonna try to nullify if that's the setting, across 15 items in your your 12 collections. Or if it is a, prevent the deletion, the question could be, do you wanna replace where this is used with something else before you can delete it? Right? That would be a pretty helpful thing. I know we're getting a bit of trend for this particular feature request, but these are all kind of part of the bigger part of the same, sort of questions and user stories. Agreed. And, again, I think he's got he's got some of those cases covered. They they've got some of those cases covered here. Right? So they know to delete the file, keep the file, prompt the user. I must delete it in the collection. Well, to to get it back on track for this particular feature request though Yeah. Because the what we just described is, you know, if you wanna follow just a regular foreign key constraint, and we wanna inform the user of what's about to happen, that's part 1. We wanna allow them to take some action if they have to make some changes for it to go through. But then the third one, and that's the new thing in this feature, requested the question, do we wanna do it the other way around as well? Where unlike in a database, we allow a secondary foreign key constraint type of thing where we say, if you delete the the the record that holds the foreign key, delete the related thing. And that then has a sort of recursive compound effect where it's like, do we now show any other foreign keys that might exist on the thing that you're deleting through the foreign key that you just deleted? What? Yeah. I think that's probably one of the reasons why, you know, databases don't sort of recursive, like, have that way of doing it both ways because that really feels like, you know, you're you're quickly ending in a in a in a sort of infinite loop type of deal. Yeah. And that might be where maybe you don't show them that. You just simply know that is still being so if they choose, say, the delete option to say, you know, I do wanna delete the file, or the the record in this case, then you could provide an indication that there are existing relationships. Are you really sure? And then it'll affect those other you know, it will affect other things, you know, and number of other things or just the fact that it exists, that it is actually related to other active items. So instead of and not try to show them what all because that could be a large number of things. I don't know that you'd want to try and show that in a dialogue box, but you could show this is related to any number of other things or this is related to other things. And therefore, you know, this could have catastrophic effects. Yes or no? And maybe even making an option that, you know, a toggle config flag or something on the settings of that says, if this is still related to other things, then they're not allowed to do that from a permissioning standpoint kind of thing. Then, you know, the option is, no, you can't delete this because this is related to other things. Go to the source record and clean it up and do whatever. Right? The whatever workflow. So I think you can go that route, maybe. On another note, I'm also wondering if we should that it's it goes in a similar direction, but, like, protecting the users from making mistakes, would be like a feature where you have a protected collection, that basically says, okay, you have to, for example, enter the, the name or ID or some type of validation before you can actually delete the, the item in the collection. For example, if you're managing users and you don't want to mess up have, your managers mess up the user's collection because that could be catastrophic or something similar, where they would have to then, for example, enter something. And that would also tie into this, like, what happens on the delete part where it's like, okay. This collection is related to other collection that is protected, and we should also, in this collection, mind you that you're deleting something from another collection that is protected. Right? Yes. Yes. Yes. Yes. This old man now this makes me think from a technical perspective. We're gonna dive a little deep here. But from a technical perspective, this makes me wonder right now, We rely on the database foreign key constraints for all this. Right? That we say, okay. You said it's a set null. That is literally a database foreign key index with a cascade set null. Right? Yeah. But that also means that from a Directus perspective, we're not triggering any logic or hooks or or anything necessarily on the Directus side for those nested deletes because we're just relying on the database to do it. Right? Which is a technically performant way, but at the same time, that does raise newer questions around, you know, if you have a flow that handles something on file, delete to clean something up. Right? And that file gets deleted through. Well, I guess a file is actually a bad example, ironically enough, because it's on the disk and not on the database. But if you have a flow that cleans up something, on a database change, but it is changed directly from a foreign key constraint that changes it. We're not triggering it as an event. Right? Yeah. I think that was also one of the problems a lot of people had with working with flows that it wasn't, like, cascading these, like, deviate events to, to flows, basically, I think, which would also solve that problem in a sense, I think. Although, I don't remember exactly what the problem there was in detail because it's quite a long time ago, which where I had read through these issues. But I do still like the idea of, like, this this type of protected collection. I I do think that could I'm not sure. I haven't seen many press on that idea, but I do think it could be a nice enhancement just from on the side of, like, making sure you don't do anything. But, I mean, aren't we doing something sim I guess, no. We're not. But, for example, on the director's collection, you could technically delete those from the app. Right? But we somehow oh, oh, at least Yeah. No. In a sense, we also do some sort of protection there because, like, yeah, sure. You can do that technically, but you really shouldn't. No. No. I'm pretty sure we're we're preventing any any direct to stable modification from the product itself. Yeah. Yeah. I mean, you can add custom fields and remove those, but you cannot mess with the the system ones, which I think is a separate feature request, actually, funnily enough. I don't know if we we I forgot if we did one of those, an episode of one of those, but there's a feature request to be able to update, you know, the locked system fields because some people are rightfully so saying, I don't use a description field on files. Can I just delete it? And I'm like, no. Right. Not at the moment. Well, I think it was that was actually a permissioning thing. They wanted to be able to not show it. Right? Be able to deselect that as a visible option to the user. That should be fine. So that there there were some key things there. That was actually related to the permissioning. I remember that one because that was I was like, oh, you can't. Different description. There are there are things that you can't remove from the user view. Right? So, again, we do lock the fields, and I think we should we should allow it to have these. But on the access policy side for that particular one, it was, you know, if I'm in the content manager policy here that called app access permission set has you can see your own user, but then that c includes some of those system fields that you may or may not care about. That's Yep. Yep. Yep. So that was all related to this of, you know, if I'm in my directest users on the permissioning side of the app and those kinds of things in the read permissions here. Yeah. Custom coming down into the field permission, I can't deselect. So if I don't want to show, you know, the location of the title or the description, because we're not using them or whatever. There's only certain things you can deselect from the key fields. So Yeah. No. That makes sense. That makes sense. That was a little bit. Let's not get I I recognize how bug big of his detour this is from what we're talking about. But I guess the other thing that would be nice, the the one thing that our our locks currently prevent is, like, reordering the fields. Right? So, like, we stick the roll and some of the configuration details at the very bottom. Well, what if I wanted to roll up at the top in the item form to see it Yeah. In in a certain way? I think our founders sells the topic for the next one of these. Yeah. Sometimes. But back to file delete. What was I taking notes on? Larger configuration. Now here's another very real question. I know there's, of course, enough upvotes for us to warn discussing this, but there's also a very real question of, is this something we want? Like, the whole flow of warning people for a cascade delete and doing it one way as per the database the way we do it now. Sure. That that feels like an obvious one. That that part, obvious to me. But this one is interesting where it's like I deselect a file from a relationship. Do I wanna be able to delete the file, or is that confusing? For content user for the user experience, yes, I would like that option because the user doesn't want to the the user workflow wouldn't necessarily be I now have to go to the direct to file library, find that file. Once again, how do I find the file? Do I remember the file name? Do I do those things? How do I go and find and now delete that file? That I think is the the key thing from a content editors perspective. Yes or no? Yeah. I'm I'm thinking about 80, 20 here we have to think about, but from a content editors experience, right, we find it we all find it very annoying if I have to go do 3 more steps to go delete something. Right? If I have to go now, remember to go back to the file library, go search and find the file, and then delete the file, only to find out that the file can't be deleted because it is being used. Or Right. Accidentally now not know that deleting the file has now removed it from some place that I didn't know about or didn't even have access to. That's the other permissioning adds another level of complexity to this. Right? So my ability to delete that file, well, it might be in use somewhere else. And that's another reason I think we shouldn't show the items or try to show items necessarily because they may not have access or permissions to where that file is being used. Mhmm. And so you get back to the concept of, well, how do I, you know, how do we deal with that? What do we what do we allow or not allow? I think it's a nice workflow, because commonly, right, it's not so much that we're I I think in most of these kind this this particular use case. Right? The file is only being used in the record that it was uploaded for, you know, you know, it's a gallery. It's something that they're doing in the, you know, the content users adding that for that specific thing. And then when they're done, they just want it cleaned up. We don't wanna have gigabytes of files laying around in our file storage and in our file systems that we're not using. Mhmm. Two questions. Just a thought. One one thought. Two questions. One technical interesting difficulty is nested relational staged changes. As in, if I'm on an item where the file field is required and I choose to delete the file instead of deselect, is that file immediately deleted, or is that file only deleted the moment I hit save on the parent that I'm clicking the delete on? I think that is that is a question. And that is mostly around if you're in content versioning, for example. Right? And you make a new version of an existing thing and it uses the same file. If I click delete the file in the version, I now destroy the published one. You know what I mean? So that file should only be deleted the moment I publish the staged versions item. I think that's an interesting interesting little problem. But then how is that presented in the UI? Because if you have marked the file as deleted and then you select a different one, do we keep that as a state? Do we show a little indicator that you have 5 things scheduled to be deleted? Or how does that work? I think that's a difficult question. And then That's how we're doing it. So that's how we do it today. Right? So we do show that indicator. Right? So if you go to I have that will work for that. On most of our relational kinds of things, some of the relational interfaces, we do indicate that you are deleting and adding. Right? So we're removing this. Right? We have that indicator there. We don't actually delete until you commit the record. Right? We don't actually propagate any of the changes until we I'm I'm supposed to think of it right in a in a file picker, a deselect and a and a delete is very similar. You deselect with the intent to select a different one. Right? But if I deleted with the intent of selecting a different one, now I'm effectively presented with 2 changes that I made. We don't really have a UI pattern for that, yet, which is something what was it to think about when I left the ad. It's just like a regular drop down. It's all the same same problem. The second thing that popped to mind, and this is a bit of a bigger just think out loud type of thing, would it make more sense for a use case like this to make your own custom direct these files collection where there is no relationship to direct to files. There's a relationship just directly to the file. So you would have to store some of the required fields for that to work, like where is it on disk, what disk is it on. Is that a thing? Because technically speaking for direct test files, the only required columns to turn that into file management would be what adapter is this on and what is the the file path to find it. Everything else is is metadata that we use to enrich the platform, but not necessarily required to do file management. Good. Then you've got to handle I mean, again, some of the key issues that we run into, right, with how does the file when and where does the file get uploaded? How is that managed? Or are you assuming that the file exists on that file system? Right? The the file interfaces do a whole lot of or the APIs. Right? The direct to these APIs do a whole lot of things for you. And we've, you know, some of those new enhancements around file streaming and restarting where you left off. And, you know, these capabilities, when you're dealing with files, it gets, you know, there's a lot of complexity there. And I don't think a lot of folks are gonna want to take that on as a I when I wanted to manage a custom extension that's doing these things for me or, you know, replicating the capabilities of the direct to SAP application. Flows is an option. Right? So that is something that there's actually a link. Someone else put a link in here on how they kind of configured a flow to do some of this. Right. Right. Down here. So this thread here, there's actually an example of a flow. Right? So we do have on hook. You can action based on file upload, based on item update, create, delete. So you can apply whatever custom logic. I think the main issue there is you don't get a user prompt per se in those cases. I have I have another probably probably a bit of side of like, side topic idea. That like, not not side topic, but, what is it called? Going outside of the the main road. We could have something like reference counted tiles where it's like, okay. This file exists as long as there are any uses of it. And when there are no uses of it anymore, it automatically gets deleted, which then we could also look into something where it's like, okay. If I upload the same image twice, we do, like, a hash of the image to compare if we already have that file somewhere, and some fancy fancy things, which in the end should just make it easier to manage all these files. Like, with 500 people uploading separate files, you get the files collection in, like, a giant amount of people having stuff there. And, like, a reference counter thing could just make it a lot easier to manage all these different file or, like, deduplicate files and get rid of, like, unused ones because that's a thing I've run into even by just myself using directors. Mhmm. Yeah. When you don't have direct file store access, which many times in SAS slash cloud slash whatever. Very interesting to start. So my brain is immediately in 12 places with that comment. But it's the an additional piece of functionality around relational data files and otherwise is the show me where the stuff that is not used anywhere. I think it's a very Yeah. That's a good point. Yeah. Right. And this is something on on a layout level where it's like, okay, filter down by stuff that is not used anywhere else, which is, it does make me think of a comment that was on the chat a little earlier from our our own team that was like, hey. I'm already looking forward to the technical complexity of tracking references within text and JSON blobs in a barrel. Right? Which is very, very, very, very, very, very, very, very, pretty true. But Yeah. And in that case, it definitely has to be restrained to things that, again, we can, like, check where it's, like, on JSON blobs or, like, repeat us. That will be very, very difficult and probably not feasible. But at least we, like, at least at least we make the option to if you want to have a feature like reference counted files, then there is a way to have it easily and without any manual labor, basically. You could still do a repeater, but then you won't have the fancy it's basically like the idea of a repeater. A repeater, you can slap on really quickly to have just any list of data, but it's nothing fancy. You can't fancy query that. I mean, now you can, but a long time you couldn't. Alright. Are we approaching a what do we think is actually achievable? What we would actually like it to do? Oh, what do you want? I love it. I'm I'm still a little bit in two minds of this whole going doing doing for keeping strange whole place. And and the main reason there is because it kinda strays away from the database as the source of truth for this stuff, which could be a good thing because it unlocks other things, but it's quite a big breaking change in how we handle relationships just in general. Right? Because right now, foreign keys and direct relations are 1 to 1 the exact same. And we're reaching a point now with this where we're saying, well, we'll use foreign key constraints on the database for performance reasons because they're they're indexed, but we will not rely on them for this sort of behavior on foreign key changes. So, like, for things like, I wanna delete the related thing when I deselect the current thing, that is not a database thing anymore. That's the directest thing. We're just fine. It's just a little bigger change. Or not, don't we have to anyways implement it in the future on the server side and not the database side on these constraints? Because when no supporting relations between databases, we can't have that anymore anyways. So we Great. Have to implement it ourselves anyway. So it's just on the road anyway. Yeah. Just a thing you have to pick up and then do, which is not as easy as it sounds. But Mhmm. So with that in mind, I think some of the stuff that we talked about around, you know, warning and informing the user when a form key constraint is about to delete a bunch of stuff or set null to a bunch of stuff. I think that's an obvious thing. Definitely, it's something we wanna have. That would be a big quality of life improvement. I think doing the relationship the other way around, I think if something happened, it'd probably be a longer term thing just because it is a very big breaking change and how we handle relationships today that I wanna do a bit carefully and just more more planned out. The whole idea around custom tables for direct to files, I'm not feeling that one. I think that was a oh, you know, a bit of a brain fart. Just thinking that wrong. That's what these specials are for, but it doesn't feel like the right move right here. Because of all the the system logic that's going on under the hood, we we have to keep that in in control. What else was on the was Jonathan? You've been taking notes furiously. What am I missing? I'm trying to keep up some of the workflow options, relationship existence, tracking kind of graph style or being able to understand the that was one thing that Nils mentioned just from a UX perspective. I think that's that almost feels more like the admin side and the data model having ERD style visibility and understanding that, oh, there is a relationship to files from x number of collections and being aware of those things. Taking it away from, you know, embedded inside of rich text or repeaters or weird things, but just the actual ERD, the understanding the graph of what is actually related to my content. From an admin perspective, that would be useful. I do really like the idea of at least understanding files is one of the most common things that I see around this, but we can also think, I was talking to Alex and, Antonio on our team this morning. They mentioned that, you know, many to any is another one that has this kind of weird thing where there's not an actual relationship. We do manage that in app. It applies to many to any though in the sense of I can go delete a component that might actually be in use somewhere and you don't get a warning of, hey, this is, you know, you you're you're impacting other things, or impacting something. Deleting it from the relationship. You're not deleting the related item as of today rather. Like, that would be a similar to your point though. That's a similar thing with this feature request where if we add delete as a proper delete the related thing, then you're in a whole new world of how do you display that and how does that work? Does that happen to you sync or do the hood or yeah. And I think a lot of this is probably considerations that need to be handled as part of, you know, the new abstraction layers and things where, as Nils mentioned. Right? Longer term, this is not going to you know, as we move to data federation next year, we're gonna need to be able to handle these things as Directus. Right? Understanding that now it's not just it's data in 2 different databases, right, that we've created a virtual relation for. We need to understand and track, I think, against I I think as you've said, isn't just a files thing. This really becomes a more kind of global direct us handling logic around how content can be connected to one another. Right? I'll take relationship out. It's just it's connected. It's there's there's some form of tracking that needs to be maintained. And I think as as you said, one of the problems that we have today is, well, if someone's doing stuff direct sequel, you know, we try to support all of that and make sure that we're aware of that. But I think as we move towards, you know, as you move towards something like this, well, if you choose to create or do things under the hood, you may not get them. It's the same thing that we do with activity and revision logging. You're not gonna get that. If you're doing something outside of the APIs, you don't get those benefits that exist within the Directus application. Now it could be that there's ways to crawl and find that they've done something and add that or indicate that there's something that then needs to be configured. But I think as we move towards Directus doing some of this logic in the abstraction layers, it's going to require that we handle these use cases in a in a comprehensive way. Mhmm. Cool. Well, I think that probably concludes our our little discussion here around feature request 17 853. Any other last closing thoughts here, Neil's, Jonathan? Nope. Awesome. Well, cool. Well, thanks everybody for tuning in. As per usual, this will be up on direct us that a o slash TV. I hope you tune in next time about something else. Interesting, I'm sure. But until then, thank you for watching. Like and subscribe. Subscribe. Thanks, team. Get in. Cheers.","b7827b99-c9e6-4cd5-8e67-6331a3670b63",[1783,1784],"9d8800a8-d3c8-4530-bdee-c67734a7cffc","4497c8ce-8792-4586-b4d1-7df91c3b4b02",[],{"year":978,"number":11,"id":1787,"episodes":1788,"show":1801},"6aa046f1-bd53-4510-9af0-c0f3daaf4415",[1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1768,1800],"daed2c08-703a-43d6-ac97-aacac61be4c0","86fa152b-6a8b-477e-94b5-bd91e1202d21","0b5f4343-1494-455b-b41a-25811c151242","b2b01569-d8c6-49a7-adaa-429fe84f204f","b63afbe1-6418-4e9e-b1da-4890979789f0","69ad81e8-5e1d-4b85-9fa9-3b767a3a3478","5c9c888c-f527-4608-a2f7-56f156d00980","243daa59-3772-4ebe-b212-c2a09a4a0b71","d66c1e46-cc57-49fe-a914-2e440bbc1576","12c8f72d-22fa-4ffa-a9d1-57047216fd1a","8896c934-aa2c-43b6-9342-8275682ab8b2","044b7c89-aaec-43b2-9d6d-6743a0fb5afd",{"title":1802,"slug":1803},"Request Review","request-review",{"id":1800,"slug":1805,"vimeo_id":1806,"description":1807,"tile":1808,"length":687,"resources":1809,"people":12,"episode_number":675,"published":1777,"title":1812,"video_transcript_html":1813,"video_transcript_text":1814,"content":12,"seo":1815,"status":19,"episode_people":1816,"recommendations":1820,"season":1821},"20594","1013663375","In this recording of our live event on September 12th 2024, Nils, Jonathan, and Hannes discuss Field locking and notifications.","18984108-11ac-467c-b8a1-44c264c8dac4",[1810],{"name":1775,"url":1811},"https:\u002F\u002Fgithub.com\u002Fdirectus\u002Fdirectus\u002Fdiscussions\u002F20594","Field Locking and Notifications","\u003Cp>Speaker 0: Let's rock and roll. I think this fundamentally comes down to something that we've been talking about off and on for a couple of years now is the ability to have things like collaborative editing, at least some level of notification somebody else is working a record as our as we all as as we all here know very well that it's a first save wins at the moment, and you have no idea if anybody else is editing a record that you happen to be editing. Generally, it works out. I don't think it's a frequent occurrence, but it's probably it is something that I think we would all like to see solved. And I'm hopeful that I think I see BrainSlug is in the audience as well.\u003C\u002Fp>\u003Cp>That's good because I think he's actually got a custom extension that actually uses websites to actually do this, for, like, live chat and other things. But I think he actually did something as a test, and we'll we'll get through that. But that's what I think this generally is, and we'll get this thing kicked off. I'll turn it to the brains of the operation, and let's roll through this thing. Shout if you need me to show something or pop over to somewhere, and we'll go.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Tim is disappearing. Yes. I think\u003C\u002Fp>\u003Cp>Speaker 0: this was\u003C\u002Fp>\u003Cp>Speaker 1: actually a fairly easy feature request, you know, in of itself if you don't consider, like, all the possibilities you could evolve evolve to. Because in the end, it's just asking for, hey. We need a notification if someone is editing something, and that doesn't really it needs some kind of real time possibility, but it doesn't really need any collaborative editing efforts. It enhances it by already. If you put where WordPress does as well.\u003C\u002Fp>\u003Cp>Haven't used WordPress in a long, long time, but I think it had something to do with where I told you, hey. This is, this part is embedded by someone else, which is basically what this feature is asking for, just to give them heads up and just to improve the experience. But I think the author of the Hippo Trust, should also be in the audience. Maybe you can raise your hand or something, say hi, to see what you envisioned there. Oh, yeah.\u003C\u002Fp>\u003Cp>There he is. Because I know it's, like, just reading it, it seems like a fairly easy thing to implement and to consider. It just we just need to figure out, like, what are the extended use cases we wanna have with it. Is it enough to show that occasion as a starter? Who we able to wanna evolve it into collaborative editing and all of that?\u003C\u002Fp>\u003Cp>Because we already had a discussion on real time. I think I just went through a couple of discussions almost October last year, so there have been some discussions about that in general as well. And this feels like an obvious feature to merge into that as well. But I see that Uwe, Uwe. So, excuse me for butchering your name.\u003C\u002Fp>\u003Cp>He's typing right now, and he has some additional insight that he collected over last year that this feature request has been up and being voted by the community.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. And and to just give a bit more context from what I remember, I think this was, like, in the talks. Like, then when we started, like, working on WebSocket support and stuff like that, that's was, like, one of the, like, key things we looked at for, like, WebSockets and stuff. But as you mentioned, like, it doesn't really necessarily need WebSockets. You could also, like, pull, like, a get request each second in if you would want to go without WebSockets.\u003C\u002Fp>\u003Cp>Like, there's always, like, the possibility to implement this. It's just, like, about what do we want to have this as a real scope in terms of, like, we just implementing this is probably there's more to it in the end, basically. There's more we can do, more we can, like, find out before we go implement this feature. And yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Also something to consider is can we implement it as an extension? Does it have to be in core, or is it something that could be a presentation interface, for example, which can, utilize the real time WebSocket API to figure out, like, is there anyone else editing it right now, maybe through a system collection or something that, along those lines where you write an item as soon as somebody is there and you kind of keep it alive, had it write a time stamp when it was last visited or something, and then you could figure out based on that if there currently is one editing it. I can certainly see going that route with an extension as well. It doesn't really need to be a core feature even though it could be nice to have as a core feature. Tim said that one thing he recalls about being difficult is that what do you show about the other user if editing, what the other user that is editing it?\u003C\u002Fp>\u003Cp>If you don't have the permissions for other users, I think you would just say, hey. There's one other user editing or something. It doesn't really it's nice to have a name and to have a thumbnail and profile picture and all of that, but it doesn't necessarily need it to function in the 1st place, I would say.\u003C\u002Fp>\u003Cp>Speaker 0: So that that was the question that I just wrote down is what's considered editing. Right? Is it just someone has the item record open and they have edit permissions, and therefore that's considered editing or they've actually made a change and that's considered editing. Just something that\u003C\u002Fp>\u003Cp>Speaker 1: Or step in between is have they focus on input, for example, if like a step towards editing.\u003C\u002Fp>\u003Cp>Speaker 2: Yep. Yeah. That's just another thing we, like, probably have to, like, decide on first for something like this could be implemented because that is like a known edge case. And, like, for example, if we would go with the locking route, where, like, an item gets locked if a user edits, then the real important question is, like, with that system, somebody could just, like, lock us, like, an item forever and not release it, and at any point of time in the future, and that would be a big problem because then you have, like, oh, my my colleague from work is not, giving me access to this item right now. I can't do my job, and that that is not an option, really.\u003C\u002Fp>\u003Cp>Speaker 1: So RCS, which is an ancient, vision control version control system from, like, the eighties, like, eighties, nineties from Unix. And they have this concept of locking a file. So if somebody locks a file, you're not able to edit it anymore, and you can, like, force unlock a file. So they certainly use that possibility as well. But Right.\u003C\u002Fp>\u003Cp>There is always gets overtook this kind of locking approach, where you're trying to negotiate and take these afterwards. I mean, in this case, I think, as well as the author of the PR, feature, I guess, sorry, said the notification itself should be the better option. Also, seems I wanna say simpler implementation wise because both of them kind of require either, some state on the server or requires some, entries in the database. Having the item page feels easier.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Because you also have so as as Mills mentioned, right, you have the ability to potentially so we got a lock record route then how long do you allow that lock to remain held? And you automatically time it out and just, you know, that person loses their edits.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: You know? Or is there an admin way to force specific out of whack and now you got a record block that can't be accessed by the users in any way, shape, or form, even the original user that, you know, initiated the lock. So that means we need some admin, ways of managing and releasing force releasing and resetting locks.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, Pedro brings up a very good point. We we have relations in directors, so you're not always just editing a single item, but you can basically walk a whole tree of changes and go 7 drawers deep and start editing, items that don't that aren't actually your item page. That is a very good call, which I haven't considered myself so far.\u003C\u002Fp>\u003Cp>Speaker 2: That that's yeah. And that in that sense, locking will probably be, like, very difficult to implement because then the question like, the real question is what even do we lock? Not only, like, when do we lock, but how and what and why. So that's way more tricky than just giving you the information of somebody. Like, in a perfect world, we would have to, like, live collaboration that basically solves the problem out of the box, but that's not really feasible, I think, at the moment in the core implementation of our app.\u003C\u002Fp>\u003Cp>So, like, some way of not like, not an either a notification bar or, like, a, like, a schedule to request to see if there's somebody editing your current item and then update it or something like that. That would be like a, I would say, trade off between what we can do right now with your time and what should improve the experience by, like, the minimum, at least. Like, the minimum, of, like, to yeah. Basically, to have some way of knowing that somebody is working on this cost right now, there isn't. I mean, I guess there is on the user because the user table has the records on the last edited item.\u003C\u002Fp>\u003Cp>So there is currently already the information stored in directors.\u003C\u002Fp>\u003Cp>Speaker 1: Well, the last page, which, gets terribly confusing if you have multiple tabs open. Right? And as soon as you start True.\u003C\u002Fp>\u003Cp>Speaker 2: In that case yeah.\u003C\u002Fp>\u003Cp>Speaker 1: The information gets up because that's a single entry.\u003C\u002Fp>\u003Cp>Speaker 2: But that would be, like, I would say, like, a good start to use that information that we already have to work on that week. Although, we if we were to, want to go for it, we could also make it, like, fully fetched then. Yeah. That's, like, that's also true. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: The problem with the last page is, like, it isn't very it it isn't really, like, a secure information on what is the user what the user is currently doing, because they might open 1 page, might open another page, and it not, like, might open the item page and then tab out, go to a different tab, and navigate in there. And suddenly, you lose all the information because it's really only intended as a way to basically bring the user back to the last page they open once they log out and log back in. That is the intention of page, to have like a more fluent experience when you revisit the same director's instance. So using that really doesn't seem like the right option, but Tim linked his experimental, live collaboration interface extension, which live collaboration really is a big word for it, I wouldn't say, because right now it's just a presentational interface that can notify you if somebody else is, working on the same item. And I just peruse that the code, Tim, please correct me, but it seem it seems like it's using our real time WebSocket, and it just sends custom messages over it.\u003C\u002Fp>\u003Cp>I don't know if it needs some does, the WebSocket implementation automatically fan it out to all connected clients, or is there some sort of a component as well in there that is necessary? I'm not able to use that one. I'm thinking, like, not while doing that, let's read some chats. Joshua, she's she's it suggests lock timeouts.\u003C\u002Fp>\u003Cp>Speaker 0: It's\u003C\u002Fp>\u003Cp>Speaker 1: certainly an option, 100%. Still leaves you with the possibility. What is a lock time out? Is it 5 minutes? Somebody might be editing longer than 5 minutes.\u003C\u002Fp>\u003Cp>Is it an hour you might wanna add it to an item sooner than an hour after your colleague left? So if we were to go to the lock route, there certainly has to be a mechanism for you. Yes. I'm sure I wanna override this lock. I wanna take the over this page, mechanism kind of.\u003C\u002Fp>\u003Cp>Yeah. I'm with, Pedro's on this, notification that is shown once the user starts editing a record should be great, And it's just a simple avatar of as a fallback, as we said, if the user currently editing the item doesn't have any information about other users or something that doesn't have the permissions.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. And another thing I am wondering right now, or thinking about is, regarding where we would want to store that information, who is currently editing what item. We could, like, store it somewhere in the user collection. We could store it somewhere on the item. So what I was thinking, for example, would be a custom interface that basically makes a request to the, API, on, like, somebody opened this item, a request goes like, because interface interface gets loaded, the interface lets the API know that, like, the value of this field gets then sent to the user's name.\u003C\u002Fp>\u003Cp>And, others visiting the same page, the basically, the data gets fetched from the API. And in that field will be the name of the current user who's editing that item, and that interface could then display that information. And that would also, like, require no real time WebSocket connections, and that would also be a very straightforward in that sense that it is very similar to the created or updated on or modified on, interface, which, like, could also make this as an extension because then we are not like, we can just flop, I mean, you're gonna have to edit your schema for that feature to work, and it is not like, it is basically an opt in feature in that case. But it would be very straightforward to implement even as an extension, and that could also be another interesting approach to this problem, basically.\u003C\u002Fp>\u003Cp>Speaker 1: So, basically, either extending your current items to a user, or you can even introduce a whole new table that is just a, a lookup table kind of, like, this user, this item timestamp could work. And then, basically, once a user opens the page, it looks up, and the user would have to refresh their page in order to for that information to update. Right? So it wouldn't live update.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. If somebody would leave it and would set it again to null, it wouldn't like, it there's no live update, but it is it allows you to give a get a hint of or if already somebody else opened it recently, basically. I think it\u003C\u002Fp>\u003Cp>Speaker 1: should be\u003C\u002Fp>\u003Cp>Speaker 2: This quite, like, quite nicely solves the problem in a very stupid way, I would say, and, like like, a very straightforward and simple way. And that might already be enough for, like, most people who want to have this feature. Although, like, the big drawback of this still is that you're basically editing your schema, and that's in that point, and that like, it is an opt in per collection and not just something that works out of the box, except you usually would create a separate table for that. Then it would be for all collections at once. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: That is actually fine because you can just add a new field. It doesn't have to be a presentational field, but you can add, like, a\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Exactly. Yeah. We can just add a field that's a string, basically, and that gets set by the interface to the user's name who last opened it if there's none already in it. And that kinda could, like, trick around, like, real time and stuff and just, very, like, get to the point of there's just a name in there and or maybe concatenate even multiple names to each other.\u003C\u002Fp>\u003Cp>Speaker 1: The field could be reset. So I would I wouldn't just save the user, but it would also add a current timestamp as well. Okay. Just just for the problem Tim mentions. Like, for example, if your browser crashes, like, for example Yep.\u003C\u002Fp>\u003Cp>Normally, you would update the field actually with real time. That is kind of a necess null. That isn't really a necessity since you can send out beacon requests. I don't know if beacons can be post requests. I'm not a 100% sure, but there's a mechanism in a browser that is, like, even if the tab closes, please send out this request to this URL, which would kind of allow us to reset this field whenever a top uploads.\u003C\u002Fp>\u003Cp>It just leaves us with the additional problem of if your router crashes. Yeah. They are risky. They're not guaranteed to be a little bit all of our shenanigans that, time attribute, basically. Basically,\u003C\u002Fp>\u003Cp>Speaker 2: that's what we've got in the browser, I would say. Yeah. Regarding that disconnects, yeah.\u003C\u002Fp>\u003Cp>Speaker 1: WebSocket, but then you would have to go browser, like a module sorry. A bundle route where you also have some server side thing. Because right now we are just thinking, of terms how to implement it in extensions. But certainly, if you're going for implementing any core, there's more possibility to detect if a user has left a page because we can just continuously, we could just continuously monitor which WebSockets are connected, and we could send live updates as soon as the user navigates from there. So very certainly, it was a bit I'm just trying to, like, imagine if there's a possibility to do it, not in core so we can fast track it because, as you know, we have a director's left extension program, where we have extension experts currently developing a lot of features that are that have open feature requests, for example, but never get never really have been implemented in core so far, which is kind of like our experimentation program for figuring out, hey.\u003C\u002Fp>\u003Cp>These could be features that could let into core, but let's see if we can use bring them to users quicker by, getting the extension route just because as soon as you'll have a feature in core, you we always wanna make sure it works for 99% of the users, maybe not 99, but a lot of the users. So the process of bringing something to a core always, as you see with all these feature requests, is a lot more involved than just dropping quick extensions saying, hey. This is something experimental.\u003C\u002Fp>\u003Cp>Speaker 2: That's correct.\u003C\u002Fp>\u003Cp>Speaker 0: It's actually a trigger for me. Alex Vandervalk and I created a we've we had a use case from a client where they wanted to be able to do some relation they wanted to be able to do some enforcement on, or they wanted to know that someone had a current, like, project design. So or a technical design document. It was the one that they were working on right now. And so they wanted to because of that, they wanted to auto filter for those things and be able to indicate that for the user.\u003C\u002Fp>\u003Cp>It also then did relational enforcement, through the relational hierarchy of the technical design document, where you had software components and relationals and many to any's and all kinds of things going on. So what we actually did was we used because Directus is actually always you know, the the Directus application is always watching, you know, user actions and saving last page and all those things. We actually intercept that as part of a flow operation, And we if they change to a different technical design document, we auto update the current design document so that it it does the relational enforcement through the permissions and things like that.\u003C\u002Fp>\u003Cp>Speaker 2: So very weak.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So this this actually does that. So this flow actually so this is kind of along Nils' lines of if you add a field or you add a collection that you wanna keep track of and that, you know, provide notification to the user, where you do those kinds of updates. Then in this case, we're actually updating. In this case, it's the direct user table.\u003C\u002Fp>\u003Cp>We're setting that to the current TDD that they're working on. So when they change TDDs, it automatically updates this, and therefore, we have that field throughout the relational enforcement. We've got that field, you know, and we can then with many of the filters or your field filters or your policy filters or whatever you wanna apply, we can then enforce that you can't add software components that are part of another TDD. And it was one of those kinds of use cases that we're actually solving. But I think it actually, in some degree, fits this use case as well.\u003C\u002Fp>\u003Cp>Speaker 1: That still leaves you with, like, the edge cases of multiple tabs, which wreak havoc with that or could potentially wreak havoc with that. Because right now, we don't really have, like, a possibility to try.\u003C\u002Fp>\u003Cp>Speaker 0: Do a bad job. It's actually gonna just do it based on whichever tab you have open and when you're navigating back. Yeah. The the multi tab is a problem anyway. Directus does weird stuff with this particular page save issue anyway.\u003C\u002Fp>\u003Cp>I've I've learned never to have multiple tabs of a directus instance open because it just it will wreck havoc on your stuff.\u003C\u002Fp>\u003Cp>Speaker 1: I love do a duveis or however you pronounce it, please let me know. Work around right now. They basically have a dedicated Slack channel where they message, I'm editing this, hands off.\u003C\u002Fp>\u003Cp>Speaker 2: I love it, damn that's most straightforward, just do it solution, I love it.\u003C\u002Fp>\u003Cp>Speaker 0: Out of band, out of band, SS7 messaging. Keep your hand up, miss.\u003C\u002Fp>\u003Cp>Speaker 1: That's beautiful.\u003C\u002Fp>\u003Cp>Speaker 0: I think\u003C\u002Fp>\u003Cp>Speaker 1: we have a solution. I had it short here. Thank you for attending.\u003C\u002Fp>\u003Cp>Speaker 0: So so you could use the flow in this case to do that. You could actually automate that and send this send the Slack message to say, hey. I've got this item open. Now, again, it comes back to, am I editing it, not editing it, that kind of thing. But Yeah.\u003C\u002Fp>\u003Cp>You could at least have a flow so you don't have to be the one going and typing in the channel that I'm in this record right now.\u003C\u002Fp>\u003Cp>Speaker 1: Coming back to, like, how it actually could be implemented, I think there is a viable route to have this implemented as an extension just because, we provide you with all the necessities to do it. We there is real time once you enable WebSockets. So you could potentially even life update the like, life update in a way of, the user who was currently editing the field just left, and you basically subscribe to the changes on that item if you go with the route to have it on this. On one eye like, on a, on the same item or if you introduce, like, Joshua or like I've been talking about, like, a separate table that contains all the locks or not locks, but the current edits or the user presence kind of, I think in collaborative adding new all these presence indication, which basically tells you like this user can get this item, which also allows you to have multiple, which also allows you at some point oh, yeah. Because you would have to so thinking out loud here.\u003C\u002Fp>\u003Cp>Coming back to the problem that, Petros mentioned, we have relation items. So we we can traverse an item to different items. And as soon as all of them have this tracking interface, all the necessary, hey. This item is being edited notifications would be there, and they would automatically, oh, they would automatically be gone. That is might be a problem.\u003C\u002Fp>\u003Cp>Well, anyways, so the the there is, like, a possibility to have that as an extension. That's kind of what I'm trying to say. And, yep. Because I'm kind of having\u003C\u002Fp>\u003Cp>Speaker 0: things for relational, so it's just does a relational edit because just because I'm editing this item doesn't mean I'm editing the relationship right around Oh,\u003C\u002Fp>\u003Cp>Speaker 1: yeah. No. No. No. They they would, for example, be loaded as soon as you start opening a drawer, for example, that interface would also be loaded.\u003C\u002Fp>\u003Cp>It knows where it's knows\u003C\u002Fp>\u003Cp>Speaker 0: and it would do the update and tag that. Yeah. The\u003C\u002Fp>\u003Cp>Speaker 1: only problem is the only problem is with that, you would typically use an unmount trigger or something. As soon as that interface is unmounted, you would say, hey. I'm not editing this item anymore. But if you're editing through another item, all those changes would be staged. So if you close the drawer, it actually would tell somebody else, hey, it's not being edited right now or anymore.\u003C\u002Fp>\u003Cp>But in fact, it is because in fact, you just made an edit and staged it, and it's only gonna be committed or saved to the database when you save the paradigm.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. That's a good point. With relational data, that that would be tricky to get right in the yeah. Although, it like, at least the the the the one way, the the setting, the username correctly works on relational items if you go for the route of having a field on the separate item, like, on each collection on based on adding a field for tracking, who's currently editing what.\u003C\u002Fp>\u003Cp>Speaker 1: But when do you, like, release that information? When do\u003C\u002Fp>\u003Cp>Speaker 2: you release That's a good question, which I not yet know the answer to.\u003C\u002Fp>\u003Cp>Speaker 1: Like, you would do it on an unknown trigger for the interface or something. Right. Tim says the way he's done it in his, experiment extension is that he only notifies if it has been saved in in the meantime so people can concurrently edit it. Ah. Not that they just notify, hey.\u003C\u002Fp>\u003Cp>This has been saved while you are still on this page, which certainly is an option as well. Just doesn't feel as nice, but it feels a lot simpler to implement with some resolving options. Okay. So, like, hey. Take my changes, override it, or take their changes kind of deal.\u003C\u002Fp>\u003Cp>Right?\u003C\u002Fp>\u003Cp>Speaker 0: Which is commented on the version control. Right? As the you get notified that someone else has made a change, the changes get merged into you can either choose to merge those changes or throw them away and lose out.\u003C\u002Fp>\u003Cp>Speaker 1: Which is more along the lines of, like, resolving a conflict and not really and notifying you of a conflict ahead, but not trying to prevent the, conflict before it actually happens, I guess, which is, like, true process or the same kind of thing. I'm honestly divided on which one is better. It won't be, like, trying to prevent the conflict before it's happening. It feels like the nicer user experience to me.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Like, I I would always go for the user experience, though, in that you want to know in advance that somebody's editing something, not when somebody then actually did their changes and saved them. Because then you can, for example, communicate something with somebody who's working on the same project, on the same item in that sense. Are you editing fields that I'm editing too? Yes.\u003C\u002Fp>\u003Cp>No. And depending on that, they can either just continue working on it together or, like, wait for that other person to then actually do the changes and then the next person. Basically, they have the option of, do we have to do it in series, or can we make it in parallel, basically? Like, via Slack or something, like, like, basically talking that out. At least they know that somebody has had has the intent to do changes on this item in the future.\u003C\u002Fp>\u003Cp>Speaker 1: There is a Twitter account well, x account, whatever, that explores hideous, design decisions, UX design decisions. We could also add a button, boot this per pay $5 to boot this person out of that item or something.\u003C\u002Fp>\u003Cp>Speaker 2: I like it. Though yeah. That would be another option instead of going like the okay. We do it automatically when the user joins in these, we make it like an basically, like an option where it's like you opt in now to mark this item as you're currently editing, or you can leave it if you maybe, for example, like to even make the opt in optional in a sense, which is, like, even then more work to do. And it's probably nicer to make it automatically set to the current user by when editing.\u003C\u002Fp>\u003Cp>But, again, like, that opt in makes it also that you have to opt out of it kinda thing where where if it's automatic opt in, you expect to have it automatically, like, put your like, basically know when you're done editing. But if you manually opt it in, then you also have the expectation of you have to manually opt out again of editing this item, if that makes sense.\u003C\u002Fp>\u003Cp>Speaker 1: It might be automatic. Otherwise, it's just a hassle like an introduce additional hassle and nobody will start actually.\u003C\u002Fp>\u003Cp>Speaker 2: I mean, it's it's less of a hassle than going through Slack conversation and seeing if somebody has the same item name somewhere.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It's still it's still not as nice as it could be if it's automatic. As that said, editing some editing field should be disabled for others, which introduces another possibility or option or\u003C\u002Fp>\u003Cp>Speaker 2: That would be possible through the field by, making filters. That that is if the if there's already text in that field where the name would be saved, that all other items are although that would make it different good for the person actually editing it themselves because then these figures would probably also apply for the same person. Although maybe you can fill it out by the user username and the username matching the in the field. I'm not sure how how crazy good our filter or, like, our, things for that are are that might also work, like I'm not sure.\u003C\u002Fp>\u003Cp>Speaker 1: I was actually thinking more along the lines of enhancing the notification, possibilities that basically saying this field has been modified by another user. So I have it in line with all the fields and have a notification for all the fields that have been edited in the meantime, which certainly is possible again with real time. It might introduce another permission hell because people might not be able to see some fields. And if we just send them over WebSocket, anybody can read it, which, yeah, introduces problems again. But from UX perspective, again, it's super, super nice to have the possibility to, being able to tell, hey.\u003C\u002Fp>\u003Cp>They edited the event name. They edited the Slack. They edited whatever, as an enhancement to the original request of just shown in banner on my like, for the full item, which then again already puts you more into the collaborative editing experience where you have presence markers on all your fields and, know who's currently doing live edits on the page and seeing the edits and all of that, which is would be amazing to have. But as you already said in the beginning, it's a long ways away with the current architecture of, directors just showing you what's in the database because effectively, that's just what we're doing. Right?\u003C\u002Fp>\u003Cp>As soon as something is in the database, you see the most recent data, present in there. We don't really have any mechanisms on top of that for distributing life changes and all that that aren't already committed to the database.\u003C\u002Fp>\u003Cp>Speaker 2: Oh, another interesting approach regarding the like, when is a user done with editing an item? This is, like, a crazy solution and probably not really feasible. I still want to go with it because it sounds quite fun. Instead of subscribing and unsubscribing from an item, we could go the road as long as a user is continuously pinging an endpoint, that he's currently editing. This was which also tackles the who which tab is currently open because only the active tab will then ping that endpoint basically ping a certain endpoint to let, the basically, back end know who where the user is currently editing something.\u003C\u002Fp>\u003Cp>And we just do it like a automatic opt out after a minute of not receiving a ping from that person. That would also be another interesting approach regarding the when is a user ready like, done with editing an item or something like that. But also that that also has edge cases in a lot of senses, like, in a lot of ways.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Also, you can, like, you can do that through WebSockets as well since you just\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Track. I mean, yeah. I'm sure. I'm just thinking of, like, would it how tricky would it be to get this working without WebSockets?\u003C\u002Fp>\u003Cp>Because that's like, WebSockets makes it also a bit more, like, advanced in a lot of sense. Like, the the I I think we have, like, a ton of good documentation on how to do rest request with like, indirect us with extensions, but on WebSockets, I don't think there's a lot of documentation yet, which makes it difficult for, like, community members to implement features like that. There's always a way, but it's a lot more tricky, and that's I would assume. I'm not sure yet though.\u003C\u002Fp>\u003Cp>Speaker 1: We put a couple of guides on how to use the real time SDK, which provides you with a lot of options to use that already in base starting point of Tim's extension as well, for example.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. That's that's we have Tim's example. That's a good starting point.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. But yeah. So I think we agree it's a very good idea. We just aren't sure if it should be in core or if it can be extend an extension because if it can be an extension, let's try to go throughout first and then see it where it breaks and if there are things that have to be implemented for, just because of this many possibilities and flavors to implement this. And if we wanna tackle it, I feel like we should put it in line with presence indications and collaborative editing or at least some way of collaborative editing experience, and that is, like, the next step kind of.\u003C\u002Fp>\u003Cp>And I don't know if you wanna have a future in core which will be replacing the not just in future, for example.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Sure. Not no. Yeah. Right.\u003C\u002Fp>\u003Cp>In yeah. I I am definitely on the side of, like, let's make this as an extension, and then people like, even people can go with different ways of implementation depending on their preferences. Like, maybe somebody prefers the way of doing it per item where it's like, we add a field to a collection, and that field stores the current user who's editing it. Maybe some people would prefer that solution, and there would be may maybe somebody else implements another solution where it's stored in a separate collection or where it's done on the WebSocket depending what the requirements or, like, where the interests are. And, having this an extension basically allows us also to explore what people prefer as an implementation and see, like, what implementation of this gets favored over time, and also helps us guide, like, in the next iterations of directors to to find out the right way to do this basically in advance.\u003C\u002Fp>\u003Cp>Speaker 1: Which is the exact pitch for directors' lab extensions, I wanna say. It allows us to experiment with features to figure out, like, what is correct, what sticks, what are the things we wanna see, once they might be coming to core. Another great example is the command palette, etcetera, which has been released a couple of\u003C\u002Fp>\u003Cp>Speaker 0: weeks ago.\u003C\u002Fp>\u003Cp>Speaker 2: I wonder by whom?\u003C\u002Fp>\u003Cp>Speaker 1: Because that explores a lot of concepts of, global extensions and everything that can be implemented in core at some point. But for now, there's ways around it, and we can use it to prototype features, and we can use it to prototype\u003C\u002Fp>\u003Cp>Speaker 2: stuff\u003C\u002Fp>\u003Cp>Speaker 1: like the editing notifications, for example, as well. Jonathan, you've been terribly silent while we've been chugging along here.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. I'm just trying to keep up with you guys from writing notes over here. I actually like I I like the VIM style option that you guys have discussed here where you would actually say, just add a toggle at the top, and you can do this in your current data model. Right? You can use roles and permissions that restrict its read only unless you've actually toggled it to say, I wanna edit this item.\u003C\u002Fp>\u003Cp>Therefore, I can put the notification and update the notification to say, this user is currently editing. So you could have the flow and the hook and, extensions doing those things. But simply adding a field to the top of your item that says toggle on or off that I'm updating. Right? So VIM, you know, d I, whatever, you know, Linux editing editors who have always been that way where if I'm updating, I have to actually explicitly enter edit mode, and then I have to explicitly exit edit mode.\u003C\u002Fp>\u003Cp>So as long as I'm in edit mode, I have it locked. Now that means that I want the ability, you know, to give a flow manual button or other things where you could say, you know, I and it could be a flow button, right, that just says, I wanna enable edit on this. Therefore, send notification to the team that also is responsible.\u003C\u002Fp>\u003Cp>Speaker 2: Right. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: You'd send an email. You could send an in app notification. Or, again, just update another field next to it that says, I'm editing this. Therefore, if the administrators need to go in and override you, they could. But at that point, you can use role and policy permissions to say the policy permission is gonna say based on the toggle being an edit, I now have edit permissions on the appropriate fields that I'm allowed access to.\u003C\u002Fp>\u003Cp>I can make my edits, save my changes, and you could even make it so that if on save, if you wanted to, right, make your edits, if on save, that automatically toggles it back to unedit. Right? So you could do something like that for the users, and you'd have some control over the workflow there. So I like that idea.\u003C\u002Fp>\u003Cp>Speaker 1: Problem with that is it sounds terribly manual.\u003C\u002Fp>\u003Cp>Speaker 0: Sure.\u003C\u002Fp>\u003Cp>Speaker 2: It's Have the design just behind that. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Having to have all the hands conditions, and also, additionally, I don't I'm not a 100% sure. I don't think so, to be honest, that it is possible with the current policies because policies are additive. So if you add like a policy, you can't edit this item except for this, and you have another policy which allows the user to edit certain fields in that item. They still will be will be able to edit, edit some fields on those items.\u003C\u002Fp>\u003Cp>Speaker 2: Although we don't have the option to make, like, fields disabled depending on other fields, like, these like, depending on things and Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: You can\u003C\u002Fp>\u003Cp>Speaker 2: make it, like, if the the field has a certain value, other fields get disabled some or something like that. And, like, that's the point where I'm not certain that this would work because we have to make it relative to the user's name. But that could be, like, an option to have it like a no code basically, where you don't have to implement any extension and can get this running in a matter of, like, minute or, like, hours, config by just configuring directors in the end, basically. Hours as\u003C\u002Fp>\u003Cp>Speaker 1: soon as you start adding another field or another collection with 10 or 20 fields because you will have to go into every single field and apply that condition to it, because those are fields that you\u003C\u002Fp>\u003Cp>Speaker 2: will be horribly manual, but it will get the job done, basically. And for some people, that might already be enough. That's what I'm thinking. Like, a proper solution is always better than just doing it manually in a hacky way, but maybe that's better than writing single things in Slack. So Always better than Slack.\u003C\u002Fp>\u003Cp>Speaker 0: You know, it's definitely better than a than a Slack manual. I'm working on this item kind of. So that's that's a lot of work. That's a brutal thing. So\u003C\u002Fp>\u003Cp>Speaker 2: But, you know, you're terrific. Right? And then it would be a giant mess of configuration that would that you would have to go through, like, an giant effort for a simple feature that could be implemented with an extension. But on the other side, not everybody is an extension developer and hiring one could also be expensive. Like, I'm not sure.\u003C\u002Fp>\u003Cp>So that's maybe, like, you want to just have this and without any, like, effort input like, any large effort put into that and kick that configuration, that might be the way to go using, like, flows and, custom fields or something like that.\u003C\u002Fp>\u003Cp>Speaker 1: You still would have to have the user toggle that field and save it. Like, save the item, then only then once somebody else opens the item, you will be able to see it. So there's a lot of, like,\u003C\u002Fp>\u003Cp>Speaker 2: user flaws. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: It seems like there's more user error involved than writing a Slack message, because the fields will start appearing, as editable as soon as you toggle it, but you need to remember to save it before. Otherwise, nobody else would see it. So\u003C\u002Fp>\u003Cp>Speaker 2: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Certainly is an option. Certainly isn't the route I would go for.\u003C\u002Fp>\u003Cp>Speaker 2: No. I probably neither, but I'm also like an I I I'm also able to develop my own extension. So that also solves the problem for me of, like, would I go the extension route? But, yeah, like, custom extensions, I think that's a great place to for that. Like, that's a powerful way to use, custom extensions, and to just basically extend Reactors without having to con like, to commit to a certain solution that we will decide on.\u003C\u002Fp>\u003Cp>Like, if we would now decide on this solution as the best, like, maybe for us right now at the moment, but maybe, like, we don't know of any of certain use cases where people would prefer another solution and, like, going the extension or, like, the lab route, that's definitely sounds like the best solution at the moment, to like, for everybody that people can try this feature out without having to, like, have it in core. And, then, like, depending on how this develops, people can then, like, we can then start looking into how does this get used and how often and how much, like, interest is behind that after it's gets in after it's get gets implemented as a custom extension, and then we can iterate on the next versions of directors to see what we want to have this rigging call just to make it, like, the setup a lot easier for people. Right?\u003C\u002Fp>\u003Cp>Speaker 1: And since we still have the original author of that feature request in the chat, I really wanna ask him to, like, as a closing thought kind of deal, because we're doing quarter like, 45 minutes usually for you, your entry, feature request. Right? Or is it a full hour?\u003C\u002Fp>\u003Cp>Speaker 0: It's technically a full hour, but we're well, I think we're just about there. I'm putting in some final notes here. But, yes, if they if they offer some ideas, I'd love to hear.\u003C\u002Fp>\u003Cp>Speaker 1: Have we covered whatever we were thinking about? Have we outlined the thoughts behind why it's currently not in core or how it could be implemented as an extension? And maybe, like, future options for collaborative editing, which kind of overtake this or replace it at some point. Jonathan, you will have to tell a story while he's telling chat, I think.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. Nope. I'm just quickly I'm quickly scanning back through here to see if there was anything that we missed. I think we've covered the general requirements here. And I agree that longer term, I think, you know, ideally collaborative editing solves this problem outright.\u003C\u002Fp>\u003Cp>Right? Because then you've got field level notification of who's making changes, when they're making changes. It's real time. You see the changes in real time. But that's a story for another day.\u003C\u002Fp>\u003Cp>This this particular use case, I really like the idea of we can potentially put some solutions out into the wild, help us identify what are the edge cases. Because even with collaborative editing, what are the edge cases we're gonna run into? What are things that can happen or will happen? You know, relational always creates the complexity. Right?\u003C\u002Fp>\u003Cp>When you start digging into relationals and, you know, at what point are you locking or not locking, what points are you notifying, not notifying, handling some of those cases. And collaborative may just make that all go away. There may not be edge cases other than speed and performance of getting notifications back and forth, for the users. But,\u003C\u002Fp>\u003Cp>Speaker 1: Tim isn't allowed to participate in these\u003C\u002Fp>\u003Cp>Speaker 0: Oh, versions.\u003C\u002Fp>\u003Cp>Speaker 1: Versions. Should be fun. Yeah. Versions. We haven't really talked about versions in this whole thing.\u003C\u002Fp>\u003Cp>Right? That is yeah. You're not allowed to participate. I don't think so. You also versions.\u003C\u002Fp>\u003Cp>There's a thing like versions.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. That would make it a lot more difficult. Although I'm\u003C\u002Fp>\u003Cp>Speaker 1: sorry though. Do\u003C\u002Fp>\u003Cp>Speaker 0: for for the for the extensions, you can build your own. Right? That's an option. You can start with that today.\u003C\u002Fp>\u003Cp>Speaker 1: You bring it to a very good point. We as\u003C\u002Fp>\u003Cp>Speaker 0: the core team, I will get this in as a request into the Directus Labs extensions group. So that there is a core team that is working custom extensions as part of the marketplace. That's where things like yeah. You may have seen it today, but I have this I actually have this one installed. I love this one.\u003C\u002Fp>\u003Cp>The spreadsheet. Right? The spreadsheet layout, this ability to edit these things live right here. Fantastic. Love it.\u003C\u002Fp>\u003Cp>This has been one that was that's been asked for since the day I joined. I've I've heard people asking for spreadsheet style layout. So I\u003C\u002Fp>\u003Cp>Speaker 2: think I remember, Ben asking for this feature, like, in 2,000 well, at 2,021 or something, like, or even before that. So, yeah, it has been on a long road of, like, when will this come? When will this come?\u003C\u002Fp>\u003Cp>Speaker 1: When will\u003C\u002Fp>\u003Cp>Speaker 2: this get implemented?\u003C\u002Fp>\u003Cp>Speaker 1: But doom is bringing up a very good point because, yeah, sure. You don't use versions, but the reason why it's taking us so long to bring new stuff like this into core sometimes is you don't use versions, other people might. So everything that is in core has to work for all the features that we already support.\u003C\u002Fp>\u003Cp>Speaker 2: Didn't he set the other way around? Because it's like double negation. Is it like who does not use versions in a sense that, like, everybody use versions? Or am I misunderstanding his message?\u003C\u002Fp>\u003Cp>Speaker 0: No. Content versioning is\u003C\u002Fp>\u003Cp>Speaker 1: I would think Unless we have\u003C\u002Fp>\u003Cp>Speaker 0: fixed the permissioning problem, we still it it's it's very, very limited in its use case options and where you can actually today, because of publishing and editing permissions. So if I wanna main should technically be read only and locked, which is published state. But as soon as you create a content version, if you've got the main record locked, all content versions are locked without changing main. So there are permissioning issues, policy issues right now on the permissioning side that make content versioning difficult to use if not impossible. What's that?\u003C\u002Fp>\u003Cp>Speaker 1: Do we have an issue for that? I have to ask now because I was the one\u003C\u002Fp>\u003Cp>Speaker 0: Yep.\u003C\u002Fp>\u003Cp>Speaker 1: We had is it is it if you have a problem?\u003C\u002Fp>\u003Cp>Speaker 0: We've got open feature request for it. Yes.\u003C\u002Fp>\u003Cp>Speaker 1: What is feature request?\u003C\u002Fp>\u003Cp>Speaker 0: So it's a again, this comes back to, you know, the way the policies are implemented currently. It relies on the main permissioning rather than the version permission version having its own permissioning. Yeah. So you can lock things like I can prevent promoting. I can do things around.\u003C\u002Fp>\u003Cp>Can they create or not create, you know, versions? Can they promote or not promote versions? But the ability to edit that version is dependent on the permissions of main, and therefore, I can't do things like lock main, which means now a user, if they've got the permission, they can edit the published record live without doing it inside of a version. So there's some things that are problematic there.\u003C\u002Fp>\u003Cp>Speaker 1: That's a very good point. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Neither here nor there. Just for the folks here, if you see anything in this particular project, this is kind of core team provided. It's not guaranteed that it's maintained long term, but you have if you see these in the marketplace, right, so Directus marketplace here, if you see those in this particular layout and if you see anyone with the let me put an AI space here. If you see anyone with this little check mark here, that tends to be core team. They've been approved, so you can at least trust that it's coming from a reliable source, in that sense, generally.\u003C\u002Fp>\u003Cp>But marketplace extensions is where this will would show up. So if we get the team to agree that they wanna take this on, you'll see that show up at some point in the next 2 to 4 months, generally. We tend to try and get these we're we're tend to roll out, these lab extensions on a monthly basis. So we've got well, again, no guarantees, but, hopefully, the team will decide that this is worthwhile. I'll put the feedback in to say that this seems like a really cool one to actually have out there in the wild to get this haptic feedback so that we know what we need to watch out for as we continue to implement these kinds of features in Directus\u003C\u002Fp>\u003Cp>Speaker 1: Core. So\u003C\u002Fp>\u003Cp>Speaker 2: so,\u003C\u002Fp>\u003Cp>Speaker 0: Petros, give it a give it an upvote or you're not finding the ticket, open a ticket. Yep. The this is\u003C\u002Fp>\u003Cp>Speaker 2: a I\u003C\u002Fp>\u003Cp>Speaker 0: ran into it. It was what the the first week that we rolled content versioning out, I had a client reach out. I need to be able to do I'm trying to do x y z, and it doesn't seem to be working. And I tried I tried flows. I tried work around.\u003C\u002Fp>\u003Cp>I tried all kinds of hacks. I could not get around the fact that main is driving permissions for the version. So it's, it is the there's a slight problem there. Although I guess you could with a flow, you could watch on main if main's in published, you could just prevent. But it's a it's a it's a bit hacky in the sense of the user can make a whole bunch of changes.\u003C\u002Fp>\u003Cp>Now try to save that and realize that they were in main to be blocked. But you can at least you could put some validation in place that force prevents that save.\u003C\u002Fp>\u003Cp>Speaker 1: Feels like a very good discussion topic for another feature request. So\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Absolutely. The point.\u003C\u002Fp>\u003Cp>Speaker 1: Straight to the point. Yeah. Of having versions and promoting and demoting versions, versions. Where we get those permissions from since, like, you correctly said all the permissions are determined by the main items since we have one, like, one permissioning engine in core. And it just looks at the main item, and it actually does it actually has a database evaluate all those permissions for you, which makes it easier in a lot of senses, but makes stuff like this where versions are actually separately maintained JSON blobs of that item a lot more difficult.\u003C\u002Fp>\u003Cp>Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Very true. But we're off topic. Any final parting thoughts on editing, notification?\u003C\u002Fp>\u003Cp>Speaker 1: Sounds cool.\u003C\u002Fp>\u003Cp>Speaker 2: It's like\u003C\u002Fp>\u003Cp>Speaker 1: there's an extension. There those are my final words I wanna say. Yeah. Because I definitely see the US improvement. I see the value in it.\u003C\u002Fp>\u003Cp>I'm just not a 100% sure if we can get it into core properly while keeping all the features we currently already have in mind in a reasonable time frame. Yep. It's\u003C\u002Fp>\u003Cp>Speaker 2: always the goddamn time.\u003C\u002Fp>\u003Cp>Speaker 0: Well, no.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. But let's let's see what we can do. Put it. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: And, I mean, we can also, like, put it out here. If you feel like, hey, that is an extension I'm gonna develop, reach out to us. Maybe we can help you.\u003C\u002Fp>\u003Cp>Speaker 2: That would be awesome. Yeah. I would love to hear people getting just their hands dirty in this.\u003C\u002Fp>\u003Cp>Speaker 1: Because in the end, anyone can develop an extension for directors. We try to make it as open and easy as possible. So if you feel like that is something you wanna try, reach out to any of us, and we'll see what the options are.\u003C\u002Fp>\u003Cp>Speaker 0: Actually, reach out via the ticket. So post your comments, thoughts, work, other things that you're doing. Let's make sure it gets back to this ticket so that we can actually track that progress. And Yeah. We'll try and do the same.\u003C\u002Fp>\u003Cp>We'll, again, when we we do these kinds of extension developments, we'll tag that back into this ticket. So if it gets accepted and committed and delivered, we'll we'll try to remember to populate that back into the ticket. Alright. Cool. Well,\u003C\u002Fp>\u003Cp>Speaker 1: well, honest sales.\u003C\u002Fp>\u003Cp>Speaker 0: Wonderful having you guys join on short notice.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Was this last of the year?\u003C\u002Fp>\u003Cp>Speaker 1: As usual.\u003C\u002Fp>\u003Cp>Speaker 0: It's exciting. Team, thanks everyone for joining. Have a wonderful rest of your week. And as always, reach out through Discord or through the ticketing systems, and we're here to\u003C\u002Fp>\u003Cp>Speaker 1: help us find out. October 15th for the next change log that covers all the changes that will be released in the new version. If you're there.\u003C\u002Fp>\u003Cp>Speaker 0: And as always, this will be posted in the request review channel of the Directus IO TV.\u003C\u002Fp>\u003Cp>Speaker 1: Chat out everyone. Last chat message. All the feedback from this call is has actually been typed up by Jonathan while we were talking until Thursday.\u003C\u002Fp>\u003Cp>Speaker 0: I will see. Yeah. Well, I did. You guys feel free\u003C\u002Fp>\u003Cp>Speaker 1: to add\u003C\u002Fp>\u003Cp>Speaker 0: if I missed anything. Cheers, everyone.\u003C\u002Fp>\u003Cp>Speaker 2: Cheers. Cheers. Bye bye.\u003C\u002Fp>","Let's rock and roll. I think this fundamentally comes down to something that we've been talking about off and on for a couple of years now is the ability to have things like collaborative editing, at least some level of notification somebody else is working a record as our as we all as as we all here know very well that it's a first save wins at the moment, and you have no idea if anybody else is editing a record that you happen to be editing. Generally, it works out. I don't think it's a frequent occurrence, but it's probably it is something that I think we would all like to see solved. And I'm hopeful that I think I see BrainSlug is in the audience as well. That's good because I think he's actually got a custom extension that actually uses websites to actually do this, for, like, live chat and other things. But I think he actually did something as a test, and we'll we'll get through that. But that's what I think this generally is, and we'll get this thing kicked off. I'll turn it to the brains of the operation, and let's roll through this thing. Shout if you need me to show something or pop over to somewhere, and we'll go. Yeah. Tim is disappearing. Yes. I think this was actually a fairly easy feature request, you know, in of itself if you don't consider, like, all the possibilities you could evolve evolve to. Because in the end, it's just asking for, hey. We need a notification if someone is editing something, and that doesn't really it needs some kind of real time possibility, but it doesn't really need any collaborative editing efforts. It enhances it by already. If you put where WordPress does as well. Haven't used WordPress in a long, long time, but I think it had something to do with where I told you, hey. This is, this part is embedded by someone else, which is basically what this feature is asking for, just to give them heads up and just to improve the experience. But I think the author of the Hippo Trust, should also be in the audience. Maybe you can raise your hand or something, say hi, to see what you envisioned there. Oh, yeah. There he is. Because I know it's, like, just reading it, it seems like a fairly easy thing to implement and to consider. It just we just need to figure out, like, what are the extended use cases we wanna have with it. Is it enough to show that occasion as a starter? Who we able to wanna evolve it into collaborative editing and all of that? Because we already had a discussion on real time. I think I just went through a couple of discussions almost October last year, so there have been some discussions about that in general as well. And this feels like an obvious feature to merge into that as well. But I see that Uwe, Uwe. So, excuse me for butchering your name. He's typing right now, and he has some additional insight that he collected over last year that this feature request has been up and being voted by the community. Yeah. And and to just give a bit more context from what I remember, I think this was, like, in the talks. Like, then when we started, like, working on WebSocket support and stuff like that, that's was, like, one of the, like, key things we looked at for, like, WebSockets and stuff. But as you mentioned, like, it doesn't really necessarily need WebSockets. You could also, like, pull, like, a get request each second in if you would want to go without WebSockets. Like, there's always, like, the possibility to implement this. It's just, like, about what do we want to have this as a real scope in terms of, like, we just implementing this is probably there's more to it in the end, basically. There's more we can do, more we can, like, find out before we go implement this feature. And yeah. Also something to consider is can we implement it as an extension? Does it have to be in core, or is it something that could be a presentation interface, for example, which can, utilize the real time WebSocket API to figure out, like, is there anyone else editing it right now, maybe through a system collection or something that, along those lines where you write an item as soon as somebody is there and you kind of keep it alive, had it write a time stamp when it was last visited or something, and then you could figure out based on that if there currently is one editing it. I can certainly see going that route with an extension as well. It doesn't really need to be a core feature even though it could be nice to have as a core feature. Tim said that one thing he recalls about being difficult is that what do you show about the other user if editing, what the other user that is editing it? If you don't have the permissions for other users, I think you would just say, hey. There's one other user editing or something. It doesn't really it's nice to have a name and to have a thumbnail and profile picture and all of that, but it doesn't necessarily need it to function in the 1st place, I would say. So that that was the question that I just wrote down is what's considered editing. Right? Is it just someone has the item record open and they have edit permissions, and therefore that's considered editing or they've actually made a change and that's considered editing. Just something that Or step in between is have they focus on input, for example, if like a step towards editing. Yep. Yeah. That's just another thing we, like, probably have to, like, decide on first for something like this could be implemented because that is like a known edge case. And, like, for example, if we would go with the locking route, where, like, an item gets locked if a user edits, then the real important question is, like, with that system, somebody could just, like, lock us, like, an item forever and not release it, and at any point of time in the future, and that would be a big problem because then you have, like, oh, my my colleague from work is not, giving me access to this item right now. I can't do my job, and that that is not an option, really. So RCS, which is an ancient, vision control version control system from, like, the eighties, like, eighties, nineties from Unix. And they have this concept of locking a file. So if somebody locks a file, you're not able to edit it anymore, and you can, like, force unlock a file. So they certainly use that possibility as well. But Right. There is always gets overtook this kind of locking approach, where you're trying to negotiate and take these afterwards. I mean, in this case, I think, as well as the author of the PR, feature, I guess, sorry, said the notification itself should be the better option. Also, seems I wanna say simpler implementation wise because both of them kind of require either, some state on the server or requires some, entries in the database. Having the item page feels easier. Yeah. Because you also have so as as Mills mentioned, right, you have the ability to potentially so we got a lock record route then how long do you allow that lock to remain held? And you automatically time it out and just, you know, that person loses their edits. Mhmm. You know? Or is there an admin way to force specific out of whack and now you got a record block that can't be accessed by the users in any way, shape, or form, even the original user that, you know, initiated the lock. So that means we need some admin, ways of managing and releasing force releasing and resetting locks. Oh, Pedro brings up a very good point. We we have relations in directors, so you're not always just editing a single item, but you can basically walk a whole tree of changes and go 7 drawers deep and start editing, items that don't that aren't actually your item page. That is a very good call, which I haven't considered myself so far. That that's yeah. And that in that sense, locking will probably be, like, very difficult to implement because then the question like, the real question is what even do we lock? Not only, like, when do we lock, but how and what and why. So that's way more tricky than just giving you the information of somebody. Like, in a perfect world, we would have to, like, live collaboration that basically solves the problem out of the box, but that's not really feasible, I think, at the moment in the core implementation of our app. So, like, some way of not like, not an either a notification bar or, like, a, like, a schedule to request to see if there's somebody editing your current item and then update it or something like that. That would be like a, I would say, trade off between what we can do right now with your time and what should improve the experience by, like, the minimum, at least. Like, the minimum, of, like, to yeah. Basically, to have some way of knowing that somebody is working on this cost right now, there isn't. I mean, I guess there is on the user because the user table has the records on the last edited item. So there is currently already the information stored in directors. Well, the last page, which, gets terribly confusing if you have multiple tabs open. Right? And as soon as you start True. In that case yeah. The information gets up because that's a single entry. But that would be, like, I would say, like, a good start to use that information that we already have to work on that week. Although, we if we were to, want to go for it, we could also make it, like, fully fetched then. Yeah. That's, like, that's also true. Yeah. The problem with the last page is, like, it isn't very it it isn't really, like, a secure information on what is the user what the user is currently doing, because they might open 1 page, might open another page, and it not, like, might open the item page and then tab out, go to a different tab, and navigate in there. And suddenly, you lose all the information because it's really only intended as a way to basically bring the user back to the last page they open once they log out and log back in. That is the intention of page, to have like a more fluent experience when you revisit the same director's instance. So using that really doesn't seem like the right option, but Tim linked his experimental, live collaboration interface extension, which live collaboration really is a big word for it, I wouldn't say, because right now it's just a presentational interface that can notify you if somebody else is, working on the same item. And I just peruse that the code, Tim, please correct me, but it seem it seems like it's using our real time WebSocket, and it just sends custom messages over it. I don't know if it needs some does, the WebSocket implementation automatically fan it out to all connected clients, or is there some sort of a component as well in there that is necessary? I'm not able to use that one. I'm thinking, like, not while doing that, let's read some chats. Joshua, she's she's it suggests lock timeouts. It's certainly an option, 100%. Still leaves you with the possibility. What is a lock time out? Is it 5 minutes? Somebody might be editing longer than 5 minutes. Is it an hour you might wanna add it to an item sooner than an hour after your colleague left? So if we were to go to the lock route, there certainly has to be a mechanism for you. Yes. I'm sure I wanna override this lock. I wanna take the over this page, mechanism kind of. Yeah. I'm with, Pedro's on this, notification that is shown once the user starts editing a record should be great, And it's just a simple avatar of as a fallback, as we said, if the user currently editing the item doesn't have any information about other users or something that doesn't have the permissions. Yeah. And another thing I am wondering right now, or thinking about is, regarding where we would want to store that information, who is currently editing what item. We could, like, store it somewhere in the user collection. We could store it somewhere on the item. So what I was thinking, for example, would be a custom interface that basically makes a request to the, API, on, like, somebody opened this item, a request goes like, because interface interface gets loaded, the interface lets the API know that, like, the value of this field gets then sent to the user's name. And, others visiting the same page, the basically, the data gets fetched from the API. And in that field will be the name of the current user who's editing that item, and that interface could then display that information. And that would also, like, require no real time WebSocket connections, and that would also be a very straightforward in that sense that it is very similar to the created or updated on or modified on, interface, which, like, could also make this as an extension because then we are not like, we can just flop, I mean, you're gonna have to edit your schema for that feature to work, and it is not like, it is basically an opt in feature in that case. But it would be very straightforward to implement even as an extension, and that could also be another interesting approach to this problem, basically. So, basically, either extending your current items to a user, or you can even introduce a whole new table that is just a, a lookup table kind of, like, this user, this item timestamp could work. And then, basically, once a user opens the page, it looks up, and the user would have to refresh their page in order to for that information to update. Right? So it wouldn't live update. Yeah. If somebody would leave it and would set it again to null, it wouldn't like, it there's no live update, but it is it allows you to give a get a hint of or if already somebody else opened it recently, basically. I think it should be This quite, like, quite nicely solves the problem in a very stupid way, I would say, and, like like, a very straightforward and simple way. And that might already be enough for, like, most people who want to have this feature. Although, like, the big drawback of this still is that you're basically editing your schema, and that's in that point, and that like, it is an opt in per collection and not just something that works out of the box, except you usually would create a separate table for that. Then it would be for all collections at once. Okay. That is actually fine because you can just add a new field. It doesn't have to be a presentational field, but you can add, like, a Yeah. Exactly. Yeah. We can just add a field that's a string, basically, and that gets set by the interface to the user's name who last opened it if there's none already in it. And that kinda could, like, trick around, like, real time and stuff and just, very, like, get to the point of there's just a name in there and or maybe concatenate even multiple names to each other. The field could be reset. So I would I wouldn't just save the user, but it would also add a current timestamp as well. Okay. Just just for the problem Tim mentions. Like, for example, if your browser crashes, like, for example Yep. Normally, you would update the field actually with real time. That is kind of a necess null. That isn't really a necessity since you can send out beacon requests. I don't know if beacons can be post requests. I'm not a 100% sure, but there's a mechanism in a browser that is, like, even if the tab closes, please send out this request to this URL, which would kind of allow us to reset this field whenever a top uploads. It just leaves us with the additional problem of if your router crashes. Yeah. They are risky. They're not guaranteed to be a little bit all of our shenanigans that, time attribute, basically. Basically, that's what we've got in the browser, I would say. Yeah. Regarding that disconnects, yeah. WebSocket, but then you would have to go browser, like a module sorry. A bundle route where you also have some server side thing. Because right now we are just thinking, of terms how to implement it in extensions. But certainly, if you're going for implementing any core, there's more possibility to detect if a user has left a page because we can just continuously, we could just continuously monitor which WebSockets are connected, and we could send live updates as soon as the user navigates from there. So very certainly, it was a bit I'm just trying to, like, imagine if there's a possibility to do it, not in core so we can fast track it because, as you know, we have a director's left extension program, where we have extension experts currently developing a lot of features that are that have open feature requests, for example, but never get never really have been implemented in core so far, which is kind of like our experimentation program for figuring out, hey. These could be features that could let into core, but let's see if we can use bring them to users quicker by, getting the extension route just because as soon as you'll have a feature in core, you we always wanna make sure it works for 99% of the users, maybe not 99, but a lot of the users. So the process of bringing something to a core always, as you see with all these feature requests, is a lot more involved than just dropping quick extensions saying, hey. This is something experimental. That's correct. It's actually a trigger for me. Alex Vandervalk and I created a we've we had a use case from a client where they wanted to be able to do some relation they wanted to be able to do some enforcement on, or they wanted to know that someone had a current, like, project design. So or a technical design document. It was the one that they were working on right now. And so they wanted to because of that, they wanted to auto filter for those things and be able to indicate that for the user. It also then did relational enforcement, through the relational hierarchy of the technical design document, where you had software components and relationals and many to any's and all kinds of things going on. So what we actually did was we used because Directus is actually always you know, the the Directus application is always watching, you know, user actions and saving last page and all those things. We actually intercept that as part of a flow operation, And we if they change to a different technical design document, we auto update the current design document so that it it does the relational enforcement through the permissions and things like that. So very weak. Yeah. So this this actually does that. So this flow actually so this is kind of along Nils' lines of if you add a field or you add a collection that you wanna keep track of and that, you know, provide notification to the user, where you do those kinds of updates. Then in this case, we're actually updating. In this case, it's the direct user table. We're setting that to the current TDD that they're working on. So when they change TDDs, it automatically updates this, and therefore, we have that field throughout the relational enforcement. We've got that field, you know, and we can then with many of the filters or your field filters or your policy filters or whatever you wanna apply, we can then enforce that you can't add software components that are part of another TDD. And it was one of those kinds of use cases that we're actually solving. But I think it actually, in some degree, fits this use case as well. That still leaves you with, like, the edge cases of multiple tabs, which wreak havoc with that or could potentially wreak havoc with that. Because right now, we don't really have, like, a possibility to try. Do a bad job. It's actually gonna just do it based on whichever tab you have open and when you're navigating back. Yeah. The the multi tab is a problem anyway. Directus does weird stuff with this particular page save issue anyway. I've I've learned never to have multiple tabs of a directus instance open because it just it will wreck havoc on your stuff. I love do a duveis or however you pronounce it, please let me know. Work around right now. They basically have a dedicated Slack channel where they message, I'm editing this, hands off. I love it, damn that's most straightforward, just do it solution, I love it. Out of band, out of band, SS7 messaging. Keep your hand up, miss. That's beautiful. I think we have a solution. I had it short here. Thank you for attending. So so you could use the flow in this case to do that. You could actually automate that and send this send the Slack message to say, hey. I've got this item open. Now, again, it comes back to, am I editing it, not editing it, that kind of thing. But Yeah. You could at least have a flow so you don't have to be the one going and typing in the channel that I'm in this record right now. Coming back to, like, how it actually could be implemented, I think there is a viable route to have this implemented as an extension just because, we provide you with all the necessities to do it. We there is real time once you enable WebSockets. So you could potentially even life update the like, life update in a way of, the user who was currently editing the field just left, and you basically subscribe to the changes on that item if you go with the route to have it on this. On one eye like, on a, on the same item or if you introduce, like, Joshua or like I've been talking about, like, a separate table that contains all the locks or not locks, but the current edits or the user presence kind of, I think in collaborative adding new all these presence indication, which basically tells you like this user can get this item, which also allows you to have multiple, which also allows you at some point oh, yeah. Because you would have to so thinking out loud here. Coming back to the problem that, Petros mentioned, we have relation items. So we we can traverse an item to different items. And as soon as all of them have this tracking interface, all the necessary, hey. This item is being edited notifications would be there, and they would automatically, oh, they would automatically be gone. That is might be a problem. Well, anyways, so the the there is, like, a possibility to have that as an extension. That's kind of what I'm trying to say. And, yep. Because I'm kind of having things for relational, so it's just does a relational edit because just because I'm editing this item doesn't mean I'm editing the relationship right around Oh, yeah. No. No. No. They they would, for example, be loaded as soon as you start opening a drawer, for example, that interface would also be loaded. It knows where it's knows and it would do the update and tag that. Yeah. The only problem is the only problem is with that, you would typically use an unmount trigger or something. As soon as that interface is unmounted, you would say, hey. I'm not editing this item anymore. But if you're editing through another item, all those changes would be staged. So if you close the drawer, it actually would tell somebody else, hey, it's not being edited right now or anymore. But in fact, it is because in fact, you just made an edit and staged it, and it's only gonna be committed or saved to the database when you save the paradigm. Yeah. That's a good point. With relational data, that that would be tricky to get right in the yeah. Although, it like, at least the the the the one way, the the setting, the username correctly works on relational items if you go for the route of having a field on the separate item, like, on each collection on based on adding a field for tracking, who's currently editing what. But when do you, like, release that information? When do you release That's a good question, which I not yet know the answer to. Like, you would do it on an unknown trigger for the interface or something. Right. Tim says the way he's done it in his, experiment extension is that he only notifies if it has been saved in in the meantime so people can concurrently edit it. Ah. Not that they just notify, hey. This has been saved while you are still on this page, which certainly is an option as well. Just doesn't feel as nice, but it feels a lot simpler to implement with some resolving options. Okay. So, like, hey. Take my changes, override it, or take their changes kind of deal. Right? Which is commented on the version control. Right? As the you get notified that someone else has made a change, the changes get merged into you can either choose to merge those changes or throw them away and lose out. Which is more along the lines of, like, resolving a conflict and not really and notifying you of a conflict ahead, but not trying to prevent the, conflict before it actually happens, I guess, which is, like, true process or the same kind of thing. I'm honestly divided on which one is better. It won't be, like, trying to prevent the conflict before it's happening. It feels like the nicer user experience to me. Yeah. Like, I I would always go for the user experience, though, in that you want to know in advance that somebody's editing something, not when somebody then actually did their changes and saved them. Because then you can, for example, communicate something with somebody who's working on the same project, on the same item in that sense. Are you editing fields that I'm editing too? Yes. No. And depending on that, they can either just continue working on it together or, like, wait for that other person to then actually do the changes and then the next person. Basically, they have the option of, do we have to do it in series, or can we make it in parallel, basically? Like, via Slack or something, like, like, basically talking that out. At least they know that somebody has had has the intent to do changes on this item in the future. There is a Twitter account well, x account, whatever, that explores hideous, design decisions, UX design decisions. We could also add a button, boot this per pay $5 to boot this person out of that item or something. I like it. Though yeah. That would be another option instead of going like the okay. We do it automatically when the user joins in these, we make it like an basically, like an option where it's like you opt in now to mark this item as you're currently editing, or you can leave it if you maybe, for example, like to even make the opt in optional in a sense, which is, like, even then more work to do. And it's probably nicer to make it automatically set to the current user by when editing. But, again, like, that opt in makes it also that you have to opt out of it kinda thing where where if it's automatic opt in, you expect to have it automatically, like, put your like, basically know when you're done editing. But if you manually opt it in, then you also have the expectation of you have to manually opt out again of editing this item, if that makes sense. It might be automatic. Otherwise, it's just a hassle like an introduce additional hassle and nobody will start actually. I mean, it's it's less of a hassle than going through Slack conversation and seeing if somebody has the same item name somewhere. Yeah. It's still it's still not as nice as it could be if it's automatic. As that said, editing some editing field should be disabled for others, which introduces another possibility or option or That would be possible through the field by, making filters. That that is if the if there's already text in that field where the name would be saved, that all other items are although that would make it different good for the person actually editing it themselves because then these figures would probably also apply for the same person. Although maybe you can fill it out by the user username and the username matching the in the field. I'm not sure how how crazy good our filter or, like, our, things for that are are that might also work, like I'm not sure. I was actually thinking more along the lines of enhancing the notification, possibilities that basically saying this field has been modified by another user. So I have it in line with all the fields and have a notification for all the fields that have been edited in the meantime, which certainly is possible again with real time. It might introduce another permission hell because people might not be able to see some fields. And if we just send them over WebSocket, anybody can read it, which, yeah, introduces problems again. But from UX perspective, again, it's super, super nice to have the possibility to, being able to tell, hey. They edited the event name. They edited the Slack. They edited whatever, as an enhancement to the original request of just shown in banner on my like, for the full item, which then again already puts you more into the collaborative editing experience where you have presence markers on all your fields and, know who's currently doing live edits on the page and seeing the edits and all of that, which is would be amazing to have. But as you already said in the beginning, it's a long ways away with the current architecture of, directors just showing you what's in the database because effectively, that's just what we're doing. Right? As soon as something is in the database, you see the most recent data, present in there. We don't really have any mechanisms on top of that for distributing life changes and all that that aren't already committed to the database. Oh, another interesting approach regarding the like, when is a user done with editing an item? This is, like, a crazy solution and probably not really feasible. I still want to go with it because it sounds quite fun. Instead of subscribing and unsubscribing from an item, we could go the road as long as a user is continuously pinging an endpoint, that he's currently editing. This was which also tackles the who which tab is currently open because only the active tab will then ping that endpoint basically ping a certain endpoint to let, the basically, back end know who where the user is currently editing something. And we just do it like a automatic opt out after a minute of not receiving a ping from that person. That would also be another interesting approach regarding the when is a user ready like, done with editing an item or something like that. But also that that also has edge cases in a lot of senses, like, in a lot of ways. Yeah. Also, you can, like, you can do that through WebSockets as well since you just Yeah. Track. I mean, yeah. I'm sure. I'm just thinking of, like, would it how tricky would it be to get this working without WebSockets? Because that's like, WebSockets makes it also a bit more, like, advanced in a lot of sense. Like, the the I I think we have, like, a ton of good documentation on how to do rest request with like, indirect us with extensions, but on WebSockets, I don't think there's a lot of documentation yet, which makes it difficult for, like, community members to implement features like that. There's always a way, but it's a lot more tricky, and that's I would assume. I'm not sure yet though. We put a couple of guides on how to use the real time SDK, which provides you with a lot of options to use that already in base starting point of Tim's extension as well, for example. Yeah. That's that's we have Tim's example. That's a good starting point. Yeah. But yeah. So I think we agree it's a very good idea. We just aren't sure if it should be in core or if it can be extend an extension because if it can be an extension, let's try to go throughout first and then see it where it breaks and if there are things that have to be implemented for, just because of this many possibilities and flavors to implement this. And if we wanna tackle it, I feel like we should put it in line with presence indications and collaborative editing or at least some way of collaborative editing experience, and that is, like, the next step kind of. And I don't know if you wanna have a future in core which will be replacing the not just in future, for example. Yeah. Sure. Not no. Yeah. Right. In yeah. I I am definitely on the side of, like, let's make this as an extension, and then people like, even people can go with different ways of implementation depending on their preferences. Like, maybe somebody prefers the way of doing it per item where it's like, we add a field to a collection, and that field stores the current user who's editing it. Maybe some people would prefer that solution, and there would be may maybe somebody else implements another solution where it's stored in a separate collection or where it's done on the WebSocket depending what the requirements or, like, where the interests are. And, having this an extension basically allows us also to explore what people prefer as an implementation and see, like, what implementation of this gets favored over time, and also helps us guide, like, in the next iterations of directors to to find out the right way to do this basically in advance. Which is the exact pitch for directors' lab extensions, I wanna say. It allows us to experiment with features to figure out, like, what is correct, what sticks, what are the things we wanna see, once they might be coming to core. Another great example is the command palette, etcetera, which has been released a couple of weeks ago. I wonder by whom? Because that explores a lot of concepts of, global extensions and everything that can be implemented in core at some point. But for now, there's ways around it, and we can use it to prototype features, and we can use it to prototype stuff like the editing notifications, for example, as well. Jonathan, you've been terribly silent while we've been chugging along here. Yes. I'm just trying to keep up with you guys from writing notes over here. I actually like I I like the VIM style option that you guys have discussed here where you would actually say, just add a toggle at the top, and you can do this in your current data model. Right? You can use roles and permissions that restrict its read only unless you've actually toggled it to say, I wanna edit this item. Therefore, I can put the notification and update the notification to say, this user is currently editing. So you could have the flow and the hook and, extensions doing those things. But simply adding a field to the top of your item that says toggle on or off that I'm updating. Right? So VIM, you know, d I, whatever, you know, Linux editing editors who have always been that way where if I'm updating, I have to actually explicitly enter edit mode, and then I have to explicitly exit edit mode. So as long as I'm in edit mode, I have it locked. Now that means that I want the ability, you know, to give a flow manual button or other things where you could say, you know, I and it could be a flow button, right, that just says, I wanna enable edit on this. Therefore, send notification to the team that also is responsible. Right. Yeah. You'd send an email. You could send an in app notification. Or, again, just update another field next to it that says, I'm editing this. Therefore, if the administrators need to go in and override you, they could. But at that point, you can use role and policy permissions to say the policy permission is gonna say based on the toggle being an edit, I now have edit permissions on the appropriate fields that I'm allowed access to. I can make my edits, save my changes, and you could even make it so that if on save, if you wanted to, right, make your edits, if on save, that automatically toggles it back to unedit. Right? So you could do something like that for the users, and you'd have some control over the workflow there. So I like that idea. Problem with that is it sounds terribly manual. Sure. It's Have the design just behind that. Yeah. Having to have all the hands conditions, and also, additionally, I don't I'm not a 100% sure. I don't think so, to be honest, that it is possible with the current policies because policies are additive. So if you add like a policy, you can't edit this item except for this, and you have another policy which allows the user to edit certain fields in that item. They still will be will be able to edit, edit some fields on those items. Although we don't have the option to make, like, fields disabled depending on other fields, like, these like, depending on things and Yeah. You can make it, like, if the the field has a certain value, other fields get disabled some or something like that. And, like, that's the point where I'm not certain that this would work because we have to make it relative to the user's name. But that could be, like, an option to have it like a no code basically, where you don't have to implement any extension and can get this running in a matter of, like, minute or, like, hours, config by just configuring directors in the end, basically. Hours as soon as you start adding another field or another collection with 10 or 20 fields because you will have to go into every single field and apply that condition to it, because those are fields that you will be horribly manual, but it will get the job done, basically. And for some people, that might already be enough. That's what I'm thinking. Like, a proper solution is always better than just doing it manually in a hacky way, but maybe that's better than writing single things in Slack. So Always better than Slack. You know, it's definitely better than a than a Slack manual. I'm working on this item kind of. So that's that's a lot of work. That's a brutal thing. So But, you know, you're terrific. Right? And then it would be a giant mess of configuration that would that you would have to go through, like, an giant effort for a simple feature that could be implemented with an extension. But on the other side, not everybody is an extension developer and hiring one could also be expensive. Like, I'm not sure. So that's maybe, like, you want to just have this and without any, like, effort input like, any large effort put into that and kick that configuration, that might be the way to go using, like, flows and, custom fields or something like that. You still would have to have the user toggle that field and save it. Like, save the item, then only then once somebody else opens the item, you will be able to see it. So there's a lot of, like, user flaws. Yeah. It seems like there's more user error involved than writing a Slack message, because the fields will start appearing, as editable as soon as you toggle it, but you need to remember to save it before. Otherwise, nobody else would see it. So Yeah. Certainly is an option. Certainly isn't the route I would go for. No. I probably neither, but I'm also like an I I I'm also able to develop my own extension. So that also solves the problem for me of, like, would I go the extension route? But, yeah, like, custom extensions, I think that's a great place to for that. Like, that's a powerful way to use, custom extensions, and to just basically extend Reactors without having to con like, to commit to a certain solution that we will decide on. Like, if we would now decide on this solution as the best, like, maybe for us right now at the moment, but maybe, like, we don't know of any of certain use cases where people would prefer another solution and, like, going the extension or, like, the lab route, that's definitely sounds like the best solution at the moment, to like, for everybody that people can try this feature out without having to, like, have it in core. And, then, like, depending on how this develops, people can then, like, we can then start looking into how does this get used and how often and how much, like, interest is behind that after it's gets in after it's get gets implemented as a custom extension, and then we can iterate on the next versions of directors to see what we want to have this rigging call just to make it, like, the setup a lot easier for people. Right? And since we still have the original author of that feature request in the chat, I really wanna ask him to, like, as a closing thought kind of deal, because we're doing quarter like, 45 minutes usually for you, your entry, feature request. Right? Or is it a full hour? It's technically a full hour, but we're well, I think we're just about there. I'm putting in some final notes here. But, yes, if they if they offer some ideas, I'd love to hear. Have we covered whatever we were thinking about? Have we outlined the thoughts behind why it's currently not in core or how it could be implemented as an extension? And maybe, like, future options for collaborative editing, which kind of overtake this or replace it at some point. Jonathan, you will have to tell a story while he's telling chat, I think. Yep. Nope. I'm just quickly I'm quickly scanning back through here to see if there was anything that we missed. I think we've covered the general requirements here. And I agree that longer term, I think, you know, ideally collaborative editing solves this problem outright. Right? Because then you've got field level notification of who's making changes, when they're making changes. It's real time. You see the changes in real time. But that's a story for another day. This this particular use case, I really like the idea of we can potentially put some solutions out into the wild, help us identify what are the edge cases. Because even with collaborative editing, what are the edge cases we're gonna run into? What are things that can happen or will happen? You know, relational always creates the complexity. Right? When you start digging into relationals and, you know, at what point are you locking or not locking, what points are you notifying, not notifying, handling some of those cases. And collaborative may just make that all go away. There may not be edge cases other than speed and performance of getting notifications back and forth, for the users. But, Tim isn't allowed to participate in these Oh, versions. Versions. Should be fun. Yeah. Versions. We haven't really talked about versions in this whole thing. Right? That is yeah. You're not allowed to participate. I don't think so. You also versions. There's a thing like versions. Yeah. That would make it a lot more difficult. Although I'm sorry though. Do for for the for the extensions, you can build your own. Right? That's an option. You can start with that today. You bring it to a very good point. We as the core team, I will get this in as a request into the Directus Labs extensions group. So that there is a core team that is working custom extensions as part of the marketplace. That's where things like yeah. You may have seen it today, but I have this I actually have this one installed. I love this one. The spreadsheet. Right? The spreadsheet layout, this ability to edit these things live right here. Fantastic. Love it. This has been one that was that's been asked for since the day I joined. I've I've heard people asking for spreadsheet style layout. So I think I remember, Ben asking for this feature, like, in 2,000 well, at 2,021 or something, like, or even before that. So, yeah, it has been on a long road of, like, when will this come? When will this come? When will this get implemented? But doom is bringing up a very good point because, yeah, sure. You don't use versions, but the reason why it's taking us so long to bring new stuff like this into core sometimes is you don't use versions, other people might. So everything that is in core has to work for all the features that we already support. Didn't he set the other way around? Because it's like double negation. Is it like who does not use versions in a sense that, like, everybody use versions? Or am I misunderstanding his message? No. Content versioning is I would think Unless we have fixed the permissioning problem, we still it it's it's very, very limited in its use case options and where you can actually today, because of publishing and editing permissions. So if I wanna main should technically be read only and locked, which is published state. But as soon as you create a content version, if you've got the main record locked, all content versions are locked without changing main. So there are permissioning issues, policy issues right now on the permissioning side that make content versioning difficult to use if not impossible. What's that? Do we have an issue for that? I have to ask now because I was the one Yep. We had is it is it if you have a problem? We've got open feature request for it. Yes. What is feature request? So it's a again, this comes back to, you know, the way the policies are implemented currently. It relies on the main permissioning rather than the version permission version having its own permissioning. Yeah. So you can lock things like I can prevent promoting. I can do things around. Can they create or not create, you know, versions? Can they promote or not promote versions? But the ability to edit that version is dependent on the permissions of main, and therefore, I can't do things like lock main, which means now a user, if they've got the permission, they can edit the published record live without doing it inside of a version. So there's some things that are problematic there. That's a very good point. Yeah. Neither here nor there. Just for the folks here, if you see anything in this particular project, this is kind of core team provided. It's not guaranteed that it's maintained long term, but you have if you see these in the marketplace, right, so Directus marketplace here, if you see those in this particular layout and if you see anyone with the let me put an AI space here. If you see anyone with this little check mark here, that tends to be core team. They've been approved, so you can at least trust that it's coming from a reliable source, in that sense, generally. But marketplace extensions is where this will would show up. So if we get the team to agree that they wanna take this on, you'll see that show up at some point in the next 2 to 4 months, generally. We tend to try and get these we're we're tend to roll out, these lab extensions on a monthly basis. So we've got well, again, no guarantees, but, hopefully, the team will decide that this is worthwhile. I'll put the feedback in to say that this seems like a really cool one to actually have out there in the wild to get this haptic feedback so that we know what we need to watch out for as we continue to implement these kinds of features in Directus Core. So so, Petros, give it a give it an upvote or you're not finding the ticket, open a ticket. Yep. The this is a I ran into it. It was what the the first week that we rolled content versioning out, I had a client reach out. I need to be able to do I'm trying to do x y z, and it doesn't seem to be working. And I tried I tried flows. I tried work around. I tried all kinds of hacks. I could not get around the fact that main is driving permissions for the version. So it's, it is the there's a slight problem there. Although I guess you could with a flow, you could watch on main if main's in published, you could just prevent. But it's a it's a it's a bit hacky in the sense of the user can make a whole bunch of changes. Now try to save that and realize that they were in main to be blocked. But you can at least you could put some validation in place that force prevents that save. Feels like a very good discussion topic for another feature request. So Yeah. Absolutely. The point. Straight to the point. Yeah. Of having versions and promoting and demoting versions, versions. Where we get those permissions from since, like, you correctly said all the permissions are determined by the main items since we have one, like, one permissioning engine in core. And it just looks at the main item, and it actually does it actually has a database evaluate all those permissions for you, which makes it easier in a lot of senses, but makes stuff like this where versions are actually separately maintained JSON blobs of that item a lot more difficult. Yeah. Very true. But we're off topic. Any final parting thoughts on editing, notification? Sounds cool. It's like there's an extension. There those are my final words I wanna say. Yeah. Because I definitely see the US improvement. I see the value in it. I'm just not a 100% sure if we can get it into core properly while keeping all the features we currently already have in mind in a reasonable time frame. Yep. It's always the goddamn time. Well, no. Yeah. But let's let's see what we can do. Put it. Yeah. And, I mean, we can also, like, put it out here. If you feel like, hey, that is an extension I'm gonna develop, reach out to us. Maybe we can help you. That would be awesome. Yeah. I would love to hear people getting just their hands dirty in this. Because in the end, anyone can develop an extension for directors. We try to make it as open and easy as possible. So if you feel like that is something you wanna try, reach out to any of us, and we'll see what the options are. Actually, reach out via the ticket. So post your comments, thoughts, work, other things that you're doing. Let's make sure it gets back to this ticket so that we can actually track that progress. And Yeah. We'll try and do the same. We'll, again, when we we do these kinds of extension developments, we'll tag that back into this ticket. So if it gets accepted and committed and delivered, we'll we'll try to remember to populate that back into the ticket. Alright. Cool. Well, well, honest sales. Wonderful having you guys join on short notice. Yeah. Was this last of the year? As usual. It's exciting. Team, thanks everyone for joining. Have a wonderful rest of your week. And as always, reach out through Discord or through the ticketing systems, and we're here to help us find out. October 15th for the next change log that covers all the changes that will be released in the new version. If you're there. And as always, this will be posted in the request review channel of the Directus IO TV. Chat out everyone. Last chat message. All the feedback from this call is has actually been typed up by Jonathan while we were talking until Thursday. I will see. Yeah. Well, I did. You guys feel free to add if I missed anything. Cheers, everyone. Cheers. Cheers. Bye bye.","22421881-df9b-45ac-9008-a65f73b19434",[1817,1818,1819],"64cc5c90-2113-43d9-b02a-476f57841394","1a4269ad-d7a5-42bb-bf94-b921d6972d5e","37101fb3-a511-4ca6-9005-acc6a5b525fe",[],{"year":978,"number":11,"id":1787,"episodes":1822,"show":1823},[1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1768,1800],{"title":1802,"slug":1803},{"id":1825,"slug":1826,"vimeo_id":1827,"description":1828,"tile":1829,"length":1830,"resources":12,"people":12,"episode_number":195,"published":1831,"title":1832,"video_transcript_html":1833,"video_transcript_text":1834,"content":12,"seo":1835,"status":19,"episode_people":1836,"recommendations":1840,"season":1841},"edffa3aa-a85e-4eca-bdc1-c05e1d8188e8","dana-lawson","1010549290","Dana Lawson, CTO of Netlify, discusses her unconventional path to tech leadership. She shares insights on effective communication, embracing AI, fostering psychological safety, and balancing technical and soft skills. ","e73894f0-193a-4422-8752-325b956110df",51,"2024-10-02","Dana Lawson, CTO at Netlify","\u003Cp>Speaker 0: But I do feel like that connection between creativity and artistry is what developers and technologists crave. Like, what are we doing? We're we're creating a digital world that is for humans to have a presentation layer to experience new and amazing things, and that's that's living.\u003C\u002Fp>\n\u003Cp>Speaker 1: Hey, everyone. Welcome back to another episode of Trace Talks. We have a wonderful guest today. I'll let you introduce yourself, Dana. But today, we have Dana, who is, an amazing person.\u003C\u002Fp>\n\u003Cp>So go ahead and introduce yourself.\u003C\u002Fp>\n\u003Cp>Speaker 0: Howdy, y'all. Dana Lawson, CTO of Netlify, x GitHub, x Envisioner, x NewRelecan, x all the things. Great to be on the show. Thanks for having me, Pedro and John.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. Absolutely.\u003C\u002Fp>\n\u003Cp>Speaker 1: Of course. We had a great, intro conversation a couple weeks ago with your with you and really enjoyed the conversation. But for those of us who or for those of, the people who might be listening, why don't you let them know who Netlify is and kind of a little bit about what you do there?\u003C\u002Fp>\n\u003Cp>Speaker 0: Netlify is a activities. My my daily oh, man. Oh, my daily activities. Spreadsheets and email. Duh.\u003C\u002Fp>\n\u003Cp>I'm an executive. No. Netlify is a composable web platform. We help enable web developers and creators alike, take their idea all the way to production. So we connect to your Git repositories.\u003C\u002Fp>\n\u003Cp>No matter what flavor of Git you use, we help you build, design, deploy with a global CDN network with, performance. It's pretty fun. And my daily duties I mean, I'm chief technology officer, but that means a lot of different stuff to a lot of different people. And as I joke, it is mainly spreadsheets and and emails and videos. But, honestly, my job is to inspire, lead, and and break through all the problems that we have, building amazing and compelling software.\u003C\u002Fp>\n\u003Cp>So I have the product design, and engineering teams. We all work together under my organization.\u003C\u002Fp>\n\u003Cp>Speaker 2: Amazing. And I know that, you know, now you're you're an inspiring leader. You're a CTO of a company, but it, obviously wasn't always that way. There was, like, a path to get there. And I think you came from a much different background before.\u003C\u002Fp>\n\u003Cp>Right? We had a call about you being sort of an artist and that. So I'd love to hear sort of where your career started and your sort of trajectory and how you got here as a CTO.\u003C\u002Fp>\n\u003Cp>Speaker 0: I mean, you know, I am so blessed to be surrounded with a lot of, like, the Silicon Valley greats. You know? And most of them went to MIT and Stanford, but not all of them. Some of them are dropouts too. But I did have the trajectory.\u003C\u002Fp>\n\u003Cp>You know, when I started out being a nerd, I was I was really into art. You know? I mean, I've always loved science and math because I'm one of those people. But, I grew up in Southeast New Mexico, Texas, and I was like, I'm gonna be Georgia O'Keefe. I'm gonna paint.\u003C\u002Fp>\n\u003Cp>I'm gonna draw. Parents weren't really thrilled about going to college for painting and drawing because that means being a poor starving artist. You gotta be really good. In the art world, you gotta be like you first of all, you gotta know some people.\u003C\u002Fp>\n\u003Cp>Speaker 1: Especially when you're alive.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. And you gotta be good. And I was mediocre and knew nobody. So, like, my odds weren't very good, and this is pre Internet. And so on a crazy whim, you know, I was like, what am I gonna do with my life?\u003C\u002Fp>\n\u003Cp>I joined the US army. How you go from art art school to the army? Hello? I'm a multifaceted person. And I really letters\u003C\u002Fp>\n\u003Cp>Speaker 1: are the same. Right? Two letters, art army?\u003C\u002Fp>\n\u003Cp>Speaker 0: I think that, you know, like, you know, it wasn't always great with spelling. I was more of a math person. I should have looked a little little bit closer. But, you know, it's funny because, like, you go and you sign up for the military in the United States, they make you take this test. Well, they used back in the day.\u003C\u002Fp>\n\u003Cp>I think they still do. I mean, I don't do army research anymore. It's called the ASVAB. And, essentially, it determines your military occupation. Right?\u003C\u002Fp>\n\u003Cp>Like, what are you good at? I scored really good on the math stuff. They didn't have an art section. I would have dominated. Go fig.\u003C\u002Fp>\n\u003Cp>They didn't have it. And this job came up in the late nineties. It's like information systems operator analysis developer, like everything tech because they didn't know what to call it. And I was like, computers need air conditioning. And here I am.\u003C\u002Fp>\n\u003Cp>Here I am because of that fateful choice of choosing air conditioning because computers needed it, my own, my own need to be comfortable. But, honestly, I loved it. I went to the US computer science school that they had made. I don't know if it's still there. It was in Georgia at the time, and it was an immersive, like, 12 months of, like, bare bones networking to all the way to, like, sea language.\u003C\u002Fp>\n\u003Cp>And then you get out and you're like, okay. And I got I got positioned to, to really create and administer all the military applications. And so from there, it's like, wow. But long story even longer, you know, I was IC. I've been I've been everything you can be in in developing and creating software.\u003C\u002Fp>\n\u003Cp>I was a sysadmin. I was dev ops. I was a SRE. Every letter in the alphabet that we attach to, to what our jobs are, I've done them like QA, TPM, TIM, PM. And, you know, I I just got bit with that leadership bug, and I really started going down the path of leading teams and not becoming an individual contributor, and then here we are.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. That's a interesting way to get into a career. There's air conditioning there, so I'm gonna jump in. Did you find that that transition from being an artist, a former art student artist, and then going into army and tech, was that an easy transition for you? Did you really enjoy it?\u003C\u002Fp>\n\u003Cp>Did you pick it up quickly, or did you just really enjoy the air conditioning?\u003C\u002Fp>\n\u003Cp>Speaker 0: I mean, one, I'm in Portland, Oregon, and and today is gonna be a 100 degrees. And I think this entire city is, like, scared. I mean, I'm looking out. I'm not going out today. It never gets this hot.\u003C\u002Fp>\n\u003Cp>But no. I mean, you know, I was already doing a lot of stuff in Adobe, like the early Adobe days in graphic arts, and, like, I had back back when Microsoft and front page. I remember that. Did y'all do any of that? Oh, it's probably a file.\u003C\u002Fp>\n\u003Cp>I love it. I love it too. Oh, well then, you know, so like I did web design when there wasn't like, you didn't know anything. Like we all know how to make websites. Like I was making some geo cities.\u003C\u002Fp>\n\u003Cp>I was doing that. I was a webmaster y'all. Alright? So but I always looked at through this lens of, like, being able to be creative. Like, that is all we actually are.\u003C\u002Fp>\n\u003Cp>Like, maybe it's not you hands to keyboards and you're taking those requirements, but a lot of the times, those early ideas are all you. And the way that we write code is individual to us even though the AIs come and take our jobs. It still is because you get to determine what goes where, how it functions, and how the end user is gonna deal with it. And, like, it can be language frameworks. I mean, you could do it in so many different ways, and it wasn't a weird push for me.\u003C\u002Fp>\n\u003Cp>It was actually really natural. I was just like, but I'm very curious. I'm one of those people that, like, it don't matter where I'm at. I'm gonna have a good time, and it's very annoying to other people. But to me, I'm like, you know what?\u003C\u002Fp>\n\u003Cp>That's just how I roll. You know? Because it's not the place you are. It's the people and experiences you have and and who you're with. And so I loved it.\u003C\u002Fp>\n\u003Cp>I I I I was just like, this is fascinating. I never, in a in a in my wildest dreams, ever thought I would be doing what I got to do. And getting to, like, live from man, the first job they gave me in the military was changing backup tapes. Like, they didn't trust me to do shit except change tapes, and they were smart because I had Yeah. I had ruined a bunch of servers at one point, but that's a whole different story.\u003C\u002Fp>\n\u003Cp>And so they're like, Lawson, you get to change the tapes. And so I got to incrementally, like, grow my skill set and apply it. And it was great. It was a great job. But I do feel like that connection between creativity and artistry is what developers and technologists crave.\u003C\u002Fp>\n\u003Cp>Like, what are we doing? We're we're creating a digital world that is for humans to have a presentation layer to experience new and amazing things, and that's that's living.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. Yeah. And I I I feel that most of the people we talk to as well, on this podcast and otherwise just in our daily sales life. We're speaking to a highly technical audience, you know, selling our product, and most of them are creatives in some capacity. A lot of musicians, tons of musicians.\u003C\u002Fp>\n\u003Cp>I think almost everyone at Directus plays some kind of musical instrument. So I I definitely agree there. So what was that like? The yeah. Go ahead, John.\u003C\u002Fp>\n\u003Cp>Speaker 1: Well, I just wanna really pause to to call out that you changed backup tapes. When I first got into tech and I realized that backups were being kept on tapes, literal tapes, I was so shocked because I I was selling data center equipment. I'm like, wait a second. Mhmm. People are still backing up on tapes.\u003C\u002Fp>\n\u003Cp>So Oh, yeah. I just had to call that out that that that brought back some memories from my past days that and probably still to this day. I know some obvious obviously, digital backups are huge, but, I'm impressed that you were changing those actual tapes because I always I knew those people existed, but you were one of them.\u003C\u002Fp>\n\u003Cp>Speaker 0: Oh, man. It's it's it it was not a glamorous job. You literally go into a colo with a tray, and, you're like, it's the a day because you have a b tapes. Alright? And there's a software called Veritas.\u003C\u002Fp>\n\u003Cp>No. It's this software called Veritas. Oh my gosh. People are gonna have to go Google this shit. Alright.\u003C\u002Fp>\n\u003Cp>Speaker 1: I sold that stuff.\u003C\u002Fp>\n\u003Cp>Speaker 0: You sold Veritas. Oh, man. No. But, yeah, it is. It is amazing.\u003C\u002Fp>\n\u003Cp>Yeah. Tapes. Well, we we should have a whole, like, discussion one day about, like, tech of past, like, the tech museum.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Sorry, Pedro. Go ahead.\u003C\u002Fp>\n\u003Cp>Speaker 2: I I didn't remember because I had\u003C\u002Fp>\n\u003Cp>Speaker 1: to call it out.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. No. Absolutely. Maybe that's another another show we could put on Directus TV digging into the tech past. You know?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 2: No. That's awesome. And so what was that moment that for you kind of transitioning from an individual contributor to a manager? You you'd mentioned you sort of got into leadership. You really liked leading teams.\u003C\u002Fp>\n\u003Cp>What was that initial pivot point, from being an individual contributor to a leader?\u003C\u002Fp>\n\u003Cp>Speaker 0: You know, I mean, I think a lot of of people that I've met that have been in these positions, there's something that honestly is natural that comes with it. And for me, it wasn't this, like, I wanna be a manager. I absolutely did not wanna tell people what to do. I can't like people telling me what to do, believe it or not. But I just wouldn't stop my mouth from running.\u003C\u002Fp>\n\u003Cp>And I was like, we should do this. We should do this. Here's why. And I'm like, on my soapbox just saying, here's why we're here's here's how we're gonna unlock this. And they're like, listen.\u003C\u002Fp>\n\u003Cp>Why don't you just tell everybody what to do? You're doing it anyway. They don't want a sideline manager. You might as well just be it. And I was like, well, you know, I've never done that.\u003C\u002Fp>\n\u003Cp>I mean, I've done, I guess, different leadership stuff because naturally you have those opportunities in the military that kind of force you into, like, these these different things to do. And I don't know. I've had jobs and jobs of days past. And, I just feel like, you know, at that moment, I was like, oh, okay. And from there, it was just like, okay.\u003C\u002Fp>\n\u003Cp>How do I change this directory? The hardest transition, I think, really was, you know, when you become into leadership and you start leading teams, a lot of us have been individual contributors, and you're like, wait a minute. And and if you have a good culture, I hate that us versus them culture. It's like your boss is just an adult too trying to make money. There ain't no us versus them.\u003C\u002Fp>\n\u003Cp>I mean, if there is, like, go somewhere else. And so I really just would approach it. The hardest part for me was not separating myself and saying, oh, you know, now this is my new role, but more of, like, this isn't my role. Like, that's not your like, for me, it was like that that's not your job no more. And that's still hard for me where you could get so immersed with just, like, in the flow zone of of writing code.\u003C\u002Fp>\n\u003Cp>And you're like, as a leader, like, yeah. You can as a frontline manager, you may do some of that, but that's not your job no more. Like, if you wanna be great, your job is to empower, enable, align, and motivate, and realize what your role is. And maybe you're a team where you're a player coach and you get to still do that, but that's not your primary. Your engineers, that's their primary.\u003C\u002Fp>\n\u003Cp>And that was the hardest transition for me making that. But to get to that, I mean, it kinda came a little naturally. And then from there, it just was like that that hunger to take on more. And, you know, it was funny because, again, I never expected, like, you know, a lot of people are like, one day, I will run my own company or I'm gonna be a CEO. I was like, no way.\u003C\u002Fp>\n\u003Cp>I never thought that. I just always wanted to do amazing things with amazing people and and hopefully change the world. Like, to me, that's more important. But, yeah, I mean, it wasn't it wasn't a hard trajectory. I think, again, like, separating what you used to do seems to be the hardest part for people that transition.\u003C\u002Fp>\n\u003Cp>And there's only been, like, 1 or 2 times in my career where I've ever met anybody that was like, I wanna be a manager. And I'm like, what? Like, that's your goal? Like, why? Like, why do you wanna do that?\u003C\u002Fp>\n\u003Cp>And and usually, when they wanna be a manager and they're that resound, it's for the wrong reasons, like, straight up. It's not it not all the time, but I mean, I've heard one person's like, oh, I wanna be a manager. So why do you wanna be a manager? Because I could tell everybody what to do, and I I could quit fighting battles and, like, we'll just I could just get it done. And I was like, what planet do you live in?\u003C\u002Fp>\n\u003Cp>Like, because people I said I said, welcome to management. Your job is just fighting battles and trying to get, you know, people to to to agree and commit or disagree and commit, but commit is your out is your long goal. And so, like, it's you really feel like a used car salesman. Half the time going like, no. So I no.\u003C\u002Fp>\n\u003Cp>That price is not gonna work. I'm sorry. This is not gonna work. Yeah. See, it's not if you have a fantasy of command and control and leadership, like, again, you gotta find that the culture that that's how that's how they vibe.\u003C\u002Fp>\n\u003Cp>They're out there. But like most places where a lot of people are happy to work, they don't want that.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. You mentioned putting down some of the tasks that you had previously done as an IC and and finding that a little bit of a struggle to put those tasks down. And then you also mentioned kind of fitting right into leadership. Was that was there any learning curve, or is your personality one where you whatever the task is on your plate, you'll figure out and do it? And then I guess the second part of that question is, were there parts?\u003C\u002Fp>\n\u003Cp>Because you you didn't go in searching for leadership. So did you get into leadership and managing teams and say, oh, man. This spreadsheet stuff sucks. You know? Like, I don't like this part, but I'll do it anyways.\u003C\u002Fp>\n\u003Cp>But so I guess what was that learning curve like, and were there parts where you're like, oh, I didn't even know I'd have to do this stuff?\u003C\u002Fp>\n\u003Cp>Speaker 0: Oh, man. You know, I think the actual learning curve is is not knowing what you do need to shift and having to grow into that on the other side of the house because you can really look really shallowly and be like, well, what's the big difference? You're just breaking down tasks and giving projects out. That's what leadership is. Absolutely not.\u003C\u002Fp>\n\u003Cp>I think the learning curve is really dialing into, okay, well, how do I really start having effective communication? Like, obviously, I was somewhat good at it. I'm now in this position, but how do I refine it? How do I talk to different personalities? Because as a tech leader, an IC, you're just talking to mostly other engineers, sometimes product people, but they're still your people.\u003C\u002Fp>\n\u003Cp>Right? Now when you become into leadership, you have different stakeholders. And I think the biggest opportunity to learn is to understand how to speak their language, how to communicate effectively, and how to distill that information down so that there's still alignment between both parties so that you're not communicating in a way where this side hears something different than this side. And sometimes that can happen because breaking down really audacious technical things for people that are more on the business side, sometimes when they're motivated by different things, they'll hinder different things. And the example I give is, like, you know, let's say a success manager comes to you and says, so where are we at with that bug?\u003C\u002Fp>\n\u003Cp>And and and the engineer is like, well, we're gonna be code complete in 2 days, and we're gonna ship it tomorrow. Great. You're shipping it tomorrow. It's like, no. No.\u003C\u002Fp>\n\u003Cp>No. No. No. Stop. Stop.\u003C\u002Fp>\n\u003Cp>Stop. Stop. Yes. Technically, we are shipping it into staging and integration tomorrow, and there's still more behind that. And what you mean by code complete means something different to go to market.\u003C\u002Fp>\n\u003Cp>So you have to be really that translator or you're gonna cause friction. And as a manager, guess what your only job really, really, really, really is, is to remove friction. At the at the end of the day, your job is to remove friction. And the way that you do that is to to the the the morning's problems that cascade into the night. And you're constantly being agile and saying, where do I help people not run into anything that's impeding them from what they're trying to do?\u003C\u002Fp>\n\u003Cp>That's really all you gotta do. So I think that was the hardest kind of, like, is is is and then and then as you go further into it, right, like your frontline manager, you're like, okay. Maybe I'm talking to support. Maybe I'm talking to marketing. You go to director.\u003C\u002Fp>\n\u003Cp>Now the table stakes are like, now you're now you're learning the economics of operating and running a business. Right? K? Now it's like contribution margins, cost of goods sold, CapEx, OPEX, all these fin financial terms that you think we have acronyms? Go talk to an accountant.\u003C\u002Fp>\n\u003Cp>They have just as many. K? And sales, I don't even know if you're talking about half time. BDR, SDR. I'm like, I don't know.\u003C\u002Fp>\n\u003Cp>I do now though, but that's the stuff that like as an engineer, you don't learn that. Nobody taught there's no school to teach. There's no engine. We can go like I said, you can go to Stanford, MIT. They ain't gonna teach you about how to look at a financial forecast weighted against 2 growth motions and and outputs and forecasted.\u003C\u002Fp>\n\u003Cp>Like, it's all things that maybe you took a a side business class, but most of the time you're learning it. You're learning it on the job as you as you move up that corporate ladder.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. That's wild. I think that, you know, I I just personally, that's something that I've been focused on a lot is really understanding different points of view, and it's something that I sort of I don't know. I mean, maybe it's the Libra in me just trying to, like, balance everyone and play like the the diplomat in most situations. But what do you think that you pull from, to to have that sort of knowledge now and being that translator and being able to effectively communicate and and be that diplomat between, you know, your teams?\u003C\u002Fp>\n\u003Cp>Speaker 0: I mean, I have a big mouth, Pedro, so, like, you wouldn't believe this, but, like, active listening is so important and really just practicing on my mind runs a 1000000 1000000 miles a minute. Like, I am like I've I've main I've I've been able to challenge my ADHD into something productive, which is good, not good. Okay? But it's really about taking that moment and forcing myself to really go into active listening mode and pushing against those instinctual, I need to go. I need to execute.\u003C\u002Fp>\n\u003Cp>I need to push because you're so trained to have maximum productivity. And and in software engineering, it's about that tempo of delivery. And then when you're on the other side and you're working through really how do I express this to 100 of 1000 of users and get to start internally with my team first, like, it it's isn't it is again an art. It is it is something that you have to inherently start to develop. And what I do is I try to spend time just learning their business, understanding their workflows, putting on that product research mindset that, you know, we have as engineers and technologists to say, well, if I wanted to understand my ideal customer profile, well, my ideal customer right now is my partner over here on this side of the business.\u003C\u002Fp>\n\u003Cp>How do they work? How do they operate? How can I meet them where they are? And so also just, like, talking to other mentors and, like, people and, like, getting advice and, like, seeking feedback. I don't think I've ever not had a a, you know, an employer review tell me you're too verbose.\u003C\u002Fp>\n\u003Cp>I mean, it's a work in progress. I'll always be too verbose. But, like, really taking in that feedback and and trying to understand where we can be better, so that self reflection, those 3 sixties, so all those things that you can do and bringing them in together to understand. But I think active listening, really, which there's a whole bunch of techniques out there to do that.\u003C\u002Fp>\n\u003Cp>Speaker 2: No. Absolutely. And and do you think that your your experience and your previous background in, you know, the alphabet soup of all the roles has helped in that? Like, you've had a sort of understand the different roles that you that you manage and the different roles that you sort of sell to internally and understand the languages that they speak?\u003C\u002Fp>\n\u003Cp>Speaker 0: Oh, yeah. I mean, I love it. That's where I always encourage people to get out of their comfort zone. Like, do it. Like, I've done so many things that I never won obviously, was qualified for.\u003C\u002Fp>\n\u003Cp>So thank you to all those people out there that believed in me, because, like, I don't know if I would've put myself in that position, but here we are. And it's really about taking opportunities and and and finding those spaces within what you know and filling the gap. Right? Like, it's really human nature to double down in the areas where we're comfortable, where we can sit and say, I can just crush it. And in times of duress, we do that.\u003C\u002Fp>\n\u003Cp>We seek that comfort in in in repetitiveness and consistency. I don't like it. I get bored. I like it sometimes. And most of the time, like, when I'm getting comfortable, I'm like, oh my gosh, I gotta go solve.\u003C\u002Fp>\n\u003Cp>I gotta go find my next problem. And so I push and I've had the opportunity through the alphabet soup to do stuff that was kind of orthogonal to what I already had known. Like I got a wonderful opportunity to do business development, like pure business development. And I when I got tapped to do that, I was like, are you sure? I was like, I have been an I have been an engineering leader for a 1000000 years.\u003C\u002Fp>\n\u003Cp>You want me to do biz dev? Like, that's on the other it is on the other side of the house. I do not even know. Yeah. But they're like, no.\u003C\u002Fp>\n\u003Cp>Our business is talking to developer. Our business is expansion. Our business is helping, you know, really the value of our platform be open to everyone. And so when you think of business development, there's nobody better than a technologist that's right in it sometimes. However, you still need that other side of business acumen and that understanding of the sales operation and motion to be able to be effective because at the end of the day, the stakes are different, and and it's just a different type of ball game than delivering software.\u003C\u002Fp>\n\u003Cp>And that experience alone, I'm like, like, you can't go to you you can't go to school to get to learn that. And to me, it just strengthens that the inner team empathy that you need to have a startup because you're all pushing for the same goal and it's gonna be uncomfortable even if you are in your sweet spot. You you're gonna have moments in your trajectory where you're like, what am I doing? Why am I doing this? And and having empathy holistically helps everybody be better to deliver what they're doing.\u003C\u002Fp>\n\u003Cp>You can't just have a well oiled machine on one side of your house and then the other not be working or it's all gonna fail.\u003C\u002Fp>\n\u003Cp>Speaker 2: No. I I absolutely agree in it. And, you know, I've seen that at the different companies that I've been in. Sometimes it's hard to align, especially when it's the technical teams versus the less technical teams and having being able to speak that common language. Is that something that obviously, they don't teach that in school, like you've mentioned.\u003C\u002Fp>\n\u003Cp>Is that something that you've taught as a manager to the the folks that you manage on the technical side, like balancing, you know, the technical acumen with the people skills and sort of the the goals of the business, which are, you know, ultimately to to earn revenue and keep everyone employed?\u003C\u002Fp>\n\u003Cp>Speaker 0: Heck. Yeah. I mean, I think that's my number one job. Right? Like, I'm always trying to get my leaders to work me out of a job.\u003C\u002Fp>\n\u003Cp>Like, the best the best, you know, example of leadership is the person behind you taking your job or them going on to do the job you were doing. That's it. That's all I live for is to see see the people that I had the opportunity to partner with, go on and do bigger, better things, and me having a small little piece of that narrative. And hopefully being positive. I know it ain't all roses, but I think that's something that you have to do because it doesn't, you know, I talk about, oh, it all comes naturally.\u003C\u002Fp>\n\u003Cp>This is the part of the house that don't come naturally. This is the part where you have to be curious. You have to have your humility low and do a little homework, do a little research, and and try to meet them in the common ground. And I try to do that. Like, a lot of us have been utilizing, you know, objectives, key results, or other frameworks, and they can get so convoluted with with what you see the lens of the world.\u003C\u002Fp>\n\u003Cp>And as an executive, it's about that abstraction so that everybody can understand it. If they can't, then why why? And so from a manager training perspective, manager enablement is so important unless, you know, I don't think a lot of places are gonna get managers and be like, listen. I expect you to be a career manager. So, like, I ain't gonna teach you nothing new.\u003C\u002Fp>\n\u003Cp>Just keep the job rolling. Like, I don't know. I've been around for a hot minute. That's how it was in my day when we had cubicle cities. You were just, like, waiting for somebody to retire to get your job, to get the next job.\u003C\u002Fp>\n\u003Cp>It's not like that, and it hasn't been like that. And so, again, as a leader, if you have your goal set on really raising the bar on your team and getting them to the next level, it's finding those opportunities and those skill gaps and then teaching them that that common language. And there's so many there's so many darn resources because it's not just business speak, but when you have also a growing company and a global customer base, you also need to know culturally, like, the nuances. And there's some great books out there. I always recommend the cultural map.\u003C\u002Fp>\n\u003Cp>It's it's a book that helps you distinguish really how if you're like an American, how you show up or Western society. And it's really an interesting read. And I think those are some of the things that I try to encourage my leadership team as they're working on that voice is be customer centric, internal and external. And that means business acumen and speak, but also just human to human and understanding how they how they were brought up in this world and how they culturally represent themselves.\u003C\u002Fp>\n\u003Cp>Speaker 1: So I think it goes\u003C\u002Fp>\n\u003Cp>Speaker 0: hand in hand.\u003C\u002Fp>\n\u003Cp>Speaker 2: So many nuances to it because, you know, yeah, it's not just internally between the different teams and the way that they speak, but also culturally, you know, you've got companies that are now global with remote work just being sort of the the default in a lot of these areas. So maintaining that company culture is also important, and these small little things can cause a rub between, you know, the Americans and the Europeans. They have totally different styles of work life balance and and culture and everything. So, yeah, I think that's that's really important. Are there any specific, like, exercises or resources that, or, you know, strategies that you implement within your teams to try and keep that cohesive\u003C\u002Fp>\n\u003Cp>Speaker 0: culture. I mean, I mean, definitely, you know, we we try to do enablements and trainings, not just for managers, but for people, and they cross the chasm from technical skills to soft skills. Like, you need both, whether it's public speaking. Because public speaking is actually another good opportunity to refine your message house, your internal message house. If you can't say it in an elevator pitch, then you're probably not speaking common enough language so that people can understand your value.\u003C\u002Fp>\n\u003Cp>So I always recommend, like, get some speaker training. Believe it or not, I've had it. They teach you some tips. They're really, really good. But in addition to that, I would also say that, you know, some good resources would be writing things down with clear lines of ownership to also help clear up some of the ambiguity.\u003C\u002Fp>\n\u003Cp>Because, again, when you're talking about multicultural teams, from many, many different countries and and and English is second language for peeps. I mean, at Netlify, I think we have 17 countries represented in our in our little amazing startup. That's a lot of different people from a lot of different places doing a lot of different things. As we utilize tools like the DAISY, drivers, approvers, contributors, inform, and these frameworks so that you can drive decision making. And sometimes you have to leverage just written.\u003C\u002Fp>\n\u003Cp>Because maybe maybe maybe maybe you're having a block where there's friction within the the spoken communication. A lot of people can read better than they can speak, self included. Right? Like, I can I can I can speak I can read Spanish all day and I can speak it? But, I think that there's those opportunities too and other things that you can utilize to find that alignment.\u003C\u002Fp>\n\u003Cp>So I'm a big fan of DCs and RACES to really ensure you have clear ownership and that that's been, understood. And then also, you know, really just pushing a written culture. Like, don't keep stuff in the ether. And there's so many come on now. Like, there's so many tools that do it for you.\u003C\u002Fp>\n\u003Cp>Like, 4 years ago, we didn't have a little AI recorder that did a transcript and a summary. You can do all that now. Don't tell me you can't. And I I tell people, I'm like, you're not comfortable. I don't know.\u003C\u002Fp>\n\u003Cp>Go use go go go use your your model or your choice, your chatbot of choice. I don't care.\u003C\u002Fp>\n\u003Cp>Speaker 2: It's cool. I training even as a training tool. Right? Like, you can have conversations with these things, and that's only improving. I mean, I've done job interviews even where I'm like, simulate this,\u003C\u002Fp>\n\u003Cp>Speaker 0: Yes.\u003C\u002Fp>\n\u003Cp>Speaker 2: Simulate. You're the you're the you're interviewing me for this role. Here's the resume. Copy paste. Right?\u003C\u002Fp>\n\u003Cp>And have a little conversation. Those those types of things can even be be pretty\u003C\u002Fp>\n\u003Cp>Speaker 0: Use them. Use the tools. Like, we're all learning them. And, like, again, that's a double edged sword. You're like, I get to understand this, whatever we're saying AI is and how to be better.\u003C\u002Fp>\n\u003Cp>And I think, you know, you gotta be a technophile. You know, that's why you're in this role. You gotta stay on the latest. You gotta love it. You gotta know it.\u003C\u002Fp>\n\u003Cp>And I I do. I encourage it. I'm like, go. Go go go go ask go ask go ask the AI for you.\u003C\u002Fp>\n\u003Cp>Speaker 1: At the\u003C\u002Fp>\n\u003Cp>Speaker 2: end of the day, like, refining your communication skill set. I I keep seeing these ads for, like, Bob Iger's master class or whatever. I think that the thing he's saying in these ads is just like your your vision or your idea is only as good as your ability to articulate it. And, you know, back to your point, it's like having that ability to to communicate those ideas. If you can't do it verbally, like, at least writing them down and continuing to practice those so that you can win over your team so you could become a better a better leader, better even a better individual contributor.\u003C\u002Fp>\n\u003Cp>Like, everyone's got ideas. So, your ability to communicate that with others, I think, is is probably the point here.\u003C\u002Fp>\n\u003Cp>Speaker 0: You better believe it. I mean, shoot. Like, a lot of, a lot of engineers have that that leadership charisma, and they want to refine that side of the house. Let them, like that's the I mean, again, I think that comes back to the harder part. You know, we are so so lucky to have this advanced toolset around us that we can go and source information.\u003C\u002Fp>\n\u003Cp>We can ask questions. We can autofill. We can move, but you gotta put in the work on the other side. And so, like, I definitely see, you know, an opportunity with getting people more comfortable and, again, utilizing the tool sets to do so. And that's where, you know, like you said, using it to, like, mock interview.\u003C\u002Fp>\n\u003Cp>Like, yes. Go do that. It's it it some of them are good enough. I mean, some of them are are not great enough, but it gives you also some which is interesting, I think, because people were like, oh, I don't trust the AI and the models, but it's private. It it's better than going into like, can we role play in Slack?\u003C\u002Fp>\n\u003Cp>Like, nobody wants to do that. Like, you\u003C\u002Fp>\n\u003Cp>Speaker 2: know using even, like, there's an app that I've been using called Sonja. I actually spoke to the team a couple weeks ago. They were gathering some feedback, but it's an AI therapy app. And what I kind of started to realize, and maybe this is what some of these apps are sort of you know, they've they've modeled everything after their CBT techniques, and it has, like, an in-depth knowledge of, therapeutic techniques. But what I've realized is there's this barrier when you're talking to a human.\u003C\u002Fp>\n\u003Cp>Obviously, you're presenting the best version of yourself. You're not being as raw as you could be.\u003C\u002Fp>\n\u003Cp>Speaker 1: Mhmm. And\u003C\u002Fp>\n\u003Cp>Speaker 2: there's always some sort of, you know, I don't know, like subjectivism on the other side. Mhmm. And you know that somebody's, you know, judging you in a way where when you talk to the AI, there's zero judgment. You're talking to a computer. Your mind sort of knows it.\u003C\u002Fp>\n\u003Cp>So you speak to it a little differently. And for something like therapy or something like training, I remember back in college we used to do these sales role plays. Those things are terrifying. You're doing it in front of the class with the professor and it's like, you know, you're getting graded on this. And, to do that with AI, I think would be, you know, it's just such a such an improvement.\u003C\u002Fp>\n\u003Cp>Even if it's not perfect, it's it's still going to at least let you drop the guard, speak in your natural voice, and and learn effectively.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. I I also think with AI tools and tools in general or I guess when you're dealing with humans, people are afraid to make mistakes in front of other humans. And so with an AI tool or a software tool, you're like, oh, I just made a mistake. I can just redo it again. You know, as a salesperson or as a if I do code wrong and I'm just testing it, it's easy to it's easy to make a mistake.\u003C\u002Fp>\n\u003Cp>So, Dana, do you find that you know, you you mentioned encouraging people to go try a ai tools and encouraging people to go test new things. Do you find that after you encourage them a couple times, they're more more willing to go and try new things on their own, or they're more willing to kind of understand that making mistakes is okay. Because similar to what you're saying, Pedro, is when I try something with an AI tool and I make a mistake, it's, oh, next turn. Let's go to the next turn. So I feel like the the stigma of making mistakes is removed when you when you have a barrier removed like that.\u003C\u002Fp>\n\u003Cp>So do you encourage your team members, Dana, to go and try new things and then find them down the road trying more and more things without having to be encouraged to go try those things?\u003C\u002Fp>\n\u003Cp>Speaker 0: Oh, definitely, man. I love growth mindset. Like, you know, and and and what it comes down to is putting that human imposter syndrome, shame, or what other baggage you have thinking you ain't good enough down and building a place where people can do that and with psychological safety. Like, I totally I tell them all the time. I was like, y'all know that was written by AI because it's so it has all the right commas.\u003C\u002Fp>\n\u003Cp>It's not saying random shit. It's it's actually a little bit more, more focused. And I just try as a leader to not not be embarrassed about it either. It's like, hey. I'm human too.\u003C\u002Fp>\n\u003Cp>And I think, like, as as somebody that's creating and and producing the next set of advanced developer tools, you best know. Like, how how could you come? Like, where's your street cred? Like, people are using this stuff. I think you have to be really public about it, but also, like, be skeptical.\u003C\u002Fp>\n\u003Cp>Like like, it's not perfect. I don't a 100% trust it either. But again, I think the benefits are more than the cons right now if you utilize it like a tool and really put the right boundaries of how you're using it to function. But again, for me as a leader, it's taking out the hesitation because you gotta remember. Right?\u003C\u002Fp>\n\u003Cp>Like, tech has been through it. People are freaking like, you know, it's a doom scroll of, like, what's what's what's busted up in Silicon Valley right now. We're having a moment. I hope we're on the other side of it. Those feds cut that interest rate.\u003C\u002Fp>\n\u003Cp>But I think, like, people are paranoid. Not everybody, but, you know, a lot of people are like, hey. You know, the economy's unstable. Like, I don't wanna be found out. I'm like, no way.\u003C\u002Fp>\n\u003Cp>You're gonna be better. Like, if our number one goal again is to remove friction and increase productivity, you you should be doing your part. And, again, I don't think a lot of it is on the the hard skills of, like, utilizing these tool sets to be better. But on the soft skills, having those those moments to be like, I wanna ask it this really dumb question that maybe I wasn't comfortable going in Slack and asking it, or maybe I don't wanna even talk to my mentor. You know, 1, you know, don't put all your eggs into that AI basket and trust it out.\u003C\u002Fp>\n\u003Cp>But I think as a guiding point, like, you're gonna start self serving those answers to yourself. And who doesn't wanna self serve? Like, nobody wants to talk I mean, I love talking to people because, like, that's what I do, and I'm from the 1900. But people this day and age, they won't be talking to nobody. Okay?\u003C\u002Fp>\n\u003Cp>Yeah. And, like, that's cool. They're gonna talk to you when they want to, but they're gonna be more comfortable behind the keyboard. And we just have to embrace that and find different ways that people do communicate, whether it is short form video or, say, other AI tools, because that's the part that they don't teach you. And as being a leader, like, you have to pay attention to societal norms to understand how the current generation and the next are are taking in information, especially if you're a provider of how to do that.\u003C\u002Fp>\n\u003Cp>And if we're in software, we all play a part in that experience.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. I agree. And I I think that at the end of the day, you know, asking like you were saying, asking the AI, maybe some of the questions that you think might be dumb or, you know, getting sort of that, introductory knowledge about something out of the way. Maybe it does, if used correctly, break down the barriers between human to human connection where it's just like, you know what? I can actually do some of this research on my own in a very quick and concise way before I come to the table with my ideas or Yeah.\u003C\u002Fp>\n\u003Cp>Yeah. So\u003C\u002Fp>\n\u003Cp>Speaker 1: How how how important and I think this is a good a good thought on leadership. How important do you think it is to cultivate a a culture internally where, people aren't afraid to ask those what might be considered a stupid question. Because I've I've in the world of alphabet soup, there's so many acronyms you never know. And some people might be afraid to ask that question. What does BDR even mean?\u003C\u002Fp>\n\u003Cp>Or, I mean, I hear acronyms all day. I don't know what they mean. So how important is it to build a culture where people are comfortable asking those questions to other humans? You know, you can go ask AI. But I think as a leader, you also wanna have that culture where people aren't afraid to ask questions because in the end, they just wanna be smarter.\u003C\u002Fp>\n\u003Cp>Speaker 0: I mean, I think it's a it's a it's a big challenge, and you're always fighting it because, you know, as your team scales, you lose some of those opportunities for interpersonal relationships. Right? We've all heard Dunbar's number of a 150. You can't know everybody past that. I disagree.\u003C\u002Fp>\n\u003Cp>I don't know. I came from a small town, and I knew everyone. But, but I do think that you it starts from the top. You know, your job as a leader, especially as an executive leader, is to set the tone and the pace. And, like, if you can show up and be your true authentic self, that gives people permission around you to do so.\u003C\u002Fp>\n\u003Cp>And that means you're gonna probably f up. You're gonna say something that you're embarrassed about. Like, trust me. There's not a week that goes by that I'm just like, oh, Dana Lawson. Why did that come out of your mouth?\u003C\u002Fp>\n\u003Cp>Why are you you? But you know what? I am me, and you're you, and I want you to be you at work because putting energy into being somebody else is taking away from your productivity. I ain't got time for that. I want you to spend those mental calories on on my stuff, which is getting shit done.\u003C\u002Fp>\n\u003Cp>And I think you have to really, again, make it really known. You have to practice what you preach, and then give people those opportunities to find the psychological safety for them to be okay doing that. Again, you know, you can't you could just saunder people's experiences. You're not living them. They're theirs.\u003C\u002Fp>\n\u003Cp>So you have to just kind of think about, well, how how would somebody that is 12 hours on the other side of the world take this information if they don't get it from me? How do we form a relationship? So again, establishing a written culture, you know, coming from the top down, being your true authentic self, that means messing up and being being a little scared sometimes you are. It's okay. That, like, that again, that gives permission to mess up too because humans are fallible.\u003C\u002Fp>\n\u003Cp>But then finding other avenues where people can feel safe to be able to share and and communicate, whether it's surveys, Slack messages, written, town halls, q and a's. Do all the things because you're gonna need to do a wide splatter to get all the differences on how people need to be felt and heard. But, again, I also try to encourage that, like, one to 1, like, come and ask me. You know? I when my AI startup, AI Migos, you'll pay a premium for the real Dana Lawson.\u003C\u002Fp>\n\u003Cp>What the day Dana bought, you know, I don't know, $20, I'll give you yes or no's. I joke about that, but, like, I want people to come to me because there are some things that you're just not gonna get on the other end, but utilize it as a tool. Practice. Like, get yourself comfortable. But when it comes from leadership, there's nothing more powerful than people see that you're authentic, genuine, and real.\u003C\u002Fp>\n\u003Cp>Because I don't know. People can read it when you're full of baloney. Like, you think they ain't. They are. And maybe not everybody is, but you got some skeptics out there going like, that person full of shit.\u003C\u002Fp>\n\u003Cp>And so, you know, I just say, don't be. Be you.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. I love this idea of, like the Dana bot or like imagine the CTO at a company of thousands of people, that clearly it doesn't scale. You can't have a one on one conversations with everyone in the company or even the CEO or something like that. But cloning cloning that person's personality. And anyone now can have a one on one with the CEO at any time\u003C\u002Fp>\n\u003Cp>Speaker 0: Oh, man.\u003C\u002Fp>\n\u003Cp>Speaker 2: By the CTO. And if if you want to book the real thing, like, yeah, that that may happen as well, but anyone at every\u003C\u002Fp>\n\u003Cp>Speaker 0: rag is gonna be crazy. Right? You're rag. You're like, wait a minute. No.\u003C\u002Fp>\n\u003Cp>Let me let me make my rag on, lots of howdy's in y'alls. I but here's this you know, I think that that's gonna happen. That's what Hollywood's scared of. I don't blame them. But, you know, the the reality is is, like, you can't take away human to human.\u003C\u002Fp>\n\u003Cp>And as a leader again, you just have to you have to stay in line with what's happening out there and who your people are and what's important to them. And right now, you know, it's it's not super easy. There's so much going on. I mean, we were all in a pandemic for 4 years. That was probably the hardest 4 years of leadership for a lot of people because everybody on the planet was fearful.\u003C\u002Fp>\n\u003Cp>Nobody knew what was going on. And the stuff that you did know was happening, you didn't know what was really happening because there's so much information and be blasted at you. And it was a really interesting and I believe challenging time for a lot of people, you know, separating that work and life balance. And, like, the world's just gonna continue to get more complex, and there's always stuff going on. And, you know, you can only play your part and do so much.\u003C\u002Fp>\n\u003Cp>So I always say, like, you know, like, while you're at work, I want you to not have to worry about some of those things, but focus in, get all those chores, get the friction out of place, and just feel good about producing. Because I think a lot of humans feel good when they produce because that's what working and living is, is seeing the output of your energy.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. I think, yeah, I think sometimes that's important to remember is to still be able to contribute in certain ways. If we go back to a part at the beginning of this conversation, you mentioned having to learn to put down some things that you used to do as an IC. I've seen CTO calendars before, so you might not have any spare time these days. Do you still get to contribute at all?\u003C\u002Fp>\n\u003Cp>Do you ever get to code or do anything fun kind of to go back into your IC role, Or is it just all spreadsheets these days?\u003C\u002Fp>\n\u003Cp>Speaker 0: If if if I touch production, like, the team would be like, smack. Get out of here. Come in. What do you what are you doing in here? So no.\u003C\u002Fp>\n\u003Cp>But no. I do on-site. Like, we're there's always cool stuff coming out. Right? Do I do it at work?\u003C\u002Fp>\n\u003Cp>Mostly on the architectural level, I do a lot of, design work implementation and and that piece of it. But day to day, like, I get to I I have to try it out. Like, we release something. I'm the first person that's going down there, and I'm like, oh, look at this new Astro template. And I'm a terrible web developer.\u003C\u002Fp>\n\u003Cp>Let me just be really real with y'all. Like, not my forte. More back end. But that's why I love doing web development because I'm so bad at it. And all these tools for they they they make it a little easier.\u003C\u002Fp>\n\u003Cp>So I'm always trying that stuff out. I'm the 1st to give it a go. I'm like, oh, but, you know, I think you have to know what you're doing and pushing. Like, Netlify, we're always pushing out these core primitives that are really kind of nascent to specific things that you do within, how data transmits from the Internet to your computer and so forth. And there's so much to do on.\u003C\u002Fp>\n\u003Cp>And so to really get that deep, you you gotta kinda get in there and know what's going on on on different levels of the stack. And so I think, again, you know, for myself, it can't just be all the fun soft stuff. As I say, I'm basically Napoleon Dynamite's brother Kip. I love people, but I love technology too. And so on my free time, because I get my peoples at work, believe it or not, I do a lot of stuff on the side and I'm always trying it out.\u003C\u002Fp>\n\u003Cp>But, yeah, I mean, it's like bits and bobs there. I've gotten really good at macros though. So if you do need some spreadsheet food, like, you know, like, call me.\u003C\u002Fp>\n\u003Cp>Speaker 1: You're entering those, Excel competitions. You're like, I know how to do that.\u003C\u002Fp>\n\u003Cp>Speaker 0: Shoot. No. I'm like and now I'm I'm telling you, this is one area where I'm like, thank you AI. I'm like, no more. No more.\u003C\u002Fp>\n\u003Cp>No mas. I mean, I could do it, but why would I? When I can be like, the train left the station at 2 PM and the other train left at 3 PM. And there's a person that gets I mean, I love it. AI is good at that kind of stuff.\u003C\u002Fp>\n\u003Cp>Speaker 1: I hated those questions in school. It just was like, I don't it doesn't matter. Are they gonna crash into each other or not if they're not?\u003C\u002Fp>\n\u003Cp>Speaker 0: It's like spreadsheets. You're like, if I take if I take column e and then I divide it by c and then I pivot it to your yeah. I know. I don't\u003C\u002Fp>\n\u003Cp>Speaker 1: do it. Yeah. So so what's what's some of the favorite things that you've learned in your climbing to CTO? You know, what's some I guess, since this is kind of a podcast on leadership, what are some of the best leadership things that you've learned that you had no idea you would need to learn or maybe some things that you knew you weren't good at, but you've honed that that skill set? So what are some of the favorite things you've gotten great at over your your time?\u003C\u002Fp>\n\u003Cp>Speaker 0: I would say, really, just I don't know if I'm great at it, but effective communication, I feel like I got a sweet spot for it. It's it's not been good. I mean, like I said, like, a a 1000000 miles a minute. So really practicing that focus, which I'm always still trying to refine my talking points. That's been one that I've been, like, you know, I feel kinda proud about where I've I've started at because it was a lot more.\u003C\u002Fp>\n\u003Cp>Public speaking, like, you know, you never like, getting up and talking to a 5,000, 10000 person audience is is scary. Like, it is so scary. You're like, what am I doing up here? Some people are natural at it. There's like, put the mic on, and let's go.\u003C\u002Fp>\n\u003Cp>And, like, you know, I play a natural at it, but I'm like, I'm scared. Every time I'm like, oh my god. Even this podcast this morning, I'm like, oh my god. Why am I doing a podcast?\u003C\u002Fp>\n\u003Cp>Speaker 2: Couldn't tell when you're when you're giving a talk at the the Netlify conference. How\u003C\u002Fp>\n\u003Cp>Speaker 0: was it? I know. I'm always scared. Always scared. But I would say, you know, public speaking is is one to crack over and, like, that's a hard one for leadership.\u003C\u002Fp>\n\u003Cp>Public speaking alone, I just think it's it's not, you know, not natural to a lot of people and especially, like, hardcore technology is not very natural, but as leaders, you gotta be great at it. You gotta refine it. So it's all in that communication vein. And, the other part is just, like, humility. Right?\u003C\u002Fp>\n\u003Cp>I'm a driven person. Like, we all have ego. We all wanna, like, crush it, win and be the best. Man, it don't take nothing but a big smack to show you, like, yeah, you ate perfect. You broke stuff and, like, it ain't all roses.\u003C\u002Fp>\n\u003Cp>And I try to be, you know, I try to really have self reflection on how to show up and be better, but it takes a lot of humility because your ego can get in the way of your decision making. And, you know, sometimes when you're doing something so excited, you know, you you you you get that that tunnel vision. And I think as a leader, like, really taking those moments, self reflecting on how you show up, honing your communication, and putting your ego at the door is gonna make you effective. And, really, there's no secret recipe except, you know, continue to be smart, hungry, and humble with everything that you do, and and learn. You know?\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. That that's amazing. And, you know, being that we we kinda have a technical audience that views this podcast, individual contributors looking to make their way towards, you know, leadership and management. Are there any other, like, kinda as we come to the the end of this conversation, like, are there any other pieces of feedback besides the one that you've just given that that you'd like to leave with with the audience?\u003C\u002Fp>\n\u003Cp>Speaker 0: I would tell the I would tell these ICs, like, talk to your freaking manager. Build that rapport. Be transparent about your goals. Like, don't listen to me. If you wanna be a manager to because you just wanna be a manager, like, go explore it.\u003C\u002Fp>\n\u003Cp>Like, go. Even if it is for some reason that may not align with the reality, you're gonna learn by having that conversation out in the open. And if you're not sure, you can always start picking up some advanced things to give you those skill sets. Like, a lot of us have engineering levels and leadership levels, and they're in plus. As you go up and you get better, you get more natural leadership behaviors on your engineering levels as an IC and definitely on your manager levels.\u003C\u002Fp>\n\u003Cp>And so there's this expectation once you go across the chasm of senior engineer, like, you should have some leadership capabilities because your job is not just to execute, but it's to break down and execute. And to be able to break down and execute, you have to talk to people, you have to motivate them, and you have to align them. You may not be their manager, but believe me, you are just as influential, if not more. And if you really want to drive towards a position where you have more strategic input from a wider scale, again, it's about bridging into those things that you hadn't done before and honing those crafts while you have them right now. Do it now and and and and, you know, form that relationship.\u003C\u002Fp>\n\u003Cp>Managers aren't all scary. Some are pretty good. Most of them are pretty good. And if they're terrible, do what everybody else does. Leave them.\u003C\u002Fp>\n\u003Cp>It's alright.\u003C\u002Fp>\n\u003Cp>Speaker 1: Well, yeah, it's it's I think that's important. Communication is big no matter no matter whether it's at work or relationships or family or whatever it ends up being. I've I've really appreciated the conversation. I'm sure Pedro has as well. Is there any last minute things that you want to promote?\u003C\u002Fp>\n\u003Cp>Maybe Netlify or your Etsy art store or analog tape backups?\u003C\u002Fp>\n\u003Cp>Speaker 0: Oh, man. Analog tape backups. Yes. Come see me at veritas.dl. No.\u003C\u002Fp>\n\u003Cp>No. Oh, I'm gonna go Google that after this. No. I do. As Pedro said, he saw me at the Netlify conference.\u003C\u002Fp>\n\u003Cp>Netlify is having our own developer focused conference, October 2nd 3rd at the amazing Fairmont Hotel in San Francisco. So if any of y'all are listening and wanna come out and hang out with me and and see me pretend that I'm not scared even though I'm absolutely scared, apparently, Pedro said I was good at it. Thank you. That really actually helps. Come on down.\u003C\u002Fp>\n\u003Cp>Come hang out with us. Tickets are on sale right now, and there's also, I heard, some codes out there. If somebody, you know, doesn't have all the cash to spend and still wants to come and hang. Awesome.\u003C\u002Fp>\n\u003Cp>Speaker 2: Well, thanks so much for your time, Dana.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. I really appreciate it. Don't think I've smiled as much in any of these podcasts. You're just amazing to talk to. So definitely appreciate the time.\u003C\u002Fp>\n\u003Cp>Speaker 2: Thank you.\u003C\u002Fp>\n\u003Cp>Speaker 0: Well, I had a great time too, y'all, and I hope you have a wonderful day. Thanks for having me on.\u003C\u002Fp>","But I do feel like that connection between creativity and artistry is what developers and technologists crave. Like, what are we doing? We're we're creating a digital world that is for humans to have a presentation layer to experience new and amazing things, and that's that's living. Hey, everyone. Welcome back to another episode of Trace Talks. We have a wonderful guest today. I'll let you introduce yourself, Dana. But today, we have Dana, who is, an amazing person. So go ahead and introduce yourself. Howdy, y'all. Dana Lawson, CTO of Netlify, x GitHub, x Envisioner, x NewRelecan, x all the things. Great to be on the show. Thanks for having me, Pedro and John. Yeah. Absolutely. Of course. We had a great, intro conversation a couple weeks ago with your with you and really enjoyed the conversation. But for those of us who or for those of, the people who might be listening, why don't you let them know who Netlify is and kind of a little bit about what you do there? Netlify is a activities. My my daily oh, man. Oh, my daily activities. Spreadsheets and email. Duh. I'm an executive. No. Netlify is a composable web platform. We help enable web developers and creators alike, take their idea all the way to production. So we connect to your Git repositories. No matter what flavor of Git you use, we help you build, design, deploy with a global CDN network with, performance. It's pretty fun. And my daily duties I mean, I'm chief technology officer, but that means a lot of different stuff to a lot of different people. And as I joke, it is mainly spreadsheets and and emails and videos. But, honestly, my job is to inspire, lead, and and break through all the problems that we have, building amazing and compelling software. So I have the product design, and engineering teams. We all work together under my organization. Amazing. And I know that, you know, now you're you're an inspiring leader. You're a CTO of a company, but it, obviously wasn't always that way. There was, like, a path to get there. And I think you came from a much different background before. Right? We had a call about you being sort of an artist and that. So I'd love to hear sort of where your career started and your sort of trajectory and how you got here as a CTO. I mean, you know, I am so blessed to be surrounded with a lot of, like, the Silicon Valley greats. You know? And most of them went to MIT and Stanford, but not all of them. Some of them are dropouts too. But I did have the trajectory. You know, when I started out being a nerd, I was I was really into art. You know? I mean, I've always loved science and math because I'm one of those people. But, I grew up in Southeast New Mexico, Texas, and I was like, I'm gonna be Georgia O'Keefe. I'm gonna paint. I'm gonna draw. Parents weren't really thrilled about going to college for painting and drawing because that means being a poor starving artist. You gotta be really good. In the art world, you gotta be like you first of all, you gotta know some people. Especially when you're alive. Yeah. And you gotta be good. And I was mediocre and knew nobody. So, like, my odds weren't very good, and this is pre Internet. And so on a crazy whim, you know, I was like, what am I gonna do with my life? I joined the US army. How you go from art art school to the army? Hello? I'm a multifaceted person. And I really letters are the same. Right? Two letters, art army? I think that, you know, like, you know, it wasn't always great with spelling. I was more of a math person. I should have looked a little little bit closer. But, you know, it's funny because, like, you go and you sign up for the military in the United States, they make you take this test. Well, they used back in the day. I think they still do. I mean, I don't do army research anymore. It's called the ASVAB. And, essentially, it determines your military occupation. Right? Like, what are you good at? I scored really good on the math stuff. They didn't have an art section. I would have dominated. Go fig. They didn't have it. And this job came up in the late nineties. It's like information systems operator analysis developer, like everything tech because they didn't know what to call it. And I was like, computers need air conditioning. And here I am. Here I am because of that fateful choice of choosing air conditioning because computers needed it, my own, my own need to be comfortable. But, honestly, I loved it. I went to the US computer science school that they had made. I don't know if it's still there. It was in Georgia at the time, and it was an immersive, like, 12 months of, like, bare bones networking to all the way to, like, sea language. And then you get out and you're like, okay. And I got I got positioned to, to really create and administer all the military applications. And so from there, it's like, wow. But long story even longer, you know, I was IC. I've been I've been everything you can be in in developing and creating software. I was a sysadmin. I was dev ops. I was a SRE. Every letter in the alphabet that we attach to, to what our jobs are, I've done them like QA, TPM, TIM, PM. And, you know, I I just got bit with that leadership bug, and I really started going down the path of leading teams and not becoming an individual contributor, and then here we are. Yeah. That's a interesting way to get into a career. There's air conditioning there, so I'm gonna jump in. Did you find that that transition from being an artist, a former art student artist, and then going into army and tech, was that an easy transition for you? Did you really enjoy it? Did you pick it up quickly, or did you just really enjoy the air conditioning? I mean, one, I'm in Portland, Oregon, and and today is gonna be a 100 degrees. And I think this entire city is, like, scared. I mean, I'm looking out. I'm not going out today. It never gets this hot. But no. I mean, you know, I was already doing a lot of stuff in Adobe, like the early Adobe days in graphic arts, and, like, I had back back when Microsoft and front page. I remember that. Did y'all do any of that? Oh, it's probably a file. I love it. I love it too. Oh, well then, you know, so like I did web design when there wasn't like, you didn't know anything. Like we all know how to make websites. Like I was making some geo cities. I was doing that. I was a webmaster y'all. Alright? So but I always looked at through this lens of, like, being able to be creative. Like, that is all we actually are. Like, maybe it's not you hands to keyboards and you're taking those requirements, but a lot of the times, those early ideas are all you. And the way that we write code is individual to us even though the AIs come and take our jobs. It still is because you get to determine what goes where, how it functions, and how the end user is gonna deal with it. And, like, it can be language frameworks. I mean, you could do it in so many different ways, and it wasn't a weird push for me. It was actually really natural. I was just like, but I'm very curious. I'm one of those people that, like, it don't matter where I'm at. I'm gonna have a good time, and it's very annoying to other people. But to me, I'm like, you know what? That's just how I roll. You know? Because it's not the place you are. It's the people and experiences you have and and who you're with. And so I loved it. I I I I was just like, this is fascinating. I never, in a in a in my wildest dreams, ever thought I would be doing what I got to do. And getting to, like, live from man, the first job they gave me in the military was changing backup tapes. Like, they didn't trust me to do shit except change tapes, and they were smart because I had Yeah. I had ruined a bunch of servers at one point, but that's a whole different story. And so they're like, Lawson, you get to change the tapes. And so I got to incrementally, like, grow my skill set and apply it. And it was great. It was a great job. But I do feel like that connection between creativity and artistry is what developers and technologists crave. Like, what are we doing? We're we're creating a digital world that is for humans to have a presentation layer to experience new and amazing things, and that's that's living. Yeah. Yeah. And I I I feel that most of the people we talk to as well, on this podcast and otherwise just in our daily sales life. We're speaking to a highly technical audience, you know, selling our product, and most of them are creatives in some capacity. A lot of musicians, tons of musicians. I think almost everyone at Directus plays some kind of musical instrument. So I I definitely agree there. So what was that like? The yeah. Go ahead, John. Well, I just wanna really pause to to call out that you changed backup tapes. When I first got into tech and I realized that backups were being kept on tapes, literal tapes, I was so shocked because I I was selling data center equipment. I'm like, wait a second. Mhmm. People are still backing up on tapes. So Oh, yeah. I just had to call that out that that that brought back some memories from my past days that and probably still to this day. I know some obvious obviously, digital backups are huge, but, I'm impressed that you were changing those actual tapes because I always I knew those people existed, but you were one of them. Oh, man. It's it's it it was not a glamorous job. You literally go into a colo with a tray, and, you're like, it's the a day because you have a b tapes. Alright? And there's a software called Veritas. No. It's this software called Veritas. Oh my gosh. People are gonna have to go Google this shit. Alright. I sold that stuff. You sold Veritas. Oh, man. No. But, yeah, it is. It is amazing. Yeah. Tapes. Well, we we should have a whole, like, discussion one day about, like, tech of past, like, the tech museum. Yeah. Sorry, Pedro. Go ahead. I I didn't remember because I had to call it out. Yeah. No. Absolutely. Maybe that's another another show we could put on Directus TV digging into the tech past. You know? Yeah. No. That's awesome. And so what was that moment that for you kind of transitioning from an individual contributor to a manager? You you'd mentioned you sort of got into leadership. You really liked leading teams. What was that initial pivot point, from being an individual contributor to a leader? You know, I mean, I think a lot of of people that I've met that have been in these positions, there's something that honestly is natural that comes with it. And for me, it wasn't this, like, I wanna be a manager. I absolutely did not wanna tell people what to do. I can't like people telling me what to do, believe it or not. But I just wouldn't stop my mouth from running. And I was like, we should do this. We should do this. Here's why. And I'm like, on my soapbox just saying, here's why we're here's here's how we're gonna unlock this. And they're like, listen. Why don't you just tell everybody what to do? You're doing it anyway. They don't want a sideline manager. You might as well just be it. And I was like, well, you know, I've never done that. I mean, I've done, I guess, different leadership stuff because naturally you have those opportunities in the military that kind of force you into, like, these these different things to do. And I don't know. I've had jobs and jobs of days past. And, I just feel like, you know, at that moment, I was like, oh, okay. And from there, it was just like, okay. How do I change this directory? The hardest transition, I think, really was, you know, when you become into leadership and you start leading teams, a lot of us have been individual contributors, and you're like, wait a minute. And and if you have a good culture, I hate that us versus them culture. It's like your boss is just an adult too trying to make money. There ain't no us versus them. I mean, if there is, like, go somewhere else. And so I really just would approach it. The hardest part for me was not separating myself and saying, oh, you know, now this is my new role, but more of, like, this isn't my role. Like, that's not your like, for me, it was like that that's not your job no more. And that's still hard for me where you could get so immersed with just, like, in the flow zone of of writing code. And you're like, as a leader, like, yeah. You can as a frontline manager, you may do some of that, but that's not your job no more. Like, if you wanna be great, your job is to empower, enable, align, and motivate, and realize what your role is. And maybe you're a team where you're a player coach and you get to still do that, but that's not your primary. Your engineers, that's their primary. And that was the hardest transition for me making that. But to get to that, I mean, it kinda came a little naturally. And then from there, it just was like that that hunger to take on more. And, you know, it was funny because, again, I never expected, like, you know, a lot of people are like, one day, I will run my own company or I'm gonna be a CEO. I was like, no way. I never thought that. I just always wanted to do amazing things with amazing people and and hopefully change the world. Like, to me, that's more important. But, yeah, I mean, it wasn't it wasn't a hard trajectory. I think, again, like, separating what you used to do seems to be the hardest part for people that transition. And there's only been, like, 1 or 2 times in my career where I've ever met anybody that was like, I wanna be a manager. And I'm like, what? Like, that's your goal? Like, why? Like, why do you wanna do that? And and usually, when they wanna be a manager and they're that resound, it's for the wrong reasons, like, straight up. It's not it not all the time, but I mean, I've heard one person's like, oh, I wanna be a manager. So why do you wanna be a manager? Because I could tell everybody what to do, and I I could quit fighting battles and, like, we'll just I could just get it done. And I was like, what planet do you live in? Like, because people I said I said, welcome to management. Your job is just fighting battles and trying to get, you know, people to to to agree and commit or disagree and commit, but commit is your out is your long goal. And so, like, it's you really feel like a used car salesman. Half the time going like, no. So I no. That price is not gonna work. I'm sorry. This is not gonna work. Yeah. See, it's not if you have a fantasy of command and control and leadership, like, again, you gotta find that the culture that that's how that's how they vibe. They're out there. But like most places where a lot of people are happy to work, they don't want that. Yeah. You mentioned putting down some of the tasks that you had previously done as an IC and and finding that a little bit of a struggle to put those tasks down. And then you also mentioned kind of fitting right into leadership. Was that was there any learning curve, or is your personality one where you whatever the task is on your plate, you'll figure out and do it? And then I guess the second part of that question is, were there parts? Because you you didn't go in searching for leadership. So did you get into leadership and managing teams and say, oh, man. This spreadsheet stuff sucks. You know? Like, I don't like this part, but I'll do it anyways. But so I guess what was that learning curve like, and were there parts where you're like, oh, I didn't even know I'd have to do this stuff? Oh, man. You know, I think the actual learning curve is is not knowing what you do need to shift and having to grow into that on the other side of the house because you can really look really shallowly and be like, well, what's the big difference? You're just breaking down tasks and giving projects out. That's what leadership is. Absolutely not. I think the learning curve is really dialing into, okay, well, how do I really start having effective communication? Like, obviously, I was somewhat good at it. I'm now in this position, but how do I refine it? How do I talk to different personalities? Because as a tech leader, an IC, you're just talking to mostly other engineers, sometimes product people, but they're still your people. Right? Now when you become into leadership, you have different stakeholders. And I think the biggest opportunity to learn is to understand how to speak their language, how to communicate effectively, and how to distill that information down so that there's still alignment between both parties so that you're not communicating in a way where this side hears something different than this side. And sometimes that can happen because breaking down really audacious technical things for people that are more on the business side, sometimes when they're motivated by different things, they'll hinder different things. And the example I give is, like, you know, let's say a success manager comes to you and says, so where are we at with that bug? And and and the engineer is like, well, we're gonna be code complete in 2 days, and we're gonna ship it tomorrow. Great. You're shipping it tomorrow. It's like, no. No. No. No. No. Stop. Stop. Stop. Stop. Yes. Technically, we are shipping it into staging and integration tomorrow, and there's still more behind that. And what you mean by code complete means something different to go to market. So you have to be really that translator or you're gonna cause friction. And as a manager, guess what your only job really, really, really, really is, is to remove friction. At the at the end of the day, your job is to remove friction. And the way that you do that is to to the the the morning's problems that cascade into the night. And you're constantly being agile and saying, where do I help people not run into anything that's impeding them from what they're trying to do? That's really all you gotta do. So I think that was the hardest kind of, like, is is is and then and then as you go further into it, right, like your frontline manager, you're like, okay. Maybe I'm talking to support. Maybe I'm talking to marketing. You go to director. Now the table stakes are like, now you're now you're learning the economics of operating and running a business. Right? K? Now it's like contribution margins, cost of goods sold, CapEx, OPEX, all these fin financial terms that you think we have acronyms? Go talk to an accountant. They have just as many. K? And sales, I don't even know if you're talking about half time. BDR, SDR. I'm like, I don't know. I do now though, but that's the stuff that like as an engineer, you don't learn that. Nobody taught there's no school to teach. There's no engine. We can go like I said, you can go to Stanford, MIT. They ain't gonna teach you about how to look at a financial forecast weighted against 2 growth motions and and outputs and forecasted. Like, it's all things that maybe you took a a side business class, but most of the time you're learning it. You're learning it on the job as you as you move up that corporate ladder. Yeah. That's wild. I think that, you know, I I just personally, that's something that I've been focused on a lot is really understanding different points of view, and it's something that I sort of I don't know. I mean, maybe it's the Libra in me just trying to, like, balance everyone and play like the the diplomat in most situations. But what do you think that you pull from, to to have that sort of knowledge now and being that translator and being able to effectively communicate and and be that diplomat between, you know, your teams? I mean, I have a big mouth, Pedro, so, like, you wouldn't believe this, but, like, active listening is so important and really just practicing on my mind runs a 1000000 1000000 miles a minute. Like, I am like I've I've main I've I've been able to challenge my ADHD into something productive, which is good, not good. Okay? But it's really about taking that moment and forcing myself to really go into active listening mode and pushing against those instinctual, I need to go. I need to execute. I need to push because you're so trained to have maximum productivity. And and in software engineering, it's about that tempo of delivery. And then when you're on the other side and you're working through really how do I express this to 100 of 1000 of users and get to start internally with my team first, like, it it's isn't it is again an art. It is it is something that you have to inherently start to develop. And what I do is I try to spend time just learning their business, understanding their workflows, putting on that product research mindset that, you know, we have as engineers and technologists to say, well, if I wanted to understand my ideal customer profile, well, my ideal customer right now is my partner over here on this side of the business. How do they work? How do they operate? How can I meet them where they are? And so also just, like, talking to other mentors and, like, people and, like, getting advice and, like, seeking feedback. I don't think I've ever not had a a, you know, an employer review tell me you're too verbose. I mean, it's a work in progress. I'll always be too verbose. But, like, really taking in that feedback and and trying to understand where we can be better, so that self reflection, those 3 sixties, so all those things that you can do and bringing them in together to understand. But I think active listening, really, which there's a whole bunch of techniques out there to do that. No. Absolutely. And and do you think that your your experience and your previous background in, you know, the alphabet soup of all the roles has helped in that? Like, you've had a sort of understand the different roles that you that you manage and the different roles that you sort of sell to internally and understand the languages that they speak? Oh, yeah. I mean, I love it. That's where I always encourage people to get out of their comfort zone. Like, do it. Like, I've done so many things that I never won obviously, was qualified for. So thank you to all those people out there that believed in me, because, like, I don't know if I would've put myself in that position, but here we are. And it's really about taking opportunities and and and finding those spaces within what you know and filling the gap. Right? Like, it's really human nature to double down in the areas where we're comfortable, where we can sit and say, I can just crush it. And in times of duress, we do that. We seek that comfort in in in repetitiveness and consistency. I don't like it. I get bored. I like it sometimes. And most of the time, like, when I'm getting comfortable, I'm like, oh my gosh, I gotta go solve. I gotta go find my next problem. And so I push and I've had the opportunity through the alphabet soup to do stuff that was kind of orthogonal to what I already had known. Like I got a wonderful opportunity to do business development, like pure business development. And I when I got tapped to do that, I was like, are you sure? I was like, I have been an I have been an engineering leader for a 1000000 years. You want me to do biz dev? Like, that's on the other it is on the other side of the house. I do not even know. Yeah. But they're like, no. Our business is talking to developer. Our business is expansion. Our business is helping, you know, really the value of our platform be open to everyone. And so when you think of business development, there's nobody better than a technologist that's right in it sometimes. However, you still need that other side of business acumen and that understanding of the sales operation and motion to be able to be effective because at the end of the day, the stakes are different, and and it's just a different type of ball game than delivering software. And that experience alone, I'm like, like, you can't go to you you can't go to school to get to learn that. And to me, it just strengthens that the inner team empathy that you need to have a startup because you're all pushing for the same goal and it's gonna be uncomfortable even if you are in your sweet spot. You you're gonna have moments in your trajectory where you're like, what am I doing? Why am I doing this? And and having empathy holistically helps everybody be better to deliver what they're doing. You can't just have a well oiled machine on one side of your house and then the other not be working or it's all gonna fail. No. I I absolutely agree in it. And, you know, I've seen that at the different companies that I've been in. Sometimes it's hard to align, especially when it's the technical teams versus the less technical teams and having being able to speak that common language. Is that something that obviously, they don't teach that in school, like you've mentioned. Is that something that you've taught as a manager to the the folks that you manage on the technical side, like balancing, you know, the technical acumen with the people skills and sort of the the goals of the business, which are, you know, ultimately to to earn revenue and keep everyone employed? Heck. Yeah. I mean, I think that's my number one job. Right? Like, I'm always trying to get my leaders to work me out of a job. Like, the best the best, you know, example of leadership is the person behind you taking your job or them going on to do the job you were doing. That's it. That's all I live for is to see see the people that I had the opportunity to partner with, go on and do bigger, better things, and me having a small little piece of that narrative. And hopefully being positive. I know it ain't all roses, but I think that's something that you have to do because it doesn't, you know, I talk about, oh, it all comes naturally. This is the part of the house that don't come naturally. This is the part where you have to be curious. You have to have your humility low and do a little homework, do a little research, and and try to meet them in the common ground. And I try to do that. Like, a lot of us have been utilizing, you know, objectives, key results, or other frameworks, and they can get so convoluted with with what you see the lens of the world. And as an executive, it's about that abstraction so that everybody can understand it. If they can't, then why why? And so from a manager training perspective, manager enablement is so important unless, you know, I don't think a lot of places are gonna get managers and be like, listen. I expect you to be a career manager. So, like, I ain't gonna teach you nothing new. Just keep the job rolling. Like, I don't know. I've been around for a hot minute. That's how it was in my day when we had cubicle cities. You were just, like, waiting for somebody to retire to get your job, to get the next job. It's not like that, and it hasn't been like that. And so, again, as a leader, if you have your goal set on really raising the bar on your team and getting them to the next level, it's finding those opportunities and those skill gaps and then teaching them that that common language. And there's so many there's so many darn resources because it's not just business speak, but when you have also a growing company and a global customer base, you also need to know culturally, like, the nuances. And there's some great books out there. I always recommend the cultural map. It's it's a book that helps you distinguish really how if you're like an American, how you show up or Western society. And it's really an interesting read. And I think those are some of the things that I try to encourage my leadership team as they're working on that voice is be customer centric, internal and external. And that means business acumen and speak, but also just human to human and understanding how they how they were brought up in this world and how they culturally represent themselves. So I think it goes hand in hand. So many nuances to it because, you know, yeah, it's not just internally between the different teams and the way that they speak, but also culturally, you know, you've got companies that are now global with remote work just being sort of the the default in a lot of these areas. So maintaining that company culture is also important, and these small little things can cause a rub between, you know, the Americans and the Europeans. They have totally different styles of work life balance and and culture and everything. So, yeah, I think that's that's really important. Are there any specific, like, exercises or resources that, or, you know, strategies that you implement within your teams to try and keep that cohesive culture. I mean, I mean, definitely, you know, we we try to do enablements and trainings, not just for managers, but for people, and they cross the chasm from technical skills to soft skills. Like, you need both, whether it's public speaking. Because public speaking is actually another good opportunity to refine your message house, your internal message house. If you can't say it in an elevator pitch, then you're probably not speaking common enough language so that people can understand your value. So I always recommend, like, get some speaker training. Believe it or not, I've had it. They teach you some tips. They're really, really good. But in addition to that, I would also say that, you know, some good resources would be writing things down with clear lines of ownership to also help clear up some of the ambiguity. Because, again, when you're talking about multicultural teams, from many, many different countries and and and English is second language for peeps. I mean, at Netlify, I think we have 17 countries represented in our in our little amazing startup. That's a lot of different people from a lot of different places doing a lot of different things. As we utilize tools like the DAISY, drivers, approvers, contributors, inform, and these frameworks so that you can drive decision making. And sometimes you have to leverage just written. Because maybe maybe maybe maybe you're having a block where there's friction within the the spoken communication. A lot of people can read better than they can speak, self included. Right? Like, I can I can I can speak I can read Spanish all day and I can speak it? But, I think that there's those opportunities too and other things that you can utilize to find that alignment. So I'm a big fan of DCs and RACES to really ensure you have clear ownership and that that's been, understood. And then also, you know, really just pushing a written culture. Like, don't keep stuff in the ether. And there's so many come on now. Like, there's so many tools that do it for you. Like, 4 years ago, we didn't have a little AI recorder that did a transcript and a summary. You can do all that now. Don't tell me you can't. And I I tell people, I'm like, you're not comfortable. I don't know. Go use go go go use your your model or your choice, your chatbot of choice. I don't care. It's cool. I training even as a training tool. Right? Like, you can have conversations with these things, and that's only improving. I mean, I've done job interviews even where I'm like, simulate this, Yes. Simulate. You're the you're the you're interviewing me for this role. Here's the resume. Copy paste. Right? And have a little conversation. Those those types of things can even be be pretty Use them. Use the tools. Like, we're all learning them. And, like, again, that's a double edged sword. You're like, I get to understand this, whatever we're saying AI is and how to be better. And I think, you know, you gotta be a technophile. You know, that's why you're in this role. You gotta stay on the latest. You gotta love it. You gotta know it. And I I do. I encourage it. I'm like, go. Go go go go ask go ask go ask the AI for you. At the end of the day, like, refining your communication skill set. I I keep seeing these ads for, like, Bob Iger's master class or whatever. I think that the thing he's saying in these ads is just like your your vision or your idea is only as good as your ability to articulate it. And, you know, back to your point, it's like having that ability to to communicate those ideas. If you can't do it verbally, like, at least writing them down and continuing to practice those so that you can win over your team so you could become a better a better leader, better even a better individual contributor. Like, everyone's got ideas. So, your ability to communicate that with others, I think, is is probably the point here. You better believe it. I mean, shoot. Like, a lot of, a lot of engineers have that that leadership charisma, and they want to refine that side of the house. Let them, like that's the I mean, again, I think that comes back to the harder part. You know, we are so so lucky to have this advanced toolset around us that we can go and source information. We can ask questions. We can autofill. We can move, but you gotta put in the work on the other side. And so, like, I definitely see, you know, an opportunity with getting people more comfortable and, again, utilizing the tool sets to do so. And that's where, you know, like you said, using it to, like, mock interview. Like, yes. Go do that. It's it it some of them are good enough. I mean, some of them are are not great enough, but it gives you also some which is interesting, I think, because people were like, oh, I don't trust the AI and the models, but it's private. It it's better than going into like, can we role play in Slack? Like, nobody wants to do that. Like, you know using even, like, there's an app that I've been using called Sonja. I actually spoke to the team a couple weeks ago. They were gathering some feedback, but it's an AI therapy app. And what I kind of started to realize, and maybe this is what some of these apps are sort of you know, they've they've modeled everything after their CBT techniques, and it has, like, an in-depth knowledge of, therapeutic techniques. But what I've realized is there's this barrier when you're talking to a human. Obviously, you're presenting the best version of yourself. You're not being as raw as you could be. Mhmm. And there's always some sort of, you know, I don't know, like subjectivism on the other side. Mhmm. And you know that somebody's, you know, judging you in a way where when you talk to the AI, there's zero judgment. You're talking to a computer. Your mind sort of knows it. So you speak to it a little differently. And for something like therapy or something like training, I remember back in college we used to do these sales role plays. Those things are terrifying. You're doing it in front of the class with the professor and it's like, you know, you're getting graded on this. And, to do that with AI, I think would be, you know, it's just such a such an improvement. Even if it's not perfect, it's it's still going to at least let you drop the guard, speak in your natural voice, and and learn effectively. Yeah. I I also think with AI tools and tools in general or I guess when you're dealing with humans, people are afraid to make mistakes in front of other humans. And so with an AI tool or a software tool, you're like, oh, I just made a mistake. I can just redo it again. You know, as a salesperson or as a if I do code wrong and I'm just testing it, it's easy to it's easy to make a mistake. So, Dana, do you find that you know, you you mentioned encouraging people to go try a ai tools and encouraging people to go test new things. Do you find that after you encourage them a couple times, they're more more willing to go and try new things on their own, or they're more willing to kind of understand that making mistakes is okay. Because similar to what you're saying, Pedro, is when I try something with an AI tool and I make a mistake, it's, oh, next turn. Let's go to the next turn. So I feel like the the stigma of making mistakes is removed when you when you have a barrier removed like that. So do you encourage your team members, Dana, to go and try new things and then find them down the road trying more and more things without having to be encouraged to go try those things? Oh, definitely, man. I love growth mindset. Like, you know, and and and what it comes down to is putting that human imposter syndrome, shame, or what other baggage you have thinking you ain't good enough down and building a place where people can do that and with psychological safety. Like, I totally I tell them all the time. I was like, y'all know that was written by AI because it's so it has all the right commas. It's not saying random shit. It's it's actually a little bit more, more focused. And I just try as a leader to not not be embarrassed about it either. It's like, hey. I'm human too. And I think, like, as as somebody that's creating and and producing the next set of advanced developer tools, you best know. Like, how how could you come? Like, where's your street cred? Like, people are using this stuff. I think you have to be really public about it, but also, like, be skeptical. Like like, it's not perfect. I don't a 100% trust it either. But again, I think the benefits are more than the cons right now if you utilize it like a tool and really put the right boundaries of how you're using it to function. But again, for me as a leader, it's taking out the hesitation because you gotta remember. Right? Like, tech has been through it. People are freaking like, you know, it's a doom scroll of, like, what's what's what's busted up in Silicon Valley right now. We're having a moment. I hope we're on the other side of it. Those feds cut that interest rate. But I think, like, people are paranoid. Not everybody, but, you know, a lot of people are like, hey. You know, the economy's unstable. Like, I don't wanna be found out. I'm like, no way. You're gonna be better. Like, if our number one goal again is to remove friction and increase productivity, you you should be doing your part. And, again, I don't think a lot of it is on the the hard skills of, like, utilizing these tool sets to be better. But on the soft skills, having those those moments to be like, I wanna ask it this really dumb question that maybe I wasn't comfortable going in Slack and asking it, or maybe I don't wanna even talk to my mentor. You know, 1, you know, don't put all your eggs into that AI basket and trust it out. But I think as a guiding point, like, you're gonna start self serving those answers to yourself. And who doesn't wanna self serve? Like, nobody wants to talk I mean, I love talking to people because, like, that's what I do, and I'm from the 1900. But people this day and age, they won't be talking to nobody. Okay? Yeah. And, like, that's cool. They're gonna talk to you when they want to, but they're gonna be more comfortable behind the keyboard. And we just have to embrace that and find different ways that people do communicate, whether it is short form video or, say, other AI tools, because that's the part that they don't teach you. And as being a leader, like, you have to pay attention to societal norms to understand how the current generation and the next are are taking in information, especially if you're a provider of how to do that. And if we're in software, we all play a part in that experience. Yeah. I agree. And I I think that at the end of the day, you know, asking like you were saying, asking the AI, maybe some of the questions that you think might be dumb or, you know, getting sort of that, introductory knowledge about something out of the way. Maybe it does, if used correctly, break down the barriers between human to human connection where it's just like, you know what? I can actually do some of this research on my own in a very quick and concise way before I come to the table with my ideas or Yeah. Yeah. So How how how important and I think this is a good a good thought on leadership. How important do you think it is to cultivate a a culture internally where, people aren't afraid to ask those what might be considered a stupid question. Because I've I've in the world of alphabet soup, there's so many acronyms you never know. And some people might be afraid to ask that question. What does BDR even mean? Or, I mean, I hear acronyms all day. I don't know what they mean. So how important is it to build a culture where people are comfortable asking those questions to other humans? You know, you can go ask AI. But I think as a leader, you also wanna have that culture where people aren't afraid to ask questions because in the end, they just wanna be smarter. I mean, I think it's a it's a it's a big challenge, and you're always fighting it because, you know, as your team scales, you lose some of those opportunities for interpersonal relationships. Right? We've all heard Dunbar's number of a 150. You can't know everybody past that. I disagree. I don't know. I came from a small town, and I knew everyone. But, but I do think that you it starts from the top. You know, your job as a leader, especially as an executive leader, is to set the tone and the pace. And, like, if you can show up and be your true authentic self, that gives people permission around you to do so. And that means you're gonna probably f up. You're gonna say something that you're embarrassed about. Like, trust me. There's not a week that goes by that I'm just like, oh, Dana Lawson. Why did that come out of your mouth? Why are you you? But you know what? I am me, and you're you, and I want you to be you at work because putting energy into being somebody else is taking away from your productivity. I ain't got time for that. I want you to spend those mental calories on on my stuff, which is getting shit done. And I think you have to really, again, make it really known. You have to practice what you preach, and then give people those opportunities to find the psychological safety for them to be okay doing that. Again, you know, you can't you could just saunder people's experiences. You're not living them. They're theirs. So you have to just kind of think about, well, how how would somebody that is 12 hours on the other side of the world take this information if they don't get it from me? How do we form a relationship? So again, establishing a written culture, you know, coming from the top down, being your true authentic self, that means messing up and being being a little scared sometimes you are. It's okay. That, like, that again, that gives permission to mess up too because humans are fallible. But then finding other avenues where people can feel safe to be able to share and and communicate, whether it's surveys, Slack messages, written, town halls, q and a's. Do all the things because you're gonna need to do a wide splatter to get all the differences on how people need to be felt and heard. But, again, I also try to encourage that, like, one to 1, like, come and ask me. You know? I when my AI startup, AI Migos, you'll pay a premium for the real Dana Lawson. What the day Dana bought, you know, I don't know, $20, I'll give you yes or no's. I joke about that, but, like, I want people to come to me because there are some things that you're just not gonna get on the other end, but utilize it as a tool. Practice. Like, get yourself comfortable. But when it comes from leadership, there's nothing more powerful than people see that you're authentic, genuine, and real. Because I don't know. People can read it when you're full of baloney. Like, you think they ain't. They are. And maybe not everybody is, but you got some skeptics out there going like, that person full of shit. And so, you know, I just say, don't be. Be you. Yeah. I love this idea of, like the Dana bot or like imagine the CTO at a company of thousands of people, that clearly it doesn't scale. You can't have a one on one conversations with everyone in the company or even the CEO or something like that. But cloning cloning that person's personality. And anyone now can have a one on one with the CEO at any time Oh, man. By the CTO. And if if you want to book the real thing, like, yeah, that that may happen as well, but anyone at every rag is gonna be crazy. Right? You're rag. You're like, wait a minute. No. Let me let me make my rag on, lots of howdy's in y'alls. I but here's this you know, I think that that's gonna happen. That's what Hollywood's scared of. I don't blame them. But, you know, the the reality is is, like, you can't take away human to human. And as a leader again, you just have to you have to stay in line with what's happening out there and who your people are and what's important to them. And right now, you know, it's it's not super easy. There's so much going on. I mean, we were all in a pandemic for 4 years. That was probably the hardest 4 years of leadership for a lot of people because everybody on the planet was fearful. Nobody knew what was going on. And the stuff that you did know was happening, you didn't know what was really happening because there's so much information and be blasted at you. And it was a really interesting and I believe challenging time for a lot of people, you know, separating that work and life balance. And, like, the world's just gonna continue to get more complex, and there's always stuff going on. And, you know, you can only play your part and do so much. So I always say, like, you know, like, while you're at work, I want you to not have to worry about some of those things, but focus in, get all those chores, get the friction out of place, and just feel good about producing. Because I think a lot of humans feel good when they produce because that's what working and living is, is seeing the output of your energy. Yeah. I think, yeah, I think sometimes that's important to remember is to still be able to contribute in certain ways. If we go back to a part at the beginning of this conversation, you mentioned having to learn to put down some things that you used to do as an IC. I've seen CTO calendars before, so you might not have any spare time these days. Do you still get to contribute at all? Do you ever get to code or do anything fun kind of to go back into your IC role, Or is it just all spreadsheets these days? If if if I touch production, like, the team would be like, smack. Get out of here. Come in. What do you what are you doing in here? So no. But no. I do on-site. Like, we're there's always cool stuff coming out. Right? Do I do it at work? Mostly on the architectural level, I do a lot of, design work implementation and and that piece of it. But day to day, like, I get to I I have to try it out. Like, we release something. I'm the first person that's going down there, and I'm like, oh, look at this new Astro template. And I'm a terrible web developer. Let me just be really real with y'all. Like, not my forte. More back end. But that's why I love doing web development because I'm so bad at it. And all these tools for they they they make it a little easier. So I'm always trying that stuff out. I'm the 1st to give it a go. I'm like, oh, but, you know, I think you have to know what you're doing and pushing. Like, Netlify, we're always pushing out these core primitives that are really kind of nascent to specific things that you do within, how data transmits from the Internet to your computer and so forth. And there's so much to do on. And so to really get that deep, you you gotta kinda get in there and know what's going on on on different levels of the stack. And so I think, again, you know, for myself, it can't just be all the fun soft stuff. As I say, I'm basically Napoleon Dynamite's brother Kip. I love people, but I love technology too. And so on my free time, because I get my peoples at work, believe it or not, I do a lot of stuff on the side and I'm always trying it out. But, yeah, I mean, it's like bits and bobs there. I've gotten really good at macros though. So if you do need some spreadsheet food, like, you know, like, call me. You're entering those, Excel competitions. You're like, I know how to do that. Shoot. No. I'm like and now I'm I'm telling you, this is one area where I'm like, thank you AI. I'm like, no more. No more. No mas. I mean, I could do it, but why would I? When I can be like, the train left the station at 2 PM and the other train left at 3 PM. And there's a person that gets I mean, I love it. AI is good at that kind of stuff. I hated those questions in school. It just was like, I don't it doesn't matter. Are they gonna crash into each other or not if they're not? It's like spreadsheets. You're like, if I take if I take column e and then I divide it by c and then I pivot it to your yeah. I know. I don't do it. Yeah. So so what's what's some of the favorite things that you've learned in your climbing to CTO? You know, what's some I guess, since this is kind of a podcast on leadership, what are some of the best leadership things that you've learned that you had no idea you would need to learn or maybe some things that you knew you weren't good at, but you've honed that that skill set? So what are some of the favorite things you've gotten great at over your your time? I would say, really, just I don't know if I'm great at it, but effective communication, I feel like I got a sweet spot for it. It's it's not been good. I mean, like I said, like, a a 1000000 miles a minute. So really practicing that focus, which I'm always still trying to refine my talking points. That's been one that I've been, like, you know, I feel kinda proud about where I've I've started at because it was a lot more. Public speaking, like, you know, you never like, getting up and talking to a 5,000, 10000 person audience is is scary. Like, it is so scary. You're like, what am I doing up here? Some people are natural at it. There's like, put the mic on, and let's go. And, like, you know, I play a natural at it, but I'm like, I'm scared. Every time I'm like, oh my god. Even this podcast this morning, I'm like, oh my god. Why am I doing a podcast? Couldn't tell when you're when you're giving a talk at the the Netlify conference. How was it? I know. I'm always scared. Always scared. But I would say, you know, public speaking is is one to crack over and, like, that's a hard one for leadership. Public speaking alone, I just think it's it's not, you know, not natural to a lot of people and especially, like, hardcore technology is not very natural, but as leaders, you gotta be great at it. You gotta refine it. So it's all in that communication vein. And, the other part is just, like, humility. Right? I'm a driven person. Like, we all have ego. We all wanna, like, crush it, win and be the best. Man, it don't take nothing but a big smack to show you, like, yeah, you ate perfect. You broke stuff and, like, it ain't all roses. And I try to be, you know, I try to really have self reflection on how to show up and be better, but it takes a lot of humility because your ego can get in the way of your decision making. And, you know, sometimes when you're doing something so excited, you know, you you you you get that that tunnel vision. And I think as a leader, like, really taking those moments, self reflecting on how you show up, honing your communication, and putting your ego at the door is gonna make you effective. And, really, there's no secret recipe except, you know, continue to be smart, hungry, and humble with everything that you do, and and learn. You know? Yeah. That that's amazing. And, you know, being that we we kinda have a technical audience that views this podcast, individual contributors looking to make their way towards, you know, leadership and management. Are there any other, like, kinda as we come to the the end of this conversation, like, are there any other pieces of feedback besides the one that you've just given that that you'd like to leave with with the audience? I would tell the I would tell these ICs, like, talk to your freaking manager. Build that rapport. Be transparent about your goals. Like, don't listen to me. If you wanna be a manager to because you just wanna be a manager, like, go explore it. Like, go. Even if it is for some reason that may not align with the reality, you're gonna learn by having that conversation out in the open. And if you're not sure, you can always start picking up some advanced things to give you those skill sets. Like, a lot of us have engineering levels and leadership levels, and they're in plus. As you go up and you get better, you get more natural leadership behaviors on your engineering levels as an IC and definitely on your manager levels. And so there's this expectation once you go across the chasm of senior engineer, like, you should have some leadership capabilities because your job is not just to execute, but it's to break down and execute. And to be able to break down and execute, you have to talk to people, you have to motivate them, and you have to align them. You may not be their manager, but believe me, you are just as influential, if not more. And if you really want to drive towards a position where you have more strategic input from a wider scale, again, it's about bridging into those things that you hadn't done before and honing those crafts while you have them right now. Do it now and and and and, you know, form that relationship. Managers aren't all scary. Some are pretty good. Most of them are pretty good. And if they're terrible, do what everybody else does. Leave them. It's alright. Well, yeah, it's it's I think that's important. Communication is big no matter no matter whether it's at work or relationships or family or whatever it ends up being. I've I've really appreciated the conversation. I'm sure Pedro has as well. Is there any last minute things that you want to promote? Maybe Netlify or your Etsy art store or analog tape backups? Oh, man. Analog tape backups. Yes. Come see me at veritas.dl. No. No. Oh, I'm gonna go Google that after this. No. I do. As Pedro said, he saw me at the Netlify conference. Netlify is having our own developer focused conference, October 2nd 3rd at the amazing Fairmont Hotel in San Francisco. So if any of y'all are listening and wanna come out and hang out with me and and see me pretend that I'm not scared even though I'm absolutely scared, apparently, Pedro said I was good at it. Thank you. That really actually helps. Come on down. Come hang out with us. Tickets are on sale right now, and there's also, I heard, some codes out there. If somebody, you know, doesn't have all the cash to spend and still wants to come and hang. Awesome. Well, thanks so much for your time, Dana. Yeah. I really appreciate it. Don't think I've smiled as much in any of these podcasts. You're just amazing to talk to. So definitely appreciate the time. Thank you. Well, I had a great time too, y'all, and I hope you have a wonderful day. Thanks for having me on.","f975bfdf-e18b-4b5e-b12c-01bc80c2f5ba",[1837,1838,1839],"62fb353c-b32e-41c0-9e44-7d99a5ad8cf8","59b69192-314d-4f61-bf66-bc83fdb1f097","5a27e791-75e1-4cfd-b436-5ce7bffbaba4",[],{"year":978,"number":1830,"id":1842,"episodes":1843,"show":1848},"814d854d-db03-4f92-99ba-d825df61fb36",[1844,1845,1846,1847,1825],"5755bb0e-40d5-49c3-bc89-29fb576a4d37","e134329e-226e-4c35-a8a9-ba50fd7fdd62","26e762ac-1fcd-4f49-a364-b5b2e21b1ebe","b748cd39-d86b-44b2-a219-2d58a116a038",{"title":1849,"slug":1850},"Trace Talks","trace-talks",{"id":1852,"slug":1853,"vimeo_id":1854,"description":1855,"tile":1856,"length":264,"resources":12,"people":12,"episode_number":195,"published":1857,"title":1858,"video_transcript_html":1859,"video_transcript_text":1860,"content":12,"seo":1861,"status":19,"episode_people":1862,"recommendations":1864,"season":1865},"20666ca0-00e3-4d71-985b-f7c4abbe3bd1","directus-insights","1009260574","Explore the powerful features of Directus Insights for building interactive dashboards with customizable panels, charts, and global variables to visualize and analyze your data.","f6fdb697-d613-48c0-b02d-18ec93f498ce","2024-09-13","Directus Insights","\u003Cp>Speaker 0: Directus insights allows rapid no code dashboard building to display and act on data. Regardless of your use case, you will be able to set up key metrics from data held in your director's project in just a few clicks. Dashboards can be used for business intelligence, user progress tracking, web traffic conversions, or any other time you need to analyze data within your director's project. Each dash board is made up of panels, building blocks to display and interact with data. They include charts and graphs like bar charts, line charts, meters, pie charts and data lists.\u003C\u002Fp>\n\u003Cp>The list panel allows filtering and listing of items from a specified collection. If inline editing is enabled, clicking an item will open a directors editor drawer allowing users to examine each item and edit data directly from the dashboard. Now there are 2 specific built in panels which are very interesting. The global variable and the global relational variable panels. The global variable panel allows you to create a variable which is globally accessible across other panels.\u003C\u002Fp>\n\u003Cp>When creating a variable, a key is provided. The value can then be accessed in any other panel by using the mustache syntax with the name of the key in the middle. The global relational variable panel allows for variables to be set by selecting 1 or more items from a collection. A drop down select interface is shown which will open a director's explore draw. The returned value will be a single item ID or an array of IDs if select multiple items is enabled.\u003C\u002Fp>\n\u003Cp>You can use this, for example, to create a dynamic filter. Directus insights comes with some baked in panels, but you can always get more straight from the directus marketplace or build your own as an extension and install it in your project. I hope this provides an interesting insight into how director's insights works. If you have any questions, feel free to reach out and we'll see you in the next video.\u003C\u002Fp>","Directus insights allows rapid no code dashboard building to display and act on data. Regardless of your use case, you will be able to set up key metrics from data held in your director's project in just a few clicks. Dashboards can be used for business intelligence, user progress tracking, web traffic conversions, or any other time you need to analyze data within your director's project. Each dash board is made up of panels, building blocks to display and interact with data. They include charts and graphs like bar charts, line charts, meters, pie charts and data lists. The list panel allows filtering and listing of items from a specified collection. If inline editing is enabled, clicking an item will open a directors editor drawer allowing users to examine each item and edit data directly from the dashboard. Now there are 2 specific built in panels which are very interesting. The global variable and the global relational variable panels. The global variable panel allows you to create a variable which is globally accessible across other panels. When creating a variable, a key is provided. The value can then be accessed in any other panel by using the mustache syntax with the name of the key in the middle. The global relational variable panel allows for variables to be set by selecting 1 or more items from a collection. A drop down select interface is shown which will open a director's explore draw. The returned value will be a single item ID or an array of IDs if select multiple items is enabled. You can use this, for example, to create a dynamic filter. Directus insights comes with some baked in panels, but you can always get more straight from the directus marketplace or build your own as an extension and install it in your project. I hope this provides an interesting insight into how director's insights works. If you have any questions, feel free to reach out and we'll see you in the next video.","c1c793eb-6117-45c5-b4ec-51462a062177",[1863],"e73b7e97-b6e9-4750-b6bc-919a744e1fe8",[],{"year":978,"number":129,"id":1866,"episodes":1867,"show":1876},"7915da85-0e6a-40d5-a43b-fe0514ec2b6c",[1868,6,1869,1870,1871,1872,1852,1873,1874,1875],"96b44cbc-1b14-4bea-87cd-0c4cb34d261d","04ffd615-6d1d-45de-9c1b-2ff9206fe343","4cc18530-ba2a-44f3-bb2e-2bfe4ad024d5","43612e4c-1bd9-411e-bd73-9c835a9b51e0","6e6965e7-13cc-4f86-b512-f567d66cfbe9","8ad4795b-963c-4ebd-8921-38ea2dda9f44","4d3c062f-0f30-41b7-83e9-3d2ed34a86f4","c7ab64da-f34d-42c9-8a1a-03e6026cc7e5",{"title":26,"slug":31},{"id":1875,"slug":1878,"vimeo_id":1879,"description":1880,"tile":1881,"length":264,"resources":12,"people":12,"episode_number":527,"published":1857,"title":1882,"video_transcript_html":1883,"video_transcript_text":1884,"content":12,"seo":1885,"status":19,"episode_people":1886,"recommendations":1888,"season":1889},"extensions","1009260726","Discover the power of Directus extensions in this overview, exploring how they can enhance your project's functionality and integrate with external services⁠.","cf0efaae-ee67-4a49-934e-2c79311582ce","Extensions","\u003Cp>Speaker 0: Directus has been built with flexibility and extensibility at its core. And can be added to by building or installing extensions to build features or integrate with external services. Extensions in directives run within the same environment as the core application, allowing them to leverage existing access to underlying services, authentication and access control systems, and UI components. App extensions run entirely in the data studio and API extensions run on the server and have different utilities available based on where they are run. Interfaces are used in the Directus editor providing the user with ways to create and edit items through forms and other elements, for example, a whiteboard interface.\u003C\u002Fp>\u003Cp>Displays are used in directus explore to represent single data points in new ways. For example, showing a 1 to 5 rating as various emojis. Layouts are also used in directus explore to give control over how multiple items in a collection are presented. For example, inside of a Gantt chart or a spreadsheet. Modules introduce a new level of extensibility to Directus, allowing you to add an entirely new page to your Directus app from the module bar on the far left.\u003C\u002Fp>\u003Cp>For example, a page solely containing an integration with an external service while still having access to all of the primitives inside of directors including all of the user state and the application state. Panels are used in directors insights to display or interact with data. Panels can also gather information from users through interfaces and act on them. For example, a panel to send an email to users. Hooks and operations allow for custom logic to be executed when data is changed or on schedules.\u003C\u002Fp>\u003Cp>Hooks are entirely code based and operations provide the functionality inside of directors automate. And finally, end points, which allow for creating additional API end points on top of those provided by Directus. End points are implemented as collections of express route handlers. The Directus marketplace provides a way for admins to install extensions in their projects directly in the data studio. It's available for all projects whether run on directors cloud or on your own infrastructure.\u003C\u002Fp>\u003Cp>While all app extensions can be published in the marketplace, API extensions must use our sandboxed extensions framework in order to be published, which is designed to provide robust security to your data and maintain strict control over interactions with the external environment by running extension code in an isolated sandbox. The world of extensions runs deep so I'm gonna stop at this high level overview. Just know that extensions are built in Node. Js and Vue, can be manually installed or installed via the marketplace and provide new functionality within your directors project.\u003C\u002Fp>","Directus has been built with flexibility and extensibility at its core. And can be added to by building or installing extensions to build features or integrate with external services. Extensions in directives run within the same environment as the core application, allowing them to leverage existing access to underlying services, authentication and access control systems, and UI components. App extensions run entirely in the data studio and API extensions run on the server and have different utilities available based on where they are run. Interfaces are used in the Directus editor providing the user with ways to create and edit items through forms and other elements, for example, a whiteboard interface. Displays are used in directus explore to represent single data points in new ways. For example, showing a 1 to 5 rating as various emojis. Layouts are also used in directus explore to give control over how multiple items in a collection are presented. For example, inside of a Gantt chart or a spreadsheet. Modules introduce a new level of extensibility to Directus, allowing you to add an entirely new page to your Directus app from the module bar on the far left. For example, a page solely containing an integration with an external service while still having access to all of the primitives inside of directors including all of the user state and the application state. Panels are used in directors insights to display or interact with data. Panels can also gather information from users through interfaces and act on them. For example, a panel to send an email to users. Hooks and operations allow for custom logic to be executed when data is changed or on schedules. Hooks are entirely code based and operations provide the functionality inside of directors automate. And finally, end points, which allow for creating additional API end points on top of those provided by Directus. End points are implemented as collections of express route handlers. The Directus marketplace provides a way for admins to install extensions in their projects directly in the data studio. It's available for all projects whether run on directors cloud or on your own infrastructure. While all app extensions can be published in the marketplace, API extensions must use our sandboxed extensions framework in order to be published, which is designed to provide robust security to your data and maintain strict control over interactions with the external environment by running extension code in an isolated sandbox. The world of extensions runs deep so I'm gonna stop at this high level overview. Just know that extensions are built in Node. Js and Vue, can be manually installed or installed via the marketplace and provide new functionality within your directors project.","a522211e-162c-4612-89e9-99553c402107",[1887],"381099bd-2e00-4678-b01f-f79e937af716",[],{"year":978,"number":129,"id":1866,"episodes":1890,"show":1891},[1868,6,1869,1870,1871,1872,1852,1873,1874,1875],{"title":26,"slug":31},{"id":1873,"slug":1893,"vimeo_id":1894,"description":1895,"tile":1896,"length":264,"resources":12,"people":12,"episode_number":129,"published":1857,"title":1897,"video_transcript_html":1898,"video_transcript_text":1899,"content":12,"seo":1900,"status":19,"episode_people":1901,"recommendations":1903,"season":1904},"theming","1009260633","Explore Directus' advanced custom theming options, including light and dark themes, CSS customization, and public page styling, to create a personalized look for your company or client's project⁠.","e09a8852-8d67-40c0-b10b-7c91d0528858","Theming","\u003Cp>Speaker 0: The ability to make Directus feel like it belongs to your company or client is very important and entirely possible thanks to the advanced custom theming options available. The direct us theming engine contains a number of customizable themes including a light theme and a dark theme. If default values are changed, they will automatically be mapped to the correct elements inside of the data studio even between upgrades. Both themes can be updated for all users in the settings module or for specific users in the users module. Any valid css values will be accepted by the directors theming engine.\u003C\u002Fp>\n\u003Cp>Custom CSS also allows custom styles or overrides in the data studio. If there are ever changes to the markup of the data studio, the directors team will ensure that all custom values set in the theming interface will continue to be applied correctly. You can also use custom CSS to add custom styles or overrides in the data studio. However, the data studio's core code and therefore the DOM selectors may change in any future release. So these updates are not considered a breaking change.\u003C\u002Fp>\n\u003Cp>So you should try and use the theming engine where possible and validate custom CSS still works after updating your director's project. The public pages of directors are shown before a user logs in and are related to authentication. Within your project's appearance settings, you can customize a project color, logo and favicon. Or is it favicon? Who knows favicon, favicon.\u003C\u002Fp>\n\u003Cp>You can, also pick custom images to display in the right half of the public pages. Public pages can also be further customized using custom CSS and our theming engine. And we'll talk about extensions in a future video, but themes can also be packaged up as extensions and installed in projects manually or via the directors marketplace. We want directors to be a super flexible tool that makes sense inside of your organization, and we hope that our custom theming options enable that to take place. If you have any questions, feel free to reach out, and we'll see you in the next video.\u003C\u002Fp>","The ability to make Directus feel like it belongs to your company or client is very important and entirely possible thanks to the advanced custom theming options available. The direct us theming engine contains a number of customizable themes including a light theme and a dark theme. If default values are changed, they will automatically be mapped to the correct elements inside of the data studio even between upgrades. Both themes can be updated for all users in the settings module or for specific users in the users module. Any valid css values will be accepted by the directors theming engine. Custom CSS also allows custom styles or overrides in the data studio. If there are ever changes to the markup of the data studio, the directors team will ensure that all custom values set in the theming interface will continue to be applied correctly. You can also use custom CSS to add custom styles or overrides in the data studio. However, the data studio's core code and therefore the dom selectors may change in any future release. So these updates are not considered a breaking change. So you should try and use the theming engine where possible and validate custom CSS still works after updating your director's project. The public pages of directors are shown before a user logs in and are related to authentication. Within your project's appearance settings, you can customize a project color, logo and favicon. Or is it favicon? Who knows favicon, favicon. You can, also pick custom images to display in the right half of the public pages. Public pages can also be further customized using custom CSS and our theming engine. And we'll talk about extensions in a future video, but themes can also be packaged up as extensions and installed in projects manually or via the directors marketplace. We want directors to be a super flexible tool that makes sense inside of your organization, and we hope that our custom theming options enable that to take place. If you have any questions, feel free to reach out, and we'll see you in the next video.","dc95bfd4-ec6f-4c3f-9900-2d144c953ddf",[1902],"1d57ea7a-24a2-42aa-8523-188aa536fe10",[],{"year":978,"number":129,"id":1866,"episodes":1905,"show":1906},[1868,6,1869,1870,1871,1872,1852,1873,1874,1875],{"title":26,"slug":31},{"id":1872,"slug":1908,"vimeo_id":1909,"description":1910,"tile":1911,"length":213,"resources":12,"people":12,"episode_number":213,"published":1857,"title":1912,"video_transcript_html":1913,"video_transcript_text":1914,"content":12,"seo":1915,"status":19,"episode_people":1916,"recommendations":1918,"season":1919},"directus-automate","1007702460","Understand Directus Flows, We'll explain how it enables custom event-driven task automation within Directus through triggers, operations, and data chains⁠.","35aa07c6-947b-4859-9e04-f44870140ffa","Directus Automate","\u003Cp>Speaker 0: Directus Automate provides flows, which enable custom event driven task automation within Directus. Each flow is composed of 1 trigger followed by a series of operations and finally returning data. Each flow starts with 1 trigger. The first is event driven based on data changes in your project. You can create a filter trigger which intercepts a database transaction before it happens, allowing you to validate or manipulate the data and optionally rejecting the transaction.\u003C\u002Fp>\u003Cp>Or action filters trigger once the change has happened in your database. This includes create, update, and deleting of items. This is great for data enrichment or follow-up logic that doesn't need to delay the database transaction from happening. The schedule trigger will run regularly on a specified interval. It uses the 6 point cron job syntax, so you can get as granular as second or as wide as weeks.\u003C\u002Fp>\u003Cp>The webhook trigger creates a unique URL for your flow and will be run whenever that URL is requested. Any data sent with the request will be available in the trigger object. The manual flow trigger adds a button to configured collections in Directus Explore and Directus Editor that can be clicked by users in the data studio. Optionally, a confirmation prompt can be shown with a form to collect data. And finally, you can chain flows together and configure them to be triggered by a parent flow.\u003C\u002Fp>\u003Cp>Data can be passed into them from the parent and once the flow has concluded it can be returned back up. Each time a flow is run it has its own data chain which is a JSON object passed from the trigger and to each operation, which will append information to the data chain. Now there are some dynamic properties available in every data chain. Trigger contains all of the data generated by the flows trigger. Accountability contains data about who or what started the flow.\u003C\u002Fp>\u003Cp>Env contains all of the environment variables that have been allowed to be shown and used inside of flows. And last contains return data from the previous operation whenever used. That means in every operation, the value of last will change to always be the output of the previous step. Each step which we call operations will receive all data in the data chain, perform some logic and return data back into the data chain. Each operation has a success and failure path which we call resolve and reject.\u003C\u002Fp>\u003Cp>So be sure to account for failed operations, especially when they rely on specific permissions or external services. A couple of very common operations include the webhook request url operation that allows your flow to make an external web request. You can select a method, url, and set custom headers as well as providing a payload. The create, read, update and delete data operations perform actions on items from a collection and adds the result to the data chain. You may select items by their ID or by running a query using directives' filter syntax.\u003C\u002Fp>\u003Cp>The run script operation lets you add custom JavaScript snippets. The script will be executed securely in an isolated sandbox, which means it only has access to standard built in objects and has no access outside of the sandbox except for sharing input and output values. This means, for example, no access to the file system, no ability to do network requests, and no support for third party packages. You can do each of these by using other operations that are provided as part of directors or by building custom operation extensions that use packages. Accessible from the sidebar, logs store information for each flow execution.\u003C\u002Fp>\u003Cp>Each log will display information from triggers as well as each operation in the flow. Keep in mind that if you've configured a flow to track logs, all of this will be stored in your database. So you may periodically need to delete this data if your flows get a lot of use. Directus automate is super powerful and I hope this explains the basics of how to utilize them. You can download custom operations from the directors marketplace and follow the tutorials in our documentation to set up specific flows.\u003C\u002Fp>","Directus Automate provides flows, which enable custom event driven task automation within Directus. Each flow is composed of 1 trigger followed by a series of operations and finally returning data. Each flow starts with 1 trigger. The first is event driven based on data changes in your project. You can create a filter trigger which intercepts a database transaction before it happens, allowing you to validate or manipulate the data and optionally rejecting the transaction. Or action filters trigger once the change has happened in your database. This includes create, update, and deleting of items. This is great for data enrichment or follow-up logic that doesn't need to delay the database transaction from happening. The schedule trigger will run regularly on a specified interval. It uses the 6 point cron job syntax, so you can get as granular as second or as wide as weeks. The webhook trigger creates a unique URL for your flow and will be run whenever that URL is requested. Any data sent with the request will be available in the trigger object. The manual flow trigger adds a button to configured collections in Directus Explore and Directus Editor that can be clicked by users in the data studio. Optionally, a confirmation prompt can be shown with a form to collect data. And finally, you can chain flows together and configure them to be triggered by a parent flow. Data can be passed into them from the parent and once the flow has concluded it can be returned back up. Each time a flow is run it has its own data chain which is a JSON object passed from the trigger and to each operation, which will append information to the data chain. Now there are some dynamic properties available in every data chain. Trigger contains all of the data generated by the flows trigger. Accountability contains data about who or what started the flow. Env contains all of the environment variables that have been allowed to be shown and used inside of flows. And last contains return data from the previous operation whenever used. That means in every operation, the value of last will change to always be the output of the previous step. Each step which we call operations will receive all data in the data chain, perform some logic and return data back into the data chain. Each operation has a success and failure path which we call resolve and reject. So be sure to account for failed operations, especially when they rely on specific permissions or external services. A couple of very common operations include the webhook request url operation that allows your flow to make an external web request. You can select a method, url, and set custom headers as well as providing a payload. The create, read, update and delete data operations perform actions on items from a collection and adds the result to the data chain. You may select items by their ID or by running a query using directives' filter syntax. The run script operation lets you add custom JavaScript snippets. The script will be executed securely in an isolated sandbox, which means it only has access to standard built in objects and has no access outside of the sandbox except for sharing input and output values. This means, for example, no access to the file system, no ability to do network requests, and no support for third party packages. You can do each of these by using other operations that are provided as part of directors or by building custom operation extensions that use packages. Accessible from the sidebar, logs store information for each flow execution. Each log will display information from triggers as well as each operation in the flow. Keep in mind that if you've configured a flow to track logs, all of this will be stored in your database. So you may periodically need to delete this data if your flows get a lot of use. Directus automate is super powerful and I hope this explains the basics of how to utilize them. You can download custom operations from the directors marketplace and follow the tutorials in our documentation to set up specific flows.","84bfeb3c-0e79-4204-bde1-a4926d26de01",[1917],"5acd1c81-4e55-4cbf-ba4b-1b89556a6ee4",[],{"year":978,"number":129,"id":1866,"episodes":1920,"show":1921},[1868,6,1869,1870,1871,1872,1852,1873,1874,1875],{"title":26,"slug":31},{"id":1874,"slug":1923,"vimeo_id":1924,"description":1925,"tile":1926,"length":264,"resources":12,"people":12,"episode_number":11,"published":1857,"title":1927,"video_transcript_html":1928,"video_transcript_text":1929,"content":12,"seo":1930,"status":19,"episode_people":1931,"recommendations":1933,"season":1934},"directus-realtime","1009260676","Discover how Directus Realtime keeps your application data up-to-date instantly, enabling realtime features like multi-user chat, live updates, and time-sensitive data transfers through WebSocket connections and flexible subscription options⁠.","d3d60bb4-86f0-4f8b-8159-ac3aefc641fb","Directus Realtime","\u003Cp>Speaker 0: Directus real time can be used anytime you need to ensure data is always up to date in your application. Common use cases include building multi user chat, audience participation, and any time sensitive data transfer like auction value, stock prices and sports scores. Unlike many real time platform providers, directus emits events once changes happen in your collections. This means that you can always trust that the latest version of your data is persisted and retrievable without needing to implement your own storage logic. You can connect using a standard WebSocket connection, using GraphQL subscriptions or by using the directives Javascript SDK.\u003C\u002Fp>\u003Cp>You can begin any number of subscriptions over your real time connection with directives. Subscriptions are made on a collection level and will by default receive messages whenever an item is created, updated, or deleted. You can specify a subscription to only receive events on specific operations and optionally provide a unique identifier, which will be returned in all messages related to the subscription, which is really useful if you have multiple of them and need to track where data needs to be processed in your application. You can also execute actions over a real time connection, so you can create, read, update, and delete data without needing to make a separate HTTP request. This can be useful when for example, building a chat where the current user must both subscribe to new messages and be able to submit their own.\u003C\u002Fp>\u003Cp>With directors real time, you can do that all over a single authenticated connection. Check out our documentation for tutorials on implementing directus real time in your applications. Or check out our directus tv show, make it real time for a set of project based tutorials, which actually have you building real projects with real time. As always, hope you found this interesting, and we'll see you in the next video.\u003C\u002Fp>","Directus real time can be used anytime you need to ensure data is always up to date in your application. Common use cases include building multi user chat, audience participation, and any time sensitive data transfer like auction value, stock prices and sports scores. Unlike many real time platform providers, directus emits events once changes happen in your collections. This means that you can always trust that the latest version of your data is persisted and retrievable without needing to implement your own storage logic. You can connect using a standard WebSocket connection, using GraphQL subscriptions or by using the directives Javascript SDK. You can begin any number of subscriptions over your real time connection with directives. Subscriptions are made on a collection level and will by default receive messages whenever an item is created, updated, or deleted. You can specify a subscription to only receive events on specific operations and optionally provide a unique identifier, which will be returned in all messages related to the subscription, which is really useful if you have multiple of them and need to track where data needs to be processed in your application. You can also execute actions over a real time connection, so you can create, read, update, and delete data without needing to make a separate HTTP request. This can be useful when for example, building a chat where the current user must both subscribe to new messages and be able to submit their own. With directors real time, you can do that all over a single authenticated connection. Check out our documentation for tutorials on implementing directus real time in your applications. Or check out our directus tv show, make it real time for a set of project based tutorials, which actually have you building real projects with real time. As always, hope you found this interesting, and we'll see you in the next video.","cb5636c2-bb9b-45f8-b18c-fcd33725a136",[1932],"c4e1455a-db0e-4e89-ab86-eb044a562653",[],{"year":978,"number":129,"id":1866,"episodes":1935,"show":1936},[1868,6,1869,1870,1871,1872,1852,1873,1874,1875],{"title":26,"slug":31},{"id":1540,"slug":1938,"vimeo_id":1939,"description":1940,"tile":1941,"length":674,"resources":12,"people":12,"episode_number":177,"published":1942,"title":1943,"video_transcript_html":1944,"video_transcript_text":1945,"content":12,"seo":1946,"status":19,"episode_people":1947,"recommendations":1951,"season":1952},"2-september-2024","1008099269","Join us for The Changelog, taking you through the month’s Directus updates including product updates, new content and community contribution highlights. This month's show includes a community showcase from websyte.ai, Rijk taking us through what's in his dock and Kevin with a tutorial on complex media transformations.","fb790812-6a22-49ff-b39a-cb8c0bc7818c","2024-09-11","September 2024","\u003Cp>Speaker 0: Welcome to the second version of the change log. If you didn't catch the last one, which was our very first one, welcome. Nice to have you here. The change log is where we go through what is new with directors this last month, and then we've also got some fun segments as well, so you're in for a treat, stick around. But the very first one is going to be Kevin taking it away with what is new with product, so hopefully you can hear this as well.\u003C\u002Fp>\u003Cp>Speaker 1: Last week, we released Directus 11.1, and with it, a major new feature, which is useful for every Directus project but really impactful for users of Directus Cloud, where Directus runs on our infrastructure, and that feature is log streaming. Let me show you how it works. Okay. So here we have a system logs, kind of session already begun over here. And this is ultimately a debugging tool to help you understand what requests are being made in your Director's project.\u003C\u002Fp>\u003Cp>So as I click around, you will see here that logs are being shown over on the left hand side. And that, of course, also includes errors, that may be happening, so you can have a little bit more of an idea of how that's happening. You can click into any given request and get a little bit more information about it. And you can filter all of the logs here, by log level. By default they're all enabled.\u003C\u002Fp>\u003Cp>And if you're running a Node Directus instance, you can also specify which, which nodes or instances you want to see logs for. You can access this straight from your project settings here from Directus version 11.1.0, and you can change the logging style by changing the log style environment variable. Now, like always, we are open to feedback, so we'd love to see, your feedback about this feature, but hope you find it useful. In the last month, there have also been a couple of smaller releases of Directus before 11.1, and these included some notable new features. Firstly, we added support for listening on Unix sockets instead of a host and port, which allows for inter process communication.\u003C\u002Fp>\u003Cp>This was a community contribution, so thank you very much for that. You can now also use an environment variable to configure how long invites last before they expire. This is really helpful if you have to create invites, but they were tending to expire before being used. And finally, we have also added support for indices via a new is indexed field, which means you don't have to do this directly in your database anymore, but can instead use Directus. So that's what's new across Directus version 11.0.2, 11.0.3, and Directus 11.1.0.\u003C\u002Fp>\u003Cp>They're available now on Docker Hub and on Directus Cloud, and we can't wait for you to use them. Alright. Thank you so much for this time, Beth. I'm gonna hand back over to you.\u003C\u002Fp>\u003Cp>Speaker 2: I'm gonna take you through what's new with Directors Labs. And as a quick reminder, Directors Labs is our experimental extension location where we regularly release new extensions we think are cool, and we hope you agree. So if I open up our demo for you and check that it's her. Yes. It is.\u003C\u002Fp>\u003Cp>Within flows, the first two to talk about are both email focused. Firstly, the liquid JS template operation, which enables dynamically generated content creation, such as personalized emails or really any scenario where you need to combine data with templates within a flow. Templates can be configured in the flow as a custom template or sourced from a collection. You can dynamically create different outputs based on multiple items. Here, the data is an array of users.\u003C\u002Fp>\u003Cp>You can use the liquid templating language, including loops and conditionals, injecting custom data in the data chain using these slightly altered liquid delimiters. If I go into the logs\u003C\u002Fp>\u003Cp>Speaker 0: here,\u003C\u002Fp>\u003Cp>Speaker 2: here is the output of the operation as well.\u003C\u002Fp>\u003Cp>Speaker 0: That is the first,\u003C\u002Fp>\u003Cp>Speaker 2: that runs into our second to talk about which is the resend email operation which integrates resend's email API into your director's flow. So here you, we are preparing the emails to send via resend, It's using the templates returned from the previous operation as the HTML property. And if we go into the resend operation itself, the operation supports most of resend's, endpoints along with the various options that we pass in the array from the previous operation. So there are 2 email focused operations. We go into insights for the panel demo, which are our next 3 to talk about.\u003C\u002Fp>\u003Cp>They look good. This one is the table in insight panel, which facilitates data from across multiple tables. And if you click an item, it'll open the director's editor drawer, but you can see here under socks. The next is the plausible analytics panel. This is part of a bundle and embeds your plausible analytics right within your director's project.\u003C\u002Fp>\u003Cp>I believe we're using the plausible sample data for this, which are the graphs that you're seeing. And lastly, another part of a bundle panel is the flow trigger panel that allows you to run manually trigger flows from right within a dashboard. If we go into content, you can also see the matching interfaces as part of the plausible analytics bundle and the flow trigger bundle. So you can trigger a flow or show a page level analytics dashboard as an interface as part of those as well. Next, we have the list interface, a small UI and UX, experiment for tags.\u003C\u002Fp>\u003Cp>So you can't reorder tags. You need to remove and re add them in order. This interface is a mix of tags and a repeater, and you can easily add and reorder strings. There's also full keyboard support with that as well. Lastly, I am gonna move location to show you.\u003C\u002Fp>\u003Cp>Here. Perfect. This is a global command palette now available along with a global search. So this module enables a command k shortcut that lets you navigate to collections, like so. Team.\u003C\u002Fp>\u003Cp>Not he, team. What else can I show you here? So, we can also search collections. So if we say search resources, like so, and I'd be looking for a spreadsheet, like so. And in the moment, if I wanted to create a new item, I could do that by create a to z partners.\u003C\u002Fp>\u003Cp>And, for example, this also allows run context aware actions like copying the API URL or running flows. It's gonna be a really nice tool for power users to navigate the data studio more quickly. I know I said lastly, but I have got a bonus for you. This isn't an extension, but it is for extension authors. We do often get asked how to customize the WYSIWYG and the block editor interfaces with new plugins, and our answer has been to adapt the interface into an extension and add the customizations yourself.\u003C\u002Fp>\u003Cp>While that isn't any different, we've now got these 2 interfaces, available as boilerplate extensions with guides on adding plugins.\u003C\u002Fp>\u003Cp>Speaker 0: This will get you started much quicker and let\u003C\u002Fp>\u003Cp>Speaker 2: you create the functionality you need. With all of these extensions, we hope that you find them useful. If you have any thoughts on them and you've got feedback, we'd love to hear from it. You can find all of these extensions in the directors lab slash extensions repo on GitHub.\u003C\u002Fp>\u003Cp>Speaker 0: We have new content this all right, that is the extensions for this month. Hopefully you can still all hear me again when I change between, so the next step sorry, yes, amazing, we also have, new content for you so, we have on Directus TV which for the people that might not have heard about it you are already on it because of the link, so congrats. You're already in the right place. Directors TV is where we have a whole bunch of educational and entertaining shows, and the latest one that we have just released the first few episodes of is Directors Academy. That is Kevin taking you through the different tools as part of Directors.\u003C\u002Fp>\u003Cp>Now if you use Directors all the time, this might not be the one for you. But if you are new or still getting to grips with everything that directors can do, this is a really great set of resources to show you exactly how to get around, parts of directors. And thank you, Kevin, for the link so that you can just move along, into it and check it out for yourself. Next up, we have Wrike, who is our CTO, talking through a very special edition of what's in your doc. If you haven't heard of what's in your doc, what's in your doc is another show that we usually have on directors TV with different people who tell us through the tools that they use, in everyday, life for their jobs and we are so lucky that Wrike has got one for this, month so I'm gonna pass it over to Wrike.\u003C\u002Fp>\u003Cp>Speaker 3: Hey. How's it going? So for my daily devices, I use a MacBook Pro in 2021, an iPad Pro, and an iPhone 15 Pro Max. Most of my day to day consists of a split between, you know, doing a lot of meetings, programming, and a bit of design on the side. For those meetings, I use a Sony Alpha 64100 with a 16 mil Sigma Prime Lens.\u003C\u002Fp>\u003Cp>I do have an Elgato prompter to make it look like I'm looking at you instead of a screen off to the side, and I use a Blue Yeti mic, a Yeti x. I also use a Logi Lytra Glow for a little bit of extra light. And when the meetings are a little bit less interesting, I have a little Tetris mic recart on my desk, off screen. Don't tell anyone. When it comes to software, let's take a look at the dock left to right first.\u003C\u002Fp>\u003Cp>1st and foremost, I use Arc as a browser. I am the type of person that doesn't really do more than 5 taps open at a time, or I'm losing my mind. So Arcs' organization tools for renaming tabs, making folders, having spaces has really been a game changer for my productivity in that space. Although, I do have to admit, tabs that go to a different space go there to die and then get deleted anyways after a long while. For the basic snail calendar reminders, I use the Apple standard apps.\u003C\u002Fp>\u003Cp>I have tried every new calendar app that comes out. I love experimenting with them. But at the end of the day, I always find myself coming back to the default ones as they basically do what I need them to do and nothing more. Right? They're very pleasing.\u003C\u002Fp>\u003Cp>At Directus, we use Notion for note taking. In meetings, we have a bunch of different teams and a bunch of documents. It's a bit unstructured, but that's kind of the beauty of Notion. And we use Linear for task management. So Linear, think of it as GitHub Issues as a separate app, which has really elevated our productivity in the especially the product team.\u003C\u002Fp>\u003Cp>Then we use Slack for communication with the team internally, and we use Discord for communication with the larger community of users, of Directus. When I'm programming, I similarly to calendar apps. I've tried various different apps. I recently tried Zed. I've been using some of the JetBrains stuff.\u003C\u002Fp>\u003Cp>I've been on Code at 2 way back in the day when that was a thing. I've tried Nova. I always come back to Versus Code for the last couple of years. I've set that up very customized. I'm one of the the weird ones that does it in light mode, with the 3 tap space setting.\u003C\u002Fp>\u003Cp>And I use Iterm on the side because I cannot deal with the built in terminal. Just a personal preference thing. For debugging databases, I'm a huge fan of TablePlus. So TablePlus is kind of PHPMyAdmin as a native app, but they support effectively every database under the sun, which is very, very nice. You have to learn the tool once, and then you can just use it, which is great for raw database management.\u003C\u002Fp>\u003Cp>So direct, you know, columns or insertions into databases. On the flip side of that, I use Repay API, previously known as Paul, as an API debugging tool. So it's kinda similar to Postman or Hopscotch or some of those tools, but has a magnative app. I'm still a UX designer at heart, so apps looking nice is a huge reason for me to use them over over something else. Then we use Figma for all the design files.\u003C\u002Fp>\u003Cp>I am a very big fan of FigJam as well for quick notes and figuring out projects and doing more product design or user experience design. And then last but not least, we have Reader and Ivory. So Reader is an RSS faint reader. I use that to stay up to date with a bunch of blogs and personal blogs that I like to follow for, any news in the tech industry or improvements to the web platform or any other interesting developments. And I use Ivory as my client for the VedaVerse.\u003C\u002Fp>\u003Cp>So I'm part of the Fostodon server right now, and I try to follow, again, interesting people on the Internet. And then last but not least, on the doctor's Spotify, I always got music playing. I mostly listen to everything is what I'm realizing now. There's there's a lot of pop funky stuff in there. There's a lot of alt rock in there, and there's a lot of a little bit of pop sometimes if I just wanna focus a little bit and not care about music.\u003C\u002Fp>\u003Cp>Sometimes I have my own stuff on repeat to get the numbers up because you gotta game the system a little bit. Outside of the dock, I am a huge fan of a new app that just came out from Syndra called Scratchpad, which is just a little icon in your your menu bar. The only thing it does is it just opens a tiny note, and and it goes away when you click it again. So it's just great for a quick in the middle of a meeting, I have to jot something down, I don't know where it goes. I need to have something to write in within a split second.\u003C\u002Fp>\u003Cp>Fantastic for that. I use one password for all of the password management. Couldn't do without. Use CleanShot for screenshots and screen recordings. Highly recommend that one.\u003C\u002Fp>\u003Cp>That is that is one I cannot do without nowadays. And then the main thing that I install on every machine every time is called Paste. So it's a clipboard manager, similar to the reasoning behind Repet API. It's a MEC native thing. It feels like it was designed by Apple, and I'm sure they get shirts at some point, and I'd be set when that happens.\u003C\u002Fp>\u003Cp>To write. What else? What else do we have here? Oh, yeah. So the the the hobby stops.\u003C\u002Fp>\u003Cp>Sorry. Outside of work, I like to write and play a lot of music. So one thing I have right off of my desk here is just there's an acoustic guitar sitting ready to go in a demo at any moment. You see, of course, you know, some guitars on the wall. There's there's too many in my apartment here.\u003C\u002Fp>\u003Cp>For that, on the computer, I use Logic Pro Reader Report through my, Line 6 HX Stomp XL as an interface. And other than that, what we got going on in here? Oh, yeah. Less little desk gadgets. I brought it up.\u003C\u002Fp>\u003Cp>Before, I have a little Tetris MicroCard. I don't know if they still make them, but this this gets used a little bit too much in the day. Great way to take a little break. I have an analog Nixie clock sitting here on my desk as a way to keep the time as if that's not a thing on my computer. And one sort of guilty pleasure that I thought it was gonna be stupid, but I cannot do without nowadays, is an Amber mug.\u003C\u002Fp>\u003Cp>They're the most again, when I when I bought it, I thought it was gonna be stupid as hell in Ellen's books. So recommend one of those as well. I have a little, Belkin wireless charger to prop my phone up and a Sonos on the desk for all of the music that I previously mentioned. And I think that's everything I have around me.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Amazing. Thanks so much, Royce, for taking us through What's in Your Doc. And if you want to see the other episodes, they're really great for finding new recommendations. I believe Kevin has put a link, over for you in the chat as well.\u003C\u002Fp>\u003Cp>I know we shouldn't have favorite segments, but personally, my favorite segment isn't it is the next one. We have the community showcase, and for this month, we have, Manuel Yang talking through website dotai, a chat to edit page builder. So I'm gonna hand over to Manuel now.\u003C\u002Fp>\u003Cp>Speaker 4: Hey, everyone. I'm Manuel, and I'm building website dotai, a chat to edit page builder. You can now skip the learning curve and chat with your landing page, just like you would with a developer. You can add advanced widgets or fancy animations super easily without learning how to code. So what was the motivation for me?\u003C\u002Fp>\u003Cp>I used a lot of the page builders out there, such as, like, Webflow, Framer, Bubble. And I've also implemented one myself for users at the company I currently work at. We use it to build out interactive article style pages for sponsored content. And I realized the biggest bottleneck was the learning curve needed to actually use the tool. Right?\u003C\u002Fp>\u003Cp>For example, with, these no code builders, you still need to know how basic CSS works, right? Such as padding or Flexbox. And most users that are trying to build out a page won't have this knowledge. So the goal was to provide a way a user can use natural language to build out a page while still giving them their traditional editing tools. So So the idea is that this makes it very flexible and gives you the benefit of iterating quickly without needing to wait for the back and forth with a developer where communication challenges can also delay timelines.\u003C\u002Fp>\u003Cp>You also get a lot of the AI features out of the box, such as copywriting so that you don't need to switch Windows copy and paste text. For example, if you want to rephrase all text in your landing page, you can literally just chat, rephrase all text in the landing page, have a more friendly tone, and then boom, press enter. So all that pretty cool stuff. Right? So, how did I approach building it?\u003C\u002Fp>\u003Cp>Yeah. When I started the build, I already knew Direct This was gonna be a part of Stacks since, you know, a page builder will need some sort of content management system to persist and edit the data. And I've also always been a fan of Directus since they used their front end framework. Vuego has been my go tos that first came out. I think it's pretty dope.\u003C\u002Fp>\u003Cp>However, one side, one downside of the developing modules inside of Directus is the lack of depth tool integration. And I know I can just copy over the source code, but it's been pretty nice and clean to be able to just run a Docker image with some extensions. Right? I've also considered using Supabase instead, but realized it was a bit of overkill for my needs, and it would a lot of it would add a lot of extra complexity I didn't need. And I needed something I can self host pretty easily and Directus provided everything I needed.\u003C\u002Fp>\u003Cp>Yeah. So for biggest challenges, for me was the prompts. Right? Getting the right prompt and iterating over it to get exactly how I want it to be was pretty tough. I'm sure most people who can develop what AI knows that getting consistent results can be a challenge sometimes.\u003C\u002Fp>\u003Cp>And I originally implemented this in a way where I would have the AI spit out Graef JS specific JavaScript code, which should get executed in the browser. But now I'm moving towards more of a RAG and artifact style architecture, which I think will be a lot more robust. So looking forward to that. And then probably the biggest challenge for me is marketing. Right?\u003C\u002Fp>\u003Cp>As a dev, I start with zero marketing skills, but was forced to learn in order to get the word out. And I'm doing things like build in public and some email marketing, but, you know, I'm still learning as I go. It's been pretty fun though. And, looking forward to other techniques that I can learn. But, yeah, in terms of expansions, yeah, I mean, I'm always looking for someone to help out with marketing or design.\u003C\u002Fp>\u003Cp>I do have a call with a designer sometime in the next week to try to see what I can do to improve the UI and the UX. But, yeah, always open to more users to help me test out the, app as well, make sure it aligns what you guys are looking for. Because right now, I'm just you know, maybe I can be a little tunnel vision sometimes, but just building it to what I need. But, yeah, that's about it. Thanks, guys.\u003C\u002Fp>\u003Cp>Bye.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Thanks so much, Manuel, for attending to take part. We appreciate it. And it is always, very cool to see what people are building with directors. If you have been inspired and want to see more, check out the I Made This channel in Discord.\u003C\u002Fp>\u003Cp>Lots of people are talking through what they're building and what's especially nice in my opinion is that often they will come back and update us too so we get to see kind of the work in progress and how people are getting along. Next up, we have Kevin with a short tutorial on complex media transformations. So I will pass over to him.\u003C\u002Fp>\u003Cp>Speaker 1: We see a ton of questions come through from the community. And when we see questions repeated, we tend to create some kind of content to answer that question to help you and us answer it in future. And this time, we're gonna talk about advanced image transformation. Now this is all rooted in the node Sharp library, but before we get on to that, let me show you a little bit about how less advanced, custom image transformation actually work. So, in case you didn't already know, every single image, every single file stored in your Director's project can be accessed via the URL of the director's project\u002Fassets\u002Fand then the ID of the file itself.\u003C\u002Fp>\u003Cp>And here we have this nice image of boats. It's a portrait image. And here in the docs, we see inside of the file, API reference that we can request a thumbnail or an image transformation. Now out of the box, Directus automatically, or for free I suppose, implements a set of very common transformations, things like fit, width and height, quality, and file format. So, let's just show how these work.\u003C\u002Fp>\u003Cp>Here we see width, so we can go width equals, I don't know, 300. And we see that the image is a lot smaller. It's 300 pixels wide. And what's actually happening there is when we're making that request for the first time, Directus Inn in the background is going to use the Node Sharp Image Transformation Library, transform the asset, store it in our asset storage, and then return it. And then if we request it a second time, it isn't gonna do the processing.\u003C\u002Fp>\u003Cp>Instead, it's just going to serve up the already transformed image, which is a lot more performant. But we can add as many, as many, of these custom transformations as we want. But this is only a small number of what is offered. Today, we're gonna talk about advanced transformations. So Directus exposes the Node Sharp library.\u003C\u002Fp>\u003Cp>Let's take a little look at this here. And allows us to use it via a transforms query parameter. These are things like rotating an image, flipping or flopping. You can blur it. There's also color manipulation, things like tinting towards a provided color and so on.\u003C\u002Fp>\u003Cp>And what I wanna help you do is look at this API reference that Sharp offer and understand how to turn that into a Directus, advanced transformation. So let's start with rotate. So the only thing that is strictly required here is an angle, which is a number here. It is called rotate. Right?\u003C\u002Fp>\u003Cp>The actual, the actual, function itself, the operation itself is called rotate, and it requires an angle. Keep that in your mind. So let's use the instead of width, let's use transforms. And transforms is a 2 d array. So we have to start with an outer array and each operation has an array inside of it.\u003C\u002Fp>\u003Cp>First value is the name of the operation, so rotate. And the next one is the amount we're rotating by. Let's say 90. And we see that the returned image there has been rotated 90 degrees. And we can add as many of these as we want.\u003C\u002Fp>\u003Cp>We have blur, for example, was in there as well. And this is a value, I think, between 0 a100, so we'll say 10. There you go. And we see there's a slight blur there. Let me just double check that it was out of a 100.\u003C\u002Fp>\u003Cp>Speaker 4: Doesn't specify.\u003C\u002Fp>\u003Cp>Speaker 1: Options sigma. A value between 0.3a1000. Not entirely sure. Not entirely sure. Anyway, that's that's the blur.\u003C\u002Fp>\u003Cp>Let's do another one here. Let's do, a flip. So we're gonna rotate it, then we're gonna flip it. And notice here that there was no additional parameters. That's because in preparing for this, I knew that it was just a Boolean.\u003C\u002Fp>\u003Cp>So you just need to provide there are no, like, options inside of that. Let's take a look at color manipulation as well. So we can provide a tint, and that tint can either be an object with an RGB value or a string which I believe passed by the colour module. Let's take a look. Which I believe can just be any, yeah, here we go.\u003C\u002Fp>\u003Cp>Can be any string here for CSS color. That's what's used under the hood by by Sharp. So if we go back into this, let's add one more here. Let's add a tint. And I think the easiest thing to do here will be just to pass a string, rgb, 255,\u003C\u002Fp>\u003Cp>Speaker 4: 0. Sure. And\u003C\u002Fp>\u003Cp>Speaker 1: again, that's gonna take a moment just to whoops. That's very red. It's gonna take a moment the first time, but on subsequent loads, it should be very, very quick, because that asset has already been created. Now this is a really long URL, but hopefully you see how to apply custom image transformations. The only other thing I kinda wanted to add here was just a note that you can also, generate them using the SDK.\u003C\u002Fp>\u003Cp>So here you can provide read asset role, the ID, and then you can use an array or a nested array of transforms in a little bit of a nicer way there. And this will return the full URL. So if you don't wanna construct it yourself manually each time, this is another option as well. So hopefully oh, I gotta get that off my screen. Hopefully, that gives you a little bit of an indication on how you can take the Sharp API reference and apply it to Directus using the transforms query parameter.\u003C\u002Fp>\u003Cp>Thanks for joining me, and I will hand back over to Beth.\u003C\u002Fp>\u003Cp>Speaker 2: We've got a new segment for this month's changelog called the reading list. We've asked some of the directors team for resource suggestions on things that they found interesting and educational, and so we've got some to share with you. The first is Charles Fillard's blog, there's no percentage 20, as in space, in file system naming and some of the logic behind that. Secondly, we've got notes on buttonedown. Com and their migration from buttonedown.email to buttonedown.com, including the process notes.\u003C\u002Fp>\u003Cp>We also have how to write a git commit message and why writing them well matters. We also have Inclusive Components, a blog about designing inclusive web interfaces piece by piece. And lastly, we have the 12 Factor app talking through a methodology for building web apps. Those are all of our reading suggestions for this month, if you would like to read them yourself we'll share the links in the comments on the deck at the end.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. And I can see as we go that Kevin is actually putting the links down in the chat for you. So hopefully, you can check them out. And, if you've heard of them before we'd love to know that. Let us know in the chat if these are resources you are familiar with.\u003C\u002Fp>\u003Cp>If they are new we highly recommend checking them out and if you've also got any resources that you think you, everyone else would like to also read and would be educational if you've learned something new this month or anytime really and you've got like a go through recommendation we'd love to hear from you, we'd love to hear what you go to you to get your knowledge that would be great. So yeah, anyone got any thoughts in the chat while we've got a few minutes we could definitely have a share, but yeah if not I think the the chat recommendations that we've just shared, the links are in there for you as well. And hopefully, we'll be bringing this back, monthly so we can build a nice collection of resources between the team and so you'll have lots of different, interests covered as well because we only had 5 to showcase, but there's lots more to come, I'm sure. Cool. While, we're waiting to see, I'll be around on chat, but in the meantime, we've got some thanks to give out.\u003C\u002Fp>\u003Cp>Speaker 2: We want to take a moment to thank all of the contributors since the last month's change log, which encapsulates 11.0.2 to 11.1.0. The start to interview, he's so kindly given your time to improve the thorough of this project, and\u003C\u002Fp>\u003Cp>Speaker 0: we want to thank each\u003C\u002Fp>\u003Cp>Speaker 2: of you individually. So a massive thank you to Jun Hong for fixing bookmark duplication when rapidly switching between bookmarks, preventing a crash in the data studio when switching to the date type while configuring date time interface, improving the grid layout for radio and checkboxes interface, and fixing the length of icon fields in system collections, allowing to use icons with long names. Thank you to Azeri for fixing a read only mutation to fields in the store via item draw. Fixing an issue with the 1st column would be missing when importing some CSV files, fixing a typo for invalid aggregate query parameter log, and finally fixing the notification for Synch file exports to be sent out to users without requiring any permissions on directed notifications. Thank you to Dominic for improving the WYSIWYG editor to hide the toolbar if no toolbar items are selected, for ensuring the open detail in the sidebar is persisted across site navigation and reloads, her browser tab, and for adding item count to the notification drawer.\u003C\u002Fp>\u003Cp>Thank you to Crowe for adding persisted default value and nullable during field schema updates. Thank you to Matt for introducing the user invite token TTL option allowing to configure the expiration of user invites. Thank you to SEM for improving selection label of the new tab checkbox in the HTML rich text editor. Thank you to Florian for fixing styling issues with the related values display. Thank you to Heiko for fixing the image tune menu toggle state indication in the block editor interface.\u003C\u002Fp>\u003Cp>Thank you to Bernard for fixing an issue that would cause local extensions built via workspaces to not be watched when extensions reload if active. Thank you to Gerard for fixing the generated open API specs to include post patch and delete path. Thank you to Eduard for fixing an outdated property name in their permissions typing. Thank you to Elle For fixing director's policy policy typescript declaration. And thank you to Andrew for fixing an issue that would cause Cloudinary to throw an inconsistent check sign.\u003C\u002Fp>\u003Cp>Thank you again to all of the, above people. You can see there's specific flow requests inside of the full release notes on GitHub. While we're saying thank you, I also want to give a massive thank you to the GitHub Sponsors for August. These are all of our amazing sponsors, who kindly financially contribute to their excellent development, and I want to give a special huge thank you to Clement and until this month as well.\u003C\u002Fp>\u003Cp>Speaker 0: All right that concludes the, thank yous for the contributors and the sponsors but lastly a massive thank you to you for, showing up and keeping us company and hopefully learning some new things about what is new with Directus for this month. We would absolutely love to see you again next month, for the same time. And on Thursday, so in 2 days' time, we also have another event, which is the regularly recurring request review. Oh, didn't mean to, like, triple, quadruple, illiterate that. The request review, It happens about every 2 weeks and, there is more information on the Discord events, now for the next 2 minimum if not more yet the regular recurring request review, Kevin.\u003C\u002Fp>\u003Cp>It's a lot of hours, but it's great. We go through and talk about, the request and what's going on and moving forward and exactly, Jonathan is hosting too. So yeah join us for that we hope you will be around, we always want feedback with the change log, as well so if you have any segments that you would like to request for this then we absolutely want to hear it. You can reach out to us in Discord and, yeah, hopefully we will see you next time. Thanks so much for joining.\u003C\u002Fp>","Welcome to the second version of the change log. If you didn't catch the last one, which was our very first one, welcome. Nice to have you here. The change log is where we go through what is new with directors this last month, and then we've also got some fun segments as well, so you're in for a treat, stick around. But the very first one is going to be Kevin taking it away with what is new with product, so hopefully you can hear this as well. Last week, we released Directus 11.1, and with it, a major new feature, which is useful for every Directus project but really impactful for users of Directus Cloud, where Directus runs on our infrastructure, and that feature is log streaming. Let me show you how it works. Okay. So here we have a system logs, kind of session already begun over here. And this is ultimately a debugging tool to help you understand what requests are being made in your Director's project. So as I click around, you will see here that logs are being shown over on the left hand side. And that, of course, also includes errors, that may be happening, so you can have a little bit more of an idea of how that's happening. You can click into any given request and get a little bit more information about it. And you can filter all of the logs here, by log level. By default they're all enabled. And if you're running a Node Directus instance, you can also specify which, which nodes or instances you want to see logs for. You can access this straight from your project settings here from Directus version 11.1.0, and you can change the logging style by changing the log style environment variable. Now, like always, we are open to feedback, so we'd love to see, your feedback about this feature, but hope you find it useful. In the last month, there have also been a couple of smaller releases of Directus before 11.1, and these included some notable new features. Firstly, we added support for listening on Unix sockets instead of a host and port, which allows for inter process communication. This was a community contribution, so thank you very much for that. You can now also use an environment variable to configure how long invites last before they expire. This is really helpful if you have to create invites, but they were tending to expire before being used. And finally, we have also added support for indices via a new is indexed field, which means you don't have to do this directly in your database anymore, but can instead use Directus. So that's what's new across Directus version 11.0.2, 11.0.3, and Directus 11.1.0. They're available now on Docker Hub and on Directus Cloud, and we can't wait for you to use them. Alright. Thank you so much for this time, Beth. I'm gonna hand back over to you. I'm gonna take you through what's new with Directors Labs. And as a quick reminder, Directors Labs is our experimental extension location where we regularly release new extensions we think are cool, and we hope you agree. So if I open up our demo for you and check that it's her. Yes. It is. Within flows, the first two to talk about are both email focused. Firstly, the liquid JS template operation, which enables dynamically generated content creation, such as personalized emails or really any scenario where you need to combine data with templates within a flow. Templates can be configured in the flow as a custom template or sourced from a collection. You can dynamically create different outputs based on multiple items. Here, the data is an array of users. You can use the liquid templating language, including loops and conditionals, injecting custom data in the data chain using these slightly altered liquid delimiters. If I go into the logs here, here is the output of the operation as well. That is the first, that runs into our second to talk about which is the resend email operation which integrates resend's email API into your director's flow. So here you, we are preparing the emails to send via resend, It's using the templates returned from the previous operation as the HTML property. And if we go into the resend operation itself, the operation supports most of resend's, endpoints along with the various options that we pass in the array from the previous operation. So there are 2 email focused operations. We go into insights for the panel demo, which are our next 3 to talk about. They look good. This one is the table in insight panel, which facilitates data from across multiple tables. And if you click an item, it'll open the director's editor drawer, but you can see here under socks. The next is the plausible analytics panel. This is part of a bundle and embeds your plausible analytics right within your director's project. I believe we're using the plausible sample data for this, which are the graphs that you're seeing. And lastly, another part of a bundle panel is the flow trigger panel that allows you to run manually trigger flows from right within a dashboard. If we go into content, you can also see the matching interfaces as part of the plausible analytics bundle and the flow trigger bundle. So you can trigger a flow or show a page level analytics dashboard as an interface as part of those as well. Next, we have the list interface, a small UI and UX, experiment for tags. So you can't reorder tags. You need to remove and re add them in order. This interface is a mix of tags and a repeater, and you can easily add and reorder strings. There's also full keyboard support with that as well. Lastly, I am gonna move location to show you. Here. Perfect. This is a global command palette now available along with a global search. So this module enables a command k shortcut that lets you navigate to collections, like so. Team. Not he, team. What else can I show you here? So, we can also search collections. So if we say search resources, like so, and I'd be looking for a spreadsheet, like so. And in the moment, if I wanted to create a new item, I could do that by create a to z partners. And, for example, this also allows run context aware actions like copying the API URL or running flows. It's gonna be a really nice tool for power users to navigate the data studio more quickly. I know I said lastly, but I have got a bonus for you. This isn't an extension, but it is for extension authors. We do often get asked how to customize the WYSIWYG and the block editor interfaces with new plugins, and our answer has been to adapt the interface into an extension and add the customizations yourself. While that isn't any different, we've now got these 2 interfaces, available as boilerplate extensions with guides on adding plugins. This will get you started much quicker and let you create the functionality you need. With all of these extensions, we hope that you find them useful. If you have any thoughts on them and you've got feedback, we'd love to hear from it. You can find all of these extensions in the directors lab slash extensions repo on GitHub. We have new content this all right, that is the extensions for this month. Hopefully you can still all hear me again when I change between, so the next step sorry, yes, amazing, we also have, new content for you so, we have on Directus TV which for the people that might not have heard about it you are already on it because of the link, so congrats. You're already in the right place. Directors TV is where we have a whole bunch of educational and entertaining shows, and the latest one that we have just released the first few episodes of is Directors Academy. That is Kevin taking you through the different tools as part of Directors. Now if you use Directors all the time, this might not be the one for you. But if you are new or still getting to grips with everything that directors can do, this is a really great set of resources to show you exactly how to get around, parts of directors. And thank you, Kevin, for the link so that you can just move along, into it and check it out for yourself. Next up, we have Wrike, who is our CTO, talking through a very special edition of what's in your doc. If you haven't heard of what's in your doc, what's in your doc is another show that we usually have on directors TV with different people who tell us through the tools that they use, in everyday, life for their jobs and we are so lucky that Wrike has got one for this, month so I'm gonna pass it over to Wrike. Hey. How's it going? So for my daily devices, I use a MacBook Pro in 2021, an iPad Pro, and an iPhone 15 Pro Max. Most of my day to day consists of a split between, you know, doing a lot of meetings, programming, and a bit of design on the side. For those meetings, I use a Sony Alpha 64100 with a 16 mil Sigma Prime Lens. I do have an Elgato prompter to make it look like I'm looking at you instead of a screen off to the side, and I use a Blue Yeti mic, a Yeti x. I also use a Logi Lytra Glow for a little bit of extra light. And when the meetings are a little bit less interesting, I have a little Tetris mic recart on my desk, off screen. Don't tell anyone. When it comes to software, let's take a look at the dock left to right first. 1st and foremost, I use Arc as a browser. I am the type of person that doesn't really do more than 5 taps open at a time, or I'm losing my mind. So Arcs' organization tools for renaming tabs, making folders, having spaces has really been a game changer for my productivity in that space. Although, I do have to admit, tabs that go to a different space go there to die and then get deleted anyways after a long while. For the basic snail calendar reminders, I use the Apple standard apps. I have tried every new calendar app that comes out. I love experimenting with them. But at the end of the day, I always find myself coming back to the default ones as they basically do what I need them to do and nothing more. Right? They're very pleasing. At Directus, we use Notion for note taking. In meetings, we have a bunch of different teams and a bunch of documents. It's a bit unstructured, but that's kind of the beauty of Notion. And we use Linear for task management. So Linear, think of it as GitHub Issues as a separate app, which has really elevated our productivity in the especially the product team. Then we use Slack for communication with the team internally, and we use Discord for communication with the larger community of users, of Directus. When I'm programming, I similarly to calendar apps. I've tried various different apps. I recently tried Zed. I've been using some of the JetBrains stuff. I've been on Code at 2 way back in the day when that was a thing. I've tried Nova. I always come back to Versus Code for the last couple of years. I've set that up very customized. I'm one of the the weird ones that does it in light mode, with the 3 tap space setting. And I use Iterm on the side because I cannot deal with the built in terminal. Just a personal preference thing. For debugging databases, I'm a huge fan of TablePlus. So TablePlus is kind of PHPMyAdmin as a native app, but they support effectively every database under the sun, which is very, very nice. You have to learn the tool once, and then you can just use it, which is great for raw database management. So direct, you know, columns or insertions into databases. On the flip side of that, I use Repay API, previously known as Paul, as an API debugging tool. So it's kinda similar to Postman or Hopscotch or some of those tools, but has a magnative app. I'm still a UX designer at heart, so apps looking nice is a huge reason for me to use them over over something else. Then we use Figma for all the design files. I am a very big fan of FigJam as well for quick notes and figuring out projects and doing more product design or user experience design. And then last but not least, we have Reader and Ivory. So Reader is an RSS faint reader. I use that to stay up to date with a bunch of blogs and personal blogs that I like to follow for, any news in the tech industry or improvements to the web platform or any other interesting developments. And I use Ivory as my client for the VedaVerse. So I'm part of the Fostodon server right now, and I try to follow, again, interesting people on the Internet. And then last but not least, on the doctor's Spotify, I always got music playing. I mostly listen to everything is what I'm realizing now. There's there's a lot of pop funky stuff in there. There's a lot of alt rock in there, and there's a lot of a little bit of pop sometimes if I just wanna focus a little bit and not care about music. Sometimes I have my own stuff on repeat to get the numbers up because you gotta game the system a little bit. Outside of the dock, I am a huge fan of a new app that just came out from Syndra called Scratchpad, which is just a little icon in your your menu bar. The only thing it does is it just opens a tiny note, and and it goes away when you click it again. So it's just great for a quick in the middle of a meeting, I have to jot something down, I don't know where it goes. I need to have something to write in within a split second. Fantastic for that. I use one password for all of the password management. Couldn't do without. Use CleanShot for screenshots and screen recordings. Highly recommend that one. That is that is one I cannot do without nowadays. And then the main thing that I install on every machine every time is called Paste. So it's a clipboard manager, similar to the reasoning behind Repet API. It's a MEC native thing. It feels like it was designed by Apple, and I'm sure they get shirts at some point, and I'd be set when that happens. To write. What else? What else do we have here? Oh, yeah. So the the the hobby stops. Sorry. Outside of work, I like to write and play a lot of music. So one thing I have right off of my desk here is just there's an acoustic guitar sitting ready to go in a demo at any moment. You see, of course, you know, some guitars on the wall. There's there's too many in my apartment here. For that, on the computer, I use Logic Pro Reader Report through my, Line 6 HX Stomp XL as an interface. And other than that, what we got going on in here? Oh, yeah. Less little desk gadgets. I brought it up. Before, I have a little Tetris MicroCard. I don't know if they still make them, but this this gets used a little bit too much in the day. Great way to take a little break. I have an analog Nixie clock sitting here on my desk as a way to keep the time as if that's not a thing on my computer. And one sort of guilty pleasure that I thought it was gonna be stupid, but I cannot do without nowadays, is an Amber mug. They're the most again, when I when I bought it, I thought it was gonna be stupid as hell in Ellen's books. So recommend one of those as well. I have a little, Belkin wireless charger to prop my phone up and a Sonos on the desk for all of the music that I previously mentioned. And I think that's everything I have around me. Alright. Amazing. Thanks so much, Royce, for taking us through What's in Your Doc. And if you want to see the other episodes, they're really great for finding new recommendations. I believe Kevin has put a link, over for you in the chat as well. I know we shouldn't have favorite segments, but personally, my favorite segment isn't it is the next one. We have the community showcase, and for this month, we have, Manuel Yang talking through website dotai, a chat to edit page builder. So I'm gonna hand over to Manuel now. Hey, everyone. I'm Manuel, and I'm building website dotai, a chat to edit page builder. You can now skip the learning curve and chat with your landing page, just like you would with a developer. You can add advanced widgets or fancy animations super easily without learning how to code. So what was the motivation for me? I used a lot of the page builders out there, such as, like, Webflow, Framer, Bubble. And I've also implemented one myself for users at the company I currently work at. We use it to build out interactive article style pages for sponsored content. And I realized the biggest bottleneck was the learning curve needed to actually use the tool. Right? For example, with, these no code builders, you still need to know how basic CSS works, right? Such as padding or Flexbox. And most users that are trying to build out a page won't have this knowledge. So the goal was to provide a way a user can use natural language to build out a page while still giving them their traditional editing tools. So So the idea is that this makes it very flexible and gives you the benefit of iterating quickly without needing to wait for the back and forth with a developer where communication challenges can also delay timelines. You also get a lot of the AI features out of the box, such as copywriting so that you don't need to switch Windows copy and paste text. For example, if you want to rephrase all text in your landing page, you can literally just chat, rephrase all text in the landing page, have a more friendly tone, and then boom, press enter. So all that pretty cool stuff. Right? So, how did I approach building it? Yeah. When I started the build, I already knew Direct This was gonna be a part of Stacks since, you know, a page builder will need some sort of content management system to persist and edit the data. And I've also always been a fan of Directus since they used their front end framework. Vuego has been my go tos that first came out. I think it's pretty dope. However, one side, one downside of the developing modules inside of Directus is the lack of depth tool integration. And I know I can just copy over the source code, but it's been pretty nice and clean to be able to just run a Docker image with some extensions. Right? I've also considered using Supabase instead, but realized it was a bit of overkill for my needs, and it would a lot of it would add a lot of extra complexity I didn't need. And I needed something I can self host pretty easily and Directus provided everything I needed. Yeah. So for biggest challenges, for me was the prompts. Right? Getting the right prompt and iterating over it to get exactly how I want it to be was pretty tough. I'm sure most people who can develop what AI knows that getting consistent results can be a challenge sometimes. And I originally implemented this in a way where I would have the AI spit out Graef JS specific JavaScript code, which should get executed in the browser. But now I'm moving towards more of a RAG and artifact style architecture, which I think will be a lot more robust. So looking forward to that. And then probably the biggest challenge for me is marketing. Right? As a dev, I start with zero marketing skills, but was forced to learn in order to get the word out. And I'm doing things like build in public and some email marketing, but, you know, I'm still learning as I go. It's been pretty fun though. And, looking forward to other techniques that I can learn. But, yeah, in terms of expansions, yeah, I mean, I'm always looking for someone to help out with marketing or design. I do have a call with a designer sometime in the next week to try to see what I can do to improve the UI and the UX. But, yeah, always open to more users to help me test out the, app as well, make sure it aligns what you guys are looking for. Because right now, I'm just you know, maybe I can be a little tunnel vision sometimes, but just building it to what I need. But, yeah, that's about it. Thanks, guys. Bye. Alright. Thanks so much, Manuel, for attending to take part. We appreciate it. And it is always, very cool to see what people are building with directors. If you have been inspired and want to see more, check out the I Made This channel in Discord. Lots of people are talking through what they're building and what's especially nice in my opinion is that often they will come back and update us too so we get to see kind of the work in progress and how people are getting along. Next up, we have Kevin with a short tutorial on complex media transformations. So I will pass over to him. We see a ton of questions come through from the community. And when we see questions repeated, we tend to create some kind of content to answer that question to help you and us answer it in future. And this time, we're gonna talk about advanced image transformation. Now this is all rooted in the node Sharp library, but before we get on to that, let me show you a little bit about how less advanced, custom image transformation actually work. So, in case you didn't already know, every single image, every single file stored in your Director's project can be accessed via the URL of the director's project\u002Fassets\u002Fand then the ID of the file itself. And here we have this nice image of boats. It's a portrait image. And here in the docs, we see inside of the file, API reference that we can request a thumbnail or an image transformation. Now out of the box, Directus automatically, or for free I suppose, implements a set of very common transformations, things like fit, width and height, quality, and file format. So, let's just show how these work. Here we see width, so we can go width equals, I don't know, 300. And we see that the image is a lot smaller. It's 300 pixels wide. And what's actually happening there is when we're making that request for the first time, Directus Inn in the background is going to use the Node Sharp Image Transformation Library, transform the asset, store it in our asset storage, and then return it. And then if we request it a second time, it isn't gonna do the processing. Instead, it's just going to serve up the already transformed image, which is a lot more performant. But we can add as many, as many, of these custom transformations as we want. But this is only a small number of what is offered. Today, we're gonna talk about advanced transformations. So Directus exposes the Node Sharp library. Let's take a little look at this here. And allows us to use it via a transforms query parameter. These are things like rotating an image, flipping or flopping. You can blur it. There's also color manipulation, things like tinting towards a provided color and so on. And what I wanna help you do is look at this API reference that Sharp offer and understand how to turn that into a Directus, advanced transformation. So let's start with rotate. So the only thing that is strictly required here is an angle, which is a number here. It is called rotate. Right? The actual, the actual, function itself, the operation itself is called rotate, and it requires an angle. Keep that in your mind. So let's use the instead of width, let's use transforms. And transforms is a 2 d array. So we have to start with an outer array and each operation has an array inside of it. First value is the name of the operation, so rotate. And the next one is the amount we're rotating by. Let's say 90. And we see that the returned image there has been rotated 90 degrees. And we can add as many of these as we want. We have blur, for example, was in there as well. And this is a value, I think, between 0 a100, so we'll say 10. There you go. And we see there's a slight blur there. Let me just double check that it was out of a 100. Doesn't specify. Options sigma. A value between 0.3a1000. Not entirely sure. Not entirely sure. Anyway, that's that's the blur. Let's do another one here. Let's do, a flip. So we're gonna rotate it, then we're gonna flip it. And notice here that there was no additional parameters. That's because in preparing for this, I knew that it was just a Boolean. So you just need to provide there are no, like, options inside of that. Let's take a look at color manipulation as well. So we can provide a tint, and that tint can either be an object with an RGB value or a string which I believe passed by the colour module. Let's take a look. Which I believe can just be any, yeah, here we go. Can be any string here for CSS color. That's what's used under the hood by by Sharp. So if we go back into this, let's add one more here. Let's add a tint. And I think the easiest thing to do here will be just to pass a string, rgb, 255, 0. Sure. And again, that's gonna take a moment just to whoops. That's very red. It's gonna take a moment the first time, but on subsequent loads, it should be very, very quick, because that asset has already been created. Now this is a really long URL, but hopefully you see how to apply custom image transformations. The only other thing I kinda wanted to add here was just a note that you can also, generate them using the SDK. So here you can provide read asset role, the ID, and then you can use an array or a nested array of transforms in a little bit of a nicer way there. And this will return the full URL. So if you don't wanna construct it yourself manually each time, this is another option as well. So hopefully oh, I gotta get that off my screen. Hopefully, that gives you a little bit of an indication on how you can take the Sharp API reference and apply it to Directus using the transforms query parameter. Thanks for joining me, and I will hand back over to Beth. We've got a new segment for this month's changelog called the reading list. We've asked some of the directors team for resource suggestions on things that they found interesting and educational, and so we've got some to share with you. The first is Charles Fillard's blog, there's no percentage 20, as in space, in file system naming and some of the logic behind that. Secondly, we've got notes on buttonedown. Com and their migration from buttonedown.email to buttonedown.com, including the process notes. We also have how to write a git commit message and why writing them well matters. We also have Inclusive Components, a blog about designing inclusive web interfaces piece by piece. And lastly, we have the 12 Factor app talking through a methodology for building web apps. Those are all of our reading suggestions for this month, if you would like to read them yourself we'll share the links in the comments on the deck at the end. Alright. And I can see as we go that Kevin is actually putting the links down in the chat for you. So hopefully, you can check them out. And, if you've heard of them before we'd love to know that. Let us know in the chat if these are resources you are familiar with. If they are new we highly recommend checking them out and if you've also got any resources that you think you, everyone else would like to also read and would be educational if you've learned something new this month or anytime really and you've got like a go through recommendation we'd love to hear from you, we'd love to hear what you go to you to get your knowledge that would be great. So yeah, anyone got any thoughts in the chat while we've got a few minutes we could definitely have a share, but yeah if not I think the the chat recommendations that we've just shared, the links are in there for you as well. And hopefully, we'll be bringing this back, monthly so we can build a nice collection of resources between the team and so you'll have lots of different, interests covered as well because we only had 5 to showcase, but there's lots more to come, I'm sure. Cool. While, we're waiting to see, I'll be around on chat, but in the meantime, we've got some thanks to give out. We want to take a moment to thank all of the contributors since the last month's change log, which encapsulates 11.0.2 to 11.1.0. The start to interview, he's so kindly given your time to improve the thorough of this project, and we want to thank each of you individually. So a massive thank you to Jun Hong for fixing bookmark duplication when rapidly switching between bookmarks, preventing a crash in the data studio when switching to the date type while configuring date time interface, improving the grid layout for radio and checkboxes interface, and fixing the length of icon fields in system collections, allowing to use icons with long names. Thank you to Azeri for fixing a read only mutation to fields in the store via item draw. Fixing an issue with the 1st column would be missing when importing some CSV files, fixing a typo for invalid aggregate query parameter log, and finally fixing the notification for Synch file exports to be sent out to users without requiring any permissions on directed notifications. Thank you to Dominic for improving the WYSIWYG editor to hide the toolbar if no toolbar items are selected, for ensuring the open detail in the sidebar is persisted across site navigation and reloads, her browser tab, and for adding item count to the notification drawer. Thank you to Crowe for adding persisted default value and nullable during field schema updates. Thank you to Matt for introducing the user invite token TTL option allowing to configure the expiration of user invites. Thank you to SEM for improving selection label of the new tab checkbox in the HTML rich text editor. Thank you to Florian for fixing styling issues with the related values display. Thank you to Heiko for fixing the image tune menu toggle state indication in the block editor interface. Thank you to Bernard for fixing an issue that would cause local extensions built via workspaces to not be watched when extensions reload if active. Thank you to Gerard for fixing the generated open API specs to include post patch and delete path. Thank you to Eduard for fixing an outdated property name in their permissions typing. Thank you to Elle For fixing director's policy policy typescript declaration. And thank you to Andrew for fixing an issue that would cause Cloudinary to throw an inconsistent check sign. Thank you again to all of the, above people. You can see there's specific flow requests inside of the full release notes on GitHub. While we're saying thank you, I also want to give a massive thank you to the GitHub Sponsors for August. These are all of our amazing sponsors, who kindly financially contribute to their excellent development, and I want to give a special huge thank you to Clement and until this month as well. All right that concludes the, thank yous for the contributors and the sponsors but lastly a massive thank you to you for, showing up and keeping us company and hopefully learning some new things about what is new with Directus for this month. We would absolutely love to see you again next month, for the same time. And on Thursday, so in 2 days' time, we also have another event, which is the regularly recurring request review. Oh, didn't mean to, like, triple, quadruple, illiterate that. The request review, It happens about every 2 weeks and, there is more information on the Discord events, now for the next 2 minimum if not more yet the regular recurring request review, Kevin. It's a lot of hours, but it's great. We go through and talk about, the request and what's going on and moving forward and exactly, Jonathan is hosting too. So yeah join us for that we hope you will be around, we always want feedback with the change log, as well so if you have any segments that you would like to request for this then we absolutely want to hear it. You can reach out to us in Discord and, yeah, hopefully we will see you next time. Thanks so much for joining.","e0d5a954-3694-470e-9c84-05e0c7f533c5",[1948,1949,1950],"18d8802c-262c-4079-a656-e74fef8dbbd5","c7695b89-faef-465b-ad0f-ccb858fc2fbd","33ae5a49-5b54-4c8c-a8a7-e2b6ba8cb2a3",[],{"year":978,"number":1536,"id":1537,"episodes":1953,"show":1954},[1539,1540,1541,1542,1519],{"title":297,"slug":298},{"id":1025,"slug":1956,"vimeo_id":1957,"description":1958,"tile":1959,"length":129,"resources":12,"people":12,"episode_number":13,"published":14,"title":1960,"video_transcript_html":1961,"video_transcript_text":1962,"content":12,"seo":1963,"status":19,"episode_people":1964,"recommendations":1966,"season":1967},"rijk","1007792087","Rijk is a developer and designer born and raised in the Netherlands. He first came to America to work on Directus where he is CTO to this day. When not coding you can find him playing bass in the Lower East Side or hanging out with his cats in Brooklyn. Coder-designer by nature, musician at heart; prefers code to be indented in threes. ","145ec186-e959-460e-88a6-e22c11ef1e15","What's in your Dock, Rijk?","\u003Cp>Speaker 0: Hey. How's it going? So for my daily devices, I use a MacBook Pro in 2021, an iPad Pro, and an iPhone 15 Pro Max. Most of my day to day consists of a split between, you know, doing a lot of meetings, programming, and a bit of design on the side. For those meetings, I use a Sony Alpha s 6,400 with a 16 mil Sigma prime lens.\u003C\u002Fp>\u003Cp>I do have an Elgato prompter to make it look like I'm looking at you instead of a screen off to the side, and I use a Blue Yeti mic, a Yeti x. I also use a Logi Lytra glow for a little bit of extra light. And when the meetings are a little bit less interesting, I have a little Tetris micro cart on my desk, off screen. Don't tell anyone. When it comes to software, let's take a look at the dock left to right first.\u003C\u002Fp>\u003Cp>First and foremost, I use Arc as a browser. I'm the type of person that doesn't really do more than five taps open at a time or I'm losing my mind. So Arc's organization tools for renaming tabs, making folders, having spaces has really been a game changer for my productivity in that space. Although, I do have to admit, tabs that go to a different space go there to die and then get deleted anyways after a long while. For the basic Smell calendar reminders, I use the Apple standard apps.\u003C\u002Fp>\u003Cp>I have tried every new calendar app that comes out. I love experimenting with them. But at the end of the day, I always find myself coming back to the default ones as they basically do what I need them to do and nothing more. Right? They're very pleasing.\u003C\u002Fp>\u003Cp>At Directus, we use Notion for note taking. In meetings, we have a bunch of different teams and a bunch of documents. It's a bit unstructured, but that's kind of the beauty of Notion. And we use Linear for task management. So Linear, think of it as GitHub Issues as a separate app, which has really elevated our productivity in the especially the product team.\u003C\u002Fp>\u003Cp>Then we use Slack for communication with the team internally, and we use Discord for communication with the larger community of users, of Triactus. When I'm programming, I similarly to calendar apps. I've tried, various different apps. I recently tried Zed. I've been using some of the JetBrains stuff.\u003C\u002Fp>\u003Cp>I've been on Coda two way back in the day when that was a thing. I've tried Nova. I always come back to Versus Code for the last couple of years. I've set that up very customized. I'm one of the the weird ones that does it in light mode, with a three tap space setting.\u003C\u002Fp>\u003Cp>And I use iTerm on the side because I cannot deal with the built in terminal. Just a personal preference thing. For debugging databases, I'm a huge fan of TablePlus. So TablePlus is kind of PHPMyAdmin as a native app, but they support effectively every database under the sun, which is very, very nice. You have to learn the tool once, and then you can just use it, which is great for raw database management, so direct, you know, columns or insertions into databases.\u003C\u002Fp>\u003Cp>On the flip side of that, I used RapidAPI, previously known as Paul, as an API debugging tool. So it's kind of similar to Postman or Hopscotch or some of those tools, but as a magnated app. I'm still a UX designer at heart, so apps looking nice is a huge reason for me to use them over something else. Then we use Figma for all the design files. I am a very big fan of FigJam as well for quick notes and figuring out projects and doing more product design, or user experience design.\u003C\u002Fp>\u003Cp>And then last but not least, we have Reader and Ivory. So Reader is an RSS feed reader. I use that to stay up to date with a bunch of blogs and personal blogs that I like to follow for any news in the tech industry or improvements to the web platform or any other interesting developments. And I use Ivory as my client for the Vedaverse. So I'm part of the Fostodon server right now, and I try to follow, again, interesting people on the Internet.\u003C\u002Fp>\u003Cp>And then last but not least, on the doctor's Spotify, I always got music playing. I mostly listen to everything is what I'm realizing now. There's there's a lot of pop funky stuff in there. There's a lot of alt rock in there, and there's a lot of a little bit of pop sometimes if I just want to focus a little bit and not care about music. Sometimes, I have my own stuff on repeat to get the numbers up, because you gotta game the system a little bit.\u003C\u002Fp>\u003Cp>Outside of the dock, I am a huge fan of a new app that just came out from Syndra called Scratchpad, which is just a little icon in your menu bar. The only thing it does is it just opens a tiny note, and it goes away when you click it again. So it's just great for a quick, in the middle of a meeting, I have to jot something down. I don't know where it goes. I need to have something to write in within a split second.\u003C\u002Fp>\u003Cp>Fantastic for that. I use 1Password for all of the password management. Couldn't do without. Use CleanShot for screenshots and screen recordings. Highly recommend that one.\u003C\u002Fp>\u003Cp>That is one I cannot do without nowadays. And then the main thing that I install on every machine every time is called Paste. So it's a clipboard manager, similar to the reasoning behind Repit API. It's a MEC native thing. It feels like it was designed by Apple, and I'm sure they get shirts at some point, and I'd be sad when that happens.\u003C\u002Fp>\u003Cp>To write. What else? What else do we have here? Oh, yeah. So the the the hobby stops.\u003C\u002Fp>\u003Cp>Sorry. Outside of work, I like to write and play a lot of music. So one thing I have right off at my desk here is just there's an acoustic guitar sitting ready to go in a demo at any moment. You see, of course, you know, some guitars on the wall. There's there's too many in my apartment here.\u003C\u002Fp>\u003Cp>For that, on the computer, I use Logic Pro to record through my, line six HX stomp XL as an interface. And other than that what do we got going on in here? Oh, yeah. Less little desk gadgets. I brought it up.\u003C\u002Fp>\u003Cp>Before, I have a little Tetris micro card. I don't know if they still make them, but this this gets used a little bit too much in the day. Great way to take a little break. I have an analog Nixie clock sitting here on my desk as a way to keep the time, as if that's not a thing on my computer. And one sort of guilty pleasure that I thought it was gonna be stupid, but I cannot do without nowadays, is an ember mug.\u003C\u002Fp>\u003Cp>They're the most again, when I when I bought it, I thought it was gonna be stupid as hell, and now I'm hooked. So recommend one of those as well. I have a little, Belkin wireless charger to prop my phone up and, Sonos on the desk for all of the music that I previously mentioned. And I think that's everything I have around me.\u003C\u002Fp>","Hey. How's it going? So for my daily devices, I use a MacBook Pro in 2021, an iPad Pro, and an iPhone 15 Pro Max. Most of my day to day consists of a split between, you know, doing a lot of meetings, programming, and a bit of design on the side. For those meetings, I use a Sony Alpha s 6,400 with a 16 mil Sigma prime lens. I do have an Elgato prompter to make it look like I'm looking at you instead of a screen off to the side, and I use a Blue Yeti mic, a Yeti x. I also use a Logi Lytra glow for a little bit of extra light. And when the meetings are a little bit less interesting, I have a little Tetris micro cart on my desk, off screen. Don't tell anyone. When it comes to software, let's take a look at the dock left to right first. First and foremost, I use Arc as a browser. I'm the type of person that doesn't really do more than five taps open at a time or I'm losing my mind. So Arc's organization tools for renaming tabs, making folders, having spaces has really been a game changer for my productivity in that space. Although, I do have to admit, tabs that go to a different space go there to die and then get deleted anyways after a long while. For the basic Smell calendar reminders, I use the Apple standard apps. I have tried every new calendar app that comes out. I love experimenting with them. But at the end of the day, I always find myself coming back to the default ones as they basically do what I need them to do and nothing more. Right? They're very pleasing. At Directus, we use Notion for note taking. In meetings, we have a bunch of different teams and a bunch of documents. It's a bit unstructured, but that's kind of the beauty of Notion. And we use Linear for task management. So Linear, think of it as GitHub Issues as a separate app, which has really elevated our productivity in the especially the product team. Then we use Slack for communication with the team internally, and we use Discord for communication with the larger community of users, of Triactus. When I'm programming, I similarly to calendar apps. I've tried, various different apps. I recently tried Zed. I've been using some of the JetBrains stuff. I've been on Coda two way back in the day when that was a thing. I've tried Nova. I always come back to Versus Code for the last couple of years. I've set that up very customized. I'm one of the the weird ones that does it in light mode, with a three tap space setting. And I use iTerm on the side because I cannot deal with the built in terminal. Just a personal preference thing. For debugging databases, I'm a huge fan of TablePlus. So TablePlus is kind of PHPMyAdmin as a native app, but they support effectively every database under the sun, which is very, very nice. You have to learn the tool once, and then you can just use it, which is great for raw database management, so direct, you know, columns or insertions into databases. On the flip side of that, I used RapidAPI, previously known as Paul, as an API debugging tool. So it's kind of similar to Postman or Hopscotch or some of those tools, but as a magnated app. I'm still a UX designer at heart, so apps looking nice is a huge reason for me to use them over something else. Then we use Figma for all the design files. I am a very big fan of FigJam as well for quick notes and figuring out projects and doing more product design, or user experience design. And then last but not least, we have Reader and Ivory. So Reader is an RSS feed reader. I use that to stay up to date with a bunch of blogs and personal blogs that I like to follow for any news in the tech industry or improvements to the web platform or any other interesting developments. And I use Ivory as my client for the Vedaverse. So I'm part of the Fostodon server right now, and I try to follow, again, interesting people on the Internet. And then last but not least, on the doctor's Spotify, I always got music playing. I mostly listen to everything is what I'm realizing now. There's there's a lot of pop funky stuff in there. There's a lot of alt rock in there, and there's a lot of a little bit of pop sometimes if I just want to focus a little bit and not care about music. Sometimes, I have my own stuff on repeat to get the numbers up, because you gotta game the system a little bit. Outside of the dock, I am a huge fan of a new app that just came out from Syndra called Scratchpad, which is just a little icon in your menu bar. The only thing it does is it just opens a tiny note, and it goes away when you click it again. So it's just great for a quick, in the middle of a meeting, I have to jot something down. I don't know where it goes. I need to have something to write in within a split second. Fantastic for that. I use 1Password for all of the password management. Couldn't do without. Use CleanShot for screenshots and screen recordings. Highly recommend that one. That is one I cannot do without nowadays. And then the main thing that I install on every machine every time is called Paste. So it's a clipboard manager, similar to the reasoning behind Repit API. It's a MEC native thing. It feels like it was designed by Apple, and I'm sure they get shirts at some point, and I'd be sad when that happens. To write. What else? What else do we have here? Oh, yeah. So the the the hobby stops. Sorry. Outside of work, I like to write and play a lot of music. So one thing I have right off at my desk here is just there's an acoustic guitar sitting ready to go in a demo at any moment. You see, of course, you know, some guitars on the wall. There's there's too many in my apartment here. For that, on the computer, I use Logic Pro to record through my, line six HX stomp XL as an interface. And other than that what do we got going on in here? Oh, yeah. Less little desk gadgets. I brought it up. Before, I have a little Tetris micro card. I don't know if they still make them, but this this gets used a little bit too much in the day. Great way to take a little break. I have an analog Nixie clock sitting here on my desk as a way to keep the time, as if that's not a thing on my computer. And one sort of guilty pleasure that I thought it was gonna be stupid, but I cannot do without nowadays, is an ember mug. They're the most again, when I when I bought it, I thought it was gonna be stupid as hell, and now I'm hooked. So recommend one of those as well. I have a little, Belkin wireless charger to prop my phone up and, Sonos on the desk for all of the music that I previously mentioned. And I think that's everything I have around me.","99bce9bc-f32f-486e-8c4b-bd7a57ae6ba9",[1965],"471218ab-24c4-4cd0-8af6-11f2d9115cde",[],{"year":978,"number":1022,"id":1023,"episodes":1968,"show":1969},[1025,1009],{"title":1027,"slug":1028},{"id":1871,"slug":1971,"vimeo_id":1972,"description":1973,"tile":1974,"length":264,"resources":12,"people":12,"episode_number":159,"published":14,"title":1975,"video_transcript_html":1976,"video_transcript_text":1977,"content":12,"seo":1978,"status":19,"episode_people":1979,"recommendations":1981,"season":1982},"directus-files","1007702379","Discover the powerful features of Directus Files, a comprehensive Digital Asset Management system that simplifies file storage, organization, and transformation within your Directus projects⁠.","113f6680-9c83-4111-99a9-9e1f1dc1dbac","Directus Files","\u003Cp>Speaker 0: Directus files aggregate all assets within your Directus project into one consolidated library. It is a full featured digital asset management system for storing, organizing, browsing and transforming your files. By default, director stores all uploaded files locally on disk. However you can also configure directors to use any AWS S3 compatible storage, Google Cloud storage, Azure storage, Cloudinary or Superbase. Each file managed by Directus also corresponds to an item in the Directus file system collection.\u003C\u002Fp>\u003Cp>Which provides a unique for each item and additional metadata. Folders in directors files can be used to organize assets within the platform, but folders are virtual and aren't mirrored within the storage adapter. As well as organization, folders can be used within access control settings to limit a user's ability to create, read, update, and delete files. The full file tree can be used so virtual folder hierarchies can reflect a user's permission. Each file in your directors project has a number of additional system fields that can be used for management.\u003C\u002Fp>\u003Cp>Any additional metadata directors was able to scrape from the file will also be stored. For images, this includes exif, I iptc and icc information. From within the file detail page, metadata can be seen and edited including manual cropping and setting a focal point for cropping via API. You can access files via URL in your own application by using the following structure. Being sure to provide your directors project URL and the specific file uuid that is shown in the data studio.\u003C\u002Fp>\u003Cp>And there's lots we can do with this url. We can provide an optional file name after the id to optimize for search engines. And directives provides out of the box asset transformations, allowing for cropping with width, height and fit parameters. A specific quality and format can also be requested along with other transformations offered by the node sharp library such as blurring, flipping and re coloring. Once an asset is transformed with all of the transformation parameters for the first time, it is stored and will not need to be transformed again.\u003C\u002Fp>\u003Cp>Meaning that subsequent loads should be quicker and more performant. You can also save combinations of transforms into what we call storage asset presets and then apply them just by using a key when accessing the image. Director's files is super powerful. And when combined with several of our interfaces that are both built in and provided via the director's marketplace, you have a super rich digital asset management system for any project to your building.\u003C\u002Fp>","Directus files aggregate all assets within your Directus project into one consolidated library. It is a full featured digital asset management system for storing, organizing, browsing and transforming your files. By default, director stores all uploaded files locally on disk. However you can also configure directors to use any AWS S3 compatible storage, Google Cloud storage, Azure storage, Cloudinary or Superbase. Each file managed by Directus also corresponds to an item in the Directus file system collection. Which provides a unique for each item and additional metadata. Folders in directors files can be used to organize assets within the platform, but folders are virtual and aren't mirrored within the storage adapter. As well as organization, folders can be used within access control settings to limit a user's ability to create, read, update, and delete files. The full file tree can be used so virtual folder hierarchies can reflect a user's permission. Each file in your directors project has a number of additional system fields that can be used for management. Any additional metadata directors was able to scrape from the file will also be stored. For images, this includes exif, I iptc and icc information. From within the file detail page, metadata can be seen and edited including manual cropping and setting a focal point for cropping via API. You can access files via URL in your own application by using the following structure. Being sure to provide your directors project URL and the specific file uuid that is shown in the data studio. And there's lots we can do with this url. We can provide an optional file name after the id to optimize for search engines. And directives provides out of the box asset transformations, allowing for cropping with width, height and fit parameters. A specific quality and format can also be requested along with other transformations offered by the node sharp library such as blurring, flipping and re coloring. Once an asset is transformed with all of the transformation parameters for the first time, it is stored and will not need to be transformed again. Meaning that subsequent loads should be quicker and more performant. You can also save combinations of transforms into what we call storage asset presets and then apply them just by using a key when accessing the image. Director's files is super powerful. And when combined with several of our interfaces that are both built in and provided via the director's marketplace, you have a super rich digital asset management system for any project to your building.","6a90e070-f6f4-4d93-a335-34d6b711b0fc",[1980],"0dba09ec-49a6-4cb8-9113-5a232c00a831",[],{"year":978,"number":129,"id":1866,"episodes":1983,"show":1984},[1868,6,1869,1870,1871,1872,1852,1873,1874,1875],{"title":26,"slug":31},{"id":1869,"slug":1986,"vimeo_id":1987,"description":1988,"tile":1989,"length":159,"resources":12,"people":12,"episode_number":177,"published":14,"title":1990,"video_transcript_html":1991,"video_transcript_text":1992,"content":12,"seo":1993,"status":19,"episode_people":1994,"recommendations":1996,"season":1997},"access-control","1007702200","This video explains Directus' access control system, covering user management, authentication, roles, permissions, and policies for secure and flexible data management.","8191a290-8378-460f-b6f9-7230fea84937","Access Control","\u003Cp>Speaker 0: Access control is a critical part of managing data. What different users in their various roles and responsibilities should be able to create, read, update, delete and share. Directus auth along with Directus' access control provide a complete solution for user management. From initial registration, through to interacting with data held in your project. In side of a directors project, you will have 1 or more users.\u003C\u002Fp>\u003Cp>A user conceptually is just one item in a special directors users collection, that represents a person or a service that has access to your data. Users may only be able to access your data via API or your custom application but can also be given access to data via the data studio web application. You can invite users, enable public registration or create users via API from your own application. Directus auth provides all of the required end points to manage users. From generating and refreshing access tokens, checking permissions, resetting passwords and more.\u003C\u002Fp>\u003Cp>Directus can fully manage your users or you can connect it with an existing single sign on provider to use the existing accounts to authenticate with directus. But that's all authentication, Let's talk about access control. The first concept is the permission itself. A permission is a combination of a collection, an action such as create or update, and either allow all or custom rules. By default, all actions on all collections are denied, and you can add access by creating permissions.\u003C\u002Fp>\u003Cp>Allow all is self explanatory, but for each action in each collection, custom rules can be created, which use Directus' filter syntax to specify which items can be interacted with. For example, only items created by the current user. You can also apply custom rules to limit which fields in a collection a user can access. So you might allow certain roles to edit data and others to only see it. Now given a permission is just a single action on a single collection, a user is likely to have many of them.\u003C\u002Fp>\u003Cp>And to organize them, we create access policies. And an access policy is just that, a set of permissions. For example, you might have a policy called manage own posts and use it to group the edit and delete permissions on posts created by the current user. Now roles describe a user's position within a team. For example, marketing team or editors.\u003C\u002Fp>\u003Cp>You can add roles to other roles allowing for a cascade that describes someone's full position and all that they will inherit. Now policies can be attached to either roles or users, making them a flexible organization tool for permissions. Any user's overall set of permissions are just an aggregate of all of the policies that are attached to them or their role. Now there are a lot of use case specific nuance to how access control could be configured. But just remember that a policy is a set of permissions, and policy can be attached to either a user or a role.\u003C\u002Fp>\u003Cp>Hopefully, you found this useful. Feel free to reach out with any questions, and we'll see you in the next video.\u003C\u002Fp>","Access control is a critical part of managing data. What different users in their various roles and responsibilities should be able to create, read, update, delete and share. Directus auth along with Directus' access control provide a complete solution for user management. From initial registration, through to interacting with data held in your project. In side of a directors project, you will have 1 or more users. A user conceptually is just one item in a special directors users collection, that represents a person or a service that has access to your data. Users may only be able to access your data via API or your custom application but can also be given access to data via the data studio web application. You can invite users, enable public registration or create users via API from your own application. Directus auth provides all of the required end points to manage users. From generating and refreshing access tokens, checking permissions, resetting passwords and more. Directus can fully manage your users or you can connect it with an existing single sign on provider to use the existing accounts to authenticate with directus. But that's all authentication, Let's talk about access control. The first concept is the permission itself. A permission is a combination of a collection, an action such as create or update, and either allow all or custom rules. By default, all actions on all collections are denied, and you can add access by creating permissions. Allow all is self explanatory, but for each action in each collection, custom rules can be created, which use Directus' filter syntax to specify which items can be interacted with. For example, only items created by the current user. You can also apply custom rules to limit which fields in a collection a user can access. So you might allow certain roles to edit data and others to only see it. Now given a permission is just a single action on a single collection, a user is likely to have many of them. And to organize them, we create access policies. And an access policy is just that, a set of permissions. For example, you might have a policy called manage own posts and use it to group the edit and delete permissions on posts created by the current user. Now roles describe a user's position within a team. For example, marketing team or editors. You can add roles to other roles allowing for a cascade that describes someone's full position and all that they will inherit. Now policies can be attached to either roles or users, making them a flexible organization tool for permissions. Any user's overall set of permissions are just an aggregate of all of the policies that are attached to them or their role. Now there are a lot of use case specific nuance to how access control could be configured. But just remember that a policy is a set of permissions, and policy can be attached to either a user or a role. Hopefully, you found this useful. Feel free to reach out with any questions, and we'll see you in the next video.","92be10a7-1bbf-4f83-a045-b7484a89ed1b",[1995],"f009801a-f129-4434-bf02-1c57c706487d",[],{"year":978,"number":129,"id":1866,"episodes":1998,"show":1999},[1868,6,1869,1870,1871,1872,1852,1873,1874,1875],{"title":26,"slug":31},{"id":1870,"slug":2001,"vimeo_id":2002,"description":2003,"tile":2004,"length":159,"resources":12,"people":12,"episode_number":264,"published":14,"title":2005,"video_transcript_html":2006,"video_transcript_text":2007,"content":12,"seo":2008,"status":19,"episode_people":2009,"recommendations":2011,"season":2012},"directus-connect","1007702290","This video introduces Directus Connect — automatic REST and GraphQL APIs for your database and asset storage, highlighting key features such as resource-based URLs, query parameters, and the Directus JavaScript SDK.","246ec429-2758-4590-93c5-b802fe5be579","Directus Connect","\u003Cp>Speaker 0: Part of the Directus data engine is Directus Connect. Automatic REST and GraphQL APIs for your database and asset storage. The REST API has predictable resource based URLs, relies on standard HTTP status codes, and uses JSON for input and output. To explain resource based URLs, let's use an example. Your project has a collection called posts.\u003C\u002Fp>\u003Cp>The primary key is an auto incrementing number called id. Other fields include status, title, date published, and content. For this example, some of the available resource based endpoints include listing all posts, getting a single post by ID, creating a post while passing a payload, and so on. The URL path for working with items are dynamic based on the resources in your project, but they all follow a standard format. Most directives API endpoints can utilize a number of powerful query parameters to alter the data that is returned.\u003C\u002Fp>\u003Cp>They include specifying which fields are returned, including relational fields. Filtering the returned items using the filter rules syntax, including logical operators and dynamic variables such as current user, sorting, paginating, and limiting the number of returned items, and applying aggregate functions either on the whole data set or on groups of data. Having a standard rest and graph ql API means that you can integrate a directus backend into any application in any language or framework by making an HTTP request. If you're building in a javascript the official directory's javascript sdk provides an improved developer experience. It's type safe, separated into several modules that provide granular control over which features include and then prune those that aren't at build time.\u003C\u002Fp>\u003Cp>And does not require any external libraries. You can check out the directory's API reference. Every single operation that you can make is shown both with the rest example, the graphql example and the directivesdk example side by side. Using directives over API uses the same access control as the data studio. The permissions are the same, so you don't have to set anything up again or worry that your application will receive or be able to use data in ways it shouldn't be allowed to.\u003C\u002Fp>\u003Cp>You can log in and use an access token which performs requests as your user or you can use a static token which is generated in your user profile and does not expire. If you make a request without an access token, it will use the configured public permissions, which are used when a user is unauthenticated. So this is a little bit about the APIs and the SDK. We have a load of tutorials inside of our documentation on Directus TV and on our YouTube channel in order to understand how to actually use the APIs and SDK to build real world projects. If you have any questions, feel free to reach out, and we'll see you in the next video.\u003C\u002Fp>","Part of the Directus data engine is Directus Connect. Automatic REST and GraphQL APIs for your database and asset storage. The REST API has predictable resource based URLs, relies on standard HTTP status codes, and uses JSON for input and output. To explain resource based URLs, let's use an example. Your project has a collection called posts. The primary key is an auto incrementing number called id. Other fields include status, title, date published, and content. For this example, some of the available resource based endpoints include listing all posts, getting a single post by ID, creating a post while passing a payload, and so on. The URL path for working with items are dynamic based on the resources in your project, but they all follow a standard format. Most directives API endpoints can utilize a number of powerful query parameters to alter the data that is returned. They include specifying which fields are returned, including relational fields. Filtering the returned items using the filter rules syntax, including logical operators and dynamic variables such as current user, sorting, paginating, and limiting the number of returned items, and applying aggregate functions either on the whole data set or on groups of data. Having a standard rest and graph ql API means that you can integrate a directus backend into any application in any language or framework by making an HTTP request. If you're building in a javascript the official directory's javascript sdk provides an improved developer experience. It's type safe, separated into several modules that provide granular control over which features include and then prune those that aren't at build time. And does not require any external libraries. You can check out the directory's API reference. Every single operation that you can make is shown both with the rest example, the graphql example and the directivesdk example side by side. Using directives over API uses the same access control as the data studio. The permissions are the same, so you don't have to set anything up again or worry that your application will receive or be able to use data in ways it shouldn't be allowed to. You can log in and use an access token which performs requests as your user or you can use a static token which is generated in your user profile and does not expire. If you make a request without an access token, it will use the configured public permissions, which are used when a user is unauthenticated. So this is a little bit about the APIs and the SDK. We have a load of tutorials inside of our documentation on Directus TV and on our YouTube channel in order to understand how to actually use the APIs and SDK to build real world projects. If you have any questions, feel free to reach out, and we'll see you in the next video.","25b805c7-a2c2-4d36-9476-12c890c65746",[2010],"51785675-45e0-419d-aa67-42bfc8f90cd7",[],{"year":978,"number":129,"id":1866,"episodes":2013,"show":2014},[1868,6,1869,1870,1871,1872,1852,1873,1874,1875],{"title":26,"slug":31},{"id":6,"slug":7,"vimeo_id":8,"description":9,"tile":10,"length":11,"resources":12,"people":12,"episode_number":13,"published":14,"title":15,"video_transcript_html":16,"video_transcript_text":17,"content":12,"seo":18,"status":19,"episode_people":2016,"recommendations":2017,"season":2018},[21],[],{"year":978,"number":129,"id":1866,"episodes":2019,"show":2020},[1868,6,1869,1870,1871,1872,1852,1873,1874,1875],{"title":26,"slug":31},{"id":1868,"slug":2022,"vimeo_id":2023,"description":2024,"tile":2025,"length":159,"resources":12,"people":12,"episode_number":2026,"published":14,"title":2027,"video_transcript_html":2028,"video_transcript_text":2029,"content":12,"seo":2030,"status":19,"episode_people":2031,"recommendations":2033,"season":2034},"what-is-directus","1026478250","In this introduction, you will learn about the Directus platform, offering rich developer tools and an intuitive web application for managing data-driven projects⁠.","016a7543-2363-4d5f-a9ab-fa65d0c84999",0,"What is Directus?","\u003Cp>Speaker 0: Welcome to Directus Academy. My name is Kevin Lewis and I run our education programs here at Directus. Throughout this set of videos, I'm gonna take you on a tour of the Directus platform. So you understand what it's capable of and how to use it. So Directus is a back end for developers.\u003C\u002Fp>\u003Cp>It connects to any new or existing SQL database and asset storage and immediately provides rich developer tooling that we call the data engine, and a really lovely web application that is approachable enough to give to all of your team whether they are developers or not. We call it the data studio. Granular and powerful access control means users can only see, interact with, and create data allowed by their role and access policies used by both the engine and the studio. This means that however a user is given access via API, your own application built on top of those APIs, or the data studio, their level of access remains the same. Directus is also hugely extensible through the Directus marketplace.\u003C\u002Fp>\u003Cp>Developers can author and publish extensions with Node. Js and Vue. And project admins can install them in any Directus project. Now Directus does not add or change fields in your database to operate, which means you can remove it at any time. You can self host directors or use directors cloud, if you don't wanna worry about infrastructure, maintenance, or updating your project.\u003C\u002Fp>\u003Cp>Directors can be used to power any data driven project. Whether that's fleets of connected devices, ecommerce data, building a sass, or content for your applications. You can use it as a back end, a content management system, an internal tool builder, or a data management and analytics platform. When you connect directors to your database and asset storage, we use a technique called abstraction to handle the nuance of your specific data sources and provide them in a sources and provide them in a consistent interface both in terms of the engine and the studio. On top of the abstraction layer for your database is a powerful query language and an event based automation system that powers multiple parts of directors.\u003C\u002Fp>\u003Cp>On top of the asset storage abstraction is a system to allow on the fly media transformation. Director's auth comes next, effectively gating the ability to access and operate on your data through a native authentication system or via your existing single sign on provider. Finally, the interfaces to access the data. Directus offers rest and graph ql APIs for each table in your database. As well as a real time connection via web sockets or graph ql subscriptions.\u003C\u002Fp>\u003Cp>The directus SDK makes working with directus in your JavaScript projects even easier. And of course, if you want to give access to your data via a web UI to anyone in your application, you can use the directors data studio. Before we wrap up, a note on the directors license. Directors is licensed under the business source license 1.1 for commercial usage. This means you require a license if you are an entity with over $5,000,000 in total finances.\u003C\u002Fp>\u003Cp>Otherwise, you can continue to enjoy Directus in full and commercially without a license. Now that's all for this video. Thank you for joining me. And we're gonna start jumping into the various parts of Directus in more depth in the next one. Have a great day and bye for now.\u003C\u002Fp>","Welcome to Directus Academy. My name is Kevin Lewis and I run our education programs here at Directus. Throughout this set of videos, I'm gonna take you on a tour of the Directus platform. So you understand what it's capable of and how to use it. So Directus is a back end for developers. It connects to any new or existing SQL database and asset storage and immediately provides rich developer tooling that we call the data engine, and a really lovely web application that is approachable enough to give to all of your team whether they are developers or not. We call it the data studio. Granular and powerful access control means users can only see, interact with, and create data allowed by their role and access policies used by both the engine and the studio. This means that however a user is given access via API, your own application built on top of those APIs, or the data studio, their level of access remains the same. Directus is also hugely extensible through the Directus marketplace. Developers can author and publish extensions with Node. Js and Vue. And project admins can install them in any Directus project. Now Directus does not add or change fields in your database to operate, which means you can remove it at any time. You can self host directors or use directors cloud, if you don't wanna worry about infrastructure, maintenance, or updating your project. Directors can be used to power any data driven project. Whether that's fleets of connected devices, ecommerce data, building a sass, or content for your applications. You can use it as a back end, a content management system, an internal tool builder, or a data management and analytics platform. When you connect directors to your database and asset storage, we use a technique called abstraction to handle the nuance of your specific data sources and provide them in a sources and provide them in a consistent interface both in terms of the engine and the studio. On top of the abstraction layer for your database is a powerful query language and an event based automation system that powers multiple parts of directors. On top of the asset storage abstraction is a system to allow on the fly media transformation. Director's auth comes next, effectively gating the ability to access and operate on your data through a native authentication system or via your existing single sign on provider. Finally, the interfaces to access the data. Directus offers rest and graph ql APIs for each table in your database. As well as a real time connection via web sockets or graph ql subscriptions. The directus SDK makes working with directus in your JavaScript projects even easier. And of course, if you want to give access to your data via a web UI to anyone in your application, you can use the directors data studio. Before we wrap up, a note on the directors license. Directors is licensed under the business source license 1.1 for commercial usage. This means you require a license if you are an entity with over $5,000,000 in total finances. Otherwise, you can continue to enjoy Directus in full and commercially without a license. Now that's all for this video. Thank you for joining me. And we're gonna start jumping into the various parts of Directus in more depth in the next one. Have a great day and bye for now.","faa0fe76-e10b-43b0-942b-28aa3ad7b2f4",[2032],"f70a8276-d086-4f34-bcea-0a31318e3126",[],{"year":978,"number":129,"id":1866,"episodes":2035,"show":2036},[1868,6,1869,1870,1871,1872,1852,1873,1874,1875],{"title":26,"slug":31},{"id":2038,"slug":2039,"vimeo_id":2040,"description":2041,"tile":2042,"length":159,"resources":12,"people":12,"episode_number":13,"published":2043,"title":2044,"video_transcript_html":2045,"video_transcript_text":2046,"content":12,"seo":2047,"status":19,"episode_people":2048,"recommendations":2050,"season":2051},"f2329f45-fd10-4214-8e5b-37201ce6b0ba","echobind","1005903280","Meet Echobind, a product consultancy and digital agency who work with companies looking to build ambitious software, and hear about how they use Directus for multiple clients such as Annex, an e-commerce platform for document storage warehouses and Hope Media Group, a Christian media conglomerate that fosters faith and community.","3ca05862-d636-4508-b219-f4f96ac7dae5","2024-09-04","Echobind","\u003Cp>Speaker 0: Hi. My name's Michael Yared. I'm the co founder and CEO of EchoBind, a product consultancy and digital agency. Our employees reside in 16 different states across the US and we work with companies looking to build ambitious software. We have deep experience in healthcare, media and financial services.\u003C\u002Fp>\n\u003Cp>Speaker 1: Annex is an e commerce platform for document storage warehouses, allowing customers to pick and choose the best warehouses for their needs. When we're talking about document warehouses, imagine warehouses filled with boxes of documents, paper, film, anything like that that you might see in a movie or a court drama. We use Directus to power both the marketing content for the Nx platform, but also provide a way to provide dynamic layouts, marketing blurbs, and engineering specific needs. So being able to reorder, being able to create new content on the fly or create new types of content on the fly. We had an interesting challenge with this particular customer where they have multiple sites, multiple different environments that needed to be able to sync up everything back onto their production website.\u003C\u002Fp>\n\u003Cp>Directus, the flexibility that it provides, allowed us to easily sync up both their marketing site, their staging environment, and their production environments, and move data and content back and forth as we needed to throughout the entire process.\u003C\u002Fp>\n\u003Cp>Speaker 2: Hope Media Group is a Christian media conglomerate that fosters faith and community through a whole bunch of websites, radio stations, and events that they put on. And they wanted to recreate their website experience to make it so that they could set up new websites faster and share content between those websites. So we created a Directus template, which they could use to spin up new websites very quickly just by launching a new Directus instance. This template connects to a parent instance, which is able to keep track of all of the children, all of the other sites. They can push content to the parent, and then the parent can choose which of the websites they want to send the content down to so they can easily share between the different websites.\u003C\u002Fp>\n\u003Cp>We also included a block builder for creating pages based on Directus' agent c OS template so that they can slot in dynamic content sections onto whichever page that they want. And since we're building this using Next. Js and React server components, we're able to fetch the data that we need from Directus, pull down the client components that are needed for interactivity so it's very efficient. And Next. Js automatically caches all of the data that it pulls, so the websites load really fast.\u003C\u002Fp>\n\u003Cp>We can also invalidate those caches and get the freshest data possible from Directus by using Directus triggers to, let Next. Js know whenever anything's changed.\u003C\u002Fp>\n\u003Cp>Speaker 0: And at EchoBind, we love using Directus for a number of reasons. It allows our team to move incredibly quickly, and we can usually see an app come to life the same day we start. Directus also allows us to integrate with any stack we're working on. It's perfect because we can use Directus with any of our clients and that flexibility is so helpful. It gives us the ability to morph a project as our client's needs change.\u003C\u002Fp>\n\u003Cp>In a few recent projects, we've used it to configure a mobile app on the fly, power a company intranet, and it even serves as the primary interface for an electric vehicle database. It's that versatility and speed that makes Directus an invaluable part of our toolkit. If you enjoyed this episode and wanna learn more, best thing to do is to get in touch with us through our website. Visit echobind.com, click contact, and we'll be in touch really quickly. Our team is always happy to chat about your business challenges.\u003C\u002Fp>","Hi. My name's Michael Yarid. I'm the co founder and CEO of EchoBind, a product consultancy and digital agency. Our employees reside in 16 different states across the US and we work with companies looking to build ambitious software. We have deep experience in healthcare, media and financial services. Annex is an e commerce platform for document storage warehouses, allowing customers to pick and choose the best warehouses for their needs. When we're talking about document warehouses, imagine warehouses filled with boxes of documents, paper, film, anything like that that you might see in a movie or a court drama. We use Directus to power both the marketing content for the Nx platform, but also provide a way to provide dynamic layouts, marketing blurbs, and engineering specific needs. So being able to reorder, being able to create new content on the fly or create new types of content on the fly. We had an interesting challenge with this particular customer where they have multiple sites, multiple different environments that needed to be able to sync up everything back onto their production website. Directus, the flexibility that it provides, allowed us to easily sync up both their marketing site, their staging environment, and their production environments, and move data and content back and forth as we needed to throughout the entire process. Hope Media Group is a Christian media conglomerate that fosters faith and community through a whole bunch of websites, radio stations, and events that they put on. And they wanted to recreate their website experience to make it so that they could set up new websites faster and share content between those websites. So we created a Directus template, which they could use to spin up new websites very quickly just by launching a new Directus instance. This template connects to a parent instance, which is able to keep track of all of the children, all of the other sites. They can push content to the parent, and then the parent can choose which of the websites they want to send the content down to so they can easily share between the different websites. We also included a block builder for creating pages based on Directus' agent c OS template so that they can slot in dynamic content sections onto whichever page that they want. And since we're building this using Next. Js and React server components, we're able to fetch the data that we need from Directus, pull down the client components that are needed for interactivity so it's very efficient. And Next. Js automatically caches all of the data that it pulls, so the websites load really fast. We can also invalidate those caches and get the freshest data possible from Directus by using Directus triggers to, let Next. Js know whenever anything's changed. And at EchoBind, we love using Directus for a number of reasons. It allows our team to move incredibly quickly, and we can usually see an app come to life the same day we start. Directus also allows us to integrate with any stack we're working on. It's perfect because we can use Directus with any of our clients and that flexibility is so helpful. It gives us the ability to morph a project as our client's needs change. In a few recent projects, we've used it to configure a mobile app on the fly, power a company intranet, and it even serves as the primary interface for an electric vehicle database. It's that versatility and speed that makes Directus an invaluable part of our toolkit. If you enjoyed this episode and wanna learn more, best thing to do is to get in touch with us through our website. Visit echobind.com, click contact, and we'll be in touch really quickly. Our team is always happy to chat about your business challenges.","9e9930a7-acc3-4cfc-8ec8-e0a8d9017f41",[2049],"93c0ec35-5510-431c-b0a3-4afa2596dba4",[],{"year":978,"number":195,"id":2052,"episodes":2053,"show":2054},"d6ad0313-a62a-4195-a21a-e30c963e0b7b",[2038],{"title":2055,"slug":2056},"Agency Corner","agency-corner",{"id":1799,"slug":2058,"vimeo_id":2059,"description":2060,"tile":2061,"length":2062,"resources":2063,"people":12,"episode_number":740,"published":2066,"title":2067,"video_transcript_html":2068,"video_transcript_text":2069,"content":12,"seo":2070,"status":19,"episode_people":2071,"recommendations":2075,"season":2076},"9161","1004131420","In this recording of our live event on August 29th 2024, Daniel, Jonathan, and Rijk discuss Additional Form\u002FField Layout Options.","fb2eeeee-6c4e-4227-adb1-9806fc50f2cf",54,[2064],{"name":1775,"url":2065},"https:\u002F\u002Fgithub.com\u002Fdirectus\u002Fdirectus\u002Fdiscussions\u002F9161","2024-08-29","Additional Form\u002FField Layout Options","\u003Cp>Speaker 0: We are covering additional form and field layout options. This is actually related to our item editing experience, and it is something that I would actually love to have. We're very we have some awesome capabilities right now, but the 2 field across kinda limitation, it would be nice to have some more flexibility there. So that's our discussion. This is discussion number 9161.\u003C\u002Fp>\u003Cp>I'll drop it in the chat in case anyone wants to follow along there.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, 4 digits. You know, it's an old one when you're in the 4 digits.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, yeah. This is an oldie. An oldie but a goodie. This was an October 26, 2021 Ben Haines special.\u003C\u002Fp>\u003Cp>Speaker 2: Oh, damn.\u003C\u002Fp>\u003Cp>Speaker 0: But it actually surprisingly, this was pre RFC, but Ben actually put in example screenshots, good description. It wasn't just a one liner. I I was actually very pleasantly surprised when I opened this up to review it.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It's it's a good day when the feature request is not a one liner. It's Yes. It's great. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: So I will let you guys kick it off. Alright. Well, 4 pages.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Right. So if I understand this correctly, we are talking about having a different layout for editing entries, which we currently do in 2 columns. You can divide your editing experience into a left column, right column, or go through with the whole input field? And wouldn't it be nice if we could do something else?\u003C\u002Fp>\u003Cp>I guess, it's the question. What could we have?\u003C\u002Fp>\u003Cp>Speaker 0: So yeah. That's the the general question is a little bit more flexibility, more feet you know, again, things like toggles. Right? Don't need even half width. They could be 25%.\u003C\u002Fp>\u003Cp>3rd. You know, maybe maybe a percentage based. Some of the more the more interesting feature styles, I think, that we can do today or that are possible. So Ben's got some different examples of what you might wanna have for layout options. Although this this was actually noted a little bit further down in this request that putting the label to the side is actually negatively impacting to users' experience.\u003C\u002Fp>\u003Cp>It's not not a recommended practice in general, but having some flexibility and being able to do some things. I think more so just being able to add more more configuration in the layout style, and and how things get positioned It's the general my my general client feedback, community feedback that I've heard, you know, in general with meetings and talking to people is just the ability to have more flexibility in the item editing design experience.\u003C\u002Fp>\u003Cp>Speaker 2: So quick question.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I was gonna I I I think you're gonna go the same way as I'm gonna go, Daniel, but I'll let you go first.\u003C\u002Fp>\u003Cp>Speaker 2: Not sure. I'm I'm just, I just want to clarify a little bit because, because of the screenshot. So currently, I think we're only talking or a question. Are we only talking about the layout in and of itself? So just positioning of elements beside each other, or are we also talking about different visual styles for the same interfaces?\u003C\u002Fp>\u003Cp>Because, when you scroll down a little bit, for the checkboxes, for example, they look different than what we have currently. Is also part of this, idea that, for example, you can have multiple different, toggles? Let's say the this one this let's call it filled in toggle or a different toggle. What about the, like like, what is it checkboxes? Now the selects where you can only select 1, like radio buttons for example cause they are a couple of examples here right the product category for example.\u003C\u002Fp>\u003Cp>Because they look different. Like, are we currently just talking about the layout or are we also talking about different styles?\u003C\u002Fp>\u003Cp>Speaker 0: Let's look.\u003C\u002Fp>\u003Cp>Speaker 2: Okay. Oh,\u003C\u002Fp>\u003Cp>Speaker 0: no. He's got interface options to add well. So he's adding additional interface optioning capabilities as well. I thought, initially, I missed that. I thought it was just the item view formatting, you know, tab styles, other kinds of capabilities potentially.\u003C\u002Fp>\u003Cp>It's another frequent one that we see is instead of having just a full list of fields. Right? We have detailed things currently, but like a tab style interface there as a presentation layer. Could be other things we can consider. We can decide what we want this to be.\u003C\u002Fp>\u003Cp>We can narrow this. We can put this into RFC spec format, when we're done. So I'm happy to, well, you know, we'll do our let's do explode and then figure out\u003C\u002Fp>\u003Cp>Speaker 2: what we\u003C\u002Fp>\u003Cp>Speaker 0: want this to actually cover when we're done. So primary things that clients currently right? This is your current capability. Let me grab one that's got some, do I have one with groups? Remember if I've got something with field groups in it.\u003C\u002Fp>\u003Cp>Top of my, those products haven't.\u003C\u002Fp>\u003Cp>Speaker 1: Why?\u003C\u002Fp>\u003Cp>Speaker 2: You guys I think in in the end, when we when we scope down again, in the end, we can probably, like, make a make a rough, MVP of this feature where we just say, okay, maybe this is just it. But if you wanna take it further, then it would also include different, visuals for the same interface.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So these are kind of your current options. Right? You can have field groupings. Right?\u003C\u002Fp>\u003Cp>And otherwise, you got it didn't half with. So to me yeah. I think we should start with with let's let's think about the I think I mean, options to existing interfaces could be discussed here. But I think in general, the way that this thing started and the way that I see this, the the biggest struggle that clients have today is you have very limited kinds of options actual form editing experience. How things can be laid out.\u003C\u002Fp>\u003Cp>Yeah. For sure. I think the other parts,\u003C\u002Fp>\u003Cp>Speaker 1: you know, a toggle instead of a checkbox, that's a new interface. That's just a custom interface or an option or stuff like that. But it doesn't really change the way the form itself is rendered. So I think for the sake of this discussion, we can focus on that. Cool.\u003C\u002Fp>\u003Cp>I think some of the bigger ones that we see here in the feature request is around where does the field label and description go. I think that is sort of one bucket. Does it go top? Does it go bottom? Does it go left?\u003C\u002Fp>\u003Cp>Does it go right? Then around how do certain groups come together? So there's like a sort of a new group UI, like a group of the title, and it's in a gray box instead of on a white background sort of visually, you know, cluster some of these form inputs, which then made some where the descriptions go. And then the other one is around, and it's not so much in the original screenshots, funnily enough, but it is I think of an important one here to bring up is what does the grit look like if you go beyond 2? Right?\u003C\u002Fp>\u003Cp>And and, where I can see this discussion going quickly is what about responsive? How does that work? And what about, on the screen that is about 6 meters wide, to make that work in a way that is still user friendly?\u003C\u002Fp>\u003Cp>Speaker 2: That is a good question. I this reminds me of a small little bug if we want to just take a little tangent for a story time. Because we have this little bug, where if you have a checkbox with multiple, options, we change the way how they are displayed depending on how many options you have. So if you only have 2, then they are right beside each other. But, for example, or if you have, like, I don't know, 5 or let's say 10, then we put them below each other.\u003C\u002Fp>\u003Cp>And we had this interesting fun little little bug is that we made this also dependent on the content of those checkboxes. So if you have long text, then we actually, you know, span the whole width so you can actually read the thing inside of the checkbox. But what this resulted in is that actually, in like languages which are very short and there are languages that which are very long, like in German words, you know, which means that you have different layouts depending on what kind of language you have. And then, there were a couple users that used Chinese characters, Mandarin characters. And this then resulted, like, if you translate it and then you got different layouts for different languages and stuff, that that was quite quite a fun thing.\u003C\u002Fp>\u003Cp>I think I fixed that, by the way. But, okay. Enough of that. Sorry. Yeah.\u003C\u002Fp>\u003Cp>Let's see. I mean, there's there's lots of perspectives. Right? Like, how many columns should that be dynamic? Do we want to provide a fixed fixed width as of right now?\u003C\u002Fp>\u003Cp>Like, 2. Let's just say, okay. You get 12. Is that good enough? And how do you configure the responsiveness?\u003C\u002Fp>\u003Cp>Then how do we have breakpoints? How many breakpoints do we have? 2, mobile, desktop, but not every screen is the same. How do you configure that? Do do we even want to configure that?\u003C\u002Fp>\u003Cp>Like, yes, but no. Meh. I mean, really, how much database work and actual data modeling are you interested in doing on mobile, to be honest? Like, how many people actually, editing the databases and data records on the phone? Like, how important is he's he's funny.\u003C\u002Fp>\u003Cp>The answer\u003C\u002Fp>\u003Cp>Speaker 1: is very important.\u003C\u002Fp>\u003Cp>Speaker 0: This guy does.\u003C\u002Fp>\u003Cp>Speaker 1: You'd you'd be surprised by how many people use mobile or mobile adjacent stuff now too. Right? If you have the folks with flip phones and you have people with with, you know, small iPads or, yeah. Yeah. Just going by the chat.\u003C\u002Fp>\u003Cp>People are saying the same thing too. Mobile interface, main point, which isn't direct is people editing videos on their phones. The people do everything on their phones nowadays, and I'm one of them too for what it's worth. So when it comes to grit and responsive, this is this is one of the reasons why, there's a lot of apps that are sort of, like, app builders, like a read tool, for example, that opt to make it a scrollable window when you go smaller for this exact reason so it doesn't have to do any sort of weird new reflowing. There is tools out there that I won't name by name that just straight up say there's no native there's no responsive design at all.\u003C\u002Fp>\u003Cp>There's no mobile version. There is different ways of of handling that. The way Directus does it right now is in the in the sort of 2 grid in the 2 up, the screen gets too small, becomes a one up. And that an easy easy enough sort of reflow to make. Right?\u003C\u002Fp>\u003Cp>Because whatever used to be side by side is now beneath each other, and that is good enough. Right? It it's it's what you'd sort of expect to happen, and it doesn't really cause anything too strange, from happening. If you're in a 6 column grid or so, like, if I'm scrolling down this feature request a little bit more, there were some screenshots from a tool called They have, like, a a sort of rich layout, which is roughly 4 columns. But then within, you know, one of the columns, there's another sort of 6 column y thing nested.\u003C\u002Fp>\u003Cp>And now I'm like, okay. Well, if you're responsibly squishing that down, weird things will happen. Like, what goes where? How does it flow? How does it grow?\u003C\u002Fp>\u003Cp>Do you expect, like, the 4th column to become, 3 columns of white when it goes to the next one? Do you intend it to still be blocked, but then you have 3 and 1 sort of weirdly underneath it? Or does it switch forward to a 2 grid? There's there's weird questions that happen at that point when you go down. And all so how does it interact with other fields that are then further down the page?\u003C\u002Fp>\u003Cp>Right? So right now, because every field is basically just in the same, same list of fields, It would start showing up side by side with other fields in that same grid, which could get funky and sort of unintentional. So one way around that potentially is to instead of doing a grid, let me do it now where it is based always one column, but there's a group type for creating grid sections in your form. Right? Because that way, it will always wrap within just that group.\u003C\u002Fp>\u003Cp>You don't your unintended interactions with other fields further down the page. And then that also means that you can start nesting those in whatever way you want. So if you're trying to re replicate that sort of 4 knocks set up where you have 4 columns, where the 4th column itself is like a a 6 column grid or something, you could because you can start nesting those those grid forms. Right? And it it kinda starts responsively matching the column size rather than the form size.\u003C\u002Fp>\u003Cp>Hannes in the chat says, additional groups feel cumbersome to set up. Yeah. Fair. Although at the same time, you know, to achieve the same thing that we have now, you could have one group and just put everything in it. Doesn't necessarily, you know is that is that what the price of admission get for, flexibility in the new layouts?\u003C\u002Fp>\u003Cp>And then on that same note, you know, the the some of the other layout settings, like, is the field name on the left or on the top, the description on the top or on the bottom, that kind of stuff could be settings of a group. Like, it doesn't have to be, settings of the whole form at once. Right? We could also say that the form, by definition, just has one root group, and therefore, it's always there, but it uses the same same system.\u003C\u002Fp>\u003Cp>Speaker 2: Alright. I can see that work in my hand. Like, is there a leaner way with less clicking? Currently trying to, like because if for me for me personally, the I I want I would want it to be as least intrusive as it can be because because I just wanna set up my thing and I wanna keep going. Right?\u003C\u002Fp>\u003Cp>Like I do not want to spend my time with designing, you know, layout. Like like this is like, I'm not sure how to say it. Like I I want to model stuff, I want to make like this this is how it how it works now. Like I just want to model stuff left, right, things and move on, you know. And I have other stuff to do.\u003C\u002Fp>\u003Cp>I have problems to solve as a administrator, let's say, of an instance, and I just wanna put some stuff in and keep going. So if you then have to jump through hoops and and, you know, like with all the, oh, but then on mobile, because I didn't, click on the full width. Now on mobile it is only half and it looks bad. And then I have to click this again and then the drawer opens again and I have to go into specific settings of a thing and then close the drawer again and choose the other. And that's that that feels icky.\u003C\u002Fp>\u003Cp>Like, I'm not sure if if we could design it in a way maybe with just small little toggles, like, without, specific options for those interfaces, when, like, the drawer opens, maybe we can make them just, small little toggles that you quickly go through just just without leaving the modeling page. Maybe maybe I don't know, like, it did did that make sense?\u003C\u002Fp>\u003Cp>Speaker 1: It does. It it's a, you know, it's a tricky thing because on the one hand, it's like if you wanna do it sort of quick and dirty, so to speak, then that's fine. That should be possible. At the same time, if you wanna make the best user experience for your end user so so let me back up a little bit. I think there's a difference in, flow when you're first setting it up and you're doing the data modeling versus when your data modeling is done and sort of finalized and you go into the details to make it a nicer user experience for your end user.\u003C\u002Fp>\u003Cp>At which point, you might set up, you know, I wanna use monospace for an input and add a little icon to make it, you know, friendlier for the end user, that kind of stuff. Right? I think that is also where this whole gridding stuff comes in. It's like, do you care about grids and how it responsibly shows up side by side instead of ordered top to bottom? Well, if you're doing the data modeling, possibly, probably not.\u003C\u002Fp>\u003Cp>It's just like you need to have the fields to check if it works, build the integration, move on. Right? Only then when your data modeling piece is done, can you circle back and be like, okay. Now I'm gonna spend an hour or so to just really make this form the best form it can possibly be for the use case that I have. I wanna conditionally show and hide some things.\u003C\u002Fp>\u003Cp>I wanna do it in certain sections have to be like a 4 up grid or a half half width group where, I wanna order it differently, etcetera, etcetera. Right? So I think there's 2 different points in time and 2 different, user flows that that are related. So to your point, I do agree. It's like for that first one, I mean, you're just setting up the data modeling, experimenting with what fields and what names and everything else.\u003C\u002Fp>\u003Cp>Quick and dirty. You don't wanna be slowed down by having to think about, is it 2 or 4 or 6 columns? Like, who cares? Just go quick. Right?\u003C\u002Fp>\u003Cp>Make it work. But I do think that is where we can introduce that additional flexibility that we're talking about as part of this this feature request to saying, well, if we hard code this form to be 4 columns instead of 2, we're gonna have the same discussion a month from now where it's like, okay. What about 6? Right?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. And we're\u003C\u002Fp>\u003Cp>Speaker 1: gonna have the same discussion a month from now where it's like, okay. Somebody wants 12. So by doing it as a group extension type where we say, okay. The group is just the thing that makes it x number of columns and we don't care. We allow that flexibility in a sort of opt in type of fashion rather than us having to predefine, okay, a direct this form is always x.\u003C\u002Fp>\u003Cp>Like, it is now. We say it's always to deal with it. Right?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. For for me, priority would be, just looking at at the screen right now, let's see, let's look at the theming group, right, or the SEO group also. I would like to avoid, or it would be it is near and dear to my heart, you know. Like, if I now want to split the theming group, I don't, like, I don't want to be the kind of back end where you have to click on the, 3 dots, then a dialogue or, like, a popover opens, then you have to click on edit fields, then the drawer opens, then you have to go inside of that, and then you have to configure some type of grid system and break points and stuff and then the whole thing closes and then, oops, I forgot one thing, I have to go through the entire thing again like this. Like I really want to avoid that.\u003C\u002Fp>\u003Cp>So I'm like, okay, And we maybe make sure that this doesn't happen. Maybe we can include some controls that are maybe enough already on the screen right now. So like, side the three points where the options would be, maybe there could be quick toggles where you can very quickly just with one click, like, boop, and then it's half view. And, you can configure it and then another click, boop, it's it's it's the mobile view. And then you can configure it again.\u003C\u002Fp>\u003Cp>Yeah. How about that?\u003C\u002Fp>\u003Cp>Speaker 1: So the reason why it has to be a drawer now is that those groups and interfaces themselves are an extension type. So, therefore, we don't know what those options are. Right? So if somebody makes a new custom grid group now, which you could theoretically, the options for that group is like a an open ended question mark. It's just a set of fields, and we don't know what they are.\u003C\u002Fp>\u003Cp>And we don't know how to, you know we we know how to render them because we ask you we ask the extensions, like, what options do you wanna render? And it's basically just another view component. Right? So it's like a black we just anything goes into that drawer and have have at it. So we can do that.\u003C\u002Fp>\u003Cp>We'll have to come up with some sort of standardized syntax for those plugins to say, here is the 2 or 3 things I wanna be able to edit in line that makes sense for this type of thing that I'm doing. Or we have to go the other way and say, well, we just built more of an Apple approach. That's who it where we say, well, the direct extensions are special and they get to do some of these inline things, but regular extensions are back to the old drawer style. You know what I mean? It's just that that flexibility angle because all of these are extensions.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I get it. That's a bummer. Like, because we we strive and we always, our strength, right, is that everything is so super duper flexible that you can do anything yourself. Yeah.\u003C\u002Fp>\u003Cp>But for this, can we?\u003C\u002Fp>\u003Cp>Speaker 1: It's a question. Right? I I think when it comes to the drag and drop UI of of doing the layout of the form, if if you have a 4 grid, we have to show it as a 4 grid in line there as well in your editing experience. Like, that, I think, is is definitely something that we have to code in. But then if there's a group that has an option for number of grids, how does that how do we connect the dots?\u003C\u002Fp>\u003Cp>Right? How do we know that the grid column option in or an extension should be mapped to our editing experience for the form that makes it so that makes it work like that. Or again, maybe that there's the case that we say, well, you know, the way to handle grids is just not an extension type and there's no way for extensions to do that. It's just the native direct listing. Where we stay, okay, form layout, order, and grid, that is just a a direct as native thing.\u003C\u002Fp>\u003Cp>You can change it through extensions or anything else. You can still make a custom group, but the layouting options for that is not something that we can natively, manage on your behalf. Like, that that is also, you know, an option.\u003C\u002Fp>\u003Cp>Speaker 2: Mhmm. Yeah. Like, I I would be like, let let's let's just, you know, the, gut gut feeling is just this sounds reasonable because it's not actually concerning your data. It's just like our our, product UI. So it makes sense that we have this control of the layout.\u003C\u002Fp>\u003Cp>But I can already see, like, it's this is gonna be the first comment in that PR. It's like, oh, but I really would like to do this in my thing. Can we can we add that also? And then every time. Every content.\u003C\u002Fp>\u003Cp>Okay. Alright. So so this is the layout type of thing of that PR of that of that RC. I think it just okay. Let's you said in the beginning the other interfaces are mostly just other interfaces, so they aren't really concerned with this.\u003C\u002Fp>\u003Cp>Speaker 1: I think the difference between a checkbox and a toggle, that is an interface option or that is a new interface. I don't think that has anything to do necessarily with form rendering. So I think for the sake of this art for this discussion, we can just keep the scope to form rendering itself. I saw there there was some a couple of good points in the chat earlier right before we started with a couple of points around a little bit more general updates to forms and and interfaces, what we can do with it. 20 minutes ago, somebody was saying, it will be cool if you have, like, a 3, 4 column layout instead of just 2, expand the form width.\u003C\u002Fp>\u003Cp>So it's like the whole screen instead of just, you know, a a max width. But you could kinda do because there's, like, a full width option for an individual in the face or group, but it's I I do agree it's the whole form. It doesn't quite quite work like that. Aligning the form itself to the left or the center of the screen or maybe even the right. And then it gets into which I feel like a bit of a different feature request, but I wanna call it out because it's in all caps, so it sounds important.\u003C\u002Fp>\u003Cp>Actual relation drop downs, which I think is just make making sure that a second drop down is updated based on the value of the first drop down, which is, again, kind of not really\u003C\u002Fp>\u003Cp>Speaker 0: formed. I think it's actually I think it's actually more related to the relational interface itself right now. They always go to a drawer. I think it's more of the so like, a many to 1 or one to many.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, that part of that nature drop down.\u003C\u002Fp>\u003Cp>Speaker 0: As opposed to a drawer pop. Yeah. That's an interface itself. That to me is again, I think we're kind of a little out of scope for this for what I think we want this review to cover in detail. Okay.\u003C\u002Fp>\u003Cp>Yes. There are there are dozens of things we would all like to have on the interfaces side of the house, but that's why custom extensions exist and you can't build them. So although, you know, this grouping style that we support, I have seen tab style, the group interfaces where someone has built a custom interface that actually does, like, tab styling and multiple fields and tabs and things inside their groups. So custom extensions support those kinds of things. Yes.\u003C\u002Fp>\u003Cp>Relational drop downs instead of drawers would be really nice in many use cases.\u003C\u002Fp>\u003Cp>Speaker 1: You know, it's, you know, it's funny. This is a complete sidebar, and I I wanna be cognizant that we don't end up spending 20 minutes on this because it's an easy one to slip into. But the many to 1 interface, the drop down one that opens drawer, it has the the actual drop down bits built in. It like, when we built it, it had that baked in. It's the problem was that similarly with most every other thing in direct is, like, week 2, somebody was like, I have a 100,000 items in the related\u003C\u002Fp>\u003Cp>Speaker 0: Yes. Related table and I I am now going to break your interface. Outright.\u003C\u002Fp>\u003Cp>Speaker 1: The drop down only shows the first a 100, so I cannot find the thing to select. And I'm like, yep. Yep. Yep. Yep.\u003C\u002Fp>\u003Cp>Yep.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. So then\u003C\u002Fp>\u003Cp>Speaker 1: then we made the call to, like, always show it as a drawer, which works for everything, but it's it's not as nice as a little drop down. But it it used to work like that at one point in time. Yeah. Yep. Yep.\u003C\u002Fp>\u003Cp>Speaker 0: I know we've had bring that\u003C\u002Fp>\u003Cp>Speaker 1: back as a as an option to to but but the the funny thing there is, like, to do it the best way possible, we need to have an updated count of the number of related items at any point, and getting that count can be very slow if you have 100 of 1,000 of them. So we have to come up with some sort of way to cache the count our site first so we have a sort of estimate count that we can use and then update that over time because, otherwise, it's gonna be not performing enough to do you know? Anyways, long story short\u003C\u002Fp>\u003Cp>Speaker 0: Yes. Similar thing that so we did that for the actual layouts, as part of that performance improvement a year or so back, a year and a half back, where you started caching the accounts and not running the count every single time, only watching for changes or what. I'm not sure what happens behind the scenes in the caching aspects. But for those clients who had millions of rows of data and being able to present that and reflect that and not pulling\u003C\u002Fp>\u003Cp>Speaker 1: everything Something like that. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: On the counts. That's so The database running every single time you clicked was hugely performance impacting. So\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. There's there's also some databases, and this is, of course, where the devil's in the details here. Some databases also have a difference between, an estimate count versus the actual count. So, like, in Postgres, there's a system table that keeps an estimate count that is not guaranteed to be accurate, but it's sort of a ballpark. So at least, you know, are we over under on 1,000, or is it just, you know, a couple?\u003C\u002Fp>\u003Cp>Yeah. So there's also a world this is again a big sidebar, but such goes in these episodes. There's also a world where we first get the estimate count, which is cheap. And then if that estimate count is less than a couple thousand, then we meet the actual account. So we can show that.\u003C\u002Fp>\u003Cp>Nice. Anyways, that is a whole different\u003C\u002Fp>\u003Cp>Speaker 0: That is a very different ballgame and Oh, okay. Another discussion.\u003C\u002Fp>\u003Cp>Speaker 2: Mhmm. Mhmm. That that's a fun one. Okay. How many items do I have?\u003C\u002Fp>\u003Cp>And you asked the database and it's saying, probably 10,000 maybe? Best I can do.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It's it's an interesting problem though because it's one of those things where in order to know that, it's it's, what is that in COMS size? Is that one of those o n problems or an o two problem? Where you know what I mean. Right?\u003C\u002Fp>\u003Cp>Where it's like, the more items you have, the slower it gets linearly to count the number of items. So, therefore, if you have a table with with 20,000,000 rows in it, how do you know it's 20,000,000 rows efficiently? That's weird. Yeah. Oh, and, yeah, that's that's what I thought.\u003C\u002Fp>\u003Cp>It's it's clear that if not comp side. I'm still just a UX designer, guys. Take it easy. Cool. Some of some of the other stuff in the chat here before I I miss it again.\u003C\u002Fp>\u003Cp>So it says, I wanna speak with a person with a 100,000 items. I need a quick chat.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, for\u003C\u002Fp>\u003Cp>Speaker 1: what it's worth, we, we even do a similar thing ourselves where we have, you know, we keep, error logs for, you know, certain cloud deployments. Those error logs have a one to many back to the project, so we can see the most recent 10 per project. Right? But that also means that the whole error logs table can be in the millions. So that one to many needs to be prepared for, you know, what that looks like if you said select existing, and now all of a sudden you have a list of 10,000,000.\u003C\u002Fp>\u003Cp>Like, how does that work? Right? It it's it's a very real thing that people do, ourselves included, which makes some of these discussions a lot more fun if you're thinking about it many to one. I am making the drop down nice and easy. Nope.\u003C\u002Fp>\u003Cp>Yep. Cool. And then on this another one says here, it feels like we could add a special component that can be defined for group interfaces where they decide on the layout for field layout. Right. Yeah.\u003C\u002Fp>\u003Cp>So this is similar. This is going back to the, how do we do inline options for groups? Right? So honest suggestion here is basically saying, well, maybe just like inner like options for interfaces, there's a way to say, let the actual form extension itself decide how to render on the settings page so we can make that optimized, which is, yep, interesting. Something along those lines.\u003C\u002Fp>\u003Cp>There's something about a half width group. That's fine. Otherwise, do it with 2 columns. I think we touched on the other stuff. Right?\u003C\u002Fp>\u003Cp>Cool. Then the second thing is around in this feature request rather, is around, where does the label go in a form? And is it something that you should be changing? Or is that something that is just not something you should care about? It's an interesting one.\u003C\u002Fp>\u003Cp>Kava is it it gets to a bit of a user experience question. Right? And and this is, again, a topic that can easily get off the reels in terms of theming and customization and how far do we wanna take it in the sense of, you know, if you give the end user infinite flexibility in this kind of stuff, you also give them infinite food guns to make it trash, right, for the for the end user. So there's a there's a very real discussion around, you know, is form labels left aligned input, right aligned? Is that good for good?\u003C\u002Fp>\u003Cp>There be opinions. Is that good for the end user or not? Right? And then the question becomes, who should be in charge of making that call? Because we make a lot of calls because it's a studio.\u003C\u002Fp>\u003Cp>Right? And it's the same, yeah. Yeah. Hans says the same now. It's a layout too.\u003C\u002Fp>\u003Cp>Once you have weird layout problems, you get a similar similar, UX problem. Right? But the question becomes, who's responsible? Right? Do we say well, because we built the studio, we have good defaults.\u003C\u002Fp>\u003Cp>We wanna make sure that everybody has a good user experience. That's what we pride ourselves on. Therefore, are we gonna limit some of these things? Or are we gonna say, go nuts, and it's your problem, which is great for some and a problem for others. Right?\u003C\u002Fp>\u003Cp>And we've seen this before. We've seen this in the past. Like, when we ship theming, which I think has been been pretty pretty good to have, generally speaking. It opens some of those questions too. Right?\u003C\u002Fp>\u003Cp>Because I I know, my very own Kevin made a, like a teenage mutant Ninja Turtles one, or or what do we call it? Something non something not trademark related, as as a sort of experiment to see where how far you can take it, but it's also objectively horrendous. It's objectively bad, right, for for user experience wise. But who who's who's responsible then? Right?\u003C\u002Fp>\u003Cp>And and where do you draw that line? I think it's an interesting question here too because, like, if we make a if we kept just if you make a 12 column grid, you can do some very powerful, easy to use layouts. You can also easily make a garbage nightmare. Just like you can make a dumpster fire. Who's then responsible for the dumpster fire part of that?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Because the yeah. Like you said, this is an actual thing. Like, if somebody badly configures their instance, it may reflect even bad on us because, like, the users come in and say, like, oh, what what what oh, brother. Oh.\u003C\u002Fp>\u003Cp>What what happened here? You know? Like, Directus is ugly. Directus doesn't work. Oh, this is not good.\u003C\u002Fp>\u003Cp>And then it reflects bad on us. So actually, we should think about that we make I mean, you know, flexibility is nice. It is it is cool, and it is one very, very good point for us. But to what extent? Like you said, okay.\u003C\u002Fp>\u003Cp>So we we do have to so\u003C\u002Fp>\u003Cp>Speaker 1: quickly and I love it.\u003C\u002Fp>\u003Cp>Speaker 2: We do have to draw a line somewhere.\u003C\u002Fp>\u003Cp>Speaker 1: There there's an interesting part of the tech industry that people have been yelling about for decades now, which is the whole Android versus iOS thing. Right? Or Linux versus Mac OS thing, which is, like, for the longest time on iOS, you cannot you can you can reorder your app icons, and that's about as much customization as you get. Right? For the first couple of iOS, if the background was black, shut up.\u003C\u002Fp>\u003Cp>Your wallpaper on the lock screen was the was the globe. No options. Right? And then they were like, oh, maybe maybe you can have maybe you can have a wallpaper, and then you could have some fish instead of a globe, and then that was it. Right?\u003C\u002Fp>\u003Cp>But then, you know, people on on the Android side of the house have been screaming for decades. Like, oh, my wallpaper moves this fantastic life great. But, also, you can make an absolute dumpster fire of of your phone, which is whose problem is that? Right? Is that is that you're gonna make it better for the power users, but you also give a bunch of food guns to everybody else?\u003C\u002Fp>\u003Cp>And for what it's worth, Apple is now doing the same thing, I think, right, with this new iOS 18 where you can, like, tint your app callers or something, and it looks horrendously bad just just objectively. Of course, it's an opinion, but I'm right. It's objectively bad. Where because you just see the icon from, like, I don't know, Notion or something turned bright yellow because you you tinted it that way. It's just not good.\u003C\u002Fp>\u003Cp>It's just not work. But the question is, like, you you can make it good with a bit of effort, but people don't will not. Is that is that a problem? Like, is that\u003C\u002Fp>\u003Cp>Speaker 2: It it the older I get, the more I think, yes. That's a problem.\u003C\u002Fp>\u003Cp>Speaker 1: Because the So you're gonna look at an iPhone now, and you're like, man, that is ugly. It's so good and hard to use, and I don't care. But at the same time, the the people that set it up that way for themselves, they might love it. It might be their preferred Yep. You know, becomes an opinion thing.\u003C\u002Fp>\u003Cp>Right? But it's like, how do you decide where to draw a line?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Quick. Because there was the the quote from, I think Henry Ford, was it? Like, with the car, like, you can have, the Ford in any color that you'd like as long as the color is black. Black.\u003C\u002Fp>\u003Cp>Yeah. Right? Yeah. The same for iOS. You can you can do anything you like, but as long as it's not what you like.\u003C\u002Fp>\u003Cp>Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: As long as you like what we like.\u003C\u002Fp>\u003Cp>Speaker 2: Right? Exactly. Yeah. So, like, I I think I personally I mean, of course, this is just, you know, opinion piece. Careful.\u003C\u002Fp>\u003Cp>It's I think we should be for the live stream, and I posted a picture of, Tim did Ios screens. It it is something.\u003C\u002Fp>\u003Cp>Speaker 1: And these are the ones done well, mind you. Anyways yeah. So so for for the listener, it's basically, you know, 5 different iOS screenshots with, like, different color background, and then all of the front, front end, all of the the icon colors, is is the same color as the background. So, therefore, at a glance, everything looks the exact same. It's just that's that's the main issue for me.\u003C\u002Fp>\u003Cp>It's like, I don't know what app is what app anymore because they all look identical to me now. Google oh, man. This is such a sidebar again. But Google did the same thing. Right?\u003C\u002Fp>\u003Cp>When they went through the whole material design thing, now every single Google app looks the exact same. If I log in to our Google Workspace and I click on apps, I just see the exact same icon, like, 200 times, and they are all the same. It's there's a small variation in shape, and I just don't know it. Anyways, yeah, it is let's try to get that back on track. It it is an interesting discussion in general, though, for of, like, how far do you let people take it?\u003C\u002Fp>\u003Cp>And and is it a good thing, or is it a bad thing? And where do you draw the line? I think in this case, as long as we have good defaults, I wanna on the side of let them have the flexibility, but make sure that by default, for the people that don't spend the time setting it up, it looks good and it works well. Right? Mhmm.\u003C\u002Fp>\u003Cp>I think that's kinda where your earlier suggestion comes in for, like, if I just wanna go in at a con at a bunch of fields and not think about it, we need to make sure that things work out the right way at that point. It's like, what makes sense? Help the user as much as possible to have a good default. And then if you wanna go in and be like, well, I want my address field right aligned next to an email and the name field, and then the address needs to be split up between, like, street and and number, and then all this kind of more detailed stuff, they can come back in and start using grids and and nested groups and all that kind of stuff to do it. But by default, you know, we'll we'll make it as good as we can.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. And it's\u003C\u002Fp>\u003Cp>Speaker 1: a very hands off approach. It's like you break it, you fix it.\u003C\u002Fp>\u003Cp>Speaker 0: My my final question before we kinda diverge back, any compliance or standards we need to consider here? WCAG,\u003C\u002Fp>\u003Cp>Speaker 1: accessible I\u003C\u002Fp>\u003Cp>Speaker 2: don't think so right now.\u003C\u002Fp>\u003Cp>Speaker 1: Always. I mean, I assume that the the the, it's funny you mentioned it because, I mean, yes and no. It's it's another question of who's responsible. Like, if if our defaults should always, yes, I think, we should always provide the tooling to make it as accessible as possible and to easy as use as possible. But at the same time, if you wanna go in and remove all of the field labels and align it in a way that turns it into a piece of art, is that allowed?\u003C\u002Fp>\u003Cp>It is. That's part of\u003C\u002Fp>\u003Cp>Speaker 0: that question. I'm more considering I think right now, we do have some issues in the space, and I don't know if it is if it's part of this that would fix that or be part of you know, if we're gonna touch some of this stuff anyway, like, tabbing. Right? Being able to tab through your item fields and, you know, be able to quickly navigate to things. Some of that doesn't work very well right now in the data studio.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Or at all. Right. And so just some, I just, I, because I've been doing so much standard and security.\u003C\u002Fp>\u003Cp>Speaker 1: It it it's a very good point, though. It's it is very true. It's like if you have, you know, a a if if you went nuts, so you have a super rich grid and everything sort of aligns funky, funkily. What is the tab order, and does that make sense? And is that something that somebody needs to be able to configure?\u003C\u002Fp>\u003Cp>Do you tab in and out of groups? Do you tab in and out of individual fields? How do you make sure that the tabbing order is visually consistent with what's about to happen? It's a difficult question for sure. For sure.\u003C\u002Fp>\u003Cp>And it and it goes right back to the who's responsible. Right? Who's responsible for all of this at that point? This this could even very quickly turn political in the sense of, is it the tool builder who's at fault, or is it the tool wielder who's at fault? No.\u003C\u002Fp>\u003Cp>No. No. No. No. No.\u003C\u002Fp>\u003Cp>I think I think some good guardrails today, but, you know, let's,\u003C\u002Fp>\u003Cp>Speaker 0: let me let me try\u003C\u002Fp>\u003Cp>Speaker 1: to summarize this discussion as the only the only thing stopping a bad guy with a form is a good guy with a form. If we get it, who's responsible?\u003C\u002Fp>\u003Cp>Speaker 2: I think I\u003C\u002Fp>\u003Cp>Speaker 0: think good guardrails, with some flexibility inside the rails. Right? And I think it's Yeah. It's always worked out well for us in general. I'm okay with some level of right to to some degree.\u003C\u002Fp>\u003Cp>Right? I think what we're trying to do here is improve the current experience.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: We don't have to give ultimate any flexibility. Right? It is our data. You you wanna do that? Build your own, you know, use the APIs, build whatever you want.\u003C\u002Fp>\u003Cp>Right?\u003C\u002Fp>\u003Cp>Speaker 1: You have you have that\u003C\u002Fp>\u003Cp>Speaker 0: flexibility today. You can build an interface in front of this or a\u003C\u002Fp>\u003Cp>Speaker 1: Sure. You can build your own direct just for like\u003C\u002Fp>\u003Cp>Speaker 2: oh, yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And, you know, to some degree, right, web design and things that people are building, some of the applications and tooling that people will build on top of the directors APIs. They have this flex they have the ultimate flexibility to build whatever they want. You wanna use the data studio. Again, we have a reputation.\u003C\u002Fp>\u003Cp>We have our own. And and I think good guardrails, good guidelines, and, you know, just configuration options. I think there's some of that as just adding some additional configuration parameters to allow for you know, we do wanna follow good accessibility practice. In order to do that, there has to be some options for that. And we can put some default, or we can just say these are the defaults.\u003C\u002Fp>\u003Cp>Right? The the defaults are what they are. Just making it so that it actually works and meets the guidelines at a minimum. I like it. Cool.\u003C\u002Fp>\u003Cp>Alright. We've got, 8 minutes, 7 minutes. How do we wanna start wrapping up, gents?\u003C\u002Fp>\u003Cp>Speaker 1: I think I\u003C\u002Fp>\u003Cp>Speaker 2: mean, it's just okay. Go for it. Go for it.\u003C\u002Fp>\u003Cp>Speaker 1: I was gonna say circling back to the initial thing of is a grid a system thing or is it more of a group type setup? I think I'm kinda leaning towards even though it is a bit more setup, doing it the group style way. Because that way, you can start nesting things. You can start configuring it to your heart's content. You can mix and match.\u003C\u002Fp>\u003Cp>You can have a section that is a 4. You can have a section that's a 6 upwards, 2, or 1, and mix and match on the page. I think making that a sort of nestable thing feels like the right move. We can still say that the form itself by default is one of those units, right, for ease of setup. So if you just want the whole field the whole form to be a 2 up, then you're done by default.\u003C\u002Fp>\u003Cp>Absolutely. But making that a sort of thing you can nest as a section feels like a powerful thing to have. Because that's what's gonna allow for one of these things where you have a 4 up, but then in the 4th column, you have a thing that also has field side by side, right, to make that as flexible as you want. I think when it comes to form label, that feels like a very system y type of thing. Like, we render a form with or without those groups with with grids, and then maybe on the form level, maybe on the the grid group form, whatever you wanna call it level, or maybe both, you can just set, okay, left line top of line, label and we can be a bit more flexible in that.\u003C\u002Fp>\u003Cp>And the same goes for the description. Right? Show at the bottom, show at the top. Those could be be just a toggle for for the form in in general. The new styles for check boxes, those types of things, the the the making them into 1 drop down and actual drop down.\u003C\u002Fp>\u003Cp>I think that is interface options. That's just separate from from this discussion to me. And then last but not least, there's 2 things coming in from our favorite team, which is our own. Sounds like we're in the last 8 minutes. Should different interface styles be just styles in existing interfaces or new extensions or interfaces?\u003C\u002Fp>\u003Cp>Opinions. Opinions. Opinions. I think it has all to do with the user experience. So to me, a checkbox and a toggle are effectively the same thing from a user experience perspective because you clicked enable, you clicked to disable.\u003C\u002Fp>\u003Cp>They're the same. Right? So to me, a toggle is just a different visual representation of the same underlying thing. This is the pain. Don't hate me too hard.\u003C\u002Fp>\u003Cp>It's a it's a different visual style for the same thing. When it comes to and and, funnily enough, we are not always good about doing it this way either. Right? So, for example, we have a drop down single select and a drop down multi select. In our components in code, it's the same component.\u003C\u002Fp>\u003Cp>A bit hard to maintain, arguably. Shouldn't have done that. We'll probably refect it at some point. Mhmm. Because the user experience is different.\u003C\u002Fp>\u003Cp>Right? Because one is, like, you're you're rendering a list of things. You're checking off multiple. You render it. Now when it comes to a multi select as a list of checkboxes versus a drop down that then shows the list of checkboxes, that is again a different visual style for the same interaction.\u003C\u002Fp>\u003Cp>Right? But where do you draw the line? Because in that example, you could say, well, they're visually so incredibly different. And for the end user, they might expect them to be do 2 different things. So where do you draw that line?\u003C\u002Fp>\u003Cp>I think it's just an opinion in a case by case. Right? It's like in this case, I would say a list of checkboxes or a drop down where you can select multiple things. Those are so different from, like, a mindset perspective that we should make it 2 different interfaces even though they do the exact same thing. You choose you pick and choose 1 or more things from a list, and that's what you're doing.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Especially if you have 2 different styles, like you said, right, with with the drop down or they could have different options, and then we would have to respect those also in the in the drawer, for example, depending, you know, on what you have. But this has to be configurable then also for extensions. And that also sounds like okay. Just make a new interface.\u003C\u002Fp>\u003Cp>It everything is in order. It is just like that. Deal with it.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Go with it.\u003C\u002Fp>\u003Cp>Speaker 2: It sounds very\u003C\u002Fp>\u003Cp>Speaker 1: There's there's 2 more fun ones for the last 2 minutes here. Tim asked, make it difficult nearing the end, will it be able to configure conditions across groups and sections? I think the answer is yes. I think conditions for an individual field or group should always be able to listen to anything else on the form, just whatever nesting level you're in. Of course, there's the same responsibility things.\u003C\u002Fp>\u003Cp>Like, if you listen to something that is visually completely disconnected and related, you can make it very difficult to use for your end users. But from a technical angle, I think we should make it possible. And then the last question here now is saying, what about applying conditions on hiding or disabling fields in the relation drawer based on the state of the form? And for that, I'd like to say tune back in 2 weeks from now. That that is a bit of a different discussion around how that drawer works.\u003C\u002Fp>\u003Cp>The the relation drawer right now, you know, it renders the same layout that you use for that collection, in the main sort of layout for that collection. So, therefore, the fields that show up are are the same that you see on the layout page for that collection. It also means it's a bit tricky to dynamically change that because it is matching the other one, but that is a different moving level. It should be possible with custom CSS and a group extension. Yep.\u003C\u002Fp>\u003Cp>Yep. Yep. Yep.\u003C\u002Fp>\u003Cp>Speaker 0: So many ideas.\u003C\u002Fp>\u003Cp>Speaker 1: Anyways, with that, we're at the top of the hour. I wanna thank you all very much for tuning in once again. We'll be back at some point in the near future.\u003C\u002Fp>\u003Cp>Speaker 0: I think\u003C\u002Fp>\u003Cp>Speaker 1: we're in another one in 2 weeks from now. Make sure to find this on direct to zio slash tv. Any other closing thoughts? Dan, John?\u003C\u002Fp>\u003Cp>Speaker 2: I figured it out.\u003C\u002Fp>\u003Cp>Speaker 0: Thanks to the community and all the good feedback on these on these issues. I'll post comments here shortly and then, Greg, if we need to do a full RFC on this one, we can work it out.\u003C\u002Fp>\u003Cp>Speaker 1: Sounds good.\u003C\u002Fp>\u003Cp>Speaker 0: Alright, team. Have a wonderful rest of your day.\u003C\u002Fp>","We are covering additional form and field layout options. This is actually related to our item editing experience, and it is something that I would actually love to have. We're very we have some awesome capabilities right now, but the 2 field across kinda limitation, it would be nice to have some more flexibility there. So that's our discussion. This is discussion number 9161. I'll drop it in the chat in case anyone wants to follow along there. Oh, 4 digits. You know, it's an old one when you're in the 4 digits. Oh, yeah. This is an oldie. An oldie but a goodie. This was an October 26, 2021 Ben Haines special. Oh, damn. But it actually surprisingly, this was pre RFC, but Ben actually put in example screenshots, good description. It wasn't just a one liner. I I was actually very pleasantly surprised when I opened this up to review it. Yeah. It's it's a good day when the feature request is not a one liner. It's Yes. It's great. Yeah. So I will let you guys kick it off. Alright. Well, 4 pages. Yeah. Right. So if I understand this correctly, we are talking about having a different layout for editing entries, which we currently do in 2 columns. You can divide your editing experience into a left column, right column, or go through with the whole input field? And wouldn't it be nice if we could do something else? I guess, it's the question. What could we have? So yeah. That's the the general question is a little bit more flexibility, more feet you know, again, things like toggles. Right? Don't need even half width. They could be 25%. 3rd. You know, maybe maybe a percentage based. Some of the more the more interesting feature styles, I think, that we can do today or that are possible. So Ben's got some different examples of what you might wanna have for layout options. Although this this was actually noted a little bit further down in this request that putting the label to the side is actually negatively impacting to users' experience. It's not not a recommended practice in general, but having some flexibility and being able to do some things. I think more so just being able to add more more configuration in the layout style, and and how things get positioned It's the general my my general client feedback, community feedback that I've heard, you know, in general with meetings and talking to people is just the ability to have more flexibility in the item editing design experience. So quick question. Yeah. I was gonna I I I think you're gonna go the same way as I'm gonna go, Daniel, but I'll let you go first. Not sure. I'm I'm just, I just want to clarify a little bit because, because of the screenshot. So currently, I think we're only talking or a question. Are we only talking about the layout in and of itself? So just positioning of elements beside each other, or are we also talking about different visual styles for the same interfaces? Because, when you scroll down a little bit, for the checkboxes, for example, they look different than what we have currently. Is also part of this, idea that, for example, you can have multiple different, toggles? Let's say the this one this let's call it filled in toggle or a different toggle. What about the, like like, what is it checkboxes? Now the selects where you can only select 1, like radio buttons for example cause they are a couple of examples here right the product category for example. Because they look different. Like, are we currently just talking about the layout or are we also talking about different styles? Let's look. Okay. Oh, no. He's got interface options to add well. So he's adding additional interface optioning capabilities as well. I thought, initially, I missed that. I thought it was just the item view formatting, you know, tab styles, other kinds of capabilities potentially. It's another frequent one that we see is instead of having just a full list of fields. Right? We have detailed things currently, but like a tab style interface there as a presentation layer. Could be other things we can consider. We can decide what we want this to be. We can narrow this. We can put this into RFC spec format, when we're done. So I'm happy to, well, you know, we'll do our let's do explode and then figure out what we want this to actually cover when we're done. So primary things that clients currently right? This is your current capability. Let me grab one that's got some, do I have one with groups? Remember if I've got something with field groups in it. Top of my, those products haven't. Why? You guys I think in in the end, when we when we scope down again, in the end, we can probably, like, make a make a rough, MVP of this feature where we just say, okay, maybe this is just it. But if you wanna take it further, then it would also include different, visuals for the same interface. Yeah. So these are kind of your current options. Right? You can have field groupings. Right? And otherwise, you got it didn't half with. So to me yeah. I think we should start with with let's let's think about the I think I mean, options to existing interfaces could be discussed here. But I think in general, the way that this thing started and the way that I see this, the the biggest struggle that clients have today is you have very limited kinds of options actual form editing experience. How things can be laid out. Yeah. For sure. I think the other parts, you know, a toggle instead of a checkbox, that's a new interface. That's just a custom interface or an option or stuff like that. But it doesn't really change the way the form itself is rendered. So I think for the sake of this discussion, we can focus on that. Cool. I think some of the bigger ones that we see here in the feature request is around where does the field label and description go. I think that is sort of one bucket. Does it go top? Does it go bottom? Does it go left? Does it go right? Then around how do certain groups come together? So there's like a sort of a new group UI, like a group of the title, and it's in a gray box instead of on a white background sort of visually, you know, cluster some of these form inputs, which then made some where the descriptions go. And then the other one is around, and it's not so much in the original screenshots, funnily enough, but it is I think of an important one here to bring up is what does the grit look like if you go beyond 2? Right? And and, where I can see this discussion going quickly is what about responsive? How does that work? And what about, on the screen that is about 6 meters wide, to make that work in a way that is still user friendly? That is a good question. I this reminds me of a small little bug if we want to just take a little tangent for a story time. Because we have this little bug, where if you have a checkbox with multiple, options, we change the way how they are displayed depending on how many options you have. So if you only have 2, then they are right beside each other. But, for example, or if you have, like, I don't know, 5 or let's say 10, then we put them below each other. And we had this interesting fun little little bug is that we made this also dependent on the content of those checkboxes. So if you have long text, then we actually, you know, span the whole width so you can actually read the thing inside of the checkbox. But what this resulted in is that actually, in like languages which are very short and there are languages that which are very long, like in German words, you know, which means that you have different layouts depending on what kind of language you have. And then, there were a couple users that used Chinese characters, Mandarin characters. And this then resulted, like, if you translate it and then you got different layouts for different languages and stuff, that that was quite quite a fun thing. I think I fixed that, by the way. But, okay. Enough of that. Sorry. Yeah. Let's see. I mean, there's there's lots of perspectives. Right? Like, how many columns should that be dynamic? Do we want to provide a fixed fixed width as of right now? Like, 2. Let's just say, okay. You get 12. Is that good enough? And how do you configure the responsiveness? Then how do we have breakpoints? How many breakpoints do we have? 2, mobile, desktop, but not every screen is the same. How do you configure that? Do do we even want to configure that? Like, yes, but no. Meh. I mean, really, how much database work and actual data modeling are you interested in doing on mobile, to be honest? Like, how many people actually, editing the databases and data records on the phone? Like, how important is he's he's funny. The answer is very important. This guy does. You'd you'd be surprised by how many people use mobile or mobile adjacent stuff now too. Right? If you have the folks with flip phones and you have people with with, you know, small iPads or, yeah. Yeah. Just going by the chat. People are saying the same thing too. Mobile interface, main point, which isn't direct is people editing videos on their phones. The people do everything on their phones nowadays, and I'm one of them too for what it's worth. So when it comes to grit and responsive, this is this is one of the reasons why, there's a lot of apps that are sort of, like, app builders, like a read tool, for example, that opt to make it a scrollable window when you go smaller for this exact reason so it doesn't have to do any sort of weird new reflowing. There is tools out there that I won't name by name that just straight up say there's no native there's no responsive design at all. There's no mobile version. There is different ways of of handling that. The way Directus does it right now is in the in the sort of 2 grid in the 2 up, the screen gets too small, becomes a one up. And that an easy easy enough sort of reflow to make. Right? Because whatever used to be side by side is now beneath each other, and that is good enough. Right? It it's it's what you'd sort of expect to happen, and it doesn't really cause anything too strange, from happening. If you're in a 6 column grid or so, like, if I'm scrolling down this feature request a little bit more, there were some screenshots from a tool called They have, like, a a sort of rich layout, which is roughly 4 columns. But then within, you know, one of the columns, there's another sort of 6 column y thing nested. And now I'm like, okay. Well, if you're responsibly squishing that down, weird things will happen. Like, what goes where? How does it flow? How does it grow? Do you expect, like, the 4th column to become, 3 columns of white when it goes to the next one? Do you intend it to still be blocked, but then you have 3 and 1 sort of weirdly underneath it? Or does it switch forward to a 2 grid? There's there's weird questions that happen at that point when you go down. And all so how does it interact with other fields that are then further down the page? Right? So right now, because every field is basically just in the same, same list of fields, It would start showing up side by side with other fields in that same grid, which could get funky and sort of unintentional. So one way around that potentially is to instead of doing a grid, let me do it now where it is based always one column, but there's a group type for creating grid sections in your form. Right? Because that way, it will always wrap within just that group. You don't your unintended interactions with other fields further down the page. And then that also means that you can start nesting those in whatever way you want. So if you're trying to re replicate that sort of 4 knocks set up where you have 4 columns, where the 4th column itself is like a a 6 column grid or something, you could because you can start nesting those those grid forms. Right? And it it kinda starts responsively matching the column size rather than the form size. Hannes in the chat says, additional groups feel cumbersome to set up. Yeah. Fair. Although at the same time, you know, to achieve the same thing that we have now, you could have one group and just put everything in it. Doesn't necessarily, you know is that is that what the price of admission get for, flexibility in the new layouts? And then on that same note, you know, the the some of the other layout settings, like, is the field name on the left or on the top, the description on the top or on the bottom, that kind of stuff could be settings of a group. Like, it doesn't have to be, settings of the whole form at once. Right? We could also say that the form, by definition, just has one root group, and therefore, it's always there, but it uses the same same system. Alright. I can see that work in my hand. Like, is there a leaner way with less clicking? Currently trying to, like because if for me for me personally, the I I want I would want it to be as least intrusive as it can be because because I just wanna set up my thing and I wanna keep going. Right? Like I do not want to spend my time with designing, you know, layout. Like like this is like, I'm not sure how to say it. Like I I want to model stuff, I want to make like this this is how it how it works now. Like I just want to model stuff left, right, things and move on, you know. And I have other stuff to do. I have problems to solve as a administrator, let's say, of an instance, and I just wanna put some stuff in and keep going. So if you then have to jump through hoops and and, you know, like with all the, oh, but then on mobile, because I didn't, click on the full width. Now on mobile it is only half and it looks bad. And then I have to click this again and then the drawer opens again and I have to go into specific settings of a thing and then close the drawer again and choose the other. And that's that that feels icky. Like, I'm not sure if if we could design it in a way maybe with just small little toggles, like, without, specific options for those interfaces, when, like, the drawer opens, maybe we can make them just, small little toggles that you quickly go through just just without leaving the modeling page. Maybe maybe I don't know, like, it did did that make sense? It does. It it's a, you know, it's a tricky thing because on the one hand, it's like if you wanna do it sort of quick and dirty, so to speak, then that's fine. That should be possible. At the same time, if you wanna make the best user experience for your end user so so let me back up a little bit. I think there's a difference in, flow when you're first setting it up and you're doing the data modeling versus when your data modeling is done and sort of finalized and you go into the details to make it a nicer user experience for your end user. At which point, you might set up, you know, I wanna use monospace for an input and add a little icon to make it, you know, friendlier for the end user, that kind of stuff. Right? I think that is also where this whole gridding stuff comes in. It's like, do you care about grids and how it responsibly shows up side by side instead of ordered top to bottom? Well, if you're doing the data modeling, possibly, probably not. It's just like you need to have the fields to check if it works, build the integration, move on. Right? Only then when your data modeling piece is done, can you circle back and be like, okay. Now I'm gonna spend an hour or so to just really make this form the best form it can possibly be for the use case that I have. I wanna conditionally show and hide some things. I wanna do it in certain sections have to be like a 4 up grid or a half half width group where, I wanna order it differently, etcetera, etcetera. Right? So I think there's 2 different points in time and 2 different, user flows that that are related. So to your point, I do agree. It's like for that first one, I mean, you're just setting up the data modeling, experimenting with what fields and what names and everything else. Quick and dirty. You don't wanna be slowed down by having to think about, is it 2 or 4 or 6 columns? Like, who cares? Just go quick. Right? Make it work. But I do think that is where we can introduce that additional flexibility that we're talking about as part of this this feature request to saying, well, if we hard code this form to be 4 columns instead of 2, we're gonna have the same discussion a month from now where it's like, okay. What about 6? Right? Yeah. And we're gonna have the same discussion a month from now where it's like, okay. Somebody wants 12. So by doing it as a group extension type where we say, okay. The group is just the thing that makes it x number of columns and we don't care. We allow that flexibility in a sort of opt in type of fashion rather than us having to predefine, okay, a direct this form is always x. Like, it is now. We say it's always to deal with it. Right? Yeah. For for me, priority would be, just looking at at the screen right now, let's see, let's look at the theming group, right, or the SEO group also. I would like to avoid, or it would be it is near and dear to my heart, you know. Like, if I now want to split the theming group, I don't, like, I don't want to be the kind of back end where you have to click on the, 3 dots, then a dialogue or, like, a popover opens, then you have to click on edit fields, then the drawer opens, then you have to go inside of that, and then you have to configure some type of grid system and break points and stuff and then the whole thing closes and then, oops, I forgot one thing, I have to go through the entire thing again like this. Like I really want to avoid that. So I'm like, okay, And we maybe make sure that this doesn't happen. Maybe we can include some controls that are maybe enough already on the screen right now. So like, side the three points where the options would be, maybe there could be quick toggles where you can very quickly just with one click, like, boop, and then it's half view. And, you can configure it and then another click, boop, it's it's it's the mobile view. And then you can configure it again. Yeah. How about that? So the reason why it has to be a drawer now is that those groups and interfaces themselves are an extension type. So, therefore, we don't know what those options are. Right? So if somebody makes a new custom grid group now, which you could theoretically, the options for that group is like a an open ended question mark. It's just a set of fields, and we don't know what they are. And we don't know how to, you know we we know how to render them because we ask you we ask the extensions, like, what options do you wanna render? And it's basically just another view component. Right? So it's like a black we just anything goes into that drawer and have have at it. So we can do that. We'll have to come up with some sort of standardized syntax for those plugins to say, here is the 2 or 3 things I wanna be able to edit in line that makes sense for this type of thing that I'm doing. Or we have to go the other way and say, well, we just built more of an Apple approach. That's who it where we say, well, the direct extensions are special and they get to do some of these inline things, but regular extensions are back to the old drawer style. You know what I mean? It's just that that flexibility angle because all of these are extensions. Yeah. I get it. That's a bummer. Like, because we we strive and we always, our strength, right, is that everything is so super duper flexible that you can do anything yourself. Yeah. But for this, can we? It's a question. Right? I I think when it comes to the drag and drop UI of of doing the layout of the form, if if you have a 4 grid, we have to show it as a 4 grid in line there as well in your editing experience. Like, that, I think, is is definitely something that we have to code in. But then if there's a group that has an option for number of grids, how does that how do we connect the dots? Right? How do we know that the grid column option in or an extension should be mapped to our editing experience for the form that makes it so that makes it work like that. Or again, maybe that there's the case that we say, well, you know, the way to handle grids is just not an extension type and there's no way for extensions to do that. It's just the native direct listing. Where we stay, okay, form layout, order, and grid, that is just a a direct as native thing. You can change it through extensions or anything else. You can still make a custom group, but the layouting options for that is not something that we can natively, manage on your behalf. Like, that that is also, you know, an option. Mhmm. Yeah. Like, I I would be like, let let's let's just, you know, the, gut gut feeling is just this sounds reasonable because it's not actually concerning your data. It's just like our our, product UI. So it makes sense that we have this control of the layout. But I can already see, like, it's this is gonna be the first comment in that PR. It's like, oh, but I really would like to do this in my thing. Can we can we add that also? And then every time. Every content. Okay. Alright. So so this is the layout type of thing of that PR of that of that RC. I think it just okay. Let's you said in the beginning the other interfaces are mostly just other interfaces, so they aren't really concerned with this. I think the difference between a checkbox and a toggle, that is an interface option or that is a new interface. I don't think that has anything to do necessarily with form rendering. So I think for the sake of this art for this discussion, we can just keep the scope to form rendering itself. I saw there there was some a couple of good points in the chat earlier right before we started with a couple of points around a little bit more general updates to forms and and interfaces, what we can do with it. 20 minutes ago, somebody was saying, it will be cool if you have, like, a 3, 4 column layout instead of just 2, expand the form width. So it's like the whole screen instead of just, you know, a a max width. But you could kinda do because there's, like, a full width option for an individual in the face or group, but it's I I do agree it's the whole form. It doesn't quite quite work like that. Aligning the form itself to the left or the center of the screen or maybe even the right. And then it gets into which I feel like a bit of a different feature request, but I wanna call it out because it's in all caps, so it sounds important. Actual relation drop downs, which I think is just make making sure that a second drop down is updated based on the value of the first drop down, which is, again, kind of not really formed. I think it's actually I think it's actually more related to the relational interface itself right now. They always go to a drawer. I think it's more of the so like, a many to 1 or one to many. Oh, that part of that nature drop down. As opposed to a drawer pop. Yeah. That's an interface itself. That to me is again, I think we're kind of a little out of scope for this for what I think we want this review to cover in detail. Okay. Yes. There are there are dozens of things we would all like to have on the interfaces side of the house, but that's why custom extensions exist and you can't build them. So although, you know, this grouping style that we support, I have seen tab style, the group interfaces where someone has built a custom interface that actually does, like, tab styling and multiple fields and tabs and things inside their groups. So custom extensions support those kinds of things. Yes. Relational drop downs instead of drawers would be really nice in many use cases. You know, it's, you know, it's funny. This is a complete sidebar, and I I wanna be cognizant that we don't end up spending 20 minutes on this because it's an easy one to slip into. But the many to 1 interface, the drop down one that opens drawer, it has the the actual drop down bits built in. It like, when we built it, it had that baked in. It's the problem was that similarly with most every other thing in direct is, like, week 2, somebody was like, I have a 100,000 items in the related Yes. Related table and I I am now going to break your interface. Outright. The drop down only shows the first a 100, so I cannot find the thing to select. And I'm like, yep. Yep. Yep. Yep. Yep. Yep. So then then we made the call to, like, always show it as a drawer, which works for everything, but it's it's not as nice as a little drop down. But it it used to work like that at one point in time. Yeah. Yep. Yep. I know we've had bring that back as a as an option to to but but the the funny thing there is, like, to do it the best way possible, we need to have an updated count of the number of related items at any point, and getting that count can be very slow if you have 100 of 1,000 of them. So we have to come up with some sort of way to cache the count our site first so we have a sort of estimate count that we can use and then update that over time because, otherwise, it's gonna be not performing enough to do you know? Anyways, long story short Yes. Similar thing that so we did that for the actual layouts, as part of that performance improvement a year or so back, a year and a half back, where you started caching the accounts and not running the count every single time, only watching for changes or what. I'm not sure what happens behind the scenes in the caching aspects. But for those clients who had millions of rows of data and being able to present that and reflect that and not pulling everything Something like that. Yeah. On the counts. That's so The database running every single time you clicked was hugely performance impacting. So Yeah. There's there's also some databases, and this is, of course, where the devil's in the details here. Some databases also have a difference between, an estimate count versus the actual count. So, like, in Postgres, there's a system table that keeps an estimate count that is not guaranteed to be accurate, but it's sort of a ballpark. So at least, you know, are we over under on 1,000, or is it just, you know, a couple? Yeah. So there's also a world this is again a big sidebar, but such goes in these episodes. There's also a world where we first get the estimate count, which is cheap. And then if that estimate count is less than a couple thousand, then we meet the actual account. So we can show that. Nice. Anyways, that is a whole different That is a very different ballgame and Oh, okay. Another discussion. Mhmm. Mhmm. That that's a fun one. Okay. How many items do I have? And you asked the database and it's saying, probably 10,000 maybe? Best I can do. Yeah. It's it's an interesting problem though because it's one of those things where in order to know that, it's it's, what is that in COMS size? Is that one of those o n problems or an o two problem? Where you know what I mean. Right? Where it's like, the more items you have, the slower it gets linearly to count the number of items. So, therefore, if you have a table with with 20,000,000 rows in it, how do you know it's 20,000,000 rows efficiently? That's weird. Yeah. Oh, and, yeah, that's that's what I thought. It's it's clear that if not comp side. I'm still just a UX designer, guys. Take it easy. Cool. Some of some of the other stuff in the chat here before I I miss it again. So it says, I wanna speak with a person with a 100,000 items. I need a quick chat. Oh, for what it's worth, we, we even do a similar thing ourselves where we have, you know, we keep, error logs for, you know, certain cloud deployments. Those error logs have a one to many back to the project, so we can see the most recent 10 per project. Right? But that also means that the whole error logs table can be in the millions. So that one to many needs to be prepared for, you know, what that looks like if you said select existing, and now all of a sudden you have a list of 10,000,000. Like, how does that work? Right? It it's it's a very real thing that people do, ourselves included, which makes some of these discussions a lot more fun if you're thinking about it many to one. I am making the drop down nice and easy. Nope. Yep. Cool. And then on this another one says here, it feels like we could add a special component that can be defined for group interfaces where they decide on the layout for field layout. Right. Yeah. So this is similar. This is going back to the, how do we do inline options for groups? Right? So honest suggestion here is basically saying, well, maybe just like inner like options for interfaces, there's a way to say, let the actual form extension itself decide how to render on the settings page so we can make that optimized, which is, yep, interesting. Something along those lines. There's something about a half width group. That's fine. Otherwise, do it with 2 columns. I think we touched on the other stuff. Right? Cool. Then the second thing is around in this feature request rather, is around, where does the label go in a form? And is it something that you should be changing? Or is that something that is just not something you should care about? It's an interesting one. Kava is it it gets to a bit of a user experience question. Right? And and this is, again, a topic that can easily get off the reels in terms of theming and customization and how far do we wanna take it in the sense of, you know, if you give the end user infinite flexibility in this kind of stuff, you also give them infinite food guns to make it trash, right, for the for the end user. So there's a there's a very real discussion around, you know, is form labels left aligned input, right aligned? Is that good for good? There be opinions. Is that good for the end user or not? Right? And then the question becomes, who should be in charge of making that call? Because we make a lot of calls because it's a studio. Right? And it's the same, yeah. Yeah. Hans says the same now. It's a layout too. Once you have weird layout problems, you get a similar similar, UX problem. Right? But the question becomes, who's responsible? Right? Do we say well, because we built the studio, we have good defaults. We wanna make sure that everybody has a good user experience. That's what we pride ourselves on. Therefore, are we gonna limit some of these things? Or are we gonna say, go nuts, and it's your problem, which is great for some and a problem for others. Right? And we've seen this before. We've seen this in the past. Like, when we ship theming, which I think has been been pretty pretty good to have, generally speaking. It opens some of those questions too. Right? Because I I know, my very own Kevin made a, like a teenage mutant Ninja Turtles one, or or what do we call it? Something non something not trademark related, as as a sort of experiment to see where how far you can take it, but it's also objectively horrendous. It's objectively bad, right, for for user experience wise. But who who's who's responsible then? Right? And and where do you draw that line? I think it's an interesting question here too because, like, if we make a if we kept just if you make a 12 column grid, you can do some very powerful, easy to use layouts. You can also easily make a garbage nightmare. Just like you can make a dumpster fire. Who's then responsible for the dumpster fire part of that? Yeah. Because the yeah. Like you said, this is an actual thing. Like, if somebody badly configures their instance, it may reflect even bad on us because, like, the users come in and say, like, oh, what what what oh, brother. Oh. What what happened here? You know? Like, Directus is ugly. Directus doesn't work. Oh, this is not good. And then it reflects bad on us. So actually, we should think about that we make I mean, you know, flexibility is nice. It is it is cool, and it is one very, very good point for us. But to what extent? Like you said, okay. So we we do have to so quickly and I love it. We do have to draw a line somewhere. There there's an interesting part of the tech industry that people have been yelling about for decades now, which is the whole Android versus iOS thing. Right? Or Linux versus Mac OS thing, which is, like, for the longest time on iOS, you cannot you can you can reorder your app icons, and that's about as much customization as you get. Right? For the first couple of iOS, if the background was black, shut up. Your wallpaper on the lock screen was the was the globe. No options. Right? And then they were like, oh, maybe maybe you can have maybe you can have a wallpaper, and then you could have some fish instead of a globe, and then that was it. Right? But then, you know, people on on the Android side of the house have been screaming for decades. Like, oh, my wallpaper moves this fantastic life great. But, also, you can make an absolute dumpster fire of of your phone, which is whose problem is that? Right? Is that is that you're gonna make it better for the power users, but you also give a bunch of food guns to everybody else? And for what it's worth, Apple is now doing the same thing, I think, right, with this new iOS 18 where you can, like, tint your app callers or something, and it looks horrendously bad just just objectively. Of course, it's an opinion, but I'm right. It's objectively bad. Where because you just see the icon from, like, I don't know, Notion or something turned bright yellow because you you tinted it that way. It's just not good. It's just not work. But the question is, like, you you can make it good with a bit of effort, but people don't will not. Is that is that a problem? Like, is that It it the older I get, the more I think, yes. That's a problem. Because the So you're gonna look at an iPhone now, and you're like, man, that is ugly. It's so good and hard to use, and I don't care. But at the same time, the the people that set it up that way for themselves, they might love it. It might be their preferred Yep. You know, becomes an opinion thing. Right? But it's like, how do you decide where to draw a line? Yeah. Quick. Because there was the the quote from, I think Henry Ford, was it? Like, with the car, like, you can have, the Ford in any color that you'd like as long as the color is black. Black. Yeah. Right? Yeah. The same for iOS. You can you can do anything you like, but as long as it's not what you like. Yeah. As long as you like what we like. Right? Exactly. Yeah. So, like, I I think I personally I mean, of course, this is just, you know, opinion piece. Careful. It's I think we should be for the live stream, and I posted a picture of, Tim did Ios screens. It it is something. And these are the ones done well, mind you. Anyways yeah. So so for for the listener, it's basically, you know, 5 different iOS screenshots with, like, different color background, and then all of the front, front end, all of the the icon colors, is is the same color as the background. So, therefore, at a glance, everything looks the exact same. It's just that's that's the main issue for me. It's like, I don't know what app is what app anymore because they all look identical to me now. Google oh, man. This is such a sidebar again. But Google did the same thing. Right? When they went through the whole material design thing, now every single Google app looks the exact same. If I log in to our Google Workspace and I click on apps, I just see the exact same icon, like, 200 times, and they are all the same. It's there's a small variation in shape, and I just don't know it. Anyways, yeah, it is let's try to get that back on track. It it is an interesting discussion in general, though, for of, like, how far do you let people take it? And and is it a good thing, or is it a bad thing? And where do you draw the line? I think in this case, as long as we have good defaults, I wanna on the side of let them have the flexibility, but make sure that by default, for the people that don't spend the time setting it up, it looks good and it works well. Right? Mhmm. I think that's kinda where your earlier suggestion comes in for, like, if I just wanna go in at a con at a bunch of fields and not think about it, we need to make sure that things work out the right way at that point. It's like, what makes sense? Help the user as much as possible to have a good default. And then if you wanna go in and be like, well, I want my address field right aligned next to an email and the name field, and then the address needs to be split up between, like, street and and number, and then all this kind of more detailed stuff, they can come back in and start using grids and and nested groups and all that kind of stuff to do it. But by default, you know, we'll we'll make it as good as we can. Yep. And it's a very hands off approach. It's like you break it, you fix it. My my final question before we kinda diverge back, any compliance or standards we need to consider here? WCAG, accessible I don't think so right now. Always. I mean, I assume that the the the, it's funny you mentioned it because, I mean, yes and no. It's it's another question of who's responsible. Like, if if our defaults should always, yes, I think, we should always provide the tooling to make it as accessible as possible and to easy as use as possible. But at the same time, if you wanna go in and remove all of the field labels and align it in a way that turns it into a piece of art, is that allowed? It is. That's part of that question. I'm more considering I think right now, we do have some issues in the space, and I don't know if it is if it's part of this that would fix that or be part of you know, if we're gonna touch some of this stuff anyway, like, tabbing. Right? Being able to tab through your item fields and, you know, be able to quickly navigate to things. Some of that doesn't work very well right now in the data studio. Yeah. Yeah. Or at all. Right. And so just some, I just, I, because I've been doing so much standard and security. It it it's a very good point, though. It's it is very true. It's like if you have, you know, a a if if you went nuts, so you have a super rich grid and everything sort of aligns funky, funkily. What is the tab order, and does that make sense? And is that something that somebody needs to be able to configure? Do you tab in and out of groups? Do you tab in and out of individual fields? How do you make sure that the tabbing order is visually consistent with what's about to happen? It's a difficult question for sure. For sure. And it and it goes right back to the who's responsible. Right? Who's responsible for all of this at that point? This this could even very quickly turn political in the sense of, is it the tool builder who's at fault, or is it the tool wielder who's at fault? No. No. No. No. No. No. I think I think some good guardrails today, but, you know, let's, let me let me try to summarize this discussion as the only the only thing stopping a bad guy with a form is a good guy with a form. If we get it, who's responsible? I think I think good guardrails, with some flexibility inside the rails. Right? And I think it's Yeah. It's always worked out well for us in general. I'm okay with some level of right to to some degree. Right? I think what we're trying to do here is improve the current experience. Yeah. We don't have to give ultimate any flexibility. Right? It is our data. You you wanna do that? Build your own, you know, use the APIs, build whatever you want. Right? You have you have that flexibility today. You can build an interface in front of this or a Sure. You can build your own direct just for like oh, yeah. Yeah. Yeah. And, you know, to some degree, right, web design and things that people are building, some of the applications and tooling that people will build on top of the directors APIs. They have this flex they have the ultimate flexibility to build whatever they want. You wanna use the data studio. Again, we have a reputation. We have our own. And and I think good guardrails, good guidelines, and, you know, just configuration options. I think there's some of that as just adding some additional configuration parameters to allow for you know, we do wanna follow good accessibility practice. In order to do that, there has to be some options for that. And we can put some default, or we can just say these are the defaults. Right? The the defaults are what they are. Just making it so that it actually works and meets the guidelines at a minimum. I like it. Cool. Alright. We've got, 8 minutes, 7 minutes. How do we wanna start wrapping up, gents? I think I mean, it's just okay. Go for it. Go for it. I was gonna say circling back to the initial thing of is a grid a system thing or is it more of a group type setup? I think I'm kinda leaning towards even though it is a bit more setup, doing it the group style way. Because that way, you can start nesting things. You can start configuring it to your heart's content. You can mix and match. You can have a section that is a 4. You can have a section that's a 6 upwards, 2, or 1, and mix and match on the page. I think making that a sort of nestable thing feels like the right move. We can still say that the form itself by default is one of those units, right, for ease of setup. So if you just want the whole field the whole form to be a 2 up, then you're done by default. Absolutely. But making that a sort of thing you can nest as a section feels like a powerful thing to have. Because that's what's gonna allow for one of these things where you have a 4 up, but then in the 4th column, you have a thing that also has field side by side, right, to make that as flexible as you want. I think when it comes to form label, that feels like a very system y type of thing. Like, we render a form with or without those groups with with grids, and then maybe on the form level, maybe on the the grid group form, whatever you wanna call it level, or maybe both, you can just set, okay, left line top of line, label and we can be a bit more flexible in that. And the same goes for the description. Right? Show at the bottom, show at the top. Those could be be just a toggle for for the form in in general. The new styles for check boxes, those types of things, the the the making them into 1 drop down and actual drop down. I think that is interface options. That's just separate from from this discussion to me. And then last but not least, there's 2 things coming in from our favorite team, which is our own. Sounds like we're in the last 8 minutes. Should different interface styles be just styles in existing interfaces or new extensions or interfaces? Opinions. Opinions. Opinions. I think it has all to do with the user experience. So to me, a checkbox and a toggle are effectively the same thing from a user experience perspective because you clicked enable, you clicked to disable. They're the same. Right? So to me, a toggle is just a different visual representation of the same underlying thing. This is the pain. Don't hate me too hard. It's a it's a different visual style for the same thing. When it comes to and and, funnily enough, we are not always good about doing it this way either. Right? So, for example, we have a drop down single select and a drop down multi select. In our components in code, it's the same component. A bit hard to maintain, arguably. Shouldn't have done that. We'll probably refect it at some point. Mhmm. Because the user experience is different. Right? Because one is, like, you're you're rendering a list of things. You're checking off multiple. You render it. Now when it comes to a multi select as a list of checkboxes versus a drop down that then shows the list of checkboxes, that is again a different visual style for the same interaction. Right? But where do you draw the line? Because in that example, you could say, well, they're visually so incredibly different. And for the end user, they might expect them to be do 2 different things. So where do you draw that line? I think it's just an opinion in a case by case. Right? It's like in this case, I would say a list of checkboxes or a drop down where you can select multiple things. Those are so different from, like, a mindset perspective that we should make it 2 different interfaces even though they do the exact same thing. You choose you pick and choose 1 or more things from a list, and that's what you're doing. Yeah. Especially if you have 2 different styles, like you said, right, with with the drop down or they could have different options, and then we would have to respect those also in the in the drawer, for example, depending, you know, on what you have. But this has to be configurable then also for extensions. And that also sounds like okay. Just make a new interface. It everything is in order. It is just like that. Deal with it. Yeah. Go with it. It sounds very There's there's 2 more fun ones for the last 2 minutes here. Tim asked, make it difficult nearing the end, will it be able to configure conditions across groups and sections? I think the answer is yes. I think conditions for an individual field or group should always be able to listen to anything else on the form, just whatever nesting level you're in. Of course, there's the same responsibility things. Like, if you listen to something that is visually completely disconnected and related, you can make it very difficult to use for your end users. But from a technical angle, I think we should make it possible. And then the last question here now is saying, what about applying conditions on hiding or disabling fields in the relation drawer based on the state of the form? And for that, I'd like to say tune back in 2 weeks from now. That that is a bit of a different discussion around how that drawer works. The the relation drawer right now, you know, it renders the same layout that you use for that collection, in the main sort of layout for that collection. So, therefore, the fields that show up are are the same that you see on the layout page for that collection. It also means it's a bit tricky to dynamically change that because it is matching the other one, but that is a different moving level. It should be possible with custom CSS and a group extension. Yep. Yep. Yep. Yep. So many ideas. Anyways, with that, we're at the top of the hour. I wanna thank you all very much for tuning in once again. We'll be back at some point in the near future. I think we're in another one in 2 weeks from now. Make sure to find this on direct to zio slash tv. Any other closing thoughts? Dan, John? I figured it out. Thanks to the community and all the good feedback on these on these issues. I'll post comments here shortly and then, Greg, if we need to do a full RFC on this one, we can work it out. Sounds good. Alright, team. Have a wonderful rest of your day.","f5bdab73-98c5-4b82-82cd-d08babe50bca",[2072,2073,2074],"f89c7bab-7b50-4e16-ba47-6c56fd82cf60","13cd4775-d2d1-4305-967e-7282174f4af3","d512e3fc-2b28-4f38-8f0d-25b7041c22bf",[],{"year":978,"number":11,"id":1787,"episodes":2077,"show":2078},[1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1768,1800],{"title":1802,"slug":1803},{"id":1539,"slug":2080,"vimeo_id":2081,"description":2082,"tile":2083,"length":158,"resources":12,"people":12,"episode_number":13,"published":2084,"title":2085,"video_transcript_html":2086,"video_transcript_text":2087,"content":12,"seo":2088,"status":19,"episode_people":2089,"recommendations":2093,"season":2094},"1-august-2024","1003271030","Join us for The Changelog, a new way of taking you through the month’s Directus updates including product updates, new content and community contribution highlights. This month's show includes the MUX Uploader extension contribution, Hannes talking though solving hard problems with the Directus 11 access policies and Kevin with a tutorial on using the Directus UI library in extensions.","5ca9dc07-11c8-4350-9103-63f54c62f804","2024-08-27","August 2024","\u003Cp>Speaker 0: Welcome to the changelog everyone. This is the first of, regular recurring monthly event that we're hoping to do, showing you what is next, new with Directus. So hi. I'm Beth. I am in the developer experience team here with Directus.\u003C\u002Fp>\u003Cp>We would really like to hear your feedback on this event and what we've got in it and what you'd like to see from us in the future. So with that being said, the first set of is with Kevin.\u003C\u002Fp>\u003Cp>Speaker 1: We are going to kick off the changelog with what is probably the most significant announcement for today, and that is the full release of 11. Following on from the release candidate, which we announced in June at our last leap week. And the headline feature of directors 11 is our brand new permission system, which is now based on policies. This is a big shift in how directors handles access control, giving you more power and flexibility by creating reusable sets of permissions. Now of course I can just sit here and talk to you about it, but I've prepared a little demo because I think that's gonna be a little bit better.\u003C\u002Fp>\u003Cp>So let's go over to my screen and I'll show you around. In this demo of directors 11, we are managing a restaurant. And there are 4 collections, staff, shifts, bookings, and sales. And here in the settings, we have this new access policies section, which allows you to manage the new access policies feature. Now I've created a number of policies, each with their own set of permissions.\u003C\u002Fp>\u003Cp>If we look at bookings manage, we see that this policy allows for full, permissions in the bookings collection. And I've also created others to allow users just to add new sales or perhaps to manage sales providing full CRUD access. If we head over to this shifts view policy, we see that there are a number of permissions across multiple collections. Here, there is full read permissions over the shifts collection and partial read access over the staff collection. And specifically here, we're only allowing users with this policy to see the names of staff and no other information.\u003C\u002Fp>\u003Cp>Now policies can be attached to either users or roles or both. Let's add the ability to view shifts to the shift staff. So this is a role I have already created, and now this policy has been added to this role and all users which have it. Now each role or user can have any number of policies attached, and their permissions are an aggregate of all policies. Now this is just a small demo, but I'm sure you can see the power behind the new policies feature for modular, reusable sets of permissions.\u003C\u002Fp>\u003Cp>So that isn't all for directives 11. Of course, access policies is the primary new feature, but there is more. One of our highest upvoted feature requests of all time has been to assign multiple roles to a user. And you can now achieve this as roles can contain other roles and all of the characteristics of all of the child roles will be applied to the user. We also now have a new dynamic variable called current roles, which is an array of all of the roles that is held in a user, both the top level and all of the child level roles.\u003C\u002Fp>\u003Cp>And finally, a couple of breaking changes to be aware of. Of course, there has been a change to the user roles, permissions, and now policies APIs to account for this new data model. So take a look at our API reference for that, but this is also quite important. If you request non existent fields, you will now get thrown an error. So if you're using the query parameter or you're using a query property using the director's SDK and you ask, for example, for an age field and that does not exist for this user, you will receive a 4 zero three error.\u003C\u002Fp>\u003Cp>And another change that's similar is if you are using a many to any field and you're using it via the API, you now have to specify the target collection for fields. You can find details about this in the breaking changes page in the documentation. So that's all about directors 11. Thank you so much, Beth, for giving me the time, and I'll hand back off to you.\u003C\u002Fp>\u003Cp>Speaker 0: Excellent. So, next, we're gonna be discussing the Director's Labs experiment, extensions. And for those of you who might not know what Director's Labs is, it's where we publish the extensions I'm about to show you. It's on the Directors Labs org in GitHub. So the first set of extensions we have coined as the media demos, and they allow rich rendering of the file types.\u003C\u002Fp>\u003Cp>So we have the video which displays a video from either directors in the local file from directors, which is what you're seeing now, Or we've got Vimeo, and we've also got YouTube. So if I get a YouTube ID to add in here, it shows you immediately the video, within the directors. And if I show you the raw value, you can see that the service and ID are both saved in a JSON object here as well. Very similarly, we have a audio player, which allows you to select an audio source and display an audio player from either a URL or a local file from Directus. Again, like the video interface, you can see the field holds the service and the source.\u003C\u002Fp>\u003Cp>Lastly, with the media trio we've got is the PDF viewer, which allows me to see beautiful. A PDF directly within the editor. It's very simple, but it allows if you work with PDF regularly, it's really lovely to see. You can also fit your page to your screen really comfortable. So if you do work a lot with PDFs, this is a nice one for you.\u003C\u002Fp>\u003Cp>Next up, we have the video, no. We've got we've got a whiteboard demo. So the whiteboard interface, it supports, free drawing, shapes, text, and uploading images from your, files. And I'll show you here. Beautiful.\u003C\u002Fp>\u003Cp>We love it. We've got a lot of for the whiteboard. The extension implements fabric. Js and stores data in a restricted way as you can see. So if you want a whiteboard you can also scroll around with it.\u003C\u002Fp>\u003Cp>So, it's a lot of, space for your drawing and text needs. Next up, we have the multilevel demo. So the multilevel API autocomplete interface is probably one of the most powerful, flexible interfaces, that we've released. So it allows for multistep completion where each step can reference the chosen values before it. And we've got 2 demos for you.\u003C\u002Fp>\u003Cp>The first, we've got countries. And so this is where we can select the region. And here, we've got Europe and Americas. This is populated from a hard coded list containing just these two items. I'm gonna select Europe, and then you can hopefully see yep.\u003C\u002Fp>\u003Cp>Perfect. That once that's completed, the value selected is passed into an external web request to an API that can filter based on selected region. So with the European, we can now see that only European entries are being shown. Although, pick this one. Pick whatever.\u003C\u002Fp>\u003Cp>There we go. Although this demo only has 2 steps, you can continue to add more. As you can see within the raw data, each step is stored along with a payload object in a format where we defined, which sorry. Along with the payload object in a format we defined when setting up the interface. So this is what you're seeing.\u003C\u002Fp>\u003Cp>The second demo part is part of the multilevel. We're gonna filter based on the data in the director's, project. So we've got an author's collection, which shows that we have the authors Ben and Reich, and we have a post collection with 3 blog posts that are attached to either then or right here. If we go back into posts, we can see that the first autocomplete this time uses, the author generated director's API for this project to return the authors. So we've been alright.\u003C\u002Fp>\u003Cp>And then from there, that feeds the result in the second API call to filter and show us the post that are by the selected author. So we've got returning, introducing director Sullivan and what we considered when building a marketplace. Cool. The penultimate one, the many to any interface demo. Very cool.\u003C\u002Fp>\u003Cp>So the cool thing about these extensions is that they allow us to experiment and this one is, very much in akin with that. So you can have new UI and UX ideas without needing to roll the changes out to everyone. This is a fairly small experiment. We introduced the UI change to the many set any builder, by adding these big buttons for creating new items. So you can see here, we've got authors, posts, and directors files in create new.\u003C\u002Fp>\u003Cp>It's a small idea and one we rolled out but we're not necessarily seeing a future in it. We'd welcome your thoughts though if you do think this is gonna be useful for something else or you are inspired by it, think you're gonna use it, we'd love to hear about that. Moving on to the last of the demos going into flows, we have the rss t JSON operation. So a small extension to pass, RSS feeds into JSON that can be added to the data chain within Directus flows. What it does is takes a URL, in this case, it's a podcast, RSS feed, and returns JSON.\u003C\u002Fp>\u003Cp>Okay. So what you're viewing here is the output of the you can but these you can infer steps of your automation. For example, you could extract the m p 3 URL and use our AI transcription extension. Kubers does it, and this is the RSS team at JSON, operation, which we think is pretty cool. So we've got some cloud updates for you.\u003C\u002Fp>\u003Cp>Very excitingly, the key update we have is a new starter tier at $15 a month when paid annually. So we've reduced the entry point from $99 a month to our breakeven point for our managed hosted, product. This includes provisioning and hosting a database, asset storage, and caching, all things you need to configure if you were self hosting. So we're hoping you are as excited as that as we are. And we've also got a new self-service that includes single sign on and the introduction of seats for billing.\u003C\u002Fp>\u003Cp>And seats are a new billing product. Seat is a user any user who has access to the Data Studio web application that admins and any users in roles that have access enabled. The starter date comes with one seat, and you can add more. There's also a blog post, around if you want to read more about it. And if you've got any questions, we'd also we're here to, help you as well.\u003C\u002Fp>\u003Cp>Got some upcoming, content updates for you. So we'll start with docs. So over the last few months, we've put a lot of effort into inviting our friends from other language ecosystems to direct us. We already had a lot of JavaScript framework guides. So we've recently expanded out and published getting started guides for the PHP framework Laravel, hyphen framework Flask to accompany our existing post, Flutter with Dart, and building Android app with Directus and Kotlin.\u003C\u002Fp>\u003Cp>So they are all ready for you with getting started guides within the dock. We've also just today released the third of, 3 tutorials on integrating search indexing services with Directus. So you can keep your Elasticsearch, MiniSearch, and Algolia indexes up to date with your Directus project through building custom extensions. And we also have some very cool project tutorials from our community. So I think we've got 3 to showcase.\u003C\u002Fp>\u003Cp>This post from Andreas features reverse engineering a baby monitor and hooking up children to a working service, Opsgenie, via direct us. It's very, very funny. It's great to see. It's very helpful. It sounds very funny, I should say.\u003C\u002Fp>\u003Cp>It's not very funny. It's actually really useful. We also have Jay showing you how to integrate directors with a Chrome extension, which includes authentication, grabbing page data, and storing it and retrieving it from a director's back end. And lastly, we've got Triste, who wrote a tutorial building an ecommerce store with Next. Js as its front end, director's.\u003C\u002Fp>\u003Cp>And then there's also Stripe for payment processing. Lastly, within the content, we've got Directus TV. If you're not familiar, that is our developer focused streaming platform that has dozens of shows focused on education, entertainment, and stories from across the Directus ecosystem. And we've got short hops where Bryant has just released a brand new season, with 8 new quick tips to get the most out of Directus. Season 2 includes an episode on asset transformation presets, focal points, dynamic variables, and complex API queries.\u003C\u002Fp>\u003Cp>And that wraps up the announcements. For the rest of the changelog, we've got 3 short segments we think you'll find interesting. The first one is we've got Hannes, one of our engineers, talking through some of the hard technical problems we solved for the new Directus, 11 access control system.\u003C\u002Fp>\u003Cp>Speaker 2: I just wanna talk about what hard problems we had to solve as part of the policies rewrite or permissions rewrite of the whole director system. First of all, I wanna start off with saying that I joined the company mid April while the rewrite was already way on the road and happening, since Wrike took the leak on lead on that and started implementing it. I looked back in our notion docs. I saw that we started planning it officially, I think, November last year or something. And then the whole process of implementing it from start of development all the way to the release probably took a couple of months, like, 4 or 5 months since it has been quite a big rewrite of the whole permission system, the underlying permission system, which we're using in the core of directors.\u003C\u002Fp>\u003Cp>And touching that and rewriting that and rethinking that presents a challenge since everything relies on it, basically. So whenever we touch all of this, you really need to be sure that we don't break user setups, that we don't introduce any security vulnerabilities, and all of that has to work across 7 different database vendors. Under the hood, we are using the when case statements, so the SQL when case statement. Since we already talked about it a lot, right, we allow different policies for one collection now, and different sets of permissions for those. So what happens if you have a policy that allows access to one field in some cases and a different policy that allows access to some other fields in other cases?\u003C\u002Fp>\u003Cp>There's conflict, essentially. So your item structure might vary. For some items, you might be allowed to see a set of fields, and for another item, you're allowed to see a set of other fields. So to manage that and to have the database do the hard work of deciding which fields are, should be visible, we had to resort to using SQL internal mechanisms or SQL, mechanisms, which we're using. We're using when case, which essentially is an if statement within your query, that allows you to, have a set of permissions.\u003C\u002Fp>\u003Cp>So basically, similar to what you would do in a where clause, that allows you there was always a case in there. No is a new page, problem with, policies. Kevin just asked, wasn't that always the case that there might be a convergence in fields we allowed to see previously with roles. There was one permission per collection per action. So in the end, this decided if an item was visible.\u003C\u002Fp>\u003Cp>And if that policy or that permission didn't match, you didn't see the complete item. And now within your policies, you might see some items and some fields within those items, but not all of the fields. And those sets of what you see can be different now since there's multiple policies applying for the same, applying to the same collection and the same action. So we use when case. When case essentially is a, if statement in your SQL query that, allows us to write stuff like, if the price of this item is larger than $5, show or allow access to the name.\u003C\u002Fp>\u003Cp>And the allow access is basically return the name, otherwise, return null. So in your new API output with policies, you might see some fields showing up as null even though they might be non nullable, for example. But that just comes back to the fact that for some items, you might be able to see the name of the item, and so for some, you might not. Using this statement within our core querying engine resulted in a lot of edge cases, a lot of tinkering, a lot of, r and d involving cross vendor support. You know, director supports 7 different different database vendors last time I counted with a lot of different dialects since MySQL isn't always MySQL.\u003C\u002Fp>\u003Cp>So in the end, we had to test it cross vendors and figure out what are the constructs we can use since there are is a common set of SQL statements you can use in SQL, like, a common part group of stuff you can use. But they might behave differently in Postgres or SQLite or they might return different things. They might expect different different inputs. So all of that had to be solved in a cross vendor support, which again, because it's very deeply integrated within the core itself, prevented a hard challenge to figure out, like, how do we rewrite the actual court permission engine to take into account the new updated requirements for allowing some fields to be present and some not. I just said it's very deeply integrated.\u003C\u002Fp>\u003Cp>So there was a lot of corner cases we had to account for, a lot of, internal APIs we relied on that aren't up to date anymore, that has to be updated, that now expect a completely different inputs, completely new paradigms of looking at permissions because previously, it was fairly straightforward. But now, there is more edge cases to take care of, which has been very interesting to look at. In the end, it resulted in a lot of automated and manual testing to ensure that we don't introduce any regressions, that the API output stays the same, and that we provide a migration that allows users to directly migrate from b 10 to b 11 without any hiccups, hopefully. So we migrate all the existing permissions from roles, stuff them into policies, and figuring that out also was fun. Let's say fun.\u003C\u002Fp>\u003Cp>And something we're noticing right now is that we have a very, very large API surface. So, a lot of users of our product expect some undocumented behavior to work the same before v 10 and v 11, which now in the aftermath of having it released to general availability shows that a lot of edge cases we didn't account for, which we have to now after the facts actually look at and solve on a timely basis. And I think that's my 5 minutes to talk about why it was very hard to solve all the new policy and permissions across different vendors.\u003C\u002Fp>\u003Cp>Speaker 0: Thank you so much. Moving along to our community showcase section. We have a video very kindly, sent to us by Matthew Ruffino, who is talking about the Mutts uploader.\u003C\u002Fp>\u003Cp>Speaker 3: Hi, everybody. My name is Matthew, and I am the current developer for the Mucs upload extension. We're gonna watch a quick little demo. So here we have a brand new page where you can upload directly to Mucs via your direct us back end, that is. This is using the built in Mucs uploader.\u003C\u002Fp>\u003Cp>Future plans will have our own custom uploader where we can have it as a custom interface into pre existing collections. Currently, we'll go to our new videos collection and we'll see here that we have a new upload ID, asset ID, and playback ID. When we go into the page, we can see a new interface type that uses the custom mux player for our playback. This right here allows us to quickly upload video, have it transcoded, and be available for view and playback on our back end or in our front end applications. So the motivation for this is client facing like always.\u003C\u002Fp>\u003Cp>I had a client that reached out to me that wanted to build a custom video vault. They had years years years of video content that they wanted to upload and they wanted a way to have it nice and digitally organized. And so the first thing that I thought was, let's use the Directus. And so I did. Directus is now powering this video vault.\u003C\u002Fp>\u003Cp>But there was just a little bit more that was needed. Right? So the video vault is good for for back end stuff. We can quickly load s 3 and have playback there via mpeg 4. But what if we want to now share this via front end, right, via something like a streaming service?\u003C\u002Fp>\u003Cp>This is where Mucs comes into play. You can easily be able to share video via an Amazon S3 bucket, But when you start serving this content for thousands and thousands of people, you'll start to see a lot of slowdown in variation unless you do a whole bunch of configuration. Using mux allows you to easily transcode and get a playback ID immediately to be able to use in your front end application by use of thousands of people and be able to securely know that your video content is safe using DRM. So the approach that I built this was considering some other options that were available. But I've always wanted to use mukes in a project and so I thought that this would be the perfect use case.\u003C\u002Fp>\u003Cp>The biggest challenge was really just sitting down and be able to start. Right? We look at documentation all the time and we have ideas, but the hardest part is literally sitting down and just getting started. Once I got started and read through all the documentation and spun up something on Docker, I was off to the races. And it was one of the most easy things that I've ever started to do.\u003C\u002Fp>\u003Cp>To be honest, when it comes to development for different services, back ends, and and things like that, right? Directus just made it very easy out of the gate to be able to start developing this extension. So as I'm developing this, I have a lot of ideas planned for version 1. So the extra things planned would be something like live streaming. Being able to quickly create a live stream in our back end and receive the keys needed for programs like OBS or vMix, or to be able to tie that into a front end and to allow users or other people in the front end application that we're building to receive their own live streaming keys and then to live stream.\u003C\u002Fp>\u003Cp>Other options and other planned features would be things like watermarking, closed captioning, and secure stream options. So with each video that you upload, you'll be able to include custom watermarks, closed captioning files like SRTs to have them built in, and to be able to secure each video the way that you want to tied to user roles. I'm actually also planning to build the same thing for BunnyStream by BunnyCDN. BunnyCDN is a great choice for storage and CDN services, and they have been expanding their technology over the years. BunnyCDN offers something very similar to Mucs where you can upload content, video content, audio content, and to be able to receive streaming links right in return.\u003C\u002Fp>\u003Cp>Same thing with closed captioning, DRM, and more. So once the Mucs upload extension is completed, I'm gonna set my sights on BunnyCDN to be able to use BunnyStream built into Directus. I'm always looking for help. Always looking for help. I have so many clients, I have 3 little girls, and my hands are always tied.\u003C\u002Fp>\u003Cp>If you like this idea and you think that you could benefit from it, the source code is available in GitHub right now. I'm always looking for contributors to help me build the best things that I can. And if you wanna be a part of that, that would be amazing. Please find me as Mateo on the Directus Discord and we can talk more. Or go ahead and make a PR and we can start there too.\u003C\u002Fp>\u003Cp>Now the best for last, Mukes is actually planning on featuring this extension on their website once it's completed. I had preliminary talks with them this year saying, hey, you guys support all these other platforms but why not Directus? And so I showed them what I what I was working on and I shared videos and code base with them and they're very excited. While they can't say or take the code and run with it, they are gonna do a highlight series kind of similar to this where they'll be able to talk about me, Directus, and how everything works together with mukes. And it's not only gonna be beneficial for mukes, it's gonna be amazing for Directus to have that kind of of, you know, happiness and feedback from a completely different team.\u003C\u002Fp>\u003Cp>I'm very excited and I hope that you guys are just as excited as I am. Again, my name is Matthew and I'm the sole developer here currently for the Mucs uploader looking for contributors. You guys have a great rest of your day. Bye.\u003C\u002Fp>\u003Cp>Speaker 0: Thank you so much, Rousseau. I very much all of you legend in the Director's, Discord. We now have a developing tutorial. And for this month, Kevin is going through using Directvist UI library and extensions.\u003C\u002Fp>\u003Cp>Speaker 1: For the very first change log, we've decided to, as we get towards the end, pop a little developer tutorial in here for one of the questions we're seeing come up an increasing amount. And that is for new extension authors who are wondering how to make their extensions feel consistent with the rest of the director's data studio, by using some of the premade components that exist in the rest of the application. So here I spun up a new directors project locally. Take note that extensions auto reload is set to true. And we are mounting in an extensions volume, which is this directory here in the sidebar.\u003C\u002Fp>\u003Cp>And then we're going to go into the extensions, one moment, we're gonna go into the extensions directory, and run MPX create extension at latest. You can pick an extension type. We're going to pick a panel. I'll just call it test very inspired, and leave all of the other options as default. So now we have this test directory here, which contains our entry point file, this index dotjs, which contains all the configuration for the panel, and the view component itself, and this is where we'll be doing our work.\u003C\u002Fp>\u003Cp>So we're gonna CD into this directory, and run NPM run dev. And what that's going to do is watch for changes inside of this directory source folder. And whenever there is a change made, it will rebuild it into this dist folder. And this is what will actually load in. So I'm just gonna just first time just restart the, container, restart the the Docker container for the first time, and then it should auto reload when we make changes.\u003C\u002Fp>\u003Cp>And if I refresh this empty, dashboard here, we should now see there is our custom panel right there. There is the test, sorry, text option, which is this one. So I might say, hello, and hit save. And we see that that's rendered into the panel here. It's passed in as a prop, and we can use it straight within here.\u003C\u002Fp>\u003Cp>So that's fantastic. We've got this panel. But now we wanna actually start to include, these UI components that exist. Now we actually ship what we call a component playground. This is a components dot directors.io.\u003C\u002Fp>\u003Cp>We're still adding a few last components here. So this isn't an exhaustive list, but it is many of them. And this contains everything from a directors feeling checkbox, we can see here that looks like what exists in the data studio. A input complete with, you know, the focus and the active states, stuff like that. Pagination and so on and so forth.\u003C\u002Fp>\u003Cp>And you can use any of these by taking code that is generated by this component playground. So we have this fancy select, for example, here. Right. Ultimately, just to select, kinda cool. And what's nice is we get this little controls area on the right hand side where we can make changes.\u003C\u002Fp>\u003Cp>So let's find one. This is probably not a fantastic example of the customizability. Maybe we look at the input for a quick example here. We see that there are quite a few options. Is it disabled?\u003C\u002Fp>\u003Cp>And you see immediately it gets grayed out with all of the, behavior and the styling that you would expect. Are we gonna it? So whenever you type, you know, you put a space, it replaces it with a dash and so on, only lower case and so on. The slug separator, you know, you could make it an underscore. And there's just all of these options that we have here.\u003C\u002Fp>\u003Cp>And you'll notice that as I'm checking them here or changing the values, the prefix could be, I don't know, a smiley face or maybe HTTPS, something like that. We see that it's changing in the UI here in this little, this little example, but it's also changing it down here. And the best thing about this is all of these components are made globally available inside of the data studio, including for extension. So I can literally copy this v input here, copy it. I can paste it directly in.\u003C\u002Fp>\u003Cp>Now this one has a v model, so just because it's, just because it's view, I will just quickly make sure that it has a value it can re it can write into value quote. Great. And that's it. Like, that is us using the v input. The extensions auto reloaded.\u003C\u002Fp>\u003Cp>So if I go back here and refresh, there we are. There's our UI component. And this is a panel, but you can use this across any of the app extension types, any of any of the components that are listed in that components playground there. So that's a little example of how you can use the work that we have already done in order to build consistent, and in my opinion, quite nice looking, extensions that feel like they belong inside of the Data Studio. So, again, thank you for giving me the time to show you this, and I'll hand back over to Beth.\u003C\u002Fp>\u003Cp>Speaker 0: So wrapping this up, we want to take this moment towards the end of the changelog to thank our amazing community contributors who do a lot and give their time to improve the Derivatives project and for whom we are very very grateful. So since the last time we did this where we thanked contributors, we've had multiple releases and multiple contributors so there is quite a bit of a list. I am going to thank them individually. So, thank you to the following people. Parker for adding support for ad admin token environment variable, which will be great for testing and initial project bootstrapping.\u003C\u002Fp>\u003Cp>Josh for fixing an issue causing the TUS uploads not to respect the relative path of the app. Joel for prioritising access token in a query over cookies for web sockets authentication. Dominic for optimizing the type signature of the item service collection parameter. Yep. Gerald for enabling caching of field information and foreign keys as part of schema caching.\u003C\u002Fp>\u003Cp>Florian for increasing visibility of the data model expand slash collapse buttons. Junhong for ensuring the drop down interface correctly works when there are no options. Adding auto reset of a drop down face value after conditional update of options, fixing the versioning drop down to with long version names, fixing detail groups collapsing on save and stay, fixing the calendar layout crashing with invalid dates, removing the update delay in the block editor interface, and lastly, fixing the list structure in draggable list. Thanks also to Max for fixing filename disk extension not getting updated when replacing a file with another file extension. Andre for ensuring the assets transform image max dimension value is also respected for extraction of metadata during image upload.\u003C\u002Fp>\u003Cp>Johan for adding a retry mechanism for SQLite if a SQLite busy error occurs. Florian for adding support for listening on UNIX WebSocket, UNIX sockets via a new UNIX socket path variable, Danilo Burger for fixing an issue that would cause the translation display not to use the correct language if the user relied on system language, and finally, clicker for fixing a warning when using Docker with MySQL slash Maria DB. So that was a long list, and we are so grateful for every single one of you. Thank you again. And if you're listening to this and you want to see the specific pull requests, you can find them inside of the full release notes on GitHub.\u003C\u002Fp>\u003Cp>That concludes I'm just going to see and pause if there are any questions that we've done with. There's not. That concludes the first, change log. And as we said at the beginning, but just to reiterate, we are so welcoming of feedback of anything you might like to see, what you like to see more of, maybe what you like to see less of if it's constructive. We are hoping to be really flexible with the different segments we show you so some things will stay consistent I.\u003C\u002Fp>\u003Cp>E. What's new and the updates but we'll be bringing different things. We've got a lot of good ideas and we definitely want to be here yours as well. But that's everything so thank you so much for joining us and taking the time. We really appreciate it.\u003C\u002Fp>\u003Cp>We hope that you found this interesting and we'll be back again so keep an eye out on the events section and hopefully we'll see you next time. But if nothing else we'll be around here for a couple minutes in case there's any last questions But if not, thanks everyone and have a very great rest of your day.\u003C\u002Fp>","Welcome to the changelog everyone. This is the first of, regular recurring monthly event that we're hoping to do, showing you what is next, new with Directus. So hi. I'm Beth. I am in the developer experience team here with Directus. We would really like to hear your feedback on this event and what we've got in it and what you'd like to see from us in the future. So with that being said, the first set of is with Kevin. We are going to kick off the changelog with what is probably the most significant announcement for today, and that is the full release of 11. Following on from the release candidate, which we announced in June at our last leap week. And the headline feature of directors 11 is our brand new permission system, which is now based on policies. This is a big shift in how directors handles access control, giving you more power and flexibility by creating reusable sets of permissions. Now of course I can just sit here and talk to you about it, but I've prepared a little demo because I think that's gonna be a little bit better. So let's go over to my screen and I'll show you around. In this demo of directors 11, we are managing a restaurant. And there are 4 collections, staff, shifts, bookings, and sales. And here in the settings, we have this new access policies section, which allows you to manage the new access policies feature. Now I've created a number of policies, each with their own set of permissions. If we look at bookings manage, we see that this policy allows for full, permissions in the bookings collection. And I've also created others to allow users just to add new sales or perhaps to manage sales providing full CRUD access. If we head over to this shifts view policy, we see that there are a number of permissions across multiple collections. Here, there is full read permissions over the shifts collection and partial read access over the staff collection. And specifically here, we're only allowing users with this policy to see the names of staff and no other information. Now policies can be attached to either users or roles or both. Let's add the ability to view shifts to the shift staff. So this is a role I have already created, and now this policy has been added to this role and all users which have it. Now each role or user can have any number of policies attached, and their permissions are an aggregate of all policies. Now this is just a small demo, but I'm sure you can see the power behind the new policies feature for modular, reusable sets of permissions. So that isn't all for directives 11. Of course, access policies is the primary new feature, but there is more. One of our highest upvoted feature requests of all time has been to assign multiple roles to a user. And you can now achieve this as roles can contain other roles and all of the characteristics of all of the child roles will be applied to the user. We also now have a new dynamic variable called current roles, which is an array of all of the roles that is held in a user, both the top level and all of the child level roles. And finally, a couple of breaking changes to be aware of. Of course, there has been a change to the user roles, permissions, and now policies APIs to account for this new data model. So take a look at our API reference for that, but this is also quite important. If you request non existent fields, you will now get thrown an error. So if you're using the query parameter or you're using a query property using the director's SDK and you ask, for example, for an age field and that does not exist for this user, you will receive a 4 zero three error. And another change that's similar is if you are using a many to any field and you're using it via the API, you now have to specify the target collection for fields. You can find details about this in the breaking changes page in the documentation. So that's all about directors 11. Thank you so much, Beth, for giving me the time, and I'll hand back off to you. Excellent. So, next, we're gonna be discussing the Director's Labs experiment, extensions. And for those of you who might not know what Director's Labs is, it's where we publish the extensions I'm about to show you. It's on the Directors Labs org in GitHub. So the first set of extensions we have coined as the media demos, and they allow rich rendering of the file types. So we have the video which displays a video from either directors in the local file from directors, which is what you're seeing now, Or we've got Vimeo, and we've also got YouTube. So if I get a YouTube ID to add in here, it shows you immediately the video, within the directors. And if I show you the raw value, you can see that the service and ID are both saved in a JSON object here as well. Very similarly, we have a audio player, which allows you to select an audio source and display an audio player from either a URL or a local file from Directus. Again, like the video interface, you can see the field holds the service and the source. Lastly, with the media trio we've got is the PDF viewer, which allows me to see beautiful. A PDF directly within the editor. It's very simple, but it allows if you work with PDF regularly, it's really lovely to see. You can also fit your page to your screen really comfortable. So if you do work a lot with PDFs, this is a nice one for you. Next up, we have the video, no. We've got we've got a whiteboard demo. So the whiteboard interface, it supports, free drawing, shapes, text, and uploading images from your, files. And I'll show you here. Beautiful. We love it. We've got a lot of for the whiteboard. The extension implements fabric. Js and stores data in a restricted way as you can see. So if you want a whiteboard you can also scroll around with it. So, it's a lot of, space for your drawing and text needs. Next up, we have the multilevel demo. So the multilevel API autocomplete interface is probably one of the most powerful, flexible interfaces, that we've released. So it allows for multistep completion where each step can reference the chosen values before it. And we've got 2 demos for you. The first, we've got countries. And so this is where we can select the region. And here, we've got Europe and Americas. This is populated from a hard coded list containing just these two items. I'm gonna select Europe, and then you can hopefully see yep. Perfect. That once that's completed, the value selected is passed into an external web request to an API that can filter based on selected region. So with the European, we can now see that only European entries are being shown. Although, pick this one. Pick whatever. There we go. Although this demo only has 2 steps, you can continue to add more. As you can see within the raw data, each step is stored along with a payload object in a format where we defined, which sorry. Along with the payload object in a format we defined when setting up the interface. So this is what you're seeing. The second demo part is part of the multilevel. We're gonna filter based on the data in the director's, project. So we've got an author's collection, which shows that we have the authors Ben and Reich, and we have a post collection with 3 blog posts that are attached to either then or right here. If we go back into posts, we can see that the first autocomplete this time uses, the author generated director's API for this project to return the authors. So we've been alright. And then from there, that feeds the result in the second API call to filter and show us the post that are by the selected author. So we've got returning, introducing director Sullivan and what we considered when building a marketplace. Cool. The penultimate one, the many to any interface demo. Very cool. So the cool thing about these extensions is that they allow us to experiment and this one is, very much in akin with that. So you can have new UI and UX ideas without needing to roll the changes out to everyone. This is a fairly small experiment. We introduced the UI change to the many set any builder, by adding these big buttons for creating new items. So you can see here, we've got authors, posts, and directors files in create new. It's a small idea and one we rolled out but we're not necessarily seeing a future in it. We'd welcome your thoughts though if you do think this is gonna be useful for something else or you are inspired by it, think you're gonna use it, we'd love to hear about that. Moving on to the last of the demos going into flows, we have the rss t JSON operation. So a small extension to pass, RSS feeds into JSON that can be added to the data chain within Directus flows. What it does is takes a URL, in this case, it's a podcast, RSS feed, and returns JSON. Okay. So what you're viewing here is the output of the you can but these you can infer steps of your automation. For example, you could extract the m p 3 URL and use our AI transcription extension. Kubers does it, and this is the RSS team at JSON, operation, which we think is pretty cool. So we've got some cloud updates for you. Very excitingly, the key update we have is a new starter tier at $15 a month when paid annually. So we've reduced the entry point from $99 a month to our breakeven point for our managed hosted, product. This includes provisioning and hosting a database, asset storage, and caching, all things you need to configure if you were self hosting. So we're hoping you are as excited as that as we are. And we've also got a new self-service that includes single sign on and the introduction of seats for billing. And seats are a new billing product. Seat is a user any user who has access to the Data Studio web application that admins and any users in roles that have access enabled. The starter date comes with one seat, and you can add more. There's also a blog post, around if you want to read more about it. And if you've got any questions, we'd also we're here to, help you as well. Got some upcoming, content updates for you. So we'll start with docs. So over the last few months, we've put a lot of effort into inviting our friends from other language ecosystems to direct us. We already had a lot of JavaScript framework guides. So we've recently expanded out and published getting started guides for the PHP framework Laravel, hyphen framework Flask to accompany our existing post, Flutter with Dart, and building Android app with Directus and Kotlin. So they are all ready for you with getting started guides within the dock. We've also just today released the third of, 3 tutorials on integrating search indexing services with Directus. So you can keep your Elasticsearch, MiniSearch, and Algolia indexes up to date with your Directus project through building custom extensions. And we also have some very cool project tutorials from our community. So I think we've got 3 to showcase. This post from Andreas features reverse engineering a baby monitor and hooking up children to a working service, Opsgenie, via direct us. It's very, very funny. It's great to see. It's very helpful. It sounds very funny, I should say. It's not very funny. It's actually really useful. We also have Jay showing you how to integrate directors with a Chrome extension, which includes authentication, grabbing page data, and storing it and retrieving it from a director's back end. And lastly, we've got Triste, who wrote a tutorial building an ecommerce store with Next. Js as its front end, director's. And then there's also Stripe for payment processing. Lastly, within the content, we've got Directus TV. If you're not familiar, that is our developer focused streaming platform that has dozens of shows focused on education, entertainment, and stories from across the Directus ecosystem. And we've got short hops where Bryant has just released a brand new season, with 8 new quick tips to get the most out of Directus. Season 2 includes an episode on asset transformation presets, focal points, dynamic variables, and complex API queries. And that wraps up the announcements. For the rest of the changelog, we've got 3 short segments we think you'll find interesting. The first one is we've got Hannes, one of our engineers, talking through some of the hard technical problems we solved for the new Directus, 11 access control system. I just wanna talk about what hard problems we had to solve as part of the policies rewrite or permissions rewrite of the whole director system. First of all, I wanna start off with saying that I joined the company mid April while the rewrite was already way on the road and happening, since Wrike took the leak on lead on that and started implementing it. I looked back in our notion docs. I saw that we started planning it officially, I think, November last year or something. And then the whole process of implementing it from start of development all the way to the release probably took a couple of months, like, 4 or 5 months since it has been quite a big rewrite of the whole permission system, the underlying permission system, which we're using in the core of directors. And touching that and rewriting that and rethinking that presents a challenge since everything relies on it, basically. So whenever we touch all of this, you really need to be sure that we don't break user setups, that we don't introduce any security vulnerabilities, and all of that has to work across 7 different database vendors. Under the hood, we are using the when case statements, so the SQL when case statement. Since we already talked about it a lot, right, we allow different policies for one collection now, and different sets of permissions for those. So what happens if you have a policy that allows access to one field in some cases and a different policy that allows access to some other fields in other cases? There's conflict, essentially. So your item structure might vary. For some items, you might be allowed to see a set of fields, and for another item, you're allowed to see a set of other fields. So to manage that and to have the database do the hard work of deciding which fields are, should be visible, we had to resort to using SQL internal mechanisms or SQL, mechanisms, which we're using. We're using when case, which essentially is an if statement within your query, that allows you to, have a set of permissions. So basically, similar to what you would do in a where clause, that allows you there was always a case in there. No is a new page, problem with, policies. Kevin just asked, wasn't that always the case that there might be a convergence in fields we allowed to see previously with roles. There was one permission per collection per action. So in the end, this decided if an item was visible. And if that policy or that permission didn't match, you didn't see the complete item. And now within your policies, you might see some items and some fields within those items, but not all of the fields. And those sets of what you see can be different now since there's multiple policies applying for the same, applying to the same collection and the same action. So we use when case. When case essentially is a, if statement in your SQL query that, allows us to write stuff like, if the price of this item is larger than $5, show or allow access to the name. And the allow access is basically return the name, otherwise, return null. So in your new API output with policies, you might see some fields showing up as null even though they might be non nullable, for example. But that just comes back to the fact that for some items, you might be able to see the name of the item, and so for some, you might not. Using this statement within our core querying engine resulted in a lot of edge cases, a lot of tinkering, a lot of, r and d involving cross vendor support. You know, director supports 7 different different database vendors last time I counted with a lot of different dialects since MySQL isn't always MySQL. So in the end, we had to test it cross vendors and figure out what are the constructs we can use since there are is a common set of SQL statements you can use in SQL, like, a common part group of stuff you can use. But they might behave differently in Postgres or SQLite or they might return different things. They might expect different different inputs. So all of that had to be solved in a cross vendor support, which again, because it's very deeply integrated within the core itself, prevented a hard challenge to figure out, like, how do we rewrite the actual court permission engine to take into account the new updated requirements for allowing some fields to be present and some not. I just said it's very deeply integrated. So there was a lot of corner cases we had to account for, a lot of, internal APIs we relied on that aren't up to date anymore, that has to be updated, that now expect a completely different inputs, completely new paradigms of looking at permissions because previously, it was fairly straightforward. But now, there is more edge cases to take care of, which has been very interesting to look at. In the end, it resulted in a lot of automated and manual testing to ensure that we don't introduce any regressions, that the API output stays the same, and that we provide a migration that allows users to directly migrate from b 10 to b 11 without any hiccups, hopefully. So we migrate all the existing permissions from roles, stuff them into policies, and figuring that out also was fun. Let's say fun. And something we're noticing right now is that we have a very, very large API surface. So, a lot of users of our product expect some undocumented behavior to work the same before v 10 and v 11, which now in the aftermath of having it released to general availability shows that a lot of edge cases we didn't account for, which we have to now after the facts actually look at and solve on a timely basis. And I think that's my 5 minutes to talk about why it was very hard to solve all the new policy and permissions across different vendors. Thank you so much. Moving along to our community showcase section. We have a video very kindly, sent to us by Matthew Ruffino, who is talking about the Mutts uploader. Hi, everybody. My name is Matthew, and I am the current developer for the Mucs upload extension. We're gonna watch a quick little demo. So here we have a brand new page where you can upload directly to Mucs via your direct us back end, that is. This is using the built in Mucs uploader. Future plans will have our own custom uploader where we can have it as a custom interface into pre existing collections. Currently, we'll go to our new videos collection and we'll see here that we have a new upload ID, asset ID, and playback ID. When we go into the page, we can see a new interface type that uses the custom mux player for our playback. This right here allows us to quickly upload video, have it transcoded, and be available for view and playback on our back end or in our front end applications. So the motivation for this is client facing like always. I had a client that reached out to me that wanted to build a custom video vault. They had years years years of video content that they wanted to upload and they wanted a way to have it nice and digitally organized. And so the first thing that I thought was, let's use the Directus. And so I did. Directus is now powering this video vault. But there was just a little bit more that was needed. Right? So the video vault is good for for back end stuff. We can quickly load s 3 and have playback there via mpeg 4. But what if we want to now share this via front end, right, via something like a streaming service? This is where Mucs comes into play. You can easily be able to share video via an Amazon S3 bucket, But when you start serving this content for thousands and thousands of people, you'll start to see a lot of slowdown in variation unless you do a whole bunch of configuration. Using mux allows you to easily transcode and get a playback ID immediately to be able to use in your front end application by use of thousands of people and be able to securely know that your video content is safe using DRM. So the approach that I built this was considering some other options that were available. But I've always wanted to use mukes in a project and so I thought that this would be the perfect use case. The biggest challenge was really just sitting down and be able to start. Right? We look at documentation all the time and we have ideas, but the hardest part is literally sitting down and just getting started. Once I got started and read through all the documentation and spun up something on Docker, I was off to the races. And it was one of the most easy things that I've ever started to do. To be honest, when it comes to development for different services, back ends, and and things like that, right? Directus just made it very easy out of the gate to be able to start developing this extension. So as I'm developing this, I have a lot of ideas planned for version 1. So the extra things planned would be something like live streaming. Being able to quickly create a live stream in our back end and receive the keys needed for programs like OBS or vMix, or to be able to tie that into a front end and to allow users or other people in the front end application that we're building to receive their own live streaming keys and then to live stream. Other options and other planned features would be things like watermarking, closed captioning, and secure stream options. So with each video that you upload, you'll be able to include custom watermarks, closed captioning files like SRTs to have them built in, and to be able to secure each video the way that you want to tied to user roles. I'm actually also planning to build the same thing for BunnyStream by BunnyCDN. BunnyCDN is a great choice for storage and CDN services, and they have been expanding their technology over the years. BunnyCDN offers something very similar to Mucs where you can upload content, video content, audio content, and to be able to receive streaming links right in return. Same thing with closed captioning, DRM, and more. So once the Mucs upload extension is completed, I'm gonna set my sights on BunnyCDN to be able to use BunnyStream built into Directus. I'm always looking for help. Always looking for help. I have so many clients, I have 3 little girls, and my hands are always tied. If you like this idea and you think that you could benefit from it, the source code is available in GitHub right now. I'm always looking for contributors to help me build the best things that I can. And if you wanna be a part of that, that would be amazing. Please find me as Mateo on the Directus Discord and we can talk more. Or go ahead and make a PR and we can start there too. Now the best for last, Mukes is actually planning on featuring this extension on their website once it's completed. I had preliminary talks with them this year saying, hey, you guys support all these other platforms but why not Directus? And so I showed them what I what I was working on and I shared videos and code base with them and they're very excited. While they can't say or take the code and run with it, they are gonna do a highlight series kind of similar to this where they'll be able to talk about me, Directus, and how everything works together with mukes. And it's not only gonna be beneficial for mukes, it's gonna be amazing for Directus to have that kind of of, you know, happiness and feedback from a completely different team. I'm very excited and I hope that you guys are just as excited as I am. Again, my name is Matthew and I'm the sole developer here currently for the Mucs uploader looking for contributors. You guys have a great rest of your day. Bye. Thank you so much, Rousseau. I very much all of you legend in the Director's, Discord. We now have a developing tutorial. And for this month, Kevin is going through using Directvist UI library and extensions. For the very first change log, we've decided to, as we get towards the end, pop a little developer tutorial in here for one of the questions we're seeing come up an increasing amount. And that is for new extension authors who are wondering how to make their extensions feel consistent with the rest of the director's data studio, by using some of the premade components that exist in the rest of the application. So here I spun up a new directors project locally. Take note that extensions auto reload is set to true. And we are mounting in an extensions volume, which is this directory here in the sidebar. And then we're going to go into the extensions, one moment, we're gonna go into the extensions directory, and run MPX create extension at latest. You can pick an extension type. We're going to pick a panel. I'll just call it test very inspired, and leave all of the other options as default. So now we have this test directory here, which contains our entry point file, this index dotjs, which contains all the configuration for the panel, and the view component itself, and this is where we'll be doing our work. So we're gonna CD into this directory, and run NPM run dev. And what that's going to do is watch for changes inside of this directory source folder. And whenever there is a change made, it will rebuild it into this dist folder. And this is what will actually load in. So I'm just gonna just first time just restart the, container, restart the the Docker container for the first time, and then it should auto reload when we make changes. And if I refresh this empty, dashboard here, we should now see there is our custom panel right there. There is the test, sorry, text option, which is this one. So I might say, hello, and hit save. And we see that that's rendered into the panel here. It's passed in as a prop, and we can use it straight within here. So that's fantastic. We've got this panel. But now we wanna actually start to include, these UI components that exist. Now we actually ship what we call a component playground. This is a components dot directors.io. We're still adding a few last components here. So this isn't an exhaustive list, but it is many of them. And this contains everything from a directors feeling checkbox, we can see here that looks like what exists in the data studio. A input complete with, you know, the focus and the active states, stuff like that. Pagination and so on and so forth. And you can use any of these by taking code that is generated by this component playground. So we have this fancy select, for example, here. Right. Ultimately, just to select, kinda cool. And what's nice is we get this little controls area on the right hand side where we can make changes. So let's find one. This is probably not a fantastic example of the customizability. Maybe we look at the input for a quick example here. We see that there are quite a few options. Is it disabled? And you see immediately it gets grayed out with all of the, behavior and the styling that you would expect. Are we gonna it? So whenever you type, you know, you put a space, it replaces it with a dash and so on, only lower case and so on. The slug separator, you know, you could make it an underscore. And there's just all of these options that we have here. And you'll notice that as I'm checking them here or changing the values, the prefix could be, I don't know, a smiley face or maybe HTTPS, something like that. We see that it's changing in the UI here in this little, this little example, but it's also changing it down here. And the best thing about this is all of these components are made globally available inside of the data studio, including for extension. So I can literally copy this v input here, copy it. I can paste it directly in. Now this one has a v model, so just because it's, just because it's view, I will just quickly make sure that it has a value it can re it can write into value quote. Great. And that's it. Like, that is us using the v input. The extensions auto reloaded. So if I go back here and refresh, there we are. There's our UI component. And this is a panel, but you can use this across any of the app extension types, any of any of the components that are listed in that components playground there. So that's a little example of how you can use the work that we have already done in order to build consistent, and in my opinion, quite nice looking, extensions that feel like they belong inside of the Data Studio. So, again, thank you for giving me the time to show you this, and I'll hand back over to Beth. So wrapping this up, we want to take this moment towards the end of the changelog to thank our amazing community contributors who do a lot and give their time to improve the Derivatives project and for whom we are very very grateful. So since the last time we did this where we thanked contributors, we've had multiple releases and multiple contributors so there is quite a bit of a list. I am going to thank them individually. So, thank you to the following people. Parker for adding support for ad admin token environment variable, which will be great for testing and initial project bootstrapping. Josh for fixing an issue causing the TUS uploads not to respect the relative path of the app. Joel for prioritising access token in a query over cookies for web sockets authentication. Dominic for optimizing the type signature of the item service collection parameter. Yep. Gerald for enabling caching of field information and foreign keys as part of schema caching. Florian for increasing visibility of the data model expand slash collapse buttons. Junhong for ensuring the drop down interface correctly works when there are no options. Adding auto reset of a drop down face value after conditional update of options, fixing the versioning drop down to with long version names, fixing detail groups collapsing on save and stay, fixing the calendar layout crashing with invalid dates, removing the update delay in the block editor interface, and lastly, fixing the list structure in draggable list. Thanks also to Max for fixing filename disk extension not getting updated when replacing a file with another file extension. Andre for ensuring the assets transform image max dimension value is also respected for extraction of metadata during image upload. Johan for adding a retry mechanism for SQLite if a SQLite busy error occurs. Florian for adding support for listening on UNIX WebSocket, UNIX sockets via a new UNIX socket path variable, Danilo Burger for fixing an issue that would cause the translation display not to use the correct language if the user relied on system language, and finally, clicker for fixing a warning when using Docker with MySQL slash Maria DB. So that was a long list, and we are so grateful for every single one of you. Thank you again. And if you're listening to this and you want to see the specific pull requests, you can find them inside of the full release notes on GitHub. That concludes I'm just going to see and pause if there are any questions that we've done with. There's not. That concludes the first, change log. And as we said at the beginning, but just to reiterate, we are so welcoming of feedback of anything you might like to see, what you like to see more of, maybe what you like to see less of if it's constructive. We are hoping to be really flexible with the different segments we show you so some things will stay consistent I. E. What's new and the updates but we'll be bringing different things. We've got a lot of good ideas and we definitely want to be here yours as well. But that's everything so thank you so much for joining us and taking the time. We really appreciate it. We hope that you found this interesting and we'll be back again so keep an eye out on the events section and hopefully we'll see you next time. But if nothing else we'll be around here for a couple minutes in case there's any last questions But if not, thanks everyone and have a very great rest of your day.","503c4ebe-b99b-4706-9418-eb6409863fed",[2090,2091,2092],"be894782-2277-4ab0-904b-b872d7460fd8","55bcda6a-85ef-46e2-9f9d-11cfa049f5a0","eed47369-d68d-49a8-ac36-ea260db0eab4",[],{"year":978,"number":1536,"id":1537,"episodes":2095,"show":2096},[1539,1540,1541,1542,1519],{"title":297,"slug":298},{"id":1798,"slug":2098,"vimeo_id":2099,"description":2100,"tile":2101,"length":2062,"resources":2102,"people":12,"episode_number":889,"published":2105,"title":2106,"video_transcript_html":2107,"video_transcript_text":2108,"content":12,"seo":2109,"status":19,"episode_people":2110,"recommendations":2114,"season":2115},"18618","1000292187","In this recording of our live event on August 15 2024, Daniel, Jonathan, and Rick discuss import and export options for flows","d71dfd7c-0662-41cc-aece-dfa8bc79919a",[2103],{"name":1775,"url":2104},"https:\u002F\u002Fgithub.com\u002Fdirectus\u002Fdirectus\u002Fdiscussions\u002F18618","2024-08-22","Import & Export option for Flows","\u003Cp>Speaker 0: Welcome to another episode of request review. What are we talking about today?\u003C\u002Fp>\u003Cp>Speaker 1: We're talking about import and export options for flows.\u003C\u002Fp>\u003Cp>Speaker 0: Oh. Now then if he says a like that, it sounds\u003C\u002Fp>\u003Cp>Speaker 2: Why don't we just edit then? You know? It should be quick fix. Right?\u003C\u002Fp>\u003Cp>Speaker 0: I think that's a great idea. Well, with that being said, thank you so much for watching. Find this episode on of course, I'm kidding. There's there's interesting, as per usual, these episodes, that's why we do them. Some fun edge cases here to consider.\u003C\u002Fp>\u003Cp>First and foremost, for those, you know, out of the loop flows, automation workflows, you can set up, you know, triggers. If somebody saves a thing, somebody hits an endpoint, somebody clicks a button in the app, do something. Right? And that something is a set of individual operations, different blocks, so to speak. You can connect together to make it do things.\u003C\u002Fp>\u003Cp>Now if you create multiple similar flows or you wanna move them to and from different installations, having some sort of way to import them and export them makes sense. Right? Just as a core set of functionality, absolutely. Somebody in the chat too. You know, flow and operation import is the number one thing that breaks when we sync our schemas and database across different projects.\u003C\u002Fp>\u003Cp>So why doesn't it not exist then, or why does it not work properly?\u003C\u002Fp>\u003Cp>Speaker 2: That's a good question. I think, that was before my time. So you're probably better equipped to answer that question.\u003C\u002Fp>\u003Cp>Speaker 1: There's there's a variety of things that happen here. Right? So schema differentials. Right? So if the scheme is different ahead of the flows being imported, if you're watching and monitoring things that don't exist in that import side, problematic, variables, referencing data and information that doesn't exist in one system to another.\u003C\u002Fp>\u003Cp>There's a flows are moving flows, I mean, the APIs are there. We do it all the time with our templating stuff, but we do that in a very controlled way where we're importing and handling the relationals. Because once again, now you've got relational data operations to the flows themselves and all of the data. What I've got a marketplace operation that's not installed in the next environment, there's, you know, there's a there's a great many things that have to be validated and checked, especially on import. Export, not so much, but on import, import gets\u003C\u002Fp>\u003Cp>Speaker 0: Absolutely. Has to be And then another important part of those same edge cases there is what about options that need to be different installation to installation, but, you know, 90% of the rest of it is the same. So for example, a a super simple flow that I oftentimes use as an example is, you know, you make a change and you trigger a build on your CD platform, like a Netlify build or something. That production Netlify URL is different project to project because you're building different websites, but the flow configuration, the trigger, you know, the setup for making the request and all that is the same. Cool.\u003C\u002Fp>\u003Cp>So looking at this discussion here on GitHub, basic example, motivation. Now the motivation makes a lot of sense. That's just basically, you know, what we're what we're talking about here. I think there's no no discussion on, the usefulness here. When it comes to actual implementation, there's a question of, do we tie this to part of the schema snapshot and apply logic?\u003C\u002Fp>\u003Cp>Right? Do we really treat this as it's part of your project's configuration, and we wanna move that between different instances? Or do we treat this more as a kinda more traditional import export like we have for regular collections where you can just say, well, give me all of the operations, and we'll reimport them back in.\u003C\u002Fp>\u003Cp>Speaker 2: To me, personally, it sounds more of the traditional way. Like, the correct way would be the traditional way because flows are not specifically schemas. Right? Like like, they're entries in the table, and that's not the schema, those would be items, and I think we should sync them separately. But yeah.\u003C\u002Fp>\u003Cp>Speaker 0: And immediately, people start chat typing in the chat. I love it. So this is this is a fun discussion that that has come up in the past in in these request review sessions anyways, but we're we're yet to reach the the the silver bullet answer for what is schema or what is configuration. Right? Flows is to me is one of those points where I think 80% of it is configuration of your project.\u003C\u002Fp>\u003Cp>And if you wanna duplicate your project or if you wanna move that from dev to prod or you wanna, you know, use it as a template starting point for something else, you can make the argument that it's definitely part of the same thing that is just one unit of export for the whole configuration of your project. Right? So that's schema and and flows. Last time we were talking about it, you know, it was in the context of roles and permissions, which is a similar but different, you know, part of data, part not. But to to just Josh's point here in the chat, in the data model, you know, the flows are just data in the database.\u003C\u002Fp>\u003Cp>But for the perspective of that end user, they're all part of the application that you're, you know, configuring, right, which makes sense. And in that case, you can definitely make the argument, oh, it's just part of, you know, the the application model settings, schema export, and and apply. But that still raises the question, is it the blanket everything? How do you subset it? Because for the schema right now, it's it's everything.\u003C\u002Fp>\u003Cp>And how do you deal with options that are different? And how do you deal with active versus inactive? Do we deactivate them on import by default just to be safe. Because you could have a cron thing that could go haywire and talk to, you know, 3rd party production systems if you just spin it up in a new thing.\u003C\u002Fp>\u003Cp>Speaker 2: Very valid points. And I and I do see the argument also from Joshua. It kinda it does make sense, right, if you interpret the flows as being part of your infrastructure, then they do kind of feel\u003C\u002Fp>\u003Cp>Speaker 0: schemey. Scheme.\u003C\u002Fp>\u003Cp>Speaker 2: I'm gonna use that. Scheme esque. Yeah. I I can see that argument. Okay.\u003C\u002Fp>\u003Cp>I'm kinda okay. Like in, in the beginning I was like, no, no, no, no. The data, they just stayed up. Those are rules. But now thinking about it, yeah.\u003C\u002Fp>\u003Cp>Speaker 0: It's the rules.\u003C\u002Fp>\u003Cp>Speaker 2: Damn. Interesting. Yeah. Kinda I'm kinda torn. I'm kinda in between.\u003C\u002Fp>\u003Cp>Like, it does it does make a hell of a lot of sense, but,\u003C\u002Fp>\u003Cp>Speaker 0: they got a lot. It's funny because we we keep finding ourselves coming back to the age old question of what is and isn't, project configuration that should be sort of version controlled is kinda the the where that's going. Right? Where you wanna have a file that you version control and that includes a bunch of stuff versus within the data, so you differentiate between the 2. And this is this is kinda what earlier when I said, oh, if I make if I build flow, the whole flow but I want the URL that we're talking to to be different.\u003C\u002Fp>\u003Cp>So that URL should be in the same static file export. Then by definition, it needs to be in this static file. Right? Because otherwise, you have to do both. Is that easier to maintain?\u003C\u002Fp>\u003Cp>Is that worse? Right? But if you're treating it as I wanna have a template that I use to create new projects, but then manage the rest in the project itself, that would still work. If you're treating it as the file becomes the source of truth, now you have to make duplicates of the same, you know, config files to make it work for your different projects. Might be a good thing.\u003C\u002Fp>\u003Cp>Might not be a good thing.\u003C\u002Fp>\u003Cp>Speaker 2: Did we did we actually, I think there's another small thing, related to this with, like, if if I just want to quickly export one specific one or, I had a not sure if this is, like, a slight tangent that we should go down on, but, when I played around with flows, I had the problem of, I created, like, 15 or 20 different ones, because I was, you know, trying it out and, they got automatically generated. And I had this little gripe with our way of displaying them because there was no, possibility to actually select multiple ones and delete multiple ones. And that kinda sounds to me like if we touch this, you know, with selecting them, exporting them, maybe, you know, filtering them so you only export a couple of them, it feels like selecting them would also fall or, like, lend itself to also be done with this issue. Right? But this is kinda a little different.\u003C\u002Fp>\u003Cp>Sorry. Just came to mind.\u003C\u002Fp>\u003Cp>Speaker 0: No. Yeah. You're absolutely right, though. Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: Okay. So Joshua says, for a user story, we use tons of flows in production and development across 2 applications with the following workflow. We run the whole application locally, modify flows, code extensions. Okay. So far so good.\u003C\u002Fp>\u003Cp>Export the schema flows, collections, fields, etcetera etcetera, using a schema sync extension. Okay. So far so good. Commit the schema to version control and build images. Okay.\u003C\u002Fp>\u003Cp>In production, import the schema collections and flows as defined in the image. Alright. The result is that flows are always consistent between development and production, and the production flows are immutable. It makes sense. But this, I I guess this is to the point that I said earlier.\u003C\u002Fp>\u003Cp>Right? With then, you know, if you want to have, like, development flows production flows, then you have to duplicate them and both of them.\u003C\u002Fp>\u003Cp>Speaker 0: Here we go the route in the chat as well saying, you know, what I usually do is try to keep the flows clean by saying, the only thing I export is the values everywhere. And then for the parts where you use different day the data model, there's a sort of settings collection, I imagine, for those flow operations.\u003C\u002Fp>\u003Cp>Speaker 2: So you're actually fetching your needed stuff out of your database, and those fields would be different on your dev machine and the production environment. Correct?\u003C\u002Fp>\u003Cp>Speaker 0: Right. Yeah. It's basically the way to have that sort of 90%, 10%, you know, split I described earlier solved by using a a settings collection for that, which which makes me think, is that something that could be a native thing where at the top of whatever the file export is for flows, there is a set of very tools you can define, reuse within the file. So you have a single file source of truth that then the app, once you're importing it, the requests, what values do you wanna use for these variables to get food for thought. Right?\u003C\u002Fp>\u003Cp>For for those new in this session, it's always about divergently thinking through all of the options, and all of the edge cases and then trying to converge back to, okay, what is something we can do today versus the long term plan? That would be a kinda cool in between, I think, where, you know, in the static file, we have to make sure it's human readable and easy to manage, of course, but you can have you can just describe at the top of the file. Here's a couple of variables that I wanna be installation installation, and then reuse those in your operation settings. The moment you you know, from within Directus where you're using those flows, we can show a a sort of mini form in options that says, okay. What what are those variable values?\u003C\u002Fp>\u003Cp>Okay. Okay. Then I think one other thing in, in close that has been there's there's a couple of I mean, I don't know if it's a bug, but it's kind of annoying to deal with is that right now flows make an operations to tables. And it works right. So if you're using the current APIs, you have an export for operations expect you have an export for flows.\u003C\u002Fp>\u003Cp>Flows are sort of it's it's a one to many type of thing. Right? You have more 1 or more operations per flow. A very normalized SQL data model for that, which in its, you know, theoretical purity is correct. But it does also definitely mean that sometimes if you're importing operations where the flows doesn't exist, you get foreign key constraint problems.\u003C\u002Fp>\u003Cp>Right? Because you're pointing it to a flow that doesn't exist. It also means that when you're exporting it, you end up with 2 different export flows, one for the operations. So that is a bit of an order of insertion interesting thing. It also means that every operation points to the next operation it needs to trigger.\u003C\u002Fp>\u003Cp>So that also means that operation insertion order matters, in that table. And, again, that all makes sense from a SQL database perspective, but definitely not, you know, the most user friendly way to do it. And that is just, you know, the the thing you learn over time in in terms of the data model. The question actually came up just now. I see it in the chat, Joshua.\u003C\u002Fp>\u003Cp>Out of curiosity, you know, why is that a separate table instead of a big JSON field? Concerns about size and speed, script operations. The, it's partially because JSON fields were just not as well supported or existed at all. And at that point, it did become a bit of a, you know, performance concern in some databases. Luckily, you know, I know 2024, that is that is a different, picture now or, you know, even SQLite has support for for JSF, JSON fields, which is fantastic.\u003C\u002Fp>\u003Cp>Concerns about speed has been sort of resolved because of that because now databases can store them, you know, efficiently instead of the large text blob. Size is a concern still a little bit, and that is a similar thing where before it was just a text field, you know, databases were fairly inefficient around storing, you know, unbound blobs of text that would have been JSON but that should also now be way more doable across the SQL vendors. So for what it's worth, I actually think that now, especially now with SQLite support and Oracle DB also being on the JSON train, this is now a new question, right? Should operation just be a nested object on a flow? To which the answer is probably yes, actually, because it's always a tight coupling.\u003C\u002Fp>\u003Cp>You're not really moving an operation from one flow to another And storing the actual flow as a nested tree of operations is more efficient, so we don't have to stitch together that tree from from the c port rows. That was just the thing we couldn't do yet, you know, when we when we shipped flows, but nowadays, for sure. Yeah. So that is that is also an interesting thing, which helps with exporting and importing and helps with the ease of authoring it as a file. Right?\u003C\u002Fp>\u003Cp>Because if we're saying that operations are represented as a nested tree, kinda like, for those who are familiar with it, like CICD pipelines in GitHub or or those tools, it's oftentimes just a YAML file that says it runs step, step, step, step. Right? So a flow could be a very similar syntax in that sense if we store it that way.\u003C\u002Fp>\u003Cp>Speaker 2: Sounds reasonable.\u003C\u002Fp>\u003Cp>Speaker 0: And it's only a tiny method for I can change.\u003C\u002Fp>\u003Cp>Speaker 2: Okay. So we talked a little bit. How does it look like? How could it look like? Why would this feature be not as easy as you think?\u003C\u002Fp>\u003Cp>It's probably our standard go to.\u003C\u002Fp>\u003Cp>Speaker 0: So I think in its purest form, just export everything from one flow and import everything from one flow, either through a duplicate button or a separate endpoint. That is not super tricky. The insertion order when you're doing the import is a bit of technical complexity on the implementer side. But from a user experience perspective, it shouldn't be the end of the world. I think the real questions that we have to prepare ourselves for are around what happens if you have a flow that does something destructive and like a cron.\u003C\u002Fp>\u003Cp>And the moment you import it, it starts wreaking havoc. Right? Do we import them as disabled by default? Is that what you want? If you're in an environment where your prod is just importing it and and otherwise, it's considered immutable, you want them to be active instead of inactive.\u003C\u002Fp>\u003Cp>Right? So how do we do with deal with that? And then around that variableness of you have the flow that's the same, but you have some of the settings that need to be different. Is that something that we have to bake in, or is that something that we say, well, sucks. That just doesn't exist, and you have to duplicate files.\u003C\u002Fp>\u003Cp>I think it's worth at least thinking through those questions even if the initial implementation is just going to be, you know, almost the exact same what was described here, which is just import flows, export flows. Bang. Right?\u003C\u002Fp>\u003Cp>Speaker 2: According to Joshua, having a, destructive crone is just a skill issue. Fair enough. Problem problem fixed. One less problem to worry about. Alright.\u003C\u002Fp>\u003Cp>And, yeah, okay. So so the so the template ability, it it really does sound like a mustache template kinda thing. But, like, I'm I mean, we had a similar thing before, right, with, like, the script operation, for example, where it pulls in, like, your environment. So technically, the environment variables could be different. So you can actually have differing logic inside of them.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And we do allow you know, in every operation, we allow you to use a value out of a previous step. So using, you know, the the suggestion that we set as a settings table is is a very good alternative, I think. Honestly, it makes a lot of sense. It makes a lot of sense.\u003C\u002Fp>\u003Cp>But that is something that we have to just say, okay. If that is our recommended approach, then let's document it as such, because we know it is a I I I mean, of course, we haven't done the the full surveys and everything else, but I would assume that this is a fairly common use case where, you know, the the main use story here is you have a dev environment, you export it so you have a single source of truth that's version controlled, and then you use that to power your production instance. Right? So you use the UI and everything else to configure it, and then you use the code as your immutable source of truth for prod. That's that's sort of the the first user story.\u003C\u002Fp>\u003Cp>And I think the second one is really that you have the export as a templated starting point or templated source of truth for multiple projects that are all in prod. Right? So either it is a starting point where if you spin up a new, you know, templated, the, event website or something and they're all the same, so you wanna use the same kickoff point. You have multiple production instances that use the same config. Right?\u003C\u002Fp>\u003Cp>That I think those are basically the main to, use cases for for this. Mhmm. And I think in both cases, there's a case to be made for, you know, template ability or at least the the variable, settings, which, again, table.\u003C\u002Fp>\u003Cp>Speaker 2: Table does sound pretty good. Pretty pretty reasonable. And similar to what you said, Rijk, earlier, or Jonathan rather, I think, the flows, according to Joshua, you know, if you import something, you could be referencing some tables that no longer exist. So how do we deal with that? Error messages, error pop ups, aborting, disabling the flow and just, you\u003C\u002Fp>\u003Cp>Speaker 0: know I think there's also a good data modeling question to that to be honest. I think for operations right now those settings that hold a a field or a collection name, those are basically just a string. Right? And then we, one once the operation runs, it uses that string to try to read data from those tables and whatnot. I think the only solution here is that we treat those as a separate data type.\u003C\u002Fp>\u003Cp>So instead of saying it's a string, we say it has to be a collection reference. And therefore, the moment you do an export or import, Directus as a platform can recognize what those settings are supposed to be and validate if they are valid paths. Yes or no. Right? So I I think the the the longer term answer to this question would be to treat those as different data types where it's like, okay, collection reference, field reference.\u003C\u002Fp>\u003Cp>So therefore, we can validate it on import or export or both, and almost do like a if you ever see it in in Word or Pages when you open the document, somebody had a bunch of custom fonts and it shows, you know, cannot use cannot find these fonts. Choose what you wanna use instead. That would be kinda the only way I can imagine that to work. Yeah? Mhmm.\u003C\u002Fp>\u003Cp>Speaker 2: I mean, if we, yeah, if we treat, you know, the, flows as part of your schema or your your infrastructure, your application, and if then an imported flow does not work because of, you know, like an an unreferenced or wrongly referenced thing in an operation, then technically we should reject that whole import because your entire application becomes inconsistent or can can could become inconsistent. So think, like, if we really consider it part of your application and your whole infrastructure, then we just have to reject everything if something goes wrong.\u003C\u002Fp>\u003Cp>Speaker 0: Hans makes a good point here in, in the chat saying, you know, same with role IDs in the permission settings. Effectively, what we're talking about is foreign key constraints, but in JSON, right, where it's like you have a a blob of settings that is different and unique to an operation type because every operation is different and we don't have like a we don't have individual tables for each of the operation types or something where that'd be insane. But what we're talking about here is effectively, how do we do forward key constraints within an operation here? Right? And exactly everything comes back to cache invalidation, naming things, and validating and structure data formats.\u003C\u002Fp>\u003Cp>That's great. Yeah. But some sort of way, you know, in the operation value, if we can read it statically, that'd be great, you know, where you can look at an operation settings value and just be recognize what are supposed to be form keys. It could be some sort of unique value syntax, although that feels a little proprietary. It could be, you know, that the operation defines, a sort of JSON schema type of thing that we know what that unstructured data type is supposed to be so we can validate it.\u003C\u002Fp>\u003Cp>But, yeah, at the end of the day, what we're talking about is really much foreign key constraints for, operational layout settings and everything else.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Validation. Just to because I got curious, like, I'm, I think, just to getting back to the import order, like, please correct me if I'm wrong. So I think, if I remember correctly, the flow, references the first operation, and then each operation references the next operation. So the order of operate the the order of import would have to start with the first operate no.\u003C\u002Fp>\u003Cp>Because then you can't access you can't reference the second one. Oh, okay. So you have to do it backwards. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Yeah. Yeah. Exactly.\u003C\u002Fp>\u003Cp>Speaker 2: Exactly. Exactly.\u003C\u002Fp>\u003Cp>Speaker 0: I'm still the father\u003C\u002Fp>\u003Cp>Speaker 2: to change.\u003C\u002Fp>\u003Cp>Speaker 0: Go on. It's the other way, bro.\u003C\u002Fp>\u003Cp>Speaker 2: Teacher, that's wrong. No. Right. I can see you have to step step through it once and then just pop off the stack and then go backwards. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Which is also why, you know, I mentioned if we save it as a nested object tree as is and just treat it as one big blob, right, instead of a semi structured thing, that that simplifies export import, like, tremendously, really, because we don't have to do that. We don't have to care about, insertion order because it's all nested on the flow. So, therefore, you don't have those foreign keys pointing back and forth. And a resolve and a reject just becomes a nested object instead of, what you call it, reference to a different, thing in the flat list. Right?\u003C\u002Fp>\u003Cp>So that simplifies quite a bit, in in that sense. It's a bit of a break and change for tooling that exists against the operations endpoint, but it would make this a lot a lot less annoying. And I think it also solves one other bug. We we've had a bug in flows where people have run-in into where if you're editing a flow, and I think it's you disconnect operation, then you try to reconnect it somewhere else\u003C\u002Fp>\u003Cp>Speaker 1: Right.\u003C\u002Fp>\u003Cp>Speaker 0: You get that error that is, like, cannot 4 giga straight. Right? Because you're, Yep.\u003C\u002Fp>\u003Cp>Speaker 1: You have to\u003C\u002Fp>\u003Cp>Speaker 0: disconnect it. You're dealing with 4 giga\u003C\u002Fp>\u003Cp>Speaker 1: from reconnect it in the order that you want. Yeah. It's a Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Exactly. And that is because you're trying to change those foreign key and how they point to each other in a way that is database technically speaking.\u003C\u002Fp>\u003Cp>Speaker 1: We have to answer that question for clients on\u003C\u002Fp>\u003Cp>Speaker 0: probably almost It's it's a it's theoretically correct. It's also annoying as hell. Yeah. It's one of those\u003C\u002Fp>\u003Cp>Speaker 2: Is neck beard correct? Actually actually, it is correct.\u003C\u002Fp>\u003Cp>Speaker 0: It is. Pure. It's database pure. Suck\u003C\u002Fp>\u003Cp>Speaker 2: it. Yeah. Oh, asks, are we talking about creating custom operations from the UI? No. We are actually talking about importing and exporting flows.\u003C\u002Fp>\u003Cp>So, for example, if you export your current schema and import your schema into your new environment, Should flows be included, how should how would that look like, etcetera etcetera.\u003C\u002Fp>\u003Cp>Speaker 1: So the template CLI utility that Alex and Bryant and the team put together, we migrate everything including flows. But we have full control over everything. It it's exporting content and schema and everything else comes back in in the appropriate order. So your schema, your, you know, updates and things that need to exist before flows get created all happens as part of that kind of operation steps through the CLI. So configuration as code, kind of the general thoughts that we're working towards there will help with this generally, but I I was taking notes here.\u003C\u002Fp>\u003Cp>I saw some of the comments over here. If you're updating an existing flow, Well, if you're updating something in production, flow migration is now gonna have to be maintenance window for anybody doing that. Right? Because if you've got active flows, you've got crons, you've got other things that are going on, if you suddenly come in and hammer a flow that made changes, you're gonna cause issues. You can break, you can lose data, cause problems.\u003C\u002Fp>\u003Cp>Transactional processing and other things can be impacted with updating an existing flow. So because currently, you know, our practice with the CLI tool is we delete all the operations and recreate them in order to avoid any kind of, you know, what changed or didn't change just from a complexity standpoint. But those are things that we're gonna have to consider as well as when you're migrating a flow. Are you updating the existing flow, or are you gonna delete and recreate?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. But,\u003C\u002Fp>\u003Cp>Speaker 1: you know, from an operational standpoint, my my alerts go off from a security risk perspective of if I'm using Flows for invoice processing or I'm using Flows as part of a payment process, which we have clients doing, And we're suddenly gonna, you know, whack a flow and recreate it, or we're gonna update it in some way. What are the operational impacts and risks of doing that? And they've gone crazy in the chat again.\u003C\u002Fp>\u003Cp>Speaker 0: To Hamza's point, that's kind of always a problem with with any sort of data migration. Right? If you import anything new into a production environment, you gotta make sure that you know what you're importing. That's that's the name of the game. That is also why, you know, the static file route in between is is what we're all talking about here.\u003C\u002Fp>\u003Cp>So that becomes your source of truth, and that is version controlled so you can see when it was updated by who. You can have some, you know, review processes in place to make sure that it has needs to have sign off, all that kind of stuff. But, yeah, it is technically always just a problem with any sort of data migration. If you have a settings table and import whatever on top of it, then it also breaks. Thinking about the settings table a little bit more, Are the settings always tied to a flow or is it more common to have settings that are global to your whole project and then happen to be reused in the flow?\u003C\u002Fp>\u003Cp>The reason why I'm saying that is in in a flow execution, we have that data object. Right? So that holds the all the data that's available in the operations and in the flow. One of the fields in there is that dollar sign trigger. That is just the information of the flow itself.\u003C\u002Fp>\u003Cp>Like, what caused it to trigger? I figured we could just have an additional flag in there for any sort of custom data that you wanna just have in that flow global, Right? That you can then reuse in those operations with the existing tools. So therefore, you can make a flow field, flow setting, Netlify URL and that just is becomes one of those data things that you can in in your request operation, you can use the parenthesis the the curly brackets, to reference back to your global flow data. Right?\u003C\u002Fp>\u003Cp>That could be a a sort of alternative to a settings table, but it would be unique to each individual flow. Yeah. Exactly what Joshua is saying right now. It's basically specific flow environment variables. Right?\u003C\u002Fp>\u003Cp>It would be, you know, environments in the, just that flow. And, yeah, you could theoretically already do that. You could use script operations or you could use the the JSON operation to just return, you know, some static data and then use that elsewhere. That's very true. Very true.\u003C\u002Fp>\u003Cp>But it could be a native thing. Then when you export and import it, you just have it as a sort of flow level thing, instead of as a separate operation.\u003C\u002Fp>\u003Cp>Speaker 2: Oh, I mean yeah. There's definitely both, both options. Like, having a global thing, is very useful for general stuff, like, even general info between operations, like a brand name, a URL that you reuse in between different actions. But I can also see, you know, the usefulness of tying something specifically to one, thing just for one thing. Sounds pretty good.\u003C\u002Fp>\u003Cp>I guess the the the, the actual question is just, okay, how do you manage that in a good way? You know, like, how do we make it pretty? How do we make it a good UX? But I yeah. It it makes sense from,\u003C\u002Fp>\u003Cp>Speaker 0: and One one I know Brian and Kevin will yell at me for if I don't bring it up. How do you save it encrypted? Like, some of those settings could very well be, you know, an API token or or things like that. Right? So how do you make sure that that stays secure as well?\u003C\u002Fp>\u003Cp>Speaker 1: Yep. That's why I I I think traditionally, most people seen here end up creating a custom table, that's an admin accessible only table. It's not given any permissions for anybody else. You can then store hashed keys. You can store data and values that are masked, only accessible to the administrators kinds of things.\u003C\u002Fp>\u003Cp>But I, you know, I've run into this. I actually had this report just a, like, a default language as a filter right across a flow. I was doing a bunch of flow operations and they were nested translations, and I wanted to be able to just say, oh, I only want a specific language. And then I was and you can do things like it's it's simple enough to do a run script that just exports the value. So now you've got that variable in memory, right, in in the data data payloads.\u003C\u002Fp>\u003Cp>But Right.\u003C\u002Fp>\u003Cp>Speaker 0: So That's that's where it gets that's oh, here comes another site engine. That's the fun thing about these episodes, man. I thought this is gonna be a 10 minute topic, and here we are 45 minutes in, and I just realized something new. When it comes to secret values like that, we have to store them encrypted, not hashed because we have to be able to decrypt them for use in the actual operation. Right?\u003C\u002Fp>\u003Cp>So when you make a request and you have to include some sort of token, it needs to be the original token, not the encrypted version. But to the chat's question here from Joshua again, you've been killing all of these questions. Thank you for them. You know, who's allowed to see the config? Right?\u003C\u002Fp>\u003Cp>Then, well, I'd say for the right UX at this, you can insert them once, and then that's it. Right? We don't show them again. But at the exact same time, you can then use a run script operation, return them, and then there they are. Right?\u003C\u002Fp>\u003Cp>So it's it's just a bit of a fake fake sense of security. They are encrypted into the database to make sure that that stays as secure as we can, so it will never be I mean, that's that's kinda this is kinda the crux of it. I it will never be exported as is, but at the same time, you know, does that matter? Even when you're doing it, it's not a closed closed.\u003C\u002Fp>\u003Cp>Speaker 2: Right? Yeah. Yeah. Especially since flows are, at least at least right now, just, an admin exclusive thing. So we kinda you know, like, security schmecurity.\u003C\u002Fp>\u003Cp>You know? Like, if you're already an admin, you you have different problems. Like, if your attacker is already an admin, your flow keys are the least of your worries, basically.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yes. No. Yes. No.\u003C\u002Fp>\u003Cp>Yes. No. Maybe. It's it's tricky in that sense. It's it's weird, though, when you think about it.\u003C\u002Fp>\u003Cp>And this is the same when you're talking from a platform team perspective, this is the same problem you're seeing on, you know, AWS secrets manager or d o environment variables or something like that. If you have an encrypted value and you save it, you then, you know, s h into your container, you do print f and there they are. Right? It's like they're stored as secure as possible, but then in your running process, you can just print the environment and everything is right there. But you gotta figure out what is what is the right move.\u003C\u002Fp>\u003Cp>Right? Because it can you have read only access to flows where they're hidden? But then if you have update access now, by definition, you have a way to expose them. Right? Is there a way is there a different security level where there should be the the sort of admins that have access to secure stuff and admins that can edit flows, but they're not the same thing?\u003C\u002Fp>\u003Cp>That that's a different question. Yeah. I\u003C\u002Fp>\u003Cp>Speaker 2: mean, if if we go down, you know, the the settings table route, then we already profit from our, authentication, authorization, system. So, you know, you you can already then assign oh, exclusively these people are allowed to read them, look at them, and stuff like that. So You don't need to plan out every case ahead of time. Well, then you're you're in the wrong stream, my friend. This this is what we do.\u003C\u002Fp>\u003Cp>Speaker 0: And again, this is this is the divergent part of all this. Right? This is luckily Yeah. A lot of these things are not blockers to at least get to the minimum viable, which is let us explore these damn things.\u003C\u002Fp>\u003Cp>Speaker 2: But Right.\u003C\u002Fp>\u003Cp>Speaker 0: These these are the things that people will, you know, use it for and maybe use it wrongly and then maybe expose themselves to massive headaches and issues down the line. So I do I do wanna at least be aware of the types of things that we might see in the future. So we can either prevent it in code, make it better, make it make it better down the line or document it in a way or just warn people about some of the dangers that they might hit. Right? It's, it's it's fun that you mentioned it, but at the same time, we've seen people do shit before.\u003C\u002Fp>\u003Cp>It's like, make a cron job that runs every second and put something in the table. Table blows up. Whose fault is that? Right? Did we did we have to do something to prevent that?\u003C\u002Fp>\u003Cp>Is that a user error? That who's who's responsible? Exactly. Featurex activity, see revisions. Working working on that, by the way.\u003C\u002Fp>\u003Cp>Unrelated for this call.\u003C\u002Fp>\u003Cp>Speaker 2: Don't tell. Don't tell. Hooray. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: I don't want the sidetracked too much, but, yeah, we have the, retention settings for that coming, shipping fairly soon. Thank god. Yes, Amar. If you want to.\u003C\u002Fp>\u003Cp>Speaker 2: No. I\u003C\u002Fp>\u003Cp>Speaker 0: don't wanna get in trouble with HR. I don't think that's a good idea.\u003C\u002Fp>\u003Cp>Speaker 2: Goosebumps. Oh, no. Okay. So, just just keeping the time, in mind, we don't have that much time left. So maybe we should start to kinda converge now a little bit, You know, yeah, just to, get back on track a little bit.\u003C\u002Fp>\u003Cp>Oh, wow. And then the whole essay drops\u003C\u002Fp>\u003Cp>Speaker 0: from Yeah. There goes the next 10 minutes.\u003C\u002Fp>\u003Cp>Speaker 2: Oh, wow. Should we read that aloud? Are there bad words in it? Looks good. Okay.\u003C\u002Fp>\u003Cp>In my humble opinion, the whole edit complexity of environment migration I'm using directdis comes from the yaw application becoming data itself. Alright. While not using Directus, you can run different versions of the application side by side, and it's easy to switch version which version is running because the structure is defined within the application. Wildirectors, most of you. My guy, there there's not a single dot in that sentence.\u003C\u002Fp>\u003Cp>Oh my god. That's that's one sentence.\u003C\u002Fp>\u003Cp>Speaker 0: Don't throw too much shade. And it the the gist of it is because all the configuration is in the database instead of in code, therefore, you have migrations back and forth prompts. Right? And to Joshua's point, that is definitely a configuration as code discussion, which I think we had a couple episodes ago, actually, which is a great segue. Directs.i0\u002Ftv under request review.\u003C\u002Fp>\u003Cp>Highly recommend it. The the the 5 second summary of that discussion is really around there's multiple use cases. Right? There's a lot of people that prefer configuring everything from the UX and UI and then making an export. So by definition, it is in the database.\u003C\u002Fp>\u003Cp>Right? It's it's a mixed environment where there needs to be a two way binding into configuration. We don't wanna end up in a sort of again, the choices are different different use cases, different type of people. I'd I'd rather not end up with a sanity slash strappy type of environment where you have to pull things locally to code, you know, your settings and then redeploy it. That is just not quite the vision that we have for, you know, the ease of use and the user experience for this.\u003C\u002Fp>\u003Cp>Also, I know from some inside chatter that they're trying to get out of that as well. So this is this is a bit of a two way binding discussion.\u003C\u002Fp>\u003Cp>Speaker 2: Did you did you see They updated their comment and broke up the paragraph into multiple sentences now.\u003C\u002Fp>\u003Cp>Speaker 0: Danielle, look at what you've done. I think this is called cyberbullies. We don't condone that here.\u003C\u002Fp>\u003Cp>Speaker 2: Oh, no. Thank you for thank you for the, not not, what is it? Thank you for your cooperation, Wolfolas. Thanks for being here. Thanks for your message.\u003C\u002Fp>\u003Cp>We do appreciate it.\u003C\u002Fp>\u003Cp>Speaker 1: I'm not sure how you say it, but Wolf Wolf of us is an awesome contributor. He's been an incredible community member and contributor and helps out across everywhere across the platform. So\u003C\u002Fp>\u003Cp>Speaker 0: we are\u003C\u002Fp>\u003Cp>Speaker 2: It turns out.\u003C\u002Fp>\u003Cp>Speaker 0: Anyhoo, to your point then, converging it down to okay. What can we do? I think the the question that I do have is, do we wanna look into storing this as a nested JSON blob versus a separate table? Right? It would be a bit of a breaking change, but it simplifies both the output files and it simplifies the, the way to import it for us quite a bit.\u003C\u002Fp>\u003Cp>And it also solves that cannot resolve foreign key type thing bug in the same time. Right? The alternative is that we don't do that, but we avoid that breaking change in the operations endpoint with the downside that then for both export and import, we have to make sure that the whole nested tree is included at all times. And then during import, we do it in the correct insertion order. Right?\u003C\u002Fp>\u003Cp>Gut feeling wise, I'm kinda leaning towards let's blop it all as a nest of blob because it simplifies things. At the same time, couple downsides of that is that I think we'd lose the ability to choose what fields are returned from operations. I don't know if that matters as much if you're dealing with flows. Theoretically speaking, most of the databases now should support field selection for JSON. But seeing that we're talking about a theoretical infinitely deep object, that might be tricky.\u003C\u002Fp>\u003Cp>Right? Or we have to filter it down in in post after we got the data back.\u003C\u002Fp>\u003Cp>Speaker 2: Quick quick tangent. Can SQLite do that? I'm not sure. I don't think so. Right?\u003C\u002Fp>\u003Cp>Speaker 0: I think that update came earlier this year that they have some sort of SQL path, JSON path selection. Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: They they've\u003C\u002Fp>\u003Cp>Speaker 1: got some JSON path.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It's fairly recent. That's also one of the reasons why I missed we couldn't really do this earlier. Right?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Oh, that's nice. Okay. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So the lights are getting a lot of attention because you've got companies like Terso and others that are using this distributed edge file based databasing, caching kinds of things. So there's some there's a lot of work going on in that space and SQLite's gotten some pretty good attention.\u003C\u002Fp>\u003Cp>Speaker 0: But, I\u003C\u002Fp>\u003Cp>Speaker 1: mean, vendor variation, I think, as we move towards hopefully, as we move towards Duris, you know, whether or not that vendor supports it, then we can decide sourcing wise how we handle that. But for the main\u003C\u002Fp>\u003Cp>Speaker 2: I'm a big proponent of s Eskelet. Like, if if we if I do something, it it I wanted it for an SQLite, please. Okay. The end of this tangent. I'm sorry to I just I was curious.\u003C\u002Fp>\u003Cp>Yeah. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: So so I think because, you know, the nested JSON stuff is something we haven't historically done because we couldn't really do that cross database vendor properly support it. It is something that probably comes with new issues that we don't know about yet. Right? So oh, yeah. Somebody actually mentioned it here too.\u003C\u002Fp>\u003Cp>It was like, Joshua again. Get a shout out, Josh. Stick with the current structure because making breaking breaking changes to the operation config will get to be a huge problem if using nested JSON objects. It's gonna be easier if you have a single row that changes. Just fair enough.\u003C\u002Fp>\u003Cp>And also see support for older databases and vendors that don't have JSON support. Also true.\u003C\u002Fp>\u003Cp>Speaker 2: Although,\u003C\u002Fp>\u003Cp>Speaker 0: I I wanna say that the ones that don't have an end of life I guess SQLite, the previous version, not yet, of course. Yeah. So and and, also, obviously, the big breaking change. People have been exporting, importing things through the APIs. The separate tools have been made.\u003C\u002Fp>\u003Cp>We saw that BCC schema sync shout out earlier. So we wanna make sure that that is not a huge freaking change either, right, where we just completely wreck any of those existing things. So sticking with the current structure probably makes the most sense. And then that raises the question around, you know, data integrity and import order, but that is a technical problem to solve that we just have to do during import. So that does mean that it'll probably be a separate way that we, do this than than compared to your usual export import just because order matters and that integrity matters.\u003C\u002Fp>\u003Cp>No. That's fine.\u003C\u002Fp>\u003Cp>Speaker 2: That should be really doable. You know? Walk the tree once and then just pop off from the back.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And and the age old question, is this part of the schema export? Yes or no?\u003C\u002Fp>\u003Cp>Speaker 2: Oh, right. The the gonna\u003C\u002Fp>\u003Cp>Speaker 0: be a discussion for a different day, which is discussion we've had before around roles. And the yeah. But Joshua basically is concluding as well. We just need some schema export flags, which is, like, what do we include? What do we not include?\u003C\u002Fp>\u003Cp>This this this is the discussion we had exactly the same around roles. Do you wanna include roles and permissions? Probably. Do you wanna include users? Probably not.\u003C\u002Fp>\u003Cp>Do you wanna include permissions? Probably. Do you wanna include all of the permissions or just the ones that are about your production databases? Probably just the prod ones. How do you filter that down?\u003C\u002Fp>\u003Cp>This is the exact same stuff. Right? Is it all the flows? Is it some of the flows? How do you choose which of the flows are included and which ones aren't?\u003C\u002Fp>\u003Cp>Do we just export everything and then assume that somebody goes into the file and deletes them manually? Lots of more fun questions to be had in this. And for that, I wanna say, subscribe, like, and subscribe.\u003C\u002Fp>\u003Cp>Speaker 1: Nope. I think\u003C\u002Fp>\u003Cp>Speaker 2: Hit the bell. Hit the bell.\u003C\u002Fp>\u003Cp>Speaker 1: Always been.\u003C\u002Fp>\u003Cp>Speaker 0: Hit the bell and make sure you don't miss that episode when we go deep on that.\u003C\u002Fp>\u003Cp>Speaker 1: Now selective import export is gonna be needed for most of these things, I think. And ideally, whether export could export everything, but selective import on the import side, the ability to choose what I'm actually merging into the next iteration is is key.\u003C\u002Fp>\u003Cp>Speaker 2: Right.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, that being said, I've not been muting. I'm muting. I'm muting. I'm muting. And I'm muting.\u003C\u002Fp>\u003Cp>The the doorbell is ringing, so I just wanted to make sure it didn't get too annoying. But that being said, we're at the top of the hour here. There's one more question that just came in. So another quick thought, what about pulling changes instead of pushing them in? Like, a sort of federation type of thing where you link data from multiple direct instances, you pull change from another instance.\u003C\u002Fp>\u003Cp>That sounds like a discussion for another day.\u003C\u002Fp>\u003Cp>Speaker 1: I think we've had that as part of the dual syncing as part of the configuration as code anyway. It is something that we we've we're we're thinking about, on that side of the house, and that's this is part of that as well. Right? So the configuration as code affects this, having utilities that make this easy now, versus, you know\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I I think that two way bind to me always has the file in between. So you push it from dev to file, and you pull it from prod from file. That is kind of the the the one to jump. Anyways, with that being said, thank you all for tuning in.\u003C\u002Fp>\u003Cp>Thank you all for the great questions and ideas in the chat. This episode will be available on Directus TV in the very near future. Shout out to shout out to Nat. Dan, did you make sure there's any any Zingers in here? Do we have the thumbnail?\u003C\u002Fp>\u003Cp>Speaker 2: Oh, that's not quite sure. May may maybe the the technically technically correct, maybe. I'm not sure.\u003C\u002Fp>\u003Cp>Speaker 0: There you go. That's that's the perfect one. That being said, check it out drex. Io\u002Ftv. We'll be doing this again in in the near future.\u003C\u002Fp>","Welcome to another episode of request review. What are we talking about today? We're talking about import and export options for flows. Oh. Now then if he says a like that, it sounds Why don't we just edit then? You know? It should be quick fix. Right? I think that's a great idea. Well, with that being said, thank you so much for watching. Find this episode on of course, I'm kidding. There's there's interesting, as per usual, these episodes, that's why we do them. Some fun edge cases here to consider. First and foremost, for those, you know, out of the loop flows, automation workflows, you can set up, you know, triggers. If somebody saves a thing, somebody hits an endpoint, somebody clicks a button in the app, do something. Right? And that something is a set of individual operations, different blocks, so to speak. You can connect together to make it do things. Now if you create multiple similar flows or you wanna move them to and from different installations, having some sort of way to import them and export them makes sense. Right? Just as a core set of functionality, absolutely. Somebody in the chat too. You know, flow and operation import is the number one thing that breaks when we sync our schemas and database across different projects. So why doesn't it not exist then, or why does it not work properly? That's a good question. I think, that was before my time. So you're probably better equipped to answer that question. There's there's a variety of things that happen here. Right? So schema differentials. Right? So if the scheme is different ahead of the flows being imported, if you're watching and monitoring things that don't exist in that import side, problematic, variables, referencing data and information that doesn't exist in one system to another. There's a flows are moving flows, I mean, the APIs are there. We do it all the time with our templating stuff, but we do that in a very controlled way where we're importing and handling the relationals. Because once again, now you've got relational data operations to the flows themselves and all of the data. What I've got a marketplace operation that's not installed in the next environment, there's, you know, there's a there's a great many things that have to be validated and checked, especially on import. Export, not so much, but on import, import gets Absolutely. Has to be And then another important part of those same edge cases there is what about options that need to be different installation to installation, but, you know, 90% of the rest of it is the same. So for example, a a super simple flow that I oftentimes use as an example is, you know, you make a change and you trigger a build on your CD platform, like a Netlify build or something. That production Netlify URL is different project to project because you're building different websites, but the flow configuration, the trigger, you know, the setup for making the request and all that is the same. Cool. So looking at this discussion here on GitHub, basic example, motivation. Now the motivation makes a lot of sense. That's just basically, you know, what we're what we're talking about here. I think there's no no discussion on, the usefulness here. When it comes to actual implementation, there's a question of, do we tie this to part of the schema snapshot and apply logic? Right? Do we really treat this as it's part of your project's configuration, and we wanna move that between different instances? Or do we treat this more as a kinda more traditional import export like we have for regular collections where you can just say, well, give me all of the operations, and we'll reimport them back in. To me, personally, it sounds more of the traditional way. Like, the correct way would be the traditional way because flows are not specifically schemas. Right? Like like, they're entries in the table, and that's not the schema, those would be items, and I think we should sync them separately. But yeah. And immediately, people start chat typing in the chat. I love it. So this is this is a fun discussion that that has come up in the past in in these request review sessions anyways, but we're we're yet to reach the the the silver bullet answer for what is schema or what is configuration. Right? Flows is to me is one of those points where I think 80% of it is configuration of your project. And if you wanna duplicate your project or if you wanna move that from dev to prod or you wanna, you know, use it as a template starting point for something else, you can make the argument that it's definitely part of the same thing that is just one unit of export for the whole configuration of your project. Right? So that's schema and and flows. Last time we were talking about it, you know, it was in the context of roles and permissions, which is a similar but different, you know, part of data, part not. But to to just Josh's point here in the chat, in the data model, you know, the flows are just data in the database. But for the perspective of that end user, they're all part of the application that you're, you know, configuring, right, which makes sense. And in that case, you can definitely make the argument, oh, it's just part of, you know, the the application model settings, schema export, and and apply. But that still raises the question, is it the blanket everything? How do you subset it? Because for the schema right now, it's it's everything. And how do you deal with options that are different? And how do you deal with active versus inactive? Do we deactivate them on import by default just to be safe. Because you could have a cron thing that could go haywire and talk to, you know, 3rd party production systems if you just spin it up in a new thing. Very valid points. And I and I do see the argument also from Joshua. It kinda it does make sense, right, if you interpret the flows as being part of your infrastructure, then they do kind of feel schemey. Scheme. I'm gonna use that. Scheme esque. Yeah. I I can see that argument. Okay. I'm kinda okay. Like in, in the beginning I was like, no, no, no, no. The data, they just stayed up. Those are rules. But now thinking about it, yeah. It's the rules. Damn. Interesting. Yeah. Kinda I'm kinda torn. I'm kinda in between. Like, it does it does make a hell of a lot of sense, but, they got a lot. It's funny because we we keep finding ourselves coming back to the age old question of what is and isn't, project configuration that should be sort of version controlled is kinda the the where that's going. Right? Where you wanna have a file that you version control and that includes a bunch of stuff versus within the data, so you differentiate between the 2. And this is this is kinda what earlier when I said, oh, if I make if I build flow, the whole flow but I want the URL that we're talking to to be different. So that URL should be in the same static file export. Then by definition, it needs to be in this static file. Right? Because otherwise, you have to do both. Is that easier to maintain? Is that worse? Right? But if you're treating it as I wanna have a template that I use to create new projects, but then manage the rest in the project itself, that would still work. If you're treating it as the file becomes the source of truth, now you have to make duplicates of the same, you know, config files to make it work for your different projects. Might be a good thing. Might not be a good thing. Did we did we actually, I think there's another small thing, related to this with, like, if if I just want to quickly export one specific one or, I had a not sure if this is, like, a slight tangent that we should go down on, but, when I played around with flows, I had the problem of, I created, like, 15 or 20 different ones, because I was, you know, trying it out and, they got automatically generated. And I had this little gripe with our way of displaying them because there was no, possibility to actually select multiple ones and delete multiple ones. And that kinda sounds to me like if we touch this, you know, with selecting them, exporting them, maybe, you know, filtering them so you only export a couple of them, it feels like selecting them would also fall or, like, lend itself to also be done with this issue. Right? But this is kinda a little different. Sorry. Just came to mind. No. Yeah. You're absolutely right, though. Yeah. Okay. So Joshua says, for a user story, we use tons of flows in production and development across 2 applications with the following workflow. We run the whole application locally, modify flows, code extensions. Okay. So far so good. Export the schema flows, collections, fields, etcetera etcetera, using a schema sync extension. Okay. So far so good. Commit the schema to version control and build images. Okay. In production, import the schema collections and flows as defined in the image. Alright. The result is that flows are always consistent between development and production, and the production flows are immutable. It makes sense. But this, I I guess this is to the point that I said earlier. Right? With then, you know, if you want to have, like, development flows production flows, then you have to duplicate them and both of them. Here we go the route in the chat as well saying, you know, what I usually do is try to keep the flows clean by saying, the only thing I export is the values everywhere. And then for the parts where you use different day the data model, there's a sort of settings collection, I imagine, for those flow operations. So you're actually fetching your needed stuff out of your database, and those fields would be different on your dev machine and the production environment. Correct? Right. Yeah. It's basically the way to have that sort of 90%, 10%, you know, split I described earlier solved by using a a settings collection for that, which which makes me think, is that something that could be a native thing where at the top of whatever the file export is for flows, there is a set of very tools you can define, reuse within the file. So you have a single file source of truth that then the app, once you're importing it, the requests, what values do you wanna use for these variables to get food for thought. Right? For for those new in this session, it's always about divergently thinking through all of the options, and all of the edge cases and then trying to converge back to, okay, what is something we can do today versus the long term plan? That would be a kinda cool in between, I think, where, you know, in the static file, we have to make sure it's human readable and easy to manage, of course, but you can have you can just describe at the top of the file. Here's a couple of variables that I wanna be installation installation, and then reuse those in your operation settings. The moment you you know, from within Directus where you're using those flows, we can show a a sort of mini form in options that says, okay. What what are those variable values? Okay. Okay. Then I think one other thing in, in close that has been there's there's a couple of I mean, I don't know if it's a bug, but it's kind of annoying to deal with is that right now flows make an operations to tables. And it works right. So if you're using the current APIs, you have an export for operations expect you have an export for flows. Flows are sort of it's it's a one to many type of thing. Right? You have more 1 or more operations per flow. A very normalized SQL data model for that, which in its, you know, theoretical purity is correct. But it does also definitely mean that sometimes if you're importing operations where the flows doesn't exist, you get foreign key constraint problems. Right? Because you're pointing it to a flow that doesn't exist. It also means that when you're exporting it, you end up with 2 different export flows, one for the operations. So that is a bit of an order of insertion interesting thing. It also means that every operation points to the next operation it needs to trigger. So that also means that operation insertion order matters, in that table. And, again, that all makes sense from a SQL database perspective, but definitely not, you know, the most user friendly way to do it. And that is just, you know, the the thing you learn over time in in terms of the data model. The question actually came up just now. I see it in the chat, Joshua. Out of curiosity, you know, why is that a separate table instead of a big JSON field? Concerns about size and speed, script operations. The, it's partially because JSON fields were just not as well supported or existed at all. And at that point, it did become a bit of a, you know, performance concern in some databases. Luckily, you know, I know 2024, that is that is a different, picture now or, you know, even SQLite has support for for JSF, JSON fields, which is fantastic. Concerns about speed has been sort of resolved because of that because now databases can store them, you know, efficiently instead of the large text blob. Size is a concern still a little bit, and that is a similar thing where before it was just a text field, you know, databases were fairly inefficient around storing, you know, unbound blobs of text that would have been JSON but that should also now be way more doable across the SQL vendors. So for what it's worth, I actually think that now, especially now with SQLite support and Oracle DB also being on the JSON train, this is now a new question, right? Should operation just be a nested object on a flow? To which the answer is probably yes, actually, because it's always a tight coupling. You're not really moving an operation from one flow to another And storing the actual flow as a nested tree of operations is more efficient, so we don't have to stitch together that tree from from the c port rows. That was just the thing we couldn't do yet, you know, when we when we shipped flows, but nowadays, for sure. Yeah. So that is that is also an interesting thing, which helps with exporting and importing and helps with the ease of authoring it as a file. Right? Because if we're saying that operations are represented as a nested tree, kinda like, for those who are familiar with it, like CICD pipelines in GitHub or or those tools, it's oftentimes just a YAML file that says it runs step, step, step, step. Right? So a flow could be a very similar syntax in that sense if we store it that way. Sounds reasonable. And it's only a tiny method for I can change. Okay. So we talked a little bit. How does it look like? How could it look like? Why would this feature be not as easy as you think? It's probably our standard go to. So I think in its purest form, just export everything from one flow and import everything from one flow, either through a duplicate button or a separate endpoint. That is not super tricky. The insertion order when you're doing the import is a bit of technical complexity on the implementer side. But from a user experience perspective, it shouldn't be the end of the world. I think the real questions that we have to prepare ourselves for are around what happens if you have a flow that does something destructive and like a cron. And the moment you import it, it starts wreaking havoc. Right? Do we import them as disabled by default? Is that what you want? If you're in an environment where your prod is just importing it and and otherwise, it's considered immutable, you want them to be active instead of inactive. Right? So how do we do with deal with that? And then around that variableness of you have the flow that's the same, but you have some of the settings that need to be different. Is that something that we have to bake in, or is that something that we say, well, sucks. That just doesn't exist, and you have to duplicate files. I think it's worth at least thinking through those questions even if the initial implementation is just going to be, you know, almost the exact same what was described here, which is just import flows, export flows. Bang. Right? According to Joshua, having a, destructive crone is just a skill issue. Fair enough. Problem problem fixed. One less problem to worry about. Alright. And, yeah, okay. So so the so the template ability, it it really does sound like a mustache template kinda thing. But, like, I'm I mean, we had a similar thing before, right, with, like, the script operation, for example, where it pulls in, like, your environment. So technically, the environment variables could be different. So you can actually have differing logic inside of them. Yeah. And we do allow you know, in every operation, we allow you to use a value out of a previous step. So using, you know, the the suggestion that we set as a settings table is is a very good alternative, I think. Honestly, it makes a lot of sense. It makes a lot of sense. But that is something that we have to just say, okay. If that is our recommended approach, then let's document it as such, because we know it is a I I I mean, of course, we haven't done the the full surveys and everything else, but I would assume that this is a fairly common use case where, you know, the the main use story here is you have a dev environment, you export it so you have a single source of truth that's version controlled, and then you use that to power your production instance. Right? So you use the UI and everything else to configure it, and then you use the code as your immutable source of truth for prod. That's that's sort of the the first user story. And I think the second one is really that you have the export as a templated starting point or templated source of truth for multiple projects that are all in prod. Right? So either it is a starting point where if you spin up a new, you know, templated, the, event website or something and they're all the same, so you wanna use the same kickoff point. You have multiple production instances that use the same config. Right? That I think those are basically the main to, use cases for for this. Mhmm. And I think in both cases, there's a case to be made for, you know, template ability or at least the the variable, settings, which, again, table. Table does sound pretty good. Pretty pretty reasonable. And similar to what you said, Rijk, earlier, or Jonathan rather, I think, the flows, according to Joshua, you know, if you import something, you could be referencing some tables that no longer exist. So how do we deal with that? Error messages, error pop ups, aborting, disabling the flow and just, you know I think there's also a good data modeling question to that to be honest. I think for operations right now those settings that hold a a field or a collection name, those are basically just a string. Right? And then we, one once the operation runs, it uses that string to try to read data from those tables and whatnot. I think the only solution here is that we treat those as a separate data type. So instead of saying it's a string, we say it has to be a collection reference. And therefore, the moment you do an export or import, Directus as a platform can recognize what those settings are supposed to be and validate if they are valid paths. Yes or no. Right? So I I think the the the longer term answer to this question would be to treat those as different data types where it's like, okay, collection reference, field reference. So therefore, we can validate it on import or export or both, and almost do like a if you ever see it in in Word or Pages when you open the document, somebody had a bunch of custom fonts and it shows, you know, cannot use cannot find these fonts. Choose what you wanna use instead. That would be kinda the only way I can imagine that to work. Yeah? Mhmm. I mean, if we, yeah, if we treat, you know, the, flows as part of your schema or your your infrastructure, your application, and if then an imported flow does not work because of, you know, like an an unreferenced or wrongly referenced thing in an operation, then technically we should reject that whole import because your entire application becomes inconsistent or can can could become inconsistent. So think, like, if we really consider it part of your application and your whole infrastructure, then we just have to reject everything if something goes wrong. Hans makes a good point here in, in the chat saying, you know, same with role IDs in the permission settings. Effectively, what we're talking about is foreign key constraints, but in JSON, right, where it's like you have a a blob of settings that is different and unique to an operation type because every operation is different and we don't have like a we don't have individual tables for each of the operation types or something where that'd be insane. But what we're talking about here is effectively, how do we do forward key constraints within an operation here? Right? And exactly everything comes back to cache invalidation, naming things, and validating and structure data formats. That's great. Yeah. But some sort of way, you know, in the operation value, if we can read it statically, that'd be great, you know, where you can look at an operation settings value and just be recognize what are supposed to be form keys. It could be some sort of unique value syntax, although that feels a little proprietary. It could be, you know, that the operation defines, a sort of JSON schema type of thing that we know what that unstructured data type is supposed to be so we can validate it. But, yeah, at the end of the day, what we're talking about is really much foreign key constraints for, operational layout settings and everything else. Yeah. Validation. Just to because I got curious, like, I'm, I think, just to getting back to the import order, like, please correct me if I'm wrong. So I think, if I remember correctly, the flow, references the first operation, and then each operation references the next operation. So the order of operate the the order of import would have to start with the first operate no. Because then you can't access you can't reference the second one. Oh, okay. So you have to do it backwards. Yeah. Yeah. Yeah. Yeah. Yeah. Exactly. Exactly. Exactly. I'm still the father to change. Go on. It's the other way, bro. Teacher, that's wrong. No. Right. I can see you have to step step through it once and then just pop off the stack and then go backwards. Okay. Which is also why, you know, I mentioned if we save it as a nested object tree as is and just treat it as one big blob, right, instead of a semi structured thing, that that simplifies export import, like, tremendously, really, because we don't have to do that. We don't have to care about, insertion order because it's all nested on the flow. So, therefore, you don't have those foreign keys pointing back and forth. And a resolve and a reject just becomes a nested object instead of, what you call it, reference to a different, thing in the flat list. Right? So that simplifies quite a bit, in in that sense. It's a bit of a break and change for tooling that exists against the operations endpoint, but it would make this a lot a lot less annoying. And I think it also solves one other bug. We we've had a bug in flows where people have run-in into where if you're editing a flow, and I think it's you disconnect operation, then you try to reconnect it somewhere else Right. You get that error that is, like, cannot 4 giga straight. Right? Because you're, Yep. You have to disconnect it. You're dealing with 4 giga from reconnect it in the order that you want. Yeah. It's a Yeah. Exactly. And that is because you're trying to change those foreign key and how they point to each other in a way that is database technically speaking. We have to answer that question for clients on probably almost It's it's a it's theoretically correct. It's also annoying as hell. Yeah. It's one of those Is neck beard correct? Actually actually, it is correct. It is. Pure. It's database pure. Suck it. Yeah. Oh, asks, are we talking about creating custom operations from the UI? No. We are actually talking about importing and exporting flows. So, for example, if you export your current schema and import your schema into your new environment, Should flows be included, how should how would that look like, etcetera etcetera. So the template CLI utility that Alex and Bryant and the team put together, we migrate everything including flows. But we have full control over everything. It it's exporting content and schema and everything else comes back in in the appropriate order. So your schema, your, you know, updates and things that need to exist before flows get created all happens as part of that kind of operation steps through the CLI. So configuration as code, kind of the general thoughts that we're working towards there will help with this generally, but I I was taking notes here. I saw some of the comments over here. If you're updating an existing flow, Well, if you're updating something in production, flow migration is now gonna have to be maintenance window for anybody doing that. Right? Because if you've got active flows, you've got crons, you've got other things that are going on, if you suddenly come in and hammer a flow that made changes, you're gonna cause issues. You can break, you can lose data, cause problems. Transactional processing and other things can be impacted with updating an existing flow. So because currently, you know, our practice with the CLI tool is we delete all the operations and recreate them in order to avoid any kind of, you know, what changed or didn't change just from a complexity standpoint. But those are things that we're gonna have to consider as well as when you're migrating a flow. Are you updating the existing flow, or are you gonna delete and recreate? Yeah. But, you know, from an operational standpoint, my my alerts go off from a security risk perspective of if I'm using Flows for invoice processing or I'm using Flows as part of a payment process, which we have clients doing, And we're suddenly gonna, you know, whack a flow and recreate it, or we're gonna update it in some way. What are the operational impacts and risks of doing that? And they've gone crazy in the chat again. To Hamza's point, that's kind of always a problem with with any sort of data migration. Right? If you import anything new into a production environment, you gotta make sure that you know what you're importing. That's that's the name of the game. That is also why, you know, the static file route in between is is what we're all talking about here. So that becomes your source of truth, and that is version controlled so you can see when it was updated by who. You can have some, you know, review processes in place to make sure that it has needs to have sign off, all that kind of stuff. But, yeah, it is technically always just a problem with any sort of data migration. If you have a settings table and import whatever on top of it, then it also breaks. Thinking about the settings table a little bit more, Are the settings always tied to a flow or is it more common to have settings that are global to your whole project and then happen to be reused in the flow? The reason why I'm saying that is in in a flow execution, we have that data object. Right? So that holds the all the data that's available in the operations and in the flow. One of the fields in there is that dollar sign trigger. That is just the information of the flow itself. Like, what caused it to trigger? I figured we could just have an additional flag in there for any sort of custom data that you wanna just have in that flow global, Right? That you can then reuse in those operations with the existing tools. So therefore, you can make a flow field, flow setting, Netlify URL and that just is becomes one of those data things that you can in in your request operation, you can use the parenthesis the the curly brackets, to reference back to your global flow data. Right? That could be a a sort of alternative to a settings table, but it would be unique to each individual flow. Yeah. Exactly what Joshua is saying right now. It's basically specific flow environment variables. Right? It would be, you know, environments in the, just that flow. And, yeah, you could theoretically already do that. You could use script operations or you could use the the JSON operation to just return, you know, some static data and then use that elsewhere. That's very true. Very true. But it could be a native thing. Then when you export and import it, you just have it as a sort of flow level thing, instead of as a separate operation. Oh, I mean yeah. There's definitely both, both options. Like, having a global thing, is very useful for general stuff, like, even general info between operations, like a brand name, a URL that you reuse in between different actions. But I can also see, you know, the usefulness of tying something specifically to one, thing just for one thing. Sounds pretty good. I guess the the the, the actual question is just, okay, how do you manage that in a good way? You know, like, how do we make it pretty? How do we make it a good UX? But I yeah. It it makes sense from, and One one I know Brian and Kevin will yell at me for if I don't bring it up. How do you save it encrypted? Like, some of those settings could very well be, you know, an API token or or things like that. Right? So how do you make sure that that stays secure as well? Yep. That's why I I I think traditionally, most people seen here end up creating a custom table, that's an admin accessible only table. It's not given any permissions for anybody else. You can then store hashed keys. You can store data and values that are masked, only accessible to the administrators kinds of things. But I, you know, I've run into this. I actually had this report just a, like, a default language as a filter right across a flow. I was doing a bunch of flow operations and they were nested translations, and I wanted to be able to just say, oh, I only want a specific language. And then I was and you can do things like it's it's simple enough to do a run script that just exports the value. So now you've got that variable in memory, right, in in the data data payloads. But Right. So That's that's where it gets that's oh, here comes another site engine. That's the fun thing about these episodes, man. I thought this is gonna be a 10 minute topic, and here we are 45 minutes in, and I just realized something new. When it comes to secret values like that, we have to store them encrypted, not hashed because we have to be able to decrypt them for use in the actual operation. Right? So when you make a request and you have to include some sort of token, it needs to be the original token, not the encrypted version. But to the chat's question here from Joshua again, you've been killing all of these questions. Thank you for them. You know, who's allowed to see the config? Right? Then, well, I'd say for the right UX at this, you can insert them once, and then that's it. Right? We don't show them again. But at the exact same time, you can then use a run script operation, return them, and then there they are. Right? So it's it's just a bit of a fake fake sense of security. They are encrypted into the database to make sure that that stays as secure as we can, so it will never be I mean, that's that's kinda this is kinda the crux of it. I it will never be exported as is, but at the same time, you know, does that matter? Even when you're doing it, it's not a closed closed. Right? Yeah. Yeah. Especially since flows are, at least at least right now, just, an admin exclusive thing. So we kinda you know, like, security schmecurity. You know? Like, if you're already an admin, you you have different problems. Like, if your attacker is already an admin, your flow keys are the least of your worries, basically. Yeah. Yes. No. Yes. No. Yes. No. Maybe. It's it's tricky in that sense. It's it's weird, though, when you think about it. And this is the same when you're talking from a platform team perspective, this is the same problem you're seeing on, you know, AWS secrets manager or d o environment variables or something like that. If you have an encrypted value and you save it, you then, you know, s h into your container, you do print f and there they are. Right? It's like they're stored as secure as possible, but then in your running process, you can just print the environment and everything is right there. But you gotta figure out what is what is the right move. Right? Because it can you have read only access to flows where they're hidden? But then if you have update access now, by definition, you have a way to expose them. Right? Is there a way is there a different security level where there should be the the sort of admins that have access to secure stuff and admins that can edit flows, but they're not the same thing? That that's a different question. Yeah. I mean, if if we go down, you know, the the settings table route, then we already profit from our, authentication, authorization, system. So, you know, you you can already then assign oh, exclusively these people are allowed to read them, look at them, and stuff like that. So You don't need to plan out every case ahead of time. Well, then you're you're in the wrong stream, my friend. This this is what we do. And again, this is this is the divergent part of all this. Right? This is luckily Yeah. A lot of these things are not blockers to at least get to the minimum viable, which is let us explore these damn things. But Right. These these are the things that people will, you know, use it for and maybe use it wrongly and then maybe expose themselves to massive headaches and issues down the line. So I do I do wanna at least be aware of the types of things that we might see in the future. So we can either prevent it in code, make it better, make it make it better down the line or document it in a way or just warn people about some of the dangers that they might hit. Right? It's, it's it's fun that you mentioned it, but at the same time, we've seen people do shit before. It's like, make a cron job that runs every second and put something in the table. Table blows up. Whose fault is that? Right? Did we did we have to do something to prevent that? Is that a user error? That who's who's responsible? Exactly. Featurex activity, see revisions. Working working on that, by the way. Unrelated for this call. Don't tell. Don't tell. Hooray. Okay. I don't want the sidetracked too much, but, yeah, we have the, retention settings for that coming, shipping fairly soon. Thank god. Yes, Amar. If you want to. No. I don't wanna get in trouble with HR. I don't think that's a good idea. Goosebumps. Oh, no. Okay. So, just just keeping the time, in mind, we don't have that much time left. So maybe we should start to kinda converge now a little bit, You know, yeah, just to, get back on track a little bit. Oh, wow. And then the whole essay drops from Yeah. There goes the next 10 minutes. Oh, wow. Should we read that aloud? Are there bad words in it? Looks good. Okay. In my humble opinion, the whole edit complexity of environment migration I'm using directdis comes from the yaw application becoming data itself. Alright. While not using Directus, you can run different versions of the application side by side, and it's easy to switch version which version is running because the structure is defined within the application. Wildirectors, most of you. My guy, there there's not a single dot in that sentence. Oh my god. That's that's one sentence. Don't throw too much shade. And it the the gist of it is because all the configuration is in the database instead of in code, therefore, you have migrations back and forth prompts. Right? And to Joshua's point, that is definitely a configuration as code discussion, which I think we had a couple episodes ago, actually, which is a great segue. Directs.i0\u002Ftv under request review. Highly recommend it. The the the 5 second summary of that discussion is really around there's multiple use cases. Right? There's a lot of people that prefer configuring everything from the UX and UI and then making an export. So by definition, it is in the database. Right? It's it's a mixed environment where there needs to be a two way binding into configuration. We don't wanna end up in a sort of again, the choices are different different use cases, different type of people. I'd I'd rather not end up with a sanity slash strappy type of environment where you have to pull things locally to code, you know, your settings and then redeploy it. That is just not quite the vision that we have for, you know, the ease of use and the user experience for this. Also, I know from some inside chatter that they're trying to get out of that as well. So this is this is a bit of a two way binding discussion. Did you did you see They updated their comment and broke up the paragraph into multiple sentences now. Danielle, look at what you've done. I think this is called cyberbullies. We don't condone that here. Oh, no. Thank you for thank you for the, not not, what is it? Thank you for your cooperation, Wolfolas. Thanks for being here. Thanks for your message. We do appreciate it. I'm not sure how you say it, but Wolf Wolf of us is an awesome contributor. He's been an incredible community member and contributor and helps out across everywhere across the platform. So we are It turns out. Anyhoo, to your point then, converging it down to okay. What can we do? I think the the question that I do have is, do we wanna look into storing this as a nested JSON blob versus a separate table? Right? It would be a bit of a breaking change, but it simplifies both the output files and it simplifies the, the way to import it for us quite a bit. And it also solves that cannot resolve foreign key type thing bug in the same time. Right? The alternative is that we don't do that, but we avoid that breaking change in the operations endpoint with the downside that then for both export and import, we have to make sure that the whole nested tree is included at all times. And then during import, we do it in the correct insertion order. Right? Gut feeling wise, I'm kinda leaning towards let's blop it all as a nest of blob because it simplifies things. At the same time, couple downsides of that is that I think we'd lose the ability to choose what fields are returned from operations. I don't know if that matters as much if you're dealing with flows. Theoretically speaking, most of the databases now should support field selection for JSON. But seeing that we're talking about a theoretical infinitely deep object, that might be tricky. Right? Or we have to filter it down in in post after we got the data back. Quick quick tangent. Can SQLite do that? I'm not sure. I don't think so. Right? I think that update came earlier this year that they have some sort of SQL path, JSON path selection. Yeah. They they've got some JSON path. Yeah. It's fairly recent. That's also one of the reasons why I missed we couldn't really do this earlier. Right? Yeah. Oh, that's nice. Okay. Okay. Yeah. So the lights are getting a lot of attention because you've got companies like Terso and others that are using this distributed edge file based databasing, caching kinds of things. So there's some there's a lot of work going on in that space and SQLite's gotten some pretty good attention. But, I mean, vendor variation, I think, as we move towards hopefully, as we move towards Duris, you know, whether or not that vendor supports it, then we can decide sourcing wise how we handle that. But for the main I'm a big proponent of s Eskelet. Like, if if we if I do something, it it I wanted it for an SQLite, please. Okay. The end of this tangent. I'm sorry to I just I was curious. Yeah. Okay. So so I think because, you know, the nested JSON stuff is something we haven't historically done because we couldn't really do that cross database vendor properly support it. It is something that probably comes with new issues that we don't know about yet. Right? So oh, yeah. Somebody actually mentioned it here too. It was like, Joshua again. Get a shout out, Josh. Stick with the current structure because making breaking breaking changes to the operation config will get to be a huge problem if using nested JSON objects. It's gonna be easier if you have a single row that changes. Just fair enough. And also see support for older databases and vendors that don't have JSON support. Also true. Although, I I wanna say that the ones that don't have an end of life I guess SQLite, the previous version, not yet, of course. Yeah. So and and, also, obviously, the big breaking change. People have been exporting, importing things through the APIs. The separate tools have been made. We saw that BCC schema sync shout out earlier. So we wanna make sure that that is not a huge freaking change either, right, where we just completely wreck any of those existing things. So sticking with the current structure probably makes the most sense. And then that raises the question around, you know, data integrity and import order, but that is a technical problem to solve that we just have to do during import. So that does mean that it'll probably be a separate way that we, do this than than compared to your usual export import just because order matters and that integrity matters. No. That's fine. That should be really doable. You know? Walk the tree once and then just pop off from the back. Yeah. And and the age old question, is this part of the schema export? Yes or no? Oh, right. The the gonna be a discussion for a different day, which is discussion we've had before around roles. And the yeah. But Joshua basically is concluding as well. We just need some schema export flags, which is, like, what do we include? What do we not include? This this this is the discussion we had exactly the same around roles. Do you wanna include roles and permissions? Probably. Do you wanna include users? Probably not. Do you wanna include permissions? Probably. Do you wanna include all of the permissions or just the ones that are about your production databases? Probably just the prod ones. How do you filter that down? This is the exact same stuff. Right? Is it all the flows? Is it some of the flows? How do you choose which of the flows are included and which ones aren't? Do we just export everything and then assume that somebody goes into the file and deletes them manually? Lots of more fun questions to be had in this. And for that, I wanna say, subscribe, like, and subscribe. Nope. I think Hit the bell. Hit the bell. Always been. Hit the bell and make sure you don't miss that episode when we go deep on that. Now selective import export is gonna be needed for most of these things, I think. And ideally, whether export could export everything, but selective import on the import side, the ability to choose what I'm actually merging into the next iteration is is key. Right. Oh, that being said, I've not been muting. I'm muting. I'm muting. I'm muting. And I'm muting. The the doorbell is ringing, so I just wanted to make sure it didn't get too annoying. But that being said, we're at the top of the hour here. There's one more question that just came in. So another quick thought, what about pulling changes instead of pushing them in? Like, a sort of federation type of thing where you link data from multiple direct instances, you pull change from another instance. That sounds like a discussion for another day. I think we've had that as part of the dual syncing as part of the configuration as code anyway. It is something that we we've we're we're thinking about, on that side of the house, and that's this is part of that as well. Right? So the configuration as code affects this, having utilities that make this easy now, versus, you know Yeah. I I think that two way bind to me always has the file in between. So you push it from dev to file, and you pull it from prod from file. That is kind of the the the one to jump. Anyways, with that being said, thank you all for tuning in. Thank you all for the great questions and ideas in the chat. This episode will be available on Directus TV in the very near future. Shout out to shout out to Nat. Dan, did you make sure there's any any Zingers in here? Do we have the thumbnail? Oh, that's not quite sure. May may maybe the the technically technically correct, maybe. I'm not sure. There you go. That's that's the perfect one. That being said, check it out drex. Io\u002Ftv. We'll be doing this again in in the near future.","c781a3e8-c119-41ef-a569-febf1970edb1",[2111,2112,2113],"bb27ac7a-c413-4c5e-9fd0-0ea8bd9e86be","75add66e-c2ed-48da-a664-7a20c7da6a24","9d371e56-3e80-472c-abd3-22faf2a10c32",[],{"year":978,"number":11,"id":1787,"episodes":2116,"show":2117},[1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1768,1800],{"title":1802,"slug":1803},{"id":1560,"slug":2119,"vimeo_id":2120,"description":2121,"tile":2122,"length":159,"resources":12,"people":12,"episode_number":177,"published":2123,"title":2124,"video_transcript_html":2125,"video_transcript_text":2126,"content":12,"seo":2127,"status":19,"episode_people":2128,"recommendations":2130,"season":2131},"q2-2024","974260478","Director of Customer Success Will gives a rundown of the latest news, product updates, and more from Q2 of 2024. ","e3ee7895-191c-4be7-85cc-bf26606282d5","2024-08-02","Q2 2024","\u003Cp>Speaker 0: Hey, guys. Will Friedman here, director of customer success and support here at Directus, dropping in to give you the latest updates with Directus. Just had Directus Sleep Week, which is our, week of announcements with latest updates and features, with Directus. And our biggest announcement was the release of Directus version 11, which is now available on release candidate to download and test out. And it comes with our most upvoted feature request, of all time, which is something many of you have been asking for, which is the ability to do multiple roles per user, which is now solved by in our implementation of what we call policies.\u003C\u002Fp>\u003Cp>It allows for more flexibility when creating roles and permissions. And, you should check more out by checking out our docs and and and looking at some of the video links that are included in this email. While we just announced Directus version 11, we're also announcing extended security updates for Directus version 10 dot 12. This will allow you to stay on version 10.12 while you plan your upgrade to version 11 and receive, security updates for any discovered vulnerabilities. We also announced Directus Labs, which is our place to build more experimental features quickly and gather feedback to meet your needs.\u003C\u002Fp>\u003Cp>As a part of that, we just shipped recently Directus AI in May. Spreadsheet layout this month, which is something I know a lot of folks including you guys, have been asking for, and there's lots more to come. If you have suggestions, please reach out. We're we're we're looking forward to hearing your feedback and suggestions. Yeah.\u003C\u002Fp>\u003Cp>As many of you know, we we released the marketplace in beta form back in our last leap week in March. So if you haven't checked it out, I would advise you checking it out in, your direct to this version. And there's a lot more information in our docs and some videos on Directus TV as well that I advise you to check out as well. In our most recent leap week, this June, we finally announced our plans to further develop the marketplace and the extensions ecosystem. We're gonna be finalizing the registry API, publishing it, and supporting multiple registries in the registries in the marketplace as well.\u003C\u002Fp>\u003Cp>You know, this will allow you to access all the extensions published in our registry, plus any other registries that, you know, could be private for your project as well. So, look forward for more updates, on that coming soon. We have some exciting things coming, to Directus Cloud and the Directus Cloud dashboard. If you guys haven't seen or heard, we we announced and we released custom domains for enterprise cloud projects. This is something that is self-service, and you can do if you're an enterprise customer right in the Directus cloud dashboard.\u003C\u002Fp>\u003Cp>So if you have questions about configuring that, at all, don't hesitate to reach out to our support team so we can assist. We're we're also on the process of implementing log streaming, which I know many of you have asked for and are still asking for even today. Self-service configurable environment variables. And we're also updating the telemetry that you see for your project in the Directus Cloud dashboard in the coming months to include longer time frames, requests, CDN requests, users, and and much more. So, looking forward to giving you guys an update when when all of that is is launched and updating the Directus cloud dashboard.\u003C\u002Fp>\u003Cp>Hopefully, these updates were beneficial to you. And if you have any questions about anything, whether it was Leap Week or in this video, don't hesitate to reach out, so so we can get those questions answered and assist you with anything you got going on. And, until next quarter. See you later.\u003C\u002Fp>","Hey, guys. Will Friedman here, director of customer success and support here at Directus, dropping in to give you the latest updates with Directus. Just had Directus Sleep Week, which is our, week of announcements with latest updates and features, with Directus. And our biggest announcement was the release of Directus version 11, which is now available on release candidate to download and test out. And it comes with our most upvoted feature request, of all time, which is something many of you have been asking for, which is the ability to do multiple roles per user, which is now solved by in our implementation of what we call policies. It allows for more flexibility when creating roles and permissions. And, you should check more out by checking out our docs and and and looking at some of the video links that are included in this email. While we just announced Directus version 11, we're also announcing extended security updates for Directus version 10 dot 12. This will allow you to stay on version 10.12 while you plan your upgrade to version 11 and receive, security updates for any discovered vulnerabilities. We also announced Directus Labs, which is our place to build more experimental features quickly and gather feedback to meet your needs. As a part of that, we just shipped recently Directus AI in May. Spreadsheet layout this month, which is something I know a lot of folks including you guys, have been asking for, and there's lots more to come. If you have suggestions, please reach out. We're we're we're looking forward to hearing your feedback and suggestions. Yeah. As many of you know, we we released the marketplace in beta form back in our last leap week in March. So if you haven't checked it out, I would advise you checking it out in, your direct to this version. And there's a lot more information in our docs and some videos on Directus TV as well that I advise you to check out as well. In our most recent leap week, this June, we finally announced our plans to further develop the marketplace and the extensions ecosystem. We're gonna be finalizing the registry API, publishing it, and supporting multiple registries in the registries in the marketplace as well. You know, this will allow you to access all the extensions published in our registry, plus any other registries that, you know, could be private for your project as well. So, look forward for more updates, on that coming soon. We have some exciting things coming, to Directus Cloud and the Directus Cloud dashboard. If you guys haven't seen or heard, we we announced and we released custom domains for enterprise cloud projects. This is something that is self-service, and you can do if you're an enterprise customer right in the Directus cloud dashboard. So if you have questions about configuring that, at all, don't hesitate to reach out to our support team so we can assist. We're we're also on the process of implementing log streaming, which I know many of you have asked for and are still asking for even today. Self-service configurable environment variables. And we're also updating the telemetry that you see for your project in the Directus Cloud dashboard in the coming months to include longer time frames, requests, CDN requests, users, and and much more. So, looking forward to giving you guys an update when when all of that is is launched and updating the Directus cloud dashboard. Hopefully, these updates were beneficial to you. And if you have any questions about anything, whether it was Leap Week or in this video, don't hesitate to reach out, so so we can get those questions answered and assist you with anything you got going on. And, until next quarter. See you later.","7b601880-8579-4268-9dce-d82e044473f6",[2129],"a466ae2f-13cf-4e04-87cf-1021d086a7fd",[],{"year":978,"number":740,"id":1557,"episodes":2132,"show":2133},[1559,1560,1545],{"title":1562,"slug":1563},{"id":2135,"slug":2136,"vimeo_id":2137,"description":2138,"tile":2139,"length":129,"resources":12,"people":12,"episode_number":264,"published":2140,"title":2141,"video_transcript_html":2142,"video_transcript_text":2143,"content":2144,"seo":2145,"status":19,"episode_people":2146,"recommendations":2148,"season":2149},"f711e94f-14c1-48dd-b00c-70a340351412","m2a-relationships","983672403","Discover how to use Directus's powerful many-to-any relationships to create flexible and dynamic content structures. Learn to set up a versatile page builder that allows linking diverse content types to a single parent collection. ","d59cf454-5e42-4cbd-a1cd-27ddf09f151d","2024-07-26","M2A Relationships","\u003Cp>Speaker 0: Welcome back to another episode of Short Hops. I'm your host Brian Gillespie. And today, we're gonna show you how to relate anything to anything or everything inside your Directus instance. So that is done using the many to any relationships or the builder interface that we have inside the project. And, basically, it allows you to link items from a parent collection to any other child collection in the database.\u003C\u002Fp>\u003Cp>So it is useful for page builder scenarios, other types of dynamic content creation, very helpful when you're building a CMS. You know, you might want to empower your marketing team and allow them to build complex layouts without actually touching code, and without a ton of effort on your part. So, you know, this graphic kind of illustrates what we've got going on here. We've got 3 different types of content. These are gonna be set up as different collections inside Directus.\u003C\u002Fp>\u003Cp>We've got a container for that content, like we wanna put all this content on a blog post or a page. And then we have this Junction collection, which has the ID of the blog, the collection for the actual different types of content we have, and then a ID for the content within that collection. So that's kind of how it all fits together at a high level. Let's jump into Directus and actually set this up. So I'm just in one of my demo projects and I'm gonna hop over to the data model section here and I'm also gonna do a little mouse highlighter thing.\u003C\u002Fp>\u003Cp>So what I've already done here is basically gone through and defined collections for all of my different components on a page. I've got a hero section that has a title, some buttons, and a background. I've got a, CTA section which has a title, some buttons and an image. I've got FAQs that have a title, a headline and some actual FAQs, some questions for us to answer. And what I don't have here is a container for this.\u003C\u002Fp>\u003Cp>What is it? What is the vessel for this? Let's create a new collection called pages. We'll just give this a generated UUID. I'm gonna leave the rest of this blank for now.\u003C\u002Fp>\u003Cp>We'll give this page a title. Probably want to give it a slug so we can access it or a permalink or something like that. And now we're into the many to any relationships inside Directus. So how do we set this up? Alright.\u003C\u002Fp>\u003Cp>I'm gonna go down to the relational section, I'm gonna look for the builder or m2a, and I typically call this something like blocks, content blocks, page blocks, you know, your specific naming convention, you know, use what feels right for you. But when we set this up, unlike the other relationships in Directus where we have to pick 1 collection that we want to relate to, here we can pick as many collections as we want. So I want the block hero. I want the, block FAQs. I want the block CTA collection.\u003C\u002Fp>\u003Cp>And once I'm done with that, I'll hit save, and that will create that field for me. Now if I go into my data model and I look at the very bottom, you can see that Directus has created this junction collection for us called pages blocks, or pages underscore blocks. If we take a look at it, we can see we have a relationship back to the pages ID, and then we have an item and a collection. So, these relate to the actual blocks that we selected like hero or CTA or FAQ. So now, how does this all come together when we're actually setting up or creating a page?\u003C\u002Fp>\u003Cp>Alright. We give it a title. We'll call this slash for the slug just because that's the homepage or you know, root home page, whatever you wanna call it, index. Great. And then I go in and create some blocks.\u003C\u002Fp>\u003Cp>So I can create a hero block. This is my hero. You are my hero. Okay. Go in and fill out the rest of these fields as well.\u003C\u002Fp>\u003Cp>Maybe we add some FAQs. Right? FAQs, we'll give it a title. Let's add one question to it. What is many to any?\u003C\u002Fp>\u003Cp>The answer is magic. Alright. So we'll go ahead and save this. Save this, and now we have that page builder scenario where we can set this up, right? If I wanna be able to drag and drop the order of that, I could just go back to that relationship.\u003C\u002Fp>\u003Cp>I go to pages, I go to blocks, and I look for my relationship here. I'm just gonna add a sort field, we'll call it sort. Directus will create that for me. And now I can drag and drop to rearrange these blocks and that would be the order that we would render those on the front end. So now I'm just gonna go in and quickly give public access to those blocks.\u003C\u002Fp>\u003Cp>So I've got a hero, I've got FAQs, and I also have pages and pages blocks. You know, set up your permissions based on what you need for your specific project, but I just wanted to take a look at this really quickly on this video. So we're gonna go into our API. We'll do a slash item slash pages. And here we could see we've got a title, we've got our slug, we've got our blocks.\u003C\u002Fp>\u003Cp>Great. But how do I get the information within the blocks? Right? So I'll just call fields. Let's get all the root level fields and we'll do a comma, and then I'm gonna say blocks dot item dot ID.\u003C\u002Fp>\u003Cp>Let's do another one like blocks dot item or no, let's just get all the fields from the item and take a look at that. Right. Cool. Alright. So now I could see just by, you know, setting up this quick little param for fields, I've got the actual content of my blocks.\u003C\u002Fp>\u003Cp>So I've got my FAQs and here's what that looks like. I've got my item, my hero here, so we could see that as well. And then you would obviously, like feed that into your front end. You probably got some type of component that loops through these things and dynamically renders the correct for that. So that is it for this video.\u003C\u002Fp>\u003Cp>I hope this demystified some of the many to any relationships for you. There's definitely more meat on the bone here, but come back for the next video. We'll see you.\u003C\u002Fp>","Welcome back to another episode of Short Hops. I'm your host Brian Gillespie. And today, we're gonna show you how to relate anything to anything or everything inside your Directus instance. So that is done using the many to any relationships or the builder interface that we have inside the project. And, basically, it allows you to link items from a parent collection to any other child collection in the database. So it is useful for page builder scenarios, other types of dynamic content creation, very helpful when you're building a CMS. You know, you might want to empower your marketing team and allow them to build complex layouts without actually touching code, and without a ton of effort on your part. So, you know, this graphic kind of illustrates what we've got going on here. We've got 3 different types of content. These are gonna be set up as different collections inside Directus. We've got a container for that content, like we wanna put all this content on a blog post or a page. And then we have this Junction collection, which has the ID of the blog, the collection for the actual different types of content we have, and then a ID for the content within that collection. So that's kind of how it all fits together at a high level. Let's jump into Directus and actually set this up. So I'm just in one of my demo projects and I'm gonna hop over to the data model section here and I'm also gonna do a little mouse highlighter thing. So what I've already done here is basically gone through and defined collections for all of my different components on a page. I've got a hero section that has a title, some buttons, and a background. I've got a, CTA section which has a title, some buttons and an image. I've got FAQs that have a title, a headline and some actual FAQs, some questions for us to answer. And what I don't have here is a container for this. What is it? What is the vessel for this? Let's create a new collection called pages. We'll just give this a generated UUID. I'm gonna leave the rest of this blank for now. We'll give this page a title. Probably want to give it a slug so we can access it or a permalink or something like that. And now we're into the many to any relationships inside Directus. So how do we set this up? Alright. I'm gonna go down to the relational section, I'm gonna look for the builder or m2a, and I typically call this something like blocks, content blocks, page blocks, you know, your specific naming convention, you know, use what feels right for you. But when we set this up, unlike the other relationships in Directus where we have to pick 1 collection that we want to relate to, here we can pick as many collections as we want. So I want the block hero. I want the, block FAQs. I want the block CTA collection. And once I'm done with that, I'll hit save, and that will create that field for me. Now if I go into my data model and I look at the very bottom, you can see that Directus has created this junction collection for us called pages blocks, or pages underscore blocks. If we take a look at it, we can see we have a relationship back to the pages ID, and then we have an item and a collection. So, these relate to the actual blocks that we selected like hero or CTA or FAQ. So now, how does this all come together when we're actually setting up or creating a page? Alright. We give it a title. We'll call this slash for the slug just because that's the homepage or you know, root home page, whatever you wanna call it, index. Great. And then I go in and create some blocks. So I can create a hero block. This is my hero. You are my hero. Okay. Go in and fill out the rest of these fields as well. Maybe we add some FAQs. Right? FAQs, we'll give it a title. Let's add one question to it. What is many to any? The answer is magic. Alright. So we'll go ahead and save this. Save this, and now we have that page builder scenario where we can set this up, right? If I wanna be able to drag and drop the order of that, I could just go back to that relationship. I go to pages, I go to blocks, and I look for my relationship here. I'm just gonna add a sort field, we'll call it sort. Directus will create that for me. And now I can drag and drop to rearrange these blocks and that would be the order that we would render those on the front end. So now I'm just gonna go in and quickly give public access to those blocks. So I've got a hero, I've got FAQs, and I also have pages and pages blocks. You know, set up your permissions based on what you need for your specific project, but I just wanted to take a look at this really quickly on this video. So we're gonna go into our API. We'll do a slash item slash pages. And here we could see we've got a title, we've got our slug, we've got our blocks. Great. But how do I get the information within the blocks? Right? So I'll just call fields. Let's get all the root level fields and we'll do a comma, and then I'm gonna say blocks dot item dot ID. Let's do another one like blocks dot item or no, let's just get all the fields from the item and take a look at that. Right. Cool. Alright. So now I could see just by, you know, setting up this quick little param for fields, I've got the actual content of my blocks. So I've got my FAQs and here's what that looks like. I've got my item, my hero here, so we could see that as well. And then you would obviously, like feed that into your front end. You probably got some type of component that loops through these things and dynamically renders the correct for that. So that is it for this video. I hope this demystified some of the many to any relationships for you. There's definitely more meat on the bone here, but come back for the next video. We'll see you.","\u003Cp>In this comprehensive episode of Short Hops, host Bryant Gillespie dives deep into the world of many-to-any relationships in Directus. Learn how to create flexible content structures that allow you to relate anything to well... anything within your Directus instance, opening up powerful possibilities for dynamic content creation and management.\u003C\u002Fp>\n\u003Cp>What you'll learn:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Understanding the concept and benefits of many-to-any relationships\u003C\u002Fli>\n\u003Cli>Setting up collections for different content types (e.g., hero sections, CTAs, FAQs)\u003C\u002Fli>\n\u003Cli>Creating a parent collection to contain diverse content blocks\u003C\u002Fli>\n\u003Cli>Implementing the builder interface for many-to-any relationships\u003C\u002Fli>\n\u003Cli>Configuring and managing junction collections\u003C\u002Fli>\n\u003Cli>Adding drag-and-drop functionality for content block ordering\u003C\u002Fli>\n\u003Cli>Querying and retrieving complex relational data through the API\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Bryant guides you through a practical, step-by-step process of:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Creating collections for various content types\u003C\u002Fli>\n\u003Cli>Setting up a parent 'pages' collection\u003C\u002Fli>\n\u003Cli>Implementing the many-to-any relationship using the builder interface\u003C\u002Fli>\n\u003Cli>Understanding the automatically generated junction collection\u003C\u002Fli>\n\u003Cli>Adding and managing content blocks within pages\u003C\u002Fli>\n\u003Cli>Implementing sort functionality for content blocks\u003C\u002Fli>\n\u003Cli>Setting up permissions for public access\u003C\u002Fli>\n\u003Cli>Querying the API to retrieve complex relational data\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Whether you're building a website with dynamic layouts, a complex CMS, or any system requiring versatile content structures, this technique will empower your team to create sophisticated content without the need for constant developer intervention.\u003C\u002Fp>","ac6f05df-e1e7-4170-9114-e13f2420a669",[2147],"53c09b3e-ea64-421b-8b1b-da48e6ec07fd",[],{"year":978,"number":2150,"id":2151,"episodes":2152,"show":2160},46,"a1886f5f-e5c4-4dd2-ac8a-d2e7c176f813",[2153,2154,2135,2155,2156,2157,2158,2159],"e82ffa88-91c1-4605-aefa-67bb8e20a9ed","1fb83779-28e9-4523-bcbc-57065d7177a1","fcb6f3f2-d6f3-4905-a3d9-b44aed7bb25c","2d192e76-378b-4540-9d41-2506460a50af","cc653542-7721-4b37-8978-60fee90081dc","0bfed0fe-2c73-4528-8a6a-d3b39b4c0528","0cbf2b23-545e-4ea7-ae45-47707292caec",{"title":2161,"slug":2162},"Short Hops","short-hops",{"id":2159,"slug":2164,"vimeo_id":2165,"description":2166,"tile":2167,"length":527,"resources":12,"people":12,"episode_number":11,"published":2140,"title":2168,"video_transcript_html":2169,"video_transcript_text":2170,"content":2171,"seo":2172,"status":19,"episode_people":2173,"recommendations":2175,"season":2176},"complex-queries","983670600","Dive into the world of Directus REST API querying in this informative episode. Learn how to perform complex queries, filter results, and work with relational data using the Directus SDK. ","222a28d6-c2e4-43fc-a61f-dc57132bac26","Complex Queries via API","\u003Cp>Speaker 0: Welcome back to yet another episode of Short Hops. I'm your host, Brian Gillespie. And today, I wanna cover, a tough topic for new comers to direct us, which can be setting up your queries via the REST API. So today we are going to query our hearts out, and kind of run through some complex queries more than just, yeah, you know, calling a standard endpoint, very simple. Alright.\u003C\u002Fp>\u003Cp>So, I've got a example project over here. I've got some pages that we're working with within a collection. And over here on the right, I've just set up a simple little node project and pulled in our SDK. If you are working with the Directus API, I highly recommend using the SDK, just because we've got some little convenience methods in here for you. It makes it much easier to work with your Directus API.\u003C\u002Fp>\u003Cp>So I have got, I've imported the create Directus function. We've got a a rest API, we're gonna read some items, so that's one of the methods we're gonna call. And then I'm just creating a client here, so the format's very simple. We pass the URL, we tag on, a rest function at the end, or with rest function at the end, and then we can call that client. So I'm just saying, hey, defining a constant.\u003C\u002Fp>\u003Cp>We'll await that request to pages, and I don't have anything in the filter here. I could actually comment this out if I wanted to. And then let's just run node index dotjs, and we could see I'm getting a bunch of pages here. So one of the first things that you might wanna do when working with the API, the REST API acts like GraphQL in that I can call the specific fields that I want. So I can add a parameter here for fields and we'll just say I want the ID, the title, and maybe the blocks for this specific page.\u003C\u002Fp>\u003Cp>So now if I call this again, we can see a Directus has given me just the information that I asked for. That's great. What if we wanted to filter this down though? Right? I'm getting a bunch of different pages.\u003C\u002Fp>\u003Cp>Some of these are published, some of them are not. Maybe we just wanna see only the published pages, right? So that's where our filter comes back in. And here, let's add a comma there. And now let's do a filter where the status, and we've got a couple of operators here.\u003C\u002Fp>\u003Cp>There's actually a lot of operators. You can find these on the documentation. And we want the status to be equal to published. Great. We can run that again.\u003C\u002Fp>\u003Cp>And now I shouldn't see, like, a test with new default because I'm seeing that over there. Great. Okay. I don't see that one in the list, But let's say I wanted to actually limit the number of results that I see, I could do something like this where I just add a limit parameter and we'll pass that too. And now I should only get back to pages.\u003C\u002Fp>\u003Cp>Right? So I see my home page. I'm getting the manifesto page here. And I can see the blocks. This is an ID.\u003C\u002Fp>\u003Cp>We'll go about fetching that in a moment. Now what if we have a more complex filter where we need to stack different conditions together? So for that, you have the and and or operators. So I could do something like this. And instead of an object, this is gonna be an array.\u003C\u002Fp>\u003Cp>So I'm gonna pass an array of objects like status is equal to published. Can I do formatting on this? I don't have Prettier or ESLint setup on this project. But So I want the status is equal to published and maybe the permalink let's just get the home page, right? So I'm gonna do equal to not home but slash because I can see that right here, this permalink.\u003C\u002Fp>\u003Cp>Great. And even though I've got a limit of 2, I should only get one back. Permalink is equal to, did I save that, or status is equal to publish. Oh, that's an or filter. So we want both of these conditions.\u003C\u002Fp>\u003Cp>Right? Let's do that. So we want to match both of those conditions. And now the only item I get back in my array of data is the home page. Great.\u003C\u002Fp>\u003Cp>So that's how we can stack filters together. Let's take this a step further and for our blocks, we want to get the content within those blocks. And, you can expand the relationships within a single API call within Directus which is again is super nice as well. So we'll do blocks, let's add, this is actually gonna be an object so we'll do blocks. We'll give it an array of fields that we want to give for the block.\u003C\u002Fp>\u003Cp>Let's get the block ID, maybe we want to get the collection for that block, This is set up using our many to any builder relationship and we'll do item dot star. So the star or asterisk is a wild card. That should return all the fields or all the root level fields for the item within the block. So let's run this and actually see what we get out of it. And now you can see I'm actually getting the data for my blocks.\u003C\u002Fp>\u003Cp>So I can see, you know, this is columns, we've got a title, we've got a headline, we've got the actual rows. So I could even drill further into those relationships via a single API call if I wanted. Now there's one other field that I want to pull within this and we'll do hide block. And do we have hide block? Okay.\u003C\u002Fp>\u003Cp>So now I've I've got this hide block on here. You know, within the page builder, maybe I wanna hide one of these individual blocks so that it doesn't show on a page. I can query that on my front end so that, you know, hey, we we don't have to remove this, maybe we want to show it at a later date, but I wanna hide this specific block and not render that when I consume that API on my front end. So how would I do something like that? This is a setup on the relational field, right?\u003C\u002Fp>\u003Cp>So at the top level here we can filter based on the pages, but there is a special parameter that you can use to filter the relational data in a single API call. So you'll see how powerful this is in just a moment. We'll go in and we'll do deep parameter. And we're going to look at our blocks. And now it's like we've got a whole another query within this, right?\u003C\u002Fp>\u003Cp>So I'm just going to preface this with a underscore or an underscore and we're gonna do a filter and now we are filtering on the block level. So the related blocks we wanna do hide block is equal to not equal to true. Alright so let's see what that returns. And I'm just gonna clear out my console here and I shouldn't see the first item as a quote because I have removed that or filtered it out. Forgot my comma at the end there.\u003C\u002Fp>\u003Cp>So we'll clear one more time, call this out. If I zoom back up to the top of this, you could see the first block in my array here is the form section. Right now if I go in and I just hide a lot of these blocks, right? I've hidden the first three blocks, the first section that I see in my API call should be this FAQ one. Let's clear this again.\u003C\u002Fp>\u003Cp>Just to make it easier we'll do node dot JS and, oh, I forgot to save the actual item over here. Let's try that one more time. Node. Js index dot JS, there we go. And now the first block that we see is the FAQ section.\u003C\u002Fp>\u003Cp>Right? If I wanted to see all the blocks that we hid, I could just change that to not equal to false or I could set it to equal to true, either way. But now I can see here's all the blocks that we hid within that specific page. So I hope this has been a great tutorial on just the sorts of things you could do with the, global parameters when you're calling the API. Stay tuned for more episodes of Short Hops.\u003C\u002Fp>\u003Cp>That's it. We'll see you.\u003C\u002Fp>","Welcome back to yet another episode of Short Hops. I'm your host, Brian Gillespie. And today, I wanna cover, a tough topic for new comers to direct us, which can be setting up your queries via the REST API. So today we are going to query our hearts out, and kind of run through some complex queries more than just, yeah, you know, calling a standard endpoint, very simple. Alright. So, I've got a example project over here. I've got some pages that we're working with within a collection. And over here on the right, I've just set up a simple little node project and pulled in our SDK. If you are working with the Directus API, I highly recommend using the SDK, just because we've got some little convenience methods in here for you. It makes it much easier to work with your Directus API. So I have got, I've imported the create Directus function. We've got a a rest API, we're gonna read some items, so that's one of the methods we're gonna call. And then I'm just creating a client here, so the format's very simple. We pass the URL, we tag on, a rest function at the end, or with rest function at the end, and then we can call that client. So I'm just saying, hey, defining a constant. We'll await that request to pages, and I don't have anything in the filter here. I could actually comment this out if I wanted to. And then let's just run node index dotjs, and we could see I'm getting a bunch of pages here. So one of the first things that you might wanna do when working with the API, the REST API acts like GraphQL in that I can call the specific fields that I want. So I can add a parameter here for fields and we'll just say I want the ID, the title, and maybe the blocks for this specific page. So now if I call this again, we can see a Directus has given me just the information that I asked for. That's great. What if we wanted to filter this down though? Right? I'm getting a bunch of different pages. Some of these are published, some of them are not. Maybe we just wanna see only the published pages, right? So that's where our filter comes back in. And here, let's add a comma there. And now let's do a filter where the status, and we've got a couple of operators here. There's actually a lot of operators. You can find these on the documentation. And we want the status to be equal to published. Great. We can run that again. And now I shouldn't see, like, a test with new default because I'm seeing that over there. Great. Okay. I don't see that one in the list, But let's say I wanted to actually limit the number of results that I see, I could do something like this where I just add a limit parameter and we'll pass that too. And now I should only get back to pages. Right? So I see my home page. I'm getting the manifesto page here. And I can see the blocks. This is an ID. We'll go about fetching that in a moment. Now what if we have a more complex filter where we need to stack different conditions together? So for that, you have the and and or operators. So I could do something like this. And instead of an object, this is gonna be an array. So I'm gonna pass an array of objects like status is equal to published. Can I do formatting on this? I don't have Prettier or ESLint setup on this project. But So I want the status is equal to published and maybe the permalink let's just get the home page, right? So I'm gonna do equal to not home but slash because I can see that right here, this permalink. Great. And even though I've got a limit of 2, I should only get one back. Permalink is equal to, did I save that, or status is equal to publish. Oh, that's an or filter. So we want both of these conditions. Right? Let's do that. So we want to match both of those conditions. And now the only item I get back in my array of data is the home page. Great. So that's how we can stack filters together. Let's take this a step further and for our blocks, we want to get the content within those blocks. And, you can expand the relationships within a single API call within Directus which is again is super nice as well. So we'll do blocks, let's add, this is actually gonna be an object so we'll do blocks. We'll give it an array of fields that we want to give for the block. Let's get the block ID, maybe we want to get the collection for that block, This is set up using our many to any builder relationship and we'll do item dot star. So the star or asterisk is a wild card. That should return all the fields or all the root level fields for the item within the block. So let's run this and actually see what we get out of it. And now you can see I'm actually getting the data for my blocks. So I can see, you know, this is columns, we've got a title, we've got a headline, we've got the actual rows. So I could even drill further into those relationships via a single API call if I wanted. Now there's one other field that I want to pull within this and we'll do hide block. And do we have hide block? Okay. So now I've I've got this hide block on here. You know, within the page builder, maybe I wanna hide one of these individual blocks so that it doesn't show on a page. I can query that on my front end so that, you know, hey, we we don't have to remove this, maybe we want to show it at a later date, but I wanna hide this specific block and not render that when I consume that API on my front end. So how would I do something like that? This is a setup on the relational field, right? So at the top level here we can filter based on the pages, but there is a special parameter that you can use to filter the relational data in a single API call. So you'll see how powerful this is in just a moment. We'll go in and we'll do deep parameter. And we're going to look at our blocks. And now it's like we've got a whole another query within this, right? So I'm just going to preface this with a underscore or an underscore and we're gonna do a filter and now we are filtering on the block level. So the related blocks we wanna do hide block is equal to not equal to true. Alright so let's see what that returns. And I'm just gonna clear out my console here and I shouldn't see the first item as a quote because I have removed that or filtered it out. Forgot my comma at the end there. So we'll clear one more time, call this out. If I zoom back up to the top of this, you could see the first block in my array here is the form section. Right now if I go in and I just hide a lot of these blocks, right? I've hidden the first three blocks, the first section that I see in my API call should be this FAQ one. Let's clear this again. Just to make it easier we'll do node dot JS and, oh, I forgot to save the actual item over here. Let's try that one more time. Node. Js index dot JS, there we go. And now the first block that we see is the FAQ section. Right? If I wanted to see all the blocks that we hid, I could just change that to not equal to false or I could set it to equal to true, either way. But now I can see here's all the blocks that we hid within that specific page. So I hope this has been a great tutorial on just the sorts of things you could do with the, global parameters when you're calling the API. Stay tuned for more episodes of Short Hops. That's it. We'll see you.","\u003Cp>Join host Bryant Gillespie in this in-depth episode of Short Hops as he tackles a challenging topic for Directus newcomers: setting up queries via the REST API. Bryant walks you through complex querying techniques using the Directus SDK, demonstrating how to efficiently interact with your Directus instance.\u003C\u002Fp>\n\u003Cp>In this episode, you'll learn:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>How to set up a basic Node.js project with the Directus SDK\u003C\u002Fli>\n\u003Cli>Querying specific fields and limiting results\u003C\u002Fli>\n\u003Cli>Implementing filters for published content\u003C\u002Fli>\n\u003Cli>Using complex filters with AND\u002FOR operators\u003C\u002Fli>\n\u003Cli>Expanding relationships within a single API call\u003C\u002Fli>\n\u003Cli>Filtering relational data using the deep parameter\u003C\u002Fli>\n\u003Cli>Practical examples of querying and filtering page builder blocks\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Whether you're new to Directus or looking to level up your API skills, this episode provides valuable insights into crafting powerful queries. Discover how to efficiently retrieve and filter your data, work with relational fields, and optimize your API calls for better performance.\u003C\u002Fp>","5c71b202-e026-4251-856d-20466be7a17e",[2174],"20ccfe78-9e3e-449c-81ea-324f7e3b22de",[],{"year":978,"number":2150,"id":2151,"episodes":2177,"show":2178},[2153,2154,2135,2155,2156,2157,2158,2159],{"title":2161,"slug":2162},{"id":2154,"slug":2180,"vimeo_id":2181,"description":2182,"tile":2183,"length":264,"resources":12,"people":12,"episode_number":177,"published":2140,"title":2184,"video_transcript_html":2185,"video_transcript_text":2186,"content":2187,"seo":2188,"status":19,"episode_people":2189,"recommendations":2191,"season":2192},"soft-delete","983672903","Learn how to set up and use the archive functionality to declutter your views, improve workflow, and maintain a clean data structure without permanently deleting items. ","725e6b81-e003-479b-b5c0-154330a159f1","Soft Delete","\u003Cp>Speaker 0: Hello, and welcome back to yet another episode of Short Hops. I'm your host, Brian Gillespie. Today, I wanna cover the soft delete features inside Directus. It's called archive. So the archive feature allows you just to move records or items out of your way that are archived, that are no longer needed.\u003C\u002Fp>\u003Cp>Alright? So the example that we have here is our inbox. You can see I've got a couple of new items that have arrived in the inbox with, you know, contact forms and newsletter. Some of these things I need to take care of. Some of them have already been assigned.\u003C\u002Fp>\u003Cp>Some of them have already been closed. That's great. What do I what if I wanna get rid of these ones that are closed, get them out of my view? You know, I could certainly come in and create a filter for that using the filters inside Directus, really easy. Isn't empty or, I'm sorry, isn't does not equal closed.\u003C\u002Fp>\u003Cp>Right? So I could save that as the default view, but I've got another option. And that is using the archive feature. So to find that, I just go to the specific collection that I wanna edit. In that case, this is the inbox.\u003C\u002Fp>\u003Cp>I'm gonna scroll down the page until I come across the archive settings. So I'm gonna choose the archive field, which is basically what field do we wanna act on to archive or unarchive records. And then we want to enable this. And then we're gonna add a value. So if the value is closed we're gonna archive that record.\u003C\u002Fp>\u003Cp>If we unarchive something we bring it back, we're gonna set the value to new. Right? And that's really all we have to do in this case. We go back to our inbox and now you can see I don't see any of these records that have been closed. And this one that came in 19 hours ago, if I go in and just do like a quick edit on that, change this to closed, you can see that no longer shows up.\u003C\u002Fp>\u003Cp>Now on the right hand side bar, I get a new archive option. So I could show active items, I could show archived items, or I could show all of them. And then if I wanna bring an item back, I could just click in it or, and click this unarchive option, and that will change the status for me. Or I could manually change the status or I could do this in bulk by just setting the status to assigned, right? And now I no longer have those archived items.\u003C\u002Fp>\u003Cp>Now super simple, great little UX addition. One thing to remember though, this does not necessarily apply to the actual API. You know, this is only inside the data studio. If you want like a soft delete feature inside the API you can either do that using permissions or when you run your API calls, you wanna make sure that you look for the ones where status is new or assigned. So these settings only apply within the data studio.\u003C\u002Fp>\u003Cp>And with that, that's it for this episode of Short Hops. Stay tuned for the next one.\u003C\u002Fp>","Hello, and welcome back to yet another episode of Short Hops. I'm your host, Brian Gillespie. Today, I wanna cover the soft delete features inside Directus. It's called archive. So the archive feature allows you just to move records or items out of your way that are archived, that are no longer needed. Alright? So the example that we have here is our inbox. You can see I've got a couple of new items that have arrived in the inbox with, you know, contact forms and newsletter. Some of these things I need to take care of. Some of them have already been assigned. Some of them have already been closed. That's great. What do I what if I wanna get rid of these ones that are closed, get them out of my view? You know, I could certainly come in and create a filter for that using the filters inside Directus, really easy. Isn't empty or, I'm sorry, isn't does not equal closed. Right? So I could save that as the default view, but I've got another option. And that is using the archive feature. So to find that, I just go to the specific collection that I wanna edit. In that case, this is the inbox. I'm gonna scroll down the page until I come across the archive settings. So I'm gonna choose the archive field, which is basically what field do we wanna act on to archive or unarchive records. And then we want to enable this. And then we're gonna add a value. So if the value is closed we're gonna archive that record. If we unarchive something we bring it back, we're gonna set the value to new. Right? And that's really all we have to do in this case. We go back to our inbox and now you can see I don't see any of these records that have been closed. And this one that came in 19 hours ago, if I go in and just do like a quick edit on that, change this to closed, you can see that no longer shows up. Now on the right hand side bar, I get a new archive option. So I could show active items, I could show archived items, or I could show all of them. And then if I wanna bring an item back, I could just click in it or, and click this unarchive option, and that will change the status for me. Or I could manually change the status or I could do this in bulk by just setting the status to assigned, right? And now I no longer have those archived items. Now super simple, great little UX addition. One thing to remember though, this does not necessarily apply to the actual API. You know, this is only inside the data studio. If you want like a soft delete feature inside the API you can either do that using permissions or when you run your API calls, you wanna make sure that you look for the ones where status is new or assigned. So these settings only apply within the data studio. And with that, that's it for this episode of Short Hops. Stay tuned for the next one.","\u003Cp>In this episode of Short Hops, host Bryant Gillespie delves into the soft delete functionality in Directus, known as the archive feature. Discover how this powerful tool can help you manage your records more efficiently and keep your workspace organized.\u003C\u002Fp>\n\u003Cp>What you'll learn:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Understanding the archive feature and its benefits\u003C\u002Fli>\n\u003Cli>How to set up and configure archive settings for a collection\u003C\u002Fli>\n\u003Cli>Using the archive feature to declutter your views\u003C\u002Fli>\n\u003Cli>Archiving and unarchiving individual or bulk items\u003C\u002Fli>\n\u003Cli>Viewing active, archived, or all items in your collection\u003C\u002Fli>\n\u003Cli>Important considerations when using the archive feature with the API\u003C\u002Fli>\n\u003C\u002Ful>","17168efd-4993-4e52-9615-d2558575ceed",[2190],"1fc9239c-6474-4e3e-b01c-85f25181d668",[],{"year":978,"number":2150,"id":2151,"episodes":2193,"show":2194},[2153,2154,2135,2155,2156,2157,2158,2159],{"title":2161,"slug":2162},{"id":2153,"slug":2196,"vimeo_id":2197,"description":2198,"tile":2199,"length":159,"resources":12,"people":12,"episode_number":13,"published":2140,"title":2200,"video_transcript_html":2201,"video_transcript_text":2202,"content":2203,"seo":2204,"status":19,"episode_people":2205,"recommendations":2207,"season":2208},"transformation-presets","983673160","Discover the power of transformation presets in Directus to streamline your image handling processes. Learn how to create and use these presets to standardize image formats, sizes, and even apply custom transformations across your entire website or application","9ceeec94-7ef3-455a-bb85-ca8c0cad2827","Transformation Presets","\u003Cp>Speaker 0: Welcome back to another episode of Short Hops. I'm your host Brian Gillespie. And today, we are covering transformation presets. What are transformation presets, you ask? So one of the more powerful features inside Directus is the ability to transform your images.\u003C\u002Fp>\u003Cp>We could do things like cropping. We can adjust the color, change it to gray scale. We can use more compressed formats like webp and avif to make our site load faster. Transformation presets are just shortcuts for those. So you can standardize images and formats, sizes across your entire website or application quickly and easily.\u003C\u002Fp>\u003Cp>So let's take a look. I'm inside a sample project. I've got, some assets here. And if I just open up this, bunny, this is actually probably a hare. You know, we could do things like this where we have our transformations.\u003C\u002Fp>\u003Cp>Right? So I can adjust the width of this image. I can adjust the height of this image. But imagine I've got a blog page where I have cards that are all, roughly a same size. That's a good use case for transformation presets.\u003C\u002Fp>\u003Cp>So what can I do? We're gonna go into the Directus settings, to our project settings. We're gonna scroll down to files and storage. And I'll zoom in even further for you here. So there's a couple of settings when it comes to transformations.\u003C\u002Fp>\u003Cp>I can allow all transformations, which is basically, hey, I can whatever transformation I wanna do as long as it is valid sharp, which is the underlying library that we use, we can do that. I can also disable all transformations, so you get what you get as far as images. And last but not least, I can limit the transformations to presets below. So if you're concerned about people absolutely killing your server with transformations or kind of standardizing, you probably wanna do this limiting option. But as far as our presets, again, these are just shortcuts that you set up to, you know, standardize the format.\u003C\u002Fp>\u003Cp>So you could see this one called 800 width, or 800 w, is basically just a preset width of 800. We use the webp format. And instead of having to pass all these parameters when I actually get the image, I could just do something like this where in my URL for that specific image, you know, slash assets, slash the UUID for that file, I add a key param and I do 800w. Right? So that saves me a lot of time.\u003C\u002Fp>\u003Cp>And again, helps me standardize across my website. Creating these presets is super simple. I just go in. If I wanted to create a new preset, let's call it purple. Right?\u003C\u002Fp>\u003Cp>We'll do 800 wide. Let's do a webp format. And I've just got a additional transform that I wanna do here. We're gonna tint the image and we're gonna apply our directus purple, hence the name purple. So we'll do fit inside, that's great.\u003C\u002Fp>\u003Cp>Save this. And now if I go back to this specific image, all I have to do is change that key. And I now get a purple tinted hair in a field image. Looks amazing. So that is it for transformation presets.\u003C\u002Fp>\u003Cp>Try this out. Leverage it in your projects. It's super helpful. We'll catch you on the next one.\u003C\u002Fp>","Welcome back to another episode of Short Hops. I'm your host Brian Gillespie. And today, we are covering transformation presets. What are transformation presets, you ask? So one of the more powerful features inside Directus is the ability to transform your images. We could do things like cropping. We can adjust the color, change it to gray scale. We can use more compressed formats like webp and avif to make our site load faster. Transformation presets are just shortcuts for those. So you can standardize images and formats, sizes across your entire website or application quickly and easily. So let's take a look. I'm inside a sample project. I've got, some assets here. And if I just open up this, bunny, this is actually probably a hare. You know, we could do things like this where we have our transformations. Right? So I can adjust the width of this image. I can adjust the height of this image. But imagine I've got a blog page where I have cards that are all, roughly a same size. That's a good use case for transformation presets. So what can I do? We're gonna go into the Directus settings, to our project settings. We're gonna scroll down to files and storage. And I'll zoom in even further for you here. So there's a couple of settings when it comes to transformations. I can allow all transformations, which is basically, hey, I can whatever transformation I wanna do as long as it is valid sharp, which is the underlying library that we use, we can do that. I can also disable all transformations, so you get what you get as far as images. And last but not least, I can limit the transformations to presets below. So if you're concerned about people absolutely killing your server with transformations or kind of standardizing, you probably wanna do this limiting option. But as far as our presets, again, these are just shortcuts that you set up to, you know, standardize the format. So you could see this one called 800 width, or 800 w, is basically just a preset width of 800. We use the webp format. And instead of having to pass all these parameters when I actually get the image, I could just do something like this where in my URL for that specific image, you know, slash assets, slash the UUID for that file, I add a key param and I do 800w. Right? So that saves me a lot of time. And again, helps me standardize across my website. Creating these presets is super simple. I just go in. If I wanted to create a new preset, let's call it purple. Right? We'll do 800 wide. Let's do a webp format. And I've just got a additional transform that I wanna do here. We're gonna tint the image and we're gonna apply our directus purple, hence the name purple. So we'll do fit inside, that's great. Save this. And now if I go back to this specific image, all I have to do is change that key. And I now get a purple tinted hair in a field image. Looks amazing. So that is it for transformation presets. Try this out. Leverage it in your projects. It's super helpful. We'll catch you on the next one.","\u003Cp>In this episode of Short Hops, dive into the world of transformation presets in Directus. Host Bryant Gillespie guides you through this powerful feature that allows you to standardize and optimize images across your projects with ease.\u003C\u002Fp>\n\u003Cp>What you'll learn:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Understanding transformation presets and their benefits\u003C\u002Fli>\n\u003Cli>How to access and configure transformation settings in Directus\u003C\u002Fli>\n\u003Cli>Creating custom presets for width, format, and special effects\u003C\u002Fli>\n\u003Cli>Applying presets to images using simple URL parameters\u003C\u002Fli>\n\u003Cli>Balancing flexibility and control with transformation options\u003C\u002Fli>\n\u003Cli>Real-world use cases for standardizing images in web applications\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Whether you're managing a blog, e-commerce site, or complex web application, this episode provides valuable insights into streamlining your image handling process. Learn how to create presets that ensure consistency, improve load times with modern formats like WebP, and even apply custom transformations like color tinting.\u003C\u002Fp>","a142eef1-b2ac-48e7-8b53-d4add5047669",[2206],"4c955a81-4deb-4d16-940d-fe54a4f3307f",[],{"year":978,"number":2150,"id":2151,"episodes":2209,"show":2210},[2153,2154,2135,2155,2156,2157,2158,2159],{"title":2161,"slug":2162},{"id":2158,"slug":2212,"vimeo_id":2213,"description":2214,"tile":2215,"length":213,"resources":12,"people":12,"episode_number":129,"published":2140,"title":2216,"video_transcript_html":2217,"video_transcript_text":2218,"content":2219,"seo":2220,"status":19,"episode_people":2221,"recommendations":2223,"season":2224},"version-control","983671293","Learn how to make stress-free edits to published content without affecting the live version. Bryant walks you through how to enable content versioning in Directus and how to create and manage different versions of content.","54340f51-3329-4209-8cae-5589ac7d6f33","Version Control","\u003Cp>Speaker 0: Alright. And welcome back to another episode of Short Hops. I'm your host, Brian Gillespie. In this episode, we are gonna cover content versioning. So what is content versioning?\u003C\u002Fp>\u003Cp>Basically it allows you to do stress free editing on articles or content or pages, whatever you're serving up, that you've already published. Right? We've all been through it. Hey, we need to make some edits to a page. I need to be able to save those edits, but I don't wanna push those live until we get approval or we've had time to vet them out.\u003C\u002Fp>\u003Cp>So content versioning allows you to basically make changes and edits without affecting the published version of a piece of content. Now how do we set that up? Right? I am in a sample Directus instance. This is one of my blog posts, a very nice piece by mister Carrot Gillespie, about steampunk rabbits and why they are the future of work.\u003C\u002Fp>\u003Cp>So I've got some edits that I wanna make to this, but this is already published. So any edits that I make, if I save, could potentially show up live on our site. So content versioning to the rescue. How do we enable this? We go to our settings.\u003C\u002Fp>\u003Cp>We go to the collection we would like to enable versioning for. And we'll go down to the settings for this and we'll find the content versioning header. We'll check the box. I'm sure you know what that will do. And now we'll go back to our post.\u003C\u002Fp>\u003Cp>So at the top we can see now we have this little badge for main. So that is the main piece of content. That's the the main version that we are going to publish. So if I wanna make some changes here, I could just go and create a new version. This is, BG edits, And the key is important here.\u003C\u002Fp>\u003Cp>We'll just call this Bryant's edits. Great. I've created that new version. Now I could go in and make all the changes I want here. This looks pretty good.\u003C\u002Fp>\u003Cp>Okay. So what I can do now is save this version and I've got 2 versions here. I can see Bryant's edits. I could potentially, you know, send this to somebody on our team, ask them to review it. And then once I am ready to promote or publish this version, we've got a a function here called promote version.\u003C\u002Fp>\u003Cp>So basically I'm gonna click promote version. This will then show me a difference between what is published and what is the, the edits that I'm making. And here I can confirm which version should be used for each of these individual fields. So if I don't want to overwrite the summary, I can, you know, use whatever is in the the main version here. Or likewise, in this case, I want to overwrite everything entirely.\u003C\u002Fp>\u003Cp>I'll hit this and I've got the option to keep or delete. I could, keep this change, and now those changes that I make are now published in the main content version. Now when I'm accessing versions via the API, especially on like a front end or something, let's say I'm trying to fetch this piece of content. So I'm just gonna go to items slash posts and I could see the post here. Right?\u003C\u002Fp>\u003Cp>If I were to go in and, you know, create a new version, let's just call it test version, and here's some more changes. We'll just save. Oh, actually, let's switch that. Here's the changes. And we'll save this version.\u003C\u002Fp>\u003Cp>So now I'm working on the test version. If I run that API call again, you could see I don't see those changes. Right? But if I am calling an individual piece of content, I can add a version param to this. So I could say version equals test, and I could see here's the changes that we have.\u003C\u002Fp>\u003Cp>Right? Great. So, that is content versioning. There's also a separate API endpoint for this as well if you wanna get all the individual content versions and it does support all the regular global parameters. So make sure you check out the documentation on this on how to leverage it further.\u003C\u002Fp>\u003Cp>That's it for this episode. We'll catch you on the next one.\u003C\u002Fp>","Alright. And welcome back to another episode of Short Hops. I'm your host, Brian Gillespie. In this episode, we are gonna cover content versioning. So what is content versioning? Basically it allows you to do stress free editing on articles or content or pages, whatever you're serving up, that you've already published. Right? We've all been through it. Hey, we need to make some edits to a page. I need to be able to save those edits, but I don't wanna push those live until we get approval or we've had time to vet them out. So content versioning allows you to basically make changes and edits without affecting the published version of a piece of content. Now how do we set that up? Right? I am in a sample Directus instance. This is one of my blog posts, a very nice piece by mister Carrot Gillespie, about steampunk rabbits and why they are the future of work. So I've got some edits that I wanna make to this, but this is already published. So any edits that I make, if I save, could potentially show up live on our site. So content versioning to the rescue. How do we enable this? We go to our settings. We go to the collection we would like to enable versioning for. And we'll go down to the settings for this and we'll find the content versioning header. We'll check the box. I'm sure you know what that will do. And now we'll go back to our post. So at the top we can see now we have this little badge for main. So that is the main piece of content. That's the the main version that we are going to publish. So if I wanna make some changes here, I could just go and create a new version. This is, BG edits, And the key is important here. We'll just call this Bryant's edits. Great. I've created that new version. Now I could go in and make all the changes I want here. This looks pretty good. Okay. So what I can do now is save this version and I've got 2 versions here. I can see Bryant's edits. I could potentially, you know, send this to somebody on our team, ask them to review it. And then once I am ready to promote or publish this version, we've got a a function here called promote version. So basically I'm gonna click promote version. This will then show me a difference between what is published and what is the, the edits that I'm making. And here I can confirm which version should be used for each of these individual fields. So if I don't want to overwrite the summary, I can, you know, use whatever is in the the main version here. Or likewise, in this case, I want to overwrite everything entirely. I'll hit this and I've got the option to keep or delete. I could, keep this change, and now those changes that I make are now published in the main content version. Now when I'm accessing versions via the API, especially on like a front end or something, let's say I'm trying to fetch this piece of content. So I'm just gonna go to items slash posts and I could see the post here. Right? If I were to go in and, you know, create a new version, let's just call it test version, and here's some more changes. We'll just save. Oh, actually, let's switch that. Here's the changes. And we'll save this version. So now I'm working on the test version. If I run that API call again, you could see I don't see those changes. Right? But if I am calling an individual piece of content, I can add a version param to this. So I could say version equals test, and I could see here's the changes that we have. Right? Great. So, that is content versioning. There's also a separate API endpoint for this as well if you wanna get all the individual content versions and it does support all the regular global parameters. So make sure you check out the documentation on this on how to leverage it further. That's it for this episode. We'll catch you on the next one.","\u003Cp>In this episode of Short Hops, host Bryant Gillespie explains content versioning in Directus. Learn how to make stress-free edits to published content without affecting the live version.\u003C\u002Fp>\n\u003Cp>Bryant walks you through:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>What content versioning is and why it's useful\u003C\u002Fli>\n\u003Cli>How to enable content versioning in Directus\u003C\u002Fli>\n\u003Cli>Creating and managing different versions of content\u003C\u002Fli>\n\u003Cli>Promoting edited versions to replace the main published version\u003C\u002Fli>\n\u003Cli>Accessing specific versions via the API\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Perfect for content managers, developers, and anyone looking to streamline their content editing process. Discover how to make changes confidently and collaborate effectively using Directus's powerful versioning features.\u003C\u002Fp>","1e57efe3-b06c-4a0e-bb18-e34a2b4ca08a",[2222],"0ab97766-5821-4f3a-b192-7c38cf1288af",[],{"year":978,"number":2150,"id":2151,"episodes":2225,"show":2226},[2153,2154,2135,2155,2156,2157,2158,2159],{"title":2161,"slug":2162},{"id":2156,"slug":2228,"vimeo_id":2229,"description":2230,"tile":2231,"length":159,"resources":12,"people":12,"episode_number":213,"published":2140,"title":2232,"video_transcript_html":2233,"video_transcript_text":2234,"content":2235,"seo":2236,"status":19,"episode_people":2237,"recommendations":2239,"season":2240},"dynamic-relational-interfaces","983671908","Learn how to create a more intuitive user experience in Directus by implementing dynamic filters on relational interfaces. This episode teaches you to set up dependent field relationships, allowing users to see only relevant options when selecting related items. ","0eb0d695-342c-447c-b406-bc7980733cae","Dynamic Relational Interfaces","\u003Cp>Speaker 0: Welcome back to another episode of Short Hops. I'm your host Brian Gillespie. And in this episode, I'm gonna give you one of the coolest tips and tricks that I've probably ever shared here on Short Ops. So stick with me on this one. It's really cool.\u003C\u002Fp>\u003Cp>Alright. So what are we focused on today? We're going to build a better user experience using filters on our relational interfaces. That is a mouthful, kind of underwhelming title. But let's dive into an example project and I'll show you what we've got going on.\u003C\u002Fp>\u003Cp>So this is a sample Directus instance. This is my Agency OS project that we put out under the Directus Labs organization. Be sure to check that out. But I wanna show you the scenario. So I'm creating a project in here, and we've got an organization, and we could add multiple contacts to this.\u003C\u002Fp>\u003Cp>So a a project belongs to an organization. Let's go with Tesla, in this case, pretty ambiguous name. And then I want to add an existing contact to this. But you could see here, here's my problem. If I take a look, I get all the contacts for this.\u003C\u002Fp>\u003Cp>So, you know, I'm only wanting to be able to select contacts that are at Tesla. So this is kind of a dependency here. How do we manage something like this? So I'm just going to open the settings, we're going to go into our data model. I'm going to look for the projects collection.\u003C\u002Fp>\u003Cp>And here inside organization, we've got contacts right beside it. What I'm looking for, you know, we could see the relationship tab and how all these things come together. But when I go into the interface, and this is a mini to mini collection or relationship, so you'll have to be mindful of how you've got your relationship set up. But if I want to set that up dependent upon a field that we already have, I could do something like this. Down here in my filter, you know, I could go in and and like hard code a value of, you know, show me all the contacts where the name contains Ashley.\u003C\u002Fp>\u003Cp>But we want this to be dynamic, right, so I'm gonna drill into our organizations. This is a many to many relationship. And I'm gonna say organizations dot id equals, and I could do this. So I'm gonna do mustache brackets here, and I'm gonna do organization, which is the name of that other field inside our data model. Alright.\u003C\u002Fp>\u003Cp>Great. So let's take a look at this and we'll go back, we'll create a new project. Right now if I click add existing, no contacts show up. How come? Right?\u003C\u002Fp>\u003Cp>So what I could do here, we'll say Directus test. That's the company we wanna do this project for. And now I only see the contacts who are part of that organization. Right? Likewise, if I change this to Tesla, I open it up and I see I've got Elon Dusk here as the only option to select.\u003C\u002Fp>\u003Cp>So that's it. Take a look at the filter on any of the relational interfaces that you've set up, this is a a nice little technique to use in certain scenarios like this one. That's it for this episode. Stay tuned for the next one.\u003C\u002Fp>","Welcome back to another episode of Short Hops. I'm your host Brian Gillespie. And in this episode, I'm gonna give you one of the coolest tips and tricks that I've probably ever shared here on Short Ops. So stick with me on this one. It's really cool. Alright. So what are we focused on today? We're going to build a better user experience using filters on our relational interfaces. That is a mouthful, kind of underwhelming title. But let's dive into an example project and I'll show you what we've got going on. So this is a sample Directus instance. This is my Agency OS project that we put out under the Directus Labs organization. Be sure to check that out. But I wanna show you the scenario. So I'm creating a project in here, and we've got an organization, and we could add multiple contacts to this. So a a project belongs to an organization. Let's go with Tesla, in this case, pretty ambiguous name. And then I want to add an existing contact to this. But you could see here, here's my problem. If I take a look, I get all the contacts for this. So, you know, I'm only wanting to be able to select contacts that are at Tesla. So this is kind of a dependency here. How do we manage something like this? So I'm just going to open the settings, we're going to go into our data model. I'm going to look for the projects collection. And here inside organization, we've got contacts right beside it. What I'm looking for, you know, we could see the relationship tab and how all these things come together. But when I go into the interface, and this is a mini to mini collection or relationship, so you'll have to be mindful of how you've got your relationship set up. But if I want to set that up dependent upon a field that we already have, I could do something like this. Down here in my filter, you know, I could go in and and like hard code a value of, you know, show me all the contacts where the name contains Ashley. But we want this to be dynamic, right, so I'm gonna drill into our organizations. This is a many to many relationship. And I'm gonna say organizations dot id equals, and I could do this. So I'm gonna do mustache brackets here, and I'm gonna do organization, which is the name of that other field inside our data model. Alright. Great. So let's take a look at this and we'll go back, we'll create a new project. Right now if I click add existing, no contacts show up. How come? Right? So what I could do here, we'll say Directus test. That's the company we wanna do this project for. And now I only see the contacts who are part of that organization. Right? Likewise, if I change this to Tesla, I open it up and I see I've got Elon Dusk here as the only option to select. So that's it. Take a look at the filter on any of the relational interfaces that you've set up, this is a a nice little technique to use in certain scenarios like this one. That's it for this episode. Stay tuned for the next one.","\u003Cp>In this episode of Short Hops, host Bryant Gillespie shares an invaluable tip for improving the user experience in Directus projects. Learn how to implement dynamic filters on relational interfaces, creating more intuitive and efficient data entry processes.\u003C\u002Fp>\n\u003Cp>What you'll learn:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Understanding the challenge of managing related data in complex structures\u003C\u002Fli>\n\u003Cli>How to set up dependent field relationships in Directus\u003C\u002Fli>\n\u003Cli>Implementing dynamic filters on many-to-many relationships\u003C\u002Fli>\n\u003Cli>Using mustache syntax to create context-aware filters\u003C\u002Fli>\n\u003Cli>Practical demonstration using a real-world project management scenario\u003C\u002Fli>\n\u003C\u002Ful>","014f5dc9-1c15-4db0-adae-425834f62e90",[2238],"3553bb41-b085-40b8-ab7c-ee118829e345",[],{"year":978,"number":2150,"id":2151,"episodes":2241,"show":2242},[2153,2154,2135,2155,2156,2157,2158,2159],{"title":2161,"slug":2162},{"id":2157,"slug":2244,"vimeo_id":2245,"description":2246,"tile":2247,"length":213,"resources":12,"people":12,"episode_number":195,"published":2140,"title":2248,"video_transcript_html":2249,"video_transcript_text":2250,"content":2251,"seo":2252,"status":19,"episode_people":2253,"recommendations":2255,"season":2256},"dynamic-variables","983671644","Learn to use dynamic variables like $CURRENT_USER, $CURRENT_ROLE, and $NOW to create personalized views and time-based queries both in the app interface and API calls. ","6eee710c-cce7-4302-889b-841998b61be1","Dynamic Variables","\u003Cp>Speaker 0: Alright. Welcome back to another episode of Short Hops. I'm your host, Brian Gillespie. In this episode, we are talking about your friends who make filtering easier, dynamic variables. Dynamic variables, you can use these in any API calls when you are doing some filtering.\u003C\u002Fp>\u003Cp>You can also use these inside the app. And I'll show you a couple of quick use cases for them. But we'll just run through the most popular ones. These are the main ones. We have dollar sign current underscore user.\u003C\u002Fp>\u003Cp>That will give you the current ID of a particular logged in user. Current role will give you the current role ID of a logged in user, of the current logged in user. And then we have dollar sign now which will give you the current timestamp and you can do adjustments with this as well like now minus 1 year, now plus 2 hours. And I'll I'll show you how to get the most out of these in the application here. Alright, so let's take a look at a specific scenario.\u003C\u002Fp>\u003Cp>I'm inside a sample app. I've got a contacts table here. Some of these would be assigned to me, some of them would not be. But if I take a look at it, just a a standard kind of CRM format. So now if I wanted to take a look at all of my contacts, and maybe what I want to do is put a bookmark in that every user has access to, a global bookmark.\u003C\u002Fp>\u003Cp>But it's dynamic in that when they click on it, it shows their specific contacts. So the bookmark is is globally available, but it is filtered based on the current user. So how can we do something like that? Right? I can go in and I'm gonna set a filter here, let's say user created equals dollar sign underscore current user.\u003C\u002Fp>\u003Cp>And now, I get a list of my specific contacts or the ones that I created. Right? You know, I could also, you know, have the owner of my organization as well. So if the owner of the organization equals current user, that is my list. Right?\u003C\u002Fp>\u003Cp>And now I can go in and actually bookmark this and I may call it something like my contacts, because we can make this globally available. Great. So that is one way to use this. Let's take the scenario where I want to, you know, show me all the posts that have been published within the last month. Right, so again, I can use those dynamic variables to make filtering easier because I, you know, I don't wanna do the math and figure out what it was a month ago.\u003C\u002Fp>\u003Cp>So I'm just gonna go in, date published is greater than and we'll do dollar sign now minus 1 month. And boom we could see that. And again, these same filters you can use within the API as well. So if I just pull up just a little API tool called Bruno that I've been using. And we are calling these posts.\u003C\u002Fp>\u003Cp>So if we just get the post resource, we could see we're getting all of our posts here. This is just items slash posts. Now if I add a param for the date published, and let's do gt or underscore gt, which is greater than. And we'll do that dollar sign now. We'll do that same kind of query that we used.\u003C\u002Fp>\u003Cp>1 month. And here you could see I'm only getting like 1 or 2 posts. Or if I wanted to see everything that's published within the last day, boom. So that's how this works. That is dynamic variables.\u003C\u002Fp>\u003Cp>A lot you can do with these. So make sure you check them out. Read up on the documentation. And we'll see you in the next episode.\u003C\u002Fp>","Alright. Welcome back to another episode of Short Hops. I'm your host, Brian Gillespie. In this episode, we are talking about your friends who make filtering easier, dynamic variables. Dynamic variables, you can use these in any API calls when you are doing some filtering. You can also use these inside the app. And I'll show you a couple of quick use cases for them. But we'll just run through the most popular ones. These are the main ones. We have dollar sign current underscore user. That will give you the current ID of a particular logged in user. Current role will give you the current role ID of a logged in user, of the current logged in user. And then we have dollar sign now which will give you the current timestamp and you can do adjustments with this as well like now minus 1 year, now plus 2 hours. And I'll I'll show you how to get the most out of these in the application here. Alright, so let's take a look at a specific scenario. I'm inside a sample app. I've got a contacts table here. Some of these would be assigned to me, some of them would not be. But if I take a look at it, just a a standard kind of CRM format. So now if I wanted to take a look at all of my contacts, and maybe what I want to do is put a bookmark in that every user has access to, a global bookmark. But it's dynamic in that when they click on it, it shows their specific contacts. So the bookmark is is globally available, but it is filtered based on the current user. So how can we do something like that? Right? I can go in and I'm gonna set a filter here, let's say user created equals dollar sign underscore current user. And now, I get a list of my specific contacts or the ones that I created. Right? You know, I could also, you know, have the owner of my organization as well. So if the owner of the organization equals current user, that is my list. Right? And now I can go in and actually bookmark this and I may call it something like my contacts, because we can make this globally available. Great. So that is one way to use this. Let's take the scenario where I want to, you know, show me all the posts that have been published within the last month. Right, so again, I can use those dynamic variables to make filtering easier because I, you know, I don't wanna do the math and figure out what it was a month ago. So I'm just gonna go in, date published is greater than and we'll do dollar sign now minus 1 month. And boom we could see that. And again, these same filters you can use within the API as well. So if I just pull up just a little API tool called Bruno that I've been using. And we are calling these posts. So if we just get the post resource, we could see we're getting all of our posts here. This is just items slash posts. Now if I add a param for the date published, and let's do gt or underscore gt, which is greater than. And we'll do that dollar sign now. We'll do that same kind of query that we used. 1 month. And here you could see I'm only getting like 1 or 2 posts. Or if I wanted to see everything that's published within the last day, boom. So that's how this works. That is dynamic variables. A lot you can do with these. So make sure you check them out. Read up on the documentation. And we'll see you in the next episode.","\u003Cp>In this episode of Short Hops, host Bryant Gillespie introduces you to the power of dynamic variables in Directus. Learn how these versatile tools can significantly enhance your filtering capabilities, allowing you to create more personalized and time-sensitive data views with ease.\u003C\u002Fp>\n\u003Cp>What you'll learn:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Understanding the concept and benefits of dynamic variables in Directus\u003C\u002Fli>\n\u003Cli>Overview of key dynamic variables: $CURRENT_USER, $CURRENT_ROLE, and $NOW\u003C\u002Fli>\n\u003Cli>Using dynamic variables in the Directus app interface\u003C\u002Fli>\n\u003Cli>Implementing dynamic variables in API calls\u003C\u002Fli>\n\u003Cli>Creating user-specific views and global bookmarks with personalized results\u003C\u002Fli>\n\u003Cli>Utilizing time-based queries for content management\u003C\u002Fli>\n\u003C\u002Ful>","7c0faac8-c4a5-42fe-a6a8-5199de342e6c",[2254],"59719367-7875-4483-a727-162fd24e9064",[],{"year":978,"number":2150,"id":2151,"episodes":2257,"show":2258},[2153,2154,2135,2155,2156,2157,2158,2159],{"title":2161,"slug":2162},{"id":2155,"slug":2260,"vimeo_id":2261,"description":2262,"tile":2263,"length":159,"resources":12,"people":12,"episode_number":159,"published":2140,"title":2264,"video_transcript_html":2265,"video_transcript_text":2266,"content":2267,"seo":2268,"status":19,"episode_people":2269,"recommendations":2271,"season":2272},"focal-point","983672154","Learn to maintain the focus of your images during asset transformations, enhancing the visual appeal of your website or application.","9031652a-9389-473a-9f8b-a5473b3fc8bc","Focal Point","\u003Cp>Speaker 0: And welcome back to another episode of Short Hops. I'm your host, Brian Gillespie. And in this episode, I wanna talk about the focal point feature inside Directus. What is a focal point? It's basically just a way for you to keep your image, your the focus of your image in frame when you do asset transformation like cropping at different image sizes, different image aspect ratios.\u003C\u002Fp>\u003Cp>Basically, you wanna keep folks in frame. So what does that actually look like? Right? When you are using Directus in a CMS use case, like this is powering content for our website or our application and we're serving images from that. Directus has a robust asset transformation.\u003C\u002Fp>\u003Cp>So I can do all types of cropping and formatting for our images, compositing, you know, color manipulation if I need to. But you want your website to look amazing. And part of that is images. Right? So you could see here in this case, I've got a article card here, a blog post card and our rabbit is actually getting cut off.\u003C\u002Fp>\u003Cp>We don't want that. Right? The focal point feature inside Directus comes to the rescue here. So let's open up our Directus instance and I just want to showcase this, right? So we've got an image over here of a coach tying a soccer player's shoes or a football for anybody outside the states.\u003C\u002Fp>\u003Cp>And, you could see that this should be the obvious focal point of the image, but it's kind of not in the center. Right? So by default, we're gonna crop from the center. But, here down below, I can see focal point, I have an x coordinate and a y coordinate. When we leave those things blank, if I just open up this image in a new tab, and we do something like this where we adjust the width and height of this image.\u003C\u002Fp>\u003Cp>Width equals, let's say, 400 and we wanna look at taller, skinnier image. So I'm gonna do the height at 800. And immediately what you could see is we we lose context. Right? Our football player is halfway cut off.\u003C\u002Fp>\u003Cp>We don't even see the coach. How can we fix that? That is where the focal point comes into play. So you can manually adjust this or there's even a nice extension that, uses AI to automatically generate this. But the easiest way to edit the focal point is just go to the edit button on the top right hand side.\u003C\u002Fp>\u003Cp>We'll see the image editor over here. There is the focal point tool. I'll click that, that'll give me a little focal point highlight here. And I'll just place that over the elements that I want to focus on and just expand that, right? So I wanna make sure I get the player and the coach, and we'll just save that.\u003C\u002Fp>\u003Cp>We can see those x and y coordinates have been updated. And then I'm just gonna simply refresh this image and now that focal point, the coach and his player, are in frame, are in focus. So that is the focal point tool inside Directus. Stay tuned for the next episode of Short Hops.\u003C\u002Fp>","And welcome back to another episode of Short Hops. I'm your host, Brian Gillespie. And in this episode, I wanna talk about the focal point feature inside Directus. What is a focal point? It's basically just a way for you to keep your image, your the focus of your image in frame when you do asset transformation like cropping at different image sizes, different image aspect ratios. Basically, you wanna keep folks in frame. So what does that actually look like? Right? When you are using Directus in a CMS use case, like this is powering content for our website or our application and we're serving images from that. Directus has a robust asset transformation. So I can do all types of cropping and formatting for our images, compositing, you know, color manipulation if I need to. But you want your website to look amazing. And part of that is images. Right? So you could see here in this case, I've got a article card here, a blog post card and our rabbit is actually getting cut off. We don't want that. Right? The focal point feature inside Directus comes to the rescue here. So let's open up our Directus instance and I just want to showcase this, right? So we've got an image over here of a coach tying a soccer player's shoes or a football for anybody outside the states. And, you could see that this should be the obvious focal point of the image, but it's kind of not in the center. Right? So by default, we're gonna crop from the center. But, here down below, I can see focal point, I have an x coordinate and a y coordinate. When we leave those things blank, if I just open up this image in a new tab, and we do something like this where we adjust the width and height of this image. Width equals, let's say, 400 and we wanna look at taller, skinnier image. So I'm gonna do the height at 800. And immediately what you could see is we we lose context. Right? Our football player is halfway cut off. We don't even see the coach. How can we fix that? That is where the focal point comes into play. So you can manually adjust this or there's even a nice extension that, uses AI to automatically generate this. But the easiest way to edit the focal point is just go to the edit button on the top right hand side. We'll see the image editor over here. There is the focal point tool. I'll click that, that'll give me a little focal point highlight here. And I'll just place that over the elements that I want to focus on and just expand that, right? So I wanna make sure I get the player and the coach, and we'll just save that. We can see those x and y coordinates have been updated. And then I'm just gonna simply refresh this image and now that focal point, the coach and his player, are in frame, are in focus. So that is the focal point tool inside Directus. Stay tuned for the next episode of Short Hops.","\u003Cp>In this episode of Short Hops, host Bryant Gillespie explores the powerful focal point feature in Directus. Learn how this tool can significantly improve the visual quality of your website or application by ensuring that the key elements of your images remain in frame, even when cropped or resized.\u003C\u002Fp>\n\u003Cp>What you'll learn:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Understanding the concept of focal points in image management\u003C\u002Fli>\n\u003Cli>How focal points affect image cropping and transformations\u003C\u002Fli>\n\u003Cli>Setting up and adjusting focal points in Directus\u003C\u002Fli>\n\u003Cli>Practical demonstration using a real-world example\u003C\u002Fli>\n\u003Cli>Improving image presentation across different sizes and aspect ratios\u003C\u002Fli>\n\u003Cli>Tips for optimizing your images for various layouts and devices\u003C\u002Fli>\n\u003C\u002Ful>","22dc323c-b8a7-4156-a058-7f6d19e732c9",[2270],"065d18bb-3c9f-49a7-916f-eb4418da66b9",[],{"year":978,"number":2150,"id":2151,"episodes":2273,"show":2274},[2153,2154,2135,2155,2156,2157,2158,2159],{"title":2161,"slug":2162},{"id":1797,"slug":2276,"vimeo_id":2277,"description":2278,"tile":2279,"length":2280,"resources":2281,"people":12,"episode_number":527,"published":2284,"title":2285,"video_transcript_html":2286,"video_transcript_text":2287,"content":12,"seo":2288,"status":19,"episode_people":2289,"recommendations":2293,"season":2294},"19659","986515873","Rijk, Jonathan and special guest Hannes discuss WYSIWYG Linking Existing Files.","4aa280eb-c685-4d24-b8d0-96477cd047d9",52,[2282],{"name":1775,"url":2283},"https:\u002F\u002Fgithub.com\u002Fdirectus\u002Fdirectus\u002Fdiscussions\u002F19659","2024-07-25","WYSIWYG Linking Existing Files","\u003Cp>Speaker 0: Welcome everybody once more. We're ready to rock. Welcome to request review. Daniel is out this week, so we got a very special guest for you here. Say hello.\u003C\u002Fp>\u003Cp>Hello. Hello. Good day.\u003C\u002Fp>\u003Cp>Speaker 1: Thomas. Welcome to the welcome to the phone.\u003C\u002Fp>\u003Cp>Speaker 2: Welcome to\u003C\u002Fp>\u003Cp>Speaker 0: the having me. So today, we're gonna be chatting about yet another feature request. And this time, Jonathan, what have we pulled up?\u003C\u002Fp>\u003Cp>Speaker 1: We have the lovely improving the way that files are injected in the WYSIWYG editors, is the discussion for today. I believe the extensions team may actually be taking this on behind the scenes in a in a short term solution. But for now, it'd be nice to understand the requirements, the the gotchas, the whys and wherefores, the way that current the way we currently add links and images and videos within a WYSIWYG field. So you can edit links, move your links, take a look at your links here. But according to the to the request, they'd like this to be a little more fully featured in the way that the tags and things are added around the HTML under the hood.\u003C\u002Fp>\u003Cp>It's my understanding. But I'm no front end expert, so let's see what we can figure out and decipher.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. So this I can feel it. This is not one of those where it feels like, oh, that that should just be one button. Right? That's easy enough to do.\u003C\u002Fp>\u003Cp>Alright. So starting at the basics and pulling it up, if you seem to look enough to decide over here, by the way, I just added the same thing up, but a little bit bigger. The goal here is basically just like you can put in an image, you wanna link to a file. Right? Easy enough, you\u003C\u002Fp>\u003Cp>Speaker 1: think.\u003C\u002Fp>\u003Cp>Speaker 0: Inserting a link to a file in its purest form shouldn't be too crazy because we know what the IDs of the file. We know where you would find it slash assets. It's the same as an image. Right? Mhmm.\u003C\u002Fp>\u003Cp>Just as with an image though, the question immediately becomes permissions? Big one. Right? The user that is inserting the file has permissions to see those files and then select it to be linked. But that doesn't mean that the person on the other end that is that's seeing this HTML rendered on a website or something also has the permissions to that same file.\u003C\u002Fp>\u003Cp>Right? And we've seen this before with images.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I was trying to say it's the same problem with images. Right? Because images share the same\u003C\u002Fp>\u003Cp>Speaker 0: properties. Yeah. So the way we do it for images right now is we do have that option where you can sort of hard code in one of the the tokens if that's the way you wanna do it. You could, of course, make permissions a little leaner for reading images. Although that way, you know, it's a little bit more than permission set up.\u003C\u002Fp>\u003Cp>Not ideal. You can have people log in, of course, on your own front end and then use that for permissions. But it feels like even with the link to files, it might get a bit worse because at least with an image, you can immediately see if it doesn't work because you get a broken image. Right? Because you get a permissions issue.\u003C\u002Fp>\u003Cp>So as the person that's implementing it, it becomes a little bit more obvious what's going on. But if you just have a link and the link doesn't work for some, but it works for others, that's tricky. That's tricky. So I think as per usual for for those who haven't seen one of these before, the way this oftentimes works is that, you know, we take the smallest feature request that that is in here. The what is the the status?\u003C\u002Fp>\u003Cp>What is the minimum viable? Then convergently think about, okay, what scope does this live in? What are all the edge cases that we have to think about? What are the problems that will come up? And then sort of take it back down to, okay, what can we actually do today?\u003C\u002Fp>\u003Cp>Right? And what is the long term plan and how do we get there? So I think to me, an important one for to make this one work as expected is all around permissions. So I'm actually very glad that you're here, Thomas, because you just work a lot on permissions as part of this, v 11 release in the RC that is out now, in the GA coming up very soon. Any immediate thoughts on the permission set up for this?\u003C\u002Fp>\u003Cp>Speaker 2: Especially with files, I do worry that giving read access to a lot of files like you suggested previously is a big foot gun for people because you might store private files. You might store exports from collections or something in your files and giving, like, real access to any of them feels wrong to do it as a blanket or something. That's like a blanket permission. We we, the best possible way would be to surface that information somehow. Like, how visible is a link or how visible is, like, something you link to, since even if you're testing out the link on your live preview or something, you still might be logged into your CMS behind the scenes.\u003C\u002Fp>\u003Cp>So for you, the link also works. So you have would have to test it in a private window or something. But then again, we could show you, hey. This is how the public what the public permissions for this file look like. Users might not be able to access it.\u003C\u002Fp>\u003Cp>On the other hand, we don't know about your setup. Right? So we don't know, are you actually just visiting those with the public public permissions? Or are you adding a token? Or are you doing some other stuff with it since we don't know really what is the implementation that relies on it.\u003C\u002Fp>\u003Cp>So it's hard to think about a catch all solution that would help people not shoot their foot. So right now with images, we do it. We basically let you do anything with it, and don't hold your hand while doing it. And I wonder if that is also the solution in this case. So just provide more functionality.\u003C\u002Fp>\u003Cp>But if stuff breaks on your end, you have to be aware of it. Seems could be a documentation issue, but then again, the people reading our documentation aren't actually the users using the product in the end maybe.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It's a tricky one. And and to your point\u003C\u002Fp>\u003Cp>Speaker 2: of, like, where do you wanna put this education?\u003C\u002Fp>\u003Cp>Speaker 0: Right. Yeah. And and to your point, we when we had this issue for images, the conclusion was, well, you know what? Let's just make all the options available and and put the burden of setup on the the end user to make sure that they're using it right instead of on us to to try to help you with it. It did this made me, think of another feature request that we I don't think we've discussed quite yet here, but it gets come up a couple of times in the past, which is the nested relational permissions, so to speak, where you could say, files are inaccessible by default, but you can allow file reads if they are being read through another relational field.\u003C\u002Fp>\u003Cp>So for example, you could say files are off limits, but if you look up an avatar through a user, you can still access that one avatar in some of its fields, but only if you go in relationally through a user.\u003C\u002Fp>\u003Cp>Speaker 2: How does it work with the assets endpoint though?\u003C\u002Fp>\u003Cp>Speaker 0: Right. That's where it gets interesting because I'm I'm just wondering coming from. Yeah. No. Exactly.\u003C\u002Fp>\u003Cp>I'm just curious if there is an angle to that, because it made me think of this where I'm like, oh, if you assign an image or an asset into a WYSIWYG blob, maybe we can have it rely on a similar idea of permissions where it's like, okay. The things you make available within, one of those fields can be read in a certain way, but not others. Again, just convergently thinking about it.\u003C\u002Fp>\u003Cp>Speaker 1: Do you have any other issues? Sorry. Good. I I don't wanna distract you from a thought. So go ahead, Hans.\u003C\u002Fp>\u003Cp>Speaker 2: The the issue I'm seeing with that is, again, like, accessing some data of that asset, totally fine. I can see that work, how it would be set up in permissions.\u003C\u002Fp>\u003Cp>Speaker 1: I\u003C\u002Fp>\u003Cp>Speaker 2: would be able to do, like, the relational reversal of permissions or something or for items. But since the asset endpoint doesn't know about it, we are still at some point where we why would this user be able to see this? Because then we would have to look up. Are there any permissions that could allow it? And so it gets it gets really complicated on the asset end point.\u003C\u002Fp>\u003Cp>Like, the actual, like, file permissions isn't too much of a problem, but all of the metadata, but the whole, assets itself isn't too trivial to figure out Yeah. Image for that one.\u003C\u002Fp>\u003Cp>Speaker 0: Maybe. Again, divergently thinking about this. Maybe the, there's an in between where we can do kinda like what what s 3 supports and and some of those storage providers where you have, like, a signed link that is unique to where it was used. So we can know from the way the asset was accessed, what field in what collection it was accessed from. And then that way, we can do the relational permissions thingy instead of relying on having to search.\u003C\u002Fp>\u003Cp>Because to your point, if you open an asset and we have to search through every single field to see if there's any chance that you used it, that feels crazy. That feels insane. But if we make it a unique link that is unique to that collection of field combination, then that simplifies it a little bit. Because now we do know A\u003C\u002Fp>\u003Cp>Speaker 2: lot of things.\u003C\u002Fp>\u003Cp>Speaker 0: Where it was accessed from. Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: Which effectively could be something like an access token for that.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Effectively. So so it could be it's it's effectively just just a token with that connects the dots between you're trying to access this image or this asset through an HTML blob, that is on your website. And because we generated a custom link for it, we know what that blob is, and then we can also help you debug, you know, if your file got, is being accessed from sources that you didn't expect it to, we can show you. It's like, well, you got it read from your your HTML body and this and this article or something.\u003C\u002Fp>\u003Cp>Right?\u003C\u002Fp>\u003Cp>Speaker 1: Interesting. I like it. That was my thinking as well was either inherit permissions for the item, but then as Hannes noted that the asset endpoint has no idea about that currently. Now that doesn't mean that we don't enhance or find a way, kind of a unique URL or a unique identifier that indicates where this is being used. We've actually seen this.\u003C\u002Fp>\u003Cp>There's another request out there about knowing what what files are associated with which items. Right?\u003C\u002Fp>\u003Cp>Speaker 0: It's Right. Right.\u003C\u002Fp>\u003Cp>Speaker 1: We we have this problem right now without writing some very complex hook logic that track tracks through a whole set of relationals. You know, we don't because that asset endpoint is kind of independent of all of that and files kind of live independent of all of that, The way that the relational is unless you're forcing relational reverse creation, which then makes your file through, you know, direct us files gets really ugly really fast that way. We have this it's a fairly common request that we see from clients of, I wanna know what my files are associated with. Right? I wanna be able to see that and understand that.\u003C\u002Fp>\u003Cp>So This\u003C\u002Fp>\u003Cp>Speaker 0: is this is also an interesting topic in the whole security versus usability piece. Because on on from from a user experience and even a developer experience, if I have a, public HTML blob and I put an image in there, I kinda assume that that image is now also in that same public blob. Right? So I would assume it to work. At the same time, if you if if we were to make it that way where it's like, oh, whenever you put an image inside of a HTML thing, it's now public.\u003C\u002Fp>\u003Cp>You can accidentally expose a whole lot of stuff. Right? Because it's it's a little opaque under the hood. From a security perspective, it's a little tricky.\u003C\u002Fp>\u003Cp>Speaker 2: Is it though? I guess again, it's really one of those, like, what are all the different use cases Directus is used in kind of problem. And, like, who are the actual end users and who has who has been educated about that problem, caveat, whatever we wanna call it. Because I I generally like the idea like, it doesn't really have anything to do with that, feature request in general since that seems a lot more low level. But I do like the general idea of having something like a unique or signed, asset URL, which can be used to conditionally allow access to something.\u003C\u002Fp>\u003Cp>Speaker 1: And so generating something could\u003C\u002Fp>\u003Cp>Speaker 2: be go for it. Sorry.\u003C\u002Fp>\u003Cp>Speaker 1: No. No. I was just curious. So when you're generating, you know, like, this this input screen right here, so you'd have a unique URL that's being generated here, right, in some way, shape, or form, whether that's a tag on the end of the, you know, asset endpoint that gives you a question mark, you know, unique key. That has to be stored then.\u003C\u002Fp>\u003Cp>Is that stored then as part of the director's files or a relational to the director's files kind of a system table where we store those unique IDs? And not so much implementation. Just I I'm thinking about so you'd have that ID. But you'd also be able to see that here. Could you also or optionally ensure that you give, you know, an access token option here?\u003C\u002Fp>\u003Cp>So as an actual field input where you could supply an access token or even a default access token for as part of the admin configuration. Because once again, I don't necessarily want users putting in access tokens, but have a way for the administrators to get to apply an access token that has the appropriate permissions for the file.\u003C\u002Fp>\u003Cp>Speaker 0: That is the key piece there. That last that last little snippet of the sentence is the most important crucially important part that has the appropriate permissions for the files. The foot gun with the excess token is that somebody just puts in their own users' access token, YOLO it, and then it works. But now you're technically exposing that all over the place, right?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Because once you're, you're putting that excess token in the image and HTML, it's just a query parameter. So therefore, wherever you render it, somebody can see it. You have to consider it public.\u003C\u002Fp>\u003Cp>Speaker 2: See it. They can\u003C\u002Fp>\u003Cp>Speaker 0: So what you're effectively doing is you're not using the public role for those images. You're just using a token that you then make public. Right? So that last part is why the sort of developer or user experience or both. I mean, just the experience as a whole for using the static tokens as way to get access to those asset in a link or an image or otherwise.\u003C\u002Fp>\u003Cp>There's a couple of food guns, right, that you gotta be aware of that people may or may not be. Because again, that's one of those things where, you know, if you explain it, that makes a lot of sense. But if you don't know and you just wrongly assume you could put in any access token, you can be in a in a world of trouble. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Absolutely. K.\u003C\u002Fp>\u003Cp>Speaker 2: Which makes me wonder if that is something I haven't really thought about all the ash cases, but it's something probably we can solve by using a j signed JWT or something that just scopes it to that specific item in a collection, then we don't even have to store that in the database, assuming that you have access to that. Because then in the asset endpoint, we could check, hey, does the user like, this request actually have access to that item, which is signed in the JWT? And that way, we can verify the access. And if that basically overrides whatever, like, more restrictive permissions you have, you would be good to go only for that specific item where that image is linked in.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So we're basically saying\u003C\u002Fp>\u003Cp>Speaker 2: Linked or whatever.\u003C\u002Fp>\u003Cp>Speaker 0: Try to summarize that so I understand that we're basically saying if you attach an image or an a file, a general asset, any asset to an item in a WYSIWYG or relational interfaces, maybe even anywhere else. Well, relational interface, I guess not because then you just have a data point, not a file. But if you have an asset link inside of a Wizzywig or a block editor, some of those markdown, we use a signed link that contains what collection and item it came from. And then we can use the permissions for that item instead of the permission for files. So therefore, if you have read access to that HTML field, you defect don't have read access to the images within it.\u003C\u002Fp>\u003Cp>That feels like a very clean setup as far as I'm concerned.\u003C\u002Fp>\u003Cp>Speaker 2: Again, probably also food counts with exposing files or people linking to files, which should be internal at that point. It's more of a shooting your own foot than more, of a case where we are trying to prevent it the best we can. Yeah. But that's,\u003C\u002Fp>\u003Cp>Speaker 1: that's kind of already here anyway. Right? Because you can restrict the files and folders that this has access to. Right? In the in the actual WYSIWYG config, you can, I I think Do\u003C\u002Fp>\u003Cp>Speaker 2: the root folder or something? It's what we do. Right?\u003C\u002Fp>\u003Cp>Speaker 1: It should only be what this user has access to. Right? So if I add\u003C\u002Fp>\u003Cp>Speaker 2: Add in general. Yeah. Yeah. For sure.\u003C\u002Fp>\u003Cp>Speaker 1: If I add an image, you know, I can add or edit. I don't wanna do that.\u003C\u002Fp>\u003Cp>Speaker 2: I wanna So your your condominiums and images.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. This is my my permissions restrict me to whatever I have access to, and so I shouldn't be able to technically insert something that I shouldn't be able to now. Could get a little more complex, but I like the that was my thinking and couple of notes that I took. I want a way to restrict the permissions to based on the item's permissions. Yep.\u003C\u002Fp>\u003Cp>So I like the\u003C\u002Fp>\u003Cp>Speaker 2: change that we do to go with Amazon. Yep. It's interesting. And it's it also could be something that is just a tick mark or something in that dialogue, which can say, hey. Create a signed link or create an unsigned link because people might not want to have that additional check, but just generally make stuff public or something.\u003C\u002Fp>\u003Cp>But it could be an option either for the end user to decide or for the admin setting it up to decide. I feel like that is a good way of doing it also to introduce it in a non breaking way if you ever wanna introduce it in just a feature release.\u003C\u002Fp>\u003Cp>Speaker 1: Judd's asking if we could display make it so that it's visible in the sense of you'd be able to see who has access to that data. This is not great. Can we do that? If if we're\u003C\u002Fp>\u003Cp>Speaker 2: doing it with a token, probably not.\u003C\u002Fp>\u003Cp>Speaker 1: No. But if we use, like, the scope JWT, would you be able to see so if I was in here looking at this, would I be able to see could you display here who has access? That seems crazy.\u003C\u002Fp>\u003Cp>Speaker 2: Whoever has access to, like, whatever item you're inserting this into has access to that file in or something. So it's not something where we have either this user, like, harnesses you in your file or something, or or do they do, like, color eagle? Or I don't know. So I didn't at least with the route we're planning on going or considering going, that wouldn't be the case.\u003C\u002Fp>\u003Cp>Speaker 0: Although, now I'm thinking about it. If you have the we use the permissions for the item for the asset endpoint, you still have a similar case where you could render the HTML somewhere for a person that's not logged in. Although, I guess, at that point, it's it's more expected. But you still Oh, you\u003C\u002Fp>\u003Cp>Speaker 2: mean, like, service side rendering or something where you have Yeah. Token or, like, some service level token that actually does the rendering and you still break your links?\u003C\u002Fp>\u003Cp>Speaker 0: Yep.\u003C\u002Fp>\u003Cp>Speaker 2: I mean, at that point at that point, you wanna do like, you wanna manually track what is publicly available or something, I feel like.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I'm I'm just wondering because still divergently thinking what we can do to make this just very obvious in how it works, but still secure at the same time. Like, I'm also thinking an alternative, and this is just completely other idea. But if there is a native dedicated public folder in the file library, just a different root folder in the file library that is always public, you make it super obvious that, oh, if you wanna put a public image in your article, pull it from the public folder, right, or move it there first to make it public and then use it in your article.\u003C\u002Fp>\u003Cp>Speaker 2: Now I'm wondering if that is way too CMS specific.\u003C\u002Fp>\u003Cp>Speaker 0: It is. Although, I guess, yeah, public sharing of files, yes, no, yes, no, maybe. I don't know. Tricky. I know a lot of dams\u003C\u002Fp>\u003Cp>Speaker 2: are all public by default, which\u003C\u002Fp>\u003Cp>Speaker 0: is also a strange idea.\u003C\u002Fp>\u003Cp>Speaker 2: Lot of what? Sorry.\u003C\u002Fp>\u003Cp>Speaker 0: I know a a couple of dam providers that default to everything being public for ease of use and then you have the opposite where you can mark certain files as private instead of the other way around. But that is also\u003C\u002Fp>\u003Cp>Speaker 2: I mean, we do allow you to conf to set that up. Right? We do allow you to set up permissions. Hey. You're allowed to access files which are located within this folder\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: Through public permissions, public policies, whatever you wanna do it with. So we already have that. It's just not there by default.\u003C\u002Fp>\u003Cp>Speaker 1: We tried again, I'm kind of okay with that from, I I I don't think we have nobody complains about the ability to make something public. It's more so the one. The other thing we should make sure we come back to in this, is it's also about the way it's being embedded. I think the HTML coding that's actually being used, making it easier to con\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. That is something we've glanced over so far. Right? Because\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. We're more concerned. Security is to me the the first thing we should be thinking about.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Because for an image, it's kind of there's an obvious way of how to do it. But for an a tag, I mean, there are some standard attributes we wanna put in there. Yeah. But now the question is, like, what is a standard way of linking to something?\u003C\u002Fp>\u003Cp>It's probably an a tag, but is it something that we want to make configurable if use people are using it in service side reactor ending, if you're rendering whatever? Or is that so much out of scope that we say, if you're doing this, you better use the block editor and do your custom rendering pipeline or something or use markdown and go with that? Is it something where we wanna be really open to not rendering just an a tag, but something user defined? Because in the initial request, it looks like it's the requesting only a tags or link tags, whatever. I haven't really been able to see that in the request there.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Here. Well, that's an alternative. This is what they're doing as an alternative. They're doing some things strangely behind\u003C\u002Fp>\u003Cp>Speaker 2: the scenes. Rendered as oh, I think it's a typo. The link should be rendered as an tag. So as a tag maybe in the first sentence or the first paragraph, the motivation.\u003C\u002Fp>\u003Cp>Speaker 0: I think that's supposed to be as an as an anchor tag. Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: So what is this considered currently when we're inserting? Because this is an h ref. Right?\u003C\u002Fp>\u003Cp>Speaker 2: It's an image.\u003C\u002Fp>\u003Cp>Speaker 0: Is it it's an anchor text. I mean, for for images, it's an image. And for for links, it's just an anchor, an a tag.\u003C\u002Fp>\u003Cp>Speaker 2: But a link is not something where we directly allow linking to any records or something. Right? It's just a insert user.\u003C\u002Fp>\u003Cp>Speaker 0: And it's just a link. Insert a link.\u003C\u002Fp>\u003Cp>Speaker 2: Yep. Yeah. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: So so this feature request in its base in most basic form, it's just when I click add a link, allow me to choose a file instead of add a link. That's that's that's bare bones what this is saying.\u003C\u002Fp>\u003Cp>Speaker 1: So the same way that we can do image the image and media because they're applying specific tags. But if you're just adding a tag, right, so if I just say I wanna, you you know, make this a link or if I'm just creating a link, I have to specify the URL. So what they\u003C\u002Fp>\u003Cp>Speaker 0: want is the to a static file, you'd now have to type in mydirectors.com\u002Fasset\u002Fwhatever the ID is. And and this is this in its simplest form, this is basically just saying, give me a link to a file.\u003C\u002Fp>\u003Cp>Speaker 1: Give me a link to a file, same as when you're doing this, where you're inserting an image and you're searching. So they want the they want this regardless of image or anything else. Got\u003C\u002Fp>\u003Cp>Speaker 0: it. It's basically the exact same flows in certain image except it pops in a link instead of an inline embed.\u003C\u002Fp>\u003Cp>Speaker 1: So you'd have another you'd have essentially another option here that's, you know, insert a direct as file or insert a file. Right? And maybe the image library filters and searches. Got it.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Which means we immediately derailed this discussion by going, hey. What about permissions?\u003C\u002Fp>\u003Cp>Speaker 0: Exactly. That's that's the beauty of these these sessions, Thomas. Welcome aboard.\u003C\u002Fp>\u003Cp>Speaker 1: Welcome to our crazy party. I still like the idea of making this this better. Right? So making this more understandable and more usable.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Image images within WYSIWYG are are infinitely. Because I have another topic if you wanna just derail it a little bit more on this at the other the other way, which is migrations to and from different URLs. If you change the URL of your direct installation, this technically breaks Yeah. It's paint.\u003C\u002Fp>\u003Cp>Speaker 1: Actually, I it happened on this very record. I popped this open in preparation for this this morning, and I was like, why is my image not showing up? And I right clicked and it was I'd actually migrated this from, another project, a while back.\u003C\u002Fp>\u003Cp>Speaker 2: You see.\u003C\u002Fp>\u003Cp>Speaker 1: And now my URL was just back\u003C\u002Fp>\u003Cp>Speaker 2: in just this morning.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. So when I was looking at the, you know, the link here, I had to actually adjust this to my correct URL. So this is a this is a known issue when we're doing, like, data migrations and things. We've got, our support team has some scripts and things that we'll go through and find try to find and replace known values here. But it's a\u003C\u002Fp>\u003Cp>Speaker 0: It's a tricky problem to solve because it's it's by definition, the WYSIWYG one is the trickiest because it is HTML. It's not a pro like a readable format necessarily. I mean, you know.\u003C\u002Fp>\u003Cp>Speaker 2: It's something that is like easily solved in I don't know. Like, for example, our block editor or something, right?\u003C\u002Fp>\u003Cp>Speaker 0: Right. Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: Image could\u003C\u002Fp>\u003Cp>Speaker 0: just be nested in JSON or something\u003C\u002Fp>\u003Cp>Speaker 2: you can even learn back. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Because you're also dealing with an ID and not a full link. That helps.\u003C\u002Fp>\u003Cp>Speaker 2: It's best case best case, it shouldn't even include the link. Like, I don't know what our block editor does, to be honest, but best case, it should just be a acid link or something that just includes the ID of that item you're interested in or that acid.\u003C\u002Fp>\u003Cp>Speaker 0: Right. The tricky bit is if you plop that into a browser, nothing shows up. Yeah. It doesn't\u003C\u002Fp>\u003Cp>Speaker 2: know what to do with it. Yeah. Yeah. For sure.\u003C\u002Fp>\u003Cp>Speaker 0: So you, you have very tricky, tricky setup where that is that is always an issue. And and I'm more than happy because we we have a little bit of time like Lockheed here to to try to to noodle on some ideas for ways around that. Because, you know, some some more CMS specific platforms, they use, like, short codes or something within the HTML. Right? Where it's just like bracket asset ID this and then go figure it out in in post processing.\u003C\u002Fp>\u003Cp>That is an option, but that requires the user of\u003C\u002Fp>\u003Cp>Speaker 2: the Custom.\u003C\u002Fp>\u003Cp>Speaker 0: The output to do something, or we have to have some sort of custom post processing set up for some fields where we say, oh, if you have an HTML field, we'll search through short codes and and execute them. Interesting. Or Yeah. Because custom web components.\u003C\u002Fp>\u003Cp>Speaker 2: Custom web components. We could provide a set of custom web components that is a director's image and that knows your CMS location and then just needs the ID, which is effectively what we're doing for Nuxt. Right? There is, like, a country like a Nuxt image module direct as Nuxt module, which is, like, the base URL, and then it just knows the image ID and figures the rest out.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: So that could be an option for the WYSIWYG that it's like, hey, if you're using a custom image resolver or something, you could just insert the ID in the link.\u003C\u002Fp>\u003Cp>Speaker 0: You can't use the face tag outside of the head, could you, in HTML?\u003C\u002Fp>\u003Cp>Speaker 2: Oh, don't ask me.\u003C\u002Fp>\u003Cp>Speaker 0: I don't think so. I think base is only in the head. The for that that's just I'm not just thinking HTML brain because you were saying it should just be, like, a relative link instead of a full URL. More or less, you were talking just the ID. But I'm like, if you make it slash assets slash the ID, then it will work within Directus presumably, but it will not in your own website.\u003C\u002Fp>\u003Cp>But a base tag in HTML says, you know, graphics every single link to everything with this domain, which could be work around for that. Right? Where you just have a base tag that says, okay, make the base tag, the direct installation, and then it would just work. But I think a base\u003C\u002Fp>\u003Cp>Speaker 2: tag breaks every relative link on your Right. Right.\u003C\u002Fp>\u003Cp>Speaker 0: Right. I was gonna say breaks every relative link in your whole package. That's what I'm saying. If only there was a locally scoped base tag where you can just say only within the main tag of my Dom is the base tag something else. But, yeah, I don't think that's gonna be a good solution for this.\u003C\u002Fp>\u003Cp>Custom web component is an interesting one too because it requires, you know, a bit of custom JavaScript that needs to happen on every single website that wants to use this, and it's just just a bit of yeah. It is\u003C\u002Fp>\u003Cp>Speaker 2: one possibility. Yep.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I think, if if we if we rely on more of a short code, based approach with some post processing, then you make the migration between projects really lightweight, assuming there's a way to skip the post processing. And then the API output could still be the full URL, but we rely on a short code in the database. So therefore, the value that we store is just like bracket asset ID with all the parameters that you want, and then we generate those into images on the fly. But it's just a bit of a I don't know.\u003C\u002Fp>\u003Cp>It's\u003C\u002Fp>\u003Cp>Speaker 2: It it doesn't feel like our responsibility necessarily. Yeah. That's kind of why I'm struggling. Same with, the public folder or something. That that feels too much like forcing one way of solving it on people.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. You're not\u003C\u002Fp>\u003Cp>Speaker 1: wrong. You're\u003C\u002Fp>\u003Cp>Speaker 0: not wrong. Tricky bum. Tricky bum. Oh, one more thing related and related, maybe a bit of both. For images, we just plop in an image tag.\u003C\u002Fp>\u003Cp>Right? That's that's what what it does. Do we want to support any other file types that are in line? Because videos images are in line, but if you link to a PDF file or something, Do we want\u003C\u002Fp>\u003Cp>Speaker 1: to It comes up. We we see that request as well as additional, like, inline viewers. Right? PDFs.\u003C\u002Fp>\u003Cp>Speaker 0: Right.\u003C\u002Fp>\u003Cp>Speaker 1: Docs, you know, spreadsheets, whatever. Being able to actually have a preview of what that looks like.\u003C\u002Fp>\u003Cp>Speaker 0: The tricky thing with those things is that they're all custom front end frameworks or plugins or libraries or web components. It's not it's not a browser standard. So if we wanna go that route, we would have to have some sort of way where you can configure what markup it spits out if you insert a PDF. Because then you get to choose between, oh, when when I'm inserting a PDF, I'm gonna insert my little HTML markup that spits out a PDF preview or something instead of just the link.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. So there's that's that's another common thing that we see on those types of fields. Right? So if I go to paragraph here\u003C\u002Fp>\u003Cp>Speaker 2: Which already screams use a freaking block editor or anything other than a wizarding question.\u003C\u002Fp>\u003Cp>Speaker 0: I you're not wrong. You're not wrong.\u003C\u002Fp>\u003Cp>Speaker 1: So but, again, we support, you know, on some level. Right? Whether this is TinyMC or TipTap or name your editor. People have built their own interfaces here. There's a number of marketplace interfaces around this where the underlying libraries actually are more than we're currently exposing.\u003C\u002Fp>\u003Cp>And the question is, can we could we have interfaces have more composability or more extensibility kind of pre exposed, that you could then adapt. You can when the users, you know, when the administrators are setting these up, you could have custom, menu options. Right? So if you could actually I I know tinymc supports it because we've done it. Right?\u003C\u002Fp>\u003Cp>We've got the image and the video editor buttons that are there, and they inspect the appropriate tags. I think Tiny MC and Tiptap both support this ability to have an override or a templating option where you can adjust the configuration, add new buttons, add new drop down.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, sure. Yeah. We could we could support that for, you know, file insert as well. If we have an insert button that we say, oh, if you're inserting a type of a PDF or something that we, you know, we render it in a different way. It's just so much setup that you end up having to do.\u003C\u002Fp>\u003Cp>Right? So right now, indirect is you would have to do that per field, which will be kind of annoying. So we would have to add some sort of global defaults as well for for interfaces to make that a little bit more editable.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. And that's why, again, I think some of that we're actually looking at. It's for the extensions team, the the directus labs team to take a look at actually doing some tests on creating some new interfaces or modifying faces Yeah. To give us an idea of what's possible, what would what would work well, and then we can decide down the line whether we adapt that and roll that back into the core interface options as well. Yeah.\u003C\u002Fp>\u003Cp>But\u003C\u002Fp>\u003Cp>Speaker 0: Or or use a block editor. Like, there's there's, of course, you know, when it comes to and this is this is huge breaking changes and that's something we will do anytime soon, but there's also the argument to be made that is maybe we shouldn't be dealing with HTML at all. I I know it's it's the sort of easiest path to website, but from a data format perspective and for a data portability perspective and a migration perspective and, using it in non web platforms perspective, it is objectively not the right tool for the job, I think.\u003C\u002Fp>\u003Cp>Speaker 2: Yep. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yep.\u003C\u002Fp>\u003Cp>Speaker 0: I think markdown is slightly better. You still have to render it out yourself and you still have some of these migration problems, but at least, you know, renderers for markdown exist across, you know, output types, iOS development, web development, etcetera. But as far as I'm concerned, July 2024, you know, a block editor that returns it in in JSON is the most friendly way to actually render it out across different output vendors. Right? So there's also a world where we can say, maybe we don't concern ourselves with an HTML editor at all.\u003C\u002Fp>\u003Cp>We really focus on having one really good block editor, be it based on, tip- tab, shout out to chat, or or anything else, and then offer some utilities to have that transform to HTML, just to make your life a little bit easier, but not try to save it and manage it as HTML within directness.\u003C\u002Fp>\u003Cp>Speaker 1: So like an on the fly, the API endpoint would dynamically transform the block editor JSON format into HTML as an option parameter.\u003C\u002Fp>\u003Cp>Speaker 2: Right.\u003C\u002Fp>\u003Cp>Speaker 1: Clever. Clever.\u003C\u002Fp>\u003Cp>Speaker 0: Because that way we get around a lot of these these issues effectively all of them by just not dealing with HTML.\u003C\u002Fp>\u003Cp>Speaker 1: No.\u003C\u002Fp>\u003Cp>Speaker 2: Are there, folks are doing it as well to provide you with JavaScript libraries to convert to HTML. It doesn't even need to be server side. Though server side seems like a cool option, but, there are libraries out there for converting, like, adjacent tree to whatever, and you can have custom elements. You can render them customly within your frameworks, all of that. I've done that personally as well.\u003C\u002Fp>\u003Cp>Like, I've worked with abstract documents as a JSON and rendered them in different frameworks with different components, all of that. And it just makes it a lot nicer to handle if you're willing to spend the effort. But that is kind of the catch of it. Right? HTML is just super easy, but it doesn't give you all the nice options which you would have if you dealt with it in a more advanced way.\u003C\u002Fp>\u003Cp>Yeah. But probably it's the reason why we have Wizzy Week in the first place But it's it leaves you with certain limits. Yeah. It leaves you with certain limits.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. No. I've got people that are, you know, they they\u003C\u002Fp>\u003Cp>Speaker 2: Which makes me always wonder, like\u003C\u002Fp>\u003Cp>Speaker 1: Go ahead.\u003C\u002Fp>\u003Cp>Speaker 2: No. Go for it.\u003C\u002Fp>\u003Cp>Speaker 1: No. I was just saying I've seen people copying and pasting in Wizzy, you know, HTML from other sources and breaking their actual web page.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Because they've got CSS and other things that they're trying to override, and it's being overridden. And they're complaining that, oh, my fonts don't match on this thing that I copied and pasted from some third party source. Right? Think Oh,\u003C\u002Fp>\u003Cp>Speaker 0: that's a big thing. Work. Oh.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It's I I actually dealt with this just recently for one of our and for one of our clients, one of our actually, a a legacy Ranger Studios client, that we still kind of help out and monitor and support their site. And they came complaining that the fonts weren't matching on articles that they were that they just published. And they said, sometimes it works and sometimes it doesn't. And I was initially, I'm thinking there's some I'm digging through\u003C\u002Fp>\u003Cp>Speaker 2: Some of the code on the site. Google Docs and sometimes go into And\u003C\u002Fp>\u003Cp>Speaker 1: and then I'm looking at the Wizzio, and then I happen I'm like, what are all these spans and these classes don't exist in our code.\u003C\u002Fp>\u003Cp>Speaker 0: What are yeah. And\u003C\u002Fp>\u003Cp>Speaker 1: then I real and then I started removing so and suddenly the formatting for the correct. It was picking up the correct font, but they were it was this font code for this HTML was overriding all of the underlying CSS that was configured for the site. So yeah. Yeah. It's a nightmare.\u003C\u002Fp>\u003Cp>Speaker 2: Okay. That leaves us with one more feature request after this feature request review, which is how do we get shareable, like, permission permissions baked into an asset link, which makes it easier to manage permissions for anything you link in your editor. And with the agreement that it's probably cool idea and a good idea to include a, a, an, button form, whatever in there that allows you to create an 8 anchor tag to any asset in directors.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I think that would be a great one to have. And to your point, that's a new feature of choice.\u003C\u002Fp>\u003Cp>Speaker 2: Have anyone in the audience who wanna wants to tag on to that.\u003C\u002Fp>\u003Cp>Speaker 0: Now the good news is of this session is that all of these things that we described so far, they're all non blockers or supporting the MVP of this, which is insert link to file, which is fantastic, which means that I wanna say, okay. Let's just do it. Let's just do it. It's improvement. It's a new feature, but small one, you know, PRs are welcome.\u003C\u002Fp>\u003Cp>It's a good first issue. Let's just do it. Like, the the minimum viable of this? Sure.\u003C\u002Fp>\u003Cp>Speaker 2: Is it something we only wanna do in the week, or is it also something we wanna support in the\u003C\u002Fp>\u003Cp>Speaker 0: That is a good question. I don't think it's relevant for the block editor because, a file is filed. You can render it out as a link or an image however you want for the markdown interface though. I think it's relevant.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Because currently, it's just a URL link. Right? You've got the add in an image. I don't see do we support video in markdown?\u003C\u002Fp>\u003Cp>I There's markdown, and that's\u003C\u002Fp>\u003Cp>Speaker 0: There's really no such yeah. There's there's no native markdown for video.\u003C\u002Fp>\u003Cp>Speaker 1: K.\u003C\u002Fp>\u003Cp>Speaker 0: So not really. There's also no HTML attributes in markdown. There's a lot of things that are in markdown.\u003C\u002Fp>\u003Cp>Speaker 1: That's okay. So, technically, we just need the file link. I'm putting a note in the ticket here in the in the discussion here. Go ahead. Go on this.\u003C\u002Fp>\u003Cp>Speaker 2: Alright. Same question for the block editor. Is it also something where we wanna support it as well? Since we might want feature parity between between all of our editor interfaces.\u003C\u002Fp>\u003Cp>Speaker 1: Agree.\u003C\u002Fp>\u003Cp>Speaker 2: I don't even know what the block editor is capable of doing right now.\u003C\u002Fp>\u003Cp>Speaker 1: So it's got image.\u003C\u002Fp>\u003Cp>Speaker 2: Like we do image and attachment. What is attachment? Is is attachment already what we're trying to do?\u003C\u002Fp>\u003Cp>Speaker 0: I think it is. Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: It might be. Yeah?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I think it is.\u003C\u002Fp>\u003Cp>Speaker 1: Already is. Yep. So the only thing we because,\u003C\u002Fp>\u003Cp>Speaker 0: the block editor natively supports that. The the library that we use for that just has it. Yep. Yep. The Wizzy tiny MCE does not doesn't have a concept of that yet, so we have to edit edit ourselves.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. Yeah. So you get select the file, and I guess the the one thing that I noticed is it doesn't have video. Should block editor have video? Or is it just file as a file as a file for these guys outside of the images that have captions?\u003C\u002Fp>\u003Cp>That's a good question. Let's see. Do I have video first deal in here? It's just in some video and see what the preview or window. I don't know what that just did.\u003C\u002Fp>\u003Cp>Oh, it downloaded. No. I have a duplicate copy of that. Lovely. So should it have so for parity, does does the block editor support video?\u003C\u002Fp>\u003Cp>Speaker 2: Seems like it's just an attachment. Right?\u003C\u002Fp>\u003Cp>Speaker 0: I think it's just an attachment.\u003C\u002Fp>\u003Cp>Speaker 1: It's just doing it as an attachment right now, but should it have a video interface the same as the HTML does? No. Maybe. Or a video blank. Just for, like, again, feature parity across the where where possible.\u003C\u002Fp>\u003Cp>Speaker 0: I think there's a separate plugin for editor JS, which is what we use in the for the block editor, but we'll have to try it out. Different feature request for a different day.\u003C\u002Fp>\u003Cp>Speaker 1: All good. So we already have file, so the only place that this needs to be done is. Markdown in HTML. Right?\u003C\u002Fp>\u003Cp>Speaker 2: And I think the block edit can just treat whatever attachment the way it sees fit.\u003C\u002Fp>\u003Cp>Speaker 1: I will put a note Just\u003C\u002Fp>\u003Cp>Speaker 2: for videos in the WEEZY week, do we just insert a video tag, or how does it work?\u003C\u002Fp>\u003Cp>Speaker 1: Let's have a look.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: I'll go test it.\u003C\u002Fp>\u003Cp>Speaker 2: Yep. K. That's what I figured. Wizardwig. Also, what kind of fan fancy collection is it that you have there, Jonathan?\u003C\u002Fp>\u003Cp>My test my test collections always have all 3 of the editors in there. You guys can actually show and hide it. That's cool.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So I'm using the conditional rules. I use it for demos. So I have this in my Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: My CMS demo essentially where I've got a page a paragraph component. And then depending on, you know, if they're a WYSIWYG shop or they're a markdown shop or they're a block header, just being able to show off the different interfaces quickly and easily, but show and hide them. And it shows off the conditional rule capabilities of the app as well. So\u003C\u002Fp>\u003Cp>Speaker 0: It's cool. My my debugging instance is just one of each on every page.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So so you do get a video player in Wizziweb. Yeah. It's yeah. I think\u003C\u002Fp>\u003Cp>Speaker 0: it's just a video tag. If you check out the raw HTML of that, it's it's literally Giant\u003C\u002Fp>\u003Cp>Speaker 2: kitchen sink collection. We everything in there.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. I have including just a flat text area. So all of our kind\u003C\u002Fp>\u003Cp>Speaker 2: of Yeah. So it probably is a video. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. So it actually renders it with Under the hood. With video HTML tags accordingly. So the question is, does the block editor and or we know markdown doesn't support it, but does the block editor support something similar? Right now we're just inserting it as a file.\u003C\u002Fp>\u003Cp>But if the block editor technically supports video, it would be nice to have parity to have the equivalency where video renders nicely. So I'll just put a note.\u003C\u002Fp>\u003Cp>Speaker 0: Anyways, taking this back slightly more on track. This feature request as a whole, easy. I think it's easy easy to to accept it. That that's what I was going for. That's so much easy to implement necessarily.\u003C\u002Fp>\u003Cp>Easy to accept it. I think it's we should just do it. It's a good first issue. I think it's great for for PRs, if anybody wants to take a swing at this. I think there's a lot of new ideas and new feature requests that are worth discussing more.\u003C\u002Fp>\u003Cp>Like, I think this signed link approach to permissions is a very, very interesting one that will be very relevant for something like this. But with that being said, the eye on the clock and me also have a hard stop, unfortunately. I wanna say thank you very much for watching once more. We'll be back soon. Soon, Asia.\u003C\u002Fp>\u003Cp>It was a it was a minute since last time, but we'll be back, I believe in 2 weeks, and if not, soon. Make sure to check out this show on direct. Io\u002Ftv. Your request review. If you like doing this, somebody like having to get here.\u003C\u002Fp>","Welcome everybody once more. We're ready to rock. Welcome to request review. Daniel is out this week, so we got a very special guest for you here. Say hello. Hello. Hello. Good day. Thomas. Welcome to the welcome to the phone. Welcome to the having me. So today, we're gonna be chatting about yet another feature request. And this time, Jonathan, what have we pulled up? We have the lovely improving the way that files are injected in the WYSIWYG editors, is the discussion for today. I believe the extensions team may actually be taking this on behind the scenes in a in a short term solution. But for now, it'd be nice to understand the requirements, the the gotchas, the whys and wherefores, the way that current the way we currently add links and images and videos within a WYSIWYG field. So you can edit links, move your links, take a look at your links here. But according to the to the request, they'd like this to be a little more fully featured in the way that the tags and things are added around the HTML under the hood. It's my understanding. But I'm no front end expert, so let's see what we can figure out and decipher. Alright. So this I can feel it. This is not one of those where it feels like, oh, that that should just be one button. Right? That's easy enough to do. Alright. So starting at the basics and pulling it up, if you seem to look enough to decide over here, by the way, I just added the same thing up, but a little bit bigger. The goal here is basically just like you can put in an image, you wanna link to a file. Right? Easy enough, you think. Inserting a link to a file in its purest form shouldn't be too crazy because we know what the IDs of the file. We know where you would find it slash assets. It's the same as an image. Right? Mhmm. Just as with an image though, the question immediately becomes permissions? Big one. Right? The user that is inserting the file has permissions to see those files and then select it to be linked. But that doesn't mean that the person on the other end that is that's seeing this HTML rendered on a website or something also has the permissions to that same file. Right? And we've seen this before with images. Yeah. I was trying to say it's the same problem with images. Right? Because images share the same properties. Yeah. So the way we do it for images right now is we do have that option where you can sort of hard code in one of the the tokens if that's the way you wanna do it. You could, of course, make permissions a little leaner for reading images. Although that way, you know, it's a little bit more than permission set up. Not ideal. You can have people log in, of course, on your own front end and then use that for permissions. But it feels like even with the link to files, it might get a bit worse because at least with an image, you can immediately see if it doesn't work because you get a broken image. Right? Because you get a permissions issue. So as the person that's implementing it, it becomes a little bit more obvious what's going on. But if you just have a link and the link doesn't work for some, but it works for others, that's tricky. That's tricky. So I think as per usual for for those who haven't seen one of these before, the way this oftentimes works is that, you know, we take the smallest feature request that that is in here. The what is the the status? What is the minimum viable? Then convergently think about, okay, what scope does this live in? What are all the edge cases that we have to think about? What are the problems that will come up? And then sort of take it back down to, okay, what can we actually do today? Right? And what is the long term plan and how do we get there? So I think to me, an important one for to make this one work as expected is all around permissions. So I'm actually very glad that you're here, Thomas, because you just work a lot on permissions as part of this, v 11 release in the RC that is out now, in the GA coming up very soon. Any immediate thoughts on the permission set up for this? Especially with files, I do worry that giving read access to a lot of files like you suggested previously is a big foot gun for people because you might store private files. You might store exports from collections or something in your files and giving, like, real access to any of them feels wrong to do it as a blanket or something. That's like a blanket permission. We we, the best possible way would be to surface that information somehow. Like, how visible is a link or how visible is, like, something you link to, since even if you're testing out the link on your live preview or something, you still might be logged into your CMS behind the scenes. So for you, the link also works. So you have would have to test it in a private window or something. But then again, we could show you, hey. This is how the public what the public permissions for this file look like. Users might not be able to access it. On the other hand, we don't know about your setup. Right? So we don't know, are you actually just visiting those with the public public permissions? Or are you adding a token? Or are you doing some other stuff with it since we don't know really what is the implementation that relies on it. So it's hard to think about a catch all solution that would help people not shoot their foot. So right now with images, we do it. We basically let you do anything with it, and don't hold your hand while doing it. And I wonder if that is also the solution in this case. So just provide more functionality. But if stuff breaks on your end, you have to be aware of it. Seems could be a documentation issue, but then again, the people reading our documentation aren't actually the users using the product in the end maybe. Yeah. It's a tricky one. And and to your point of, like, where do you wanna put this education? Right. Yeah. And and to your point, we when we had this issue for images, the conclusion was, well, you know what? Let's just make all the options available and and put the burden of setup on the the end user to make sure that they're using it right instead of on us to to try to help you with it. It did this made me, think of another feature request that we I don't think we've discussed quite yet here, but it gets come up a couple of times in the past, which is the nested relational permissions, so to speak, where you could say, files are inaccessible by default, but you can allow file reads if they are being read through another relational field. So for example, you could say files are off limits, but if you look up an avatar through a user, you can still access that one avatar in some of its fields, but only if you go in relationally through a user. How does it work with the assets endpoint though? Right. That's where it gets interesting because I'm I'm just wondering coming from. Yeah. No. Exactly. I'm just curious if there is an angle to that, because it made me think of this where I'm like, oh, if you assign an image or an asset into a WYSIWYG blob, maybe we can have it rely on a similar idea of permissions where it's like, okay. The things you make available within, one of those fields can be read in a certain way, but not others. Again, just convergently thinking about it. Do you have any other issues? Sorry. Good. I I don't wanna distract you from a thought. So go ahead, Hans. The the issue I'm seeing with that is, again, like, accessing some data of that asset, totally fine. I can see that work, how it would be set up in permissions. I would be able to do, like, the relational reversal of permissions or something or for items. But since the asset endpoint doesn't know about it, we are still at some point where we why would this user be able to see this? Because then we would have to look up. Are there any permissions that could allow it? And so it gets it gets really complicated on the asset end point. Like, the actual, like, file permissions isn't too much of a problem, but all of the metadata, but the whole, assets itself isn't too trivial to figure out Yeah. Image for that one. Maybe. Again, divergently thinking about this. Maybe the, there's an in between where we can do kinda like what what s 3 supports and and some of those storage providers where you have, like, a signed link that is unique to where it was used. So we can know from the way the asset was accessed, what field in what collection it was accessed from. And then that way, we can do the relational permissions thingy instead of relying on having to search. Because to your point, if you open an asset and we have to search through every single field to see if there's any chance that you used it, that feels crazy. That feels insane. But if we make it a unique link that is unique to that collection of field combination, then that simplifies it a little bit. Because now we do know A lot of things. Where it was accessed from. Yeah. Which effectively could be something like an access token for that. Yeah. Effectively. So so it could be it's it's effectively just just a token with that connects the dots between you're trying to access this image or this asset through an HTML blob, that is on your website. And because we generated a custom link for it, we know what that blob is, and then we can also help you debug, you know, if your file got, is being accessed from sources that you didn't expect it to, we can show you. It's like, well, you got it read from your your HTML body and this and this article or something. Right? Interesting. I like it. That was my thinking as well was either inherit permissions for the item, but then as Hannes noted that the asset endpoint has no idea about that currently. Now that doesn't mean that we don't enhance or find a way, kind of a unique URL or a unique identifier that indicates where this is being used. We've actually seen this. There's another request out there about knowing what what files are associated with which items. Right? It's Right. Right. We we have this problem right now without writing some very complex hook logic that track tracks through a whole set of relationals. You know, we don't because that asset endpoint is kind of independent of all of that and files kind of live independent of all of that, The way that the relational is unless you're forcing relational reverse creation, which then makes your file through, you know, direct us files gets really ugly really fast that way. We have this it's a fairly common request that we see from clients of, I wanna know what my files are associated with. Right? I wanna be able to see that and understand that. So This is this is also an interesting topic in the whole security versus usability piece. Because on on from from a user experience and even a developer experience, if I have a, public HTML blob and I put an image in there, I kinda assume that that image is now also in that same public blob. Right? So I would assume it to work. At the same time, if you if if we were to make it that way where it's like, oh, whenever you put an image inside of a HTML thing, it's now public. You can accidentally expose a whole lot of stuff. Right? Because it's it's a little opaque under the hood. From a security perspective, it's a little tricky. Is it though? I guess again, it's really one of those, like, what are all the different use cases Directus is used in kind of problem. And, like, who are the actual end users and who has who has been educated about that problem, caveat, whatever we wanna call it. Because I I generally like the idea like, it doesn't really have anything to do with that, feature request in general since that seems a lot more low level. But I do like the general idea of having something like a unique or signed, asset URL, which can be used to conditionally allow access to something. And so generating something could be go for it. Sorry. No. No. I was just curious. So when you're generating, you know, like, this this input screen right here, so you'd have a unique URL that's being generated here, right, in some way, shape, or form, whether that's a tag on the end of the, you know, asset endpoint that gives you a question mark, you know, unique key. That has to be stored then. Is that stored then as part of the director's files or a relational to the director's files kind of a system table where we store those unique IDs? And not so much implementation. Just I I'm thinking about so you'd have that ID. But you'd also be able to see that here. Could you also or optionally ensure that you give, you know, an access token option here? So as an actual field input where you could supply an access token or even a default access token for as part of the admin configuration. Because once again, I don't necessarily want users putting in access tokens, but have a way for the administrators to get to apply an access token that has the appropriate permissions for the file. That is the key piece there. That last that last little snippet of the sentence is the most important crucially important part that has the appropriate permissions for the files. The foot gun with the excess token is that somebody just puts in their own users' access token, YOLO it, and then it works. But now you're technically exposing that all over the place, right? Yeah. Because once you're, you're putting that excess token in the image and HTML, it's just a query parameter. So therefore, wherever you render it, somebody can see it. You have to consider it public. See it. They can So what you're effectively doing is you're not using the public role for those images. You're just using a token that you then make public. Right? So that last part is why the sort of developer or user experience or both. I mean, just the experience as a whole for using the static tokens as way to get access to those asset in a link or an image or otherwise. There's a couple of food guns, right, that you gotta be aware of that people may or may not be. Because again, that's one of those things where, you know, if you explain it, that makes a lot of sense. But if you don't know and you just wrongly assume you could put in any access token, you can be in a in a world of trouble. Right? Absolutely. K. Which makes me wonder if that is something I haven't really thought about all the ash cases, but it's something probably we can solve by using a j signed JWT or something that just scopes it to that specific item in a collection, then we don't even have to store that in the database, assuming that you have access to that. Because then in the asset endpoint, we could check, hey, does the user like, this request actually have access to that item, which is signed in the JWT? And that way, we can verify the access. And if that basically overrides whatever, like, more restrictive permissions you have, you would be good to go only for that specific item where that image is linked in. Yeah. So we're basically saying Linked or whatever. Try to summarize that so I understand that we're basically saying if you attach an image or an a file, a general asset, any asset to an item in a WYSIWYG or relational interfaces, maybe even anywhere else. Well, relational interface, I guess not because then you just have a data point, not a file. But if you have an asset link inside of a Wizzywig or a block editor, some of those markdown, we use a signed link that contains what collection and item it came from. And then we can use the permissions for that item instead of the permission for files. So therefore, if you have read access to that HTML field, you defect don't have read access to the images within it. That feels like a very clean setup as far as I'm concerned. Again, probably also food counts with exposing files or people linking to files, which should be internal at that point. It's more of a shooting your own foot than more, of a case where we are trying to prevent it the best we can. Yeah. But that's, that's kind of already here anyway. Right? Because you can restrict the files and folders that this has access to. Right? In the in the actual WYSIWYG config, you can, I I think Do the root folder or something? It's what we do. Right? It should only be what this user has access to. Right? So if I add Add in general. Yeah. Yeah. For sure. If I add an image, you know, I can add or edit. I don't wanna do that. I wanna So your your condominiums and images. Yep. This is my my permissions restrict me to whatever I have access to, and so I shouldn't be able to technically insert something that I shouldn't be able to now. Could get a little more complex, but I like the that was my thinking and couple of notes that I took. I want a way to restrict the permissions to based on the item's permissions. Yep. So I like the change that we do to go with Amazon. Yep. It's interesting. And it's it also could be something that is just a tick mark or something in that dialogue, which can say, hey. Create a signed link or create an unsigned link because people might not want to have that additional check, but just generally make stuff public or something. But it could be an option either for the end user to decide or for the admin setting it up to decide. I feel like that is a good way of doing it also to introduce it in a non breaking way if you ever wanna introduce it in just a feature release. Judd's asking if we could display make it so that it's visible in the sense of you'd be able to see who has access to that data. This is not great. Can we do that? If if we're doing it with a token, probably not. No. But if we use, like, the scope JWT, would you be able to see so if I was in here looking at this, would I be able to see could you display here who has access? That seems crazy. Whoever has access to, like, whatever item you're inserting this into has access to that file in or something. So it's not something where we have either this user, like, harnesses you in your file or something, or or do they do, like, color eagle? Or I don't know. So I didn't at least with the route we're planning on going or considering going, that wouldn't be the case. Although, now I'm thinking about it. If you have the we use the permissions for the item for the asset endpoint, you still have a similar case where you could render the HTML somewhere for a person that's not logged in. Although, I guess, at that point, it's it's more expected. But you still Oh, you mean, like, service side rendering or something where you have Yeah. Token or, like, some service level token that actually does the rendering and you still break your links? Yep. I mean, at that point at that point, you wanna do like, you wanna manually track what is publicly available or something, I feel like. Yeah. I'm I'm just wondering because still divergently thinking what we can do to make this just very obvious in how it works, but still secure at the same time. Like, I'm also thinking an alternative, and this is just completely other idea. But if there is a native dedicated public folder in the file library, just a different root folder in the file library that is always public, you make it super obvious that, oh, if you wanna put a public image in your article, pull it from the public folder, right, or move it there first to make it public and then use it in your article. Now I'm wondering if that is way too CMS specific. It is. Although, I guess, yeah, public sharing of files, yes, no, yes, no, maybe. I don't know. Tricky. I know a lot of dams are all public by default, which is also a strange idea. Lot of what? Sorry. I know a a couple of dam providers that default to everything being public for ease of use and then you have the opposite where you can mark certain files as private instead of the other way around. But that is also I mean, we do allow you to conf to set that up. Right? We do allow you to set up permissions. Hey. You're allowed to access files which are located within this folder Yeah. Through public permissions, public policies, whatever you wanna do it with. So we already have that. It's just not there by default. We tried again, I'm kind of okay with that from, I I I don't think we have nobody complains about the ability to make something public. It's more so the one. The other thing we should make sure we come back to in this, is it's also about the way it's being embedded. I think the HTML coding that's actually being used, making it easier to con Yeah. That is something we've glanced over so far. Right? Because Yeah. We're more concerned. Security is to me the the first thing we should be thinking about. Yeah. Because for an image, it's kind of there's an obvious way of how to do it. But for an a tag, I mean, there are some standard attributes we wanna put in there. Yeah. But now the question is, like, what is a standard way of linking to something? It's probably an a tag, but is it something that we want to make configurable if use people are using it in service side reactor ending, if you're rendering whatever? Or is that so much out of scope that we say, if you're doing this, you better use the block editor and do your custom rendering pipeline or something or use markdown and go with that? Is it something where we wanna be really open to not rendering just an a tag, but something user defined? Because in the initial request, it looks like it's the requesting only a tags or link tags, whatever. I haven't really been able to see that in the request there. Yeah. Here. Well, that's an alternative. This is what they're doing as an alternative. They're doing some things strangely behind the scenes. Rendered as oh, I think it's a typo. The link should be rendered as an tag. So as a tag maybe in the first sentence or the first paragraph, the motivation. I think that's supposed to be as an as an anchor tag. Yeah. Yeah. So what is this considered currently when we're inserting? Because this is an h ref. Right? It's an image. Is it it's an anchor text. I mean, for for images, it's an image. And for for links, it's just an anchor, an a tag. But a link is not something where we directly allow linking to any records or something. Right? It's just a insert user. And it's just a link. Insert a link. Yep. Yeah. Yeah. Yeah. So so this feature request in its base in most basic form, it's just when I click add a link, allow me to choose a file instead of add a link. That's that's that's bare bones what this is saying. So the same way that we can do image the image and media because they're applying specific tags. But if you're just adding a tag, right, so if I just say I wanna, you you know, make this a link or if I'm just creating a link, I have to specify the URL. So what they want is the to a static file, you'd now have to type in mydirectors.com\u002Fasset\u002Fwhatever the ID is. And and this is this in its simplest form, this is basically just saying, give me a link to a file. Give me a link to a file, same as when you're doing this, where you're inserting an image and you're searching. So they want the they want this regardless of image or anything else. Got it. It's basically the exact same flows in certain image except it pops in a link instead of an inline embed. So you'd have another you'd have essentially another option here that's, you know, insert a direct as file or insert a file. Right? And maybe the image library filters and searches. Got it. Yeah. Which means we immediately derailed this discussion by going, hey. What about permissions? Exactly. That's that's the beauty of these these sessions, Thomas. Welcome aboard. Welcome to our crazy party. I still like the idea of making this this better. Right? So making this more understandable and more usable. Yeah. Image images within WYSIWYG are are infinitely. Because I have another topic if you wanna just derail it a little bit more on this at the other the other way, which is migrations to and from different URLs. If you change the URL of your direct installation, this technically breaks Yeah. It's paint. Actually, I it happened on this very record. I popped this open in preparation for this this morning, and I was like, why is my image not showing up? And I right clicked and it was I'd actually migrated this from, another project, a while back. You see. And now my URL was just back in just this morning. Yep. So when I was looking at the, you know, the link here, I had to actually adjust this to my correct URL. So this is a this is a known issue when we're doing, like, data migrations and things. We've got, our support team has some scripts and things that we'll go through and find try to find and replace known values here. But it's a It's a tricky problem to solve because it's it's by definition, the WYSIWYG one is the trickiest because it is HTML. It's not a pro like a readable format necessarily. I mean, you know. It's something that is like easily solved in I don't know. Like, for example, our block editor or something, right? Right. Yeah. Image could just be nested in JSON or something you can even learn back. Yeah. Yeah. Because you're also dealing with an ID and not a full link. That helps. It's best case best case, it shouldn't even include the link. Like, I don't know what our block editor does, to be honest, but best case, it should just be a acid link or something that just includes the ID of that item you're interested in or that acid. Right. The tricky bit is if you plop that into a browser, nothing shows up. Yeah. It doesn't know what to do with it. Yeah. Yeah. For sure. So you, you have very tricky, tricky setup where that is that is always an issue. And and I'm more than happy because we we have a little bit of time like Lockheed here to to try to to noodle on some ideas for ways around that. Because, you know, some some more CMS specific platforms, they use, like, short codes or something within the HTML. Right? Where it's just like bracket asset ID this and then go figure it out in in post processing. That is an option, but that requires the user of the Custom. The output to do something, or we have to have some sort of custom post processing set up for some fields where we say, oh, if you have an HTML field, we'll search through short codes and and execute them. Interesting. Or Yeah. Because custom web components. Custom web components. We could provide a set of custom web components that is a director's image and that knows your CMS location and then just needs the ID, which is effectively what we're doing for Nuxt. Right? There is, like, a country like a Nuxt image module direct as Nuxt module, which is, like, the base URL, and then it just knows the image ID and figures the rest out. Yeah. So that could be an option for the WYSIWYG that it's like, hey, if you're using a custom image resolver or something, you could just insert the ID in the link. You can't use the face tag outside of the head, could you, in HTML? Oh, don't ask me. I don't think so. I think base is only in the head. The for that that's just I'm not just thinking HTML brain because you were saying it should just be, like, a relative link instead of a full URL. More or less, you were talking just the ID. But I'm like, if you make it slash assets slash the ID, then it will work within Directus presumably, but it will not in your own website. But a base tag in HTML says, you know, graphics every single link to everything with this domain, which could be work around for that. Right? Where you just have a base tag that says, okay, make the base tag, the direct installation, and then it would just work. But I think a base tag breaks every relative link on your Right. Right. Right. I was gonna say breaks every relative link in your whole package. That's what I'm saying. If only there was a locally scoped base tag where you can just say only within the main tag of my Dom is the base tag something else. But, yeah, I don't think that's gonna be a good solution for this. Custom web component is an interesting one too because it requires, you know, a bit of custom JavaScript that needs to happen on every single website that wants to use this, and it's just just a bit of yeah. It is one possibility. Yep. Yeah. I think, if if we if we rely on more of a short code, based approach with some post processing, then you make the migration between projects really lightweight, assuming there's a way to skip the post processing. And then the API output could still be the full URL, but we rely on a short code in the database. So therefore, the value that we store is just like bracket asset ID with all the parameters that you want, and then we generate those into images on the fly. But it's just a bit of a I don't know. It's It it doesn't feel like our responsibility necessarily. Yeah. That's kind of why I'm struggling. Same with, the public folder or something. That that feels too much like forcing one way of solving it on people. Yeah. You're not wrong. You're not wrong. Tricky bum. Tricky bum. Oh, one more thing related and related, maybe a bit of both. For images, we just plop in an image tag. Right? That's that's what what it does. Do we want to support any other file types that are in line? Because videos images are in line, but if you link to a PDF file or something, Do we want to It comes up. We we see that request as well as additional, like, inline viewers. Right? PDFs. Right. Docs, you know, spreadsheets, whatever. Being able to actually have a preview of what that looks like. The tricky thing with those things is that they're all custom front end frameworks or plugins or libraries or web components. It's not it's not a browser standard. So if we wanna go that route, we would have to have some sort of way where you can configure what markup it spits out if you insert a PDF. Because then you get to choose between, oh, when when I'm inserting a PDF, I'm gonna insert my little HTML markup that spits out a PDF preview or something instead of just the link. Yep. So there's that's that's another common thing that we see on those types of fields. Right? So if I go to paragraph here Which already screams use a freaking block editor or anything other than a wizarding question. I you're not wrong. You're not wrong. So but, again, we support, you know, on some level. Right? Whether this is TinyMC or TipTap or name your editor. People have built their own interfaces here. There's a number of marketplace interfaces around this where the underlying libraries actually are more than we're currently exposing. And the question is, can we could we have interfaces have more composability or more extensibility kind of pre exposed, that you could then adapt. You can when the users, you know, when the administrators are setting these up, you could have custom, menu options. Right? So if you could actually I I know tinymc supports it because we've done it. Right? We've got the image and the video editor buttons that are there, and they inspect the appropriate tags. I think Tiny MC and Tiptap both support this ability to have an override or a templating option where you can adjust the configuration, add new buttons, add new drop down. Oh, sure. Yeah. We could we could support that for, you know, file insert as well. If we have an insert button that we say, oh, if you're inserting a type of a PDF or something that we, you know, we render it in a different way. It's just so much setup that you end up having to do. Right? So right now, indirect is you would have to do that per field, which will be kind of annoying. So we would have to add some sort of global defaults as well for for interfaces to make that a little bit more editable. Yep. And that's why, again, I think some of that we're actually looking at. It's for the extensions team, the the directus labs team to take a look at actually doing some tests on creating some new interfaces or modifying faces Yeah. To give us an idea of what's possible, what would what would work well, and then we can decide down the line whether we adapt that and roll that back into the core interface options as well. Yeah. But Or or use a block editor. Like, there's there's, of course, you know, when it comes to and this is this is huge breaking changes and that's something we will do anytime soon, but there's also the argument to be made that is maybe we shouldn't be dealing with HTML at all. I I know it's it's the sort of easiest path to website, but from a data format perspective and for a data portability perspective and a migration perspective and, using it in non web platforms perspective, it is objectively not the right tool for the job, I think. Yep. Yeah. Yep. I think markdown is slightly better. You still have to render it out yourself and you still have some of these migration problems, but at least, you know, renderers for markdown exist across, you know, output types, iOS development, web development, etcetera. But as far as I'm concerned, July 2024, you know, a block editor that returns it in in JSON is the most friendly way to actually render it out across different output vendors. Right? So there's also a world where we can say, maybe we don't concern ourselves with an HTML editor at all. We really focus on having one really good block editor, be it based on, tip- tab, shout out to chat, or or anything else, and then offer some utilities to have that transform to HTML, just to make your life a little bit easier, but not try to save it and manage it as HTML within directness. So like an on the fly, the API endpoint would dynamically transform the block editor JSON format into HTML as an option parameter. Right. Clever. Clever. Because that way we get around a lot of these these issues effectively all of them by just not dealing with HTML. No. Are there, folks are doing it as well to provide you with JavaScript libraries to convert to HTML. It doesn't even need to be server side. Though server side seems like a cool option, but, there are libraries out there for converting, like, adjacent tree to whatever, and you can have custom elements. You can render them customly within your frameworks, all of that. I've done that personally as well. Like, I've worked with abstract documents as a JSON and rendered them in different frameworks with different components, all of that. And it just makes it a lot nicer to handle if you're willing to spend the effort. But that is kind of the catch of it. Right? HTML is just super easy, but it doesn't give you all the nice options which you would have if you dealt with it in a more advanced way. Yeah. But probably it's the reason why we have Wizzy Week in the first place But it's it leaves you with certain limits. Yeah. It leaves you with certain limits. Yeah. Yeah. No. I've got people that are, you know, they they Which makes me always wonder, like Go ahead. No. Go for it. No. I was just saying I've seen people copying and pasting in Wizzy, you know, HTML from other sources and breaking their actual web page. Oh, yeah. Because they've got CSS and other things that they're trying to override, and it's being overridden. And they're complaining that, oh, my fonts don't match on this thing that I copied and pasted from some third party source. Right? Think Oh, that's a big thing. Work. Oh. Yeah. It's I I actually dealt with this just recently for one of our and for one of our clients, one of our actually, a a legacy Ranger Studios client, that we still kind of help out and monitor and support their site. And they came complaining that the fonts weren't matching on articles that they were that they just published. And they said, sometimes it works and sometimes it doesn't. And I was initially, I'm thinking there's some I'm digging through Some of the code on the site. Google Docs and sometimes go into And and then I'm looking at the Wizzio, and then I happen I'm like, what are all these spans and these classes don't exist in our code. What are yeah. And then I real and then I started removing so and suddenly the formatting for the correct. It was picking up the correct font, but they were it was this font code for this HTML was overriding all of the underlying CSS that was configured for the site. So yeah. Yeah. It's a nightmare. Okay. That leaves us with one more feature request after this feature request review, which is how do we get shareable, like, permission permissions baked into an asset link, which makes it easier to manage permissions for anything you link in your editor. And with the agreement that it's probably cool idea and a good idea to include a, a, an, button form, whatever in there that allows you to create an 8 anchor tag to any asset in directors. Yeah. I think that would be a great one to have. And to your point, that's a new feature of choice. Have anyone in the audience who wanna wants to tag on to that. Now the good news is of this session is that all of these things that we described so far, they're all non blockers or supporting the MVP of this, which is insert link to file, which is fantastic, which means that I wanna say, okay. Let's just do it. Let's just do it. It's improvement. It's a new feature, but small one, you know, PRs are welcome. It's a good first issue. Let's just do it. Like, the the minimum viable of this? Sure. Is it something we only wanna do in the week, or is it also something we wanna support in the That is a good question. I don't think it's relevant for the block editor because, a file is filed. You can render it out as a link or an image however you want for the markdown interface though. I think it's relevant. Yeah. Because currently, it's just a URL link. Right? You've got the add in an image. I don't see do we support video in markdown? I There's markdown, and that's There's really no such yeah. There's there's no native markdown for video. K. So not really. There's also no HTML attributes in markdown. There's a lot of things that are in markdown. That's okay. So, technically, we just need the file link. I'm putting a note in the ticket here in the in the discussion here. Go ahead. Go on this. Alright. Same question for the block editor. Is it also something where we wanna support it as well? Since we might want feature parity between between all of our editor interfaces. Agree. I don't even know what the block editor is capable of doing right now. So it's got image. Like we do image and attachment. What is attachment? Is is attachment already what we're trying to do? I think it is. Yeah. It might be. Yeah? Yeah. I think it is. Already is. Yep. So the only thing we because, the block editor natively supports that. The the library that we use for that just has it. Yep. Yep. The Wizzy tiny MCE does not doesn't have a concept of that yet, so we have to edit edit ourselves. Yep. Yeah. So you get select the file, and I guess the the one thing that I noticed is it doesn't have video. Should block editor have video? Or is it just file as a file as a file for these guys outside of the images that have captions? That's a good question. Let's see. Do I have video first deal in here? It's just in some video and see what the preview or window. I don't know what that just did. Oh, it downloaded. No. I have a duplicate copy of that. Lovely. So should it have so for parity, does does the block editor support video? Seems like it's just an attachment. Right? I think it's just an attachment. It's just doing it as an attachment right now, but should it have a video interface the same as the HTML does? No. Maybe. Or a video blank. Just for, like, again, feature parity across the where where possible. I think there's a separate plugin for editor JS, which is what we use in the for the block editor, but we'll have to try it out. Different feature request for a different day. All good. So we already have file, so the only place that this needs to be done is. Markdown in HTML. Right? And I think the block edit can just treat whatever attachment the way it sees fit. I will put a note Just for videos in the WEEZY week, do we just insert a video tag, or how does it work? Let's have a look. Yeah. I'll go test it. Yep. K. That's what I figured. Wizardwig. Also, what kind of fan fancy collection is it that you have there, Jonathan? My test my test collections always have all 3 of the editors in there. You guys can actually show and hide it. That's cool. Yeah. So I'm using the conditional rules. I use it for demos. So I have this in my Yeah. Yeah. Yeah. My CMS demo essentially where I've got a page a paragraph component. And then depending on, you know, if they're a WYSIWYG shop or they're a markdown shop or they're a block header, just being able to show off the different interfaces quickly and easily, but show and hide them. And it shows off the conditional rule capabilities of the app as well. So It's cool. My my debugging instance is just one of each on every page. Yeah. So so you do get a video player in Wizziweb. Yeah. It's yeah. I think it's just a video tag. If you check out the raw HTML of that, it's it's literally Giant kitchen sink collection. We everything in there. Yep. I have including just a flat text area. So all of our kind of Yeah. So it probably is a video. Yeah. Yep. So it actually renders it with Under the hood. With video HTML tags accordingly. So the question is, does the block editor and or we know markdown doesn't support it, but does the block editor support something similar? Right now we're just inserting it as a file. But if the block editor technically supports video, it would be nice to have parity to have the equivalency where video renders nicely. So I'll just put a note. Anyways, taking this back slightly more on track. This feature request as a whole, easy. I think it's easy easy to to accept it. That that's what I was going for. That's so much easy to implement necessarily. Easy to accept it. I think it's we should just do it. It's a good first issue. I think it's great for for PRs, if anybody wants to take a swing at this. I think there's a lot of new ideas and new feature requests that are worth discussing more. Like, I think this signed link approach to permissions is a very, very interesting one that will be very relevant for something like this. But with that being said, the eye on the clock and me also have a hard stop, unfortunately. I wanna say thank you very much for watching once more. We'll be back soon. Soon, Asia. It was a it was a minute since last time, but we'll be back, I believe in 2 weeks, and if not, soon. Make sure to check out this show on direct. Io\u002Ftv. Your request review. If you like doing this, somebody like having to get here.","e668e4c9-da13-438e-b855-95afe8a9e24c",[2290,2291,2292],"6a0ed408-e558-4d6a-b647-0ad0ebe976f9","1205f141-ef72-462f-9515-e073fd888720","daf59fdc-7852-4d6b-abb0-47c605254e9e",[],{"year":978,"number":11,"id":1787,"episodes":2295,"show":2296},[1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1768,1800],{"title":1802,"slug":1803},{"id":2298,"slug":2299,"vimeo_id":2300,"description":2301,"tile":2302,"length":1094,"resources":12,"people":12,"episode_number":889,"published":2284,"title":2303,"video_transcript_html":2304,"video_transcript_text":2305,"content":12,"seo":2306,"status":19,"episode_people":2307,"recommendations":2309,"season":2310},"383c24d5-b6b5-4d66-aba6-6997af5f77b4","form-builder","988084197","Join Bryant for his final 60 minute rush of the season as he attempts to plan and build a form builder. ","23e92277-c632-4fcb-8253-71f79d984430","Mission: Form Builder","\u003Cp>Speaker 0: Welcome back to another episode of 100 apps, 100 hours where we try to build or rebuild some of your favorite apps and app ideas in 60 minutes or less or die trying. Emphasis on the die trying. But, if you've not seen the show before, there are basically two rules. You have 60 minutes to plan and build, no more, no less. And the second rule, the anti rule, use whatever you have at your disposal.\u003C\u002Fp>\n\u003Cp>So today, let's get into the episode, we are going to be building a form builder slash Typeform clone. If you're watching this, you're probably familiar with Typeform, but let's just run through it really quickly. Basically, it is a form builder that, does questions. It's like 1 question at a time. You can customize the design a bit.\u003C\u002Fp>\n\u003Cp>It kinda looks like this where, hey, we fill out a form. I have to do an actual email here. Then, another question shows up. We go through this. Another question shows up.\u003C\u002Fp>\n\u003Cp>There's a lot of configurability there, but that's it really. It's a nice looking form builder that that hopefully makes people wanna fill out your forms. That's it. That's what we're gonna build. So let's dive into it.\u003C\u002Fp>\n\u003Cp>We will put where's my where's my widget? Where's my widget? Timer. Where's the time? Oh, they moved the timer on me.\u003C\u002Fp>\n\u003Cp>Okay. So we're gonna put 60 minutes on the clock. Let's roll with this. Alright. So let's start kind of planning this out.\u003C\u002Fp>\n\u003Cp>Right? I always like to start with the functionality. Feature what features do we need? Right? We need to be able to build a form, fields, field types, etcetera.\u003C\u002Fp>\n\u003Cp>How do I there we go. And then we want to be able to submit that form. Maybe render and submit form. Boom. That's it.\u003C\u002Fp>\n\u003Cp>Really? That's all the features that we need for this. We wanna build a form, maybe we call this a schema. Then we wanna render the form and submit the form on a front end. Alright.\u003C\u002Fp>\n\u003Cp>So how do we go about actually doing this? Now, there's a project that I created previously called hgos that has a kind of form builder in there. It is, not the most robust solution. So, the setup there is basically using the JSON repeater inside Directus, which is basically, just an array of form fields that we're gonna use. We pass that JSON to our component on the front end that renders a form dynamically based on the different types of inputs that we want.\u003C\u002Fp>\n\u003Cp>And then when you submit that, it all goes into a single table, into a single collection. So that is super flexible. And and if all you need to do is, output a a JSON schema to create a form and have a single collection, where you store all that data as JSON, great. Let's take this a step further in this episode though. And let's add this, make this a little more robust.\u003C\u002Fp>\n\u003Cp>So when I think about the data model, we're going to have a form or forms. Each one of those forms will probably have some form fields. Great. And then on the submission side, we've got like a form submissions. And within that, we have an ans we have answers, I guess.\u003C\u002Fp>\n\u003Cp>And so we've got the form submissions belong to the forms, the answers belong to the form submissions. Just kind of drawing these things out. There'll be a relationship between the answers and the form fields. I skipped the magic arrows here. Great.\u003C\u002Fp>\n\u003Cp>There we go. So this is kinda the way I've got it in my mind. We've got a form that holds all of the form fields. There's probably a title. We've got like a redirect or or some kind of on submit action, what we wanna do with it.\u003C\u002Fp>\n\u003Cp>On the front end, we fetch that form and all the form fields, we render it. When somebody fills out that form, we create a form submission, and that form submission is made up of answers and probably some metadata about when that form was submitted, who's submitting it, etcetera. That sounds like a good start for our planning. I'm just gonna throw these up side by side so I don't forget. And to start this out, I've got my blank instance of Directus, that's what we're using on the back end here.\u003C\u002Fp>\n\u003Cp>We're just going to hit create a new collection. Let's call this Forms. Now I'm gonna use the UUID for this. Maybe we just do the standard format here. User created, date created, date updated.\u003C\u002Fp>\n\u003Cp>I'm gonna wanna know what the status of these forms is. Do we need a sort for the forms? Maybe. Do we need a status? We'll just go ahead and add it.\u003C\u002Fp>\n\u003Cp>Alright, so let's give the form a title, maybe a form description, so that'll be a text area. And then we're going to create another collection called Form Fields. So we'll just go in, create a new collection. What's happening behind the scenes, obviously Directus is creating these tables in MySQL database. The fields, we definitely need to sort on the fields.\u003C\u002Fp>\n\u003Cp>Do we want a status on those? Maybe, maybe not. No. Let's keep status off of this. You know, do we want to know when the actual form fields were updated?\u003C\u002Fp>\n\u003Cp>Maybe I'm not super concerned with that, so we'll just leave those off. All right. So for the form fields, when we're thinking through this, right, what are we gonna need on our actual form fields? We're gonna need a name for the field. So what's the field name?\u003C\u002Fp>\n\u003Cp>We're gonna need a type for the field. So the type is probably gonna be a string, but maybe as far as, like, the interface that I wanna use, I want this to be a drop down. So we can think about input. Great. Do we have a, like, a form field for that or an icon?\u003C\u002Fp>\n\u003Cp>Yeah. Let's try that. Let's do text area. Text area. Text area.\u003C\u002Fp>\n\u003Cp>Do we have one for that? We don't. We'll just go with that option. What else do we wanna display? Like a select?\u003C\u002Fp>\n\u003Cp>Just thinking along the lines of the actual HTML form elements we're gonna use here. Drop down, maybe select. Good looking icons are sparse here. Okay. So that's probably good for now.\u003C\u002Fp>\n\u003Cp>We'll just make this a label, format each label, we don't want to show as a dot. I can actually copy and paste the choices for our interface into the display here. And this is what shows up on the, like, the index pages where you could potentially see a list of the form fields. Alright, so we've got a name, let's make this half width, make it look good when we're actually creating our form. We've got a name, I could double as label.\u003C\u002Fp>\n\u003Cp>Trying to think of what else we've got on this. Maybe some type of validation name and type. If it is a select type, we probably need, like, some available choices. Right? So let's go ahead and do that.\u003C\u002Fp>\n\u003Cp>We'll do that as the, like, JSON repeater. We'll call this select options, I guess. Great. We'll add a text field. So this is the text we're gonna display or we use kind of the label value format.\u003C\u002Fp>\n\u003Cp>So we set that up, that'll be an input, very simple. And then we have a value. In case we wanna store those differently, we can require a value, make that half width as well. Great. And let's add some conditionality to this that, if the type is select that's when we show this.\u003C\u002Fp>\n\u003Cp>So I'm gonna go into our field options, let's just hide this by default, and then inside Directus I can add conditionals to the forms inside the Studio by doing something like this. So we'll say show if type equals select. And this is just a description so I can keep my logic together. We'll say if the type equals select we're going to make sure this is not hidden, and that should give me what I am looking for. Right?\u003C\u002Fp>\n\u003Cp>We could expand this further and add, like, conditions to our front end and etcetera. What we need to do now, though, is add the relationship between those. So in this case, we want a form field. It should only belong to a single form in my mind. I I you could I guess you could make the argument either way, but in this case, let's just create that relationship.\u003C\u002Fp>\n\u003Cp>So this is gonna be a one to many relationship here. So this is gonna be called Fields, and I'm inside our forms collection, our forms table right now. I'm just going to call this key fields and we're going to look, the related collection is our form fields. And the foreign key inside the form fields table, I'm going to create a new field called form that relates back to the actual form. Gravy will show a link to the item.\u003C\u002Fp>\n\u003Cp>And if I open up this advanced field creation mode inside Directus, I get a lot more options that I can configure and play with. So I'm going to add a sort field, we're just going to make sure that sort is selected here. That allows us to drag and drop the order of those fields. And then for our triggers, like if we deselect a form field, do I want to delete that item or not? In this case I'm just gonna nullify the form field.\u003C\u002Fp>\n\u003Cp>Great, we'll display the related values. Bada bing bada boom. Now we should have a form with some fields. Let's just test this out really quickly. So we're gonna go in, we got a form.\u003C\u002Fp>\n\u003Cp>This is our contact us form, the most standard of all forms on the Internet. And here's the standard contact us form. Great. Alright, so we'll do name, is gonna be email, this will be an input. Great.\u003C\u002Fp>\n\u003Cp>What are some of the other things that we'd probably want to add to this? Like a placeholder, I would say. First name, that'll be an input. What else? Last name.\u003C\u002Fp>\n\u003Cp>Okay. Comments, that'll be maybe a text area. And now let's, let's get into the select option. Right? How can we help you?\u003C\u002Fp>\n\u003Cp>Alright. So for the options here, I need a developer, so we'll give it a label. Maybe we store the value as just developer. I need a sales rep. I don't know.\u003C\u002Fp>\n\u003Cp>Great. Okay. So a couple of things here. I don't see the actual form values that I I might look. You know, here's they're just showing IDs.\u003C\u002Fp>\n\u003Cp>How can I fix that, right? I want to make sure this looks nice when we're setting these things up. So what I can do, if I go into my form fields, we have the display template. So this is the default anytime we're referencing this form field. Or I could go into forms and go to the fields section here and I can control the display template just within forms.\u003C\u002Fp>\n\u003Cp>So that's super handy if you, need different display templates in different places, but in this case let's just go and edit the default one. Let's do the name and then we'll maybe put like a special little dot there and maybe we want to show the type of field that is. And now if we just go back to our forms, great, we can kind of see what we're working with here. I don't really let's put first name, last name, then email, then comments, and how can we help you? Maybe we move that up above.\u003C\u002Fp>\n\u003Cp>Right. Great. Alright. So now we've got the start to this form. Let's go in and actually start messing with the front end.\u003C\u002Fp>\n\u003Cp>We've got 47 minutes on the clock. I'm just gonna pull this up. Let's pull up my Nuxt application. This is just the standard Nuxt application that I have out of the box. That's my weapon of choice in this case.\u003C\u002Fp>\n\u003Cp>And let's just go in, I'm going to create a new directory in the pages directory. So this will give us a route automatically on the front end. I'm gonna call this forms, and then I'm gonna do just form in brackets here, and that will give us, like, a a dynamic form. If I do view script setup, TS setup, this is kind of what I want. Great.\u003C\u002Fp>\n\u003Cp>We've got the standard Vue Form Component. And we'll just say Form Component. Great. Alright. So let's test this out on the frontend, forms.\u003C\u002Fp>\n\u003Cp>Test. What? Uh-oh. Some type of issue going on. There it is.\u003C\u002Fp>\n\u003Cp>Form component. And this has got a layout attached to it where we're showing, like, the logo and things like that. Maybe not a huge issue for this, but, you know, the type form has, like, this full screen form effect, so, you know, maybe we dive into that later. Alright. So now I actually want to fetch that form from Directus, right?\u003C\u002Fp>\n\u003Cp>So a couple things, we probably need to edit our access control. So by default, Directus locks down every single thing inside your instance. Only admins are able to, interact with the API by default. So we're just going to go in. I'm gonna set readability for our forms and our form fields, and now if I just wanted to test that out quickly, I could do something like this in the browser where I go to my directus url\u002Fitems\u002Fforms, and I can see I get the form data here, and and we'll expand that, once we dive into the Nuxt side of things.\u003C\u002Fp>\n\u003Cp>Right? So the first thing that we want to do here, we're gonna get the routes, so we know which form to fetch. And then we're gonna be using the Nuxt Async Data Call. So this Nuxt application, I've got a Directus plug in here that basically fetches information from the Directus API. We provide the SDK to the Nuxt application so I could do things like this where I just simply say give me the Directus client.\u003C\u002Fp>\n\u003Cp>And that way it's shared across the actual Nuxt application. So I get my Directus client, and then we're gonna use the, let's say data is coming back. We're gonna use the Async data call from Nuxt. And within that we are going to return Directus dot request. And then we need to import the readitem from the Directus SDK.\u003C\u002Fp>\n\u003Cp>Alright. So this is just going to read a single item. We're gonna pick that up from the actual form, from the route itself. So we'll say directives dot request. It'll be read item.\u003C\u002Fp>\n\u003Cp>We're reading items from the form collection. And then we've got route dot params dot form. Right? So that will it should give us the form. And then I can pass a a set of parameters to the Directus API to get things like form fields, etcetera.\u003C\u002Fp>\n\u003Cp>But let's just take it from here. We'll wrap this in another div. I always end up doing this just to take a look at my data and just wrap it in a pre tag. And now if I do forms test, we're probably not gonna get back any data. If I was to open this up, I don't really see anything here.\u003C\u002Fp>\n\u003Cp>Great. Alright. So now let's actually test this with the ID of one of the forms. We'll go in. Great.\u003C\u002Fp>\n\u003Cp>Okay. Looks like we are getting a cores error. So I've goos something up in my direct to settings. CORS enabled. CORS origin.\u003C\u002Fp>\n\u003Cp>Should be interesting here. Let me restart this Docker container and see if that solves my problem. If I look at my ENV, I've got HTTP local host 8055. That is that's where my direct instance is running. Okay.\u003C\u002Fp>\n\u003Cp>There we go. I just needed to set the cores in this case to make sure that local host 3,000 is allowed, and basically, I've done that the quick and dirty way just by allowing any origin here. But, typically, I would set this to, like, local host or something like that. Alright. So we could see here's our form data.\u003C\u002Fp>\n\u003Cp>That's great. How do we actually get the form fields? One of the nice things about the Direct Express API is the ability to call this data in a single API call to expand those relationships, right? So inside my form fields, maybe on the front end, I don't really care about this extra data when it was created. So let's just say we want the ID, we want the name of the form, we want the oh, it's actually the title of the form.\u003C\u002Fp>\n\u003Cp>Title. Great. And we want the description. And then we get into the fields. All right, so I could do something like this where I add a dot notation and a wildcard which will give me all the items for that field or all the all the fields within the fields collection or the the related items there basically.\u003C\u002Fp>\n\u003Cp>So instead, what I'm gonna do, if you're using the SDK, we could pass it a syntax like this where we say fields. And here, we're gonna specify the actual fields. We want the name. We want the type. We want the options, and that should give us what we're looking for here.\u003C\u002Fp>\n\u003Cp>Right? And we probably do need the ID of those fields as well. We'll look at submissions in a few. Okay, so now we've got these fields coming in, this looks good, we've got a type, we've got a name for these, we can start to construct like a form with this actual data. Now on the front end in this Nuxt application, I have this Nuxt UI library.\u003C\u002Fp>\n\u003Cp>It would be suicide to try to build this without a form library, ahead of time here or, like, some type of UI library so I could do forms. They've got this really nice setup where you can pass it a form component. They've got Zod validation, support for Yelp and Joy if you're into that. But looking at this, everything gets wrapped in this form group component, which gives you like a nice label, etcetera. So let's just try to iterate through this.\u003C\u002Fp>\n\u003Cp>Right? We've got a form. They have a u form component. Not concerned with, like, validation at this moment. Let's just display a form.\u003C\u002Fp>\n\u003Cp>Right? So we're gonna go through Uform group, v 4 fields and forms dot fields. The field, we wanna pass like a I think there's a name component for each one of these. Like, if we go in and look at the props, we gotta pass a name to it. So we'll do name.\u003C\u002Fp>\n\u003Cp>It's field dot name. Great. What else do we need here? So within the form group, then we are going to do the actual component. So we've got an input here.\u003C\u002Fp>\n\u003Cp>There's a select box. Great. What else do we have? We had text area. Right?\u003C\u002Fp>\n\u003Cp>So what I'm going to do, I'm just gonna create a map here, basically. So we'll do, a field map. And I'm just gonna do something like this where, that actually looks pretty good. You input, you text area. That's how all these are name spaced.\u003C\u002Fp>\n\u003Cp>But what I wanna do instead is use this, like, resolve component or, you know, I could even actually import these as well. So this resolve component is just a helper. You could just straight import them as well. And with Nuxt, you can actually load these dynamically as well by adding lazy in the front of them. So it only pulls in that, that component when it actually needs it dynamically.\u003C\u002Fp>\n\u003Cp>Alright. So we've got our fields, we've got our form groups within the form, and GitHub Copilot is helpful again in this aspect that we want to do the field dot type inside. We wanna pull that correct component from our map, give it a name, give it an options. Let's take a look. Options are there.\u003C\u002Fp>\n\u003Cp>Name and value. Okay. So let's save this, see what we get. It looks like we have some we got some form of something going on. I need to change this right away to input so we can actually see those.\u003C\u002Fp>\n\u003Cp>Let's give this a little bit of styling. Right? Maybe we do, like, a max width XL. Format with math width Excel MX Auto. Great.\u003C\u002Fp>\n\u003Cp>Maybe we make this a grid. We give it some gap. Why is this actually not should be parsing out my is Tailwind not included in this? What what have we got going on? My styling is not being applied from Tailwind.\u003C\u002Fp>\n\u003Cp>That's kind of upsetting. We can definitely sort that out though. And then we need a label for this as well. So we'll do the field dot name for the label. Okay, so now we've got a kinda set up in there.\u003C\u002Fp>\n\u003Cp>Let's do view button. And we'll do, like, a submit option here. Let's add that outside the form group. So we get a submit button at the bottom of this. Right?\u003C\u002Fp>\n\u003Cp>Okay. So how can we make this kind of like typeform in a way because we wanna answer these one question at a time? Or how can we kind of loop through these? Right? Gillespie.\u003C\u002Fp>\n\u003Cp>Great. We don't have a state for these, so we'll we'd need to update that as well. Let's do v model equals set up, like, our form data. Form data, maybe we use like a ref for this. It'll be form data, and we can dynamically do field dot ID.\u003C\u002Fp>\n\u003Cp>Let's put this in like an answers array. Answers. And then we're gonna have the field dot ID. Okay. Alright.\u003C\u002Fp>\n\u003Cp>And within this, I'm just gonna do answers, and we'll just preset that. All right. So now if I take a look at like my Vue Dev Tools and we go to Form, Okay. I can see my Directus object. I've got my form data.\u003C\u002Fp>\n\u003Cp>There's my answers. Now if I populate these, is that actually doing what I want? More data. Click form. Do we see our answers?\u003C\u002Fp>\n\u003Cp>Oh, maybe we want to grab the index of this as well. Field index inform dot fields. That could be helpful. Answers field index field dot id. Bryant Gillespie.\u003C\u002Fp>\n\u003Cp>Bryantdirectus.io. Let's take a look at the data we're pushing here. Okay. So I can see my fields now. Oh, nope.\u003C\u002Fp>\n\u003Cp>Form data. There's my answers. Okay. That'll that'll suffice for now. All right.\u003C\u002Fp>\n\u003Cp>So let's do these one at a time as well, right? So maybe we give this a const current field idx. We set that to 0. And then, how are we gonna want to display this? Alright.\u003C\u002Fp>\n\u003Cp>So we're gonna render each field conditionally. Alright. So let's just wrap this in a template. This will be the what are we gonna do here? So we still need to show the actual field.\u003C\u002Fp>\n\u003Cp>V. This could actually be something like this, I think. V if current field current field IDX equals field IDX template. We'll loop for these Fields. Field IDX, inform dot fields key equals field dot field.\u003C\u002Fp>\n\u003Cp>Nope. Field.id. Alright. Let's see what we've got here. Oh, don't wanna close.\u003C\u002Fp>\n\u003Cp>Let me open this up. Alright. V field dot ID. We don't need a key on that one. V4fieldsfield.\u003C\u002Fp>\n\u003Cp>Alright. So now we've got kind of this one field for each. One of the cool utilities that I use a lot is also the auto animate by a group called FormKit. It's, v autoformkit auto animate. One line of code or just a couple lines of code and you've got, like, nice form animations, for, like, list transitions, showing and hiding inputs, etcetera.\u003C\u002Fp>\n\u003Cp>And we're only gonna show the button if fields, field idx is equal to form dot fields dot length. Field.ex +1 is equal to form.fields.length. So here we shouldn't be showing that button. Give us a padding here. Cool.\u003C\u002Fp>\n\u003Cp>Alright. Now within this, we probably need, like, a previous and next button as well. So let's just do, like, a flexbox, Go to some gap. We'll do U button. Next.\u003C\u002Fp>\n\u003Cp>Okay. GitHub Copilot. Alright. So basically, if the current field IDX is greater than 0, we're going to display the previous button. If the current field IDX is not greater than the end of the form, we want to show that.\u003C\u002Fp>\n\u003Cp>But we need to fix the actual setup here. You form group. Maybe we wrap this in a div. Okay. So now you could see we could kinda go through each one of these.\u003C\u002Fp>\n\u003Cp>And do I have the actual auto enemy applied properly in this case? Via, it should be. It's not actually animating. Let me refresh. Ah, there we go.\u003C\u002Fp>\n\u003Cp>Now we're getting some nice animation or animations. Right? So we'll do some margin there. Add justification between these. And we do one of these as variant equals soft.\u003C\u002Fp>\n\u003Cp>Is that what it is? There we go. Alright, so this is the primary. Great. There we go.\u003C\u002Fp>\n\u003Cp>Cool. Alright. So now I could go through and fill out this form. Alright. Oh, that's actually submitting the form.\u003C\u002Fp>\n\u003Cp>I don't wanna do that. Alright. Next. Gillespie. May we change this to button?\u003C\u002Fp>\n\u003Cp>Great. Give him my email. Cool. Starting to look something like Typeform in this case. Alright.\u003C\u002Fp>\n\u003Cp>So test, test, test. V if field equals fields dot length. Oh, maybe this is the actual code that we need here. Why are we not seeing the so, oh, because it's not in that actual loop. There we go.\u003C\u002Fp>\n\u003Cp>So now we should see the submit button. Great. We don't have any type of handler on the submit button. And it's also not doing anything if we actually, were to submit this anyway because we've got, we we don't have anywhere to post it inside Directus. Right?\u003C\u002Fp>\n\u003Cp>We've got roughly 20 some odd minutes. Let's make this form actually submit. Right? So if we go back to our setup, right, we've got, a form submission, we've got some answers that are within that. So let's go ahead and set up those collections.\u003C\u002Fp>\n\u003Cp>Let's say form submissions. These will be DUID. This will be let's call this submitted, date submitted. That kinda matches what we've got already. Yeah.\u003C\u002Fp>\n\u003Cp>Maybe we wanna track the time that it actually user created, date updated, user updated, the status of this submission. You know, you could even like, building your own here, you could certainly, like, track partial submissions and things like that as well if you wanted to. Like, we could submit any time and then update whenever they submit the final one, sort of thing. But let's say we have, like, a date started type of thing. Maybe we wanna, like, store that whenever they start this form submission.\u003C\u002Fp>\n\u003Cp>So we could track things like how long it actually took them to do this. For that we want the date time. We're just gonna do time stamp. Cool. Day started.\u003C\u002Fp>\n\u003Cp>Date submitted. Let's make sure we show that field. I'm just going to make one of these full width so I can get what I want out of this. Okay. Alright, so the form submission, we've got to link that back to an actual form as well.\u003C\u002Fp>\n\u003Cp>So we will do a mini to 1 here. This will be our forms. And let's do forms as the related collection. Now if I open up advanced mode, I can also create the inverse relationship as well. So if I wanted to do something like this where inside forms I've got a relationship back to the submissions, which in this case makes a lot of sense because of probably want to see the submissions.\u003C\u002Fp>\n\u003Cp>We could set up that and create that relationship in a single go. So there's our form. And then the next thing that we want to do is create a form, I'm gonna call it form submission answers. It could just be form answers, I guess. Always like to, have like a a common prefix, among these things, especially when you start building projects that have a ton of tables in them.\u003C\u002Fp>\n\u003Cp>Alright. So there's probably a sort on that specific answer. Do we need any of these additional fields? Probably not, right? So when I think of the answer, we've got a relationship back to the form field.\u003C\u002Fp>\n\u003Cp>So we'll just call this field. Let's Create that. There's the form fields collection. And again, I could do the same kind of thing. And setting this up this way, you know, with all the different database tables and the collections inside Directus allows us to, you know, say I wanted to query all the fields or all the answers from a single field on the form.\u003C\u002Fp>\n\u003Cp>This makes that a lot easier as well. So if we go in here, let's just add that corresponding one to many relationship as well. Let's call this answers. Great. And then we've got, what, just like a value?\u003C\u002Fp>\n\u003Cp>I'm I'm just gonna simplify this. I'm gonna sort everything as text. You know, if you had different field types or like numbers or dates, maybe you wanna store those in the underlying database as that specific data type. You know, you could set up some different fields for that like value date, value text, value number, etcetera. But to keep this easy we're just gonna store everything as a value.\u003C\u002Fp>\n\u003Cp>Alright, so now if we just poke around, right, if I were to like manually add a submission here, I could click create. I'm not seeing the actual answers relationship here. Form answers, form submissions. Oh, that that would be why I'm not seeing it because we haven't created it yet. So the last thing that we need to do is link this form answers to the actual submissions.\u003C\u002Fp>\n\u003Cp>So we'll go in and on the form submission, this is going to be a one to many relationship, so we'll call this answers. And the submission ID, great. We'll show a link to that item. Just inspect this. It looks great.\u003C\u002Fp>\n\u003Cp>We got a sort field for those answers. Cool. Everything should be gravy. Right? So what's helpful sometimes, especially, like, if you're doing CRUD operations from the front end, maybe I wanna go into this form and I add a new submission manually.\u003C\u002Fp>\n\u003Cp>Alright. So just say this. Great. Add some answers. Here's our field that we're answering.\u003C\u002Fp>\n\u003Cp>Bryant. Create a new one. Last name, Gillespie. And this is a little bit cumbersome, but, again, we're gonna be handling this from the front end. Gmail.com.\u003C\u002Fp>\n\u003Cp>How can we help? This is gonna be what? Sales rep. And then some comments. Comments.\u003C\u002Fp>\n\u003Cp>Okay. Alright. So now we've got a submission. I can see kind of the answers here, and these are not super helpful again because we haven't adjusted like the display templates for those. So our form answers, maybe we want to show the the field name, then the actual answer.\u003C\u002Fp>\n\u003Cp>Great. Let's set that up. For our form submissions, maybe we want to show the, like, the date this was created, submitted. We wanna show the form as well. So we'll just show form dot title.\u003C\u002Fp>\n\u003Cp>And let's just edit this. Underneath the hood, this is just much syntax. We can also do dot notation to access the related fields as well. Set a little bullet point. All right, so now if we go into forms we can see the contact us form submission a minute ago.\u003C\u002Fp>\n\u003Cp>And now I can see the individual answers here, which is kind of helpful, right? But let's take a look at the structure of this on the other end. So if I wanted to see an individual submission, I'm just gonna pull this up, copy this, we've got items, form submissions. And then I can get the actual submission here. So here's kind of the structure of this, and this is what we would need to post as well, right?\u003C\u002Fp>\n\u003Cp>So we've got a form ID, then we have the individual answers. If I do question mark fields, we do just all the root level fields, we grab the answer fields, see what what kind of thing that we need to pass, right? So we got the field ID that we're gonna need to send via the API request when we submit that. And then we've got the value, so what the value of that is. Okay.\u003C\u002Fp>\n\u003Cp>So now let's take a look at actually submitting this form inside here. So let's create a new function. Let's call it Async function. We'll just call it Submit Form. Great.\u003C\u002Fp>\n\u003Cp>And what are we gonna do here? We're gonna have, like, a transform the form data to the format you need, submit to the Directus API. Alright. So if we look at our actual form data, how do we get this set up? So it's just an array of answers.\u003C\u002Fp>\n\u003Cp>I probably could have made this even simpler than that, honestly, and could have done, what, just the the form field ID. Let's just test this out. Right? Form data dot field. Let's do it this way.\u003C\u002Fp>\n\u003Cp>Answers. Form data. We still do answers. Just make that an object, and then we can add the ID for that. Async function.\u003C\u002Fp>\n\u003Cp>Oh, misspelled function. Just wondering why that wouldn't compile. Colosbybryant@gmail. Great. And now if I just refresh down here, click form, do we see our form data?\u003C\u002Fp>\n\u003Cp>There we go. We've got that. Cool. Alright. So when we submit this form, let's, transform this data.\u003C\u002Fp>\n\u003Cp>So, we're going to map through the answers. So we will do data. This is a good ID as well. We're gonna need the form ID. So this will be route dot params.form.\u003C\u002Fp>\n\u003Cp>Then we have the answers, but we're gonna need to map those first as well. Map answers. Alright. So object what do we want? Object dot values, object dot keys, form data dot answers.\u003C\u002Fp>\n\u003Cp>Yeah. There we go. For each. So we're gonna do the data key. Let's just take a look at our structure here.\u003C\u002Fp>\n\u003Cp>We've got the answers. So we need an ID. No. We don't need an ID for the actual answer. Directus should create that for us, but we do need the field ID.\u003C\u002Fp>\n\u003Cp>Alright. So we've got answers here. This is gonna be an actual array. And then for that, we're going to do answers dot push field dot key dot value. Answer is key.\u003C\u002Fp>\n\u003Cp>Great. Hey. We do console dot log just to test this out. Let's Data to be submitted. Save this.\u003C\u002Fp>\n\u003Cp>See what we got, Bryant. Oh, got all this selected. Gillespie. Okay. And then let's add submit or at click submit form.\u003C\u002Fp>\n\u003Cp>We refresh really quickly. Right. Gillespie. And now if I hit submit, Form data. Oh, that's a value.\u003C\u002Fp>\n\u003Cp>That's reactive. So let's do, like, an on that. Bryant Gillespie. Hit submit. Cannot read properties of undefined.\u003C\u002Fp>\n\u003Cp>Field uncaught in promise. Object. Keys. Answers.key. Form data answers.\u003C\u002Fp>\n\u003Cp>Console dot log, form data. Let's just unref this form data to see what we're getting. Brandt. Form data. Target.\u003C\u002Fp>\n\u003Cp>Answers for each key, data dot answers dot push, field key, form data. Oh, duh. Gotta unref that again. Silly sometimes. Great.\u003C\u002Fp>\n\u003Cp>So now I can see here's the data to be submitted. There's the field. There's my value, etcetera. Now all we should need to do is actually send this. So, let's do create item from the Directus SDK.\u003C\u002Fp>\n\u003Cp>And just the same way we can fetch those, relationships, those related fields in a single call, we can also push responses to the related fields in a single call as well. So we'll do let's do data. Let's just see it this way. I can't remember what's gonna be coming back. The async data call returns reactive values for data.\u003C\u002Fp>\n\u003Cp>But we're not gonna use async data here. This will be just like regular fetch. So we'll do await. Actually we can just use the directus SDK here as well. So we'll do directus dot request, create item, and this will be form_ submissions data.\u003C\u002Fp>\n\u003Cp>Is there anything specific we want to return from this? Nope. Let's wrap this in a try catch and just console error any errors that we receive. Form underscore submissions with the data. Console dot log response.\u003C\u002Fp>\n\u003Cp>Great. Let's see what we got. Alright. So we're just gonna pick on teammates here. We'll do a Matt Minor.\u003C\u002Fp>\n\u003Cp>Maddie atexample.com. I need a developer. Help me, please. Alright. So we hit submit.\u003C\u002Fp>\n\u003Cp>Do we see an actual network call going out here? Post forbidden. Oh, no. What are we doing wrong? Right?\u003C\u002Fp>\n\u003Cp>Where are we at on time? We got 10 minutes left. We're we're in pretty decent shape. What we don't have here is the permissions to actually create items in form answers and submissions. So we go into our public option here.\u003C\u002Fp>\n\u003Cp>We can see that, we've got the ability to read form fields and read forms, but we don't have the ability to create form submissions and create answers. Alright? So there's a couple different ways that you might wanna set this up. You know, if you wanted to, like, potentially route submissions through, directly to the Directus API or, you know, through some other, like, server side redirect, inside Nuxt through like a server route or something. Or in this case, we can just give direct public permissions to create these things.\u003C\u002Fp>\n\u003Cp>Now one of the other things that you might wanna do on the read side of it, you know, I I might want to use like a custom permission or something like that so that, people can't read all the actual form submissions. So maybe just the, IDs, etcetera. Or I could even leave this off as well. I'm just gonna enable this just while in testing. Just remember to restrict this before you go to production.\u003C\u002Fp>\n\u003Cp>So now if we test this form submission again, what do I get? I get a 200 okay. There's our submission. If I go into Directus, we can see we've got our form submission there. There's all of our answers.\u003C\u002Fp>\n\u003Cp>That's great. We don't have the date started because I didn't populate that. Right? But what if we were just to do something like this where we've got the date underscore started, And we do new date. Great.\u003C\u002Fp>\n\u003Cp>And this, let's say to ISO string. Just to be sure Directus will swallow that down. Alright. Let's test this one more time and maybe we can fix the field IDX, form field dot link, currentfieldidx. That's where we goofed up here.\u003C\u002Fp>\n\u003Cp>Okay. So now we've got Bryant. We got Gillespie. We'll just add a bunch of junk here. Email test at example.com.\u003C\u002Fp>\n\u003Cp>I need a developer. Oh, I like 35 submit buttons here. Let's go ahead and submit that down. Submit that up and see if we've got that date started field. Why do I not have that?\u003C\u002Fp>\n\u003Cp>Didn't populate. Oh, I didn't actually pass that in there. Date started. Current field, IDX. This actually needs to be just out of here, doesn't it?\u003C\u002Fp>\n\u003Cp>Alright. Bryant Gillespie test@example.com. Got a developer. Here's some comments. There's our submit.\u003C\u002Fp>\n\u003Cp>Great. Take a look at our submissions. There we go. We've got our date started. So now we could actually, like, run calculations on how long it took to fill out this form.\u003C\u002Fp>\n\u003Cp>We've got all of our answers in the correct place. We've got, like, 6 minutes left. Did we check all the boxes here? Right? We've got a form.\u003C\u002Fp>\n\u003Cp>We've got the fields. We built the form here on the back end. We've rendered and submitted the form on the front end. Amazing. Let's see if we could just take this a step further.\u003C\u002Fp>\n\u003Cp>Maybe we wanna create a dashboard for all of our form submissions. Let's see what we can get here. Maybe we do a simple metric. We want to just track the number of form submissions. So that'll be the field ID.\u003C\u002Fp>\n\u003Cp>We'll just do like a count of that ID. Total form submissions. Boom, I can see we've got 4, right? One of the other things that I like to do when building these dashboards inside Directus is make this dynamic. So what I can do is, let's say I wanted to see the answers for a specific form.\u003C\u002Fp>\n\u003Cp>What I could do is create a global relational variable. We'll call this, like, form ID. So we'll pick the form. Great. Show that out.\u003C\u002Fp>\n\u003Cp>Select a form. See what we got here. I'm just gonna move this out of the way. So we can select a form, that variable is named form ID, and then maybe I want to show a list of responses. So let's show the form submissions.\u003C\u002Fp>\n\u003Cp>And for the filter here, what I want to do, let's just show the date that that was submitted. Great. Under the filter section, what I can do here is go in and I'm gonna create a filter that uses that, that variable that we set up. So instead of hard coding a form value here, I could just do something like this where I say form underscore ID equals that variable and just wrap that in the mustache syntax. So responses for this form.\u003C\u002Fp>\n\u003Cp>Alright, so when I save this, we shouldn't see any responses here. But if I pick our contact us form, we can see those, then I can open each of those up. Or, you know, we could also change this to something like field ID. So if I wanted to see responses for a certain field, so we'll just change this from forms to a form field. We can go in, the display template for this, maybe we want to show the name of the field.\u003C\u002Fp>\n\u003Cp>Maybe we want to show the name of the form that it comes from, the title of that form, where are you. There we go. We don't need a filter there. That's gonna be our field ID. How are we doing on time?\u003C\u002Fp>\n\u003Cp>We still got 2 minutes for this. We'll change this to select a field. Field. Great. Responses for selected field.\u003C\u002Fp>\n\u003Cp>And then we'll just do the same thing here. We're just gonna change this, right? We'll change the collection to form answers. For our displayed template in this actual list, maybe we want to see the value. So we'll just show the value.\u003C\u002Fp>\n\u003Cp>And instead of this, we're gonna do the field dot ID equals field underscore ID wrapped in mustache syntax here. Alright, so now if I select First Name I can see a list of all the responses. Great. Looking great. And then maybe we even duplicate this.\u003C\u002Fp>\n\u003Cp>And let's say I wanna show those values in like a chart or something, right? So we got our donut chart here. We got our styles. The field that we want to count in that case is gonna be the value. And now I can get a breakdown of all of those responses.\u003C\u002Fp>\n\u003Cp>Great. Cool. 1 minute 24 seconds left. Ring the bell. Again, this has been a fun episode.\u003C\u002Fp>\n\u003Cp>You know, taking this from here, obviously, like, I would totally improve the styling, add some validation into it, but this would be super handy to have a form builder that you've got direct control over, that you don't have to pay outrageous prices for, well, like 30 submissions or something like that. I don't know. Let's look at the pricing. What do we have on Typeform? I like Typeform a lot.\u003C\u002Fp>\n\u003Cp>The pricing is a bit crazy. Alright. $25 a month gets you or $29 a month gets you a 100 responses. Using Directus, get way more than that for less. Amazing.\u003C\u002Fp>\n\u003Cp>Alright. That's it for this episode of 100 apps, 100 hours. I hope you've enjoyed it. I sure have. We'll catch you next time.\u003C\u002Fp>","Welcome back to another episode of 100 apps, 100 hours where we try to build or rebuild some of your favorite apps and app ideas in 60 minutes or less or die trying. Emphasis on the die trying. But, if you've not seen the show before, there are basically two rules. You have 60 minutes to plan and build, no more, no less. And the second rule, the anti rule, use whatever you have at your disposal. So today, let's get into the episode, we are going to be building a form builder slash Typeform clone. If you're watching this, you're probably familiar with Typeform, but let's just run through it really quickly. Basically, it is a form builder that, does questions. It's like 1 question at a time. You can customize the design a bit. It kinda looks like this where, hey, we fill out a form. I have to do an actual email here. Then, another question shows up. We go through this. Another question shows up. There's a lot of configurability there, but that's it really. It's a nice looking form builder that that hopefully makes people wanna fill out your forms. That's it. That's what we're gonna build. So let's dive into it. We will put where's my where's my widget? Where's my widget? Timer. Where's the time? Oh, they moved the timer on me. Okay. So we're gonna put 60 minutes on the clock. Let's roll with this. Alright. So let's start kind of planning this out. Right? I always like to start with the functionality. Feature what features do we need? Right? We need to be able to build a form, fields, field types, etcetera. How do I there we go. And then we want to be able to submit that form. Maybe render and submit form. Boom. That's it. Really? That's all the features that we need for this. We wanna build a form, maybe we call this a schema. Then we wanna render the form and submit the form on a front end. Alright. So how do we go about actually doing this? Now, there's a project that I created previously called hgos that has a kind of form builder in there. It is, not the most robust solution. So, the setup there is basically using the JSON repeater inside Directus, which is basically, just an array of form fields that we're gonna use. We pass that JSON to our component on the front end that renders a form dynamically based on the different types of inputs that we want. And then when you submit that, it all goes into a single table, into a single collection. So that is super flexible. And and if all you need to do is, output a a JSON schema to create a form and have a single collection, where you store all that data as JSON, great. Let's take this a step further in this episode though. And let's add this, make this a little more robust. So when I think about the data model, we're going to have a form or forms. Each one of those forms will probably have some form fields. Great. And then on the submission side, we've got like a form submissions. And within that, we have an ans we have answers, I guess. And so we've got the form submissions belong to the forms, the answers belong to the form submissions. Just kind of drawing these things out. There'll be a relationship between the answers and the form fields. I skipped the magic arrows here. Great. There we go. So this is kinda the way I've got it in my mind. We've got a form that holds all of the form fields. There's probably a title. We've got like a redirect or or some kind of on submit action, what we wanna do with it. On the front end, we fetch that form and all the form fields, we render it. When somebody fills out that form, we create a form submission, and that form submission is made up of answers and probably some metadata about when that form was submitted, who's submitting it, etcetera. That sounds like a good start for our planning. I'm just gonna throw these up side by side so I don't forget. And to start this out, I've got my blank instance of Directus, that's what we're using on the back end here. We're just going to hit create a new collection. Let's call this Forms. Now I'm gonna use the UUID for this. Maybe we just do the standard format here. User created, date created, date updated. I'm gonna wanna know what the status of these forms is. Do we need a sort for the forms? Maybe. Do we need a status? We'll just go ahead and add it. Alright, so let's give the form a title, maybe a form description, so that'll be a text area. And then we're going to create another collection called Form Fields. So we'll just go in, create a new collection. What's happening behind the scenes, obviously Directus is creating these tables in MySQL database. The fields, we definitely need to sort on the fields. Do we want a status on those? Maybe, maybe not. No. Let's keep status off of this. You know, do we want to know when the actual form fields were updated? Maybe I'm not super concerned with that, so we'll just leave those off. All right. So for the form fields, when we're thinking through this, right, what are we gonna need on our actual form fields? We're gonna need a name for the field. So what's the field name? We're gonna need a type for the field. So the type is probably gonna be a string, but maybe as far as, like, the interface that I wanna use, I want this to be a drop down. So we can think about input. Great. Do we have a, like, a form field for that or an icon? Yeah. Let's try that. Let's do text area. Text area. Text area. Do we have one for that? We don't. We'll just go with that option. What else do we wanna display? Like a select? Just thinking along the lines of the actual HTML form elements we're gonna use here. Drop down, maybe select. Good looking icons are sparse here. Okay. So that's probably good for now. We'll just make this a label, format each label, we don't want to show as a dot. I can actually copy and paste the choices for our interface into the display here. And this is what shows up on the, like, the index pages where you could potentially see a list of the form fields. Alright, so we've got a name, let's make this half width, make it look good when we're actually creating our form. We've got a name, I could double as label. Trying to think of what else we've got on this. Maybe some type of validation name and type. If it is a select type, we probably need, like, some available choices. Right? So let's go ahead and do that. We'll do that as the, like, JSON repeater. We'll call this select options, I guess. Great. We'll add a text field. So this is the text we're gonna display or we use kind of the label value format. So we set that up, that'll be an input, very simple. And then we have a value. In case we wanna store those differently, we can require a value, make that half width as well. Great. And let's add some conditionality to this that, if the type is select that's when we show this. So I'm gonna go into our field options, let's just hide this by default, and then inside Directus I can add conditionals to the forms inside the Studio by doing something like this. So we'll say show if type equals select. And this is just a description so I can keep my logic together. We'll say if the type equals select we're going to make sure this is not hidden, and that should give me what I am looking for. Right? We could expand this further and add, like, conditions to our front end and etcetera. What we need to do now, though, is add the relationship between those. So in this case, we want a form field. It should only belong to a single form in my mind. I I you could I guess you could make the argument either way, but in this case, let's just create that relationship. So this is gonna be a one to many relationship here. So this is gonna be called Fields, and I'm inside our forms collection, our forms table right now. I'm just going to call this key fields and we're going to look, the related collection is our form fields. And the foreign key inside the form fields table, I'm going to create a new field called form that relates back to the actual form. Gravy will show a link to the item. And if I open up this advanced field creation mode inside Directus, I get a lot more options that I can configure and play with. So I'm going to add a sort field, we're just going to make sure that sort is selected here. That allows us to drag and drop the order of those fields. And then for our triggers, like if we deselect a form field, do I want to delete that item or not? In this case I'm just gonna nullify the form field. Great, we'll display the related values. Bada bing bada boom. Now we should have a form with some fields. Let's just test this out really quickly. So we're gonna go in, we got a form. This is our contact us form, the most standard of all forms on the Internet. And here's the standard contact us form. Great. Alright, so we'll do name, is gonna be email, this will be an input. Great. What are some of the other things that we'd probably want to add to this? Like a placeholder, I would say. First name, that'll be an input. What else? Last name. Okay. Comments, that'll be maybe a text area. And now let's, let's get into the select option. Right? How can we help you? Alright. So for the options here, I need a developer, so we'll give it a label. Maybe we store the value as just developer. I need a sales rep. I don't know. Great. Okay. So a couple of things here. I don't see the actual form values that I I might look. You know, here's they're just showing IDs. How can I fix that, right? I want to make sure this looks nice when we're setting these things up. So what I can do, if I go into my form fields, we have the display template. So this is the default anytime we're referencing this form field. Or I could go into forms and go to the fields section here and I can control the display template just within forms. So that's super handy if you, need different display templates in different places, but in this case let's just go and edit the default one. Let's do the name and then we'll maybe put like a special little dot there and maybe we want to show the type of field that is. And now if we just go back to our forms, great, we can kind of see what we're working with here. I don't really let's put first name, last name, then email, then comments, and how can we help you? Maybe we move that up above. Right. Great. Alright. So now we've got the start to this form. Let's go in and actually start messing with the front end. We've got 47 minutes on the clock. I'm just gonna pull this up. Let's pull up my Nuxt application. This is just the standard Nuxt application that I have out of the box. That's my weapon of choice in this case. And let's just go in, I'm going to create a new directory in the pages directory. So this will give us a route automatically on the front end. I'm gonna call this forms, and then I'm gonna do just form in brackets here, and that will give us, like, a a dynamic form. If I do view script setup, TS setup, this is kind of what I want. Great. We've got the standard Vue Form Component. And we'll just say Form Component. Great. Alright. So let's test this out on the frontend, forms. Test. What? Uh-oh. Some type of issue going on. There it is. Form component. And this has got a layout attached to it where we're showing, like, the logo and things like that. Maybe not a huge issue for this, but, you know, the type form has, like, this full screen form effect, so, you know, maybe we dive into that later. Alright. So now I actually want to fetch that form from Directus, right? So a couple things, we probably need to edit our access control. So by default, Directus locks down every single thing inside your instance. Only admins are able to, interact with the API by default. So we're just going to go in. I'm gonna set readability for our forms and our form fields, and now if I just wanted to test that out quickly, I could do something like this in the browser where I go to my directus url\u002Fitems\u002Fforms, and I can see I get the form data here, and and we'll expand that, once we dive into the Nuxt side of things. Right? So the first thing that we want to do here, we're gonna get the routes, so we know which form to fetch. And then we're gonna be using the Nuxt Async Data Call. So this Nuxt application, I've got a Directus plug in here that basically fetches information from the Directus API. We provide the SDK to the Nuxt application so I could do things like this where I just simply say give me the Directus client. And that way it's shared across the actual Nuxt application. So I get my Directus client, and then we're gonna use the, let's say data is coming back. We're gonna use the Async data call from Nuxt. And within that we are going to return Directus dot request. And then we need to import the readitem from the Directus SDK. Alright. So this is just going to read a single item. We're gonna pick that up from the actual form, from the route itself. So we'll say directives dot request. It'll be read item. We're reading items from the form collection. And then we've got route dot params dot form. Right? So that will it should give us the form. And then I can pass a a set of parameters to the Directus API to get things like form fields, etcetera. But let's just take it from here. We'll wrap this in another div. I always end up doing this just to take a look at my data and just wrap it in a pre tag. And now if I do forms test, we're probably not gonna get back any data. If I was to open this up, I don't really see anything here. Great. Alright. So now let's actually test this with the ID of one of the forms. We'll go in. Great. Okay. Looks like we are getting a cores error. So I've goos something up in my direct to settings. CORS enabled. CORS origin. Should be interesting here. Let me restart this Docker container and see if that solves my problem. If I look at my ENV, I've got HTTP local host 8055. That is that's where my direct instance is running. Okay. There we go. I just needed to set the cores in this case to make sure that local host 3,000 is allowed, and basically, I've done that the quick and dirty way just by allowing any origin here. But, typically, I would set this to, like, local host or something like that. Alright. So we could see here's our form data. That's great. How do we actually get the form fields? One of the nice things about the Direct Express API is the ability to call this data in a single API call to expand those relationships, right? So inside my form fields, maybe on the front end, I don't really care about this extra data when it was created. So let's just say we want the ID, we want the name of the form, we want the oh, it's actually the title of the form. Title. Great. And we want the description. And then we get into the fields. All right, so I could do something like this where I add a dot notation and a wildcard which will give me all the items for that field or all the all the fields within the fields collection or the the related items there basically. So instead, what I'm gonna do, if you're using the SDK, we could pass it a syntax like this where we say fields. And here, we're gonna specify the actual fields. We want the name. We want the type. We want the options, and that should give us what we're looking for here. Right? And we probably do need the ID of those fields as well. We'll look at submissions in a few. Okay, so now we've got these fields coming in, this looks good, we've got a type, we've got a name for these, we can start to construct like a form with this actual data. Now on the front end in this Nuxt application, I have this Nuxt UI library. It would be suicide to try to build this without a form library, ahead of time here or, like, some type of UI library so I could do forms. They've got this really nice setup where you can pass it a form component. They've got Zod validation, support for Yelp and Joy if you're into that. But looking at this, everything gets wrapped in this form group component, which gives you like a nice label, etcetera. So let's just try to iterate through this. Right? We've got a form. They have a u form component. Not concerned with, like, validation at this moment. Let's just display a form. Right? So we're gonna go through Uform group, v 4 fields and forms dot fields. The field, we wanna pass like a I think there's a name component for each one of these. Like, if we go in and look at the props, we gotta pass a name to it. So we'll do name. It's field dot name. Great. What else do we need here? So within the form group, then we are going to do the actual component. So we've got an input here. There's a select box. Great. What else do we have? We had text area. Right? So what I'm going to do, I'm just gonna create a map here, basically. So we'll do, a field map. And I'm just gonna do something like this where, that actually looks pretty good. You input, you text area. That's how all these are name spaced. But what I wanna do instead is use this, like, resolve component or, you know, I could even actually import these as well. So this resolve component is just a helper. You could just straight import them as well. And with Nuxt, you can actually load these dynamically as well by adding lazy in the front of them. So it only pulls in that, that component when it actually needs it dynamically. Alright. So we've got our fields, we've got our form groups within the form, and GitHub Copilot is helpful again in this aspect that we want to do the field dot type inside. We wanna pull that correct component from our map, give it a name, give it an options. Let's take a look. Options are there. Name and value. Okay. So let's save this, see what we get. It looks like we have some we got some form of something going on. I need to change this right away to input so we can actually see those. Let's give this a little bit of styling. Right? Maybe we do, like, a max width XL. Format with math width Excel MX Auto. Great. Maybe we make this a grid. We give it some gap. Why is this actually not should be parsing out my is Tailwind not included in this? What what have we got going on? My styling is not being applied from Tailwind. That's kind of upsetting. We can definitely sort that out though. And then we need a label for this as well. So we'll do the field dot name for the label. Okay, so now we've got a kinda set up in there. Let's do view button. And we'll do, like, a submit option here. Let's add that outside the form group. So we get a submit button at the bottom of this. Right? Okay. So how can we make this kind of like typeform in a way because we wanna answer these one question at a time? Or how can we kind of loop through these? Right? Gillespie. Great. We don't have a state for these, so we'll we'd need to update that as well. Let's do v model equals set up, like, our form data. Form data, maybe we use like a ref for this. It'll be form data, and we can dynamically do field dot ID. Let's put this in like an answers array. Answers. And then we're gonna have the field dot ID. Okay. Alright. And within this, I'm just gonna do answers, and we'll just preset that. All right. So now if I take a look at like my Vue Dev Tools and we go to Form, Okay. I can see my Directus object. I've got my form data. There's my answers. Now if I populate these, is that actually doing what I want? More data. Click form. Do we see our answers? Oh, maybe we want to grab the index of this as well. Field index inform dot fields. That could be helpful. Answers field index field dot id. Bryant Gillespie. Bryantdirectus.io. Let's take a look at the data we're pushing here. Okay. So I can see my fields now. Oh, nope. Form data. There's my answers. Okay. That'll that'll suffice for now. All right. So let's do these one at a time as well, right? So maybe we give this a const current field idx. We set that to 0. And then, how are we gonna want to display this? Alright. So we're gonna render each field conditionally. Alright. So let's just wrap this in a template. This will be the what are we gonna do here? So we still need to show the actual field. V. This could actually be something like this, I think. V if current field current field IDX equals field IDX template. We'll loop for these Fields. Field IDX, inform dot fields key equals field dot field. Nope. Field.id. Alright. Let's see what we've got here. Oh, don't wanna close. Let me open this up. Alright. V field dot ID. We don't need a key on that one. V4fieldsfield. Alright. So now we've got kind of this one field for each. One of the cool utilities that I use a lot is also the auto animate by a group called FormKit. It's, v autoformkit auto animate. One line of code or just a couple lines of code and you've got, like, nice form animations, for, like, list transitions, showing and hiding inputs, etcetera. And we're only gonna show the button if fields, field idx is equal to form dot fields dot length. Field.ex +1 is equal to form.fields.length. So here we shouldn't be showing that button. Give us a padding here. Cool. Alright. Now within this, we probably need, like, a previous and next button as well. So let's just do, like, a flexbox, Go to some gap. We'll do U button. Next. Okay. GitHub Copilot. Alright. So basically, if the current field IDX is greater than 0, we're going to display the previous button. If the current field IDX is not greater than the end of the form, we want to show that. But we need to fix the actual setup here. You form group. Maybe we wrap this in a div. Okay. So now you could see we could kinda go through each one of these. And do I have the actual auto enemy applied properly in this case? Via, it should be. It's not actually animating. Let me refresh. Ah, there we go. Now we're getting some nice animation or animations. Right? So we'll do some margin there. Add justification between these. And we do one of these as variant equals soft. Is that what it is? There we go. Alright, so this is the primary. Great. There we go. Cool. Alright. So now I could go through and fill out this form. Alright. Oh, that's actually submitting the form. I don't wanna do that. Alright. Next. Gillespie. May we change this to button? Great. Give him my email. Cool. Starting to look something like Typeform in this case. Alright. So test, test, test. V if field equals fields dot length. Oh, maybe this is the actual code that we need here. Why are we not seeing the so, oh, because it's not in that actual loop. There we go. So now we should see the submit button. Great. We don't have any type of handler on the submit button. And it's also not doing anything if we actually, were to submit this anyway because we've got, we we don't have anywhere to post it inside Directus. Right? We've got roughly 20 some odd minutes. Let's make this form actually submit. Right? So if we go back to our setup, right, we've got, a form submission, we've got some answers that are within that. So let's go ahead and set up those collections. Let's say form submissions. These will be DUID. This will be let's call this submitted, date submitted. That kinda matches what we've got already. Yeah. Maybe we wanna track the time that it actually user created, date updated, user updated, the status of this submission. You know, you could even like, building your own here, you could certainly, like, track partial submissions and things like that as well if you wanted to. Like, we could submit any time and then update whenever they submit the final one, sort of thing. But let's say we have, like, a date started type of thing. Maybe we wanna, like, store that whenever they start this form submission. So we could track things like how long it actually took them to do this. For that we want the date time. We're just gonna do time stamp. Cool. Day started. Date submitted. Let's make sure we show that field. I'm just going to make one of these full width so I can get what I want out of this. Okay. Alright, so the form submission, we've got to link that back to an actual form as well. So we will do a mini to 1 here. This will be our forms. And let's do forms as the related collection. Now if I open up advanced mode, I can also create the inverse relationship as well. So if I wanted to do something like this where inside forms I've got a relationship back to the submissions, which in this case makes a lot of sense because of probably want to see the submissions. We could set up that and create that relationship in a single go. So there's our form. And then the next thing that we want to do is create a form, I'm gonna call it form submission answers. It could just be form answers, I guess. Always like to, have like a a common prefix, among these things, especially when you start building projects that have a ton of tables in them. Alright. So there's probably a sort on that specific answer. Do we need any of these additional fields? Probably not, right? So when I think of the answer, we've got a relationship back to the form field. So we'll just call this field. Let's Create that. There's the form fields collection. And again, I could do the same kind of thing. And setting this up this way, you know, with all the different database tables and the collections inside Directus allows us to, you know, say I wanted to query all the fields or all the answers from a single field on the form. This makes that a lot easier as well. So if we go in here, let's just add that corresponding one to many relationship as well. Let's call this answers. Great. And then we've got, what, just like a value? I'm I'm just gonna simplify this. I'm gonna sort everything as text. You know, if you had different field types or like numbers or dates, maybe you wanna store those in the underlying database as that specific data type. You know, you could set up some different fields for that like value date, value text, value number, etcetera. But to keep this easy we're just gonna store everything as a value. Alright, so now if we just poke around, right, if I were to like manually add a submission here, I could click create. I'm not seeing the actual answers relationship here. Form answers, form submissions. Oh, that that would be why I'm not seeing it because we haven't created it yet. So the last thing that we need to do is link this form answers to the actual submissions. So we'll go in and on the form submission, this is going to be a one to many relationship, so we'll call this answers. And the submission ID, great. We'll show a link to that item. Just inspect this. It looks great. We got a sort field for those answers. Cool. Everything should be gravy. Right? So what's helpful sometimes, especially, like, if you're doing CRUD operations from the front end, maybe I wanna go into this form and I add a new submission manually. Alright. So just say this. Great. Add some answers. Here's our field that we're answering. Bryant. Create a new one. Last name, Gillespie. And this is a little bit cumbersome, but, again, we're gonna be handling this from the front end. Gmail.com. How can we help? This is gonna be what? Sales rep. And then some comments. Comments. Okay. Alright. So now we've got a submission. I can see kind of the answers here, and these are not super helpful again because we haven't adjusted like the display templates for those. So our form answers, maybe we want to show the the field name, then the actual answer. Great. Let's set that up. For our form submissions, maybe we want to show the, like, the date this was created, submitted. We wanna show the form as well. So we'll just show form dot title. And let's just edit this. Underneath the hood, this is just much syntax. We can also do dot notation to access the related fields as well. Set a little bullet point. All right, so now if we go into forms we can see the contact us form submission a minute ago. And now I can see the individual answers here, which is kind of helpful, right? But let's take a look at the structure of this on the other end. So if I wanted to see an individual submission, I'm just gonna pull this up, copy this, we've got items, form submissions. And then I can get the actual submission here. So here's kind of the structure of this, and this is what we would need to post as well, right? So we've got a form ID, then we have the individual answers. If I do question mark fields, we do just all the root level fields, we grab the answer fields, see what what kind of thing that we need to pass, right? So we got the field ID that we're gonna need to send via the API request when we submit that. And then we've got the value, so what the value of that is. Okay. So now let's take a look at actually submitting this form inside here. So let's create a new function. Let's call it Async function. We'll just call it Submit Form. Great. And what are we gonna do here? We're gonna have, like, a transform the form data to the format you need, submit to the Directus API. Alright. So if we look at our actual form data, how do we get this set up? So it's just an array of answers. I probably could have made this even simpler than that, honestly, and could have done, what, just the the form field ID. Let's just test this out. Right? Form data dot field. Let's do it this way. Answers. Form data. We still do answers. Just make that an object, and then we can add the ID for that. Async function. Oh, misspelled function. Just wondering why that wouldn't compile. Colosbybryant@gmail. Great. And now if I just refresh down here, click form, do we see our form data? There we go. We've got that. Cool. Alright. So when we submit this form, let's, transform this data. So, we're going to map through the answers. So we will do data. This is a good ID as well. We're gonna need the form ID. So this will be route dot params.form. Then we have the answers, but we're gonna need to map those first as well. Map answers. Alright. So object what do we want? Object dot values, object dot keys, form data dot answers. Yeah. There we go. For each. So we're gonna do the data key. Let's just take a look at our structure here. We've got the answers. So we need an ID. No. We don't need an ID for the actual answer. Directus should create that for us, but we do need the field ID. Alright. So we've got answers here. This is gonna be an actual array. And then for that, we're going to do answers dot push field dot key dot value. Answer is key. Great. Hey. We do console dot log just to test this out. Let's Data to be submitted. Save this. See what we got, Bryant. Oh, got all this selected. Gillespie. Okay. And then let's add submit or at click submit form. We refresh really quickly. Right. Gillespie. And now if I hit submit, Form data. Oh, that's a value. That's reactive. So let's do, like, an on that. Bryant Gillespie. Hit submit. Cannot read properties of undefined. Field uncaught in promise. Object. Keys. Answers.key. Form data answers. Console dot log, form data. Let's just unref this form data to see what we're getting. Brandt. Form data. Target. Answers for each key, data dot answers dot push, field key, form data. Oh, duh. Gotta unref that again. Silly sometimes. Great. So now I can see here's the data to be submitted. There's the field. There's my value, etcetera. Now all we should need to do is actually send this. So, let's do create item from the Directus SDK. And just the same way we can fetch those, relationships, those related fields in a single call, we can also push responses to the related fields in a single call as well. So we'll do let's do data. Let's just see it this way. I can't remember what's gonna be coming back. The async data call returns reactive values for data. But we're not gonna use async data here. This will be just like regular fetch. So we'll do await. Actually we can just use the directus SDK here as well. So we'll do directus dot request, create item, and this will be form_ submissions data. Is there anything specific we want to return from this? Nope. Let's wrap this in a try catch and just console error any errors that we receive. Form underscore submissions with the data. Console dot log response. Great. Let's see what we got. Alright. So we're just gonna pick on teammates here. We'll do a Matt Minor. Maddie atexample.com. I need a developer. Help me, please. Alright. So we hit submit. Do we see an actual network call going out here? Post forbidden. Oh, no. What are we doing wrong? Right? Where are we at on time? We got 10 minutes left. We're we're in pretty decent shape. What we don't have here is the permissions to actually create items in form answers and submissions. So we go into our public option here. We can see that, we've got the ability to read form fields and read forms, but we don't have the ability to create form submissions and create answers. Alright? So there's a couple different ways that you might wanna set this up. You know, if you wanted to, like, potentially route submissions through, directly to the Directus API or, you know, through some other, like, server side redirect, inside Nuxt through like a server route or something. Or in this case, we can just give direct public permissions to create these things. Now one of the other things that you might wanna do on the read side of it, you know, I I might want to use like a custom permission or something like that so that, people can't read all the actual form submissions. So maybe just the, IDs, etcetera. Or I could even leave this off as well. I'm just gonna enable this just while in testing. Just remember to restrict this before you go to production. So now if we test this form submission again, what do I get? I get a 200 okay. There's our submission. If I go into Directus, we can see we've got our form submission there. There's all of our answers. That's great. We don't have the date started because I didn't populate that. Right? But what if we were just to do something like this where we've got the date underscore started, And we do new date. Great. And this, let's say to ISO string. Just to be sure Directus will swallow that down. Alright. Let's test this one more time and maybe we can fix the field IDX, form field dot link, currentfieldidx. That's where we goofed up here. Okay. So now we've got Bryant. We got Gillespie. We'll just add a bunch of junk here. Email test at example.com. I need a developer. Oh, I like 35 submit buttons here. Let's go ahead and submit that down. Submit that up and see if we've got that date started field. Why do I not have that? Didn't populate. Oh, I didn't actually pass that in there. Date started. Current field, IDX. This actually needs to be just out of here, doesn't it? Alright. Bryant Gillespie test@example.com. Got a developer. Here's some comments. There's our submit. Great. Take a look at our submissions. There we go. We've got our date started. So now we could actually, like, run calculations on how long it took to fill out this form. We've got all of our answers in the correct place. We've got, like, 6 minutes left. Did we check all the boxes here? Right? We've got a form. We've got the fields. We built the form here on the back end. We've rendered and submitted the form on the front end. Amazing. Let's see if we could just take this a step further. Maybe we wanna create a dashboard for all of our form submissions. Let's see what we can get here. Maybe we do a simple metric. We want to just track the number of form submissions. So that'll be the field ID. We'll just do like a count of that ID. Total form submissions. Boom, I can see we've got 4, right? One of the other things that I like to do when building these dashboards inside Directus is make this dynamic. So what I can do is, let's say I wanted to see the answers for a specific form. What I could do is create a global relational variable. We'll call this, like, form ID. So we'll pick the form. Great. Show that out. Select a form. See what we got here. I'm just gonna move this out of the way. So we can select a form, that variable is named form ID, and then maybe I want to show a list of responses. So let's show the form submissions. And for the filter here, what I want to do, let's just show the date that that was submitted. Great. Under the filter section, what I can do here is go in and I'm gonna create a filter that uses that, that variable that we set up. So instead of hard coding a form value here, I could just do something like this where I say form underscore ID equals that variable and just wrap that in the mustache syntax. So responses for this form. Alright, so when I save this, we shouldn't see any responses here. But if I pick our contact us form, we can see those, then I can open each of those up. Or, you know, we could also change this to something like field ID. So if I wanted to see responses for a certain field, so we'll just change this from forms to a form field. We can go in, the display template for this, maybe we want to show the name of the field. Maybe we want to show the name of the form that it comes from, the title of that form, where are you. There we go. We don't need a filter there. That's gonna be our field ID. How are we doing on time? We still got 2 minutes for this. We'll change this to select a field. Field. Great. Responses for selected field. And then we'll just do the same thing here. We're just gonna change this, right? We'll change the collection to form answers. For our displayed template in this actual list, maybe we want to see the value. So we'll just show the value. And instead of this, we're gonna do the field dot ID equals field underscore ID wrapped in mustache syntax here. Alright, so now if I select First Name I can see a list of all the responses. Great. Looking great. And then maybe we even duplicate this. And let's say I wanna show those values in like a chart or something, right? So we got our donut chart here. We got our styles. The field that we want to count in that case is gonna be the value. And now I can get a breakdown of all of those responses. Great. Cool. 1 minute 24 seconds left. Ring the bell. Again, this has been a fun episode. You know, taking this from here, obviously, like, I would totally improve the styling, add some validation into it, but this would be super handy to have a form builder that you've got direct control over, that you don't have to pay outrageous prices for, well, like 30 submissions or something like that. I don't know. Let's look at the pricing. What do we have on Typeform? I like Typeform a lot. The pricing is a bit crazy. Alright. $25 a month gets you or $29 a month gets you a 100 responses. Using Directus, get way more than that for less. Amazing. Alright. That's it for this episode of 100 apps, 100 hours. I hope you've enjoyed it. I sure have. We'll catch you next time.","e02b678a-4832-4658-8fac-490a73dce2fd",[2308],"fad4d940-21cd-4966-9a11-e5951a79c61d",[],{"year":978,"number":2062,"id":2311,"episodes":2312,"show":2322},"14fda5f2-95de-4dbe-a4e2-3fd956c21c19",[2313,2314,2315,2316,2317,2318,2319,2320,2321,2298],"9a3a8ffa-a27b-421c-93cf-3da2dcb726e9","d072a935-906e-4208-a5dc-e9b117d0ab29","b9f1d4cf-f53c-49db-9e87-adf7e3b9ff99","aad8d674-2b58-4604-8e43-b98f7c6e05cb","6bff0c09-ad87-4d5c-b227-89b8c3c02220","6fb9aa9a-2b59-44b6-b78f-d1831fa657c6","620cf225-a23a-415a-ad95-9ba8e2dec984","b8b36125-7a4a-40e4-85f6-f4fe9138085e","385bdd7d-038d-4f9c-8037-357e5272420a",{"title":322,"slug":323},{"id":2324,"slug":2325,"vimeo_id":2326,"description":2327,"tile":2328,"length":159,"resources":12,"people":12,"episode_number":13,"published":2329,"title":2330,"video_transcript_html":2331,"video_transcript_text":2332,"content":12,"seo":2333,"status":19,"episode_people":2334,"recommendations":2337,"season":2338},"47fc9836-9b70-4756-be35-fb321a3e3cf7","european-athletics","985790892","Discover how European Athletics use Directus as they support 51 national federations for track and field sport and organize events including the European Athletics Championships. Starting with their aim to bring their website in-house, hear about how Directus powers the European Athletics website and their ambitious plans to treat it as the backbone of their digital strategy.","cc4491dc-b67e-4531-8c14-9a51a2f8b2c3","2024-07-24","European Athletics","\u003Cp>Speaker 0: European Athletics is the umbrella organization of National, Athletics Associations, track and field. We represent that sport in, in the continent of Europe. We have 51 national federations that are our members and we are the governing authority which means we are here to support them to represent the sport. European Athletics also organises its own events and our biggest event is the European Athletics Championships which we organize every 2 years. It's our flagship event where all our members come and bring their best athletes to come to to the European Athletics Championships.\u003C\u002Fp>\u003Cp>The European Athletics Championships predates Internet and, of course, once a website kind of became the thing to do, it as well evolved and developed with us as an organization. And so our past experience where we had a really robust website, it was basically rented. And, realizing once the relationship ended, we had nothing. We didn't have, any of the data, any of the ownership of, what we previously built. So we went for a solution where we wanted to in house, where we take control, where we want to design and build the website according to our needs and wishes to really have a proper product for the 21st century.\u003C\u002Fp>\u003Cp>Speaker 1: Our current use case for directors is for serving data and content on the European Athletics website. Currently, our editors, who are the main primary users, use that to create different types of long form editorial content, which goes on the website. While our current use case for Directus is limited to using it for our website, going forward, we have the use case will increase quite a fair bit. For example, European Athletics is in the process of developing its own native apps, both for Android and iOS. This would be the first time in Europe in aesthetics history that we'll have our own native mobile apps.\u003C\u002Fp>\u003Cp>And directors would be the central data and content platform which will power these mobile apps. I can safely say that Directus is one of the better CMSs that I've worked with. The data model is very, very intuitive. It's very flexible. If we have to deploy any new services, we can do that on the go using the unified API that we may have.\u003C\u002Fp>\u003Cp>So that means that the time to market for any new functionality is reduced a fair bit. It's also setting up integrations with directors because of this flexible data model is very easy. You can set up new tables or collections for all these different integrations very easily, whether it's SEO, whether if we want to do e commerce in the future, analytics, login. All of these can be integrated within the data model very quickly. So from a product perspective, this provides us a lot of benefit.\u003C\u002Fp>\u003Cp>We can work with leaner teams, who are more dynamic, who are and allows the developers to be more creative, which is always appreciated by them. But for me, as a product manager, what I value most about Deritus is the quality of the client servicing. It provides us a lot of comfort because we are a small team internally. So for us to have such strong and dedicated partners is always very helpful. We have found a partner who we know we can work with for the foreseeable future because we will not scale up as fast as the technology and the fan aspirations and needs will scale up.\u003C\u002Fp>\u003Cp>We know that directors can provide the requisite system that is needed so that we are able to continuously serve all our different constituents in a very scalable, in a very flexible manner on the go. So for us, that is the biggest advantage of having directors as a partner. So if we want to provide a more robust fan experience or if we want to keep up with the changing digital ecosystem, which is happening in the sports world, we know that with the help of directors, we'd be able to do that fairly seamlessly.\u003C\u002Fp>\u003Cp>Speaker 0: This is not just the website. This is the backbone of our digital strategy going forward. The, European Athletics championships every 2 years is our peak event. However, in between those two events or those 2 years, we want to build and offer experiences for fans on an everyday basis and Directus positions us to achieve that.\u003C\u002Fp>","European Athletics is the umbrella organization of National, Athletics Associations, track and field. We represent that sport in, in the continent of Europe. We have 51 national federations that are our members and we are the governing authority which means we are here to support them to represent the sport. European Athletics also organises its own events and our biggest event is the European Athletics Championships which we organize every 2 years. It's our flagship event where all our members come and bring their best athletes to come to to the European Athletics Championships. The European Athletics Championships predates Internet and, of course, once a website kind of became the thing to do, it as well evolved and developed with us as an organization. And so our past experience where we had a really robust website, it was basically rented. And, realizing once the relationship ended, we had nothing. We didn't have, any of the data, any of the ownership of, what we previously built. So we went for a solution where we wanted to in house, where we take control, where we want to design and build the website according to our needs and wishes to really have a proper product for the 21st century. Our current use case for directors is for serving data and content on the European Athletics website. Currently, our editors, who are the main primary users, use that to create different types of long form editorial content, which goes on the website. While our current use case for Directus is limited to using it for our website, going forward, we have the use case will increase quite a fair bit. For example, European Athletics is in the process of developing its own native apps, both for Android and iOS. This would be the first time in Europe in aesthetics history that we'll have our own native mobile apps. And directors would be the central data and content platform which will power these mobile apps. I can safely say that Directus is one of the better CMSs that I've worked with. The data model is very, very intuitive. It's very flexible. If we have to deploy any new services, we can do that on the go using the unified API that we may have. So that means that the time to market for any new functionality is reduced a fair bit. It's also setting up integrations with directors because of this flexible data model is very easy. You can set up new tables or collections for all these different integrations very easily, whether it's SEO, whether if we want to do e commerce in the future, analytics, login. All of these can be integrated within the data model very quickly. So from a product perspective, this provides us a lot of benefit. We can work with leaner teams, who are more dynamic, who are and allows the developers to be more creative, which is always appreciated by them. But for me, as a product manager, what I value most about Deritus is the quality of the client servicing. It provides us a lot of comfort because we are a small team internally. So for us to have such strong and dedicated partners is always very helpful. We have found a partner who we know we can work with for the foreseeable future because we will not scale up as fast as the technology and the fan aspirations and needs will scale up. We know that directors can provide the requisite system that is needed so that we are able to continuously serve all our different constituents in a very scalable, in a very flexible manner on the go. So for us, that is the biggest advantage of having directors as a partner. So if we want to provide a more robust fan experience or if we want to keep up with the changing digital ecosystem, which is happening in the sports world, we know that with the help of directors, we'd be able to do that fairly seamlessly. This is not just the website. This is the backbone of our digital strategy going forward. The, European Athletics championships every 2 years is our peak event. However, in between those two events or those 2 years, we want to build and offer experiences for fans on an everyday basis and Directus positions us to achieve that.","398ca513-07a1-4651-85b1-fd5f534608b5",[2335,2336],"ac3bf9f0-b823-447d-acce-9f928f8542f4","fd037aba-7a85-4a6b-974c-28ee24a8f5b6",[],{"year":978,"number":2339,"id":2340,"episodes":2341,"show":2342},44,"41fa8e23-c54c-4789-97a8-07d45a1b2088",[2324],{"title":2343,"slug":2344},"Client Cache","client-cache",{"id":2346,"slug":2347,"vimeo_id":2348,"description":2349,"tile":2350,"length":1094,"resources":12,"people":12,"episode_number":177,"published":2329,"title":1990,"video_transcript_html":2351,"video_transcript_text":2352,"content":12,"seo":2353,"status":19,"episode_people":2354,"recommendations":2357,"season":2358},"b696b1c5-75ae-430f-9c5d-a00deaf8defb","technically-access-control","962588514","What happens when a non-technical person attempts to get technical? In episode 2 of Technically I'm Lost (TIL), non-technical marketer Matt attempts to set-up roles and permissions for a partner directory from scratch, with help from resident Directus expert Bryant. ","76d08d20-6c8c-427e-839f-9d912b1c62b3","\u003Cp>Speaker 0: Everybody, welcome back to the part 2 of Technically, I'm Lost, t I l. As, again, you're probably joining us from the last episode. I'm Matt, and this is Bryant. Put a little French spin on it today. If you've watched part 1, if are we gonna did I just cut you off?\u003C\u002Fp>\u003Cp>Speaker 1: I no. No. It's okay. I was gonna say do I look French to you? But\u003C\u002Fp>\u003Cp>Speaker 0: We'll have to ask, Alex Chopin, our director of engineering from France. I digress. But yeah. So if you watched part 1, if you haven't, you're probably confused. We suggest you go watch part 1, because this is part 2 of part 1.\u003C\u002Fp>\u003Cp>But, part 1, we are building a partner directory for us to use with our current partners. In that first episode, we planned out, how it was all gonna look, and we built out the form, which I can actually pull up my screen here and show you kind of how far we've gotten along. So, we I will say I, with the help of Bryant, me being a nontechnical person, learning directors for the first time, set up, this initial form in here. So we were able to set up, you know, point of contact, some information about the actual agency themselves, what they specialize in, with the drop down, partner logos, and we had, these projects, here as well as as this. So the general idea just to catch you up, is we wanna create a partner directory on the director's website for our partners, and this is the back end to get that whole thing running.\u003C\u002Fp>\u003Cp>So where we left off last time, Brian, do you remember?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. Kind of. No. I I think what we need to do now, Daniel, like, we've got our agency partner collection configured.\u003C\u002Fp>\u003Cp>That's looking nice. We need to go through and create the actual projects that we want them to add. So what's a data model look like for that? And then we need to scope a role for our partners. So, they can only edit their content and only, like, the agency partners and the project's collections.\u003C\u002Fp>\u003Cp>Speaker 0: Great. Okay. So let's start with that part 1. So the projects, to give you a little background on how we're thinking about this. So when somebody comes to, you know, direct style slash partners, they'll filter down on country and specialization.\u003C\u002Fp>\u003Cp>By then, they'll find an agency they like. They click on them, and then that page will have a showcase of the projects that that agency has done with Directus. So we'll need to create that collection, as you mentioned. What's a good starting point for this? Is it just diving in?\u003C\u002Fp>\u003Cp>Is it a little bit of planning your strategic approach to this? Or what do you think?\u003C\u002Fp>\u003Cp>Speaker 1: I I mean, I I think at a high level, you wanna know what what goes on a project or or, like, what form feels, like, what are we gonna display when you click into a project. Mhmm. You know, if I'm imagining it, like, you've got an agency page where you're you're surfacing all these projects, maybe, like do we have an extra page for each project as well? So I can I'm on the agency page. I see a list of their recent projects.\u003C\u002Fp>\u003Cp>I click in the project, and I get, like, a a mini case study type of thing, or we're gonna show those in a model, like, a pop up window. How what do we we wanna do there?\u003C\u002Fp>\u003Cp>Speaker 0: I think it would be good to have a page. I like what you said about, like, a mini case study page. So maybe we'll have, like, the a scroller here of, like, they can go back and forth for, like, images of the actual website. And then\u003C\u002Fp>\u003Cp>Speaker 1: I don't like it.\u003C\u002Fp>\u003Cp>Speaker 0: You don't like it?\u003C\u002Fp>\u003Cp>Speaker 1: I I want a grid. I don't like it. Give me a grid of projects.\u003C\u002Fp>\u003Cp>Speaker 0: A grid. Wait for the individual project.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, no. No. No. For the individual projects, I I'm thinking well, you got, like, an image gallery. I I'm thinking like a grid still.\u003C\u002Fp>\u003Cp>Speaker 0: Big grid guy.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Let me I'm gonna share. I'm gonna share a link.\u003C\u002Fp>\u003Cp>Speaker 0: Let me share.\u003C\u002Fp>\u003Cp>Speaker 1: This is, again, this is the agent c OS link. So this is a sample starter kit project that that I put together, to showcase what you could do with Directus. Let me I guess I could put it in the chat here instead of our team comps.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Let me pull this up over here.\u003C\u002Fp>\u003Cp>Speaker 1: This is not a a like, the text doesn't really jive here, but, you know, you've got, like, a a title for the project. You got, like, a summary, then you have some content, and then you have an image gallery that, you know, if you click on one of those images, then you could cycle through.\u003C\u002Fp>\u003Cp>Speaker 0: Wow.\u003C\u002Fp>\u003Cp>Speaker 1: Instead of, like, a instead of a carousel on the actual page, show a grid. And then you have some metadata, like, hey. What's the client? What's it built with? What's the cost?\u003C\u002Fp>\u003Cp>I I don't know if we wanna probably omit that for this. You know? Would be nice to show, like, what it was built with. Those could just be, like, tags. So this is kinda what I'm envisioning.\u003C\u002Fp>\u003Cp>You know, the the header there is probably not it doesn't really jive with, like, the direct as brand guidelines. So we'd probably do something different. But, you know, I I think at a high level, you got a title, a short summary of the project. You've got a featured image for the project. You have some content, in case they wanna expand or go into as much detail as they want to.\u003C\u002Fp>\u003Cp>And then there's probably, like, the the content would probably be, like, text. Right? Mhmm. And then the yeah. And then you could have, like, a separate gallery if you wanted to.\u003C\u002Fp>\u003Cp>Speaker 0: Separate gallery.\u003C\u002Fp>\u003Cp>Speaker 1: And then, you know, like, some tags for what it was built with, like front end type of stuff, I guess. Like, hey. Is it built with Next? Is it built with Next? I I don't know.\u003C\u002Fp>\u003Cp>I don't know if that's necessary either.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. I like this. So, yeah, so we would create a collection with these different items inside of Directus. Last time, if you can refresh my memory, when we created the projects here, this was a one to mini or a mini to 1?\u003C\u002Fp>\u003Cp>Speaker 1: So it depends on where you're at. Right? Those are 2 inverse relationships. If you are a for this example, if we're on the agency partner, projects would be a one to many relationship because I've got one agency, many projects. But if I'm inside projects, it's the reverse.\u003C\u002Fp>\u003Cp>Right? I've got, one project or, actually, I'm sorry. Is it many projects to one agency, basically.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That makes sense.\u003C\u002Fp>\u003Cp>Speaker 1: Like a project can only have belong to a single agency is what I'm trying to say.\u003C\u002Fp>\u003Cp>Speaker 0: That makes sense. So we'd have this is the one to many from the agency side. So if we go here, this would be the many to 1.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. So what you're gonna have to do here is go into our data model for projects because we we didn't add any fields for that.\u003C\u002Fp>\u003Cp>Speaker 0: Mhmm. Where is our here. So from the agency side, if if I click here, this will take me\u003C\u002Fp>\u003Cp>Speaker 1: automatically there.\u003C\u002Fp>\u003Cp>Speaker 0: It's over here. Great. So just like we did last time, you know, obviously, going through, filling out these fields. Title, I assume, would be just a standard input field. Title, String.\u003C\u002Fp>\u003Cp>Speaker 1: There you go.\u003C\u002Fp>\u003Cp>Speaker 0: I mean, required, obviously. Project title, let's see. The name of the client, input project client. And,\u003C\u002Fp>\u003Cp>Speaker 1: again, I would probably just do, like, client name. Right? Because there's, like, where else are you using client name? You're not.\u003C\u002Fp>\u003Cp>Speaker 0: Naming conventions. I'm about to get open that can of worms. So short summary. Would you use an input field, or would you use a text area field? Or\u003C\u002Fp>\u003Cp>Speaker 1: Text area for sure. No wysiwyg. I don't wanna deal with rendering HTML or, stripping out HTML. I I just want, like, a short description to display on a card or in a heading.\u003C\u002Fp>\u003Cp>Speaker 0: And if I'm gonna add a placeholder here, I will do it in the placeholder field.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. You've also got the help field or the the note field, I think, is is what it is as well. Like, the placeholder is gonna be it's gonna disappear as soon as they start typing. Right? The if you go to the field tab when you create 1, you got some helper text that you can add.\u003C\u002Fp>\u003Cp>Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So here, we are creating the main image versus the gallery of images that they could just, like, see. So\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. There there's 2 ways you could you could do that. Right? You could just have an image gallery and use the first item in the gallery as, like, the featured image. It's kinda a matter of personal preference.\u003C\u002Fp>\u003Cp>Usually, I like to be more explicit. And because of the way that Directus, like, queries the data and, like, if I'm showing a list of projects, I I can create it or treat it my gosh. I came to talk today. You could treat it like GraphQL, and I could tell it specifically the fields that I want. So, you know, on the index page where we're showing a list of projects, I can just grab the featured image and not worry about the image gallery.\u003C\u002Fp>\u003Cp>So that's that's probably why I would do 2 separate fields for those. So you might have, like, a, like, a featured image field and then, like, an image gallery or gallery, whatever you wanna call it. This is just a single image that you're gonna use across the site.\u003C\u002Fp>\u003Cp>Speaker 0: Featured No.\u003C\u002Fp>\u003Cp>Speaker 1: No. No. No. What did you do?\u003C\u002Fp>\u003Cp>Speaker 0: I created a featured\u003C\u002Fp>\u003Cp>Speaker 1: It's not just messing with you. No. You don't need your Oh,\u003C\u002Fp>\u003Cp>Speaker 0: Don't be doing that today. Oh, man. Got my heart rate up. So next part, if we're doing a showcase or gallery, I assume files based on the image here.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. Definitely.\u003C\u002Fp>\u003Cp>Speaker 0: And then showcase.\u003C\u002Fp>\u003Cp>Speaker 1: Bro, call it image gallery or a gallery.\u003C\u002Fp>\u003Cp>Speaker 0: I was gonna call it a project showcase.\u003C\u002Fp>\u003Cp>Speaker 1: I feel like we're going to The Price is Right if we're, like, doing showcase.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So I don't know if we want this required because as long as they have the the Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: You don't necessarily have to add an image gallery. Cool.\u003C\u002Fp>\u003Cp>Speaker 0: So what else what else here?\u003C\u002Fp>\u003Cp>Speaker 1: That's it.\u003C\u002Fp>\u003Cp>Speaker 0: Really? Alright.\u003C\u002Fp>\u003Cp>Speaker 1: Now you're also gonna want what else do we have? We had the we had, like, the built with tags. That's gonna be yeah. You can use the tag interface for that. It'd be fine.\u003C\u002Fp>\u003Cp>Speaker 0: Built with JSON.\u003C\u002Fp>\u003Cp>Speaker 1: There you go. And in this case, you're gonna, like, do allow other values. You could also add some presets to this if you wanted\u003C\u002Fp>\u003Cp>Speaker 0: to. I'll add a few\u003C\u002Fp>\u003Cp>Speaker 1: where people can just choose those. There you go.\u003C\u002Fp>\u003Cp>Speaker 0: There you go. Allow other values. So when they type in, it'll add it. So that'll be a continually growing list. Big fan.\u003C\u002Fp>\u003Cp>Speaker 1: So for. You could force alphabetical order if you wanted to. Way to standardize. If not, you could have, you know, potentially, like, next being shown in a separate place. Mhmm.\u003C\u002Fp>\u003Cp>Also, just to clarify, that's not my stomach growling. That is this squirrel that I have on my lap.\u003C\u002Fp>\u003Cp>Speaker 0: Dog is protecting the house. Love it.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, it's not protecting the house. It's it's being a pain in the butt. Tiny dogs, what do you do?\u003C\u002Fp>\u003Cp>Speaker 0: Would you force, like, capitalization and and white space and all that sort of stuff? It doesn't matter.\u003C\u002Fp>\u003Cp>Speaker 1: I don't know that I would just because, like, some of these well, I'm trying to think of a a good example of, like, a framework that prefers lowercase. I can't really think of any. Like, the white space, you might you know, like, white space, I would definitely, like, trim the white space, like, the the beginning and end, just so you remove white space. There you go. That way, there's, like, no extra, like, the space that's that's big, like breaking formatting or anything like that.\u003C\u002Fp>\u003Cp>Speaker 0: Cool. Alright. Awesome. Anything else I should consider here, in advanced field creation mode? Or Nah.\u003C\u002Fp>\u003Cp>Speaker 1: I wouldn't worry about it.\u003C\u002Fp>\u003Cp>Speaker 0: Great. So I feel like we're good there. I\u003C\u002Fp>\u003Cp>Speaker 1: think the next thing\u003C\u002Fp>\u003Cp>Speaker 0: we were looking for. Oh, the main content, which would be text area again.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. No. WYSIWYG. This, we're gonna give them formatting options. Why is it why is it main content?\u003C\u002Fp>\u003Cp>Not just content.\u003C\u002Fp>\u003Cp>Speaker 0: Because it's different from the short summary. Long\u003C\u002Fp>\u003Cp>Speaker 1: But that's already it's already a different field, my man.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Content it is.\u003C\u002Fp>\u003Cp>Speaker 1: Good here? I'm good. I'm good. My dog is not good. This This data model is good, though.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Feels good. I might actually go and make this content required. At least I have to share something about it. Great.\u003C\u002Fp>\u003Cp>So our mini to 1 is now done. Yeah. I guess we could go here. Let's say we're going to start a new project. Everything looks good.\u003C\u002Fp>\u003Cp>Yeah. Yeah. Amazing. Alright. So we have our partners.\u003C\u002Fp>\u003Cp>We have our one to many connection to the projects, then we've got the many to one back to the partners. So feel good about these 2 collections. Now it's about actioning the actual, role based permission control. So when somebody signs up on the agency side, they're gonna have to go in and be able to add these projects. Maybe invite somebody to add these projects, invite members of the other team if they have to update, agency information.\u003C\u002Fp>\u003Cp>Let's dive into role based access control. RBAC, direct us off. This is my first time ever using this, so it should be interesting. Oh,\u003C\u002Fp>\u003Cp>Speaker 1: okay. Alright. So what do you think you know?\u003C\u002Fp>\u003Cp>Speaker 0: What do I think I know? I see this big plus, and then that's what I think I know is I click this to create something.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That would be a a user. But as far as, like, what we're trying to achieve, like, what's it what what is your current plan of attack in your mind? And then we'll, like, course correct.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Current plane of attack is and I will visualize this. So we're gonna get a partner in, and that's this little circle. This is a partner that fills out our partner request form. They go through the process.\u003C\u002Fp>\u003Cp>Once they have become a partner, via our partner manager, then that is when\u003C\u002Fp>\u003Cp>Speaker 1: Are are you supposed to be showing something on the screen? All I see is the user directory.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. Yes. I am. Here we go.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Alright. Yeah. Now, we're seeing the circles. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. You missed my circles. Alright. But these these are the phases.\u003C\u002Fp>\u003Cp>Right? So, actually, this is going to be, there's no preset yellow, of course. So I'll use orange. Initial come in, person fills out the form. They are vetted, become a partner, and then at this point is when we would send the send invite to partner directory.\u003C\u002Fp>\u003Cp>Send them here, and then once they fill that out, then they'd be officially a partner as part of that ecosystem.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: These circles are super misshapen. So at that point\u003C\u002Fp>\u003Cp>Speaker 1: I think that's an oval.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It's definitely an oval. This is gonna be main oh my gosh. This is crazy. Main agency contact.\u003C\u002Fp>\u003Cp>They would fill fill out that part that we built. And then at this point, they can invite their own team. And I think that's it. So we'd have to figure out a role for this person? Because they're coming in cold, so they have to be able to log in.\u003C\u002Fp>\u003Cp>I think we'll provide them a username and a password they can change.\u003C\u002Fp>\u003Cp>Speaker 1: Yep.\u003C\u002Fp>\u003Cp>Speaker 0: And then we'll have to figure out a role for this person who doesn't necessarily have the ability to change the point of contact information, but just has access to a few fields in that collection, to update the agency information if they need to.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. So we've got a admin at the agency, and then we have, like, a a team member at the agency\u003C\u002Fp>\u003Cp>Speaker 0: Yep.\u003C\u002Fp>\u003Cp>Speaker 1: Kind of set up.\u003C\u002Fp>\u003Cp>Speaker 0: Exactly. So 2 two roles, I think.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Now can a person be a part of multiple agencies?\u003C\u002Fp>\u003Cp>Speaker 0: I don't think so. I think that's too too much.\u003C\u002Fp>\u003Cp>Speaker 1: Too much. Too much.\u003C\u002Fp>\u003Cp>Speaker 0: I'm trying to think of any scenario. It seems like a very rare edge case that would be the case and would have to probably be treated on a one to one basis.\u003C\u002Fp>\u003Cp>Speaker 1: You could still accommodate that from, like, a using a separate email, basically, I guess. Separate login Maybe. Kind of scenario.\u003C\u002Fp>\u003Cp>Speaker 0: This is also the v two of this partner directory, I'd love to build in a thing where, like, an agency partner can go in and request a license or something for, like, a new project that they're building. I'm trying to think that would that would be either the admin or the team member can make that request, but it would be logged to that specific agency. So Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: I I think this is good.\u003C\u002Fp>\u003Cp>Speaker 0: Good start.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. We'll just scope it down to, like, hey. You can only be a part of 1 agency at a time, which makes sense to me.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Cool. I like it.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So what are you what are you gonna do, man? What are you gonna do?\u003C\u002Fp>\u003Cp>Speaker 0: I'm gonna come to access control. I'm gonna click this big purple create role button. The role I create, the naming convention here is gonna be well, you know what? Why don't you tell me? Because I know whatever I put in this little purple Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Thank you. So you give me The no. I I would just say, like, agency partner admin or something.\u003C\u002Fp>\u003Cp>Speaker 0: Agency partner admin. Not giving them admin access because that would\u003C\u002Fp>\u003Cp>Speaker 1: be No. No. No. No. That's just, like, a different thing.\u003C\u002Fp>\u003Cp>Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: So app access only.\u003C\u002Fp>\u003Cp>Speaker 1: App access only.\u003C\u002Fp>\u003Cp>Speaker 0: As a small pardon me.\u003C\u002Fp>\u003Cp>Speaker 1: Yep.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. So now, this is where things get interesting, because we have our website powered through this, which we've got, like, the blocks. So there's going to be a ton of these in here. And it's we're gonna have to provide the right access\u003C\u002Fp>\u003Cp>Speaker 1: to these. Correct.\u003C\u002Fp>\u003Cp>Speaker 0: All we need to really give them access to is the agency specific things.\u003C\u002Fp>\u003Cp>Speaker 1: Right? Correct. You got it, boss man. Which So yeah. Command f comes in handy here, for sure.\u003C\u002Fp>\u003Cp>We don't have a search in here yet, but I think it's on the docket.\u003C\u002Fp>\u003Cp>Speaker 0: You know, feature request would be great if we had the you know, it actually is frozen up here. That's very nice. I was gonna say, wait. If we could freeze that top column.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So what are you gonna do? Like, how do you wanna play this? You you want me to tell you you wanna step through this?\u003C\u002Fp>\u003Cp>Speaker 0: No. Let's I wanna think through this because I am looking at this. I haven't really messed with roles before, but, obviously, this is CRUD. You know, create, update, re what's the r for CRUD?\u003C\u002Fp>\u003Cp>Speaker 1: Update. The r? Oh, read. Read. Read.\u003C\u002Fp>\u003Cp>Speaker 0: Update. Delete. Yeah. So for the agents for the admin, we're gonna want them to be able to create.\u003C\u002Fp>\u003Cp>Speaker 1: Are you? Are you are you not just gonna, like, create a partner and then add the user as part of that partner? Like, is this is this is kind of a high touch program. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So we would create the agency partner, get the send them the login information. So it would already be created, so they don't need access to create, plus they might go and create stuff that they shouldn't.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. In this case, like, the only thing that they need to be able to do is read their own edit their own. We're not gonna let them delete. We're going to let them create, edit projects. They should be able\u003C\u002Fp>\u003Cp>Speaker 0: to delete projects too. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I was I would allow them to delete their own projects out of the system, but maybe not if it's published. Right? So as long as it's not published, you could delete it. If it is if it's already been published, like, we gotta go through some kind of flow so you're not breaking website.\u003C\u002Fp>\u003Cp>Speaker 0: So this would be custom.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. We don't have a field for we didn't add a field for the status of a project. Hey. Like, is this draft or published or not?\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Add that to the to do list. Alright. And then project files would be the same where you can add project files as you can. Because this is this is just the image gallery. Right?\u003C\u002Fp>\u003Cp>So you need to be able to delete those as well.\u003C\u002Fp>\u003Cp>Speaker 0: So question. We have the share functionality, which barely gets used, admittedly, because I think there's a little bit of confusion around, like, what it actually allows for. So in this scenario of sharing a collection, would it be useful for them to share with the non, like, the non admin users That works.\u003C\u002Fp>\u003Cp>Speaker 1: So if we're gonna keep everybody contained inside, we probably wouldn't use share. We would just give them access. But but, like, say that somebody did not have user access and you wanted to share something with them, you could do that through the share functionality of, like, hey. I wanna share this one specific piece of content with someone, that's not a part of the Directus application.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So this makes sense. So I think for if they're if they're putting in one of those projects in the showcase and they wanna share it with, like, the client and be like, hey. We wrote this little mini study, like, or case study about the project we did with you. Can you confirm it?\u003C\u002Fp>\u003Cp>Would that be a good application for this?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. You could potentially do that. Alright. Like, if you if you wanted to see it before you go live yeah. Hey.\u003C\u002Fp>\u003Cp>Hey. Well, the other rub is, like, if you're rendering this on the front end, they're probably gonna wanna see the front end as well. So, you know, that kinda goes into, like, a live preview mode on the front end. But just to see the actual content to get approval, you could certainly add share access for this.\u003C\u002Fp>\u003Cp>Speaker 0: And we would do that at the\u003C\u002Fp>\u003Cp>Speaker 1: project? Project level. Yeah. Cool.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. This feels good so far.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Saves automatically. So Yeah. Alright. Agency So\u003C\u002Fp>\u003Cp>Speaker 1: so what's next?\u003C\u002Fp>\u003Cp>Speaker 0: I'm gonna create the other role just so\u003C\u002Fp>\u003Cp>Speaker 1: it's Okay. Alright.\u003C\u002Fp>\u003Cp>Speaker 0: Agency, partner. Boy. Not the admin. Team role. That's it.\u003C\u002Fp>\u003Cp>Speaker 1: Team. Just team. Just team. It's already a role.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, man. Cracked me up. Alright. So here, they're not going to have access to ad. They'll have access to c, but we don't want them to have access to edit it because that'll be from the admin.\u003C\u002Fp>\u003Cp>No. Actually, we do, because can we use a custom permission here, so where they can't see the proof of the point of contact and, like, edit that?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. You certainly can.\u003C\u002Fp>\u003Cp>Speaker 0: So\u003C\u002Fp>\u003Cp>Speaker 1: It would be within the field permissions.\u003C\u002Fp>\u003Cp>Speaker 0: Field permissions.\u003C\u002Fp>\u003Cp>Speaker 1: So here's the here's the fields that they can update. So you're going to hit show more there so you can see the rest of them. Mhmm. Alright.\u003C\u002Fp>\u003Cp>Speaker 0: So check everything except for point of contact group because everything that's within that group.\u003C\u002Fp>\u003Cp>Speaker 1: That's a great question. Yeah. Let's try it and see.\u003C\u002Fp>\u003Cp>Speaker 0: Okay.\u003C\u002Fp>\u003Cp>Speaker 1: I I I I think it will just be I I think you'll still have to do contact first name, last name, email. Or well, you don't want them to edit that. I'm sorry. Yeah. Leave that unchecked.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Printer name. Divider. Country. Partners.\u003C\u002Fp>\u003Cp>Work description, specialization, logo, team size, projects. Alright. So now they have, custom access to that, if that makes sense.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. They're gonna need access to projects, so they can add projects, update projects.\u003C\u002Fp>\u003Cp>Speaker 0: Don't want them to be able to delete. We wanna leave that to the admin. And project files, add, see, and edit. Yeah. I think this makes sense for v one rule.\u003C\u002Fp>\u003Cp>And if we need to update it later, we can. It's the beauty of automatically saving. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Cool. Cool.\u003C\u002Fp>\u003Cp>Speaker 0: Good with these 2. Now the tricky part is\u003C\u002Fp>\u003Cp>Speaker 1: alright. What's what is the what is what's the tricky part?\u003C\u002Fp>\u003Cp>Speaker 0: The tricky part is whenever we create, when we manually create an agency partner inside of this, we have to create their contact information for that that point of contact. So, like, user directory, we can see here. We can create a user within here. So we fill out, like, their first name, last name, email, password. Interesting we have company info already baked in here.\u003C\u002Fp>\u003Cp>So\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That is, that's interesting. I think that is a factor of the docs.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. No. This is good. Okay. So if we were to create a fake person here, fake person, and I'll just add in my email.\u003C\u002Fp>\u003Cp>We'll have to blur these so they don't pop up.\u003C\u002Fp>\u003Cp>Speaker 1: I'd I'd hopefully will be deleting fake person later. And you're gonna have to change your email address because you're already\u003C\u002Fp>\u003Cp>Speaker 0: I'm already a member here.\u003C\u002Fp>\u003Cp>Speaker 1: You're already in in the system.\u003C\u002Fp>\u003Cp>Speaker 0: Auto pop are you seeing my auto\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It looks like it's, spazzing out a little bit there.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I don't know if you see, like, the pop up where it's, like, all of the things I've pre filled.\u003C\u002Fp>\u003Cp>Speaker 1: No. No. I don't see the pop up. Alright. It has to do with your sharing settings, I'm sure.\u003C\u002Fp>\u003Cp>Speaker 0: Gotcha. Well, that's good because there's some private info in here. So I'm gonna use a personal Gmail here. I'm not worried about that. I'm not worried about any of that.\u003C\u002Fp>\u003Cp>I just wanted to create the fake person stuff. So\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Here.\u003C\u002Fp>\u003Cp>Speaker 0: I will save. Save. And then for this, I'll go check my email.\u003C\u002Fp>\u003Cp>Speaker 1: Did you did you create a password for them? You can also just open up an incognito window and log in. So, like, if you do the if you invite them versus so there's a there's 2 ways. Right? You can invite a user, or you can create a user.\u003C\u002Fp>\u003Cp>So what you did was created a user. They're already in the system. They should already have access. Yes. If you send an invite.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. So we're in. As an admin, I have access to, these things. I see you just added a agency I\u003C\u002Fp>\u003Cp>Speaker 1: added some agency partners for you.\u003C\u002Fp>\u003Cp>Speaker 0: Thank you. So if I were to look at yours, I see your name, your agency, your mug, team size, specializations. Looks good to me. There's my mug.\u003C\u002Fp>\u003Cp>Speaker 1: What's the problem here, bro?\u003C\u002Fp>\u003Cp>Speaker 0: Problem is you have access to all of our files and our user directory of other team members. And we want to scope that down. Additionally, you have access to our insights, which shouldn't have access to that. So we have to limit access to parts of the app from the admin.\u003C\u002Fp>\u003Cp>Speaker 1: And what else?\u003C\u002Fp>\u003Cp>Speaker 0: And\u003C\u002Fp>\u003Cp>Speaker 1: And then, and then, alright. Can you edit my agency?\u003C\u002Fp>\u003Cp>Speaker 0: I can edit your agency. That's right. That is right.\u003C\u002Fp>\u003Cp>Speaker 1: I'm I'm not saying, like, that would be in a potential issue. Like, we've got a a great list of community members, but\u003C\u002Fp>\u003Cp>Speaker 0: Oh, man.\u003C\u002Fp>\u003Cp>Speaker 1: Probably not good if you can update somebody else's agency information.\u003C\u002Fp>\u003Cp>Speaker 0: If I had other agencies in the US that were in my territory, I'd be editing, like, this agency sucks.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So how do we fix it?\u003C\u002Fp>\u003Cp>Speaker 0: So we're gonna go back, step 1, to our normal back enroll right here. I think what we do here, access control, printer admin.\u003C\u002Fp>\u003Cp>Speaker 1: There's a step that comes before this.\u003C\u002Fp>\u003Cp>Speaker 0: I don't know what the step is. That's why I brought you.\u003C\u002Fp>\u003Cp>Speaker 1: So you need, if if you're gonna restrict to a certain agency partner. Right? You got a user. You got an agency partner. You need an arrow between the 2.\u003C\u002Fp>\u003Cp>If we're looking at, like, a your diagram of your your circles. You gotta have a relationship between the 2 to be able to filter permissions based on it. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Right.\u003C\u002Fp>\u003Cp>Speaker 1: Alright.\u003C\u002Fp>\u003Cp>Speaker 0: Right. I think. It's early. I'm having trouble conceptualizing this. So\u003C\u002Fp>\u003Cp>Speaker 1: Okay. So, basically, we have to create a relationship between the user and the agency partner.\u003C\u002Fp>\u003Cp>Speaker 0: Who's the user in this scenario?\u003C\u002Fp>\u003Cp>Speaker 1: The the person who's logging in to update the actual information.\u003C\u002Fp>\u003Cp>Speaker 0: The the team member? Like, the team\u003C\u002Fp>\u003Cp>Speaker 1: The team member or the admin role. Right? They're still logging in to direct us.\u003C\u002Fp>\u003Cp>Speaker 0: Gotcha. What would be that step?\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So you're gonna go to your data model.\u003C\u002Fp>\u003Cp>Speaker 0: Going to the data model. Alright. Sorry.\u003C\u002Fp>\u003Cp>Speaker 1: That's where we're gonna create the relationship. You're gonna go to your partners collection.\u003C\u002Fp>\u003Cp>Speaker 0: Agency partners.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. And we're gonna create a relationship here. We're gonna create a relationship here.\u003C\u002Fp>\u003Cp>Speaker 0: I don't know.\u003C\u002Fp>\u003Cp>Speaker 1: We're gonna create a relationship here.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Where does, where do you create a relationship at? I have no idea.\u003C\u002Fp>\u003Cp>Speaker 1: Right there, man.\u003C\u002Fp>\u003Cp>Speaker 0: Where? Here? Create\u003C\u002Fp>\u003Cp>Speaker 1: It has a field. Create a field. Yep. Alright. So you tell me what the relationship is gonna be.\u003C\u002Fp>\u003Cp>Speaker 0: This is stringing into territory.\u003C\u002Fp>\u003Cp>Speaker 1: Get, like, the, like, the jeopardy timer up? Yeah. Like, the final jeopardy question?\u003C\u002Fp>\u003Cp>Speaker 0: This is where I'm not sure. Because when I think of a collection, I think of a form, like fields. And the admin here is gonna be creating a mini to 1. Right? Because it'll be multiple team members associated to one agency.\u003C\u002Fp>\u003Cp>Speaker 1: Correct. They will create that via a form, or it's not just, like, automatically So we've gotta create the relationship first, and then you can have it automatically populate the information\u003C\u002Fp>\u003Cp>Speaker 0: Okay.\u003C\u002Fp>\u003Cp>Speaker 1: When users get created.\u003C\u002Fp>\u003Cp>Speaker 0: So from the agency partner's perspective, it's gonna be many to 1 because on the other side is the team member that's a one. Now wait.\u003C\u002Fp>\u003Cp>Speaker 1: That's it. No. No. No. If you've got if you've got multiple people that are part of the same agency, it is a The agency to many because a user belongs to one agency.\u003C\u002Fp>\u003Cp>The agency could have many users.\u003C\u002Fp>\u003Cp>Speaker 0: Gotcha. So\u003C\u002Fp>\u003Cp>Speaker 1: here, you're gonna pick the one to one to many option.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. One to many.\u003C\u002Fp>\u003Cp>Speaker 1: There you go. This is gonna be\u003C\u002Fp>\u003Cp>Speaker 0: the admin is the one that's filling this out. So they're gonna be building they're gonna invite team members.\u003C\u002Fp>\u003Cp>Speaker 1: User. You're gonna call it user. Yeah. Or servers. Users, plural.\u003C\u002Fp>\u003Cp>There you go. Users. Alright. The related collection is going to be directus_users. That is a system collection.\u003C\u002Fp>\u003Cp>That's why it's prefixed with directus. Yeah. And then for the foreign key, that's gonna be the field that holds the agency partner ID inside the Directus users table or the directus users collection. So that would probably be agency partner or agency partner ID.\u003C\u002Fp>\u003Cp>Speaker 0: Is that something that's built already?\u003C\u002Fp>\u003Cp>Speaker 1: No. You don't have to build it, though. If you key it in and it doesn't exist, Directus will create it for you.\u003C\u002Fp>\u003Cp>Speaker 0: No. Agency partner ID.\u003C\u002Fp>\u003Cp>Speaker 1: There you go.\u003C\u002Fp>\u003Cp>Speaker 0: Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. Doesn't really matter if we show these in a list or a table. Solid. Do we wanna show a link to the user? Probably.\u003C\u002Fp>\u003Cp>Yeah. And we're gonna dismiss. Not sure why that error shows up. But now we have created a relationship. Right?\u003C\u002Fp>\u003Cp>And if you go back to the data model, open up the system fields. There you go.\u003C\u002Fp>\u003Cp>Speaker 0: Fields. System collection.\u003C\u002Fp>\u003Cp>Speaker 1: There you go. Click on direct as users. So these are the system collections. You can't update any of the, like, the default fields, but we should see at the very bottom of this, you should see an agency partner ID field. You might wanna unhide that just for fun.\u003C\u002Fp>\u003Cp>There you go. Alright. So we've got the relationship now. Now we can actually use that to restrict permissions. That make sense?\u003C\u002Fp>\u003Cp>Speaker 0: Yep.\u003C\u002Fp>\u003Cp>Speaker 1: Got it? Alright. So we're gonna go back to access control, and we'll we'll just do, like, the part agency partner admin for now because we that's the user that we've got. Alright. So down the bottom, you got scroll up to where you got the actual permissions.\u003C\u002Fp>\u003Cp>There you go. Alright. So agency partners, all these are gonna be custom permissions. Right? So change this from all to custom for read.\u003C\u002Fp>\u003Cp>Right? We only want them to be able to see their own agency.\u003C\u002Fp>\u003Cp>Speaker 0: The admin?\u003C\u002Fp>\u003Cp>Speaker 1: Right. K. So Alright. So for item permissions there oh, go back.\u003C\u002Fp>\u003Cp>Speaker 0: So for here\u003C\u002Fp>\u003Cp>Speaker 1: Yep. View. Go to use custom. Yeah. Alright.\u003C\u002Fp>\u003Cp>So for item permissions, we're gonna add a filter for this. Right? And the filter is gonna be the ID. So the agency partner ID, right, is equal to we're gonna use a little bit of Directus magic here. You're gonna type in dollar sign, all caps, current_user.\u003C\u002Fp>\u003Cp>So that's gonna give us the current user that's logged in. Right? We can use the related fields that are attached to that user in our filter here. So we're gonna do current underscore user dot agency underscore, you gotta get now we're going back all lowercase. There you go.\u003C\u002Fp>\u003Cp>Partner\u003C\u002Fp>\u003Cp>Speaker 0: underscore\u003C\u002Fp>\u003Cp>Speaker 1: underscore ID. Right? That's the field that we set up on that particular on the direct us users collection. So now what we've just said is basically, hey. I can only read the agency partner's collection, like, the items within that collection that that equal this rule, which is only gonna be 1.\u003C\u002Fp>\u003Cp>Right?\u003C\u002Fp>\u003Cp>Speaker 0: Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Makes sense? Now what you're gonna do, click, click the drop down. Well, hover over rule or just the actual heading. Let me give you a shortcut. Yeah.\u003C\u002Fp>\u003Cp>Or it it says rule right up above that. Click that. Click copy raw value. Mhmm. Save this.\u003C\u002Fp>\u003Cp>And then go to the edit permissions for that agency partners Mhmm. And click use custom, and paste that there for the item permissions here. Say okay. So now we can only see our own agencies that we're a part of. We can only edit our own agency.\u003C\u002Fp>\u003Cp>Now we have to go through and do something similar for projects. Right? Because I I shouldn't be able to edit some other agency's project.\u003C\u002Fp>\u003Cp>Speaker 0: Right.\u003C\u002Fp>\u003Cp>Speaker 1: So I can create projects. When when I go to custom here and instead of there's no item permissions for creating an item because there's we're creating an item. The item doesn't exist yet. We can't filter by it. But what we're gonna do for the presets, in this case, we're gonna give it a preset.\u003C\u002Fp>\u003Cp>And the preset we're gonna do here, you're gonna do, the mustache brackets so we get some JSON. Okay. And within that, you're gonna create a new field. There you go. What do we hit enter.\u003C\u002Fp>\u003Cp>Mhmm. And what are we gonna do here? Agency okay. So the if we're in the project trying to think of the field. What did we call the field relating back?\u003C\u002Fp>\u003Cp>Partner. Okay. So you're gonna put partner in quotation marks. K. Colon.\u003C\u002Fp>\u003Cp>There you go. Colon. Quotation mark. 2 left mustache brackets, dollar sign current, Whoop. That's a hash.\u003C\u002Fp>\u003Cp>Current. Underscore user dot what? Agency underscore partner underscore ID. And then you're gonna close that mustache syntax and hit the quotation mark. Alright.\u003C\u002Fp>\u003Cp>So what that's gonna do, whenever this user within this role creates a new project, it's gonna default the partner value to that specific field or to the agency partner ID that's attached to that user. So whenever they create a project, it's always gonna be scoped to that specific agency.\u003C\u002Fp>\u003Cp>Speaker 0: Gotcha. Okay. That makes sense.\u003C\u002Fp>\u003Cp>Speaker 1: Cool. So now you hit save.\u003C\u002Fp>\u003Cp>Speaker 0: Should I oh, no. Because this is on creation. So\u003C\u002Fp>\u003Cp>Speaker 1: Yep. And then so within that go back down. And then we're gonna apply custom permissions for the other items here. Right? Mhmm.\u003C\u002Fp>\u003Cp>So the rule here is not gonna be ID because now we're inside the project. Right? We're not inside the partner anymore. So it's gonna be the partner dot ID. So there you go.\u003C\u002Fp>\u003Cp>Expand that. Partner dot ID. Yeah. But it's gonna be the same value. Dollar sign current underscore user dot agency_partner.i oh, not dotid_id.\u003C\u002Fp>\u003Cp>Yeah. You got it right. Alright. And now you can copy that, and you can apply it to all the other ones.\u003C\u002Fp>\u003Cp>Speaker 0: This is something that's so for as I paste this, that, like, custom type of filter and and building the presets and things like that. Like, that's that's high level stuff. Like, like, the doc, would the docs be the best place to learn things like that, for somebody\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. There's a good primer on the on roles and permissions within docs. So hover over role. There you go. Paste.\u003C\u002Fp>\u003Cp>But the the docs are certainly good. There's a couple of great guides on it as well. But all those variables that are available are documented there. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And they should only be able to share their own things. There you go. Now Alright. So files or\u003C\u002Fp>\u003Cp>Speaker 1: So for project files, that's a junction collection. We could go in and, like, scope that as well. It probably not necessary for for this episode.\u003C\u002Fp>\u003Cp>Speaker 0: No. We can do that.\u003C\u002Fp>\u003Cp>Speaker 1: Because, basically, what that project files collection is is just a, like, a pointer to a direct to file and a pointer to a project. There's nothing else that's being stored in there.\u003C\u002Fp>\u003Cp>Speaker 0: Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Now the other thing that we're probably gonna want to adjust, and there's there's a couple ways we could get this done, is the file access. Right? We don't want them to be able to access all of our files.\u003C\u002Fp>\u003Cp>Speaker 0: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 1: We don't want them to be able to delete our files. You know, we probably trying to think of the best way to scope this where you get tied to a specific user who has the same partner. Like, I could see only the files that we've uploaded into the system.\u003C\u002Fp>\u003Cp>Speaker 0: So the Does\u003C\u002Fp>\u003Cp>Speaker 1: that make sense?\u003C\u002Fp>\u003Cp>Speaker 0: You're thinking of doing it tied to the main admin user, or would it be like a\u003C\u002Fp>\u003Cp>Speaker 1: So each each user who is logged in is gonna have like, if they're part of this agency partner program, they're gonna have to be tied to a specific agency. Right? We should be able to go through that relationship and just show all of the files that were uploaded by members of that agency. I'm thinking we should be able to do that. This gets a little deeper into, like, the nesting, but, a couple things that I would do here.\u003C\u002Fp>\u003Cp>Right? Like, the the other option is to do something like a folder or, you know, add we could go in and modify the Directus files collection to have a relationship to the actual agency. And then whenever a file gets created, we add the agency relationship to that specific file. So there's multiple ways we could achieve it. I'm trying to think of the the easiest one to do for for this one.\u003C\u002Fp>\u003Cp>Speaker 0: Here's a question. Should we make a part 3 where we focus on kind of the system collections and how they interact with, like, the role based access control stuff.\u003C\u002Fp>\u003Cp>Speaker 1: Where are we at time wise?\u003C\u002Fp>\u003Cp>Speaker 0: We're coming up on about an hour. So may I mean, that seems like a good part through to me, because it sounds like it's gonna be a little bit more in the weeds on kind of structuring this out on the more of the back less of the partner directory itself, so more of, like, how Directus interacts with specific roles and and things like that. So, if you think it's quick fix, quick solution, I'm up for it. But\u003C\u002Fp>\u003Cp>Speaker 1: Hey. Let's log in and test this out. Hey. Let's test what we've got so far Alright. And see see how that's working.\u003C\u002Fp>\u003Cp>Right? So at at this point, like, actually, do one more thing for me. Go to the system collections, and we wanna restrict access to the insights. Right? So if you go into Directus dashboards\u003C\u002Fp>\u003Cp>Speaker 0: Directus dashboards here.\u003C\u002Fp>\u003Cp>Speaker 1: Just set that to none. So there's a shortcut over there. Right? Go to Directus panels.\u003C\u002Fp>\u003Cp>Speaker 0: Panels.\u003C\u002Fp>\u003Cp>Speaker 1: Set that to none. Mhmm. There you go. Alright. So that should limit any of the actual folks from seeing any of the inside spales.\u003C\u002Fp>\u003Cp>Speaker 0: Great. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, and then the other thing that we wanna do here, where this is the agency partner role, So where it says direct as\u003C\u002Fp>\u003Cp>Speaker 0: roles Mhmm.\u003C\u002Fp>\u003Cp>Speaker 1: We let's change that. So add a custom permission for that.\u003C\u002Fp>\u003Cp>Speaker 0: For which one?\u003C\u002Fp>\u003Cp>Speaker 1: For the the read.\u003C\u002Fp>\u003Cp>Speaker 0: Read. Use custom.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So for the role here, we want to, what roles do we wanna see? The roles of the item yeah. So there's a oh, okay. I think it's actually already set in it.\u003C\u002Fp>\u003Cp>ID equals current rule. Follow automatically applied when okay. So I think that should be good then.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So they'll only be able to see because we should they should only be able to see in the in the admin of, like, users. It should just be their agency users. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Right. No. So what we need to do there is we just need to add a condition to the direct as users collection instead of the role. So go ahead and close this. Scroll down to direct as users, and the read there is the one that we're gonna update.\u003C\u002Fp>\u003Cp>Speaker 0: Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So the yeah. It's gonna be the agency partner. You should be able to search for it. Agency partner ID.\u003C\u002Fp>\u003Cp>Yep. There you go. Equals current, dollar sign. Right? Current underscore user.\u003C\u002Fp>\u003Cp>Yeah. Dot agency_partner.id. Great. Alright. So now let's log in.\u003C\u002Fp>\u003Cp>We'll worry about files later. We're gonna open up that other browser, whatever you had. Now hit refresh.\u003C\u002Fp>\u003Cp>Speaker 0: And any other agency projects or partners.\u003C\u002Fp>\u003Cp>Speaker 1: And you shouldn't be able to see any projects either because we don't have any Yeah. Yeah. We don't have any projects, but as far as the user directory, I shouldn't see any other users either.\u003C\u002Fp>\u003Cp>Speaker 0: I see myself.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And and, again, like, we could go in and restrict that list of roles, I guess. You know, not particularly sensitive, but, you know, we could restrict that to just the agency partner roles if we wanted to. You know, files, they're still gonna be able to see all files. But what I'm gonna do behind the scenes here, we don't have this relationship between your fake user and the project or the agency.\u003C\u002Fp>\u003Cp>Right? So that's why you're not seeing any projects. Maybe you wanna pull the admin back up. That way we could just show everybody, I guess. Mhmm.\u003C\u002Fp>\u003Cp>Alright. So go to your user's directory. Here. In the the module bar. So, like, the actual user's directory.\u003C\u002Fp>\u003Cp>There you go. Alright. Find your fake person. And I don't want any scrubs on my team, so it could be in your agency.\u003C\u002Fp>\u003Cp>Speaker 0: Don't want no scrubs.\u003C\u002Fp>\u003Cp>Speaker 1: Agency So the agency partner ID, yeah, you could put them in your agency, and hit save. So now you've added that user to that agency, and that user has the appropriate role. They've got a partner ID that's linked. If you log in as that user now, if you switch back to that incognito window and hit refresh Mhmm. We should see, like, on the agency partner side of it, should see your agency.\u003C\u002Fp>\u003Cp>And, like, if you click into it, should have the ability to edit that as well. Wonderful.\u003C\u002Fp>\u003Cp>Speaker 0: It's just okay. Yeah. Just okay. Oh, cool. So, and then here we could instead of, you know, a table layout, I could do map.\u003C\u002Fp>\u003Cp>And wherever the country is, maybe that shows up here.\u003C\u002Fp>\u003Cp>Speaker 1: Very cool. There you go. So now within that, right, we could go in and like, you could go in and flesh out all of your projects if you wanted to.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And then they would all automatically be associated just with that single agency because that's what the person's Correct. Fantastic.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So as far as, like, the the next steps, right, would be a figure out I I I at this point, I don't think we fully understand how we wanna scope, like, the file library because, yeah, I would think that it it would be helpful for agency partners to have access to, like, all the brand assets.\u003C\u002Fp>\u003Cp>Speaker 0: Right.\u003C\u002Fp>\u003Cp>Speaker 1: Right? There's there's certain internal files we wanna give them access to, but, you know, there's probably some private things that we wouldn't want them to have access to. So it it would make sense to do it on a combination of maybe, like, folder structure and files that they've uploaded as well. Like, any of the files that we've uploaded, they can't change. They could read those files.\u003C\u002Fp>\u003Cp>They could download them. They can't delete or edit. But files that they've uploaded, you know, maybe they've got the ability to edit or replace those.\u003C\u002Fp>\u003Cp>Speaker 0: That's a great point. That brand kit. Alright. Awesome. Well, episode 2, part 2.\u003C\u002Fp>\u003Cp>I feel like we've covered a lot of good ground here. Part 1, we got the actual data model set up. Part 2, we got the roles and access. Next part, part 3, we'll talk about how to share specific internal assets, how they can upload assets, and only access the things that they need within a direct instance test.\u003C\u002Fp>","Everybody, welcome back to the part 2 of Technically, I'm Lost, t I l. As, again, you're probably joining us from the last episode. I'm Matt, and this is Bryant. Put a little French spin on it today. If you've watched part 1, if are we gonna did I just cut you off? I no. No. It's okay. I was gonna say do I look French to you? But We'll have to ask, Alex Chopin, our director of engineering from France. I digress. But yeah. So if you watched part 1, if you haven't, you're probably confused. We suggest you go watch part 1, because this is part 2 of part 1. But, part 1, we are building a partner directory for us to use with our current partners. In that first episode, we planned out, how it was all gonna look, and we built out the form, which I can actually pull up my screen here and show you kind of how far we've gotten along. So, we I will say I, with the help of Bryant, me being a nontechnical person, learning directors for the first time, set up, this initial form in here. So we were able to set up, you know, point of contact, some information about the actual agency themselves, what they specialize in, with the drop down, partner logos, and we had, these projects, here as well as as this. So the general idea just to catch you up, is we wanna create a partner directory on the director's website for our partners, and this is the back end to get that whole thing running. So where we left off last time, Brian, do you remember? Yeah. Yeah. Kind of. No. I I think what we need to do now, Daniel, like, we've got our agency partner collection configured. That's looking nice. We need to go through and create the actual projects that we want them to add. So what's a data model look like for that? And then we need to scope a role for our partners. So, they can only edit their content and only, like, the agency partners and the project's collections. Great. Okay. So let's start with that part 1. So the projects, to give you a little background on how we're thinking about this. So when somebody comes to, you know, direct style slash partners, they'll filter down on country and specialization. By then, they'll find an agency they like. They click on them, and then that page will have a showcase of the projects that that agency has done with Directus. So we'll need to create that collection, as you mentioned. What's a good starting point for this? Is it just diving in? Is it a little bit of planning your strategic approach to this? Or what do you think? I I mean, I I think at a high level, you wanna know what what goes on a project or or, like, what form feels, like, what are we gonna display when you click into a project. Mhmm. You know, if I'm imagining it, like, you've got an agency page where you're you're surfacing all these projects, maybe, like do we have an extra page for each project as well? So I can I'm on the agency page. I see a list of their recent projects. I click in the project, and I get, like, a a mini case study type of thing, or we're gonna show those in a model, like, a pop up window. How what do we we wanna do there? I think it would be good to have a page. I like what you said about, like, a mini case study page. So maybe we'll have, like, the a scroller here of, like, they can go back and forth for, like, images of the actual website. And then I don't like it. You don't like it? I I want a grid. I don't like it. Give me a grid of projects. A grid. Wait for the individual project. Oh, no. No. No. For the individual projects, I I'm thinking well, you got, like, an image gallery. I I'm thinking like a grid still. Big grid guy. Yeah. Let me I'm gonna share. I'm gonna share a link. Let me share. This is, again, this is the agent c OS link. So this is a sample starter kit project that that I put together, to showcase what you could do with Directus. Let me I guess I could put it in the chat here instead of our team comps. Alright. Let me pull this up over here. This is not a a like, the text doesn't really jive here, but, you know, you've got, like, a a title for the project. You got, like, a summary, then you have some content, and then you have an image gallery that, you know, if you click on one of those images, then you could cycle through. Wow. Instead of, like, a instead of a carousel on the actual page, show a grid. And then you have some metadata, like, hey. What's the client? What's it built with? What's the cost? I I don't know if we wanna probably omit that for this. You know? Would be nice to show, like, what it was built with. Those could just be, like, tags. So this is kinda what I'm envisioning. You know, the the header there is probably not it doesn't really jive with, like, the direct as brand guidelines. So we'd probably do something different. But, you know, I I think at a high level, you got a title, a short summary of the project. You've got a featured image for the project. You have some content, in case they wanna expand or go into as much detail as they want to. And then there's probably, like, the the content would probably be, like, text. Right? Mhmm. And then the yeah. And then you could have, like, a separate gallery if you wanted to. Separate gallery. And then, you know, like, some tags for what it was built with, like front end type of stuff, I guess. Like, hey. Is it built with Next? Is it built with Next? I I don't know. I don't know if that's necessary either. Okay. I like this. So, yeah, so we would create a collection with these different items inside of Directus. Last time, if you can refresh my memory, when we created the projects here, this was a one to mini or a mini to 1? So it depends on where you're at. Right? Those are 2 inverse relationships. If you are a for this example, if we're on the agency partner, projects would be a one to many relationship because I've got one agency, many projects. But if I'm inside projects, it's the reverse. Right? I've got, one project or, actually, I'm sorry. Is it many projects to one agency, basically. Yeah. That makes sense. Like a project can only have belong to a single agency is what I'm trying to say. That makes sense. So we'd have this is the one to many from the agency side. So if we go here, this would be the many to 1. Yep. So what you're gonna have to do here is go into our data model for projects because we we didn't add any fields for that. Mhmm. Where is our here. So from the agency side, if if I click here, this will take me automatically there. It's over here. Great. So just like we did last time, you know, obviously, going through, filling out these fields. Title, I assume, would be just a standard input field. Title, String. There you go. I mean, required, obviously. Project title, let's see. The name of the client, input project client. And, again, I would probably just do, like, client name. Right? Because there's, like, where else are you using client name? You're not. Naming conventions. I'm about to get open that can of worms. So short summary. Would you use an input field, or would you use a text area field? Or Text area for sure. No wysiwyg. I don't wanna deal with rendering HTML or, stripping out HTML. I I just want, like, a short description to display on a card or in a heading. And if I'm gonna add a placeholder here, I will do it in the placeholder field. Yeah. You've also got the help field or the the note field, I think, is is what it is as well. Like, the placeholder is gonna be it's gonna disappear as soon as they start typing. Right? The if you go to the field tab when you create 1, you got some helper text that you can add. Okay. Okay. So here, we are creating the main image versus the gallery of images that they could just, like, see. So Yeah. There there's 2 ways you could you could do that. Right? You could just have an image gallery and use the first item in the gallery as, like, the featured image. It's kinda a matter of personal preference. Usually, I like to be more explicit. And because of the way that Directus, like, queries the data and, like, if I'm showing a list of projects, I I can create it or treat it my gosh. I came to talk today. You could treat it like GraphQL, and I could tell it specifically the fields that I want. So, you know, on the index page where we're showing a list of projects, I can just grab the featured image and not worry about the image gallery. So that's that's probably why I would do 2 separate fields for those. So you might have, like, a, like, a featured image field and then, like, an image gallery or gallery, whatever you wanna call it. This is just a single image that you're gonna use across the site. Featured No. No. No. No. What did you do? I created a featured It's not just messing with you. No. You don't need your Oh, Don't be doing that today. Oh, man. Got my heart rate up. So next part, if we're doing a showcase or gallery, I assume files based on the image here. Yep. Definitely. And then showcase. Bro, call it image gallery or a gallery. I was gonna call it a project showcase. I feel like we're going to The Price is Right if we're, like, doing showcase. Okay. So I don't know if we want this required because as long as they have the the Yeah. You don't necessarily have to add an image gallery. Cool. So what else what else here? That's it. Really? Alright. Now you're also gonna want what else do we have? We had the we had, like, the built with tags. That's gonna be yeah. You can use the tag interface for that. It'd be fine. Built with JSON. There you go. And in this case, you're gonna, like, do allow other values. You could also add some presets to this if you wanted to. I'll add a few where people can just choose those. There you go. There you go. Allow other values. So when they type in, it'll add it. So that'll be a continually growing list. Big fan. So for. You could force alphabetical order if you wanted to. Way to standardize. If not, you could have, you know, potentially, like, next being shown in a separate place. Mhmm. Also, just to clarify, that's not my stomach growling. That is this squirrel that I have on my lap. Dog is protecting the house. Love it. Oh, it's not protecting the house. It's it's being a pain in the butt. Tiny dogs, what do you do? Would you force, like, capitalization and and white space and all that sort of stuff? It doesn't matter. I don't know that I would just because, like, some of these well, I'm trying to think of a a good example of, like, a framework that prefers lowercase. I can't really think of any. Like, the white space, you might you know, like, white space, I would definitely, like, trim the white space, like, the the beginning and end, just so you remove white space. There you go. That way, there's, like, no extra, like, the space that's that's big, like breaking formatting or anything like that. Cool. Alright. Awesome. Anything else I should consider here, in advanced field creation mode? Or Nah. I wouldn't worry about it. Great. So I feel like we're good there. I think the next thing we were looking for. Oh, the main content, which would be text area again. Yeah. No. WYSIWYG. This, we're gonna give them formatting options. Why is it why is it main content? Not just content. Because it's different from the short summary. Long But that's already it's already a different field, my man. Alright. Content it is. Good here? I'm good. I'm good. My dog is not good. This This data model is good, though. Alright. Feels good. I might actually go and make this content required. At least I have to share something about it. Great. So our mini to 1 is now done. Yeah. I guess we could go here. Let's say we're going to start a new project. Everything looks good. Yeah. Yeah. Amazing. Alright. So we have our partners. We have our one to many connection to the projects, then we've got the many to one back to the partners. So feel good about these 2 collections. Now it's about actioning the actual, role based permission control. So when somebody signs up on the agency side, they're gonna have to go in and be able to add these projects. Maybe invite somebody to add these projects, invite members of the other team if they have to update, agency information. Let's dive into role based access control. RBAC, direct us off. This is my first time ever using this, so it should be interesting. Oh, okay. Alright. So what do you think you know? What do I think I know? I see this big plus, and then that's what I think I know is I click this to create something. Yeah. That would be a a user. But as far as, like, what we're trying to achieve, like, what's it what what is your current plan of attack in your mind? And then we'll, like, course correct. Alright. Current plane of attack is and I will visualize this. So we're gonna get a partner in, and that's this little circle. This is a partner that fills out our partner request form. They go through the process. Once they have become a partner, via our partner manager, then that is when Are are you supposed to be showing something on the screen? All I see is the user directory. Yes. Yes. I am. Here we go. Okay. Alright. Yeah. Now, we're seeing the circles. Okay. Yeah. Yeah. You missed my circles. Alright. But these these are the phases. Right? So, actually, this is going to be, there's no preset yellow, of course. So I'll use orange. Initial come in, person fills out the form. They are vetted, become a partner, and then at this point is when we would send the send invite to partner directory. Send them here, and then once they fill that out, then they'd be officially a partner as part of that ecosystem. Okay. These circles are super misshapen. So at that point I think that's an oval. Yeah. It's definitely an oval. This is gonna be main oh my gosh. This is crazy. Main agency contact. They would fill fill out that part that we built. And then at this point, they can invite their own team. And I think that's it. So we'd have to figure out a role for this person? Because they're coming in cold, so they have to be able to log in. I think we'll provide them a username and a password they can change. Yep. And then we'll have to figure out a role for this person who doesn't necessarily have the ability to change the point of contact information, but just has access to a few fields in that collection, to update the agency information if they need to. Okay. So we've got a admin at the agency, and then we have, like, a a team member at the agency Yep. Kind of set up. Exactly. So 2 two roles, I think. Okay. Now can a person be a part of multiple agencies? I don't think so. I think that's too too much. Too much. Too much. I'm trying to think of any scenario. It seems like a very rare edge case that would be the case and would have to probably be treated on a one to one basis. You could still accommodate that from, like, a using a separate email, basically, I guess. Separate login Maybe. Kind of scenario. This is also the v two of this partner directory, I'd love to build in a thing where, like, an agency partner can go in and request a license or something for, like, a new project that they're building. I'm trying to think that would that would be either the admin or the team member can make that request, but it would be logged to that specific agency. So Yeah. I I think this is good. Good start. Yeah. We'll just scope it down to, like, hey. You can only be a part of 1 agency at a time, which makes sense to me. Okay. Cool. I like it. Alright. So what are you what are you gonna do, man? What are you gonna do? I'm gonna come to access control. I'm gonna click this big purple create role button. The role I create, the naming convention here is gonna be well, you know what? Why don't you tell me? Because I know whatever I put in this little purple Okay. Thank you. So you give me The no. I I would just say, like, agency partner admin or something. Agency partner admin. Not giving them admin access because that would be No. No. No. No. That's just, like, a different thing. Yeah. So app access only. App access only. As a small pardon me. Yep. Alright. So now, this is where things get interesting, because we have our website powered through this, which we've got, like, the blocks. So there's going to be a ton of these in here. And it's we're gonna have to provide the right access to these. Correct. All we need to really give them access to is the agency specific things. Right? Correct. You got it, boss man. Which So yeah. Command f comes in handy here, for sure. We don't have a search in here yet, but I think it's on the docket. You know, feature request would be great if we had the you know, it actually is frozen up here. That's very nice. I was gonna say, wait. If we could freeze that top column. Alright. So what are you gonna do? Like, how do you wanna play this? You you want me to tell you you wanna step through this? No. Let's I wanna think through this because I am looking at this. I haven't really messed with roles before, but, obviously, this is CRUD. You know, create, update, re what's the r for CRUD? Update. The r? Oh, read. Read. Read. Update. Delete. Yeah. So for the agents for the admin, we're gonna want them to be able to create. Are you? Are you are you not just gonna, like, create a partner and then add the user as part of that partner? Like, is this is this is kind of a high touch program. Right? Yeah. So we would create the agency partner, get the send them the login information. So it would already be created, so they don't need access to create, plus they might go and create stuff that they shouldn't. Yeah. In this case, like, the only thing that they need to be able to do is read their own edit their own. We're not gonna let them delete. We're going to let them create, edit projects. They should be able to delete projects too. Right? Yeah. I was I would allow them to delete their own projects out of the system, but maybe not if it's published. Right? So as long as it's not published, you could delete it. If it is if it's already been published, like, we gotta go through some kind of flow so you're not breaking website. So this would be custom. Yeah. We don't have a field for we didn't add a field for the status of a project. Hey. Like, is this draft or published or not? Alright. Yeah. Add that to the to do list. Alright. And then project files would be the same where you can add project files as you can. Because this is this is just the image gallery. Right? So you need to be able to delete those as well. So question. We have the share functionality, which barely gets used, admittedly, because I think there's a little bit of confusion around, like, what it actually allows for. So in this scenario of sharing a collection, would it be useful for them to share with the non, like, the non admin users That works. So if we're gonna keep everybody contained inside, we probably wouldn't use share. We would just give them access. But but, like, say that somebody did not have user access and you wanted to share something with them, you could do that through the share functionality of, like, hey. I wanna share this one specific piece of content with someone, that's not a part of the Directus application. Okay. So this makes sense. So I think for if they're if they're putting in one of those projects in the showcase and they wanna share it with, like, the client and be like, hey. We wrote this little mini study, like, or case study about the project we did with you. Can you confirm it? Would that be a good application for this? Yeah. You could potentially do that. Alright. Like, if you if you wanted to see it before you go live yeah. Hey. Hey. Well, the other rub is, like, if you're rendering this on the front end, they're probably gonna wanna see the front end as well. So, you know, that kinda goes into, like, a live preview mode on the front end. But just to see the actual content to get approval, you could certainly add share access for this. And we would do that at the project? Project level. Yeah. Cool. Yeah. This feels good so far. Okay. Saves automatically. So Yeah. Alright. Agency So so what's next? I'm gonna create the other role just so it's Okay. Alright. Agency, partner. Boy. Not the admin. Team role. That's it. Team. Just team. Just team. It's already a role. Oh, man. Cracked me up. Alright. So here, they're not going to have access to ad. They'll have access to c, but we don't want them to have access to edit it because that'll be from the admin. No. Actually, we do, because can we use a custom permission here, so where they can't see the proof of the point of contact and, like, edit that? Yeah. You certainly can. So It would be within the field permissions. Field permissions. So here's the here's the fields that they can update. So you're going to hit show more there so you can see the rest of them. Mhmm. Alright. So check everything except for point of contact group because everything that's within that group. That's a great question. Yeah. Let's try it and see. Okay. I I I I think it will just be I I think you'll still have to do contact first name, last name, email. Or well, you don't want them to edit that. I'm sorry. Yeah. Leave that unchecked. Yeah. Printer name. Divider. Country. Partners. Work description, specialization, logo, team size, projects. Alright. So now they have, custom access to that, if that makes sense. Yep. They're gonna need access to projects, so they can add projects, update projects. Don't want them to be able to delete. We wanna leave that to the admin. And project files, add, see, and edit. Yeah. I think this makes sense for v one rule. And if we need to update it later, we can. It's the beauty of automatically saving. Okay. Cool. Cool. Good with these 2. Now the tricky part is alright. What's what is the what is what's the tricky part? The tricky part is whenever we create, when we manually create an agency partner inside of this, we have to create their contact information for that that point of contact. So, like, user directory, we can see here. We can create a user within here. So we fill out, like, their first name, last name, email, password. Interesting we have company info already baked in here. So Yeah. That is, that's interesting. I think that is a factor of the docs. Okay. No. This is good. Okay. So if we were to create a fake person here, fake person, and I'll just add in my email. We'll have to blur these so they don't pop up. I'd I'd hopefully will be deleting fake person later. And you're gonna have to change your email address because you're already I'm already a member here. You're already in in the system. Auto pop are you seeing my auto Yeah. It looks like it's, spazzing out a little bit there. Yeah. I don't know if you see, like, the pop up where it's, like, all of the things I've pre filled. No. No. I don't see the pop up. Alright. It has to do with your sharing settings, I'm sure. Gotcha. Well, that's good because there's some private info in here. So I'm gonna use a personal Gmail here. I'm not worried about that. I'm not worried about any of that. I just wanted to create the fake person stuff. So Okay. Here. I will save. Save. And then for this, I'll go check my email. Did you did you create a password for them? You can also just open up an incognito window and log in. So, like, if you do the if you invite them versus so there's a there's 2 ways. Right? You can invite a user, or you can create a user. So what you did was created a user. They're already in the system. They should already have access. Yes. If you send an invite. Alright. So we're in. As an admin, I have access to, these things. I see you just added a agency I added some agency partners for you. Thank you. So if I were to look at yours, I see your name, your agency, your mug, team size, specializations. Looks good to me. There's my mug. What's the problem here, bro? Problem is you have access to all of our files and our user directory of other team members. And we want to scope that down. Additionally, you have access to our insights, which shouldn't have access to that. So we have to limit access to parts of the app from the admin. And what else? And And then, and then, alright. Can you edit my agency? I can edit your agency. That's right. That is right. I'm I'm not saying, like, that would be in a potential issue. Like, we've got a a great list of community members, but Oh, man. Probably not good if you can update somebody else's agency information. If I had other agencies in the US that were in my territory, I'd be editing, like, this agency sucks. Alright. So how do we fix it? So we're gonna go back, step 1, to our normal back enroll right here. I think what we do here, access control, printer admin. There's a step that comes before this. I don't know what the step is. That's why I brought you. So you need, if if you're gonna restrict to a certain agency partner. Right? You got a user. You got an agency partner. You need an arrow between the 2. If we're looking at, like, a your diagram of your your circles. You gotta have a relationship between the 2 to be able to filter permissions based on it. Right? Right. Alright. Right. I think. It's early. I'm having trouble conceptualizing this. So Okay. So, basically, we have to create a relationship between the user and the agency partner. Who's the user in this scenario? The the person who's logging in to update the actual information. The the team member? Like, the team The team member or the admin role. Right? They're still logging in to direct us. Gotcha. What would be that step? Alright. So you're gonna go to your data model. Going to the data model. Alright. Sorry. That's where we're gonna create the relationship. You're gonna go to your partners collection. Agency partners. Alright. And we're gonna create a relationship here. We're gonna create a relationship here. I don't know. We're gonna create a relationship here. Yeah. Where does, where do you create a relationship at? I have no idea. Right there, man. Where? Here? Create It has a field. Create a field. Yep. Alright. So you tell me what the relationship is gonna be. This is stringing into territory. Get, like, the, like, the jeopardy timer up? Yeah. Like, the final jeopardy question? This is where I'm not sure. Because when I think of a collection, I think of a form, like fields. And the admin here is gonna be creating a mini to 1. Right? Because it'll be multiple team members associated to one agency. Correct. They will create that via a form, or it's not just, like, automatically So we've gotta create the relationship first, and then you can have it automatically populate the information Okay. When users get created. So from the agency partner's perspective, it's gonna be many to 1 because on the other side is the team member that's a one. Now wait. That's it. No. No. No. If you've got if you've got multiple people that are part of the same agency, it is a The agency to many because a user belongs to one agency. The agency could have many users. Gotcha. So here, you're gonna pick the one to one to many option. Okay. One to many. There you go. This is gonna be the admin is the one that's filling this out. So they're gonna be building they're gonna invite team members. User. You're gonna call it user. Yeah. Or servers. Users, plural. There you go. Users. Alright. The related collection is going to be directus_users. That is a system collection. That's why it's prefixed with directus. Yeah. And then for the foreign key, that's gonna be the field that holds the agency partner ID inside the Directus users table or the directus users collection. So that would probably be agency partner or agency partner ID. Is that something that's built already? No. You don't have to build it, though. If you key it in and it doesn't exist, Directus will create it for you. No. Agency partner ID. There you go. Okay. Alright. Doesn't really matter if we show these in a list or a table. Solid. Do we wanna show a link to the user? Probably. Yeah. And we're gonna dismiss. Not sure why that error shows up. But now we have created a relationship. Right? And if you go back to the data model, open up the system fields. There you go. Fields. System collection. There you go. Click on direct as users. So these are the system collections. You can't update any of the, like, the default fields, but we should see at the very bottom of this, you should see an agency partner ID field. You might wanna unhide that just for fun. There you go. Alright. So we've got the relationship now. Now we can actually use that to restrict permissions. That make sense? Yep. Got it? Alright. So we're gonna go back to access control, and we'll we'll just do, like, the part agency partner admin for now because we that's the user that we've got. Alright. So down the bottom, you got scroll up to where you got the actual permissions. There you go. Alright. So agency partners, all these are gonna be custom permissions. Right? So change this from all to custom for read. Right? We only want them to be able to see their own agency. The admin? Right. K. So Alright. So for item permissions there oh, go back. So for here Yep. View. Go to use custom. Yeah. Alright. So for item permissions, we're gonna add a filter for this. Right? And the filter is gonna be the ID. So the agency partner ID, right, is equal to we're gonna use a little bit of Directus magic here. You're gonna type in dollar sign, all caps, current_user. So that's gonna give us the current user that's logged in. Right? We can use the related fields that are attached to that user in our filter here. So we're gonna do current underscore user dot agency underscore, you gotta get now we're going back all lowercase. There you go. Partner underscore underscore ID. Right? That's the field that we set up on that particular on the direct us users collection. So now what we've just said is basically, hey. I can only read the agency partner's collection, like, the items within that collection that that equal this rule, which is only gonna be 1. Right? Okay. Makes sense? Now what you're gonna do, click, click the drop down. Well, hover over rule or just the actual heading. Let me give you a shortcut. Yeah. Or it it says rule right up above that. Click that. Click copy raw value. Mhmm. Save this. And then go to the edit permissions for that agency partners Mhmm. And click use custom, and paste that there for the item permissions here. Say okay. So now we can only see our own agencies that we're a part of. We can only edit our own agency. Now we have to go through and do something similar for projects. Right? Because I I shouldn't be able to edit some other agency's project. Right. So I can create projects. When when I go to custom here and instead of there's no item permissions for creating an item because there's we're creating an item. The item doesn't exist yet. We can't filter by it. But what we're gonna do for the presets, in this case, we're gonna give it a preset. And the preset we're gonna do here, you're gonna do, the mustache brackets so we get some JSON. Okay. And within that, you're gonna create a new field. There you go. What do we hit enter. Mhmm. And what are we gonna do here? Agency okay. So the if we're in the project trying to think of the field. What did we call the field relating back? Partner. Okay. So you're gonna put partner in quotation marks. K. Colon. There you go. Colon. Quotation mark. 2 left mustache brackets, dollar sign current, Whoop. That's a hash. Current. Underscore user dot what? Agency underscore partner underscore ID. And then you're gonna close that mustache syntax and hit the quotation mark. Alright. So what that's gonna do, whenever this user within this role creates a new project, it's gonna default the partner value to that specific field or to the agency partner ID that's attached to that user. So whenever they create a project, it's always gonna be scoped to that specific agency. Gotcha. Okay. That makes sense. Cool. So now you hit save. Should I oh, no. Because this is on creation. So Yep. And then so within that go back down. And then we're gonna apply custom permissions for the other items here. Right? Mhmm. So the rule here is not gonna be ID because now we're inside the project. Right? We're not inside the partner anymore. So it's gonna be the partner dot ID. So there you go. Expand that. Partner dot ID. Yeah. But it's gonna be the same value. Dollar sign current underscore user dot agency_partner.i oh, not dotid_id. Yeah. You got it right. Alright. And now you can copy that, and you can apply it to all the other ones. This is something that's so for as I paste this, that, like, custom type of filter and and building the presets and things like that. Like, that's that's high level stuff. Like, like, the doc, would the docs be the best place to learn things like that, for somebody Yeah. There's a good primer on the on roles and permissions within docs. So hover over role. There you go. Paste. But the the docs are certainly good. There's a couple of great guides on it as well. But all those variables that are available are documented there. Right? Yeah. And they should only be able to share their own things. There you go. Now Alright. So files or So for project files, that's a junction collection. We could go in and, like, scope that as well. It probably not necessary for for this episode. No. We can do that. Because, basically, what that project files collection is is just a, like, a pointer to a direct to file and a pointer to a project. There's nothing else that's being stored in there. Okay. Now the other thing that we're probably gonna want to adjust, and there's there's a couple ways we could get this done, is the file access. Right? We don't want them to be able to access all of our files. Mhmm. We don't want them to be able to delete our files. You know, we probably trying to think of the best way to scope this where you get tied to a specific user who has the same partner. Like, I could see only the files that we've uploaded into the system. So the Does that make sense? You're thinking of doing it tied to the main admin user, or would it be like a So each each user who is logged in is gonna have like, if they're part of this agency partner program, they're gonna have to be tied to a specific agency. Right? We should be able to go through that relationship and just show all of the files that were uploaded by members of that agency. I'm thinking we should be able to do that. This gets a little deeper into, like, the nesting, but, a couple things that I would do here. Right? Like, the the other option is to do something like a folder or, you know, add we could go in and modify the Directus files collection to have a relationship to the actual agency. And then whenever a file gets created, we add the agency relationship to that specific file. So there's multiple ways we could achieve it. I'm trying to think of the the easiest one to do for for this one. Here's a question. Should we make a part 3 where we focus on kind of the system collections and how they interact with, like, the role based access control stuff. Where are we at time wise? We're coming up on about an hour. So may I mean, that seems like a good part through to me, because it sounds like it's gonna be a little bit more in the weeds on kind of structuring this out on the more of the back less of the partner directory itself, so more of, like, how Directus interacts with specific roles and and things like that. So, if you think it's quick fix, quick solution, I'm up for it. But Hey. Let's log in and test this out. Hey. Let's test what we've got so far Alright. And see see how that's working. Right? So at at this point, like, actually, do one more thing for me. Go to the system collections, and we wanna restrict access to the insights. Right? So if you go into Directus dashboards Directus dashboards here. Just set that to none. So there's a shortcut over there. Right? Go to Directus panels. Panels. Set that to none. Mhmm. There you go. Alright. So that should limit any of the actual folks from seeing any of the inside spales. Great. Okay. Oh, and then the other thing that we wanna do here, where this is the agency partner role, So where it says direct as roles Mhmm. We let's change that. So add a custom permission for that. For which one? For the the read. Read. Use custom. Alright. So for the role here, we want to, what roles do we wanna see? The roles of the item yeah. So there's a oh, okay. I think it's actually already set in it. ID equals current rule. Follow automatically applied when okay. So I think that should be good then. Okay. So they'll only be able to see because we should they should only be able to see in the in the admin of, like, users. It should just be their agency users. Yeah. Right. No. So what we need to do there is we just need to add a condition to the direct as users collection instead of the role. So go ahead and close this. Scroll down to direct as users, and the read there is the one that we're gonna update. Okay. Alright. So the yeah. It's gonna be the agency partner. You should be able to search for it. Agency partner ID. Yep. There you go. Equals current, dollar sign. Right? Current underscore user. Yeah. Dot agency_partner.id. Great. Alright. So now let's log in. We'll worry about files later. We're gonna open up that other browser, whatever you had. Now hit refresh. And any other agency projects or partners. And you shouldn't be able to see any projects either because we don't have any Yeah. Yeah. We don't have any projects, but as far as the user directory, I shouldn't see any other users either. I see myself. Yeah. And and, again, like, we could go in and restrict that list of roles, I guess. You know, not particularly sensitive, but, you know, we could restrict that to just the agency partner roles if we wanted to. You know, files, they're still gonna be able to see all files. But what I'm gonna do behind the scenes here, we don't have this relationship between your fake user and the project or the agency. Right? So that's why you're not seeing any projects. Maybe you wanna pull the admin back up. That way we could just show everybody, I guess. Mhmm. Alright. So go to your user's directory. Here. In the the module bar. So, like, the actual user's directory. There you go. Alright. Find your fake person. And I don't want any scrubs on my team, so it could be in your agency. Don't want no scrubs. Agency So the agency partner ID, yeah, you could put them in your agency, and hit save. So now you've added that user to that agency, and that user has the appropriate role. They've got a partner ID that's linked. If you log in as that user now, if you switch back to that incognito window and hit refresh Mhmm. We should see, like, on the agency partner side of it, should see your agency. And, like, if you click into it, should have the ability to edit that as well. Wonderful. It's just okay. Yeah. Just okay. Oh, cool. So, and then here we could instead of, you know, a table layout, I could do map. And wherever the country is, maybe that shows up here. Very cool. There you go. So now within that, right, we could go in and like, you could go in and flesh out all of your projects if you wanted to. Yeah. And then they would all automatically be associated just with that single agency because that's what the person's Correct. Fantastic. Yeah. So as far as, like, the the next steps, right, would be a figure out I I I at this point, I don't think we fully understand how we wanna scope, like, the file library because, yeah, I would think that it it would be helpful for agency partners to have access to, like, all the brand assets. Right. Right? There's there's certain internal files we wanna give them access to, but, you know, there's probably some private things that we wouldn't want them to have access to. So it it would make sense to do it on a combination of maybe, like, folder structure and files that they've uploaded as well. Like, any of the files that we've uploaded, they can't change. They could read those files. They could download them. They can't delete or edit. But files that they've uploaded, you know, maybe they've got the ability to edit or replace those. That's a great point. That brand kit. Alright. Awesome. Well, episode 2, part 2. I feel like we've covered a lot of good ground here. Part 1, we got the actual data model set up. Part 2, we got the roles and access. Next part, part 3, we'll talk about how to share specific internal assets, how they can upload assets, and only access the things that they need within a direct instance test.","704ea83d-44e6-4b22-993a-22108f6f6d96",[2355,2356],"1c46a971-8c5a-4921-9061-3e4eb9c7d7d1","f37a8ab8-6b45-4e08-b0de-b9e5c3be37f9",[],{"year":978,"number":329,"id":2359,"episodes":2360,"show":2362},"b507c659-1dcf-4328-adca-5b77aa0eba6f",[2361,2346],"fb8ecc5d-0e74-4807-b59b-4273ca3424af",{"title":2363,"slug":2364},"Technically I'm Lost","technically-im-lost",{"id":2366,"slug":2367,"vimeo_id":2368,"description":2369,"tile":2370,"length":177,"resources":12,"people":12,"episode_number":675,"published":2371,"title":2372,"video_transcript_html":2373,"video_transcript_text":2374,"content":12,"seo":2375,"status":19,"episode_people":2376,"recommendations":2378,"season":2379},"bfcce865-ac73-4f51-a1f2-2136a2a3ca11","10-13","983280662","Directus Version 10.13 introduces resumable uploads, some fixes around shares, and some small quality of life improvements for the Data Studio.","6ebd70ad-95a8-460a-9127-a656fb60d4ea","2024-07-15","What's New in Directus Version 10.13","\u003Cp>Hello. My name is Kevin and this is what's new in Directus version 10.13, resumable uploads. The main new feature in this release is resumable uploads, which believe it or not, allows file uploads to resume if they have been paused or interrupted. This also means that you can circumvent limits on a proxy setup if you have one. You can enable this new feature by setting the new TUS_ENABLED environment variable to true.\u003C\u002Fp>\n\u003Cp>Community contributions. Since the release of director's version 10.12, we've had a number of really lovely and impactful community contributions and here they are. Thanks to Joey for fixing performance issues with schema introspection on Microsoft SQL. Thanks to drennvinn for adding toggles to quickly expand or collapse all rows in the data model view. Thanks to Matthew for including file extensions when images are added in the WYSIWYG, which makes it easier to embed them in external services&nbsp;And finally, thanks to Ivo for fixing session verification for shares.\u003C\u002Fp>\n\u003Cp>And that's Directus 10.13. It's now available on Docker Hub and on Directus Cloud. Until the next release. Bye for now.\u003C\u002Fp>","Hello. My name is Kevin and this is what's new in Directus version 10.13, resumable uploads. The main new feature in this release is resumable uploads, which believe it or not, allows file uploads to resume if they have been paused or interrupted. This also means that you can circumvent limits on a proxy setup if you have one. You can enable this new feature by setting the new tus enabled environment variable to true. Community contributions. Since the release of director's version 10.12, we've had a number of really lovely and impactful community contributions and here they are. Thanks to Joey for fixing performance issues with schema introspection on Microsoft SQL. Thanks to Drenvin for adding toggles to quickly expand or collapse all rows in the data model view. Thanks to Matthew for including file extensions when images are added in the WYSIWYG, which makes it easier to embed them in external services. And finally, thanks to Evo for fixing session verification for shares. And that's Directus 10.13. It's now available on Docker Hub and on Directus Cloud. Until the next release. Bye for now.","a3631d57-5767-44b7-8f7c-3a5c8d868218",[2377],"44cf850a-69fa-4bf5-9f15-3218af2d7c4d",[],{"year":2380,"number":588,"id":2381,"episodes":2382,"show":2395},"2023","163f0228-6a46-438a-91ee-097e6432a3e4",[2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394,2366],"afd41541-e7d1-4438-bb7a-6c5fafdfdf29","0096309a-4f7d-42f9-956d-d96c7ffaacef","39873f71-cbf2-4445-b34a-da762fac5cbb","6c39a2d8-b0a6-4d23-b5f2-3b1d86486bff","0746bb8d-9ce3-4170-bb08-ac243ed8e98d","5cfc3232-0395-4163-bbc7-fabe8d5dd0df","8790b2be-6f04-4d63-b00b-0e20956e3696","2487da26-8f4e-44e8-ad95-b6ad1d461fc0","0794a2b0-cda8-491f-ba23-5a1d4afa4102","3027ab51-cef1-4b36-b641-28b175d49f2a","907a59ed-86c4-4573-89ad-28b026db6605","5c929816-2aa1-4930-95c2-57f3443ee7b3",{"title":2396,"slug":2397},"Release Notes","release-notes",{"id":2361,"slug":2399,"vimeo_id":2400,"description":2401,"tile":2402,"length":2403,"resources":12,"people":12,"episode_number":13,"published":2404,"title":2405,"video_transcript_html":2406,"video_transcript_text":2407,"content":12,"seo":2408,"status":19,"episode_people":2409,"recommendations":2412,"season":2413},"data-modeling","962569420","What happens when a non-technical person attempts to get technical? In episode 1 of Technically I'm Lost (TIL), non-technical marketer Matt attempts to build the data model for a partner directory from scratch, with help from resident Directus expert Bryant. ","4a5968a6-a416-4562-bdd6-c9c39ca2fa3b",70,"2024-07-09","Data Modeling","\u003Cp>Speaker 0: Hey, everyone. I'm Matt here with the Directus Marketing team. I am joined by my friend and confidant for this call, Bryant Gillespie. Bryant, how are you?\u003C\u002Fp>\n\u003Cp>Speaker 1: I'm awesome.\u003C\u002Fp>\n\u003Cp>Speaker 0: Great. Yeah. I feel like I don't have to introduce you because you're, like, on every show here. But, you know?\u003C\u002Fp>\n\u003Cp>Speaker 1: I I appreciate the intro, man. I you know? Like, I it's always nice when people pretend not to do me. My wife does that all the time.\u003C\u002Fp>\n\u003Cp>Speaker 0: You're just too famous around these parts. I get it. I get it. But yeah. So Bryant was literally just asking because I roped him in right before this.\u003C\u002Fp>\n\u003Cp>What are we doing again, and what is the concept for this show? So as a person on the direct marketing team, 1 of the few here that is actually very, nontechnical, I sit more on the marketing side of things. I just am trying to learn how to become more technical in my role and learning how to use Directus and things like that. So, concept for this show, technically, I'm lost, is gonna be me kinda navigating a lot of these trials and tribulations that it comes with being a nontechnical person that's trying to learn how to actually use technology and code and direct us to the product itself, and I'll be doing so with help from guests like mister Bryant here. We'll be building various things that we actually have to use internally, that we have coming up, and I figured why not delay the timeline and try to build it myself.\u003C\u002Fp>\n\u003Cp>So here we are. Bryant, was that a pretty good breakdown of everything?\u003C\u002Fp>\n\u003Cp>Speaker 1: III like it. I like the trials and tribulations part of it really got me.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I feel like, I don't know what to expect out of this, but I expect it to be painful. Like, there's a forecast of pain coming. That's about all I know.\u003C\u002Fp>\n\u003Cp>Speaker 1: This will be fun if this is your version of 100 apps, 1 100 hours, where there's, like, a a public shaming portion of it, where it's like, oh, hey, like, hey. You start out really overconfident, or at least I do. And then, like, by the end of it, you're either, like, yeah, you do the Tiger Woods thing, or you're like, oh, wow. Really humbled at that point.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I'm gonna try not to turn this into 1 app 100 hours, but we'll see what happens. So\u003C\u002Fp>\n\u003Cp>Speaker 1: what's on the dish for today, man?\u003C\u002Fp>\n\u003Cp>Speaker 0: So today's episode, I'm gonna give you a lay of the land. So we have just revamped our partner program here at Directus. You know, in the past, we've we've tried to put things together. It's been very, loosey goosey, I think, is the technical term for it. So we've brought on a partner manager, Colton, who's heading that up.\u003C\u002Fp>\n\u003Cp>He's put a really awesome program in place. The next step for this and the evolution of it is to create a partner directory on our website. So figured directory, I don't know if you've built 1 of those on a 100 apps, a 100 hours yet, but, director seems like the perfect use case to build a directory directory of some sort. A couple things, and actually, what I'll do is I'll pull up Excalidraw because I know that's what you do. And Oh, yeah.\u003C\u002Fp>\n\u003Cp>The way that you do things.\u003C\u002Fp>\n\u003Cp>Speaker 1: I'm I'm a, like, FigJam guy just because Oh, you're\u003C\u002Fp>\n\u003Cp>Speaker 0: a FigJammer? Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: Hey. Excel I like Excel draw as well. To each their own.\u003C\u002Fp>\n\u003Cp>Speaker 0: FigJam's pretty cool. I have nothing against FigJam. But, okay. So a couple of things. Partner directory.\u003C\u002Fp>\n\u003Cp>So it's gotta be accessible for our partners. We want partners to be able to update their own, instance, and put in things like their their part their agency name, main point of contact, where they're located, and, like, upload things like showcase work. So Okay. Obviously that name, point of contact, location,\u003C\u002Fp>\n\u003Cp>Speaker 1: and Description, summary. Description slash summary,\u003C\u002Fp>\n\u003Cp>Speaker 0: areas of expertise. What are\u003C\u002Fp>\n\u003Cp>Speaker 1: you thinking as far as, like, location data?\u003C\u002Fp>\n\u003Cp>Speaker 0: Location data. So that's probably what we'll filter on the front end is, you know, I'm thinking of the user experience they land on, like, direct us dot I o slash partners, and then they filter down based on a partner that's, you know, the general location because I like to support local. So, if I was looking for an agency, I would look for someone here in the East Coast of the United States. So I think by country, and then they can just drill down from there. But I would say, like, country top level.\u003C\u002Fp>\n\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\n\u003Cp>Speaker 0: Areas of expertise is interesting because I'm trying to think of, like I guess it would be like they wanna like, is it, like, a website or is it, like, app development? Or is it more granular than that, do you think? Like, like, actually, like, flows and insights and, like, direct us things like that?\u003C\u002Fp>\n\u003Cp>Speaker 1: If for an agency, I would think, like, areas of expertise to me is like, hey. Like, what do we specialize in? Is it, like, web development? Is it mobile app development? Is it, UI, UX?\u003C\u002Fp>\n\u003Cp>Is it all of it, I guess? At least that's the way that I see a lot of these things handled. Right? At other companies or or other spots online, I guess.\u003C\u002Fp>\n\u003Cp>Speaker 0: That makes sense. That makes sense. Cool. Trying to think of what else. We'll have a showcase area where people can upload upload projects.\u003C\u002Fp>\n\u003Cp>Speaker 1: You're probably gonna want a logo. Yes. Logo. And then the I always hesitate to, like, throw, like, pricing, if we've got some, like, freelancers in there, if we've got some, like, freelancers in there, you know, they're, like, stacking them up directly with an agency that has a 100 developers is not is not fair to either 1 of them. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I think I have to figure that out. Maybe it's like a pick list with, like like, 0 to not 0. It wouldn't be a agency. But 1 to 9, 10 to 49.\u003C\u002Fp>\n\u003Cp>Speaker 1: Oh, he's getting it very granular. I like it.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I'll say 10 to 99, and then a 100 plus. Seems like a pretty good 3 to 3. Small, medium, large. What else do you think?\u003C\u002Fp>\n\u003Cp>Actually, this feels like a pretty good, v 1.\u003C\u002Fp>\n\u003Cp>Speaker 1: This is a this is a good jumping off point.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yes. V 1 is my favorite and not because I'm lazy. It's just because it's good to just get something out there, and also because I'm sort of lazy. So, feels like a good scope of work.\u003C\u002Fp>\n\u003Cp>Speaker 1: So I like it.\u003C\u002Fp>\n\u003Cp>Speaker 0: First\u003C\u002Fp>\n\u003Cp>Speaker 1: steps. I'm I'm just monitoring the environment. That's, I Oh, yeah. Yeah. Yeah.\u003C\u002Fp>\n\u003Cp>We we've stepped up the difficulty level for this episode because I've got 3 kiddos downstairs who've been sick all week. Fully expect 1 of them to bust in the office. And so that might make this more interesting.\u003C\u002Fp>\n\u003Cp>Speaker 0: You know, that's technically, I'm lost. We roll with\u003C\u002Fp>\n\u003Cp>Speaker 1: I that could be could be set for me as well at this point. Like, technically, parental wise, I am lost right now.\u003C\u002Fp>\n\u003Cp>Speaker 0: Technically, I am totally lost. Alright. Cool. Alright. So next step, hopping into Directus.\u003C\u002Fp>\n\u003Cp>So for anybody that doesn't know, we use Directus internally for basically everything on the marketing side. So our entire website's powered. These are all our pages here. We use it for our docs, for publishing and and maintaining. Also, like, when you leave reviews, we capture that stuff.\u003C\u002Fp>\n\u003Cp>We have our direct to stop pizza. That's in here too. This is the main place where we can make changes to all that stuff, the the interactive demo. We capture metrics, and we capture key marketing metrics in here. It's my weekly dashboard that I put together.\u003C\u002Fp>\n\u003Cp>So the partner portal is gonna fit nicely in here because it gives us 1 place to access everything, and we'll just limit things with roles and permissions at some point, in this episode and all of this stuff. But yeah. So, Bryant, starting from scratch with this partner directory, where do you suggest we start?\u003C\u002Fp>\n\u003Cp>Speaker 1: Where do you well, like, you've already got a, like, start of a data model kind of mapped out in a scale draw. Like Yeah. Why wouldn't we start there?\u003C\u002Fp>\n\u003Cp>Speaker 0: I think that is a great place to start. So, again, I'm just completely, doing this as a nontechnical person. I wanna caveat that. I'll probably say it a 100 times in this episode, but I'm, yeah, starting from scratch here. So what I assume, and just correct me if I'm making a mistake here, we're gonna wanna create a data schema, which we will do inside of collections, inside of settings.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. We will create a new collection. Now, you get to encounter the hardest part of development, which is naming things.\u003C\u002Fp>\n\u003Cp>Speaker 0: Naming conventions or So, hey, like, hey, what\u003C\u002Fp>\n\u003Cp>Speaker 1: are we gonna call this new collection? Like, is it gonna be\u003C\u002Fp>\n\u003Cp>Speaker 0: Do we call it is it so that's the thing. Is it, like, the high level thing? Could we just call it partner directory and then all of the fields would be within that? Or do we have to get, like\u003C\u002Fp>\n\u003Cp>Speaker 1: so I like, typically, like, the way that I name my tables, and this is I say this in every video. It's 1 of my favorite features inside Directus is the ability to, like, translate that. Right? So, usually, when I name tables, it is like a what's the object? And then I I usually end up with, like, a plural.\u003C\u002Fp>\n\u003Cp>So it's pages or posts or partners or agency partners or, you know, trying to think on, like, the partner side, we've got agencies, we've also got tech partners. Do those live in the same directory? And they're you know, do they have a lot of the same characteristics as as far as, like, the data or totally different thing?\u003C\u002Fp>\n\u003Cp>Speaker 0: It's a great point. So I think at some point, as we start to grow out the tech partner side of things, we'll have kind of the same concept for them, and we'll have, like, integrations pages maybe or, like, you know, something like that. We wouldn't wanna conflate the 2. So maybe it is just agency partners for this because that's what the hyperfocus is on. So are all you think all partners would be considered agencies or if there's any, like\u003C\u002Fp>\n\u003Cp>Speaker 1: Again, right? That's the and now you're waiting into developer land where it's like, hey, I I know what I wanna do with this, but what do I name the thing? Because it's gonna confuse somebody later.\u003C\u002Fp>\n\u003Cp>Speaker 0: I've been in developer land for 10 minutes, and I'm already not liking it. So I don't know I don't know how you guys do this. But I it let's roll\u003C\u002Fp>\n\u003Cp>Speaker 1: with it. And, you know, again, like, within the interface, we can rename it. Now before you move on here, the other thing I want you to do, and this is mostly just out of habit for me, when I'm building all of these, like, direct us plus starter kits. The primary key field, set the type on that to UUID. The type not no.\u003C\u002Fp>\n\u003Cp>No. No. No. No. No.\u003C\u002Fp>\n\u003Cp>No. No. No. No. Slow down, cowboy.\u003C\u002Fp>\n\u003Cp>There you go. Let's go. It's the type 2 generated UUID. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: Here. What the hell? I mean, what the heck do any of these mean? This is where I get lost, and I don't wanna turn this into, like, a long,\u003C\u002Fp>\n\u003Cp>Speaker 1: like, discussion. So, basically, every table in the database has to have a or every row within a table. Right? So when you're adding this collection, you're creating a table inside the database called agency partners. Every row within that has to have a unique identifier so that the database knows what that is and how to reference it.\u003C\u002Fp>\n\u003Cp>I got you. So you can you can basically generate that identifier either, like, a auto incremented integer, which is, like, 1, 2, 3, 4, 5, 6, 7. When you are I I doubt we'll use this for a starter kit. But, when you try to transfer those IDs to a different database, like, you've gotta reset, like, the the increment count on that database. Okay.\u003C\u002Fp>\n\u003Cp>Our things get squirrelly. So using a UUID prevents that because it's just a random string of 36 characters.\u003C\u002Fp>\n\u003Cp>Speaker 0: Okay. I'm tempted to go big or go home, but I will go give you ID.\u003C\u002Fp>\n\u003Cp>Speaker 1: Oh, yes. Now you could also do the manually entered string, but, like, like, I I see people will use that for slug a lot. But the problem there is you can't change that after the fact. So if somebody renames their agency, that primary key would still be the same thing because you can't change that.\u003C\u002Fp>\n\u003Cp>Speaker 0: Okay.\u003C\u002Fp>\n\u003Cp>Speaker 1: So if it's Matt's agency and then you sell it, it becomes my agency. Like, I'm stuck with Matt's agency.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Should've bought it. Okay. Your UID. That makes sense now.\u003C\u002Fp>\n\u003Cp>So, everything will have a automatically generated, identifier that if we decide to pull all the stuff out of Directus at some point, it'll follow it. If we wanna integrate it with something like a tool like clay, when we pull that in, it'll be able to keep everything in rows nice and clean.\u003C\u002Fp>\n\u003Cp>Speaker 1: Nice and easy.\u003C\u002Fp>\n\u003Cp>Speaker 0: I'm learning. Technically, I'm lost. TIL today. Learning.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. There you go.\u003C\u002Fp>\n\u003Cp>Speaker 0: Cool. I know there's a lot of things with, optional fields here, but, you know\u003C\u002Fp>\n\u003Cp>Speaker 1: Best thing to do is just check them all. You could delete them later if you don't need them. Right? So these are just, like, the helpers within the system.\u003C\u002Fp>\n\u003Cp>Speaker 0: Nice. Okay.\u003C\u002Fp>\n\u003Cp>Speaker 1: Like, they created whenever you create a new record, it will populate the timestamp for you. Automatic? Same thing. Yep.\u003C\u002Fp>\n\u003Cp>Speaker 0: Genius. I created a collection, like, a little while ago before this episode, and I forgot to check that. And I'm, like, oh, crap. It's not generating.\u003C\u002Fp>\n\u003Cp>Speaker 1: You could you could do it. It's just easier to check the boxes and then delete it if you don't need it. Alright.\u003C\u002Fp>\n\u003Cp>Speaker 0: Beautiful. Alright. So we've got our agency partners data model. Now, next step for me is gonna be to pick a color because I'm in marketing. So I'll just, do this nice little\u003C\u002Fp>\n\u003Cp>Speaker 1: Colors and icons, baby. Yep.\u003C\u002Fp>\n\u003Cp>Speaker 0: There's a I hate this shirt search. Oh, handshake. I found 1. Nice. Alright.\u003C\u002Fp>\n\u003Cp>The search always busts for me, with those icons. Me and icons have a lot of hate.\u003C\u002Fp>\n\u003Cp>Speaker 1: I the Google naming team on those. Guys, hit me up. I'd like to make you guys need some help.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. But, anyways, next steps. We got our handshake. We got our nice green color. Green is great.\u003C\u002Fp>\n\u003Cp>I love Luigi. So we're all there. Good there. Now the next step is gonna be creating those fields that we had mapped out earlier. Correct?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah, baby. You got it.\u003C\u002Fp>\n\u003Cp>Speaker 0: Alright. So, this is 1 of my favorite parts of directors actually because of these nice little visualizations that give you a a clear preview of what you're getting into. Let's see. So input seems like the 1 that's generally used.\u003C\u002Fp>\n\u003Cp>Speaker 1: For just like a string of text? Like, what do what what are we starting with? Like, the agency name?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. So we're gonna start with name. Actually, you know what I'm gonna do? A little arc magic here. Yeah.\u003C\u002Fp>\n\u003Cp>Alright. So we've got our window. Alright. So side by side here. I think the important thing to start with is gonna be, obviously, the name of the agency.\u003C\u002Fp>\n\u003Cp>So input, just a string of text, name, unless we have a specific name field here, which I don't think we would because it seems very unique.\u003C\u002Fp>\n\u003Cp>Speaker 1: Nope. You got it, boss.\u003C\u002Fp>\n\u003Cp>Speaker 0: So question I had here was, should we, in the grand scheme of, like, all of the other fields and collections and things inside of Directus, when I'm naming things, should the naming convention be, like, instead of name, should it be, like, agency underscore name, or does it really matter in the grand scheme of everything else?\u003C\u002Fp>\n\u003Cp>Speaker 1: This is the the fun. Yeah. I would probably just call it name because, like, within this, you're not gonna have, like, a lot of related items. Right? Like, you're probably gonna have some projects to showcase, you know.\u003C\u002Fp>\n\u003Cp>I guess, in that sense, you could have, like, a partner name and a project name. I only, like, start adding prefixes when it really starts to get confusing. Like, if I've got a bunch of nested relationships that I'm going to be, like, dealing with all that data at once. So, like, the name of a page and, like, the name of a block and, like, the name of a button and stuff like that. Like, at that point, you might wanna, like, start adding prefixes so that you it doesn't get confusing.\u003C\u002Fp>\n\u003Cp>Or or, like, if you think of, like, organizations and contacts, you know, that might be organization name and then, like, contact name type of convention.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Maybe I'll because we're gonna have a point of contact in here, so they'll wanna enter their name. So maybe I just I'll I'll just go with agency name here.\u003C\u002Fp>\n\u003Cp>Speaker 1: Or it could be partner name.\u003C\u002Fp>\n\u003Cp>Speaker 0: Partner name. That's right. Partner name. String, we wanna keep it empty. And go to fill it out.\u003C\u002Fp>\n\u003Cp>Obviously, that's gonna have to be required. I don't think any placeholder. If I wanna get fancy, I can add an icon. I'll hold off on that. Is there anything in the advanced field creation mode that I should be aware of?\u003C\u002Fp>\n\u003Cp>It doesn't seem like it's that advanced other than, like, maximizing the length of what they'll input.\u003C\u002Fp>\n\u003Cp>Speaker 1: Unique?\u003C\u002Fp>\n\u003Cp>Speaker 0: I'm not worried about that 1.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. So the name is probably not yeah. You're not really concerned about a unique name. The advanced field settings just gives you, like, more control over it. And you can always go back in later, like, after you create this and edit that.\u003C\u002Fp>\n\u003Cp>Right? When we get into, like, how you're gonna reference this on the front end. Right? That would probably be, like, the next thing that I would tackle as well as, like, a slug of, like, hey. We we've got this random string of characters, like, in the URL.\u003C\u002Fp>\n\u003Cp>We don't wanna use that. We wanna use, like, a slug. So\u003C\u002Fp>\n\u003Cp>Speaker 0: Okay.\u003C\u002Fp>\n\u003Cp>Speaker 1: That that's where we'll get into advanced.\u003C\u002Fp>\n\u003Cp>Speaker 0: And we don't wanna allow a null value.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. It it like, required is probably gonna take care of that. Like, that is gonna be on, like, the database side as well. Okay. Like, if you had any existing records in this, I probably wouldn't let you, like, check that or uncheck that box because, you know, like, you might have records that don't have a value for that specific field, like, if that field already existed.\u003C\u002Fp>\n\u003Cp>Speaker 0: Right. Okay. Cool.\u003C\u002Fp>\n\u003Cp>Speaker 1: Today, I'm in the weeds. Not sure what the acronym is for that 1. But\u003C\u002Fp>\n\u003Cp>Speaker 0: I do.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. So we're gonna keep editing. Alright. So your next field, I vote for slug. Slug.\u003C\u002Fp>\n\u003Cp>That is gonna be a string.\u003C\u002Fp>\n\u003Cp>Speaker 0: Having trouble with this.\u003C\u002Fp>\n\u003Cp>Speaker 1: You're gonna you're arching yourself. Yeah. Time for a page refresh.\u003C\u002Fp>\n\u003Cp>Speaker 0: Page refresh. Save. I lost it. Found it. Okay.\u003C\u002Fp>\n\u003Cp>Slug.\u003C\u002Fp>\n\u003Cp>Speaker 1: Alright. It's gonna be an input. Okay. Same thing. The key of slug.\u003C\u002Fp>\n\u003Cp>Okay. Yeah. Yeah. I would just call it slug. Be fine.\u003C\u002Fp>\n\u003Cp>Alright. So now then you have a a go into the Advanced Field Creation Mode.\u003C\u002Fp>\n\u003Cp>Speaker 0: Mhmm.\u003C\u002Fp>\n\u003Cp>Speaker 1: And we need to make sure this is URL safe. So you are going to go to the interface section, and there is a slugify option.\u003C\u002Fp>\n\u003Cp>Speaker 0: And what does that do? Prefix it?\u003C\u002Fp>\n\u003Cp>Speaker 1: Basically, like, anytime you try to add a space, it's gonna add a hyphen. Or if you try to use any, like, special characters that aren't allowed in a URL, it's not gonna allow you to input those.\u003C\u002Fp>\n\u003Cp>Speaker 0: Nice. Okay. Very cool.\u003C\u002Fp>\n\u003Cp>Speaker 1: And then in that case, you also are probably gonna want this to be unique. Right? Because, on the front end, this is gonna be part of the URL, and it's gonna be the unique identifier that we're 1 of the unique identifiers we're using to fetch that data for the agency. So, yeah, you could certainly set it to be required if you want to, or we could have, like, a flow that generates the slug. But on the, the schema page or the schema tab, you set that value to unique.\u003C\u002Fp>\n\u003Cp>Speaker 0: Feeling good about it. Alright. Bam. Now question. So we'll have agency name.\u003C\u002Fp>\n\u003Cp>We'll have their website. When it comes to adding, like it's just kind of jarring, I think, when you're filling out a form, when it's like these 2 questions and then 3 completely unrelated questions, which in this case would be like, here's about your agency. Oh, tell me about yourself. Let's talk about, like, something else. Is there a good way to maybe split up this?\u003C\u002Fp>\n\u003Cp>Like, use, like, maybe add in a header or some sort of field that breaks it up, or is it just, like, best best use case, best scenarios? Just like\u003C\u002Fp>\n\u003Cp>Speaker 1: you know, what I like to do in my forms is just make them all, like, half width. So you got, like, this, and then just add as many as you can in a row so that it's really confusing. Love it. No. There there's there's lots of great settings for that.\u003C\u002Fp>\n\u003Cp>If you open back up the create field, like, the ones that you're looking for, like, we call them alias fields, which basically, like, it's not really storing any, like, specific data inside the the actual database.\u003C\u002Fp>\n\u003Cp>Speaker 0: Right? Mhmm.\u003C\u002Fp>\n\u003Cp>Speaker 1: It is just controlling, like, the layout within the form. So those are gonna be under the group section.\u003C\u002Fp>\n\u003Cp>Speaker 0: Group. Groups.\u003C\u002Fp>\n\u003Cp>Speaker 1: Presentation. So you see there's a couple there. You have, like, presentation wise, there's, like, a divider. You can add some button links. You can add a notice to the form, and you can make that conditional.\u003C\u002Fp>\n\u003Cp>But if you wanna group related fields together, you got, like, the group options. So depends on how you want to slice it. Most of the time, I end up using, like, the detail group, which is basically, like, I could throw 35 group or 35 fields in that and hide them all with, like, a collapsible accordion type thing. The accordion is basically 1 field at a time, pops open. So, okay.\u003C\u002Fp>\n\u003Cp>Gotcha. Yeah. Most of the time, I end up using detail group.\u003C\u002Fp>\n\u003Cp>Speaker 0: Nice. I'm gonna try that. So then I'll call this, contact.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. And, usually, like, this is just like an OCD thing for me. What I end up doing is, like, a like, underscore details or group or, like, put, like, a meta prefix underneath it just so, like, I know, like, this field is just for, like, grouping.\u003C\u002Fp>\n\u003Cp>Speaker 0: I like it. Point of contact group. So, so we're gonna nest things underneath this group, or is it the once we hit save\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Just hit save. Do we can come back.\u003C\u002Fp>\n\u003Cp>Speaker 0: Question on this. Would we want it required, or do we require the separate ones? No.\u003C\u002Fp>\n\u003Cp>Speaker 1: You don't have to require that 1. I'm not even actually sure what what that would be.\u003C\u002Fp>\n\u003Cp>Speaker 0: Feels dangerous.\u003C\u002Fp>\n\u003Cp>Speaker 1: That might be a good feature request of, like, hey. We should probably do something within that. Alright. So now, basically, what you got, you've got a container within the form that you can drag fields into. But you gotta create some fields first for the point of contact.\u003C\u002Fp>\n\u003Cp>Speaker 0: Great. First name, last name, any like, just 2 separate inputs side by side, I guess.\u003C\u002Fp>\n\u003Cp>Speaker 1: I yeah. Are we going to use is there a situation where for your point of contact, you're gonna, like, use the just the first name?\u003C\u002Fp>\n\u003Cp>Speaker 0: I would say if we automate emails from Directus when they register their company, we're gonna wanna obviously personalize it, and we don't wanna say, hey, Bryant Gillespie. Thanks for signing up. It'd be much more casual to be like, hey, Bryant. So probably in that case, would it make sense to split up the 2?\u003C\u002Fp>\n\u003Cp>Speaker 1: I I would break them up in that case. Now the other consideration here is that you're gonna have a user that logs into this as well. And, like, are you gonna send that email to the user from that agency? Or, like, who the the designated point of contact is?\u003C\u002Fp>\n\u003Cp>Speaker 0: I would think whoever the designated point of contact is.\u003C\u002Fp>\n\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\n\u003Cp>Speaker 0: So, like, as they they will give them ability to add other people under their their agency. And then when we can trigger notifications, like, when, hey, so and so from your team has signed up or maybe we put into delay that's like, hey. You sent an invite. They never signed up. Go, you know, strangle them.\u003C\u002Fp>\n\u003Cp>Not that\u003C\u002Fp>\n\u003Cp>Speaker 1: Go strangle them. Yeah. I got you. I got you. I I so and, like, POC to me, like, proof of concept, not point of contact.\u003C\u002Fp>\n\u003Cp>That's just because I'm building. So I would just call this, like, either, like, contact first name.\u003C\u002Fp>\n\u003Cp>Speaker 0: Contact first name.\u003C\u002Fp>\n\u003Cp>Speaker 1: Or you could probably I mean, yeah. That's probably fine.\u003C\u002Fp>\n\u003Cp>Speaker 0: Cool. Like it. Require. Save. This is the boring part of the show where I just fill in these things.\u003C\u002Fp>\n\u003Cp>Nat, you might wanna fast forward this part.\u003C\u002Fp>\n\u003Cp>Speaker 1: Get, like, some Benny Hill or something playing in the back. Right. Alright.\u003C\u002Fp>\n\u003Cp>Speaker 0: So here's\u003C\u002Fp>\n\u003Cp>Speaker 1: a question.\u003C\u002Fp>\n\u003Cp>Speaker 0: Let's say I wanna bump these 2 together. So I click here. Obviously, half width. I might be able to figure this 1 out myself, Bryant. I don't think You got\u003C\u002Fp>\n\u003Cp>Speaker 1: it, boss. You got it. Alright. So You can Now yeah. Go ahead and add your other field there.\u003C\u002Fp>\n\u003Cp>What are you you're gonna have, like, a point of contact email or contact email?\u003C\u002Fp>\n\u003Cp>Speaker 0: Point of contact email will be just another input field? Is there any special\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. There is there's not like, as far as the database is concerned, it's just a string of text. Okay. You could go in and, like, apply some validation to it. You you can use, like, regex.\u003C\u002Fp>\n\u003Cp>We'll we'll leave that 1 for another call.\u003C\u002Fp>\n\u003Cp>Speaker 0: I've had my time I've served my time in regex. So let's let's say I wanna add a validation. Right? So, validation here, I wanna add, like it has to have an at, obviously, if it's an email. Just a little quick little validation.\u003C\u002Fp>\n\u003Cp>I would add a filter contact email. And this is must, like Contains. Yeah. Contains just a at symbol? Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: It has to contain an at symbol somewhere. Now I didn't like That's The the other, like, fun bit of this, right, is what if they put 2 at symbols. Right? That your validation there would pass. So this is where, like, you could reach for reg x, but, you know, again, that's just we could spend, like, 45 minutes to an hour just like, what is reg x?\u003C\u002Fp>\n\u003Cp>Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: Is this, do you think this is something that can be well, not think, but once I create these fields, are they set in stone? And what I have to, like\u003C\u002Fp>\n\u003Cp>Speaker 1: so the only thing that you can't change here is gonna be, like, the keys. So, like, the key of the field that you set on the schema. Like, once you commit this, or that can't go back and change the key of that field because there's a lot of references inside the database.\u003C\u002Fp>\n\u003Cp>Speaker 0: Right. Okay.\u003C\u002Fp>\n\u003Cp>Speaker 1: Now if I've got database access, I could, but I I wouldn't put that evil on you.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. We'll try it without.\u003C\u002Fp>\n\u003Cp>Speaker 1: Alright.\u003C\u002Fp>\n\u003Cp>Speaker 0: Cool. So that makes sense. I can change that validation later in that case. So it didn't require it. Edit field, required.\u003C\u002Fp>\n\u003Cp>So I can go in. Look at that.\u003C\u002Fp>\n\u003Cp>Speaker 1: You can do all that. Reach.\u003C\u002Fp>\n\u003Cp>Speaker 0: Okay. Cool. So I feel like that's good for the point of contact. The next thing is gonna be location.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: So for this section, and just because I saw this divider thing, and I'm just gonna add that in there. I'm just gonna save a divider. Location. Now I know we have, like, a map, geospatial fields, things like that. I don't know if it makes sense to have the map as your input for the location.\u003C\u002Fp>\n\u003Cp>But if we're doing it by country, it would make sense to do it as a pick list or a drop down. Right?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Or you could just have it as an input as well. It depends on, like, the like, if you wanna normalize it. Right? So So on the\u003C\u002Fp>\n\u003Cp>Speaker 0: front end yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: So on the front end\u003C\u002Fp>\n\u003Cp>Speaker 0: if we're gonna do it on the front end, like, thinking of the filter ability is it'll be a drop down for them. Like, I'm imagining, like, a bar at the top, and then they can just drop down, list of countries, pick it, and then it'll propagate all the ones that have that. As an input, would that if somebody misspells, if somebody enters the USA as opposed to, like, United States or America, all of those little, like, side use cases cases, it would run into an issue there. We'd have to figure a way to normalize them on the back end.\u003C\u002Fp>\n\u003Cp>Speaker 1: Correct. Yeah. It'd be a pain in the ass, basically.\u003C\u002Fp>\n\u003Cp>Speaker 0: Technically speaking, that would be a pain in the ass. Thank you, doctor.\u003C\u002Fp>\n\u003Cp>Speaker 1: Drop down? You could. You could do a drop down. Like, the the trouble here, right, is you gotta get a list of all the country codes or all the countries that that we have partners in if you're gonna do a drop down. Right?\u003C\u002Fp>\n\u003Cp>Because I got it. Like, you you could oh, you've got it. Well, then, yeah, go for it, dude. Is this the same\u003C\u002Fp>\n\u003Cp>Speaker 0: or, like, GitHub country list? And there's a plain text list of countries that you can copy and paste, from directly from GitHub.\u003C\u002Fp>\n\u003Cp>Speaker 1: Woah. They\u003C\u002Fp>\n\u003Cp>Speaker 0: also have it in a JSON format, but with the ISO code. But Alright.\u003C\u002Fp>\n\u003Cp>Speaker 1: So, like, underneath the hood, when you are setting up that drop down feel, like, if you wanna click on it, those like, the choices there. So, yeah, if you just call this you might as well just call it country. Right? So we'll give it a key of country. The choices that you've got there, if you go in and you just add 1, right, You've got a text and a value.\u003C\u002Fp>\n\u003Cp>Right. So the text is gonna be what's displayed inside the application, and then, like, the value here could either be, like, the same thing or or the probably the way that I would do it would be, like, the the 2 digit country code, like, the ISO standard or whatever that is. I'm not sure what that is for Afghanistan.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I'm gonna go with the easier 1 here.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. And I would honestly, it'd probably be, Yeah. I don't know. What's your does your GitHub data have it in, like, all caps or lowercase?\u003C\u002Fp>\n\u003Cp>Speaker 0: I will share the screen. Actually, I'll pull it up here. Cool.\u003C\u002Fp>\n\u003Cp>Speaker 1: Oh, yeah. They're all uppercase.\u003C\u002Fp>\n\u003Cp>Speaker 0: So all uppercase, and then they've got somebody did the due diligence of typing in the things down here in a JSON format. So what do you think is the best way to go about this?\u003C\u002Fp>\n\u003Cp>Speaker 1: I would grab the JSON data. You if you got Versus code?\u003C\u002Fp>\n\u003Cp>Speaker 0: I do. I do, actually.\u003C\u002Fp>\n\u003Cp>Speaker 1: Okay. Alright. And we're gonna make a giant drop down here. Like, the the other thing that we could do, there's a there's an auto complete option where you can call an API, but I don't know I don't have a good country code API right off the the top to get just like the the ISO codes, so it'd be fine. Open up Versus Code.\u003C\u002Fp>\n\u003Cp>Speaker 0: Alright. I gotta change my streaming here. Getting me all twisted. Versus Code.\u003C\u002Fp>\n\u003Cp>Speaker 1: Alright. So copy that list. Create a new file here. Copy your list of the data from GitHub. Paste the\u003C\u002Fp>\n\u003Cp>Speaker 0: JSON list that was created?\u003C\u002Fp>\n\u003Cp>Speaker 1: Alright.\u003C\u002Fp>\n\u003Cp>Speaker 0: This is a long list.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Let's, alright. It it gets more interesting when you, like, hey. Are you gonna reference this again somewhere else? And then at that point, you might as well add it as a table in the database and reference it that way.\u003C\u002Fp>\n\u003Cp>So you could use it in other collections without having to either copy the field or maintain this thing. Alright. So what are you gonna do here? Like, basically, this is not in the correct format for Directus. Right?\u003C\u002Fp>\n\u003Cp>We've got an ISO code, and we've got the country. Directus, as far as like your choices for the drop down, is expecting a text and a value. So this what are you gonna do here? You are going to, like, highlight where it says ISO code, and hit command f or you're on a Mac, so it should be command so just pick 1 of those those values. Go over there where it says ISO code.\u003C\u002Fp>\n\u003Cp>Just highlight that part.\u003C\u002Fp>\n\u003Cp>Speaker 0: Mhmm. 1 second. I think this list might be wrong, actually, because I don't see will the USB enter a different name?\u003C\u002Fp>\n\u003Cp>Speaker 1: United States.\u003C\u002Fp>\n\u003Cp>Speaker 0: I'm not finding it.\u003C\u002Fp>\n\u003Cp>Speaker 1: Do you is it is your search case sensitive?\u003C\u002Fp>\n\u003Cp>Speaker 0: Is it? I don't know. No. No results. America only turns up.\u003C\u002Fp>\n\u003Cp>Samoa Americana. Yeah. This is wrong. This is the hazard. We're using GitHub.\u003C\u002Fp>\n\u003Cp>I'm gonna go ahead and just use this standard list that this person didn't falsify because this 1 looks right, which is just the list of countries, not in a JSON format.\u003C\u002Fp>\n\u003Cp>Speaker 1: Okay, boss man. Alright. What do you think? So, alright. This is gonna be the wizardry on the keyboard.\u003C\u002Fp>\n\u003Cp>Alright. So select all, command a. Mhmm. Alright. Now hit command right.\u003C\u002Fp>\n\u003Cp>Or is it shift command right? No. I I don't know how you got yours set up. Hold shift and do command right.\u003C\u002Fp>\n\u003Cp>Speaker 0: Shift command right.\u003C\u002Fp>\n\u003Cp>Speaker 1: Okay. Yeah. We don't have you set up right. If you have this list, is it if I select all yeah. It's not we need to get you to split this into lines.\u003C\u002Fp>\n\u003Cp>Is it Command P? Do Command P. Is that the Toolbar? Okay. Alright.\u003C\u002Fp>\n\u003Cp>Now do a greater than symbol, and search for, like, split into lines. What is it? Split? Hang on. I'll tell you.\u003C\u002Fp>\n\u003Cp>I'm trying it now.\u003C\u002Fp>\n\u003Cp>Speaker 0: Split join tag.\u003C\u002Fp>\n\u003Cp>Speaker 1: Print that lines, join lines, expand line selection.\u003C\u002Fp>\n\u003Cp>Speaker 0: Insert line?\u003C\u002Fp>\n\u003Cp>Speaker 1: No.\u003C\u002Fp>\n\u003Cp>Speaker 0: Insert line below?\u003C\u002Fp>\n\u003Cp>Speaker 1: No. It should be, what is the I know the command on mine. Add cursor. Is it add cursor? Endo.\u003C\u002Fp>\n\u003Cp>Line. Add cursors to line ends is the name of it.\u003C\u002Fp>\n\u003Cp>Speaker 0: Add cursor to line end.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yep. Alright. So what that's gonna do. Shift option. Oh, yeah.\u003C\u002Fp>\n\u003Cp>Yeah. I've I've got a different command. Alright. So now, you see you got, like, a cursor on all of those. Woah.\u003C\u002Fp>\n\u003Cp>It's pretty pretty dope. Right? Alright. So now what you're gonna do, now you should be able to hold command and shift and left. That's gonna select all those individual yeah.\u003C\u002Fp>\n\u003Cp>Go left. Alright. Alright. Selected. So now you get them all individually.\u003C\u002Fp>\n\u003Cp>Now you're gonna pop the, quotation mark.\u003C\u002Fp>\n\u003Cp>Speaker 0: Just put in a quotation mark.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yep. Shift quotation. Yeah. I mean, not shift quotation, but just quotation mark. Alright?\u003C\u002Fp>\n\u003Cp>Yeah. Now you're gonna do command left. Command left. That'll get you to the start of the line. Now you're gonna add a left code bracket or mustache syntax there.\u003C\u002Fp>\n\u003Cp>Left. Yep. Alright. Do, what are we looking for? We're looking for a value.\u003C\u002Fp>\n\u003Cp>Mhmm. So you're gonna do quotation mark value. Type type\u003C\u002Fp>\n\u003Cp>Speaker 0: in value?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yep. This is gonna be proper JSON now.\u003C\u002Fp>\n\u003Cp>Speaker 0: Oh, boy. Quotation mark.\u003C\u002Fp>\n\u003Cp>Speaker 1: Quotation mark, comma, or no. Not comma. I'm sorry. It's a, colon. There you go.\u003C\u002Fp>\n\u003Cp>Alright. Now go to the end of the line.\u003C\u002Fp>\n\u003Cp>Speaker 0: Command right. Yep.\u003C\u002Fp>\n\u003Cp>Speaker 1: Command right. Yep. Add your What's that? Is your right mustache. And then you are going to add a comma.\u003C\u002Fp>\n\u003Cp>Alright. Now, go hit command up. And because this is an array of values, we gotta use the, I don't even know what that's called. Bracket? What is that called?\u003C\u002Fp>\n\u003Cp>It's it's not it's it's not the shift key.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yep. And then I'll do this.\u003C\u002Fp>\n\u003Cp>Speaker 1: At the very bottom, you gotta add another 1.\u003C\u002Fp>\n\u003Cp>Speaker 0: And then it's gonna be the closed.\u003C\u002Fp>\n\u003Cp>Speaker 1: This is an array of values. There you go. And then on that last 1, what you're gonna do, the very last value there, remove that comma. And now you have a valid JSON array that you can copy that whole thing. The formatting doesn't matter.\u003C\u002Fp>\n\u003Cp>Speaker 0: How do I copy? Just kidding.\u003C\u002Fp>\n\u003Cp>Speaker 1: It's I can't help you with that.\u003C\u002Fp>\n\u003Cp>Speaker 0: This is amazing, by the way. I just learned how to do JSON.\u003C\u002Fp>\n\u003Cp>Speaker 1: TIO. Yeah. You you learned a couple things. Right? You learned, hey.\u003C\u002Fp>\n\u003Cp>Like, the some of the keyboard shortcuts for Versus Code, especially when you're trying to do reformatting and stuff like this. Super helpful for that.\u003C\u002Fp>\n\u003Cp>Speaker 0: I'll never remember him. But, so I'm gonna discard this because this was what we were trying to do earlier. But, am I on the wrong page? On the wrong page.\u003C\u002Fp>\n\u003Cp>Speaker 1: So Yeah. Nobody could see what you're doing.\u003C\u002Fp>\n\u003Cp>Speaker 0: I didn't mean what we just did. I meant what and yeah. That would've been terrible.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. I'm gonna jump through the computer and strangle you at that point.\u003C\u002Fp>\n\u003Cp>Speaker 0: Show me all the shortcuts again. Okay. So we've got our JSON copied. Yep. We're using this drop down unless you\u003C\u002Fp>\n\u003Cp>Speaker 1: So where it says choices. Mhmm. Paste your JSON. So click where it says choices. Just the header.\u003C\u002Fp>\n\u003Cp>There you go. Paste raw value or yeah. You could hit edit raw value and paste it in there.\u003C\u002Fp>\n\u003Cp>Speaker 0: Done? Done. Done. Done. Done.\u003C\u002Fp>\n\u003Cp>Speaker 1: Except we forgot to add text to all of those, do we?\u003C\u002Fp>\n\u003Cp>Speaker 0: Did we? Oh, from the front end. Because it won't show the presentation of it. Right? We forgot.\u003C\u002Fp>\n\u003Cp>Speaker 1: Now what do you do?\u003C\u002Fp>\n\u003Cp>Speaker 0: Should I restart? I think we're we're really, like, pretty fast. Right? We should be able to do that pretty fast. Or wait.\u003C\u002Fp>\n\u003Cp>Can I just take the no?\u003C\u002Fp>\n\u003Cp>Speaker 1: You could just copy the values. Yeah. For now, just save it. It should let you save it. Yeah.\u003C\u002Fp>\n\u003Cp>And I don't know if it's going to\u003C\u002Fp>\n\u003Cp>Speaker 0: throw an error on.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. I'm actually not sure how the drop down will behave. It'll let you save it for sure. But I I don't know if when you actually test it out, if it will show that value because it doesn't have the actual text to to do that with. Okay.\u003C\u002Fp>\n\u003Cp>We can we can look at it, though. We'll sort that out in a moment. Alright. What's next, man?\u003C\u002Fp>\n\u003Cp>Speaker 0: So next.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. It's driving me freaking crazy. The OCD part of him, like, the x designer in me. Like, you've you've got the point of contact group. Just drag those contact fields into that.\u003C\u002Fp>\n\u003Cp>Because, otherwise, they're not gonna display inside that group or or not be collapsible.\u003C\u002Fp>\n\u003Cp>Speaker 0: How's this helping your OCD?\u003C\u002Fp>\n\u003Cp>Speaker 1: Gotcha. That's that's why I call myself a recovering designer.\u003C\u002Fp>\n\u003Cp>Speaker 0: Gotcha. Gotcha. Okay. Cool. So I think we're in a good spot here.\u003C\u002Fp>\n\u003Cp>I'm gonna add this quick, for, like, a description or something like that. Just use, like, a text area probably. Makes sense.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: Agency description.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. So, like, the in the database, it's gonna be stored as, like, text no matter what which you whether you use, like, a WYSIWYG editor or a text area. The difference with the text area is you're not gonna have any, like, rich text formatting. So it depends on if you need that. You know, you could have hey.\u003C\u002Fp>\n\u003Cp>This is where you go back to, like, modeling your data of, like, hey. Do we have, like, a short description? Like, if we're showing cards on a page, we probably have, like, a short description. And then maybe on, like, if you click into a partner, you might have, like, a longer section of content or something.\u003C\u002Fp>\n\u003Cp>Speaker 0: That's a great point. So, yeah, this is actually gonna be structured as 2 pages. So we'll have the directory, which is this, gonna be the main thing. And then when they click into it, we'll have a separate page that is a little bit more and we'll have the WYSIWYG there. But this will just be like a short description of partner that'll be on the front.\u003C\u002Fp>\n\u003Cp>And honestly, like, I would probably limit this to a certain number of characters, setting a softer limit to I don't know. What do you think? 300 characters? What's the old Twitter thing? 250?\u003C\u002Fp>\n\u003Cp>Speaker 1: You're asking the wrong guy. 170. Is that was that what it is? 270?\u003C\u002Fp>\n\u003Cp>Speaker 0: 280. We'll set that.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Soft limit. So, hey, that soft limit that you just set there, that is only on the the UI. Right? Like, the there's no particular limit there on the the actual database.\u003C\u002Fp>\n\u003Cp>Speaker 0: Okay. That's fine. I, no. Actually, it's not fine because they're That's where, like, the building this out in the database. Right?\u003C\u002Fp>\n\u003Cp>Speaker 1: No. No. No. It's fine. Yeah.\u003C\u002Fp>\n\u003Cp>Hey. Like, because we're interacting through this, this layer, right, this Directus Studio, and through the Directus APIs, it's not a problem. Like, Directus will give us that validation. Great. If I wanted to enter validation.\u003C\u002Fp>\n\u003Cp>Speaker 0: Let's say I just want to say in the field itself, by the way, only 280 characters. Placeholder would not be the 1 to do that because they'd have to, like, erase that. Right? Would there be\u003C\u002Fp>\n\u003Cp>Speaker 1: a placeholder is basically just like, hey, what do I want? You know, like, a an extra hint for somebody, like, on a\u003C\u002Fp>\n\u003Cp>Speaker 0: Maybe a note here. Like\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. A note is, like, a description below the field basically.\u003C\u002Fp>\n\u003Cp>Speaker 0: Let's try that. Great. Feeling good about it. Description, areas of expertise, we'll probably wanna keep this limited to, the ones that we have so we can fill use those as filters as well at some point. We'll do that as a drop down, multiple because you're gonna have multiple special specializations, I guess.\u003C\u002Fp>\n\u003Cp>Yay? You and A?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Yeah. I mean\u003C\u002Fp>\n\u003Cp>Speaker 0: I see it says JSON here. I always get Or Expertise.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. It's a JSON. It's just gonna just the way it's being stored. Like, are we going to are you gonna allow them to filter based on those specializations or no?\u003C\u002Fp>\n\u003Cp>Speaker 0: I think at some point, I think it would be country and specialization would be the 2 things that you're gonna that you're gonna be searching for when you land on, like, a directory page.\u003C\u002Fp>\n\u003Cp>Speaker 1: Okay. So on that point, like, the the current operation, like, I cannot set I I can't query the data within that individual, like, JSON column. That should be coming in the future. As of right now, we don't have support for\u003C\u002Fp>\n\u003Cp>Speaker 0: it. Okay.\u003C\u002Fp>\n\u003Cp>Speaker 1: So at that point, then we need to look at a different way to do it, which would probably be, like, adding a database table for it. Or, what you can do do is just change the type to CSV, and that would allow us to get, like, a, hey, does this specialization field contain x? Okay. Because III think that's probably the extent of it. Right?\u003C\u002Fp>\n\u003Cp>Like, you're just gonna use it as a criteria for filtering this 1 thing. Probably, it should be fine. We'll do CSV.\u003C\u002Fp>\n\u003Cp>Speaker 0: Nice. Okay. So then we'll do website, development. Website. Web\u003C\u002Fp>\n\u003Cp>Speaker 1: web development.\u003C\u002Fp>\n\u003Cp>Speaker 0: You developers. So cool. Why say long word when short word work better?\u003C\u002Fp>\n\u003Cp>Speaker 1: Well, it could be like a web app. Alright. So these are these are the things that go through your mind, like the average developer, like, 100 times a day when you're building a project. If you see, like, a a puzzled look on their face, they're probably trying to figure out what to name the thing that they're trying to create. They know what the they already know what the thing looks like.\u003C\u002Fp>\n\u003Cp>It's all going up here, but it like, naming it is like, what\u003C\u002Fp>\n\u003Cp>Speaker 0: do we call this? Number 1. The number 1 thing. Okay. Mobile application, mobile app.\u003C\u002Fp>\n\u003Cp>Is that fine? Can I shorten application to app? It What about that?\u003C\u002Fp>\n\u003Cp>Speaker 1: It is. It's fine, man.\u003C\u002Fp>\n\u003Cp>Speaker 0: Alright. If you you being a previous agency owner, you're probably the best 1 to ask for this. What are some other specializations you can think off the top of your head?\u003C\u002Fp>\n\u003Cp>Speaker 1: Like UX, UI.\u003C\u002Fp>\n\u003Cp>Speaker 0: Should I And you get When I'm doing the value here, do they have to be a specific\u003C\u002Fp>\n\u003Cp>Speaker 1: like, option? It it doesn't. Like, it you know, like, technically, it could just be, like, UXUI.\u003C\u002Fp>\n\u003Cp>Speaker 0: Like this without an underscore?\u003C\u002Fp>\n\u003Cp>Speaker 1: It doesn't have to be. You see me start to sweat, like I'll do doing. Like, what whatever you do, it like, it's just gotta be normalized. Right? Like, you the most important part is, like, whatever convention you decide on, it's the same across the board, so you don't have major issues.\u003C\u002Fp>\n\u003Cp>Speaker 0: Okay. How do you feel about the spaces between this?\u003C\u002Fp>\n\u003Cp>Speaker 1: That's fine. I like it.\u003C\u002Fp>\n\u003Cp>Speaker 0: Alright. Web development, mobile application. I should probably change that to apps. Mobile applications. UXUI.\u003C\u002Fp>\n\u003Cp>Speaker 1: Digital transformation? That was that was the 1 that came up. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: Digital transformations.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. And and, like, technically, like, you could also like, do you get into, like, the specific frameworks here? You know? That's I I don't know. I would say,\u003C\u002Fp>\n\u003Cp>Speaker 0: you know, you come in with you need a project. There's only a few there's the way executing the project, you can do it building in a reactor view or whatever, but the actual project itself is what I'm trying to zoom in here on. And I feel like the 4 of these would probably touch most of those because we're, you know, we're not gonna skew into, like, public relations or, like, digital marketing, whatever that means. But,\u003C\u002Fp>\n\u003Cp>Speaker 1: yeah, it's like I'm I'm saying, like, hey, would you be, like, a yeah. We're like a a Laravel agency or, like, we are, you know, like a a React agency or, you know, those sorts of things.\u003C\u002Fp>\n\u003Cp>Speaker 0: That's another consideration and probably not for this episode, but I would probably add that as another field here of like this these are the things we specialize in React view.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. That could be, like, your expertise, I guess.\u003C\u002Fp>\n\u003Cp>Speaker 0: I don't know. Yeah. Specialization in expertise. These feel good. I'll, I'll leave them as the for now, for the sake of this episode and update those later.\u003C\u002Fp>\n\u003Cp>Let's see. Just exit that.\u003C\u002Fp>\n\u003Cp>Speaker 1: I was also a friend to you and and updated your country codes for you as well.\u003C\u002Fp>\n\u003Cp>Speaker 0: Oh, thank you. Much appreciated. So for logo, image, or file? What do you think?\u003C\u002Fp>\n\u003Cp>Speaker 1: Either 1. So, like, behind the scenes is basically the same. It's just like how you want it to display. So in this case, you probably wanna see, like, a nice image. We'll just call it logo.\u003C\u002Fp>\n\u003Cp>Yeah. That's fine. Partner logo. Yeah. So just use the image.\u003C\u002Fp>\n\u003Cp>Like, it it files there is gonna create a junction collection. So, like, if you need, like, a gallery, we would use that. But in this case, we just want a single logo to display.\u003C\u002Fp>\n\u003Cp>Speaker 0: Nice. Okay. Feels good. We can update this file library route later. We'll have, like, an agency specific folder.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. That's what I would do.\u003C\u002Fp>\n\u003Cp>Speaker 0: Crop to fit. Anything to be aware of in the advanced field creation for this sort of thing? Like, images or anything?\u003C\u002Fp>\n\u003Cp>Speaker 1: If you go into, like, display, like, you could control, like, how you want this to actually display. Right? So you wanna see, like, a tiny image preview, and then you can control, like, how it crops, basically.\u003C\u002Fp>\n\u003Cp>Speaker 0: Okay. Cool. Partner logo. And then this last bit is gonna be actually, we'll do size, 2 more fields. So size, numbers, but it'll still be kind of a drop down situation, I'm assuming.\u003C\u002Fp>\n\u003Cp>Speaker 1: So is it a is it like a are you storing a number, or is it like a it'll be a string? It'll be a Is it like 1 to 10 or, like, hey, like, we have a 100 or we have 56, we have 7.\u003C\u002Fp>\n\u003Cp>Speaker 0: I don't wanna get, like, too specific because I'd rather group it into, like, small, medium, large because at some point, we can filter that on the front end too. Yep. I need the enterprise company. I'm an enterprise company, enterprise agency, yadayada. So drop down\u003C\u002Fp>\n\u003Cp>Speaker 1: with Just hit me with the Seinfeld.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Seinfeld references into 2024. Let's go. Agency, partner size. Let's say partner size.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Yeah. I would get, like, team size or something. Right? Like, hey.\u003C\u002Fp>\n\u003Cp>That's the the other thing here is, like, do you need to, like, prefix each and every 1 of these fields or or just the ones that, like, could get confusing?\u003C\u002Fp>\n\u003Cp>Speaker 0: Now you're making me sweat about naming conventions. I'm not gonna\u003C\u002Fp>\n\u003Cp>Speaker 1: It's it's seriously like the hardest part.\u003C\u002Fp>\n\u003Cp>Speaker 0: Not today, Satan. I'm gonna leave the the naming confessions away for for now. Let's do 109. That'll be small. For value, here we go.\u003C\u002Fp>\n\u003Cp>1 underscore 9? No.\u003C\u002Fp>\n\u003Cp>Speaker 1: Just just do 1 to just keep it the same for it.\u003C\u002Fp>\n\u003Cp>Speaker 0: What about 5?\u003C\u002Fp>\n\u003Cp>Speaker 1: Alright. No. Just keep it the same, like, 1 to 9. Just the exact same thing. Right.\u003C\u002Fp>\n\u003Cp>Speaker 0: 19. 10 to 99.\u003C\u002Fp>\n\u003Cp>Speaker 1: It it it's just a string of text. It doesn't really matter.\u003C\u002Fp>\n\u003Cp>Speaker 0: It's a string of text, yet it's numbers. How's that for some\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. I get yeah. Confucius wisdom. Confucius. Yep.\u003C\u002Fp>\n\u003Cp>Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: Alright. That feels good. Those are the 3 required. At some point here's a question. Here's a question for you.\u003C\u002Fp>\n\u003Cp>Alright. So let's say at some point down the road, I want to have a field that is, if choice is 1 through 9, show small or, like, show another type of text. Is that something that's doable through here?\u003C\u002Fp>\n\u003Cp>Speaker 1: Like, what are you gonna show?\u003C\u002Fp>\n\u003Cp>Speaker 0: Like, if I wanted to Small,\u003C\u002Fp>\n\u003Cp>Speaker 1: like, you are small.\u003C\u002Fp>\n\u003Cp>Speaker 0: Like Yeah. Like Yeah. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: Like, that feels unfair. Like, yeah, I did. Like, III didn't have a team larger than 9.\u003C\u002Fp>\n\u003Cp>Speaker 0: Let's say, I said that.\u003C\u002Fp>\n\u003Cp>Speaker 1: Partner program. Hey. You are small.\u003C\u002Fp>\n\u003Cp>Speaker 0: You're just like a shrimp. Alright. So instead of that, let's say internally, we wanna have some, like, normalization and, like, it when you're just talking about internally or showing, like, we have, we have 10, 1 through 9 agencies. It's kinda confusing as opposed to, like, we have 10 small agencies or agencies on the small side.\u003C\u002Fp>\n\u003Cp>Speaker 1: I so I mean, you could you could, like, for the values, like, if you really wanted small, you could. But, like, you know, my question to you would be, like, what does small mean? Right? So, like like, I I think what you've got here is fine. You might you say, like, 1 to 9 employees or something.\u003C\u002Fp>\n\u003Cp>You know? But, like, if you wanted to, like, do something based on this data, it it depends on where you're doing it. Right? If I'm doing it on the front end, like, I could just add that to my front end logic. Like, hey.\u003C\u002Fp>\n\u003Cp>If this is a small agency or if the value is 1 to 9, we show a badge that says, hey. This is a freelancer, or this is a small agency, or boutique, or something like that. You know, that always sounds nicer than small boutique. Bespoke.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Boutique. It's not the size of the dog, Bryant. It's the fight in the dog.\u003C\u002Fp>\n\u003Cp>Speaker 1: There you go. Yeah. So you can certainly do something like that. Or if you wanted to, like, gather some additional information, like, if if they were, like, a a larger company, if you needed, like, some type of like, hey. We need a a VAT number or something.\u003C\u002Fp>\n\u003Cp>You can set up conditional fields for that as well if you need to.\u003C\u002Fp>\n\u003Cp>Speaker 0: Okay. Cool. Sorry to digress. But, yeah, good to know. Yeah.\u003C\u002Fp>\n\u003Cp>We we can do that down the road. Last thing here is we wanna showcase. So we're we want them to be able to upload a couple of their projects that they've completed with Directus. So would we use files for that? Multiple uploads?\u003C\u002Fp>\n\u003Cp>The idea is gonna be the deliverable, I guess, is gonna be we want them to upload\u003C\u002Fp>\n\u003Cp>Speaker 1: Are they gonna is it just images, or are they describing the projects and, like, talking about them?\u003C\u002Fp>\n\u003Cp>Speaker 0: It would probably be screenshots with some a little bit of text about what the project was. So a little bit of, like, descriptor. So maybe treating that like a caption to the image. I'm not really quite sure the best way to deliver it unless it's a different out outside of this initial form where they would have a different form where they could upload all this project information.\u003C\u002Fp>\n\u003Cp>Speaker 1: This is a relationship in my mind. Because 1 of the other stated goals that I heard, when we were initially putting this together was we wanna have a page, that showcases these projects. Yeah. So to me, that would be, like, a separate table, separate collection called projects, partner projects, could just be projects. I don't think we've got a I don't think we have a project table in here right now.\u003C\u002Fp>\n\u003Cp>We have what do we have? We have a project management folder. Yeah. So you're you're you're clear. Right?\u003C\u002Fp>\n\u003Cp>Speaker 0: Mhmm.\u003C\u002Fp>\n\u003Cp>Speaker 1: What are you gonna do now? Now that you know the way forward.\u003C\u002Fp>\n\u003Cp>Speaker 0: Something to something. I I don't know the difference between many to many or 1 of many, but I feel like it's 1 of these. 1 to many, I would assume. Because you're gonna have this 1 record attached to many different projects. Or is it the other way around?\u003C\u002Fp>\n\u003Cp>Speaker 1: So in this case, you can't have the same project belonging to 2 different agencies. Right. So it would be a 1 to many in this case because you're on the agency. Mhmm. 1 agency could have many projects.\u003C\u002Fp>\n\u003Cp>Now if you were, like, inside of a a project, it would be reversed. It would be, many to 1.\u003C\u002Fp>\n\u003Cp>Speaker 0: Okay.\u003C\u002Fp>\n\u003Cp>Speaker 1: I I would just call it projects. Projects. Killing me. We don't have we don't have, we don't even have a project stable. So that's gonna be fun as well.\u003C\u002Fp>\n\u003Cp>Right? So whenever you're creating these relationships, you have to have a related collection, which is basically just a table in the database underlying that.\u003C\u002Fp>\n\u003Cp>Speaker 0: Okay. Cool. Great. So that'll be what I'm thinking is for this show, we'll split it up into, like, a few different episodes because I've we've we're coming up on an hour here, having just built, like, this initial form. So we'll do, like, the next part.\u003C\u002Fp>\n\u003Cp>Let's set up those the showcase pages and potentially get into, like, role based access control a little bit. But, yeah. Is it Don't\u003C\u002Fp>\n\u003Cp>Speaker 1: leave me hanging here, bruh. Finish this guy out.\u003C\u002Fp>\n\u003Cp>Speaker 0: I can't. I'm unable to.\u003C\u002Fp>\n\u003Cp>Speaker 1: It's because you get to pick a related collection. Oh, this is the cliffhanger.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. This is where, you know, the end of every ER show, there's, like, something that happens. A helicopter explosion. This is our helicopter explosion. But, to move forward here on the next episode, I will have created just a simple projects collection with no data in it, and then this will be saved to that collection because I'll pick it here.\u003C\u002Fp>\n\u003Cp>Right?\u003C\u002Fp>\n\u003Cp>Speaker 1: You can actually create it right here.\u003C\u002Fp>\n\u003Cp>Speaker 0: Can I?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. In like 2 seconds. So where you have the related collection, it's it's 1 of the required fields. Those are the ones that have the stars beside them. Yes.\u003C\u002Fp>\n\u003Cp>Speaker 0: Stars Alright. Equal required. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: Alright. It it doesn't show up in the drop down, but what you could do, just type in projects. Alright? Now you're gonna need a foreign key. A foreign key is in that other table.\u003C\u002Fp>\n\u003Cp>How does it link back? Right? So that would be your partner, Or that's what I would call it anyway. Like, what is the field gonna be called inside the projects table, inside that projects collection?\u003C\u002Fp>\n\u003Cp>Speaker 0: That will\u003C\u002Fp>\n\u003Cp>Speaker 1: Is gonna be the part.\u003C\u002Fp>\n\u003Cp>Speaker 0: To this table? Okay.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yep. Now this is it's not apparent what's happening here. So what are you gonna do? Go to Advanced Field Creation Mode, and go to the Relationship tab. Alright.\u003C\u002Fp>\n\u003Cp>So do you see down at the bottom what's happening here?\u003C\u002Fp>\n\u003Cp>Speaker 0: Problem that we created within your data model. So they're gonna create projects and then the identifier for that project of, like, where it ties back to.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yep.\u003C\u002Fp>\n\u003Cp>Speaker 0: Okay. I mean, that's, I think, a great stopping point for this first part 1. We didn't know it was gonna split into parts, so I'm cool with that. This feels good. Now everything auto saves was 1 of my favorite features ever.\u003C\u002Fp>\n\u003Cp>If I go to content right here, agency partners, and then this is kind of the base thing that they'll see. Obviously, we can tweak this a little bit. But, yeah, everything we've set up is here. Great. Technically, I'm found.\u003C\u002Fp>\n\u003Cp>Speaker 1: I don't know. I I think you're still in the yeah. Okay. It's like you started moving towards the target there. Like, you're still, like, a 100 clicks off.\u003C\u002Fp>\n\u003Cp>Where would you be for this?\u003C\u002Fp>\n\u003Cp>Speaker 0: I've left the trials, and I'm officially in the tribulations. So, we're making our way there. But awesome. Bryant, I appreciate you joining, helping guide me, shepherd me through this, you know, data forest.\u003C\u002Fp>\n\u003Cp>Speaker 1: Data forest. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: Next episode, we're gonna tackle setting up the showcase pages, and we will start to get into role based access control for those partners to access this stuff. Thanks everybody for tuning in. Looking forward to the next 1, next episode of Technically, I'm Lost.\u003C\u002Fp>","Hey, everyone. I'm Matt here with the Directus Marketing team. I am joined by my friend and confidant for this call, Bryant Gillespie. Bryant, how are you? I'm awesome. Great. Yeah. I feel like I don't have to introduce you because you're, like, on every show here. But, you know? I I appreciate the intro, man. I you know? Like, I it's always nice when people pretend not to do me. My wife does that all the time. You're just too famous around these parts. I get it. I get it. But yeah. So Bryant was literally just asking because I roped him in right before this. What are we doing again, and what is the concept for this show? So as a person on the direct marketing team, 1 of the few here that is actually very, nontechnical, I sit more on the marketing side of things. I just am trying to learn how to become more technical in my role and learning how to use Directus and things like that. So, concept for this show, technically, I'm lost, is gonna be me kinda navigating a lot of these trials and tribulations that it comes with being a nontechnical person that's trying to learn how to actually use technology and code and direct us to the product itself, and I'll be doing so with help from guests like mister Bryant here. We'll be building various things that we actually have to use internally, that we have coming up, and I figured why not delay the timeline and try to build it myself. So here we are. Bryant, was that a pretty good breakdown of everything? III like it. I like the trials and tribulations part of it really got me. Yeah. I feel like, I don't know what to expect out of this, but I expect it to be painful. Like, there's a forecast of pain coming. That's about all I know. This will be fun if this is your version of 100 apps, 1 100 hours, where there's, like, a a public shaming portion of it, where it's like, oh, hey, like, hey. You start out really overconfident, or at least I do. And then, like, by the end of it, you're either, like, yeah, you do the Tiger Woods thing, or you're like, oh, wow. Really humbled at that point. Yeah. I'm gonna try not to turn this into 1 app 100 hours, but we'll see what happens. So what's on the dish for today, man? So today's episode, I'm gonna give you a lay of the land. So we have just revamped our partner program here at Directus. You know, in the past, we've we've tried to put things together. It's been very, loosey goosey, I think, is the technical term for it. So we've brought on a partner manager, Colton, who's heading that up. He's put a really awesome program in place. The next step for this and the evolution of it is to create a partner directory on our website. So figured directory, I don't know if you've built 1 of those on a 100 apps, a 100 hours yet, but, director seems like the perfect use case to build a directory directory of some sort. A couple things, and actually, what I'll do is I'll pull up Excalidraw because I know that's what you do. And Oh, yeah. The way that you do things. I'm I'm a, like, FigJam guy just because Oh, you're a FigJammer? Yeah. Hey. Excel I like Excel draw as well. To each their own. FigJam's pretty cool. I have nothing against FigJam. But, okay. So a couple of things. Partner directory. So it's gotta be accessible for our partners. We want partners to be able to update their own, instance, and put in things like their their part their agency name, main point of contact, where they're located, and, like, upload things like showcase work. So Okay. Obviously that name, point of contact, location, and Description, summary. Description slash summary, areas of expertise. What are you thinking as far as, like, location data? Location data. So that's probably what we'll filter on the front end is, you know, I'm thinking of the user experience they land on, like, direct us dot I o slash partners, and then they filter down based on a partner that's, you know, the general location because I like to support local. So, if I was looking for an agency, I would look for someone here in the East Coast of the United States. So I think by country, and then they can just drill down from there. But I would say, like, country top level. Okay. Areas of expertise is interesting because I'm trying to think of, like I guess it would be like they wanna like, is it, like, a website or is it, like, app development? Or is it more granular than that, do you think? Like, like, actually, like, flows and insights and, like, direct us things like that? If for an agency, I would think, like, areas of expertise to me is like, hey. Like, what do we specialize in? Is it, like, web development? Is it mobile app development? Is it, UI, UX? Is it all of it, I guess? At least that's the way that I see a lot of these things handled. Right? At other companies or or other spots online, I guess. That makes sense. That makes sense. Cool. Trying to think of what else. We'll have a showcase area where people can upload upload projects. You're probably gonna want a logo. Yes. Logo. And then the I always hesitate to, like, throw, like, pricing, if we've got some, like, freelancers in there, if we've got some, like, freelancers in there, you know, they're, like, stacking them up directly with an agency that has a 100 developers is not is not fair to either 1 of them. Yeah. Yeah. I think I have to figure that out. Maybe it's like a pick list with, like like, 0 to not 0. It wouldn't be a agency. But 1 to 9, 10 to 49. Oh, he's getting it very granular. I like it. Yeah. I'll say 10 to 99, and then a 100 plus. Seems like a pretty good 3 to 3. Small, medium, large. What else do you think? Actually, this feels like a pretty good, v 1. This is a this is a good jumping off point. Yes. V 1 is my favorite and not because I'm lazy. It's just because it's good to just get something out there, and also because I'm sort of lazy. So, feels like a good scope of work. So I like it. First steps. I'm I'm just monitoring the environment. That's, I Oh, yeah. Yeah. Yeah. We we've stepped up the difficulty level for this episode because I've got 3 kiddos downstairs who've been sick all week. Fully expect 1 of them to bust in the office. And so that might make this more interesting. You know, that's technically, I'm lost. We roll with I that could be could be set for me as well at this point. Like, technically, parental wise, I am lost right now. Technically, I am totally lost. Alright. Cool. Alright. So next step, hopping into Directus. So for anybody that doesn't know, we use Directus internally for basically everything on the marketing side. So our entire website's powered. These are all our pages here. We use it for our docs, for publishing and and maintaining. Also, like, when you leave reviews, we capture that stuff. We have our direct to stop pizza. That's in here too. This is the main place where we can make changes to all that stuff, the the interactive demo. We capture metrics, and we capture key marketing metrics in here. It's my weekly dashboard that I put together. So the partner portal is gonna fit nicely in here because it gives us 1 place to access everything, and we'll just limit things with roles and permissions at some point, in this episode and all of this stuff. But yeah. So, Bryant, starting from scratch with this partner directory, where do you suggest we start? Where do you well, like, you've already got a, like, start of a data model kind of mapped out in a scale draw. Like Yeah. Why wouldn't we start there? I think that is a great place to start. So, again, I'm just completely, doing this as a nontechnical person. I wanna caveat that. I'll probably say it a 100 times in this episode, but I'm, yeah, starting from scratch here. So what I assume, and just correct me if I'm making a mistake here, we're gonna wanna create a data schema, which we will do inside of collections, inside of settings. Yeah. We will create a new collection. Now, you get to encounter the hardest part of development, which is naming things. Naming conventions or So, hey, like, hey, what are we gonna call this new collection? Like, is it gonna be Do we call it is it so that's the thing. Is it, like, the high level thing? Could we just call it partner directory and then all of the fields would be within that? Or do we have to get, like so I like, typically, like, the way that I name my tables, and this is I say this in every video. It's 1 of my favorite features inside Directus is the ability to, like, translate that. Right? So, usually, when I name tables, it is like a what's the object? And then I I usually end up with, like, a plural. So it's pages or posts or partners or agency partners or, you know, trying to think on, like, the partner side, we've got agencies, we've also got tech partners. Do those live in the same directory? And they're you know, do they have a lot of the same characteristics as as far as, like, the data or totally different thing? It's a great point. So I think at some point, as we start to grow out the tech partner side of things, we'll have kind of the same concept for them, and we'll have, like, integrations pages maybe or, like, you know, something like that. We wouldn't wanna conflate the 2. So maybe it is just agency partners for this because that's what the hyperfocus is on. So are all you think all partners would be considered agencies or if there's any, like Again, right? That's the and now you're waiting into developer land where it's like, hey, I I know what I wanna do with this, but what do I name the thing? Because it's gonna confuse somebody later. I've been in developer land for 10 minutes, and I'm already not liking it. So I don't know I don't know how you guys do this. But I it let's roll with it. And, you know, again, like, within the interface, we can rename it. Now before you move on here, the other thing I want you to do, and this is mostly just out of habit for me, when I'm building all of these, like, direct us plus starter kits. The primary key field, set the type on that to UUID. The type not no. No. No. No. No. No. No. No. No. No. Slow down, cowboy. There you go. Let's go. It's the type 2 generated UUID. Yeah. Here. What the hell? I mean, what the heck do any of these mean? This is where I get lost, and I don't wanna turn this into, like, a long, like, discussion. So, basically, every table in the database has to have a or every row within a table. Right? So when you're adding this collection, you're creating a table inside the database called agency partners. Every row within that has to have a unique identifier so that the database knows what that is and how to reference it. I got you. So you can you can basically generate that identifier either, like, a auto incremented integer, which is, like, 1, 2, 3, 4, 5, 6, 7. When you are I I doubt we'll use this for a starter kit. But, when you try to transfer those IDs to a different database, like, you've gotta reset, like, the the increment count on that database. Okay. Our things get squirrelly. So using a UUID prevents that because it's just a random string of 36 characters. Okay. I'm tempted to go big or go home, but I will go give you ID. Oh, yes. Now you could also do the manually entered string, but, like, like, I I see people will use that for slug a lot. But the problem there is you can't change that after the fact. So if somebody renames their agency, that primary key would still be the same thing because you can't change that. Okay. So if it's Matt's agency and then you sell it, it becomes my agency. Like, I'm stuck with Matt's agency. Yeah. Should've bought it. Okay. Your UID. That makes sense now. So, everything will have a automatically generated, identifier that if we decide to pull all the stuff out of Directus at some point, it'll follow it. If we wanna integrate it with something like a tool like clay, when we pull that in, it'll be able to keep everything in rows nice and clean. Nice and easy. I'm learning. Technically, I'm lost. TIL today. Learning. Yeah. There you go. Cool. I know there's a lot of things with, optional fields here, but, you know Best thing to do is just check them all. You could delete them later if you don't need them. Right? So these are just, like, the helpers within the system. Nice. Okay. Like, they created whenever you create a new record, it will populate the timestamp for you. Automatic? Same thing. Yep. Genius. I created a collection, like, a little while ago before this episode, and I forgot to check that. And I'm, like, oh, crap. It's not generating. You could you could do it. It's just easier to check the boxes and then delete it if you don't need it. Alright. Beautiful. Alright. So we've got our agency partners data model. Now, next step for me is gonna be to pick a color because I'm in marketing. So I'll just, do this nice little Colors and icons, baby. Yep. There's a I hate this shirt search. Oh, handshake. I found 1. Nice. Alright. The search always busts for me, with those icons. Me and icons have a lot of hate. I the Google naming team on those. Guys, hit me up. I'd like to make you guys need some help. Yeah. But, anyways, next steps. We got our handshake. We got our nice green color. Green is great. I love Luigi. So we're all there. Good there. Now the next step is gonna be creating those fields that we had mapped out earlier. Correct? Yeah, baby. You got it. Alright. So, this is 1 of my favorite parts of directors actually because of these nice little visualizations that give you a a clear preview of what you're getting into. Let's see. So input seems like the 1 that's generally used. For just like a string of text? Like, what do what what are we starting with? Like, the agency name? Yeah. So we're gonna start with name. Actually, you know what I'm gonna do? A little arc magic here. Yeah. Alright. So we've got our window. Alright. So side by side here. I think the important thing to start with is gonna be, obviously, the name of the agency. So input, just a string of text, name, unless we have a specific name field here, which I don't think we would because it seems very unique. Nope. You got it, boss. So question I had here was, should we, in the grand scheme of, like, all of the other fields and collections and things inside of Directus, when I'm naming things, should the naming convention be, like, instead of name, should it be, like, agency underscore name, or does it really matter in the grand scheme of everything else? This is the the fun. Yeah. I would probably just call it name because, like, within this, you're not gonna have, like, a lot of related items. Right? Like, you're probably gonna have some projects to showcase, you know. I guess, in that sense, you could have, like, a partner name and a project name. I only, like, start adding prefixes when it really starts to get confusing. Like, if I've got a bunch of nested relationships that I'm going to be, like, dealing with all that data at once. So, like, the name of a page and, like, the name of a block and, like, the name of a button and stuff like that. Like, at that point, you might wanna, like, start adding prefixes so that you it doesn't get confusing. Or or, like, if you think of, like, organizations and contacts, you know, that might be organization name and then, like, contact name type of convention. Yeah. Maybe I'll because we're gonna have a point of contact in here, so they'll wanna enter their name. So maybe I just I'll I'll just go with agency name here. Or it could be partner name. Partner name. That's right. Partner name. String, we wanna keep it empty. And go to fill it out. Obviously, that's gonna have to be required. I don't think any placeholder. If I wanna get fancy, I can add an icon. I'll hold off on that. Is there anything in the advanced field creation mode that I should be aware of? It doesn't seem like it's that advanced other than, like, maximizing the length of what they'll input. Unique? I'm not worried about that 1. Yeah. So the name is probably not yeah. You're not really concerned about a unique name. The advanced field settings just gives you, like, more control over it. And you can always go back in later, like, after you create this and edit that. Right? When we get into, like, how you're gonna reference this on the front end. Right? That would probably be, like, the next thing that I would tackle as well as, like, a slug of, like, hey. We we've got this random string of characters, like, in the URL. We don't wanna use that. We wanna use, like, a slug. So Okay. That that's where we'll get into advanced. And we don't wanna allow a null value. Yeah. It it like, required is probably gonna take care of that. Like, that is gonna be on, like, the database side as well. Okay. Like, if you had any existing records in this, I probably wouldn't let you, like, check that or uncheck that box because, you know, like, you might have records that don't have a value for that specific field, like, if that field already existed. Right. Okay. Cool. Today, I'm in the weeds. Not sure what the acronym is for that 1. But I do. Yeah. So we're gonna keep editing. Alright. So your next field, I vote for slug. Slug. That is gonna be a string. Having trouble with this. You're gonna you're arching yourself. Yeah. Time for a page refresh. Page refresh. Save. I lost it. Found it. Okay. Slug. Alright. It's gonna be an input. Okay. Same thing. The key of slug. Okay. Yeah. Yeah. I would just call it slug. Be fine. Alright. So now then you have a a go into the Advanced Field Creation Mode. Mhmm. And we need to make sure this is URL safe. So you are going to go to the interface section, and there is a slugify option. And what does that do? Prefix it? Basically, like, anytime you try to add a space, it's gonna add a hyphen. Or if you try to use any, like, special characters that aren't allowed in a URL, it's not gonna allow you to input those. Nice. Okay. Very cool. And then in that case, you also are probably gonna want this to be unique. Right? Because, on the front end, this is gonna be part of the URL, and it's gonna be the unique identifier that we're 1 of the unique identifiers we're using to fetch that data for the agency. So, yeah, you could certainly set it to be required if you want to, or we could have, like, a flow that generates the slug. But on the, the schema page or the schema tab, you set that value to unique. Feeling good about it. Alright. Bam. Now question. So we'll have agency name. We'll have their website. When it comes to adding, like it's just kind of jarring, I think, when you're filling out a form, when it's like these 2 questions and then 3 completely unrelated questions, which in this case would be like, here's about your agency. Oh, tell me about yourself. Let's talk about, like, something else. Is there a good way to maybe split up this? Like, use, like, maybe add in a header or some sort of field that breaks it up, or is it just, like, best best use case, best scenarios? Just like you know, what I like to do in my forms is just make them all, like, half width. So you got, like, this, and then just add as many as you can in a row so that it's really confusing. Love it. No. There there's there's lots of great settings for that. If you open back up the create field, like, the ones that you're looking for, like, we call them alias fields, which basically, like, it's not really storing any, like, specific data inside the the actual database. Right? Mhmm. It is just controlling, like, the layout within the form. So those are gonna be under the group section. Group. Groups. Presentation. So you see there's a couple there. You have, like, presentation wise, there's, like, a divider. You can add some button links. You can add a notice to the form, and you can make that conditional. But if you wanna group related fields together, you got, like, the group options. So depends on how you want to slice it. Most of the time, I end up using, like, the detail group, which is basically, like, I could throw 35 group or 35 fields in that and hide them all with, like, a collapsible accordion type thing. The accordion is basically 1 field at a time, pops open. So, okay. Gotcha. Yeah. Most of the time, I end up using detail group. Nice. I'm gonna try that. So then I'll call this, contact. Yeah. And, usually, like, this is just like an OCD thing for me. What I end up doing is, like, a like, underscore details or group or, like, put, like, a meta prefix underneath it just so, like, I know, like, this field is just for, like, grouping. I like it. Point of contact group. So, so we're gonna nest things underneath this group, or is it the once we hit save Yeah. Just hit save. Do we can come back. Question on this. Would we want it required, or do we require the separate ones? No. You don't have to require that 1. I'm not even actually sure what what that would be. Feels dangerous. That might be a good feature request of, like, hey. We should probably do something within that. Alright. So now, basically, what you got, you've got a container within the form that you can drag fields into. But you gotta create some fields first for the point of contact. Great. First name, last name, any like, just 2 separate inputs side by side, I guess. I yeah. Are we going to use is there a situation where for your point of contact, you're gonna, like, use the just the first name? I would say if we automate emails from Directus when they register their company, we're gonna wanna obviously personalize it, and we don't wanna say, hey, Bryant Gillespie. Thanks for signing up. It'd be much more casual to be like, hey, Bryant. So probably in that case, would it make sense to split up the 2? I I would break them up in that case. Now the other consideration here is that you're gonna have a user that logs into this as well. And, like, are you gonna send that email to the user from that agency? Or, like, who the the designated point of contact is? I would think whoever the designated point of contact is. Okay. So, like, as they they will give them ability to add other people under their their agency. And then when we can trigger notifications, like, when, hey, so and so from your team has signed up or maybe we put into delay that's like, hey. You sent an invite. They never signed up. Go, you know, strangle them. Not that Go strangle them. Yeah. I got you. I got you. I I so and, like, POC to me, like, proof of concept, not point of contact. That's just because I'm building. So I would just call this, like, either, like, contact first name. Contact first name. Or you could probably I mean, yeah. That's probably fine. Cool. Like it. Require. Save. This is the boring part of the show where I just fill in these things. Nat, you might wanna fast forward this part. Get, like, some Benny Hill or something playing in the back. Right. Alright. So here's a question. Let's say I wanna bump these 2 together. So I click here. Obviously, half width. I might be able to figure this 1 out myself, Bryant. I don't think You got it, boss. You got it. Alright. So You can Now yeah. Go ahead and add your other field there. What are you you're gonna have, like, a point of contact email or contact email? Point of contact email will be just another input field? Is there any special Yeah. There is there's not like, as far as the database is concerned, it's just a string of text. Okay. You could go in and, like, apply some validation to it. You you can use, like, regex. We'll we'll leave that 1 for another call. I've had my time I've served my time in regex. So let's let's say I wanna add a validation. Right? So, validation here, I wanna add, like it has to have an at, obviously, if it's an email. Just a little quick little validation. I would add a filter contact email. And this is must, like Contains. Yeah. Contains just a at symbol? Yeah. It has to contain an at symbol somewhere. Now I didn't like That's The the other, like, fun bit of this, right, is what if they put 2 at symbols. Right? That your validation there would pass. So this is where, like, you could reach for reg x, but, you know, again, that's just we could spend, like, 45 minutes to an hour just like, what is reg x? Yeah. Is this, do you think this is something that can be well, not think, but once I create these fields, are they set in stone? And what I have to, like so the only thing that you can't change here is gonna be, like, the keys. So, like, the key of the field that you set on the schema. Like, once you commit this, or that can't go back and change the key of that field because there's a lot of references inside the database. Right. Okay. Now if I've got database access, I could, but I I wouldn't put that evil on you. Yeah. We'll try it without. Alright. Cool. So that makes sense. I can change that validation later in that case. So it didn't require it. Edit field, required. So I can go in. Look at that. You can do all that. Reach. Okay. Cool. So I feel like that's good for the point of contact. The next thing is gonna be location. Yeah. So for this section, and just because I saw this divider thing, and I'm just gonna add that in there. I'm just gonna save a divider. Location. Now I know we have, like, a map, geospatial fields, things like that. I don't know if it makes sense to have the map as your input for the location. But if we're doing it by country, it would make sense to do it as a pick list or a drop down. Right? Yeah. Or you could just have it as an input as well. It depends on, like, the like, if you wanna normalize it. Right? So So on the front end yeah. So on the front end if we're gonna do it on the front end, like, thinking of the filter ability is it'll be a drop down for them. Like, I'm imagining, like, a bar at the top, and then they can just drop down, list of countries, pick it, and then it'll propagate all the ones that have that. As an input, would that if somebody misspells, if somebody enters the USA as opposed to, like, United States or America, all of those little, like, side use cases cases, it would run into an issue there. We'd have to figure a way to normalize them on the back end. Correct. Yeah. It'd be a pain in the ass, basically. Technically speaking, that would be a pain in the ass. Thank you, doctor. Drop down? You could. You could do a drop down. Like, the the trouble here, right, is you gotta get a list of all the country codes or all the countries that that we have partners in if you're gonna do a drop down. Right? Because I got it. Like, you you could oh, you've got it. Well, then, yeah, go for it, dude. Is this the same or, like, GitHub country list? And there's a plain text list of countries that you can copy and paste, from directly from GitHub. Woah. They also have it in a JSON format, but with the ISO code. But Alright. So, like, underneath the hood, when you are setting up that drop down feel, like, if you wanna click on it, those like, the choices there. So, yeah, if you just call this you might as well just call it country. Right? So we'll give it a key of country. The choices that you've got there, if you go in and you just add 1, right, You've got a text and a value. Right. So the text is gonna be what's displayed inside the application, and then, like, the value here could either be, like, the same thing or or the probably the way that I would do it would be, like, the the 2 digit country code, like, the ISO standard or whatever that is. I'm not sure what that is for Afghanistan. Yeah. I'm gonna go with the easier 1 here. Yeah. And I would honestly, it'd probably be, Yeah. I don't know. What's your does your GitHub data have it in, like, all caps or lowercase? I will share the screen. Actually, I'll pull it up here. Cool. Oh, yeah. They're all uppercase. So all uppercase, and then they've got somebody did the due diligence of typing in the things down here in a JSON format. So what do you think is the best way to go about this? I would grab the JSON data. You if you got Versus code? I do. I do, actually. Okay. Alright. And we're gonna make a giant drop down here. Like, the the other thing that we could do, there's a there's an auto complete option where you can call an API, but I don't know I don't have a good country code API right off the the top to get just like the the ISO codes, so it'd be fine. Open up Versus Code. Alright. I gotta change my streaming here. Getting me all twisted. Versus Code. Alright. So copy that list. Create a new file here. Copy your list of the data from GitHub. Paste the JSON list that was created? Alright. This is a long list. Yeah. Let's, alright. It it gets more interesting when you, like, hey. Are you gonna reference this again somewhere else? And then at that point, you might as well add it as a table in the database and reference it that way. So you could use it in other collections without having to either copy the field or maintain this thing. Alright. So what are you gonna do here? Like, basically, this is not in the correct format for Directus. Right? We've got an ISO code, and we've got the country. Directus, as far as like your choices for the drop down, is expecting a text and a value. So this what are you gonna do here? You are going to, like, highlight where it says ISO code, and hit command f or you're on a Mac, so it should be command so just pick 1 of those those values. Go over there where it says ISO code. Just highlight that part. Mhmm. 1 second. I think this list might be wrong, actually, because I don't see will the USB enter a different name? United States. I'm not finding it. Do you is it is your search case sensitive? Is it? I don't know. No. No results. America only turns up. Samoa Americana. Yeah. This is wrong. This is the hazard. We're using GitHub. I'm gonna go ahead and just use this standard list that this person didn't falsify because this 1 looks right, which is just the list of countries, not in a JSON format. Okay, boss man. Alright. What do you think? So, alright. This is gonna be the wizardry on the keyboard. Alright. So select all, command a. Mhmm. Alright. Now hit command right. Or is it shift command right? No. I I don't know how you got yours set up. Hold shift and do command right. Shift command right. Okay. Yeah. We don't have you set up right. If you have this list, is it if I select all yeah. It's not we need to get you to split this into lines. Is it Command P? Do Command P. Is that the Toolbar? Okay. Alright. Now do a greater than symbol, and search for, like, split into lines. What is it? Split? Hang on. I'll tell you. I'm trying it now. Split join tag. Print that lines, join lines, expand line selection. Insert line? No. Insert line below? No. It should be, what is the I know the command on mine. Add cursor. Is it add cursor? Endo. Line. Add cursors to line ends is the name of it. Add cursor to line end. Yep. Alright. So what that's gonna do. Shift option. Oh, yeah. Yeah. I've I've got a different command. Alright. So now, you see you got, like, a cursor on all of those. Woah. It's pretty pretty dope. Right? Alright. So now what you're gonna do, now you should be able to hold command and shift and left. That's gonna select all those individual yeah. Go left. Alright. Alright. Selected. So now you get them all individually. Now you're gonna pop the, quotation mark. Just put in a quotation mark. Yep. Shift quotation. Yeah. I mean, not shift quotation, but just quotation mark. Alright? Yeah. Now you're gonna do command left. Command left. That'll get you to the start of the line. Now you're gonna add a left code bracket or mustache syntax there. Left. Yep. Alright. Do, what are we looking for? We're looking for a value. Mhmm. So you're gonna do quotation mark value. Type type in value? Yep. This is gonna be proper JSON now. Oh, boy. Quotation mark. Quotation mark, comma, or no. Not comma. I'm sorry. It's a, colon. There you go. Alright. Now go to the end of the line. Command right. Yep. Command right. Yep. Add your What's that? Is your right mustache. And then you are going to add a comma. Alright. Now, go hit command up. And because this is an array of values, we gotta use the, I don't even know what that's called. Bracket? What is that called? It's it's not it's it's not the shift key. Yep. And then I'll do this. At the very bottom, you gotta add another 1. And then it's gonna be the closed. This is an array of values. There you go. And then on that last 1, what you're gonna do, the very last value there, remove that comma. And now you have a valid JSON array that you can copy that whole thing. The formatting doesn't matter. How do I copy? Just kidding. It's I can't help you with that. This is amazing, by the way. I just learned how to do JSON. TIO. Yeah. You you learned a couple things. Right? You learned, hey. Like, the some of the keyboard shortcuts for Versus Code, especially when you're trying to do reformatting and stuff like this. Super helpful for that. I'll never remember him. But, so I'm gonna discard this because this was what we were trying to do earlier. But, am I on the wrong page? On the wrong page. So Yeah. Nobody could see what you're doing. I didn't mean what we just did. I meant what and yeah. That would've been terrible. Yeah. I'm gonna jump through the computer and strangle you at that point. Show me all the shortcuts again. Okay. So we've got our JSON copied. Yep. We're using this drop down unless you So where it says choices. Mhmm. Paste your JSON. So click where it says choices. Just the header. There you go. Paste raw value or yeah. You could hit edit raw value and paste it in there. Done? Done. Done. Done. Done. Except we forgot to add text to all of those, do we? Did we? Oh, from the front end. Because it won't show the presentation of it. Right? We forgot. Now what do you do? Should I restart? I think we're we're really, like, pretty fast. Right? We should be able to do that pretty fast. Or wait. Can I just take the no? You could just copy the values. Yeah. For now, just save it. It should let you save it. Yeah. And I don't know if it's going to throw an error on. Yeah. I'm actually not sure how the drop down will behave. It'll let you save it for sure. But I I don't know if when you actually test it out, if it will show that value because it doesn't have the actual text to to do that with. Okay. We can we can look at it, though. We'll sort that out in a moment. Alright. What's next, man? So next. Yeah. It's driving me freaking crazy. The OCD part of him, like, the x designer in me. Like, you've you've got the point of contact group. Just drag those contact fields into that. Because, otherwise, they're not gonna display inside that group or or not be collapsible. How's this helping your OCD? Gotcha. That's that's why I call myself a recovering designer. Gotcha. Gotcha. Okay. Cool. So I think we're in a good spot here. I'm gonna add this quick, for, like, a description or something like that. Just use, like, a text area probably. Makes sense. Yeah. Agency description. Yeah. So, like, the in the database, it's gonna be stored as, like, text no matter what which you whether you use, like, a WYSIWYG editor or a text area. The difference with the text area is you're not gonna have any, like, rich text formatting. So it depends on if you need that. You know, you could have hey. This is where you go back to, like, modeling your data of, like, hey. Do we have, like, a short description? Like, if we're showing cards on a page, we probably have, like, a short description. And then maybe on, like, if you click into a partner, you might have, like, a longer section of content or something. That's a great point. So, yeah, this is actually gonna be structured as 2 pages. So we'll have the directory, which is this, gonna be the main thing. And then when they click into it, we'll have a separate page that is a little bit more and we'll have the WYSIWYG there. But this will just be like a short description of partner that'll be on the front. And honestly, like, I would probably limit this to a certain number of characters, setting a softer limit to I don't know. What do you think? 300 characters? What's the old Twitter thing? 250? You're asking the wrong guy. 170. Is that was that what it is? 270? 280. We'll set that. Yeah. Soft limit. So, hey, that soft limit that you just set there, that is only on the the UI. Right? Like, the there's no particular limit there on the the actual database. Okay. That's fine. I, no. Actually, it's not fine because they're That's where, like, the building this out in the database. Right? No. No. No. It's fine. Yeah. Hey. Like, because we're interacting through this, this layer, right, this Directus Studio, and through the Directus APIs, it's not a problem. Like, Directus will give us that validation. Great. If I wanted to enter validation. Let's say I just want to say in the field itself, by the way, only 280 characters. Placeholder would not be the 1 to do that because they'd have to, like, erase that. Right? Would there be a placeholder is basically just like, hey, what do I want? You know, like, a an extra hint for somebody, like, on a Maybe a note here. Like Yeah. A note is, like, a description below the field basically. Let's try that. Great. Feeling good about it. Description, areas of expertise, we'll probably wanna keep this limited to, the ones that we have so we can fill use those as filters as well at some point. We'll do that as a drop down, multiple because you're gonna have multiple special specializations, I guess. Yay? You and A? Yeah. Yeah. I mean I see it says JSON here. I always get Or Expertise. Yeah. It's a JSON. It's just gonna just the way it's being stored. Like, are we going to are you gonna allow them to filter based on those specializations or no? I think at some point, I think it would be country and specialization would be the 2 things that you're gonna that you're gonna be searching for when you land on, like, a directory page. Okay. So on that point, like, the the current operation, like, I cannot set I I can't query the data within that individual, like, JSON column. That should be coming in the future. As of right now, we don't have support for it. Okay. So at that point, then we need to look at a different way to do it, which would probably be, like, adding a database table for it. Or, what you can do do is just change the type to CSV, and that would allow us to get, like, a, hey, does this specialization field contain x? Okay. Because III think that's probably the extent of it. Right? Like, you're just gonna use it as a criteria for filtering this 1 thing. Probably, it should be fine. We'll do CSV. Nice. Okay. So then we'll do website, development. Website. Web web development. You developers. So cool. Why say long word when short word work better? Well, it could be like a web app. Alright. So these are these are the things that go through your mind, like the average developer, like, 100 times a day when you're building a project. If you see, like, a a puzzled look on their face, they're probably trying to figure out what to name the thing that they're trying to create. They know what the they already know what the thing looks like. It's all going up here, but it like, naming it is like, what do we call this? Number 1. The number 1 thing. Okay. Mobile application, mobile app. Is that fine? Can I shorten application to app? It What about that? It is. It's fine, man. Alright. If you you being a previous agency owner, you're probably the best 1 to ask for this. What are some other specializations you can think off the top of your head? Like UX, UI. Should I And you get When I'm doing the value here, do they have to be a specific like, option? It it doesn't. Like, it you know, like, technically, it could just be, like, UXUI. Like this without an underscore? It doesn't have to be. You see me start to sweat, like I'll do doing. Like, what whatever you do, it like, it's just gotta be normalized. Right? Like, you the most important part is, like, whatever convention you decide on, it's the same across the board, so you don't have major issues. Okay. How do you feel about the spaces between this? That's fine. I like it. Alright. Web development, mobile application. I should probably change that to apps. Mobile applications. UXUI. Digital transformation? That was that was the 1 that came up. Yeah. Digital transformations. Yeah. And and, like, technically, like, you could also like, do you get into, like, the specific frameworks here? You know? That's I I don't know. I would say, you know, you come in with you need a project. There's only a few there's the way executing the project, you can do it building in a reactor view or whatever, but the actual project itself is what I'm trying to zoom in here on. And I feel like the 4 of these would probably touch most of those because we're, you know, we're not gonna skew into, like, public relations or, like, digital marketing, whatever that means. But, yeah, it's like I'm I'm saying, like, hey, would you be, like, a yeah. We're like a a Laravel agency or, like, we are, you know, like a a React agency or, you know, those sorts of things. That's another consideration and probably not for this episode, but I would probably add that as another field here of like this these are the things we specialize in React view. Yeah. That could be, like, your expertise, I guess. I don't know. Yeah. Specialization in expertise. These feel good. I'll, I'll leave them as the for now, for the sake of this episode and update those later. Let's see. Just exit that. I was also a friend to you and and updated your country codes for you as well. Oh, thank you. Much appreciated. So for logo, image, or file? What do you think? Either 1. So, like, behind the scenes is basically the same. It's just like how you want it to display. So in this case, you probably wanna see, like, a nice image. We'll just call it logo. Yeah. That's fine. Partner logo. Yeah. So just use the image. Like, it it files there is gonna create a junction collection. So, like, if you need, like, a gallery, we would use that. But in this case, we just want a single logo to display. Nice. Okay. Feels good. We can update this file library route later. We'll have, like, an agency specific folder. Yeah. That's what I would do. Crop to fit. Anything to be aware of in the advanced field creation for this sort of thing? Like, images or anything? If you go into, like, display, like, you could control, like, how you want this to actually display. Right? So you wanna see, like, a tiny image preview, and then you can control, like, how it crops, basically. Okay. Cool. Partner logo. And then this last bit is gonna be actually, we'll do size, 2 more fields. So size, numbers, but it'll still be kind of a drop down situation, I'm assuming. So is it a is it like a are you storing a number, or is it like a it'll be a string? It'll be a Is it like 1 to 10 or, like, hey, like, we have a 100 or we have 56, we have 7. I don't wanna get, like, too specific because I'd rather group it into, like, small, medium, large because at some point, we can filter that on the front end too. Yep. I need the enterprise company. I'm an enterprise company, enterprise agency, yadayada. So drop down with Just hit me with the Seinfeld. Yeah. Seinfeld references into 2024. Let's go. Agency, partner size. Let's say partner size. Yeah. Yeah. I would get, like, team size or something. Right? Like, hey. That's the the other thing here is, like, do you need to, like, prefix each and every 1 of these fields or or just the ones that, like, could get confusing? Now you're making me sweat about naming conventions. I'm not gonna It's it's seriously like the hardest part. Not today, Satan. I'm gonna leave the the naming confessions away for for now. Let's do 109. That'll be small. For value, here we go. 1 underscore 9? No. Just just do 1 to just keep it the same for it. What about 5? Alright. No. Just keep it the same, like, 1 to 9. Just the exact same thing. Right. 19. 10 to 99. It it it's just a string of text. It doesn't really matter. It's a string of text, yet it's numbers. How's that for some Yeah. I get yeah. Confucius wisdom. Confucius. Yep. Yeah. Alright. That feels good. Those are the 3 required. At some point here's a question. Here's a question for you. Alright. So let's say at some point down the road, I want to have a field that is, if choice is 1 through 9, show small or, like, show another type of text. Is that something that's doable through here? Like, what are you gonna show? Like, if I wanted to Small, like, you are small. Like Yeah. Like Yeah. Yeah. Like, that feels unfair. Like, yeah, I did. Like, III didn't have a team larger than 9. Let's say, I said that. Partner program. Hey. You are small. You're just like a shrimp. Alright. So instead of that, let's say internally, we wanna have some, like, normalization and, like, it when you're just talking about internally or showing, like, we have, we have 10, 1 through 9 agencies. It's kinda confusing as opposed to, like, we have 10 small agencies or agencies on the small side. I so I mean, you could you could, like, for the values, like, if you really wanted small, you could. But, like, you know, my question to you would be, like, what does small mean? Right? So, like like, I I think what you've got here is fine. You might you say, like, 1 to 9 employees or something. You know? But, like, if you wanted to, like, do something based on this data, it it depends on where you're doing it. Right? If I'm doing it on the front end, like, I could just add that to my front end logic. Like, hey. If this is a small agency or if the value is 1 to 9, we show a badge that says, hey. This is a freelancer, or this is a small agency, or boutique, or something like that. You know, that always sounds nicer than small boutique. Bespoke. Yeah. Boutique. It's not the size of the dog, Bryant. It's the fight in the dog. There you go. Yeah. So you can certainly do something like that. Or if you wanted to, like, gather some additional information, like, if if they were, like, a a larger company, if you needed, like, some type of like, hey. We need a a VAT number or something. You can set up conditional fields for that as well if you need to. Okay. Cool. Sorry to digress. But, yeah, good to know. Yeah. We we can do that down the road. Last thing here is we wanna showcase. So we're we want them to be able to upload a couple of their projects that they've completed with Directus. So would we use files for that? Multiple uploads? The idea is gonna be the deliverable, I guess, is gonna be we want them to upload Are they gonna is it just images, or are they describing the projects and, like, talking about them? It would probably be screenshots with some a little bit of text about what the project was. So a little bit of, like, descriptor. So maybe treating that like a caption to the image. I'm not really quite sure the best way to deliver it unless it's a different out outside of this initial form where they would have a different form where they could upload all this project information. This is a relationship in my mind. Because 1 of the other stated goals that I heard, when we were initially putting this together was we wanna have a page, that showcases these projects. Yeah. So to me, that would be, like, a separate table, separate collection called projects, partner projects, could just be projects. I don't think we've got a I don't think we have a project table in here right now. We have what do we have? We have a project management folder. Yeah. So you're you're you're clear. Right? Mhmm. What are you gonna do now? Now that you know the way forward. Something to something. I I don't know the difference between many to many or 1 of many, but I feel like it's 1 of these. 1 to many, I would assume. Because you're gonna have this 1 record attached to many different projects. Or is it the other way around? So in this case, you can't have the same project belonging to 2 different agencies. Right. So it would be a 1 to many in this case because you're on the agency. Mhmm. 1 agency could have many projects. Now if you were, like, inside of a a project, it would be reversed. It would be, many to 1. Okay. I I would just call it projects. Projects. Killing me. We don't have we don't have, we don't even have a project stable. So that's gonna be fun as well. Right? So whenever you're creating these relationships, you have to have a related collection, which is basically just a table in the database underlying that. Okay. Cool. Great. So that'll be what I'm thinking is for this show, we'll split it up into, like, a few different episodes because I've we've we're coming up on an hour here, having just built, like, this initial form. So we'll do, like, the next part. Let's set up those the showcase pages and potentially get into, like, role based access control a little bit. But, yeah. Is it Don't leave me hanging here, bruh. Finish this guy out. I can't. I'm unable to. It's because you get to pick a related collection. Oh, this is the cliffhanger. Yeah. This is where, you know, the end of every ER show, there's, like, something that happens. A helicopter explosion. This is our helicopter explosion. But, to move forward here on the next episode, I will have created just a simple projects collection with no data in it, and then this will be saved to that collection because I'll pick it here. Right? You can actually create it right here. Can I? Yeah. In like 2 seconds. So where you have the related collection, it's it's 1 of the required fields. Those are the ones that have the stars beside them. Yes. Stars Alright. Equal required. Yeah. Alright. It it doesn't show up in the drop down, but what you could do, just type in projects. Alright? Now you're gonna need a foreign key. A foreign key is in that other table. How does it link back? Right? So that would be your partner, Or that's what I would call it anyway. Like, what is the field gonna be called inside the projects table, inside that projects collection? That will Is gonna be the part. To this table? Okay. Yep. Now this is it's not apparent what's happening here. So what are you gonna do? Go to Advanced Field Creation Mode, and go to the Relationship tab. Alright. So do you see down at the bottom what's happening here? Problem that we created within your data model. So they're gonna create projects and then the identifier for that project of, like, where it ties back to. Yep. Okay. I mean, that's, I think, a great stopping point for this first part 1. We didn't know it was gonna split into parts, so I'm cool with that. This feels good. Now everything auto saves was 1 of my favorite features ever. If I go to content right here, agency partners, and then this is kind of the base thing that they'll see. Obviously, we can tweak this a little bit. But, yeah, everything we've set up is here. Great. Technically, I'm found. I don't know. I I think you're still in the yeah. Okay. It's like you started moving towards the target there. Like, you're still, like, a 100 clicks off. Where would you be for this? I've left the trials, and I'm officially in the tribulations. So, we're making our way there. But awesome. Bryant, I appreciate you joining, helping guide me, shepherd me through this, you know, data forest. Data forest. Yeah. Next episode, we're gonna tackle setting up the showcase pages, and we will start to get into role based access control for those partners to access this stuff. Thanks everybody for tuning in. Looking forward to the next 1, next episode of Technically, I'm Lost.","4ffab5a0-1099-42af-81ae-43b227be5bf3",[2410,2411],"aa9aacc7-6347-4769-970f-57846da8709b","bb322b01-0cf8-4bc3-a219-a72d5b257c29",[],{"year":978,"number":329,"id":2359,"episodes":2414,"show":2415},[2361,2346],{"title":2363,"slug":2364},{"id":2417,"slug":2418,"vimeo_id":2419,"description":2420,"tile":2421,"length":2422,"resources":12,"people":12,"episode_number":264,"published":2423,"title":2424,"video_transcript_html":2425,"video_transcript_text":2426,"content":12,"seo":2427,"status":19,"episode_people":2428,"recommendations":2432,"season":2433},"d7bb19ee-8843-4543-aa2a-b37a66806eea","how-agencies-innovate","964703470","A discussion on how agencies source and assess technology, including the process of technology sourcing, evaluating technology for clients and addressing client concerns.","d2ea6ed3-0c8e-4257-b763-45fde6f07a76",69,"2024-07-02","How Agencies Innovate","\u003Cp>Speaker 0: Today, we are doing another Bridging Bytes episode, in the midst of our of our Leap Week, Leap Week 3, over at Directus. So an exciting time for multiple reasons, but, super super jazzed to be here, joined by Steven Majid and or Megitt, sorry, and Nemanja, Nićiforović. We are gonna be running through how agencies innovate, something very, you know, important to me. This is how I kinda got started with Directus, got started in general. So, yeah, we'll be spending next hour or so running through all things agencies, sort of the the primer, the the history of Directus, and then diving into all the, so the nuance of, you know, the overlap between agencies and how you choose your software, how you, you know, build out these projects, oftentimes very, very different projects for different clients.\u003C\u002Fp>\n\u003Cp>So I think it would probably make sense. Why don't we dive in with some introductions? I can go first just kinda lay you know, set the stage for, again, you know, how I got started with Directus, with my agency, that I started about 20, 20 some odd years ago, called Ranger Studio out of New York City. We were headquartered in Brooklyn. It was, truly, you know, going back to 1 of our previous bridging bytes episodes, it was the intersection of design and development.\u003C\u002Fp>\n\u003Cp>So we were lucky enough to do builds for some of the amazing, you know, marketing and design studios out there, you know, IDEO, Wolf Allens, Pentagram, Interbrand, etcetera. And we would work to basically be the hands, to build out their their projects for amazing clients. You know, we worked with Google and Snapchat, AOL, AT and T, the US government, just doing really white glove amazing projects, you know, thus Directus because we needed to find software that would actually fit the bill for all of those. But ran that studio for about 15 years, until it sort of transitioned into into Directus, formally. But, yeah, it was it was a very interesting you know, we'll we'll get into it, I think, more in this episode, but it's very interesting sourcing, technology, especially back then 20 years ago when technology was very different.\u003C\u002Fp>\n\u003Cp>There was far less to choose from. There wasn't as much information about what you were choosing. But that's really how I how I got started with Directus was building software that would make, our internal development more efficient, give us administrative tools we could hand off to those end customers, that wasn't strictly, a head you know, a CMS. They didn't even have have headless back then, or was it something completely custom? That's really what we ended up doing constantly.\u003C\u002Fp>\n\u003Cp>It was just building custom, solutions and back ends for every project, which is inefficient. So today, we're gonna be diving into how people do it today, the modern way of approaching this problem. So why don't we start off, Steven Meggitt, who is over from, if you wanna give give an intro. Sounds good. Awesome.\u003C\u002Fp>\n\u003Cp>Speaker 1: Sounds good. Ben, that was AAA good intro and backstory for, the genesis of of Directus. It seems that, you're not the only agency that started out that way. 37 signals, I think, comes to mind of a similar trajectory. So it's it's interesting to hear that.\u003C\u002Fp>\n\u003Cp>Pleasure to be here, Ben, Manja. Great to connect with you as well. Thanks for Directus for for having me on having us on. So I've been at for, close to 5 years now. I've spent my career designing, developing, and delivering really, really unexpectedly clever customer experiences that generate return on investment.\u003C\u002Fp>\n\u003Cp>I started in 2, 001 in my parents' basement, and then built a small but highly focused digital agency. Ben, you talked about the intersection of design and development. I like to add in 1 more element to that, where my agency was highly, highly focused on research, user experience and design and development. And that eventually led to me growing the the agency pretty organically and built a small but highly focused agency on those elements. And that led to an acquisition by in 2019, to help build out, what was then a, city centric design studio capability, which then was was then grown into a national practice.\u003C\u002Fp>\n\u003Cp>So now I'm a partner in the customer transformation practice, which brings together the work that I do in the studio, along with everything that I'm passionate about, which is helping organizations really build sustainable value and growth by making every touchpoint in the customer journey an exceptional 1.\u003C\u002Fp>\n\u003Cp>Speaker 0: That is awesome. Yeah. And, you know, 37 signals, obviously, that's a great sort of template. You know, was taking very close looks at them back in the day and, you know, III are they still around?\u003C\u002Fp>\n\u003Cp>Speaker 1: Oh, yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I I get I get whiffs of, like, you know, the there are, different product suites every once in a while, but I can never can never quite tell. Some people are a little bit more ephemeral. But, thank you so much, Steven. That's that's a great great intro.\u003C\u002Fp>\n\u003Cp>Super happy to have you here. Nemanja, do you wanna, run through a quick intro from over at Workinco?\u003C\u002Fp>\n\u003Cp>Speaker 2: Yep. Hi, everyone. My name is Nemanja, and I'm a technology partner at, Workinco. I've been with WorkingCo since December 2013, joined as developer number 4, and, it's been quite a ride. I've been, meddling with almost any technology project that we've ever worked on.\u003C\u002Fp>\n\u003Cp>From Virgin America that, made Working Code what it is, over through NBA, Disney, Google, CBS, more recently Contentful and Decathlon. As far as, my trajectory goes, I started coding, straight out of high school, like 2, 002. And I was always fascinated by hands on work, clean architecture, design patterns, and I was fortunate to have great mentors before Workingco and during Workingco. So 1 of the things that made me stick around for for as long as I have is that Working Group partners, in particular, are very hands on, so I get to do what I love every single day still.\u003C\u002Fp>\n\u003Cp>Speaker 0: Little known. I don't even think we talked about this, Nemanja, but I actually when I was when I moved to the city and I was figuring out exactly where I was gonna be, you know, working next, I actually went over to Work and Co. I knew some of the partners over there at the time, and applied. I was like, I would love to work here. This is an amazing, amazing shop.\u003C\u002Fp>\n\u003Cp>Things went a different direction. I ended up starting my own, my own agency, but I certainly what that is what you described is exactly what resonated with me is all the partners were super hands on. There was no, you know, okay. All these, you know, lower level junior designers are gonna go out and do the grunt work, etcetera. Everybody was really active in, you know, building out, the project.\u003C\u002Fp>\n\u003Cp>And, again, not just amazing clients, but amazing projects for those clients. Excellent. So that's basically intros. I think again, just to reiterate, today, we're gonna be really going through, the agency side, how we how we sourced different technologies, which, of course, can be very disparate. You've got, depending on the agency, some are a little bit more.\u003C\u002Fp>\n\u003Cp>This is, you know, lather and repeat exactly what we do, you know, bread and butter. And then others are more divergent, in in the types of project they take on. And, of course, that means that the tech that you use to build those projects can be all over the place. And so sourcing, assessing, you know, and making recommended, recommendations to those clients can be can be very tricky. There's a lot to sort of cover there.\u003C\u002Fp>\n\u003Cp>Maybe we kick it off. Steve, I'd love to hear a little bit more about how, you know, you, your team, and and navigates, you know, the process of recommending that that tech, for your clients. You know, especially given your role, that dual role as an auditor, and also a consultant.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. It's a tough 1. Or sometimes it can be a tough 1, especially if we audit the clients. So, generally speaking, you know, the the audit side of the house and the consulting side of the house remain separated. It's sort of the the only way that that it can work.\u003C\u002Fp>\n\u003Cp>We end up if if they're intertwined, then we end up in a situation where we could be auditing our own work. And from a conflict of interest perspective, that doesn't really work. It it reduces our integrity, and it reduces our objectivity from as an auditor. So, I think we tend to be a little bit more on the risk averse side. And oftentimes, you don't actually recommend, technology to our client.\u003C\u002Fp>\n\u003Cp>We take a very empathetic approach and try to understand, what our clients' business objectives are, and we try to understand what their current state is from a technology posture is. So what are the legacy tools that they've got in place right now? What are their growth ambitions? What is gonna prevent them from achieve helping them achieve those growth ambitions? And then we sort of lay out a criteria for understanding, okay, who are the possibilities that might help them either leave their legacy architecture in place and and do a bolt on to newer technology, or we identify a path forward that, you know, involves slowly removing that legacy, technology.\u003C\u002Fp>\n\u003Cp>But we we talk through this with the client, and we under and we get them to understand what does the road map look like and then how we would chunk out that road map and reduce risk for them along the way, reduce spend, for them along the way, and just overall make sure that we are paying attention to their, you know, strategic objectives and their regulatory environment, as well as, you know, the industry that they're operating in. I think that is primarily the place that plays, is really just taking that empathetic approach, knowing that we operate in almost every single sector globally, and knowing really, really key, insight around regulatory environment, strategic objectives of an of an organization, as well as their technology posture and sort of taking that big lens and then identifying what the route forward is going to be.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. That makes it. I'm curious. You kinda you mentioned sort of digital transformation at the enterprise level. When you see that type of modernization, do you is it more often than not that people are looking to do it, you know, pretty swiftly, like rip the band aid off approach, or are they sort of 1 1 foot at a time?\u003C\u002Fp>\n\u003Cp>Like, let's do this incrementally. Let's look at phase 1 and sort of, like you said, maybe it's getting bolted on to the side. Maybe there's some technical debt. Maybe it's more expensive to do it, you know, incrementally. But what seems to be, sort of best practice, or what do you what do you see more of these days within that?\u003C\u002Fp>\n\u003Cp>Speaker 1: It's a really good question. I I think very rarely is it ripped the Band Aid off because when you're talking about digital transformation, I know it's a term that's probably often maligned at this point. It's just it's just transformation at this point. It's really hard to just cut over and make something new because the way that we look at everything, it's it's people, process, and technology. And if you're changing technology, there's a really good chance that you're changing process.\u003C\u002Fp>\n\u003Cp>And if you're changing process, it's gonna impact people. And so whenever you rip the band aid off or make that very, very quick change, you have to have a very, very robust change management work stream that's attached to a project. And, you know, being we're in a fortunate enough position that we've got technology consulting, business consulting, people advisory consulting. We bring this massive amount of expertise in large scale transformations that can sort of safeguard and risk mitigate the, the areas where it could get a little hairy. And more often than not, the technology isn't the hard part.\u003C\u002Fp>\n\u003Cp>It's the people part that makes it really, really difficult for adoption and for new process knowledge transfer and for change management. So, I guess, long winded way of saying, rip the Band Aid off to your detriment if you don't include a change management work stream.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Oh, absolutely. And, again, like, rip the Band Aid off isn't necessarily you know, it's just a 1 and done. It's it's still, like, how expeditious are you trying to be? It's probably it's always gonna be phased out Yeah.\u003C\u002Fp>\n\u003Cp>Or or done in phases, I should say. But, yeah, no. That that makes a lot of sense. I I like the sort of the people process in tech, more of a holistic view of, you know, how you approach these problems. Nemanja, I'm curious.\u003C\u002Fp>\n\u003Cp>So at Work and Co, you know, how do you how do you guys balance, like, the innovation and responsibility when you are selecting, you know, technology for for your clients?\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. The eternal question. I wish I knew. But joking aside, innovation, as you know, is inevitable in our line of work. It's impossible to be in technology and not to innovate, or else to be named the dinosaur by everyone else in the industry very, very quickly.\u003C\u002Fp>\n\u003Cp>And, we are where we are because we innovate constantly. There are a couple of ways. I identified 3 as the main equally important ways of balancing responsibility, which is proper planning. And I cannot emphasize this enough. We pride ourselves in a really, really thorough planning and discovery phase before we even get to, any actual engineering work where you can, mitigate these risks and, create strategies, about how you're going to implement.\u003C\u002Fp>\n\u003Cp>Steven said a wonderful thing, which is changing technology. Technology is simple. That's why I like it. Technology is very straightforward, and it's an exact science. Everything that comes as a side effect, as a consequence.\u003C\u002Fp>\n\u003Cp>Adoption by people, some cost fallacy, pushback by the clients' teams, those are the real problems to solve. And these are most often at least tackled during the planning planning phase. So a very, very comprehensive planning phase, then validating as a second point, validating the ideas we get to, through the planning phase via prototyping. 1 of our 1 of our paradigms at Working Call is, prototypes, not presentations. And I can't stress how amazing that is.\u003C\u002Fp>\n\u003Cp>Playing around with a new technology and making something work, discovering ups and downs throughout the journey, There's no white paper that can that can beat an actual working prototype and that can give you a sense and a feel about the community, about the usage, about the tooling. So, definitely, prototyping is the second point. And then, last but not least, agility in its purest form and in its purest sense, the context is the ability to rapidly adapt and respond to changes. Many times, these innovations, like if you balance responsibility with innovation, it's inevitable that you'll miss the mark quite a few times. It's really important to be agile, and it's really important to then change course, to have, an open, direct, and candid conversation with all the stakeholders and to course correct to something that's better for the the the work, the client, and everyone involved.\u003C\u002Fp>\n\u003Cp>So being truly being truly agile and nimble when hurdles inevitably come is what helps balance out the constant call of innovation, especially now when the newer the newer lineage of developers just rush to every new innovation because there's this appeal, And, it's really enticing to use cool new tools. So\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I mean, as a as a developer myself, like, you know or in our entire team at Directus, it's like there's always this. It's like, oh, I just wanna try it out. You know? Especially because we're open source.\u003C\u002Fp>\n\u003Cp>People have this. You know, typically, open source is like nights and weekends, and it's like you can do whatever you want. And then when that becomes your day job, it's, you know, that balance is a little bit more interesting. You know, we're obviously very small, you know, had a full time head count of, like, 30 plus, but, Work and Co, a lot larger. And obviously, a lot even larger still.\u003C\u002Fp>\n\u003Cp>Nemanja, you mentioned sort of agile. I always used to kinda refer to that, as, like, a really important facet of of our agency, etcetera, but always, you know, mention the lowercase a so not to be confused with the methodology. I like nimble. Do you feel is do you feel like it's more important for for Work and Co, you as, as the agency? You find it more easier for you as an agency to be nimble, and and does that sort of align with your clients a lot, or are they a little bit, like, who where would you say the balance is there?\u003C\u002Fp>\n\u003Cp>Because I could imagine, you know, you you get your sort of things locked in, and you're ready to change at any moment, but maybe even after you've done run through some prototypes and you've gone through what things might look like, maybe they have more of a a set vision and they're not as nimble, once you get into the process? Or what what would you what are your thoughts on that?\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. III think the the word I like even more than agile is candid. I think it's really, really important to get to the bottom of why. Like, 1 of our partners, 1 of our founding partners, Joe Stewart, said when we did Virgin America, the clients know their business a 1000 times than we a 1000 times better than we ever will. There's always a reason for pushback.\u003C\u002Fp>\n\u003Cp>And as I said, it could be sunken cost fallacy. It could be the teams not being ready to adopt new technology. It could be because the technology is so deeply ingrained in the process, in the staffing, in everything that Steven mentioned. It it it's just really hard to pull out, like, a LEGO block and then replace with a nicer 1. So when things like this happen, when the other side, the the clients aren't as nimble, I think it's our responsibility as their partner to figure out why, and then to try to build a relationship and a sense of joint ownership.\u003C\u002Fp>\n\u003Cp>So the decision we land at is the decision that's best for the product and that's best for the work. I feel like very rarely is there irrational pushback. 9 out of 10 times, it's very good reasoning that's hidden behind business decisions and, people decisions. It's it's not as simple as it looks. That's why I like technology.\u003C\u002Fp>\n\u003Cp>Technology is super simple. Like, this is the perfect database to store this type of data, and that's where it ends. With business, it's much much more complicated.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Much I mean, the tech is is purely objective and that that at least removes it not removes it from the equation, but makes it an easier part of the equation. But I would say, you know, we talk about all these different intersections. There's certainly an intersection of people and tech. You know, specifically, when you're building out a project, that technology will be used by people.\u003C\u002Fp>\n\u003Cp>And, again, something that I'm aware of, 1 of the big reasons we built Directus was, you know, unifying you. This great shift from, like, the monolithic ERP systems to microservices was was amazing, but it also means, you know, those people might now have a lot more complexity in their day to day of, you know, 10, 20 different portals they log into and different APIs they have to, you know, go into references for, etcetera. So it's I I guess that sort of leads into the next question, which I'm curious, for both of you, your respective agencies. You know, how when you're really just getting started, how you do that initial sourcing of of tech, you know, for the stack of a project or for a whole company depending on the scope. Like, what is the key criteria you look for?\u003C\u002Fp>\n\u003Cp>You know, through the lens of the tech is simple, you know, you know, in a way, but the people in the process are are a bigger part of the of the problem to be solved, but also very interrelated with the technology choices. And open for open for either of you.\u003C\u002Fp>\n\u003Cp>Speaker 1: Well, I think it I think it has to it has to start with business value. I I think that's the the most logical place to start. Inherently, I think you you have to go under the assumption that if if a client is engaging you, they're engaging you for a reason, that their business isn't where it wants to be. And they're either trying to streamline internal processes and backstage, goings on, or they're trying to enhance and improve the front stage experience for their customers. And so the only reason really to consider new technology or new engagement is going to be the, you know, the additive business value that the technology is going to bring.\u003C\u002Fp>\n\u003Cp>And so for us, I mean, Nemanja said it actually quite well in the in the previous in the previous question, where he's talking about, you know, there's there's always gonna be pushback, and there's always gonna be things that people are not telling you. And all of that is gonna come down to you, the client, or you, the vendor, working with the client, trying to establish trust in a relationship, and you can really only move at the speed of trust. And so the faster that you can develop that trust with the client, you're gonna get the whole story, and you're going to get the underlying reasons for considering a change. And that helps to understand how to move forward. You know, there's there's not a whole lot of rocket science here.\u003C\u002Fp>\n\u003Cp>We're talking about technology being simple. I think we often need to boil down business into more simplified terms where people just like doing business with people they like. And, usually, when you like someone, you're going to be open and trusting that they have your best interest at heart and that they're coming from a place of abundance that just wants to be helpful. And I think that's the benefit of working at is that I get to turn off, you know, this, agency owner cash flow mentality and turn on, well, how can I be the most helpful partner for this client? And I think that's really, you know, where it starts is truly understanding the the the place that they're in, the place that they wanna get to, and how new technology is going to unlock new value for them with the challenges that they're facing.\u003C\u002Fp>\n\u003Cp>Speaker 0: A 100%. Yeah. And, like, absolute the idea that people wanna spend time with, do business with, whatever the the relationship is, like, they you people wanna spend time with people that they respect, that they that they like. You know? That you're gonna be interacting with people on a daily basis.\u003C\u002Fp>\n\u003Cp>You're gonna be getting good news and bad news. You're gonna have to have the, you know, the agility, and and with that comes, you know, the trust that's very much resonates with me. I I found that that was, 1 of the best ways to ensure really solid communication was just establish that trust, at the onset of even before you've, you know, closed the job. You know, when you're in the RFP state and you're trying to make sure that that you're, you're getting awarded that bid is be the most likable client. Like, they just want to have another meeting with you.\u003C\u002Fp>\n\u003Cp>Like, even just starting there is is is a pretty great place to be.\u003C\u002Fp>\n\u003Cp>Speaker 1: It it's funny you it's funny you say that, because I'm probably gonna be giving away 1 of my, my own secrets here, but that's okay. I'm coming from a place of abundance and being helpful. But to your point, Ben, you'll always get like, every single agency pitch team, will get the question, you know, why should I choose you, in the presentation stage? And it's always someone who's, like, asking the question and then sitting back in their chair waiting for you to squirm. I have found that answering that question in a way that just sort of lays everything bare to that point and creates a foundation for trust is the reason why we've been successful in those stages.\u003C\u002Fp>\n\u003Cp>And, you know, my my template answer for that question, it it it's essentially look. Everyone you're gonna speak to is gonna have a great body of work and great case studies and a great portfolio. We do too. Everyone is gonna come to the table with a methodology and something that is going to help derisk the project. We do too.\u003C\u002Fp>\n\u003Cp>But what you're not going to get in everyone and what you are going to get in us is going to be a team that is advocating for your business outcomes and your audience, and we'll have arguments with you. And we will have passionate discourse about the best possible solution, but we're also gonna be able to come back to the table the next day and like each other and continue to work together and make your project a success.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. And that that is, like, the respect. It's like that radical transparency. We're not just gonna say what you wanna hear. You know, if you gotta hear the latest trend of technology, like, yeah.\u003C\u002Fp>\n\u003Cp>Oh, I know that you're gonna be jazzed by hearing this. So, like, it's really about having the trust, that the can and the candor to be able to say what's needed to be said to to get that project. Because, again, if it's not successful project for them, certainly won't be for you. You're not gonna have the client, you know, as a repeat business. Yeah.\u003C\u002Fp>\n\u003Cp>It won't be a great case study, etcetera. Nemani, I'm I'm curious your thoughts on that that same question, you know, how, how you over at Work and Co, you know, find find that initial tech and what you're really, assessing assessing it against.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. So, for us, the most important thing is to really truly understand our clients' long term value objectives. I think that's the fits really nicely into the narrative of business requirements often being a lot more complex than the tech ones. We make sure that we establish a vision for the experience that we're building for both the team and the end users, and then we work with the clients to determine the KPIs. After we are sure that we agree on what problems we're trying to solve, we can guarantee that we're not trying to use technology to dictate how we're gonna solve the problem, but it that it's the other way around, that we're gonna use technology as a tool.\u003C\u002Fp>\n\u003Cp>And then there are several critical criteria that we use to evaluate technologies. Number 1 is always efficiency and effectiveness in solving the problem. Not every technology is good at solving everything. We've learned about that with Clojure, for example, an amazing language who solve a lot of really complex data manipulations, not great at other stuff. And user base and community adoption equally important.\u003C\u002Fp>\n\u003Cp>Are there people that you can talk to when you hit a a wall? Is there a is there a lot of libraries? Is there an open source community? Are there people that are evangelists for the technology actively speaking and promoting their their their tech? And then availability of resources and documentation, of course.\u003C\u002Fp>\n\u003Cp>Once all of this finds a like, an average market that's good, we try it out. We either build the proof of concepts that I've mentioned, trying to solve specific parts of problems, or we use the tool to, work on an internal initiative we have. We do this a lot, where we have a bunch of internal internal tools, where we try technologies or stacks out that fit seem like they could fit the problem nicely, and that gives us firsthand experience in some of the newer stuff that we wouldn't feel comfortable trying out with clients immediately. It's an investment, but it's a well well worth investment.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. A 100%. It's interesting too. I think you kinda led by saying, you know, focusing on on the long term vision, the long term solution, etcetera. What how do you quantify that?\u003C\u002Fp>\n\u003Cp>Like, when I was, you know, working through similar projects, everyone has a different idea of long term. Some people, you know, sit on a digital experience that's built for a decade, then they come back like, hey. It's not working because things got deprecated. It's not even supported or whatever. Other people have a very rapid, you know, iterative cycle.\u003C\u002Fp>\n\u003Cp>But, also, there's, like, strata to what you're building. You know, to what what we build for clients, there's foundational levels that can, you know, have the longevity to to survive and sort of support multiple iterations of a project. And maybe the facade, you know, the front end or what have you, maybe that does change and this that's part of the stack gets swapped out more frequently. But, you know, on the whole, like, what when you're talking about, like, long term vision, I'm curious I'm curious what that means, to you in the in that dialogue with your clients.\u003C\u002Fp>\n\u003Cp>Speaker 2: In that sense, it means more whether the emphasis is going to be on like, long term vision for me particularly means scale and performance more than anything else. Like, how do we see the platform, grow over time? Do we see a huge surge in in the number of users, the number of visits? Do we see the data spiraling out of the the capabilities of the current platform? So we have a very honest conversation at the beginning about life cycle of technologies and stacks, about the the need to update libraries and platforms and the need to maintain as if you would maintain any other machine.\u003C\u002Fp>\n\u003Cp>The fact that it's software and that it's abstract does not remove the the reality of it being a machine. But the context there is more of a are we building a small tool for a discrete number of users? Do you see this growing? Do you see this expanding both in terms of people using it, the data living in it, and the number of features? Do you see your business expanding into other areas, out of which this part that we're building now will be a, foundational framework for or maybe a part of a larger system?\u003C\u002Fp>\n\u003Cp>All these things help a lot with decision making. Overoptimization is the root of all evil, said someone really smart. I can't remember who. But the point is you are not gonna build a an insane event bus orchestration layer with Kafka for an app that's gonna be used by 100, maybe 1, 000 of people. It's redundant and unnecessary and a huge waste of time.\u003C\u002Fp>\n\u003Cp>So aligning on on what the build is the build strives to be down the road, helps a lot with cost, helps a lot with effort, and with the the technology choices as well.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. No. A 100%. You said over optimization never I also think that optimizing too early. I mean, there's there's all sorts of gotchas, you know, in there.\u003C\u002Fp>\n\u003Cp>But now I'm I'm curious. Is there an example, you know, that that you can think of that you've, like, you know, pretty recently where you maybe had some issues that you had to overcome with with a client in these in this decision process?\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. 1 of 1 of the examples that, comes to mind is working, with a client partnering to transform their legacy technical ecosystem, making sure that it can better serve the needs of their increasingly digitally focused user base. And then a critical decision point was choosing between building separate mobile apps using bespoke technologies like Swift and, Kotlin, or going through a cross platform mobile framework like Flutter that we really, really like or, React Native. And, the decision was complex. It involved a lot of a lot of moving parts.\u003C\u002Fp>\n\u003Cp>The existing team capability, their goals, their capacity, their performance, and that long term vision that we mentioned, which is how they see the the product moving forward. Also, security, a really, really important point. Yeah. So the way we approach it is we set the stage by really clearly defining the opportunities and challenges of all the approaches and making sure they understand the context and the impact each approach will take. We define proposed architectures based on the feedback that we got from them.\u003C\u002Fp>\n\u003Cp>Again, developed a lot of proof of concepts to show how different technologies on different devices behave when solving the same problem, and then went with a decision that I think all of us, the clients and us included, were very, very satisfied by and got a great outcome. And it was a it was not an easy process, I would.\u003C\u002Fp>\n\u003Cp>Speaker 0: It\u003C\u002Fp>\n\u003Cp>Speaker 2: was really, really tolling to get to a point of mutual satisfaction, but I think what got us there is a very close collaboration. And, again, I'm like a parrot now, but a lot of really honest candid conversations where you have to be really clear. Yes. This is 5% faster and better, but it is going to increase the development time 2 fold or 3 fold. Or maybe saying this is faster, but once you hit, 1, 000 or 2, 000 concurrent users, we are gonna start seeing problems.\u003C\u002Fp>\n\u003Cp>So having these conversations happen in real time and being ahead of the curve and making sure that all the angles are met greatly helps with\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Situation with this. And a lot of knowledge that you as an agency who has gone through this process at different scales, different customers iterated through, you know, okay. We built this, and now here's version 1.1 or 2.0 and the adjustments we had to make make. There's a lot of, you know, institutional knowledge there that you can give to, that client and make sure that you keep things on the rails that requires that trust.\u003C\u002Fp>\n\u003Cp>You know, they they have to understand that you have gone through this before, and you can, you know, hopefully learn just like in life. You can learn from those mistakes before you make them, you know, whenever possible. Steven, how about same thing for you. Like, have you have you had, a project recently where you were sourcing that tech and it just there was some sort of challenge or issue that you that you ran into that you had to try and figure a good way to work around?\u003C\u002Fp>\n\u003Cp>Speaker 1: I think there's always challenges and issues. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: A more notable 1. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: I yeah. I I think we have been doing or, about to conclude a project that is a, a commerce, transformation, retaining legacy ERP and then migrating, the entire experience off of 1 commerce platform to another. And, you know, someone told me once the the, really great analogy. The organization has been speaking French, for, like, years, for however long they've been using this commerce platform. And they know French, and it's second nature to them.\u003C\u002Fp>\n\u003Cp>And moving on to another another commerce platform, yeah, it does the same thing. It's still a commerce platform. It's gonna do cart. It's gonna do checkout, but it's in Spanish. And so now they have to think in French and then translate to Spanish.\u003C\u002Fp>\n\u003Cp>And sometimes the way that you've been speaking and the way that you've been working for so long, even though it's a very similar context, it's an entirely different language, and you have to learn an entirely different language. And so 1 of the things that often comes up in in these scenarios is a client essentially saying, you know, but I wanna do it this way. And there's a limitation of the technology because of the opinionated nature of that technology that's essentially saying, well, you can't do it this way. And so then we need to come together as, you know, vendor or service integrator and and client and have a conversation about what is really driving, you know, this request and what is really driving this requirement. And when you understand what it is, then you can identify maybe a way to work around that.\u003C\u002Fp>\n\u003Cp>And then what you what ends up happening is to to Nemanja's point, a real candid conversation around the value of that workaround. And so it's, you know, we found in 1 of the in what in this example that about 5% of their revenue needed to perform a function that they were requiring. It came from this, you know, servicing of a customer. And so do they really wanna spend, you know, AAAA pretty hefty amount relative to the overall project cost to incorporate something that's gonna take in 3 to 5% of their revenue when they could probably just live without it and then find ways of servicing the customer in a different way that they won't find an impact on. So I I think it's again coming to, you know, really ensuring that you've got future proof and scalable technology from several factors.\u003C\u002Fp>\n\u003Cp>You know, the technology's adaptability to the evolving industry standards or the regulate regulatory environment, its ability to integrate with other systems, and really the provider's commitment to continuous improvement and support. And we look at all of that and its potential to leverage emerging technology. I mean, we're all talking about AI these days, and, you know, Directus has also gotten into the, into the AI game as well. And then looking at those regular reviews and updates, the technology road map, all of that coupled with those candid conversations and trying to find POCs that are going to help create workarounds, but then evaluating the POCs based on return on investment, I think that's, like, the way that that we approach it, and that's the way that we go. And that's a relatively recent as of, like, 2 weeks ago example.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. It's a it's a big 1, and it's it can be hard to kinda make those decisions when you want to be improving. You wanna sort of modernize every ask you know, every corner of of your business, your process, etcetera. And sometimes, like, the ROI is not there. And, you know, when is the right time to kind of remove that technical debt, that legacy, part of the stack?\u003C\u002Fp>\n\u003Cp>You know, or, you know, do you ever you know, do you just kinda band aid it, add infinitum until, the ROI changes or it just no longer functions or, you know, there's there's a lot to that. It's interesting, though. I think you both have mentioned sort of this idea of, like, being opinionated or unopinionated. I'm curious, you know, Steven, you just mentioned making something future proof, which I I always I'll always kinda throw quotes around air quotes around that, and scalable, which I think is a big part of being future proof. You don't know how you're gonna scale necessarily.\u003C\u002Fp>\n\u003Cp>I mean, in an ideal world, you don't know because you could think things are gonna go amazing, they go even better. But how do you ensure that the the tools that you source are future, you know, future proof. And, you know, when you're thinking of things like, you know, the words like unopinionated or opinionated, I think both have, you know, pros and cons. You know, does that tie into this thinking, this, like, methodology for for choosing the Oh, yeah. The the tech?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Yeah. I I think that everything that I just mentioned around, understanding the technology's ability to, incorporate emerging technology, its evolving nature with industry standards, as well as, you know, regulatory standards. All of that, like, it has to it it creates the the the foundation and the guardrails for businesses to be operating in. And selecting a technology that doesn't take into account those guardrails or those foundational aspects of the business operating environment, I think, is a surefire way to surefire way to choose the wrong technology.\u003C\u002Fp>\n\u003Cp>Speaker 0: Do do you feel that, like, again, just do you feel that something choosing tech that is opinionated and follows those specs, you know, that compliance and just, like, checks those boxes, you know, out of the box, so to speak, off the shelf versus maybe a tool that's more unopinionated, that allows you to take those those paths when needed or if those paths change, if there's a new spec that's introduced to you. You feel like there's a benefit to either of those options, or do they both kinda have their place, depending?\u003C\u002Fp>\n\u003Cp>Speaker 1: I think they both have their place. I I think it's gotta be fit for purpose. Like, commerce is a great example. You do want it to be opinionated to some degree. Like, you don't want to be messing around with the world's best commerce platform, and then find out that your conversion rate is suffering.\u003C\u002Fp>\n\u003Cp>So in some respects, like, if you want to maintain your legacy stack, but you want to change your commerce platform as part of your architecture, then getting the benefit out of that commerce platform, you're going to have to find a way to make your technology stack and architecture work with that platform, if you want to reap the benefits of that. So that's, you know, a very, very purposeful and intentional incorporation of opinionated technology. But then in areas where you don't really know what the end goal is and you have a good inkling as to how you can foundationally get off the ground very quickly with an unopinionated and flexible piece of technology, then that's a a really, really big benefit. So something like Directus is a really, really great place to start because most people can create a frame of reference around Directus as, like, a CMS. And so when you're, you know, doing any kind of project, most projects require a CMS.\u003C\u002Fp>\n\u003Cp>And so you can anchor onto that frame of reference, but then because Directus is so amorphous, it can literally be anything you want it to be or need it to be. And it's got a very friendly microservices architecture. There's really no sort of cookie cutter approach to what you're gonna use Directus for. So that's a a really you know, in my in my experience, that's a good example of leveraging opinionated architecture for what it's intended for and then leveraging something that's as unopinionated as can possibly be to help you grow to what's next.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. And then, ideally, both sides of the coin have strong points of integration so that they work well together. Exactly. Obviously, you wanna, you know, mitigate as much negative space as possible because that's where, you basically just have to start building custom to kinda fill in those gaps. So if you can have things bookend nicely together, and and play both sides of of that, that's sort of the ideal state.\u003C\u002Fp>\n\u003Cp>And, you know, side side note, you we we spoke a week or 2 ago, and you had mentioned you used that word amorphous to describe direct us, and I ran back to the team, and probably end up in all sorts of our our marketing collateral. It was just like a a really great way of describing it. Similar to sort of like nebulous, but nebulous has this kind of like more pejorative, like, what are we? What is this? And amorphous is is is just a great term.\u003C\u002Fp>\n\u003Cp>I love that. Nehemiah, you you had kinda mentioned a second ago, I think it was like prototypes, not presentations. When you are, you know, going out and and sort of pitching to clients, may could you talk a little bit more about that? Like, I'm I'm so curious about, like, that process. Like, what does that look like?\u003C\u002Fp>\n\u003Cp>You know, very aware of, like, what a prototype is, you know, versus a more traditional presentation. But how does that typically go with clients, and and how do they how do they receive that?\u003C\u002Fp>\n\u003Cp>Speaker 2: Very well. I think the first of course, that's never the first step. The first step is gathering requirements and hearing about the problem, aligning on what we need to solve and what needs to be tackled. As we're focused on products a lot, very often, it's a an exciting segment of, the the customer's business or a new feature or a new, new product within their ecosystem. Then we go back to the drawing board, and we build something that we we show the clients.\u003C\u002Fp>\n\u003Cp>And I think that leaves the best impression, by far, seeing something, work. And we have amazing, extremely talented designers that can, in a very, very, I would even say, absurdly short time frames produce amazing work. Once, once you see something that you only have in your head, in front of you on a screen, that helps immensely. I I have never seen I've never seen feedback after a a, like, a keynote presentation as I have after a couple of framer framer prototypes. Then the tech comes into play, and then we have a very, very thorough and deep, initial discussion where we try to understand the the tech team composition, the capabilities, the current infrastructure, architecture platforms, and everything that's there in order to better propose a solution.\u003C\u002Fp>\n\u003Cp>But, yes, the the prototype comes not as a first, but as a very close to first step, where it's the wow effect that, really brings it home. Because, we are visual beings by nature. And, that's why I feel like every developer, every engineer needs to I am let's put it like this. I'm com I used to be completely void of any sense of aesthetics. So math, math, math, then physics and electronics in in university.\u003C\u002Fp>\n\u003Cp>And then I worked through it because we are visual beings and to be in the line of work we are, which is digital, everything is very, very aesthetic, and people respond to, amazing inspiring interfaces. So, that's where I think prototypes are key. Again, seeing these ideas that we all talk about in presentations come to life is the same with features. There's a lot of talk about AI now and the capabilities of LLM and the conversational platforms. Seeing a happy path prototype often says a lot lot more than a bunch of keynote slides illustrating our general capability.\u003C\u002Fp>\n\u003Cp>Just seeing a happy pet alum consult you on which product to buy, I think it sends a really strong message about capability, preparedness, and the the the talent within the team.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Definitely, if you go into it with the mindset of getting that massive knowledge transfer, like truly understanding, you know, the problems, the root the root, you know, things that need to be solved, the goals, etcetera. And you understand the business, the team. Once you have all that, you know, over at work and co or or UI, like, I think then you can kind of jump into that. It's it's interesting, though.\u003C\u002Fp>\n\u003Cp>I agree. You know, humans are so visual. Like, the idea of, like, inspiring through, like, you know, pen to paper, so to speak, is huge. But at the same time, like, I started thinking because that that's how I operate. You know, I'm very quick to get in and start just divergently coming up with with UX, UI, like prototypes.\u003C\u002Fp>\n\u003Cp>And I feel like that conveys so much, so much more than a presentation if you have that, you know, the initial work ready, you know, informing that process. But, also, it makes me think of, like, books to movies. I watch a lot more movies than I do read books. I you know, just time wise. But there's this idea of, like, you read a book and we're also imaginative creatures.\u003C\u002Fp>\n\u003Cp>Does it ever backfire? Because when you kinda go in with a prototype, that's like the movie. Right? You're sort of saying, like, this is this is what this could look like versus a book, and it's like, oh, that's I read the book, and then it was so different in the movie. And I don't know how I feel about that.\u003C\u002Fp>\n\u003Cp>Once you've kind of gone that direction of a prototype, do you ever feel like you are locked in because they've seen it and it's like, oh, this this is what we want, and then it that agility, that flexibility becomes a little bit more difficult?\u003C\u002Fp>\n\u003Cp>Speaker 2: That's where the the experience of years of pitching like this comes in really, really handily. We build prototypes to solve specific problems, sometimes even specific parts of specific problems. So it's not such a, such a heavy buy in or such a huge commitment. The prototypes often are, vital and instrumental at solving this 1 key thing. And, 9 out of 10 times when we do show a prototype, we know that that's the direction that's ultimately the best for the product.\u003C\u002Fp>\n\u003Cp>So showing a prototype solving that 1 key thing isn't completely selling you into 1 solution. It's a learning experience, and, it's articulated by us being a lot more diverse and potent prototypes that were selling us into solutions. Now it's a a good balance of both. You explain, show capability, demonstrate previous. As Steven said, we all have really nice case studies to fall back where everyone's impressed at how we solved really complex problems for really great really great clients, but then for specific segments or of specific problems, I feel like that's the sweet spot sweet spot for prototypes.\u003C\u002Fp>\n\u003Cp>Not to committing, but great at showing capability and putting like, giving a visual aspect to the solution.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Oh, I mean, a 100%. Steven, I'm curious. So when we're talking about, like, we're we're presenting to the client, you know, sometimes thing things backfire. When when a client comes in, maybe they already have preconceptions.\u003C\u002Fp>\n\u003Cp>I guess this could apply to to either of you, but they have a preconception. This happened to me a lot. You know, I I used to joke, with with our team that it was, you know, 1 of the executives, like, they had a niece that, like, worked for a company. It's like, this is the bet. Like, I've heard so much about this and, like, that is the preferred stack or tech tech choice.\u003C\u002Fp>\n\u003Cp>You know, I think Carlo had a, who just posted in the in the audience questions had a similar, you know, how do you manage client expectations when the recommendation recommended technology has a steep learning curve? I I think there's, you know, all of these sort of questions around how do you get past, you know, when there's tech that might not be a good fit for the company or it is a good fit, but you're just not aligned. And and maybe trust isn't quite getting you there. You have to really dive in and and, you know, sort of or cover that somehow and and help bridge the gap of, like, yes. I understand that this is, you know, the the latest, you know, hype train tech, or I understand that you've heard a lot about this this other thing from a family member or whatever it might look like.\u003C\u002Fp>\n\u003Cp>But any thoughts on that, like, how you navigate that?\u003C\u002Fp>\n\u003Cp>Speaker 1: So many thoughts. I do like, I I just wanna go back to, Nemanja's comments about, prototyping. I I think prototyping has a huge, huge amount of value in the work that we do for clients. Ben, I'm I certainly don't wanna poo poo your analogy on, you know, books and movies, but I think the important thing there is that, people can either create their own context or the people that they are hiring as experts can help create a better context. And if you leave someone to read the book, then they're creating their own context.\u003C\u002Fp>\n\u003Cp>They're creating their own imagination. They're filling in the gaps for themselves. Whereas the movie is telling them, like, this is what it is, and there's no imagination here. There's nothing, you know, left untold. This is the story, and, you know, you should pay attention to to this story.\u003C\u002Fp>\n\u003Cp>David Kelly, from IDO has got a great quote about prototypes. If a picture is worth a 1, 000 words, a prototype is worth a 1, 000 meetings. And so, like, you know, IIII do love the idea of prototypes because it does create that on ramp to trust. If a client's got a problem that is, you know, in their mind, immutably solve unsolvable, then, you know, a prototype from a experienced and professional team coming in to, you know, help highlight how it could be solved with proof of concepts or prototypes that, like, that accelerates the engagement massively. It creates a huge amount of trust.\u003C\u002Fp>\n\u003Cp>It helps solve a problem. It creates business value, and it's 1 of those things that clients can easily get behind because, again, going back to people, like, doing business with people they like, the person who you're working with has to look good in front of someone. And if you're helping them look good in front of their boss, like, that just solidifies and galvanizes the relationship because you're solving a business problem that goes beyond them. So I'll say that on on prototypes.\u003C\u002Fp>\n\u003Cp>Speaker 0: Well, I just and IIII do agree because, again, we're hired as agencies to it's a full service agency, typically. You know, you are delivering a full suite of deliverables. And I think the the book and or, you know, the the the story itself is 1 piece of that, but it's our job as, you know, on the UX side, the UI side, the all these different pieces to be the cinematographer, to be the director, to to kinda give them the entire experience. Otherwise, you know, you're leaving it up to them. And maybe they're they're great and they have some great visions, but you're also probably gonna end up with a roomful of people\u003C\u002Fp>\n\u003Cp>Speaker 2: who are\u003C\u002Fp>\n\u003Cp>Speaker 0: all interpreting things quite differently. And And then you're gonna be navigating the people problem of, okay. Now we gotta try and wrangle everybody to the same page.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. So I I think the the way the way that that we try to solve that is really trying to be proactive, right from discovery. I there's 2 things that, I will do within my engagements every single time. The first is is, an exercise called the graphic game plan. And, it was initially invented by, Grove Consultants, And, I leveraged this sort of storytelling visual of, hey.\u003C\u002Fp>\n\u003Cp>We're all we're all in our in our car, on our road trip heading towards this destination. The destination is project completion. What is going to be the measure of success for project completion? And then we put everyone everyone, including our team, the client team, puts their measures of success right in the center of that target. Then around that, you've got primary and secondary objectives.\u003C\u002Fp>\n\u003Cp>What do we want this experience to be? How do we wanna feel when we're doing the work? All of these sorts of things, We get them up there on the target as well. Then in the car are gonna be all of the stages and tasks that we need to do in order to achieve our objectives and achieve our our measure of success. Below the car, there's these bumps.\u003C\u002Fp>\n\u003Cp>And those are gonna be the challenges that we're gonna be facing along the way. And then the wheels on the car are gonna be the success factors. If everyone gets into a room and starts talking about success factors, challenges, all of the things that we have to do, the resources available to us, the objectives that we're trying to hit, then everyone owns the scope collectively. There isn't 1 side that owns the scope. And when you both share the scope, that's when you avoid things that I lovingly call climbing Mount Death March, where it's the vendor that is figuring out the scope of the project because it was ill defined at the beginning while they're developing.\u003C\u002Fp>\n\u003Cp>And, like, that is just the worst place to be in. So that's the graphic game plan. And the second thing that I'll do is ensure that we have stakeholder interviews from everyone in the company that could say no even at the 11th hour on the project strategy and the project direction. Because the 1 thing that every project should try to avoid is the swoop and poop. And that's, you know, when you're at a beach having a great day, team's doing great job, and a seagull flies in, shits all over your picnic, and then flies away.\u003C\u002Fp>\n\u003Cp>Speaker 0: And then you're Good luck. There.\u003C\u002Fp>\n\u003Cp>Speaker 1: What's that?\u003C\u002Fp>\n\u003Cp>Speaker 0: I've heard that's good luck. That's a good thing to say. Trying to make you feel a little better about it.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Only the only the people that, they get crapped on tell you that it's good luck. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: Only in the metaphor.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. So those are the 2 things that we'll do to make sure that we've got alignment, right from the beginning. And that is probably the most important thing. Because if you take a flight around the world, around the equator, and you're 1 degree off course, by the time you get around, you're gonna be 500 miles off target. And that's 1 degree of deviation.\u003C\u002Fp>\n\u003Cp>So better to get that, you know, that heading right on target right from the beginning.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Yeah. I mean, it becomes rocket science. I mean, you the the more off you are, the earlier you're off, that just kinda compounds and you you're scaling problems. So if you can mitigate that, that's definitely something to be very aware of, in that early process.\u003C\u002Fp>\n\u003Cp>Yeah. Nemanja, I'm curious. Do you have anything similar, like, where you you've kinda had to deal with this issue of people coming in with preconceptions or trying to wrangle, you know, someone away from this idea that really just wouldn't work, but they're they're just struggling to to see the vision and to have that trust.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. More oftentimes than I would like. But, I think that, you can't win them all. Sometimes just having a having done all of your homework, having done an extensive discovery phase, having validated your assumptions, done all the prototyping in the world, shown capability, discussed caveats, her hearing the client out and hearing their reasoning, seeing that there is no greater mystery behind why someone's as adamant as they are about a certain technology or not adopting a certain technology, the conclusion might, in the end, be that it's someone's need. And, that's okay.\u003C\u002Fp>\n\u003Cp>What we do in these situations is that we establish a very, very loud and very clear communication channel throughout the process from the first meeting where things are we're an agency, and we eventually perform a service for our clients. So if you've exhausted all of your options and if there is no other way to argue your point, you just have to be very, very clear and very direct and to communicate very frequently what the risks and consequences of a decision you believe are is suboptimal are. So making sure that this 1 degree deviation that everyone knows that it's gonna be 500 kilometers by the time we get back and alerting date, like, an alarm clock every hour. Like, saying in every retrospective meeting or every time there's an opportunity to discuss technical depth or to discuss progress, just making sure everyone understands the consequences of the choices that we believe were suboptimal. 100%.\u003C\u002Fp>\n\u003Cp>As I said, you can't win them all. There's no way that everyone's always gonna be aligned regardless of how good your argumentation is. But I think it's our responsibility as partners to make sure that we alert to these consequences as often and as early as possible.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. No. A 100%. And I think, again, when things go south, the best thing that can happen is to already have established trust. You know, they they understand why the communication is there, and at least then it's you know, you you can hopefully navigate, through the project or whatever that ends up looking like.\u003C\u002Fp>\n\u003Cp>But, yeah, I we've kind of mentioned that word so much. I it really, you know, going back to, I think, the beginning of this, Steven, you had said you can only move at the speed of trust, which I took a note of. I think that's I think that's an amazing, sort of, like, way to kinda think about this and highlights the importance of that trust and communication, and it connects it to something that's very real. Like, when you're building a project, you've you know, speed. Speed is important.\u003C\u002Fp>\n\u003Cp>You can't you when I was running my agency, there was kind of 4, sort of levers that you really had to work with, and they were all I I think there's even a website where you can turn certain ones on and off. It was speed, scope, cost, and quality. And, of course, any good agency is gonna take quality off the table and say that's always gotta be good. We're we're not gonna sacrifice that. So you have these 3 toggles that you can kinda, you know, choose to choose your own adventure.\u003C\u002Fp>\n\u003Cp>And it's great to know that trust is a huge part of of the speed of the process, because, really, in the beginning when you're figuring out the scope and you're, you know, setting up what the cost looks like, you just efficiency and speed is is the name of the game. You know, how can you get through and and help to establish that and help the client really understand what their options are, that you are the right vendor, or, you know, partner to help them get through this. And I guess it all just comes down to to to that trust. So, we we've said that word a lot, and that's I think that's we we could probably rename this episode into, you know, establishing trust and, you know, the importance of trust at the agency level. But, you know, we we we ran over time a little bit, which is which is always good.\u003C\u002Fp>\n\u003Cp>We didn't even get through through all of our questions here, or, you know, I think there was a few that were thrown into the chat. But, any final thoughts, Steven Steven or Nemanja?\u003C\u002Fp>\n\u003Cp>Speaker 1: Nemanja, why don't you go first?\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. I was just gonna say because it piggybacks nicely off of what you've just said. It's not only trust, I would say, it's partnership. Like, 1 of the things that got me, when I was deciding whether I'll go into agencies and service work or whether I'll go into, like, building a product startup or something like that was agencies, at their core, help solve problems, and you can't help if you're in an adversarial position. I really every relationship that we've had with a client that was hugely successful and that brought a lot of, for lack of a better word, happiness to me after the resolution was when we were true partners.\u003C\u002Fp>\n\u003Cp>So it's not like we're warning you this will fail because you're not listening to us. It's like, how do we make sure that since these are the the axioms that we have to work against, we fail less as a team. Like, being a partner to our clients is, I think, a lost forgotten art. Sometimes we get into a mindset of us versus them. I don't think that's a good mindset for agencies or tech providers at all.\u003C\u002Fp>\n\u003Cp>We serve technologies is there to serve solve problems. And, like, at its core, what we do is we facilitate. We use amazing tools to change the world. So partnership and not pointing fingers. The name of the game for me.\u003C\u002Fp>\n\u003Cp>Speaker 0: That's hon. I that's exactly right. Open, trust, you know, those are all key things, but really the goal of those things is to, establish a partnership where you're you're in it together because, I mean, you truly are at the end of the day. Yep. Steven, thoughts on that or, just, you know, closing closing thoughts?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. I I mean, I, I feel like Nemanja and I drink the same Kool Aid. It's 1 of those things where, for me, there was a very, very defining moment earlier on in my agency life where I I really just took on this mantle of being a designer is the most important thing in the world, and I would tell my clients, you know, the overarching responsibility of a designer and and how we're, you know, empowered to create amazing tools. The bottom line is they really didn't give a shit, about my my little speech about, you know, design. And it really sort of came to a head when I started changing the language that I was using around the value of the work that we do and what we bring to the table.\u003C\u002Fp>\n\u003Cp>And it was speaking my client's language, being empathetic to what their needs were and how they were articulating their business problems. And it was all in the language of business. And so if you can get out of your own way and stop speaking the language of design or stop speaking the language of technology and speak the language of business and how the tools that you wield are going to help solve their problem in a way that makes sense to them, that's when you actually engender really good partnership and really good, foundation of trust. And so I think that was a a really key moment for me in my, in my sort of growth and learning, as a agency owner and now someone who's in the, you know, the big 4 consulting world.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Couldn't could have said it better. I I again, these are all just huge pillars in how you how you kind of establish and foster, those those relationships. And then again, the the benefit of that is then you end up working with these clients, over and over and over again, and they just become huge advocates, for not only the stack that you help them select, you know, which, you know, we're a great great in a great position to be chosen by such a great you know, by so many great companies, but also, you just get to, have that repeat business and long enduring, partnerships with with great companies. So excellent.\u003C\u002Fp>\n\u003Cp>Steve and Damania, this was has been an awesome chat. Again, probably, we'll have to, like, circle back and maybe think about another 1 because a lot more. Again, this is the, you know, the whole reason for me being here, was was how how to solve these problems within an agency and trying to find the right tooling. And it's not always direct us. It's not, you know, that's the name of the game is, you know, just finding finding the right tool for the right job, which is not easy, as we say in an hour and 8 minutes in.\u003C\u002Fp>\n\u003Cp>But thank you both so much for joining, and we'll be seeing you at the rest of Leap Week for everybody else who's who's watching now.\u003C\u002Fp>\n\u003Cp>Speaker 1: Thanks so much for having us.\u003C\u002Fp>\n\u003Cp>Speaker 0: Absolutely. Alright. Thanks, everybody.\u003C\u002Fp>","Today, we are doing another Bridging Bytes episode, in the midst of our of our Leap Week, Leap Week 3, over at Directus. So an exciting time for multiple reasons, but, super super jazzed to be here, joined by Steven Majid and or Meggitt, sorry, and Nemanja, Nichoforovich. We are gonna be running through how agencies innovate, something very, you know, important to me. This is how I kinda got started with Directus, got started in general. So, yeah, we'll be spending next hour or so running through all things agencies, sort of the the primer, the the history of Directus, and then diving into all the, so the nuance of, you know, the overlap between agencies and how you choose your software, how you, you know, build out these projects, oftentimes very, very different projects for different clients. So I think it would probably make sense. Why don't we dive in with some introductions? I can go first just kinda lay you know, set the stage for, again, you know, how I got started with Directus, with my agency, that I started about 20, 20 some odd years ago, called Ranger Studio out of New York City. We were headquartered in Brooklyn. It was, truly, you know, going back to 1 of our previous bridging bytes episodes, it was the intersection of design and development. So we were lucky enough to do builds for some of the amazing, you know, marketing and design studios out there, you know, IDEO, Wolf Allens, Pentagram, Interbrand, etcetera. And we would work to basically be the hands, to build out their their projects for amazing clients. You know, we worked with Google and Snapchat, AOL, AT and T, the US government, just doing really white glove amazing projects, you know, thus Directus because we needed to find software that would actually fit the bill for all of those. But ran that studio for about 15 years, until it sort of transitioned into into Directus, formally. But, yeah, it was it was a very interesting you know, we'll we'll get into it, I think, more in this episode, but it's very interesting sourcing, technology, especially back then 20 years ago when technology was very different. There was far less to choose from. There wasn't as much information about what you were choosing. But that's really how I how I got started with Directus was building software that would make, our internal development more efficient, give us administrative tools we could hand off to those end customers, that wasn't strictly, a head you know, a CMS. They didn't even have have headless back then, or was it something completely custom? That's really what we ended up doing constantly. It was just building custom, solutions and back ends for every project, which is inefficient. So today, we're gonna be diving into how people do it today, the modern way of approaching this problem. So why don't we start off, Steven Meggitt, who is over from, if you wanna give give an intro. Sounds good. Awesome. Sounds good. Ben, that was AAA good intro and backstory for, the genesis of of Directus. It seems that, you're not the only agency that started out that way. 37 signals, I think, comes to mind of a similar trajectory. So it's it's interesting to hear that. Pleasure to be here, Ben, Manja. Great to connect with you as well. Thanks for Directus for for having me on having us on. So I've been at for, close to 5 years now. I've spent my career designing, developing, and delivering really, really unexpectedly clever customer experiences that generate return on investment. I started in 2, 001 in my parents' basement, and then built a small but highly focused digital agency. Ben, you talked about the intersection of design and development. I like to add in 1 more element to that, where my agency was highly, highly focused on research, user experience and design and development. And that eventually led to me growing the the agency pretty organically and built a small but highly focused agency on those elements. And that led to an acquisition by in 2019, to help build out, what was then a, city centric design studio capability, which then was was then grown into a national practice. So now I'm a partner in the customer transformation practice, which brings together the work that I do in the studio, along with everything that I'm passionate about, which is helping organizations really build sustainable value and growth by making every touchpoint in the customer journey an exceptional 1. That is awesome. Yeah. And, you know, 37 signals, obviously, that's a great sort of template. You know, was taking very close looks at them back in the day and, you know, III are they still around? Oh, yeah. Yeah. I I get I get whiffs of, like, you know, the there are, different product suites every once in a while, but I can never can never quite tell. Some people are a little bit more ephemeral. But, thank you so much, Steven. That's that's a great great intro. Super happy to have you here. Nemanja, do you wanna, run through a quick intro from over at Workinco? Yep. Hi, everyone. My name is Nemanja, and I'm a technology partner at, Workinco. I've been with WorkingCo since December 2013, joined as developer number 4, and, it's been quite a ride. I've been, meddling with almost any technology project that we've ever worked on. From Virgin America that, made Working Code what it is, over through NBA, Disney, Google, CBS, more recently Contentful and Decathlon. As far as, my trajectory goes, I started coding, straight out of high school, like 2, 002. And I was always fascinated by hands on work, clean architecture, design patterns, and I was fortunate to have great mentors before Workingco and during Workingco. So 1 of the things that made me stick around for for as long as I have is that Working Group partners, in particular, are very hands on, so I get to do what I love every single day still. Little known. I don't even think we talked about this, Nemanja, but I actually when I was when I moved to the city and I was figuring out exactly where I was gonna be, you know, working next, I actually went over to Work and Co. I knew some of the partners over there at the time, and applied. I was like, I would love to work here. This is an amazing, amazing shop. Things went a different direction. I ended up starting my own, my own agency, but I certainly what that is what you described is exactly what resonated with me is all the partners were super hands on. There was no, you know, okay. All these, you know, lower level junior designers are gonna go out and do the grunt work, etcetera. Everybody was really active in, you know, building out, the project. And, again, not just amazing clients, but amazing projects for those clients. Excellent. So that's basically intros. I think again, just to reiterate, today, we're gonna be really going through, the agency side, how we how we sourced different technologies, which, of course, can be very disparate. You've got, depending on the agency, some are a little bit more. This is, you know, lather and repeat exactly what we do, you know, bread and butter. And then others are more divergent, in in the types of project they take on. And, of course, that means that the tech that you use to build those projects can be all over the place. And so sourcing, assessing, you know, and making recommended, recommendations to those clients can be can be very tricky. There's a lot to sort of cover there. Maybe we kick it off. Steve, I'd love to hear a little bit more about how, you know, you, your team, and and navigates, you know, the process of recommending that that tech, for your clients. You know, especially given your role, that dual role as an auditor, and also a consultant. Yeah. It's a tough 1. Or sometimes it can be a tough 1, especially if we audit the clients. So, generally speaking, you know, the the audit side of the house and the consulting side of the house remain separated. It's sort of the the only way that that it can work. We end up if if they're intertwined, then we end up in a situation where we could be auditing our own work. And from a conflict of interest perspective, that doesn't really work. It it reduces our integrity, and it reduces our objectivity from as an auditor. So, I think we tend to be a little bit more on the risk averse side. And oftentimes, you don't actually recommend, technology to our client. We take a very empathetic approach and try to understand, what our clients' business objectives are, and we try to understand what their current state is from a technology posture is. So what are the legacy tools that they've got in place right now? What are their growth ambitions? What is gonna prevent them from achieve helping them achieve those growth ambitions? And then we sort of lay out a criteria for understanding, okay, who are the possibilities that might help them either leave their legacy architecture in place and and do a bolt on to newer technology, or we identify a path forward that, you know, involves slowly removing that legacy, technology. But we we talk through this with the client, and we under and we get them to understand what does the road map look like and then how we would chunk out that road map and reduce risk for them along the way, reduce spend, for them along the way, and just overall make sure that we are paying attention to their, you know, strategic objectives and their regulatory environment, as well as, you know, the industry that they're operating in. I think that is primarily the place that plays, is really just taking that empathetic approach, knowing that we operate in almost every single sector globally, and knowing really, really key, insight around regulatory environment, strategic objectives of an of an organization, as well as their technology posture and sort of taking that big lens and then identifying what the route forward is going to be. Yeah. That makes it. I'm curious. You kinda you mentioned sort of digital transformation at the enterprise level. When you see that type of modernization, do you is it more often than not that people are looking to do it, you know, pretty swiftly, like rip the band aid off approach, or are they sort of 1 1 foot at a time? Like, let's do this incrementally. Let's look at phase 1 and sort of, like you said, maybe it's getting bolted on to the side. Maybe there's some technical debt. Maybe it's more expensive to do it, you know, incrementally. But what seems to be, sort of best practice, or what do you what do you see more of these days within that? It's a really good question. I I think very rarely is it ripped the Band Aid off because when you're talking about digital transformation, I know it's a term that's probably often maligned at this point. It's just it's just transformation at this point. It's really hard to just cut over and make something new because the way that we look at everything, it's it's people, process, and technology. And if you're changing technology, there's a really good chance that you're changing process. And if you're changing process, it's gonna impact people. And so whenever you rip the band aid off or make that very, very quick change, you have to have a very, very robust change management work stream that's attached to a project. And, you know, being we're in a fortunate enough position that we've got technology consulting, business consulting, people advisory consulting. We bring this massive amount of expertise in large scale transformations that can sort of safeguard and risk mitigate the, the areas where it could get a little hairy. And more often than not, the technology isn't the hard part. It's the people part that makes it really, really difficult for adoption and for new process knowledge transfer and for change management. So, I guess, long winded way of saying, rip the Band Aid off to your detriment if you don't include a change management work stream. Yeah. Oh, absolutely. And, again, like, rip the Band Aid off isn't necessarily you know, it's just a 1 and done. It's it's still, like, how expeditious are you trying to be? It's probably it's always gonna be phased out Yeah. Or or done in phases, I should say. But, yeah, no. That that makes a lot of sense. I I like the sort of the people process in tech, more of a holistic view of, you know, how you approach these problems. Nemanja, I'm curious. So at Work and Co, you know, how do you how do you guys balance, like, the innovation and responsibility when you are selecting, you know, technology for for your clients? Yeah. The eternal question. I wish I knew. But joking aside, innovation, as you know, is inevitable in our line of work. It's impossible to be in technology and not to innovate, or else to be named the dinosaur by everyone else in the industry very, very quickly. And, we are where we are because we innovate constantly. There are a couple of ways. I identified 3 as the main equally important ways of balancing responsibility, which is proper planning. And I cannot emphasize this enough. We pride ourselves in a really, really thorough planning and discovery phase before we even get to, any actual engineering work where you can, mitigate these risks and, create strategies, about how you're going to implement. Steven said a wonderful thing, which is changing technology. Technology is simple. That's why I like it. Technology is very straightforward, and it's an exact science. Everything that comes as a side effect, as a consequence. Adoption by people, some cost fallacy, pushback by the clients' teams, those are the real problems to solve. And these are most often at least tackled during the planning planning phase. So a very, very comprehensive planning phase, then validating as a second point, validating the ideas we get to, through the planning phase via prototyping. 1 of our 1 of our paradigms at Working Call is, prototypes, not presentations. And I can't stress how amazing that is. Playing around with a new technology and making something work, discovering ups and downs throughout the journey, There's no white paper that can that can beat an actual working prototype and that can give you a sense and a feel about the community, about the usage, about the tooling. So, definitely, prototyping is the second point. And then, last but not least, agility in its purest form and in its purest sense, the context is the ability to rapidly adapt and respond to changes. Many times, these innovations, like if you balance responsibility with innovation, it's inevitable that you'll miss the mark quite a few times. It's really important to be agile, and it's really important to then change course, to have, an open, direct, and candid conversation with all the stakeholders and to course correct to something that's better for the the the work, the client, and everyone involved. So being truly being truly agile and nimble when hurdles inevitably come is what helps balance out the constant call of innovation, especially now when the newer the newer lineage of developers just rush to every new innovation because there's this appeal, And, it's really enticing to use cool new tools. So Yeah. I mean, as a as a developer myself, like, you know or in our entire team at Directus, it's like there's always this. It's like, oh, I just wanna try it out. You know? Especially because we're open source. People have this. You know, typically, open source is like nights and weekends, and it's like you can do whatever you want. And then when that becomes your day job, it's, you know, that balance is a little bit more interesting. You know, we're obviously very small, you know, had a full time head count of, like, 30 plus, but, Work and Co, a lot larger. And obviously, a lot even larger still. Nemanja, you mentioned sort of agile. I always used to kinda refer to that, as, like, a really important facet of of our agency, etcetera, but always, you know, mention the lowercase a so not to be confused with the methodology. I like nimble. Do you feel is do you feel like it's more important for for Work and Co, you as, as the agency? You find it more easier for you as an agency to be nimble, and and does that sort of align with your clients a lot, or are they a little bit, like, who where would you say the balance is there? Because I could imagine, you know, you you get your sort of things locked in, and you're ready to change at any moment, but maybe even after you've done run through some prototypes and you've gone through what things might look like, maybe they have more of a a set vision and they're not as nimble, once you get into the process? Or what what would you what are your thoughts on that? Yeah. III think the the word I like even more than agile is candid. I think it's really, really important to get to the bottom of why. Like, 1 of our partners, 1 of our founding partners, Joe Stewart, said when we did Virgin America, the clients know their business a 1000 times than we a 1000 times better than we ever will. There's always a reason for pushback. And as I said, it could be sunken cost fallacy. It could be the teams not being ready to adopt new technology. It could be because the technology is so deeply ingrained in the process, in the staffing, in everything that Steven mentioned. It it it's just really hard to pull out, like, a LEGO block and then replace with a nicer 1. So when things like this happen, when the other side, the the clients aren't as nimble, I think it's our responsibility as their partner to figure out why, and then to try to build a relationship and a sense of joint ownership. So the decision we land at is the decision that's best for the product and that's best for the work. I feel like very rarely is there irrational pushback. 9 out of 10 times, it's very good reasoning that's hidden behind business decisions and, people decisions. It's it's not as simple as it looks. That's why I like technology. Technology is super simple. Like, this is the perfect database to store this type of data, and that's where it ends. With business, it's much much more complicated. Yeah. Much I mean, the tech is is purely objective and that that at least removes it not removes it from the equation, but makes it an easier part of the equation. But I would say, you know, we talk about all these different intersections. There's certainly an intersection of people and tech. You know, specifically, when you're building out a project, that technology will be used by people. And, again, something that I'm aware of, 1 of the big reasons we built Directus was, you know, unifying you. This great shift from, like, the monolithic ERP systems to microservices was was amazing, but it also means, you know, those people might now have a lot more complexity in their day to day of, you know, 10, 20 different portals they log into and different APIs they have to, you know, go into references for, etcetera. So it's I I guess that sort of leads into the next question, which I'm curious, for both of you, your respective agencies. You know, how when you're really just getting started, how you do that initial sourcing of of tech, you know, for the stack of a project or for a whole company depending on the scope. Like, what is the key criteria you look for? You know, through the lens of the tech is simple, you know, you know, in a way, but the people in the process are are a bigger part of the of the problem to be solved, but also very interrelated with the technology choices. And open for open for either of you. Well, I think it I think it has to it has to start with business value. I I think that's the the most logical place to start. Inherently, I think you you have to go under the assumption that if if a client is engaging you, they're engaging you for a reason, that their business isn't where it wants to be. And they're either trying to streamline internal processes and backstage, goings on, or they're trying to enhance and improve the front stage experience for their customers. And so the only reason really to consider new technology or new engagement is going to be the, you know, the additive business value that the technology is going to bring. And so for us, I mean, Nemanja said it actually quite well in the in the previous in the previous question, where he's talking about, you know, there's there's always gonna be pushback, and there's always gonna be things that people are not telling you. And all of that is gonna come down to you, the client, or you, the vendor, working with the client, trying to establish trust in a relationship, and you can really only move at the speed of trust. And so the faster that you can develop that trust with the client, you're gonna get the whole story, and you're going to get the underlying reasons for considering a change. And that helps to understand how to move forward. You know, there's there's not a whole lot of rocket science here. We're talking about technology being simple. I think we often need to boil down business into more simplified terms where people just like doing business with people they like. And, usually, when you like someone, you're going to be open and trusting that they have your best interest at heart and that they're coming from a place of abundance that just wants to be helpful. And I think that's the benefit of working at is that I get to turn off, you know, this, agency owner cash flow mentality and turn on, well, how can I be the most helpful partner for this client? And I think that's really, you know, where it starts is truly understanding the the the place that they're in, the place that they wanna get to, and how new technology is going to unlock new value for them with the challenges that they're facing. A 100%. Yeah. And, like, absolute the idea that people wanna spend time with, do business with, whatever the the relationship is, like, they you people wanna spend time with people that they respect, that they that they like. You know? That you're gonna be interacting with people on a daily basis. You're gonna be getting good news and bad news. You're gonna have to have the, you know, the agility, and and with that comes, you know, the trust that's very much resonates with me. I I found that that was, 1 of the best ways to ensure really solid communication was just establish that trust, at the onset of even before you've, you know, closed the job. You know, when you're in the RFP state and you're trying to make sure that that you're, you're getting awarded that bid is be the most likable client. Like, they just want to have another meeting with you. Like, even just starting there is is is a pretty great place to be. It it's funny you it's funny you say that, because I'm probably gonna be giving away 1 of my, my own secrets here, but that's okay. I'm coming from a place of abundance and being helpful. But to your point, Ben, you'll always get like, every single agency pitch team, will get the question, you know, why should I choose you, in the presentation stage? And it's always someone who's, like, asking the question and then sitting back in their chair waiting for you to squirm. I have found that answering that question in a way that just sort of lays everything bare to that point and creates a foundation for trust is the reason why we've been successful in those stages. And, you know, my my template answer for that question, it it it's essentially look. Everyone you're gonna speak to is gonna have a great body of work and great case studies and a great portfolio. We do too. Everyone is gonna come to the table with a methodology and something that is going to help derisk the project. We do too. But what you're not going to get in everyone and what you are going to get in us is going to be a team that is advocating for your business outcomes and your audience, and we'll have arguments with you. And we will have passionate discourse about the best possible solution, but we're also gonna be able to come back to the table the next day and like each other and continue to work together and make your project a success. Yeah. And that that is, like, the respect. It's like that radical transparency. We're not just gonna say what you wanna hear. You know, if you gotta hear the latest trend of technology, like, yeah. Oh, I know that you're gonna be jazzed by hearing this. So, like, it's really about having the trust, that the can and the candor to be able to say what's needed to be said to to get that project. Because, again, if it's not successful project for them, certainly won't be for you. You're not gonna have the client, you know, as a repeat business. Yeah. It won't be a great case study, etcetera. Nemani, I'm I'm curious your thoughts on that that same question, you know, how, how you over at Work and Co, you know, find find that initial tech and what you're really, assessing assessing it against. Yeah. So, for us, the most important thing is to really truly understand our clients' long term value objectives. I think that's the fits really nicely into the narrative of business requirements often being a lot more complex than the tech ones. We make sure that we establish a vision for the experience that we're building for both the team and the end users, and then we work with the clients to determine the KPIs. After we are sure that we agree on what problems we're trying to solve, we can guarantee that we're not trying to use technology to dictate how we're gonna solve the problem, but it that it's the other way around, that we're gonna use technology as a tool. And then there are several critical criteria that we use to evaluate technologies. Number 1 is always efficiency and effectiveness in solving the problem. Not every technology is good at solving everything. We've learned about that with Clojure, for example, an amazing language who solve a lot of really complex data manipulations, not great at other stuff. And user base and community adoption equally important. Are there people that you can talk to when you hit a a wall? Is there a is there a lot of libraries? Is there an open source community? Are there people that are evangelists for the technology actively speaking and promoting their their their tech? And then availability of resources and documentation, of course. Once all of this finds a like, an average market that's good, we try it out. We either build the proof of concepts that I've mentioned, trying to solve specific parts of problems, or we use the tool to, work on an internal initiative we have. We do this a lot, where we have a bunch of internal internal tools, where we try technologies or stacks out that fit seem like they could fit the problem nicely, and that gives us firsthand experience in some of the newer stuff that we wouldn't feel comfortable trying out with clients immediately. It's an investment, but it's a well well worth investment. Yeah. A 100%. It's interesting too. I think you kinda led by saying, you know, focusing on on the long term vision, the long term solution, etcetera. What how do you quantify that? Like, when I was, you know, working through similar projects, everyone has a different idea of long term. Some people, you know, sit on a digital experience that's built for a decade, then they come back like, hey. It's not working because things got deprecated. It's not even supported or whatever. Other people have a very rapid, you know, iterative cycle. But, also, there's, like, strata to what you're building. You know, to what what we build for clients, there's foundational levels that can, you know, have the longevity to to survive and sort of support multiple iterations of a project. And maybe the facade, you know, the front end or what have you, maybe that does change and this that's part of the stack gets swapped out more frequently. But, you know, on the whole, like, what when you're talking about, like, long term vision, I'm curious I'm curious what that means, to you in the in that dialogue with your clients. In that sense, it means more whether the emphasis is going to be on like, long term vision for me particularly means scale and performance more than anything else. Like, how do we see the platform, grow over time? Do we see a huge surge in in the number of users, the number of visits? Do we see the data spiraling out of the the capabilities of the current platform? So we have a very honest conversation at the beginning about life cycle of technologies and stacks, about the the need to update libraries and platforms and the need to maintain as if you would maintain any other machine. The fact that it's software and that it's abstract does not remove the the reality of it being a machine. But the context there is more of a are we building a small tool for a discrete number of users? Do you see this growing? Do you see this expanding both in terms of people using it, the data living in it, and the number of features? Do you see your business expanding into other areas, out of which this part that we're building now will be a, foundational framework for or maybe a part of a larger system? All these things help a lot with decision making. Overoptimization is the root of all evil, said someone really smart. I can't remember who. But the point is you are not gonna build a an insane event bus orchestration layer with Kafka for an app that's gonna be used by 100, maybe 1, 000 of people. It's redundant and unnecessary and a huge waste of time. So aligning on on what the build is the build strives to be down the road, helps a lot with cost, helps a lot with effort, and with the the technology choices as well. Yeah. No. A 100%. You said over optimization never I also think that optimizing too early. I mean, there's there's all sorts of gotchas, you know, in there. But now I'm I'm curious. Is there an example, you know, that that you can think of that you've, like, you know, pretty recently where you maybe had some issues that you had to overcome with with a client in these in this decision process? Yeah. 1 of 1 of the examples that, comes to mind is working, with a client partnering to transform their legacy technical ecosystem, making sure that it can better serve the needs of their increasingly digitally focused user base. And then a critical decision point was choosing between building separate mobile apps using bespoke technologies like Swift and, Kotlin, or going through a cross platform mobile framework like Flutter that we really, really like or, React Native. And, the decision was complex. It involved a lot of a lot of moving parts. The existing team capability, their goals, their capacity, their performance, and that long term vision that we mentioned, which is how they see the the product moving forward. Also, security, a really, really important point. Yeah. So the way we approach it is we set the stage by really clearly defining the opportunities and challenges of all the approaches and making sure they understand the context and the impact each approach will take. We define proposed architectures based on the feedback that we got from them. Again, developed a lot of proof of concepts to show how different technologies on different devices behave when solving the same problem, and then went with a decision that I think all of us, the clients and us included, were very, very satisfied by and got a great outcome. And it was a it was not an easy process, I would. It was really, really tolling to get to a point of mutual satisfaction, but I think what got us there is a very close collaboration. And, again, I'm like a parrot now, but a lot of really honest candid conversations where you have to be really clear. Yes. This is 5% faster and better, but it is going to increase the development time 2 fold or 3 fold. Or maybe saying this is faster, but once you hit, 1, 000 or 2, 000 concurrent users, we are gonna start seeing problems. So having these conversations happen in real time and being ahead of the curve and making sure that all the angles are met greatly helps with Yeah. Situation with this. And a lot of knowledge that you as an agency who has gone through this process at different scales, different customers iterated through, you know, okay. We built this, and now here's version 1.1 or 2.0 and the adjustments we had to make make. There's a lot of, you know, institutional knowledge there that you can give to, that client and make sure that you keep things on the rails that requires that trust. You know, they they have to understand that you have gone through this before, and you can, you know, hopefully learn just like in life. You can learn from those mistakes before you make them, you know, whenever possible. Steven, how about same thing for you. Like, have you have you had, a project recently where you were sourcing that tech and it just there was some sort of challenge or issue that you that you ran into that you had to try and figure a good way to work around? I think there's always challenges and issues. Yeah. A more notable 1. Yeah. I yeah. I I think we have been doing or, about to conclude a project that is a, a commerce, transformation, retaining legacy ERP and then migrating, the entire experience off of 1 commerce platform to another. And, you know, someone told me once the the, really great analogy. The organization has been speaking French, for, like, years, for however long they've been using this commerce platform. And they know French, and it's second nature to them. And moving on to another another commerce platform, yeah, it does the same thing. It's still a commerce platform. It's gonna do cart. It's gonna do checkout, but it's in Spanish. And so now they have to think in French and then translate to Spanish. And sometimes the way that you've been speaking and the way that you've been working for so long, even though it's a very similar context, it's an entirely different language, and you have to learn an entirely different language. And so 1 of the things that often comes up in in these scenarios is a client essentially saying, you know, but I wanna do it this way. And there's a limitation of the technology because of the opinionated nature of that technology that's essentially saying, well, you can't do it this way. And so then we need to come together as, you know, vendor or service integrator and and client and have a conversation about what is really driving, you know, this request and what is really driving this requirement. And when you understand what it is, then you can identify maybe a way to work around that. And then what you what ends up happening is to to Nemanja's point, a real candid conversation around the value of that workaround. And so it's, you know, we found in 1 of the in what in this example that about 5% of their revenue needed to perform a function that they were requiring. It came from this, you know, servicing of a customer. And so do they really wanna spend, you know, AAAA pretty hefty amount relative to the overall project cost to incorporate something that's gonna take in 3 to 5% of their revenue when they could probably just live without it and then find ways of servicing the customer in a different way that they won't find an impact on. So I I think it's again coming to, you know, really ensuring that you've got future proof and scalable technology from several factors. You know, the technology's adaptability to the evolving industry standards or the regulate regulatory environment, its ability to integrate with other systems, and really the provider's commitment to continuous improvement and support. And we look at all of that and its potential to leverage emerging technology. I mean, we're all talking about AI these days, and, you know, Directus has also gotten into the, into the AI game as well. And then looking at those regular reviews and updates, the technology road map, all of that coupled with those candid conversations and trying to find POCs that are going to help create workarounds, but then evaluating the POCs based on return on investment, I think that's, like, the way that that we approach it, and that's the way that we go. And that's a relatively recent as of, like, 2 weeks ago example. Yeah. It's a it's a big 1, and it's it can be hard to kinda make those decisions when you want to be improving. You wanna sort of modernize every ask you know, every corner of of your business, your process, etcetera. And sometimes, like, the ROI is not there. And, you know, when is the right time to kind of remove that technical debt, that legacy, part of the stack? You know, or, you know, do you ever you know, do you just kinda band aid it, add infinitum until, the ROI changes or it just no longer functions or, you know, there's there's a lot to that. It's interesting, though. I think you both have mentioned sort of this idea of, like, being opinionated or unopinionated. I'm curious, you know, Steven, you just mentioned making something future proof, which I I always I'll always kinda throw quotes around air quotes around that, and scalable, which I think is a big part of being future proof. You don't know how you're gonna scale necessarily. I mean, in an ideal world, you don't know because you could think things are gonna go amazing, they go even better. But how do you ensure that the the tools that you source are future, you know, future proof. And, you know, when you're thinking of things like, you know, the words like unopinionated or opinionated, I think both have, you know, pros and cons. You know, does that tie into this thinking, this, like, methodology for for choosing the Oh, yeah. The the tech? Yeah. Yeah. I I think that everything that I just mentioned around, understanding the technology's ability to, incorporate emerging technology, its evolving nature with industry standards, as well as, you know, regulatory standards. All of that, like, it has to it it creates the the the foundation and the guardrails for businesses to be operating in. And selecting a technology that doesn't take into account those guardrails or those foundational aspects of the business operating environment, I think, is a surefire way to surefire way to choose the wrong technology. Do do you feel that, like, again, just do you feel that something choosing tech that is opinionated and follows those specs, you know, that compliance and just, like, checks those boxes, you know, out of the box, so to speak, off the shelf versus maybe a tool that's more unopinionated, that allows you to take those those paths when needed or if those paths change, if there's a new spec that's introduced to you. You feel like there's a benefit to either of those options, or do they both kinda have their place, depending? I think they both have their place. I I think it's gotta be fit for purpose. Like, commerce is a great example. You do want it to be opinionated to some degree. Like, you don't want to be messing around with the world's best commerce platform, and then find out that your conversion rate is suffering. So in some respects, like, if you want to maintain your legacy stack, but you want to change your commerce platform as part of your architecture, then getting the benefit out of that commerce platform, you're going to have to find a way to make your technology stack and architecture work with that platform, if you want to reap the benefits of that. So that's, you know, a very, very purposeful and intentional incorporation of opinionated technology. But then in areas where you don't really know what the end goal is and you have a good inkling as to how you can foundationally get off the ground very quickly with an unopinionated and flexible piece of technology, then that's a a really, really big benefit. So something like Directus is a really, really great place to start because most people can create a frame of reference around Directus as, like, a CMS. And so when you're, you know, doing any kind of project, most projects require a CMS. And so you can anchor onto that frame of reference, but then because Directus is so amorphous, it can literally be anything you want it to be or need it to be. And it's got a very friendly microservices architecture. There's really no sort of cookie cutter approach to what you're gonna use Directus for. So that's a a really you know, in my in my experience, that's a good example of leveraging opinionated architecture for what it's intended for and then leveraging something that's as unopinionated as can possibly be to help you grow to what's next. Yeah. And then, ideally, both sides of the coin have strong points of integration so that they work well together. Exactly. Obviously, you wanna, you know, mitigate as much negative space as possible because that's where, you basically just have to start building custom to kinda fill in those gaps. So if you can have things bookend nicely together, and and play both sides of of that, that's sort of the ideal state. And, you know, side side note, you we we spoke a week or 2 ago, and you had mentioned you used that word amorphous to describe direct us, and I ran back to the team, and probably end up in all sorts of our our marketing collateral. It was just like a a really great way of describing it. Similar to sort of like nebulous, but nebulous has this kind of like more pejorative, like, what are we? What is this? And amorphous is is is just a great term. I love that. Nehemiah, you you had kinda mentioned a second ago, I think it was like prototypes, not presentations. When you are, you know, going out and and sort of pitching to clients, may could you talk a little bit more about that? Like, I'm I'm so curious about, like, that process. Like, what does that look like? You know, very aware of, like, what a prototype is, you know, versus a more traditional presentation. But how does that typically go with clients, and and how do they how do they receive that? Very well. I think the first of course, that's never the first step. The first step is gathering requirements and hearing about the problem, aligning on what we need to solve and what needs to be tackled. As we're focused on products a lot, very often, it's a an exciting segment of, the the customer's business or a new feature or a new, new product within their ecosystem. Then we go back to the drawing board, and we build something that we we show the clients. And I think that leaves the best impression, by far, seeing something, work. And we have amazing, extremely talented designers that can, in a very, very, I would even say, absurdly short time frames produce amazing work. Once, once you see something that you only have in your head, in front of you on a screen, that helps immensely. I I have never seen I've never seen feedback after a a, like, a keynote presentation as I have after a couple of framer framer prototypes. Then the tech comes into play, and then we have a very, very thorough and deep, initial discussion where we try to understand the the tech team composition, the capabilities, the current infrastructure, architecture platforms, and everything that's there in order to better propose a solution. But, yes, the the prototype comes not as a first, but as a very close to first step, where it's the wow effect that, really brings it home. Because, we are visual beings by nature. And, that's why I feel like every developer, every engineer needs to I am let's put it like this. I'm com I used to be completely void of any sense of aesthetics. So math, math, math, then physics and electronics in in university. And then I worked through it because we are visual beings and to be in the line of work we are, which is digital, everything is very, very aesthetic, and people respond to, amazing inspiring interfaces. So, that's where I think prototypes are key. Again, seeing these ideas that we all talk about in presentations come to life is the same with features. There's a lot of talk about AI now and the capabilities of LLM and the conversational platforms. Seeing a happy path prototype often says a lot lot more than a bunch of keynote slides illustrating our general capability. Just seeing a happy pet alum consult you on which product to buy, I think it sends a really strong message about capability, preparedness, and the the the talent within the team. Yeah. Definitely, if you go into it with the mindset of getting that massive knowledge transfer, like truly understanding, you know, the problems, the root the root, you know, things that need to be solved, the goals, etcetera. And you understand the business, the team. Once you have all that, you know, over at work and co or or UI, like, I think then you can kind of jump into that. It's it's interesting, though. I agree. You know, humans are so visual. Like, the idea of, like, inspiring through, like, you know, pen to paper, so to speak, is huge. But at the same time, like, I started thinking because that that's how I operate. You know, I'm very quick to get in and start just divergently coming up with with UX, UI, like prototypes. And I feel like that conveys so much, so much more than a presentation if you have that, you know, the initial work ready, you know, informing that process. But, also, it makes me think of, like, books to movies. I watch a lot more movies than I do read books. I you know, just time wise. But there's this idea of, like, you read a book and we're also imaginative creatures. Does it ever backfire? Because when you kinda go in with a prototype, that's like the movie. Right? You're sort of saying, like, this is this is what this could look like versus a book, and it's like, oh, that's I read the book, and then it was so different in the movie. And I don't know how I feel about that. Once you've kind of gone that direction of a prototype, do you ever feel like you are locked in because they've seen it and it's like, oh, this this is what we want, and then it that agility, that flexibility becomes a little bit more difficult? That's where the the experience of years of pitching like this comes in really, really handily. We build prototypes to solve specific problems, sometimes even specific parts of specific problems. So it's not such a, such a heavy buy in or such a huge commitment. The prototypes often are, vital and instrumental at solving this 1 key thing. And, 9 out of 10 times when we do show a prototype, we know that that's the direction that's ultimately the best for the product. So showing a prototype solving that 1 key thing isn't completely selling you into 1 solution. It's a learning experience, and, it's articulated by us being a lot more diverse and potent prototypes that were selling us into solutions. Now it's a a good balance of both. You explain, show capability, demonstrate previous. As Steven said, we all have really nice case studies to fall back where everyone's impressed at how we solved really complex problems for really great really great clients, but then for specific segments or of specific problems, I feel like that's the sweet spot sweet spot for prototypes. Not to committing, but great at showing capability and putting like, giving a visual aspect to the solution. Yeah. Oh, I mean, a 100%. Steven, I'm curious. So when we're talking about, like, we're we're presenting to the client, you know, sometimes thing things backfire. When when a client comes in, maybe they already have preconceptions. I guess this could apply to to either of you, but they have a preconception. This happened to me a lot. You know, I I used to joke, with with our team that it was, you know, 1 of the executives, like, they had a niece that, like, worked for a company. It's like, this is the bet. Like, I've heard so much about this and, like, that is the preferred stack or tech tech choice. You know, I think Carlo had a, who just posted in the in the audience questions had a similar, you know, how do you manage client expectations when the recommendation recommended technology has a steep learning curve? I I think there's, you know, all of these sort of questions around how do you get past, you know, when there's tech that might not be a good fit for the company or it is a good fit, but you're just not aligned. And and maybe trust isn't quite getting you there. You have to really dive in and and, you know, sort of or cover that somehow and and help bridge the gap of, like, yes. I understand that this is, you know, the the latest, you know, hype train tech, or I understand that you've heard a lot about this this other thing from a family member or whatever it might look like. But any thoughts on that, like, how you navigate that? So many thoughts. I do like, I I just wanna go back to, Nemanja's comments about, prototyping. I I think prototyping has a huge, huge amount of value in the work that we do for clients. Ben, I'm I certainly don't wanna poo poo your analogy on, you know, books and movies, but I think the important thing there is that, people can either create their own context or the people that they are hiring as experts can help create a better context. And if you leave someone to read the book, then they're creating their own context. They're creating their own imagination. They're filling in the gaps for themselves. Whereas the movie is telling them, like, this is what it is, and there's no imagination here. There's nothing, you know, left untold. This is the story, and, you know, you should pay attention to to this story. David Kelly, from IDO has got a great quote about prototypes. If a picture is worth a 1, 000 words, a prototype is worth a 1, 000 meetings. And so, like, you know, IIII do love the idea of prototypes because it does create that on ramp to trust. If a client's got a problem that is, you know, in their mind, immutably solve unsolvable, then, you know, a prototype from a experienced and professional team coming in to, you know, help highlight how it could be solved with proof of concepts or prototypes that, like, that accelerates the engagement massively. It creates a huge amount of trust. It helps solve a problem. It creates business value, and it's 1 of those things that clients can easily get behind because, again, going back to people, like, doing business with people they like, the person who you're working with has to look good in front of someone. And if you're helping them look good in front of their boss, like, that just solidifies and galvanizes the relationship because you're solving a business problem that goes beyond them. So I'll say that on on prototypes. Well, I just and IIII do agree because, again, we're hired as agencies to it's a full service agency, typically. You know, you are delivering a full suite of deliverables. And I think the the book and or, you know, the the the story itself is 1 piece of that, but it's our job as, you know, on the UX side, the UI side, the all these different pieces to be the cinematographer, to be the director, to to kinda give them the entire experience. Otherwise, you know, you're leaving it up to them. And maybe they're they're great and they have some great visions, but you're also probably gonna end up with a roomful of people who are all interpreting things quite differently. And And then you're gonna be navigating the people problem of, okay. Now we gotta try and wrangle everybody to the same page. Yeah. So I I think the the way the way that that we try to solve that is really trying to be proactive, right from discovery. I there's 2 things that, I will do within my engagements every single time. The first is is, an exercise called the graphic game plan. And, it was initially invented by, Grove Consultants, And, I leveraged this sort of storytelling visual of, hey. We're all we're all in our in our car, on our road trip heading towards this destination. The destination is project completion. What is going to be the measure of success for project completion? And then we put everyone everyone, including our team, the client team, puts their measures of success right in the center of that target. Then around that, you've got primary and secondary objectives. What do we want this experience to be? How do we wanna feel when we're doing the work? All of these sorts of things, We get them up there on the target as well. Then in the car are gonna be all of the stages and tasks that we need to do in order to achieve our objectives and achieve our our measure of success. Below the car, there's these bumps. And those are gonna be the challenges that we're gonna be facing along the way. And then the wheels on the car are gonna be the success factors. If everyone gets into a room and starts talking about success factors, challenges, all of the things that we have to do, the resources available to us, the objectives that we're trying to hit, then everyone owns the scope collectively. There isn't 1 side that owns the scope. And when you both share the scope, that's when you avoid things that I lovingly call climbing Mount Death March, where it's the vendor that is figuring out the scope of the project because it was ill defined at the beginning while they're developing. And, like, that is just the worst place to be in. So that's the graphic game plan. And the second thing that I'll do is ensure that we have stakeholder interviews from everyone in the company that could say no even at the 11th hour on the project strategy and the project direction. Because the 1 thing that every project should try to avoid is the swoop and poop. And that's, you know, when you're at a beach having a great day, team's doing great job, and a seagull flies in, shits all over your picnic, and then flies away. And then you're Good luck. There. What's that? I've heard that's good luck. That's a good thing to say. Trying to make you feel a little better about it. Yeah. Only the only the people that, they get crapped on tell you that it's good luck. Yeah. Only in the metaphor. Yeah. So those are the 2 things that we'll do to make sure that we've got alignment, right from the beginning. And that is probably the most important thing. Because if you take a flight around the world, around the equator, and you're 1 degree off course, by the time you get around, you're gonna be 500 miles off target. And that's 1 degree of deviation. So better to get that, you know, that heading right on target right from the beginning. Yeah. Yeah. I mean, it becomes rocket science. I mean, you the the more off you are, the earlier you're off, that just kinda compounds and you you're scaling problems. So if you can mitigate that, that's definitely something to be very aware of, in that early process. Yeah. Nemanja, I'm curious. Do you have anything similar, like, where you you've kinda had to deal with this issue of people coming in with preconceptions or trying to wrangle, you know, someone away from this idea that really just wouldn't work, but they're they're just struggling to to see the vision and to have that trust. Yeah. More oftentimes than I would like. But, I think that, you can't win them all. Sometimes just having a having done all of your homework, having done an extensive discovery phase, having validated your assumptions, done all the prototyping in the world, shown capability, discussed caveats, her hearing the client out and hearing their reasoning, seeing that there is no greater mystery behind why someone's as adamant as they are about a certain technology or not adopting a certain technology, the conclusion might, in the end, be that it's someone's need. And, that's okay. What we do in these situations is that we establish a very, very loud and very clear communication channel throughout the process from the first meeting where things are we're an agency, and we eventually perform a service for our clients. So if you've exhausted all of your options and if there is no other way to argue your point, you just have to be very, very clear and very direct and to communicate very frequently what the risks and consequences of a decision you believe are is suboptimal are. So making sure that this 1 degree deviation that everyone knows that it's gonna be 500 kilometers by the time we get back and alerting date, like, an alarm clock every hour. Like, saying in every retrospective meeting or every time there's an opportunity to discuss technical depth or to discuss progress, just making sure everyone understands the consequences of the choices that we believe were suboptimal. 100%. As I said, you can't win them all. There's no way that everyone's always gonna be aligned regardless of how good your argumentation is. But I think it's our responsibility as partners to make sure that we alert to these consequences as often and as early as possible. Yeah. No. A 100%. And I think, again, when things go south, the best thing that can happen is to already have established trust. You know, they they understand why the communication is there, and at least then it's you know, you you can hopefully navigate, through the project or whatever that ends up looking like. But, yeah, I we've kind of mentioned that word so much. I it really, you know, going back to, I think, the beginning of this, Steven, you had said you can only move at the speed of trust, which I took a note of. I think that's I think that's an amazing, sort of, like, way to kinda think about this and highlights the importance of that trust and communication, and it connects it to something that's very real. Like, when you're building a project, you've you know, speed. Speed is important. You can't you when I was running my agency, there was kind of 4, sort of levers that you really had to work with, and they were all I I think there's even a website where you can turn certain ones on and off. It was speed, scope, cost, and quality. And, of course, any good agency is gonna take quality off the table and say that's always gotta be good. We're we're not gonna sacrifice that. So you have these 3 toggles that you can kinda, you know, choose to choose your own adventure. And it's great to know that trust is a huge part of of the speed of the process, because, really, in the beginning when you're figuring out the scope and you're, you know, setting up what the cost looks like, you just efficiency and speed is is the name of the game. You know, how can you get through and and help to establish that and help the client really understand what their options are, that you are the right vendor, or, you know, partner to help them get through this. And I guess it all just comes down to to to that trust. So, we we've said that word a lot, and that's I think that's we we could probably rename this episode into, you know, establishing trust and, you know, the importance of trust at the agency level. But, you know, we we we ran over time a little bit, which is which is always good. We didn't even get through through all of our questions here, or, you know, I think there was a few that were thrown into the chat. But, any final thoughts, Steven Steven or Nemanja? Nemanja, why don't you go first? Yeah. I was just gonna say because it piggybacks nicely off of what you've just said. It's not only trust, I would say, it's partnership. Like, 1 of the things that got me, when I was deciding whether I'll go into agencies and service work or whether I'll go into, like, building a product startup or something like that was agencies, at their core, help solve problems, and you can't help if you're in an adversarial position. I really every relationship that we've had with a client that was hugely successful and that brought a lot of, for lack of a better word, happiness to me after the resolution was when we were true partners. So it's not like we're warning you this will fail because you're not listening to us. It's like, how do we make sure that since these are the the axioms that we have to work against, we fail less as a team. Like, being a partner to our clients is, I think, a lost forgotten art. Sometimes we get into a mindset of us versus them. I don't think that's a good mindset for agencies or tech providers at all. We serve technologies is there to serve solve problems. And, like, at its core, what we do is we facilitate. We use amazing tools to change the world. So partnership and not pointing fingers. The name of the game for me. That's hon. I that's exactly right. Open, trust, you know, those are all key things, but really the goal of those things is to, establish a partnership where you're you're in it together because, I mean, you truly are at the end of the day. Yep. Steven, thoughts on that or, just, you know, closing closing thoughts? Yeah. I I mean, I, I feel like Nemanja and I drink the same Kool Aid. It's 1 of those things where, for me, there was a very, very defining moment earlier on in my agency life where I I really just took on this mantle of being a designer is the most important thing in the world, and I would tell my clients, you know, the overarching responsibility of a designer and and how we're, you know, empowered to create amazing tools. The bottom line is they really didn't give a shit, about my my little speech about, you know, design. And it really sort of came to a head when I started changing the language that I was using around the value of the work that we do and what we bring to the table. And it was speaking my client's language, being empathetic to what their needs were and how they were articulating their business problems. And it was all in the language of business. And so if you can get out of your own way and stop speaking the language of design or stop speaking the language of technology and speak the language of business and how the tools that you wield are going to help solve their problem in a way that makes sense to them, that's when you actually engender really good partnership and really good, foundation of trust. And so I think that was a a really key moment for me in my, in my sort of growth and learning, as a agency owner and now someone who's in the, you know, the big 4 consulting world. Yeah. Couldn't could have said it better. I I again, these are all just huge pillars in how you how you kind of establish and foster, those those relationships. And then again, the the benefit of that is then you end up working with these clients, over and over and over again, and they just become huge advocates, for not only the stack that you help them select, you know, which, you know, we're a great great in a great position to be chosen by such a great you know, by so many great companies, but also, you just get to, have that repeat business and long enduring, partnerships with with great companies. So excellent. Steve and Damania, this was has been an awesome chat. Again, probably, we'll have to, like, circle back and maybe think about another 1 because a lot more. Again, this is the, you know, the whole reason for me being here, was was how how to solve these problems within an agency and trying to find the right tooling. And it's not always direct us. It's not, you know, that's the name of the game is, you know, just finding finding the right tool for the right job, which is not easy, as we say in an hour and 8 minutes in. But thank you both so much for joining, and we'll be seeing you at the rest of Leap Week for everybody else who's who's watching now. Thanks so much for having us. Absolutely. Alright. Thanks, everybody.","4b21caf7-40e8-4f5a-b7b4-7d23c2aba06e",[2429,2430,2431],"10147a57-dc26-4090-b5ea-d5ff4b089b04","a334f73f-c0c0-4021-8a9f-4f874eefea24","56797030-83a9-41f3-b194-791d0979c2d5",[],{"year":2380,"number":212,"id":2434,"episodes":2435,"show":2438},"d0ae16c8-ec29-4076-87e2-f4e0ef7383ea",[2436,2437,2417],"31c435b5-daf6-47c0-b158-9846dd808982","d74e3c5c-77dc-4f34-a247-2cb6cf7cee12",{"title":2439,"slug":2440},"Bridging Bytes","bridging-bytes",{"id":2321,"slug":2442,"vimeo_id":2443,"description":2444,"tile":2445,"length":1094,"resources":12,"people":12,"episode_number":527,"published":2446,"title":2447,"video_transcript_html":2448,"video_transcript_text":2449,"content":12,"seo":2450,"status":19,"episode_people":2451,"recommendations":2453,"season":2454},"social-media-platform","959732000","It’s social time. Join Bryant as he races to build a social media platform with registration, content feed and the ability to dish out likes and follows in just 60 minutes.","9aa4373b-1377-445d-acd3-e473766e6fd0","2024-06-28","Mission: Social Media Platform","\u003Cp>Speaker 0: Alright. Alright. Alright. Welcome back to yet another episode of 100 Apps, 100 Hours. I'm your host Brian Gillespie, developer advocate here at Directus.\u003C\u002Fp>\n\u003Cp>And I got a doozy today. It's one, honestly, I've not been looking forward to. So if you're new to the show, basically, we rebuild or try to build some of your favorite app ideas in 60 minutes or less, or publicly fail trying. I fail a lot. But that's okay.\u003C\u002Fp>\n\u003Cp>We learn something each time. There are only two rules to this. You have 60 minutes to plan and build, which always ends up not being enough time. And rule number 2 is use whatever you have at your disposal, kind of the anti rule. So with that, let's fire into this episode, the social media platform.\u003C\u002Fp>\n\u003Cp>Again, I like I said, I've not been looking forward to this. What are we gonna be building? We're gonna try to build something similar to Twitter. Where we've got a feed, post from different people that we're gonna follow on social. And, that is what it is.\u003C\u002Fp>\n\u003Cp>So, again, it should be an interesting episode. Let's get started on the clock. Start the timer and away we go. Right? So when I look at social media platform like Twitter, you've got a list of posts, you've got users that are on the service, You can follow people.\u003C\u002Fp>\n\u003Cp>You can favorite their posts. But as far as the functionality we're really looking for out of this, let's define it right when we go to plan here. We're going to, register users, which I've already got set up, inside my example project that I use all the time in these shows. We want to follow other users, create a feed of chirps. We won't call them tweet.\u003C\u002Fp>\n\u003Cp>We'll call them chirps. Sounds great. Let's shrink this down. This is gonna be our functionality needed. Alright.\u003C\u002Fp>\n\u003Cp>That seems like a pretty good functionality set for this. Again, yeah, I can't stress how much fun I'm looking forward to having on this episode. Alright. Now as far as our data model, you know, when you kind of break it down, there's the obvious ones like users. Right?\u003C\u002Fp>\n\u003Cp>So if I do some diagramming here, we have our users. These are actually gonna come from our directus users collection that directus gives us out of the box. That gives us auth and permissions and all the fancy stuff that we're really interested in. So we'll have Directus users and then we've got the chirps. As far as the chirp, it probably has, what, like some content.\u003C\u002Fp>\n\u003Cp>Maybe there's an image or a video. You know, this could just be a file if we wanted to share that. And then a timestamp or, created at date. Right? Those are our chirps.\u003C\u002Fp>\n\u003Cp>Those have a relationship back to the user. There's a user that chirped, so we can represent that. And then, what else are we gonna have here? We're gonna have some followers. Alright.\u003C\u002Fp>\n\u003Cp>So, basically the the users are gonna follow themselves, but because that's a many to many relationship, we're gonna need like a separate table for this. So when we talk about this, we're gonna have a follower follower ID. So this is I am the follower. The followee. I always hate naming stuff.\u003C\u002Fp>\n\u003Cp>Followee ID, followedid. So again those are gonna be a two way relationship with users. And then, what else do we have? Right? The other part that I'm not a 100% sure about, we've got this feed that is unique for each user.\u003C\u002Fp>\n\u003Cp>Right? So to me that means a separate collection. So we have feeds, we've got a user that's associated to the feed. And then what else do we have? Right?\u003C\u002Fp>\n\u003Cp>We're gonna have followers, feeds. The feed is gonna be a or like updated at. When was the feed last updated? And trying to think of, like, the other paradigm here. We're gonna just have a, like, the actual tweets that are or chirps that are in that feed.\u003C\u002Fp>\n\u003Cp>Alright. And then that means we need, like, a junction table as well, feed chirps. So this will be the chirp and the feed. Alright. So I I think this is the the data model that I wanna go with for this.\u003C\u002Fp>\n\u003Cp>We'll see how this actually plays out. As far as what I've got set up, right, is the standard config that I use for all these episodes, if you've ever caught one of the past episodes. I've just got a little Nuxt starter application that has a a couple of routes, like a register and a login. Let me just clear my cookies so we can see what those look like. Right?\u003C\u002Fp>\n\u003Cp>Just a register and login route. I've got a Directus SDK plugin configured here. So we just create a REST client. There's also a real time client in case I decide that we need to use real time in this. And then on the other side of it, I've got a blank direct assistance, just a single admin user.\u003C\u002Fp>\n\u003Cp>So, alright. Let's get to work on our data model. That's what we're gonna bang out first. And like I said, we've already got direct us users here. Let's work on our chirps.\u003C\u002Fp>\n\u003Cp>So that will be the first collection that we create, and I could zoom way in inside the Directus instance here. And do I really am I concerned with the primary key field for these being UUID or auto incremented integer? You know, typically, I'm choosing UID, but in in this case, you know, maybe integer is is fine really. For created on, we're gonna do created at. And created by, we're just gonna do the user I'm gonna call that user ID.\u003C\u002Fp>\n\u003Cp>So whatever user creates this chirp, that'll be saved in the database. And then whenever they create this chirp, will be saved as well. Once the chirp is out there, it is gone. We are not going to allow editing or anything like that. And as far as our content, maybe we want to support markdown for this.\u003C\u002Fp>\n\u003Cp>Message content. Chirp content. Sounds good. And then, you know, let's get wild with it and maybe even support a file. You can upload a single file to your Chirp to share.\u003C\u002Fp>\n\u003Cp>Alright, so that's our model. I'm gonna unhide these other fields here just so I could see these when I look at them. And next I want to I'm just gonna chirp. Chirp chirp. Save it.\u003C\u002Fp>\n\u003Cp>And you'll see that saves the admin user and the date, and now we've got our first chirp for this social media platform. I don't know what we're gonna call this. Why? Why not? I think why is the example that, our CTO, Reich Van Zanten, used in one of his talks.\u003C\u002Fp>\n\u003Cp>So, alright. What's next? We're gonna do our followers. Right? So we have our followers.\u003C\u002Fp>\n\u003Cp>And in this case, we have got, really not concerned with any of those other fields. What we're gonna do, I want to go into my data model. I'm I'm gonna go to our system collection, right? And we're gonna do a Directus users, and we're gonna go in and create a many to many relationship here. It's gonna be followers.\u003C\u002Fp>\n\u003Cp>We're gonna go to the followers table. And what do we have here? We've got the directus user. This is the follower followee ID. This will be the follower ID, I think.\u003C\u002Fp>\n\u003Cp>Alright. Is that gonna work out? We got followers. We're good. And then we could add, oh, that's our where do we have that?\u003C\u002Fp>\n\u003Cp>Follow words collection. Yeah. We don't need to add that. Right? Great.\u003C\u002Fp>\n\u003Cp>And then if we delete any of these other items, we're basically just going to like if somebody if a user gets deleted or a follower gets deleted, we're just going to remove those. So okay. I hope that's gonna give us what we're looking for. So now we've got a followers table and junction direct us users followers. Oh, okay.\u003C\u002Fp>\n\u003Cp>So it's created that extra table for me. Why did it do that? Because I have a relationship there? That's some unexpected behavior, but that'd be okay. We'll roll with that.\u003C\u002Fp>\n\u003Cp>So on our users, this table is pretty much worthless, we'll just remove that. Unexpected error. Okay, fun stuff as always. Let's let's try this over again, right? We've got followers, we're just gonna delete that.\u003C\u002Fp>\n\u003Cp>Now that table is removed. We'll do another mini to mini. And we'll call it followers. And actually the related collection here we want is direct us users and not followers. So that's where I goofed up originally.\u003C\u002Fp>\n\u003Cp>And we're gonna call this followers. That'll be our new collection. We got the followee ID and then the follower ID. And I think this will get us what we want. So this will be followees.\u003C\u002Fp>\n\u003Cp>Alright. Let's try that. See if that gets us what we want. So now we have followers and followees. And then we have a table called followers that we can inspect and make sure everything is looking correct.\u003C\u002Fp>\n\u003Cp>Alright, great. So we've got that set. Now we're gonna work on our feeds, right? Whenever I want to, I like check my feed, I I need some way to store the posts that are going into that specific feed. So we'll have a feeds collection.\u003C\u002Fp>\n\u003Cp>Again this could be auto incremented. And for the feed side of it, basically we're gonna have a when was this feed updated? Let's do updated at. Just carry that same one. I I really love these optional system fields inside Directus.\u003C\u002Fp>\n\u003Cp>Again, it just gives you some of that functionality that you need like time stamps and status and things like that baked right out of the gate. Alright. So we've got a feed. We've got updated at. We've got a we're gonna have to add a user to this feed.\u003C\u002Fp>\n\u003Cp>Right? So this is a user that's gonna be a mini to 1 relationship to the user's collection, direct to users. And then if I open up the advanced settings for for any of these fields that I'm creating, I could go in and create the corresponding field. Right? So I can add additional feeds for this specific user.\u003C\u002Fp>\n\u003Cp>You know, maybe we wanted to subscribe to different topics or something like that. So on delete of directus users, we wanna delete the feeds item because there's no need for a feed for a user that is not around, right? And when it comes to our display template, I can also go in and control what this will actually display here as well. Just a nice thing if you're doing a lot of editing inside the Directus Data Studio. So we got the updated at.\u003C\u002Fp>\n\u003Cp>There's the user. And then we're gonna need another relationship between the chirps and the feed, right? So we need to know what are all the chirps within that specific feed. So, how are we gonna do this? That'll be a many to many relationship again.\u003C\u002Fp>\n\u003Cp>Because a one chirp could be in many different feeds. So we'll go here inside Directus, we'll just search for the many to many relationship, right? And we are currently where? We're on feeds. So this will be our chirps.\u003C\u002Fp>\n\u003Cp>We'll add chirps as the related collection here, we'll show a link. And I'm gonna go into the relationship under advanced settings in case I want to add this, right? This is the feed ID, this is the chirp ID. Sounds great. Looks good.\u003C\u002Fp>\n\u003Cp>And then we can add the inverse relationship that we want. So we could look at a chirp and see all the feeds that it's involved in. Not sure we necessarily need that either. As far as the sort field, we're just gonna sort by the the timing of the specific chirp. Alright.\u003C\u002Fp>\n\u003Cp>So I with that, I think we've got our data model, like, pretty fleshed out here. We got the users already. We got chirps. We got followers. We got feeds.\u003C\u002Fp>\n\u003Cp>Cool. As a user, I could go in and I could follow someone, followee, except I don't have any actual users to follow at this point, right? So if we turn our attention to the front end of the project, I can go ahead and register a new user, Right. Let's call this edonbusk@example.com. And we'll give this a very secure exam model.\u003C\u002Fp>\n\u003Cp>Okay. Alright. So if we go to log in now, we should see we're already logged in. Great. Do I see a user here?\u003C\u002Fp>\n\u003Cp>I do. Elon Busk. Should've added a first name and last name to the actual registration form. Cool. So now I've got users that I can actually follow inside this.\u003C\u002Fp>\n\u003Cp>So if I go into my admin user, I should be able to add Elon Busk as one of my followees. Man, that terminology is gonna trip me up the whole time. I should have done something else. Following or something like that. Alright, so now, we don't have a feed for this.\u003C\u002Fp>\n\u003Cp>So one of the things that we could do inside Directus, whenever a a new user is created, you probably want to add a feed for them, right? So a couple ways you could go about that. One way is just flows, right? So flows are automations inside Directus that we can create. And I could trigger these based on a number of different actions.\u003C\u002Fp>\n\u003Cp>So we're just gonna do a create profile or create feed for new users. Okay. As far as a trigger setup we're going to do an event hook. And the action we want to be is non blocking. And should be whenever we is there a way to do it?\u003C\u002Fp>\n\u003Cp>Whenever a user is created auth.create. Auth.update. So whenever items are created and I just am going to do the Directus users collection here. So we're gonna trigger this whenever a new user is created, right? If I go in and I'll just throw these up side by side.\u003C\u002Fp>\n\u003Cp>Right? I'm already logged in. Let me just destroy this. Now we'll register a new user, new user at example.com. New password.\u003C\u002Fp>\n\u003Cp>User has been created. If I refresh this we could see that. Great. We can see there's the user, there's the key that we're interested in. Alright.\u003C\u002Fp>\n\u003Cp>And now I'm just gonna go in and create a feed for this, create feed. Alright, so there's our feed. And as far as the feed data model, I'm just gonna duplicate this because I do not remember what that looked like. Hazard of the gig. Right?\u003C\u002Fp>\n\u003Cp>So if I look at the feed, we've got a user and we've got chirps. So we'll just have one param that we're gonna pass. And we could even add like a a system message if we wanted to. But for now let's just create the feed. So we'll add an operation for it, we're gonna create data, we're gonna create a feed.\u003C\u002Fp>\n\u003Cp>And then inside the payload we've got the user key. And then we're gonna dynamically pull information from the trigger for this. So trigger dot key. And we'll just use that mustache syntax. And this should get us what we want.\u003C\u002Fp>\n\u003Cp>We'll do full access. Let me just check that one more time. Trigger dot key. Okay, great. Save.\u003C\u002Fp>\n\u003Cp>And now let's try this again. Right? I see 0 feeds in here. I'm just going to remove my session token. And we'll try john@example.com.\u003C\u002Fp>\n\u003Cp>Password. So we should have a John and example here. And we can see we've got a feed for that user now as well. Great. Perfect.\u003C\u002Fp>\n\u003Cp>Alright, so if we go back to our functionality that we need, we can register users, right. Do we have the ability to follow other users? In this case, yes, we do. We don't have it wired up to the front end yet. But let's let's stay focused on Directus, right?\u003C\u002Fp>\n\u003Cp>If I am following someone, and this is not showing up really nicely. So let's just edit the interface for this. We are going to show the avatar thumbnail with a first name, last name. And I can just copy this display template here and I can do that same thing here. So the the difference between the interface and the display, the interface shows up on the form itself.\u003C\u002Fp>\n\u003Cp>So when you're in that detail view, the display shows up on the layouts. So if I'm here looking at a list of followers, this is the display and this is the actual interface, right. So let's go back into our followers and we'll we'll fix this one as well. Should just be able to copy paste the same thing. Great.\u003C\u002Fp>\n\u003Cp>Cool. Cool. There it is. We got our followers, followees. Now if I am following Elon Musk, great name by the way, anytime he chirps it should populate into my feed, right?\u003C\u002Fp>\n\u003Cp>So, if I look at my feed, I don't have a feed yet, So let's create a feed for me. And here's, here's my feed. I can't really tell which one that is, so we'll just fix that really quickly as well. We'll show a display the direct us user. Great.\u003C\u002Fp>\n\u003Cp>Cool. Alright. So now I can see there's my feed, there's John's feed. Anytime Elon Dusk tweets, I want to populate that into, a feed. Right?\u003C\u002Fp>\n\u003Cp>Or let's say if I go into John's user, for example, John Doe, maybe John Doe is following me. So if we add me as an admin user, that'll be a good example. Right? If I chirp, chirp again. Right?\u003C\u002Fp>\n\u003Cp>This is, again, this is just writing it to the SQL database. Direct is is a is a nice complement to that. Like whatever changes I'm making here being mirrored. But, I chirp. If I go to the feed, nothing happens for John Doe.\u003C\u002Fp>\n\u003Cp>Right? So whenever a new chirp comes in, I keep wanting to call it a tweet, I want to basically populate that feed for all the other users that are, following that user, right? So again, I could reach for flows on this, populate feeds. Now at the scale of something like x or or Twitter or Facebook, this is going to probably break really quickly. Because, you know, you could have millions of followers and, you're gonna want to look at other solutions to scale this.\u003C\u002Fp>\n\u003Cp>But again, this is what we can build in an hour, so let's do that. So whenever an item is created, a chirp, we're gonna do some logic on that. Right? What are we gonna do? So as far as the logic, if we just map it out.\u003C\u002Fp>\n\u003Cp>Right? New chirp. We're gonna get the user from the chirp. User from the chirp. We're going to find all the users who are following that user push chirp to their feed.\u003C\u002Fp>\n\u003Cp>Alright, seems relatively straightforward. Whenever this chirp occurs, right now that we've got this flow set up, let's just go and test new flow. Hit save. Go back to our Flows, and now populate fees. We should have a payload.\u003C\u002Fp>\n\u003Cp>Right? We've got the content. We don't see the actual user, that is in our accountability object. Right? So we need to basically get a list of all the followers and their feeds.\u003C\u002Fp>\n\u003Cp>So how are we gonna do that? We are going to get a list of feeds. So we're gonna read data. Let's do like get feeds here. We'll do from the we'll do full access.\u003C\u002Fp>\n\u003Cp>We want to basically get a list of feeds. And the feeds here, we're gonna do a filter where the, we want to dig into this, right? So if we look at our other items, if we look at our data model, right. If we're getting a feed, there are there's a user for that feed. And then we want to inside the user, we have the followees.\u003C\u002Fp>\n\u003Cp>Actually, we wanna get all the followers and then their associated feeds as well. Okay. So let's go through that route instead. We're gonna get a list of all the followers where the follow e id is equal to the dollar sign accountability dot user. And for that we're also going to get a list of fields and we want to dive in and actually go through the nesting here.\u003C\u002Fp>\n\u003Cp>So Directus allows me to basically populate all the relational data in a single API call. So we'll do the root level fields, and then again if we're going through the followers we've got a follower ID. So we'll do follower ID, that'll get us to the user, dot feeds. Is that what we want? Follower ID dot feeds.\u003C\u002Fp>\n\u003Cp>And that should give us a list of the feeds that we want to populate. Alright, let's test this out and see. Alright. So if I chirp, the expected action here for this flow it should give me like John Doe's feed ID, if I've got that set up right. Right, because John Doe has feeds here.\u003C\u002Fp>\n\u003Cp>Alright, let's test it out and see. So we'll do show up in John John's feed. Hit save. And let's go test this flow. Alright.\u003C\u002Fp>\n\u003Cp>Alright. Less than a minute ago. Let's populate. Alright. So here's all of our feeds that we've got.\u003C\u002Fp>\n\u003Cp>We can see there's the feed, there's the or those are the followers actually. And then we need to collate all these feeds. So I'm just gonna copy this over here into Versus Code. And the next thing we're gonna do, we want to push that chirp into those feeds. So we're going to update a list of feeds and push the chirp into those feeds.\u003C\u002Fp>\n\u003Cp>Alright, so how we're gonna do that? We will go in and so we are going to update the feeds. And actually we probably need a like an intermediate here. So we're gonna return let's format the feeds. Alright so if we look we are getting well we got git feeds.\u003C\u002Fp>\n\u003Cp>So that's gonna be accessible through our data object as git feeds, Alright. So here's all the feeds, that is data.gitfeeds. And in that feed where you're wanting to map through those and we're going to return an array of the feed IDs that we want to push this tweet into. Alright so feeds to update equals feeds dot map. That's gonna be our feed.\u003C\u002Fp>\n\u003Cp>And then we're going to return what the feed dot follower_id feeds the first item in that array. Feeds to update, and then we're just gonna return feeds to update. Oops, gotta spell that correct. Alright, so just a little bit of formatting logic to make this easier. And then now what we're gonna do, let's call this get feeds ID.\u003C\u002Fp>\n\u003Cp>Get feeds ID. And that should give us an array and now we're gonna update those individual feeds. So we'll do update feed, update feeds. The collection is gonna be feeds from full access. And then as far as the IDs here I'm just gonna pass this array.\u003C\u002Fp>\n\u003Cp>So get feeds, Should've put IDs at the end of that, but no worries. And then we are going to do what? Can we do the can we do the array syntax here? Create chirpid. I think we can do this.\u003C\u002Fp>\n\u003Cp>So this is gonna be in, we need chirps. So that's the field that we're gonna update within the feed. And then we're gonna use the create update syntax here. So we'll do something like this, create. So instead of like modifying the value of these chirps, alright, we're gonna create a new record inside that array, which is gonna be what?\u003C\u002Fp>\n\u003Cp>What is that gonna be exactly? So we'll go back. Chirps are in our feed. Chirps, a chirp is what, just pass the chirp ID. Alright.\u003C\u002Fp>\n\u003Cp>And the chirpid is coming from what, trigger.key. Create trigger.chirp_, actually trigger.key. Alright. Is this actually gonna do what we wanna do or not? We'll see.\u003C\u002Fp>\n\u003Cp>Alright, let's test this out, right? So now if we take a look at our feed, let's do this side by side. Alright. So we'll go back over here. How are we doing on time?\u003C\u002Fp>\n\u003Cp>I don't know if we're gonna get to the like any front end stuff on this or not. Alright. So we got our feeds. Nothing in John Doe. Now if I chirp, please show up in feed.\u003C\u002Fp>\n\u003Cp>Refresh. It's not so there's something wrong with our logic here. Let's take a look at what's going on with this. So we got our feeds, run script, syntax missing. Forgot some type of feeds.\u003C\u002Fp>\n\u003Cp>Oh, of course forgot a parenthesis, that'll always do it. Alright. So let's just test again. Test again, please. Alright.\u003C\u002Fp>\n\u003Cp>If I refresh the feed, do we have the feed? We don't see any chirps in that feed over here. So something else is going wrong. There's our invalid payload. It must be of type 1 object.\u003C\u002Fp>\n\u003Cp>Would it just be passing the ID? ID. Let's add this in. Test again. Test one more time.\u003C\u002Fp>\n\u003Cp>Save. Refresh this. Okay, okay. Did we test one more time? If I look at the feed, we've got a feed for John Doe.\u003C\u002Fp>\n\u003Cp>It's not exactly what I wanted. It's pushing a new, I guess that would be like update instead of create. Update. Let's look at the directus docs actually. Create update.\u003C\u002Fp>\n\u003Cp>That's gonna be adding items, I think. Creating multiple items. Relationships. Where is this gonna be? Global parameters.\u003C\u002Fp>\n\u003Cp>Create an item. Relational data. Okay. So assign the existing item to be a child of the current item. You can use the same structure to select what the related items are.\u003C\u002Fp>\n\u003Cp>Simply omit them from the array. So we just wanna add an item in the array. You can provide an object detailing the changes. Like we don't wanna create a blank chirp. Right?\u003C\u002Fp>\n\u003Cp>Is this what happened here? No? Why is this chirp not showing any content? So it does show up in John Doe's feed. It doesn't show the actual content, which is weird.\u003C\u002Fp>\n\u003Cp>Let's just test one more time. So what is actually happening here? This is the chirp. Oh, duh. That's what's going on.\u003C\u002Fp>\n\u003Cp>Chirp underscore ID. So we're gonna create, and it should be chirp underscore ID because we've got to go through the junction collection. Alright. So we're gonna create a new item. And this should is that gonna solve this worse?\u003C\u002Fp>\n\u003Cp>Let's see. Alright. Chirp. We'll just test this. Feeds.\u003C\u002Fp>\n\u003Cp>We hit save. Now we see there's the chirp. Baller. Okay. So now that is working as expected, right?\u003C\u002Fp>\n\u003Cp>So if I now chirp, I'm the admin user and John Doe is following me. Follow me, John. I hit save. That shirt should show up in John's actual feed. Amazing, right?\u003C\u002Fp>\n\u003Cp>It doesn't show up in anybody else's feed. Awesome. Okay. So now that logic is working as intended. So we have all that working.\u003C\u002Fp>\n\u003Cp>Create a feed of chirps. We've gotta follow other users. We've got all this functionality. Right? Let's try to put together something on the front end for this.\u003C\u002Fp>\n\u003Cp>So what is this gonna look like? I'm using Tailwind and I've got the Nuxt UI library inside this thing. Tailwind Twitter clone maybe. Search for that. Tailwind Twitter clone pages, Tailwind clone with CSS.\u003C\u002Fp>\n\u003Cp>Hey, there we go. This is probably close enough. Right? It kinda looks a mess, but let's go for it. We're just gonna copy this code.\u003C\u002Fp>\n\u003Cp>Let's create a new page. Let's just call it feed dot view. Do some script setup. Oh, that didn't give me what we want. Script setup lang equals ts.\u003C\u002Fp>\n\u003Cp>And alright so now we're gonna add this thing here. This is a giant mess of HTML. Let's just load up our user over here. Let's get logged in. Are we logged in right now?\u003C\u002Fp>\n\u003Cp>We're missing a tag somewhere. Div div. Div. Where is this thing? Oh, see it's not even a particularly great clone because there's some missing div somewhere.\u003C\u002Fp>\n\u003Cp>Great. Lots of fun. Don't ever trust these templates that you see online. Let's still where's the issue? Is it that one little div there?\u003C\u002Fp>\n\u003Cp>Where is the issue? Div. Div. Div. Div.\u003C\u002Fp>\n\u003Cp>You know what? Forget it. 1st tweet start. I don't even tired of messing with this. Alright.\u003C\u002Fp>\n\u003Cp>So let's just go back to our index page. Wasting too much time to actually get anything done here. Alright. So we're gonna have a, what, list of tweets, chirps equals, we're gonna use the await. Use async data.\u003C\u002Fp>\n\u003Cp>We're gonna give it a key. This is the user chirps, user feed. Alright. And then we're gonna do return. We're gonna import our we're actually gonna grab that from our used Nuxt composable.\u003C\u002Fp>\n\u003Cp>So used Nuxt app. Okay. We'll await the user feed. And then we're gonna import the read items from the directus SDK atdirectus SDK, and we're gonna return directus dot request read items. Wait.\u003C\u002Fp>\n\u003Cp>Use async data. Return direct us read items. Chirps. Actually, we're gonna get the feed. Where the actually, let's get all the chirps.\u003C\u002Fp>\n\u003Cp>Chirps where the feed So if this is my user, and I've got the chirps, We wanna get the feeds, actually. Get the feed for my user where the, do I have the do I have a composable in here for the user? Where is my actual user state in this application? Let's take a look at our actual state. We have a user.\u003C\u002Fp>\n\u003Cp>We have a user ID. Okay. So we're also gonna do the user equals use state user. Okay. And then we're gonna get all the chirps where the user oh, we're gonna get the feed where the user is the current user.\u003C\u002Fp>\n\u003Cp>And actually, like a a lot of this could be controlled via access control here inside Directus. So I could just see I could see anybody's chirps. I could create chirps. I can't delete any chirps. But as far as the feed, I can only see my feed.\u003C\u002Fp>\n\u003Cp>So let's just adjust this. Alright. The permissions so I can only see my feed. So the user dot ID is equal to current user dot id. Alright.\u003C\u002Fp>\n\u003Cp>So now if I just do this, honestly. Feeds, chirps. Alright. We'll refresh, failed to resolve, directus sdk@directus\u002Fsdk. Alright.\u003C\u002Fp>\n\u003Cp>So and then maybe we'll just log those out. Pre chirps. Do we see any actual chirps data? Oh, actually gonna return that. We need to deconstruct that.\u003C\u002Fp>\n\u003Cp>Chirps. Okay. So there's our feed. And we actually want to see all the chirps that are in that specific feed. Right?\u003C\u002Fp>\n\u003Cp>So now if we log in, I'm not exactly sure who I'm logged in as. And let's update our user role inside Directus to where we can at least read the item permissions. We wanna be able to read our own user. So ID equals current user. And what's that gonna give us?\u003C\u002Fp>\n\u003Cp>Refresh. Alright. So I'm not logged in. If I log in as John Doe here I don't even know what password that I gave John. Pretty sure I do know.\u003C\u002Fp>\n\u003Cp>Example. Okay. So we're gonna go back. If we refresh, do we what do we see? Not seeing anything, actually.\u003C\u002Fp>\n\u003Cp>Default invalid user credentials. If we take a look at Wait. Use async data. Data feeds. It's gonna be the feed.\u003C\u002Fp>\n\u003Cp>Let's just move this logic somewhere else. Killing me. K. Index. Just go somewhere else with this.\u003C\u002Fp>\n\u003Cp>NuxLink. Or, view button to feed to the feed Batman. Alright. So there's our feed. Why don't I see any actual chirps inside there?\u003C\u002Fp>\n\u003Cp>To the feed, Batman. And we're also not getting this stuff on the actual server side as well. So that's another thing to figure out. But our access control settings here are preventing us from seeing the actual feeds inside the the chirps inside the feed. We probably wanna be able to see followers or what as well.\u003C\u002Fp>\n\u003Cp>If we go to the feed, now, can we actually see any of this project to the feed? Just totally blow this away. Got 9 minutes left on this episode. Really struggling here to kinda sort this one out. So this is our user's feed.\u003C\u002Fp>\n\u003Cp>We should be able to see the actual chirps within that though, and I'm not sure why that is not showing up. So there's the feeds, direct us users. You know, if I were to just do something like this, local host 8055\u002Ffeeds, should be like actually items slash feeds. So there I can see the actual chirps that are coming in. But is that because we got, like, caching going on here?\u003C\u002Fp>\n\u003Cp>Cash. Is there, like, a cache? User ID. Okay. So now we can see the chirps.\u003C\u002Fp>\n\u003Cp>Okay. Let's actually show the chirps themselves fields. ID, we probably want the user information, and then we'll get the chirps. Chirps. And let's just get all the the details of the chirps.\u003C\u002Fp>\n\u003Cp>Okay. So those are the the that's the junction collection. We really want the chirp dotchirp_id dash star. Okay. So now we've got a list of the chirps.\u003C\u002Fp>\n\u003Cp>Let's just iterate over that. We'll do a, dev4. What, chirpinfeed.chirps. And we can actually, like, maybe deconstruct this a little bit. Chirp underscore ID as the chirp.\u003C\u002Fp>\n\u003Cp>Key is gonna be chirp dot ID. Thank you. Then we have the chirp content. We also probably want the user ID from the chirps. We wanna know that that specific user.\u003C\u002Fp>\n\u003Cp>And in that case I'm probably gonna need to adjust my permissions as well. So access control, we'll go into users. For here, we'll just allow all access, but let's just restrict certain fields. Alright? Can't see anything except for the ones that I want.\u003C\u002Fp>\n\u003Cp>First name, last name, avatar, we don't wanna show email. We'll show ID. Followers, followee. Okay. Alright.\u003C\u002Fp>\n\u003Cp>Chirps dot underscore id.user. What are we missing? Oh, chirpsfeed.chirps. Let's just back up a minute. Feed.\u003C\u002Fp>\n\u003Cp>Pre let's throw this up here. And I'm just gonna, like, show this. Let's refresh the page, see what we got going on here To the feed. Alright. Chirps, chirp ID as the chirp in feed dot chirps.\u003C\u002Fp>\n\u003Cp>Dot content. This should work. Not sure why it's not. Right. V if feed and feed dot chirps.\u003C\u002Fp>\n\u003Cp>Feed. Oh, duh. We gotta get the first item of the feed. Transform data data dot 0. Okay.\u003C\u002Fp>\n\u003Cp>Definitely a frustrating frustrating run here. Transform data. That should give us what we need. My gosh. Return, comma.\u003C\u002Fp>\n\u003Cp>Still not. Oh, this is okay if I put this in the right spot, what a mess. Okay. I think that that should have it now, and we got a grand total of, like, 3 minutes left anyway. Do we have a feed?\u003C\u002Fp>\n\u003Cp>Yeah. Okay. So there's the chirps. Man, when we fail, we spell spectacularly sometimes. Alright.\u003C\u002Fp>\n\u003Cp>So here we go. We've got our chirps. We're showing a feed of those chirps. We've got the user underscore ID. So we refresh that.\u003C\u002Fp>\n\u003Cp>Now we could see the actual user that is chirping about that. So and we'll have a divv4. Let's do like a flex, flex call, give these some gap. We'll give each tweet some padding. We'll show a div.\u003C\u002Fp>\n\u003Cp>We'll do the username. Username chirp dot user ID or underscore user ID. Admin user. There's all of my my actual feed. Great.\u003C\u002Fp>\n\u003Cp>And, this is really really lovely. Now what if we wanted to actually chirp a bit? Right? We would create a new function, async function. Doesn't even actually make sense.\u003C\u002Fp>\n\u003Cp>We got a minute 30 left. I'm gonna go ahead and call this one a fail for today. We're gonna call this. We've got the direct us in set up really nicely on the the front end. We just really struggled a bit.\u003C\u002Fp>\n\u003Cp>You know, just an off day for me. Happens every once in a while, right? So we've got our chirps over here. The feed is is working as intended, right? If I go in and if I were to set up another follower, like if Elon Busk follows me, admin user.\u003C\u002Fp>\n\u003Cp>Save that. Obviously, like he needs a feed as well. Elon Busk. Great. And then if I chirp again, chirp for Idan.\u003C\u002Fp>\n\u003Cp>In his feed, we can see that one shows up. So there's our chirp. We can see that showing up there, which is nice. Chirp to eat on. That chirp also showed up inside John Doe's feed.\u003C\u002Fp>\n\u003Cp>So that part of it, we nailed UI. Do the explosion here. 10 seconds left. Yeah, I I think this is a good exercise from a data modeling standpoint and kind of a how to put this together. Just wasn't all there on the front end today.\u003C\u002Fp>\n\u003Cp>That's the way some of these things go. That's it for this episode of 100 apps, 100 hours. Hope you'll join me for the next one. We'll see you.\u003C\u002Fp>","Alright. Alright. Alright. Welcome back to yet another episode of 100 Apps, 100 Hours. I'm your host Brian Gillespie, developer advocate here at Directus. And I got a doozy today. It's one, honestly, I've not been looking forward to. So if you're new to the show, basically, we rebuild or try to build some of your favorite app ideas in 60 minutes or less, or publicly fail trying. I fail a lot. But that's okay. We learn something each time. There are only two rules to this. You have 60 minutes to plan and build, which always ends up not being enough time. And rule number 2 is use whatever you have at your disposal, kind of the anti rule. So with that, let's fire into this episode, the social media platform. Again, I like I said, I've not been looking forward to this. What are we gonna be building? We're gonna try to build something similar to Twitter. Where we've got a feed, post from different people that we're gonna follow on social. And, that is what it is. So, again, it should be an interesting episode. Let's get started on the clock. Start the timer and away we go. Right? So when I look at social media platform like Twitter, you've got a list of posts, you've got users that are on the service, You can follow people. You can favorite their posts. But as far as the functionality we're really looking for out of this, let's define it right when we go to plan here. We're going to, register users, which I've already got set up, inside my example project that I use all the time in these shows. We want to follow other users, create a feed of chirps. We won't call them tweet. We'll call them chirps. Sounds great. Let's shrink this down. This is gonna be our functionality needed. Alright. That seems like a pretty good functionality set for this. Again, yeah, I can't stress how much fun I'm looking forward to having on this episode. Alright. Now as far as our data model, you know, when you kind of break it down, there's the obvious ones like users. Right? So if I do some diagramming here, we have our users. These are actually gonna come from our directus users collection that directus gives us out of the box. That gives us auth and permissions and all the fancy stuff that we're really interested in. So we'll have Directus users and then we've got the chirps. As far as the chirp, it probably has, what, like some content. Maybe there's an image or a video. You know, this could just be a file if we wanted to share that. And then a timestamp or, created at date. Right? Those are our chirps. Those have a relationship back to the user. There's a user that chirped, so we can represent that. And then, what else are we gonna have here? We're gonna have some followers. Alright. So, basically the the users are gonna follow themselves, but because that's a many to many relationship, we're gonna need like a separate table for this. So when we talk about this, we're gonna have a follower follower ID. So this is I am the follower. The followee. I always hate naming stuff. Followee ID, followedid. So again those are gonna be a two way relationship with users. And then, what else do we have? Right? The other part that I'm not a 100% sure about, we've got this feed that is unique for each user. Right? So to me that means a separate collection. So we have feeds, we've got a user that's associated to the feed. And then what else do we have? Right? We're gonna have followers, feeds. The feed is gonna be a or like updated at. When was the feed last updated? And trying to think of, like, the other paradigm here. We're gonna just have a, like, the actual tweets that are or chirps that are in that feed. Alright. And then that means we need, like, a junction table as well, feed chirps. So this will be the chirp and the feed. Alright. So I I think this is the the data model that I wanna go with for this. We'll see how this actually plays out. As far as what I've got set up, right, is the standard config that I use for all these episodes, if you've ever caught one of the past episodes. I've just got a little Nuxt starter application that has a a couple of routes, like a register and a login. Let me just clear my cookies so we can see what those look like. Right? Just a register and login route. I've got a Directus SDK plugin configured here. So we just create a REST client. There's also a real time client in case I decide that we need to use real time in this. And then on the other side of it, I've got a blank direct assistance, just a single admin user. So, alright. Let's get to work on our data model. That's what we're gonna bang out first. And like I said, we've already got direct us users here. Let's work on our chirps. So that will be the first collection that we create, and I could zoom way in inside the Directus instance here. And do I really am I concerned with the primary key field for these being UUID or auto incremented integer? You know, typically, I'm choosing UID, but in in this case, you know, maybe integer is is fine really. For created on, we're gonna do created at. And created by, we're just gonna do the user I'm gonna call that user ID. So whatever user creates this chirp, that'll be saved in the database. And then whenever they create this chirp, will be saved as well. Once the chirp is out there, it is gone. We are not going to allow editing or anything like that. And as far as our content, maybe we want to support markdown for this. Message content. Chirp content. Sounds good. And then, you know, let's get wild with it and maybe even support a file. You can upload a single file to your Chirp to share. Alright, so that's our model. I'm gonna unhide these other fields here just so I could see these when I look at them. And next I want to I'm just gonna chirp. Chirp chirp. Save it. And you'll see that saves the admin user and the date, and now we've got our first chirp for this social media platform. I don't know what we're gonna call this. Why? Why not? I think why is the example that, our CTO, Reich Van Zanten, used in one of his talks. So, alright. What's next? We're gonna do our followers. Right? So we have our followers. And in this case, we have got, really not concerned with any of those other fields. What we're gonna do, I want to go into my data model. I'm I'm gonna go to our system collection, right? And we're gonna do a Directus users, and we're gonna go in and create a many to many relationship here. It's gonna be followers. We're gonna go to the followers table. And what do we have here? We've got the directus user. This is the follower followee ID. This will be the follower ID, I think. Alright. Is that gonna work out? We got followers. We're good. And then we could add, oh, that's our where do we have that? Follow words collection. Yeah. We don't need to add that. Right? Great. And then if we delete any of these other items, we're basically just going to like if somebody if a user gets deleted or a follower gets deleted, we're just going to remove those. So okay. I hope that's gonna give us what we're looking for. So now we've got a followers table and junction direct us users followers. Oh, okay. So it's created that extra table for me. Why did it do that? Because I have a relationship there? That's some unexpected behavior, but that'd be okay. We'll roll with that. So on our users, this table is pretty much worthless, we'll just remove that. Unexpected error. Okay, fun stuff as always. Let's let's try this over again, right? We've got followers, we're just gonna delete that. Now that table is removed. We'll do another mini to mini. And we'll call it followers. And actually the related collection here we want is direct us users and not followers. So that's where I goofed up originally. And we're gonna call this followers. That'll be our new collection. We got the followee ID and then the follower ID. And I think this will get us what we want. So this will be followees. Alright. Let's try that. See if that gets us what we want. So now we have followers and followees. And then we have a table called followers that we can inspect and make sure everything is looking correct. Alright, great. So we've got that set. Now we're gonna work on our feeds, right? Whenever I want to, I like check my feed, I I need some way to store the posts that are going into that specific feed. So we'll have a feeds collection. Again this could be auto incremented. And for the feed side of it, basically we're gonna have a when was this feed updated? Let's do updated at. Just carry that same one. I I really love these optional system fields inside Directus. Again, it just gives you some of that functionality that you need like time stamps and status and things like that baked right out of the gate. Alright. So we've got a feed. We've got updated at. We've got a we're gonna have to add a user to this feed. Right? So this is a user that's gonna be a mini to 1 relationship to the user's collection, direct to users. And then if I open up the advanced settings for for any of these fields that I'm creating, I could go in and create the corresponding field. Right? So I can add additional feeds for this specific user. You know, maybe we wanted to subscribe to different topics or something like that. So on delete of directus users, we wanna delete the feeds item because there's no need for a feed for a user that is not around, right? And when it comes to our display template, I can also go in and control what this will actually display here as well. Just a nice thing if you're doing a lot of editing inside the Directus Data Studio. So we got the updated at. There's the user. And then we're gonna need another relationship between the chirps and the feed, right? So we need to know what are all the chirps within that specific feed. So, how are we gonna do this? That'll be a many to many relationship again. Because a one chirp could be in many different feeds. So we'll go here inside Directus, we'll just search for the many to many relationship, right? And we are currently where? We're on feeds. So this will be our chirps. We'll add chirps as the related collection here, we'll show a link. And I'm gonna go into the relationship under advanced settings in case I want to add this, right? This is the feed ID, this is the chirp ID. Sounds great. Looks good. And then we can add the inverse relationship that we want. So we could look at a chirp and see all the feeds that it's involved in. Not sure we necessarily need that either. As far as the sort field, we're just gonna sort by the the timing of the specific chirp. Alright. So I with that, I think we've got our data model, like, pretty fleshed out here. We got the users already. We got chirps. We got followers. We got feeds. Cool. As a user, I could go in and I could follow someone, followee, except I don't have any actual users to follow at this point, right? So if we turn our attention to the front end of the project, I can go ahead and register a new user, Right. Let's call this edonbusk@example.com. And we'll give this a very secure exam model. Okay. Alright. So if we go to log in now, we should see we're already logged in. Great. Do I see a user here? I do. Elon Busk. Should've added a first name and last name to the actual registration form. Cool. So now I've got users that I can actually follow inside this. So if I go into my admin user, I should be able to add Elon Busk as one of my followees. Man, that terminology is gonna trip me up the whole time. I should have done something else. Following or something like that. Alright, so now, we don't have a feed for this. So one of the things that we could do inside Directus, whenever a a new user is created, you probably want to add a feed for them, right? So a couple ways you could go about that. One way is just flows, right? So flows are automations inside Directus that we can create. And I could trigger these based on a number of different actions. So we're just gonna do a create profile or create feed for new users. Okay. As far as a trigger setup we're going to do an event hook. And the action we want to be is non blocking. And should be whenever we is there a way to do it? Whenever a user is created auth.create. Auth.update. So whenever items are created and I just am going to do the Directus users collection here. So we're gonna trigger this whenever a new user is created, right? If I go in and I'll just throw these up side by side. Right? I'm already logged in. Let me just destroy this. Now we'll register a new user, new user at example.com. New password. User has been created. If I refresh this we could see that. Great. We can see there's the user, there's the key that we're interested in. Alright. And now I'm just gonna go in and create a feed for this, create feed. Alright, so there's our feed. And as far as the feed data model, I'm just gonna duplicate this because I do not remember what that looked like. Hazard of the gig. Right? So if I look at the feed, we've got a user and we've got chirps. So we'll just have one param that we're gonna pass. And we could even add like a a system message if we wanted to. But for now let's just create the feed. So we'll add an operation for it, we're gonna create data, we're gonna create a feed. And then inside the payload we've got the user key. And then we're gonna dynamically pull information from the trigger for this. So trigger dot key. And we'll just use that mustache syntax. And this should get us what we want. We'll do full access. Let me just check that one more time. Trigger dot key. Okay, great. Save. And now let's try this again. Right? I see 0 feeds in here. I'm just going to remove my session token. And we'll try john@example.com. Password. So we should have a John and example here. And we can see we've got a feed for that user now as well. Great. Perfect. Alright, so if we go back to our functionality that we need, we can register users, right. Do we have the ability to follow other users? In this case, yes, we do. We don't have it wired up to the front end yet. But let's let's stay focused on Directus, right? If I am following someone, and this is not showing up really nicely. So let's just edit the interface for this. We are going to show the avatar thumbnail with a first name, last name. And I can just copy this display template here and I can do that same thing here. So the the difference between the interface and the display, the interface shows up on the form itself. So when you're in that detail view, the display shows up on the layouts. So if I'm here looking at a list of followers, this is the display and this is the actual interface, right. So let's go back into our followers and we'll we'll fix this one as well. Should just be able to copy paste the same thing. Great. Cool. Cool. There it is. We got our followers, followees. Now if I am following Elon Musk, great name by the way, anytime he chirps it should populate into my feed, right? So, if I look at my feed, I don't have a feed yet, So let's create a feed for me. And here's, here's my feed. I can't really tell which one that is, so we'll just fix that really quickly as well. We'll show a display the direct us user. Great. Cool. Alright. So now I can see there's my feed, there's John's feed. Anytime Elon Dusk tweets, I want to populate that into, a feed. Right? Or let's say if I go into John's user, for example, John Doe, maybe John Doe is following me. So if we add me as an admin user, that'll be a good example. Right? If I chirp, chirp again. Right? This is, again, this is just writing it to the SQL database. Direct is is a is a nice complement to that. Like whatever changes I'm making here being mirrored. But, I chirp. If I go to the feed, nothing happens for John Doe. Right? So whenever a new chirp comes in, I keep wanting to call it a tweet, I want to basically populate that feed for all the other users that are, following that user, right? So again, I could reach for flows on this, populate feeds. Now at the scale of something like x or or Twitter or Facebook, this is going to probably break really quickly. Because, you know, you could have millions of followers and, you're gonna want to look at other solutions to scale this. But again, this is what we can build in an hour, so let's do that. So whenever an item is created, a chirp, we're gonna do some logic on that. Right? What are we gonna do? So as far as the logic, if we just map it out. Right? New chirp. We're gonna get the user from the chirp. User from the chirp. We're going to find all the users who are following that user push chirp to their feed. Alright, seems relatively straightforward. Whenever this chirp occurs, right now that we've got this flow set up, let's just go and test new flow. Hit save. Go back to our Flows, and now populate fees. We should have a payload. Right? We've got the content. We don't see the actual user, that is in our accountability object. Right? So we need to basically get a list of all the followers and their feeds. So how are we gonna do that? We are going to get a list of feeds. So we're gonna read data. Let's do like get feeds here. We'll do from the we'll do full access. We want to basically get a list of feeds. And the feeds here, we're gonna do a filter where the, we want to dig into this, right? So if we look at our other items, if we look at our data model, right. If we're getting a feed, there are there's a user for that feed. And then we want to inside the user, we have the followees. Actually, we wanna get all the followers and then their associated feeds as well. Okay. So let's go through that route instead. We're gonna get a list of all the followers where the follow e id is equal to the dollar sign accountability dot user. And for that we're also going to get a list of fields and we want to dive in and actually go through the nesting here. So Directus allows me to basically populate all the relational data in a single API call. So we'll do the root level fields, and then again if we're going through the followers we've got a follower ID. So we'll do follower ID, that'll get us to the user, dot feeds. Is that what we want? Follower ID dot feeds. And that should give us a list of the feeds that we want to populate. Alright, let's test this out and see. Alright. So if I chirp, the expected action here for this flow it should give me like John Doe's feed ID, if I've got that set up right. Right, because John Doe has feeds here. Alright, let's test it out and see. So we'll do show up in John John's feed. Hit save. And let's go test this flow. Alright. Alright. Less than a minute ago. Let's populate. Alright. So here's all of our feeds that we've got. We can see there's the feed, there's the or those are the followers actually. And then we need to collate all these feeds. So I'm just gonna copy this over here into Versus Code. And the next thing we're gonna do, we want to push that chirp into those feeds. So we're going to update a list of feeds and push the chirp into those feeds. Alright, so how we're gonna do that? We will go in and so we are going to update the feeds. And actually we probably need a like an intermediate here. So we're gonna return let's format the feeds. Alright so if we look we are getting well we got git feeds. So that's gonna be accessible through our data object as git feeds, Alright. So here's all the feeds, that is data.gitfeeds. And in that feed where you're wanting to map through those and we're going to return an array of the feed IDs that we want to push this tweet into. Alright so feeds to update equals feeds dot map. That's gonna be our feed. And then we're going to return what the feed dot follower_id feeds the first item in that array. Feeds to update, and then we're just gonna return feeds to update. Oops, gotta spell that correct. Alright, so just a little bit of formatting logic to make this easier. And then now what we're gonna do, let's call this get feeds ID. Get feeds ID. And that should give us an array and now we're gonna update those individual feeds. So we'll do update feed, update feeds. The collection is gonna be feeds from full access. And then as far as the IDs here I'm just gonna pass this array. So get feeds, Should've put IDs at the end of that, but no worries. And then we are going to do what? Can we do the can we do the array syntax here? Create chirpid. I think we can do this. So this is gonna be in, we need chirps. So that's the field that we're gonna update within the feed. And then we're gonna use the create update syntax here. So we'll do something like this, create. So instead of like modifying the value of these chirps, alright, we're gonna create a new record inside that array, which is gonna be what? What is that gonna be exactly? So we'll go back. Chirps are in our feed. Chirps, a chirp is what, just pass the chirp ID. Alright. And the chirpid is coming from what, trigger.key. Create trigger.chirp_, actually trigger.key. Alright. Is this actually gonna do what we wanna do or not? We'll see. Alright, let's test this out, right? So now if we take a look at our feed, let's do this side by side. Alright. So we'll go back over here. How are we doing on time? I don't know if we're gonna get to the like any front end stuff on this or not. Alright. So we got our feeds. Nothing in John Doe. Now if I chirp, please show up in feed. Refresh. It's not so there's something wrong with our logic here. Let's take a look at what's going on with this. So we got our feeds, run script, syntax missing. Forgot some type of feeds. Oh, of course forgot a parenthesis, that'll always do it. Alright. So let's just test again. Test again, please. Alright. If I refresh the feed, do we have the feed? We don't see any chirps in that feed over here. So something else is going wrong. There's our invalid payload. It must be of type 1 object. Would it just be passing the ID? ID. Let's add this in. Test again. Test one more time. Save. Refresh this. Okay, okay. Did we test one more time? If I look at the feed, we've got a feed for John Doe. It's not exactly what I wanted. It's pushing a new, I guess that would be like update instead of create. Update. Let's look at the directus docs actually. Create update. That's gonna be adding items, I think. Creating multiple items. Relationships. Where is this gonna be? Global parameters. Create an item. Relational data. Okay. So assign the existing item to be a child of the current item. You can use the same structure to select what the related items are. Simply omit them from the array. So we just wanna add an item in the array. You can provide an object detailing the changes. Like we don't wanna create a blank chirp. Right? Is this what happened here? No? Why is this chirp not showing any content? So it does show up in John Doe's feed. It doesn't show the actual content, which is weird. Let's just test one more time. So what is actually happening here? This is the chirp. Oh, duh. That's what's going on. Chirp underscore ID. So we're gonna create, and it should be chirp underscore ID because we've got to go through the junction collection. Alright. So we're gonna create a new item. And this should is that gonna solve this worse? Let's see. Alright. Chirp. We'll just test this. Feeds. We hit save. Now we see there's the chirp. Baller. Okay. So now that is working as expected, right? So if I now chirp, I'm the admin user and John Doe is following me. Follow me, John. I hit save. That shirt should show up in John's actual feed. Amazing, right? It doesn't show up in anybody else's feed. Awesome. Okay. So now that logic is working as intended. So we have all that working. Create a feed of chirps. We've gotta follow other users. We've got all this functionality. Right? Let's try to put together something on the front end for this. So what is this gonna look like? I'm using Tailwind and I've got the Nuxt UI library inside this thing. Tailwind Twitter clone maybe. Search for that. Tailwind Twitter clone pages, Tailwind clone with CSS. Hey, there we go. This is probably close enough. Right? It kinda looks a mess, but let's go for it. We're just gonna copy this code. Let's create a new page. Let's just call it feed dot view. Do some script setup. Oh, that didn't give me what we want. Script setup lang equals ts. And alright so now we're gonna add this thing here. This is a giant mess of HTML. Let's just load up our user over here. Let's get logged in. Are we logged in right now? We're missing a tag somewhere. Div div. Div. Where is this thing? Oh, see it's not even a particularly great clone because there's some missing div somewhere. Great. Lots of fun. Don't ever trust these templates that you see online. Let's still where's the issue? Is it that one little div there? Where is the issue? Div. Div. Div. Div. You know what? Forget it. 1st tweet start. I don't even tired of messing with this. Alright. So let's just go back to our index page. Wasting too much time to actually get anything done here. Alright. So we're gonna have a, what, list of tweets, chirps equals, we're gonna use the await. Use async data. We're gonna give it a key. This is the user chirps, user feed. Alright. And then we're gonna do return. We're gonna import our we're actually gonna grab that from our used Nuxt composable. So used Nuxt app. Okay. We'll await the user feed. And then we're gonna import the read items from the directus SDK atdirectus SDK, and we're gonna return directus dot request read items. Wait. Use async data. Return direct us read items. Chirps. Actually, we're gonna get the feed. Where the actually, let's get all the chirps. Chirps where the feed So if this is my user, and I've got the chirps, We wanna get the feeds, actually. Get the feed for my user where the, do I have the do I have a composable in here for the user? Where is my actual user state in this application? Let's take a look at our actual state. We have a user. We have a user ID. Okay. So we're also gonna do the user equals use state user. Okay. And then we're gonna get all the chirps where the user oh, we're gonna get the feed where the user is the current user. And actually, like a a lot of this could be controlled via access control here inside Directus. So I could just see I could see anybody's chirps. I could create chirps. I can't delete any chirps. But as far as the feed, I can only see my feed. So let's just adjust this. Alright. The permissions so I can only see my feed. So the user dot ID is equal to current user dot id. Alright. So now if I just do this, honestly. Feeds, chirps. Alright. We'll refresh, failed to resolve, directus sdk@directus\u002Fsdk. Alright. So and then maybe we'll just log those out. Pre chirps. Do we see any actual chirps data? Oh, actually gonna return that. We need to deconstruct that. Chirps. Okay. So there's our feed. And we actually want to see all the chirps that are in that specific feed. Right? So now if we log in, I'm not exactly sure who I'm logged in as. And let's update our user role inside Directus to where we can at least read the item permissions. We wanna be able to read our own user. So ID equals current user. And what's that gonna give us? Refresh. Alright. So I'm not logged in. If I log in as John Doe here I don't even know what password that I gave John. Pretty sure I do know. Example. Okay. So we're gonna go back. If we refresh, do we what do we see? Not seeing anything, actually. Default invalid user credentials. If we take a look at Wait. Use async data. Data feeds. It's gonna be the feed. Let's just move this logic somewhere else. Killing me. K. Index. Just go somewhere else with this. NuxLink. Or, view button to feed to the feed Batman. Alright. So there's our feed. Why don't I see any actual chirps inside there? To the feed, Batman. And we're also not getting this stuff on the actual server side as well. So that's another thing to figure out. But our access control settings here are preventing us from seeing the actual feeds inside the the chirps inside the feed. We probably wanna be able to see followers or what as well. If we go to the feed, now, can we actually see any of this project to the feed? Just totally blow this away. Got 9 minutes left on this episode. Really struggling here to kinda sort this one out. So this is our user's feed. We should be able to see the actual chirps within that though, and I'm not sure why that is not showing up. So there's the feeds, direct us users. You know, if I were to just do something like this, local host 8055\u002Ffeeds, should be like actually items slash feeds. So there I can see the actual chirps that are coming in. But is that because we got, like, caching going on here? Cash. Is there, like, a cache? User ID. Okay. So now we can see the chirps. Okay. Let's actually show the chirps themselves fields. ID, we probably want the user information, and then we'll get the chirps. Chirps. And let's just get all the the details of the chirps. Okay. So those are the the that's the junction collection. We really want the chirp dotchirp_id dash star. Okay. So now we've got a list of the chirps. Let's just iterate over that. We'll do a, dev4. What, chirpinfeed.chirps. And we can actually, like, maybe deconstruct this a little bit. Chirp underscore ID as the chirp. Key is gonna be chirp dot ID. Thank you. Then we have the chirp content. We also probably want the user ID from the chirps. We wanna know that that specific user. And in that case I'm probably gonna need to adjust my permissions as well. So access control, we'll go into users. For here, we'll just allow all access, but let's just restrict certain fields. Alright? Can't see anything except for the ones that I want. First name, last name, avatar, we don't wanna show email. We'll show ID. Followers, followee. Okay. Alright. Chirps dot underscore id.user. What are we missing? Oh, chirpsfeed.chirps. Let's just back up a minute. Feed. Pre let's throw this up here. And I'm just gonna, like, show this. Let's refresh the page, see what we got going on here To the feed. Alright. Chirps, chirp ID as the chirp in feed dot chirps. Dot content. This should work. Not sure why it's not. Right. V if feed and feed dot chirps. Feed. Oh, duh. We gotta get the first item of the feed. Transform data data dot 0. Okay. Definitely a frustrating frustrating run here. Transform data. That should give us what we need. My gosh. Return, comma. Still not. Oh, this is okay if I put this in the right spot, what a mess. Okay. I think that that should have it now, and we got a grand total of, like, 3 minutes left anyway. Do we have a feed? Yeah. Okay. So there's the chirps. Man, when we fail, we spell spectacularly sometimes. Alright. So here we go. We've got our chirps. We're showing a feed of those chirps. We've got the user underscore ID. So we refresh that. Now we could see the actual user that is chirping about that. So and we'll have a divv4. Let's do like a flex, flex call, give these some gap. We'll give each tweet some padding. We'll show a div. We'll do the username. Username chirp dot user ID or underscore user ID. Admin user. There's all of my my actual feed. Great. And, this is really really lovely. Now what if we wanted to actually chirp a bit? Right? We would create a new function, async function. Doesn't even actually make sense. We got a minute 30 left. I'm gonna go ahead and call this one a fail for today. We're gonna call this. We've got the direct us in set up really nicely on the the front end. We just really struggled a bit. You know, just an off day for me. Happens every once in a while, right? So we've got our chirps over here. The feed is is working as intended, right? If I go in and if I were to set up another follower, like if Elon Busk follows me, admin user. Save that. Obviously, like he needs a feed as well. Elon Busk. Great. And then if I chirp again, chirp for Idan. In his feed, we can see that one shows up. So there's our chirp. We can see that showing up there, which is nice. Chirp to eat on. That chirp also showed up inside John Doe's feed. So that part of it, we nailed UI. Do the explosion here. 10 seconds left. Yeah, I I think this is a good exercise from a data modeling standpoint and kind of a how to put this together. Just wasn't all there on the front end today. That's the way some of these things go. That's it for this episode of 100 apps, 100 hours. Hope you'll join me for the next one. We'll see you.","00b2fc67-4c3a-47fd-9741-502f88b7231c",[2452],"72dfcc94-f557-452c-b35c-0e2e84d67288",[],{"year":978,"number":2062,"id":2311,"episodes":2455,"show":2456},[2313,2314,2315,2316,2317,2318,2319,2320,2321,2298],{"title":322,"slug":323},{"id":1847,"slug":2458,"vimeo_id":2459,"description":2460,"tile":2461,"length":282,"resources":12,"people":12,"episode_number":159,"published":2462,"title":2463,"video_transcript_html":2464,"video_transcript_text":2465,"content":12,"seo":2466,"status":19,"episode_people":2467,"recommendations":2471,"season":2472},"scott-adrian","948811579","In this episode of Trace Talks, Scott Adrian shares his journey from professional piercer and musician to Senior WordPress Engineer. Scott discusses his early experiences with web development, starting with Myspace layouts for his band, and his first tech job at 1 Stop Internet. He highlights his transition to WordPress, leading teams, and mentoring junior developers. Scott also talks about balancing his career with personal projects, including publishing a sci-fi fantasy novel and developing a WordPress-based RPG game. Tune in for insights on career growth, leadership, and leveraging AI in development.","b6eb2819-3e45-4d1c-92fc-de0c6151a78b","2024-06-27","Scott Adrian, Sr. Wordpress Developer","\u003Cp>Speaker 0: Always thought, you know, I'm gonna I'll I'll just be a rock star, you know, no biggie. You know, I'll just go through the school thing and then be be famous for that.\u003C\u002Fp>\n\u003Cp>Speaker 1: Hey, everyone. Welcome to another episode of Trace Talks. Today, we have Scott Adrian. I'm Pedro. We also have John here as well, who you may recognize from past episodes because we run this podcast.\u003C\u002Fp>\n\u003Cp>Don't we, John?\u003C\u002Fp>\n\u003Cp>Speaker 2: Yep.\u003C\u002Fp>\n\u003Cp>Speaker 1: Any anyway, yeah. Scott, you know, I'd love to hear a little bit more about your background. I know you are a WordPress engineer, have been for some time transitioned from different, you know, backgrounds. Like, many of the folks we talked to maybe started off in a different industry, and ended up in, you know, in tech and in in web tech. So Mhmm.\u003C\u002Fp>\n\u003Cp>I'd love to hear about your career, what got you to this point, and, you know, what brought you from professional piercer to musician to, you know, maybe a career in technology?\u003C\u002Fp>\n\u003Cp>Speaker 0: Right. Money. Yeah. That's Yeah. That's what.\u003C\u002Fp>\n\u003Cp>Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: He's a good driver.\u003C\u002Fp>\n\u003Cp>Speaker 2: Wait. There's money in tech?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Exactly. That was the only one that made it. Yeah. Yeah.\u003C\u002Fp>\n\u003Cp>Because, you know, when I, yeah, when I started out, I was like, oh, okay. So I'm gonna be famous. That wasn't a question. It's like, what am I gonna be famous for? My dad was a long time rock star and lead guitarist and stuff in bands.\u003C\u002Fp>\n\u003Cp>So, I always thought, you know, I'm gonna, I'll, I'll just be a rock star, You know, no biggie. You know, I'll just go through the school thing and and then be be famous for that. And then yeah. Music was hard and I got into piercing, because I wanna make money at a young age. And, you know, back in 2,000, 2,000 ish, you know, 2,002, 3, Piercings were hot.\u003C\u002Fp>\n\u003Cp>Right? Everybody everybody was trying to it was getting more popular tattoos, piercings, and and, it really helped that everybody underage had somebody else that was underage that could pierce them. So, yeah, I I, that's how I that was my first business. And then when I turned 18, I actually started working in shops because I'm like, okay, well, doing so well, you know, out, you know, an underground, go to a shop and there'll be gangbusters. But yeah.\u003C\u002Fp>\n\u003Cp>Yeah. You you learn early on when you start working in shops. I was in Hollywood for a couple years. It's called the Walk of Fame tattoo. And on top of being horrifying and tear terribly scary, you know, working long nights in downtown Hollywood, There's no money in piercing specifically.\u003C\u002Fp>\n\u003Cp>I can't talk about tattooing. They seem to be doing well, and I I have long time friends that are still, you know, very successful tattoo artists. But, yeah, Pierce Piercing's such a novelty. And people that come to shops, you know, walk ins are are like, here, I wanna nose. I want my ear.\u003C\u002Fp>\n\u003Cp>And, you know, you're getting $20 on a Saturday.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: And, yeah. Didn't didn't work out for me. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: So what so then what prompted that transition into, like you know, obviously, there was there's a bit more money to be made, you said, in in the web stuff, but what was sort of that initial catalyst to get you thinking about programming?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. So I I I'm\u003C\u002Fp>\n\u003Cp>Speaker 2: also really curious because I also you know, I I I wanted to be a professional golfer. I was like, oh, that's that's me. You need to be a professional golfer. And then there you know, I'd\u003C\u002Fp>\n\u003Cp>Speaker 1: You need to be good at golf for that, John.\u003C\u002Fp>\n\u003Cp>Speaker 2: Well, I I forgot about that part. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. But\u003C\u002Fp>\n\u003Cp>Speaker 2: but when I made that transition when I made that transition into web design or websites, I just had a friend who said, hey. I'll pay you $50 to build a website. So I'm just curious because that was what it was for me that kind of got me into web design and technology. So I'm kinda curious to hear your story about how you've gotten to tech, but also what was your first paying job for that?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. There you go. Yeah. Because, you know, I've been doing web development for, you know, closer to 20 years, on my own. Because originally, when I was in bands, we needed a website, we needed a Myspace, custom Myspace layout.\u003C\u002Fp>\n\u003Cp>And so that's what that was my first instances of HTML, CSS. Just I\u003C\u002Fp>\n\u003Cp>Speaker 1: think I think for all 3 of us, that was probably probably a similar story. Exactly.\u003C\u002Fp>\n\u003Cp>Speaker 0: So I knew I could do it. And, and, you know, it it was, you know, it's a skill I was gaining just for fun and just for my own stuff. Same with graphic design. I was making all the albums and shirt designs and all that kind of stuff. But, yeah, the the catalyst, was really I got a job for 1 stop Internet.\u003C\u002Fp>\n\u003Cp>This was in 2,000, Yeah. Because I left I left in 2006 or 7. Yeah. So I was there for a while. I, I started working in the warehouse at 1 Stop Internet and they're, they're an outsourced e commerce company, basically large companies would be like, Hey, we need you to be our e commerce.\u003C\u002Fp>\n\u003Cp>So they, they build the website. They house, they had warehouses, they'd store all the product and they'd handle all the distribution themselves and all their shipping and all that stuff. So website and fulfillment all in one. So we had giant names. I mean, we had Von Dutch, giant name.\u003C\u002Fp>\n\u003Cp>We had we had, you know, Betsy Johnson, you know, Vestal Watches, Lululemon, True Religion, giant giant, brands, that that were housing other stuff, and we were doing the websites. And I was picking items for the warehouse at first. They promised me a job in the photo studio. So after after a little while and that finally happened, I started doing high volume photo retouching. And that's when I got on a computer, and I finally was getting paid to do work on a computer.\u003C\u002Fp>\n\u003Cp>It was photo, Photoshop and 1,000 images a day, you know, but, I ended up, you know, moving up and kind of running the post production. I was post production coordinator for years. And then, and I started doing little web work for them because clients would need like a slideshow to see the images that they were, that we were taking for them and they wanted to view them. So I'd make these little flash websites and and these little Dreamweaver based websites, with sliders and all that kind of stuff. And, it it kinda sucked because one stop Internet built websites.\u003C\u002Fp>\n\u003Cp>Mhmm. Like, for these clients, giant giant ecommerce websites. And I never had a chance to work for that department ever, which is crazy. I I tried. I I tried to apply it.\u003C\u002Fp>\n\u003Cp>It's just like, hey, any openings and, you know, and they just had these very talented programmers working there, you know? So, so I left I left for, an e commerce company who took a chance on me to like run their website. And, it was on asp.net storefront, asp.net storefront, which was not a great first, website to be running and work on. But luckily, they needed a blog, and, we went for WordPress. And that's where I first kinda got introduced into WordPress.\u003C\u002Fp>\n\u003Cp>And, so at that point, Piercing was side job, and, you know, I only did on the weekends or whenever the shop needed me. And, all all my income was coming from, you know, website work. And, yeah. And that's when I first started working in WordPress just with custom themes and plugins like everybody else does. And, and, eventually we needed some extra stuff and I would jump into the code and, Yeah.\u003C\u002Fp>\n\u003Cp>From there, I I, you know, to to gain more knowledge, I was doing a lot of side jobs, a lot of freelance work. I had a lot of time at this gig.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: I played a lot of video games and watched a lot of series of animes, so much, that, yeah, I ended up being able to do a bunch of side work while I was working there. And, it was yeah. Just Craigslist. That was the Alpha main got out 50% of my income was Craigslist jobs, building websites.\u003C\u002Fp>\n\u003Cp>Speaker 2: What was the initial driver to WordPress? Was it the ease of use? I honestly never got into WordPress myself. I was a Dreamweaver designer, which\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. It was. The ease of use. It was really easy to pick up. And at that time, I was scrappy, so I was just pulling in third party themes and plugins, hard coding CSS to just make them look how they wanted.\u003C\u002Fp>\n\u003Cp>You know, I'd get something very close, 3rd party, and I just manipulate it. And, yeah, it was easy money, really. That that was how it got to that point. And Yeah. Do you do\u003C\u002Fp>\n\u003Cp>Speaker 1: you think, like, specializing on WordPress allowed you to do that? Because typically in, like, the industry, we see there's, you know, sort of a debate on having a broad set of you know, being a full stack engineering, having a broad set of of tools at your disposal to really focusing in on a niche and being an expert in maybe WordPress or some specific Right. Language, for example. How has that kind of shaped your career trajectory thinking, you know, that you started in WordPress and and continue to focus on on that as a niche?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. You know, it wasn't a conscious decision. I think like everybody else, I got pigeonholed. My resume started one way and it just kept growing that way. And then at a certain point, it was, you know, I was the WordPress guy.\u003C\u002Fp>\n\u003Cp>So, you know, I started working for Beachbody's and, and working in their in their coaching department. We're developing a whole coaching platform in WordPress. And I was working some with some really, Austin Passi, big WordPress name. He ran the LA WordCamp a few years, and, they brought him in. And I I absorbed as much as I could because at that point, I was, like, the least experienced.\u003C\u002Fp>\n\u003Cp>I I I could do whatever you wanted, but I would do it wrong because I was just using old old people's code and, you know, just piecemealing things together. But he he showed me like, oh, I can just build a theme. Oh, I can build a plugin, you know, to make this happen. And, and there was other guys on the team too that were really advanced. And, yeah.\u003C\u002Fp>\n\u003Cp>So that's what I did. I faked it till I made it, and, and then I And\u003C\u002Fp>\n\u003Cp>Speaker 1: you made it.\u003C\u002Fp>\n\u003Cp>Speaker 0: And I made it. Yeah. So I had that on my resume, moved on to more jobs, you know, investors.com, like, one of at the time, one of the highest trafficked WordPress sites in the world. I jumped on as a lead, and I was leading, you know, 4 or 5 devs, all WordPress based. And, we were just developing custom themes and plugins for them.\u003C\u002Fp>\n\u003Cp>And, you know, I started working with they I forget the scenario, but they brought XWP in, which is a big name in the WordPress world as well. They they're the ones that did, like, a collaboration with Google to get, like, the Google AMP plugin and, and, other things. And, Thierry and some of the other guys, I would literally have them in my, in my office with me. And these guys were like core contributors to the max, to WordPress. And, again, I just absorbed so much.\u003C\u002Fp>\n\u003Cp>I'm talking, I learned unit testing there. I learned sanitization and escaping. All the you know, I already know how to program, but I learned coding standards from from that company, while pretending I already knew them, you know, which was a big theme.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: And, Yeah. And and so, that's that got me to the expert level, really.\u003C\u002Fp>\n\u003Cp>Speaker 2: Mhmm.\u003C\u002Fp>\n\u003Cp>Speaker 0: And I knew at that point, you know, any job I take going forward, I'm asking double and I'm, you know, and Yeah. And they're gonna, you know, they're gonna pay it.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. What was that transition like into leadership leading a team of 4 or 5 devs when, you know, 10, 15 years prior, you're like, I'm a be a professional tattoo artist, You know? And now you're leading people as sort of, like, a boss almost. What was that like, that first leadership role?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I don't I guess it was weird. You know, when you're in a place of authority, it's all about appearance. As long as they're they think you're the authority and they think that you know what you're talking about and you and you're not completely horrible at your job and and you're not completely talking out of your ass. I was very good at, figuring things out on the fly while I was talking.\u003C\u002Fp>\n\u003Cp>So still am.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: So, yeah, I guess it wasn't that bad. You know, they weren't I mentored a couple people who are really junior in the WordPress world. So, you know, you learn the most when you're teaching, and that really helped, helped me develop them into the dev that I needed them to be for for what I was doing and what I needed them to do. So, yeah, it wasn't that bad. I was I wasn't too nervous about leading the team, but it was surreal.\u003C\u002Fp>\n\u003Cp>You know? Because I never really thought about the trajectory. I just thought I'm gonna make money being the doer, the code monkey. You know what I mean? And, but, yeah, I mean, I got old.\u003C\u002Fp>\n\u003Cp>Yeah. You know? I got experience.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. Well well, as you've gotten older, you've maintained these side projects. You just released a book 6 months ago, so congrats on the book. And then I saw that you also you also released, like, an RPG game on the side or something. So as you've Sorry.\u003C\u002Fp>\n\u003Cp>As you progress Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: You're you're correct. Yeah. So, last in 2023, I first published my my first sci fi fantasy cover, you know, novel, called Orbum. And, another another great learning experience, knew nothing about the book industry, made a lot of assumptions, and, I just recently rereleased it because I did it wrong.\u003C\u002Fp>\n\u003Cp>Speaker 1: Amazing.\u003C\u002Fp>\n\u003Cp>Speaker 0: In January 28th, I rereleased all of them properly.\u003C\u002Fp>\n\u003Cp>Speaker 1: Got\u003C\u002Fp>\n\u003Cp>Speaker 0: it. And, and actually book 2 is on preorder now, Myroupelia, which should be out July 1st. It's gonna be a 4 book series. Never thought I was gonna be an author. I read a lot of sci fi books at one point.\u003C\u002Fp>\n\u003Cp>I was going through, like, 20 or so series. And I basically I have a really bad habit of if I'm spending a lot of time doing something that, that I'm not making money on, I feel, you know, that I'm not being productive. It's that really bad ambition kinda bug.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Well, it's it's definitely a really good timing this year. I think I think 2024 is a great year for sci fi having, you know, just seen Dune. And and all the stuff that's coming later this year. I think it's it's definitely a great year to be, you know, releasing that that type of material.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 2: I\u003C\u002Fp>\n\u003Cp>Speaker 1: wonder if that came from, you know, all the the free time that you mentioned that you had that you were watching all these anime and playing video games. Like, maybe that sort of inspired the the book as well.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I mean, it the story structure's definitely inspired by tons of the anime I I was watching. You know, play I played a lot of video games when I was younger. I do not have time to do that, you know, in the last 10 years. But, yeah, the, I, I, I think I had a, I had a big, you know, childhood of media.\u003C\u002Fp>\n\u003Cp>You know, we always had a TV. We always had movies. We always had video games. And, so, yeah, everything's ingrained in my brain, and it's all just straight imagination. I don't, you know, the research I do for the books is are for the technical scientific terms because it's technically a hard sci fi book, because I do a lot of explaining of things that don't exist.\u003C\u002Fp>\n\u003Cp>So, but, yeah, more more more or less, I don't plot the entire thing out. I kinda know where it's gonna go in my head to the very end. But, yeah, the main the main, driving force is I wanna know what happened next. You know? I wanna know what they're gonna say and to make people laugh because I I my natural timbre is snark.\u003C\u002Fp>\n\u003Cp>You know? Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 2: Same. Same. Yeah. Do you think\u003C\u002Fp>\n\u003Cp>Speaker 1: that that draws, like, the sort of the the authorship and the tech world sort of draw a parallel in that sense of, you know, you're sort of creating something out of nothing, especially when designing, you know, WordPress sort of extensions or plugins or coding things from, from scratch?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I mean, if, if you ever get a chance to read it, and being in the industry you're in and knowing where I came from, you're going to get a lot, a lot of the same, you're going to get a lot of the corporate world in there. A lot of the same terms, programming terms, and stuff like that. Things that maybe some people wouldn't, offhand know, like smoke testing and regression testing, you know, talking about his systems and, just little things like that, which, spill into the, the novel. Cause you know, these are scientists or they were all working for a corporation and then they all had to run away, you know?\u003C\u002Fp>\n\u003Cp>So, it's easy to to to make, correlations there, but, yeah, I mean, it's all creative. I feel like as I get more into leadership, I get less creative outlets. So this is good for me, you know, rather than, you know, leading a team or managing a team. Like, I, you know, I used to be making things all the time, that kind of stuff. And, you know, and when you get to a certain level, it's a lot about maintenance and it's a lot about keeping things afloat, especially in the industry now where budgets are getting cut left and right.\u003C\u002Fp>\n\u003Cp>Nobody wants to build a brand new this, you know, except for AI. Everybody wants a brand new AI tool. So\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Yeah. What's your, what's your take on that?\u003C\u002Fp>\n\u003Cp>Speaker 0: What are we using AI for? Love AI. Right. I'm not against AI. You know, I've been working with OpenAI's API and using it for various things.\u003C\u002Fp>\n\u003Cp>I use chat gpt all the damn time. I'm not a prompt engineer, but, man, I hate writing, you know, array building loops sometimes. So I'm just like, write me a function that takes this and moves the, you know, the key to the end and then move this key to the beginning and then merges them all the oh, dude. I just created a an object merging function. I I just don't have the patience to merge a bunch of objects together as long as they have true as their key value or whatever.\u003C\u002Fp>\n\u003Cp>You know?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: So With your Love that.\u003C\u002Fp>\n\u003Cp>Speaker 2: Do you do you explore other new tech? Because, obviously, AI is very popular, but any other new tech or how much time do you even have to explore new tech or interest?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I don't, kind of both. I don't have interest, and I don't have time. Yeah. I don't know.\u003C\u002Fp>\n\u003Cp>You think I I always say the the career is is for the money. Right? And then use the money to do things that make you happy. So I don't put I am very interested in the tech world. I keep up with, the general tech industry and general things.\u003C\u002Fp>\n\u003Cp>I mean, I work with people that are like, Oh, look at this new API. Oh, look at this new endpoint. Look at this new, tool, this new whatever framework. And they keep me in the know. Luckily, You know, I'm not sitting there building a sandbox of, of the newest, you know, React threads or whatever.\u003C\u002Fp>\n\u003Cp>And I'm just and we're like, look. It's like, oh, okay. Yes. I love that. What are we using it for?\u003C\u002Fp>\n\u003Cp>It's I really have to learn by doing something that is being productive and and for a reason. And that's how I've learned my entire career. I think it's, you know, I think it's very valuable too when I find other programmers. You know, if I'm if I wanna hire somebody, that they have the skill set to just pick something up and learn it when they're doing it, and then applying it accordingly. And that goes to the whole philosophy of, you know, college degree versus the same amount of time as experience in work.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: You know, I don't I didn't go to college for doing any of this. I didn't I I did a weird trade school just to have a certificate on my LinkedIn, And, and it's never slowed me down or stopped me.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: So I don't care about that. If I'm gonna look at somebody's resume, you know, it it's I'd way rather you have 4 years working for, butt effing startup, whatever\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: Company, you know, where they were making you wear multiple hats and you, you know, and you still have a positive referral. So Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: You know? And outlook on life.\u003C\u002Fp>\n\u003Cp>Speaker 0: And you're still happy and you wanna keep going. Yeah. Exactly.\u003C\u002Fp>\n\u003Cp>Speaker 1: Right. Exactly.\u003C\u002Fp>\n\u003Cp>Speaker 0: Dude. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 2: That's probably the opposite of Pedro. I I probably get 4 or 5 text messages or tweets a day from Pedro telling me to check out new tech. And, like, I can't pay I can't pay attention to all of this.\u003C\u002Fp>\n\u003Cp>Speaker 1: I'm the, yeah, I'm the tools fanatic. It's just like, what can make me more efficient so that I can go sit on a beach somewhere?\u003C\u002Fp>\n\u003Cp>Speaker 0: I yeah. I I have a buddy, Scott Weaver. Same same exact way. I hate looking at his setup. Somebody give it give me that give me that that bash.\u003C\u002Fp>\n\u003Cp>What do you what do you he builds his own bashes for his own computer just to make setting it up quick. I'm like, give me that. He's given me it. He's given it before. Like, when you do a computer transfer from 1 to 2, I was like, oh, it's such a drag.\u003C\u002Fp>\n\u003Cp>He built an entire bash function that takes all the apps he likes and takes all the setups he does and it installs it all and just runs it. Wow. And I'm just like, you know, he has a repo for it, and I just take it.\u003C\u002Fp>\n\u003Cp>Speaker 1: Incredible.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. But that's I know I know my my work style. I know my personality traits. I know my time, my multitasking and time structures, and, you know, it's just not me. I can appreciate people that do live that way.\u003C\u002Fp>\n\u003Cp>You know?\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. Do you are you still able to find any time to to I think you mentioned you don't really watch too much anime or play too many games. You know? What kind of a personal life do you have? I I think I I think I hear kids, so there's also potentially some kids who take up time.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Apologize for that if it's loud. Let me know because I got the headphones in. But, yeah, wife and 2 kids, 4 dogs. I have plenty of other things I need to do during the day.\u003C\u002Fp>\n\u003Cp>Yeah. Plenty of time to spend with there and happiness that they bring. And, you know, my youngest, has autism and, she's the one that's usually around when she's not in in school. Yeah. And, so, you know,\u003C\u002Fp>\n\u003Cp>Speaker 2: I'm I'm kind of the the main tear\u003C\u002Fp>\n\u003Cp>Speaker 0: caretaker for her as well. I I handle all her therapy sessions and all the paperwork and all that kind of stuff. So that's that's also an extra time spent that I need to focus on as well. You know, I have documenting her progress and stuff too. Just teaching her to brush her teeth right now.\u003C\u002Fp>\n\u003Cp>She's 8. I'm teaching her to brush her teeth right now, and I have to, like, document it, you know, how how her progress going and stuff like that. So, you know, I do watch anime, though, still. I just caught up with 1 piece because I did watch all the way to the, Wano saga and then I ran out of episodes and this was 2014 or something, 2015. Holy crap.\u003C\u002Fp>\n\u003Cp>Getting back to it. So I finished the Wano, recently, because because I'll tell you why too. It's because my other kid, my my oldest, she's 10. She plays Roblox and I kept seeing these freaking luffy 5th gear avatars. And I'm like, what is that?\u003C\u002Fp>\n\u003Cp>And I hate spoilers and I hate not knowing. Oh, no. Especially if it's a series that I watched a 1,000 episodes already. I'm like, I should know what's going on. Yeah.\u003C\u002Fp>\n\u003Cp>Right. It took a really long time to get to 5th gear, man. Really Yeah. Fucking long. So, yeah, did that.\u003C\u002Fp>\n\u003Cp>So, yeah, I still like to keep it up every now and then. I I mean, I watch a streamer, Pirate Software, on Twitch that I really like, Yeah. Just in the background. And, and he's inspired me to actually start developing my game more and I'm streaming it now on Twitch. Oh, amazing.\u003C\u002Fp>\n\u003Cp>Yeah. Twitch, it's just slash orb and mortar. I try to do it, every day, 10:10 to 12 or something like that where it's showing the development of my WordPress based RPG game that I'm developing. Incredible.\u003C\u002Fp>\n\u003Cp>Speaker 1: And that will be based on the same, content as the as the novels?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. It's in the urban world. I'm start I'm using it, as a story structure for it. It'll have a lot of extra stuff, and I'm gonna have to tweak some things so he has more weapons and he can maybe use magic a little earlier than in the book. Spoiler alert.\u003C\u002Fp>\n\u003Cp>Yeah. But, that's, yeah. So I already have an entire universe to build from, so now it's all about the mechanics. And it's all Yeah. All the content management, all the assets are WordPress based.\u003C\u002Fp>\n\u003Cp>So it's, and vanilla JavaScript and SASS styling.\u003C\u002Fp>\n\u003Cp>Speaker 1: That's amazing. I don't think I've ever heard of anyone building a a an entire game off of WordPress, but that we'll have to check that out. That's very interesting.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Yeah. Yeah. Feel it's it's it's been an interesting ride, and, I'm not using Canvas. So if anybody's listening and they're like, oh, he's using Canvas, probably.\u003C\u002Fp>\n\u003Cp>I'm not. It's straight vanilla JavaScript and styling. Yeah. Nice. Very fun.\u003C\u002Fp>\n\u003Cp>Speaker 2: Well, yeah. Take I mean, taking a look back again at want from wanting to be a rock star and piercing all the way up to leading teams, you know, I guess, what have what's you know, do you do you miss the days of being purely just an individual contributor, you know, or or do you have you enjoyed the journey all the way to where you are now?\u003C\u002Fp>\n\u003Cp>Speaker 0: You know, I'm still an individual contributor in in a sense for what I'm doing. You know, when I worked for Disney for a couple years, I I was full on individual contributor until their their horrible timing structure of canceling contractors happened. And then, you know, Warner Brothers, same thing, individual contributor, and, those were all in the midst of different, you know, job structures and stuff like that, same for Universal. So it's like, I don't care at this point if I'm the builder or the one telling people to build. In the future, I assume that I'll just be managing giant teams and, you know, whatever, move up to a CTO role, the natural progression.\u003C\u002Fp>\n\u003Cp>And I don't mind, you know, doing the whole recruiting, hiring, firing, meeting setup, collabs, all that kind of stuff, agile. You know? I know everybody hates it and whatever. I mean, I I'm not I'm not seeking it out to be a scrum master, but I can handle it. Yeah.\u003C\u002Fp>\n\u003Cp>Better than others.\u003C\u002Fp>\n\u003Cp>Speaker 2: At some point, you'll become a CTO, and you'll be on stage talking to others. So you'll be on a stage, just not as a rock star. Well, you'll be a rock star maybe with somebody.\u003C\u002Fp>\n\u003Cp>Speaker 0: And I still release music too. So, that's that it could happen. I could still be a rock star someday. If somebody Yeah. Somebody takes it up, and it's also some of the music is, Orban based.\u003C\u002Fp>\n\u003Cp>So Yeah. Definitely Oh, awesome. Finding a lot a lot of ways to incorporate this universe for sure.\u003C\u002Fp>\n\u003Cp>Speaker 1: That's amazing. Yeah. Maybe maybe, you know, to kinda wrap this conversation of every everything we've learned about you, maybe you could tell everyone where to where to follow along with you on LinkedIn or if you have any other social media. Obviously, where to check out the the book and follow your progress on Twitch would be would be awesome.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Awesome. You if you wanna see anything Orban based or everything Orban based, it's literally Orban order, one word. I'm at that or slash that on all the social medias. Except for Facebook, I had to do the Orbum order because they flagged Orbom order for, like, a weird advertising bug, and it, like, closed I could not make ads on Facebook anymore.\u003C\u002Fp>\n\u003Cp>They thought I was doing something nefarious, so I completely closed that out. And it's it's the Orgim order on Facebook. But, yeah, I got a threads, Instagram, Twitter, x. I've, Twitch, and Discord even. The Discord is open for everything.\u003C\u002Fp>\n\u003Cp>And, yeah, like I said, oh, and the YouTube too. So the YouTube's where you can see my music, it's being distributed through CD Baby. So I mean, it's everywhere. You just look up, The Orban Order is the band name.\u003C\u002Fp>\n\u003Cp>Speaker 2: Nice.\u003C\u002Fp>\n\u003Cp>Speaker 0: Amazing. And, yeah, you can find I I have, like, 3 singles out, that are that are fun. Just power metal stuff, and and, it's yeah. And, that's\u003C\u002Fp>\n\u003Cp>Speaker 2: it. Now you've had to go through a whole branding process, so you're also a brander.\u003C\u002Fp>\n\u003Cp>Speaker 0: Dude, marketing. Yeah. I didn't know how hard, book marketing was. I mean, at the end of the day, I'm luckily in a tech sector where I work for a lot of marketing teams. So it completely coincides.\u003C\u002Fp>\n\u003Cp>I mean, running ad campaigns on AMS, you know, Amazon, media services and running ad campaigns on Facebook ads and and, just handling I mean, I did all the typesetting for my book too in InDesign. I just Nice. I had to learn that. Because I'm I I know all the creative suite, you know, I do all my own video editing too. And, you know, just just from the years of doing stuff, and, the, you know, and I do on my own music editing on Garage Band.\u003C\u002Fp>\n\u003Cp>And it's, like once you once you can work one program, everybody copies everyone. Right? So it's it's you know, you could pick if you wanna do it, you could do it. Not the best at any of it, but Yeah. I can release it.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. Well, awesome. It it's so cool to hear, you know, we've we've talked to a lot of others who have this creative spark in them. So it's it's been great to hear everything in in your journey, and it's I'll definitely follow along and grab a copy of that book because I like sci fi books. I don't I don't like biographies or things like that.\u003C\u002Fp>\n\u003Cp>So we'll put your links and all the information so people can follow along, but we definitely appreciate the conversation today.\u003C\u002Fp>\n\u003Cp>Speaker 0: Awesome. Thanks so much. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: Thanks so much, Scott. And\u003C\u002Fp>\n\u003Cp>Speaker 0: there's an audio book too if you guys don't wanna read.\u003C\u002Fp>\n\u003Cp>Speaker 2: I like to read. I like to read.\u003C\u002Fp>","Always thought, you know, I'm gonna I'll I'll just be a rock star, you know, no biggie. You know, I'll just go through the school thing and then be be famous for that. Hey, everyone. Welcome to another episode of Trace Talks. Today, we have Scott Adrian. I'm Pedro. We also have John here as well, who you may recognize from past episodes because we run this podcast. Don't we, John? Yep. Any anyway, yeah. Scott, you know, I'd love to hear a little bit more about your background. I know you are a WordPress engineer, have been for some time transitioned from different, you know, backgrounds. Like, many of the folks we talked to maybe started off in a different industry, and ended up in, you know, in tech and in in web tech. So Mhmm. I'd love to hear about your career, what got you to this point, and, you know, what brought you from professional piercer to musician to, you know, maybe a career in technology? Right. Money. Yeah. That's Yeah. That's what. Yeah. He's a good driver. Wait. There's money in tech? Yeah. Exactly. That was the only one that made it. Yeah. Yeah. Because, you know, when I, yeah, when I started out, I was like, oh, okay. So I'm gonna be famous. That wasn't a question. It's like, what am I gonna be famous for? My dad was a long time rock star and lead guitarist and stuff in bands. So, I always thought, you know, I'm gonna, I'll, I'll just be a rock star, You know, no biggie. You know, I'll just go through the school thing and and then be be famous for that. And then yeah. Music was hard and I got into piercing, because I wanna make money at a young age. And, you know, back in 2,000, 2,000 ish, you know, 2,002, 3, Piercings were hot. Right? Everybody everybody was trying to it was getting more popular tattoos, piercings, and and, it really helped that everybody underage had somebody else that was underage that could pierce them. So, yeah, I I, that's how I that was my first business. And then when I turned 18, I actually started working in shops because I'm like, okay, well, doing so well, you know, out, you know, an underground, go to a shop and there'll be gangbusters. But yeah. Yeah. You you learn early on when you start working in shops. I was in Hollywood for a couple years. It's called the Walk of Fame tattoo. And on top of being horrifying and tear terribly scary, you know, working long nights in downtown Hollywood, There's no money in piercing specifically. I can't talk about tattooing. They seem to be doing well, and I I have long time friends that are still, you know, very successful tattoo artists. But, yeah, Pierce Piercing's such a novelty. And people that come to shops, you know, walk ins are are like, here, I wanna nose. I want my ear. And, you know, you're getting $20 on a Saturday. Yeah. And, yeah. Didn't didn't work out for me. Yeah. So what so then what prompted that transition into, like you know, obviously, there was there's a bit more money to be made, you said, in in the web stuff, but what was sort of that initial catalyst to get you thinking about programming? Yeah. So I I I'm also really curious because I also you know, I I I wanted to be a professional golfer. I was like, oh, that's that's me. You need to be a professional golfer. And then there you know, I'd You need to be good at golf for that, John. Well, I I forgot about that part. Yeah. Yeah. But but when I made that transition when I made that transition into web design or websites, I just had a friend who said, hey. I'll pay you $50 to build a website. So I'm just curious because that was what it was for me that kind of got me into web design and technology. So I'm kinda curious to hear your story about how you've gotten to tech, but also what was your first paying job for that? Yeah. There you go. Yeah. Because, you know, I've been doing web development for, you know, closer to 20 years, on my own. Because originally, when I was in bands, we needed a website, we needed a Myspace, custom Myspace layout. And so that's what that was my first instances of HTML, CSS. Just I think I think for all 3 of us, that was probably probably a similar story. Exactly. So I knew I could do it. And, and, you know, it it was, you know, it's a skill I was gaining just for fun and just for my own stuff. Same with graphic design. I was making all the albums and shirt designs and all that kind of stuff. But, yeah, the the catalyst, was really I got a job for 1 stop Internet. This was in 2,000, Yeah. Because I left I left in 2006 or 7. Yeah. So I was there for a while. I, I started working in the warehouse at 1 Stop Internet and they're, they're an outsourced e commerce company, basically large companies would be like, Hey, we need you to be our e commerce. So they, they build the website. They house, they had warehouses, they'd store all the product and they'd handle all the distribution themselves and all their shipping and all that stuff. So website and fulfillment all in one. So we had giant names. I mean, we had Von Dutch, giant name. We had we had, you know, Betsy Johnson, you know, Vestal Watches, Lululemon, True Religion, giant giant, brands, that that were housing other stuff, and we were doing the websites. And I was picking items for the warehouse at first. They promised me a job in the photo studio. So after after a little while and that finally happened, I started doing high volume photo retouching. And that's when I got on a computer, and I finally was getting paid to do work on a computer. It was photo, Photoshop and 1,000 images a day, you know, but, I ended up, you know, moving up and kind of running the post production. I was post production coordinator for years. And then, and I started doing little web work for them because clients would need like a slideshow to see the images that they were, that we were taking for them and they wanted to view them. So I'd make these little flash websites and and these little Dreamweaver based websites, with sliders and all that kind of stuff. And, it it kinda sucked because one stop Internet built websites. Mhmm. Like, for these clients, giant giant ecommerce websites. And I never had a chance to work for that department ever, which is crazy. I I tried. I I tried to apply it. It's just like, hey, any openings and, you know, and they just had these very talented programmers working there, you know? So, so I left I left for, an e commerce company who took a chance on me to like run their website. And, it was on asp.net storefront, asp.net storefront, which was not a great first, website to be running and work on. But luckily, they needed a blog, and, we went for WordPress. And that's where I first kinda got introduced into WordPress. And, so at that point, Piercing was side job, and, you know, I only did on the weekends or whenever the shop needed me. And, all all my income was coming from, you know, website work. And, yeah. And that's when I first started working in WordPress just with custom themes and plugins like everybody else does. And, and, eventually we needed some extra stuff and I would jump into the code and, Yeah. From there, I I, you know, to to gain more knowledge, I was doing a lot of side jobs, a lot of freelance work. I had a lot of time at this gig. Yeah. I played a lot of video games and watched a lot of series of animes, so much, that, yeah, I ended up being able to do a bunch of side work while I was working there. And, it was yeah. Just Craigslist. That was the Alpha main got out 50% of my income was Craigslist jobs, building websites. What was the initial driver to WordPress? Was it the ease of use? I honestly never got into WordPress myself. I was a Dreamweaver designer, which Yeah. It was. The ease of use. It was really easy to pick up. And at that time, I was scrappy, so I was just pulling in third party themes and plugins, hard coding CSS to just make them look how they wanted. You know, I'd get something very close, 3rd party, and I just manipulate it. And, yeah, it was easy money, really. That that was how it got to that point. And Yeah. Do you do you think, like, specializing on WordPress allowed you to do that? Because typically in, like, the industry, we see there's, you know, sort of a debate on having a broad set of you know, being a full stack engineering, having a broad set of of tools at your disposal to really focusing in on a niche and being an expert in maybe WordPress or some specific Right. Language, for example. How has that kind of shaped your career trajectory thinking, you know, that you started in WordPress and and continue to focus on on that as a niche? Yeah. You know, it wasn't a conscious decision. I think like everybody else, I got pigeonholed. My resume started one way and it just kept growing that way. And then at a certain point, it was, you know, I was the WordPress guy. So, you know, I started working for Beachbody's and, and working in their in their coaching department. We're developing a whole coaching platform in WordPress. And I was working some with some really, Austin Passi, big WordPress name. He ran the LA WordCamp a few years, and, they brought him in. And I I absorbed as much as I could because at that point, I was, like, the least experienced. I I I could do whatever you wanted, but I would do it wrong because I was just using old old people's code and, you know, just piecemealing things together. But he he showed me like, oh, I can just build a theme. Oh, I can build a plugin, you know, to make this happen. And, and there was other guys on the team too that were really advanced. And, yeah. So that's what I did. I faked it till I made it, and, and then I And you made it. And I made it. Yeah. So I had that on my resume, moved on to more jobs, you know, investors.com, like, one of at the time, one of the highest trafficked WordPress sites in the world. I jumped on as a lead, and I was leading, you know, 4 or 5 devs, all WordPress based. And, we were just developing custom themes and plugins for them. And, you know, I started working with they I forget the scenario, but they brought XWP in, which is a big name in the WordPress world as well. They they're the ones that did, like, a collaboration with Google to get, like, the Google AMP plugin and, and, other things. And, Thierry and some of the other guys, I would literally have them in my, in my office with me. And these guys were like core contributors to the max, to WordPress. And, again, I just absorbed so much. I'm talking, I learned unit testing there. I learned sanitization and escaping. All the you know, I already know how to program, but I learned coding standards from from that company, while pretending I already knew them, you know, which was a big theme. Yeah. And, Yeah. And and so, that's that got me to the expert level, really. Mhmm. And I knew at that point, you know, any job I take going forward, I'm asking double and I'm, you know, and Yeah. And they're gonna, you know, they're gonna pay it. Yeah. What was that transition like into leadership leading a team of 4 or 5 devs when, you know, 10, 15 years prior, you're like, I'm a be a professional tattoo artist, You know? And now you're leading people as sort of, like, a boss almost. What was that like, that first leadership role? Yeah. I don't I guess it was weird. You know, when you're in a place of authority, it's all about appearance. As long as they're they think you're the authority and they think that you know what you're talking about and you and you're not completely horrible at your job and and you're not completely talking out of your ass. I was very good at, figuring things out on the fly while I was talking. So still am. Yeah. So, yeah, I guess it wasn't that bad. You know, they weren't I mentored a couple people who are really junior in the WordPress world. So, you know, you learn the most when you're teaching, and that really helped, helped me develop them into the dev that I needed them to be for for what I was doing and what I needed them to do. So, yeah, it wasn't that bad. I was I wasn't too nervous about leading the team, but it was surreal. You know? Because I never really thought about the trajectory. I just thought I'm gonna make money being the doer, the code monkey. You know what I mean? And, but, yeah, I mean, I got old. Yeah. You know? I got experience. Yeah. Well well, as you've gotten older, you've maintained these side projects. You just released a book 6 months ago, so congrats on the book. And then I saw that you also you also released, like, an RPG game on the side or something. So as you've Sorry. As you progress Yeah. You're you're correct. Yeah. So, last in 2023, I first published my my first sci fi fantasy cover, you know, novel, called Orbum. And, another another great learning experience, knew nothing about the book industry, made a lot of assumptions, and, I just recently rereleased it because I did it wrong. Amazing. In January 28th, I rereleased all of them properly. Got it. And, and actually book 2 is on preorder now, Myroupelia, which should be out July 1st. It's gonna be a 4 book series. Never thought I was gonna be an author. I read a lot of sci fi books at one point. I was going through, like, 20 or so series. And I basically I have a really bad habit of if I'm spending a lot of time doing something that, that I'm not making money on, I feel, you know, that I'm not being productive. It's that really bad ambition kinda bug. Yeah. Well, it's it's definitely a really good timing this year. I think I think 2024 is a great year for sci fi having, you know, just seen Dune. And and all the stuff that's coming later this year. I think it's it's definitely a great year to be, you know, releasing that that type of material. Yeah. I wonder if that came from, you know, all the the free time that you mentioned that you had that you were watching all these anime and playing video games. Like, maybe that sort of inspired the the book as well. Yeah. I mean, it the story structure's definitely inspired by tons of the anime I I was watching. You know, play I played a lot of video games when I was younger. I do not have time to do that, you know, in the last 10 years. But, yeah, the, I, I, I think I had a, I had a big, you know, childhood of media. You know, we always had a TV. We always had movies. We always had video games. And, so, yeah, everything's ingrained in my brain, and it's all just straight imagination. I don't, you know, the research I do for the books is are for the technical scientific terms because it's technically a hard sci fi book, because I do a lot of explaining of things that don't exist. So, but, yeah, more more more or less, I don't plot the entire thing out. I kinda know where it's gonna go in my head to the very end. But, yeah, the main the main, driving force is I wanna know what happened next. You know? I wanna know what they're gonna say and to make people laugh because I I my natural timbre is snark. You know? Yeah. Same. Same. Yeah. Do you think that that draws, like, the sort of the the authorship and the tech world sort of draw a parallel in that sense of, you know, you're sort of creating something out of nothing, especially when designing, you know, WordPress sort of extensions or plugins or coding things from, from scratch? Yeah. I mean, if, if you ever get a chance to read it, and being in the industry you're in and knowing where I came from, you're going to get a lot, a lot of the same, you're going to get a lot of the corporate world in there. A lot of the same terms, programming terms, and stuff like that. Things that maybe some people wouldn't, offhand know, like smoke testing and regression testing, you know, talking about his systems and, just little things like that, which, spill into the, the novel. Cause you know, these are scientists or they were all working for a corporation and then they all had to run away, you know? So, it's easy to to to make, correlations there, but, yeah, I mean, it's all creative. I feel like as I get more into leadership, I get less creative outlets. So this is good for me, you know, rather than, you know, leading a team or managing a team. Like, I, you know, I used to be making things all the time, that kind of stuff. And, you know, and when you get to a certain level, it's a lot about maintenance and it's a lot about keeping things afloat, especially in the industry now where budgets are getting cut left and right. Nobody wants to build a brand new this, you know, except for AI. Everybody wants a brand new AI tool. So Yeah. Yeah. What's your, what's your take on that? What are we using AI for? Love AI. Right. I'm not against AI. You know, I've been working with OpenAI's API and using it for various things. I use chat gpt all the damn time. I'm not a prompt engineer, but, man, I hate writing, you know, array building loops sometimes. So I'm just like, write me a function that takes this and moves the, you know, the key to the end and then move this key to the beginning and then merges them all the oh, dude. I just created a an object merging function. I I just don't have the patience to merge a bunch of objects together as long as they have true as their key value or whatever. You know? Yeah. Yeah. So With your Love that. Do you do you explore other new tech? Because, obviously, AI is very popular, but any other new tech or how much time do you even have to explore new tech or interest? Yeah. I don't, kind of both. I don't have interest, and I don't have time. Yeah. I don't know. You think I I always say the the career is is for the money. Right? And then use the money to do things that make you happy. So I don't put I am very interested in the tech world. I keep up with, the general tech industry and general things. I mean, I work with people that are like, Oh, look at this new API. Oh, look at this new endpoint. Look at this new, tool, this new whatever framework. And they keep me in the know. Luckily, You know, I'm not sitting there building a sandbox of, of the newest, you know, React threads or whatever. And I'm just and we're like, look. It's like, oh, okay. Yes. I love that. What are we using it for? It's I really have to learn by doing something that is being productive and and for a reason. And that's how I've learned my entire career. I think it's, you know, I think it's very valuable too when I find other programmers. You know, if I'm if I wanna hire somebody, that they have the skill set to just pick something up and learn it when they're doing it, and then applying it accordingly. And that goes to the whole philosophy of, you know, college degree versus the same amount of time as experience in work. Yeah. You know, I don't I didn't go to college for doing any of this. I didn't I I did a weird trade school just to have a certificate on my LinkedIn, And, and it's never slowed me down or stopped me. Yeah. So I don't care about that. If I'm gonna look at somebody's resume, you know, it it's I'd way rather you have 4 years working for, butt effing startup, whatever Yeah. Company, you know, where they were making you wear multiple hats and you, you know, and you still have a positive referral. So Yeah. You know? And outlook on life. And you're still happy and you wanna keep going. Yeah. Exactly. Right. Exactly. Dude. Yeah. That's probably the opposite of Pedro. I I probably get 4 or 5 text messages or tweets a day from Pedro telling me to check out new tech. And, like, I can't pay I can't pay attention to all of this. I'm the, yeah, I'm the tools fanatic. It's just like, what can make me more efficient so that I can go sit on a beach somewhere? I yeah. I I have a buddy, Scott Weaver. Same same exact way. I hate looking at his setup. Somebody give it give me that give me that that bash. What do you what do you he builds his own bashes for his own computer just to make setting it up quick. I'm like, give me that. He's given me it. He's given it before. Like, when you do a computer transfer from 1 to 2, I was like, oh, it's such a drag. He built an entire bash function that takes all the apps he likes and takes all the setups he does and it installs it all and just runs it. Wow. And I'm just like, you know, he has a repo for it, and I just take it. Incredible. Yeah. But that's I know I know my my work style. I know my personality traits. I know my time, my multitasking and time structures, and, you know, it's just not me. I can appreciate people that do live that way. You know? Yeah. Do you are you still able to find any time to to I think you mentioned you don't really watch too much anime or play too many games. You know? What kind of a personal life do you have? I I think I I think I hear kids, so there's also potentially some kids who take up time. Yeah. Apologize for that if it's loud. Let me know because I got the headphones in. But, yeah, wife and 2 kids, 4 dogs. I have plenty of other things I need to do during the day. Yeah. Plenty of time to spend with there and happiness that they bring. And, you know, my youngest, has autism and, she's the one that's usually around when she's not in in school. Yeah. And, so, you know, I'm I'm kind of the the main tear caretaker for her as well. I I handle all her therapy sessions and all the paperwork and all that kind of stuff. So that's that's also an extra time spent that I need to focus on as well. You know, I have documenting her progress and stuff too. Just teaching her to brush her teeth right now. She's 8. I'm teaching her to brush her teeth right now, and I have to, like, document it, you know, how how her progress going and stuff like that. So, you know, I do watch anime, though, still. I just caught up with 1 piece because I did watch all the way to the, Wano saga and then I ran out of episodes and this was 2014 or something, 2015. Holy crap. Getting back to it. So I finished the Wano, recently, because because I'll tell you why too. It's because my other kid, my my oldest, she's 10. She plays Roblox and I kept seeing these freaking luffy 5th gear avatars. And I'm like, what is that? And I hate spoilers and I hate not knowing. Oh, no. Especially if it's a series that I watched a 1,000 episodes already. I'm like, I should know what's going on. Yeah. Right. It took a really long time to get to 5th gear, man. Really Yeah. Fucking long. So, yeah, did that. So, yeah, I still like to keep it up every now and then. I I mean, I watch a streamer, Pirate Software, on Twitch that I really like, Yeah. Just in the background. And, and he's inspired me to actually start developing my game more and I'm streaming it now on Twitch. Oh, amazing. Yeah. Twitch, it's just slash orb and mortar. I try to do it, every day, 10:10 to 12 or something like that where it's showing the development of my WordPress based RPG game that I'm developing. Incredible. And that will be based on the same, content as the as the novels? Yeah. It's in the urban world. I'm start I'm using it, as a story structure for it. It'll have a lot of extra stuff, and I'm gonna have to tweak some things so he has more weapons and he can maybe use magic a little earlier than in the book. Spoiler alert. Yeah. But, that's, yeah. So I already have an entire universe to build from, so now it's all about the mechanics. And it's all Yeah. All the content management, all the assets are WordPress based. So it's, and vanilla JavaScript and SASS styling. That's amazing. I don't think I've ever heard of anyone building a a an entire game off of WordPress, but that we'll have to check that out. That's very interesting. Yeah. Yeah. Yeah. Feel it's it's it's been an interesting ride, and, I'm not using Canvas. So if anybody's listening and they're like, oh, he's using Canvas, probably. I'm not. It's straight vanilla JavaScript and styling. Yeah. Nice. Very fun. Well, yeah. Take I mean, taking a look back again at want from wanting to be a rock star and piercing all the way up to leading teams, you know, I guess, what have what's you know, do you do you miss the days of being purely just an individual contributor, you know, or or do you have you enjoyed the journey all the way to where you are now? You know, I'm still an individual contributor in in a sense for what I'm doing. You know, when I worked for Disney for a couple years, I I was full on individual contributor until their their horrible timing structure of canceling contractors happened. And then, you know, Warner Brothers, same thing, individual contributor, and, those were all in the midst of different, you know, job structures and stuff like that, same for Universal. So it's like, I don't care at this point if I'm the builder or the one telling people to build. In the future, I assume that I'll just be managing giant teams and, you know, whatever, move up to a CTO role, the natural progression. And I don't mind, you know, doing the whole recruiting, hiring, firing, meeting setup, collabs, all that kind of stuff, agile. You know? I know everybody hates it and whatever. I mean, I I'm not I'm not seeking it out to be a scrum master, but I can handle it. Yeah. Better than others. At some point, you'll become a CTO, and you'll be on stage talking to others. So you'll be on a stage, just not as a rock star. Well, you'll be a rock star maybe with somebody. And I still release music too. So, that's that it could happen. I could still be a rock star someday. If somebody Yeah. Somebody takes it up, and it's also some of the music is, Orban based. So Yeah. Definitely Oh, awesome. Finding a lot a lot of ways to incorporate this universe for sure. That's amazing. Yeah. Maybe maybe, you know, to kinda wrap this conversation of every everything we've learned about you, maybe you could tell everyone where to where to follow along with you on LinkedIn or if you have any other social media. Obviously, where to check out the the book and follow your progress on Twitch would be would be awesome. Yeah. Awesome. You if you wanna see anything Orban based or everything Orban based, it's literally Orban order, one word. I'm at that or slash that on all the social medias. Except for Facebook, I had to do the Orbum order because they flagged Orbom order for, like, a weird advertising bug, and it, like, closed I could not make ads on Facebook anymore. They thought I was doing something nefarious, so I completely closed that out. And it's it's the Orgim order on Facebook. But, yeah, I got a threads, Instagram, Twitter, x. I've, Twitch, and Discord even. The Discord is open for everything. And, yeah, like I said, oh, and the YouTube too. So the YouTube's where you can see my music, it's being distributed through CD Baby. So I mean, it's everywhere. You just look up, The Orban Order is the band name. Nice. Amazing. And, yeah, you can find I I have, like, 3 singles out, that are that are fun. Just power metal stuff, and and, it's yeah. And, that's it. Now you've had to go through a whole branding process, so you're also a brander. Dude, marketing. Yeah. I didn't know how hard, book marketing was. I mean, at the end of the day, I'm luckily in a tech sector where I work for a lot of marketing teams. So it completely coincides. I mean, running ad campaigns on AMS, you know, Amazon, media services and running ad campaigns on Facebook ads and and, just handling I mean, I did all the typesetting for my book too in InDesign. I just Nice. I had to learn that. Because I'm I I know all the creative suite, you know, I do all my own video editing too. And, you know, just just from the years of doing stuff, and, the, you know, and I do on my own music editing on Garage Band. And it's, like once you once you can work one program, everybody copies everyone. Right? So it's it's you know, you could pick if you wanna do it, you could do it. Not the best at any of it, but Yeah. I can release it. Yeah. Well, awesome. It it's so cool to hear, you know, we've we've talked to a lot of others who have this creative spark in them. So it's it's been great to hear everything in in your journey, and it's I'll definitely follow along and grab a copy of that book because I like sci fi books. I don't I don't like biographies or things like that. So we'll put your links and all the information so people can follow along, but we definitely appreciate the conversation today. Awesome. Thanks so much. Yeah. Thanks so much, Scott. And there's an audio book too if you guys don't wanna read. I like to read. I like to read.","67ea3f7d-7d56-4022-ab8b-ae2397df6ad1",[2468,2469,2470],"b27a6428-ab10-474e-be8d-72e4e40146ec","3da559f6-d763-414d-82ec-4c1c09927e45","5f68c3c6-6abb-4b51-bc32-dd8ba6f8c44b",[],{"year":978,"number":1830,"id":1842,"episodes":2473,"show":2474},[1844,1845,1846,1847,1825],{"title":1849,"slug":1850},{"id":2476,"slug":2477,"vimeo_id":2478,"description":2479,"tile":2480,"length":159,"resources":12,"people":12,"episode_number":213,"published":2481,"title":2482,"video_transcript_html":2483,"video_transcript_text":2484,"content":12,"seo":2485,"status":19,"episode_people":2486,"recommendations":2488,"season":2489},"63108e0e-f091-4419-8086-3912f481aea5","wtf-is-mach","941314137","The term Jamstack has been floating around for the past few years, but a new term is starting to make the rounds. Join Matt as he explores MACH, and how it compares to Jamstack. ","7c248c06-500e-4466-8cf2-4c579954f55f","2024-06-25","WTF is MACH?","\u003Cp>Speaker 0: Everybody. Welcome back to another episode of Buzzword Wilderness. I'm Matt. And today, we're looking at 2 very important tech architectures you've probably heard of, which are Mach and Jamstack. We'll talk about the different frameworks, how they shape the future of digital experiences, their unique features, and how you can pick the right one based on what you need at the moment.\u003C\u002Fp>\u003Cp>So what is mock? Mock technology is a way to build, platforms that are agile, flexible, and scalable. And, actually, each part of the mock acronym stands for a modern best practice in software development. So the m stands for microservices, which is splitting into smaller separate services instead of a big monolith. The a stands for API first.\u003C\u002Fp>\u003Cp>Make sure all the APIs are a top priority when you're designing something or or building something from from the ground up. The c stands for cloud native. So applications are created to take full advantage of the cloud so they can easily scale up and are resilient. And then h stands for headless, which separates the front end from the back end, allowing different front end tools to be used without, you know, back end limitations being all kind of bundled together. So what is Jamstack?\u003C\u002Fp>\u003Cp>Jamstack stands for JavaScript J, APIs a, and markup m. It's a way to build websites, to be more fast, secure, and easier to scale. So JavaScript of that, you can manage dynamic features and user interactions. APIs, server side tasks are simplified into reusable APIs. And then markup created in advance during the build time, it boosts the speed and security by delivering static files.\u003C\u002Fp>\u003Cp>So comparing mock and jamstack. As you can probably guess, both mock and Jamstack obviously use APIs to make services more flexible and independent, but they serve different purposes. So think of mock being designed for creating dynamic scalable applications that work across different platforms. And Jamstack, on the other hand, really hones in on the web development process to make sites faster and more secure with pre rendered content. Mock offers more flexibility with a modular setup, while Jamstack is really good for websites that need high performance and strong security, thanks to use of the static content.\u003C\u002Fp>\u003Cp>So mock and Jamstack offer different ways to create efficient, modern, and strong digital experiences. Choosing between them depends on on exactly what you need, whether you need a complete digital overhaul of your back end infrastructure, for applications, or if you just need a more streamlined web development process. You can't go wrong with either one. Both are really gaining popularity. Jamstack, in particular, has been around for a while.\u003C\u002Fp>\u003Cp>Mock is a little bit of a newer term. So, hopefully, this helped you kinda navigate the forest of of buzzwords on mock and jamstack. I really appreciate you spending time with me out here in the woods of Virginia in 90 degree Fahrenheit, heat. Can't wait for the next episode, and I appreciate the time.\u003C\u002Fp>","Everybody. Welcome back to another episode of Buzzword Wilderness. I'm Matt. And today, we're looking at 2 very important tech architectures you've probably heard of, which are Mach and Jamstack. We'll talk about the different frameworks, how they shape the future of digital experiences, their unique features, and how you can pick the right one based on what you need at the moment. So what is mock? Mock technology is a way to build, platforms that are agile, flexible, and scalable. And, actually, each part of the mock acronym stands for a modern best practice in software development. So the m stands for microservices, which is splitting into smaller separate services instead of a big monolith. The a stands for API first. Make sure all the APIs are a top priority when you're designing something or or building something from from the ground up. The c stands for cloud native. So applications are created to take full advantage of the cloud so they can easily scale up and are resilient. And then h stands for headless, which separates the front end from the back end, allowing different front end tools to be used without, you know, back end limitations being all kind of bundled together. So what is Jamstack? Jamstack stands for JavaScript J, APIs a, and markup m. It's a way to build websites, to be more fast, secure, and easier to scale. So JavaScript of that, you can manage dynamic features and user interactions. APIs, server side tasks are simplified into reusable APIs. And then markup created in advance during the build time, it boosts the speed and security by delivering static files. So comparing mock and jamstack. As you can probably guess, both mock and Jamstack obviously use APIs to make services more flexible and independent, but they serve different purposes. So think of mock being designed for creating dynamic scalable applications that work across different platforms. And Jamstack, on the other hand, really hones in on the web development process to make sites faster and more secure with pre rendered content. Mock offers more flexibility with a modular setup, while Jamstack is really good for websites that need high performance and strong security, thanks to use of the static content. So mock and Jamstack offer different ways to create efficient, modern, and strong digital experiences. Choosing between them depends on on exactly what you need, whether you need a complete digital overhaul of your back end infrastructure, for applications, or if you just need a more streamlined web development process. You can't go wrong with either one. Both are really gaining popularity. Jamstack, in particular, has been around for a while. Mock is a little bit of a newer term. So, hopefully, this helped you kinda navigate the forest of of buzzwords on mock and jamstack. I really appreciate you spending time with me out here in the woods of Virginia in 90 degree Fahrenheit, heat. Can't wait for the next episode, and I appreciate the time.","23e9413b-0c1c-4cd0-aaa3-433ee7b05a79",[2487],"02fa84b4-65cf-448c-a944-86e962a60681",[],{"year":978,"number":674,"id":2490,"episodes":2491,"show":2496},"e09fe5f4-2319-48aa-b211-898b0b896829",[2492,2493,2494,2495,2476],"bfa56eb0-8caa-4561-9bca-6899379e3f22","17b03db0-01cb-44ba-9fe3-927f39adac45","03ae6f72-5aee-4d8d-b924-7494c9fe947d","1bf494ec-1cae-4017-9231-81aa9f82cfe2",{"title":2497,"slug":2498},"Buzzword Wilderness","buzzword-wilderness",{"id":983,"slug":2500,"vimeo_id":2501,"description":2502,"tile":2503,"length":2504,"resources":12,"people":12,"episode_number":177,"published":2505,"title":2506,"video_transcript_html":2507,"video_transcript_text":2508,"content":12,"seo":2509,"status":19,"episode_people":2510,"recommendations":2513,"season":2514},"deepgram-audio-podcast-summarizer","964316267","Join Kevin and Damien Murphy, Solutions Engineer at Deepgram, as they use Deepgram to build an audio podcast summarizer in Directus Automate.","e77b0dc4-21ba-4657-aa71-bfb4c09cd917",84,"2024-06-21","Build an Audio Podcast Summarizer in Directus Automate with Deepgram","\u003Cp>Speaker 0: Hello. Hello. Hello. Hello. Damien, you are still muted, but we are we are here.\u003C\u002Fp>\u003Cp>Hello. I'm Kevin.\u003C\u002Fp>\u003Cp>Speaker 1: I'm Damien. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Nice to meet you. Yeah. For the next hour and a half, we're gonna be trying to get things to work maybe successfully. We'll see. We'll talk about the project in just a moment, but I actually thought some more thorough introductions might be in order.\u003C\u002Fp>\u003Cp>Damien, would you like to tell us who you are and who you work\u003C\u002Fp>\u003Cp>Speaker 1: for? Yeah. I'm Damien Murphy, applied engineer here at Deepgram. So, you know, working with customers, building, you know, real time low latency voice spots and transcribing their audio.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Excellent. And I am very, very, very fond of Deepgram, so I'm really excited and thankful that you're joining us for the next little bit. My name's Kevin. I work on the director's core team, and in this workshop or rather, this workshop is part of, Leap Week.\u003C\u002Fp>\u003Cp>Hopefully, you are already aware, but Leap Week is our week of announcements where we announce new features and also run a series of other events to celebrate directors and our community. We're starting to near the end of the week now, but don't worry. There's still lots more to come. Tomorrow, we are doing a community networking social. And right now, right here, we're gonna be building some cool stuff with directors and Deepgram.\u003C\u002Fp>\u003Cp>Maybe if we take a moment to talk about the project, that'd be a cool way to to stop. So podcasts. I love podcasts. Podcasts are actually all standards. Podcasts are just an RSS feed that contains some metadata and links to episodes.\u003C\u002Fp>\u003Cp>And in this workshop, we're going to string together using Director's automate and flows, our kind of visual automation, tool, a, you know, semi complex automation where we are going to go grab a RSS feed of a podcast, go grab the latest episode, send it off to Deepgram's transcription service. So maybe before we I jump straight into the whole project, maybe we break down each part. Could you tell us a little bit about, Deepgram's transcription service?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So we're able to process, you know, audio, video, pretty much any format, and turn that into, text. Right? So we'll basically transcribe every bit of speech that's spoken and then give you back a word level and time stamp level, you know, what was spoken. We also have multiple other APIs, which we'll get into a little bit later.\u003C\u002Fp>\u003Cp>But,\u003C\u002Fp>\u003Cp>Speaker 0: I mean, we can we can rock on now. So we're going to go and transcribe these podcasts. I listened to one the other day that was like an hour long. Then we're gonna use this audio intelligence. Tell us about this one.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So we have the ability to pass the transcript once it's transcribed through our audio intelligence features. So this can do things like sentiment analysis, summarization, intent detection, and topic detection. And this can be really useful for, you know, pulling out that valuable metadata, and it's all time stamped as well. So you can even, you know, build an overview of the podcast, using those, audio intelligence features.\u003C\u002Fp>\u003Cp>Speaker 0: Cool. And then you can also understand it on, like, a segment basis as well. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. So each part of the the audio that comes through will pick up topics as they happen. So we can do major topics and, minor topics as well.\u003C\u002Fp>\u003Cp>Speaker 0: Awesome. Oh, that's really interesting. Justine, a question here in chat. And, yes, please do use the chat. I will answer the question while encouraging you to use the chat.\u003C\u002Fp>\u003Cp>Will this demo be available on demand? Yes. Like everything at Leap Week, it is all recorded. It will be available on DIRECTUS TV tomorrow. In fact, the workshop from yesterday with Twilio is already up in our brand new show called Enter the Workshop as you will be able to watch this on demand, of course.\u003C\u002Fp>\u003Cp>So, but by being here live, you have access to the chat, so take advantage of it. I'll be monitoring it. You can ask either of us questions about Directus or Deepgram or what we're doing, and we'll be more than happy to answer in that. So we're gonna transcribe a latest podcast episode. We are going to use the audio intelligence, features that, Deepgram offers.\u003C\u002Fp>\u003Cp>I'm gonna struggle because Directus in Deepgram both start with these. So sometimes I might do this. I feel myself maybe doing it already. And then finally, we will use, one of the newer Deepgram products, Text to Speech. Tell us about this one.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So we recently released, our text to speech. It's one of the the lowest latency text to speeches on the market, with high quality voices. So you can get a very low latency text to speech generated at a very low price point as well.\u003C\u002Fp>\u003Cp>Speaker 0: Just to help me understand, because latency only, I suppose, matters well, it doesn't only matter, but it matters more when you're doing live, like, real time stuff. So you can use this real time as well?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Absolutely. And that's where we see a lot of the demand in the market is for, you know, building real time voice box with sub second latency. So with this text to speech, you can get about 250 milliseconds of of, latency for time to first byte.\u003C\u002Fp>\u003Cp>Speaker 0: Excellent. We won't be using it real time today because obviously podcast episodes are already static hosted files, but that's, I suppose, where the latency matters. So you can do, like, true conversational voice bots, I suppose. Cool. So we're gonna do all of that.\u003C\u002Fp>\u003Cp>Just to summarize how this is going to work, we are going to first build a flow that will take in a podcast URL. We will grab the latest podcast episode from that podcast feed. We will send that off to Deepgram to receive a transcript, then we're gonna send it off for, text intelligence, so text to text API that Deepgram offers. We'll talk a little bit about why they're separate when they don't have to be. You can do those 2 steps together, but it will become clear as we go through the workshop.\u003C\u002Fp>\u003Cp>Then armed with a summary of that podcast, we are going to send it back off to Deepgram to generate a short summary, I suppose, in the audio bite, which we will then save back to the director's project so you can go and listen to it at your leisure. Any questions in the chat? Any thoughts, Damian, before we kick off?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. If anybody wants to sign up for Deepgram, we give $200 in free credits as well. So, you'll be able to transcribe about 750 hours of of audio for for free, essentially.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It's really, really cool. Really nice way to get started. And indeed, that is what we will be doing today. Okay.\u003C\u002Fp>\u003Cp>I think that means we are ready to kind of, jump in and get started. And the very first thing we are going to do here is we are gonna set up a directors project running locally now. I will give you a very quick summary of what Directus is in case you're coming from the Deepgram world and you've not heard of Directus before. So Directus is a really cool back end that you can use as a developer to build your applications. You connect it to a database.\u003C\u002Fp>\u003Cp>We provide developer tooling and this really beautiful web application which you can use to interact with that data. And it's, suitable for handing to non developers as well, which is not very typical of back end, back end tooling. So we're gonna spin this up, and then we are specifically going to use Directus Automate, which is part of this application in order to build this kind of multistep flow, something that looks a bit like this, except each one will take on one of the steps we described in our project. This project will use some, extensions that we built and published to the marketplace, which is available in all directors projects. We can go and do that together.\u003C\u002Fp>\u003Cp>And then that very final step where we create a new audio file and save it back to our directors project, we're gonna build that extension together because it doesn't currently exist. So that's that's the kind of rundown of how this is going to shake out. So with that in mind, I have this empty directory here on my local machine. It's just this empty directory called live. Let's move into it here, and we're gonna spin up a director's project.\u003C\u002Fp>\u003Cp>The first thing we're gonna do is create a docker compose dot yml file. And I do happen to have one here. This is the docker compose file for spinning up directives locally with a SQLite database. There isn't too much to talk about here. We will use the latest version of directives that has been published on Docker Hub.\u003C\u002Fp>\u003Cp>We have 3 volumes. So these are, directories that exist inside of the Docker container that we are going to map to local directories. And you'll see exactly what these do in about a minute. We need some environment variables, a key and a secret. You should replace both with random values.\u003C\u002Fp>\u003Cp>For the sake of this workshop, I think replace with random value is random enough, so we'll leave that be. The initial admin email and password, which, of course, you can go change. The database client and being SQLite is just a file, So we're just telling it where that file will live. We have WebSockets enabled so you could do, like, real time subscriptions. It's part of my kind of default snippet that I have.\u003C\u002Fp>\u003Cp>We're not gonna use that today. And then we're also turning on extensions auto reload, which is gonna be really important for the developer experience of building our extension at the very end of this work shop. So with all of that done, you can just run docker compose up. No. Oh, did I hit save?\u003C\u002Fp>\u003Cp>I did not. There we go. And so it's now gonna go ahead and, spin that up. And you'll notice immediately an upload and extensions and a database folder. So they are the 3 volumes that are inside of the Docker container, but also mapped to a local volume.\u003C\u002Fp>\u003Cp>It did a whole bunch of, like, first time, you know, seeding, and then we have directives running right now on local host 8055 with my admin, email, and password that we set in the Docker Compose. That's it. That's how I was having set up directives. This is the full fat version of directives running here. It's the same version we host in Directus Cloud, and we can with that jump straight in.\u003C\u002Fp>\u003Cp>Damian, I might just give you a quick tour of it if that makes sense. We have a database. Yeah. We have a database. It's that SQL like database.\u003C\u002Fp>\u003Cp>In here, we can create tables in that database and we can query them. We can interact with the data. Great. We also have users that we can create. We have a whole auth service.\u003C\u002Fp>\u003Cp>So immediately, we have this admin user that you can invite other users. Users in turn can have various permissions, which grant them access to do different actions on collection. So create, read, update, delete, and share. We also connect to your asset storage, or you can save files locally as well. So this will connect to an s 3 bucket and, an Azure storage, Backblaze, and various others.\u003C\u002Fp>\u003Cp>We are gonna use this later to actually save the summary back, from Deepgram. By default, if you don't say anything, it will be just local file storage, and it'll actually just get dumped here in this in this uploads folder right here in the sidebar. We have a little insights dashboard builder. We used that in yesterday's workshop. And then over here in settings, we have access to flows, which is the automation builder, which is what we're gonna use today.\u003C\u002Fp>\u003Cp>I think the only other thing we wanna do before we kick off is let me just have a quick think here. The only other thing we wanna do is our public role. So this is this represents, all of the requests that are made that have no permissions that have not authenticated. And I'm just gonna give it the ability to read and write files. In the real, you shouldn't do this.\u003C\u002Fp>\u003Cp>But for the sake of this, it'll be fine. What's the worst that could happen? So this will allow us to read and write files without needing to authenticate with with directors. What else is needed? We need we need the extensions from the marketplace.\u003C\u002Fp>\u003Cp>So there are 3 extensions we need here. If I type in Deepgram, I built a few. I don't I don't like the spinning wheel. There there it is, Deepgram. So we have the AI transcription operation, and we have the AI text intelligence operation.\u003C\u002Fp>\u003Cp>We believe in making things nice and small and modular, so we have separated them, and each one's very simple. In reality actually, question. I think in reality, you could do the the intelligence at the same time as the transcription. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. You can send a single request, and you'll basically just enable those parameters, and you'll get both back.\u003C\u002Fp>\u003Cp>Speaker 0: Great. As these are don't know what's going on there. Although, I've had issues with my Internet all day, so I'm gonna go out. I'd rather this was a bit slow than you not being able to hear or see what's going on. So we did the AI transcription, and then we had the AI text intelligence.\u003C\u002Fp>\u003Cp>So we'll just install both of those. So these were released, last month as part of our directors AI bundle of, of operations for our automation builder. And then there's one more that I created just to make our life a bit easier today, and it's this extension here, RSS to JSON. It will allow you to go off and get an RSS feed, and it will return it will, pass it and turn it into a JSON object. And this will be really helpful because we obviously need to pass the RSS feed of a podcast.\u003C\u002Fp>\u003Cp>So we'll go ahead and install that too. There we go. We'll just give it a quick refresh as it is prompting us to do, and we're ready to rock on. So we're gonna create a new flow. Podcast summarizer summarizer summarizer.\u003C\u002Fp>\u003Cp>Sure. I don't think summarizer is a word, so I don't know why I am. So hooking up on it. And we can trigger this automation in 1 of 5 ways. We can do an event hook.\u003C\u002Fp>\u003Cp>So an event hook can be triggered whenever or will be triggered whenever something happens in your database. So it could be, a new item is created in the posts collection or a new user is registered or a new file is uploaded. We have, webhooks, which takes an inbound HTTP request, so you can receive data from third party services. In the world of Deepgram, how we actually use it here on DIRECTOR's TV, our on demand shows all have transcripts. Some of our shows are very long, so we use Deepgram's, asynchronous callback mode.\u003C\u002Fp>\u003Cp>So it goes up and does work and then pings you once it's done. And so that would be a webhook trigger. You can run them based on time, you know, schedules. You can have flows trigger other flows. So if you have complex, you know, use cases, you can kind of bounce portions off into their own modularized automations and then return the data back up.\u003C\u002Fp>\u003Cp>And finally, manual. And this will add a button, this will add a button to the side of the data studio when you're in collections or item pages, and you can go and trigger it from there. We're gonna use a webhook because I just want the ability to call it really quickly and just making a quick call request is probably gonna be the easiest way to do it. I don't care about any of this because it really is just a quick trigger. So if I hit this URL in fact, let's do that.\u003C\u002Fp>\u003Cp>I open a new, let me just build these 2 terminals. If I open a new terminal and just call this URL and refresh here, we'll see it's been triggered once in the logs. So I think that's gonna be the quickest way of just constantly running it as we go to to test it. Okay. Any questions so far?\u003C\u002Fp>\u003Cp>Anyone in the chat? I I raised through this. I got us to this point super, super quick. We We scheduled an hour and a half in for this, and I think it won't take long at all. So unless questions are asked.\u003C\u002Fp>\u003Cp>So, feel feel free. Not that you have to, although you need questions. A question for you though, Damien. With Deepgram's callback mode, can you give some use cases for when that's useful? Because it's a really good, you know, demonstration, I think, of the fact that you can do you can trigger flows based on webhooks.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. A lot of customers use it, because it allows their server to, you know, get back to doing other tasks. Right? So rather than waiting for the response, the more features you enable, the, you know, the longer the request will take. So, you know, adding summarization and topic detection, entity detection, you know, it it can go up into, you know, the 30, 42nd range, and as the audio gets longer as well.\u003C\u002Fp>\u003Cp>But yeah. Like, by default, if you've if you're just transcribing, you know, you can transcribe an hour long podcast in probably, you know, 10 seconds. Right? So, one of the other cool features is you can pass a URL to, like, an s three Booker. So you can tell us, hey, you know, when you're transcribing it, instead of me sending you the file, go pull it from an s 3 Booker.\u003C\u002Fp>\u003Cp>And you can even tell us to put it back into an s 3 bucket as well, which is pretty cool.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. We have actually, over in our docs, I've written a post before a deep gram post right here. Right. But that makes sense. It's to stop you having, like, hanging long connections open.\u003C\u002Fp>\u003Cp>Right. And that that makes total sense. So this, what this does, is it listens for any file upload. It verifies that it's an audio file, and then it will send the URL of your of your file directly to to Deepgram authenticated with your token. It has a transcript returned, and then you can save that straight back to the file.\u003C\u002Fp>\u003Cp>So it's placed right next to the file, which is really cool. It's a really straightforward automation here. And this also featured on, let me find it. This also featured on our quick connect series right here. So it's that same project but over in video form.\u003C\u002Fp>\u003Cp>So if you're interested in kinda learning more about what's quite a common automation, I think, with Deepgram, you can see how to set that up. Okay. First thing we need to do then is we need to go ahead and get a podcast, like, actually go get, an RSS feed. I have loads of podcasts. I actually agonized over which to pick.\u003C\u002Fp>\u003Cp>So I picked Darknet Diaries. You heard of Darknet Diaries?\u003C\u002Fp>\u003Cp>Speaker 1: No. Haven't heard of it.\u003C\u002Fp>\u003Cp>Speaker 0: Fantastic podcast all about cybersecurity. Really, really, really good. I just listened to just listened to this latest episode here, Anom, like, 2 days ago, came out June 4th. It was it was so good. It was not what I expected.\u003C\u002Fp>\u003Cp>But a 146 episodes of Darknet Diaries and any I'm gonna say true podcast because I think Spotify has started to screw with the definition of a podcast is just an RSS feed, and they all follow exactly the same format. If it's not if it doesn't have an open RSS feed, it isn't actually a podcast. It's an appropriation of the term podcast. But the podcast is this kind of XML document, this RSS feed, and they all have, you know, some metadata that, you know, will be shown in your podcast acts. And then they have a number of items here.\u003C\u002Fp>\u003Cp>So this item here that I'm highlighting is a single episode. It's that one we just saw, Anum. And you'll notice here in the enclosure, there is this attribute called URL, and that contains a direct m p 3 link. And that's how podcasts will work. And that's really handy because with, Deepgram, you can send a a binary file or you can send a URL.\u003C\u002Fp>\u003Cp>And podcasts have this URL just hanging out there. So our job is get the URL. I can take this whole feed URL and use our brand new, I built it yesterday, RSS to JSON, RSS to JSON operation here, and I'm gonna call it feed. The fact I call it feed will become clear in their own. Why does this key matter when it has a name?\u003C\u002Fp>\u003Cp>Why does this key matter? We'll talk about that in just a moment. We'll stick the URL in there, Save it. Hit it again. And I think we configured this flow to actually return the data from the last step.\u003C\u002Fp>\u003Cp>So we are expecting to basically see it here. Yeah. There it is. The whole RSS feed, but turned to JSON. If we refresh here, we can also see it in our logs.\u003C\u002Fp>\u003Cp>There it is. So there it is. That's pretty cool. There's our item. Where is it?\u003C\u002Fp>\u003Cp>Here we are. There's our item array, and there is the MP 3. Now it does actually say in the docs of this, extension that I built yes yesterday. If ever there's an attribute, you'll note that you may remember it was an enclosure. I can show you.\u003C\u002Fp>\u003Cp>It was an enclosure tag with an attribute of URL. And somehow I had to map that to a JSON object. So the chosen method was to make it an object and the attributes are just underscored. I think that's valid. So now we wanna dig in and actually get that data.\u003C\u002Fp>\u003Cp>We wanna get that URL. So we will create a new we will create a new, step here. And this one, I will call, latest, I guess. Latest because we just wanna get the latest episode. This has all the episodes.\u003C\u002Fp>\u003Cp>And we're gonna just run some JavaScript in here. Now the this, kind of, boilerplate here, is it is the zoom level okay?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It looks okay.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Cool. Have this data property. And data is a big object and properties in that object include the keys of all of the steps. So I can get the I can go and get the feed step by, you know, going data dot feed, and that's that whole object that was returned.\u003C\u002Fp>\u003Cp>So if you name the keys, you can more easily pick specific values from all the way up what we call the data chain, and every operation adds a new object to the data chain. So we have data dot feed here. Now I happen to know because I didn't wanna I didn't wanna sync too much time here. I know where the value of the URL is. It's in dot RSS dot channel dot items dot item That's an array and we want the first item.\u003C\u002Fp>\u003Cp>That's the value of the episode. Suppose we'll just store that. And now that that episode had a ton of data, how long is it? When was it published? What's the description?\u003C\u002Fp>\u003Cp>What's the title? What's the cover art? The m p 3, obviously, and a whole bunch of additional metadata. It was huge. It was a really, really big object, actually.\u003C\u002Fp>\u003Cp>The ID, the pub date, the link to the, like, web website, the description formatted, the URL, and data about the URL. Some data specifically for iTunes, the author, iTunes summary so much so much. But actually not so much. That's the end of it. I reached the end, but significant.\u003C\u002Fp>\u003Cp>We don't need all of it. We only need some of it. So we're gonna just stop pulling out some values. So what we'll do is we'll grab the date That feels like a viable thing to to store. We'll turn that into a JavaScript date.\u003C\u002Fp>\u003Cp>What was it called? Pub date. Pub date. And I know that we want it in an just in a an ISO string. So that kind of standardizes it.\u003C\u002Fp>\u003Cp>So I don't think it comes in an ISO string. No. It comes in whatever this archaic thing is. That's the date. We want the title that also fills the digits at episode dot title.\u003C\u002Fp>\u003Cp>We could grab the description. There are a few variants of this description. Taking a look. Let's take a look what's the difference. This one has HTML tags, p and 2 break tags.\u003C\u002Fp>\u003Cp>This one does not. So this is the one we want here. The Itunes summary. Itunes colon summary, which means we have to use this syntax to dig in there. And finally, the actual URL, of course, episode dot enclo enclosure_url because it was it was a an attribute.\u003C\u002Fp>\u003Cp>Okay. Looks legit. Save that. Let's run it again. Nothing.\u003C\u002Fp>\u003Cp>Great. That's not what we want. What happened here? To ISO string is not a function. Oh, because it said to ISO sting.\u003C\u002Fp>\u003Cp>That's a typo. Ring. There it is. The date, the description, the title, the URL. Cool.\u003C\u002Fp>\u003Cp>Yeah. It's a pretty nice little automation builder here. Now we have the URL. I mean, strictly speaking, we didn't need that step. Right?\u003C\u002Fp>\u003Cp>We could just crack on, but I like just reducing down that complex data structure into something quite known. So that we called this latest. We'll need that in this next step, which is actually gonna be the AI transcription operation that, that we built and released. So there are some options here. The first thing we need is a Deepgram API key, which you can get from your Deepgram dashboard.\u003C\u002Fp>\u003Cp>We'll do that together in a moment. You need a full file URL, which we have. It's the it's the m p 3. You can provide a callback URL optionally and then sort of flip over into callback mode, which again stops long hanging, you know, connections, but this will be fine for this. We allow you to enable diarization, which do you know why it's called diarization?\u003C\u002Fp>\u003Cp>This isn't leaving. I don't know the answer.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It could be called speaker identification as well, but, yeah, I think the research term first is a diarization. So it basically tells you who's speaking when you have a mono channel, and multiple speakers. If you have multichannel audio, you you don't really need to diarize, because you know each speaker's on a different channel. But, yeah, a lot a lot of people have a single channel, especially with a podcast.\u003C\u002Fp>\u003Cp>It's it's not multichannel.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. And, thank you, Ramsey. I'm glad I caught it really quickly, but, yes, there was a missing r in ISO string. So you can optionally enable diarization, and then you can also add keywords. Talk to us about keywords.\u003C\u002Fp>\u003Cp>Someone who works for a word that sounds like directors, I'm very, very intimately familiar with this.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So so keywords allows you to kind of increase the probability that we would, you know, pick up the rectus and direct us. Right? You know, as a single word versus, like, direct us. Right?\u003C\u002Fp>\u003Cp>So if you put in that keyword with the spelling and then you increase the intensifier, and the intensity is actually it's a exponential scale. So as you go up higher, it gets extremely strong. Yeah, value of 1 or 2 is is pretty normal. If you were to put in a value of a 1,000, nearly every word will start turning into direct us. But that kinda gives you an idea of how you can leverage that feature.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Interestingly, it's not direct to us. It's always directors. Like, I am the director of the film. That's always like if when it's wrong, that's how it gets it wrong.\u003C\u002Fp>\u003Cp>We don't need to use keywords for this. So first thing we'll need is a Deepgram API key. Here is our director. Here is our Deepgram console. Signed up for an account.\u003C\u002Fp>\u003Cp>And you can go make a new API key. You can give it a nice name here so we can call this leap week work shop, workshop. You can optionally set an expiration. I will do that. I will expire this after 1 hour.\u003C\u002Fp>\u003Cp>Right? Because I don't we won't be going for more than an hour, and then this key will just stop working. You can also, add some tags, but this is the thing that's interesting. You can change the permissions of the key, which is nice. Yeah.\u003C\u002Fp>\u003Cp>Do you have any notes about this or just yeah. You can do that.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Like, if if you have certain needs, right, sometimes you might wanna generate keys, like, more API keys with an API key.\u003C\u002Fp>\u003Cp>Speaker 0: Build an admin. Like, if you're if you're creating this as a service, for example, you're using Deepgram in, like, yeah. Cool. That makes sense. You get an API key, which probably shouldn't share, but mine is in an hour and a half, and it has a fixed amount of credit and no credit card.\u003C\u002Fp>\u003Cp>So the US, we'll pop the Deepgram API key in there. Next thing we want is the file URL. You can add dynamic values using mustache impacts, double squigglies on each side. The last step was called latest and the value was URL. So that will resolve to the full URL that was inside of that enclosure.\u003C\u002Fp>\u003Cp>And I think we'll leave everything else. I think that's that's the shortest version. I'll call this transcription transcription. Sure. Hit save.\u003C\u002Fp>\u003Cp>No. Let's try it out. So now it's taking a little bit longer because it's not just making one HTTP request. We are waiting for for it to happen. Now by default, I happen to know because I built this extension.\u003C\u002Fp>\u003Cp>We do turn on a couple of features. So I'll wait for this to finish and then we'll talk about those features. Maybe taking a hot minute there. Has it? Oh, oh, there we are.\u003C\u002Fp>\u003Cp>There we are. Boom. Look at that. Huge. Right.\u003C\u002Fp>\u003Cp>Before we look at the data structure that comes back, I will tell you that we are using smart format and we are using the Nova 2 model. So maybe let's talk briefly about each. Should we start with the model?\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm. Yeah. So so the base model is our oldest model. So that was, you know, from kinda 2020 18, 2019 era. It's an extremely performant model, but the accuracy is is a lot lower.\u003C\u002Fp>\u003Cp>Some customers still opt to use it because it is just so compute efficient. And then we have our enhanced model, which, you know, added a bit more compute to it. But, yeah, our nova 2 model is the most accurate model that we have, and it's, yeah, available now in 36 languages, and we're we're adding more languages every month.\u003C\u002Fp>\u003Cp>Speaker 0: Nice. And that is what we're using here in this, operation. And then what smart format do? I think smart format basically checks a bunch of other boxes for us.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So smart formatting, is actually baked into the model. So the model itself when it's transcribing is is generating the formatting. If you turn that off and you enable, like, punctuation and and numerals and things like that, that will apply post processing formatting, and which tends to lose a little bit of the, the context. Because, you know, some like, the number one isn't always meant to be a number.\u003C\u002Fp>\u003Cp>Right? Like, if if I you know, I am the the one and only. You don't want the digit to come in there. Right? So that that's essentially what that's there for.\u003C\u002Fp>\u003Cp>Speaker 0: Fascinating. So we applied smart formats. So we make that we make that available. So you don't have the option to turn those off or change them. That's just what you get with this, with this extension.\u003C\u002Fp>\u003Cp>Okay. Let's look at what came back there. Big old payload. Now, just because this is a slightly I've gone into the big data structure that Deepgram returns, which, Damian, you've probably spotted that immediately. This is the first alternative is always returned.\u003C\u002Fp>\u003Cp>So I can just speed speed up our I can speed run us here. So the first thing is this transcript, which yeah? Like you said, it's nice. It's formatted. Interest I didn't know it was baked into the model and that it's not post processing and that's the difference.\u003C\u002Fp>\u003Cp>I thought it was just a shortcut to checking a few other boxes, but it isn't. It actually does something different.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And some customers will want digits but not punctuation or punctuation and not digits. So having them split out as well allows them to pick and choose between the the features.\u003C\u002Fp>\u003Cp>Speaker 0: Right. So we have reached the point where this is to Deepgram directs us. This is too big for me to just scroll through and and talk about. So what I'll do is I'll just look at the docs for this specific extension, and we can talk about about it. So this was the AI transcription operation.\u003C\u002Fp>\u003Cp>This is the data structure that's returned if it was a really short transcript. So we have the transcript. We saw that. We didn't actually manage to scroll to the end of it. Can you talk to us about the other objects that are all the other, properties that are returned?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So the words array is gonna give you the start and end times of each of the words, also the confidence that we have for that word. Like, if you detect a very low confidence word, you know, some people will actually choose to omit it. Right? It could have just been, you know, picked up from a cough or something like that.\u003C\u002Fp>\u003Cp>And, yeah, if it if it's down at, like, 5%, it's usually, probably gonna be wrong. Right? But for the most part, you'll see confidences in the high nineties. We also have the punctuated words, so, you know, you you'll get the word as it was, you know, printed out, without any punctuation or formatting.\u003C\u002Fp>\u003Cp>Speaker 0: What about that?\u003C\u002Fp>\u003Cp>Speaker 1: And then what what we apply to\u003C\u002Fp>\u003Cp>Speaker 0: They're not the same words. Like, it's a typo. Oh, it's a typo in my readme. It's a typo in my readme. Ignore me.\u003C\u002Fp>\u003Cp>I'll go and fix that another time.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. You would have seen lowercase h I and no no full stop in that case. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: And then there's also paragraphs, which is which is also interesting. Mhmm.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So we can we can split it up, by paragraphs. If you enable diarization, we'll also split it up by, you know, who who said what as they said. Cool. And you can do utterances as well.\u003C\u002Fp>\u003Cp>So that will give you kind of logical semantic breaks in in speech as well. Yeah. And if you were to enable diarization in each word, you would get a speaker ID as well. So you would have, like, speaker 0, speaker 1, and, you know, whoever spoke the word.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Lovely. And we see here the transcript is there. It's formatted, but it adds these line breaks in. So, you know, you can kind of print that.\u003C\u002Fp>\u003Cp>We get paragraphs. We get sentences. We get start ends for all of them. So It's really nice and flexible. Yeah.\u003C\u002Fp>\u003Cp>I see sentences could work quite nicely for, putting captions on a screen, like a sentence at a time or something like that. Okay. So that's the data that comes back from that. I think for this, all we really care about is this top level transcript, But the rest of it does exist. Now just a reminder, you can do audio intelligence within that single request if you're using the Deepgram API or SDK, but we've chosen to split them into 2 distinct operations so you can just have what you need, and each one can be a little more simple rather than being a kitchen sink of options.\u003C\u002Fp>\u003Cp>So let's crack on then. Let's go ahead and add the text intelligence right here. So I'll call this analyze, I think. Once again, Deepgram API key. I think I, I can't see it again.\u003C\u002Fp>\u003Cp>Oh, wait. The it's in the it's in the last one. We'll do that. We'll grab it out of here. There it is.\u003C\u002Fp>\u003Cp>There's the raw value, which again will expire by the time this is over. Right. AI text intelligence and lies. Deepgram API key and the text is going to be transcription was the name of that was the key of the last step dot transcript. Now this is a point to and it will be the last operation.\u003C\u002Fp>\u003Cp>I hate this. As a as a educators, I like, you know, lead educator. I think this lets you foot gum. If you start rewiring your operations, this value is not always the same. That's why I'm personally a big fan of explicit naming of keys and explicit inclusion of keys.\u003C\u002Fp>\u003Cp>But key sorry. Last always exists. Another one that exists is the trigger, which would give you data from that, from that very first step. So it's just a couple of conveniences there. But we will make this try and go ahead.\u003C\u002Fp>\u003Cp>Speaker 1: Is there any way to see all the available, step values or objects or explore them?\u003C\u002Fp>\u003Cp>Speaker 0: Yes. There's a number of ways that debugging flows is an area we know needs improvement. I'm gonna just save this. You can take a look over here. Right?\u003C\u002Fp>\u003Cp>And you can look through the logs and you can go, right, well, this was for each step, but and it was called latest, but you don't have the key immediately available. You can simply just log them. We have a logging step, which will add an extra operation. You can also just return it in the last step and it will return here. Or rather, I think when you configure the trigger, let's take a look, you can get all data back and that will return the entire object.\u003C\u002Fp>\u003Cp>So you have options, but no, there isn't a really nice way to do this right now.\u003C\u002Fp>\u003Cp>Speaker 1: It might be a cool addition. I know when I when I use, like, email, syntax injection, there's, like, a little list that lets me pick from them.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. No. That that makes no sense. And, this was actually the topic of back to Directus TV we go.\u003C\u002Fp>\u003Cp>Of one of our recent request reviews. What was it? It was the improvements to flows debugging. So we spoke about it for a whole hour, with our community around what they'd like to see based on an open feature request. So maybe that's something we'll see in the not so not too distant future.\u003C\u002Fp>\u003Cp>Alright. Let's try this. Now we are expecting to wait a moment for this because it's going to transcribe the whole hour, then it's gonna run the text intelligence. So I'll kick it off, but I am expecting to to wait.\u003C\u002Fp>\u003Cp>Speaker 1: And then are are flows always triggered from an API request, or is there a way\u003C\u002Fp>\u003Cp>Speaker 0: to There was there was 5 different triggers. So now we're a little bit deeper. I'll do this again because I think you're building more context around this. So the first is an event hook. So you can say, hey.\u003C\u002Fp>\u003Cp>Whenever an item is created in this specific collection or these collections, trigger the flow. So you can do event based hooks. You can either do it before the database transaction occurs so you can validate, manipulate the inbound data before it gets committed or perhaps stop it in its tracks, right, and fail out if something that isn't correct, or you can do it after the data has been committed. So that's the event hook. We have the web hook, which we are using for this just for speedy rerunning.\u003C\u002Fp>\u003Cp>We can run it on a schedule, so you can provide the 6 point cron syntax here and run it up to every minute. You can trigger it based on another flow. So one of the, operations in the list was to run another flow. You can put data in, and it will return data out so you can modularize your your flows a little. And then finally, manual.\u003C\u002Fp>\u003Cp>And I think the easiest way to look at manual is probably just a quick trip to the docs. The manual flow trigger, you pick a set of collections and it adds this button over here to the sidebar. So this sidebar, it requires you to check 1 or more you 1 or more items and hits and hit the button, or you can do it from within an individual record, an individual item. And it will send the IDs of those items into the flow as part of the trigger. That did take a little while there.\u003C\u002Fp>\u003Cp>You can additionally add this confirmation dialogue and collect per invocation values. So this could be useful for things like sending an email. Right? So you type in a message, you hit go, you've maybe picked some users or send a text message with Twilio, press a button, and off it goes. So they're the 5 ways to trigger, to trigger your flow.\u003C\u002Fp>\u003Cp>But we're just using the webhook so I can just run it just by hitting up and enter here. Mhmm. Let's, see what that big object look like. We have the summary, which is nice. It's a nice length for an hour's worth of footage.\u003C\u002Fp>\u003Cp>Can you talk to us about the rest? That's the summary, but we've got more.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So the topics, it's it's got a a lot of predetermined topics that the model's capable of picking up. You also have the option to pass custom topics. So if you have a topic that's kind of nuanced, very unusual, you can add that in as well. And that's gonna figure out, like, okay.\u003C\u002Fp>\u003Cp>Whereabouts in, you know, this transcript, right, based on, the text in in this case. And whereabouts was it talking about WikiLeaks or fake off or scammer or spyware? And and that's really useful because now you have the ability to actually jump to that position. Right? So you could imagine if you wanted to find, you know, the area that was talking about, WikiLeaks, you could just click a button, and it would jump you to that segment in in the actual transcript.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Exactly. You could build out a search, You're not searching just the raw transcripts. You're you're searching for topics because that's more realistic to people's usage. That's really cool.\u003C\u002Fp>\u003Cp>This again is quite long. So let's find our way to the to the example I I have written up here. So, yes, we get the topics on a per segment basis. You get the intents. Let's talk about intents.\u003C\u002Fp>\u003Cp>And can we talk a bit about how intents are different to topics? Because I'm a little fuzzy on it.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So so topics can can be all sorts of things. You're probably gonna have, you know, say, 10 x topics versus intents. An intent is really like if if I'm making a phone call and I want to cancel my plan, you know, or update my address, right, that might be one thing. But I may go off on a tangent and start talking about my holiday in Spain and do you know what I mean?\u003C\u002Fp>\u003Cp>And that that could be a topic, but the intent of the call really was to, you know, achieve something. And the same can be said about, you know, a video, a podcast. And so, yeah, I'm I'm interested about the intents that actually brought back for that podcast.\u003C\u002Fp>\u003Cp>Speaker 0: Let's take a look. Might have to yeah. I might have to do the mother of all scrolls here.\u003C\u002Fp>\u003Cp>Speaker 1: You could try a control f me.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I could. Yeah. You think I'd know that using a computer every day. What do we call it?\u003C\u002Fp>\u003Cp>Intent. Thank you for that. Wow. Okay. Yes.\u003C\u002Fp>\u003Cp>So Explore Samsung Smart TV features. That's funny. Though they were talking about because I just listened to this the other day. Basically, Samsung TVs have this feature built in where you could put it in, like, low low power mode where, like, it looks off, but it's not. And so the yeah.\u003C\u002Fp>\u003Cp>If you push mute 182 and then power the TV appears to be off, but it isn't. And then if you basically run spyware on it and then put it in that mode, no one knows. So instead of needing to plant bugs, you could actually just use the Samsung Smart TV, which will record to the TV, and then you just go by and retrieve it later.\u003C\u002Fp>\u003Cp>Speaker 1: And you can see how useful this intent is. Right? Like, straight away, it got us to something, you know, very interesting. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So there there's definitely gonna be intense. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That's where discuss Anum's features. That is what happened. Interesting. Okay.\u003C\u002Fp>\u003Cp>It might make a little less sense here, but in a call center context in particular Yeah. So with sentiment, and and it's pretty cool. I don't know if you're\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So with sentiment, and and it's pretty cool. I don't I don't know if you have the playground up. There's a good visualization of the sentiment in there.\u003C\u002Fp>\u003Cp>Speaker 0: Yes.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So if you scroll up and then you see just at the very top Per month. Okay. Next to summary. Yeah.\u003C\u002Fp>\u003Cp>Sentiment. So so you can track the sentiment over time. Right? Because we're giving you, like, you know, sentiment, you know, at each sentence or utterance. And if you scroll down, you can see as the sentiment changes, you know, it goes to see negative negative negative.\u003C\u002Fp>\u003Cp>So that kinda gives you an idea of, you know, what's happening throughout, you know, the show or or the phone call.\u003C\u002Fp>\u003Cp>Speaker 0: Got it. Got it. But you might only need to know the average. So I think if memory serves me right, there is also I think it is literally called average. Mhmm.\u003C\u002Fp>\u003Cp>Yeah. An average sentiment as well.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And and the average is gonna tend towards neutral. Right? Because, you know, the vast majority of of text is is kind of neutral. Right?\u003C\u002Fp>\u003Cp>It's only it's only gonna be parts of the call go negative. So, like, if if you see there's I don't know if you can search for positive, see how many results you get.\u003C\u002Fp>\u003Cp>Speaker 0: And so\u003C\u002Fp>\u003Cp>Speaker 1: I'll say 10 and then negative.\u003C\u002Fp>\u003Cp>Speaker 0: 69.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And then neutral. 81. Okay. So so so it looks like it it was kind of 5050 on the on the neutral and negative.\u003C\u002Fp>\u003Cp>Yeah. Just enough to bring it kind of back to that new Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Well, it's just gonna average the sentiment scores, which are between minus 1 and 1, I'm guessing, given that this is minus 0.0.15. Mhmm. Okay.\u003C\u002Fp>\u003Cp>So we now have a summary, and now it's time to go ahead. And, and that summary was held in the output of that. I think it's called summary dot text. Summary was an object. And now it's time to use, the text to speech, APIs.\u003C\u002Fp>\u003Cp>And to do that, we are going to build an extension, which I'm really excited about. Now for those watching, this isn't intended to be a play along, so I'm gonna go a little bit faster than I would running a hands on workshop because this is gonna be available tomorrow on director's TV. I'm also gonna turn this into a blog post sometime in the next couple of weeks so you can follow step by step in written form if that's more your thing. So, we're gonna go into our extensions folder here and MPX create direct us extent extend. This always gives me extension Extension.\u003C\u002Fp>\u003Cp>Direct. Yeah. Sure. Let wait for the latest version, please. And here are all of the extension types.\u003C\u002Fp>\u003Cp>You can create custom panels for direct us insights, the dashboard builder, custom interfaces, which are form inputs for the editor, but we are going to create an operation for flows. And I will call this Deepgram TTS, text to speech. I'll just write it in JavaScript and auto install dependencies. And given the speed of other things that have happened on my system during this session, I think we'll just be waiting a hot minute for that. But what we're gonna do here is we're gonna set up this operation, and we are going to use Deepgram's JavaScript SDK, which makes interacting with Aura, the text to speech service, Just a lot just a lot easier.\u003C\u002Fp>\u003Cp>So while that's scaffolding, let's, oh, it did it. It did it. So I next time next time we wait, we'll take a look at Aura. So we're gonna jump we're gonna jump in here. Right.\u003C\u002Fp>\u003Cp>Let's take a look inside of our new Deepgram TTS extension at the code. There are 2 files that matter. The first is app dotjs, and this describes all of the configuration. So this, says what is shown on the card here on this kind of overview and what options are presented here and then fed into the into the back end. So the API key and the text and stuff like that.\u003C\u002Fp>\u003Cp>The, App. Js, yeah, will also do things like what icon is shown here, what text and description, stuff like that. And then there is the API JS which runs server side and actually executes executes the, you know, the the operation, then it will be here where we install and use the SDK. So let's\u003C\u002Fp>\u003Cp>Speaker 1: that you can that you can build the UI through that code and do all back end process. Lot of other ideas kinda come into mind now that I see it.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And the on a lot with breakfast in multiple ways. You know? Yesterday, we built out actually, not for those watching on demand. Sorry for kind of crossing streams.\u003C\u002Fp>\u003Cp>So you probably, you you may have already seen this. If we take a look at Directus and just take a very quick look at yesterday's workshop, another thing you may not consider is I'm just mute that. You may not have considered is, you know, we have this dashboard builder, and you'll be thinking, oh, okay. You know, it's all out displaying displaying insights. You know?\u003C\u002Fp>\u003Cp>That could be useful or whatever. But what? Look at that, look at that quality there. I'll click back over here in a minute. Maybe just here.\u003C\u002Fp>\u003Cp>Here. But this panel, you pick a user from a drop down and hit call, and it would use the Twilio voice SDK to actually do a two way phone call from your browser to the target to the user's phone number. So, yeah, really, really flexible. You can very much build a lot in it. Anyway, right.\u003C\u002Fp>\u003Cp>So we're gonna create a custom operation. So first thing we're gonna do here is we are going to change the ID. The ID has to be unique across all operations in your project. So it's quite typical that, you know, people will prefix the name of their extension with their author name. I'm just gonna call this one Deepgram TTS because I doubt there will be another one called that.\u003C\u002Fp>\u003Cp>And that has to be the same in both files. So also here, the ID. We'll call this 1 Deepgram TTS. What are we gonna do for icon? We will use record voice over.\u003C\u002Fp>\u003Cp>I think it's what the one that I've used in the past for, Deepgram. And then for description, generate text to speech. Well, we don't need to save too much time insights. Just a a little visual thing. Now what we need to do is we're gonna pop in some text.\u003C\u002Fp>\u003Cp>So actually, I think we'll just leave that as it is, but we are also going to pick the model. So let's actually take this moment to pause. Could you talk to us about the models in Aura?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. In the playground that you you should be able to access, and we we literally just added it the other day. So on the top at the right hand side the very top right hand.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, text to speech. I didn't see that then. Oh, perfect. Yes. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: So you can type in any text you want here, and it will generate it. Yeah. And you can just hit play on one of the voices. Angus at the very top, actually, is my voice. So, yeah, if you ever wanna\u003C\u002Fp>\u003Cp>Speaker 0: Actually, I listened to it yesterday. That's so funny. So we have 2 here. You know what? Let's just for sake of argument, we'll just pick the top 2, Angus and Arcus.\u003C\u002Fp>\u003Cp>But they each have this model name, Aura Angus e n, and AURA ARCUS en. So we're going to provide a way to do a drop down and just pick between them. And in theory, you would populate as many as you wish, or you would take away the choice and just pick 1 and not provide this option. But we can do that. So we have this text box in the option.\u003C\u002Fp>\u003Cp>Let's, go and create a new one. So we will do field. This is what you name it. So I'll name it model. Right?\u003C\u002Fp>\u003Cp>That's like the key that we saw. We're gonna give it a visual name. So we'll capitalize that. That is ultimately just going to store a string. And then we get to provide some information in here.\u003C\u002Fp>\u003Cp>First thing we'll do is just the the width will make it full, which just means I'll go under it. So you can make them half, but whatever. But the more important thing here is the interface or the form input where you can create custom interfaces as we spoke about earlier. And the one we want is called select drop down like so. This interface has some options.\u003C\u002Fp>\u003Cp>As you would probably expect, it's the choices. And each choice has a text, and that has a value. And, like I said, we will do 2. So the text for the first one is Angus, and we can see here, Aura Angus e n. Is that what it is?\u003C\u002Fp>\u003Cp>Yeah. Aura Angus e n, and Arcus was the second one. Aura, Arcus, e n. Nice. Now the only other thing we'll do here is we'll just show it on that card.\u003C\u002Fp>\u003Cp>This is optional. This is just this is just, you know, UI further to a to a degree, but we will and sorry. It's over here in the in the overview. So we'll also bring in the model, and we will show that on the card as well. You'll see what that does in just a moment.\u003C\u002Fp>\u003Cp>Speaker 1: And what would be the default if that wasn't populated? Or is it just always It would just\u003C\u002Fp>\u003Cp>Speaker 0: be an empty card. It would just be an Oh, it would be an empty card. Just like this.\u003C\u002Fp>\u003Cp>Speaker 1: If the if the model wasn't selected.\u003C\u002Fp>\u003Cp>Speaker 0: Good question. I think it might default to the first. Did I? You could probably set a default or handle the default over on the server side. If not selected, pick this.\u003C\u002Fp>\u003Cp>I think kinda similar approach to most drop downs that you could build. So let's let's run this. Let's go npm run dev. And that's going to build our extension, watch for changes, and rebuild it whenever there's a change. Over in our first terminal here, we see here extensions reloaded.\u003C\u002Fp>\u003Cp>If I hit save, it will rebuild the extension. Directors will see that I've rebuilt the that I've that the that the extension has been rebuilt. Will it?\u003C\u002Fp>\u003Cp>Speaker 1: You might need to make a change rather than\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That's what I was that's what I was waiting on. Interesting. I might just quickly restart it and see if it needed just a one time restart. And if that continues, then whatever will might just have to kick it up the bum.\u003C\u002Fp>\u003Cp>So that's rerunning now. So I'll just save that. There you go. Extensions reloaded. Okay.\u003C\u002Fp>\u003Cp>Just needed one quick one quick kick up the bum. So let's, let's see what happens now. So we will add to this new extension on the end. There it is. Deepgram TTS.\u003C\u002Fp>\u003Cp>There's the icon we pick. That's the text. That's the title. We pass in the text, which we know is annual annualize dot summary Mhmm. Dot x, I think.\u003C\u002Fp>\u003Cp>So analyze dot summary.text. Sure. And we pick the model, and there they are. So we'll pick your voice. That's quite funny.\u003C\u002Fp>\u003Cp>I didn't know. I didn't know that. That's pretty cool. And we'll hit save. So and you see there the model is shown on the front, and that's the text input that we put in.\u003C\u002Fp>\u003Cp>So we hit save. When this, operation runs, the API side will run. So the first thing we'll do is we will go ahead and, so go ahead and just pull the model in as well. So that'll just be the the e n, you know, Angus or aura Angus e n. Let's let's do this.\u003C\u002Fp>\u003Cp>I'm really excited, actually. Right. We are gonna use the Deepgram SDK. So npm install at deepgram\u002Fsdk. Good.\u003C\u002Fp>\u003Cp>Good. Good. And we'll go ahead and, initialize this. It's funny when I was a developer advocate at Deepgram, I did this all the time. So import, create, client from not that, from Deepgram SDK, and then you create an instance, Deepgram equals create client, and the API key have to go in here.\u003C\u002Fp>\u003Cp>Yeah. Obviously, I Eric, why did I even bother hitting save? We need to pass in the API key here. We We don't really wanna hard code it in our extension. So instead, what we're gonna do is we're gonna add it here to our Docker compose file, which will bring it into the environment variables.\u003C\u002Fp>\u003Cp>So mostly because I've already forgotten it, let's grab that key again. Let's pop it in here. We'll call it deep oh, Deepgram API key. There it is. We do need to restart our Docker container whenever we update the Docker Compose file because it just reads that once it load.\u003C\u002Fp>\u003Cp>And straightaway in here, process dotm.deepgramapi key. One moment. These are fine. These are not errors. These are little warnings, not a problem.\u003C\u002Fp>\u003Cp>It's just, some of some of the way that the, yeah, some of the the build of the Deepgram SDK, but it's not a bother at all. They are just warnings. And now it's time to actually build the hands, build out the the handler. So, what happens? We press the button, it goes in.\u003C\u002Fp>\u003Cp>Now what we're gonna want to do here is ultimately we want to save a file to our director's project. And we expose a bunch of services to your project, which you can use to interact directly with these kind of directors primitives. Now, the first thing we wanna do here is we wanna go ahead and just add in here a second variable called context. And inside of here, con, const. There we go.\u003C\u002Fp>\u003Cp>Const. We want services and we also want get schema, which we'll need to, initialize the service. Services is a list of all the services. The user service, the item service, the permission service, the role service. We only care about the files service.\u003C\u002Fp>\u003Cp>So we'll just pull that out just just to make it easier. And then we'll go ahead and we will initialize a files service. A new file service. And in here, you have to pass in the schema of your project, and that thankfully is just returned in get schema. And I did just catch in the little tool tip there that that needs to be, awaited, and therefore, this needs to be async.\u003C\u002Fp>\u003Cp>Not there. That's an object. So that's us creating the file service. That means we now have an interface with which we can create a file in just a moment. Next, we're gonna go ahead and use the Deepgram SDK to generate a a stream of audio, and this was lovely by the way.\u003C\u002Fp>\u003Cp>I was speaking to to Luke, one of the DX engineers at at Deepgram recently about this. And the fact that this SDK uses the native interface makes this next bit really, really nice. So what we wanna do here is create I'll call it response for now, I guess. Or e g response maybe. Deepgram response.\u003C\u002Fp>\u003Cp>What we can do here is just use the initialized client here with our environment variable dot speak dot request. First first argument here is the source, so we can just pass in the text. So you would yeah. This is how you do it, but a shorthand because the name of the of the, property and the value is the same, you can just shorthand it there. And secondly, any options that we want to use.\u003C\u002Fp>\u003Cp>I might just create options as its own just to keep it really clear. I might just do this above here and then feed it in. So what do we wanna do here? I think all we really wanna do is we wanna pick the model. The same thing, we can use the shorthand.\u003C\u002Fp>\u003Cp>So that'll be, Angus. And we're going to tell it what file format we want it to return. If memory serves me right, you can return quite a lot of audio formats from Deepgram. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. We we support quite a few different formats. Yeah. If you wanna play it back, typically m p 3 or WAV, if you wanna stream it, like, to Twilio and things like that, you'd probably do, like, raw audio linear 16 or Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: That's cool. We'll do this because in just a moment, we are gonna need to know the file format. So I want to explicitly ask it for a files format. So we know with confidence it's gonna be an MP 3. Then finally oh, hang on.\u003C\u002Fp>\u003Cp>Let's pass this in. DG options. There we go. And finally is the file stream. I might call it dgstream, just again to be very explicit.\u003C\u002Fp>\u003Cp>Now gonna await response dot get stream Stream. That's it. That is a DG response. That is now just a live stream of audio, which is fantastic. Because it is a file stream, we can pass it straight into the file service to upload it.\u003C\u002Fp>\u003Cp>Now before we continue, we are also going to need a file name. Right? We we need to tell direct us when we create a file, what we want the file name to be at least as when you download it. And I think what we might wanna do is actually collect that from the user upfront. Like, hey.\u003C\u002Fp>\u003Cp>What filename do you want this to be? So let's go through the motions here of adding a new item here and just a new text box. Right? And we'll call this one file name, I guess. Field name type.\u003C\u002Fp>\u003Cp>I think that's all we need. File name. I And you\u003C\u002Fp>\u003Cp>Speaker 1: need to change the name. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Nope. Oh, yeah. Yeah. Yeah. Sure.\u003C\u002Fp>\u003Cp>But I mean, that's that's that will stop us getting confused, but strictly speaking, you you don't need to. So we have the file name. Great. We're not gonna bother showing it on the overview, so it can just stay here. And then over on the API side, we just wanna pull that pull that in here.\u003C\u002Fp>\u003Cp>File name. Yeah. Cool. And just to make this consistent, I might just move this, and we can call this, like, request or something like that. Just, again, kind of handle these the same way.\u003C\u002Fp>\u003Cp>So now we have the file name that we'll pass in. Let's save this. Let's, just refresh this. Make sure all of that works. And let the white screen stay for just a moment longer than I would have liked it to.\u003C\u002Fp>\u003Cp>Look, there's a problem here, which tells me something went wrong. But I don't know what. Look. They've all gone a bit they've all gone a bit funny, which means I have caused a problem. Love that for me.\u003C\u002Fp>\u003Cp>Might just zoom out one step for the sake of scanning a larger surface of code file name. I could have broken at any of those points. We haven't refreshed this in a while. Thought there. And these are, let let me scan this because they were just, these are just warnings there.\u003C\u002Fp>\u003Cp>Oh, no. No. No. That's still the warning. Interesting.\u003C\u002Fp>\u003Cp>Interesting. Let's look over here again. We passed in the the create client. Let's just save ourselves just a moment of effort and just figure out if it's in here, first of all. It was.\u003C\u002Fp>\u003Cp>Okay. That at least helped somewhat. Let's. Was it maybe the fact that you I've called it request? It's request meant no.\u003C\u002Fp>\u003Cp>That's fine. Okay. I mean, this is a top tier debugging. This. Okay.\u003C\u002Fp>\u003Cp>It's something down here. Okay. I mean, okay. WAVs. Yeah.\u003C\u002Fp>\u003Cp>So there's nothing wrong. Yeah. Let me just make sure the extension reloads. Fine. No bloody problem.\u003C\u002Fp>\u003Cp>Nothing happened here. Right. Let's open this one up, and now there's a file name. Right? So I suppose we can put in a dynamic value here and call it and, and call it latest, which was the latest episode dot I think we called it title.\u003C\u002Fp>\u003Cp>I'll save it, and then I'll just look back. Latest. Yeah. We called it title. That'll be the title.\u003C\u002Fp>\u003Cp>That's a dynamic value, which is nice. So that gets passed in as a file name. We get the stream, and, all we need to do now is upload it to directives. And the way we do that is let's do the same thing again. We'll call it, directives options.\u003C\u002Fp>\u003Cp>Just so again be really explicit. We need 3 things, I think, are mandatory here or it won't work. The first thing is the file name downloads. Whenever you download the file from director status, the file name that it will have, We've already established, so that's gonna be file name. Oh, dotmp 3, I suppose.\u003C\u002Fp>\u003Cp>I'm not sure if that's needed, but I'm gonna do it anyway. It needs a MIME type. We just call it type here. We already know that's an MP 3. I know the standard format for MIME types is this, audio slash.\u003C\u002Fp>\u003Cp>And again, that's why we specifically requested a file format. It doesn't really matter which one it was and where is it going. This matters because you can connect more than one asset storage to Directus. By default, it will use local storage, which is just this up which is just this kind of link to this uploads folder, but you can connect it to an s 3 bucket, and Azure Blob storage and and many more. So we're just telling it, hey.\u003C\u002Fp>\u003Cp>This is where we want storage. Should that\u003C\u002Fp>\u003Cp>Speaker 1: should that meme type be MPEG rather than MP 3?\u003C\u002Fp>\u003Cp>Speaker 0: No. I think it's this. I think it's I think if if I caught my mind back, I think that's right. Don't know. We'll find out in a couple of minutes.\u003C\u002Fp>\u003Cp>So that'll be, that'll be our first point of debugging. And then the final thing we'll do, although it is optional, is we will actually set the title of the new of the new file. So this is the, like, visual labeled title where the other one is what happens when you download it. That isn't strictly needed, but we'll we'll do that. Then what we'll do is we'll call it directors file, I guess.\u003C\u002Fp>\u003Cp>The new file is we will use our file service. We will upload 1, and the first value is going to be a stream. Unfortunately, Deepgram just returns a stream, so we can dump that in there. And the second one is the directors options. What is returned from upload 1 is the new primary key of the new file that's being created.\u003C\u002Fp>\u003Cp>So we will just return the rectus file. That's the whole thing. That's the whole operation. Before I good before I get all excited and describe everything that's happened, let's make sure it works because it it might not. Right.\u003C\u002Fp>\u003Cp>Nothing seems to have gone wrong here. We have a file name set, so let's trigger this one more time, and I am expecting to have to wait now because we are doing all of these steps back to back. I'm expecting to have to wait maybe 30 ish, 40 ish seconds. But we'll see. It's an hour of audio.\u003C\u002Fp>\u003Cp>So while that's happening, let's recap what's happening. We're going and grabbing the RSS feed and converting it to JSON with this custom extension you can download for free in the marketplace. We just, you know, traverse that that big objects that that's returned to just get the latest item. We didn't end up doing anything with the date, actually. Now I think about it or the description.\u003C\u002Fp>\u003Cp>We transcribe it using the AI transcription endpoint and then run text intelligence. Both of these are Deepgram to receive the summary. And then this custom built extension we just built does text to speech. You pass in some text, You pick a model from the drop down. We just pick the first two out the list, Arcus and Angus, and you give it the file name for the original file for the new file.\u003C\u002Fp>\u003Cp>And as you see here, what was returned was a string, which means there wasn't an error, which means there is our 42 second summary. Let me listen to your voice. I don't think this will come through. It it's you. It's so you.\u003C\u002Fp>\u003Cp>That's so funny. So do people do people at Deepgram just get proposition to, like, get their voices up?\u003C\u002Fp>\u003Cp>Speaker 1: 6 of the 12 voices are Deepgrammers, and then the 6 are voice actors.\u003C\u002Fp>\u003Cp>Speaker 0: It's really funny. But there's your little summary. There's your summary. That's sounds freaking cool. So let's talk about the code, and then we'll talk about what more you could do with this.\u003C\u002Fp>\u003Cp>Right? So we grab the data that was actually, you know, provided by the operation. We grab the file service and the get schema function, and we initialize a new file service that allows us to interact directly with the director's files collection. These three lines is all that's needed, and strictly speaking, you know, this object could go in here. So two lines is all that's needed to request a text to speech, a text to speech operation, I suppose, from Deepgram.\u003C\u002Fp>\u003Cp>That's all that's needed and it returns a stream of data. Then we, you know, configure all of our options for our new file we're about to create, and we upload the file by just providing the stream directly, providing the options, and that returns the primary key, which we return. That's the whole thing. Do you have any questions, in the chat? Who have been remarkably quiet, by the way.\u003C\u002Fp>\u003Cp>I hope you've enjoyed. Damien, do you have any questions, thoughts?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I'm I'm just amazed at how easy that was. Like, less than 30 times code, and it's all hooked up and it works. And a lot a lot of that code is verbose code as well. Right?\u003C\u002Fp>\u003Cp>Like it's Absolutely. Trying to be expanded. So, absolutely really easy. And, yeah, being able to pass that stream straight into the file\u003C\u002Fp>\u003Cp>Speaker 0: was very useful. It was very, very useful indeed. So what more could you do with this? Well, the obvious kind of first step is that load more data is returned from Deepgram. So you can do more with that.\u003C\u002Fp>\u003Cp>You know, you why we could save the description directly to the file if we wanted. We can provide you know, we can maybe tag it with topics. We could do whatever whatever we want here. It's completely up to us. You could also run further automations either as part of the same flow or a separate flow.\u003C\u002Fp>\u003Cp>It's all good that, you know, a new podcast has been transcribed, but do we know? Maybe we send an alert. We send, you know, an email or a notification to the user, which if we take a look here, there is a send email operation right here. So you could tell them that there's a new summary and maybe directly link them to the director's files m p 3, Because everything in directors, if we take a look at this new file here, it has this ID, and you can just go to local host 8055\u002Fassets\u002Fthat. And there is our m p 3.\u003C\u002Fp>\u003Cp>So you could link them directly to it if you fancied. Another thing is that, you know, this was a slightly conceited example in that we have to manually run it. But you could run a crop. You could use a chrome here. You could use a chrome, grab the feed and say, hey.\u003C\u002Fp>\u003Cp>Has there been anything new in the last 24 hours since I last ran? Okay. Now go and transcribe the latest episode. You know? So you could run this on a schedule and make it like a daily roll up of new shows, new episodes that you could listen to.\u003C\u002Fp>\u003Cp>Speaker 1: One other idea is, like, obviously, this was audio to begin with, so we we kinda compressed it, and we create a summary that became audio. But maybe there is, like, you know, a cool blog that you follow, but you may not have the chance to read the blog, but you'd like to listen to it. Right? You know, maybe in the car. So you could take a blog, turn it into a, like, you know, an audiobook, very easily, or you could even, you know, summarize it.\u003C\u002Fp>\u003Cp>Right? And and play it out. I had a pretty interesting idea of, like, a a real time radio station that's basically, you know, tailored to exactly what you like. Right? So you could have a, you know, maybe archive research papers being fed in, and then it's giving you kinda the updates in real time.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, pretend I didn't see that. I remember I'm not sure if it will still be live. Yeah. Here it is. This is a post I wrote.\u003C\u002Fp>\u003Cp>The date's wrong because I I worked here at that point. But what this did, it used my JSON. It literally literally live transcribed a radio station. I could edit in BBC Radio 4. Mhmm.\u003C\u002Fp>\u003Cp>And it would live transcribe it, which was super cool. Super, super cool.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So now you can even do the reverse.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Yeah. That's pretty, pretty cool. There's so much scope for this, you know, based on more complex triggers, you know, more logic in the middle.\u003C\u002Fp>\u003Cp>Like I said, you know, this could be a cron instead of a trigger. So many options. But I think that is just about our time, and we have 2 more minutes. So, yeah, thank you so much for indulging me and and and and getting involved in this and sharing your insights. I learned actually quite a lot, during that.\u003C\u002Fp>\u003Cp>Especially the smart format being part of the model itself. Fascinating. Not not what I thought.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Thanks for having me. This this is super interesting, and I'm kind of amazed that you're able to build all this from scratch, you know, in in the length of time that we're chatting here. And, yeah, it really just shows what's possible with Directus. So I might be I might be building a few little, thing that flows with it myself.\u003C\u002Fp>\u003Cp>Speaker 0: That's how we get you. That's how we get you. And you can run it locally. Right? And it's the full fat thing.\u003C\u002Fp>\u003Cp>You know, it's not like a a less good version. Like, it is the full thing. It's what we host. I will say that I need to add it to this, I think it's still in a PR, actually. I don't think we merged it in yet.\u003C\u002Fp>\u003Cp>But the RSS to JSON operation, I will show it because it is also really, like, light. I just didn't wanna have to do it now because it kinda wasn't the point. We're taking the URL as you saw, and all we do here is that all the code for this operation is here. That's it. That's the whole thing, the whole operation.\u003C\u002Fp>\u003Cp>We import a library called XML parser. We go off and get the RSS feed. And assuming everything was good, we just pass it, the attribute name prefix underscore, and then we returned we returned the past data. So that whole operation, that first one was the code. We could have built it live.\u003C\u002Fp>\u003Cp>I just didn't think it was gonna be that interesting. Thank you so much folks in the chat, for your kind words. I'm glad we made your life easier. I like lots of claps. Yes.\u003C\u002Fp>\u003Cp>There are lots of use cases, both for Directus and Deepgram and the 2 together, and I completely echo Jonathan's sentiment. Welcome to the director's community. We're very happy to have you. Great. And with that, we are at time.\u003C\u002Fp>\u003Cp>So have a wonderful rest of your week, everyone. Have a wonderful rest of your, week, Damien. And tomorrow, just a reminder that there is one more event this week week and then there's this community networking social. It is using the one and only platform I have ever done networking on that doesn't absolutely suck. So if you're interested in meeting other people who are interested or use or involved in Directus in some way, shape, or form, drop by.\u003C\u002Fp>\u003Cp>It's at if you go to leapweek.dev, it will be, localized to your time zone. But here in Berlin, in Central European, it is at 4 PM. So, yeah, hopefully, we'll see you at that tomorrow. Damien, anything else you wanna share just before we hit end?\u003C\u002Fp>\u003Cp>Speaker 1: No. Thanks very much everybody for joining. And, yeah, really interesting possibilities. This is open all.\u003C\u002Fp>\u003Cp>Speaker 0: Excellent. Right. With that, have a good rest of your day, nerds. Bye for now.\u003C\u002Fp>\u003Cp>Speaker 1: Bye bye.\u003C\u002Fp>","Hello. Hello. Hello. Hello. Damien, you are still muted, but we are we are here. Hello. I'm Kevin. I'm Damien. Yeah. Nice to meet you. Yeah. For the next hour and a half, we're gonna be trying to get things to work maybe successfully. We'll see. We'll talk about the project in just a moment, but I actually thought some more thorough introductions might be in order. Damien, would you like to tell us who you are and who you work for? Yeah. I'm Damien Murphy, applied engineer here at Deepgram. So, you know, working with customers, building, you know, real time low latency voice spots and transcribing their audio. Yeah. Excellent. And I am very, very, very fond of Deepgram, so I'm really excited and thankful that you're joining us for the next little bit. My name's Kevin. I work on the director's core team, and in this workshop or rather, this workshop is part of, Leap Week. Hopefully, you are already aware, but Leap Week is our week of announcements where we announce new features and also run a series of other events to celebrate directors and our community. We're starting to near the end of the week now, but don't worry. There's still lots more to come. Tomorrow, we are doing a community networking social. And right now, right here, we're gonna be building some cool stuff with directors and Deepgram. Maybe if we take a moment to talk about the project, that'd be a cool way to to stop. So podcasts. I love podcasts. Podcasts are actually all standards. Podcasts are just an RSS feed that contains some metadata and links to episodes. And in this workshop, we're going to string together using Director's automate and flows, our kind of visual automation, tool, a, you know, semi complex automation where we are going to go grab a RSS feed of a podcast, go grab the latest episode, send it off to Deepgram's transcription service. So maybe before we I jump straight into the whole project, maybe we break down each part. Could you tell us a little bit about, Deepgram's transcription service? Yeah. So we're able to process, you know, audio, video, pretty much any format, and turn that into, text. Right? So we'll basically transcribe every bit of speech that's spoken and then give you back a word level and time stamp level, you know, what was spoken. We also have multiple other APIs, which we'll get into a little bit later. But, I mean, we can we can rock on now. So we're going to go and transcribe these podcasts. I listened to one the other day that was like an hour long. Then we're gonna use this audio intelligence. Tell us about this one. Yeah. So we have the ability to pass the transcript once it's transcribed through our audio intelligence features. So this can do things like sentiment analysis, summarization, intent detection, and topic detection. And this can be really useful for, you know, pulling out that valuable metadata, and it's all time stamped as well. So you can even, you know, build an overview of the podcast, using those, audio intelligence features. Cool. And then you can also understand it on, like, a segment basis as well. Right? Yeah. Yeah. So each part of the the audio that comes through will pick up topics as they happen. So we can do major topics and, minor topics as well. Awesome. Oh, that's really interesting. Justine, a question here in chat. And, yes, please do use the chat. I will answer the question while encouraging you to use the chat. Will this demo be available on demand? Yes. Like everything at Leap Week, it is all recorded. It will be available on DIRECTUS TV tomorrow. In fact, the workshop from yesterday with Twilio is already up in our brand new show called Enter the Workshop as you will be able to watch this on demand, of course. So, but by being here live, you have access to the chat, so take advantage of it. I'll be monitoring it. You can ask either of us questions about Directus or Deepgram or what we're doing, and we'll be more than happy to answer in that. So we're gonna transcribe a latest podcast episode. We are going to use the audio intelligence, features that, Deepgram offers. I'm gonna struggle because Directus in Deepgram both start with these. So sometimes I might do this. I feel myself maybe doing it already. And then finally, we will use, one of the newer Deepgram products, Text to Speech. Tell us about this one. Yeah. So we recently released, our text to speech. It's one of the the lowest latency text to speeches on the market, with high quality voices. So you can get a very low latency text to speech generated at a very low price point as well. Just to help me understand, because latency only, I suppose, matters well, it doesn't only matter, but it matters more when you're doing live, like, real time stuff. So you can use this real time as well? Yeah. Absolutely. And that's where we see a lot of the demand in the market is for, you know, building real time voice box with sub second latency. So with this text to speech, you can get about 250 milliseconds of of, latency for time to first byte. Excellent. We won't be using it real time today because obviously podcast episodes are already static hosted files, but that's, I suppose, where the latency matters. So you can do, like, true conversational voice bots, I suppose. Cool. So we're gonna do all of that. Just to summarize how this is going to work, we are going to first build a flow that will take in a podcast URL. We will grab the latest podcast episode from that podcast feed. We will send that off to Deepgram to receive a transcript, then we're gonna send it off for, text intelligence, so text to text API that Deepgram offers. We'll talk a little bit about why they're separate when they don't have to be. You can do those 2 steps together, but it will become clear as we go through the workshop. Then armed with a summary of that podcast, we are going to send it back off to Deepgram to generate a short summary, I suppose, in the audio bite, which we will then save back to the director's project so you can go and listen to it at your leisure. Any questions in the chat? Any thoughts, Damian, before we kick off? Yeah. If anybody wants to sign up for Deepgram, we give $200 in free credits as well. So, you'll be able to transcribe about 750 hours of of audio for for free, essentially. Yeah. It's really, really cool. Really nice way to get started. And indeed, that is what we will be doing today. Okay. I think that means we are ready to kind of, jump in and get started. And the very first thing we are going to do here is we are gonna set up a directors project running locally now. I will give you a very quick summary of what Directus is in case you're coming from the Deepgram world and you've not heard of Directus before. So Directus is a really cool back end that you can use as a developer to build your applications. You connect it to a database. We provide developer tooling and this really beautiful web application which you can use to interact with that data. And it's, suitable for handing to non developers as well, which is not very typical of back end, back end tooling. So we're gonna spin this up, and then we are specifically going to use Directus Automate, which is part of this application in order to build this kind of multistep flow, something that looks a bit like this, except each one will take on one of the steps we described in our project. This project will use some, extensions that we built and published to the marketplace, which is available in all directors projects. We can go and do that together. And then that very final step where we create a new audio file and save it back to our directors project, we're gonna build that extension together because it doesn't currently exist. So that's that's the kind of rundown of how this is going to shake out. So with that in mind, I have this empty directory here on my local machine. It's just this empty directory called live. Let's move into it here, and we're gonna spin up a director's project. The first thing we're gonna do is create a docker compose dot yml file. And I do happen to have one here. This is the docker compose file for spinning up directives locally with a SQLite database. There isn't too much to talk about here. We will use the latest version of directives that has been published on Docker Hub. We have 3 volumes. So these are, directories that exist inside of the Docker container that we are going to map to local directories. And you'll see exactly what these do in about a minute. We need some environment variables, a key and a secret. You should replace both with random values. For the sake of this workshop, I think replace with random value is random enough, so we'll leave that be. The initial admin email and password, which, of course, you can go change. The database client and being SQLite is just a file, So we're just telling it where that file will live. We have WebSockets enabled so you could do, like, real time subscriptions. It's part of my kind of default snippet that I have. We're not gonna use that today. And then we're also turning on extensions auto reload, which is gonna be really important for the developer experience of building our extension at the very end of this work shop. So with all of that done, you can just run docker compose up. No. Oh, did I hit save? I did not. There we go. And so it's now gonna go ahead and, spin that up. And you'll notice immediately an upload and extensions and a database folder. So they are the 3 volumes that are inside of the Docker container, but also mapped to a local volume. It did a whole bunch of, like, first time, you know, seeding, and then we have directives running right now on local host 8055 with my admin, email, and password that we set in the Docker Compose. That's it. That's how I was having set up directives. This is the full fat version of directives running here. It's the same version we host in Directus Cloud, and we can with that jump straight in. Damian, I might just give you a quick tour of it if that makes sense. We have a database. Yeah. We have a database. It's that SQL like database. In here, we can create tables in that database and we can query them. We can interact with the data. Great. We also have users that we can create. We have a whole auth service. So immediately, we have this admin user that you can invite other users. Users in turn can have various permissions, which grant them access to do different actions on collection. So create, read, update, delete, and share. We also connect to your asset storage, or you can save files locally as well. So this will connect to an s 3 bucket and, an Azure storage, Backblaze, and various others. We are gonna use this later to actually save the summary back, from Deepgram. By default, if you don't say anything, it will be just local file storage, and it'll actually just get dumped here in this in this uploads folder right here in the sidebar. We have a little insights dashboard builder. We used that in yesterday's workshop. And then over here in settings, we have access to flows, which is the automation builder, which is what we're gonna use today. I think the only other thing we wanna do before we kick off is let me just have a quick think here. The only other thing we wanna do is our public role. So this is this represents, all of the requests that are made that have no permissions that have not authenticated. And I'm just gonna give it the ability to read and write files. In the real, you shouldn't do this. But for the sake of this, it'll be fine. What's the worst that could happen? So this will allow us to read and write files without needing to authenticate with with directors. What else is needed? We need we need the extensions from the marketplace. So there are 3 extensions we need here. If I type in Deepgram, I built a few. I don't I don't like the spinning wheel. There there it is, Deepgram. So we have the AI transcription operation, and we have the AI text intelligence operation. We believe in making things nice and small and modular, so we have separated them, and each one's very simple. In reality actually, question. I think in reality, you could do the the intelligence at the same time as the transcription. Right? Yeah. Yeah. You can send a single request, and you'll basically just enable those parameters, and you'll get both back. Great. As these are don't know what's going on there. Although, I've had issues with my Internet all day, so I'm gonna go out. I'd rather this was a bit slow than you not being able to hear or see what's going on. So we did the AI transcription, and then we had the AI text intelligence. So we'll just install both of those. So these were released, last month as part of our directors AI bundle of, of operations for our automation builder. And then there's one more that I created just to make our life a bit easier today, and it's this extension here, RSS to JSON. It will allow you to go off and get an RSS feed, and it will return it will, pass it and turn it into a JSON object. And this will be really helpful because we obviously need to pass the RSS feed of a podcast. So we'll go ahead and install that too. There we go. We'll just give it a quick refresh as it is prompting us to do, and we're ready to rock on. So we're gonna create a new flow. Podcast summarizer summarizer summarizer. Sure. I don't think summarizer is a word, so I don't know why I am. So hooking up on it. And we can trigger this automation in 1 of 5 ways. We can do an event hook. So an event hook can be triggered whenever or will be triggered whenever something happens in your database. So it could be, a new item is created in the posts collection or a new user is registered or a new file is uploaded. We have, webhooks, which takes an inbound HTTP request, so you can receive data from third party services. In the world of Deepgram, how we actually use it here on DIRECTOR's TV, our on demand shows all have transcripts. Some of our shows are very long, so we use Deepgram's, asynchronous callback mode. So it goes up and does work and then pings you once it's done. And so that would be a webhook trigger. You can run them based on time, you know, schedules. You can have flows trigger other flows. So if you have complex, you know, use cases, you can kind of bounce portions off into their own modularized automations and then return the data back up. And finally, manual. And this will add a button, this will add a button to the side of the data studio when you're in collections or item pages, and you can go and trigger it from there. We're gonna use a webhook because I just want the ability to call it really quickly and just making a quick call request is probably gonna be the easiest way to do it. I don't care about any of this because it really is just a quick trigger. So if I hit this URL in fact, let's do that. I open a new, let me just build these 2 terminals. If I open a new terminal and just call this URL and refresh here, we'll see it's been triggered once in the logs. So I think that's gonna be the quickest way of just constantly running it as we go to to test it. Okay. Any questions so far? Anyone in the chat? I I raised through this. I got us to this point super, super quick. We We scheduled an hour and a half in for this, and I think it won't take long at all. So unless questions are asked. So, feel feel free. Not that you have to, although you need questions. A question for you though, Damien. With Deepgram's callback mode, can you give some use cases for when that's useful? Because it's a really good, you know, demonstration, I think, of the fact that you can do you can trigger flows based on webhooks. Yeah. A lot of customers use it, because it allows their server to, you know, get back to doing other tasks. Right? So rather than waiting for the response, the more features you enable, the, you know, the longer the request will take. So, you know, adding summarization and topic detection, entity detection, you know, it it can go up into, you know, the 30, 42nd range, and as the audio gets longer as well. But yeah. Like, by default, if you've if you're just transcribing, you know, you can transcribe an hour long podcast in probably, you know, 10 seconds. Right? So, one of the other cool features is you can pass a URL to, like, an s three Booker. So you can tell us, hey, you know, when you're transcribing it, instead of me sending you the file, go pull it from an s 3 Booker. And you can even tell us to put it back into an s 3 bucket as well, which is pretty cool. Yeah. We have actually, over in our docs, I've written a post before a deep gram post right here. Right. But that makes sense. It's to stop you having, like, hanging long connections open. Right. And that that makes total sense. So this, what this does, is it listens for any file upload. It verifies that it's an audio file, and then it will send the URL of your of your file directly to to Deepgram authenticated with your token. It has a transcript returned, and then you can save that straight back to the file. So it's placed right next to the file, which is really cool. It's a really straightforward automation here. And this also featured on, let me find it. This also featured on our quick connect series right here. So it's that same project but over in video form. So if you're interested in kinda learning more about what's quite a common automation, I think, with Deepgram, you can see how to set that up. Okay. First thing we need to do then is we need to go ahead and get a podcast, like, actually go get, an RSS feed. I have loads of podcasts. I actually agonized over which to pick. So I picked Darknet Diaries. You heard of Darknet Diaries? No. Haven't heard of it. Fantastic podcast all about cybersecurity. Really, really, really good. I just listened to just listened to this latest episode here, Anom, like, 2 days ago, came out June 4th. It was it was so good. It was not what I expected. But a 146 episodes of Darknet Diaries and any I'm gonna say true podcast because I think Spotify has started to screw with the definition of a podcast is just an RSS feed, and they all follow exactly the same format. If it's not if it doesn't have an open RSS feed, it isn't actually a podcast. It's an appropriation of the term podcast. But the podcast is this kind of XML document, this RSS feed, and they all have, you know, some metadata that, you know, will be shown in your podcast acts. And then they have a number of items here. So this item here that I'm highlighting is a single episode. It's that one we just saw, Anum. And you'll notice here in the enclosure, there is this attribute called URL, and that contains a direct m p 3 link. And that's how podcasts will work. And that's really handy because with, Deepgram, you can send a a binary file or you can send a URL. And podcasts have this URL just hanging out there. So our job is get the URL. I can take this whole feed URL and use our brand new, I built it yesterday, RSS to JSON, RSS to JSON operation here, and I'm gonna call it feed. The fact I call it feed will become clear in their own. Why does this key matter when it has a name? Why does this key matter? We'll talk about that in just a moment. We'll stick the URL in there, Save it. Hit it again. And I think we configured this flow to actually return the data from the last step. So we are expecting to basically see it here. Yeah. There it is. The whole RSS feed, but turned to JSON. If we refresh here, we can also see it in our logs. There it is. So there it is. That's pretty cool. There's our item. Where is it? Here we are. There's our item array, and there is the MP 3. Now it does actually say in the docs of this, extension that I built yes yesterday. If ever there's an attribute, you'll note that you may remember it was an enclosure. I can show you. It was an enclosure tag with an attribute of URL. And somehow I had to map that to a JSON object. So the chosen method was to make it an object and the attributes are just underscored. I think that's valid. So now we wanna dig in and actually get that data. We wanna get that URL. So we will create a new we will create a new, step here. And this one, I will call, latest, I guess. Latest because we just wanna get the latest episode. This has all the episodes. And we're gonna just run some JavaScript in here. Now the this, kind of, boilerplate here, is it is the zoom level okay? Yeah. It looks okay. Yeah. Cool. Have this data property. And data is a big object and properties in that object include the keys of all of the steps. So I can get the I can go and get the feed step by, you know, going data dot feed, and that's that whole object that was returned. So if you name the keys, you can more easily pick specific values from all the way up what we call the data chain, and every operation adds a new object to the data chain. So we have data dot feed here. Now I happen to know because I didn't wanna I didn't wanna sync too much time here. I know where the value of the URL is. It's in dot RSS dot channel dot items dot item That's an array and we want the first item. That's the value of the episode. Suppose we'll just store that. And now that that episode had a ton of data, how long is it? When was it published? What's the description? What's the title? What's the cover art? The m p 3, obviously, and a whole bunch of additional metadata. It was huge. It was a really, really big object, actually. The ID, the pub date, the link to the, like, web website, the description formatted, the URL, and data about the URL. Some data specifically for iTunes, the author, iTunes summary so much so much. But actually not so much. That's the end of it. I reached the end, but significant. We don't need all of it. We only need some of it. So we're gonna just stop pulling out some values. So what we'll do is we'll grab the date That feels like a viable thing to to store. We'll turn that into a JavaScript date. What was it called? Pub date. Pub date. And I know that we want it in an just in a an ISO string. So that kind of standardizes it. So I don't think it comes in an ISO string. No. It comes in whatever this archaic thing is. That's the date. We want the title that also fills the digits at episode dot title. We could grab the description. There are a few variants of this description. Taking a look. Let's take a look what's the difference. This one has HTML tags, p and 2 break tags. This one does not. So this is the one we want here. The Itunes summary. Itunes colon summary, which means we have to use this syntax to dig in there. And finally, the actual URL, of course, episode dot enclo enclosure_url because it was it was a an attribute. Okay. Looks legit. Save that. Let's run it again. Nothing. Great. That's not what we want. What happened here? To ISO string is not a function. Oh, because it said to ISO sting. That's a typo. Ring. There it is. The date, the description, the title, the URL. Cool. Yeah. It's a pretty nice little automation builder here. Now we have the URL. I mean, strictly speaking, we didn't need that step. Right? We could just crack on, but I like just reducing down that complex data structure into something quite known. So that we called this latest. We'll need that in this next step, which is actually gonna be the AI transcription operation that, that we built and released. So there are some options here. The first thing we need is a Deepgram API key, which you can get from your Deepgram dashboard. We'll do that together in a moment. You need a full file URL, which we have. It's the it's the m p 3. You can provide a callback URL optionally and then sort of flip over into callback mode, which again stops long hanging, you know, connections, but this will be fine for this. We allow you to enable diarization, which do you know why it's called diarization? This isn't leaving. I don't know the answer. Yeah. It could be called speaker identification as well, but, yeah, I think the research term first is a diarization. So it basically tells you who's speaking when you have a mono channel, and multiple speakers. If you have multichannel audio, you you don't really need to diarize, because you know each speaker's on a different channel. But, yeah, a lot a lot of people have a single channel, especially with a podcast. It's it's not multichannel. Yes. And, thank you, Ramsey. I'm glad I caught it really quickly, but, yes, there was a missing r in ISO string. So you can optionally enable diarization, and then you can also add keywords. Talk to us about keywords. Someone who works for a word that sounds like directors, I'm very, very intimately familiar with this. Yeah. So so keywords allows you to kind of increase the probability that we would, you know, pick up the rectus and direct us. Right? You know, as a single word versus, like, direct us. Right? So if you put in that keyword with the spelling and then you increase the intensifier, and the intensity is actually it's a exponential scale. So as you go up higher, it gets extremely strong. Yeah, value of 1 or 2 is is pretty normal. If you were to put in a value of a 1,000, nearly every word will start turning into direct us. But that kinda gives you an idea of how you can leverage that feature. Yeah. Interestingly, it's not direct to us. It's always directors. Like, I am the director of the film. That's always like if when it's wrong, that's how it gets it wrong. We don't need to use keywords for this. So first thing we'll need is a Deepgram API key. Here is our director. Here is our Deepgram console. Signed up for an account. And you can go make a new API key. You can give it a nice name here so we can call this leap week work shop, workshop. You can optionally set an expiration. I will do that. I will expire this after 1 hour. Right? Because I don't we won't be going for more than an hour, and then this key will just stop working. You can also, add some tags, but this is the thing that's interesting. You can change the permissions of the key, which is nice. Yeah. Do you have any notes about this or just yeah. You can do that. Yeah. Like, if if you have certain needs, right, sometimes you might wanna generate keys, like, more API keys with an API key. Build an admin. Like, if you're if you're creating this as a service, for example, you're using Deepgram in, like, yeah. Cool. That makes sense. You get an API key, which probably shouldn't share, but mine is in an hour and a half, and it has a fixed amount of credit and no credit card. So the US, we'll pop the Deepgram API key in there. Next thing we want is the file URL. You can add dynamic values using mustache impacts, double squigglies on each side. The last step was called latest and the value was URL. So that will resolve to the full URL that was inside of that enclosure. And I think we'll leave everything else. I think that's that's the shortest version. I'll call this transcription transcription. Sure. Hit save. No. Let's try it out. So now it's taking a little bit longer because it's not just making one HTTP request. We are waiting for for it to happen. Now by default, I happen to know because I built this extension. We do turn on a couple of features. So I'll wait for this to finish and then we'll talk about those features. Maybe taking a hot minute there. Has it? Oh, oh, there we are. There we are. Boom. Look at that. Huge. Right. Before we look at the data structure that comes back, I will tell you that we are using smart format and we are using the Nova 2 model. So maybe let's talk briefly about each. Should we start with the model? Mhmm. Yeah. So so the base model is our oldest model. So that was, you know, from kinda 2020 18, 2019 era. It's an extremely performant model, but the accuracy is is a lot lower. Some customers still opt to use it because it is just so compute efficient. And then we have our enhanced model, which, you know, added a bit more compute to it. But, yeah, our nova 2 model is the most accurate model that we have, and it's, yeah, available now in 36 languages, and we're we're adding more languages every month. Nice. And that is what we're using here in this, operation. And then what smart format do? I think smart format basically checks a bunch of other boxes for us. Yeah. So smart formatting, is actually baked into the model. So the model itself when it's transcribing is is generating the formatting. If you turn that off and you enable, like, punctuation and and numerals and things like that, that will apply post processing formatting, and which tends to lose a little bit of the, the context. Because, you know, some like, the number one isn't always meant to be a number. Right? Like, if if I you know, I am the the one and only. You don't want the digit to come in there. Right? So that that's essentially what that's there for. Fascinating. So we applied smart formats. So we make that we make that available. So you don't have the option to turn those off or change them. That's just what you get with this, with this extension. Okay. Let's look at what came back there. Big old payload. Now, just because this is a slightly I've gone into the big data structure that Deepgram returns, which, Damian, you've probably spotted that immediately. This is the first alternative is always returned. So I can just speed speed up our I can speed run us here. So the first thing is this transcript, which yeah? Like you said, it's nice. It's formatted. Interest I didn't know it was baked into the model and that it's not post processing and that's the difference. I thought it was just a shortcut to checking a few other boxes, but it isn't. It actually does something different. Yeah. And some customers will want digits but not punctuation or punctuation and not digits. So having them split out as well allows them to pick and choose between the the features. Right. So we have reached the point where this is to Deepgram directs us. This is too big for me to just scroll through and and talk about. So what I'll do is I'll just look at the docs for this specific extension, and we can talk about about it. So this was the AI transcription operation. This is the data structure that's returned if it was a really short transcript. So we have the transcript. We saw that. We didn't actually manage to scroll to the end of it. Can you talk to us about the other objects that are all the other, properties that are returned? Yeah. So the words array is gonna give you the start and end times of each of the words, also the confidence that we have for that word. Like, if you detect a very low confidence word, you know, some people will actually choose to omit it. Right? It could have just been, you know, picked up from a cough or something like that. And, yeah, if it if it's down at, like, 5%, it's usually, probably gonna be wrong. Right? But for the most part, you'll see confidences in the high nineties. We also have the punctuated words, so, you know, you you'll get the word as it was, you know, printed out, without any punctuation or formatting. What about that? And then what what we apply to They're not the same words. Like, it's a typo. Oh, it's a typo in my readme. It's a typo in my readme. Ignore me. I'll go and fix that another time. Yeah. You would have seen lowercase h I and no no full stop in that case. Yeah. And then there's also paragraphs, which is which is also interesting. Mhmm. Yeah. So we can we can split it up, by paragraphs. If you enable diarization, we'll also split it up by, you know, who who said what as they said. Cool. And you can do utterances as well. So that will give you kind of logical semantic breaks in in speech as well. Yeah. And if you were to enable diarization in each word, you would get a speaker ID as well. So you would have, like, speaker 0, speaker 1, and, you know, whoever spoke the word. Yeah. Lovely. And we see here the transcript is there. It's formatted, but it adds these line breaks in. So, you know, you can kind of print that. We get paragraphs. We get sentences. We get start ends for all of them. So It's really nice and flexible. Yeah. I see sentences could work quite nicely for, putting captions on a screen, like a sentence at a time or something like that. Okay. So that's the data that comes back from that. I think for this, all we really care about is this top level transcript, But the rest of it does exist. Now just a reminder, you can do audio intelligence within that single request if you're using the Deepgram API or SDK, but we've chosen to split them into 2 distinct operations so you can just have what you need, and each one can be a little more simple rather than being a kitchen sink of options. So let's crack on then. Let's go ahead and add the text intelligence right here. So I'll call this analyze, I think. Once again, Deepgram API key. I think I, I can't see it again. Oh, wait. The it's in the it's in the last one. We'll do that. We'll grab it out of here. There it is. There's the raw value, which again will expire by the time this is over. Right. AI text intelligence and lies. Deepgram API key and the text is going to be transcription was the name of that was the key of the last step dot transcript. Now this is a point to and it will be the last operation. I hate this. As a as a educators, I like, you know, lead educator. I think this lets you foot gum. If you start rewiring your operations, this value is not always the same. That's why I'm personally a big fan of explicit naming of keys and explicit inclusion of keys. But key sorry. Last always exists. Another one that exists is the trigger, which would give you data from that, from that very first step. So it's just a couple of conveniences there. But we will make this try and go ahead. Is there any way to see all the available, step values or objects or explore them? Yes. There's a number of ways that debugging flows is an area we know needs improvement. I'm gonna just save this. You can take a look over here. Right? And you can look through the logs and you can go, right, well, this was for each step, but and it was called latest, but you don't have the key immediately available. You can simply just log them. We have a logging step, which will add an extra operation. You can also just return it in the last step and it will return here. Or rather, I think when you configure the trigger, let's take a look, you can get all data back and that will return the entire object. So you have options, but no, there isn't a really nice way to do this right now. It might be a cool addition. I know when I when I use, like, email, syntax injection, there's, like, a little list that lets me pick from them. Yeah. Yeah. No. That that makes no sense. And, this was actually the topic of back to Directus TV we go. Of one of our recent request reviews. What was it? It was the improvements to flows debugging. So we spoke about it for a whole hour, with our community around what they'd like to see based on an open feature request. So maybe that's something we'll see in the not so not too distant future. Alright. Let's try this. Now we are expecting to wait a moment for this because it's going to transcribe the whole hour, then it's gonna run the text intelligence. So I'll kick it off, but I am expecting to to wait. And then are are flows always triggered from an API request, or is there a way to There was there was 5 different triggers. So now we're a little bit deeper. I'll do this again because I think you're building more context around this. So the first is an event hook. So you can say, hey. Whenever an item is created in this specific collection or these collections, trigger the flow. So you can do event based hooks. You can either do it before the database transaction occurs so you can validate, manipulate the inbound data before it gets committed or perhaps stop it in its tracks, right, and fail out if something that isn't correct, or you can do it after the data has been committed. So that's the event hook. We have the web hook, which we are using for this just for speedy rerunning. We can run it on a schedule, so you can provide the 6 point cron syntax here and run it up to every minute. You can trigger it based on another flow. So one of the, operations in the list was to run another flow. You can put data in, and it will return data out so you can modularize your your flows a little. And then finally, manual. And I think the easiest way to look at manual is probably just a quick trip to the docs. The manual flow trigger, you pick a set of collections and it adds this button over here to the sidebar. So this sidebar, it requires you to check 1 or more you 1 or more items and hits and hit the button, or you can do it from within an individual record, an individual item. And it will send the IDs of those items into the flow as part of the trigger. That did take a little while there. You can additionally add this confirmation dialogue and collect per invocation values. So this could be useful for things like sending an email. Right? So you type in a message, you hit go, you've maybe picked some users or send a text message with Twilio, press a button, and off it goes. So they're the 5 ways to trigger, to trigger your flow. But we're just using the webhook so I can just run it just by hitting up and enter here. Mhmm. Let's, see what that big object look like. We have the summary, which is nice. It's a nice length for an hour's worth of footage. Can you talk to us about the rest? That's the summary, but we've got more. Yeah. So the topics, it's it's got a a lot of predetermined topics that the model's capable of picking up. You also have the option to pass custom topics. So if you have a topic that's kind of nuanced, very unusual, you can add that in as well. And that's gonna figure out, like, okay. Whereabouts in, you know, this transcript, right, based on, the text in in this case. And whereabouts was it talking about WikiLeaks or fake off or scammer or spyware? And and that's really useful because now you have the ability to actually jump to that position. Right? So you could imagine if you wanted to find, you know, the area that was talking about, WikiLeaks, you could just click a button, and it would jump you to that segment in in the actual transcript. Yeah. Exactly. You could build out a search, You're not searching just the raw transcripts. You're you're searching for topics because that's more realistic to people's usage. That's really cool. This again is quite long. So let's find our way to the to the example I I have written up here. So, yes, we get the topics on a per segment basis. You get the intents. Let's talk about intents. And can we talk a bit about how intents are different to topics? Because I'm a little fuzzy on it. Yeah. So so topics can can be all sorts of things. You're probably gonna have, you know, say, 10 x topics versus intents. An intent is really like if if I'm making a phone call and I want to cancel my plan, you know, or update my address, right, that might be one thing. But I may go off on a tangent and start talking about my holiday in Spain and do you know what I mean? And that that could be a topic, but the intent of the call really was to, you know, achieve something. And the same can be said about, you know, a video, a podcast. And so, yeah, I'm I'm interested about the intents that actually brought back for that podcast. Let's take a look. Might have to yeah. I might have to do the mother of all scrolls here. You could try a control f me. Yeah. I could. Yeah. You think I'd know that using a computer every day. What do we call it? Intent. Thank you for that. Wow. Okay. Yes. So Explore Samsung Smart TV features. That's funny. Though they were talking about because I just listened to this the other day. Basically, Samsung TVs have this feature built in where you could put it in, like, low low power mode where, like, it looks off, but it's not. And so the yeah. If you push mute 182 and then power the TV appears to be off, but it isn't. And then if you basically run spyware on it and then put it in that mode, no one knows. So instead of needing to plant bugs, you could actually just use the Samsung Smart TV, which will record to the TV, and then you just go by and retrieve it later. And you can see how useful this intent is. Right? Like, straight away, it got us to something, you know, very interesting. Right? Yeah. Yeah. So there there's definitely gonna be intense. Yeah. Yeah. That's where discuss Anum's features. That is what happened. Interesting. Okay. It might make a little less sense here, but in a call center context in particular Yeah. So with sentiment, and and it's pretty cool. I don't know if you're Yeah. So with sentiment, and and it's pretty cool. I don't I don't know if you have the playground up. There's a good visualization of the sentiment in there. Yes. Yeah. So if you scroll up and then you see just at the very top Per month. Okay. Next to summary. Yeah. Sentiment. So so you can track the sentiment over time. Right? Because we're giving you, like, you know, sentiment, you know, at each sentence or utterance. And if you scroll down, you can see as the sentiment changes, you know, it goes to see negative negative negative. So that kinda gives you an idea of, you know, what's happening throughout, you know, the show or or the phone call. Got it. Got it. But you might only need to know the average. So I think if memory serves me right, there is also I think it is literally called average. Mhmm. Yeah. An average sentiment as well. Yeah. And and the average is gonna tend towards neutral. Right? Because, you know, the vast majority of of text is is kind of neutral. Right? It's only it's only gonna be parts of the call go negative. So, like, if if you see there's I don't know if you can search for positive, see how many results you get. And so I'll say 10 and then negative. 69. Yeah. And then neutral. 81. Okay. So so so it looks like it it was kind of 5050 on the on the neutral and negative. Yeah. Just enough to bring it kind of back to that new Yeah. Yeah. Yeah. Well, it's just gonna average the sentiment scores, which are between minus 1 and 1, I'm guessing, given that this is minus 0.0.15. Mhmm. Okay. So we now have a summary, and now it's time to go ahead. And, and that summary was held in the output of that. I think it's called summary dot text. Summary was an object. And now it's time to use, the text to speech, APIs. And to do that, we are going to build an extension, which I'm really excited about. Now for those watching, this isn't intended to be a play along, so I'm gonna go a little bit faster than I would running a hands on workshop because this is gonna be available tomorrow on director's TV. I'm also gonna turn this into a blog post sometime in the next couple of weeks so you can follow step by step in written form if that's more your thing. So, we're gonna go into our extensions folder here and MPX create direct us extent extend. This always gives me extension Extension. Direct. Yeah. Sure. Let wait for the latest version, please. And here are all of the extension types. You can create custom panels for direct us insights, the dashboard builder, custom interfaces, which are form inputs for the editor, but we are going to create an operation for flows. And I will call this Deepgram TTS, text to speech. I'll just write it in JavaScript and auto install dependencies. And given the speed of other things that have happened on my system during this session, I think we'll just be waiting a hot minute for that. But what we're gonna do here is we're gonna set up this operation, and we are going to use Deepgram's JavaScript SDK, which makes interacting with Aura, the text to speech service, Just a lot just a lot easier. So while that's scaffolding, let's, oh, it did it. It did it. So I next time next time we wait, we'll take a look at Aura. So we're gonna jump we're gonna jump in here. Right. Let's take a look inside of our new Deepgram TTS extension at the code. There are 2 files that matter. The first is app dotjs, and this describes all of the configuration. So this, says what is shown on the card here on this kind of overview and what options are presented here and then fed into the into the back end. So the API key and the text and stuff like that. The, App. Js, yeah, will also do things like what icon is shown here, what text and description, stuff like that. And then there is the API JS which runs server side and actually executes executes the, you know, the the operation, then it will be here where we install and use the SDK. So let's that you can that you can build the UI through that code and do all back end process. Lot of other ideas kinda come into mind now that I see it. Yeah. And the on a lot with breakfast in multiple ways. You know? Yesterday, we built out actually, not for those watching on demand. Sorry for kind of crossing streams. So you probably, you you may have already seen this. If we take a look at Directus and just take a very quick look at yesterday's workshop, another thing you may not consider is I'm just mute that. You may not have considered is, you know, we have this dashboard builder, and you'll be thinking, oh, okay. You know, it's all out displaying displaying insights. You know? That could be useful or whatever. But what? Look at that, look at that quality there. I'll click back over here in a minute. Maybe just here. Here. But this panel, you pick a user from a drop down and hit call, and it would use the Twilio voice SDK to actually do a two way phone call from your browser to the target to the user's phone number. So, yeah, really, really flexible. You can very much build a lot in it. Anyway, right. So we're gonna create a custom operation. So first thing we're gonna do here is we are going to change the ID. The ID has to be unique across all operations in your project. So it's quite typical that, you know, people will prefix the name of their extension with their author name. I'm just gonna call this one Deepgram TTS because I doubt there will be another one called that. And that has to be the same in both files. So also here, the ID. We'll call this 1 Deepgram TTS. What are we gonna do for icon? We will use record voice over. I think it's what the one that I've used in the past for, Deepgram. And then for description, generate text to speech. Well, we don't need to save too much time insights. Just a a little visual thing. Now what we need to do is we're gonna pop in some text. So actually, I think we'll just leave that as it is, but we are also going to pick the model. So let's actually take this moment to pause. Could you talk to us about the models in Aura? Yeah. In the playground that you you should be able to access, and we we literally just added it the other day. So on the top at the right hand side the very top right hand. Oh, text to speech. I didn't see that then. Oh, perfect. Yes. Yeah. So you can type in any text you want here, and it will generate it. Yeah. And you can just hit play on one of the voices. Angus at the very top, actually, is my voice. So, yeah, if you ever wanna Actually, I listened to it yesterday. That's so funny. So we have 2 here. You know what? Let's just for sake of argument, we'll just pick the top 2, Angus and Arcus. But they each have this model name, Aura Angus e n, and AURA ARCUS en. So we're going to provide a way to do a drop down and just pick between them. And in theory, you would populate as many as you wish, or you would take away the choice and just pick 1 and not provide this option. But we can do that. So we have this text box in the option. Let's, go and create a new one. So we will do field. This is what you name it. So I'll name it model. Right? That's like the key that we saw. We're gonna give it a visual name. So we'll capitalize that. That is ultimately just going to store a string. And then we get to provide some information in here. First thing we'll do is just the the width will make it full, which just means I'll go under it. So you can make them half, but whatever. But the more important thing here is the interface or the form input where you can create custom interfaces as we spoke about earlier. And the one we want is called select drop down like so. This interface has some options. As you would probably expect, it's the choices. And each choice has a text, and that has a value. And, like I said, we will do 2. So the text for the first one is Angus, and we can see here, Aura Angus e n. Is that what it is? Yeah. Aura Angus e n, and Arcus was the second one. Aura, Arcus, e n. Nice. Now the only other thing we'll do here is we'll just show it on that card. This is optional. This is just this is just, you know, UI further to a to a degree, but we will and sorry. It's over here in the in the overview. So we'll also bring in the model, and we will show that on the card as well. You'll see what that does in just a moment. And what would be the default if that wasn't populated? Or is it just always It would just be an empty card. It would just be an Oh, it would be an empty card. Just like this. If the if the model wasn't selected. Good question. I think it might default to the first. Did I? You could probably set a default or handle the default over on the server side. If not selected, pick this. I think kinda similar approach to most drop downs that you could build. So let's let's run this. Let's go npm run dev. And that's going to build our extension, watch for changes, and rebuild it whenever there's a change. Over in our first terminal here, we see here extensions reloaded. If I hit save, it will rebuild the extension. Directors will see that I've rebuilt the that I've that the that the extension has been rebuilt. Will it? You might need to make a change rather than Yeah. That's what I was that's what I was waiting on. Interesting. I might just quickly restart it and see if it needed just a one time restart. And if that continues, then whatever will might just have to kick it up the bum. So that's rerunning now. So I'll just save that. There you go. Extensions reloaded. Okay. Just needed one quick one quick kick up the bum. So let's, let's see what happens now. So we will add to this new extension on the end. There it is. Deepgram TTS. There's the icon we pick. That's the text. That's the title. We pass in the text, which we know is annual annualize dot summary Mhmm. Dot x, I think. So analyze dot summary.text. Sure. And we pick the model, and there they are. So we'll pick your voice. That's quite funny. I didn't know. I didn't know that. That's pretty cool. And we'll hit save. So and you see there the model is shown on the front, and that's the text input that we put in. So we hit save. When this, operation runs, the API side will run. So the first thing we'll do is we will go ahead and, so go ahead and just pull the model in as well. So that'll just be the the e n, you know, Angus or aura Angus e n. Let's let's do this. I'm really excited, actually. Right. We are gonna use the Deepgram SDK. So npm install at deepgram\u002Fsdk. Good. Good. Good. And we'll go ahead and, initialize this. It's funny when I was a developer advocate at Deepgram, I did this all the time. So import, create, client from not that, from Deepgram SDK, and then you create an instance, Deepgram equals create client, and the API key have to go in here. Yeah. Obviously, I Eric, why did I even bother hitting save? We need to pass in the API key here. We We don't really wanna hard code it in our extension. So instead, what we're gonna do is we're gonna add it here to our Docker compose file, which will bring it into the environment variables. So mostly because I've already forgotten it, let's grab that key again. Let's pop it in here. We'll call it deep oh, Deepgram API key. There it is. We do need to restart our Docker container whenever we update the Docker Compose file because it just reads that once it load. And straightaway in here, process dotm.deepgramapi key. One moment. These are fine. These are not errors. These are little warnings, not a problem. It's just, some of some of the way that the, yeah, some of the the build of the Deepgram SDK, but it's not a bother at all. They are just warnings. And now it's time to actually build the hands, build out the the handler. So, what happens? We press the button, it goes in. Now what we're gonna want to do here is ultimately we want to save a file to our director's project. And we expose a bunch of services to your project, which you can use to interact directly with these kind of directors primitives. Now, the first thing we wanna do here is we wanna go ahead and just add in here a second variable called context. And inside of here, con, const. There we go. Const. We want services and we also want get schema, which we'll need to, initialize the service. Services is a list of all the services. The user service, the item service, the permission service, the role service. We only care about the files service. So we'll just pull that out just just to make it easier. And then we'll go ahead and we will initialize a files service. A new file service. And in here, you have to pass in the schema of your project, and that thankfully is just returned in get schema. And I did just catch in the little tool tip there that that needs to be, awaited, and therefore, this needs to be async. Not there. That's an object. So that's us creating the file service. That means we now have an interface with which we can create a file in just a moment. Next, we're gonna go ahead and use the Deepgram SDK to generate a a stream of audio, and this was lovely by the way. I was speaking to to Luke, one of the DX engineers at at Deepgram recently about this. And the fact that this SDK uses the native interface makes this next bit really, really nice. So what we wanna do here is create I'll call it response for now, I guess. Or e g response maybe. Deepgram response. What we can do here is just use the initialized client here with our environment variable dot speak dot request. First first argument here is the source, so we can just pass in the text. So you would yeah. This is how you do it, but a shorthand because the name of the of the, property and the value is the same, you can just shorthand it there. And secondly, any options that we want to use. I might just create options as its own just to keep it really clear. I might just do this above here and then feed it in. So what do we wanna do here? I think all we really wanna do is we wanna pick the model. The same thing, we can use the shorthand. So that'll be, Angus. And we're going to tell it what file format we want it to return. If memory serves me right, you can return quite a lot of audio formats from Deepgram. Right? Yeah. We we support quite a few different formats. Yeah. If you wanna play it back, typically m p 3 or WAV, if you wanna stream it, like, to Twilio and things like that, you'd probably do, like, raw audio linear 16 or Yeah. That's cool. We'll do this because in just a moment, we are gonna need to know the file format. So I want to explicitly ask it for a files format. So we know with confidence it's gonna be an MP 3. Then finally oh, hang on. Let's pass this in. DG options. There we go. And finally is the file stream. I might call it dgstream, just again to be very explicit. Now gonna await response dot get stream Stream. That's it. That is a DG response. That is now just a live stream of audio, which is fantastic. Because it is a file stream, we can pass it straight into the file service to upload it. Now before we continue, we are also going to need a file name. Right? We we need to tell direct us when we create a file, what we want the file name to be at least as when you download it. And I think what we might wanna do is actually collect that from the user upfront. Like, hey. What filename do you want this to be? So let's go through the motions here of adding a new item here and just a new text box. Right? And we'll call this one file name, I guess. Field name type. I think that's all we need. File name. I And you need to change the name. Right? Nope. Oh, yeah. Yeah. Yeah. Sure. But I mean, that's that's that will stop us getting confused, but strictly speaking, you you don't need to. So we have the file name. Great. We're not gonna bother showing it on the overview, so it can just stay here. And then over on the API side, we just wanna pull that pull that in here. File name. Yeah. Cool. And just to make this consistent, I might just move this, and we can call this, like, request or something like that. Just, again, kind of handle these the same way. So now we have the file name that we'll pass in. Let's save this. Let's, just refresh this. Make sure all of that works. And let the white screen stay for just a moment longer than I would have liked it to. Look, there's a problem here, which tells me something went wrong. But I don't know what. Look. They've all gone a bit they've all gone a bit funny, which means I have caused a problem. Love that for me. Might just zoom out one step for the sake of scanning a larger surface of code file name. I could have broken at any of those points. We haven't refreshed this in a while. Thought there. And these are, let let me scan this because they were just, these are just warnings there. Oh, no. No. No. That's still the warning. Interesting. Interesting. Let's look over here again. We passed in the the create client. Let's just save ourselves just a moment of effort and just figure out if it's in here, first of all. It was. Okay. That at least helped somewhat. Let's. Was it maybe the fact that you I've called it request? It's request meant no. That's fine. Okay. I mean, this is a top tier debugging. This. Okay. It's something down here. Okay. I mean, okay. WAVs. Yeah. So there's nothing wrong. Yeah. Let me just make sure the extension reloads. Fine. No bloody problem. Nothing happened here. Right. Let's open this one up, and now there's a file name. Right? So I suppose we can put in a dynamic value here and call it and, and call it latest, which was the latest episode dot I think we called it title. I'll save it, and then I'll just look back. Latest. Yeah. We called it title. That'll be the title. That's a dynamic value, which is nice. So that gets passed in as a file name. We get the stream, and, all we need to do now is upload it to directives. And the way we do that is let's do the same thing again. We'll call it, directives options. Just so again be really explicit. We need 3 things, I think, are mandatory here or it won't work. The first thing is the file name downloads. Whenever you download the file from director status, the file name that it will have, We've already established, so that's gonna be file name. Oh, dotmp 3, I suppose. I'm not sure if that's needed, but I'm gonna do it anyway. It needs a MIME type. We just call it type here. We already know that's an MP 3. I know the standard format for MIME types is this, audio slash. And again, that's why we specifically requested a file format. It doesn't really matter which one it was and where is it going. This matters because you can connect more than one asset storage to Directus. By default, it will use local storage, which is just this up which is just this kind of link to this uploads folder, but you can connect it to an s 3 bucket, and Azure Blob storage and and many more. So we're just telling it, hey. This is where we want storage. Should that should that meme type be MPEG rather than MP 3? No. I think it's this. I think it's I think if if I caught my mind back, I think that's right. Don't know. We'll find out in a couple of minutes. So that'll be, that'll be our first point of debugging. And then the final thing we'll do, although it is optional, is we will actually set the title of the new of the new file. So this is the, like, visual labeled title where the other one is what happens when you download it. That isn't strictly needed, but we'll we'll do that. Then what we'll do is we'll call it directors file, I guess. The new file is we will use our file service. We will upload 1, and the first value is going to be a stream. Unfortunately, Deepgram just returns a stream, so we can dump that in there. And the second one is the directors options. What is returned from upload 1 is the new primary key of the new file that's being created. So we will just return the rectus file. That's the whole thing. That's the whole operation. Before I good before I get all excited and describe everything that's happened, let's make sure it works because it it might not. Right. Nothing seems to have gone wrong here. We have a file name set, so let's trigger this one more time, and I am expecting to have to wait now because we are doing all of these steps back to back. I'm expecting to have to wait maybe 30 ish, 40 ish seconds. But we'll see. It's an hour of audio. So while that's happening, let's recap what's happening. We're going and grabbing the RSS feed and converting it to JSON with this custom extension you can download for free in the marketplace. We just, you know, traverse that that big objects that that's returned to just get the latest item. We didn't end up doing anything with the date, actually. Now I think about it or the description. We transcribe it using the AI transcription endpoint and then run text intelligence. Both of these are Deepgram to receive the summary. And then this custom built extension we just built does text to speech. You pass in some text, You pick a model from the drop down. We just pick the first two out the list, Arcus and Angus, and you give it the file name for the original file for the new file. And as you see here, what was returned was a string, which means there wasn't an error, which means there is our 42 second summary. Let me listen to your voice. I don't think this will come through. It it's you. It's so you. That's so funny. So do people do people at Deepgram just get proposition to, like, get their voices up? 6 of the 12 voices are Deepgrammers, and then the 6 are voice actors. It's really funny. But there's your little summary. There's your summary. That's sounds freaking cool. So let's talk about the code, and then we'll talk about what more you could do with this. Right? So we grab the data that was actually, you know, provided by the operation. We grab the file service and the get schema function, and we initialize a new file service that allows us to interact directly with the director's files collection. These three lines is all that's needed, and strictly speaking, you know, this object could go in here. So two lines is all that's needed to request a text to speech, a text to speech operation, I suppose, from Deepgram. That's all that's needed and it returns a stream of data. Then we, you know, configure all of our options for our new file we're about to create, and we upload the file by just providing the stream directly, providing the options, and that returns the primary key, which we return. That's the whole thing. Do you have any questions, in the chat? Who have been remarkably quiet, by the way. I hope you've enjoyed. Damien, do you have any questions, thoughts? Yeah. I'm I'm just amazed at how easy that was. Like, less than 30 times code, and it's all hooked up and it works. And a lot a lot of that code is verbose code as well. Right? Like it's Absolutely. Trying to be expanded. So, absolutely really easy. And, yeah, being able to pass that stream straight into the file was very useful. It was very, very useful indeed. So what more could you do with this? Well, the obvious kind of first step is that load more data is returned from Deepgram. So you can do more with that. You know, you why we could save the description directly to the file if we wanted. We can provide you know, we can maybe tag it with topics. We could do whatever whatever we want here. It's completely up to us. You could also run further automations either as part of the same flow or a separate flow. It's all good that, you know, a new podcast has been transcribed, but do we know? Maybe we send an alert. We send, you know, an email or a notification to the user, which if we take a look here, there is a send email operation right here. So you could tell them that there's a new summary and maybe directly link them to the director's files m p 3, Because everything in directors, if we take a look at this new file here, it has this ID, and you can just go to local host 8055\u002Fassets\u002Fthat. And there is our m p 3. So you could link them directly to it if you fancied. Another thing is that, you know, this was a slightly conceited example in that we have to manually run it. But you could run a crop. You could use a chrome here. You could use a chrome, grab the feed and say, hey. Has there been anything new in the last 24 hours since I last ran? Okay. Now go and transcribe the latest episode. You know? So you could run this on a schedule and make it like a daily roll up of new shows, new episodes that you could listen to. One other idea is, like, obviously, this was audio to begin with, so we we kinda compressed it, and we create a summary that became audio. But maybe there is, like, you know, a cool blog that you follow, but you may not have the chance to read the blog, but you'd like to listen to it. Right? You know, maybe in the car. So you could take a blog, turn it into a, like, you know, an audiobook, very easily, or you could even, you know, summarize it. Right? And and play it out. I had a pretty interesting idea of, like, a a real time radio station that's basically, you know, tailored to exactly what you like. Right? So you could have a, you know, maybe archive research papers being fed in, and then it's giving you kinda the updates in real time. Oh, pretend I didn't see that. I remember I'm not sure if it will still be live. Yeah. Here it is. This is a post I wrote. The date's wrong because I I worked here at that point. But what this did, it used my JSON. It literally literally live transcribed a radio station. I could edit in BBC Radio 4. Mhmm. And it would live transcribe it, which was super cool. Super, super cool. Yeah. So now you can even do the reverse. Yeah. Yeah. Yeah. That's pretty, pretty cool. There's so much scope for this, you know, based on more complex triggers, you know, more logic in the middle. Like I said, you know, this could be a cron instead of a trigger. So many options. But I think that is just about our time, and we have 2 more minutes. So, yeah, thank you so much for indulging me and and and and getting involved in this and sharing your insights. I learned actually quite a lot, during that. Especially the smart format being part of the model itself. Fascinating. Not not what I thought. Yeah. Thanks for having me. This this is super interesting, and I'm kind of amazed that you're able to build all this from scratch, you know, in in the length of time that we're chatting here. And, yeah, it really just shows what's possible with Directus. So I might be I might be building a few little, thing that flows with it myself. That's how we get you. That's how we get you. And you can run it locally. Right? And it's the full fat thing. You know, it's not like a a less good version. Like, it is the full thing. It's what we host. I will say that I need to add it to this, I think it's still in a PR, actually. I don't think we merged it in yet. But the RSS to JSON operation, I will show it because it is also really, like, light. I just didn't wanna have to do it now because it kinda wasn't the point. We're taking the URL as you saw, and all we do here is that all the code for this operation is here. That's it. That's the whole thing, the whole operation. We import a library called XML parser. We go off and get the RSS feed. And assuming everything was good, we just pass it, the attribute name prefix underscore, and then we returned we returned the past data. So that whole operation, that first one was the code. We could have built it live. I just didn't think it was gonna be that interesting. Thank you so much folks in the chat, for your kind words. I'm glad we made your life easier. I like lots of claps. Yes. There are lots of use cases, both for Directus and Deepgram and the 2 together, and I completely echo Jonathan's sentiment. Welcome to the director's community. We're very happy to have you. Great. And with that, we are at time. So have a wonderful rest of your week, everyone. Have a wonderful rest of your, week, Damien. And tomorrow, just a reminder that there is one more event this week week and then there's this community networking social. It is using the one and only platform I have ever done networking on that doesn't absolutely suck. So if you're interested in meeting other people who are interested or use or involved in Directus in some way, shape, or form, drop by. It's at if you go to leapweek.dev, it will be, localized to your time zone. But here in Berlin, in Central European, it is at 4 PM. So, yeah, hopefully, we'll see you at that tomorrow. Damien, anything else you wanna share just before we hit end? No. Thanks very much everybody for joining. And, yeah, really interesting possibilities. This is open all. Excellent. Right. With that, have a good rest of your day, nerds. Bye for now. Bye bye.","af6d8b32-20dd-4215-aa51-218331552265",[2511,2512],"6651b07b-b7d0-4ad7-aa06-82596fd2b3ab","1df7e6e1-df7b-4f62-b2e1-1c19f3794829",[],{"year":978,"number":979,"id":980,"episodes":2515,"show":2516},[982,983,984,963],{"title":986,"slug":987},{"id":982,"slug":2518,"vimeo_id":2519,"description":2520,"tile":2521,"length":2522,"resources":12,"people":12,"episode_number":13,"published":2523,"title":2524,"video_transcript_html":2525,"video_transcript_text":2526,"content":12,"seo":2527,"status":19,"episode_people":2528,"recommendations":2531,"season":2532},"twiliio-dialer-panel-extension","963063255","Join Kevin and Nathaniel Okenwa, Developer Evangelist at Twilio, as they utilize Twilio's Voice SDK to build a Directus extension that allows outbound phone calls directly from the browser.","7f9a2314-505c-4f60-a5eb-1da0b9c07992",109,"2024-06-20","Build a Dialer Panel Extension in Directus Insights with Twilio","\u003Cp>Speaker 0: Hello. Hello. Hello.\u003C\u002Fp>\u003Cp>Speaker 1: How are you doing?\u003C\u002Fp>\u003Cp>Speaker 0: It's good to hear you. Great, mate. How are you? Yeah. Yeah.\u003C\u002Fp>\u003Cp>Yeah. I'm doing alright.\u003C\u002Fp>\u003Cp>Speaker 1: I'm doing alright.\u003C\u002Fp>\u003Cp>Speaker 0: It's wild to me that we have been friends in the same kind of job family for so long, and I don't think we've ever run a workshop before.\u003C\u002Fp>\u003Cp>Speaker 1: I know. I mean, I think it's been a missed opportunity for all the wonderful people that we haven't been able to hang out. Genuinely, it's crazy because we went to university together, for those of you who maybe have never heard. So we we've been known each other for a long time, and our careers have, like, had these moments where they almost look like they're about to cross. And I feel like this might be one of the first professional cross that you've said.\u003C\u002Fp>\u003Cp>I'm really excited.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So I thought we'd open by just introducing ourselves, introducing what this event is, introducing what we're gonna be doing, how it's gonna work, and then we'll launch straight in and make a mess of it for 2 hours. How's that sound?\u003C\u002Fp>\u003Cp>Speaker 1: Sounds good. Sounds good. I could start by introducing\u003C\u002Fp>\u003Cp>Speaker 0: you first.\u003C\u002Fp>\u003Cp>Speaker 1: Hi, folks. My name is Nathaniel Okenwa. As Kevin would know, I talk a lot and I write code, so my friends call me Chatterbox Coder. That's where you can find me on all the socials. And I work for a company called Twilio, which is kind of what we're gonna be talking about today.\u003C\u002Fp>\u003Cp>Twilio, for those of you who don't know, it does many things. But here's the TLDR. We are telecommunications APIs that help you build amazing communications into your applications. That sounds like a lot of words, but you've definitely used Twilio at some point. If you've ever received a text message from a company, a phone call from a company, maybe a 2 f a text, lots of emails when it comes to Black Friday, even all sorts of communications, chances are they may have been using Twilio under the hood.\u003C\u002Fp>\u003Cp>Some of your biggest some of your biggest brands and favorite brands that people use use Twilio under the hood. But we also do so much more. So if you do wanna find out about some of the more advanced use cases, feel free to chat to me because the well of Twilio can be quite bottomless.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Definitely one way to put it. Yeah. If you're here, you might be coming from the Twilio world and not have heard of Directus before. So I will also tell you a bit about Directus.\u003C\u002Fp>\u003Cp>Directus is a back end, basically, that you can use to build wicked applications. You connect it to a new or existing database, and any number of asset storage, storages, storage. I don't know if it's like the word sheep where it's the same in plural. And you immediately get developer tooling, including APIs, a real time interface, authentication, and a user management system, and this really lovely web application with which to interact with that database, which you can easily hand to people who aren't developers. So you don't need to build APIs.\u003C\u002Fp>\u003Cp>You don't need to build kind of these admin panels, admin panel back ends. Really cool tool. And today, we get to converge the 2, which I'm really, really excited about. So, the project today, is oh, actually, no. A little bit more preamble.\u003C\u002Fp>\u003Cp>This event's happening as part of Leap Week 3. This is our week of announcements. On Monday, we did a keynote where we announced directors 11, which is coming out this week as a release candidate where we announce new shows for Directus TV. This platform you're watching this video in right now has, now 35 shows worth of content. You can go and potter around and find some cool awesome content too.\u003C\u002Fp>\u003Cp>Speaker 1: Like, I must say.\u003C\u002Fp>\u003Cp>Speaker 0: Thank thank you very much. Thank you very much for that. I'll pay you under the table later. And also, and and a bunch of other things, as well. This is one of the workshops that's happening this week.\u003C\u002Fp>\u003Cp>So this one right here is this Twilio workshop. After this, like, an hour and a quarter after the end of this event is a 100 apps and a 100 hours live where Bryant and some of our colleagues are gonna build an app in 60 minutes. I don't know what he's gonna build yet. I'm not sure he knows yet, so that would be chaotic as it was last time. Tomorrow, we're doing a workshop with Deepgram, which is a voice AI company.\u003C\u002Fp>\u003Cp>We'll be building a cool project there and then a community social networking thing on Friday using a platform that doesn't suck. So you can come and have a chat with other people who use or know about or are interested in direct us, in hopefully not too of a not too much of an awkward format. But we're getting ahead of ourselves. We're here for this Twilio workshop. And what we will be building today is a panel extension for Directus Insights.\u003C\u002Fp>\u003Cp>So Directus Insights is this dashboard builder tool here that we have. And in a dashboard, you have any number of these panels which can interact with the data in your database. But these panels aren't just for reading data. You can actually put components within them that are interactive. So you can add, like, forms and buttons, and you could just run arbitrary codes in them if you want.\u003C\u002Fp>\u003Cp>And that's what we're gonna be exploiting today.\u003C\u002Fp>\u003Cp>Speaker 1: So so what I'm hearing, right, because I have not played around with insights. So, insights essentially just give you the ability to create components that are powered by your data. So there are some obviously components that are really well built. And then see, it doesn't actually have to just be reading data because the thing that I'm really excited about is the fact that the stuff we're gonna do is going to read data, but it's gonna interact with it and cause other things to happen as a result. And that's something which I think is very, very powerful, especially when it starts to come to, like, workflows and building maybe internal tooling as well for people.\u003C\u002Fp>\u003Cp>This could be really, really useful.\u003C\u002Fp>\u003Cp>Speaker 0: Ding ding ding. So we've not spoken much about director, so you're kind of coming in it pretty fresh as well. So please keep asking questions. But, yes, you said a word you said a pair of words there, which I think is really interesting, which is internal apps. And I actually think that is the kind of hidden power of director's insights.\u003C\u002Fp>\u003Cp>While all of the panels that ship out the box are very much about, you know, building graphs and charts and reading data and analyzing it, it doesn't it's not just that. And you can also obviously build panel extensions and distribute them through the marketplace. So today, we're gonna build a panel that will have a drop down and show you the users inside of your directors project. With other phone number to those users. And so there'll be a phone number attached to them.\u003C\u002Fp>\u003Cp>And then you press a button, and we'll use the please forgive me if I get it around the Twilio voice SDK. Yes. That's what it's called. Fantastic. And we're gonna call that that person from the browser.\u003C\u002Fp>\u003Cp>So the browser will connect to a phone number and do a two way call.\u003C\u002Fp>\u003Cp>Speaker 1: Cool. Yeah. I'm really excited because this is something which I think lots of people oftentimes, we end up having tools in different places. Right? So for example, especially when it comes to telecommunications and telephony, we always, like, separate so that you have your email account or a phone, like, application or something separate to all the places you have you there.\u003C\u002Fp>\u003Cp>And, yeah, I'm not saying that's not bad, but when you start wanting to be efficient or maybe have some more smart and cool interactions, bringing those 2 together gets really powerful. And I think, like, what I could see here is and we could talk about, like, potential use cases. Look, we're not trying to sell you a thing, but kind of just tell you practically some of the ways in which you could be using these these tools. And I think like this, it could be a good way for you to, for example, like, quickly integrate communications without necessarily exposing PII. Like, you can have it so the person using the tool never sees a phone number, but it's still able to phone them.\u003C\u002Fp>\u003Cp>Some of your favorite ride sharing apps kind of use a similar technology behind this if your driver ever calls you to be like, hey. I can't find you. But, anyway, I'll stop talking. Let's get on.\u003C\u002Fp>\u003Cp>Speaker 0: No. No. Please do. A couple of other just bits of context. So we'll refer to the director's docs, obviously, a bunch today.\u003C\u002Fp>\u003Cp>We'll take a look at some of the extension docs. As part of that, inside of these docs, there are a couple of existing Twilio guides, and one additional non Twilio guide. And I wanna talk about them before we crack on so you kinda understand the approach which we are going to use today. First of all, there is this guide here called use custom endpoints to create an authenticated API proxy, which is a lot of words. So to break that down, endpoints, custom endpoints are one of Directus' extension types, and they allow you to just create kind of what they sound like, arbitrary endpoints that you can hit.\u003C\u002Fp>\u003Cp>We expose an express router, so it works just like that. You know, you set up route handlers, and then you can hit them. But within the context of directives, you can start doing stuff like checking if the user's authenticated in in directors or checking their permission sets and so on and so forth. So, this is an example where we actually just expose the full Twilio. Let's find it here, where we expose let me find it.\u003C\u002Fp>\u003Cp>Twilio host. Yeah. Where we expose just a root URL. And then what we do is we check whether you are authenticated, and that's the key part right here, which means you can't just hit this endpoint from anywhere. Right?\u003C\u002Fp>\u003Cp>You have to do it either with your Directus API token or cookie, or from within Directus itself. So that's 1. The second one is, another Twilio integration. This uses our automation tool, direct us automate. They're called flows each kind of, workflow.\u003C\u002Fp>\u003Cp>And this one here allows you to send SMS notifications. That's kinda interesting.\u003C\u002Fp>\u003Cp>Speaker 1: Can you do for these? Because, like, obviously, with SMS, when I'm talking to devs who are building into their applications, they're often either a couple of things. Either they wanna send a message, like, when a specific action occurs, Maybe they wanna do a batch, like, at the end of the day, or there's, like, a campaign going out, especially when you start getting to marketing and stuff like that. And the same with email, because we we also you we have email API. So, like, how can people trigger those, those things?\u003C\u002Fp>\u003Cp>Those flows?\u003C\u002Fp>\u003Cp>Speaker 0: Great question. Five ways. Event hooks. So something happens in your director's project often in one of your collections. A collection is a database with, like, additional director's metadata.\u003C\u002Fp>\u003Cp>So when I say collection, you can think table, but it is not just a table. Right? When something happens in your database, you can immediately fire off an automation. So this can be, a new user is created, a new file is uploaded, or any of your tables are have CRUD operations, executed against them. They can run as blocking, as blocking triggers, which means the whole flow has to see its way to the end, and then the database transaction will execute so it, like, intercepts it.\u003C\u002Fp>\u003Cp>And as a result, you can actually fail out. So, we have an example somewhere in our docs using a verification API where if you fail the verification, we actually just block you from signing up entirely. It just never commits to the database.\u003C\u002Fp>\u003Cp>Speaker 1: Ah, it's\u003C\u002Fp>\u003Cp>Speaker 0: really cool. On the other hand, it is cool because you can also manipulate data in the middle. Maybe you enrich it or stuff like that. That's really, really good. And then and then you have actions, which happen after data after data has, been committed or after a transaction has been committed, then it will run.\u003C\u002Fp>\u003Cp>So that's event hooks. Then we have webhooks, so just inbound HTTP requests. We have a schedule. So, you know, you set up an interval using, the 6 point Chrome job syntax, and then we'll run the flow, which is how you could then batch. Or, 2 more, actually.\u003C\u002Fp>\u003Cp>There's another flow so you can compartmentalize functionality and trigger other flows from core from, like, your your controller flow and path data in and backup and stuff like that. And then finally, manual. What manual does is in your data views where you've got your list of all your items in a collection or you're in an individual item in the editor, there's a button you can press that will pass in the IDs of the items that you've either checked in the collection or the page, and then send that into the payload as well. Manual flow triggers also have confirmation dialogues so you can pop up and collect a bit more information. There's the button there on the right.\u003C\u002Fp>\u003Cp>And it can you can pop up a box, ask for arbitrary information, and then trigger the flow, which I think is a very, like I'm looking at thinking comms. So they're very communication type, you know, option here. You can go write a message and hit go, for example. So they're the triggers. There's a bunch of inbuilt operations.\u003C\u002Fp>\u003Cp>You can also build custom operations, but that allows you to interact with your database, make it external web requests, just write some arbitrary JavaScript that can manipulate data in a little more of a clever way. But, yeah, that's something that's really cool\u003C\u002Fp>\u003Cp>Speaker 1: about this tool. Yeah. No. Absolutely. And a thing which I'm kind of noticing is the number of different ways, the flexibility that is given to you to try and to to trigger these things in different ways allows you to put into put the automation into different parts of your application.\u003C\u002Fp>\u003Cp>Especially, I'm loving the filtering functionality. I can already spin off a couple of great reasons why I'd use that, especially when you start to want to reduce computational load or con reduce things from blocking our operations from going through or vice versa when you actually do wanna filter stuff. So that's useful. I'm gonna play around with that next time I get a chance.\u003C\u002Fp>\u003Cp>Speaker 0: So I wanna jump in. There's one more guide I wanna show because this guide is going to basically be the north star for our approach today. And it\u003C\u002Fp>\u003Cp>Speaker 1: is this, which is filled\u003C\u002Fp>\u003Cp>Speaker 0: a little bit with, but it's using external weather data in a custom panel extension. And you might be thinking, I don't really see the translation between this and using Twilio in a in a panel. So before I explain this, maybe we take a moment to explain the Twilio flow in this all the way from generating I think it'll come in quite nicely. I can share your screen or you can just talk. It's completely up to you.\u003C\u002Fp>\u003Cp>Speaker 1: Well, I can start by just talking, and then we can, we can get even further. So we start with the Twibio voice API. So Twilio has a bunch of different APIs, and we have one that we focus on programmable voice. I would say one is more of a collection because even with invoice, there's a lot of diversity depending on what you want to do. In today's example, kind of like the external weather data, we essentially want to have a component in a web page, and this component is going to be able to make phone calls out to the users that are stored in the director's database.\u003C\u002Fp>\u003Cp>Right. So in the director's back end. So what we're going to need to do is we need a couple of things. We need a browser component or a component that that lives in a browser. So essentially a browser that is able to make phone calls.\u003C\u002Fp>\u003Cp>Your browser, unfortunately, doesn't have telephone APIs. So what your browser does is your browser can connect to Twilio, Twilio's servers. And then Twilio then handles and creates telephony using, old school telephony that, like, is really, really boring slash complex or really, really interesting depending on how what floats your boat, and then does all of that in the background. But what we do is we expose an SDK which you can build into your front end applications. Now there is one extra step which maybe adds a little bit of complexity, but I just wanna talk a little bit about that, which is just a bit of authentication and security.\u003C\u002Fp>\u003Cp>Because we can't just hand anyone the keys to make phone calls from any number in the world. Right? So when you have a Twilio account, you have a phone number. And when your browser connects it with you, what happens first is there is an exchange of a token. A token must be generated, it's what we call it, and that browser must use that token to connect to Twilio.\u003C\u002Fp>\u003Cp>Now that token gives it a couple things. It first says, hi. I am Nathaniel's computer, and I am connecting to Twilio, and I'd like to be able to make phone calls. Also, it says I have a phone number identity. So, like, a a a phone number which is attached to my identity.\u003C\u002Fp>\u003Cp>So when I do make phone calls, I will come across as if this phone call is coming from said telephone number. And these things happen in the background. Now the SDK has a lot of, flexibility, so we start off with a few basic methods which you can use. And then if you want to build a really, really custom interface, there are a lot more events that we expose. But if you are looking to get started with Twilio voice, I would recommend heading over to the Twilio docs, which I am showing on my screen.\u003C\u002Fp>\u003Cp>They often take you through starting your first phone call. This is from a server. You may want to start your first phone phone call, but then we also have these client side SDKs, and we talk you through how you use the client side SDKs. We have JavaScript SDKs, iOS, Android, and we even have React Native SDKs. I often like to say it's good to start with a quick start and then work backwards because the quick start builds all of that functionality, and then you can customize it as you are.\u003C\u002Fp>\u003Cp>But then there is also the reference if you want to dig deeper into all of the methods and the things going on behind the scenes. Is there anything I've left out? I know we've talked about this before.\u003C\u002Fp>\u003Cp>Speaker 0: No. You did mention the thing I hoped you would mention so I can explain why whether the data and Twilio are related. Before I jump in, actually, a reminder for those watching you, we've got a chat here. I'm watching it. So by all means, ask questions, thoughts, concerns, grievances even, and we'll address them as we go.\u003C\u002Fp>\u003Cp>But let's come back to my screen. Right. Why does this matter? Extensions and directives live in 1 of 2 places. They live in the data studio, which is the web app, or they live in the data engine, which is the back end, an API side extension, you could call it.\u003C\u002Fp>\u003Cp>The browser, because of just security in the browser and the way that we lock that down, can't always confidently make an external web request. Right? It can't go off to Twilio and say, hey. Go generate an access token for me. In this example, the same way it can't go out to a third party weather API confident.\u003C\u002Fp>\u003Cp>Like, if you control both sides and you configure security on both sides, you'll be fine. But you often don't control the the vendor. You don't control Twilio. You don't control the weather API. So what do you do with this?\u003C\u002Fp>\u003Cp>Well, you use an endpoint. Use an endpoint, which is an API extension. Use an endpoint first to actually make those external requests do what it needs. And then your front end extension, the panel in this case, and in this case in the tutorial, then calls an internal endpoint because it's that's what it is now. It's now gonna be like \u002F Twilio token, that my direct test project slash Twilio token.\u003C\u002Fp>\u003Cp>So it's now an internal API. So you you act you treat it like a proxy, which is actually not dissimilar to the post I showed earlier. To bring these together and make sure that you always have both of them and you don't have to deploy them separately, we have a concept called a bundle. So first, you create a bundle extension, which is an empty shell, then you add an endpoint, then you add a panel. And when you install the bundle, you get both of them together as as is required.\u003C\u002Fp>\u003Cp>So that's gonna be more\u003C\u002Fp>\u003Cp>Speaker 1: hearing you right. Just as a mental model, what I'm hearing is in this bundle, if this bundle was the application that fits into the panel, you kind of have the component, which would be the front end logic and then the endpoints can stand in for like a like almost like your server side. I know this is a weird way to think about it, but ways that you can query data and interact with other applications.\u003C\u002Fp>\u003Cp>Speaker 0: Yes and no. The only thing I'll change there is query data because panel extensions, for example, can query collections in directors without needing a a server. So if it's within the bounds of your director's project and the services we provide, in fact, we have them the the the they are called\u003C\u002Fp>\u003Cp>Speaker 1: Query external data. So data from outside. Correct.\u003C\u002Fp>\u003Cp>Speaker 0: Correct. Because, for example, your front end, your front end, your app extensions is what we call them, your front end extensions, have immediate access to all of your data because you're in this authenticated, you know, box that you can work within. But, yes, exactly. So the reason I wanted to show this, this weather panel thing is because that's basically we're gonna follow a very similar approach here. We're gonna create a bundle.\u003C\u002Fp>\u003Cp>We're gonna create an endpoint which will allow us to generate a token, then our panel will call that to grab the token and then use the voice SDK. So we've been talking for probably about minutes. I'd love to jump in. Now the format this is going to take, we decided just because, of of of the fiddliness of certain parts of this, is we are gonna go nice and slow, but we're going to present. We're gonna build it together.\u003C\u002Fp>\u003Cp>You can watch, and this will be available on demand if you wanna play along. This will also get turned into a blog post, and hello there is on demand. This will also turn into a blog post you can follow if reading, you know, technical material is more your thing. So if you're watching along, I I would say don't try and play along necessarily, but, take advantage of the fact that chat is here because we're we're here to chat to you. Right?\u003C\u002Fp>\u003Cp>Otherwise, this would just be a just a video, but this is a live event for a reason. Before we jump in, is there anything else you wanna add?\u003C\u002Fp>\u003Cp>Speaker 1: Nothing comes to mind. Let's get going.\u003C\u002Fp>\u003Cp>Speaker 0: Let's get going. So Why\u003C\u002Fp>\u003Cp>Speaker 1: do we do this? Why don't we have you start with, like, spinning up the environment that we're gonna use, getting us set up, so that we are ready to start building the telephony parts? And then I can step in. I can focus on building the Twilio parts. I know we we've we will switch between them and then connect those together and then do a little test\u003C\u002Fp>\u003Cp>Speaker 0: test as well. That sounds good. I actually had a little bit of a rundown that structure which looks slightly different. We'll follow yours, but as a result, this might end up being slightly more chaotic, but I like that. I like that format a little bit more.\u003C\u002Fp>\u003Cp>The the gremlins have just returned home, so we'll see what happens in the next 10 minutes. So can you hear that? You can hear that. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, now I can. Now I can.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Now you can. So here I have a brand new completely empty code editor. I am running Docker in the background. We'll be running Directus locally with Docker.\u003C\u002Fp>\u003Cp>So if you're following along, you'll need that. And what we're gonna do first is we're going to create a Docker composed dot yml file. And I do just happen to have here sequel I do happen to just have a light one prewritten because, you don't wanna watch me write this. Go grab the latest version of Directus, expose it on port 8255, map some of the internal volumes that Directus uses to local directories. So when we first run this, it will create a database in the uploads and an extensions folder.\u003C\u002Fp>\u003Cp>We need a key and secret, which you should replace with a random value. To me, replace with random value is random enough for today. The initial admin email and password, you, of course, can change that. We're gonna just use SQLite because I don't wanna we don't need anything more heavy, and so that will, pop the file inside of this, data file here. WebSockets are enabled, not that I think we're gonna use them today, but this exposes a WebSocket and GraphQL subscription interface for, subscribing to updates and directors.\u003C\u002Fp>\u003Cp>This one line here is not in our quick start, in our docs, and it just will improve the developer experience of building extensions. Whenever we'll we'll set it running so whenever we save it or rebuild, whenever it rebuilds, it'll auto upload indirectus. So with that done, we're gonna go ahead and, just taking a quick look at the chat. Hello, Scott. And that's c collection that logs different tokens for Twilio or other APIs to variable use token by another key.\u003C\u002Fp>\u003Cp>Then you have Internet access to modify tokens for phone numbers or something. This is to always be missing in Directus, and you end up doing this outside Directus UI. Hold that thought for some short period of time. We know. But today, we'll use environment variables here in, here in the Docker Compose file.\u003C\u002Fp>\u003Cp>But we're not gonna that's not gonna be the approach today in the interest of time. And then Docker Compose. That should just do its thing, he says. There we go. Running all the first time setup, and then it will run-in just a moment at 8055.\u003C\u002Fp>\u003Cp>So local host 8055. There is our brand new director's project.\u003C\u002Fp>\u003Cp>Speaker 1: That was so quick.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I like it. It's pretty sick. And this is also full fact directives. It's not like a shitter version you run locally.\u003C\u002Fp>\u003Cp>Like, it is the full thing when you use direct to this is what you use. The only caveat here is SQLite does not have geospatial plugins included out the box. So if you wanna use the mapping features, you'll wanna use just a different docker compose that's in our docs. It just takes a little longer to bootstrap, and it it wasn't necessary for today. Okay.\u003C\u002Fp>\u003Cp>Right now, there are a bunch of directors collections that have been created out of the box. Directors doesn't alter your tables in order to run. Instead, we create this, I don't know, like 20 ish tables or prepended with directus underscore. That means if you wanna get rid of directus later, you can just delete the application, delete these tables, and it's like we were never there. But this handles all of the UI, all of the settings, all of the configuration, they all live here.\u003C\u002Fp>\u003Cp>And the one thing I'm gonna do, because you can extend these, you can't delete these default fields, but you can add new ones, is for all the things that are here, avatar, email, title, you know, all of these, there isn't one for a phone number. So I am just gonna add phone because I think that's gonna be useful for later. Cool. I, out the box, have this one user here, but I think it would\u003C\u002Fp>\u003Cp>Speaker 1: be So quick question. So when you added phone, you you did it as a string. Just question, can you do any sort of validation from there\u003C\u002Fp>\u003Cp>Speaker 0: Yes.\u003C\u002Fp>\u003Cp>Speaker 1: I can. On that input?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. You can indeed. Over here, edit field validation.\u003C\u002Fp>\u003Cp>Speaker 1: Nice. Nice. And and, validation, what kind of validation do you use? What other rules? What is it?\u003C\u002Fp>\u003Cp>Speaker 0: Fantastic question. I think you could just throw in a red x to be honest. Yeah. It matches. You could\u003C\u002Fp>\u003Cp>Speaker 1: But also, it's got a bunch of other ones. Nice. Nice. That's awesome.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Yeah. It's pretty cool. And you can do, like, you know, logic you can do grouping.\u003C\u002Fp>\u003Cp>Right? And or and stuff like that. We'll leave it, and we'll just trust ourselves to put phone numbers in. Right? Don't do that in the real.\u003C\u002Fp>\u003Cp>I worked at another telephony company that offers APIs in the past. And you and I both know, no one ever knows how to put a phone number in correctly, and it's a huge pain in the ass.\u003C\u002Fp>\u003Cp>Speaker 1: So enforced validation. Because many different places, actually regionally write their phone numbers differently. So the way Americans write their phone numbers, especially compared to Europeans, Even, like, recently, we I had been I had built an app. Go to Singapore. We're testing the app.\u003C\u002Fp>\u003Cp>The app fails because Singaporean numbers just have way more digits than we had accounted for.\u003C\u002Fp>\u003Cp>Speaker 0: So Interesting.\u003C\u002Fp>\u003Cp>Speaker 1: Make sure you, like, keep it flexible for all of these inputs. But anyway, phone number.\u003C\u002Fp>\u003Cp>Speaker 0: Give me a phone number in whatever the format Twilio likes me. I think Twilio likes the plus.\u003C\u002Fp>\u003Cp>Speaker 1: Plus? 44 Yeah. 79 4757 41 48.\u003C\u002Fp>\u003Cp>Speaker 0: Why are you giving those me two numbers at a time? Anyway, whatever. Here. There. When we put you in.\u003C\u002Fp>\u003Cp>This is a user now in our in our project. If I provided the email and the password, you would be able to, you know, log and enable the app access. You would be able to log in here. The password is hashed when you save it, but you could you could just pop it in here. Yeah.\u003C\u002Fp>\u003Cp>Yeah. See? I see it in the chat here. I have to add a one in brackets. Like, it's a huge, honestly.\u003C\u002Fp>\u003Cp>Also, Americans in particular, that one in the brackets and sometimes you put other numbers in the brackets too, Okay. Absolutely. Archaic. Anyway,\u003C\u002Fp>\u003Cp>Speaker 1: it's it's like as it's as bad as them in the month before the day and the date.\u003C\u002Fp>\u003Cp>Speaker 0: Let's I'm not even gonna\u003C\u002Fp>\u003Cp>Speaker 1: Alright. But but seriously, though, there is international standards, ISO. I forget what the rest of the number is. But there is a Is it\u003C\u002Fp>\u003Cp>Speaker 0: the E164 thing?\u003C\u002Fp>\u003Cp>Speaker 1: I believe so. Yes. There is an international standard for phone yes. E164. That's what I meant.\u003C\u002Fp>\u003Cp>So you know, ISO. I don't know why it where that came from in my brain. But, you should check this out. And what you can often do, there are lots of tools and one that is actually sponsored by Twilio where they can, change inputs to fit the format. So it's always international plus international code, then the digits with no spaces.\u003C\u002Fp>\u003Cp>Speaker 0: But, interestingly, other telephony API company matches e164, but doesn't put the plus in front of it. So I don't think it's technically to standard, but it whatever. Twilio needs the plus. We've put the plus\u003C\u002Fp>\u003Cp>Speaker 1: in.\u003C\u002Fp>\u003Cp>Speaker 0: We we\u003C\u002Fp>\u003Cp>Speaker 1: Twilio doesn't need the plus. Like, we will pop in\u003C\u002Fp>\u003Cp>Speaker 0: the plus. Interesting. But\u003C\u002Fp>\u003Cp>Speaker 1: but the the important thing that I recommend to people is that you use that format when you have it stored because it's then uniform across every single phone. Absolutely. I wanna do is have some people have that ability and some people don't. So whichever way you do it, store it in your in your, in your, database, in your back end as, like, just one format, and then have logic that separates it out and changes what it looks like to users based on regionalization.\u003C\u002Fp>\u003Cp>Speaker 0: And not to derail us too much here, but Twilio does provide an API which formats phone numbers. I think it's called insights or lookup or something. Lookup. Lookup like the basic tier or whatever whatever will format the number for you. So you could introduce that as either a filter or an action event based trigger, and you could format it before it ever gets to your database.\u003C\u002Fp>\u003Cp>You rely on, you know, the user to get it somewhat correct in order to know. You know, you drop the country code entirely, for example, and sometimes it won't know which country is it. But in any case, let's I feel I I feel our time slipping away. So we have set up a project. We've added this extra field.\u003C\u002Fp>\u003Cp>I've added you in here. You'll be the guinea pig who gets the calls. Next in my little list was to add a bunch of environment variables and handle, like, all all of the Twilio configurations so we can focus on extension building. But I'm happy to get to the point where we first need a Twilio environment variable to do that. So it's up to you.\u003C\u002Fp>\u003Cp>What do you wanna do?\u003C\u002Fp>\u003Cp>Speaker 1: Why don't we jump to my screen and start from a console? I'm gonna start from the dashboard. Like,\u003C\u002Fp>\u003Cp>Speaker 0: 5 times zoomed in, like 5 zoom in points, please.\u003C\u002Fp>\u003Cp>Speaker 1: Would you like me to zoom in more?\u003C\u002Fp>\u003Cp>Speaker 0: No. That's good. That's solid. Cool.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. Let's go. So the first thing we're gonna need, because we're going to be making phone calls, is we're going to need a phone number. So we're gonna come over here in our dashboard, and we're gonna find our active numbers. Now I actually went in and bought a phone number earlier.\u003C\u002Fp>\u003Cp>And the reason I did this is because, phone regulations and some some of you maybe who've used Twilio in the past, Buying a phone number can be very, very quick and simple. Simple. However, there are regulations that come in from countries and, governments, and it's really good because it helps protect us from spam, which means you often have to upload, either ID or maybe address an address and other sorts of identification to say that you are who you say you are. So I had to do this. It literally I uploaded my ID with this number, and within half an hour, it was done.\u003C\u002Fp>\u003Cp>I will say I have a at Twilio dot com email so that expedites it. But within 48 hours, we usually get 1 get it done for you. And you can still start building and sending messages to the phone number that was verified with your account. So, for example, when you create an account, you have a 2 f a number. You can start messaging that 2 f a number, but then start messaging all users.\u003C\u002Fp>\u003Cp>You then need to have that extra level of, ID and regulation com regulatory compliance. Okay. So I'm based in the UK, so I think we should just use a UK phone number.\u003C\u002Fp>\u003Cp>Speaker 0: Works for me.\u003C\u002Fp>\u003Cp>Speaker 1: So now I have a UK phone number, and this is the first, I number that I'm going to hold on and keep into a safe place. So now I own a phone number. Now the thing is, for us\u003C\u002Fp>\u003Cp>Speaker 0: Could I put could I pause you? Yes. Throw me the phone number in our little joint chat. I wanna show what I'm gonna do with it because then I don't need to show again what we're gonna do with them. If it's okay with you, give me 2 ticks.\u003C\u002Fp>\u003Cp>Steal it stealing it for a moment. Over here in our Docker Compose file, we have these environment variables. I've added one called Twilio phone number. I'm gonna take the one that Nathaniel just sent me here, and there will be a series of other environment variables as Nathaniel goes around the UI. I will populate here.\u003C\u002Fp>\u003Cp>A couple of them are sensitive, so I'm no longer going to show the Docker Compose. I'll show you what happens once we get them all populated, though. Okay. As you\u003C\u002Fp>\u003Cp>Speaker 1: were. Awesome. So now that we have this, the next thing we're going to need, and it's just underneath, is a TwiML application. TwiML applications are pretty important. They are ways that we can essentially give Twilio some instructions of what what to do when phone calls happen.\u003C\u002Fp>\u003Cp>We are going to create a new to more application, and we're going to call this directus for the workshop. Now, what you often have is you have a voice configuration. This says what we should what should happen when a phone call is made to or from this TwiML app. I'm going to come and fill this in in just one moment. But I'm gonna hit create.\u003C\u002Fp>\u003Cp>So now we've got this director's Twilio workshop. We're gonna open it into a new tab. And I'm gonna come back and grab this SID. I need this SID for what we're going to do next. So this is the identifier of the application that we are going to be using.\u003C\u002Fp>\u003Cp>Alright. One more thing that I need to do. I'm going to be using something called functions to do this. You don't actually need to use functions. You can write your own endpoints, and your own ways to do this.\u003C\u002Fp>\u003Cp>I am going to be using this. I'm going to create a new function. I'm going to be creating a function that uses the Twilio client quick start, and I'm going to give it that AppSid that we just collected. I'm also going to give it a phone number. This phone number is the caller ID, and we're going to create it.\u003C\u002Fp>\u003Cp>Drum roll. It's creating in the background Any second now. I know why it's failed to create\u003C\u002Fp>\u003Cp>Speaker 0: Twilio cogs go brr.\u003C\u002Fp>\u003Cp>Speaker 1: The reason why it's failed to create it is because, I did a practice run a couple of minutes ago, and they have the same name. So I'm just gonna quickly delete these and then start again. Create this, pop in a caller ID and pop in that ID. This time, touching wood, it should be fine. Uh-oh.\u003C\u002Fp>\u003Cp>Okay. I might have to come back to this. Come on, folks.\u003C\u002Fp>\u003Cp>Speaker 0: I can always do this step as well. Like, it's all good. We'll we'll we'll we'll get this.\u003C\u002Fp>\u003Cp>Speaker 1: We'll get there.\u003C\u002Fp>\u003Cp>Speaker 0: You got this.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Once I've done this one, we'll we'll come back to it in a second. I think it's just, like, deploying, and it's actually going and taking down some assets, and it maybe needs to be taken a second. But we'll come back to this. Okay.\u003C\u002Fp>\u003Cp>So we'll come back and do this later, but for now, we have our Twilio phone number. We have our Twilio API appsid, and then we also need one more thing. We need to get API keys and tokens. That's\u003C\u002Fp>\u003Cp>Speaker 0: How many do you account set?\u003C\u002Fp>\u003Cp>Speaker 1: Yep. As well. Yeah. Keys and tokens and then accounts set. And this is essentially how we can, authenticate with Twilio.\u003C\u002Fp>\u003Cp>Give me a second. Just need to log in because I've been in for a certain amount of time, and I'm trying to access a secure page. And I need to turn over and hit my\u003C\u002Fp>\u003Cp>Speaker 0: So when all is said and done, to generate tokens using a an endpoint in Directus and to do all the stuff we're gonna need to do later, there are 5 pieces of information we will need from Twilio. We need a phone number. We need the the TwiML app SID, which you've generated. I'll grab it off you in a moment. We need your account SID.\u003C\u002Fp>\u003Cp>We need an API key and secret, which we're in the middle of generating now. We'll use the API key and secret to generate tokens, and we'll do that in the endpoint.\u003C\u002Fp>\u003Cp>Speaker 1: Cool. Sending over the SIT and sending Lovely.\u003C\u002Fp>\u003Cp>Speaker 0: This is the TwiML the TwiML app, Sid?\u003C\u002Fp>\u003Cp>Speaker 1: These are the API keys and secrets, and the TwiML Sid begins with an a p. So if it starts with a s k, it is always a secret key. If it starts with a a p\u003C\u002Fp>\u003Cp>Speaker 0: Man, you just sent you just sent me 3 long ass numbers, and I don't know it right. The first one is what?\u003C\u002Fp>\u003Cp>Speaker 1: So if it starts with a s k, it's a secret key.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Right. Right. Right. Got you.\u003C\u002Fp>\u003Cp>Got you. Got you. Got you. Got you.\u003C\u002Fp>\u003Cp>Speaker 1: The one under the secret key is the, secret no. So the see. Yeah. Secret keys partner. Secretly keys is the secret secret?\u003C\u002Fp>\u003Cp>Yeah. And then the last one that starts with an a p is the TwiML app.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Right. I'm gonna just say this again. The one that begins AP is the TwiML app.\u003C\u002Fp>\u003Cp>Speaker 1: Yes.\u003C\u002Fp>\u003Cp>Speaker 0: The one that begins 2 k is the API key.\u003C\u002Fp>\u003Cp>Speaker 1: Is the API secret?\u003C\u002Fp>\u003Cp>Speaker 0: Alright. And the one that begins SK is the\u003C\u002Fp>\u003Cp>Speaker 1: API key.\u003C\u002Fp>\u003Cp>Speaker 0: Right. Okay. Cool. That's why we double check. Can you imagine if we got much further and we're just like, why the hell isn't this working?\u003C\u002Fp>\u003Cp>It's because I missed copy and pasted a key. Freaking hell, man. I need your account, Sid, too. And then we've got everything we need from the Twilio side.\u003C\u002Fp>\u003Cp>Speaker 1: Account Sid\u003C\u002Fp>\u003Cp>Speaker 0: asked. Thank you. And we need to just make sure that function can be deployed correctly. And that's linked to the TwiML app.\u003C\u002Fp>\u003Cp>Speaker 1: Let me quickly go there and try this. I'm just gonna quickly hit this function. Alright.\u003C\u002Fp>\u003Cp>Speaker 0: Good. We're literally still in Sab. I'm having a blast doing this with you, by the way. We should do it more often.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Alright. Now it works perfectly fine because there's nothing else that has the same name. And let me just quickly tell people what this generates. This generates a function with just a little bit of code that just says, that if this number is coming from from a Twilio client, it should be forwarded to, whoever it's going to be calling and vice versa.\u003C\u002Fp>\u003Cp>Right? I'm\u003C\u002Fp>\u003Cp>Speaker 0: gonna copy this It's kinda nice that you could just create it from the boilerplate and forget about it to a degree. I still need your account, Sid. And then we are all groovy. I mean, get onto building building. Yeah.\u003C\u002Fp>\u003Cp>You didn't send it to me. Sid.\u003C\u002Fp>\u003Cp>Speaker 1: It's the last one. A c. Oh, cool.\u003C\u002Fp>\u003Cp>Speaker 0: Alright.\u003C\u002Fp>\u003Cp>Speaker 1: And, last thing, I'm just gonna quickly grab this URL, head over to the TwilioTwiML app that we created, and pop it into our voice. And voila, we are done.\u003C\u002Fp>\u003Cp>Speaker 0: Lovely. And what I will just say is the recording will be available on demand. We'll also write this up. So there was there was quite a few little moving parts there. They do all have purposes that, you know, that are important in this whole journey.\u003C\u002Fp>\u003Cp>We'll detail them there as well so you can in case it hasn't quite sunk in, it can. Of course, we also have the chat if you wanna ask further questions now. But with that, can I take my screen share back? Yep. Wonderful.\u003C\u002Fp>\u003Cp>So I popped all of those environment variables inside of the Docker Compose file, and now I just need to restart. So control c, up and enter. And now we restart the Docker container, and it will grab those variables and put them in the environment. So there we are. So now we're ready to actually go ahead and just start creating an extension.\u003C\u002Fp>\u003Cp>So what we're gonna do is I'm gonna create a new a new, terminal over here. And, in here, this is this folder, I wanna get into this extensions directory. So cd extensions, and I'm gonna run the npx create directors extension. Extension. Extension.\u003C\u002Fp>\u003Cp>That doesn't look right. Extension. Extend. That's correct. Latest.\u003C\u002Fp>\u003Cp>Just to make sure I definitely have the latest version of that. So, I get to pick the extension type that it will boilerplate, and I actually want a bundle, which I just happen to know is the last in the list. So I went up. I can call it whatever I want. I suppose I'll call it, Twilio, Twilio.\u003C\u002Fp>\u003Cp>I'll call it Twilio, whatever. Auto install dependencies. So just a reminder that a bundle isn't really an extension type in its own right. It's a shell where we can put multiple extensions. Now when these were originally created, they were actually to share dependencies and, you know, and reduce the overall code bundle size, But they also have this purpose of making sure you can distribute multiple extensions together.\u003C\u002Fp>\u003Cp>So it's just going and scaffolding that now. And the moment that's done, we're gonna go ahead and add an extension straight into that bundle. So we'll just give that a moment there. There we go. We will go into this new Twilio directory, and we will go, we'll run npm run ads.\u003C\u002Fp>\u003Cp>I believe that's what it is. And once again, we get to pick an extension type. Now if you remember, we're gonna build 2. We're gonna build a panel and an end point. The end point will generate the token.\u003C\u002Fp>\u003Cp>So we're gonna do that first, make sure we can generate a token, then we'll move on to building the panel. So we want an endpoint, and we'll call this one Twilio token. We'll just do it in JavaScript, and it will go and add that to this, package here.\u003C\u002Fp>\u003Cp>Speaker 1: And for those of you who are wondering why are we doing all of these Twilio tokens? Twilio tokens are because you're not going to actually put any of the credentials that we've just generated. Most of them none of them actually going to live in the browser, because that's unsafe. Someone could find them in the client. But what they do is they can be used by this function to create a temporary token, which then gets sent to the browser.\u003C\u002Fp>\u003Cp>Does that make sense? So we use these tokens from Twilio or no. These credentials from Twilio, I'm gonna call them, to create tokens. That token, it uses the account SID API key and secret to say, I am a Twilio approved. But I am the person on this account making this request.\u003C\u002Fp>\u003Cp>It then uses the TwiML appsid to say, this is the application I want to interact with and I would like to have permission to interact with. And then that generates a token which gets sent over to the browser, which can now interact with Twilio.\u003C\u002Fp>\u003Cp>Speaker 0: Hang on a minute. Oh, did it did it update in the thing here? It does say extensions reloaded. That was just while it was in the middle of boiler plating. Just to I'm I'm just gonna restart the Docker container.\u003C\u002Fp>\u003Cp>I don't think I actually need to do this. I just saw an error and I was like, let's work that out. So I think it should just be Twilio token. Twilio token. Oh, there it is.\u003C\u002Fp>\u003Cp>There we go. So that so that is this wrap. Ahoy ahoy world. Ahoy, world.\u003C\u002Fp>\u003Cp>Speaker 1: And for those of you who are wondering why we said ahoy, world. Ahoy was the greeting that was created for it wasn't created for, but it was used for phones when phones were first created.\u003C\u002Fp>\u003Cp>Speaker 0: I thought it was the first word ever said down a phone line.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Oh, holy world. Great. So we check that that works. Now in here, we wanna go ahead and actually, and generate a token.\u003C\u002Fp>\u003Cp>So I'll just create a new endpoint, a new route handler here. So router dot post, and we'll call this one generate rec res. Again, if you've ever done any kind of Node. Js web development, this will feel very, very familiar to you with good reason. This is just the express route to the I was talking about\u003C\u002Fp>\u003Cp>Speaker 1: it feels like express.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. Yep. Yep. That's exactly what this is. Now inside of here, we wanna go ahead and use the Twilio, helper library SDK.\u003C\u002Fp>\u003Cp>What do you call it?\u003C\u002Fp>\u003Cp>Speaker 1: From here, it will be the Twilio helper library.\u003C\u002Fp>\u003Cp>Speaker 0: The Twilio helper library. And so we actually wanna go ahead and install that. So npm install Twilio. Fantastic. Now we wanna go ahead and, and use it.\u003C\u002Fp>\u003Cp>So there is a page in the docs. Let's find it. I actually saved the link earlier. It's just shortcut here. This is the access tokens page, and it shows us how we can generate access tokens.\u003C\u002Fp>\u003Cp>The access token oh my god. What are these kids doing? I swear they're getting, like, pamphlets, and they're just going, can you hear them?\u003C\u002Fp>\u003Cp>Speaker 1: I can a little bit.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Like this. Anyway, so an access token is just this really, really long string here, that contains this information.\u003C\u002Fp>\u003Cp>Speaker 1: Web token, just in case some people know. So you can break it apart into its pieces.\u003C\u002Fp>\u003Cp>Speaker 0: Now we just wanna go ahead and generate them, and the docs, for Twilio provide these, these these different, snippets here. We are gonna be using the voice SDK in the browser. So we're actually gonna use create an access token, create an access token. So what we're gonna do here is is go ahead and copy and paste this, into this. Now there's a few things we're gonna do, we're gonna do ahead of time.\u003C\u002Fp>\u003Cp>So we are going to one moment. We are going to, obviously, pull these to the top.\u003C\u002Fp>\u003Cp>Speaker 1: Those we\u003C\u002Fp>\u003Cp>Speaker 0: definitely need.\u003C\u002Fp>\u003Cp>Speaker 1: We need to bring in our environment variables.\u003C\u002Fp>\u003Cp>Speaker 0: We do need to bring in a a couple of our environment variables, or we could just use them use them directly from process to end. But I was gonna say this is an ESM environment, so we are just gonna very quickly change, change the way that we import Twilio here. So import Twilio from Twilio, and then the access token will be twilio.jwtdotaccess. That was just a small small, semantic change there, but it is important. Then we'll take the rest of this That should bring you in the environment.\u003C\u002Fp>\u003Cp>You're dead right. You're dead right. And I believe that is exactly what I called them in the Docker Compose file, so I don't need to do anything with those. Then and this is now where we're moving into the, into the actual route handler itself. We're gonna go ahead and generate a ticket.\u003C\u002Fp>\u003Cp>Speaker 1: Through that as well so that people know what's going on. So the outgoing application SID is the application SID we created earlier. So if you remember, I generated it, and he's going to pop that in. And then we also have an identity. Now remember, because this is a browser panel, this browser panel could be anyone.\u003C\u002Fp>\u003Cp>Usually, you would decide a string to identify who is this browser. Quick question. Do we have access to who the logged in you Directus user is right here?\u003C\u002Fp>\u003Cp>Speaker 0: We absolutely do, and we're gonna come back to this once we have the panel because the panel is gonna send an authenticated request to this endpoint. And within that authentication will come the ID of the user along with all the roles and permissions that that user holds. So for now, we'll just hard code it as a user, but we are gonna swing back around to this later and, as you said, actually provide the user ID.\u003C\u002Fp>\u003Cp>Speaker 1: Cool. Alright.\u003C\u002Fp>\u003Cp>Speaker 0: Now I don't think if we take a look, you know, alright. We're not doing incoming calls, but I think we'll just leave it as is. We generate a new access token with with all of these values we've set up. The only thing left to actually do is, is return this. Yeah.\u003C\u002Fp>\u003Cp>Res.send, and we'll just send this value instead of\u003C\u002Fp>\u003Cp>Speaker 1: the document. People are wondering, they're like, what is that voice grant stuff I am seeing? That's because you can you might want to give multiple permissions to different products with one token. So you can give a voice grant, a, a messaging grant, a conversations grant, a video grant. You could do all of that and send it in one token just to give that token lots of permissions rather than doing this if you're using multiple products.\u003C\u002Fp>\u003Cp>Speaker 0: Absolutely. So with that, it's built, which is cool. This should generate a token for us if we go to slash willio token slash generate as a post request. Once again, it has the voice grounds. It has our account SID, API key, and secret.\u003C\u002Fp>\u003Cp>It has our identity, which is, for now, just a fixed string, but we will update that in a moment. And then it will go ahead and generate a token and, and provide it as a JSON web token, which is what we're gonna need in our panel. So let's just quickly test this.\u003C\u002Fp>\u003Cp>Speaker 1: Expect to see a string of epic proportions and randomness show up in a second, if this works.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. So Twilio token slash generate.\u003C\u002Fp>\u003Cp>Speaker 1: And we do. Perfect.\u003C\u002Fp>\u003Cp>Speaker 0: Lovely. So that's fantastic. That's actually really at its absolute core, all this endpoint needs to do. There are 2 things it doesn't do, which we're gonna do later. The first is actually identify the user, and the second is be authenticated at all.\u003C\u002Fp>\u003Cp>Right now, any user on the web, if this was a hosted application, could hit this endpoint and generate tokens. Not good. So, later on, we're going to make sure that this is more locked down. But, for now, I think this is good.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And I think that and you can keep going while I say this. I think that people must remember on the web is just because a, URL is not publicly advertised does not mean that people will not be able to find it. And oftentimes, people use, like, just URLs are public to create really important private stuff like this and end up creating exploit, like, holes that the application can be exploited through. So make sure you protect this, outside of Demoland as well.\u003C\u002Fp>\u003Cp>Okay. So we're creating now our actual panel.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Is that how you spell dialer?\u003C\u002Fp>\u003Cp>Speaker 1: With yeah. That was perfect. You corrected yourself.\u003C\u002Fp>\u003Cp>Speaker 0: Nice. We'll just do it with JavaScript again. So we're adding this to the bundle now. The nice thing about this is we only need to be running that npm run dev at a bundle level, and it will rebuild when the things under are updated as well.\u003C\u002Fp>\u003Cp>Speaker 1: Cool. So\u003C\u002Fp>\u003Cp>Speaker 0: I think you see the extension. Yeah. I let's, let's npm run dev this, and, let's take let's take a little look firstly around the code and then then what it does. So there is. That's like, where is it?\u003C\u002Fp>\u003Cp>Oh, there we are, the dialer. The dialer is made up of 2 files, the index dot JS and the panel dot Vue. The front end of directives is built in Vue. JS. And therefore, when you're building these app extensions, they are also built in Vue.\u003C\u002Fp>\u003Cp>Js. So we have an ID. This has to be unique across the whole system. So, you know, you can't have 2 conflicting extensions. It's generally best practice to prepend this with your author name, to, you know, namespace it somewhat.\u003C\u002Fp>\u003Cp>But for now, I'll just be like Twilio dialer, whatever. We provide a name. This will show in the UI. Don't know why I keep writing it like that. We are going to put in an icon.\u003C\u002Fp>\u003Cp>You can use any Google material icon. Descriptions, make own calls. Now all I want you to do for a moment is take in the fact that there is this option called text. And I'm gonna show you what that does in the actual directus UI in just a moment. Now the panel, is a view component.\u003C\u002Fp>\u003Cp>It takes in the props from from the index JS. So here we have the text. Text is there. That's how data gets passed between this kind of configuration pane and the panel itself, and then it's just a view component. By default, out of the box, it comes with the options API.\u003C\u002Fp>\u003Cp>You are, of course, completely, able to use the composition API, which is, I think, what we're gonna do today. I think most of you developers now kind of lean towards expecting to see that. So what I'm actually gonna do is delete no. I'm not gonna delete anything yet. I'm gonna show you what this panel does.\u003C\u002Fp>\u003Cp>So let's create a new insights dashboard. We'll call this Twilio workshop, and we will add we will restart container. I didn't need to do that. I just needed to refresh the browser. That was it.\u003C\u002Fp>\u003Cp>I couldn't remember what step I needed to take, so I'm like, let's do them all. There we go.\u003C\u002Fp>\u003Cp>Speaker 1: Gotta be sure. Better safe.\u003C\u002Fp>\u003Cp>Speaker 0: You gotta be sure. There's our Twilio guy. There's the icon of phone.\u003C\u002Fp>\u003Cp>Speaker 1: I was really\u003C\u002Fp>\u003Cp>Speaker 0: also pass in yeah. It was easy. Thanks. I mean You can also pass in these SVGs. Drop it.\u003C\u002Fp>\u003Cp>Speaker 1: I'm still impressed.\u003C\u002Fp>\u003Cp>Speaker 0: Cool. You can pop in SVGs too. And as you can see, the SVGs are all purple. That is the theme color of the director's projects. You can also use these CSS variables in the SVG, which is kind of nifty.\u003C\u002Fp>\u003Cp>So they all feel, you know, like they belong. There's the text that this is the show header show header right here. You can add extra configuration if you want, and there it is. Show header, you can put some text in, Call people. There it is.\u003C\u002Fp>\u003Cp>There's the text that came from the from this configuration options. I think it's called options pane. We don't want text, but, just wanna show you that's that's kinda how how it works. So now we're going to start now we're going to start ripping it apart. First thing I think we're going to do is we are going to let's just have a think here.\u003C\u002Fp>\u003Cp>I think we're going to remove all the options. I don't think we need options in this.\u003C\u002Fp>\u003Cp>Speaker 1: No.\u003C\u002Fp>\u003Cp>Speaker 0: It has you know, it's it's going to just show the users.\u003C\u002Fp>\u003Cp>Speaker 1: Like an example is, for example, a lot of companies are international. So they end up having, like, a German number, a UK number, a different number. So I'm saying I'm probably seeing you could have, like, as an option, a drop down to be, like, I wanna phone this person, but from this number because they're in Germany, and they wanna use the German number. So they it feels familiar to them, something like that.\u003C\u002Fp>\u003Cp>Speaker 0: Absolutely. Absolutely. I think what we'll do here is we'll just empty that out to a div and onto the let's just let's just rock on. Let's just you know? Yeah.\u003C\u002Fp>\u003Cp>I love some boiler plating, but we're just gonna we we get we're gonna go from scratch. So but and I'm gonna use setup here, so we'll use the composition API. I'm not gonna bother reloading the browser because nothing's gonna show up. It's gonna be an empty box. So now we're at the point where with this blank slate, we can really talk about every line of code we're gonna write, help you understand what it does, and build this extension that will call this this endpoint we've created, which is now an internal API endpoint.\u003C\u002Fp>\u003Cp>We'll instal and configure the voice SDK, and we will eventually just make a phone call. I'm gonna pause for just a just a moment. We're about halfway through our time, and this is comfortable. This is a good spot to be in. Does anyone have any questions in the chat?\u003C\u002Fp>\u003Cp>And, Nathaniel, too, you're kinda seeing this set up, I think, for, you know, one of the first few times. Do you have any questions so far?\u003C\u002Fp>\u003Cp>Speaker 1: No questions. I'm curious. I'm really excited about some of the the options as well. So you know how it had text. What are the types of things we can put into the options?\u003C\u002Fp>\u003Cp>So, like, I I just said, like, a drop down list, but are there other kinds of, interactions or ways we can select?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Yeah. All of the built in they're called interfaces in Director, so form input, you know, you could we call an interface. You can use any of the built in interfaces.\u003C\u002Fp>\u003Cp>So you have the the WYSIWYG. You have a codes input box that does some syntax highlighting. What the hell is going on? You have a text box that, you know, can be integer or float type. Like, you can enforce that.\u003C\u002Fp>\u003Cp>You have sliders. You have relationships. You can actually pull data from collections and select an item within the collection. All of the built in interfaces and directives are exposed to you through through this.\u003C\u002Fp>\u003Cp>Speaker 1: Interesting. Good to know. Good to know.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. Yep. Yep. And you can also in fact, if I just if I just undo this slightly, there's other, like, meta information you can provide. You can, hide values, make them like a password It says width full.\u003C\u002Fp>\u003Cp>You can also do width half and put 2 things side by side. So we give you some flexibility around how that looks and\u003C\u002Fp>\u003Cp>Speaker 1: feels. And last but not least, like, because I'm thinking, like, we're making this panel and it's kind of just a demo one. But people can people, share panels maybe for other users to to maybe try out?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So, on the director's marketplace, you can publish them on npm. If you boilerplate it with, the CLI, which I boiler plated mine with, we everything's set up. You can just push it straight to NPM, and in a few hours, you'll see it appear in the marketplace.\u003C\u002Fp>\u003Cp>Speaker 1: Awesome.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Cool. Right. So let's move into the panel dot view. Let's move into the panel dot view.\u003C\u002Fp>\u003Cp>So we're starting from scratch. What we're gonna do here? First thing we're gonna do is in is import all of the composables that come with the view, with the directors extensions SDK that we're gonna use today. So we're gonna import use API. We'll talk about what that does in just a moment.\u003C\u002Fp>\u003Cp>And use items. Once again, we'll talk about what that does in just a moment.\u003C\u002Fp>\u003Cp>Speaker 1: Is related to how well, I know we're gonna talk about in just a second. No. Yeah. These are things that are coming from Directus that we can use Yeah. In our component.\u003C\u002Fp>\u003Cp>Speaker 0: Correct. And, actually, I might just take this moment to pause and show you inside of the composables right here. These components make working with direct this easier. Use API effectively wraps your API request with all of the authentication that comes in your that is in your Directus client. So you could, of course, just use fetch.\u003C\u002Fp>\u003Cp>But then how does your your how does your endpoint know it's you? But if you instead use the use API, which, I think, yes, actually, it's under the hood. It also sends all of your client details. And that's how we're gonna know which users there, whether they're authenticated and so on and so forth.\u003C\u002Fp>\u003Cp>Speaker 1: And this is really because I'm guessing it means you don't need to write, like, all of that metadata into\u003C\u002Fp>\u003Cp>Speaker 0: your\u003C\u002Fp>\u003Cp>Speaker 1: access to your endpoint URL.\u003C\u002Fp>\u003Cp>Speaker 0: We're just gonna use the use API composable instead. UseStores allows you to go in and actually access information, use information within your directors project itself. So you can access things like all of the permissions users hold, data about the collections, the metadata about collections. And the other one I imported was the specific use items composable. And this will allow you to query data in your director's project directly from your panel.\u003C\u002Fp>\u003Cp>So we're not going off to a back end to do this. We're doing this within the client itself.\u003C\u002Fp>\u003Cp>Speaker 1: And so In\u003C\u002Fp>\u003Cp>Speaker 0: turn, it knows what you have access to and will honor the access control of your logged in user.\u003C\u002Fp>\u003Cp>Speaker 1: So yeah. So this is like takes away the need to query a database because you are building an application over inside slash beside your database.\u003C\u002Fp>\u003Cp>Speaker 0: Absolutely. Absolutely. You know, I think a lot of people see directors as a CMS or see directors as a back end for an external application. But through a Directus insights and through, like, these panels and through modules, which is another extension type, which is these. So they add they basically give you a blank slate, which you can build.\u003C\u002Fp>\u003Cp>Yeah. It's the most low level extension type, I suppose. It's a good base to actually just build your application within the Data Studio itself. So, yeah, pretty pretty nifty. So we're gonna use use API to make that API call to get our token.\u003C\u002Fp>\u003Cp>We're gonna use use items to populate the drop down list of all of the users that we can call. We're also just because, just because we're in a, a view app here, we're just gonna import graph. There's not nothing. We don't need to explain that terribly. That's just a kind of quite boilerplatey stuff.\u003C\u002Fp>\u003Cp>Now we're going to create an instance of use API. We call it API. Use API. And we're also going to now create our instance of use items as well. So we are going to pull out the items from use items.\u003C\u002Fp>\u003Cp>Now the signature here is a little bit wild, so bear with me. The first thing we need to pass in as shown here, I don't know why it's using this. Maybe I've called it a font that doesn't exist, but why the hell does it look like this? Anyway, the first thing we need to pass in is a ref that contains a string of the collection name we're querying. So we don't just pass in the string, we pass in a ref.\u003C\u002Fp>\u003Cp>Cool. We can we can rock on there. We can literally just put in ref. Not a problem, and we had to import it to do this. And we're gonna we're gonna query the directus views as collection.\u003C\u002Fp>\u003Cp>Speaker 1: So use item The second now gives you access to Yeah. All of the Directus users. You can just pull them in.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Because that is a collection in your database like any other. Now we do have a distinction between system collections and user collections. User collections you create, System collections are these kind of 20 I showed you earlier out of the box. In fact, we haven't got any user created collections in this project.\u003C\u002Fp>\u003Cp>We're not gonna use them. But, yes, you can query these just like you can any other any other collection. So that's completely correct. And the second thing we we provide is a query. Now directors has quite a robust I'll show you here quite a robust query language.\u003C\u002Fp>\u003Cp>Here it is. Not query parameters. That's what I was looking for. So you can specify what fields are returned. You don't need everything.\u003C\u002Fp>\u003Cp>Right? You might only just need the ID and the title of a blog post, you know, for example. You can apply filters. You can do searches. You can do, like, patch basic pagination, you know, by limiting how many per page and what page you're on and what the offset is and so on and so forth.\u003C\u002Fp>\u003Cp>You can apply sorting and just more. Now we're gonna do that just to specify what fields we want returned because I don't want that huge object for every user. I just want a little bit a little bit less. So we're going to pass in fields. And we we want yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And and Go ahead. You can keep typing, but I'm guessing this is something, like, that people should always be doing because it makes your your moving smaller chunks of data around, so only getting the things you need rather than Got it. Like, I feel like it's a very early in career developer move to just, like, pull down the entire database to get one field from one user.\u003C\u002Fp>\u003Cp>Speaker 0: And what's really, really nice about this is we put we expose a GraphQL, you know, API, but we also expose a REST API. And now what I think is one of the core value propositions of GraphQL, which is you you construct these complex queries that only bring back what you need, you can do regardless. You can really pick what's right for you. And this works with, this works with relational data as well. Here in the items, so in the use items composable, I could, for example, let's say there's a relation, right, called, I don't know, posts.\u003C\u002Fp>\u003Cp>I could be like post stock title, and I can start getting the relational data too. So it's really powerful.\u003C\u002Fp>\u003Cp>Speaker 1: So in in that case, you almost end up querying 2 tables. Right? Because you're querying a table and then querying Yeah. That's critical.\u003C\u002Fp>\u003Cp>Speaker 0: And that's where being very selective about what you're bringing back is really critical because another valid fields query is this. Give me everything on this level. Give me everything one level down. And you know what? You know, you can do that.\u003C\u002Fp>\u003Cp>This is incredibly computationally expensive. But you could do this. I mean, you could. You could. But at any level, you could do this.\u003C\u002Fp>\u003Cp>You could be like, you know, give me everything in the you give me everything in the posts collection, for example. So, you know, we give you that flexibility. You know, you can blow your own shit off.\u003C\u002Fp>\u003Cp>Speaker 1: There's great power. Great responsibility.\u003C\u002Fp>\u003Cp>Speaker 0: You you absolutely got it. Now, what's gonna come back from here is in fact, this might be a point where if I just console log items, we can just get a little look in on what's actually happening here.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, I'm guessing we're gonna see user 1, Nathaniel Okeno.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And there's 2 users. There's the admin too. Yes. My user.\u003C\u002Fp>\u003Cp>I just got phone. Account. Right? You know what? We could apply a filter here that says, just give me users who have phone numbers.\u003C\u002Fp>\u003Cp>I'm not going to do that. But if I refresh here, there's the array. The array has 2 users, the admin, no phone, Nathaniel, as we expect. Awesome.\u003C\u002Fp>\u003Cp>Speaker 1: Cool.\u003C\u002Fp>\u003Cp>Speaker 0: What else what else is important here? I might just rename this users because items is a little bit of a weird word to use when they are users. So that's just a convenience thing, I think. And then what we're gonna do now is populate a a select, a drop down. And then when you select something, we're just gonna bind it to a variable in here.\u003C\u002Fp>\u003Cp>So we'll just create that variable now. All we want is their phone number. Right? We don't care about the rest of the objects. We just care about the phone number, and that can start off with a value of null.\u003C\u002Fp>\u003Cp>Now that we've pulled in all these users, let's actually display up here. And now I have the kind of pleasure of showing you the component library. Here's something else. This is the component playground. These are components we use within the data studio that we expose to extension authors.\u003C\u002Fp>\u003Cp>So we are going to use the v select, and it has all of the kind of styling applied. Really, really nice to kinda out the box. This is what it looks like. We need to v we need to bind it with a with a variable, which is why I just created phone number. Phone number will go in there, and you pass in items.\u003C\u002Fp>\u003Cp>Items have text and value. Now our items do not have text and value. They have first name, last name, phone number, and so on and so forth. You can you can change which is which field is used for the text. So we'll we'll do, like, first name.\u003C\u002Fp>\u003Cp>In reality, you would probably do, like, a computed, you know, a computed array that would add the first and last name together and display that. I am too lazy for that. We'll just display the first name. But that's what you would do in the real. Right?\u003C\u002Fp>\u003Cp>So, that's I mean, that I I may as well copy it, to be honest. There we are. Yeah. Let's put it inside this div here.\u003C\u002Fp>\u003Cp>Speaker 1: K. So we don't That is the value is going to be so I'm guessing the value is going to\u003C\u002Fp>\u003Cp>Speaker 0: be the phone number\u003C\u002Fp>\u003Cp>Speaker 1: that we Yep.\u003C\u002Fp>\u003Cp>Speaker 0: Created earlier. Phone number.\u003C\u002Fp>\u003Cp>Speaker 1: Yep.\u003C\u002Fp>\u003Cp>Speaker 0: And the items, is going to be users.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. So it's gonna grab the list of users that we got.\u003C\u002Fp>\u003Cp>Speaker 0: And then grab the list of users. As noted, we do not, we don't carry out value. As noted, it's interesting that it did this because you're meant to do this in Vue, when it's an attribute. So we have the item text, which is first name, and there was item value was the one under the item.\u003C\u002Fp>\u003Cp>Speaker 1: So you can choose what is visible to click, but then what that value actually trick the way it adds to whatever. Okay. That makes sense.\u003C\u002Fp>\u003Cp>Speaker 0: I mean, let let's make this easy. Let's just let's just, you know, print the value. So now if we refresh, there's our drop down admin and Nathaniel. When you pick Nathaniel, there's the\u003C\u002Fp>\u003Cp>Speaker 1: top up. Blank when it was admin because admin doesn't have a number.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Because, actually, initially, it was, on, I think I need to put in, like, a select. It's unset to begin with. It's null. So whatever.\u003C\u002Fp>\u003Cp>We would need to put in, like, a select user default setting.\u003C\u002Fp>\u003Cp>Speaker 1: Doesn't have a number. So\u003C\u002Fp>\u003Cp>Speaker 0: The admin doesn't have one, though. But, yeah, originally, this isn't because admin has nothing. It's because it's null. But whatever, like yeah. Small small nuance, small small edge case kind of thing there.\u003C\u002Fp>\u003Cp>So now we have that, then we are\u003C\u002Fp>\u003Cp>Speaker 1: I think we need\u003C\u002Fp>\u003Cp>Speaker 0: I mean, that's probably we need a we need a button. We do need a button. Can actually I think yeah. Yeah. We do.\u003C\u002Fp>\u003Cp>Let's use another component for that. Let's look at here. We have the button, shock horror. I mean, it's just the v button. We actually need to do nothing else.\u003C\u002Fp>\u003Cp>I'm not even gonna bother copying that, to be honest. V he says and then makes a typo. V button, call user. And then we will when it's clicked, we will start call. And, of course, we need a function, Let's get this const stop call.\u003C\u002Fp>\u003Cp>I think it needs to be an async function for later. I just happened I mean, we could change it later, but we'll do it out there. So there you go. That will start, and then we'll we'll grab the value of phone number at that point. When we start a call, we will then go and grab the token.\u003C\u002Fp>\u003Cp>Tokens have a short lifespan, so you really wanna grab them at the point you're gonna use them all very soon before. So we'll we'll go and handle that.\u003C\u002Fp>\u003Cp>Speaker 1: Spend the token time time to time to live. It is not recommended for you to just, like, create one that lasts the whole year. You want ones that are gonna last a short amount of time.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Because they can they I mean, if they're intercepted, they're usable, basically. So, yeah, you do only want them to live They wanna run a short while. So we obviously haven't done anything with the use API yet, but I think you broadly understand what it's about to do. Do you have any questions?\u003C\u002Fp>\u003Cp>I'm pretty happy with how it's shaping up.\u003C\u002Fp>\u003Cp>Speaker 1: I like how quickly you can build the especially with the component library, how quickly you can build the dysfunctionality, with all of and I'm guessing the great thing is, like, with stuff like that, you're standing on the shoulders of giants, of heroes where, like, they have done all the sorts of things to, like, think like, I saw you change in the width of the button, like, really easy just dragging across and stuff. Yeah. That is really, really useful.\u003C\u002Fp>\u003Cp>Speaker 0: This one's my favorite personally. It's called fancy select, and this is just a select 2 items, a divider and a third item. And it's just a select, but look how pretty that is.\u003C\u002Fp>\u003Cp>Speaker 1: I love how the pass.\u003C\u002Fp>\u003Cp>Speaker 0: And that's used and that's used inside of the UI as well. We actually used it when we, when we or did we use it actually? Nope. Lied to you. I used it earlier today.\u003C\u002Fp>\u003Cp>No. So there you go. Right. Okay. Save\u003C\u002Fp>\u003Cp>Speaker 1: and refresh.\u003C\u002Fp>\u003Cp>Speaker 0: There's the button. The button just runs this function. Now with the point where we can go and get the Twilio voice SDK. So let's let's go ahead and do that. And, yeah, that's a really good point, Alex, in the chat.\u003C\u002Fp>\u003Cp>This makes customizations feel native to the by using the component library, it all feels native. Don't get me wrong. Obviously, it lacks polish. It needs some padding. But, actually, to be fair, just by adding padding\u003C\u002Fp>\u003Cp>Speaker 1: It would be a really good job.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Yeah. Yeah. Right?\u003C\u002Fp>\u003Cp>We're we're lazy, so this is what you get today, but you could Speak\u003C\u002Fp>\u003Cp>Speaker 1: for yourself. I'm joking.\u003C\u002Fp>\u003Cp>Speaker 0: Speak for you. Yeah. I'm I'm the one with the keyboard, so I'm speaking for both of us. Hey. Right.\u003C\u002Fp>\u003Cp>Let's go and install npm install, hang on a minute.\u003C\u002Fp>\u003Cp>Speaker 1: Twilio and I think voice SDK.\u003C\u002Fp>\u003Cp>Speaker 0: It is. It is. Npm install at Twilio slash voice SDK.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Not my first rodeo, mister Academy.\u003C\u002Fp>\u003Cp>Speaker 1: I have made this mistake multiple times.\u003C\u002Fp>\u003Cp>Speaker 0: Now Cool. Before we continue, there is one more piece of configuration. And it does involve going back to that, to the, Docker composed file and adding one more environment variable. Let me let me shove this up. We're still here underneath the eyes.\u003C\u002Fp>\u003Cp>Hello. But I just wanna get it to the point where I'm not about to leak all the information. So I will get rid of that now. Okay. Back it back here.\u003C\u002Fp>\u003Cp>We're back here. Just above the cursor, a couple of lines are all those environment variables I don't wanna show you, but we do have to add one more. You might be thinking, pretty damn sure I've added enough of these now. Like, I I don't don't wanna do this. But what we do need to do is, let's find it.\u003C\u002Fp>\u003Cp>I did write because it's long. I did write it earlier. Needs to add this environment variable. Why? What the hell is this?\u003C\u002Fp>\u003Cp>As mentioned earlier, the director's data studio, doesn't allow request to external necessarily without perfect configuration allow requests to third party services, like to 3rd party servers or whatever. And that's a security precaution. That's great. But we are going to be connecting directly from our browser to a phone number. I think, strictly speaking, we could probably build this as an endpoint too and use the endpoint as like a middleman proxy, but we don't we don't have time for that.\u003C\u002Fp>\u003Cp>The easiest way to do it is this environment variable where we're basically changing the content security policy, to allow the connection to this WebSocket URL, which is what the voice SDK is gonna do in a bit.\u003C\u002Fp>\u003Cp>Speaker 1: I was wondering. I was like, have we ever seen this before? Like, but now that makes sense. So you are giving It\u003C\u002Fp>\u003Cp>Speaker 0: took login. It\u003C\u002Fp>\u003Cp>Speaker 1: took Yeah. Yeah. So you said\u003C\u002Fp>\u003Cp>Speaker 0: to work that out.\u003C\u002Fp>\u003Cp>Speaker 1: We are making sure that the, the director's browser I'm gonna call it the director's browser, which has these extra security walls around it. We're just like, yo, let, WebSocket connections to this Twilio u URL happen. Awesome.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. And otherwise, that will error. And while we were putting this together, that error, it's like I had to add that environment variable for it to work. It's pretty descriptive. The error tells you it's a CSP, a content security policy problem, and it's on this connect source value.\u003C\u002Fp>\u003Cp>And that it was while trying to access this this, URL. So, you know, it it was pretty easy to work out, but let's save ourselves the the pain and do it now. So so we've installed the SDK. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Talk about. So we've installed the SDK, and as I'm talking, you can maybe, like, type. So with the voice SDK, what we start, we bring in the SDK, and we bring in specifically a device. The idea of a device is a device is anything that can connect to Twilio, because the device right now, it's gonna be a browser, but it could be a phone if you're using the, like, react native or the iOS or Android SDKs. But we are bringing in a device, and that device needs to have permissions, and we give those permissions from a token.\u003C\u002Fp>\u003Cp>But we don't have a token now. So how are we gonna get this token from our endpoint? Great.\u003C\u002Fp>\u003Cp>Speaker 0: We've kind of spoken about it already. We're gonna use this use API composable. Call our now internal endpoint slash, slash Twilio dash token slash generate as a post request, we'll then get the we'll then get the value here. So, the way we're gonna do that, we've already created this, is we are going to, pull out the data value that comes back. I'm already gonna just call it token.\u003C\u002Fp>\u003Cp>Await API dot post because it is just an Axios instance. Ultimately, I'm gonna go to \u002F Twilio token slash generate. That will return a string, which is the token, which we can then remove these question marks. We'll pop this directly in here. We are That's how you pass this device.\u003C\u002Fp>\u003Cp>Yes. We're ready to register the device. There is one more thing I would just wanna take a moment to do. And, it's not I've I've not taken note of it, but I just wanna derail this for a moment. I did oh, shall I do it?\u003C\u002Fp>\u003Cp>Shall I do it after? It's the whole sending over the sending over the correct user and authentic. I think we'll make the request first, and then we'll we'll see what happens. So we have a device, and then let's just console log device. Realistically, we're either gonna see a device or we're gonna see an error because that token's invalid.\u003C\u002Fp>\u003Cp>There is no there is no other outcome. It's one of those two things. Extensions reloaded. I hit save. So we're gonna refresh this.\u003C\u002Fp>\u003Cp>Open the console. Go to you. Hit call user, and that's the device. Rock on. What else is important here?\u003C\u002Fp>\u003Cp>Oh, no. No. No. No. We're not done.\u003C\u002Fp>\u003Cp>We need to also do the device. We need to register the device. That's my bad. That extension's reloaded. I might just rename I think it's alphabetical because, again, lazy.\u003C\u002Fp>\u003Cp>Oh, being being too hopeful there, I think. Okay. Whatever. Cool user. Oh, problem.\u003C\u002Fp>\u003Cp>Why? WebSocket received error undefined. Oh, I think oh, I didn't restart the I just needed to because I changed the environment error.\u003C\u002Fp>\u003Cp>Speaker 1: That was the error he told us we were going to see, and he walked right\u003C\u002Fp>\u003Cp>Speaker 0: into it. But there it is, by the way. Refuse to connect to this URL because it violates the following content security policy directive, connect source self HTTPS. So they were there, and then I added on to the end of it the WSS, you know, Twilio voicing. The Twilio token generate is, I feel like you probably came into this, into this session a little bit later on, John.\u003C\u002Fp>\u003Cp>But here here it is. We built it first. So, yes, what we've built is a bundle. In the bundle, it's an endpoint and the panel. The panel calls the endpoint.\u003C\u002Fp>\u003Cp>The endpoint talks to Twilio, comes back, returns it to the panel. So, yes, it is now a direct test custom endpoint, by virtue of this file here. Okay. Let's do that again. When you see an error in a workshop and you're like, damn.\u003C\u002Fp>\u003Cp>I hope this isn't gonna derail everything, but no. There there we go. No errors. So we're good. No errors.\u003C\u002Fp>\u003Cp>And we did see there it did a post to the Twilio token generate endpoint. It returned in 25 milliseconds with the\u003C\u002Fp>\u003Cp>Speaker 1: a a device, register device with Twilio, and say, hey, device. I'm authenticated. I am now ready to start making calls and receiving calls if you set up to do that.\u003C\u002Fp>\u003Cp>Speaker 0: Let's do this, and then we'll go back in the in up the the endpoint because that is there's no problem now for its hard coding user in the endpoint. But in the real, you should not be doing that. So we'll cover that at the end as like a let's it's not a next step. It is critical, but we will treat it as if it's a next step because I wanna I wanna get a call. I wanna get a call going.\u003C\u002Fp>\u003Cp>I'm getting impatient now, as you know. That is the person I am. Okay. So, let's now yeah. And once the so there's one other thing here, which is this device doesn't register like that.\u003C\u002Fp>\u003Cp>Instead, it emits an event when the device has been successfully registered. Sometimes that takes a moment. I think that it also pops up and, like, asks you for, you know Yes. So My hat says\u003C\u002Fp>\u003Cp>Speaker 1: why register can take a couple of seconds is because sometimes it needs to ask permissions to use your microphone, which could take you one second, 2 seconds. In Kevin's case, where he's already done like a we we've done this before\u003C\u002Fp>\u003Cp>Speaker 0: It doesn't show up. So it's\u003C\u002Fp>\u003Cp>Speaker 1: given it permission. So it's not going to do that. So while it may seem lightning quick for him and you might be like, why do we have to do this and then wait for it to finish? It's not always gonna be like that for all your users. So then what we use are these events where we say when the device has been registered, then we want to do something.\u003C\u002Fp>\u003Cp>Now what a lot of people do, especially if they're creating dial up panels, is you might have you might register a device when someone opens the dialer. Right? So that it's quicker that when they click a call, they're already registered. And then you can already, like, say that something's gone wrong. You are not authenticated to use this dialer straight away once they open it.\u003C\u002Fp>\u003Cp>Different\u003C\u002Fp>\u003Cp>Speaker 0: Can I ask you a question? Yes. The the tokens don't last very long, and we we use the token in the code when we register the device. And I don't think we use the token again. But what happens if the token expires between registering the event and actually trying to make the call?\u003C\u002Fp>\u003Cp>At what point have the handshake happened?\u003C\u002Fp>\u003Cp>Speaker 1: The handshake happens when you register the event when you register the device. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Not when you make the call.\u003C\u002Fp>\u003Cp>Speaker 1: Not when you make the\u003C\u002Fp>\u003Cp>Speaker 0: call. Oh, interesting.\u003C\u002Fp>\u003Cp>Speaker 1: Is you also get a event when your token's about to expire and when your token expires. What we recommend people do is when your token's about to expire, generate a new token. So hit that API call then start a new token get a new token.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Exactly.\u003C\u002Fp>\u003Cp>Speaker 1: Again, this limits your surface area for exploitation. So it's it may seem like a little bit of a faff, but trust me, you do not wanna be hit with a crazy Twilio bill because someone's hijacked your phone call and used it to call premium numbers that paid themselves.\u003C\u002Fp>\u003Cp>Speaker 0: That. So we're gonna make a call here. The call is going to be an object. I'm going to save it up here because we want to start the call later. We're gonna wanna handle hanging up.\u003C\u002Fp>\u003Cp>So we'll need access to that up to that object in the in the global scope. So we're going to go const, call, I think we'll call it, and we'll just again initialize that with a value of null. Now it's time to actually make the call. So we'll set the value of this ref here, into device dot and yeah. Thank you.\u003C\u002Fp>\u003Cp>But my uncertainty was creeping in there and it has a params object and the value is going to be phone number, this one here, dot value. Because it's a And it's that didn't get the raw value.\u003C\u002Fp>\u003Cp>Speaker 1: You might be like, why connect and why is it that params too? Because there are a couple of things. You can clone phone numbers, but, like, with Vince with you, you can actually phone video call rooms. So let's say people having a video room, you can literally dive in to it from here. You might want to dial another client, which isn't gonna have a phone number.\u003C\u002Fp>\u003Cp>It might have, like, a a name attached to it, like the user that we have. So, there could be a couple of things. We're using phone numbers here, but that could be a few other things, which which is why there is a bit more flexibility in I don't wanna say ambiguity, but a bit more flexibility of what could go into that params. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. This is everything we need to actually make a call. We're not done. We're not gonna get any UI that we've made a call. There's gonna be no ability to hang up.\u003C\u002Fp>\u003Cp>There's none of that, but I I think this is a moment we can actually try this out.\u003C\u002Fp>\u003Cp>Speaker 1: Alright.\u003C\u002Fp>\u003Cp>Speaker 0: That's it. You know, we've got 33 lines of code with a few lines of white space, and I think this might be all we need. So your phone number's in there.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Let's ready for the error? I am. I am\u003C\u002Fp>\u003Cp>Speaker 1: ready for an error.\u003C\u002Fp>\u003Cp>Speaker 0: There it is.\u003C\u002Fp>\u003Cp>Speaker 1: Allow. You've hit call. I am getting a phone call. I'm gonna mute my mic and just join the phone call.\u003C\u002Fp>\u003Cp>Speaker 0: Hi. Hey. Yeah. You sound you sound suitably terrible as phone calls do. I can't I can't I can't be bothered.\u003C\u002Fp>\u003Cp>I literally can't be bothered to figure out sharing your audio. So anyway, the thing is like, you should share my audio. I'm like, I can't be bothered.\u003C\u002Fp>\u003Cp>Speaker 1: I was speaking on the phone. He could hear me. Just because we're not sharing audio via the the stream, you couldn't hear me. So next time, I won't mute my microphone. But his browser is you couldn't hear me.\u003C\u002Fp>\u003Cp>So next time, I won't mute my microphone. But his browser called my phone. Hey. There was no indication we did it. There was no indication it ended.\u003C\u002Fp>\u003Cp>Speaker 0: So there there's some things we need to work out now. But we're basically sound. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: There were\u003C\u002Fp>\u003Cp>Speaker 0: some sound things.\u003C\u002Fp>\u003Cp>Speaker 1: So, Twilio just built in we have, like, these audio, like, files that, it's, like, plays a sound when you connect. It plays a sound when you disconnect. But, visually, there are no cues. Now you can customize the sounds that you that are in there to have something that's like your own UI. And, like, what we're gonna do next is you probably should change your UI so people can see visually what state the call is in.\u003C\u002Fp>\u003Cp>Speaker 0: We're at the point now where we are polishing this. When we get back to disconnecting the call, I think we'll, I'll slow back down. But for the next couple of minutes, I'm gonna speed run this because this is just little view UI, you know, things. So we do this. We currently have this div here that shows start call pick phone number.\u003C\u002Fp>\u003Cp>So I'm gonna create a new ref called show call. Sure. Let's make that false because at the beginning, there is no call being made. The call, I believe, also emits events. So call dot value is this, then we'll go ahead and say call dot value dot on.\u003C\u002Fp>\u003Cp>And there is a ring ring.\u003C\u002Fp>\u003Cp>Speaker 1: There is a Yeah. I don't know if it's answered or connected off the top of my head. I will check it out later, but it's subject to tell you it's connected, and then one to tell you when it's I think\u003C\u002Fp>\u003Cp>Speaker 0: we're gonna be lazy and we'll just do ringing and disconnected because disconnected is by either hand, I think. It's just disconnected. It has been dis\u003C\u002Fp>\u003Cp>Speaker 1: phone ended. Whether you hung up or they hung up. There is another way to, like, distinctly choose between which person hung up, but we're not gonna be using that today.\u003C\u002Fp>\u003Cp>Speaker 0: Exactly. So show call dot value becomes true. And then on disconnect disconnect, we'll set it back to force. Then in here, we'll say v if not show call, do all of this. And then, oh, just some just some chat right here.\u003C\u002Fp>\u003Cp>Loving it, but the, the potential tying this ability with other business use cases like customer engagement, etcetera. Yeah. I mean, I see, like, customer, like, outbound call centers as being, like, rich for this. We'll talk about that more more a little bit later because I have some thoughts on where this could go in the future with more time. Also, the possibility to include the verification style systems, generate a confirmation code in direct us.\u003C\u002Fp>\u003Cp>You read it out. You know, you could, send it via Twilio and then confirm it on the phone, stuff like that. Yep. Excellent. Yeah.\u003C\u002Fp>\u003Cp>I love the part with the browser called the phone. So did we. I'm I'm glad it worked.\u003C\u002Fp>\u003Cp>Speaker 1: The first time. Right?\u003C\u002Fp>\u003Cp>Speaker 0: The confidence in it. Yeah. We also need, I suppose, just a v l's here, and we'll just say call in let's do it more like this. Call ongoing. Not that.\u003C\u002Fp>\u003Cp>E. I just wanna No. I'm just\u003C\u002Fp>\u003Cp>Speaker 1: gonna say, what is that p else?\u003C\u002Fp>\u003Cp>Speaker 0: No bloody clue. The or the first result having a mad one. And then I think we'll just want another button. But this one is end call. And exactly.\u003C\u002Fp>\u003Cp>And we'll do an end call function, which I will just create, and we'll deal with it in a moment. Const end call. I don't doesn't hurt anyone.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I was it doesn't have to be, but right\u003C\u002Fp>\u003Cp>Speaker 0: in there. Yeah. So that's the UI. It'll call ongoing end call, and then there's an end call button. It will do nothing.\u003C\u002Fp>\u003Cp>We we do actually need to tie this up to ever get back to that first state by pressing the button. And I think called dot value dot disconnect, the thing where we have a connect, there is also a disconnect. And then call, show call dot value force. I think it's handled by this, but, whatever. I wanted to work first time, so we're just gonna we're just gonna keep throwing some redundancy in there.\u003C\u002Fp>\u003Cp>I think that's groovy. So extensions reloaded. Let's try this out. Nathaniel, call. Nice.\u003C\u002Fp>\u003Cp>Speaker 1: Good. Call's coming in.\u003C\u002Fp>\u003Cp>Speaker 0: Answer.\u003C\u002Fp>\u003Cp>Speaker 1: Hello? Testing. Testing. 123.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I hear you twice through through the browser and through our streaming system, and, it's it's twice too much, to be honest. Bye. Oh, forget that. It did hang up the call.\u003C\u002Fp>\u003Cp>Stereo. Yeah. It did it did hang up the call.\u003C\u002Fp>\u003Cp>Speaker 1: Dun dun dun. Oh.\u003C\u002Fp>\u003Cp>Speaker 0: I don't know what this is. I did hang up the call. Just before we get to this, because I think this might be in the voice SDK. But I'm not convinced. Get on proxy.\u003C\u002Fp>\u003Cp>Log is read only and not configurable. Now that means logs being used somewhere inside of the call dot disconnect. Alright. Call dot underscore disconnect. Alright.\u003C\u002Fp>\u003Cp>We are yeah. It's here. It's somewhere in here. We are going into the depths of the of the SDK, the voice SDK, and I have a better idea. I have a better idea on how to handle this.\u003C\u002Fp>\u003Cp>Call.value.onerror. Yeah. Damn. Does that even work?\u003C\u002Fp>\u003Cp>Speaker 1: I I do not know. I I I am desperately looking up the voice SDK to see\u003C\u002Fp>\u003Cp>Speaker 0: what Honestly honestly, my guy, it's always the end of our it's always the end of our Workday. Yeah. It's a frantically look up anything. I'm swallowing the error. It's not an error if you if you swallow it and don't display it to the user.\u003C\u002Fp>\u003Cp>Speaker 1: Is it. You're you're absolutely right. There is an error. But like, sorry. There is, on error event.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, I just I'd made a guess there. But I'm gonna call you again.\u003C\u002Fp>\u003Cp>Speaker 1: Hello? How are you? Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I I don't care how you are, mate. Sorry. Bye. Okay.\u003C\u002Fp>\u003Cp>Interesting. We'll pause on that. I'm gonna I'm gonna refresh and call you one more time, but I'm gonna hang up because we actually haven't tested that side. Sorry. You're gonna hang up the other way.\u003C\u002Fp>\u003Cp>I've I've been the one to hit end call.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Interesting. Look. Works great. Call ended. Question.\u003C\u002Fp>\u003Cp>It's when Question. I disconnect.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Question. You, when you disconnect\u003C\u002Fp>\u003Cp>Speaker 0: works. Yeah. Here.\u003C\u002Fp>\u003Cp>Speaker 1: So you hit disconnect. Do wait. Hold on. I wanna quickly check something because, you've got on disc. That's on disconnect.\u003C\u002Fp>\u003Cp>Speaker 0: I'm\u003C\u002Fp>\u003Cp>Speaker 1: not sure, actually. Mhmm. I'm not sure.\u003C\u002Fp>\u003Cp>Speaker 0: Neither am I. Neither am I. Refresh, reboot, pick up your changes. The changes are happening. Look.\u003C\u002Fp>\u003Cp>When I hit save, just watch it. Extensions reloaded and other UI changes were displaying. So I'm confident that happy to call this a a success, to be honest. Now there are a few other things we need to do. We need to like, I I think we must tighten this up a little bit because it's currently a little too open for everyone.\u003C\u002Fp>\u003Cp>Because right now do here\u003C\u002Fp>\u003Cp>Speaker 1: is anyone who gets access to the URL gets a token that allows them to phone anybody they want and charge it to you.\u003C\u002Fp>\u003Cp>Speaker 0: Agree. So, Wreck, I want to say it's accountability. But just in case it isn't, I'm gonna just oh, do you wanna know the way I'm gonna do this is I'm gonna go, object dot keys rec, and we can work from there. Oh, was that too hasty? Oh, was that too hasty?\u003C\u002Fp>\u003Cp>Is it building? Oh, but it's still building. It's it's packaging the whole Twilio SDK t k and and that's why it's just taking a hot minute there. So we're gonna do this. What's the best way of doing this?\u003C\u002Fp>\u003Cp>I think what I'll do is in the panel is I'll just pause on I'll just comment all of this out briefly. I don't actually wanna be making calls. Interesting there. Refresh and call. And over in this terminal here, great.\u003C\u002Fp>\u003Cp>We have\u003C\u002Fp>\u003Cp>Speaker 1: You have accountability.\u003C\u002Fp>\u003Cp>Speaker 0: Paul, last in the chat, obviously, you can lock this down in direct, but imagine inside of Twilio, there are also options to lock down your numbers. Zip rank as well. No?\u003C\u002Fp>\u003Cp>Speaker 1: Lockdown ask that question. I am locked down the\u003C\u002Fp>\u003Cp>Speaker 0: Obviously, you can lock this down in direct test. We might need to expand on what that means. But I imagine inside of Twilio, there are options to lock down your numbers or SIP trunk as well.\u003C\u002Fp>\u003Cp>Speaker 1: You're not I mean, these on your numbers.\u003C\u002Fp>\u003Cp>Speaker 0: Tokens are the way to do that. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: These tokens are the lock. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: So you wanna make sure you're securing the generation of them. So And, accountability.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. The generation of these tokens is essentially how they are locked, and only someone who essentially only someone with your credentials can create tokens, which means only someone\u003C\u002Fp>\u003Cp>Speaker 0: with your\u003C\u002Fp>\u003Cp>Speaker 1: credentials can create calls.\u003C\u002Fp>\u003Cp>Speaker 0: I'm gonna pause you for clarification. Lockdown calling premium numbers or long distance\u003C\u002Fp>\u003Cp>Speaker 1: Yes. Oh, yes. Yes. Yes. Yes.\u003C\u002Fp>\u003Cp>Yes. Yes. Yes. You can. I can actually show you that in the browser.\u003C\u002Fp>\u003Cp>So, there is\u003C\u002Fp>\u003Cp>Speaker 0: I'll I'll pop over to you while I just do this. There you go. See yours now.\u003C\u002Fp>\u003Cp>Speaker 1: No worries. So you have voice geographic permissions. So eventually, let's go back to that. Voice geographic permissions. And give it a second.\u003C\u002Fp>\u003Cp>It will load. And so you've got, programmable voice and SIP trunking as well. And you've got, they give you also an average cost of how much things will be. You've got low risk and high risk. This is a connection.\u003C\u002Fp>\u003Cp>This is not just countries, so it's not entire countries that are, high risk. Although it is could be a large proportion of countries, large percentage of that country. It is specifically network carriers in specific countries that have been known to have a higher risk of toll fraud, so you can block them. So I can, like, block the whole of North America. I can block specific country and so on.\u003C\u002Fp>\u003Cp>And then you can also pop in a number to check if it's got permissions at all. So that's another way to just, like, lock it down, and this is great because it's on an account level. So your whole account can be locked down.\u003C\u002Fp>\u003Cp>Speaker 0: All good? Yep. I did I did the changes. So at the top of the at the top of the, route handler, we inside of this, request, this rack, you get this object called accountability. Accountability contains the following properties, user role, whether or not they're an admin, whether or not they have app access, IP, user agent origin, and permissions.\u003C\u002Fp>\u003Cp>So you can further go on and expand, but all we care about are you logged in, and authenticated with director? So so that's all this does. It says, hey. If you don't have a user ID, go away. Now I think about it, there might be more you wanna do here because you could just jam an accountability object and user.\u003C\u002Fp>\u003Cp>So there's probably some other levels of, of restriction you wanna do here. For example, checking the user's role, which then will return they don't have a role because they're not a valid user. But I think this is I think this is okay. And then here in identity being\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. User, it can be\u003C\u002Fp>\u003Cp>Speaker 0: There you go. Rec.accountability.com. So which is the ID.\u003C\u002Fp>\u003Cp>Speaker 1: Name of the user that we're logged in on in the system?\u003C\u002Fp>\u003Cp>Speaker 0: No. It's the UUID.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. UUID. Cool.\u003C\u002Fp>\u003Cp>Speaker 0: It's the UUID of the user. I mean, if if you want to see, we can just console log.\u003C\u002Fp>\u003Cp>Speaker 1: Well, because I was gonna show how it appears in the Twilio logs when a call\u003C\u002Fp>\u003Cp>Speaker 0: comes in. Sure. Give that a moment just to reload one more time.\u003C\u002Fp>\u003Cp>Speaker 1: No worries.\u003C\u002Fp>\u003Cp>Speaker 0: Again, it's bundling that whole Twilio, helper library, and so it just takes a moment to build. We'll hit that.\u003C\u002Fp>\u003Cp>Speaker 1: Did you uncomment the call?\u003C\u002Fp>\u003Cp>Speaker 0: No.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: That would help.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. We'll make this a short call, just so we can show you the logs. So now only authenticated users on directors can use this application. They he's gonna call me. I'm gonna answer.\u003C\u002Fp>\u003Cp>I'm gonna keep it on for one second. He's gonna end the call. My phone call ends. And then I'm just gonna refresh my logs so that we can see the latest call that happened, which was 838. Yep.\u003C\u002Fp>\u003Cp>Which was this one, and we can see here it's complete from a client, and it was from client. And then this was the UUID that he talked about. I can actually show you a previous call. Remember, it used to be hard coded to user. If I get to this one over here, we can see it was client user before, and now it's client and then the user ID.\u003C\u002Fp>\u003Cp>Speaker 0: That's cool. Cool. So, I mean, I I think and I thought about it more actually. I may update the docs. I've just thought about it in this moment.\u003C\u002Fp>\u003Cp>The user actually this doesn't prove the user is authenticated. It proves that there is an accountability object with a user I with a user value that is not foresee. That's what I check against. But what you may wanna do is expand this further, use the permission, service, check they have certain permissions, which they either will or won't have. That is more direct us, that's locked into direct us and it will say no if they don't exist.\u003C\u002Fp>\u003Cp>So just a thought there. What we did was a lightweight, like, check, but it is not foolproof. And I've just realized in this moment that that's the case. But that's fine for this. We've acknowledged it.\u003C\u002Fp>\u003Cp>We've shared that with you, and we've given you an approach. Now I think that's it for, like, what what we can do here. But what what more could we do? Firstly, we need to handle that rejection state better when I hung up. Can't be bothered now, but just I'll figure it out.\u003C\u002Fp>\u003Cp>It'll find its way into the blog post that'll accompany this. One thing that's really cool if I just come back to my screen share is these are real users. Right? And they're queried using the composables that are exposed to view to to this view component here, this extension. But you have access to all the collections.\u003C\u002Fp>\u003Cp>So let's say, for example, you have, customer calls or customer notes, customer note whatever, a timeline of events and they exist in your project, you could select the user, get maybe their latest notes, then call them. So now you have context. Additionally, insights dashboards have this concept of a global relational variable. So what this will do is user, is you select directors users in here. Let's say we want first name and last name in here.\u003C\u002Fp>\u003Cp>Let's not have them overlapping each other. You could select a user from here, and this now contains the UUID of that user or the object of that user. I can't remember. But you could feed that into this panel. You could feed it into the panel that gets customer information.\u003C\u002Fp>\u003Cp>So you can have multiple parts of your dashboard all changing because you selected the user once in this global relational value panel. So there's so much. There's so so much you can do with this. And I'm just really excited for more people to see directors insights as a really valid app builder surface more than just, more than just insights and BI. So with a couple more minutes to to go, if anyone in the question if anyone in the chat has questions, please do chatter and let us know.\u003C\u002Fp>\u003Cp>Nathaniel, do you have any any closing thoughts while we wait for those?\u003C\u002Fp>\u003Cp>Speaker 1: No questions. But the thing is, like, I'm I'm just really trying to think about, like, other ways. So, like, there's obviously, like, building your own mini contact center. Because contact centers are, like, big and expensive, and actually sometimes what you need is you don't want this massive, like, stood up, like, huge at scale contact center. You just wanna be able to have the ability to contact your users just really, really quickly, and you could start building applications like this.\u003C\u002Fp>\u003Cp>You can then also because you're using Twilio phone calls, you can access, like, a lot of other Twilio functionality. Like, we've got, like, voice intelligence transcriptions where, like, you have a phone call. At the end, you can run AI operators on it to just be like, yo, this person, like, that person asked for a manager, or the person was happy. We can just do sentiment analysis and have that, like, just pop straight into a panel, for example, because you are just connected to that ecosystem as well. So, there is way more than just the calling calls that you can do, but, it all starts with, like, ring ring.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And I suppose you can query APIs for that data as well from within here as we have just demonstrated, and we spoke about with the weather API, you know, example way up top. You can just call off to arbitrary third party APIs and bring data in via custom endpoints. So, yeah, there were no other questions in the chat, so I'm pretty confident at this point saying I think we are off the clock, which is wonderful. This has been awesome.\u003C\u002Fp>\u003Cp>Thank you so much for joining me for this. I had a really fun time putting this together and delivering it with you.\u003C\u002Fp>\u003Cp>Speaker 1: Always enjoy hanging out with you. Next time we go on a roller coaster journey, we should write another workshop as well.\u003C\u002Fp>\u003Cp>Speaker 0: Which is what happened. Sorry. That that that's what happened. We we wrote this basically while while going to a theme park together.\u003C\u002Fp>\u003Cp>Speaker 1: We had a\u003C\u002Fp>\u003Cp>Speaker 0: long, long, long, long drive, but something could came of it. Weekends. Well, on that note, thank you so much everyone for joining in. We will see you somewhere. Bye.\u003C\u002Fp>\u003Cp>Speaker 1: Bye.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, wait. Where can people find you? Oh, online at Twilio dot.\u003C\u002Fp>\u003Cp>Speaker 1: They can email me at nocennwall@twillio.com. I do check my emails, and, yeah, just say hi.\u003C\u002Fp>\u003Cp>Speaker 0: I was ready for you to that. I don't check my emails, but you can send me an email there. Alright. Alright. We're done.\u003C\u002Fp>\u003Cp>We're done. We're done. Bye.\u003C\u002Fp>","Hello. Hello. Hello. How are you doing? It's good to hear you. Great, mate. How are you? Yeah. Yeah. Yeah. I'm doing alright. I'm doing alright. It's wild to me that we have been friends in the same kind of job family for so long, and I don't think we've ever run a workshop before. I know. I mean, I think it's been a missed opportunity for all the wonderful people that we haven't been able to hang out. Genuinely, it's crazy because we went to university together, for those of you who maybe have never heard. So we we've been known each other for a long time, and our careers have, like, had these moments where they almost look like they're about to cross. And I feel like this might be one of the first professional cross that you've said. I'm really excited. Yeah. So I thought we'd open by just introducing ourselves, introducing what this event is, introducing what we're gonna be doing, how it's gonna work, and then we'll launch straight in and make a mess of it for 2 hours. How's that sound? Sounds good. Sounds good. I could start by introducing you first. Hi, folks. My name is Nathaniel Okenwa. As Kevin would know, I talk a lot and I write code, so my friends call me Chatterbox Coder. That's where you can find me on all the socials. And I work for a company called Twilio, which is kind of what we're gonna be talking about today. Twilio, for those of you who don't know, it does many things. But here's the TLDR. We are telecommunications APIs that help you build amazing communications into your applications. That sounds like a lot of words, but you've definitely used Twilio at some point. If you've ever received a text message from a company, a phone call from a company, maybe a 2 f a text, lots of emails when it comes to Black Friday, even all sorts of communications, chances are they may have been using Twilio under the hood. Some of your biggest some of your biggest brands and favorite brands that people use use Twilio under the hood. But we also do so much more. So if you do wanna find out about some of the more advanced use cases, feel free to chat to me because the well of Twilio can be quite bottomless. Yeah. Definitely one way to put it. Yeah. If you're here, you might be coming from the Twilio world and not have heard of Directus before. So I will also tell you a bit about Directus. Directus is a back end, basically, that you can use to build wicked applications. You connect it to a new or existing database, and any number of asset storage, storages, storage. I don't know if it's like the word sheep where it's the same in plural. And you immediately get developer tooling, including APIs, a real time interface, authentication, and a user management system, and this really lovely web application with which to interact with that database, which you can easily hand to people who aren't developers. So you don't need to build APIs. You don't need to build kind of these admin panels, admin panel back ends. Really cool tool. And today, we get to converge the 2, which I'm really, really excited about. So, the project today, is oh, actually, no. A little bit more preamble. This event's happening as part of Leap Week 3. This is our week of announcements. On Monday, we did a keynote where we announced directors 11, which is coming out this week as a release candidate where we announce new shows for Directus TV. This platform you're watching this video in right now has, now 35 shows worth of content. You can go and potter around and find some cool awesome content too. Like, I must say. Thank thank you very much. Thank you very much for that. I'll pay you under the table later. And also, and and a bunch of other things, as well. This is one of the workshops that's happening this week. So this one right here is this Twilio workshop. After this, like, an hour and a quarter after the end of this event is a 100 apps and a 100 hours live where Bryant and some of our colleagues are gonna build an app in 60 minutes. I don't know what he's gonna build yet. I'm not sure he knows yet, so that would be chaotic as it was last time. Tomorrow, we're doing a workshop with Deepgram, which is a voice AI company. We'll be building a cool project there and then a community social networking thing on Friday using a platform that doesn't suck. So you can come and have a chat with other people who use or know about or are interested in direct us, in hopefully not too of a not too much of an awkward format. But we're getting ahead of ourselves. We're here for this Twilio workshop. And what we will be building today is a panel extension for Directus Insights. So Directus Insights is this dashboard builder tool here that we have. And in a dashboard, you have any number of these panels which can interact with the data in your database. But these panels aren't just for reading data. You can actually put components within them that are interactive. So you can add, like, forms and buttons, and you could just run arbitrary codes in them if you want. And that's what we're gonna be exploiting today. So so what I'm hearing, right, because I have not played around with insights. So, insights essentially just give you the ability to create components that are powered by your data. So there are some obviously components that are really well built. And then see, it doesn't actually have to just be reading data because the thing that I'm really excited about is the fact that the stuff we're gonna do is going to read data, but it's gonna interact with it and cause other things to happen as a result. And that's something which I think is very, very powerful, especially when it starts to come to, like, workflows and building maybe internal tooling as well for people. This could be really, really useful. Ding ding ding. So we've not spoken much about director, so you're kind of coming in it pretty fresh as well. So please keep asking questions. But, yes, you said a word you said a pair of words there, which I think is really interesting, which is internal apps. And I actually think that is the kind of hidden power of director's insights. While all of the panels that ship out the box are very much about, you know, building graphs and charts and reading data and analyzing it, it doesn't it's not just that. And you can also obviously build panel extensions and distribute them through the marketplace. So today, we're gonna build a panel that will have a drop down and show you the users inside of your directors project. With other phone number to those users. And so there'll be a phone number attached to them. And then you press a button, and we'll use the please forgive me if I get it around the Twilio voice SDK. Yes. That's what it's called. Fantastic. And we're gonna call that that person from the browser. So the browser will connect to a phone number and do a two way call. Cool. Yeah. I'm really excited because this is something which I think lots of people oftentimes, we end up having tools in different places. Right? So for example, especially when it comes to telecommunications and telephony, we always, like, separate so that you have your email account or a phone, like, application or something separate to all the places you have you there. And, yeah, I'm not saying that's not bad, but when you start wanting to be efficient or maybe have some more smart and cool interactions, bringing those 2 together gets really powerful. And I think, like, what I could see here is and we could talk about, like, potential use cases. Look, we're not trying to sell you a thing, but kind of just tell you practically some of the ways in which you could be using these these tools. And I think like this, it could be a good way for you to, for example, like, quickly integrate communications without necessarily exposing PII. Like, you can have it so the person using the tool never sees a phone number, but it's still able to phone them. Some of your favorite ride sharing apps kind of use a similar technology behind this if your driver ever calls you to be like, hey. I can't find you. But, anyway, I'll stop talking. Let's get on. No. No. Please do. A couple of other just bits of context. So we'll refer to the director's docs, obviously, a bunch today. We'll take a look at some of the extension docs. As part of that, inside of these docs, there are a couple of existing Twilio guides, and one additional non Twilio guide. And I wanna talk about them before we crack on so you kinda understand the approach which we are going to use today. First of all, there is this guide here called use custom endpoints to create an authenticated API proxy, which is a lot of words. So to break that down, endpoints, custom endpoints are one of Directus' extension types, and they allow you to just create kind of what they sound like, arbitrary endpoints that you can hit. We expose an express router, so it works just like that. You know, you set up route handlers, and then you can hit them. But within the context of directives, you can start doing stuff like checking if the user's authenticated in in directors or checking their permission sets and so on and so forth. So, this is an example where we actually just expose the full Twilio. Let's find it here, where we expose let me find it. Twilio host. Yeah. Where we expose just a root URL. And then what we do is we check whether you are authenticated, and that's the key part right here, which means you can't just hit this endpoint from anywhere. Right? You have to do it either with your Directus API token or cookie, or from within Directus itself. So that's 1. The second one is, another Twilio integration. This uses our automation tool, direct us automate. They're called flows each kind of, workflow. And this one here allows you to send SMS notifications. That's kinda interesting. Can you do for these? Because, like, obviously, with SMS, when I'm talking to devs who are building into their applications, they're often either a couple of things. Either they wanna send a message, like, when a specific action occurs, Maybe they wanna do a batch, like, at the end of the day, or there's, like, a campaign going out, especially when you start getting to marketing and stuff like that. And the same with email, because we we also you we have email API. So, like, how can people trigger those, those things? Those flows? Great question. Five ways. Event hooks. So something happens in your director's project often in one of your collections. A collection is a database with, like, additional director's metadata. So when I say collection, you can think table, but it is not just a table. Right? When something happens in your database, you can immediately fire off an automation. So this can be, a new user is created, a new file is uploaded, or any of your tables are have CRUD operations, executed against them. They can run as blocking, as blocking triggers, which means the whole flow has to see its way to the end, and then the database transaction will execute so it, like, intercepts it. And as a result, you can actually fail out. So, we have an example somewhere in our docs using a verification API where if you fail the verification, we actually just block you from signing up entirely. It just never commits to the database. Ah, it's really cool. On the other hand, it is cool because you can also manipulate data in the middle. Maybe you enrich it or stuff like that. That's really, really good. And then and then you have actions, which happen after data after data has, been committed or after a transaction has been committed, then it will run. So that's event hooks. Then we have webhooks, so just inbound HTTP requests. We have a schedule. So, you know, you set up an interval using, the 6 point Chrome job syntax, and then we'll run the flow, which is how you could then batch. Or, 2 more, actually. There's another flow so you can compartmentalize functionality and trigger other flows from core from, like, your your controller flow and path data in and backup and stuff like that. And then finally, manual. What manual does is in your data views where you've got your list of all your items in a collection or you're in an individual item in the editor, there's a button you can press that will pass in the IDs of the items that you've either checked in the collection or the page, and then send that into the payload as well. Manual flow triggers also have confirmation dialogues so you can pop up and collect a bit more information. There's the button there on the right. And it can you can pop up a box, ask for arbitrary information, and then trigger the flow, which I think is a very, like I'm looking at thinking comms. So they're very communication type, you know, option here. You can go write a message and hit go, for example. So they're the triggers. There's a bunch of inbuilt operations. You can also build custom operations, but that allows you to interact with your database, make it external web requests, just write some arbitrary JavaScript that can manipulate data in a little more of a clever way. But, yeah, that's something that's really cool about this tool. Yeah. No. Absolutely. And a thing which I'm kind of noticing is the number of different ways, the flexibility that is given to you to try and to to trigger these things in different ways allows you to put into put the automation into different parts of your application. Especially, I'm loving the filtering functionality. I can already spin off a couple of great reasons why I'd use that, especially when you start to want to reduce computational load or con reduce things from blocking our operations from going through or vice versa when you actually do wanna filter stuff. So that's useful. I'm gonna play around with that next time I get a chance. So I wanna jump in. There's one more guide I wanna show because this guide is going to basically be the north star for our approach today. And it is this, which is filled a little bit with, but it's using external weather data in a custom panel extension. And you might be thinking, I don't really see the translation between this and using Twilio in a in a panel. So before I explain this, maybe we take a moment to explain the Twilio flow in this all the way from generating I think it'll come in quite nicely. I can share your screen or you can just talk. It's completely up to you. Well, I can start by just talking, and then we can, we can get even further. So we start with the Twibio voice API. So Twilio has a bunch of different APIs, and we have one that we focus on programmable voice. I would say one is more of a collection because even with invoice, there's a lot of diversity depending on what you want to do. In today's example, kind of like the external weather data, we essentially want to have a component in a web page, and this component is going to be able to make phone calls out to the users that are stored in the director's database. Right. So in the director's back end. So what we're going to need to do is we need a couple of things. We need a browser component or a component that that lives in a browser. So essentially a browser that is able to make phone calls. Your browser, unfortunately, doesn't have telephone APIs. So what your browser does is your browser can connect to Twilio, Twilio's servers. And then Twilio then handles and creates telephony using, old school telephony that, like, is really, really boring slash complex or really, really interesting depending on how what floats your boat, and then does all of that in the background. But what we do is we expose an SDK which you can build into your front end applications. Now there is one extra step which maybe adds a little bit of complexity, but I just wanna talk a little bit about that, which is just a bit of authentication and security. Because we can't just hand anyone the keys to make phone calls from any number in the world. Right? So when you have a Twilio account, you have a phone number. And when your browser connects it with you, what happens first is there is an exchange of a token. A token must be generated, it's what we call it, and that browser must use that token to connect to Twilio. Now that token gives it a couple things. It first says, hi. I am Nathaniel's computer, and I am connecting to Twilio, and I'd like to be able to make phone calls. Also, it says I have a phone number identity. So, like, a a a phone number which is attached to my identity. So when I do make phone calls, I will come across as if this phone call is coming from said telephone number. And these things happen in the background. Now the SDK has a lot of, flexibility, so we start off with a few basic methods which you can use. And then if you want to build a really, really custom interface, there are a lot more events that we expose. But if you are looking to get started with Twilio voice, I would recommend heading over to the Twilio docs, which I am showing on my screen. They often take you through starting your first phone call. This is from a server. You may want to start your first phone phone call, but then we also have these client side SDKs, and we talk you through how you use the client side SDKs. We have JavaScript SDKs, iOS, Android, and we even have React Native SDKs. I often like to say it's good to start with a quick start and then work backwards because the quick start builds all of that functionality, and then you can customize it as you are. But then there is also the reference if you want to dig deeper into all of the methods and the things going on behind the scenes. Is there anything I've left out? I know we've talked about this before. No. You did mention the thing I hoped you would mention so I can explain why whether the data and Twilio are related. Before I jump in, actually, a reminder for those watching you, we've got a chat here. I'm watching it. So by all means, ask questions, thoughts, concerns, grievances even, and we'll address them as we go. But let's come back to my screen. Right. Why does this matter? Extensions and directives live in 1 of 2 places. They live in the data studio, which is the web app, or they live in the data engine, which is the back end, an API side extension, you could call it. The browser, because of just security in the browser and the way that we lock that down, can't always confidently make an external web request. Right? It can't go off to Twilio and say, hey. Go generate an access token for me. In this example, the same way it can't go out to a third party weather API confident. Like, if you control both sides and you configure security on both sides, you'll be fine. But you often don't control the the vendor. You don't control Twilio. You don't control the weather API. So what do you do with this? Well, you use an endpoint. Use an endpoint, which is an API extension. Use an endpoint first to actually make those external requests do what it needs. And then your front end extension, the panel in this case, and in this case in the tutorial, then calls an internal endpoint because it's that's what it is now. It's now gonna be like \u002F Twilio token, that my direct test project slash Twilio token. So it's now an internal API. So you you act you treat it like a proxy, which is actually not dissimilar to the post I showed earlier. To bring these together and make sure that you always have both of them and you don't have to deploy them separately, we have a concept called a bundle. So first, you create a bundle extension, which is an empty shell, then you add an endpoint, then you add a panel. And when you install the bundle, you get both of them together as as is required. So that's gonna be more hearing you right. Just as a mental model, what I'm hearing is in this bundle, if this bundle was the application that fits into the panel, you kind of have the component, which would be the front end logic and then the endpoints can stand in for like a like almost like your server side. I know this is a weird way to think about it, but ways that you can query data and interact with other applications. Yes and no. The only thing I'll change there is query data because panel extensions, for example, can query collections in directors without needing a a server. So if it's within the bounds of your director's project and the services we provide, in fact, we have them the the the they are called Query external data. So data from outside. Correct. Correct. Because, for example, your front end, your front end, your app extensions is what we call them, your front end extensions, have immediate access to all of your data because you're in this authenticated, you know, box that you can work within. But, yes, exactly. So the reason I wanted to show this, this weather panel thing is because that's basically we're gonna follow a very similar approach here. We're gonna create a bundle. We're gonna create an endpoint which will allow us to generate a token, then our panel will call that to grab the token and then use the voice SDK. So we've been talking for probably about minutes. I'd love to jump in. Now the format this is going to take, we decided just because, of of of the fiddliness of certain parts of this, is we are gonna go nice and slow, but we're going to present. We're gonna build it together. You can watch, and this will be available on demand if you wanna play along. This will also get turned into a blog post, and hello there is on demand. This will also turn into a blog post you can follow if reading, you know, technical material is more your thing. So if you're watching along, I I would say don't try and play along necessarily, but, take advantage of the fact that chat is here because we're we're here to chat to you. Right? Otherwise, this would just be a just a video, but this is a live event for a reason. Before we jump in, is there anything else you wanna add? Nothing comes to mind. Let's get going. Let's get going. So Why do we do this? Why don't we have you start with, like, spinning up the environment that we're gonna use, getting us set up, so that we are ready to start building the telephony parts? And then I can step in. I can focus on building the Twilio parts. I know we we've we will switch between them and then connect those together and then do a little test test as well. That sounds good. I actually had a little bit of a rundown that structure which looks slightly different. We'll follow yours, but as a result, this might end up being slightly more chaotic, but I like that. I like that format a little bit more. The the gremlins have just returned home, so we'll see what happens in the next 10 minutes. So can you hear that? You can hear that. Yeah. Oh, now I can. Now I can. Yeah. Now you can. So here I have a brand new completely empty code editor. I am running Docker in the background. We'll be running Directus locally with Docker. So if you're following along, you'll need that. And what we're gonna do first is we're going to create a Docker composed dot yml file. And I do just happen to have here sequel I do happen to just have a light one prewritten because, you don't wanna watch me write this. Go grab the latest version of Directus, expose it on port 8255, map some of the internal volumes that Directus uses to local directories. So when we first run this, it will create a database in the uploads and an extensions folder. We need a key and secret, which you should replace with a random value. To me, replace with random value is random enough for today. The initial admin email and password, you, of course, can change that. We're gonna just use SQLite because I don't wanna we don't need anything more heavy, and so that will, pop the file inside of this, data file here. WebSockets are enabled, not that I think we're gonna use them today, but this exposes a WebSocket and GraphQL subscription interface for, subscribing to updates and directors. This one line here is not in our quick start, in our docs, and it just will improve the developer experience of building extensions. Whenever we'll we'll set it running so whenever we save it or rebuild, whenever it rebuilds, it'll auto upload indirectus. So with that done, we're gonna go ahead and, just taking a quick look at the chat. Hello, Scott. And that's c collection that logs different tokens for Twilio or other APIs to variable use token by another key. Then you have Internet access to modify tokens for phone numbers or something. This is to always be missing in Directus, and you end up doing this outside Directus UI. Hold that thought for some short period of time. We know. But today, we'll use environment variables here in, here in the Docker Compose file. But we're not gonna that's not gonna be the approach today in the interest of time. And then Docker Compose. That should just do its thing, he says. There we go. Running all the first time setup, and then it will run-in just a moment at 8055. So local host 8055. There is our brand new director's project. That was so quick. Yeah. I like it. It's pretty sick. And this is also full fact directives. It's not like a shitter version you run locally. Like, it is the full thing when you use direct to this is what you use. The only caveat here is SQLite does not have geospatial plugins included out the box. So if you wanna use the mapping features, you'll wanna use just a different docker compose that's in our docs. It just takes a little longer to bootstrap, and it it wasn't necessary for today. Okay. Right now, there are a bunch of directors collections that have been created out of the box. Directors doesn't alter your tables in order to run. Instead, we create this, I don't know, like 20 ish tables or prepended with directus underscore. That means if you wanna get rid of directus later, you can just delete the application, delete these tables, and it's like we were never there. But this handles all of the UI, all of the settings, all of the configuration, they all live here. And the one thing I'm gonna do, because you can extend these, you can't delete these default fields, but you can add new ones, is for all the things that are here, avatar, email, title, you know, all of these, there isn't one for a phone number. So I am just gonna add phone because I think that's gonna be useful for later. Cool. I, out the box, have this one user here, but I think it would be So quick question. So when you added phone, you you did it as a string. Just question, can you do any sort of validation from there Yes. I can. On that input? Yeah. You can indeed. Over here, edit field validation. Nice. Nice. And and, validation, what kind of validation do you use? What other rules? What is it? Fantastic question. I think you could just throw in a red x to be honest. Yeah. It matches. You could But also, it's got a bunch of other ones. Nice. Nice. That's awesome. Yeah. Yeah. Yeah. It's pretty cool. And you can do, like, you know, logic you can do grouping. Right? And or and stuff like that. We'll leave it, and we'll just trust ourselves to put phone numbers in. Right? Don't do that in the real. I worked at another telephony company that offers APIs in the past. And you and I both know, no one ever knows how to put a phone number in correctly, and it's a huge pain in the ass. So enforced validation. Because many different places, actually regionally write their phone numbers differently. So the way Americans write their phone numbers, especially compared to Europeans, Even, like, recently, we I had been I had built an app. Go to Singapore. We're testing the app. The app fails because Singaporean numbers just have way more digits than we had accounted for. So Interesting. Make sure you, like, keep it flexible for all of these inputs. But anyway, phone number. Give me a phone number in whatever the format Twilio likes me. I think Twilio likes the plus. Plus? 44 Yeah. 79 4757 41 48. Why are you giving those me two numbers at a time? Anyway, whatever. Here. There. When we put you in. This is a user now in our in our project. If I provided the email and the password, you would be able to, you know, log and enable the app access. You would be able to log in here. The password is hashed when you save it, but you could you could just pop it in here. Yeah. Yeah. See? I see it in the chat here. I have to add a one in brackets. Like, it's a huge, honestly. Also, Americans in particular, that one in the brackets and sometimes you put other numbers in the brackets too, Okay. Absolutely. Archaic. Anyway, it's it's like as it's as bad as them in the month before the day and the date. Let's I'm not even gonna Alright. But but seriously, though, there is international standards, ISO. I forget what the rest of the number is. But there is a Is it the E164 thing? I believe so. Yes. There is an international standard for phone yes. E164. That's what I meant. So you know, ISO. I don't know why it where that came from in my brain. But, you should check this out. And what you can often do, there are lots of tools and one that is actually sponsored by Twilio where they can, change inputs to fit the format. So it's always international plus international code, then the digits with no spaces. But, interestingly, other telephony API company matches e164, but doesn't put the plus in front of it. So I don't think it's technically to standard, but it whatever. Twilio needs the plus. We've put the plus in. We we Twilio doesn't need the plus. Like, we will pop in the plus. Interesting. But but the the important thing that I recommend to people is that you use that format when you have it stored because it's then uniform across every single phone. Absolutely. I wanna do is have some people have that ability and some people don't. So whichever way you do it, store it in your in your, in your, database, in your back end as, like, just one format, and then have logic that separates it out and changes what it looks like to users based on regionalization. And not to derail us too much here, but Twilio does provide an API which formats phone numbers. I think it's called insights or lookup or something. Lookup. Lookup like the basic tier or whatever whatever will format the number for you. So you could introduce that as either a filter or an action event based trigger, and you could format it before it ever gets to your database. You rely on, you know, the user to get it somewhat correct in order to know. You know, you drop the country code entirely, for example, and sometimes it won't know which country is it. But in any case, let's I feel I I feel our time slipping away. So we have set up a project. We've added this extra field. I've added you in here. You'll be the guinea pig who gets the calls. Next in my little list was to add a bunch of environment variables and handle, like, all all of the Twilio configurations so we can focus on extension building. But I'm happy to get to the point where we first need a Twilio environment variable to do that. So it's up to you. What do you wanna do? Why don't we jump to my screen and start from a console? I'm gonna start from the dashboard. Like, 5 times zoomed in, like 5 zoom in points, please. Would you like me to zoom in more? No. That's good. That's solid. Cool. Alright. Let's go. So the first thing we're gonna need, because we're going to be making phone calls, is we're going to need a phone number. So we're gonna come over here in our dashboard, and we're gonna find our active numbers. Now I actually went in and bought a phone number earlier. And the reason I did this is because, phone regulations and some some of you maybe who've used Twilio in the past, Buying a phone number can be very, very quick and simple. Simple. However, there are regulations that come in from countries and, governments, and it's really good because it helps protect us from spam, which means you often have to upload, either ID or maybe address an address and other sorts of identification to say that you are who you say you are. So I had to do this. It literally I uploaded my ID with this number, and within half an hour, it was done. I will say I have a at Twilio dot com email so that expedites it. But within 48 hours, we usually get 1 get it done for you. And you can still start building and sending messages to the phone number that was verified with your account. So, for example, when you create an account, you have a 2 f a number. You can start messaging that 2 f a number, but then start messaging all users. You then need to have that extra level of, ID and regulation com regulatory compliance. Okay. So I'm based in the UK, so I think we should just use a UK phone number. Works for me. So now I have a UK phone number, and this is the first, I number that I'm going to hold on and keep into a safe place. So now I own a phone number. Now the thing is, for us Could I put could I pause you? Yes. Throw me the phone number in our little joint chat. I wanna show what I'm gonna do with it because then I don't need to show again what we're gonna do with them. If it's okay with you, give me 2 ticks. Steal it stealing it for a moment. Over here in our Docker Compose file, we have these environment variables. I've added one called Twilio phone number. I'm gonna take the one that Nathaniel just sent me here, and there will be a series of other environment variables as Nathaniel goes around the UI. I will populate here. A couple of them are sensitive, so I'm no longer going to show the Docker Compose. I'll show you what happens once we get them all populated, though. Okay. As you were. Awesome. So now that we have this, the next thing we're going to need, and it's just underneath, is a TwiML application. TwiML applications are pretty important. They are ways that we can essentially give Twilio some instructions of what what to do when phone calls happen. We are going to create a new to more application, and we're going to call this directus for the workshop. Now, what you often have is you have a voice configuration. This says what we should what should happen when a phone call is made to or from this TwiML app. I'm going to come and fill this in in just one moment. But I'm gonna hit create. So now we've got this director's Twilio workshop. We're gonna open it into a new tab. And I'm gonna come back and grab this SID. I need this SID for what we're going to do next. So this is the identifier of the application that we are going to be using. Alright. One more thing that I need to do. I'm going to be using something called functions to do this. You don't actually need to use functions. You can write your own endpoints, and your own ways to do this. I am going to be using this. I'm going to create a new function. I'm going to be creating a function that uses the Twilio client quick start, and I'm going to give it that AppSid that we just collected. I'm also going to give it a phone number. This phone number is the caller ID, and we're going to create it. Drum roll. It's creating in the background Any second now. I know why it's failed to create Twilio cogs go brr. The reason why it's failed to create it is because, I did a practice run a couple of minutes ago, and they have the same name. So I'm just gonna quickly delete these and then start again. Create this, pop in a caller ID and pop in that ID. This time, touching wood, it should be fine. Uh-oh. Okay. I might have to come back to this. Come on, folks. I can always do this step as well. Like, it's all good. We'll we'll we'll we'll get this. We'll get there. You got this. Okay. Once I've done this one, we'll we'll come back to it in a second. I think it's just, like, deploying, and it's actually going and taking down some assets, and it maybe needs to be taken a second. But we'll come back to this. Okay. So we'll come back and do this later, but for now, we have our Twilio phone number. We have our Twilio API appsid, and then we also need one more thing. We need to get API keys and tokens. That's How many do you account set? Yep. As well. Yeah. Keys and tokens and then accounts set. And this is essentially how we can, authenticate with Twilio. Give me a second. Just need to log in because I've been in for a certain amount of time, and I'm trying to access a secure page. And I need to turn over and hit my So when all is said and done, to generate tokens using a an endpoint in Directus and to do all the stuff we're gonna need to do later, there are 5 pieces of information we will need from Twilio. We need a phone number. We need the the TwiML app SID, which you've generated. I'll grab it off you in a moment. We need your account SID. We need an API key and secret, which we're in the middle of generating now. We'll use the API key and secret to generate tokens, and we'll do that in the endpoint. Cool. Sending over the SIT and sending Lovely. This is the TwiML the TwiML app, Sid? These are the API keys and secrets, and the TwiML Sid begins with an a p. So if it starts with a s k, it is always a secret key. If it starts with a a p Man, you just sent you just sent me 3 long ass numbers, and I don't know it right. The first one is what? So if it starts with a s k, it's a secret key. Yeah. Right. Right. Right. Got you. Got you. Got you. Got you. Got you. The one under the secret key is the, secret no. So the see. Yeah. Secret keys partner. Secretly keys is the secret secret? Yeah. And then the last one that starts with an a p is the TwiML app. Yeah. Right. I'm gonna just say this again. The one that begins AP is the TwiML app. Yes. The one that begins 2 k is the API key. Is the API secret? Alright. And the one that begins SK is the API key. Right. Okay. Cool. That's why we double check. Can you imagine if we got much further and we're just like, why the hell isn't this working? It's because I missed copy and pasted a key. Freaking hell, man. I need your account, Sid, too. And then we've got everything we need from the Twilio side. Account Sid asked. Thank you. And we need to just make sure that function can be deployed correctly. And that's linked to the TwiML app. Let me quickly go there and try this. I'm just gonna quickly hit this function. Alright. Good. We're literally still in Sab. I'm having a blast doing this with you, by the way. We should do it more often. Yeah. Alright. Now it works perfectly fine because there's nothing else that has the same name. And let me just quickly tell people what this generates. This generates a function with just a little bit of code that just says, that if this number is coming from from a Twilio client, it should be forwarded to, whoever it's going to be calling and vice versa. Right? I'm gonna copy this It's kinda nice that you could just create it from the boilerplate and forget about it to a degree. I still need your account, Sid. And then we are all groovy. I mean, get onto building building. Yeah. You didn't send it to me. Sid. It's the last one. A c. Oh, cool. Alright. And, last thing, I'm just gonna quickly grab this URL, head over to the TwilioTwiML app that we created, and pop it into our voice. And voila, we are done. Lovely. And what I will just say is the recording will be available on demand. We'll also write this up. So there was there was quite a few little moving parts there. They do all have purposes that, you know, that are important in this whole journey. We'll detail them there as well so you can in case it hasn't quite sunk in, it can. Of course, we also have the chat if you wanna ask further questions now. But with that, can I take my screen share back? Yep. Wonderful. So I popped all of those environment variables inside of the Docker Compose file, and now I just need to restart. So control c, up and enter. And now we restart the Docker container, and it will grab those variables and put them in the environment. So there we are. So now we're ready to actually go ahead and just start creating an extension. So what we're gonna do is I'm gonna create a new a new, terminal over here. And, in here, this is this folder, I wanna get into this extensions directory. So cd extensions, and I'm gonna run the npx create directors extension. Extension. Extension. That doesn't look right. Extension. Extend. That's correct. Latest. Just to make sure I definitely have the latest version of that. So, I get to pick the extension type that it will boilerplate, and I actually want a bundle, which I just happen to know is the last in the list. So I went up. I can call it whatever I want. I suppose I'll call it, Twilio, Twilio. I'll call it Twilio, whatever. Auto install dependencies. So just a reminder that a bundle isn't really an extension type in its own right. It's a shell where we can put multiple extensions. Now when these were originally created, they were actually to share dependencies and, you know, and reduce the overall code bundle size, But they also have this purpose of making sure you can distribute multiple extensions together. So it's just going and scaffolding that now. And the moment that's done, we're gonna go ahead and add an extension straight into that bundle. So we'll just give that a moment there. There we go. We will go into this new Twilio directory, and we will go, we'll run npm run ads. I believe that's what it is. And once again, we get to pick an extension type. Now if you remember, we're gonna build 2. We're gonna build a panel and an end point. The end point will generate the token. So we're gonna do that first, make sure we can generate a token, then we'll move on to building the panel. So we want an endpoint, and we'll call this one Twilio token. We'll just do it in JavaScript, and it will go and add that to this, package here. And for those of you who are wondering why are we doing all of these Twilio tokens? Twilio tokens are because you're not going to actually put any of the credentials that we've just generated. Most of them none of them actually going to live in the browser, because that's unsafe. Someone could find them in the client. But what they do is they can be used by this function to create a temporary token, which then gets sent to the browser. Does that make sense? So we use these tokens from Twilio or no. These credentials from Twilio, I'm gonna call them, to create tokens. That token, it uses the account SID API key and secret to say, I am a Twilio approved. But I am the person on this account making this request. It then uses the TwiML appsid to say, this is the application I want to interact with and I would like to have permission to interact with. And then that generates a token which gets sent over to the browser, which can now interact with Twilio. Hang on a minute. Oh, did it did it update in the thing here? It does say extensions reloaded. That was just while it was in the middle of boiler plating. Just to I'm I'm just gonna restart the Docker container. I don't think I actually need to do this. I just saw an error and I was like, let's work that out. So I think it should just be Twilio token. Twilio token. Oh, there it is. There we go. So that so that is this wrap. Ahoy ahoy world. Ahoy, world. And for those of you who are wondering why we said ahoy, world. Ahoy was the greeting that was created for it wasn't created for, but it was used for phones when phones were first created. I thought it was the first word ever said down a phone line. Yeah. Yeah. Okay. Oh, holy world. Great. So we check that that works. Now in here, we wanna go ahead and actually, and generate a token. So I'll just create a new endpoint, a new route handler here. So router dot post, and we'll call this one generate rec res. Again, if you've ever done any kind of Node. Js web development, this will feel very, very familiar to you with good reason. This is just the express route to the I was talking about it feels like express. Yep. Yep. Yep. That's exactly what this is. Now inside of here, we wanna go ahead and use the Twilio, helper library SDK. What do you call it? From here, it will be the Twilio helper library. The Twilio helper library. And so we actually wanna go ahead and install that. So npm install Twilio. Fantastic. Now we wanna go ahead and, and use it. So there is a page in the docs. Let's find it. I actually saved the link earlier. It's just shortcut here. This is the access tokens page, and it shows us how we can generate access tokens. The access token oh my god. What are these kids doing? I swear they're getting, like, pamphlets, and they're just going, can you hear them? I can a little bit. Yeah. Like this. Anyway, so an access token is just this really, really long string here, that contains this information. Web token, just in case some people know. So you can break it apart into its pieces. Now we just wanna go ahead and generate them, and the docs, for Twilio provide these, these these different, snippets here. We are gonna be using the voice SDK in the browser. So we're actually gonna use create an access token, create an access token. So what we're gonna do here is is go ahead and copy and paste this, into this. Now there's a few things we're gonna do, we're gonna do ahead of time. So we are going to one moment. We are going to, obviously, pull these to the top. Those we definitely need. We need to bring in our environment variables. We do need to bring in a a couple of our environment variables, or we could just use them use them directly from process to end. But I was gonna say this is an ESM environment, so we are just gonna very quickly change, change the way that we import Twilio here. So import Twilio from Twilio, and then the access token will be twilio.jwtdotaccess. That was just a small small, semantic change there, but it is important. Then we'll take the rest of this That should bring you in the environment. You're dead right. You're dead right. And I believe that is exactly what I called them in the Docker Compose file, so I don't need to do anything with those. Then and this is now where we're moving into the, into the actual route handler itself. We're gonna go ahead and generate a ticket. Through that as well so that people know what's going on. So the outgoing application SID is the application SID we created earlier. So if you remember, I generated it, and he's going to pop that in. And then we also have an identity. Now remember, because this is a browser panel, this browser panel could be anyone. Usually, you would decide a string to identify who is this browser. Quick question. Do we have access to who the logged in you Directus user is right here? We absolutely do, and we're gonna come back to this once we have the panel because the panel is gonna send an authenticated request to this endpoint. And within that authentication will come the ID of the user along with all the roles and permissions that that user holds. So for now, we'll just hard code it as a user, but we are gonna swing back around to this later and, as you said, actually provide the user ID. Cool. Alright. Now I don't think if we take a look, you know, alright. We're not doing incoming calls, but I think we'll just leave it as is. We generate a new access token with with all of these values we've set up. The only thing left to actually do is, is return this. Yeah. Res.send, and we'll just send this value instead of the document. People are wondering, they're like, what is that voice grant stuff I am seeing? That's because you can you might want to give multiple permissions to different products with one token. So you can give a voice grant, a, a messaging grant, a conversations grant, a video grant. You could do all of that and send it in one token just to give that token lots of permissions rather than doing this if you're using multiple products. Absolutely. So with that, it's built, which is cool. This should generate a token for us if we go to slash willio token slash generate as a post request. Once again, it has the voice grounds. It has our account SID, API key, and secret. It has our identity, which is, for now, just a fixed string, but we will update that in a moment. And then it will go ahead and generate a token and, and provide it as a JSON web token, which is what we're gonna need in our panel. So let's just quickly test this. Expect to see a string of epic proportions and randomness show up in a second, if this works. Yep. So Twilio token slash generate. And we do. Perfect. Lovely. So that's fantastic. That's actually really at its absolute core, all this endpoint needs to do. There are 2 things it doesn't do, which we're gonna do later. The first is actually identify the user, and the second is be authenticated at all. Right now, any user on the web, if this was a hosted application, could hit this endpoint and generate tokens. Not good. So, later on, we're going to make sure that this is more locked down. But, for now, I think this is good. Yeah. And I think that and you can keep going while I say this. I think that people must remember on the web is just because a, URL is not publicly advertised does not mean that people will not be able to find it. And oftentimes, people use, like, just URLs are public to create really important private stuff like this and end up creating exploit, like, holes that the application can be exploited through. So make sure you protect this, outside of Demoland as well. Okay. So we're creating now our actual panel. Yeah. Is that how you spell dialer? With yeah. That was perfect. You corrected yourself. Nice. We'll just do it with JavaScript again. So we're adding this to the bundle now. The nice thing about this is we only need to be running that npm run dev at a bundle level, and it will rebuild when the things under are updated as well. Cool. So I think you see the extension. Yeah. I let's, let's npm run dev this, and, let's take let's take a little look firstly around the code and then then what it does. So there is. That's like, where is it? Oh, there we are, the dialer. The dialer is made up of 2 files, the index dot JS and the panel dot Vue. The front end of directives is built in Vue. JS. And therefore, when you're building these app extensions, they are also built in Vue. Js. So we have an ID. This has to be unique across the whole system. So, you know, you can't have 2 conflicting extensions. It's generally best practice to prepend this with your author name, to, you know, namespace it somewhat. But for now, I'll just be like Twilio dialer, whatever. We provide a name. This will show in the UI. Don't know why I keep writing it like that. We are going to put in an icon. You can use any Google material icon. Descriptions, make own calls. Now all I want you to do for a moment is take in the fact that there is this option called text. And I'm gonna show you what that does in the actual directus UI in just a moment. Now the panel, is a view component. It takes in the props from from the index JS. So here we have the text. Text is there. That's how data gets passed between this kind of configuration pane and the panel itself, and then it's just a view component. By default, out of the box, it comes with the options API. You are, of course, completely, able to use the composition API, which is, I think, what we're gonna do today. I think most of you developers now kind of lean towards expecting to see that. So what I'm actually gonna do is delete no. I'm not gonna delete anything yet. I'm gonna show you what this panel does. So let's create a new insights dashboard. We'll call this Twilio workshop, and we will add we will restart container. I didn't need to do that. I just needed to refresh the browser. That was it. I couldn't remember what step I needed to take, so I'm like, let's do them all. There we go. Gotta be sure. Better safe. You gotta be sure. There's our Twilio guy. There's the icon of phone. I was really also pass in yeah. It was easy. Thanks. I mean You can also pass in these SVGs. Drop it. I'm still impressed. Cool. You can pop in SVGs too. And as you can see, the SVGs are all purple. That is the theme color of the director's projects. You can also use these CSS variables in the SVG, which is kind of nifty. So they all feel, you know, like they belong. There's the text that this is the show header show header right here. You can add extra configuration if you want, and there it is. Show header, you can put some text in, Call people. There it is. There's the text that came from the from this configuration options. I think it's called options pane. We don't want text, but, just wanna show you that's that's kinda how how it works. So now we're going to start now we're going to start ripping it apart. First thing I think we're going to do is we are going to let's just have a think here. I think we're going to remove all the options. I don't think we need options in this. No. It has you know, it's it's going to just show the users. Like an example is, for example, a lot of companies are international. So they end up having, like, a German number, a UK number, a different number. So I'm saying I'm probably seeing you could have, like, as an option, a drop down to be, like, I wanna phone this person, but from this number because they're in Germany, and they wanna use the German number. So they it feels familiar to them, something like that. Absolutely. Absolutely. I think what we'll do here is we'll just empty that out to a div and onto the let's just let's just rock on. Let's just you know? Yeah. I love some boiler plating, but we're just gonna we we get we're gonna go from scratch. So but and I'm gonna use setup here, so we'll use the composition API. I'm not gonna bother reloading the browser because nothing's gonna show up. It's gonna be an empty box. So now we're at the point where with this blank slate, we can really talk about every line of code we're gonna write, help you understand what it does, and build this extension that will call this this endpoint we've created, which is now an internal API endpoint. We'll instal and configure the voice SDK, and we will eventually just make a phone call. I'm gonna pause for just a just a moment. We're about halfway through our time, and this is comfortable. This is a good spot to be in. Does anyone have any questions in the chat? And, Nathaniel, too, you're kinda seeing this set up, I think, for, you know, one of the first few times. Do you have any questions so far? No questions. I'm curious. I'm really excited about some of the the options as well. So you know how it had text. What are the types of things we can put into the options? So, like, I I just said, like, a drop down list, but are there other kinds of, interactions or ways we can select? Yeah. Yeah. Yeah. All of the built in they're called interfaces in Director, so form input, you know, you could we call an interface. You can use any of the built in interfaces. So you have the the WYSIWYG. You have a codes input box that does some syntax highlighting. What the hell is going on? You have a text box that, you know, can be integer or float type. Like, you can enforce that. You have sliders. You have relationships. You can actually pull data from collections and select an item within the collection. All of the built in interfaces and directives are exposed to you through through this. Interesting. Good to know. Good to know. Yep. Yep. Yep. And you can also in fact, if I just if I just undo this slightly, there's other, like, meta information you can provide. You can, hide values, make them like a password It says width full. You can also do width half and put 2 things side by side. So we give you some flexibility around how that looks and feels. And last but not least, like, because I'm thinking, like, we're making this panel and it's kind of just a demo one. But people can people, share panels maybe for other users to to maybe try out? Yeah. So, on the director's marketplace, you can publish them on npm. If you boilerplate it with, the CLI, which I boiler plated mine with, we everything's set up. You can just push it straight to NPM, and in a few hours, you'll see it appear in the marketplace. Awesome. Yeah. Cool. Right. So let's move into the panel dot view. Let's move into the panel dot view. So we're starting from scratch. What we're gonna do here? First thing we're gonna do is in is import all of the composables that come with the view, with the directors extensions SDK that we're gonna use today. So we're gonna import use API. We'll talk about what that does in just a moment. And use items. Once again, we'll talk about what that does in just a moment. Is related to how well, I know we're gonna talk about in just a second. No. Yeah. These are things that are coming from Directus that we can use Yeah. In our component. Correct. And, actually, I might just take this moment to pause and show you inside of the composables right here. These components make working with direct this easier. Use API effectively wraps your API request with all of the authentication that comes in your that is in your Directus client. So you could, of course, just use fetch. But then how does your your how does your endpoint know it's you? But if you instead use the use API, which, I think, yes, actually, it's under the hood. It also sends all of your client details. And that's how we're gonna know which users there, whether they're authenticated and so on and so forth. And this is really because I'm guessing it means you don't need to write, like, all of that metadata into your access to your endpoint URL. We're just gonna use the use API composable instead. UseStores allows you to go in and actually access information, use information within your directors project itself. So you can access things like all of the permissions users hold, data about the collections, the metadata about collections. And the other one I imported was the specific use items composable. And this will allow you to query data in your director's project directly from your panel. So we're not going off to a back end to do this. We're doing this within the client itself. And so In turn, it knows what you have access to and will honor the access control of your logged in user. So yeah. So this is like takes away the need to query a database because you are building an application over inside slash beside your database. Absolutely. Absolutely. You know, I think a lot of people see directors as a CMS or see directors as a back end for an external application. But through a Directus insights and through, like, these panels and through modules, which is another extension type, which is these. So they add they basically give you a blank slate, which you can build. Yeah. It's the most low level extension type, I suppose. It's a good base to actually just build your application within the Data Studio itself. So, yeah, pretty pretty nifty. So we're gonna use use API to make that API call to get our token. We're gonna use use items to populate the drop down list of all of the users that we can call. We're also just because, just because we're in a, a view app here, we're just gonna import graph. There's not nothing. We don't need to explain that terribly. That's just a kind of quite boilerplatey stuff. Now we're going to create an instance of use API. We call it API. Use API. And we're also going to now create our instance of use items as well. So we are going to pull out the items from use items. Now the signature here is a little bit wild, so bear with me. The first thing we need to pass in as shown here, I don't know why it's using this. Maybe I've called it a font that doesn't exist, but why the hell does it look like this? Anyway, the first thing we need to pass in is a ref that contains a string of the collection name we're querying. So we don't just pass in the string, we pass in a ref. Cool. We can we can rock on there. We can literally just put in ref. Not a problem, and we had to import it to do this. And we're gonna we're gonna query the directus views as collection. So use item The second now gives you access to Yeah. All of the Directus users. You can just pull them in. Yeah. Because that is a collection in your database like any other. Now we do have a distinction between system collections and user collections. User collections you create, System collections are these kind of 20 I showed you earlier out of the box. In fact, we haven't got any user created collections in this project. We're not gonna use them. But, yes, you can query these just like you can any other any other collection. So that's completely correct. And the second thing we we provide is a query. Now directors has quite a robust I'll show you here quite a robust query language. Here it is. Not query parameters. That's what I was looking for. So you can specify what fields are returned. You don't need everything. Right? You might only just need the ID and the title of a blog post, you know, for example. You can apply filters. You can do searches. You can do, like, patch basic pagination, you know, by limiting how many per page and what page you're on and what the offset is and so on and so forth. You can apply sorting and just more. Now we're gonna do that just to specify what fields we want returned because I don't want that huge object for every user. I just want a little bit a little bit less. So we're going to pass in fields. And we we want yeah. Yeah. And and Go ahead. You can keep typing, but I'm guessing this is something, like, that people should always be doing because it makes your your moving smaller chunks of data around, so only getting the things you need rather than Got it. Like, I feel like it's a very early in career developer move to just, like, pull down the entire database to get one field from one user. And what's really, really nice about this is we put we expose a GraphQL, you know, API, but we also expose a REST API. And now what I think is one of the core value propositions of GraphQL, which is you you construct these complex queries that only bring back what you need, you can do regardless. You can really pick what's right for you. And this works with, this works with relational data as well. Here in the items, so in the use items composable, I could, for example, let's say there's a relation, right, called, I don't know, posts. I could be like post stock title, and I can start getting the relational data too. So it's really powerful. So in in that case, you almost end up querying 2 tables. Right? Because you're querying a table and then querying Yeah. That's critical. And that's where being very selective about what you're bringing back is really critical because another valid fields query is this. Give me everything on this level. Give me everything one level down. And you know what? You know, you can do that. This is incredibly computationally expensive. But you could do this. I mean, you could. You could. But at any level, you could do this. You could be like, you know, give me everything in the you give me everything in the posts collection, for example. So, you know, we give you that flexibility. You know, you can blow your own shit off. There's great power. Great responsibility. You you absolutely got it. Now, what's gonna come back from here is in fact, this might be a point where if I just console log items, we can just get a little look in on what's actually happening here. Oh, I'm guessing we're gonna see user 1, Nathaniel Okeno. Yeah. And there's 2 users. There's the admin too. Yes. My user. I just got phone. Account. Right? You know what? We could apply a filter here that says, just give me users who have phone numbers. I'm not going to do that. But if I refresh here, there's the array. The array has 2 users, the admin, no phone, Nathaniel, as we expect. Awesome. Cool. What else what else is important here? I might just rename this users because items is a little bit of a weird word to use when they are users. So that's just a convenience thing, I think. And then what we're gonna do now is populate a a select, a drop down. And then when you select something, we're just gonna bind it to a variable in here. So we'll just create that variable now. All we want is their phone number. Right? We don't care about the rest of the objects. We just care about the phone number, and that can start off with a value of null. Now that we've pulled in all these users, let's actually display up here. And now I have the kind of pleasure of showing you the component library. Here's something else. This is the component playground. These are components we use within the data studio that we expose to extension authors. So we are going to use the v select, and it has all of the kind of styling applied. Really, really nice to kinda out the box. This is what it looks like. We need to v we need to bind it with a with a variable, which is why I just created phone number. Phone number will go in there, and you pass in items. Items have text and value. Now our items do not have text and value. They have first name, last name, phone number, and so on and so forth. You can you can change which is which field is used for the text. So we'll we'll do, like, first name. In reality, you would probably do, like, a computed, you know, a computed array that would add the first and last name together and display that. I am too lazy for that. We'll just display the first name. But that's what you would do in the real. Right? So, that's I mean, that I I may as well copy it, to be honest. There we are. Yeah. Let's put it inside this div here. K. So we don't That is the value is going to be so I'm guessing the value is going to be the phone number that we Yep. Created earlier. Phone number. Yep. And the items, is going to be users. Okay. So it's gonna grab the list of users that we got. And then grab the list of users. As noted, we do not, we don't carry out value. As noted, it's interesting that it did this because you're meant to do this in Vue, when it's an attribute. So we have the item text, which is first name, and there was item value was the one under the item. So you can choose what is visible to click, but then what that value actually trick the way it adds to whatever. Okay. That makes sense. I mean, let let's make this easy. Let's just let's just, you know, print the value. So now if we refresh, there's our drop down admin and Nathaniel. When you pick Nathaniel, there's the top up. Blank when it was admin because admin doesn't have a number. Yeah. Because, actually, initially, it was, on, I think I need to put in, like, a select. It's unset to begin with. It's null. So whatever. We would need to put in, like, a select user default setting. Doesn't have a number. So The admin doesn't have one, though. But, yeah, originally, this isn't because admin has nothing. It's because it's null. But whatever, like yeah. Small small nuance, small small edge case kind of thing there. So now we have that, then we are I think we need I mean, that's probably we need a we need a button. We do need a button. Can actually I think yeah. Yeah. We do. Let's use another component for that. Let's look at here. We have the button, shock horror. I mean, it's just the v button. We actually need to do nothing else. I'm not even gonna bother copying that, to be honest. V he says and then makes a typo. V button, call user. And then we will when it's clicked, we will start call. And, of course, we need a function, Let's get this const stop call. I think it needs to be an async function for later. I just happened I mean, we could change it later, but we'll do it out there. So there you go. That will start, and then we'll we'll grab the value of phone number at that point. When we start a call, we will then go and grab the token. Tokens have a short lifespan, so you really wanna grab them at the point you're gonna use them all very soon before. So we'll we'll go and handle that. Spend the token time time to time to live. It is not recommended for you to just, like, create one that lasts the whole year. You want ones that are gonna last a short amount of time. Yeah. Because they can they I mean, if they're intercepted, they're usable, basically. So, yeah, you do only want them to live They wanna run a short while. So we obviously haven't done anything with the use API yet, but I think you broadly understand what it's about to do. Do you have any questions? I'm pretty happy with how it's shaping up. I like how quickly you can build the especially with the component library, how quickly you can build the dysfunctionality, with all of and I'm guessing the great thing is, like, with stuff like that, you're standing on the shoulders of giants, of heroes where, like, they have done all the sorts of things to, like, think like, I saw you change in the width of the button, like, really easy just dragging across and stuff. Yeah. That is really, really useful. This one's my favorite personally. It's called fancy select, and this is just a select 2 items, a divider and a third item. And it's just a select, but look how pretty that is. I love how the pass. And that's used and that's used inside of the UI as well. We actually used it when we, when we or did we use it actually? Nope. Lied to you. I used it earlier today. No. So there you go. Right. Okay. Save and refresh. There's the button. The button just runs this function. Now with the point where we can go and get the Twilio voice SDK. So let's let's go ahead and do that. And, yeah, that's a really good point, Alex, in the chat. This makes customizations feel native to the by using the component library, it all feels native. Don't get me wrong. Obviously, it lacks polish. It needs some padding. But, actually, to be fair, just by adding padding It would be a really good job. Yeah. Yeah. Yeah. Yeah. Right? We're we're lazy, so this is what you get today, but you could Speak for yourself. I'm joking. Speak for you. Yeah. I'm I'm the one with the keyboard, so I'm speaking for both of us. Hey. Right. Let's go and install npm install, hang on a minute. Twilio and I think voice SDK. It is. It is. Npm install at Twilio slash voice SDK. Yeah. Not my first rodeo, mister Academy. I have made this mistake multiple times. Now Cool. Before we continue, there is one more piece of configuration. And it does involve going back to that, to the, Docker composed file and adding one more environment variable. Let me let me shove this up. We're still here underneath the eyes. Hello. But I just wanna get it to the point where I'm not about to leak all the information. So I will get rid of that now. Okay. Back it back here. We're back here. Just above the cursor, a couple of lines are all those environment variables I don't wanna show you, but we do have to add one more. You might be thinking, pretty damn sure I've added enough of these now. Like, I I don't don't wanna do this. But what we do need to do is, let's find it. I did write because it's long. I did write it earlier. Needs to add this environment variable. Why? What the hell is this? As mentioned earlier, the director's data studio, doesn't allow request to external necessarily without perfect configuration allow requests to third party services, like to 3rd party servers or whatever. And that's a security precaution. That's great. But we are going to be connecting directly from our browser to a phone number. I think, strictly speaking, we could probably build this as an endpoint too and use the endpoint as like a middleman proxy, but we don't we don't have time for that. The easiest way to do it is this environment variable where we're basically changing the content security policy, to allow the connection to this WebSocket URL, which is what the voice SDK is gonna do in a bit. I was wondering. I was like, have we ever seen this before? Like, but now that makes sense. So you are giving It took login. It took Yeah. Yeah. So you said to work that out. We are making sure that the, the director's browser I'm gonna call it the director's browser, which has these extra security walls around it. We're just like, yo, let, WebSocket connections to this Twilio u URL happen. Awesome. Yes. And otherwise, that will error. And while we were putting this together, that error, it's like I had to add that environment variable for it to work. It's pretty descriptive. The error tells you it's a CSP, a content security policy problem, and it's on this connect source value. And that it was while trying to access this this, URL. So, you know, it it was pretty easy to work out, but let's save ourselves the the pain and do it now. So so we've installed the SDK. Yeah. Talk about. So we've installed the SDK, and as I'm talking, you can maybe, like, type. So with the voice SDK, what we start, we bring in the SDK, and we bring in specifically a device. The idea of a device is a device is anything that can connect to Twilio, because the device right now, it's gonna be a browser, but it could be a phone if you're using the, like, react native or the iOS or Android SDKs. But we are bringing in a device, and that device needs to have permissions, and we give those permissions from a token. But we don't have a token now. So how are we gonna get this token from our endpoint? Great. We've kind of spoken about it already. We're gonna use this use API composable. Call our now internal endpoint slash, slash Twilio dash token slash generate as a post request, we'll then get the we'll then get the value here. So, the way we're gonna do that, we've already created this, is we are going to, pull out the data value that comes back. I'm already gonna just call it token. Await API dot post because it is just an Axios instance. Ultimately, I'm gonna go to \u002F Twilio token slash generate. That will return a string, which is the token, which we can then remove these question marks. We'll pop this directly in here. We are That's how you pass this device. Yes. We're ready to register the device. There is one more thing I would just wanna take a moment to do. And, it's not I've I've not taken note of it, but I just wanna derail this for a moment. I did oh, shall I do it? Shall I do it after? It's the whole sending over the sending over the correct user and authentic. I think we'll make the request first, and then we'll we'll see what happens. So we have a device, and then let's just console log device. Realistically, we're either gonna see a device or we're gonna see an error because that token's invalid. There is no there is no other outcome. It's one of those two things. Extensions reloaded. I hit save. So we're gonna refresh this. Open the console. Go to you. Hit call user, and that's the device. Rock on. What else is important here? Oh, no. No. No. No. We're not done. We need to also do the device. We need to register the device. That's my bad. That extension's reloaded. I might just rename I think it's alphabetical because, again, lazy. Oh, being being too hopeful there, I think. Okay. Whatever. Cool user. Oh, problem. Why? WebSocket received error undefined. Oh, I think oh, I didn't restart the I just needed to because I changed the environment error. That was the error he told us we were going to see, and he walked right into it. But there it is, by the way. Refuse to connect to this URL because it violates the following content security policy directive, connect source self HTTPS. So they were there, and then I added on to the end of it the WSS, you know, Twilio voicing. The Twilio token generate is, I feel like you probably came into this, into this session a little bit later on, John. But here here it is. We built it first. So, yes, what we've built is a bundle. In the bundle, it's an endpoint and the panel. The panel calls the endpoint. The endpoint talks to Twilio, comes back, returns it to the panel. So, yes, it is now a direct test custom endpoint, by virtue of this file here. Okay. Let's do that again. When you see an error in a workshop and you're like, damn. I hope this isn't gonna derail everything, but no. There there we go. No errors. So we're good. No errors. And we did see there it did a post to the Twilio token generate endpoint. It returned in 25 milliseconds with the a a device, register device with Twilio, and say, hey, device. I'm authenticated. I am now ready to start making calls and receiving calls if you set up to do that. Let's do this, and then we'll go back in the in up the the endpoint because that is there's no problem now for its hard coding user in the endpoint. But in the real, you should not be doing that. So we'll cover that at the end as like a let's it's not a next step. It is critical, but we will treat it as if it's a next step because I wanna I wanna get a call. I wanna get a call going. I'm getting impatient now, as you know. That is the person I am. Okay. So, let's now yeah. And once the so there's one other thing here, which is this device doesn't register like that. Instead, it emits an event when the device has been successfully registered. Sometimes that takes a moment. I think that it also pops up and, like, asks you for, you know Yes. So My hat says why register can take a couple of seconds is because sometimes it needs to ask permissions to use your microphone, which could take you one second, 2 seconds. In Kevin's case, where he's already done like a we we've done this before It doesn't show up. So it's given it permission. So it's not going to do that. So while it may seem lightning quick for him and you might be like, why do we have to do this and then wait for it to finish? It's not always gonna be like that for all your users. So then what we use are these events where we say when the device has been registered, then we want to do something. Now what a lot of people do, especially if they're creating dial up panels, is you might have you might register a device when someone opens the dialer. Right? So that it's quicker that when they click a call, they're already registered. And then you can already, like, say that something's gone wrong. You are not authenticated to use this dialer straight away once they open it. Different Can I ask you a question? Yes. The the tokens don't last very long, and we we use the token in the code when we register the device. And I don't think we use the token again. But what happens if the token expires between registering the event and actually trying to make the call? At what point have the handshake happened? The handshake happens when you register the event when you register the device. Right? Not when you make the call. Not when you make the call. Oh, interesting. Is you also get a event when your token's about to expire and when your token expires. What we recommend people do is when your token's about to expire, generate a new token. So hit that API call then start a new token get a new token. Yeah. Exactly. Again, this limits your surface area for exploitation. So it's it may seem like a little bit of a faff, but trust me, you do not wanna be hit with a crazy Twilio bill because someone's hijacked your phone call and used it to call premium numbers that paid themselves. That. So we're gonna make a call here. The call is going to be an object. I'm going to save it up here because we want to start the call later. We're gonna wanna handle hanging up. So we'll need access to that up to that object in the in the global scope. So we're going to go const, call, I think we'll call it, and we'll just again initialize that with a value of null. Now it's time to actually make the call. So we'll set the value of this ref here, into device dot and yeah. Thank you. But my uncertainty was creeping in there and it has a params object and the value is going to be phone number, this one here, dot value. Because it's a And it's that didn't get the raw value. You might be like, why connect and why is it that params too? Because there are a couple of things. You can clone phone numbers, but, like, with Vince with you, you can actually phone video call rooms. So let's say people having a video room, you can literally dive in to it from here. You might want to dial another client, which isn't gonna have a phone number. It might have, like, a a name attached to it, like the user that we have. So, there could be a couple of things. We're using phone numbers here, but that could be a few other things, which which is why there is a bit more flexibility in I don't wanna say ambiguity, but a bit more flexibility of what could go into that params. Yeah. Alright. This is everything we need to actually make a call. We're not done. We're not gonna get any UI that we've made a call. There's gonna be no ability to hang up. There's none of that, but I I think this is a moment we can actually try this out. Alright. That's it. You know, we've got 33 lines of code with a few lines of white space, and I think this might be all we need. So your phone number's in there. Yeah. Let's ready for the error? I am. I am ready for an error. There it is. Allow. You've hit call. I am getting a phone call. I'm gonna mute my mic and just join the phone call. Hi. Hey. Yeah. You sound you sound suitably terrible as phone calls do. I can't I can't I can't be bothered. I literally can't be bothered to figure out sharing your audio. So anyway, the thing is like, you should share my audio. I'm like, I can't be bothered. I was speaking on the phone. He could hear me. Just because we're not sharing audio via the the stream, you couldn't hear me. So next time, I won't mute my microphone. But his browser is you couldn't hear me. So next time, I won't mute my microphone. But his browser called my phone. Hey. There was no indication we did it. There was no indication it ended. So there there's some things we need to work out now. But we're basically sound. Yeah. There were some sound things. So, Twilio just built in we have, like, these audio, like, files that, it's, like, plays a sound when you connect. It plays a sound when you disconnect. But, visually, there are no cues. Now you can customize the sounds that you that are in there to have something that's like your own UI. And, like, what we're gonna do next is you probably should change your UI so people can see visually what state the call is in. We're at the point now where we are polishing this. When we get back to disconnecting the call, I think we'll, I'll slow back down. But for the next couple of minutes, I'm gonna speed run this because this is just little view UI, you know, things. So we do this. We currently have this div here that shows start call pick phone number. So I'm gonna create a new ref called show call. Sure. Let's make that false because at the beginning, there is no call being made. The call, I believe, also emits events. So call dot value is this, then we'll go ahead and say call dot value dot on. And there is a ring ring. There is a Yeah. I don't know if it's answered or connected off the top of my head. I will check it out later, but it's subject to tell you it's connected, and then one to tell you when it's I think we're gonna be lazy and we'll just do ringing and disconnected because disconnected is by either hand, I think. It's just disconnected. It has been dis phone ended. Whether you hung up or they hung up. There is another way to, like, distinctly choose between which person hung up, but we're not gonna be using that today. Exactly. So show call dot value becomes true. And then on disconnect disconnect, we'll set it back to force. Then in here, we'll say v if not show call, do all of this. And then, oh, just some just some chat right here. Loving it, but the, the potential tying this ability with other business use cases like customer engagement, etcetera. Yeah. I mean, I see, like, customer, like, outbound call centers as being, like, rich for this. We'll talk about that more more a little bit later because I have some thoughts on where this could go in the future with more time. Also, the possibility to include the verification style systems, generate a confirmation code in direct us. You read it out. You know, you could, send it via Twilio and then confirm it on the phone, stuff like that. Yep. Excellent. Yeah. I love the part with the browser called the phone. So did we. I'm I'm glad it worked. The first time. Right? The confidence in it. Yeah. We also need, I suppose, just a v l's here, and we'll just say call in let's do it more like this. Call ongoing. Not that. E. I just wanna No. I'm just gonna say, what is that p else? No bloody clue. The or the first result having a mad one. And then I think we'll just want another button. But this one is end call. And exactly. And we'll do an end call function, which I will just create, and we'll deal with it in a moment. Const end call. I don't doesn't hurt anyone. Yeah. I was it doesn't have to be, but right in there. Yeah. So that's the UI. It'll call ongoing end call, and then there's an end call button. It will do nothing. We we do actually need to tie this up to ever get back to that first state by pressing the button. And I think called dot value dot disconnect, the thing where we have a connect, there is also a disconnect. And then call, show call dot value force. I think it's handled by this, but, whatever. I wanted to work first time, so we're just gonna we're just gonna keep throwing some redundancy in there. I think that's groovy. So extensions reloaded. Let's try this out. Nathaniel, call. Nice. Good. Call's coming in. Answer. Hello? Testing. Testing. 123. Yeah. I hear you twice through through the browser and through our streaming system, and, it's it's twice too much, to be honest. Bye. Oh, forget that. It did hang up the call. Stereo. Yeah. It did it did hang up the call. Dun dun dun. Oh. I don't know what this is. I did hang up the call. Just before we get to this, because I think this might be in the voice SDK. But I'm not convinced. Get on proxy. Log is read only and not configurable. Now that means logs being used somewhere inside of the call dot disconnect. Alright. Call dot underscore disconnect. Alright. We are yeah. It's here. It's somewhere in here. We are going into the depths of the of the SDK, the voice SDK, and I have a better idea. I have a better idea on how to handle this. Call.value.onerror. Yeah. Damn. Does that even work? I I do not know. I I I am desperately looking up the voice SDK to see what Honestly honestly, my guy, it's always the end of our it's always the end of our Workday. Yeah. It's a frantically look up anything. I'm swallowing the error. It's not an error if you if you swallow it and don't display it to the user. Is it. You're you're absolutely right. There is an error. But like, sorry. There is, on error event. Oh, I just I'd made a guess there. But I'm gonna call you again. Hello? How are you? Yeah. Yeah. I I don't care how you are, mate. Sorry. Bye. Okay. Interesting. We'll pause on that. I'm gonna I'm gonna refresh and call you one more time, but I'm gonna hang up because we actually haven't tested that side. Sorry. You're gonna hang up the other way. I've I've been the one to hit end call. Okay. Interesting. Look. Works great. Call ended. Question. It's when Question. I disconnect. Yeah. Question. You, when you disconnect works. Yeah. Here. So you hit disconnect. Do wait. Hold on. I wanna quickly check something because, you've got on disc. That's on disconnect. I'm not sure, actually. Mhmm. I'm not sure. Neither am I. Neither am I. Refresh, reboot, pick up your changes. The changes are happening. Look. When I hit save, just watch it. Extensions reloaded and other UI changes were displaying. So I'm confident that happy to call this a a success, to be honest. Now there are a few other things we need to do. We need to like, I I think we must tighten this up a little bit because it's currently a little too open for everyone. Because right now do here is anyone who gets access to the URL gets a token that allows them to phone anybody they want and charge it to you. Agree. So, Wreck, I want to say it's accountability. But just in case it isn't, I'm gonna just oh, do you wanna know the way I'm gonna do this is I'm gonna go, object dot keys rec, and we can work from there. Oh, was that too hasty? Oh, was that too hasty? Is it building? Oh, but it's still building. It's it's packaging the whole Twilio SDK t k and and that's why it's just taking a hot minute there. So we're gonna do this. What's the best way of doing this? I think what I'll do is in the panel is I'll just pause on I'll just comment all of this out briefly. I don't actually wanna be making calls. Interesting there. Refresh and call. And over in this terminal here, great. We have You have accountability. Paul, last in the chat, obviously, you can lock this down in direct, but imagine inside of Twilio, there are also options to lock down your numbers. Zip rank as well. No? Lockdown ask that question. I am locked down the Obviously, you can lock this down in direct test. We might need to expand on what that means. But I imagine inside of Twilio, there are options to lock down your numbers or SIP trunk as well. You're not I mean, these on your numbers. Tokens are the way to do that. Yeah. These tokens are the lock. Yeah. So you wanna make sure you're securing the generation of them. So And, accountability. Yeah. The generation of these tokens is essentially how they are locked, and only someone who essentially only someone with your credentials can create tokens, which means only someone with your credentials can create calls. I'm gonna pause you for clarification. Lockdown calling premium numbers or long distance Yes. Oh, yes. Yes. Yes. Yes. Yes. Yes. Yes. You can. I can actually show you that in the browser. So, there is I'll I'll pop over to you while I just do this. There you go. See yours now. No worries. So you have voice geographic permissions. So eventually, let's go back to that. Voice geographic permissions. And give it a second. It will load. And so you've got, programmable voice and SIP trunking as well. And you've got, they give you also an average cost of how much things will be. You've got low risk and high risk. This is a connection. This is not just countries, so it's not entire countries that are, high risk. Although it is could be a large proportion of countries, large percentage of that country. It is specifically network carriers in specific countries that have been known to have a higher risk of toll fraud, so you can block them. So I can, like, block the whole of North America. I can block specific country and so on. And then you can also pop in a number to check if it's got permissions at all. So that's another way to just, like, lock it down, and this is great because it's on an account level. So your whole account can be locked down. All good? Yep. I did I did the changes. So at the top of the at the top of the, route handler, we inside of this, request, this rack, you get this object called accountability. Accountability contains the following properties, user role, whether or not they're an admin, whether or not they have app access, IP, user agent origin, and permissions. So you can further go on and expand, but all we care about are you logged in, and authenticated with director? So so that's all this does. It says, hey. If you don't have a user ID, go away. Now I think about it, there might be more you wanna do here because you could just jam an accountability object and user. So there's probably some other levels of, of restriction you wanna do here. For example, checking the user's role, which then will return they don't have a role because they're not a valid user. But I think this is I think this is okay. And then here in identity being Yeah. User, it can be There you go. Rec.accountability.com. So which is the ID. Name of the user that we're logged in on in the system? No. It's the UUID. Okay. UUID. Cool. It's the UUID of the user. I mean, if if you want to see, we can just console log. Well, because I was gonna show how it appears in the Twilio logs when a call comes in. Sure. Give that a moment just to reload one more time. No worries. Again, it's bundling that whole Twilio, helper library, and so it just takes a moment to build. We'll hit that. Did you uncomment the call? No. Yeah. That would help. Alright. We'll make this a short call, just so we can show you the logs. So now only authenticated users on directors can use this application. They he's gonna call me. I'm gonna answer. I'm gonna keep it on for one second. He's gonna end the call. My phone call ends. And then I'm just gonna refresh my logs so that we can see the latest call that happened, which was 838. Yep. Which was this one, and we can see here it's complete from a client, and it was from client. And then this was the UUID that he talked about. I can actually show you a previous call. Remember, it used to be hard coded to user. If I get to this one over here, we can see it was client user before, and now it's client and then the user ID. That's cool. Cool. So, I mean, I I think and I thought about it more actually. I may update the docs. I've just thought about it in this moment. The user actually this doesn't prove the user is authenticated. It proves that there is an accountability object with a user I with a user value that is not foresee. That's what I check against. But what you may wanna do is expand this further, use the permission, service, check they have certain permissions, which they either will or won't have. That is more direct us, that's locked into direct us and it will say no if they don't exist. So just a thought there. What we did was a lightweight, like, check, but it is not foolproof. And I've just realized in this moment that that's the case. But that's fine for this. We've acknowledged it. We've shared that with you, and we've given you an approach. Now I think that's it for, like, what what we can do here. But what what more could we do? Firstly, we need to handle that rejection state better when I hung up. Can't be bothered now, but just I'll figure it out. It'll find its way into the blog post that'll accompany this. One thing that's really cool if I just come back to my screen share is these are real users. Right? And they're queried using the composables that are exposed to view to to this view component here, this extension. But you have access to all the collections. So let's say, for example, you have, customer calls or customer notes, customer note whatever, a timeline of events and they exist in your project, you could select the user, get maybe their latest notes, then call them. So now you have context. Additionally, insights dashboards have this concept of a global relational variable. So what this will do is user, is you select directors users in here. Let's say we want first name and last name in here. Let's not have them overlapping each other. You could select a user from here, and this now contains the UUID of that user or the object of that user. I can't remember. But you could feed that into this panel. You could feed it into the panel that gets customer information. So you can have multiple parts of your dashboard all changing because you selected the user once in this global relational value panel. So there's so much. There's so so much you can do with this. And I'm just really excited for more people to see directors insights as a really valid app builder surface more than just, more than just insights and BI. So with a couple more minutes to to go, if anyone in the question if anyone in the chat has questions, please do chatter and let us know. Nathaniel, do you have any any closing thoughts while we wait for those? No questions. But the thing is, like, I'm I'm just really trying to think about, like, other ways. So, like, there's obviously, like, building your own mini contact center. Because contact centers are, like, big and expensive, and actually sometimes what you need is you don't want this massive, like, stood up, like, huge at scale contact center. You just wanna be able to have the ability to contact your users just really, really quickly, and you could start building applications like this. You can then also because you're using Twilio phone calls, you can access, like, a lot of other Twilio functionality. Like, we've got, like, voice intelligence transcriptions where, like, you have a phone call. At the end, you can run AI operators on it to just be like, yo, this person, like, that person asked for a manager, or the person was happy. We can just do sentiment analysis and have that, like, just pop straight into a panel, for example, because you are just connected to that ecosystem as well. So, there is way more than just the calling calls that you can do, but, it all starts with, like, ring ring. Yeah. And I suppose you can query APIs for that data as well from within here as we have just demonstrated, and we spoke about with the weather API, you know, example way up top. You can just call off to arbitrary third party APIs and bring data in via custom endpoints. So, yeah, there were no other questions in the chat, so I'm pretty confident at this point saying I think we are off the clock, which is wonderful. This has been awesome. Thank you so much for joining me for this. I had a really fun time putting this together and delivering it with you. Always enjoy hanging out with you. Next time we go on a roller coaster journey, we should write another workshop as well. Which is what happened. Sorry. That that that's what happened. We we wrote this basically while while going to a theme park together. We had a long, long, long, long drive, but something could came of it. Weekends. Well, on that note, thank you so much everyone for joining in. We will see you somewhere. Bye. Bye. Oh, wait. Where can people find you? Oh, online at Twilio dot. They can email me at nocennwall@twillio.com. I do check my emails, and, yeah, just say hi. I was ready for you to that. I don't check my emails, but you can send me an email there. Alright. Alright. We're done. We're done. We're done. Bye.","dc015249-9e16-4724-b6d2-d16cbaae7a2e",[2529,2530],"75d81975-d6de-4576-b6c5-e5ca707efd53","f07c9fd0-4608-4f90-a95a-079338db2d37",[],{"year":978,"number":979,"id":980,"episodes":2533,"show":2534},[982,983,984,963],{"title":986,"slug":987},{"id":2536,"slug":2537,"vimeo_id":2538,"description":2539,"tile":2540,"length":159,"resources":12,"people":12,"episode_number":129,"published":2541,"title":2542,"video_transcript_html":2543,"video_transcript_text":2544,"content":12,"seo":2545,"status":19,"episode_people":2546,"recommendations":2548,"season":2549},"1927e10b-f96b-41e4-a57a-42ed26094a0a","directus-tv-summer-2024","959642640","New shows and renewals for Directus TV - our streaming platform which brings together education, entertainment, and stories from across the Directus ecosystem. ","bad02aa6-b270-412e-b671-e6ca322d1f99","2024-06-17","This Summer on Directus TV","\u003Cp>Kevin: No. Hang on. We gotta go back. We gotta do that one again. Sorry.\u003C\u002Fp>\n\u003Cp>Bryant: Hello, everybody. Now let's dive into our theme. So for\u003C\u002Fp>\n\u003Cp>Terence: this, I'm gonna pull out my trusty palette knife.\u003C\u002Fp>\n\u003Cp>Bryant: This is not gonna be good for my lisp, but we're gonna try it anyways.\u003C\u002Fp>\n\u003Cp>Saron: It's just a really great tool and I've just been really enjoying still using Typeform all these years later.\u003C\u002Fp>\n\u003Cp>Kevin: So I'll give that just a moment, and there are the maple trees again.\u003C\u002Fp>\n\u003Cp>Terence: I like this because every time I put it on, it reminds me that some tech is just rubbish.\u003C\u002Fp>\n\u003Cp>Kevin: Not terribly elegant, but we're just gonna throw a new error.\u003C\u002Fp>\n\u003Cp>Terence: Hop open my inbox. Boom. There we&nbsp;can see the message.\u003C\u002Fp>\n\u003Cp>Andrew: Let's take a look at our logs. We have got some logs in here. Holy moly.\u003C\u002Fp>\n\u003Cp>Bryant: I guess this is gonna be all sorts of copyright infringement here. I wanna see your facial expressions because I wanna know if this is a terrible idea. This is for entertainment purposes only. Any lawyers watching this, just so we know.\u003C\u002Fp>\n\u003Cp>Bryant: The creative code or design and development depending on which alliteration you choose.\u003C\u002Fp>\n\u003Cp>Saron: Keyboards are probably the main one that I could talk about where&nbsp;I have a bunch of custom mechanical keyboards. I have some behind me. I've got a bunch all over the place.\u003C\u002Fp>\n\u003Cp>Kevin: Let's get started with Directus and Astro.\u003C\u002Fp>\n\u003Cp>Daniel: Welcome to another exciting episode of Request Review, where we go over your hopes, wishes, and dreams, and potentially crush them.\u003C\u002Fp>\n\u003Cp>Kevin: This was a huge amount of fun it was just a little quick project which I think is gonna actually enrich my day to day life. Oh hey this is from the cat. I was hoping it would show up.\u003C\u002Fp>\n\u003Cp>Naz: They have a nice cat. Christopher.\u003C\u002Fp>\n\u003Cp>Bryant: What does it all mean?\u003C\u002Fp>\n\u003Cp>Salma: Thanks for joining me and, I'll see you somewhere.\u003C\u002Fp>\n\u003Cp>Kevin: No. No. No. If&nbsp;you're if you're okay with it. I love that as the end.\u003C\u002Fp>\n\u003Cp>Salma: Alright. Fine.\u003C\u002Fp>\n\u003Cp>Kevin: Next, Directus TV. If you've not heard of it, it's our streaming platform, which brings together education, entertainment, and stories from across the director's ecosystem. Every week, we publish new episodes across our 30 shows. Since the last leap week, we've released buzzword wilderness, which has Matt asking if it's real or just marketing, democratizing data where we bring new life to open datasets with directors, and new seasons of both Trace Talks and a 100 Apps in a 100 hours. Today, we are announcing some new shows.\u003C\u002Fp>\n\u003Cp>In TIL, that's technically I'm lost, Matt dives deep into the world of code. Along the way, he shares his discovery, successes, and occasional missteps making tech more accessible to nontechnical professionals. In authentication avenue, we travel down the world of all things authentication, authorization, and access control. Director's auth is hugely powerful, and we're excited to help you better understand it. In dungeons and dashboards, we will build tools for our fellow dungeon masters, guides, and navigators, and build the ultimate toolkit for running campaigns with our parties.\u003C\u002Fp>\n\u003Cp>Cassidy: And I'll be your host in Talking Heads, Coding Hands. It's a developer game show bringing together elements from other Greek games you may know. Join our contestants as they talk and code against the clock.\u003C\u002Fp>\n\u003Cp>Kevin: Thanks for that, Cassidy. There are even more shows hitting your screen this summer, so you can get truly lost down the rabbit hole.\u003C\u002Fp>","No. Hang on. We gotta go back. We gotta do that one again. Sorry. Hello, everybody. Now let's dive into our theme. So for this, I'm gonna pull out my trusty palette knife. This is not gonna be good for my lisp, but we're gonna try it anyways. It's just a really great tool and I've just been really enjoying still using Typeform all these years later. So I'll give that just a moment, and there are the maple trees again. I like this because every time I put it on, it reminds me that some tech is just rubbish. Not terribly elegant, but we're just gonna throw a new error. Hop open my inbox. Boom. There we can see the message. Let's take a look at our logs. We have got some logs in here. Holy moly. I guess this is gonna be all sorts of copyright infringement here. I wanna see your facial expressions because I wanna know if this is a terrible idea. This is for entertainment purposes only. Any lawyers watching this, just so we know. The creative code or design and development depending on which alliteration you choose. Keyboards are probably the main one that I could talk about where I have a bunch of custom mechanical keyboards. I have some behind me. I've got a bunch all over the place. Let's get started with Directus and Astro. Welcome to another exciting episode of, Review, where we go over your hopes, wishes, and dreams, and potentially crush them. This was a huge amount of fun it was just a little quick project which I think is gonna actually enrich my day to day life. Oh hey this is from the cat. I was hoping it would show up. They have a nice cat. Christopher. What does it all mean? Thanks for joining me and, I'll see you somewhere. No. No. No. If you're if you're okay with it. I love that as the end. Alright. Fine. Next, Directus TV. If you've not heard of it, it's our streaming platform, which brings together education, entertainment, and stories from across the director's ecosystem. Every week, we publish new episodes across our 30 shows. Since the last leap week, we've released buzzword wilderness, which has Matt asking if it's real or just marketing, democratizing data where we bring new life to open datasets with directors, and new seasons of both Trace Talks and a 100 Apps in a 100 hours. Today, we are announcing some new shows. In t I l, that's technically I'm lost, Matt dives deep into the world of code. Along the way, he shares his discovery, successes, and occasional missteps making tech more accessible to nontechnical professionals. In authentication avenue, we travel down the world of all things authentication, authorization, and access control. Director's auth is hugely powerful, and we're excited to help you better understand it. In dungeons and dashboards, we will build tools for our fellow dungeon masters, guides, and navigators, and build the ultimate toolkit for running campaigns with our parties. And I'll be your host in Talking Heads, Coding Hands. It's a developer game show bringing together elements from other Greek games you may know. Join our contestants as they talk and code against the clock. Thanks for that, Cassidy. There are even more shows hitting your screen this summer, so you can get truly lost down the rabbit hole.","a5eb5979-dc67-4639-b8b2-2dafb243faa4",[2547],"f4fa786d-273c-4473-add9-d16c970e3f7a",[],{"year":978,"number":2550,"id":2551,"episodes":2552,"show":2560},55,"edaf4f46-b4d7-468c-bb14-4778c0e3b304",[2553,2554,2555,2556,2557,2558,2536,2559],"a8eb1187-ee58-4583-824a-5d9cbefa8d7c","e477c1e4-2942-493c-b2d4-e41f589eac72","191de350-707d-4a57-a8f5-e749820530d9","369abe83-ca5d-4fbc-81c8-626912b0a7f8","c5593b03-9801-43e9-9606-facfcfb2791f","b9284b99-793b-4e71-a706-7b2f0ae2cb7a","a48019be-7fd1-4b64-9b25-2b579196f121",{"title":151,"slug":152},{"id":2557,"slug":2562,"vimeo_id":2563,"description":2564,"tile":2565,"length":177,"resources":12,"people":12,"episode_number":213,"published":2541,"title":2566,"video_transcript_html":2567,"video_transcript_text":2568,"content":12,"seo":2569,"status":19,"episode_people":2570,"recommendations":2572,"season":2573},"directus-labs-spreadsheet-layout","959645214","Explore the new Spreadsheet Layout on Directus Marketplace for efficient data editing, launched as part of our new Directus Labs project. ","6be5e3ce-c8f4-489e-b0ed-ca8ebe6127da","Directus Labs & Spreadsheet Layout","\u003Cp>Pedro: Those of you who are eagle eyed may have noticed a start to publish packages under the Directus Labs name. Directus Labs is our space to build experiments and gather feedback. Now not everything that's published by Directus Labs will be maintained the same way as the core project, but it does give us room to try out new things and respond to our users. In fact, last month's Directus AI announcement was a project launched inside of Directus Labs. So in case you missed it, Directus AI is a set of extensions that you can install from the marketplace and leverage best in class AI platforms directly within Directus.\u003C\u002Fp>\n\u003Cp>So whether you're looking to rewrite text, generate images, run text analysis, or or moderation, these building blocks can be built into your workflows with Directus Automate. You can find out more about Directus AI by checking out our Directus TV series going through each of these extensions in more detail. And today, we're announcing a new Directus Labs project, which closes out one of our most requested features and one that I've been waiting for for a long time, a spreadsheet layout. So now you can edit items directly from within Directus Explore, either automatically saving after each edit or manually saving after you've made all the changes. This may sound small, but it's gonna be a huge change for teams that need to edit data often.\u003C\u002Fp>\n\u003Cp>This isn't the only new extension we've published through Directsys Labs, but it's certainly one of the most impactful. We specifically want to shout out our community member, Florian, for working with us on this project. Directus Labs allows us to ship new integrations, interfaces, and automations really quickly and embrace Directus as a super extensible platform ready for your projects.\u003C\u002Fp>","Those of you who are eagle eyed may have noticed a start to publish packages under the Directus Labs name. Directus Labs is our space to build experiments and gather feedback. Now not everything that's published by Directus Labs will be maintained the same way as the core project, but it does give us room to try out new things and respond to our users. In fact, last month's Directus AI announcement was a project launched inside of Directus Labs. So in case you missed it, Directus AI is a set of extensions that you can install from the marketplace and leverage best in class AI platforms directly within Directus. So whether you're looking to rewrite text, generate images, run text analysis, or or moderation, these building blocks can be built into your workflows with Directus Automate. You can find out more about Directus AI by checking out our Directus TV series going through each of these extensions in more detail. And today, we're announcing a new Directus Labs project, which closes out one of our most requested features and one that I've been waiting for for a long time, a spreadsheet layout. So now you can edit items directly from within Directus Explore, either automatically saving after each edit or manually saving after you've made all the changes. This may sound small, but it's gonna be a huge change for teams that need to edit data often. This isn't the only new extension we've published through Directsys Labs, but it's certainly one of the most impactful. We specifically want to shout out our community member, Florian, for working with us on this project. Directus Labs allows us to ship new integrations, interfaces, and automations really quickly and embrace Directus as a super extensible platform ready for your projects.","a6455525-e932-4edd-8eed-05e2ff2e4ea2",[2571],"2fa0ad95-1ee8-4f03-82f7-1b114f81fc6e",[],{"year":978,"number":2550,"id":2551,"episodes":2574,"show":2575},[2553,2554,2555,2556,2557,2558,2536,2559],{"title":151,"slug":152},{"id":2554,"slug":2577,"vimeo_id":2578,"description":2579,"tile":2580,"length":264,"resources":12,"people":12,"episode_number":177,"published":2541,"title":2581,"video_transcript_html":2582,"video_transcript_text":2583,"content":12,"seo":2584,"status":19,"episode_people":2585,"recommendations":2587,"season":2588},"directus-11-rc","959645858","Directus 11 is here with policies - our key new feature making access control more powerful and flexible in your projects.","1030116d-a508-4c55-b0a1-5d519035a9c8","Directus 11 Release Candidate","\u003Cp>Rijk: I'm excited to announce today how we're overhauling access control in Directus. We spent the last months researching and developing the next big iteration of permissions that we're calling policies. Policies allow you to compose the access control for your roles and users. Each role and user can have 1 or more policies which can be reused across the system. In direct list 10, permissions are directly attached to roles.\u003C\u002Fp>\n\u003Cp>This means that you may have many similar roles with duplicated permissions to achieve the granularity that your project requires. Before, a user could only have one set of permissions based on their role. A policy, however, is a set of permissions that, like today, allow you to control what a user can see or do across your collections. Policies are effectively an abstraction allowing for 1 or more permissions to be reused across roles or users. You will be able to add policies directly to a role, a user, or both.\u003C\u002Fp>\n\u003Cp>For example, when managing a website you could create a page edit policy that contains all the permissions required to be able to edit the website's pages, which you can then attach to your marketing and engineering roles. Or, for example, when generating sales reports, you could create a view quarterly results policy that allows your analysts, account executives, and demand team to view the quarterly results. A user or role can have multiple of these policies, so each policy can be very granular as opposed to having one role that has to contain everything. Much easier to manage. Another example, when configuring a multi tenant system, you can now create a role per tenant but have all of the permissions for those roles rely on the same shared policies, allowing you to manage the permissions for each user in a centralized place while keeping the tenants separated by role.\u003C\u002Fp>\n\u003Cp>Policies allow for new ways of organizing and managing your permission sets. We'll be providing these examples and more in our new docs platform coming later this summer. All these changes might sound like a lot at first, but rest assured, as with the releases of directus 10, we're providing an automatic migration between directus 10 and directus 11 to help you get started with policies. We're very excited to give you this new flexibility, which is perfect for simplifying your more complicated projects. Come chat with us about this and more at 11 AM EST in our town hall in Discord.\u003C\u002Fp>","I'm excited to announce today how we're overhauling access control in Directus. We spent the last months researching and developing the next big iteration of permissions that we're calling policies. Policies allow you to compose the access control for your roles and users. Each role and user can have 1 or more policies which can be reused across the system. In direct list 10, permissions are directly attached to roles. This means that you may have many similar roles with duplicated permissions to achieve the granularity that your project requires. Before, a user could only have one set of permissions based on their role. A policy, however, is a set of permissions that, like today, allow you to control what a user can see or do across your collections. Policies are effectively an abstraction allowing for 1 or more permissions to be reused across roles or users. You will be able to add policies directly to a role, a user, or both. For example, when managing a website you could create a page edit policy that contains all the permissions required to be able to edit the website's pages, which you can then attach to your marketing and engineering roles. Or, for example, when generating sales reports, you could create a view quarterly results policy that allows your analysts, account executives, and demand team to view the quarterly results. A user or role can have multiple of these policies, so each policy can be very granular as opposed to having one role that has to contain everything. Much easier to manage. Another example, when configuring a multi tenant system, you can now create a role per tenant but have all of the permissions for those roles rely on the same shared policies, allowing you to manage the permissions for each user in a centralized place while keeping the tenants separated by role. Policies allow for new ways of organizing and managing your permission sets. We'll be providing these examples and more in our new docs platform coming later this summer. All these changes might sound like a lot at first, but rest assured, as with the releases of directus 10, we're providing an automatic migration between directus 10 and directus 11 to help you get started with policies. We're very excited to give you this new flexibility, which is perfect for simplifying your more complicated projects. Come chat with us about this and more at 11 AM EST in our town hall in Discord.","70a5b4d3-78bf-47cb-b55a-156211b6a239",[2586],"2e0d3c2f-e239-4529-9263-b4e0574d7a41",[],{"year":978,"number":2550,"id":2551,"episodes":2589,"show":2590},[2553,2554,2555,2556,2557,2558,2536,2559],{"title":151,"slug":152},{"id":2556,"slug":2592,"vimeo_id":2593,"description":2594,"tile":2595,"length":13,"resources":12,"people":12,"episode_number":159,"published":2541,"title":2596,"video_transcript_html":2597,"video_transcript_text":2598,"content":12,"seo":2599,"status":19,"episode_people":2600,"recommendations":2602,"season":2603},"extended-security-updates-directus-10-12","959693784","Announcing Extended Security Updates (ESU) for Directus 10.12 until the end of 2024, ensuring critical security updates and a flexible upgrade timeframe for our users.","ac653ef1-0d5b-45c9-9637-464569f31d8d","Extended Security Updates for Directus 10.12","\u003Cp>Kevin: Up until now we've only supported the very latest version of But we've heard that this can be difficult for users, needing to sometimes adapt your project to meet the needs of later versions when perhaps all you need is security updates. So Directus 10.12 will be the 1st version of Directus to receive extended security updates. We want you to think of this as providing an upgrade window to update and take advantage of latest features while being able to keep your project secure in the meantime. Directus 10.12 will receive extended security updates until the end of 2024.\u003C\u002Fp>","Up until now we've only supported the very latest version of But we've heard that this can be difficult for users, needing to sometimes adapt your project to meet the needs of later versions when perhaps all you need is security updates. So Directus 10.12 will be the first version of Directus to receive extended security updates. We want you to think of this as providing an upgrade window to update and take advantage of latest features while being able to keep your project secure in the meantime. Directus 10.12 will receive extended security updates until the end of 2024.","2497dc12-00ff-44b4-ace0-b4de995bdb48",[2601],"c95c001d-f5b3-4f22-b1e3-8d4c345e649a",[],{"year":978,"number":2550,"id":2551,"episodes":2604,"show":2605},[2553,2554,2555,2556,2557,2558,2536,2559],{"title":151,"slug":152},{"id":2555,"slug":2607,"vimeo_id":2608,"description":2609,"tile":2610,"length":177,"resources":12,"people":12,"episode_number":264,"published":2541,"title":2611,"video_transcript_html":2612,"video_transcript_text":2613,"content":12,"seo":2614,"status":19,"episode_people":2615,"recommendations":2617,"season":2618},"public-user-registration","959645678","We've shipped public user registration in Directus 10, allowing users to register for your project without the need for complex permission setups. ","7d4664fa-82fd-4a08-8714-631456d50754","Public User Registration","\u003Cp>Daniel: In Directus 10.11, we introduced public user registration. This allows for users to register new accounts directly from the login screen or via the API. Let me show you how it works. Once enabled in your project settings, users will see a link on the sign in screen. They click it, register, and can immediately log in.\u003C\u002Fp>\n\u003Cp>You can also enable email verification, and they and they won't be able to log in. You can also set up rules that the email address must match. This is more powerful than just enabling email allow listing as you can set more complex rules based on your needs. And then in Directus 10.12, we applied feedback about this feature and created a set of environment variables to control how many users can exist in your project. Before now, you had to use a series of perfectly configured settings to enable public registration in a separate application or custom extensions.\u003C\u002Fp>\n\u003Cp>We're always looking to apply your feedback to directors. We hope this feature makes your development easier.\u003C\u002Fp>","In Directus 10.11, we introduced public user registration. This allows for users to register new accounts directly from the login screen or via the API. Let me show you how it works. Once enabled in your project settings, users will see a link on the sign in screen. They click it, register, and can immediately log in. You can also enable email verification, and they and they won't be able to log in. You can also set up rules that the email address must match. This is more powerful than just enabling email allow listing as you can set more complex rules based on your needs. And then in Directus 10.12, we applied feedback about this feature and created a set of environment variables to control how many users can exist in your project. Before now, you had to use a series of perfectly configured settings to enable public registration in a separate application or custom extensions. We're always looking to apply your feedback to directors. We hope this feature makes your development easier.","d60fe98c-1da3-404d-a805-6de4062a49d6",[2616],"1d22ac07-b656-4128-8e3c-513647e9e4ab",[],{"year":978,"number":2550,"id":2551,"episodes":2619,"show":2620},[2553,2554,2555,2556,2557,2558,2536,2559],{"title":151,"slug":152},{"id":2553,"slug":2622,"vimeo_id":2623,"description":2624,"tile":2625,"length":194,"resources":12,"people":12,"episode_number":13,"published":2541,"title":2626,"video_transcript_html":2627,"video_transcript_text":2628,"content":12,"seo":2629,"status":19,"episode_people":2630,"recommendations":2639,"season":2640},"leap-week-3-keynote","959681429","The full keynote from our third Leap Week in June 2024.","fd848d69-a531-4b37-89be-d8ca21023625","Leap Week 3: Full Keynote","\u003Cp>Speaker 0: Welcome to my office and to Leap week 3. It's already been 3 months since the last one, and we've put together what we hope you'll find to be a very enjoyable week of events. This kickoff is split into 3 sections, Directus Open Source, Content and Community, and finally, What's New for Partners and Customers. Just after this broadcast at 11 AM Eastern, we're inviting you to talk about our announcements in an event in our Discord community server. Tomorrow, there's an event all about how agencies source and assess technologies, moderated by our CEO, Ben Haines, with guests from and Work and Co.\u003C\u002Fp>\u003Cp>Tomorrow, there's also an exclusive workshop for Directors Plus members, where you'll build a global search module that will search across all collections in your project. On Wednesday, we're partnering with our friends at Twilio for a hands on workshop on building an outbound phone system in Directus Insights. On Wednesday, there's also a live episode of a 100 apps in a 100 hours, where we challenge Brian to try and build a project in just 60 minutes. He'll be joined by some of our colleagues and friends who will either make things easier or much, much harder. On Thursday, we're running another workshop with voice AI company Deepgram, where we'll build new automations within Directus to create audio summaries of YouTube videos.\u003C\u002Fp>\u003Cp>And finally, on Friday, we invite all of you to a community networking social. You can find all of the events listed on our event page, on our Discord events tab, and in your calendar if you have a Leap Week ticket. Just over a year ago, we released Directus 10, a really important moment for us in creating premium and sustainable open source software. Internally, we ran what we called the summer of Directus 10, where we could renew our focus in rapidly bringing out new features for everyone, including live preview, real time, a new SDK, and a lot more. In a landscape where lots of companies have changed their license unexpectedly, we invited you, our community and users, to help figure out the right steps to take.\u003C\u002Fp>\u003Cp>And we've recently published how that change has gone so far. Today, we're not announcing Directus 10.13, but instead the release candidate of the next major version of Directus, Directus 11. To talk to you more about what's new, I want to hand over to our co founder, CTO, and lead maintainer, Ryke.\u003C\u002Fp>\u003Cp>Speaker 1: I'm excited to announce today how we're overhauling access control in Directus. We spent the last months researching and developing the next big iteration of permissions that we're calling policies. Policies allow you to compose the access control for your roles and users. Each role and user can have 1 or more policies, which can be reused across the system. Indirect is 10.\u003C\u002Fp>\u003Cp>Permissions are directly attached to roles. This means that you may have many similar roles with duplicated permissions to achieve the granularity that your project requires. Before, the user could only have one set of permissions based on their role. A policy, however, is a set of permissions that, like today, allow you to control what a user can see or do across your collections. Policies are effectively an abstraction, allowing for 1 or more permissions to be reused across roles or users.\u003C\u002Fp>\u003Cp>You will be able to add policies directly to a role, a user, or both. For example, when managing a website, you could create a page edit policy that contains all the permissions required to be able to edit the website's pages, which you can then attach to your marketing and engineering roles. Or for example, when generating sales reports, you could create a view quarterly results policy that allows your analysts, account executives, and demand team to view the quarterly results. The user or role can have multiple of these policies so each policy can be very granular as opposed to having one role that has to contain everything. Much easier to manage.\u003C\u002Fp>\u003Cp>Another example, when configuring a multi tenant system, you can now create a role per tenant but have all of the permissions for those roles rely on the same shared policies, allowing you to manage the permissions for each user in a centralized place while keeping the tenants separated by role. Policies allow for new ways of organizing and managing your permission sets. We'll be providing these examples and more in our new docs platform coming later this summer. All these changes might sound like a lot at first, but rest assured, as with the releases of Directus 10, we're providing an automatic migration between Directus 10 and Directus 11 to help you get started with policies. We're very excited to give you this new flexibility which is perfect for simplifying your more complicated projects.\u003C\u002Fp>\u003Cp>Come chat with us about this and more at 11 AM EST in our town hall in Discord.\u003C\u002Fp>\u003Cp>Speaker 0: Being such an important and major change to how permissions and access control works in Directus, we've decided to first publish a release candidate version of directors 11 to get it into your hands and crush any last remaining bugs that may remain before general availability. The directors 11 release candidate will be available this week with a general availability in a few weeks' time. Now since the last sleep week, we've also released a feature that's been requested for ages. Over to Daniel to tell you more.\u003C\u002Fp>\u003Cp>Speaker 2: In Directus 10.11, we introduced public user registration. This allows for users to register new accounts directly from the login screen or via the API. Let me show you how it works. Once enabled in your project settings, users will see a link on the sign in screen. They click it, register, and can immediately log in.\u003C\u002Fp>\u003Cp>You can also enable email verification, which will send the user an email with a link they need to click on. Until they do, the user account will be marked as unverified and they won't be able to log in. You can also set up rules that the email address must match. This is more powerful than just enabling email allow listing as you can set more complex rules based on your needs. And then in Directus 10.12, we applied feedback about this feature and created a set of environment variables to control how many users can exist in your project.\u003C\u002Fp>\u003Cp>Before now, you had to use a series of perfectly configured settings to enable public registration in a separate application or custom extensions. We're always looking to apply your feedback to Directus. We hope this feature makes your development easier.\u003C\u002Fp>\u003Cp>Speaker 0: Up until now, we've only supported the very latest version of Directus. But we've heard that this can be difficult for users, needing to sometimes adapt your project to meet the needs of later versions when perhaps all you need is security updates. So Directus 10.12 will be the first version of Directus to receive extended security updates. We want you to think of this as providing an upgrade window to update and take advantage of latest features while being able to keep your project secure in the meantime. Directus 10.12 will receive extended security updates until the end of 2024.\u003C\u002Fp>\u003Cp>Now we've been releasing more than just the Director's core project in the last few months, and I want to hand over to Pedro to tell you more.\u003C\u002Fp>\u003Cp>Speaker 3: Those of you who are eagle eyed may have noticed a start to publish packages under the Directus Labs name. Directus Labs is our space to build experiments and gather feedback. Now not everything that's published by Directus Labs will be maintained the same way as the core project, but it does give us room to try out new things and respond to our users. In fact, last month's Directus AI announcement was a project launched inside of Directus Labs. So in case you missed it, Directus AI is a set of extensions that you can install from the marketplace and leverage best in class AI platforms directly within Directus.\u003C\u002Fp>\u003Cp>So whether you're looking to rewrite text, generate images, run text analysis, or or moderation, these building blocks can be built into your workflows with Directus Automate. You can find out more about Directus AI by checking out our Directus TV series going through each of these extensions in more detail. And today, we're announcing new Directus Labs project, which closes out one of our most requested features and one that I've been waiting for for a long time, a spreadsheet layout. So now you can edit items directly from within Directus Explore, either automatically saving after each edit or manually saving after you've made all the changes. This may sound small, but it's gonna be a huge change for teams that need to edit data often.\u003C\u002Fp>\u003Cp>This isn't the only new extension we published through DXIS Labs, but it's certainly one of the most impactful. We specifically wanna shout out our community member, Florian, for working with us on this project. Directus Labs allows us to ship new integrations, interfaces, and automations really quickly and embrace Directus as a super extensible platform ready for your projects. Talking about extensions, I wanna hand it over to Benny to tell you more.\u003C\u002Fp>\u003Cp>Speaker 4: Good day, everyone. I'm Benny, and I have the privilege of talking about our plans for the extensions and marketplace ecosystem. I recently joined the team with the goal of focusing on the developer and user experiences of Directus Extensions. This includes the overall extension development and deployment lifecycle, a public facing marketplace, and the underlying registry API. Many of you have been wondering what will come next for the marketplace since our release of the beta at our last Leap Week.\u003C\u002Fp>\u003Cp>Let me start by thanking each of you who have contributed to testing and providing feedback on the marketplace in any way. Let's discuss the developer experience first. One of the key parts of improving the developer experience, and also partly the discoverability of extensions, is in bringing new capabilities to the extensions sandbox SDK. SDK. With the upcoming release of our new policy system, we are expecting to be able to provide granular access to things like the underlying file systems, users, notifications, and emails.\u003C\u002Fp>\u003Cp>One of the biggest opportunities for improving the sandbox that we have identified is of course enabling support for importing external libraries. Even though this may be a significant technical challenge to implement whilst retaining the important security benefits of having a sandbox, we are looking forward to solving this for you. Okay, so what about new extension features? We are exploring how to augment existing extensions, how to deploy data and config templates using the same APIs as extensions, how to add functionality to allow developers to define extension specific settings, and we are looking at increasing the amount of life cycle events available to extension developers. We are frequently asked how to add new plugins to the built in block editor for example, as well as adding tweaks to other extensions and experiences.\u003C\u002Fp>\u003Cp>Right now this is cumbersome, and we think that extensions will benefit from being able to install lightweight enhancements. This means extension developers will be able to package the core functionality for their custom developed user experiences, whilst being able to allow others to build on their work. Being able to deploy templates and config via extension system will allow users to include things like data structures and email templates in a controlled way. Extension settings being configurable in the app will allow the inclusion of API keys and other configuration that isn't dependent on having access to environment variables at deploy time. Our life cycle hooks will allow for better management of installing and uninstalling extensions.\u003C\u002Fp>\u003Cp>These are just some of the features we are planning to add to the road map soon to help craft our growing extension library. The last set of work we will be focusing on before moving the marketplace out of beta will ensure your extensions are seen by the widest audience possible. Our marketplace listings will be more dynamic and easier to find what you are looking for. This will include providing more options for meta information to control elements such as how details about individual contributors are displayed, as well as adding elements like hero screenshots and logos. This may also include enhancements to the configurable meta information like better tagging options to improve searching for extensions.\u003C\u002Fp>\u003Cp>To help increase visibility, we are looking at how to make extensions listed in the marketplace discoverable outside of the director's studio so that anyone can link to them for consideration outside of an existing project. We also want to provide extension authors with insights into how their extensions are performing. This may include being able to do things like react to comments, reviews, and feedback from users as a verified extension author. Finally, we are looking at how to indicate the quality of each of the extensions public in the marketplace in a clear and transparent way. This will help users get the best experience and provide developers clear guidelines on how to produce high quality extensions.\u003C\u002Fp>\u003Cp>These new changes will be implemented in both the data studio as well as the registry API. Once these changes are implemented, we will publish the registry API spec to enable developers to publish and maintain their own additional registries. As you have heard, there is a lot going into the marketplace. We are looking forward to sharing the roadmap with you in the next couple of weeks, and you'll be able to see the priority of item stem. We wanted to take this opportunity to provide some insight into how the roadmap is being developed and what will be coming.\u003C\u002Fp>\u003Cp>Once this work is done, the marketplace will be ready for general availability. We hope you are as excited as we are for some of these upcoming changes. We are really passionate about the directors extension ecosystem, and I can't wait to see what you create.\u003C\u002Fp>\u003Cp>Speaker 0: No. Hang on. We gotta go back. We gotta do that one again. Sorry.\u003C\u002Fp>\u003Cp>Speaker 5: Hello, everybody. Now let's dive into our theme. So for this, I'm gonna pull out my trusty palette knife. This is not gonna be good for my lisp, but we're gonna try it anyways.\u003C\u002Fp>\u003Cp>Speaker 6: It's just a really great tool and I've just been really enjoying still using tyform all these years later.\u003C\u002Fp>\u003Cp>Speaker 0: So let's give that just a moment, and there are the maple trees again.\u003C\u002Fp>\u003Cp>Speaker 7: I like this because every time I put it on, it reminds me that some tech is just rubbish.\u003C\u002Fp>\u003Cp>Speaker 0: Not terribly elegant, but we're just gonna throw a new error.\u003C\u002Fp>\u003Cp>Speaker 5: Hop open my inbox. Boom. There we can see the message. Let's take a look at our logs. We have got some logs in here.\u003C\u002Fp>\u003Cp>Holy moly. I guess this is gonna be all sorts of copyright infringement here. I wanna see your facial expressions because I wanna know if this is a terrible idea. This is for entertainment purposes only. Any lawyers watching this, just so we know.\u003C\u002Fp>\u003Cp>Speaker 8: The creative code for design and development depending on which alliteration you choose.\u003C\u002Fp>\u003Cp>Speaker 9: Keyboards are probably the main one that I could talk about where I have a bunch of custom mechanical keyboards. I have some behind me. I've got a bunch all over the place.\u003C\u002Fp>\u003Cp>Speaker 0: Let's get started with Directus and Astro.\u003C\u002Fp>\u003Cp>Speaker 2: Welcome to another exciting episode of, Request Review, where we go over your hopes, wishes, and dreams and potentially crush them.\u003C\u002Fp>\u003Cp>Speaker 0: This was a huge amount of fun. It was just a little quick project which I think is gonna actually enrich my day to day life.\u003C\u002Fp>\u003Cp>Speaker 5: Oh, hey. It's visit from the cat. I was hoping he'd show up.\u003C\u002Fp>\u003Cp>Speaker 6: No. It's okay. Christopher.\u003C\u002Fp>\u003Cp>Speaker 5: What does it all mean?\u003C\u002Fp>\u003Cp>Speaker 6: Thanks for joining me and, I'll see you\u003C\u002Fp>\u003Cp>Speaker 8: somewhere.\u003C\u002Fp>\u003Cp>Speaker 0: No. No. No. If you're if you're okay with it. I love that as the\u003C\u002Fp>\u003Cp>Speaker 9: Alright. Fine.\u003C\u002Fp>\u003Cp>Speaker 0: Next, Directus TV. If you've not heard of it, it's our streaming platform which brings together education, entertainment, and stories from across the director's ecosystem. Every week we publish new episodes across our 30 shows. Since the last leap week, we've released buzzword wilderness, which has Matt asking if it's real or just marketing. Democratizing data where we bring new life to open datasets with directors and new seasons of both Trace Talks and a 100 apps in a 100 hours.\u003C\u002Fp>\u003Cp>Today we are announcing some new shows. In t I l, that's technically I'm lost, Matt dives deep into the world of code. Along the way he shares his discoveries, successes and occasional missteps making tech more accessible to nontechnical professionals. In authentication avenue, we travel down the world of all things authentication, authorization, and access control. Director's auth is hugely powerful and we're excited to help you better understand it.\u003C\u002Fp>\u003Cp>In dungeons and dashboards, we will build tools for our fellow dungeon masters, guides, and navigators, and build the ultimate toolkit for running campaigns with our parties.\u003C\u002Fp>\u003Cp>Speaker 9: And I'll be your host in Talking Heads, Coding Hands. It's a developer game show bringing together elements from other great games you may know.\u003C\u002Fp>\u003Cp>Speaker 7: Join our contestants as they talk and code against the clock.\u003C\u002Fp>\u003Cp>Speaker 0: Thanks for that, Cassidy. There are even more shows hitting your screen this summer, so you can get truly lost down the rabbit hole. Now everything we do is for our community, so I'm going to pass over to Beth, the newest member of our developer relations team, for the next announcement.\u003C\u002Fp>\u003Cp>Speaker 6: Directors is a project that has only been successful thanks to our vibrant community. We have 12500 members in our Discord server, where community members help each other better understand and build with Directus, show off what they have built and collaborate. We have 100 of GitHub contributors and tens of thousands of GitHub stars. All of this has helped shape Directus and we are super thankful for the love and care the community give. We are often asked how people can contribute to the project beyond just code.\u003C\u002Fp>\u003Cp>So today, we are announcing the Directus Community Experts Programme. This programme supports you in bringing Directus to your own communities and networks across the world. At launch, this will be a program focused on events, both bringing Directors to events named you and running new events with our support. We've designed this program to make sure there is loads for you to get out of it and of course if writing is more your thing our guest author program remains open for submission. Register interest in joining the Community Experts program at directors.\u003C\u002Fp>\u003Cp>Iscommunityexperts by the end of June, where we'll be reviewing and sending more information across.\u003C\u002Fp>\u003Cp>Speaker 0: Thank you so much for that, Beth, and I'm super excited to be putting together the Directus community experts program. Now moving on, our agency partners are a really important part of how we reach customers and make sure their projects are successful. And at the last Leap Week, Colton announced our revamp partner program. Since then, more has happened, so I want to hand over to him to tell you more.\u003C\u002Fp>\u003Cp>Speaker 8: It's been an exciting past few months since we announced our new partner program. The feedback from the community and agency partners has been amazing. You can find direct us partners in over 35 countries to help build your projects. No matter where you're located, we have great partners like Sunzanet based in Germany, HarmonyX based in Thailand, Rockulab based in Australia, untile based in Portugal, and Echobind based in the USA. All of these agencies are experts in consulting, developing, and supporting your projects.\u003C\u002Fp>\u003Cp>We can't wait to introduce you to our full list of agency partners through the new partner directory and also showcase them through agency corner, a new show launching on Directus TV this July. Alongside the partner directory and agency corner, our certification path for new partners is dropping with the latest educational material, including our newly announced policies feature. Tomorrow is a live bridging bytes event on how agencies assess and buy technologies. Our CEO Ben is hosting with guests from and work and co. You're now going to hear from Bryant for our final set of announcements today.\u003C\u002Fp>\u003Cp>Speaker 5: It's been 3 months since we released Directus Plus, our premium companion subscription for developers building with Directus. The subscription includes access to powerful starter kits designed to help you ship projects faster, and advanced workshops that are exclusive to members. So far, we've got over 100 plus individual developers and agencies in the Directus Plus program. And we've gotten a ton of great feedback. But before I share some exciting new updates, first, let's run through all the things we've shipped to date.\u003C\u002Fp>\u003Cp>We've released a total of 7 starter kits for a lot of different use cases. A learning management system to build custom courseware. A PIM system to manage all of your product data and catalogs. A multi tenant SaaS application backend. A video streaming platform, similar to Directus TV.\u003C\u002Fp>\u003Cp>A status page to keep users informed of any incidents or downtime. An onboarding checklist application that is perfect for managing new employees or even new clients. And our most recent starter kit, an AI content machine that works with our Directus AI extensions to absolutely crush your content workflow. Generate ideas from notes and recordings. Create first drafts with a click of a button, and automatically translate content to multiple languages.\u003C\u002Fp>\u003Cp>We've also completed 2 workshops. In Minimum Viable Billing, we used Directus Flows to accept payments for online products via Stripe. In database to data engine, we gave a sad lonely SQL database, new purpose, and equipped our non technical team members with rest APIs, dashboards and automated workflows. Now, I'm sure Kev is saying, okay, enough of the past, Brian. Tell them what's coming up.\u003C\u002Fp>\u003Cp>So let's hop into the updates. Our next starter kit drops this week. It's a virtual event registration platform, inspired by our own Leap Week site. You can now stop stringing together 30 different services to run online events for your company, or for your clients. It's also the 1st Starter Kit to include a front end that already has all the plumbing connected to the Directus Backend, enabling you to ship even faster.\u003C\u002Fp>\u003Cp>It includes registration and ticketing, personalized social images, a referral tracking system, and more. I'm also excited to announce a new Directus plus team plan. We heard your feedback. And for Teams, you wanted an easier way to share the value that Directus Plus offers. Starter kits, training, and access to the private community channels.\u003C\u002Fp>\u003Cp>Pricing for the Team Plan will be 5.99 per year, which includes up to 20 developers. That's a huge savings over the individual plan. The team plan will be rolling out over the next couple weeks, so stay tuned to your inbox. Lastly, the introductory period and the promotional pricing of $99 for the year will be available for just 2 more weeks. On July 1st, the cost of an individual plan for Directus Plus will go up to 2.99 annually.\u003C\u002Fp>\u003Cp>As we've built and released more starter kits, and firmed up the offering, this felt like the right moment to launch Directus Plus more fully. And with that, back to\u003C\u002Fp>\u003Cp>Speaker 0: you, Kip. Today, we announced Directus 11 with policies, a concept that builds a new level of flexibility into your access control, along with a 6 month upgrade window in which Directus 10.12 will receive security updates. Pedro introduced Directus Labs and our new spreadsheet layout, and Benny talked you through our plans for extensions development and the Directus Marketplace. We teased what's new on director's TV this summer, with great new shows like Technically Unlost, Dungeons and Dashboards and Talking Heads Coding Hands, hosted by Cassidy Williams. Beth revealed direct to Community Experts, a new program to involve and reward community members who go above and beyond for each other.\u003C\u002Fp>\u003Cp>Colton shared more about our partner program, and Brian showed off what's new for Directus Plus. And it's only Monday. Join us after this at 11 AM Eastern for a community town hall, where you can come and talk to the core team about today's announcements. Tomorrow is Bridging Bites, and an exclusive workshop for Directors Plus. It's not too late to sign up and join in.\u003C\u002Fp>\u003Cp>On Wednesday, we're running a workshop on integrating Twilio with Directus and our second ever a 100 apps in a 100 hours live. On Thursday, we'll be building complex automations with Deepgram's voice AI tools. And finally, on Friday, we invite all of you to our community networking social. Thank you again on behalf of the directors core team, which now accounts for over 30 of us over 10 countries. Your enthusiasm and support is what keeps us focused on building the best back end for your projects.\u003C\u002Fp>\u003Cp>Until next time. Bye for now.\u003C\u002Fp>","Welcome to my office and to Leap week 3. It's already been 3 months since the last one, and we've put together what we hope you'll find to be a very enjoyable week of events. This kickoff is split into 3 sections, Directus Open Source, Content and Community, and finally, What's New for Partners and Customers. Just after this broadcast at 11 AM Eastern, we're inviting you to talk about our announcements in an event in our Discord community server. Tomorrow, there's an event all about how agencies source and assess technologies, moderated by our CEO, Ben Haines, with guests from and Work and Co. Tomorrow, there's also an exclusive workshop for Directors Plus members, where you'll build a global search module that will search across all collections in your project. On Wednesday, we're partnering with our friends at Twilio for a hands on workshop on building an outbound phone system in Directus Insights. On Wednesday, there's also a live episode of a 100 apps in a 100 hours, where we challenge Brian to try and build a project in just 60 minutes. He'll be joined by some of our colleagues and friends who will either make things easier or much, much harder. On Thursday, we're running another workshop with voice AI company Deepgram, where we'll build new automations within Directus to create audio summaries of YouTube videos. And finally, on Friday, we invite all of you to a community networking social. You can find all of the events listed on our event page, on our Discord events tab, and in your calendar if you have a Leap Week ticket. Just over a year ago, we released Directus 10, a really important moment for us in creating premium and sustainable open source software. Internally, we ran what we called the summer of Directus 10, where we could renew our focus in rapidly bringing out new features for everyone, including live preview, real time, a new SDK, and a lot more. In a landscape where lots of companies have changed their license unexpectedly, we invited you, our community and users, to help figure out the right steps to take. And we've recently published how that change has gone so far. Today, we're not announcing Directus 10.13, but instead the release candidate of the next major version of Directus, Directus 11. To talk to you more about what's new, I want to hand over to our co founder, CTO, and lead maintainer, Ryke. I'm excited to announce today how we're overhauling access control in Directus. We spent the last months researching and developing the next big iteration of permissions that we're calling policies. Policies allow you to compose the access control for your roles and users. Each role and user can have 1 or more policies, which can be reused across the system. Indirect is 10. Permissions are directly attached to roles. This means that you may have many similar roles with duplicated permissions to achieve the granularity that your project requires. Before, the user could only have one set of permissions based on their role. A policy, however, is a set of permissions that, like today, allow you to control what a user can see or do across your collections. Policies are effectively an abstraction, allowing for 1 or more permissions to be reused across roles or users. You will be able to add policies directly to a role, a user, or both. For example, when managing a website, you could create a page edit policy that contains all the permissions required to be able to edit the website's pages, which you can then attach to your marketing and engineering roles. Or for example, when generating sales reports, you could create a view quarterly results policy that allows your analysts, account executives, and demand team to view the quarterly results. The user or role can have multiple of these policies so each policy can be very granular as opposed to having one role that has to contain everything. Much easier to manage. Another example, when configuring a multi tenant system, you can now create a role per tenant but have all of the permissions for those roles rely on the same shared policies, allowing you to manage the permissions for each user in a centralized place while keeping the tenants separated by role. Policies allow for new ways of organizing and managing your permission sets. We'll be providing these examples and more in our new docs platform coming later this summer. All these changes might sound like a lot at first, but rest assured, as with the releases of Directus 10, we're providing an automatic migration between Directus 10 and Directus 11 to help you get started with policies. We're very excited to give you this new flexibility which is perfect for simplifying your more complicated projects. Come chat with us about this and more at 11 AM EST in our town hall in Discord. Being such an important and major change to how permissions and access control works in Directus, we've decided to first publish a release candidate version of directors 11 to get it into your hands and crush any last remaining bugs that may remain before general availability. The directors 11 release candidate will be available this week with a general availability in a few weeks' time. Now since the last sleep week, we've also released a feature that's been requested for ages. Over to Daniel to tell you more. In Directus 10.11, we introduced public user registration. This allows for users to register new accounts directly from the login screen or via the API. Let me show you how it works. Once enabled in your project settings, users will see a link on the sign in screen. They click it, register, and can immediately log in. You can also enable email verification, which will send the user an email with a link they need to click on. Until they do, the user account will be marked as unverified and they won't be able to log in. You can also set up rules that the email address must match. This is more powerful than just enabling email allow listing as you can set more complex rules based on your needs. And then in Directus 10.12, we applied feedback about this feature and created a set of environment variables to control how many users can exist in your project. Before now, you had to use a series of perfectly configured settings to enable public registration in a separate application or custom extensions. We're always looking to apply your feedback to Directus. We hope this feature makes your development easier. Up until now, we've only supported the very latest version of Directus. But we've heard that this can be difficult for users, needing to sometimes adapt your project to meet the needs of later versions when perhaps all you need is security updates. So Directus 10.12 will be the first version of Directus to receive extended security updates. We want you to think of this as providing an upgrade window to update and take advantage of latest features while being able to keep your project secure in the meantime. Directus 10.12 will receive extended security updates until the end of 2024. Now we've been releasing more than just the Director's core project in the last few months, and I want to hand over to Pedro to tell you more. Those of you who are eagle eyed may have noticed a start to publish packages under the Directus Labs name. Directus Labs is our space to build experiments and gather feedback. Now not everything that's published by Directus Labs will be maintained the same way as the core project, but it does give us room to try out new things and respond to our users. In fact, last month's Directus AI announcement was a project launched inside of Directus Labs. So in case you missed it, Directus AI is a set of extensions that you can install from the marketplace and leverage best in class AI platforms directly within Directus. So whether you're looking to rewrite text, generate images, run text analysis, or or moderation, these building blocks can be built into your workflows with Directus Automate. You can find out more about Directus AI by checking out our Directus TV series going through each of these extensions in more detail. And today, we're announcing new Directus Labs project, which closes out one of our most requested features and one that I've been waiting for for a long time, a spreadsheet layout. So now you can edit items directly from within Directus Explore, either automatically saving after each edit or manually saving after you've made all the changes. This may sound small, but it's gonna be a huge change for teams that need to edit data often. This isn't the only new extension we published through DXIS Labs, but it's certainly one of the most impactful. We specifically wanna shout out our community member, Florian, for working with us on this project. Directus Labs allows us to ship new integrations, interfaces, and automations really quickly and embrace Directus as a super extensible platform ready for your projects. Talking about extensions, I wanna hand it over to Benny to tell you more. Good day, everyone. I'm Benny, and I have the privilege of talking about our plans for the extensions and marketplace ecosystem. I recently joined the team with the goal of focusing on the developer and user experiences of Directus Extensions. This includes the overall extension development and deployment lifecycle, a public facing marketplace, and the underlying registry API. Many of you have been wondering what will come next for the marketplace since our release of the beta at our last Leap Week. Let me start by thanking each of you who have contributed to testing and providing feedback on the marketplace in any way. Let's discuss the developer experience first. One of the key parts of improving the developer experience, and also partly the discoverability of extensions, is in bringing new capabilities to the extensions sandbox SDK. SDK. With the upcoming release of our new policy system, we are expecting to be able to provide granular access to things like the underlying file systems, users, notifications, and emails. One of the biggest opportunities for improving the sandbox that we have identified is of course enabling support for importing external libraries. Even though this may be a significant technical challenge to implement whilst retaining the important security benefits of having a sandbox, we are looking forward to solving this for you. Okay, so what about new extension features? We are exploring how to augment existing extensions, how to deploy data and config templates using the same APIs as extensions, how to add functionality to allow developers to define extension specific settings, and we are looking at increasing the amount of life cycle events available to extension developers. We are frequently asked how to add new plugins to the built in block editor for example, as well as adding tweaks to other extensions and experiences. Right now this is cumbersome, and we think that extensions will benefit from being able to install lightweight enhancements. This means extension developers will be able to package the core functionality for their custom developed user experiences, whilst being able to allow others to build on their work. Being able to deploy templates and config via extension system will allow users to include things like data structures and email templates in a controlled way. Extension settings being configurable in the app will allow the inclusion of API keys and other configuration that isn't dependent on having access to environment variables at deploy time. Our life cycle hooks will allow for better management of installing and uninstalling extensions. These are just some of the features we are planning to add to the road map soon to help craft our growing extension library. The last set of work we will be focusing on before moving the marketplace out of beta will ensure your extensions are seen by the widest audience possible. Our marketplace listings will be more dynamic and easier to find what you are looking for. This will include providing more options for meta information to control elements such as how details about individual contributors are displayed, as well as adding elements like hero screenshots and logos. This may also include enhancements to the configurable meta information like better tagging options to improve searching for extensions. To help increase visibility, we are looking at how to make extensions listed in the marketplace discoverable outside of the director's studio so that anyone can link to them for consideration outside of an existing project. We also want to provide extension authors with insights into how their extensions are performing. This may include being able to do things like react to comments, reviews, and feedback from users as a verified extension author. Finally, we are looking at how to indicate the quality of each of the extensions public in the marketplace in a clear and transparent way. This will help users get the best experience and provide developers clear guidelines on how to produce high quality extensions. These new changes will be implemented in both the data studio as well as the registry API. Once these changes are implemented, we will publish the registry API spec to enable developers to publish and maintain their own additional registries. As you have heard, there is a lot going into the marketplace. We are looking forward to sharing the roadmap with you in the next couple of weeks, and you'll be able to see the priority of item stem. We wanted to take this opportunity to provide some insight into how the roadmap is being developed and what will be coming. Once this work is done, the marketplace will be ready for general availability. We hope you are as excited as we are for some of these upcoming changes. We are really passionate about the directors extension ecosystem, and I can't wait to see what you create. No. Hang on. We gotta go back. We gotta do that one again. Sorry. Hello, everybody. Now let's dive into our theme. So for this, I'm gonna pull out my trusty palette knife. This is not gonna be good for my lisp, but we're gonna try it anyways. It's just a really great tool and I've just been really enjoying still using tyform all these years later. So let's give that just a moment, and there are the maple trees again. I like this because every time I put it on, it reminds me that some tech is just rubbish. Not terribly elegant, but we're just gonna throw a new error. Hop open my inbox. Boom. There we can see the message. Let's take a look at our logs. We have got some logs in here. Holy moly. I guess this is gonna be all sorts of copyright infringement here. I wanna see your facial expressions because I wanna know if this is a terrible idea. This is for entertainment purposes only. Any lawyers watching this, just so we know. The creative code for design and development depending on which alliteration you choose. Keyboards are probably the main one that I could talk about where I have a bunch of custom mechanical keyboards. I have some behind me. I've got a bunch all over the place. Let's get started with Directus and Astro. Welcome to another exciting episode of, Request Review, where we go over your hopes, wishes, and dreams and potentially crush them. This was a huge amount of fun. It was just a little quick project which I think is gonna actually enrich my day to day life. Oh, hey. It's visit from the cat. I was hoping he'd show up. No. It's okay. Christopher. What does it all mean? Thanks for joining me and, I'll see you somewhere. No. No. No. If you're if you're okay with it. I love that as the Alright. Fine. Next, Directus TV. If you've not heard of it, it's our streaming platform which brings together education, entertainment, and stories from across the director's ecosystem. Every week we publish new episodes across our 30 shows. Since the last leap week, we've released buzzword wilderness, which has Matt asking if it's real or just marketing. Democratizing data where we bring new life to open datasets with directors and new seasons of both Trace Talks and a 100 apps in a 100 hours. Today we are announcing some new shows. In t I l, that's technically I'm lost, Matt dives deep into the world of code. Along the way he shares his discoveries, successes and occasional missteps making tech more accessible to nontechnical professionals. In authentication avenue, we travel down the world of all things authentication, authorization, and access control. Director's auth is hugely powerful and we're excited to help you better understand it. In dungeons and dashboards, we will build tools for our fellow dungeon masters, guides, and navigators, and build the ultimate toolkit for running campaigns with our parties. And I'll be your host in Talking Heads, Coding Hands. It's a developer game show bringing together elements from other great games you may know. Join our contestants as they talk and code against the clock. Thanks for that, Cassidy. There are even more shows hitting your screen this summer, so you can get truly lost down the rabbit hole. Now everything we do is for our community, so I'm going to pass over to Beth, the newest member of our developer relations team, for the next announcement. Directors is a project that has only been successful thanks to our vibrant community. We have 12500 members in our Discord server, where community members help each other better understand and build with Directus, show off what they have built and collaborate. We have 100 of GitHub contributors and tens of thousands of GitHub stars. All of this has helped shape Directus and we are super thankful for the love and care the community give. We are often asked how people can contribute to the project beyond just code. So today, we are announcing the Directus Community Experts Programme. This programme supports you in bringing Directus to your own communities and networks across the world. At launch, this will be a program focused on events, both bringing Directors to events named you and running new events with our support. We've designed this program to make sure there is loads for you to get out of it and of course if writing is more your thing our guest author program remains open for submission. Register interest in joining the Community Experts program at directors. Iscommunityexperts by the end of June, where we'll be reviewing and sending more information across. Thank you so much for that, Beth, and I'm super excited to be putting together the Directus community experts program. Now moving on, our agency partners are a really important part of how we reach customers and make sure their projects are successful. And at the last Leap Week, Colton announced our revamp partner program. Since then, more has happened, so I want to hand over to him to tell you more. It's been an exciting past few months since we announced our new partner program. The feedback from the community and agency partners has been amazing. You can find direct us partners in over 35 countries to help build your projects. No matter where you're located, we have great partners like Sunzanet based in Germany, HarmonyX based in Thailand, Rockulab based in Australia, untile based in Portugal, and Echobind based in the USA. All of these agencies are experts in consulting, developing, and supporting your projects. We can't wait to introduce you to our full list of agency partners through the new partner directory and also showcase them through agency corner, a new show launching on Directus TV this July. Alongside the partner directory and agency corner, our certification path for new partners is dropping with the latest educational material, including our newly announced policies feature. Tomorrow is a live bridging bytes event on how agencies assess and buy technologies. Our CEO Ben is hosting with guests from and work and co. You're now going to hear from Bryant for our final set of announcements today. It's been 3 months since we released Directus Plus, our premium companion subscription for developers building with Directus. The subscription includes access to powerful starter kits designed to help you ship projects faster, and advanced workshops that are exclusive to members. So far, we've got over 100 plus individual developers and agencies in the Directus Plus program. And we've gotten a ton of great feedback. But before I share some exciting new updates, first, let's run through all the things we've shipped to date. We've released a total of 7 starter kits for a lot of different use cases. A learning management system to build custom courseware. A PIM system to manage all of your product data and catalogs. A multi tenant SaaS application backend. A video streaming platform, similar to Directus TV. A status page to keep users informed of any incidents or downtime. An onboarding checklist application that is perfect for managing new employees or even new clients. And our most recent starter kit, an AI content machine that works with our Directus AI extensions to absolutely crush your content workflow. Generate ideas from notes and recordings. Create first drafts with a click of a button, and automatically translate content to multiple languages. We've also completed 2 workshops. In Minimum Viable Billing, we used Directus Flows to accept payments for online products via Stripe. In database to data engine, we gave a sad lonely SQL database, new purpose, and equipped our non technical team members with rest APIs, dashboards and automated workflows. Now, I'm sure Kev is saying, okay, enough of the past, Brian. Tell them what's coming up. So let's hop into the updates. Our next starter kit drops this week. It's a virtual event registration platform, inspired by our own Leap Week site. You can now stop stringing together 30 different services to run online events for your company, or for your clients. It's also the 1st Starter Kit to include a front end that already has all the plumbing connected to the Directus Backend, enabling you to ship even faster. It includes registration and ticketing, personalized social images, a referral tracking system, and more. I'm also excited to announce a new Directus plus team plan. We heard your feedback. And for Teams, you wanted an easier way to share the value that Directus Plus offers. Starter kits, training, and access to the private community channels. Pricing for the Team Plan will be 5.99 per year, which includes up to 20 developers. That's a huge savings over the individual plan. The team plan will be rolling out over the next couple weeks, so stay tuned to your inbox. Lastly, the introductory period and the promotional pricing of $99 for the year will be available for just 2 more weeks. On July 1st, the cost of an individual plan for Directus Plus will go up to 2.99 annually. As we've built and released more starter kits, and firmed up the offering, this felt like the right moment to launch Directus Plus more fully. And with that, back to you, Kip. Today, we announced Directus 11 with policies, a concept that builds a new level of flexibility into your access control, along with a 6 month upgrade window in which Directus 10.12 will receive security updates. Pedro introduced Directus Labs and our new spreadsheet layout, and Benny talked you through our plans for extensions development and the Directus Marketplace. We teased what's new on director's TV this summer, with great new shows like Technically Unlost, Dungeons and Dashboards and Talking Heads Coding Hands, hosted by Cassidy Williams. Beth revealed direct to Community Experts, a new program to involve and reward community members who go above and beyond for each other. Colton shared more about our partner program, and Brian showed off what's new for Directus Plus. And it's only Monday. Join us after this at 11 AM Eastern for a community town hall, where you can come and talk to the core team about today's announcements. Tomorrow is Bridging Bites, and an exclusive workshop for Directors Plus. It's not too late to sign up and join in. On Wednesday, we're running a workshop on integrating Twilio with Directus and our second ever a 100 apps in a 100 hours live. On Thursday, we'll be building complex automations with Deepgram's voice AI tools. And finally, on Friday, we invite all of you to our community networking social. Thank you again on behalf of the directors core team, which now accounts for over 30 of us over 10 countries. Your enthusiasm and support is what keeps us focused on building the best back end for your projects. Until next time. Bye for now.","7fb1af38-0fdc-4889-9807-14bee931d9c9",[2631,2632,2633,2634,2635,2636,2637,2638],"ddd017e8-e179-4d0b-a7bb-2577e5968128","6a180043-5de7-41a4-9424-c0f3e13d30d3","397e8612-f4be-4259-875e-8438bb4c9217","1bd27721-b2bc-439a-a382-83fdc40aa463","9cb6b883-3af8-406d-b474-c58309a7f268","82852f77-6bf3-48a0-b0b4-c00756134f37","3d1e49bf-1481-45dd-9789-e176565265a4","697b1b92-c7b1-48d0-ae98-53bf0a002b5b",[],{"year":978,"number":2550,"id":2551,"episodes":2641,"show":2642},[2553,2554,2555,2556,2557,2558,2536,2559],{"title":151,"slug":152},{"id":2558,"slug":2644,"vimeo_id":2645,"description":2646,"tile":2647,"length":213,"resources":12,"people":12,"episode_number":195,"published":2541,"title":2648,"video_transcript_html":2649,"video_transcript_text":2650,"content":12,"seo":2651,"status":19,"episode_people":2652,"recommendations":2654,"season":2655},"extensions-and-marketplace-plans","959643594","Learn about the main core themes for our new team focused on extensions and the Directus Marketplace.","87826de1-4c2d-4d1b-8e0f-ffc8d28a958e","Plans for Extensions and Marketplace","\u003Cp>Speaker 0: Good day, everyone. I'm Benny, and I have the privilege of talking about our plans for the extensions and marketplace ecosystem. I recently joined the team with the goal of focusing on the developer and user experiences of Directus Extensions. This includes the overall extension development and deployment lifecycle, our public facing marketplace, and the underlying registry API. Many of you have been wondering what will come next for the marketplace since our release of the beta at our last Leap Week.\u003C\u002Fp>\u003Cp>Let me start by thanking each of you who have contributed to testing and providing feedback on the marketplace in any way. Let's discuss the developer experience first. One of the key parts of improving the developer experience, and also partly the discoverability of extensions is in bringing new capabilities to the Extension Sandbox SDK. With the upcoming release of our new policy system, we are expecting to be able to provide granular access to things like the underlying file systems, users, notifications, and emails. One of the biggest opportunities for improving the sandbox that we have identified is, of course, enabling support for importing external libraries.\u003C\u002Fp>\u003Cp>Even though this may be a significant technical challenge to implement whilst retaining the important security benefits of having a sandbox, we are looking forward to solving this for you. Okay. So what about new extension features? We are exploring how to augment existing extensions, how to deploy data and config templates using the same APIs as extensions, how to add functionality to allow developers to define extension specific settings, and we are looking at increasing the amount of life cycle events available to extension developers. We are frequently asked how to add new plugins to the built in block editor, for example, as well as adding tweaks to other extensions and experiences.\u003C\u002Fp>\u003Cp>Right now, this is cumbersome, and we think that extensions will benefit from being able to install lightweight enhancements. This means extension developers will be able to package the core functionality for their custom developed user experiences whilst being able to allow others to build on their work. Being able to deploy templates and config via extension system will allow users to include things like data structures and email templates in a controlled way. Extension settings being configurable in the app will allow the inclusion of API keys and other configuration that isn't dependent on having access to environment variables at deploy time. Our life cycle hooks will allow for better management of installing and uninstalling extensions.\u003C\u002Fp>\u003Cp>These are just some of the features we are planning to add to the road map soon to help craft our growing extension library. The last set of work we will be focusing on before moving the marketplace out of beta will ensure your extensions are seen by the widest audience possible. Our marketplace listings will be more dynamic and easier to find what you are looking for. This will include providing more options for meta information to control elements such as how details about individual contributors are displayed, as well as adding elements like hero screenshots and logos. This may also include enhancements to the configurable meta information, like better tagging options to improve searching for extensions.\u003C\u002Fp>\u003Cp>To help increase visibility, we are looking at how to make extensions listed in the marketplace discoverable outside of the director's studio so that anyone can link to them for consideration outside of an existing project. We also want to provide extension authors with insights into how their extensions are performing. This may include being able to do things like react to comments, reviews, and feedback from users as a verified extension author. Finally, we are looking at how to indicate the quality of each of the extensions public in the marketplace in a clear and transparent way. This will help users get the best experience and provide developers clear guidelines on how to produce high quality extensions.\u003C\u002Fp>\u003Cp>These new changes will be implemented in both the Data Studio as well as the registry API. Once these changes are implemented, we will publish the registry API spec to enable developers to publish and maintain their own additional registries. As you have heard, there is a lot going into the marketplace. We are looking forward to sharing the road map with you in the next couple of weeks, and you'll be able to see the priority of the item stem. We wanted to take this opportunity to provide some insight into how the road map is being developed and what will be coming.\u003C\u002Fp>\u003Cp>Once this work is done, the marketplace will be ready for general availability. We hope you are as excited as we are for some of these upcoming changes. We are really passionate about the directors extension ecosystem, and I can't wait to see what you create.\u003C\u002Fp>","Good day, everyone. I'm Benny, and I have the privilege of talking about our plans for the extensions and marketplace ecosystem. I recently joined the team with the goal of focusing on the developer and user experiences of Directus Extensions. This includes the overall extension development and deployment lifecycle, our public facing marketplace, and the underlying registry API. Many of you have been wondering what will come next for the marketplace since our release of the beta at our last Leap Week. Let me start by thanking each of you who have contributed to testing and providing feedback on the marketplace in any way. Let's discuss the developer experience first. One of the key parts of improving the developer experience, and also partly the discoverability of extensions is in bringing new capabilities to the Extension Sandbox SDK. With the upcoming release of our new policy system, we are expecting to be able to provide granular access to things like the underlying file systems, users, notifications, and emails. One of the biggest opportunities for improving the sandbox that we have identified is, of course, enabling support for importing external libraries. Even though this may be a significant technical challenge to implement whilst retaining the important security benefits of having a sandbox, we are looking forward to solving this for you. Okay. So what about new extension features? We are exploring how to augment existing extensions, how to deploy data and config templates using the same APIs as extensions, how to add functionality to allow developers to define extension specific settings, and we are looking at increasing the amount of life cycle events available to extension developers. We are frequently asked how to add new plugins to the built in block editor, for example, as well as adding tweaks to other extensions and experiences. Right now, this is cumbersome, and we think that extensions will benefit from being able to install lightweight enhancements. This means extension developers will be able to package the core functionality for their custom developed user experiences whilst being able to allow others to build on their work. Being able to deploy templates and config via extension system will allow users to include things like data structures and email templates in a controlled way. Extension settings being configurable in the app will allow the inclusion of API keys and other configuration that isn't dependent on having access to environment variables at deploy time. Our life cycle hooks will allow for better management of installing and uninstalling extensions. These are just some of the features we are planning to add to the road map soon to help craft our growing extension library. The last set of work we will be focusing on before moving the marketplace out of beta will ensure your extensions are seen by the widest audience possible. Our marketplace listings will be more dynamic and easier to find what you are looking for. This will include providing more options for meta information to control elements such as how details about individual contributors are displayed, as well as adding elements like hero screenshots and logos. This may also include enhancements to the configurable meta information, like better tagging options to improve searching for extensions. To help increase visibility, we are looking at how to make extensions listed in the marketplace discoverable outside of the director's studio so that anyone can link to them for consideration outside of an existing project. We also want to provide extension authors with insights into how their extensions are performing. This may include being able to do things like react to comments, reviews, and feedback from users as a verified extension author. Finally, we are looking at how to indicate the quality of each of the extensions public in the marketplace in a clear and transparent way. This will help users get the best experience and provide developers clear guidelines on how to produce high quality extensions. These new changes will be implemented in both the Data Studio as well as the registry API. Once these changes are implemented, we will publish the registry API spec to enable developers to publish and maintain their own additional registries. As you have heard, there is a lot going into the marketplace. We are looking forward to sharing the road map with you in the next couple of weeks, and you'll be able to see the priority of the item stem. We wanted to take this opportunity to provide some insight into how the road map is being developed and what will be coming. Once this work is done, the marketplace will be ready for general availability. We hope you are as excited as we are for some of these upcoming changes. We are really passionate about the directors extension ecosystem, and I can't wait to see what you create.","efdbb9a7-a5b3-4ac2-9c86-4452e25049cc",[2653],"457d9ca9-0ab8-4b28-b75f-5e258d12ba89",[],{"year":978,"number":2550,"id":2551,"episodes":2656,"show":2657},[2553,2554,2555,2556,2557,2558,2536,2559],{"title":151,"slug":152},{"id":2559,"slug":2659,"vimeo_id":2660,"description":2661,"tile":2662,"length":177,"resources":12,"people":12,"episode_number":11,"published":2541,"title":2663,"video_transcript_html":2664,"video_transcript_text":2665,"content":12,"seo":2666,"status":19,"episode_people":2667,"recommendations":2669,"season":2670},"directus-plus-full-launch","959641354","Get access to powerful starter kits and advanced workshops with Directus+, our premium subscription for developers. Promotional pricing ends soon.","27b7d49f-a743-48b9-8f8b-683111992c5c","Directus+ Team Plans, Starter Kits, and Full Launch","\u003Cp>Bryant: It's been 3 months since we released Directus Plus, our premium companion subscription for developers building with Directus. The subscription includes access to powerful starter kits designed to help you ship projects faster and advanced workshops that are exclusive to members. So far, we've got over 100 plus individual developers and agencies in the Directus plus program, and we've gotten a ton of great feedback. But before I share some exciting new updates, first, let's run through all the things we've shipped to date. We've released a total of 7 starter kits for a lot of different use cases.\u003C\u002Fp>\n\u003Cp>A learning management system to build custom courseware, a PIM system to manage all of your product data and catalogs, a multitenant SaaS application back end, a video streaming platform similar to Directus TV, a status page to keep users informed of any incidents or downtime, An onboarding checklist application that is perfect for managing new employees or even new clients. And our most recent starter kit, an AI content machine that works with our directus AI extensions to absolutely crush your content workflow, generate ideas from notes and recordings, create first drafts with a click of a button, and automatically translate content to multiple languages. We've also completed 2 workshops. In minimum viable billing, we use directus flows to accept payments for online products via Stripe. In database to data engine, we gave a sad lonely SQL database new purpose and equipped our non technical team members with REST APIs, dashboards, and automated workflows.\u003C\u002Fp>\n\u003Cp>Now I'm sure Kev is saying, okay, enough of the past, Bryant. Tell them what's coming up. So let's hop into the updates. Our next starter kit drops this week. It's a virtual event registration platform inspired by our own Leap Week site.\u003C\u002Fp>\n\u003Cp>You can now stop stringing together 30 different services to run online events for your company or for your clients. It's also the 1st starter kit to include a front end that already has all the plumbing connected to the Directus back end, enabling you to ship even faster. It includes registration and ticketing, personalized social images, a referral tracking system, and more. I'm also excited to announce a new Directus plus team plan. We heard your feedback.\u003C\u002Fp>\n\u003Cp>And for teams, you wanted an easier way to share the value that Directus plus offers. Starter kits, training, and access to the private community channels. Pricing for the team plan will be $599 per year, which includes up to 20 developers. That's a huge savings over the individual plan. The team plan will be rolling out over the next couple weeks, so stay tuned to your inbox.\u003C\u002Fp>\n\u003Cp>Lastly, the introductory period and the promotional pricing of $99 for the year will be available for just 2 more weeks. On July 1st, the cost of an individual plan for Directus Plus will go up to $299 annually. As we built and released more starter kits and firmed up the offering, this felt like the right moment to launch Directus Plus more fully. And with that, back to you, Kevin.\u003C\u002Fp>","It's been 3 months since we released Directus Plus, our premium companion subscription for developers building with Directus. The subscription includes access to powerful starter kits designed to help you ship projects faster and advanced workshops that are exclusive to members. So far, we've got over 100 plus individual developers and agencies in the Directus plus program, and we've gotten a ton of great feedback. But before I share some exciting new updates, first, let's run through all the things we've shipped to date. We've released a total of 7 starter kits for a lot of different use cases. A learning management system to build custom courseware, a PIM system to manage all of your product data and catalogs, a multitenant SaaS application back end, a video streaming platform similar to Directus TV, a status page to keep users informed of any incidents or downtime, An onboarding checklist application that is perfect for managing new employees or even new clients. And our most recent starter kit, an AI content machine that works with our directus AI extensions to absolutely crush your content workflow, generate ideas from notes and recordings, create first drafts with a click of a button, and automatically translate content to multiple languages. We've also completed 2 workshops. In minimum viable billing, we use directus flows to accept payments for online products via Stripe. In database to data engine, we gave a sad lonely SQL database new purpose and equipped our non technical team members with REST APIs, dashboards, and automated workflows. Now I'm sure Kev is saying, okay, enough of the past, Brian. Tell them what's coming up. So let's hop into the updates. Our next starter kit drops this week. It's a virtual event registration platform inspired by our own Leap Week site. You can now stop stringing together 30 different services to run online events for your company or for your clients. It's also the 1st starter kit to include a front end that already has all the plumbing connected to the Directus back end, enabling you to ship even faster. It includes registration and ticketing, personalized social images, a referral tracking system, and more. I'm also excited to announce a new Directus plus team plan. We heard your feedback. And for teams, you wanted an easier way to share the value that Directus plus offers. Starter kits, training, and access to the private community channels. Pricing for the team plan will be 5.99 per year, which includes up to 20 developers. That's a huge savings over the individual plan. The team plan will be rolling out over the next couple weeks, so stay tuned to your inbox. Lastly, the introductory period and the promotional pricing of $99 for the year will be available for just 2 more weeks. On July 1st, the cost of an individual plan for Directus Plus will go up to 2.99 annually. As we built and released more starter kits and firmed up the offering, this felt like the right moment to launch Directus Plus more fully. And with that, back to you, Kevin.","7cc4d938-3cb9-47e4-a85e-ccfd85397e3b",[2668],"7162af25-cb3a-474b-93e8-7ba638493410",[],{"year":978,"number":2550,"id":2551,"episodes":2671,"show":2672},[2553,2554,2555,2556,2557,2558,2536,2559],{"title":151,"slug":152},{"id":1846,"slug":2674,"vimeo_id":2675,"description":2676,"tile":2677,"length":282,"resources":12,"people":12,"episode_number":264,"published":2678,"title":2679,"video_transcript_html":2680,"video_transcript_text":2681,"content":12,"seo":2682,"status":19,"episode_people":2683,"recommendations":2687,"season":2688},"jordan-cutler","947424116","In this episode of Trace Talks, Jordan Cutler shares his journey from intern to Senior Software Engineer at Pinterest. He discusses overcoming imposter syndrome after not getting a return offer from Twitter, and how he quickly advanced at Gusto by focusing on being a reliable team member. Jordan emphasizes the importance of curiosity, seeking feedback, and understanding the intricacies of tools and technologies. He also talks about his passion for teaching through his High Growth Engineer newsletter and courses.","b55ab912-f6ff-40f1-a757-40dce11b8e7d","2024-06-13","Jordan Cutler, Senior Software Engineer at Pinterest","\u003Cp>Speaker 0: If you accept that there will always be room to grow, then there you will never be afraid of feedback.\u003C\u002Fp>\n\u003Cp>Speaker 1: Alright, everyone. Welcome back to another episode of Trace Talks. We have Jordan with us today. Jordan, I'll let you do an introduction on yourself. Just give us a brief background into who you are, where you're from, maybe your favorite, winter activity since we're recording this during the winter.\u003C\u002Fp>\n\u003Cp>And then after that, we'll kind of dive into the conversation.\u003C\u002Fp>\n\u003Cp>Speaker 0: Alright. Yeah. Sounds good. Yeah. My name is Jordan.\u003C\u002Fp>\n\u003Cp>I'm a senior software engineer. I'm primarily doing front end right now, but I've been all across the stack. I started at, my career, like, my full time career at Gusto. It's about might have been 4, 5 years ago now, something like that. And I quickly, you know, climbed up the ranks to senior engineer.\u003C\u002Fp>\n\u003Cp>And, more recently in the past year, I've kind of been writing about that journey, helping, others try to do the same and learn how to level up, go from junior to mid level, then from mid level to senior. And then finally, I've also, you know, just been exploring a bunch of different avenues. One of them right now is a is a course. So I've, you know, started doing, teaching through a cohort course on on Maven, and it's called mid level to senior engineer. I forget if I mentioned that I'm from Philly, but, my favorite, winter activity is probably stumbling through the snow as I'm walking to anywhere I wanna try to get to, because I refuse to wear anything other than my sneakers.\u003C\u002Fp>\n\u003Cp>So\u003C\u002Fp>\n\u003Cp>Speaker 1: You you get the crunch the crunch under your sneakers.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: But, yes, thanks so much for the introduction. It's so funny to hear somebody say, you know, back at the beginning of my career, and you said 4 or 5 years ago. When I say that phrase, that was 15 years ago. So, what a what a yeah. I think that'd be a great place to start.\u003C\u002Fp>\n\u003Cp>You know? You you've advanced pretty quickly in your career and as as a software engineer. So if you could, let us know about 4 or 5 years ago at the beginning of your career journey. Let us understand how you made that that quick trajectory from junior engineer, essentially, to senior engineer so quickly.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Well, I mean, it's it's not all sunshine and rainbows. I mean, in fact, like, when I first started my first job at Gusto, I was kind of feeling a bit of imposter syndrome, because, before that I had done 2 Twitter internships, which you might think, wow, you did 2 Twitter internships and you're feeling imposter syndrome? Well, the thing is is on, you know, on that second Twitter internship, I didn't get a return offer. And, you know, that was kinda like a devastating blow to, oh my gosh.\u003C\u002Fp>\n\u003Cp>Like, am I gonna be you you you thought you had everything sort of figured out and then it's like, oh, you know, is there something wrong with me? What did I what did I do wrong? Am I gonna get a job at one of these places that I've always dreamt about and all that stuff? And, to get the job at Gusto, I I was very excited, but at the same time, I was scared that the same thing would happen again. You know, maybe they let go of me within 3 months or something like that.\u003C\u002Fp>\n\u003Cp>So I I I took a lot of the lessons from that experience and what I feel like I did wrong, to not get that return offer going into Gusto. It was almost sort of a blessing in a way. I mean, like, I mean, I I as long as you learn from it. Right? And so one of the things that I remember distinctly sort of being one of my, like, guiding principles is to not try to, like, overshoot too much, and instead to just, like, try to be normal.\u003C\u002Fp>\n\u003Cp>Like, try to be, like, a normal person in the team, but, like, show how you're kinda contributing. Right? Like, rather than try to be the hero, you know, always and, you know, try to appear like you're smarter than you are or anything like that. Instead, I just felt like, I tried to be the contributing, you know, entry level member of the team, respecting the people, you know, above me, their opinion, and, you know, what what guidance they have, doing the projects that are assigned to me, and just doing them really well rather than trying to, you know, like, change the whole system and, you know, be better, you know, and, like, that that can be done later when maybe, you know, you're at the staff level and, like, you really need to improve things from a foundational perspective.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. You you speak to, something that we don't typically talk about on this podcast, but something that we all deal with every day, which is sort of rejection or our ideas of rejection. Like, you maybe thought you were on this course to do this one thing. And like you said, fortunately, it led you this other path, and and and it turned out to be probably or maybe the better scenario. But we we all face, like, a lot of rejection in our lives, especially John and I, you know, in on the sales side.\u003C\u002Fp>\n\u003Cp>Of course, it's, like, 90% dealing with rejection, which is just part of the job. But, it'd be interesting to know, you know, through that first of all, with the the Twitter experience, was it was it a a rejection of you, or was that just that your interpretation of it at the time? Or maybe there were external factors that contributed to it that it really wasn't like, oh, I didn't get the callback. It had nothing to do with me. It was maybe organization restructuring or, like, something else.\u003C\u002Fp>\n\u003Cp>And speak to, like, the psychology of of that. Like, damn, this is a crushing blow to me, but maybe it actually doesn't have anything to do with me.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Definitely. I mean, there there was some elements at the time where, I wasn't really sure about it a 100 percent because there was other people who didn't get the offer as well. And there were, I think, talks of, like, maybe there were certain limitations on the number of return offers, that they they could give for some reason. But I do know that even in those circumstances, even if maybe you are, you know, it's almost kinda like when you hit the end of the year, and you just, like, start to have that mindset shift of, like, alright, what are my goals gonna be, you know, for the the next year?\u003C\u002Fp>\n\u003Cp>Like, it's kinda like that in a way where it's just kinda a reality check regardless where it's, you know, what what could I have done better? So that way, regardless of the situation from Twitter's end, I could have done my part the best that I possibly could. And I don't feel like I did like, if I take that time to reflect on that, there were tons of things that I felt like I I could have done better, on that end.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. And I think maybe that's the the takeaway here. It's really about, like, whether it's, you know, wholly our responsibility or not, it's we can at least learn from these things and taking the time to reflect can make us grow. Like, every time we're, you know, I lose an opportunity or something like that, I'd take a sit down and say, like, okay. What part of this was in my control?\u003C\u002Fp>\n\u003Cp>What part was out of my control? And how can I, you know, improve myself in the future so that this I can minimize this at least from the things that are in my control? So it sounds like it's, you know, reflecting is that valuable time you do after something like that that kinda sets you up for the next stage of the career, I guess.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Yeah. Exactly. I would say that that's a that's a huge, like, lesson in general as part of the career growth. There's always gonna be tons of things that aren't in your control.\u003C\u002Fp>\n\u003Cp>I mean, just to give, like, a practical example, I wrote, like, a post yesterday about estimates. A lot of people complain about when you give estimates, people interpret it as deadlines. Well, what are you doing maybe that could potentially give that impression? You know, maybe if you like, someone asked you, hey. How long do you think like this would take?\u003C\u002Fp>\n\u003Cp>And you say it in a way where it's like, I think if I worked on it, it would take me, like, 3 days. And, like, if you say it like that versus, well, if I were to just give a rough ballpark number right now, then, you know, my guess would be 3 days. But if you wanted me to give you a more accurate number, then let you know, give me some time to to look into it. Like, if you say it like that, well, then it's a much different impression that the person receives. And so even if you could blame them for, you know, can't you know, making it a deadline when it really wasn't, well, what what could you do, like, on your part to make sure that that's as clear as possible?\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. Like, basically, don't pull an Elon and tell everyone full self driving is coming in a year when it's 2016.\u003C\u002Fp>\n\u003Cp>Speaker 0: Uh-huh. Exactly.\u003C\u002Fp>\n\u003Cp>Speaker 1: Well, at least at least the Cybertruck finally came out. You know, it might have taken 4 years for that, but it's here. You you mentioned your, you you mentioned writing a post, and I I think that's a good segue into who you are publicly. You know, you are, I guess, a engineer influencer. I don't I don't even know what those terms are anymore.\u003C\u002Fp>\n\u003Cp>But, I I've read a lot of your articles, and I think there's a lot of lessons that you you share, you know, about being a go to player and and taking rejection and all of those types of posts. Why did you originally start posting to social media and LinkedIn, and then you now have a, a newsletter that goes out? So I'd love to understand how I I typically perceive engineers as a little bit kind of quieter and typically don't post those types of things. But was it some of the lessons that you learned early on that made you want to be more social and share those experiences, or was there different reason that you started posting and and started the newsletter?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Yeah. It's it's a great question. I mean, it's it's hard to pinpoint, like, one exact thing, but I do remember, like, where it started. And, you know, the it started from, one single post, and it had nothing to do with, like, what I write about now, actually.\u003C\u002Fp>\n\u003Cp>It was, you know, it was on accessibility, like, alt text. And, I think, like, at the time, I was thinking that previously at at my, you know, at Gusto, I had started a channel that was called, Jordan's Front End Learnings, and it was essentially like, me just taking notes, but, like, kinda sharing it in a way that could be consumed, like, more broadly rather than just, like, a brain dump, you know, of things. And it would almost be, like, little mini lessons. And I would, like, write, you know, in there as I learn stuff because I wanted to learn more about front end. And, like, over time, you know, people started to join that, And, it kinda opened my eyes a little bit of, like, oh, okay.\u003C\u002Fp>\n\u003Cp>Like, people like, I'm not even really trying. And people seem to enjoy some of these, like, this sort of, like, learning community, you know, like, and eventually it got up to, like, 50 people and it started, you know, from 0 where I'm just like not even advertising it, like just kind of spread through word-of-mouth a little bit. And it made me think, well, like, if I'm doing this, like, on a company scale, like, what if I try this out, like, more broadly? And that's kinda why, like, my first post was a little bit, like, about front end because Yeah. I was just like, well, let me think, like, okay.\u003C\u002Fp>\n\u003Cp>I wrote about front end before. Let me, like, write about front end here. And then that first post, it got, like, 30 plus, you know, likes on LinkedIn. And I was like, oh my gosh. Like, this is already, like, more than my front end learning channel, you know.\u003C\u002Fp>\n\u003Cp>So then I just sort of, like, continued a little bit, but I I was thinking, well, you know, this is good. Like, I can write about front end, but long term, I think, like, the thing that kind of makes me stand out is maybe more of my career journey and it also has, like, more of a broader audience. And, I mean, I've always, like, been into, like, a couple things, like, one, like, the Feynman technique of, you know, like, you teach and you learn it better. And then also just in general, like, I think if I wasn't gonna be an engineer, I've always talked about, like, being a teacher, But, unfortunately, like, teachers are not really paid that way.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: So so, like, this is kinda like a win win on that boat, you know, where I could get get the benefits there, kinda do what I, you know, wanted to. And I've kinda just continued from there, and I have made, like, small iterative tweaks to how I do things. But, I mean, I'm still generally just doing the same thing as I did, you know, day 1.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. That really reminds me, it takes me back to to my elementary school days when the teacher is teaching something and they asked, does anybody have any questions? And you're hoping that one person asks a question because everybody's asking the same question. So being a being that person who shares that that front end experience, you know, even about alt text or whatever, you whatever your learnings are, I kind of equate you to that purse that kid in in elementary school who raised their hand and asked the question that everybody had because I was the kid who was kind of scared that I would look dumb if I asked a question. Were you that type of kid in school?\u003C\u002Fp>\n\u003Cp>Speaker 0: It's so funny you mentioned that because, yeah, I definitely was. Like, there was even I had a elementary school teacher that was, you know, like, I remember I raised my hand at one point and he kind of made like a joking, like, tantrum a little bit where it was like, oh, Jordan. Okay. What is it now? You know?\u003C\u002Fp>\n\u003Cp>What is the color of the sky? Like, what is it? You know? Like, and he just, like, started asking, like, a bunch of, like, rent like, questions kinda, like, making fun of me a little bit. But, I mean, I didn't take it to heart.\u003C\u002Fp>\n\u003Cp>I think we're Yeah. We kinda, you know, we had a decent relationship, I suppose. But, yeah, it was to the point where someone even made fun of me for it.\u003C\u002Fp>\n\u003Cp>Speaker 1: Even a teacher. Yeah. Yeah. No. I think that's a I think that's a trait that a lot of us have in common.\u003C\u002Fp>\n\u003Cp>It's kind of you wanna understand is it's kind of a curiosity almost. Do you do you find that that's actually an important trait to have as an engineer is curiosity? And then how important do you think it is to ask questions that you feel might make you look dumb, but it's important to ask the questions so that you understand what you're trying to learn.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Yeah. Absolutely. I curiosity, and just, you know, this is part of curiosity, but, like, the willingness to not just look at the surface level and accept it for what it is is super important. One of the things that I, that I teach, like, in my course about, like, senior level coding is that you are constantly thinking about, like, if you use maybe a library, you know, an external tool or something like that, how, like, how does this thing actually work?\u003C\u002Fp>\n\u003Cp>Like, you're not just accepting it for, like, a black box, right? Like, when you use chat gpt, what is it actually doing behind the scenes? And if you, like, think about, like, those types of things, like, enough, then over time you start to build kind of like a like a mental model or like a mental map in your head of like, oh, okay. Yeah. Like this thing is kind of similar to this thing.\u003C\u002Fp>\n\u003Cp>I expect it to work in this certain way. And because I can, like, make that assumption, I might even just be able to, like, try this method and see if it exists. And, like, you know, I don't even need to look at the docs. And, like, I Yeah. I have a feeling, like, that this is gonna work this way.\u003C\u002Fp>\n\u003Cp>And, or, like, if you need to build a similar tool yourself, you kinda already have an idea, you know, of of how you're gonna do it and you can, you know, form the architecture for it or ace your interview because they'll ask you like, hey, design Netflix. And if you've thought enough, like, while you're watching Netflix, like, how is the video streaming, you know, to my to my TV right now? Then Yeah. Maybe you have a decent idea of, like, you know, how you'll be able to do it in an interview. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 2: Right. Yeah. And I I think, like, just to add to that, like, curiosity is a big element of that, especially with engineering. And I think the more that you like you said, the more that you dive into, like, the what, why, and how. Of course, there's gonna be questions that sound stupid and elementary and basic around it.\u003C\u002Fp>\n\u003Cp>Right? But, they're important questions to get you to dive a bit deeper. Right? And and they're just, like, instinctual things. Like, oh, I just wanna know how this works.\u003C\u002Fp>\n\u003Cp>Right? And sometimes those instinctual things to to me and and to many people are the things that actually end up being these big light bulb moments. At least in I I always relate things back to music because I was, like, sort of the first career path I had. And it was always funny to me to learn that, like, some of these really big number one hits. Right?\u003C\u002Fp>\n\u003Cp>Like, Guns N' Roses, for example, Sweet Child of Mine, that riff was written as a total joke because they had gone to, like they had gone out to Vegas and the casinos, and Slash is just writing this, like, oh, this is what the slot machines sound like. And they just it was a pure joke, and they gave it to the label, and they're like, this is the best song, you know, you guys have. And and they're like, no. We're not releasing this. This is like a joke.\u003C\u002Fp>\n\u003Cp>But sometimes the point is that, like, these through these, like, initial, like they're just, like, gut instincts, and you treat them as that. You don't, like, you know, you don't put too much weight and judge them. They're just, like, these ideas that once you put them out, it's like, oh, actually, that's that's the brilliant idea. It's not the stupid idea. Right?\u003C\u002Fp>\n\u003Cp>That's the brilliant question that led you down the path, not the stupid question. So maybe, yeah, maybe it's it's best that we don't, like, put others' judgment into these things because that can just, like, stop us right there and kill the curiosity.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Exactly. And don't, yeah, don't let it die for\u003C\u002Fp>\n\u003Cp>Speaker 2: sure. Yeah. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: Do do you find that your community you've built, you know, a a decent a good sized following, about 40,000 times bigger than mine. But do you find that your community, leads you to new tools and new technologies? I'm sure you're out there kind of seeing things that are new on the market or people are sharing, but do you find that one of the benefits of having this community is people sharing back things that you then become curious about? Is that the feedback that you're getting from your community?\u003C\u002Fp>\n\u003Cp>Speaker 0: Mhmm. Yeah. It's Or\u003C\u002Fp>\n\u003Cp>Speaker 1: or are they just silent listeners?\u003C\u002Fp>\n\u003Cp>Speaker 0: Definitely not silent listeners, but, I mean, I I think I do get questions sometimes about, like, oh, you know, like, have you have you heard about this tool? Have you tried this tool or something like that? And I think one downside, honestly, you know, because a lot of people reach out to me and they'll they'll say also that they were inspired by my journey to start content creation. It doesn't come without its downsides. It for sure.\u003C\u002Fp>\n\u003Cp>Like, one of the downsides that I've personally experienced is you lose a lot of time to actually especially if you're talking about, like, career growth stuff, more like soft skills, you lose a lot of time to dive into some of those, like, more technical, like, alright. Let me figure out, like, how, you know, Next. Js versus Vite versus Solid. Js, like, all these things, like, work. Like, the most, you know, that I can really do a lot of the time is maybe watch a video, you know, on some some person that's kinda already done that deep dive, and I can't really, like, do it myself.\u003C\u002Fp>\n\u003Cp>You know? So it is something that I wish I could, like I'm trying to sort out and actually, maybe, hopefully, like, if I can figure out how to use AI to save some time on some of the things that I do, then maybe I'll be able to get some time back on\u003C\u002Fp>\n\u003Cp>Speaker 2: that. Yeah. Yeah. Yeah. Has sorry.\u003C\u002Fp>\n\u003Cp>I was gonna ask, did has the the high growth engineer, right, your your newsletter, has that led you to meet anyone interesting or inspiring that maybe somebody that you've looked up to or just someone notable in the community?\u003C\u002Fp>\n\u003Cp>Speaker 0: Oh, yeah. Yeah. That I mean, that would I would say would be, like, one of the biggest benefits. I mean, just like a couple off the top of my head, would be, like, Gregor, you know, the engineering leadership guy, Caleb, John Cricket, Wes Kao, the founders, like, at at Plato. It was like a mentorship platform.\u003C\u002Fp>\n\u003Cp>Through that, like, I was able to, get, an opportunity to emcee at a conference that they held in San Francisco. I they would have never known about me if I didn't write online or anything like that. And then through that conference, I met, like, a bunch of senior engineering leaders, you know, like VPs of engineering. I met a director of engineering at Netflix, Bruce, a a bunch of other, you know, people as well. Oh, one one 2 more quick shout outs too on people.\u003C\u002Fp>\n\u003Cp>Ryan Peterman, he's a staff engineer at at Meta. Ravi Raj Achar, he's, like, tech lead mentor. And he him and Irina, Irina Stanescu, they have also base essentially, like, participated in my course as guests, and they just, like, do it out of the goodness of their heart. Like, because we're, you know, connected, we help each other out and all that stuff, that helps like improve the value of my course as well. People love them, you know, when, when they're there, it's like super awesome to be like, hey\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: I I have like a past, you know, engineering manager and stat like staff engineer, like at Meta, Google, Uber, like all this stuff. And they're like, you know, they're just like their eye all the students in the course, like, their eyes light up and everything. You know?\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. That that really speaks to, like we we had spoken to someone else on this podcast, maybe a few episodes ago, but one of the key takeaways is, like, get out there, meet people, like, be a part of the community, like, shake some hands. Right? Because networking is something that's never gonna go away, and it's even actually increasingly more important to meet people in real life. So it sounds like you've definitely benefited from the, you know, the publicity or the or the the exposure that you've gotten online, but actually converted that into, like, actual handshakes, which is really impressive and, like, I think a great way to build your career, especially, like, you know, at at a at a younger age too.\u003C\u002Fp>\n\u003Cp>Speaker 0: Oh, yeah. Absolutely. Yeah. It's it's it's amazing. Yeah.\u003C\u002Fp>\n\u003Cp>And one one interesting insight that I've found about networking, especially because, like, you know, I guess within the creator space, normally, it's kind of about, when you help people, you get help in return. Right? Like, the you know, maybe you give someone a shout out and they'll be like, oh, wow. Like, I didn't even know about you before, but, like, you're giving me a shout out. That's super cool.\u003C\u002Fp>\n\u003Cp>Let me check out your stuff. Oh, yeah. I'll, like, I'd love to, like, add you as a shout out. And so it's kinda like cool that you've realized that networking really is about, like, giving value first and then, like, getting value back. And, it's not like, hey, you know, tell me about your life story.\u003C\u002Fp>\n\u003Cp>I'm here as, like, a person that you can talk to even though you know nothing about, like, you don't you don't know me at all. And then, like, after you tell me your life story, can I ask you, like, for a job? You know? Yeah. It's nothing it's nothing like about that.\u003C\u002Fp>\n\u003Cp>Speaker 2: Sounds like the typical LinkedIn message. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Yeah. Yeah. Exactly. But the the thing that I found particularly especially as I help other people is, like, it's a little easier for me to kind of do that giving value thing when I have something like, you know, my audience, my LinkedIn post, my newsletter thing, all that stuff.\u003C\u002Fp>\n\u003Cp>But I I could see it being difficult for someone that is maybe, like, just getting started in the industry or something like that and it's, like, you know, maybe they just, like, my my just view of it from from where I'm looking, it doesn't look like they have that much to give. But I also don't know, You know, if they like, that's the high level principle. If you can find something to give, then you're good. But I don't I don't really know, like, what I can suggest to them. That's part of the issue that I run into when I try to help people with that.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Do you so looking back at your quick journey from junior engineer to senior engineer and building this audience, you know, what do you did you have this where you're at now, did you kind of have this as a goal back 4 or 5 years ago? And then what is your goal for the next 5 years? You mentioned love the love for teaching, and you would do teaching if it was more, lucrative. But, you know, where do you do you wanna move further into leadership and become even more of a mentor for people who are both in your company but also external, or would you rather go down the content creation leader or teacher type, journey?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I I think the content creation, it needs to come from your actual experience. If maybe I wanted to write, you know, indefinitely about how to become a senior engineer because that's where I am right now, right, then I could leave my, you know, leave my job maybe and try this out. But, like, I you know, it would be, it would be, like, inauthentic of me to try to expand outward and say, hey. Here's how you go from senior engineer to staff engineer.\u003C\u002Fp>\n\u003Cp>Here's how you go from, you know, senior engineer to manager. I haven't tried those things out. So Yeah. I think one of the unique things that, like, I, bring to the table, like, as a creator is I do have, like, a wide range of experiences in a short amount of time. Like, I, you know, I I worked at Twitter as, like, internship.\u003C\u002Fp>\n\u003Cp>I got I got rejected, like, on my like, I got I also I got the return offer, but I also got rejected. So I, like, I know what that both of those, like, feel like. And I also, like, worked at a startup, you know, crypto startup, like, 3, 4 person thing, you know, like, while I was in university. And then, like, I did Gusto, which was, like, medium size. So, like, just in those 3 alone, I basically have, like, all the different sizes.\u003C\u002Fp>\n\u003Cp>Like, I have, like, super small, like, Gusto, like, medium size, Twitter, like, you know, bigger. And then now I'm kinda, like, in between, like, 0 and gusto size right now. I qualified.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: So I think, like, if I could just keep on, you know, varying the experiences up and and, like, you know, just just add to the repertoire of different things that I've tried, then, it'll probably, you know, make me just be able to relate to pretty much anybody, hopefully, and and keep writing about, you know, things that that hopefully provide value to people.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. No. That's that's such a great point is continuing on the the leadership to, you know, higher positions will naturally create that content that other people are looking for. So it's it's almost like you don't have to make that decision, yet you just kind of continue the path that you've been on, and both will come naturally naturally because it sounds like you're a hard worker and you're curious and that that from what who we've who we've talked to before for this podcast and just from our own personal experiences, that's the kind of things that it takes to continue climbing up in the ranks. And so that's such a good point that you don't have to make that decision.\u003C\u002Fp>\n\u003Cp>You just keep going where what you're doing, and both will naturally come. So\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. The inspiration for the content will seem to generate itself. Right? And then it's just, like, translating that experience into content.\u003C\u002Fp>\n\u003Cp>Speaker 0: Exactly. Yeah. Yeah. I I feel like it would it would be, difficult to come up with ideas, you know, after you're, like, you know, not, you know, in a tech working environment for, you know, the 8 hours a day. At the same time, though, it is it is a lot of work, to do both.\u003C\u002Fp>\n\u003Cp>So so I don't know. I'm gonna try to figure out something, but, you know, for TBD.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Well, there's one position that it sounds like you haven't had, and that is founder of a company. So True. Yeah. Maybe if, you know, you are your founder of your content, but maybe you've been at a startup, but you haven't been the founder.\u003C\u002Fp>\n\u003Cp>So maybe that's in your journey eventually. Yeah. So so, you know, what have you learned over the years? What would be some good advice that you give to people who are listening? Pretend that the listeners are are you're their mentor?\u003C\u002Fp>\n\u003Cp>You know? What would you what advice would you give them from your experience?\u003C\u002Fp>\n\u003Cp>Speaker 0: Well, so it's tough to give any general advice because every depending on the stage of your career, the advice is gonna be, you know, different. But if I had to give, like, something, you know, maybe to everyone, then I would say, one one really nice takeaway is if you accept that there will always be room to grow, then there you will never be afraid of feedback. And, you know, one way that that kinda sees itself is, a lot of time, you know, we're scared to ask our peers, you know, for feedback or say, hey. How did I do, like, in that presentation? Or, like, how do you how do you feel like, do you feel like I could have done anything better, like, in that meeting that I just ran?\u003C\u002Fp>\n\u003Cp>Or, you know, like, how was my how did I how did I do leading this project or something like that? Or, like, just anything like that. A lot of times, we're a little scared to ask. And understandably, you know, we all kinda put our we we put our best foot forward and we we we don't wanna hear that potential that we didn't do as best as we can. But at the same time, the the those two things, like, you can put your best foot forward and you can also always grow.\u003C\u002Fp>\n\u003Cp>So if you accept that, then you'll constantly seek out that feedback, and you'll also constantly be growing.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Wow. I might just put that phrase on my wall, you know, because that that's actually some really, really good advice. You know? So that's very good advice that was that was very generic for generic questions.\u003C\u002Fp>\n\u003Cp>So well well said. Hopefully, broadly applicable. Yeah. Yeah. Yeah.\u003C\u002Fp>\n\u003Cp>Awesome. So I guess, you know, we'll we'll wrap it up here. It's been a awesome conversation. But, you know, we've mentioned throughout the conversation some of the things that you're doing, but, obviously, we want people to be, followers of you because you're putting out good things. You're putting out good advice, good content.\u003C\u002Fp>\n\u003Cp>So where can people find you? You know? Where what is the courses that you teach? What is your newsletter? Just repeat that.\u003C\u002Fp>\n\u003Cp>And then if people wanna follow you in other places, where where are you?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Yeah. Absolutely. So, LinkedIn is the main place where I post every day, you know, updates on kinda everything. So just search Jordan Cutler on LinkedIn.\u003C\u002Fp>\n\u003Cp>The newsletter is called high growth engineer. And the course, if you're interested in that, I don't think I will be running that many more cohorts. I think I'm gonna explore different avenues. So try to get in either on this upcoming cohort, although I don't know when the podcast will be out Yep. Or the next one might be my last one.\u003C\u002Fp>\n\u003Cp>We'll see. And that one is called mid level to senior engineer, on Maven.\u003C\u002Fp>\n\u003Cp>Speaker 1: Okay. Awesome. Well, any any last questions from you, Pedro? I feel like I just took over that end.\u003C\u002Fp>\n\u003Cp>Speaker 2: No. That's that's amazing. Definitely excited to to continue following your journey as I'm sure most of the, you know, the viewers and the listeners are as well. And, yeah, so subscribe. Right?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Yeah. I think I think the last word I'll say is I think it's really motivational. There's a lot of young engineers out there and and even older engineers too, so I don't wanna pigeonhole into young engineers who they're curious and they wanna learn. And so I think some of the articles that you've put out about, you know, being a go to in a specific, field or a specific piece of content or what it what may it be.\u003C\u002Fp>\n\u003Cp>But, it's really, encouraging to see somebody who's younger than me who puts in the effort and has had the success that, they can then share with other people. So it's been a fantastic conversation.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. Definitely appreciate it.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Yeah. It's been great, guys. I really appreciate it.\u003C\u002Fp>","If you accept that there will always be room to grow, then there you will never be afraid of feedback. Alright, everyone. Welcome back to another episode of Trace Talks. We have Jordan with us today. Jordan, I'll let you do an introduction on yourself. Just give us a brief background into who you are, where you're from, maybe your favorite, winter activity since we're recording this during the winter. And then after that, we'll kind of dive into the conversation. Alright. Yeah. Sounds good. Yeah. My name is Jordan. I'm a senior software engineer. I'm primarily doing front end right now, but I've been all across the stack. I started at, my career, like, my full time career at Gusto. It's about might have been 4, 5 years ago now, something like that. And I quickly, you know, climbed up the ranks to senior engineer. And, more recently in the past year, I've kind of been writing about that journey, helping, others try to do the same and learn how to level up, go from junior to mid level, then from mid level to senior. And then finally, I've also, you know, just been exploring a bunch of different avenues. One of them right now is a is a course. So I've, you know, started doing, teaching through a cohort course on on Maven, and it's called mid level to senior engineer. I forget if I mentioned that I'm from Philly, but, my favorite, winter activity is probably stumbling through the snow as I'm walking to anywhere I wanna try to get to, because I refuse to wear anything other than my sneakers. So You you get the crunch the crunch under your sneakers. Yeah. But, yes, thanks so much for the introduction. It's so funny to hear somebody say, you know, back at the beginning of my career, and you said 4 or 5 years ago. When I say that phrase, that was 15 years ago. So, what a what a yeah. I think that'd be a great place to start. You know? You you've advanced pretty quickly in your career and as as a software engineer. So if you could, let us know about 4 or 5 years ago at the beginning of your career journey. Let us understand how you made that that quick trajectory from junior engineer, essentially, to senior engineer so quickly. Yeah. Well, I mean, it's it's not all sunshine and rainbows. I mean, in fact, like, when I first started my first job at Gusto, I was kind of feeling a bit of imposter syndrome, because, before that I had done 2 Twitter internships, which you might think, wow, you did 2 Twitter internships and you're feeling imposter syndrome? Well, the thing is is on, you know, on that second Twitter internship, I didn't get a return offer. And, you know, that was kinda like a devastating blow to, oh my gosh. Like, am I gonna be you you you thought you had everything sort of figured out and then it's like, oh, you know, is there something wrong with me? What did I what did I do wrong? Am I gonna get a job at one of these places that I've always dreamt about and all that stuff? And, to get the job at Gusto, I I was very excited, but at the same time, I was scared that the same thing would happen again. You know, maybe they let go of me within 3 months or something like that. So I I I took a lot of the lessons from that experience and what I feel like I did wrong, to not get that return offer going into Gusto. It was almost sort of a blessing in a way. I mean, like, I mean, I I as long as you learn from it. Right? And so one of the things that I remember distinctly sort of being one of my, like, guiding principles is to not try to, like, overshoot too much, and instead to just, like, try to be normal. Like, try to be, like, a normal person in the team, but, like, show how you're kinda contributing. Right? Like, rather than try to be the hero, you know, always and, you know, try to appear like you're smarter than you are or anything like that. Instead, I just felt like, I tried to be the contributing, you know, entry level member of the team, respecting the people, you know, above me, their opinion, and, you know, what what guidance they have, doing the projects that are assigned to me, and just doing them really well rather than trying to, you know, like, change the whole system and, you know, be better, you know, and, like, that that can be done later when maybe, you know, you're at the staff level and, like, you really need to improve things from a foundational perspective. Yeah. You you speak to, something that we don't typically talk about on this podcast, but something that we all deal with every day, which is sort of rejection or our ideas of rejection. Like, you maybe thought you were on this course to do this one thing. And like you said, fortunately, it led you this other path, and and and it turned out to be probably or maybe the better scenario. But we we all face, like, a lot of rejection in our lives, especially John and I, you know, in on the sales side. Of course, it's, like, 90% dealing with rejection, which is just part of the job. But, it'd be interesting to know, you know, through that first of all, with the the Twitter experience, was it was it a a rejection of you, or was that just that your interpretation of it at the time? Or maybe there were external factors that contributed to it that it really wasn't like, oh, I didn't get the callback. It had nothing to do with me. It was maybe organization restructuring or, like, something else. And speak to, like, the psychology of of that. Like, damn, this is a crushing blow to me, but maybe it actually doesn't have anything to do with me. Yeah. Definitely. I mean, there there was some elements at the time where, I wasn't really sure about it a 100 percent because there was other people who didn't get the offer as well. And there were, I think, talks of, like, maybe there were certain limitations on the number of return offers, that they they could give for some reason. But I do know that even in those circumstances, even if maybe you are, you know, it's almost kinda like when you hit the end of the year, and you just, like, start to have that mindset shift of, like, alright, what are my goals gonna be, you know, for the the next year? Like, it's kinda like that in a way where it's just kinda a reality check regardless where it's, you know, what what could I have done better? So that way, regardless of the situation from Twitter's end, I could have done my part the best that I possibly could. And I don't feel like I did like, if I take that time to reflect on that, there were tons of things that I felt like I I could have done better, on that end. Yeah. And I think maybe that's the the takeaway here. It's really about, like, whether it's, you know, wholly our responsibility or not, it's we can at least learn from these things and taking the time to reflect can make us grow. Like, every time we're, you know, I lose an opportunity or something like that, I'd take a sit down and say, like, okay. What part of this was in my control? What part was out of my control? And how can I, you know, improve myself in the future so that this I can minimize this at least from the things that are in my control? So it sounds like it's, you know, reflecting is that valuable time you do after something like that that kinda sets you up for the next stage of the career, I guess. Yeah. Yeah. Exactly. I would say that that's a that's a huge, like, lesson in general as part of the career growth. There's always gonna be tons of things that aren't in your control. I mean, just to give, like, a practical example, I wrote, like, a post yesterday about estimates. A lot of people complain about when you give estimates, people interpret it as deadlines. Well, what are you doing maybe that could potentially give that impression? You know, maybe if you like, someone asked you, hey. How long do you think like this would take? And you say it in a way where it's like, I think if I worked on it, it would take me, like, 3 days. And, like, if you say it like that versus, well, if I were to just give a rough ballpark number right now, then, you know, my guess would be 3 days. But if you wanted me to give you a more accurate number, then let you know, give me some time to to look into it. Like, if you say it like that, well, then it's a much different impression that the person receives. And so even if you could blame them for, you know, can't you know, making it a deadline when it really wasn't, well, what what could you do, like, on your part to make sure that that's as clear as possible? Yeah. Like, basically, don't pull an Elon and tell everyone full self driving is coming in a year when it's 2016. Uh-huh. Exactly. Well, at least at least the Cybertruck finally came out. You know, it might have taken 4 years for that, but it's here. You you mentioned your, you you mentioned writing a post, and I I think that's a good segue into who you are publicly. You know, you are, I guess, a engineer influencer. I don't I don't even know what those terms are anymore. But, I I've read a lot of your articles, and I think there's a lot of lessons that you you share, you know, about being a go to player and and taking rejection and all of those types of posts. Why did you originally start posting to social media and LinkedIn, and then you now have a, a newsletter that goes out? So I'd love to understand how I I typically perceive engineers as a little bit kind of quieter and typically don't post those types of things. But was it some of the lessons that you learned early on that made you want to be more social and share those experiences, or was there different reason that you started posting and and started the newsletter? Yeah. Yeah. It's it's a great question. I mean, it's it's hard to pinpoint, like, one exact thing, but I do remember, like, where it started. And, you know, the it started from, one single post, and it had nothing to do with, like, what I write about now, actually. It was, you know, it was on accessibility, like, alt text. And, I think, like, at the time, I was thinking that previously at at my, you know, at Gusto, I had started a channel that was called, Jordan's Front End Learnings, and it was essentially like, me just taking notes, but, like, kinda sharing it in a way that could be consumed, like, more broadly rather than just, like, a brain dump, you know, of things. And it would almost be, like, little mini lessons. And I would, like, write, you know, in there as I learn stuff because I wanted to learn more about front end. And, like, over time, you know, people started to join that, And, it kinda opened my eyes a little bit of, like, oh, okay. Like, people like, I'm not even really trying. And people seem to enjoy some of these, like, this sort of, like, learning community, you know, like, and eventually it got up to, like, 50 people and it started, you know, from 0 where I'm just like not even advertising it, like just kind of spread through word-of-mouth a little bit. And it made me think, well, like, if I'm doing this, like, on a company scale, like, what if I try this out, like, more broadly? And that's kinda why, like, my first post was a little bit, like, about front end because Yeah. I was just like, well, let me think, like, okay. I wrote about front end before. Let me, like, write about front end here. And then that first post, it got, like, 30 plus, you know, likes on LinkedIn. And I was like, oh my gosh. Like, this is already, like, more than my front end learning channel, you know. So then I just sort of, like, continued a little bit, but I I was thinking, well, you know, this is good. Like, I can write about front end, but long term, I think, like, the thing that kind of makes me stand out is maybe more of my career journey and it also has, like, more of a broader audience. And, I mean, I've always, like, been into, like, a couple things, like, one, like, the Feynman technique of, you know, like, you teach and you learn it better. And then also just in general, like, I think if I wasn't gonna be an engineer, I've always talked about, like, being a teacher, But, unfortunately, like, teachers are not really paid that way. Yeah. Yeah. So so, like, this is kinda like a win win on that boat, you know, where I could get get the benefits there, kinda do what I, you know, wanted to. And I've kinda just continued from there, and I have made, like, small iterative tweaks to how I do things. But, I mean, I'm still generally just doing the same thing as I did, you know, day 1. Yeah. That really reminds me, it takes me back to to my elementary school days when the teacher is teaching something and they asked, does anybody have any questions? And you're hoping that one person asks a question because everybody's asking the same question. So being a being that person who shares that that front end experience, you know, even about alt text or whatever, you whatever your learnings are, I kind of equate you to that purse that kid in in elementary school who raised their hand and asked the question that everybody had because I was the kid who was kind of scared that I would look dumb if I asked a question. Were you that type of kid in school? It's so funny you mentioned that because, yeah, I definitely was. Like, there was even I had a elementary school teacher that was, you know, like, I remember I raised my hand at one point and he kind of made like a joking, like, tantrum a little bit where it was like, oh, Jordan. Okay. What is it now? You know? What is the color of the sky? Like, what is it? You know? Like, and he just, like, started asking, like, a bunch of, like, rent like, questions kinda, like, making fun of me a little bit. But, I mean, I didn't take it to heart. I think we're Yeah. We kinda, you know, we had a decent relationship, I suppose. But, yeah, it was to the point where someone even made fun of me for it. Even a teacher. Yeah. Yeah. No. I think that's a I think that's a trait that a lot of us have in common. It's kind of you wanna understand is it's kind of a curiosity almost. Do you do you find that that's actually an important trait to have as an engineer is curiosity? And then how important do you think it is to ask questions that you feel might make you look dumb, but it's important to ask the questions so that you understand what you're trying to learn. Yeah. Yeah. Absolutely. I curiosity, and just, you know, this is part of curiosity, but, like, the willingness to not just look at the surface level and accept it for what it is is super important. One of the things that I, that I teach, like, in my course about, like, senior level coding is that you are constantly thinking about, like, if you use maybe a library, you know, an external tool or something like that, how, like, how does this thing actually work? Like, you're not just accepting it for, like, a black box, right? Like, when you use chat gpt, what is it actually doing behind the scenes? And if you, like, think about, like, those types of things, like, enough, then over time you start to build kind of like a like a mental model or like a mental map in your head of like, oh, okay. Yeah. Like this thing is kind of similar to this thing. I expect it to work in this certain way. And because I can, like, make that assumption, I might even just be able to, like, try this method and see if it exists. And, like, you know, I don't even need to look at the docs. And, like, I Yeah. I have a feeling, like, that this is gonna work this way. And, or, like, if you need to build a similar tool yourself, you kinda already have an idea, you know, of of how you're gonna do it and you can, you know, form the architecture for it or ace your interview because they'll ask you like, hey, design Netflix. And if you've thought enough, like, while you're watching Netflix, like, how is the video streaming, you know, to my to my TV right now? Then Yeah. Maybe you have a decent idea of, like, you know, how you'll be able to do it in an interview. Yeah. Right. Yeah. And I I think, like, just to add to that, like, curiosity is a big element of that, especially with engineering. And I think the more that you like you said, the more that you dive into, like, the what, why, and how. Of course, there's gonna be questions that sound stupid and elementary and basic around it. Right? But, they're important questions to get you to dive a bit deeper. Right? And and they're just, like, instinctual things. Like, oh, I just wanna know how this works. Right? And sometimes those instinctual things to to me and and to many people are the things that actually end up being these big light bulb moments. At least in I I always relate things back to music because I was, like, sort of the first career path I had. And it was always funny to me to learn that, like, some of these really big number one hits. Right? Like, Guns N' Roses, for example, Sweet Child of Mine, that riff was written as a total joke because they had gone to, like they had gone out to Vegas and the casinos, and Slash is just writing this, like, oh, this is what the slot machines sound like. And they just it was a pure joke, and they gave it to the label, and they're like, this is the best song, you know, you guys have. And and they're like, no. We're not releasing this. This is like a joke. But sometimes the point is that, like, these through these, like, initial, like they're just, like, gut instincts, and you treat them as that. You don't, like, you know, you don't put too much weight and judge them. They're just, like, these ideas that once you put them out, it's like, oh, actually, that's that's the brilliant idea. It's not the stupid idea. Right? That's the brilliant question that led you down the path, not the stupid question. So maybe, yeah, maybe it's it's best that we don't, like, put others' judgment into these things because that can just, like, stop us right there and kill the curiosity. Yeah. Exactly. And don't, yeah, don't let it die for sure. Yeah. Yeah. Do do you find that your community you've built, you know, a a decent a good sized following, about 40,000 times bigger than mine. But do you find that your community, leads you to new tools and new technologies? I'm sure you're out there kind of seeing things that are new on the market or people are sharing, but do you find that one of the benefits of having this community is people sharing back things that you then become curious about? Is that the feedback that you're getting from your community? Mhmm. Yeah. It's Or or are they just silent listeners? Definitely not silent listeners, but, I mean, I I think I do get questions sometimes about, like, oh, you know, like, have you have you heard about this tool? Have you tried this tool or something like that? And I think one downside, honestly, you know, because a lot of people reach out to me and they'll they'll say also that they were inspired by my journey to start content creation. It doesn't come without its downsides. It for sure. Like, one of the downsides that I've personally experienced is you lose a lot of time to actually especially if you're talking about, like, career growth stuff, more like soft skills, you lose a lot of time to dive into some of those, like, more technical, like, alright. Let me figure out, like, how, you know, Next. Js versus Vite versus Solid. Js, like, all these things, like, work. Like, the most, you know, that I can really do a lot of the time is maybe watch a video, you know, on some some person that's kinda already done that deep dive, and I can't really, like, do it myself. You know? So it is something that I wish I could, like I'm trying to sort out and actually, maybe, hopefully, like, if I can figure out how to use AI to save some time on some of the things that I do, then maybe I'll be able to get some time back on that. Yeah. Yeah. Yeah. Has sorry. I was gonna ask, did has the the high growth engineer, right, your your newsletter, has that led you to meet anyone interesting or inspiring that maybe somebody that you've looked up to or just someone notable in the community? Oh, yeah. Yeah. That I mean, that would I would say would be, like, one of the biggest benefits. I mean, just like a couple off the top of my head, would be, like, Gregor, you know, the engineering leadership guy, Caleb, John Cricket, Wes Kao, the founders, like, at at Plato. It was like a mentorship platform. Through that, like, I was able to, get, an opportunity to emcee at a conference that they held in San Francisco. I they would have never known about me if I didn't write online or anything like that. And then through that conference, I met, like, a bunch of senior engineering leaders, you know, like VPs of engineering. I met a director of engineering at Netflix, Bruce, a a bunch of other, you know, people as well. Oh, one one 2 more quick shout outs too on people. Ryan Peterman, he's a staff engineer at at Meta. Ravi Raj Achar, he's, like, tech lead mentor. And he him and Irina, Irina Stanescu, they have also base essentially, like, participated in my course as guests, and they just, like, do it out of the goodness of their heart. Like, because we're, you know, connected, we help each other out and all that stuff, that helps like improve the value of my course as well. People love them, you know, when, when they're there, it's like super awesome to be like, hey Yeah. I I have like a past, you know, engineering manager and stat like staff engineer, like at Meta, Google, Uber, like all this stuff. And they're like, you know, they're just like their eye all the students in the course, like, their eyes light up and everything. You know? Yeah. That that really speaks to, like we we had spoken to someone else on this podcast, maybe a few episodes ago, but one of the key takeaways is, like, get out there, meet people, like, be a part of the community, like, shake some hands. Right? Because networking is something that's never gonna go away, and it's even actually increasingly more important to meet people in real life. So it sounds like you've definitely benefited from the, you know, the publicity or the or the the exposure that you've gotten online, but actually converted that into, like, actual handshakes, which is really impressive and, like, I think a great way to build your career, especially, like, you know, at at a at a younger age too. Oh, yeah. Absolutely. Yeah. It's it's it's amazing. Yeah. And one one interesting insight that I've found about networking, especially because, like, you know, I guess within the creator space, normally, it's kind of about, when you help people, you get help in return. Right? Like, the you know, maybe you give someone a shout out and they'll be like, oh, wow. Like, I didn't even know about you before, but, like, you're giving me a shout out. That's super cool. Let me check out your stuff. Oh, yeah. I'll, like, I'd love to, like, add you as a shout out. And so it's kinda like cool that you've realized that networking really is about, like, giving value first and then, like, getting value back. And, it's not like, hey, you know, tell me about your life story. I'm here as, like, a person that you can talk to even though you know nothing about, like, you don't you don't know me at all. And then, like, after you tell me your life story, can I ask you, like, for a job? You know? Yeah. It's nothing it's nothing like about that. Sounds like the typical LinkedIn message. Yeah. Yeah. Yeah. Yeah. Exactly. But the the thing that I found particularly especially as I help other people is, like, it's a little easier for me to kind of do that giving value thing when I have something like, you know, my audience, my LinkedIn post, my newsletter thing, all that stuff. But I I could see it being difficult for someone that is maybe, like, just getting started in the industry or something like that and it's, like, you know, maybe they just, like, my my just view of it from from where I'm looking, it doesn't look like they have that much to give. But I also don't know, You know, if they like, that's the high level principle. If you can find something to give, then you're good. But I don't I don't really know, like, what I can suggest to them. That's part of the issue that I run into when I try to help people with that. Yeah. Do you so looking back at your quick journey from junior engineer to senior engineer and building this audience, you know, what do you did you have this where you're at now, did you kind of have this as a goal back 4 or 5 years ago? And then what is your goal for the next 5 years? You mentioned love the love for teaching, and you would do teaching if it was more, lucrative. But, you know, where do you do you wanna move further into leadership and become even more of a mentor for people who are both in your company but also external, or would you rather go down the content creation leader or teacher type, journey? Yeah. I I think the content creation, it needs to come from your actual experience. If maybe I wanted to write, you know, indefinitely about how to become a senior engineer because that's where I am right now, right, then I could leave my, you know, leave my job maybe and try this out. But, like, I you know, it would be, it would be, like, inauthentic of me to try to expand outward and say, hey. Here's how you go from senior engineer to staff engineer. Here's how you go from, you know, senior engineer to manager. I haven't tried those things out. So Yeah. I think one of the unique things that, like, I, bring to the table, like, as a creator is I do have, like, a wide range of experiences in a short amount of time. Like, I, you know, I I worked at Twitter as, like, internship. I got I got rejected, like, on my like, I got I also I got the return offer, but I also got rejected. So I, like, I know what that both of those, like, feel like. And I also, like, worked at a startup, you know, crypto startup, like, 3, 4 person thing, you know, like, while I was in university. And then, like, I did Gusto, which was, like, medium size. So, like, just in those 3 alone, I basically have, like, all the different sizes. Like, I have, like, super small, like, Gusto, like, medium size, Twitter, like, you know, bigger. And then now I'm kinda, like, in between, like, 0 and gusto size right now. I qualified. Yeah. So I think, like, if I could just keep on, you know, varying the experiences up and and, like, you know, just just add to the repertoire of different things that I've tried, then, it'll probably, you know, make me just be able to relate to pretty much anybody, hopefully, and and keep writing about, you know, things that that hopefully provide value to people. Yeah. No. That's that's such a great point is continuing on the the leadership to, you know, higher positions will naturally create that content that other people are looking for. So it's it's almost like you don't have to make that decision, yet you just kind of continue the path that you've been on, and both will come naturally naturally because it sounds like you're a hard worker and you're curious and that that from what who we've who we've talked to before for this podcast and just from our own personal experiences, that's the kind of things that it takes to continue climbing up in the ranks. And so that's such a good point that you don't have to make that decision. You just keep going where what you're doing, and both will naturally come. So Yeah. The inspiration for the content will seem to generate itself. Right? And then it's just, like, translating that experience into content. Exactly. Yeah. Yeah. I I feel like it would it would be, difficult to come up with ideas, you know, after you're, like, you know, not, you know, in a tech working environment for, you know, the 8 hours a day. At the same time, though, it is it is a lot of work, to do both. So so I don't know. I'm gonna try to figure out something, but, you know, for TBD. Yeah. Well, there's one position that it sounds like you haven't had, and that is founder of a company. So True. Yeah. Maybe if, you know, you are your founder of your content, but maybe you've been at a startup, but you haven't been the founder. So maybe that's in your journey eventually. Yeah. So so, you know, what have you learned over the years? What would be some good advice that you give to people who are listening? Pretend that the listeners are are you're their mentor? You know? What would you what advice would you give them from your experience? Well, so it's tough to give any general advice because every depending on the stage of your career, the advice is gonna be, you know, different. But if I had to give, like, something, you know, maybe to everyone, then I would say, one one really nice takeaway is if you accept that there will always be room to grow, then there you will never be afraid of feedback. And, you know, one way that that kinda sees itself is, a lot of time, you know, we're scared to ask our peers, you know, for feedback or say, hey. How did I do, like, in that presentation? Or, like, how do you how do you feel like, do you feel like I could have done anything better, like, in that meeting that I just ran? Or, you know, like, how was my how did I how did I do leading this project or something like that? Or, like, just anything like that. A lot of times, we're a little scared to ask. And understandably, you know, we all kinda put our we we put our best foot forward and we we we don't wanna hear that potential that we didn't do as best as we can. But at the same time, the the those two things, like, you can put your best foot forward and you can also always grow. So if you accept that, then you'll constantly seek out that feedback, and you'll also constantly be growing. Yeah. Wow. I might just put that phrase on my wall, you know, because that that's actually some really, really good advice. You know? So that's very good advice that was that was very generic for generic questions. So well well said. Hopefully, broadly applicable. Yeah. Yeah. Yeah. Awesome. So I guess, you know, we'll we'll wrap it up here. It's been a awesome conversation. But, you know, we've mentioned throughout the conversation some of the things that you're doing, but, obviously, we want people to be, followers of you because you're putting out good things. You're putting out good advice, good content. So where can people find you? You know? Where what is the courses that you teach? What is your newsletter? Just repeat that. And then if people wanna follow you in other places, where where are you? Yeah. Yeah. Absolutely. So, LinkedIn is the main place where I post every day, you know, updates on kinda everything. So just search Jordan Cutler on LinkedIn. The newsletter is called high growth engineer. And the course, if you're interested in that, I don't think I will be running that many more cohorts. I think I'm gonna explore different avenues. So try to get in either on this upcoming cohort, although I don't know when the podcast will be out Yep. Or the next one might be my last one. We'll see. And that one is called mid level to senior engineer, on Maven. Okay. Awesome. Well, any any last questions from you, Pedro? I feel like I just took over that end. No. That's that's amazing. Definitely excited to to continue following your journey as I'm sure most of the, you know, the viewers and the listeners are as well. And, yeah, so subscribe. Right? Yeah. Yeah. I think I think the last word I'll say is I think it's really motivational. There's a lot of young engineers out there and and even older engineers too, so I don't wanna pigeonhole into young engineers who they're curious and they wanna learn. And so I think some of the articles that you've put out about, you know, being a go to in a specific, field or a specific piece of content or what it what may it be. But, it's really, encouraging to see somebody who's younger than me who puts in the effort and has had the success that, they can then share with other people. So it's been a fantastic conversation. Yeah. Definitely appreciate it. Yeah. Yeah. It's been great, guys. I really appreciate it.","4464b245-d6a0-4ec6-93d1-1a5c4c22a6e5",[2684,2685,2686],"fe544209-43f4-46ad-8e81-571b70fbe1ca","9962d71f-c125-4f09-9cd5-687cfeb0de23","9d080d12-4247-4622-adaf-ba6f1b796ac6",[],{"year":978,"number":1830,"id":1842,"episodes":2689,"show":2690},[1844,1845,1846,1847,1825],{"title":1849,"slug":1850},{"id":2692,"slug":2693,"vimeo_id":2694,"description":2695,"tile":2696,"length":527,"resources":12,"people":12,"episode_number":264,"published":2697,"title":2698,"video_transcript_html":2699,"video_transcript_text":2700,"content":12,"seo":2701,"status":19,"episode_people":2702,"recommendations":2704,"season":2705},"d13394df-7843-498f-bfd6-0c3c6e95b546","cycle-around-new-york-city","950738035","Citi Bike publish the millions of rides used by their customers around New York City's thousands of stations. Kevin shows you how Directus can be used to further explore, understand, and remix this dataset both in the Data Studio and via API.","93c14160-8b9c-4fe7-9db8-0831d9c914de","2024-06-12","Cycle Around New York City","\u003Cp>Speaker 0: In this show, we give new life to open datasets with the help of directors. Join me as we explore, analyze, and generate APIs to improve access to and to democratize data. Before we get started, if you have or know of an interesting open dataset and want us to use it, just reach out and it might just feature on a future episode. Today's dataset comes from Citi Bike. Across New York City, there are over 2,000 stations where rental bikes can be picked up and dropped off.\u003C\u002Fp>\u003Cp>Every customer ride is represented in this dataset detailing when and where a bike was picked up and when and where it was dropped off, as well as some additional metadata about the vehicle and the customer. Now in March 2024, there were over 2,000,000 rides that took place, but for this show, I've just imported the first million to direct us. Now compared to other, episodes, I've done a little more preprocessing on this data, And that's because I wanted to have 2 collections when this data set only provides one set of data. I wanted to have stations and then rides separately. So I needed to split out the stations from the rides data and, import those separately.\u003C\u002Fp>\u003Cp>Additionally, like every previous episode, I converted station coordinates to GeoJSON objects to enable mapping features inside of. And with that, I think we're ready to jump in and get started. So as you can see here, we have 2 separate collections, stations and rides. Each station has its internal identifier in the Citi Bike system and the friendly name. Now if we go into any of these, we additionally see the map rendered because we converted that set of coordinates to GeoJSON along with all of the rides that were started and ended inside of this dataset.\u003C\u002Fp>\u003Cp>This is one of the less popular station, it seems, compared to others. Now because this is all inside of Directus, we can start using Directus' powerful filter and search interface in order to narrow this down if we want. But for now, I think we will just use everything. Now inside of layout options, we can change the layout. Right now, we are rendering this data inside of a table, which, of course, is a standard way to represent data in a database.\u003C\u002Fp>\u003Cp>But what's really nice is that Directus, out of the box, provides a map interface a map layout rather. And so we can explore this data kind of a little more visually and as it is represented in the real world. So this is just 820 4 of the items that are inside this area of the map. And we can click in if we want to individual items like this. So here we see them, perhaps a little more of a popular station in terms of ride started and ride ended.\u003C\u002Fp>\u003Cp>Talking about rides, let's look at the rides collection. So this is a huge dataset. This is a 1000000 items. And once again, there was over 2,000,000 in March, but I've just imported the first million, because that was the first CSV that is provided from Citibank. They chunk it up.\u003C\u002Fp>\u003Cp>And each individual ride has a unique identifier, the station start and time, the station end and time, whether or not the member is a casual user or a member, and whether the bike is a classic bike or an electric bike. Now this station start and end, these are relational fields. So these link back to the stations, to the stations items. So we can click here, and we can see the station item that we just opened up before. Now, again, we can explore this in many different ways inside of Directus.\u003C\u002Fp>\u003Cp>But when it comes to analyzing data, we want to use Directus insights. So let's go see something that I've set up earlier. This is a directus insights dashboard that I built before I started recording. And if I'm completely honest, this was one episode where I got a little bit of choice paralysis about what I could possibly represent because there is just so much rich data inside of this dataset. But for now, this is what I've built to demonstrate something that's possible.\u003C\u002Fp>\u003Cp>So here I have a relational global variable field, which means it will show every station in the interface. So I clicked 1. I clicked this one here, And all of the other panels relate to the to the item that I selected. So firstly, we have the internal identifier. We see how many rides were started at this station and ended, which perhaps is interesting in itself.\u003C\u002Fp>\u003Cp>People seem to want to end at this location more than leave it. We get to see the number of rides started per day within this dataset and whether or not it is a cat mostly casual user or member or, a member user type and what kind of bikes are picked up. Now one thing that's really interesting about this one that I do think is telling, for example, is almost every station I have tried this with in the past has a majority member, and less casual users. But, of course, being Central Park, this is definitely a tourist location. So it's no surprise that perhaps there are more casual users picking up bikes or dropping off bikes at this location.\u003C\u002Fp>\u003Cp>So that's just really just an interesting note, I suppose. Maybe one way this can be used is if you're spotting there are areas where there are more casual users, but there are a high volume of users. Maybe this is an opportunity to run campaigns to convert people into members, for example. But I'm sure there's a lot more you can do with this data. Now this isn't all.\u003C\u002Fp>\u003Cp>Everything you do inside of directors can also be done using the automatically generated APIs. So let's go see how they work. So I've prepared a small number of requests just to demonstrate the capabilities of the API. But this is just a tiny sliver of what's available in terms of fetching data. So firstly, we have a stations collection.\u003C\u002Fp>\u003Cp>And as a result, we automatically have this endpoint slash items slash stations to go fetch all of the stations. So here we see, you know, an array is returned of objects. Each object represents 1 item in the in the collection, and this looks very similar to the UI that we saw in the editor. We have an internal ID, a name, a point, a set of coordinates, in GeoJSON format, the ride started, and the ride's ended. Now given that they all have an ID, you can also further break this down and perhaps just get a single item back.\u003C\u002Fp>\u003Cp>So this is now not an array anymore. It's just that single object. So that's stations. We also have individual rides. So once again, each one has an ID and all of that data we just saw.\u003C\u002Fp>\u003Cp>Right? All of that data we just saw. Station start and station end are IDs of, of the stations. Now you can further start to narrow this down by using Directus' filter language, filter filter syntax. So here, we are applying 2 filters.\u003C\u002Fp>\u003Cp>And all we're doing is saying, grab all of the rides that happened inside of the 3rd month. In this case, that's all of them because they're all in March, but that may not always be the case, and on the 7th day. So this is everything from the 7th March. You could further, of course, narrow this down to specific stations if you want or specific member types and so on and so forth. You can just add more filters on the end here.\u003C\u002Fp>\u003Cp>Now this final request is not so much about fetching data, but rather analyzing it using our aggregate and group by query parameters. And it's these which actually power the panels inside of Directus insights. So here we're saying, count all of the items and then group them by the day in which that ride was started. And what we get as a as a result is for every day, we get a count. So we get to say, hey.\u003C\u002Fp>\u003Cp>On the 1st March, there were 32,329 rights that took part inside of this dataset and so on and so forth. For every single day inside of this dataset, there is a, a different count. So that's a little bit about how you can use Directus Connect and the automatically generated APIs in order to explore the data either to actually query the data itself or run some analysis. So once again, I hope you found this interesting. And if you have an interesting dataset or know of an interesting dataset that we can use in a future episode, just reach out.\u003C\u002Fp>\u003Cp>I love to see it. Until next time. See you later.\u003C\u002Fp>","In this show, we give new life to open datasets with the help of directors. Join me as we explore, analyze, and generate APIs to improve access to and to democratize data. Before we get started, if you have or know of an interesting open dataset and want us to use it, just reach out and it might just feature on a future episode. Today's dataset comes from Citi Bike. Across New York City, there are over 2,000 stations where rental bikes can be picked up and dropped off. Every customer ride is represented in this dataset detailing when and where a bike was picked up and when and where it was dropped off, as well as some additional metadata about the vehicle and the customer. Now in March 2024, there were over 2,000,000 rides that took place, but for this show, I've just imported the first million to direct us. Now compared to other, episodes, I've done a little more preprocessing on this data, And that's because I wanted to have 2 collections when this data set only provides one set of data. I wanted to have stations and then rides separately. So I needed to split out the stations from the rides data and, import those separately. Additionally, like every previous episode, I converted station coordinates to GeoJSON objects to enable mapping features inside of. And with that, I think we're ready to jump in and get started. So as you can see here, we have 2 separate collections, stations and rides. Each station has its internal identifier in the Citi Bike system and the friendly name. Now if we go into any of these, we additionally see the map rendered because we converted that set of coordinates to GeoJSON along with all of the rides that were started and ended inside of this dataset. This is one of the less popular station, it seems, compared to others. Now because this is all inside of Directus, we can start using Directus' powerful filter and search interface in order to narrow this down if we want. But for now, I think we will just use everything. Now inside of layout options, we can change the layout. Right now, we are rendering this data inside of a table, which, of course, is a standard way to represent data in a database. But what's really nice is that Directus, out of the box, provides a map interface a map layout rather. And so we can explore this data kind of a little more visually and as it is represented in the real world. So this is just 820 4 of the items that are inside this area of the map. And we can click in if we want to individual items like this. So here we see them, perhaps a little more of a popular station in terms of ride started and ride ended. Talking about rides, let's look at the rides collection. So this is a huge dataset. This is a 1000000 items. And once again, there was over 2,000,000 in March, but I've just imported the first million, because that was the first CSV that is provided from Citibank. They chunk it up. And each individual ride has a unique identifier, the station start and time, the station end and time, whether or not the member is a casual user or a member, and whether the bike is a classic bike or an electric bike. Now this station start and end, these are relational fields. So these link back to the stations, to the stations items. So we can click here, and we can see the station item that we just opened up before. Now, again, we can explore this in many different ways inside of Directus. But when it comes to analyzing data, we want to use Directus insights. So let's go see something that I've set up earlier. This is a directus insights dashboard that I built before I started recording. And if I'm completely honest, this was one episode where I got a little bit of choice paralysis about what I could possibly represent because there is just so much rich data inside of this dataset. But for now, this is what I've built to demonstrate something that's possible. So here I have a relational global variable field, which means it will show every station in the interface. So I clicked 1. I clicked this one here, And all of the other panels relate to the to the item that I selected. So firstly, we have the internal identifier. We see how many rides were started at this station and ended, which perhaps is interesting in itself. People seem to want to end at this location more than leave it. We get to see the number of rides started per day within this dataset and whether or not it is a cat mostly casual user or member or, a member user type and what kind of bikes are picked up. Now one thing that's really interesting about this one that I do think is telling, for example, is almost every station I have tried this with in the past has a majority member, and less casual users. But, of course, being Central Park, this is definitely a tourist location. So it's no surprise that perhaps there are more casual users picking up bikes or dropping off bikes at this location. So that's just really just an interesting note, I suppose. Maybe one way this can be used is if you're spotting there are areas where there are more casual users, but there are a high volume of users. Maybe this is an opportunity to run campaigns to convert people into members, for example. But I'm sure there's a lot more you can do with this data. Now this isn't all. Everything you do inside of directors can also be done using the automatically generated APIs. So let's go see how they work. So I've prepared a small number of requests just to demonstrate the capabilities of the API. But this is just a tiny sliver of what's available in terms of fetching data. So firstly, we have a stations collection. And as a result, we automatically have this endpoint slash items slash stations to go fetch all of the stations. So here we see, you know, an array is returned of objects. Each object represents 1 item in the in the collection, and this looks very similar to the UI that we saw in the editor. We have an internal ID, a name, a point, a set of coordinates, in GeoJSON format, the ride started, and the ride's ended. Now given that they all have an ID, you can also further break this down and perhaps just get a single item back. So this is now not an array anymore. It's just that single object. So that's stations. We also have individual rides. So once again, each one has an ID and all of that data we just saw. Right? All of that data we just saw. Station start and station end are IDs of, of the stations. Now you can further start to narrow this down by using Directus' filter language, filter filter syntax. So here, we are applying 2 filters. And all we're doing is saying, grab all of the rides that happened inside of the 3rd month. In this case, that's all of them because they're all in March, but that may not always be the case, and on the 7th day. So this is everything from the 7th March. You could further, of course, narrow this down to specific stations if you want or specific member types and so on and so forth. You can just add more filters on the end here. Now this final request is not so much about fetching data, but rather analyzing it using our aggregate and group by query parameters. And it's these which actually power the panels inside of Directus insights. So here we're saying, count all of the items and then group them by the day in which that ride was started. And what we get as a as a result is for every day, we get a count. So we get to say, hey. On the 1st March, there were 32,329 rights that took part inside of this dataset and so on and so forth. For every single day inside of this dataset, there is a, a different count. So that's a little bit about how you can use Directus Connect and the automatically generated APIs in order to explore the data either to actually query the data itself or run some analysis. So once again, I hope you found this interesting. And if you have an interesting dataset or know of an interesting dataset that we can use in a future episode, just reach out. I love to see it. Until next time. See you later.","003dfe29-aa33-4254-8f7c-81ffb324b087",[2703],"9b19a43c-34bc-428e-b438-1ffa66f23523",[],{"year":978,"number":2706,"id":2707,"episodes":2708,"show":2711},40,"4753d194-3ddc-4a66-a921-8bec80139ccf",[2709,2710,2692],"b0b59e98-dd57-4fe4-a5df-60c8a5ec6415","246b31c7-0e1e-4a1c-8b9f-2a078863bb0f",{"title":2712,"slug":2713},"Democratizing Data","democratizing-data",{"id":2495,"slug":2715,"vimeo_id":2716,"description":2717,"tile":2718,"length":264,"resources":12,"people":12,"episode_number":159,"published":2719,"title":2720,"video_transcript_html":2721,"video_transcript_text":2722,"content":12,"seo":2723,"status":19,"episode_people":2724,"recommendations":2726,"season":2727},"wtf-is-composable","941313811","These days, it's nearly impossible to avoid the term \"composable.\" But what does it actually mean in the context of development? ","63050d36-c141-4876-b085-7c4fc19f7816","2024-06-11","WTF is Composable?","\u003Cp>Speaker 0: Hello, and welcome back to another episode of Buzzword Wilderness. I'm Matt. And today, we're tackling a term that you have probably heard of, which is composable. It's a term, it's an approach, it's a philosophy. It's really changing the game in how we build and and talk about digital platforms.\u003C\u002Fp>\u003Cp>It, emphasizes adaptability and flexibility. So let's dive in. What is composable? So composable technology is all about building systems that are flexible and easy to adjust. The systems are are made up of different parts that can be combined in different ways or swapped out, really depends on on the demands you have and how those change.\u003C\u002Fp>\u003Cp>Change. Imagine this is like LEGOs. Each block is a piece of your technology setup, whether it's for storing data, managing content, or anything else. You can rearrange these blocks to fit your needs or teams, whether you're scaling up or shifting focus. Application and technology.\u003C\u002Fp>\u003Cp>So composable architecture really helps businesses stay agile. Instead of replacing an entire system when needs change, you can simply change parts of it. It's like changing the tires instead of buying a whole new car. For developers, especially, this means more freedom. They can choose the best tools for specific tasks without being locked into one system or vendor, and you can customize it exactly like your team needs the back end.\u003C\u002Fp>\u003Cp>A few episodes ago, we talked about digital experience platforms. DXPs traditionally came as one complete package, hard to modify if your needs change, but now a lot of them are embracing these composable principles. Thinking like a smartphone. Right? The older models came fixed with apps you couldn't change.\u003C\u002Fp>\u003Cp>The Apple iPhone came out and completely changed the game. Get the apps you want, the apps you need, for that moment. Download them. Delete them. Do whatever you want.\u003C\u002Fp>\u003Cp>It's completely flexible all just from one base device. So I don't think composable technology is really just another trend or buzzword. It's a new way of thinking about how to structure the back end of your business. You'll see a lot of this in the coming years. I think, as a lot more enterprises start to adopt it in, the terminology is just starting to take off.\u003C\u002Fp>\u003Cp>So I appreciate you sticking with me as always, and I hope this explanation clears up some of the fog around composable. I look forward to seeing you again on the next episode. I appreciate your time.\u003C\u002Fp>","Hello, and welcome back to another episode of Buzzword Wilderness. I'm Matt. And today, we're tackling a term that you have probably heard of, which is composable. It's a term, it's an approach, it's a philosophy. It's really changing the game in how we build and and talk about digital platforms. It, emphasizes adaptability and flexibility. So let's dive in. What is composable? So composable technology is all about building systems that are flexible and easy to adjust. The systems are are made up of different parts that can be combined in different ways or swapped out, really depends on on the demands you have and how those change. Change. Imagine this is like LEGOs. Each block is a piece of your technology setup, whether it's for storing data, managing content, or anything else. You can rearrange these blocks to fit your needs or teams, whether you're scaling up or shifting focus. Application and technology. So composable architecture really helps businesses stay agile. Instead of replacing an entire system when needs change, you can simply change parts of it. It's like changing the tires instead of buying a whole new car. For developers, especially, this means more freedom. They can choose the best tools for specific tasks without being locked into one system or vendor, and you can customize it exactly like your team needs the back end. A few episodes ago, we talked about digital experience platforms. DXPs traditionally came as one complete package, hard to modify if your needs change, but now a lot of them are embracing these composable principles. Thinking like a smartphone. Right? The older models came fixed with apps you couldn't change. The Apple iPhone came out and completely changed the game. Get the apps you want, the apps you need, for that moment. Download them. Delete them. Do whatever you want. It's completely flexible all just from one base device. So I don't think composable technology is really just another trend or buzzword. It's a new way of thinking about how to structure the back end of your business. You'll see a lot of this in the coming years. I think, as a lot more enterprises start to adopt it in, the terminology is just starting to take off. So I appreciate you sticking with me as always, and I hope this explanation clears up some of the fog around composable. I look forward to seeing you again on the next episode. I appreciate your time.","810221b9-e030-494b-adf1-f132251bb1c8",[2725],"f1ed5596-6ed0-42bb-856b-b074252cf580",[],{"year":978,"number":674,"id":2490,"episodes":2728,"show":2729},[2492,2493,2494,2495,2476],{"title":2497,"slug":2498},{"id":2731,"slug":2732,"vimeo_id":2733,"description":2734,"tile":2735,"length":527,"resources":12,"people":12,"episode_number":480,"published":2736,"title":2737,"video_transcript_html":2738,"video_transcript_text":2739,"content":12,"seo":2740,"status":19,"episode_people":2741,"recommendations":2743,"season":2744},"4ba7fb8e-72ae-4e8f-908a-8d9032a1603f","just-do-it","941267333","It might seem hard to get started theming, but it's important to just do it.  Join the GOAT of theming Bry Ross and he kicks out a fresh theme that captures the essence of footwear and apparel legends – Nike.","c134ca69-4f5a-4f54-9227-4b0160cd08b0","2024-06-10","Nike (Just Do It)","\u003Cp>Speaker 0: Hi. Welcome back. You know, when I do public theming shows, I get a lot of questions from folks. Ask me they say, Bry, how did you learn to paint like this? How did you learn to paint those amazing themes?\u003C\u002Fp>\u003Cp>I'll never be able to do that. And I look at them in the face, and I say, look, if I can do this, you can. Just a small town boy from the hills of West Virginia. With enough practice and just a little bit of creativity, you too can paint beautiful themes. Let's dive into today's theme.\u003C\u002Fp>\u003Cp>We're gonna be painting a beautiful theme for Nike. We're just gonna do it. Alright. So sizing up their web site. It's got a very, very black and white color palette.\u003C\u002Fp>\u003Cp>Just a couple of neutral tones. So this is gonna be a challenging theme. And anytime you're theming, one of the things you have to ask yourself is this a dark theme or is this a light theme and depending on how you're feeling you have to let that theme guide you along the journey. Let's start. So we've got our project settings out of the way.\u003C\u002Fp>\u003Cp>Let's look at getting our logo from the Nike website. Using my trusty Logitech keyboard here. Just a solid bit of kit. Really good with the hex codes. Great key action as well.\u003C\u002Fp>\u003Cp>Alright So we're gonna take that project logo. Alright. We're just gonna upload this logo. Great. SVG.\u003C\u002Fp>\u003Cp>It should be showing my logo there. Not sure what's wrong. Let's let's just keep going with that. Actually, let's go in. We're gonna look for the Nike brand assets and we're gonna download these logos.\u003C\u002Fp>\u003Cp>Let's let's grab all these. We need an icon. We'll use the icon for the public favicon, which is, close relative to ShakaCon. And now we'll go in and just copy this link address. We're gonna look for the Nike public foreground background image here.\u003C\u002Fp>\u003Cp>Trying to find this background image. Background images are important because that's what people see when they first log in to your Directus project. We want to set a good example, evoke all the proper emotions, and this image of of these soccer players just really drives it home for me. Alright, so still looking to find this particular image. There we go.\u003C\u002Fp>\u003Cp>Alright. Sometimes themes are just slow to reveal themselves. Looks like we've got this image. That's what we're gonna use for our public background. That's great.\u003C\u002Fp>\u003Cp>Now the last thing that I'm gonna do is just go in and paint some beautiful project logos in here. Of course, we have to unzip the file that we downloaded first to be able to use those. So great. Okay. Alright.\u003C\u002Fp>\u003Cp>So we're gonna say welcome to Nike PEM. This is where we're gonna store all of our products so we can distribute that information wherever it needs to be. Alright, let's fix our logo there because there were some issues and now this is looking great. Looking really, really good. Okay.\u003C\u002Fp>\u003Cp>Alright. So we'll just extract this. For some reason that's not working. Let's just paint in a new logo there. Just take your time paint in a new logo.\u003C\u002Fp>\u003Cp>And I'm using the dark logo because I want to mirror the Nike website. We're gonna use the Directus Minimal theme as our base. Lots of white space, lots of white calm atmosphere. That's what we're looking for out of this theme. We're going to adjust the, logo bar at the top.\u003C\u002Fp>\u003Cp>Module bar logo. Don't forget your hyphens. Very important in CSS to have your hyphens. And then we will add background. We'll make that white and we'll make that important.\u003C\u002Fp>\u003Cp>Or we could do transparent as well. Either one is gonna work in this case. But if you're adding that important modifier, make sure you hit the keys twice as hard so that it sticks. Alright. This is looking great.\u003C\u002Fp>\u003Cp>So we've got a really light, airy theme. It's the tone that we're trying to set. We don't want to get it in your way. We're just gonna let you do it. And that's what we're doing now.\u003C\u002Fp>\u003Cp>We're doing this theme together. You're doing great. Keep up the keep up the pace. Alright. So for our background, we're gonna use this subtle gray that they use.\u003C\u002Fp>\u003Cp>We're gonna just use that in our list. So this is where our secondary navigation shows up. And one of the things that I want to just remove here is are these borders. They're just, you know, normally, I like a good border to provide separation and contrast, but in this case, it's just it's not calming me down. It's it's adding stress.\u003C\u002Fp>\u003Cp>So let's just smooth those rough edges and remove those borders. You know, I don't see very many borders on the Nike website and we shall make it so within our Directus project. It's great. Alright. So now we'll just remove that border from the header.\u003C\u002Fp>\u003Cp>Just completely erase that like we had just a magic eraser. Those are great products as well, by the way. Very handy if you had little ones. Alright. So now we've got a great looking base.\u003C\u002Fp>\u003Cp>Let's go in and adjust our list view. So we're going to add a different background on hover and active states, just so we get a little bit of separation. Now this is where I really like to use my half inch brush because it's just great for painting all those fine details. You just go up and down and side to side, semicircle left semicircle right and then we're just gonna paint in a little bit of color mix action there to, you know, on hover. That's a pretty good effect.\u003C\u002Fp>\u003Cp>Maybe we lighten that just a little bit though. I like that effect. I like it. Just happy thoughts. Thoughts, just happy themes, lots of color mixing.\u003C\u002Fp>\u003Cp>Let the colors mix and match themselves into whatever form, shape, or fashion they need to be in. Wow. Okay. This is this has a really calming effect. Looks great.\u003C\u002Fp>\u003Cp>Alright. So we're gonna dive back into our customization. We're gonna go into our buttons under the module navigation and we're just going to set our project color and we really want to drive home to the user what page they're currently on, what module are they using. And there's nothing better than stark contrast to do that. So it's very clear now which module you're in and it it doesn't take away from the rest of the theme.\u003C\u002Fp>\u003Cp>It adds to it. Blends together harmoniously. Alright. So we're just gonna go in and, make a few time final tweaks to this with our half inch brush. We're just gonna adjust our background, our borders just a little bit.\u003C\u002Fp>\u003Cp>That's great. Looks great. Okay. Alright. Now, typography is everything to a theme.\u003C\u002Fp>\u003Cp>Good typography can elevate a theme. Just pass Mount Olympus. So what we're gonna do is copy this Nike Futura. Futura? Futura?\u003C\u002Fp>\u003Cp>I I don't know. Whatever it is I like it. It's a good font. So we're going to use a little font face rules here and then we're gonna use that palette knife. We're gonna be a little thicker here.\u003C\u002Fp>\u003Cp>We're just gonna paint that in. Paint that broad stroke in there, paint that broad theme in there. Great. So now we've got that strong bold headline up at the top that that boldly announces where we're at on the on the director's project. But there's one final touch, one final piece de resistance that we have to add to this.\u003C\u002Fp>\u003Cp>We're just gonna add type title, so we're gonna use that class. Just do a little text transform action and voila. Now we have the perfect Nike theme. That completes this wonderful theme, and thank you for joining me. This has been such a wonderful journey in theme building.\u003C\u002Fp>\u003Cp>We don't always have to use bright colors to have a bright impact. I hope to see you on the next episode. Bye bye.\u003C\u002Fp>","Hi. Welcome back. You know, when I do public theming shows, I get a lot of questions from folks. Ask me they say, Bry, how did you learn to paint like this? How did you learn to paint those amazing themes? I'll never be able to do that. And I look at them in the face, and I say, look, if I can do this, you can. Just a small town boy from the hills of West Virginia. With enough practice and just a little bit of creativity, you too can paint beautiful themes. Let's dive into today's theme. We're gonna be painting a beautiful theme for Nike. We're just gonna do it. Alright. So sizing up their web site. It's got a very, very black and white color palette. Just a couple of neutral tones. So this is gonna be a challenging theme. And anytime you're theming, one of the things you have to ask yourself is this a dark theme or is this a light theme and depending on how you're feeling you have to let that theme guide you along the journey. Let's start. So we've got our project settings out of the way. Let's look at getting our logo from the Nike website. Using my trusty Logitech keyboard here. Just a solid bit of kit. Really good with the hex codes. Great key action as well. Alright So we're gonna take that project logo. Alright. We're just gonna upload this logo. Great. SVG. It should be showing my logo there. Not sure what's wrong. Let's let's just keep going with that. Actually, let's go in. We're gonna look for the Nike brand assets and we're gonna download these logos. Let's let's grab all these. We need an icon. We'll use the icon for the public favicon, which is, close relative to ShakaCon. And now we'll go in and just copy this link address. We're gonna look for the Nike public foreground background image here. Trying to find this background image. Background images are important because that's what people see when they first log in to your Directus project. We want to set a good example, evoke all the proper emotions, and this image of of these soccer players just really drives it home for me. Alright, so still looking to find this particular image. There we go. Alright. Sometimes themes are just slow to reveal themselves. Looks like we've got this image. That's what we're gonna use for our public background. That's great. Now the last thing that I'm gonna do is just go in and paint some beautiful project logos in here. Of course, we have to unzip the file that we downloaded first to be able to use those. So great. Okay. Alright. So we're gonna say welcome to Nike PEM. This is where we're gonna store all of our products so we can distribute that information wherever it needs to be. Alright, let's fix our logo there because there were some issues and now this is looking great. Looking really, really good. Okay. Alright. So we'll just extract this. For some reason that's not working. Let's just paint in a new logo there. Just take your time paint in a new logo. And I'm using the dark logo because I want to mirror the Nike website. We're gonna use the Directus Minimal theme as our base. Lots of white space, lots of white calm atmosphere. That's what we're looking for out of this theme. We're going to adjust the, logo bar at the top. Module bar logo. Don't forget your hyphens. Very important in CSS to have your hyphens. And then we will add background. We'll make that white and we'll make that important. Or we could do transparent as well. Either one is gonna work in this case. But if you're adding that important modifier, make sure you hit the keys twice as hard so that it sticks. Alright. This is looking great. So we've got a really light, airy theme. It's the tone that we're trying to set. We don't want to get it in your way. We're just gonna let you do it. And that's what we're doing now. We're doing this theme together. You're doing great. Keep up the keep up the pace. Alright. So for our background, we're gonna use this subtle gray that they use. We're gonna just use that in our list. So this is where our secondary navigation shows up. And one of the things that I want to just remove here is are these borders. They're just, you know, normally, I like a good border to provide separation and contrast, but in this case, it's just it's not calming me down. It's it's adding stress. So let's just smooth those rough edges and remove those borders. You know, I don't see very many borders on the Nike website and we shall make it so within our Directus project. It's great. Alright. So now we'll just remove that border from the header. Just completely erase that like we had just a magic eraser. Those are great products as well, by the way. Very handy if you had little ones. Alright. So now we've got a great looking base. Let's go in and adjust our list view. So we're going to add a different background on hover and active states, just so we get a little bit of separation. Now this is where I really like to use my half inch brush because it's just great for painting all those fine details. You just go up and down and side to side, semicircle left semicircle right and then we're just gonna paint in a little bit of color mix action there to, you know, on hover. That's a pretty good effect. Maybe we lighten that just a little bit though. I like that effect. I like it. Just happy thoughts. Thoughts, just happy themes, lots of color mixing. Let the colors mix and match themselves into whatever form, shape, or fashion they need to be in. Wow. Okay. This is this has a really calming effect. Looks great. Alright. So we're gonna dive back into our customization. We're gonna go into our buttons under the module navigation and we're just going to set our project color and we really want to drive home to the user what page they're currently on, what module are they using. And there's nothing better than stark contrast to do that. So it's very clear now which module you're in and it it doesn't take away from the rest of the theme. It adds to it. Blends together harmoniously. Alright. So we're just gonna go in and, make a few time final tweaks to this with our half inch brush. We're just gonna adjust our background, our borders just a little bit. That's great. Looks great. Okay. Alright. Now, typography is everything to a theme. Good typography can elevate a theme. Just pass Mount Olympus. So what we're gonna do is copy this Nike Futura. Futura? Futura? I I don't know. Whatever it is I like it. It's a good font. So we're going to use a little font face rules here and then we're gonna use that palette knife. We're gonna be a little thicker here. We're just gonna paint that in. Paint that broad stroke in there, paint that broad theme in there. Great. So now we've got that strong bold headline up at the top that that boldly announces where we're at on the on the director's project. But there's one final touch, one final piece de resistance that we have to add to this. We're just gonna add type title, so we're gonna use that class. Just do a little text transform action and voila. Now we have the perfect Nike theme. That completes this wonderful theme, and thank you for joining me. This has been such a wonderful journey in theme building. We don't always have to use bright colors to have a bright impact. I hope to see you on the next episode. Bye bye.","597ace33-814f-4b7d-ad60-56fac119e49d",[2742],"022c239d-4b4b-41a4-868c-1988fe165f23",[],{"year":978,"number":2745,"id":2746,"episodes":2747,"show":2759},41,"61d16eae-2910-4009-a3e0-65e1a96be877",[2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2731],"ef8afd5a-ba9f-49c5-ae0e-091de1d7d85e","e7dd4c60-0a87-4ce5-a054-fbef2c80bd74","a4c00ba5-0cb9-4b40-90a4-8c03b4410e4b","22e15c9f-e0d1-4a6b-8dd1-c5b51c9f1568","2b1886f5-7508-44dc-b0a9-c4f73e18d889","c9c86f91-d3e9-4287-9bfc-4c0fd50c6dfd","c338c610-19d5-45b1-af66-3cf34c67a2ec","07649ac4-e135-42c0-bcc9-bafb980dd9ec","ed8c1a76-e3b6-4c75-b4b9-9a7a37ebcf61","118828b8-a8d9-4329-8537-aa3716455328","03a91de3-cffa-41be-a56e-6448ae0cbfd8",{"title":2760,"slug":2761},"The Joy of Theming","joy-of-theming",{"id":2320,"slug":2763,"vimeo_id":2764,"description":2765,"tile":2766,"length":2767,"resources":12,"people":12,"episode_number":11,"published":2768,"title":2769,"video_transcript_html":2770,"video_transcript_text":2771,"content":12,"seo":2772,"status":19,"episode_people":2773,"recommendations":2775,"season":2776},"intercom-messenger","954528032","It's a race to a real-time chat widget in this episode. Bryant tries to build his own version of the Intercom Messenger - a business messaging widget that includes live chat, helpful articles, and other rich content.","28671146-5bfb-411f-9bea-a57589a931a0",64,"2024-06-07","MIssion: Intercom Messenger Clone","\u003Cp>Speaker 0: Alright. Welcome back to yet another episode of 100 Apps, 100 Hours. I'm your host, Brian Gillespie, developer advocate here at Directus. And if you're new to the show, the basic gist is this, we take 60 minutes and we try to build or rebuild some of your favorite apps or some of your wildest app ideas, in that 60 minutes, we achieve our stated goals or we publicly fail trying. And by we, I mean me.\u003C\u002Fp>\u003Cp>That's okay though. When I fail, I fail spectacularly, usually. But I've got 3 little girls so I'm used to being hobbled. That's okay. There are just two rules.\u003C\u002Fp>\u003Cp>Number 1, you have 60 minutes to plan and build, which does pose some challenges. There's no more, no less. And number 2 is you use whatever you have at your disposal, just like in the real world. You know, make it work. Figure it out.\u003C\u002Fp>\u003Cp>And in today's episode, we are going to take advantage of rule number 2. So what are we building today? We are working on the intercom Messenger. Right? Intercom is a, like, help desk, onboarding tool, messaging tool.\u003C\u002Fp>\u003Cp>They've got all these things that are wrapped up into it. It looks like their latest positioning is, like, customer service, which is great. But like most of their it comes from this little messenger widget over here. Right? So it's not just live chat.\u003C\u002Fp>\u003Cp>It is kind of like a rich format where they can check product news, they can chat with the support team, there's bots, there's AI, there are articles integrated, you know, there's, like, product tours and stuff like that. The the main things that we're concerned with here are this kind of nice interface for this, and then number 2, like this actual, like, live chat functionality and maybe the ability to add articles. So we keep the user in context, That's what we're gonna be tackling today. I I think it's doable, not a 100% sure. Let's go in and start the clock on this.\u003C\u002Fp>\u003Cp>We will start our timer, 60 minutes and away we go. Alright. So as far as what type of functionality we want out of this, you know, what are the the jobs to be done or what are the goals for this specific project. We want to be able to chat chat with visitors in real time and then, you know, maybe display rich content inside the messenger. This could take the form of articles, help center stuff, updates, etcetera, and I totally cannot spell.\u003C\u002Fp>\u003Cp>How are we gonna get there though? Right? Normally, if you've watched any of the past episodes, at this point, I'm like doing some data modeling. The starting point for today is a little different in that I've already got a project that we're going to lean on. It's called Agency OS and it already has a a fully fleshed out data model.\u003C\u002Fp>\u003Cp>You can explore the front end of this at agencyos.dev. But this actual project you can download and test out yourself. It lives in our Directus Labs organization at, Directus dash labs slash agency dash os on GitHub. Basically, there's some instructions, but the main features of this include a website, CRM project tracker type of thing, and then you have a client portal. So on the front end, it kinda looks like this, your standard agency website.\u003C\u002Fp>\u003Cp>You know, we've got all these different blocks. It uses the page builder inside Directus. We got blog posts and articles. You know, there's a a help center that should be displaying some articles that doesn't. And then you have, this client portal where clients can log in, keep track of their different projects, see files, see their invoices, pay for those specific invoices.\u003C\u002Fp>\u003Cp>Right? This is this is pretty neat functionality. And then, you know, you would basically manage your entire workflow from your deals all the way through, like proposals, projects, and tasks inside your direct assistance. Right? So I can see a list of all the tasks that are necessary to complete this specific project, who's the organization, who are our contacts for that organization, And I can manage this whole thing here.\u003C\u002Fp>\u003Cp>But in this case, I'm using this because we've already got this conversations model and this messages collection. And we have things like help center collections and help center articles already mapped out, which is is really nice, just to kind of piggyback off of what I've already built in the past. So if we dive into conversations, you know, those are gonna be I can have multiple conversations ongoing at once. Those would be like the different threads. Inside that we've got like a user that created it.\u003C\u002Fp>\u003Cp>Maybe a visitor ID in this case, if the visitor is not known. We've got some messages inside there. Inside the messages, each message has a bit of text, who is the user or the visitor ID that created it, and away we go. That's what it looks like. We're gonna dive into like some of the real time features of Directus as well.\u003C\u002Fp>\u003Cp>So what have I actually got set up for you here? In starting this project, I've got a Nuxt application that I often use as a starter here for this. You know, the Nuxt application just has a direct us client using the SDK already preconfigured with, authentication and rest. I've got some login and register pages, so we can kinda simulate that. But that's kind of it.\u003C\u002Fp>\u003Cp>The other like 2 minutes worth of setup that I have done, this Agency OS project, before the V2 release, I had a janky little chat widget that I was trying to use in this specific project. So I can go back in time here and find this little chat widget. Let's just copy this in. And, it looks like I've already got it copied in, and cleaned up. But just a little chat box that we could use.\u003C\u002Fp>\u003Cp>And, I could never quite get this to work before the big release, so, it it ended up getting scrapped. So let's revisit this. I've got our chat widget. Let's drop this thing into our index page. So if we open up this incognito window so I don't spoil any of the, so I'm not using that direct to session token basically.\u003C\u002Fp>\u003Cp>Alright, so I've got my page here, I'm just going to drop this chat widget in there. And now I've just got this shell for a chat widget. It opens, it closes, it kinda looks nice. That's really all it does, right? There's this motionable component that uses the view motion View Use Motion, composables.\u003C\u002Fp>\u003Cp>Where is it? View Use Motion at view use slash motion. Just to get like some smooth animations using, I think it uses pop motion the hood. And it's just, tailwind for styling, right? Our content is gonna go in here.\u003C\u002Fp>\u003Cp>But let's fulfill that first thing, right? We wanna be able to chat with visitors in real time. I don't even know why these are bold, that's gonna aggravate me. Alright, how do we chat with visitors in real time? Directus has powerful real time functionality built in.\u003C\u002Fp>\u003Cp>Now inside my Docker Compose file for this, I've already got real time enabled. So that is the WebSockets enabled part here. And when you're working with real time and direct us, there's 3 authentication modes. There's public, handshake, and strict. Public just means anybody can open a connection.\u003C\u002Fp>\u003Cp>Handshake means that you don't have to provide the authentication as the you could still connect but the first message has to be authentication. And then strict means you have to pass the authentication before you actually initiate a connection. Alright, so what we're wanting to do here basically, we're gonna have to extend our plugin that we have. So I'm gonna go into my Directus plugin that I've got. And again, it's just creating a simple SDK client to work with.\u003C\u002Fp>\u003Cp>I'm gonna go in here and let's actually create a second client. Create Directus Real Time Client. So I'm just gonna create a new Directus client and I'll just copy paste this previous client. Great. Except with, we're not gonna use any of this, we're gonna use the real time composable.\u003C\u002Fp>\u003Cp>Alright? And there is an auth mode property in this case and I'm just gonna pass public because we want anybody to be able to connect to this. So I'm gonna create that client and then the next thing that I'm gonna do is provide that client to you, the Nuxt application. Let's just call it directus sws for directus web socket. And we're gonna pass directus ws.\u003C\u002Fp>\u003Cp>Okay. So now we have created this client. Our chat widget still doesn't do anything. That's fine. What we're gonna do is open up chat widget and we're going to access that client.\u003C\u002Fp>\u003Cp>So we'll do directus ws, because Nuxt provide when you use Nuxt provide it adds a dollar sign to the front of it. And we're just going to do use Nuxt app composable. That will give us access to our web socket client. And then we're going to actually open a connection, right. And this application is using like server side rendering inside Nuxt.\u003C\u002Fp>\u003Cp>So I probably want to like wrap this up inside a composable as well like the like an on mounted hook. So we'll await the Directus WebSocket connect. And I I don't work with Directus real time a lot, so we may get some funky behavior here that needs to be async. Okay. So I'm just gonna open up the console and see if I open up all and I go to web socket, we can show a connection here.\u003C\u002Fp>\u003Cp>It says switching protocols. Okay, AWS local host. Alright, so looks like we've got the WebSocket connection opened. Alright, now let's just kind of dig into the documentation a little bit. Because I the next thing is gonna be, like, subscribing, but we probably need some message handlers.\u003C\u002Fp>\u003Cp>So we'll go into get started with real time on the documentation. I am doing this, just as you might. So you can see that we've got the real time client here. We've opened a connection to the client, or at least I think we have. And then now we've got like this client.\u003C\u002Fp>\u003Cp>We need to listen for changes. Right? So I might as well just plop this in our on mounted hook. And I'm just gonna replace client dot with dollar sign directus ws. And now let's see what we actually get out of this.\u003C\u002Fp>\u003Cp>Do we see any actual messages here? I don't know why am I not seeing any messages. Directus cannot connect when state is open. Do we need to do we actually need to pass the auth mode to this? Is that an actual property?\u003C\u002Fp>\u003Cp>Switching protocols, directus.ws.subscribe. Let's subscribe to messages and see what happens. On message data dot undefined. Message. What if we just console log the message here?\u003C\u002Fp>\u003Cp>Console log message. Alright. Alright. So we see we're not getting anything there. Subscribe error, invalid collection.\u003C\u002Fp>\u003Cp>Okay. So not getting the correct collection. Let's just take a look at this. This is messages. Oh, let's try subscribing to messages, right?\u003C\u002Fp>\u003Cp>On open console log event open. I don't know why it is not not sure why it's not working correctly. You do not have permission to access this. Right? So as a public user, we don't have access to the individual messages.\u003C\u002Fp>\u003Cp>Alright, so two ways around this. We could potentially provide some authentication, if I can actually talk, right, or we could give access to this. Alright. So if I go into my access control settings, I'm gonna go to public in this case, and I'm not gonna give all access, but maybe we use custom. Right?\u003C\u002Fp>\u003Cp>2 things here, we get the public can create messages, that's fine. But when it comes to reading messages, we don't want people to be able to read other people's conversations. So in this case, let's add just a rule to it. If the conversation, conversation visitor ID contains what? Can we even do that, right?\u003C\u002Fp>\u003Cp>We'd have to pass the let's do this instead. We'll use our website API user for this. And okay. We'll do messages. You can see conversations.\u003C\u002Fp>\u003Cp>I tell you what, let's just open the floodgates here just to test this out and make sure we can see these things. Alright, so we've got a subscription. There's our data, okay. And if I open this up and I add a new message, right, which is the test message, save this, we should see that data come through, which we do over here. Right?\u003C\u002Fp>\u003Cp>So we see our subscription for the messages. Do we see our actual data here? Great. There's our text, test message. Here's the user that created that.\u003C\u002Fp>\u003Cp>That's gonna be me, the admin user. And we've got the date created. Right? We've got all the details of this specific message. Alright.\u003C\u002Fp>\u003Cp>So at least now we are getting the messages. Alright, if we go back to the documentation, we're getting some messages here. That's great. You know, a couple things when we create a subscription, right? We can choose what event to subscribe to.\u003C\u002Fp>\u003Cp>So the event wise, we'll probably do like create. And we can even add a query, right. So imagine we've got a website visitor. We want to basically, like, create a unique session or a unique ID for that visitor, so that that user can only see their messages. Right?\u003C\u002Fp>\u003Cp>So what do we need to do there? This might be something like a Nuxt Middleware. Do I have do I have UUIDs in this? Okay. So I've just got like a function in here and and this is, like a lot of this is actually ripped from the AgisUS project to begin with.\u003C\u002Fp>\u003Cp>But I've just got a function here for generating an ID, which basically just creates like a UUIDv4. Alright, so what I'm gonna do here, let's just create like a session dot global dot ts. I'm gonna add this in the middleware folder inside Nuxt. And when I do this, if I use dot global that will run on every route. So I'm just gonna go here, I'm gonna copy this, and I don't really need to here.\u003C\u002Fp>\u003Cp>Basically, anytime we navigate to a route, we wanna do some things. Right. We want to, let's check like a session ID or visitor ID. Visitor ID equals use cookie so we can set a cookie that way we can access it server and client side. We got visitor ID.\u003C\u002Fp>\u003Cp>If visitor ID dot value, if there's no visitor ID dot value, we are going to set up a where is it? I'm gonna do Visitor.id.value equals generate ID. There. Okay. Alright so now basically we just created a middleware that's gonna run globally on every route within this application and it will create a cookie for the visitor ID.\u003C\u002Fp>\u003Cp>Okay. And now I can see that here that I've got a visitor ID cookie which is some random UUID value. If I delete that, refresh, we can see we get a new visitor ID. But that cookie will persist across sessions, which is nice, and we don't have to do anything else there. Okay, so as far as our query, if we just go back to the documentation, right, we can add a query, and we can do all of the same query params that we would in the regular Directus API.\u003C\u002Fp>\u003Cp>So we do something like filter where, well actually let's back up a minute, we need to get that cookie, right? So visitor ID is equal to use cookie visitor ID. Okay. And then for our filter, we want the messages. Let's think about this, right?\u003C\u002Fp>\u003Cp>We're gonna have to create a conversation. Let's just get some messages first, right? So we're gonna add the visitor ID to each message. Alright. So maybe we just wanna be able to see we're gonna have to like create a conversation.\u003C\u002Fp>\u003Cp>There's no way getting around that. Alright. How can we create items, right? So we can use that same connection and then we can actually get all of our like CRUD operations. So let's just do this.\u003C\u002Fp>\u003Cp>We're gonna actually set this up. I wanna subscribe to 2 items, right? Conversations. And in this case, we want to filter where the visitor ID is visitor.id.value. So inside our data model, right, when we create a conversation there's a visitor ID for it so we could track that.\u003C\u002Fp>\u003Cp>Great. Okay. So I could see that there. Then we have the individual messages, and we we've honestly got a lot of extra stuff that we're probably not even really gonna need here. But we got visitor.\u003C\u002Fp>\u003Cp>Id_id, that's what it is inside our database. And then we've got the cookie value here. So visitor dot ID, and then the param is gonna be like this, equals to visor.id.value. And then I think we can also pass a UID to this subscription. Let's look at it.\u003C\u002Fp>\u003Cp>Learn more about authentication, learn more about subscriptions with WebSockets. Alright. Using UID's type is subscribe multiple subscription UID. Okay. So we'll call this UID And this will be visitor conversations.\u003C\u002Fp>\u003Cp>Thank you, GitHub Copilot. Let's also open a connection for messages as well. Subscribe messages where messages dot create, query, UID. And in this case we can't just filter out for the visitor messages, right? We also have to be able to show the admin messages as well.\u003C\u002Fp>\u003Cp>So in this case what we're gonna do, we're gonna work on the conversations because we can use the related fields inside direct us collection. So I can go up a level here. It's actually conversation. Is what's the parameter gonna be? Right?\u003C\u002Fp>\u003Cp>So we could have potentially multiple conversations. Right? So we'll have conversations equals ref, and then we'll have messages. Actually, we might just store all of those. Right?\u003C\u002Fp>\u003Cp>Conversation, let's look for our query params, global query parameters for our actual filter here, filter rules, we're looking for items within an array. So we're going to use n, in conversations dot value map conversation dot ID. Okay. I think this should give us what we want. And if I open this now we can see that we've got a subscription for conversations, we've got a subscription for messages.\u003C\u002Fp>\u003Cp>I'm gonna just copy the visitor ID here. And the other thing I'm gonna do, I'm gonna create a new conversation. So we'll say new conversation. I'm gonna add that visitor ID here. We'll call this test convo.\u003C\u002Fp>\u003Cp>And do we get a message for that? We can see this conversation here. And now let's just test this as well. Will we get, TestConvo not from visitor? Just want to make sure.\u003C\u002Fp>\u003Cp>Okay. So you can see I created a new conversation that didn't have that user ID or that visitor ID. And now we don't actually see that. So how are we doing on time? We got about 35 minutes left.\u003C\u002Fp>\u003Cp>We're looking okay. So let's start having some conversations. Right? On messages, on init, Subscription init. Get conversations.\u003C\u002Fp>\u003Cp>Alright, subscribe on open on message. Okay, so basically all we're doing at this point is listening for messages. And just logging that out, right? So we need to do some other things here. Is there like a is there another property on a knit?\u003C\u002Fp>\u003Cp>No, a knit. I need more practice with real time, obviously. Alright. So when we receive the message, let's look and see what our references are here. Full code sample, I think there's actually a guide that I built on multi user chat or somebody built this.\u003C\u002Fp>\u003Cp>Alright. Connect cleanup, Subscribe. Okay. So you can get the actual subscription equals await client dot subscribe. Constant subscription equals ws await client.subscribe.\u003C\u002Fp>\u003Cp>And then we can get our initial messages. So here, let's just call this get initial combos. And we might also want to grab all the fields within that as well. So we're gonna get all the root level fields for the conversation, and then we're also gonna get all the message values included in that. And then let's populate conversations.value=subscription.data.\u003C\u002Fp>\u003Cp>I'm not even sure it's gonna be that. We'll just do it's actually console log subscription. See what we get there. Alright, subscription L2 generator. Cons to subscription.\u003C\u002Fp>\u003Cp>Subscription, unsubscribe, generator state suspended. For message of subscription, for await combo of subscription push combo. Subscription is not iterable. Let's just wrap it like in the documentation here and see what we actually get. Right?\u003C\u002Fp>\u003Cp>So now if I show let's just show the actual conversations. So we'll go into our content section, and we'll do divv4conversationinconversation. Flex call. Let's just add let's just log that out in conversation. Prepping the pretag.\u003C\u002Fp>\u003Cp>Type subscription event, init. Yeah. So this might need to actually be updated. Right? Subscription event, await client subscribe.\u003C\u002Fp>\u003Cp>Subscription Subscribe to messages, create event. What am I missing here? Subscribe if type. This message is create, receive message. Subscription started.\u003C\u002Fp>\u003Cp>Okay. We're not actually getting the message history there. Display historical messages. Okay. Alright.\u003C\u002Fp>\u003Cp>So basically to get all of the historical messages, we need to read the messages. So we'll do client dot, it's actually gonna be directus dot send message, and then we'll get all of the items within the conversations. So that'll just be items. The collection will be conversations. And the action is gonna be read here.\u003C\u002Fp>\u003Cp>So we just wanna read all of the conversations. And then as far as the query, we'll set a filter where visitor ID equals visitor ID and we'll just get all of those conversations. And maybe we limit those to like the past 10 conversations, kinda like our documentation here. Alright. Send message.\u003C\u002Fp>\u003Cp>Okay. So we're gonna get that message. Send message. There's our items that we could see. There's those conversations there.\u003C\u002Fp>\u003Cp>We could see the individual messages within those, but we're not actually populating that data somewhere. Right? So we're going to need on our message function here, basically if the data type equals items, okay, so when we receive a message, we are going to take our message. Alright. If the message dot type conversations there we go.\u003C\u002Fp>\u003Cp>GitHub Copilot knows what we want. And we'll just refresh when we open the connection. If I go in and we look for chat widget inside the dev tools, do we see our conversations? We don't. Alright.\u003C\u002Fp>\u003Cp>Why not? Message. Message, message. If let's just update this as well. Is connected.\u003C\u002Fp>\u003Cp>State is not open. Okay. So we're getting our messages. They're just not populating to what we want. Right?\u003C\u002Fp>\u003Cp>So we see the data is an array. That should be what we want though. If we open this up, we should be seeing those conversations. Direct to send messages, items, conversations, if equals conversations, conversations dot value equals messages dot data. Conversations.\u003C\u002Fp>\u003Cp>Yeah. That should be right. Collection equals conversations message dot type. What is the the type equals items? Oh, we don't really have a, maybe we wanna add a UID.\u003C\u002Fp>\u003Cp>Type equal to UID equals get combos. Alright. So now if we take a look at that, right, we should have that UID get combos. And we could just target it that way. If message dotuid equals get combos, and now we populate that data.\u003C\u002Fp>\u003Cp>Okay. Alright. So now we can see a conversation there. That was a lot of stress just to get this conversation. And let's just go in, and I'm gonna add, a message to this.\u003C\u002Fp>\u003Cp>Hi. Hey. Okay. Now if I refresh, we can see the actual messages within that. That's solid.\u003C\u002Fp>\u003Cp>Okay. And then we wanna do something like this where we have selected convo equals ref. Let's just call it conversation. Okay. Solid.\u003C\u002Fp>\u003Cp>Alright. So now we need to actually like start showing our our conversations. Right? So within this, we want to maybe wrap this up. Right?\u003C\u002Fp>\u003Cp>We'll do it in like a template tag. If there is no if no selected conversation, We're gonna show this information. Then we'll come down and do another template v if selected conversation will show something else. Alright. So instead of our actual conversations here, what if we show we wanna add a button.\u003C\u002Fp>\u003Cp>Do we want to make it a button? Yeah, we do. And then we'll do like the conversation p conversation dot title, I think we've got a title for that. Alright, so we refresh, we see test convo. Let's give each one of these a lot of padding.\u003C\u002Fp>\u003Cp>Okay. So we'll do like py6px4. You know, maybe we wrap these again. Div. Do like a divide y situation.\u003C\u002Fp>\u003Cp>Okay. Test convo. Test messages dot text. And, you know, maybe we show like the user avatar as well. Alright, so again, like we might have to drill down into this a little further and do messages dot user created, user underscore created.\u003C\u002Fp>\u003Cp>And this should give us like all the actual user data in addition to that. Right? So I wanna make sure that we've got let's see if we can get, like, the user avatar source equals, use files, conversations dot messages dot user ID. Can we show the user avatar? Where does this use files to?\u003C\u002Fp>\u003Cp>File ID. Alright, let's just check the messages real quick. Chat widget, conversations, messages. Are we getting user created? We're not getting that information, so we probably need to control that with access control as well.\u003C\u002Fp>\u003Cp>Alright. Users. Again, we're just gonna open the floodgates. Would not do this on a regular app. Make sure what other rules do we have here?\u003C\u002Fp>\u003Cp>Public. Okay. And I really don't even have a I don't think the admin user has an avatar here either. So let's just add one for that. All files, just added avatar for me.\u003C\u002Fp>\u003Cp>Refresh. Are we actually getting that avatar? We're not. Anonymous component chat widgets. We'll go in.\u003C\u002Fp>\u003Cp>Here's our conversations. Here's our messages. We'll get the user created. We could see the avatar, access control public, Directus files. Let's just give all access.\u003C\u002Fp>\u003Cp>Yep. Still not showing it. Use files. Let's just call it, like, constant file. It may not even be able to use files.\u003C\u002Fp>\u003Cp>What is actually this? Get probably need to look at the actual file URL. That's the actual method here. That's why I'm not getting that correct. Alright.\u003C\u002Fp>\u003Cp>So file URL. Okay. There we go. Alright. So that's not necessarily pretty or amazing.\u003C\u002Fp>\u003Cp>Let's flex it, test convo. Maybe we'll just show like the number of messages dot length. And wrap this. And then we do, p, maybe like get I got a getRelativeTime conversation. Oops.\u003C\u002Fp>\u003Cp>Just got to wrap that. Get relative time, conversation, date created. 16 minutes ago, we'll add a little bit of gap here. And let's do text left. Okay.\u003C\u002Fp>\u003Cp>Alright, so we got the test convo and now when I click the button at click, we want to set the selected conversation to the conversation dot ID. Okay. And now we can see that goes away. And now we just need to show the messages within that, right? So selected, let's do a conversation messages.\u003C\u002Fp>\u003Cp>Yep, get up Copilot knows what I want to do. If that is has no value, we're gonna return conversation messages. Let's see. Select a conversation dot value, conversations dot ID. Okay.\u003C\u002Fp>\u003Cp>Return conversation messages. Okay. Yeah. Sometimes AI is not to be trusted. Sometimes it's okay.\u003C\u002Fp>\u003Cp>Alright, so then let's just show the actual messages, right? Do the same thing. Let's copy this down. Div. Alright.\u003C\u002Fp>\u003Cp>And then we're gonna show we'll wrap this. And then we need a div for each message. So v for message in selected conversations, key equals message ID. Let's see what this shows us. Right?\u003C\u002Fp>\u003Cp>We've got the test conversation, selected conversation messages. Alright, so I select that. We can see this. We don't have the message text. Hey, there's the message text.\u003C\u002Fp>\u003Cp>We're going to need like some padding here. Okay. Maybe we wanna wrap each message in like a let's add just a little bit of padding. We'll wrap each message in white light gray, gray 100. And give it a little bit of padding.\u003C\u002Fp>\u003Cp>P p 2, Rounded XL. Okay. So there's our messages. How are we doing on time? We got like 17 minutes.\u003C\u002Fp>\u003Cp>We are quickly running out of time, right? So now the bottom of this, we can see the messages generated from the user here. And the other thing that I wanna do here is when we send a new message, or when we receive a message, right, if this is the, so we whenever we create new messages, it needs to be we're subscribing to the messages. Alright. So here, we're gonna need to pass the messages are received.\u003C\u002Fp>\u003Cp>We wanna add those add them to the proper conversation. Alright. So what is GitHub Copilot coming up with? We got conversations dot value dot find, message dot data dot conversation. Okay.\u003C\u002Fp>\u003Cp>If conversation dot push. All right, let's just test this out and see if we're getting what we actually want here. Local host 8055. Alright. So we got our convo there.\u003C\u002Fp>\u003Cp>I could click into it and see our test message. And now, like hopefully if I go in and add another message here and just save, We should get that, but we're not seeing that message. Test combo, yo. Why are we not receiving that? Console dot log dot message, web socket messages.\u003C\u002Fp>\u003Cp>Conversation In That should be a message of the conversations. Subscribe. Is that after maybe we need to wait getting those initial messages? All right, so let's open this up. Now we can see both of those messages, right?\u003C\u002Fp>\u003Cp>I do tttt, we're still not getting that message. So let's just drop this for now. We would come back and clean this up, but I I just wanna actually get the messages. Alright. T t t.\u003C\u002Fp>\u003Cp>Test. So we're getting that visitor messages array. It's just not populating to the proper conversation. Alright. So don't trust GitHub.\u003C\u002Fp>\u003Cp>If git combo and messages what's the actual message that we received? Git convo subscription event dot create visitor messages. It's basically we need to use that UID again. UID, those visitor messages and message data. I think that should take care of it.\u003C\u002Fp>\u003Cp>Test convo. Still not getting that. Right? Chat widget. Select a conversation messages, select a conversation, conversation.\u003C\u002Fp>\u003Cp>It's just not populating those messages here. What are we doing wrong? What are we doing wrong? If conversation oh, conversation dot value? Conversations dot value dot fun.\u003C\u002Fp>\u003Cp>That's not really gonna work, is it? So we need to like we're getting the conversation value. So here's the message. Here's the data. We're getting back an array of data actually.\u003C\u002Fp>\u003Cp>Conversations dot value dot find data. Message dot data is an array. Okay. Conversation. I don't even know if this is actually gonna work, is it?\u003C\u002Fp>\u003Cp>That shouldn't work. We'll test it and see. Yeah. It's not gonna work. Promise dot avatar.\u003C\u002Fp>\u003Cp>So we need to definitely add that as well. To our query, we're gonna add the same thing or similar, where we have fields, we're gonna have user created dot dollar sign. Okay. So we got our test convo. Can we receive messages from Reading an avatar.\u003C\u002Fp>\u003Cp>Man. Okay. Conversation dot messages dot push. Why is it not seeing the avatar? Chat widget, conversations, messages within that.\u003C\u002Fp>\u003Cp>It's pushing that into the array, message.data.0. That would be why. Alright. One more time again. I'm just gonna delete some of these.\u003C\u002Fp>\u003Cp>Save it. We refresh over here. We can see there's our convo. We do yo. We save.\u003C\u002Fp>\u003Cp>Okay. So now over here on the client, I can see that conversation or that message being populated. So we'll just add a little gap between these space space y 2. Okay. And we'll probably need to do like message dot user created dot if we have a message user dot created and message dot user created dot avatar.\u003C\u002Fp>\u003Cp>Alright, so we're only gonna show that if we have it. And then the next thing that we need to do in a hurry here, about 9 minutes, is to actually start populating these messages, right? So we want to add some form fields to this down here at the bottom somewhere. So if we're inside the selected conversation, great, let's add the message form. Cool.\u003C\u002Fp>\u003Cp>Alright, and we'll do, I've got this nice Nuxt UI library here. So we'll do Submit and Message and it just gives us some nice inputs here. Flex column, there's the wrapper. Where is this actually gonna be? Inset 0, uform, send message, v model, new message.\u003C\u002Fp>\u003Cp>Alright, so let's add our new message. New message equals ref, that'll be blank. Let's have a function for send message. Okay, send messages, create conversation, select a conversation dot value, new message dot value, user created. Nope, Where did you go?\u003C\u002Fp>\u003Cp>We can rely on AI here. And we don't want a UUID or UID for this. Oh, maybe we do. Send message. And we're we're not gonna use the user created.\u003C\u002Fp>\u003Cp>Alright. Does this give us, where's my little bar at? User, User selected conversation. There's no selected conversations. Vfusermessage.created.\u003C\u002Fp>\u003Cp>Oh, message dot user created dot avatar. Okay. So now we're going to form. The form should actually be pushed to the bottom. So that'll be flex 1 height full.\u003C\u002Fp>\u003Cp>Okay. Then inside this form, I want that to flex, justify between. Okay. And we add a bit of padding to that as well, p 2. Give it a border, border t.\u003C\u002Fp>\u003Cp>And we stick it to the bottom. Nope. This will be like overflow. Why? Auto.\u003C\u002Fp>\u003Cp>Okay. Flex justify between, and this could probably even be like stuck to the bottom in the absolute. Alright. So let's just change this to like text area. Hey hey hey.\u003C\u002Fp>\u003Cp>And now we can see we can actually chat with that specific person, inside our send message, we're probably gonna delete that new message as well. Just set that value once that's actually populating. Okay. So now I can see my convos. I can open up that convo.\u003C\u002Fp>\u003Cp>I can send a message to their team. Great. How could we actually show the help center articles? Right? In 5 minutes, can we get this rich content within there?\u003C\u002Fp>\u003Cp>We're going to be pushing that, right? Pushing it quite a bit. Alright, so we got our chat widget. We're probably gonna need like a page functionality or something. Like hey, what page are we on?\u003C\u002Fp>\u003Cp>The default is, let's set that to chat. So this will be this is our conversations. And can we wrap this in another template tag? Or it might be easier just to stick it inside a div. Alright, so div if page equals chat, We're gonna show that.\u003C\u002Fp>\u003Cp>Alright, so we should still see that now when we open this up. Still see that. Great. But let's just show another page. Div v if page equals articles.\u003C\u002Fp>\u003Cp>Alright. So then we're gonna show the articles. Maybe we show like a card v 4, Article in articles. I don't think any of these are actually gonna apply. View card, we'll just do like a p tag for the article dot title And see what this shows.\u003C\u002Fp>\u003Cp>Key is equal to article dot ID. Alright. And now we need like some kind of widget at the bottom of this to actually show, right? We need like a menu. So let's do div, do you like buttons?\u003C\u002Fp>\u003Cp>This is not at all. Show an icon. Name equals herocon's chat bubble left ellipsis. Let's just call this chat. And actually, and then we need a new button for icon name equals articles.\u003C\u002Fp>\u003Cp>How are we doing on time? 3 minutes. Are we gonna actually get this or not? Let's see what this shows. Chat.\u003C\u002Fp>\u003Cp>This is gonna be absolute bottom 0, p 4flexgap4, give that a little gap. I don't particularly like the icons for this. Let's just okay. And then if click at click, this is gonna be page equals articles. There we go.\u003C\u002Fp>\u003Cp>And at click page is equal to chat. Okay. Is that actually gonna work? Page is equal to chat. Page is equal to articles.\u003C\u002Fp>\u003Cp>Okay. So now we're switching those back and forth. We just don't have any articles, right? So let's use our client here. We've got articles.\u003C\u002Fp>\u003Cp>We could do a ref. Just make that an array. And then we'll have like a function, get articles. And we can use that same connection again. Right?\u003C\u002Fp>\u003Cp>Fetch articles. Yeah. I could use the regular rest connection as well in this case. But let's try to use this. Send message.\u003C\u002Fp>\u003Cp>We want collection of articles. This is gonna be a read. And the data, UID, we'll just do get articles. Okay. Oh, how we doing?\u003C\u002Fp>\u003Cp>We got 1 minute 20 seconds. Get articles. That's our function. Okay. And then on our receive bit here, if message dot UID and message data articles dot value dot messages.\u003C\u002Fp>\u003Cp>Okay. What else do we what else do we need to do here? We need to fetch those articles first, right? So let's do like a watcher. Watch page.\u003C\u002Fp>\u003Cp>New page. If page equals articles, get articles. Is this actually gonna work? Error. Invalid collection.\u003C\u002Fp>\u003Cp>Oh, no. We don't have articles. They're what? Post. Post.\u003C\u002Fp>\u003Cp>Post. So what is this? There we go. With what time to spare? 21 seconds.\u003C\u002Fp>\u003Cp>Right? Okay. So we can actually see a list of the articles. That's about as good as we're going to get in in this amount of time left. I'm calling that a win.\u003C\u002Fp>\u003Cp>So we can actually show the list of articles here. The next part would be to just probably actually, like, set these up to to, like, be able to read the rich text for those articles right within the little widget. But, you know, that is a wrap. Great. And if I open up Directus, we can see all the messages within that conversation.\u003C\u002Fp>\u003Cp>And honestly, I would probably build some kind of interface over here inside Directus to be able to manage this. Alright. So if we just wrap this one up, did we actually get to the intercom messenger? You know what? In an hour, almost impossible to rebuild.\u003C\u002Fp>\u003Cp>I'm I'm calling that now unless you built it before. But, yeah, far cry. But hey, the basics are there. And it goes to show you just how, how far along you can get with Directus in an hour using the tools that are provided. So that's it for this episode of 100 Apps, 100 Hours.\u003C\u002Fp>\u003Cp>Hope you enjoyed following along. You could probably see the sweat bubble up on my face at this point, but stay tuned for the next one. We'll see you.\u003C\u002Fp>","Alright. Welcome back to yet another episode of 100 Apps, 100 Hours. I'm your host, Brian Gillespie, developer advocate here at Directus. And if you're new to the show, the basic gist is this, we take 60 minutes and we try to build or rebuild some of your favorite apps or some of your wildest app ideas, in that 60 minutes, we achieve our stated goals or we publicly fail trying. And by we, I mean me. That's okay though. When I fail, I fail spectacularly, usually. But I've got 3 little girls so I'm used to being hobbled. That's okay. There are just two rules. Number 1, you have 60 minutes to plan and build, which does pose some challenges. There's no more, no less. And number 2 is you use whatever you have at your disposal, just like in the real world. You know, make it work. Figure it out. And in today's episode, we are going to take advantage of rule number 2. So what are we building today? We are working on the intercom Messenger. Right? Intercom is a, like, help desk, onboarding tool, messaging tool. They've got all these things that are wrapped up into it. It looks like their latest positioning is, like, customer service, which is great. But like most of their it comes from this little messenger widget over here. Right? So it's not just live chat. It is kind of like a rich format where they can check product news, they can chat with the support team, there's bots, there's AI, there are articles integrated, you know, there's, like, product tours and stuff like that. The the main things that we're concerned with here are this kind of nice interface for this, and then number 2, like this actual, like, live chat functionality and maybe the ability to add articles. So we keep the user in context, That's what we're gonna be tackling today. I I think it's doable, not a 100% sure. Let's go in and start the clock on this. We will start our timer, 60 minutes and away we go. Alright. So as far as what type of functionality we want out of this, you know, what are the the jobs to be done or what are the goals for this specific project. We want to be able to chat chat with visitors in real time and then, you know, maybe display rich content inside the messenger. This could take the form of articles, help center stuff, updates, etcetera, and I totally cannot spell. How are we gonna get there though? Right? Normally, if you've watched any of the past episodes, at this point, I'm like doing some data modeling. The starting point for today is a little different in that I've already got a project that we're going to lean on. It's called Agency OS and it already has a a fully fleshed out data model. You can explore the front end of this at agencyos.dev. But this actual project you can download and test out yourself. It lives in our Directus Labs organization at, Directus dash labs slash agency dash os on GitHub. Basically, there's some instructions, but the main features of this include a website, CRM project tracker type of thing, and then you have a client portal. So on the front end, it kinda looks like this, your standard agency website. You know, we've got all these different blocks. It uses the page builder inside Directus. We got blog posts and articles. You know, there's a a help center that should be displaying some articles that doesn't. And then you have, this client portal where clients can log in, keep track of their different projects, see files, see their invoices, pay for those specific invoices. Right? This is this is pretty neat functionality. And then, you know, you would basically manage your entire workflow from your deals all the way through, like proposals, projects, and tasks inside your direct assistance. Right? So I can see a list of all the tasks that are necessary to complete this specific project, who's the organization, who are our contacts for that organization, And I can manage this whole thing here. But in this case, I'm using this because we've already got this conversations model and this messages collection. And we have things like help center collections and help center articles already mapped out, which is is really nice, just to kind of piggyback off of what I've already built in the past. So if we dive into conversations, you know, those are gonna be I can have multiple conversations ongoing at once. Those would be like the different threads. Inside that we've got like a user that created it. Maybe a visitor ID in this case, if the visitor is not known. We've got some messages inside there. Inside the messages, each message has a bit of text, who is the user or the visitor ID that created it, and away we go. That's what it looks like. We're gonna dive into like some of the real time features of Directus as well. So what have I actually got set up for you here? In starting this project, I've got a Nuxt application that I often use as a starter here for this. You know, the Nuxt application just has a direct us client using the SDK already preconfigured with, authentication and rest. I've got some login and register pages, so we can kinda simulate that. But that's kind of it. The other like 2 minutes worth of setup that I have done, this Agency OS project, before the V2 release, I had a janky little chat widget that I was trying to use in this specific project. So I can go back in time here and find this little chat widget. Let's just copy this in. And, it looks like I've already got it copied in, and cleaned up. But just a little chat box that we could use. And, I could never quite get this to work before the big release, so, it it ended up getting scrapped. So let's revisit this. I've got our chat widget. Let's drop this thing into our index page. So if we open up this incognito window so I don't spoil any of the, so I'm not using that direct to session token basically. Alright, so I've got my page here, I'm just going to drop this chat widget in there. And now I've just got this shell for a chat widget. It opens, it closes, it kinda looks nice. That's really all it does, right? There's this motionable component that uses the view motion View Use Motion, composables. Where is it? View Use Motion at view use slash motion. Just to get like some smooth animations using, I think it uses pop motion the hood. And it's just, tailwind for styling, right? Our content is gonna go in here. But let's fulfill that first thing, right? We wanna be able to chat with visitors in real time. I don't even know why these are bold, that's gonna aggravate me. Alright, how do we chat with visitors in real time? Directus has powerful real time functionality built in. Now inside my Docker Compose file for this, I've already got real time enabled. So that is the WebSockets enabled part here. And when you're working with real time and direct us, there's 3 authentication modes. There's public, handshake, and strict. Public just means anybody can open a connection. Handshake means that you don't have to provide the authentication as the you could still connect but the first message has to be authentication. And then strict means you have to pass the authentication before you actually initiate a connection. Alright, so what we're wanting to do here basically, we're gonna have to extend our plugin that we have. So I'm gonna go into my Directus plugin that I've got. And again, it's just creating a simple SDK client to work with. I'm gonna go in here and let's actually create a second client. Create Directus Real Time Client. So I'm just gonna create a new Directus client and I'll just copy paste this previous client. Great. Except with, we're not gonna use any of this, we're gonna use the real time composable. Alright? And there is an auth mode property in this case and I'm just gonna pass public because we want anybody to be able to connect to this. So I'm gonna create that client and then the next thing that I'm gonna do is provide that client to you, the Nuxt application. Let's just call it directus sws for directus web socket. And we're gonna pass directus ws. Okay. So now we have created this client. Our chat widget still doesn't do anything. That's fine. What we're gonna do is open up chat widget and we're going to access that client. So we'll do directus ws, because Nuxt provide when you use Nuxt provide it adds a dollar sign to the front of it. And we're just going to do use Nuxt app composable. That will give us access to our web socket client. And then we're going to actually open a connection, right. And this application is using like server side rendering inside Nuxt. So I probably want to like wrap this up inside a composable as well like the like an on mounted hook. So we'll await the Directus WebSocket connect. And I I don't work with Directus real time a lot, so we may get some funky behavior here that needs to be async. Okay. So I'm just gonna open up the console and see if I open up all and I go to web socket, we can show a connection here. It says switching protocols. Okay, AWS local host. Alright, so looks like we've got the WebSocket connection opened. Alright, now let's just kind of dig into the documentation a little bit. Because I the next thing is gonna be, like, subscribing, but we probably need some message handlers. So we'll go into get started with real time on the documentation. I am doing this, just as you might. So you can see that we've got the real time client here. We've opened a connection to the client, or at least I think we have. And then now we've got like this client. We need to listen for changes. Right? So I might as well just plop this in our on mounted hook. And I'm just gonna replace client dot with dollar sign directus ws. And now let's see what we actually get out of this. Do we see any actual messages here? I don't know why am I not seeing any messages. Directus cannot connect when state is open. Do we need to do we actually need to pass the auth mode to this? Is that an actual property? Switching protocols, directus.ws.subscribe. Let's subscribe to messages and see what happens. On message data dot undefined. Message. What if we just console log the message here? Console log message. Alright. Alright. So we see we're not getting anything there. Subscribe error, invalid collection. Okay. So not getting the correct collection. Let's just take a look at this. This is messages. Oh, let's try subscribing to messages, right? On open console log event open. I don't know why it is not not sure why it's not working correctly. You do not have permission to access this. Right? So as a public user, we don't have access to the individual messages. Alright, so two ways around this. We could potentially provide some authentication, if I can actually talk, right, or we could give access to this. Alright. So if I go into my access control settings, I'm gonna go to public in this case, and I'm not gonna give all access, but maybe we use custom. Right? 2 things here, we get the public can create messages, that's fine. But when it comes to reading messages, we don't want people to be able to read other people's conversations. So in this case, let's add just a rule to it. If the conversation, conversation visitor ID contains what? Can we even do that, right? We'd have to pass the let's do this instead. We'll use our website API user for this. And okay. We'll do messages. You can see conversations. I tell you what, let's just open the floodgates here just to test this out and make sure we can see these things. Alright, so we've got a subscription. There's our data, okay. And if I open this up and I add a new message, right, which is the test message, save this, we should see that data come through, which we do over here. Right? So we see our subscription for the messages. Do we see our actual data here? Great. There's our text, test message. Here's the user that created that. That's gonna be me, the admin user. And we've got the date created. Right? We've got all the details of this specific message. Alright. So at least now we are getting the messages. Alright, if we go back to the documentation, we're getting some messages here. That's great. You know, a couple things when we create a subscription, right? We can choose what event to subscribe to. So the event wise, we'll probably do like create. And we can even add a query, right. So imagine we've got a website visitor. We want to basically, like, create a unique session or a unique ID for that visitor, so that that user can only see their messages. Right? So what do we need to do there? This might be something like a Nuxt Middleware. Do I have do I have UUIDs in this? Okay. So I've just got like a function in here and and this is, like a lot of this is actually ripped from the AgisUS project to begin with. But I've just got a function here for generating an ID, which basically just creates like a UUIDv4. Alright, so what I'm gonna do here, let's just create like a session dot global dot ts. I'm gonna add this in the middleware folder inside Nuxt. And when I do this, if I use dot global that will run on every route. So I'm just gonna go here, I'm gonna copy this, and I don't really need to here. Basically, anytime we navigate to a route, we wanna do some things. Right. We want to, let's check like a session ID or visitor ID. Visitor ID equals use cookie so we can set a cookie that way we can access it server and client side. We got visitor ID. If visitor ID dot value, if there's no visitor ID dot value, we are going to set up a where is it? I'm gonna do Visitor.id.value equals generate ID. There. Okay. Alright so now basically we just created a middleware that's gonna run globally on every route within this application and it will create a cookie for the visitor ID. Okay. And now I can see that here that I've got a visitor ID cookie which is some random UUID value. If I delete that, refresh, we can see we get a new visitor ID. But that cookie will persist across sessions, which is nice, and we don't have to do anything else there. Okay, so as far as our query, if we just go back to the documentation, right, we can add a query, and we can do all of the same query params that we would in the regular Directus API. So we do something like filter where, well actually let's back up a minute, we need to get that cookie, right? So visitor ID is equal to use cookie visitor ID. Okay. And then for our filter, we want the messages. Let's think about this, right? We're gonna have to create a conversation. Let's just get some messages first, right? So we're gonna add the visitor ID to each message. Alright. So maybe we just wanna be able to see we're gonna have to like create a conversation. There's no way getting around that. Alright. How can we create items, right? So we can use that same connection and then we can actually get all of our like CRUD operations. So let's just do this. We're gonna actually set this up. I wanna subscribe to 2 items, right? Conversations. And in this case, we want to filter where the visitor ID is visitor.id.value. So inside our data model, right, when we create a conversation there's a visitor ID for it so we could track that. Great. Okay. So I could see that there. Then we have the individual messages, and we we've honestly got a lot of extra stuff that we're probably not even really gonna need here. But we got visitor. Id_id, that's what it is inside our database. And then we've got the cookie value here. So visitor dot ID, and then the param is gonna be like this, equals to visor.id.value. And then I think we can also pass a UID to this subscription. Let's look at it. Learn more about authentication, learn more about subscriptions with WebSockets. Alright. Using UID's type is subscribe multiple subscription UID. Okay. So we'll call this UID And this will be visitor conversations. Thank you, GitHub Copilot. Let's also open a connection for messages as well. Subscribe messages where messages dot create, query, UID. And in this case we can't just filter out for the visitor messages, right? We also have to be able to show the admin messages as well. So in this case what we're gonna do, we're gonna work on the conversations because we can use the related fields inside direct us collection. So I can go up a level here. It's actually conversation. Is what's the parameter gonna be? Right? So we could have potentially multiple conversations. Right? So we'll have conversations equals ref, and then we'll have messages. Actually, we might just store all of those. Right? Conversation, let's look for our query params, global query parameters for our actual filter here, filter rules, we're looking for items within an array. So we're going to use n, in conversations dot value map conversation dot ID. Okay. I think this should give us what we want. And if I open this now we can see that we've got a subscription for conversations, we've got a subscription for messages. I'm gonna just copy the visitor ID here. And the other thing I'm gonna do, I'm gonna create a new conversation. So we'll say new conversation. I'm gonna add that visitor ID here. We'll call this test convo. And do we get a message for that? We can see this conversation here. And now let's just test this as well. Will we get, TestConvo not from visitor? Just want to make sure. Okay. So you can see I created a new conversation that didn't have that user ID or that visitor ID. And now we don't actually see that. So how are we doing on time? We got about 35 minutes left. We're looking okay. So let's start having some conversations. Right? On messages, on init, Subscription init. Get conversations. Alright, subscribe on open on message. Okay, so basically all we're doing at this point is listening for messages. And just logging that out, right? So we need to do some other things here. Is there like a is there another property on a knit? No, a knit. I need more practice with real time, obviously. Alright. So when we receive the message, let's look and see what our references are here. Full code sample, I think there's actually a guide that I built on multi user chat or somebody built this. Alright. Connect cleanup, Subscribe. Okay. So you can get the actual subscription equals await client dot subscribe. Constant subscription equals ws await client.subscribe. And then we can get our initial messages. So here, let's just call this get initial combos. And we might also want to grab all the fields within that as well. So we're gonna get all the root level fields for the conversation, and then we're also gonna get all the message values included in that. And then let's populate conversations.value=subscription.data. I'm not even sure it's gonna be that. We'll just do it's actually console log subscription. See what we get there. Alright, subscription L2 generator. Cons to subscription. Subscription, unsubscribe, generator state suspended. For message of subscription, for await combo of subscription push combo. Subscription is not iterable. Let's just wrap it like in the documentation here and see what we actually get. Right? So now if I show let's just show the actual conversations. So we'll go into our content section, and we'll do divv4conversationinconversation. Flex call. Let's just add let's just log that out in conversation. Prepping the pretag. Type subscription event, init. Yeah. So this might need to actually be updated. Right? Subscription event, await client subscribe. Subscription Subscribe to messages, create event. What am I missing here? Subscribe if type. This message is create, receive message. Subscription started. Okay. We're not actually getting the message history there. Display historical messages. Okay. Alright. So basically to get all of the historical messages, we need to read the messages. So we'll do client dot, it's actually gonna be directus dot send message, and then we'll get all of the items within the conversations. So that'll just be items. The collection will be conversations. And the action is gonna be read here. So we just wanna read all of the conversations. And then as far as the query, we'll set a filter where visitor ID equals visitor ID and we'll just get all of those conversations. And maybe we limit those to like the past 10 conversations, kinda like our documentation here. Alright. Send message. Okay. So we're gonna get that message. Send message. There's our items that we could see. There's those conversations there. We could see the individual messages within those, but we're not actually populating that data somewhere. Right? So we're going to need on our message function here, basically if the data type equals items, okay, so when we receive a message, we are going to take our message. Alright. If the message dot type conversations there we go. GitHub Copilot knows what we want. And we'll just refresh when we open the connection. If I go in and we look for chat widget inside the dev tools, do we see our conversations? We don't. Alright. Why not? Message. Message, message. If let's just update this as well. Is connected. State is not open. Okay. So we're getting our messages. They're just not populating to what we want. Right? So we see the data is an array. That should be what we want though. If we open this up, we should be seeing those conversations. Direct to send messages, items, conversations, if equals conversations, conversations dot value equals messages dot data. Conversations. Yeah. That should be right. Collection equals conversations message dot type. What is the the type equals items? Oh, we don't really have a, maybe we wanna add a UID. Type equal to UID equals get combos. Alright. So now if we take a look at that, right, we should have that UID get combos. And we could just target it that way. If message dotuid equals get combos, and now we populate that data. Okay. Alright. So now we can see a conversation there. That was a lot of stress just to get this conversation. And let's just go in, and I'm gonna add, a message to this. Hi. Hey. Okay. Now if I refresh, we can see the actual messages within that. That's solid. Okay. And then we wanna do something like this where we have selected convo equals ref. Let's just call it conversation. Okay. Solid. Alright. So now we need to actually like start showing our our conversations. Right? So within this, we want to maybe wrap this up. Right? We'll do it in like a template tag. If there is no if no selected conversation, We're gonna show this information. Then we'll come down and do another template v if selected conversation will show something else. Alright. So instead of our actual conversations here, what if we show we wanna add a button. Do we want to make it a button? Yeah, we do. And then we'll do like the conversation p conversation dot title, I think we've got a title for that. Alright, so we refresh, we see test convo. Let's give each one of these a lot of padding. Okay. So we'll do like py6px4. You know, maybe we wrap these again. Div. Do like a divide y situation. Okay. Test convo. Test messages dot text. And, you know, maybe we show like the user avatar as well. Alright, so again, like we might have to drill down into this a little further and do messages dot user created, user underscore created. And this should give us like all the actual user data in addition to that. Right? So I wanna make sure that we've got let's see if we can get, like, the user avatar source equals, use files, conversations dot messages dot user ID. Can we show the user avatar? Where does this use files to? File ID. Alright, let's just check the messages real quick. Chat widget, conversations, messages. Are we getting user created? We're not getting that information, so we probably need to control that with access control as well. Alright. Users. Again, we're just gonna open the floodgates. Would not do this on a regular app. Make sure what other rules do we have here? Public. Okay. And I really don't even have a I don't think the admin user has an avatar here either. So let's just add one for that. All files, just added avatar for me. Refresh. Are we actually getting that avatar? We're not. Anonymous component chat widgets. We'll go in. Here's our conversations. Here's our messages. We'll get the user created. We could see the avatar, access control public, Directus files. Let's just give all access. Yep. Still not showing it. Use files. Let's just call it, like, constant file. It may not even be able to use files. What is actually this? Get probably need to look at the actual file URL. That's the actual method here. That's why I'm not getting that correct. Alright. So file URL. Okay. There we go. Alright. So that's not necessarily pretty or amazing. Let's flex it, test convo. Maybe we'll just show like the number of messages dot length. And wrap this. And then we do, p, maybe like get I got a getRelativeTime conversation. Oops. Just got to wrap that. Get relative time, conversation, date created. 16 minutes ago, we'll add a little bit of gap here. And let's do text left. Okay. Alright, so we got the test convo and now when I click the button at click, we want to set the selected conversation to the conversation dot ID. Okay. And now we can see that goes away. And now we just need to show the messages within that, right? So selected, let's do a conversation messages. Yep, get up Copilot knows what I want to do. If that is has no value, we're gonna return conversation messages. Let's see. Select a conversation dot value, conversations dot ID. Okay. Return conversation messages. Okay. Yeah. Sometimes AI is not to be trusted. Sometimes it's okay. Alright, so then let's just show the actual messages, right? Do the same thing. Let's copy this down. Div. Alright. And then we're gonna show we'll wrap this. And then we need a div for each message. So v for message in selected conversations, key equals message ID. Let's see what this shows us. Right? We've got the test conversation, selected conversation messages. Alright, so I select that. We can see this. We don't have the message text. Hey, there's the message text. We're going to need like some padding here. Okay. Maybe we wanna wrap each message in like a let's add just a little bit of padding. We'll wrap each message in white light gray, gray 100. And give it a little bit of padding. P p 2, Rounded XL. Okay. So there's our messages. How are we doing on time? We got like 17 minutes. We are quickly running out of time, right? So now the bottom of this, we can see the messages generated from the user here. And the other thing that I wanna do here is when we send a new message, or when we receive a message, right, if this is the, so we whenever we create new messages, it needs to be we're subscribing to the messages. Alright. So here, we're gonna need to pass the messages are received. We wanna add those add them to the proper conversation. Alright. So what is GitHub Copilot coming up with? We got conversations dot value dot find, message dot data dot conversation. Okay. If conversation dot push. All right, let's just test this out and see if we're getting what we actually want here. Local host 8055. Alright. So we got our convo there. I could click into it and see our test message. And now, like hopefully if I go in and add another message here and just save, We should get that, but we're not seeing that message. Test combo, yo. Why are we not receiving that? Console dot log dot message, web socket messages. Conversation In That should be a message of the conversations. Subscribe. Is that after maybe we need to wait getting those initial messages? All right, so let's open this up. Now we can see both of those messages, right? I do tttt, we're still not getting that message. So let's just drop this for now. We would come back and clean this up, but I I just wanna actually get the messages. Alright. T t t. Test. So we're getting that visitor messages array. It's just not populating to the proper conversation. Alright. So don't trust GitHub. If git combo and messages what's the actual message that we received? Git convo subscription event dot create visitor messages. It's basically we need to use that UID again. UID, those visitor messages and message data. I think that should take care of it. Test convo. Still not getting that. Right? Chat widget. Select a conversation messages, select a conversation, conversation. It's just not populating those messages here. What are we doing wrong? What are we doing wrong? If conversation oh, conversation dot value? Conversations dot value dot fun. That's not really gonna work, is it? So we need to like we're getting the conversation value. So here's the message. Here's the data. We're getting back an array of data actually. Conversations dot value dot find data. Message dot data is an array. Okay. Conversation. I don't even know if this is actually gonna work, is it? That shouldn't work. We'll test it and see. Yeah. It's not gonna work. Promise dot avatar. So we need to definitely add that as well. To our query, we're gonna add the same thing or similar, where we have fields, we're gonna have user created dot dollar sign. Okay. So we got our test convo. Can we receive messages from Reading an avatar. Man. Okay. Conversation dot messages dot push. Why is it not seeing the avatar? Chat widget, conversations, messages within that. It's pushing that into the array, message.data.0. That would be why. Alright. One more time again. I'm just gonna delete some of these. Save it. We refresh over here. We can see there's our convo. We do yo. We save. Okay. So now over here on the client, I can see that conversation or that message being populated. So we'll just add a little gap between these space space y 2. Okay. And we'll probably need to do like message dot user created dot if we have a message user dot created and message dot user created dot avatar. Alright, so we're only gonna show that if we have it. And then the next thing that we need to do in a hurry here, about 9 minutes, is to actually start populating these messages, right? So we want to add some form fields to this down here at the bottom somewhere. So if we're inside the selected conversation, great, let's add the message form. Cool. Alright, and we'll do, I've got this nice Nuxt UI library here. So we'll do Submit and Message and it just gives us some nice inputs here. Flex column, there's the wrapper. Where is this actually gonna be? Inset 0, uform, send message, v model, new message. Alright, so let's add our new message. New message equals ref, that'll be blank. Let's have a function for send message. Okay, send messages, create conversation, select a conversation dot value, new message dot value, user created. Nope, Where did you go? We can rely on AI here. And we don't want a UUID or UID for this. Oh, maybe we do. Send message. And we're we're not gonna use the user created. Alright. Does this give us, where's my little bar at? User, User selected conversation. There's no selected conversations. Vfusermessage.created. Oh, message dot user created dot avatar. Okay. So now we're going to form. The form should actually be pushed to the bottom. So that'll be flex 1 height full. Okay. Then inside this form, I want that to flex, justify between. Okay. And we add a bit of padding to that as well, p 2. Give it a border, border t. And we stick it to the bottom. Nope. This will be like overflow. Why? Auto. Okay. Flex justify between, and this could probably even be like stuck to the bottom in the absolute. Alright. So let's just change this to like text area. Hey hey hey. And now we can see we can actually chat with that specific person, inside our send message, we're probably gonna delete that new message as well. Just set that value once that's actually populating. Okay. So now I can see my convos. I can open up that convo. I can send a message to their team. Great. How could we actually show the help center articles? Right? In 5 minutes, can we get this rich content within there? We're going to be pushing that, right? Pushing it quite a bit. Alright, so we got our chat widget. We're probably gonna need like a page functionality or something. Like hey, what page are we on? The default is, let's set that to chat. So this will be this is our conversations. And can we wrap this in another template tag? Or it might be easier just to stick it inside a div. Alright, so div if page equals chat, We're gonna show that. Alright, so we should still see that now when we open this up. Still see that. Great. But let's just show another page. Div v if page equals articles. Alright. So then we're gonna show the articles. Maybe we show like a card v 4, Article in articles. I don't think any of these are actually gonna apply. View card, we'll just do like a p tag for the article dot title And see what this shows. Key is equal to article dot ID. Alright. And now we need like some kind of widget at the bottom of this to actually show, right? We need like a menu. So let's do div, do you like buttons? This is not at all. Show an icon. Name equals herocon's chat bubble left ellipsis. Let's just call this chat. And actually, and then we need a new button for icon name equals articles. How are we doing on time? 3 minutes. Are we gonna actually get this or not? Let's see what this shows. Chat. This is gonna be absolute bottom 0, p 4flexgap4, give that a little gap. I don't particularly like the icons for this. Let's just okay. And then if click at click, this is gonna be page equals articles. There we go. And at click page is equal to chat. Okay. Is that actually gonna work? Page is equal to chat. Page is equal to articles. Okay. So now we're switching those back and forth. We just don't have any articles, right? So let's use our client here. We've got articles. We could do a ref. Just make that an array. And then we'll have like a function, get articles. And we can use that same connection again. Right? Fetch articles. Yeah. I could use the regular rest connection as well in this case. But let's try to use this. Send message. We want collection of articles. This is gonna be a read. And the data, UID, we'll just do get articles. Okay. Oh, how we doing? We got 1 minute 20 seconds. Get articles. That's our function. Okay. And then on our receive bit here, if message dot UID and message data articles dot value dot messages. Okay. What else do we what else do we need to do here? We need to fetch those articles first, right? So let's do like a watcher. Watch page. New page. If page equals articles, get articles. Is this actually gonna work? Error. Invalid collection. Oh, no. We don't have articles. They're what? Post. Post. Post. So what is this? There we go. With what time to spare? 21 seconds. Right? Okay. So we can actually see a list of the articles. That's about as good as we're going to get in in this amount of time left. I'm calling that a win. So we can actually show the list of articles here. The next part would be to just probably actually, like, set these up to to, like, be able to read the rich text for those articles right within the little widget. But, you know, that is a wrap. Great. And if I open up Directus, we can see all the messages within that conversation. And honestly, I would probably build some kind of interface over here inside Directus to be able to manage this. Alright. So if we just wrap this one up, did we actually get to the intercom messenger? You know what? In an hour, almost impossible to rebuild. I'm I'm calling that now unless you built it before. But, yeah, far cry. But hey, the basics are there. And it goes to show you just how, how far along you can get with Directus in an hour using the tools that are provided. So that's it for this episode of 100 Apps, 100 Hours. Hope you enjoyed following along. You could probably see the sweat bubble up on my face at this point, but stay tuned for the next one. We'll see you.","7d3f53c0-2619-46ed-86c6-f7ead9eb6ea7",[2774],"ceba6402-a949-47b4-8ad6-7e84252f53d9",[],{"year":978,"number":2062,"id":2311,"episodes":2777,"show":2778},[2313,2314,2315,2316,2317,2318,2319,2320,2321,2298],{"title":322,"slug":323},{"id":1845,"slug":2780,"vimeo_id":2781,"description":2782,"tile":2783,"length":830,"resources":12,"people":12,"episode_number":177,"published":2784,"title":2785,"video_transcript_html":2786,"video_transcript_text":2787,"content":12,"seo":2788,"status":19,"episode_people":2789,"recommendations":2793,"season":2794},"david-simmer","948632658","In this episode of Trace Talks, David Simmer, Senior Software Engineer at Netflix, shares his unique journey into tech, starting from college AV\u002FIT jobs to helping technical teams be more productive at Netflix. He talks about the importance of empathy in engineering, balancing hands-on coding with leadership, and using generative AI to boost efficiency. David also offers practical advice for aspiring leaders on seizing opportunities and promoting themselves. ","d5252dcc-cdb3-4caa-bc2d-7b32622b8900","2024-06-06","David Simmer, Senior Software Engineer at Netflix","\u003Cp>Speaker 0: Remember the first time I, like, normalized a relational database. There was this clouds parting, feeling of of, like, I'm smart and I got this thing, and it feels so good to to see the elegance of it.\u003C\u002Fp>\n\u003Cp>Speaker 1: Hey, everyone. Welcome to another episode of Trace Talks. We've been graced with the presence of David today. John and Pedro here as well. Don't don't worry.\u003C\u002Fp>\n\u003Cp>We're still here for this conversation as well. But, David, I'd love for you to give yourself a brief introduction. Let the world know who you are. Just brief because we're gonna definitely dive into your background. So but let the world know who you are.\u003C\u002Fp>\n\u003Cp>Speaker 0: You got it. I am employed right now as a senior engineer at Netflix. I'm in the consumer foundations team, but I spent, nearly the first 4 years there in the engineering. And outside that, I have a list of hobbies that's about 5 miles long.\u003C\u002Fp>\n\u003Cp>Speaker 2: Amazing. Yeah. We'll we'll definitely wanna get to that. But maybe at first, like, you know, what could you take us back to the the moment that kinda ignited your passion for, you know, blending technology and and design and, you know, kind of what got you into technology and into the space in the first place?\u003C\u002Fp>\n\u003Cp>Speaker 0: For sure. For me, it was a student job in college. I got a job in the AV department, and it turned out to also be the IT department, which tells you something about when that was and how small things were at the time. And what got me into that was as a student at that college attempting to use the online scheduling tool that they'd built to just figure out what my class schedule is gonna be for for the term. And, the two things I noticed, most strongly about it were that it was ugly and hard to use.\u003C\u002Fp>\n\u003Cp>And I started asking, could someone change this? And then that question turned into, can I change this? And one of the great fortunate circumstances of my life was that the person in charge said, sure. Here's something you can change, and that was really the start. And and those two things, like, ugly and hard to use are really the the the converse, which is beautiful and nice to use is just continues to be a motivation.\u003C\u002Fp>\n\u003Cp>Speaker 2: Amazing.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. I was I was gonna I was gonna make a joke that half the technology out there today still is ugly and hard to use. So you saw you saw that problem 20 something years ago, which is pretty awesome. Did you have any technical experience before that or maybe just tinkering on a a computer every now and then? Or or you just saw something ugly and you wanted to fix it?\u003C\u002Fp>\n\u003Cp>Speaker 0: Only a little bit. I had taken a rudimentary Photoshop course at Kinko's, before I owned a computer. And then, you know, just really basic stuff. I think a friend had had me over and been like, this is email, and here's how to get on AOL. But, other than that, a little bit of tinkering with, like, a hand me down DOS computer and a little bit of work on trail.\u003C\u002Fp>\n\u003Cp>That was it. So it really was just the motivation of, I don't know how to do it, but I would like to learn, and I have some, you know, I have even though I don't know anything yet, I have some ideas about how that maybe this could be better, and that was the start.\u003C\u002Fp>\n\u003Cp>Speaker 2: Mhmm. Got it. Did have you since taken any sort of formal sort of design or UX UI, dove into that space?\u003C\u002Fp>\n\u003Cp>Speaker 0: Not not in the terms of, like, higher education or degree programs. So I I went to school originally to be a chiropractor. I wish I'd had better career advice or maybe had had changed tech earlier, but, I I have a, like, science and biology education, in undergrad, and all of my tech learning was, you know, the sir the term self taught is usually applied, but, this is something I mentioned a lot. I am taught by all the thousands of other people in this industry who love and are excited about what they do and who are delighted to write articles and make online courses and, produce demos showing how to do things with various technology. And so I have always loved, like, the design aspect.\u003C\u002Fp>\n\u003Cp>My mom, had an art background, and so I was homeschooled as a kid, and so she made sure to incorporate that. And I always I always really enjoyed that part. But the actual melding of design and tech, that was just this exciting new field that there was, you know, there were so many resources out there, to consume that that's really where that came about.\u003C\u002Fp>\n\u003Cp>Speaker 1: What what was the because I also got into technology kind of probably around the same time frame, and I was in school for design. Sure. But I remember back then, there wasn't there wasn't as many forums or definitely wasn't as many videos or boot camps, all those stuff that's out there today. What did you rely on? You know, you you took on you were going to school for chiropractory and science.\u003C\u002Fp>\n\u003Cp>I don't think that's even a word, chiropractory.\u003C\u002Fp>\n\u003Cp>Speaker 0: I like it.\u003C\u002Fp>\n\u003Cp>Speaker 1: But you were going to school but you're going to school for something different, and you took on this tech project. How did you learn to build I looked at your LinkedIn, and you built a CMS. You built everything that was needed. How did you find how how to do that back then? You know?\u003C\u002Fp>\n\u003Cp>Because I had people in person in school. I was going to school for that, but there wasn't forums. You know? Were you in communities in person asking these questions, or did you find some online forums to ask questions?\u003C\u002Fp>\n\u003Cp>Speaker 0: It was there was a little bit. I remember, the, Jeffrey Zeltman's resource was a huge, or, like, his both his personal writing and then a list of part, which is a heavily CSS and, like, web standard sort of resource. That was one of my main go to's. And from there, that is really how I think I I you know, you could say I got my UI design education by their emphasis on not just, hey. This is beautiful and here are principal's design.\u003C\u002Fp>\n\u003Cp>I mean, they mentioned those things, and, clearly, a a lot of the people in the community cared about that kind of stuff. But the, the larger point of so much of their writing and their tutorials and their excitement about, like, CSS becoming the main way to style things and and and the accessibility benefits of that versus the old clutches we were using to try to get things layout on screen that totally ignored the concerns of screen readers or assistive devices or anything else was this, it needs to be nice for humans to use. And it being beautiful is sometimes a companion, sometimes as a side effect to that, but that the primary thing is here's how to make it nice for him to use. And, also, by the way, here's how to have it kind of be more pleasant as a developer. You get that that mental satisfaction of having done this in a way that is elegant.\u003C\u002Fp>\n\u003Cp>Again, that that elegance, you know, can turn into cleverness and be awful in a lot of ways, but that was always a side effect of doing building things in a way that are pleasant for humans to use.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. And that and that makes sense. I think that was that's sort of, like, the turning point for technology become more widely adopted, right, with the PC and, kind of personal computing. And still companies today are pushing that forward. Like, a company that could take something very technical and and and just make it user friendly and beautiful as I I don't know.\u003C\u002Fp>\n\u003Cp>The the example I always think of is, like, Square. Right? Like, there have been payment processors and credit card terminals and all these things, but they made a very simple, smart little thing that had a beautiful, simple design and Yeah. Captured a ton of market share by just doing that, like, leading with design.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. It felt like the future instead of this awkward thing that beeps ain't really at you. It was just nice and yeah. Makes sense. Yeah.\u003C\u002Fp>\n\u003Cp>But there I I think all the time about, like, this sort of if you build it, they will come aspect of, like, you can kind of fall into that trap as a designer engineer where you just think, if I just build the beautiful thing, my marketing and business viability and all those other problems will solve themselves, which, of course, is your however, like, that do build something beautiful is still something worth hanging on.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. You've that's a good point to make. You know, you've you see a problem. You make it beautiful. You make it usable, and and people will gravitate towards it naturally.\u003C\u002Fp>\n\u003Cp>Nice analogy, Pedro. I use Square myself. So it definitely I've I've found the the simplicity as well as ease of use, important. You've been on this long journey, not long, but you've been on this, wide ranging journey of different types of roles. I mean, even starting from chiropractor school to doing a c a CMS, and now you're at Netflix.\u003C\u002Fp>\n\u003Cp>And there's been a long journey along the way. What do you look for when you're looking for new projects or new roles, whether it's internal where you're at or, external companies? You know? What do you gravitate towards? Do you gravitate towards somewhere where you know you can fix problems, or do you gravitate towards somewhere where you know you can grow as an employee and as as a engineer designer?\u003C\u002Fp>\n\u003Cp>You know, what I guess, what ranks importance for you when you're looking for new projects and roles?\u003C\u002Fp>\n\u003Cp>Speaker 0: Having, like, a baked in change of scenery or having having a change of scenery every so often has always been really important to me. I had, for a while, when I in one of my first roles, I was working for a small company. I probably stayed there longer than I should have. I had a bunch of friends, who were in agencies, and they had this sort of mental turnover of new clients, new projects that kept them interested in tackling new things. And I really started to desire that.\u003C\u002Fp>\n\u003Cp>And so that, for me, has been a motivator in finding new things. In fact, it was and being the reason I left that company and set out to freelance for a while. I, rely on money to pay for the things that I enjoy and and, need to live in life. So that's also a motivator. But, really, you know, that that aside, I think there is a blend of the 2.\u003C\u002Fp>\n\u003Cp>So when I found the role I was hired into at Netflix when I read that job description, it described it used all of the words that I was looking for in terms of the the mission of the role and what I'd be working with and the variety that was baked into it. And so that was one of the exciting things. But I think what, also really drew me to that was that it was on a team of people who had a wide variety of skills, but where my skills weren't sort of fully representative, weren't already duplicated by somebody else. And so it was gonna be that chance to to bring the things I knew that I was good at, and to to add them there. I I didn't come to it that role with the feeling of, though, that, like, I got this.\u003C\u002Fp>\n\u003Cp>I'll show them, you know, what's going on. It was it was very much the opposite of, okay. This is the role that sounds awesome, but, of course, it's Netflix. And, you know, I'm not ready. Maybe they'll give me more time to prepare.\u003C\u002Fp>\n\u003Cp>And, of course, you know, the interview, you know, was like, can you come talk next week? And so I, you know, I I put a lot of effort into it, but it was that, that combination of this is, this is an amazing opportunity. It's exactly what I wanna do that I think really spurred, you know, really putting a lot of focus and effort into into that interview process there.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. And it and it seems like throughout your career, you've kind of transitioned between different roles and different industries. And now at Netflix, like, what what experience do you think was the most important or the most pivotal in shaping your approach to leadership that that you're sort of in it in that position now?\u003C\u002Fp>\n\u003Cp>Speaker 0: Sure. There's been a few things. I I think the the progression I've been I've made in my career, the opportunities that I've had have been, thanks to other people who kind of already understood it. One of the people I think of a lot is is a old boss of mine, Gunther, at that first company, who in our one on ones, instead of it being project recap time, he made sure that we were talking about what did I want to do in my career and what was I interested in long term beyond this company, beyond working for him. And then fast forward a few, and there was a moment where I was a more senior engineer on a relatively small team.\u003C\u002Fp>\n\u003Cp>And there was really a need, in order to make our junior engineers more effective. It wasn't going to be sort of teaching them in the sense of, hey. Here's how you do complicated things. They already had a lot of of skill at the technical things that they were taking on. Where, leadership was needed was for someone like me to do the glue work as it's, been described of, you know, a little bit of private project management, a lot of, taking care of some, you know, just sort of, like, unblocking chores for the team, being ahead of things like that, setting a a direction, you know, in cases where there was some ambiguity, providing, at least a decision and some reasoning, that we can all act on from there.\u003C\u002Fp>\n\u003Cp>Yeah. I think it was seeing examples of other people and then just kind of realizing that there's this need and this is that by serving these other people on the team, that is actually a huge part of being a leader. And, and it was satisfying to start doing that. You know? In in isolation, the idea of of just, like, project management work sometimes feels boring or a chore or something you wanna procrastinate, on.\u003C\u002Fp>\n\u003Cp>But when you are multiplying the happiness and and effectiveness of other people by doing that, then it becomes satisfying.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. I that's a common thread that we've heard in a lot of these conversations that we've had is people who have worked their way up to some form of leadership. They've had good leaders, a lot of good leaders along the way. Some of them have had not great leaders, but, they all have this drive, and I I think it's ingrained in them by those good leaders. I think good leaders today have been trained by good leaders.\u003C\u002Fp>\n\u003Cp>So do you enjoy leading more now, or do you enjoy getting your hands, you know, in and doing coding and designing still, a mixture of both? Some people prefer, you know, to train people and lead them like a true leader should, but some people still miss getting in their getting their hands in the dirt and and doing development work. So do you still get a mixture of those, and do you like that combination, or do you prefer 1 or the other?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I do still get a mixture. I do still love that mixture. I I often think about this because definitely the part of me, the engineer designer who loves building things, can very much get in the zone on a feature and be very happy, you know, towing away there and making it really awesome. And what I have to remind myself of is, like, I do have a desire to be a a good leader, a good engineering leader.\u003C\u002Fp>\n\u003Cp>And I often have this feeling that and this is probably something, you know, I was gonna say, real leaders also still still feel what that that feeling of, like, well, I'm I I don't have as much experience or as much to give there yet or, you know, I'm not I want to be, but I'm not yet. And I think that that awareness isn't all bad, but also that just saying, well, I'm not a leader yet isn't necessarily helpful because I am doing things that are, leading other folks. For instance, I'm I'm frequently mentoring a junior engineer on our team right now, who is awesome, but there's so much about being a software engineer that isn't necessarily computer science. And a lot of those things, like, we are working together on, and it doesn't feel like me wagging my finger at Hertz. We're talking about these things, and I am able to provide that.\u003C\u002Fp>\n\u003Cp>And I love doing that. I think one of the things that's essential if you want to do that is to make sure that you have some time set aside to do that. If I get so attached to my feature work and building the thing and then realize, like, oh, it's 1 minute until my 1 on 1, with the person I'm mentoring. Okay. Well, I'll just ask her how things are going.\u003C\u002Fp>\n\u003Cp>Then not only have I will I probably not do as, I probably won't be as good a resource for her, but also I've short changed myself on putting having some time to think ahead about what is important here, even even if there wasn't mentorship involved. For me to spend more time thinking ahead about the strategy for the project I'm working on, where I fit in my organization, how I see roles shifting, or the importance of teams, or how a strategy doc that I read might affect our team. Those are the things that leaders do. And if you begin spending the time doing the things that leaders do, either you'll find yourself being equipped to be a leader or you'll find yourself actually being in a leadership position, because you began acting that way. And that's that's really the the approach I've been trying to take.\u003C\u002Fp>\n\u003Cp>You know, I I love getting to have a 1 on 1 with a director or, you know, like, several skip levels up. And and I'm always asking, like, how did you get here? But it's not just what did you do to get here. It's what circumstances got you here, and what do you spend your time doing. And these these roles that I, you know, possibly aspire to have one day, I'd like to be, at least able to think like a person who has that role now even when it's not my role yet.\u003C\u002Fp>\n\u003Cp>Because without that reflection and beginning to get yourself in the mindset of that different role, it's extremely unlikely someone's gonna come along and be like, great work building that thing. You're gonna direct some people now. So that's that's the mindset I've tried to take.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. I think it's important also as you as people continue to to climb upward in their, you know, personal and professional trajectory to to remember because you had mentioned, feeling like you might not be qualified or feeling like you don't know everything to be in a position that you might be in. I think it's important for people to realize that nobody nobody knows everything, and what you just mentioned there still having time to reach out to your superiors to to ask those same type as a questions that you're passing that information down to people who you're training as well. I think it's important to remember growing as a human and growing as a employee and growing as just whatever you're in, whatever field you're in, it's a two way street. You know, you learn from those who have been there before, and that's kind of the reason of this podcast.\u003C\u002Fp>\n\u003Cp>And then you you pass that information down to those that you're training. So do you these days, I assume it's probably a different answer than if you'd answered this 15 years ago. Do you find more satisfaction in a junior engineer that you're training, get unstuck from a bug that they've been trying to fix or get promoted, or do you find more enjoyment in fixing a a major bug yourself? I assume it's probably seeing those that you're training, you know, get to the next stage in their development.\u003C\u002Fp>\n\u003Cp>Speaker 0: Right. Like, it's it's tough to answer because it's still still joy in both, but I think you're right that it is the the latter because that's beyond that there's a a bigger effect that cascades far beyond that I, you know, unstuck a button that that wasn't working. Yeah. To see another person take off. And especially, like, there's there's so much joy.\u003C\u002Fp>\n\u003Cp>You know, I I had mentioned that I had been in a in one career and then switched. And in in down moments, I have phrased it as I wasted those years of my life. That's not true, of course, because the person I am now is built out of those experiences. But one of the things that I get a lot of joy out and mention explicitly to younger engineers that I see, is that I'm so stoked that they are, like, getting started now. At work and especially for those who are really good at customer empathy and being curious and, thinking about strategic things and the health of the team and and sort of the work beyond just the the work itself that they are going to go so far.\u003C\u002Fp>\n\u003Cp>And especially, you know, when I, like, I when I run into these sorts of folks at Netflix, I'm even more excited of like, for me, it feels like this amazing place to work where, I I finally got here, but after quite a long time in my career at, you know, you could say, like, smaller companies. I mean, smaller companies. It felt it felt like a a personally, like, such a huge achievement to make this this prospective company. And, I am so stoked to see young engineers getting jobs at, like, awesome companies and getting to start learning from amazing people way earlier than I did. It's it's so cool to see that, and I love being a part of that.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Stoked. I haven't heard that word in a while, so I I I appreciate the usage of that word. I mean, if you also, if you train people who are you, you know, who are your ment mentees, they'll do a lot of the work that then will possibly or potentially even enable you to then get more hands on in the code and and start fixing things because they're they're being smart enough and self sufficient enough to solve some problems that maybe a leader who hasn't trained their employees, would have to solve all of those problems. So I guess in a in a way, it probably helps you actually get back into coding and fixing bugs if you're training people to do some of that forward thinking leadership type stuff.\u003C\u002Fp>\n\u003Cp>Speaker 0: Totally. And, you know, there's so much that I still don't know or things where, you know, if I was just working by myself, I'd be looking up. Of course, the the great secret of of senior engineers is that we're still googling everything. And to do that in front of a junior engineer, I think, is a really helpful experience too to say I don't know. Or, you know, I've used this 4 times, or I configure this thing once a year when I set up a new whatever it is.\u003C\u002Fp>\n\u003Cp>And I don't remember what I was doing, so let's pull up in the documentation together and walk through it, and, you know, we'll we'll figure out how to do this again. Or that thing you're you're dealing with could be this, this, and this. It's not those. Okay. I don't have a ready answer off the top of my head, but you do a little more investigation.\u003C\u002Fp>\n\u003Cp>I will as well. Let's talk again in a couple hours and see if we've gotten anywhere. And that's that's what being an engineer is all the time, and, it is freeing to, be able to admit that. And, also, like, that would be a a chance for my own growth and my own either remembering how to do the technical task or, in the larger sense, just reminding myself that there's, there's I'm I there's so much I don't know. There's so much I'm gonna forget to remember and just it's I think it's so valuable for anyone who hasn't figured that out yet to get a glimpse into.\u003C\u002Fp>\n\u003Cp>That's just always that's gonna be how it is throughout your career. You don't have to feel bad about not knowing right away because 20 years from now, you still won't know anything right away.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. The other thing that's interesting that you mentioned is empathy because it's it's something that we don't typically think about as a, you know, an engineering skill. Right? You think about the technical skills associated. But would you say that empathy and ABC's some of these other skills are, just as important or maybe more important as engineers are making their way, you know, up through the ranks or, you know, in different roles, within organizations?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Absolutely. Because there's so much, like, delightfulness and fun in embracing and understanding a complex thing. I remember the first time I, like, normalized a relational database. There was this clouds parting, feeling of of, like, I'm smart, and I got this thing, and it feels so good to to see the elegance of it.\u003C\u002Fp>\n\u003Cp>So there's plenty of pleasure there. But, I think once you get out of just building something for its own sake or learning a technology or or kind of seeing how clever you can get with your understanding of a knowledge of a language that you know well, Ultimately, you're going to be engineering things, designing things for someone else to use. You may be several layers removed from, let's say, a customer outside your company. In my case, you know, productivity engineering, your your customers are other engineers inside the company. And so still that, that aspect of empathy is necessary for a couple things.\u003C\u002Fp>\n\u003Cp>1 is just that when you have gotten a request for something to be built, something to be changed, You could just take it exactly as it's written and knock out that thing and feel like you check the box and move the Jira store over to the done column. But to be good at what you do, the result should really not just be only the thing you were told to do. It should accomplish the purpose of what are we trying to do here. Are we trying to make this simpler for a user? Are we trying to actually make the experience a little higher friction to avoid someone mistakenly deleting something important?\u003C\u002Fp>\n\u003Cp>There's so many of those ways of thinking about things that that you're designing and engineering. Were to produce the highest quality version of the thing that you were assigned, you need to think through the eyes of the customer, probably the business, you know, understand all the needs that go into that. But then I think in addition to just producing the best version of the thing you could, I think empathy gives you curiosity and excitement and delight and the interest in taking on or suggesting changes that weren't already thought of, that weren't, identified. You can be the person who identifies those things. When you hear the frustration that someone has of, like, like, I have to click this 5 times, and then sometimes that this last step doesn't work.\u003C\u002Fp>\n\u003Cp>One way it's you could view that is, like, okay. Well, the problem is, like, the the bug is that it breaks at that 5th step depending on a certain scenario. So you could fix that, but, also, is there I have to click 5 things to do this at once? Like, that customer just gave you some very valuable insight into what they're thinking or kind of what they want or what annoys them. And if you're not attuned to catching those kind of things, you might not ever take the time to say, can we rethink how this interaction works so that it's one click?\u003C\u002Fp>\n\u003Cp>It's it's none. We, you know, make this whole thing more pleasant for the user. We don't just fix the bug that was described. We listened, and we might be able to invent something that really makes this whole thing more pleasant.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. I think I think you're so right. Yeah. Like, that was\u003C\u002Fp>\n\u003Cp>Speaker 1: I think you could have been a chiropractor. Someone says my back hurts, and we kinda find out why their back is actually hurting. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 2: I think you're so right, though. Sometimes we're so we're so involved, and we're so deep and close to something that we can't zoom out and see the bigger picture, and it does take empathy to do that. I mean, you know, John and I can speak for ourselves, but we're in conversations with folks all the time that are just like, oh, I need this software to check these four boxes. Like, well, hold on. Let's back up and, like, how does this impact you, the organization, the people you work with?\u003C\u002Fp>\n\u003Cp>Like, what's the actual business outcome, the personal outcome? And I think, engineers thinking that way is is definitely an interesting way to look at it because you can you can actually solve the core problem. Sometimes they're just like, oh, it doesn't like you said, it doesn't work at the 5th step. Well, maybe the whole process is the problem, and it could be reinvented. So that's that's really interesting.\u003C\u002Fp>\n\u003Cp>Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. So what you know, all of working your way up to leadership isn't, roses and daisies. So what kind of, you know, part of this podcast is kind of passing on this advice and and your experiences to people who are listening. So what have been some of the struggles that you you faced? Is it, you know, having to deal with meetings, leadership meetings?\u003C\u002Fp>\n\u003Cp>I I assume those are I don't know how they are at Netflix, but in in some places, they're not fun. They're just kind of monotonous. But what have been some of the struggles that you've faced as you've worked your way up and and some things that you would say, hey, 15 year younger myself, be aware of this and or do this.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. The first things come to mind are kind of personal habits of, like, giving myself a little too much room to procrastinate, not being as frequent a communicator, an aversion to maintenance. Like, I I was thinking about this recently that I think I have this inbuilt feeling that whenever I, like, buy a thing, it should just continue working forever. And, of course, you have to, like, change oil and dust and clean and, you know, all those may maintenance things. And, I think in similar ways, times where I've done you know, I I was given a chance to lead a thing and I didn't do a great job at it, Oftentimes came down to that of a lack of personal organization.\u003C\u002Fp>\n\u003Cp>You know, I didn't kind of put in some important work ahead of time, and so I had to scramble to get a result at the end. And sometimes that works out, and sometimes it doesn't. And, in making yourself, you know, not making yourself visible enough or not being a communicator, not chiming in on meetings, I think there's a lot of a lot of times where had I been more aware that just that involvement, which takes work and energy to get yourself to, like, be there with your camera on and and, you know, to really to really be involved and not just, I saw a cartoon someone was like, how to how to, you know, make it appear that you've been contributing me to a meeting is to just say, thanks, everybody, at the end when you haven't said anything to the meeting. I mean, I so I've done that for a while, but I think I think avoiding those habits that require less energy, less investment, and just kind of, working on that is something I wish I'd started earlier. I heard I heard someone say that they've, it was another engineer at Netflix, It's Molly Stroup.\u003C\u002Fp>\n\u003Cp>She said, I realized that I wouldn't be an effective leader until I was an effective leader from in my own life, and that's really stuck with me of you just your personal inclination to wash the dishes and wipe down the kitchen counter when you'd rather just leave it for somebody else or not do it at all. I think that's that is a great little microcosm of, the kind of things you need to be, just willing to put the time into if you want to be a leader. And, you know, I mentioned I I mentioned, like, being on camera is an offhand thing, and there's plenty of reasons why in meetings. Like, I I'm glad our shift towards remote work and getting used to doing things over Zoom and the reasons why someone might be on camera. I'm not trying to denigrate any of those.\u003C\u002Fp>\n\u003Cp>But, in the the sort of being if if leadership is something you want to grow into, that putting this extra work in to provide energy and provide involvement to other people is really part of it. Just if you are there with contributions and and the feeling of, like, this is useful to be here and I am bringing my bringing my whole self here. Other people pick up on that, and that I think is is, super valuable. So I wish I'd I wish I'd focused on being that kind of person sooner.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Yeah. That reminds me, kind of on the opposite spectrum. I I used to work at Dell, and there's a guy who had been in the same position for, I don't know, 15 years, 20 years, something like that. And I asked him one day.\u003C\u002Fp>\n\u003Cp>His name is Joe. I'll I'll call out someone's name too, Joe O'Brien. I was like, Joe, you've don't you ever wanna, you know, climb up the ranks and be a manager or be a regional manager? He's and he said something he said, I'm really happy with my life, and I really like where I'm at, and so I'm comfortable here. And so I think on the other end of the spectrum, it's and this applies to leadership as well.\u003C\u002Fp>\n\u003Cp>You kinda have to understand what your personal goals are. Don't feel like you have to climb to leadership because someone else thinks you should climb to leadership. But if it's something that you feel like you want to do, then start to put those habits into your life. And so that that's some really good advice that you actually just gave there, which is be a proponent for yourself. Even if you're not ready, start putting yourself in that position to have those opportunities because you never know when you're gonna learn something, just because you're present.\u003C\u002Fp>\n\u003Cp>So that's good advice. So, yeah, you know, what speaking of tech, you've seen, you know, both you and I came sounds like we came into tech around the same time frame. So there's been a lot that's changed over, you know, 15, 20 years, and a lot has changed in the last year or 2. You know, you start talking about AI and all that kind of stuff. So what's your vision for the future?\u003C\u002Fp>\n\u003Cp>Are you always one of those on the forefront of technology? I think Pedro bought the r one Rabbit, which is a brand new AI device. But do you always try and embrace new technology? Do you kind of skirt the outsides to see what other people are doing with it?\u003C\u002Fp>\n\u003Cp>Speaker 0: I think I'm a little more of a scooter. I I like keeping up with things, and I always have the the thought whenever I've seen I think, like, we all have maybe a a character, like, you know, the sort of person to which someone would say, okay, boomer. You know? The the idea of someone who's so fixed in their ways, they that all modern music is shit and, kids these days and nobody wants to work and all those kind of things you hear. Like, how did you become that kind of person?\u003C\u002Fp>\n\u003Cp>You know, we've had that kind of person throughout every generation, and I don't wanna be that person. And so, similarly, in technology, you know, I have my formative era of what I got good at and what I was excited about, and there are certain branches off of those things that I spent most of my time on that I continue to be excited about innovations in. And, of course, no one has time to sort of be fully on top of every new innovation that's out there. So I often have a feeling that I should be more on top of things, but also, like, keeping an openness and and an excitement about how other people are using things, I think, is is still important to have. I could do more of it, but at least, I think I'm maintaining the idea of of openness.\u003C\u002Fp>\n\u003Cp>I I am definitely surrounded by people who do a great job of more quickly embracing and tinkering with something new. And so, you know, I'm sort of maybe getting a secondhand keeping up by by keeping up with them.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. Are there are are there any of these technologies that you're you've seen make an impact at, you know, at Netflix or any other companies recently?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I think the generative AI one is interesting for, like, specifically in writing code. I I gave a talk about this, a little over a year ago in which I expressed a lot of, if not skepticism, at least in encouraging people to pump their brakes on embracing it too quickly. Because, for instance, with writing code, you can only trust the output of an LLM when you can actually verify it. And so paradoxically, in order to make use of it, you have to kind of be able to do it on your own.\u003C\u002Fp>\n\u003Cp>But, also, there are time savings to be had. There are so many sort of, like, we still bump into boilerplate all the time, and to have something where you can, automate for yourself the repetitive things, you know, the the why should I be typing the same syntax over and over again perhaps when there's some way I can automate the snippet or the generation of it or say, hey. Write me a unit test for this thing. And to incorporate some of that without fully relying on it, I think, is something that I need to get better at and is a good skill to have. Yeah.\u003C\u002Fp>\n\u003Cp>That one is, I think, like, dominating the airways right now, and I think that the people who will make the most of it, maintain sort of, like, a little bit of a healthy distance. Like, they use it for what it is, but don't see it as, true artificial intelligence yet or or replacing their job. I think the other thing that, that I have isn't necessarily the future, maybe it's sort of standard now, but the idea of design tools that have less of a barrier to entry. So I am a big Figma user. Before that, it was InVision and Sketch and and those.\u003C\u002Fp>\n\u003Cp>And and, of course, like, I grew up with Photoshop and Adobe Suite. And, you know, spending every day in Photoshop, I got used to it and I can move around quickly, but it's difficult to learn that kind of thing. And the way design tools have shifted to you don't need to have spent 20 hours in this to learn how to do it. Everyone can participate in from lo fi to hi fi, getting their ideas in a visual form and building from there. I think that in all its various forms has been incredibly valuable, and I am glad to see that continuing to happen.\u003C\u002Fp>\n\u003Cp>There's so much engineering and design work within companies, even Netflix, where a lo fi, low code kind of way to do something is the really smart way to do it. And, I think that the more people who are encouraged to think of themselves as doing design work, if not necessarily being designers, is a really healthy thing.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. I can tell you from firsthand experience, like, I used Sketch actually to design my first startups, like MVP app. Right? And I just kinda wireframe the whole thing out and gave it to the engineers, and we're like, make this happen. This looks good.\u003C\u002Fp>\n\u003Cp>And there were some elements of that design, like, found in some kinda, you know, kit I downloaded, like, some UI elements that I had pieced together and changed around a little bit. But you're right. It was and that allowed us to launch our app in this, like, you know, it wasn't completely no code, but, definitely low code in in the sense of me designing the front end and just handing it to the engineers to to make happen. So it definitely accelerated our development and put a product out faster. So, yeah, I\u003C\u002Fp>\n\u003Cp>Speaker 0: think\u003C\u002Fp>\n\u003Cp>Speaker 2: it's super important.\u003C\u002Fp>\n\u003Cp>Speaker 0: And there's still a need for pros who are great at design, and and that won't go away. But the, lowering the barrier to entry, I think, has been amazing. Tools that continue to lower the barrier to entry to producing something, to getting the satisfaction of the thing I built I can see on screen, is great.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. That's a great point. I also learned Photoshop and Illustrator, and I can still use those today. But for example, I'm actually I'm not gonna show it off in camera, but I'm wearing a shirt that I used AI to create 2 images, front and back. Took me 30 seconds when that would have taken me an illustrator, you know, 40 minutes, if if not more.\u003C\u002Fp>\n\u003Cp>But like you said, when you talk about programming and using AI for programming, you still have to know what you're doing because AI is not even anywhere to the point where people claim it can be. So it's smart to have that sort of healthy skepticism both with that type of a trend like AI, but I think in general, I think skirting the out or or, you know, skirting the edges of anything that you're approaching, whether you're thinking about being a leader or you're thinking about doing x or y. You know? Do your research, understand the pros and cons of anything, and find where it can help, lower the barrier. I think that was another great phrase that you used because, you know, I can have AI draft the first draft, and then I go and clean it up.\u003C\u002Fp>\n\u003Cp>Because robots, I probably shouldn't say this on recording, but robots will never be as great as humans. So, it's but it's yeah. I think it's that was a great term that you used, lowering the barrier.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. So so, you know, you've definitely gone on a a long journey. I think there's been some great advice. I think having, having empathy and, you know, leading on a two way road has been some great advice. So, you know, I guess for those who are listening who either don't know if they wanna be a leader yet or those who do know that they wanna keep climbing the ranks, what's some advice you gave advice for your younger self, but what would be some advice just in general that you would give to people who are out there looking to climb the ranks?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I would say raise your hand, when opportunities arise for anything. Volunteer to help set up your next team on-site. There's an extra project that would, you know, clean up some old data, you know, not to not to take on, so much work that you burn out or that you're sacrificing other parts of your life. But within, your ability to do so, you know, say yes to to things.\u003C\u002Fp>\n\u003Cp>Those, those opportunities that you get, are not just about the task that you might do. They are about your they provide you this chance to work with other people that you might not have already, to be seen by other people you might not have already. And one of the things that really, some of my close friends had to drill into me, at some point in my career was promote yourself of of, like, don't be so humble and self aware that you don't ever say something nice about the things you're proud of, or that you, hesitate to, say, yes. I can take care of that. And I think even beyond that, you know, my wife, will frequently point out, what she used phrases, my white boy confidence, which is just like, oh, sure.\u003C\u002Fp>\n\u003Cp>Why not? I could do it. Like, you might have seen the thing recently where, it was, like, 11% of all men surveyed believed that they could totally land a passenger airplane if they had to. You know?\u003C\u002Fp>\n\u003Cp>Speaker 2: They could.\u003C\u002Fp>\n\u003Cp>Speaker 0: They probably could. Right. Or, like, yeah. I could score a point against Serena Williams. So there's that there's overconfidence, and then I think there's a healthy amount of just a little bit more confidence than you actually maybe know you can back up.\u003C\u002Fp>\n\u003Cp>And so that, for me, has been helpful of I I used the example planning on-site. That's something I'm involved in right now. And am I do I have a track record of doing that before or having to do that? Like, little aspects maybe, not not to the size of of the one that I'm helping with right now. But I raised my hand and said yes, because this is gonna give me a chance to collaborate with some other people on bringing this thing together.\u003C\u002Fp>\n\u003Cp>It is fun to do some of this work to ensure that on those days, it is a valuable, engaging, worthwhile, fun experience for everybody. And even selfishly as part of my career, now there are more people, assuming I do a good job with this, who have seen me do a good job at this and have, gotten to see that I care and and putting some energy and bringing some ideas towards this thing. And whenever you have a chance to do that, that is, that's part of it. Being offered a leadership position, which is really what a lot of, you know, a lot of our our fortunes in life are dependent on what we do, and a lot of them are depending are dependent on what other people decide to do. And especially when we're talking about careers and jobs, almost always, it is someone else who is going to make a decision to hire you, to promote you, and they have to base that on something.\u003C\u002Fp>\n\u003Cp>And when you have been seen to be an empathetic, you know, knowledgeable, skilled person in all these various ways that go beyond just the code that you're writing, the systems that you can understand and talk about. All of those feed into the possibility of you getting to whatever level it is that you want to advance to.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. That's that's great advice. So speaking about promoting yourself, where else, you know, can the listeners go find you after this? You know, they've seen this video. They've heard this conversation.\u003C\u002Fp>\n\u003Cp>Where else can they find your you know, follow your journey and, and learn more about you? This is a great time to mention that the\u003C\u002Fp>\n\u003Cp>Speaker 0: that this is something I don't do a good job at yet. But so I have a I have a website, which as many of us, gets updated whenever I'm looking for a new job. And I've had a few updates since then. But, otherwise, I am not widely active online right now. Most of my time right now is spent, on my job, spending time with my wife and my dogs.\u003C\u002Fp>\n\u003Cp>I'm remodeling my office. So it's in sort of a lull of of personal promotion and and visibility. But there there are a few things. I'm on LinkedIn. You can find me at Simmer there.\u003C\u002Fp>\n\u003Cp>And so if you're interested in reaching out or talking more, you can send me a message there. Otherwise, my website, Simmer dot 0 0. When I first came across that domain extension, it was like, sweet. My portfolio site, with that, people are gonna have to say, oh.\u003C\u002Fp>\n\u003Cp>Speaker 1: Of course,\u003C\u002Fp>\n\u003Cp>Speaker 0: OO probably means out of office, which is also you know? Yeah. Yeah. So\u003C\u002Fp>\n\u003Cp>Speaker 2: That's where you can find your nature photography.\u003C\u002Fp>\n\u003Cp>Speaker 1: You're out of options.\u003C\u002Fp>\n\u003Cp>Speaker 0: O o o. That's good. Yeah. Yeah. Yeah.\u003C\u002Fp>\n\u003Cp>So I've got some of my past work there. And when I get to writing, which has been a goal of mine for a while, then it'll start popping up there. Amazing.\u003C\u002Fp>\n\u003Cp>Speaker 1: Awesome. Well, yeah, I I think, as I as I say with most of our guests, I definitely learned a lot. I think it's always it's always cool to hear the common threads that everybody has, but also each individual story. Everyone has their own individual story. So it's it's definitely it was really cool to hear your journey.\u003C\u002Fp>\n\u003Cp>What I would say is for everyone listening, start go and follow him so that he has to start writing some more, because there were some great there were some great advice. Absolutely. And, I I you know, I I always listen to these podcasts back, but I might actually listen back and take some actual notes because it was it was a good podcast. But we greatly appreciate your time. Start your writing up again so when people follow you, they can actually follow current events.\u003C\u002Fp>\n\u003Cp>Update your out of office website, and thank you so much for your time. Any any parting words that you wanna say? Any last pieces of advice?\u003C\u002Fp>\n\u003Cp>Speaker 0: I I really appreciate you 2 having me on here, and I think just say yes to things when you when you have the ability is is my advice. That's that's why I'm literally here talking to you as well right now. So yeah. Yeah. Say yes.\u003C\u002Fp>\n\u003Cp>Have empathy.\u003C\u002Fp>","Remember the first time I, like, normalized a relational database. There was this clouds parting, feeling of of, like, I'm smart and I got this thing, and it feels so good to to see the elegance of it. Hey, everyone. Welcome to another episode of Trace Talks. We've been graced with the presence of David today. John and Pedro here as well. Don't don't worry. We're still here for this conversation as well. But, David, I'd love for you to give yourself a brief introduction. Let the world know who you are. Just brief because we're gonna definitely dive into your background. So but let the world know who you are. You got it. I am employed right now as a senior engineer at Netflix. I'm in the consumer foundations team, but I spent, nearly the first 4 years there in the engineering. And outside that, I have a list of hobbies that's about 5 miles long. Amazing. Yeah. We'll we'll definitely wanna get to that. But maybe at first, like, you know, what could you take us back to the the moment that kinda ignited your passion for, you know, blending technology and and design and, you know, kind of what got you into technology and into the space in the first place? For sure. For me, it was a student job in college. I got a job in the AV department, and it turned out to also be the IT department, which tells you something about when that was and how small things were at the time. And what got me into that was as a student at that college attempting to use the online scheduling tool that they'd built to just figure out what my class schedule is gonna be for for the term. And, the two things I noticed, most strongly about it were that it was ugly and hard to use. And I started asking, could someone change this? And then that question turned into, can I change this? And one of the great fortunate circumstances of my life was that the person in charge said, sure. Here's something you can change, and that was really the start. And and those two things, like, ugly and hard to use are really the the the converse, which is beautiful and nice to use is just continues to be a motivation. Amazing. Yeah. I was I was gonna I was gonna make a joke that half the technology out there today still is ugly and hard to use. So you saw you saw that problem 20 something years ago, which is pretty awesome. Did you have any technical experience before that or maybe just tinkering on a a computer every now and then? Or or you just saw something ugly and you wanted to fix it? Only a little bit. I had taken a rudimentary Photoshop course at Kinko's, before I owned a computer. And then, you know, just really basic stuff. I think a friend had had me over and been like, this is email, and here's how to get on AOL. But, other than that, a little bit of tinkering with, like, a hand me down DOS computer and a little bit of work on trail. That was it. So it really was just the motivation of, I don't know how to do it, but I would like to learn, and I have some, you know, I have even though I don't know anything yet, I have some ideas about how that maybe this could be better, and that was the start. Mhmm. Got it. Did have you since taken any sort of formal sort of design or UX UI, dove into that space? Not not in the terms of, like, higher education or degree programs. So I I went to school originally to be a chiropractor. I wish I'd had better career advice or maybe had had changed tech earlier, but, I I have a, like, science and biology education, in undergrad, and all of my tech learning was, you know, the sir the term self taught is usually applied, but, this is something I mentioned a lot. I am taught by all the thousands of other people in this industry who love and are excited about what they do and who are delighted to write articles and make online courses and, produce demos showing how to do things with various technology. And so I have always loved, like, the design aspect. My mom, had an art background, and so I was homeschooled as a kid, and so she made sure to incorporate that. And I always I always really enjoyed that part. But the actual melding of design and tech, that was just this exciting new field that there was, you know, there were so many resources out there, to consume that that's really where that came about. What what was the because I also got into technology kind of probably around the same time frame, and I was in school for design. Sure. But I remember back then, there wasn't there wasn't as many forums or definitely wasn't as many videos or boot camps, all those stuff that's out there today. What did you rely on? You know, you you took on you were going to school for chiropractory and science. I don't think that's even a word, chiropractory. I like it. But you were going to school but you're going to school for something different, and you took on this tech project. How did you learn to build I looked at your LinkedIn, and you built a CMS. You built everything that was needed. How did you find how how to do that back then? You know? Because I had people in person in school. I was going to school for that, but there wasn't forums. You know? Were you in communities in person asking these questions, or did you find some online forums to ask questions? It was there was a little bit. I remember, the, Jeffrey Zeltman's resource was a huge, or, like, his both his personal writing and then a list of part, which is a heavily CSS and, like, web standard sort of resource. That was one of my main go to's. And from there, that is really how I think I I you know, you could say I got my UI design education by their emphasis on not just, hey. This is beautiful and here are principal's design. I mean, they mentioned those things, and, clearly, a a lot of the people in the community cared about that kind of stuff. But the, the larger point of so much of their writing and their tutorials and their excitement about, like, CSS becoming the main way to style things and and and the accessibility benefits of that versus the old clutches we were using to try to get things layout on screen that totally ignored the concerns of screen readers or assistive devices or anything else was this, it needs to be nice for humans to use. And it being beautiful is sometimes a companion, sometimes as a side effect to that, but that the primary thing is here's how to make it nice for him to use. And, also, by the way, here's how to have it kind of be more pleasant as a developer. You get that that mental satisfaction of having done this in a way that is elegant. Again, that that elegance, you know, can turn into cleverness and be awful in a lot of ways, but that was always a side effect of doing building things in a way that are pleasant for humans to use. Yeah. And that and that makes sense. I think that was that's sort of, like, the turning point for technology become more widely adopted, right, with the PC and, kind of personal computing. And still companies today are pushing that forward. Like, a company that could take something very technical and and and just make it user friendly and beautiful as I I don't know. The the example I always think of is, like, Square. Right? Like, there have been payment processors and credit card terminals and all these things, but they made a very simple, smart little thing that had a beautiful, simple design and Yeah. Captured a ton of market share by just doing that, like, leading with design. Yeah. It felt like the future instead of this awkward thing that beeps ain't really at you. It was just nice and yeah. Makes sense. Yeah. But there I I think all the time about, like, this sort of if you build it, they will come aspect of, like, you can kind of fall into that trap as a designer engineer where you just think, if I just build the beautiful thing, my marketing and business viability and all those other problems will solve themselves, which, of course, is your however, like, that do build something beautiful is still something worth hanging on. Yeah. You've that's a good point to make. You know, you've you see a problem. You make it beautiful. You make it usable, and and people will gravitate towards it naturally. Nice analogy, Pedro. I use Square myself. So it definitely I've I've found the the simplicity as well as ease of use, important. You've been on this long journey, not long, but you've been on this, wide ranging journey of different types of roles. I mean, even starting from chiropractor school to doing a c a CMS, and now you're at Netflix. And there's been a long journey along the way. What do you look for when you're looking for new projects or new roles, whether it's internal where you're at or, external companies? You know? What do you gravitate towards? Do you gravitate towards somewhere where you know you can fix problems, or do you gravitate towards somewhere where you know you can grow as an employee and as as a engineer designer? You know, what I guess, what ranks importance for you when you're looking for new projects and roles? Having, like, a baked in change of scenery or having having a change of scenery every so often has always been really important to me. I had, for a while, when I in one of my first roles, I was working for a small company. I probably stayed there longer than I should have. I had a bunch of friends, who were in agencies, and they had this sort of mental turnover of new clients, new projects that kept them interested in tackling new things. And I really started to desire that. And so that, for me, has been a motivator in finding new things. In fact, it was and being the reason I left that company and set out to freelance for a while. I, rely on money to pay for the things that I enjoy and and, need to live in life. So that's also a motivator. But, really, you know, that that aside, I think there is a blend of the 2. So when I found the role I was hired into at Netflix when I read that job description, it described it used all of the words that I was looking for in terms of the the mission of the role and what I'd be working with and the variety that was baked into it. And so that was one of the exciting things. But I think what, also really drew me to that was that it was on a team of people who had a wide variety of skills, but where my skills weren't sort of fully representative, weren't already duplicated by somebody else. And so it was gonna be that chance to to bring the things I knew that I was good at, and to to add them there. I I didn't come to it that role with the feeling of, though, that, like, I got this. I'll show them, you know, what's going on. It was it was very much the opposite of, okay. This is the role that sounds awesome, but, of course, it's Netflix. And, you know, I'm not ready. Maybe they'll give me more time to prepare. And, of course, you know, the interview, you know, was like, can you come talk next week? And so I, you know, I I put a lot of effort into it, but it was that, that combination of this is, this is an amazing opportunity. It's exactly what I wanna do that I think really spurred, you know, really putting a lot of focus and effort into into that interview process there. Yeah. And it and it seems like throughout your career, you've kind of transitioned between different roles and different industries. And now at Netflix, like, what what experience do you think was the most important or the most pivotal in shaping your approach to leadership that that you're sort of in it in that position now? Sure. There's been a few things. I I think the the progression I've been I've made in my career, the opportunities that I've had have been, thanks to other people who kind of already understood it. One of the people I think of a lot is is a old boss of mine, Gunther, at that first company, who in our one on ones, instead of it being project recap time, he made sure that we were talking about what did I want to do in my career and what was I interested in long term beyond this company, beyond working for him. And then fast forward a few, and there was a moment where I was a more senior engineer on a relatively small team. And there was really a need, in order to make our junior engineers more effective. It wasn't going to be sort of teaching them in the sense of, hey. Here's how you do complicated things. They already had a lot of of skill at the technical things that they were taking on. Where, leadership was needed was for someone like me to do the glue work as it's, been described of, you know, a little bit of private project management, a lot of, taking care of some, you know, just sort of, like, unblocking chores for the team, being ahead of things like that, setting a a direction, you know, in cases where there was some ambiguity, providing, at least a decision and some reasoning, that we can all act on from there. Yeah. I think it was seeing examples of other people and then just kind of realizing that there's this need and this is that by serving these other people on the team, that is actually a huge part of being a leader. And, and it was satisfying to start doing that. You know? In in isolation, the idea of of just, like, project management work sometimes feels boring or a chore or something you wanna procrastinate, on. But when you are multiplying the happiness and and effectiveness of other people by doing that, then it becomes satisfying. Yeah. I that's a common thread that we've heard in a lot of these conversations that we've had is people who have worked their way up to some form of leadership. They've had good leaders, a lot of good leaders along the way. Some of them have had not great leaders, but, they all have this drive, and I I think it's ingrained in them by those good leaders. I think good leaders today have been trained by good leaders. So do you enjoy leading more now, or do you enjoy getting your hands, you know, in and doing coding and designing still, a mixture of both? Some people prefer, you know, to train people and lead them like a true leader should, but some people still miss getting in their getting their hands in the dirt and and doing development work. So do you still get a mixture of those, and do you like that combination, or do you prefer 1 or the other? Yeah. I do still get a mixture. I do still love that mixture. I I often think about this because definitely the part of me, the engineer designer who loves building things, can very much get in the zone on a feature and be very happy, you know, towing away there and making it really awesome. And what I have to remind myself of is, like, I do have a desire to be a a good leader, a good engineering leader. And I often have this feeling that and this is probably something, you know, I was gonna say, real leaders also still still feel what that that feeling of, like, well, I'm I I don't have as much experience or as much to give there yet or, you know, I'm not I want to be, but I'm not yet. And I think that that awareness isn't all bad, but also that just saying, well, I'm not a leader yet isn't necessarily helpful because I am doing things that are, leading other folks. For instance, I'm I'm frequently mentoring a junior engineer on our team right now, who is awesome, but there's so much about being a software engineer that isn't necessarily computer science. And a lot of those things, like, we are working together on, and it doesn't feel like me wagging my finger at Hertz. We're talking about these things, and I am able to provide that. And I love doing that. I think one of the things that's essential if you want to do that is to make sure that you have some time set aside to do that. If I get so attached to my feature work and building the thing and then realize, like, oh, it's 1 minute until my 1 on 1, with the person I'm mentoring. Okay. Well, I'll just ask her how things are going. Then not only have I will I probably not do as, I probably won't be as good a resource for her, but also I've short changed myself on putting having some time to think ahead about what is important here, even even if there wasn't mentorship involved. For me to spend more time thinking ahead about the strategy for the project I'm working on, where I fit in my organization, how I see roles shifting, or the importance of teams, or how a strategy doc that I read might affect our team. Those are the things that leaders do. And if you begin spending the time doing the things that leaders do, either you'll find yourself being equipped to be a leader or you'll find yourself actually being in a leadership position, because you began acting that way. And that's that's really the the approach I've been trying to take. You know, I I love getting to have a 1 on 1 with a director or, you know, like, several skip levels up. And and I'm always asking, like, how did you get here? But it's not just what did you do to get here. It's what circumstances got you here, and what do you spend your time doing. And these these roles that I, you know, possibly aspire to have one day, I'd like to be, at least able to think like a person who has that role now even when it's not my role yet. Because without that reflection and beginning to get yourself in the mindset of that different role, it's extremely unlikely someone's gonna come along and be like, great work building that thing. You're gonna direct some people now. So that's that's the mindset I've tried to take. Yeah. I think it's important also as you as people continue to to climb upward in their, you know, personal and professional trajectory to to remember because you had mentioned, feeling like you might not be qualified or feeling like you don't know everything to be in a position that you might be in. I think it's important for people to realize that nobody nobody knows everything, and what you just mentioned there still having time to reach out to your superiors to to ask those same type as a questions that you're passing that information down to people who you're training as well. I think it's important to remember growing as a human and growing as a employee and growing as just whatever you're in, whatever field you're in, it's a two way street. You know, you learn from those who have been there before, and that's kind of the reason of this podcast. And then you you pass that information down to those that you're training. So do you these days, I assume it's probably a different answer than if you'd answered this 15 years ago. Do you find more satisfaction in a junior engineer that you're training, get unstuck from a bug that they've been trying to fix or get promoted, or do you find more enjoyment in fixing a a major bug yourself? I assume it's probably seeing those that you're training, you know, get to the next stage in their development. Right. Like, it's it's tough to answer because it's still still joy in both, but I think you're right that it is the the latter because that's beyond that there's a a bigger effect that cascades far beyond that I, you know, unstuck a button that that wasn't working. Yeah. To see another person take off. And especially, like, there's there's so much joy. You know, I I had mentioned that I had been in a in one career and then switched. And in in down moments, I have phrased it as I wasted those years of my life. That's not true, of course, because the person I am now is built out of those experiences. But one of the things that I get a lot of joy out and mention explicitly to younger engineers that I see, is that I'm so stoked that they are, like, getting started now. At work and especially for those who are really good at customer empathy and being curious and, thinking about strategic things and the health of the team and and sort of the work beyond just the the work itself that they are going to go so far. And especially, you know, when I, like, I when I run into these sorts of folks at Netflix, I'm even more excited of like, for me, it feels like this amazing place to work where, I I finally got here, but after quite a long time in my career at, you know, you could say, like, smaller companies. I mean, smaller companies. It felt it felt like a a personally, like, such a huge achievement to make this this prospective company. And, I am so stoked to see young engineers getting jobs at, like, awesome companies and getting to start learning from amazing people way earlier than I did. It's it's so cool to see that, and I love being a part of that. Yeah. Stoked. I haven't heard that word in a while, so I I I appreciate the usage of that word. I mean, if you also, if you train people who are you, you know, who are your ment mentees, they'll do a lot of the work that then will possibly or potentially even enable you to then get more hands on in the code and and start fixing things because they're they're being smart enough and self sufficient enough to solve some problems that maybe a leader who hasn't trained their employees, would have to solve all of those problems. So I guess in a in a way, it probably helps you actually get back into coding and fixing bugs if you're training people to do some of that forward thinking leadership type stuff. Totally. And, you know, there's so much that I still don't know or things where, you know, if I was just working by myself, I'd be looking up. Of course, the the great secret of of senior engineers is that we're still googling everything. And to do that in front of a junior engineer, I think, is a really helpful experience too to say I don't know. Or, you know, I've used this 4 times, or I configure this thing once a year when I set up a new whatever it is. And I don't remember what I was doing, so let's pull up in the documentation together and walk through it, and, you know, we'll we'll figure out how to do this again. Or that thing you're you're dealing with could be this, this, and this. It's not those. Okay. I don't have a ready answer off the top of my head, but you do a little more investigation. I will as well. Let's talk again in a couple hours and see if we've gotten anywhere. And that's that's what being an engineer is all the time, and, it is freeing to, be able to admit that. And, also, like, that would be a a chance for my own growth and my own either remembering how to do the technical task or, in the larger sense, just reminding myself that there's, there's I'm I there's so much I don't know. There's so much I'm gonna forget to remember and just it's I think it's so valuable for anyone who hasn't figured that out yet to get a glimpse into. That's just always that's gonna be how it is throughout your career. You don't have to feel bad about not knowing right away because 20 years from now, you still won't know anything right away. Yeah. The other thing that's interesting that you mentioned is empathy because it's it's something that we don't typically think about as a, you know, an engineering skill. Right? You think about the technical skills associated. But would you say that empathy and ABC's some of these other skills are, just as important or maybe more important as engineers are making their way, you know, up through the ranks or, you know, in different roles, within organizations? Yeah. Absolutely. Because there's so much, like, delightfulness and fun in embracing and understanding a complex thing. I remember the first time I, like, normalized a relational database. There was this clouds parting, feeling of of, like, I'm smart, and I got this thing, and it feels so good to to see the elegance of it. So there's plenty of pleasure there. But, I think once you get out of just building something for its own sake or learning a technology or or kind of seeing how clever you can get with your understanding of a knowledge of a language that you know well, Ultimately, you're going to be engineering things, designing things for someone else to use. You may be several layers removed from, let's say, a customer outside your company. In my case, you know, productivity engineering, your your customers are other engineers inside the company. And so still that, that aspect of empathy is necessary for a couple things. 1 is just that when you have gotten a request for something to be built, something to be changed, You could just take it exactly as it's written and knock out that thing and feel like you check the box and move the Jira store over to the done column. But to be good at what you do, the result should really not just be only the thing you were told to do. It should accomplish the purpose of what are we trying to do here. Are we trying to make this simpler for a user? Are we trying to actually make the experience a little higher friction to avoid someone mistakenly deleting something important? There's so many of those ways of thinking about things that that you're designing and engineering. Were to produce the highest quality version of the thing that you were assigned, you need to think through the eyes of the customer, probably the business, you know, understand all the needs that go into that. But then I think in addition to just producing the best version of the thing you could, I think empathy gives you curiosity and excitement and delight and the interest in taking on or suggesting changes that weren't already thought of, that weren't, identified. You can be the person who identifies those things. When you hear the frustration that someone has of, like, like, I have to click this 5 times, and then sometimes that this last step doesn't work. One way it's you could view that is, like, okay. Well, the problem is, like, the the bug is that it breaks at that 5th step depending on a certain scenario. So you could fix that, but, also, is there I have to click 5 things to do this at once? Like, that customer just gave you some very valuable insight into what they're thinking or kind of what they want or what annoys them. And if you're not attuned to catching those kind of things, you might not ever take the time to say, can we rethink how this interaction works so that it's one click? It's it's none. We, you know, make this whole thing more pleasant for the user. We don't just fix the bug that was described. We listened, and we might be able to invent something that really makes this whole thing more pleasant. Yeah. I think I think you're so right. Yeah. Like, that was I think you could have been a chiropractor. Someone says my back hurts, and we kinda find out why their back is actually hurting. Yeah. I think you're so right, though. Sometimes we're so we're so involved, and we're so deep and close to something that we can't zoom out and see the bigger picture, and it does take empathy to do that. I mean, you know, John and I can speak for ourselves, but we're in conversations with folks all the time that are just like, oh, I need this software to check these four boxes. Like, well, hold on. Let's back up and, like, how does this impact you, the organization, the people you work with? Like, what's the actual business outcome, the personal outcome? And I think, engineers thinking that way is is definitely an interesting way to look at it because you can you can actually solve the core problem. Sometimes they're just like, oh, it doesn't like you said, it doesn't work at the 5th step. Well, maybe the whole process is the problem, and it could be reinvented. So that's that's really interesting. Yeah. Yeah. So what you know, all of working your way up to leadership isn't, roses and daisies. So what kind of, you know, part of this podcast is kind of passing on this advice and and your experiences to people who are listening. So what have been some of the struggles that you you faced? Is it, you know, having to deal with meetings, leadership meetings? I I assume those are I don't know how they are at Netflix, but in in some places, they're not fun. They're just kind of monotonous. But what have been some of the struggles that you've faced as you've worked your way up and and some things that you would say, hey, 15 year younger myself, be aware of this and or do this. Yeah. The first things come to mind are kind of personal habits of, like, giving myself a little too much room to procrastinate, not being as frequent a communicator, an aversion to maintenance. Like, I I was thinking about this recently that I think I have this inbuilt feeling that whenever I, like, buy a thing, it should just continue working forever. And, of course, you have to, like, change oil and dust and clean and, you know, all those may maintenance things. And, I think in similar ways, times where I've done you know, I I was given a chance to lead a thing and I didn't do a great job at it, Oftentimes came down to that of a lack of personal organization. You know, I didn't kind of put in some important work ahead of time, and so I had to scramble to get a result at the end. And sometimes that works out, and sometimes it doesn't. And, in making yourself, you know, not making yourself visible enough or not being a communicator, not chiming in on meetings, I think there's a lot of a lot of times where had I been more aware that just that involvement, which takes work and energy to get yourself to, like, be there with your camera on and and, you know, to really to really be involved and not just, I saw a cartoon someone was like, how to how to, you know, make it appear that you've been contributing me to a meeting is to just say, thanks, everybody, at the end when you haven't said anything to the meeting. I mean, I so I've done that for a while, but I think I think avoiding those habits that require less energy, less investment, and just kind of, working on that is something I wish I'd started earlier. I heard I heard someone say that they've, it was another engineer at Netflix, It's Molly Stroup. She said, I realized that I wouldn't be an effective leader until I was an effective leader from in my own life, and that's really stuck with me of you just your personal inclination to wash the dishes and wipe down the kitchen counter when you'd rather just leave it for somebody else or not do it at all. I think that's that is a great little microcosm of, the kind of things you need to be, just willing to put the time into if you want to be a leader. And, you know, I mentioned I I mentioned, like, being on camera is an offhand thing, and there's plenty of reasons why in meetings. Like, I I'm glad our shift towards remote work and getting used to doing things over Zoom and the reasons why someone might be on camera. I'm not trying to denigrate any of those. But, in the the sort of being if if leadership is something you want to grow into, that putting this extra work in to provide energy and provide involvement to other people is really part of it. Just if you are there with contributions and and the feeling of, like, this is useful to be here and I am bringing my bringing my whole self here. Other people pick up on that, and that I think is is, super valuable. So I wish I'd I wish I'd focused on being that kind of person sooner. Yeah. Yeah. That reminds me, kind of on the opposite spectrum. I I used to work at Dell, and there's a guy who had been in the same position for, I don't know, 15 years, 20 years, something like that. And I asked him one day. His name is Joe. I'll I'll call out someone's name too, Joe O'Brien. I was like, Joe, you've don't you ever wanna, you know, climb up the ranks and be a manager or be a regional manager? He's and he said something he said, I'm really happy with my life, and I really like where I'm at, and so I'm comfortable here. And so I think on the other end of the spectrum, it's and this applies to leadership as well. You kinda have to understand what your personal goals are. Don't feel like you have to climb to leadership because someone else thinks you should climb to leadership. But if it's something that you feel like you want to do, then start to put those habits into your life. And so that that's some really good advice that you actually just gave there, which is be a proponent for yourself. Even if you're not ready, start putting yourself in that position to have those opportunities because you never know when you're gonna learn something, just because you're present. So that's good advice. So, yeah, you know, what speaking of tech, you've seen, you know, both you and I came sounds like we came into tech around the same time frame. So there's been a lot that's changed over, you know, 15, 20 years, and a lot has changed in the last year or 2. You know, you start talking about AI and all that kind of stuff. So what's your vision for the future? Are you always one of those on the forefront of technology? I think Pedro bought the r one Rabbit, which is a brand new AI device. But do you always try and embrace new technology? Do you kind of skirt the outsides to see what other people are doing with it? I think I'm a little more of a scooter. I I like keeping up with things, and I always have the the thought whenever I've seen I think, like, we all have maybe a a character, like, you know, the sort of person to which someone would say, okay, boomer. You know? The the idea of someone who's so fixed in their ways, they that all modern music is shit and, kids these days and nobody wants to work and all those kind of things you hear. Like, how did you become that kind of person? You know, we've had that kind of person throughout every generation, and I don't wanna be that person. And so, similarly, in technology, you know, I have my formative era of what I got good at and what I was excited about, and there are certain branches off of those things that I spent most of my time on that I continue to be excited about innovations in. And, of course, no one has time to sort of be fully on top of every new innovation that's out there. So I often have a feeling that I should be more on top of things, but also, like, keeping an openness and and an excitement about how other people are using things, I think, is is still important to have. I could do more of it, but at least, I think I'm maintaining the idea of of openness. I I am definitely surrounded by people who do a great job of more quickly embracing and tinkering with something new. And so, you know, I'm sort of maybe getting a secondhand keeping up by by keeping up with them. Yeah. Are there are are there any of these technologies that you're you've seen make an impact at, you know, at Netflix or any other companies recently? Yeah. I think the generative AI one is interesting for, like, specifically in writing code. I I gave a talk about this, a little over a year ago in which I expressed a lot of, if not skepticism, at least in encouraging people to pump their brakes on embracing it too quickly. Because, for instance, with writing code, you can only trust the output of an LLM when you can actually verify it. And so paradoxically, in order to make use of it, you have to kind of be able to do it on your own. But, also, there are time savings to be had. There are so many sort of, like, we still bump into boilerplate all the time, and to have something where you can, automate for yourself the repetitive things, you know, the the why should I be typing the same syntax over and over again perhaps when there's some way I can automate the snippet or the generation of it or say, hey. Write me a unit test for this thing. And to incorporate some of that without fully relying on it, I think, is something that I need to get better at and is a good skill to have. Yeah. That one is, I think, like, dominating the airways right now, and I think that the people who will make the most of it, maintain sort of, like, a little bit of a healthy distance. Like, they use it for what it is, but don't see it as, true artificial intelligence yet or or replacing their job. I think the other thing that, that I have isn't necessarily the future, maybe it's sort of standard now, but the idea of design tools that have less of a barrier to entry. So I am a big Figma user. Before that, it was InVision and Sketch and and those. And and, of course, like, I grew up with Photoshop and Adobe Suite. And, you know, spending every day in Photoshop, I got used to it and I can move around quickly, but it's difficult to learn that kind of thing. And the way design tools have shifted to you don't need to have spent 20 hours in this to learn how to do it. Everyone can participate in from lo fi to hi fi, getting their ideas in a visual form and building from there. I think that in all its various forms has been incredibly valuable, and I am glad to see that continuing to happen. There's so much engineering and design work within companies, even Netflix, where a lo fi, low code kind of way to do something is the really smart way to do it. And, I think that the more people who are encouraged to think of themselves as doing design work, if not necessarily being designers, is a really healthy thing. Yeah. I can tell you from firsthand experience, like, I used Sketch actually to design my first startups, like MVP app. Right? And I just kinda wireframe the whole thing out and gave it to the engineers, and we're like, make this happen. This looks good. And there were some elements of that design, like, found in some kinda, you know, kit I downloaded, like, some UI elements that I had pieced together and changed around a little bit. But you're right. It was and that allowed us to launch our app in this, like, you know, it wasn't completely no code, but, definitely low code in in the sense of me designing the front end and just handing it to the engineers to to make happen. So it definitely accelerated our development and put a product out faster. So, yeah, I think it's super important. And there's still a need for pros who are great at design, and and that won't go away. But the, lowering the barrier to entry, I think, has been amazing. Tools that continue to lower the barrier to entry to producing something, to getting the satisfaction of the thing I built I can see on screen, is great. Yeah. That's a great point. I also learned Photoshop and Illustrator, and I can still use those today. But for example, I'm actually I'm not gonna show it off in camera, but I'm wearing a shirt that I used AI to create 2 images, front and back. Took me 30 seconds when that would have taken me an illustrator, you know, 40 minutes, if if not more. But like you said, when you talk about programming and using AI for programming, you still have to know what you're doing because AI is not even anywhere to the point where people claim it can be. So it's smart to have that sort of healthy skepticism both with that type of a trend like AI, but I think in general, I think skirting the out or or, you know, skirting the edges of anything that you're approaching, whether you're thinking about being a leader or you're thinking about doing x or y. You know? Do your research, understand the pros and cons of anything, and find where it can help, lower the barrier. I think that was another great phrase that you used because, you know, I can have AI draft the first draft, and then I go and clean it up. Because robots, I probably shouldn't say this on recording, but robots will never be as great as humans. So, it's but it's yeah. I think it's that was a great term that you used, lowering the barrier. Yeah. Yeah. So so, you know, you've definitely gone on a a long journey. I think there's been some great advice. I think having, having empathy and, you know, leading on a two way road has been some great advice. So, you know, I guess for those who are listening who either don't know if they wanna be a leader yet or those who do know that they wanna keep climbing the ranks, what's some advice you gave advice for your younger self, but what would be some advice just in general that you would give to people who are out there looking to climb the ranks? Yeah. I would say raise your hand, when opportunities arise for anything. Volunteer to help set up your next team on-site. There's an extra project that would, you know, clean up some old data, you know, not to not to take on, so much work that you burn out or that you're sacrificing other parts of your life. But within, your ability to do so, you know, say yes to to things. Those, those opportunities that you get, are not just about the task that you might do. They are about your they provide you this chance to work with other people that you might not have already, to be seen by other people you might not have already. And one of the things that really, some of my close friends had to drill into me, at some point in my career was promote yourself of of, like, don't be so humble and self aware that you don't ever say something nice about the things you're proud of, or that you, hesitate to, say, yes. I can take care of that. And I think even beyond that, you know, my wife, will frequently point out, what she used phrases, my white boy confidence, which is just like, oh, sure. Why not? I could do it. Like, you might have seen the thing recently where, it was, like, 11% of all men surveyed believed that they could totally land a passenger airplane if they had to. You know? They could. They probably could. Right. Or, like, yeah. I could score a point against Serena Williams. So there's that there's overconfidence, and then I think there's a healthy amount of just a little bit more confidence than you actually maybe know you can back up. And so that, for me, has been helpful of I I used the example planning on-site. That's something I'm involved in right now. And am I do I have a track record of doing that before or having to do that? Like, little aspects maybe, not not to the size of of the one that I'm helping with right now. But I raised my hand and said yes, because this is gonna give me a chance to collaborate with some other people on bringing this thing together. It is fun to do some of this work to ensure that on those days, it is a valuable, engaging, worthwhile, fun experience for everybody. And even selfishly as part of my career, now there are more people, assuming I do a good job with this, who have seen me do a good job at this and have, gotten to see that I care and and putting some energy and bringing some ideas towards this thing. And whenever you have a chance to do that, that is, that's part of it. Being offered a leadership position, which is really what a lot of, you know, a lot of our our fortunes in life are dependent on what we do, and a lot of them are depending are dependent on what other people decide to do. And especially when we're talking about careers and jobs, almost always, it is someone else who is going to make a decision to hire you, to promote you, and they have to base that on something. And when you have been seen to be an empathetic, you know, knowledgeable, skilled person in all these various ways that go beyond just the code that you're writing, the systems that you can understand and talk about. All of those feed into the possibility of you getting to whatever level it is that you want to advance to. Yeah. That's that's great advice. So speaking about promoting yourself, where else, you know, can the listeners go find you after this? You know, they've seen this video. They've heard this conversation. Where else can they find your you know, follow your journey and, and learn more about you? This is a great time to mention that the that this is something I don't do a good job at yet. But so I have a I have a website, which as many of us, gets updated whenever I'm looking for a new job. And I've had a few updates since then. But, otherwise, I am not widely active online right now. Most of my time right now is spent, on my job, spending time with my wife and my dogs. I'm remodeling my office. So it's in sort of a lull of of personal promotion and and visibility. But there there are a few things. I'm on LinkedIn. You can find me at Simmer there. And so if you're interested in reaching out or talking more, you can send me a message there. Otherwise, my website, Simmer dot 0 0. When I first came across that domain extension, it was like, sweet. My portfolio site, with that, people are gonna have to say, oh. Of course, OO probably means out of office, which is also you know? Yeah. Yeah. So That's where you can find your nature photography. You're out of options. O o o. That's good. Yeah. Yeah. Yeah. So I've got some of my past work there. And when I get to writing, which has been a goal of mine for a while, then it'll start popping up there. Amazing. Awesome. Well, yeah, I I think, as I as I say with most of our guests, I definitely learned a lot. I think it's always it's always cool to hear the common threads that everybody has, but also each individual story. Everyone has their own individual story. So it's it's definitely it was really cool to hear your journey. What I would say is for everyone listening, start go and follow him so that he has to start writing some more, because there were some great there were some great advice. Absolutely. And, I I you know, I I always listen to these podcasts back, but I might actually listen back and take some actual notes because it was it was a good podcast. But we greatly appreciate your time. Start your writing up again so when people follow you, they can actually follow current events. Update your out of office website, and thank you so much for your time. Any any parting words that you wanna say? Any last pieces of advice? I I really appreciate you 2 having me on here, and I think just say yes to things when you when you have the ability is is my advice. That's that's why I'm literally here talking to you as well right now. So yeah. Yeah. Say yes. Have empathy.","c8610be2-7db5-44e7-8a51-89078647ecb3",[2790,2791,2792],"c023b9c9-82cf-4d7f-b405-586ae9b6e5d6","045d933f-b70f-4abb-8f3a-2d2b146b1519","6b32879b-7c61-4e65-8090-17610f59028f",[],{"year":978,"number":1830,"id":1842,"episodes":2795,"show":2796},[1844,1845,1846,1847,1825],{"title":1849,"slug":1850},{"id":2710,"slug":2798,"vimeo_id":2799,"description":2800,"tile":2801,"length":740,"resources":12,"people":12,"episode_number":177,"published":2802,"title":2803,"video_transcript_html":2804,"video_transcript_text":2805,"content":12,"seo":2806,"status":19,"episode_people":2807,"recommendations":2809,"season":2810},"meteorite-landings","950737408","NASA publishes a dataset of over 34,000 meteorite landings as recorded by The Meteoritical Society.  Kevin shows you how Directus can be used to further explore, understand, and remix this dataset both in the Data Studio and via API.\n","23ca152b-37f6-4cfe-97b9-7c2a2af2f4fa","2024-06-05","Meteorite Landings","\u003Cp>Speaker 0: In this show, we give new life to open datasets with the help of directors. Join me as we explore, analyze, and generate APIs to improve access to and democratize data. Before we get started, if you have or know of an interesting open dataset and want us to feature it, just reach out to our team and it may well feature in a future episode. Today's dataset was published by NASA and contains data on over 34,000 meteorites from the meteoritical society. It contains information about their classification, where and when they were observed, and whether they fell or were found.\u003C\u002Fp>\u003Cp>Now just a quick note before we continue, I later, after setting everything up, learnt about the difference between a meteor and a meteorite. Basically, it's a meteor when it's in the air, and when it survives its journey down to Earth and it lands, it becomes a meteorite. This is a dataset of meteorites, but I'm going to, for ease, just refer to them as meteors throughout. I just I want you to know that I know the difference now and so do you. So these are meteorites.\u003C\u002Fp>\u003Cp>Right? But we'll refer to them as meteors from here. Now there's only a couple of very small processing steps that I made to this data before I imported it in Directus. Firstly, I converted coordinates to GeoJSON objects, which allows us to use all of the mapping features inside of Directus. Secondly, there was one data point in here which was incorrectly entered.\u003C\u002Fp>\u003Cp>It said that a a meteor fell at, what was the year? 2101, and it was 2010. I googled it, and I fixed it in the dataset because it was ruining all of the graphs. So just to note there that there was a small error in the data that I fixed, and I changed the format of just the mapping and location data so it works following the GeoJSON standard. Now before we jump in and see what we can now do with this data, just to note that this specific dataset just stops in 2013.\u003C\u002Fp>\u003Cp>And as we can see here in the web page, there is a new dataset on a new URL, but that URL doesn't resolve. So I just use this dataset. Just know that it stops in 2013, but I'm sure it could be updated if you were to follow along at home. This is the director's data studio. It's a web application that lets you explore, analyze, and work with data in a database.\u003C\u002Fp>\u003Cp>And as you can see, all 38,000 items from that dataset have been imported. Now straight away, we have access to everything Directus offers. So we can filter these items perhaps by the year that they they were discovered or whether they fell or were found, stuff like that. We can step in to a single item like this one here and see a bit more data around it. Here we can see the weight of it or the mass of it rather, when it was found.\u003C\u002Fp>\u003Cp>And just to be clear, this is a date field, but the dataset only has the year. So the year is the only thing that actually matters here. Whether or not it was, it fell or it was found, it's class, and so on. And because we encoded the coordinates as a GeoJSON point, we know the exact point that this coordinate resolves to. So we can zoom out there and see that on a map.\u003C\u002Fp>\u003Cp>Now this is a pretty standard way to look at data. It's a table, but directors does also have other layouts including a map. And as these data points are encoded, as GeoJSON, as I mentioned, we now get to use the map layout, which I think is super interesting. So you can see a concentration here perhaps, of data that is stored in this dataset. So I just think that's really fascinating.\u003C\u002Fp>\u003Cp>There's obviously a lot more we can do with this data inside of directors, but what I wanna draw your attention to is the insights module. So this is an insights dashboard for this dataset here that I prepared earlier, and it it just allows us to analyse this data. So I've added a few panels here. Firstly, I've added this start year and end year input so we can change this. You know, we can change this and all of these other fields are hooked into these two values.\u003C\u002Fp>\u003Cp>To show you how that works, inside of this panel, we add a filter and we say the year, must be greater than or equal to this 1st year and less than or equal to the end year. You can apply other filters, of course, in here manually, like hard coded or using dynamic values from text boxes. So in here, we also see, you know, this, this graph that I've put in showing all of the meteors in the dataset for these given years. The average size in the time frame. So we see that there was a kind of peak in size, I suppose, of of, meteors in 2004 and back up in 2013.\u003C\u002Fp>\u003Cp>I don't know if that means anything, but you can discover it, I suppose. You can also look at, you know, whether found or fell. So we see there was just 1, 1 meteor in this dataset that fell and was observed to have fallen versus having later been found. The classes, of those meteors so we see in this time frame here, a majority of meteors were l six class. And here is a list of meteors in this criteria here.\u003C\u002Fp>\u003Cp>They're just alphabetized, so we can step into those and start to look in a little more detail. So as you saw, we can use the director's data studio to explore and query data using the web application. But every director's project also comes with a set of auto generated APIs to do the same thing. And this means that developers can not only interact with that data using APIs, but can build applications on top of those APIs to increase its usage, remix it, and encourage citizen participation. Now these APIs can also be used to create or update data, and they can be locked down in a number of ways.\u003C\u002Fp>\u003Cp>So they can be public, but they might also require registration or specific permissions. Now I've prepared some requests to show you so you can get an idea of how querying data works using the REST API. But, of course, there are API endpoints as well for updating or creating new data as well. Let's jump into postman and see how it works. This end point will fetch all items from the meteors collection.\u003C\u002Fp>\u003Cp>It will return an array of objects where each object represents 1 meteor. Now by default, this will return a 100 items and you can either paginate or change the size of the response or both. And realistically, you'll probably want to do both. And just a reminder that you can also lock down which fields are actually returned by directors to a public user or indeed any user with any role. So here we see the ID, the name, the class, the location encoded as a GeoJSON object, and the mass in grams.\u003C\u002Fp>\u003Cp>Now we can also tack on any number of director's query parameters to change the data that comes back. So this example here has 2 query parameters. The first one as shown here and here will only return items with a mass that is greater than or equal to a 1,000. That's grams. So that's 1 kilo.\u003C\u002Fp>\u003Cp>So it will never return items that are less than a 1,000 in this field. It will also sort the returned items starting with the biggest. So this is the biggest item on record found in 1920, and the next biggest was found in 18\u002F18, and so on and so forth. So you can add any number of these filters, sorts, query parameters on, and you can add several, you can add several filters at the same time as well. Now this final example isn't about getting data itself, but it's about using our aggregation and group by query parameters to start to analyze the data.\u003C\u002Fp>\u003Cp>And this is more or less how the director's insights module actually generates data in the panels. So here we're saying, get every item and group it by the year. So for every year in this data set, we get to see how many meteors exist. So there's 1 in this year, 1 in this year, and so on. But down in let's find an example here.\u003C\u002Fp>\u003Cp>In 17, 68, there are 2 meteors in the dataset and so on and so forth. These are really powerful. This is obviously quite a straightforward example, but you can use this in many, many ways. There are a whole set of query parameters you can use, not just aggregation and grouping. We saw filter.\u003C\u002Fp>\u003Cp>We saw sort, but there's also a search. There's stuff for pagination, aliases, functions you can use, especially around dates and more. And there are also really complex filter operators that you can use. So we just use greater than or equal to, and this is, you know, one of quite a standard set of operators for any time you're querying data. But ones that I find interesting are the intersects and the intersects bounding box.\u003C\u002Fp>\u003Cp>These are only available when you have geospatial data. So you can set a box and say, is this point in the box or not, or only return items that are in the box. And so I'm sure you can start to imagine the applications you can build on top of this data along with the Directus APIs. So that's a little bit of an example on how to take data from NASA around meteorites that have fallen to Earth, and bring them into direct us to explore them, to analyze them, and of course, to use the auto generated APIs. Once again, if you have a dataset that you or you know of a dataset that you think would be interesting for this series, just get in touch, and maybe it will feature in a future episode.\u003C\u002Fp>\u003Cp>But until then, bye for now.\u003C\u002Fp>","In this show, we give new life to open datasets with the help of directors. Join me as we explore, analyze, and generate APIs to improve access to and democratize data. Before we get started, if you have or know of an interesting open dataset and want us to feature it, just reach out to our team and it may well feature in a future episode. Today's dataset was published by NASA and contains data on over 34,000 meteorites from the meteoritical society. It contains information about their classification, where and when they were observed, and whether they fell or were found. Now just a quick note before we continue, I later, after setting everything up, learnt about the difference between a meteor and a meteorite. Basically, it's a meteor when it's in the air, and when it survives its journey down to Earth and it lands, it becomes a meteorite. This is a dataset of meteorites, but I'm going to, for ease, just refer to them as meteors throughout. I just I want you to know that I know the difference now and so do you. So these are meteorites. Right? But we'll refer to them as meteors from here. Now there's only a couple of very small processing steps that I made to this data before I imported it in Directus. Firstly, I converted coordinates to GeoJSON objects, which allows us to use all of the mapping features inside of Directus. Secondly, there was one data point in here which was incorrectly entered. It said that a a meteor fell at, what was the year? 2101, and it was 2010. I googled it, and I fixed it in the dataset because it was ruining all of the graphs. So just to note there that there was a small error in the data that I fixed, and I changed the format of just the mapping and location data so it works following the GeoJSON standard. Now before we jump in and see what we can now do with this data, just to note that this specific dataset just stops in 2013. And as we can see here in the web page, there is a new dataset on a new URL, but that URL doesn't resolve. So I just use this dataset. Just know that it stops in 2013, but I'm sure it could be updated if you were to follow along at home. This is the director's data studio. It's a web application that lets you explore, analyze, and work with data in a database. And as you can see, all 38,000 items from that dataset have been imported. Now straight away, we have access to everything Directus offers. So we can filter these items perhaps by the year that they they were discovered or whether they fell or were found, stuff like that. We can step in to a single item like this one here and see a bit more data around it. Here we can see the weight of it or the mass of it rather, when it was found. And just to be clear, this is a date field, but the dataset only has the year. So the year is the only thing that actually matters here. Whether or not it was, it fell or it was found, it's class, and so on. And because we encoded the coordinates as a GeoJSON point, we know the exact point that this coordinate resolves to. So we can zoom out there and see that on a map. Now this is a pretty standard way to look at data. It's a table, but directors does also have other layouts including a map. And as these data points are encoded, as GeoJSON, as I mentioned, we now get to use the map layout, which I think is super interesting. So you can see a concentration here perhaps, of data that is stored in this dataset. So I just think that's really fascinating. There's obviously a lot more we can do with this data inside of directors, but what I wanna draw your attention to is the insights module. So this is an insights dashboard for this dataset here that I prepared earlier, and it it just allows us to analyse this data. So I've added a few panels here. Firstly, I've added this start year and end year input so we can change this. You know, we can change this and all of these other fields are hooked into these two values. To show you how that works, inside of this panel, we add a filter and we say the year, must be greater than or equal to this 1st year and less than or equal to the end year. You can apply other filters, of course, in here manually, like hard coded or using dynamic values from text boxes. So in here, we also see, you know, this, this graph that I've put in showing all of the meteors in the dataset for these given years. The average size in the time frame. So we see that there was a kind of peak in size, I suppose, of of, meteors in 2004 and back up in 2013. I don't know if that means anything, but you can discover it, I suppose. You can also look at, you know, whether found or fell. So we see there was just 1, 1 meteor in this dataset that fell and was observed to have fallen versus having later been found. The classes, of those meteors so we see in this time frame here, a majority of meteors were l six class. And here is a list of meteors in this criteria here. They're just alphabetized, so we can step into those and start to look in a little more detail. So as you saw, we can use the director's data studio to explore and query data using the web application. But every director's project also comes with a set of auto generated APIs to do the same thing. And this means that developers can not only interact with that data using APIs, but can build applications on top of those APIs to increase its usage, remix it, and encourage citizen participation. Now these APIs can also be used to create or update data, and they can be locked down in a number of ways. So they can be public, but they might also require registration or specific permissions. Now I've prepared some requests to show you so you can get an idea of how querying data works using the REST API. But, of course, there are API endpoints as well for updating or creating new data as well. Let's jump into postman and see how it works. This end point will fetch all items from the meteors collection. It will return an array of objects where each object represents 1 meteor. Now by default, this will return a 100 items and you can either paginate or change the size of the response or both. And realistically, you'll probably want to do both. And just a reminder that you can also lock down which fields are actually returned by directors to a public user or indeed any user with any role. So here we see the ID, the name, the class, the location encoded as a GeoJSON object, and the mass in grams. Now we can also tack on any number of director's query parameters to change the data that comes back. So this example here has 2 query parameters. The first one as shown here and here will only return items with a mass that is greater than or equal to a 1,000. That's grams. So that's 1 kilo. So it will never return items that are less than a 1,000 in this field. It will also sort the returned items starting with the biggest. So this is the biggest item on record found in 1920, and the next biggest was found in 18\u002F18, and so on and so forth. So you can add any number of these filters, sorts, query parameters on, and you can add several, you can add several filters at the same time as well. Now this final example isn't about getting data itself, but it's about using our aggregation and group by query parameters to start to analyze the data. And this is more or less how the director's insights module actually generates data in the panels. So here we're saying, get every item and group it by the year. So for every year in this data set, we get to see how many meteors exist. So there's 1 in this year, 1 in this year, and so on. But down in let's find an example here. In 17, 68, there are 2 meteors in the dataset and so on and so forth. These are really powerful. This is obviously quite a straightforward example, but you can use this in many, many ways. There are a whole set of query parameters you can use, not just aggregation and grouping. We saw filter. We saw sort, but there's also a search. There's stuff for pagination, aliases, functions you can use, especially around dates and more. And there are also really complex filter operators that you can use. So we just use greater than or equal to, and this is, you know, one of quite a standard set of operators for any time you're querying data. But ones that I find interesting are the intersects and the intersects bounding box. These are only available when you have geospatial data. So you can set a box and say, is this point in the box or not, or only return items that are in the box. And so I'm sure you can start to imagine the applications you can build on top of this data along with the Directus APIs. So that's a little bit of an example on how to take data from NASA around meteorites that have fallen to Earth, and bring them into direct us to explore them, to analyze them, and of course, to use the auto generated APIs. Once again, if you have a dataset that you or you know of a dataset that you think would be interesting for this series, just get in touch, and maybe it will feature in a future episode. But until then, bye for now.","ac30eac9-474e-4c9f-be0a-b07f896ced12",[2808],"43ea5c2a-29a4-4e00-9495-35383d499869",[],{"year":978,"number":2706,"id":2707,"episodes":2811,"show":2812},[2709,2710,2692],{"title":2712,"slug":2713},{"id":2394,"slug":2814,"vimeo_id":2815,"description":2816,"tile":2817,"length":264,"resources":12,"people":2818,"episode_number":480,"published":2802,"title":2822,"video_transcript_html":2823,"video_transcript_text":2824,"content":12,"seo":12,"status":19,"episode_people":2825,"recommendations":2827,"season":2828},"10-12","953540772","Directus 10.12 enhances public user registration with a set of new features to control your project.","9f0e4722-1a2c-4892-bf1c-6ad9f2d2b477",[2819],{"name":2820,"url":2821},"Kevin Lewis","https:\u002F\u002Fdirectus.io\u002Fteam\u002Fkevin-lewis","What's New in Directus Version 10.12","\u003Cp>Speaker 0: My name is Kevin. And today, I'm gonna tell you what's new in Directus 10.12, public user registration enhancements. You can now provide a custom verification URL when registering new users. Like with password resets and single sign on, this means that your users never have to be exposed to Directus if you're using it in your separate application. This release also introduces new environment variables to limit the number of users in your project.\u003C\u002Fp>\u003Cp>Admins, users with API access, and users who can access the Data Studio. This gives you more control over how your project runs, especially when enabling public registration. Fixing expiring sessions. As many of you reported in Directus 10.11, there was a small but impactful bug that would sometimes lock you out of the Data Studio and requiring your cookies to be cleared. We've now fixed that and, obviously, that's quite a big user experience thing.\u003C\u002Fp>\u003Cp>So make sure you do update in order to receive these fixes. Community contributions. Thank you to those who contributed towards this release. Thanks to Zaheer for fixing an issue where calling the random string utility endpoint with an invalid length parameter could prevent the creation of valid sessions until the next restart. Thanks to Alejandro for an update to one of our extension guides and for Nicholas for fixing the new registration verification method in our SDK.\u003C\u002Fp>\u003Cp>Directus 10.12 is now available on NPM and Docker Hub, and we'll see you in the next release.\u003C\u002Fp>","My name is Kevin. And today, I'm gonna tell you what's new in Directus 10.12, public user registration enhancements. You can now provide a custom verification URL when registering new users. Like with password resets and single sign on, this means that your users never have to be exposed to Directus if you're using it in your separate application. This release also introduces new environment variables to limit the number of users in your project. Admins, users with API access, and users who can access the Data Studio. This gives you more control over how your project runs, especially when enabling public registration. Fixing expiring sessions. As many of you reported in Directus 10.11, there was a small but impactful bug that would sometimes lock you out of the Data Studio and requiring your cookies to be cleared. We've now fixed that and, obviously, that's quite a big user experience thing. So make sure you do update in order to receive these fixes. Community contributions. Thank you to those who contributed towards this release. Thanks to Zaheer for fixing an issue where calling the random string utility endpoint with an invalid length parameter could prevent the creation of valid sessions until the next restart. Thanks to Alejandro for an update to one of our extension guides and for Nicholas for fixing the new registration verification method in our SDK. Directus 10.12 is now available on NPM and Docker Hub, and we'll see you in the next release.",[2826],"781ec858-413c-44e1-afca-a3ae43964ff9",[],{"year":2380,"number":588,"id":2381,"episodes":2829,"show":2830},[2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394,2366],{"title":2396,"slug":2397},{"id":2758,"slug":2832,"vimeo_id":2833,"description":2834,"tile":2835,"length":527,"resources":12,"people":12,"episode_number":740,"published":2836,"title":2837,"video_transcript_html":2838,"video_transcript_text":2839,"content":12,"seo":2840,"status":19,"episode_people":2841,"recommendations":2843,"season":2844},"think-different","941259359","Designed by Bry Ross in California. Time to join your favorite theme guru for an exercise in simplistic theming as he delivers a captivating Apple theme.","78d093b5-7e1e-4833-a1eb-10ac46686dbe","2024-06-03","Apple (Think Different)","\u003Cp>Speaker 0: Welcome back to the show. I'm your host, Bry Ross. I thought you might join me with just a little breathing exercise before we theme today, just to bring us all down to the baseline vibration that we wanna be on when theming. Care to join me? Great.\u003C\u002Fp>\u003Cp>Alright. So let's take a deep breath in and out. That's perfect. Right. Let's dive into our theme today.\u003C\u002Fp>\u003Cp>Today we are going to be painting a beautiful apple theme. So I always start any project. Couple things. We want to make sure that we've got a properly prepared canvas. And for us, that means we've gotta have a nice data model with some data in there.\u003C\u002Fp>\u003Cp>If you don't have any sample data, you need some. Theming, you gotta be prepared. You gotta have that base layer to really properly theme. And then I always set up the project settings. So now that we've got that out of the way, we can go in and start theming.\u003C\u002Fp>\u003Cp>So let's go in and just take this Apple logo, looks like it's an SVG. For whatever reason, it's really hard save SVGs that are embedded. So I'm going to open up Versus Code. We're just going to save this SVG out to a file and then we're gonna import that, upload it into this particular project. Alright.\u003C\u002Fp>\u003Cp>So we're gonna save that and just paint that project logo on there. Looking great. Looking great. Apple logo. Amazing.\u003C\u002Fp>\u003Cp>Alright. For our public foreground, what are we gonna do? We're gonna do the same thing. We wanna use the Apple logo. I don't see any text with Apple on it, so we're just gonna use that same bit.\u003C\u002Fp>\u003Cp>I'm gonna take and pinch my palette knife and just dab that logo on the public foreground there. Again, advanced technique, work your way up to it. But just this dabbing and just a little turn can get you great results in your themes. Alright, so next little known fact, but you can include MP 4 files as a public background. So static images, great.\u003C\u002Fp>\u003Cp>Beautiful. Videos, even more. Wow. Really upped the wow factor on that. Makes you feel really welcoming when you log in and see a publicly beautiful log in page.\u003C\u002Fp>\u003Cp>Alright. So now we're just going to save a little bit. We've got got the basics of our theme. We've got our brand in tact. Now we just want to pull this theme out of the Apple website, out of our psyche, out of our out of our chi.\u003C\u002Fp>\u003Cp>And we're gonna do this using the directest minimal base theme. Lots of white space, lots of white space. We've got lots of, room to breathe here and that's really what Apple does well. Inside their design, lots of webs with these. They let the products shine.\u003C\u002Fp>\u003Cp>Alright. So we're gonna go into our modules now and let's just remove all the borders. I'm seeing this. I'm seeing the vision. We're not gonna have any borders on this.\u003C\u002Fp>\u003Cp>So we're just gonna paint over all those pretty borders. Great. Great. Looks really good. Okay.\u003C\u002Fp>\u003Cp>This looks really solid. Alright. So what I'm gonna do now, I'm looking for this subtle gray background that they use. The hex code is actually f a f a f a. In reverse that's AFAF.\u003C\u002Fp>\u003Cp>And this theme is calm AF. Alright so we're gonna go in and I want to adjust the logo bar, module bar logo, so we're just gonna edit our custom CSS there. Just, just give it a little background. Alright. Okay.\u003C\u002Fp>\u003Cp>And then anytime you change colors, you definitely wanna dive in and make sure you dust those demons out of it. Make sure those hex codes don't get stuck in your number pad. You really gotta take extra special care of that number pad. Alright, so we're gonna put this down. And now we've got this really nice looking canvas.\u003C\u002Fp>\u003Cp>It's all white, except we we've got a little border up there at the tippy top that we want to get rid of. But everything else looks it's really nice. Now one of the other things that I noticed on the Apple website is this large border radius around these cards. So if we look for this we could see the card global corner radius is like 28 pixels. This is rather large, but maybe let's dial it into like 18 pixels and see what that looks like.\u003C\u002Fp>\u003Cp>That gives like a nice clean effect. I'm feeling this. Do you feel it? Yes. You're doing great.\u003C\u002Fp>\u003Cp>Just keep following along. Let the spirit of the theme guide you to the final output. Alright. So, we're gonna pick up our half inch brush here and we're gonna look to dial in this border width on the project. Just like that.\u003C\u002Fp>\u003Cp>Just a quick stroke of your brush. You can blend that in, and there are no mistakes. There's only happy accidents and opportunities to paint a better theme. Alright, so within our header we're also going to look to remove that border so we can see it there inside our CSS properties. Border bottom.\u003C\u002Fp>\u003Cp>So we're just going to look for the border width inside the header and there we go. We've got this nice no border subtle effects. It looks great. Alright, so now what do we want to do? We want to take a look at our fonts.\u003C\u002Fp>\u003Cp>And we're actually using the system UI fonts. And I'm on a Mac as I paint this beautiful theme so we don't necessarily have to even use a different font. And that probably we cut this part here. I do all this and, get, like, a Kors error for this. So maybe, again, like, we cut this out, cut to some point in the future.\u003C\u002Fp>\u003Cp>Where is it? Where are you? Where are you? Cut. This is not gonna work.\u003C\u002Fp>\u003Cp>I think I'll find the course issue here. Open up the console. Great. Don't even need it. Can't use that font either.\u003C\u002Fp>\u003Cp>Yeah. Unfortunately, nope. Not doing it. System UI. Okay.\u003C\u002Fp>\u003Cp>Alright. So now within our SANs family we're just going to use the system UI as well and that should adjust all the labels within our forms to use the system default font. Now there's one other thing that I see that I wanna adjust here which is just this search input. So we're gonna go into our CSS settings here. We're going to go to our light theme customization inside our project and we're gonna change this background normal to the same white Xcode.\u003C\u002Fp>\u003Cp>Emphasis. Alright, so this is looking great. We'll just paint on a few final touches of this particular theme and we're gonna log out and we've got this beautiful public login page with a very stunning video for the viewers to log in to. That's it for this episode of The Joy of Theming. I hope you've picked up a few tips and tricks to use in your own themes.\u003C\u002Fp>\u003Cp>I'm not an expert, you're the expert. Thanks for following along on this journey. We'll see you.\u003C\u002Fp>","Welcome back to the show. I'm your host, Bry Ross. I thought you might join me with just a little breathing exercise before we theme today, just to bring us all down to the baseline vibration that we wanna be on when theming. Care to join me? Great. Alright. So let's take a deep breath in and out. That's perfect. Right. Let's dive into our theme today. Today we are going to be painting a beautiful apple theme. So I always start any project. Couple things. We want to make sure that we've got a properly prepared canvas. And for us, that means we've gotta have a nice data model with some data in there. If you don't have any sample data, you need some. Theming, you gotta be prepared. You gotta have that base layer to really properly theme. And then I always set up the project settings. So now that we've got that out of the way, we can go in and start theming. So let's go in and just take this Apple logo, looks like it's an SVG. For whatever reason, it's really hard save SVGs that are embedded. So I'm going to open up Versus Code. We're just going to save this SVG out to a file and then we're gonna import that, upload it into this particular project. Alright. So we're gonna save that and just paint that project logo on there. Looking great. Looking great. Apple logo. Amazing. Alright. For our public foreground, what are we gonna do? We're gonna do the same thing. We wanna use the Apple logo. I don't see any text with Apple on it, so we're just gonna use that same bit. I'm gonna take and pinch my palette knife and just dab that logo on the public foreground there. Again, advanced technique, work your way up to it. But just this dabbing and just a little turn can get you great results in your themes. Alright, so next little known fact, but you can include MP 4 files as a public background. So static images, great. Beautiful. Videos, even more. Wow. Really upped the wow factor on that. Makes you feel really welcoming when you log in and see a publicly beautiful log in page. Alright. So now we're just going to save a little bit. We've got got the basics of our theme. We've got our brand in tact. Now we just want to pull this theme out of the Apple website, out of our psyche, out of our out of our chi. And we're gonna do this using the directest minimal base theme. Lots of white space, lots of white space. We've got lots of, room to breathe here and that's really what Apple does well. Inside their design, lots of webs with these. They let the products shine. Alright. So we're gonna go into our modules now and let's just remove all the borders. I'm seeing this. I'm seeing the vision. We're not gonna have any borders on this. So we're just gonna paint over all those pretty borders. Great. Great. Looks really good. Okay. This looks really solid. Alright. So what I'm gonna do now, I'm looking for this subtle gray background that they use. The hex code is actually f a f a f a. In reverse that's AFAF. And this theme is calm AF. Alright so we're gonna go in and I want to adjust the logo bar, module bar logo, so we're just gonna edit our custom CSS there. Just, just give it a little background. Alright. Okay. And then anytime you change colors, you definitely wanna dive in and make sure you dust those demons out of it. Make sure those hex codes don't get stuck in your number pad. You really gotta take extra special care of that number pad. Alright, so we're gonna put this down. And now we've got this really nice looking canvas. It's all white, except we we've got a little border up there at the tippy top that we want to get rid of. But everything else looks it's really nice. Now one of the other things that I noticed on the Apple website is this large border radius around these cards. So if we look for this we could see the card global corner radius is like 28 pixels. This is rather large, but maybe let's dial it into like 18 pixels and see what that looks like. That gives like a nice clean effect. I'm feeling this. Do you feel it? Yes. You're doing great. Just keep following along. Let the spirit of the theme guide you to the final output. Alright. So, we're gonna pick up our half inch brush here and we're gonna look to dial in this border width on the project. Just like that. Just a quick stroke of your brush. You can blend that in, and there are no mistakes. There's only happy accidents and opportunities to paint a better theme. Alright, so within our header we're also going to look to remove that border so we can see it there inside our CSS properties. Border bottom. So we're just going to look for the border width inside the header and there we go. We've got this nice no border subtle effects. It looks great. Alright, so now what do we want to do? We want to take a look at our fonts. And we're actually using the system UI fonts. And I'm on a Mac as I paint this beautiful theme so we don't necessarily have to even use a different font. And that probably we cut this part here. I do all this and, get, like, a Kors error for this. So maybe, again, like, we cut this out, cut to some point in the future. Where is it? Where are you? Where are you? Cut. This is not gonna work. I think I'll find the course issue here. Open up the console. Great. Don't even need it. Can't use that font either. Yeah. Unfortunately, nope. Not doing it. System UI. Okay. Alright. So now within our SANs family we're just going to use the system UI as well and that should adjust all the labels within our forms to use the system default font. Now there's one other thing that I see that I wanna adjust here which is just this search input. So we're gonna go into our CSS settings here. We're going to go to our light theme customization inside our project and we're gonna change this background normal to the same white Xcode. Emphasis. Alright, so this is looking great. We'll just paint on a few final touches of this particular theme and we're gonna log out and we've got this beautiful public login page with a very stunning video for the viewers to log in to. That's it for this episode of The Joy of Theming. I hope you've picked up a few tips and tricks to use in your own themes. I'm not an expert, you're the expert. Thanks for following along on this journey. We'll see you.","ee082bea-c80f-4df4-a403-e58c2cc738df",[2842],"ee50788f-5581-4a8b-80d1-9987d743b177",[],{"year":978,"number":2745,"id":2746,"episodes":2845,"show":2846},[2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2731],{"title":2760,"slug":2761},{"id":2319,"slug":2848,"vimeo_id":2849,"description":2850,"tile":2851,"length":1156,"resources":12,"people":12,"episode_number":129,"published":2852,"title":2853,"video_transcript_html":2854,"video_transcript_text":2855,"content":12,"seo":2856,"status":19,"episode_people":2857,"recommendations":2859,"season":2860},"databox-clone","951271332","It’s time to act on your data. So join Bryant as he sprints to build a KPI dashboard to better understand your data. He dives into metrics vs events and pulling in data from third party systems.","42781675-cf4d-4cc4-9460-e0f36c4f5829","2024-05-31","Mission: Databox Clone","\u003Cp>Speaker 0: Hi. Welcome back to another episode of 100 apps, 100 hours. I'm your host Brian Gillespie, developer advocate here at Directus. And if you're new to the show, what do we do? We rebuild or build some of your favorite apps, ideas, suggestions in 1 hour less or publicly fail trying.\u003C\u002Fp>\u003Cp>Sometimes spectacularly fail, but hopefully not in this episode. What are we gonna be building? Cover that in a moment. There are 2 rules if you're new to the show. 1st and foremost, there are only 60 minutes to plan and build.\u003C\u002Fp>\u003Cp>No more, no less. Get what you get. You don't throw a fit, as I like to say to my kids. Rule number 2 is you use whatever you have at your disposal, whether this is AI, GitHub Copilot, Tailwind, CSS, UI libraries, even past projects. Right?\u003C\u002Fp>\u003Cp>Whatever we need to get the job done. So back to what we're building, a Data Box clone. So you may have heard of Data Box, you may not have. This was a suggestion from one of my colleagues. Data Box is an well, it's built as an easy to use analytics platform for growing businesses.\u003C\u002Fp>\u003Cp>Basically, what I see here is a dashboard. We've got multiple sources of data for that dashboard. I see them speak of a a centralized source of truth, which is one of the strengths of Directus, at least in my mind. We get to be the the hub for all of your data, that you get APIs to work with. So that's why I like this type of project for Directus.\u003C\u002Fp>\u003Cp>The bind line here, I'm just calling it KPI dashboard type thingy. Right? We're gonna ingest some data. We're going to display it on a dashboard, report on it, make that whole process easier. Sounds great.\u003C\u002Fp>\u003Cp>Let's dive right in. 60 minutes on the clock, and away we go. So looking at the Databox website, right, we've got these different sources of incoming data. We're reporting on those in, like, a time series or just a a metric, maybe a percentage, it looks like. But one of the first things I'd like to do is just study the documentation or the API references for a service.\u003C\u002Fp>\u003Cp>So I'm just gonna search for the Databox API. Looks like we've got that here. If I zoom in, we'll just kinda browse through this. Right? So in my mind, it created a couple of similar things in the past where we're storing events and metrics.\u003C\u002Fp>\u003Cp>We've even got some of this metric functionality inside our our own dashboard that we use internally for things like doc feedback. But in this particular case, I've not really built like a KPI dashboard. So let's take a look. We've got a see a token in the the data box website, that is the unique identifier that points to a storage container within their warehouse. It's like a bucket for your data.\u003C\u002Fp>\u003Cp>Looks like that's all that maybe, like, the different services. I see the metric here. Alright. So the metric is a quantitative measure of performance. That's a pretty good definition of that.\u003C\u002Fp>\u003Cp>Basically, all metrics are going to be storing numerical values. Alright. That's a a key part of this. They define that, that metric has a key and a display name, and then we have some data. So here's how we send the data to DataBox.\u003C\u002Fp>\u003Cp>Looks like we're referencing that key there, and then we give it a value. And what else? Where is the date information coming from? By default, the current date and time will be used to store information about the events. So when you send an event, it will automatically store the current time, unless you maybe change that.\u003C\u002Fp>\u003Cp>Got it. Okay. So at a high level, I understand, kind of, what they're doing here, just building a nice UI on that. On our side for the setup here, I've just got a blank Directus project. Nothing in here, no dashboards, any of that.\u003C\u002Fp>\u003Cp>We'll we'll certainly dive into it. But for now, let's dive into Figma and actually sketch this thing out. Right? One of the the big things that come up in my mind is the difference between metrics and something like events. Right?\u003C\u002Fp>\u003Cp>So metrics are like an aggregation. Could be of of, like, potential events or other data. Right? So a metric is typically on, like, well, I I won't say typically. There's all all types of metrics.\u003C\u002Fp>\u003Cp>You know, on, like, a a website or a server, you might have, like, time to first byte, API response time that you're tracking as a metric, and maybe you wanna report on that every few seconds. Right? On things like billing data, you know, you may have, like, let's say, AAR, our MRR, new users, monthly users, etcetera, you know, something like that. On the CRM side, you probably got pipeline, number of new deals, qualified leads coming in, that you're tracking. So those would all be, examples of metrics.\u003C\u002Fp>\u003Cp>And then the events are the actual things that happen, not on the schedule, I guess. On the schedule. Things that happened, basically. Let's call it that. Right?\u003C\u002Fp>\u003Cp>This could be when a new user signs up for a service that have happened. Things that have happened. Great. Alright. So in in a system like this, it might be helpful to have both of those.\u003C\u002Fp>\u003Cp>Right? Because one of our metrics could be aggregating all the events that happened for that particular day. Page views, you know, video views for Directus TV. Lots of lots of opportunity there. And then, what else are we gonna have on this?\u003C\u002Fp>\u003Cp>Right? Data sources, sources of data. Data sources, this would be, you know, our CRM, our, websites, analytics, billing, accounting, etcetera. Whatever those sources are, it looks like Databox has a lot of those built in. You know, we'll probably be just basically creating our own at this point.\u003C\u002Fp>\u003Cp>But this feels pretty good as far as functionality is concerned. I think there's a there's a pattern that I've used in a previous project. It's It's actually our directus dot pizzademo. A really cool demo if you wanna check it out. Just a a look at, like, a a full fledged direct to this project in in many different facets.\u003C\u002Fp>\u003Cp>There's 5 or 6 different use cases slammed into this thing. But we've got, this pattern of, like, metrics where I've got, my name and description, and I've got, I like some prefixes and suffixes that I could show inside the UI or on a front end, and then I have my data. So instead of shoving it all in one table, kinda separate that out. I kinda like that pattern, so let's do that. Do metric underscore data.\u003C\u002Fp>\u003Cp>Looks good. Alright. I'm just gonna pull this up to the side. Again, this is the use whatever you have at your disposal part. Right?\u003C\u002Fp>\u003Cp>So I've got my Clean Directus project over here. We're gonna create a couple of new tables. Let's do metrics first. So that's the one we're working on here. Do we need a status for this?\u003C\u002Fp>\u003Cp>Probably not. Do I wanna know when it was created, who it was created by? Yeah. Maybe. Maybe we wanna add a sort for it.\u003C\u002Fp>\u003Cp>No big deal. Alright. Then we're gonna give this a name. Great. We can give it a, like, a key if we want, as well.\u003C\u002Fp>\u003Cp>So if I want to have a unique identifier beyond like a UID, You can also, like, set these up to be manually generated strings for the primary key. Obviously, you've gotta enter that every time, but I can go in here and make this string unique. And if we're looking for a key, you know, we might want to make that URL safe and just use the input option to slugify it. Alright. And then we've got, like, a description of the metric.\u003C\u002Fp>\u003Cp>Maybe I'll go in and add a note for this. What does this metric measure in detail? Great. And I've got prefix and suffix on here. Not sure we necessarily need those.\u003C\u002Fp>\u003Cp>We'll just keep that off for now. But basically, we're gonna make sure we unhide this ID. Need this ID when sending data, sending metric data. That'll be the collection that we create next. Right?\u003C\u002Fp>\u003Cp>So we've got a name, we've got a key, we've got a description. Right? We've got API response time over here just as a a reference, but, what am I looking at initially? Let's call this, like, ARR. Alright.\u003C\u002Fp>\u003Cp>Tracks changes to annual recurring revenue over time. Great. Alright, so we got a metric in there, looking great. Now we need a place to store that metric data. Right?\u003C\u002Fp>\u003Cp>So applying that same pattern, I'm just gonna fade that away. Just close this out. Alright. We're gonna have a metric data table. Metrics, data, metric data could go either way.\u003C\u002Fp>\u003Cp>Naming stuff, always the hardest part of these episodes. Right? So on this one, we've got, like, the optional fields here, just shortcuts for things like recording a timestamp whenever a thing is created. Now one of the things that I like to do on something like this is just remove any ambiguity and call it a time stamp, or create it at, but you can change those on the fly as well. So I'm gonna unhide that.\u003C\u002Fp>\u003Cp>For our metric data, we're also gonna have a value for that data. Right? This is gonna be let's go with a decimal on this, just because we might wanna store something with the decimal place. It's not always integers. Great.\u003C\u002Fp>\u003Cp>We'll choose type decimal. And if I wanted to flesh this out even further, I could go to the advanced settings and, like, control my precision and scale if I needed to. If I go into the display here, I can also auto format this, which Directus will try to guess as to what format this should be in and apply some styling and, conditional formatting for you, make that look nice. Alright. So we got a timestamp.\u003C\u002Fp>\u003Cp>We've got metric data. Let's link these things together with a many to one relationship. So Directus makes it super simple. To create these relationships, we're just gonna call the key for this field inside metric data. We're gonna call that metric, and then we have our metrics over here.\u003C\u002Fp>\u003Cp>So that's the related collection. Now I could hit save here, but I'm gonna open up in the advanced settings and I'm gonna go to the corresponding field under the relationship tab. And here I can go ahead and create that inverse relationship in the metrics table. So I'm just gonna call this data just to keep it simple and Directus will show me, hey, we're gonna create this field for you. So if I delete a metric, you know, maybe we do wanna delete the metric data just because it doesn't make so much sense without the identifier and, you know, the name and the description of that metric.\u003C\u002Fp>\u003Cp>Alright. So this looks pretty good. We'll just, make that half with clean this up a little bit. Right? What else do we have?\u003C\u002Fp>\u003Cp>We have events incoming as well. Right? So, again, the difference between events and metrics, like, a a metric is something that is going to be at a specific point in time. An event is something that that happened. Right?\u003C\u002Fp>\u003Cp>So a a ARR doesn't happen. Right? A user subscribes and that has an effect on ARR. So also starting to sound like a pilot or a pirate in this episode, not a pilot. So we got an event.\u003C\u002Fp>\u003Cp>Let's see. We'll, again, do the same thing with the timestamp. The events are probably not going to to change much, so maybe we'll just call, like, created by, like, the user here. That's cool. Alright.\u003C\u002Fp>\u003Cp>And for the event, let's see. What are we gonna have on the event? We'll probably have something like we have on, we'll have a name for the event. Maybe that's just a string. We could have a key for that event.\u003C\u002Fp>\u003Cp>Key. What kind of event key is this? We will go back in, make sure this is slugified, make it URL safe. We got the user. We probably wanna show that in this case.\u003C\u002Fp>\u003Cp>Even though those are hidden by default, we wanna show the timestamp of this event. And then, you know, you might have, like, a a JSON field to pass metadata. So in this case, I'm just gonna pick the code interface, choose the JSON type, and we'll just call it metadata in case I wanna store additional stuff on the events. Alright. That's looking nice.\u003C\u002Fp>\u003Cp>And then, you know, to bring this all home, let's add some icons. But, one of the other things that I'm gonna do is add the sources just in case we could we wanted multiples. Like, I'm imagining if you got your CRM data, you're probably looking at 2 or 3 different metrics within that. Or, like, if you've got your Stripe data, you're probably checking your ARR, your MRR, number of new sign ups, your churn rate, things like that. So, there's maybe one more level of abstraction here that we're gonna go for.\u003C\u002Fp>\u003Cp>Alright. So we've got events. Let's just get chip extraction. That looks good. Metric data, we could probably hide that if we want to, or we just call it data, see what we got.\u003C\u002Fp>\u003Cp>There we go. Data usage. Good enough for me. Alright. Great.\u003C\u002Fp>\u003Cp>So let's add that sources, or this could be something like services. Maybe that makes more sense in this particular case. We'll use the UID. You know, we'll see when users update these services. We'll give it a name, and then we can link, a service to, you know, both of these, like events and metrics.\u003C\u002Fp>\u003Cp>So we'll link, use the mini took one here. We'll call this the service that is related to this metric. And if I flip over to that advanced section, again, I could go in and add the different metrics that are associated with that service. Great. And if we go to events, same thing.\u003C\u002Fp>\u003Cp>Right? I might call this service Services. K. Go to our relationship. We'll create that extra corresponding field.\u003C\u002Fp>\u003Cp>It show the related values in the display template. And now we've kind of got this whole thing fleshed out. Right? We've got ARR, the service here in this case. And maybe this is Stripe as the service.\u003C\u002Fp>\u003Cp>Great. Okay. Looking great. Love it. Love it.\u003C\u002Fp>\u003Cp>You may we do some drag and drop to kinda change the stuff around. See the ID. Don't really need it. But let's go in and actually, you know, populate some of this data now that we've we've got something in here. Right?\u003C\u002Fp>\u003Cp>So I'm gonna go in and create something here, but you could see I can't change my time stamp, so I could manually change that. Let's see. The metric we're using here is ARR, so this is gonna record the current value because of the way that time stamp field is set up, or the current date and time. I'm sorry. We'll do something like $1,998.\u003C\u002Fp>\u003Cp>That's that's what we're making ARR right now in this particular app. Not doing so well, but, if I'm manually throwing these in here, I can't edit this. So two ways I could populate this data. I could use the API to quickly, like, shove data in here for this specific metric if I wanted to. That's available to me.\u003C\u002Fp>\u003Cp>Right? Or I could, you know, remove the restriction here on this to basically let me edit that value if needed. And then you could see on create, there's kind of this save current date and time thing going on as well. So I might do both here just in case we need to edit that. I'll change that.\u003C\u002Fp>\u003Cp>Save it. Cool. Alright. And then, you know, if we change this again, we go to change it to 8:8:8, maybe last Thursday. That's what our ARR was.\u003C\u002Fp>\u003Cp>And you can see that time stamp was populated, so I could turn off that behavior. But if I just hit the save button twice here, we'll get some actual data from this. But if I wanted to do that via the API, that's all ready to go for me as well. The only thing I need to do is either use a static token or make that publicly available. Right?\u003C\u002Fp>\u003Cp>In this case, I might just create a static token. So I'm gonna go into my user directory. We'll say, scroll down to the bottom here. We're gonna create a static token to use as we're calling this. And I'm just gonna pull up a little app that I've got called Bruno.\u003C\u002Fp>\u003Cp>It's kinda like Postman, except it is offline first. So Postman, you have to be logged into their service. Bruno is, I think it's open source and offline first, basically. Like, it sorts all of your stuff locally. So, what is the name of this?\u003C\u002Fp>\u003Cp>This is metric data. Right? And we've got our metric that we're using here. So I could copy this. And And as far as the payload, we should just need to pass, like, a timestamp and a value.\u003C\u002Fp>\u003Cp>So this is using the, timestamp value. So we've actually got the time zone in here as well. But if we go in, we're just gonna do 8055 post metric data. Let's set this up as a post request. My headers, I've got, like, a Directus token set up already for this.\u003C\u002Fp>\u003Cp>I'm just gonna replace that value. Is it secret? Secret for now. There we go. We'll save.\u003C\u002Fp>\u003Cp>Changes saved successfully. And let's actually just see if we can get the Directus data first. Says my token is incorrect. Correctamundo. That's not great.\u003C\u002Fp>\u003Cp>Let's try this again. Did I actually save the user? Maybe I saved the wrong user. I think there's only one user, but alright. Delete.\u003C\u002Fp>\u003Cp>Save. There's the value. I'll hit save. Try this one again. There we go.\u003C\u002Fp>\u003Cp>So now we can see our actual metrics. Right? That's the beauty of Directus. Take any SQL database, wrap it, gonna sit alongside of it and give you APIs in order to access it. But now I could go in and I should be able to post this with just like a raw JSON body.\u003C\u002Fp>\u003Cp>Got value of, what, 777. The metric, we could just copy that. Right? That's the unique identifier for that metric. I could have set that up to be, again, a manually entered string.\u003C\u002Fp>\u003Cp>That might have been easier for this. And then we're gonna add a time stamp to this. So if I populate the time stamp, it should do that for me. Let's just dig back in time here and do that. So we'll send that one.\u003C\u002Fp>\u003Cp>Now if I refresh, it should have a couple of metrics, 777 generated less than a minute ago. Right? So it's not saving that timestamp. Maybe we just need to actually turn that off for now. So we go into our metric data.\u003C\u002Fp>\u003Cp>Just turn this off. Where are you? Save current on create, do nothing. Alright. So now if we try this again, you can see that the date that's being stored is that 22nd that we had.\u003C\u002Fp>\u003Cp>Just mix this up a little bit. 555. Dig back into time. Great. Alright.\u003C\u002Fp>\u003Cp>Sweetness. Okay. So now we got some data. Right? Let's build a dashboard out of this data.\u003C\u002Fp>\u003Cp>So we're gonna go into the insights module inside Directus. The beauty of insights is that I can build dashboards with low code, no code. Really, it's no code unless you start needing to adjust some of the the JSON within the filters, and use some dynamic variables. But beyond that, we could create these dashboards really easily. Right?\u003C\u002Fp>\u003Cp>So we'll call this the our dashboard, and we've got a do we have an no. Let's do it like a chart. Chart. See what we got. Yep.\u003C\u002Fp>\u003Cp>There we go. Got a chart going on. Let's start adding to this. Right? So if I look if we do I have the data box up?\u003C\u002Fp>\u003Cp>Let's just see what they've got. Like, first on the list here is, like, revenue. I see new customers, like, website sessions, all pretty standard stuff. We're we're just gonna go in and let's just show, like, a a metric. Right?\u003C\u002Fp>\u003Cp>Like, what is our current ARR? If I wanna send this out to, you know, one of my team leaders or, a CEO or, you know, my manager, like, how how are we gonna set this up? So in this case, the collection that we're working with is the actual metric data. That's where we're storing the values that we wanna see. The field that we wanna report on is the value itself.\u003C\u002Fp>\u003Cp>And the aggregate function here is basically, like, what are we gonna run to get this specific data? In this case, you know, I really just want, like, the last value. As of, like, the last value in that series, what's our ARR currently? So I could do this, and the sort field here would probably be timestamp. And then the filter, basically, like, of all the metric data items in there, how do we select just the ones for ARR?\u003C\u002Fp>\u003Cp>So I'm gonna go in. We've got our metric thing here. You know, I could go by the key here, that the key is equal to arr. Hopefully, I set that up, we'll find out in a moment. And then I'm just gonna scroll down.\u003C\u002Fp>\u003Cp>We'll give this a name, like current ARR. Great. And we could see that. So we got 777. That's the last value in that series.\u003C\u002Fp>\u003Cp>Right? Maybe we add a prefix, so we get dollar sign. But let's just check that. And if we look here, 4 minutes ago, we have 777. Now if I were to go in and delete that value, right, that should become 999 when we go to the dashboard.\u003C\u002Fp>\u003Cp>Okay. All is right with the world. That's that's looking nice. So this is a little underwhelming though. Right?\u003C\u002Fp>\u003Cp>We wanna see more data than this. So actually, let's let's add some time series data to this. Let's track those ARR changes over time. So the time series is good for this. You could also use the line chart, that will allow you to get multiple groups.\u003C\u002Fp>\u003Cp>So if you wanted to, kinda look at groups of users within a a certain time period over, like, a certain set of events, that sort of thing. So, again, we're going to choose Metric Data. The aggregation here, if I've got 2 values for a given day. Right? This is probably the way that I'm gonna report on this, is by the by a given day.\u003C\u002Fp>\u003Cp>Do I average those values together, or do I do the minimum or the maximum? Let's just average them together. You know, one of the things that we might set up is just a a flow or an automation to go in and run those metrics for us on a daily basis. For the group precision, we'll just use day for this. Date field, we got our time stamp.\u003C\u002Fp>\u003Cp>And when it comes to the date range, this is, a little tricky in that, like, if I select one of these, it's always gonna be that specific date range unless I do something like automatic based on data. Or, you know, I could fill this in with other and make it like a dynamic variable where I could pick past 30 days, past 14 days, etcetera. In this case, what I'm gonna do, I'm gonna just set that to be automatic based on the data, and the value field here is gonna be the value. Plain and simple. We'll do ARR over time.\u003C\u002Fp>\u003Cp>Great. Looking nice. Okay. So now we can actually see those changes. And again, this is a full drag and drop builder.\u003C\u002Fp>\u003Cp>One of the nice things here is you could see if I connect these 2 together, the little border radii, border radius, those all they connect nicely. It's very subtle detail, but very helpful. Alright. So now we got a chart. You know, we could make this type of thing dynamic as well.\u003C\u002Fp>\u003Cp>It looks like the Databox dashboards are relatively fixed. I see like month to date for a lot of these. But let's say if we wanted to zoom in on a particular timeline. Right? You know, hey.\u003C\u002Fp>\u003Cp>This is only data for May, but, you know, maybe I wanted to look at last month's data. So what we can do, you can make these things dynamic using variables, like either the global relational variable, which allows you to select a specific thing. In this case, maybe you wanna report on, a specific metric. Or like a global variable here could be based on time. Right?\u003C\u002Fp>\u003Cp>So we could do something like this where we have variable key. We'll do date from, and that'll be a time stamp. Great. Use the date time interface. In the US, We don't use that 24 hour clock very often.\u003C\u002Fp>\u003Cp>Alright. So let's call this date from. Alright. So now we get like a panel here. We could duplicate this thing.\u003C\u002Fp>\u003Cp>We can call it date 2. So in the variable key, we'll call it date 2. I'll make sure I change my panel header just so I don't confuse any users of this dashboard. But we're all about empowering people. You know, if you've got, like, a ton of internal data, you could potentially and it's in a SQL database.\u003C\u002Fp>\u003Cp>You could connect Directus to it, get APIs, and get a dashboard experience out of the box, right, that that all members of your team would be able to use. So now that we have created those relationships or those variables in this case, these are not relational variables, they're just global variables. I can use the names of those inside my other data. Right? So current ARR, we're always looking for that to kind of show the same data.\u003C\u002Fp>\u003Cp>Right? What is the current ARR? If we're talking current, it's always x. But in in the case of like over time or like showing a list of records, maybe I want to filter that for a specific date range. So I could do something like this where I say the timestamp, I've got these filters here that you can see.\u003C\u002Fp>\u003Cp>But we also have greater than or equal to. And I could do something like this, is between, and I use a mustache syntax. And now we can use those date variables. Okay? So now I'm not seeing anything, right?\u003C\u002Fp>\u003Cp>What do I change this to? For default value, we have some dynamic variables here too. So let's say, for the date from, maybe we change it to the start of this year. So just back up to Jan 1. That's great.\u003C\u002Fp>\u003Cp>And then for date 2, maybe we always wanted to show, like, the latest value. So we could just go to something like dollar sign now, which should populate the current time stamp in that particular field. If I just refresh, now you could see what we've got going on here. So if I back this up, like let's go to like May 23rd. Okay, so now you could see like the scope of my data changes within the chart.\u003C\u002Fp>\u003Cp>That's how I can make these dashboards dynamic. Really, really neat feature. Right? Alright. So going back to our our board here.\u003C\u002Fp>\u003Cp>We've got some metrics. We've got metric data. We've got the sources planned out. We've got events. We haven't populated any of those.\u003C\u002Fp>\u003Cp>But one of the things that I kinda jumped the gun on here was what do we actually want out of this specific application. Right? We wanna manage all of our metrics in one place. That's right. Directus becomes the single source of truth for all of these particular metrics that we are managing, that we have under our control.\u003C\u002Fp>\u003Cp>And what else? So we're gonna build a usable dashboard for these metrics with usable dashboards. Yeah. We could slice this a couple different ways. And then as, a larger goal, you know, maybe we wanna integrate some third party service.\u003C\u002Fp>\u003Cp>3rd party service. Alright. So let's take a look at, like, the events side of it. Right? Events would be very helpful, I created a new account.\u003C\u002Fp>\u003Cp>Right? So let's just call this 1, created a new account. Alright? So let's just call this one, like, new account registration. Yeah.\u003C\u002Fp>\u003Cp>Seems great. New account and registration. I've got metadata here that we could store. It's great. We'll just pick the service.\u003C\u002Fp>\u003Cp>You know, maybe that's we create a new one. Can we create a new one from here? We can. Let's just call it website. Great.\u003C\u002Fp>\u003Cp>Okay. And cool. So now we got an event. The name is no account registration. It happened at this particular time with this particular user.\u003C\u002Fp>\u003Cp>I could go in and maybe I just create, like, a a fake user as well to populate some of these events. Fake user Fake user 2. That's a real original name. Right, Brian? Yeah.\u003C\u002Fp>\u003Cp>Okay. Alright. So we got another event. Maybe we want to, in this case, I'm gonna just disables the read only part of this. And oh, I didn't save it, did I?\u003C\u002Fp>\u003Cp>Disable Disable, just in case I need to change those items. Alright. And the difference here is we're gonna assign this to fake user. Let me go up to the 3 dots. If you are pretty new to Directus, this is a helpful hint here where you can use these keyboard shortcuts, command s to save and stay.\u003C\u002Fp>\u003Cp>Or let's say you wanna create a new record. You could save and create new. In this case, I'm gonna save as a copy. So we're not gonna touch the original event. We're just gonna create a copy of it, and I'm gonna assign this to fake user, account user registration, blah blah blah.\u003C\u002Fp>\u003Cp>If I was doing this on my front end, I would certainly be passing, like, some metadata of, yeah, what account let me maybe some of those account details that would be relevant. Of course, not any personally identifiable information. Don't wanna store any of that. And then we change the timestamp, and we change that to May, etcetera. Okay.\u003C\u002Fp>\u003Cp>Alright. So I've got a couple events. Right? And now, let's say I needed to run a report that shows me all of the new registrations within a certain time period. Right?\u003C\u002Fp>\u003Cp>Now I could go in and have, like, basically set up a a chart based on that. Right? And I could use this event data to get a list of those events or kinda aggregate those. But how would you track the the changes between those things? So, you know, how do I show on a dashboard how many new registrations there were 2 weeks ago versus today?\u003C\u002Fp>\u003Cp>Right? Or how can we analyze those things under time and compare them to other details? So in that case, maybe we want to convert those events over into metrics. One way you could do that is with direct as flows. Some automation inside the system.\u003C\u002Fp>\u003Cp>Right? So every day, maybe I want to go in and summarize all this information and store it so we can make quick work of analyzing the data. So in this case, let's just set up an automation. Right? We're going to summarize registrations.\u003C\u002Fp>\u003Cp>Sounds good. Alright. For an icon, You know, my recovering designer in me always has to have an icon. And for the trigger here, we're gonna trigger this at regular points in time. I always get hung up on the Kron syntax.\u003C\u002Fp>\u003Cp>Kron syntax for every minute. Let's see what we come up with for this. Every minute. Okay. You're probably gonna only set this up to run once a day.\u003C\u002Fp>\u003Cp>I'm gonna run this once a minute just so we can, like, get a good look at it. And you could even, like, manually trigger this sort of thing if you wanted to. But in this case, we'll set that up. And now what do we wanna do with this? Right?\u003C\u002Fp>\u003Cp>So if we're gonna summarize this registration data, we'll go in, and we need to get that registration data. So we'll say get data, and I could go in and hit read data. So we're gonna get the items from the database. Let's use full access, which gives it full permissions instead of the ones from the user, from the trigger, or the public role, in this case, like a cron job. We'll go to our metric data, and we can either specify the specific IDs we wanna find, or we can set up a query.\u003C\u002Fp>\u003Cp>So these are just the standard query, like, global query parameters that we support via the Directus API. Just flesh this out. Where are, which metric are we looking for? We want the metric. So here, we're at our metric data.\u003C\u002Fp>\u003Cp>I can also dig deeper into the actual related fields. Right? So the related field, metric key equals let's say equal no. Metric key. Yeah.\u003C\u002Fp>\u003Cp>I think it would be something like this. We'll see if this actually works. Key equals arr. No. Okay.\u003C\u002Fp>\u003Cp>Alright. So we look for the filter. This is a global parameter. So within the metric data, we look at the metric, which normally is just stored a UUID for that. But we can also go into the related fields here and say the key of this metric is equal to ARR.\u003C\u002Fp>\u003Cp>Is that actually gonna run? We'll see. Right? So we can see this already ran twice. If I wanted to to sit around for another minute, we can take a look at it, see what happens, or, you know, I could manually trigger this thing as well.\u003C\u002Fp>\u003Cp>So let's do that. We'll just go in metric data. Cool. Summarize registrations. Alright.\u003C\u002Fp>\u003Cp>I'll hit save. And now if I go into metric data, I can also run this thing. I forgot to turn off the need to select something. But now if I run that, we could see if we actually got the data that we were looking for. Great.\u003C\u002Fp>\u003Cp>I can see this is the data that we need. And just to make sure that's working as intended, I'm gonna just add another one here and maybe maybe just leave off a metric to make sure we should have 4 values inside that flow instead of 5. So, again, I'll just run this flow. Great. I'll go back and look at the logs really quickly.\u003C\u002Fp>\u003Cp>And I can see I'm only getting 4 values here. That's great. Cool. Alright. So now maybe I want to summarize those.\u003C\u002Fp>\u003Cp>But this is not our I'm actually looking at the wrong thing, aren't I? I'm getting the data from our metric data and not our events. So we need to make sure we actually change that up. Just an oversight here on my part. So in this case, we're gonna do the key equals new user registration.\u003C\u002Fp>\u003Cp>Alright. Now let's test that again just to make sure this is actually gonna run. K. Having a look. Alright.\u003C\u002Fp>\u003Cp>No. Are we getting our data? Did we call it something else in our events? New account registration. Always remember what you name things.\u003C\u002Fp>\u003Cp>It's a challenge. All right. Just paste that key here inside our query and hit save. Alright. So, again, maybe we wanna change where we're actually running this from to.\u003C\u002Fp>\u003Cp>Go to Events. Alright. Lots of back and forth here. So I'll select all these just because of the way that this flow was set up. Normally, I would set this up to be a cron job like we had before.\u003C\u002Fp>\u003Cp>But now if I check the logs, we could see we've got all of our events here. That's great. Now we are going to collect all those events and save them as a metric, right? We could also specify, You might want to use our dynamic filters as well, right? So let's actually just save this first.\u003C\u002Fp>\u003Cp>We'll come back to it. So we're gonna format this. Let's just do a quick run script. We'll call this like format data. And if we see here, we've got, this is our actual data.\u003C\u002Fp>\u003Cp>All right, so I'm gonna just copy paste this into Versus Code here, so I've got it. And then inside this operation, this run script operation, we can run arbitrary JavaScript to do whatever we want. So in this case, we are just going to actually combine these two pieces of data. Like, summarize how many we just want to get a count of all of the events for this, right? So something like this where we have, we're gonna get the data.\u003C\u002Fp>\u003Cp>We call it get data. So we could say that const events equals data dot get data. As each operation within a flow runs, it appends the data that operation returns under this key. So after this step, the results I'd be able to access via data dot format data inside a run script. But in this case, we're just gonna do const events.\u003C\u002Fp>\u003Cp>And then maybe we just return the the length of those, I think. Right? Simple as that. We don't have any values that we're like summing up here. We're just going to return events dot length.\u003C\u002Fp>\u003Cp>And this could even be simpler. It's basically just return dot length. Right. And actually maybe we do something like this where we say this is the metric data. The value is data dot get data dot length because we're returning an array there.\u003C\u002Fp>\u003Cp>The timestamp would be, what, new date. So we'll get the current timestamp. And then the service Okay. Maybe we'll open this up in a new tab. Just duplicate this.\u003C\u002Fp>\u003Cp>We'll go in and like our website service, I could pick this off up here. Or if I look in the information, no it's not up there. I'm just gonna use the URL to grab this specific service. Where do we go? Alright.\u003C\u002Fp>\u003Cp>So we'll populate that service ID. And then we have the, what, metric. So we're gonna have a metric attached to this, which we have not populated yet. So let's just do that as well in this second tab. We'll go in and create a metric.\u003C\u002Fp>\u003Cp>We'll call it new account registration. New account registration. Okay. Save and stay. And then I get my ID for this metric.\u003C\u002Fp>\u003Cp>Just paste that in there. Great. So we can kind of see the the format that we want here. And, actually, we don't need the service because that should be on the, the actual metric here. So this is gonna be the metric data.\u003C\u002Fp>\u003Cp>And we're just gonna return that value, return metric data. All right, great. And then the last step of this puzzle would just be to go in here and create that data. So we have formatted the data that we want. We're just going to create metric data.\u003C\u002Fp>\u003Cp>We'll use the metric data option here. We've got our full access. And for the payload here, I'm just gonna do this, where we say format_data in like a mustache syntax. Doesn't seem to like that. Maybe we wrap it here.\u003C\u002Fp>\u003Cp>Okay, all right, so now let's actually try and run this flow to see what we get out of it. All right, so I'm here on my events, Right, we've got 2 events. We should get like a summary for those. So if I go here, now we should have like our Metric Data. Okay.\u003C\u002Fp>\u003Cp>50. No, that's not correct. That's the one we manually created. Let's take a look at this and see where things are falling out. The time stamp.\u003C\u002Fp>\u003Cp>Oh, we're not getting the proper value for the timestamp. Alright. Let's take it. Do we need to save that to ISO string? I think that's maybe what we need.\u003C\u002Fp>\u003Cp>2 ISO string. Is it 2 ISO string? Let's just take a look. 2 ISO string. To, okay, ISO is capitalized.\u003C\u002Fp>\u003Cp>I always forget about that. All right. There we go. To ISO string, new date to ISO string. And we might even do it like this, just to new date.\u003C\u002Fp>\u003Cp>Date. Not data. To ISO stream. See if that gets us where we want to be. So we'll just go back to our events, run this flow, summarize our registrations, see what we get out of it.\u003C\u002Fp>\u003Cp>It's great. Create our metric data. There it is. That's all running, right? So now I could switch this over back to a CronJob.\u003C\u002Fp>\u003Cp>Let's call it Cron. Yep. Set this up on an interval. Save it. And, you know, now in the background, this is going to run.\u003C\u002Fp>\u003Cp>It's gonna summarize all those events for our registrations. Right? So now I got I have the individual events that we can see and, you know, I can potentially act on those individual things when they come in. You know, maybe I wanna send those to Slack for our sales team to follow-up on. But also on our metrics.\u003C\u002Fp>\u003Cp>Right? Within our dashboard, I can see I've got our data for that specific metric as well. And in this case, maybe I wanna clean this up a little bit if I'm looking at it inside the data studio. So I could go into this. We look for our metric inside the metric data.\u003C\u002Fp>\u003Cp>Just use a display for this, call it name. Great. And now if I look, I could see, okay, this is the new account registration. I could see the value for that. And again, like, we could make this dashboard totally customizable.\u003C\u002Fp>\u003Cp>Right? What if we wanted to change this metric that we're actually showing here? What if I wanted to make this chart variable? So we could call this something like Metric over time. And now inside the collection, before we do that, we're going to create a global relational variable.\u003C\u002Fp>\u003Cp>I'm gonna call this key, the metric underscore key, just so I don't get those confused. But we're gonna use the collection metrics here. We're gonna pick a single metric that we wanna use. And we're just gonna say pick a metric to Report on. Great, okay.\u003C\u002Fp>\u003Cp>So we'll add this into our lineup. And here, I want to show just items from the metric that I have selected, right? So how do we make that work? I just basically go in here. I'm going to toggle raw data.\u003C\u002Fp>\u003Cp>I'm going to use that same mustache syntax. We're going to say metric_ key. Alright. Let's take a look and see if this is actually gonna work out how we want it to. Alright.\u003C\u002Fp>\u003Cp>I don't see anything going correctly there. But if I look for new accounts, no. That's not doing what we want. Right? So let's go in and actually fix this.\u003C\u002Fp>\u003Cp>This is not the way we wanna do it. Right? We still wanna report on that metric data, but we want to only filter on the metrics that are selected there. So we're gonna do this instead. We're gonna change that to our filter.\u003C\u002Fp>\u003Cp>And the metric ID has to be equal to the metric key that we use from that variable. So you can use just regular variables like date or string or something like that if you wanted to. Or you can use relational variables. So the other thing that we might set up here is like an AND filter. This should be AND by default.\u003C\u002Fp>\u003Cp>But let's see what we get, right? ARR. This is still not populating correctly. We've broken something. Our date and value fields were not set.\u003C\u002Fp>\u003Cp>So let's test this out one more time. We get no data. If I pick a metric, now we can see our ARR And maybe I wanna change the display template on this as well, where we're just showing the actual name of that. There we go. All right.\u003C\u002Fp>\u003Cp>So now I can see, hey, we're looking at our ARR over time. Or if I wanna check out our new user registrations, we could see those, just 2 on this particular day. Great. And let's just change this a bit, right? We'll do like 6 previously.\u003C\u002Fp>\u003Cp>Let's put that like last Tuesday. Save as a copy. And now if we go back to our dashboard, start to be able to see some of this, right? So we can see the variance there. Great, it's a good way to do it.\u003C\u002Fp>\u003Cp>Alright. So same kind of thing. I could potentially call a third party service using Flows if I wanted to. So, you know, there are multiple ways to do this, but I could go out. And if we use Stripe as a good example, I guess.\u003C\u002Fp>\u003Cp>Running out of time, so Stripe is gonna have to work. We'll just go to Stripe. I'll log in. I'm going to look at just some of my test mode data. Which account am I in?\u003C\u002Fp>\u003Cp>Let's get into one of my test accounts. Boom boom boom boom. Alright. Developer mode. There we go.\u003C\u002Fp>\u003Cp>Okay. What are we gonna need from this? Right? We're probably gonna need a key. API key, create a restricted key, key name, test key.\u003C\u002Fp>\u003Cp>What do we wanna look at? Invoices. Read invoices. Permissions. Okay.\u003C\u002Fp>\u003Cp>Smart enough. All right, reveal test key. I'm going to copy this down. And let's look at Stripe API list invoices. List all invoices.\u003C\u002Fp>\u003Cp>Alright. So let's do this. We are going to get today's invoices. Maybe we just wanna show what we brought in on that particular day. I'm gonna, again, trigger this manually, not requiring a selection.\u003C\u002Fp>\u003Cp>Great. And then inside Flows, I could just add regular HTTP requests here. So we'll say call Stripe. Now a, like a more in-depth way to do this would be to create, custom extensions for things like this, where, you know, you've got more functionality built into it. But I could just go in and get a list of all the invoices here.\u003C\u002Fp>\u003Cp>Let's see what the curl is gonna be for this API. Alright. This is just gonna be a get request. We're gonna use the authorization header. Use bear.\u003C\u002Fp>\u003Cp>And I'm just gonna paste my token that I've got there. Alright. Cool. As far as the request body, do we really need anything there? No, I'm not sure.\u003C\u002Fp>\u003Cp>Let's just go in and test this out. Right? Get today's invoices. Go back to flows. We'll see what ran did we get some actual data.\u003C\u002Fp>\u003Cp>Status, okay. We get a list of the data coming back. And cool, all right. So what I'm going to do, I'm going to just pull this data into, again, Versus Code. And that just helps me traverse it easier when I'm putting this together.\u003C\u002Fp>\u003Cp>Alright. Alright. So today's invoices, you know, let's go in and make a new metric for this. Today's payments, today's invoices, invoices today, daily. That's great.\u003C\u002Fp>\u003Cp>Whatever we wanna call it. We'll add our Stripe service to this, just so we know that's linked to Stripe. We'll hit save. Copy this metric ID. Cool.\u003C\u002Fp>\u003Cp>And a couple of things, right? What are we going to do? We're going to run a script, calculate totals. Okay. How we doing on time?\u003C\u002Fp>\u003Cp>Alright. Let's cheat. GPT, you can type faster than I can. Write a JS function to summarize, to calculate the invoice total for this data, the total of all invoices. Total of all invoices.\u003C\u002Fp>\u003Cp>There we go. So this is gonna spit out a function. It's gonna be a reduce function, but hopefully, it figures out the exact structure of the data. Give it a shot. Right?\u003C\u002Fp>\u003Cp>Alright. So we'll just paste this here. This looks to be correct. Invoice data dot data. Let's just traverse it ourselves.\u003C\u002Fp>\u003Cp>Right? InvoiceData. Data. Amount due. Okay.\u003C\u002Fp>\u003Cp>Calculate total invoices. Let's do amount paid. Paided. Amount underscore paid. Okay.\u003C\u002Fp>\u003Cp>And then we're gonna call this function. Alright. Return, Let's say totals equals calculate total invoices. And the data here is gonna be data dot, what do we call that? Call Stripe.\u003C\u002Fp>\u003Cp>Data dot call Stripe. How are you on time? We're running dangerously low on time. Call Stripe dot data? Call Stripe dot data dot data dot zero?\u003C\u002Fp>\u003Cp>Is that what it's looking for? The InvoiceData, InvoiceData.data. Oh, it's looking for that. Okay. All right.\u003C\u002Fp>\u003Cp>That's what we'll do. We'll return payload to update this. So, oh, forgot to close that off. Okay. We're going to return, what, a symmetric.\u003C\u002Fp>\u003Cp>Okay. What's gonna be our metric? There's our there's our North Star metric. There's the ID for that. I think I'd already copied that before.\u003C\u002Fp>\u003Cp>Okay. Then we've got the value equals the totals and the timestamp equals new date to ISO string. Alright. Can we actually get this first shot? I don't know if we will or not, but we'll try it out.\u003C\u002Fp>\u003Cp>Create Metric. Okay. Calculate totals. That's what we're going to stuff here in our payload. And we will run this thing on metric data.\u003C\u002Fp>\u003Cp>Full access. How we doing? Time, 42 seconds left. We are chugging along. Alright.\u003C\u002Fp>\u003Cp>Get today's invoices. Did this actually create our metric for us? Yes. 30,000. There it is.\u003C\u002Fp>\u003Cp>Bada bing bada boom. If we go into our dashboard really quickly, we should even be able to report on this, today's invoices. We could see that value of $30,000 even though it's not inside the well, we don't have any other data points, so there's not, like, a a necessarily a chart here to show. But within 10 seconds left, bada bing bada boom, we're calling that a win. We have managed all of our metrics.\u003C\u002Fp>\u003Cp>We have build usable dashboards or built usable dashboards, and we have integrated Stripe as a third party service to pull in our KPI dashboard. Right? This is a little underwhelming here, as far as, like, the actual dashboard. That would be pretty easy to flesh out, though, as soon as I started populating all of my data. So for me, that would be my next steps.\u003C\u002Fp>\u003Cp>I've hoped you'd enjoyed this episode. Hope to see you on the next one.\u003C\u002Fp>","Hi. Welcome back to another episode of 100 apps, 100 hours. I'm your host Brian Gillespie, developer advocate here at Directus. And if you're new to the show, what do we do? We rebuild or build some of your favorite apps, ideas, suggestions in 1 hour less or publicly fail trying. Sometimes spectacularly fail, but hopefully not in this episode. What are we gonna be building? Cover that in a moment. There are 2 rules if you're new to the show. 1st and foremost, there are only 60 minutes to plan and build. No more, no less. Get what you get. You don't throw a fit, as I like to say to my kids. Rule number 2 is you use whatever you have at your disposal, whether this is AI, GitHub Copilot, Tailwind, CSS, UI libraries, even past projects. Right? Whatever we need to get the job done. So back to what we're building, a Data Box clone. So you may have heard of Data Box, you may not have. This was a suggestion from one of my colleagues. Data Box is an well, it's built as an easy to use analytics platform for growing businesses. Basically, what I see here is a dashboard. We've got multiple sources of data for that dashboard. I see them speak of a a centralized source of truth, which is one of the strengths of Directus, at least in my mind. We get to be the the hub for all of your data, that you get APIs to work with. So that's why I like this type of project for Directus. The bind line here, I'm just calling it KPI dashboard type thingy. Right? We're gonna ingest some data. We're going to display it on a dashboard, report on it, make that whole process easier. Sounds great. Let's dive right in. 60 minutes on the clock, and away we go. So looking at the Databox website, right, we've got these different sources of incoming data. We're reporting on those in, like, a time series or just a a metric, maybe a percentage, it looks like. But one of the first things I'd like to do is just study the documentation or the API references for a service. So I'm just gonna search for the Databox API. Looks like we've got that here. If I zoom in, we'll just kinda browse through this. Right? So in my mind, it created a couple of similar things in the past where we're storing events and metrics. We've even got some of this metric functionality inside our our own dashboard that we use internally for things like doc feedback. But in this particular case, I've not really built like a KPI dashboard. So let's take a look. We've got a see a token in the the data box website, that is the unique identifier that points to a storage container within their warehouse. It's like a bucket for your data. Looks like that's all that maybe, like, the different services. I see the metric here. Alright. So the metric is a quantitative measure of performance. That's a pretty good definition of that. Basically, all metrics are going to be storing numerical values. Alright. That's a a key part of this. They define that, that metric has a key and a display name, and then we have some data. So here's how we send the data to DataBox. Looks like we're referencing that key there, and then we give it a value. And what else? Where is the date information coming from? By default, the current date and time will be used to store information about the events. So when you send an event, it will automatically store the current time, unless you maybe change that. Got it. Okay. So at a high level, I understand, kind of, what they're doing here, just building a nice UI on that. On our side for the setup here, I've just got a blank Directus project. Nothing in here, no dashboards, any of that. We'll we'll certainly dive into it. But for now, let's dive into Figma and actually sketch this thing out. Right? One of the the big things that come up in my mind is the difference between metrics and something like events. Right? So metrics are like an aggregation. Could be of of, like, potential events or other data. Right? So a metric is typically on, like, well, I I won't say typically. There's all all types of metrics. You know, on, like, a a website or a server, you might have, like, time to first byte, API response time that you're tracking as a metric, and maybe you wanna report on that every few seconds. Right? On things like billing data, you know, you may have, like, let's say, AAR, our MRR, new users, monthly users, etcetera, you know, something like that. On the CRM side, you probably got pipeline, number of new deals, qualified leads coming in, that you're tracking. So those would all be, examples of metrics. And then the events are the actual things that happen, not on the schedule, I guess. On the schedule. Things that happened, basically. Let's call it that. Right? This could be when a new user signs up for a service that have happened. Things that have happened. Great. Alright. So in in a system like this, it might be helpful to have both of those. Right? Because one of our metrics could be aggregating all the events that happened for that particular day. Page views, you know, video views for Directus TV. Lots of lots of opportunity there. And then, what else are we gonna have on this? Right? Data sources, sources of data. Data sources, this would be, you know, our CRM, our, websites, analytics, billing, accounting, etcetera. Whatever those sources are, it looks like Databox has a lot of those built in. You know, we'll probably be just basically creating our own at this point. But this feels pretty good as far as functionality is concerned. I think there's a there's a pattern that I've used in a previous project. It's It's actually our directus dot pizzademo. A really cool demo if you wanna check it out. Just a a look at, like, a a full fledged direct to this project in in many different facets. There's 5 or 6 different use cases slammed into this thing. But we've got, this pattern of, like, metrics where I've got, my name and description, and I've got, I like some prefixes and suffixes that I could show inside the UI or on a front end, and then I have my data. So instead of shoving it all in one table, kinda separate that out. I kinda like that pattern, so let's do that. Do metric underscore data. Looks good. Alright. I'm just gonna pull this up to the side. Again, this is the use whatever you have at your disposal part. Right? So I've got my Clean Directus project over here. We're gonna create a couple of new tables. Let's do metrics first. So that's the one we're working on here. Do we need a status for this? Probably not. Do I wanna know when it was created, who it was created by? Yeah. Maybe. Maybe we wanna add a sort for it. No big deal. Alright. Then we're gonna give this a name. Great. We can give it a, like, a key if we want, as well. So if I want to have a unique identifier beyond like a UID, You can also, like, set these up to be manually generated strings for the primary key. Obviously, you've gotta enter that every time, but I can go in here and make this string unique. And if we're looking for a key, you know, we might want to make that URL safe and just use the input option to slugify it. Alright. And then we've got, like, a description of the metric. Maybe I'll go in and add a note for this. What does this metric measure in detail? Great. And I've got prefix and suffix on here. Not sure we necessarily need those. We'll just keep that off for now. But basically, we're gonna make sure we unhide this ID. Need this ID when sending data, sending metric data. That'll be the collection that we create next. Right? So we've got a name, we've got a key, we've got a description. Right? We've got API response time over here just as a a reference, but, what am I looking at initially? Let's call this, like, ARR. Alright. Tracks changes to annual recurring revenue over time. Great. Alright, so we got a metric in there, looking great. Now we need a place to store that metric data. Right? So applying that same pattern, I'm just gonna fade that away. Just close this out. Alright. We're gonna have a metric data table. Metrics, data, metric data could go either way. Naming stuff, always the hardest part of these episodes. Right? So on this one, we've got, like, the optional fields here, just shortcuts for things like recording a timestamp whenever a thing is created. Now one of the things that I like to do on something like this is just remove any ambiguity and call it a time stamp, or create it at, but you can change those on the fly as well. So I'm gonna unhide that. For our metric data, we're also gonna have a value for that data. Right? This is gonna be let's go with a decimal on this, just because we might wanna store something with the decimal place. It's not always integers. Great. We'll choose type decimal. And if I wanted to flesh this out even further, I could go to the advanced settings and, like, control my precision and scale if I needed to. If I go into the display here, I can also auto format this, which Directus will try to guess as to what format this should be in and apply some styling and, conditional formatting for you, make that look nice. Alright. So we got a timestamp. We've got metric data. Let's link these things together with a many to one relationship. So Directus makes it super simple. To create these relationships, we're just gonna call the key for this field inside metric data. We're gonna call that metric, and then we have our metrics over here. So that's the related collection. Now I could hit save here, but I'm gonna open up in the advanced settings and I'm gonna go to the corresponding field under the relationship tab. And here I can go ahead and create that inverse relationship in the metrics table. So I'm just gonna call this data just to keep it simple and Directus will show me, hey, we're gonna create this field for you. So if I delete a metric, you know, maybe we do wanna delete the metric data just because it doesn't make so much sense without the identifier and, you know, the name and the description of that metric. Alright. So this looks pretty good. We'll just, make that half with clean this up a little bit. Right? What else do we have? We have events incoming as well. Right? So, again, the difference between events and metrics, like, a a metric is something that is going to be at a specific point in time. An event is something that that happened. Right? So a a ARR doesn't happen. Right? A user subscribes and that has an effect on ARR. So also starting to sound like a pilot or a pirate in this episode, not a pilot. So we got an event. Let's see. We'll, again, do the same thing with the timestamp. The events are probably not going to to change much, so maybe we'll just call, like, created by, like, the user here. That's cool. Alright. And for the event, let's see. What are we gonna have on the event? We'll probably have something like we have on, we'll have a name for the event. Maybe that's just a string. We could have a key for that event. Key. What kind of event key is this? We will go back in, make sure this is slugified, make it URL safe. We got the user. We probably wanna show that in this case. Even though those are hidden by default, we wanna show the timestamp of this event. And then, you know, you might have, like, a a JSON field to pass metadata. So in this case, I'm just gonna pick the code interface, choose the JSON type, and we'll just call it metadata in case I wanna store additional stuff on the events. Alright. That's looking nice. And then, you know, to bring this all home, let's add some icons. But, one of the other things that I'm gonna do is add the sources just in case we could we wanted multiples. Like, I'm imagining if you got your CRM data, you're probably looking at 2 or 3 different metrics within that. Or, like, if you've got your Stripe data, you're probably checking your ARR, your MRR, number of new sign ups, your churn rate, things like that. So, there's maybe one more level of abstraction here that we're gonna go for. Alright. So we've got events. Let's just get chip extraction. That looks good. Metric data, we could probably hide that if we want to, or we just call it data, see what we got. There we go. Data usage. Good enough for me. Alright. Great. So let's add that sources, or this could be something like services. Maybe that makes more sense in this particular case. We'll use the UID. You know, we'll see when users update these services. We'll give it a name, and then we can link, a service to, you know, both of these, like events and metrics. So we'll link, use the mini took one here. We'll call this the service that is related to this metric. And if I flip over to that advanced section, again, I could go in and add the different metrics that are associated with that service. Great. And if we go to events, same thing. Right? I might call this service Services. K. Go to our relationship. We'll create that extra corresponding field. It show the related values in the display template. And now we've kind of got this whole thing fleshed out. Right? We've got ARR, the service here in this case. And maybe this is Stripe as the service. Great. Okay. Looking great. Love it. Love it. You may we do some drag and drop to kinda change the stuff around. See the ID. Don't really need it. But let's go in and actually, you know, populate some of this data now that we've we've got something in here. Right? So I'm gonna go in and create something here, but you could see I can't change my time stamp, so I could manually change that. Let's see. The metric we're using here is ARR, so this is gonna record the current value because of the way that time stamp field is set up, or the current date and time. I'm sorry. We'll do something like $1,998. That's that's what we're making ARR right now in this particular app. Not doing so well, but, if I'm manually throwing these in here, I can't edit this. So two ways I could populate this data. I could use the API to quickly, like, shove data in here for this specific metric if I wanted to. That's available to me. Right? Or I could, you know, remove the restriction here on this to basically let me edit that value if needed. And then you could see on create, there's kind of this save current date and time thing going on as well. So I might do both here just in case we need to edit that. I'll change that. Save it. Cool. Alright. And then, you know, if we change this again, we go to change it to 8:8:8, maybe last Thursday. That's what our ARR was. And you can see that time stamp was populated, so I could turn off that behavior. But if I just hit the save button twice here, we'll get some actual data from this. But if I wanted to do that via the API, that's all ready to go for me as well. The only thing I need to do is either use a static token or make that publicly available. Right? In this case, I might just create a static token. So I'm gonna go into my user directory. We'll say, scroll down to the bottom here. We're gonna create a static token to use as we're calling this. And I'm just gonna pull up a little app that I've got called Bruno. It's kinda like Postman, except it is offline first. So Postman, you have to be logged into their service. Bruno is, I think it's open source and offline first, basically. Like, it sorts all of your stuff locally. So, what is the name of this? This is metric data. Right? And we've got our metric that we're using here. So I could copy this. And And as far as the payload, we should just need to pass, like, a timestamp and a value. So this is using the, timestamp value. So we've actually got the time zone in here as well. But if we go in, we're just gonna do 8055 post metric data. Let's set this up as a post request. My headers, I've got, like, a Directus token set up already for this. I'm just gonna replace that value. Is it secret? Secret for now. There we go. We'll save. Changes saved successfully. And let's actually just see if we can get the Directus data first. Says my token is incorrect. Correctamundo. That's not great. Let's try this again. Did I actually save the user? Maybe I saved the wrong user. I think there's only one user, but alright. Delete. Save. There's the value. I'll hit save. Try this one again. There we go. So now we can see our actual metrics. Right? That's the beauty of Directus. Take any SQL database, wrap it, gonna sit alongside of it and give you APIs in order to access it. But now I could go in and I should be able to post this with just like a raw JSON body. Got value of, what, 777. The metric, we could just copy that. Right? That's the unique identifier for that metric. I could have set that up to be, again, a manually entered string. That might have been easier for this. And then we're gonna add a time stamp to this. So if I populate the time stamp, it should do that for me. Let's just dig back in time here and do that. So we'll send that one. Now if I refresh, it should have a couple of metrics, 777 generated less than a minute ago. Right? So it's not saving that timestamp. Maybe we just need to actually turn that off for now. So we go into our metric data. Just turn this off. Where are you? Save current on create, do nothing. Alright. So now if we try this again, you can see that the date that's being stored is that 22nd that we had. Just mix this up a little bit. 555. Dig back into time. Great. Alright. Sweetness. Okay. So now we got some data. Right? Let's build a dashboard out of this data. So we're gonna go into the insights module inside Directus. The beauty of insights is that I can build dashboards with low code, no code. Really, it's no code unless you start needing to adjust some of the the JSON within the filters, and use some dynamic variables. But beyond that, we could create these dashboards really easily. Right? So we'll call this the our dashboard, and we've got a do we have an no. Let's do it like a chart. Chart. See what we got. Yep. There we go. Got a chart going on. Let's start adding to this. Right? So if I look if we do I have the data box up? Let's just see what they've got. Like, first on the list here is, like, revenue. I see new customers, like, website sessions, all pretty standard stuff. We're we're just gonna go in and let's just show, like, a a metric. Right? Like, what is our current ARR? If I wanna send this out to, you know, one of my team leaders or, a CEO or, you know, my manager, like, how how are we gonna set this up? So in this case, the collection that we're working with is the actual metric data. That's where we're storing the values that we wanna see. The field that we wanna report on is the value itself. And the aggregate function here is basically, like, what are we gonna run to get this specific data? In this case, you know, I really just want, like, the last value. As of, like, the last value in that series, what's our ARR currently? So I could do this, and the sort field here would probably be timestamp. And then the filter, basically, like, of all the metric data items in there, how do we select just the ones for ARR? So I'm gonna go in. We've got our metric thing here. You know, I could go by the key here, that the key is equal to arr. Hopefully, I set that up, we'll find out in a moment. And then I'm just gonna scroll down. We'll give this a name, like current ARR. Great. And we could see that. So we got 777. That's the last value in that series. Right? Maybe we add a prefix, so we get dollar sign. But let's just check that. And if we look here, 4 minutes ago, we have 777. Now if I were to go in and delete that value, right, that should become 999 when we go to the dashboard. Okay. All is right with the world. That's that's looking nice. So this is a little underwhelming though. Right? We wanna see more data than this. So actually, let's let's add some time series data to this. Let's track those ARR changes over time. So the time series is good for this. You could also use the line chart, that will allow you to get multiple groups. So if you wanted to, kinda look at groups of users within a a certain time period over, like, a certain set of events, that sort of thing. So, again, we're going to choose Metric Data. The aggregation here, if I've got 2 values for a given day. Right? This is probably the way that I'm gonna report on this, is by the by a given day. Do I average those values together, or do I do the minimum or the maximum? Let's just average them together. You know, one of the things that we might set up is just a a flow or an automation to go in and run those metrics for us on a daily basis. For the group precision, we'll just use day for this. Date field, we got our time stamp. And when it comes to the date range, this is, a little tricky in that, like, if I select one of these, it's always gonna be that specific date range unless I do something like automatic based on data. Or, you know, I could fill this in with other and make it like a dynamic variable where I could pick past 30 days, past 14 days, etcetera. In this case, what I'm gonna do, I'm gonna just set that to be automatic based on the data, and the value field here is gonna be the value. Plain and simple. We'll do ARR over time. Great. Looking nice. Okay. So now we can actually see those changes. And again, this is a full drag and drop builder. One of the nice things here is you could see if I connect these 2 together, the little border radii, border radius, those all they connect nicely. It's very subtle detail, but very helpful. Alright. So now we got a chart. You know, we could make this type of thing dynamic as well. It looks like the Databox dashboards are relatively fixed. I see like month to date for a lot of these. But let's say if we wanted to zoom in on a particular timeline. Right? You know, hey. This is only data for May, but, you know, maybe I wanted to look at last month's data. So what we can do, you can make these things dynamic using variables, like either the global relational variable, which allows you to select a specific thing. In this case, maybe you wanna report on, a specific metric. Or like a global variable here could be based on time. Right? So we could do something like this where we have variable key. We'll do date from, and that'll be a time stamp. Great. Use the date time interface. In the US, We don't use that 24 hour clock very often. Alright. So let's call this date from. Alright. So now we get like a panel here. We could duplicate this thing. We can call it date 2. So in the variable key, we'll call it date 2. I'll make sure I change my panel header just so I don't confuse any users of this dashboard. But we're all about empowering people. You know, if you've got, like, a ton of internal data, you could potentially and it's in a SQL database. You could connect Directus to it, get APIs, and get a dashboard experience out of the box, right, that that all members of your team would be able to use. So now that we have created those relationships or those variables in this case, these are not relational variables, they're just global variables. I can use the names of those inside my other data. Right? So current ARR, we're always looking for that to kind of show the same data. Right? What is the current ARR? If we're talking current, it's always x. But in in the case of like over time or like showing a list of records, maybe I want to filter that for a specific date range. So I could do something like this where I say the timestamp, I've got these filters here that you can see. But we also have greater than or equal to. And I could do something like this, is between, and I use a mustache syntax. And now we can use those date variables. Okay? So now I'm not seeing anything, right? What do I change this to? For default value, we have some dynamic variables here too. So let's say, for the date from, maybe we change it to the start of this year. So just back up to Jan 1. That's great. And then for date 2, maybe we always wanted to show, like, the latest value. So we could just go to something like dollar sign now, which should populate the current time stamp in that particular field. If I just refresh, now you could see what we've got going on here. So if I back this up, like let's go to like May 23rd. Okay, so now you could see like the scope of my data changes within the chart. That's how I can make these dashboards dynamic. Really, really neat feature. Right? Alright. So going back to our our board here. We've got some metrics. We've got metric data. We've got the sources planned out. We've got events. We haven't populated any of those. But one of the things that I kinda jumped the gun on here was what do we actually want out of this specific application. Right? We wanna manage all of our metrics in one place. That's right. Directus becomes the single source of truth for all of these particular metrics that we are managing, that we have under our control. And what else? So we're gonna build a usable dashboard for these metrics with usable dashboards. Yeah. We could slice this a couple different ways. And then as, a larger goal, you know, maybe we wanna integrate some third party service. 3rd party service. Alright. So let's take a look at, like, the events side of it. Right? Events would be very helpful, I created a new account. Right? So let's just call this 1, created a new account. Alright? So let's just call this one, like, new account registration. Yeah. Seems great. New account and registration. I've got metadata here that we could store. It's great. We'll just pick the service. You know, maybe that's we create a new one. Can we create a new one from here? We can. Let's just call it website. Great. Okay. And cool. So now we got an event. The name is no account registration. It happened at this particular time with this particular user. I could go in and maybe I just create, like, a a fake user as well to populate some of these events. Fake user Fake user 2. That's a real original name. Right, Brian? Yeah. Okay. Alright. So we got another event. Maybe we want to, in this case, I'm gonna just disables the read only part of this. And oh, I didn't save it, did I? Disable Disable, just in case I need to change those items. Alright. And the difference here is we're gonna assign this to fake user. Let me go up to the 3 dots. If you are pretty new to Directus, this is a helpful hint here where you can use these keyboard shortcuts, command s to save and stay. Or let's say you wanna create a new record. You could save and create new. In this case, I'm gonna save as a copy. So we're not gonna touch the original event. We're just gonna create a copy of it, and I'm gonna assign this to fake user, account user registration, blah blah blah. If I was doing this on my front end, I would certainly be passing, like, some metadata of, yeah, what account let me maybe some of those account details that would be relevant. Of course, not any personally identifiable information. Don't wanna store any of that. And then we change the timestamp, and we change that to May, etcetera. Okay. Alright. So I've got a couple events. Right? And now, let's say I needed to run a report that shows me all of the new registrations within a certain time period. Right? Now I could go in and have, like, basically set up a a chart based on that. Right? And I could use this event data to get a list of those events or kinda aggregate those. But how would you track the the changes between those things? So, you know, how do I show on a dashboard how many new registrations there were 2 weeks ago versus today? Right? Or how can we analyze those things under time and compare them to other details? So in that case, maybe we want to convert those events over into metrics. One way you could do that is with direct as flows. Some automation inside the system. Right? So every day, maybe I want to go in and summarize all this information and store it so we can make quick work of analyzing the data. So in this case, let's just set up an automation. Right? We're going to summarize registrations. Sounds good. Alright. For an icon, You know, my recovering designer in me always has to have an icon. And for the trigger here, we're gonna trigger this at regular points in time. I always get hung up on the Kron syntax. Kron syntax for every minute. Let's see what we come up with for this. Every minute. Okay. You're probably gonna only set this up to run once a day. I'm gonna run this once a minute just so we can, like, get a good look at it. And you could even, like, manually trigger this sort of thing if you wanted to. But in this case, we'll set that up. And now what do we wanna do with this? Right? So if we're gonna summarize this registration data, we'll go in, and we need to get that registration data. So we'll say get data, and I could go in and hit read data. So we're gonna get the items from the database. Let's use full access, which gives it full permissions instead of the ones from the user, from the trigger, or the public role, in this case, like a cron job. We'll go to our metric data, and we can either specify the specific IDs we wanna find, or we can set up a query. So these are just the standard query, like, global query parameters that we support via the Directus API. Just flesh this out. Where are, which metric are we looking for? We want the metric. So here, we're at our metric data. I can also dig deeper into the actual related fields. Right? So the related field, metric key equals let's say equal no. Metric key. Yeah. I think it would be something like this. We'll see if this actually works. Key equals arr. No. Okay. Alright. So we look for the filter. This is a global parameter. So within the metric data, we look at the metric, which normally is just stored a UUID for that. But we can also go into the related fields here and say the key of this metric is equal to ARR. Is that actually gonna run? We'll see. Right? So we can see this already ran twice. If I wanted to to sit around for another minute, we can take a look at it, see what happens, or, you know, I could manually trigger this thing as well. So let's do that. We'll just go in metric data. Cool. Summarize registrations. Alright. I'll hit save. And now if I go into metric data, I can also run this thing. I forgot to turn off the need to select something. But now if I run that, we could see if we actually got the data that we were looking for. Great. I can see this is the data that we need. And just to make sure that's working as intended, I'm gonna just add another one here and maybe maybe just leave off a metric to make sure we should have 4 values inside that flow instead of 5. So, again, I'll just run this flow. Great. I'll go back and look at the logs really quickly. And I can see I'm only getting 4 values here. That's great. Cool. Alright. So now maybe I want to summarize those. But this is not our I'm actually looking at the wrong thing, aren't I? I'm getting the data from our metric data and not our events. So we need to make sure we actually change that up. Just an oversight here on my part. So in this case, we're gonna do the key equals new user registration. Alright. Now let's test that again just to make sure this is actually gonna run. K. Having a look. Alright. No. Are we getting our data? Did we call it something else in our events? New account registration. Always remember what you name things. It's a challenge. All right. Just paste that key here inside our query and hit save. Alright. So, again, maybe we wanna change where we're actually running this from to. Go to Events. Alright. Lots of back and forth here. So I'll select all these just because of the way that this flow was set up. Normally, I would set this up to be a cron job like we had before. But now if I check the logs, we could see we've got all of our events here. That's great. Now we are going to collect all those events and save them as a metric, right? We could also specify, You might want to use our dynamic filters as well, right? So let's actually just save this first. We'll come back to it. So we're gonna format this. Let's just do a quick run script. We'll call this like format data. And if we see here, we've got, this is our actual data. All right, so I'm gonna just copy paste this into Versus Code here, so I've got it. And then inside this operation, this run script operation, we can run arbitrary JavaScript to do whatever we want. So in this case, we are just going to actually combine these two pieces of data. Like, summarize how many we just want to get a count of all of the events for this, right? So something like this where we have, we're gonna get the data. We call it get data. So we could say that const events equals data dot get data. As each operation within a flow runs, it appends the data that operation returns under this key. So after this step, the results I'd be able to access via data dot format data inside a run script. But in this case, we're just gonna do const events. And then maybe we just return the the length of those, I think. Right? Simple as that. We don't have any values that we're like summing up here. We're just going to return events dot length. And this could even be simpler. It's basically just return dot length. Right. And actually maybe we do something like this where we say this is the metric data. The value is data dot get data dot length because we're returning an array there. The timestamp would be, what, new date. So we'll get the current timestamp. And then the service Okay. Maybe we'll open this up in a new tab. Just duplicate this. We'll go in and like our website service, I could pick this off up here. Or if I look in the information, no it's not up there. I'm just gonna use the URL to grab this specific service. Where do we go? Alright. So we'll populate that service ID. And then we have the, what, metric. So we're gonna have a metric attached to this, which we have not populated yet. So let's just do that as well in this second tab. We'll go in and create a metric. We'll call it new account registration. New account registration. Okay. Save and stay. And then I get my ID for this metric. Just paste that in there. Great. So we can kind of see the the format that we want here. And, actually, we don't need the service because that should be on the, the actual metric here. So this is gonna be the metric data. And we're just gonna return that value, return metric data. All right, great. And then the last step of this puzzle would just be to go in here and create that data. So we have formatted the data that we want. We're just going to create metric data. We'll use the metric data option here. We've got our full access. And for the payload here, I'm just gonna do this, where we say format_data in like a mustache syntax. Doesn't seem to like that. Maybe we wrap it here. Okay, all right, so now let's actually try and run this flow to see what we get out of it. All right, so I'm here on my events, Right, we've got 2 events. We should get like a summary for those. So if I go here, now we should have like our Metric Data. Okay. 50. No, that's not correct. That's the one we manually created. Let's take a look at this and see where things are falling out. The time stamp. Oh, we're not getting the proper value for the timestamp. Alright. Let's take it. Do we need to save that to ISO string? I think that's maybe what we need. 2 ISO string. Is it 2 ISO string? Let's just take a look. 2 ISO string. To, okay, ISO is capitalized. I always forget about that. All right. There we go. To ISO string, new date to ISO string. And we might even do it like this, just to new date. Date. Not data. To ISO stream. See if that gets us where we want to be. So we'll just go back to our events, run this flow, summarize our registrations, see what we get out of it. It's great. Create our metric data. There it is. That's all running, right? So now I could switch this over back to a CronJob. Let's call it Cron. Yep. Set this up on an interval. Save it. And, you know, now in the background, this is going to run. It's gonna summarize all those events for our registrations. Right? So now I got I have the individual events that we can see and, you know, I can potentially act on those individual things when they come in. You know, maybe I wanna send those to Slack for our sales team to follow-up on. But also on our metrics. Right? Within our dashboard, I can see I've got our data for that specific metric as well. And in this case, maybe I wanna clean this up a little bit if I'm looking at it inside the data studio. So I could go into this. We look for our metric inside the metric data. Just use a display for this, call it name. Great. And now if I look, I could see, okay, this is the new account registration. I could see the value for that. And again, like, we could make this dashboard totally customizable. Right? What if we wanted to change this metric that we're actually showing here? What if I wanted to make this chart variable? So we could call this something like Metric over time. And now inside the collection, before we do that, we're going to create a global relational variable. I'm gonna call this key, the metric underscore key, just so I don't get those confused. But we're gonna use the collection metrics here. We're gonna pick a single metric that we wanna use. And we're just gonna say pick a metric to Report on. Great, okay. So we'll add this into our lineup. And here, I want to show just items from the metric that I have selected, right? So how do we make that work? I just basically go in here. I'm going to toggle raw data. I'm going to use that same mustache syntax. We're going to say metric_ key. Alright. Let's take a look and see if this is actually gonna work out how we want it to. Alright. I don't see anything going correctly there. But if I look for new accounts, no. That's not doing what we want. Right? So let's go in and actually fix this. This is not the way we wanna do it. Right? We still wanna report on that metric data, but we want to only filter on the metrics that are selected there. So we're gonna do this instead. We're gonna change that to our filter. And the metric ID has to be equal to the metric key that we use from that variable. So you can use just regular variables like date or string or something like that if you wanted to. Or you can use relational variables. So the other thing that we might set up here is like an AND filter. This should be AND by default. But let's see what we get, right? ARR. This is still not populating correctly. We've broken something. Our date and value fields were not set. So let's test this out one more time. We get no data. If I pick a metric, now we can see our ARR And maybe I wanna change the display template on this as well, where we're just showing the actual name of that. There we go. All right. So now I can see, hey, we're looking at our ARR over time. Or if I wanna check out our new user registrations, we could see those, just 2 on this particular day. Great. And let's just change this a bit, right? We'll do like 6 previously. Let's put that like last Tuesday. Save as a copy. And now if we go back to our dashboard, start to be able to see some of this, right? So we can see the variance there. Great, it's a good way to do it. Alright. So same kind of thing. I could potentially call a third party service using Flows if I wanted to. So, you know, there are multiple ways to do this, but I could go out. And if we use Stripe as a good example, I guess. Running out of time, so Stripe is gonna have to work. We'll just go to Stripe. I'll log in. I'm going to look at just some of my test mode data. Which account am I in? Let's get into one of my test accounts. Boom boom boom boom. Alright. Developer mode. There we go. Okay. What are we gonna need from this? Right? We're probably gonna need a key. API key, create a restricted key, key name, test key. What do we wanna look at? Invoices. Read invoices. Permissions. Okay. Smart enough. All right, reveal test key. I'm going to copy this down. And let's look at Stripe API list invoices. List all invoices. Alright. So let's do this. We are going to get today's invoices. Maybe we just wanna show what we brought in on that particular day. I'm gonna, again, trigger this manually, not requiring a selection. Great. And then inside Flows, I could just add regular HTTP requests here. So we'll say call Stripe. Now a, like a more in-depth way to do this would be to create, custom extensions for things like this, where, you know, you've got more functionality built into it. But I could just go in and get a list of all the invoices here. Let's see what the curl is gonna be for this API. Alright. This is just gonna be a get request. We're gonna use the authorization header. Use bear. And I'm just gonna paste my token that I've got there. Alright. Cool. As far as the request body, do we really need anything there? No, I'm not sure. Let's just go in and test this out. Right? Get today's invoices. Go back to flows. We'll see what ran did we get some actual data. Status, okay. We get a list of the data coming back. And cool, all right. So what I'm going to do, I'm going to just pull this data into, again, Versus Code. And that just helps me traverse it easier when I'm putting this together. Alright. Alright. So today's invoices, you know, let's go in and make a new metric for this. Today's payments, today's invoices, invoices today, daily. That's great. Whatever we wanna call it. We'll add our Stripe service to this, just so we know that's linked to Stripe. We'll hit save. Copy this metric ID. Cool. And a couple of things, right? What are we going to do? We're going to run a script, calculate totals. Okay. How we doing on time? Alright. Let's cheat. GPT, you can type faster than I can. Write a JS function to summarize, to calculate the invoice total for this data, the total of all invoices. Total of all invoices. There we go. So this is gonna spit out a function. It's gonna be a reduce function, but hopefully, it figures out the exact structure of the data. Give it a shot. Right? Alright. So we'll just paste this here. This looks to be correct. Invoice data dot data. Let's just traverse it ourselves. Right? InvoiceData. Data. Amount due. Okay. Calculate total invoices. Let's do amount paid. Paided. Amount underscore paid. Okay. And then we're gonna call this function. Alright. Return, Let's say totals equals calculate total invoices. And the data here is gonna be data dot, what do we call that? Call Stripe. Data dot call Stripe. How are you on time? We're running dangerously low on time. Call Stripe dot data? Call Stripe dot data dot data dot zero? Is that what it's looking for? The InvoiceData, InvoiceData.data. Oh, it's looking for that. Okay. All right. That's what we'll do. We'll return payload to update this. So, oh, forgot to close that off. Okay. We're going to return, what, a symmetric. Okay. What's gonna be our metric? There's our there's our North Star metric. There's the ID for that. I think I'd already copied that before. Okay. Then we've got the value equals the totals and the timestamp equals new date to ISO string. Alright. Can we actually get this first shot? I don't know if we will or not, but we'll try it out. Create Metric. Okay. Calculate totals. That's what we're going to stuff here in our payload. And we will run this thing on metric data. Full access. How we doing? Time, 42 seconds left. We are chugging along. Alright. Get today's invoices. Did this actually create our metric for us? Yes. 30,000. There it is. Bada bing bada boom. If we go into our dashboard really quickly, we should even be able to report on this, today's invoices. We could see that value of $30,000 even though it's not inside the well, we don't have any other data points, so there's not, like, a a necessarily a chart here to show. But within 10 seconds left, bada bing bada boom, we're calling that a win. We have managed all of our metrics. We have build usable dashboards or built usable dashboards, and we have integrated Stripe as a third party service to pull in our KPI dashboard. Right? This is a little underwhelming here, as far as, like, the actual dashboard. That would be pretty easy to flesh out, though, as soon as I started populating all of my data. So for me, that would be my next steps. I've hoped you'd enjoyed this episode. Hope to see you on the next one.","454d776b-08f8-426e-a630-93af2026cbc5",[2858],"9c1ee329-fcd4-4b1c-9d29-55c8ae9b932e",[],{"year":978,"number":2062,"id":2311,"episodes":2861,"show":2862},[2313,2314,2315,2316,2317,2318,2319,2320,2321,2298],{"title":322,"slug":323},{"id":1844,"slug":2864,"vimeo_id":2865,"description":2866,"tile":2867,"length":2280,"resources":12,"people":12,"episode_number":13,"published":2868,"title":2869,"video_transcript_html":2870,"video_transcript_text":2871,"content":12,"seo":2872,"status":19,"episode_people":2873,"recommendations":2877,"season":2878},"naz-delam","948642030","In this episode of Trace Talks, engineering leader Naz Delam shares her journey to LinkedIn, highlighting key leadership lessons she's learned along the way – from challenges of leading teams during tech layoffs to empowering teams through autonomy and ownership.\n\nNaz also offers practical advice on handling layoffs, continuous learning, and effective networking. \n\nThis episode is a MUST-LISTEN for any aspiring leaders in the tech industry.","64ce77c4-fc3c-4b5b-b344-771d6b867731","2024-05-30","Naz Delam, Software Engineering Manager at LinkedIn","\u003Cp>Speaker 0: Oh, hey. It's a visit from the cat. I was hoping it would show up.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. It has my cat, Christopher.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I was hoping it would show up. Yeah. Hey, everyone. Welcome back to another episode of Trace Talks.\u003C\u002Fp>\n\u003Cp>Today, we have Naz. Naz, first off, congratulations on becoming a naturalized US citizen today. That's very exciting, very exciting. Sometimes I take that for granted. So it's very exciting when people I know have that happen.\u003C\u002Fp>\n\u003Cp>But, yeah, we'll we'll let you introduce yourself. Why don't you let the people know just a brief background into who you are, maybe just a brief introduction into where you are, and then we'll get into the conversation.\u003C\u002Fp>\n\u003Cp>Speaker 1: Absolutely. Good morning, everyone. Good afternoon wherever you are in the world. My name is Naz. I am currently an engineering leader at LinkedIn, leading media, and, previously worked as as a engineering manager and software engineer at Netflix.\u003C\u002Fp>\n\u003Cp>My background to tech was very traditional, so studied bachelor and master's, in computer engineering and afterwards, systems engineering. So, yeah, it's been, 10 plus years that I am in San Francisco Bay Area, and I got my first job here right after school.\u003C\u002Fp>\n\u003Cp>Speaker 0: Awesome. Yeah. Thanks so much for that quick intro. As this is kind of a a a podcast focus on leadership, I figured we'd start on that leadership trend. Right now, obviously, in in tech and even starting outside of tech, there's a lot of organizational changes happening and tech layoffs and everything that's, you know, kind of on the front the front of everyone's mind.\u003C\u002Fp>\n\u003Cp>So I guess let's let's kinda start off on, an interesting take. You know, what what are some of the challenges that you see in today's market, in today's, environment being a leadership at a company like LinkedIn in in in in a tech, you know, a tech world where there's a lot of instability and uncertainty?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. That's that's a great question. Unstability and uncertainty. Being comfortable with both of those and making those a norm, I guess that's the biggest change that we are seeing. And to frame it better, maybe it's just being more comfortable with a lot of change as it's coming your way and be more flexible to perform, knowing all of these uncertainties around you.\u003C\u002Fp>\n\u003Cp>So, as a leader, it's it's very hard sometimes to motivate the team and uplevel engineers when you are in this situation, an industry that everyone are hesitant to put their 100% in. So how do you do that? How do you motivate people? Well, you know, things around you are so chaotic. People are not sure if they're gonna get laid off or not.\u003C\u002Fp>\n\u003Cp>There is not more of certainty to the mission of the company. I guess, like, couple of things we are doing as a company is being very transparent with engineers if we have layoffs or not. So we are very clear that we don't have layoffs currently. If we know there's more layoffs, we'll let everyone know. So people will rest assure that there's no surprises.\u003C\u002Fp>\n\u003Cp>And I think it's been happening mostly in a lot of big takes as a surprise, which just has to change for people. It needs to be very more transparent.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. That's that's a good point. And I think that, like, you know, especially at start ups and smaller companies, these things can turn on a dime on people, especially when, you know, maybe the company isn't doing so well and people get shocked. And in those environments, I've seen a lot of managers and and colleagues get together and at least share information so that they can prepare for those next steps. Is that something that you've seen happen before?\u003C\u002Fp>\n\u003Cp>And, how would you recommend the first step if you do get laid off? What that first step is in finding that new position is is, like, would you recommend applying to a bunch of, you know, jobs that you're interested in or really reaching out to that network?\u003C\u002Fp>\n\u003Cp>Speaker 1: I will recommend take a 2 day off for yourself and do a self reflection. I've been through a layoff. So my last job at Netflix, I was laid off. And, I was on both sides of the coin, and seen both sides. So it's it's hard to get laid off.\u003C\u002Fp>\n\u003Cp>It's never easy, especially if you're performing and you're a great engineer and you're really asking yourself questions why. You know, I have dedicated to this amount of time to this company mission. I have put my all 100% in to develop features. I cared for our users, and now you're telling me to go. And this is not a great feeling for anybody.\u003C\u002Fp>\n\u003Cp>So I would say the first thing you do is just taking time off and self reflect and try to understand that this is not you, and try to think about your strengths. So what are the things that I'm great at? Because a lot of times when things happen, I see people start to blame themselves and really focusing on shortcomings. These are the things I don't have. Maybe that's why I got laid off because maybe I wasn't great at this.\u003C\u002Fp>\n\u003Cp>Oh, how can I find a job now? I'm not great at that. But instead of that, try to be focusing on your strength and see the things that you are getting hired for at the current role and then what are the values that you can take into the next role and next company. And, you know, I coach a lot of engineers nowadays, and I always even say to my team, my direct reports that you're not bound to any company. Your career is you.\u003C\u002Fp>\n\u003Cp>So when you go to a company, don't bound yourself to a company specifically. Think longer term about your career and know that layoff can happen anytime. So you enter a company. You do your best. You're bringing value with your talent.\u003C\u002Fp>\n\u003Cp>If that's an exit, then that's a goodbye in in in different form, and you're taking those values and strengths to another company and bring more value for more people around the world. And that's not the end of your career journey.\u003C\u002Fp>\n\u003Cp>Speaker 2: That's true. Yeah. And Yeah. Go ahead, John.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I've that brings up a good point, and I'll I'll branch us into more of some questions around your leadership style because I I was watching a podcast that you were on previously this morning, and you mentioned something really interesting. You said you don't wanna be a leader on a team of followers. You wanna be a leader on a team of leaders. And so what what's some of the advice in the in the typical regular day to day that you provide to your team?\u003C\u002Fp>\n\u003Cp>You know? Because it sounds like you want to train your team to be self sufficient, hire smart people who can solve problems, and you wanna lead people who are leaders, which in the end makes you a more valuable employee anyway. So what are some of those other values that you have as a leader? Number 1, leading a team of leaders, but what are some other things that you train your team, some values that you train your team to have so that they're just a more valuable employee in general?\u003C\u002Fp>\n\u003Cp>Speaker 1: Mhmm. I wouldn't call it a valuable employee. I would call them call them great engineers, who can go to any any company or even build their own products. Yeah. That would that was a concept I, read a book called turn the ship around.\u003C\u002Fp>\n\u003Cp>It's just an amazing leadership book, and, it talks about, how do you empower your team to make decisions on on their own. And that's so true. And it's a very easy statement, but it's not easy in practice. There's a lot of things required for that to be able to do that for your team. Autonomy.\u003C\u002Fp>\n\u003Cp>Like, how do you, as a leader, get all the information to the team that they need? How do you onboard them onto the mission vision? How do you take them to the front lines, how do you get them areas of ownership. And at the same time, as you are in the back of the things, they're also accountable for their work because you don't wanna leave everyone and go. Not everyone feel accountable.\u003C\u002Fp>\n\u003Cp>People sometimes just leave things, and they don't follow-up. So how do they be very proactive, and feel ownership of their area? So I think, like, the most important thing is if the person thinks and feels that they are creating the impact and the impact is visible, they feel more ownership to putting in the work. So for me, also, how do I recognize this engineer? How do I shed light on their work in different forums?\u003C\u002Fp>\n\u003Cp>Let's say, like, if if I like, it's a upper leadership type of forum. How do I present their work? How do I have them come and demo and show their work? So they feel they own it, and they feel the impact and importance of their work. And for me, not all the projects have the huge, huge impact.\u003C\u002Fp>\n\u003Cp>So how do you actually empower engineers to feel that sense of ownership even on projects which is like migration? And, I think for us is whenever we wanna do something, we wanna think about the why of doing it, and it's one of LinkedIn's engineering principles. We need to have a very strong why. If you have an initiative, you have an idea why are you wanna do this. And if that why is very strong, that correlates to impact.\u003C\u002Fp>\n\u003Cp>It means there is an impact. And if there is an impact and if I'm as a leader being able to give all the credit to the person who's doing the work, showcase all the light on that person, you know, and then really reward them based on their impact that they're having. They will feel the sense of ownership, and they actually execute pretty well. So that's what it says in that book is that also in addition to this, give them all the knowledge they need to perform and, have them make mistakes. I think that's one of the scariest things as a leader to leave the team to make mistakes, and it needs to be controlled mistakes.\u003C\u002Fp>\n\u003Cp>So you you need to know when you can leave like an engineer. You know what's the right solution is, but you're not saying that. You you have them take their path. You have them make mistakes, and you have them learn from those. And a lot of time when people become managers newly, they try to control that.\u003C\u002Fp>\n\u003Cp>Right? They try to make sure everything is done right on the team, and there's no area of mistake. But mistakes are very crucial for people development. So as a leader, we need to be able to have those controlled risk so people can make mistakes on areas that's it's not gonna fall apart.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. I love where you talked about sense of ownership, and I think we've talked about that a few times on this podcast. Obviously, everyone feeling like they're making an impact, feeling like they're driving themselves and the organization forward. But on the flip side of that, you know, especially in today's market, like, we were talking about with the layoffs and the reorganizations that can sometimes happen at that level that it almost doesn't matter the contribution that you made. Like, how do you coach someone through that?\u003C\u002Fp>\n\u003Cp>Does that bring down morale if they see maybe half their, you know, half their colleagues have been laid off and now they're still at the company? But they're you know, they recognize that sense of ownership, and they're like, well, they didn't do anything wrong either. How do you keep morale high? How do you coach people through that?\u003C\u002Fp>\n\u003Cp>Speaker 1: I think one of the important things is to acknowledge all the emotions. Right? Is that it is hard to be in that situation. We had a situation like that last year where I think a bunch of our engineers were laid off late last year. And it is it is hard.\u003C\u002Fp>\n\u003Cp>It's scary. And, you know, as a leader, you need to acknowledge that. When you go in a room and say, well, everything is butterfly and sunflowers, and we're good, so let's get to work, people doesn't feel like that. Instead, get to a room and say, yes. I feel very bad today.\u003C\u002Fp>\n\u003Cp>I, myself, am scared. I, myself, am not happy. I need actually some time to get my, you know, self up and running to be able to execute at my current job as a leader. So take that time too. It is heartbreaking to see people go around us, and I think as you're acknowledging that emotions, people feel more safe.\u003C\u002Fp>\n\u003Cp>People feel that they're in an environment that their leaders feel the same as they are, and no one is trying to, you know, hide things from them or display things in a different way as they are. And that transparency, even on the feelings level, will help people navigate these situations. And be very frank with people. I mean, this is an environment we are in at tech where layoffs are happening. It's been happening forever.\u003C\u002Fp>\n\u003Cp>This is not something new. When you sign a contract with a company, you sign that you can exit anytime, and they can let you go anytime. And this is a norm, and I don't think people should really bound themselves to these things and things like this is the end, and all my contributions are gone. This is not the case. Even for people, the they got laid off.\u003C\u002Fp>\n\u003Cp>There have been many instances that we help them find their new role, so we are there for them. This wasn't something they have done. They are great engineers. There were many instances that I referred them to different places. We tried our best to find them new places, and, you know, business is business, and I think that's for people to understand.\u003C\u002Fp>\n\u003Cp>If CEOs of companies getting laid off, you know, it's it's okay for employees to get laid off. So it's it's a norm. I guess, like, in that situation, just to summarize, just be very honest with people. Talk about their feelings. Acknowledge.\u003C\u002Fp>\n\u003Cp>So there is the 7 stages of grief that we all know. You have to walk your team through that 7 stages. And don't do that in one day. Day 1, every stage in one day, and then in the 7th day, people are back to their norm. If you're actually acknowledging the emotion, give them time to come back.\u003C\u002Fp>\n\u003Cp>Take that anger. Create spaces for people to talk to you, and have them feel safe by you opening up yourself as a leader and talking about how you feel about this. People continue to open up and just put out everything that they're feeling. There would be anger. There would be resentment.\u003C\u002Fp>\n\u003Cp>There would be lack of trust for a while. The morale will get impact, and there is nothing we can do about it. That's the reality of the layoffs. And the companies who are doing that, they know that it will have an impact, Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. Be prepared for productivity. Yeah. Productivity to drop, morale to drop, but but, you know, maybe after a few weeks, it returns back to the norm if you can manage through it.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yes. Yes. Yeah. Be prepared because you can expect your team to perform the same way they've been. It's it's grief.\u003C\u002Fp>\n\u003Cp>You you know, human beings, we can perform the same way. It's it's exactly the last process.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Yeah. Let's let's take a step back in your journey because I'm always curious how people find their way to leadership, roles that they've had. And I believe part of your story is going through some of these transitions at previous companies that you're in. So I guess let's kind of take take it back to the beginning of your engineering journey.\u003C\u002Fp>\n\u003Cp>You know, what were you doing? What were some of those roles that you had? And what does that transition from an individual contributor to leadership look like\u003C\u002Fp>\n\u003Cp>Speaker 1: for you? Sure. I wrote a blog post on this as my story called transition to leadership like a butterfly, if if you anyone wanna read, in detail. But I guess I started my engineering journey, like, years back as a back end engineer. So I was doing Java.\u003C\u002Fp>\n\u003Cp>That time was Spring, Khybernet, all these technologies, and I had a lot of pivots. So I'm very curious person, and I wanted to pick up different things. So I pivoted from back end to front end. And then and decide, I did things like machine learning. I worked on self driving cars, did a little bit of AI here and there, studied system engineering while I was working, worked a little bit on NASA.\u003C\u002Fp>\n\u003Cp>So so many different branches I had, where I was an engineer. So I wasn't only doing my work. I was very active even outside of my work. And then during that time, as I was exploring different things, was I got this role to becoming a software architect, which was one of the most interesting and fruitful roles I had as an engineer where I could work on, like, a system end to end and work with so many amazing people who had many, many years of experience. So as an engineer, I definitely had experience with all over, all the technologies that we have, across the stack.\u003C\u002Fp>\n\u003Cp>And I think that made me very, versatile and being able to shift very quickly from, like, back end to front end or using different technologies and, bringing systems upgraded systems and having system understanding. How do you build complex systems that are highly aligned and loosely coupled? So how do you, like, handle risk? How do you test very well? So, that's part of my engineering journey.\u003C\u002Fp>\n\u003Cp>But things change. You know? As I started, I was very more technical, and then all of the softer skills getting added as requirement for my role. Right? How do I run a meeting effectively?\u003C\u002Fp>\n\u003Cp>And as I grew more and more, I had to deal with more and more people, and that people side got way more interesting for me. So, I talk about my library when I, when I write that blog post where, initially, most of my books were technical. When I started my engineering journey, you can find my library from a programming books. And then as this continues, there's, like, addition of softer skills book getting added to my library. And I would see myself reading those books more more than my technical books, and sometimes I ask myself, like, am I doing the right thing?\u003C\u002Fp>\n\u003Cp>Am I, like, going away from my path and doing something new? But, I could say, like, my mindset as my library kind of get transitioned to the people side of it. And over time, I find that more interesting because if I can enable and empower people, and I've been told many times that this is your strength, If I can do that, I would have way more impact than just running myself and executing myself.\u003C\u002Fp>\n\u003Cp>Speaker 2: Mhmm. Do\u003C\u002Fp>\n\u003Cp>Speaker 0: you find that curiosity that you you found for evolving, you know, soft skills and and newer technologies? That that's definitely a theme that we've heard from almost everyone that we've talked to on this podcast, the curiosity to try new things and then also not be afraid to fail when you're trying new things, but just that curiosity in general. Do you find that to be a crucial a crucial trait to have for a leader? And if so, is that something that you you try and cultivate with your, oh, hey. Visit from the cat.\u003C\u002Fp>\n\u003Cp>I was hoping he'd show up.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. It has my cat, Christopher.\u003C\u002Fp>\n\u003Cp>Speaker 0: I'm gonna show up. Show up. Yeah. Is that a trait that you is is that a trait that you cultivate in people that you're leading who who also show that they wanna be leaders? If they don't have that trait or if they do have it, is that something that you cultivate with them?\u003C\u002Fp>\n\u003Cp>Speaker 1: I mean, people have different personalities. That's the thing I've learned, and not everyone wanna branch this many branches, to be honest. Some people like to go in-depth in one thing, and they stay in one thing, and they they, like, create expertise. Some people like me, they they just like to explore, and they gets maybe bored. I I would like I feel whenever I feel comfortable, and I learn something and I know I'm good at it right now and I'm comfortable with it, now I start to, like, seek something else.\u003C\u002Fp>\n\u003Cp>I challenge myself constantly. Not everyone is like that, and it's okay, not to be like that. I think people have very, very different personalities. And, if they are the opposite of the spectrum, doesn't mean that they're not successful, and they can't be greatly less. But for me, specifically, my personality was in that spectrum of, like, I I don't stay in one zone when I'm comfortable, and I'm constantly want to learn something new.\u003C\u002Fp>\n\u003Cp>Every year, I pick a new technology to learn out of nowhere. Like, this year, I was looking at quantum computing. Is it related to my job? No. It's it's like out of nowhere I do learn it.\u003C\u002Fp>\n\u003Cp>It's interesting to me.\u003C\u002Fp>\n\u003Cp>Speaker 0: Is there any re that's a that's a hefty, hefty topic to start learning. Is there a reason you chose that, or you just you saw it, you had almost no idea what it was, and you just wanted to start exploring it?\u003C\u002Fp>\n\u003Cp>Speaker 1: Exactly. I saw it. I had no idea. I read things about it, how it solves problem, like, developing new drugs and proteins and working on human DNA, and I'm like, oh my god. This is this is super interesting.\u003C\u002Fp>\n\u003Cp>How does this work? And I started, like, listening to YouTube videos and then reading books about, like, quantum mechanics. So, yeah, I think it's just a curiosity and me wanting to pushing myself constantly out of my comfort zone and challenge myself. I feel that I'm if I feel like I'm in one place, I feel I'm stagnating. So it's it's all about personalities.\u003C\u002Fp>\n\u003Cp>Like, we have different personality traits. I think my my my personality, I'm more of, like, an alpha personality where I have, like, very extreme growth mindset. But people have, like, different spectrums. Some people are very focused on one thing, and they're really great at it, and they don't go out of it. So I think that's totally fine to be in a different spectrum.\u003C\u002Fp>\n\u003Cp>As long as you're growing, it should be totally fine.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. I think it's also a future it sounds like it's also a future focus too. Right? Curious about what comes next. I mean, people studying people have been studying, obviously, AI and large language models for, you know, years already.\u003C\u002Fp>\n\u003Cp>We just, as the public, come to see it now. So maybe, like you're saying, quantum computing is that next frontier in 10, 20 years that sort of becomes more more mainstream.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 2: So, yeah, that that future focus is is interesting. I heard somewhere that, like, I guess at some point, quantum computers can essentially break all encrypted passwords that we have today. So it it could be an interesting problem to solve for tomorrow. Right? Like, how do we secure devices if not with, you know, with passwords and things?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. I think that one thing that it was around 2015 when I went to ACM of ours. I don't know if anyone familiar. Association of Computer Machinery is, like, one of the most prestigious computer science, association, and this award is, like, a Nobel Prize for scientists. So that year, I think, was Bernice Lee who invented the Internet who got the award, and it was at the award ceremony.\u003C\u002Fp>\n\u003Cp>And there was a committee who is protecting our data. And it was very interesting to me because all of our data now is binary. And with quantum computing, be the future, and it's not transferrable, how are how are our data gonna be read with quantum machines? Who's gonna protect it? Who's gonna keep a repository of all of our data?\u003C\u002Fp>\n\u003Cp>And it was surprising to me that there is a whole committee who's responsible for taking care of our data, you know, as a society. And, they're thinking about all of these things. It's like, how do we transfer this data to quantum later on? So people can look at their images, or look at their binary data. But, yeah, if when you go to these places and you see people forward thinking these many levels, you'll be amazed to hear, like, there are people actually thinking about this thing.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. And I also assume one thing I've heard you talk about in other podcasts as well is networking, especially as you just wanna become a better a better engineer, better employee, networking with people. And I'm sure those types of events also introduce you to people with really interesting things that they say and and even new topics that you can then, 5 years later, start to investigate yourself. And so what's the for for this podcast and for the people listening here, what is the value of networking that you, you know, find yourself and that you train for or or you you pass on to your mentees as well?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. That's a good question. Well, a couple of things. First of all, don't network because you want something from people. Network before you want something from people.\u003C\u002Fp>\n\u003Cp>So beat those relationships before it comes to, like, looking for a job or, you know, changing your role. And, I always call it vertical and horizontal net networking. So when you are at a role in in your company, you need both of them. So you start the first three months of your role to 1st 6 months doing vertical networking. And by vertical, I mean your team, your manager, your skip manager, and everyone in the chain, and your close partners, so anyone you work with day to day base.\u003C\u002Fp>\n\u003Cp>So build relationship with those people. But after that 6 months, you start to do horizon networking, which means going outside your team to other teams that you don't know about. A lot of companies have so many tools. Like, I remember when I was at Netflix, there was this coffee chat where you could register, and you get randomly, matched with someone across the company and you could get a coffee with. At LinkedIn, we have, like, similar tools that you can go in and, you know, chat with people across the company.\u003C\u002Fp>\n\u003Cp>If your company doesn't have 1, be the leader and build 1, so people can chat with each other. Or just simply, you know, ping people for, like, a 15 minute coffee chat to learn about them, introduce yourself, and, just connect with them. And then later on, you will be surprised how those people would show up in your journey, in your career. Not everyone have to give you a job or refer you. They will inspire you in so many ways as you're networking with them.\u003C\u002Fp>\n\u003Cp>You may have a question or you may get an idea and you can run by them or they would just be your advocates. They know you somehow. And when there is an opportunity, they remember you, and they put your name up on the table. So that's that's a thing. Always remember.\u003C\u002Fp>\n\u003Cp>Don't forget that horizontal networking. People usually go to companies, and they just rely on the vertical networking. They don't know really anyone across the company, and it's similar outside. As I mentioned, your career is not bound to a company, so think the same thing. Vertical networking in your area, horizontal networking outside your area, network with recruiters in different companies, network with people outside your field.\u003C\u002Fp>\n\u003Cp>LinkedIn right now make it super easy for everybody. I think 10 years was their LinkedIn, but it wasn't as active as now where you could find people this easily. Now we are getting to the area of conferences being back again after COVID. So attended conferences, meetups, and chat with people, learn about them, and they will learn about you too. So that's a that's a good thing to be at.\u003C\u002Fp>\n\u003Cp>And there is so many things nowadays. I mean, you see all of these mentorship platforms that's showing up, which wasn't there 10 years ago when we started. That's a great way of networking, getting a mentor. It's a next level because that person is investing in you. And if someone is invest in you, they're way more willingly advocating for you later on than someone who just you meet in a conference.\u003C\u002Fp>\n\u003Cp>So go on on these platforms. You know, there's a ton of them across the industry. Maybe get to the session with different mentors, people you aspire to be like them or people you they're ahead of you and you wanna get there. Learn how do they get there and ask for their advice. And then later on, when you build these relationships and talk to so many different people, they will invest in you and advocate for you whenever you are in your career.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. You mentioned LinkedIn was a good resource for that. Obviously, you know, that's bringing the entire network together and having making it easier for people to just reach out. In the past, like, I found it maybe awkward or or scary almost to reach out to people for advice. Like, what would you when you're reaching out to someone that doesn't know you at all completely cold and wanting to network with them, what are some tips that you would recommend on that initial sort of cold message?\u003C\u002Fp>\n\u003Cp>Speaker 1: I won't really do that. Right? I won't reach out to someone I don't know, and I don't advise that. That's why I'm saying, like, network before you actually need an advice, like, have a really broad people that you know. I won't send a cold message to someone and say, hey.\u003C\u002Fp>\n\u003Cp>I need an advice, and I know need 30 minutes of your time. People are super busy, especially people who are in better stages that can advise you. Right? Go get a mentor. Right now, there is so many ways of doing that.\u003C\u002Fp>\n\u003Cp>There's all of these platforms. There's Play Doh, Mentor Cruise, MentorMe. I mean, there's so many of them out there that you can find these people on those platforms actually offering you time. So, go find those platforms and find those people, and that's respecting their time and also going there professionally and getting the advice you need. And that will build trust between you and that person because your mentor is now trusting you.\u003C\u002Fp>\n\u003Cp>And then you can send the cold message after you have couple of sessions with this person and say, you know what? I need an advice on this. What do I do? And they will always there for you. Like, I'll tell all my mentees, like, I'm your all time advocate.\u003C\u002Fp>\n\u003Cp>Anytime I mentor, I'll be there for them, like, through their career. So, yes, it can happen. People, like, send them cold messages and say, hey. Can you make a referral? And I'm like, I I don't even know you.\u003C\u002Fp>\n\u003Cp>And that makes me question your judgment because how would you want me someone who actually doesn't know you at all to refer you to a role versus someone who's coming in and say, look. I'm looking for a job, and these are the things I wanna get better at. I have a good understanding of myself. Can you advise me how can I find a job? And then at that stage, when I have couple of chats with that person, may mock interview them, I know how their level and how they are.\u003C\u002Fp>\n\u003Cp>And I actually myself refer them to places that I think it's a great fit for them. So it happens very naturally when you build the relationship the right way. I am not a believer in those cold messages. Sometimes they work, but, I mostly believe in building relationships that are strong with people that have really good foundation and trust.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. That's that's really good advice. I think it's crucial these days to kind of build those are you when you open a position, are Are you when you open a position, are you first thinking of people who have built that type of a relationship with you, whether they're, you know, someone who would fit the position? And then, are you reaching out to your network of people saying, do you know somebody who might fill this position or might fit this position? I guess, which one or both?\u003C\u002Fp>\n\u003Cp>You know, do you look for both of those types of sources for people when you're scaling your teams?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. So I try to be fair because I don't wanna only look at the people I know and advocate for them, only because I wanna give opportunity to people who I don't know and they can apply to the role. So we I usually build a pool of people where 10 to 20% of them are people I know and I know it's a good fit for this role, and the rest are opportunity for other people to apply to the role. And I take a look at those resumes too. But because I know these people, because I invested them, when a role pops up, I have them top of my mind, and I ping them that this is a role that's popped up.\u003C\u002Fp>\n\u003Cp>If you're interested, apply. But I'm not giving them extra benefits as other people. But the fact that I'm pinging them as a hiring manager and I'm saying apply, they will get an interview pretty easier than people who are applying to the roles, especially in this market. You know a role is popping up in 2 hours. There's 100 of applicants.\u003C\u002Fp>\n\u003Cp>And, if you have that relationships that people know you, top of their mind, that's the key to get a role is that people who advocate for you and put your name on the table without you wanting that. And that's a huge key to success in the in your career is that building those relationship that can result in this. So whenever I open a role, I have people on top of my mind that I know they're looking for a role and a great fit for this role. And, and I really do that because I know it benefit their role and benefit them. So I don't really refer people who are I don't think they're a good fit.\u003C\u002Fp>\n\u003Cp>And, you know, they've been people that their life changed because of that. I mean, they get into really big tech companies. I had a mentee who was a waiter, and, afterwards, he got hired at Meta, and that's changed his life. So many of these stories, people who are just entering tech and, you know, it's really hard to find your first job and build relationships like this with people across the industry who can refer them and advocate for it. Them.\u003C\u002Fp>\n\u003Cp>So it's a it's a both mix up pool because fairness is super important when I hire. I wanna make sure I give, like, opportunity to all type of people, be as, you know, inclusive as possible to, like, all people from different backgrounds. Also, look at my team and say, like, okay. Is my team diverse enough? You know, what I'm needing.\u003C\u002Fp>\n\u003Cp>Like, do I have enough women at my team? I don't want, like, to having 1 woman in the room or 1 man in room to feel, like, isolated. I want everyone to feel belonging. So looking at the gaps on my team and see, like, what are those gaps that I'm looking for to fill in and really building that diverse pool of people, who are coming in to interview.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. And on the taking it from from the hiring side, I'm I'm curious putting you as the candidate. I know you were presented with options, you know, to maybe go to YouTube or Google and and LinkedIn, and, obviously, you you ultimately chose LinkedIn. When looking for a place to go, like, what was that deciding factor that made you choose LinkedIn and and framing that as advice for people who are listening? You know, if they're presented with so many options or so many different paths, like, how do you choose?\u003C\u002Fp>\n\u003Cp>Speaker 1: Mhmm. So, the framework I walk my mentees through is we do have a value table. Before you actually start job searching, you put together maximum five values you're looking into the next role. What are the things you really want in your next role? Example, for me, like, people mattered.\u003C\u002Fp>\n\u003Cp>I wanna work in a culture that people come first. Growth mattered. I wanted to work in in a company that supports my growth. You know, technology mattered. I wanted to be a cutting edge technology.\u003C\u002Fp>\n\u003Cp>Product mattered for me. I wanted to work on a product I've never worked on before, and there are a couple of other things that I have in my value table. And whenever I am interviewing a company, and I always say that interview is a two way assessment. There are things you're assessing too. These values are the things you have to assess.\u003C\u002Fp>\n\u003Cp>Ask the recruiter. Ask the hiring manager in your first interview. What is the growth framework on your team? Who was the last person who got promoted on your team? How does promotion happens?\u003C\u002Fp>\n\u003Cp>How do you support an engineer who falls behind? How do you onboard people onto new team? You know, all of these questions you can ask to assess how good is that company and that team role for you in terms of your value. Ask about the product if it matters. Like, how old is the product?\u003C\u002Fp>\n\u003Cp>Is it a new thing? Why are we is this a bet? You know? Is it something that we're just gonna do right now and just, disappear, like, a year from now, or is this something the company wanna invest in long term? If these things matter for you, be prepared to ask these questions because a lot of people go into roles blindly just because it's at a good company, and then they're not happy there afterwards because they're not assessing in their interview, their role in the company.\u003C\u002Fp>\n\u003Cp>And then when you have these values and you're interviewing, I give a score from 1 to 10. 10 means they're great at it. 1, they're very poor. And, then when you're reaching to the offer stage, like top 3, top 2, interview that you're doing, you can see the differences. So, for example, for me, between YouTube and LinkedIn, it was a product.\u003C\u002Fp>\n\u003Cp>I wanted to work on a product I've never worked on, and for me, YouTube was a streaming again. And I'm not a person to go in-depth. As you know, my personality, I love the breadth. I love to get things which is new. I never work on a social media platform like LinkedIn.\u003C\u002Fp>\n\u003Cp>So that was a a factor like, one of the biggest factor on that table that I picked LinkedIn over YouTube. So if you have that values, it's easy to to know, like, which one I I wanna go to and which value I wanna compromise, which one is compromiseable for me, which one is nice. For me, work life balance is not compromiseable. It's a value. I do a lot of things outside work, and work is not my only thing in life.\u003C\u002Fp>\n\u003Cp>So, I wanted a company that, you know, I don't have to work 247 because I really don't admire that type of environment. So I think it's toxic. So what are those values and which one you cannot compromise and assessing those over your interview then when you're at a offer stage? You you don't have a hard time to know which one do you have to pick.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Yeah. I I think I saw that you last end of towards the end of last year ran a marathon, and so those types of hobbies, I think, are pretty important. You know? Pedro and I play music music, and, I think Pedro is a coffee aficionado, so he loves making coffee.\u003C\u002Fp>\n\u003Cp>So I think it's important to have those things outside of work to to make sure you stay balanced. It sounds like you have several ways that you evolve yourself. You know, you you said you mentioned reading books and just being curious and learning new technologies. You probably also have your own mentor who you who you get advice from or several mentors. So is it is it hard, or do you just find it come naturally to still make sure that you're growing as a as a person?\u003C\u002Fp>\n\u003Cp>Do those does that time come easy, or do you have to make sure that you, you know, spend a couple hours a week making sure that you're growing as a leader as well?\u003C\u002Fp>\n\u003Cp>Speaker 1: That's a great question. I feel that's come naturally for me. It's not like I mostly build habits versus, like, I have to read, like, 3 hours per week. It's not like that. It comes as a habit.\u003C\u002Fp>\n\u003Cp>You all know the book, Atomic Habits. Right? It's like stacking things and building habits and systems. For me, mostly, it's systems in my life. And you can't have all of it at the same time.\u003C\u002Fp>\n\u003Cp>Like, you can't go to the gym and read books and be great at your work and get a mentor and run projects. It's not possible. You need to have balance long term, but in short term, you may not have a balance. So let's say if you if you wanna get better at something and you have, like, 3 books to finish, you may have to cut off from your gym time or running time or whatever you're doing at on the side for, like, a week. And then a week after, you know that what is cutting off was my running, and now I'm done with this book.\u003C\u002Fp>\n\u003Cp>So this is the things I'm gonna prioritize. I'm pulling it back up this week. I'm gonna focus on it more. So in long run, there is a balance between all of these things you're doing. But in short run, you prioritize between these things because you can't have all of these things together.\u003C\u002Fp>\n\u003Cp>And then you build systems for yourself. Systems of learning. What's your system of learning? Is that books right now is so integrated into my life. It's something that, you know, I usually read in the morning when I wake up or if it's a book.\u003C\u002Fp>\n\u003Cp>Even learning how to read a book pretty fast is super important and summarize it so you can refer to it later on. But building that systems that actually can help you. For me, Fridays is my self reflection days. It's a day I have a session in the morning with my mentor. And after that session, I self reflect on, like, how what did I do this week?\u003C\u002Fp>\n\u003Cp>What are the areas I did really well? What are the top things top five things I did at my work? Then that top five things goes to 1 on 1 next week with my manager. And this is kind of what we call it managing up. Like, how do you how do you manage up, and how do you tell your manager and your escape what are you doing, and it's getting harder as you're going higher and higher.\u003C\u002Fp>\n\u003Cp>So that's that's a way of me managing up by taking that 5 outlines and putting in my 1 on 1 doc, and then self reflecting of what are the things I have to do next week, looking at my team structure, how everyone is doing, did I capture what they did greatly this week? I do that for my team too, like, every individual on the team. I have a running doc that I say these are the things they did really well this week. So when it comes to their performance review, I am very detailed for advocating for them, for every engineer, and I'm taking all that recognition in. What are the feedbacks I have to give them?\u003C\u002Fp>\n\u003Cp>I'm very proactive on giving my direct reports feedback. I will let it sit. If someone is not doing something very well or there is there is a gap that they can do better, it would be right away. Every week, I'm thinking about their performance and how they did. So it won't be a surprise.\u003C\u002Fp>\n\u003Cp>You know? There are some managers that suddenly come after, like, a year, and they say, you didn't do all of these things. And, that's hinder trust. The trust is broken at that stage. So the more honest and the more periodic and the smaller these pieces of feedbacks are and very targeted, the better those those people can grow.\u003C\u002Fp>\n\u003Cp>So Friday, my system of self reflection into my life, plan of next week, How's my month look like? Am I having enough fun? How's my gym is going? How's my workouts? Relationship wise, am I spending time with people I love?\u003C\u002Fp>\n\u003Cp>Like, sometimes I'm so much slanted at work. I forget about my friends. I'm like, next week, I'm gonna set time with my friends. Next week, I'm gonna spend more time talking to my mom. There are, like, 4 categories in my life.\u003C\u002Fp>\n\u003Cp>It's career, which is one that you all have. It's love and relationships, like you being very socially, satisfied with, like, going out with people, your best friends, your family, your close relatives, spending time with them. For me, financial life matters. I'm doing on that. Am I, like, on track with my goals?\u003C\u002Fp>\n\u003Cp>And then, health, like, in terms of am I drinking enough water? Am I a happy person? How's my mental health? Do I need, like, some of the weeks that I'm very exhausted? I usually go to a lighter sort of workout.\u003C\u002Fp>\n\u003Cp>And I'm like, okay. This week, I'm gonna go do yoga instead of running because I want less intensity in my life. So how are you assessing that and then having that, like, long term balance? There is this book also called design your life where it talks about these categories, which is something I took from this book, And then, having that categories help me to balance. So I measure every category every week, and I see how I'm doing and which category requires attention.\u003C\u002Fp>\n\u003Cp>I prioritize a week after.\u003C\u002Fp>\n\u003Cp>Speaker 2: I like that. I have to check out that book.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I'm I am writing that down because, there's definitely weeks or several weeks in a row where I might neglect 1 or 2 of those categories for sure. So that's that's a good book to write down.\u003C\u002Fp>\n\u003Cp>Speaker 2: Awesome. So, yeah, I think, we're coming close to time here for you know, thank you, Naz, for your for your time. I think maybe we could wrap it up with, you know, what's some advice that you would give to anyone who's listening, maybe something top of mind that's, you know, come across your mind over the last week or 2, that you'd like to just kinda pass along? And then where can everyone find you and and follow your journey?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Sure. Oh, that's a hard question. I mean, something that has been top of my mind. I was reading yesterday a quote about someone who lays off.\u003C\u002Fp>\n\u003Cp>I think it was a company called Clarify, and she wrote couple of points I shared on my LinkedIn as opposed to where things I've learned from layoffs. And one of it is, like, really thinking about how do you not bound to one job and then have some back ups, make sure that that happens. The other was, like, really thinking about yourself and getting yourself back up. And, one point that was funny, and a lot of people didn't agree, is that people are there for you when you get laid off. But the reality is they're not.\u003C\u002Fp>\n\u003Cp>Everyone having their own job, and you can expect that you're getting laid off and everyone is there for you. And now your friends are taking you to their companies. I mean, they do their best, but it's not the case. So be prepared. I mean, be prepared.\u003C\u002Fp>\n\u003Cp>Have your resume ready all the time. I mean, don't just get on your resume when you need a job. Your resume is a reflection of you, and it needs to be ready and updated. Your LinkedIn needs to be ready and updated with latest things you do. And then always and always have 1 or 2 interview per year.\u003C\u002Fp>\n\u003Cp>What are you doing with the company? Even if you're at a place that you're comfortable, always, always interview. You are you'll be at the ready state all the time. Also, you learn about the market and what's going on, in other companies when you interview with them, and it's a it's a way of also networking. So when you interview with hiring managers and they get to know you, later on when they have a role and if you perform there in that interview, they're gonna they're gonna reach out to you.\u003C\u002Fp>\n\u003Cp>So be ready, before something happens is actually the key in this market. And knowing that your work is not bound to a company. You're taking your work to different companies' missions and vision and enabling part of their journey as a really amazing engineer. And, none of us have to be in a place forever. At least in tech, it's not the case.\u003C\u002Fp>\n\u003Cp>So Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: Awesome. Yeah. I I think I think you also once mentioned sorry to sorry to jump in there. I think you also once mentioned around the interviewing process. You also just received feedback feedback from those people about how you present on a camera or your answers, or you get some valuable feedback from those interviews as well.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. And be very transparent when you do those interviews. Like, I'm not actively looking, but this looks like a good opportunity. I wanna I wanna assess and learn more about this company. Why not doing an interview?\u003C\u002Fp>\n\u003Cp>And putting yourself in those situations more often makes you more comfortable. You'll be more confident. So if people wanna find me, I'm on LinkedIn, nazdaalum. Also, you can find all my handles on my website, nazda dev, and feel free to reach out to me if you have any questions about anything I mentioned. Hopefully, we can have those books listed, John, somewhere, so people can find it on-site reference.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Yeah. I was definitely gonna say I'm gonna keep in contact with you just to see what books you're reading. Awesome, Naz. Well, yeah, I I really I personally found a lot of value from this conversation.\u003C\u002Fp>\n\u003Cp>There's a lot of things that, you mentioned that I just tend to forget over time, and so I definitely found some great value out of this conversation. Everyone, go follow her on LinkedIn. Pedro's battery is exhausted.\u003C\u002Fp>\n\u003Cp>Speaker 2: My battery is exhausted, so my camera died, but I am here. Thank you, Nez. Thanks so much for your time. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: Thank you, everyone. Thank you for having me. It was great.\u003C\u002Fp>","Oh, hey. It's a visit from the cat. I was hoping it would show up. Yeah. It has my cat, Christopher. Yeah. I was hoping it would show up. Yeah. Hey, everyone. Welcome back to another episode of Trace Talks. Today, we have Naz. Naz, first off, congratulations on becoming a naturalized US citizen today. That's very exciting, very exciting. Sometimes I take that for granted. So it's very exciting when people I know have that happen. But, yeah, we'll we'll let you introduce yourself. Why don't you let the people know just a brief background into who you are, maybe just a brief introduction into where you are, and then we'll get into the conversation. Absolutely. Good morning, everyone. Good afternoon wherever you are in the world. My name is Naz. I am currently an engineering leader at LinkedIn, leading media, and, previously worked as as a engineering manager and software engineer at Netflix. My background to tech was very traditional, so studied bachelor and master's, in computer engineering and afterwards, systems engineering. So, yeah, it's been, 10 plus years that I am in San Francisco Bay Area, and I got my first job here right after school. Awesome. Yeah. Thanks so much for that quick intro. As this is kind of a a a podcast focus on leadership, I figured we'd start on that leadership trend. Right now, obviously, in in tech and even starting outside of tech, there's a lot of organizational changes happening and tech layoffs and everything that's, you know, kind of on the front the front of everyone's mind. So I guess let's let's kinda start off on, an interesting take. You know, what what are some of the challenges that you see in today's market, in today's, environment being a leadership at a company like LinkedIn in in in in a tech, you know, a tech world where there's a lot of instability and uncertainty? Yeah. That's that's a great question. Unstability and uncertainty. Being comfortable with both of those and making those a norm, I guess that's the biggest change that we are seeing. And to frame it better, maybe it's just being more comfortable with a lot of change as it's coming your way and be more flexible to perform, knowing all of these uncertainties around you. So, as a leader, it's it's very hard sometimes to motivate the team and uplevel engineers when you are in this situation, an industry that everyone are hesitant to put their 100% in. So how do you do that? How do you motivate people? Well, you know, things around you are so chaotic. People are not sure if they're gonna get laid off or not. There is not more of certainty to the mission of the company. I guess, like, couple of things we are doing as a company is being very transparent with engineers if we have layoffs or not. So we are very clear that we don't have layoffs currently. If we know there's more layoffs, we'll let everyone know. So people will rest assure that there's no surprises. And I think it's been happening mostly in a lot of big takes as a surprise, which just has to change for people. It needs to be very more transparent. Yeah. That's that's a good point. And I think that, like, you know, especially at start ups and smaller companies, these things can turn on a dime on people, especially when, you know, maybe the company isn't doing so well and people get shocked. And in those environments, I've seen a lot of managers and and colleagues get together and at least share information so that they can prepare for those next steps. Is that something that you've seen happen before? And, how would you recommend the first step if you do get laid off? What that first step is in finding that new position is is, like, would you recommend applying to a bunch of, you know, jobs that you're interested in or really reaching out to that network? I will recommend take a 2 day off for yourself and do a self reflection. I've been through a layoff. So my last job at Netflix, I was laid off. And, I was on both sides of the coin, and seen both sides. So it's it's hard to get laid off. It's never easy, especially if you're performing and you're a great engineer and you're really asking yourself questions why. You know, I have dedicated to this amount of time to this company mission. I have put my all 100% in to develop features. I cared for our users, and now you're telling me to go. And this is not a great feeling for anybody. So I would say the first thing you do is just taking time off and self reflect and try to understand that this is not you, and try to think about your strengths. So what are the things that I'm great at? Because a lot of times when things happen, I see people start to blame themselves and really focusing on shortcomings. These are the things I don't have. Maybe that's why I got laid off because maybe I wasn't great at this. Oh, how can I find a job now? I'm not great at that. But instead of that, try to be focusing on your strength and see the things that you are getting hired for at the current role and then what are the values that you can take into the next role and next company. And, you know, I coach a lot of engineers nowadays, and I always even say to my team, my direct reports that you're not bound to any company. Your career is you. So when you go to a company, don't bound yourself to a company specifically. Think longer term about your career and know that layoff can happen anytime. So you enter a company. You do your best. You're bringing value with your talent. If that's an exit, then that's a goodbye in in in different form, and you're taking those values and strengths to another company and bring more value for more people around the world. And that's not the end of your career journey. That's true. Yeah. And Yeah. Go ahead, John. Yeah. I've that brings up a good point, and I'll I'll branch us into more of some questions around your leadership style because I I was watching a podcast that you were on previously this morning, and you mentioned something really interesting. You said you don't wanna be a leader on a team of followers. You wanna be a leader on a team of leaders. And so what what's some of the advice in the in the typical regular day to day that you provide to your team? You know? Because it sounds like you want to train your team to be self sufficient, hire smart people who can solve problems, and you wanna lead people who are leaders, which in the end makes you a more valuable employee anyway. So what are some of those other values that you have as a leader? Number 1, leading a team of leaders, but what are some other things that you train your team, some values that you train your team to have so that they're just a more valuable employee in general? Mhmm. I wouldn't call it a valuable employee. I would call them call them great engineers, who can go to any any company or even build their own products. Yeah. That would that was a concept I, read a book called turn the ship around. It's just an amazing leadership book, and, it talks about, how do you empower your team to make decisions on on their own. And that's so true. And it's a very easy statement, but it's not easy in practice. There's a lot of things required for that to be able to do that for your team. Autonomy. Like, how do you, as a leader, get all the information to the team that they need? How do you onboard them onto the mission vision? How do you take them to the front lines, how do you get them areas of ownership. And at the same time, as you are in the back of the things, they're also accountable for their work because you don't wanna leave everyone and go. Not everyone feel accountable. People sometimes just leave things, and they don't follow-up. So how do they be very proactive, and feel ownership of their area? So I think, like, the most important thing is if the person thinks and feels that they are creating the impact and the impact is visible, they feel more ownership to putting in the work. So for me, also, how do I recognize this engineer? How do I shed light on their work in different forums? Let's say, like, if if I like, it's a upper leadership type of forum. How do I present their work? How do I have them come and demo and show their work? So they feel they own it, and they feel the impact and importance of their work. And for me, not all the projects have the huge, huge impact. So how do you actually empower engineers to feel that sense of ownership even on projects which is like migration? And, I think for us is whenever we wanna do something, we wanna think about the why of doing it, and it's one of LinkedIn's engineering principles. We need to have a very strong why. If you have an initiative, you have an idea why are you wanna do this. And if that why is very strong, that correlates to impact. It means there is an impact. And if there is an impact and if I'm as a leader being able to give all the credit to the person who's doing the work, showcase all the light on that person, you know, and then really reward them based on their impact that they're having. They will feel the sense of ownership, and they actually execute pretty well. So that's what it says in that book is that also in addition to this, give them all the knowledge they need to perform and, have them make mistakes. I think that's one of the scariest things as a leader to leave the team to make mistakes, and it needs to be controlled mistakes. So you you need to know when you can leave like an engineer. You know what's the right solution is, but you're not saying that. You you have them take their path. You have them make mistakes, and you have them learn from those. And a lot of time when people become managers newly, they try to control that. Right? They try to make sure everything is done right on the team, and there's no area of mistake. But mistakes are very crucial for people development. So as a leader, we need to be able to have those controlled risk so people can make mistakes on areas that's it's not gonna fall apart. Yeah. I love where you talked about sense of ownership, and I think we've talked about that a few times on this podcast. Obviously, everyone feeling like they're making an impact, feeling like they're driving themselves and the organization forward. But on the flip side of that, you know, especially in today's market, like, we were talking about with the layoffs and the reorganizations that can sometimes happen at that level that it almost doesn't matter the contribution that you made. Like, how do you coach someone through that? Does that bring down morale if they see maybe half their, you know, half their colleagues have been laid off and now they're still at the company? But they're you know, they recognize that sense of ownership, and they're like, well, they didn't do anything wrong either. How do you keep morale high? How do you coach people through that? I think one of the important things is to acknowledge all the emotions. Right? Is that it is hard to be in that situation. We had a situation like that last year where I think a bunch of our engineers were laid off late last year. And it is it is hard. It's scary. And, you know, as a leader, you need to acknowledge that. When you go in a room and say, well, everything is butterfly and sunflowers, and we're good, so let's get to work, people doesn't feel like that. Instead, get to a room and say, yes. I feel very bad today. I, myself, am scared. I, myself, am not happy. I need actually some time to get my, you know, self up and running to be able to execute at my current job as a leader. So take that time too. It is heartbreaking to see people go around us, and I think as you're acknowledging that emotions, people feel more safe. People feel that they're in an environment that their leaders feel the same as they are, and no one is trying to, you know, hide things from them or display things in a different way as they are. And that transparency, even on the feelings level, will help people navigate these situations. And be very frank with people. I mean, this is an environment we are in at tech where layoffs are happening. It's been happening forever. This is not something new. When you sign a contract with a company, you sign that you can exit anytime, and they can let you go anytime. And this is a norm, and I don't think people should really bound themselves to these things and things like this is the end, and all my contributions are gone. This is not the case. Even for people, the they got laid off. There have been many instances that we help them find their new role, so we are there for them. This wasn't something they have done. They are great engineers. There were many instances that I referred them to different places. We tried our best to find them new places, and, you know, business is business, and I think that's for people to understand. If CEOs of companies getting laid off, you know, it's it's okay for employees to get laid off. So it's it's a norm. I guess, like, in that situation, just to summarize, just be very honest with people. Talk about their feelings. Acknowledge. So there is the 7 stages of grief that we all know. You have to walk your team through that 7 stages. And don't do that in one day. Day 1, every stage in one day, and then in the 7th day, people are back to their norm. If you're actually acknowledging the emotion, give them time to come back. Take that anger. Create spaces for people to talk to you, and have them feel safe by you opening up yourself as a leader and talking about how you feel about this. People continue to open up and just put out everything that they're feeling. There would be anger. There would be resentment. There would be lack of trust for a while. The morale will get impact, and there is nothing we can do about it. That's the reality of the layoffs. And the companies who are doing that, they know that it will have an impact, Yeah. Yeah. Be prepared for productivity. Yeah. Productivity to drop, morale to drop, but but, you know, maybe after a few weeks, it returns back to the norm if you can manage through it. Yes. Yes. Yeah. Be prepared because you can expect your team to perform the same way they've been. It's it's grief. You you know, human beings, we can perform the same way. It's it's exactly the last process. Yeah. Yeah. Let's let's take a step back in your journey because I'm always curious how people find their way to leadership, roles that they've had. And I believe part of your story is going through some of these transitions at previous companies that you're in. So I guess let's kind of take take it back to the beginning of your engineering journey. You know, what were you doing? What were some of those roles that you had? And what does that transition from an individual contributor to leadership look like for you? Sure. I wrote a blog post on this as my story called transition to leadership like a butterfly, if if you anyone wanna read, in detail. But I guess I started my engineering journey, like, years back as a back end engineer. So I was doing Java. That time was Spring, Khybernet, all these technologies, and I had a lot of pivots. So I'm very curious person, and I wanted to pick up different things. So I pivoted from back end to front end. And then and decide, I did things like machine learning. I worked on self driving cars, did a little bit of AI here and there, studied system engineering while I was working, worked a little bit on NASA. So so many different branches I had, where I was an engineer. So I wasn't only doing my work. I was very active even outside of my work. And then during that time, as I was exploring different things, was I got this role to becoming a software architect, which was one of the most interesting and fruitful roles I had as an engineer where I could work on, like, a system end to end and work with so many amazing people who had many, many years of experience. So as an engineer, I definitely had experience with all over, all the technologies that we have, across the stack. And I think that made me very, versatile and being able to shift very quickly from, like, back end to front end or using different technologies and, bringing systems upgraded systems and having system understanding. How do you build complex systems that are highly aligned and loosely coupled? So how do you, like, handle risk? How do you test very well? So, that's part of my engineering journey. But things change. You know? As I started, I was very more technical, and then all of the softer skills getting added as requirement for my role. Right? How do I run a meeting effectively? And as I grew more and more, I had to deal with more and more people, and that people side got way more interesting for me. So, I talk about my library when I, when I write that blog post where, initially, most of my books were technical. When I started my engineering journey, you can find my library from a programming books. And then as this continues, there's, like, addition of softer skills book getting added to my library. And I would see myself reading those books more more than my technical books, and sometimes I ask myself, like, am I doing the right thing? Am I, like, going away from my path and doing something new? But, I could say, like, my mindset as my library kind of get transitioned to the people side of it. And over time, I find that more interesting because if I can enable and empower people, and I've been told many times that this is your strength, If I can do that, I would have way more impact than just running myself and executing myself. Mhmm. Do you find that curiosity that you you found for evolving, you know, soft skills and and newer technologies? That that's definitely a theme that we've heard from almost everyone that we've talked to on this podcast, the curiosity to try new things and then also not be afraid to fail when you're trying new things, but just that curiosity in general. Do you find that to be a crucial a crucial trait to have for a leader? And if so, is that something that you you try and cultivate with your, oh, hey. Visit from the cat. I was hoping he'd show up. Yeah. It has my cat, Christopher. I'm gonna show up. Show up. Yeah. Is that a trait that you is is that a trait that you cultivate in people that you're leading who who also show that they wanna be leaders? If they don't have that trait or if they do have it, is that something that you cultivate with them? I mean, people have different personalities. That's the thing I've learned, and not everyone wanna branch this many branches, to be honest. Some people like to go in-depth in one thing, and they stay in one thing, and they they, like, create expertise. Some people like me, they they just like to explore, and they gets maybe bored. I I would like I feel whenever I feel comfortable, and I learn something and I know I'm good at it right now and I'm comfortable with it, now I start to, like, seek something else. I challenge myself constantly. Not everyone is like that, and it's okay, not to be like that. I think people have very, very different personalities. And, if they are the opposite of the spectrum, doesn't mean that they're not successful, and they can't be greatly less. But for me, specifically, my personality was in that spectrum of, like, I I don't stay in one zone when I'm comfortable, and I'm constantly want to learn something new. Every year, I pick a new technology to learn out of nowhere. Like, this year, I was looking at quantum computing. Is it related to my job? No. It's it's like out of nowhere I do learn it. It's interesting to me. Is there any re that's a that's a hefty, hefty topic to start learning. Is there a reason you chose that, or you just you saw it, you had almost no idea what it was, and you just wanted to start exploring it? Exactly. I saw it. I had no idea. I read things about it, how it solves problem, like, developing new drugs and proteins and working on human DNA, and I'm like, oh my god. This is this is super interesting. How does this work? And I started, like, listening to YouTube videos and then reading books about, like, quantum mechanics. So, yeah, I think it's just a curiosity and me wanting to pushing myself constantly out of my comfort zone and challenge myself. I feel that I'm if I feel like I'm in one place, I feel I'm stagnating. So it's it's all about personalities. Like, we have different personality traits. I think my my my personality, I'm more of, like, an alpha personality where I have, like, very extreme growth mindset. But people have, like, different spectrums. Some people are very focused on one thing, and they're really great at it, and they don't go out of it. So I think that's totally fine to be in a different spectrum. As long as you're growing, it should be totally fine. Yeah. I think it's also a future it sounds like it's also a future focus too. Right? Curious about what comes next. I mean, people studying people have been studying, obviously, AI and large language models for, you know, years already. We just, as the public, come to see it now. So maybe, like you're saying, quantum computing is that next frontier in 10, 20 years that sort of becomes more more mainstream. Yeah. So, yeah, that that future focus is is interesting. I heard somewhere that, like, I guess at some point, quantum computers can essentially break all encrypted passwords that we have today. So it it could be an interesting problem to solve for tomorrow. Right? Like, how do we secure devices if not with, you know, with passwords and things? Yeah. I think that one thing that it was around 2015 when I went to ACM of ours. I don't know if anyone familiar. Association of Computer Machinery is, like, one of the most prestigious computer science, association, and this award is, like, a Nobel Prize for scientists. So that year, I think, was Bernice Lee who invented the Internet who got the award, and it was at the award ceremony. And there was a committee who is protecting our data. And it was very interesting to me because all of our data now is binary. And with quantum computing, be the future, and it's not transferrable, how are how are our data gonna be read with quantum machines? Who's gonna protect it? Who's gonna keep a repository of all of our data? And it was surprising to me that there is a whole committee who's responsible for taking care of our data, you know, as a society. And, they're thinking about all of these things. It's like, how do we transfer this data to quantum later on? So people can look at their images, or look at their binary data. But, yeah, if when you go to these places and you see people forward thinking these many levels, you'll be amazed to hear, like, there are people actually thinking about this thing. Yeah. And I also assume one thing I've heard you talk about in other podcasts as well is networking, especially as you just wanna become a better a better engineer, better employee, networking with people. And I'm sure those types of events also introduce you to people with really interesting things that they say and and even new topics that you can then, 5 years later, start to investigate yourself. And so what's the for for this podcast and for the people listening here, what is the value of networking that you, you know, find yourself and that you train for or or you you pass on to your mentees as well? Yeah. That's a good question. Well, a couple of things. First of all, don't network because you want something from people. Network before you want something from people. So beat those relationships before it comes to, like, looking for a job or, you know, changing your role. And, I always call it vertical and horizontal net networking. So when you are at a role in in your company, you need both of them. So you start the first three months of your role to 1st 6 months doing vertical networking. And by vertical, I mean your team, your manager, your skip manager, and everyone in the chain, and your close partners, so anyone you work with day to day base. So build relationship with those people. But after that 6 months, you start to do horizon networking, which means going outside your team to other teams that you don't know about. A lot of companies have so many tools. Like, I remember when I was at Netflix, there was this coffee chat where you could register, and you get randomly, matched with someone across the company and you could get a coffee with. At LinkedIn, we have, like, similar tools that you can go in and, you know, chat with people across the company. If your company doesn't have 1, be the leader and build 1, so people can chat with each other. Or just simply, you know, ping people for, like, a 15 minute coffee chat to learn about them, introduce yourself, and, just connect with them. And then later on, you will be surprised how those people would show up in your journey, in your career. Not everyone have to give you a job or refer you. They will inspire you in so many ways as you're networking with them. You may have a question or you may get an idea and you can run by them or they would just be your advocates. They know you somehow. And when there is an opportunity, they remember you, and they put your name up on the table. So that's that's a thing. Always remember. Don't forget that horizontal networking. People usually go to companies, and they just rely on the vertical networking. They don't know really anyone across the company, and it's similar outside. As I mentioned, your career is not bound to a company, so think the same thing. Vertical networking in your area, horizontal networking outside your area, network with recruiters in different companies, network with people outside your field. LinkedIn right now make it super easy for everybody. I think 10 years was their LinkedIn, but it wasn't as active as now where you could find people this easily. Now we are getting to the area of conferences being back again after COVID. So attended conferences, meetups, and chat with people, learn about them, and they will learn about you too. So that's a that's a good thing to be at. And there is so many things nowadays. I mean, you see all of these mentorship platforms that's showing up, which wasn't there 10 years ago when we started. That's a great way of networking, getting a mentor. It's a next level because that person is investing in you. And if someone is invest in you, they're way more willingly advocating for you later on than someone who just you meet in a conference. So go on on these platforms. You know, there's a ton of them across the industry. Maybe get to the session with different mentors, people you aspire to be like them or people you they're ahead of you and you wanna get there. Learn how do they get there and ask for their advice. And then later on, when you build these relationships and talk to so many different people, they will invest in you and advocate for you whenever you are in your career. Yeah. You mentioned LinkedIn was a good resource for that. Obviously, you know, that's bringing the entire network together and having making it easier for people to just reach out. In the past, like, I found it maybe awkward or or scary almost to reach out to people for advice. Like, what would you when you're reaching out to someone that doesn't know you at all completely cold and wanting to network with them, what are some tips that you would recommend on that initial sort of cold message? I won't really do that. Right? I won't reach out to someone I don't know, and I don't advise that. That's why I'm saying, like, network before you actually need an advice, like, have a really broad people that you know. I won't send a cold message to someone and say, hey. I need an advice, and I know need 30 minutes of your time. People are super busy, especially people who are in better stages that can advise you. Right? Go get a mentor. Right now, there is so many ways of doing that. There's all of these platforms. There's Play Doh, Mentor Cruise, MentorMe. I mean, there's so many of them out there that you can find these people on those platforms actually offering you time. So, go find those platforms and find those people, and that's respecting their time and also going there professionally and getting the advice you need. And that will build trust between you and that person because your mentor is now trusting you. And then you can send the cold message after you have couple of sessions with this person and say, you know what? I need an advice on this. What do I do? And they will always there for you. Like, I'll tell all my mentees, like, I'm your all time advocate. Anytime I mentor, I'll be there for them, like, through their career. So, yes, it can happen. People, like, send them cold messages and say, hey. Can you make a referral? And I'm like, I I don't even know you. And that makes me question your judgment because how would you want me someone who actually doesn't know you at all to refer you to a role versus someone who's coming in and say, look. I'm looking for a job, and these are the things I wanna get better at. I have a good understanding of myself. Can you advise me how can I find a job? And then at that stage, when I have couple of chats with that person, may mock interview them, I know how their level and how they are. And I actually myself refer them to places that I think it's a great fit for them. So it happens very naturally when you build the relationship the right way. I am not a believer in those cold messages. Sometimes they work, but, I mostly believe in building relationships that are strong with people that have really good foundation and trust. Yeah. That's that's really good advice. I think it's crucial these days to kind of build those are you when you open a position, are Are you when you open a position, are you first thinking of people who have built that type of a relationship with you, whether they're, you know, someone who would fit the position? And then, are you reaching out to your network of people saying, do you know somebody who might fill this position or might fit this position? I guess, which one or both? You know, do you look for both of those types of sources for people when you're scaling your teams? Yeah. So I try to be fair because I don't wanna only look at the people I know and advocate for them, only because I wanna give opportunity to people who I don't know and they can apply to the role. So we I usually build a pool of people where 10 to 20% of them are people I know and I know it's a good fit for this role, and the rest are opportunity for other people to apply to the role. And I take a look at those resumes too. But because I know these people, because I invested them, when a role pops up, I have them top of my mind, and I ping them that this is a role that's popped up. If you're interested, apply. But I'm not giving them extra benefits as other people. But the fact that I'm pinging them as a hiring manager and I'm saying apply, they will get an interview pretty easier than people who are applying to the roles, especially in this market. You know a role is popping up in 2 hours. There's 100 of applicants. And, if you have that relationships that people know you, top of their mind, that's the key to get a role is that people who advocate for you and put your name on the table without you wanting that. And that's a huge key to success in the in your career is that building those relationship that can result in this. So whenever I open a role, I have people on top of my mind that I know they're looking for a role and a great fit for this role. And, and I really do that because I know it benefit their role and benefit them. So I don't really refer people who are I don't think they're a good fit. And, you know, they've been people that their life changed because of that. I mean, they get into really big tech companies. I had a mentee who was a waiter, and, afterwards, he got hired at Meta, and that's changed his life. So many of these stories, people who are just entering tech and, you know, it's really hard to find your first job and build relationships like this with people across the industry who can refer them and advocate for it. Them. So it's a it's a both mix up pool because fairness is super important when I hire. I wanna make sure I give, like, opportunity to all type of people, be as, you know, inclusive as possible to, like, all people from different backgrounds. Also, look at my team and say, like, okay. Is my team diverse enough? You know, what I'm needing. Like, do I have enough women at my team? I don't want, like, to having 1 woman in the room or 1 man in room to feel, like, isolated. I want everyone to feel belonging. So looking at the gaps on my team and see, like, what are those gaps that I'm looking for to fill in and really building that diverse pool of people, who are coming in to interview. Yeah. And on the taking it from from the hiring side, I'm I'm curious putting you as the candidate. I know you were presented with options, you know, to maybe go to YouTube or Google and and LinkedIn, and, obviously, you you ultimately chose LinkedIn. When looking for a place to go, like, what was that deciding factor that made you choose LinkedIn and and framing that as advice for people who are listening? You know, if they're presented with so many options or so many different paths, like, how do you choose? Mhmm. So, the framework I walk my mentees through is we do have a value table. Before you actually start job searching, you put together maximum five values you're looking into the next role. What are the things you really want in your next role? Example, for me, like, people mattered. I wanna work in a culture that people come first. Growth mattered. I wanted to work in in a company that supports my growth. You know, technology mattered. I wanted to be a cutting edge technology. Product mattered for me. I wanted to work on a product I've never worked on before, and there are a couple of other things that I have in my value table. And whenever I am interviewing a company, and I always say that interview is a two way assessment. There are things you're assessing too. These values are the things you have to assess. Ask the recruiter. Ask the hiring manager in your first interview. What is the growth framework on your team? Who was the last person who got promoted on your team? How does promotion happens? How do you support an engineer who falls behind? How do you onboard people onto new team? You know, all of these questions you can ask to assess how good is that company and that team role for you in terms of your value. Ask about the product if it matters. Like, how old is the product? Is it a new thing? Why are we is this a bet? You know? Is it something that we're just gonna do right now and just, disappear, like, a year from now, or is this something the company wanna invest in long term? If these things matter for you, be prepared to ask these questions because a lot of people go into roles blindly just because it's at a good company, and then they're not happy there afterwards because they're not assessing in their interview, their role in the company. And then when you have these values and you're interviewing, I give a score from 1 to 10. 10 means they're great at it. 1, they're very poor. And, then when you're reaching to the offer stage, like top 3, top 2, interview that you're doing, you can see the differences. So, for example, for me, between YouTube and LinkedIn, it was a product. I wanted to work on a product I've never worked on, and for me, YouTube was a streaming again. And I'm not a person to go in-depth. As you know, my personality, I love the breadth. I love to get things which is new. I never work on a social media platform like LinkedIn. So that was a a factor like, one of the biggest factor on that table that I picked LinkedIn over YouTube. So if you have that values, it's easy to to know, like, which one I I wanna go to and which value I wanna compromise, which one is compromiseable for me, which one is nice. For me, work life balance is not compromiseable. It's a value. I do a lot of things outside work, and work is not my only thing in life. So, I wanted a company that, you know, I don't have to work 247 because I really don't admire that type of environment. So I think it's toxic. So what are those values and which one you cannot compromise and assessing those over your interview then when you're at a offer stage? You you don't have a hard time to know which one do you have to pick. Yeah. Yeah. I I think I saw that you last end of towards the end of last year ran a marathon, and so those types of hobbies, I think, are pretty important. You know? Pedro and I play music music, and, I think Pedro is a coffee aficionado, so he loves making coffee. So I think it's important to have those things outside of work to to make sure you stay balanced. It sounds like you have several ways that you evolve yourself. You know, you you said you mentioned reading books and just being curious and learning new technologies. You probably also have your own mentor who you who you get advice from or several mentors. So is it is it hard, or do you just find it come naturally to still make sure that you're growing as a as a person? Do those does that time come easy, or do you have to make sure that you, you know, spend a couple hours a week making sure that you're growing as a leader as well? That's a great question. I feel that's come naturally for me. It's not like I mostly build habits versus, like, I have to read, like, 3 hours per week. It's not like that. It comes as a habit. You all know the book, Atomic Habits. Right? It's like stacking things and building habits and systems. For me, mostly, it's systems in my life. And you can't have all of it at the same time. Like, you can't go to the gym and read books and be great at your work and get a mentor and run projects. It's not possible. You need to have balance long term, but in short term, you may not have a balance. So let's say if you if you wanna get better at something and you have, like, 3 books to finish, you may have to cut off from your gym time or running time or whatever you're doing at on the side for, like, a week. And then a week after, you know that what is cutting off was my running, and now I'm done with this book. So this is the things I'm gonna prioritize. I'm pulling it back up this week. I'm gonna focus on it more. So in long run, there is a balance between all of these things you're doing. But in short run, you prioritize between these things because you can't have all of these things together. And then you build systems for yourself. Systems of learning. What's your system of learning? Is that books right now is so integrated into my life. It's something that, you know, I usually read in the morning when I wake up or if it's a book. Even learning how to read a book pretty fast is super important and summarize it so you can refer to it later on. But building that systems that actually can help you. For me, Fridays is my self reflection days. It's a day I have a session in the morning with my mentor. And after that session, I self reflect on, like, how what did I do this week? What are the areas I did really well? What are the top things top five things I did at my work? Then that top five things goes to 1 on 1 next week with my manager. And this is kind of what we call it managing up. Like, how do you how do you manage up, and how do you tell your manager and your escape what are you doing, and it's getting harder as you're going higher and higher. So that's that's a way of me managing up by taking that 5 outlines and putting in my 1 on 1 doc, and then self reflecting of what are the things I have to do next week, looking at my team structure, how everyone is doing, did I capture what they did greatly this week? I do that for my team too, like, every individual on the team. I have a running doc that I say these are the things they did really well this week. So when it comes to their performance review, I am very detailed for advocating for them, for every engineer, and I'm taking all that recognition in. What are the feedbacks I have to give them? I'm very proactive on giving my direct reports feedback. I will let it sit. If someone is not doing something very well or there is there is a gap that they can do better, it would be right away. Every week, I'm thinking about their performance and how they did. So it won't be a surprise. You know? There are some managers that suddenly come after, like, a year, and they say, you didn't do all of these things. And, that's hinder trust. The trust is broken at that stage. So the more honest and the more periodic and the smaller these pieces of feedbacks are and very targeted, the better those those people can grow. So Friday, my system of self reflection into my life, plan of next week, How's my month look like? Am I having enough fun? How's my gym is going? How's my workouts? Relationship wise, am I spending time with people I love? Like, sometimes I'm so much slanted at work. I forget about my friends. I'm like, next week, I'm gonna set time with my friends. Next week, I'm gonna spend more time talking to my mom. There are, like, 4 categories in my life. It's career, which is one that you all have. It's love and relationships, like you being very socially, satisfied with, like, going out with people, your best friends, your family, your close relatives, spending time with them. For me, financial life matters. I'm doing on that. Am I, like, on track with my goals? And then, health, like, in terms of am I drinking enough water? Am I a happy person? How's my mental health? Do I need, like, some of the weeks that I'm very exhausted? I usually go to a lighter sort of workout. And I'm like, okay. This week, I'm gonna go do yoga instead of running because I want less intensity in my life. So how are you assessing that and then having that, like, long term balance? There is this book also called design your life where it talks about these categories, which is something I took from this book, And then, having that categories help me to balance. So I measure every category every week, and I see how I'm doing and which category requires attention. I prioritize a week after. I like that. I have to check out that book. Yeah. I'm I am writing that down because, there's definitely weeks or several weeks in a row where I might neglect 1 or 2 of those categories for sure. So that's that's a good book to write down. Awesome. So, yeah, I think, we're coming close to time here for you know, thank you, Naz, for your for your time. I think maybe we could wrap it up with, you know, what's some advice that you would give to anyone who's listening, maybe something top of mind that's, you know, come across your mind over the last week or 2, that you'd like to just kinda pass along? And then where can everyone find you and and follow your journey? Yeah. Sure. Oh, that's a hard question. I mean, something that has been top of my mind. I was reading yesterday a quote about someone who lays off. I think it was a company called Clarify, and she wrote couple of points I shared on my LinkedIn as opposed to where things I've learned from layoffs. And one of it is, like, really thinking about how do you not bound to one job and then have some back ups, make sure that that happens. The other was, like, really thinking about yourself and getting yourself back up. And, one point that was funny, and a lot of people didn't agree, is that people are there for you when you get laid off. But the reality is they're not. Everyone having their own job, and you can expect that you're getting laid off and everyone is there for you. And now your friends are taking you to their companies. I mean, they do their best, but it's not the case. So be prepared. I mean, be prepared. Have your resume ready all the time. I mean, don't just get on your resume when you need a job. Your resume is a reflection of you, and it needs to be ready and updated. Your LinkedIn needs to be ready and updated with latest things you do. And then always and always have 1 or 2 interview per year. What are you doing with the company? Even if you're at a place that you're comfortable, always, always interview. You are you'll be at the ready state all the time. Also, you learn about the market and what's going on, in other companies when you interview with them, and it's a it's a way of also networking. So when you interview with hiring managers and they get to know you, later on when they have a role and if you perform there in that interview, they're gonna they're gonna reach out to you. So be ready, before something happens is actually the key in this market. And knowing that your work is not bound to a company. You're taking your work to different companies' missions and vision and enabling part of their journey as a really amazing engineer. And, none of us have to be in a place forever. At least in tech, it's not the case. So Yeah. Awesome. Yeah. I I think I think you also once mentioned sorry to sorry to jump in there. I think you also once mentioned around the interviewing process. You also just received feedback feedback from those people about how you present on a camera or your answers, or you get some valuable feedback from those interviews as well. Yeah. And be very transparent when you do those interviews. Like, I'm not actively looking, but this looks like a good opportunity. I wanna I wanna assess and learn more about this company. Why not doing an interview? And putting yourself in those situations more often makes you more comfortable. You'll be more confident. So if people wanna find me, I'm on LinkedIn, nazdaalum. Also, you can find all my handles on my website, nazda dev, and feel free to reach out to me if you have any questions about anything I mentioned. Hopefully, we can have those books listed, John, somewhere, so people can find it on-site reference. Yeah. Yeah. I was definitely gonna say I'm gonna keep in contact with you just to see what books you're reading. Awesome, Naz. Well, yeah, I I really I personally found a lot of value from this conversation. There's a lot of things that, you mentioned that I just tend to forget over time, and so I definitely found some great value out of this conversation. Everyone, go follow her on LinkedIn. Pedro's battery is exhausted. My battery is exhausted, so my camera died, but I am here. Thank you, Nez. Thanks so much for your time. Yeah. Thank you, everyone. Thank you for having me. It was great.","a60d2206-ae2c-4b55-8852-1d6ccdb37b08",[2874,2875,2876],"85ed5f74-71ab-41e4-819b-8fbf9016b148","fffe5668-66d7-4e0f-a750-7681b9b28ed5","b5fd03ff-9663-403d-8a8f-3e70b923f054",[],{"year":978,"number":1830,"id":1842,"episodes":2879,"show":2880},[1844,1845,1846,1847,1825],{"title":1849,"slug":1850},{"id":1796,"slug":2882,"vimeo_id":2883,"description":2884,"tile":2885,"length":2062,"resources":2886,"people":12,"episode_number":11,"published":2868,"title":2889,"video_transcript_html":2890,"video_transcript_text":2891,"content":12,"seo":2892,"status":19,"episode_people":2893,"recommendations":2897,"season":2898},"strictness-of-api-errors","950779919","In this recording of our live event on May 23 2024, Daniel, Jonathan, and Rick discuss making the strictness of API errors","6d44c6a0-aaa7-4ad2-bbcb-d6e0f1de2829",[2887],{"name":1775,"url":2888},"https:\u002F\u002Fgithub.com\u002Fdirectus\u002Fdirectus\u002Fdiscussions\u002F4368","Strictness of API errors","\u003Cp>Speaker 0: Thank you for joining for this week's, session of request review. We have a fascinating topic for you, which is a little deeper than you think from the from the title. But thanks for joining. We hope you like it. This week's topic is about the strictness of API errors.\u003C\u002Fp>\u003Cp>So what does that even mean? Let's kick off with that. Everybody knows software doesn't work always quite how you want it to work, so sometimes you will receive errors in your application, and Directus is no exception. So as you might have noticed, we are very strict as of right now. So if you do something wrong, chances are high that you're just gonna see the message forbidden.\u003C\u002Fp>\u003Cp>Like, even when the message, even when the error isn't actually that type of error. So why do we even do that? Throw throwing the, throwing the conversation ball to mister Reich over there, please.\u003C\u002Fp>\u003Cp>Speaker 1: To me? Oh, man. Why do we do that? Well, it stems from a security report back in those days in that if you, let's let's use let's use a, a a 4 zero four as an example because it's it's an easy one to think about. So let's say you're fetching a collection that doesn't exist.\u003C\u002Fp>\u003Cp>Right? So you should get a 404 because the thing doesn't exist, so there's no route for it. No endpoint. If we would return a 404 for those, you could theoretically extract what the data model looks like by brute forcing your way through the whole API and checking which things return a 404 and which things return a 403, which, you know, to some security minded folks or setups, that's an issue because you don't wanna expose what type of data that you're managing in this system for security reasons. Now this is immediately where we're kicking off with opinions.\u003C\u002Fp>\u003Cp>Speaker 0: Let's go.\u003C\u002Fp>\u003Cp>Speaker 1: Because you can definitely, you know, you you can see it from both sides. You can easily say, well, that just makes the developer experience very annoying. And that's also why I created this feature request the moment I built it in this way. But it makes sense from a security perspective. Right?\u003C\u002Fp>\u003Cp>It's a similar reason why, during login, it'll say wrong email password or account doesn't exist, but you never exposed which of the 2 it is just to make sure that people can't slate, you know, what users exist. And the same goes for, you know, individual items in records. It's like you wanna, you wanna make sure that you can't just say, okay, slash item slash xyz and just keep scraping all of the different numbers, and now you know exactly what records exist, etcetera, etcetera. Somebody rightfully called out, you know, nobody does that in this trustworthy world, which I think sort of concludes this discussion then. Just ignore it.\u003C\u002Fp>\u003Cp>Speaker 0: Lying on the Internet? What? Who does that? No. What?\u003C\u002Fp>\u003Cp>No. No. No. We don't do that. And a perfect example.\u003C\u002Fp>\u003Cp>So leaking of information is a very, very big thing. You don't want to expose stuff that you don't have to expose, but as you just said, it would be nice, for example, like the most basic, setup when you're just developing locally, it would be nice if you could actually see what went wrong. Because currently, it's just okay, forbidden. Nope. No information for you.\u003C\u002Fp>\u003Cp>So then the question comes. Oh, La Prima asked, can we go on stage? Sure thing. I think so. But let's let's first, summarize a little bit.\u003C\u002Fp>\u003Cp>So There should be\u003C\u002Fp>\u003Cp>Speaker 1: a button somewhere.\u003C\u002Fp>\u003Cp>Speaker 0: The question is. Yeah. Yeah. Exactly. So, immediately the question comes to mind.\u003C\u002Fp>\u003Cp>Okay. So if you would have multiple errors in that case, a strict one which just says forbidden and another one that has more information, Okay. How many different errors would you even like to have? So are we happy with just 2? Because, I guess this is opinion alert or something, or maybe even a useful thing for many other people.\u003C\u002Fp>\u003Cp>Like, what what do you guys think about this? Like, how many error, different error, messages would be nice, would be good. Useful.\u003C\u002Fp>\u003Cp>Speaker 1: I mean, that is very much a sliding scale, isn't it? Because you really go from, from like, just give me every single thing. It's like, oh, I was trying to read the articles collection that I don't have access to. So I'm gonna get a forbidden error that says, well, articles exist, but you can't read it. And that's because of this permission that you don't have.\u003C\u002Fp>\u003Cp>For development purposes, that's really nice. But if you go to production with that, it's tricky because now you're just exposing a bunch of information about, you know, your setup. So, yeah, I'd I'd say it it there's a large range there's a large range in in that. And even within that forbidden error, you know, we can, think about how we wanna return stuff. So one thing I've been working on recently as part of this the the rules and permissions sprint that we'll we'll ship in soon is the, is that forbidden errors now include a little bit more information as in, you know, the field x that you were trying to query, you know, you don't have access to it or it doesn't exist, but at least it's a little bit more explicit while not necessarily exposing the schema, but just exposing what part of your query is wrong.\u003C\u002Fp>\u003Cp>Right? Which is not leaking any of that information but still being a little bit more useful.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. That does sound exciting, to be honest. Like, it's such a developer thing, like, normal users. I don't know if how how many people care about this, but this is such a nice thing for developers. So I gather from your answer right now maybe 3 types of messages.\u003C\u002Fp>\u003Cp>At least from your answer, it sounded like 2 in the beginning, like, one strict one with a little bit of information, but that could be counted as 3, right? Like the very strict one with nothing, a strict one with a little bit, and another one with very much information. Are we happy with 3?\u003C\u002Fp>\u003Cp>Speaker 1: No? Yes. Asterisk. There's one more tricky thing in that the output type signature of the errors themselves change between those modes Right? So for example, if you're trying to fetch a thing that doesn't exist, we want to return a 404 status code with a not found error message.\u003C\u002Fp>\u003Cp>If you then program your app around that, you know, that it on your front end, it shows, you know, something because it checks 4 Oh, it was a 404 status. Therefore, do this logic. Right? The moment you now change that option to be strict when you go to production, that messes up again. Because now it's returning a 403 with a forbidden instead of a 404 with a not found.\u003C\u002Fp>\u003Cp>Cricky.\u003C\u002Fp>\u003Cp>Speaker 0: So Let's see. May maybe maybe after we dive in a little bit deeper, we have more clarity around this. But so far, like, I personally like would like, I think, 3 modes. Let let's call them levels. Do we call them levels, modes?\u003C\u002Fp>\u003Cp>I think levels. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It depends. As per usual, it's\u003C\u002Fp>\u003Cp>Speaker 0: great.\u003C\u002Fp>\u003Cp>Speaker 1: It it depends on, you know, are we considering multiple different flavors at the same time, or is it just an on and off? Because somebody in the chat just now rightfully mentioned, maybe it's just a single production versus development flag that changes that. Right? It could also be a setting within the app. Maybe it's even a setting on a per collection basis that you wanna say, well, this collection is not as, you know, private as the other ones.\u003C\u002Fp>\u003Cp>So for this one I wanna have, you know, nice and friendly, messages. And for the rest, we wanna keep private. There's options. It's tricky. It's it's when it comes to a single sort of Boolean flag that says production yes or no, I would be a little bit worried about getting into a point where undoubtedly 3 months later, somebody will show up and be like, well, we kinda need something in between.\u003C\u002Fp>\u003Cp>You know?\u003C\u002Fp>\u003Cp>Speaker 0: Exactly. You\u003C\u002Fp>\u003Cp>Speaker 1: can't really\u003C\u002Fp>\u003Cp>Speaker 2: Or more importantly, somebody develops in development mode thinking they've got those messages, then they move that code to production, and suddenly nothing works.\u003C\u002Fp>\u003Cp>Speaker 1: Right. Which is gonna be a bit of an issue no matter what because\u003C\u002Fp>\u003Cp>Speaker 0: Agreed.\u003C\u002Fp>\u003Cp>Speaker 1: We're not talking about, you know, changing the output errors. You could consider those, part of, the the what what would you call it? The public API contract of the APIs that you're building against. Right? Because because, you know, in your intro then, you said stuff sometimes crashes, and that's just part of part of software life.\u003C\u002Fp>\u003Cp>Stuff crashes on purpose. Right? In this case, like when validation fails, it's we're still throwing an error, but it's not an unexpected error or anything else. It's, you know, we stop you from doing something on purpose, so we throw an error, which is, you know, a bit of a difference there as well, of course. Because when it comes to exposing internal server errors, it's a whole different question.\u003C\u002Fp>\u003Cp>Because right now we do a similar thing, right, where we say you just get internal server error. That's it. Like regular users don't see anything else. If you're an admin user though, that's today, that's the difference. It'll show, you know, the stack trace of the actual JavaScript error, with where in the source codes it it it happened.\u003C\u002Fp>\u003Cp>So but exposing internal server errors to non authenticated users, that's gonna be that could easily become an issue. Right? Because now you find some sort of vulnerability path or whatever that's you don't wanna expose that.\u003C\u002Fp>\u003Cp>Speaker 0: Definitely.\u003C\u002Fp>\u003Cp>Speaker 1: From the chat, somebody has a suggestion here. It says, what about setting a trusted IP address? So you can just say, okay. When I am the one from my home to do this, I get all the pretty errors and otherwise, I can't. I think you're gonna have a similar issue with, you know, def versus prod, strict versus friendly error types, and how do you handle those in your app.\u003C\u002Fp>\u003Cp>It's gonna be a tricky tricky thing to figure out. And then somebody else said, the only thing where that really isn't enough is when there's a delete constraint. Users trying to delete items, and they can be deleted because they're still a referencing item. Admins already get a little oh, yeah. That's yeah.\u003C\u002Fp>\u003Cp>Because right now that is technically, a database error that bubbles up. So it comes out as an internal server error that for admins, it'll show the the SQL error underneath. But for regular users, they just get a whoops, you know, internal server error.\u003C\u002Fp>\u003Cp>Speaker 0: Also with the IP, since currently currently, IP access, is attributed to roles. Right? You you customize that on a role with that access. So we could use that, but it does, like, sound very, very, very likely to me that you wouldn't be interested in, like, giving this to every single user of that role. You would like to have something more granular than that.\u003C\u002Fp>\u003Cp>So how about this also comes from, someone from the community. How about we attach this to permissions themselves? So let's say this user can access this and that, and that user is authorized to receive better errors than other users. For example, like, the simplest example, like admin users. Right?\u003C\u002Fp>\u003Cp>Mhmm. Like, all admin users are allowed to get the best errors that you can, But users other users get strict errors. Okay? But that, yeah. That's that, like, I I I'm not really sure how exactly complicated that would be to do at every single point in the in the code base.\u003C\u002Fp>\u003Cp>Speaker 1: Well, I mean, in in terms of implementation, how like, they currently were throwing forbidden errors all over the place, but the way this would work in implementation is right at the end where we return the error in the APIs, that's where the sort of translation layer kicks in. Right? So all over the code base, it should just throw the actual errors or the actual error types and the correct ones. So if you're trying to hit something that doesn't exist, it should throw a not found error. And then all the way at the end in the middleware stack in the error response handler, it should say, oh, if your current, you know, e n v setting for errors or if the, which I'm gonna call it, if the the error permissions or whatever you wanna call it are set to a certain state, then it translates them into a different way.\u003C\u002Fp>\u003Cp>I think that's the way that that will be implemented realistically speaking.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That does sound quite good. Adjust intercept as a middleware. Okay. You are allowed to see this or not.\u003C\u002Fp>\u003Cp>Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Because we're doing that on a case by case. It's gonna be insane. Just trying to do it every single time that we throw an error. That's that's too much.\u003C\u002Fp>\u003Cp>We currently already do a similar thing, right, where the the error handling stack, we check was this a direct as error type or any other error and that chooses between internal server error or, sort of the the expected error output.\u003C\u002Fp>\u003Cp>Speaker 0: Very reasonable. I think, if somebody from the chat also has another recommendation or anything, please let us know. There's quite a quite a bit of action today, which is pretty nice, actually. Like, seeing the chat scroll up and down is pretty nice. Somebody says, I can't explain it in the chat, but I'll try.\u003C\u002Fp>\u003Cp>That's lovely.\u003C\u002Fp>\u003Cp>Speaker 2: Well, well, La Prema can if if you you guys are okay with it, La Prema can join us on the stage.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I'm down. There should be a button somewhere that just says, I\u003C\u002Fp>\u003Cp>Speaker 2: can invite them or they can add they can request, but we can also do an invite.\u003C\u002Fp>\u003Cp>Speaker 1: How is that often with these?\u003C\u002Fp>\u003Cp>Speaker 2: They asked to speak earlier, so I'm inviting. Or have they does Kevin have that blocked in this event?\u003C\u002Fp>\u003Cp>Speaker 1: Oh, boy. With this guy.\u003C\u002Fp>\u003Cp>Speaker 2: Nope. Nope. There we go.\u003C\u002Fp>\u003Cp>Speaker 1: I got the invite. I got Does it work? I got\u003C\u002Fp>\u003Cp>Speaker 2: him up here.\u003C\u002Fp>\u003Cp>Speaker 3: I'm trying to\u003C\u002Fp>\u003Cp>Speaker 0: see if I wait. Weekend.\u003C\u002Fp>\u003Cp>Speaker 3: Yeah. It works. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: It works. Hello. Welcome. Hello.\u003C\u002Fp>\u003Cp>Speaker 3: As always, sorry for my accent. I'm I'm Frenchy. But, like, I Don't worry.\u003C\u002Fp>\u003Cp>Speaker 1: You're you're chatting with a Dutch and a German, so we we know you need to know the guy.\u003C\u002Fp>\u003Cp>Speaker 3: Oh, yeah. But your English is better than mine. I don't know if you remember, but we used to talk to Get A Rich for about the the error management system. And at that time, I was working for a company who uses directors, but I leave them. So after, what I haven't the time to to follow the the the case of the the server management system.\u003C\u002Fp>\u003Cp>But, I think the the main problem, we had is is, dealing with the the the, yeah, the, like, the granular system of a thing. And what I proposed at the that time was, like, adding in the the collection a a new, column, like, you already have, edit and all that stuff. And, in permission, column where you will have, like, 1 or 2 level no. Mostly 2, but 2 or 3 level of, strictness of the error. Like, do do do I wanna send them all on that collection or not?\u003C\u002Fp>\u003Cp>Because, yeah, I I think that would be the I'm sorry. But because it's user related, it would be better for the companies. Because, if you if you, like, have only one private, instance of directors, I think it's not a problem to throw error to people because they are part of the company. So if they don't, respect them, their permissions, you they can have, like, legal, troubles, and this is possible. But if you have, like, an public API and also a private one, On one instances, if it's just a mode, I think it won't fit because a lot of data can be really sensitive.\u003C\u002Fp>\u003Cp>And, same goes with the developer mode. Yeah. I I totally agree with, like, developing something, in a in a development mode and then going to production, it doesn't work. That would not be a solution for me. And, as well that the the this this production and development mode would be probably used somewhere else, at another time.\u003C\u002Fp>\u003Cp>And, in a long term view, that will just lean in people active in the development, mode every time and, development searching, be thrown everywhere.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's right. It's such a tricky, and I I think if that, what was that nodes flag called? It node e and v or something as taught as anything is that you should not rely on a single dev and prod flag. Yeah.\u003C\u002Fp>\u003Cp>No. It's a good one. It's the the doing it on a collection level, on a permissions level for for collections, basically. It's it's a good angle. It's it raises some different interesting questions though as per usual.\u003C\u002Fp>\u003Cp>Everything does, Especially around requests that touch multiple collections at the same time, right? Where in REST, you can technically go, you know, nest it and then you end up with you could have an error that's thrown in the sort of nested layer of the same request. Or in a GraphQL request, you can technically request multiple collections at the root level at the same time. And then it gets a little interesting because now you can get, you know, a different error output from that request based on which of the data points of that request crashed, I guess, instead of, you know, the request itself,\u003C\u002Fp>\u003Cp>Speaker 3: which opens a little\u003C\u002Fp>\u003Cp>Speaker 1: bit of a different kind of words.\u003C\u002Fp>\u003Cp>Speaker 3: Yeah. But I I think in some case, that's what we want. Because, in the the app I I was working on, we we used to to to get in that kind of problem where, we have people, who had access to certain company, but not certain order. And we retrieve that data in one block. And if some points fails, the the whole, request will fail, so we'll have to deal with that.\u003C\u002Fp>\u003Cp>And, what what we can do at that point, I guess, it's when you you have the error only in one part of the query, it's just returning not an error not an error code, but just like in the API, an error, tag in, that object and not all the thing. But I I know that would be, like, a big breaking change. So it can be hard to implement for some people. But, I guess, by default, if we just disable all the error, like, the the basic settings will just be, what Direct just currently does, it won't be a problem because the people who turn the the feature on, would know what they do or or would have ways to find out.\u003C\u002Fp>\u003Cp>Speaker 1: I think that is\u003C\u002Fp>\u003Cp>Speaker 0: You're you're just assuming that people know what they do. I barely know what I'm doing. And when when I add features oh, no.\u003C\u002Fp>\u003Cp>Speaker 1: I think, it it there's a there's a new question hidden in there that we hadn't touched on yet, nor in our notes right now, which is if an error is thrown somewhere nested, let's say, you know, the let's use the GraphQL as an example because it's it's easy to reason about. Let's let's say you're requesting 5 collections of data at the same time. Right? And one of the 5 crashes. The question is, do you return an error and the whole thing failed, or do you return the data that worked and then have an additional error flag that just indicates this is the path of your query that didn't work?\u003C\u002Fp>\u003Cp>But return what you have and leave the rest.\u003C\u002Fp>\u003Cp>Speaker 3: And, I think for that opinions. Yeah. I think for that, the the the best way to to do it is just, like, throwing the error only for what phase. Because, if we use these directives, I assume it's that we just want to cut the the front end. And and so, it it would be, most of the time, like, an interactive app or a single page application on or something of of that kind.\u003C\u002Fp>\u003Cp>And those kind of errors are really easy to to treat with that method. So and and, I I think most of the time, if we activate the the feature, it's what we want because, that will lead in, less, just big crashes, like internal server error proof and nothing worse. So I don't know.\u003C\u002Fp>\u003Cp>Speaker 1: It's it's an interesting one because, somebody in Chet just now says it too. It's a rather fundamental question. But, you know, you see both in the wild, and both are kinda alright. It's it's, there's also an implicit difference between, you know, reading and write operations there as well, of course, that if you do a large nested update, you kinda want it to work as a single unit, as a single transaction to make sure that if, you know, part of the insertion doesn't work or part of the update, the whole thing shouldn't, you know, go through because otherwise you have that sort of half half done state. But for read queries, it could be different, but that really depends on, you know, what are you reading, and is it all tightly coupled, or are you reading multiple things at the same time for convenience sake?\u003C\u002Fp>\u003Cp>Because if I'm not mistaken, in in sort of the GraphQL spec, Like, it allows you to return partial data and then errors for other root fields. But I feel like if there's an error in a nested field in the query, it'll still fill that one sort of root query as a whole. But that is that is something I love to double check.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. I'm I'm I'm not sure either. Like, my my gut feeling was, no. I I I don't wanna say something wrong right now.\u003C\u002Fp>\u003Cp>So, okay. Another another suggestion from the chat. How about hashing the error plus a UUID, I assume that is? Only admins will be able to see what the error really is. Well, I mean, I mean, you are probably talking about, like, error codes.\u003C\u002Fp>\u003Cp>Like, for example, many different other apps just say, okay this is error 3, 1,172 or something. And then you can look up internally what that exactly means, But, you would still be missing out on, like, more information, like the stack trace or which which file failed or something something that you would have to encode in that thing, but I don't think that's always possible or\u003C\u002Fp>\u003Cp>Speaker 1: even Or to just think very divergently. You know, an alternative approach is that you basically encrypt the whole error, save that to a database, and then only return the ID of the error that it was. So then the admin can log in and look up what the full stack was and and and and sort of decrypt it and see the whole the whole contents and everything else. But that that feels like a way to sort of persist to security and give more information at the same time. But it also feels like another skip and a hop and a skip for regular users to be able to just, you know, use do the errors that are output.\u003C\u002Fp>\u003Cp>Speaker 0: That's an interesting thing. I have never thought about it like that. People do that? Do their apps do that? That's interesting.\u003C\u002Fp>\u003Cp>Speaker 3: Yes. Some app does. I I used to work on one with the that. But I think it's the it's a problem because, like, most of the time, you you wanna informing in some way your user of what the error is because you don't want to to get someone just to to create a ticket to the IT support just every time they need, something, didn't work. But they just didn't have the permission in the first time.\u003C\u002Fp>\u003Cp>So, yeah, I don't think, it's a global sit solution. But maybe in some case in some case, if if we add, like, an EMV approach, that would be a a great option for some people that maybe need that.\u003C\u002Fp>\u003Cp>Speaker 1: And I could see it work like that for for internal server errors. Right? Well, that's one of those things where we wanna hide away the stack trace no matter what for for the API output and then save it elsewhere. But at the same time, you know, saving every error could easily blow up in your face too, because if you just have, you know, an unauthenticated user that tries to access some data there's no permissions for, we don't wanna save those. Right?\u003C\u002Fp>\u003Cp>Because now you could easily just blow up a database by a public malicious actor that just spams your API knowing that they're gonna get errors. So there is there is definitely a a difference between sort of expected and unexpected errors there and which ones are saved. Have AI blocked them. AI solves all of our problems.\u003C\u002Fp>\u003Cp>Speaker 0: Sure. Just do what I want. Parentheses open, parenthesis close. That should do it. Right?\u003C\u002Fp>\u003Cp>If only if only it would be that easy. K. So let's let's summarize a little bit because we have been, you know, throwing stuff out there. Maybe we should get back to reality a little bit. So, okay.\u003C\u002Fp>\u003Cp>So so let's say we have, we like to use the Moscow list. Right? The must have, should have, could have, type of stuff. So for must haves, let's let's get on the same train. Like like, what do we need?\u003C\u002Fp>\u003Cp>What what must we include? So, yeah. How granular do we make this? Oh, no. It has to be actionable, man.\u003C\u002Fp>\u003Cp>It has to be actionable. Okay.\u003C\u002Fp>\u003Cp>Speaker 3: I I think the the point where we've been stuck the the last time was reached is, like, we we both agree on the fact we need a granular system, but we both must agree, on the fact that it would be so long to to make and to think and all that stuff that maybe it would be nice to have something, before that. But,\u003C\u002Fp>\u003Cp>Speaker 1: Right. Yeah.\u003C\u002Fp>\u003Cp>Speaker 3: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 3: But, I I think, that's, my my point was, like, directors, is a tool who is proposing things like a a different way than the other, tools in the same category. Like, you you just want it to to be a little more, accessible and, and usable. And not just, like, being that's the way you should do that and, just follow it. And and so I I think that's the problem there. If we did something else, than a granular system, you would, follow a path that doesn't feels like directors to me.\u003C\u002Fp>\u003Cp>And I don't know that that's totally your point, but but, I I'm not sure if that's what people need. Otherwise, they they will lose WordPress or or something like that.\u003C\u002Fp>\u003Cp>Speaker 1: You you you sound like a team member in the sense of when the choice arises, let's do it the difficult way and make it unopinionated wherever we can. So no. I I do agree though. I do agree because it's it it is at the end of the day, this is a very opinionated thing, where for some apps, it's acceptable to have it a little looser or even want it. For some, you know, security minded folks, it's absolutely not, and we can't really make that decision for people at that global level, even though we have right now.\u003C\u002Fp>\u003Cp>Oops. Right now we are on the side of safety first, which, you know, it's it's defendable, but not ideal. Like, that's also why I created that feature request. It feels like decades ago, but it was 3 years. I think that the the main unresolved question now, and and that is sort of to answer your point, Dan, before we can really fill in the blanks for what are the must haves, is what is that ideal approach to how do you configure this?\u003C\u002Fp>\u003Cp>And then from from a developer experience first and then secondly does that make sense from an implementation perspective. Because doing it on the collection scope level makes sense for errors that are thrown on the collection scope, but not all errors are thrown within the context of a single collection. So that that that opens up, you know, new questions. And the opposite is true for doing it globally on the e and v level. Right?\u003C\u002Fp>\u003Cp>Because, you know, it's a low pretty much excellent point. It's not that black on white that you say make it insecure for the whole API. You know, you wanna do it on a case by case basis. That is a that's a that's just a statement with no no answer.\u003C\u002Fp>\u003Cp>Speaker 3: No. I don't another, I don't know what to say it in English, but, like, another thing we, think about at time was, like, just, if if we take, like, the the point, if we need the error, it's just to treat them. One, thing, which, was a a little bit, between the two option is just like, having some kind of way to tell directors just, okay. This error, I want you to to throw it, and other errors I don't want. So that would be a little bit more granular, but that would be more like, just a techy approach.\u003C\u002Fp>\u003Cp>And and then not, being, so universal.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, Rike, you're muted, by the way.\u003C\u002Fp>\u003Cp>Speaker 1: I hit the wrong mute button. That's what you get when you have 2. I I muted myself here, and then I and then I muted myself to unmute on this. Oh, man. I mean, we've only been doing this remote thing for about 5 years now, so I'm I'll I'll get it.\u003C\u002Fp>\u003Cp>Yeah. Because I what what I was trying to say is that that approach to say, okay, which error types do we expose? That is something we could do on the role level or soon to be policy level, And it is a system where we can still have one translation middleware that handles that error no matter where is thrown from, which is you if you compare that to the sort of collection idea that we had earlier, that one is a little trickier to implement in that sense, because now you have to make sure that you know what the collection context is for every single function call that may or may not throw an error. And then do that sort of translation piece, in in situ of where those errors are created, That makes sense. Whereas with, you know, an approach where you say, okay, not found errors can be exposed.\u003C\u002Fp>\u003Cp>If that's your setting, we can do that on one middleware at the end of the stack to just say, okay. Was the error\u003C\u002Fp>\u003Cp>Speaker 0: Again, I'm not sure if if I can wrap my head around every single edge case, but, like, it does sound and may maybe I understood it a little incorrectly, but it does sound quite easy to mess that up. Like, it to to to bubble to bubble something up that does not get blocked then. And somehow I am exposing a random error that I have not thought about, like, on on an error level.\u003C\u002Fp>\u003Cp>Speaker 1: It's it's the difference of an allow list versus a deny list is basically what you're saying. Right? So if you have a filter where you're denying individual pieces, it's easy to miss something. Whereas we do the opposite way where it's blocked by default and you allow more and more stuff, it's you can't really accidentally expose anything because nothing is exposed by default. Which is similar to what, Andre Andre Andre Key are.\u003C\u002Fp>\u003Cp>Just gonna assume that's how you pronounce it. Just said in the chat as well as, you know, if you really want it super granular, effectively just make it a rule based system, right. Where you run a filter against the error object that is thrown. Like if the error types is XYZ and the extensions contain blah, blah, blah, collections, such and such, then expose it, which will be one way to make that super granular, but also a little harder to configure at that point. Because I could, I mean, at the end of the day, I could also imagine that, you know, for just local development, that is gonna be a pain in the ass because now you have to configure so much stuff to get the errors out.\u003C\u002Fp>\u003Cp>Speaker 3: I I think we what we could do is just like doing an EMV, variable just for real development case, then that system for the production mode. And later on, if we think it's too difficult or, then we can add the system, with the the permissions because that would make, like, stacks on top of each each other, with the the the the first step would be, like, the programmatic approach on the error throwing and all. And then, the the we could add later on just, instead of just throwing nothing if the error do not doesn't match, any case. Like, is it a permission error? Then is there a plain way to treat it?\u003C\u002Fp>\u003Cp>So it's made steps, and we have something, in between no permission management and on the other end, full thing.\u003C\u002Fp>\u003Cp>Speaker 2: What this makes me think of is the we've got a client that we're playing around within a proof of concept. They're they're actually have an error table for their front end, and they've actually got translations. So they've actually got a translations table on that so that they can have the language the errors translated to multiple languages. If we went something like that where we have, like, a direct us errors collection, right, as part of the system tables with a set of, you know, with translation, then we have native translations capabilities right there. So if you wanna adjust the error message, so we could have the defaults.\u003C\u002Fp>\u003Cp>Right? The I don't know. We've got a dozen or whatever that we have now, with some variable support maybe even there so that if you wanted collection information or you want that stack trace information to percolate up, you could. And you could determine at least in the short term, we can we can avoid the permissions for the short term, give you just control over your error messages. You wanna show something that we don't do natively by default, well, you can override the messaging as a thought.\u003C\u002Fp>\u003Cp>And I think that would actually help with things like flows as well. We have the same problem in flows. We can't percolate an error message to the user even though we're throwing and controlling the erroring of the of the workflow. Potentially, if we think about it in that context as well where we've got we could use that same error structure\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 2: Idea. Not a not necessarily the solution or the right solution, but\u003C\u002Fp>\u003Cp>Speaker 1: here's the new rabbit hole, multilingual error output for the API. Oh, boy. Oh, boy. I mean, sort of it's an interesting angle to to sort of noodle on a little bit. The the general idea of errors as it stands right now is that you basically return an error code that is one of the standard sort of known error types.\u003C\u002Fp>\u003Cp>And then in whatever app that you're building, you can basically just say, you know, oh, if the error is not found, then use my own translations and my own front end and all that kind of stuff. But yeah. I mean, it it does raise I mean, we we know we need some sort of translations in the API no matter what for email templating, which has been that'll be a fun discussion for another day. But, yeah, it's it's an interesting idea to have errors as a sort of system collection that you can then modify. Although, we would have to hard code in what the keys are because otherwise, we don't know what error to throw still.\u003C\u002Fp>\u003Cp>That's a tricky one.\u003C\u002Fp>\u003Cp>Speaker 2: But we can hard set the keys. Right? And then if you create your own key or we lock the table, you're not allowed to add to it, you know, without core functionality. Or, again, you've got your hard set core pieces. Core knows what they are, and they can't be changed.\u003C\u002Fp>\u003Cp>Their primary key or whatever we wanna do around that Yeah. Gives us that level of flexibility. But then if users want to code other error messages for other applications and data, they can actually still maintain it in all in one place. I'm not smart enough to solve it. I just I just know that's an implementation I saw recently where we're for a front end application and the error messages that they wanna generate for that front end application.\u003C\u002Fp>\u003Cp>They're actually doing that in a set of tables. Kinda cool.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, I think, like, lots of interesting things that we can do, but I think we're still stuck on where do we want to control this or configure this. Let's just go to the one extreme. I mean, very That would be the easiest thing.\u003C\u002Fp>\u003Cp>Speaker 1: You have\u003C\u002Fp>\u003Cp>Speaker 0: to And you get errors. Okay. Done. That's the easiest thing that we can do. That leaves a couple of things that we would like to have open.\u003C\u002Fp>\u003Cp>Right? Like, we ideally, or at least in my mind, it would be really useful if, for example, like, admins actually get no like, good, context rich errors and normal users don't, which would you know, it's not really that possible with that environment variable approach.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So so it feels to me like out of everything that we've discussed so far, the direction where you effectively set up and allow list of errors on the role or soon policy level gives you, so far everything that we sort of want to achieve. Right? Where you can say, okay, you can allow one admin role to just see all of the errors with some sort of wild card flick, accept everything, allow everything. You could use the filter rules to be more granular that you could say, oh, if the error contains a collection, flag, then filter against that.\u003C\u002Fp>\u003Cp>So So you can you can do sort of the collection by collection, filtering. And, yeah. You you get to choose role by role, so make it admin only or make it make it, a different, you know. So the users can only see the granular errors for a a collection that's already public or something like that.\u003C\u002Fp>\u003Cp>Speaker 3: The the only problem there, it's for the flows. But, like, what do we do with them?\u003C\u002Fp>\u003Cp>Speaker 0: Mhmm. Mhmm.\u003C\u002Fp>\u003Cp>Speaker 3: For flow, that's\u003C\u002Fp>\u003Cp>Speaker 0: a grant. Like\u003C\u002Fp>\u003Cp>Speaker 3: Because I I think we could have\u003C\u002Fp>\u003Cp>Speaker 0: close the\u003C\u002Fp>\u003Cp>Speaker 3: The same system, like, in the in the settings of the flows. But is it gonna be the same way?\u003C\u002Fp>\u003Cp>Speaker 1: But we could also say that if you trigger a flow, you are a user or the public role. So therefore, the same settings of your role should still kick in.\u003C\u002Fp>\u003Cp>Speaker 3: Yeah. Yeah. Because they access the data. So that's good point. But, like, what if you want to, bypass a permission on a specific flow?\u003C\u002Fp>\u003Cp>Like, then you'll because in some case, may maybe it's the the old data you don't want to access, but just one field you want to show someone?\u003C\u002Fp>\u003Cp>Speaker 2: As long as the filtering's happening on the API level, that's still all API under you know, flows is still API under the hood. So if you uplift an operation, read CRUD, whatever, this the erroring although, I guess, it's still gonna then it would percolate to that user\u003C\u002Fp>\u003Cp>Speaker 1: type I think the the the underlying question is what if you want that error to be returned no matter what the settings are in your flow? Because your flow is effectively a custom endpoint or, you know, hook or whatever.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. That's what I think flows when you're throwing an error inside flows, that should be its own code. And if you throw a custom message back to that, then you should be able to get that custom message. That's the key thing that I always want. Right?\u003C\u002Fp>\u003Cp>Or that it the the initially, I thought was, oh, I'm gonna throw an error here and I wanna I wanna send back to the user, you know, you did x y z wrong, you know, or this this isn't allowed because you failed to give me the appropriate information instead of a internal server error, which means nothing. You know, I can't I can't tell the user what they've done wrong or what they're violating that I'm validating inside of my flow. I can't say, mister user, you must have your status in the right thing or, you know, these three fields have to be populated to be able to use this or\u003C\u002Fp>\u003Cp>Speaker 1: And and that's where the allow list approach for error filtering gets tricky again, because now you need to make sure that you allow list your custom errors from your flow in the policy or in the role, so it makes its way to the app.\u003C\u002Fp>\u003Cp>Speaker 2: Or I think, again, flows the error code or codes that are returned by flows, that there is a specific code, and you simply take the message that the user's providing out of the out of the, you know, exception. Yeah.\u003C\u002Fp>\u003Cp>Speaker 3: Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Right. And so it's it's a known code. We know what it is. Right?\u003C\u002Fp>\u003Cp>We know\u003C\u002Fp>\u003Cp>Speaker 1: Oh, so here's here's another thing we haven't really message. Yeah. Another thing we haven't really touched on in with regards to flows is that, you know, you have a very explicit error handling path that every operation has a okay. If the error fill or if the operation fails, do something else. So maybe there should just be an explicit operation that says return the error, and otherwise it just defaults to, you know, hidden by default, but then it becomes it it no longer is a a permissions or roles thing.\u003C\u002Fp>\u003Cp>It just becomes a flow thing, where you just very exclusively have to end your flow with return the actual error. And at that point, it's up to the, you know, the admin to configure that flow and and whatever they want to return.\u003C\u002Fp>\u003Cp>Speaker 2: Love it.\u003C\u002Fp>\u003Cp>Speaker 3: Me too. Convinced. But\u003C\u002Fp>\u003Cp>Speaker 1: yeah. Yeah. Okay. Problem solved.\u003C\u002Fp>\u003Cp>Speaker 3: We would have we would have to to, like, also have a a custom error block, I think, if you if we do that that way.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. Just an operation that says make a new error, whatever that error is.\u003C\u002Fp>\u003Cp>Speaker 3: Yeah. And you can pass the a variable or a text or something. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Which is then a funny new completely unrelated question. But if you have a block that says create an error, is the good path the error or is the bad path the error?\u003C\u002Fp>\u003Cp>Speaker 3: I I think you can you can do this.\u003C\u002Fp>\u003Cp>Speaker 2: There is no path. You don't get to you don't get to leave. That's the end. You're finished. Termination note.\u003C\u002Fp>\u003Cp>Speaker 3: I think the I don't I don't know. Like, I think there you called it, like, grade pass and, error pass, but I think in so in some time, like, on the if call, trigger, the error pass is not really an error pass. So\u003C\u002Fp>\u003Cp>Speaker 1: Right. Yeah.\u003C\u002Fp>\u003Cp>Speaker 3: That would be sense to be both, really.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So in the create an error operation, both both are the same. It just calls both.\u003C\u002Fp>\u003Cp>Speaker 3: Yeah. And then\u003C\u002Fp>\u003Cp>Speaker 1: Now you have oh, woah. Woah. But if it calls both, now you have a way to do parallel split paths because it's now\u003C\u002Fp>\u003Cp>Speaker 0: a trick.\u003C\u002Fp>\u003Cp>Speaker 1: That's a discussion\u003C\u002Fp>\u003Cp>Speaker 2: for the problem.\u003C\u002Fp>\u003Cp>Speaker 3: I I think that would be nice, but yeah. That would be a lot for that,\u003C\u002Fp>\u003Cp>Speaker 2: more importantly, I want merge capability. Be able to rejoin back into logic.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, good lord. Now you have to wait for both to be done and oh, boy. Okay. Let's definitely not get into this.\u003C\u002Fp>\u003Cp>Speaker 2: We've got we've got very divergent. We have about 9 minutes left, folks.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. This this feels like another 6 hour discussion if we go that route.\u003C\u002Fp>\u003Cp>Speaker 2: It is a very, very long discussion. Flows 2.0. We're already doing some research on that, so figure out what that looks like long term. Okay. Back to API errors.\u003C\u002Fp>\u003Cp>Do we have an idea of what our must have\u003C\u002Fp>\u003Cp>Speaker 3: should have?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think my my current going direction is is basically the on the role level, there is an allow list type configuration that allows you to say there needs to be a wild card that says do everything for def purposes and otherwise you can use the sort of filter structure that we have to say these are the error types that we allow you to see, and these are the and then based on whatever the error contains, you can filter it more granularly if you want to. That's for me closer to a should have than a must have. The error types is really the main the heart must have MVP. Because then because our error objects, they are, you know, they have additional extensions is what I called them right now, which is basically, a not found error can have a collection flag that says this is the collection that was not found.\u003C\u002Fp>\u003Cp>And therefore, you can use those flags inside of the filter as well to just make it real granular. And then for for flows specific specifically, just to reiterate what we just said, is you wanna have some sort of operation that it it's almost like the end of the line operation that should that sort of allows you to say, okay. This is what we're returning now, in the output. And then that could be that can have a couple of settings like, what is the status? What is the, what is the message?\u003C\u002Fp>\u003Cp>What's the body? And then that body can be generated using a, sort of create error operation. So I think that would be a nice setup if we have 2 additional operations. 1 to create an error that this custom, but uses the direct as error format and everything else to make it system standard. Therefore, you can use it in things like manual triggers to output an error to the client.\u003C\u002Fp>\u003Cp>And then we need to have some sort of operation that you can use at the end of your chain to say return the data and then just format it in this in a certain way, to really allow you a lot of control about the request.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Rest of energetic typing.\u003C\u002Fp>\u003Cp>Speaker 3: This is satisfying.\u003C\u002Fp>\u003Cp>Speaker 1: It was very nice to listen to.\u003C\u002Fp>\u003Cp>Speaker 3: ASMR.\u003C\u002Fp>\u003Cp>Speaker 0: Now we switch to the ASMR section of the Can you just show\u003C\u002Fp>\u003Cp>Speaker 1: this video?\u003C\u002Fp>\u003Cp>Speaker 0: Everybody quiet. Everybody quiet.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, no. Oh, no.\u003C\u002Fp>\u003Cp>Speaker 0: Let's not do this. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. This this feels pretty good so far. Did we we I know we we chatted a little bit before we hopped on this session. Did we have any other questions that we prepared for this? I think I think we have for most of it.\u003C\u002Fp>\u003Cp>Let me pull it off here on the second screen that is off camera. Why are we returning for threes? Went through that. How many lovelies? We kinda figured that one out.\u003C\u002Fp>\u003Cp>It could be an environment variable, but you kinda just just figure that out. And then business logic existing scripts expected specific error code. Yeah. I mean yeah. I mean, I think we're gonna we've answered all the points that we sort of note note beforehand.\u003C\u002Fp>\u003Cp>Speaker 3: I I just have and I I know I think someone in the chat is just saying, like, for what about the extensions? But I think for the extensions, we can just have, like, a a JavaScript function with with the same as the one in flows and just like custom chats.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Because we do we we haven't really documented it too properly yet because it's sort of it was a little bit in flux still, but we made that direct as error specage with a function that you can use to make a new sort of, like, error a direct as error that direct as then like, what's the right word? Recognizes as a direct as error that you can then throw to make sure that no d ep and API behave in the same way as the system would so for for custom extensions that should sort of be solved, which is good. Put that direction. Yeah.\u003C\u002Fp>\u003Cp>With that being said, I think we have a for the first time ever in one of these sessions, we left it off with a pretty clear idea of what we actually wanna do. It's usually, we go way too deep and then have a sort of couple ideas. But\u003C\u002Fp>\u003Cp>Speaker 0: But it's we're trying. This is my\u003C\u002Fp>\u003Cp>Speaker 1: No. No.\u003C\u002Fp>\u003Cp>Speaker 3: I I was going to to say it's just because it's a refurbish version of what we already think of later.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Well, you'd you'd think that about every time we do a feature request review, and it's it's never never the case.\u003C\u002Fp>\u003Cp>Speaker 3: Like, a 2 years old idea or something. Okay. Maybe Yeah. Not 2 years, but, oh, yeah. It must be.\u003C\u002Fp>\u003Cp>Like, I I see 2022. So yeah. Kinda.\u003C\u002Fp>\u003Cp>Speaker 1: How passionate people got in the actual feature request that somebody said. It's like, this is the single worst thing about I hate it so much. My whole month is ruined because Directus' errors are too strict.\u003C\u002Fp>\u003Cp>Speaker 3: No. But, sir, seriously, like\u003C\u002Fp>\u003Cp>Speaker 0: I wish. I wish. Yeah. I wish director's errors were the worst thing in directors. Then directors would be very nice.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's actually a good point. Yeah. Ouch. If this is if this is the worst problem to talk about, then we're doing real well.\u003C\u002Fp>\u003Cp>Speaker 0: Then we're really golden. We're good and then.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. Well, we're we're slipping a little bit, but let's call it for now. I wanna say as per use, thanks everybody for watching. This will be going live on direct TV, which is direct. Io\u002Ftv.\u003C\u002Fp>\u003Cp>If you haven't seen it yet, book market sends to your friends, your family, everybody else, Share it on LinkedIn, Twitter, x, whatever you whatever your flavor of the week is. We'll be back in, I think, 2 weeks. Am I saying that right? Uh-oh. Should have prepped this.\u003C\u002Fp>\u003Cp>Yes. We'll be back in 2 weeks exact, June 6th with another one. Oh, hello. Another one of these party full sessions. But until then, thanks for watching, and we'll see you next time.\u003C\u002Fp>\u003Cp>Speaker 2: Happy coding.\u003C\u002Fp>\u003Cp>Speaker 1: Bye. Like and subscribe. Save me from the algorithm.\u003C\u002Fp>","Thank you for joining for this week's, session of request review. We have a fascinating topic for you, which is a little deeper than you think from the from the title. But thanks for joining. We hope you like it. This week's topic is about the strictness of API errors. So what does that even mean? Let's kick off with that. Everybody knows software doesn't work always quite how you want it to work, so sometimes you will receive errors in your application, and Directus is no exception. So as you might have noticed, we are very strict as of right now. So if you do something wrong, chances are high that you're just gonna see the message forbidden. Like, even when the message, even when the error isn't actually that type of error. So why do we even do that? Throw throwing the, throwing the conversation ball to mister Reich over there, please. To me? Oh, man. Why do we do that? Well, it stems from a security report back in those days in that if you, let's let's use let's use a, a a 4 zero four as an example because it's it's an easy one to think about. So let's say you're fetching a collection that doesn't exist. Right? So you should get a 404 because the thing doesn't exist, so there's no route for it. No endpoint. If we would return a 404 for those, you could theoretically extract what the data model looks like by brute forcing your way through the whole API and checking which things return a 404 and which things return a 403, which, you know, to some security minded folks or setups, that's an issue because you don't wanna expose what type of data that you're managing in this system for security reasons. Now this is immediately where we're kicking off with opinions. Let's go. Because you can definitely, you know, you you can see it from both sides. You can easily say, well, that just makes the developer experience very annoying. And that's also why I created this feature request the moment I built it in this way. But it makes sense from a security perspective. Right? It's a similar reason why, during login, it'll say wrong email password or account doesn't exist, but you never exposed which of the 2 it is just to make sure that people can't slate, you know, what users exist. And the same goes for, you know, individual items in records. It's like you wanna, you wanna make sure that you can't just say, okay, slash item slash xyz and just keep scraping all of the different numbers, and now you know exactly what records exist, etcetera, etcetera. Somebody rightfully called out, you know, nobody does that in this trustworthy world, which I think sort of concludes this discussion then. Just ignore it. Lying on the Internet? What? Who does that? No. What? No. No. No. We don't do that. And a perfect example. So leaking of information is a very, very big thing. You don't want to expose stuff that you don't have to expose, but as you just said, it would be nice, for example, like the most basic, setup when you're just developing locally, it would be nice if you could actually see what went wrong. Because currently, it's just okay, forbidden. Nope. No information for you. So then the question comes. Oh, La Prima asked, can we go on stage? Sure thing. I think so. But let's let's first, summarize a little bit. So There should be a button somewhere. The question is. Yeah. Yeah. Exactly. So, immediately the question comes to mind. Okay. So if you would have multiple errors in that case, a strict one which just says forbidden and another one that has more information, Okay. How many different errors would you even like to have? So are we happy with just 2? Because, I guess this is opinion alert or something, or maybe even a useful thing for many other people. Like, what what do you guys think about this? Like, how many error, different error, messages would be nice, would be good. Useful. I mean, that is very much a sliding scale, isn't it? Because you really go from, from like, just give me every single thing. It's like, oh, I was trying to read the articles collection that I don't have access to. So I'm gonna get a forbidden error that says, well, articles exist, but you can't read it. And that's because of this permission that you don't have. For development purposes, that's really nice. But if you go to production with that, it's tricky because now you're just exposing a bunch of information about, you know, your setup. So, yeah, I'd I'd say it it there's a large range there's a large range in in that. And even within that forbidden error, you know, we can, think about how we wanna return stuff. So one thing I've been working on recently as part of this the the rules and permissions sprint that we'll we'll ship in soon is the, is that forbidden errors now include a little bit more information as in, you know, the field x that you were trying to query, you know, you don't have access to it or it doesn't exist, but at least it's a little bit more explicit while not necessarily exposing the schema, but just exposing what part of your query is wrong. Right? Which is not leaking any of that information but still being a little bit more useful. Alright. That does sound exciting, to be honest. Like, it's such a developer thing, like, normal users. I don't know if how how many people care about this, but this is such a nice thing for developers. So I gather from your answer right now maybe 3 types of messages. At least from your answer, it sounded like 2 in the beginning, like, one strict one with a little bit of information, but that could be counted as 3, right? Like the very strict one with nothing, a strict one with a little bit, and another one with very much information. Are we happy with 3? No? Yes. Asterisk. There's one more tricky thing in that the output type signature of the errors themselves change between those modes Right? So for example, if you're trying to fetch a thing that doesn't exist, we want to return a 404 status code with a not found error message. If you then program your app around that, you know, that it on your front end, it shows, you know, something because it checks 4 Oh, it was a 404 status. Therefore, do this logic. Right? The moment you now change that option to be strict when you go to production, that messes up again. Because now it's returning a 403 with a forbidden instead of a 404 with a not found. Cricky. So Let's see. May maybe maybe after we dive in a little bit deeper, we have more clarity around this. But so far, like, I personally like would like, I think, 3 modes. Let let's call them levels. Do we call them levels, modes? I think levels. Right? Yeah. It depends. As per usual, it's great. It it depends on, you know, are we considering multiple different flavors at the same time, or is it just an on and off? Because somebody in the chat just now rightfully mentioned, maybe it's just a single production versus development flag that changes that. Right? It could also be a setting within the app. Maybe it's even a setting on a per collection basis that you wanna say, well, this collection is not as, you know, private as the other ones. So for this one I wanna have, you know, nice and friendly, messages. And for the rest, we wanna keep private. There's options. It's tricky. It's it's when it comes to a single sort of Boolean flag that says production yes or no, I would be a little bit worried about getting into a point where undoubtedly 3 months later, somebody will show up and be like, well, we kinda need something in between. You know? Exactly. You can't really Or more importantly, somebody develops in development mode thinking they've got those messages, then they move that code to production, and suddenly nothing works. Right. Which is gonna be a bit of an issue no matter what because Agreed. We're not talking about, you know, changing the output errors. You could consider those, part of, the the what what would you call it? The public API contract of the APIs that you're building against. Right? Because because, you know, in your intro then, you said stuff sometimes crashes, and that's just part of part of software life. Stuff crashes on purpose. Right? In this case, like when validation fails, it's we're still throwing an error, but it's not an unexpected error or anything else. It's, you know, we stop you from doing something on purpose, so we throw an error, which is, you know, a bit of a difference there as well, of course. Because when it comes to exposing internal server errors, it's a whole different question. Because right now we do a similar thing, right, where we say you just get internal server error. That's it. Like regular users don't see anything else. If you're an admin user though, that's today, that's the difference. It'll show, you know, the stack trace of the actual JavaScript error, with where in the source codes it it it happened. So but exposing internal server errors to non authenticated users, that's gonna be that could easily become an issue. Right? Because now you find some sort of vulnerability path or whatever that's you don't wanna expose that. Definitely. From the chat, somebody has a suggestion here. It says, what about setting a trusted IP address? So you can just say, okay. When I am the one from my home to do this, I get all the pretty errors and otherwise, I can't. I think you're gonna have a similar issue with, you know, def versus prod, strict versus friendly error types, and how do you handle those in your app. It's gonna be a tricky tricky thing to figure out. And then somebody else said, the only thing where that really isn't enough is when there's a delete constraint. Users trying to delete items, and they can be deleted because they're still a referencing item. Admins already get a little oh, yeah. That's yeah. Because right now that is technically, a database error that bubbles up. So it comes out as an internal server error that for admins, it'll show the the SQL error underneath. But for regular users, they just get a whoops, you know, internal server error. Also with the IP, since currently currently, IP access, is attributed to roles. Right? You you customize that on a role with that access. So we could use that, but it does, like, sound very, very, very likely to me that you wouldn't be interested in, like, giving this to every single user of that role. You would like to have something more granular than that. So how about this also comes from, someone from the community. How about we attach this to permissions themselves? So let's say this user can access this and that, and that user is authorized to receive better errors than other users. For example, like, the simplest example, like admin users. Right? Mhmm. Like, all admin users are allowed to get the best errors that you can, But users other users get strict errors. Okay? But that, yeah. That's that, like, I I I'm not really sure how exactly complicated that would be to do at every single point in the in the code base. Well, I mean, in in terms of implementation, how like, they currently were throwing forbidden errors all over the place, but the way this would work in implementation is right at the end where we return the error in the APIs, that's where the sort of translation layer kicks in. Right? So all over the code base, it should just throw the actual errors or the actual error types and the correct ones. So if you're trying to hit something that doesn't exist, it should throw a not found error. And then all the way at the end in the middleware stack in the error response handler, it should say, oh, if your current, you know, e n v setting for errors or if the, which I'm gonna call it, if the the error permissions or whatever you wanna call it are set to a certain state, then it translates them into a different way. I think that's the way that that will be implemented realistically speaking. Yeah. That does sound quite good. Adjust intercept as a middleware. Okay. You are allowed to see this or not. Okay. Yeah. Because we're doing that on a case by case. It's gonna be insane. Just trying to do it every single time that we throw an error. That's that's too much. We currently already do a similar thing, right, where the the error handling stack, we check was this a direct as error type or any other error and that chooses between internal server error or, sort of the the expected error output. Very reasonable. I think, if somebody from the chat also has another recommendation or anything, please let us know. There's quite a quite a bit of action today, which is pretty nice, actually. Like, seeing the chat scroll up and down is pretty nice. Somebody says, I can't explain it in the chat, but I'll try. That's lovely. Well, well, La Prema can if if you you guys are okay with it, La Prema can join us on the stage. Yeah. I'm down. There should be a button somewhere that just says, I can invite them or they can add they can request, but we can also do an invite. How is that often with these? They asked to speak earlier, so I'm inviting. Or have they does Kevin have that blocked in this event? Oh, boy. With this guy. Nope. Nope. There we go. I got the invite. I got Does it work? I got him up here. I'm trying to see if I wait. Weekend. Yeah. It works. Okay. It works. Hello. Welcome. Hello. As always, sorry for my accent. I'm I'm Frenchy. But, like, I Don't worry. You're you're chatting with a Dutch and a German, so we we know you need to know the guy. Oh, yeah. But your English is better than mine. I don't know if you remember, but we used to talk to Get A Rich for about the the error management system. And at that time, I was working for a company who uses directors, but I leave them. So after, what I haven't the time to to follow the the the case of the the server management system. But, I think the the main problem, we had is is, dealing with the the the, yeah, the, like, the granular system of a thing. And what I proposed at the that time was, like, adding in the the collection a a new, column, like, you already have, edit and all that stuff. And, in permission, column where you will have, like, 1 or 2 level no. Mostly 2, but 2 or 3 level of, strictness of the error. Like, do do do I wanna send them all on that collection or not? Because, yeah, I I think that would be the I'm sorry. But because it's user related, it would be better for the companies. Because, if you if you, like, have only one private, instance of directors, I think it's not a problem to throw error to people because they are part of the company. So if they don't, respect them, their permissions, you they can have, like, legal, troubles, and this is possible. But if you have, like, an public API and also a private one, On one instances, if it's just a mode, I think it won't fit because a lot of data can be really sensitive. And, same goes with the developer mode. Yeah. I I totally agree with, like, developing something, in a in a development mode and then going to production, it doesn't work. That would not be a solution for me. And, as well that the the this this production and development mode would be probably used somewhere else, at another time. And, in a long term view, that will just lean in people active in the development, mode every time and, development searching, be thrown everywhere. Yeah. That's right. It's such a tricky, and I I think if that, what was that nodes flag called? It node e and v or something as taught as anything is that you should not rely on a single dev and prod flag. Yeah. No. It's a good one. It's the the doing it on a collection level, on a permissions level for for collections, basically. It's it's a good angle. It's it raises some different interesting questions though as per usual. Everything does, Especially around requests that touch multiple collections at the same time, right? Where in REST, you can technically go, you know, nest it and then you end up with you could have an error that's thrown in the sort of nested layer of the same request. Or in a GraphQL request, you can technically request multiple collections at the root level at the same time. And then it gets a little interesting because now you can get, you know, a different error output from that request based on which of the data points of that request crashed, I guess, instead of, you know, the request itself, which opens a little bit of a different kind of words. Yeah. But I I think in some case, that's what we want. Because, in the the app I I was working on, we we used to to to get in that kind of problem where, we have people, who had access to certain company, but not certain order. And we retrieve that data in one block. And if some points fails, the the whole, request will fail, so we'll have to deal with that. And, what what we can do at that point, I guess, it's when you you have the error only in one part of the query, it's just returning not an error not an error code, but just like in the API, an error, tag in, that object and not all the thing. But I I know that would be, like, a big breaking change. So it can be hard to implement for some people. But, I guess, by default, if we just disable all the error, like, the the basic settings will just be, what Direct just currently does, it won't be a problem because the people who turn the the feature on, would know what they do or or would have ways to find out. I think that is You're you're just assuming that people know what they do. I barely know what I'm doing. And when when I add features oh, no. I think, it it there's a there's a new question hidden in there that we hadn't touched on yet, nor in our notes right now, which is if an error is thrown somewhere nested, let's say, you know, the let's use the GraphQL as an example because it's it's easy to reason about. Let's let's say you're requesting 5 collections of data at the same time. Right? And one of the 5 crashes. The question is, do you return an error and the whole thing failed, or do you return the data that worked and then have an additional error flag that just indicates this is the path of your query that didn't work? But return what you have and leave the rest. And, I think for that opinions. Yeah. I think for that, the the the best way to to do it is just, like, throwing the error only for what phase. Because, if we use these directives, I assume it's that we just want to cut the the front end. And and so, it it would be, most of the time, like, an interactive app or a single page application on or something of of that kind. And those kind of errors are really easy to to treat with that method. So and and, I I think most of the time, if we activate the the feature, it's what we want because, that will lead in, less, just big crashes, like internal server error proof and nothing worse. So I don't know. It's it's an interesting one because, somebody in Chet just now says it too. It's a rather fundamental question. But, you know, you see both in the wild, and both are kinda alright. It's it's, there's also an implicit difference between, you know, reading and write operations there as well, of course, that if you do a large nested update, you kinda want it to work as a single unit, as a single transaction to make sure that if, you know, part of the insertion doesn't work or part of the update, the whole thing shouldn't, you know, go through because otherwise you have that sort of half half done state. But for read queries, it could be different, but that really depends on, you know, what are you reading, and is it all tightly coupled, or are you reading multiple things at the same time for convenience sake? Because if I'm not mistaken, in in sort of the GraphQL spec, Like, it allows you to return partial data and then errors for other root fields. But I feel like if there's an error in a nested field in the query, it'll still fill that one sort of root query as a whole. But that is that is something I love to double check. Yeah. Yeah. I'm I'm I'm not sure either. Like, my my gut feeling was, no. I I I don't wanna say something wrong right now. So, okay. Another another suggestion from the chat. How about hashing the error plus a UUID, I assume that is? Only admins will be able to see what the error really is. Well, I mean, I mean, you are probably talking about, like, error codes. Like, for example, many different other apps just say, okay this is error 3, 1,172 or something. And then you can look up internally what that exactly means, But, you would still be missing out on, like, more information, like the stack trace or which which file failed or something something that you would have to encode in that thing, but I don't think that's always possible or even Or to just think very divergently. You know, an alternative approach is that you basically encrypt the whole error, save that to a database, and then only return the ID of the error that it was. So then the admin can log in and look up what the full stack was and and and and sort of decrypt it and see the whole the whole contents and everything else. But that that feels like a way to sort of persist to security and give more information at the same time. But it also feels like another skip and a hop and a skip for regular users to be able to just, you know, use do the errors that are output. That's an interesting thing. I have never thought about it like that. People do that? Do their apps do that? That's interesting. Yes. Some app does. I I used to work on one with the that. But I think it's the it's a problem because, like, most of the time, you you wanna informing in some way your user of what the error is because you don't want to to get someone just to to create a ticket to the IT support just every time they need, something, didn't work. But they just didn't have the permission in the first time. So, yeah, I don't think, it's a global sit solution. But maybe in some case in some case, if if we add, like, an EMV approach, that would be a a great option for some people that maybe need that. And I could see it work like that for for internal server errors. Right? Well, that's one of those things where we wanna hide away the stack trace no matter what for for the API output and then save it elsewhere. But at the same time, you know, saving every error could easily blow up in your face too, because if you just have, you know, an unauthenticated user that tries to access some data there's no permissions for, we don't wanna save those. Right? Because now you could easily just blow up a database by a public malicious actor that just spams your API knowing that they're gonna get errors. So there is there is definitely a a difference between sort of expected and unexpected errors there and which ones are saved. Have AI blocked them. AI solves all of our problems. Sure. Just do what I want. Parentheses open, parenthesis close. That should do it. Right? If only if only it would be that easy. K. So let's let's summarize a little bit because we have been, you know, throwing stuff out there. Maybe we should get back to reality a little bit. So, okay. So so let's say we have, we like to use the Moscow list. Right? The must have, should have, could have, type of stuff. So for must haves, let's let's get on the same train. Like like, what do we need? What what must we include? So, yeah. How granular do we make this? Oh, no. It has to be actionable, man. It has to be actionable. Okay. I I think the the point where we've been stuck the the last time was reached is, like, we we both agree on the fact we need a granular system, but we both must agree, on the fact that it would be so long to to make and to think and all that stuff that maybe it would be nice to have something, before that. But, Right. Yeah. Yeah. Yeah. But, I I think, that's, my my point was, like, directors, is a tool who is proposing things like a a different way than the other, tools in the same category. Like, you you just want it to to be a little more, accessible and, and usable. And not just, like, being that's the way you should do that and, just follow it. And and so I I think that's the problem there. If we did something else, than a granular system, you would, follow a path that doesn't feels like directors to me. And I don't know that that's totally your point, but but, I I'm not sure if that's what people need. Otherwise, they they will lose WordPress or or something like that. You you you sound like a team member in the sense of when the choice arises, let's do it the difficult way and make it unopinionated wherever we can. So no. I I do agree though. I do agree because it's it it is at the end of the day, this is a very opinionated thing, where for some apps, it's acceptable to have it a little looser or even want it. For some, you know, security minded folks, it's absolutely not, and we can't really make that decision for people at that global level, even though we have right now. Oops. Right now we are on the side of safety first, which, you know, it's it's defendable, but not ideal. Like, that's also why I created that feature request. It feels like decades ago, but it was 3 years. I think that the the main unresolved question now, and and that is sort of to answer your point, Dan, before we can really fill in the blanks for what are the must haves, is what is that ideal approach to how do you configure this? And then from from a developer experience first and then secondly does that make sense from an implementation perspective. Because doing it on the collection scope level makes sense for errors that are thrown on the collection scope, but not all errors are thrown within the context of a single collection. So that that that opens up, you know, new questions. And the opposite is true for doing it globally on the e and v level. Right? Because, you know, it's a low pretty much excellent point. It's not that black on white that you say make it insecure for the whole API. You know, you wanna do it on a case by case basis. That is a that's a that's just a statement with no no answer. No. I don't another, I don't know what to say it in English, but, like, another thing we, think about at time was, like, just, if if we take, like, the the point, if we need the error, it's just to treat them. One, thing, which, was a a little bit, between the two option is just like, having some kind of way to tell directors just, okay. This error, I want you to to throw it, and other errors I don't want. So that would be a little bit more granular, but that would be more like, just a techy approach. And and then not, being, so universal. Oh, Rike, you're muted, by the way. I hit the wrong mute button. That's what you get when you have 2. I I muted myself here, and then I and then I muted myself to unmute on this. Oh, man. I mean, we've only been doing this remote thing for about 5 years now, so I'm I'll I'll get it. Yeah. Because I what what I was trying to say is that that approach to say, okay, which error types do we expose? That is something we could do on the role level or soon to be policy level, And it is a system where we can still have one translation middleware that handles that error no matter where is thrown from, which is you if you compare that to the sort of collection idea that we had earlier, that one is a little trickier to implement in that sense, because now you have to make sure that you know what the collection context is for every single function call that may or may not throw an error. And then do that sort of translation piece, in in situ of where those errors are created, That makes sense. Whereas with, you know, an approach where you say, okay, not found errors can be exposed. If that's your setting, we can do that on one middleware at the end of the stack to just say, okay. Was the error Again, I'm not sure if if I can wrap my head around every single edge case, but, like, it does sound and may maybe I understood it a little incorrectly, but it does sound quite easy to mess that up. Like, it to to to bubble to bubble something up that does not get blocked then. And somehow I am exposing a random error that I have not thought about, like, on on an error level. It's it's the difference of an allow list versus a deny list is basically what you're saying. Right? So if you have a filter where you're denying individual pieces, it's easy to miss something. Whereas we do the opposite way where it's blocked by default and you allow more and more stuff, it's you can't really accidentally expose anything because nothing is exposed by default. Which is similar to what, Andre Andre Andre Key are. Just gonna assume that's how you pronounce it. Just said in the chat as well as, you know, if you really want it super granular, effectively just make it a rule based system, right. Where you run a filter against the error object that is thrown. Like if the error types is XYZ and the extensions contain blah, blah, blah, collections, such and such, then expose it, which will be one way to make that super granular, but also a little harder to configure at that point. Because I could, I mean, at the end of the day, I could also imagine that, you know, for just local development, that is gonna be a pain in the ass because now you have to configure so much stuff to get the errors out. I I think we what we could do is just like doing an EMV, variable just for real development case, then that system for the production mode. And later on, if we think it's too difficult or, then we can add the system, with the the permissions because that would make, like, stacks on top of each each other, with the the the the first step would be, like, the programmatic approach on the error throwing and all. And then, the the we could add later on just, instead of just throwing nothing if the error do not doesn't match, any case. Like, is it a permission error? Then is there a plain way to treat it? So it's made steps, and we have something, in between no permission management and on the other end, full thing. What this makes me think of is the we've got a client that we're playing around within a proof of concept. They're they're actually have an error table for their front end, and they've actually got translations. So they've actually got a translations table on that so that they can have the language the errors translated to multiple languages. If we went something like that where we have, like, a direct us errors collection, right, as part of the system tables with a set of, you know, with translation, then we have native translations capabilities right there. So if you wanna adjust the error message, so we could have the defaults. Right? The I don't know. We've got a dozen or whatever that we have now, with some variable support maybe even there so that if you wanted collection information or you want that stack trace information to percolate up, you could. And you could determine at least in the short term, we can we can avoid the permissions for the short term, give you just control over your error messages. You wanna show something that we don't do natively by default, well, you can override the messaging as a thought. And I think that would actually help with things like flows as well. We have the same problem in flows. We can't percolate an error message to the user even though we're throwing and controlling the erroring of the of the workflow. Potentially, if we think about it in that context as well where we've got we could use that same error structure Mhmm. Idea. Not a not necessarily the solution or the right solution, but here's the new rabbit hole, multilingual error output for the API. Oh, boy. Oh, boy. I mean, sort of it's an interesting angle to to sort of noodle on a little bit. The the general idea of errors as it stands right now is that you basically return an error code that is one of the standard sort of known error types. And then in whatever app that you're building, you can basically just say, you know, oh, if the error is not found, then use my own translations and my own front end and all that kind of stuff. But yeah. I mean, it it does raise I mean, we we know we need some sort of translations in the API no matter what for email templating, which has been that'll be a fun discussion for another day. But, yeah, it's it's an interesting idea to have errors as a sort of system collection that you can then modify. Although, we would have to hard code in what the keys are because otherwise, we don't know what error to throw still. That's a tricky one. But we can hard set the keys. Right? And then if you create your own key or we lock the table, you're not allowed to add to it, you know, without core functionality. Or, again, you've got your hard set core pieces. Core knows what they are, and they can't be changed. Their primary key or whatever we wanna do around that Yeah. Gives us that level of flexibility. But then if users want to code other error messages for other applications and data, they can actually still maintain it in all in one place. I'm not smart enough to solve it. I just I just know that's an implementation I saw recently where we're for a front end application and the error messages that they wanna generate for that front end application. They're actually doing that in a set of tables. Kinda cool. Oh, I think, like, lots of interesting things that we can do, but I think we're still stuck on where do we want to control this or configure this. Let's just go to the one extreme. I mean, very That would be the easiest thing. You have to And you get errors. Okay. Done. That's the easiest thing that we can do. That leaves a couple of things that we would like to have open. Right? Like, we ideally, or at least in my mind, it would be really useful if, for example, like, admins actually get no like, good, context rich errors and normal users don't, which would you know, it's not really that possible with that environment variable approach. Yeah. So so it feels to me like out of everything that we've discussed so far, the direction where you effectively set up and allow list of errors on the role or soon policy level gives you, so far everything that we sort of want to achieve. Right? Where you can say, okay, you can allow one admin role to just see all of the errors with some sort of wild card flick, accept everything, allow everything. You could use the filter rules to be more granular that you could say, oh, if the error contains a collection, flag, then filter against that. So So you can you can do sort of the collection by collection, filtering. And, yeah. You you get to choose role by role, so make it admin only or make it make it, a different, you know. So the users can only see the granular errors for a a collection that's already public or something like that. The the only problem there, it's for the flows. But, like, what do we do with them? Mhmm. Mhmm. For flow, that's a grant. Like Because I I think we could have close the The same system, like, in the in the settings of the flows. But is it gonna be the same way? But we could also say that if you trigger a flow, you are a user or the public role. So therefore, the same settings of your role should still kick in. Yeah. Yeah. Because they access the data. So that's good point. But, like, what if you want to, bypass a permission on a specific flow? Like, then you'll because in some case, may maybe it's the the old data you don't want to access, but just one field you want to show someone? As long as the filtering's happening on the API level, that's still all API under you know, flows is still API under the hood. So if you uplift an operation, read CRUD, whatever, this the erroring although, I guess, it's still gonna then it would percolate to that user type I think the the the underlying question is what if you want that error to be returned no matter what the settings are in your flow? Because your flow is effectively a custom endpoint or, you know, hook or whatever. Yeah. That's what I think flows when you're throwing an error inside flows, that should be its own code. And if you throw a custom message back to that, then you should be able to get that custom message. That's the key thing that I always want. Right? Or that it the the initially, I thought was, oh, I'm gonna throw an error here and I wanna I wanna send back to the user, you know, you did x y z wrong, you know, or this this isn't allowed because you failed to give me the appropriate information instead of a internal server error, which means nothing. You know, I can't I can't tell the user what they've done wrong or what they're violating that I'm validating inside of my flow. I can't say, mister user, you must have your status in the right thing or, you know, these three fields have to be populated to be able to use this or And and that's where the allow list approach for error filtering gets tricky again, because now you need to make sure that you allow list your custom errors from your flow in the policy or in the role, so it makes its way to the app. Or I think, again, flows the error code or codes that are returned by flows, that there is a specific code, and you simply take the message that the user's providing out of the out of the, you know, exception. Yeah. Yeah. Yeah. Right. And so it's it's a known code. We know what it is. Right? We know Oh, so here's here's another thing we haven't really message. Yeah. Another thing we haven't really touched on in with regards to flows is that, you know, you have a very explicit error handling path that every operation has a okay. If the error fill or if the operation fails, do something else. So maybe there should just be an explicit operation that says return the error, and otherwise it just defaults to, you know, hidden by default, but then it becomes it it no longer is a a permissions or roles thing. It just becomes a flow thing, where you just very exclusively have to end your flow with return the actual error. And at that point, it's up to the, you know, the admin to configure that flow and and whatever they want to return. Love it. Me too. Convinced. But yeah. Yeah. Okay. Problem solved. We would have we would have to to, like, also have a a custom error block, I think, if you if we do that that way. Yeah. Yeah. Just an operation that says make a new error, whatever that error is. Yeah. And you can pass the a variable or a text or something. Yeah. Which is then a funny new completely unrelated question. But if you have a block that says create an error, is the good path the error or is the bad path the error? I I think you can you can do this. There is no path. You don't get to you don't get to leave. That's the end. You're finished. Termination note. I think the I don't I don't know. Like, I think there you called it, like, grade pass and, error pass, but I think in so in some time, like, on the if call, trigger, the error pass is not really an error pass. So Right. Yeah. That would be sense to be both, really. Yeah. So in the create an error operation, both both are the same. It just calls both. Yeah. And then Now you have oh, woah. Woah. But if it calls both, now you have a way to do parallel split paths because it's now a trick. That's a discussion for the problem. I I think that would be nice, but yeah. That would be a lot for that, more importantly, I want merge capability. Be able to rejoin back into logic. Oh, good lord. Now you have to wait for both to be done and oh, boy. Okay. Let's definitely not get into this. We've got we've got very divergent. We have about 9 minutes left, folks. Yeah. This this feels like another 6 hour discussion if we go that route. It is a very, very long discussion. Flows 2.0. We're already doing some research on that, so figure out what that looks like long term. Okay. Back to API errors. Do we have an idea of what our must have should have? Yeah. I think my my current going direction is is basically the on the role level, there is an allow list type configuration that allows you to say there needs to be a wild card that says do everything for def purposes and otherwise you can use the sort of filter structure that we have to say these are the error types that we allow you to see, and these are the and then based on whatever the error contains, you can filter it more granularly if you want to. That's for me closer to a should have than a must have. The error types is really the main the heart must have MVP. Because then because our error objects, they are, you know, they have additional extensions is what I called them right now, which is basically, a not found error can have a collection flag that says this is the collection that was not found. And therefore, you can use those flags inside of the filter as well to just make it real granular. And then for for flows specific specifically, just to reiterate what we just said, is you wanna have some sort of operation that it it's almost like the end of the line operation that should that sort of allows you to say, okay. This is what we're returning now, in the output. And then that could be that can have a couple of settings like, what is the status? What is the, what is the message? What's the body? And then that body can be generated using a, sort of create error operation. So I think that would be a nice setup if we have 2 additional operations. 1 to create an error that this custom, but uses the direct as error format and everything else to make it system standard. Therefore, you can use it in things like manual triggers to output an error to the client. And then we need to have some sort of operation that you can use at the end of your chain to say return the data and then just format it in this in a certain way, to really allow you a lot of control about the request. Okay. Rest of energetic typing. This is satisfying. It was very nice to listen to. ASMR. Now we switch to the ASMR section of the Can you just show this video? Everybody quiet. Everybody quiet. Oh, no. Oh, no. Let's not do this. Okay. Alright. This this feels pretty good so far. Did we we I know we we chatted a little bit before we hopped on this session. Did we have any other questions that we prepared for this? I think I think we have for most of it. Let me pull it off here on the second screen that is off camera. Why are we returning for threes? Went through that. How many lovelies? We kinda figured that one out. It could be an environment variable, but you kinda just just figure that out. And then business logic existing scripts expected specific error code. Yeah. I mean yeah. I mean, I think we're gonna we've answered all the points that we sort of note note beforehand. I I just have and I I know I think someone in the chat is just saying, like, for what about the extensions? But I think for the extensions, we can just have, like, a a JavaScript function with with the same as the one in flows and just like custom chats. Yeah. Because we do we we haven't really documented it too properly yet because it's sort of it was a little bit in flux still, but we made that direct as error specage with a function that you can use to make a new sort of, like, error a direct as error that direct as then like, what's the right word? Recognizes as a direct as error that you can then throw to make sure that no d ep and API behave in the same way as the system would so for for custom extensions that should sort of be solved, which is good. Put that direction. Yeah. With that being said, I think we have a for the first time ever in one of these sessions, we left it off with a pretty clear idea of what we actually wanna do. It's usually, we go way too deep and then have a sort of couple ideas. But But it's we're trying. This is my No. No. I I was going to to say it's just because it's a refurbish version of what we already think of later. Yeah. Well, you'd you'd think that about every time we do a feature request review, and it's it's never never the case. Like, a 2 years old idea or something. Okay. Maybe Yeah. Not 2 years, but, oh, yeah. It must be. Like, I I see 2022. So yeah. Kinda. How passionate people got in the actual feature request that somebody said. It's like, this is the single worst thing about I hate it so much. My whole month is ruined because Directus' errors are too strict. No. But, sir, seriously, like I wish. I wish. Yeah. I wish director's errors were the worst thing in directors. Then directors would be very nice. Yeah. That's actually a good point. Yeah. Ouch. If this is if this is the worst problem to talk about, then we're doing real well. Then we're really golden. We're good and then. Alright. Well, we're we're slipping a little bit, but let's call it for now. I wanna say as per use, thanks everybody for watching. This will be going live on direct TV, which is direct. Io\u002Ftv. If you haven't seen it yet, book market sends to your friends, your family, everybody else, Share it on LinkedIn, Twitter, x, whatever you whatever your flavor of the week is. We'll be back in, I think, 2 weeks. Am I saying that right? Uh-oh. Should have prepped this. Yes. We'll be back in 2 weeks exact, June 6th with another one. Oh, hello. Another one of these party full sessions. But until then, thanks for watching, and we'll see you next time. Happy coding. Bye. Like and subscribe. Save me from the algorithm.","ddd81e25-4534-4c87-af7e-f25e6832a128",[2894,2895,2896],"146b3a54-018d-4b79-8190-a52ea1afe20c","1d6a4684-3c50-4af4-bc64-3ee590527d12","e13ded04-2af2-4552-a441-a39c373fe008",[],{"year":978,"number":11,"id":1787,"episodes":2899,"show":2900},[1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1768,1800],{"title":1802,"slug":1803},{"id":2709,"slug":2902,"vimeo_id":2903,"description":2904,"tile":2905,"length":480,"resources":12,"people":12,"episode_number":13,"published":2906,"title":2907,"video_transcript_html":2908,"video_transcript_text":2909,"content":12,"seo":2910,"status":19,"episode_people":2911,"recommendations":2913,"season":2914},"trees-of-london","950736791","The Greater London Authority publishes a dataset of over 815,000 of London's Local Authority Maintained Trees. Kevin shows you how Directus can be used to further explore, understand, and remix this dataset both in the Data Studio and via API.","396bf4b0-153e-4088-9faa-2fb1076230d0","2024-05-29","Trees of London","\u003Cp>Speaker 0: There is so much fantastic open data out there, but often after publishing, very little happens with it. In this show, we give new life to open datasets with the help of directors. So join me as we explore, analyze, and generate APIs to improve access to and democratize data. Now before we get started, if you have or know of an interesting open dataset and want us to include it as part of this series, Just reach out to our team and it may well feature in a future episode. Today's dataset was published by the Greater London Authority as part of the London urban forest plan.\u003C\u002Fp>\u003Cp>It contains 815,000 trees managed by 26 of London's boroughs, the city of London, and transport for London. Now this dataset includes things like where trees are planted, what species they are, how old they are, and more. Now this data didn't need much preprocessing before we imported it into Directus. The only thing I had to do was take the coordinate set and format wise convert that into GeoJSON. Some datasets already have that as a property, you know, when you download them, but this one didn't.\u003C\u002Fp>\u003Cp>So I did that, and in a moment, you'll get to see the benefit of having done that. Other than that, I just created a collection called trees, inside of my director's project, added fields that aligned with each of the column headings and went ahead and imported it. So let's see what the outcome is. So if you've not seen it before, this is the Director's Data Studio. It's a web application that lets you explore, analyze, and interact with data inside of a database.\u003C\u002Fp>\u003Cp>And I have already imported 815,000 trees with that were included as part of this dataset. Now immediately, I can jump in and start searching through it. I can filter it. So maybe I'll say something like only show me maple trees, for example, and you see that this has now been filtered down to a 100000 maple trees in this data set, and you can create more complex filters as well. Once you've created a set of filters that you may want to return to, you can also save them as a preset bookmark, and they will appear over here in the sidebar.\u003C\u002Fp>\u003Cp>And as this data set is added to or changed inside of Directus, of course, those views will automatically update the data that they display. We can step into a single item, of course, as well. So let's look at this tree here. We see its object ID. We see its borrow.\u003C\u002Fp>\u003Cp>We see its tree name as it's called in the database, its age group, and then this location here. Now because I went ahead and formatted the, coordinates as a GeoJSON point, now I can render this map. There are several mapping views inside of Directus. If you have a map box key and set that up, you'll get an enriched map, but this is what ships out of the box. So we see that this is exactly where this maple tree in Hillingdon is located.\u003C\u002Fp>\u003Cp>Now going back out to the explore view for a moment, there's something really cool, about this explore view. And right now, we are looking at this data in a table, very standard view, obviously, for looking at, data. But we can also go ahead and use one of the other built in layouts. These are not the only layouts that are available. You can instal more layouts from the marketplace, but one that's really good and built in is the map.\u003C\u002Fp>\u003Cp>So if I zoom out here, this is the maple trees, inside of the dataset all mapped. So instead of a table view, now we get this view, and I can click straight in and go to the individual tree. So let's give that just a moment, and there are the maple trees again. So that's really cool inside of Directus Explore and the editor. And, of course, if you update data inside of the editor and hit save, it will change the data that is displayed in this view.\u003C\u002Fp>\u003Cp>Once data is in Directus, we can also use the Directus insights module to start to visualize and understand it. So here I've built a little insights dashboard, and the way this works is that we have this variable here that I populated with all of the boroughs. And when one of those boroughs is selected, we get to see how many trees are in that burrow, the breakdown by age. Now the breakdown by age is interesting, because I have applied a filter that will remove any undefined options. Because, obviously, that column's quite large, but it doesn't tell us anything.\u003C\u002Fp>\u003Cp>So we've filtered those out. So these are all of the trees that have an age assigned to them. We have a breakdown by tree type. So we see there's a lot of cherry trees, for example, in Bexley comparative to others, and the top five tree types like so. And this all of these fields here, all of these panels here use the value of this drop down.\u003C\u002Fp>\u003Cp>So in Camden, there's 24,000 trees. This is the breakdown of them. We see a majority of trees are between 31 80 years old. There's quite a lot of plain trees followed by maple trees and cherry trees. On tree types, you know, you could also filter out other, which I suppose would make sense, and we get this breakdown by tree type.\u003C\u002Fp>\u003Cp>Now the each of these panels have their own configuration. You can change the way they look, the color, the filters, whether or not, in this case, there is a legend that's being shown or just labels on hover, and a whole bunch more. This is not all of the panels that are available either. We have things like meters, metrics, line charts, and so on. And once again, using the directors extension system and the directors marketplace, you can go and install more panels and visualize data in other novel and interesting ways.\u003C\u002Fp>\u003Cp>Now as well as the data studio, directors also automatically generates APIs for your data that allow developers and applications to consume that data and perhaps remix it into new applications. There is a very, very granular set of permissions that you can set So you can perhaps allow certain data to be available publicly. Some might require a user to first register and you can enable or disable data that can be seen or interacted with on a per field basis. So I have prepared some API requests here for this specific data set that I think will demonstrate some of the powers of this API. So right here, remembering that our collection was called trees, we automatically have an API end point that will return all trees in the dataset.\u003C\u002Fp>\u003Cp>So we make this request and what is returned is an object, is an array of objects, Each object representing 1 tree and the specific fields which we have allowed to be returned publicly. By default, a 100 items are returned at once. You can edit that, and you can also paginate. So perhaps you only get a 100 or 200 at a time, and then you can build out a pagination system. Now you can also just very much the same way as inside of the data studio, filter the data that is returned.\u003C\u002Fp>\u003Cp>And the example we gave earlier was to filter to just show maple trees, and this is exactly how you would do it using the API. We add a filter and we say the tree name must equal maple. And now we only have those 100000 trees returned, or the first 100, of course. But, you know, you this only returns maple trees across all boroughs. You can do that with any field.\u003C\u002Fp>\u003Cp>So here we say we only want to return items where the borough is equal to Brent, and you can stack these. So you can say, I only wanna return trees that are in Lewisham and there are oak trees. And that hopefully you're starting to see that you can build quite powerful queries and the applications you can build on top of that. There are also API endpoints for creating, for editing, and for deleting data points. So that's also kind of introduces some really interesting potential uses for this dataset where perhaps it could even be maintained or moderated through citizen engagement.\u003C\u002Fp>\u003Cp>Now these last two examples I'm about to show you use different query parameters. So we're not filtering here, but instead, we are aggregating and grouping data to start to analyze it. So here we are saying we want to return every tree, but we wanna group it by the borough that that tree exists in. And this query here returns this result. So we see for every borough, we get to see how many trees are planted.\u003C\u002Fp>\u003Cp>Here's another example where we're grouping by a couple of different, heuristics and you'll get to see how this works. So here we're saying, again, return every tree, just count them. But I wanna group them by borough and by tree name. So now you get to see embarking in Dagenham, there are 219 elder trees. Embarking in Dagenham, there are 695 apple trees and so on.\u003C\u002Fp>\u003Cp>So, of course, there are gonna be way more objects here. It's gonna effectively be a matrix of every tree and every bar that exists in the dataset. But once again, we are effectively analyzing this data now via API. And it's these kind of queries that actually power the panels inside of directors insights. Now there are a whole bunch of query parameters that you can use over in the API here.\u003C\u002Fp>\u003Cp>You can filter, you can search, sort, limit, the number of items that are shown as seen. The default limit is 100. We mentioned that earlier. We also get to see all of the aggregation and grouping functions that are available, to, again, do more analysis on that data, and more. So refer to the documentation, but the APIs are super powerful, and allow developers to effectively remix and use data in their applications and projects.\u003C\u002Fp>\u003Cp>I hope you found this episode of democratizing data interesting. And once again, if you have a dataset or know of a dataset that might be interesting to include in this series, please do reach out to us. We'd love to see it. Until next time. Bye for now.\u003C\u002Fp>","There is so much fantastic open data out there, but often after publishing, very little happens with it. In this show, we give new life to open datasets with the help of directors. So join me as we explore, analyze, and generate APIs to improve access to and democratize data. Now before we get started, if you have or know of an interesting open dataset and want us to include it as part of this series, Just reach out to our team and it may well feature in a future episode. Today's dataset was published by the Greater London Authority as part of the London urban forest plan. It contains 815,000 trees managed by 26 of London's boroughs, the city of London, and transport for London. Now this dataset includes things like where trees are planted, what species they are, how old they are, and more. Now this data didn't need much preprocessing before we imported it into Directus. The only thing I had to do was take the coordinate set and format wise convert that into GeoJSON. Some datasets already have that as a property, you know, when you download them, but this one didn't. So I did that, and in a moment, you'll get to see the benefit of having done that. Other than that, I just created a collection called trees, inside of my director's project, added fields that aligned with each of the column headings and went ahead and imported it. So let's see what the outcome is. So if you've not seen it before, this is the Director's Data Studio. It's a web application that lets you explore, analyze, and interact with data inside of a database. And I have already imported 815,000 trees with that were included as part of this dataset. Now immediately, I can jump in and start searching through it. I can filter it. So maybe I'll say something like only show me maple trees, for example, and you see that this has now been filtered down to a 100000 maple trees in this data set, and you can create more complex filters as well. Once you've created a set of filters that you may want to return to, you can also save them as a preset bookmark, and they will appear over here in the sidebar. And as this data set is added to or changed inside of Directus, of course, those views will automatically update the data that they display. We can step into a single item, of course, as well. So let's look at this tree here. We see its object ID. We see its borrow. We see its tree name as it's called in the database, its age group, and then this location here. Now because I went ahead and formatted the, coordinates as a GeoJSON point, now I can render this map. There are several mapping views inside of Directus. If you have a map box key and set that up, you'll get an enriched map, but this is what ships out of the box. So we see that this is exactly where this maple tree in Hillingdon is located. Now going back out to the explore view for a moment, there's something really cool, about this explore view. And right now, we are looking at this data in a table, very standard view, obviously, for looking at, data. But we can also go ahead and use one of the other built in layouts. These are not the only layouts that are available. You can instal more layouts from the marketplace, but one that's really good and built in is the map. So if I zoom out here, this is the maple trees, inside of the dataset all mapped. So instead of a table view, now we get this view, and I can click straight in and go to the individual tree. So let's give that just a moment, and there are the maple trees again. So that's really cool inside of Directus Explore and the editor. And, of course, if you update data inside of the editor and hit save, it will change the data that is displayed in this view. Once data is in Directus, we can also use the Directus insights module to start to visualize and understand it. So here I've built a little insights dashboard, and the way this works is that we have this variable here that I populated with all of the boroughs. And when one of those boroughs is selected, we get to see how many trees are in that burrow, the breakdown by age. Now the breakdown by age is interesting, because I have applied a filter that will remove any undefined options. Because, obviously, that column's quite large, but it doesn't tell us anything. So we've filtered those out. So these are all of the trees that have an age assigned to them. We have a breakdown by tree type. So we see there's a lot of cherry trees, for example, in Bexley comparative to others, and the top five tree types like so. And this all of these fields here, all of these panels here use the value of this drop down. So in Camden, there's 24,000 trees. This is the breakdown of them. We see a majority of trees are between 31 80 years old. There's quite a lot of plain trees followed by maple trees and cherry trees. On tree types, you know, you could also filter out other, which I suppose would make sense, and we get this breakdown by tree type. Now the each of these panels have their own configuration. You can change the way they look, the color, the filters, whether or not, in this case, there is a legend that's being shown or just labels on hover, and a whole bunch more. This is not all of the panels that are available either. We have things like meters, metrics, line charts, and so on. And once again, using the directors extension system and the directors marketplace, you can go and install more panels and visualize data in other novel and interesting ways. Now as well as the data studio, directors also automatically generates APIs for your data that allow developers and applications to consume that data and perhaps remix it into new applications. There is a very, very granular set of permissions that you can set So you can perhaps allow certain data to be available publicly. Some might require a user to first register and you can enable or disable data that can be seen or interacted with on a per field basis. So I have prepared some API requests here for this specific data set that I think will demonstrate some of the powers of this API. So right here, remembering that our collection was called trees, we automatically have an API end point that will return all trees in the dataset. So we make this request and what is returned is an object, is an array of objects, Each object representing 1 tree and the specific fields which we have allowed to be returned publicly. By default, a 100 items are returned at once. You can edit that, and you can also paginate. So perhaps you only get a 100 or 200 at a time, and then you can build out a pagination system. Now you can also just very much the same way as inside of the data studio, filter the data that is returned. And the example we gave earlier was to filter to just show maple trees, and this is exactly how you would do it using the API. We add a filter and we say the tree name must equal maple. And now we only have those 100000 trees returned, or the first 100, of course. But, you know, you this only returns maple trees across all boroughs. You can do that with any field. So here we say we only want to return items where the borough is equal to Brent, and you can stack these. So you can say, I only wanna return trees that are in Lewisham and there are oak trees. And that hopefully you're starting to see that you can build quite powerful queries and the applications you can build on top of that. There are also API endpoints for creating, for editing, and for deleting data points. So that's also kind of introduces some really interesting potential uses for this dataset where perhaps it could even be maintained or moderated through citizen engagement. Now these last two examples I'm about to show you use different query parameters. So we're not filtering here, but instead, we are aggregating and grouping data to start to analyze it. So here we are saying we want to return every tree, but we wanna group it by the borough that that tree exists in. And this query here returns this result. So we see for every borough, we get to see how many trees are planted. Here's another example where we're grouping by a couple of different, heuristics and you'll get to see how this works. So here we're saying, again, return every tree, just count them. But I wanna group them by borough and by tree name. So now you get to see embarking in Dagenham, there are 219 elder trees. Embarking in Dagenham, there are 695 apple trees and so on. So, of course, there are gonna be way more objects here. It's gonna effectively be a matrix of every tree and every bar that exists in the dataset. But once again, we are effectively analyzing this data now via API. And it's these kind of queries that actually power the panels inside of directors insights. Now there are a whole bunch of query parameters that you can use over in the API here. You can filter, you can search, sort, limit, the number of items that are shown as seen. The default limit is 100. We mentioned that earlier. We also get to see all of the aggregation and grouping functions that are available, to, again, do more analysis on that data, and more. So refer to the documentation, but the APIs are super powerful, and allow developers to effectively remix and use data in their applications and projects. I hope you found this episode of democratizing data interesting. And once again, if you have a dataset or know of a dataset that might be interesting to include in this series, please do reach out to us. We'd love to see it. Until next time. Bye for now.","93cd94b2-425d-497b-923b-1abb71b19c95",[2912],"b96aae87-d073-4d72-a5a0-f77fa748d11d",[],{"year":978,"number":2706,"id":2707,"episodes":2915,"show":2916},[2709,2710,2692],{"title":2712,"slug":2713},{"id":2494,"slug":2918,"vimeo_id":2919,"description":2920,"tile":2921,"length":264,"resources":12,"people":12,"episode_number":264,"published":2922,"title":2923,"video_transcript_html":2924,"video_transcript_text":2925,"content":12,"seo":2926,"status":19,"episode_people":2927,"recommendations":2929,"season":2930},"wtf-is-saas","941313485","BaaS and SaaS sound a lot alike, but in practice they're entirely different. In this episode, Matt breaks down the difference between these two similar terms and offers a high-level look at both. ","82479abe-8c8c-4df9-8a13-0757be4421b3","2024-05-28","WTF is SaaS?","\u003Cp>Speaker 0: Hey there, and welcome back to another episode of Buzzword Wilderness. I'm Matt, navigating you through the forest that is Tech Terms. In this episode, we're breaking down 2 often confused terms in the Cloud world, which is BaaS and SaaS. This is not gonna be good for my list, but we're gonna try it anyways. So what is a BaaS?\u003C\u002Fp>\u003Cp>A BaaS or Backend as a Service simplifies app and website development by managing a lot of the back end operations through cloud services. It allows developers to really concentrate and focus on the fun parts, which are the user facing parts, the UX of the UI of the website or app, the front end. Some of the services managed by BaaS providers include, like, databases, cloud storage, and server management. Some of the best functionality you'll find is is user authentication, data management, and push notifications, or or things like that. What is SaaS?\u003C\u002Fp>\u003Cp>So SaaS is software as a service. It's essentially like renting out, software through the Internet. It's usually like a monthly fee for a SaaS tool or something along those lines. It's universally accessible from most any device, or it should be, with an Internet connection. Common use cases are email services, CRM systems, document creation tools, really any tool you find is probably a SaaS.\u003C\u002Fp>\u003Cp>Major providers like Google Workspace, Sales Salesforce, Microsoft Office, those are comprehensive software solutions. There's a lot of smaller ones, though. BaaS versus SaaS. So why the distinction? While both SaaS and BaaS streamline cloud service processes, they cater to different needs.\u003C\u002Fp>\u003Cp>So a BaaS is akin to delivering the ingredients and tools needed for developers to build applications. And a SaaS is a ready to consume software meal, handling all the back end, maintenance and updates. So if we're in this forest. Right? A BaaS is all of the tools and materials you need to build your own cabin, Whereas, SAS is one you find on Airbnb and you rent a fully equipped cabin.\u003C\u002Fp>\u003Cp>Probably way more expensive in the long run, but it gives you everything you need much faster. So understanding BaaS and SaaS helps demystify class you know, how you're using software in your organization. This was a short but sweet episode. I look forward to seeing you again on the next episode. Appreciate your time.\u003C\u002Fp>","Hey there, and welcome back to another episode of Buzzword Wilderness. I'm Matt, navigating you through the forest that is Tech Terms. In this episode, we're breaking down 2 often confused terms in the Cloud world, which is BaaS and SaaS. This is not gonna be good for my list, but we're gonna try it anyways. So what is a BaaS? A BaaS or Backend as a Service simplifies app and website development by managing a lot of the back end operations through cloud services. It allows developers to really concentrate and focus on the fun parts, which are the user facing parts, the UX of the UI of the website or app, the front end. Some of the services managed by BaaS providers include, like, databases, cloud storage, and server management. Some of the best functionality you'll find is is user authentication, data management, and push notifications, or or things like that. What is SaaS? So SaaS is software as a service. It's essentially like renting out, software through the Internet. It's usually like a monthly fee for a SaaS tool or something along those lines. It's universally accessible from most any device, or it should be, with an Internet connection. Common use cases are email services, CRM systems, document creation tools, really any tool you find is probably a SaaS. Major providers like Google Workspace, Sales Salesforce, Microsoft Office, those are comprehensive software solutions. There's a lot of smaller ones, though. BaaS versus SaaS. So why the distinction? While both SaaS and BaaS streamline cloud service processes, they cater to different needs. So a BaaS is akin to delivering the ingredients and tools needed for developers to build applications. And a SaaS is a ready to consume software meal, handling all the back end, maintenance and updates. So if we're in this forest. Right? A BaaS is all of the tools and materials you need to build your own cabin, Whereas, SAS is one you find on Airbnb and you rent a fully equipped cabin. Probably way more expensive in the long run, but it gives you everything you need much faster. So understanding BaaS and SaaS helps demystify class you know, how you're using software in your organization. This was a short but sweet episode. I look forward to seeing you again on the next episode. Appreciate your time.","760d8590-1637-410b-a84f-7ac518686bee",[2928],"fd96ee76-d322-483c-8ac5-9d3d1202371e",[],{"year":978,"number":674,"id":2490,"episodes":2931,"show":2932},[2492,2493,2494,2495,2476],{"title":2497,"slug":2498},{"id":2757,"slug":2934,"vimeo_id":2935,"description":2936,"tile":2937,"length":889,"resources":12,"people":12,"episode_number":889,"published":2938,"title":2939,"video_transcript_html":2940,"video_transcript_text":2941,"content":12,"seo":12,"status":19,"episode_people":2942,"recommendations":2944,"season":2945},"tesla-theme","931477106","Charge up your canvas and join Bry as teaches you how you paint an electrifying Directus theme for car maker Tesla.","cbf65318-3f34-4efb-99a1-e240228041fb","2024-05-27","Tesla (Simply Electric)","\u003Cp>Speaker 0: Hi. Welcome back to the show. I was just reading some more fan mail. I thought maybe we'd answer a question before we dive into the actual theme today. Dear Bry, how did you get your signature locks?\u003C\u002Fp>\n\u003Cp>Love, Matt. Your biggest fan. Well, Matt, I could tell you that, but I'd have to kill you. No, I'm just kidding. Only slightly.\u003C\u002Fp>\n\u003Cp>No, I've I've really kidding. No, I was once at a friend's house, you might know him. His name is, Chuck Norris. And Chuck and I were in a heated battle of Jenga, and the loser had to get a perm. And unfortunately, I toppled 1 too many blocks too fast.\u003C\u002Fp>\n\u003Cp>And here we are. Signature perm in check. It's worked in my favor, though, I think. Right. So today, we're gonna be painting a lovely theme for a lovely company and one of the richest men in the world.\u003C\u002Fp>\n\u003Cp>Now, I have not a lot of need for money. I'm very happy with just painting beautiful themes. But, mister Musk, if you're watching this, Brian Ross could use a Tesla to get to and from the office. Maybe we can work on that. Let's dive in.\u003C\u002Fp>\n\u003Cp>Alright. So let's cover a few tools of the trade. Right? We've got our trusty Logitech MX Keys keyboard. We've got a firm palette knife.\u003C\u002Fp>\n\u003Cp>This is great for painting broad strokes. And then we have our trusty half inch brush. Now you wanna hold that at 10 and 2 just like a steering wheel and that way you can get a good grip on your brush. So we're just gonna dive in. We've already added our project settings and looking at the Tesla website it's very very text heavy.\u003C\u002Fp>\n\u003Cp>So let's search for Tesla branding guidelines and see if we can find some logos that we can use. Now, I I do like the website. It's got a clean appearance. I I love lots of white space in my websites, especially in my themes. A good calm theme is great.\u003C\u002Fp>\n\u003Cp>Alright. So now we've got the Tesla logos. Let's just import those. We'll upload those here. Upload file to device.\u003C\u002Fp>\n\u003Cp>Just zip that over there. Just zip it. Alright. So we've got the project logo. That's gonna be white.\u003C\u002Fp>\n\u003Cp>Okay. We're gonna use the public foreground. That's great. And for our public background one of the, little known things inside your Directus CMS or back end whatever you're using Directus for you can use videos as the public background now this doesn't break the laws of theming. This is just a 4th dimension to that.\u003C\u002Fp>\n\u003Cp>It really makes your login page pop. So we'll just add a public note, welcome to the Tesla CMS plus the PIN. I swear we're gonna store all of our information. We've gotta make sure it's beautiful. Alright, so we'll look for our favicon.\u003C\u002Fp>\n\u003Cp>There it is. We're just gonna take that, save it out, just just a little save action and upload to device. If we really want to get fancy with it, we could just paint it on. Alright, so now we've saved this we've got our basic branding in here, but when I look at the Tesla website you know I'm thinking what could this theme be? You know, one of the things that I always consider when I'm theming is is this a dark theme?\u003C\u002Fp>\n\u003Cp>Is it a light theme? And in this case, I think we need to go dark. Now, I usually like to paint positive happy themes with bright colors, but sometimes there's no substitute for a great dark theme. Full of soul. Alright, so this is a great base.\u003C\u002Fp>\n\u003Cp>The Directus default dark theme provides a good look for theming, but let's add in that Tesla red. Let's make this thing pop. So for our project color, we're gonna change this to the Tesla red. So I'm just gonna take and carefully enter that with a half inch brush. This is an advanced technique so really pay attention.\u003C\u002Fp>\n\u003Cp>Just use that half inch brush to enter those hex codes. It's, it takes a little while to master that but I know you'll get the hang of it. Alright. So one of the other subtle effects that I see here, when they offer merch here on the Tesla web site, you got this nice grid, dot pattern background. I love texture.\u003C\u002Fp>\n\u003Cp>Texture just adds another element to blend everything harmoniously together into just the most wonderful theme. Alright, so we're going to go in and let's see if we can't find some of these colors that we're working with. Alright, so we're gonna use this dotted grid. We're just gonna paint that into our background accent color. Great.\u003C\u002Fp>\n\u003Cp>You can see we've got a a great looking sidebar or module navigation. But the next thing I'm gonna do is go into our navigation and I'm gonna dial in our background. We're gonna use the theme background color. Not not the correct variable. There we go.\u003C\u002Fp>\n\u003Cp>Alright. So now this is one color of black. It's a very dark black like a midnight. Looks great. But what we want to do is we want to add that dot pattern background.\u003C\u002Fp>\n\u003Cp>So let's just look through the document, see if we can find that. There it is. We have found it. Let's see the styles that are being applied. Right?\u003C\u002Fp>\n\u003Cp>So we're just gonna take this and we're going to convert it into a single background property. Now the navigation background can support patterns using SVG or data URLs and, we can also add gradients in. And again, anytime you're doing a lot of theming, you're switching colors, make sure that you just get in there with the dust or Dust the demons out of it. But make sure you don't ever turn your duster upside down because that that will make it freeze. It'll potentially explode.\u003C\u002Fp>\n\u003Cp>You don't want that. Alright, so now we're just going to add, negative 20 pixel offset and a little extra pixel inset and, that looks really great. So now we've got that subtle grid effect. This is this is a beautiful, beautiful theme. This is really what I was hoping for.\u003C\u002Fp>\n\u003Cp>Sometimes dark themes can be some of the most enlightening themes to paint. You're doing great, by the way. Alright. So now let's add some Tesla fonts. So we can see we've got a Gotham SS m font.\u003C\u002Fp>\n\u003Cp>If we take a look and we go through the network tab, we can see where these actual fonts are coming from. Alright. So now we're just going to, try to copy those and paint those in here. We'll search and see if we've got any font face definitions that we can already just use, but it doesn't look like it. So what we're gonna do, we're just gonna copy, and we're going to paste into our custom CSS.\u003C\u002Fp>\n\u003Cp>Now, you can get most of your theming done inside the dark or light theme customization, but sometimes you need to reach for a little extra power. That's where this custom CSS comes in. You just spread on a nice little font face property here. We're gonna use Gotham for the name. We'll add that source URL.\u003C\u002Fp>\n\u003Cp>We'll go in and paint our fonts for the dark theme customization. Just call this Gotham, kinda like Batman. Always loved that one line that he delivers in the movie. Justice never sleeps. Alright.\u003C\u002Fp>\n\u003Cp>Okay. So we'll just dial that back. That's great. The last thing that we need to do here is basically to fix this font. We want to make it all uppercase.\u003C\u002Fp>\n\u003Cp>It has to be all uppercase. So we're just gonna go in and just add a little type title. And now I'm gonna go in and just paint a little text transform in there. Just blend that text transform. Just a little up.\u003C\u002Fp>\n\u003Cp>Just keep going up until you get it all uppercase. Great job. Amazing. Alright. So this is looking like a beautiful theme.\u003C\u002Fp>\n\u003Cp>Let's just check it out. We've got all of our vehicles. It's beautiful. Great theme. Alright.\u003C\u002Fp>\n\u003Cp>So we're gonna log out, check out our public login page and wow, does that video ever make this pop? Doesn't that make you just feel like you need to be in a Tesla? I know it does me. So thank you for joining this episode of The Joy of Theming. We've got some extra special tips and techniques for painting beautiful, beautiful themes.\u003C\u002Fp>","Hi. Welcome back to the show. I was just reading some more fan mail. I thought maybe we'd answer a question before we dive into the actual theme today. Dear Bry, how did you get your signature locks? Love, Matt. Your biggest fan. Well, Matt, I could tell you that, but I'd have to kill you. No, I'm just kidding. Only slightly. No, I've I've really kidding. No, I was once at a friend's house, you might know him. His name is, Chuck Norris. And Chuck and I were in a heated battle of Jenga, and the loser had to get a perm. And unfortunately, I toppled 1 too many blocks too fast. And here we are. Signature perm in check. It's worked in my favor, though, I think. Right. So today, we're gonna be painting a lovely theme for a lovely company and one of the richest men in the world. Now, I have not a lot of need for money. I'm very happy with just painting beautiful themes. But, mister Musk, if you're watching this, Brian Ross could use a Tesla to get to and from the office. Maybe we can work on that. Let's dive in. Alright. So let's cover a few tools of the trade. Right? We've got our trusty Logitech MX Keys keyboard. We've got a firm palette knife. This is great for painting broad strokes. And then we have our trusty half inch brush. Now you wanna hold that at 10 and 2 just like a steering wheel and that way you can get a good grip on your brush. So we're just gonna dive in. We've already added our project settings and looking at the Tesla website it's very very text heavy. So let's search for Tesla branding guidelines and see if we can find some logos that we can use. Now, I I do like the website. It's got a clean appearance. I I love lots of white space in my websites, especially in my themes. A good calm theme is great. Alright. So now we've got the Tesla logos. Let's just import those. We'll upload those here. Upload file to device. Just zip that over there. Just zip it. Alright. So we've got the project logo. That's gonna be white. Okay. We're gonna use the public foreground. That's great. And for our public background one of the, little known things inside your Directus CMS or back end whatever you're using Directus for you can use videos as the public background now this doesn't break the laws of theming. This is just a 4th dimension to that. It really makes your login page pop. So we'll just add a public note, welcome to the Tesla CMS plus the PIN. I swear we're gonna store all of our information. We've gotta make sure it's beautiful. Alright, so we'll look for our favicon. There it is. We're just gonna take that, save it out, just just a little save action and upload to device. If we really want to get fancy with it, we could just paint it on. Alright, so now we've saved this we've got our basic branding in here, but when I look at the Tesla website you know I'm thinking what could this theme be? You know, one of the things that I always consider when I'm theming is is this a dark theme? Is it a light theme? And in this case, I think we need to go dark. Now, I usually like to paint positive happy themes with bright colors, but sometimes there's no substitute for a great dark theme. Full of soul. Alright, so this is a great base. The Directus default dark theme provides a good look for theming, but let's add in that Tesla red. Let's make this thing pop. So for our project color, we're gonna change this to the Tesla red. So I'm just gonna take and carefully enter that with a half inch brush. This is an advanced technique so really pay attention. Just use that half inch brush to enter those hex codes. It's, it takes a little while to master that but I know you'll get the hang of it. Alright. So one of the other subtle effects that I see here, when they offer merch here on the Tesla web site, you got this nice grid, dot pattern background. I love texture. Texture just adds another element to blend everything harmoniously together into just the most wonderful theme. Alright, so we're going to go in and let's see if we can't find some of these colors that we're working with. Alright, so we're gonna use this dotted grid. We're just gonna paint that into our background accent color. Great. You can see we've got a a great looking sidebar or module navigation. But the next thing I'm gonna do is go into our navigation and I'm gonna dial in our background. We're gonna use the theme background color. Not not the correct variable. There we go. Alright. So now this is one color of black. It's a very dark black like a midnight. Looks great. But what we want to do is we want to add that dot pattern background. So let's just look through the document, see if we can find that. There it is. We have found it. Let's see the styles that are being applied. Right? So we're just gonna take this and we're going to convert it into a single background property. Now the navigation background can support patterns using SVG or data URLs and, we can also add gradients in. And again, anytime you're doing a lot of theming, you're switching colors, make sure that you just get in there with the dust or Dust the demons out of it. But make sure you don't ever turn your duster upside down because that that will make it freeze. It'll potentially explode. You don't want that. Alright, so now we're just going to add, negative 20 pixel offset and a little extra pixel inset and, that looks really great. So now we've got that subtle grid effect. This is this is a beautiful, beautiful theme. This is really what I was hoping for. Sometimes dark themes can be some of the most enlightening themes to paint. You're doing great, by the way. Alright. So now let's add some Tesla fonts. So we can see we've got a Gotham SS m font. If we take a look and we go through the network tab, we can see where these actual fonts are coming from. Alright. So now we're just going to, try to copy those and paint those in here. We'll search and see if we've got any font face definitions that we can already just use, but it doesn't look like it. So what we're gonna do, we're just gonna copy, and we're going to paste into our custom CSS. Now, you can get most of your theming done inside the dark or light theme customization, but sometimes you need to reach for a little extra power. That's where this custom CSS comes in. You just spread on a nice little font face property here. We're gonna use Gotham for the name. We'll add that source URL. We'll go in and paint our fonts for the dark theme customization. Just call this Gotham, kinda like Batman. Always loved that one line that he delivers in the movie. Justice never sleeps. Alright. Okay. So we'll just dial that back. That's great. The last thing that we need to do here is basically to fix this font. We want to make it all uppercase. It has to be all uppercase. So we're just gonna go in and just add a little type title. And now I'm gonna go in and just paint a little text transform in there. Just blend that text transform. Just a little up. Just keep going up until you get it all uppercase. Great job. Amazing. Alright. So this is looking like a beautiful theme. Let's just check it out. We've got all of our vehicles. It's beautiful. Great theme. Alright. So we're gonna log out, check out our public login page and wow, does that video ever make this pop? Doesn't that make you just feel like you need to be in a Tesla? I know it does me. So thank you for joining this episode of The Joy of Theming. We've got some extra special tips and techniques for painting beautiful, beautiful themes.",[2943],"2b58734a-bc15-4b6a-be98-4ff372e66fab",[],{"year":978,"number":2745,"id":2746,"episodes":2946,"show":2947},[2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2731],{"title":2760,"slug":2761},{"id":2318,"slug":2949,"vimeo_id":2950,"description":2951,"tile":2952,"length":1156,"resources":12,"people":12,"episode_number":195,"published":2953,"title":2954,"video_transcript_html":2955,"video_transcript_text":2956,"content":12,"seo":2957,"status":19,"episode_people":2958,"recommendations":2960,"season":2961},"freelancer-marketplace","937054150","Today, we learn a new acronym \"JFI\"  = Just Fiverr It. Join Bryant as he tries to build a clone of the popular online marketplace for freelancers in just 60 minutes.","bdda03ab-03cc-47d7-b9d8-246c8ddc7fee","2024-05-24","Mission: Freelancer Marketplace","\u003Cp>Speaker 0: Hi, welcome back to another episode of 100 Apps 100 Hours. I'm your host, Brian Gillespie. And in this series, we will be rebuilding or building some of your suggested ideas, some of your favorite apps in 1 hour or less. There's only 2 rules. Number 1, we have 60 minutes to plan and build the application.\u003C\u002Fp>\u003Cp>No more, no less. And the second rule is use whatever you have at your disposal. Kind of the anti rule, if you will. So today we are going to be working on a freelancer marketplace. Like some of these ideas we run with on the show, this one sounds kind of vague and generic, but if you take a look at, sites like Upwork or Fiverr is one of the ones that I have kind of just imagined for this.\u003C\u002Fp>\u003Cp>Just got a listing of freelancers, a listing of talent that we can work from. On Upwork, you can actually post a job, with an estimated budget and have people bid on that. But I think the more interesting one for me, personally, is having a list of offers on this freelancer marketplace like web scraping via Excel and VBA And having a profile page here that we could purchase this offer and move forward with that as well. So, I think this is kind of what I'm envisioning for this. Let's start the clock and see how far we get with this.\u003C\u002Fp>\u003Cp>Alright. So 60 minutes on the clock, we are going to start building this thing, but before we build, we need to plan. So planning wise, I always like to try to discuss or just think out loud what type of functionality I'd like to see from an application. In this case, we want to have a, we need a user profile or a freelancer profile, And we've got a offer, freelancer offer, something like that. We want to be able to have an offer page that customers can buy, and I think that's probably going to get us most of the way there.\u003C\u002Fp>\u003Cp>That's really what I want. You know, we can set up this offer and then we need to have a form for them to submit with the details that we need to complete the offer and then complete payment. So how can we get that done? We'll see. Alright.\u003C\u002Fp>\u003Cp>So as far as the data model for this application, right, What do we want that to actually look like? So I'm imagining we've got a a user that logs in. That's gonna be actually using our Directus users collection because we are using Directus behind the scenes. Directus gives us users, it gives us authentication, it gives us permissions, and then we have, you know, this could be the customer as well, but then we have a freelancer profile or, you know, we could just call it a seller profile or something like that. Inside that seller profile, we're gonna have some basic information like a name, location, description, etcetera.\u003C\u002Fp>\u003Cp>And then attached to that seller profile, we're gonna have the user would be attached to that and then attached to that seller profile, we're probably gonna have some offers. So the offer is gonna be something like the name of the offer, the description. There's probably gonna be a slug on all these things. And then we've got a couple of different offer versions, you know, versions of this offer or packages, something like that. Packages, and those are gonna have a price, deliverables, etcetera.\u003C\u002Fp>\u003Cp>So that's the way that I think about this. On the flip side of that, we probably also got, an order or orders. Right? So I ordered this offer, there's a relationship there and a relationship there. And now we have, like, this giant rectangular flow thing going on.\u003C\u002Fp>\u003Cp>At least that's how I'm picturing it in my head. So with that in mind, let's get to work on actually building this thing out. The orders are going to have, let's see, they're going to have an offer ID. Offer ID, seller profile, and probably like some form information, form data. I don't know what that's going to be exactly yet, but we'll flesh it out.\u003C\u002Fp>\u003Cp>Right? Okay. So let's dive into actually build this application. Alright, I'm going to pull this up side by side. On the setup side, I've just got a Nuxt application we'll use for the front end here and then I've got a blank Directus instance that we're going to start building from.\u003C\u002Fp>\u003Cp>So, like I said, we get users out of the box for this. Maybe we go in and we create a new role for a user. They don't have app access but we'll give them access to some collections once we create them. But let's dive in and create our first collection. Let's just call it, our seller profiles, or it could just be profiles.\u003C\u002Fp>\u003Cp>You know, it could be, like, we have a buyer profile as well. You know what? Don't second guess it. Let's just roll with it. So we've got a date created, when this was updated, who updated it, the status of this, is this a published profile or not.\u003C\u002Fp>\u003Cp>That's great. We'll dive in, we'll add a name for the profile, we'll add a location. I I think this is honestly just like a, maybe it's like a city and a region and a country just so we could, I mean, we could potentially filter on the country if we wanted to. That gives us enough information. All this stuff is being delivered remotely.\u003C\u002Fp>\u003Cp>And then we have a description. So description may be HTML content. We want it to be rich, we want people to be able to stylize a description, maybe include some images. So we're going to use our WYSIWYG editor in Directus. Alright, so that's our seller profile.\u003C\u002Fp>\u003Cp>It's great, I can go in and set up my seller profile. This is Bry Ross. I am in Bluefield, West Virginia in the United States, just the US. I paint beautiful directus themes for you. Great.\u003C\u002Fp>\u003Cp>We may even have a image for that. Right? We've got to have an avatar for our seller profile. We probably have that on the user level as well, but you know, maybe we want to keep that user data private from the general public. So we can attach that to our seller profile and then attach that to our individual user.\u003C\u002Fp>\u003Cp>Great. Alright. So let's just search for Brad Ross, see what comes up. That's a great snap. That's what we'll use.\u003C\u002Fp>\u003Cp>Away we go. All right, so we've got our seller profile, let's start connecting that, right? That's going to be connected to a user, so we can build in a many to one relationship to that. And honestly, we probably will want to add that relationship to the user as well. Let's go for a one to many setup.\u003C\u002Fp>\u003Cp>This is going to be a user. Got the Directus users as the related collection, and the seller profile is going to be the foreign key. We'll show a link to the item. And we're going to make sure this value is unique. So inside Directus, we don't have a one to one relationship, you have to model that using many to one and one to many relationships.\u003C\u002Fp>\u003Cp>But you can achieve the same effect by saying, hey, this value has to be unique. So you can see that we are going to create a new profile. Great. Let's see. We've got our user.\u003C\u002Fp>\u003Cp>If I go to our system collections, we go to direct us users, I should now also have a seller profile that we can potentially link to. All right. So I've got that relationship set up. Let's go in and create a new orders table, and I'm just gonna leave that blank for now. I'm just gonna get that off of my mind.\u003C\u002Fp>\u003Cp>And then we're gonna do offers. Alright. So within our offers we're gonna have the same kind of thing. Who was this created? Is this offer published or not?\u003C\u002Fp>\u003Cp>And potentially within a profile maybe there's some offers that we want to sort by. So we can add all of these system fields. You can also just go back and delete those later if you don't need those things. So we've got a name for this offer. We've got, we're probably going to need a slug for this offer.\u003C\u002Fp>\u003Cp>And this is an extension that I've installed called the WP WP interface or WP slug extension. It just allows you to add a nice looking slug and you can auto generate this on Create. It doesn't work via the API, but for any heavy lifting inside the admin here, inside the Data Studio, works great. Alright, so again we've got like a rich description for this specific offer that we want to have and then you know, we probably have some packages, right? So what are the different levels of this specific offer?\u003C\u002Fp>\u003Cp>You can see here we've got like basic, standard, premium, etcetera. Let's go in and work on that. Right? To me, that would be a one to many relationship because one offer could have many packages, a package is only going to have one offer. So these are going to be constructed on an individual level, on an individual offer or product.\u003C\u002Fp>\u003Cp>I'm not sure what Fiverr calls these behind the scenes, but this is what we're going to roll with. We will model this out, we'll get the one to many relationship, we're going to call these our packages or it could be versions. Again, we're not going to get carried away with it. What if we call the Related Collection Offer Packages? So you can see I don't have this collection yet.\u003C\u002Fp>\u003Cp>And basically what Directus, the APIs are doing behind the scenes here is just mirroring all these changes to your SQL database. So that's really nice. We've got the offer is going to be our foreign key And I'm going to dig into the advanced field creation mode here. So if I go to my relationship, you can see that Directus is going to create these tables and this field for me, which is really nice, right? I don't have to have that table existing first.\u003C\u002Fp>\u003Cp>Directus is just going to create it for me. It makes things simple, especially when I'm just prototyping here. So now I've got packages, and if I back up, we can see that we have offer packages. Those are probably going to be a hidden collection. I don't really need to show those.\u003C\u002Fp>\u003Cp>And then we can actually work on the details of the package as well. Right? So we've got a title for the package or name. Package name. Sometimes when I'm working with deeply nested relational data, it helps to prefix like the name or title with the actual name of the collection, just because if you're if you're working with that data in a nested structure, it could be hard to know what the name pertains to, if you don't have some other type of identifier within that.\u003C\u002Fp>\u003Cp>So we have a package name, we've got a, like, a delivery window or something, delivery time. I see that's like 3 day, 5 day. We've got revisions. Are we really concerned about that? No.\u003C\u002Fp>\u003Cp>We've got like a short description. And in this case, it probably looks like a text description. We'll call it, you know, I could potentially call it short description or let's just call it description. Great. And then we have the deliverables.\u003C\u002Fp>\u003Cp>Right? So this could just be a list of things. The Repeater interface inside Directus is kind of nice for that, so that's what we'll use. I can just store this data as JSON information. I just want to store a list of deliverables.\u003C\u002Fp>\u003Cp>Deliverables. And then we have the title, the item. That's fine. That's what we'll call it. We'll do full width.\u003C\u002Fp>\u003Cp>We'll just make this a simple string, keep it easy. We'll make it an input. And I could do like an icon, but these are just checkboxes here on this site. Great. Alright, so we've got our offer packages, that's linked to an offer.\u003C\u002Fp>\u003Cp>Okay. And now on our order level, we need to go in and flesh this out further. Alright, so we've got orders, we've probably got a timestamp for the order. We'll just call it that. Or we could do created at or I think the standard format we use is date created.\u003C\u002Fp>\u003Cp>Maybe we use time stamp. It's fine. I can always rename those. And then when I go into the advanced field creation mode, I can set this up so that when an item is created inside this orders table, it will automatically save the current date and time. Okay, great.\u003C\u002Fp>\u003Cp>So the timestamp is saved. I can also go in and prevent this from being edited if I need to. That way inside the UI nobody can adjust the timestamp of this order. Alright. So a couple of things.\u003C\u002Fp>\u003Cp>The order is going to have a user or customer, you know, let's just call it the direct us users. Great. Alright. And then we have what? We have an offer for this and then we have the package.\u003C\u002Fp>\u003Cp>Right? So we're going to have both of those. We have an offer, that's going to be offers as the related collection. We'll save that many to one relationship and then we'll have a package, offer package, just so we make sure we get the correct package from that offer as well. Offer packages.\u003C\u002Fp>\u003Cp>Cool. And then, you know, as far as, like, a form after that, you know, maybe we've just got, like, some JSON data that we render. I'm imagining that on the backside of Fiverr, it's like a dynamic form. I could build my own form. So in this case, maybe we just, form data.\u003C\u002Fp>\u003Cp>Oh, you we just we just create, like, a JSON field that we could automatically populate data to. Hit save. We'll add a template for it. Okay. So that's what our orders look like.\u003C\u002Fp>\u003Cp>I would probably need a an amount for the order. Let's just use a, decimal. Rate. I could go in and flip this around. Maybe we've got 2 decimal places.\u003C\u002Fp>\u003Cp>This could be carried out to, I don't know, 10 places. And I could even go in and get fancy and add, like, a dollar sign or something within the interface. Pretty pretty simple. Okay. So offer user amount, form data, that's great.\u003C\u002Fp>\u003Cp>What else was the other thing that I wanted to do? On the packages, we forgot to set a price. Alright. So we need a price for this package. Let's use a decimal.\u003C\u002Fp>\u003Cp>You could store this as an integer as well. That's what Stripe does, keeps things simple. But, I like decimals displaying nicely inside the UI. Alright. So we got our seller profile.\u003C\u002Fp>\u003Cp>Let's create an offer. Right? Why is our offer blank? Oh, some kind of extra table. Okay.\u003C\u002Fp>\u003Cp>Alright. So we've got offers. Right? Great. We're gonna I will create a Directus theme for you.\u003C\u002Fp>\u003Cp>We'll go ahead and publish this. One thing that I can already tell that we're forgetting is like a gallery for this specific offer. So let me fire up chatgpt. Let's get a nice formatted description here. Create a rich text description for a Fiverr like gig called I will create a Directus theme for you.\u003C\u002Fp>\u003Cp>Alright. So while that's working, let's set up our different packages, right? This is the basic package, the delivery time is 3 days. We'll create a simple theme. We've got some deliverables.\u003C\u002Fp>\u003Cp>This is, one theme, 2 revisions. Great. Alright. The price for that is 199. And then maybe we add another one.\u003C\u002Fp>\u003Cp>Right? So I can also go in and edit this raw value if I wanted to. We'll just go in and add another package. This is the premium package. This takes 5 days.\u003C\u002Fp>\u003Cp>We'll create the most amazing theme you have ever seen. One theme. Six revisions. That's a lot of revisions. Let's just dial it back a little bit.\u003C\u002Fp>\u003Cp>Way more than what I would actually go to. Here's our description for this and then, you know, hand holding and support. Or let's call it theme installation as well. Cool. Alright.\u003C\u002Fp>\u003Cp>We'll copy this description. Paste that in there. Save. Now we've got our offer. I need to add some images to this as well, right?\u003C\u002Fp>\u003Cp>So for our offer, maybe we want to show an image gallery. Maybe we have one, there's a featured image that's going to be like a card for this. We'll save that. And then we could potentially also have multiple images. And we'll call this the gallery, image gallery.\u003C\u002Fp>\u003Cp>Let's just call it gallery. That's great. And what in this case, what it does is it creates a junction collection, ties that back to our Directus files collection, which is basically your file manager inside Directus. Alright. We've got to have a sort field for those so we can control which order those display.\u003C\u002Fp>\u003Cp>And away we go. Right? So now we have a featured image and a gallery. We can upload a feature image. Let's just make this Bry Ross.\u003C\u002Fp>\u003Cp>And for our gallery, let's see if we have, like, some screenshots. Yeah. We got some past screenshots here of a few different themes or just one theme, actually. And now we've got an offer. We don't have any orders.\u003C\u002Fp>\u003Cp>We've got a seller profile. Cool. Cool. We've got the offer. We don't have that linked to our seller profile yet, right?\u003C\u002Fp>\u003Cp>So let's do that as well. This is going to be a mini to 1. This is going to be the seller profile. We'll pick that seller profiles collection. And I'm going to open up the advanced field mode here.\u003C\u002Fp>\u003Cp>We're going to add, just this corresponding field in the table Seller Profiles. So we're going to add the reverse relationship there just so we can see that as well. So now if I go into my seller profile for Bry Ross, I should be able to see my offer, but I haven't linked that specific one yet. So we'll go here and pick Bry Ross. There we go, he's got an offer on the marketplace.\u003C\u002Fp>\u003Cp>Now if I go back to Bry Ross we can see that actual offer, Vice versa, if I am on the offers I can get to the seller profile. Alright, so that is a lot of data modeling. We've eaten up about 20 minutes of time just fleshing this out. Let's actually start building something. Alright.\u003C\u002Fp>\u003Cp>So we'll just start inside the Nuxt application. I'm going to pull this up. Actually it might be handy just to have this kind of sitting here as well. So on the index page, right, we want to get a list of our offers. We can do that.\u003C\u002Fp>\u003Cp>I've got a Directus Nuxt plugin here that has some credentials already set up. We've got real time, let's say we wanna view a list of offers. I can do this, I'm gonna access that Directus plug in. So we'll just do Directus equals use Nuxt App. So I'm going to call the Directus client here and then we're going to use the Nuxt Constant Data.\u003C\u002Fp>\u003Cp>They have a Use Async Data composable that makes this process easy. So we'll just do offers slash index, give this a key, and then we are going to add a function just to call this data. So we'll do directus dot request, we're going to do read items and we want to pick up the offers collection. We'll add some filters for this. Maybe we want to do the filter equals status.\u003C\u002Fp>\u003Cp>We want that to be equal to published, so we only want to get the published offers. And what else are we going to add to this? We need a little bit of help there. Am I missing What am I missing? Got too many.\u003C\u002Fp>\u003Cp>Not enough return. Directus offers. Got one more, and we have one more. Use async data, offers index, data, and the mesonoma syntax here. Request.\u003C\u002Fp>\u003Cp>Okay. Stuff that in there. There we go. Alright. Got that fixed.\u003C\u002Fp>\u003Cp>Now we are actually reading this data. Let's just remove this. We're going to wrap this in a pre tag just to make sure we're actually getting some kind of data from this application. Alright. So what I don't see is any actual data here.\u003C\u002Fp>\u003Cp>And if I take a look, we can see if we're getting any API calls. No calls are going out. Number 1, I have to import that read items method from the SDK. Now we can get something going here. On the server side, no, not seeing it.\u003C\u002Fp>\u003Cp>One of the potential reasons there is we are not logged in, right? So we created this data model, but Directus gives us a couple different roles for our users or our access control out of the box. By default, the public role is what users have access to when they are not authenticated. So anybody can access this level of information from the API, which is always set to 0 out of the box. So maybe we want to show our offers, when I'm getting ready to move into production I would strictly scope this down.\u003C\u002Fp>\u003Cp>Maybe they don't need actual order information, but as far as the publicly displayed information, we're going to show that. And now I can actually see that. Let's just sort this out here. Okay. So we've got like a login and register buttons.\u003C\u002Fp>\u003Cp>We don't really need those. Here is our offers. Right? I could drill into the individual packages here, but just to display a list of cards, I don't really need any of that. So we'll just do a quick h two, we'll do some offers, set up a grid.\u003C\u002Fp>\u003Cp>How many do they show here across graphic design? Maybe I have 4 up Grid calls, 4. Great. And then we've got, let's just add a card component. The offer for I don't really like any of this.\u003C\u002Fp>\u003Cp>Let's destructure this. This is gonna be our offers. Offer and offers. Key. Great.\u003C\u002Fp>\u003Cp>Now within our card, we'll just flesh this out a little bit. We'll wrap that in a Nuxt link. So we just want to do 2 offers. I'll have to create a route for this in just a moment. Offer dot ID, or, you know, it could potentially be the slug on the offer if we want.\u003C\u002Fp>\u003Cp>Let's roll with ID just for now. Make things a little bit easier. And just close this tag. Great. Okay.\u003C\u002Fp>\u003Cp>And then within the NUCs link we have, what, we've got an offer name. So this could be an h three. That's gonna be an offer dot name. Jeez. I can't actually type.\u003C\u002Fp>\u003Cp>Then I have a p for the description. Maybe we wanna strip some HTML. Do I have inside my boiler plate here, I should have just a function to strip HTML tags out. So I'll do that. We'll strip HTML.\u003C\u002Fp>\u003Cp>Great. And, let's add an image for this as well. Right? So we'll do image. I'm pretty sure I've got this set up with Nuxt Image as well, which is just a replacement for the image tag.\u003C\u002Fp>\u003Cp>Allows you to take advantage of, like Sharp library to optimize images. So we got offer dot featured image is the wording we're gonna use here, the offer dot name. Let's see what we've got. Right? Okay.\u003C\u002Fp>\u003Cp>So we've got a card, we've got grid calls for, left off an s. That's good. Let's make the font bold for this. Text a little larger, 2 x l. This is way too long as well, so let's truncate the string after we strip all the HTML.\u003C\u002Fp>\u003Cp>Maybe we limit to like a 150 characters. Much better. Add just a bit of padding. And I think the Nuxt UI library has this U container component we can use to just add that padding to it. Alright.\u003C\u002Fp>\u003Cp>So we got an offer, great. Let's go in, we'll click on that offer, the offer is not found. We need to set up a quick route for this offer, we'll do that. First I'm going to create just a folder for this, Nuxt does file based routing, which is really nice. And here we'll just do the ID dot view and I can copy a lot of this to carry over.\u003C\u002Fp>\u003Cp>Alright, so instead of Read Items, I'm just going to Read Item. Now if I was using the slug, I could change my filter to be the slug here. We're going to end up using the route, we need the route params. So we'll just use route. And here, as far as the request, we're gonna change this up just a little bit.\u003C\u002Fp>\u003Cp>This will be the offer maybe change this to show the offer ID. Still talking pop up? Yes. Still talking. Okay.\u003C\u002Fp>\u003Cp>Offer dot ID. I will, baby. It's the joys of working from home office. Right? Okay.\u003C\u002Fp>\u003Cp>So we'll log in. We've got the readitem. Readitem accepts 2 arguments, or actually 3 arguments. We've got the collection, we've got the item ID, and then we have a query that we can use to either return certain fields or not. But in this case, all I'm looking to do here is just grab this ID.\u003C\u002Fp>\u003Cp>Cannot access offer before initialization. We don't need any of that. We're not even going to need to wrap this. Right? Offer dot image, This is gonna be the offer dot title.\u003C\u002Fp>\u003Cp>Do we actually get the offer here? No. We're missing something. Gonna read the author, read item, cannot access offer before initialization. Oh, the the route dot params dot ID.\u003C\u002Fp>\u003Cp>Can't use the offer that you don't have access to yet. Okay. So there we go. We are working with an offer, author, offer. I keep getting those mixed up.\u003C\u002Fp>\u003Cp>I'm just gonna go back to this one. I'm gonna drag it over here. I just arced myself, didn't I? As far as the web scraping profile. Okay.\u003C\u002Fp>\u003Cp>I've got this on a separate screen that you can't see at the moment. I'm just going to do, like, some quick formatting for this. U container, grid calls. Why does this look this way? Probably because we have it in a grid.\u003C\u002Fp>\u003Cp>Set this up. We've got the offer title. Oh, should be the offer name above. That'll actually be an h one tag in this case. Not that you're concerned about semantics on this particular episode.\u003C\u002Fp>\u003Cp>Alright. So we've got an image, we've got the description, and in this case, what we're going to do, instead of truncating that, we want to show the whole thing. So we'll do vhmml equals offer dot description and because I've got tailwind installed, I'm just going to use the pros class. We'll use, like, a large text for this. And boom, there we go, we got that.\u003C\u002Fp>\u003Cp>Now, on the right side of our page, we've got, like, the offer details, right. But what I don't have in this case, if we just log this data out again, Alright, if I take a look at it, I don't have my package information, I don't have the seller profile that I want. So how can we actually fetch that information? Directus makes this really easy. We'll just use a fields parameter in our query.\u003C\u002Fp>\u003Cp>You don't have to, you know, this is available through the regular API, syntaxes. It's pretty much the same across the board here. You don't have to be using the SDK to access this information. So, I don't recommend doing this in production, but because we're just bumping through here, we will use an asterisk to get all the root level fields on this. What else do we want?\u003C\u002Fp>\u003Cp>We want the packages. We'll get all the fields on those packages. And then for the gallery, let's get the next level fields for the gallery. And now we can see what we're working with here. So we've got a gallery of images.\u003C\u002Fp>\u003Cp>Cool. We've got access to that information. Okay. So I'll just leave this up. That's cool.\u003C\u002Fp>\u003Cp>We want to maybe wrap this in another div. We're gonna maybe flex this container. This will be I mean, it it could even be a grid at this stage of the game. Let's make sure we are we're only targeting on, like, a small or a larger screen size. We'll have, like, a I don't know.\u003C\u002Fp>\u003Cp>Like a 3 column grid. This will be call span 2 columns. See what that looks like. Do I have this running now? Okay.\u003C\u002Fp>\u003Cp>Great. We'll just hide this pre offer. And then on the next one, this will be class call span 1. And here's where we're gonna put our packages. Alright.\u003C\u002Fp>\u003Cp>So packages are gonna go over there. Obviously, we're gonna add some nice gap for these just to clean this up a little bit. And, again, this is this is fairly rough. Right? If I want to just move this in here, you could see what do we have as far as our packages?\u003C\u002Fp>\u003Cp>Just gonna throw that down there. Alright. So as far as our packages, we've got these shown in a couple tabs. Let's look at ui.nuxt. Do we have tabs here?\u003C\u002Fp>\u003Cp>Yeah, there's like a tabs component. Okay. Tabs equals items. Alright. So let's start flushing this out.\u003C\u002Fp>\u003Cp>We got tabs. We got items equals, let's just write like an inline function. This is another case of I do as I say, not as I do. But let's actually map these packages out. What have we got here?\u003C\u002Fp>\u003Cp>Right? Offer dot packages. We're gonna map these to the actual items. What what shows up in the tab here? The content is x.\u003C\u002Fp>\u003Cp>Does this have, like, a oh, it's got, like, a slot that you can use. I to me, this feels, like, really kinda heavy handed, but, yeah. Whatever. Let's roll with it. Packages dot mat.\u003C\u002Fp>\u003Cp>Actually, let's not. Let's just like flesh these out real quick. Divv4. We use, like, a selected package. Selected package.\u003C\u002Fp>\u003Cp>That'll be what's offer dot packages offer.value.packages. And we'll do the first one. Alright. So that's gonna be our selected package dot ID. Let's call it selected package ID.\u003C\u002Fp>\u003Cp>Good enough. Alright. And then we're just gonna loop through these packages, offer dot packages, offer in offer dot packages. The key here would be key equals offer or, actually, no. We want package Package dot ID.\u003C\u002Fp>\u003Cp>Okay. How are we doing on time? We'll check this. We have got 20 minutes left. Package dot ID.\u003C\u002Fp>\u003Cp>Alright. And then within the package, we are going to have the package. Name, package dot name. Let's just see what this gives us. Then we have a list of deliverables.\u003C\u002Fp>\u003Cp>V 4 package alright. Deliverable item in package dot deliverables. Key equals we'll just call that the item, and we'll do item dot item. K. Item dotitem.\u003C\u002Fp>\u003Cp>K. What else do we have? We have a short description for that. That'd be a p tag. Package dot description.\u003C\u002Fp>\u003Cp>Alright. And, oh, transform failed with, like, 5 things. We've got just too much going on there. There's our packages. Those are not showing.\u003C\u002Fp>\u003Cp>Offer in packages. Package in offer dot packages. Key equals package dot ID. Offer package, oh, package underscore name, package description. Oh, we can't use package.\u003C\u002Fp>\u003Cp>That doesn't make a ton of sense, but, it does make sense. Pkg offer dot packages. Should learn that a long time ago. Item in pkg. Cannot read properties of dot packages, null.\u003C\u002Fp>\u003Cp>Offer dot value. Okay. Alright. So there's our different packages. We probably got a price for that as well.\u003C\u002Fp>\u003Cp>Let's take a look. Cool. I was in a div.flex p dollar sign pkg.price199. Okay. Package dot.\u003C\u002Fp>\u003Cp>It's actually package name. Okay. How are we doing on time? Probably not good. And then we'll just justify between.\u003C\u002Fp>\u003Cp>Okay. And what else? So we got our packages. Let's wrap this. Actually, we wanna do this.\u003C\u002Fp>\u003Cp>We'll do wrap it in a template tag. V if, we get selected packages, selected package ID, offer dot value, unref, offer packages, 0 dot idvif, what, selected package ID equals package.id. I can't read no packages. So this doesn't exist yet. Select the package ID.\u003C\u002Fp>\u003Cp>Offer dot packages. What are we gonna do with that? Let's just leave that. Null for now. That'll get us back to rendering, but we don't really render anything.\u003C\u002Fp>\u003Cp>And we do if offer what's that gonna do for us? Can't read, undefined, offer dot packages. Oh, did I even have that correct to begin with? Unref offer dot packages dot zero dot ID dotvalue.packages.id. Okay.\u003C\u002Fp>\u003Cp>Anyway Alright. And then what we're gonna do, we'll just add a list of buttons here. Div. Flex those. Add some gap to alright.\u003C\u002Fp>\u003Cp>So we'll do buttonv4, package and offer dot packages. And package dot package name. Alright. So we got those 2. And on the click of the button, that click, we'll do selected package ID equals pkg.id.\u003C\u002Fp>\u003Cp>No. Why is it doing that? Let's see. Expanding this when I don't want it to. Thank you, GitHub Copilot.\u003C\u002Fp>\u003Cp>Alright. So there we go. We can switch between these two packages. And then for each package, maybe we just have a buy button for that as well. Let's see what this looks like inside, Fiverr.\u003C\u002Fp>\u003Cp>They kinda send us out where you can order the quantity, you can change, you can add on, things like that. Not super concerned with that. I'm actually going to just totally cheat here. We'll open up my old faithful Stripe account, log in, Go into test mode for this. Just create a new product.\u003C\u002Fp>\u003Cp>We'll call this the theming package, theming offer, direct to themes, buy Bry. Great. We've got more pricing options. This is gonna be a one off. We've got flat rate, customer chooses price, price by package, or bundle of packages.\u003C\u002Fp>\u003Cp>Let's do a flat rate. Hit next. What did we charge for this? 199 for the one package. Do set.\u003C\u002Fp>\u003Cp>Add another price. This will be the 299 for the premium package. Great. Billing period. That's a flat rate.\u003C\u002Fp>\u003Cp>Great. Alright. So we got 2 potential options there. We'll add this product. Of course, I gotta log in.\u003C\u002Fp>\u003Cp>Alright. We're cooking. We're cooking. We got 15 minutes left. And now direct this theme is by Bry.\u003C\u002Fp>\u003Cp>You know, normally, like, taking the time to go through this, I would certainly go in and then, just basically, like, have this created through a Stripe checkout. So we got a payment link that we'll create for this. Great. We'll create this link, copy this, and if we go into our direct assistance where are you, mister directus instance? Admin@example.com.\u003C\u002Fp>\u003Cp>Password. Okay. Alright. So for each one of these packages, offered packages, let's have a buy link. That'll just be a string.\u003C\u002Fp>\u003Cp>We'll serve that on the front end inside a button component. That looks good. Alright. Offer packages. So let's set this up.\u003C\u002Fp>\u003Cp>Right? This is our offer. Where's our packages? Here's the 199 package. There's the buy link for that.\u003C\u002Fp>\u003Cp>Lost my Stripe payment link. We'll create a let's create another payment link real quick. Where are you? Directus themes by Bri. We need a payment link for this one.\u003C\u002Fp>\u003Cp>Create the link. Great. Copy that. Again, this is the quick and dirty way of doing this, but sometimes you got to. Alright.\u003C\u002Fp>\u003Cp>This will be 299 for this specific package. Save. Save. Save. Save.\u003C\u002Fp>\u003Cp>If we look local host, now we should have access to that data. So we can see those prices there. And then inside each one of these packages, after our deliverables, we'll just add a button. We'll add a 2. This is gonna be, package pkg.buyurl.\u003C\u002Fp>\u003Cp>Is that what I set that up as? By link. By underscore link. By pkgpackage_name. And by premium, this should redirect us.\u003C\u002Fp>\u003Cp>Right? Redirect us to direct us. Yeah. Nothing fancy here. Alright.\u003C\u002Fp>\u003Cp>So if I hit now, I hit buy premium. We've got what? 13 minutes left? Buy premium. I go in, I check out, that's great, we're gonna have to fill out this information.\u003C\u002Fp>\u003Cp>But one thing I wanna do here afterwards, right, is after they pay for this, we need to send them to a form. Now if I'm using the Stripe API endpoint, I can set up a referral URL after I check out to redirect. Not going to do that here because I don't really have time to set up all those specific endpoints. Again, we're gonna go quick and dirty mode. We'll go to webhooks.\u003C\u002Fp>\u003Cp>Let's set up a webhook for this. Of course, this is gonna be in my local environment as well. Okay. I gonna cut this close. Right?\u003C\u002Fp>\u003Cp>We'll go into flows. Let's create a new flow. We'll call it catch stripe. Webhook. Catch stripe will trigger on a webhook.\u003C\u002Fp>\u003Cp>Incoming webhook, it will be a post request. What are we going to do when we catch that? Are are am I I I'm I'm getting way ahead of myself. Like, we could catch this and do an email or something like that, but let's just say we yeah. It let's let's do something more interesting.\u003C\u002Fp>\u003Cp>And, switching gears here. Let's just assume gonna make a bunch of assumptions here. I'm just gonna go ahead and check out in this process. If we go back to Stripe where are you, Stripe? I think within the actual payment links, can we do this by, let's say for the premium link, after payment we are going to just redirect.\u003C\u002Fp>\u003Cp>We're gonna go to http local host 3,000\u002Fsubmit\u002Foffer\u002Fsubmit. Offers slash submit. Alright. So we're just gonna create a new page. This will be submit dot view.\u003C\u002Fp>\u003Cp>We'll take this information. Submit dot view. And what are we gonna do with this? Right? We're gonna make sure we still let's let's get our offer just to show that up at the top.\u003C\u002Fp>\u003Cp>And then we're gonna build a form to actually submit some data or actually create an order. Again, normally all this would be via webhook and either flows or like a specific endpoint, but this is what we'll do just to throw something out with our remaining 10 minutes. Right? So we've got our freelancer profile. We've got an offer page that customers can buy.\u003C\u002Fp>\u003Cp>So we check the box on that functionality. Technically, I guess this is a win. Is it, a smooth win? I don't know. And then we just want to, like, submit an order form.\u003C\u002Fp>\u003Cp>Alright. So how do we do that? We will do create item. We're gonna need read item. We still wanna pick up that offer.\u003C\u002Fp>\u003Cp>And let's make sure this submit isn't gonna actually work on our front end. Got too many links going on. Local host 3,000 slash offers slash submit. Cannot read properties. Offer oh, I don't have the route params.\u003C\u002Fp>\u003Cp>So let's drop that. Let's move offers underscore ID. We'll make that a folder. This is gonna be dropped into that folder. So it's submit.\u003C\u002Fp>\u003Cp>And this is gonna be index. Alright. So that adjusts things a little bit. And now offers route params.id. Oh, becomes let's go back to the index page.\u003C\u002Fp>\u003Cp>We go here now. Actually, that's gonna be broken. And it no. That won't be broken. Shouldn't be broken.\u003C\u002Fp>\u003Cp>There we go. Alright. So now I've got that, and then we'll just have a submit page. There we go. We've got that.\u003C\u002Fp>\u003Cp>Let's add a h two tag. Thanks for your purchase of this package. Great. And then we've got, like, a form. Alright.\u003C\u002Fp>\u003Cp>So u form, and what are we gonna do with this form? We are, what, we're going to submit this, we're going to need a function here, async function. Submit order. K. We are going to wrap that in a try catch.\u003C\u002Fp>\u003Cp>We'll do constant order equals await, direct us create offers, create orders, create item orders. We have the package, selected package dot ID dot value. Let's not worry about the package at this moment. Then we've got form data, which is just gonna be a JSON object. What else do we have inside this Directus instance that keeps magically getting logged out?\u003C\u002Fp>\u003Cp>7 minutes, offers, orders. Alright. So we had the user. We had the amount. That's gonna be the selected package.\u003C\u002Fp>\u003Cp>This is probably actually backwards. Right? Maybe we should have submitted this form first and then redirected to checkout after they submitted this form. But, hey. Nobody's perfect.\u003C\u002Fp>\u003Cp>Alright. So form data. What do we want inside the form data? We don't really have to worry about that at this moment. Then we'll just add one more.\u003C\u002Fp>\u003Cp>There's our submit order function. Will this create an order? We can console. Log the order after we create it. But let's just build a quick form that will actually submit this.\u003C\u002Fp>\u003Cp>Alright. So we got you form group. What do we need to actually build a theme for someone. So we need the website URL. We'll have a input for that.\u003C\u002Fp>\u003Cp>V model equals form dot website URL. Actually, that'll be form under underscore data. I actually need a form here. Form, let's make this reactive. And, honestly, here, this will be form.\u003C\u002Fp>\u003Cp>Alright. So form dot website URL. Great. Make that required. And I think that actually goes here on the form group.\u003C\u002Fp>\u003Cp>Again, just rolling off of the Nuxt setup. Label, what else do we want? Form. I got like 35 things going wrong here. Theme, colors.\u003C\u002Fp>\u003Cp>Maybe we add, like, some kind of extra helper text within this. What is the color palette for this theme. Okay. Let's see what this actually looks like. Okay.\u003C\u002Fp>\u003Cp>So we got like a we've got an actual form rolling here. Add some spacing. Add a submit button, new button. Submit form. Okay.\u003C\u002Fp>\u003Cp>Also, let's make sure that you have to be logged in for this. So we'll do constant user equals use state user v if user u form v else. Oh, no. I don't need this. Div v else.\u003C\u002Fp>\u003Cp>Please log in to submit the form. We'll do u button. Ah, boy. Actually, it would be just 2\u002Flogin. No.\u003C\u002Fp>\u003Cp>It's auth\u002Flogin. Log in. Alright. Are we actually logged in? I guess I am logged in because I'm sharing a session token here.\u003C\u002Fp>\u003Cp>But if I were to like, go here and look at this, right, it's not gonna show me that because I'm not logged in. And then I could log in, and then I could go back and potentially see that offer or submit. Right. So now that I'm logged in, I can see this information. But we are running dangerously low on time.\u003C\u002Fp>\u003Cp>Let's make this a text area. And let's just run through this. Right? We have purchased something via the offer page. We're now submitting this data.\u003C\u002Fp>\u003Cp>What a mess this one's turned into. But we'll do the directis. Io, violet, white, shades of pink, slight gray. Cool. We hit submit form.\u003C\u002Fp>\u003Cp>What happens what happens if I hit submit form? Nothing happens, because I didn't tie that to any actual button. At submit equals what submit order. Submit order. Submit order.\u003C\u002Fp>\u003Cp>I should be seeing a fetch request. At button oh, it's a click handler. At submit would have been on the form itself. We'll save. Website URL, httpdirectus.io.\u003C\u002Fp>\u003Cp>Okay. Do we wanna track, like, a success state? Success equals ref false. I love cutting this down to the wire. Right?\u003C\u002Fp>\u003Cp>Console log order, Success value equals true. And then if the and no success. Maybe we want to show a success message. The if success. We do yes.\u003C\u002Fp>\u003Cp>Okay. What are we looking at here? Why is it not logged in? V else if v else if. V else.\u003C\u002Fp>\u003Cp>So I fix it. Okay. HTTPS, 1 minute 25 seconds. What happens? Nothing is happening.\u003C\u002Fp>\u003Cp>What did I do? What happened to my click handler? Disappeared. Submit order. I got the wrong I put it in the wrong spot.\u003C\u002Fp>\u003Cp>Okay. Let's try it again. Refresh. Httpdirectus.io. Where is my success with 48 seconds left to spare.\u003C\u002Fp>\u003Cp>Has this actually gone through? Has it submitted an order? Can we actually look at it? Do we have an order inside the system? We do.\u003C\u002Fp>\u003Cp>We don't have the offer and the package in there. So the offer equals the value dot author dot ID. If we go back and we check that just one more time, Submit another order to this. Will that now have the there's the offer. Again, like, we could have this all set up and routed correctly with enough time, but what a wild ride that was to try and get this information actually into the system.\u003C\u002Fp>\u003Cp>So wow. Where would we take this from here? Right? I would go through and set up this logic to be very robust. Again, as far as the offers here, I would probably I don't know that I would create products inside the Stripe account for each one of these offers.\u003C\u002Fp>\u003Cp>I would just set up, a checkout to Stripe that would take the order value or the order amount from a cart, throw that to the Stripe checkout, they would check out. Stripe sends a webhook. We also redirect them back to a page where they can fill out the necessary information, on a page like this to give us the order information that we need, to actually complete that order. So we can see that information here. But that's where I would go from here.\u003C\u002Fp>\u003Cp>Obviously, I would do a little a few things different. If we had to add more than an hour, probably 2 hours here, we could have had a really nice application. But bare bones, this is pretty good. That's it for this episode of 100 apps, 100 hours. I'll catch you on the next one.\u003C\u002Fp>\u003Cp>Thanks for following along.\u003C\u002Fp>","Hi, welcome back to another episode of 100 Apps 100 Hours. I'm your host, Brian Gillespie. And in this series, we will be rebuilding or building some of your suggested ideas, some of your favorite apps in 1 hour or less. There's only 2 rules. Number 1, we have 60 minutes to plan and build the application. No more, no less. And the second rule is use whatever you have at your disposal. Kind of the anti rule, if you will. So today we are going to be working on a freelancer marketplace. Like some of these ideas we run with on the show, this one sounds kind of vague and generic, but if you take a look at, sites like Upwork or Fiverr is one of the ones that I have kind of just imagined for this. Just got a listing of freelancers, a listing of talent that we can work from. On Upwork, you can actually post a job, with an estimated budget and have people bid on that. But I think the more interesting one for me, personally, is having a list of offers on this freelancer marketplace like web scraping via Excel and VBA And having a profile page here that we could purchase this offer and move forward with that as well. So, I think this is kind of what I'm envisioning for this. Let's start the clock and see how far we get with this. Alright. So 60 minutes on the clock, we are going to start building this thing, but before we build, we need to plan. So planning wise, I always like to try to discuss or just think out loud what type of functionality I'd like to see from an application. In this case, we want to have a, we need a user profile or a freelancer profile, And we've got a offer, freelancer offer, something like that. We want to be able to have an offer page that customers can buy, and I think that's probably going to get us most of the way there. That's really what I want. You know, we can set up this offer and then we need to have a form for them to submit with the details that we need to complete the offer and then complete payment. So how can we get that done? We'll see. Alright. So as far as the data model for this application, right, What do we want that to actually look like? So I'm imagining we've got a a user that logs in. That's gonna be actually using our Directus users collection because we are using Directus behind the scenes. Directus gives us users, it gives us authentication, it gives us permissions, and then we have, you know, this could be the customer as well, but then we have a freelancer profile or, you know, we could just call it a seller profile or something like that. Inside that seller profile, we're gonna have some basic information like a name, location, description, etcetera. And then attached to that seller profile, we're gonna have the user would be attached to that and then attached to that seller profile, we're probably gonna have some offers. So the offer is gonna be something like the name of the offer, the description. There's probably gonna be a slug on all these things. And then we've got a couple of different offer versions, you know, versions of this offer or packages, something like that. Packages, and those are gonna have a price, deliverables, etcetera. So that's the way that I think about this. On the flip side of that, we probably also got, an order or orders. Right? So I ordered this offer, there's a relationship there and a relationship there. And now we have, like, this giant rectangular flow thing going on. At least that's how I'm picturing it in my head. So with that in mind, let's get to work on actually building this thing out. The orders are going to have, let's see, they're going to have an offer ID. Offer ID, seller profile, and probably like some form information, form data. I don't know what that's going to be exactly yet, but we'll flesh it out. Right? Okay. So let's dive into actually build this application. Alright, I'm going to pull this up side by side. On the setup side, I've just got a Nuxt application we'll use for the front end here and then I've got a blank Directus instance that we're going to start building from. So, like I said, we get users out of the box for this. Maybe we go in and we create a new role for a user. They don't have app access but we'll give them access to some collections once we create them. But let's dive in and create our first collection. Let's just call it, our seller profiles, or it could just be profiles. You know, it could be, like, we have a buyer profile as well. You know what? Don't second guess it. Let's just roll with it. So we've got a date created, when this was updated, who updated it, the status of this, is this a published profile or not. That's great. We'll dive in, we'll add a name for the profile, we'll add a location. I I think this is honestly just like a, maybe it's like a city and a region and a country just so we could, I mean, we could potentially filter on the country if we wanted to. That gives us enough information. All this stuff is being delivered remotely. And then we have a description. So description may be HTML content. We want it to be rich, we want people to be able to stylize a description, maybe include some images. So we're going to use our WYSIWYG editor in Directus. Alright, so that's our seller profile. It's great, I can go in and set up my seller profile. This is Bry Ross. I am in Bluefield, West Virginia in the United States, just the US. I paint beautiful directus themes for you. Great. We may even have a image for that. Right? We've got to have an avatar for our seller profile. We probably have that on the user level as well, but you know, maybe we want to keep that user data private from the general public. So we can attach that to our seller profile and then attach that to our individual user. Great. Alright. So let's just search for Brad Ross, see what comes up. That's a great snap. That's what we'll use. Away we go. All right, so we've got our seller profile, let's start connecting that, right? That's going to be connected to a user, so we can build in a many to one relationship to that. And honestly, we probably will want to add that relationship to the user as well. Let's go for a one to many setup. This is going to be a user. Got the Directus users as the related collection, and the seller profile is going to be the foreign key. We'll show a link to the item. And we're going to make sure this value is unique. So inside Directus, we don't have a one to one relationship, you have to model that using many to one and one to many relationships. But you can achieve the same effect by saying, hey, this value has to be unique. So you can see that we are going to create a new profile. Great. Let's see. We've got our user. If I go to our system collections, we go to direct us users, I should now also have a seller profile that we can potentially link to. All right. So I've got that relationship set up. Let's go in and create a new orders table, and I'm just gonna leave that blank for now. I'm just gonna get that off of my mind. And then we're gonna do offers. Alright. So within our offers we're gonna have the same kind of thing. Who was this created? Is this offer published or not? And potentially within a profile maybe there's some offers that we want to sort by. So we can add all of these system fields. You can also just go back and delete those later if you don't need those things. So we've got a name for this offer. We've got, we're probably going to need a slug for this offer. And this is an extension that I've installed called the WP WP interface or WP slug extension. It just allows you to add a nice looking slug and you can auto generate this on Create. It doesn't work via the API, but for any heavy lifting inside the admin here, inside the Data Studio, works great. Alright, so again we've got like a rich description for this specific offer that we want to have and then you know, we probably have some packages, right? So what are the different levels of this specific offer? You can see here we've got like basic, standard, premium, etcetera. Let's go in and work on that. Right? To me, that would be a one to many relationship because one offer could have many packages, a package is only going to have one offer. So these are going to be constructed on an individual level, on an individual offer or product. I'm not sure what Fiverr calls these behind the scenes, but this is what we're going to roll with. We will model this out, we'll get the one to many relationship, we're going to call these our packages or it could be versions. Again, we're not going to get carried away with it. What if we call the Related Collection Offer Packages? So you can see I don't have this collection yet. And basically what Directus, the APIs are doing behind the scenes here is just mirroring all these changes to your SQL database. So that's really nice. We've got the offer is going to be our foreign key And I'm going to dig into the advanced field creation mode here. So if I go to my relationship, you can see that Directus is going to create these tables and this field for me, which is really nice, right? I don't have to have that table existing first. Directus is just going to create it for me. It makes things simple, especially when I'm just prototyping here. So now I've got packages, and if I back up, we can see that we have offer packages. Those are probably going to be a hidden collection. I don't really need to show those. And then we can actually work on the details of the package as well. Right? So we've got a title for the package or name. Package name. Sometimes when I'm working with deeply nested relational data, it helps to prefix like the name or title with the actual name of the collection, just because if you're if you're working with that data in a nested structure, it could be hard to know what the name pertains to, if you don't have some other type of identifier within that. So we have a package name, we've got a, like, a delivery window or something, delivery time. I see that's like 3 day, 5 day. We've got revisions. Are we really concerned about that? No. We've got like a short description. And in this case, it probably looks like a text description. We'll call it, you know, I could potentially call it short description or let's just call it description. Great. And then we have the deliverables. Right? So this could just be a list of things. The Repeater interface inside Directus is kind of nice for that, so that's what we'll use. I can just store this data as JSON information. I just want to store a list of deliverables. Deliverables. And then we have the title, the item. That's fine. That's what we'll call it. We'll do full width. We'll just make this a simple string, keep it easy. We'll make it an input. And I could do like an icon, but these are just checkboxes here on this site. Great. Alright, so we've got our offer packages, that's linked to an offer. Okay. And now on our order level, we need to go in and flesh this out further. Alright, so we've got orders, we've probably got a timestamp for the order. We'll just call it that. Or we could do created at or I think the standard format we use is date created. Maybe we use time stamp. It's fine. I can always rename those. And then when I go into the advanced field creation mode, I can set this up so that when an item is created inside this orders table, it will automatically save the current date and time. Okay, great. So the timestamp is saved. I can also go in and prevent this from being edited if I need to. That way inside the UI nobody can adjust the timestamp of this order. Alright. So a couple of things. The order is going to have a user or customer, you know, let's just call it the direct us users. Great. Alright. And then we have what? We have an offer for this and then we have the package. Right? So we're going to have both of those. We have an offer, that's going to be offers as the related collection. We'll save that many to one relationship and then we'll have a package, offer package, just so we make sure we get the correct package from that offer as well. Offer packages. Cool. And then, you know, as far as, like, a form after that, you know, maybe we've just got, like, some JSON data that we render. I'm imagining that on the backside of Fiverr, it's like a dynamic form. I could build my own form. So in this case, maybe we just, form data. Oh, you we just we just create, like, a JSON field that we could automatically populate data to. Hit save. We'll add a template for it. Okay. So that's what our orders look like. I would probably need a an amount for the order. Let's just use a, decimal. Rate. I could go in and flip this around. Maybe we've got 2 decimal places. This could be carried out to, I don't know, 10 places. And I could even go in and get fancy and add, like, a dollar sign or something within the interface. Pretty pretty simple. Okay. So offer user amount, form data, that's great. What else was the other thing that I wanted to do? On the packages, we forgot to set a price. Alright. So we need a price for this package. Let's use a decimal. You could store this as an integer as well. That's what Stripe does, keeps things simple. But, I like decimals displaying nicely inside the UI. Alright. So we got our seller profile. Let's create an offer. Right? Why is our offer blank? Oh, some kind of extra table. Okay. Alright. So we've got offers. Right? Great. We're gonna I will create a Directus theme for you. We'll go ahead and publish this. One thing that I can already tell that we're forgetting is like a gallery for this specific offer. So let me fire up chatgpt. Let's get a nice formatted description here. Create a rich text description for a Fiverr like gig called I will create a Directus theme for you. Alright. So while that's working, let's set up our different packages, right? This is the basic package, the delivery time is 3 days. We'll create a simple theme. We've got some deliverables. This is, one theme, 2 revisions. Great. Alright. The price for that is 199. And then maybe we add another one. Right? So I can also go in and edit this raw value if I wanted to. We'll just go in and add another package. This is the premium package. This takes 5 days. We'll create the most amazing theme you have ever seen. One theme. Six revisions. That's a lot of revisions. Let's just dial it back a little bit. Way more than what I would actually go to. Here's our description for this and then, you know, hand holding and support. Or let's call it theme installation as well. Cool. Alright. We'll copy this description. Paste that in there. Save. Now we've got our offer. I need to add some images to this as well, right? So for our offer, maybe we want to show an image gallery. Maybe we have one, there's a featured image that's going to be like a card for this. We'll save that. And then we could potentially also have multiple images. And we'll call this the gallery, image gallery. Let's just call it gallery. That's great. And what in this case, what it does is it creates a junction collection, ties that back to our Directus files collection, which is basically your file manager inside Directus. Alright. We've got to have a sort field for those so we can control which order those display. And away we go. Right? So now we have a featured image and a gallery. We can upload a feature image. Let's just make this Bry Ross. And for our gallery, let's see if we have, like, some screenshots. Yeah. We got some past screenshots here of a few different themes or just one theme, actually. And now we've got an offer. We don't have any orders. We've got a seller profile. Cool. Cool. We've got the offer. We don't have that linked to our seller profile yet, right? So let's do that as well. This is going to be a mini to 1. This is going to be the seller profile. We'll pick that seller profiles collection. And I'm going to open up the advanced field mode here. We're going to add, just this corresponding field in the table Seller Profiles. So we're going to add the reverse relationship there just so we can see that as well. So now if I go into my seller profile for Bry Ross, I should be able to see my offer, but I haven't linked that specific one yet. So we'll go here and pick Bry Ross. There we go, he's got an offer on the marketplace. Now if I go back to Bry Ross we can see that actual offer, Vice versa, if I am on the offers I can get to the seller profile. Alright, so that is a lot of data modeling. We've eaten up about 20 minutes of time just fleshing this out. Let's actually start building something. Alright. So we'll just start inside the Nuxt application. I'm going to pull this up. Actually it might be handy just to have this kind of sitting here as well. So on the index page, right, we want to get a list of our offers. We can do that. I've got a Directus Nuxt plugin here that has some credentials already set up. We've got real time, let's say we wanna view a list of offers. I can do this, I'm gonna access that Directus plug in. So we'll just do Directus equals use Nuxt App. So I'm going to call the Directus client here and then we're going to use the Nuxt Constant Data. They have a Use Async Data composable that makes this process easy. So we'll just do offers slash index, give this a key, and then we are going to add a function just to call this data. So we'll do directus dot request, we're going to do read items and we want to pick up the offers collection. We'll add some filters for this. Maybe we want to do the filter equals status. We want that to be equal to published, so we only want to get the published offers. And what else are we going to add to this? We need a little bit of help there. Am I missing What am I missing? Got too many. Not enough return. Directus offers. Got one more, and we have one more. Use async data, offers index, data, and the mesonoma syntax here. Request. Okay. Stuff that in there. There we go. Alright. Got that fixed. Now we are actually reading this data. Let's just remove this. We're going to wrap this in a pre tag just to make sure we're actually getting some kind of data from this application. Alright. So what I don't see is any actual data here. And if I take a look, we can see if we're getting any API calls. No calls are going out. Number 1, I have to import that read items method from the SDK. Now we can get something going here. On the server side, no, not seeing it. One of the potential reasons there is we are not logged in, right? So we created this data model, but Directus gives us a couple different roles for our users or our access control out of the box. By default, the public role is what users have access to when they are not authenticated. So anybody can access this level of information from the API, which is always set to 0 out of the box. So maybe we want to show our offers, when I'm getting ready to move into production I would strictly scope this down. Maybe they don't need actual order information, but as far as the publicly displayed information, we're going to show that. And now I can actually see that. Let's just sort this out here. Okay. So we've got like a login and register buttons. We don't really need those. Here is our offers. Right? I could drill into the individual packages here, but just to display a list of cards, I don't really need any of that. So we'll just do a quick h two, we'll do some offers, set up a grid. How many do they show here across graphic design? Maybe I have 4 up Grid calls, 4. Great. And then we've got, let's just add a card component. The offer for I don't really like any of this. Let's destructure this. This is gonna be our offers. Offer and offers. Key. Great. Now within our card, we'll just flesh this out a little bit. We'll wrap that in a Nuxt link. So we just want to do 2 offers. I'll have to create a route for this in just a moment. Offer dot ID, or, you know, it could potentially be the slug on the offer if we want. Let's roll with ID just for now. Make things a little bit easier. And just close this tag. Great. Okay. And then within the NUCs link we have, what, we've got an offer name. So this could be an h three. That's gonna be an offer dot name. Jeez. I can't actually type. Then I have a p for the description. Maybe we wanna strip some HTML. Do I have inside my boiler plate here, I should have just a function to strip HTML tags out. So I'll do that. We'll strip HTML. Great. And, let's add an image for this as well. Right? So we'll do image. I'm pretty sure I've got this set up with Nuxt Image as well, which is just a replacement for the image tag. Allows you to take advantage of, like Sharp library to optimize images. So we got offer dot featured image is the wording we're gonna use here, the offer dot name. Let's see what we've got. Right? Okay. So we've got a card, we've got grid calls for, left off an s. That's good. Let's make the font bold for this. Text a little larger, 2 x l. This is way too long as well, so let's truncate the string after we strip all the HTML. Maybe we limit to like a 150 characters. Much better. Add just a bit of padding. And I think the Nuxt UI library has this U container component we can use to just add that padding to it. Alright. So we got an offer, great. Let's go in, we'll click on that offer, the offer is not found. We need to set up a quick route for this offer, we'll do that. First I'm going to create just a folder for this, Nuxt does file based routing, which is really nice. And here we'll just do the ID dot view and I can copy a lot of this to carry over. Alright, so instead of Read Items, I'm just going to Read Item. Now if I was using the slug, I could change my filter to be the slug here. We're going to end up using the route, we need the route params. So we'll just use route. And here, as far as the request, we're gonna change this up just a little bit. This will be the offer maybe change this to show the offer ID. Still talking pop up? Yes. Still talking. Okay. Offer dot ID. I will, baby. It's the joys of working from home office. Right? Okay. So we'll log in. We've got the readitem. Readitem accepts 2 arguments, or actually 3 arguments. We've got the collection, we've got the item ID, and then we have a query that we can use to either return certain fields or not. But in this case, all I'm looking to do here is just grab this ID. Cannot access offer before initialization. We don't need any of that. We're not even going to need to wrap this. Right? Offer dot image, This is gonna be the offer dot title. Do we actually get the offer here? No. We're missing something. Gonna read the author, read item, cannot access offer before initialization. Oh, the the route dot params dot ID. Can't use the offer that you don't have access to yet. Okay. So there we go. We are working with an offer, author, offer. I keep getting those mixed up. I'm just gonna go back to this one. I'm gonna drag it over here. I just arced myself, didn't I? As far as the web scraping profile. Okay. I've got this on a separate screen that you can't see at the moment. I'm just going to do, like, some quick formatting for this. U container, grid calls. Why does this look this way? Probably because we have it in a grid. Set this up. We've got the offer title. Oh, should be the offer name above. That'll actually be an h one tag in this case. Not that you're concerned about semantics on this particular episode. Alright. So we've got an image, we've got the description, and in this case, what we're going to do, instead of truncating that, we want to show the whole thing. So we'll do vhmml equals offer dot description and because I've got tailwind installed, I'm just going to use the pros class. We'll use, like, a large text for this. And boom, there we go, we got that. Now, on the right side of our page, we've got, like, the offer details, right. But what I don't have in this case, if we just log this data out again, Alright, if I take a look at it, I don't have my package information, I don't have the seller profile that I want. So how can we actually fetch that information? Directus makes this really easy. We'll just use a fields parameter in our query. You don't have to, you know, this is available through the regular API, syntaxes. It's pretty much the same across the board here. You don't have to be using the SDK to access this information. So, I don't recommend doing this in production, but because we're just bumping through here, we will use an asterisk to get all the root level fields on this. What else do we want? We want the packages. We'll get all the fields on those packages. And then for the gallery, let's get the next level fields for the gallery. And now we can see what we're working with here. So we've got a gallery of images. Cool. We've got access to that information. Okay. So I'll just leave this up. That's cool. We want to maybe wrap this in another div. We're gonna maybe flex this container. This will be I mean, it it could even be a grid at this stage of the game. Let's make sure we are we're only targeting on, like, a small or a larger screen size. We'll have, like, a I don't know. Like a 3 column grid. This will be call span 2 columns. See what that looks like. Do I have this running now? Okay. Great. We'll just hide this pre offer. And then on the next one, this will be class call span 1. And here's where we're gonna put our packages. Alright. So packages are gonna go over there. Obviously, we're gonna add some nice gap for these just to clean this up a little bit. And, again, this is this is fairly rough. Right? If I want to just move this in here, you could see what do we have as far as our packages? Just gonna throw that down there. Alright. So as far as our packages, we've got these shown in a couple tabs. Let's look at ui.nuxt. Do we have tabs here? Yeah, there's like a tabs component. Okay. Tabs equals items. Alright. So let's start flushing this out. We got tabs. We got items equals, let's just write like an inline function. This is another case of I do as I say, not as I do. But let's actually map these packages out. What have we got here? Right? Offer dot packages. We're gonna map these to the actual items. What what shows up in the tab here? The content is x. Does this have, like, a oh, it's got, like, a slot that you can use. I to me, this feels, like, really kinda heavy handed, but, yeah. Whatever. Let's roll with it. Packages dot mat. Actually, let's not. Let's just like flesh these out real quick. Divv4. We use, like, a selected package. Selected package. That'll be what's offer dot packages offer.value.packages. And we'll do the first one. Alright. So that's gonna be our selected package dot ID. Let's call it selected package ID. Good enough. Alright. And then we're just gonna loop through these packages, offer dot packages, offer in offer dot packages. The key here would be key equals offer or, actually, no. We want package Package dot ID. Okay. How are we doing on time? We'll check this. We have got 20 minutes left. Package dot ID. Alright. And then within the package, we are going to have the package. Name, package dot name. Let's just see what this gives us. Then we have a list of deliverables. V 4 package alright. Deliverable item in package dot deliverables. Key equals we'll just call that the item, and we'll do item dot item. K. Item dotitem. K. What else do we have? We have a short description for that. That'd be a p tag. Package dot description. Alright. And, oh, transform failed with, like, 5 things. We've got just too much going on there. There's our packages. Those are not showing. Offer in packages. Package in offer dot packages. Key equals package dot ID. Offer package, oh, package underscore name, package description. Oh, we can't use package. That doesn't make a ton of sense, but, it does make sense. Pkg offer dot packages. Should learn that a long time ago. Item in pkg. Cannot read properties of dot packages, null. Offer dot value. Okay. Alright. So there's our different packages. We probably got a price for that as well. Let's take a look. Cool. I was in a div.flex p dollar sign pkg.price199. Okay. Package dot. It's actually package name. Okay. How are we doing on time? Probably not good. And then we'll just justify between. Okay. And what else? So we got our packages. Let's wrap this. Actually, we wanna do this. We'll do wrap it in a template tag. V if, we get selected packages, selected package ID, offer dot value, unref, offer packages, 0 dot idvif, what, selected package ID equals package.id. I can't read no packages. So this doesn't exist yet. Select the package ID. Offer dot packages. What are we gonna do with that? Let's just leave that. Null for now. That'll get us back to rendering, but we don't really render anything. And we do if offer what's that gonna do for us? Can't read, undefined, offer dot packages. Oh, did I even have that correct to begin with? Unref offer dot packages dot zero dot ID dotvalue.packages.id. Okay. Anyway Alright. And then what we're gonna do, we'll just add a list of buttons here. Div. Flex those. Add some gap to alright. So we'll do buttonv4, package and offer dot packages. And package dot package name. Alright. So we got those 2. And on the click of the button, that click, we'll do selected package ID equals pkg.id. No. Why is it doing that? Let's see. Expanding this when I don't want it to. Thank you, GitHub Copilot. Alright. So there we go. We can switch between these two packages. And then for each package, maybe we just have a buy button for that as well. Let's see what this looks like inside, Fiverr. They kinda send us out where you can order the quantity, you can change, you can add on, things like that. Not super concerned with that. I'm actually going to just totally cheat here. We'll open up my old faithful Stripe account, log in, Go into test mode for this. Just create a new product. We'll call this the theming package, theming offer, direct to themes, buy Bry. Great. We've got more pricing options. This is gonna be a one off. We've got flat rate, customer chooses price, price by package, or bundle of packages. Let's do a flat rate. Hit next. What did we charge for this? 199 for the one package. Do set. Add another price. This will be the 299 for the premium package. Great. Billing period. That's a flat rate. Great. Alright. So we got 2 potential options there. We'll add this product. Of course, I gotta log in. Alright. We're cooking. We're cooking. We got 15 minutes left. And now direct this theme is by Bry. You know, normally, like, taking the time to go through this, I would certainly go in and then, just basically, like, have this created through a Stripe checkout. So we got a payment link that we'll create for this. Great. We'll create this link, copy this, and if we go into our direct assistance where are you, mister directus instance? Admin@example.com. Password. Okay. Alright. So for each one of these packages, offered packages, let's have a buy link. That'll just be a string. We'll serve that on the front end inside a button component. That looks good. Alright. Offer packages. So let's set this up. Right? This is our offer. Where's our packages? Here's the 199 package. There's the buy link for that. Lost my Stripe payment link. We'll create a let's create another payment link real quick. Where are you? Directus themes by Bri. We need a payment link for this one. Create the link. Great. Copy that. Again, this is the quick and dirty way of doing this, but sometimes you got to. Alright. This will be 299 for this specific package. Save. Save. Save. Save. If we look local host, now we should have access to that data. So we can see those prices there. And then inside each one of these packages, after our deliverables, we'll just add a button. We'll add a 2. This is gonna be, package pkg.buyurl. Is that what I set that up as? By link. By underscore link. By pkgpackage_name. And by premium, this should redirect us. Right? Redirect us to direct us. Yeah. Nothing fancy here. Alright. So if I hit now, I hit buy premium. We've got what? 13 minutes left? Buy premium. I go in, I check out, that's great, we're gonna have to fill out this information. But one thing I wanna do here afterwards, right, is after they pay for this, we need to send them to a form. Now if I'm using the Stripe API endpoint, I can set up a referral URL after I check out to redirect. Not going to do that here because I don't really have time to set up all those specific endpoints. Again, we're gonna go quick and dirty mode. We'll go to webhooks. Let's set up a webhook for this. Of course, this is gonna be in my local environment as well. Okay. I gonna cut this close. Right? We'll go into flows. Let's create a new flow. We'll call it catch stripe. Webhook. Catch stripe will trigger on a webhook. Incoming webhook, it will be a post request. What are we going to do when we catch that? Are are am I I I'm I'm getting way ahead of myself. Like, we could catch this and do an email or something like that, but let's just say we yeah. It let's let's do something more interesting. And, switching gears here. Let's just assume gonna make a bunch of assumptions here. I'm just gonna go ahead and check out in this process. If we go back to Stripe where are you, Stripe? I think within the actual payment links, can we do this by, let's say for the premium link, after payment we are going to just redirect. We're gonna go to http local host 3,000\u002Fsubmit\u002Foffer\u002Fsubmit. Offers slash submit. Alright. So we're just gonna create a new page. This will be submit dot view. We'll take this information. Submit dot view. And what are we gonna do with this? Right? We're gonna make sure we still let's let's get our offer just to show that up at the top. And then we're gonna build a form to actually submit some data or actually create an order. Again, normally all this would be via webhook and either flows or like a specific endpoint, but this is what we'll do just to throw something out with our remaining 10 minutes. Right? So we've got our freelancer profile. We've got an offer page that customers can buy. So we check the box on that functionality. Technically, I guess this is a win. Is it, a smooth win? I don't know. And then we just want to, like, submit an order form. Alright. So how do we do that? We will do create item. We're gonna need read item. We still wanna pick up that offer. And let's make sure this submit isn't gonna actually work on our front end. Got too many links going on. Local host 3,000 slash offers slash submit. Cannot read properties. Offer oh, I don't have the route params. So let's drop that. Let's move offers underscore ID. We'll make that a folder. This is gonna be dropped into that folder. So it's submit. And this is gonna be index. Alright. So that adjusts things a little bit. And now offers route params.id. Oh, becomes let's go back to the index page. We go here now. Actually, that's gonna be broken. And it no. That won't be broken. Shouldn't be broken. There we go. Alright. So now I've got that, and then we'll just have a submit page. There we go. We've got that. Let's add a h two tag. Thanks for your purchase of this package. Great. And then we've got, like, a form. Alright. So u form, and what are we gonna do with this form? We are, what, we're going to submit this, we're going to need a function here, async function. Submit order. K. We are going to wrap that in a try catch. We'll do constant order equals await, direct us create offers, create orders, create item orders. We have the package, selected package dot ID dot value. Let's not worry about the package at this moment. Then we've got form data, which is just gonna be a JSON object. What else do we have inside this Directus instance that keeps magically getting logged out? 7 minutes, offers, orders. Alright. So we had the user. We had the amount. That's gonna be the selected package. This is probably actually backwards. Right? Maybe we should have submitted this form first and then redirected to checkout after they submitted this form. But, hey. Nobody's perfect. Alright. So form data. What do we want inside the form data? We don't really have to worry about that at this moment. Then we'll just add one more. There's our submit order function. Will this create an order? We can console. Log the order after we create it. But let's just build a quick form that will actually submit this. Alright. So we got you form group. What do we need to actually build a theme for someone. So we need the website URL. We'll have a input for that. V model equals form dot website URL. Actually, that'll be form under underscore data. I actually need a form here. Form, let's make this reactive. And, honestly, here, this will be form. Alright. So form dot website URL. Great. Make that required. And I think that actually goes here on the form group. Again, just rolling off of the Nuxt setup. Label, what else do we want? Form. I got like 35 things going wrong here. Theme, colors. Maybe we add, like, some kind of extra helper text within this. What is the color palette for this theme. Okay. Let's see what this actually looks like. Okay. So we got like a we've got an actual form rolling here. Add some spacing. Add a submit button, new button. Submit form. Okay. Also, let's make sure that you have to be logged in for this. So we'll do constant user equals use state user v if user u form v else. Oh, no. I don't need this. Div v else. Please log in to submit the form. We'll do u button. Ah, boy. Actually, it would be just 2\u002Flogin. No. It's auth\u002Flogin. Log in. Alright. Are we actually logged in? I guess I am logged in because I'm sharing a session token here. But if I were to like, go here and look at this, right, it's not gonna show me that because I'm not logged in. And then I could log in, and then I could go back and potentially see that offer or submit. Right. So now that I'm logged in, I can see this information. But we are running dangerously low on time. Let's make this a text area. And let's just run through this. Right? We have purchased something via the offer page. We're now submitting this data. What a mess this one's turned into. But we'll do the directis. Io, violet, white, shades of pink, slight gray. Cool. We hit submit form. What happens what happens if I hit submit form? Nothing happens, because I didn't tie that to any actual button. At submit equals what submit order. Submit order. Submit order. I should be seeing a fetch request. At button oh, it's a click handler. At submit would have been on the form itself. We'll save. Website URL, httpdirectus.io. Okay. Do we wanna track, like, a success state? Success equals ref false. I love cutting this down to the wire. Right? Console log order, Success value equals true. And then if the and no success. Maybe we want to show a success message. The if success. We do yes. Okay. What are we looking at here? Why is it not logged in? V else if v else if. V else. So I fix it. Okay. HTTPS, 1 minute 25 seconds. What happens? Nothing is happening. What did I do? What happened to my click handler? Disappeared. Submit order. I got the wrong I put it in the wrong spot. Okay. Let's try it again. Refresh. Httpdirectus.io. Where is my success with 48 seconds left to spare. Has this actually gone through? Has it submitted an order? Can we actually look at it? Do we have an order inside the system? We do. We don't have the offer and the package in there. So the offer equals the value dot author dot ID. If we go back and we check that just one more time, Submit another order to this. Will that now have the there's the offer. Again, like, we could have this all set up and routed correctly with enough time, but what a wild ride that was to try and get this information actually into the system. So wow. Where would we take this from here? Right? I would go through and set up this logic to be very robust. Again, as far as the offers here, I would probably I don't know that I would create products inside the Stripe account for each one of these offers. I would just set up, a checkout to Stripe that would take the order value or the order amount from a cart, throw that to the Stripe checkout, they would check out. Stripe sends a webhook. We also redirect them back to a page where they can fill out the necessary information, on a page like this to give us the order information that we need, to actually complete that order. So we can see that information here. But that's where I would go from here. Obviously, I would do a little a few things different. If we had to add more than an hour, probably 2 hours here, we could have had a really nice application. But bare bones, this is pretty good. That's it for this episode of 100 apps, 100 hours. I'll catch you on the next one. Thanks for following along.","b22832b2-eb0b-46b5-9a16-84daed24723d",[2959],"75b51457-11d9-4c01-b003-11ae478ae94c",[],{"year":978,"number":2062,"id":2311,"episodes":2962,"show":2963},[2313,2314,2315,2316,2317,2318,2319,2320,2321,2298],{"title":322,"slug":323},{"id":2756,"slug":2965,"vimeo_id":2966,"description":2967,"tile":2968,"length":11,"resources":12,"people":12,"episode_number":527,"published":2969,"title":2970,"video_transcript_html":2971,"video_transcript_text":2972,"content":12,"seo":12,"status":19,"episode_people":2973,"recommendations":2975,"season":2976},"walmart-theme","931473260","Join Bry as he brings the essence of better living to life with a happy little Walmart theme. Grab your theming brushes and following to turn savings into art.","b0bf0366-015c-43a4-a6f9-267366bd3bbe","2024-05-20","Walmart (Blueprint for Savings)","\u003Cp>Speaker 0: Oh, hi. How are you? Welcome back to the show. I'm your host, Bry Ross. I was just taking a look at some of our fan mail.\u003C\u002Fp>\n\u003Cp>Got a couple questions from some viewers here and maybe we'll just start the show by reading those. Dear Bry, I wanted to know if your pet squirrel, Peapod, could paint themes as good as you can. Love, Mary. Well, Mary, I I think that even animals can be very creative. But, unfortunately, Peapod doesn't have opposable thumbs so it makes it really hard for her to grasp a palette knife or a half inch brush.\u003C\u002Fp>\n\u003Cp>Now, she could potentially use her teeth but I've not had the time to train her for that yet. But thank you for writing in and maybe we'll get there one day when squirrels can paint beautiful themes just like the rest of us. Alright, so let's dive into our theme today. We are going to be painting a Walmart theme. So if you're new to the show I can't overemphasize the importance of having a prepared canvas.\u003C\u002Fp>\n\u003Cp>So as you can see as I set up my project settings I've got a complete data model already ready to theme. This is absolutely vital as a starting point. So we're just gonna go in and add our project name. We're going to add the project descriptor to save money, live better. Of course Walmart does it.\u003C\u002Fp>\n\u003Cp>And then we'll go in and start adding our branding. So we're gonna lift that Walmart logo, import that using the, import from file URL, and then for the public foreground we'll just use that as well. We'll go in and also just start painting the rest of our broad strokes, looking for a public background that's gonna do us justice here. Just keep keep working the canvas until we find what we're looking for. Just a little up and down there, a little wax on, wax off if you're a fan of mister Miyagi.\u003C\u002Fp>\n\u003Cp>He was a student of mine in the past, came to me for theming help and was able to give that to him. Alright, so this looks like a beautiful representation of everything that is Walmart. Who doesn't love getting bags of groceries delivered to their door? Amazing. So we'll just paint that as the public background.\u003C\u002Fp>\n\u003Cp>Alright. So now that we've got our basic appearance, let's start adding some color to this canvas. Alright. So I'm just gonna come in and pick up a couple of hex codes here. Trying to find this on the Walmart website is what we really want to do, so we'll just look for that background for that header, that's gonna be our project color.\u003C\u002Fp>\n\u003Cp>We'll call that the Royal Walmart Blue because they are clearly American royalty. Alright, so we've got that. Now I want to make that whole module bar that same color. So I'm going to go into our light theme, into the navigation and into the background. We're just going to spray on a little bit of CSS variable using project dash color.\u003C\u002Fp>\n\u003Cp>Alright, so that's great. Now we've got that whole bar fixed, but we need to fix the foreground because we want that to be white and make it match the website exactly. We want to hit that same tone. We want to evoke those same emotions. And remember, as you're theming, there are no mistakes.\u003C\u002Fp>\n\u003Cp>There's only opportunities. Opportunities to add a little more character, a little more beauty. It's all right in your hands. Alright. So now this is looking nice.\u003C\u002Fp>\n\u003Cp>We got a nice subtle hover effect when you look over the modules. And last but not least, I'm just going to paint in a little foreground active. We're gonna use that same project color just to finish off that last little touch there. Alright. So now anytime you are theming, whenever you change colors, you wanna make sure you clean your keyboard so those hex codes don't get stuck in there.\u003C\u002Fp>\n\u003Cp>Otherwise, you might be messing with colors that you don't really want on your palette, on your theme. Alright, so now let's move on to the list view for Walmart. Right? We want to control that secondary navigation there. Let's make this whole thing blue to match the Walmart website.\u003C\u002Fp>\n\u003Cp>This is the job for our trusty pallet knife. We're just going to pick this up and just flip it at a 90 degree angle. Just break that down. Just break it on down the sidebar or navigation. Alright, so we set our foreground.\u003C\u002Fp>\n\u003Cp>We're gonna set that to be white as well. This is looking really nice. Just a little more. Just light strokes. You want to use a light touch when you're theming.\u003C\u002Fp>\n\u003Cp>You don't need to force anything. Just light little strokes. Great job. Great job. Now, we're also gonna go in and paint a couple little happy hover effects in here.\u003C\u002Fp>\n\u003Cp>Looking great, so we'll just add a little background hover, make that just a hair darker and on the background active, just lighten that up a little bit, make it a little darker as well. There we go. Okay, now this is looking really nice. Just take this. Okay.\u003C\u002Fp>\n\u003Cp>Great. Alright. So one of the next things that I want to do here is just dial in those fonts. So we're gonna inspect for the font. I'm just gonna search.\u003C\u002Fp>\n\u003Cp>So we'll bring up that keyboard, search for the font within the CSS and the font name is Bogle. Alright, so custom CSS, we're gonna use the at fontface rules. Just use a little font family action there And we're going to call this Bogle. And we're going to take this URL that we've got from Walmart and just paste that in there. Just copy, oh, just do a little copy action there.\u003C\u002Fp>\n\u003Cp>Sometimes those, those URLs are tricky, They don't wanna be copied. So you just gotta approach them slowly and just paste them. Alright. So now we've got that. We can see, if the font is being loaded.\u003C\u002Fp>\n\u003Cp>I don't necessarily see it, but, oh, there it is. Sometimes we just gotta go in and paint that into our fonts before it appears. So we go into our light theme customization for the display font. We'll set that and voila. Wow.\u003C\u002Fp>\n\u003Cp>This theme is really coming together. Let's go in and just sign out and paint the final touches there. Looking really nice. Got all of our products. We're gonna sign out and beautiful.\u003C\u002Fp>\n\u003Cp>We've got a great looking login page with a smiling happy customer. What could be better? That's it for this episode of The Joy of Theming. Thanks for joining me. And remember, you too can paint a perfect theme.\u003C\u002Fp>","Oh, hi. How are you? Welcome back to the show. I'm your host, Bry Ross. I was just taking a look at some of our fan mail. Got a couple questions from some viewers here and maybe we'll just start the show by reading those. Dear Bry, I wanted to know if your pet squirrel, Peapod, could paint themes as good as you can. Love, Mary. Well, Mary, I I think that even animals can be very creative. But, unfortunately, Peapod doesn't have opposable thumbs so it makes it really hard for her to grasp a palette knife or a half inch brush. Now, she could potentially use her teeth but I've not had the time to train her for that yet. But thank you for writing in and maybe we'll get there one day when squirrels can paint beautiful themes just like the rest of us. Alright, so let's dive into our theme today. We are going to be painting a Walmart theme. So if you're new to the show I can't overemphasize the importance of having a prepared canvas. So as you can see as I set up my project settings I've got a complete data model already ready to theme. This is absolutely vital as a starting point. So we're just gonna go in and add our project name. We're going to add the project descriptor to save money, live better. Of course Walmart does it. And then we'll go in and start adding our branding. So we're gonna lift that Walmart logo, import that using the, import from file URL, and then for the public foreground we'll just use that as well. We'll go in and also just start painting the rest of our broad strokes, looking for a public background that's gonna do us justice here. Just keep keep working the canvas until we find what we're looking for. Just a little up and down there, a little wax on, wax off if you're a fan of mister Miyagi. He was a student of mine in the past, came to me for theming help and was able to give that to him. Alright, so this looks like a beautiful representation of everything that is Walmart. Who doesn't love getting bags of groceries delivered to their door? Amazing. So we'll just paint that as the public background. Alright. So now that we've got our basic appearance, let's start adding some color to this canvas. Alright. So I'm just gonna come in and pick up a couple of hex codes here. Trying to find this on the Walmart website is what we really want to do, so we'll just look for that background for that header, that's gonna be our project color. We'll call that the Royal Walmart Blue because they are clearly American royalty. Alright, so we've got that. Now I want to make that whole module bar that same color. So I'm going to go into our light theme, into the navigation and into the background. We're just going to spray on a little bit of CSS variable using project dash color. Alright, so that's great. Now we've got that whole bar fixed, but we need to fix the foreground because we want that to be white and make it match the website exactly. We want to hit that same tone. We want to evoke those same emotions. And remember, as you're theming, there are no mistakes. There's only opportunities. Opportunities to add a little more character, a little more beauty. It's all right in your hands. Alright. So now this is looking nice. We got a nice subtle hover effect when you look over the modules. And last but not least, I'm just going to paint in a little foreground active. We're gonna use that same project color just to finish off that last little touch there. Alright. So now anytime you are theming, whenever you change colors, you wanna make sure you clean your keyboard so those hex codes don't get stuck in there. Otherwise, you might be messing with colors that you don't really want on your palette, on your theme. Alright, so now let's move on to the list view for Walmart. Right? We want to control that secondary navigation there. Let's make this whole thing blue to match the Walmart website. This is the job for our trusty pallet knife. We're just going to pick this up and just flip it at a 90 degree angle. Just break that down. Just break it on down the sidebar or navigation. Alright, so we set our foreground. We're gonna set that to be white as well. This is looking really nice. Just a little more. Just light strokes. You want to use a light touch when you're theming. You don't need to force anything. Just light little strokes. Great job. Great job. Now, we're also gonna go in and paint a couple little happy hover effects in here. Looking great, so we'll just add a little background hover, make that just a hair darker and on the background active, just lighten that up a little bit, make it a little darker as well. There we go. Okay, now this is looking really nice. Just take this. Okay. Great. Alright. So one of the next things that I want to do here is just dial in those fonts. So we're gonna inspect for the font. I'm just gonna search. So we'll bring up that keyboard, search for the font within the CSS and the font name is Bogle. Alright, so custom CSS, we're gonna use the at fontface rules. Just use a little font family action there And we're going to call this Bogle. And we're going to take this URL that we've got from Walmart and just paste that in there. Just copy, oh, just do a little copy action there. Sometimes those, those URLs are tricky, They don't wanna be copied. So you just gotta approach them slowly and just paste them. Alright. So now we've got that. We can see, if the font is being loaded. I don't necessarily see it, but, oh, there it is. Sometimes we just gotta go in and paint that into our fonts before it appears. So we go into our light theme customization for the display font. We'll set that and voila. Wow. This theme is really coming together. Let's go in and just sign out and paint the final touches there. Looking really nice. Got all of our products. We're gonna sign out and beautiful. We've got a great looking login page with a smiling happy customer. What could be better? That's it for this episode of The Joy of Theming. Thanks for joining me. And remember, you too can paint a perfect theme.",[2974],"de94847e-19bc-4892-a14e-f465a0917f4e",[],{"year":978,"number":2745,"id":2746,"episodes":2977,"show":2978},[2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2731],{"title":2760,"slug":2761},{"id":2317,"slug":2980,"vimeo_id":2981,"description":2982,"tile":2983,"length":1078,"resources":12,"people":12,"episode_number":213,"published":2984,"title":2985,"video_transcript_html":2986,"video_transcript_text":2987,"content":12,"seo":2988,"status":19,"episode_people":2989,"recommendations":2991,"season":2992},"remote-job-board","936433174","Race right along with Bryant as he tackles building a virtual job board inspired by We Work Remotely. He tries to build a ton of functionality in just 60 mins – job listings, integrating categories and companies, and job post submissions with Directus and Nuxt.","c9c86637-29d7-4b38-b798-2a5a52ac2def","2024-05-17","Mission: Remote Job Board","\u003Cp>Speaker 0: Hi. Welcome back to another episode of 100 Apps, 100 Hours. I'm your host Brian Gillespie and on the show we build some of your favorite apps or some of your suggestions or just random stuff that I've come across on the Internet in 1 hour or less or get publicly shamed trying when we fail. And I fail often. That's just the rub of this format.\u003C\u002Fp>\u003Cp>The rules are there are 60 minutes to plan and build the application, no more, no less. Sweating just thinking about it. But rule number 2 is use whatever you have at your disposal. AI, GitHub Copilot, tailwind CSS, tailwind UI, any pre made components, whatever we can to get the job done in 1 hour. Today, we are going to be building a job board.\u003C\u002Fp>\u003Cp>So the giant elephant in the room in this space is obviously Indeed. They are one of the largest job boards. Yeah. It's been a while since I've been on their site. The experience is not great, honestly.\u003C\u002Fp>\u003Cp>Maybe everybody's working on a a mobile or something, but, I I think the the job board that we're gonna model after today is we work remotely. We are an all remote team here at Directus, so this speaks to my heart. I've been working remotely for, the better part of a decade now. So, we are going to model after this. Let's dive in and get started, shall we?\u003C\u002Fp>\u003Cp>Alright. So we'll put 60 minutes on the clock. Let's just talk through the functionality that we're looking for here. And As I pull up the WeWork Remotely job board, what's our functionality? Right?\u003C\u002Fp>\u003Cp>We want to, obviously show a list of jobs. In this case they are sorted by category. We want to let someone let users post a job for a fee. That's nice. And, you know, this one's got subscribers as well, which seems like something that is relatively simple.\u003C\u002Fp>\u003Cp>There's a lot of moving pieces to that. So let's add a question mark and we'll do subscribe to job posts. Now I think this one does not allow you to apply within the platform. I think it just, like, sends you off to a third party thing. So that's the way we'll play this, you know, if you wanted to build like an application processing system, you could totally do that with Directus.\u003C\u002Fp>\u003Cp>That's a lot of ground to cover in 60 minutes though. So let's take a look at this, let's flesh out our actual data model, right? That's part of the planning process, so I'm just going to drag some boxes over here inside Figma. What are we going to have? We're going to have job posts or we could call it job listings.\u003C\u002Fp>\u003Cp>That's great. Within that, we're gonna have a few different things. Alright. What do we got? We got some tags, it looks like.\u003C\u002Fp>\u003Cp>So we got a category. We got a title for that post. We've got some content, we've got a link, we've got a like, a company. I guess if you're going to have a company, that would probably be here. That'd be a separate table.\u003C\u002Fp>\u003Cp>Right? There's a company. Company has a name, a website. Name. Website.\u003C\u002Fp>\u003Cp>What? Logo. Great. K. What else are we gonna have?\u003C\u002Fp>\u003Cp>If we make it 2 subscribers, we'll worry about that when we come across it, but let's dive in. Right? This seems pretty good. We'll just draw some arrows to make it look fancy. Right?\u003C\u002Fp>\u003Cp>A company has multiple job listings. One listing has one company. Alright. Great. Let's talk about what we're working with.\u003C\u002Fp>\u003Cp>So as far as applications, I've got a direct assistance spun up, it is blank. I've got a bare bones Nuxt app that has some login and register capability. Looks like I'm already logged in though, so all good there. And as far as the Directus communication, I've already got a Nuxt plug in defined just to talk to Directus a little bit easier, make it a little less boring on this specific episode. Alright.\u003C\u002Fp>\u003Cp>So let's start by I'm going to pull this up side by side with remote. Oh, no. That's not what we want. I could spend the whole time just messing with the Arc Browser. Alright.\u003C\u002Fp>\u003Cp>So we're talking about job listings. We're going to go ahead and add that as a new collection. And I think I can zoom in a little bit to make that more visible. And then we'll look at the actual listings that we have. Right?\u003C\u002Fp>\u003Cp>One of the other things that I do often is, I'm not sure how this site is put together, but, you know, on sites that are server side rendered, you can go into the actual code and especially if it's, like, a Next application or, I'm not sure if Nuxt does this or not, but you can actually see the data that's posted. I'm not sure if that's the case here. I see Jquery. This is probably server rendered from an actual server. No big deal.\u003C\u002Fp>\u003Cp>But, just one way you can kind of see what other folks are doing on the website, always inspect the code. I live in the JavaScript developer tools console. Alright, so we're going to have a job listing. We're going to have a pretty slug for all of these. So let's see what we're going to do, we're going to have a draft and publish listings, probably got a date created, did the user created, the date updated, etcetera.\u003C\u002Fp>\u003Cp>And let's go through and model out our listing, right. So we've got a title for the listing, we're going to have that. We've got to have a slug for the listing. So we'll just do a slug for the listing. We'll come back to that one in a moment.\u003C\u002Fp>\u003Cp>What else are we going to add? We're going to have some content. I see there's tags here. I don't know exactly how they're using the tags, but it looks like the the tags get their own page. So in that case, it makes me think we need, like, a relationship there.\u003C\u002Fp>\u003Cp>Let's look at that. We'll do a mini to mini relationship because one post could have many tags and there could be many tags to other posts. Alright, So we got tags as a key. I'm going to enter tags as a related collection here, right? And if I open up the advanced field mode inside Directus and I scroll down to the bottom, you'll see that, it's actually going to create a new table for me.\u003C\u002Fp>\u003Cp>So that tags table that I don't have, it's going to create it for me, which is kind of nice, right? And I can add this to the tags, I can show the job listings on the tags that have that specific tag. And maybe we can sort these as well. Cool. Alright.\u003C\u002Fp>\u003Cp>So we'll hit save. We've got tags. We also should have a tags collection now, which is nice. We have a junction table for those as well. Then we're gonna need some content.\u003C\u002Fp>\u003Cp>Right? We need the actual, content for this. That's gonna be the WYSIWYG editor. Is that great? Cool.\u003C\u002Fp>\u003Cp>The type will be text, that's what it's going to be saved as in the database. I can always adjust my toolbar if I need to. And, what else do we need really? Apply for this position, right? This is going to be a URL.\u003C\u002Fp>\u003Cp>So apply URL, Application URL. That's what we'll roll with. We're probably going to need a category for this as well. So if I go back to all the jobs, we can see we've got sales and marketing. If I zoom out a little bit, we can see all the different categories up here at the top, Programming, Design, etcetera.\u003C\u002Fp>\u003Cp>So we're going to create a new relationship inside Directus. Let's call it category. And we will again create a new collection here. So if, even if this doesn't exist, Directus is going to create this for me, which is, you know, super. It's a really easy, super nice way to do our data modeling.\u003C\u002Fp>\u003Cp>We can add the different job listings to the categories if we want to. In this case, maybe I don't need to show that. But we will, we don't have anything to adjust as far as the interface because this is not created yet, but we'll just go through the process. So we've got a category for this, We've got tags. And if I go back to my data, I'll see categories here now.\u003C\u002Fp>\u003Cp>So we've got categories, we've got tags. Great. What else are we missing on this? Is this a featured post or not? I'm assuming that's part of the checkout process.\u003C\u002Fp>\u003Cp>So, you know, we could add a Boolean tag for that is featured. I always like when I'm doing my keys, especially if they're Boolean prefix with the is. There's just a nice little standard naming convention. And on my interface, maybe the label is featured post. Great.\u003C\u002Fp>\u003Cp>Okay. Alright. So this looks pretty good, I want to say. Alright. We'll make some of these things half width.\u003C\u002Fp>\u003Cp>Maybe we make the title full width. We got the status. Actually, let's do this. We got status, we got title. Let's put slug down below title.\u003C\u002Fp>\u003Cp>And don't sleep on the Directus marketplace. Right? One of the extensions that I've been using a lot here lately is the WP slug interface extension. If you're used to WordPress, you got, like, this nice little slug interface there. We're just gonna install this, give it a quick refresh.\u003C\u002Fp>\u003Cp>Then I go back to my data model, I go to my slug, go to the interface, and I can switch this up to my slug. I get a template for this, so we're going to populate the title. I could potentially prefix this or, you know, auto generate it as well. So I'm just going to do On Create because after a thing is created, it's been published, changing the slug results in a different URL on your front end, could result in some nasty 404 errors. We don't want that.\u003C\u002Fp>\u003Cp>40 fours get out of here. Alright. So we got a job listing category. We've got did I\u003C\u002Fp>\u003Cp>Speaker 1: do job listing instead of job listings?\u003C\u002Fp>\u003Cp>Speaker 0: I did. Man. Alright. How can we fix that? We're not going to bother with it.\u003C\u002Fp>\u003Cp>I could dig into the SQL and adjust that if I needed to, but we're not going to worry too much with it right now. Typically, I keep my table names plural. Just a personal preference. Alright. So we've got a category.\u003C\u002Fp>\u003Cp>Let's go ahead and flesh out these categories. We need a title for the category. We probably need a slug for the category as well. So I can go back into my job listings, and if I want, I can duplicate this field to that collection. So we'll just change this to slug.\u003C\u002Fp>\u003Cp>We'll go to categories. Great. And in that case, do we actually want the tags to have their own page as well? The tag will have a title and the tag will probably have a slug as well. So we'll just copy that across.\u003C\u002Fp>\u003Cp>Slug. Change the collection to tags. And away we go. Right? The last thing that we talked about is a company.\u003C\u002Fp>\u003Cp>So we want to be able to select a company for our post and you know potentially a company could post 3 or 4 different times. Maybe they're hiring for a lot of different positions. So let's create a new table. We're going to call it companies. We'll do a generated UUID.\u003C\u002Fp>\u003Cp>Do we need all this information? Probably not. Alright. So we'll give the company name. And whatever I choose to name this key is totally up to me.\u003C\u002Fp>\u003Cp>And then we've got the website. That's just going to be a string. And then we're going to have a logo for that. Right? Logo.\u003C\u002Fp>\u003Cp>Alright. Cool. So now we've got it's just a basic data model set up. Let's go in and I'm just going to, like, just rip some of this content. We work remotely.\u003C\u002Fp>\u003Cp>Sorry for just sealing information off your job board here. Let's look at our different categories. So we'll just mop up a couple of these categories really quickly. We've got what? Programming?\u003C\u002Fp>\u003Cp>Oh, gosh, I can't spell. Programming, save. We've got Design, save. What else do we have? Sales and marketing.\u003C\u002Fp>\u003Cp>Sales and marketing. And what happens if we do this? Sales and? Oh, that's kind of a nice effect. Sales and marketing.\u003C\u002Fp>\u003Cp>Maybe we change this to just be sales dash marketing. Customer support. Customer support. Great. Okay.\u003C\u002Fp>\u003Cp>And this is all remote, so, you know, what are we gonna call this thing? Maybe we'll call it it, Dan's Remote Job Board after some of our own teammates here at Directus. Alright. So we've got some categories. Let's take a look at the tags within here.\u003C\u002Fp>\u003Cp>We've got full time. We've got, part time will be a tag. Right? Part time. What?\u003C\u002Fp>\u003Cp>SEO? Link building, WordPress, technical writing. What do we have on the development side? So we got programming. We got full stack, front end.\u003C\u002Fp>\u003Cp>What kind of tags do we have here? Europe only, etcetera. Hang on just a moment. I got an important call coming in. Be right Alright.\u003C\u002Fp>\u003Cp>So now we're back. We've got 46 minutes on the clock. Let's dive in further. We've got some tags. We've got like Europe only, etcetera.\u003C\u002Fp>\u003Cp>Great. Okay. So, those are a few tags. We've got some categories. We've got, let's see if there's any companies that we recognize on here for full stack development.\u003C\u002Fp>\u003Cp>I don't really recognize any of these. Right? Let's just create a new company here. We'll call this Directus HTTP Directus dot io. And we'll add the logo for this.\u003C\u002Fp>\u003Cp>Right? See what I got handy. Directus logo. We'll get this a couple of different ways. Logotype.\u003C\u002Fp>\u003Cp>That looks good. That's what we will roll with. We got the logotype. Maybe we add another company, but for now let's just add our first listing. Right.\u003C\u002Fp>\u003Cp>I'm just going to steal this one, full stack. Alright. We are going to make sure this is published. Is this a featured post? Looks like it is.\u003C\u002Fp>\u003Cp>Alright. We'll go in. And I could just copy this wholesale. And I'm thinking it should retain some of the content, some of the settings. Yeah.\u003C\u002Fp>\u003Cp>Okay. So it should retain the formatting for it. And then we're going to have a link for the application. And the last thing that we need to do is pick a category. And we probably need to add a company for this as well.\u003C\u002Fp>\u003Cp>And I I joked, it's not the last thing we need to do. We're going to add a few more. This is going to be a Europe only job. Be a $100,000 or more USD. That's that's kind of a nice tag.\u003C\u002Fp>\u003Cp>Let's do React as a tag. Oh, sorry. Creating an item in a tag. React. Great.\u003C\u002Fp>\u003Cp>Alright. Is this a featured post? Yes, it is. Let's save that post and then we'll go in and add a relationship to that company. So that'll be a many to one relationship inside Directus.\u003C\u002Fp>\u003Cp>We're gonna call this company because there's a single company, and then we have companies. And, you know, we could go in and add the company name here so that it displays nicely. We probably want to do the same thing for our categories, like give them a title, make sure those show on the interface in the display template. Same thing for tags. For our list view here, we're going to do the tags ID dot title and we'll do the same thing here.\u003C\u002Fp>\u003Cp>Great. Okay. So now if I open up this job, we can see our different tags for this. We've got our category. We've got the company that this is created for.\u003C\u002Fp>\u003Cp>Sweet. Alright. So now we've got a job listing. We've got some categories, we've got some tags, we've got some companies. Does a company going to have a slug?\u003C\u002Fp>\u003Cp>You know, potentially. Do they have a listing here? They do. So probably need a slug for the company as well. Slug.\u003C\u002Fp>\u003Cp>The template is going to be the name of the company. Great. Alright. So we've got our data model for this, right? The next thing that I'm going to do is just go through our access control and create this.\u003C\u002Fp>\u003Cp>So there are 2 roles that are out of the box inside Directus. I have public, I have administrator. I'm going to go through and, just basically set read access to all these. And then I can go back in and adjust this if I need to as well. So we're going to adjust the item permissions just a little bit to where we can only see published posts.\u003C\u002Fp>\u003Cp>So I'll go through listing tags, categories. I don't know the categories. Do we do publish for categories? We did not. Tags, etcetera.\u003C\u002Fp>\u003Cp>Yeah. So the rest of those are fine as is. Right? People can see all the companies, all the tags, all the categories. But if it's not a published listing, they can't see that information.\u003C\u002Fp>\u003Cp>Alright. So great. Let's go in and now, is it time to actually start fleshing something out inside our Nuxt application? Let's let's take a look at that. Right?\u003C\u002Fp>\u003Cp>So we've got here on the how do I separate these 2? Alright. Cool. So, Nuxt application on our pages directory, we've got an index page. This is probably going to be just our listing of posts.\u003C\u002Fp>\u003Cp>Right? So we can start fleshing this out a little bit. Let's remove this centering. That should move it way up here to the top. We'll just add a header for this.\u003C\u002Fp>\u003Cp>And what are we going to have in the header? We're going to flex. We are going to do Dan's remote job board. That's the name of this thing. We'll make sure it's like a mono font for Dan.\u003C\u002Fp>\u003Cp>I know how Dan likes this stuff. Okay. Let's give this header a little padding. PX6 PY. Alright.\u003C\u002Fp>\u003Cp>And we can even make this like sticky at the top if we want to. Sticky, top 0. Give it a background white. BG opacity. We do that backdrop blur effect.\u003C\u002Fp>\u003Cp>One of the nice things about using tailwind, backdrop blur, BG opacity. I could actually just adjust it this way. Let's do dash or slash 50. And cool. Alright.\u003C\u002Fp>\u003Cp>So now we've got a header. We might have some links inside our header that we'll cover in just a moment. Let's do a div to ul. Links. Do we actually need these on a list?\u003C\u002Fp>\u003Cp>Probably not. Let's do what? Nuxt link v 4, links and links, links on links on links. Let's actually call this nav links. And we'll just flesh a few of these out real quick.\u003C\u002Fp>\u003Cp>Nav links, that'll be an array equals array. To, about, contact, no. That's we want none of that. Home, we got what? Categories.\u003C\u002Fp>\u003Cp>We'll come back to it. Let's just nuke that for now. Or we could just comment it out. Alright. As far as what we want inside the actual body of this here, we'll do this in the main section, we are going to fetch our job listings, right.\u003C\u002Fp>\u003Cp>So Nuxt has a couple of nice composables for fetching data. We're going to use that here. But the first thing I'm going to do, let's import the read items operation from my Directus SDK. Nope. Directus SDK.\u003C\u002Fp>\u003Cp>And then I'm going to use the async data call, the async data composable from Nuxt. Use async data, And this handles like caching and deduplication, and actually if this is server rendered it will automatically dedupe those calls and pass that data from the server to the client so that I don't have to fetch the data twice and hit that API. So we'll give this a, let's say this is listings index. We'll give it a key and then we'll actually fire this off. And we're going to actually await this data.\u003C\u002Fp>\u003Cp>Alright. Then within here, we're actually going to use our Directus client that we've set up. We're gonna access that through our Nuxt plug in and we are going to do this. So we'll say return Directus dot request. We'll do read items.\u003C\u002Fp>\u003Cp>We're gonna read from the what do we call it, actually? Call it the Job Listing Collection. So that's going to trip me up this whole time. And then we have a query that we could set up. We could run this where it says filter status equals published, but I'm handling that here within my access control inside Directus, so it doesn't really matter.\u003C\u002Fp>\u003Cp>So as long as this is published, like if I go in and let's just say I change this. Change this. We say we maybe set this to draft, and I hit save as copy. And I probably need a new slug for this. And I would definitely make slug unique here, so let's just do that quickly before I forget.\u003C\u002Fp>\u003Cp>We'll go into the data model, we'll go to listings, we'll make sure that slug has to be a unique value. Alright. So this will be listings and then I can I typically do something like this whenever I'm building just to quickly and easily make sure I'm getting all the data that I want back from my API? Alright. So we go to local host.\u003C\u002Fp>\u003Cp>We can see we've got this job board and I need to actually get this out of the header and into the main body here. Alright. So there's our application, our job listing, basically. Alright. That's great.\u003C\u002Fp>\u003Cp>We can see our tags, we can see the company. We can't really see any of the information about them, though. Right? And if we take a look at our postings here, let's see what we've got. We've got the company information, we've got the company name, etcetera.\u003C\u002Fp>\u003Cp>So how do we go and fetch that data with inside Directus? We'll just go in here. We'll do something like this where we have fields. I don't recommend doing it this way in production, just using these wildcards. But for something like this where I'm quickly prototyping, super handy for that.\u003C\u002Fp>\u003Cp>I could just get all of the root level fields, I can get all of the company level fields, I could get the category, category dot star. And then for our tags, I'm going to get tags dot tags underscore ID. I'm going to get that field. So if I do that now, you can see I get the extra information that we're looking for. So I've expanded my relationships for tags, for the company, for our category, basically everything I need here.\u003C\u002Fp>\u003Cp>Looks great. Looks great. Okay. And I'm getting both of those things here because I am using session authentication. I've got a cookie here that's storing my session token.\u003C\u002Fp>\u003Cp>So if I wanted to, I could go into incognito window and that should solve it where I'm only getting one of those items back. Alright. So I'm gonna pull this up. We work remotely over here on the side just so I can see that. And we'll start fleshing this thing out.\u003C\u002Fp>\u003Cp>So within our listings, we've got, let's do a div. Probably add, like, a h two here. This is our job listings. Get that font bold. Text XL.\u003C\u002Fp>\u003Cp>Maybe 2 XL. Alright. And maybe we give the whole main section a little bit of padding. I I think there's a U container Nuxt UI component that we'll just use for this. I'm using uidot nuxt.com, just a Nuxt UI library, just to have something to build with quickly.\u003C\u002Fp>\u003Cp>Alright. So we've got our job listings. Let's go through we're gonna do a card component for each one of those listings. So v 4 job in listings. The key, we can use the job dot ID.\u003C\u002Fp>\u003Cp>And here, inside the card itself, let's see what we got. Job title. Is this actually gonna be what we want? What's this gonna look like? Remote.\u003C\u002Fp>\u003Cp>It's not really anything that we we actually want here. This is gonna be company name, category dot name, Category dot title. And and this could be remedied by TypeScript if I had all these things typed probably. But as far as the tags, that's going to be the title of the tags as well. Cool.\u003C\u002Fp>\u003Cp>Alright. So how can we get this to look a little more like what we have? We'll go through and flesh this out a bit. Wrap this. We'll put it in a, it doesn't even need to be a grid.\u003C\u002Fp>\u003Cp>We could just do like a single grid. Give it some gap. 6. Alright. We got some space there.\u003C\u002Fp>\u003Cp>We'll give it a little breathing room. Cool. Alright. So we've got some job listings. We're going to want to give these a link.\u003C\u002Fp>\u003Cp>Where did where did that go? Oh, just disappeared. Need to learn my keyboard shortcuts, don't I? What are we what are we doing, Brian? That did it again.\u003C\u002Fp>\u003Cp>Alright. Maybe I'll just use the cut paste here. Wrap this in a Nuxt link component. We're gonna do this, the 2 for this will be something like this, where we have listing, plus job ID. Yeah.\u003C\u002Fp>\u003Cp>Potentially let's actually do it this way, just use a template literal. Job dot slug. Wrap that, don't forget the ending tick. Cool. And now we have a link that does not work because we don't have a route for that.\u003C\u002Fp>\u003Cp>But let's see if we could show the company information as well. Maybe we put that here. It would be within a div. We got the company name. Show Nuxt image source dot company logo.\u003C\u002Fp>\u003Cp>See if we can get that displaying. Okay. Now we'll shrink that a bit. Object Oh, hang on a moment. Just go here.\u003C\u002Fp>\u003Cp>Class, object contain, make it 24 high, 24 wide. Cool. Within this, we could do, like, a flex, add a little bit of gap. And, you know, if we wanted to, we could get fancy with it. But let's, how do we set this off a little bit?\u003C\u002Fp>\u003Cp>Oh, actually, the title for the company is over here on this side in it. Alright. So go in, wrap these in a div. That should give us like some type of formatting here. And if I really wanted to make it look like the one on WeWork remotely, and I don't, we'll just add a border for this, round it a little bit, get a little bit of padding, p 1.\u003C\u002Fp>\u003Cp>And maybe we make the border pretty. Make it violet. There we go. That looks poor, actually. I don't like it.\u003C\u002Fp>\u003Cp>Alright, we'll make it more subtle. There we go. So we got Directus, we got Change This as the name. We've got our job listings. How are we doing on time?\u003C\u002Fp>\u003Cp>Time wise, we're at about half an hour, right? Alright, so we've got some\u003C\u002Fp>\u003Cp>Speaker 1: job listings. We\u003C\u002Fp>\u003Cp>Speaker 0: can see a list of those. How would we do something like a filter for these? Or we'd probably actually want to list these by categories. Right? So we'd probably actually switch up our data fetching here into, we'd probably look for categories first.\u003C\u002Fp>\u003Cp>But instead of diving into that specifically right now, let's actually get a detail page up. So we've got a route for this index page. Let's go in and add a route for our listings. Great. And then we'll go in and add a new page.\u003C\u002Fp>\u003Cp>Let's call this the slug. View. So we do have a listing there. And maybe I just copy pretty much all of this. We'll actually, what do we wanna keep here?\u003C\u002Fp>\u003Cp>Let's just keep the card component. That's got our information. Maybe we keep the u container just so we get some base styling. And if I wanted to, I can move this header component out of this and into our actual app itself or in probably like the default layout would be a good one as well. Just post that there.\u003C\u002Fp>\u003Cp>So now that will show on every single page. Alright. So as far as our job listing, we're going to fetch that data here. I'm still going to use the read items call, because we're fetching by the slug and not the actual ID. So what we're going to do inside Knox would be something like this, where we use the route composable, or use route composable to get the route.\u003C\u002Fp>\u003Cp>And then we're going to set\u003C\u002Fp>\u003Cp>Speaker 1: up a filter for\u003C\u002Fp>\u003Cp>Speaker 0: our slug, and we want that to be equal to route. Params.slug. So this should give us our listing. And then what we're going to do here is at the end of this, using the use async data and the I think it's use fetch composable inside Nuxt have an option set where I can transform this data when I return it. So this is going to give me an array and I only want the first item of the array.\u003C\u002Fp>\u003Cp>So even though this only matches one thing because the slug is unique, it still returns an array because of the read items SDK, or the read items method of the SDK. So we're just gonna transform that. And then here inside our card, we don't really need that anymore. Right? So this gives us what we want.\u003C\u002Fp>\u003Cp>Cannot read properties of undefined of slug. Why do we not have this? Right? Here's our listings. There's our slug.\u003C\u002Fp>\u003Cp>Route.params.slug. Directus.requestreaditemsjoblisting. Cannot read properties of undefined of slug. Let's just comment this out, see if the page actually loads. Yes.\u003C\u002Fp>\u003Cp>It does load. Let's figure out why we're not seeing that. So, the Vue dev tools is is really a great tool if you're developing with Vue. We will look for the slug component. Let's see what we got.\u003C\u002Fp>\u003Cp>We got listings. Oh, that's why. That would make sense. Right? Sometimes it's the typos, especially when you're building quickly.\u003C\u002Fp>\u003Cp>They get you into trouble. So here, let's just remove the card. I don't even need this Nuxt link component anymore. Okay. And within our layout, let's make it let's get some navigation so we can actually move backwards here.\u003C\u002Fp>\u003Cp>Make this a Nuxt link. Nuxt link. Change it to 2, do slash. Cool. So now I could go should be able to go back and forth.\u003C\u002Fp>\u003Cp>There we go. Go here. Go there. Amidst options next to\u003C\u002Fp>\u003Cp>Speaker 1: header slot.\u003C\u002Fp>\u003Cp>Speaker 0: What am I doing wrong there? Alright. Anyway, so this is gonna be our job. And now we have got our job listing. Cannot read properties of type.\u003C\u002Fp>\u003Cp>Alright. Let's go back into Directus. And is this because of our our access control? We've got it. Let's set it up so we can read the files as well.\u003C\u002Fp>\u003Cp>Does that give us what we want? Okay. So at least now I've got some type of card information here. How do we want to display the actual content for this? If we go into one of these, we've got, the job title, the logo here is on the right as well as the company.\u003C\u002Fp>\u003Cp>So we can go in and flesh this out. You know, we could spend a ton of time on design here. I'd rather focus more on the important bits. So let's just do a main component. This is where we'll stuff the content.\u003C\u002Fp>\u003Cp>We could do v dot vhtmlordiv.vhmml, v dash html. That'll be our job dot content. Okay. That should show. Cool.\u003C\u002Fp>\u003Cp>This is not really nicely formatted. Right? So, using Tailwind, they have this really nice pros class we could use to get that formatting. Looks pretty great. Maybe we add some padding to this container.\u003C\u002Fp>\u003Cp>I don't even need that. Strip this away or not some padding. Give it some margin. This looks a little odd here at the top. Let's remove that.\u003C\u002Fp>\u003Cp>We've got Directus is the company. And this could be another NuxLink. This could be, what, to the company?\u003C\u002Fp>\u003Cp>Speaker 1: 2\u002Fcompany\u002Fjob.company.slug.\u003C\u002Fp>\u003Cp>Speaker 0: That's what that would look like. And inside this div, maybe we just add some spacing. Okay. Alright. So now we could potentially navigate to the company, but there is no page for that.\u003C\u002Fp>\u003Cp>But here we've got our actual job posting. How do we we want to use like a button for you button. Is it the apply URL? I think I've got application URL here. Apply for\u003C\u002Fp>\u003Cp>Speaker 1: this position.\u003C\u002Fp>\u003Cp>Speaker 0: Great. What's that gonna do? Job application URL, is that what I set this up as? Let's take a look at our data model. Application URL, job application URL, that should be correct.\u003C\u002Fp>\u003Cp>And we want this to be let's make this a block class. Block. And maybe we drop that below the actual name. We don't have a description for this. It's great.\u003C\u002Fp>\u003Cp>Maybe we do this. Just set off this metadata just a little bit. Okay. We hit apply for\u003C\u002Fp>\u003Cp>Speaker 1: this position. And this\u003C\u002Fp>\u003Cp>Speaker 0: should open that in a oh, it's because I've got HRF. Let's try this. Apply for this position. That should give me this application page. Great.\u003C\u002Fp>\u003Cp>What is this going to be? In line block? There we go. Okay. So now we've got our page, we've got our company information, we've got our job description.\u003C\u002Fp>\u003Cp>We can drop another button at the bottom of this content. Looking nice. Alright. We are cruising on, like, 20 minutes worth of time. Right?\u003C\u002Fp>\u003Cp>So how can we actually create a post? Right? We've got our post. Let's go back and we'll do a new route. We'll just call it listings dot new.\u003C\u002Fp>\u003Cp>I'm just going to copy the same setup that we've got. Just moving really really quickly. Just drop everything out of here. We're gonna keep Directus. In this case we're gonna use create items.\u003C\u002Fp>\u003Cp>Create item. Great. And on the front end, if I wanted to be able to create a listing, we're going to need to add some navigation for this. So we'll just, let's actually stick this inside our header. Right.\u003C\u002Fp>\u003Cp>Where are you? That's in our default layout. So over here on the far right, we can actually flex these items, justify between. Oh, sorry. I already had the flex.\u003C\u002Fp>\u003Cp>Great. And we'll do listings dot new, post a job. Great. That takes us to the job new page. And on the new\u003C\u002Fp>\u003Cp>Speaker 1: page, why do we not okay.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So now we are going to post a job. We've got let's take our same heading here. Post this in here. We've got a create a new job post.\u003C\u002Fp>\u003Cp>Okay. Alright, so now we can start fleshing out our actual form that we want, right? So let's just do form. This could be reactive since we've got some properties here. What all do we have inside that listing?\u003C\u002Fp>\u003Cp>Right. We've got a title. You know, maybe we don't want them to actually choose the slug. We've got a category. That should just be an ID.\u003C\u002Fp>\u003Cp>We got some tags. This is gonna be an array. We got some content. That's gonna be a string. We got an application URL, and we've got a company, which is, it's actually, should be a string, just an ID, but we'll have to actually fetch that information.\u003C\u002Fp>\u003Cp>And then at the end of this, we are going to have a function to submit the form. Alright. Submit listing. And what's that function gonna look like? Oh, let's do a try catch.\u003C\u002Fp>\u003Cp>This is gonna be a constant response equals await, direct us dot request. Create item. Yes. This looks good. Form dot title.\u003C\u002Fp>\u003Cp>Okay. Alright. GitHub Copilot. I got you. And then what are we gonna do?\u003C\u002Fp>\u003Cp>Console log response. And we could send them to like a thank you page or something. Maybe we are tracking like a success state here. And we set success value to true. If there's an error, we're going to log that error.\u003C\u002Fp>\u003Cp>Great. Cool. Alright. So we got submit listing. Now we need to actually build a form.\u003C\u002Fp>\u003Cp>Right? Inside this Nuxt UI library, I think there is a, let's just dive into it, form component. You can even add validation with this using, like, Zod or something like that. But, we've got our form wrapped in a form. There's a form tag.\u003C\u002Fp>\u003Cp>We've got at submit. Let's do dot prevent. Submit listing. That's going to be our form handler. And then inside the form, each one of those gets wrapped in a form group, where we got a label equals title.\u003C\u002Fp>\u003Cp>Placeholder equals, amazing Directus developer. That'll be our placeholder\u003C\u002Fp>\u003Cp>Speaker 1: for this.\u003C\u002Fp>\u003Cp>Speaker 0: So we that's probably actually on the actual input itself. Alright. And then we have a u input form dot title. We have a placeholder. Let's see what we're getting now.\u003C\u002Fp>\u003Cp>Cool. Amazing Directus developer. Maybe our form isn't so wide. 3\u003C\u002Fp>\u003Cp>Speaker 1: x l.\u003C\u002Fp>\u003Cp>Speaker 0: Did that bring it in? It did not. Math would okay. Still a little long, but no worries. Okay.\u003C\u002Fp>\u003Cp>So we've got our title. We want to go through and add the rest of our form. We got a category. Let's see what GitHub Copilot can do here. Yeah.\u003C\u002Fp>\u003Cp>It's not giving us a lot of help here. But we can drop some of these inputs. Maybe we add some spacing between our form components here. So we got tags. We got content.\u003C\u002Fp>\u003Cp>We've got an application URL. Let's make sure we get that. Application URL. And, alright. So for this, like, the company information, the category, etcetera, This could be a list of companies.\u003C\u002Fp>\u003Cp>Right? So what do we have inside Nuxt UI for something like this? We've got a drop down. Is that what we're looking for? Does this have a search?\u003C\u002Fp>\u003Cp>No. That's gonna be input,\u003C\u002Fp>\u003Cp>Speaker 1: input menu. I'm assuming\u003C\u002Fp>\u003Cp>Speaker 0: this could be an input menu based on the preset. This is searchable. Search attributes, control the query, Async search. Okay. Here we go.\u003C\u002Fp>\u003Cp>So we got a async search function. We can search for a company inside Directus with this. This will be an async function. Let's call it search categories or search companies, maybe. Let's do the company first just to, well, actually, let's do categories.\u003C\u002Fp>\u003Cp>We have them fill out the the company information. Constant, what our data is gonna be await, direct us dot request. That's gonna be read items. We're probably gonna need, what, like, a search query as well. That'll be passed here.\u003C\u002Fp>\u003Cp>This will be our string. Loading value, read items dot categories or categories is gonna be here. Inside that request, we're gonna need a filter. Actually, let's use a search for that. And we'll just do search query.\u003C\u002Fp>\u003Cp>Now the search param here is not optimized like the filter params are, but let's see what we've got here. You form group, you input menu. Oh, wrong one. We want this to be our let's drop this in our category. You input menu, search equals search categories.\u003C\u002Fp>\u003Cp>This is gonna be form dot category. Search for a category. And we can set a loading state for this as well. Alright. Let's see what we've got here.\u003C\u002Fp>\u003Cp>Search for a category. In line properties, not length. If I do this, is it actually searching for a category? Programming? I don't see it.\u003C\u002Fp>\u003Cp>Search search categories, loading, form dot category, placeholder, option name, attribute is a title. Title. Alright. Let's see what else we got here.\u003C\u002Fp>\u003Cp>Speaker 1: I'm missing\u003C\u002Fp>\u003Cp>Speaker 0: something here. Oh, forgot to return something from that actual function. Gotta return the data, Brian. Makes sense. Alright.\u003C\u002Fp>\u003Cp>See if we got anything else now. Is this actually working? I don't see it calling any of the data, though. Direct is at request, read items, categories. Did I, oh, that's why.\u003C\u002Fp>\u003Cp>Got to import read items as well. Alright, so we refresh. Programming. Okay. Now we see sales and marketing.\u003C\u002Fp>\u003Cp>And inside my actual listing, right, where's the form? So we got our slug. This is actually new. Alright. So we got our form.\u003C\u002Fp>\u003Cp>We got the category. We need the ID of that category. Selecting the actual object. Option Attribute Title. Category dot title?\u003C\u002Fp>\u003Cp>Is that going to give us what we want? Oh, no. Option attribute by ID. Why is it not storing when it selects the value what do we actually want here? V model async search.\u003C\u002Fp>\u003Cp>Use the debounce. You know, we could add a debounce to it. Let's actually deal with that in a moment. We need to actually submit this form first. Alright.\u003C\u002Fp>\u003Cp>For the input menu, what else do we have? Okay. I see like a select menu here that is probably very similar to this. Does this one support multiple objects? It does.\u003C\u002Fp>\u003Cp>So I can select multiple tags for this. This is probably gonna want be what we're looking for there. We can actually probably genericize this as well. We have a string. We have a collection, which is a string.\u003C\u002Fp>\u003Cp>This is gonna be our search. This is gonna be a collection. And in this case, our search would be search categories. What's that gonna be? The categories?\u003C\u002Fp>\u003Cp>Actually, categories. So it'll just be search. Does that work? Is that gonna give us what we need? For some reason, that doesn't work.\u003C\u002Fp>\u003Cp>Undefined. Surf search f n. Do I have, like, another search variable\u003C\u002Fp>\u003Cp>Speaker 1: or something? String. Q.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Does that get us what we're looking for? Let's try it again. There we go. Okay.\u003C\u002Fp>\u003Cp>Alright. So now we're getting that. Let's copy in our use select menu. Does this have the same async search capabilities? It does.\u003C\u002Fp>\u003Cp>Our use select menu, this is going to be multiple, so we'll get our tags. The model equals form dot tags. Our search function here is going to look like this. So we get our search functions. This is going to be tags.\u003C\u002Fp>\u003Cp>Search for tags. We've got the option name. It's gonna be a title. That could be multiple. It could be by ID.\u003C\u002Fp>\u003Cp>Great. And now we can see, like, full time, part time. We got some tags selected. Great. Alright.\u003C\u002Fp>\u003Cp>Let's add some buttons, just a single button to our form group. We'll make it type equals submit. And what are we gonna do here? This will just be submit listing. Okay.\u003C\u002Fp>\u003Cp>We've got a job post. Cool. For the sake of this, we've got, like, 7 minutes running on the clock. I am going to allow people to submit a new listing here publicly. I would probably make them log in or create an account first, but, for the sake of just getting this done, let's try it this way.\u003C\u002Fp>\u003Cp>And then here, form dot title dot tags. Let's just see what we're getting first. Here's a new post. When I search for my programming category, we'll apply a couple of tags here. Here's the amazing content for our job post, and we just got a new link.\u003C\u002Fp>\u003Cp>So httpdirectus.i0. If we look at our form component, let me close this. So open this back up. New is the name of this actual component. Why don't I see it?\u003C\u002Fp>\u003Cp>New. There it is. We can see the state of our form. Our tags are an array of objects. Our category is an array of objects.\u003C\u002Fp>\u003Cp>Here, I'm just gonna do this where we do category form dot category dot ID. I think that should work here. For our tags, probably going to have to do some type of mapping dot map tag dot tag dot id. Okay. Oh, no.\u003C\u002Fp>\u003Cp>I lost my amazing post. We are coming up very quickly on the end. We need an amazing Directus developer and a 100 apps, a 100 hours host. Probably after I fail at this one, we'll do design, full time, SEO, select 5 tags. Here's some content for this post.\u003C\u002Fp>\u003Cp>We'll do the direct us URL, and let's see what happens. Alright. We'll do network request. Post. I don't see anything happening.\u003C\u002Fp>\u003Cp>Submit listing, submit listing, type equals submit within the form, Submit listing at submit dot prevent listing. Does this need do I need, like, a click handler on this? I shouldn't. Let's try it anyway. Submit.\u003C\u002Fp>\u003Cp>Why are we not making a single call here? Submit this. Why are we not submitting? Prevent default. At submit dot prevent.\u003C\u002Fp>\u003Cp>And maybe it automatically does that for you. Man. I hate to cut this down to the wire. We are gonna cut it down to the wire. Test.\u003C\u002Fp>\u003Cp>Test. Array of errors. We got what? Internal server error. What do we actually send to the server?\u003C\u002Fp>\u003Cp>Here's our payload tags 12, application URL, category is 2, company is test tag. What kind of response did we get? Why is this an application error? Job listing, submit listing, direct us to request, create item, category. Man.\u003C\u002Fp>\u003Cp>This is frustrating. Form.application.url. Click submit listing. Why isn't this going through? Maybe we unselect these things.\u003C\u002Fp>\u003Cp>Can I unselect 1? Submit listing. Errors. Errors. Errors.\u003C\u002Fp>\u003Cp>Internal server error. Oh, man. There's something that I'm not picking up on. Create item, job listing, title category, form dot ID. What if we just drop these 2?\u003C\u002Fp>\u003Cp>Can we actually get this to submit something? Content. Test. Job listing. We've got the public ability to do the job listing.\u003C\u002Fp>\u003Cp>Job listing tags. Is that what it is maybe? Alright. We're just throwing the Hail Mary pass here. You can edit anything.\u003C\u002Fp>\u003Cp>You can edit anything. You can edit anything. And now we're still getting errors from the direct instance. Internal server error. Is it like a cores issue?\u003C\u002Fp>\u003Cp>You know, if I log out, if I do this in an incognito window, test, test, test. Still getting it right. Items dot job listing response redirected false errors. An unexpected error occurred. What do I have wrong inside this particular setup?\u003C\u002Fp>\u003Cp>Title, status. Is it the slug that I made unique? Slug has to be unique. Is that what it is? Test.\u003C\u002Fp>\u003Cp>I'm going to kick myself after we run out of time on this one. Item, job, listing, post. What what is going on? Is it a a particular, like, a chorus problem? I thought I had chorus set up.\u003C\u002Fp>\u003Cp>Chorus enabled. Chorus origin. This is a oh my gosh, dude. If it is a chorus problem, we're gonna kick myself if it's a course issue. Docker Compose.\u003C\u002Fp>\u003Cp>Let's see what we've got here. I see some logs. Error, insert into job listing. Title created values. Invalid input syntax for ID.\u003C\u002Fp>\u003Cp>Why is it creating an ID? Should not be creating\u003C\u002Fp>\u003Cp>Speaker 1: an ID.\u003C\u002Fp>\u003Cp>Speaker 0: I don't know what's going on there. Alright. Let's try this again. Test. Test.\u003C\u002Fp>\u003Cp>Test. Error. Error. Error. Database.\u003C\u002Fp>\u003Cp>Into job listing. Invalid syntax input for UUID. Like, UUID should be getting automatically populated though. Application URL. What what am I not sending?\u003C\u002Fp>\u003Cp>Oh, it's the company. It's the company field. Oh my gosh. The company field is killing us there. Okay.\u003C\u002Fp>\u003Cp>Alright.\u003C\u002Fp>\u003Cp>Speaker 1: 4 seconds left. Submit. Job Listing.\u003C\u002Fp>\u003Cp>Speaker 0: Nothing. Still errored out. Boom. Time exploded on us. Just out of curiosity, it was the company field that was messing things up.\u003C\u002Fp>\u003Cp>Now, we're still getting, now I'm getting like course errors or something. Or, no, does it show it posted? Did we actually get a job listing in here? Yeah, we did. Of course.\u003C\u002Fp>\u003Cp>After time, the company field getting us, I should have like, actually ran that down. Did not. That's the way these things go, man. It's always hairy trying to develop against the clock. Alright.\u003C\u002Fp>\u003Cp>Let's just test this, test with categories. I'm curious if this will actually work now because we have the, now I'm getting cores errors. Alright. This is just me humoring myself at this point. PMPM dev.\u003C\u002Fp>\u003Cp>Let's restart the Docker container. Oh, no. We don't need to do dev. Okay. So we restart this Docker container, fire back up this Nuxt application, see if this is actually going to do something.\u003C\u002Fp>\u003Cp>Just again, me humoring myself here. Correctus is online. Nuxt application is online. Is this actually going to submit? Local host 3,000, post a job, test with categories, can't even spell.\u003C\u002Fp>\u003Cp>Programming, apply some tags. This is a great job. Here's the test. We hit submit. I probably need to add some submission there.\u003C\u002Fp>\u003Cp>You don't have permission to access this. Why don't I have permission to access this? I've got public controls. Tags, content is great. Oh, it's because, the Junction collection.\u003C\u002Fp>\u003Cp>Oh, boy. Yeah. So, tags is actually a should be something like this where I have tags what's this gonna be? Tags underscore ID equals tags ID. Alright.\u003C\u002Fp>\u003Cp>So in that case, now, new is this\u003C\u002Fp>\u003Cp>Speaker 1: actually going to be what we want it to be?\u003C\u002Fp>\u003Cp>Speaker 0: Full time ID. So when it gets submitted, I'm hoping that should be what we want. Tag underscore ID. Tags underscore\u003C\u002Fp>\u003Cp>Speaker 1: ID. No. Save without formatting.\u003C\u002Fp>\u003Cp>Speaker 0: What are we doing here? Sometimes you can't win them all. Sometimes you just can't win. Test. Test.\u003C\u002Fp>\u003Cp>Does that actually submit? It does submit with the category. So there we go. Roundabout way of getting to this. We still didn't get exactly where we wanted, but, you know, what did we actually get to here?\u003C\u002Fp>\u003Cp>I blew this one up. So we managed to show a list of jobs. We let users kinda post a job for free. Were these sorted by category? We didn't even do that.\u003C\u002Fp>\u003Cp>Subscribe to job posts, we didn't even get there. Man, alright, that's the way it rolls. Thanks for joining me on this episode of 100 Apps, 100 Hours. I'll catch\u003C\u002Fp>\u003Cp>Speaker 1: you on the next one.\u003C\u002Fp>","Hi. Welcome back to another episode of 100 Apps, 100 Hours. I'm your host Brian Gillespie and on the show we build some of your favorite apps or some of your suggestions or just random stuff that I've come across on the Internet in 1 hour or less or get publicly shamed trying when we fail. And I fail often. That's just the rub of this format. The rules are there are 60 minutes to plan and build the application, no more, no less. Sweating just thinking about it. But rule number 2 is use whatever you have at your disposal. AI, GitHub Copilot, tailwind CSS, tailwind UI, any pre made components, whatever we can to get the job done in 1 hour. Today, we are going to be building a job board. So the giant elephant in the room in this space is obviously Indeed. They are one of the largest job boards. Yeah. It's been a while since I've been on their site. The experience is not great, honestly. Maybe everybody's working on a a mobile or something, but, I I think the the job board that we're gonna model after today is we work remotely. We are an all remote team here at Directus, so this speaks to my heart. I've been working remotely for, the better part of a decade now. So, we are going to model after this. Let's dive in and get started, shall we? Alright. So we'll put 60 minutes on the clock. Let's just talk through the functionality that we're looking for here. And As I pull up the WeWork Remotely job board, what's our functionality? Right? We want to, obviously show a list of jobs. In this case they are sorted by category. We want to let someone let users post a job for a fee. That's nice. And, you know, this one's got subscribers as well, which seems like something that is relatively simple. There's a lot of moving pieces to that. So let's add a question mark and we'll do subscribe to job posts. Now I think this one does not allow you to apply within the platform. I think it just, like, sends you off to a third party thing. So that's the way we'll play this, you know, if you wanted to build like an application processing system, you could totally do that with Directus. That's a lot of ground to cover in 60 minutes though. So let's take a look at this, let's flesh out our actual data model, right? That's part of the planning process, so I'm just going to drag some boxes over here inside Figma. What are we going to have? We're going to have job posts or we could call it job listings. That's great. Within that, we're gonna have a few different things. Alright. What do we got? We got some tags, it looks like. So we got a category. We got a title for that post. We've got some content, we've got a link, we've got a like, a company. I guess if you're going to have a company, that would probably be here. That'd be a separate table. Right? There's a company. Company has a name, a website. Name. Website. What? Logo. Great. K. What else are we gonna have? If we make it 2 subscribers, we'll worry about that when we come across it, but let's dive in. Right? This seems pretty good. We'll just draw some arrows to make it look fancy. Right? A company has multiple job listings. One listing has one company. Alright. Great. Let's talk about what we're working with. So as far as applications, I've got a direct assistance spun up, it is blank. I've got a bare bones Nuxt app that has some login and register capability. Looks like I'm already logged in though, so all good there. And as far as the Directus communication, I've already got a Nuxt plug in defined just to talk to Directus a little bit easier, make it a little less boring on this specific episode. Alright. So let's start by I'm going to pull this up side by side with remote. Oh, no. That's not what we want. I could spend the whole time just messing with the Arc Browser. Alright. So we're talking about job listings. We're going to go ahead and add that as a new collection. And I think I can zoom in a little bit to make that more visible. And then we'll look at the actual listings that we have. Right? One of the other things that I do often is, I'm not sure how this site is put together, but, you know, on sites that are server side rendered, you can go into the actual code and especially if it's, like, a Next application or, I'm not sure if Nuxt does this or not, but you can actually see the data that's posted. I'm not sure if that's the case here. I see Jquery. This is probably server rendered from an actual server. No big deal. But, just one way you can kind of see what other folks are doing on the website, always inspect the code. I live in the JavaScript developer tools console. Alright, so we're going to have a job listing. We're going to have a pretty slug for all of these. So let's see what we're going to do, we're going to have a draft and publish listings, probably got a date created, did the user created, the date updated, etcetera. And let's go through and model out our listing, right. So we've got a title for the listing, we're going to have that. We've got to have a slug for the listing. So we'll just do a slug for the listing. We'll come back to that one in a moment. What else are we going to add? We're going to have some content. I see there's tags here. I don't know exactly how they're using the tags, but it looks like the the tags get their own page. So in that case, it makes me think we need, like, a relationship there. Let's look at that. We'll do a mini to mini relationship because one post could have many tags and there could be many tags to other posts. Alright, So we got tags as a key. I'm going to enter tags as a related collection here, right? And if I open up the advanced field mode inside Directus and I scroll down to the bottom, you'll see that, it's actually going to create a new table for me. So that tags table that I don't have, it's going to create it for me, which is kind of nice, right? And I can add this to the tags, I can show the job listings on the tags that have that specific tag. And maybe we can sort these as well. Cool. Alright. So we'll hit save. We've got tags. We also should have a tags collection now, which is nice. We have a junction table for those as well. Then we're gonna need some content. Right? We need the actual, content for this. That's gonna be the WYSIWYG editor. Is that great? Cool. The type will be text, that's what it's going to be saved as in the database. I can always adjust my toolbar if I need to. And, what else do we need really? Apply for this position, right? This is going to be a URL. So apply URL, Application URL. That's what we'll roll with. We're probably going to need a category for this as well. So if I go back to all the jobs, we can see we've got sales and marketing. If I zoom out a little bit, we can see all the different categories up here at the top, Programming, Design, etcetera. So we're going to create a new relationship inside Directus. Let's call it category. And we will again create a new collection here. So if, even if this doesn't exist, Directus is going to create this for me, which is, you know, super. It's a really easy, super nice way to do our data modeling. We can add the different job listings to the categories if we want to. In this case, maybe I don't need to show that. But we will, we don't have anything to adjust as far as the interface because this is not created yet, but we'll just go through the process. So we've got a category for this, We've got tags. And if I go back to my data, I'll see categories here now. So we've got categories, we've got tags. Great. What else are we missing on this? Is this a featured post or not? I'm assuming that's part of the checkout process. So, you know, we could add a Boolean tag for that is featured. I always like when I'm doing my keys, especially if they're Boolean prefix with the is. There's just a nice little standard naming convention. And on my interface, maybe the label is featured post. Great. Okay. Alright. So this looks pretty good, I want to say. Alright. We'll make some of these things half width. Maybe we make the title full width. We got the status. Actually, let's do this. We got status, we got title. Let's put slug down below title. And don't sleep on the Directus marketplace. Right? One of the extensions that I've been using a lot here lately is the WP slug interface extension. If you're used to WordPress, you got, like, this nice little slug interface there. We're just gonna install this, give it a quick refresh. Then I go back to my data model, I go to my slug, go to the interface, and I can switch this up to my slug. I get a template for this, so we're going to populate the title. I could potentially prefix this or, you know, auto generate it as well. So I'm just going to do On Create because after a thing is created, it's been published, changing the slug results in a different URL on your front end, could result in some nasty 404 errors. We don't want that. 40 fours get out of here. Alright. So we got a job listing category. We've got did I do job listing instead of job listings? I did. Man. Alright. How can we fix that? We're not going to bother with it. I could dig into the SQL and adjust that if I needed to, but we're not going to worry too much with it right now. Typically, I keep my table names plural. Just a personal preference. Alright. So we've got a category. Let's go ahead and flesh out these categories. We need a title for the category. We probably need a slug for the category as well. So I can go back into my job listings, and if I want, I can duplicate this field to that collection. So we'll just change this to slug. We'll go to categories. Great. And in that case, do we actually want the tags to have their own page as well? The tag will have a title and the tag will probably have a slug as well. So we'll just copy that across. Slug. Change the collection to tags. And away we go. Right? The last thing that we talked about is a company. So we want to be able to select a company for our post and you know potentially a company could post 3 or 4 different times. Maybe they're hiring for a lot of different positions. So let's create a new table. We're going to call it companies. We'll do a generated UUID. Do we need all this information? Probably not. Alright. So we'll give the company name. And whatever I choose to name this key is totally up to me. And then we've got the website. That's just going to be a string. And then we're going to have a logo for that. Right? Logo. Alright. Cool. So now we've got it's just a basic data model set up. Let's go in and I'm just going to, like, just rip some of this content. We work remotely. Sorry for just sealing information off your job board here. Let's look at our different categories. So we'll just mop up a couple of these categories really quickly. We've got what? Programming? Oh, gosh, I can't spell. Programming, save. We've got Design, save. What else do we have? Sales and marketing. Sales and marketing. And what happens if we do this? Sales and? Oh, that's kind of a nice effect. Sales and marketing. Maybe we change this to just be sales dash marketing. Customer support. Customer support. Great. Okay. And this is all remote, so, you know, what are we gonna call this thing? Maybe we'll call it it, Dan's Remote Job Board after some of our own teammates here at Directus. Alright. So we've got some categories. Let's take a look at the tags within here. We've got full time. We've got, part time will be a tag. Right? Part time. What? SEO? Link building, WordPress, technical writing. What do we have on the development side? So we got programming. We got full stack, front end. What kind of tags do we have here? Europe only, etcetera. Hang on just a moment. I got an important call coming in. Be right Alright. So now we're back. We've got 46 minutes on the clock. Let's dive in further. We've got some tags. We've got like Europe only, etcetera. Great. Okay. So, those are a few tags. We've got some categories. We've got, let's see if there's any companies that we recognize on here for full stack development. I don't really recognize any of these. Right? Let's just create a new company here. We'll call this Directus HTTP Directus dot io. And we'll add the logo for this. Right? See what I got handy. Directus logo. We'll get this a couple of different ways. Logotype. That looks good. That's what we will roll with. We got the logotype. Maybe we add another company, but for now let's just add our first listing. Right. I'm just going to steal this one, full stack. Alright. We are going to make sure this is published. Is this a featured post? Looks like it is. Alright. We'll go in. And I could just copy this wholesale. And I'm thinking it should retain some of the content, some of the settings. Yeah. Okay. So it should retain the formatting for it. And then we're going to have a link for the application. And the last thing that we need to do is pick a category. And we probably need to add a company for this as well. And I I joked, it's not the last thing we need to do. We're going to add a few more. This is going to be a Europe only job. Be a $100,000 or more USD. That's that's kind of a nice tag. Let's do React as a tag. Oh, sorry. Creating an item in a tag. React. Great. Alright. Is this a featured post? Yes, it is. Let's save that post and then we'll go in and add a relationship to that company. So that'll be a many to one relationship inside Directus. We're gonna call this company because there's a single company, and then we have companies. And, you know, we could go in and add the company name here so that it displays nicely. We probably want to do the same thing for our categories, like give them a title, make sure those show on the interface in the display template. Same thing for tags. For our list view here, we're going to do the tags ID dot title and we'll do the same thing here. Great. Okay. So now if I open up this job, we can see our different tags for this. We've got our category. We've got the company that this is created for. Sweet. Alright. So now we've got a job listing. We've got some categories, we've got some tags, we've got some companies. Does a company going to have a slug? You know, potentially. Do they have a listing here? They do. So probably need a slug for the company as well. Slug. The template is going to be the name of the company. Great. Alright. So we've got our data model for this, right? The next thing that I'm going to do is just go through our access control and create this. So there are 2 roles that are out of the box inside Directus. I have public, I have administrator. I'm going to go through and, just basically set read access to all these. And then I can go back in and adjust this if I need to as well. So we're going to adjust the item permissions just a little bit to where we can only see published posts. So I'll go through listing tags, categories. I don't know the categories. Do we do publish for categories? We did not. Tags, etcetera. Yeah. So the rest of those are fine as is. Right? People can see all the companies, all the tags, all the categories. But if it's not a published listing, they can't see that information. Alright. So great. Let's go in and now, is it time to actually start fleshing something out inside our Nuxt application? Let's let's take a look at that. Right? So we've got here on the how do I separate these 2? Alright. Cool. So, Nuxt application on our pages directory, we've got an index page. This is probably going to be just our listing of posts. Right? So we can start fleshing this out a little bit. Let's remove this centering. That should move it way up here to the top. We'll just add a header for this. And what are we going to have in the header? We're going to flex. We are going to do Dan's remote job board. That's the name of this thing. We'll make sure it's like a mono font for Dan. I know how Dan likes this stuff. Okay. Let's give this header a little padding. PX6 PY. Alright. And we can even make this like sticky at the top if we want to. Sticky, top 0. Give it a background white. BG opacity. We do that backdrop blur effect. One of the nice things about using tailwind, backdrop blur, BG opacity. I could actually just adjust it this way. Let's do dash or slash 50. And cool. Alright. So now we've got a header. We might have some links inside our header that we'll cover in just a moment. Let's do a div to ul. Links. Do we actually need these on a list? Probably not. Let's do what? Nuxt link v 4, links and links, links on links on links. Let's actually call this nav links. And we'll just flesh a few of these out real quick. Nav links, that'll be an array equals array. To, about, contact, no. That's we want none of that. Home, we got what? Categories. We'll come back to it. Let's just nuke that for now. Or we could just comment it out. Alright. As far as what we want inside the actual body of this here, we'll do this in the main section, we are going to fetch our job listings, right. So Nuxt has a couple of nice composables for fetching data. We're going to use that here. But the first thing I'm going to do, let's import the read items operation from my Directus SDK. Nope. Directus SDK. And then I'm going to use the async data call, the async data composable from Nuxt. Use async data, And this handles like caching and deduplication, and actually if this is server rendered it will automatically dedupe those calls and pass that data from the server to the client so that I don't have to fetch the data twice and hit that API. So we'll give this a, let's say this is listings index. We'll give it a key and then we'll actually fire this off. And we're going to actually await this data. Alright. Then within here, we're actually going to use our Directus client that we've set up. We're gonna access that through our Nuxt plug in and we are going to do this. So we'll say return Directus dot request. We'll do read items. We're gonna read from the what do we call it, actually? Call it the Job Listing Collection. So that's going to trip me up this whole time. And then we have a query that we could set up. We could run this where it says filter status equals published, but I'm handling that here within my access control inside Directus, so it doesn't really matter. So as long as this is published, like if I go in and let's just say I change this. Change this. We say we maybe set this to draft, and I hit save as copy. And I probably need a new slug for this. And I would definitely make slug unique here, so let's just do that quickly before I forget. We'll go into the data model, we'll go to listings, we'll make sure that slug has to be a unique value. Alright. So this will be listings and then I can I typically do something like this whenever I'm building just to quickly and easily make sure I'm getting all the data that I want back from my API? Alright. So we go to local host. We can see we've got this job board and I need to actually get this out of the header and into the main body here. Alright. So there's our application, our job listing, basically. Alright. That's great. We can see our tags, we can see the company. We can't really see any of the information about them, though. Right? And if we take a look at our postings here, let's see what we've got. We've got the company information, we've got the company name, etcetera. So how do we go and fetch that data with inside Directus? We'll just go in here. We'll do something like this where we have fields. I don't recommend doing it this way in production, just using these wildcards. But for something like this where I'm quickly prototyping, super handy for that. I could just get all of the root level fields, I can get all of the company level fields, I could get the category, category dot star. And then for our tags, I'm going to get tags dot tags underscore ID. I'm going to get that field. So if I do that now, you can see I get the extra information that we're looking for. So I've expanded my relationships for tags, for the company, for our category, basically everything I need here. Looks great. Looks great. Okay. And I'm getting both of those things here because I am using session authentication. I've got a cookie here that's storing my session token. So if I wanted to, I could go into incognito window and that should solve it where I'm only getting one of those items back. Alright. So I'm gonna pull this up. We work remotely over here on the side just so I can see that. And we'll start fleshing this thing out. So within our listings, we've got, let's do a div. Probably add, like, a h two here. This is our job listings. Get that font bold. Text XL. Maybe 2 XL. Alright. And maybe we give the whole main section a little bit of padding. I I think there's a U container Nuxt UI component that we'll just use for this. I'm using uidot nuxt.com, just a Nuxt UI library, just to have something to build with quickly. Alright. So we've got our job listings. Let's go through we're gonna do a card component for each one of those listings. So v 4 job in listings. The key, we can use the job dot ID. And here, inside the card itself, let's see what we got. Job title. Is this actually gonna be what we want? What's this gonna look like? Remote. It's not really anything that we we actually want here. This is gonna be company name, category dot name, Category dot title. And and this could be remedied by TypeScript if I had all these things typed probably. But as far as the tags, that's going to be the title of the tags as well. Cool. Alright. So how can we get this to look a little more like what we have? We'll go through and flesh this out a bit. Wrap this. We'll put it in a, it doesn't even need to be a grid. We could just do like a single grid. Give it some gap. 6. Alright. We got some space there. We'll give it a little breathing room. Cool. Alright. So we've got some job listings. We're going to want to give these a link. Where did where did that go? Oh, just disappeared. Need to learn my keyboard shortcuts, don't I? What are we what are we doing, Brian? That did it again. Alright. Maybe I'll just use the cut paste here. Wrap this in a Nuxt link component. We're gonna do this, the 2 for this will be something like this, where we have listing, plus job ID. Yeah. Potentially let's actually do it this way, just use a template literal. Job dot slug. Wrap that, don't forget the ending tick. Cool. And now we have a link that does not work because we don't have a route for that. But let's see if we could show the company information as well. Maybe we put that here. It would be within a div. We got the company name. Show Nuxt image source dot company logo. See if we can get that displaying. Okay. Now we'll shrink that a bit. Object Oh, hang on a moment. Just go here. Class, object contain, make it 24 high, 24 wide. Cool. Within this, we could do, like, a flex, add a little bit of gap. And, you know, if we wanted to, we could get fancy with it. But let's, how do we set this off a little bit? Oh, actually, the title for the company is over here on this side in it. Alright. So go in, wrap these in a div. That should give us like some type of formatting here. And if I really wanted to make it look like the one on WeWork remotely, and I don't, we'll just add a border for this, round it a little bit, get a little bit of padding, p 1. And maybe we make the border pretty. Make it violet. There we go. That looks poor, actually. I don't like it. Alright, we'll make it more subtle. There we go. So we got Directus, we got Change This as the name. We've got our job listings. How are we doing on time? Time wise, we're at about half an hour, right? Alright, so we've got some job listings. We can see a list of those. How would we do something like a filter for these? Or we'd probably actually want to list these by categories. Right? So we'd probably actually switch up our data fetching here into, we'd probably look for categories first. But instead of diving into that specifically right now, let's actually get a detail page up. So we've got a route for this index page. Let's go in and add a route for our listings. Great. And then we'll go in and add a new page. Let's call this the slug. View. So we do have a listing there. And maybe I just copy pretty much all of this. We'll actually, what do we wanna keep here? Let's just keep the card component. That's got our information. Maybe we keep the u container just so we get some base styling. And if I wanted to, I can move this header component out of this and into our actual app itself or in probably like the default layout would be a good one as well. Just post that there. So now that will show on every single page. Alright. So as far as our job listing, we're going to fetch that data here. I'm still going to use the read items call, because we're fetching by the slug and not the actual ID. So what we're going to do inside Knox would be something like this, where we use the route composable, or use route composable to get the route. And then we're going to set up a filter for our slug, and we want that to be equal to route. Params.slug. So this should give us our listing. And then what we're going to do here is at the end of this, using the use async data and the I think it's use fetch composable inside Nuxt have an option set where I can transform this data when I return it. So this is going to give me an array and I only want the first item of the array. So even though this only matches one thing because the slug is unique, it still returns an array because of the read items SDK, or the read items method of the SDK. So we're just gonna transform that. And then here inside our card, we don't really need that anymore. Right? So this gives us what we want. Cannot read properties of undefined of slug. Why do we not have this? Right? Here's our listings. There's our slug. Route.params.slug. Directus.requestreaditemsjoblisting. Cannot read properties of undefined of slug. Let's just comment this out, see if the page actually loads. Yes. It does load. Let's figure out why we're not seeing that. So, the Vue dev tools is is really a great tool if you're developing with Vue. We will look for the slug component. Let's see what we got. We got listings. Oh, that's why. That would make sense. Right? Sometimes it's the typos, especially when you're building quickly. They get you into trouble. So here, let's just remove the card. I don't even need this Nuxt link component anymore. Okay. And within our layout, let's make it let's get some navigation so we can actually move backwards here. Make this a Nuxt link. Nuxt link. Change it to 2, do slash. Cool. So now I could go should be able to go back and forth. There we go. Go here. Go there. Amidst options next to header slot. What am I doing wrong there? Alright. Anyway, so this is gonna be our job. And now we have got our job listing. Cannot read properties of type. Alright. Let's go back into Directus. And is this because of our our access control? We've got it. Let's set it up so we can read the files as well. Does that give us what we want? Okay. So at least now I've got some type of card information here. How do we want to display the actual content for this? If we go into one of these, we've got, the job title, the logo here is on the right as well as the company. So we can go in and flesh this out. You know, we could spend a ton of time on design here. I'd rather focus more on the important bits. So let's just do a main component. This is where we'll stuff the content. We could do v dot vhtmlordiv.vhmml, v dash html. That'll be our job dot content. Okay. That should show. Cool. This is not really nicely formatted. Right? So, using Tailwind, they have this really nice pros class we could use to get that formatting. Looks pretty great. Maybe we add some padding to this container. I don't even need that. Strip this away or not some padding. Give it some margin. This looks a little odd here at the top. Let's remove that. We've got Directus is the company. And this could be another NuxLink. This could be, what, to the company? 2\u002Fcompany\u002Fjob.company.slug. That's what that would look like. And inside this div, maybe we just add some spacing. Okay. Alright. So now we could potentially navigate to the company, but there is no page for that. But here we've got our actual job posting. How do we we want to use like a button for you button. Is it the apply URL? I think I've got application URL here. Apply for this position. Great. What's that gonna do? Job application URL, is that what I set this up as? Let's take a look at our data model. Application URL, job application URL, that should be correct. And we want this to be let's make this a block class. Block. And maybe we drop that below the actual name. We don't have a description for this. It's great. Maybe we do this. Just set off this metadata just a little bit. Okay. We hit apply for this position. And this should open that in a oh, it's because I've got HRF. Let's try this. Apply for this position. That should give me this application page. Great. What is this going to be? In line block? There we go. Okay. So now we've got our page, we've got our company information, we've got our job description. We can drop another button at the bottom of this content. Looking nice. Alright. We are cruising on, like, 20 minutes worth of time. Right? So how can we actually create a post? Right? We've got our post. Let's go back and we'll do a new route. We'll just call it listings dot new. I'm just going to copy the same setup that we've got. Just moving really really quickly. Just drop everything out of here. We're gonna keep Directus. In this case we're gonna use create items. Create item. Great. And on the front end, if I wanted to be able to create a listing, we're going to need to add some navigation for this. So we'll just, let's actually stick this inside our header. Right. Where are you? That's in our default layout. So over here on the far right, we can actually flex these items, justify between. Oh, sorry. I already had the flex. Great. And we'll do listings dot new, post a job. Great. That takes us to the job new page. And on the new page, why do we not okay. Okay. So now we are going to post a job. We've got let's take our same heading here. Post this in here. We've got a create a new job post. Okay. Alright, so now we can start fleshing out our actual form that we want, right? So let's just do form. This could be reactive since we've got some properties here. What all do we have inside that listing? Right. We've got a title. You know, maybe we don't want them to actually choose the slug. We've got a category. That should just be an ID. We got some tags. This is gonna be an array. We got some content. That's gonna be a string. We got an application URL, and we've got a company, which is, it's actually, should be a string, just an ID, but we'll have to actually fetch that information. And then at the end of this, we are going to have a function to submit the form. Alright. Submit listing. And what's that function gonna look like? Oh, let's do a try catch. This is gonna be a constant response equals await, direct us dot request. Create item. Yes. This looks good. Form dot title. Okay. Alright. GitHub Copilot. I got you. And then what are we gonna do? Console log response. And we could send them to like a thank you page or something. Maybe we are tracking like a success state here. And we set success value to true. If there's an error, we're going to log that error. Great. Cool. Alright. So we got submit listing. Now we need to actually build a form. Right? Inside this Nuxt UI library, I think there is a, let's just dive into it, form component. You can even add validation with this using, like, Zod or something like that. But, we've got our form wrapped in a form. There's a form tag. We've got at submit. Let's do dot prevent. Submit listing. That's going to be our form handler. And then inside the form, each one of those gets wrapped in a form group, where we got a label equals title. Placeholder equals, amazing Directus developer. That'll be our placeholder for this. So we that's probably actually on the actual input itself. Alright. And then we have a u input form dot title. We have a placeholder. Let's see what we're getting now. Cool. Amazing Directus developer. Maybe our form isn't so wide. 3 x l. Did that bring it in? It did not. Math would okay. Still a little long, but no worries. Okay. So we've got our title. We want to go through and add the rest of our form. We got a category. Let's see what GitHub Copilot can do here. Yeah. It's not giving us a lot of help here. But we can drop some of these inputs. Maybe we add some spacing between our form components here. So we got tags. We got content. We've got an application URL. Let's make sure we get that. Application URL. And, alright. So for this, like, the company information, the category, etcetera, This could be a list of companies. Right? So what do we have inside Nuxt UI for something like this? We've got a drop down. Is that what we're looking for? Does this have a search? No. That's gonna be input, input menu. I'm assuming this could be an input menu based on the preset. This is searchable. Search attributes, control the query, Async search. Okay. Here we go. So we got a async search function. We can search for a company inside Directus with this. This will be an async function. Let's call it search categories or search companies, maybe. Let's do the company first just to, well, actually, let's do categories. We have them fill out the the company information. Constant, what our data is gonna be await, direct us dot request. That's gonna be read items. We're probably gonna need, what, like, a search query as well. That'll be passed here. This will be our string. Loading value, read items dot categories or categories is gonna be here. Inside that request, we're gonna need a filter. Actually, let's use a search for that. And we'll just do search query. Now the search param here is not optimized like the filter params are, but let's see what we've got here. You form group, you input menu. Oh, wrong one. We want this to be our let's drop this in our category. You input menu, search equals search categories. This is gonna be form dot category. Search for a category. And we can set a loading state for this as well. Alright. Let's see what we've got here. Search for a category. In line properties, not length. If I do this, is it actually searching for a category? Programming? I don't see it. Search search categories, loading, form dot category, placeholder, option name, attribute is a title. Title. Alright. Let's see what else we got here. I'm missing something here. Oh, forgot to return something from that actual function. Gotta return the data, Brian. Makes sense. Alright. See if we got anything else now. Is this actually working? I don't see it calling any of the data, though. Direct is at request, read items, categories. Did I, oh, that's why. Got to import read items as well. Alright, so we refresh. Programming. Okay. Now we see sales and marketing. And inside my actual listing, right, where's the form? So we got our slug. This is actually new. Alright. So we got our form. We got the category. We need the ID of that category. Selecting the actual object. Option Attribute Title. Category dot title? Is that going to give us what we want? Oh, no. Option attribute by ID. Why is it not storing when it selects the value what do we actually want here? V model async search. Use the debounce. You know, we could add a debounce to it. Let's actually deal with that in a moment. We need to actually submit this form first. Alright. For the input menu, what else do we have? Okay. I see like a select menu here that is probably very similar to this. Does this one support multiple objects? It does. So I can select multiple tags for this. This is probably gonna want be what we're looking for there. We can actually probably genericize this as well. We have a string. We have a collection, which is a string. This is gonna be our search. This is gonna be a collection. And in this case, our search would be search categories. What's that gonna be? The categories? Actually, categories. So it'll just be search. Does that work? Is that gonna give us what we need? For some reason, that doesn't work. Undefined. Surf search f n. Do I have, like, another search variable or something? String. Q. Alright. Does that get us what we're looking for? Let's try it again. There we go. Okay. Alright. So now we're getting that. Let's copy in our use select menu. Does this have the same async search capabilities? It does. Our use select menu, this is going to be multiple, so we'll get our tags. The model equals form dot tags. Our search function here is going to look like this. So we get our search functions. This is going to be tags. Search for tags. We've got the option name. It's gonna be a title. That could be multiple. It could be by ID. Great. And now we can see, like, full time, part time. We got some tags selected. Great. Alright. Let's add some buttons, just a single button to our form group. We'll make it type equals submit. And what are we gonna do here? This will just be submit listing. Okay. We've got a job post. Cool. For the sake of this, we've got, like, 7 minutes running on the clock. I am going to allow people to submit a new listing here publicly. I would probably make them log in or create an account first, but, for the sake of just getting this done, let's try it this way. And then here, form dot title dot tags. Let's just see what we're getting first. Here's a new post. When I search for my programming category, we'll apply a couple of tags here. Here's the amazing content for our job post, and we just got a new link. So httpdirectus.i0. If we look at our form component, let me close this. So open this back up. New is the name of this actual component. Why don't I see it? New. There it is. We can see the state of our form. Our tags are an array of objects. Our category is an array of objects. Here, I'm just gonna do this where we do category form dot category dot ID. I think that should work here. For our tags, probably going to have to do some type of mapping dot map tag dot tag dot id. Okay. Oh, no. I lost my amazing post. We are coming up very quickly on the end. We need an amazing Directus developer and a 100 apps, a 100 hours host. Probably after I fail at this one, we'll do design, full time, SEO, select 5 tags. Here's some content for this post. We'll do the direct us URL, and let's see what happens. Alright. We'll do network request. Post. I don't see anything happening. Submit listing, submit listing, type equals submit within the form, Submit listing at submit dot prevent listing. Does this need do I need, like, a click handler on this? I shouldn't. Let's try it anyway. Submit. Why are we not making a single call here? Submit this. Why are we not submitting? Prevent default. At submit dot prevent. And maybe it automatically does that for you. Man. I hate to cut this down to the wire. We are gonna cut it down to the wire. Test. Test. Array of errors. We got what? Internal server error. What do we actually send to the server? Here's our payload tags 12, application URL, category is 2, company is test tag. What kind of response did we get? Why is this an application error? Job listing, submit listing, direct us to request, create item, category. Man. This is frustrating. Form.application.url. Click submit listing. Why isn't this going through? Maybe we unselect these things. Can I unselect 1? Submit listing. Errors. Errors. Errors. Internal server error. Oh, man. There's something that I'm not picking up on. Create item, job listing, title category, form dot ID. What if we just drop these 2? Can we actually get this to submit something? Content. Test. Job listing. We've got the public ability to do the job listing. Job listing tags. Is that what it is maybe? Alright. We're just throwing the Hail Mary pass here. You can edit anything. You can edit anything. You can edit anything. And now we're still getting errors from the direct instance. Internal server error. Is it like a cores issue? You know, if I log out, if I do this in an incognito window, test, test, test. Still getting it right. Items dot job listing response redirected false errors. An unexpected error occurred. What do I have wrong inside this particular setup? Title, status. Is it the slug that I made unique? Slug has to be unique. Is that what it is? Test. I'm going to kick myself after we run out of time on this one. Item, job, listing, post. What what is going on? Is it a a particular, like, a chorus problem? I thought I had chorus set up. Chorus enabled. Chorus origin. This is a oh my gosh, dude. If it is a chorus problem, we're gonna kick myself if it's a course issue. Docker Compose. Let's see what we've got here. I see some logs. Error, insert into job listing. Title created values. Invalid input syntax for ID. Why is it creating an ID? Should not be creating an ID. I don't know what's going on there. Alright. Let's try this again. Test. Test. Test. Error. Error. Error. Database. Into job listing. Invalid syntax input for UUID. Like, UUID should be getting automatically populated though. Application URL. What what am I not sending? Oh, it's the company. It's the company field. Oh my gosh. The company field is killing us there. Okay. Alright. 4 seconds left. Submit. Job Listing. Nothing. Still errored out. Boom. Time exploded on us. Just out of curiosity, it was the company field that was messing things up. Now, we're still getting, now I'm getting like course errors or something. Or, no, does it show it posted? Did we actually get a job listing in here? Yeah, we did. Of course. After time, the company field getting us, I should have like, actually ran that down. Did not. That's the way these things go, man. It's always hairy trying to develop against the clock. Alright. Let's just test this, test with categories. I'm curious if this will actually work now because we have the, now I'm getting cores errors. Alright. This is just me humoring myself at this point. PMPM dev. Let's restart the Docker container. Oh, no. We don't need to do dev. Okay. So we restart this Docker container, fire back up this Nuxt application, see if this is actually going to do something. Just again, me humoring myself here. Correctus is online. Nuxt application is online. Is this actually going to submit? Local host 3,000, post a job, test with categories, can't even spell. Programming, apply some tags. This is a great job. Here's the test. We hit submit. I probably need to add some submission there. You don't have permission to access this. Why don't I have permission to access this? I've got public controls. Tags, content is great. Oh, it's because, the Junction collection. Oh, boy. Yeah. So, tags is actually a should be something like this where I have tags what's this gonna be? Tags underscore ID equals tags ID. Alright. So in that case, now, new is this actually going to be what we want it to be? Full time ID. So when it gets submitted, I'm hoping that should be what we want. Tag underscore ID. Tags underscore ID. No. Save without formatting. What are we doing here? Sometimes you can't win them all. Sometimes you just can't win. Test. Test. Does that actually submit? It does submit with the category. So there we go. Roundabout way of getting to this. We still didn't get exactly where we wanted, but, you know, what did we actually get to here? I blew this one up. So we managed to show a list of jobs. We let users kinda post a job for free. Were these sorted by category? We didn't even do that. Subscribe to job posts, we didn't even get there. Man, alright, that's the way it rolls. Thanks for joining me on this episode of 100 Apps, 100 Hours. I'll catch you on the next one.","acde218a-5b70-4609-9ae5-797787df177b",[2990],"0be0daf8-e91d-42cc-9983-359dcf22b4b9",[],{"year":978,"number":2062,"id":2311,"episodes":2993,"show":2994},[2313,2314,2315,2316,2317,2318,2319,2320,2321,2298],{"title":322,"slug":323},{"id":2996,"slug":2997,"vimeo_id":2998,"description":2999,"tile":3000,"length":159,"resources":12,"people":12,"episode_number":177,"published":3001,"title":3002,"video_transcript_html":3003,"video_transcript_text":3004,"content":12,"seo":3005,"status":19,"episode_people":3006,"recommendations":3008,"season":3009},"50b4447b-73c7-4b49-9e70-d210595d1a7e","ai-alt-text-writer","945761043","Create captions for your images within Directus Files with this custom operation, powered by Clarifai.","ac361908-b15b-4f15-908a-c18bc28b6771","2024-05-16","AI Alt Text Writer","\u003Cp>Speaker 0: The Directus AI alt text writer will take in an image and provide a description for the image, which you can then save in the description of that image and use in your applications to make them more accessible. So we're gonna set it up today. So, automatically, whenever a new file is uploaded, it will go off and have its description automatically and we'll make this an event based hook. When and we'll make this an event based hook whenever a file is uploaded. Now just to note that here this will trigger on every single file regardless of file type.\u003C\u002Fp>\u003Cp>I'm gonna leave it like this, but in the real, you would probably want to add a condition that says, hey. Make sure this is an image or, you know, make sure this is in a certain directory. The docs on how to do that are inside of the marketplace listing for this extension, but we won't do that. So, you know, we'll assume every file that's uploaded is going to be an image. So we're gonna jump straight in and we are going to use the AI alt text writer, and this operation uses Clarify.\u003C\u002Fp>\u003Cp>So we have to provide our access token, and we have to provide a direct full URL. So what we're gonna do there is provide the core URL of our directors project slash assets slash and then a dynamic value here trigger dot key, and that will be the ID of the image or file that has just been uploaded causing this flow to start in the first place. So that will go off, and, honestly, it's pretty straightforward, this one. It just returns a string, which is the description. So we're gonna go straight ahead, and we are going to update data in the Directus files collection, specifically, the item that triggered this flow and we are going to update the description.\u003C\u002Fp>\u003Cp>And the value will be lost because it just returns the string. That's it. That's how you set up the AI alt text writer. It's really really pleasant and, you know, if you can set this up earlier rather than later you can make sure more of your images actually have alt text written. Let's, let's try it out here.\u003C\u002Fp>\u003Cp>So I've got this photo from a holiday, I did a few months ago with my friend Nathaniel, and we click into that and you know what not bad. A photograph of 2 men sitting on a bench in front of a sign that says Avenue Ave Ave. Again, do double check these, you know, there is a point where they can you know, where enough, error proneness, can actually cause a degraded experience for people who need this, but it will get you a really long way there. Of course, it was never gonna get that this said Avengers Campus, so I think I'll forgive it this time. So that is how to set up the AI alt text writer operation.\u003C\u002Fp>\u003Cp>Hope you found this interesting, and I'll see you in the next video.\u003C\u002Fp>","The Directus AI alt text writer will take in an image and provide a description for the image, which you can then save in the description of that image and use in your applications to make them more accessible. So we're gonna set it up today. So, automatically, whenever a new file is uploaded, it will go off and have its description automatically and we'll make this an event based hook. When and we'll make this an event based hook whenever a file is uploaded. Now just to note that here this will trigger on every single file regardless of file type. I'm gonna leave it like this, but in the real, you would probably want to add a condition that says, hey. Make sure this is an image or, you know, make sure this is in a certain directory. The docs on how to do that are inside of the marketplace listing for this extension, but we won't do that. So, you know, we'll assume every file that's uploaded is going to be an image. So we're gonna jump straight in and we are going to use the AI alt text writer, and this operation uses Clarify. So we have to provide our access token, and we have to provide a direct full URL. So what we're gonna do there is provide the core URL of our directors project slash assets slash and then a dynamic value here trigger dot key, and that will be the ID of the image or file that has just been uploaded causing this flow to start in the first place. So that will go off, and, honestly, it's pretty straightforward, this one. It just returns a string, which is the description. So we're gonna go straight ahead, and we are going to update data in the Directus files collection, specifically, the item that triggered this flow and we are going to update the description. And the value will be lost because it just returns the string. That's it. That's how you set up the AI alt text writer. It's really really pleasant and, you know, if you can set this up earlier rather than later you can make sure more of your images actually have alt text written. Let's, let's try it out here. So I've got this photo from a holiday, I did a few months ago with my friend Nathaniel, and we click into that and you know what not bad. A photograph of 2 men sitting on a bench in front of a sign that says Avenue Ave Ave. Again, do double check these, you know, there is a point where they can you know, where enough, error proneness, can actually cause a degraded experience for people who need this, but it will get you a really long way there. Of course, it was never gonna get that this said Avengers Campus, so I think I'll forgive it this time. So that is how to set up the AI alt text writer operation. Hope you found this interesting, and I'll see you in the next video.","58131a69-92b7-4c5e-9c09-8db247e22838",[3007],"38c120ea-75a1-47bf-badc-a46a9a72bacd",[],{"year":978,"number":158,"id":3010,"episodes":3011,"show":3022},"25c1b8ec-a696-4551-8d13-4e2abfbc7cb5",[3012,2996,3013,3014,3015,3016,3017,3018,3019,3020,3021],"6aee5cdc-3d0a-4fa6-acee-49c1860a553d","42784fad-29d4-4f01-a076-43f1817655b7","d948815e-2417-4319-814b-87d7c5fbdd43","2d94463a-5c39-4274-ba39-287e9059cc9c","afb17dd7-48b9-4297-ada8-f22c1daa82ce","be92a983-6a6a-411f-8f07-2cba4aa9308c","afc8f09e-f82f-473d-8643-14176380ae52","3d978332-855b-4eb0-a878-b932e0f9d704","40e14c84-7b60-4fc2-a125-beec15e39a28","be07b8cd-0d0f-4713-affe-88c210c29d9a",{"title":3023,"slug":3024},"Directus AI","ai",{"id":3020,"slug":3026,"vimeo_id":3027,"description":3028,"tile":3029,"length":213,"resources":12,"people":12,"episode_number":889,"published":3001,"title":3030,"video_transcript_html":3031,"video_transcript_text":3032,"content":12,"seo":3033,"status":19,"episode_people":3034,"recommendations":3036,"season":3037},"ai-text-translator","945763189","Translate text into over 30 languages with this custom oepration, powered by DeepL.","b5c4b266-6fde-4c42-ad52-430fae9037ae","AI Text Translator","\u003Cp>Speaker 0: The Directus AI text translator operation allows you to provide some text and a target language, and it will be translated into the target language. This operation extension is powered by DeepL, so you will need a DeepL API key in order to get started. Now here in our directors project, we have an episodes collection, and here we have moved transcript into a translation, which means that this bass here in English is actually not in this collection. It's actually held within the episodes translations. You can read more about translations and directors inside of our documentation, but just to give you the high level understanding, we have an item.\u003C\u002Fp>\u003Cp>The item relates to episodes translations, which contains, a link to the item, a link to the target language, and the specific translated fields in that language for that item, and we have a languages collection. In this case, just English and German. We're assuming that content will always be authored in English, and then you will be translating it into German. But, of course, you can build more flexible workflows using prompt confirmation dialogues or looping flows or stuff like that. So our goal will be to have a button here translate to German and the German translation will be applied by creating a new item in the episodes translations collection.\u003C\u002Fp>\u003Cp>Let's create a new flow called translate to German, and this will be a manual trigger on the episodes page. So this returns the ID of the item where we click the button which is actually perfect for our use case. What we're gonna do is we we're now gonna kind of cross reference and instead of grabbing data from this item we're gonna go grab the existing English translation for this item, so we have the English base to translate. So we are gonna read the data from the episodes translations collection with the following filter. We want to make sure that the item is from this, the translation item is related to this episode item and that we are only grabbing the English.\u003C\u002Fp>\u003Cp>So we will hit save. So this should return, an array with 1 item in it which contains the translation. We are then gonna send this to the AI trans the AI translation operation. Now you will need a DeepL API key. Here's one I made earlier, and select which API plan you're on.\u003C\u002Fp>\u003Cp>And in here, we pass in some text. We could hard code it, but we actually want it from the previous operation. Transcript, trans script. Yeah, and we pick a target language that this operation supports. So this should return, this should return just a string not an object just a pure string of that content in German instead.\u003C\u002Fp>\u003Cp>And the final thing we want to do is create a new translation. So we will create data in the episodes translations collection, and the payload will be as follows. The episode ID needs to match the initial one, the language that we've translated to, so this is the language code for German, and the string inside of transcript. So we'll hit save on all of those. So just to reiterate, we click the button on the episode.\u003C\u002Fp>\u003Cp>We grab the existing English translation for that item. We translate it to German, and we create a new translation item. So let's go to our episode. Let's hit translate to German, and there is our German translation for the same content. Once again you can build really powerful workflows here by looping or maybe by allowing a specific language selection as part of a confirmation prompt dialogue.\u003C\u002Fp>\u003Cp>That is how the AI, text translator operation extension works. Hope you found this interesting. We'll see you next time.\u003C\u002Fp>","The Directus AI text translator operation allows you to provide some text and a target language, and it will be translated into the target language. This operation extension is powered by DeepL, so you will need a DeepL API key in order to get started. Now here in our directors project, we have an episodes collection, and here we have moved transcript into a translation, which means that this bass here in English is actually not in this collection. It's actually held within the episodes translations. You can read more about translations and directors inside of our documentation, but just to give you the high level understanding, we have an item. The item relates to episodes translations, which contains, a link to the item, a link to the target language, and the specific translated fields in that language for that item, and we have a languages collection. In this case, just English and German. We're assuming that content will always be authored in English, and then you will be translating it into German. But, of course, you can build more flexible workflows using prompt confirmation dialogues or looping flows or stuff like that. So our goal will be to have a button here translate to German and the German translation will be applied by creating a new item in the episodes translations collection. Let's create a new flow called translate to German, and this will be a manual trigger on the episodes page. So this returns the ID of the item where we click the button which is actually perfect for our use case. What we're gonna do is we we're now gonna kind of cross reference and instead of grabbing data from this item we're gonna go grab the existing English translation for this item, so we have the English base to translate. So we are gonna read the data from the episodes translations collection with the following filter. We want to make sure that the item is from this, the translation item is related to this episode item and that we are only grabbing the English. So we will hit save. So this should return, an array with 1 item in it which contains the translation. We are then gonna send this to the AI trans the AI translation operation. Now you will need a DeepL API key. Here's one I made earlier, and select which API plan you're on. And in here, we pass in some text. We could hard code it, but we actually want it from the previous operation. Transcript, trans script. Yeah, and we pick a target language that this operation supports. So this should return, this should return just a string not an object just a pure string of that content in German instead. And the final thing we want to do is create a new translation. So we will create data in the episodes translations collection, and the payload will be as follows. The episode ID needs to match the initial one, the language that we've translated to, so this is the language code for German, and the string inside of transcript. So we'll hit save on all of those. So just to reiterate, we click the button on the episode. We grab the existing English translation for that item. We translate it to German, and we create a new translation item. So let's go to our episode. Let's hit translate to German, and there is our German translation for the same content. Once again you can build really powerful workflows here by looping or maybe by allowing a specific language selection as part of a confirmation prompt dialogue. That is how the AI, text translator operation extension works. Hope you found this interesting. We'll see you next time.","68280a24-6880-4499-9100-3d2793c606b9",[3035],"497401ce-4880-4166-a825-4e1cdaadfbde",[],{"year":978,"number":158,"id":3010,"episodes":3038,"show":3039},[3012,2996,3013,3014,3015,3016,3017,3018,3019,3020,3021],{"title":3023,"slug":3024},{"id":3017,"slug":3041,"vimeo_id":3042,"description":3043,"tile":3044,"length":195,"resources":12,"people":12,"episode_number":129,"published":3001,"title":3045,"video_transcript_html":3046,"video_transcript_text":3047,"content":12,"seo":3048,"status":19,"episode_people":3049,"recommendations":3051,"season":3052},"ai-speech-transcription","945762260","Generate transcripts from audio files with this custom operation, powered by Deepgram.","cd890799-ed45-4240-a12f-d5790cc4c35a","AI Speech Transcription","\u003Cp>Speaker 0: The Directus AI speech generation operation allows you to create realistic sounding speech from text inside of your director's project. This extension is powered by Jenny, so you will need an API key in order to follow along. Now here I have a collection called ads, and I've created just a very short textual ad that I want to run for directors. However, it's gonna go in something like a podcast or a audiobook or something, so it needs to be in audio form. So what we're gonna do is create a manual flow where when we click a button here it will go off and generate that audio for us.\u003C\u002Fp>\u003Cp>So let's go over to our flows and create a new flow, generate audio, with a manual trigger on the ads collection. This will return just the ID of the item where the button is pressed. We obviously want the text. So the first thing we're gonna do is read data and we will read data from the ads collection, specifically the ID from the from the item whether it was pressed. So trigger dot body dot keys zero.\u003C\u002Fp>\u003Cp>So this will return the text and the currently empty file field. Then we can go straight away and send this off to the AI speech generation operation. Now we do need a Jenny API key, but I have one that I made earlier. And in here, we put in our text with a maximum of 500 characters. And I am gonna go ahead and just say, hey, that last operation that fetched all the data, go grab the text value out of it.\u003C\u002Fp>\u003Cp>You get to pick one of the selected speakers, so we'll use Brian Lee junior here and you get to specify the speed at which the, the speech happens. So this will return just a URL of an mp3 on the web. Next, we need to import it into this Director's project, and we can do that using the webhook request URL operation. So we'll make a post request to this directus URL trigger dotlws.i. So that's your specific project URL\u002Fassets, not assets, sorry, slash files slash import.\u003C\u002Fp>\u003Cp>That's the endpoint. It's a post request, and then the body here, we are going to just pass in the URL. Now just a note that for this project I've made file creation publicly accessible, you may need to lock down those permissions and provide your authentication headers here, but for ease I've turned that off for this demo. So what this will do is make that API call and return with the whole payload of the brand new director's files item that was just created, and what we wanna do is extract the ID and save that back to our ads item. So let's update the ads item here.\u003C\u002Fp>\u003Cp>We wanna update trigger.body.keys 0, and what we wanna do is just update the file value, and the location of the ID in the returned JSON payload is in data dot data dot ID. That is the whole flow. We click the button. We grab the whole item, which includes the text. We generate the text, which results in an m p 3.\u003C\u002Fp>\u003Cp>We import the m p 3 and save the m p 3 back to the item. So let's see if this works. Fingers crossed, we're now going and generating that audio. It's going off to that 3rd party vendor creating that realistic sounding audio for us, importing it, and then finally adding it here inside of the file except when it doesn't. Why is that?\u003C\u002Fp>\u003Cp>Let's take a little look together. Let's take a look at the log. What happened there? Trigger, because I got completely the wrong URL of just my director's project here. Tunnel is actually where this project is located.\u003C\u002Fp>\u003Cp>So let's run that one last time, generate audio. We'll give that just a moment once again to do its thing in that time I'll tell you a joke I'm not a funny person so I won't be doing that but that allows me to stall for long enough for this operation to complete and that of course is our, is our wav file, our actual audio file there containing our ad. So that is a little bit about how the, speech generation operation works using directus AI. We hope you enjoy it. I see loads of use cases for this, but the primary one being either like video voiceovers at scale or programmatic ads or stuff like that.\u003C\u002Fp>\u003Cp>So I hope you found this interesting. I'll see you in the next video.\u003C\u002Fp>","The Directus AI speech generation operation allows you to create realistic sounding speech from text inside of your director's project. This extension is powered by Jenny, so you will need an API key in order to follow along. Now here I have a collection called ads, and I've created just a very short textual ad that I want to run for directors. However, it's gonna go in something like a podcast or a audiobook or something, so it needs to be in audio form. So what we're gonna do is create a manual flow where when we click a button here it will go off and generate that audio for us. So let's go over to our flows and create a new flow, generate audio, with a manual trigger on the ads collection. This will return just the ID of the item where the button is pressed. We obviously want the text. So the first thing we're gonna do is read data and we will read data from the ads collection, specifically the ID from the from the item whether it was pressed. So trigger dot body dot keys zero. So this will return the text and the currently empty file field. Then we can go straight away and send this off to the AI speech generation operation. Now we do need a Jenny API key, but I have one that I made earlier. And in here, we put in our text with a maximum of 500 characters. And I am gonna go ahead and just say, hey, that last operation that fetched all the data, go grab the text value out of it. You get to pick one of the selected speakers, so we'll use Brian Lee junior here and you get to specify the speed at which the, the speech happens. So this will return just a URL of an mp3 on the web. Next, we need to import it into this Director's project, and we can do that using the webhook request URL operation. So we'll make a post request to this directus URL trigger dotlws.i. So that's your specific project URL\u002Fassets, not assets, sorry, slash files slash import. That's the endpoint. It's a post request, and then the body here, we are going to just pass in the URL. Now just a note that for this project I've made file creation publicly accessible, you may need to lock down those permissions and provide your authentication headers here, but for ease I've turned that off for this demo. So what this will do is make that API call and return with the whole payload of the brand new director's files item that was just created, and what we wanna do is extract the ID and save that back to our ads item. So let's update the ads item here. We wanna update trigger.body.keys 0, and what we wanna do is just update the file value, and the location of the ID in the returned JSON payload is in data dot data dot ID. That is the whole flow. We click the button. We grab the whole item, which includes the text. We generate the text, which results in an m p 3. We import the m p 3 and save the m p 3 back to the item. So let's see if this works. Fingers crossed, we're now going and generating that audio. It's going off to that 3rd party vendor creating that realistic sounding audio for us, importing it, and then finally adding it here inside of the file except when it doesn't. Why is that? Let's take a little look together. Let's take a look at the log. What happened there? Trigger, because I got completely the wrong URL of just my director's project here. Tunnel is actually where this project is located. So let's run that one last time, generate audio. We'll give that just a moment once again to do its thing in that time I'll tell you a joke I'm not a funny person so I won't be doing that but that allows me to stall for long enough for this operation to complete and that of course is our, is our wav file, our actual audio file there containing our ad. So that is a little bit about how the, speech generation operation works using directus AI. We hope you enjoy it. I see loads of use cases for this, but the primary one being either like video voiceovers at scale or programmatic ads or stuff like that. So I hope you found this interesting. I'll see you in the next video.","00e06324-0577-4399-ad49-ac9bdc4ce2cf",[3050],"4698e4b9-382b-4b95-93ab-86921d79f77d",[],{"year":978,"number":158,"id":3010,"episodes":3053,"show":3054},[3012,2996,3013,3014,3015,3016,3017,3018,3019,3020,3021],{"title":3023,"slug":3024},{"id":3019,"slug":3056,"vimeo_id":3057,"description":3058,"tile":3059,"length":159,"resources":12,"people":12,"episode_number":527,"published":3001,"title":3060,"video_transcript_html":3061,"video_transcript_text":3062,"content":12,"seo":3063,"status":19,"episode_people":3064,"recommendations":3066,"season":3067},"ai-text-intelligence","945762911","Analyze text intents, sentiment, topics, and generate a summary within with this custom operation, powered by Deepgram.","e9291721-20ad-4a6f-a6ea-e5158d7baf79","AI Text Intelligence","\u003Cp>Speaker 0: The Directus AI text intelligence operation allows you to analyze bodies of text and generate summaries, topics, intents, and sentiments powered by Deepgram. Today, I'm gonna show you how to set it up using a manual flow trigger. Inside of this episode's collection, we have items representing podcast episodes with a transcript of every word that is said inside of this podcast. What we're gonna do is set up a manual flow. So when we click a button here, it will generate a summary using the text intelligence operation.\u003C\u002Fp>\u003Cp>And, of course, we're gonna do it manually, but you could do this automatically on the creation of a new podcast or a new transcript, but we're gonna do it manually. So let's head over to our flows and create a new flow called summarise. We're gonna do this based on a manual trigger on the operations collection. Now whenever this runs, all we get is the ID of the item where the button was pressed. We obviously need the summary.\u003C\u002Fp>\u003Cp>We need the transcript rather. So the first thing we'll do is we will create a read data operation, and what we'll do is say in the episodes collection, we want to access trigger dot body dot keys 0. So this will return the whole object including the ID, the file ID, the transcript, and the currently empty summary field. So we're then gonna send this off to the AI text intelligence service here. And the first thing you'll need to pop in is a Deepgram API key.\u003C\u002Fp>\u003Cp>The second thing you need to put in is actually the text itself. Unfortunately, this will have been returned in the last operation, and it's called transcript. So we can just send that off like so. So this will go off. It will it will return summaries, topics, intents, and sentiments.\u003C\u002Fp>\u003Cp>And for topics intents and sentiments, it will kind of break that down for different portions of the text. And for sentiments, you'll also get a kind of average summary, which is really useful if you wanna say, you know, a call into your, you know, call center or a call with a prospect or something like that. What was the general sentiment from beginning to end? So as I said, this is gonna return quite a lot of data. We only care about the summary.\u003C\u002Fp>\u003Cp>And what we wanna do is actually save that summary back to the item where we clicked the button. So that was in episodes, and the ID was trigger dot body dot keys 0. And the payload, what we actually wanna update that item with is a summary field. So we wanna update the summary field, and we want the value of that to be last dot summary dot text, like so. So if we hit save, this is actually the whole operation, the whole, sorry, flow.\u003C\u002Fp>\u003Cp>So we click the button. We grab the whole item. We send it off to the text intelligence service, and then we save the outcome. Of course, you don't need to save it back to the item. You could simply send an email out or do something else with it, but we're gonna save it back to the item.\u003C\u002Fp>\u003Cp>And if all has worked, I should be able to click this summarize button, wait a few seconds. It's being summarized. Topics are being generated. Intents and sentiments, we're grabbing just the summary, and there we go. There's the summary.\u003C\u002Fp>\u003Cp>So I'm sure you can see all of the ways this can be applied. It's really flexible. It's really interesting. And, again, it's not just summaries. It's topics.\u003C\u002Fp>\u003Cp>It's intents, and it's sentiments. So I hope you found this interesting, and I'll see you in the next show.\u003C\u002Fp>","The Directus AI text intelligence operation allows you to analyze bodies of text and generate summaries, topics, intents, and sentiments powered by Deepgram. Today, I'm gonna show you how to set it up using a manual flow trigger. Inside of this episode's collection, we have items representing podcast episodes with a transcript of every word that is said inside of this podcast. What we're gonna do is set up a manual flow. So when we click a button here, it will generate a summary using the text intelligence operation. And, of course, we're gonna do it manually, but you could do this automatically on the creation of a new podcast or a new transcript, but we're gonna do it manually. So let's head over to our flows and create a new flow called summarise. We're gonna do this based on a manual trigger on the operations collection. Now whenever this runs, all we get is the ID of the item where the button was pressed. We obviously need the summary. We need the transcript rather. So the first thing we'll do is we will create a read data operation, and what we'll do is say in the episodes collection, we want to access trigger dot body dot keys 0. So this will return the whole object including the ID, the file ID, the transcript, and the currently empty summary field. So we're then gonna send this off to the AI text intelligence service here. And the first thing you'll need to pop in is a Deepgram API key. The second thing you need to put in is actually the text itself. Unfortunately, this will have been returned in the last operation, and it's called transcript. So we can just send that off like so. So this will go off. It will it will return summaries, topics, intents, and sentiments. And for topics intents and sentiments, it will kind of break that down for different portions of the text. And for sentiments, you'll also get a kind of average summary, which is really useful if you wanna say, you know, a call into your, you know, call center or a call with a prospect or something like that. What was the general sentiment from beginning to end? So as I said, this is gonna return quite a lot of data. We only care about the summary. And what we wanna do is actually save that summary back to the item where we clicked the button. So that was in episodes, and the ID was trigger dot body dot keys 0. And the payload, what we actually wanna update that item with is a summary field. So we wanna update the summary field, and we want the value of that to be last dot summary dot text, like so. So if we hit save, this is actually the whole operation, the whole, sorry, flow. So we click the button. We grab the whole item. We send it off to the text intelligence service, and then we save the outcome. Of course, you don't need to save it back to the item. You could simply send an email out or do something else with it, but we're gonna save it back to the item. And if all has worked, I should be able to click this summarize button, wait a few seconds. It's being summarized. Topics are being generated. Intents and sentiments, we're grabbing just the summary, and there we go. There's the summary. So I'm sure you can see all of the ways this can be applied. It's really flexible. It's really interesting. And, again, it's not just summaries. It's topics. It's intents, and it's sentiments. So I hope you found this interesting, and I'll see you in the next show.","0dccd3bf-652f-448e-b07e-9ea4222535da",[3065],"4504ff0d-9ed8-479c-b7e2-d1013cd35547",[],{"year":978,"number":158,"id":3010,"episodes":3068,"show":3069},[3012,2996,3013,3014,3015,3016,3017,3018,3019,3020,3021],{"title":3023,"slug":3024},{"id":3014,"slug":3071,"vimeo_id":3072,"description":3073,"tile":3074,"length":159,"resources":12,"people":12,"episode_number":159,"published":3001,"title":3075,"video_transcript_html":3076,"video_transcript_text":3077,"content":12,"seo":3078,"status":19,"episode_people":3079,"recommendations":3081,"season":3082},"ai-image-generation","945761480","Generate new images within Directus Automate with this custom operation, powered by OpenAI.","ad0578de-c6af-432b-aa4d-6749ee367e0e","AI Image Generation","\u003Cp>Speaker 0: The Directus AI image generation operation will take in a prompt and a couple of other settings and use OpenAI's DALL E 3 model to generate an image, which you can then save into your director's project. So here we have a recipes collection and an item with a recipe that I wrote and there is an empty field here for an image. Because I'm actually not a good chef we're gonna use AI to generate an image which we can then use on things like social media or the recipe website header, things like that. So, we are going to create a new flow, and we'll call this one generate image, and we'll make this a manual flow trigger on the recipes collection. So a button appears on the right.\u003C\u002Fp>\n\u003Cp>Now we are gonna manually give it a prompt because we don't have an amount of text suitable to send for an image generation, only a big recipe that's too long. So without a single field here, I'll just call it prompt. You might choose to call it something nicer, and we'll just make it a little text input here. Now that had a key of prompt, and what that means is whenever that button is pressed, we will receive the ID of the item where we press the button and also the prompt itself. And so we are gonna use that prompt and send it off for image generation.\u003C\u002Fp>\n\u003Cp>So we will go ahead and create a new operation here, and we will use the AI image generation operation. Now we need to use our open AI API key, we'll pop that in here and our prompt. And once again this is gonna be dynamic so trigger dot body dot prompt. We want high quality, standard quality will do, and we'll make it square so we can share it on our Instagram grid. So we'll hit save.\u003C\u002Fp>\n\u003Cp>This will result in having, a URL. So we now need to import that URL into our directors project. To do that, we will use the request URL operation, and we'll make a post request to this Directus project URL slash files slash import. Just a note that I've made image, file creation body, we are going to set the URL to the value that was returned from the last operation, which is just a URL string. So that will import the image to our file library.\u003C\u002Fp>\n\u003Cp>The final step is that we want to save that image into into the actual item where we triggered the flow to begin. So we're gonna add one final operation here, which is to update data. We are going to update the recipe specifically trigger dot body dot keys 0 which is the ID. And we are going to update the file value. File, and that is going to be last dot data dot data dot ID because that is a response for an API call.\u003C\u002Fp>\n\u003Cp>And that should be it. You should be able to press the button. It will come up with a box for the prompt. It will go and generate the image, import it, and save it back to the image. So let's try that out.\u003C\u002Fp>\n\u003Cp>Let's go in here. Let's generate image. I've got this rudimentary prompt written, and we'll run Flow on current item. Now image generation takes a little while so we'll come back when it's done. We're back and it did not work because it is not called file, it is called image.\u003C\u002Fp>\n\u003Cp>So we're gonna update that and we'll go back and run it again. Welcome back, and it turns out I'm not a very good engineer because that is not an easy stuffed bell pepper. It is a bowl stuffed with bell peppers. But in any case, you need to write better prompts but that is how you use the Directus AI image generation operation. I'll see you in the next video.\u003C\u002Fp>","The Director's AI image generation operation will take in a prompt and a couple of other settings and use OpenAI's DALL E 3 model to generate an image, which you can then save into your director's project. So here we have a recipes collection and an item with a recipe that I wrote and there is an empty field here for an image. Because I'm actually not a good chef we're gonna use AI to generate an image which we can then use on things like social media or the recipe website header, things like that. So, we are going to create a new flow, and we'll call this one generate image, and we'll make this a manual flow trigger on the recipes collection. So a button appears on the right. Now we are gonna manually give it a prompt because we don't have an amount of text suitable to send for an image generation, only a big recipe that's too long. So without a single field here, I'll just call it prompt. You might choose to call it something nicer, and we'll just make it a little text input here. Now that had a key of prompt, and what that means is whenever that button is pressed, we will receive the ID of the item where we press the button and also the prompt itself. And so we are gonna use that prompt and send it off for image generation. So we will go ahead and create a new operation here, and we will use the AI image generation operation. Now we need to use our open AI API key, we'll pop that in here and our prompt. And once again this is gonna be dynamic so trigger dot body dot prompt. We want high quality, standard quality will do, and we'll make it square so we can share it on our Instagram grid. So we'll hit save. This will result in having, a URL. So we now need to import that URL into our directors project. To do that, we will use the request URL operation, and we'll make a post request to this Directus project URL slash files slash import. Just a note that I've made image, file creation body, we are going to set the URL to the value that was returned from the last operation, which is just a URL string. So that will import the image to our file library. The final step is that we want to save that image into into the actual item where we triggered the flow to begin. So we're gonna add one final operation here, which is to update data. We are going to update the recipe specifically trigger dot body dot keys 0 which is the ID. And we are going to update the file value. File, and that is going to be last dot data dot data dot ID because that is a response for an API call. And that should be it. You should be able to press the button. It will come up with a box for the prompt. It will go and generate the image, import it, and save it back to the image. So let's try that out. Let's go in here. Let's generate image. I've got this rudimentary prompt written, and we'll run Flow on current item. Now image generation takes a little while so we'll come back when it's done. We're back and it did not work because it is not called file, it is called image. So we're gonna update that and we'll go back and run it again. Welcome back, and it turns out I'm not a very good engineer because that is not an easy stuffed bell pepper. It is a bowl stuffed with bell peppers. But in any case, you need to write better prompts but that is how you use the Directus AI image generation operation. I'll see you in the next video.","b6a9a85e-53cc-4086-b7cd-4096ba305a02",[3080],"d711f2a8-80dc-4f48-95c8-8b21e0171d66",[],{"year":978,"number":158,"id":3010,"episodes":3083,"show":3084},[3012,2996,3013,3014,3015,3016,3017,3018,3019,3020,3021],{"title":3023,"slug":3024},{"id":3013,"slug":3086,"vimeo_id":3087,"description":3088,"tile":3089,"length":159,"resources":12,"people":12,"episode_number":264,"published":3001,"title":3090,"video_transcript_html":3091,"video_transcript_text":3092,"content":12,"seo":3093,"status":19,"episode_people":3094,"recommendations":3096,"season":3097},"ai-focal-point-detection","945761249","Determine an image's primary point of interest with this custom operation, powered by OpenAI.","f93156b0-e977-44bf-8616-71ae745e47d9","AI Focal Point Detection","\u003Cp>Speaker 0: The AI focal point detection operation uses AI to determine the point of interest inside of an image. You can combine this with Directus' new focal point functionality to make sure that as images are automatically cropped, or cropped via API requests that your points of interests are are not lost in that cropping. Otherwise, by default, it happens from the center. So that's what we're gonna set up today. We're gonna do this whenever a new file is uploaded.\u003C\u002Fp>\u003Cp>So focal point. We're gonna do this whenever a new file is uploaded. So we will do files dot upload. This will run with every single file upload regardless of location, regardless of file types in the reel. You will want a condition just to check that this is indeed an image.\u003C\u002Fp>\u003Cp>The documentation for this operation in the marketplace, the listing page, shows you how to set that up. For now, we're gonna assume every file needs this treatment applied to it. So we are gonna go in, and we are gonna use our AI focal point detection end point. Now this uses OpenAI. So we have to put in our open AI API key and a full file URL.\u003C\u002Fp>\u003Cp>So first thing is our director's project URL, so that's mine, slash assets slash and then a dynamic value trigger dot key. That's the ID of the image that was uploaded and triggered this flow to actually start. So that will come back with an object, which contains a focal point x, a focal point y, and a reason. The reason could be interesting based on your use case. We're not gonna do anything with it.\u003C\u002Fp>\u003Cp>Instead, we're just gonna update the directives files item, specifically the item that triggered this whole flow to start. And we are going to update the focal point x value to be the value that came from the last operation focal point x. We're gonna do the same for focal point y. And, again, you know, you may choose to do something, you may choose to do something with the reason, but we are not going to. So focal point y and with Jason no hanging commas.\u003C\u002Fp>\u003Cp>That's the whole thing. That's how you use AI to set up focal points. It's really, really cool, and really leverages this new feature of Directus along with AI to just really make your media library a lot more expressive when you're using it in your actual applications. So let's try this out. I just uploaded a new image, that should have triggered that whole flow, the focal points to be, to be generated, and then save to that back to the focal point x and y value.\u003C\u002Fp>\u003Cp>And there it is. Now you don't need to do anything more with these values. When you start to crop the image using URL parameters, using the on the fly media transformations that directors provide, that will be the central point which the image is cropped around. So I hope you found this interesting, and I'll see you in the next video.\u003C\u002Fp>","The AI focal point detection operation uses AI to determine the point of interest inside of an image. You can combine this with Directus' new focal point functionality to make sure that as images are automatically cropped, or cropped via API requests that your points of interests are are not lost in that cropping. Otherwise, by default, it happens from the center. So that's what we're gonna set up today. We're gonna do this whenever a new file is uploaded. So focal point. We're gonna do this whenever a new file is uploaded. So we will do files dot upload. This will run with every single file upload regardless of location, regardless of file types in the reel. You will want a condition just to check that this is indeed an image. The documentation for this operation in the marketplace, the listing page, shows you how to set that up. For now, we're gonna assume every file needs this treatment applied to it. So we are gonna go in, and we are gonna use our AI focal point detection end point. Now this uses OpenAI. So we have to put in our open AI API key and a full file URL. So first thing is our director's project URL, so that's mine, slash assets slash and then a dynamic value trigger dot key. That's the ID of the image that was uploaded and triggered this flow to actually start. So that will come back with an object, which contains a focal point x, a focal point y, and a reason. The reason could be interesting based on your use case. We're not gonna do anything with it. Instead, we're just gonna update the directives files item, specifically the item that triggered this whole flow to start. And we are going to update the focal point x value to be the value that came from the last operation focal point x. We're gonna do the same for focal point y. And, again, you know, you may choose to do something, you may choose to do something with the reason, but we are not going to. So focal point y and with Jason no hanging commas. That's the whole thing. That's how you use AI to set up focal points. It's really, really cool, and really leverages this new feature of Directus along with AI to just really make your media library a lot more expressive when you're using it in your actual applications. So let's try this out. I just uploaded a new image, that should have triggered that whole flow, the focal points to be, to be generated, and then save to that back to the focal point x and y value. And there it is. Now you don't need to do anything more with these values. When you start to crop the image using URL parameters, using the on the fly media transformations that directors provide, that will be the central point which the image is cropped around. So I hope you found this interesting, and I'll see you in the next video.","d234c6c1-ee09-4895-a3cd-cc8081439197",[3095],"bd63b46a-18fc-4a22-9a56-a8020924b913",[],{"year":978,"number":158,"id":3010,"episodes":3098,"show":3099},[3012,2996,3013,3014,3015,3016,3017,3018,3019,3020,3021],{"title":3023,"slug":3024},{"id":1795,"slug":3101,"vimeo_id":3102,"description":3103,"tile":3104,"length":176,"resources":3105,"people":12,"episode_number":129,"published":3001,"title":3108,"video_transcript_html":3109,"video_transcript_text":3110,"content":12,"seo":3111,"status":19,"episode_people":3112,"recommendations":3116,"season":3117},"email-translations","944604881","In this recording of our live event on May 9 2024, Daniel, Jonathan, and Kevin discuss making email templates translatable","cf94e330-0c7e-4998-a002-a8df236def76",[3106],{"name":1775,"url":3107},"https:\u002F\u002Fgithub.com\u002Fdirectus\u002Fdirectus\u002Fdiscussions\u002F8239","Email Translations","\u003Cp>Speaker 0: Talk about what this feature is.\u003C\u002Fp>\u003Cp>Speaker 1: I'll I'll I'll in I'll intro because Daniel's gonna have to do all the hard hard work to do.\u003C\u002Fp>\u003Cp>Speaker 2: Oh, no.\u003C\u002Fp>\u003Cp>Speaker 1: So, we initially I think this was initially flagged as the make email templates translatable. So currently, especially for the system templates, not support translations on those. So things like your invite email, those are kind of hard coded as English only. You can do some things with the liquid templates, but even those are without some work are not really translatable. Mhmm.\u003C\u002Fp>\u003Cp>So the content, you have to do some variableization. Now with flows, I think with the advent of flows, you can do some things where you look up the data, pull your you know, based on content or other things, but be nice to make some modifications to the way that this platform is working to use either, the current translation strings or other capabilities, the translation solutions, interfaces on the platform itself, find a way to solve this problem so that emails can be multilingual more easily. So that's our goal. But I also saw that there I I found this that actually has an RFC spec. This was our our lovely CTO, mister Reich, creating this one liner, back in the day before we hardenforced RFCs.\u003C\u002Fp>\u003Cp>But then someone actually did a nice write up, and they referenced this particular ticket along with some other things. But they had some other capabilities on some event hooks and user invites and some things to actually extend the capabilities of the email solutions a little better. So we thought we'd use this as our our driving factor since it actually has RFC and it references the email translations. So that's the goal today, talk about how do we make the emails, the the platform emails, as well as other email things more easily translatable and easier to use and extend on the platform. Miss anything, Daniel?\u003C\u002Fp>\u003Cp>Anything here?\u003C\u002Fp>\u003Cp>Speaker 2: That's a nice, initial summary because, the pain of actually having a multilingual app, comes up quite quickly. As as soon as you got a couple different users from a couple different countries, you're you will run into this. So, this is largely only a problem for multilingual apps right now because, as of right now, you do have the ability to override our system email templates. So, technically, if you only have one language, you could just go into the, email template folder. And I even have looked up a link beforehand that I can share with the chat because I'm so organized, apparently, at least today.\u003C\u002Fp>\u003Cp>Speaker 1: Alright.\u003C\u002Fp>\u003Cp>Speaker 2: You may head on over to our docs under self hosted email templates, which will help you get started with how to do that. It's very, very simple. You have a template folder where you can put in the, the liquid template file, and you're basically done. Depending on what you want to do, of course, you know, you can make it more complicated. You can expand and expand on it, or add other functionality with your instance.\u003C\u002Fp>\u003Cp>But, technically, if you only have one language that you would like to serve, you can very easily do this. It's no problem. And even for the very, very, very brand new feature that was released this week, not sure how many of you guys have seen this because, I did a little thing with the help of the others, of course, because public registration also uses, an email template that you can override yourself. So that's the thing. You can expand on it, but as Jonathan said in the beginning, yeah, multi multilingual apps have a problem.\u003C\u002Fp>\u003Cp>Right? Like, how how do you do that as of right now? There's different, routes that you could go down on. Maybe just the the most most naive way would be to have a template per language that you just prefix with the ISO code of the language, for example. This would be the most naive way that I would not recommend, but, technically, we could do that just as a you know, to throw it in the room for discussion right now.\u003C\u002Fp>\u003Cp>So we could do that. I wouldn't do it that way, but, technically, we could.\u003C\u002Fp>\u003Cp>Speaker 0: One thing you mentioned, Jonathan, is you can kinda hack around with, if you build custom templates with, like, liquid tags. Do you have any more information about that? I'm not saying it would be an ideal approach, but how might that be approached now?\u003C\u002Fp>\u003Cp>Speaker 1: So liquid is just a, you know, a templating language, and you can you can grab templates in the flows as part of the email, service, that email notification service operation. And I believe if you supply variables, you can actually swap in the variables. So you could in in your Are you\u003C\u002Fp>\u003Cp>Speaker 0: using translation strings?\u003C\u002Fp>\u003Cp>Speaker 1: Look up the look up the multilingual contents, you know, store your your template data as a record using the current translations interface. So if we were to box here. I think in this particular demo, mail, settings, email templates. So if you were to set up, like, an email template like this. Right?\u003C\u002Fp>\u003Cp>So you can you could make this data driven, where instead of having this body here and the subject here and the name here, you could actually put those into a translation's interface, using the existing translations capabilities, then you manage your content. Now at this point, I can use a flow to grab this data. Now it doesn't work, I think, as Daniel's kind of referring to user invites. You'd have to do your own custom user invite and handle the, you know, on authentication or allow public registration kinds of things to do that. So to send a nonce you know, those are kind of system emails.\u003C\u002Fp>\u003Cp>Now we can override those with a liquid template. I'd have to play around. I think if we created a liquid template with variables in it, I think you could potentially use a flow to inject into that and swap in the multilingual from something like this, where I have the content lingually translated. But I don't have any testing.\u003C\u002Fp>\u003Cp>Speaker 0: I mean, you're talking about a naive way of doing this to be to have a template for each language, but why would we not simply store the contents of the email as a string in our translation files and then use that dynamic value? Surely, that's the the optimal way to do this. No?\u003C\u002Fp>\u003Cp>Speaker 1: That's That\u003C\u002Fp>\u003Cp>Speaker 2: would have been the better way that I wanted to to get into now.\u003C\u002Fp>\u003Cp>Speaker 0: Good.\u003C\u002Fp>\u003Cp>Speaker 2: So the like, in the beginning, I meant, like, the most naive way just to prefix every, email template with, like, enus.template. Liquid or whatever and then called it in every single language. Like, this is the most naive way that I could think of. But, as you might have guessed, by me emphasizing naive is it's very naive. It's not good because we have a very, very nice, crowd and integration for all of our translations for the app.\u003C\u002Fp>\u003Cp>So it would be very cool if we, instead of making every single template, store the languages directly, we would store like you suggested now, we would store every string inside that we want to translate very similar to our app translations. So we everybody from the community can what what's the what's the Crowded. Everybody can profit off of the, yeah, of the community's breadth and and width. Like, we have people coming from everywhere, every language. So it would be nice if we can do that.\u003C\u002Fp>\u003Cp>It's also for anyone that does not know this, head on over to no. I haven't. Oh, I have I have actually,\u003C\u002Fp>\u003Cp>Speaker 0: Locales. What what we're doing? Locales dot directors. Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: Yes. If in case anybody did not know this, you can head over to locales.directors.io, and you'll see our app translations. You can contribute, can add your own. Take a look. Yeah.\u003C\u002Fp>\u003Cp>This would be a better solution to the email problem. So we can, automatically generate and pull in those, from user contributions. That would be very nice.\u003C\u002Fp>\u003Cp>Speaker 0: But there is something, you know, digging below the surface. That'd be fine for system emails. Like, for system emails, that's totally fine. Just use these. But scratching one level below the surface is the ability to act is to use translation strings maybe in custom templates.\u003C\u002Fp>\u003Cp>That's the one level on because I feel like the solution to make email tran make email templates translatable is fine for system emails. Like, that we have an approach. We have a you know, we have something. We just need to not hard code the values in English. But there's more than that, I suppose.\u003C\u002Fp>\u003Cp>Speaker 1: So I think that was one of the comments that was in the one of these tickets already. I think that was in here somewhere. Somebody mentioned that using the translation strings. I think it was the right somewhere along the way. But that is an option.\u003C\u002Fp>\u003Cp>Right? So to me, either something like this. Right? So you can do this yourself with flows for for anything non system email, whether we do the system emails in the translation strings here or whether it's in the actual crowd in files, because it's a system email, you could technically actually have that in all of the languages. I guess, technical question, how do I know what language to send the email in?\u003C\u002Fp>\u003Cp>So when I'm using a user invite, I guess, you set the language for the user, the default language for the user. Is that how you would manage that?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. For invites, I think it's a special case because you don't know the person. You could, under specific circumstances, not know what the person is speaking technically. But, like, for other system emails, we have the user. It's they are already registered, so you can just pick the language from the user or the default language of the instance.\u003C\u002Fp>\u003Cp>But for invites, I think we should fall back to the, default language of the instance Yeah. But also provide the opportunity to set a specific language inside of the, pop up. Yeah. Yeah. Exactly.\u003C\u002Fp>\u003Cp>Exactly. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: We'd add we'd add language here as well. Right? So not just the you know, you'd choose choose your role or whatever you're inviting this user to, but also have a language and set it to the default for the project as the initial. If If they wanna change it, they can choose from one of the languages. And, therefore, those things exist in the crowd inside, but that's part of the you don't have to do any translations inside the app for that.\u003C\u002Fp>\u003Cp>That's not a dynamic kind of thing necessarily. Although, if I'm overriding, then I still want. So that's something that we have to think about as well. Right? So where does this get managed, and how does this get managed?\u003C\u002Fp>\u003Cp>Crowdin is awesome for system, but the reality is I may want to customize the actual liquid tablet for this. And as soon as I do that, I'm now I now fall back outside of how do I manage the translations.\u003C\u002Fp>\u003Cp>Speaker 0: You know what would be really cool actually is if in settings, there was a a section called email templates, and our default ones were there and not deletable, and you could create new ones, and then use the built in director's translation interface to enable that. Like, that feels like a really nice flexible way to also potentially stop people needing to build their own liquid templates, like, as separate files and then load them into a directory. For many use cases, just being a just having a WYSIWYG with, well, a WYSIWYG would be plenty. Maybe with, like, a yield or, you know So\u003C\u002Fp>\u003Cp>Speaker 1: it it's this. This is fundamentally what you need is a subject and a body. That's all you really have to have for email. For the text, put this in as a, you know I I think again, the the trouble with making it a system side is I I also kind of want content users to be able to do this. So I'd I'd want my marketing teams or my whoever's to be able to manage the content in these.\u003C\u002Fp>\u003Cp>So in a sense, it would be a system user table, possibly, that would show up that you could make accessible to the content side. You could optionally make it as accessible maybe. We don't really we don't really do that with anything today. We don't we don't create anything here other than the languages table gets created if you haven't created it yet, for translations interface. I\u003C\u002Fp>\u003Cp>Speaker 0: I question that notion to a degree, though, because if you look at things like creating and editing flows, you can make exactly the same argument there. You wanna expose flows to more people in your organization, and we don't do that. I don't see why like, I think that that would be a bigger conversation about what is exposed versus what isn't and to what degree. So I I would be at peace with the idea that that is an admin setting, but I do get it. It feels a little more editorial perhaps.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. That's a separate discussion then, really, I think. So one of because this actually came up this week. Somebody noted that if I make you know, I I'd like to be able to make some role editing capabilities accessible to a user, but not give them data model access or flow access or other things. So potentially being able to enable subsets of the settings for non admin users.\u003C\u002Fp>\u003Cp>So that's a separate discussion. But I do agree. I think email templates would be really awesome in here, so that I don't have to create liquid files or other things behind the scenes.\u003C\u002Fp>\u003Cp>Speaker 0: And you don't have access to the directory on professional cloud. That's the other big thing in my mind is, like, to really be a solution that works anywhere Directus is is hosted. It can't rely on dropping files in a persistent directory. It needs to be accessible via the data studio.\u003C\u002Fp>\u003Cp>Speaker 2: Some action in the chat. I envision being able to create custom email templates and sending it to a mailing service, specifically something something from directors. Could be separate thing, but would be good if it wasn't limited to settings page and flexible enough to support this.\u003C\u002Fp>\u003Cp>Speaker 1: The reality is, though, if I do want my own email templates, I could still create the user stuff for that, and I could mimic whatever was in the system. So, hopefully, over time, maybe there's additional system emails or things that we want for the platform. So we'd wanna be able to have those, and they could be manageable in the setting side if you wanted to replicate. Because Flows already supports, you know, grabbing things, you know, pull your variables, pull your data, pull your content. So, again, in this, like, agent c OS here, we have flows for, you know, sending the email.\u003C\u002Fp>\u003Cp>Right? So when you send the email, we're actually getting data from the trigger hooks and creates and posts and whatever, so we can pull in data from wherever. Right? So if there's data in a template or places that we wanna get it, we could do that. We can grab that ahead of the email.\u003C\u002Fp>\u003Cp>Speaker 0: Could you, show the drop down\u003C\u002Fp>\u003Cp>Speaker 2: link? Sorry.\u003C\u002Fp>\u003Cp>Speaker 0: Could you show the drop down again where it was marked down? What was the other option is attempt just in send email here. The other option is WYSIWYG or a template. Interesting. Because still in my mind, like, to be really flexible, you want templates to be able to have exactly data that is popped in there.\u003C\u002Fp>\u003Cp>So, yeah, I don't see why why you couldn't be authoring those indirectors.\u003C\u002Fp>\u003Cp>Speaker 2: You know?\u003C\u002Fp>\u003Cp>Speaker 1: I I think we should. I I think it would be awesome because having to go to liquid templates and have to have custom extension deployment capabilities or access to the server. Yeah. Not ideal. Right?\u003C\u002Fp>\u003Cp>So again, part of what I think this overall system flows discussion was about, in general. So moving away from the the simple thing that Raich put in here, this was a little bit more of handling this with some system flows and setups and management, being able to manage this from the front end as opposed to back end. So making it data driven. Right? The the things we enable for everyone else, let's enable this for ourselves and make this data driven.\u003C\u002Fp>\u003Cp>I love it.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I I really want to go into that, issue as well, the system flows for, for the system actions. But, first, I wanna finish the other, message from the chat that was with the mailing service. Like, I'm not sure if I understand correctly because, like, this should be possible, right, with just the environment variables. You just define which SMTP server you want to use as an email service so you can actually connect to something else.\u003C\u002Fp>\u003Cp>And if you override the template, that is doing what you just said. Or maybe maybe am I missing something?\u003C\u002Fp>\u003Cp>Speaker 0: Maybe the ability to send them to custom places per run. Like, there's a few it depends what level of flexibility or abstraction you want to build in, but at its core, that's what happens with the email service In flows, at least.\u003C\u002Fp>\u003Cp>Speaker 1: In flows? I don't play with it much, but I don't think you get to choose your mail service here. Right?\u003C\u002Fp>\u003Cp>Speaker 0: No. No. But you configure that for the project. And then when emails are sent using this, that's how they get\u003C\u002Fp>\u003Cp>Speaker 1: sent. But they get sent with 1 service, not multiple services. Right? Single services.\u003C\u002Fp>\u003Cp>Speaker 0: I'm not I'm not sure I see I mean, maybe I'm missing it, but I'm not sure I see the value in being able to set set up multiple. I don't even think you can. I'm not sure you can. It'll be in config options.\u003C\u002Fp>\u003Cp>Speaker 2: I mean, technically, because, like, most email services have specific rest APIs that you can just call. So you can probably just set up a webhook event Use a web. Operation thingy. Yep. Send over the the specific payload, and then the email service should take over.\u003C\u002Fp>\u003Cp>So, yeah, technically, should be doable.\u003C\u002Fp>\u003Cp>Speaker 1: Nice. But I think we're single email service Yeah. On the scenes or\u003C\u002Fp>\u003Cp>Speaker 2: Yes.\u003C\u002Fp>\u003Cp>Speaker 1: And I I don't actually know.\u003C\u002Fp>\u003Cp>Speaker 0: Hello. The hello. Hello. Hello, Duff. We could should talk because I also built my wedding website with, with Directus and built a set of automations around that.\u003C\u002Fp>\u003Cp>So how funny is that? I wrote a whole blog post about this. It's one of the earliest blog posts on the developer blog. If you wanna go take a look in our docs. Yeah.\u003C\u002Fp>\u003Cp>That's fun. Gosh. Nerds really do like to overcomplicate weddings. Then don't they slash we.\u003C\u002Fp>\u003Cp>Speaker 1: Developers are as bad as some of our I\u003C\u002Fp>\u003Cp>Speaker 0: mean, I have to deal with\u003C\u002Fp>\u003Cp>Speaker 1: complicating things. Right?\u003C\u002Fp>\u003Cp>Speaker 0: That's amazing. Yeah. That's so funny. Why should\u003C\u002Fp>\u003Cp>Speaker 2: I spend 15 minutes to do the thing that I need to do when I could spend 3 hours to automate the thing.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, no. No. It was good. We used it for I mean, a quick aside, but I will explain. So weddings have, like, quite a lot of variability to them.\u003C\u002Fp>\u003Cp>So we had if you consider a ticket. Right? A ticket. Right? An RSVP.\u003C\u002Fp>\u003Cp>So the first thing is you don't generally send, a ticket per person. You'll send it a group and the group will RSVP for individuals and they might not RSVP the same way, then we, at least, our ceremony could only hold like 80 people, but our reception had like 200 people at it. So, we had variable ticket types and then some ticket types allowed a plus one, some didn't, so all this variability. So backing that with data was really useful. So you had people, then you had invites of multiple people, and then you had RSVPs against the invite.\u003C\u002Fp>\u003Cp>And then we also used it for comms, so like blasting out emails or texts to everyone. That's how we did it. And we also used it to manage the shopping list as, because we DIY'd the wedding as people RSVP. So that's that's why it needed hours and hours and hours of, of time.\u003C\u002Fp>\u003Cp>Speaker 2: Just like Benny said, you just have to get married a couple times, then the effort was totally worse.\u003C\u002Fp>\u003Cp>Speaker 1: I mean, I'm never doing that again. One time was enough.\u003C\u002Fp>\u003Cp>Speaker 2: So, yeah, I think it's it's regarding the This is\u003C\u002Fp>\u003Cp>Speaker 1: year 30 for us.\u003C\u002Fp>\u003Cp>Speaker 2: Oh, damn. Nice.\u003C\u002Fp>\u003Cp>Speaker 1: My partner's been a long time. Congratulations.\u003C\u002Fp>\u003Cp>Speaker 2: So, Alright. Let's summarize a little bit because we have, you know, 2 issues. So the first one with the general email translatability is not that complex in itself, but should be doable also. And with Kevin's mentioned, like, translation strings, if we could use those, that would be very cool, very nice. With for the system emails, if we could reuse the crowd in, stuff or, like, the static hosting of the string so we can reuse, you know, services and stuff, that would also be very nice.\u003C\u002Fp>\u003Cp>And now I think like, if nobody has anything else regarding that\u003C\u002Fp>\u003Cp>Speaker 0: Could I just add could I could I add a question? Very interesting. Which is simply Sure. Of course. Can you not use translation strings in custom templates and custom liquid templates today?\u003C\u002Fp>\u003Cp>It feels like you should be able to. No. You can't. Okay. Fine.\u003C\u002Fp>\u003Cp>Speaker 2: I don't think you can.\u003C\u002Fp>\u003Cp>Speaker 1: That's not what I know of.\u003C\u002Fp>\u003Cp>Speaker 0: No. No. No. I I defer to you. I've not tried to do it.\u003C\u002Fp>\u003Cp>I made an assumption that it was supported. So, yeah, good to know.\u003C\u002Fp>\u003Cp>Speaker 2: As far as I know, like, the the only thing that you can do in there is, like, use data that's get passed in, you know, with the keys. So, you could do that with flows. Right? Like, if you would send if you could fetch some data or decide in a flow, like, what that specific e will hold. So, like, for an English user, it will hold hello, and for a German user, it would say, like, or something.\u003C\u002Fp>\u003Cp>Then you could reuse the same key, and it works, you know, for multiple languages. But you can't actually use the language thingies, the other thingies. Oh, yeah. And and another nice point from Tim. What about right to left languages?\u003C\u002Fp>\u003Cp>Speaker 0: Oh, they don't exist. We're gonna forget about right to left lang I mean, sure. But, you know, that that is a nuance in an implementation, I think. I think don't you set it on the HTML attributes? So you would just set it in the email.\u003C\u002Fp>\u003Cp>Speaker 2: I mean, it it look. I'm I'm really not sure because I don't speak or read languages that go from right to left.\u003C\u002Fp>\u003Cp>Speaker 0: But more but more than anything, we need to think about that.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Like like a button, like like a, verification button or something. Should it also be on the other side, for example, in the email template? So, like, not only text, but also, like, buttons. Do the does the language affect those as well?\u003C\u002Fp>\u003Cp>I think so. Right?\u003C\u002Fp>\u003Cp>Speaker 0: I think my my very limp with pleading complete ignorance here, I'm pretty sure basically whole UIs flip accordingly. But, yes, I think Tim Tim makes a good point though in that it would not simply be a translation string, but you would intrinsically need to know is this email a right to left or left to right email. And, you know, change the I think I genuinely think it's just an attribute in the HTML tag, but, you know, change that\u003C\u002Fp>\u003Cp>Speaker 2: accordingly. Yeah. But but in the HTML tag, for example, like, it would need to with, like, c s like, CSS in emails is always very tricky. Like, it's very nitpicky. Something works in an email client, something doesn't, and that's, like, super archaic.\u003C\u002Fp>\u003Cp>If if anybody had to deal with this, I'm sorry for you.\u003C\u002Fp>\u003Cp>Speaker 0: We support some right to left languages though. Right? Like, in the data studio.\u003C\u002Fp>\u003Cp>Speaker 2: I believe so. I have never used them, to be honest. So please don't look at me for further info in the context.\u003C\u002Fp>\u003Cp>Speaker 0: Given given that the default language collection gets created with We do.\u003C\u002Fp>\u003Cp>Speaker 1: We do.\u003C\u002Fp>\u003Cp>Speaker 2: Yep. Definitely.\u003C\u002Fp>\u003Cp>Speaker 1: So if you look, we have Arabic Yeah. For 1. So if you scroll where's my language Uh-oh. I don't know what\u003C\u002Fp>\u003Cp>Speaker 2: Don't lose the button.\u003C\u002Fp>\u003Cp>Speaker 1: I'm good enough navigating around. Worst case, I have DirecTV access to this guy. Fix it. But it's all good. So you get your Arabic.\u003C\u002Fp>\u003Cp>So we do have the translations for it.\u003C\u002Fp>\u003Cp>Speaker 0: It's individual strings, though. Yeah. Yeah. Exactly. Exactly.\u003C\u002Fp>\u003Cp>As Tim said here in the chat, I'm not convinced this is a true right to left implementation for languages. It likely will do, but will do is not necessarily the best or optimal. But in any case, that's just worth noticing too.\u003C\u002Fp>\u003Cp>Speaker 1: Would the reality actually be that this this should flip over here in the right to left? Like, the module bar, like, the whole screen would, like, invert? Oh, that makes my head hurt.\u003C\u002Fp>\u003Cp>Speaker 0: I I am not sure that's correct. So I I feel the need to add, I believe, the 3 of us here, plus Tim and the child Oh, Benny said, yes. Okay. Someone had a degree of confidence because I was like, if we've not done it, we don't know and there are degrees of implementation. Right?\u003C\u002Fp>\u003Cp>But, at the very least, consider the way people read left to right in terms of priority and flow. They would wanna be flipped.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. But we also have to keep in mind that, Benny is from Australia, so his layout is also different. His navigation is on the bottom, and everything appears on the top. It's flipped.\u003C\u002Fp>\u003Cp>Speaker 0: You know what would be so funny if, yeah, everything was upside down or, like, even subtle things like like the the spinner stand in the other direction like the toilet water.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Oh, that would be good. Alrighty. I think I think that's for for now, enough about emails because the other issues are also very interesting, and I think we should we should look into them.\u003C\u002Fp>\u003Cp>Speaker 1: Definitely. So sounds like we have a couple of solutions to think through on how we'd wanna implement this. I was looking, Kevin, with regards to your question. It does look like if you use a custom template, so so my custom template so if you have a custom liquid template, you can define variables inside that template. So first name, you then pass that as data from a flow.\u003C\u002Fp>\u003Cp>Now this is flow implementation of that template. But, technically\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I mean, technically, being able to and it being in any way optimal. There's a very, very wide chasm between the 2, and I think this is up on one end. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So once again, what you would do is you would have a body and a subject here that you were looking up from that translation user side content translation. So you'd have a user table with a translations interface, that has your subject and your body and just simply translate the whole thing. You could variabilize or anything that you wanted inside that template itself. Flows would have to handle these, you know, use a run script.\u003C\u002Fp>\u003Cp>I did something like this recently for page templates. So defining a page template, content gets built in the user tables, and then when the user's ready to say, you know, for my my location page template, generate the location page, substitute in. I've got a flow that looks for those variables, substitutes them in, generates the page instance for that location, and I'm standing for that. So you could do those kinds of things. But again, it's a lot of work upfront, for the developers and the, you know, the administrators.\u003C\u002Fp>\u003Cp>So Yeah. Figuring out a way to make this I think, again, the system making it accessible via the admin in some way, shape, or form, I think is the ideal solution. Like it. Agreed. Cool.\u003C\u002Fp>\u003Cp>Speaker 2: And check.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. System flows. What do you wanna talk about there, mister Daniel?\u003C\u002Fp>\u003Cp>Speaker 2: Oh, this this one is very cool. This is very interesting. It's, the idea and I'm I'm I I'm always very in favor of dogfooding, like, our application. So if we could use like, integrate flows deeper into our app itself, it would, like, in a way, force us to actually improve flows a little bit for ourselves. And so we got it's not I mean, really like them.\u003C\u002Fp>\u003Cp>Speaker 1: I know. We we we talked about flows 2.0 a little while back. Right? But yes. Go ahead.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. And, yeah. So this one is pretty interesting. So, the idea is that you replace our current, like, internal behavior of, like, what even happens if somebody presses, forgot password. You know, like like, for example, maybe I would like to send a couple emails.\u003C\u002Fp>\u003Cp>I would like to send one email to the to the admin and say, like, oh, warning. This user has, requested a password request or something, you know, just as a random example. You can also do, like,\u003C\u002Fp>\u003Cp>Speaker 1: more product updates. Record above and beyond what's in the system already. Maybe you've got soft compliance or ISO compliance things where you get that logged to a system somewhere. Yeah. That's not our app.\u003C\u002Fp>\u003Cp>Interesting.\u003C\u002Fp>\u003Cp>Speaker 2: Exactly. So maybe you want to customize this. And, I think, I haven't read the entire issue yet, but I think the idea was just to start with, like, invites and password, resets. But we can expand on this a little bit. And, you know, like, depending on how we structure this, how the user experience is, like, how do you override that?\u003C\u002Fp>\u003Cp>We have to have a fallback. What happens in the UI? And and you're not allowed to delete the thing. What happens if somebody deletes the thing? You know, like, there's there's a couple things that can go wrong, but, technically, I think this is very exciting and a very smart idea.\u003C\u002Fp>\u003Cp>I I really like this.\u003C\u002Fp>\u003Cp>Speaker 1: Clever. Well written too. Well defined. Well thought through. Linking to related concepts and tickets.\u003C\u002Fp>\u003Cp>David David Zacharias. Excellent. Excellent work. I love the\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Read only read only flows. Must have read only flows AKA system flows. Additional which kinda makes sense here. You have system collections and you can extend system collections as well.\u003C\u002Fp>\u003Cp>So it could be that thing where it's like, hey, you know what? These first two steps or the first and last, you can't change those. You can either put stuff in the middle or stuff at the end, kind of like you can do with fields today in collections. So Mhmm. The core behavior stays exactly the same that you can tack on.\u003C\u002Fp>\u003Cp>I would be more inclined then to just have an event, though, that an event based hook can hook into.\u003C\u002Fp>\u003Cp>Speaker 2: I don't know. No. That's a good point, actually.\u003C\u002Fp>\u003Cp>Speaker 0: Because if you have read only flows and you have these new additional scopes, then they kind of negate the need for one another, I think.\u003C\u002Fp>\u003Cp>Speaker 2: Now that that's a good point. Yeah. That is a good point.\u003C\u002Fp>\u003Cp>Speaker 0: And our paradigm today is\u003C\u002Fp>\u003Cp>Speaker 2: because, like, oh, let me based. Even if somebody wants to just break the flow, for example, maybe somebody doesn't want the, like, even the functionality to to be there. You know? Like, maybe you want to forbid actually password requests, like, reset requests.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Or you just want it to come to the internal team and never go back out to them automatically. Like, that is a a managed process. Yeah. Interesting.\u003C\u002Fp>\u003Cp>Yep.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Also Disabling your Reasonable to me.\u003C\u002Fp>\u003Cp>Speaker 1: Disabling a password reset? Interesting.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I mean, for you know, it's it's always such a, you know, contrived example and stuff, but take like, there's so many different environments and and regulations and\u003C\u002Fp>\u003Cp>Speaker 0: Get in touch with your system administrator. Managers and Your IT team\u003C\u002Fp>\u003Cp>Speaker 2: or whatever. For example. Yeah. So I can definitely see the use case there. And and this is, like like, super interesting to me.\u003C\u002Fp>\u003Cp>Speaker 0: The the the lowest\u003C\u002Fp>\u003Cp>Speaker 2: Like, I have not thought about this.\u003C\u002Fp>\u003Cp>Speaker 0: Neither have I. The lowest hanging fruit version of this is additional scopes that allow you to execute additional logic on top of the core logic for things like inviting and password resets. These I I almost feel like that is a separate thing, which is smaller and much easier to to implement. Pop that to the side. The system flows is is diff is the thing here.\u003C\u002Fp>\u003Cp>I just find that in that addition interesting.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. Nope. And so, again, it was extensible, you know, in the sense of it'd be nice to have the translation capability, but also, again, move. We're gonna touch some of these things, potentially moving some of that logic to where it's more accessible or modifiable.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, to modify the business logic, users can duplicate the flow and adapt to their needs. The system flow would then be inactive, but could be activated anytime to restore the default functionality. That UX, for me, does not feel good, but it is an interesting way to think about it.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I I think\u003C\u002Fp>\u003Cp>Speaker 1: Well, the system side scope\u003C\u002Fp>\u003Cp>Speaker 2: in the event yeah. If you can just, like, have a filter hook, and that would do the thing. Like, you can then cancel. Like, if you throw, the hook will be canceled, and then the email won't be sent out. And if you just pass it through, the normal behavior will take place, which is just sending an email.\u003C\u002Fp>\u003Cp>Right? Like, and you can do stuff in between. Yeah. I feel like this would be a way better solution than having, like, read only flows and copying and then, you know, fallbacks and something is missing and whatever. Like, maybe just\u003C\u002Fp>\u003Cp>Speaker 0: And the And the critical thing is it matches the paradigms we have today. We have event based, event based triggers, and then we have actions and filters which run before or after the the operation happens, the the database operation happens. And yeah. So that feels that feels kinda better to me. And, yeah, you can always find a way to break it out of completing.\u003C\u002Fp>\u003Cp>And if you're if it's blocking, then the end part never happens.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Oh, this is cool. If somebody could work on this, that would be nice. This is pretty cool.\u003C\u002Fp>\u003Cp>Speaker 1: Well, that's why we're reviewing so we can Could we determine, does it make sense and what additional things do we need to think through. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Can we split this into 2 feature requests? Because that core nugget we've discussed is feeling very viable. Also, it feels like not a huge lift. And it is not system. It is not system flows.\u003C\u002Fp>\u003Cp>It is the introduction of new events, and then system flows is is something in and of its own. Right. I suppose.\u003C\u002Fp>\u003Cp>Speaker 2: But, like, if we have that event, like, do we even need system flows then?\u003C\u002Fp>\u003Cp>Speaker 0: I I I would argue not. I I would argue not. But that isn't what this. It's hard because I don't feel inclined to change people's feature requests into the 10% one liner that they've introduced, in it. But that nugget can be dealt with in isolation.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Saw that.\u003C\u002Fp>\u003Cp>Speaker 2: Okay. So so that that's that's pretty neat. That would be nice. Yeah. Very cool.\u003C\u002Fp>\u003Cp>Yeah. Then okay. So for the events, then there would be a password request reset request. I always\u003C\u002Fp>\u003Cp>Speaker 0: It's a reset it's a reset request and then a reset.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. And a oh, oh, right. Those are 2 things. Okay. The the request and the actual reset.\u003C\u002Fp>\u003Cp>Maybe you want to act on that too. Okay. Those 2. The invite would be the 3rd.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, I'm hang on. So I can just tell you because I've just written this section of our new documentation. So there is invite request and invite accept. Right, actually creating a user off the back of an invite. Then there is, password reset request and password reset.\u003C\u002Fp>\u003Cp>And then there's a set maybe around SSO specifically, but maybe not. They are that out. And then if we're talking about if we're thinking about the whole breadth of kind of off, there is also 2fa on off. Like, they are the that is the set of kind of operations that exist beyond just logging in, locking out, logging out, refreshing.\u003C\u002Fp>\u003Cp>Speaker 2: And since this week, there will be maybe coming then, register request and register request accepted, maybe?\u003C\u002Fp>\u003Cp>Speaker 0: Yes.\u003C\u002Fp>\u003Cp>Speaker 2: Something. Ah, and the email verification might also be interesting. Somebody might need that.\u003C\u002Fp>\u003Cp>Speaker 0: I would sooner add a a a, you know, like, a bundle of new events and go, well, there's the bit the biggest degree of flexibility for\u003C\u002Fp>\u003Cp>Speaker 2: That would be neat. Okay. Yeah. This this this sounds very exciting. This sounds actually very cool to me.\u003C\u002Fp>\u003Cp>Because we introduced to also new events for the content versioning. There was the promotion Promotion. Or promote or promotion, which is also useful. So this doesn't sound that far and unreasonable. This sounds very reasonable, actually.\u003C\u002Fp>\u003Cp>Speaker 0: So, Jonathan, so while you're typing, there's a there's a couple more. So there is invite, and then there's invite accept. There's password reset request and password reset, And there is 2 FA on and off. And they are the only 2 in my mind that I'm not sure how what like, I'm not sure what the impact of adding those in particular are, but they are there as well. And then I don't I don't have enough knowledge around single sign on to be confident in in listing events around that.\u003C\u002Fp>\u003Cp>Speaker 2: Cool.\u003C\u002Fp>\u003Cp>Speaker 1: Yep.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And, again, being able to extend being able to extend these would be really powerful. Right? Because if we've got a lot of clients that run us as a SaaS, we operate as a SaaS. There are system and service emails that, again, we do it through user templates and other things today, the way that we define and the way we've talked about.\u003C\u002Fp>\u003Cp>But making this kind of built in where you can create and manage your own system emails and service emails, would love it.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And the the thing is we already because we this only were basically, taking this feature request around or the other feature request rather, which is, oh, sorry. I see you're in this issue. Sorry. We're in the wrong one.\u003C\u002Fp>\u003Cp>Sorry. So emails that go out are not all of those. Sorry. Emails are just the, the yes. The password reset request and the invite.\u003C\u002Fp>\u003Cp>They are the only 2 system emails. Sorry. They are the auth events, though. Or the the potential auth you know, they are the points at which we could implement events. The fact that we already have filters and actions as blocking and non blocking, you know, code, you know, logic business logic means immediately you can effectively extend Directus' core functionality without needing to do anything else but expose these new events, which is really cool.\u003C\u002Fp>\u003Cp>Speaker 2: Yes. Agreed.\u003C\u002Fp>\u003Cp>Speaker 0: It's nice when you have a system that kind of already has the building blocks. Right? There's no new building blocks that need to be built for this one.\u003C\u002Fp>\u003Cp>Speaker 2: Yes. Only the integration and making sure that we can actually, like, cancel, for example, like, what happens if if the thing canceled and stuff. But other than that, you're completely right. Yes. So just as a reminder for everybody else in the chat because there's still a couple people here, the show will be over very soon.\u003C\u002Fp>\u003Cp>So if you have any questions that you would like to ask, now is the time. Now is the time.\u003C\u002Fp>\u003Cp>Speaker 1: Quiet group today.\u003C\u002Fp>\u003Cp>Speaker 0: Nothing wrong with that at all.\u003C\u002Fp>\u003Cp>Speaker 2: Alright. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Means maybe we did our job right.\u003C\u002Fp>\u003Cp>Speaker 0: And and not right any other\u003C\u002Fp>\u003Cp>Speaker 1: time.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That's really cool. I like the idea of system system flows. I, ultimately, like the idea of that, of exposing a set of functionality as flows in a UI that could be extended on the end. But I I, personally believe it's just simply unnecessary.\u003C\u002Fp>\u003Cp>It's just exposing the new events. And with actions and filters, it facilitates, I think, the goals of this.\u003C\u002Fp>\u003Cp>Speaker 2: Yep. Yeah. Exactly. I fully agree. So people let your let your questions out in last minute.\u003C\u002Fp>\u003Cp>1, 2. Yeah. If you have enjoyed this, or want to partake in the next one, you take a look in Discord in the events. You will see that we will be doing another episode on the 23rd May Yeah. Will be about configurable API errors.\u003C\u002Fp>\u003Cp>So errors are always very important. You might wanna let your voice be heard there. So please join in. Tune in if you like. And if you enjoyed this and want to revisit this or other episodes, head on over to directors.iotv.\u003C\u002Fp>\u003Cp>And, there will be lots of other shows as well, which are quite interesting and fun. So please check it out. Let's say it again, directors.io\u002Ftv.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. We hope\u003C\u002Fp>\u003Cp>Speaker 2: you enjoyed. Oh, there's the link.\u003C\u002Fp>\u003Cp>Speaker 0: Thank you. And all other episodes of request review bar the last one because I messed up the recording, you can find right there.\u003C\u002Fp>","Talk about what this feature is. I'll I'll I'll in I'll intro because Daniel's gonna have to do all the hard hard work to do. Oh, no. So, we initially I think this was initially flagged as the make email templates translatable. So currently, especially for the system templates, not support translations on those. So things like your invite email, those are kind of hard coded as English only. You can do some things with the liquid templates, but even those are without some work are not really translatable. Mhmm. So the content, you have to do some variableization. Now with flows, I think with the advent of flows, you can do some things where you look up the data, pull your you know, based on content or other things, but be nice to make some modifications to the way that this platform is working to use either, the current translation strings or other capabilities, the translation solutions, interfaces on the platform itself, find a way to solve this problem so that emails can be multilingual more easily. So that's our goal. But I also saw that there I I found this that actually has an RFC spec. This was our our lovely CTO, mister Reich, creating this one liner, back in the day before we hardenforced RFCs. But then someone actually did a nice write up, and they referenced this particular ticket along with some other things. But they had some other capabilities on some event hooks and user invites and some things to actually extend the capabilities of the email solutions a little better. So we thought we'd use this as our our driving factor since it actually has RFC and it references the email translations. So that's the goal today, talk about how do we make the emails, the the platform emails, as well as other email things more easily translatable and easier to use and extend on the platform. Miss anything, Daniel? Anything here? That's a nice, initial summary because, the pain of actually having a multilingual app, comes up quite quickly. As as soon as you got a couple different users from a couple different countries, you're you will run into this. So, this is largely only a problem for multilingual apps right now because, as of right now, you do have the ability to override our system email templates. So, technically, if you only have one language, you could just go into the, email template folder. And I even have looked up a link beforehand that I can share with the chat because I'm so organized, apparently, at least today. Alright. You may head on over to our docs under self hosted email templates, which will help you get started with how to do that. It's very, very simple. You have a template folder where you can put in the, the liquid template file, and you're basically done. Depending on what you want to do, of course, you know, you can make it more complicated. You can expand and expand on it, or add other functionality with your instance. But, technically, if you only have one language that you would like to serve, you can very easily do this. It's no problem. And even for the very, very, very brand new feature that was released this week, not sure how many of you guys have seen this because, I did a little thing with the help of the others, of course, because public registration also uses, an email template that you can override yourself. So that's the thing. You can expand on it, but as Jonathan said in the beginning, yeah, multi multilingual apps have a problem. Right? Like, how how do you do that as of right now? There's different, routes that you could go down on. Maybe just the the most most naive way would be to have a template per language that you just prefix with the ISO code of the language, for example. This would be the most naive way that I would not recommend, but, technically, we could do that just as a you know, to throw it in the room for discussion right now. So we could do that. I wouldn't do it that way, but, technically, we could. One thing you mentioned, Jonathan, is you can kinda hack around with, if you build custom templates with, like, liquid tags. Do you have any more information about that? I'm not saying it would be an ideal approach, but how might that be approached now? So liquid is just a, you know, a templating language, and you can you can grab templates in the flows as part of the email, service, that email notification service operation. And I believe if you supply variables, you can actually swap in the variables. So you could in in your Are you using translation strings? Look up the look up the multilingual contents, you know, store your your template data as a record using the current translations interface. So if we were to box here. I think in this particular demo, mail, settings, email templates. So if you were to set up, like, an email template like this. Right? So you can you could make this data driven, where instead of having this body here and the subject here and the name here, you could actually put those into a translation's interface, using the existing translations capabilities, then you manage your content. Now at this point, I can use a flow to grab this data. Now it doesn't work, I think, as Daniel's kind of referring to user invites. You'd have to do your own custom user invite and handle the, you know, on authentication or allow public registration kinds of things to do that. So to send a nonce you know, those are kind of system emails. Now we can override those with a liquid template. I'd have to play around. I think if we created a liquid template with variables in it, I think you could potentially use a flow to inject into that and swap in the multilingual from something like this, where I have the content lingually translated. But I don't have any testing. I mean, you're talking about a naive way of doing this to be to have a template for each language, but why would we not simply store the contents of the email as a string in our translation files and then use that dynamic value? Surely, that's the the optimal way to do this. No? That's That would have been the better way that I wanted to to get into now. Good. So the like, in the beginning, I meant, like, the most naive way just to prefix every, email template with, like, enus.template. Liquid or whatever and then called it in every single language. Like, this is the most naive way that I could think of. But, as you might have guessed, by me emphasizing naive is it's very naive. It's not good because we have a very, very nice, crowd and integration for all of our translations for the app. So it would be very cool if we, instead of making every single template, store the languages directly, we would store like you suggested now, we would store every string inside that we want to translate very similar to our app translations. So we everybody from the community can what what's the what's the Crowded. Everybody can profit off of the, yeah, of the community's breadth and and width. Like, we have people coming from everywhere, every language. So it would be nice if we can do that. It's also for anyone that does not know this, head on over to no. I haven't. Oh, I have I have actually, Locales. What what we're doing? Locales dot directors. Yeah. Yes. If in case anybody did not know this, you can head over to locales.directors.io, and you'll see our app translations. You can contribute, can add your own. Take a look. Yeah. This would be a better solution to the email problem. So we can, automatically generate and pull in those, from user contributions. That would be very nice. But there is something, you know, digging below the surface. That'd be fine for system emails. Like, for system emails, that's totally fine. Just use these. But scratching one level below the surface is the ability to act is to use translation strings maybe in custom templates. That's the one level on because I feel like the solution to make email tran make email templates translatable is fine for system emails. Like, that we have an approach. We have a you know, we have something. We just need to not hard code the values in English. But there's more than that, I suppose. So I think that was one of the comments that was in the one of these tickets already. I think that was in here somewhere. Somebody mentioned that using the translation strings. I think it was the right somewhere along the way. But that is an option. Right? So to me, either something like this. Right? So you can do this yourself with flows for for anything non system email, whether we do the system emails in the translation strings here or whether it's in the actual crowd in files, because it's a system email, you could technically actually have that in all of the languages. I guess, technical question, how do I know what language to send the email in? So when I'm using a user invite, I guess, you set the language for the user, the default language for the user. Is that how you would manage that? Yeah. For invites, I think it's a special case because you don't know the person. You could, under specific circumstances, not know what the person is speaking technically. But, like, for other system emails, we have the user. It's they are already registered, so you can just pick the language from the user or the default language of the instance. But for invites, I think we should fall back to the, default language of the instance Yeah. But also provide the opportunity to set a specific language inside of the, pop up. Yeah. Yeah. Exactly. Exactly. Yeah. We'd add we'd add language here as well. Right? So not just the you know, you'd choose choose your role or whatever you're inviting this user to, but also have a language and set it to the default for the project as the initial. If If they wanna change it, they can choose from one of the languages. And, therefore, those things exist in the crowd inside, but that's part of the you don't have to do any translations inside the app for that. That's not a dynamic kind of thing necessarily. Although, if I'm overriding, then I still want. So that's something that we have to think about as well. Right? So where does this get managed, and how does this get managed? Crowdin is awesome for system, but the reality is I may want to customize the actual liquid tablet for this. And as soon as I do that, I'm now I now fall back outside of how do I manage the translations. You know what would be really cool actually is if in settings, there was a a section called email templates, and our default ones were there and not deletable, and you could create new ones, and then use the built in director's translation interface to enable that. Like, that feels like a really nice flexible way to also potentially stop people needing to build their own liquid templates, like, as separate files and then load them into a directory. For many use cases, just being a just having a WYSIWYG with, well, a WYSIWYG would be plenty. Maybe with, like, a yield or, you know So it it's this. This is fundamentally what you need is a subject and a body. That's all you really have to have for email. For the text, put this in as a, you know I I think again, the the trouble with making it a system side is I I also kind of want content users to be able to do this. So I'd I'd want my marketing teams or my whoever's to be able to manage the content in these. So in a sense, it would be a system user table, possibly, that would show up that you could make accessible to the content side. You could optionally make it as accessible maybe. We don't really we don't really do that with anything today. We don't we don't create anything here other than the languages table gets created if you haven't created it yet, for translations interface. I I question that notion to a degree, though, because if you look at things like creating and editing flows, you can make exactly the same argument there. You wanna expose flows to more people in your organization, and we don't do that. I don't see why like, I think that that would be a bigger conversation about what is exposed versus what isn't and to what degree. So I I would be at peace with the idea that that is an admin setting, but I do get it. It feels a little more editorial perhaps. Okay. That's a separate discussion then, really, I think. So one of because this actually came up this week. Somebody noted that if I make you know, I I'd like to be able to make some role editing capabilities accessible to a user, but not give them data model access or flow access or other things. So potentially being able to enable subsets of the settings for non admin users. So that's a separate discussion. But I do agree. I think email templates would be really awesome in here, so that I don't have to create liquid files or other things behind the scenes. And you don't have access to the directory on professional cloud. That's the other big thing in my mind is, like, to really be a solution that works anywhere Directus is is hosted. It can't rely on dropping files in a persistent directory. It needs to be accessible via the data studio. Some action in the chat. I envision being able to create custom email templates and sending it to a mailing service, specifically something something from directors. Could be separate thing, but would be good if it wasn't limited to settings page and flexible enough to support this. The reality is, though, if I do want my own email templates, I could still create the user stuff for that, and I could mimic whatever was in the system. So, hopefully, over time, maybe there's additional system emails or things that we want for the platform. So we'd wanna be able to have those, and they could be manageable in the setting side if you wanted to replicate. Because Flows already supports, you know, grabbing things, you know, pull your variables, pull your data, pull your content. So, again, in this, like, agent c OS here, we have flows for, you know, sending the email. Right? So when you send the email, we're actually getting data from the trigger hooks and creates and posts and whatever, so we can pull in data from wherever. Right? So if there's data in a template or places that we wanna get it, we could do that. We can grab that ahead of the email. Could you, show the drop down link? Sorry. Could you show the drop down again where it was marked down? What was the other option is attempt just in send email here. The other option is WYSIWYG or a template. Interesting. Because still in my mind, like, to be really flexible, you want templates to be able to have exactly data that is popped in there. So, yeah, I don't see why why you couldn't be authoring those indirectors. You know? I I think we should. I I think it would be awesome because having to go to liquid templates and have to have custom extension deployment capabilities or access to the server. Yeah. Not ideal. Right? So again, part of what I think this overall system flows discussion was about, in general. So moving away from the the simple thing that Raich put in here, this was a little bit more of handling this with some system flows and setups and management, being able to manage this from the front end as opposed to back end. So making it data driven. Right? The the things we enable for everyone else, let's enable this for ourselves and make this data driven. I love it. Yeah. I I really want to go into that, issue as well, the system flows for, for the system actions. But, first, I wanna finish the other, message from the chat that was with the mailing service. Like, I'm not sure if I understand correctly because, like, this should be possible, right, with just the environment variables. You just define which SMTP server you want to use as an email service so you can actually connect to something else. And if you override the template, that is doing what you just said. Or maybe maybe am I missing something? Maybe the ability to send them to custom places per run. Like, there's a few it depends what level of flexibility or abstraction you want to build in, but at its core, that's what happens with the email service In flows, at least. In flows? I don't play with it much, but I don't think you get to choose your mail service here. Right? No. No. But you configure that for the project. And then when emails are sent using this, that's how they get sent. But they get sent with 1 service, not multiple services. Right? Single services. I'm not I'm not sure I see I mean, maybe I'm missing it, but I'm not sure I see the value in being able to set set up multiple. I don't even think you can. I'm not sure you can. It'll be in config options. I mean, technically, because, like, most email services have specific rest APIs that you can just call. So you can probably just set up a webhook event Use a web. Operation thingy. Yep. Send over the the specific payload, and then the email service should take over. So, yeah, technically, should be doable. Nice. But I think we're single email service Yeah. On the scenes or Yes. And I I don't actually know. Hello. The hello. Hello. Hello, Duff. We could should talk because I also built my wedding website with, with Directus and built a set of automations around that. So how funny is that? I wrote a whole blog post about this. It's one of the earliest blog posts on the developer blog. If you wanna go take a look in our docs. Yeah. That's fun. Gosh. Nerds really do like to overcomplicate weddings. Then don't they slash we. Developers are as bad as some of our I mean, I have to deal with complicating things. Right? That's amazing. Yeah. That's so funny. Why should I spend 15 minutes to do the thing that I need to do when I could spend 3 hours to automate the thing. Oh, no. No. It was good. We used it for I mean, a quick aside, but I will explain. So weddings have, like, quite a lot of variability to them. So we had if you consider a ticket. Right? A ticket. Right? An RSVP. So the first thing is you don't generally send, a ticket per person. You'll send it a group and the group will RSVP for individuals and they might not RSVP the same way, then we, at least, our ceremony could only hold like 80 people, but our reception had like 200 people at it. So, we had variable ticket types and then some ticket types allowed a plus one, some didn't, so all this variability. So backing that with data was really useful. So you had people, then you had invites of multiple people, and then you had RSVPs against the invite. And then we also used it for comms, so like blasting out emails or texts to everyone. That's how we did it. And we also used it to manage the shopping list as, because we DIY'd the wedding as people RSVP. So that's that's why it needed hours and hours and hours of, of time. Just like Benny said, you just have to get married a couple times, then the effort was totally worse. I mean, I'm never doing that again. One time was enough. So, yeah, I think it's it's regarding the This is year 30 for us. Oh, damn. Nice. My partner's been a long time. Congratulations. So, Alright. Let's summarize a little bit because we have, you know, 2 issues. So the first one with the general email translatability is not that complex in itself, but should be doable also. And with Kevin's mentioned, like, translation strings, if we could use those, that would be very cool, very nice. With for the system emails, if we could reuse the crowd in, stuff or, like, the static hosting of the string so we can reuse, you know, services and stuff, that would also be very nice. And now I think like, if nobody has anything else regarding that Could I just add could I could I add a question? Very interesting. Which is simply Sure. Of course. Can you not use translation strings in custom templates and custom liquid templates today? It feels like you should be able to. No. You can't. Okay. Fine. I don't think you can. That's not what I know of. No. No. No. I I defer to you. I've not tried to do it. I made an assumption that it was supported. So, yeah, good to know. As far as I know, like, the the only thing that you can do in there is, like, use data that's get passed in, you know, with the keys. So, you could do that with flows. Right? Like, if you would send if you could fetch some data or decide in a flow, like, what that specific e will hold. So, like, for an English user, it will hold hello, and for a German user, it would say, like, or something. Then you could reuse the same key, and it works, you know, for multiple languages. But you can't actually use the language thingies, the other thingies. Oh, yeah. And and another nice point from Tim. What about right to left languages? Oh, they don't exist. We're gonna forget about right to left lang I mean, sure. But, you know, that that is a nuance in an implementation, I think. I think don't you set it on the HTML attributes? So you would just set it in the email. I mean, it it look. I'm I'm really not sure because I don't speak or read languages that go from right to left. But more but more than anything, we need to think about that. Yeah. Like like a button, like like a, verification button or something. Should it also be on the other side, for example, in the email template? So, like, not only text, but also, like, buttons. Do the does the language affect those as well? I think so. Right? I think my my very limp with pleading complete ignorance here, I'm pretty sure basically whole UIs flip accordingly. But, yes, I think Tim Tim makes a good point though in that it would not simply be a translation string, but you would intrinsically need to know is this email a right to left or left to right email. And, you know, change the I think I genuinely think it's just an attribute in the HTML tag, but, you know, change that accordingly. Yeah. But but in the HTML tag, for example, like, it would need to with, like, c s like, CSS in emails is always very tricky. Like, it's very nitpicky. Something works in an email client, something doesn't, and that's, like, super archaic. If if anybody had to deal with this, I'm sorry for you. We support some right to left languages though. Right? Like, in the data studio. I believe so. I have never used them, to be honest. So please don't look at me for further info in the context. Given given that the default language collection gets created with We do. We do. Yep. Definitely. So if you look, we have Arabic Yeah. For 1. So if you scroll where's my language Uh-oh. I don't know what Don't lose the button. I'm good enough navigating around. Worst case, I have DirecTV access to this guy. Fix it. But it's all good. So you get your Arabic. So we do have the translations for it. It's individual strings, though. Yeah. Yeah. Exactly. Exactly. As Tim said here in the chat, I'm not convinced this is a true right to left implementation for languages. It likely will do, but will do is not necessarily the best or optimal. But in any case, that's just worth noticing too. Would the reality actually be that this this should flip over here in the right to left? Like, the module bar, like, the whole screen would, like, invert? Oh, that makes my head hurt. I I am not sure that's correct. So I I feel the need to add, I believe, the 3 of us here, plus Tim and the child Oh, Benny said, yes. Okay. Someone had a degree of confidence because I was like, if we've not done it, we don't know and there are degrees of implementation. Right? But, at the very least, consider the way people read left to right in terms of priority and flow. They would wanna be flipped. Yeah. But we also have to keep in mind that, Benny is from Australia, so his layout is also different. His navigation is on the bottom, and everything appears on the top. It's flipped. You know what would be so funny if, yeah, everything was upside down or, like, even subtle things like like the the spinner stand in the other direction like the toilet water. Yeah. Oh, that would be good. Alrighty. I think I think that's for for now, enough about emails because the other issues are also very interesting, and I think we should we should look into them. Definitely. So sounds like we have a couple of solutions to think through on how we'd wanna implement this. I was looking, Kevin, with regards to your question. It does look like if you use a custom template, so so my custom template so if you have a custom liquid template, you can define variables inside that template. So first name, you then pass that as data from a flow. Now this is flow implementation of that template. But, technically Yeah. I mean, technically, being able to and it being in any way optimal. There's a very, very wide chasm between the 2, and I think this is up on one end. Right? Yeah. So once again, what you would do is you would have a body and a subject here that you were looking up from that translation user side content translation. So you'd have a user table with a translations interface, that has your subject and your body and just simply translate the whole thing. You could variabilize or anything that you wanted inside that template itself. Flows would have to handle these, you know, use a run script. I did something like this recently for page templates. So defining a page template, content gets built in the user tables, and then when the user's ready to say, you know, for my my location page template, generate the location page, substitute in. I've got a flow that looks for those variables, substitutes them in, generates the page instance for that location, and I'm standing for that. So you could do those kinds of things. But again, it's a lot of work upfront, for the developers and the, you know, the administrators. So Yeah. Figuring out a way to make this I think, again, the system making it accessible via the admin in some way, shape, or form, I think is the ideal solution. Like it. Agreed. Cool. And check. Alright. System flows. What do you wanna talk about there, mister Daniel? Oh, this this one is very cool. This is very interesting. It's, the idea and I'm I'm I I'm always very in favor of dogfooding, like, our application. So if we could use like, integrate flows deeper into our app itself, it would, like, in a way, force us to actually improve flows a little bit for ourselves. And so we got it's not I mean, really like them. I know. We we we talked about flows 2.0 a little while back. Right? But yes. Go ahead. Yeah. And, yeah. So this one is pretty interesting. So, the idea is that you replace our current, like, internal behavior of, like, what even happens if somebody presses, forgot password. You know, like like, for example, maybe I would like to send a couple emails. I would like to send one email to the to the admin and say, like, oh, warning. This user has, requested a password request or something, you know, just as a random example. You can also do, like, more product updates. Record above and beyond what's in the system already. Maybe you've got soft compliance or ISO compliance things where you get that logged to a system somewhere. Yeah. That's not our app. Interesting. Exactly. So maybe you want to customize this. And, I think, I haven't read the entire issue yet, but I think the idea was just to start with, like, invites and password, resets. But we can expand on this a little bit. And, you know, like, depending on how we structure this, how the user experience is, like, how do you override that? We have to have a fallback. What happens in the UI? And and you're not allowed to delete the thing. What happens if somebody deletes the thing? You know, like, there's there's a couple things that can go wrong, but, technically, I think this is very exciting and a very smart idea. I I really like this. Clever. Well written too. Well defined. Well thought through. Linking to related concepts and tickets. David David Zacharias. Excellent. Excellent work. I love the Yeah. Read only read only flows. Must have read only flows AKA system flows. Additional which kinda makes sense here. You have system collections and you can extend system collections as well. So it could be that thing where it's like, hey, you know what? These first two steps or the first and last, you can't change those. You can either put stuff in the middle or stuff at the end, kind of like you can do with fields today in collections. So Mhmm. The core behavior stays exactly the same that you can tack on. I would be more inclined then to just have an event, though, that an event based hook can hook into. I don't know. No. That's a good point, actually. Because if you have read only flows and you have these new additional scopes, then they kind of negate the need for one another, I think. Now that that's a good point. Yeah. That is a good point. And our paradigm today is because, like, oh, let me based. Even if somebody wants to just break the flow, for example, maybe somebody doesn't want the, like, even the functionality to to be there. You know? Like, maybe you want to forbid actually password requests, like, reset requests. Yeah. Or you just want it to come to the internal team and never go back out to them automatically. Like, that is a a managed process. Yeah. Interesting. Yep. Yeah. Also Disabling your Reasonable to me. Disabling a password reset? Interesting. Yeah. I mean, for you know, it's it's always such a, you know, contrived example and stuff, but take like, there's so many different environments and and regulations and Get in touch with your system administrator. Managers and Your IT team or whatever. For example. Yeah. So I can definitely see the use case there. And and this is, like like, super interesting to me. The the the lowest Like, I have not thought about this. Neither have I. The lowest hanging fruit version of this is additional scopes that allow you to execute additional logic on top of the core logic for things like inviting and password resets. These I I almost feel like that is a separate thing, which is smaller and much easier to to implement. Pop that to the side. The system flows is is diff is the thing here. I just find that in that addition interesting. Yep. Nope. And so, again, it was extensible, you know, in the sense of it'd be nice to have the translation capability, but also, again, move. We're gonna touch some of these things, potentially moving some of that logic to where it's more accessible or modifiable. Oh, to modify the business logic, users can duplicate the flow and adapt to their needs. The system flow would then be inactive, but could be activated anytime to restore the default functionality. That UX, for me, does not feel good, but it is an interesting way to think about it. Yeah. I I think Well, the system side scope in the event yeah. If you can just, like, have a filter hook, and that would do the thing. Like, you can then cancel. Like, if you throw, the hook will be canceled, and then the email won't be sent out. And if you just pass it through, the normal behavior will take place, which is just sending an email. Right? Like, and you can do stuff in between. Yeah. I feel like this would be a way better solution than having, like, read only flows and copying and then, you know, fallbacks and something is missing and whatever. Like, maybe just And the And the critical thing is it matches the paradigms we have today. We have event based, event based triggers, and then we have actions and filters which run before or after the the operation happens, the the database operation happens. And yeah. So that feels that feels kinda better to me. And, yeah, you can always find a way to break it out of completing. And if you're if it's blocking, then the end part never happens. Yeah. Oh, this is cool. If somebody could work on this, that would be nice. This is pretty cool. Well, that's why we're reviewing so we can Could we determine, does it make sense and what additional things do we need to think through. Right? Can we split this into 2 feature requests? Because that core nugget we've discussed is feeling very viable. Also, it feels like not a huge lift. And it is not system. It is not system flows. It is the introduction of new events, and then system flows is is something in and of its own. Right. I suppose. But, like, if we have that event, like, do we even need system flows then? I I I would argue not. I I would argue not. But that isn't what this. It's hard because I don't feel inclined to change people's feature requests into the 10% one liner that they've introduced, in it. But that nugget can be dealt with in isolation. Yeah. Yeah. Saw that. Okay. So so that that's that's pretty neat. That would be nice. Yeah. Very cool. Yeah. Then okay. So for the events, then there would be a password request reset request. I always It's a reset it's a reset request and then a reset. Yeah. And a oh, oh, right. Those are 2 things. Okay. The the request and the actual reset. Maybe you want to act on that too. Okay. Those 2. The invite would be the 3rd. Oh, I'm hang on. So I can just tell you because I've just written this section of our new documentation. So there is invite request and invite accept. Right, actually creating a user off the back of an invite. Then there is, password reset request and password reset. And then there's a set maybe around SSO specifically, but maybe not. They are that out. And then if we're talking about if we're thinking about the whole breadth of kind of off, there is also 2fa on off. Like, they are the that is the set of kind of operations that exist beyond just logging in, locking out, logging out, refreshing. And since this week, there will be maybe coming then, register request and register request accepted, maybe? Yes. Something. Ah, and the email verification might also be interesting. Somebody might need that. I would sooner add a a a, you know, like, a bundle of new events and go, well, there's the bit the biggest degree of flexibility for That would be neat. Okay. Yeah. This this this sounds very exciting. This sounds actually very cool to me. Because we introduced to also new events for the content versioning. There was the promotion Promotion. Or promote or promotion, which is also useful. So this doesn't sound that far and unreasonable. This sounds very reasonable, actually. So, Jonathan, so while you're typing, there's a there's a couple more. So there is invite, and then there's invite accept. There's password reset request and password reset, And there is 2 FA on and off. And they are the only 2 in my mind that I'm not sure how what like, I'm not sure what the impact of adding those in particular are, but they are there as well. And then I don't I don't have enough knowledge around single sign on to be confident in in listing events around that. Cool. Yep. Yeah. Yeah. And, again, being able to extend being able to extend these would be really powerful. Right? Because if we've got a lot of clients that run us as a SaaS, we operate as a SaaS. There are system and service emails that, again, we do it through user templates and other things today, the way that we define and the way we've talked about. But making this kind of built in where you can create and manage your own system emails and service emails, would love it. Yeah. And the the thing is we already because we this only were basically, taking this feature request around or the other feature request rather, which is, oh, sorry. I see you're in this issue. Sorry. We're in the wrong one. Sorry. So emails that go out are not all of those. Sorry. Emails are just the, the yes. The password reset request and the invite. They are the only 2 system emails. Sorry. They are the auth events, though. Or the the potential auth you know, they are the points at which we could implement events. The fact that we already have filters and actions as blocking and non blocking, you know, code, you know, logic business logic means immediately you can effectively extend Directus' core functionality without needing to do anything else but expose these new events, which is really cool. Yes. Agreed. It's nice when you have a system that kind of already has the building blocks. Right? There's no new building blocks that need to be built for this one. Yes. Only the integration and making sure that we can actually, like, cancel, for example, like, what happens if if the thing canceled and stuff. But other than that, you're completely right. Yes. So just as a reminder for everybody else in the chat because there's still a couple people here, the show will be over very soon. So if you have any questions that you would like to ask, now is the time. Now is the time. Quiet group today. Nothing wrong with that at all. Alright. Yeah. Means maybe we did our job right. And and not right any other time. Yeah. That's really cool. I like the idea of system system flows. I, ultimately, like the idea of that, of exposing a set of functionality as flows in a UI that could be extended on the end. But I I, personally believe it's just simply unnecessary. It's just exposing the new events. And with actions and filters, it facilitates, I think, the goals of this. Yep. Yeah. Exactly. I fully agree. So people let your let your questions out in last minute. 1, 2. Yeah. If you have enjoyed this, or want to partake in the next one, you take a look in Discord in the events. You will see that we will be doing another episode on the 23rd May Yeah. Will be about configurable API errors. So errors are always very important. You might wanna let your voice be heard there. So please join in. Tune in if you like. And if you enjoyed this and want to revisit this or other episodes, head on over to directors.iotv. And, there will be lots of other shows as well, which are quite interesting and fun. So please check it out. Let's say it again, directors.io\u002Ftv. Yep. We hope you enjoyed. Oh, there's the link. Thank you. And all other episodes of request review bar the last one because I messed up the recording, you can find right there.","8fb596c2-af4e-4a21-b22b-b5da736ee51a",[3113,3114,3115],"c7f329c8-1566-48d9-bdd5-5dbd9238c097","6c66580b-883c-4b84-9825-7111ec9dcd42","01c1e460-1c3b-49ee-84b0-b6366cbe81ad",[],{"year":978,"number":11,"id":1787,"episodes":3118,"show":3119},[1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1768,1800],{"title":1802,"slug":1803},{"id":3021,"slug":3121,"vimeo_id":3122,"description":3123,"tile":3124,"length":159,"resources":12,"people":12,"episode_number":740,"published":3001,"title":3125,"video_transcript_html":3126,"video_transcript_text":3127,"content":12,"seo":3128,"status":19,"episode_people":3129,"recommendations":3131,"season":3132},"ai-writer","945763500","Generate text based on a prompt with this custom operation, powered by OpenAI.","471ed635-921b-4153-92eb-1a31d87951a5","AI Writer","\u003Cp>Speaker 0: Here we have an articles collection with a title and a piece of content. We're gonna generate a social post, a social text right here, but I'm also gonna get to show you the other prompts that are available as well as the advanced usage mode. So let's go ahead and create a new flow, and we'll call this writer. We're gonna do this based on a manual flow trigger, so you click a button in the sidebar. We'll make that available on the article item pages.\u003C\u002Fp>\u003Cp>Now this will only return when triggered, the ID. So first thing we wanna do is read the whole item so we can get the content. So we will read data from the content. Including the content. Now, we're gonna use the ai writer operation.\u003C\u002Fp>\u003Cp>Let's take a little look at it together. Firstly, you need to provide an OpenAI API key. You select the GPT model, each of these having different characteristics around speed and cost, and, of course, the quality of the output, we'll use GPT 4. And you can use one of our selected prompts, fix spelling and grammar, short social post, make longer, make shorter, create an SEO description, or a custom prompt. On top of all of that, you can also use this messages mode here in order to utilize, like, multi step prompt engineering where you can give it kind of a history of messages as either the system, the assistant, or the user, and then your message will come in after all of that.\u003C\u002Fp>\u003Cp>Really good for powerful prompt engineering. But for now, we'll just create a short social post. So we're gonna pass into this last dot content, and we're gonna use our built in prompt short social post. Let's go ahead and see what this returns. So we'll go ahead here.\u003C\u002Fp>\u003Cp>We'll go back to our item, and we'll run the writer operation. So that's going off. It's doing its work, but, of course, we've not told it to save back to the item. So that is the end, but now we get to see in the logs what was actually returned. We see here the text is indeed returned.\u003C\u002Fp>\u003Cp>So what we can do is we can add on to the end of this and update data operation. We will update the article, which triggered this flow once again. So trigger dot body dot keys 0 and we are gonna update the social text property with the value of last like so. And that's everything we need to do. Of course, the flexibility comes in the configuration, the custom prompts, and indeed the multi step prompt engineering that is available to you.\u003C\u002Fp>\u003Cp>For now, if we click this manual prompt and we wait a few seconds, we should receive a much shorter I mean, this is, like, a fairly long piece here. It's a transcription from a podcast. That is a social post that we can go ahead and share. So that's how the AI writer operation works. I hope you find it useful.\u003C\u002Fp>\u003Cp>I'm sure you can see the many many many utilizations of it, and, yeah, thank you so much for joining me. I'll see you in the next video!\u003C\u002Fp>","Here we have an articles collection with a title and a piece of content. We're gonna generate a social post, a social text right here, but I'm also gonna get to show you the other prompts that are available as well as the advanced usage mode. So let's go ahead and create a new flow, and we'll call this writer. We're gonna do this based on a manual flow trigger, so you click a button in the sidebar. We'll make that available on the article item pages. Now this will only return when triggered, the ID. So first thing we wanna do is read the whole item so we can get the content. So we will read data from the content. Including the content. Now, we're gonna use the ai writer operation. Let's take a little look at it together. Firstly, you need to provide an OpenAI API key. You select the GPT model, each of these having different characteristics around speed and cost, and, of course, the quality of the output, we'll use GPT 4. And you can use one of our selected prompts, fix spelling and grammar, short social post, make longer, make shorter, create an SEO description, or a custom prompt. On top of all of that, you can also use this messages mode here in order to utilize, like, multi step prompt engineering where you can give it kind of a history of messages as either the system, the assistant, or the user, and then your message will come in after all of that. Really good for powerful prompt engineering. But for now, we'll just create a short social post. So we're gonna pass into this last dot content, and we're gonna use our built in prompt short social post. Let's go ahead and see what this returns. So we'll go ahead here. We'll go back to our item, and we'll run the writer operation. So that's going off. It's doing its work, but, of course, we've not told it to save back to the item. So that is the end, but now we get to see in the logs what was actually returned. We see here the text is indeed returned. So what we can do is we can add on to the end of this and update data operation. We will update the article, which triggered this flow once again. So trigger dot body dot keys 0 and we are gonna update the social text property with the value of last like so. And that's everything we need to do. Of course, the flexibility comes in the configuration, the custom prompts, and indeed the multi step prompt engineering that is available to you. For now, if we click this manual prompt and we wait a few seconds, we should receive a much shorter I mean, this is, like, a fairly long piece here. It's a transcription from a podcast. That is a social post that we can go ahead and share. So that's how the AI writer operation works. I hope you find it useful. I'm sure you can see the many many many utilizations of it, and, yeah, thank you so much for joining me. I'll see you in the next video!","e48fb3eb-e55e-4454-a5e8-3fcf2e814784",[3130],"42f1c79c-17a4-45ba-8cd4-4a4cea2f166c",[],{"year":978,"number":158,"id":3010,"episodes":3133,"show":3134},[3012,2996,3013,3014,3015,3016,3017,3018,3019,3020,3021],{"title":3023,"slug":3024},{"id":3018,"slug":3136,"vimeo_id":3137,"description":3138,"tile":3139,"length":264,"resources":12,"people":12,"episode_number":11,"published":3001,"title":3140,"video_transcript_html":3141,"video_transcript_text":3142,"content":12,"seo":3143,"status":19,"episode_people":3144,"recommendations":3146,"season":3147},"ai-text-extraction","945762700","Extract text from image files within Directus Files with this custom operation, using Clarifai.","4a3c8606-c4d9-41c1-a9e3-5322f3cfc83a","AI Text Extraction","\u003Cp>Speaker 0: The Directus AI text extraction operation will do exactly that. You feed it an image and it will extract text from that image. You can then do whatever you want with it, but in today's example, we're going to save the extracted text to the file description. And we and we're going to do this based on an event hook, a non blocking event hook whenever a file is uploaded. Now we're gonna not set up any extra configuration, but in the real, you would probably want a condition that, says this has to be an image file.\u003C\u002Fp>\u003Cp>Right now, it will trigger on every file, and the documentation of this extension will show you how to do that. So then we are gonna just go straight away and we are going to use the AI text extraction operation. And you have to provide your Clarify access token, which I made earlier, and provide a direct file URL. So your full director's project URL slash assets and then slash trigger dot key. So that is the file ID that has just been uploaded that has triggered this whole flow to actually begin.\u003C\u002Fp>\u003Cp>And this will return a couple of things, it will return the overall text, but it will also return different bounding boxes, so you can say, hey, only give me the text maybe in the top left or stuff like that, but we're gonna just take that overall summary, and we're gonna save it back to the image. So we are going to update the Directus files core collection, and we are going to update specifically the item whose upload started this whole thing, and we are going to set the description to last dot text, so that is the value from this is a is an object, text is a property within it, and that is the entire setup of the AI text extraction operation. Very nifty. So let's, try this out. Let's go to our file library here and let's upload a new file.\u003C\u002Fp>\u003Cp>Just moments later we can go in and we can see right here the description has been filled in. Now probably a very fair point the AI is not perfect and you'll see here finance manager. The f is missing, but it's not done a bad job. Perhaps in your real workflows, you'll want a a step of human intervention. This will still save loads of time for stuff like business cards, for written documents, for receipts and invoices, but do always make sure that you're checking the output is exactly what you need it to be.\u003C\u002Fp>\u003Cp>But that is the, the rundown of how the AI text extraction operation works. Hope you found this interesting, and I'll see you in the next video.\u003C\u002Fp>","The Directus AI text extraction operation will do exactly that. You feed it an image and it will extract text from that image. You can then do whatever you want with it, but in today's example, we're going to save the extracted text to the file description. And we and we're going to do this based on an event hook, a non blocking event hook whenever a file is uploaded. Now we're gonna not set up any extra configuration, but in the real, you would probably want a condition that, says this has to be an image file. Right now, it will trigger on every file, and the documentation of this extension will show you how to do that. So then we are gonna just go straight away and we are going to use the AI text extraction operation. And you have to provide your Clarify access token, which I made earlier, and provide a direct file URL. So your full director's project URL slash assets and then slash trigger dot key. So that is the file ID that has just been uploaded that has triggered this whole flow to actually begin. And this will return a couple of things, it will return the overall text, but it will also return different bounding boxes, so you can say, hey, only give me the text maybe in the top left or stuff like that, but we're gonna just take that overall summary, and we're gonna save it back to the image. So we are going to update the Directus files core collection, and we are going to update specifically the item whose upload started this whole thing, and we are going to set the description to last dot text, so that is the value from this is a is an object, text is a property within it, and that is the entire setup of the AI text extraction operation. Very nifty. So let's, try this out. Let's go to our file library here and let's upload a new file. Just moments later we can go in and we can see right here the description has been filled in. Now probably a very fair point the AI is not perfect and you'll see here finance manager. The f is missing, but it's not done a bad job. Perhaps in your real workflows, you'll want a a step of human intervention. This will still save loads of time for stuff like business cards, for written documents, for receipts and invoices, but do always make sure that you're checking the output is exactly what you need it to be. But that is the, the rundown of how the AI text extraction operation works. Hope you found this interesting, and I'll see you in the next video.","302dac60-4dda-4a4c-b9b5-fa7d27a506f0",[3145],"80258ab7-b77c-4e91-bd2b-4b62ae993ebc",[],{"year":978,"number":158,"id":3010,"episodes":3148,"show":3149},[3012,2996,3013,3014,3015,3016,3017,3018,3019,3020,3021],{"title":3023,"slug":3024},{"id":3016,"slug":3151,"vimeo_id":3152,"description":3153,"tile":3154,"length":213,"resources":12,"people":12,"episode_number":195,"published":3001,"title":3155,"video_transcript_html":3156,"video_transcript_text":3157,"content":12,"seo":3158,"status":19,"episode_people":3159,"recommendations":3161,"season":3162},"ai-speech-generation","945761979","Generate realistic speech clips from text with this custom operation, powered by Genny from LOVO.\n\n","7acce675-8773-4d30-a844-56f78a198dd8","AI Speech Generation","\u003Cp>Speaker 0: The Directus AI speech generation operation allows you to create realistic sounding speech from text inside of your director's project. This extension is powered by Jenny, so you will need an API key in order to follow along. Now here I have a collection called ads, and I've created just a very short textual ad that I want to run for directors. However, it's gonna go in something like a podcast or a audiobook or something, so it needs to be in audio form. So what we're gonna do is create a manual flow where when we click a button here, it will go off and generate that audio for us.\u003C\u002Fp>\u003Cp>So let's go over to our flows and create a new flow, generate audio, with a manual trigger on the ads collection. This will return just the ID of the item where the button is pressed. We obviously want the text. So the first thing we're gonna do is read data and we will read data from the ads collection, specifically the ID from the from the item whether it was pressed. So trigger dot body dot keys zero.\u003C\u002Fp>\u003Cp>So this will return the text and the currently empty file field. Then we can go straight away and send this off to the AI speech generation operation. Now we do need a Jenny API key, but I have one that I made earlier. And in here, we put in our text with a maximum of 500 characters. And I am gonna go ahead and just say, hey, that last operation that fetched all the data, go grab the text value out of it.\u003C\u002Fp>\u003Cp>You get to pick one of the selected speakers, so we'll use Brian Lee junior here and you get to specify the speed at which the, the speech happens. So this will return just a URL of an mp3 on the web. Next, we need to import it into this Director's project, and we can do that using the webhook request URL operation. So we'll make a post request to this directus URL trigger dotlws.i. So that's your specific project URL\u002Fassets, not assets, sorry, slash files slash import.\u003C\u002Fp>\u003Cp>That's the endpoint. It's a post request, and then the body here, we are going to just pass in the URL. Now just a note that for this project I've made file creation publicly accessible, you may need to lock down those permissions and provide your authentication headers here, but for ease I've turned that off for this demo. So what this will do is make that API call and return with the whole payload of the brand new director's files item that was just created, and what we wanna do is extract the ID and save that back to our ads item. So let's update the ads item here.\u003C\u002Fp>\u003Cp>We wanna update trigger.body.keys 0, and what we wanna do is just update the file value, and the location of the ID in the returned JSON payload is in data dot data dot ID. That is the whole flow. We click the button. We grab the whole item, which includes the text. We generate the text, which results in an m p 3.\u003C\u002Fp>\u003Cp>We import the m p 3 and save the m p 3 back to the item. So let's see if this works. Fingers crossed, we're now going and generating that audio. It's going off to that 3rd party vendor creating that realistic sounding audio for us, importing it, and then finally adding it here inside of the file except when it doesn't. Why is that?\u003C\u002Fp>\u003Cp>Let's take a little look together. Let's take a look at the log. What happened there? Trigger, because I got completely the wrong URL of just my director's project here. Tunnel is actually where this project is located.\u003C\u002Fp>\u003Cp>So let's run that one last time, generate audio. We'll give that just a moment once again to do its thing in that time I'll tell you a joke I'm not a funny person so I won't be doing that but that allows me to stall for long enough for this operation to complete and that of course is our, is our wav file, our actual audio file there containing our ad. So that is a little bit about how the, speech generation operation works using directus AI. We hope you enjoy it. I see loads of use cases for this, but the primary one being either like video voiceovers at scale or programmatic ads or stuff like that.\u003C\u002Fp>\u003Cp>So I hope you found this interesting. I'll see you in the next video.\u003C\u002Fp>","The Directus AI speech generation operation allows you to create realistic sounding speech from text inside of your director's project. This extension is powered by Jenny, so you will need an API key in order to follow along. Now here I have a collection called ads, and I've created just a very short textual ad that I want to run for directors. However, it's gonna go in something like a podcast or a audiobook or something, so it needs to be in audio form. So what we're gonna do is create a manual flow where when we click a button here, it will go off and generate that audio for us. So let's go over to our flows and create a new flow, generate audio, with a manual trigger on the ads collection. This will return just the ID of the item where the button is pressed. We obviously want the text. So the first thing we're gonna do is read data and we will read data from the ads collection, specifically the ID from the from the item whether it was pressed. So trigger dot body dot keys zero. So this will return the text and the currently empty file field. Then we can go straight away and send this off to the AI speech generation operation. Now we do need a Jenny API key, but I have one that I made earlier. And in here, we put in our text with a maximum of 500 characters. And I am gonna go ahead and just say, hey, that last operation that fetched all the data, go grab the text value out of it. You get to pick one of the selected speakers, so we'll use Brian Lee junior here and you get to specify the speed at which the, the speech happens. So this will return just a URL of an mp3 on the web. Next, we need to import it into this Director's project, and we can do that using the webhook request URL operation. So we'll make a post request to this directus URL trigger dotlws.i. So that's your specific project URL\u002Fassets, not assets, sorry, slash files slash import. That's the endpoint. It's a post request, and then the body here, we are going to just pass in the URL. Now just a note that for this project I've made file creation publicly accessible, you may need to lock down those permissions and provide your authentication headers here, but for ease I've turned that off for this demo. So what this will do is make that API call and return with the whole payload of the brand new director's files item that was just created, and what we wanna do is extract the ID and save that back to our ads item. So let's update the ads item here. We wanna update trigger.body.keys 0, and what we wanna do is just update the file value, and the location of the ID in the returned JSON payload is in data dot data dot ID. That is the whole flow. We click the button. We grab the whole item, which includes the text. We generate the text, which results in an m p 3. We import the m p 3 and save the m p 3 back to the item. So let's see if this works. Fingers crossed, we're now going and generating that audio. It's going off to that 3rd party vendor creating that realistic sounding audio for us, importing it, and then finally adding it here inside of the file except when it doesn't. Why is that? Let's take a little look together. Let's take a look at the log. What happened there? Trigger, because I got completely the wrong URL of just my director's project here. Tunnel is actually where this project is located. So let's run that one last time, generate audio. We'll give that just a moment once again to do its thing in that time I'll tell you a joke I'm not a funny person so I won't be doing that but that allows me to stall for long enough for this operation to complete and that of course is our, is our wav file, our actual audio file there containing our ad. So that is a little bit about how the, speech generation operation works using directus AI. We hope you enjoy it. I see loads of use cases for this, but the primary one being either like video voiceovers at scale or programmatic ads or stuff like that. So I hope you found this interesting. I'll see you in the next video.","3d5b7ece-22e1-4315-9812-671b8e487cdf",[3160],"5a1eb839-4c7d-4f2f-974d-599639631352",[],{"year":978,"number":158,"id":3010,"episodes":3163,"show":3164},[3012,2996,3013,3014,3015,3016,3017,3018,3019,3020,3021],{"title":3023,"slug":3024},{"id":3015,"slug":3166,"vimeo_id":3167,"description":3168,"tile":3169,"length":159,"resources":12,"people":12,"episode_number":213,"published":3001,"title":3170,"video_transcript_html":3171,"video_transcript_text":3172,"content":12,"seo":3173,"status":19,"episode_people":3174,"recommendations":3176,"season":3177},"ai-image-moderation","945761757","Analyze images for drugs, suggestive or explicit material with this custom operation, powered by Clarifai.","314fcef8-4c7e-41ac-a7ab-96838c6f0aff","AI Image Moderation","\u003Cp>Speaker 0: The Directus AI image moderation operation helps you build safer and more positive applications powered by Clarify. It will take in an image and tell you whether there is any drugs, suggestive or explicit material, or gore in the images, and then you can take action based on what is right for your application. So we're gonna build that together today. So we are going to create a new flow that will trigger whenever a new image is uploaded. We'll call this moderation, image moderation.\u003C\u002Fp>\u003Cp>Lovely, and we will run this whenever a file is uploaded. Now this will run on every file that's uploaded regardless of type. So in the real, you will want to add a condition that checks that this is indeed an image or perhaps based on a certain folder maybe just end user upload folders stuff like that. The documentation for this operation shows you how to set that up, but for now, I'm going to assume every file is an image and every file wants this moderation check. So we have this file upload, and we are gonna go ahead and use our AI image moderation.\u003C\u002Fp>\u003Cp>There it is. AI image moderation operation. Now we want to provide our Clarify API token and provide a full file URL. So https tunnel.orws.i0 \u002Fasset\u002Ftrigger.key which is the ID of the file whose upload started this flow, and of course you'll want to replace this with your actual, Director's project URL. There's also a threshold here, which will change which items are flagged.\u003C\u002Fp>\u003Cp>So you will always get back a raw response between 0 and a100 of the confidence that either drug drugs suggestive or explicit material or gore is present, but there's an additional section called flags, which makes it easier to take action. This threshold changes what is needed for something to appear as a flag. In fact, I just want to show you how this works because what happens next is very very specific. So, I've got an image here of a needle with some white powder. So, sorry in our file module here we'll just go and update upload that.\u003C\u002Fp>\u003Cp>So you see that's the image there, there is definitely some drugs in that image right. So we're gonna go over to our flow and and we're gonna look at the log, and we see here that in the payload, there's a very very very high chance that drugs were present. It's not suggestive. It's not gore. It's not explicit, and because that number is above our threshold this appears as a flag.\u003C\u002Fp>\u003Cp>Now in most episodes in this series, I build a complete project, but this one is really up to you. You might choose instead of an action, which is a non blocking flow, you may choose to block it, and if and then you add a condition on to the end of the moderation, and if there's something that's flagged, you may stop it being uploaded at all, or you may flag it a certain way, change a status on the file field and stop it displaying to end users or display a warning. You may choose to email moderators to come in and do a manual check over. You may wanna send an email to the user and so on and so forth. So, hopefully, you can start seeing the applications of this image moderation operation.\u003C\u002Fp>\u003Cp>I'm gonna stop here in this video because it gets very specific after this, very implementation specific. But I hope you find loads of value in it. It's really, really powerful, and hopefully, you'll agree quite nice to set up. So I'll see you in the next video. Bye for now.\u003C\u002Fp>","The Directus AI image moderation operation helps you build safer and more positive applications powered by Clarify. It will take in an image and tell you whether there is any drugs, suggestive or explicit material, or gore in the images, and then you can take action based on what is right for your application. So we're gonna build that together today. So we are going to create a new flow that will trigger whenever a new image is uploaded. We'll call this moderation, image moderation. Lovely, and we will run this whenever a file is uploaded. Now this will run on every file that's uploaded regardless of type. So in the real, you will want to add a condition that checks that this is indeed an image or perhaps based on a certain folder maybe just end user upload folders stuff like that. The documentation for this operation shows you how to set that up, but for now, I'm going to assume every file is an image and every file wants this moderation check. So we have this file upload, and we are gonna go ahead and use our AI image moderation. There it is. AI image moderation operation. Now we want to provide our Clarify API token and provide a full file URL. So https tunnel.orws.i0 \u002Fasset\u002Ftrigger.key which is the ID of the file whose upload started this flow, and of course you'll want to replace this with your actual, Director's project URL. There's also a threshold here, which will change which items are flagged. So you will always get back a raw response between 0 and a100 of the confidence that either drug drugs suggestive or explicit material or gore is present, but there's an additional section called flags, which makes it easier to take action. This threshold changes what is needed for something to appear as a flag. In fact, I just want to show you how this works because what happens next is very very specific. So, I've got an image here of a needle with some white powder. So, sorry in our file module here we'll just go and update upload that. So you see that's the image there, there is definitely some drugs in that image right. So we're gonna go over to our flow and and we're gonna look at the log, and we see here that in the payload, there's a very very very high chance that drugs were present. It's not suggestive. It's not gore. It's not explicit, and because that number is above our threshold this appears as a flag. Now in most episodes in this series, I build a complete project, but this one is really up to you. You might choose instead of an action, which is a non blocking flow, you may choose to block it, and if and then you add a condition on to the end of the moderation, and if there's something that's flagged, you may stop it being uploaded at all, or you may flag it a certain way, change a status on the file field and stop it displaying to end users or display a warning. You may choose to email moderators to come in and do a manual check over. You may wanna send an email to the user and so on and so forth. So, hopefully, you can start seeing the applications of this image moderation operation. I'm gonna stop here in this video because it gets very specific after this, very implementation specific. But I hope you find loads of value in it. It's really, really powerful, and hopefully, you'll agree quite nice to set up. So I'll see you in the next video. Bye for now.","5030a562-a906-4869-a63c-1a3678a20bfe",[3175],"0eade7f9-5a3c-49c1-9e15-13d9f16a0bfd",[],{"year":978,"number":158,"id":3010,"episodes":3178,"show":3179},[3012,2996,3013,3014,3015,3016,3017,3018,3019,3020,3021],{"title":3023,"slug":3024},{"id":3012,"slug":3181,"vimeo_id":3182,"description":3183,"tile":3184,"length":129,"resources":12,"people":12,"episode_number":13,"published":3185,"title":3186,"video_transcript_html":3187,"video_transcript_text":3188,"content":12,"seo":3189,"status":19,"episode_people":3190,"recommendations":3193,"season":3194},"ai-overview","945772281","​Hear from our CTO Rijk about our philosophy on AI’s role in Directus and beyond, and then our Director of Developer Relations Kevin on our brand new AI extensions.","dc879be9-3817-458e-92de-72378e03db25","2024-05-15","Directus AI Overview","\u003Cp>Rijk: Hello everyone. I'm Rijk, Co-Founder and CTO here at Directus. AI is everywhere. Every company under the sun is doing something with AI but AI can mean a lot of different things Are you using it to generate new information, summarize things you've recorded before, creating images, making analysis, etcetera, etcetera. Shoving that AI into, you know, existing products just for the sake of it can lead to quite a lot of issues.\u003C\u002Fp>\n\u003Cp>We've seen, you know, in in recent times, we've seen products with a lot of hype and little follow through, confidently being wrong, some hallucinating, that kind of stuff. Fair to say, for some of those things, implementing AI doesn't really act on the core purpose of the thing, taking the focus away of what really matters most to the users. At Directus, we really don't build things just for the sake of building things. We're always building it because there's a real interest and demand from the users. The same goes for A.\u003C\u002Fp>\n\u003Cp>I. We really want to be grounded and considered in our approach around implementing artificial intelligence. Thankfully, we've done our homework. We have something to really ground our decisions in. In our stated data survey we ran a little earlier this year, we saw that 8 out of 10 developers set that they're using artificial intelligence in their day to day work today.\u003C\u002Fp>\n\u003Cp>So we knew it was something we could take time to consider, but it's important that we stay true to ourselves and our community while we explore this new space. Even with the industry pressure. We want to be able to experiment with AI without spending so much time on it that it starts to affect other work that we're doing. We want to ensure that there is a real customer and user need for everything that we're building. And so we don't really like the idea of having to rely on a single third party service to power, you know, the core Directus experience.\u003C\u002Fp>\n\u003Cp>We still wanna make sure that you can host Directus yourself and even run it in isolation air gapped from the Internet if you wanted to. So today, we're announcing Directus AI. Directus is the toolkit for building applications. It's modular, powerful, and it places you in control. This is about providing tools to use if you want and when you want that weren't there before.\u003C\u002Fp>\n\u003Cp>With Directus AI, we are providing new ways to augment what parts of Directus can do and enable those 8 and 10 developers who use AI daily to more easily do so through our platform. Through the Directus marketplace, to build powerful automation. This isn't a refocus or reframing of what Directus is. We're taking advantage of Directus' extensibility and marketplace to explore this exciting new opportunity. And today, that forms the basis of our approach to AI.\u003C\u002Fp>\n\u003Cp>As AI becomes more ubiquitous in the way that developers and everyone work, our views and approach will continue to evolve. But for now, as a company that works with data, we remain cautiously optimistic. I'd love to hand it over to Kevin to tell you more about the extension.\u003C\u002Fp>\n\u003Cp>Kevin: Thank you very much for that introduction, Wrike, and I'm really excited to talk to you about what we've built. So, today, we're bringing AI to Directus Automate with a collection of 10 new operation extensions across 3 categories, data transformation, generation, and analysis. Now today, I'm gonna give you a super quick summary of all 10, but tomorrow we are releasing a brand new series on Directus TV, which will go more in-depth into each one, including how to set them up and their use cases. So firstly, we have the AI transcription operation. You feed it in a media file, and it will output transcripts powered by Deepgram speech recognition API.\u003C\u002Fp>\n\u003Cp>The AI translation operation will take in some text and a target language, and it will translate that text for you. The AI alt text and text extraction operations use Clarify's AI models to describe images and extract text from them, which is super useful for things like, business cards or receipts or things like that. The AI writer operation is really cool. It allows you to pass in some text and select from one of our pre written prompts, or you can write your own. It will then process that text and return the output.\u003C\u002Fp>\n\u003Cp>Our built in prompts include things like writing social posts, writing SEO descriptions, checking for spelling and grammar, and changing the length of text. This operation also has an advanced mode where you can provide multi step prompts making this super powerful and flexible. The AI image generation operation will take in a prompt and generate an image, which you can then save back to your director's project. And, similarly, the AI speech generation operation will take in some written text and one of our selected voices, and it will provide a realistic MP 3 file of that text being read out. This can be useful for things like automated or at scale ad generation or video voice overs.\u003C\u002Fp>\n\u003Cp>The AI text intelligence operation uses Deepgram's text analysis API to provide summaries, sentiment, intents, and topics for the text that you provide. The AI focal point detection operation uses OpenAI's vision models to determine the primary point of interest in an image. And you can combine this with the new focal point feature for more intelligent image cropping inside of your project. And finally, the image moderation operation. This will detect drugs, suggestive or explicit material, and gore within images powered by Clarify.\u003C\u002Fp>\n\u003Cp>This is a great step at helping you build safe and positive applications powered by Directus. Now all of these operations are available right now in the Directus marketplace, and together, they unlock super powerful workflows that enrich your projects. I'm really excited to talk to you more tomorrow through our new series, Directus AI, available on Directus TV, about how to actually set these extensions up and chain them together in really interesting ways. But for now on behalf of myself, right, and the whole directors team thank you so much for tuning in. We're really excited about what we can now do with these extensions and we hope you are too.\u003C\u002Fp>\n\u003Cp>Okay. Bye for now.\u003C\u002Fp>","Hello everyone. I'm Rijk, Co-Founder and CTO here at Directus. AI is everywhere. Every company under the sun is doing something with AI but AI can mean a lot of different things Are you using it to generate new information, summarize things you've recorded before, creating images, making analysis, etcetera, etcetera. Shoving that AI into, you know, existing products just for the sake of it can lead to quite a lot of issues. We've seen, you know, in in recent times, we've seen products with a lot of hype and little follow through, confidently being wrong, some hallucinating, that kind of stuff. Fair to say, for some of those things, implementing AI doesn't really act on the core purpose of the thing, taking the focus away of what really matters most to the users. At Directus, we really don't build things just for the sake of building things. We're always building it because there's a real interest and demand from the users. The same goes for A. I. We really want to be grounded and considered in our approach around implementing artificial intelligence. Thankfully, we've done our homework. We have something to really ground our decisions in. In our stated data survey we ran a little earlier this year, we saw that 8 out of 10 developers set that they're using artificial intelligence in their day to day work today. So we knew it was something we could take time to consider, but it's important that we stay true to ourselves and our community while we explore this new space. Even with the industry pressure. We want to be able to experiment with AI without spending so much time on it that it starts to affect other work that we're doing. We want to ensure that there is a real customer and user need for everything that we're building. And so we don't really like the idea of having to rely on a single third party service to power, you know, the core Directus experience. We still wanna make sure that you can host Directus yourself and even run it in isolation air gapped from the Internet if you wanted to. So today, we're announcing Directus AI. Directus is the toolkit for building applications. It's modular, powerful, and it places you in control. This is about providing tools to use if you want and when you want that weren't there before. With Directus AI, we are providing new ways to augment what parts of Directus can do and enable those 8 and 10 developers who use AI daily to more easily do so through our platform. Through the Directus marketplace, to build powerful automation. This isn't a refocus or reframing of what Directus is. We're taking advantage of Directus' extensibility and marketplace to explore this exciting new opportunity. And today, that forms the basis of our approach to AI. As AI becomes more ubiquitous in the way that developers and everyone work, our views and approach will continue to evolve. But for now, as a company that works with data, we remain cautiously optimistic. I'd love to hand it over to Kevin to tell you more about the extension. Thank you very much for that introduction, Wrike, and I'm really excited to talk to you about what we've built. So, today, we're bringing AI to Directus Automate with a collection of 10 new operation extensions across 3 categories, data transformation, generation, and analysis. Now today, I'm gonna give you a super quick summary of all 10, but tomorrow we are releasing a brand new series on Directus TV, which will go more in-depth into each one, including how to set them up and their use cases. So firstly, we have the AI transcription operation. You feed it in a media file, and it will output transcripts powered by Deepgram speech recognition API. The AI translation operation will take in some text and a target language, and it will translate that text for you. The AI alt text and text extraction operations use Clarify's AI models to describe images and extract text from them, which is super useful for things like, business cards or receipts or things like that. The AI writer operation is really cool. It allows you to pass in some text and select from one of our pre written prompts, or you can write your own. It will then process that text and return the output. Our built in prompts include things like writing social posts, writing SEO descriptions, checking for spelling and grammar, and changing the length of text. This operation also has an advanced mode where you can provide multi step prompts making this super powerful and flexible. The AI image generation operation will take in a prompt and generate an image, which you can then save back to your director's project. And, similarly, the AI speech generation operation will take in some written text and one of our selected voices, and it will provide a realistic MP 3 file of that text being read out. This can be useful for things like automated or at scale ad generation or video voice overs. The AI text intelligence operation uses Deepgram's text analysis API to provide summaries, sentiment, intents, and topics for the text that you provide. The AI focal point detection operation uses OpenAI's vision models to determine the primary point of interest in an image. And you can combine this with the new focal point feature for more intelligent image cropping inside of your project. And finally, the image moderation operation. This will detect drugs, suggestive or explicit material, and gore within images powered by Clarify. This is a great step at helping you build safe and positive applications powered by Directus. Now all of these operations are available right now in the Directus marketplace, and together, they unlock super powerful workflows that enrich your projects. I'm really excited to talk to you more tomorrow through our new series, Directus AI, available on Directus TV, about how to actually set these extensions up and chain them together in really interesting ways. But for now on behalf of myself, right, and the whole directors team thank you so much for tuning in. We're really excited about what we can now do with these extensions and we hope you are too. Okay. Bye for now.","4480f7ca-008b-40f2-a059-1ff260217678",[3191,3192],"9a31e513-71fe-497a-bd37-a7e5970233db","f73d6a94-019c-434f-85d8-1e59e588c3a2",[],{"year":978,"number":158,"id":3010,"episodes":3195,"show":3196},[3012,2996,3013,3014,3015,3016,3017,3018,3019,3020,3021],{"title":3023,"slug":3024},{"id":2493,"slug":3198,"vimeo_id":3199,"description":3200,"tile":3201,"length":264,"resources":12,"people":12,"episode_number":177,"published":3202,"title":3203,"video_transcript_html":3204,"video_transcript_text":3205,"content":12,"seo":3206,"status":19,"episode_people":3207,"recommendations":3209,"season":3210},"wtf-is-a-cms","941314616","Is content data? Or is data content? And is a content management system really just a data management system? In this episode, Matt muses over what a CMS is, what a DMS is, and if there's a difference. ","15259da8-70f0-458e-8d42-f922adf68b93","2024-05-14","WTF is a CMS","\u003Cp>Speaker 0: Hey, everybody. Welcome back to another episode of buzzword wilderness. I'm Matt here to guide you through the tech jungle of buzzwords. Today, we're gonna explore a common confusion that I've run into in the digital world, which is the difference between a CMS and a DMS, and discussing what headless really means in the context of both. So why do we even separate these terms, when at the end of the day, it's all just data?\u003C\u002Fp>\u003Cp>Don't know. Let's dive in. What is a CMS? So a Content Management System, or CMS, is all about creating, managing, and displaying content. And although there's some technical overhead upfront, it's designed for ease of use for the end user, even if you're not tech savvy.\u003C\u002Fp>\u003Cp>You've probably heard of platforms like WordPress or Drupal. Those are the classic examples of CMSs. In a headless CMS, the content, text, images, videos is all stored without a fixed front end system. So this means it can be displayed across various platforms via an API. It's like a storage box for your content, and then you decide where and and how to display it.\u003C\u002Fp>\u003Cp>What is a DMS? So a data management system, or DMS, is broader. It handles all kinds of data, be it customer information, transaction records, or operational metrics. It's not just about data and display, but about efficiently storing and using that data. With headless data, the head or the user interface is separate from the body, which is the data storage and logic.\u003C\u002Fp>\u003Cp>This data can be accessed and and used across different applications, whether it's a web app or a mobile app or even IoT devices. So CMS versus DMS, why the distinction between the 2? While both might seem similar since they manage data, the focus is actually different. The CMS is all about content and how it engages your users. It's really front end friendly.\u003C\u002Fp>\u003Cp>A DMS is about the efficient, versatile use of data at the back end. You might not see it directly, but it powers every system you use every day. Consider a retail brand, for instance. They use a CMS to manage and update their website's product descriptions and images. Simultaneously, they use a DMS to handle inventory data, customer transactions, and analytics, the data that helps them make strategic decisions.\u003C\u002Fp>\u003Cp>So a CMS and a DMS serve distinct purposes, but ultimately, they both handle data just in different ways. Hopefully, that was a help, helpful little reminder about the difference between the two. I appreciate you tuning in, and I'll see you on the next episode.\u003C\u002Fp>","Hey, everybody. Welcome back to another episode of buzzword wilderness. I'm Matt here to guide you through the tech jungle of buzzwords. Today, we're gonna explore a common confusion that I've run into in the digital world, which is the difference between a CMS and a DMS, and discussing what headless really means in the context of both. So why do we even separate these terms, when at the end of the day, it's all just data? Don't know. Let's dive in. What is a CMS? So a Content Management System, or CMS, is all about creating, managing, and displaying content. And although there's some technical overhead upfront, it's designed for ease of use for the end user, even if you're not tech savvy. You've probably heard of platforms like WordPress or Drupal. Those are the classic examples of CMSs. In a headless CMS, the content, text, images, videos is all stored without a fixed front end system. So this means it can be displayed across various platforms via an API. It's like a storage box for your content, and then you decide where and and how to display it. What is a DMS? So a data management system, or DMS, is broader. It handles all kinds of data, be it customer information, transaction records, or operational metrics. It's not just about data and display, but about efficiently storing and using that data. With headless data, the head or the user interface is separate from the body, which is the data storage and logic. This data can be accessed and and used across different applications, whether it's a web app or a mobile app or even IoT devices. So CMS versus DMS, why the distinction between the 2? While both might seem similar since they manage data, the focus is actually different. The CMS is all about content and how it engages your users. It's really front end friendly. A DMS is about the efficient, versatile use of data at the back end. You might not see it directly, but it powers every system you use every day. Consider a retail brand, for instance. They use a CMS to manage and update their website's product descriptions and images. Simultaneously, they use a DMS to handle inventory data, customer transactions, and analytics, the data that helps them make strategic decisions. So a CMS and a DMS serve distinct purposes, but ultimately, they both handle data just in different ways. Hopefully, that was a help, helpful little reminder about the difference between the two. I appreciate you tuning in, and I'll see you on the next episode.","6f978e06-2e4a-4404-a9e3-0697495e2200",[3208],"1b68760e-b1e6-455e-b427-cd5504243f63",[],{"year":978,"number":674,"id":2490,"episodes":3211,"show":3212},[2492,2493,2494,2495,2476],{"title":2497,"slug":2498},{"id":2755,"slug":3214,"vimeo_id":3215,"description":3216,"tile":3217,"length":527,"resources":12,"people":12,"episode_number":11,"published":3218,"title":3219,"video_transcript_html":3220,"video_transcript_text":3221,"content":12,"seo":12,"status":19,"episode_people":3222,"recommendations":3224,"season":3225},"amazon-theme","931467487","Join your favorite theme master, Bry Ross, as he takes on a dynamic Amazon theme. Grab your brushes (and your duster) and let's paint a bold theme that perfectly captures the spirit of a thriving marketplace.","633d9813-59b8-4d29-8ff9-3a49b56be786","2024-05-13","Amazon (From A to Zest)","\u003Cp>Speaker 0: Hi, friends. Welcome back to the show. I'm your host, Bry Ross. Before we start our theme today, I thought it was important to cover just how to clean your brush. So, this is how I like to clean my brush.\u003C\u002Fp>\u003Cp>It's super important to have a clean brush anytime you are doing a theme. You've got to make sure that you've got the right colors that you're putting on the theme. So what I like to do, I take my duster, give it a couple shakes, and then just aim the the little red tip of this nozzle at the brush. Just just give it a little flip there. And then you can hit it on your desk and now you've got a brush that's ready to go.\u003C\u002Fp>\u003Cp>So the theme that we are building today is Amazon from a to z. So if you're following along, I hope you've got your canvas prepped with a great looking data model, and let's get started by adding our project settings. Amazon. Everything, all the time. Whenever you want it.\u003C\u002Fp>\u003Cp>Alright. So we're gonna copy the project URL and just paste that in there. I would probably remove that ref tag as well. So we'll just paste that in there. And now we're ready to theme.\u003C\u002Fp>\u003Cp>So I always start everything by dialing in the branding. The branding is so important. We're going to inspect this Amazon logo from the website. It looks like it's gonna be a real pain to get access to this. They're using a sprite sheet which is is probably smart for performance, but makes our job as themers just a little bit more difficult.\u003C\u002Fp>\u003Cp>So we'll just use the Amazon logos that they have, And what I'm going to do, I'm going to use a combo move here. I'm just going to put both of these together. Copy. And import from URL. Great.\u003C\u002Fp>\u003Cp>Okay. So now we've got our project logo, public foreground. What are we gonna use? We're gonna choose the same file from the library. That's okay.\u003C\u002Fp>\u003Cp>We don't necessarily need 2 different images for our logo and our public foreground. It's all good. So we're just gonna paint that on. Great. Good, good luck.\u003C\u002Fp>\u003Cp>Okay. Alright. So as far as a public background image, we've got to find something that works. What are we going to do? Well, I'll show you.\u003C\u002Fp>\u003Cp>Way back when, I used to paint t shirts before I painted themes. And, yeah, one of the t shirts that I loved by my friends at the mountain, are these Wolf t shirts. These are such masterpieces. If you've ever seen them, they're some of the most highly rated shirts on Amazon. So let's just use one of those as the public background.\u003C\u002Fp>\u003Cp>We'll just paint that beautiful, beautiful theme there. Alright, so now we're looking for a favicon. I don't see that here. I'm not seeing it. Again, theming is a journey.\u003C\u002Fp>\u003Cp>There's many twists and turns that we have to navigate to bring out that theme. So let's just search for Amazon Fab icon. This looks like a good one. Copy that image address, and I'm just gonna brush that in there. Just pick up that half inch brush.\u003C\u002Fp>\u003Cp>The fab icon is such a small detail. Absolutely critical though. Alright, so for our project color, what we want to do, we want to find this Amazon yellow, this Amazon orange. Is it orange? Is it yellow?\u003C\u002Fp>\u003Cp>Is it in between? I don't know. But it's it's lovely. It's reassuring, but also stands out against that darker background. Alright.\u003C\u002Fp>\u003Cp>So we're just gonna paste that in. Now we've we've got that. We've got our project color. So this is gonna bring all of our buttons up to that color, but again, we want to match that Amazon website. So we've got to go in and we need to adjust this module bar logo.\u003C\u002Fp>\u003Cp>So to do that, what we're gonna do, we're gonna go into our modules. I'm not seeing that that CSS there. We're actually gonna have to use our custom CSS here. So we'll do a class selector module dash bar dot logo and just paint some background transparency on there. It's almost like we're removing it but essentially we're just blending it in.\u003C\u002Fp>\u003Cp>Just, you know, we'll add that important modifier just to make sure that it's set. Great. Okay. Alright. So next we're gonna dive in to this second nav, this secondary navigation.\u003C\u002Fp>\u003Cp>It's more of a navy blue like a midnight navy. Like a very soulful color. Alright. So now inside our list, we need to go in and set our foreground colors. Now this is a an advanced technique.\u003C\u002Fp>\u003Cp>Your mileage may vary, but I like to take my half inch brush, Just stroke the keys ever so softly and that way we can get this nice color effect that we're looking for. So for our divider here, that white is too much. Maybe we use a little color mix to bring it down. Mute that just a little bit, but as you can see we've got a weird hover effect going on. How can I fix that, right?\u003C\u002Fp>\u003Cp>That is going to be in our background hover and our background active color. So I can take this color mix and from our background hover maybe I just dial it back a bit, just a little bit, just dial it back just a little bit. It's very nice. You're doing great, by the way. Amazing.\u003C\u002Fp>\u003Cp>It is so fun and so enlightening just to see how some of these themes take shape. Alright, so this is nice, but at the very top we have our project bar that is getting in the way. It's just too much contrast. So let's remove that, set our foreground to be the same, and now we've completed the navigation. So now what we want to do is take a look at our fonts.\u003C\u002Fp>\u003Cp>Fonts always make or break a theme. And Amazon is using this Amazon ember font. Alright so if we look at our actual font network requests we could see that font. If I check our font face inside the document, I don't see it. So we're just gonna search for Amazon Amber font.\u003C\u002Fp>\u003Cp>There we go. We've got a breakdown of this particular font. I don't want to necessarily load 25 megabytes. Who has that space? It's a lot of space.\u003C\u002Fp>\u003Cp>So what we're gonna do is just copy this from this Amazon typography site, add some font face values for it. We're gonna do a little font family, ember. And then for our source declaration here, I'm gonna use URL and just input that URL that we get from the Amazon website. Now, we save that. Directus will load that custom CSS for us on all the pages.\u003C\u002Fp>\u003Cp>But the last piece of the puzzle, we've got to take our half inch brush and we've got to go into our fonts and just apply that particular setting. Right, okay. Yeah, I'm not seeing that being applied. Why is it not? Oh.\u003C\u002Fp>\u003Cp>Silly me. I put it in the dark theme. So now as we apply it to the light theme customization, our display font is ember. This is a great looking theme. It really pops to me.\u003C\u002Fp>\u003Cp>Let's take a look at the public login page. And have you ever seen a more beautiful public login page? 2 wolves, moon, Amazon. Harmonious. Harmonious.\u003C\u002Fp>\u003Cp>That's it for this episode of The Joy of Theming. Thank you for joining me. I hope to see you on the next one. Bye.\u003C\u002Fp>","Hi, friends. Welcome back to the show. I'm your host, Bry Ross. Before we start our theme today, I thought it was important to cover just how to clean your brush. So, this is how I like to clean my brush. It's super important to have a clean brush anytime you are doing a theme. You've got to make sure that you've got the right colors that you're putting on the theme. So what I like to do, I take my duster, give it a couple shakes, and then just aim the the little red tip of this nozzle at the brush. Just just give it a little flip there. And then you can hit it on your desk and now you've got a brush that's ready to go. So the theme that we are building today is Amazon from a to z. So if you're following along, I hope you've got your canvas prepped with a great looking data model, and let's get started by adding our project settings. Amazon. Everything, all the time. Whenever you want it. Alright. So we're gonna copy the project URL and just paste that in there. I would probably remove that ref tag as well. So we'll just paste that in there. And now we're ready to theme. So I always start everything by dialing in the branding. The branding is so important. We're going to inspect this Amazon logo from the website. It looks like it's gonna be a real pain to get access to this. They're using a sprite sheet which is is probably smart for performance, but makes our job as themers just a little bit more difficult. So we'll just use the Amazon logos that they have, And what I'm going to do, I'm going to use a combo move here. I'm just going to put both of these together. Copy. And import from URL. Great. Okay. So now we've got our project logo, public foreground. What are we gonna use? We're gonna choose the same file from the library. That's okay. We don't necessarily need 2 different images for our logo and our public foreground. It's all good. So we're just gonna paint that on. Great. Good, good luck. Okay. Alright. So as far as a public background image, we've got to find something that works. What are we going to do? Well, I'll show you. Way back when, I used to paint t shirts before I painted themes. And, yeah, one of the t shirts that I loved by my friends at the mountain, are these Wolf t shirts. These are such masterpieces. If you've ever seen them, they're some of the most highly rated shirts on Amazon. So let's just use one of those as the public background. We'll just paint that beautiful, beautiful theme there. Alright, so now we're looking for a favicon. I don't see that here. I'm not seeing it. Again, theming is a journey. There's many twists and turns that we have to navigate to bring out that theme. So let's just search for Amazon Fab icon. This looks like a good one. Copy that image address, and I'm just gonna brush that in there. Just pick up that half inch brush. The fab icon is such a small detail. Absolutely critical though. Alright, so for our project color, what we want to do, we want to find this Amazon yellow, this Amazon orange. Is it orange? Is it yellow? Is it in between? I don't know. But it's it's lovely. It's reassuring, but also stands out against that darker background. Alright. So we're just gonna paste that in. Now we've we've got that. We've got our project color. So this is gonna bring all of our buttons up to that color, but again, we want to match that Amazon website. So we've got to go in and we need to adjust this module bar logo. So to do that, what we're gonna do, we're gonna go into our modules. I'm not seeing that that CSS there. We're actually gonna have to use our custom CSS here. So we'll do a class selector module dash bar dot logo and just paint some background transparency on there. It's almost like we're removing it but essentially we're just blending it in. Just, you know, we'll add that important modifier just to make sure that it's set. Great. Okay. Alright. So next we're gonna dive in to this second nav, this secondary navigation. It's more of a navy blue like a midnight navy. Like a very soulful color. Alright. So now inside our list, we need to go in and set our foreground colors. Now this is a an advanced technique. Your mileage may vary, but I like to take my half inch brush, Just stroke the keys ever so softly and that way we can get this nice color effect that we're looking for. So for our divider here, that white is too much. Maybe we use a little color mix to bring it down. Mute that just a little bit, but as you can see we've got a weird hover effect going on. How can I fix that, right? That is going to be in our background hover and our background active color. So I can take this color mix and from our background hover maybe I just dial it back a bit, just a little bit, just dial it back just a little bit. It's very nice. You're doing great, by the way. Amazing. It is so fun and so enlightening just to see how some of these themes take shape. Alright, so this is nice, but at the very top we have our project bar that is getting in the way. It's just too much contrast. So let's remove that, set our foreground to be the same, and now we've completed the navigation. So now what we want to do is take a look at our fonts. Fonts always make or break a theme. And Amazon is using this Amazon ember font. Alright so if we look at our actual font network requests we could see that font. If I check our font face inside the document, I don't see it. So we're just gonna search for Amazon Amber font. There we go. We've got a breakdown of this particular font. I don't want to necessarily load 25 megabytes. Who has that space? It's a lot of space. So what we're gonna do is just copy this from this Amazon typography site, add some font face values for it. We're gonna do a little font family, ember. And then for our source declaration here, I'm gonna use URL and just input that URL that we get from the Amazon website. Now, we save that. Directus will load that custom CSS for us on all the pages. But the last piece of the puzzle, we've got to take our half inch brush and we've got to go into our fonts and just apply that particular setting. Right, okay. Yeah, I'm not seeing that being applied. Why is it not? Oh. Silly me. I put it in the dark theme. So now as we apply it to the light theme customization, our display font is ember. This is a great looking theme. It really pops to me. Let's take a look at the public login page. And have you ever seen a more beautiful public login page? 2 wolves, moon, Amazon. Harmonious. Harmonious. That's it for this episode of The Joy of Theming. Thank you for joining me. I hope to see you on the next one. Bye.",[3223],"bf6cdff4-f3b9-4b6d-8b29-5cca5bf1f75e",[],{"year":978,"number":2745,"id":2746,"episodes":3226,"show":3227},[2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2731],{"title":2760,"slug":2761},{"id":2316,"slug":3229,"vimeo_id":3230,"description":3231,"tile":3232,"length":1217,"resources":12,"people":12,"episode_number":159,"published":3233,"title":3234,"video_transcript_html":3235,"video_transcript_text":3236,"content":12,"seo":3237,"status":19,"episode_people":3238,"recommendations":3240,"season":3241},"realtime-leaderboard","936355804","Bryant races against the clock to build a real-time leaderboard for the Directus Arcade game – Duckin' Cold Emails. Watch as he builds features to submit and display high scores for the game using Directus Realtime and websockets.","b05a9814-7065-4816-841c-74e263ce5b20","2024-05-10","Mission: Realtime Leaderboard","\u003Cp>Speaker 0: Hi. Welcome back to another episode of 100 Apps, 100 Hours. I'm your host, Brian Gillespie, and today we are building something that I think is pretty cool. But here on 100 Apps 100 Hours, we build your suggestions, your favorite apps, or rebuild a clone of some other app in 1 hour or less, or publicly fail, get humiliated, trying. It was a lot of stress, to be honest with you.\u003C\u002Fp>\u003Cp>So the rules for this, number 1, we have 60 minutes to plan and build an application, no more, no less. And number 2, just use whatever you have at your disposal. So, could be AI, could be past projects, whatever we've got. We're just trying to speed run through applications. And today we've got a cool one.\u003C\u002Fp>\u003Cp>We're going to be building a real time leaderboard. So recently, as far as, like April Fools' Day, we put out this amazing game to make you a duckin' pro at cold email. So it's called duckin' cold emails. It's just a runner game where you face off against a sales rep who is sending you cold emails and pop ups on the screen here. So you duck or jump, you get a couple of lives, and at the end of it you can choose to play again, and you get a nice little score.\u003C\u002Fp>\u003Cp>So what we're going to do here is create a leaderboard for this, and riggity jig, away we go. Alright. So, 60 minutes to plan and build. Hopefully we don't need all of that, but maybe we will. That's how this works.\u003C\u002Fp>\u003Cp>Alright. So I usually like to start by mocking out the functionality that we need out of this. So what do I really want to happen here? When I play, I wanna be able to submit my score and if somebody else is playing around the world, I want that score to and they get a high score, I want that to pop up. I wanna see that.\u003C\u002Fp>\u003Cp>So we've got a real time tracking of high scores. This is really large. Let's just trim that down. Real time tracking of high scores, submit a username and message with that, maybe? Maybe we'll make this a little more competitive and that we can talk trash to each other while we do it.\u003C\u002Fp>\u003Cp>What else is there really? Display those scores. Display high scores. I'm gonna put maybe sound effects. I'm gonna put that in question mark.\u003C\u002Fp>\u003Cp>Maybe that'll be our stretch goal. So that's that's basically the functionality of this. Right? I don't know explicitly how long that's gonna take, but what do we have as far as a data model? That's one of the other things that I like to map out.\u003C\u002Fp>\u003Cp>We've got some actual players, but, you know, I'm not going to have a user that needs to log in for this. I'm just going to let we're going to do it NBA Jam style, where you can just key in your initials or something like that. So I'm just thinking here, there's probably only maybe just a single table for this, which is rare for us on a 100 apps to a 100 hours. We're going to have a value for the score, a username, there's gonna be an ID and a time stamp of of when that high score was, and then maybe a message. That seems like it's it to me.\u003C\u002Fp>\u003Cp>I don't wanna make this over complicated. Alright. So I'm gonna go into my direct assistance. This is totally blank. We're gonna create a new table.\u003C\u002Fp>\u003Cp>We're gonna call it scores. Can I zoom in on this? Yeah. There we go. We'll make it really fancy.\u003C\u002Fp>\u003Cp>We'll do the generated UUID. We've got date created. So this is a system field or optional system field that will basically whenever this item gets created it will pre populate some stuff for me. So basically what it does on create of this record in this collection, it will save the current date and time. So we'll unhide this because I do want to see that, And then we'll add a value.\u003C\u002Fp>\u003Cp>So as far as the number of emails ducked, I can't duck half an email. So that's gonna be an integer. Looks great. What else do we have? Maybe we'll make that half width.\u003C\u002Fp>\u003Cp>And a username. So go sweet handle. Cool. And then we're gonna have a message. So we'll just use the text area for that.\u003C\u002Fp>\u003Cp>The type is text, of course, so I could add, like, markdown or something for that if I wanted to, but we'll just keep this short and sweet. Alright. So as far as our data model, is that that's probably it. Right? Looks good.\u003C\u002Fp>\u003Cp>Getting on my toes here for this one. Alright. So we got this, specific application. It's basically just a Nuxt single page application. If I look, I've got a single index page, there's an arcade cabinet component that is doing most well, it's not really doing hardly any of the work it's just presentation.\u003C\u002Fp>\u003Cp>It gives us this nice arcade cabinet and some text down below. And then we also have oh, these were my buttons I was trying the last time I was in here. And then we have a game component. So the library that we're using for the actual game here is pretty interesting itself. You might go ahead and check it out if you are trying to build a game.\u003C\u002Fp>\u003Cp>Did we start the time? Oh, yeah. We did start the timer. Okay. It's called Kaboom JS.\u003C\u002Fp>\u003Cp>A pretty interesting library, actually. Makes it really easy to build quick little mini games like this, build stuff that's fun for your users. Alright. So that's the meat and potatoes of it. You know, there's some sprites, there's some functions, there's a couple of different scenes inside the game, but that's what we've got, right?\u003C\u002Fp>\u003Cp>So we've got our scores, let's go about setting up our real time connection. So Directus has real time baked into the platform. If I pull up my other project that I'm actually connected to, you can see I have enabled my WebSockets here. And away we go. Alright, so as far as the Knox application, I've just got a simple Directus plug in that provides me with a real time client.\u003C\u002Fp>\u003Cp>That's really all we've got. I've set the auth mode to public just so I've got, you know, anybody can connect to this. Again, we're not going to make authentication required here. What I'm also going to do is the ability to read scores and push scores. Okay.\u003C\u002Fp>\u003Cp>So what else do we have? I think that's pretty much it. Right? Let's dive in. Let's go to our arcade cabinet.\u003C\u002Fp>\u003Cp>Pull this up. Right. This is our wrapper component. I'm not sure where I'm gonna stick the leaderboard. Maybe up here at the top, maybe somewhere down here at the bottom.\u003C\u002Fp>\u003Cp>We could just cannibalize this bottom section down here. I'll zoom out in a little bit. But we're gonna get started with the SDK and WebSockets. So we've got a great guide on the documentation for this. It walks you through this in great detail.\u003C\u002Fp>\u003Cp>But we're just gonna pull this up. I'm gonna pull that Directus client. Direct cuts. Directus. I can't spell.\u003C\u002Fp>\u003Cp>Alright. We're gonna use the Nuxt app. Use Nuxt app composable to get that client. And then what we're gonna do is await directus dot connect. Alright.\u003C\u002Fp>\u003Cp>So that will connect and if we keep scrolling down, we can see that we've got a, open function here. You know, if we just wanna log that this was opened. We do this and we go back. And if I look in the console, should be getting a message that this was opened. Switching protocols.\u003C\u002Fp>\u003Cp>Not seeing any messages in here for whatever reason. Yeah. I don't see any particular messages, but let's keep moving on. We probably do need this directus dot message handler. Directus dot on web socket dot message console log events.\u003C\u002Fp>\u003Cp>What if we just log the entire event? Actually, that'll be a message. Same thing. Let's see what we get here. Do we have that connection established?\u003C\u002Fp>\u003Cp>Where is our connection? Why are we not seeing any messages through here? Right? Okay. Let's just add a subscription as well and see what we got.\u003C\u002Fp>\u003Cp>Oh, could it be our permission settings? Right? Let's log in and see. Directory's password. Alright.\u003C\u002Fp>\u003Cp>So if we look at no. We've got access to that. Alright. We've solved that problem already. Alright.\u003C\u002Fp>\u003Cp>So let's just create a subscription and see if we get that. Alright. So we'll go in and do directus dot subscribe, and we're gonna subscribe to the scores, And we're gonna get, we're gonna pass some options to this. So we want the event to be create. The query do we actually need a query?\u003C\u002Fp>\u003Cp>I don't think we let's not limit ourselves at this moment. And then maybe I wanna add, like, a UID to this, and we're gonna call this the scores sub. Subscription direct us dot subscribe. And let's see what we got here. Okay.\u003C\u002Fp>\u003Cp>So now I can see that particular message coming through. There's our subscription for this. So we're actually getting some data. Let's take a look and see if we open this up in a separate window. So we'll just open scores up.\u003C\u002Fp>\u003Cp>We'll create a new scores item, see if we get subscription for that. Yep. Okay. I see some data coming through. Great.\u003C\u002Fp>\u003Cp>So we are getting some data for our scores, which is good. Not sure why it's not showing the open function. Not really concerned with that, anyway. Alright. So what we wanna do, just one moment, please.\u003C\u002Fp>\u003Cp>We'll stop the clock. Okay. Had a hot call come in. We're restarting the clock. No extra work has been done, and I've totally lost track of where we were at in the process anyway.\u003C\u002Fp>\u003Cp>Alright. So, we've got our subscription. We can see that come through. You know, if we create scores over here, 77. If we create these scores, we should not we'll still see those come through, which is great.\u003C\u002Fp>\u003Cp>You know, we need to start working on our board. Alright. So let's flush this out a little bit more. Yeah. Maybe we want to grab the let's just work on our leaderboard first.\u003C\u002Fp>\u003Cp>That's what we'll do. We'll get some UI rolling for this. I'm just gonna replace this down here at the bottom. So we'll just drop this text out. Great.\u003C\u002Fp>\u003Cp>Just totally disappeared. And then we'll do let's add a add a heading for this. P high scores. Class font mono, font bold. Make this look really, really nice.\u003C\u002Fp>\u003Cp>Yeah. Text 2 XL, Text center. Add some styling to it. See what we get. High score should be white.\u003C\u002Fp>\u003Cp>We want all this to be white. Text white. Alright. There's our high scores. We'll go in and add some padding.\u003C\u002Fp>\u003Cp>Oh, I don't wanna pad that. Let's add padding to this. Maybe we do p 6, p 8, something like that. Alright. And then let's do a list of our high scores.\u003C\u002Fp>\u003Cp>So we'll do a list v 4 scores, 5 scores, and 7 scores ago. This will be grid. Grid calls 4, and then we'll just do, this is gonna be our score. Let's say 5. These could be a span, I guess.\u003C\u002Fp>\u003Cp>I don't know what the rules are about putting a p tag inside there. Span, we got John Ross, then we've got the message, and then we've got a time stamp. 3 minutes ago. Alright. Let's see what that looks like.\u003C\u002Fp>\u003Cp>Of course, it's not displaying. Let's put that text as lime green. Make it lime green. We'll make them bold as well. Do font mono.\u003C\u002Fp>\u003Cp>Okay. Alright. And then we probably got well, actually, let's make this our header row. Right? So we got the score.\u003C\u002Fp>\u003Cp>Got the user, And then we have the time. Alright. As far as our scores, we will make those extra large. Score, message, time. Great.\u003C\u002Fp>\u003Cp>Good. Okay. So as far as our scores, right, we're gonna need an array of scores. So let's go up, and we'll just add that. So we got scores here.\u003C\u002Fp>\u003Cp>Get your scores here. There's an array of those particular scores. And how do we, on a knit, we want to get those scores. Alright? So I could potentially fetch those via the REST client, but, let's use our put our heads together for this, and we'll do, we'll just send a message.\u003C\u002Fp>\u003Cp>Right? So let's create a quick function. We'll we'll reuse the same connection. So we're creating a subscription there, but let's create get initial scores, get initial scores. It's great.\u003C\u002Fp>\u003Cp>And then we're gonna do this where we say, direct us dot send message. So we're gonna send a message via WebSocket connection. We're gonna use the items type. We're going to use our read action, and then we have our collection of scores. We want to limit that to 10 scores.\u003C\u002Fp>\u003Cp>That makes sense. And then maybe we add oh, I'm sorry. This needs to be wrapped in a query though. Limit 10. And then maybe we want to sort those by value.\u003C\u002Fp>\u003Cp>So we're gonna get initial scores, and what? Okay. We'll just call get initial scores. Let's see what we've got when we open this up. So there's our data.\u003C\u002Fp>\u003Cp>Right? Looks good. But and those are sorted in the correct order. Alright. So let's add a handler for this.\u003C\u002Fp>\u003Cp>So maybe we add a UID for this as well so we can keep track of these. These are gonna be the initial scores. Alright. So if the event dot UID equals initial scores, we're going to populate the scores dot value with event dot data. Is it just dot data?\u003C\u002Fp>\u003Cp>I think it should just be this. Right? Alright. So we open up our view dev tools. We go to arcade cabinet.\u003C\u002Fp>\u003Cp>We'll just see. We've got some scores. Cool. Alright. So now that we are getting those scores, let's just iterate through those scores.\u003C\u002Fp>\u003Cp>So we'll do the same thing with the list. And this one we're gonna add a v 4. So that'll be a score and scores. Key will be the score dot ID, not scores, just score dot ID. And then we'll start populating.\u003C\u002Fp>\u003Cp>Score dot value. Why does it keep auto completing on me? Score dot username score dot message. And then the score dot timestamp. Alright.\u003C\u002Fp>\u003Cp>So we got our scores. Those are a little huge, so maybe we shrink those down. And maybe this is actually we want the score to be larger. Tex x l. Cool.\u003C\u002Fp>\u003Cp>And maybe we actually make these small. Okay. Alright. So I do have a function already, like a helper function in this. It's called get relative time.\u003C\u002Fp>\u003Cp>It is the function. It just returns like a a relative time stamp. There we go. 8 minutes ago. Looks great.\u003C\u002Fp>\u003Cp>Got our messages. We got our username. We got our scores. That's great. So now if we anytime we load this page, we're we're getting those initial scores.\u003C\u002Fp>\u003Cp>And, you know, now we've got to like, how are we gonna submit our other scores? How are we doing on time here? We got about 40 minutes left. So we we probably wanna build a form at the end of the game to track those scores and and allow people to submit them. But one of the other things that I wanna do here is maybe we use a a computed prop to to show these as well.\u003C\u002Fp>\u003Cp>We only want the latest ten scores maybe. Or, you know, we we could show the whole list, but then we're gonna have to resort those scores as well. So this is it might be a good thing that the computer prop is for. So we'll do, what, compute, const equals sorted scores equals computed. Oh, there we go.\u003C\u002Fp>\u003Cp>GitHub Copilot to the rescue, so we're at scores dot value. And then maybe that's what we use to show. Sorted scores. Alright. Do we still get the same thing?\u003C\u002Fp>\u003Cp>That's great. Okay. Awesome. And what else are we going to do? Maybe we add just a little bit of gap.\u003C\u002Fp>\u003Cp>Gap y 2. Oh, no. It does. It's not gonna be a gap. Let's do this where we do, like, a space y 2 below them.\u003C\u002Fp>\u003Cp>Do we need a divider there? Maybe we do have a divider. Class equals text at lime 500. I don't think that's having the effect that I want. Okay.\u003C\u002Fp>\u003Cp>Regardless, there we go. We've got this function. Now we want to, whenever we play the game, right, we need to submit a score. So at the end here, I've got 0 emails. Maybe we add another button for our actual scores.\u003C\u002Fp>\u003Cp>So we're gonna go into the game itself. Right. And what are we gonna do here? We're gonna add a couple things. Right.\u003C\u002Fp>\u003Cp>We probably want to, the Kaboom library is based on Canvas, so we're not going to have like HTML elements there to work with. So I'm thinking we just maybe show a modal. I've got this Nuxt UI library included in this starter kit for this, And I do know that they have a little modal component that we could pop up. So let's roll with that and see what we got. Alright.\u003C\u002Fp>\u003Cp>So inside the game, you can see here's all our variables for that. But let's just a knit a couple of of different variables up here at the top. We've got a, maybe like a show form. That'll be reactive. And then we have, like, a, what are we gonna call this?\u003C\u002Fp>\u003Cp>I don't wanna call it score because Kaboom uses that internally. Maybe we just call this the player score, and we could use just like a reactive object for this instead of a ref. And so we have a value for the score, we'll set that to 0 by default. We've got a message for that and we've got a username. So this is gonna be the data that we submit, when the game is completed.\u003C\u002Fp>\u003Cp>Alright. So scroll down. We'll probably need a submit function for that as well. So we'll do a function. And we don't have to use async here because we're gonna reuse that same WebSocket connection.\u003C\u002Fp>\u003Cp>Right? So we got submit score. Let's go up here to the top. We're gonna get our directus client. Directus equals use Nuxt app from that plug in.\u003C\u002Fp>\u003Cp>And inside here, let's do do we do like a try catch? Just in case there are errors. Cool. We are going to submit a message. Direct us, send message.\u003C\u002Fp>\u003Cp>And here, the type is gonna be what? It will be items again. So we wanna send to the items. We're gonna do an action. I don't need to put that there.\u003C\u002Fp>\u003Cp>Do action of create. The collection is gonna be scores, and then our data will be the player score. Okay? And then after that, we would not show the form, and then, yeah, maybe here we'll just console dot error any errors that we receive. Alright.\u003C\u002Fp>\u003Cp>So that's the scaffold here for this. We're gonna need a template for this. So we'll have to add that modal to it. So we'll scroll down. Scroll down.\u003C\u002Fp>\u003Cp>And then we we're gonna need to actually show that somewhere as well. So Canvas. Let's go back into the arcade cabinet. Class p t. Actually, gonna do do I need this?\u003C\u002Fp>\u003Cp>Yes. Let's place that here. P t minus what was it? P t 24 fix. Alright.\u003C\u002Fp>\u003Cp>So I'm gonna move that here. And can I just stick this model in there? U model v model equals show form. And do we want we don't want the overlay for this either. So let's do false on the overlay.\u003C\u002Fp>\u003Cp>And we'll say form here. Alright. Does that mess with anything on our game? I don't think so. If we go into our game, we should see this variable somewhere in show form.\u003C\u002Fp>\u003Cp>We can check the box, change that variable. Nope. Not updating it. But regardless, should be good. Okay.\u003C\u002Fp>\u003Cp>Why why can't you edit that? True. Just always set to false. Weird. Alright.\u003C\u002Fp>\u003Cp>Regardless, what we're we're gonna do now is, at the end of this game, we need to add a button to submit our scores. So we're gonna find the end scene. Is it end? No. What is this gonna be?\u003C\u002Fp>\u003Cp>Countdown. Let's just start countdown. Okay. So we're looking for the lose scene. And here inside the lose scene, we've got a restart button.\u003C\u002Fp>\u003Cp>We've got a text inside that button. So I'm just gonna copy these items here. Come down. This is gonna be the submit high score, score button logic just so we can comment. Let's call this the submit score button Submit.\u003C\u002Fp>\u003Cp>Score button. Is it gonna be the same color button? Maybe we make it purple. I don't know what the RGB is for purple. Purple RGB.\u003C\u002Fp>\u003Cp>Let's see. Like a blue violet. Okay. We'll roll with that. Not sure that's exactly what I want, but okay.\u003C\u002Fp>\u003Cp>And then on the submit score button dot on click, we want to show form. Nobody ever calls me. Show form equals true for the value and then inside the actual text for this, we're gonna say submit score. And that will be where we've got that restart button. We're gonna do submit score button dotpos.\u003C\u002Fp>\u003Cp>Okay. Alright. So if we did everything correct, hopefully, this will work. And we'll be able to see the submit score button. Okay.\u003C\u002Fp>\u003Cp>It's now taking the place of our other button, but we need to fix that. So let's just move it out of the way. Submit score button. Let's move it to the other side so we could see this is width divided by 4. Let's do the width.\u003C\u002Fp>\u003Cp>We'll just do the width of the canvas minus width divided by 4. Is that gonna get us what we want? We'll just quickly lose and and test to make sure. Lots of distractions on this episode as well. Alright.\u003C\u002Fp>\u003Cp>So submit. We can see a form here. That's looking great. Let's wrap this inside a card. Ucard.\u003C\u002Fp>\u003Cp>K. And then we're gonna build a form based on this. So, Nuxt has this UI library has some of these form elements. We're going to use a form group. We'll give it a label.\u003C\u002Fp>\u003Cp>What are we going to have for the label? We'll have the username. And inside that we'll have an input. So u input v model player score dot username. Let's see what that looks like.\u003C\u002Fp>\u003Cp>Okay. That looks nice. So also inside this, maybe we have a p, submit your score. Old text x l. Submit your score.\u003C\u002Fp>\u003Cp>We got a username. And then we want to actually show the score as well. Right? So fontmonotext. Violet.\u003C\u002Fp>\u003Cp>500. You deduct what? Player score dot value emails. Alright. So we're only showing that we ducked we ducked, just 0 emails.\u003C\u002Fp>\u003Cp>Right? So we're probably gonna have to fix that. Maybe we don't even need that actually. We'll just show you doc 0 emails. Add some padding between these 2.\u003C\u002Fp>\u003Cp>Just using space y 4, just a little helper class. Alright. Is that giving me what I want? No. It's not.\u003C\u002Fp>\u003Cp>Something about the card space y 4. Okay. And then we're gonna have another form group for our message. Alright. We got our message.\u003C\u002Fp>\u003Cp>You input that's actually gonna be you text area, v model. K. And what else do we need? We need some buttons. Submit, u button, cancel.\u003C\u002Fp>\u003Cp>So we get 2 buttons. Let's make this size large. And this will be let's wrap this in a div. Just flex them, give a little bit of gap between the 2. And this will be variant, what, like a outline, maybe?\u003C\u002Fp>\u003Cp>That's good. P class. K. And then we're gonna I don't know what happened to our spacing. Let's add that back.\u003C\u002Fp>\u003Cp>Okay. So there's our form. We've got submit score. You deduct 0 emails. The other thing that we're gonna do here, if we submit this score let's just test it out and see.\u003C\u002Fp>\u003Cp>Right? Bryant Ross. Submit. Are we actually getting the scores? We are not getting the score.\u003C\u002Fp>\u003Cp>Is that score showing up though? We refresh indirectus. Okay. So we just see that score show up, but we're not populating that score to the scores array, which we need to sort. And then, also, if we submit the scores again, you can see we've got the same message.\u003C\u002Fp>\u003Cp>So we probably want to reset the message every time. So let's just do this, player score dot, message equals null. Okay. So whenever we score, submit that score. Good.\u003C\u002Fp>\u003Cp>What's the other thing that we need to do? We need to iterate this player score reactive value anytime we increase the score. So here's a score plus plus. What we're gonna do here is player score dot value equals the score. So after we iterate, we're going to plug that score, and then we need to actually populate our scores here as well.\u003C\u002Fp>\u003Cp>Right? So if we go back to our arcade cabinet, we need another event handler here. So if the event dot UID equals scores dot sub, then we want to push that event data into the scores array, and that should trigger what we want. But, this is actually gonna be an array, so we can't really push that into the array. We need to destructure that.\u003C\u002Fp>\u003Cp>So, thinking this should get us where we want to be. Event data is not iterable. Let's scope this down. Event dot event equals create. And this is confusing.\u003C\u002Fp>\u003Cp>Right? Let's swap this out. Message. Change this over to message so that that is a little less confusing. Okay.\u003C\u002Fp>\u003Cp>So now we're seeing those scores populate correctly. Let's just give this a shot and see what we got. Alright. We're facing off against John Ross. Duct a couple of emails.\u003C\u002Fp>\u003Cp>We'll close this out. We'll submit our score. You ducked 4 emails. This is gonna be John Ross. Hi, guys.\u003C\u002Fp>\u003Cp>And we submit, and now we can see that score being populated automatically. So that's pretty freaking awesome. Right? What else do we want to do with this? Maybe we animate it a little bit?\u003C\u002Fp>\u003Cp>One of the libraries that I like to use that is actually included in this one is, v auto animate. Or, well, it's not, v auto. That's the directive for this. But it is, auto animate by the the fine folks at FormKit. So one line of code, you know, you've all seen this before.\u003C\u002Fp>\u003Cp>You add logic to it. But this one, it auto animates for you, giving you kind of a nice UI. So if we try this again, test message. Yeah. Probably have to refresh the page after I save it.\u003C\u002Fp>\u003Cp>But there we go. We've got our scores populating. These should be sorted in order as well. What do we have time left on the clock? We've got 22 minutes left.\u003C\u002Fp>\u003Cp>You know, we could go through and build a dashboard for this, but let's just kinda recap where we're at. We've got the real time tracking of the scores. We've got to submit a username. We are displaying those high scores. Let's add some sound effects.\u003C\u002Fp>\u003Cp>Let's just test it out one more time, make sure this is actually working first. Alright. Duck. Alright. Got 6, 7.\u003C\u002Fp>\u003Cp>Let's just die right now at this moment. And this will be Bry Ross is my username. You stink John. And I take my place on the leaderboard. Cool.\u003C\u002Fp>\u003Cp>Alright. So calling out when, you know, sound effects, maybe we could go in those. I'm not sure you're gonna be able to hear them though on this recording. So as far as a real time leaderboard, I I think we're calling that good. That is finished.\u003C\u002Fp>\u003Cp>Pretty savvy. Where could we take this from here? Right? We could go a a lot of different directions with this. You know, expanding this for, like, user authentication, maybe pull in, like, a GitHub username, something like that.\u003C\u002Fp>\u003Cp>But I'm pretty comfortable with this. I think I'm going to button this up and we'll probably actually ship this to the live version of this at directus. Is\u002Farcade. I think this will look nice with a leaderboard on here. So that's it for this episode of 100 Apps, 100 Hours.\u003C\u002Fp>\u003Cp>I think this may be the shortest episode yet. That's it though. That's good. In and out. I hope to see you on the next one.\u003C\u002Fp>\u003Cp>Bye.\u003C\u002Fp>","Hi. Welcome back to another episode of 100 Apps, 100 Hours. I'm your host, Brian Gillespie, and today we are building something that I think is pretty cool. But here on 100 Apps 100 Hours, we build your suggestions, your favorite apps, or rebuild a clone of some other app in 1 hour or less, or publicly fail, get humiliated, trying. It was a lot of stress, to be honest with you. So the rules for this, number 1, we have 60 minutes to plan and build an application, no more, no less. And number 2, just use whatever you have at your disposal. So, could be AI, could be past projects, whatever we've got. We're just trying to speed run through applications. And today we've got a cool one. We're going to be building a real time leaderboard. So recently, as far as, like April Fools' Day, we put out this amazing game to make you a duckin' pro at cold email. So it's called duckin' cold emails. It's just a runner game where you face off against a sales rep who is sending you cold emails and pop ups on the screen here. So you duck or jump, you get a couple of lives, and at the end of it you can choose to play again, and you get a nice little score. So what we're going to do here is create a leaderboard for this, and riggity jig, away we go. Alright. So, 60 minutes to plan and build. Hopefully we don't need all of that, but maybe we will. That's how this works. Alright. So I usually like to start by mocking out the functionality that we need out of this. So what do I really want to happen here? When I play, I wanna be able to submit my score and if somebody else is playing around the world, I want that score to and they get a high score, I want that to pop up. I wanna see that. So we've got a real time tracking of high scores. This is really large. Let's just trim that down. Real time tracking of high scores, submit a username and message with that, maybe? Maybe we'll make this a little more competitive and that we can talk trash to each other while we do it. What else is there really? Display those scores. Display high scores. I'm gonna put maybe sound effects. I'm gonna put that in question mark. Maybe that'll be our stretch goal. So that's that's basically the functionality of this. Right? I don't know explicitly how long that's gonna take, but what do we have as far as a data model? That's one of the other things that I like to map out. We've got some actual players, but, you know, I'm not going to have a user that needs to log in for this. I'm just going to let we're going to do it NBA Jam style, where you can just key in your initials or something like that. So I'm just thinking here, there's probably only maybe just a single table for this, which is rare for us on a 100 apps to a 100 hours. We're going to have a value for the score, a username, there's gonna be an ID and a time stamp of of when that high score was, and then maybe a message. That seems like it's it to me. I don't wanna make this over complicated. Alright. So I'm gonna go into my direct assistance. This is totally blank. We're gonna create a new table. We're gonna call it scores. Can I zoom in on this? Yeah. There we go. We'll make it really fancy. We'll do the generated UUID. We've got date created. So this is a system field or optional system field that will basically whenever this item gets created it will pre populate some stuff for me. So basically what it does on create of this record in this collection, it will save the current date and time. So we'll unhide this because I do want to see that, And then we'll add a value. So as far as the number of emails ducked, I can't duck half an email. So that's gonna be an integer. Looks great. What else do we have? Maybe we'll make that half width. And a username. So go sweet handle. Cool. And then we're gonna have a message. So we'll just use the text area for that. The type is text, of course, so I could add, like, markdown or something for that if I wanted to, but we'll just keep this short and sweet. Alright. So as far as our data model, is that that's probably it. Right? Looks good. Getting on my toes here for this one. Alright. So we got this, specific application. It's basically just a Nuxt single page application. If I look, I've got a single index page, there's an arcade cabinet component that is doing most well, it's not really doing hardly any of the work it's just presentation. It gives us this nice arcade cabinet and some text down below. And then we also have oh, these were my buttons I was trying the last time I was in here. And then we have a game component. So the library that we're using for the actual game here is pretty interesting itself. You might go ahead and check it out if you are trying to build a game. Did we start the time? Oh, yeah. We did start the timer. Okay. It's called Kaboom JS. A pretty interesting library, actually. Makes it really easy to build quick little mini games like this, build stuff that's fun for your users. Alright. So that's the meat and potatoes of it. You know, there's some sprites, there's some functions, there's a couple of different scenes inside the game, but that's what we've got, right? So we've got our scores, let's go about setting up our real time connection. So Directus has real time baked into the platform. If I pull up my other project that I'm actually connected to, you can see I have enabled my WebSockets here. And away we go. Alright, so as far as the Knox application, I've just got a simple Directus plug in that provides me with a real time client. That's really all we've got. I've set the auth mode to public just so I've got, you know, anybody can connect to this. Again, we're not going to make authentication required here. What I'm also going to do is the ability to read scores and push scores. Okay. So what else do we have? I think that's pretty much it. Right? Let's dive in. Let's go to our arcade cabinet. Pull this up. Right. This is our wrapper component. I'm not sure where I'm gonna stick the leaderboard. Maybe up here at the top, maybe somewhere down here at the bottom. We could just cannibalize this bottom section down here. I'll zoom out in a little bit. But we're gonna get started with the SDK and WebSockets. So we've got a great guide on the documentation for this. It walks you through this in great detail. But we're just gonna pull this up. I'm gonna pull that Directus client. Direct cuts. Directus. I can't spell. Alright. We're gonna use the Nuxt app. Use Nuxt app composable to get that client. And then what we're gonna do is await directus dot connect. Alright. So that will connect and if we keep scrolling down, we can see that we've got a, open function here. You know, if we just wanna log that this was opened. We do this and we go back. And if I look in the console, should be getting a message that this was opened. Switching protocols. Not seeing any messages in here for whatever reason. Yeah. I don't see any particular messages, but let's keep moving on. We probably do need this directus dot message handler. Directus dot on web socket dot message console log events. What if we just log the entire event? Actually, that'll be a message. Same thing. Let's see what we get here. Do we have that connection established? Where is our connection? Why are we not seeing any messages through here? Right? Okay. Let's just add a subscription as well and see what we got. Oh, could it be our permission settings? Right? Let's log in and see. Directory's password. Alright. So if we look at no. We've got access to that. Alright. We've solved that problem already. Alright. So let's just create a subscription and see if we get that. Alright. So we'll go in and do directus dot subscribe, and we're gonna subscribe to the scores, And we're gonna get, we're gonna pass some options to this. So we want the event to be create. The query do we actually need a query? I don't think we let's not limit ourselves at this moment. And then maybe I wanna add, like, a UID to this, and we're gonna call this the scores sub. Subscription direct us dot subscribe. And let's see what we got here. Okay. So now I can see that particular message coming through. There's our subscription for this. So we're actually getting some data. Let's take a look and see if we open this up in a separate window. So we'll just open scores up. We'll create a new scores item, see if we get subscription for that. Yep. Okay. I see some data coming through. Great. So we are getting some data for our scores, which is good. Not sure why it's not showing the open function. Not really concerned with that, anyway. Alright. So what we wanna do, just one moment, please. We'll stop the clock. Okay. Had a hot call come in. We're restarting the clock. No extra work has been done, and I've totally lost track of where we were at in the process anyway. Alright. So, we've got our subscription. We can see that come through. You know, if we create scores over here, 77. If we create these scores, we should not we'll still see those come through, which is great. You know, we need to start working on our board. Alright. So let's flush this out a little bit more. Yeah. Maybe we want to grab the let's just work on our leaderboard first. That's what we'll do. We'll get some UI rolling for this. I'm just gonna replace this down here at the bottom. So we'll just drop this text out. Great. Just totally disappeared. And then we'll do let's add a add a heading for this. P high scores. Class font mono, font bold. Make this look really, really nice. Yeah. Text 2 XL, Text center. Add some styling to it. See what we get. High score should be white. We want all this to be white. Text white. Alright. There's our high scores. We'll go in and add some padding. Oh, I don't wanna pad that. Let's add padding to this. Maybe we do p 6, p 8, something like that. Alright. And then let's do a list of our high scores. So we'll do a list v 4 scores, 5 scores, and 7 scores ago. This will be grid. Grid calls 4, and then we'll just do, this is gonna be our score. Let's say 5. These could be a span, I guess. I don't know what the rules are about putting a p tag inside there. Span, we got John Ross, then we've got the message, and then we've got a time stamp. 3 minutes ago. Alright. Let's see what that looks like. Of course, it's not displaying. Let's put that text as lime green. Make it lime green. We'll make them bold as well. Do font mono. Okay. Alright. And then we probably got well, actually, let's make this our header row. Right? So we got the score. Got the user, And then we have the time. Alright. As far as our scores, we will make those extra large. Score, message, time. Great. Good. Okay. So as far as our scores, right, we're gonna need an array of scores. So let's go up, and we'll just add that. So we got scores here. Get your scores here. There's an array of those particular scores. And how do we, on a knit, we want to get those scores. Alright? So I could potentially fetch those via the REST client, but, let's use our put our heads together for this, and we'll do, we'll just send a message. Right? So let's create a quick function. We'll we'll reuse the same connection. So we're creating a subscription there, but let's create get initial scores, get initial scores. It's great. And then we're gonna do this where we say, direct us dot send message. So we're gonna send a message via WebSocket connection. We're gonna use the items type. We're going to use our read action, and then we have our collection of scores. We want to limit that to 10 scores. That makes sense. And then maybe we add oh, I'm sorry. This needs to be wrapped in a query though. Limit 10. And then maybe we want to sort those by value. So we're gonna get initial scores, and what? Okay. We'll just call get initial scores. Let's see what we've got when we open this up. So there's our data. Right? Looks good. But and those are sorted in the correct order. Alright. So let's add a handler for this. So maybe we add a UID for this as well so we can keep track of these. These are gonna be the initial scores. Alright. So if the event dot UID equals initial scores, we're going to populate the scores dot value with event dot data. Is it just dot data? I think it should just be this. Right? Alright. So we open up our view dev tools. We go to arcade cabinet. We'll just see. We've got some scores. Cool. Alright. So now that we are getting those scores, let's just iterate through those scores. So we'll do the same thing with the list. And this one we're gonna add a v 4. So that'll be a score and scores. Key will be the score dot ID, not scores, just score dot ID. And then we'll start populating. Score dot value. Why does it keep auto completing on me? Score dot username score dot message. And then the score dot timestamp. Alright. So we got our scores. Those are a little huge, so maybe we shrink those down. And maybe this is actually we want the score to be larger. Tex x l. Cool. And maybe we actually make these small. Okay. Alright. So I do have a function already, like a helper function in this. It's called get relative time. It is the function. It just returns like a a relative time stamp. There we go. 8 minutes ago. Looks great. Got our messages. We got our username. We got our scores. That's great. So now if we anytime we load this page, we're we're getting those initial scores. And, you know, now we've got to like, how are we gonna submit our other scores? How are we doing on time here? We got about 40 minutes left. So we we probably wanna build a form at the end of the game to track those scores and and allow people to submit them. But one of the other things that I wanna do here is maybe we use a a computed prop to to show these as well. We only want the latest ten scores maybe. Or, you know, we we could show the whole list, but then we're gonna have to resort those scores as well. So this is it might be a good thing that the computer prop is for. So we'll do, what, compute, const equals sorted scores equals computed. Oh, there we go. GitHub Copilot to the rescue, so we're at scores dot value. And then maybe that's what we use to show. Sorted scores. Alright. Do we still get the same thing? That's great. Okay. Awesome. And what else are we going to do? Maybe we add just a little bit of gap. Gap y 2. Oh, no. It does. It's not gonna be a gap. Let's do this where we do, like, a space y 2 below them. Do we need a divider there? Maybe we do have a divider. Class equals text at lime 500. I don't think that's having the effect that I want. Okay. Regardless, there we go. We've got this function. Now we want to, whenever we play the game, right, we need to submit a score. So at the end here, I've got 0 emails. Maybe we add another button for our actual scores. So we're gonna go into the game itself. Right. And what are we gonna do here? We're gonna add a couple things. Right. We probably want to, the Kaboom library is based on Canvas, so we're not going to have like HTML elements there to work with. So I'm thinking we just maybe show a modal. I've got this Nuxt UI library included in this starter kit for this, And I do know that they have a little modal component that we could pop up. So let's roll with that and see what we got. Alright. So inside the game, you can see here's all our variables for that. But let's just a knit a couple of of different variables up here at the top. We've got a, maybe like a show form. That'll be reactive. And then we have, like, a, what are we gonna call this? I don't wanna call it score because Kaboom uses that internally. Maybe we just call this the player score, and we could use just like a reactive object for this instead of a ref. And so we have a value for the score, we'll set that to 0 by default. We've got a message for that and we've got a username. So this is gonna be the data that we submit, when the game is completed. Alright. So scroll down. We'll probably need a submit function for that as well. So we'll do a function. And we don't have to use async here because we're gonna reuse that same WebSocket connection. Right? So we got submit score. Let's go up here to the top. We're gonna get our directus client. Directus equals use Nuxt app from that plug in. And inside here, let's do do we do like a try catch? Just in case there are errors. Cool. We are going to submit a message. Direct us, send message. And here, the type is gonna be what? It will be items again. So we wanna send to the items. We're gonna do an action. I don't need to put that there. Do action of create. The collection is gonna be scores, and then our data will be the player score. Okay? And then after that, we would not show the form, and then, yeah, maybe here we'll just console dot error any errors that we receive. Alright. So that's the scaffold here for this. We're gonna need a template for this. So we'll have to add that modal to it. So we'll scroll down. Scroll down. And then we we're gonna need to actually show that somewhere as well. So Canvas. Let's go back into the arcade cabinet. Class p t. Actually, gonna do do I need this? Yes. Let's place that here. P t minus what was it? P t 24 fix. Alright. So I'm gonna move that here. And can I just stick this model in there? U model v model equals show form. And do we want we don't want the overlay for this either. So let's do false on the overlay. And we'll say form here. Alright. Does that mess with anything on our game? I don't think so. If we go into our game, we should see this variable somewhere in show form. We can check the box, change that variable. Nope. Not updating it. But regardless, should be good. Okay. Why why can't you edit that? True. Just always set to false. Weird. Alright. Regardless, what we're we're gonna do now is, at the end of this game, we need to add a button to submit our scores. So we're gonna find the end scene. Is it end? No. What is this gonna be? Countdown. Let's just start countdown. Okay. So we're looking for the lose scene. And here inside the lose scene, we've got a restart button. We've got a text inside that button. So I'm just gonna copy these items here. Come down. This is gonna be the submit high score, score button logic just so we can comment. Let's call this the submit score button Submit. Score button. Is it gonna be the same color button? Maybe we make it purple. I don't know what the RGB is for purple. Purple RGB. Let's see. Like a blue violet. Okay. We'll roll with that. Not sure that's exactly what I want, but okay. And then on the submit score button dot on click, we want to show form. Nobody ever calls me. Show form equals true for the value and then inside the actual text for this, we're gonna say submit score. And that will be where we've got that restart button. We're gonna do submit score button dotpos. Okay. Alright. So if we did everything correct, hopefully, this will work. And we'll be able to see the submit score button. Okay. It's now taking the place of our other button, but we need to fix that. So let's just move it out of the way. Submit score button. Let's move it to the other side so we could see this is width divided by 4. Let's do the width. We'll just do the width of the canvas minus width divided by 4. Is that gonna get us what we want? We'll just quickly lose and and test to make sure. Lots of distractions on this episode as well. Alright. So submit. We can see a form here. That's looking great. Let's wrap this inside a card. Ucard. K. And then we're gonna build a form based on this. So, Nuxt has this UI library has some of these form elements. We're going to use a form group. We'll give it a label. What are we going to have for the label? We'll have the username. And inside that we'll have an input. So u input v model player score dot username. Let's see what that looks like. Okay. That looks nice. So also inside this, maybe we have a p, submit your score. Old text x l. Submit your score. We got a username. And then we want to actually show the score as well. Right? So fontmonotext. Violet. 500. You deduct what? Player score dot value emails. Alright. So we're only showing that we ducked we ducked, just 0 emails. Right? So we're probably gonna have to fix that. Maybe we don't even need that actually. We'll just show you doc 0 emails. Add some padding between these 2. Just using space y 4, just a little helper class. Alright. Is that giving me what I want? No. It's not. Something about the card space y 4. Okay. And then we're gonna have another form group for our message. Alright. We got our message. You input that's actually gonna be you text area, v model. K. And what else do we need? We need some buttons. Submit, u button, cancel. So we get 2 buttons. Let's make this size large. And this will be let's wrap this in a div. Just flex them, give a little bit of gap between the 2. And this will be variant, what, like a outline, maybe? That's good. P class. K. And then we're gonna I don't know what happened to our spacing. Let's add that back. Okay. So there's our form. We've got submit score. You deduct 0 emails. The other thing that we're gonna do here, if we submit this score let's just test it out and see. Right? Bryant Ross. Submit. Are we actually getting the scores? We are not getting the score. Is that score showing up though? We refresh indirectus. Okay. So we just see that score show up, but we're not populating that score to the scores array, which we need to sort. And then, also, if we submit the scores again, you can see we've got the same message. So we probably want to reset the message every time. So let's just do this, player score dot, message equals null. Okay. So whenever we score, submit that score. Good. What's the other thing that we need to do? We need to iterate this player score reactive value anytime we increase the score. So here's a score plus plus. What we're gonna do here is player score dot value equals the score. So after we iterate, we're going to plug that score, and then we need to actually populate our scores here as well. Right? So if we go back to our arcade cabinet, we need another event handler here. So if the event dot UID equals scores dot sub, then we want to push that event data into the scores array, and that should trigger what we want. But, this is actually gonna be an array, so we can't really push that into the array. We need to destructure that. So, thinking this should get us where we want to be. Event data is not iterable. Let's scope this down. Event dot event equals create. And this is confusing. Right? Let's swap this out. Message. Change this over to message so that that is a little less confusing. Okay. So now we're seeing those scores populate correctly. Let's just give this a shot and see what we got. Alright. We're facing off against John Ross. Duct a couple of emails. We'll close this out. We'll submit our score. You ducked 4 emails. This is gonna be John Ross. Hi, guys. And we submit, and now we can see that score being populated automatically. So that's pretty freaking awesome. Right? What else do we want to do with this? Maybe we animate it a little bit? One of the libraries that I like to use that is actually included in this one is, v auto animate. Or, well, it's not, v auto. That's the directive for this. But it is, auto animate by the the fine folks at FormKit. So one line of code, you know, you've all seen this before. You add logic to it. But this one, it auto animates for you, giving you kind of a nice UI. So if we try this again, test message. Yeah. Probably have to refresh the page after I save it. But there we go. We've got our scores populating. These should be sorted in order as well. What do we have time left on the clock? We've got 22 minutes left. You know, we could go through and build a dashboard for this, but let's just kinda recap where we're at. We've got the real time tracking of the scores. We've got to submit a username. We are displaying those high scores. Let's add some sound effects. Let's just test it out one more time, make sure this is actually working first. Alright. Duck. Alright. Got 6, 7. Let's just die right now at this moment. And this will be Bry Ross is my username. You stink John. And I take my place on the leaderboard. Cool. Alright. So calling out when, you know, sound effects, maybe we could go in those. I'm not sure you're gonna be able to hear them though on this recording. So as far as a real time leaderboard, I I think we're calling that good. That is finished. Pretty savvy. Where could we take this from here? Right? We could go a a lot of different directions with this. You know, expanding this for, like, user authentication, maybe pull in, like, a GitHub username, something like that. But I'm pretty comfortable with this. I think I'm going to button this up and we'll probably actually ship this to the live version of this at directus. Is\u002Farcade. I think this will look nice with a leaderboard on here. So that's it for this episode of 100 Apps, 100 Hours. I think this may be the shortest episode yet. That's it though. That's good. In and out. I hope to see you on the next one. Bye.","06ce65f4-331e-4797-8158-82b1d29dee97",[3239],"765f110e-fcec-422a-b672-2ebb1f8830d6",[],{"year":978,"number":2062,"id":2311,"episodes":3242,"show":3243},[2313,2314,2315,2316,2317,2318,2319,2320,2321,2298],{"title":322,"slug":323},{"id":2393,"slug":3245,"vimeo_id":3246,"description":3247,"tile":3248,"length":264,"resources":12,"people":3249,"episode_number":740,"published":3251,"title":3252,"video_transcript_html":3253,"video_transcript_text":3254,"content":12,"seo":12,"status":19,"episode_people":3255,"recommendations":3257,"season":3258},"10-11","953540604","Directus 10.11 introduces public registration and a set of other enhancements, optimizations, and bug fixes.","fffa27ec-9b05-4bcd-88e4-848df8ab0d73",[3250],{"name":2820,"url":2821},"2024-05-08","What's New in Directus Version 10.11","\u003Cp>Kevin: Hello. My name is Kevin. And today, I'm gonna tell you what's new in Directus 10.11, public user registration. The key new feature in this release is the introduction of public user registration. Once enabled in your project, you can register new users directly from the login screen of the data studio.\u003C\u002Fp>\n\u003Cp>There's also an option for verifying user emails which sends the user an email with a link that they must click before being able to log in. And you can use Directus' powerful filtering system to create rules around which emails can be used. And of course, like everything in Directus, you can access this new feature via our new API endpoints for registering and verifying users. This is a super useful addition, Removing hacky workarounds that many of you had to use until now. This also means that in use cases where your end users interact with Directus via the Data Studio, Directus can effectively function as your complete application.\u003C\u002Fp>\n\u003Cp>Behavior change for empty search results. This is both a fix and a potential breaking change. But if you use the search parameter and there are no results, we will now return no results. Before, we would return all items. And this is, of course, important if you're using the search parameter in your applications.\u003C\u002Fp>\n\u003Cp>Community Contributions. Thank you to all of our community members who contributed towards this release. Thank you for Florian for making sure that v list items are correctly displayed with thumbnails and for a small CSS variable fix in the search input. Thanks to Evout for updating the send method in mail service to better handle asynchronous calls. Thank you to Mingfei for also an earlier fix that we made to the Edit Data operation to the Create Data and Update operations as well.\u003C\u002Fp>\n\u003Cp>Thank you to Gerard for adding the on update constraints to the relation service and to Vasmi for adding the auto complete attribute to the 2 factor authentication field. Thank you to Niko for adding support in configuring p m 2 error log storage and Yonas for fixing an issue with the WYSIWYG interface that could cause some new links to behave unexpectedly. Finally, thanks to Miguel for adding a GraphQL singleton helper type for the SDK. And that's directives 10.11. Thank you to everyone involved in putting it together.\u003C\u002Fp>\n\u003Cp>It's now available on NPM and Docker Hub and we'll see you in the next release.\u003C\u002Fp>","Hello. My name is Kevin. And today, I'm gonna tell you what's new in Directus 10.11, public user registration. The key new feature in this release is the introduction of public user registration. Once enabled in your project, you can register new users directly from the login screen of the data studio. There's also an option for verifying user emails which sends the user an email with a link that they must click before being able to log in. And you can use Directus' powerful filtering system to create rules around which emails can be used. And of course, like everything in Directus, you can access this new feature via our new API endpoints for registering and verifying users. This is a super useful addition, Removing hacky workarounds that many of you had to use until now. This also means that in use cases where your end users interact with Directus via the Data Studio, Directus can effectively function as your complete application. Behavior change for empty search results. This is both a fix and a potential breaking change. But if you use the search parameter and there are no results, we will now return no results. Before, we would return all items. And this is, of course, important if you're using the search parameter in your applications. Community Contributions. Thank you to all of our community members who contributed towards this release. Thank you for Florian for making sure that v list items are correctly displayed with thumbnails and for a small CSS variable fix in the search input. Thanks to Evout for updating the send method in mail service to better handle asynchronous calls. Thank you to Mingfei for also an earlier fix that we made to the Edit Data operation to the Create Data and Update operations as well. Thank you to Gerard for adding the on update constraints to the relation service and to Vasmi for adding the auto complete attribute to the 2 factor authentication field. Thank you to Niko for adding support in configuring p m 2 error log storage and Yonas for fixing an issue with the WYSIWYG interface that could cause some new links to behave unexpectedly. Finally, thanks to Miguel for adding a GraphQL singleton helper type for the SDK. And that's directives 10.11. Thank you to everyone involved in putting it together. It's now available on NPM and Docker Hub and we'll see you in the next release.",[3256],"d4cffa4e-f892-4bb0-84e0-3763693914e1",[],{"year":2380,"number":588,"id":2381,"episodes":3259,"show":3260},[2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394,2366],{"title":2396,"slug":2397},{"id":3262,"slug":3263,"vimeo_id":3264,"description":3265,"tile":3266,"length":740,"resources":12,"people":3267,"episode_number":213,"published":3269,"title":3270,"video_transcript_html":3271,"video_transcript_text":3272,"content":12,"seo":12,"status":19,"episode_people":3273,"recommendations":3275,"season":3276},"d4c02199-f747-4349-bc56-d4a277ae50fd","kevin","925113229","Kevin runs Developer Relations at Directus, and runs Directus TV, and here's the software, hardware, and analog tools he uses in his day-to-day.","c137a7f4-812b-4ef1-adcf-02da048481ed",[3268],{"name":2820,"url":2821},"2024-05-07","What's In Your Dock, Kevin?","\u003Cp>Speaker 0: Hi, I'm Kevin. I run the developer relations team here at Directus and this is what's in my doc. Firstly, my devices, I'm pretty basic here, I use a 14 inch 2021 MacBook Pro with the M1 Pro Chip and 16 gigs of RAM, and I use an iPhone 14 Pro with 256 gigs of storage because a 128 never seem to be enough for the 2 years that I tend to keep phones. My day to day involves a lot of context switching between content authoring and review, writing documents, meetings, and software development. And to kind of handle that context switching, I use Arc Browser.\u003C\u002Fp>\u003Cp>There's a good chance that you've heard of this browser by now. It's available on Mac and Windows, but they have a really unique, concept of spaces, which does exist in other browsers, but not quite as well as it works in Arc. So I can set up kind of sections of my browser for different types of tasks that I do or different profiles that I need to keep. And they also have this thing called tab cleanup. So every, you know, every 7 days is what I've got it configured to.\u003C\u002Fp>\u003Cp>Anything in a particular section screenshots and small video. I find their annotation tool just really, really good, and they also, immediately save screenshots to your clipboard, which is useful, and they keep a history of things that you've captured for a little while. They also have this nice feature where you can take a screenshot and then pin it above everything else. So if I just need a little bit of reference while I go do something else, I'll often screenshot something, pin it to my monitor, and then I can move around and it just kinda stays put there. So really, really useful.\u003C\u002Fp>\u003Cp>Rewind is a tool that I use to enhance my memory for things that I've seen or said on my machine. It basically watches everything you do, compresses what you do as screenshots really small, keeps them on device, and then at any time, I can pull up this little interface something I may have said in a meeting because it provides transcriptions or something I've seen in a document somewhere, and it will show you when and where you saw it and which application. And then applications it's aware of, you can do, like, a one click open, which is really good. I'm sure we've all had that thing where we go, where was that? Was it a Google Doc?\u003C\u002Fp>\u003Cp>Was it a Notion? Was it inside of Directus? Was it in a Google Sheet? Who knows? But I know what was said.\u003C\u002Fp>\u003Cp>I know the words that were in there, so really useful. I love Rewind. I have it has just enhanced my memory so so much. I'm a big fan of cloud development environments because I have to context switch so much. It's good to have fresh code environment when I'm working on specific issues or reviewing specific pull requests.\u003C\u002Fp>\u003Cp>So for that, I use GitHub Codespaces. I previously used Gitpod, but the really tight integration with the GitHub workflow that Codespaces provides, caused me to to move over there. So from an issue, I can immediately create a new code environment. It installs all my dependencies, and I can get straight to work. It's really, really good for reviewing other people's work as well.\u003C\u002Fp>\u003Cp>For doing basic recording and editing of my screen, I use ScreenFlow. It's a macOS video editor. I just find that it works really nicely. It doesn't have the most features, but it has basically everything I need day to day. Some really innovative, like, keyboard shortcuts or rather, I should say, intuitive keyboard shortcuts, and it just allows me to get moving really quickly.\u003C\u002Fp>\u003Cp>Now if I'm recording with multiple people, I use Riverside, which is a browser based studio where you can invite multiple people in. It will do a really nice thing where it will stream to participants a lower quality version of the video than perhaps it needs to, but it will record a full quality version and then stream upload that to the Riverside like storage. So you can always know that you have the highest quality version of someone's recording that you possibly can. We use this extensively to create Directus TV series. For personal task management software, I use Todoist.\u003C\u002Fp>\u003Cp>I've tried every one of these tools under the under the sun, and I always seem to fall back to using Todoist. I really like the fact that you can use little shortcuts like the hash key to tag something with a project or an at key to do a label and so on and so forth. It does intelligent date recognition, so it automatically tags things with due date. I have app. They also have good browser integrations and general integrations with other tools that I use.\u003C\u002Fp>\u003Cp>A little utility tool that I use is called Hyperkey. Hyperkey just constantly runs in the background of my Mac, and it remaps my command, alt, control, and shift keys to my caps lock key. And then I no longer have a caps lock key, but what I now have is a hyperkey, so I can hold that and I can now have new keyboard shortcuts using the caps lock key. So I'll do things like, you know, caps t to bring up a translator or caps e to bring up an emoji picker and so on. And there's a really, really high chance that the hyper plus character key doesn't conflict with anything that already exists.\u003C\u002Fp>\u003Cp>So it gives me just a complete new layer of keyboard shortcuts to create. For day to day project management at work, we use GitHub, actually. We do everything in the open. So I mostly use a road map view, which will show here while I'm talking. And this shows every kind of inbound issue that someone has created, whether that's me, whether that's one of the core directors team or a community member.\u003C\u002Fp>\u003Cp>We learn a bit more about it to understand the scope of work. We then put it in the backlog when it's ready to be worked on. We put it in ready, and then we kind of track the life cycle of an issue accordingly. So we do all of this. It's in the open, and it's not bad at all.\u003C\u002Fp>\u003Cp>It lacks some depth of other project management tools, but the fact that our code and our issues and our road map and our workflow are just completely collocated is great. And then throw on top of that the fact that I use Git upgrade spaces, and it just works really nicely. If there is one tool I could not live without, it's Raycast. It is the ultimate productivity tool that kind of gobbles up so many other utilities I once had. At its core, it's a launcher.\u003C\u002Fp>\u003Cp>So the same way that you may have spotlight search where you hit command space and it brings up a little search bar, I've replaced that search bar with Raycast. It works very similarly. I can launch applications. I can search files, but it also does things like window management, clipboard history, snippets, camera preview. They have a really good store, which is vibrant and new, you know, new extensions are always being published on the store.\u003C\u002Fp>\u003Cp>It's just really, really good. You can create your own extensions with JavaScript. You can just run bash scripts if you want, and I just use it all the time. It's probably the first thing I install on a new machine when I have a fresh OS, so that's Raycast. I would say it depends on the kind of work I'm doing.\u003C\u002Fp>\u003Cp>Sometimes I can put on music with words, sometimes I can't. Like most other episodes, I anticipate lofi hip hop is, you know, has has been mentioned before. I listen to lofi hip hop without words. When I'm doing work where words aren't too distracting, it can be anything, musicals, metal, country, pop music, electronic music. Because my German comprehension isn't quite good enough that I find the words distracting.\u003C\u002Fp>\u003Cp>But, yeah, really, it's eclectic. And just depending on my mood and the amount of focus I need to have sometimes with or without words, that's really the only differentiator. The genre is pretty, pretty eclectic. I would say the only interesting piece of hardware I have is probably my monitor. I use the Samsung Odyssey Ultra Wide 49 inch, so it's a huge widescreen monitor.\u003C\u002Fp>\u003Cp>And then using Raycast, I have keyboard shortcuts, so I can put applications in thirds, I can put applications in quarters. I can put applications in half. And then for things like video editing, I can obviously full screen an application using keyboard shortcuts. Typically, I'll have 3 things open at a time, a code editor in the middle, reference on the left, and a preview on the right. But, yeah, it depends on the task.\u003C\u002Fp>\u003Cp>This also is kind of weird. It's a it's a curved monitor. And so when I'm using max spaces and I'm, like, swiping to move between them, it's like the entire pane shifts around, and I'm surrounded by all of these spaces that I can't see. It's kind of it's kind of weird to explain, but, yeah, I think my monitor is probably my only interesting hardware. Right here, just out of view, I've got the Elgato Wave 1 Mic in the Elgato Low Profile Arm, which is an amazing mic arm.\u003C\u002Fp>\u003Cp>I'm really really enjoying it. It's very very solid. For my camera, I'm actually using my last phone, which was an iPhone 12 Mini mounted and permanently plugged in for power, and that is a continuity camera. So I use the, you know, the built in, integration with macOS and iOS to use that. I have a couple of key lights up here which are on that light me properly, which I actually forgot to put on for this recording, so this isn't the this isn't it in its full glory.\u003C\u002Fp>\u003Cp>I've got some home kit enabled lights behind me, and then for my keyboard, I use a NuPhy s 60. And instead of a mouse, I use a magic trackpad, so I can still use all of the macOS gestures I'm used to. So I use Todoist, as I mentioned, to kind of project manage things that only relate to me. But often, you need just a little kind of micro to do list, just for something you're doing in the moment. And for that, I use these Titulo cards.\u003C\u002Fp>\u003Cp>They say Titulo up there in the right, and they're these really nice high stock cards, that just have a little column in the side for you to, you know, provide an icon, tick them off. Say, I've moved this to tomorrow and so on. They're double sided, and I keep one of these in my desk, visible kind of stood up all the time, in case I just need a temporary kind of physical to do list instead of messing around with software tools. But other than that, I really don't do that much analog. I am on my phone constantly.\u003C\u002Fp>\u003Cp>So as long as things sync well between my machine and my phone, I don't really feel the need to keep much analog. And that's all the software, hardware, and analog tools I use to run my life. I hope you found this interesting. I hope you found this series, what's in your doc, interesting. I hope you enjoy all this other content we have available for you on directors TV, and until the next show, see you next time.\u003C\u002Fp>","Hi, I'm Kevin. I run the developer relations team here at Directus and this is what's in my doc. Firstly, my devices, I'm pretty basic here, I use a 14 inch 2021 MacBook Pro with the M1 Pro Chip and 16 gigs of RAM, and I use an iPhone 14 Pro with 256 gigs of storage because a 128 never seem to be enough for the 2 years that I tend to keep phones. My day to day involves a lot of context switching between content authoring and review, writing documents, meetings, and software development. And to kind of handle that context switching, I use Arc Browser. There's a good chance that you've heard of this browser by now. It's available on Mac and Windows, but they have a really unique, concept of spaces, which does exist in other browsers, but not quite as well as it works in Arc. So I can set up kind of sections of my browser for different types of tasks that I do or different profiles that I need to keep. And they also have this thing called tab cleanup. So every, you know, every 7 days is what I've got it configured to. Anything in a particular section screenshots and small video. I find their annotation tool just really, really good, and they also, immediately save screenshots to your clipboard, which is useful, and they keep a history of things that you've captured for a little while. They also have this nice feature where you can take a screenshot and then pin it above everything else. So if I just need a little bit of reference while I go do something else, I'll often screenshot something, pin it to my monitor, and then I can move around and it just kinda stays put there. So really, really useful. Rewind is a tool that I use to enhance my memory for things that I've seen or said on my machine. It basically watches everything you do, compresses what you do as screenshots really small, keeps them on device, and then at any time, I can pull up this little interface something I may have said in a meeting because it provides transcriptions or something I've seen in a document somewhere, and it will show you when and where you saw it and which application. And then applications it's aware of, you can do, like, a one click open, which is really good. I'm sure we've all had that thing where we go, where was that? Was it a Google Doc? Was it a Notion? Was it inside of Directus? Was it in a Google Sheet? Who knows? But I know what was said. I know the words that were in there, so really useful. I love Rewind. I have it has just enhanced my memory so so much. I'm a big fan of cloud development environments because I have to context switch so much. It's good to have fresh code environment when I'm working on specific issues or reviewing specific pull requests. So for that, I use GitHub Codespaces. I previously used Gitpod, but the really tight integration with the GitHub workflow that Codespaces provides, caused me to to move over there. So from an issue, I can immediately create a new code environment. It installs all my dependencies, and I can get straight to work. It's really, really good for reviewing other people's work as well. For doing basic recording and editing of my screen, I use ScreenFlow. It's a macOS video editor. I just find that it works really nicely. It doesn't have the most features, but it has basically everything I need day to day. Some really innovative, like, keyboard shortcuts or rather, I should say, intuitive keyboard shortcuts, and it just allows me to get moving really quickly. Now if I'm recording with multiple people, I use Riverside, which is a browser based studio where you can invite multiple people in. It will do a really nice thing where it will stream to participants a lower quality version of the video than perhaps it needs to, but it will record a full quality version and then stream upload that to the Riverside like storage. So you can always know that you have the highest quality version of someone's recording that you possibly can. We use this extensively to create Directus TV series. For personal task management software, I use Todoist. I've tried every one of these tools under the under the sun, and I always seem to fall back to using Todoist. I really like the fact that you can use little shortcuts like the hash key to tag something with a project or an at key to do a label and so on and so forth. It does intelligent date recognition, so it automatically tags things with due date. I have app. They also have good browser integrations and general integrations with other tools that I use. A little utility tool that I use is called Hyperkey. Hyperkey just constantly runs in the background of my Mac, and it remaps my command, alt, control, and shift keys to my caps lock key. And then I no longer have a caps lock key, but what I now have is a hyperkey, so I can hold that and I can now have new keyboard shortcuts using the caps lock key. So I'll do things like, you know, caps t to bring up a translator or caps e to bring up an emoji picker and so on. And there's a really, really high chance that the hyper plus character key doesn't conflict with anything that already exists. So it gives me just a complete new layer of keyboard shortcuts to create. For day to day project management at work, we use GitHub, actually. We do everything in the open. So I mostly use a road map view, which will show here while I'm talking. And this shows every kind of inbound issue that someone has created, whether that's me, whether that's one of the core directors team or a community member. We learn a bit more about it to understand the scope of work. We then put it in the backlog when it's ready to be worked on. We put it in ready, and then we kind of track the life cycle of an issue accordingly. So we do all of this. It's in the open, and it's not bad at all. It lacks some depth of other project management tools, but the fact that our code and our issues and our road map and our workflow are just completely collocated is great. And then throw on top of that the fact that I use Git upgrade spaces, and it just works really nicely. If there is one tool I could not live without, it's Raycast. It is the ultimate productivity tool that kind of gobbles up so many other utilities I once had. At its core, it's a launcher. So the same way that you may have spotlight search where you hit command space and it brings up a little search bar, I've replaced that search bar with Raycast. It works very similarly. I can launch applications. I can search files, but it also does things like window management, clipboard history, snippets, camera preview. They have a really good store, which is vibrant and new, you know, new extensions are always being published on the store. It's just really, really good. You can create your own extensions with JavaScript. You can just run bash scripts if you want, and I just use it all the time. It's probably the first thing I install on a new machine when I have a fresh OS, so that's Raycast. I would say it depends on the kind of work I'm doing. Sometimes I can put on music with words, sometimes I can't. Like most other episodes, I anticipate lofi hip hop is, you know, has has been mentioned before. I listen to lofi hip hop without words. When I'm doing work where words aren't too distracting, it can be anything, musicals, metal, country, pop music, electronic music. Because my German comprehension isn't quite good enough that I find the words distracting. But, yeah, really, it's eclectic. And just depending on my mood and the amount of focus I need to have sometimes with or without words, that's really the only differentiator. The genre is pretty, pretty eclectic. I would say the only interesting piece of hardware I have is probably my monitor. I use the Samsung Odyssey Ultra Wide 49 inch, so it's a huge widescreen monitor. And then using Raycast, I have keyboard shortcuts, so I can put applications in thirds, I can put applications in quarters. I can put applications in half. And then for things like video editing, I can obviously full screen an application using keyboard shortcuts. Typically, I'll have 3 things open at a time, a code editor in the middle, reference on the left, and a preview on the right. But, yeah, it depends on the task. This also is kind of weird. It's a it's a curved monitor. And so when I'm using max spaces and I'm, like, swiping to move between them, it's like the entire pane shifts around, and I'm surrounded by all of these spaces that I can't see. It's kind of it's kind of weird to explain, but, yeah, I think my monitor is probably my only interesting hardware. Right here, just out of view, I've got the Elgato Wave 1 Mic in the Elgato Low Profile Arm, which is an amazing mic arm. I'm really really enjoying it. It's very very solid. For my camera, I'm actually using my last phone, which was an iPhone 12 Mini mounted and permanently plugged in for power, and that is a continuity camera. So I use the, you know, the built in, integration with macOS and iOS to use that. I have a couple of key lights up here which are on that light me properly, which I actually forgot to put on for this recording, so this isn't the this isn't it in its full glory. I've got some home kit enabled lights behind me, and then for my keyboard, I use a NuPhy s 60. And instead of a mouse, I use a magic trackpad, so I can still use all of the macOS gestures I'm used to. So I use Todoist, as I mentioned, to kind of project manage things that only relate to me. But often, you need just a little kind of micro to do list, just for something you're doing in the moment. And for that, I use these Titulo cards. They say Titulo up there in the right, and they're these really nice high stock cards, that just have a little column in the side for you to, you know, provide an icon, tick them off. Say, I've moved this to tomorrow and so on. They're double sided, and I keep one of these in my desk, visible kind of stood up all the time, in case I just need a temporary kind of physical to do list instead of messing around with software tools. But other than that, I really don't do that much analog. I am on my phone constantly. So as long as things sync well between my machine and my phone, I don't really feel the need to keep much analog. And that's all the software, hardware, and analog tools I use to run my life. I hope you found this interesting. I hope you found this series, what's in your doc, interesting. I hope you enjoy all this other content we have available for you on directors TV, and until the next show, see you next time.",[3274],"86cb586f-23d3-457b-aa3f-fae0eca78bd6",[],{"year":978,"number":3277,"id":3278,"episodes":3279,"show":3284},31,"9e8684b4-074a-4048-8e0d-643433c81c21",[3280,3281,3282,3283,3262],"5ae26e77-6584-426a-a247-fd1662e268bd","1c7a9c6b-a089-4919-a4c4-b6aef92c7652","02770b60-b4da-447c-9e9d-c59f5b63bab0","38e774bc-6f05-4dda-8b31-9e228f90d59d",{"title":1027,"slug":1028},{"id":2754,"slug":3286,"vimeo_id":3287,"description":3288,"tile":3289,"length":527,"resources":12,"people":12,"episode_number":129,"published":3290,"title":3291,"video_transcript_html":3292,"video_transcript_text":3293,"content":12,"seo":12,"status":19,"episode_people":3294,"recommendations":3296,"season":3297},"friends-theme","931466342","Step into Bry's studio for a splash of '90s nostalgia as he brews a Central Perk theme, rich with the essence of our favorite TV coffee shop.","07645337-6064-4baf-99de-a3dbece7c73a","2024-05-06","Central Perk (Coffee is Better with Friends)","\u003Cp>Speaker 0: Hi, friends. Welcome back to the show. I'm your host, Bry Ross. And today, we're gonna take an extra special trip back to the nineties with you, our friends and friends. Alright.\u003C\u002Fp>\u003Cp>So we're gonna be painting a beautiful Central Perk Coffee Company theme. Alright. So let's dive into our theme. 1st, gonna pull up our project settings and we're going to adjust those. Just a quick little project name and description.\u003C\u002Fp>\u003Cp>Your friend's coffee shop. What could be more beautiful for a project descriptor? Alright. So we're gonna paste the URL in there, and we can see that project settings, our name, and descriptor up there at the top. I like to pronounce it descriptor.\u003C\u002Fp>\u003Cp>Makes it sound like Thor. Very powerful. But the effect that themes can have are are very powerful, especially on your users. Alright. So let's pull up our color, and we're just gonna for our project color, we're gonna grab that green from the Central Perk logo.\u003C\u002Fp>\u003Cp>Now that's a good project color. Next, let's add a public foreground and public background. So we'll just look for Central Perk and see if we can just grab a good looking image from from all of our friends here. We're just gonna spread that across the entire background And for the public foreground, we'll just use the same logo. I think that should still look really nice on the login page, don't you?\u003C\u002Fp>\u003Cp>Yeah. Great. Now all these techniques that you see me using, you can use these yourself. There's nothing holding you back. You've got the tools.\u003C\u002Fp>\u003Cp>Just take the first step. Alright. So we're gonna paint in that first note, that public note. We've got our favicon and now we're ready to start theming. Alright.\u003C\u002Fp>\u003Cp>So I really like this walnut light, walnut brown, this almond beige color. Let's use that for our modules background. So we'll just paint that in there at the top. One thing I do see though, I want I want this to match all the way up and down, so I'm just going to paint a couple strokes there. We're just going to look for our background color.\u003C\u002Fp>\u003Cp>I see that's the project color. We need to fix that, so we're going to go into our custom CSS and just add a little dot module dash bar dash logo class. Make that background transparent. Now you see it. Now you don't.\u003C\u002Fp>\u003Cp>Boom. Looks great. Alright. So now for our icons in that module bar, what I wanna do, I wanna be heavy handed. I'm still gonna use light strokes for this, but I am going to try and pick up a, like, a darker brown.\u003C\u002Fp>\u003Cp>I I'm almost thinking they've got one here on the website, but, I I just don't see it. Right. Let's just try a black and see how that works. So we'll just go paint in a black. It's too much contrast.\u003C\u002Fp>\u003Cp>It doesn't give me those warm feelings of a coffee shop that I'm looking for. So let's pull this brown from this wood image. Now one of the other tools that I use, Color Slurp, Terrible name. Great product though. Being able to just copy those hex codes for for everything you see on your screen.\u003C\u002Fp>\u003Cp>Great tool. Stores all those hex codes as well and then you can quickly bang those in your keyboard. Alright. So next, what we wanna do is we wanna control our list, our sidebar here. So let's add a background to this.\u003C\u002Fp>\u003Cp>And for this, I'm gonna use a little tool that I like. We're going to look for the Essential Perk couch color because if you remember inside this coffee shop there's a big couch that just takes up all the area. So we're gonna lift this orange couch and make it our background. Is it weird? Maybe a little bit, but for you folks who didn't live through the 90s, they were a weird time as well.\u003C\u002Fp>\u003Cp>So let's let's lean into it and and embrace the journey, see where this theme takes us. A little unorthodox mixing colors like this. But with your tools at your side, enjoy in your heart. You can make any theme work. That looks good.\u003C\u002Fp>\u003Cp>So now we've got a nice separation there. Let's resolve the icons here. So again, having those set to the project color of green might not be the best idea. I'm just going to take that detail brush, paint in some foreground, just a white foreground. Let's go in and let's try and make this look like a cloth texture as much as we can.\u003C\u002Fp>\u003Cp>So I'm just going to use a little pattern monster here. We're going to find a pattern. Just gonna keep keep looking through our patterns until we find something that we like. Just just browse those patterns. Do we find a pattern that we like?\u003C\u002Fp>\u003Cp>This adjoined diamond pattern reminds me of my grandmother's couch and her kitchen floor at the same time. Amazing. Alright. So let's use that same orange base color and we're just going to dial back the opacity here. And then also on the other overlay, Let's just switch that from white to black and again we're gonna dial in that opacity.\u003C\u002Fp>\u003Cp>Now I think this is a subtle effect that kind of gives a texture there. Alright. So we're just gonna paint the rest of that in. And, okay. We'll dial back that opacity a little bit just to give this a little more subtle effect.\u003C\u002Fp>\u003Cp>And now I'm going to pull out that palette knife and we're just going to paste this in. We're gonna pull up that CSS. We're gonna copy the URL portion. So we've got our data URI. We're just gonna paste that into the background and just bring that all the way down.\u003C\u002Fp>\u003Cp>Alright. So now we've got this orange symbolic couch in the middle of our theme. Alright. So if we go into our background, we want to add a subtle background, that stark contrast between the white and the orange. There's opportunity there.\u003C\u002Fp>\u003Cp>So we're just gonna dial that in a little bit. Again, we're gonna use our color mix. Just paint that in. We're gonna blend that almond with 80% white. And we get this soft beige.\u003C\u002Fp>\u003Cp>Just invites you in like a warm cup of coffee in the morning. Now the last thing that we wanna do is dial in our fonts. We're just gonna pull this up. We've got a custom font here that looks like a scribble. I quite like this effect.\u003C\u002Fp>\u003Cp>So we're just gonna go in to our font face, add that there, and then we're just gonna paint this on. Paint in a little family. We're going to call it friends. We're going to paint call it friends. Yeah.\u003C\u002Fp>\u003Cp>That's nice. Alright. Use a light touch. Very important. You wanna have a handy hand, heavy hand in this.\u003C\u002Fp>\u003Cp>All right. So then we're going to go into our fonts under display, and we're just going to go friends. Just going to add friends in there. Now we can see that at the top and just adjust it a little bit. There we go.\u003C\u002Fp>\u003Cp>And now we have our coffee shop theme. We have our friends and the couch in the middle and everything is right. Just like it was in the 1990s. All right. Let's take a look at our public sign in page and while we have our central perk theme.\u003C\u002Fp>\u003Cp>That's it for this episode of the joy of theming. Thank you for joining me. It's through your support that we can continue teaching techniques to help everyone theme. Each of us has a perfect theme inside of us. It's time to let yours out.\u003C\u002Fp>\u003Cp>We'll see you.\u003C\u002Fp>","Hi, friends. Welcome back to the show. I'm your host, Bry Ross. And today, we're gonna take an extra special trip back to the nineties with you, our friends and friends. Alright. So we're gonna be painting a beautiful Central Perk Coffee Company theme. Alright. So let's dive into our theme. 1st, gonna pull up our project settings and we're going to adjust those. Just a quick little project name and description. Your friend's coffee shop. What could be more beautiful for a project descriptor? Alright. So we're gonna paste the URL in there, and we can see that project settings, our name, and descriptor up there at the top. I like to pronounce it descriptor. Makes it sound like Thor. Very powerful. But the effect that themes can have are are very powerful, especially on your users. Alright. So let's pull up our color, and we're just gonna for our project color, we're gonna grab that green from the Central Perk logo. Now that's a good project color. Next, let's add a public foreground and public background. So we'll just look for Central Perk and see if we can just grab a good looking image from from all of our friends here. We're just gonna spread that across the entire background And for the public foreground, we'll just use the same logo. I think that should still look really nice on the login page, don't you? Yeah. Great. Now all these techniques that you see me using, you can use these yourself. There's nothing holding you back. You've got the tools. Just take the first step. Alright. So we're gonna paint in that first note, that public note. We've got our favicon and now we're ready to start theming. Alright. So I really like this walnut light, walnut brown, this almond beige color. Let's use that for our modules background. So we'll just paint that in there at the top. One thing I do see though, I want I want this to match all the way up and down, so I'm just going to paint a couple strokes there. We're just going to look for our background color. I see that's the project color. We need to fix that, so we're going to go into our custom CSS and just add a little dot module dash bar dash logo class. Make that background transparent. Now you see it. Now you don't. Boom. Looks great. Alright. So now for our icons in that module bar, what I wanna do, I wanna be heavy handed. I'm still gonna use light strokes for this, but I am going to try and pick up a, like, a darker brown. I I'm almost thinking they've got one here on the website, but, I I just don't see it. Right. Let's just try a black and see how that works. So we'll just go paint in a black. It's too much contrast. It doesn't give me those warm feelings of a coffee shop that I'm looking for. So let's pull this brown from this wood image. Now one of the other tools that I use, Color Slurp, Terrible name. Great product though. Being able to just copy those hex codes for for everything you see on your screen. Great tool. Stores all those hex codes as well and then you can quickly bang those in your keyboard. Alright. So next, what we wanna do is we wanna control our list, our sidebar here. So let's add a background to this. And for this, I'm gonna use a little tool that I like. We're going to look for the Essential Perk couch color because if you remember inside this coffee shop there's a big couch that just takes up all the area. So we're gonna lift this orange couch and make it our background. Is it weird? Maybe a little bit, but for you folks who didn't live through the 90s, they were a weird time as well. So let's let's lean into it and and embrace the journey, see where this theme takes us. A little unorthodox mixing colors like this. But with your tools at your side, enjoy in your heart. You can make any theme work. That looks good. So now we've got a nice separation there. Let's resolve the icons here. So again, having those set to the project color of green might not be the best idea. I'm just going to take that detail brush, paint in some foreground, just a white foreground. Let's go in and let's try and make this look like a cloth texture as much as we can. So I'm just going to use a little pattern monster here. We're going to find a pattern. Just gonna keep keep looking through our patterns until we find something that we like. Just just browse those patterns. Do we find a pattern that we like? This adjoined diamond pattern reminds me of my grandmother's couch and her kitchen floor at the same time. Amazing. Alright. So let's use that same orange base color and we're just going to dial back the opacity here. And then also on the other overlay, Let's just switch that from white to black and again we're gonna dial in that opacity. Now I think this is a subtle effect that kind of gives a texture there. Alright. So we're just gonna paint the rest of that in. And, okay. We'll dial back that opacity a little bit just to give this a little more subtle effect. And now I'm going to pull out that palette knife and we're just going to paste this in. We're gonna pull up that CSS. We're gonna copy the URL portion. So we've got our data URI. We're just gonna paste that into the background and just bring that all the way down. Alright. So now we've got this orange symbolic couch in the middle of our theme. Alright. So if we go into our background, we want to add a subtle background, that stark contrast between the white and the orange. There's opportunity there. So we're just gonna dial that in a little bit. Again, we're gonna use our color mix. Just paint that in. We're gonna blend that almond with 80% white. And we get this soft beige. Just invites you in like a warm cup of coffee in the morning. Now the last thing that we wanna do is dial in our fonts. We're just gonna pull this up. We've got a custom font here that looks like a scribble. I quite like this effect. So we're just gonna go in to our font face, add that there, and then we're just gonna paint this on. Paint in a little family. We're going to call it friends. We're going to paint call it friends. Yeah. That's nice. Alright. Use a light touch. Very important. You wanna have a handy hand, heavy hand in this. All right. So then we're going to go into our fonts under display, and we're just going to go friends. Just going to add friends in there. Now we can see that at the top and just adjust it a little bit. There we go. And now we have our coffee shop theme. We have our friends and the couch in the middle and everything is right. Just like it was in the 1990s. All right. Let's take a look at our public sign in page and while we have our central perk theme. That's it for this episode of the joy of theming. Thank you for joining me. It's through your support that we can continue teaching techniques to help everyone theme. Each of us has a perfect theme inside of us. It's time to let yours out. We'll see you.",[3295],"9ecd99df-4e5f-47a3-be7c-15e6247fd8de",[],{"year":978,"number":2745,"id":2746,"episodes":3298,"show":3299},[2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2731],{"title":2760,"slug":2761},{"id":2315,"slug":3301,"vimeo_id":3302,"description":3303,"tile":3304,"length":2767,"resources":12,"people":12,"episode_number":264,"published":3305,"title":3306,"video_transcript_html":3307,"video_transcript_text":3308,"content":12,"seo":3309,"status":19,"episode_people":3310,"recommendations":3312,"season":3313},"virtual-events-platform","936342561","In this 60 min sprint, Bryant builds a virtual events platform that's similar to webinar services like Zoom, Livestorm, and others. Can he build all the functionality like event scheduling, event registration, separate break out rooms, and an integration to Whereby?","d3bfe6e0-cfaf-409d-bf02-010b8c0aa6aa","2024-05-03","Mission: Virtual Events Platform","\u003Cp>Speaker 0: Hi.\u003C\u002Fp>\u003Cp>Speaker 1: Welcome back to another episode of 100 apps. 100 hours where we try to build your suggestions or some of your favorite apps in 1 hour or less using Directus or publicly fail, die on the vine trying to build something. If you're new to the show, there are only 2 rules. The first rule is we have 60 minutes to plan and build, no more, no less. The second rule is the anti rule.\u003C\u002Fp>\u003Cp>Use whatever you have at your disposal. So then the brakes. Let's dive into today's episode. Right? We're gonna be building an events platform, a virtual events platform.\u003C\u002Fp>\u003Cp>Now that sounds to me very very vague, could mean a lot of things to a lot of different folks. But, what kind of products are we talking about? So Zoom comes to mind as far as, like, registering a meeting and performing that meeting. You know, maybe you've got some interactivity like a chat, some registration. You know, you've seen this beautiful, beautiful, beautiful registration page before, I'm I'm sure.\u003C\u002Fp>\u003Cp>Not knocking you Zoom, but, like, with all the engineers that you guys have, why is this the best that we get for a registration page? I don't like it. Other softwares that I've looked at, like, DemoHop is an interesting one that, you know, has, like, a science fair where each there's a bunch of different booths, some of the other ones, like, Livestorm. There's a ton of these different webinar platforms every way that you slice it. But what I've discovered and and just in my own experience with these over the years, it's nice to have your own.\u003C\u002Fp>\u003Cp>So that's what we're gonna be building today. We're gonna be building our own events platform. Let's run through it. We'll start the clock, pull up 60 minutes, and away we go. Okay.\u003C\u002Fp>\u003Cp>So, I am in FigJam here. Let's just kind of mock out our functionality that we like or what we're looking to see out of this particular app. So, as far as our functionality, this is a nice serif font. Maybe we underline that as well. Great.\u003C\u002Fp>\u003Cp>Alright. So what do we wanna do? We wanted to set up we can't have all of it underlined. That's not cool. Set up an event.\u003C\u002Fp>\u003Cp>You know, allow registrations. You know, have a public landing page, registration page. What else do we want? We wanna have chat during the event. Maybe we wanna have different rooms for the event.\u003C\u002Fp>\u003Cp>You know? You think a virtual event, it could just be just, hey. It's a workshop. It's a webinar.\u003C\u002Fp>\u003Cp>Speaker 0: Could also be\u003C\u002Fp>\u003Cp>Speaker 1: like a week long leap week like we do here at Directus. So maybe we have rooms for that. This this seems like I'm getting way in over my head, so we'll stop there. And then let's let's kinda just flesh out our data model for this a bit. So in my mind, what are we gonna have?\u003C\u002Fp>\u003Cp>We're gonna have events. K. And we got that nice purple color. Maybe we'll make this a little bit bigger so we could see it. So we've got events and then we're gonna have, users or attendees.\u003C\u002Fp>\u003Cp>You know, that's gonna be our Directus underscore users collection. We'll just pick that up. Directus gives us that out of the box. No reason not to use it. Amazing.\u003C\u002Fp>\u003Cp>And then I would imagine there's a junction table here, right, where we have could have multiple events. So these will be, like, registrations, maybe. Naming stuff is always the hardest part of development. So we got registrations, draw some arrows, just connect these, connect these in your mind as well. Great.\u003C\u002Fp>\u003Cp>And then what else? You know, if we jive with the rooms, you know, we could have rooms as well. Right? Each room has a different topic, some schedule, etcetera. If we think of, like, our events, they're gonna have a date, start date, duration, or maybe an end date,\u003C\u002Fp>\u003Cp>Speaker 0: title, description.\u003C\u002Fp>\u003Cp>Speaker 1: What else do we have? Registrations, obviously. Probably an image. Schedule, potentially. Cool.\u003C\u002Fp>\u003Cp>Alright. Getting too carried away. Anyway, let's actually start building something. Right? This feels just roughly what we're looking for.\u003C\u002Fp>\u003Cp>So let's dive into it. Right? What have I got set up for this? Just gonna close all these other things. I have a Directus instance.\u003C\u002Fp>\u003Cp>I've got a Nuxt 3 application, just a starter application set up. It's got a login and register function. I don't even think the register actually works on this thing, but that's what we're working with. Let's go ahead and get logged in to\u003C\u002Fp>\u003Cp>Speaker 0: our Directus instance. What\u003C\u002Fp>\u003Cp>Speaker 1: did I set the password to? I think it's just password. Real secure. Alright. So we've got a blank instance.\u003C\u002Fp>\u003Cp>I've got my FigJam over here on the left. Got Directus on the right, a 125% zoomed in, and let's just dive in. Right? Our first collection, we're going to start here with our events and we'll create a new collection for that. Let's give it a UUID, When it was created, when it was updated, those are all great.\u003C\u002Fp>\u003Cp>Let's do status as well for like published events, draft events, completed events. Let's just take a look at that. Right? So we have published, we have draft, we have archived. I could go in and flesh these out further if I wanted to or we could just leave it at that.\u003C\u002Fp>\u003Cp>Maybe draft is the default. That's good. Solid. Alright. So here, we're gonna have a name for the event or a title.\u003C\u002Fp>\u003Cp>The event title, that's just gonna be a string. A description of this, we want to be the anti zoom here, so we're gonna make this a text field using the WYSIWYG interface for Directus. We're going to call it description. That's our key. And I could adjust our toolbar if I wanted to.\u003C\u002Fp>\u003Cp>Maybe I want to add undo and redo. I'm not a big fan of underline, so I'll just remove that. If I need to, I can dive into advanced field creation mode, and specifically with the WYSIWYG editor, you can you've got some formatting options here and options overrides that you can pass to the tiny MCE instance if you need to. Like, you know, maybe you have a special class for buttons or something like that that you want to display inside that WYSIWYG editor. Alright.\u003C\u002Fp>\u003Cp>Let's add an image for this. So just a single image. You know, we could potentially have, you know, multiple images, but we'll just keep it simple. That's what we're going to show on a card. As I'm imagining this, on the front end, it's It's just like a running list of events or maybe a grid of cards for the events.\u003C\u002Fp>\u003Cp>When you get inside the event, you've got to register. Then once you register, maybe you can see the schedule for the event. Cool. So as far as something like a schedule, right, how should we set this up? If I'm not really sure, like, one of the things that I do a lot, I'm not really sure what this is gonna look like immediately.\u003C\u002Fp>\u003Cp>Instead of creating another table, I may use something like the repeater, which is basically a JSON type field. That's what it's going to save as our, inside our SQL database. But let's just call it schedule, and then I could go within this and edit fields to repeat, like a set of fields. So, I don't know. I like a speaker.\u003C\u002Fp>\u003Cp>It'll probably be something else. Let's just say the name or label. Okay. We'll do string. That requires a value.\u003C\u002Fp>\u003Cp>What's the name of this thing? And the interface would be an input and then we could do something like time, date, time, time. Start time, Start time. Sounds great. That's what we'll roll with.\u003C\u002Fp>\u003Cp>Let's make that full width and then we'll use the timestamp format. We'll require value. Maybe we don't require value. May not need to pick a time. Maybe we're just showing scheduling order.\u003C\u002Fp>\u003Cp>When does this thing start? Cool. Alright. Sweet. So now we have some events or at least the the start of something for events.\u003C\u002Fp>\u003Cp>Let's go in and go back to the data model settings. We're just gonna do registrations and we'll do, like, a date created, user created. Do we even I don't even think we need this. Status. Do we have a status for the registration?\u003C\u002Fp>\u003Cp>Maybe it's canceled or something like that. I'm reading too much into it. Right? So here's the date created. Maybe I wanna show this, so I'll click the drop down, show that field.\u003C\u002Fp>\u003Cp>Maybe we make it full width, just in case. And for our registrations, we're gonna start digging into our relationships. Right? So when I think of a registration, we've got the event that that registration belongs to, and we've got the user that made the registration. So those are both gonna be basically many to one relationships.\u003C\u002Fp>\u003Cp>So we'll go in for the key, we'll add the event, we'll just do events. And for our display template, let's use the title. I can already tell that I forgot to input my start and end date for the event. Amazing. So we'll have to go back and fix that as well, but there's our event.\u003C\u002Fp>\u003Cp>Let's go ahead and create our relationship to the users. So we'll say the user that's registered or, you know, this could be like the attendee. Again, naming stuff is hard. Cut me some slack. Alright.\u003C\u002Fp>\u003Cp>So I want to pick the directus underscore users collection. I'm just going to dig into the systems drop down here, or system, choose direct us users. I can get fancy with the display on this. Give them the avatar with a nice little thumbnail. We'll do first name, last name, and behind the scenes, this is just mustache syntax.\u003C\u002Fp>\u003Cp>So I can edit these raw values if I want, do it that way, or I could just pick from the drop down. That's great. We don't wanna create new users from this. We could potentially select users. Great.\u003C\u002Fp>\u003Cp>Okay. So that's our basic data model. Why do we have event? Some type of bug. Alright.\u003C\u002Fp>\u003Cp>Regardless, we have events, we have registrations. Let's go in and add our start time for this. How could I forget? So in this case, we wanna save the time zone value, so we're gonna use time stamp. This could be the start date.\u003C\u002Fp>\u003Cp>Maybe we have an end date for this or, you know, potentially a duration. I guess you could do either or or both. You know, you could potentially even use flows to calculate that if you wanted to. But this is good. Let's add some icons just to fancy it up a little bit.\u003C\u002Fp>\u003Cp>My friend Kevin on the team here will be sweating if you watch me add these icons, but, there's always room for design. Cool. Alright. So we got events. Let's create our first event.\u003C\u002Fp>\u003Cp>Right? What are we gonna call this? Let's call it Leap Week number 2 1000. Leap Week 2 1000. We are taking a huge leap forward for Directus.\u003C\u002Fp>\u003Cp>Join us as we take a huge leap forward for Directus. Lots of cool stuff. Let's add some formatting to this as well. You know, thing 1, thing 2, thing 3. Cool.\u003C\u002Fp>\u003Cp>Alright. And then let's add an image for this. Right? So let's just search Directus Leap Week and see if we can grab, do we have any images? Yeah, we got a couple laying around.\u003C\u002Fp>\u003Cp>Cool. So we'll save this image. Is that right? Hope that's the right image. We'll just upload that.\u003C\u002Fp>\u003Cp>Everything announced at our 1st Leap Week. And let's set a start date for this. Let's say May 15th is a good date. Maybe this runs 2, 3 days. Yeah.\u003C\u002Fp>\u003Cp>Great. So for our schedule, we've got workshop with Bryant. That is the I forget what day we did this now. 17th. Oh, that's it.\u003C\u002Fp>\u003Cp>May 17th. Not April 17th, Brent. Alright. Let's say that is at 3. Great.\u003C\u002Fp>\u003Cp>Cool. So I'm gonna fill out the schedule workshop with Kevin. Go for 15th at, at 16th at 1 PM. Great. Alright.\u003C\u002Fp>\u003Cp>So now we've got our event. Maybe let's just drag this out a little bit, move this around. Okay. One of the other things that I forgot here is probably like a slug. Right?\u003C\u002Fp>\u003Cp>I want a nice URL on the front end. So we'll go back into events, we'll just create a new field for it, we're going to use the input, call it slug. The other thing that I'm going to do is go in and make this URL safe, so we could slugify this thing. Now one of the other cool things with the addition of the marketplace, let's look for a slug, Permalink interface to enter URL slugs. Is this what we're looking for?\u003C\u002Fp>\u003Cp>No. WP slug interface. Yeah. This is cool. Alright.\u003C\u002Fp>\u003Cp>Let's try this out. We'll hit refresh, install this extension. I didn't have this on the first season of 100 apps, 100 hours, But now I can go into our interface and we can choose the slug interface. And our template is going to come from the title and we'll auto generate on create. Let's try that and see what we've got.\u003C\u002Fp>\u003Cp>We shrink the title and the slug. Cool. Alright. So now we got a couple settings. We can hit auto generate.\u003C\u002Fp>\u003Cp>Generates this nice slug, and it looks a lot like the old days of WordPress. I mean WordPress is still around, obviously, but this is this is nice, I like it. Alright. So we've got events, we've got registrations. What do we do next, right?\u003C\u002Fp>\u003Cp>We're gonna look at access control. So this is our settings of who has access to what inside Directus. There's 2 default roles, public and administrator. Let's make the public roles, we want to be able to view the events, We don't want anybody to edit events. And then, for as far as registrations, maybe we want to be able to create registrations and then maybe just read, you know what?\u003C\u002Fp>\u003Cp>Let's give full access to that. And, that should be maybe what we want, enough to play around with. Right? So now what are we going to do? Right?\u003C\u002Fp>\u003Cp>Let's dive into the front end of this application. Alright. So I'm going to pull this up. Again, I've just got a a Nuxt application here, and let's flesh out a couple of routes. Index dot view.\u003C\u002Fp>\u003Cp>We can make this just a list of our events. Alright. So we're gonna go in. I've got Directus configured as a Nuxt plugin here. It's just got some authentication in it.\u003C\u002Fp>\u003Cp>We've got real time in case we ever needed it. And then we've just got the REST client. That's the way I prefer it. And we just provide that to the Nuxt application. So we'll call that up like this, const directives equals use Nuxt app, and in this case, we're probably gonna do, like, read items as well.\u003C\u002Fp>\u003Cp>So we'll grab that from import items from the atdirectus SDK. And, alright, what's next? Right. So we're gonna do some data. We'll use await, use async data, and we'll do return directus dot request read items.\u003C\u002Fp>\u003Cp>And we're reading from the, what, events collection. And then I have a, query for different parameters. Right? We wanna show maybe we only wanna show published events, right, so I could do this. We'll just say status equals published.\u003C\u002Fp>\u003Cp>Thank you, GitHub Copilot. Let's see what that gets us. Directors. Cool. Maybe we scrap all of this.\u003C\u002Fp>\u003Cp>Would do we even need this to be flexed? Probably not. Alright. Let's see what we got. Data.\u003C\u002Fp>\u003Cp>Just wrap this in like a pre tag. See if we get any actual data. Right. So we've set up this filter. We're not getting any data because I'm assuming we did not publish this event.\u003C\u002Fp>\u003Cp>So if I go in and publish this, let's see what we have now. Okay. Cool. So we have our event information. What can we do with this, right?\u003C\u002Fp>\u003Cp>So I do also have the, like, Nuxt UI library built into this. So if you just go to ui.nuxt, using that, it's got, you know, some components already built in for buttons and cards and things like that. Cool. So without getting too fancy on the UI, let's just mock something up now that we can see we've got some actual data. Right?\u003C\u002Fp>\u003Cp>We got a diff. We add some padding. Y 12p y 6. We add a, do we even need an h one? Let's call it h 2.\u003C\u002Fp>\u003Cp>This will be font bold. Text, let's make the text large. And let's make it mono, font mono. Alright. So will be events.\u003C\u002Fp>\u003Cp>See what we got there. There's our events. And now let's do a grid. Right? So we'll just do a div to wrap this.\u003C\u002Fp>\u003Cp>Just grid, maybe grid calls to like a break point, maybe we set the gap to 8, and then we're gonna do a v 4. Right? So, for our events, let's just use this card. I think there's a card component in here. I feel like a nice card component is ucard.\u003C\u002Fp>\u003Cp>We've got, like, a header and a footer, or we could just throw it in the middle. So let's do that. Right. V 4. We're gonna do events and data.\u003C\u002Fp>\u003Cp>You know? And, usually, I like to destructure those.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Maybe we call it events.\u003C\u002Fp>\u003Cp>Speaker 1: And for the key here, we'll use event ID. That way, we don't have to, use, like, an index or something like that, and then we'll actually do this. Alright. So is there actual I think there's, like, a text component here, so let's just do, like, an h three. Maybe we just take the auto completion here from, GitHub Copilot.\u003C\u002Fp>\u003Cp>We'll do a little Nuxt image to optimize these images. Works very well with Directus. There's a provider for it. Then we've got the event dot image. We use the event title.\u003C\u002Fp>\u003Cp>That seems pretty good. Let's see what we come up with here. Sweet. Except for our description. Right?\u003C\u002Fp>\u003Cp>This kind of sucks because it is not what we want. So in this case, we'll do div. Let's do v h t m l event dot description, and remove that. Great. It has no formatting.\u003C\u002Fp>\u003Cp>Maybe we've got, do I have to tell when? We got pros class. Yeah. There we go. Alright.\u003C\u002Fp>\u003Cp>So we've got that, and let's add a link for this. Right? Maybe we add a little spacing here, n t 4. Maybe we make that add a little spacing here. MT2, MT8.\u003C\u002Fp>\u003Cp>It's great. Cool. So now we've got an event looking really nice. You know, I could duplicate this event if I wanted to. Let's just see what this looks like with a couple of more events.\u003C\u002Fp>\u003Cp>We're going to call this Leap Week 3000. Flush this out. And what I'm going to do here, we're just going to open this image up. I'm going to get really fancy on this, guys. And we'll just mark this up.\u003C\u002Fp>\u003Cp>Scroll this out. 3000. Oh, so fancy. Alright. For Leap Week 3,000, we're going to close this out, upload a new photo, we can see that's Leap Week 3,000.\u003C\u002Fp>\u003Cp>And then what I'm going to do here is click Save and Copy from the 3 dot menu button and now I've got 2 events. We've got Leap Week 2000, Leap Week 3000. There we go. Amazing. Okay.\u003C\u002Fp>\u003Cp>Alright. So now we need a route for our actual event. Right? So let's set this up. Let's actually add a folder for it just in case we have a couple of other routes within that.\u003C\u002Fp>\u003Cp>And then for our events, we're gonna have a slug dot view. So that's just gonna be a dynamic route. We'll view I think it's init. View init. What does that give me?\u003C\u002Fp>\u003Cp>That's not the syntax that I like. It'll do. Trying to make it fancy. I need to get my snippets set back up. I had to scrap Versus code.\u003C\u002Fp>\u003Cp>Lost all my snippets. Alright. So we got a slug. We're gonna use the route here. So we've got route equals use routes.\u003C\u002Fp>\u003Cp>Cool. And then, on the individual page, right, we want to call the specific data. And, you know, maybe we don't even need this full description on our index page, so we could, you know, potentially leave that off. Maybe we just make it the event start date, but we may wanna use that in here. And let's use just, like, a date function.\u003C\u002Fp>\u003Cp>I think it's format from date f n's. Formats\u003C\u002Fp>\u003Cp>Speaker 0: oh,\u003C\u002Fp>\u003Cp>Speaker 1: not currency. I'm gonna do format. What we got? DDD, month month, DD. Where does that get us?\u003C\u002Fp>\u003Cp>Doesn't get you anywhere because it breaks it. Forgot the year year. I guess it's all lowercase. And then okay. Yeah.\u003C\u002Fp>\u003Cp>Still still got a ways to go memory wise. So date f and s, just a handy library. We got format. What's the date? What are we looking for?\u003C\u002Fp>\u003Cp>Like, month is gonna be m m m. Like that one. So we'll do m m m. And what if we do, like, the ordinal of the dates, day of the month,\u003C\u002Fp>\u003Cp>Speaker 0: d o?\u003C\u002Fp>\u003Cp>Speaker 1: That's what we want. Now we're looking nice. Okay. And then we wanna do the time. Right?\u003C\u002Fp>\u003Cp>So maybe we show the time. Local day of the week, AM PM. What's this gonna\u003C\u002Fp>\u003Cp>Speaker 0: be? H. What minute? M\u003C\u002Fp>\u003Cp>Speaker 1: m m. And then we've got AM, PM, AAA. Wasting too much time on this. Okay. Alright.\u003C\u002Fp>\u003Cp>So we got that. Let's go in and just wrap this card in a NuxLink, or at least the content of the card. We'll do 2. And let's make this dynamic. That'll be \u002Fevents\u002F event.slug.\u003C\u002Fp>\u003Cp>Uh-oh. Oh, forgot to close that. Cool. Alright. So now we've got the links set up, so we can click on these and and move to the route.\u003C\u002Fp>\u003Cp>We don't really have anything inside our route though. Right? So I could take this function here, just copy that in, and here we're still going to be reading items. Right? I'm going to call this event, in addition to status.\u003C\u002Fp>\u003Cp>I guess we can set this up where if you call it directly, even if it's a draft, maybe you wanna see it. It. So let's just do this. We'll say slug equals route dot params dot slug. That should get us the event that we want.\u003C\u002Fp>\u003Cp>And then, the actual event here, we're gonna do, like, a transform function. The nice little part that's baked into the use async data call, that composable inside Nuxt, where basically this is gonna return an array. I could transform this and just pick up the first item of this array. And now if we do this, just do pre class. Again, I always like to do this just to see what data I'm getting back.\u003C\u002Fp>\u003Cp>And now we've got just the specific data object for this item. Alright. What's next? Right? We're gonna dive into building this actual page.\u003C\u002Fp>\u003Cp>Right? So let's give it a nice h one tag. We've got font bold, font mono, text let's make this giant. We're gonna do the event dot title. Okay.\u003C\u002Fp>\u003Cp>Leap week 3,000. What else do we have? If I dig into the clipboard, I've got that event description. We can make this pros large. We wrap that.\u003C\u002Fp>\u003Cp>Let's move down. We'll do another div. Throw this into an image, event\u003C\u002Fp>\u003Cp>Speaker 0: dot image,\u003C\u002Fp>\u003Cp>Speaker 1: class w 96. Where's our actual image at? Okay. There's our image. We could flex these 2, add a little bit of padding.\u003C\u002Fp>\u003Cp>I mean, this is not gonna win any design awards, but, hey, it'll be alright. We'll just take these items from the first one. Let's put this below. Do we have our dates? Oh, gotta actually import that function, don't we?\u003C\u002Fp>\u003Cp>Import format from datefns functions, I guess. Alright. So we got our date. Add a little margin to the top. Looking great.\u003C\u002Fp>\u003Cp>And then what if we wanted to show that schedule? Right? So, maybe we drop down over here on the left. We have a list. And within the list, take a little GitHub completion.\u003C\u002Fp>\u003Cp>That's not really what we're looking for. It's gonna be the item inside the event dot schedule. For the key, we used you the item dot label. And then we have yeah. I was wasting more time using the GitHub suggestions.\u003C\u002Fp>\u003Cp>Item dot label. Item. We'll just steal this format, and can we do this as well? Dash hmmaa. What does that give us?\u003C\u002Fp>\u003Cp>Does that get us where we want? Okay. So now we can see the schedule. We can drop a little schedule class. List.\u003C\u002Fp>\u003Cp>List inside\u003C\u002Fp>\u003Cp>Speaker 0: list disk. I guess,\u003C\u002Fp>\u003Cp>Speaker 1: is that the\u003C\u002Fp>\u003Cp>Speaker 0: list item? Why are we why does that\u003C\u002Fp>\u003Cp>Speaker 1: look weird? Not sure. Not gonna bother with it. Let's do font bold, font mono, empty 8. Okay.\u003C\u002Fp>\u003Cp>Cool. What else we got? So we got the schedule right. Good. We got a page.\u003C\u002Fp>\u003Cp>We could go back to our events. Yeah. Maybe we add a link for that up here somewhere in the top. Next link to equals slash slash I think it's\u003C\u002Fp>\u003Cp>Speaker 0: just slash.\u003C\u002Fp>\u003Cp>Speaker 1: Back to main list of events. Alright. Cool. We got all this going on, we could go into Leap Week 3000, we could go to Leap Week 2000. How are we doing on time?\u003C\u002Fp>\u003Cp>We are cruising on time. Alright, so how do we set up registration? Let's go in and what we're going to\u003C\u002Fp>\u003Cp>Speaker 0: do, we're going to need to\u003C\u002Fp>\u003Cp>Speaker 1: basically authenticate the user first. So we've got to have a user to register. And I am going to cheat here and use, like, a SSO, like, register with GitHub. I've used this in a past project. So I've just got some code over here.\u003C\u002Fp>\u003Cp>We're gonna go in and inside my env or my Docker Compose file, I would set this up. I'm missing the client secret and client ID from GitHub, so go inside my GitHub account. We're gonna go to developers, we'll go to applications, is it authorize no. Developer settings, OAuth apps, we're going to create a new OAuth app. We'll call it Events Platform.\u003C\u002Fp>\u003Cp>Super secret. The homepage URL will be HTTPS. We'll just use Directus. Do we need we need this callback. And just from past experience, the structure for this is gonna be my directus instance slash auth \u002Flogin\u002Fprovider.\u003C\u002Fp>\u003Cp>So we're using GitHub, that's gonna be the provider. We're gonna use that callback. Great. And then I can get my client ID. I can get my client secret.\u003C\u002Fp>\u003Cp>We'll just have to use a password, log in, get the secret, they'll steal my secrets, bro. Okay. And then the last piece of the puzzle here is we need a default role for that, for that specific user. So what we're gonna do, we'll go into Directus. Let's create a new role for attendee.\u003C\u002Fp>\u003Cp>And we're not going to give them app access, we're just going to save it. I'm going to pick up the actual ID for this role, I can see it over here. That's the primary key. We'll paste that in there. I'm going to save that, and then I'm going to stop this container, Docker Compose up one more time, and the effect I should get if I load this again, now I can see this login with GitHub function.\u003C\u002Fp>\u003Cp>So I could actually log in with GitHub, and let's see. Oh, yeah. I'd have to actually do my username. Maybe we swap this out. Alright.\u003C\u002Fp>\u003Cp>We get a local host, 8055. Log in. Admin, example, password. Alright. And then I log out here.\u003C\u002Fp>\u003Cp>We could test this out. But I could also do this on the front end as well. Right? So let's go back to our page. Alright.\u003C\u002Fp>\u003Cp>So I'm gonna stick a big registration button over here somewhere, and I think I've got one in register. Is it the register form or the login form? Do I have it? Yeah. There we go.\u003C\u002Fp>\u003Cp>I got this button that I've used previously. We're gonna go to our event page. We'll save this. Uncomment this button. I don't really need the divider label.\u003C\u002Fp>\u003Cp>Alright. Signing with GitHub to register. Cool. And here you can see I've got a redirect, just basically to a different page. We're going to make this size excel, giant button, just add a class to it, give it a little more padding.\u003C\u002Fp>\u003Cp>Duplicate Attribute. Oh, MT 12. We don't want to duplicate that class. Alright. So what is this going to do?\u003C\u002Fp>\u003Cp>This should ask me to log in once accessed to my GitHub account. We authorize, and now I've got, like, the index page. So if I go here, if I actually if I signed in, right, probably getting some errors here because we did not give permissions to those specific accounts or that specific role. Right? So for our attendee role, we want them to view all the events, we want them to create registrations, maybe they can view the registrations where the user equals current underscore user.\u003C\u002Fp>\u003Cp>Alright. We're gonna have to run like hell here. Apologies for the curse words. Right? So okay.\u003C\u002Fp>\u003Cp>If you I look at my Nuxt state. I've got a use state composable that's actually storing our user data. So I should be able to see that user data. I've got the ID of the user. Do I have any other information about it?\u003C\u002Fp>\u003Cp>Maybe that's really all I need. Right? So let's try this. We'll do const user equals use state, user. Will that give us access to the user?\u003C\u002Fp>\u003Cp>And if we have a user, we can hide this button And maybe if if no user. Okay. And then maybe we add another button. Register. And on this one, we're gonna add a click handler.\u003C\u002Fp>\u003Cp>It's gonna be register for event. Okay. How's that looking? V f user. Cool.\u003C\u002Fp>\u003Cp>And now we need a function for that. Right? This is gonna be an async function because we're gonna call directus. We are going to import, we need the create item. Okay.\u003C\u002Fp>\u003Cp>Async function, register for events. We're gonna call that. K. We'll do a try. Await direct us request registrations event dot ID.\u003C\u002Fp>\u003Cp>That's gonna be an actual reactive value, so we'll just add on that. We can add that around the user too just to be safe. And cool. What else did we do? Response.\u003C\u002Fp>\u003Cp>You know, I could just console log this response just to verify. Alright. So let's take a look. If I clear out my network request, we hit register, We've got registrations. Cool.\u003C\u002Fp>\u003Cp>Great. So now we're registered for this event. If I open my Directus Instance back up where are you, mister Directus Instance? We have registered for this event. I could see this specific user here.\u003C\u002Fp>\u003Cp>Maybe I wanted to send an email for this. Let's see how we're doing on time. Roughly 20 minutes remaining. Oh, we're cruising. So let's send an email event for the registrations.\u003C\u002Fp>\u003Cp>Send registration email. Registration email. Great. We'll go in. Event hook, so anytime an event happens, in this case we want the action, so after this action happens, we're not gonna block the thread.\u003C\u002Fp>\u003Cp>Anytime the\u003C\u002Fp>\u003Cp>Speaker 0: new registration\u003C\u002Fp>\u003Cp>Speaker 1: It's great. Let's fetch the actual data. Right? So we're going to read the let's just call it get registration. Find registration, whatever you want to call it.\u003C\u002Fp>\u003Cp>From the trigger, we're gonna use full access and collection here. It's gonna be registrations. And basically, what we're going to do here, I'm going to drill through the related fields. So we're gonna do this. We're gonna get all the root level fields of the registration.\u003C\u002Fp>\u003Cp>I don't\u003C\u002Fp>\u003Cp>Speaker 0: really need to do that, but,\u003C\u002Fp>\u003Cp>Speaker 1: we've got the event. I'm gonna do dot dollar symbol or dot asterisk. I'm sorry. And then the user, we're gonna get the user details. User dot k.\u003C\u002Fp>\u003Cp>Savvy. Am I forgetting? Oh, Should only be using quotation marks. Alright. Okay.\u003C\u002Fp>\u003Cp>So that should give us the fields for the registration and should give us our related data. And now we're gonna actually send this email out. So we'll send the email. Oh, actually, let's back up. We'll save that.\u003C\u002Fp>\u003Cp>I do need the key for this. So I need to figure out what IDs we have. That's gonna be dollar sign trigger dot key. I think it's just gonna be trigger dot key. We're using that dollar sign as a special operator just to access the actual trigger data.\u003C\u002Fp>\u003Cp>That's the only operation though that we need to append or prepend that dollar sign to to actually access that data. Alright, so we've got the get registration. So this is what our email is gonna be. It's gonna be get underscore registration dot user.email. That's what we'll save that to.\u003C\u002Fp>\u003Cp>You are registered for git_registration.event.title. I think that's what it is. And what are we gonna do? Here's the URL to access the event. Alright.\u003C\u002Fp>\u003Cp>And then we'll say link, and then we're gonna add the link for this. And for our link, we're gonna have what? HTTP local host 3000\u002Fevents \u002Fgetregistration.event.slug. Okay. Alright.\u003C\u002Fp>\u003Cp>So we've got this. Let's hit save. We save. And now we cross our fingers. Right?\u003C\u002Fp>\u003Cp>I'm gonna go back. We probably need to get our registrations for each user, but I'm just gonna go in and register. We'll see if this flow actually ran. So I can see the flow here. Did we get anything wrong?\u003C\u002Fp>\u003Cp>That looks really nice. Let's check my email address. Boom, there's our email. And if I click the link, boom, there's our page. Right?\u003C\u002Fp>\u003Cp>Amazing. Alright, so, what do we want to do? We probably need to check registration, but, you know, what happens if they're registered, the time is starting, we want to show this specific event. Alright. So one of the tools that I've used in the past, is called Whereby.\u003C\u002Fp>\u003Cp>They have this nice embedded functionality where you can create a video call or a webinar experience on your own site. So that's huge, especially for folks like me that that want to build their own tools that are very design oriented. I refer to myself as a recovering designer. We're gonna go in. I could create a room with no code, but the more interesting bit here is creating this via the API.\u003C\u002Fp>\u003Cp>So let's take a look at how we do this really quickly. We want to embed Whereby into our app. They have a web component for this, but we have to generate the the room first. Right? So here is create a transient room.\u003C\u002Fp>\u003Cp>I don't know what a transient room means, but, bedding I I guess that's what we want is a transient room. Create some transient room that is available between creation and an hour after the given end date. Alright. So here's the post request, here's the structure of this. We probably need to get an API key to do this as well.\u003C\u002Fp>\u003Cp>So let's go into my Whereby application. We'll go to developer guide. No. Go to settings. Members, profiles.\u003C\u002Fp>\u003Cp>Where where is my API key? Configure. Generate an API key. Okay. Event platform.\u003C\u002Fp>\u003Cp>Okay. There's our API key. We close. We've got that. Alright.\u003C\u002Fp>\u003Cp>So let's change up our data model just a little bit where we're gonna add the event. So for our event, we've got a Whereby Room. Actually, let's go back in and we'll just set this up. Let's add our rooms to this. Right?\u003C\u002Fp>\u003Cp>Room. Alright. Take created. Just add all of this. Fancy, fancy.\u003C\u002Fp>\u003Cp>Alright. Each room is gonna be attached to an event, so we'll have a mini to 1 on the events collection or to the events collection. And if I go into advanced field creation mode, I'm going to go in and add the reverse. Right? So on the events collection, we're adding a rooms field that will have an array of the rooms.\u003C\u002Fp>\u003Cp>Cool. Alright. So we got events, and then we're just gonna have, what, a whereby room URL. Alright. Let's see if we can get this done in time.\u003C\u002Fp>\u003Cp>We are crunching for time. Alright. Okay. So now we'll say anytime we create a room, let's create a Whereby Room. Create Whereby Room.\u003C\u002Fp>\u003Cp>So anytime we create a new room within an event, we're gonna trigger a webhook request. So Action Non blocking, Items Create, Rooms, and we're going to call the API for Whereby. Whereby. We're going to I think it's a post request. Right?\u003C\u002Fp>\u003Cp>The header, we're gonna be authorization. Bear. Might include that token that I had. Alright. So where'd we go?\u003C\u002Fp>\u003Cp>Create meeting. API\u003C\u002Fp>\u003Cp>Speaker 0: meetings,\u003C\u002Fp>\u003Cp>Speaker 1: post request, Content type, I'll add that header. Content type, application, JSON. Amazing. We'll go in. The end date is what we need.\u003C\u002Fp>\u003Cp>End date is the only thing that is actually required. So we'll do end date. Just copy what they have here. I think I missed 1. 202411.\u003C\u002Fp>\u003Cp>I'm just gonna hard code this for now. We'll say 425. Push it a couple weeks out. So the room mode is probably something we're gonna set as well, just because we want it to be a large room. Room mode.\u003C\u002Fp>\u003Cp>Group. Okay. And then we've got our request body. That should give us what we want. And then the response is gonna be a room URL.\u003C\u002Fp>\u003Cp>Alright. So then we're going to update. Oh, I had it right. We'll update update room. K.\u003C\u002Fp>\u003Cp>We got rooms. We got from the let's do full access, and we'll say, what's it gonna be? Trigger dot payload trigger dot key, actually. Should be. Alright.\u003C\u002Fp>\u003Cp>And then the payload what did I call this? Whereby room URL. K. And then we'll populate that data, whereby dot data. What's gonna come back from this?\u003C\u002Fp>\u003Cp>It'll say room URL. Cool. Let's run it and see. See what we got. So we'll go to this event, Leap Week 2000.\u003C\u002Fp>\u003Cp>We'll create a new room. Maybe we need a title for this room as well. Let's add that. Title. Alright.\u003C\u002Fp>\u003Cp>So we add a new room under Leap Week 2000. Let's add a new room. This is a test room for Whereby. And we had saved we saved this. Did it update?\u003C\u002Fp>\u003Cp>No. It didn't. There's something going on. Create whereby room. What did we get?\u003C\u002Fp>\u003Cp>Bad request. Why did we get a bad request? Whereby body, end date room mode. Is there something else that we're missing? Content body dot JSON dot stringify.\u003C\u002Fp>\u003Cp>Room mode\u003C\u002Fp>\u003Cp>Speaker 0: equals group. Room URL.\u003C\u002Fp>\u003Cp>Speaker 1: Whereby embedded? Where is this? Configurable room pages. Directs plus. Where's this developer guide?\u003C\u002Fp>\u003Cp>Embedding whereby in an application. The web component is included in our SDK. I'm gonna man, we're just gonna cheat. It's not cheating. Right?\u003C\u002Fp>\u003Cp>We'll just create a room. This is gonna be a large room. No cloud recording. We're not gonna record this. We're not gonna do any automatic streaming.\u003C\u002Fp>\u003Cp>We'll set this up for the end of April. Room prefix, Leap Week 2,000. Create the room. Alright. There's a participant link for the room.\u003C\u002Fp>\u003Cp>Yeah. We could go in and customize this room, but, hey, we're running short on time. Alright. So now we go back. How do we where's the embed?\u003C\u002Fp>\u003Cp>Alright. We got this web component. We are going to install. Alright. So we go to node, pmpm@wherebybrowser.\u003C\u002Fp>\u003Cp>Ppmi@wherebybrowser. We're cutting this extremely extremely close. We'll wait for that to install. Okay. PMPM dev.\u003C\u002Fp>\u003Cp>Alright. So we're gonna go back to our events. We'll do rooms, slug. No. This will be room ID.\u003C\u002Fp>\u003Cp>That's what we'll roll with. Room dot ID. Just gonna paste basically all of this. Right? Alright.\u003C\u002Fp>\u003Cp>We'll sort this out. We're gonna request the room. We're gonna actually read an item because we're gonna have the ID. Read item. And then it should be the route dot params.id.\u003C\u002Fp>\u003Cp>I think that's gonna give us what we want. We don't need to transform that. The room data, And then we're gonna need to import this as well. So we got this imported. Read room.\u003C\u002Fp>\u003Cp>Direct to site request. Alright. Let's see if we can actually get this working in time. Proom.title. Nothing fancy.\u003C\u002Fp>\u003Cp>And then we have this whereby embed, and we can make this pull in. We've got room dot whereby room URL. Is this actually gonna get us done? And then within the event, we actually need to list those rooms. So class l\u003C\u002Fp>\u003Cp>Speaker 0: I v four\u003C\u002Fp>\u003Cp>Speaker 1: i.roominevent.roomskey equals room dot ID. Okay. Okay. Hit me. Where we at time wise?\u003C\u002Fp>\u003Cp>We're we got, like, 3 minutes left. Let's see if we can actually get a list of the rooms. Where's the rooms? Where are the rooms? We did not set this up.\u003C\u002Fp>\u003Cp>We need to view all the rooms. Do we actually see the room? Where did I put that room? Where is it? What event is it under?\u003C\u002Fp>\u003Cp>Leap week 2,000? It's under leap week 2,000. Why are we not seeing it? Why are we not seeing you, mister rooms? Slug.\u003C\u002Fp>\u003Cp>Event. We don't see the rooms. Okay. Alright then. Again, we'll\u003C\u002Fp>\u003Cp>Speaker 0: just improvise. Rooms,\u003C\u002Fp>\u003Cp>Speaker 1: rooms slash. Not the right route. Events slash rooms. Document is not defined, it's, client only, isn't it? Whereby room?\u003C\u002Fp>\u003Cp>Maybe we wrap this in client only. Is that gonna fix it? Oh, no. What is it? Can you do I think you could do client only pages.\u003C\u002Fp>\u003Cp>Let's see what we got. Page not found. Pnpm dev rooms dot ID. Come on, man. We can do this.\u003C\u002Fp>\u003Cp>We can do this. Are we gonna get it? I don't think we're gonna get it. Page not found. Why are we not finding the room?\u003C\u002Fp>\u003Cp>What am I doing wrong? Rooms. Duh. There it is. Is that right?\u003C\u002Fp>\u003Cp>No. Directus dot request dot readitem rooms, route dot params dot ID. Why are we not getting the rooms? What is our request coming back from? Direct to snow match found.\u003C\u002Fp>\u003Cp>Events rooms. We are running out of time. We don't have time for this. Document is not defined. Okay.\u003C\u002Fp>\u003Cp>So that was working correctly. We're just not finding the room. Return request. Read items rooms.route.params.id. Should be events slash rooms dotid.\u003C\u002Fp>\u003Cp>Boom. We ran out of time. Not sure exactly where things have went wrong here. Things happen sometimes. The page\u003C\u002Fp>\u003Cp>Speaker 0: not found. We are not\u003C\u002Fp>\u003Cp>Speaker 1: finding it. Don't know where we went wrong. Route.params.id should be there. It is not. Sometimes you have to admit defeat.\u003C\u002Fp>\u003Cp>So unfortunately, we have not completed the things that we needed to. Let's take a run at this and and just give a recap. Right? So what did we do? We had set up an event, allow registrations, we had a public landing page, we had room support, but we did not get to the actual event platform.\u003C\u002Fp>\u003Cp>So this is a pretty neat experience, though. If you wanna check out Whereby, they're really cool for building these things. You can actually, go to this room. Now you can see that I can request permissions from this. And, you know, you can actually serve this inside your application.\u003C\u002Fp>\u003Cp>Yeah. Super helpful thing. So that is this episode of 100 Apps, 100 Hours. I hope you enjoyed it. What a fun ride.\u003C\u002Fp>\u003Cp>We'll catch you on the next one.\u003C\u002Fp>","Hi. Welcome back to another episode of 100 apps. 100 hours where we try to build your suggestions or some of your favorite apps in 1 hour or less using Directus or publicly fail, die on the vine trying to build something. If you're new to the show, there are only 2 rules. The first rule is we have 60 minutes to plan and build, no more, no less. The second rule is the anti rule. Use whatever you have at your disposal. So then the brakes. Let's dive into today's episode. Right? We're gonna be building an events platform, a virtual events platform. Now that sounds to me very very vague, could mean a lot of things to a lot of different folks. But, what kind of products are we talking about? So Zoom comes to mind as far as, like, registering a meeting and performing that meeting. You know, maybe you've got some interactivity like a chat, some registration. You know, you've seen this beautiful, beautiful, beautiful registration page before, I'm I'm sure. Not knocking you Zoom, but, like, with all the engineers that you guys have, why is this the best that we get for a registration page? I don't like it. Other softwares that I've looked at, like, DemoHop is an interesting one that, you know, has, like, a science fair where each there's a bunch of different booths, some of the other ones, like, Livestorm. There's a ton of these different webinar platforms every way that you slice it. But what I've discovered and and just in my own experience with these over the years, it's nice to have your own. So that's what we're gonna be building today. We're gonna be building our own events platform. Let's run through it. We'll start the clock, pull up 60 minutes, and away we go. Okay. So, I am in FigJam here. Let's just kind of mock out our functionality that we like or what we're looking to see out of this particular app. So, as far as our functionality, this is a nice serif font. Maybe we underline that as well. Great. Alright. So what do we wanna do? We wanted to set up we can't have all of it underlined. That's not cool. Set up an event. You know, allow registrations. You know, have a public landing page, registration page. What else do we want? We wanna have chat during the event. Maybe we wanna have different rooms for the event. You know? You think a virtual event, it could just be just, hey. It's a workshop. It's a webinar. Could also be like a week long leap week like we do here at Directus. So maybe we have rooms for that. This this seems like I'm getting way in over my head, so we'll stop there. And then let's let's kinda just flesh out our data model for this a bit. So in my mind, what are we gonna have? We're gonna have events. K. And we got that nice purple color. Maybe we'll make this a little bit bigger so we could see it. So we've got events and then we're gonna have, users or attendees. You know, that's gonna be our Directus underscore users collection. We'll just pick that up. Directus gives us that out of the box. No reason not to use it. Amazing. And then I would imagine there's a junction table here, right, where we have could have multiple events. So these will be, like, registrations, maybe. Naming stuff is always the hardest part of development. So we got registrations, draw some arrows, just connect these, connect these in your mind as well. Great. And then what else? You know, if we jive with the rooms, you know, we could have rooms as well. Right? Each room has a different topic, some schedule, etcetera. If we think of, like, our events, they're gonna have a date, start date, duration, or maybe an end date, title, description. What else do we have? Registrations, obviously. Probably an image. Schedule, potentially. Cool. Alright. Getting too carried away. Anyway, let's actually start building something. Right? This feels just roughly what we're looking for. So let's dive into it. Right? What have I got set up for this? Just gonna close all these other things. I have a Directus instance. I've got a Nuxt 3 application, just a starter application set up. It's got a login and register function. I don't even think the register actually works on this thing, but that's what we're working with. Let's go ahead and get logged in to our Directus instance. What did I set the password to? I think it's just password. Real secure. Alright. So we've got a blank instance. I've got my FigJam over here on the left. Got Directus on the right, a 125% zoomed in, and let's just dive in. Right? Our first collection, we're going to start here with our events and we'll create a new collection for that. Let's give it a UUID, When it was created, when it was updated, those are all great. Let's do status as well for like published events, draft events, completed events. Let's just take a look at that. Right? So we have published, we have draft, we have archived. I could go in and flesh these out further if I wanted to or we could just leave it at that. Maybe draft is the default. That's good. Solid. Alright. So here, we're gonna have a name for the event or a title. The event title, that's just gonna be a string. A description of this, we want to be the anti zoom here, so we're gonna make this a text field using the WYSIWYG interface for Directus. We're going to call it description. That's our key. And I could adjust our toolbar if I wanted to. Maybe I want to add undo and redo. I'm not a big fan of underline, so I'll just remove that. If I need to, I can dive into advanced field creation mode, and specifically with the WYSIWYG editor, you can you've got some formatting options here and options overrides that you can pass to the tiny MCE instance if you need to. Like, you know, maybe you have a special class for buttons or something like that that you want to display inside that WYSIWYG editor. Alright. Let's add an image for this. So just a single image. You know, we could potentially have, you know, multiple images, but we'll just keep it simple. That's what we're going to show on a card. As I'm imagining this, on the front end, it's It's just like a running list of events or maybe a grid of cards for the events. When you get inside the event, you've got to register. Then once you register, maybe you can see the schedule for the event. Cool. So as far as something like a schedule, right, how should we set this up? If I'm not really sure, like, one of the things that I do a lot, I'm not really sure what this is gonna look like immediately. Instead of creating another table, I may use something like the repeater, which is basically a JSON type field. That's what it's going to save as our, inside our SQL database. But let's just call it schedule, and then I could go within this and edit fields to repeat, like a set of fields. So, I don't know. I like a speaker. It'll probably be something else. Let's just say the name or label. Okay. We'll do string. That requires a value. What's the name of this thing? And the interface would be an input and then we could do something like time, date, time, time. Start time, Start time. Sounds great. That's what we'll roll with. Let's make that full width and then we'll use the timestamp format. We'll require value. Maybe we don't require value. May not need to pick a time. Maybe we're just showing scheduling order. When does this thing start? Cool. Alright. Sweet. So now we have some events or at least the the start of something for events. Let's go in and go back to the data model settings. We're just gonna do registrations and we'll do, like, a date created, user created. Do we even I don't even think we need this. Status. Do we have a status for the registration? Maybe it's canceled or something like that. I'm reading too much into it. Right? So here's the date created. Maybe I wanna show this, so I'll click the drop down, show that field. Maybe we make it full width, just in case. And for our registrations, we're gonna start digging into our relationships. Right? So when I think of a registration, we've got the event that that registration belongs to, and we've got the user that made the registration. So those are both gonna be basically many to one relationships. So we'll go in for the key, we'll add the event, we'll just do events. And for our display template, let's use the title. I can already tell that I forgot to input my start and end date for the event. Amazing. So we'll have to go back and fix that as well, but there's our event. Let's go ahead and create our relationship to the users. So we'll say the user that's registered or, you know, this could be like the attendee. Again, naming stuff is hard. Cut me some slack. Alright. So I want to pick the directus underscore users collection. I'm just going to dig into the systems drop down here, or system, choose direct us users. I can get fancy with the display on this. Give them the avatar with a nice little thumbnail. We'll do first name, last name, and behind the scenes, this is just mustache syntax. So I can edit these raw values if I want, do it that way, or I could just pick from the drop down. That's great. We don't wanna create new users from this. We could potentially select users. Great. Okay. So that's our basic data model. Why do we have event? Some type of bug. Alright. Regardless, we have events, we have registrations. Let's go in and add our start time for this. How could I forget? So in this case, we wanna save the time zone value, so we're gonna use time stamp. This could be the start date. Maybe we have an end date for this or, you know, potentially a duration. I guess you could do either or or both. You know, you could potentially even use flows to calculate that if you wanted to. But this is good. Let's add some icons just to fancy it up a little bit. My friend Kevin on the team here will be sweating if you watch me add these icons, but, there's always room for design. Cool. Alright. So we got events. Let's create our first event. Right? What are we gonna call this? Let's call it Leap Week number 2 1000. Leap Week 2 1000. We are taking a huge leap forward for Directus. Join us as we take a huge leap forward for Directus. Lots of cool stuff. Let's add some formatting to this as well. You know, thing 1, thing 2, thing 3. Cool. Alright. And then let's add an image for this. Right? So let's just search Directus Leap Week and see if we can grab, do we have any images? Yeah, we got a couple laying around. Cool. So we'll save this image. Is that right? Hope that's the right image. We'll just upload that. Everything announced at our 1st Leap Week. And let's set a start date for this. Let's say May 15th is a good date. Maybe this runs 2, 3 days. Yeah. Great. So for our schedule, we've got workshop with Bryant. That is the I forget what day we did this now. 17th. Oh, that's it. May 17th. Not April 17th, Brent. Alright. Let's say that is at 3. Great. Cool. So I'm gonna fill out the schedule workshop with Kevin. Go for 15th at, at 16th at 1 PM. Great. Alright. So now we've got our event. Maybe let's just drag this out a little bit, move this around. Okay. One of the other things that I forgot here is probably like a slug. Right? I want a nice URL on the front end. So we'll go back into events, we'll just create a new field for it, we're going to use the input, call it slug. The other thing that I'm going to do is go in and make this URL safe, so we could slugify this thing. Now one of the other cool things with the addition of the marketplace, let's look for a slug, Permalink interface to enter URL slugs. Is this what we're looking for? No. WP slug interface. Yeah. This is cool. Alright. Let's try this out. We'll hit refresh, install this extension. I didn't have this on the first season of 100 apps, 100 hours, But now I can go into our interface and we can choose the slug interface. And our template is going to come from the title and we'll auto generate on create. Let's try that and see what we've got. We shrink the title and the slug. Cool. Alright. So now we got a couple settings. We can hit auto generate. Generates this nice slug, and it looks a lot like the old days of WordPress. I mean WordPress is still around, obviously, but this is this is nice, I like it. Alright. So we've got events, we've got registrations. What do we do next, right? We're gonna look at access control. So this is our settings of who has access to what inside Directus. There's 2 default roles, public and administrator. Let's make the public roles, we want to be able to view the events, We don't want anybody to edit events. And then, for as far as registrations, maybe we want to be able to create registrations and then maybe just read, you know what? Let's give full access to that. And, that should be maybe what we want, enough to play around with. Right? So now what are we going to do? Right? Let's dive into the front end of this application. Alright. So I'm going to pull this up. Again, I've just got a a Nuxt application here, and let's flesh out a couple of routes. Index dot view. We can make this just a list of our events. Alright. So we're gonna go in. I've got Directus configured as a Nuxt plugin here. It's just got some authentication in it. We've got real time in case we ever needed it. And then we've just got the REST client. That's the way I prefer it. And we just provide that to the Nuxt application. So we'll call that up like this, const directives equals use Nuxt app, and in this case, we're probably gonna do, like, read items as well. So we'll grab that from import items from the atdirectus SDK. And, alright, what's next? Right. So we're gonna do some data. We'll use await, use async data, and we'll do return directus dot request read items. And we're reading from the, what, events collection. And then I have a, query for different parameters. Right? We wanna show maybe we only wanna show published events, right, so I could do this. We'll just say status equals published. Thank you, GitHub Copilot. Let's see what that gets us. Directors. Cool. Maybe we scrap all of this. Would do we even need this to be flexed? Probably not. Alright. Let's see what we got. Data. Just wrap this in like a pre tag. See if we get any actual data. Right. So we've set up this filter. We're not getting any data because I'm assuming we did not publish this event. So if I go in and publish this, let's see what we have now. Okay. Cool. So we have our event information. What can we do with this, right? So I do also have the, like, Nuxt UI library built into this. So if you just go to ui.nuxt, using that, it's got, you know, some components already built in for buttons and cards and things like that. Cool. So without getting too fancy on the UI, let's just mock something up now that we can see we've got some actual data. Right? We got a diff. We add some padding. Y 12p y 6. We add a, do we even need an h one? Let's call it h 2. This will be font bold. Text, let's make the text large. And let's make it mono, font mono. Alright. So will be events. See what we got there. There's our events. And now let's do a grid. Right? So we'll just do a div to wrap this. Just grid, maybe grid calls to like a break point, maybe we set the gap to 8, and then we're gonna do a v 4. Right? So, for our events, let's just use this card. I think there's a card component in here. I feel like a nice card component is ucard. We've got, like, a header and a footer, or we could just throw it in the middle. So let's do that. Right. V 4. We're gonna do events and data. You know? And, usually, I like to destructure those. Yeah. Yeah. Maybe we call it events. And for the key here, we'll use event ID. That way, we don't have to, use, like, an index or something like that, and then we'll actually do this. Alright. So is there actual I think there's, like, a text component here, so let's just do, like, an h three. Maybe we just take the auto completion here from, GitHub Copilot. We'll do a little Nuxt image to optimize these images. Works very well with Directus. There's a provider for it. Then we've got the event dot image. We use the event title. That seems pretty good. Let's see what we come up with here. Sweet. Except for our description. Right? This kind of sucks because it is not what we want. So in this case, we'll do div. Let's do v h t m l event dot description, and remove that. Great. It has no formatting. Maybe we've got, do I have to tell when? We got pros class. Yeah. There we go. Alright. So we've got that, and let's add a link for this. Right? Maybe we add a little spacing here, n t 4. Maybe we make that add a little spacing here. MT2, MT8. It's great. Cool. So now we've got an event looking really nice. You know, I could duplicate this event if I wanted to. Let's just see what this looks like with a couple of more events. We're going to call this Leap Week 3000. Flush this out. And what I'm going to do here, we're just going to open this image up. I'm going to get really fancy on this, guys. And we'll just mark this up. Scroll this out. 3000. Oh, so fancy. Alright. For Leap Week 3,000, we're going to close this out, upload a new photo, we can see that's Leap Week 3,000. And then what I'm going to do here is click Save and Copy from the 3 dot menu button and now I've got 2 events. We've got Leap Week 2000, Leap Week 3000. There we go. Amazing. Okay. Alright. So now we need a route for our actual event. Right? So let's set this up. Let's actually add a folder for it just in case we have a couple of other routes within that. And then for our events, we're gonna have a slug dot view. So that's just gonna be a dynamic route. We'll view I think it's init. View init. What does that give me? That's not the syntax that I like. It'll do. Trying to make it fancy. I need to get my snippets set back up. I had to scrap Versus code. Lost all my snippets. Alright. So we got a slug. We're gonna use the route here. So we've got route equals use routes. Cool. And then, on the individual page, right, we want to call the specific data. And, you know, maybe we don't even need this full description on our index page, so we could, you know, potentially leave that off. Maybe we just make it the event start date, but we may wanna use that in here. And let's use just, like, a date function. I think it's format from date f n's. Formats oh, not currency. I'm gonna do format. What we got? DDD, month month, DD. Where does that get us? Doesn't get you anywhere because it breaks it. Forgot the year year. I guess it's all lowercase. And then okay. Yeah. Still still got a ways to go memory wise. So date f and s, just a handy library. We got format. What's the date? What are we looking for? Like, month is gonna be m m m. Like that one. So we'll do m m m. And what if we do, like, the ordinal of the dates, day of the month, d o? That's what we want. Now we're looking nice. Okay. And then we wanna do the time. Right? So maybe we show the time. Local day of the week, AM PM. What's this gonna be? H. What minute? M m m. And then we've got AM, PM, AAA. Wasting too much time on this. Okay. Alright. So we got that. Let's go in and just wrap this card in a NuxLink, or at least the content of the card. We'll do 2. And let's make this dynamic. That'll be \u002Fevents\u002F event.slug. Uh-oh. Oh, forgot to close that. Cool. Alright. So now we've got the links set up, so we can click on these and and move to the route. We don't really have anything inside our route though. Right? So I could take this function here, just copy that in, and here we're still going to be reading items. Right? I'm going to call this event, in addition to status. I guess we can set this up where if you call it directly, even if it's a draft, maybe you wanna see it. It. So let's just do this. We'll say slug equals route dot params dot slug. That should get us the event that we want. And then, the actual event here, we're gonna do, like, a transform function. The nice little part that's baked into the use async data call, that composable inside Nuxt, where basically this is gonna return an array. I could transform this and just pick up the first item of this array. And now if we do this, just do pre class. Again, I always like to do this just to see what data I'm getting back. And now we've got just the specific data object for this item. Alright. What's next? Right? We're gonna dive into building this actual page. Right? So let's give it a nice h one tag. We've got font bold, font mono, text let's make this giant. We're gonna do the event dot title. Okay. Leap week 3,000. What else do we have? If I dig into the clipboard, I've got that event description. We can make this pros large. We wrap that. Let's move down. We'll do another div. Throw this into an image, event dot image, class w 96. Where's our actual image at? Okay. There's our image. We could flex these 2, add a little bit of padding. I mean, this is not gonna win any design awards, but, hey, it'll be alright. We'll just take these items from the first one. Let's put this below. Do we have our dates? Oh, gotta actually import that function, don't we? Import format from datefns functions, I guess. Alright. So we got our date. Add a little margin to the top. Looking great. And then what if we wanted to show that schedule? Right? So, maybe we drop down over here on the left. We have a list. And within the list, take a little GitHub completion. That's not really what we're looking for. It's gonna be the item inside the event dot schedule. For the key, we used you the item dot label. And then we have yeah. I was wasting more time using the GitHub suggestions. Item dot label. Item. We'll just steal this format, and can we do this as well? Dash hmmaa. What does that give us? Does that get us where we want? Okay. So now we can see the schedule. We can drop a little schedule class. List. List inside list disk. I guess, is that the list item? Why are we why does that look weird? Not sure. Not gonna bother with it. Let's do font bold, font mono, empty 8. Okay. Cool. What else we got? So we got the schedule right. Good. We got a page. We could go back to our events. Yeah. Maybe we add a link for that up here somewhere in the top. Next link to equals slash slash I think it's just slash. Back to main list of events. Alright. Cool. We got all this going on, we could go into Leap Week 3000, we could go to Leap Week 2000. How are we doing on time? We are cruising on time. Alright, so how do we set up registration? Let's go in and what we're going to do, we're going to need to basically authenticate the user first. So we've got to have a user to register. And I am going to cheat here and use, like, a SSO, like, register with GitHub. I've used this in a past project. So I've just got some code over here. We're gonna go in and inside my env or my Docker Compose file, I would set this up. I'm missing the client secret and client ID from GitHub, so go inside my GitHub account. We're gonna go to developers, we'll go to applications, is it authorize no. Developer settings, OAuth apps, we're going to create a new OAuth app. We'll call it Events Platform. Super secret. The homepage URL will be HTTPS. We'll just use Directus. Do we need we need this callback. And just from past experience, the structure for this is gonna be my directus instance slash auth \u002Flogin\u002Fprovider. So we're using GitHub, that's gonna be the provider. We're gonna use that callback. Great. And then I can get my client ID. I can get my client secret. We'll just have to use a password, log in, get the secret, they'll steal my secrets, bro. Okay. And then the last piece of the puzzle here is we need a default role for that, for that specific user. So what we're gonna do, we'll go into Directus. Let's create a new role for attendee. And we're not going to give them app access, we're just going to save it. I'm going to pick up the actual ID for this role, I can see it over here. That's the primary key. We'll paste that in there. I'm going to save that, and then I'm going to stop this container, Docker Compose up one more time, and the effect I should get if I load this again, now I can see this login with GitHub function. So I could actually log in with GitHub, and let's see. Oh, yeah. I'd have to actually do my username. Maybe we swap this out. Alright. We get a local host, 8055. Log in. Admin, example, password. Alright. And then I log out here. We could test this out. But I could also do this on the front end as well. Right? So let's go back to our page. Alright. So I'm gonna stick a big registration button over here somewhere, and I think I've got one in register. Is it the register form or the login form? Do I have it? Yeah. There we go. I got this button that I've used previously. We're gonna go to our event page. We'll save this. Uncomment this button. I don't really need the divider label. Alright. Signing with GitHub to register. Cool. And here you can see I've got a redirect, just basically to a different page. We're going to make this size excel, giant button, just add a class to it, give it a little more padding. Duplicate Attribute. Oh, MT 12. We don't want to duplicate that class. Alright. So what is this going to do? This should ask me to log in once accessed to my GitHub account. We authorize, and now I've got, like, the index page. So if I go here, if I actually if I signed in, right, probably getting some errors here because we did not give permissions to those specific accounts or that specific role. Right? So for our attendee role, we want them to view all the events, we want them to create registrations, maybe they can view the registrations where the user equals current underscore user. Alright. We're gonna have to run like hell here. Apologies for the curse words. Right? So okay. If you I look at my Nuxt state. I've got a use state composable that's actually storing our user data. So I should be able to see that user data. I've got the ID of the user. Do I have any other information about it? Maybe that's really all I need. Right? So let's try this. We'll do const user equals use state, user. Will that give us access to the user? And if we have a user, we can hide this button And maybe if if no user. Okay. And then maybe we add another button. Register. And on this one, we're gonna add a click handler. It's gonna be register for event. Okay. How's that looking? V f user. Cool. And now we need a function for that. Right? This is gonna be an async function because we're gonna call directus. We are going to import, we need the create item. Okay. Async function, register for events. We're gonna call that. K. We'll do a try. Await direct us request registrations event dot ID. That's gonna be an actual reactive value, so we'll just add on that. We can add that around the user too just to be safe. And cool. What else did we do? Response. You know, I could just console log this response just to verify. Alright. So let's take a look. If I clear out my network request, we hit register, We've got registrations. Cool. Great. So now we're registered for this event. If I open my Directus Instance back up where are you, mister Directus Instance? We have registered for this event. I could see this specific user here. Maybe I wanted to send an email for this. Let's see how we're doing on time. Roughly 20 minutes remaining. Oh, we're cruising. So let's send an email event for the registrations. Send registration email. Registration email. Great. We'll go in. Event hook, so anytime an event happens, in this case we want the action, so after this action happens, we're not gonna block the thread. Anytime the new registration It's great. Let's fetch the actual data. Right? So we're going to read the let's just call it get registration. Find registration, whatever you want to call it. From the trigger, we're gonna use full access and collection here. It's gonna be registrations. And basically, what we're going to do here, I'm going to drill through the related fields. So we're gonna do this. We're gonna get all the root level fields of the registration. I don't really need to do that, but, we've got the event. I'm gonna do dot dollar symbol or dot asterisk. I'm sorry. And then the user, we're gonna get the user details. User dot k. Savvy. Am I forgetting? Oh, Should only be using quotation marks. Alright. Okay. So that should give us the fields for the registration and should give us our related data. And now we're gonna actually send this email out. So we'll send the email. Oh, actually, let's back up. We'll save that. I do need the key for this. So I need to figure out what IDs we have. That's gonna be dollar sign trigger dot key. I think it's just gonna be trigger dot key. We're using that dollar sign as a special operator just to access the actual trigger data. That's the only operation though that we need to append or prepend that dollar sign to to actually access that data. Alright, so we've got the get registration. So this is what our email is gonna be. It's gonna be get underscore registration dot user.email. That's what we'll save that to. You are registered for git_registration.event.title. I think that's what it is. And what are we gonna do? Here's the URL to access the event. Alright. And then we'll say link, and then we're gonna add the link for this. And for our link, we're gonna have what? HTTP local host 3000\u002Fevents \u002Fgetregistration.event.slug. Okay. Alright. So we've got this. Let's hit save. We save. And now we cross our fingers. Right? I'm gonna go back. We probably need to get our registrations for each user, but I'm just gonna go in and register. We'll see if this flow actually ran. So I can see the flow here. Did we get anything wrong? That looks really nice. Let's check my email address. Boom, there's our email. And if I click the link, boom, there's our page. Right? Amazing. Alright, so, what do we want to do? We probably need to check registration, but, you know, what happens if they're registered, the time is starting, we want to show this specific event. Alright. So one of the tools that I've used in the past, is called Whereby. They have this nice embedded functionality where you can create a video call or a webinar experience on your own site. So that's huge, especially for folks like me that that want to build their own tools that are very design oriented. I refer to myself as a recovering designer. We're gonna go in. I could create a room with no code, but the more interesting bit here is creating this via the API. So let's take a look at how we do this really quickly. We want to embed Whereby into our app. They have a web component for this, but we have to generate the the room first. Right? So here is create a transient room. I don't know what a transient room means, but, bedding I I guess that's what we want is a transient room. Create some transient room that is available between creation and an hour after the given end date. Alright. So here's the post request, here's the structure of this. We probably need to get an API key to do this as well. So let's go into my Whereby application. We'll go to developer guide. No. Go to settings. Members, profiles. Where where is my API key? Configure. Generate an API key. Okay. Event platform. Okay. There's our API key. We close. We've got that. Alright. So let's change up our data model just a little bit where we're gonna add the event. So for our event, we've got a Whereby Room. Actually, let's go back in and we'll just set this up. Let's add our rooms to this. Right? Room. Alright. Take created. Just add all of this. Fancy, fancy. Alright. Each room is gonna be attached to an event, so we'll have a mini to 1 on the events collection or to the events collection. And if I go into advanced field creation mode, I'm going to go in and add the reverse. Right? So on the events collection, we're adding a rooms field that will have an array of the rooms. Cool. Alright. So we got events, and then we're just gonna have, what, a whereby room URL. Alright. Let's see if we can get this done in time. We are crunching for time. Alright. Okay. So now we'll say anytime we create a room, let's create a Whereby Room. Create Whereby Room. So anytime we create a new room within an event, we're gonna trigger a webhook request. So Action Non blocking, Items Create, Rooms, and we're going to call the API for Whereby. Whereby. We're going to I think it's a post request. Right? The header, we're gonna be authorization. Bear. Might include that token that I had. Alright. So where'd we go? Create meeting. API meetings, post request, Content type, I'll add that header. Content type, application, JSON. Amazing. We'll go in. The end date is what we need. End date is the only thing that is actually required. So we'll do end date. Just copy what they have here. I think I missed 1. 202411. I'm just gonna hard code this for now. We'll say 425. Push it a couple weeks out. So the room mode is probably something we're gonna set as well, just because we want it to be a large room. Room mode. Group. Okay. And then we've got our request body. That should give us what we want. And then the response is gonna be a room URL. Alright. So then we're going to update. Oh, I had it right. We'll update update room. K. We got rooms. We got from the let's do full access, and we'll say, what's it gonna be? Trigger dot payload trigger dot key, actually. Should be. Alright. And then the payload what did I call this? Whereby room URL. K. And then we'll populate that data, whereby dot data. What's gonna come back from this? It'll say room URL. Cool. Let's run it and see. See what we got. So we'll go to this event, Leap Week 2000. We'll create a new room. Maybe we need a title for this room as well. Let's add that. Title. Alright. So we add a new room under Leap Week 2000. Let's add a new room. This is a test room for Whereby. And we had saved we saved this. Did it update? No. It didn't. There's something going on. Create whereby room. What did we get? Bad request. Why did we get a bad request? Whereby body, end date room mode. Is there something else that we're missing? Content body dot JSON dot stringify. Room mode equals group. Room URL. Whereby embedded? Where is this? Configurable room pages. Directs plus. Where's this developer guide? Embedding whereby in an application. The web component is included in our SDK. I'm gonna man, we're just gonna cheat. It's not cheating. Right? We'll just create a room. This is gonna be a large room. No cloud recording. We're not gonna record this. We're not gonna do any automatic streaming. We'll set this up for the end of April. Room prefix, Leap Week 2,000. Create the room. Alright. There's a participant link for the room. Yeah. We could go in and customize this room, but, hey, we're running short on time. Alright. So now we go back. How do we where's the embed? Alright. We got this web component. We are going to install. Alright. So we go to node, pmpm@wherebybrowser. Ppmi@wherebybrowser. We're cutting this extremely extremely close. We'll wait for that to install. Okay. PMPM dev. Alright. So we're gonna go back to our events. We'll do rooms, slug. No. This will be room ID. That's what we'll roll with. Room dot ID. Just gonna paste basically all of this. Right? Alright. We'll sort this out. We're gonna request the room. We're gonna actually read an item because we're gonna have the ID. Read item. And then it should be the route dot params.id. I think that's gonna give us what we want. We don't need to transform that. The room data, And then we're gonna need to import this as well. So we got this imported. Read room. Direct to site request. Alright. Let's see if we can actually get this working in time. Proom.title. Nothing fancy. And then we have this whereby embed, and we can make this pull in. We've got room dot whereby room URL. Is this actually gonna get us done? And then within the event, we actually need to list those rooms. So class l I v four i.roominevent.roomskey equals room dot ID. Okay. Okay. Hit me. Where we at time wise? We're we got, like, 3 minutes left. Let's see if we can actually get a list of the rooms. Where's the rooms? Where are the rooms? We did not set this up. We need to view all the rooms. Do we actually see the room? Where did I put that room? Where is it? What event is it under? Leap week 2,000? It's under leap week 2,000. Why are we not seeing it? Why are we not seeing you, mister rooms? Slug. Event. We don't see the rooms. Okay. Alright then. Again, we'll just improvise. Rooms, rooms slash. Not the right route. Events slash rooms. Document is not defined, it's, client only, isn't it? Whereby room? Maybe we wrap this in client only. Is that gonna fix it? Oh, no. What is it? Can you do I think you could do client only pages. Let's see what we got. Page not found. Pnpm dev rooms dot ID. Come on, man. We can do this. We can do this. Are we gonna get it? I don't think we're gonna get it. Page not found. Why are we not finding the room? What am I doing wrong? Rooms. Duh. There it is. Is that right? No. Directus dot request dot readitem rooms, route dot params dot ID. Why are we not getting the rooms? What is our request coming back from? Direct to snow match found. Events rooms. We are running out of time. We don't have time for this. Document is not defined. Okay. So that was working correctly. We're just not finding the room. Return request. Read items rooms.route.params.id. Should be events slash rooms dotid. Boom. We ran out of time. Not sure exactly where things have went wrong here. Things happen sometimes. The page not found. We are not finding it. Don't know where we went wrong. Route.params.id should be there. It is not. Sometimes you have to admit defeat. So unfortunately, we have not completed the things that we needed to. Let's take a run at this and and just give a recap. Right? So what did we do? We had set up an event, allow registrations, we had a public landing page, we had room support, but we did not get to the actual event platform. So this is a pretty neat experience, though. If you wanna check out Whereby, they're really cool for building these things. You can actually, go to this room. Now you can see that I can request permissions from this. And, you know, you can actually serve this inside your application. Yeah. Super helpful thing. So that is this episode of 100 Apps, 100 Hours. I hope you enjoyed it. What a fun ride. We'll catch you on the next one.","8c0c2643-c249-4bf7-8319-87fb4758fe08",[3311],"bd184dc4-368f-468a-815a-f2a8e8b246c0",[],{"year":978,"number":2062,"id":2311,"episodes":3314,"show":3315},[2313,2314,2315,2316,2317,2318,2319,2320,2321,2298],{"title":322,"slug":323},{"id":3317,"slug":3318,"vimeo_id":3319,"description":3320,"tile":3321,"length":3322,"resources":3323,"people":3327,"episode_number":159,"published":3332,"title":3333,"video_transcript_html":3334,"video_transcript_text":3335,"content":12,"seo":12,"status":19,"episode_people":3336,"recommendations":3339,"season":3340},"fa776fca-1f88-44ac-8c54-96232474282d","toggle","925489400","In the season finale, Kevin and Andrew fly close to the sun to understand which snake strategies work best, by A\u002FB testing live with DevCycle. Do they keep their wings?","b98c544a-5cc0-453f-bce6-d75614580ac3",105,[3324],{"name":3325,"url":3326},"DevCycle","https:\u002F\u002Fdevcycle.com\u002F",[3328,3329],{"name":2820,"url":2821},{"name":3330,"url":3331},"Andrew MacLean","https:\u002F\u002Ftwitter.com\u002Fandrewdmaclean","2024-05-02","Toggle","\u003Cp>Speaker 0: Hello, everybody.\u003C\u002Fp>\u003Cp>Speaker 1: Hello. I'm sorry, Kevin. Ready yet.\u003C\u002Fp>\u003Cp>Speaker 0: You are ready. Alright. Let's try this again. Alright. 3, 2, 1.\u003C\u002Fp>\u003Cp>Hello, everybody, and welcome to ready no. I\u003C\u002Fp>\u003Cp>Speaker 1: didn't know where to do that. We're gonna\u003C\u002Fp>\u003Cp>Speaker 0: do it. Ready. Ready. Set. Set.\u003C\u002Fp>\u003Cp>Battlesnake. I don't think we're gonna get it, Kevin. I don't think\u003C\u002Fp>\u003Cp>Speaker 1: we're gonna say the final episode of\u003C\u002Fp>\u003Cp>Speaker 0: Alright. We're gonna try this again. I feel like this is I feel like this is good for the final episode. Let's do this. For the final episode.\u003C\u002Fp>\u003Cp>Let's get this right. Welcome to the final episode of Ready set, set, battle mistake. Mistake. We're close enough. We got there.\u003C\u002Fp>\u003Cp>Speaker 1: Close enough.\u003C\u002Fp>\u003Cp>Speaker 0: We got there. I'm gonna count it. Alright. So welcome, everyone. Episode 4 of Ready, Set, Battlesnape.\u003C\u002Fp>\u003Cp>I'm Andrew. This person either down below or to my right or left. I don't know where he's he is right\u003C\u002Fp>\u003Cp>Speaker 1: now. Direction.\u003C\u002Fp>\u003Cp>Speaker 0: Is Kevin, and we are your your snake charmers, your code, your code coaches, your, your fun fans. I don't even know. But we are here today.\u003C\u002Fp>\u003Cp>Speaker 1: You are fully overestimating our entertainment value.\u003C\u002Fp>\u003Cp>Speaker 0: I I don't know what you're talking about. I feel like our entertainment value can never be underestimated. We are we are more entertaining than we even think. Thank you so much for coming back and checking this out, Kevin. Thank you so much for for coming back again after I kinda threw you, a little bit of a, a little bit of a challenge at the end of our our last episode together.\u003C\u002Fp>\u003Cp>Speaker 1: It was tough.\u003C\u002Fp>\u003Cp>Speaker 0: But we got there. I'm sure you'll return the favor to me today. Yeah. So if you haven't caught episodes 1, 2, and 3, you should probably go back and check those out. We, kind of we're gonna talk a little bit more about exactly what we've been doing, But, in episode 1, we built a Battlesnake, and we'll talk a little bit about Battlesnake kind of as we go through.\u003C\u002Fp>\u003Cp>Obviously, in episode 2, we implemented, that same logic from our 1st JavaScript starter snake in, Directus. In episode 3, we added a whole bunch more logic to our snake but mostly just to visualize the games that were coming through the platform. And today Kevin, do you know what we're doing today?\u003C\u002Fp>\u003Cp>Speaker 1: I I do, but I will let you I will let you share. I think it's your turn.\u003C\u002Fp>\u003Cp>Speaker 0: I think it is. I feel like you've been leading the show for the entire time. So it's probably my turn. Also, I feel like should I be let me see. I'm, like, wanting to be more centered.\u003C\u002Fp>\u003Cp>Oh, where are we? There we go. Now I'm feeling good.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Good.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. I thought that, like, make make the choice to change where your camera is as you're as you're on on camera. Yeah. Screw it.\u003C\u002Fp>\u003Cp>Speaker 1: Why not?\u003C\u002Fp>\u003Cp>Speaker 0: Good vibes. Good vibes. So, on today's episode, we are going to be, implementing a new technology that we haven't really looked at, called Devcycle. And so, you now know from the last few episodes about what Battlesnake is. You know what Directus is because you're here on Directus TV.\u003C\u002Fp>\u003Cp>And so now is the time to introduce you to the technology that I work with on a daily basis as a developer advocate at Devcycle, which is a feature flag management platform. So our plan today is to try and add some logic to our, Directus snake to get this, working is maybe a relative term. Getting the the dev cycle platform yourself\u003C\u002Fp>\u003Cp>Speaker 1: a lot of scope there. A lot of scope. Working is a relative term.\u003C\u002Fp>\u003Cp>Speaker 0: But we're gonna use the dev cycle platform to, add a little bit of, add a little bit of flavor to our snake and give it a little bit of, give it a little bit of control. And that will make a lot more sense in a minute. But Kevin, you look like you have something to say.\u003C\u002Fp>\u003Cp>Speaker 1: I I mean, I'm gonna ask the question which I think you're probably gonna work towards, which is like, can we just talk for a moment about what feature flags are and then maybe what you had in mind, and then we launch straight in.\u003C\u002Fp>\u003Cp>Speaker 0: I love it. So, a feature flag, if you've never heard of them before, condition conditional statement in your code, that allows for you to, turn features on and off depending on a bunch of targeting criteria. So let's say you are working on a new feature on your platform, or a new feature maybe for your Battlesnake, and you're not quite ready to to roll that out in all of the games, you could add a feature flag. Just wrap it around the code and then through a feature management platform like DevCycle, you can say, for these kind of games, I want you to use kind of this logic. And for these kind of games, I want you to use these this logic.\u003C\u002Fp>\u003Cp>It's a really big part of things like trunk based development. So if you're moving from, like, branch based development where you're sort of, like, having features in branches that are long lived, and then we all had to deal with sort of, like, long lived branches and merging and dealing with merge conflicts. Mhmm. Feature flags are kind of a way to avoid those things just by constantly having your new features in code, and you can kind of decouple the code release from the feature release process.\u003C\u002Fp>\u003Cp>Speaker 1: Interesting. Okay. And just to embellish this with my understanding, and you can tell me if I'm wrong, they basically work. So you put these conditional statements in your code, and what triggers those is this feature management platform. And typically, you would do that with some form of either like AB testing where people get allotted into these random buckets or you can be a bit more pragmatic and say 5% of users are gonna have this conditional turned on and therefore get a new feature.\u003C\u002Fp>\u003Cp>And then we will also, like, we'll analyze their behavior as a result and decide whether it works, if people get it, and so on, rather than going it's an all or nothing kind of deal. Right?\u003C\u002Fp>\u003Cp>Speaker 0: You got it. You got it.\u003C\u002Fp>\u003Cp>Speaker 1: Got it.\u003C\u002Fp>\u003Cp>Speaker 0: That's it. Alright. Money. So Great. That is a very ambitious plan that I just laid out, by the way.\u003C\u002Fp>\u003Cp>I I feel as though may we get to a stage where we're able to, like, just determine, what kind of gameplay we're gonna have? That's possible. Maybe not in a 2 hour stream, but, we are gonna get Directus and Devcycle working together. How do I know they work together? Because I've already written a blog post on this separately from Battlesnake, so I got to explore.\u003C\u002Fp>\u003Cp>And so now is the time to use my knowledge for for good and for evil.\u003C\u002Fp>\u003Cp>Speaker 1: We'll link the blog post, by the way. We'll link the blog post. There are 2. There's 1 on our blog, that Esther wrote, which uses dev cycle, and 1 on the dev cycle blog, which you wrote, which is direct us. And I'll put a link to both of them in the resources section that accompany this video.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. I love it. So, y'all are seeing my screen right now. And so up here, you'll notice that this looks very similar, to, 1, mister Kevin Lewis's screen, although not in the Arc browser. I'm not apparently a strong enough, software developer to use Arc.\u003C\u002Fp>\u003Cp>Not there yet. Once it comes to Windows, I'll start using it. It's it's it's happening. I know it's coming. I'm in a month of this.\u003C\u002Fp>\u003Cp>Speaker 1: And then people will full on get this, but we're clearly recording this a day after the last one because they these are the games from the last episode they created about 23 3 hours ago, which is completely legit. We did something a little cheeky before this stream. We in a completely low rim fashion, you're running a director's to project. And I literally was like, here's a sequel database file and here's the extensions. Go run them and here's the creds I used to go log in.\u003C\u002Fp>\u003Cp>We may have to reconcile with that at some point when things break. So there is a chance things won't behave and we'll have to fix that on the fly, but should be okay.\u003C\u002Fp>\u003Cp>Speaker 0: I love it. So we have our snake our direct to snake here. It's alive. It works. Not yet.\u003C\u002Fp>\u003Cp>We haven't determined if it works yet, which we probably will have to do. I also have our snake that was running on Replit. So the original, ready set battle snake that we created from the JavaScript starter snake. So Mhmm. I have that snake, but Replit was getting real mad at me today because I was using too much RAM and and compute.\u003C\u002Fp>\u003Cp>So I pulled that down, and put it into this wonderful, repo here. Yes, sir.\u003C\u002Fp>\u003Cp>Speaker 1: And you're gonna be bumping your font size up about 5 times.\u003C\u002Fp>\u003Cp>Speaker 0: No. We're gonna expect everyone to to zoom in. Exactly. Exactly. That's the look of the look at\u003C\u002Fp>\u003Cp>Speaker 1: my spectacles.\u003C\u002Fp>\u003Cp>Speaker 0: Everyone has. And so, yeah, let's let's zoom in here a little bit. Alright. So this is the the kind of the original snake that our code is based off. So you will notice here that not all the logic that we implemented over the past couple of episodes are in here.\u003C\u002Fp>\u003Cp>That's fine. This is more just to, like, show us how it's gonna work once we get there, and then we're gonna implement this indirect disk. We're gonna try and implement it in direct disk. But I think maybe the first thing we should do, Kevin, is actually get our Directus snake working. What do you think?\u003C\u002Fp>\u003Cp>I feel like that's a logical first step.\u003C\u002Fp>\u003Cp>Speaker 1: And what we're gonna do to make that happen is we're gonna set up ngrok. And so do you pay for ngrok?\u003C\u002Fp>\u003Cp>Speaker 0: I don't, but I have ngrok. So, like, I can I can set it up right now, but it'll be ephemeral?\u003C\u002Fp>\u003Cp>Speaker 1: Incredibly heavily, like, rate limit to you. Let's pause here. Let's get NGROC set up and using temporarily my auth token, and then we will come back. Great. So this bit, we won't we won't publish.\u003C\u002Fp>\u003Cp>But, yeah, it was actually the one thing we forgot before we started. So let me just figure that out.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Throw me the key and then I can,\u003C\u002Fp>\u003Cp>Speaker 1: Indeed.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. And let me just go to engrok now because I was actually logged in earlier. It was working for me in the games that I was playing, but,\u003C\u002Fp>\u003Cp>Speaker 1: it but then it gets mad. That's the whole problem here. So what we want here is your auth token. So, here is the engrok command you are going to want to run here in the chat. Yep.\u003C\u002Fp>\u003Cp>Speaker 0: Sorry. I'm just\u003C\u002Fp>\u003Cp>Speaker 1: then because I'm on, like, old, the command looks slightly different, and that's there.\u003C\u002Fp>\u003Cp>Speaker 0: Where are you hiding? Hold on. My windows have all disappeared. Where are you, Kevin? There you are, in chat here.\u003C\u002Fp>\u003Cp>Alright. Wonderful. There's a chat. Here's a command. We're gonna add this auth token.\u003C\u002Fp>\u003Cp>Beautiful. And we're gonna head back over here to our terminal and oh, nice. There we go. Paste. Beautiful.\u003C\u002Fp>\u003Cp>Auth token saved, and let's run\u003C\u002Fp>\u003Cp>Speaker 1: Nice. This.\u003C\u002Fp>\u003Cp>Speaker 0: It's good. I set up ng croc earlier.\u003C\u002Fp>\u003Cp>Speaker 1: I knew Yeah. Yeah. Absolutely.\u003C\u002Fp>\u003Cp>Speaker 0: Beautiful. Alright.\u003C\u002Fp>\u003Cp>Speaker 1: Good. So I think we can actually snap back now, and Yep. It's up to you what you want on the screen. We could be here if you want.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I mean, I mean, if you've never used ngrok before, I mean, we talked about this in episode, I guess, 2. We were looking at this. I have the free version. Kevin's got one that's less rate limited, so we're using that one.\u003C\u002Fp>\u003Cp>But it's super easy to install. Like, just homebrew it, and then you add your auth token to ensure things are happening. But, anyways, we've got it running right now. And then my direct to snake over here is should technically be working. Now, Kevin, I'm really curious.\u003C\u002Fp>\u003Cp>You should technically be able to go and run a game on yours, and see if it works. And I'm interested I think\u003C\u002Fp>\u003Cp>Speaker 1: Are you running directors?\u003C\u002Fp>\u003Cp>Speaker 0: Apparently, I am not. I think I might have closed it. Nope. I'm running it. Yep.\u003C\u002Fp>\u003Cp>We're all good. Where is it running though? That's the real oh, you know it's running in another one.\u003C\u002Fp>\u003Cp>Speaker 1: Do you have another\u003C\u002Fp>\u003Cp>Speaker 0: I do.\u003C\u002Fp>\u003Cp>Speaker 1: Versus code do. Window. There it is. Awesome. So we're gonna watch\u003C\u002Fp>\u003Cp>Speaker 0: these logs here. We're gonna bump Shall\u003C\u002Fp>\u003Cp>Speaker 1: we whack the font size up there as well? So I'll add ready set battlesnake and hungry bot once again to an 11 by 11 board. I'll hit start game.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And there we go. It's coming through. Awesome. We're working.\u003C\u002Fp>\u003Cp>And you're like, but Andrew and Kevin, we can't see the games, and therefore, this isn't fun and enjoyable for us. Us. Fear not. Because I think actually an easy way to do this so we can keep ready, set, battlesnake in your repo. Can you make ready, set, battles cake public so that I can add him to my Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: I can. Yeah. My battlesnakes. Ready set battles cake. We named it that.\u003C\u002Fp>\u003Cp>So it had a k instead of an n because it was the Kevin version of the snake as an FYI, and it is now public.\u003C\u002Fp>\u003Cp>Speaker 0: Awesome. And so if you're ever in the Battlesnake platform, really easy. All you gotta do is just search for snake. Ready, set. Should find him skate.\u003C\u002Fp>\u003Cp>There we go. That's what we want it to look like. And so now we've got them in there. And now we're gonna see this game going on, and it should be all of our logic and direct us. Something's not happening.\u003C\u002Fp>\u003Cp>Something's unhappy here. Let's see what it is.\u003C\u002Fp>\u003Cp>Speaker 1: Womp womp.\u003C\u002Fp>\u003Cp>Speaker 0: I love that we tried and then we failed.\u003C\u002Fp>\u003Cp>Speaker 1: Could you remove the what the old one password, pop up?\u003C\u002Fp>\u003Cp>Speaker 0: I certainly can. Okay. There we go. Oh, no. Nope.\u003C\u002Fp>\u003Cp>Same thing happened again, actually. Create Rematch.\u003C\u002Fp>\u003Cp>Speaker 1: So let's think it through. What is going wrong here? It could be that our flow isn't running correctly, but it should be. Yeah. Like, it's going off.\u003C\u002Fp>\u003Cp>It's triggering the flows. It's getting a 200, which means it's getting a response, and it's returning. So what is going what is going on?\u003C\u002Fp>\u003Cp>Speaker 0: What is going on? We haven't gotten Because\u003C\u002Fp>\u003Cp>Speaker 1: it was randomly breaking last time too. At the end of session 2, at the end of the second episode, our snake was, like, overpowered. Like, it was so good, and then it started to crumble. It started to grumble, last time.\u003C\u002Fp>\u003Cp>Speaker 0: Let's take a look at our logs. We have got some logs in here. Holy moly.\u003C\u002Fp>\u003Cp>Speaker 1: We do.\u003C\u002Fp>\u003Cp>Speaker 0: Let's look here. So is there anything going on in here? We've got move now. Original. Yeah.\u003C\u002Fp>\u003Cp>So I think we've got something going on there. Some so what do we have here? Yes. Feeding a lot of things there. False, false, false, false.\u003C\u002Fp>\u003Cp>Oh, no safe moves. Avoid head to head. I think it might be in there. False, false, false, true, true. Yeah.\u003C\u002Fp>\u003Cp>I think the issue is in\u003C\u002Fp>\u003Cp>Speaker 1: Which we've already determined that we think is the case. So you know what we'll do to keep ourselves moving? If you close the pane, why don't we just avoid that entire operation and just take avoid body straight to determine move? Avoid head to head is a, you have to just hit the little edit in the top right of the Oh,\u003C\u002Fp>\u003Cp>Speaker 0: of course, we do see.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And hopefully yeah. If we just do that and hit save, yeah, just yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, we're back. We are back. We're back. It's alright. We're there.\u003C\u002Fp>\u003Cp>I have a belief that if you just hit the save button enough times, we're gonna be able to resolve.\u003C\u002Fp>\u003Cp>Speaker 1: I'm pretty sure that's the definition of insanity. I\u003C\u002Fp>\u003Cp>Speaker 0: think it is too. We're gonna switch things up for just a minute. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: And save\u003C\u002Fp>\u003Cp>Speaker 0: this, and that didn't work. And then we're going to go back over here. And then we're going to go back here. And we're going to see if that worked. Oh, disk.\u003C\u002Fp>\u003Cp>It was a bug.\u003C\u002Fp>\u003Cp>Speaker 1: It was a bug, by the way. And it had an issue has been opened on this bug. So there you go. Maybe just discard the changes and just we can have\u003C\u002Fp>\u003Cp>Speaker 0: Alright.\u003C\u002Fp>\u003Cp>Speaker 1: Let's see\u003C\u002Fp>\u003Cp>Speaker 0: if we can create another game, see if this will do it. I mean Okay. The oh, nope. Still doing the same thing. Still so moving out of bounds, logic is there.\u003C\u002Fp>\u003Cp>It's definitely happening in 2 minutes ago. Let's get You\u003C\u002Fp>\u003Cp>Speaker 1: have to refresh. Sorry.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. So let's go here, and let's see again. So determine move is still where things are, yeah, getting hung up.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, and you're saying avoid head to head is where everything went to force when they're logically, it should not been happening.\u003C\u002Fp>\u003Cp>Speaker 0: I think so. Maybe that's what it is. Let's go avoid head to head options. There we go. False, false, false, false.\u003C\u002Fp>\u003Cp>Payload. Avoid bodies.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. So so you know what we're gonna do? We're gonna jump into avoid head to head, and we're just gonna return the inbound date. Like, we're not gonna do anything.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Let's go in there and do that. So here we go.\u003C\u002Fp>\u003Cp>Speaker 1: Right. Because that's clearly where it were. Yeah. We're gonna literally delete everything apart from line 2 and 30. So everything in the middle there, ditch it off.\u003C\u002Fp>\u003Cp>Doesn't matter.\u003C\u002Fp>\u003Cp>Speaker 0: Can we not just oh, we can't comment. Really?\u003C\u002Fp>\u003Cp>Speaker 1: I think no. So just but it's fine. We don't need it. This is this is the last episode. There we go.\u003C\u002Fp>\u003Cp>I also have a copy of it, so it's fine.\u003C\u002Fp>\u003Cp>Speaker 0: Ah, success. Alright. So let's save this and see if it works. There we go. And now let's go and try and recreate a game again.\u003C\u002Fp>\u003Cp>Speaker 1: Because if that's the step that was causing grief.\u003C\u002Fp>\u003Cp>Speaker 0: It was. Yep. Do\u003C\u002Fp>\u003Cp>Speaker 1: you remember when I wrote all that really elegant code and it worked fantastically? Turns out not so much.\u003C\u002Fp>\u003Cp>Speaker 0: Well, it's alright. It worked fantastically until it didn't. And that is the key to all the programming. It's all good until, you crash production.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. That makes me feel better that we've identified the issue and removed it for now.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So ready, set, battlescape is working, and this is really the key here. It's still doing the job that it needs to do. So we're gonna make, we're gonna do something a little bit different now. We are going to actually be, having we're gonna bring ready set battlesnake back from the dead, in a new world.\u003C\u002Fp>\u003Cp>So let's, let's kinda go over here. So this is good. Directus is working, and now we're gonna we're gonna ignore that Directus even exists for the next little while. And we're actually gonna head back over to\u003C\u002Fp>\u003Cp>Speaker 1: Who needs to pay rent?\u003C\u002Fp>\u003Cp>Speaker 0: Who needs to pay rent? Not me today. And I wanna look at, wanna look at our code here. And so is this okay for you, Kevin, or is it still too small?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Maybe take up another yeah. Maybe that maybe that's good, I think, for people watching.\u003C\u002Fp>\u003Cp>Speaker 0: Awesome. So in here, not a lot has changed from when we were dealing in replit. So we still have our server dotjs, which is just an express server. We've got the 4 endpoints, the sort of root endpoint, start, move, end, and then we've got a bunch of stuff in here that's kind of, yeah, talking about what it's returning. We've got a read me here, which explains just how this works, which we don't really care about right now.\u003C\u002Fp>\u003Cp>And then we've got our package. Json. I haven't added anything new to here except for this new dependency, which is the dev cycle, SDK. And so this is necessary for us to implement dev cycle. If we want to do it this way, technically, we could do all of this via our management and bucketing APIs, but I think it's cleaner through the through the SDK.\u003C\u002Fp>\u003Cp>And then, there's some small changes. There's another file that's here, and we'll maybe call this in just a second. And I'll explain how I got to this file, which was not fun this morning when I was prepping for the show. So, originally, in here, we had our run server. We had our info endpoint.\u003C\u002Fp>\u003Cp>We had our start and end endpoints. And then we had this move endpoint, which actually had some really good logic inside, that if we go and ignore all of this stuff, here was our original logic. And I'm gonna uncomment all of this stuff. Oh, I'm gonna try and uncomment all of this stuff if I actually uncomment the right section. There we go.\u003C\u002Fp>\u003Cp>So this was our original logic. And I'm actually think just to make sure that this is working because, yeah, I've changed this so it wasn't async now, which was part of my issue earlier. I'm gonna save this. So this was sort of our dumb snake that we first started with. So Not dumb.\u003C\u002Fp>\u003Cp>Yeah. But, like, dumber. And so you can see here all of our steps have kind of been implemented here. The base steps here, same thing that's going on over in Directus. Disk.\u003C\u002Fp>\u003Cp>So let's see if this is working. I'm gonna run this express server. I think it's node index dotjs. So this is gonna run\u003C\u002Fp>\u003Cp>Speaker 1: How are you gonna expose this as a Battlesnake if we're already running ngrok for directors?\u003C\u002Fp>\u003Cp>Speaker 0: Oh my good guy. Also, updated cache direction to down. It is constantly running that caching. Interesting. That's fun.\u003C\u002Fp>\u003Cp>Speaker 1: So the caching wasn't originally there, Andrew. So Wasn't. And please tell us about the cursed file.\u003C\u002Fp>\u003Cp>Speaker 0: So we're gonna ignore the cursed file for a minute, and then we'll get to the cursed file. But let's see if we run this right now. Technically, I should be able to run 2, right, through engronk?\u003C\u002Fp>\u003Cp>Speaker 1: I'm not convinced, but we'll we'll find out.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. What port does Directus run on?\u003C\u002Fp>\u003Cp>Speaker 1: Not that one.\u003C\u002Fp>\u003Cp>Speaker 0: K. Well, let's see if this works. No. It works. It's there.\u003C\u002Fp>\u003Cp>Speaker 1: No. No. No.\u003C\u002Fp>\u003Cp>Speaker 0: No. No. No. No. No.\u003C\u002Fp>\u003Cp>No. No. No.\u003C\u002Fp>\u003Cp>Speaker 1: Locally, but you still have to expose it.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. So can you see if one shows up in your, is it list it'll show up on your ngrok dashboard on your end if it's running.\u003C\u002Fp>\u003Cp>Speaker 1: I didn't even know there was an ngrok dash I didn't even know there was an ngrok dashboard.\u003C\u002Fp>\u003Cp>Speaker 0: We'll see. But yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Let's take a look.\u003C\u002Fp>\u003Cp>Speaker 0: So behind the scenes, I'll actually pull this up just so people can see what this looks like. So when you go to the ngrok dashboard and you're logged in, if I am logged in, so you have, like, this authentication and set up stuff. And then you also have this tunnel section. And in the tunnels, I think it's tunnels where it actually shows you, yeah, what are currently online in your account. Account.\u003C\u002Fp>\u003Cp>And so you should see you should see\u003C\u002Fp>\u003Cp>Speaker 1: I've got one.\u003C\u002Fp>\u003Cp>Speaker 0: You've got one? K. So this is not doing what we wanted to do. Wait.\u003C\u002Fp>\u003Cp>Speaker 1: Wait. Wait. Wait. No. No.\u003C\u002Fp>\u003Cp>Yeah. And the one is you. Oh, it is? Yeah. Started 10 minutes ago.\u003C\u002Fp>\u003Cp>I'm not running anything locally.\u003C\u002Fp>\u003Cp>Speaker 0: That's that's Directus, though. That's why.\u003C\u002Fp>\u003Cp>Speaker 1: That's directives.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So here's what we're gonna do. And now we need\u003C\u002Fp>\u003Cp>Speaker 1: to try and run it a second time, but I'm not convinced.\u003C\u002Fp>\u003Cp>Speaker 0: We can shut down directives for right now because we don't need it as we're just kinda demonstrating how this works in the index dot JS. So we're gonna shut down Directus for a second, and then we're gonna run it over on our other one. Alright. So we've shut down Directus, and let's go back over to index dot j s. Let's cancel this out and awesome.\u003C\u002Fp>\u003Cp>Restarted that. What is the address that I'm wanting to go to, Kevin? Can you paste it somewhere for me?\u003C\u002Fp>\u003Cp>Speaker 1: I don't understand what you're asking.\u003C\u002Fp>\u003Cp>Speaker 0: Never mind.\u003C\u002Fp>\u003Cp>Speaker 1: I But, also\u003C\u002Fp>\u003Cp>Speaker 0: yeah. I've But you're not running ngrok yet. Right? Oh, that's right. I have to rerun.\u003C\u002Fp>\u003Cp>No. I am running it.\u003C\u002Fp>\u003Cp>Speaker 1: You need to rerun it.\u003C\u002Fp>\u003Cp>Speaker 0: Exactly. Rerun ngrok. Yeah. Oh, there we go.\u003C\u002Fp>\u003Cp>Speaker 1: Got it.\u003C\u002Fp>\u003Cp>Speaker 0: Bad things happening. Okay. Let's go\u003C\u002Fp>\u003Cp>Speaker 1: But this time, not 8,055. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: 8000. Alright. Here we go. Beautiful. Alright.\u003C\u002Fp>\u003Cp>Alright. So let's go over here and see if this is working. Beautiful. Alright. So this is running now.\u003C\u002Fp>\u003Cp>So if I go back over here to account settings oh, not account settings. My battle snakes. I'm gonna go ready set battlesnake, and we're gonna leave a JavaScript, and we're gonna have this on nothing, no platform today, and then we're gonna change our server URL. Yes. Like that.\u003C\u002Fp>\u003Cp>And now if I go in and add ready set set, Battlesnake to a game, create game. And ready to set Battlesnake.\u003C\u002Fp>\u003Cp>Speaker 1: Remove the Octa thing in the top right? It's very annoying.\u003C\u002Fp>\u003Cp>Speaker 0: My goodness. All of the prompts. And then we are going to see if this runs. Okay. Good.\u003C\u002Fp>\u003Cp>Basic logic, not running into himself, not running into walls. Good. So he's working. Alright. But I haven't implemented the logic for dev cycle yet.\u003C\u002Fp>\u003Cp>So let's go look at what's going on. So we've got the moves that are feeding through there. That's awesome. That's what we want. I'm gonna actually turn this off for a second, and then turn it back on.\u003C\u002Fp>\u003Cp>No. I'm not gonna turn it back on because it's gonna constantly need to be caching. Okay. So, that's our basic code. Right?\u003C\u002Fp>\u003Cp>We're good. We're there. We're not here for basic code today. We are here for confusing broken code that will cause many problems and will make our heads hurt.\u003C\u002Fp>\u003Cp>Speaker 1: You're really teeing this up, and I'm horrified before we've started.\u003C\u002Fp>\u003Cp>Speaker 0: Don't worry. You and me both. So let's look at the new logic that I have in here, which is not great logic, but, I'll explain it. So, what we're doing is every time the move endpoint is called, this move function is called. Yes?\u003C\u002Fp>\u003Cp>Everybody should track that so far. So what I'm doing here is I am calling a, this get cache direction function, which is basically every second or millisecond. I'm not sure. I think it might be millisecond. It's calling the dev cycle, platform and asking what move should I be getting for whatever user is on the platform right now.\u003C\u002Fp>\u003Cp>This will all make sense when you kinda see the logic, but, basically, it's it's pulling the dev cycle platform to ask it what move you should be making as a snake. Does that make sense? Yeah. I think your face\u003C\u002Fp>\u003Cp>Speaker 1: Unfortunately, this isn't even remotely the direction I thought we could go in.\u003C\u002Fp>\u003Cp>Speaker 0: No. Don't worry. We're not gonna we're not gonna manually we're not gonna manually do this. We're I just wanna demonstrate the, like, how it actually functions. And so this is good.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: And I actually you can do it mid game too as long as your your click game is is on point. Oh, man. Which we can do today because there's 2 of us here. So Oh my god. We're gonna go and actually look in this cache dotjs file.\u003C\u002Fp>\u003Cp>So here's our cache.js file. Couple things happening here. First thing, we're importing the server SDK that this dev cycle from there. Then we're initializing our dev cycle, client in here, despite it being a server application, we're calling it a dev cycle client. This stuff is That's fine.\u003C\u002Fp>\u003Cp>That makes sense. Yeah. This stuff isn't super important, the cloud bucketing and enable EdgeDB. It is and isn't, but really the important thing here is that we're initializing the dev cycle client. We're creating this cached direction, which is setting the default move for our snake to go down.\u003C\u002Fp>\u003Cp>So, basically, if, the first time, if there's been no caching, if this has never run before, it's gonna go down. And then, yeah, within this function, we're feeding in the user variable. So you have to feed this in every single time that you, that you do anything with the external platform. You have to feed to the user. You can feed in custom properties, which we may try and do today, where you can, like, base it on different information that you may have.\u003C\u002Fp>\u003Cp>But this is the idea here. And then once we've got this dev cycle, client established, we're then going to look for a specific variable that exists in the Dev cycle platform. Think of a variable as, like, the feature flag that you're looking to pull. So whether you're wanting to see on or off, we're actually pulling a string. So we're pulling for the direction for this user.\u003C\u002Fp>\u003Cp>What should this user be seeing if we're looking for the direction variable that's in there? And then we've got if we do end up having a preferred direction. Yeah. Because where's my preferred direction defined? I think it was defined over here.\u003C\u002Fp>\u003Cp>Right? Yes. There we go. So, preferred direction. So, basically, it's saying that if there is the DevSecLE client being established and it finds that that direction variable exists, then we're gonna look for that preferred direction variable that we had defined over an index.js.\u003C\u002Fp>\u003Cp>And if it returns anything but none, it's going to put a new cache direction value in there, and then it's gonna log that it's updated the cache direction. Otherwise, it's gonna give you an error. And then if we go down here, yeah. We're basically updating this every 10 it's not ten seconds. This is, like, every 10 milliseconds.\u003C\u002Fp>\u003Cp>Speaker 1: That's very, very often. Very often. So a1000 is one second.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. And then I gotcha. Yeah. So this is our code that we're doing here. Basically, we're just constantly pulling the dev cycle platform to, like, get the latest updated stage.\u003C\u002Fp>\u003Cp>So there's a little bit of a delay here, but it works pretty well.\u003C\u002Fp>\u003Cp>Speaker 1: I will believe it when I see it. Gonna be honest, mate. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. You're like, this is garbage spaghetti code. What are you doing, Andrew? It's not like it's not the code.\u003C\u002Fp>\u003Cp>Speaker 1: It's just this isn't how I would have. Can I give you maybe an idea of how else\u003C\u002Fp>\u003Cp>Speaker 0: we could have implemented this sort of code? Yeah. Let's yeah. Let's go.\u003C\u002Fp>\u003Cp>Speaker 1: We literally just have, like, branching logic in the flow. And based on, like, there's the snake gets an ID for every game. So you can enroll them and then, like, 10% of the time or whatever, they get thrown into, like, strategy 2. And strategy 2 could be it become it just goes left all the time. Like, it doesn't matter.\u003C\u002Fp>\u003Cp>Yeah. But you could implement branching logic. Right?\u003C\u002Fp>\u003Cp>Speaker 0: So we are and that is what we are gonna do. So you're not wrong with that. I just wanna show how this works.\u003C\u002Fp>\u003Cp>Speaker 1: That's fine. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Then we can get into the logic. So Okay. That's our logic here. So, basically, we're calling this every 10 seconds, and then it's gonna if it's updating, it's gonna show us this using cache direction, and then it's gonna do that.\u003C\u002Fp>\u003Cp>Speaker 1: And just to clarify, the whole reason you've created this notion of a cache direction is so we're not gonna go to dev cycle every single move. We will go a 100 times a second instead.\u003C\u002Fp>\u003Cp>Speaker 0: So what's what's interesting is because we're basically running an async method here, there's a delay and devs battle sync doesn't like there being any sort of a delay. So I had to I completely got it. So you\u003C\u002Fp>\u003Cp>Speaker 1: have to have some I got it.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. You've gotta do the polling yourself. I think you probably if I was a better programmer, I could probably figure it out, but this was like No.\u003C\u002Fp>\u003Cp>Speaker 1: No. I I I get it now because you could call dev cycle every time, but you're not gonna be able to respond within 500 milliseconds confidently. Yes. So for that reason, instead, we will catch the last direction it gave you. Exactly.\u003C\u002Fp>\u003Cp>I got it. I got it. That makes sense. Fine. You're I'm with you.\u003C\u002Fp>\u003Cp>And\u003C\u002Fp>\u003Cp>Speaker 0: it's interesting. The reason I figured out that this was working is so if you notice here, in the logs, so we actually it returns move 117 right. What I was seeing in my logs was it would be like move 01746328. And so it was taking time to actually do the the the calculation. So anyways\u003C\u002Fp>\u003Cp>Speaker 1: Which is no good for the snake. It just won't work. Yeah. And so\u003C\u002Fp>\u003Cp>Speaker 0: I'm gonna leave this I'm gonna leave this at, at 10 milliseconds for right now, now only because I'm running on my own device, and I think your end grunt won't get upset at me. And because I know that it will demonstrate how this works. The downside here is I actually, Kevin, will need you to be the one that is starting no. Because nobody's gonna be able to see it. Okay.\u003C\u002Fp>\u003Cp>We're gonna see how fast Andrew can click things is what we're actually gonna see. So we're gonna start this again. So if everything's working properly over here, I've uncom oh, I've gotta uncomment this. Let me cancel that again. Let's uncomment that import statement.\u003C\u002Fp>\u003Cp>I think all the rest of this logic has already been commented. Awesome. So now if we run this yeah. So we're basically checking this, like, every 10 milliseconds. It's kind of crazy.\u003C\u002Fp>\u003Cp>I don't fully know why I don't think it's running every time it shouldn't be.\u003C\u002Fp>\u003Cp>Speaker 1: I'm unsure logging every time it's making your terminal choke.\u003C\u002Fp>\u003Cp>Speaker 0: Here's my question though, is we're not calling move all the time. So I'm confused why\u003C\u002Fp>\u003Cp>Speaker 1: That's a good point.\u003C\u002Fp>\u003Cp>Speaker 0: Get cache direction is being called every single time because it shouldn't be. It should just, like, literally only be called when moved unless I've got a game running somewhere. Do I have a game running somewhere?\u003C\u002Fp>\u003Cp>Speaker 1: You shouldn't be. No. I don't even know how you would check that.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I don't know either. But, like, if I look at this okay. Request made.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, are you the squiggly brackets? Yes. The destructuring.\u003C\u002Fp>\u003Cp>Speaker 0: I shouldn't have done that. Alright. So, yeah, it should not be running. It should be running every time. What is going on here?\u003C\u002Fp>\u003Cp>It must be something in this actual cache file. Let's see what's going on here. Import dev cycle, initialize dev cycle, let cash direction go down, update cash direction, import dev cycle.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Because you're set into it here on line 33. So that's happening regardless. That's happening the moment it's imported. That's not linked to that function running.\u003C\u002Fp>\u003Cp>I don't think it's that much of an issue.\u003C\u002Fp>\u003Cp>Speaker 0: I'm gonna change this to, like, a 100 because what's that?\u003C\u002Fp>\u003Cp>Speaker 1: That would've that was gonna be my only suggestion is maybe just knock it knock it down a bit. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Anyways, we're gonna do this. We're not gonna do a lot of this, because it's gonna be less important. But, we're gonna set this running, and then we're gonna go and actually take a look at the game. So here we go.\u003C\u002Fp>\u003Cp>So we're basically changing this direction, and I'll actually actually, let me cancel that for a second. And I'll go to the dev cycle platform so I can show you. I already set this up.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. Yeah. That'd be good.\u003C\u002Fp>\u003Cp>Speaker 0: Here are the dev cycle form. Once I log in once it logs in come on, dev cycle. What are you doing? Oh, there we go. Logged into the wrong one.\u003C\u002Fp>\u003Cp>So we actually just launched a, a bunch of new example apps, and so I've been playing around with those. But there's no way to log out without, getting rid of the the demo mode. So, that's how we're gonna log out.\u003C\u002Fp>\u003Cp>Speaker 1: I feel like it's like a user retention is great if the user can ever log out, get a life hack.\u003C\u002Fp>\u003Cp>Speaker 0: All right. Let's close these again, see if I close this. And let's try app.devcycle.com again. And I think this is gonna load. Yeah.\u003C\u002Fp>\u003Cp>There we go. Awesome. Log in with my work count. It's gonna bring us over to the dev cycle platform, all of our production stuff. We're not gonna mess with our production stuff today.\u003C\u002Fp>\u003Cp>Speaker 1: Do it. Do it. Let your intrusive thoughts win.\u003C\u002Fp>\u003Cp>Speaker 0: So I did it at, at KubeCon. I actually we made a bunch of live changes while we were doing demos, and our entire engineering team got very unhappy with us. And so I think we're ready to set Battlesnake. And I've got this features area here. So I've got a feature that I've set up called my choose move feature.\u003C\u002Fp>\u003Cp>And this is Could you,\u003C\u002Fp>\u003Cp>Speaker 1: zoom in, sorry, 1 or 2 times? No. That's okay. The small text is quite small. We're good.\u003C\u002Fp>\u003Cp>Nice. Thank\u003C\u002Fp>\u003Cp>Speaker 0: you. Perfect. Yeah. So we've got our choose move feature. This isn't super important for us right now because I've only got one feature.\u003C\u002Fp>\u003Cp>And then I've got this direction variable that I have there, and I've defined 4 different variations, left, right, up, and down. These are string variables that are in there. And then in my targeting logic, I'm basically saying who to serve what or what to serve to what users that are feeding into that SDK. So here, I'm basically saying serve all of the users down, but that's also my default. So I actually want to be serving to see if this is working and actually talking right.\u003C\u002Fp>\u003Cp>I'm going to change this to serve everything right. And if this is working now, when I turn on that Battlesake server, I should be seeing everything in this updated cache direction except for the first one showing as right. So let's see if that works. There we go. And even the first one did.\u003C\u002Fp>\u003Cp>So we've got the changes happening here. And now if I go back over to the platform, change this to up and save and then take a look at my logs. And so it's like real time changes that are happening in there that you're able to kind of differentiate what's going on. So the interesting thing here is that, technically, platform to control your snake in real time. It's a terrible idea.\u003C\u002Fp>\u003Cp>Speaker 1: That is literally the like, like, we've seen people play Battlesnake live using, like, games controllers. And even that, you're like, well, there are some questionable choices here. This is like drop down and then save.\u003C\u002Fp>\u003Cp>Speaker 0: So but I'll show you something inter I'll see if I can get this to work, and I'll just do a I started using, there we go. So I started using, rectangle, and\u003C\u002Fp>\u003Cp>Speaker 1: Sure.\u003C\u002Fp>\u003Cp>Speaker 0: I can't believe that I've never used it, anything like that. Oh, yeah.\u003C\u002Fp>\u003Cp>Speaker 1: I use Raycast for it, and I have keyboard shortcuts. Oh, look at you. Which is really, really nice.\u003C\u002Fp>\u003Cp>Speaker 0: So let's grab ready set battle sneak in here. And we're gonna go start gaming. Just ready set battle in here. And so you'll see it's not gonna work right now because it's not running. So he's going up.\u003C\u002Fp>\u003Cp>So we know it's not down. There's no conversations that are happening right there. So since we know that we've set our game mode to, let's change it to left so we know that something's working here, and then I'm gonna start my server. And now if I create a rematch, should go left. Awesome.\u003C\u002Fp>\u003Cp>So we know that it's working. Sure. Alright. So now I'm gonna do the thing that is, like, the test of Andrew's ability to click things really fast. I'm gonna create a rematch.\u003C\u002Fp>\u003Cp>And then in, like, real time, I'm gonna click save. And there should be a switch if the board is big enough. But, actually, I need a bigger board because that will give me more time for things to go wrong. Let's go 19 by 19. Add, ready, set, Battlesnake.\u003C\u002Fp>\u003Cp>Start game. Oh, I shouldn't have done that. Anyways, it's fine.\u003C\u002Fp>\u003Cp>Speaker 1: You've done that. You you moved moved.\u003C\u002Fp>\u003Cp>Speaker 0: Do do do do do. No. It's too late. The game already runs as soon as you click create rematch, so I gotta be a super fast. Alright.\u003C\u002Fp>\u003Cp>Here we go. So right now, he's set to go left. I wanna change it so he goes\u003C\u002Fp>\u003Cp>Speaker 1: Don't worry about hitting play. Just hit create rematch, hit save, and we can see it in the playback that it's happened.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Here we go. There we go. Oh, and he's going left first. So literally, the worst example\u003C\u002Fp>\u003Cp>Speaker 1: that ain't gonna work. Before you create rematch before, you wanna yeah. Prime. Prime. Oh, yeah.\u003C\u002Fp>\u003Cp>Change it.\u003C\u002Fp>\u003Cp>Speaker 0: So right now, we're going down. So we're gonna get it. So we're gonna go first, we'll be going down, and then we should go right as long as we know where it, as we long as we know where it spawns. Okay. This should work, actually.\u003C\u002Fp>\u003Cp>This should be good. So here we go. Should go down first, and then at a certain point, go right. Oh, not fast enough. We're gonna try this again.\u003C\u002Fp>\u003Cp>I tell you, it did it does work. You just gotta have the the right movement.\u003C\u002Fp>\u003Cp>Speaker 1: Andrew, I full on believe you. You you don't have you're not proving anything. Don't worry.\u003C\u002Fp>\u003Cp>Speaker 0: Come on. Really?\u003C\u002Fp>\u003Cp>Speaker 1: Your your taste.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, did we see that? We did it. We did it. So here's the thing. Right?\u003C\u002Fp>\u003Cp>So we changed it from right to up. So we're going right. We're going right. We're going right. And then suddenly, we start going up.\u003C\u002Fp>\u003Cp>And that's there's no logic in here that says, dearest, dearest ready set battlesnake, avoid that wall. This is all just, Andrew changing that logic in real time. And you can see if we go to the logs there, which are gonna be, like, super overrun now, not that logs, these logs. But you can see here in our terminal there we go. Yeah.\u003C\u002Fp>\u003Cp>So we're updating our cached app, updated cache direction app. And so you can see that there's, like, the change that happens in here. Yeah. Oh, and this is the interesting thing too here. Right?\u003C\u002Fp>\u003Cp>Is you actually see so most of the time, it's updating cash Kevin, are you impressed? Are you blown away by my amazing implementation of this?\u003C\u002Fp>\u003Cp>Speaker 1: I am so blown away. Thank you so much. I feel like my life has been genuinely enriched in the last 39 minutes of recording. Thank you, Andrew. I don't\u003C\u002Fp>\u003Cp>Speaker 0: know what I would do without you as\u003C\u002Fp>\u003Cp>Speaker 1: a friend, a mentor, a colleague.\u003C\u002Fp>\u003Cp>Speaker 0: Thank you. Thank you. Good. Yeah. And that's it.\u003C\u002Fp>\u003Cp>Episode over. We're done. Episode wrapped. Thanks. So, that was just like this is I feel like in every good episode, in every good coding experience, you should do something that's totally useless and makes absolutely no sense so you have the knowledge and skills to do something that actually makes sense.\u003C\u002Fp>\u003Cp>Speaker 1: But there are some things already set up. Right? So we've got the dev cycle, like, prod forgive my terminology if I use the wrong terminology. But we've got the project set up, we've got the variable, we've got the options, we've got the ability to change it and hit save. We have a client, you know, we have like an ID for this specific feature.\u003C\u002Fp>\u003Cp>So, you know, we have all the parts, and we can just apply it in\u003C\u002Fp>\u003Cp>Speaker 0: Exactly. Different ways. So what we're gonna need to do here is I'm going to so we're gonna forget that all of this ever happened. We're gonna go shut off ngrok, for a second, and then we are going to go back to our direct us instance, and we're gonna start direct us up again so we can start to have some fun. And we've got all of our code here.\u003C\u002Fp>\u003Cp>Why\u003C\u002Fp>\u003Cp>Speaker 1: We do. Are you It's it's it's the other it's the other Visual Studio code that's in the back. Yeah. Are you\u003C\u002Fp>\u003Cp>Speaker 0: talking about? What? Hold on. Alright. We'll doc, and then we'll go back over here to engrok, and let's get this running.\u003C\u002Fp>\u003Cp>Oh, I'm running the wrong one. There we go. Nice. Wonderful. And let's go over to here, which I should actually have open in my window.\u003C\u002Fp>\u003Cp>Oh, and we don't need this for right now. So let's open that and make that bigger. And we should be able to go to here. Wonderful. I am gonna need to log in again, I think.\u003C\u002Fp>\u003Cp>Speaker 1: No. No. No. I think it'll be fine.\u003C\u002Fp>\u003Cp>Speaker 0: No. I need to log in again.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, you're gonna need to log in again. Battlesnake@lws.iohunter2. Glasses us what I set this up with. There you go.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So here is our logic. We removed a bunch of stuff from in there. Great. Our snake is working again.\u003C\u002Fp>\u003Cp>Should be working again. Logs are all there. I feel less bad about all the logging that I was doing of every time that we were calling Dev cycle now that I see these Exactly.\u003C\u002Fp>\u003Cp>Speaker 1: Thousands of We actually you know, this was I mean, all the dates, like, visible elsewhere. Just today, we actually recorded, or we we did a live request review, which is our show where we or it's our live event, which then becomes a show on director's TV a week later, where we go through feature requests to talk about them, and today's was all about flows improvements. And one of the big things is the ability to change the retention of those logs. I like that. Because, yes, they get they get chunky.\u003C\u002Fp>\u003Cp>Speaker 0: They do. So, what I think we are going to do, and I liked the idea that you were talking about before, Kevin. I think let's do 2 pieces of logic, and we're gonna we're not gonna delete all of this stuff. We're just gonna kinda remove it for right now, and then we'll reconnect it. And I think we'll have just right now 2 different blocks.\u003C\u002Fp>\u003Cp>1 of them that returns right, one of them that returns left for moves, and we'll have some branching logic in there. How does that sound to you?\u003C\u002Fp>\u003Cp>Speaker 1: Let's create a new flow for this, and then we could just hook it up to the extension, rebuild it. Like, I think that might be a good a good shout.\u003C\u002Fp>\u003Cp>Speaker 0: Let's do that. So let's call this snake, cycle. Alright. And Great. We're just gonna leave that.\u003C\u002Fp>\u003Cp>Speaker 1: All of that's good. Just next.\u003C\u002Fp>\u003Cp>Speaker 0: And webhook or webhook. Yeah. Okay. And then get, I think.\u003C\u002Fp>\u003Cp>Speaker 1: Host.\u003C\u002Fp>\u003Cp>Speaker 0: Not the get. Not I had a 5050 shot there, and I got it wrong. There we go. No async\u003C\u002Fp>\u003Cp>Speaker 1: calls either. Because because we'll get because we'll get a post request from the Battlesnake platform. Yeah, data of last operation safe. And then what we wanna do here is grab that URL. Yeah.\u003C\u002Fp>\u003Cp>I think yeah. Perfect. Go go to the code editor. Go to the extension called snake. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: I think so. And then in our source index dot JS, and we just wanna update this. Right?\u003C\u002Fp>\u003Cp>Speaker 1: For move for move. The move one. And just to not have to do the round trip, we'll change the for local host 8055. But hit paste. That'll give us the ID\u003C\u002Fp>\u003Cp>Speaker 0: of the trigger and everything.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. And then, yeah, we'll just change that for HTTP, not HTTPS, localhost\u003C\u002Fp>\u003Cp>Speaker 0: secure my how to secure my URLs. Kevin, who who are won't work. Phone's bloody work, man. Actually, it does. No.\u003C\u002Fp>\u003Cp>No. It does. I'm pretty sure. I'm pretty sure ngrok automatically anyways, I don't I don't need to\u003C\u002Fp>\u003Cp>Speaker 1: Not not on not on this one. Yeah. Yeah. But yeah. Yeah.\u003C\u002Fp>\u003Cp>Yeah. But not on the local not on local host. If you were doing it via ngrok, but then we're gonna have to do another round trip to ngrok for what? It runs locally.\u003C\u002Fp>\u003Cp>Speaker 0: Smart. Okay. So we've got our trigger there. This is our move. So now this is all feeding through to the place that\u003C\u002Fp>\u003Cp>Speaker 1: we want. Now we've got to rebuild the extension and restart directives.\u003C\u002Fp>\u003Cp>Speaker 0: Npm run build?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. From the from that extensions directory. So let's create a new terminal in here.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. CD extensions.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. You got it.\u003C\u002Fp>\u003Cp>Speaker 0: And CD directives Extension.\u003C\u002Fp>\u003Cp>Speaker 1: Extension snake. Jake and m You got it. Build. You got it.\u003C\u002Fp>\u003Cp>Speaker 0: And then restart And then we just Oh, because cannot.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. This is fine. This is like macOS going overboard. Open up your open up your settings on your Mac. Security, I think it is.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. We may not be able to do that just because of the security settings. Security\u003C\u002Fp>\u003Cp>Speaker 1: No. It was there. It said privacy and security.\u003C\u002Fp>\u003Cp>Speaker 0: Security settings.\u003C\u002Fp>\u003Cp>Speaker 1: No. Just yeah. There you go. Allow anyway. It's right there.\u003C\u002Fp>\u003Cp>It's right there. Fsevents.net. Yeah. Yeah. Cool.\u003C\u002Fp>\u003Cp>Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Great. Alright. Let's try this again.\u003C\u002Fp>\u003Cp>Speaker 1: You got it.\u003C\u002Fp>\u003Cp>Speaker 0: Beautiful. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Lovely. And then restart and then restart the Docker container. Fixed in directory version 10.9, which we have not upgraded to right now. There you go. Hooray.\u003C\u002Fp>\u003Cp>And now you're good.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So let's head back over to Directus, which is Yeah. Somewhere. It exists. It is in the world on this device.\u003C\u002Fp>\u003Cp>Too many windows. Directus. There we go. Okay. So here we go.\u003C\u002Fp>\u003Cp>So this should be talking here. I'm actually gonna go start a game just to make sure this is working. So let's go, play.battlesync.com. Let's go create game. And I think yeah.\u003C\u002Fp>\u003Cp>Does it still save recently used? It doesn't. That's so sad. Used to do recently saved escape. There we go.\u003C\u002Fp>\u003Cp>And if this is working, we should see a log showing up here. Don't look. Happen. Just no. That's fine.\u003C\u002Fp>\u003Cp>We don't it's probably not gonna work work, but it should show a log here. We have 10 logs. I love it.\u003C\u002Fp>\u003Cp>Speaker 1: Awesome.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Okay. Method post. Yes. So that's 10 moves that it took to pass away.\u003C\u002Fp>\u003Cp>Perfect. Logic is coming through there. Accountability. Awesome. Okay.\u003C\u002Fp>\u003Cp>It's working. So now we're gonna do some branching logic in here. So we're gonna go and condition. And we're gonna worry about the condition afterwards, but let's call this dev cycle response.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, it's\u003C\u002Fp>\u003Cp>Speaker 0: not here.\u003C\u002Fp>\u003Cp>Speaker 1: Well, hang on. Not in not in a condition. 1st, we need to go get data and then we run conditions on the results. So I'm assuming we can just make I'm assuming with DevCycle, just an API, we can make an API request, right, just a raw API request. So you want the webhook request URL, one there.\u003C\u002Fp>\u003Cp>Perfect. And first, we need to go get the data, then we'll do the conditional logic. So this might be a moment to look at the dev cycle docs.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah, that would make sense. We should probably Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: And there will be a moment here where we'll we'll redact it for for the recording, but you're gonna have to authenticate yourself. So you're probably gonna need to put in, like, an API key or token. Can you create multiple inside of your dev cycle?\u003C\u002Fp>\u003Cp>Speaker 0: That's a good question. We're gonna experience this together. Yeah. I haven't played around with the yeah. So we're gonna do a little bit of this together, and then we're gonna not do a little bit of this together.\u003C\u002Fp>\u003Cp>So let's head over there. So jumping into the dev cycle docs, APIs, management API. This is to create features and variables. I don't think it returns anything. We actually want the bucketing API.\u003C\u002Fp>\u003Cp>So this is used to fetch on HTTP. Yes. Yes. Yes. Unique identify the user.\u003C\u002Fp>\u003Cp>Definitely, this bucketing API. Get all the variables. Get all the features. Post events. Get variable by key.\u003C\u002Fp>\u003Cp>Okay. This is what we're looking for. So let's head over to bucketing API, and we are going to have to get our key Wait. What what my key?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Sorry. What's the endpoint we wanna hit?\u003C\u002Fp>\u003Cp>Speaker 0: So we are going to do get variable by key for user data, I think. Let me look here. Private platform SDK type. Missing SDK. Get all variables.\u003C\u002Fp>\u003Cp>What is that gonna return? Is that gonna return the status?\u003C\u002Fp>\u003Cp>Speaker 1: What's our what's our end goal here? Our end goal is to\u003C\u002Fp>\u003Cp>Speaker 0: We wanna just get the value for the variable. So it's very similar to what we're dealing with in our SDKs.\u003C\u002Fp>\u003Cp>Speaker 1: To the user ID to start.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. But everything is hidden behind our bearer token. So I'm just gonna find where all of this is hidden. So key type variation, variation in variation key, response examples, get all features by key post events. No.\u003C\u002Fp>\u003Cp>Fetch\u003C\u002Fp>\u003Cp>Speaker 1: No. I think get all fee I think I think you were right. Get all features by key for user or get all variables by key for user data. And this will no. Because this will\u003C\u002Fp>\u003Cp>Speaker 0: return Here it is. Yeah. Here it is. The value. Fantastic.\u003C\u002Fp>\u003Cp>That's what we're that's what we're after is the value. So, we're gonna go offline for one second as I go and get all of my authentication stuff sorted out, and, then we will be back with our API stuff.\u003C\u002Fp>\u003Cp>Speaker 1: Sounds good. Okay. So For what for what it for that's worth, we could totally have kept it going, and we could just redact it, adjust that slither slither\u003C\u002Fp>\u003Cp>Speaker 0: and time. Keep it going. Yeah. Let's do that. Alright.\u003C\u002Fp>\u003Cp>We're back. Yeah. We haven't gone anywhere. We thought we were gonna go somewhere. This is interesting too because we if an so, technically, our management and bucketing API are actually using the same stuff.\u003C\u002Fp>\u003Cp>I need to update our docs because we should actually have this curl command in, both, and they're not both there. I guess most people are just assuming we're gonna be using that. So, I'm gonna go and, in a different window, actually, open up, my OAuth token using the client ID. Okay. So I need to go over and what do I need here?\u003C\u002Fp>\u003Cp>Client ID and client secret in my dashboard. Alright. So let's go over here and yeah. I don't think\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Showed anything.\u003C\u002Fp>\u003Cp>Speaker 1: I'm a I'm a little bit fuzzy on how you authenticate.\u003C\u002Fp>\u003Cp>Speaker 0: Let me show you. Okay. So what we do here is alright. You're seeing my screen now. Hey?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: So and, again, I will fully admit that I have only messed around with our API in a very limited way. So, basically, we are doing a curl request with our client ID and client secret. We can actually obviously do this same post request in Directus, and we're just using our client ID and client c. We can't do it directly and directly.\u003C\u002Fp>\u003Cp>Speaker 1: I've just spotted it. The type the encoding type for this, for this request is a URL encoded form, but that request URL, you know, operation that's built in only sends JSON. And they're just you don't work around this. Like, you can't do it. However, I'm not too too sad because you can, of course, do it using a custom endpoint.\u003C\u002Fp>\u003Cp>It's not quite what we wanna do, but could we do or building a little custom operation. So maybe that's what we do in order to to do that round trip. It's not the ideal, like you know, it means we're gonna have to spend some time doing a little bit of extension development. Like, just spotted that. Is that is that the only way to do it?\u003C\u002Fp>\u003Cp>Speaker 0: Yep. Yeah. We have no real public API stuff, which is interesting, but also so what's interesting so we are having so we're working right now on some webhook stuff. So you can technically get when, certain changes have been made, like an audit log. You can read that through a webhook, but you can't actually read what people's values are.\u003C\u002Fp>\u003Cp>Speaker 1: Because that would that would be so good because on a webhook, you could trigger a separate flow, store indirect just like your cache Yeah. And then do stuff based on that. But that's completely fine. So we could do this or if we're building a a little custom, operation, we could just straight up use the SDK.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So let's do that. And then we've already got the code there. So, yeah. Okay.\u003C\u002Fp>\u003Cp>Let's, are we gonna do this here inside of our code, or are we gonna do this over on the platform to get started? Where are we gonna\u003C\u002Fp>\u003Cp>Speaker 1: I don't under let let's let's build an operation for flows as an extension. And the goal of that operation is to, right now, just get a fixed, you know, a fixed response from from the API. So, no, we don't do it like this. We actually use the extensions SDK CLI, which will boil the plate a whole extension for us.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. We're gonna delete that bad boy. Alright. Let's open\u003C\u002Fp>\u003Cp>Speaker 1: a new terminal here. Open up a new terminal. You got it. And we'll go into the extensions folder.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Oh, into the extensions folder. Extensions folder. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Sorry. Yeah. Yeah. Exactly.\u003C\u002Fp>\u003Cp>Exactly.\u003C\u002Fp>\u003Cp>Speaker 0: And then how do I pull up the docs? Or are you just gonna Yeah. Docs. I mean,\u003C\u002Fp>\u003Cp>Speaker 1: I could tell I could tell you this one, but let let's let's do the docs. Let's let's have the authentic experience with someone who doesn't live in this every day.\u003C\u002Fp>\u003Cp>Speaker 0: There we go. Alright. Let's head over. And we're gonna see this is gonna be my challenge. How long does it take Andrew to find out what I'm looking for?\u003C\u002Fp>\u003Cp>Alright. So when direct to stocks, we're in extensions. We're in device extensions.\u003C\u002Fp>\u003Cp>Speaker 1: Zoom in.\u003C\u002Fp>\u003Cp>Speaker 0: We're going to zoom in. We're going to go extensions. We're going to go, developing extensions and we're gonna go extension services and introduction. No. That doesn't look like what I'm looking for.\u003C\u002Fp>\u003Cp>Speaker 1: Maybe Very good. Very very good little piece of feedback there. Go into the fundamental section, the one you haven't opened out of the 3, the last one, and creating extensions.\u003C\u002Fp>\u003Cp>Speaker 0: Perfect. We're just gonna use this. Alright. Let's go back over here and run this.\u003C\u002Fp>\u003Cp>Speaker 1: But I I do see that. I do see that path you took. I'm glad to know that. Discussed it with one of our colleagues just yesterday. So yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Like, when things work oh, oh, I need to change my note version. It's upset with me. Wait. What? Oh, it's just warnings.\u003C\u002Fp>\u003Cp>Really? Oh, yeah. Whatever. Sure.\u003C\u002Fp>\u003Cp>Speaker 1: Me. That looks\u003C\u002Fp>\u003Cp>Speaker 0: what do\u003C\u002Fp>\u003Cp>Speaker 1: you wanna create? Operation, please.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, there it is. Hidden down in that list. Choose a name.\u003C\u002Fp>\u003Cp>Speaker 1: And they so they must start so they must start as of this release. I think it might be gone in 10.9 or it'll be gone in a soon release in a upcoming release. Directus dash extension dash whatever you want. Dev cycle is fine.\u003C\u002Fp>\u003Cp>Speaker 0: JavaScript because I don't play with TypeScript.\u003C\u002Fp>\u003Cp>Speaker 1: I respect that.\u003C\u002Fp>\u003Cp>Speaker 0: Dependencies. Yes. Scaffolding it. I love this. This is so smooth.\u003C\u002Fp>\u003Cp>I like a I like a good CLI.\u003C\u002Fp>\u003Cp>Speaker 1: Good. Great. So now we have if you go into the extensions drop down, you have a new extension called dev cycle inside of source. We have API dot JS. We have App JS.\u003C\u002Fp>\u003Cp>App JS is for all of the configuration. So it's all the UI part. It's all of the options when you create a new one, you have that little pane that pops out. It's the information that will show on the on the card in the overview where you connect to all the nodes. That's the overview there.\u003C\u002Fp>\u003Cp>And then the API dot JS is the node code that will actually be executed in the background. Right?\u003C\u002Fp>\u003Cp>Speaker 0: So we're gonna go and I think right now what we wanna do is actually grab, just so we're not having to worry about this later. We're gonna grab our code from here. So not going back and forth, because this is basically all the stuff that I need.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Great.\u003C\u002Fp>\u003Cp>Speaker 0: And we're just gonna put you down there and comment you out so that things don't get upset with us.\u003C\u002Fp>\u003Cp>Speaker 1: Sounds good.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Perfect. So let's save that, and let's do an install. So I do have to have to go into this folder to install, slash extensions. Directus extension CD.\u003C\u002Fp>\u003Cp>Extension. CD. Cycle. Not we're not we're gonna put it at the end and see what happens. No.\u003C\u002Fp>\u003Cp>Start. Start. Start. Start.\u003C\u002Fp>\u003Cp>Speaker 1: For a moment, I was like, am I about to learn something?\u003C\u002Fp>\u003Cp>Speaker 0: No. There's there's very little learning that will be\u003C\u002Fp>\u003Cp>Speaker 1: There's very little learning. I got it. I got it.\u003C\u002Fp>\u003Cp>Speaker 0: And then we gotta go over to because we're gonna install the, the SDK. We're gonna go over to the dev cycle docs. So we'll move back over to docs, and let's head back over to the dev cycle docs, docs.devcycle.com. And we're gonna head over to our SDK section, our node JS SDK\u003C\u002Fp>\u003Cp>Speaker 1: You got it.\u003C\u002Fp>\u003Cp>Speaker 0: Installation. And let's grab this because who uses yarn, NPM or bust? I've become so opinionated in my coding Alright. So that means that our import statement should work now. Actually, is this import or is this require?\u003C\u002Fp>\u003Cp>I This is import. Yeah. I thought so. Alright. Let's move you to the top.\u003C\u002Fp>\u003Cp>Alright. Perfect. So step 1 complete. We're done. Time to go home, everybody.\u003C\u002Fp>\u003Cp>End of episode.\u003C\u002Fp>\u003Cp>Speaker 1: So a couple of things that are just important before we continue. There is an ID here called custom. Let's rename that, and then we have to reflect that ID in the App JS and the corresponding App JS. I think dev cycle is a perfect ID. So we just want those 2 to match, first of all.\u003C\u002Fp>\u003Cp>Very good. And let's, while we're here, give this a name. Dev cycle, this is the nice friendly name that shows up when you pick your operation type. Sure. Yeah.\u003C\u002Fp>\u003Cp>Then are we gonna ask the user for any configuration up front? And I'm gonna say that maybe we do. Maybe we wanna collect the snake eye like, the the the ID. Right? Are we gonna do it?\u003C\u002Fp>\u003Cp>Are we gonna have time to do that? I mean, can we give it just an arbitrary ID, or does do you have to register users inside of the dev cycle system first? Like, how does that work?\u003C\u002Fp>\u003Cp>Speaker 0: So you would need to feed a specific user's information. Yeah. Let's call a snake name. Yes. I think that's a great idea.\u003C\u002Fp>\u003Cp>Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: I was a snake ID. Snake ID. So let's do that. And at first, we'll just hard code it, but later, you can put dynamic values in here in flows. So instead of text, let's just change text to snake.\u003C\u002Fp>\u003Cp>So there's a friendly name. Yeah. Yeah. Perfect. There's a friendly name.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. Is that correct? Is it? I changed this from tests from is this yeah?\u003C\u002Fp>\u003Cp>Speaker 1: Yes. That's all good. The options need to change too. That's the part. So that that's the overview that will show on the card.\u003C\u002Fp>\u003Cp>Cool. Whatever. It's the options we care about. That's the pain that comes out and you select all the options. So in here, the field, the field will be called snake.\u003C\u002Fp>\u003Cp>And that's what corresponds with the snake in the overview. That's the friendly name. It will still be a string. It'll be full of input. Great.\u003C\u002Fp>\u003Cp>Save. Now we'll go over to API dot JS, and there's text here. We'll change text for snake. So that says taking in the snake value and now we're going to execute some code. But before we do this, I think that's everything on the UI side done.\u003C\u002Fp>\u003Cp>We want to initialize this client and we'll want to initialize this client between lines 13. So everything in your yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Great. So, we're going to ignore this because we're not actually going to I'm actually I'm interested to see how this works. Once we have done this, we're going to change some things up eventually. But we're not gonna worry about that right now. So let's grab\u003C\u002Fp>\u003Cp>Speaker 1: Oh, which was straight upon commenting everything. That's that's crazy.\u003C\u002Fp>\u003Cp>Speaker 0: I mean, nothing could go wrong because nothing's really running yet. So this is good. This server is gonna change. I'm gonna leave these. I don't actually know if we need these, but I'm not gonna question it.\u003C\u002Fp>\u003Cp>Speaker 1: If it works before, our goal is to take what worked and apply it here. So we're not gonna make any changes to the logic here.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Exactly. So we are initialized there now, and now we need to pull all of this stuff up above. And I think we're not gonna need that get cached direction elsewhere.\u003C\u002Fp>\u003Cp>Speaker 1: I don't think we wanna necessarily play with that right now. And at the very least, we wanna make sure that we can get data from dev cycle in a flow Exactly. And then pass it on. But if you were having an issue where it's taking a little while to do that round trip, there's a pretty good chance that we're gonna hit that here too.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So let's go here. Awesome. Alright. So here we go, dev cycle clients.\u003C\u002Fp>\u003Cp>So we're established here. We've got this cash direction. Again, I don't actually think we need this anymore because we're gonna be calling it sort of once to determine what's gonna change. We're not gonna be calling this again and again and again. Right?\u003C\u002Fp>\u003Cp>We're not gonna be calling this every move. Is that our that's our focus. Right?\u003C\u002Fp>\u003Cp>Speaker 1: I mean, we were. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Do we we were gonna call it every move? Oh,\u003C\u002Fp>\u003Cp>Speaker 1: this is all in the wrong endpoint. This actually wants to go in the start game, doesn't it? Once you're in the start game and then for the game, we determine the strategy and then we execute on it. This is okay. Let's keep rolling now.\u003C\u002Fp>\u003Cp>And then before we've done it with yeah, exactly. Precisely. So let's really let's let's keep this light. In fact, I think doing everything in a separate function may not even be what we Exactly. Want too much here.\u003C\u002Fp>\u003Cp>Speaker 0: So I'm gonna pull all of this out of this update cache directory.\u003C\u002Fp>\u003Cp>Speaker 1: Hang on. Hang on. I think the only bit we care about is this dev cycle client variable.\u003C\u002Fp>\u003Cp>Speaker 0: I think\u003C\u002Fp>\u003Cp>Speaker 1: that's the only bit we want.\u003C\u002Fp>\u003Cp>Speaker 0: We also care about this user thing. So that's\u003C\u002Fp>\u003Cp>Speaker 1: where that's going to snake. Yeah. But that's going to that's already that's coming in from the snake value above. So we're gonna get to feed that in.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. Yes. Yep. I guess it has to feed a very specific way. It has to be a very specific object that's feeding in, but maybe you're right.\u003C\u002Fp>\u003Cp>Let's let's see. Let's see. I think I think\u003C\u002Fp>\u003Cp>Speaker 1: it'll be okay. So so if we if we go down to where the actual flow handler is, which I think is down. So inside of the handler, instead of the console log, snake is a string. So what we want to do here is we want to make on line 41, we want to make this an async function. Right?\u003C\u002Fp>\u003Cp>Yep. No. As in as in we wanna change just before the the snake. Oh, I see. We wanna make it async, then we'll just use a sync yeah.\u003C\u002Fp>\u003Cp>One more to the left. One more to the left.\u003C\u002Fp>\u003Cp>Speaker 0: Goodness gracious. And we're gonna get rid of you. You don't need to be there.\u003C\u002Fp>\u003Cp>Speaker 1: And then and then inside, what we wanna do is is run that run that SDK method, but we can do it with a wait instead of then just to clean it up in this context. So if you scroll just very slightly up oh, yeah. We'll ditch something small. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: We're just gonna do this. We're not gonna need to do this call. Perfect. Let's just work the day.\u003C\u002Fp>\u003Cp>Speaker 1: Grabbing this. Yeah. But only 17 18 to start.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, yeah. You're right. Yep.\u003C\u002Fp>\u003Cp>Speaker 1: Because we're doing a sync await.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. Yep. Absolutely.\u003C\u002Fp>\u003Cp>Speaker 1: So we want that. That goes straight in there, within within a wait at the beginning of line 33 and a store this in a variable. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Let's call this strategy.\u003C\u002Fp>\u003Cp>Speaker 1: Beautiful. Love that. And then user is now gonna be I mean, if you wanna grab lines 13 to 15 just to get the structure, but really it's just an object with the user ID and snake. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Exactly.\u003C\u002Fp>\u003Cp>Speaker 0: Let's see what happens. And then yeah. So we're gonna Other way. We're gonna define this above. Yep.\u003C\u002Fp>\u003Cp>We can assign this above. Awesome. And we're actually gonna feed this in. User ID here is gonna be snake. So we're gonna feed in snake here.\u003C\u002Fp>\u003Cp>Speaker 1: Snake? Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: I think that should work.\u003C\u002Fp>\u003Cp>Speaker 1: I think that should work. But we don't know what comes back in strategy yet, so we're gonna log it. Perfect. And then we're going to return strategy.\u003C\u002Fp>\u003Cp>Speaker 0: Beautiful. And that's actually add some comments there so we know it's being logged. Strategy.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, can I show you can I show you a quick tip for this, which I think you'll\u003C\u002Fp>\u003Cp>Speaker 0: really like? Love a quick tip.\u003C\u002Fp>\u003Cp>Speaker 1: So remove that remove that string in the comma from the console log. So it's just the word strategy again. Yep. And then highlight all of the word strategy, just the word strategy, and then surround them with squiggly brackets. And now that is an object where the key is strategy and the value is strategy.\u003C\u002Fp>\u003Cp>So when it logs, you get strategy, colon. Yeah. It will be an object. So that is just a real shorthand if you if you just want to quickly tell what something is and you have just a variable key.\u003C\u002Fp>\u003Cp>Speaker 0: You know, sometimes I enjoy JavaScript, but only when people that enjoy JavaScript are doing JavaScript with me. Okay. So technically, this should this should do all of the things that we want it to do, I think.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And\u003C\u002Fp>\u003Cp>Speaker 0: so this is wanna change this to hold on. Oh, sorry. Go ahead, Kevin.\u003C\u002Fp>\u003Cp>Speaker 1: No. No. No. Go ahead.\u003C\u002Fp>\u003Cp>Speaker 0: So I'm just saying here so here, we're actually gonna go and create an so this is a direction variable. We're gonna create a strategy variable and kind of put 2 strategies. We're gonna do, like, hungry and tailchase or something like that.\u003C\u002Fp>\u003Cp>Speaker 1: I mean, we we're not gonna implement those. So why don't we do left or\u003C\u002Fp>\u003Cp>Speaker 0: right? That is a very smart decision, Kevin. So let's head back over to dev cycle here, and we're gonna get out of there, go into our features again. Into our choose move, it doesn't really matter again what this feature is called. We're gonna create a new variable.\u003C\u002Fp>\u003Cp>We're gonna call this strategy. We're gonna set this as a strict technically, we could just do Boolean, but we'll do strategy. And because of the way that we have this set up with, like, these 4 different options of left, right, up, down, we're gonna actually go ahead and kind of move this around. So we're gonna call it, this one's gonna be left, and this one's gonna be right. Actually, we'll just leave it all exactly the same.\u003C\u002Fp>\u003Cp>Down, up.\u003C\u002Fp>\u003Cp>Speaker 1: But it's just called strategy this time.\u003C\u002Fp>\u003Cp>Speaker 0: It's called strategy now. Yeah. Sure. Alright. Amazing.\u003C\u002Fp>\u003Cp>Look at that. We changed literally nothing. I love that I did a thing that was completely unnecessary. We could\u003C\u002Fp>\u003Cp>Speaker 1: have just kept the stream.\u003C\u002Fp>\u003Cp>Speaker 0: Just kept the same.\u003C\u002Fp>\u003Cp>Speaker 1: That's fine. That's fine. We we okay. So we're logging this and we I think I think this works, but what we want to do here is is\u003C\u002Fp>\u003Cp>Speaker 0: what do we\u003C\u002Fp>\u003Cp>Speaker 1: wanna do here? We now wanna hook this up to a start.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. So we need to do we need to do a new end.\u003C\u002Fp>\u003Cp>Speaker 1: And we need to change the data model to, have this extra, like, strategy string, which I think is amazing because then even in our insights dashboard, we can be like, when using this strategy, this is the win rate. Like, it's actually really lovely.\u003C\u002Fp>\u003Cp>Speaker 0: Now we're done in code for right now, though. Right? So I think that we can No. We're not?\u003C\u002Fp>\u003Cp>Speaker 1: Build this one. Build this extension. I think we're gonna need to change which flow is being hit on start. I think we need to create a new flow. We need to create a hang on.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, metadata.\u003C\u002Fp>\u003Cp>Speaker 1: Hang on. Hang on. Is this are these errors or are these suggestions? I think this is all about and has been rewritten. I have a sneaking suspicion this is inside of the SDK.\u003C\u002Fp>\u003Cp>There\u003C\u002Fp>\u003Cp>Speaker 0: might be no module in\u003C\u002Fp>\u003Cp>Speaker 1: But it said done.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. This may\u003C\u002Fp>\u003Cp>Speaker 1: And it says and has been rewritten. All of these have resolutions. I've not seen the error before. I have a sneaking suspicion it's in the dev cycle and the way we're rolling up and bundling code, not a problem. If it works, it works.\u003C\u002Fp>\u003Cp>Not sure if it works. We'll figure that out. So what we actually wanna do is create now another\u003C\u002Fp>\u003Cp>Speaker 0: Flow. Flow. Do we need to create another item? No.\u003C\u002Fp>\u003Cp>Speaker 1: No. We don't. We can alter our existing start endpoint. Yeah. So yeah.\u003C\u002Fp>\u003Cp>But you've built this. You need to restart Directus.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Okay. There we go. We're gonna restart Directus. Oh, I need\u003C\u002Fp>\u003Cp>Speaker 1: there we\u003C\u002Fp>\u003Cp>Speaker 0: go. Let's restart here. Oh, wrong one. Just ignore the fact that we're rebuilding it again. Ignore all of those errors, everybody, and go to the right ones.\u003C\u002Fp>\u003Cp>Speaker 1: To me to me, they read as warnings. That's the spirit.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. And that should be running again, and we're gonna close that down. We're gonna get the right thing open now. And I think we were here. Let's mark\u003C\u002Fp>\u003Cp>Speaker 1: this out. Absolute chaos. I literally can't get behind your window management.\u003C\u002Fp>\u003Cp>Speaker 0: There is no there is no window management that is occurring. There is nothing. So,\u003C\u002Fp>\u003Cp>Speaker 1: there is one other thing we want to do just before we hook all this up, which is we want to go into the data model. And for a gain, we want to, just add a string strategy.\u003C\u002Fp>\u003Cp>Speaker 0: Right?\u003C\u002Fp>\u003Cp>Speaker 1: Yep. Yeah. Lovely. And then just because it makes can you make it half width, please? Sure.\u003C\u002Fp>\u003Cp>You use you you could do it with the little 3 dot things on the side come out of this pane, the little 3 dot thing, and then half Got it.\u003C\u002Fp>\u003Cp>Speaker 0: Got it. We got there. Alright. So that's in there now. Awesome.\u003C\u002Fp>\u003Cp>Let's head back into flows. Oh, that's not the right spot. Back into flows, and we're gonna change this one. I wanna change the name here before I forget. Can I change that?\u003C\u002Fp>\u003Cp>Yeah. I can.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. Back here. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Sneak strategy. Awesome. Nice. Perfect. And\u003C\u002Fp>\u003Cp>Speaker 1: then, yes, start. And now we want to see if this new thing can so we extract the snake count, and then we create data. And we basically want to create one there in the middle, which hopefully won't pose too many issues for us.\u003C\u002Fp>\u003Cp>Speaker 0: Let's build\u003C\u002Fp>\u003Cp>Speaker 1: So no. No.\u003C\u002Fp>\u003Cp>Speaker 0: Not on\u003C\u002Fp>\u003Cp>Speaker 1: the on the success path.\u003C\u002Fp>\u003Cp>Speaker 0: Great. And what do we do in our custom operation? Right?\u003C\u002Fp>\u003Cp>Speaker 1: Yes. There is at the bottom.\u003C\u002Fp>\u003Cp>Speaker 0: Look at that. It even says dev cycle. I think that's a good name to keep for it. I like that. I also like that it converts camel case into, what's what's this?\u003C\u002Fp>\u003Cp>What's the\u003C\u002Fp>\u003Cp>Speaker 1: that is called I wanna say hang on. I will I will tell you this because I actually have it inside of my raycast case. That is called that is called, coming up, camel case.\u003C\u002Fp>\u003Cp>Speaker 0: That's camel case? This is camel case.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, no. Sorry. Sorry. That is I'm I'm I've picked a ridiculous string to test against. So it had, like, it had, like, hang on.\u003C\u002Fp>\u003Cp>That is called, scrolling through, Pascal. Is it snake or is it Pascal case? Snake case. It's called snake case.\u003C\u002Fp>\u003Cp>Speaker 0: What is snake? Enough. That's a case. There we go. That is\u003C\u002Fp>\u003Cp>Speaker 1: appropriate.\u003C\u002Fp>\u003Cp>Speaker 0: It's almost like I knew this, and I wanted to bring it up. Okay. And for a sneak ID, we can actually put in anything. We're just\u003C\u002Fp>\u003Cp>Speaker 1: Oh, encoded.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. We're just gonna put, like, a really basic actually, no. Let's call this ready, set, Battlesnake because there's some cool stuff you can do with your targeting rules, including include. So let's just set that.\u003C\u002Fp>\u003Cp>Speaker 1: I mean, remember, that can be a dynamic value from the first step.\u003C\u002Fp>\u003Cp>Speaker 0: It can. But for now, we'll But\u003C\u002Fp>\u003Cp>Speaker 1: we can we can yeah. Absolutely agree. So inside of the dev cycle step now sorry. Can we just go in? That was the only option.\u003C\u002Fp>\u003Cp>And so dev cycle is gonna return some data. Yeah. All of that happens in the background. So we don't know whether it works yet. So now we wanna redo the nodules.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Oh, yeah. We just wanna move this to here. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And this is where we seem to keep having a bad time.\u003C\u002Fp>\u003Cp>Speaker 0: Awesome. Thank you, Directus, for doing exactly what we were expecting you to do.\u003C\u002Fp>\u003Cp>Speaker 1: On behalf of the core team, you are very welcome.\u003C\u002Fp>\u003Cp>Speaker 0: So what is our issue in here? What is our issue?\u003C\u002Fp>\u003Cp>Speaker 1: But remember, we had this all the way from the first episode, and we just kind of wrote like, there was a specific set of moves we did, and it worked.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. That works. I think it\u003C\u002Fp>\u003Cp>Speaker 1: might have been this. I hit edit again.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And then bring it back and then bring it back. No. What if I just disconnect that altogether? K.\u003C\u002Fp>\u003Cp>That works.\u003C\u002Fp>\u003Cp>Speaker 1: We're getting there. And then and then do it again now? Edit and just do that last one?\u003C\u002Fp>\u003Cp>Speaker 0: Come on. There we go. There we go. We got it. 2, we're gonna\u003C\u002Fp>\u003Cp>Speaker 1: set of actions. There you go.\u003C\u002Fp>\u003Cp>Speaker 0: It's 1 at a time. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. So that Now we want to start a game and just see if it works. And there's a we're also going to want to look at our directors logs too because we did console log just data. But let's we need to start the game.\u003C\u002Fp>\u003Cp>Speaker 0: Let's start Then we can first. And let's grab create new gate. Oh, not create new battle sync, create game, and let's grab escape. Oops. Escape.\u003C\u002Fp>\u003Cp>Escape.\u003C\u002Fp>\u003Cp>Speaker 1: Which I now regret.\u003C\u002Fp>\u003Cp>Speaker 0: K. Add game. Is it in there? There we go. Beautiful.\u003C\u002Fp>\u003Cp>Okay. Let's see. Game is running. Something's happening. Not what we want to happen.\u003C\u002Fp>\u003Cp>Sure. That's okay. Let's head back over to Directus and see what our logs are saying.\u003C\u002Fp>\u003Cp>Speaker 1: Scroll up because, obviously, all the turns happen. Right. Strategy. Boom. We\u003C\u002Fp>\u003Cp>Speaker 0: just need to do dot value.\u003C\u002Fp>\u003Cp>Speaker 1: That's why. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: It's it's returning this. It's the yeah. Request to get variable strategy failed with the response variable not found for key strategy. Hold on. Did I not save?\u003C\u002Fp>\u003Cp>I feel like I might not have saved something over on the cycle, which is a necessary step here is to actually save it. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: That does sound necessary.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, hold on. Maybe this is not that window. Ah, see this? Save. It doesn't actually know that it's there.\u003C\u002Fp>\u003Cp>It's my own darn fault. I got too many windows open. Darn you, Andrew. Honestly, I'm pretty sure\u003C\u002Fp>\u003Cp>Speaker 1: it's the curse of academics. Every academic I've ever seen or a covering academic. I think you are though current. Correct. Yeah.\u003C\u002Fp>\u003Cp>It's it is that. So while we're here, maybe we just straight because this is a DVC variable. Let inside of the extension itself return strategy return dot value. Yeah. And then rebuild, restart.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. So we're gonna return\u003C\u002Fp>\u003Cp>Speaker 1: This is good.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. Dot value. Awesome. There we go. So that should work.\u003C\u002Fp>\u003Cp>That should do what we want it to do. Let's rebuild here.\u003C\u002Fp>\u003Cp>Speaker 1: You got it.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. We're in the right oh, that's snake. We wanna be dev cycle and not CD. Let's go npm run build. And now we're going to need to restart again.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Once again, not the case now, but I'm too I'm too nervous just just in case. The upgrade is right in this moment.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. And I need to oh, why did I do\u003C\u002Fp>\u003Cp>Speaker 1: that? Why did you do that? I\u003C\u002Fp>\u003Cp>Speaker 0: don't know. Questions questions have been asked. Okay. So let's see if we can create a rematch. Should work this time.\u003C\u002Fp>\u003Cp>Nope. Not working.\u003C\u002Fp>\u003Cp>Speaker 1: Well, it's not so much that. That's just the bit we care about because it's our new snake. What we care about is if we go back to the logs\u003C\u002Fp>\u003Cp>Speaker 0: Are these the new\u003C\u002Fp>\u003Cp>Speaker 1: No. No. No. No. The the log the logs won't tell us anything.\u003C\u002Fp>\u003Cp>Go see if the strategy has been saved against the latest game.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, that's true. Yes.\u003C\u002Fp>\u003Cp>Speaker 1: That's what we're actually testing here. Oh, no. No. No. We're not doing that.\u003C\u002Fp>\u003Cp>Sorry. We check the flow logs, the flow logs for start. This is what we're doing. And we check the latest log. We check the dev cycle payload.\u003C\u002Fp>\u003Cp>Speaker 0: Ready, set, battle, snake is the snake we're feeding in. Payload up. That's good. I should be feeding everybody up right now. Up, up, up, up, up, up, up.\u003C\u002Fp>\u003Cp>Yes. Hey. We did it. That's awesome.\u003C\u002Fp>\u003Cp>Speaker 1: Now so now we wanna store the strategy. And then in the move logic, we want to grab this next this game strategy. We want to act on it.\u003C\u002Fp>\u003Cp>Speaker 0: I love this. Okay. So we have this here. Now we need to store it in here, right? We're actually Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: We just wanna edit the create data.\u003C\u002Fp>\u003Cp>Speaker 0: Edit the create data. Oh, in here. Gotcha. Yeah. Because we're already creating it.\u003C\u002Fp>\u003Cp>Speaker 1: We just need to add\u003C\u002Fp>\u003Cp>Speaker 0: strategy.\u003C\u002Fp>\u003Cp>Speaker 1: You're missing a comma on line 3.\u003C\u002Fp>\u003Cp>Speaker 0: But I laugh at commas.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Do you\u003C\u002Fp>\u003Cp>Speaker 0: wanna hear do you wanna hear a fun do you wanna hear a fun, little story while we're doing this? I'm ready. So I, am teaching at at the university this this term. And, I was talking to my students who are all future teachers about using proper grammar in the posts that they're doing on the WordPress blogs they create. And I misspelled grammar.\u003C\u002Fp>\u003Cp>Beautiful.\u003C\u002Fp>\u003Cp>Speaker 1: Well, you can't have it all. You could have good grammar or good spelling. So so remember, the value was no not trigger. The returned value from the dev cycle step. And remember, the key was dev underscore cycle or lowercase, so not the dollar sign, was the string.\u003C\u002Fp>\u003Cp>So it is just dev underscore cycle, and the value of that will be the string.\u003C\u002Fp>\u003Cp>Speaker 0: All right. Let's see if this works.\u003C\u002Fp>\u003Cp>Speaker 1: Because that's what was returned out of that step to save. And so let's start one more game. Let's make sure that's being being stored.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, cut out there, Kevin.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, I actually just stopped talking.\u003C\u002Fp>\u003Cp>Speaker 0: I'm not sure. Oh. You you definitely froze.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, okay. Fair fair enough. Yeah?\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Good. Okay. Perfect. So we ran a game.\u003C\u002Fp>\u003Cp>Yeah. That's fine. And then let's head back over to our logs here. And the I mean,\u003C\u002Fp>\u003Cp>Speaker 1: the proof is gonna be in the pudding here.\u003C\u002Fp>\u003Cp>Speaker 0: No. So But then no\u003C\u002Fp>\u003Cp>Speaker 1: no no the options. Strategy up. So if we now go look\u003C\u002Fp>\u003Cp>Speaker 0: at the latest Oh, there we go. Yeah. We're there.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. So if so if we go just check the content module in the sidebar here in the, module bar far left, let's go look at the latest game and make sure our strategy was saved. That's the top one. Top one.\u003C\u002Fp>\u003Cp>Speaker 0: What is Games.\u003C\u002Fp>\u003Cp>Speaker 1: What what an easy quick way to do this is if you just hit the plus in the top. Like, if you add a if you add a field, perfect strategy, boom. Nice. Amazing. So now we're gonna go into our move, our new move, which does nothing, which is why our snake is doing nothing, and we wanna respond to that strategy.\u003C\u002Fp>\u003Cp>Alright. So So not this one. We we we we we we we Oh, that's right.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. We did a strategy one. That's right. Okay.\u003C\u002Fp>\u003Cp>Awesome.\u003C\u002Fp>\u003Cp>Speaker 1: Snake strategy.\u003C\u002Fp>\u003Cp>Speaker 0: And let's do this as a we're just gonna return. Right? So we just need to do\u003C\u002Fp>\u003Cp>Speaker 1: No. Because there's a few steps here. So this takes in a move, right? And that move has a game ID. So the first thing we want to do is we want to look up the game to find out the strategy, right?\u003C\u002Fp>\u003Cp>So we want to read data first and my confidence has just gone through the floss. So let's close out this for a moment. Can we just look at the logs here and just understand the shape of the data again?\u003C\u002Fp>\u003Cp>Speaker 0: All right.\u003C\u002Fp>\u003Cp>Speaker 1: So So the payload, body, game, ID. And that should match the ID of the game in our collection that has been created by the start step.\u003C\u002Fp>\u003Cp>Speaker 0: So let's see. 4ff ending in 70. Let's see if that is in fact the case.\u003C\u002Fp>\u003Cp>Speaker 1: Sure. I I think it should be. Yep. There we go. Perfect.\u003C\u002Fp>\u003Cp>So what we want to get out is that strategy. So, so we'll create a new step here, and we can call this get game or something.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Get game strategy. Let's call it.\u003C\u002Fp>\u003Cp>Speaker 1: I'll just get this is gonna get the whole game. So let's just get game. And then permissions full access because I can't be bothered to deal with permissions, collection, games, IDs. Now this is important. We want in squiggly brackets, double squiggly brackets here, trick, dollar sign trigger dot body\u003C\u002Fp>\u003Cp>Speaker 0: Body. Yeah. Dot game\u003C\u002Fp>\u003Cp>Speaker 1: ID. Game. Yeah. You got it. Dot ID.\u003C\u002Fp>\u003Cp>Yeah. And hit enter. And hitting enter just slightly, you know, you have to enter. And just like you're hitting save over there in dev cycle, that's one that gets me.\u003C\u002Fp>\u003Cp>Speaker 0: There we go.\u003C\u002Fp>\u003Cp>Speaker 1: And this should get the game. So hit save. Let's see. And let's just do this very sequentially. So, yes, let's let's test this again.\u003C\u002Fp>\u003Cp>Once again, the value should absolutely be up.\u003C\u002Fp>\u003Cp>Speaker 0: Perfect. Options.\u003C\u002Fp>\u003Cp>Speaker 1: Permission to pull\u003C\u002Fp>\u003Cp>Speaker 0: the games ID. Wonderful. Wonderful. And we get our strategy.\u003C\u002Fp>\u003Cp>Speaker 1: Lovely. And the strategy now is what we're gonna check. So now we're gonna do some branching logic. So what we want is, I think, a conditional.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. Right? Yep. And we're going to call this strategy.\u003C\u002Fp>\u003Cp>Speaker 1: Strategy. Well, let's be more specific because we're gonna check if it is one strategy or the other. So Oh, that's true. Is, you know, strategy, yeah, is up. Although, was was up the value yeah.\u003C\u002Fp>\u003Cp>Yeah. Off was the value.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It's up the value.\u003C\u002Fp>\u003Cp>Speaker 1: It's up. Yeah. Perfect. So let's fill in the so I don't think we do wanna fill in the default. Yeah.\u003C\u002Fp>\u003Cp>So squiggly like, this is JSON now. So squiggly brackets on the outside.\u003C\u002Fp>\u003Cp>Speaker 0: There we go. I love this, by the way. This is such a smart little\u003C\u002Fp>\u003Cp>Speaker 1: Except we are gonna have to go edit it. So I know. It's all it's. So trigger change for last because we wanna grab stuff from the last step. Oh, sorry.\u003C\u002Fp>\u003Cp>A dollar sign. Yeah. Last. Perfect. Yeah.\u003C\u002Fp>\u003Cp>Correct. And we're gonna be checking the strategy. So not category, strategy. Yeah. And when you see whether it is equal to up and it's case sensitive.\u003C\u002Fp>\u003Cp>So yeah. Perfect. Wonderful. And hit save.\u003C\u002Fp>\u003Cp>Speaker 0: Now, honestly, this is where this is where\u003C\u002Fp>\u003Cp>Speaker 1: I mean, nothing would oh, yeah. Suppose we can check. We can check that.\u003C\u002Fp>\u003Cp>Speaker 0: Kevin, let's follow, strategy every time we do this, but, no, we won't follow the strategy. Let's see.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, wait. Wait. Wait. Nothing it didn't get saved.\u003C\u002Fp>\u003Cp>Speaker 0: What?\u003C\u002Fp>\u003Cp>Speaker 1: Didn't save the flow. You just refreshed.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, did I really?\u003C\u002Fp>\u003Cp>Speaker 1: No. Womp. Womp. It's okay. Come on, Andrew.\u003C\u002Fp>\u003Cp>Speaker 0: Come on. I mean, to be fair to\u003C\u002Fp>\u003Cp>Speaker 1: be fair, there is a little bit of allowing you to do that without prompts. Anyway, anyway, is up Yep. Was the name of the condition, and it's gonna be lost. Last. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: And you call them strategy.\u003C\u002Fp>\u003Cp>Speaker 1: You call it.\u003C\u002Fp>\u003Cp>Speaker 0: And oh, strategy, and up, and save and save. Up. It's the double save.\u003C\u002Fp>\u003Cp>Speaker 1: I get it. I get it.\u003C\u002Fp>\u003Cp>Speaker 0: Let's set out. Let's finish this game. And so I figured out the thing that it is here. You actually have to finish the game before it'll let you do a rematch. You have to play the whole game.\u003C\u002Fp>\u003Cp>That was the issue we were facing.\u003C\u002Fp>\u003Cp>Speaker 1: Good tonight.\u003C\u002Fp>\u003Cp>Speaker 0: So we should have it showing up now. Yes. He's up. Payload. Yep.\u003C\u002Fp>\u003Cp>Awesome. Is\u003C\u002Fp>\u003Cp>Speaker 1: up. Now, this basically will fail if it isn't up. It will it will, like, it will have a validation error, and the whole flow will fail. And this is correct.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Let's see. Look at you go. All right. Here we go.\u003C\u002Fp>\u003Cp>And amazing. Alright. And let's go back over to directors refresh. Look at our logs. Shut up a bunch more.\u003C\u002Fp>\u003Cp>Let's go. Oh, oh, something's happening. Oh, I need to actually press the button. Alright. Here we go.\u003C\u002Fp>\u003Cp>Oh, option payload validation error.\u003C\u002Fp>\u003Cp>Speaker 1: Validation error. Wonderful. And so this is where, we have to be creative with flows and work within some of the limitations, to be frank. So because all all conditions, there is no, like, case statement where you can be like a, b, or c. It is true or it is false.\u003C\u002Fp>\u003Cp>It is resolved or it is reject. Right now, our flow simply halts because it goes out that reject path and fails. Right? But what we're gonna do now is we're just going to put, you know, we're going to just put one thing on the reject path, but it isn't failed. It's just the else.\u003C\u002Fp>\u003Cp>Right? So it's just an interesting terminology thing there. So we don't, we don't we don't even need this now.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, I guess Unless you wanna do can be Well, why don't you wanna do it? Yeah. Let's just do all 4. There's only 4 to do. So\u003C\u002Fp>\u003Cp>Speaker 1: So hang on. Hang on a moment. Have you put you've put this on the reject path. Right? Yes.\u003C\u002Fp>\u003Cp>Perfect.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. Okay. Last strategy down, and we're gonna call this is down. Perfect. Yes.\u003C\u002Fp>\u003Cp>And then we're gonna create another.\u003C\u002Fp>\u003Cp>Speaker 1: Off the reject path. Off the reject path. So it gets a little bit unwieldly here. So it's not up. It's not down.\u003C\u002Fp>\u003Cp>Speaker 0: Strategy. Let's call this last.\u003C\u002Fp>\u003Cp>Speaker 1: Copy it out this time before you hit save.\u003C\u002Fp>\u003Cp>Speaker 0: Look at you, left. Perfect. Copy. And then one more.\u003C\u002Fp>\u003Cp>Speaker 1: So you so you see here how these kind of more convoluted case statements can get a little bit messy in here?\u003C\u002Fp>\u003Cp>Speaker 0: Yep. I definitely do. But, is right. Okay. So let's go there.\u003C\u002Fp>\u003Cp>Save this. And go to the game. And let's finish this game. Shouldn't take too long.\u003C\u002Fp>\u003Cp>Speaker 1: And this one should do the same thing. It should have a validation error after the first one.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Alright. Oh, yeah. That's true. Oh, no.\u003C\u002Fp>\u003Cp>Speaker 1: See if\u003C\u002Fp>\u003Cp>Speaker 0: it does. No. No. No. No.\u003C\u002Fp>\u003Cp>Speaker 1: That's a lie. Sorry. It will it will go until it fails. Just ignore me. That was incorrect.\u003C\u002Fp>\u003Cp>Sorry.\u003C\u002Fp>\u003Cp>Speaker 0: Is up. So that was our first one. Is up failed. Must be up, and then it dot stopped.\u003C\u002Fp>\u003Cp>Speaker 1: That's not correct.\u003C\u002Fp>\u003Cp>Speaker 0: Did I do is up twice? Is left, is up, is down.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, no. No. It's connected to read data. It needs to be connected to the reject path of is up.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, there we go.\u003C\u002Fp>\u003Cp>Speaker 1: Not that one. There we go.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Yeah. There we go. Oh, did too much stuff.\u003C\u002Fp>\u003Cp>Disconnect, save, edit, reconnect.\u003C\u002Fp>\u003Cp>Speaker 1: Oops. I'm glad. I'm no. No. No.\u003C\u002Fp>\u003Cp>Reject. Reject. Yeah. I'm glad we finally got back to that point of just finding that little that little set of actions. I can't believe it got us so, so much.\u003C\u002Fp>\u003Cp>But, yeah, I've submitted\u003C\u002Fp>\u003Cp>Speaker 0: that as a bug report. Awesome. Alright. Let's refresh this. And we should be now we should be seeing the process that you were hoping for.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Lots more moves there. So we should be seeing is oh, yeah. Here we go. Awesome. Yeah.\u003C\u002Fp>\u003Cp>It's up. Validation error. Validation error, validation error, validation error.\u003C\u002Fp>\u003Cp>Speaker 1: Why is there a validation error on all 4? It has to be one of them.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, last must be of type object? I wonder if that is not Oh,\u003C\u002Fp>\u003Cp>Speaker 1: of course. Last is the last step. We've foot gummed by using last, which I actually spoke about in a previous episode. What we wanna check is, can you click into read data? The read data Yeah.\u003C\u002Fp>\u003Cp>Key is called get game. Right? So let's maybe rename this game. So if you go up to the key in the top right of this whole pane, scroll up. Yeah.\u003C\u002Fp>\u003Cp>Yeah. And the last will always be the one that came before it. But in this case, we're chaining a bunch. So, like, changes. So we actually wanna change.\u003C\u002Fp>\u003Cp>So if we change this to just game, let's keep it nice and short.\u003C\u002Fp>\u003Cp>Speaker 0: Perfect.\u003C\u002Fp>\u003Cp>Speaker 1: And and, yeah, save. And now we could do it in is up. The is up last is correct, right? But in all the others, it isn't lost anymore.\u003C\u002Fp>\u003Cp>Speaker 0: And then we're just removing this, and it's actually just gonna be game. Right? No Correct.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Correct.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So let's go No,\u003C\u002Fp>\u003Cp>Speaker 1: dollar sign.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Or no dollar sign. Yeah. There we go. And let's call this one game.\u003C\u002Fp>\u003Cp>Oh, game. And copy that this time. And I'm quite impressed with where we've gotten here. I feel like episode 4 finally figured things out.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Alright. Here we go. So Yeah. So now so so let's test that once more.\u003C\u002Fp>\u003Cp>And while we're doing that, let's talk about what's next. Because all we're gonna do is execute the most basic strategy, which is to just return, move up, down, left, right. And we're not gonna do anything more fancy here. Right?\u003C\u002Fp>\u003Cp>Speaker 0: You would implement\u003C\u002Fp>\u003Cp>Speaker 1: is up, not correct. Is down though, is correct, which Awesome. Great. So now out of each of these success paths, now we need to run the strategy, which of course could be a lot more convoluted. Another thing that's worth noting is there is a flow operation, which is to run another flow.\u003C\u002Fp>\u003Cp>So you could you could kind of modularize your strategies into separate flows, go off and call those those come back with the response. And then so, you know, there are ways of organizing your flows. It's not just one big mega flow. But it's I think for what we're doing, it's beyond the scope, but that that would kind of be how you organize game logic, I think.\u003C\u002Fp>\u003Cp>Speaker 0: Great. Okay. So what are we gonna do here? What are we we're sending this\u003C\u002Fp>\u003Cp>Speaker 1: to We are literally gonna return. So run script. So sorry. I run script was near the top. And the reason we're doing run\u003C\u002Fp>\u003Cp>Speaker 0: script just to move and then up and down and left. You got it.\u003C\u002Fp>\u003Cp>Speaker 1: That's awesome. You got it. Because in the trigger, we said respond with the last operation. Yeah. I love that.\u003C\u002Fp>\u003Cp>Speaker 0: All right. So let's just grab that, save, and let's do oh, yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Then that's correct. Yep. That's correct. So that's the is up success path. The names and the keys no longer matter, because If\u003C\u002Fp>\u003Cp>Speaker 0: we unless we forget which one where it is down, so this one's gonna be down. Save that, and then we're gonna go here. This is left.\u003C\u002Fp>\u003Cp>Speaker 1: I'm glad you you've come with me on the up, down, left, right rather than the clockwise.\u003C\u002Fp>\u003Cp>Speaker 0: We got there. We got there eventually. We got there. Alright. And then our final one is gonna be right here.\u003C\u002Fp>\u003Cp>And then this one's gonna be run script and\u003C\u002Fp>\u003Cp>Speaker 1: return You got it.\u003C\u002Fp>\u003Cp>Speaker 0: Move right. And let's see if this works. So if this works, our move right now is down. So snake should go down if everything is done the way it's supposed to be. So we've been going up because it's been failing.\u003C\u002Fp>\u003Cp>Now we should go down. Alright. This doesn't necessarily mean it's working yet because let's see if we go right. Save. Did we just actually implement this in, like, an effective way?\u003C\u002Fp>\u003Cp>This is wild. I'm, like, kind of\u003C\u002Fp>\u003Cp>Speaker 1: Hang on. Hang on. Hit play first.\u003C\u002Fp>\u003Cp>Speaker 0: We don't know yet. Yeah. What is up?\u003C\u002Fp>\u003Cp>Speaker 1: Snake went right straight off the board and died.\u003C\u002Fp>\u003Cp>Speaker 0: This is fantastic. I have never been so happy to see a snake die. I mean, last episode, I was really happy because we really wanted our snake to die. But, like, I'm\u003C\u002Fp>\u003Cp>Speaker 1: Oh, yeah.\u003C\u002Fp>\u003Cp>Speaker 0: I'm far more happy. That's pretty freaking cool.\u003C\u002Fp>\u003Cp>Speaker 1: That is cool. But there was one final part, and then I think we're we we can we can call it on ReadySet Battlesnake, at least season 1 of ReadySet Battlesnake, who knows, which is right now we are saying all users that there is no segmentation. There are no different audiences. It's just everyone gets one response. But in the flow creation, in this start game, you know, flow, we can put in a dynamic value there.\u003C\u002Fp>\u003Cp>Now, I do not know how this works in dev cycle. Is this something that's viable?\u003C\u002Fp>\u003Cp>Speaker 0: Yep. Yeah. Yeah. So hold on. Let's go back over and actually look at this.\u003C\u002Fp>\u003Cp>Because you said words and I heard the words, but then I stopped hearing the words. And so let's, let's go and shift this in that flow. And so we could do it in our in our dashboard. Right? We could actually\u003C\u002Fp>\u003Cp>Speaker 1: We we we wanna do it in both. So here in the start, right, in the dev cycle operation that we created, if you go in and edit it, you see we put in our as fixed snake. So the question here is right now we're getting I think there's a step missing here, which is we want to register users.\u003C\u002Fp>\u003Cp>Speaker 0: No. No. You don't actually so you don't technically need to register them there. Oh, I've gone all blurry. Hold on.\u003C\u002Fp>\u003Cp>Hold the phone. Why have I gone all blurry? What's happening? There we go.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. I'm back.\u003C\u002Fp>\u003Cp>Speaker 0: I'm back. Okay. So what we can do here is this is all just stuff used for targeting rules. So let's say, okay. So you know how if it doesn't see anything, it goes up.\u003C\u002Fp>\u003Cp>Right? Correct? Not right. I shouldn't say right. If if it Yeah.\u003C\u002Fp>\u003Cp>If it's not seeing anything, it's gonna do that. So let's leave this as ready, set, Battlesnake and go over to the platform here Yes. And say Yes. Go, let's say go left, but only if the user ID contains ready.\u003C\u002Fp>\u003Cp>Speaker 1: But but what I'm wondering is, can we get this to a spot? And, like, we can assume the outcome here, like, we'll we'll we'll do this. But what I'm wondering specifically is can we, in 10% of games, maybe this is about the game ID and not the snake ID. So maybe the the name of the property isn't quite right. But can we say in 10% of games or 10% of users, I suppose, can we change it?\u003C\u002Fp>\u003Cp>So in that case, when we first feed it the snake ID, which will make the game ID, is that can we do that?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It's just this. You literally set it to random distribution, and we say that we wanna serve 25% each of these distributions, and it will automatically do this for all users.\u003C\u002Fp>\u003Cp>Speaker 1: So we don't need to do anything with this passing a random name or anything? No. I mean, you could do it within that. You could say only the snakes that have ready in it. Yeah.\u003C\u002Fp>\u003Cp>Exactly. Do this, you know, so you can further branch that logic down and get a real small selection of your audience. Should we remove that, the first set, the first conditional where you put ready? Yeah. Should we remove that,\u003C\u002Fp>\u003Cp>Speaker 0: I guess? Because it's kind of just do all users. Yeah. And now we're just saying that all users random distribution. A random distribution.\u003C\u002Fp>\u003Cp>Speaker 1: So if\u003C\u002Fp>\u003Cp>Speaker 0: I save this, we should see if we run, say, 10 games, we should see something similar to that. So let's make sure this works. That's good.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, that was remarkably easy.\u003C\u002Fp>\u003Cp>Speaker 0: It is very smooth.\u003C\u002Fp>\u003Cp>Speaker 1: But but there is that other thing still where, like, if we if we register, can we say, no, ignore me. That makes part of that same mistake.\u003C\u002Fp>\u003Cp>Speaker 0: All of these things are possible. If you can feed information into the platform, then you can you can do lots of stuff with it.\u003C\u002Fp>\u003Cp>Speaker 1: And we've only discussed all of these ways of feeding data in, so we don't necessarily need to go too granular. But, yeah, let's create a ton of games.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So here we go. So let's go first game. So we went left first game.\u003C\u002Fp>\u003Cp>Speaker 1: Sure.\u003C\u002Fp>\u003Cp>Speaker 0: Let's go create rematch. So we're going left. So that's 2 for left. Let's create a rematch. We should start going a different direction, and we're not.\u003C\u002Fp>\u003Cp>Maybe I didn't save. Hold on.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I was gonna say, did you hit that save button?\u003C\u002Fp>\u003Cp>Speaker 0: All users, random distribution, schedule, none. No random distribution. We should be seeing that. That. All users feeding that in.\u003C\u002Fp>\u003Cp>25%. K. Let me change this. I just wanna see if something happens. A 100%,\u003C\u002Fp>\u003Cp>Speaker 1: 0. Oh, you you you have to make it worth what happens if it exceeds a 100 there?\u003C\u002Fp>\u003Cp>Speaker 0: I don't I don't know. I'm curious. Yeah. Yeah. So we should see this.\u003C\u002Fp>\u003Cp>Like, all users should ever run a 100% should be there. Random distribution might require something else, but let's see.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Maybe.\u003C\u002Fp>\u003Cp>Speaker 0: No. No. That's right. Yeah. I guess\u003C\u002Fp>\u003Cp>Speaker 1: if I may.\u003C\u002Fp>\u003Cp>Speaker 0: It must just be the logic that's being used in there. So let's do 5050, and that might be a little bit easier. 50 right, 50 down, and see if that Sure. Does it. That's there.\u003C\u002Fp>\u003Cp>It also does say a couple minutes to update. So let's see.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Maybe that's it. Maybe there's a little cache. Which is fine.\u003C\u002Fp>\u003Cp>Speaker 0: Right.\u003C\u002Fp>\u003Cp>Speaker 1: As I say, our snakes aren't spawning in particularly in particularly good spots to live. They're not? This isn't feeling unless it is really that thing because you you don't need feature flags to be literally like sub second. Know, there can be a little bit of a of a moment there.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So this is over, like, a longer period of time. So that might be part of it, but we should see a down eventually,\u003C\u002Fp>\u003Cp>Speaker 1: because\u003C\u002Fp>\u003Cp>Speaker 0: the logic that's there is technically correct. Oh, you know what it is? No. I'm wrong. Here's the thing.\u003C\u002Fp>\u003Cp>I I totally forgot about this. So because it feeds in user ID and we're feeding in the same user ID every time. So if we wanted to change this, it's actually not that bad. So that So this is\u003C\u002Fp>\u003Cp>Speaker 1: actually what I wanted. Yeah. Yeah. So users and that makes sense because in the real world, you have a feature flag. You don't want that user getting a random thing every time they reload.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So ready, set, battle, snake was getting right after Oh, no.\u003C\u002Fp>\u003Cp>Speaker 1: No. No. No. Hang on. Hang on.\u003C\u002Fp>\u003Cp>Go back. There's a there's a better value to put in here. Let's put in the game ID. Let's put in the because that's random. That's in every new game is a new is a new That's true.\u003C\u002Fp>\u003Cp>User in this context. So actually\u003C\u002Fp>\u003Cp>Speaker 0: grab game.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Double double, double brackets first, and it will be trigger dot body dot Game\u003C\u002Fp>\u003Cp>Speaker 0: dot ID.\u003C\u002Fp>\u003Cp>Speaker 1: Game dot ID because that's a random value each time. Yeah. It is. That's good. Yeah.\u003C\u002Fp>\u003Cp>Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: That might that might do it. So we should now see 5050 if it works, which it may not.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm. Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: K? Good. So right on the first one, right on the second one. It's not looking good so far.\u003C\u002Fp>\u003Cp>Speaker 1: It's not looking good so far, is it?\u003C\u002Fp>\u003Cp>Speaker 0: Oh, there we go. Hey. Hey. There we go. So So\u003C\u002Fp>\u003Cp>Speaker 1: that logic that I think that's Andrew, I I I'm I'm both really happy that we got this to work, But I'm also kind of sad because I think I think that might bring us. What do you think?\u003C\u002Fp>\u003Cp>Speaker 0: I think I think we have we have eaten into to your evenings for many, many dates.\u003C\u002Fp>\u003Cp>Speaker 1: Not for that reason. Not for that reason, but just I think that's I think anything more now is us just in hot like, doing things we've already done, but in this slightly altered context, but that's awesome. So you can really, like,\u003C\u002Fp>\u003Cp>Speaker 0: yeah. So smooth. Yeah. I gotta say, like, I when we set out to do this, I knew that we might be able I knew Directus would work. Like, that made sense.\u003C\u002Fp>\u003Cp>But when we decided that I was gonna take the lead for the final show and we were gonna do the dev cycle, I was like, I don't know if this is gonna actually work because I tried to implement dev cycle with Directus a lot of ways. So I'm over the moon that this works. And, like, what a cool piece. And it's so fast too. Right?\u003C\u002Fp>\u003Cp>You're not having to worry about direct us doing anything. It's all managed externally. Like\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And and part of this is an approach that we chose. And this is a very battle snake kind of thing. It is an approach we chose to make sure that we are we're not combating that 500 millisecond return require return requirement. And that is because we determined a strategy at the beginning of a game, not every move.\u003C\u002Fp>\u003Cp>So there is a couple more round trips within directors. We're having every move to go and figure out the game strategy, but this is all internal. These are just little database operations. That's awesome. And then, of course, going back to the last episode, we could now enrich our insights dashboards and saying when this was the strategy, this was the outcome.\u003C\u002Fp>\u003Cp>We could use the little global variable drop down to to pick. We could even go off to dev cycle, populate directors with all viable strategies, That was awesome. That was fun. Happy?\u003C\u002Fp>\u003Cp>Speaker 0: This was a fun day. I'm I'm so Kevin, I was so it's been a couple episodes since I've been, like, on the keyboard. And, like, you were walking away so pumped after every and I was happy. I was definitely feeling it. But, like, I'm having a good this was a good It's good.\u003C\u002Fp>\u003Cp>This was a good day. We got we got things working, and our snake does the things, and other people can now go and use this to meld the world's all the d's, Directus, Devcycle.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. What what do you say, as for as for Directus as a Battlesnake engine?\u003C\u002Fp>\u003Cp>Speaker 0: I love it. I think it's super, super smooth. And I love the fact that there's actually, some code that exists out there for building a dev cycle management setup within the Directus platform. So, like, like, use Directus and then implement dev cycle and then play Battlesnake. That's the real that's the real goal here.\u003C\u002Fp>\u003Cp>Do Everybody wins. Everybody wins.\u003C\u002Fp>\u003Cp>Speaker 1: Everybody wins. Everybody wins. Everyone, thank you so much for joining us. Andrew, thank you for getting on board with this idea to to, you know, have a Battlesnake video, you know, series after after a little while. Thank you audience for joining us for this limited series of Ready Set Battlesnake.\u003C\u002Fp>\u003Cp>If you want to learn more about Battlesnake as with all of these episodes down in the resources, you'll find a link to the docs for Battlesnake. It is such a satisfying and fun hobby project for developers at all stages of their career, whether you're just getting started or you are really pushing, you know, your very, very advanced knowledge and it becomes an optimization game. There's something in it for everyone. I'm glad they all worked. Honestly, there were so many points in this where we might have discovered this doesn't work irreparably and the whole thing's ruined.\u003C\u002Fp>\u003Cp>And every barrier we came against, there was a solution primarily actually through the ability to build extensions. Yeah. So that is wonderful. Oh, lovely. And a melding of my world as a big time and long time battlesnake lover and running dev rel, at, director.\u003C\u002Fp>\u003Cp>So Andrew, once again, thank you so much.\u003C\u002Fp>\u003Cp>Speaker 0: You're welcome. And should we do it our one last try? This is our last opportunity? So\u003C\u002Fp>\u003Cp>Speaker 1: here we go.\u003C\u002Fp>\u003Cp>Speaker 0: What do you mean? What do you mean? You have little faith? This is it. You have a So here we go.\u003C\u002Fp>\u003Cp>What do\u003C\u002Fp>\u003Cp>Speaker 1: you mean? What do you mean? You have little faith. This is it. You ready?\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Here we go. Alright. Do you want to count it down? Should we count it down first?\u003C\u002Fp>\u003Cp>No. Hang on.\u003C\u002Fp>\u003Cp>Speaker 1: Hang on. Hang on. Hang on. I've got it. Uh-uh.\u003C\u002Fp>\u003Cp>Alright. This has been\u003C\u002Fp>\u003Cp>Speaker 0: ready. Ready. Set. Set. Battlesnake.\u003C\u002Fp>\u003Cp>Battlesnake. Bye for now. Bye.\u003C\u002Fp>","Hello, everybody. Hello. I'm sorry, Kevin. Ready yet. You are ready. Alright. Let's try this again. Alright. 3, 2, 1. Hello, everybody, and welcome to ready no. I didn't know where to do that. We're gonna do it. Ready. Ready. Set. Set. Battlesnake. I don't think we're gonna get it, Kevin. I don't think we're gonna say the final episode of Alright. We're gonna try this again. I feel like this is I feel like this is good for the final episode. Let's do this. For the final episode. Let's get this right. Welcome to the final episode of Ready set, set, battle mistake. Mistake. We're close enough. We got there. Close enough. We got there. I'm gonna count it. Alright. So welcome, everyone. Episode 4 of Ready, Set, Battlesnape. I'm Andrew. This person either down below or to my right or left. I don't know where he's he is right now. Direction. Is Kevin, and we are your your snake charmers, your code, your code coaches, your, your fun fans. I don't even know. But we are here today. You are fully overestimating our entertainment value. I I don't know what you're talking about. I feel like our entertainment value can never be underestimated. We are we are more entertaining than we even think. Thank you so much for coming back and checking this out, Kevin. Thank you so much for for coming back again after I kinda threw you, a little bit of a, a little bit of a challenge at the end of our our last episode together. It was tough. But we got there. I'm sure you'll return the favor to me today. Yeah. So if you haven't caught episodes 1, 2, and 3, you should probably go back and check those out. We, kind of we're gonna talk a little bit more about exactly what we've been doing, But, in episode 1, we built a Battlesnake, and we'll talk a little bit about Battlesnake kind of as we go through. Obviously, in episode 2, we implemented, that same logic from our 1st JavaScript starter snake in, Directus. In episode 3, we added a whole bunch more logic to our snake but mostly just to visualize the games that were coming through the platform. And today Kevin, do you know what we're doing today? I I do, but I will let you I will let you share. I think it's your turn. I think it is. I feel like you've been leading the show for the entire time. So it's probably my turn. Also, I feel like should I be let me see. I'm, like, wanting to be more centered. Oh, where are we? There we go. Now I'm feeling good. Okay. Good. Alright. I thought that, like, make make the choice to change where your camera is as you're as you're on on camera. Yeah. Screw it. Why not? Good vibes. Good vibes. So, on today's episode, we are going to be, implementing a new technology that we haven't really looked at, called Devcycle. And so, you now know from the last few episodes about what Battlesnake is. You know what Directus is because you're here on Directus TV. And so now is the time to introduce you to the technology that I work with on a daily basis as a developer advocate at Devcycle, which is a feature flag management platform. So our plan today is to try and add some logic to our, Directus snake to get this, working is maybe a relative term. Getting the the dev cycle platform yourself a lot of scope there. A lot of scope. Working is a relative term. But we're gonna use the dev cycle platform to, add a little bit of, add a little bit of flavor to our snake and give it a little bit of, give it a little bit of control. And that will make a lot more sense in a minute. But Kevin, you look like you have something to say. I I mean, I'm gonna ask the question which I think you're probably gonna work towards, which is like, can we just talk for a moment about what feature flags are and then maybe what you had in mind, and then we launch straight in. I love it. So, a feature flag, if you've never heard of them before, condition conditional statement in your code, that allows for you to, turn features on and off depending on a bunch of targeting criteria. So let's say you are working on a new feature on your platform, or a new feature maybe for your Battlesnake, and you're not quite ready to to roll that out in all of the games, you could add a feature flag. Just wrap it around the code and then through a feature management platform like DevCycle, you can say, for these kind of games, I want you to use kind of this logic. And for these kind of games, I want you to use these this logic. It's a really big part of things like trunk based development. So if you're moving from, like, branch based development where you're sort of, like, having features in branches that are long lived, and then we all had to deal with sort of, like, long lived branches and merging and dealing with merge conflicts. Mhmm. Feature flags are kind of a way to avoid those things just by constantly having your new features in code, and you can kind of decouple the code release from the feature release process. Interesting. Okay. And just to embellish this with my understanding, and you can tell me if I'm wrong, they basically work. So you put these conditional statements in your code, and what triggers those is this feature management platform. And typically, you would do that with some form of either like AB testing where people get allotted into these random buckets or you can be a bit more pragmatic and say 5% of users are gonna have this conditional turned on and therefore get a new feature. And then we will also, like, we'll analyze their behavior as a result and decide whether it works, if people get it, and so on, rather than going it's an all or nothing kind of deal. Right? You got it. You got it. Got it. That's it. Alright. Money. So Great. That is a very ambitious plan that I just laid out, by the way. I I feel as though may we get to a stage where we're able to, like, just determine, what kind of gameplay we're gonna have? That's possible. Maybe not in a 2 hour stream, but, we are gonna get Directus and Devcycle working together. How do I know they work together? Because I've already written a blog post on this separately from Battlesnake, so I got to explore. And so now is the time to use my knowledge for for good and for evil. We'll link the blog post, by the way. We'll link the blog post. There are 2. There's 1 on our blog, that Esther wrote, which uses dev cycle, and 1 on the dev cycle blog, which you wrote, which is direct us. And I'll put a link to both of them in the resources section that accompany this video. Yes. I love it. So, y'all are seeing my screen right now. And so up here, you'll notice that this looks very similar, to, 1, mister Kevin Lewis's screen, although not in the Arc browser. I'm not apparently a strong enough, software developer to use Arc. Not there yet. Once it comes to Windows, I'll start using it. It's it's it's happening. I know it's coming. I'm in a month of this. And then people will full on get this, but we're clearly recording this a day after the last one because they these are the games from the last episode they created about 23 3 hours ago, which is completely legit. We did something a little cheeky before this stream. We in a completely low rim fashion, you're running a director's to project. And I literally was like, here's a sequel database file and here's the extensions. Go run them and here's the creds I used to go log in. We may have to reconcile with that at some point when things break. So there is a chance things won't behave and we'll have to fix that on the fly, but should be okay. I love it. So we have our snake our direct to snake here. It's alive. It works. Not yet. We haven't determined if it works yet, which we probably will have to do. I also have our snake that was running on Replit. So the original, ready set battle snake that we created from the JavaScript starter snake. So Mhmm. I have that snake, but Replit was getting real mad at me today because I was using too much RAM and and compute. So I pulled that down, and put it into this wonderful, repo here. Yes, sir. And you're gonna be bumping your font size up about 5 times. No. We're gonna expect everyone to to zoom in. Exactly. Exactly. That's the look of the look at my spectacles. Everyone has. And so, yeah, let's let's zoom in here a little bit. Alright. So this is the the kind of the original snake that our code is based off. So you will notice here that not all the logic that we implemented over the past couple of episodes are in here. That's fine. This is more just to, like, show us how it's gonna work once we get there, and then we're gonna implement this indirect disk. We're gonna try and implement it in direct disk. But I think maybe the first thing we should do, Kevin, is actually get our Directus snake working. What do you think? I feel like that's a logical first step. And what we're gonna do to make that happen is we're gonna set up ngrok. And so do you pay for ngrok? I don't, but I have ngrok. So, like, I can I can set it up right now, but it'll be ephemeral? Incredibly heavily, like, rate limit to you. Let's pause here. Let's get NGROC set up and using temporarily my auth token, and then we will come back. Great. So this bit, we won't we won't publish. But, yeah, it was actually the one thing we forgot before we started. So let me just figure that out. Yeah. Throw me the key and then I can, Indeed. Alright. And let me just go to engrok now because I was actually logged in earlier. It was working for me in the games that I was playing, but, it but then it gets mad. That's the whole problem here. So what we want here is your auth token. So, here is the engrok command you are going to want to run here in the chat. Yep. Sorry. I'm just then because I'm on, like, old, the command looks slightly different, and that's there. Where are you hiding? Hold on. My windows have all disappeared. Where are you, Kevin? There you are, in chat here. Alright. Wonderful. There's a chat. Here's a command. We're gonna add this auth token. Beautiful. And we're gonna head back over here to our terminal and oh, nice. There we go. Paste. Beautiful. Auth token saved, and let's run Nice. This. It's good. I set up ng croc earlier. I knew Yeah. Yeah. Absolutely. Beautiful. Alright. Good. So I think we can actually snap back now, and Yep. It's up to you what you want on the screen. We could be here if you want. Yeah. I mean, I mean, if you've never used ngrok before, I mean, we talked about this in episode, I guess, 2. We were looking at this. I have the free version. Kevin's got one that's less rate limited, so we're using that one. But it's super easy to install. Like, just homebrew it, and then you add your auth token to ensure things are happening. But, anyways, we've got it running right now. And then my direct to snake over here is should technically be working. Now, Kevin, I'm really curious. You should technically be able to go and run a game on yours, and see if it works. And I'm interested I think Are you running directors? Apparently, I am not. I think I might have closed it. Nope. I'm running it. Yep. We're all good. Where is it running though? That's the real oh, you know it's running in another one. Do you have another I do. Versus code do. Window. There it is. Awesome. So we're gonna watch these logs here. We're gonna bump Shall we whack the font size up there as well? So I'll add ready set battlesnake and hungry bot once again to an 11 by 11 board. I'll hit start game. Yeah. And there we go. It's coming through. Awesome. We're working. And you're like, but Andrew and Kevin, we can't see the games, and therefore, this isn't fun and enjoyable for us. Us. Fear not. Because I think actually an easy way to do this so we can keep ready, set, battlesnake in your repo. Can you make ready, set, battles cake public so that I can add him to my Yeah. I can. Yeah. My battlesnakes. Ready set battles cake. We named it that. So it had a k instead of an n because it was the Kevin version of the snake as an FYI, and it is now public. Awesome. And so if you're ever in the Battlesnake platform, really easy. All you gotta do is just search for snake. Ready, set. Should find him skate. There we go. That's what we want it to look like. And so now we've got them in there. And now we're gonna see this game going on, and it should be all of our logic and direct us. Something's not happening. Something's unhappy here. Let's see what it is. Womp womp. I love that we tried and then we failed. Could you remove the what the old one password, pop up? I certainly can. Okay. There we go. Oh, no. Nope. Same thing happened again, actually. Create Rematch. So let's think it through. What is going wrong here? It could be that our flow isn't running correctly, but it should be. Yeah. Like, it's going off. It's triggering the flows. It's getting a 200, which means it's getting a response, and it's returning. So what is going what is going on? What is going on? We haven't gotten Because it was randomly breaking last time too. At the end of session 2, at the end of the second episode, our snake was, like, overpowered. Like, it was so good, and then it started to crumble. It started to grumble, last time. Let's take a look at our logs. We have got some logs in here. Holy moly. We do. Let's look here. So is there anything going on in here? We've got move now. Original. Yeah. So I think we've got something going on there. Some so what do we have here? Yes. Feeding a lot of things there. False, false, false, false. Oh, no safe moves. Avoid head to head. I think it might be in there. False, false, false, true, true. Yeah. I think the issue is in Which we've already determined that we think is the case. So you know what we'll do to keep ourselves moving? If you close the pane, why don't we just avoid that entire operation and just take avoid body straight to determine move? Avoid head to head is a, you have to just hit the little edit in the top right of the Oh, of course, we do see. Yeah. And hopefully yeah. If we just do that and hit save, yeah, just yeah. Oh, we're back. We are back. We're back. It's alright. We're there. I have a belief that if you just hit the save button enough times, we're gonna be able to resolve. I'm pretty sure that's the definition of insanity. I think it is too. We're gonna switch things up for just a minute. Yeah. And save this, and that didn't work. And then we're going to go back over here. And then we're going to go back here. And we're going to see if that worked. Oh, disk. It was a bug. It was a bug, by the way. And it had an issue has been opened on this bug. So there you go. Maybe just discard the changes and just we can have Alright. Let's see if we can create another game, see if this will do it. I mean Okay. The oh, nope. Still doing the same thing. Still so moving out of bounds, logic is there. It's definitely happening in 2 minutes ago. Let's get You have to refresh. Sorry. Alright. So let's go here, and let's see again. So determine move is still where things are, yeah, getting hung up. Oh, and you're saying avoid head to head is where everything went to force when they're logically, it should not been happening. I think so. Maybe that's what it is. Let's go avoid head to head options. There we go. False, false, false, false. Payload. Avoid bodies. Okay. So so you know what we're gonna do? We're gonna jump into avoid head to head, and we're just gonna return the inbound date. Like, we're not gonna do anything. Alright. Let's go in there and do that. So here we go. Right. Because that's clearly where it were. Yeah. We're gonna literally delete everything apart from line 2 and 30. So everything in the middle there, ditch it off. Doesn't matter. Can we not just oh, we can't comment. Really? I think no. So just but it's fine. We don't need it. This is this is the last episode. There we go. I also have a copy of it, so it's fine. Ah, success. Alright. So let's save this and see if it works. There we go. And now let's go and try and recreate a game again. Because if that's the step that was causing grief. It was. Yep. Do you remember when I wrote all that really elegant code and it worked fantastically? Turns out not so much. Well, it's alright. It worked fantastically until it didn't. And that is the key to all the programming. It's all good until, you crash production. Okay. That makes me feel better that we've identified the issue and removed it for now. Okay. So ready, set, battlescape is working, and this is really the key here. It's still doing the job that it needs to do. So we're gonna make, we're gonna do something a little bit different now. We are going to actually be, having we're gonna bring ready set battlesnake back from the dead, in a new world. So let's, let's kinda go over here. So this is good. Directus is working, and now we're gonna we're gonna ignore that Directus even exists for the next little while. And we're actually gonna head back over to Who needs to pay rent? Who needs to pay rent? Not me today. And I wanna look at, wanna look at our code here. And so is this okay for you, Kevin, or is it still too small? Yeah. Maybe take up another yeah. Maybe that maybe that's good, I think, for people watching. Awesome. So in here, not a lot has changed from when we were dealing in replit. So we still have our server dotjs, which is just an express server. We've got the 4 endpoints, the sort of root endpoint, start, move, end, and then we've got a bunch of stuff in here that's kind of, yeah, talking about what it's returning. We've got a read me here, which explains just how this works, which we don't really care about right now. And then we've got our package. Json. I haven't added anything new to here except for this new dependency, which is the dev cycle, SDK. And so this is necessary for us to implement dev cycle. If we want to do it this way, technically, we could do all of this via our management and bucketing APIs, but I think it's cleaner through the through the SDK. And then, there's some small changes. There's another file that's here, and we'll maybe call this in just a second. And I'll explain how I got to this file, which was not fun this morning when I was prepping for the show. So, originally, in here, we had our run server. We had our info endpoint. We had our start and end endpoints. And then we had this move endpoint, which actually had some really good logic inside, that if we go and ignore all of this stuff, here was our original logic. And I'm gonna uncomment all of this stuff. Oh, I'm gonna try and uncomment all of this stuff if I actually uncomment the right section. There we go. So this was our original logic. And I'm actually think just to make sure that this is working because, yeah, I've changed this so it wasn't async now, which was part of my issue earlier. I'm gonna save this. So this was sort of our dumb snake that we first started with. So Not dumb. Yeah. But, like, dumber. And so you can see here all of our steps have kind of been implemented here. The base steps here, same thing that's going on over in Directus. Disk. So let's see if this is working. I'm gonna run this express server. I think it's node index dotjs. So this is gonna run How are you gonna expose this as a Battlesnake if we're already running ngrok for directors? Oh my good guy. Also, updated cache direction to down. It is constantly running that caching. Interesting. That's fun. So the caching wasn't originally there, Andrew. So Wasn't. And please tell us about the cursed file. So we're gonna ignore the cursed file for a minute, and then we'll get to the cursed file. But let's see if we run this right now. Technically, I should be able to run 2, right, through engronk? I'm not convinced, but we'll we'll find out. Okay. What port does Directus run on? Not that one. K. Well, let's see if this works. No. It works. It's there. No. No. No. No. No. No. No. No. No. No. No. Locally, but you still have to expose it. Yes. So can you see if one shows up in your, is it list it'll show up on your ngrok dashboard on your end if it's running. I didn't even know there was an ngrok dash I didn't even know there was an ngrok dashboard. We'll see. But yeah. Let's take a look. So behind the scenes, I'll actually pull this up just so people can see what this looks like. So when you go to the ngrok dashboard and you're logged in, if I am logged in, so you have, like, this authentication and set up stuff. And then you also have this tunnel section. And in the tunnels, I think it's tunnels where it actually shows you, yeah, what are currently online in your account. Account. And so you should see you should see I've got one. You've got one? K. So this is not doing what we wanted to do. Wait. Wait. Wait. Wait. No. No. Yeah. And the one is you. Oh, it is? Yeah. Started 10 minutes ago. I'm not running anything locally. That's that's Directus, though. That's why. That's directives. Okay. So here's what we're gonna do. And now we need to try and run it a second time, but I'm not convinced. We can shut down directives for right now because we don't need it as we're just kinda demonstrating how this works in the index dot JS. So we're gonna shut down Directus for a second, and then we're gonna run it over on our other one. Alright. So we've shut down Directus, and let's go back over to index dot j s. Let's cancel this out and awesome. Restarted that. What is the address that I'm wanting to go to, Kevin? Can you paste it somewhere for me? I don't understand what you're asking. Never mind. I But, also yeah. I've But you're not running ngrok yet. Right? Oh, that's right. I have to rerun. No. I am running it. You need to rerun it. Exactly. Rerun ngrok. Yeah. Oh, there we go. Got it. Bad things happening. Okay. Let's go But this time, not 8,055. Yeah. Yeah. 8000. Alright. Here we go. Beautiful. Alright. Alright. So let's go over here and see if this is working. Beautiful. Alright. So this is running now. So if I go back over here to account settings oh, not account settings. My battle snakes. I'm gonna go ready set battlesnake, and we're gonna leave a JavaScript, and we're gonna have this on nothing, no platform today, and then we're gonna change our server URL. Yes. Like that. And now if I go in and add ready set set, Battlesnake to a game, create game. And ready to set Battlesnake. Remove the Octa thing in the top right? It's very annoying. My goodness. All of the prompts. And then we are going to see if this runs. Okay. Good. Basic logic, not running into himself, not running into walls. Good. So he's working. Alright. But I haven't implemented the logic for dev cycle yet. So let's go look at what's going on. So we've got the moves that are feeding through there. That's awesome. That's what we want. I'm gonna actually turn this off for a second, and then turn it back on. No. I'm not gonna turn it back on because it's gonna constantly need to be caching. Okay. So, that's our basic code. Right? We're good. We're there. We're not here for basic code today. We are here for confusing broken code that will cause many problems and will make our heads hurt. You're really teeing this up, and I'm horrified before we've started. Don't worry. You and me both. So let's look at the new logic that I have in here, which is not great logic, but, I'll explain it. So, what we're doing is every time the move endpoint is called, this move function is called. Yes? Everybody should track that so far. So what I'm doing here is I am calling a, this get cache direction function, which is basically every second or millisecond. I'm not sure. I think it might be millisecond. It's calling the dev cycle, platform and asking what move should I be getting for whatever user is on the platform right now. This will all make sense when you kinda see the logic, but, basically, it's it's pulling the dev cycle platform to ask it what move you should be making as a snake. Does that make sense? Yeah. I think your face Unfortunately, this isn't even remotely the direction I thought we could go in. No. Don't worry. We're not gonna we're not gonna manually we're not gonna manually do this. We're I just wanna demonstrate the, like, how it actually functions. And so this is good. Mhmm. And I actually you can do it mid game too as long as your your click game is is on point. Oh, man. Which we can do today because there's 2 of us here. So Oh my god. We're gonna go and actually look in this cache dotjs file. So here's our cache.js file. Couple things happening here. First thing, we're importing the server SDK that this dev cycle from there. Then we're initializing our dev cycle, client in here, despite it being a server application, we're calling it a dev cycle client. This stuff is That's fine. That makes sense. Yeah. This stuff isn't super important, the cloud bucketing and enable EdgeDB. It is and isn't, but really the important thing here is that we're initializing the dev cycle client. We're creating this cached direction, which is setting the default move for our snake to go down. So, basically, if, the first time, if there's been no caching, if this has never run before, it's gonna go down. And then, yeah, within this function, we're feeding in the user variable. So you have to feed this in every single time that you, that you do anything with the external platform. You have to feed to the user. You can feed in custom properties, which we may try and do today, where you can, like, base it on different information that you may have. But this is the idea here. And then once we've got this dev cycle, client established, we're then going to look for a specific variable that exists in the Dev cycle platform. Think of a variable as, like, the feature flag that you're looking to pull. So whether you're wanting to see on or off, we're actually pulling a string. So we're pulling for the direction for this user. What should this user be seeing if we're looking for the direction variable that's in there? And then we've got if we do end up having a preferred direction. Yeah. Because where's my preferred direction defined? I think it was defined over here. Right? Yes. There we go. So, preferred direction. So, basically, it's saying that if there is the DevSecLE client being established and it finds that that direction variable exists, then we're gonna look for that preferred direction variable that we had defined over an index.js. And if it returns anything but none, it's going to put a new cache direction value in there, and then it's gonna log that it's updated the cache direction. Otherwise, it's gonna give you an error. And then if we go down here, yeah. We're basically updating this every 10 it's not ten seconds. This is, like, every 10 milliseconds. That's very, very often. Very often. So a1000 is one second. Yep. And then I gotcha. Yeah. So this is our code that we're doing here. Basically, we're just constantly pulling the dev cycle platform to, like, get the latest updated stage. So there's a little bit of a delay here, but it works pretty well. I will believe it when I see it. Gonna be honest, mate. Okay. Alright. You're like, this is garbage spaghetti code. What are you doing, Andrew? It's not like it's not the code. It's just this isn't how I would have. Can I give you maybe an idea of how else we could have implemented this sort of code? Yeah. Let's yeah. Let's go. We literally just have, like, branching logic in the flow. And based on, like, there's the snake gets an ID for every game. So you can enroll them and then, like, 10% of the time or whatever, they get thrown into, like, strategy 2. And strategy 2 could be it become it just goes left all the time. Like, it doesn't matter. Yeah. But you could implement branching logic. Right? So we are and that is what we are gonna do. So you're not wrong with that. I just wanna show how this works. That's fine. Okay. Then we can get into the logic. So Okay. That's our logic here. So, basically, we're calling this every 10 seconds, and then it's gonna if it's updating, it's gonna show us this using cache direction, and then it's gonna do that. And just to clarify, the whole reason you've created this notion of a cache direction is so we're not gonna go to dev cycle every single move. We will go a 100 times a second instead. So what's what's interesting is because we're basically running an async method here, there's a delay and devs battle sync doesn't like there being any sort of a delay. So I had to I completely got it. So you have to have some I got it. Yeah. You've gotta do the polling yourself. I think you probably if I was a better programmer, I could probably figure it out, but this was like No. No. I I I get it now because you could call dev cycle every time, but you're not gonna be able to respond within 500 milliseconds confidently. Yes. So for that reason, instead, we will catch the last direction it gave you. Exactly. I got it. I got it. That makes sense. Fine. You're I'm with you. And it's interesting. The reason I figured out that this was working is so if you notice here, in the logs, so we actually it returns move 117 right. What I was seeing in my logs was it would be like move 01746328. And so it was taking time to actually do the the the calculation. So anyways Which is no good for the snake. It just won't work. Yeah. And so I'm gonna leave this I'm gonna leave this at, at 10 milliseconds for right now, now only because I'm running on my own device, and I think your end grunt won't get upset at me. And because I know that it will demonstrate how this works. The downside here is I actually, Kevin, will need you to be the one that is starting no. Because nobody's gonna be able to see it. Okay. We're gonna see how fast Andrew can click things is what we're actually gonna see. So we're gonna start this again. So if everything's working properly over here, I've uncom oh, I've gotta uncomment this. Let me cancel that again. Let's uncomment that import statement. I think all the rest of this logic has already been commented. Awesome. So now if we run this yeah. So we're basically checking this, like, every 10 milliseconds. It's kind of crazy. I don't fully know why I don't think it's running every time it shouldn't be. I'm unsure logging every time it's making your terminal choke. Here's my question though, is we're not calling move all the time. So I'm confused why That's a good point. Get cache direction is being called every single time because it shouldn't be. It should just, like, literally only be called when moved unless I've got a game running somewhere. Do I have a game running somewhere? You shouldn't be. No. I don't even know how you would check that. Yeah. I don't know either. But, like, if I look at this okay. Request made. Oh, are you the squiggly brackets? Yes. The destructuring. I shouldn't have done that. Alright. So, yeah, it should not be running. It should be running every time. What is going on here? It must be something in this actual cache file. Let's see what's going on here. Import dev cycle, initialize dev cycle, let cash direction go down, update cash direction, import dev cycle. Yeah. Because you're set into it here on line 33. So that's happening regardless. That's happening the moment it's imported. That's not linked to that function running. I don't think it's that much of an issue. I'm gonna change this to, like, a 100 because what's that? That would've that was gonna be my only suggestion is maybe just knock it knock it down a bit. Okay. Yeah. Anyways, we're gonna do this. We're not gonna do a lot of this, because it's gonna be less important. But, we're gonna set this running, and then we're gonna go and actually take a look at the game. So here we go. So we're basically changing this direction, and I'll actually actually, let me cancel that for a second. And I'll go to the dev cycle platform so I can show you. I already set this up. Yeah. Yeah. Yeah. That'd be good. Here are the dev cycle form. Once I log in once it logs in come on, dev cycle. What are you doing? Oh, there we go. Logged into the wrong one. So we actually just launched a, a bunch of new example apps, and so I've been playing around with those. But there's no way to log out without, getting rid of the the demo mode. So, that's how we're gonna log out. I feel like it's like a user retention is great if the user can ever log out, get a life hack. All right. Let's close these again, see if I close this. And let's try app.devcycle.com again. And I think this is gonna load. Yeah. There we go. Awesome. Log in with my work count. It's gonna bring us over to the dev cycle platform, all of our production stuff. We're not gonna mess with our production stuff today. Do it. Do it. Let your intrusive thoughts win. So I did it at, at KubeCon. I actually we made a bunch of live changes while we were doing demos, and our entire engineering team got very unhappy with us. And so I think we're ready to set Battlesnake. And I've got this features area here. So I've got a feature that I've set up called my choose move feature. And this is Could you, zoom in, sorry, 1 or 2 times? No. That's okay. The small text is quite small. We're good. Nice. Thank you. Perfect. Yeah. So we've got our choose move feature. This isn't super important for us right now because I've only got one feature. And then I've got this direction variable that I have there, and I've defined 4 different variations, left, right, up, and down. These are string variables that are in there. And then in my targeting logic, I'm basically saying who to serve what or what to serve to what users that are feeding into that SDK. So here, I'm basically saying serve all of the users down, but that's also my default. So I actually want to be serving to see if this is working and actually talking right. I'm going to change this to serve everything right. And if this is working now, when I turn on that Battlesake server, I should be seeing everything in this updated cache direction except for the first one showing as right. So let's see if that works. There we go. And even the first one did. So we've got the changes happening here. And now if I go back over to the platform, change this to up and save and then take a look at my logs. And so it's like real time changes that are happening in there that you're able to kind of differentiate what's going on. So the interesting thing here is that, technically, platform to control your snake in real time. It's a terrible idea. That is literally the like, like, we've seen people play Battlesnake live using, like, games controllers. And even that, you're like, well, there are some questionable choices here. This is like drop down and then save. So but I'll show you something inter I'll see if I can get this to work, and I'll just do a I started using, there we go. So I started using, rectangle, and Sure. I can't believe that I've never used it, anything like that. Oh, yeah. I use Raycast for it, and I have keyboard shortcuts. Oh, look at you. Which is really, really nice. So let's grab ready set battle sneak in here. And we're gonna go start gaming. Just ready set battle in here. And so you'll see it's not gonna work right now because it's not running. So he's going up. So we know it's not down. There's no conversations that are happening right there. So since we know that we've set our game mode to, let's change it to left so we know that something's working here, and then I'm gonna start my server. And now if I create a rematch, should go left. Awesome. So we know that it's working. Sure. Alright. So now I'm gonna do the thing that is, like, the test of Andrew's ability to click things really fast. I'm gonna create a rematch. And then in, like, real time, I'm gonna click save. And there should be a switch if the board is big enough. But, actually, I need a bigger board because that will give me more time for things to go wrong. Let's go 19 by 19. Add, ready, set, Battlesnake. Start game. Oh, I shouldn't have done that. Anyways, it's fine. You've done that. You you moved moved. Do do do do do. No. It's too late. The game already runs as soon as you click create rematch, so I gotta be a super fast. Alright. Here we go. So right now, he's set to go left. I wanna change it so he goes Don't worry about hitting play. Just hit create rematch, hit save, and we can see it in the playback that it's happened. Alright. Here we go. There we go. Oh, and he's going left first. So literally, the worst example that ain't gonna work. Before you create rematch before, you wanna yeah. Prime. Prime. Oh, yeah. Change it. So right now, we're going down. So we're gonna get it. So we're gonna go first, we'll be going down, and then we should go right as long as we know where it, as we long as we know where it spawns. Okay. This should work, actually. This should be good. So here we go. Should go down first, and then at a certain point, go right. Oh, not fast enough. We're gonna try this again. I tell you, it did it does work. You just gotta have the the right movement. Andrew, I full on believe you. You you don't have you're not proving anything. Don't worry. Come on. Really? Your your taste. Oh, did we see that? We did it. We did it. So here's the thing. Right? So we changed it from right to up. So we're going right. We're going right. We're going right. And then suddenly, we start going up. And that's there's no logic in here that says, dearest, dearest ready set battlesnake, avoid that wall. This is all just, Andrew changing that logic in real time. And you can see if we go to the logs there, which are gonna be, like, super overrun now, not that logs, these logs. But you can see here in our terminal there we go. Yeah. So we're updating our cached app, updated cache direction app. And so you can see that there's, like, the change that happens in here. Yeah. Oh, and this is the interesting thing too here. Right? Is you actually see so most of the time, it's updating cash Kevin, are you impressed? Are you blown away by my amazing implementation of this? I am so blown away. Thank you so much. I feel like my life has been genuinely enriched in the last 39 minutes of recording. Thank you, Andrew. I don't know what I would do without you as a friend, a mentor, a colleague. Thank you. Thank you. Good. Yeah. And that's it. Episode over. We're done. Episode wrapped. Thanks. So, that was just like this is I feel like in every good episode, in every good coding experience, you should do something that's totally useless and makes absolutely no sense so you have the knowledge and skills to do something that actually makes sense. But there are some things already set up. Right? So we've got the dev cycle, like, prod forgive my terminology if I use the wrong terminology. But we've got the project set up, we've got the variable, we've got the options, we've got the ability to change it and hit save. We have a client, you know, we have like an ID for this specific feature. So, you know, we have all the parts, and we can just apply it in Exactly. Different ways. So what we're gonna need to do here is I'm going to so we're gonna forget that all of this ever happened. We're gonna go shut off ngrok, for a second, and then we are going to go back to our direct us instance, and we're gonna start direct us up again so we can start to have some fun. And we've got all of our code here. Why We do. Are you It's it's it's the other it's the other Visual Studio code that's in the back. Yeah. Are you talking about? What? Hold on. Alright. We'll doc, and then we'll go back over here to engrok, and let's get this running. Oh, I'm running the wrong one. There we go. Nice. Wonderful. And let's go over to here, which I should actually have open in my window. Oh, and we don't need this for right now. So let's open that and make that bigger. And we should be able to go to here. Wonderful. I am gonna need to log in again, I think. No. No. No. I think it'll be fine. No. I need to log in again. Oh, you're gonna need to log in again. Battlesnake@lws.iohunter2. Glasses us what I set this up with. There you go. Okay. So here is our logic. We removed a bunch of stuff from in there. Great. Our snake is working again. Should be working again. Logs are all there. I feel less bad about all the logging that I was doing of every time that we were calling Dev cycle now that I see these Exactly. Thousands of We actually you know, this was I mean, all the dates, like, visible elsewhere. Just today, we actually recorded, or we we did a live request review, which is our show where we or it's our live event, which then becomes a show on director's TV a week later, where we go through feature requests to talk about them, and today's was all about flows improvements. And one of the big things is the ability to change the retention of those logs. I like that. Because, yes, they get they get chunky. They do. So, what I think we are going to do, and I liked the idea that you were talking about before, Kevin. I think let's do 2 pieces of logic, and we're gonna we're not gonna delete all of this stuff. We're just gonna kinda remove it for right now, and then we'll reconnect it. And I think we'll have just right now 2 different blocks. 1 of them that returns right, one of them that returns left for moves, and we'll have some branching logic in there. How does that sound to you? Let's create a new flow for this, and then we could just hook it up to the extension, rebuild it. Like, I think that might be a good a good shout. Let's do that. So let's call this snake, cycle. Alright. And Great. We're just gonna leave that. All of that's good. Just next. And webhook or webhook. Yeah. Okay. And then get, I think. Host. Not the get. Not I had a 5050 shot there, and I got it wrong. There we go. No async calls either. Because because we'll get because we'll get a post request from the Battlesnake platform. Yeah, data of last operation safe. And then what we wanna do here is grab that URL. Yeah. I think yeah. Perfect. Go go to the code editor. Go to the extension called snake. Yeah. I think so. And then in our source index dot JS, and we just wanna update this. Right? For move for move. The move one. And just to not have to do the round trip, we'll change the for local host 8055. But hit paste. That'll give us the ID of the trigger and everything. Yep. And then, yeah, we'll just change that for HTTP, not HTTPS, localhost secure my how to secure my URLs. Kevin, who who are won't work. Phone's bloody work, man. Actually, it does. No. No. It does. I'm pretty sure. I'm pretty sure ngrok automatically anyways, I don't I don't need to Not not on not on this one. Yeah. Yeah. But yeah. Yeah. Yeah. But not on the local not on local host. If you were doing it via ngrok, but then we're gonna have to do another round trip to ngrok for what? It runs locally. Smart. Okay. So we've got our trigger there. This is our move. So now this is all feeding through to the place that we want. Now we've got to rebuild the extension and restart directives. Npm run build? Yeah. From the from that extensions directory. So let's create a new terminal in here. Yeah. CD extensions. Yeah. You got it. And CD directives Extension. Extension snake. Jake and m You got it. Build. You got it. And then restart And then we just Oh, because cannot. Okay. This is fine. This is like macOS going overboard. Open up your open up your settings on your Mac. Security, I think it is. Yeah. We may not be able to do that just because of the security settings. Security No. It was there. It said privacy and security. Security settings. No. Just yeah. There you go. Allow anyway. It's right there. It's right there. Fsevents.net. Yeah. Yeah. Cool. Yeah. Great. Alright. Let's try this again. You got it. Beautiful. Okay. Lovely. And then restart and then restart the Docker container. Fixed in directory version 10.9, which we have not upgraded to right now. There you go. Hooray. And now you're good. Okay. So let's head back over to Directus, which is Yeah. Somewhere. It exists. It is in the world on this device. Too many windows. Directus. There we go. Okay. So here we go. So this should be talking here. I'm actually gonna go start a game just to make sure this is working. So let's go, play.battlesync.com. Let's go create game. And I think yeah. Does it still save recently used? It doesn't. That's so sad. Used to do recently saved escape. There we go. And if this is working, we should see a log showing up here. Don't look. Happen. Just no. That's fine. We don't it's probably not gonna work work, but it should show a log here. We have 10 logs. I love it. Awesome. Yeah. Okay. Method post. Yes. So that's 10 moves that it took to pass away. Perfect. Logic is coming through there. Accountability. Awesome. Okay. It's working. So now we're gonna do some branching logic in here. So we're gonna go and condition. And we're gonna worry about the condition afterwards, but let's call this dev cycle response. Oh, it's not here. Well, hang on. Not in not in a condition. 1st, we need to go get data and then we run conditions on the results. So I'm assuming we can just make I'm assuming with DevCycle, just an API, we can make an API request, right, just a raw API request. So you want the webhook request URL, one there. Perfect. And first, we need to go get the data, then we'll do the conditional logic. So this might be a moment to look at the dev cycle docs. Yeah, that would make sense. We should probably Yeah. And there will be a moment here where we'll we'll redact it for for the recording, but you're gonna have to authenticate yourself. So you're probably gonna need to put in, like, an API key or token. Can you create multiple inside of your dev cycle? That's a good question. We're gonna experience this together. Yeah. I haven't played around with the yeah. So we're gonna do a little bit of this together, and then we're gonna not do a little bit of this together. So let's head over there. So jumping into the dev cycle docs, APIs, management API. This is to create features and variables. I don't think it returns anything. We actually want the bucketing API. So this is used to fetch on HTTP. Yes. Yes. Yes. Unique identify the user. Definitely, this bucketing API. Get all the variables. Get all the features. Post events. Get variable by key. Okay. This is what we're looking for. So let's head over to bucketing API, and we are going to have to get our key Wait. What what my key? Yeah. Sorry. What's the endpoint we wanna hit? So we are going to do get variable by key for user data, I think. Let me look here. Private platform SDK type. Missing SDK. Get all variables. What is that gonna return? Is that gonna return the status? What's our what's our end goal here? Our end goal is to We wanna just get the value for the variable. So it's very similar to what we're dealing with in our SDKs. To the user ID to start. Yeah. But everything is hidden behind our bearer token. So I'm just gonna find where all of this is hidden. So key type variation, variation in variation key, response examples, get all features by key post events. No. Fetch No. I think get all fee I think I think you were right. Get all features by key for user or get all variables by key for user data. And this will no. Because this will return Here it is. Yeah. Here it is. The value. Fantastic. That's what we're that's what we're after is the value. So, we're gonna go offline for one second as I go and get all of my authentication stuff sorted out, and, then we will be back with our API stuff. Sounds good. Okay. So For what for what it for that's worth, we could totally have kept it going, and we could just redact it, adjust that slither slither and time. Keep it going. Yeah. Let's do that. Alright. We're back. Yeah. We haven't gone anywhere. We thought we were gonna go somewhere. This is interesting too because we if an so, technically, our management and bucketing API are actually using the same stuff. I need to update our docs because we should actually have this curl command in, both, and they're not both there. I guess most people are just assuming we're gonna be using that. So, I'm gonna go and, in a different window, actually, open up, my OAuth token using the client ID. Okay. So I need to go over and what do I need here? Client ID and client secret in my dashboard. Alright. So let's go over here and yeah. I don't think Yeah. Showed anything. I'm a I'm a little bit fuzzy on how you authenticate. Let me show you. Okay. So what we do here is alright. You're seeing my screen now. Hey? Yeah. So and, again, I will fully admit that I have only messed around with our API in a very limited way. So, basically, we are doing a curl request with our client ID and client secret. We can actually obviously do this same post request in Directus, and we're just using our client ID and client c. We can't do it directly and directly. I've just spotted it. The type the encoding type for this, for this request is a URL encoded form, but that request URL, you know, operation that's built in only sends JSON. And they're just you don't work around this. Like, you can't do it. However, I'm not too too sad because you can, of course, do it using a custom endpoint. It's not quite what we wanna do, but could we do or building a little custom operation. So maybe that's what we do in order to to do that round trip. It's not the ideal, like you know, it means we're gonna have to spend some time doing a little bit of extension development. Like, just spotted that. Is that is that the only way to do it? Yep. Yeah. We have no real public API stuff, which is interesting, but also so what's interesting so we are having so we're working right now on some webhook stuff. So you can technically get when, certain changes have been made, like an audit log. You can read that through a webhook, but you can't actually read what people's values are. Because that would that would be so good because on a webhook, you could trigger a separate flow, store indirect just like your cache Yeah. And then do stuff based on that. But that's completely fine. So we could do this or if we're building a a little custom, operation, we could just straight up use the SDK. Yeah. So let's do that. And then we've already got the code there. So, yeah. Okay. Let's, are we gonna do this here inside of our code, or are we gonna do this over on the platform to get started? Where are we gonna I don't under let let's let's build an operation for flows as an extension. And the goal of that operation is to, right now, just get a fixed, you know, a fixed response from from the API. So, no, we don't do it like this. We actually use the extensions SDK CLI, which will boil the plate a whole extension for us. Alright. We're gonna delete that bad boy. Alright. Let's open a new terminal here. Open up a new terminal. You got it. And we'll go into the extensions folder. Alright. Oh, into the extensions folder. Extensions folder. Yeah. Yeah. Yeah. Sorry. Yeah. Yeah. Exactly. Exactly. And then how do I pull up the docs? Or are you just gonna Yeah. Docs. I mean, I could tell I could tell you this one, but let let's let's do the docs. Let's let's have the authentic experience with someone who doesn't live in this every day. There we go. Alright. Let's head over. And we're gonna see this is gonna be my challenge. How long does it take Andrew to find out what I'm looking for? Alright. So when direct to stocks, we're in extensions. We're in device extensions. Zoom in. We're going to zoom in. We're going to go extensions. We're going to go, developing extensions and we're gonna go extension services and introduction. No. That doesn't look like what I'm looking for. Maybe Very good. Very very good little piece of feedback there. Go into the fundamental section, the one you haven't opened out of the 3, the last one, and creating extensions. Perfect. We're just gonna use this. Alright. Let's go back over here and run this. But I I do see that. I do see that path you took. I'm glad to know that. Discussed it with one of our colleagues just yesterday. So yeah. Like, when things work oh, oh, I need to change my note version. It's upset with me. Wait. What? Oh, it's just warnings. Really? Oh, yeah. Whatever. Sure. Me. That looks what do you wanna create? Operation, please. Oh, there it is. Hidden down in that list. Choose a name. And they so they must start so they must start as of this release. I think it might be gone in 10.9 or it'll be gone in a soon release in a upcoming release. Directus dash extension dash whatever you want. Dev cycle is fine. JavaScript because I don't play with TypeScript. I respect that. Dependencies. Yes. Scaffolding it. I love this. This is so smooth. I like a I like a good CLI. Good. Great. So now we have if you go into the extensions drop down, you have a new extension called dev cycle inside of source. We have API dot JS. We have App JS. App JS is for all of the configuration. So it's all the UI part. It's all of the options when you create a new one, you have that little pane that pops out. It's the information that will show on the on the card in the overview where you connect to all the nodes. That's the overview there. And then the API dot JS is the node code that will actually be executed in the background. Right? So we're gonna go and I think right now what we wanna do is actually grab, just so we're not having to worry about this later. We're gonna grab our code from here. So not going back and forth, because this is basically all the stuff that I need. Yeah. Great. And we're just gonna put you down there and comment you out so that things don't get upset with us. Sounds good. Alright. Perfect. So let's save that, and let's do an install. So I do have to have to go into this folder to install, slash extensions. Directus extension CD. Extension. CD. Cycle. Not we're not we're gonna put it at the end and see what happens. No. Start. Start. Start. Start. For a moment, I was like, am I about to learn something? No. There's there's very little learning that will be There's very little learning. I got it. I got it. And then we gotta go over to because we're gonna install the, the SDK. We're gonna go over to the dev cycle docs. So we'll move back over to docs, and let's head back over to the dev cycle docs, docs.devcycle.com. And we're gonna head over to our SDK section, our node JS SDK You got it. Installation. And let's grab this because who uses yarn, NPM or bust? I've become so opinionated in my coding Alright. So that means that our import statement should work now. Actually, is this import or is this require? I This is import. Yeah. I thought so. Alright. Let's move you to the top. Alright. Perfect. So step 1 complete. We're done. Time to go home, everybody. End of episode. So a couple of things that are just important before we continue. There is an ID here called custom. Let's rename that, and then we have to reflect that ID in the App JS and the corresponding App JS. I think dev cycle is a perfect ID. So we just want those 2 to match, first of all. Very good. And let's, while we're here, give this a name. Dev cycle, this is the nice friendly name that shows up when you pick your operation type. Sure. Yeah. Then are we gonna ask the user for any configuration up front? And I'm gonna say that maybe we do. Maybe we wanna collect the snake eye like, the the the ID. Right? Are we gonna do it? Are we gonna have time to do that? I mean, can we give it just an arbitrary ID, or does do you have to register users inside of the dev cycle system first? Like, how does that work? So you would need to feed a specific user's information. Yeah. Let's call a snake name. Yes. I think that's a great idea. Yeah. I was a snake ID. Snake ID. So let's do that. And at first, we'll just hard code it, but later, you can put dynamic values in here in flows. So instead of text, let's just change text to snake. So there's a friendly name. Yeah. Yeah. Perfect. There's a friendly name. Yes. Is that correct? Is it? I changed this from tests from is this yeah? Yes. That's all good. The options need to change too. That's the part. So that that's the overview that will show on the card. Cool. Whatever. It's the options we care about. That's the pain that comes out and you select all the options. So in here, the field, the field will be called snake. And that's what corresponds with the snake in the overview. That's the friendly name. It will still be a string. It'll be full of input. Great. Save. Now we'll go over to API dot JS, and there's text here. We'll change text for snake. So that says taking in the snake value and now we're going to execute some code. But before we do this, I think that's everything on the UI side done. We want to initialize this client and we'll want to initialize this client between lines 13. So everything in your yeah. Great. So, we're going to ignore this because we're not actually going to I'm actually I'm interested to see how this works. Once we have done this, we're going to change some things up eventually. But we're not gonna worry about that right now. So let's grab Oh, which was straight upon commenting everything. That's that's crazy. I mean, nothing could go wrong because nothing's really running yet. So this is good. This server is gonna change. I'm gonna leave these. I don't actually know if we need these, but I'm not gonna question it. If it works before, our goal is to take what worked and apply it here. So we're not gonna make any changes to the logic here. Yeah. Exactly. So we are initialized there now, and now we need to pull all of this stuff up above. And I think we're not gonna need that get cached direction elsewhere. I don't think we wanna necessarily play with that right now. And at the very least, we wanna make sure that we can get data from dev cycle in a flow Exactly. And then pass it on. But if you were having an issue where it's taking a little while to do that round trip, there's a pretty good chance that we're gonna hit that here too. Okay. So let's go here. Awesome. Alright. So here we go, dev cycle clients. So we're established here. We've got this cash direction. Again, I don't actually think we need this anymore because we're gonna be calling it sort of once to determine what's gonna change. We're not gonna be calling this again and again and again. Right? We're not gonna be calling this every move. Is that our that's our focus. Right? I mean, we were. Yeah. Do we we were gonna call it every move? Oh, this is all in the wrong endpoint. This actually wants to go in the start game, doesn't it? Once you're in the start game and then for the game, we determine the strategy and then we execute on it. This is okay. Let's keep rolling now. And then before we've done it with yeah, exactly. Precisely. So let's really let's let's keep this light. In fact, I think doing everything in a separate function may not even be what we Exactly. Want too much here. So I'm gonna pull all of this out of this update cache directory. Hang on. Hang on. I think the only bit we care about is this dev cycle client variable. I think that's the only bit we want. We also care about this user thing. So that's where that's going to snake. Yeah. But that's going to that's already that's coming in from the snake value above. So we're gonna get to feed that in. Yes. Yes. Yep. I guess it has to feed a very specific way. It has to be a very specific object that's feeding in, but maybe you're right. Let's let's see. Let's see. I think I think it'll be okay. So so if we if we go down to where the actual flow handler is, which I think is down. So inside of the handler, instead of the console log, snake is a string. So what we want to do here is we want to make on line 41, we want to make this an async function. Right? Yep. No. As in as in we wanna change just before the the snake. Oh, I see. We wanna make it async, then we'll just use a sync yeah. One more to the left. One more to the left. Goodness gracious. And we're gonna get rid of you. You don't need to be there. And then and then inside, what we wanna do is is run that run that SDK method, but we can do it with a wait instead of then just to clean it up in this context. So if you scroll just very slightly up oh, yeah. We'll ditch something small. Yeah. We're just gonna do this. We're not gonna need to do this call. Perfect. Let's just work the day. Grabbing this. Yeah. But only 17 18 to start. Oh, yeah. You're right. Yep. Because we're doing a sync await. Yep. Yep. Absolutely. So we want that. That goes straight in there, within within a wait at the beginning of line 33 and a store this in a variable. Right? Yeah. Let's call this strategy. Beautiful. Love that. And then user is now gonna be I mean, if you wanna grab lines 13 to 15 just to get the structure, but really it's just an object with the user ID and snake. Yeah. Yeah. Exactly. Let's see what happens. And then yeah. So we're gonna Other way. We're gonna define this above. Yep. We can assign this above. Awesome. And we're actually gonna feed this in. User ID here is gonna be snake. So we're gonna feed in snake here. Snake? Yeah. I think that should work. I think that should work. But we don't know what comes back in strategy yet, so we're gonna log it. Perfect. And then we're going to return strategy. Beautiful. And that's actually add some comments there so we know it's being logged. Strategy. Oh, can I show you can I show you a quick tip for this, which I think you'll really like? Love a quick tip. So remove that remove that string in the comma from the console log. So it's just the word strategy again. Yep. And then highlight all of the word strategy, just the word strategy, and then surround them with squiggly brackets. And now that is an object where the key is strategy and the value is strategy. So when it logs, you get strategy, colon. Yeah. It will be an object. So that is just a real shorthand if you if you just want to quickly tell what something is and you have just a variable key. You know, sometimes I enjoy JavaScript, but only when people that enjoy JavaScript are doing JavaScript with me. Okay. So technically, this should this should do all of the things that we want it to do, I think. Yeah. And so this is wanna change this to hold on. Oh, sorry. Go ahead, Kevin. No. No. No. Go ahead. So I'm just saying here so here, we're actually gonna go and create an so this is a direction variable. We're gonna create a strategy variable and kind of put 2 strategies. We're gonna do, like, hungry and tailchase or something like that. I mean, we we're not gonna implement those. So why don't we do left or right? That is a very smart decision, Kevin. So let's head back over to dev cycle here, and we're gonna get out of there, go into our features again. Into our choose move, it doesn't really matter again what this feature is called. We're gonna create a new variable. We're gonna call this strategy. We're gonna set this as a strict technically, we could just do Boolean, but we'll do strategy. And because of the way that we have this set up with, like, these 4 different options of left, right, up, down, we're gonna actually go ahead and kind of move this around. So we're gonna call it, this one's gonna be left, and this one's gonna be right. Actually, we'll just leave it all exactly the same. Down, up. But it's just called strategy this time. It's called strategy now. Yeah. Sure. Alright. Amazing. Look at that. We changed literally nothing. I love that I did a thing that was completely unnecessary. We could have just kept the stream. Just kept the same. That's fine. That's fine. We we okay. So we're logging this and we I think I think this works, but what we want to do here is is what do we wanna do here? We now wanna hook this up to a start. Yes. So we need to do we need to do a new end. And we need to change the data model to, have this extra, like, strategy string, which I think is amazing because then even in our insights dashboard, we can be like, when using this strategy, this is the win rate. Like, it's actually really lovely. Now we're done in code for right now, though. Right? So I think that we can No. We're not? Build this one. Build this extension. I think we're gonna need to change which flow is being hit on start. I think we need to create a new flow. We need to create a hang on. Oh, metadata. Hang on. Hang on. Is this are these errors or are these suggestions? I think this is all about and has been rewritten. I have a sneaking suspicion this is inside of the SDK. There might be no module in But it said done. Yeah. This may And it says and has been rewritten. All of these have resolutions. I've not seen the error before. I have a sneaking suspicion it's in the dev cycle and the way we're rolling up and bundling code, not a problem. If it works, it works. Not sure if it works. We'll figure that out. So what we actually wanna do is create now another Flow. Flow. Do we need to create another item? No. No. We don't. We can alter our existing start endpoint. Yeah. So yeah. But you've built this. You need to restart Directus. Yeah. Okay. There we go. We're gonna restart Directus. Oh, I need there we go. Let's restart here. Oh, wrong one. Just ignore the fact that we're rebuilding it again. Ignore all of those errors, everybody, and go to the right ones. To me to me, they read as warnings. That's the spirit. Alright. And that should be running again, and we're gonna close that down. We're gonna get the right thing open now. And I think we were here. Let's mark this out. Absolute chaos. I literally can't get behind your window management. There is no there is no window management that is occurring. There is nothing. So, there is one other thing we want to do just before we hook all this up, which is we want to go into the data model. And for a gain, we want to, just add a string strategy. Right? Yep. Yeah. Lovely. And then just because it makes can you make it half width, please? Sure. You use you you could do it with the little 3 dot things on the side come out of this pane, the little 3 dot thing, and then half Got it. Got it. We got there. Alright. So that's in there now. Awesome. Let's head back into flows. Oh, that's not the right spot. Back into flows, and we're gonna change this one. I wanna change the name here before I forget. Can I change that? Yeah. I can. Yeah. Yeah. Back here. Yeah. Yeah. Yeah. Sneak strategy. Awesome. Nice. Perfect. And then, yes, start. And now we want to see if this new thing can so we extract the snake count, and then we create data. And we basically want to create one there in the middle, which hopefully won't pose too many issues for us. Let's build So no. No. Not on the on the success path. Great. And what do we do in our custom operation? Right? Yes. There is at the bottom. Look at that. It even says dev cycle. I think that's a good name to keep for it. I like that. I also like that it converts camel case into, what's what's this? What's the that is called I wanna say hang on. I will I will tell you this because I actually have it inside of my raycast case. That is called that is called, coming up, camel case. That's camel case? This is camel case. Oh, no. Sorry. Sorry. That is I'm I'm I've picked a ridiculous string to test against. So it had, like, it had, like, hang on. That is called, scrolling through, Pascal. Is it snake or is it Pascal case? Snake case. It's called snake case. What is snake? Enough. That's a case. There we go. That is appropriate. It's almost like I knew this, and I wanted to bring it up. Okay. And for a sneak ID, we can actually put in anything. We're just Oh, encoded. Yeah. We're just gonna put, like, a really basic actually, no. Let's call this ready, set, Battlesnake because there's some cool stuff you can do with your targeting rules, including include. So let's just set that. I mean, remember, that can be a dynamic value from the first step. It can. But for now, we'll But we can we can yeah. Absolutely agree. So inside of the dev cycle step now sorry. Can we just go in? That was the only option. And so dev cycle is gonna return some data. Yeah. All of that happens in the background. So we don't know whether it works yet. So now we wanna redo the nodules. Yeah. Oh, yeah. We just wanna move this to here. Right? Yeah. And this is where we seem to keep having a bad time. Awesome. Thank you, Directus, for doing exactly what we were expecting you to do. On behalf of the core team, you are very welcome. So what is our issue in here? What is our issue? But remember, we had this all the way from the first episode, and we just kind of wrote like, there was a specific set of moves we did, and it worked. Okay. That works. I think it might have been this. I hit edit again. Yeah. And then bring it back and then bring it back. No. What if I just disconnect that altogether? K. That works. We're getting there. And then and then do it again now? Edit and just do that last one? Come on. There we go. There we go. We got it. 2, we're gonna set of actions. There you go. It's 1 at a time. Yeah. Okay. So that Now we want to start a game and just see if it works. And there's a we're also going to want to look at our directors logs too because we did console log just data. But let's we need to start the game. Let's start Then we can first. And let's grab create new gate. Oh, not create new battle sync, create game, and let's grab escape. Oops. Escape. Escape. Which I now regret. K. Add game. Is it in there? There we go. Beautiful. Okay. Let's see. Game is running. Something's happening. Not what we want to happen. Sure. That's okay. Let's head back over to Directus and see what our logs are saying. Scroll up because, obviously, all the turns happen. Right. Strategy. Boom. We just need to do dot value. That's why. Okay. It's it's returning this. It's the yeah. Request to get variable strategy failed with the response variable not found for key strategy. Hold on. Did I not save? I feel like I might not have saved something over on the cycle, which is a necessary step here is to actually save it. Okay. That does sound necessary. Oh, hold on. Maybe this is not that window. Ah, see this? Save. It doesn't actually know that it's there. It's my own darn fault. I got too many windows open. Darn you, Andrew. Honestly, I'm pretty sure it's the curse of academics. Every academic I've ever seen or a covering academic. I think you are though current. Correct. Yeah. It's it is that. So while we're here, maybe we just straight because this is a DVC variable. Let inside of the extension itself return strategy return dot value. Yeah. And then rebuild, restart. Yep. So we're gonna return This is good. Yep. Dot value. Awesome. There we go. So that should work. That should do what we want it to do. Let's rebuild here. You got it. Yeah. We're in the right oh, that's snake. We wanna be dev cycle and not CD. Let's go npm run build. And now we're going to need to restart again. Yeah. Once again, not the case now, but I'm too I'm too nervous just just in case. The upgrade is right in this moment. Okay. And I need to oh, why did I do that? Why did you do that? I don't know. Questions questions have been asked. Okay. So let's see if we can create a rematch. Should work this time. Nope. Not working. Well, it's not so much that. That's just the bit we care about because it's our new snake. What we care about is if we go back to the logs Are these the new No. No. No. No. The the log the logs won't tell us anything. Go see if the strategy has been saved against the latest game. Oh, that's true. Yes. That's what we're actually testing here. Oh, no. No. No. We're not doing that. Sorry. We check the flow logs, the flow logs for start. This is what we're doing. And we check the latest log. We check the dev cycle payload. Ready, set, battle, snake is the snake we're feeding in. Payload up. That's good. I should be feeding everybody up right now. Up, up, up, up, up, up, up. Yes. Hey. We did it. That's awesome. Now so now we wanna store the strategy. And then in the move logic, we want to grab this next this game strategy. We want to act on it. I love this. Okay. So we have this here. Now we need to store it in here, right? We're actually Yeah. We just wanna edit the create data. Edit the create data. Oh, in here. Gotcha. Yeah. Because we're already creating it. We just need to add strategy. You're missing a comma on line 3. But I laugh at commas. Okay. Do you wanna hear do you wanna hear a fun do you wanna hear a fun, little story while we're doing this? I'm ready. So I, am teaching at at the university this this term. And, I was talking to my students who are all future teachers about using proper grammar in the posts that they're doing on the WordPress blogs they create. And I misspelled grammar. Beautiful. Well, you can't have it all. You could have good grammar or good spelling. So so remember, the value was no not trigger. The returned value from the dev cycle step. And remember, the key was dev underscore cycle or lowercase, so not the dollar sign, was the string. So it is just dev underscore cycle, and the value of that will be the string. All right. Let's see if this works. Because that's what was returned out of that step to save. And so let's start one more game. Let's make sure that's being being stored. Oh, cut out there, Kevin. Oh, I actually just stopped talking. I'm not sure. Oh. You you definitely froze. Oh, okay. Fair fair enough. Yeah? Okay. Good. Okay. Perfect. So we ran a game. Yeah. That's fine. And then let's head back over to our logs here. And the I mean, the proof is gonna be in the pudding here. No. So But then no no no the options. Strategy up. So if we now go look at the latest Oh, there we go. Yeah. We're there. Yeah. Yeah. So if so if we go just check the content module in the sidebar here in the, module bar far left, let's go look at the latest game and make sure our strategy was saved. That's the top one. Top one. What is Games. What what an easy quick way to do this is if you just hit the plus in the top. Like, if you add a if you add a field, perfect strategy, boom. Nice. Amazing. So now we're gonna go into our move, our new move, which does nothing, which is why our snake is doing nothing, and we wanna respond to that strategy. Alright. So So not this one. We we we we we we we Oh, that's right. Yeah. Yeah. We did a strategy one. That's right. Okay. Awesome. Snake strategy. And let's do this as a we're just gonna return. Right? So we just need to do No. Because there's a few steps here. So this takes in a move, right? And that move has a game ID. So the first thing we want to do is we want to look up the game to find out the strategy, right? So we want to read data first and my confidence has just gone through the floss. So let's close out this for a moment. Can we just look at the logs here and just understand the shape of the data again? All right. So So the payload, body, game, ID. And that should match the ID of the game in our collection that has been created by the start step. So let's see. 4ff ending in 70. Let's see if that is in fact the case. Sure. I I think it should be. Yep. There we go. Perfect. So what we want to get out is that strategy. So, so we'll create a new step here, and we can call this get game or something. Yeah. Get game strategy. Let's call it. I'll just get this is gonna get the whole game. So let's just get game. And then permissions full access because I can't be bothered to deal with permissions, collection, games, IDs. Now this is important. We want in squiggly brackets, double squiggly brackets here, trick, dollar sign trigger dot body Body. Yeah. Dot game ID. Game. Yeah. You got it. Dot ID. Yeah. And hit enter. And hitting enter just slightly, you know, you have to enter. And just like you're hitting save over there in dev cycle, that's one that gets me. There we go. And this should get the game. So hit save. Let's see. And let's just do this very sequentially. So, yes, let's let's test this again. Once again, the value should absolutely be up. Perfect. Options. Permission to pull the games ID. Wonderful. Wonderful. And we get our strategy. Lovely. And the strategy now is what we're gonna check. So now we're gonna do some branching logic. So what we want is, I think, a conditional. Yep. Right? Yep. And we're going to call this strategy. Strategy. Well, let's be more specific because we're gonna check if it is one strategy or the other. So Oh, that's true. Is, you know, strategy, yeah, is up. Although, was was up the value yeah. Yeah. Off was the value. Yeah. It's up the value. It's up. Yeah. Perfect. So let's fill in the so I don't think we do wanna fill in the default. Yeah. So squiggly like, this is JSON now. So squiggly brackets on the outside. There we go. I love this, by the way. This is such a smart little Except we are gonna have to go edit it. So I know. It's all it's. So trigger change for last because we wanna grab stuff from the last step. Oh, sorry. A dollar sign. Yeah. Last. Perfect. Yeah. Correct. And we're gonna be checking the strategy. So not category, strategy. Yeah. And when you see whether it is equal to up and it's case sensitive. So yeah. Perfect. Wonderful. And hit save. Now, honestly, this is where this is where I mean, nothing would oh, yeah. Suppose we can check. We can check that. Kevin, let's follow, strategy every time we do this, but, no, we won't follow the strategy. Let's see. Oh, wait. Wait. Wait. Nothing it didn't get saved. What? Didn't save the flow. You just refreshed. Oh, did I really? No. Womp. Womp. It's okay. Come on, Andrew. Come on. I mean, to be fair to be fair, there is a little bit of allowing you to do that without prompts. Anyway, anyway, is up Yep. Was the name of the condition, and it's gonna be lost. Last. Yeah. And you call them strategy. You call it. And oh, strategy, and up, and save and save. Up. It's the double save. I get it. I get it. Let's set out. Let's finish this game. And so I figured out the thing that it is here. You actually have to finish the game before it'll let you do a rematch. You have to play the whole game. That was the issue we were facing. Good tonight. So we should have it showing up now. Yes. He's up. Payload. Yep. Awesome. Is up. Now, this basically will fail if it isn't up. It will it will, like, it will have a validation error, and the whole flow will fail. And this is correct. Yeah. Let's see. Look at you go. All right. Here we go. And amazing. Alright. And let's go back over to directors refresh. Look at our logs. Shut up a bunch more. Let's go. Oh, oh, something's happening. Oh, I need to actually press the button. Alright. Here we go. Oh, option payload validation error. Validation error. Wonderful. And so this is where, we have to be creative with flows and work within some of the limitations, to be frank. So because all all conditions, there is no, like, case statement where you can be like a, b, or c. It is true or it is false. It is resolved or it is reject. Right now, our flow simply halts because it goes out that reject path and fails. Right? But what we're gonna do now is we're just going to put, you know, we're going to just put one thing on the reject path, but it isn't failed. It's just the else. Right? So it's just an interesting terminology thing there. So we don't, we don't we don't even need this now. Oh, I guess Unless you wanna do can be Well, why don't you wanna do it? Yeah. Let's just do all 4. There's only 4 to do. So So hang on. Hang on a moment. Have you put you've put this on the reject path. Right? Yes. Perfect. Yep. Okay. Last strategy down, and we're gonna call this is down. Perfect. Yes. And then we're gonna create another. Off the reject path. Off the reject path. So it gets a little bit unwieldly here. So it's not up. It's not down. Strategy. Let's call this last. Copy it out this time before you hit save. Look at you, left. Perfect. Copy. And then one more. So you so you see here how these kind of more convoluted case statements can get a little bit messy in here? Yep. I definitely do. But, is right. Okay. So let's go there. Save this. And go to the game. And let's finish this game. Shouldn't take too long. And this one should do the same thing. It should have a validation error after the first one. Yeah. Alright. Oh, yeah. That's true. Oh, no. See if it does. No. No. No. No. That's a lie. Sorry. It will it will go until it fails. Just ignore me. That was incorrect. Sorry. Is up. So that was our first one. Is up failed. Must be up, and then it dot stopped. That's not correct. Did I do is up twice? Is left, is up, is down. Oh, no. No. It's connected to read data. It needs to be connected to the reject path of is up. Oh, there we go. Not that one. There we go. Yeah. Yeah. Yeah. There we go. Oh, did too much stuff. Disconnect, save, edit, reconnect. Oops. I'm glad. I'm no. No. No. Reject. Reject. Yeah. I'm glad we finally got back to that point of just finding that little that little set of actions. I can't believe it got us so, so much. But, yeah, I've submitted that as a bug report. Awesome. Alright. Let's refresh this. And we should be now we should be seeing the process that you were hoping for. Yeah. Lots more moves there. So we should be seeing is oh, yeah. Here we go. Awesome. Yeah. It's up. Validation error. Validation error, validation error, validation error. Why is there a validation error on all 4? It has to be one of them. Oh, last must be of type object? I wonder if that is not Oh, of course. Last is the last step. We've foot gummed by using last, which I actually spoke about in a previous episode. What we wanna check is, can you click into read data? The read data Yeah. Key is called get game. Right? So let's maybe rename this game. So if you go up to the key in the top right of this whole pane, scroll up. Yeah. Yeah. And the last will always be the one that came before it. But in this case, we're chaining a bunch. So, like, changes. So we actually wanna change. So if we change this to just game, let's keep it nice and short. Perfect. And and, yeah, save. And now we could do it in is up. The is up last is correct, right? But in all the others, it isn't lost anymore. And then we're just removing this, and it's actually just gonna be game. Right? No Correct. Yeah. Correct. Okay. So let's go No, dollar sign. Yeah. Or no dollar sign. Yeah. There we go. And let's call this one game. Oh, game. And copy that this time. And I'm quite impressed with where we've gotten here. I feel like episode 4 finally figured things out. Yeah. Alright. Here we go. So Yeah. So now so so let's test that once more. And while we're doing that, let's talk about what's next. Because all we're gonna do is execute the most basic strategy, which is to just return, move up, down, left, right. And we're not gonna do anything more fancy here. Right? You would implement is up, not correct. Is down though, is correct, which Awesome. Great. So now out of each of these success paths, now we need to run the strategy, which of course could be a lot more convoluted. Another thing that's worth noting is there is a flow operation, which is to run another flow. So you could you could kind of modularize your strategies into separate flows, go off and call those those come back with the response. And then so, you know, there are ways of organizing your flows. It's not just one big mega flow. But it's I think for what we're doing, it's beyond the scope, but that that would kind of be how you organize game logic, I think. Great. Okay. So what are we gonna do here? What are we we're sending this to We are literally gonna return. So run script. So sorry. I run script was near the top. And the reason we're doing run script just to move and then up and down and left. You got it. That's awesome. You got it. Because in the trigger, we said respond with the last operation. Yeah. I love that. All right. So let's just grab that, save, and let's do oh, yeah. Then that's correct. Yep. That's correct. So that's the is up success path. The names and the keys no longer matter, because If we unless we forget which one where it is down, so this one's gonna be down. Save that, and then we're gonna go here. This is left. I'm glad you you've come with me on the up, down, left, right rather than the clockwise. We got there. We got there eventually. We got there. Alright. And then our final one is gonna be right here. And then this one's gonna be run script and return You got it. Move right. And let's see if this works. So if this works, our move right now is down. So snake should go down if everything is done the way it's supposed to be. So we've been going up because it's been failing. Now we should go down. Alright. This doesn't necessarily mean it's working yet because let's see if we go right. Save. Did we just actually implement this in, like, an effective way? This is wild. I'm, like, kind of Hang on. Hang on. Hit play first. We don't know yet. Yeah. What is up? Snake went right straight off the board and died. This is fantastic. I have never been so happy to see a snake die. I mean, last episode, I was really happy because we really wanted our snake to die. But, like, I'm Oh, yeah. I'm far more happy. That's pretty freaking cool. That is cool. But there was one final part, and then I think we're we we can we can call it on ReadySet Battlesnake, at least season 1 of ReadySet Battlesnake, who knows, which is right now we are saying all users that there is no segmentation. There are no different audiences. It's just everyone gets one response. But in the flow creation, in this start game, you know, flow, we can put in a dynamic value there. Now, I do not know how this works in dev cycle. Is this something that's viable? Yep. Yeah. Yeah. So hold on. Let's go back over and actually look at this. Because you said words and I heard the words, but then I stopped hearing the words. And so let's, let's go and shift this in that flow. And so we could do it in our in our dashboard. Right? We could actually We we we wanna do it in both. So here in the start, right, in the dev cycle operation that we created, if you go in and edit it, you see we put in our as fixed snake. So the question here is right now we're getting I think there's a step missing here, which is we want to register users. No. No. You don't actually so you don't technically need to register them there. Oh, I've gone all blurry. Hold on. Hold the phone. Why have I gone all blurry? What's happening? There we go. Alright. I'm back. I'm back. Okay. So what we can do here is this is all just stuff used for targeting rules. So let's say, okay. So you know how if it doesn't see anything, it goes up. Right? Correct? Not right. I shouldn't say right. If if it Yeah. If it's not seeing anything, it's gonna do that. So let's leave this as ready, set, Battlesnake and go over to the platform here Yes. And say Yes. Go, let's say go left, but only if the user ID contains ready. But but what I'm wondering is, can we get this to a spot? And, like, we can assume the outcome here, like, we'll we'll we'll do this. But what I'm wondering specifically is can we, in 10% of games, maybe this is about the game ID and not the snake ID. So maybe the the name of the property isn't quite right. But can we say in 10% of games or 10% of users, I suppose, can we change it? So in that case, when we first feed it the snake ID, which will make the game ID, is that can we do that? Yeah. It's just this. You literally set it to random distribution, and we say that we wanna serve 25% each of these distributions, and it will automatically do this for all users. So we don't need to do anything with this passing a random name or anything? No. I mean, you could do it within that. You could say only the snakes that have ready in it. Yeah. Exactly. Do this, you know, so you can further branch that logic down and get a real small selection of your audience. Should we remove that, the first set, the first conditional where you put ready? Yeah. Should we remove that, I guess? Because it's kind of just do all users. Yeah. And now we're just saying that all users random distribution. A random distribution. So if I save this, we should see if we run, say, 10 games, we should see something similar to that. So let's make sure this works. That's good. Oh, that was remarkably easy. It is very smooth. But but there is that other thing still where, like, if we if we register, can we say, no, ignore me. That makes part of that same mistake. All of these things are possible. If you can feed information into the platform, then you can you can do lots of stuff with it. And we've only discussed all of these ways of feeding data in, so we don't necessarily need to go too granular. But, yeah, let's create a ton of games. Okay. So here we go. So let's go first game. So we went left first game. Sure. Let's go create rematch. So we're going left. So that's 2 for left. Let's create a rematch. We should start going a different direction, and we're not. Maybe I didn't save. Hold on. Yeah. I was gonna say, did you hit that save button? All users, random distribution, schedule, none. No random distribution. We should be seeing that. That. All users feeding that in. 25%. K. Let me change this. I just wanna see if something happens. A 100%, 0. Oh, you you you have to make it worth what happens if it exceeds a 100 there? I don't I don't know. I'm curious. Yeah. Yeah. So we should see this. Like, all users should ever run a 100% should be there. Random distribution might require something else, but let's see. Yeah. Maybe. No. No. That's right. Yeah. I guess if I may. It must just be the logic that's being used in there. So let's do 5050, and that might be a little bit easier. 50 right, 50 down, and see if that Sure. Does it. That's there. It also does say a couple minutes to update. So let's see. Yeah. Maybe that's it. Maybe there's a little cache. Which is fine. Right. As I say, our snakes aren't spawning in particularly in particularly good spots to live. They're not? This isn't feeling unless it is really that thing because you you don't need feature flags to be literally like sub second. Know, there can be a little bit of a of a moment there. Yeah. So this is over, like, a longer period of time. So that might be part of it, but we should see a down eventually, because the logic that's there is technically correct. Oh, you know what it is? No. I'm wrong. Here's the thing. I I totally forgot about this. So because it feeds in user ID and we're feeding in the same user ID every time. So if we wanted to change this, it's actually not that bad. So that So this is actually what I wanted. Yeah. Yeah. So users and that makes sense because in the real world, you have a feature flag. You don't want that user getting a random thing every time they reload. Yeah. So ready, set, battle, snake was getting right after Oh, no. No. No. No. Hang on. Hang on. Go back. There's a there's a better value to put in here. Let's put in the game ID. Let's put in the because that's random. That's in every new game is a new is a new That's true. User in this context. So actually grab game. Okay. Double double, double brackets first, and it will be trigger dot body dot Game dot ID. Game dot ID because that's a random value each time. Yeah. It is. That's good. Yeah. Yeah. Yeah. That might that might do it. So we should now see 5050 if it works, which it may not. Mhmm. Mhmm. K? Good. So right on the first one, right on the second one. It's not looking good so far. It's not looking good so far, is it? Oh, there we go. Hey. Hey. There we go. So So that logic that I think that's Andrew, I I I'm I'm both really happy that we got this to work, But I'm also kind of sad because I think I think that might bring us. What do you think? I think I think we have we have eaten into to your evenings for many, many dates. Not for that reason. Not for that reason, but just I think that's I think anything more now is us just in hot like, doing things we've already done, but in this slightly altered context, but that's awesome. So you can really, like, yeah. So smooth. Yeah. I gotta say, like, I when we set out to do this, I knew that we might be able I knew Directus would work. Like, that made sense. But when we decided that I was gonna take the lead for the final show and we were gonna do the dev cycle, I was like, I don't know if this is gonna actually work because I tried to implement dev cycle with Directus a lot of ways. So I'm over the moon that this works. And, like, what a cool piece. And it's so fast too. Right? You're not having to worry about direct us doing anything. It's all managed externally. Like Yeah. And and part of this is an approach that we chose. And this is a very battle snake kind of thing. It is an approach we chose to make sure that we are we're not combating that 500 millisecond return require return requirement. And that is because we determined a strategy at the beginning of a game, not every move. So there is a couple more round trips within directors. We're having every move to go and figure out the game strategy, but this is all internal. These are just little database operations. That's awesome. And then, of course, going back to the last episode, we could now enrich our insights dashboards and saying when this was the strategy, this was the outcome. We could use the little global variable drop down to to pick. We could even go off to dev cycle, populate directors with all viable strategies, That was awesome. That was fun. Happy? This was a fun day. I'm I'm so Kevin, I was so it's been a couple episodes since I've been, like, on the keyboard. And, like, you were walking away so pumped after every and I was happy. I was definitely feeling it. But, like, I'm having a good this was a good It's good. This was a good day. We got we got things working, and our snake does the things, and other people can now go and use this to meld the world's all the d's, Directus, Devcycle. Yeah. What what do you say, as for as for Directus as a Battlesnake engine? I love it. I think it's super, super smooth. And I love the fact that there's actually, some code that exists out there for building a dev cycle management setup within the Directus platform. So, like, like, use Directus and then implement dev cycle and then play Battlesnake. That's the real that's the real goal here. Do Everybody wins. Everybody wins. Everybody wins. Everybody wins. Everyone, thank you so much for joining us. Andrew, thank you for getting on board with this idea to to, you know, have a Battlesnake video, you know, series after after a little while. Thank you audience for joining us for this limited series of Ready Set Battlesnake. If you want to learn more about Battlesnake as with all of these episodes down in the resources, you'll find a link to the docs for Battlesnake. It is such a satisfying and fun hobby project for developers at all stages of their career, whether you're just getting started or you are really pushing, you know, your very, very advanced knowledge and it becomes an optimization game. There's something in it for everyone. I'm glad they all worked. Honestly, there were so many points in this where we might have discovered this doesn't work irreparably and the whole thing's ruined. And every barrier we came against, there was a solution primarily actually through the ability to build extensions. Yeah. So that is wonderful. Oh, lovely. And a melding of my world as a big time and long time battlesnake lover and running dev rel, at, director. So Andrew, once again, thank you so much. You're welcome. And should we do it our one last try? This is our last opportunity? So here we go. What do you mean? What do you mean? You have little faith? This is it. You have a So here we go. What do you mean? What do you mean? You have little faith. This is it. You ready? Alright. Here we go. Alright. Do you want to count it down? Should we count it down first? No. Hang on. Hang on. Hang on. Hang on. I've got it. Uh-uh. Alright. This has been ready. Ready. Set. Set. Battlesnake. Battlesnake. Bye for now. Bye.",[3337,3338],"d2934fc9-1a8a-4260-8128-0ea918de1854","95293c50-dd25-47f3-b558-913cbe79f9b4",[],{"year":978,"number":993,"id":3341,"episodes":3342,"show":3346},"398d2955-6ca4-4847-810d-a07f4f48a319",[3343,3344,3345,3317],"08d92984-9c2a-496d-a748-2ba54e64e378","7c58048d-ab8e-42cc-823d-541848ad4a7d","5a14d9c9-0b37-4655-b45a-b4783c0bb92f",{"title":3347,"slug":3348},"Ready Set Battlesnake","ready-set-battlesnake",{"id":3350,"slug":3351,"vimeo_id":3352,"description":3353,"tile":3354,"length":13,"resources":12,"people":12,"episode_number":939,"published":3355,"title":3356,"video_transcript_html":3357,"video_transcript_text":3358,"content":12,"seo":12,"status":19,"episode_people":3359,"recommendations":3360,"season":3361},"a0ebc4d5-a0f5-4922-b043-92e343260b10","jimicolon","893668684","Finally somebody explains the proper usage of the jimicolon.","226390a1-fd76-473b-be31-8a9da85a81c5","2024-05-01","Jimicolon","\u003Cp>Speaker 0: And now Dev Thoughts by Jack Rabbit. I spent 3 hours looking for a bug in my code. It turns out, it was just a missing semicolon. I named it Jimmy. Now every time I forget a semicolon, I tell my code, Jimmy's on vacation again.\u003C\u002Fp>\u003Cp>My code doesn't laugh, but I think deep down, it appreciates the effort.\u003C\u002Fp>","And now Dev Thoughts by Jack Rabbit. I spent 3 hours looking for a bug in my code. It turns out, it was just a missing semicolon. I named it Jimmy. Now every time I forget a semicolon, I tell my code, Jimmy's on vacation again. My code doesn't laugh, but I think deep down, it appreciates the effort.",[],[],{"year":978,"number":263,"id":3362,"episodes":3363,"show":3379},"dd3e52bd-2e7a-4e2d-815f-1dbef51c99d5",[3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3350,3378],"95bbf27d-ff11-4c79-851f-72e61a1d013e","2974ff79-cd83-4741-a1c0-a25ccc286bdc","9b8335c6-27b6-4d6f-a70e-0af5070490a2","a2d63bd2-27a4-4f88-a878-c96dbb871688","b4cef2b4-6ccc-46dc-be10-e8ce4b554f56","3a3c3fe6-b9ca-4b6b-8d47-d6cdaa438e61","172d134b-4460-4e6e-a18c-0c3a522fc1af","09d682c6-d64a-4fa5-9e04-aa7e5a607ddd","fdcc2c29-40f9-48a8-94a1-6e1f0caa132a","d9da97a9-1559-45e7-83c6-a1eb3ddf1fb1","e18220c5-27d1-499c-b29f-7304a56d219d","a620fb85-89e3-423a-b885-b78fb96fee2b","fc54dbbf-d183-452f-b2ce-704df0df978f","c65a069b-f43a-463d-80b8-29a6701e45e6","95d0904b-d9b6-4c68-a0f3-ec9696910360",{"title":3380,"slug":3381},"Dev Thoughts","dev-thoughts",{"id":3378,"slug":3383,"vimeo_id":3384,"description":3385,"tile":3386,"length":13,"resources":12,"people":12,"episode_number":329,"published":3355,"title":3387,"video_transcript_html":3388,"video_transcript_text":3389,"content":12,"seo":12,"status":19,"episode_people":3390,"recommendations":3391,"season":3392},"greg","893668835","Sometimes we find companionship in the least expected places.","9d83fb63-4fe5-41a7-8fbb-879828e732e0","Greg","\u003Cp>Speaker 0: And now, DevThoughts by Jack Rabbit. One time, I left a console log statement in the code by mistake, and it kept outputting hello there every time I ran the program. After a while, it started to feel like I had made a new friend. I named it Greg.\u003C\u002Fp>","And now, DevThoughts by Jack Rabbit. One time, I left a console log statement in the code by mistake, and it kept outputting hello there every time I ran the program. After a while, it started to feel like I had made a new friend. I named it Greg.",[],[],{"year":978,"number":263,"id":3362,"episodes":3393,"show":3394},[3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3350,3378],{"title":3380,"slug":3381},{"id":2492,"slug":3396,"vimeo_id":3397,"description":3398,"tile":3399,"length":195,"resources":12,"people":12,"episode_number":13,"published":3400,"title":3401,"video_transcript_html":3402,"video_transcript_text":3403,"content":12,"seo":3404,"status":19,"episode_people":3405,"recommendations":3407,"season":3408},"wtf-is-a-dxp","941233406","Lately, it seems like DXPs, or digital experience platforms, are all anyone can talk about. But WTF are DXPs (other than a fancy acronym), and why should anyone care about them? Touch grass with Matt as he shares his POV on the origin, meaning, and application of DXPs.","d9230c33-76b6-44e4-a4ec-c8d50f890f04","2024-04-30","WTF is a DXP? ","\u003Cp>Matt: Hey there. I'm Matt. I'm on the marketing team here at Directus. As somebody who's nontechnical and someone who's fairly new to the world of developer tooling and open source, I find myself completely lost when trying to learn, like, new terms, concepts, and a lot of the buzzwords that I've seen a lot lately. You have too.\u003C\u002Fp>\u003Cp>You know, things like DXP, CMS, DMS, BaaS, SAS, composability, mock. I mean, what does it all mean? And as a marketer saying this, is any of it real, or is it just marketing? So, having some discussions with folks in the same position I am, turns out I'm not the only one. So I figured why not film a new series on Directus TV discussing each term, its origin, application, and, to switch things up?\u003C\u002Fp>\u003Cp>I figured I'd touch grass, by filming out here in the in the woods of Virginia. This is real, not a green screen. Sometimes it does feel like you can get lost in the wilderness. First episode, we're discussing a fairly popular acronym I've seen a lot of recently, which is DXP. So in this episode, I wanna dive into WTF a DXP is.\u003C\u002Fp>\u003Cp>What is a DXP? So a DXP is shorthand for digital experience platform. A digital experience platform, or DXP, isn't just a tool. It's a suite of tools. So think of it as, something that's engineered to construct and manage and optimize digital experiences across a bunch of different channels, web, mobile, even in store.\u003C\u002Fp>\u003Cp>But what does that really mean when you get to the core of it for someone like you or me? So imagine you're using an app on your phone or you're browsing a website or you're interacting or kicking a malfunctioning digital kiosk, every smooth interaction, every personalized advertisement, every transition from one platform to another, that's the handiwork of a DXP. It's really about making your digital life as intuitive and enjoyable as possible. The goal is to cater your digital experience exactly to your preferences across every channel, which really isn't a bad thing. I would prefer to get things that I'm interested in as opposed to things I don't care about.\u003C\u002Fp>\u003Cp>So some examples of things in the suite that I mentioned of, like, tools. Obviously, content management system, sees what you sees what you buy or the things that you prefer and changes what you see to suit you better. Analytics tools, obviously, that kind of power everything and let the person know that's running it, what's working and what's not. CRM, where everything is kinda stored, as creepy as that is about you. And then marketing automation tools, you know, email, SMS, things like that.\u003C\u002Fp>\u003Cp>It's a lot like microservices in that, everything is kinda put together, but every touch is about providing you a great personalized experience. Origins of DXP. DXP. So why did DXPs even start? Well, back when the Internet was younger and more naive, websites were pretty simple just like a digital brochure.\u003C\u002Fp>\u003Cp>Shout out to my favorite childhood website from 1996, Space Jam. Still one of the best ever. But as technology evolved and our expectations grew, just having a static website wasn't enough. We wanted more interaction, more personalization, more more more more more, and that's where DXPs came from. They came into play as, like, kind of a beefed up version of the traditional CMS.\u003C\u002Fp>\u003Cp>They provide a rich interactive digital experience, kind of like going from a Nokia phone to a smartphone. Like, suddenly, there's so much more you can do. Real words real world scenario. So let's consider a real world scenario. A university using a DXP to manage its digital footprint.\u003C\u002Fp>\u003Cp>Right? The platform enables them to provide, personalized learning environments, student portals, community engagement platforms, everything across different devices. It enhances the student experience, but also simplifies the back end processes for administration. So imagine having a personalized dashboard that knows your study habits, it recommends resources based on your course load, and connects you with, you know, fellow students in your major. That's something that would be powered by a DXP.\u003C\u002Fp>\u003Cp>So benefits and drawbacks. The the advantages of a DXP are pretty clear. Right? They offer a smoother, more integrated user experience tailored to individual needs. They also allow organizations to manage all their digital stuff from one place, cut down on the chaos, keep things streamlined.\u003C\u002Fp>\u003Cp>But really, it's, as you can guess, like, very complex. It requires a significant investment in time and money. Plus, you need the right skills in your team to keep everything running smoothly, or you have to find an agency to run it for you, and we know those don't always work out. But, yeah. I mean, those are just some of the drawbacks of using a DXP.\u003C\u002Fp>\u003Cp>And, really, that's the lowdown of DXPs. They're powerful platforms designed to make digital experiences, like, really rich. So, hopefully, that was helpful for you. That's what I've learned so far. Obviously, I'm still learning, so I'll probably do another episode of this, once I learn more.\u003C\u002Fp>\u003Cp>But, yeah, hopefully, it was helpful. I appreciate you joining me out here in the woods in this 90 degree Fahrenheit weather. If I'm sweaty, I apologize, but I appreciate the time today. I'll see you next time.\u003C\u002Fp>","Hey there. I'm Matt. I'm on the marketing team here at Directus. As somebody who's nontechnical and someone who's fairly new to the world of developer tooling and open source, I find myself completely lost when trying to learn, like, new terms, concepts, and a lot of the buzzwords that I've seen a lot lately. You have too. You know, things like DXP, CMS, DMS, BaaS, SAS, composability, mock. I mean, what does it all mean? And as a marketer saying this, is any of it real, or is it just marketing? So, having some discussions with folks in the same position I am, turns out I'm not the only one. So I figured why not film a new series on Directus TV discussing each term, its origin, application, and, to switch things up? I figured I'd touch grass, by filming out here in the in the woods of Virginia. This is real, not a green screen. Sometimes it does feel like you can get lost in the wilderness. First episode, we're discussing a fairly popular acronym I've seen a lot of recently, which is DXP. So in this episode, I wanna dive into WTF a DXP is. What is a DXP? So a DXP is shorthand for digital experience platform. A digital experience platform, or DXP, isn't just a tool. It's a suite of tools. So think of it as, something that's engineered to construct and manage and optimize digital experiences across a bunch of different channels, web, mobile, even in store. But what does that really mean when you get to the core of it for someone like you or me? So imagine you're using an app on your phone or you're browsing a website or you're interacting or kicking a malfunctioning digital kiosk, every smooth interaction, every personalized advertisement, every transition from one platform to another, that's the handiwork of a DXP. It's really about making your digital life as intuitive and enjoyable as possible. The goal is to cater your digital experience exactly to your preferences across every channel, which really isn't a bad thing. I would prefer to get things that I'm interested in as opposed to things I don't care about. So some examples of things in the suite that I mentioned of, like, tools. Obviously, content management system, sees what you sees what you buy or the things that you prefer and changes what you see to suit you better. Analytics tools, obviously, that kind of power everything and let the person know that's running it, what's working and what's not. CRM, where everything is kinda stored, as creepy as that is about you. And then marketing automation tools, you know, email, SMS, things like that. It's a lot like microservices in that, everything is kinda put together, but every touch is about providing you a great personalized experience. Origins of DXP. DXP. So why did DXPs even start? Well, back when the Internet was younger and more naive, websites were pretty simple just like a digital brochure. Shout out to my favorite childhood website from 1996, Space Jam. Still one of the best ever. But as technology evolved and our expectations grew, just having a static website wasn't enough. We wanted more interaction, more personalization, more more more more more, and that's where DXPs came from. They came into play as, like, kind of a beefed up version of the traditional CMS. They provide a rich interactive digital experience, kind of like going from a Nokia phone to a smartphone. Like, suddenly, there's so much more you can do. Real words real world scenario. So let's consider a real world scenario. A university using a DXP to manage its digital footprint. Right? The platform enables them to provide, personalized learning environments, student portals, community engagement platforms, everything across different devices. It enhances the student experience, but also simplifies the back end processes for administration. So imagine having a personalized dashboard that knows your study habits, it recommends resources based on your course load, and connects you with, you know, fellow students in your major. That's something that would be powered by a DXP. So benefits and drawbacks. The the advantages of a DXP are pretty clear. Right? They offer a smoother, more integrated user experience tailored to individual needs. They also allow organizations to manage all their digital stuff from one place, cut down on the chaos, keep things streamlined. But really, it's, as you can guess, like, very complex. It requires a significant investment in time and money. Plus, you need the right skills in your team to keep everything running smoothly, or you have to find an agency to run it for you, and we know those don't always work out. But, yeah. I mean, those are just some of the drawbacks of using a DXP. And, really, that's the lowdown of DXPs. They're powerful platforms designed to make digital experiences, like, really rich. So, hopefully, that was helpful for you. That's what I've learned so far. Obviously, I'm still learning, so I'll probably do another episode of this, once I learn more. But, yeah, hopefully, it was helpful. I appreciate you joining me out here in the woods in this 90 degree Fahrenheit weather. If I'm sweaty, I apologize, but I appreciate the time today. I'll see you next time.","92aee796-fd09-416d-a679-f76e40a88b7b",[3406],"d6068dca-119d-46f9-a0ce-2f87b2f624f6",[],{"year":978,"number":674,"id":2490,"episodes":3409,"show":3410},[2492,2493,2494,2495,2476],{"title":2497,"slug":2498},{"id":2753,"slug":3412,"vimeo_id":3413,"description":3414,"tile":3415,"length":11,"resources":12,"people":12,"episode_number":195,"published":3416,"title":3417,"video_transcript_html":3418,"video_transcript_text":3419,"content":12,"seo":12,"status":19,"episode_people":3420,"recommendations":3422,"season":3423},"chickfila-theme","931472203","Theming can seem hard on the surface. If you get stuck waffling back and forth on color palettes and font choices, then you need to be chikin out this episode. It's our pleasure to present the \"Chick-fil-A theme\". Join host Bry Ross as he paints a happy little theme for the king of the chicken sandwich.","151c0c30-3116-48d8-945a-bcb47291fbce","2024-04-29","Chick-fil-A (Eat Mor Chikin)","\u003Cp>Speaker 0: Hi. Welcome to the show. I'm your host, Bry Ross. And for the next few moments of this beautiful day, you and I together will experience the joy of theming. Now, before we get started on today's painting, the beautiful Chick Fil A theme, I just wanted to cover a few of the tools of the trade so that you feel right at home.\u003C\u002Fp>\u003Cp>You can paint beautiful themes with just about anything, but here's what I like to use. First, I have my trusty Logitech MX Keys keyboard. It's a rather heavy keyboard but it does have a number pad. So hex code entry, lightning fast. Next up, we have our trusty pallet knife.\u003C\u002Fp>\u003Cp>Now, this is not just any pallet knife. It is a firm pallet knife and it is great for painting the broad strokes of whatever theme that we're working with today. Last but not least, we have our half inch brush. Now this is perfect for painting all the details. Our border radius, border radii, depending on your preferred spelling, or, font sizes.\u003C\u002Fp>\u003Cp>It's perfect for that. Really easy to paint all the detailed strokes that you need. So those are our tools of the trade. Let's dive in to creating a wonderful, happy little theme together. Alright, so let's dive right in.\u003C\u002Fp>\u003Cp>Alright, first up, we're just going to navigate to our settings and we're gonna enter in a beautiful project name. Let's just call it Chick Fil A. Now we'll set a project descriptor, not description, but descriptor for the Latin variety, and that will be our internal CMS and app. And then we're just grab the URL from the browser and we'll paste that into project URL. Next, we're ready to paint our theme.\u003C\u002Fp>\u003Cp>So we're gonna go to the appearance section in our Directus instance and let's pick up the primary color from the website. So I'll just inspect and grab that. Great. Now we're gonna get the logo. So I can do this by inspecting that element, looking for the CSS and boom, there we go.\u003C\u002Fp>\u003Cp>We've found the SVG for Chick Fil A logo. It's a glorious logo. Just a fine beautiful example of a logo. Now next we'll add a public background. So the public background is what displays on the login page And anytime you want to entice people to log in, they need a great looking login page.\u003C\u002Fp>\u003Cp>We'll add a quick little public note. And wow, this is starting to look really nice. Now I'm just gonna steal the favicon here, favicon. I never know the spelling. I argued with somebody about that at the mall the other day.\u003C\u002Fp>\u003Cp>But now let's save our appearance. And it looks like our logo is not showing in the top left corner like we want it to. So, luckily, I can go in and change the CSS inside Directus as well. Not just the the default theming, but I can go in and change the CSS to be whatever I want. So, in this case, I'm gonna copy the class, which is a CSS selector, and paste that into custom CSS.\u003C\u002Fp>\u003Cp>And then we'll just set a nice, lovely background color of cream white. Now, since we're gonna be changing colors soon, anytime you change colors on your your pallet, you wanna make sure you take your duster and just dust the demons out of it. Just dust it real good like that and then you're ready. Alright. So now we've picked our light theme and now we're just gonna step into painting the broad strokes of this theme.\u003C\u002Fp>\u003Cp>So we are now working with the minimal theme inside Directus. This is one of the default themes that comes out of the gate and it it just, it looks great. So we're going to set the primary color so that our save buttons look glorious, that that wonderful deep chick fil a red and we'll use that blue, it's like a navy, just so deep you get lost in it when you stare at it. We're gonna use that navy blue as the foreground which will show up on the titles and the headers. Great.\u003C\u002Fp>\u003Cp>Looking magnificent, you know, and when you're painting themes like this there are no mistakes. You know, if you mistake a border radius by 4 or 5 pixels, it's a happy accident because the results can be wondrous. So now this theme is looking really great and we want to go back in and paint some of the finer details like our fonts. So the Chick Fil A website uses a very specific font that very likely you would have to pay for because it's a very, very fine exquisite font. So I'm just gonna go in here and just paint that font CSS in there and we'll just pick that up from the Chick Fil A website.\u003C\u002Fp>\u003Cp>Let's just paint it in there. At font face, we're gonna use the font family apercou apercu, again, latin spelling. And the font weight here is gonna be 400, that's just the regular standard font weight we like to use when we're doing the details and we can inspect the network requests from the Chick fil A site to actually get the URL for that font. In this case it looks like a CDN CloudFront. No big deal.\u003C\u002Fp>\u003Cp>So when we refresh, now you can see all those beautiful Chick Fil A fonts that line up with the website perfectly, look great inside our app, inside our CMS that we're building. Alright. So now that we're into the fine detail as well, let's let's fix those border radius, those border radii. I never know what to call it. Just get so lost in theming sometimes.\u003C\u002Fp>\u003Cp>So I noticed on the navigation there's a very, so a full border radius, but on some of the other buttons, it is 12 pixels. So we'll just paint those 12 little pixels in there. I I don't like using decimal points in my pixels. Just messes with the whole flow, the whole zen. Alright.\u003C\u002Fp>\u003Cp>So now let's go in and log out so we can check out that login page. This is the public login page. Oh, wow. Is it beautiful? So that is a lovely theme.\u003C\u002Fp>\u003Cp>But, oh, wait. There is one little accident that we need to fix here. So, again, the logo is not displaying on the public login page because it it is hidden by this red background. So we're just gonna pull out that detail brush and just paint in that new CSS and boom, there it is. You can clearly see it now.\u003C\u002Fp>\u003Cp>This is perfect. I really love the feel of this Chick Fil A theme, and I hope you've learned a ton here. Each one of us has a perfect theme inside of us. I'm hoping that through this show, we can help you express that perfect theme. I'll see you next time.\u003C\u002Fp>","Hi. Welcome to the show. I'm your host, Bry Ross. And for the next few moments of this beautiful day, you and I together will experience the joy of theming. Now, before we get started on today's painting, the beautiful Chick Fil A theme, I just wanted to cover a few of the tools of the trade so that you feel right at home. You can paint beautiful themes with just about anything, but here's what I like to use. First, I have my trusty Logitech MX Keys keyboard. It's a rather heavy keyboard but it does have a number pad. So hex code entry, lightning fast. Next up, we have our trusty pallet knife. Now, this is not just any pallet knife. It is a firm pallet knife and it is great for painting the broad strokes of whatever theme that we're working with today. Last but not least, we have our half inch brush. Now this is perfect for painting all the details. Our border radius, border radii, depending on your preferred spelling, or, font sizes. It's perfect for that. Really easy to paint all the detailed strokes that you need. So those are our tools of the trade. Let's dive in to creating a wonderful, happy little theme together. Alright, so let's dive right in. Alright, first up, we're just going to navigate to our settings and we're gonna enter in a beautiful project name. Let's just call it Chick Fil A. Now we'll set a project descriptor, not description, but descriptor for the Latin variety, and that will be our internal CMS and app. And then we're just grab the URL from the browser and we'll paste that into project URL. Next, we're ready to paint our theme. So we're gonna go to the appearance section in our Directus instance and let's pick up the primary color from the website. So I'll just inspect and grab that. Great. Now we're gonna get the logo. So I can do this by inspecting that element, looking for the CSS and boom, there we go. We've found the SVG for Chick Fil A logo. It's a glorious logo. Just a fine beautiful example of a logo. Now next we'll add a public background. So the public background is what displays on the login page And anytime you want to entice people to log in, they need a great looking login page. We'll add a quick little public note. And wow, this is starting to look really nice. Now I'm just gonna steal the favicon here, favicon. I never know the spelling. I argued with somebody about that at the mall the other day. But now let's save our appearance. And it looks like our logo is not showing in the top left corner like we want it to. So, luckily, I can go in and change the CSS inside Directus as well. Not just the the default theming, but I can go in and change the CSS to be whatever I want. So, in this case, I'm gonna copy the class, which is a CSS selector, and paste that into custom CSS. And then we'll just set a nice, lovely background color of cream white. Now, since we're gonna be changing colors soon, anytime you change colors on your your pallet, you wanna make sure you take your duster and just dust the demons out of it. Just dust it real good like that and then you're ready. Alright. So now we've picked our light theme and now we're just gonna step into painting the broad strokes of this theme. So we are now working with the minimal theme inside Directus. This is one of the default themes that comes out of the gate and it it just, it looks great. So we're going to set the primary color so that our save buttons look glorious, that that wonderful deep chick fil a red and we'll use that blue, it's like a navy, just so deep you get lost in it when you stare at it. We're gonna use that navy blue as the foreground which will show up on the titles and the headers. Great. Looking magnificent, you know, and when you're painting themes like this there are no mistakes. You know, if you mistake a border radius by 4 or 5 pixels, it's a happy accident because the results can be wondrous. So now this theme is looking really great and we want to go back in and paint some of the finer details like our fonts. So the Chick Fil A website uses a very specific font that very likely you would have to pay for because it's a very, very fine exquisite font. So I'm just gonna go in here and just paint that font CSS in there and we'll just pick that up from the Chick Fil A website. Let's just paint it in there. At font face, we're gonna use the font family apercou apercu, again, latin spelling. And the font weight here is gonna be 400, that's just the regular standard font weight we like to use when we're doing the details and we can inspect the network requests from the Chick fil A site to actually get the URL for that font. In this case it looks like a CDN CloudFront. No big deal. So when we refresh, now you can see all those beautiful Chick Fil A fonts that line up with the website perfectly, look great inside our app, inside our CMS that we're building. Alright. So now that we're into the fine detail as well, let's let's fix those border radius, those border radii. I never know what to call it. Just get so lost in theming sometimes. So I noticed on the navigation there's a very, so a full border radius, but on some of the other buttons, it is 12 pixels. So we'll just paint those 12 little pixels in there. I I don't like using decimal points in my pixels. Just messes with the whole flow, the whole zen. Alright. So now let's go in and log out so we can check out that login page. This is the public login page. Oh, wow. Is it beautiful? So that is a lovely theme. But, oh, wait. There is one little accident that we need to fix here. So, again, the logo is not displaying on the public login page because it it is hidden by this red background. So we're just gonna pull out that detail brush and just paint in that new CSS and boom, there it is. You can clearly see it now. This is perfect. I really love the feel of this Chick Fil A theme, and I hope you've learned a ton here. Each one of us has a perfect theme inside of us. I'm hoping that through this show, we can help you express that perfect theme. I'll see you next time.",[3421],"5719725d-1143-41e9-b94d-80d4e5df80ee",[],{"year":978,"number":2745,"id":2746,"episodes":3424,"show":3425},[2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2731],{"title":2760,"slug":2761},{"id":2314,"slug":3427,"vimeo_id":3428,"description":3429,"tile":3430,"length":3431,"resources":12,"people":12,"episode_number":177,"published":3432,"title":3433,"video_transcript_html":3434,"video_transcript_text":3435,"content":12,"seo":3436,"status":19,"episode_people":3437,"recommendations":3439,"season":3440},"equipment-booking-manager","936331509","In a fast and furious hour, Bryant tackles building an equipment booking manager. Watch as he designs and implement a system to manage videography and photography gear, including features for checking items in and out, reserving equipment, and maintaining a comprehensive inventory dashboard.","f1a4f4a6-b473-4d02-a1c6-17474dad5c02",60,"2024-04-26","Mission: Equipment Booking Manager ","\u003Cp>Speaker 0: Hi. Welcome back to another episode of 100 apps, 100 hours, where we build some of your favorite apps or publicly fail trying. Today we've got a really neat use case that we're gonna try to build, an equipment booking manager. This sounds really fancy. I've seen this topic come up a few times within our direct us community, and I I thought, hey, let's tackle it today.\u003C\u002Fp>\u003Cp>It's perfect. So in all of the research I did, which is basically about 5 minutes before I pressed record on this, I found a comparable solution. It's called Checkroom, c h e q, Room. Looks like a pretty neat solution, but basically it's an internal app that allows you to manage all of your equipment. So equipment, in this case, it looks like they're targeting videographers, photography, you know, maybe studios, anybody who's got expensive camera equipment or video cameras, etcetera.\u003C\u002Fp>\u003Cp>But basically, you know, you can check these items in and out, reserve them for a time slot, manages all your inventory, what's the the status of all those, gives you a nice friendly dashboard. So that's what we're gonna be building today. If this is your first time watching 100 Apps 100 Hours, there are 2 rules. Number 1, we have 60 minutes to plan and build, so we're on a bit of a time crunch. There's no more, no less.\u003C\u002Fp>\u003Cp>And rule number 2 is, kind of, the anti rule. Use whatever we have at our disposal. So with that let's build our equipment booking manager. I'm gonna start the clock here and away we go. So I'm inside, Figma here or FigJam.\u003C\u002Fp>\u003Cp>This is how I like to plan before I build, just kind of quickly whiteboard things. So let's go in and just flesh out the the actual functionality that we're looking for out of this specific app. Right? We wanna track inventory of all of our different items, of all equipment and items. This could be, like, mobile phones or Android devices if we're doing web development for testing, camera equipment, printers, what whatever.\u003C\u002Fp>\u003Cp>We wanna be able to reserve equipment equipment on a schedule. We want to be able to track, details about the equipment. Let's say user can reserve the equipment on a schedule. We're gonna be able to track details of track equipment status. I mean, that's that's pretty much the the bare bones for this.\u003C\u002Fp>\u003Cp>You know, when are these things scheduled? Is it available for checkout? You know, maybe we wanna account for things like maintenance. Put, like, a big question mark there. Yeah.\u003C\u002Fp>\u003Cp>Okay. That that seems pretty good. Let's let's dive in and actually start planning what our data model may look like for something like this. So I'm just going to drag a nice square over here. Let's change this to red to kind of match.\u003C\u002Fp>\u003Cp>Alright. So thinking about this and and just talking it through out loud, obviously we have our inventory. This could be called items, could be called equipment. Let's let's go with equipment. That that seems like a a good compromise there.\u003C\u002Fp>\u003Cp>Items has a, kind of, a specific meaning inside Directus, which is just the actual individual pieces of content or data. So let's go with equipment, and then we're going to have, users. Alright. So that's gonna come actually from a Directus underscore user collection that Directus is gonna give us out of the box, which again is really nice. We got that authentication tied to that as well.\u003C\u002Fp>\u003Cp>And then we're gonna have, what, bookings for each of the this piece of equipment. Right? So we have a relationship here between equipment and bookings. The user creates a booking for a specific piece of equipment. You know, we have a check-in, check out type of functionality.\u003C\u002Fp>\u003Cp>It's great. Yeah. And this could be, you know, one of the other things that I saw here on the check room website, where was this? Inventory. One inventory to rule them all.\u003C\u002Fp>\u003Cp>You could potentially bundle these things up. Capture the flag. So, you know, maybe some type of checklist. Right? Let's go through and and just start fleshing this out.\u003C\u002Fp>\u003Cp>Right? We're about 4 minutes in. We need to actually start building something. So I'm gonna pull up my instance of Directus, so this side by side. We'll zoom in just a little bit.\u003C\u002Fp>\u003Cp>And this is just a blank direct assistance that I've spun up here locally, if I can remember the actual password. I think it's real secure. It's probably just password. Great. So we've got a totally blank instance of Directus.\u003C\u002Fp>\u003Cp>Let's start building something. Let's start with equipment. Right. We're going to just call this table equipment. Usually I've got a plural at the end of a lot of my tables, but equipments doesn't make a ton of sense to me.\u003C\u002Fp>\u003Cp>So, what are we going to have on this? We're going to have a status, like the default status field here. These just make it easy to add some of the most common functionality you'll have. It uses, like, a draft, published, archived. So I'm just gonna leave that blank for now.\u003C\u002Fp>\u003Cp>The sort field, do we really need a a sort for our equipment? Probably not. It might be good to track when it was created, so when it was added to the database, who it was created by, and the last time that information was updated. So we'll just roll with that. And let's give this, a name, right?\u003C\u002Fp>\u003Cp>This is the name of this piece of equipment, it's just gonna be a string. Really simple. No need to get fancy with it. Let's require a name for it. And next, let's probably add a a serial number.\u003C\u002Fp>\u003Cp>We need a unique identifier for this. You know, maybe we want, we end up printing these on labels or QR codes. Serial number would be a good way to track that. And, you know, we can choose to require that as a value, you know, maybe we don't necessarily need to, but we'll add that. What else do we wanna add here?\u003C\u002Fp>\u003Cp>We want to add a description. So maybe that's gonna be a wysiwyg editor. You know, this could be a HTML description, so we'll go with that. The type is gonna be text. I could customize my toolbar if I want to limit the options that we've got, but we'll just roll with the the default selections for now.\u003C\u002Fp>\u003Cp>We're probably going to have, what, some images of this or at least one image. You know, maybe we've got a gallery, but let's just call this the image. That's gonna be a relational field, Just a mini to 1 to our directus files collection. Make sure we can add an image for it. What else am I forgetting?\u003C\u002Fp>\u003Cp>We've got a status field. So, let's use a drop down for this. We'll call it status. The type, we're just gonna store that as a string. And for our choices, right, when we think through the choices, the equipment is either currently available, it is unavailable, you know, maybe it could be like, hey, it's getting serviced or it's in maintenance.\u003C\u002Fp>\u003Cp>I'm not sure if that's worthwhile to track or not. Alright. So let's just add a new one. We're gonna call it available. For the value, we can use lower case.\u003C\u002Fp>\u003Cp>Let's give a, maybe, a check mark. Hey. This is available. We'll make it green. Then we'll have unavailable.\u003C\u002Fp>\u003Cp>I'm sure there's probably a better naming convention here, but this is what we'll roll with. Maybe like a x. Okay. This is unavailable. And, yeah, maybe we do it like a status for maintenance just to differentiate.\u003C\u002Fp>\u003Cp>Maintenance. Wrench, maybe. Let's look for a wrench repair. There we go. We got a toolbox.\u003C\u002Fp>\u003Cp>That'll work. Okay. And I think yellow or orange is a good color for that. Great. So we've got available, meaning it's currently in inventory.\u003C\u002Fp>\u003Cp>Unavailable means it's out, Not in inventory. And then we have maintenance. Don't need to allow any other values. Looks great. Trying to think of what else we actually need for this data model.\u003C\u002Fp>\u003Cp>You know, we may have, like, specific details that we wanted to track, those are escaping me at this particular moment. So we've got equipment. Let's go in and add a few pieces of equipment. You know, we'll just start out and say, this is available. We have a, just looking at my desk here, I have an iPad Pro that is BG iPad Pro.\u003C\u002Fp>\u003Cp>Just create a serial number. The most amazing iPad you've ever seen. And we'll just go to Google, and it's still an image. Right? IPad Pro.\u003C\u002Fp>\u003Cp>Perfect. Where are you? Apple's gonna sue me, I'm sure. Please don't. Alright.\u003C\u002Fp>\u003Cp>Swipe an image here. That's great. We've got an iPad Pro. I may want to adjust my view just a little bit. I can't I don't see those nice images.\u003C\u002Fp>\u003Cp>I could go in and add my thumbnail for this. Looks great. Cool. We can make this a larger view. We can make it comfortable, and I can see a little more of that.\u003C\u002Fp>\u003Cp>Or I could even go straight to cards view here and get, just kind of like a gallery of where we're at. So the subtitle here could be the status, it's available. Great. We could add one more piece of equipment. You know, maybe I've got a, show my age here.\u003C\u002Fp>\u003Cp>The Sony a 64100. Sony a 64100. We could call this 1. Great. So we'll find a image of the Sony.\u003C\u002Fp>\u003Cp>I think it's the a 64100. Yep. That's the one. Copy image address. We'll just pull that in.\u003C\u002Fp>\u003Cp>Great. And, yeah, maybe I've got something like 2 of these cameras. How can I, like, achieve that really quickly? How can I duplicate these inside Directus? Well, what I could do is just go ahead and change the name a little bit, change the serial number, and I could go into the 3 dots up here at the top and click save as copy.\u003C\u002Fp>\u003Cp>So now I've got 2 of those specific cameras, maybe one's available, one's not, one's currently checked out, the other isn't. Cool. Alright. So we've got some equipment. What is the next thing that we're going to build?\u003C\u002Fp>\u003Cp>Let's take a look at bookings. Right? So how is this going to work? We're going to maintain a schedule of bookings. We're probably going to use our calendar view inside Directus for this, and then we'll be able to check-in and out this equipment, or actually reserve this equipment.\u003C\u002Fp>\u003Cp>The check-in, check out part may be, something else a little different. So let's just call this table bookings, makes sense. For the primary key we'll just use an ID. I'll use generated UUID for that. For the status, right, Let's leave status off here.\u003C\u002Fp>\u003Cp>We're just gonna check the box for all of the date created, date updated, all these system fields that auto populate when certain actions happen. And now we've got a booking, right, so when is the we we need the times for the booking. So let's do, like, a scheduled from. We use a time stamp format just so we get the time zone value. K.\u003C\u002Fp>\u003Cp>Let's make that high second half width. We'll do schedule 2 for that as well. Schedule from, schedule 2. Do we have notes? You know, I'm thinking, like, hey, we may have to have some type of notes.\u003C\u002Fp>\u003Cp>We'll use a text area for that. I don't need these to be formatted in a WYSIWYG or markdown. You know, could be potentially nice if you need that level of functionality. Alright. So we've got a booking, we've got schedule from, schedule to.\u003C\u002Fp>\u003Cp>We need, obviously, a piece of equipment tied to this booking. So now we're gonna start reaching for our relationships inside Directus, because each booking could only have a single piece of equipment, or could it? You know, we could potentially book a 3 or 4 pieces of equipment, but maybe those would be individual bookings as well. You know, it gets more complicated when you have a kit. Maybe we'll try to explore that.\u003C\u002Fp>\u003Cp>But for now, let's just keep it simple. We'll do a mini to 1. Or would it be, yeah, let's do mini to 1 for now. The related collection will be equipment. And we'll go into the relationship here.\u003C\u002Fp>\u003Cp>I can see what this setup looks like, but I can go in and also add that inverse relationship. So, I can add equipment to my bookings or add bookings inside my equipment. So I if I pull up a piece of equipment, I can see all the bookings right inside that item detail page that that that particular piece of equipment has had. Alright. Long winded explanation there, Brian.\u003C\u002Fp>\u003Cp>Alright. So we've got a schedule from, schedule 2, notes, equipment. Who is this assigned to, or who's pulling this equipment? I again, naming things is is probably the hardest challenge in development. Let's just call this assigned to or bookie.\u003C\u002Fp>\u003Cp>Call here bookie. Let's call it assign to. That's fine. So now I can't see my users collection here. Directus users is actually collection.\u003C\u002Fp>\u003Cp>So we're just gonna go in and expand that system drop down, pick Directus users. I could control the display template here, so what actually shows up. You know, maybe I want to show the image of the user, the first name, the last name. Great. And save it.\u003C\u002Fp>\u003Cp>Alright. So we've got the schedule from, schedule to. We probably need a status on this as well. Right? So who is this booking for?\u003C\u002Fp>\u003Cp>Alright. The status, when we think about that, again, let's use a drop down just because we get, like, a rich look at it, now that we have the ability to add icons and colors. But when I think about this, let's say, what's the initial state on a booking? Would be new or maybe unconfirmed is what I'm thinking here. So a new booking comes in, somebody has to verify that that booking is okay, or maybe we could do that automatically via flow.\u003C\u002Fp>\u003Cp>But we'll call it unconfirmed. And let's just see if we've got, like, a there we go. We've got a new flow. Fiber new. That's an interesting name for that particular icon.\u003C\u002Fp>\u003Cp>What else do we have? Scheduled. Scheduled. K. Time.\u003C\u002Fp>\u003Cp>See what we got. Okay. Let's just do, like, blue. That's kind of like a neutral color as far as what's going on. You know, maybe in progress is the next one.\u003C\u002Fp>\u003Cp>We'll just create some text and value for that. Settings. Maybe, like, a gear, progress bar, meter, bar. Yeah. There we go.\u003C\u002Fp>\u003Cp>That'll work. Alright. We'll mark that as I guess, as green in the state of a booking. Maybe it's red. In progress.\u003C\u002Fp>\u003Cp>Maybe completed is the state. Alright. So this is a completed booking. Check mark. I think that's a that'll be a nice icon for this.\u003C\u002Fp>\u003Cp>Alright. Completed booking. We'll go to check mark. We'll make that green. Great.\u003C\u002Fp>\u003Cp>Okay. So now we've got our status field filled out. We'll hit save, and now we can start looking at at how this is gonna come together. So we've got equipment, we've got our bookings, you know, maybe we look at this on a calendar view. Our layout options here we're gonna adjust this view.\u003C\u002Fp>\u003Cp>Each one of these layouts inside Directus has, separate configuration options that are dependent on the specific view. So here maybe we display, let's see. We're gonna display the equipment name. That makes sense to me. And then let's also maybe display the user as well, so who this is assigned to.\u003C\u002Fp>\u003Cp>Assign to first name. Assign to so we'll reach into that related collection, and I can display those related values. I thought I had added that. Assign to last name. Oh, I'm just not seeing it there.\u003C\u002Fp>\u003Cp>First name, assign to first name. I added it, I just didn't see it. Last name. Okay. Alright.\u003C\u002Fp>\u003Cp>The start date field is gonna be scheduled from, scheduled to. 1st day of the week is Sunday, and then we're good. Right? So let's go in and create our first booking. We'll just do this manually.\u003C\u002Fp>\u003Cp>And I can actually do default values as well, one of the handy things about when when you're setting up your data model inside Directus. If I go into my status, I've got unconfirmed as a value. If I go to my schema, I can just set the default value to be unconfirmed, and now whenever I create a new booking, the default is unconfirmed. Alright. So we're going to pick a piece of equipment.\u003C\u002Fp>\u003Cp>Let's say we want this Sony a 64100. That's gonna be assigned to me. I wanna check this out, for 2 days at near the end of this month and using for a photo shoot. Great. So now I could see that booking on my calendar, and presumably as these bookings come in, you know, I could go in and and see this schedule.\u003C\u002Fp>\u003Cp>Right? This is really a bare bones functionality. You know, I I could give access to our team to this and set up some separate permissions, but now, I let's maybe add some automation to manage this booking process. Right? It seems kind of hey, the flow doesn't seem quite right to me.\u003C\u002Fp>\u003Cp>So we wouldn't have somebody go in and manually adjust the status for their booking. You know, maybe we can take advantage of Directus flows and and build something to to, account for this. Right? So we'll go inside flows, and let's just let's think about how to manage the bookings. Each one of these specific bookings, has some actions that you're taking against it.\u003C\u002Fp>\u003Cp>So to me, if you're used to building, like, inventory systems, you probably got some type of transaction against that inventory of, hey, this was checked in, this was checked out, it was reserved, or it the on hand quantity was reduced. So maybe we do something like that where we track the individual transactions against these bookings of when it was checked out, when it was checked in. Let's do that. So we'll do bookings underscore transactions, and I'll just use UUID again. Just kind of the standard for me.\u003C\u002Fp>\u003Cp>Maybe the created on do we really need, alright. We're not gonna update these transactions. So, yeah, if we're created on, I'm just gonna change this to timestamp because that is the timestamp this particular transaction occurred. We'll use created by just so we have the user created that particular transaction, so if that was me checking myself in or somebody else checking in that booking. Alright.\u003C\u002Fp>\u003Cp>So at that point we also are gonna have a relationship to our bookings. So let's go ahead and set that up. Here it's going to be a mini to one relationship from booking transactions to bookings, because, a a transaction only has one booking, but a booking could have many different transactions attached to it. So we'll call this the booking, and our related collection is gonna be bookings. And for the display template, let's say that is the equipment name, space, date, schedule from.\u003C\u002Fp>\u003Cp>And we do schedule to. And if I wanted to, I can even go in and add the the specific user. So we'll do first name, assign to last name. Perfect. Alright.\u003C\u002Fp>\u003Cp>Cool. So now we've got a booking, and I forgot to create the inverse relationship. There's when you're creating that mini to 1, there's a second tab for that, but no fear. We can just go into our bookings and and we can add that from here. So we'll do the one to many field.\u003C\u002Fp>\u003Cp>So this is the inverse relationship. We'll call this our transactions. And let's do booking transactions and the foreign key we'll already have, which is the booking. So this is the foreign key for bookings inside transactions. We'll just save that, maybe show a link to it and hit save.\u003C\u002Fp>\u003Cp>Alright. So now if we take a look at one of our bookings we can see this transaction table. Alright. The other thing that we're probably gonna need on our booking transactions, and honestly, like, this may be hidden behind the scenes, and I'm just gonna go in and add some icons to this before it drives me crazy. Recovering designer folks.\u003C\u002Fp>\u003Cp>Bear with me. Camera. There we go. Looks great. Alright.\u003C\u002Fp>\u003Cp>So on our booking transactions, and I may even just nest that, we're gonna need a action type. Alright. So it's a check-in, check out. You know, move to maintenance, maybe, as well. For this, let's try something like radio buttons.\u003C\u002Fp>\u003Cp>So this will be the action. The choices that we'll have will be check-in, the value here, check underscore in, Check out. Check out. Maintenance. Move to maintenance.\u003C\u002Fp>\u003Cp>Request maintenance. Let's just call it maintenance. I'm getting too fancy with it. Taking too much time when I'm actually maintenance. I'm not even sure if that's the correct spelling.\u003C\u002Fp>\u003Cp>That's what we're rolling with. Alright. So now, what have we done? We've got some equipment, we've got bookings we can use to manage that, and then we can attach transactions to those. Right?\u003C\u002Fp>\u003Cp>So as far as our underlying data model, this looks pretty cool. Let's work on our our flow. Right? How can we make this easier? So I can imagine, like, if I'm looking up bookings, you know, maybe I want to bookmark this.\u003C\u002Fp>\u003Cp>This is the calendar view. So we'll add a calendar icon for that as well. Alright. So when I save a bookmark, that gives me the calendar here. And then maybe I can go in and maybe we just want, like, a list of all the bookings as well.\u003C\u002Fp>\u003Cp>This could be a new potential bookmark. Let's go in. We don't wanna update that original bookmark. I go back to bookings. We'll just create a bookmark for this.\u003C\u002Fp>\u003Cp>We'll call this list. This is a list of bookings. Great. So one of the other nice things about creating these bookmarks inside Directus is if you have certain filters applied or, you know, even a search query, you could save that search query and create these really nice slices of your data. But as far as my list view, you know, maybe I want to clean this up a little bit.\u003C\u002Fp>\u003Cp>My assign to field looks kind of boring, my equipment is not showing. Let's just make this look a little nicer. So I'm going to go back inside my data model and where I have equipment I'm gonna look at my interface and display. So here we have our interface, which is gonna be how it looks inside the detail view. The display is gonna be how it presents on the layout.\u003C\u002Fp>\u003Cp>So I could go in and maybe we want to show the, image, And I can expand that a little bit just to get a a thumbnail. And we'll show the name of this, and we'll do the same here. One of the little tricks I can use here is just drop this down, copy raw value, we can also paste it here and get access to the same thing. Great. As far as the assign to, maybe we want to do the same thing.\u003C\u002Fp>\u003Cp>Copy for the display. Because this is a user, I could also just show the direct us user. And maybe we wanna show the user in a circle instead of a square. Totally up to you. So there we go.\u003C\u002Fp>\u003Cp>This looks a little better. We'll probably have the schedule 2 on there as well. And maybe I wanted to get a little fancy with some conditional formatting for the status. Again, pretty easy to do. We'll just go to status, we'll do display, a couple of different ways I could display this.\u003C\u002Fp>\u003Cp>Let's just go with a label, and I'm just going to copy the raw value from these choices. I'm gonna paste it here and then, you know, I could adjust the actual colors, like background and foreground if I need to. But let's just see how this presents. Yeah. That looks that looks definitely a lot better, and nice to have like an icon as well.\u003C\u002Fp>\u003Cp>Alright. So now we've cleaned this up a bit. Let's build a flow to check-in and out. So what we'll do, we'll go into our flows. Directus Flows is a great workflow automation tool.\u003C\u002Fp>\u003Cp>So I can set up, flows to run on any number of things. This one we're going to use the manual trigger for, which is one that I use often. I really like this. So let's call this the check-in. And do we have, like, an in there's an in out.\u003C\u002Fp>\u003Cp>Maybe that's, like, a in out burger, maybe? I don't know. Let's, let's do, like, an arrow forward for check-in. That looks good. And then when we do a checkout flow we can move backwards.\u003C\u002Fp>\u003Cp>So I move on to the trigger setup. Again, you've got lots of options here. You've got event hook, you could, so basically when an event happens inside Directus we're going to start this specific flow. I could trigger on webhook requests from other third party services, a schedule, or in this case we're going to run this manually. Alright.\u003C\u002Fp>\u003Cp>So this is going to be on our bookings collection. Right? The location is going to be where this displays. So if I want to show it on the collection, like the view, the layout page, I can do collection page only or I can do item page only to show it with in a specific booking. Or if I do both, I can, get get the option to pick from the collection page or I could do it from the detail page as well.\u003C\u002Fp>\u003Cp>So great. We'll go in and let's just require confirmation on this specific booking and, you know, let's add some inputs for this. Let's add some notes. Any notes about this booking? So I'm trying to think of what this would be.\u003C\u002Fp>\u003Cp>You know, maybe, hey, they showed up late or, you know, something like that. But we're already gonna be picking a booking so we know who this is. Let's give this a shot. And the first time I'm, the first thing I do whenever I'm building flows is basically start with my trigger, and then I'm going to go in and actually trigger my flow. So over here on the right, I can see my flows, I can select this, right?\u003C\u002Fp>\u003Cp>If I unselect, I won't be able to check this in. But we'll just say, hey, I'm going to check this in and here's some notes about this. Great. Alright. So now if I go back into my Flows, we'll see a couple things.\u003C\u002Fp>\u003Cp>Right? We've got our logs that we're gonna take a look at. We've got our payload. And if I look at the body, we've got the notes, we've got our collection, and we've got our keys. So this is the actual ID of the booking that we selected.\u003C\u002Fp>\u003Cp>Great. So when I think through this flow, right, what are we going to do? There's a a couple things we're probably going to want to achieve with this flow. If we go back into our data model just thinking out loud here. Alright.\u003C\u002Fp>\u003Cp>We're going to this is getting in the way. Let's just do text. We're going to want to create a booking transaction. We're probably going to update the status, update the status of the booking, and then we're going to update the status of the underlying equipment. Right?\u003C\u002Fp>\u003Cp>So that's gonna move from available to unavailable. Alright. We can achieve all of those inside of Flow. Let's get to it. Alright.\u003C\u002Fp>\u003Cp>So we are going to create our first operation. Let's call this, create booking, create transaction. Okay. We're gonna create data. So we're going to create an item in the database that's going to be our booking transactions.\u003C\u002Fp>\u003Cp>We can leave the permission set to from trigger because we're going to run this flow inside Directus. If this was triggered on a webhook event or something like that, you would probably want to use full access because you're not going to provide that accountability, that direct us user to actually run this flow. But we'll go into our payload and listening through this, we've got a booking. So let's just throw that out there. And then that is going to be coming from our trigger.\u003C\u002Fp>\u003Cp>So whenever I want to access data from that trigger event, I have to use this special syntax and just add a dollar sign in front of it. That is the only time I need to use that to access data from this flow. So only in the trigger. And let me just take a look. It could be handy if you take and copy this, into Versus Code or your editor just to make it easier to remember what it was as you're building out these flows.\u003C\u002Fp>\u003Cp>So we've got the trigger dot body dot keys. This is an array. We're gonna have to pick off the first value of that array. Alright. So let's run this again.\u003C\u002Fp>\u003Cp>Create transaction, and we'll do booking transactions, and paste this in. Trigger dot body dot keys, and we're gonna grab the first item from that array. And again, we're using the mustache syntax here. We can close this, and we're also going to have an action. Alright.\u003C\u002Fp>\u003Cp>So if you remember, that action is going to be check-in. And is that all we need? I think that's all we need. Great. Alright.\u003C\u002Fp>\u003Cp>So the next thing that we're gonna do, we want to update the status of that booking. So this is a check-in. We are going to update the status of that booking to what? So if we look at our booking, we've got, we're gonna change that to in progress. Right?\u003C\u002Fp>\u003Cp>Great. So we'll do update our booking, and that's gonna be update data. So we'll just call that update booking. That's gonna be our bookings collection. Again, we'll use the trigger permissions.\u003C\u002Fp>\u003Cp>Okay. So 2 ways I could go about grabbing the ID of the item that we're gonna update. I could use the IDs field to specify the specific IDs, or I could run a query for this. I'm going to lean this way just because we already know what that value looks like. So it's going to be dollar sign trigger dot body dot keys, first item of that array, and I'm going to press enter to make sure we store that.\u003C\u002Fp>\u003Cp>I can also go in and edit the raw value here just to see what that looks like. So again, if I'm using this mustache syntax, it's going to dynamically populate that data for me. We do not want to omit events for this. We don't want to trigger any type of, like, infinite loop using that, so always be careful when you're using that. And for our payload here, we're just going to update the status of this booking.\u003C\u002Fp>\u003Cp>The status is going to be in underscore progress. And I think I think that's all we're really looking for here, right. Now, we need to update the underlying piece of equipment as well. And if you take a look at our log, though, right, we are not getting that information here. We're only getting the key for the booking.\u003C\u002Fp>\u003Cp>So in that case, before we can update the actual piece of equipment this is attached to, we probably need to get the booking so we can find that, equipment that's attached to it. So let's just create a new operation. We're going to call it find booking. We're going to use the read data operation. We're going to find bookings collection.\u003C\u002Fp>\u003Cp>And then, again, we're just gonna do the same one, trigger.body.keys, save that. And for our query here, I could pass any of the the standard query parameters that are available. If you're not familiar with those, check out our docs. But what I'm gonna do, I'm gonna pass a fields query, or fields property, and this is gonna be an array. I'm gonna get all the root level fields, that's what this asterisk will do, And then I'm also going to get all of the equipment fields as well.\u003C\u002Fp>\u003Cp>So I am drilling through my relationships here. One of my favorite features of Directus is the ability to get and expand those relationships in a single API call. So assuming that goes well, then we are going to update that piece of equipment. Right. Before we do that, let's just stop and go test this out.\u003C\u002Fp>\u003Cp>Alright. So we are gonna go here. I'm gonna hit check-in. Here's some notes, and, you know, I could also save these on that transaction. We'll maybe do that in a moment.\u003C\u002Fp>\u003Cp>But we can see here the change. We've got a status of in progress. We've got a new transaction here. We can see the time stamp that was created. It was a check-in action.\u003C\u002Fp>\u003Cp>Let's go into that booking transactions. We'll add that notes field as well, just so we can store those notes. Great. And let's run into our flows and just see the log. Right?\u003C\u002Fp>\u003Cp>What did we get for the booking? Are we getting the data that we want? We can see the equipment dot ID. That's what we wanna update, and we're gonna change that status to available or unavailable because it it's unavailable. Alright.\u003C\u002Fp>\u003Cp>So this last link in the chain here will go in and update equipment. And You can see Directus will automatically generate the key for me. If you want to change that, feel free to. Not a big deal. But these keys are how you access the data in subsequent operations.\u003C\u002Fp>\u003Cp>Right. So as you can see in just a moment, we're going to go into choose our equipment collection. We'll do from trigger. But as far as the ID's that we're going to update, we're going to use, find booking_orfindbooking.equipment.id. So that is how we're gonna access this.\u003C\u002Fp>\u003Cp>And for our payload, we're just gonna change the status. Unavailable. Okay. Great. Cool.\u003C\u002Fp>\u003Cp>Alright. So I'm just gonna go in and manually change this. This is, let's call it a scheduled booking, for I'm gonna undo these transactions, unhide those just because I want to delete this one. Alright. So we're starting fresh.\u003C\u002Fp>\u003Cp>Everything's available. We've got a booking for our specific camera. I'm gonna go in, select this. I can also go into the item detail page here and do the same thing. And, oh, also forgot to update our notes.\u003C\u002Fp>\u003Cp>Right? Let's go ahead and do that as well. The perks of building on the fly. How are we looking time wise? Yeah, good.\u003C\u002Fp>\u003Cp>Pretty pretty solid time wise. Alright. So for our transactions here, we're gonna pass the notes, and that will be trigger, dollar sign trigger, dot body dot notes. Okay. Just some standard JSON format there.\u003C\u002Fp>\u003Cp>Great. Alright. Moment of truth. Let's see if this is actually gonna work. We'll go in, run check-in, our check-in flow.\u003C\u002Fp>\u003Cp>Bryant was late showing up to pick up this camera. Great. We'll hit run flow. Now we can see this is an in progress booking. If we look at our equipment, it shows as unavailable.\u003C\u002Fp>\u003Cp>You know, maybe we could potentially, show that a little nicer, maybe with a badge or something. And what else did we update? Right? We've got a transaction, we're storing our note. If we go into that detailed booking, we could see the transaction here.\u003C\u002Fp>\u003Cp>Maybe we want to clean this up a little bit for our users because we don't want them to see that UUID. Alright, so again, optimizing for our users. Let's go in and we'll go to the transactions. What do we want to do? We want to do show the timestamp, what the action was, and maybe if there's any notes.\u003C\u002Fp>\u003Cp>Oops. Let's do notes. See that? Cool. Okay.\u003C\u002Fp>\u003Cp>So now I can see this, 1 minute ago it was a check-in event, Bryant was late showing up to pick up this camera. And I can even go a step further with this, or I should be able to, in the action here for my display, I could show a formatted value of this. So we can show a border, and if this is a check-in, you know, maybe we wanna show, what did we use for this? Maybe, yeah, green will be fine. We could show a checkbox.\u003C\u002Fp>\u003Cp>Or, actually, I think we were using, like, a arrow. Right? Arrow. Check-in. And check out of it.\u003C\u002Fp>\u003Cp>So if the value equals that, we can actually, I'm getting these wrong, aren't I? We'd probably be checking out the equipment, not checking in. So maybe I need to go in and fix that as well. Arrow. Left.\u003C\u002Fp>\u003Cp>Right. There we go. Check-in. Oh, let's use left for that one. Great.\u003C\u002Fp>\u003Cp>Okay. Alright. Cool. So now we've got our actions. If I take a look at our transactions, we could see we got a badge for that, and I would probably actually flop these.\u003C\u002Fp>\u003Cp>Right? That would be this would be like a, yeah, checkout and not check-in. So we can change this. Right? This is a checkout event.\u003C\u002Fp>\u003Cp>And is there anything else we need to? I don't think we need to change any of these other ones. Alright. Cool. So let me go in and just fix this again.\u003C\u002Fp>\u003Cp>Alright. I'll delete this record so we don't have any more booking transactions there. Reset this guy to scheduled, and let's try it again. Check out. Late again.\u003C\u002Fp>\u003Cp>Run one flow on the selected item in progress. Now we got our checkout. Great. So I could do the same operation just in reverse for, like, a a check-in. So when we are checking this equipment back in, pretty easy to do.\u003C\u002Fp>\u003Cp>What other functionality do we need out of this? You know, is there an opportunity to take this one step further? We still got, about 15 minutes here. So what if we were to send some notifications on check-in or, like, if we got a new booking, we want to send some type of alert or or notification to someone. So maybe we reach into our flows for that.\u003C\u002Fp>\u003Cp>We'll go in and, you know, we could do a maybe like a a check-in flow as well. Right? So we do all this over again. We are going to do a check-in flow. Isn't that exciting?\u003C\u002Fp>\u003Cp>Let's do a notification. Right? New booking notification. Alright. So whenever a new booking occurs, we want to trigger a notification.\u003C\u002Fp>\u003Cp>That is going to be using our event hook, and you get 2 types of those event hooks inside Directus, whether that's a filter, which is a blocking. So this is great if I want to run some type of logic and then potentially either adjust the payload of the item that we're saving or, you know, updating, deleting, etcetera, or, you know, if I want to actually cancel that event pending some logic. In this case, we're going to do action non blocking. We don't want to block anything from occurring. Just after a new booking occurs, we are going to run a notification to a user.\u003C\u002Fp>\u003Cp>Right? So our scope here is items dot create. That's gonna be on a new booking And this also could be we could condition this down if we wanted to, where we only trigger on unconfirmed bookings. Unconfirmed. Alright.\u003C\u002Fp>\u003Cp>So the condition gives us, if, then, or if, else logic. So I can separate my flow into 2 separate paths, right. If the condition passes, we go this way and we run some other operations. If it fails, we do something else. So here's the syntax for this.\u003C\u002Fp>\u003Cp>We're just gonna use trigger. I'm breaking my own rules here. Let's just save this as is, and let's actually go create a new booking. And let's do it for the iPad Pro. We'll do the rest of this week.\u003C\u002Fp>\u003Cp>That's gonna be assigned to me. And here's some notes. Alright. So we created a new booking. I'm gonna get, a a payload here that I can take a look at, right.\u003C\u002Fp>\u003Cp>So the the payload is a little different than what we get from when we manually trigger these. But we can see we've got the equipment, we've got schedule from, assigned to. Great. So only on unconfirmed, what are we going to trigger this on? Basically, the unconfirmed.\u003C\u002Fp>\u003Cp>I really don't even think we need a condition here because the default value is unconfirmed. So, yeah, we could we could potentially do something like this. Like status is I think it's not equal to unconfirmed. Let's see if that runs. And then we're going to notify a certain member of the team.\u003C\u002Fp>\u003Cp>Right? Let's send a notify Matt. I'm going to pick on Matt on my team. We're going to send Matt a notification. And to do that, we have to have a UUID for that user.\u003C\u002Fp>\u003Cp>And I don't actually have a user in this account for Matt. Maybe we'll just use myself. Or we'll just create 1. Matt Minor. Great.\u003C\u002Fp>\u003Cp>Alright. We'll pick up the UUID from that specific user. And, you know, part of this flow could be like picking that up dynamically, depending on how you decide the logic of who to notify here. But I could just hard code that value in, hit save or hit enter to record that, and then we're going to add a notification. Right.\u003C\u002Fp>\u003Cp>New booking. Please review this booking and confirm. And as far as the collection here, the item, we are going to do what? Let's look at our log here. We got the payload.\u003C\u002Fp>\u003Cp>We're gonna do the key. So that's the created booking. And I can link that notification to that specific item. So it looks something like this, where we have trigger dot key. And that should do the trick.\u003C\u002Fp>\u003Cp>Alright. One of the other things that we may want to do, let's just check time really quick, we've got about 10 minutes. One of the other things that we may want to do is lock down some of these fields when you're actually editing this data. Right? When I am looking at a booking, I may not want anybody to be able to update the status without going through a specific, like, check-in or check out flow that handles all the logic that we want.\u003C\u002Fp>\u003Cp>Or likewise, you know, I I don't want any of my non admin users to be able to change the assignment for this. So there's 2 ways I could do this. If I go through the actual data model, you know, if if the booking status is controlled almost exclusively through Flows, I could go in and I could disable editing for this value. That'll be across the board, basically. Inside the UI nobody can update this value.\u003C\u002Fp>\u003Cp>So this is what that looks like. Now if I go into status, it just shows me the read only field, and if I wanted to change the status I would have one of my flows or update this via the API. Right. Here's my notes. I run this flow.\u003C\u002Fp>\u003Cp>My status is changed to in progress, and, potentially, I've got some type of, operation here to move this, like my check-in process. Right. But the other ways I could do this are via the Directus access control. So I could go in and create a, you know, let's call it a team member role. They will have app access, they won't have admin access, so they can't adjust the flows, they can't adjust our data model, but we do want them to be able to do certain things, like, view all of our bookings.\u003C\u002Fp>\u003Cp>Right. So I can give all access to start with, maybe we start whittling down some of these permissions. There's no need to share it. I don't want them to be able to delete bookings, delete equipment. I do want them to be able to update these fields.\u003C\u002Fp>\u003Cp>It could potentially mess with our flows as well, but let's address it. Right. So we're going to use the custom permissions for our bookings, the field permissions oh, I've gotta go in and do no access. Alright. So here's what we could do.\u003C\u002Fp>\u003Cp>We want to allow them to update only certain fields. Right. Maybe they can change the equipment in case we need to swap that. They can't change the status. They can change they can't change the assign to.\u003C\u002Fp>\u003Cp>They can't adjust the transactions, but they can see them. Sounds great. I can even configure field validation rules, or presets. Like, hey, here's the the default values for those. The only other thing that I'm concerned with here is because we're locking down that status, inside our flow for checkout we're actually using the permissions from the trigger here.\u003C\u002Fp>\u003Cp>So we'll just do full access, which basically gives unlimited access to, update these things. Great. Cool. And then, you know, if we go back into access control or team member, maybe we don't want them to update the equipment status either. Alright.\u003C\u002Fp>\u003Cp>So we'll give access field permissions. These are the fields that this role can update. They can update the name, serial number, description, image. I can't update the bookings. Status, no.\u003C\u002Fp>\u003Cp>Okay. Obviously they can update these. Cool. Alright. So we'll save this And now let's open up an incognito window.\u003C\u002Fp>\u003Cp>But first I need to give mister Matt Minor some permissions, or just a login. Matt@example.com. Give it a password. Great. Open up this incognito window.\u003C\u002Fp>\u003Cp>Go local. And we'll do matt@example.com. Password. Password. Did I did I get the password wrong?\u003C\u002Fp>\u003Cp>Did I even give them a role? So a couple things. I need to give Matt that team member role so he can have app access. And, you know, maybe I typed the password wrong. Password.\u003C\u002Fp>\u003Cp>Let's just copy that and make sure. Hit save. Okay. There we go. Alright.\u003C\u002Fp>\u003Cp>So now this is what Matt would see when he's logged in. You know, immediately you'd see he doesn't have access to the admin interface. But if he goes within the bookings, he can't update the status, he can't change the assignments, he can't do any of this stuff. But what he can do is run this flow. Here's some notes.\u003C\u002Fp>\u003Cp>This is not gonna really change any of this. Let's just zoom back out. Too zoomed in. Alright. So for the iPad Pro, I'm just gonna change this did I lock that down?\u003C\u002Fp>\u003Cp>I totally locked that down, didn't I? Let's go here. We'll change status. Go to the disable editing value. I'm just going to remove that just to demonstrate this capability.\u003C\u002Fp>\u003Cp>Alright. So now we go to iPad Pro. You can see over here I'm logged in as admin on the right hand side over here. I can change this status to be whatever I want. Hey, this is scheduled.\u003C\u002Fp>\u003Cp>I've got full permissions. Matt, when he's logged in, he can't edit any of the specific values. Right? But what he can do, he can still run these flows. And here's my notes about this booking.\u003C\u002Fp>\u003Cp>Right. We run the flow. It still updates all of our data, but in a restricted manner. You know, it only runs the logic that we want to run, so we can force Matt down a particular path that we want him to go. Alright.\u003C\u002Fp>\u003Cp>So as far as equipment manager, I'm calling this a win. We have got our inventory. Let's look at our list. Right? Track inventory of all of our equipment.\u003C\u002Fp>\u003Cp>User reserves equipment on a schedule. We track equipment status. We've got maintenance here. You know, we could easily create a flow modeled after our checkout flow to send this to maintenance, add some notes for it, or I could even just quickly, like, add some maintenance records for this the same way I do my transactions. Right?\u003C\u002Fp>\u003Cp>And I could even potentially just add those maintenance notes to this specific transaction. So as far as the equipment booking manager, in 1 hour or less, it's a win. It's good. Is it Checkroom? No, it's not.\u003C\u002Fp>\u003Cp>So if you need a more robust tool, make sure you check this one out. What was it again? Check Room? Looks like a pretty cool tool. Give it a free trial.\u003C\u002Fp>\u003Cp>I I like building inside Directus because, hey, bookings is is just one part of it. Right? You've got your other workflow. If you're managing your equipment, you're going to have to, plan where you're using that equipment at and things like that. So, inside Directus, in just a couple of tables, in less than an hour you can easily build an internal app to solve some of your problems.\u003C\u002Fp>\u003Cp>That's it. That's it for this episode of 100 Apps, 100 Hours. Thanks for joining me. Hope to see you on the next one.\u003C\u002Fp>","Hi. Welcome back to another episode of 100 apps, 100 hours, where we build some of your favorite apps or publicly fail trying. Today we've got a really neat use case that we're gonna try to build, an equipment booking manager. This sounds really fancy. I've seen this topic come up a few times within our direct us community, and I I thought, hey, let's tackle it today. It's perfect. So in all of the research I did, which is basically about 5 minutes before I pressed record on this, I found a comparable solution. It's called Checkroom, c h e q, Room. Looks like a pretty neat solution, but basically it's an internal app that allows you to manage all of your equipment. So equipment, in this case, it looks like they're targeting videographers, photography, you know, maybe studios, anybody who's got expensive camera equipment or video cameras, etcetera. But basically, you know, you can check these items in and out, reserve them for a time slot, manages all your inventory, what's the the status of all those, gives you a nice friendly dashboard. So that's what we're gonna be building today. If this is your first time watching 100 Apps 100 Hours, there are 2 rules. Number 1, we have 60 minutes to plan and build, so we're on a bit of a time crunch. There's no more, no less. And rule number 2 is, kind of, the anti rule. Use whatever we have at our disposal. So with that let's build our equipment booking manager. I'm gonna start the clock here and away we go. So I'm inside, Figma here or FigJam. This is how I like to plan before I build, just kind of quickly whiteboard things. So let's go in and just flesh out the the actual functionality that we're looking for out of this specific app. Right? We wanna track inventory of all of our different items, of all equipment and items. This could be, like, mobile phones or Android devices if we're doing web development for testing, camera equipment, printers, what whatever. We wanna be able to reserve equipment equipment on a schedule. We want to be able to track, details about the equipment. Let's say user can reserve the equipment on a schedule. We're gonna be able to track details of track equipment status. I mean, that's that's pretty much the the bare bones for this. You know, when are these things scheduled? Is it available for checkout? You know, maybe we wanna account for things like maintenance. Put, like, a big question mark there. Yeah. Okay. That that seems pretty good. Let's let's dive in and actually start planning what our data model may look like for something like this. So I'm just going to drag a nice square over here. Let's change this to red to kind of match. Alright. So thinking about this and and just talking it through out loud, obviously we have our inventory. This could be called items, could be called equipment. Let's let's go with equipment. That that seems like a a good compromise there. Items has a, kind of, a specific meaning inside Directus, which is just the actual individual pieces of content or data. So let's go with equipment, and then we're going to have, users. Alright. So that's gonna come actually from a Directus underscore user collection that Directus is gonna give us out of the box, which again is really nice. We got that authentication tied to that as well. And then we're gonna have, what, bookings for each of the this piece of equipment. Right? So we have a relationship here between equipment and bookings. The user creates a booking for a specific piece of equipment. You know, we have a check-in, check out type of functionality. It's great. Yeah. And this could be, you know, one of the other things that I saw here on the check room website, where was this? Inventory. One inventory to rule them all. You could potentially bundle these things up. Capture the flag. So, you know, maybe some type of checklist. Right? Let's go through and and just start fleshing this out. Right? We're about 4 minutes in. We need to actually start building something. So I'm gonna pull up my instance of Directus, so this side by side. We'll zoom in just a little bit. And this is just a blank direct assistance that I've spun up here locally, if I can remember the actual password. I think it's real secure. It's probably just password. Great. So we've got a totally blank instance of Directus. Let's start building something. Let's start with equipment. Right. We're going to just call this table equipment. Usually I've got a plural at the end of a lot of my tables, but equipments doesn't make a ton of sense to me. So, what are we going to have on this? We're going to have a status, like the default status field here. These just make it easy to add some of the most common functionality you'll have. It uses, like, a draft, published, archived. So I'm just gonna leave that blank for now. The sort field, do we really need a a sort for our equipment? Probably not. It might be good to track when it was created, so when it was added to the database, who it was created by, and the last time that information was updated. So we'll just roll with that. And let's give this, a name, right? This is the name of this piece of equipment, it's just gonna be a string. Really simple. No need to get fancy with it. Let's require a name for it. And next, let's probably add a a serial number. We need a unique identifier for this. You know, maybe we want, we end up printing these on labels or QR codes. Serial number would be a good way to track that. And, you know, we can choose to require that as a value, you know, maybe we don't necessarily need to, but we'll add that. What else do we wanna add here? We want to add a description. So maybe that's gonna be a wysiwyg editor. You know, this could be a HTML description, so we'll go with that. The type is gonna be text. I could customize my toolbar if I want to limit the options that we've got, but we'll just roll with the the default selections for now. We're probably going to have, what, some images of this or at least one image. You know, maybe we've got a gallery, but let's just call this the image. That's gonna be a relational field, Just a mini to 1 to our directus files collection. Make sure we can add an image for it. What else am I forgetting? We've got a status field. So, let's use a drop down for this. We'll call it status. The type, we're just gonna store that as a string. And for our choices, right, when we think through the choices, the equipment is either currently available, it is unavailable, you know, maybe it could be like, hey, it's getting serviced or it's in maintenance. I'm not sure if that's worthwhile to track or not. Alright. So let's just add a new one. We're gonna call it available. For the value, we can use lower case. Let's give a, maybe, a check mark. Hey. This is available. We'll make it green. Then we'll have unavailable. I'm sure there's probably a better naming convention here, but this is what we'll roll with. Maybe like a x. Okay. This is unavailable. And, yeah, maybe we do it like a status for maintenance just to differentiate. Maintenance. Wrench, maybe. Let's look for a wrench repair. There we go. We got a toolbox. That'll work. Okay. And I think yellow or orange is a good color for that. Great. So we've got available, meaning it's currently in inventory. Unavailable means it's out, Not in inventory. And then we have maintenance. Don't need to allow any other values. Looks great. Trying to think of what else we actually need for this data model. You know, we may have, like, specific details that we wanted to track, those are escaping me at this particular moment. So we've got equipment. Let's go in and add a few pieces of equipment. You know, we'll just start out and say, this is available. We have a, just looking at my desk here, I have an iPad Pro that is BG iPad Pro. Just create a serial number. The most amazing iPad you've ever seen. And we'll just go to Google, and it's still an image. Right? IPad Pro. Perfect. Where are you? Apple's gonna sue me, I'm sure. Please don't. Alright. Swipe an image here. That's great. We've got an iPad Pro. I may want to adjust my view just a little bit. I can't I don't see those nice images. I could go in and add my thumbnail for this. Looks great. Cool. We can make this a larger view. We can make it comfortable, and I can see a little more of that. Or I could even go straight to cards view here and get, just kind of like a gallery of where we're at. So the subtitle here could be the status, it's available. Great. We could add one more piece of equipment. You know, maybe I've got a, show my age here. The Sony a 64100. Sony a 64100. We could call this 1. Great. So we'll find a image of the Sony. I think it's the a 64100. Yep. That's the one. Copy image address. We'll just pull that in. Great. And, yeah, maybe I've got something like 2 of these cameras. How can I, like, achieve that really quickly? How can I duplicate these inside Directus? Well, what I could do is just go ahead and change the name a little bit, change the serial number, and I could go into the 3 dots up here at the top and click save as copy. So now I've got 2 of those specific cameras, maybe one's available, one's not, one's currently checked out, the other isn't. Cool. Alright. So we've got some equipment. What is the next thing that we're going to build? Let's take a look at bookings. Right? So how is this going to work? We're going to maintain a schedule of bookings. We're probably going to use our calendar view inside Directus for this, and then we'll be able to check-in and out this equipment, or actually reserve this equipment. The check-in, check out part may be, something else a little different. So let's just call this table bookings, makes sense. For the primary key we'll just use an ID. I'll use generated UUID for that. For the status, right, Let's leave status off here. We're just gonna check the box for all of the date created, date updated, all these system fields that auto populate when certain actions happen. And now we've got a booking, right, so when is the we we need the times for the booking. So let's do, like, a scheduled from. We use a time stamp format just so we get the time zone value. K. Let's make that high second half width. We'll do schedule 2 for that as well. Schedule from, schedule 2. Do we have notes? You know, I'm thinking, like, hey, we may have to have some type of notes. We'll use a text area for that. I don't need these to be formatted in a WYSIWYG or markdown. You know, could be potentially nice if you need that level of functionality. Alright. So we've got a booking, we've got schedule from, schedule to. We need, obviously, a piece of equipment tied to this booking. So now we're gonna start reaching for our relationships inside Directus, because each booking could only have a single piece of equipment, or could it? You know, we could potentially book a 3 or 4 pieces of equipment, but maybe those would be individual bookings as well. You know, it gets more complicated when you have a kit. Maybe we'll try to explore that. But for now, let's just keep it simple. We'll do a mini to 1. Or would it be, yeah, let's do mini to 1 for now. The related collection will be equipment. And we'll go into the relationship here. I can see what this setup looks like, but I can go in and also add that inverse relationship. So, I can add equipment to my bookings or add bookings inside my equipment. So I if I pull up a piece of equipment, I can see all the bookings right inside that item detail page that that that particular piece of equipment has had. Alright. Long winded explanation there, Brian. Alright. So we've got a schedule from, schedule 2, notes, equipment. Who is this assigned to, or who's pulling this equipment? I again, naming things is is probably the hardest challenge in development. Let's just call this assigned to or bookie. Call here bookie. Let's call it assign to. That's fine. So now I can't see my users collection here. Directus users is actually collection. So we're just gonna go in and expand that system drop down, pick Directus users. I could control the display template here, so what actually shows up. You know, maybe I want to show the image of the user, the first name, the last name. Great. And save it. Alright. So we've got the schedule from, schedule to. We probably need a status on this as well. Right? So who is this booking for? Alright. The status, when we think about that, again, let's use a drop down just because we get, like, a rich look at it, now that we have the ability to add icons and colors. But when I think about this, let's say, what's the initial state on a booking? Would be new or maybe unconfirmed is what I'm thinking here. So a new booking comes in, somebody has to verify that that booking is okay, or maybe we could do that automatically via flow. But we'll call it unconfirmed. And let's just see if we've got, like, a there we go. We've got a new flow. Fiber new. That's an interesting name for that particular icon. What else do we have? Scheduled. Scheduled. K. Time. See what we got. Okay. Let's just do, like, blue. That's kind of like a neutral color as far as what's going on. You know, maybe in progress is the next one. We'll just create some text and value for that. Settings. Maybe, like, a gear, progress bar, meter, bar. Yeah. There we go. That'll work. Alright. We'll mark that as I guess, as green in the state of a booking. Maybe it's red. In progress. Maybe completed is the state. Alright. So this is a completed booking. Check mark. I think that's a that'll be a nice icon for this. Alright. Completed booking. We'll go to check mark. We'll make that green. Great. Okay. So now we've got our status field filled out. We'll hit save, and now we can start looking at at how this is gonna come together. So we've got equipment, we've got our bookings, you know, maybe we look at this on a calendar view. Our layout options here we're gonna adjust this view. Each one of these layouts inside Directus has, separate configuration options that are dependent on the specific view. So here maybe we display, let's see. We're gonna display the equipment name. That makes sense to me. And then let's also maybe display the user as well, so who this is assigned to. Assign to first name. Assign to so we'll reach into that related collection, and I can display those related values. I thought I had added that. Assign to last name. Oh, I'm just not seeing it there. First name, assign to first name. I added it, I just didn't see it. Last name. Okay. Alright. The start date field is gonna be scheduled from, scheduled to. 1st day of the week is Sunday, and then we're good. Right? So let's go in and create our first booking. We'll just do this manually. And I can actually do default values as well, one of the handy things about when when you're setting up your data model inside Directus. If I go into my status, I've got unconfirmed as a value. If I go to my schema, I can just set the default value to be unconfirmed, and now whenever I create a new booking, the default is unconfirmed. Alright. So we're going to pick a piece of equipment. Let's say we want this Sony a 64100. That's gonna be assigned to me. I wanna check this out, for 2 days at near the end of this month and using for a photo shoot. Great. So now I could see that booking on my calendar, and presumably as these bookings come in, you know, I could go in and and see this schedule. Right? This is really a bare bones functionality. You know, I I could give access to our team to this and set up some separate permissions, but now, I let's maybe add some automation to manage this booking process. Right? It seems kind of hey, the flow doesn't seem quite right to me. So we wouldn't have somebody go in and manually adjust the status for their booking. You know, maybe we can take advantage of Directus flows and and build something to to, account for this. Right? So we'll go inside flows, and let's just let's think about how to manage the bookings. Each one of these specific bookings, has some actions that you're taking against it. So to me, if you're used to building, like, inventory systems, you probably got some type of transaction against that inventory of, hey, this was checked in, this was checked out, it was reserved, or it the on hand quantity was reduced. So maybe we do something like that where we track the individual transactions against these bookings of when it was checked out, when it was checked in. Let's do that. So we'll do bookings underscore transactions, and I'll just use UUID again. Just kind of the standard for me. Maybe the created on do we really need, alright. We're not gonna update these transactions. So, yeah, if we're created on, I'm just gonna change this to timestamp because that is the timestamp this particular transaction occurred. We'll use created by just so we have the user created that particular transaction, so if that was me checking myself in or somebody else checking in that booking. Alright. So at that point we also are gonna have a relationship to our bookings. So let's go ahead and set that up. Here it's going to be a mini to one relationship from booking transactions to bookings, because, a a transaction only has one booking, but a booking could have many different transactions attached to it. So we'll call this the booking, and our related collection is gonna be bookings. And for the display template, let's say that is the equipment name, space, date, schedule from. And we do schedule to. And if I wanted to, I can even go in and add the the specific user. So we'll do first name, assign to last name. Perfect. Alright. Cool. So now we've got a booking, and I forgot to create the inverse relationship. There's when you're creating that mini to 1, there's a second tab for that, but no fear. We can just go into our bookings and and we can add that from here. So we'll do the one to many field. So this is the inverse relationship. We'll call this our transactions. And let's do booking transactions and the foreign key we'll already have, which is the booking. So this is the foreign key for bookings inside transactions. We'll just save that, maybe show a link to it and hit save. Alright. So now if we take a look at one of our bookings we can see this transaction table. Alright. The other thing that we're probably gonna need on our booking transactions, and honestly, like, this may be hidden behind the scenes, and I'm just gonna go in and add some icons to this before it drives me crazy. Recovering designer folks. Bear with me. Camera. There we go. Looks great. Alright. So on our booking transactions, and I may even just nest that, we're gonna need a action type. Alright. So it's a check-in, check out. You know, move to maintenance, maybe, as well. For this, let's try something like radio buttons. So this will be the action. The choices that we'll have will be check-in, the value here, check underscore in, Check out. Check out. Maintenance. Move to maintenance. Request maintenance. Let's just call it maintenance. I'm getting too fancy with it. Taking too much time when I'm actually maintenance. I'm not even sure if that's the correct spelling. That's what we're rolling with. Alright. So now, what have we done? We've got some equipment, we've got bookings we can use to manage that, and then we can attach transactions to those. Right? So as far as our underlying data model, this looks pretty cool. Let's work on our our flow. Right? How can we make this easier? So I can imagine, like, if I'm looking up bookings, you know, maybe I want to bookmark this. This is the calendar view. So we'll add a calendar icon for that as well. Alright. So when I save a bookmark, that gives me the calendar here. And then maybe I can go in and maybe we just want, like, a list of all the bookings as well. This could be a new potential bookmark. Let's go in. We don't wanna update that original bookmark. I go back to bookings. We'll just create a bookmark for this. We'll call this list. This is a list of bookings. Great. So one of the other nice things about creating these bookmarks inside Directus is if you have certain filters applied or, you know, even a search query, you could save that search query and create these really nice slices of your data. But as far as my list view, you know, maybe I want to clean this up a little bit. My assign to field looks kind of boring, my equipment is not showing. Let's just make this look a little nicer. So I'm going to go back inside my data model and where I have equipment I'm gonna look at my interface and display. So here we have our interface, which is gonna be how it looks inside the detail view. The display is gonna be how it presents on the layout. So I could go in and maybe we want to show the, image, And I can expand that a little bit just to get a a thumbnail. And we'll show the name of this, and we'll do the same here. One of the little tricks I can use here is just drop this down, copy raw value, we can also paste it here and get access to the same thing. Great. As far as the assign to, maybe we want to do the same thing. Copy for the display. Because this is a user, I could also just show the direct us user. And maybe we wanna show the user in a circle instead of a square. Totally up to you. So there we go. This looks a little better. We'll probably have the schedule 2 on there as well. And maybe I wanted to get a little fancy with some conditional formatting for the status. Again, pretty easy to do. We'll just go to status, we'll do display, a couple of different ways I could display this. Let's just go with a label, and I'm just going to copy the raw value from these choices. I'm gonna paste it here and then, you know, I could adjust the actual colors, like background and foreground if I need to. But let's just see how this presents. Yeah. That looks that looks definitely a lot better, and nice to have like an icon as well. Alright. So now we've cleaned this up a bit. Let's build a flow to check-in and out. So what we'll do, we'll go into our flows. Directus Flows is a great workflow automation tool. So I can set up, flows to run on any number of things. This one we're going to use the manual trigger for, which is one that I use often. I really like this. So let's call this the check-in. And do we have, like, an in there's an in out. Maybe that's, like, a in out burger, maybe? I don't know. Let's, let's do, like, an arrow forward for check-in. That looks good. And then when we do a checkout flow we can move backwards. So I move on to the trigger setup. Again, you've got lots of options here. You've got event hook, you could, so basically when an event happens inside Directus we're going to start this specific flow. I could trigger on webhook requests from other third party services, a schedule, or in this case we're going to run this manually. Alright. So this is going to be on our bookings collection. Right? The location is going to be where this displays. So if I want to show it on the collection, like the view, the layout page, I can do collection page only or I can do item page only to show it with in a specific booking. Or if I do both, I can, get get the option to pick from the collection page or I could do it from the detail page as well. So great. We'll go in and let's just require confirmation on this specific booking and, you know, let's add some inputs for this. Let's add some notes. Any notes about this booking? So I'm trying to think of what this would be. You know, maybe, hey, they showed up late or, you know, something like that. But we're already gonna be picking a booking so we know who this is. Let's give this a shot. And the first time I'm, the first thing I do whenever I'm building flows is basically start with my trigger, and then I'm going to go in and actually trigger my flow. So over here on the right, I can see my flows, I can select this, right? If I unselect, I won't be able to check this in. But we'll just say, hey, I'm going to check this in and here's some notes about this. Great. Alright. So now if I go back into my Flows, we'll see a couple things. Right? We've got our logs that we're gonna take a look at. We've got our payload. And if I look at the body, we've got the notes, we've got our collection, and we've got our keys. So this is the actual ID of the booking that we selected. Great. So when I think through this flow, right, what are we going to do? There's a a couple things we're probably going to want to achieve with this flow. If we go back into our data model just thinking out loud here. Alright. We're going to this is getting in the way. Let's just do text. We're going to want to create a booking transaction. We're probably going to update the status, update the status of the booking, and then we're going to update the status of the underlying equipment. Right? So that's gonna move from available to unavailable. Alright. We can achieve all of those inside of Flow. Let's get to it. Alright. So we are going to create our first operation. Let's call this, create booking, create transaction. Okay. We're gonna create data. So we're going to create an item in the database that's going to be our booking transactions. We can leave the permission set to from trigger because we're going to run this flow inside Directus. If this was triggered on a webhook event or something like that, you would probably want to use full access because you're not going to provide that accountability, that direct us user to actually run this flow. But we'll go into our payload and listening through this, we've got a booking. So let's just throw that out there. And then that is going to be coming from our trigger. So whenever I want to access data from that trigger event, I have to use this special syntax and just add a dollar sign in front of it. That is the only time I need to use that to access data from this flow. So only in the trigger. And let me just take a look. It could be handy if you take and copy this, into Versus Code or your editor just to make it easier to remember what it was as you're building out these flows. So we've got the trigger dot body dot keys. This is an array. We're gonna have to pick off the first value of that array. Alright. So let's run this again. Create transaction, and we'll do booking transactions, and paste this in. Trigger dot body dot keys, and we're gonna grab the first item from that array. And again, we're using the mustache syntax here. We can close this, and we're also going to have an action. Alright. So if you remember, that action is going to be check-in. And is that all we need? I think that's all we need. Great. Alright. So the next thing that we're gonna do, we want to update the status of that booking. So this is a check-in. We are going to update the status of that booking to what? So if we look at our booking, we've got, we're gonna change that to in progress. Right? Great. So we'll do update our booking, and that's gonna be update data. So we'll just call that update booking. That's gonna be our bookings collection. Again, we'll use the trigger permissions. Okay. So 2 ways I could go about grabbing the ID of the item that we're gonna update. I could use the IDs field to specify the specific IDs, or I could run a query for this. I'm going to lean this way just because we already know what that value looks like. So it's going to be dollar sign trigger dot body dot keys, first item of that array, and I'm going to press enter to make sure we store that. I can also go in and edit the raw value here just to see what that looks like. So again, if I'm using this mustache syntax, it's going to dynamically populate that data for me. We do not want to omit events for this. We don't want to trigger any type of, like, infinite loop using that, so always be careful when you're using that. And for our payload here, we're just going to update the status of this booking. The status is going to be in underscore progress. And I think I think that's all we're really looking for here, right. Now, we need to update the underlying piece of equipment as well. And if you take a look at our log, though, right, we are not getting that information here. We're only getting the key for the booking. So in that case, before we can update the actual piece of equipment this is attached to, we probably need to get the booking so we can find that, equipment that's attached to it. So let's just create a new operation. We're going to call it find booking. We're going to use the read data operation. We're going to find bookings collection. And then, again, we're just gonna do the same one, trigger.body.keys, save that. And for our query here, I could pass any of the the standard query parameters that are available. If you're not familiar with those, check out our docs. But what I'm gonna do, I'm gonna pass a fields query, or fields property, and this is gonna be an array. I'm gonna get all the root level fields, that's what this asterisk will do, And then I'm also going to get all of the equipment fields as well. So I am drilling through my relationships here. One of my favorite features of Directus is the ability to get and expand those relationships in a single API call. So assuming that goes well, then we are going to update that piece of equipment. Right. Before we do that, let's just stop and go test this out. Alright. So we are gonna go here. I'm gonna hit check-in. Here's some notes, and, you know, I could also save these on that transaction. We'll maybe do that in a moment. But we can see here the change. We've got a status of in progress. We've got a new transaction here. We can see the time stamp that was created. It was a check-in action. Let's go into that booking transactions. We'll add that notes field as well, just so we can store those notes. Great. And let's run into our flows and just see the log. Right? What did we get for the booking? Are we getting the data that we want? We can see the equipment dot ID. That's what we wanna update, and we're gonna change that status to available or unavailable because it it's unavailable. Alright. So this last link in the chain here will go in and update equipment. And You can see Directus will automatically generate the key for me. If you want to change that, feel free to. Not a big deal. But these keys are how you access the data in subsequent operations. Right. So as you can see in just a moment, we're going to go into choose our equipment collection. We'll do from trigger. But as far as the ID's that we're going to update, we're going to use, find booking_orfindbooking.equipment.id. So that is how we're gonna access this. And for our payload, we're just gonna change the status. Unavailable. Okay. Great. Cool. Alright. So I'm just gonna go in and manually change this. This is, let's call it a scheduled booking, for I'm gonna undo these transactions, unhide those just because I want to delete this one. Alright. So we're starting fresh. Everything's available. We've got a booking for our specific camera. I'm gonna go in, select this. I can also go into the item detail page here and do the same thing. And, oh, also forgot to update our notes. Right? Let's go ahead and do that as well. The perks of building on the fly. How are we looking time wise? Yeah, good. Pretty pretty solid time wise. Alright. So for our transactions here, we're gonna pass the notes, and that will be trigger, dollar sign trigger, dot body dot notes. Okay. Just some standard JSON format there. Great. Alright. Moment of truth. Let's see if this is actually gonna work. We'll go in, run check-in, our check-in flow. Bryant was late showing up to pick up this camera. Great. We'll hit run flow. Now we can see this is an in progress booking. If we look at our equipment, it shows as unavailable. You know, maybe we could potentially, show that a little nicer, maybe with a badge or something. And what else did we update? Right? We've got a transaction, we're storing our note. If we go into that detailed booking, we could see the transaction here. Maybe we want to clean this up a little bit for our users because we don't want them to see that UUID. Alright, so again, optimizing for our users. Let's go in and we'll go to the transactions. What do we want to do? We want to do show the timestamp, what the action was, and maybe if there's any notes. Oops. Let's do notes. See that? Cool. Okay. So now I can see this, 1 minute ago it was a check-in event, Bryant was late showing up to pick up this camera. And I can even go a step further with this, or I should be able to, in the action here for my display, I could show a formatted value of this. So we can show a border, and if this is a check-in, you know, maybe we wanna show, what did we use for this? Maybe, yeah, green will be fine. We could show a checkbox. Or, actually, I think we were using, like, a arrow. Right? Arrow. Check-in. And check out of it. So if the value equals that, we can actually, I'm getting these wrong, aren't I? We'd probably be checking out the equipment, not checking in. So maybe I need to go in and fix that as well. Arrow. Left. Right. There we go. Check-in. Oh, let's use left for that one. Great. Okay. Alright. Cool. So now we've got our actions. If I take a look at our transactions, we could see we got a badge for that, and I would probably actually flop these. Right? That would be this would be like a, yeah, checkout and not check-in. So we can change this. Right? This is a checkout event. And is there anything else we need to? I don't think we need to change any of these other ones. Alright. Cool. So let me go in and just fix this again. Alright. I'll delete this record so we don't have any more booking transactions there. Reset this guy to scheduled, and let's try it again. Check out. Late again. Run one flow on the selected item in progress. Now we got our checkout. Great. So I could do the same operation just in reverse for, like, a a check-in. So when we are checking this equipment back in, pretty easy to do. What other functionality do we need out of this? You know, is there an opportunity to take this one step further? We still got, about 15 minutes here. So what if we were to send some notifications on check-in or, like, if we got a new booking, we want to send some type of alert or or notification to someone. So maybe we reach into our flows for that. We'll go in and, you know, we could do a maybe like a a check-in flow as well. Right? So we do all this over again. We are going to do a check-in flow. Isn't that exciting? Let's do a notification. Right? New booking notification. Alright. So whenever a new booking occurs, we want to trigger a notification. That is going to be using our event hook, and you get 2 types of those event hooks inside Directus, whether that's a filter, which is a blocking. So this is great if I want to run some type of logic and then potentially either adjust the payload of the item that we're saving or, you know, updating, deleting, etcetera, or, you know, if I want to actually cancel that event pending some logic. In this case, we're going to do action non blocking. We don't want to block anything from occurring. Just after a new booking occurs, we are going to run a notification to a user. Right? So our scope here is items dot create. That's gonna be on a new booking And this also could be we could condition this down if we wanted to, where we only trigger on unconfirmed bookings. Unconfirmed. Alright. So the condition gives us, if, then, or if, else logic. So I can separate my flow into 2 separate paths, right. If the condition passes, we go this way and we run some other operations. If it fails, we do something else. So here's the syntax for this. We're just gonna use trigger. I'm breaking my own rules here. Let's just save this as is, and let's actually go create a new booking. And let's do it for the iPad Pro. We'll do the rest of this week. That's gonna be assigned to me. And here's some notes. Alright. So we created a new booking. I'm gonna get, a a payload here that I can take a look at, right. So the the payload is a little different than what we get from when we manually trigger these. But we can see we've got the equipment, we've got schedule from, assigned to. Great. So only on unconfirmed, what are we going to trigger this on? Basically, the unconfirmed. I really don't even think we need a condition here because the default value is unconfirmed. So, yeah, we could we could potentially do something like this. Like status is I think it's not equal to unconfirmed. Let's see if that runs. And then we're going to notify a certain member of the team. Right? Let's send a notify Matt. I'm going to pick on Matt on my team. We're going to send Matt a notification. And to do that, we have to have a UUID for that user. And I don't actually have a user in this account for Matt. Maybe we'll just use myself. Or we'll just create 1. Matt Minor. Great. Alright. We'll pick up the UUID from that specific user. And, you know, part of this flow could be like picking that up dynamically, depending on how you decide the logic of who to notify here. But I could just hard code that value in, hit save or hit enter to record that, and then we're going to add a notification. Right. New booking. Please review this booking and confirm. And as far as the collection here, the item, we are going to do what? Let's look at our log here. We got the payload. We're gonna do the key. So that's the created booking. And I can link that notification to that specific item. So it looks something like this, where we have trigger dot key. And that should do the trick. Alright. One of the other things that we may want to do, let's just check time really quick, we've got about 10 minutes. One of the other things that we may want to do is lock down some of these fields when you're actually editing this data. Right? When I am looking at a booking, I may not want anybody to be able to update the status without going through a specific, like, check-in or check out flow that handles all the logic that we want. Or likewise, you know, I I don't want any of my non admin users to be able to change the assignment for this. So there's 2 ways I could do this. If I go through the actual data model, you know, if if the booking status is controlled almost exclusively through Flows, I could go in and I could disable editing for this value. That'll be across the board, basically. Inside the UI nobody can update this value. So this is what that looks like. Now if I go into status, it just shows me the read only field, and if I wanted to change the status I would have one of my flows or update this via the API. Right. Here's my notes. I run this flow. My status is changed to in progress, and, potentially, I've got some type of, operation here to move this, like my check-in process. Right. But the other ways I could do this are via the Directus access control. So I could go in and create a, you know, let's call it a team member role. They will have app access, they won't have admin access, so they can't adjust the flows, they can't adjust our data model, but we do want them to be able to do certain things, like, view all of our bookings. Right. So I can give all access to start with, maybe we start whittling down some of these permissions. There's no need to share it. I don't want them to be able to delete bookings, delete equipment. I do want them to be able to update these fields. It could potentially mess with our flows as well, but let's address it. Right. So we're going to use the custom permissions for our bookings, the field permissions oh, I've gotta go in and do no access. Alright. So here's what we could do. We want to allow them to update only certain fields. Right. Maybe they can change the equipment in case we need to swap that. They can't change the status. They can change they can't change the assign to. They can't adjust the transactions, but they can see them. Sounds great. I can even configure field validation rules, or presets. Like, hey, here's the the default values for those. The only other thing that I'm concerned with here is because we're locking down that status, inside our flow for checkout we're actually using the permissions from the trigger here. So we'll just do full access, which basically gives unlimited access to, update these things. Great. Cool. And then, you know, if we go back into access control or team member, maybe we don't want them to update the equipment status either. Alright. So we'll give access field permissions. These are the fields that this role can update. They can update the name, serial number, description, image. I can't update the bookings. Status, no. Okay. Obviously they can update these. Cool. Alright. So we'll save this And now let's open up an incognito window. But first I need to give mister Matt Minor some permissions, or just a login. Matt@example.com. Give it a password. Great. Open up this incognito window. Go local. And we'll do matt@example.com. Password. Password. Did I did I get the password wrong? Did I even give them a role? So a couple things. I need to give Matt that team member role so he can have app access. And, you know, maybe I typed the password wrong. Password. Let's just copy that and make sure. Hit save. Okay. There we go. Alright. So now this is what Matt would see when he's logged in. You know, immediately you'd see he doesn't have access to the admin interface. But if he goes within the bookings, he can't update the status, he can't change the assignments, he can't do any of this stuff. But what he can do is run this flow. Here's some notes. This is not gonna really change any of this. Let's just zoom back out. Too zoomed in. Alright. So for the iPad Pro, I'm just gonna change this did I lock that down? I totally locked that down, didn't I? Let's go here. We'll change status. Go to the disable editing value. I'm just going to remove that just to demonstrate this capability. Alright. So now we go to iPad Pro. You can see over here I'm logged in as admin on the right hand side over here. I can change this status to be whatever I want. Hey, this is scheduled. I've got full permissions. Matt, when he's logged in, he can't edit any of the specific values. Right? But what he can do, he can still run these flows. And here's my notes about this booking. Right. We run the flow. It still updates all of our data, but in a restricted manner. You know, it only runs the logic that we want to run, so we can force Matt down a particular path that we want him to go. Alright. So as far as equipment manager, I'm calling this a win. We have got our inventory. Let's look at our list. Right? Track inventory of all of our equipment. User reserves equipment on a schedule. We track equipment status. We've got maintenance here. You know, we could easily create a flow modeled after our checkout flow to send this to maintenance, add some notes for it, or I could even just quickly, like, add some maintenance records for this the same way I do my transactions. Right? And I could even potentially just add those maintenance notes to this specific transaction. So as far as the equipment booking manager, in 1 hour or less, it's a win. It's good. Is it Checkroom? No, it's not. So if you need a more robust tool, make sure you check this one out. What was it again? Check Room? Looks like a pretty cool tool. Give it a free trial. I I like building inside Directus because, hey, bookings is is just one part of it. Right? You've got your other workflow. If you're managing your equipment, you're going to have to, plan where you're using that equipment at and things like that. So, inside Directus, in just a couple of tables, in less than an hour you can easily build an internal app to solve some of your problems. That's it. That's it for this episode of 100 Apps, 100 Hours. Thanks for joining me. Hope to see you on the next one.","d4522b33-cbf4-4d8b-bbce-7a87abb83c9d",[3438],"e74a7bc0-b593-44f2-a603-95b7fd47fbce",[],{"year":978,"number":2062,"id":2311,"episodes":3441,"show":3442},[2313,2314,2315,2316,2317,2318,2319,2320,2321,2298],{"title":322,"slug":323},{"id":3377,"slug":3444,"vimeo_id":3445,"description":3446,"tile":3447,"length":13,"resources":12,"people":12,"episode_number":247,"published":3448,"title":3449,"video_transcript_html":3450,"video_transcript_text":3451,"content":12,"seo":12,"status":19,"episode_people":3452,"recommendations":3453,"season":3454},"rome","893668587","All time comparisons lead to Rome.","47926616-e665-4933-ae5c-e907eb78935f","2024-04-24","Rome","\u003Cp>Speaker 0: And now, Dev Thoughts, by Jack Rabbit. They say Rome wasn't built in a day, but they didn't have JavaScript frameworks back then. Now Rome can be built in a day, but it will take an eternity to load on your browser.\u003C\u002Fp>","And now, Dev Thoughts, by Jack Rabbit. They say Rome wasn't built in a day, but they didn't have JavaScript frameworks back then. Now Rome can be built in a day, but it will take an eternity to load on your browser.",[],[],{"year":978,"number":263,"id":3362,"episodes":3455,"show":3456},[3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3350,3378],{"title":3380,"slug":3381},{"id":3376,"slug":3458,"vimeo_id":3459,"description":3460,"tile":3461,"length":13,"resources":12,"people":12,"episode_number":675,"published":3448,"title":3462,"video_transcript_html":3463,"video_transcript_text":3464,"content":12,"seo":12,"status":19,"episode_people":3465,"recommendations":3466,"season":3467},"thor","893668432","Could Keyboard Warrior be the next Marvel super-hero? Stay tuned.","911dd640-7026-4caa-8719-8078695931d4","Thor","\u003Cp>Speaker 0: And now, Dev Thoughts by Jack Rabbit. Sometimes, when I'm writing code, I pause and think about the electricity flowing through the circuits, carrying my logic to the processor. Then, I realize, my code is just a bunch of organized lightning bolts, and suddenly I feel like Thor, but with a keyboard.\u003C\u002Fp>","And now, Dev Thoughts by Jack Rabbit. Sometimes, when I'm writing code, I pause and think about the electricity flowing through the circuits, carrying my logic to the processor. Then, I realize, my code is just a bunch of organized lightning bolts, and suddenly I feel like Thor, but with a keyboard.",[],[],{"year":978,"number":263,"id":3362,"episodes":3468,"show":3469},[3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3350,3378],{"title":3380,"slug":3381},{"id":3283,"slug":3471,"vimeo_id":3472,"description":3473,"tile":3474,"length":527,"resources":12,"people":3475,"episode_number":159,"published":3479,"title":3480,"video_transcript_html":3481,"video_transcript_text":3482,"content":12,"seo":12,"status":19,"episode_people":3483,"recommendations":3485,"season":3486},"saron","925119261","Saron is the founder of Disco and the creator of Not a Designer, and here's the software, hardware, and analog tools she uses in her day-to-day.","c1ab432f-678c-4eba-add2-956de04e834a",[3476],{"name":3477,"url":3478},"Saron Yitbarek","https:\u002F\u002Ftwitter.com\u002Fsaronyitbarek","2024-04-23","What's In Your Dock, Saron?","\u003Cp>Saron: Hi. My name is Saron. I am the founder of disco and the creator of not a designer, and this is what's in my dock. Firstly, my devices. I use the MacBook Pro as my primary computing device.\u003C\u002Fp>\n\u003Cp>I also have my trusty iPhone, which is my phone. It has a lovely crack on it, which happened the day before New Year's, which honestly I think makes it look kind of pretty, and gives it some character, so I think I'm okay with it. And then lastly, I have my smartwatch, which actually isn't too smart. I was very opposed to having an Apple Watch. I tried it for a week, didn't really like it.\u003C\u002Fp>\n\u003Cp>I hate getting notifications on it. This is purely for health and fitness. It counts my steps, It tells me the time. It tells me the weather, and that's all I need from a watch. And it has, it's performed beautifully.\u003C\u002Fp>\n\u003Cp>My day to day as a founder involves a lot of writing and a lot of spreadsheets and a lot of planning, so I am in the Google Apps ecosystem all the time. I'm constantly writing emails or I'm busting out a spreadsheet to calculate something financial, something budget related, or to make, you know, let yet another contact list for another project I'm launching, or I'm in Google Docs and I'm writing my latest, newsletter issue for not a designer, my newsletter that I drop, or I'm in slides. I actually use slides quite a bit, Google slides. I use that for meeting agendas. I used to do agendas in Google Docs, but what I found is when you have this nice long agenda, you end up skipping ahead and kind of reading the whole thing while someone else is talking.\u003C\u002Fp>\n\u003Cp>So instead, now I use Google Slides, and now I can just focus one agenda item per slide and just have people focus on that one thing, and I found that it makes for much better meetings. So a couple other tools I recommend for newsletter writing is Beehive. I absolutely love Beehive. It's relatively new. It's only a couple years old, but I think they do such a phenomenal job.\u003C\u002Fp>\n\u003Cp>The focus of the user interface really is on the writing, so you get to really focus on getting your ideas on paper, getting really good content. I use it for my newsletter, not a designer, which is all about design tips for developers, and it does such a good job of giving you enough flexibility that you can kind of make it your own. It has some, you know, colors and some branding guidelines and things like that, but it's also relatively standardized, so you don't feel like you're overwhelmed with all these new design decisions you have to make. It comes with some segmentation. It comes with a survey.\u003C\u002Fp>\n\u003Cp>It has referrals. You can actually do, like, swag and do giveaways for referrals. So it's just a really fully featured, platform that does a really good job for newsletters. So if you're starting a newsletter, you can start one for free. I highly recommend Beehive.\u003C\u002Fp>\n\u003Cp>It's a really great piece of software. Another piece of software that I really like that I've been using, a lot recently is actually Typeform. It feels kind of old because it's not new. I feel like Typeform has been around for quite some time, but I was doing this application for my new initiative, Big Cash Money 2024, which is all about, income streams, different income streams for people who are looking to maximize revenue. And I was creating this application and trying out all these different tools, and typeform is like still the best one.\u003C\u002Fp>\n\u003Cp>It makes it so easy to break down the the questions one at a time. Their logic and their flow is really beautiful and really intuitive to use, and I feel like they took just as much care of the admin, the person actually administering the form, as they did the people taking the form, and it's just a delight to use. It has all these really great stats. It's a little pricey, so you gotta kinda gotta watch out for that, but besides that, it's just a really great tool, and I've just been really enjoying still using Typeform all these years later. And the 3rd piece of software is this new tool that I recently came across called Passionfruit, and it is essentially like a storefront, but for sponsorships for ads for creators.\u003C\u002Fp>\n\u003Cp>So for example, if I have a newsletter, like Not A Designer, and I want to sell an ad, I would have a storefront, and listed as a product is one of the ad spots available on my newsletter for that week. And I've actually used it not really as a creator, but I've used it as a sponsor. And it has made things just so much faster. It's so easy to just look at someone's storefront, look at their stats, see how much it costs, see when it's available, take care of payment all in one go, get your sponsorship accepted, get it reviewed. I've done sponsorships for many, many years for my first business, CodeNewbie, and I've started to do it for not a designer.\u003C\u002Fp>\n\u003Cp>And it has always been kind of a pain in the butt. It's been like a lot of back and forth, and this just makes the whole process so much I hope the creators you go for are on Passionfruit because it'll save you a lot of time. I don't know what's gone into me lately, but I've been really into country music, but very specific songs. So there are 4 specific country music songs that I've been really into. They're by Walker Hayes, Morgan Wallen, and Jordan Davis.\u003C\u002Fp>\n\u003Cp>And I just have those songs on repeat, and I've had them on repeat for the past maybe 2 or 3 weeks. Like, that's pretty much all I've been listening to, which is very strange because I'm usually not intercountry music, and I only got introduced to it, like, last year. But something about it just feels so great and comforting to work through and to to have in the background, and it's just been, like, feeding my soul. So that's been my big music go to recently. I really love my podcast mic.\u003C\u002Fp>\n\u003Cp>It's actually this mic right here. It's the Shure MV 7. And I love it because it is a USB mic that I feel like all the podcasters really got hip to over the last couple of years. I I feel like I see it on all the YouTube channels and all the the video podcasts that I've been looking at, that I've been watching. But it's just a really great mic.\u003C\u002Fp>\n\u003Cp>It's it's easy to use. It's it's I don't need, like, a mixer for it. I can just plug the tray into my laptop, and it's just a a really great convenient relatively affordable. I think it was, like, 200, 250, which is not too bad considering how expensive mics can get. So, yeah.\u003C\u002Fp>\n\u003Cp>That's been one of my favorite piece of hardware that, that I've been using. To be honest, I'm not very big on hobbies, but I have been doing weightlifting for almost a year. I think it's been about like 9, 10 months so far. And one of my favorite things that I've been using for weightlifting is actually this, like, grip hook thing. So, basically, you put it around your wrist, and you tighten it like this.\u003C\u002Fp>\n\u003Cp>And then you use this to lift the the bar. And it has been so helpful because I get a lot of wrist pain when I'm lifting, and this helps me kinda keep my wrist in place and keeps it just straight on, and it's helped me lift way more than I was able to lift before. So hugely love. I think they're called like power grip hooks or something, but they have been, really really helpful in that hobby. One thing that I do analog is my to do list.\u003C\u002Fp>\n\u003Cp>I don't use apps, I don't use software for it. I got my trusty little notebook, and I have this amazing, really cheap, classic, multicolored pen. There's 4 colors. I think think this is like a couple bucks, but I love this tool so much because I love using color coding to manage my to do items. I have blue as my primary task.\u003C\u002Fp>\n\u003Cp>I have black if it's like super serious. I've got green for, move it on to the next day. I use red if it's something that needs attention. And this gives me all the 4 colors I need. It's really cheap.\u003C\u002Fp>\n\u003Cp>It writes really smoothly. So, yeah, I do my to do list completely analog, and I use this trusty pen to help me get it done. And finally, something for my workspace. This is my absolute favorite thing that I have and that I own as far as work is concerned, and it's barely even work related. It's actually more for stress relief and anxiety, but this is my comfort wrap.\u003C\u002Fp>\n\u003Cp>So this is an electric blanket that you wrap around your shoulders, and it turns on. And it even has a little space here where you can put a little, aromatic, a little aromatherapy moment for you here. So you can like spray aroma essential oils and put in this little pocket, but I absolutely love this tool. I am a big fan of making work as enjoyable as possible. So when it's a little bit chilly at night especially, I'll put on my comfort wrap.\u003C\u002Fp>\n\u003Cp>I've got my hot tea going. I've got my little country music. I'm setting the scene, and I am super productive, super relaxed, having a great time. And that's how I get my work done. I got my hardware.\u003C\u002Fp>\n\u003Cp>I got my software. I got my physical tools and my comfort wrap. And if you wanna learn more about me or my work, check me out on Twitter at saronyitbarek, s aronyitbarek. Thanks.\u003C\u002Fp>","Hi. My name is Saron. I am the founder of disco and the creator of not a designer, and this is what's in my dock. Firstly, my devices. I use the MacBook Pro as my primary computing device. I also have my trusty iPhone, which is my phone. It has a lovely crack on it, which happened the day before New Year's, which honestly I think makes it look kind of pretty, and gives it some character, so I think I'm okay with it. And then lastly, I have my smartwatch, which actually isn't too smart. I was very opposed to having an Apple Watch. I tried it for a week, didn't really like it. I hate getting notifications on it. This is purely for health and fitness. It counts my steps, It tells me the time. It tells me the weather, and that's all I need from a watch. And it has, it's performed beautifully. My day to day as a founder involves a lot of writing and a lot of spreadsheets and a lot of planning, so I am in the Google Apps ecosystem all the time. I'm constantly writing emails or I'm busting out a spreadsheet to calculate something financial, something budget related, or to make, you know, let yet another contact list for another project I'm launching, or I'm in Google Docs and I'm writing my latest, newsletter issue for not a designer, my newsletter that I drop, or I'm in slides. I actually use slides quite a bit, Google slides. I use that for meeting agendas. I used to do agendas in Google Docs, but what I found is when you have this nice long agenda, you end up skipping ahead and kind of reading the whole thing while someone else is talking. So instead, now I use Google Slides, and now I can just focus one agenda item per slide and just have people focus on that one thing, and I found that it makes for much better meetings. So a couple other tools I recommend for newsletter writing is Beehive. I absolutely love Beehive. It's relatively new. It's only a couple years old, but I think they do such a phenomenal job. The focus of the user interface really is on the writing, so you get to really focus on getting your ideas on paper, getting really good content. I use it for my newsletter, not a designer, which is all about design tips for developers, and it does such a good job of giving you enough flexibility that you can kind of make it your own. It has some, you know, colors and some branding guidelines and things like that, but it's also relatively standardized, so you don't feel like you're overwhelmed with all these new design decisions you have to make. It comes with some segmentation. It comes with a survey. It has referrals. You can actually do, like, swag and do giveaways for referrals. So it's just a really fully featured, platform that does a really good job for newsletters. So if you're starting a newsletter, you can start one for free. I highly recommend Beehive. It's a really great piece of software. Another piece of software that I really like that I've been using, a lot recently is actually Typeform. It feels kind of old because it's not new. I feel like Typeform has been around for quite some time, but I was doing this application for my new initiative, Big Cash Money 2024, which is all about, income streams, different income streams for people who are looking to maximize revenue. And I was creating this application and trying out all these different tools, and typeform is like still the best one. It makes it so easy to break down the the questions one at a time. Their logic and their flow is really beautiful and really intuitive to use, and I feel like they took just as much care of the admin, the person actually administering the form, as they did the people taking the form, and it's just a delight to use. It has all these really great stats. It's a little pricey, so you gotta kinda gotta watch out for that, but besides that, it's just a really great tool, and I've just been really enjoying still using Typeform all these years later. And the 3rd piece of software is this new tool that I recently came across called Passionfruit, and it is essentially like a storefront, but for sponsorships for ads for creators. So for example, if I have a newsletter, like Not A Designer, and I want to sell an ad, I would have a storefront, and listed as a product is one of the ad spots available on my newsletter for that week. And I've actually used it not really as a creator, but I've used it as a sponsor. And it has made things just so much faster. It's so easy to just look at someone's storefront, look at their stats, see how much it costs, see when it's available, take care of payment all in one go, get your sponsorship accepted, get it reviewed. I've done sponsorships for many, many years for my first business, CodeNewbie, and I've started to do it for not a designer. And it has always been kind of a pain in the butt. It's been like a lot of back and forth, and this just makes the whole process so much I hope the creators you go for are on Passionfruit because it'll save you a lot of time. I don't know what's gone into me lately, but I've been really into country music, but very specific songs. So there are 4 specific country music songs that I've been really into. They're by Walker Hayes, Morgan Wallen, and Jordan Davis. And I just have those songs on repeat, and I've had them on repeat for the past maybe 2 or 3 weeks. Like, that's pretty much all I've been listening to, which is very strange because I'm usually not intercountry music, and I only got introduced to it, like, last year. But something about it just feels so great and comforting to work through and to to have in the background, and it's just been, like, feeding my soul. So that's been my big music go to recently. I really love my podcast mic. It's actually this mic right here. It's the Shure MV 7. And I love it because it is a USB mic that I feel like all the podcasters really got hip to over the last couple of years. I I feel like I see it on all the YouTube channels and all the the video podcasts that I've been looking at, that I've been watching. But it's just a really great mic. It's it's easy to use. It's it's I don't need, like, a mixer for it. I can just plug the tray into my laptop, and it's just a a really great convenient relatively affordable. I think it was, like, 200, 250, which is not too bad considering how expensive mics can get. So, yeah. That's been one of my favorite piece of hardware that, that I've been using. To be honest, I'm not very big on hobbies, but I have been doing weightlifting for almost a year. I think it's been about like 9, 10 months so far. And one of my favorite things that I've been using for weightlifting is actually this, like, grip hook thing. So, basically, you put it around your wrist, and you tighten it like this. And then you use this to lift the the bar. And it has been so helpful because I get a lot of wrist pain when I'm lifting, and this helps me kinda keep my wrist in place and keeps it just straight on, and it's helped me lift way more than I was able to lift before. So hugely love. I think they're called like power grip hooks or something, but they have been, really really helpful in that hobby. One thing that I do analog is my to do list. I don't use apps, I don't use software for it. I got my trusty little notebook, and I have this amazing, really cheap, classic, multicolored pen. There's 4 colors. I think think this is like a couple bucks, but I love this tool so much because I love using color coding to manage my to do items. I have blue as my primary task. I have black if it's like super serious. I've got green for, move it on to the next day. I use red if it's something that needs attention. And this gives me all the 4 colors I need. It's really cheap. It writes really smoothly. So, yeah, I do my to do list completely analog, and I use this trusty pen to help me get it done. And finally, something for my workspace. This is my absolute favorite thing that I have and that I own as far as work is concerned, and it's barely even work related. It's actually more for stress relief and anxiety, but this is my comfort wrap. So this is an electric blanket that you wrap around your shoulders, and it turns on. And it even has a little space here where you can put a little, aromatic, a little aromatherapy moment for you here. So you can like spray aroma essential oils and put in this little pocket, but I absolutely love this tool. I am a big fan of making work as enjoyable as possible. So when it's a little bit chilly at night especially, I'll put on my comfort wrap. I've got my hot tea going. I've got my little country music. I'm setting the scene, and I am super productive, super relaxed, having a great time. And that's how I get my work done. I got my hardware. I got my software. I got my physical tools and my comfort wrap. And if you wanna learn more about me or my work, check me out on Twitter at saronyitbarek, s aronyitbarek. Thanks.",[3484],"b53d69ab-6188-4836-836e-f151133fa82d",[],{"year":978,"number":3277,"id":3278,"episodes":3487,"show":3488},[3280,3281,3282,3283,3262],{"title":1027,"slug":1028},{"id":2752,"slug":3490,"vimeo_id":3491,"description":3492,"tile":3493,"length":527,"resources":12,"people":12,"episode_number":213,"published":3494,"title":3495,"video_transcript_html":3496,"video_transcript_text":3497,"content":12,"seo":12,"status":19,"episode_people":3498,"recommendations":3500,"season":3501},"the-simpsons-theme","931210680","There are no mistakes in this episode. Join Bry as he dusts the demons out of the keyboard and paints a wonderfully delightful theme worthy of the longest running TV on American television – The Simpsons.","a8d63115-8078-44e0-aefb-db5ea5cf7dc8","2024-04-22","The Simpsons (Doh!)","\u003Cp>Speaker 0: Hi. Welcome back to the show. I'm your host, Brad Ross. And for the next few moments together, you and I will experience the joy of theming. So we've got an extra special show for you today.\u003C\u002Fp>\u003Cp>We're gonna be painting a beautiful Simpsons theme. The Simpsons is one of the longest running animated shows in history and couples very well with Directus because Directus has been 20 years in the making. The Simpsons may be even older than some of you watching this. You know what? That's okay, We can all paint beautiful themes.\u003C\u002Fp>\u003Cp>So let's dive in. Alright, so whenever I'm painting a theme the first thing I always like to do is pull out my keyboard and bang in the project settings. So we'll just name this The Simpsons, it's the greatest show, and then we'll add our URL. So now that that's done we're going to pull out our brush a little bit and just dab a little bit of that Simpsons yellow all over our theme, all over our canvas here. Now we're gonna pick up the logo, add some public foreground, just use that same logo there.\u003C\u002Fp>\u003Cp>And then let's find a a great background for The Simpsons. What could be more perfect than painting just a little of that that couch scene that is in every episode. Now as far as the public note this is what displays on the bottom of the login page. So we'll just add that welcome to Simpsons Wiki. Wiki Wiki.\u003C\u002Fp>\u003Cp>Alright. And now for the public favicon maybe we'll just use this lovely image of Homer. Just paint that in there. Just great. Okay.\u003C\u002Fp>\u003Cp>So now we've got our basic shades. We've got a nice looking logo. Let's dive in and actually start theming this. One of the things that I wanna do, I'm gonna pull out my trusty palette knife and for our module background we're just gonna spread that yellow on our module background and then adjust our foreground colors. Now this is looking very nice, it's looking very Simpson y, if that's a good word.\u003C\u002Fp>\u003Cp>Now we'll just add a little bit of a hover effect to this, make it very subtle. And, any time you change colors remember that you have to just get in there and dust those demons out so those colors don't create unwanted combinations. Now as far as this theme, it's looking really nice. Let's go in and adjust some of our global settings. So our background for the the whole page here, I wanna use a Simpson color.\u003C\u002Fp>\u003Cp>So we're just gonna use a little CSS color mix there. That's a little bright, little bright. We're gonna dial that back to, like, 80%. Okay. So that's a nice mix.\u003C\u002Fp>\u003Cp>Color mix is one of my favorite methods in CSS to blend colors together. So now let's, how fun would it be if we took our palette knife and we just spread a little of that Homer Simpson donut all over this theme. So let's try and recreate that effect. We'll just look for a sprinkle SVG background and let's make a donut out of this. So we'll use a light pink color here and then bring out that fine detail brush that half inch brush just dial back the sprinkle opacity a little bit and wow this is looking really great.\u003C\u002Fp>\u003Cp>So I'm gonna copy this CSS, we're gonna open up my code editor and, I just want the URL piece of this. Now inside our navigation background paste that in there and just just paint those lovely sprinkles up and down this theme. That's looking really nice. This is looking like something Homer himself would love to eat. Alright, so we're gonna dive in as well.\u003C\u002Fp>\u003Cp>Now we're gonna change the background of the project settings up at the top, give it just a little bit of opacity to make it easier to see, but we don't want to obstruct our beautiful Let's also set up our little active, for those links. So we want the background to be white on active and then on hover we're just gonna paint in a nice little opacity, just 80% opacity. That's a really good value for opacity. You're doing great, hope you're following along at home and really enjoying this theme. Alright, so now we're gonna pick back up that palette knife just paint some border colors on there.\u003C\u002Fp>\u003Cp>There we go, we get a nice little border accent. Looking great, looking great. Alright, now we want to add a little bit of border to separate these different sections, give them a little more breathing room. So again, I'm gonna pull up, just this sprinkle, and a little darker 4 pixel border for the navigation on the far right side. And then I'm also gonna do the same on the module setting so that we can, separate that donut, make it look like, an actual donut here.\u003C\u002Fp>\u003Cp>So we'll just bang in some settings there for our border color and voila, it's looking really nice. Again, don't don't forget to use that duster liberally just because you don't want those colors to mix unwantedly. Oh, I just missed a little spot there. Alright, so now we've got this great, great looking thing. Things are starting to come together.\u003C\u002Fp>\u003Cp>But, I wanna adjust my fonts. So we see this font, it's a good looking font, but we need to add our title heading, but we also need that blue background. So let's do that. We're gonna use a little subtle blue across the top just to really set this theme off. So we'll just use our 5 inch, half inch brush there.\u003C\u002Fp>\u003Cp>Just really nice. Just paint that little detail in. Just keeps blending those colors a little bit till we get exactly what we're looking for out of it. Great. Great.\u003C\u002Fp>\u003Cp>Yeah. Nice. Very good. We've got a little darker border. This is chef's kiss.\u003C\u002Fp>\u003Cp>This is looking very nice. This is totally a theme that Homer would want to eat. Alright, so this is not a great font, but this, this rock salt font on Google Fonts looks very close to what the Simpsons title font is. So we'll just add that in there. Take a look at it.\u003C\u002Fp>\u003Cp>Maybe we dial that back a little bit to 400. And what I also wanna do here is just change the color to white. So I may just spread that color all over there. One of the other things that you'll notice is the Simpsons title font has a little subtle offset. It's got a black offset to it as well.\u003C\u002Fp>\u003Cp>So we can do that here inside custom CSS inside Directus. So it doesn't have to be all just themes, we can just go in and paint some custom CSS in there. That looks really nice. So now we've got our appearance set up. Everything's looking good.\u003C\u002Fp>\u003Cp>Let's dial this in even further because we we see we've got a search collection box that is just messing with our Mojo, and I don't like it. There are no mistakes, only happy accidents, opportunities to improve our theme, but this is one that I have to fix. So we'll just go in there and use our CSS selector for that. Just clean that up a little bit there. Blend that in.\u003C\u002Fp>\u003Cp>Looking very nice. Okay. All right. So we're done with our theme. Let's log out, take a look at our public login page, and wow, this really makes me feel like I'm right there in Springfield.\u003C\u002Fp>\u003Cp>That's it for this episode of the joy of theming. Tune in for the next episode where we have an extra special surprise for you.\u003C\u002Fp>","Hi. Welcome back to the show. I'm your host, Brad Ross. And for the next few moments together, you and I will experience the joy of theming. So we've got an extra special show for you today. We're gonna be painting a beautiful Simpsons theme. The Simpsons is one of the longest running animated shows in history and couples very well with Directus because Directus has been 20 years in the making. The Simpsons may be even older than some of you watching this. You know what? That's okay, We can all paint beautiful themes. So let's dive in. Alright, so whenever I'm painting a theme the first thing I always like to do is pull out my keyboard and bang in the project settings. So we'll just name this The Simpsons, it's the greatest show, and then we'll add our URL. So now that that's done we're going to pull out our brush a little bit and just dab a little bit of that Simpsons yellow all over our theme, all over our canvas here. Now we're gonna pick up the logo, add some public foreground, just use that same logo there. And then let's find a a great background for The Simpsons. What could be more perfect than painting just a little of that that couch scene that is in every episode. Now as far as the public note this is what displays on the bottom of the login page. So we'll just add that welcome to Simpsons Wiki. Wiki Wiki. Alright. And now for the public favicon maybe we'll just use this lovely image of Homer. Just paint that in there. Just great. Okay. So now we've got our basic shades. We've got a nice looking logo. Let's dive in and actually start theming this. One of the things that I wanna do, I'm gonna pull out my trusty palette knife and for our module background we're just gonna spread that yellow on our module background and then adjust our foreground colors. Now this is looking very nice, it's looking very Simpson y, if that's a good word. Now we'll just add a little bit of a hover effect to this, make it very subtle. And, any time you change colors remember that you have to just get in there and dust those demons out so those colors don't create unwanted combinations. Now as far as this theme, it's looking really nice. Let's go in and adjust some of our global settings. So our background for the the whole page here, I wanna use a Simpson color. So we're just gonna use a little CSS color mix there. That's a little bright, little bright. We're gonna dial that back to, like, 80%. Okay. So that's a nice mix. Color mix is one of my favorite methods in CSS to blend colors together. So now let's, how fun would it be if we took our palette knife and we just spread a little of that Homer Simpson donut all over this theme. So let's try and recreate that effect. We'll just look for a sprinkle SVG background and let's make a donut out of this. So we'll use a light pink color here and then bring out that fine detail brush that half inch brush just dial back the sprinkle opacity a little bit and wow this is looking really great. So I'm gonna copy this CSS, we're gonna open up my code editor and, I just want the URL piece of this. Now inside our navigation background paste that in there and just just paint those lovely sprinkles up and down this theme. That's looking really nice. This is looking like something Homer himself would love to eat. Alright, so we're gonna dive in as well. Now we're gonna change the background of the project settings up at the top, give it just a little bit of opacity to make it easier to see, but we don't want to obstruct our beautiful Let's also set up our little active, for those links. So we want the background to be white on active and then on hover we're just gonna paint in a nice little opacity, just 80% opacity. That's a really good value for opacity. You're doing great, hope you're following along at home and really enjoying this theme. Alright, so now we're gonna pick back up that palette knife just paint some border colors on there. There we go, we get a nice little border accent. Looking great, looking great. Alright, now we want to add a little bit of border to separate these different sections, give them a little more breathing room. So again, I'm gonna pull up, just this sprinkle, and a little darker 4 pixel border for the navigation on the far right side. And then I'm also gonna do the same on the module setting so that we can, separate that donut, make it look like, an actual donut here. So we'll just bang in some settings there for our border color and voila, it's looking really nice. Again, don't don't forget to use that duster liberally just because you don't want those colors to mix unwantedly. Oh, I just missed a little spot there. Alright, so now we've got this great, great looking thing. Things are starting to come together. But, I wanna adjust my fonts. So we see this font, it's a good looking font, but we need to add our title heading, but we also need that blue background. So let's do that. We're gonna use a little subtle blue across the top just to really set this theme off. So we'll just use our 5 inch, half inch brush there. Just really nice. Just paint that little detail in. Just keeps blending those colors a little bit till we get exactly what we're looking for out of it. Great. Great. Yeah. Nice. Very good. We've got a little darker border. This is chef's kiss. This is looking very nice. This is totally a theme that Homer would want to eat. Alright, so this is not a great font, but this, this rock salt font on Google Fonts looks very close to what the Simpsons title font is. So we'll just add that in there. Take a look at it. Maybe we dial that back a little bit to 400. And what I also wanna do here is just change the color to white. So I may just spread that color all over there. One of the other things that you'll notice is the Simpsons title font has a little subtle offset. It's got a black offset to it as well. So we can do that here inside custom CSS inside Directus. So it doesn't have to be all just themes, we can just go in and paint some custom CSS in there. That looks really nice. So now we've got our appearance set up. Everything's looking good. Let's dial this in even further because we we see we've got a search collection box that is just messing with our Mojo, and I don't like it. There are no mistakes, only happy accidents, opportunities to improve our theme, but this is one that I have to fix. So we'll just go in there and use our CSS selector for that. Just clean that up a little bit there. Blend that in. Looking very nice. Okay. All right. So we're done with our theme. Let's log out, take a look at our public login page, and wow, this really makes me feel like I'm right there in Springfield. That's it for this episode of the joy of theming. Tune in for the next episode where we have an extra special surprise for you.",[3499],"96d82e6e-504f-4595-8d58-58dc7f4ca48e",[],{"year":978,"number":2745,"id":2746,"episodes":3502,"show":3503},[2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2731],{"title":2760,"slug":2761},{"id":2313,"slug":3505,"vimeo_id":3506,"description":3507,"tile":3508,"length":1060,"resources":12,"people":12,"episode_number":13,"published":3509,"title":3510,"video_transcript_html":3511,"video_transcript_text":3512,"content":12,"seo":3513,"status":19,"episode_people":3514,"recommendations":3516,"season":3517},"crm","936325383","In this intense one-hour challenge, watch as Bryant incredibly builds a full-featured custom CRM from the ground up using Directus. He builds contacts, organizations, deal pipelines, activities, and more.","f6b880a0-5cd2-45ad-beff-3117f0a78581","2024-04-19","Mission: Customer Relationship Manager","\u003Cp>Speaker 0: Hi. Welcome back to another episode of 100 apps. 100 hours where we build some of your favorite apps or try to rebuild some of your favorite apps in 1 hour or less, or get publicly humiliated trying. Alright. Super excited to be back for another season.\u003C\u002Fp>\u003Cp>If you are new to 100 apps 100 hours, there are 2 basic rules. Number 1, you have 60 minutes to build and plan and build, no more, no less. So when that clock strikes 0, that's it. And then the second rule is there are no other rules. Use whatever you have at your disposal to complete the functionality.\u003C\u002Fp>\u003Cp>That's it. Let's dive into this episode. So today, I've got a custom CRM. Tools like Salesforce, Pipedrive, HubSpot, they really need no introduction. Everybody needs a CRM.\u003C\u002Fp>\u003Cp>A lot of them, depending on your purposes, may be way too much for what you need or they may not be specific enough for your industry. So we are going to build our own custom CRM in 1 hour or less. Let's do it baby. Alright. So, let's start the clock and away we go.\u003C\u002Fp>\u003Cp>So, when we plan our CRM, what do we need out of a CRM? Right? What kind of functionality do we want to see inside our CRM? So basically, we want to manage all of our contacts. We want to manage all the different organizations those contacts belong to.\u003C\u002Fp>\u003Cp>What else? We wanna manage our sales pipeline. Manage sales pipeline. That's gonna be deals or activities. We want to be able to track activities and follow-up.\u003C\u002Fp>\u003Cp>So this seems like a a pretty good set of functionality for a basic CRM. I'm sure we might embellish this a little bit depending on how far we get, but let's dive into actually fleshing out what the data model just might look like for something like this. So we'll drag a nice square up here. We're gonna have contacts. We could just call those people.\u003C\u002Fp>\u003Cp>I'm a big fan of that. We're gonna have organizations. There's definitely gonna be a relationship between those 2, but I I feel like this could be a many to many relationship because one contact could belong to multiple organizations, and and some CRMs make that a little more difficult to do than others. What else do we have? We're gonna have deals or opportunities.\u003C\u002Fp>\u003Cp>Deals is kind of a a standard nomenclature. We're gonna have activities that are attached to what, those are attached to organizations, they're attached to contacts, they're probably also gonna be attached to deals. What else are we going to have? We're probably going to have some sales reps, those are going to be our users inside our accounts. This looks pretty good for a a base set of functionality.\u003C\u002Fp>\u003Cp>Let's dive in and actually start building something. So, I'm just gonna pull up my Directus instance, that's what we're using on the back end. Totally blank instance, we'll zoom in on this, and let's start building. Right? Let's create our first collection.\u003C\u002Fp>\u003Cp>Let's start with contacts. So we're gonna give this a contacts as the name. For the primary key, let's use generated UUID. What kind of fields do we wanna add for our contacts? Right?\u003C\u002Fp>\u003Cp>When we think of contacts, we're gonna need name, email, but we may also wanna track when this was updated, when it was created. Directus makes that super easy. And one tip that you may not have realized if you've used Directus before, is I can go in and change these to be whatever I want. So if I wanted this to be created at and created by, I could go in and update those, change them to be whatever I want. Updated at, updated by.\u003C\u002Fp>\u003Cp>That's the the nomenclature that you're used to. Do we actually need a sort for our contacts? I don't think. Let's just go ahead and save this. Alright.\u003C\u002Fp>\u003Cp>So for our contacts, we're gonna add a first name. Great. We'll make that a string. Let's do half width for that. I can go in and click the three little dots here and just quickly duplicate this field to save myself some time.\u003C\u002Fp>\u003Cp>And, of course, as I am plotting away on this, Directus underneath is mirroring all these changes to my database schema. So we've got our first name, we've got our last name, we're gonna need a email or an email. Great. Let's require this value. And, you know, I could even go as far as, like, making this unique if I I wanted to have some type of unique identifier to match these up with.\u003C\u002Fp>\u003Cp>Alright. As far as an interface, you know, I could get fancy with this and we could add a little email icon like an at symbol. That's great. I think everything else is good. You know, one of the other nice things that's built into Directus is I can go in and add my own custom reg x validation.\u003C\u002Fp>\u003Cp>So, you know, I can put in some type of reg x that matches email addresses. I don't have one of those handy here, but we'll go ahead and keep marching along here. So we got first name, we got last name, we got email. We'll probably have a phone number at some point, but let's let's go for, like, a job title, probably. I'm trying to think of all the standard fields.\u003C\u002Fp>\u003Cp>Maybe we have some notes. We could set that to be a text area field. I don't really need formatting for those. Great. Job title.\u003C\u002Fp>\u003Cp>And maybe honestly, let let's strip this out because I'm I'm thinking about that many to many relationship with organizations, and we may have, different titles at different organizations. So I'll show you how we can handle that coming up. Alright. So we got first name, we got email. We could go ahead and add phone number as a string.\u003C\u002Fp>\u003Cp>Phone or phone number. Let's just keep it short. We'll say phone. Let's give it a phone icon. Great.\u003C\u002Fp>\u003Cp>K. Phone, email. We'll put email above phone. Cool. Looks nice.\u003C\u002Fp>\u003Cp>Alright. Let's just take a look. Right? We've got first name. Go ahead.\u003C\u002Fp>\u003Cp>Oh, I was gonna add my wife here, but she's gonna get mad at me if I misspell it. Right? Ashley atexample.com. I had a phone number, 555-5555. Here's some nice notes for our contact.\u003C\u002Fp>\u003Cp>These are looking great. Right? We've got our contact in here. I could potentially sort and filter this a a hundred different ways, maybe change the layout. But let's dive into the next collection that we wanna set up, our organizations.\u003C\u002Fp>\u003Cp>So we'll have organizations, again, for the primary key, I'm gonna use ID and just use generated UUID as the type. And, again, I could change created at I could change this structure for these system fields to be whatever I want. Created by, date updated, that's gonna be updated at, if I can actually spell. And we use this one as well and call it updated by. Great.\u003C\u002Fp>\u003Cp>Okay. So now we've got an organization. For our organization, what are we gonna have? We're gonna have a name of the organization. Right?\u003C\u002Fp>\u003Cp>Probably, some addresses for that organization. Right? We could have multiple. So that's where we might reach into an another bag of just a different table. What else are we gonna have for an organization?\u003C\u002Fp>\u003Cp>You know, let's just do something like country in case we want to, even, like, automatically route new organizations to a specific sales rep. Potentially, we've got a name. What are we gonna have? We're gonna have a website. You know, we may have a a logo if we wanna track that for a company.\u003C\u002Fp>\u003Cp>So let's do an image file. Let's call it logo. You know, you might even add things like brand color, things like that if if you're really in tracking that. And then we'll just add another section for notes. This is gonna be a text area field with the type text, and we'll hit save.\u003C\u002Fp>\u003Cp>So we've got our organizations. We've got our contacts. Let's make a link between the 2. Right? So how do we go about that?\u003C\u002Fp>\u003Cp>Depends on how you want the data to actually be structured. Right? In an application, depending on the setup, maybe a user can only belong to one specific organization. But often inside of CRM and maybe I am working with the local little league. I am a member of the board there, and then I'm also a developer advocate here at Directus or, you know, a a founder at Better Side Shop.\u003C\u002Fp>\u003Cp>So a lot of different relationships that is gonna be modeled with the mini to mini relationship inside Directus. Here's how we set that up. It's gonna be pretty easy. We'll just go to create field. We'll look for a many to many relationship.\u003C\u002Fp>\u003Cp>And because we're on organizations, our key here is gonna be contact for the or contacts, I should say. Our related collection is gonna be contacts and then we'll just go through and paint by numbers here. Do we want to show these in a list or a table? I'm good either way. We definitely want to show a link to that item so we can get to that specific contact, but I'm gonna pop open this advanced field creation mode just to show you that you do have control over the naming of the junction collection and the individual fields within that.\u003C\u002Fp>\u003Cp>So, the default setup here is just going to take this table name and this table name or this collection name and this collection name and marry the 2 together and we end up with organizations underscore contacts. Works for me. We'll leave that autofill set up. And then on the reverse, I'm gonna add that many to many field to the contacts as well. We're gonna keep that as the organizations.\u003C\u002Fp>\u003Cp>Alright? In this case, we may have a sort field. So we wanna control the the sorting for contacts, like who's 1st, 2nd, 3rd, that could be helpful in, like, a primary contact situation. And then we have some of our relational triggers. Right?\u003C\u002Fp>\u003Cp>On deselect of organizations' contacts, what do we want to do here? Yeah. Maybe I want to delete that association. So I'll set these to cascade. Great.\u003C\u002Fp>\u003Cp>So now that back out, we can see we've got our contacts field here. If we go into, yep, looks like we've got, an extra contact table created. I might have typed something wrong. But, so we can see contacts there in the organizations. And then on our contact, I should see organizations here.\u003C\u002Fp>\u003Cp>Let's just add an organization. Right? So let's say my wife worked at Tesla. That's in the United States. And, you know, we could add the website, logo, notes.\u003C\u002Fp>\u003Cp>I could choose existing contacts. But I'm just going to go ahead and say 1. Right? So now, I could see I've got that organization here, but it's showing an ID, which is not super helpful. Right?\u003C\u002Fp>\u003Cp>So I can go into the organization itself and control the display template where I have a name. I could even potentially add the logo to that if I wanted. So now if we check it again, we can see, okay, here's the organizations that they're a part of. Right? Now, you also have the ability to manage the data inside that junction collection.\u003C\u002Fp>\u003Cp>Right? So if I go in and and like I said, I may have a different job title at all these different organizations. So I could go in and add a new field here inside this junction collection for job title. Great. Alright.\u003C\u002Fp>\u003Cp>So, now if we go into Tesla, we can see the job title is down here at the bottom, but Directus also gives me an easy way to control where that displays. So if I go to our many to many relationship inside our contacts, I can go to maybe we wanna add a sort field for this just to make sure. And then if I go to the interface, I can control where my junction fields are located. So I can put this at the top, which should be great. Okay.\u003C\u002Fp>\u003Cp>So now if we just check that out one more time. Right. Now I could see director of DirectUs. So now I could give a specific job title to this specific person within that organization. I could go in and add a new organization as well, like, hey, the little league.\u003C\u002Fp>\u003Cp>We can say a board member. Right? Little League Baseball is the name of the organization. Great. Okay.\u003C\u002Fp>\u003Cp>So now there my wife is a part of 2 organizations, different job titles for each. Right? Alright. How are we looking on time? We're looking great.\u003C\u002Fp>\u003Cp>Let's move on to our next collection that we want to set up. That's gonna be what? Our deals, our activities, what do we want to set up next? Let's go for deals or opportunities. Deals is probably the standard naming for this.\u003C\u002Fp>\u003Cp>So that's what we'll stick with. Could be opportunities. Could be something else. That's fine. We'll do created at.\u003C\u002Fp>\u003Cp>Just to keep the same structure, we'll do created by. And by adding these, whenever this record is created, it's going to populate a timestamp and a user. The same thing for Updated, whenever it gets updated or who updates it, it's gonna populate that info for us. Updated by. Great.\u003C\u002Fp>\u003Cp>Okay. So our deals what we're gonna have for our deal? We'll probably have a name or a title of the deal. Deal name sounds great. It could be good to prefix some of these sometimes if you're dealing with a lot of nested relational data.\u003C\u002Fp>\u003Cp>So you get used to seeing name in there a 100 times. Could be confusing whether that's the actual deal name or the organization name or the contact name. So we'll just save the deal name. What else are we gonna have on this deal? Right?\u003C\u002Fp>\u003Cp>We'll probably have a average dollar amount or a potential dollar amount. Let's set that to be, decimal. Deal value. We can add a nice little dollar sign to the icon to make this look nice and pretty. And then we are going to have some related fields.\u003C\u002Fp>\u003Cp>But before we do that, let's just add, like, some deal notes or something like that. Sounds great. Okay. So we got a deal name, we got a deal value, you know, maybe these are side by side, not a big deal. Let's go in, and now we want to add a relationship to the organization and probably to our primary contact for this deal.\u003C\u002Fp>\u003Cp>So those are both gonna be many to one relationships. And we'll set a key of organization here. So the related collection, we'll set that to be organizations. Great. And I could control the display template here.\u003C\u002Fp>\u003Cp>We'll just use the name and we hit save. So now we've got an organization that we're gonna tie to the deal. I'll make that half width. And then we also want to add a primary contact. So we'll just say primary contact.\u003C\u002Fp>\u003Cp>The related collection here is going to be the Contacts collection. And for the Display template, let's use first name, last name, and let's use this format, which I think is typically how it displays inside an email client where you have the email address inside these less than or greater than SQL or symbols. So let's save that. We've got our primary contact. We've got our notes.\u003C\u002Fp>\u003Cp>We've got our deal. What do we need next? Right? We need to track where that deal is at. So we could use, like, a a status field as, like, a drop down for that potentially.\u003C\u002Fp>\u003Cp>Right? What's the status of this particular deal? Blah blah blah. Or we could do something else where we have a relationship to a deal stage or our individual pipeline. Right?\u003C\u002Fp>\u003Cp>Because then we may want to, potentially have separate pipelines or we we wanna give more control back to our users, so the the sales reps or the sales manager to control that actual pipeline without getting into the admin section. So how can we do that? Right? Looks like I've got a little extra couple of fields that I've been creating here inadvertently. Alright, so now let's add a new collection.\u003C\u002Fp>\u003Cp>Let's just call it Deal Stages. We want to manage what are the stages of a particular deal. As far as the optional fields, maybe I don't really not super concerned with these, deal stages. We're gonna call this the name of this particular stage. Maybe we wanna give it a color so we can add some color to it.\u003C\u002Fp>\u003Cp>And, you know, I guess we could even give it an icon if we wanted to. We could play around with that and see what that looks like. Deal stages. Let's make both of those half width. And, now, let's just go through and map some of these particular stages.\u003C\u002Fp>\u003Cp>Right? And I and I, you know, I've got this s on here. One of the other things that you could do inside Directus, you can't control the translations for all of these. So even if you're working in English, on the the back end, you may want to use prefix tables to keep things nice and organized as a developer. This is a great way to control what displays via the interface to your actual users within the application.\u003C\u002Fp>\u003Cp>So I could just call this deal stages or pipeline stages, whatever makes a lot of sense here, and hit save. Alright. So it still shows me deals_stages here, but when I start looking inside the actual app, there we go, we can see our pipeline stages. So, let's create our first one. Right?\u003C\u002Fp>\u003Cp>Let's just say, New, this is inbound. Do we have a symbol? We do have a symbol for this. Right? So let's say red.\u003C\u002Fp>\u003Cp>This is we need to take action on this. What's next? Right? Qualified, maybe? Contacted?\u003C\u002Fp>\u003Cp>Or, let's say assigned. Right? We forgot to assign those to a sales rep. Assign assignment. That's a nice looking icon for that.\u003C\u002Fp>\u003Cp>Then we'll say qualified. You know, if we're doing software sales, like, a check mark. Okay. What else? Demo.\u003C\u002Fp>\u003Cp>Let's save green. Oh, let's make this orange. Do we have like a demo? Maybe a TV? What have we got?\u003C\u002Fp>\u003Cp>Yeah, there we go. That works for a demo as far as the icons. And then we are going to set, like, a last stage, like, proposal. Great. Let's just go gray.\u003C\u002Fp>\u003Cp>Cool. Document. Awesome. Alright. So I can control the way that these are displayed through my actual settings, here.\u003C\u002Fp>\u003Cp>So I can go into each specific one. And on the display tab, let's display a colored dot for the color. And then for the icon, we're just gonna display the actual icon. Alright. Great.\u003C\u002Fp>\u003Cp>If we take a look now, I can see what these actually look like. Cool. And then for my display template for the deal stages, I may even go in and just mirror that same structure where I have a color, then I have an icon with a name. Okay. So, now, what we've done, we've effectively given control over these, and we probably actually need a sort on these as well, so so we can control that value.\u003C\u002Fp>\u003Cp>So, let's go in and quickly add a sort. That's going to be an integer value. And because this is coming after the fact, after we've created this actual table, I'm just gonna scroll down a little bit, and I will have a sort field here. So we'll choose sort just to make sure we manage that. And now I should be able to drag and drop these, in whatever order that I like.\u003C\u002Fp>\u003Cp>Again, we're giving control back to the users of our custom CRM. We're definitely gonna prevent them from getting inside the admin so they don't mess with the data model. But here, we give them the ability to control what those pipeline stages are gonna be. So now we need to add those to our actual deals. Right?\u003C\u002Fp>\u003Cp>So we're gonna go into our deals. We're going to create a many to one relationship. And, you know, this is going to be the deal stage or just stage. And we're gonna use deals underscore stages as the related collection. We can open this up and and just see what's going on.\u003C\u002Fp>\u003Cp>I I don't really need to create that inverse relationship. I could. Doesn't make a ton of sense, though. I'm not sure. Alright.\u003C\u002Fp>\u003Cp>Display related values, validation. Let's keep it very simple, and then we're gonna use our deal stage. Alright. So now if I go into our deals Why is this oh, it goes in the machine. I don't know what's going on on this particular example, but it keeps creating additional fields that we don't need.\u003C\u002Fp>\u003Cp>Alright. So let's create a new deal. Alright. This is gonna be a new deal. We can see, hey, there's our stage.\u003C\u002Fp>\u003Cp>That looks great. The deal name is 100 apps, 100 hours contract. It's worth $1,000,000. So we'll add that. How many zeros do we have there?\u003C\u002Fp>\u003Cp>1, 2, 3, 4, 5, 6. Alright. Let's make this for the Little League team. Great. My wife is the primary contact.\u003C\u002Fp>\u003Cp>Here's some notes on this deal. Great. Did we save it? Numeric value and deals is out of range. Uh-oh.\u003C\u002Fp>\u003Cp>Wouldn't be a 100 apps, a 100 hours without, some type of issue. Right? So let's take a look at this. The deal value, what did I set? Precision and scale.\u003C\u002Fp>\u003Cp>Maybe we bumped this up way up. Probably could have gone with integers for this as well. Do I have a minimum and a maximum? I I don't have that set. So not entirely sure what's going on.\u003C\u002Fp>\u003Cp>Let's just try this again. 100 apps, 100 hours contract, 1 100,000, 1,000,000. Try it again. Save. Okay.\u003C\u002Fp>\u003Cp>Alright. So now, we've got this particular deal set up. This is kind of an underwhelming view. Right? And, again, I might control how these things display.\u003C\u002Fp>\u003Cp>So if I just go into our deals, we go to the deal stage. If I wanna control how it displays, I can go in and set this. So let's do the color, do the icon, do the name, just because, I'm a I'm a very visual person, and I suspect, a lot of your end users may be as well. We still don't see the organization there. So, again, we'll go back.\u003C\u002Fp>\u003Cp>We'll adjust our organization. We wanna display related values. We want to show the name of that specific organization. And let's get even fancier and maybe we wanna show that logo. I'm gonna click on this and then you'll see this one that has a little magic beside it that's a thumbnail.\u003C\u002Fp>\u003Cp>That's just a shortcut for, like, a nice thumbnail sized image instead of loading the actual image size. So I donit have a logo for this company, but I could quickly find 1. Alright. So, we'll just copy this image address, go back to our instance, let's load up that specific company, they who shall not be named, Hit import. Oh, that's a data URL.\u003C\u002Fp>\u003Cp>Let's find an actual URL that we can just copy. Great. Let's try this again. We can import via URL. Cool.\u003C\u002Fp>\u003Cp>And now when I'm looking at my deals, I can see the logo of that organization. That makes it really handy, when I'm working on a deal just to have that extra extra visual reinforcement. Now, when I'm looking at deals, I may want to set up like a traditional pipeline type of view. So I could do that really easily just by switching the layout here inside Directus. So we'll change this to a Kanban layout.\u003C\u002Fp>\u003Cp>Let's group by the deal stage and the group title is gonna be the name. So I can see new assigned qualified demo. We have that Kanban view that we're used to. As far as the text, what do we wanna look at? We want to control, let's say, when this was created.\u003C\u002Fp>\u003Cp>I'm not really concerned about that. The tags, what do we want to set that to be? Do we want a card image? Honestly, this looks okay. Do we need an actual user on here?\u003C\u002Fp>\u003Cp>Probably not. Cool. Alright. We'll just keep it as is and let's let's start fleshing this out a little more as well. Right?\u003C\u002Fp>\u003Cp>So within a deal, when I'm working this deal, we're going to have activities assigned to this as it moves across the pipeline. And, for that, let's just go in and add a new table, a new collection, we're going to call it activities. Activities, I think that's the correct spelling. We use generated UID, we'll do created at, createdby, create up, updatedat, updated by. Great.\u003C\u002Fp>\u003Cp>And, in this case, maybe we do give a status for this particular activity. Right. Status, great. Let's give a name of the activity, probably a type of activity. Makes sense.\u003C\u002Fp>\u003Cp>We use a drop down for that. It seems pretty straightforward or, you know, maybe they're not gonna need to change the activity types on a a very frequent basis. So we'll call it the activity type, and let's add a couple choices to this. So this is a relatively new addition to Directus within the drop down, the ability to have an icon and a color. So let's say text is a phone call, value is a phone.\u003C\u002Fp>\u003Cp>One of the other cool things if you want to translate this value, you could use this phone t, or this dollar sign t for a translatable string. And then anytime you have users who are using the app within a different language, inside the settings, you can control all those custom translations. We'll take a look at that in a moment. Let's call this what? Phone call.\u003C\u002Fp>\u003Cp>The value could just be phone call, depending on how I wanna store this. Right? It may have an underscore within it as well. It could just be the same thing. Either way.\u003C\u002Fp>\u003Cp>Look for that phone. Great. And I could even add a color for that if needed. Maybe I just wanna keep these all the same. Phone call, we'll call it a meeting.\u003C\u002Fp>\u003Cp>And for this, do we have a meeting icon? Right? We calendar looks nice. Maybe we want to track demos separate from meetings. Alright.\u003C\u002Fp>\u003Cp>So we drag a demo. That was the TV icon that we had. Great. What else are we gonna need? Like, an email?\u003C\u002Fp>\u003Cp>Not sure I would Yeah. Maybe, like, a follow-up email. We wanna reschedule that. And then we'll do an email. Great.\u003C\u002Fp>\u003Cp>Demo, phone call. I think I'm gonna set this to be underscore value. Cool. Alright. Looking good.\u003C\u002Fp>\u003Cp>Activity type. Okay. What else are we going to need for this? We need a due date for this activity and the status, we can use as as is. It's published draft, archived.\u003C\u002Fp>\u003Cp>What do I really care about this activity? It probably isn't completed or not. Right? So maybe we scrap status, and maybe we just go for a toggle instead. Right?\u003C\u002Fp>\u003Cp>Hey. Is this completed? The default value, great. For our label, maybe we change it to completed and give it a color of color on. Color off is red, just to show that it has not been completed.\u003C\u002Fp>\u003Cp>Great. And let's just clean up this form a little bit, making sure everything looks nice. Okay. We are going to add a date for this, and let's set a specific date and time that this thing occurred. Call it due date, end date.\u003C\u002Fp>\u003Cp>Due date seems reasonable, but when do we actually need to complete this specific task? Okay. So now we've got an activity. We want to, link this to our actual deal so we can track those. Right?\u003C\u002Fp>\u003Cp>So, what I'm gonna do in this case is create a mini to one relationship because this activity could only belong to a single deal. Right? We're gonna use the key of deal. The related collection, we'll set that to deals. We'll show the name of that deal.\u003C\u002Fp>\u003Cp>Maybe we show the actual organization as well so I can actually dig into the related collections and and show values from that? Excuse me one second. Sorry about that. Cool. Alright.\u003C\u002Fp>\u003Cp>So now, what I forgot to do is create that inverse relationship. You can actually set that up via direct us when you're creating that relationship. But now I can also just go into our deals, not deal. We'll go into our deals, and now we're gonna create a one to many relationship back to those activities. So we'll call this key of activities.\u003C\u002Fp>\u003Cp>We've got activities. The foreign key will be deal. That already exists. Maybe we wanna show these in a table. We'll choose the columns, due date, Name, Due Date, Activity Type.\u003C\u002Fp>\u003Cp>Seems pretty savvy. And I can even filter these, right? So if I wanted to see just activities where they were not completed. We can enable search and filtering and show a link to these. We'll take a look at what all these look like in just a moment.\u003C\u002Fp>\u003Cp>But we've forgotten one important step through this whole process is, hey. We need somebody to assign these deals to. Right? So, let's add a many to one relationship. We'll call that the deal owner or, you know, you potentially say who this is assigned to.\u003C\u002Fp>\u003Cp>Like, the deal owners, again, kind of standard naming in these scenarios. And for the related collection here, we're gonna use directus underscore users. So these are gonna be actual users of the application that we're assigning this to. Invalid payload collections can't start with direct us users. Oh, deal owner.\u003C\u002Fp>\u003Cp>Let's go to our related collection, and let's get direct us underscore users. And, in this case, we're gonna show the first name, last name. We may back up and do an avatar as well. So just the the thumbnail, the avatar, I could move these around just by using edit raw value. You'll see these are just, the standard mustache syntax that you see throughout Directus as well.\u003C\u002Fp>\u003Cp>Excuse me. Let me get this a drink of water. I'm actually dying. That's a turn of fate. Okay.\u003C\u002Fp>\u003Cp>Alright. So we're gonna save this. Deal owner already exists in deals. Okay. Alright.\u003C\u002Fp>\u003Cp>Great. Let's move this around. Maybe we make deal stage half width. We slide deal owner up there. Let's actually take a look at this now.\u003C\u002Fp>\u003Cp>I should be able to assign folks. So let's create a new user. We'll just call it sales rep. Salesrep@example.com. And maybe we give them a nice avatar.\u003C\u002Fp>\u003Cp>Right? Sales rep, avatar. Let's just see what Google comes back with. John's inside sales rep. Yeah.\u003C\u002Fp>\u003Cp>This looks this is perfect. This is my guy right here. Alright. There's his avatar. We'll just save that.\u003C\u002Fp>\u003Cp>And now we can see who we've assigned this particular deal to. And now maybe within the deal card user here, I wanna show who that deal owner is. Great. Mister sales rep. Looking good.\u003C\u002Fp>\u003Cp>Alright. One of the other things that we need to do on our activities, we probably got a an owner of that activity or assigned to. It's been assigned to somebody on the team. Again, that's going to be assigned to a direct us user. We'll save that.\u003C\u002Fp>\u003Cp>And lots going on here. Just some type of weird glitch. I could see a couple of extra collections. I'll just remove these. Alright.\u003C\u002Fp>\u003Cp>Cool. So now weive got a deal. Weive got a table full of activities. I can go ahead and add these, like, follow-up on proposal. This is assigned to Mr.\u003C\u002Fp>\u003Cp>Sales Rep. We've got the activity type. We can see that's going to be, just a quick phone call. This is completed. We can see that conditional, conditional formatting for that.\u003C\u002Fp>\u003Cp>And we can add a due date of, let's say, the next Friday. Great. Save that. Keep editing. Cool.\u003C\u002Fp>\u003Cp>Alright. So, now, we've got the basic inner workings of a CRM. Right? We've got our deals, we've got contacts, we've got organizations, we've got our different pipeline stages. Right?\u003C\u002Fp>\u003Cp>If I wanted to organize these things a bit, we could go in and add different icons for each of these. So, you know, maybe we set some people icons for our contacts. We've got our organizations. Do we have an organization option? Let's look and see.\u003C\u002Fp>\u003Cp>Business. Is there a business? There you go. That looks somewhat like a business. We've got activities.\u003C\u002Fp>\u003Cp>Maybe this will be like a checklist. Cool. We've got our deals. Let's make those the money. Dollar signs, that's great.\u003C\u002Fp>\u003Cp>And then, deal stages, to me this is like a settings. Right? So I could create a new folder, let's just call it settings folder. You don't necessarily have to add this, but maybe we just do to keep it clean. And we look for, like, a settings icon just to use here.\u003C\u002Fp>\u003Cp>That's great. This one looks magical. Settings suggest. Right? And, again, I can change the name of this to just say settings.\u003C\u002Fp>\u003Cp>So, it still creates this collection, but, we can call it whatever we want. So we'll drag this up within settings folder. We'll drag deal stages and, this will be, what, like the Kanban view. There we go. Awesome.\u003C\u002Fp>\u003Cp>Okay. So now we get a little more organization to this. One of the other things that you might do and, you know, that you use all the time within ACRM are saved views. Right? So Directus gives you that ability with bookmarks.\u003C\u002Fp>\u003Cp>We'll just go in to the top here and maybe I want to sort by a specific sales rep. Right? Like, the deal owner is, a specific person and specific name is sales rep. That's the only one at this point. But I could go in and create a bookmark for this, and we could call it, deals sales rep Man.\u003C\u002Fp>\u003Cp>And we can change this up, give it a color. Now, within that collection, now we can see we've got our deals for Sales Rep Van, and I could save that bookmark. So even if I go into the main deals view, and maybe we change this back to a table view, which could be easier for, you know, maybe a sales manager or something who's controlling this. So I had deal owner back to this as well. Now if I go back, deal sales rep man view, boom, there it is.\u003C\u002Fp>\u003Cp>It's saved. I could go in and update this if I wanted to as well. So now we've got our pipeline. When we go into each one of the deal, we have our name, we've got our organization, we've got the notes, we've got our activities. You know, we can mark these activities off as completed.\u003C\u002Fp>\u003Cp>That seems like a great CRM baseline. Let's let's take a look at where we're at. Right? We got, like, 16 minutes on the clock. This feels like a win.\u003C\u002Fp>\u003Cp>I don't I don't know if we wanna run that one, let's discuss where we could go from here, right, maybe we want to automatically send some emails when it hits a certain stage in the pipeline. So let's just call these things done. Right? We can manage all of our contacts. We can manage all of our organizations.\u003C\u002Fp>\u003Cp>We can manage our sales pipelines. We can track our activities and follow-up. So let's say, you know, we get a new deal inbound. Maybe we want to automatically assign that to a a particular person, or we want to send a notification to our sales rep when that assignment happens. Let's figure out how to do that.\u003C\u002Fp>\u003Cp>Right? So I'm gonna go in. Let's just create a new deal. We'll say actually, let's wait a moment. Let's go into our flows.\u003C\u002Fp>\u003Cp>This is a good example. Right? Whenever a a new deal comes inbound, we want to send an email notification to our sales rep to to let them know. Alright. So we're gonna create a new flow.\u003C\u002Fp>\u003Cp>We'll just call it new inbound deal. Pretty straightforward. We could change this to the new symbol if we want to and just do a trigger setup. So what are we going to choose here? Directus gives you a ton of different options, as far as what to use when you're creating a flow.\u003C\u002Fp>\u003Cp>In this case, we're going to use the event hook. So when a certain event happens inside the platform, we wanna trigger an automation. The type that we're gonna choose here is action non blocking because we don't, the the filter allows you to basically either adjust the payload when a new deal gets created or a new event happens. Action non blocking, again, that runs after a create or update action. So in this case, let's do the items dot create.\u003C\u002Fp>\u003Cp>Whenever we're gonna trigger this based on the deals collection. And cool. Alright, so now I'm just going to save this, right? I'm going to go in and let's create a new deal inside the system. It's in the new stage.\u003C\u002Fp>\u003Cp>We're gonna assign this to mister sales rep. New deal automation. And we add, let's set this one to be for the little league. Again, we choose a specific contact, add some notes, and, maybe, the deal value, we'll just ballpark it at $5,000. Great.\u003C\u002Fp>\u003Cp>If I go back, now I can see that in my logs, I've got this flow. Here's the payload of this particular flow, and we could see who the deal owner is on this particular deal. How do we send an email notification to that specific owner? Right? I'm just gonna take this, copy it, and I open up just my Versus code editor where I've got my 100 apps, just, Docker Compose file here set up to run this locally.\u003C\u002Fp>\u003Cp>I'm just going to save that in case I need it. And, next, let's flesh this out a little bit. Right? So you can see the data we're getting back here. We're probably gonna need to look up that specific user.\u003C\u002Fp>\u003Cp>We can find them there. Those are the deal owner. We could send them a notification inside the app or we could send them a notification via email. Right? There's 2 options there.\u003C\u002Fp>\u003Cp>We've got notification. In this case, we've got the UUID of the user that we're going to send that to. So, you know, potentially, you want to send that in app. App. In this case, if there's a new deal, they're probably gonna be in their inbox.\u003C\u002Fp>\u003Cp>We're going to send that new deal to them. But let's actually find that email address first, though. Alright. So we're gonna read data from a specific collection. Let's call it Find User is the actual step we're gonna do here.\u003C\u002Fp>\u003Cp>For the permissions, let's just give full access. And, for the collection, you can see I don't have the Directus Users collection here, but I can go in and edit my raw value and just use Directus underscore users. And for our IDs, right, what are we gonna put here? So if I open this back up, we're gonna use the trigger. Payload.\u003C\u002Fp>\u003Cp>Dealowner. So we'll do this, we'll do trigger dot payload dot deal underscore owner. And I'm gonna wrap this in mustache syntax. And let's try this again. So we'll read the user.\u003C\u002Fp>\u003Cp>Let's go ahead and maybe just add this send email as well. So we'll send the email, and this is gonna be the read underscore user. We're using the key that we set of the previous operation within that flow. Readuser. Email should be.\u003C\u002Fp>\u003Cp>We'll input that. New deal assigned to you. And hey. Read_user.firstname. We've assigned a new deal to you.\u003C\u002Fp>\u003Cp>And then I can even go through and add those different variables if I wanted to for things like the deal name or the deal ID and and add a link back to that. So let's just try trigger. Payload.deal_name. Great. We'll save it.\u003C\u002Fp>\u003Cp>And now let's just test this out. I'm not actually sure if I've got emails set up here locally on this particular instance though, so that could be fun. But we should be able to actually see if this runs. In light of that bit of news, because it just looking at my configuration here, I do not have email configured here locally. So let's let's detach that one, and let's just test the notifications.\u003C\u002Fp>\u003Cp>Send notification. Cool. Send notification. The find_user.id. Cool.\u003C\u002Fp>\u003Cp>Full access. And it was a new deal assigned. We'll just add the key here. So that'll be trigger dot payload. Or no.\u003C\u002Fp>\u003Cp>Actually, it may be something like this where we have key. Alright. Let's just take a look just to make sure. Alright. Within our payload, we can see the key there.\u003C\u002Fp>\u003Cp>That's good. In that case, we probably didn't need to get the actual user there, but that's okay. I'm just gonna copy this message that we set here. Just paste that. And let's take a look at where this gets us.\u003C\u002Fp>\u003Cp>Alright. So we've got a new inbound deal. We're going to read the data of the user that we've assigned that to and we're going to send a notification to that specific person. Alright. Now we've got a new deal, deal stage.\u003C\u002Fp>\u003Cp>Let's just set it to new. Deal owner. I'm gonna choose myself here so we could test this notification. Say deal name. Test deal.\u003C\u002Fp>\u003Cp>It's worth $6,000,000. Very nice deal. We got a primary contact. Here's some notes. We'll just save that and let's see what happens.\u003C\u002Fp>\u003Cp>Right? We go to our Flow, we get a new inbound deal, we can see our logs, send notification, recipient is so and so. Hey, undefined. We've sent you a new test deal. Underscore first name.\u003C\u002Fp>\u003Cp>Why did that not come back? Read underscore oh, that's why I used the wrong key. Sometimes that happens. Find user dot first name. Great.\u003C\u002Fp>\u003Cp>So now, if I look and I check my activity log, I can see that I've got this new deal signed. Here's the notification for that. And if I click on it where it says view content, it should take me to that specific test deal. Great. Awesome.\u003C\u002Fp>\u003Cp>Let's call that a win. Right? We've got our custom CRM built out. We've set up some automation for this. We could go further and flesh this out a a ton of different ways.\u003C\u002Fp>\u003Cp>Right? If we talk about it, like, we could go through and send emails to our actual primary contact when it reaches a certain stage, that would be fairly easy to do using direct dis flows. You know, we could maybe even go as far as, like, a future future iteration of this could be setting up an inbox to parse incoming emails like a BCC functionality and add these to those specific deals as well. But this this feels really good. I'm I'm calling this a win.\u003C\u002Fp>\u003Cp>That's our custom CRM. Thanks for joining me on this episode of 100 apps, 100 hours. We'll catch you on the next one.\u003C\u002Fp>","Hi. Welcome back to another episode of 100 apps. 100 hours where we build some of your favorite apps or try to rebuild some of your favorite apps in 1 hour or less, or get publicly humiliated trying. Alright. Super excited to be back for another season. If you are new to 100 apps 100 hours, there are 2 basic rules. Number 1, you have 60 minutes to build and plan and build, no more, no less. So when that clock strikes 0, that's it. And then the second rule is there are no other rules. Use whatever you have at your disposal to complete the functionality. That's it. Let's dive into this episode. So today, I've got a custom CRM. Tools like Salesforce, Pipedrive, HubSpot, they really need no introduction. Everybody needs a CRM. A lot of them, depending on your purposes, may be way too much for what you need or they may not be specific enough for your industry. So we are going to build our own custom CRM in 1 hour or less. Let's do it baby. Alright. So, let's start the clock and away we go. So, when we plan our CRM, what do we need out of a CRM? Right? What kind of functionality do we want to see inside our CRM? So basically, we want to manage all of our contacts. We want to manage all the different organizations those contacts belong to. What else? We wanna manage our sales pipeline. Manage sales pipeline. That's gonna be deals or activities. We want to be able to track activities and follow-up. So this seems like a a pretty good set of functionality for a basic CRM. I'm sure we might embellish this a little bit depending on how far we get, but let's dive into actually fleshing out what the data model just might look like for something like this. So we'll drag a nice square up here. We're gonna have contacts. We could just call those people. I'm a big fan of that. We're gonna have organizations. There's definitely gonna be a relationship between those 2, but I I feel like this could be a many to many relationship because one contact could belong to multiple organizations, and and some CRMs make that a little more difficult to do than others. What else do we have? We're gonna have deals or opportunities. Deals is kind of a a standard nomenclature. We're gonna have activities that are attached to what, those are attached to organizations, they're attached to contacts, they're probably also gonna be attached to deals. What else are we going to have? We're probably going to have some sales reps, those are going to be our users inside our accounts. This looks pretty good for a a base set of functionality. Let's dive in and actually start building something. So, I'm just gonna pull up my Directus instance, that's what we're using on the back end. Totally blank instance, we'll zoom in on this, and let's start building. Right? Let's create our first collection. Let's start with contacts. So we're gonna give this a contacts as the name. For the primary key, let's use generated UUID. What kind of fields do we wanna add for our contacts? Right? When we think of contacts, we're gonna need name, email, but we may also wanna track when this was updated, when it was created. Directus makes that super easy. And one tip that you may not have realized if you've used Directus before, is I can go in and change these to be whatever I want. So if I wanted this to be created at and created by, I could go in and update those, change them to be whatever I want. Updated at, updated by. That's the the nomenclature that you're used to. Do we actually need a sort for our contacts? I don't think. Let's just go ahead and save this. Alright. So for our contacts, we're gonna add a first name. Great. We'll make that a string. Let's do half width for that. I can go in and click the three little dots here and just quickly duplicate this field to save myself some time. And, of course, as I am plotting away on this, Directus underneath is mirroring all these changes to my database schema. So we've got our first name, we've got our last name, we're gonna need a email or an email. Great. Let's require this value. And, you know, I could even go as far as, like, making this unique if I I wanted to have some type of unique identifier to match these up with. Alright. As far as an interface, you know, I could get fancy with this and we could add a little email icon like an at symbol. That's great. I think everything else is good. You know, one of the other nice things that's built into Directus is I can go in and add my own custom reg x validation. So, you know, I can put in some type of reg x that matches email addresses. I don't have one of those handy here, but we'll go ahead and keep marching along here. So we got first name, we got last name, we got email. We'll probably have a phone number at some point, but let's let's go for, like, a job title, probably. I'm trying to think of all the standard fields. Maybe we have some notes. We could set that to be a text area field. I don't really need formatting for those. Great. Job title. And maybe honestly, let let's strip this out because I'm I'm thinking about that many to many relationship with organizations, and we may have, different titles at different organizations. So I'll show you how we can handle that coming up. Alright. So we got first name, we got email. We could go ahead and add phone number as a string. Phone or phone number. Let's just keep it short. We'll say phone. Let's give it a phone icon. Great. K. Phone, email. We'll put email above phone. Cool. Looks nice. Alright. Let's just take a look. Right? We've got first name. Go ahead. Oh, I was gonna add my wife here, but she's gonna get mad at me if I misspell it. Right? Ashley atexample.com. I had a phone number, 555-5555. Here's some nice notes for our contact. These are looking great. Right? We've got our contact in here. I could potentially sort and filter this a a hundred different ways, maybe change the layout. But let's dive into the next collection that we wanna set up, our organizations. So we'll have organizations, again, for the primary key, I'm gonna use ID and just use generated UUID as the type. And, again, I could change created at I could change this structure for these system fields to be whatever I want. Created by, date updated, that's gonna be updated at, if I can actually spell. And we use this one as well and call it updated by. Great. Okay. So now we've got an organization. For our organization, what are we gonna have? We're gonna have a name of the organization. Right? Probably, some addresses for that organization. Right? We could have multiple. So that's where we might reach into an another bag of just a different table. What else are we gonna have for an organization? You know, let's just do something like country in case we want to, even, like, automatically route new organizations to a specific sales rep. Potentially, we've got a name. What are we gonna have? We're gonna have a website. You know, we may have a a logo if we wanna track that for a company. So let's do an image file. Let's call it logo. You know, you might even add things like brand color, things like that if if you're really in tracking that. And then we'll just add another section for notes. This is gonna be a text area field with the type text, and we'll hit save. So we've got our organizations. We've got our contacts. Let's make a link between the 2. Right? So how do we go about that? Depends on how you want the data to actually be structured. Right? In an application, depending on the setup, maybe a user can only belong to one specific organization. But often inside of CRM and maybe I am working with the local little league. I am a member of the board there, and then I'm also a developer advocate here at Directus or, you know, a a founder at Better Side Shop. So a lot of different relationships that is gonna be modeled with the mini to mini relationship inside Directus. Here's how we set that up. It's gonna be pretty easy. We'll just go to create field. We'll look for a many to many relationship. And because we're on organizations, our key here is gonna be contact for the or contacts, I should say. Our related collection is gonna be contacts and then we'll just go through and paint by numbers here. Do we want to show these in a list or a table? I'm good either way. We definitely want to show a link to that item so we can get to that specific contact, but I'm gonna pop open this advanced field creation mode just to show you that you do have control over the naming of the junction collection and the individual fields within that. So, the default setup here is just going to take this table name and this table name or this collection name and this collection name and marry the 2 together and we end up with organizations underscore contacts. Works for me. We'll leave that autofill set up. And then on the reverse, I'm gonna add that many to many field to the contacts as well. We're gonna keep that as the organizations. Alright? In this case, we may have a sort field. So we wanna control the the sorting for contacts, like who's 1st, 2nd, 3rd, that could be helpful in, like, a primary contact situation. And then we have some of our relational triggers. Right? On deselect of organizations' contacts, what do we want to do here? Yeah. Maybe I want to delete that association. So I'll set these to cascade. Great. So now that back out, we can see we've got our contacts field here. If we go into, yep, looks like we've got, an extra contact table created. I might have typed something wrong. But, so we can see contacts there in the organizations. And then on our contact, I should see organizations here. Let's just add an organization. Right? So let's say my wife worked at Tesla. That's in the United States. And, you know, we could add the website, logo, notes. I could choose existing contacts. But I'm just going to go ahead and say 1. Right? So now, I could see I've got that organization here, but it's showing an ID, which is not super helpful. Right? So I can go into the organization itself and control the display template where I have a name. I could even potentially add the logo to that if I wanted. So now if we check it again, we can see, okay, here's the organizations that they're a part of. Right? Now, you also have the ability to manage the data inside that junction collection. Right? So if I go in and and like I said, I may have a different job title at all these different organizations. So I could go in and add a new field here inside this junction collection for job title. Great. Alright. So, now if we go into Tesla, we can see the job title is down here at the bottom, but Directus also gives me an easy way to control where that displays. So if I go to our many to many relationship inside our contacts, I can go to maybe we wanna add a sort field for this just to make sure. And then if I go to the interface, I can control where my junction fields are located. So I can put this at the top, which should be great. Okay. So now if we just check that out one more time. Right. Now I could see director of DirectUs. So now I could give a specific job title to this specific person within that organization. I could go in and add a new organization as well, like, hey, the little league. We can say a board member. Right? Little League Baseball is the name of the organization. Great. Okay. So now there my wife is a part of 2 organizations, different job titles for each. Right? Alright. How are we looking on time? We're looking great. Let's move on to our next collection that we want to set up. That's gonna be what? Our deals, our activities, what do we want to set up next? Let's go for deals or opportunities. Deals is probably the standard naming for this. So that's what we'll stick with. Could be opportunities. Could be something else. That's fine. We'll do created at. Just to keep the same structure, we'll do created by. And by adding these, whenever this record is created, it's going to populate a timestamp and a user. The same thing for Updated, whenever it gets updated or who updates it, it's gonna populate that info for us. Updated by. Great. Okay. So our deals what we're gonna have for our deal? We'll probably have a name or a title of the deal. Deal name sounds great. It could be good to prefix some of these sometimes if you're dealing with a lot of nested relational data. So you get used to seeing name in there a 100 times. Could be confusing whether that's the actual deal name or the organization name or the contact name. So we'll just save the deal name. What else are we gonna have on this deal? Right? We'll probably have a average dollar amount or a potential dollar amount. Let's set that to be, decimal. Deal value. We can add a nice little dollar sign to the icon to make this look nice and pretty. And then we are going to have some related fields. But before we do that, let's just add, like, some deal notes or something like that. Sounds great. Okay. So we got a deal name, we got a deal value, you know, maybe these are side by side, not a big deal. Let's go in, and now we want to add a relationship to the organization and probably to our primary contact for this deal. So those are both gonna be many to one relationships. And we'll set a key of organization here. So the related collection, we'll set that to be organizations. Great. And I could control the display template here. We'll just use the name and we hit save. So now we've got an organization that we're gonna tie to the deal. I'll make that half width. And then we also want to add a primary contact. So we'll just say primary contact. The related collection here is going to be the Contacts collection. And for the Display template, let's use first name, last name, and let's use this format, which I think is typically how it displays inside an email client where you have the email address inside these less than or greater than SQL or symbols. So let's save that. We've got our primary contact. We've got our notes. We've got our deal. What do we need next? Right? We need to track where that deal is at. So we could use, like, a a status field as, like, a drop down for that potentially. Right? What's the status of this particular deal? Blah blah blah. Or we could do something else where we have a relationship to a deal stage or our individual pipeline. Right? Because then we may want to, potentially have separate pipelines or we we wanna give more control back to our users, so the the sales reps or the sales manager to control that actual pipeline without getting into the admin section. So how can we do that? Right? Looks like I've got a little extra couple of fields that I've been creating here inadvertently. Alright, so now let's add a new collection. Let's just call it Deal Stages. We want to manage what are the stages of a particular deal. As far as the optional fields, maybe I don't really not super concerned with these, deal stages. We're gonna call this the name of this particular stage. Maybe we wanna give it a color so we can add some color to it. And, you know, I guess we could even give it an icon if we wanted to. We could play around with that and see what that looks like. Deal stages. Let's make both of those half width. And, now, let's just go through and map some of these particular stages. Right? And I and I, you know, I've got this s on here. One of the other things that you could do inside Directus, you can't control the translations for all of these. So even if you're working in English, on the the back end, you may want to use prefix tables to keep things nice and organized as a developer. This is a great way to control what displays via the interface to your actual users within the application. So I could just call this deal stages or pipeline stages, whatever makes a lot of sense here, and hit save. Alright. So it still shows me deals_stages here, but when I start looking inside the actual app, there we go, we can see our pipeline stages. So, let's create our first one. Right? Let's just say, New, this is inbound. Do we have a symbol? We do have a symbol for this. Right? So let's say red. This is we need to take action on this. What's next? Right? Qualified, maybe? Contacted? Or, let's say assigned. Right? We forgot to assign those to a sales rep. Assign assignment. That's a nice looking icon for that. Then we'll say qualified. You know, if we're doing software sales, like, a check mark. Okay. What else? Demo. Let's save green. Oh, let's make this orange. Do we have like a demo? Maybe a TV? What have we got? Yeah, there we go. That works for a demo as far as the icons. And then we are going to set, like, a last stage, like, proposal. Great. Let's just go gray. Cool. Document. Awesome. Alright. So I can control the way that these are displayed through my actual settings, here. So I can go into each specific one. And on the display tab, let's display a colored dot for the color. And then for the icon, we're just gonna display the actual icon. Alright. Great. If we take a look now, I can see what these actually look like. Cool. And then for my display template for the deal stages, I may even go in and just mirror that same structure where I have a color, then I have an icon with a name. Okay. So, now, what we've done, we've effectively given control over these, and we probably actually need a sort on these as well, so so we can control that value. So, let's go in and quickly add a sort. That's going to be an integer value. And because this is coming after the fact, after we've created this actual table, I'm just gonna scroll down a little bit, and I will have a sort field here. So we'll choose sort just to make sure we manage that. And now I should be able to drag and drop these, in whatever order that I like. Again, we're giving control back to the users of our custom CRM. We're definitely gonna prevent them from getting inside the admin so they don't mess with the data model. But here, we give them the ability to control what those pipeline stages are gonna be. So now we need to add those to our actual deals. Right? So we're gonna go into our deals. We're going to create a many to one relationship. And, you know, this is going to be the deal stage or just stage. And we're gonna use deals underscore stages as the related collection. We can open this up and and just see what's going on. I I don't really need to create that inverse relationship. I could. Doesn't make a ton of sense, though. I'm not sure. Alright. Display related values, validation. Let's keep it very simple, and then we're gonna use our deal stage. Alright. So now if I go into our deals Why is this oh, it goes in the machine. I don't know what's going on on this particular example, but it keeps creating additional fields that we don't need. Alright. So let's create a new deal. Alright. This is gonna be a new deal. We can see, hey, there's our stage. That looks great. The deal name is 100 apps, 100 hours contract. It's worth $1,000,000. So we'll add that. How many zeros do we have there? 1, 2, 3, 4, 5, 6. Alright. Let's make this for the Little League team. Great. My wife is the primary contact. Here's some notes on this deal. Great. Did we save it? Numeric value and deals is out of range. Uh-oh. Wouldn't be a 100 apps, a 100 hours without, some type of issue. Right? So let's take a look at this. The deal value, what did I set? Precision and scale. Maybe we bumped this up way up. Probably could have gone with integers for this as well. Do I have a minimum and a maximum? I I don't have that set. So not entirely sure what's going on. Let's just try this again. 100 apps, 100 hours contract, 1 100,000, 1,000,000. Try it again. Save. Okay. Alright. So now, we've got this particular deal set up. This is kind of an underwhelming view. Right? And, again, I might control how these things display. So if I just go into our deals, we go to the deal stage. If I wanna control how it displays, I can go in and set this. So let's do the color, do the icon, do the name, just because, I'm a I'm a very visual person, and I suspect, a lot of your end users may be as well. We still don't see the organization there. So, again, we'll go back. We'll adjust our organization. We wanna display related values. We want to show the name of that specific organization. And let's get even fancier and maybe we wanna show that logo. I'm gonna click on this and then you'll see this one that has a little magic beside it that's a thumbnail. That's just a shortcut for, like, a nice thumbnail sized image instead of loading the actual image size. So I donit have a logo for this company, but I could quickly find 1. Alright. So, we'll just copy this image address, go back to our instance, let's load up that specific company, they who shall not be named, Hit import. Oh, that's a data URL. Let's find an actual URL that we can just copy. Great. Let's try this again. We can import via URL. Cool. And now when I'm looking at my deals, I can see the logo of that organization. That makes it really handy, when I'm working on a deal just to have that extra extra visual reinforcement. Now, when I'm looking at deals, I may want to set up like a traditional pipeline type of view. So I could do that really easily just by switching the layout here inside Directus. So we'll change this to a Kanban layout. Let's group by the deal stage and the group title is gonna be the name. So I can see new assigned qualified demo. We have that Kanban view that we're used to. As far as the text, what do we wanna look at? We want to control, let's say, when this was created. I'm not really concerned about that. The tags, what do we want to set that to be? Do we want a card image? Honestly, this looks okay. Do we need an actual user on here? Probably not. Cool. Alright. We'll just keep it as is and let's let's start fleshing this out a little more as well. Right? So within a deal, when I'm working this deal, we're going to have activities assigned to this as it moves across the pipeline. And, for that, let's just go in and add a new table, a new collection, we're going to call it activities. Activities, I think that's the correct spelling. We use generated UID, we'll do created at, createdby, create up, updatedat, updated by. Great. And, in this case, maybe we do give a status for this particular activity. Right. Status, great. Let's give a name of the activity, probably a type of activity. Makes sense. We use a drop down for that. It seems pretty straightforward or, you know, maybe they're not gonna need to change the activity types on a a very frequent basis. So we'll call it the activity type, and let's add a couple choices to this. So this is a relatively new addition to Directus within the drop down, the ability to have an icon and a color. So let's say text is a phone call, value is a phone. One of the other cool things if you want to translate this value, you could use this phone t, or this dollar sign t for a translatable string. And then anytime you have users who are using the app within a different language, inside the settings, you can control all those custom translations. We'll take a look at that in a moment. Let's call this what? Phone call. The value could just be phone call, depending on how I wanna store this. Right? It may have an underscore within it as well. It could just be the same thing. Either way. Look for that phone. Great. And I could even add a color for that if needed. Maybe I just wanna keep these all the same. Phone call, we'll call it a meeting. And for this, do we have a meeting icon? Right? We calendar looks nice. Maybe we want to track demos separate from meetings. Alright. So we drag a demo. That was the TV icon that we had. Great. What else are we gonna need? Like, an email? Not sure I would Yeah. Maybe, like, a follow-up email. We wanna reschedule that. And then we'll do an email. Great. Demo, phone call. I think I'm gonna set this to be underscore value. Cool. Alright. Looking good. Activity type. Okay. What else are we going to need for this? We need a due date for this activity and the status, we can use as as is. It's published draft, archived. What do I really care about this activity? It probably isn't completed or not. Right? So maybe we scrap status, and maybe we just go for a toggle instead. Right? Hey. Is this completed? The default value, great. For our label, maybe we change it to completed and give it a color of color on. Color off is red, just to show that it has not been completed. Great. And let's just clean up this form a little bit, making sure everything looks nice. Okay. We are going to add a date for this, and let's set a specific date and time that this thing occurred. Call it due date, end date. Due date seems reasonable, but when do we actually need to complete this specific task? Okay. So now we've got an activity. We want to, link this to our actual deal so we can track those. Right? So, what I'm gonna do in this case is create a mini to one relationship because this activity could only belong to a single deal. Right? We're gonna use the key of deal. The related collection, we'll set that to deals. We'll show the name of that deal. Maybe we show the actual organization as well so I can actually dig into the related collections and and show values from that? Excuse me one second. Sorry about that. Cool. Alright. So now, what I forgot to do is create that inverse relationship. You can actually set that up via direct us when you're creating that relationship. But now I can also just go into our deals, not deal. We'll go into our deals, and now we're gonna create a one to many relationship back to those activities. So we'll call this key of activities. We've got activities. The foreign key will be deal. That already exists. Maybe we wanna show these in a table. We'll choose the columns, due date, Name, Due Date, Activity Type. Seems pretty savvy. And I can even filter these, right? So if I wanted to see just activities where they were not completed. We can enable search and filtering and show a link to these. We'll take a look at what all these look like in just a moment. But we've forgotten one important step through this whole process is, hey. We need somebody to assign these deals to. Right? So, let's add a many to one relationship. We'll call that the deal owner or, you know, you potentially say who this is assigned to. Like, the deal owners, again, kind of standard naming in these scenarios. And for the related collection here, we're gonna use directus underscore users. So these are gonna be actual users of the application that we're assigning this to. Invalid payload collections can't start with direct us users. Oh, deal owner. Let's go to our related collection, and let's get direct us underscore users. And, in this case, we're gonna show the first name, last name. We may back up and do an avatar as well. So just the the thumbnail, the avatar, I could move these around just by using edit raw value. You'll see these are just, the standard mustache syntax that you see throughout Directus as well. Excuse me. Let me get this a drink of water. I'm actually dying. That's a turn of fate. Okay. Alright. So we're gonna save this. Deal owner already exists in deals. Okay. Alright. Great. Let's move this around. Maybe we make deal stage half width. We slide deal owner up there. Let's actually take a look at this now. I should be able to assign folks. So let's create a new user. We'll just call it sales rep. Salesrep@example.com. And maybe we give them a nice avatar. Right? Sales rep, avatar. Let's just see what Google comes back with. John's inside sales rep. Yeah. This looks this is perfect. This is my guy right here. Alright. There's his avatar. We'll just save that. And now we can see who we've assigned this particular deal to. And now maybe within the deal card user here, I wanna show who that deal owner is. Great. Mister sales rep. Looking good. Alright. One of the other things that we need to do on our activities, we probably got a an owner of that activity or assigned to. It's been assigned to somebody on the team. Again, that's going to be assigned to a direct us user. We'll save that. And lots going on here. Just some type of weird glitch. I could see a couple of extra collections. I'll just remove these. Alright. Cool. So now weive got a deal. Weive got a table full of activities. I can go ahead and add these, like, follow-up on proposal. This is assigned to Mr. Sales Rep. We've got the activity type. We can see that's going to be, just a quick phone call. This is completed. We can see that conditional, conditional formatting for that. And we can add a due date of, let's say, the next Friday. Great. Save that. Keep editing. Cool. Alright. So, now, we've got the basic inner workings of a CRM. Right? We've got our deals, we've got contacts, we've got organizations, we've got our different pipeline stages. Right? If I wanted to organize these things a bit, we could go in and add different icons for each of these. So, you know, maybe we set some people icons for our contacts. We've got our organizations. Do we have an organization option? Let's look and see. Business. Is there a business? There you go. That looks somewhat like a business. We've got activities. Maybe this will be like a checklist. Cool. We've got our deals. Let's make those the money. Dollar signs, that's great. And then, deal stages, to me this is like a settings. Right? So I could create a new folder, let's just call it settings folder. You don't necessarily have to add this, but maybe we just do to keep it clean. And we look for, like, a settings icon just to use here. That's great. This one looks magical. Settings suggest. Right? And, again, I can change the name of this to just say settings. So, it still creates this collection, but, we can call it whatever we want. So we'll drag this up within settings folder. We'll drag deal stages and, this will be, what, like the Kanban view. There we go. Awesome. Okay. So now we get a little more organization to this. One of the other things that you might do and, you know, that you use all the time within ACRM are saved views. Right? So Directus gives you that ability with bookmarks. We'll just go in to the top here and maybe I want to sort by a specific sales rep. Right? Like, the deal owner is, a specific person and specific name is sales rep. That's the only one at this point. But I could go in and create a bookmark for this, and we could call it, deals sales rep Man. And we can change this up, give it a color. Now, within that collection, now we can see we've got our deals for Sales Rep Van, and I could save that bookmark. So even if I go into the main deals view, and maybe we change this back to a table view, which could be easier for, you know, maybe a sales manager or something who's controlling this. So I had deal owner back to this as well. Now if I go back, deal sales rep man view, boom, there it is. It's saved. I could go in and update this if I wanted to as well. So now we've got our pipeline. When we go into each one of the deal, we have our name, we've got our organization, we've got the notes, we've got our activities. You know, we can mark these activities off as completed. That seems like a great CRM baseline. Let's let's take a look at where we're at. Right? We got, like, 16 minutes on the clock. This feels like a win. I don't I don't know if we wanna run that one, let's discuss where we could go from here, right, maybe we want to automatically send some emails when it hits a certain stage in the pipeline. So let's just call these things done. Right? We can manage all of our contacts. We can manage all of our organizations. We can manage our sales pipelines. We can track our activities and follow-up. So let's say, you know, we get a new deal inbound. Maybe we want to automatically assign that to a a particular person, or we want to send a notification to our sales rep when that assignment happens. Let's figure out how to do that. Right? So I'm gonna go in. Let's just create a new deal. We'll say actually, let's wait a moment. Let's go into our flows. This is a good example. Right? Whenever a a new deal comes inbound, we want to send an email notification to our sales rep to to let them know. Alright. So we're gonna create a new flow. We'll just call it new inbound deal. Pretty straightforward. We could change this to the new symbol if we want to and just do a trigger setup. So what are we going to choose here? Directus gives you a ton of different options, as far as what to use when you're creating a flow. In this case, we're going to use the event hook. So when a certain event happens inside the platform, we wanna trigger an automation. The type that we're gonna choose here is action non blocking because we don't, the the filter allows you to basically either adjust the payload when a new deal gets created or a new event happens. Action non blocking, again, that runs after a create or update action. So in this case, let's do the items dot create. Whenever we're gonna trigger this based on the deals collection. And cool. Alright, so now I'm just going to save this, right? I'm going to go in and let's create a new deal inside the system. It's in the new stage. We're gonna assign this to mister sales rep. New deal automation. And we add, let's set this one to be for the little league. Again, we choose a specific contact, add some notes, and, maybe, the deal value, we'll just ballpark it at $5,000. Great. If I go back, now I can see that in my logs, I've got this flow. Here's the payload of this particular flow, and we could see who the deal owner is on this particular deal. How do we send an email notification to that specific owner? Right? I'm just gonna take this, copy it, and I open up just my Versus code editor where I've got my 100 apps, just, Docker Compose file here set up to run this locally. I'm just going to save that in case I need it. And, next, let's flesh this out a little bit. Right? So you can see the data we're getting back here. We're probably gonna need to look up that specific user. We can find them there. Those are the deal owner. We could send them a notification inside the app or we could send them a notification via email. Right? There's 2 options there. We've got notification. In this case, we've got the UUID of the user that we're going to send that to. So, you know, potentially, you want to send that in app. App. In this case, if there's a new deal, they're probably gonna be in their inbox. We're going to send that new deal to them. But let's actually find that email address first, though. Alright. So we're gonna read data from a specific collection. Let's call it Find User is the actual step we're gonna do here. For the permissions, let's just give full access. And, for the collection, you can see I don't have the Directus Users collection here, but I can go in and edit my raw value and just use Directus underscore users. And for our IDs, right, what are we gonna put here? So if I open this back up, we're gonna use the trigger. Payload. Dealowner. So we'll do this, we'll do trigger dot payload dot deal underscore owner. And I'm gonna wrap this in mustache syntax. And let's try this again. So we'll read the user. Let's go ahead and maybe just add this send email as well. So we'll send the email, and this is gonna be the read underscore user. We're using the key that we set of the previous operation within that flow. Readuser. Email should be. We'll input that. New deal assigned to you. And hey. Read_user.firstname. We've assigned a new deal to you. And then I can even go through and add those different variables if I wanted to for things like the deal name or the deal ID and and add a link back to that. So let's just try trigger. Payload.deal_name. Great. We'll save it. And now let's just test this out. I'm not actually sure if I've got emails set up here locally on this particular instance though, so that could be fun. But we should be able to actually see if this runs. In light of that bit of news, because it just looking at my configuration here, I do not have email configured here locally. So let's let's detach that one, and let's just test the notifications. Send notification. Cool. Send notification. The find_user.id. Cool. Full access. And it was a new deal assigned. We'll just add the key here. So that'll be trigger dot payload. Or no. Actually, it may be something like this where we have key. Alright. Let's just take a look just to make sure. Alright. Within our payload, we can see the key there. That's good. In that case, we probably didn't need to get the actual user there, but that's okay. I'm just gonna copy this message that we set here. Just paste that. And let's take a look at where this gets us. Alright. So we've got a new inbound deal. We're going to read the data of the user that we've assigned that to and we're going to send a notification to that specific person. Alright. Now we've got a new deal, deal stage. Let's just set it to new. Deal owner. I'm gonna choose myself here so we could test this notification. Say deal name. Test deal. It's worth $6,000,000. Very nice deal. We got a primary contact. Here's some notes. We'll just save that and let's see what happens. Right? We go to our Flow, we get a new inbound deal, we can see our logs, send notification, recipient is so and so. Hey, undefined. We've sent you a new test deal. Underscore first name. Why did that not come back? Read underscore oh, that's why I used the wrong key. Sometimes that happens. Find user dot first name. Great. So now, if I look and I check my activity log, I can see that I've got this new deal signed. Here's the notification for that. And if I click on it where it says view content, it should take me to that specific test deal. Great. Awesome. Let's call that a win. Right? We've got our custom CRM built out. We've set up some automation for this. We could go further and flesh this out a a ton of different ways. Right? If we talk about it, like, we could go through and send emails to our actual primary contact when it reaches a certain stage, that would be fairly easy to do using direct dis flows. You know, we could maybe even go as far as, like, a future future iteration of this could be setting up an inbox to parse incoming emails like a BCC functionality and add these to those specific deals as well. But this this feels really good. I'm I'm calling this a win. That's our custom CRM. Thanks for joining me on this episode of 100 apps, 100 hours. We'll catch you on the next one.","592c22ad-1cb2-4d65-9bd7-e0d7c98fe4f2",[3515],"e9e66fa8-0650-4e37-ae8b-74755fdd5dca",[],{"year":978,"number":2062,"id":2311,"episodes":3518,"show":3519},[2313,2314,2315,2316,2317,2318,2319,2320,2321,2298],{"title":322,"slug":323},{"id":3345,"slug":3521,"vimeo_id":3522,"description":3523,"tile":3524,"length":3525,"resources":12,"people":3526,"episode_number":264,"published":3529,"title":3530,"video_transcript_html":3531,"video_transcript_text":3532,"content":12,"seo":12,"status":19,"episode_people":3533,"recommendations":3536,"season":3537},"visualize-battlesnake","925465713","Having build a very powerful snake, Kevin and Andrew seek to bask in the glory of their intellect by visualizing their wins. But not is all as it seems...","a1039337-33b5-4299-a982-bc258e3b8fa3",130,[3527,3528],{"name":2820,"url":2821},{"name":3330,"url":3331},"2024-04-18","Visualize","\u003Cp>Speaker 0: Hello? Oh, were were you waiting to do the thing? Alright. We can\u003C\u002Fp>\u003Cp>Speaker 1: I got you excited? I got you excited.\u003C\u002Fp>\u003Cp>Speaker 0: Hello, and welcome to ready Ready.\u003C\u002Fp>\u003Cp>Speaker 1: Set. Battlesnake.\u003C\u002Fp>\u003Cp>Speaker 0: We are running out of time to nail it, and I I have to be honest. My my, my confidence is starting to wane that we're gonna get it. But hello, everyone. Welcome to episode 3 of 4 for Ready, Set, Battlesnake. My name is Kevin.\u003C\u002Fp>\u003Cp>I work at Directus and\u003C\u002Fp>\u003Cp>Speaker 1: My name is Andrew, and I work at Devcycle.\u003C\u002Fp>\u003Cp>Speaker 0: Absolutely. And today, we're gonna pick up right where we left off in building a Battlesnake with, Directus and in the next episode with Devcycle. We've both been involved in the Battlesnake project for some time in various ilks. And honestly, we're we're just having a great time, if I may if I may talk for us both.\u003C\u002Fp>\u003Cp>Speaker 1: I'm with you. I think this has just been so fun. I haven't dug into Battlesnake in such a long time that this was such an awesome thing to come back into, especially oh, look at that. My light turned off. Especially after, especially after just getting into the the nitty gritty of Directus over the past, like, couple months, getting ready to build some of this stuff out.\u003C\u002Fp>\u003Cp>So, yeah, this has just been it's been so much fun, and I'm excited that we're only halfway through right now. Like, we've got 2 full sessions left, and I think it's\u003C\u002Fp>\u003Cp>Speaker 0: gonna be amazing. So am I. So let's recap where we were at just at a high level. So we have a local directors instance running. Inside of that instance, we have a custom endpoint extension.\u003C\u002Fp>\u003Cp>We have a custom endpoint extension because we need to know with confidence that, a snake can exist having a root endpoint and a slash move endpoint at the very least. And the webhook, the webhook triggers for flows can't guarantee that you get a random URL and we need we need both of those relative past. So we created an endpoint and all the endpoint does is it fires off this flow. There's one for the info of our snake here, which gives the information about our snake. Is the font size okay?\u003C\u002Fp>\u003Cp>I can bump it up if necessary.\u003C\u002Fp>\u003Cp>Speaker 1: It's okay. I mean, bump it up a little bit. I feel like it's not bad for folks that are watching. Yeah. Yeah.\u003C\u002Fp>\u003Cp>It's good.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Yeah. Yeah. And then our move endpoint, which is what we spent most of our time working on in the last episode.\u003C\u002Fp>\u003Cp>So request comes in with a full board state. We set up some global information from that state. We grab is move safe, up, down, left, right, or true. We grab snakes, board width, board height, my head, and you kind of all of these conveniences. Then we run avoid walls, and we see whether whether the wall's gonna be hit.\u003C\u002Fp>\u003Cp>But what you'll notice is at the top is that we pull in is move safe. We do all the work to it and then all we do is return is move safe. So we're moving this object down the chain kind of manipulating it at each step because that's ultimately all we need. We avoid the walls. We avoid all bodies of all snakes once again, starting with is move safe, ending with is move safe.\u003C\u002Fp>\u003Cp>Then we avoid head to head. This is what we spent most of last session doing once we got the flow running. It might not be the most elegant, but what we do is we look one space around every snake that isn't us, and we avoid those spaces too. Once again, we start with this move safe coming in, and we end with this move safe going out. And finally I love we determine\u003C\u002Fp>\u003Cp>Speaker 1: I love that you're saying this is not elegant, Kevin. Like, this is like yours is so much more elegant than mine. As you're gonna find out next week when I jump back in, week 1 week 4 are Andrew, learn learn with Andrew. Specials.\u003C\u002Fp>\u003Cp>Speaker 0: Andrew specials.\u003C\u002Fp>\u003Cp>Speaker 1: Exactly. So this is such clean code. I love it.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, well, thank you very much. I'm sure some of our viewers will disagree and I won't stop them. And then and then finally, we grab that array of is move safe. We basically just pick 1 at random that is still available and we return that as an object move next move, which is either up, down, left or right, and we continue on. And so in this episode, I think we were going to start playing around with not necessarily adding more logic to the snake.\u003C\u002Fp>\u003Cp>I think it's a pretty robust snake right now, but instead, we're gonna start saving game data to this director's project, and then perhaps we'll do something with the insights module. How does that sound?\u003C\u002Fp>\u003Cp>Speaker 1: That sounds fantastic, Kevin. That sounds like a great way to spend an afternoon for me slash Wonderful.\u003C\u002Fp>\u003Cp>Speaker 0: And afternoon, no?\u003C\u002Fp>\u003Cp>Speaker 1: It is now but I realized that people are watching this from everywhere. So maybe it's from morning. Everywhere. Technically, it's it's, we've got afternoon for me, evening for Kevin, and morning for one of our wonderful viewers out. I'm sure somebody\u003C\u002Fp>\u003Cp>Speaker 0: is more important for you. Good good morning from 9:30 at night. Okay. So so so I've already had to think about how how this should probably shape up, which is this return move this return move operation here does some work, right? It picks the move and then it returns it.\u003C\u002Fp>\u003Cp>I think we should decouple these. So we should do one final step which picks a move and then the final step which just returns the move. And then we can put in like an inter we could put in one additional operation in which will store data in a database. I think that's probably the, you know, we have to decouple it and then we can we can take in the entire board state from the trigger, store that and we can store the move that we ended up concluding before we send it on. That's that's what I think we should probably do.\u003C\u002Fp>\u003Cp>There is a question of what we want to store. You know, we also have this additional start and end move point. I think end always returns the final state of the board. So there's something to be to be said about that as well. Yeah.\u003C\u002Fp>\u003Cp>What do we what do we want to store? Maybe we start with we we just store the the move. Like, we start really simple. We just say what is the final move we chose?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think that's probably easy number 1. I was also gonna say we could record win or loss, like, if we wanna go really simple. But technically, I think move is probably simpler because then otherwise, we're gonna have to parse through the JSON data that we get back. So yeah.\u003C\u002Fp>\u003Cp>Let's do let's do move and then maybe, like status of the game and, and then go from there.\u003C\u002Fp>\u003Cp>Speaker 0: Interesting. Let's just let's just go till we can't go anymore. Now just as a note, there is a limitation that we have to work within with directors. Now we actually just did a different directors TV episode on this. It was request review, filtering JSON objects, I think it was called.\u003C\u002Fp>\u003Cp>But, basically, if we store a JSON blob inside of a field in directors, we can't filter inside of that blob. It is just a string. So what we'll need to do is extract specific values and store those as individual values if we want to use them for insights. We just have to be aware of that. Let's let's start by doing what we said we would do, which is adding this extra, splitting this out into 2, and then we can figure out how we're gonna store data.\u003C\u002Fp>\u003Cp>I think that's probably the safest bet. So, let's add a new operation here. Let's call this 1, determine move. I think that feels okay. I think we'll give it the key of move.\u003C\u002Fp>\u003Cp>And I don't know why I've started doing that without having copied out this. So determine move, and this will be a run script. You can copy and paste that in here, except all we're gonna return now is move next. I think all we need to return is the strength next move. That's all this one has to do.\u003C\u002Fp>\u003Cp>And then we're just gonna re kind of configure these. So this one's gonna go here. This one's gonna go here and we hit save. And this keeps happening. Don't know why, but it seems to work if you just kind of what was it?\u003C\u002Fp>\u003Cp>Do it again? Yeah. Because that's what it was.\u003C\u002Fp>\u003Cp>Speaker 1: Disconnect it and then save and it should work. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Like this. We do. Well, we did that. Okay. Cool.\u003C\u002Fp>\u003Cp>Cool. Cool. Cool.\u003C\u002Fp>\u003Cp>Speaker 1: Yes. And now we're\u003C\u002Fp>\u003Cp>Speaker 0: And then, which feels odd. Now today, there was actually a release of Directus the day that we are recording this, but I'm not feeling brave enough just just in case. Just saying it. So this this may well have been fixed. Do we remember what it was?\u003C\u002Fp>\u003Cp>Because it ain't ain't Don't say these. Bloody words.\u003C\u002Fp>\u003Cp>Speaker 1: Is it not working? See. Hang on. Hang on. Okay.\u003C\u002Fp>\u003Cp>What if\u003C\u002Fp>\u003Cp>Speaker 0: we go this way?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Okay. Fine. And now switch it.\u003C\u002Fp>\u003Cp>Speaker 0: I think we had to, like, maybe.\u003C\u002Fp>\u003Cp>Speaker 1: No. What the heck? This this resolved with this last time. So this is\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. But nothing nothing's changed, Andrew. Nothing's changed.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Go back, and, like, remove it from there, save, and then go back, go out, and come back in.\u003C\u002Fp>\u003Cp>Speaker 0: You think?\u003C\u002Fp>\u003Cp>Speaker 1: I don't think that's what we did, but I wonder if\u003C\u002Fp>\u003Cp>Speaker 0: it will No. No. We we definitely didn't do that. We did this and then this and then\u003C\u002Fp>\u003Cp>Speaker 1: This is why you should always watch your VOD recording if you're trying to bug\u003C\u002Fp>\u003Cp>Speaker 0: some of the buttons. We fixed it. Watch your VOD recording. We did. Oh, no.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Remove the okay. But so let's maybe there is actually an error in there. Maybe there is a new error that we've introduced.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. But not nothing new has happened. Right. Value fulfilled resolve in collection, direct to this operation has to be unique.\u003C\u002Fp>\u003Cp>Speaker 1: Are move the same? Is determine move and return move? Is it the same key? Is that what it is?\u003C\u002Fp>\u003Cp>Speaker 0: Oh, possibly, but I really don't think so. This is no. This is called return move.\u003C\u002Fp>\u003Cp>Speaker 1: Nope. Nope. It's a return move. You're right.\u003C\u002Fp>\u003Cp>Speaker 0: So it works that way around. Cool. Cool. Cool. Cool.\u003C\u002Fp>\u003Cp>Cool. So so then we do this. And it does It's this. This is this is where there's a problem.\u003C\u002Fp>\u003Cp>Speaker 1: I feel like our logic is not going to work. Can we just trigger another what if we just trigger another flow instead of doing it past the data? I know it's not as clean but, like, if we're gonna, like, bug solve even just temporarily, why don't we just send it to another flow? It's a new thing we haven't looked at as well because you can trigger\u003C\u002Fp>\u003Cp>Speaker 0: another flow. I don't wanna do that. I don't I I want it to work.\u003C\u002Fp>\u003Cp>Speaker 1: I mean, we all want these things to work.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. We're gonna pause and and we're going to come back when we have worked it out.\u003C\u002Fp>\u003Cp>Speaker 1: What was so wait, what was, what was the you're right. The solution was exactly what you described last time. Are you getting error logs in your console that are different than that?\u003C\u002Fp>\u003Cp>Speaker 0: Oh, no. Really? Shit. It's a great way to start a bloody director's TV thing, isn't it?\u003C\u002Fp>\u003Cp>Speaker 1: Fantastic. Bugs are what make this feel interesting. K. Oh, what?\u003C\u002Fp>\u003Cp>Speaker 0: No. No. No. This this one was the good one.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, that's yes. That's the reversed way, so it won't work that way.\u003C\u002Fp>\u003Cp>Speaker 0: What have I just done? So this goes into return move. No. We want this one to go into determine move. Determine move goes into return move, save error.\u003C\u002Fp>\u003Cp>Speaker 1: An error. Delete that card and create a new one and see if it solves the problem.\u003C\u002Fp>\u003Cp>Speaker 0: Call return move. Card. Oh, doesn't like that.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Just return. There we go. Same thing. Director's operation.\u003C\u002Fp>\u003Cp>What is okay. Can we see that that field go into the data model? Can we not see that operations logic?\u003C\u002Fp>\u003Cp>Speaker 0: I mean, I mean, yeah, I guess. But, like, we're looking at quite, like, low level, like, built in system. Okay. Cool. There is an error.\u003C\u002Fp>\u003Cp>Copy to action. This is what Riker's just messaged. Hang on. Did it work? No.\u003C\u002Fp>\u003Cp>Okay. I'm telling him what happened. Right. I meant I'm talking to the lead maintainer. Node 13 were.\u003C\u002Fp>\u003Cp>I made node 2 then changed the nodules.\u003C\u002Fp>\u003Cp>Speaker 1: And\u003C\u002Fp>\u003Cp>Speaker 0: okay. Fuck. Fuck.\u003C\u002Fp>\u003Cp>Speaker 1: This is the way to this is definitely what what what was meant to happen this week. Is it okay. What if we run a simple script? Like, just detach that new one that you just created. Add any random script after the determined move.\u003C\u002Fp>\u003Cp>Okay. So we need to create\u003C\u002Fp>\u003Cp>Speaker 0: a new one first of all. Right? Yes.\u003C\u002Fp>\u003Cp>Speaker 1: Yes. It's\u003C\u002Fp>\u003Cp>Speaker 0: great. Do will return data. Great. Right? It's like a pass through.\u003C\u002Fp>\u003Cp>It does nothing.\u003C\u002Fp>\u003Cp>Speaker 1: Is it the script? What if we do the same thing with the I Yeah. If we duplicate this script, can you duplicate a flow?\u003C\u002Fp>\u003Cp>Speaker 0: Not a whole flow. Oh, yeah. Flow? Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: I wonder if duplicating the flow might solve this issue.\u003C\u002Fp>\u003Cp>Speaker 0: Duplicating the flow will mean we have to reconfigure the extension. But\u003C\u002Fp>\u003Cp>Speaker 1: Well, I mean, let's let's see let's see if it works.\u003C\u002Fp>\u003Cp>Speaker 0: Well, yeah. Well, I don't know why I'm doing that. So return. Okay. And we'll copy out.\u003C\u002Fp>\u003Cp>Blows. And then we can\u003C\u002Fp>\u003Cp>Speaker 1: Oh, no. I actually can't duplicate 1.\u003C\u002Fp>\u003Cp>Speaker 0: You could, once upon a time. No. It's insights. That's insights.\u003C\u002Fp>\u003Cp>Speaker 1: Good to know. Fucking Here we are. Now we'll figure this out. We got this. I have I have faith in us.\u003C\u002Fp>\u003Cp>Okay. So our regulars our regular script\u003C\u002Fp>\u003Cp>Speaker 0: our no.\u003C\u002Fp>\u003Cp>Speaker 1: Our regular script was working. Okay. Skip just to the end. So, like, remove the the head to head and go straight from avoid body to the very end. So skip over the avoid head to head because this is where we started to get these issues.\u003C\u002Fp>\u003Cp>No. It's the final card that's fucking this up. No matter what it is.\u003C\u002Fp>\u003Cp>Speaker 0: It's reassign.\u003C\u002Fp>\u003Cp>Speaker 1: It worked. Did it work?\u003C\u002Fp>\u003Cp>Speaker 0: It worked. Don't question it. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: It worked. We did it. What what was our solution, Kevin? We just tried many times and then waited to see what happened. Just wait until I cause many problems to my team.\u003C\u002Fp>\u003Cp>Okay.\u003C\u002Fp>\u003Cp>Speaker 0: On our screen. Okay. Alright. Gonna resume recording. Thanks for the click.\u003C\u002Fp>\u003Cp>Speaker 1: We got there. We did it. Go team. Go, Kevin.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Hubert, you're welcome. Alright. Ready to come back?\u003C\u002Fp>\u003Cp>Speaker 1: We're ready. Let's do it.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. And welcome back. We fixed it. We don't know how we fixed it. It just started working again.\u003C\u002Fp>\u003Cp>We will undoubtedly come across this again. Having spoken to like our lead maintainer, he thinks it's a bug, which I will open a GitHub issue on after this recording. Okay. Wicked. So now determining the move and then returning the move.\u003C\u002Fp>\u003Cp>Okay, cool. So we determine the move, which just returns the next move. And then all we want to do here, the literally the only thing we want to do here is return last. Right? Data no.\u003C\u002Fp>\u003Cp>Last. Last? Yeah. Just last.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think just last.\u003C\u002Fp>\u003Cp>Speaker 0: Date data dot data dot last.\u003C\u002Fp>\u003Cp>Speaker 1: Is it? Alright. And the reason for this, if if nobody realizes this already, is because, like, you have to feedback this very specific JSON structure into the response that that Battlesnake precedes. Which kinda sucks. It just seems like, it should.\u003C\u002Fp>\u003Cp>It should just read this the engine should just get it. It should understand at a core level.\u003C\u002Fp>\u003Cp>Speaker 0: If you're if you're being told to implement an API specifically, it's fine to have to do it specifically, but we'll test it works. And in theory, we should be able to in theory, we should be able to add another another flow operation here to do logging. Any logging we want at the end of the whole run. So let's manage our battlesnakes. Let's create a game where I'd already set a battlesnake and a hungry bot an 11 by 11 board, and we'll start the game.\u003C\u002Fp>\u003Cp>And that should be running game in progress. I can kind of see my logs over here that the game is still happening. I'll wait for the game to conclude, which it has not yet. You know what? I've run out of patience.\u003C\u002Fp>\u003Cp>Oh, no. It happened. So let's hit.\u003C\u002Fp>\u003Cp>Speaker 1: There we go.\u003C\u002Fp>\u003Cp>Speaker 0: Excellent. It did end up doing itself in, so there is something wrong, but we're not we're not looking into that bit right now.\u003C\u002Fp>\u003Cp>Speaker 1: It worked. That's the important thing.\u003C\u002Fp>\u003Cp>Speaker 0: It it worked for some period. Anyway, that's all so it is it is talking successfully, to directors, which is useful. Now what we now wanna do is figure out what we need to log. And before we do this, let's create a collection for it. So we'll go to our data model, we'll create a collection and what are we going to log?\u003C\u002Fp>\u003Cp>I don't think we should log every turn. I mean, we could log every turn.\u003C\u002Fp>\u003Cp>Speaker 1: Well, I I mean, we could. I think I don't think it's necessary either. I think let's let's let's right now just log the final move just as like a getting started place. We're just gonna pass that data right through. And then let's do at like, ultimately, we'll end up adding something around, win or loss, because I think there's something cool visualizing that.\u003C\u002Fp>\u003Cp>And then I think there's honestly, I'd be love to explore us actually pulling the game GIF from this, which I think is more logic, but I think it's very interesting should be pretty straightforward.\u003C\u002Fp>\u003Cp>Speaker 0: So if we want to log this, if we wanna log the the game, we need to be doing this actually in the end endpoint. Right? We don't want to be doing this in the move endpoint. We want to do it in the endgame. So this actually means we need to a little bit more fiddling, a little bit more fiddling with directors.\u003C\u002Fp>\u003Cp>And to the dismay of our lovely editor, I'm actually gonna unshare and reshare, but I'm gonna do my whole screen this time because No camera job. It's too late. I'm doing it. It's done. It's gone.\u003C\u002Fp>\u003Cp>It's done.\u003C\u002Fp>\u003Cp>Speaker 1: Farewell. It's it's gone. Farewell screen.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Apologies for for for that. But what we have here I'm gonna just reconfigure also.\u003C\u002Fp>\u003Cp>Speaker 1: See every day, Kevin, by the way? Is this your screen, this large massive thing?\u003C\u002Fp>\u003Cp>Speaker 0: It's a 49 inch ultrawide monitor, which will feature in a future episode of What's in My Doc because the final episode of the season is yours truly. We talk about it more then. So if I just make this bigger, we want to we have this move endpoint here. We can delete all of this logic that was left actually. But we have let me just shrink this down a bit.\u003C\u002Fp>\u003Cp>Just a reminder, when we hit the root endpoint, we're gonna hit this flow. When we hit the move endpoint, we're gonna hit this flow, the start and there's end. So what we're gonna do is basically add this logic to to end. So what we're gonna do is, kind of similar to the info end point. You know?\u003C\u002Fp>\u003Cp>The info end point. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. Yep. Same thing.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Although that is a get endpoint. We need a post endpoint. So it's more similar to move, I guess. But what we'll do is we'll create a new one.\u003C\u002Fp>\u003Cp>We'll call this snake, end. This can be based on a web hook trigger. It will be a post request. It is a post request. Right?\u003C\u002Fp>\u003Cp>I mean, we'll\u003C\u002Fp>\u003Cp>Speaker 1: find out together. I'm pretty sure it's a post request. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: We we will, but I'm also gonna I'm also gonna double check.\u003C\u002Fp>\u003Cp>Speaker 1: Look at the documentation. Probably smart idea.\u003C\u002Fp>\u003Cp>Speaker 0: Look at the documentation.\u003C\u002Fp>\u003Cp>Speaker 1: Not there though.\u003C\u002Fp>\u003Cp>Speaker 0: Webhooks. Here we are. Game over. It's a post request as an error hit, by the way. It's it's not formatted as a code block, but it's a post request to slash end.\u003C\u002Fp>\u003Cp>It will be the game object, the final turn number, which is useful, the board describing the initial state of the game board. That doesn't feel correct. Oh, on that final turn, I guess. And you describing your battle snake. And I wonder if there's a death reason, but we can find out.\u003C\u002Fp>\u003Cp>So response body date of last operation. Great. And first thing we're gonna do is just copy this URL. I'll hit save. We'll copy this URL and we will go ahead here and const flow response.\u003C\u002Fp>\u003Cp>I'm not sure we need to, respond to this, but we will Await fetch full URL. We're basically just gonna copy and paste the all of these options because they remain the same. And then we're just gonna send okay. We don't need to do like we actually don't care about the flow response. We're just gonna send all the data by fetch and then send okay.\u003C\u002Fp>\u003Cp>We just need the data. We don't and eventually to respond. So we'll save that.\u003C\u002Fp>\u003Cp>Speaker 1: Hear that flow response? We don't need you. We\u003C\u002Fp>\u003Cp>Speaker 0: don't need you. Yeah. So inside of our extensions, director snake, we'll run npm run build. Oh, in today's release of Directus, we squash the bug that stopped the autoreloading working. So That's awesome.\u003C\u002Fp>\u003Cp>Nice.\u003C\u002Fp>\u003Cp>Speaker 1: Was that because of us?\u003C\u002Fp>\u003Cp>Speaker 0: No. Not even remotely. Well, I'd like to think it is. 10.9. 10.9 came out yesterday, so it resolved that.\u003C\u002Fp>\u003Cp>So now we have our new extension. With this new endpoint, we don't need to do anything more there. Back over to our code editor here. I suppose we can just we can actually just leave this be. Yeah.\u003C\u002Fp>\u003Cp>And we can Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Let's just log, see if it shows\u003C\u002Fp>\u003Cp>Speaker 0: before. It. Yeah. Exactly. Create rematch.\u003C\u002Fp>\u003Cp>So that whole game is is running. We see that 200 all the way. Really low really low, response. That's great. Running, running, running.\u003C\u002Fp>\u003Cp>And then we now no longer, we remove the console log for end. Oh, this is on start. Have we done it in the wrong place? Everyone, we've done it in the wrong place. I know.\u003C\u002Fp>\u003Cp>Amateur hour. But it is logging it, so we know what we know what the shape would be. Yep. So we can do that instead. We'll make this async.\u003C\u002Fp>\u003Cp>We'll remove async here. We will go and rebuild the extension, and then we'll restart directives. And while that's kinda happening, we can we can take a look at this object here. So we have game, which is an ID, which we'll probably wanna store. The rule set perhaps, probably not for this one, The the final turn, 95.\u003C\u002Fp>\u003Cp>Don't know what we wanna know about the board probably because 95, it's the turn where you're dead. So that's that array will not include us or it will include us and we'll be the last one standing, I guess. You has health. So I wonder oh, maybe we won this one. Maybe we won this one.\u003C\u002Fp>\u003Cp>Let's, let's watch it. Have a sneaking suspicion we did win. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: So wait. How did we how do does it show that for us in that result? Like, it doesn't it doesn't say, like, in our in\u003C\u002Fp>\u003Cp>Speaker 0: the law No. No. But I I I wondered why is it still giving me a health of 90? Why did it give me a health of whatever it was, 83? Smart.\u003C\u002Fp>\u003Cp>I have a sneaking suspicion if you die, the payload looks different, but there's only one way to test it, which is I've now restarted Directus. If I refresh this, there should be some logs. So this was the log of the one we just accidentally ran when we shouldn't have. So this is the start. So we'll just forget that one.\u003C\u002Fp>\u003Cp>Let's, let's do a rematch. And now we should only get the response when when it dies. So we can see this. We see it's posting slash snake slash move. And the last thing it should do is post slash snake slash end and it should hit a different trigger.\u003C\u002Fp>\u003Cp>So we'll just watch that happening and this is happening real quick. Wonderful. And that is what happened. It had a different flow at the end slash snake slash end. So if we go back to our flow now and refresh, we should be able to look at the logs.\u003C\u002Fp>\u003Cp>Payload. Okay. So this oh, did we win or or lose? That's probably worth knowing ahead of time. You won\u003C\u002Fp>\u003Cp>Speaker 1: again. Come on, ready, set, battle snake. What are you doing winning all the time? You're too good. You're too good.\u003C\u002Fp>\u003Cp>Speaker 0: Too good. So 73 turn 73. That is us, ready, set, battlesnake. We know our ID. I wonder if there's something in here where we log what our own Battlesnake is, but it may not be necessary.\u003C\u002Fp>\u003Cp>Let let's keep looking. It has health of 92. It has a length of 6, which could be interesting. We don't need to know what all the food is, where all the hazards are. You is just a convenience again.\u003C\u002Fp>\u003Cp>Alright. Let's, let's literally just keep creating rematches till we die. We'll let this run-in real time which means we we're gonna have to wait a hot minute as every move happens. Are we too Lindsay.\u003C\u002Fp>\u003Cp>Speaker 1: Are we too good at programming Battlesnakes that he will live forever? Is that are we gonna need to are we gonna need to, pause?\u003C\u002Fp>\u003Cp>Speaker 0: Artificially. I'm gonna have to artificially make him just, like, die.\u003C\u002Fp>\u003Cp>Speaker 1: Come on. I think we oh my goodness. Come on. We gotta do it again. Gotta play again.\u003C\u002Fp>\u003Cp>Okay. Okay. Come on. 3rd time's the charm. We did, we did too much, Kevin.\u003C\u002Fp>\u003Cp>We went too hard.\u003C\u002Fp>\u003Cp>Speaker 0: That, game in progress thing is not behaving, so we'll restart a game this way. I mean, if I just add ourselves, eventually, we'll die.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, there you go. Yeah. And it's interesting too. So, I think part of the game in progress thing might be I know I know Battle Sneak scaled down the server load, so it may be something related to that. Maybe.\u003C\u002Fp>\u003Cp>But, yes, we should live for we're gonna live forever. That's really what you've just done here. You're about to, you're about to kill your local version of Directus just because this will run forever. Hey. Good time.\u003C\u002Fp>\u003Cp>Alright. I immediately got it right and wrong. Thank you. Thank you. I reset Battlesnake.\u003C\u002Fp>\u003Cp>Alright.\u003C\u002Fp>\u003Cp>Speaker 0: I mean, eventually, we had to lose. Right? So let's look at the final log when we lose. And I have a hunch. It is that our battlesnake does have a health of 97.\u003C\u002Fp>\u003Cp>So there is a health in there. So that isn't how we determine whether we live or die, unfortunately. We have a board. We have you. Although, where are the oh, the snakes array is empty.\u003C\u002Fp>\u003Cp>So we are not so we are there. Sorry. But we are not present in the snakes array.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, okay. That makes sense. So if the snake array is empty, we didn't win. If we are not empty\u003C\u002Fp>\u003Cp>Speaker 0: if we are not in it. Yes. If we are not in it. So the easy the easy thing to do here is we have a we know our snake ID. Here it is.\u003C\u002Fp>\u003Cp>We could store it in a collection of our snakes to like factor out or we could just hard code it in.\u003C\u002Fp>\u003Cp>Speaker 1: Well, let's let's let's have some fun. Let's let's do the more challenging\u003C\u002Fp>\u003Cp>Speaker 0: Sure. More challenging. Snakes. And then, actually, if we look at the if we look at the API, I don't think there is a a meta Battlesnake API is there to get information about games and stuff. No, that's fine.\u003C\u002Fp>\u003Cp>What else should what there could be. Yeah. What if there is there's a start endpoint. I wonder if no, it's fine. We'll we'll we'll hard code this snakes.\u003C\u002Fp>\u003Cp>All we care about snakes is that they have a manually entered string as the ID and we can add all the others later. So if we go to our snakes, we'll put in the ID. That's ours. And you know what? We'll just add a name to it just so we can tell what it is.\u003C\u002Fp>\u003Cp>Right? So, this battle snake is ready, set, battlescape, I actually think is its actual name because we wanted to distinguish the one you made to the one I made. And my name is Elsford k. So it's registered Battlescape. Okay.\u003C\u002Fp>\u003Cp>Cool. So we've got that. So what are we gonna do inside of this end flow? First thing we need to do, I suppose, is we get this whole payload. The only thing we wanna enrich it with right now is are we alive?\u003C\u002Fp>\u003Cp>So Yeah. Is alive. Did live. Did I live? And that's going to run scripts.\u003C\u002Fp>\u003Cp>No, it isn't. First of all, we're gonna get all of our snakes. That's what we should do. Read items from the database. So, get snakes.\u003C\u002Fp>\u003Cp>Permissions from the from trigger. I'm just gonna give it full access so we don't need to contend with permissions. Snakes. If I don't add any IDs, do I get them all?\u003C\u002Fp>\u003Cp>Speaker 1: We'll find out. We gotta find out.\u003C\u002Fp>\u003Cp>Speaker 0: This is\u003C\u002Fp>\u003Cp>Speaker 1: trial and error, Kevin. This is trial and\u003C\u002Fp>\u003Cp>Speaker 0: error. Real, real trial and error thing going on here. Alright. Let's create a rematch with ourselves. I feel like I should add another snake back in just to end the games quicker.\u003C\u002Fp>\u003Cp>Speaker 1: So do it. Yeah. Do it. Add another snake in. Just go back and\u003C\u002Fp>\u003Cp>Speaker 0: make sure I'll let I'll let this game conclude. We'll do it until the next time we do\u003C\u002Fp>\u003Cp>Speaker 1: it.\u003C\u002Fp>\u003Cp>Speaker 0: Still running.\u003C\u002Fp>\u003Cp>Speaker 1: By the way, it looks\u003C\u002Fp>\u003Cp>Speaker 0: like games. Oh, thank you. I actually was really trying to find, like, the end of Loki season 2, all of the timelines. I'm not sure if you've seen it. And so it was kind of Yes, I've probably seen it.\u003C\u002Fp>\u003Cp>I've seen it's going for. Yeah. It was kind of that vibe I was going for. Okay. So obviously we eventually die.\u003C\u002Fp>\u003Cp>That's just the nature of playing by yourself. Let's just see how they perform now. Okay. Again, no idea why. Really shouldn't be doing that.\u003C\u002Fp>\u003Cp>But\u003C\u002Fp>\u003Cp>Speaker 1: Don't question it. We wanted that to happen. We willed it into being. We willed it to happen.\u003C\u002Fp>\u003Cp>Speaker 0: We want you to be chitter. Alright. There there's all of our snakes. So that's great. So that that returns an array of objects which contain IDs which are our snakes.\u003C\u002Fp>\u003Cp>So the next thing we want to do then is did live, like, you know, did live. And we will run a script here. And all we wanna determine here is, what did we call the last one? Snakes. So data dot snakes is the array of snakes and we want to know and we want to know did the trigger dot I need to I don't want I don't wanna close it.\u003C\u002Fp>\u003Cp>Don't wanna close it to to take a look. Let's look at the docs. Webhooks. So it will be trigger dot, I think, payload.\u003C\u002Fp>\u003Cp>Speaker 1: I think so. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. We can always fix that later. The payload.board.snakes. So we wanna see, did that contain that? So, JavaScript, does array contain other does array contain specific item?\u003C\u002Fp>\u003Cp>I think it's like includes yeah. Does array contain item from other? Right? I'm pretty sure we just did this in our last episode.\u003C\u002Fp>\u003Cp>Speaker 1: I think so. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Some. And I think I used find instead. So, live, which is like the if it lives, it will be found. So we'll say is inside of data dot snakes, is does that contain yes. Does that contain does data.snakes contain?\u003C\u002Fp>\u003Cp>And this is where it gets a bit honky, and I need to just just have a moment to think. So we wanna know out of all of the items in the snake array of which of which we know the I the ID is gonna be snake dot it. We know that already. Does is snake ID equal or does snake ID I got it. Does snake ID feature inside of this array?\u003C\u002Fp>\u003Cp>So what we actually wanna do is return whether this array, the one with all the snakes on the board contains, which I think we've determined is includes includes.\u003C\u002Fp>\u003Cp>Speaker 1: Yep.\u003C\u002Fp>\u003Cp>Speaker 0: Is it includes? I think we need to simp I think we need to do one more step beforehand which is take this big array here and just simplify it down to IDs. So, const IDs, IDs on board. So we'll take all of that and we'll map it and all we wanna do is turn that into an array of IDs. So it's just IDs.\u003C\u002Fp>\u003Cp>It's nothing else but IDs. Forget all the other information. That's all of the boards. We don't That's all of the IDs of the snake on the board. Exactly.\u003C\u002Fp>\u003Cp>And then we will return whether or not IDs on board that's it. IDs on board includes any of our snakes IDs and we will return to live. So true force, true false, did it. Really?\u003C\u002Fp>\u003Cp>Speaker 1: It's gonna work. First time's the charm.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, no. I've done it again. I've hit create rematch, But eventually the answer will be false, right, this time with confidence. It should be. Or it'll just live forever and this will be this will be our time.\u003C\u002Fp>\u003Cp>Okay. Hit its hit its own tail. Mounted itself into a corner and did itself in. So let's, look at the last log. Cannot read properties of undefined.\u003C\u002Fp>\u003Cp>Okay. Cool. And it was body, by the way. It was trigger dot body. So that will probably be why.\u003C\u002Fp>\u003Cp>Trigger.body.board.snakes.map. So let's, let's just check that. Body dot board dot snakes. Cool. So we will create rematch.\u003C\u002Fp>\u003Cp>Speaker 1: And now second time.\u003C\u002Fp>\u003Cp>Speaker 0: And now.\u003C\u002Fp>\u003Cp>Speaker 1: Go, red snappettlesnake.\u003C\u002Fp>\u003Cp>Speaker 0: So stop eating food. Stop eating food. As it gets towards the food, I'm like, no.\u003C\u002Fp>\u003Cp>Speaker 1: Come on. What are you doing? We don't want you to live anymore. What are you why?\u003C\u002Fp>\u003Cp>Speaker 0: Why are you doing this? This battle snake is not bad apart from those times it randomly goes off the edge of the board, and we don't know why. I think it's just got\u003C\u002Fp>\u003Cp>Speaker 1: a it's got a it's got a spirit of its own.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, what's happened there? Oh, there we go. Oh, okay.\u003C\u002Fp>\u003Cp>Speaker 1: There was some real They\u003C\u002Fp>\u003Cp>Speaker 0: were clearly in a queue or something.\u003C\u002Fp>\u003Cp>Speaker 1: Goodness. Wow. We're doing I Oh, there we go. Why did you do\u003C\u002Fp>\u003Cp>Speaker 0: that? I don't know. Oh, yeah. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Right. It knocked itself into a into that kind of spot where where it can't. There is no viable move. Right. So we'll look at this.\u003C\u002Fp>\u003Cp>Cannot read properties of undefined. Okay. Okay. What is undefined? What's undefined is what we're running find on.\u003C\u002Fp>\u003Cp>So let's take a look at that. We're running find on data dot snakes. Okay? Except oh, because we call it get snakes. Fair.\u003C\u002Fp>\u003Cp>We'll call that snakes. Great rematch. This is the only thing when you'll see ready. Set up\u003C\u002Fp>\u003Cp>Speaker 1: the snake. Sorry. This is the only thing when you're debugging. Go on. And you were saying something valuable as part of this conversation.\u003C\u002Fp>\u003Cp>I was trying to add\u003C\u002Fp>\u003Cp>Speaker 0: some color. Wait. You there's there is just a bit of waiting here. Right? I suppose because the rules engine is open source.\u003C\u002Fp>\u003Cp>I imagine people I think people have built, like, line emulators that can obviously execute this way, way, way quicker for the sake of debugging. And I see the value in those tools.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. You can actually do it in the CLI. In the CLI, you can actually play full games and see them.\u003C\u002Fp>\u003Cp>Speaker 0: Because we just had to weigh a 153 turns. Alright. Trigger is not defined. Still. Okay.\u003C\u002Fp>\u003Cp>Trigger is and you know what? Because it's data dot trigger. So many amateur amateur hour little mistakes today. Never.\u003C\u002Fp>\u003Cp>Speaker 1: Never, Kevin. Amateur is, is a term that we need to take back as a positive\u003C\u002Fp>\u003Cp>Speaker 0: As amateurs.\u003C\u002Fp>\u003Cp>Speaker 1: As amateur. As amateurs, yeah. Alright. Come on, ready set battle snake. You can die.\u003C\u002Fp>\u003Cp>Don't survive.\u003C\u002Fp>\u003Cp>Speaker 0: Please don't.\u003C\u002Fp>\u003Cp>Speaker 1: I believe in I I don't believe in you. I feel like he thrives.\u003C\u002Fp>\u003Cp>Speaker 0: So the weather so the weather today.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Oh. Oh. Is that is this it?\u003C\u002Fp>\u003Cp>Speaker 0: No. No. It it will lit. It's just obviously saving up moves. Yeah.\u003C\u002Fp>\u003Cp>There we go. Woah. It's like a taunt. That was like a taunt to us.\u003C\u002Fp>\u003Cp>Speaker 1: Come on.\u003C\u002Fp>\u003Cp>Speaker 0: We're doing exactly as we the thing is we made we made this snake. We made this snake.\u003C\u002Fp>\u003Cp>Speaker 1: I feel like we is a very, oh, there we go. Alright. He made himself, really. Alright. Let's see if this worked this time.\u003C\u002Fp>\u003Cp>What do our logs say? Hey. We got data.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. But this isn't what we want. What do you mean? What we want. I don't know, but I just want, I just want, I just want a true false value so something's not right.\u003C\u002Fp>\u003Cp>Speaker 1: But wait\u003C\u002Fp>\u003Cp>Speaker 0: So, yeah, go on.\u003C\u002Fp>\u003Cp>Speaker 1: Well, I'm just like, what are we returning? Sorry. It's the text\u003C\u002Fp>\u003Cp>Speaker 0: is really small.\u003C\u002Fp>\u003Cp>Speaker 1: Not because of your\u003C\u002Fp>\u003Cp>Speaker 0: screen. It's because We are returning, the whole, like, body of the script, which feels a little bit weird. And I have a hunch. I have a hunch. It's oh, no.\u003C\u002Fp>\u003Cp>Did live. This is live. So what's going on? What's going on there?\u003C\u002Fp>\u003Cp>Speaker 1: Super weird. That shouldn't be returning that.\u003C\u002Fp>\u003Cp>Speaker 0: No. It should not. Let's, look at our previous flow, the move one, and just maybe not, like just take a little bit of yeah. I mean, if we take all of this, and I'm just doing this as a comparison point just so we can kind of dump it in here and take a look kind of side by side, if you will. Yeah.\u003C\u002Fp>\u003Cp>Data dot trigger, Return. We do just return a value. Maybe, let's take a look at, array find method. Let's just understand this a little bit better. This returns the 1st element in the provided array that satisfies the provided testing condition.\u003C\u002Fp>\u003Cp>If you need an index, you can use find index. If you need to find the value, if you need to find if a value exists, you can use includes, which actually I think is what we want because that just returns a true or false. So maybe we just swap this out for an includes, includes, And we wanna know yeah. It's like a nested includes. Fine.\u003C\u002Fp>\u003Cp>And then we return I'm just I'm a little bit confused why it was returning\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. The whole code.\u003C\u002Fp>\u003Cp>Speaker 0: Well, we no. You know what? We also don't get here. We don't get a returned payload, which makes me think there was some kind of error happening here that wasn't being captured correctly. Is there anything in here?\u003C\u002Fp>\u003Cp>I wouldn't imagine there is, and it's now really hard to tell. Let's, let's create a game. Let's let's make this game a lot, a lot quicker to end.\u003C\u002Fp>\u003Cp>Speaker 1: Hey. There we go.\u003C\u002Fp>\u003Cp>Speaker 0: Well, do you see how quick that was?\u003C\u002Fp>\u003Cp>Speaker 1: That was fast. We are smarter than hungry.\u003C\u002Fp>\u003Cp>Speaker 0: Niamis.\u003C\u002Fp>\u003Cp>Speaker 1: Come on. What are you doing, Raze at Battlesnake? We want you to not die. We just want you to die.\u003C\u002Fp>\u003Cp>Speaker 0: We might we might have to nip out some of the logic or something.\u003C\u002Fp>\u003Cp>Speaker 1: I've never had a\u003C\u002Fp>\u003Cp>Speaker 0: Well, well, that will that that will be the end. That will be the last move when it finally catches up with that with with Toast. Yeah. Snake end. The game seems to be struggling with the concept of ending, but it has ended.\u003C\u002Fp>\u003Cp>Alright. Yeah. Look. We get the options here, but we don't get the payload. Because if you look at the other flows, for example, wow, 1632.\u003C\u002Fp>\u003Cp>Wow. You see that all of these run scripts. I know they have the payloads. They have the options and they have a payload. Options payload, determine move interestingly interestingly, does does not have a returned payload.\u003C\u002Fp>\u003Cp>Is there something about them returning objects and strings versus just returning like a primitive. Maybe that's what it is. So if I so if I because this could be fine, you know, return. That's returning an object. Right?\u003C\u002Fp>\u003Cp>Mhmm. Let's create a rematch. Okay. Our snake just went up and up again. Hey.\u003C\u002Fp>\u003Cp>We don't know why happened.\u003C\u002Fp>\u003Cp>Speaker 1: Bad things happened. We changed we changed the code. Shouldn't have done that.\u003C\u002Fp>\u003Cp>Speaker 0: Changed the code. Shouldn't have done oh, there we are. Did live force. Okay. Awesome.\u003C\u002Fp>\u003Cp>Cool. So so there was actually no problem. Just an interesting note there that for it to show up in the payload, it needs to be an object or an array, I presume. So just a good just a good note there. I would love to play a game where we don't lose.\u003C\u002Fp>\u003Cp>So we can just so we can just check both sides of that equation and that they make sense. Let's create small boards.\u003C\u002Fp>\u003Cp>Speaker 1: If we play with ourselves It wouldn't then technically, we'll always win and always win.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, well, yeah. But then I think it's just gonna get a little bit weird. There we are. Hey. We no.\u003C\u002Fp>\u003Cp>Wait. That's not what we want.\u003C\u002Fp>\u003Cp>Speaker 1: That's what we want. But We've wanted to lose so much that now to win seems like a wrong thing.\u003C\u002Fp>\u003Cp>Speaker 0: So did live should be forced. Yeah. Live should be forced. Let's create a rematch. Maybe we need to put in a a a a less clever snake.\u003C\u002Fp>\u003Cp>Goodness. Okay. Try Do you want the right bot? Yeah. Yeah?\u003C\u002Fp>\u003Cp>We we haven't we haven't gotta live long to make that work. Okay. Great. So now if we refresh, we look at the logs. Did live hopefully should be\u003C\u002Fp>\u003Cp>Speaker 1: False. Absolutely. That's totally what we were wanting to have happen.\u003C\u002Fp>\u003Cp>Speaker 0: Great. Is this really what we're gonna spend this episode doing? Okay. Fine.\u003C\u002Fp>\u003Cp>Speaker 1: That's it. Just debugging JavaScript. That's really what we're here for.\u003C\u002Fp>\u003Cp>Speaker 0: Right. All we want to do is now maybe maybe we just roll back for a sec. We're doing all of this grab snakes thing. I don't think this is adding I've deleted it. I've just done it.\u003C\u002Fp>\u003Cp>I've just done it. I've been busy for decision. Exec executive decision. All we care about is this ID. Right?\u003C\u002Fp>\u003Cp>So let's just we'll we'll delete this. We're gonna\u003C\u002Fp>\u003Cp>Speaker 1: hard code it in now. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Like, I don't think it's worth, like, going at all costs. Right? So all we want to know is does data dot snake say const?\u003C\u002Fp>\u003Cp>In fact, I think we can say, does snake include data dot trigger dot body dot board dot snakes dot map. Sure. So that's just a set of IDs. And all we wanna know is, does IDs onboard include this? And I'm I'm assuming can I console log inside of these?\u003C\u002Fp>\u003Cp>Best bit of, debugging we got. Great rematch. Well, I think our snake's broken. I think we spoke too soon about the capability of our snake. In it doesn't like includes.\u003C\u002Fp>\u003Cp>Payload.body. Right. Hang on. It doesn't like includes, which is this one here. And we don't get a log, annoyingly.\u003C\u002Fp>\u003Cp>So all we care about, Let's, you know what? I'm actually gonna be bold here and just remove this, and we can start we can start again. So so all we wanna know is did it live? And to know if it lives, we're gonna go through oh, no. I do need to I need this too.\u003C\u002Fp>\u003Cp>Speaker 1: I like it. I like the I like that you were confident in the removal,\u003C\u002Fp>\u003Cp>Speaker 0: but also\u003C\u002Fp>\u003Cp>Speaker 1: truly confident. That I you\u003C\u002Fp>\u003Cp>Speaker 0: know, I really wasn't. So all you wanna know is, does data dot trigger dot body dot board dot snakes, does that include includes includes let's take a look. We need to give it a specific value here. So I think what we'll first do is we'll say snake IDs on board. We'll take this and we'll map it once again.\u003C\u002Fp>\u003Cp>So we just extract the IDs. Then all we wanna do is check whether snake IDs on board includes this value here. It's all we wanna know. Please work. Create rematch.\u003C\u002Fp>\u003Cp>Speaker 1: We've got this.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Our snake just keeps going up by the way. We fundamentally broken something.\u003C\u002Fp>\u003Cp>Speaker 1: But I mean, these things happen. These things happen. We'll figure it out.\u003C\u002Fp>\u003Cp>Speaker 0: We've got Live force.\u003C\u002Fp>\u003Cp>Speaker 1: The rest of today.\u003C\u002Fp>\u003Cp>Speaker 0: Live force. Okay. And we did we did we did live force. Yes. That is that is true.\u003C\u002Fp>\u003Cp>And all we need to do is outlive the snake, which we did, and hopefully live is true in this context. We may need to go back as as we commented in Okay. But we lived, but we lived. Let's let's take a look at the payload. Let's take a look at the payload.\u003C\u002Fp>\u003Cp>So body dot game, no. Body dot board dot snakes has ready set battlesnake present, and that's it. The snakes have an ID. That's our ID. That is I'm just gonna double check.\u003C\u002Fp>\u003Cp>Yep. The ID, nope. Not the ID we've been pasting in. What does it get a different does it well, it's different. So I'm wondering, look at this ID, w d at the end.\u003C\u002Fp>\u003Cp>If I look one game back, it gets a different ID every time, Andrew.\u003C\u002Fp>\u003Cp>Speaker 1: So we gotta pull name then, I guess. I guess the ID is not\u003C\u002Fp>\u003Cp>Speaker 0: I'm literally so mad. Yes. Fine.\u003C\u002Fp>\u003Cp>Speaker 1: As well, you should be. Damn you, battle stake and your your ID that is not truly the snake ID. Who do\u003C\u002Fp>\u003Cp>Speaker 0: you It does feel that way. I can't believe we sunk so much time into that. Okay. Cool. No.\u003C\u002Fp>\u003Cp>Right. Create custom game. Create rematch. Okay. We lost because apparently we just go up now.\u003C\u002Fp>\u003Cp>Yeah. Liveforce. Cool. And create rematch. Yeah.\u003C\u002Fp>\u003Cp>I mean, we lost again. Sure. Because that's what's happening now. What is going on with that, by the way? What is going on with that?\u003C\u002Fp>\u003Cp>Is it is it work. Are we struggling under the number of logs that are being generated?\u003C\u002Fp>\u003Cp>Speaker 1: No.\u003C\u002Fp>\u003Cp>Speaker 0: I mean, possible. Look, avoid head to head force. So there's something in the avoid head to head. Oh, god. Because there was no head to head issue here.\u003C\u002Fp>\u003Cp>No. There wasn't. Let's create rematch just until we get a a seed. I think, yeah, we both die at the same time there. Let's create rematch.\u003C\u002Fp>\u003Cp>Let's just create a seed just to test out this logic. Yeah. That's good. Nope. Not good.\u003C\u002Fp>\u003Cp>Okay. Create game. Okay. We we broke it. We broke it.\u003C\u002Fp>\u003Cp>Okay. We are still not gonna do well this oh, what is going on? There we go. Is it\u003C\u002Fp>\u003Cp>Speaker 1: there someone\u003C\u002Fp>\u003Cp>Speaker 0: is there something is there something about we did? Is there something about the board size being different that's throwing it off? In which case, I'm quite happy to say how Battlesnake only operates in 11.\u003C\u002Fp>\u003Cp>Speaker 1: That's the right answer. Don't make it work on all the boards. Only standard class.\u003C\u002Fp>\u003Cp>Speaker 0: True. Okay. Hey. There we go. Finally know whether it lives.\u003C\u002Fp>\u003Cp>Now it's all about logging information. So let's look at this payload and see what we have. So we have a game ID, which I think feels like a no brainer. Right? Yeah.\u003C\u002Fp>\u003Cp>And you said we can do something with the ID to get the GIF?\u003C\u002Fp>\u003Cp>Speaker 1: I believe so. I'm gonna go check and see something right now. I'm gonna go over while you're doing this, I'm gonna go over to my other screen. But, yes, we'll pull back so that we need that.\u003C\u002Fp>\u003Cp>Speaker 0: We know the turn of the final move, which is 2. Yep. Right. So that's the term where we were victorious. We know that we lived.\u003C\u002Fp>\u003Cp>We know our length, I suppose.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think so.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And maybe we do maybe we start with just games. Games get an ID, which can be a manually entered string because we'll we'll pop that in from the API. And I think all we need here is win or lose, right? Win.\u003C\u002Fp>\u003Cp>That's a Boolean. We wanna know what about it. Like, a turn, I suppose. That can be an integer.\u003C\u002Fp>\u003Cp>Speaker 1: I mean, game mode? Turns. Yep. Turns.\u003C\u002Fp>\u003Cp>Speaker 0: Turns feels better. Yep. Turns. Into game mode, but we're not playing around with other game modes. So I just think That's true.\u003C\u002Fp>\u003Cp>Yeah. It's fair. Yeah. But but the idea is, in theory, you could you could store all of this. Wind turns.\u003C\u002Fp>\u003Cp>What else have we got at our disposal? Let's take another look at our end state here. We have live die. We had turns. We can start with how many snakes there were on the board from the trigger.\u003C\u002Fp>\u003Cp>So snakes dot so trigger trigger body board snakes length. Just quickly gonna go, store. We're gonna prepare data. So trigger dot body dot board, did we say it was? Dot snakes.length.\u003C\u002Fp>\u003Cp>So I'm just gonna pop that in there for later. Yeah. So snakes, that can be another integer. What what else what else do we wanna know?\u003C\u002Fp>\u003Cp>Speaker 1: I mean, let's let's let's stick it with that. Good because we\u003C\u002Fp>\u003Cp>Speaker 0: you could do, like, a win ratio. You could do, like, average turns per game. Like there's lots you can do here. Do you continue getting data if you are not the last to loot? Like if you die and it goes on a few more steps, do you only get the end state on your final turn or do you like, let's take a look.\u003C\u002Fp>\u003Cp>Your battle snake will whenever a game it has playing has been ended, use it to learn how you battle snake won or loss and deallocate any server side resources. Okay. This was like a good starting point. So this is what a game looks like. ID win turns snakes.\u003C\u002Fp>\u003Cp>So going back to flows, let's, do our end. So did live. We're gonna prepare data. So the only extra thing we wanted to know here was snakes. Snake count.\u003C\u002Fp>\u003Cp>What else do we wanna know here? We we might as well get it already to be fair. Might as well just do it straight within here. So snake count. Is this.\u003C\u002Fp>\u003Cp>We have we had snake count. We had turns. So once again, let's just take a look at the it was game. I think it's game dot turn. Game?\u003C\u002Fp>\u003Cp>Maybe not. Bored?\u003C\u002Fp>\u003Cp>Speaker 1: Bored? Bored?\u003C\u002Fp>\u003Cp>Speaker 0: Hold on. No. Am I using it? Oh, it's right here. At the root, turn, integer.\u003C\u002Fp>\u003Cp>So that should just be trigger.body.turn. And we wanna know we wanna tell how many things. We wanna tell how many turns. We wanna tell whether we lived or died that we needed the ID. ID.\u003C\u002Fp>\u003Cp>So trigger dot body dot the ID. I think it's just I game dot ID, I think. We'll find That sounds right. Game dot ID. Yeah.\u003C\u002Fp>\u003Cp>Nice. And while we're here, let's, let's just also get the did live. So live. Live? What did what did we call it in our director's collection?\u003C\u002Fp>\u003Cp>Speaker 1: Lip libs? Let's take a look. Was just libs?\u003C\u002Fp>\u003Cp>Speaker 0: It's called win, actually. Win. Alright. Smart. Win.\u003C\u002Fp>\u003Cp>So, oh, so in here, we'll go prepare data. We'll go live, but it's called win here. Live, live. Fine. Whatever.\u003C\u002Fp>\u003Cp>So we have, I really don't like using last or I can help it because it allows you to obviously move things around without your data becoming like brittle. So I'm gonna pop did live. And all of these have to be prepended with data. Okay. So in theory, that's the preparation of the data.\u003C\u002Fp>\u003Cp>Let's, let's run a custom game one more time. If our snake performs well, it will have been the 7 by 7 board that did it in. Well, I think I think oh, of course, we've just got let's create the game with the ready set battle snake and the hungry bot now. Yeah. Weird, isn't it?\u003C\u002Fp>\u003Cp>Anyway That's such a good idea.\u003C\u002Fp>\u003Cp>Speaker 1: I wonder what we did. Don't don't wanna know. No. That's a question for, for season 2.\u003C\u002Fp>\u003Cp>Speaker 0: That was a question. Set back. That was a question for last week of and now it's this week of do you see how big that snake's getting? Oh, of course, it's hungry, but isn't it? That's its whole that's its whole shtick.\u003C\u002Fp>\u003Cp>Speaker 1: Are we gonna win this one? Oh, we won it still.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. Legit did. So Well if we refresh here and look at our log payload, snake count 1. This is wrong. So we need to just look at this because we wanna know how many snakes were in the whole game, which in theory should always be\u003C\u002Fp>\u003Cp>Speaker 1: no. It won't always be this. End. We're pulling from end, so it's only gonna pull the last snake. Right?\u003C\u002Fp>\u003Cp>Is that what we that's what\u003C\u002Fp>\u003Cp>Speaker 0: the Yeah. Maybe we maybe we need to do it on start. We need to create it. And then on end, we need to update it. We have turns, we have the ID and we have whether we want, which is true.\u003C\u002Fp>\u003Cp>So maybe for snake count, because I do think snake count is interesting. We should create just create the data. What do you think? Or do you think it's too heavy handed to create a whole flow for that?\u003C\u002Fp>\u003Cp>Speaker 1: I mean, I don't think we need a whole flow for it. That seems like a bit of\u003C\u002Fp>\u003Cp>Speaker 0: a change. I mean, we need we need a flow for it. That's the truth. So may I mean, we we've we've got pretty we've got pretty proficient at it now. So what we're gonna do is create a flow.\u003C\u002Fp>\u003Cp>Yeah. Yeah. We'll call it snake start, Right? And so what we're gonna do here is we're gonna do this as a webhook. It'll be a post request.\u003C\u002Fp>\u003Cp>We'll go like this. Just double check the API reference webhooks. Your request to this your response to this request will be ignored. We don't need to care about our response. We'll save this, grab this URL, and then we are going to take this whole end here.\u003C\u002Fp>\u003Cp>And on the start, we'll make that async. We'll paste that in there, except this time we will change the URL to this 1, 704. So that start, we need to just kill this, go here, rebuild our extension, Done. Go here and Docker Compose up. Wonderful.\u003C\u002Fp>\u003Cp>Should be able to refresh. Should be able to refresh. Now I think it just took a moment just a moment to spin up there. And what are we gonna do here? We're first gonna save.\u003C\u002Fp>\u003Cp>We're gonna go and create a game. This will need we don't even need to wait for the game to pan out in order to look at this one log. All we care about is I suppose number of snakes here. So trigger, body, board snakes. Trigger body board snakes.\u003C\u002Fp>\u003Cp>So what we'll do here is create sorry? Board trigger body snakes. No. Trigger. Shouldn't be Stop it.\u003C\u002Fp>\u003Cp>Shouldn't be Stop it.\u003C\u002Fp>\u003Cp>Speaker 1: I shouldn't do that.\u003C\u002Fp>\u003Cp>Speaker 0: I'm sorry. Create game. We will create data in the game's collection with full access. And all we're gonna do here is we're gonna say the snakes is equal to.\u003C\u002Fp>\u003Cp>Speaker 1: Trigger body board snakes. I got it. I got it.\u003C\u002Fp>\u003Cp>Speaker 0: They need to be in quotes because it's Jason. Trigger\u003C\u002Fp>\u003Cp>Speaker 1: Jason or Jason? I I am a JSON.\u003C\u002Fp>\u003Cp>Speaker 0: Jason. There is one there is actually, extract snake count. We just need to get the value out in the previous step. I don't strictly think you need to do this, but just to keep it really clean, what we're gonna do here is we're gonna return snake count. Snakes is equal to data dot trigger dot body dot board dot snakes dot length.\u003C\u002Fp>\u003Cp>Snakes. Copy that key. And then the next one, we will create data. The name and key doesn't really matter because it's our last step here. All we care about here is we need to create an ID at the time.\u003C\u002Fp>\u003Cp>Oh, but we can create the ID as well. We can get the ID as well at the time of creation. So snakes is going to be equal to, this snake count dot snakes. And I actually think we can ignore this error. There seems to want a string, and I think that's fine even if it's, an int just to stop it moaning.\u003C\u002Fp>\u003Cp>And then we also want the ID, which is going to be oh, oh, throwing my cursor around. Here we are. Trigger dot body dot game dot id. So if I save this, we create a new game. Oh, got a little Game in progress.\u003C\u002Fp>\u003Cp>I don't think it is. Our snake 1, thank you very much, but we will create a new game. That's fine. Start game. We immediately should get a new log here and we got a trigger but nothing else happened.\u003C\u002Fp>\u003Cp>Why? Oh, no. There we are. Create data. Type error cannot read properties of undefined snakes 2 body trigger body game ID.\u003C\u002Fp>\u003Cp>Okay. Cannot read properties of undefined reading starts with. Interesting. This looks\u003C\u002Fp>\u003Cp>Speaker 1: Looks good.\u003C\u002Fp>\u003Cp>Speaker 0: Looks like what we want. Snakes and ID. Is that what we called them? Snakes and ID. What is going on?\u003C\u002Fp>\u003Cp>So star. Shrek snake count. Because in theory, this is looking oh, pick collection. That'll do it for the access. Not telling it to do anything.\u003C\u002Fp>\u003Cp>Alright. Create game. I've got really you get really proficient at starting new games with Battlesnake, don't you, when debugging? You do. Alright.\u003C\u002Fp>\u003Cp>Speaker 1: Did it work? Oh, there we go. Now how That's\u003C\u002Fp>\u003Cp>Speaker 0: the idea of a new item, item, which means we now have a new snake. Wonderful. So now going back to our end state, we don't actually want to so let's refresh ourselves. Prepare data, prepares a snake count, turns, ID and win. We do not want a snake count.\u003C\u002Fp>\u003Cp>We do not want a snake count. So when we prepare data, we can remove snake count and that leaves us with turns, ID, and win. Yep. Turns, ID, and win. Turns ID and win.\u003C\u002Fp>\u003Cp>And, actually, we don't even want the ID here.\u003C\u002Fp>\u003Cp>Speaker 1: No. We we do want the ID because we wanna be able to pull that GIF eventually.\u003C\u002Fp>\u003Cp>Speaker 0: But we stored it. It already exists.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, that's right. Yes. You're right. You're absolutely right.\u003C\u002Fp>\u003Cp>Speaker 0: This is what we're gonna This is what we're gonna so all we're gonna do here is now update the data Lovely. In the game's collection with full access with this ID, trigger body game ID, and the payload, I think we might just be able to put in lost and I think that's gonna be equal. I'm not a 100% sure, but I think that's gonna be equal to the object. So we'll see. Alright.\u003C\u002Fp>\u003Cp>The first thing we'll do is create a new game, and then I think we'll be ready to jump into insights and see how that works for us. So here's our snake performing well in these apparently quite controlled conditions of an 11 by 11 board with 1 other snake. Okay. Come on. Some something happened now.\u003C\u002Fp>\u003Cp>Speaker 1: Come on, hungry. You don't wanna survive. You're not really that hungry anymore.\u003C\u002Fp>\u003Cp>Speaker 0: It could be all all one either. You both suck. Hey. Hey. So first thing to do is let's see.\u003C\u002Fp>\u003Cp>So you will notice nothing happened there. I've also, to be fair, noticed we don't have any we just have IDs. We probably do also wanna capture, like, the date, like like the now date that's happening because, we just wanna be able to sort the latest item if possible. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: That's fair.\u003C\u002Fp>\u003Cp>Speaker 0: So we'll create a date time, finished. We can call this. I think this will work. Date time. Sure.\u003C\u002Fp>\u003Cp>And then at the well, firstly, that didn't work. So we don't wanna spend too too much time on this, but we'll prepare the data here and we'll say what was the name of the field? Do you remember?\u003C\u002Fp>\u003Cp>Speaker 1: Oh, it was, we didn't call it datetime. We set it today. No. Now? When?\u003C\u002Fp>\u003Cp>I'm not sure. Jeez. That that's terrible. Both have forgotten Finished. The minutes of creating it.\u003C\u002Fp>\u003Cp>Speaker 0: I mean, same. Same to be fair. Finished. We also wanna store finished, which I think you can just say new date, new date dot now. Is that how you do it in JavaScript?\u003C\u002Fp>\u003Cp>Speaker 1: Your guess is as good as mine, sir.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. New date dot now. Cool. But it didn't work. So as much as this is cool, what happened here?\u003C\u002Fp>\u003Cp>We updated it. We updated it with the key. Oh, do you wanna know what didn't happen? It isn't showing here that we had a payload. So I think there was an issue with the payload.\u003C\u002Fp>\u003Cp>Oh, look, the payload didn't save. Interesting. So I'm just gonna double check that that is what happened there. If I hit save and then step back in, maybe it shouldn't let me do that, but this is okay. We're not gonna lose much sleep over it.\u003C\u002Fp>\u003Cp>Finished is going to be, no, last dot finished. Then we had, win is going to be last dot win. We'll wrap that in quotes too. What was the third one? What what are\u003C\u002Fp>\u003Cp>Speaker 1: the 2 that we have now?\u003C\u002Fp>\u003Cp>Speaker 0: Win, turn Yeah. Turns. I believe. Like\u003C\u002Fp>\u003Cp>Speaker 1: a living rubber duck.\u003C\u002Fp>\u003Cp>Speaker 0: Last stop turns. Turns. I'll hit save before we, you know, spend time on a full snake. We'll take a look. Turns and win.\u003C\u002Fp>\u003Cp>Turns and win. And now we have finished. Turns, win, finished. Turns, win, finished. Great.\u003C\u002Fp>\u003Cp>So let's save all of that. Let's create a rematch. We should see, hopefully, that this is stored. And then I don't think we're gonna spend much more time capturing data. Instead, we'll run a few games, and then we'll try some insights.\u003C\u002Fp>\u003Cp>Nothing's happening still. So still something done in our logic. Let's look at our latest I'll just refresh it. Let's look at our latest log. Oh, intermediate value dot now is not a function.\u003C\u002Fp>\u003Cp>I actually know why this is the case, and it is because, these run script operations running what are known as JavaScript isolates, which are these incredibly limited JavaScript like runtimes that only contain pure JavaScript objects. Let's actually take a quick look at that because I think it's interesting. Isolut. Yeah. This page looks legit.\u003C\u002Fp>\u003Cp>That's not what I want. I do kinda wanna show you like pure Java script or, objects. This is what I'm looking for. What I'm looking for is just like, where is the list? Am I am I what's happening?\u003C\u002Fp>\u003Cp>You might\u003C\u002Fp>\u003Cp>Speaker 1: be imagining. You might be imagining this. You may have already used JavaScript.\u003C\u002Fp>\u003Cp>Speaker 0: I just I have imagined it.\u003C\u002Fp>\u003Cp>Speaker 1: Created it. Maybe you created. There's no documentation. What did you call them again? Isolates.\u003C\u002Fp>\u003Cp>Isolates. Interesting. I've never\u003C\u002Fp>\u003Cp>Speaker 0: heard of that. Isolates.\u003C\u002Fp>\u003Cp>Speaker 1: And now is that just a security thing within Directus to ensure that, like, bad actors\u003C\u002Fp>\u003Cp>Speaker 0: So this So this actually happened in version 10.6. We had I think this is quite what I want. We had prior to version 10.6, you could build, you could use NPM modules inside of extensions. Extensions. Well, video has gone a bit funny.\u003C\u002Fp>\u003Cp>I'm not sure if the is fine now. It resolved itself. Anyway, and the library which we use to isolate flows from each other and operations from each other basically became insecure. Like, it was it was became known it was insecure, and so we had to remove it. And in doing that, we replaced it.\u003C\u002Fp>\u003Cp>And now it now it runs what are known as isolates. And this is what I'm looking for, I think. 1st class. Nope. I'm looking for oh god.\u003C\u002Fp>\u003Cp>It's really bothering me, but I will take a moment to find it. What's is it in our docs? In breaking changes 10.6, we dropped support for custom npm modules in the run script operation. Prior to this release, directors relied on VM 2 to run codes in the run script operations and flows. VM 2 is now unmaintained with a critical security issue that could potentially allow for code to escape the sandbox and access the machine, which hosts directors.\u003C\u002Fp>\u003Cp>So we've migrated to isolated VM to allow flows to run safely. This basically means you can't use just arbitrary NPM packages in flows anymore. You can still turn them into custom operations, but it isn't isn't here anyway. I learned a lot in during the process of this. There are lots of things you use in JavaScript, which you think are part of JavaScript, things like set interval, things like console log, set time out, fetch, that you just think, oh, this is part of JavaScript, but it's not part of JavaScript.\u003C\u002Fp>\u003Cp>It's either part of the browser's implementation of JavaScript or Node JS' implementation of JavaScript, many of which are shared. Isolates don't have this. Isolates just have pure JavaScript functionality. And the I I what I was trying to find was the list of what that meant. I've I can't find the word off the top of my standard objects.\u003C\u002Fp>\u003Cp>Boom. Found it. Thank you. So this chapter documents all of JavaScript standard built in objects. So you have access to global this infinity, not a number undefined.\u003C\u002Fp>\u003Cp>You have these function properties only. You have some fundamental objects like object function, Boolean symbol, a few error objects, a few number and dates, a couple of text processing functions, a few on arrays, key collection structured data and so on. What you'll notice here is a lack of console logs. What you'll notice here is a lack of date, which is what I was which is what I was going for there. We're not gonna lose any sleep over this.\u003C\u002Fp>\u003Cp>I'm literally just gonna we'll remove finished. It just isn't worth the time right now. You know, I do like it though. I wonder if can I add can I add after the fact a date created? Or did that have to be done at the time of Based on my I think it might have had to be done at Yeah.\u003C\u002Fp>\u003Cp>I just scroll down. Though. Exactly. And then just flip it over. I'm just taking a look.\u003C\u002Fp>\u003Cp>There's a sort field, which is great. That's okay. Take a little take a little cheeky screenshot of this so I can refer back to it. There we go. I'll put that off at the site, and then we'll delete this collection.\u003C\u002Fp>\u003Cp>I there might be a way to do it, but I I don't have the the patience. If I see But what I want is date created because I think that would be interesting. That's all. I just wanted that one extra field. So half width.\u003C\u002Fp>\u003Cp>So we also had in here a win, which was a boolean. We had turns, which was an int. We had snakes, which was account. It was a number. It was also an int.\u003C\u002Fp>\u003Cp>And we had finished, which we've effectively replaced with a date created. So half, half, and whatever. Half. Okay. So but but so they run-in isolates.\u003C\u002Fp>\u003Cp>And isolates what it taught me was all these creature comforts I have in JavaScript are not in JavaScript, they're in Node. Js or they're in the browser implementation of JavaScript. But, yeah, that's how I basically learned that.\u003C\u002Fp>\u003Cp>Speaker 1: There you go. I had no idea. I had no idea console log was not built into JavaScript. That seems so odd. But I guess the console you're dealing with is in the browser, so it does make sense.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Let's create a new game. Let's create a rematch, see if we can get it to work. But, yeah, just fascinating. Yeah.\u003C\u002Fp>\u003Cp>Isolates. And isolates are not, you know, while that's running, isolates Cloudflare. I wanna say Cloudflare Workers. Yeah. Here.\u003C\u002Fp>\u003Cp>They have some really good reference while I was learning more about it. Isolates. VA orchestrates isolates, lightweight context, provide your code with variables that can access in a safe environment to be executed within. You could even consider an isolate a sandbox for your function to run-in. A single runtime can run hundreds of thousands of isolates switching between them.\u003C\u002Fp>\u003Cp>And isolates memory is completely isolated, so each piece of code is protected from other untrusted and user written code in the runtime. Islets are also designed to start very quickly. Instead of creating a virtual machine for each function, ISIT is created within an existing environment, blah, blah, blah, blah, blah. So but what's interesting is they are they are way more limited.\u003C\u002Fp>\u003Cp>Speaker 1: Yep.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: I wondered I wondered why because I I think what did I I was reading yeah. I've been reading through the documentation, and it said that the the specific JavaScript that you were able to call within within lots of these areas, the directives were limited. And I was like, oh, that's that's an interesting and I figured it was security, but I didn't realize there was so much to learn here. But that is why you come here to Ready, Set, Battlesnake is to learn things about Battlesnake and JavaScript and directives.\u003C\u002Fp>\u003Cp>Speaker 0: And it worked.\u003C\u002Fp>\u003Cp>Speaker 1: And it worked. Let's go.\u003C\u002Fp>\u003Cp>Speaker 0: And it worked.\u003C\u002Fp>\u003Cp>Speaker 1: We did it.\u003C\u002Fp>\u003Cp>Speaker 0: So what we're gonna do now really is we're gonna just hit create rematch. And if I can I create loads of games in in, like like Yeah? Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It's gonna break things.\u003C\u002Fp>\u003Cp>Speaker 0: We we we have also not built just a moment. We have not built logic to know which game is happening. We'll do one at a time. Top of them, we have. We have.\u003C\u002Fp>\u003Cp>We have. Because every turn is individual on the move endpoint. And then on the start and end, we're referring to the IDs. Actually, we could be running a load of these in parallel.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So give it a try. Let's see what happens. See what happens.\u003C\u002Fp>\u003Cp>Speaker 0: What's the worst that could happen?\u003C\u002Fp>\u003Cp>Speaker 1: We end up having to pause our episode because we made a mistake and we have to fix it off camera. That is the really the worst that could happen.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, I am a bit concerned now because I don't even know when they've completed. That's another problem is I actually don't know when they I've taken a a guess that they won't take longer than each other. Like, alright, that game's over. I mean, let's just see. There you go.\u003C\u002Fp>\u003Cp>We're not winning every time. We have a number of turns. We have a number of snakes. Let's, let's just create a few more games. Like, I I want a a reasonable set to start with.\u003C\u002Fp>\u003Cp>All of them are gonna start with 2 snakes. Go on.\u003C\u002Fp>\u003Cp>Speaker 1: I was gonna say just so if you, like, if you're if you're watching this and you're like, oh, I don't wanna have to, like, do this. It's it's causing my browser. I don't like all those tabs. You can, like, do all this from the CLI too. Like, you can actually go in and just, like, continually create games for the CLI.\u003C\u002Fp>\u003Cp>This is just easier than going in and setting up the CLI right now.\u003C\u002Fp>\u003Cp>Speaker 0: And I don't wanna set up the CLI in this moment. So we should actually know if any games are outstanding because, yes, see, there's no turns and no wins on this one, which tells us that game is pending and now it has concluded. So, actually, we do have a little indication of how games are going.\u003C\u002Fp>\u003Cp>Speaker 1: Look at us.\u003C\u002Fp>\u003Cp>Speaker 0: So we'll create a few more games with a mixed player count. Okay. And if we refresh Directus, we should see okay, there are a few games still pending completion. If I refresh, 2 games pending completion, refresh 1 game pending completion, refresh. That game is still going.\u003C\u002Fp>\u003Cp>That game is still going. That game is still going.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, still going. We're doing well.\u003C\u002Fp>\u003Cp>Speaker 0: Is that is that the last one we created?\u003C\u002Fp>\u003Cp>Speaker 1: I mean, we'll find out. It must be. I hope it is.\u003C\u002Fp>\u003Cp>Speaker 0: Descending. Oh, they've all concluded now. Okay. I think that's good to start. So Yeah.\u003C\u002Fp>\u003Cp>Let's go to insights, see see what we can do. Snake battle station. Let's pick a little kiss. Pick wonderful. Pick a little thing here.\u003C\u002Fp>\u003Cp>Is there a I don't is there like a snake? No? Is there is I don't want this game. Yeah. Cool.\u003C\u002Fp>\u003Cp>And our battlesnake. You know, do you wanna know why green feels like a very snaky color to me?\u003C\u002Fp>\u003Cp>Speaker 1: It does. Save. That is\u003C\u002Fp>\u003Cp>Speaker 0: the right one. Snake battle station. So this is, director's insights. It's this little module over here. And here, we can create dashboards.\u003C\u002Fp>\u003Cp>I don't really use it very much personally, just because my use cases don't demand many dashboards. So there might be a little bit of learning here. You can create panels. Just as a note, this is panels also an extension type. So if you wanna, you know, build on top of these panels, you know, build additional panels, you're more than welcome to.\u003C\u002Fp>\u003Cp>For this, I think we're going to stick in here. You know, we have the pie or donut chart, which feels like a good starting point. So we're gonna look at exactly what I had in mind. So win, Win. Count.\u003C\u002Fp>\u003Cp>Sure. Don't care about donut. That's just a display thing. Show labels. Yes, please.\u003C\u002Fp>\u003Cp>Show legend. Yeah. Pro oh, do you wanna know what? Let let's just see what happens here. Can because I think we can do conditional styles as well.\u003C\u002Fp>\u003Cp>So win rate. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Hey. Dang. That was I don't wanna say that was too easy, but that was very smooth.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Although I we do need a legend. We do need a legend quite desperately.\u003C\u002Fp>\u003Cp>Speaker 1: I would just always assume the the bigger one is my win rate. That's really\u003C\u002Fp>\u003Cp>Speaker 0: It it you you are correct. Alright. Win rate win no. Win rate feels good. We can do, games, you know, we could do, the x axis is this one.\u003C\u002Fp>\u003Cp>Right? So date created maybe and turns, like how well am I doing over time.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. I like it.\u003C\u002Fp>\u003Cp>Speaker 0: Aggregation. I don't really know what I'm doing here.\u003C\u002Fp>\u003Cp>Speaker 1: Well, okay. Just leave it leave it without aggregation and see what happens.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Sure. And we'll call this one what are we doing here? Turns. Turns feels like a weird one.\u003C\u002Fp>\u003Cp>Maybe we just do turns feels weird. Why don't we do, like, account? Because I think what it will do, that's tell\u003C\u002Fp>\u003Cp>Speaker 1: us the number of games. Sure. Let's find out. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Right. Games over time. Of course, we'll be looking at a very micro Woah. I'm not sure this is gonna pan out. This totally is not correct.\u003C\u002Fp>\u003Cp>Okay. Something. We did a bad thing. Aggregation can be forced as well. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Can you? Well, set it as false to see what happens. See what that change does. Oh, didn't like that. We are not doing that.\u003C\u002Fp>\u003Cp>I guess we do need to have Oh,\u003C\u002Fp>\u003Cp>Speaker 0: aggregation is gone. Okay. So we have turns. We have snakes, we have x axis, which is date created. Do we need something on both axis?\u003C\u002Fp>\u003Cp>I'm sure. Do we? Oh, maybe not. I mean, it kind of looks like we do. I mean, I'm kind of as you can tell, I really don't make a lot of dashboards because, you know, it's just not it's just not my world.\u003C\u002Fp>\u003Cp>Maybe if we let's let's let's, see what else we can do. We have I like metric firstly, which is like total games played. That one feels easy.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's simple.\u003C\u002Fp>\u003Cp>Speaker 0: Aggregate function, just count. Count. Yep. And, suffix, Games played. Star decimal unit.\u003C\u002Fp>\u003Cp>Notations standard. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Oh. Uh-oh. GraphQL validation error?\u003C\u002Fp>\u003Cp>Speaker 0: I think I'm just not I'm genuinely thinking I'm just not selecting enough field. Do we need to do a field ID? Yeah. We did. Oh, there we go.\u003C\u002Fp>\u003Cp>15, 15 groups load. Nice. Sure. Okay. It's not pleasing my eyes very much that, but let's, keep taking a look.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. What do we have still?\u003C\u002Fp>\u003Cp>Speaker 0: Font size auto, which makes sense, or you can make it fixed and then you can add padding accordingly. I do get the auto. Auto makes a ton of sense. We have a line chart. We have time series.\u003C\u002Fp>\u003Cp>So the collection, once again, games, the group aggregation, oh, group precision minute because we set a few games off at the same time. Count, date field, date created, date range, past 15 minutes. Sure. Value field. I, turns.\u003C\u002Fp>\u003Cp>Snakes. Oh, I don't know. I mean, can I edit can I can I oh, there it was? Can I clear the value, please? I'm just gonna hit tick.\u003C\u002Fp>\u003Cp>Alright. Didn't do anything. I've picked I've picked the wrong combination of picked the wrong\u003C\u002Fp>\u003Cp>Speaker 1: combination of options. We almost we almost did it.\u003C\u002Fp>\u003Cp>Speaker 0: Did you see how confident I went in?\u003C\u002Fp>\u003Cp>Speaker 1: You went in. Listen, confidence is half the game here. We just gotta go in thinking you know what you're doing and, eventually,\u003C\u002Fp>\u003Cp>Speaker 0: you know I'm not really sure. I'm not really showing this bit of director stuff pretty well.\u003C\u002Fp>\u003Cp>Speaker 1: No. I think it's I think you're doing this fantastic. I like that we've been able to get stuff like we so it's because I said it was so smooth. Hey. There we go.\u003C\u002Fp>\u003Cp>Nice. And these are showing us?\u003C\u002Fp>\u003Cp>Speaker 0: So this is showing us the number of turns taken on a minutely basis. And that was where I set loads of games off, and this was just as it was coming down from the last data point that was held. If I do it the past 30 minutes, I think I'll get a slightly oh, maybe not.\u003C\u002Fp>\u003Cp>Speaker 1: It's a good try.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. This is oh, here we are. Automatic based on data. That feels good. Okay.\u003C\u002Fp>\u003Cp>So 80 turns over here and then 320 turns over here. And then if we create a couple more games now, we'll create a game.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And I do like this. Like, if you were actually looking at this over a period of time, this would make a lot more sense.\u003C\u002Fp>\u003Cp>Speaker 0: Could be really insightful. Yeah. So let me just save that, and we'll just make sure. Oh, thank you. Woah.\u003C\u002Fp>\u003Cp>Last game has not concluded yet, so we'll just let that conclude. Okay. So if we go back to insights now, there you go. It's a little bit more of a of a view. Right?\u003C\u002Fp>\u003Cp>So I don't really wanna look at the number of turns though. I wanna look at is it count? I think that will give us the same graph. Yeah. What I want is I just wanna know how many there are.\u003C\u002Fp>\u003Cp>Speaker 1: How many games?\u003C\u002Fp>\u003Cp>Speaker 0: How many how many games? Do you think turns is interesting?\u003C\u002Fp>\u003Cp>Speaker 1: I no. I mean, I think let's I so turns is interesting, but only if you're playing 1 at a time.\u003C\u002Fp>\u003Cp>Speaker 0: Exactly. And I think I think I think there's a way to do that. I've seen other people do it. So we had I think it might this global variable variable key can be like game. Confidence.\u003C\u002Fp>\u003Cp>Speaker 1: Lots of confidence.\u003C\u002Fp>\u003Cp>Speaker 0: Game interface. That's not what I want. Type. Is it in here? No.\u003C\u002Fp>\u003Cp>I've seen it. I've seen it somewhere else. Is it maybe called global relational and you pick a collection, you pick games, display template? Yes. You find the ID.\u003C\u002Fp>\u003Cp>I think this is it. So you select a game. So it says select an item. So can I, whatever? But we we won't look at editing that.\u003C\u002Fp>\u003Cp>So you select a game, right, like this. Great. And then you can use that global variable in all of the other boards Okay. In all of the other panels. So if I you know, maybe we do this.\u003C\u002Fp>\u003Cp>This is like global values up here. Great. And then we have this, you know, dynamic portion. So what do we want to know here? Well, we want to know I I don't know.\u003C\u002Fp>\u003Cp>Like, what about are we doing we're not doing per turns. We're not storing turns too. Maybe we just do what do you think?\u003C\u002Fp>\u003Cp>Speaker 1: There's only one other thing that I thought would be really fun, but I think that the logic is gonna take too long. No. And it I mean, not gonna be related to this panel. I think it's I I'd be very interested to pull in some custom that that the the actual gift for the game. I think there's something interesting there to be able to pull in.\u003C\u002Fp>\u003Cp>Speaker 0: I think we could do that. I think we could do that. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: That's an ambitious it's an ambitious goal. You wanna try?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Go on then. Right. Tell me how to get the tell me how to get the URL. Go to your game.\u003C\u002Fp>\u003Cp>I'm I'm going. Got it. Alright. Go to view\u003C\u002Fp>\u003Cp>Speaker 1: as GIF, and then it'll give you the format\u003C\u002Fp>\u003Cp>Speaker 0: that you I will only click it when you say it correctly.\u003C\u002Fp>\u003Cp>Speaker 1: GIF\u003C\u002Fp>\u003Cp>Speaker 0: GIF. You may. GIF. Okay. And there's a URL and that's the game ID.\u003C\u002Fp>\u003Cp>Speaker 1: Grab that,\u003C\u002Fp>\u003Cp>Speaker 0: And that's that ID is movable. Right? Like, that ID is the ID of the game.\u003C\u002Fp>\u003Cp>Speaker 1: So\u003C\u002Fp>\u003Cp>Speaker 0: you can buy back\u003C\u002Fp>\u003Cp>Speaker 1: and game by game.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So let's try our hand.\u003C\u002Fp>\u003Cp>Speaker 1: We need to do a custom panel.\u003C\u002Fp>\u003Cp>Speaker 0: That is that is definitely what we need to do. Yeah. But, right. So what are we gonna do here? We're gonna go up one layer.\u003C\u002Fp>\u003Cp>We're gonna go MPX create directus extension at latest. Whoops. Extension. Helps when you type it correctly. Right.\u003C\u002Fp>\u003Cp>What we're gonna do, we are oh, you're you're putting me on the spot here. I'm not overly confident. And viewers, if we don't get it done, we're not gonna get it done. But let's call let's consider this a stretch goal. I also haven't used these global variable values too, too much.\u003C\u002Fp>\u003Cp>While that's kind of doing its thing whoops. Docs, Data Studio app. I think it might be over in the user guide, insights, panels, global relational value here. Oh, and then in other in other, ones, you can use the variable key like in other panels. That's interesting.\u003C\u002Fp>\u003Cp>Anyway, we want a panel, director's extension, snake panel. JavaScript install dependencies. Okeydokey.\u003C\u002Fp>\u003Cp>Speaker 1: I'm glad I gave you this challenge, and I am not the one having to implement it. This seems right. This seems good.\u003C\u002Fp>\u003Cp>Speaker 0: It all seems good with the world. Alright. So we have the panel here. We have our panel dot view. It has been a while since I built an extension.\u003C\u002Fp>\u003Cp>So here we have all of the configs. We'll call this snake GIF snake GIF. Sure. We could do loads of configuration. We're not gonna do that.\u003C\u002Fp>\u003Cp>First thing we're gonna test is if we just have a if we just have a string like we manually insert it to start, can we can we play? Can we play it back? So in the panel, we have this text here, and I think all we would need to do, there's a bit of an assumption in here, is we're gonna paste in this URL and we're going to interpolate here the text, right? That feels right?\u003C\u002Fp>\u003Cp>Speaker 1: It feels very good.\u003C\u002Fp>\u003Cp>Speaker 0: Npm run dev. Sorry. Npm. I'm I'm not even in the Npm run build. I wasn't right running the right command or in the right location.\u003C\u002Fp>\u003Cp>And then we will I think what I'll do is I'll can I hang on? Kinda wanna open them side by side. There we go. And then I wanna just kill that, restart it. Once again, fixed as of, like, yesterday's release.\u003C\u002Fp>\u003Cp>I just haven't got around to upgrading this one yet. Open up our panel, and I'm hoping snake GIF. Hey. We insert an ID here. Let's go get an ID.\u003C\u002Fp>\u003Cp>This one, 72 turns. And so I I can't believe it might actually why did I say that?\u003C\u002Fp>\u003Cp>Speaker 1: You've jinxed it now,\u003C\u002Fp>\u003Cp>Speaker 0: but it's okay. Here. Hit save. Okay. Save.\u003C\u002Fp>\u003Cp>Speaker 1: And\u003C\u002Fp>\u003Cp>Speaker 0: oh. Don't know, mate.\u003C\u002Fp>\u003Cp>Speaker 1: Happening. Oh,\u003C\u002Fp>\u003Cp>Speaker 0: I have my dev tools kind of detached. Content security policy.\u003C\u002Fp>\u003Cp>Speaker 1: So this might actually be cross it might be course with the dev side\u003C\u002Fp>\u003Cp>Speaker 0: platform. Which is which is, the ilk of what this is. How do I solve it? How do I solve it? It's a GIF.\u003C\u002Fp>\u003Cp>It's a GIF at a location. Can we import it maybe? Can we import it? And then how would we import it? Let's take care of let's let's consult the docs.\u003C\u002Fp>\u003Cp>So we have in here extensions, developing extensions, extension services, accessing files, import a file. This is inside of an API extension how we would import a file. We would instantiate a file service. We have an asset key, which has the URL and a file object, which contains additional metadata, and then we read it in order to to return it. Excuse me.\u003C\u002Fp>\u003Cp>So let's have a think about the right way to play this. We could press a button. It'll import it and then play it back. It's a good idea. Let's try\u003C\u002Fp>\u003Cp>Speaker 1: that. That seems easy. Let's\u003C\u002Fp>\u003Cp>Speaker 0: try that.\u003C\u002Fp>\u003Cp>Speaker 1: I say this. I say this.\u003C\u002Fp>\u003Cp>Speaker 0: Easy as a That sounds easy. That sounds easy.\u003C\u002Fp>\u003Cp>Speaker 1: Kevin problem. Sounds like an Android watch, and Kevin solves this tactical challenge.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Alright. Alright. So let's just keep this easy for a moment. Let's let's stitch this off, and let's create a new bundle.\u003C\u002Fp>\u003Cp>And I'll call this one directus extension, and I'll just call it, Battlesnake Viewer and install dependencies. So that's creating a bundle. In that bundle, we'll have an endpoint and we will have, and we'll have an endpoint and a and a panel. So we'll CD into the battlesnake viewer, and we'll go npm run add. And the first thing we'll add is actually the endpoint.\u003C\u002Fp>\u003Cp>We can just call this one importer. JavaScript. And now inside of Battlesnake Viewer, source importer index dot j. So this is again, our custom route as mentioned earlier. Now inside of our existing one, we've actually got this is snake GIF.\u003C\u002Fp>\u003Cp>We have our actual snake here. We're gonna we're gonna just kind of nab this structure for a moment, inside of the importer here. We'll remove these actual things here, like so. Okay. So we'll call this one importer.\u003C\u002Fp>\u003Cp>Right. So when we hit importer, what we're gonna do, and we're gonna do a post request here, is we are going to access files. I'm gonna copy all of this. Here. We'll see what we can do.\u003C\u002Fp>\u003Cp>Oops. Okay. Router dot post. We will create a files service. Now I actually think, the docs do talk about importing that.\u003C\u002Fp>\u003Cp>So let's just take a little look at the top here. Yes. Okay. So in the endpoint itself, we have a router and a context. Awesome.\u003C\u002Fp>\u003Cp>Let's do a let's do a little more side by side jobby here. Right. So we have router and we have context. And up here, we're going to grab these values out. Then inside of router.post\u002Fcool, we will create a new file service.\u003C\u002Fp>\u003Cp>We will look at rec. So file.url is going to be req.body. URL. Cool. And the file object itself, how much of this must we pass?\u003C\u002Fp>\u003Cp>Let's look at the file object. API reference files. File object looks like this. How much of this must we pass? I don't think any.\u003C\u002Fp>\u003Cp>I don't think we need any of this. So I think we'll remove that. So we have our URL. Then we will import it. It will come back.\u003C\u002Fp>\u003Cp>We will then have the asset key, and we'll return the data. So if all goes well, npm run build, This is our importer, docker compose up. And I think what we wanna do here is test whether this endpoint works. Now I still have ngrok running. I don't still have it running.\u003C\u002Fp>\u003Cp>I've been running it since the beginning of this, this session. And I have hopscotch here, which is similar to Postman. It's just like an HTTP Explorer. So here, if I go importer post and inside of the let's just hit send there. Okay.\u003C\u002Fp>\u003Cp>Timing out, timing out, timing out. There is an error, invalid URL, input object object. Okay. Let's just cancel that. Let's set a body.\u003C\u002Fp>\u003Cp>Let's set this to be application JSON. There we are. And what we want in here is a URL of and then this exporter battle sync. So hopefully this will return the ID of a new item in our directors project. Okay?\u003C\u002Fp>\u003Cp>It's still going. So not feeling good about that. Invalid URL. Is it, though?\u003C\u002Fp>\u003Cp>Speaker 1: Is it? Apparent I mean, no.\u003C\u002Fp>\u003Cp>Speaker 0: No. No. Object object. Import 1. So it was struggling with import 1.\u003C\u002Fp>\u003Cp>So let's let's take another look at this. So it was struggling here with the file service dot import 1. Req dot body dot URL. Now to be fair, we never once took a moment to check whether it was gonna be req dot body. So let's just res.sendokay for a moment and that's console.log req.body.\u003C\u002Fp>\u003Cp>There was a bit of an assumption in there. So we will rebuild that and then we will restart that. Okay. And we will send that and we should hopefully get an okay back. We did.\u003C\u002Fp>\u003Cp>So let's take a look. So inside of req.body, yeah, dot URL. What's going on? Is this a URL we can hit? Oops.\u003C\u002Fp>\u003Cp>Yeah. Should be. Yeah. Sure. And let's just test that there's nothing funny going on with, like, access here.\u003C\u002Fp>\u003Cp>If I go to our file library and import, this is the RF for importing and hit import. The GIF is there. So there is nothing wrong with that. So we're just doing that via API instead. So fair enough.\u003C\u002Fp>\u003Cp>Fair enough. So we have the URL. We have some accountability, which I think we might just be able to bundle some\u003C\u002Fp>\u003Cp>Speaker 1: for now.\u003C\u002Fp>\u003Cp>Speaker 0: Get rid of it for now. Let's do npm run build and restart the container. Go to hopscotch and send this.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Great.\u003C\u002Fp>\u003Cp>Speaker 0: And this time, we've got a URL, which means there was nothing wrong. So let's just do one step at a time. That's console dot log the asset key. Yep. Rebuild.\u003C\u002Fp>\u003Cp>I'm determined to get this to work now.\u003C\u002Fp>\u003Cp>Speaker 1: We are gonna do it. We have we have the Cool. Technology.\u003C\u002Fp>\u003Cp>Speaker 0: We have the ability. That should still just return an okay, but it hasn't. Why? Code error invalid URL. It gives me it gives me it gives me the docs might not be right, which is possible.\u003C\u002Fp>\u003Cp>So as much as I wanna just be like, but that's what it says to do. Let's take a moment and let's actually, if we go to accessing if if let's find let's find the docs for this and let's just step through because all of the services are just in the code base. It's not my preferred way, of course, to be looking at stuff. Let's take let's take a little look. What we care about here is import 1, and it does expect an import URL and a body partial.\u003C\u002Fp>\u003Cp>So maybe maybe we did need to pass in, could not fetch from URL. The error said invalid URL out of interest. So maybe yeah. Because what it does is it grabs the raw file and then uploads it. Maybe we need to we must pass in this body.\u003C\u002Fp>\u003Cp>Just a thought. And that was the partial body objects. And maybe I mean, that's a partial body object. Maybe there's something else we must pass in this at the time of creation. Let's, let's hit send.\u003C\u002Fp>\u003Cp>Speaker 1: Come on. Okay. Okay. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: In fact, again, it's taken that long. Yeah. No. Input object object code invalid URL. That invalid URL.\u003C\u002Fp>\u003Cp>Couldn't fetch file from URL object object. Isn't that interesting? Because the URL to me is a is a string. Right? Yep.\u003C\u002Fp>\u003Cp>And found that URL. But this time, before all of this, I logged service external file is unavailable. Couldn't fetch file from URL object object, but it isn't. It is a string, and this is the string. Fascinating.\u003C\u002Fp>\u003Cp>I mean, let let's just, you know, I've been a bit cheeky here. Maybe we, go back to accessing files, and I do actually create it with this accountability object here. Yep. I'll take that. I'm pretty sure I know it's not needed.\u003C\u002Fp>\u003Cp>I know it's not needed. But that the error isn't saying something else is missing. You've triggered an unhandled rejection. You may okay. Yeah.\u003C\u002Fp>\u003Cp>Fine. Okay. True. So if we go if we try around this, right, and then we catch an error. We console dot log the error.\u003C\u002Fp>\u003Cp>And we send this.\u003C\u002Fp>\u003Cp>Speaker 1: And\u003C\u002Fp>\u003Cp>Speaker 0: We are expecting an error, which is fine. We've got 503 service unavailable. Why? Couldn't fetch file from URL. This is what's troubling me.\u003C\u002Fp>\u003Cp>Object object. Let's take a let's just take another look here. So import 1. I think I worked it out.\u003C\u002Fp>\u003Cp>Speaker 1: No. That was quick. Goodness.\u003C\u002Fp>\u003Cp>Speaker 0: It is an opt I did pass in an object, didn't I? But that's not what's being passed in in the in the reference. The import URL is a string and the body, which is a partial file that should have been passed in as a string. All you need to do, Kevin, is read. Read the docs.\u003C\u002Fp>\u003Cp>So read\u003C\u002Fp>\u003Cp>Speaker 1: the There's another person here, and we still didn't get there.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Okay. Asset key. We got an asset key that time. Success.\u003C\u002Fp>\u003Cp>There should now be a file in the file library. Right? Correct. So we are loading it in. We are loading it in.\u003C\u002Fp>\u003Cp>Title, description, tags, file name disk, all grand, but\u003C\u002Fp>\u003Cp>Speaker 1: Beautiful.\u003C\u002Fp>\u003Cp>Speaker 0: What we also want to do is this. We want to, YOLO. I can't be bothered with all this. We'll just there we are. We'll take the asset key, then we'll read the new asset, and we'll return the data.\u003C\u002Fp>\u003Cp>So what we'll get back in in response to this is it will always import. So that's not ideal because if it already exists, we don't necessarily want to reimport it every time. But for the sake of this little demo, I think it's fine. Then we'll get the ID back. So now if I rebuild this and we run this again oh, it was still spinning up.\u003C\u002Fp>\u003Cp>What we should get back is the new file object indeed, including the ID. And the nice thing about this is, if I go to access control, public, and I turn on directors files actually, I don't even need to do that. If I go to httpsphzedm.ngrok.io, right, slash assets slash ID will get a you don't have permission to access this error. Right? But what I can do is go to the user, generate a token.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Generate the token, save it, and I pin that to the URL. Nice. Access token equals. And there's our Hey. Merge directly, which is pretty which is pretty cool.\u003C\u002Fp>\u003Cp>That's very cool. To keep this easier, though, I'm actually going to change the permissions of the files collection. So then you net we never need to pass in, pass in the key. So public system collections, directors files, all access on. Great.\u003C\u002Fp>\u003Cp>So now that's public. So now we've done the endpoint. The job is to create the panel. So what we'll do here is npm run add.\u003C\u002Fp>\u003Cp>Speaker 1: That should be pretty easy, I think. I say that. That's a dangerous\u003C\u002Fp>\u003Cp>Speaker 0: Easy for you to say.\u003C\u002Fp>\u003Cp>Speaker 1: He does it. I know. It's very easy for I I think you've got this, Kevin. I trust I believe. I trust in Bodie.\u003C\u002Fp>\u003Cp>Next week, when I'm when I'm driving again, things are gonna go terribly awry, and you're just gonna kick back and and relax and\u003C\u002Fp>\u003Cp>Speaker 0: enjoy the experience. Oh, yeah. I know. Viewer. What did we call the previous attempt to an extension here?\u003C\u002Fp>\u003Cp>Snake GIF, find viewer, viewer. This is my custom panel. Now there is some text. Now what we need to do, I think what we'll do is we'll remove the options entirely, right, because we don't want any options. It'll be a standard panel.\u003C\u002Fp>\u003Cp>And inside of that panel, we are going to have button button. We no longer have text. That's not a prop that's being passed in anymore. We just deleted it. So what we'll do is we'll do a v button.\u003C\u002Fp>\u003Cp>No. We won't. We'll do a v let's talk about what all these v things are for a moment. Back in our docs here, back in our docs, we have, resources for building extensions, components. And we have this components playground.\u003C\u002Fp>\u003Cp>These are all of the directors components that are inside of the Data Studio are made available to extensions. So we have here v button, and this is the director's button inside of the director's app. So we we have an immediate availability of this v button component, which is, yeah, pretty, pretty nice. So we can access that straight away. So we can go v.\u003C\u002Fp>\u003Cp>I did just notice it was v button like this and we'll call this, not v. I keep going back to the button. Sorry. We're looking for\u003C\u002Fp>\u003Cp>Speaker 1: No. That was right.\u003C\u002Fp>\u003Cp>Speaker 0: No. Yeah. But we want the input because we want to put in the ID. Yes. Yes.\u003C\u002Fp>\u003Cp>Yes. Because we'll keep it easy. We'll we'll put, like, right now, manual ID of a game hit go\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Rather than, Automating it. Yes. Pick pick it from a drop down because we're running we're running out of time. So v input to v model. So, what are we gonna do here?\u003C\u002Fp>\u003Cp>Export default. We're gonna do data return, and we'll call this ID. Like so. Then inside of here, we'll put in the v input with a model of ID. Then we'll say on key up dot enter fetch, game, GIF, get GIF, fetch, git.\u003C\u002Fp>\u003Cp>Cool. And we will add and this is not really an ideal way of of writing these, of course, but methods and we'll call this fetch GIF. Yeah. And we'll just console dot log or you know what? We'll alert with the this dot ID value.\u003C\u002Fp>\u003Cp>That's what we'll do for now just to test all of that works before we go too far. So we'll go in here. Oh, I need to sorry. Npm run build was the command there. Oh, error.\u003C\u002Fp>\u003Cp>Speaker 1: Come on. What are you doing, error?\u003C\u002Fp>\u003Cp>Speaker 0: What are you doing? After component panel component, we're missing a comma. Okay. NPM run build. Lovely.\u003C\u002Fp>\u003Cp>And we'll restart that. Also lovely. So let's refresh. Let's refresh. Just took a hot minute there.\u003C\u002Fp>\u003Cp>Insights. New panel. Viewer. No options available. Right?\u003C\u002Fp>\u003Cp>Because nothing happens to the panel itself. This, wonderful, is our panel and we'll say we'll save it. Hey, enter. Great. So this means we are able to do something with that.\u003C\u002Fp>\u003Cp>What we're gonna do with it is we're going to go back to our docs. We're gonna look at our composables for app extensions, which is what this is. I'm gonna just close out everything else for now. And we're gonna use API. And what this will do is it will allow us to make API calls, to our endpoints that are already authenticated.\u003C\u002Fp>\u003Cp>Though, to be fair, I believe this is a completely public endpoint, so it shouldn't matter much. But still, we'll do it as as it should be done. So what we're gonna do here is we are going to import use API. We are going to use this composable, like so, and then we can make our endpoints like and then we can make our calls like this. So we will make this async.\u003C\u002Fp>\u003Cp>And what we're gonna do is we're gonna post to slash importer\u002Fimporter. And we have response dot data here. So all we're going to do is console dot log response dot data. Let's build. And this is all basically to get around that that security issue.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That course issue. I love that there's always just like a 6 step workaround to deal with course. Thank you.\u003C\u002Fp>\u003Cp>Speaker 0: As opposed to just dealing with it head on. Yeah. So, we refreshed it. This is great. What's happened to my text box?\u003C\u002Fp>\u003Cp>What's happened to the text box? Is it no, but it's not. Where's my text box? Also, what's happened to this one? What's happening?\u003C\u002Fp>\u003Cp>Not seeing any errors. The API could not be found. Couldn't load extensions. Okeydokey. Have I missed a step?\u003C\u002Fp>\u003Cp>Have I rushed it again? Have I rushed a little bit? Oh, script setup. So we'll be using, we have to move this to script setup. And let's just take a quick look at how this I need to refactor this.\u003C\u002Fp>\u003Cp>Of course, we do, but that is fine. Async fetch GIF should just be able to, for the most part, come up here. We'll say, const fetch GIF equals a sync function like so. Then is that legit? Yeah.\u003C\u002Fp>\u003Cp>That is legit. And then so that's the methods. Then we have the data, which is the ID. So we will be import import ref from view. We'll create a new variable for that.\u003C\u002Fp>\u003Cp>We'll go const ID equals ref. Great. So we've handled that. Then we have props. And I believe the way to do props here is we say define props.\u003C\u002Fp>\u003Cp>Can we do this? I think we can do this. And all we care about here is show header, which to be honest, I do not care about. In theory, I've just refactored all of that. Let's take one more quick look, fetch GIF.\u003C\u002Fp>\u003Cp>So we have this dot ID. So we don't need that anymore. It'd be ID dot value, but we're not gonna alert it anyway. Kevin, watching\u003C\u002Fp>\u003Cp>Speaker 1: your work is just fantastic, by the way. I love that I gave you this challenge and, like, everything has gone wrong. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: This is this is an Axios instance. I don't quite remember how it bloody works. So I think what we do here is we do, and I need to check, axios post. Oh, we do we literally just pass in the whole object as the second. Great.\u003C\u002Fp>\u003Cp>So in here, we're gonna do URL equals ID dot value. Okay. And then we have response dot data. Cool. So we'll console mock that.\u003C\u002Fp>\u003Cp>Alright. Let's rebuild that. Is it gonna complain this time? No, it bloody isn't. Docker Compose up.\u003C\u002Fp>\u003Cp>You give me a right workout here. Let's refresh, and we should now have access to use API. Now none of it's loading. Now it is. And there's our text box.\u003C\u002Fp>\u003Cp>Let's open up our dev tools. Of course, it didn't like the URL that was before. That was this panel here. Just testing that out. So in here Can I grab a screen?\u003C\u002Fp>\u003Cp>Pop it off at the side. Over here, yeah, we need a game ID. So we can do that here. Game ID. So we're not done yet because we're not gonna display it yet.\u003C\u002Fp>\u003Cp>If we hit enter, what's happened?\u003C\u002Fp>\u003Cp>Speaker 1: Refused refused to load the images.\u003C\u002Fp>\u003Cp>Speaker 0: No. No. No. That that's the old one. That's the old one.\u003C\u002Fp>\u003Cp>In fact, if I just remove that whole thing, we'll be better off for it. Let's refresh. See no errors. Let's paste that and let's hit enter. Nothing's happening.\u003C\u002Fp>\u003Cp>Why? Fetch GIF. Let's make sure this is ever running. You know what? This is all cute and all this.\u003C\u002Fp>\u003Cp>Let's just, just in case it's some silly async fetch gift, just in case it's some silly, async function, just in case it's something silly in the way that oh, I think it might have said invalid URL. I think it might. Do you wanna know why invalid URL? Because it isn't the full URL. We're just posting the ID.\u003C\u002Fp>\u003Cp>Oh, that's right. Yes. So what we'll do on the importer package is we're actually going to, we're gonna import req.body.url. Cool. So we're gonna make URL.\u003C\u002Fp>\u003Cp>Where is it? Here. Exported.battlesnake.comporter.battlesnake.games. And then here, req.body.idgamegame, maybe we call it. And in here, we pass in the earth.\u003C\u002Fp>\u003Cp>So now it needs a game. That's the value we need to pass going back to our panel. This is going to be game ID dot value. Let's rebuild the extension. Okay.\u003C\u002Fp>\u003Cp>Oh, yeah. We'll refresh. I do understand our network error showing there. Now we'll paste in the ID of the game. We'll hit enter.\u003C\u002Fp>\u003Cp>Oh, you know what we didn't do? We didn't open up dev tools. Let's skip to console. Wicked. Okay.\u003C\u002Fp>\u003Cp>We now have an ID of a directus I like, a directus asset in the ID. So all we need is the ID here. So this is the ID of the game, game ID, game ID, and then an image in here and the source of that image is going to be, how is this going to work? We need to just have a quick think about this. I'm gonna try something out.\u003C\u002Fp>\u003Cp>I think if we go \u002Fassets slash and then we need a value in here. So in here, we're gonna go image ID, and it's gonna be a ref and image ID dot value equals response dot data dot ID. Right? So Yep. Now this has the value in it of the ID, image ID.\u003C\u002Fp>\u003Cp>First thing we'll do here is just a quick v if image ID. We only wanna show it when that's the ID, and then here is going to be image ID. Let's see if that works. I'm not a 100% sure it will, but should do.\u003C\u002Fp>\u003Cp>Speaker 1: I have faith oh, yep. There we go.\u003C\u002Fp>\u003Cp>Speaker 0: I don't. Let's refresh.\u003C\u002Fp>\u003Cp>Speaker 1: 1 of us has got 2.\u003C\u002Fp>\u003Cp>Speaker 0: Great. ID is not defined. Why? Because ID dot value is gonna be game ID dot value. I'm feeling the heat, but I'm having a blast.\u003C\u002Fp>\u003Cp>Speaker 1: I know. I was gonna say I feel like I gave you, like, the small challenge that became the much larger Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: But not not 145 in or whenever it was. That's not really the time to do it. But if we wait just a moment, nothing happens. Why? Console.\u003C\u002Fp>\u003Cp>Assets undefined 4 3. Fine. Let's figure out oh, image ID. Complete typo there.\u003C\u002Fp>\u003Cp>Speaker 1: Remember this, Kevin. Remember this as the challenge comes next week.\u003C\u002Fp>\u003Cp>Speaker 0: I'm having I'm having a blast. I really enjoyed last week, last episode. And now\u003C\u002Fp>\u003Cp>Speaker 1: we have Hey. What is I? Kevin. Kevin. Kevin.\u003C\u002Fp>\u003Cp>Kevin.\u003C\u002Fp>\u003Cp>Speaker 0: In an ideal world, you can obviously select from your existing games. It will handle all the logic for you. I think we're firmly out of time to do that this time around. Next time we and next time we won't be touching insights. But everyone, I hope you've had a wicked time.\u003C\u002Fp>\u003Cp>Any final remarks, Andrew?\u003C\u002Fp>\u003Cp>Speaker 1: Thank you, Kevin, for taking on the challenge. Thank you all for watching. And be sure to tune in for our next episode of\u003C\u002Fp>\u003Cp>Speaker 0: Ready.\u003C\u002Fp>\u003Cp>Speaker 1: Ready. Set. Set. Battlesnake. Battlesnake.\u003C\u002Fp>\u003Cp>Speaker 0: Nat, when you edit this, can you just time it so we get that right? Thank you. Bye.\u003C\u002Fp>","Hello? Oh, were were you waiting to do the thing? Alright. We can I got you excited? I got you excited. Hello, and welcome to ready Ready. Set. Battlesnake. We are running out of time to nail it, and I I have to be honest. My my, my confidence is starting to wane that we're gonna get it. But hello, everyone. Welcome to episode 3 of 4 for Ready, Set, Battlesnake. My name is Kevin. I work at Directus and My name is Andrew, and I work at Devcycle. Absolutely. And today, we're gonna pick up right where we left off in building a Battlesnake with, Directus and in the next episode with Devcycle. We've both been involved in the Battlesnake project for some time in various ilks. And honestly, we're we're just having a great time, if I may if I may talk for us both. I'm with you. I think this has just been so fun. I haven't dug into Battlesnake in such a long time that this was such an awesome thing to come back into, especially oh, look at that. My light turned off. Especially after, especially after just getting into the the nitty gritty of Directus over the past, like, couple months, getting ready to build some of this stuff out. So, yeah, this has just been it's been so much fun, and I'm excited that we're only halfway through right now. Like, we've got 2 full sessions left, and I think it's gonna be amazing. So am I. So let's recap where we were at just at a high level. So we have a local directors instance running. Inside of that instance, we have a custom endpoint extension. We have a custom endpoint extension because we need to know with confidence that, a snake can exist having a root endpoint and a slash move endpoint at the very least. And the webhook, the webhook triggers for flows can't guarantee that you get a random URL and we need we need both of those relative past. So we created an endpoint and all the endpoint does is it fires off this flow. There's one for the info of our snake here, which gives the information about our snake. Is the font size okay? I can bump it up if necessary. It's okay. I mean, bump it up a little bit. I feel like it's not bad for folks that are watching. Yeah. Yeah. It's good. Yeah. Yeah. Yeah. Yeah. And then our move endpoint, which is what we spent most of our time working on in the last episode. So request comes in with a full board state. We set up some global information from that state. We grab is move safe, up, down, left, right, or true. We grab snakes, board width, board height, my head, and you kind of all of these conveniences. Then we run avoid walls, and we see whether whether the wall's gonna be hit. But what you'll notice is at the top is that we pull in is move safe. We do all the work to it and then all we do is return is move safe. So we're moving this object down the chain kind of manipulating it at each step because that's ultimately all we need. We avoid the walls. We avoid all bodies of all snakes once again, starting with is move safe, ending with is move safe. Then we avoid head to head. This is what we spent most of last session doing once we got the flow running. It might not be the most elegant, but what we do is we look one space around every snake that isn't us, and we avoid those spaces too. Once again, we start with this move safe coming in, and we end with this move safe going out. And finally I love we determine I love that you're saying this is not elegant, Kevin. Like, this is like yours is so much more elegant than mine. As you're gonna find out next week when I jump back in, week 1 week 4 are Andrew, learn learn with Andrew. Specials. Andrew specials. Exactly. So this is such clean code. I love it. Oh, well, thank you very much. I'm sure some of our viewers will disagree and I won't stop them. And then and then finally, we grab that array of is move safe. We basically just pick 1 at random that is still available and we return that as an object move next move, which is either up, down, left or right, and we continue on. And so in this episode, I think we were going to start playing around with not necessarily adding more logic to the snake. I think it's a pretty robust snake right now, but instead, we're gonna start saving game data to this director's project, and then perhaps we'll do something with the insights module. How does that sound? That sounds fantastic, Kevin. That sounds like a great way to spend an afternoon for me slash Wonderful. And afternoon, no? It is now but I realized that people are watching this from everywhere. So maybe it's from morning. Everywhere. Technically, it's it's, we've got afternoon for me, evening for Kevin, and morning for one of our wonderful viewers out. I'm sure somebody is more important for you. Good good morning from 9:30 at night. Okay. So so so I've already had to think about how how this should probably shape up, which is this return move this return move operation here does some work, right? It picks the move and then it returns it. I think we should decouple these. So we should do one final step which picks a move and then the final step which just returns the move. And then we can put in like an inter we could put in one additional operation in which will store data in a database. I think that's probably the, you know, we have to decouple it and then we can we can take in the entire board state from the trigger, store that and we can store the move that we ended up concluding before we send it on. That's that's what I think we should probably do. There is a question of what we want to store. You know, we also have this additional start and end move point. I think end always returns the final state of the board. So there's something to be to be said about that as well. Yeah. What do we what do we want to store? Maybe we start with we we just store the the move. Like, we start really simple. We just say what is the final move we chose? Yeah. I think that's probably easy number 1. I was also gonna say we could record win or loss, like, if we wanna go really simple. But technically, I think move is probably simpler because then otherwise, we're gonna have to parse through the JSON data that we get back. So yeah. Let's do let's do move and then maybe, like status of the game and, and then go from there. Interesting. Let's just let's just go till we can't go anymore. Now just as a note, there is a limitation that we have to work within with directors. Now we actually just did a different directors TV episode on this. It was request review, filtering JSON objects, I think it was called. But, basically, if we store a JSON blob inside of a field in directors, we can't filter inside of that blob. It is just a string. So what we'll need to do is extract specific values and store those as individual values if we want to use them for insights. We just have to be aware of that. Let's let's start by doing what we said we would do, which is adding this extra, splitting this out into 2, and then we can figure out how we're gonna store data. I think that's probably the safest bet. So, let's add a new operation here. Let's call this 1, determine move. I think that feels okay. I think we'll give it the key of move. And I don't know why I've started doing that without having copied out this. So determine move, and this will be a run script. You can copy and paste that in here, except all we're gonna return now is move next. I think all we need to return is the strength next move. That's all this one has to do. And then we're just gonna re kind of configure these. So this one's gonna go here. This one's gonna go here and we hit save. And this keeps happening. Don't know why, but it seems to work if you just kind of what was it? Do it again? Yeah. Because that's what it was. Disconnect it and then save and it should work. Yeah. Like this. We do. Well, we did that. Okay. Cool. Cool. Cool. Cool. Yes. And now we're And then, which feels odd. Now today, there was actually a release of Directus the day that we are recording this, but I'm not feeling brave enough just just in case. Just saying it. So this this may well have been fixed. Do we remember what it was? Because it ain't ain't Don't say these. Bloody words. Is it not working? See. Hang on. Hang on. Okay. What if we go this way? Yeah. Okay. Fine. And now switch it. I think we had to, like, maybe. No. What the heck? This this resolved with this last time. So this is Yeah. But nothing nothing's changed, Andrew. Nothing's changed. Okay. Go back, and, like, remove it from there, save, and then go back, go out, and come back in. You think? I don't think that's what we did, but I wonder if it will No. No. We we definitely didn't do that. We did this and then this and then This is why you should always watch your VOD recording if you're trying to bug some of the buttons. We fixed it. Watch your VOD recording. We did. Oh, no. Okay. Remove the okay. But so let's maybe there is actually an error in there. Maybe there is a new error that we've introduced. Alright. But not nothing new has happened. Right. Value fulfilled resolve in collection, direct to this operation has to be unique. Are move the same? Is determine move and return move? Is it the same key? Is that what it is? Oh, possibly, but I really don't think so. This is no. This is called return move. Nope. Nope. It's a return move. You're right. So it works that way around. Cool. Cool. Cool. Cool. Cool. So so then we do this. And it does It's this. This is this is where there's a problem. I feel like our logic is not going to work. Can we just trigger another what if we just trigger another flow instead of doing it past the data? I know it's not as clean but, like, if we're gonna, like, bug solve even just temporarily, why don't we just send it to another flow? It's a new thing we haven't looked at as well because you can trigger another flow. I don't wanna do that. I don't I I want it to work. I mean, we all want these things to work. Okay. We're gonna pause and and we're going to come back when we have worked it out. What was so wait, what was, what was the you're right. The solution was exactly what you described last time. Are you getting error logs in your console that are different than that? Oh, no. Really? Shit. It's a great way to start a bloody director's TV thing, isn't it? Fantastic. Bugs are what make this feel interesting. K. Oh, what? No. No. No. This this one was the good one. Oh, that's yes. That's the reversed way, so it won't work that way. What have I just done? So this goes into return move. No. We want this one to go into determine move. Determine move goes into return move, save error. An error. Delete that card and create a new one and see if it solves the problem. Call return move. Card. Oh, doesn't like that. Yeah. Just return. There we go. Same thing. Director's operation. What is okay. Can we see that that field go into the data model? Can we not see that operations logic? I mean, I mean, yeah, I guess. But, like, we're looking at quite, like, low level, like, built in system. Okay. Cool. There is an error. Copy to action. This is what Riker's just messaged. Hang on. Did it work? No. Okay. I'm telling him what happened. Right. I meant I'm talking to the lead maintainer. Node 13 were. I made node 2 then changed the nodules. And okay. Fuck. Fuck. This is the way to this is definitely what what what was meant to happen this week. Is it okay. What if we run a simple script? Like, just detach that new one that you just created. Add any random script after the determined move. Okay. So we need to create a new one first of all. Right? Yes. Yes. It's great. Do will return data. Great. Right? It's like a pass through. It does nothing. Is it the script? What if we do the same thing with the I Yeah. If we duplicate this script, can you duplicate a flow? Not a whole flow. Oh, yeah. Flow? Yeah. I wonder if duplicating the flow might solve this issue. Duplicating the flow will mean we have to reconfigure the extension. But Well, I mean, let's let's see let's see if it works. Well, yeah. Well, I don't know why I'm doing that. So return. Okay. And we'll copy out. Blows. And then we can Oh, no. I actually can't duplicate 1. You could, once upon a time. No. It's insights. That's insights. Good to know. Fucking Here we are. Now we'll figure this out. We got this. I have I have faith in us. Okay. So our regulars our regular script our no. Our regular script was working. Okay. Skip just to the end. So, like, remove the the head to head and go straight from avoid body to the very end. So skip over the avoid head to head because this is where we started to get these issues. No. It's the final card that's fucking this up. No matter what it is. It's reassign. It worked. Did it work? It worked. Don't question it. Okay. It worked. We did it. What what was our solution, Kevin? We just tried many times and then waited to see what happened. Just wait until I cause many problems to my team. Okay. On our screen. Okay. Alright. Gonna resume recording. Thanks for the click. We got there. We did it. Go team. Go, Kevin. Okay. Hubert, you're welcome. Alright. Ready to come back? We're ready. Let's do it. Okay. And welcome back. We fixed it. We don't know how we fixed it. It just started working again. We will undoubtedly come across this again. Having spoken to like our lead maintainer, he thinks it's a bug, which I will open a GitHub issue on after this recording. Okay. Wicked. So now determining the move and then returning the move. Okay, cool. So we determine the move, which just returns the next move. And then all we want to do here, the literally the only thing we want to do here is return last. Right? Data no. Last. Last? Yeah. Just last. Yeah. I think just last. Date data dot data dot last. Is it? Alright. And the reason for this, if if nobody realizes this already, is because, like, you have to feedback this very specific JSON structure into the response that that Battlesnake precedes. Which kinda sucks. It just seems like, it should. It should just read this the engine should just get it. It should understand at a core level. If you're if you're being told to implement an API specifically, it's fine to have to do it specifically, but we'll test it works. And in theory, we should be able to in theory, we should be able to add another another flow operation here to do logging. Any logging we want at the end of the whole run. So let's manage our battlesnakes. Let's create a game where I'd already set a battlesnake and a hungry bot an 11 by 11 board, and we'll start the game. And that should be running game in progress. I can kind of see my logs over here that the game is still happening. I'll wait for the game to conclude, which it has not yet. You know what? I've run out of patience. Oh, no. It happened. So let's hit. There we go. Excellent. It did end up doing itself in, so there is something wrong, but we're not we're not looking into that bit right now. It worked. That's the important thing. It it worked for some period. Anyway, that's all so it is it is talking successfully, to directors, which is useful. Now what we now wanna do is figure out what we need to log. And before we do this, let's create a collection for it. So we'll go to our data model, we'll create a collection and what are we going to log? I don't think we should log every turn. I mean, we could log every turn. Well, I I mean, we could. I think I don't think it's necessary either. I think let's let's let's right now just log the final move just as like a getting started place. We're just gonna pass that data right through. And then let's do at like, ultimately, we'll end up adding something around, win or loss, because I think there's something cool visualizing that. And then I think there's honestly, I'd be love to explore us actually pulling the game GIF from this, which I think is more logic, but I think it's very interesting should be pretty straightforward. So if we want to log this, if we wanna log the the game, we need to be doing this actually in the end endpoint. Right? We don't want to be doing this in the move endpoint. We want to do it in the endgame. So this actually means we need to a little bit more fiddling, a little bit more fiddling with directors. And to the dismay of our lovely editor, I'm actually gonna unshare and reshare, but I'm gonna do my whole screen this time because No camera job. It's too late. I'm doing it. It's done. It's gone. It's done. Farewell. It's it's gone. Farewell screen. Okay. Apologies for for for that. But what we have here I'm gonna just reconfigure also. See every day, Kevin, by the way? Is this your screen, this large massive thing? It's a 49 inch ultrawide monitor, which will feature in a future episode of What's in My Doc because the final episode of the season is yours truly. We talk about it more then. So if I just make this bigger, we want to we have this move endpoint here. We can delete all of this logic that was left actually. But we have let me just shrink this down a bit. Just a reminder, when we hit the root endpoint, we're gonna hit this flow. When we hit the move endpoint, we're gonna hit this flow, the start and there's end. So what we're gonna do is basically add this logic to to end. So what we're gonna do is, kind of similar to the info end point. You know? The info end point. Yeah. Yep. Yep. Same thing. Yeah. Although that is a get endpoint. We need a post endpoint. So it's more similar to move, I guess. But what we'll do is we'll create a new one. We'll call this snake, end. This can be based on a web hook trigger. It will be a post request. It is a post request. Right? I mean, we'll find out together. I'm pretty sure it's a post request. Yeah. We we will, but I'm also gonna I'm also gonna double check. Look at the documentation. Probably smart idea. Look at the documentation. Not there though. Webhooks. Here we are. Game over. It's a post request as an error hit, by the way. It's it's not formatted as a code block, but it's a post request to slash end. It will be the game object, the final turn number, which is useful, the board describing the initial state of the game board. That doesn't feel correct. Oh, on that final turn, I guess. And you describing your battle snake. And I wonder if there's a death reason, but we can find out. So response body date of last operation. Great. And first thing we're gonna do is just copy this URL. I'll hit save. We'll copy this URL and we will go ahead here and const flow response. I'm not sure we need to, respond to this, but we will Await fetch full URL. We're basically just gonna copy and paste the all of these options because they remain the same. And then we're just gonna send okay. We don't need to do like we actually don't care about the flow response. We're just gonna send all the data by fetch and then send okay. We just need the data. We don't and eventually to respond. So we'll save that. Hear that flow response? We don't need you. We don't need you. Yeah. So inside of our extensions, director snake, we'll run npm run build. Oh, in today's release of Directus, we squash the bug that stopped the autoreloading working. So That's awesome. Nice. Was that because of us? No. Not even remotely. Well, I'd like to think it is. 10.9. 10.9 came out yesterday, so it resolved that. So now we have our new extension. With this new endpoint, we don't need to do anything more there. Back over to our code editor here. I suppose we can just we can actually just leave this be. Yeah. And we can Yeah. Let's just log, see if it shows before. It. Yeah. Exactly. Create rematch. So that whole game is is running. We see that 200 all the way. Really low really low, response. That's great. Running, running, running. And then we now no longer, we remove the console log for end. Oh, this is on start. Have we done it in the wrong place? Everyone, we've done it in the wrong place. I know. Amateur hour. But it is logging it, so we know what we know what the shape would be. Yep. So we can do that instead. We'll make this async. We'll remove async here. We will go and rebuild the extension, and then we'll restart directives. And while that's kinda happening, we can we can take a look at this object here. So we have game, which is an ID, which we'll probably wanna store. The rule set perhaps, probably not for this one, The the final turn, 95. Don't know what we wanna know about the board probably because 95, it's the turn where you're dead. So that's that array will not include us or it will include us and we'll be the last one standing, I guess. You has health. So I wonder oh, maybe we won this one. Maybe we won this one. Let's, let's watch it. Have a sneaking suspicion we did win. Yeah. So wait. How did we how do does it show that for us in that result? Like, it doesn't it doesn't say, like, in our in the law No. No. But I I I wondered why is it still giving me a health of 90? Why did it give me a health of whatever it was, 83? Smart. I have a sneaking suspicion if you die, the payload looks different, but there's only one way to test it, which is I've now restarted Directus. If I refresh this, there should be some logs. So this was the log of the one we just accidentally ran when we shouldn't have. So this is the start. So we'll just forget that one. Let's, let's do a rematch. And now we should only get the response when when it dies. So we can see this. We see it's posting slash snake slash move. And the last thing it should do is post slash snake slash end and it should hit a different trigger. So we'll just watch that happening and this is happening real quick. Wonderful. And that is what happened. It had a different flow at the end slash snake slash end. So if we go back to our flow now and refresh, we should be able to look at the logs. Payload. Okay. So this oh, did we win or or lose? That's probably worth knowing ahead of time. You won again. Come on, ready, set, battle snake. What are you doing winning all the time? You're too good. You're too good. Too good. So 73 turn 73. That is us, ready, set, battlesnake. We know our ID. I wonder if there's something in here where we log what our own Battlesnake is, but it may not be necessary. Let let's keep looking. It has health of 92. It has a length of 6, which could be interesting. We don't need to know what all the food is, where all the hazards are. You is just a convenience again. Alright. Let's, let's literally just keep creating rematches till we die. We'll let this run-in real time which means we we're gonna have to wait a hot minute as every move happens. Are we too Lindsay. Are we too good at programming Battlesnakes that he will live forever? Is that are we gonna need to are we gonna need to, pause? Artificially. I'm gonna have to artificially make him just, like, die. Come on. I think we oh my goodness. Come on. We gotta do it again. Gotta play again. Okay. Okay. Come on. 3rd time's the charm. We did, we did too much, Kevin. We went too hard. That, game in progress thing is not behaving, so we'll restart a game this way. I mean, if I just add ourselves, eventually, we'll die. Oh, there you go. Yeah. And it's interesting too. So, I think part of the game in progress thing might be I know I know Battle Sneak scaled down the server load, so it may be something related to that. Maybe. But, yes, we should live for we're gonna live forever. That's really what you've just done here. You're about to, you're about to kill your local version of Directus just because this will run forever. Hey. Good time. Alright. I immediately got it right and wrong. Thank you. Thank you. I reset Battlesnake. Alright. I mean, eventually, we had to lose. Right? So let's look at the final log when we lose. And I have a hunch. It is that our battlesnake does have a health of 97. So there is a health in there. So that isn't how we determine whether we live or die, unfortunately. We have a board. We have you. Although, where are the oh, the snakes array is empty. So we are not so we are there. Sorry. But we are not present in the snakes array. Oh, okay. That makes sense. So if the snake array is empty, we didn't win. If we are not empty if we are not in it. Yes. If we are not in it. So the easy the easy thing to do here is we have a we know our snake ID. Here it is. We could store it in a collection of our snakes to like factor out or we could just hard code it in. Well, let's let's let's have some fun. Let's let's do the more challenging Sure. More challenging. Snakes. And then, actually, if we look at the if we look at the API, I don't think there is a a meta Battlesnake API is there to get information about games and stuff. No, that's fine. What else should what there could be. Yeah. What if there is there's a start endpoint. I wonder if no, it's fine. We'll we'll we'll hard code this snakes. All we care about snakes is that they have a manually entered string as the ID and we can add all the others later. So if we go to our snakes, we'll put in the ID. That's ours. And you know what? We'll just add a name to it just so we can tell what it is. Right? So, this battle snake is ready, set, battlescape, I actually think is its actual name because we wanted to distinguish the one you made to the one I made. And my name is Elsford k. So it's registered Battlescape. Okay. Cool. So we've got that. So what are we gonna do inside of this end flow? First thing we need to do, I suppose, is we get this whole payload. The only thing we wanna enrich it with right now is are we alive? So Yeah. Is alive. Did live. Did I live? And that's going to run scripts. No, it isn't. First of all, we're gonna get all of our snakes. That's what we should do. Read items from the database. So, get snakes. Permissions from the from trigger. I'm just gonna give it full access so we don't need to contend with permissions. Snakes. If I don't add any IDs, do I get them all? We'll find out. We gotta find out. This is trial and error, Kevin. This is trial and error. Real, real trial and error thing going on here. Alright. Let's create a rematch with ourselves. I feel like I should add another snake back in just to end the games quicker. So do it. Yeah. Do it. Add another snake in. Just go back and make sure I'll let I'll let this game conclude. We'll do it until the next time we do it. Still running. By the way, it looks like games. Oh, thank you. I actually was really trying to find, like, the end of Loki season 2, all of the timelines. I'm not sure if you've seen it. And so it was kind of Yes, I've probably seen it. I've seen it's going for. Yeah. It was kind of that vibe I was going for. Okay. So obviously we eventually die. That's just the nature of playing by yourself. Let's just see how they perform now. Okay. Again, no idea why. Really shouldn't be doing that. But Don't question it. We wanted that to happen. We willed it into being. We willed it to happen. We want you to be chitter. Alright. There there's all of our snakes. So that's great. So that that returns an array of objects which contain IDs which are our snakes. So the next thing we want to do then is did live, like, you know, did live. And we will run a script here. And all we wanna determine here is, what did we call the last one? Snakes. So data dot snakes is the array of snakes and we want to know and we want to know did the trigger dot I need to I don't want I don't wanna close it. Don't wanna close it to to take a look. Let's look at the docs. Webhooks. So it will be trigger dot, I think, payload. I think so. Yeah. Yeah. We can always fix that later. The payload.board.snakes. So we wanna see, did that contain that? So, JavaScript, does array contain other does array contain specific item? I think it's like includes yeah. Does array contain item from other? Right? I'm pretty sure we just did this in our last episode. I think so. Yeah. Some. And I think I used find instead. So, live, which is like the if it lives, it will be found. So we'll say is inside of data dot snakes, is does that contain yes. Does that contain does data.snakes contain? And this is where it gets a bit honky, and I need to just just have a moment to think. So we wanna know out of all of the items in the snake array of which of which we know the I the ID is gonna be snake dot it. We know that already. Does is snake ID equal or does snake ID I got it. Does snake ID feature inside of this array? So what we actually wanna do is return whether this array, the one with all the snakes on the board contains, which I think we've determined is includes includes. Yep. Is it includes? I think we need to simp I think we need to do one more step beforehand which is take this big array here and just simplify it down to IDs. So, const IDs, IDs on board. So we'll take all of that and we'll map it and all we wanna do is turn that into an array of IDs. So it's just IDs. It's nothing else but IDs. Forget all the other information. That's all of the boards. We don't That's all of the IDs of the snake on the board. Exactly. And then we will return whether or not IDs on board that's it. IDs on board includes any of our snakes IDs and we will return to live. So true force, true false, did it. Really? It's gonna work. First time's the charm. Oh, no. I've done it again. I've hit create rematch, But eventually the answer will be false, right, this time with confidence. It should be. Or it'll just live forever and this will be this will be our time. Okay. Hit its hit its own tail. Mounted itself into a corner and did itself in. So let's, look at the last log. Cannot read properties of undefined. Okay. Cool. And it was body, by the way. It was trigger dot body. So that will probably be why. Trigger.body.board.snakes.map. So let's, let's just check that. Body dot board dot snakes. Cool. So we will create rematch. And now second time. And now. Go, red snappettlesnake. So stop eating food. Stop eating food. As it gets towards the food, I'm like, no. Come on. What are you doing? We don't want you to live anymore. What are you why? Why are you doing this? This battle snake is not bad apart from those times it randomly goes off the edge of the board, and we don't know why. I think it's just got a it's got a it's got a spirit of its own. Oh, what's happened there? Oh, there we go. Oh, okay. There was some real They were clearly in a queue or something. Goodness. Wow. We're doing I Oh, there we go. Why did you do that? I don't know. Oh, yeah. Yeah. Yeah. Right. It knocked itself into a into that kind of spot where where it can't. There is no viable move. Right. So we'll look at this. Cannot read properties of undefined. Okay. Okay. What is undefined? What's undefined is what we're running find on. So let's take a look at that. We're running find on data dot snakes. Okay? Except oh, because we call it get snakes. Fair. We'll call that snakes. Great rematch. This is the only thing when you'll see ready. Set up the snake. Sorry. This is the only thing when you're debugging. Go on. And you were saying something valuable as part of this conversation. I was trying to add some color. Wait. You there's there is just a bit of waiting here. Right? I suppose because the rules engine is open source. I imagine people I think people have built, like, line emulators that can obviously execute this way, way, way quicker for the sake of debugging. And I see the value in those tools. Yeah. You can actually do it in the CLI. In the CLI, you can actually play full games and see them. Because we just had to weigh a 153 turns. Alright. Trigger is not defined. Still. Okay. Trigger is and you know what? Because it's data dot trigger. So many amateur amateur hour little mistakes today. Never. Never, Kevin. Amateur is, is a term that we need to take back as a positive As amateurs. As amateur. As amateurs, yeah. Alright. Come on, ready set battle snake. You can die. Don't survive. Please don't. I believe in I I don't believe in you. I feel like he thrives. So the weather so the weather today. Yeah. Oh. Oh. Is that is this it? No. No. It it will lit. It's just obviously saving up moves. Yeah. There we go. Woah. It's like a taunt. That was like a taunt to us. Come on. We're doing exactly as we the thing is we made we made this snake. We made this snake. I feel like we is a very, oh, there we go. Alright. He made himself, really. Alright. Let's see if this worked this time. What do our logs say? Hey. We got data. Yeah. But this isn't what we want. What do you mean? What we want. I don't know, but I just want, I just want, I just want a true false value so something's not right. But wait So, yeah, go on. Well, I'm just like, what are we returning? Sorry. It's the text is really small. Not because of your screen. It's because We are returning, the whole, like, body of the script, which feels a little bit weird. And I have a hunch. I have a hunch. It's oh, no. Did live. This is live. So what's going on? What's going on there? Super weird. That shouldn't be returning that. No. It should not. Let's, look at our previous flow, the move one, and just maybe not, like just take a little bit of yeah. I mean, if we take all of this, and I'm just doing this as a comparison point just so we can kind of dump it in here and take a look kind of side by side, if you will. Yeah. Data dot trigger, Return. We do just return a value. Maybe, let's take a look at, array find method. Let's just understand this a little bit better. This returns the 1st element in the provided array that satisfies the provided testing condition. If you need an index, you can use find index. If you need to find the value, if you need to find if a value exists, you can use includes, which actually I think is what we want because that just returns a true or false. So maybe we just swap this out for an includes, includes, And we wanna know yeah. It's like a nested includes. Fine. And then we return I'm just I'm a little bit confused why it was returning Yeah. The whole code. Well, we no. You know what? We also don't get here. We don't get a returned payload, which makes me think there was some kind of error happening here that wasn't being captured correctly. Is there anything in here? I wouldn't imagine there is, and it's now really hard to tell. Let's, let's create a game. Let's let's make this game a lot, a lot quicker to end. Hey. There we go. Well, do you see how quick that was? That was fast. We are smarter than hungry. Niamis. Come on. What are you doing, Raze at Battlesnake? We want you to not die. We just want you to die. We might we might have to nip out some of the logic or something. I've never had a Well, well, that will that that will be the end. That will be the last move when it finally catches up with that with with Toast. Yeah. Snake end. The game seems to be struggling with the concept of ending, but it has ended. Alright. Yeah. Look. We get the options here, but we don't get the payload. Because if you look at the other flows, for example, wow, 1632. Wow. You see that all of these run scripts. I know they have the payloads. They have the options and they have a payload. Options payload, determine move interestingly interestingly, does does not have a returned payload. Is there something about them returning objects and strings versus just returning like a primitive. Maybe that's what it is. So if I so if I because this could be fine, you know, return. That's returning an object. Right? Mhmm. Let's create a rematch. Okay. Our snake just went up and up again. Hey. We don't know why happened. Bad things happened. We changed we changed the code. Shouldn't have done that. Changed the code. Shouldn't have done oh, there we are. Did live force. Okay. Awesome. Cool. So so there was actually no problem. Just an interesting note there that for it to show up in the payload, it needs to be an object or an array, I presume. So just a good just a good note there. I would love to play a game where we don't lose. So we can just so we can just check both sides of that equation and that they make sense. Let's create small boards. If we play with ourselves It wouldn't then technically, we'll always win and always win. Oh, well, yeah. But then I think it's just gonna get a little bit weird. There we are. Hey. We no. Wait. That's not what we want. That's what we want. But We've wanted to lose so much that now to win seems like a wrong thing. So did live should be forced. Yeah. Live should be forced. Let's create a rematch. Maybe we need to put in a a a a less clever snake. Goodness. Okay. Try Do you want the right bot? Yeah. Yeah? We we haven't we haven't gotta live long to make that work. Okay. Great. So now if we refresh, we look at the logs. Did live hopefully should be False. Absolutely. That's totally what we were wanting to have happen. Great. Is this really what we're gonna spend this episode doing? Okay. Fine. That's it. Just debugging JavaScript. That's really what we're here for. Right. All we want to do is now maybe maybe we just roll back for a sec. We're doing all of this grab snakes thing. I don't think this is adding I've deleted it. I've just done it. I've just done it. I've been busy for decision. Exec executive decision. All we care about is this ID. Right? So let's just we'll we'll delete this. We're gonna hard code it in now. Okay. Yeah. Yeah. Like, I don't think it's worth, like, going at all costs. Right? So all we want to know is does data dot snake say const? In fact, I think we can say, does snake include data dot trigger dot body dot board dot snakes dot map. Sure. So that's just a set of IDs. And all we wanna know is, does IDs onboard include this? And I'm I'm assuming can I console log inside of these? Best bit of, debugging we got. Great rematch. Well, I think our snake's broken. I think we spoke too soon about the capability of our snake. In it doesn't like includes. Payload.body. Right. Hang on. It doesn't like includes, which is this one here. And we don't get a log, annoyingly. So all we care about, Let's, you know what? I'm actually gonna be bold here and just remove this, and we can start we can start again. So so all we wanna know is did it live? And to know if it lives, we're gonna go through oh, no. I do need to I need this too. I like it. I like the I like that you were confident in the removal, but also truly confident. That I you know, I really wasn't. So all you wanna know is, does data dot trigger dot body dot board dot snakes, does that include includes includes let's take a look. We need to give it a specific value here. So I think what we'll first do is we'll say snake IDs on board. We'll take this and we'll map it once again. So we just extract the IDs. Then all we wanna do is check whether snake IDs on board includes this value here. It's all we wanna know. Please work. Create rematch. We've got this. Okay. Our snake just keeps going up by the way. We fundamentally broken something. But I mean, these things happen. These things happen. We'll figure it out. We've got Live force. The rest of today. Live force. Okay. And we did we did we did live force. Yes. That is that is true. And all we need to do is outlive the snake, which we did, and hopefully live is true in this context. We may need to go back as as we commented in Okay. But we lived, but we lived. Let's let's take a look at the payload. Let's take a look at the payload. So body dot game, no. Body dot board dot snakes has ready set battlesnake present, and that's it. The snakes have an ID. That's our ID. That is I'm just gonna double check. Yep. The ID, nope. Not the ID we've been pasting in. What does it get a different does it well, it's different. So I'm wondering, look at this ID, w d at the end. If I look one game back, it gets a different ID every time, Andrew. So we gotta pull name then, I guess. I guess the ID is not I'm literally so mad. Yes. Fine. As well, you should be. Damn you, battle stake and your your ID that is not truly the snake ID. Who do you It does feel that way. I can't believe we sunk so much time into that. Okay. Cool. No. Right. Create custom game. Create rematch. Okay. We lost because apparently we just go up now. Yeah. Liveforce. Cool. And create rematch. Yeah. I mean, we lost again. Sure. Because that's what's happening now. What is going on with that, by the way? What is going on with that? Is it is it work. Are we struggling under the number of logs that are being generated? No. I mean, possible. Look, avoid head to head force. So there's something in the avoid head to head. Oh, god. Because there was no head to head issue here. No. There wasn't. Let's create rematch just until we get a a seed. I think, yeah, we both die at the same time there. Let's create rematch. Let's just create a seed just to test out this logic. Yeah. That's good. Nope. Not good. Okay. Create game. Okay. We we broke it. We broke it. Okay. We are still not gonna do well this oh, what is going on? There we go. Is it there someone is there something is there something about we did? Is there something about the board size being different that's throwing it off? In which case, I'm quite happy to say how Battlesnake only operates in 11. That's the right answer. Don't make it work on all the boards. Only standard class. True. Okay. Hey. There we go. Finally know whether it lives. Now it's all about logging information. So let's look at this payload and see what we have. So we have a game ID, which I think feels like a no brainer. Right? Yeah. And you said we can do something with the ID to get the GIF? I believe so. I'm gonna go check and see something right now. I'm gonna go over while you're doing this, I'm gonna go over to my other screen. But, yes, we'll pull back so that we need that. We know the turn of the final move, which is 2. Yep. Right. So that's the term where we were victorious. We know that we lived. We know our length, I suppose. Yeah. I think so. Yeah. And maybe we do maybe we start with just games. Games get an ID, which can be a manually entered string because we'll we'll pop that in from the API. And I think all we need here is win or lose, right? Win. That's a Boolean. We wanna know what about it. Like, a turn, I suppose. That can be an integer. I mean, game mode? Turns. Yep. Turns. Turns feels better. Yep. Turns. Into game mode, but we're not playing around with other game modes. So I just think That's true. Yeah. It's fair. Yeah. But but the idea is, in theory, you could you could store all of this. Wind turns. What else have we got at our disposal? Let's take another look at our end state here. We have live die. We had turns. We can start with how many snakes there were on the board from the trigger. So snakes dot so trigger trigger body board snakes length. Just quickly gonna go, store. We're gonna prepare data. So trigger dot body dot board, did we say it was? Dot snakes.length. So I'm just gonna pop that in there for later. Yeah. So snakes, that can be another integer. What what else what else do we wanna know? I mean, let's let's let's stick it with that. Good because we you could do, like, a win ratio. You could do, like, average turns per game. Like there's lots you can do here. Do you continue getting data if you are not the last to loot? Like if you die and it goes on a few more steps, do you only get the end state on your final turn or do you like, let's take a look. Your battle snake will whenever a game it has playing has been ended, use it to learn how you battle snake won or loss and deallocate any server side resources. Okay. This was like a good starting point. So this is what a game looks like. ID win turns snakes. So going back to flows, let's, do our end. So did live. We're gonna prepare data. So the only extra thing we wanted to know here was snakes. Snake count. What else do we wanna know here? We we might as well get it already to be fair. Might as well just do it straight within here. So snake count. Is this. We have we had snake count. We had turns. So once again, let's just take a look at the it was game. I think it's game dot turn. Game? Maybe not. Bored? Bored? Bored? Hold on. No. Am I using it? Oh, it's right here. At the root, turn, integer. So that should just be trigger.body.turn. And we wanna know we wanna tell how many things. We wanna tell how many turns. We wanna tell whether we lived or died that we needed the ID. ID. So trigger dot body dot the ID. I think it's just I game dot ID, I think. We'll find That sounds right. Game dot ID. Yeah. Nice. And while we're here, let's, let's just also get the did live. So live. Live? What did what did we call it in our director's collection? Lip libs? Let's take a look. Was just libs? It's called win, actually. Win. Alright. Smart. Win. So, oh, so in here, we'll go prepare data. We'll go live, but it's called win here. Live, live. Fine. Whatever. So we have, I really don't like using last or I can help it because it allows you to obviously move things around without your data becoming like brittle. So I'm gonna pop did live. And all of these have to be prepended with data. Okay. So in theory, that's the preparation of the data. Let's, let's run a custom game one more time. If our snake performs well, it will have been the 7 by 7 board that did it in. Well, I think I think oh, of course, we've just got let's create the game with the ready set battle snake and the hungry bot now. Yeah. Weird, isn't it? Anyway That's such a good idea. I wonder what we did. Don't don't wanna know. No. That's a question for, for season 2. That was a question. Set back. That was a question for last week of and now it's this week of do you see how big that snake's getting? Oh, of course, it's hungry, but isn't it? That's its whole that's its whole shtick. Are we gonna win this one? Oh, we won it still. Yes. Legit did. So Well if we refresh here and look at our log payload, snake count 1. This is wrong. So we need to just look at this because we wanna know how many snakes were in the whole game, which in theory should always be no. It won't always be this. End. We're pulling from end, so it's only gonna pull the last snake. Right? Is that what we that's what the Yeah. Maybe we maybe we need to do it on start. We need to create it. And then on end, we need to update it. We have turns, we have the ID and we have whether we want, which is true. So maybe for snake count, because I do think snake count is interesting. We should create just create the data. What do you think? Or do you think it's too heavy handed to create a whole flow for that? I mean, I don't think we need a whole flow for it. That seems like a bit of a change. I mean, we need we need a flow for it. That's the truth. So may I mean, we we've we've got pretty we've got pretty proficient at it now. So what we're gonna do is create a flow. Yeah. Yeah. We'll call it snake start, Right? And so what we're gonna do here is we're gonna do this as a webhook. It'll be a post request. We'll go like this. Just double check the API reference webhooks. Your request to this your response to this request will be ignored. We don't need to care about our response. We'll save this, grab this URL, and then we are going to take this whole end here. And on the start, we'll make that async. We'll paste that in there, except this time we will change the URL to this 1, 704. So that start, we need to just kill this, go here, rebuild our extension, Done. Go here and Docker Compose up. Wonderful. Should be able to refresh. Should be able to refresh. Now I think it just took a moment just a moment to spin up there. And what are we gonna do here? We're first gonna save. We're gonna go and create a game. This will need we don't even need to wait for the game to pan out in order to look at this one log. All we care about is I suppose number of snakes here. So trigger, body, board snakes. Trigger body board snakes. So what we'll do here is create sorry? Board trigger body snakes. No. Trigger. Shouldn't be Stop it. Shouldn't be Stop it. I shouldn't do that. I'm sorry. Create game. We will create data in the game's collection with full access. And all we're gonna do here is we're gonna say the snakes is equal to. Trigger body board snakes. I got it. I got it. They need to be in quotes because it's Jason. Trigger Jason or Jason? I I am a JSON. Jason. There is one there is actually, extract snake count. We just need to get the value out in the previous step. I don't strictly think you need to do this, but just to keep it really clean, what we're gonna do here is we're gonna return snake count. Snakes is equal to data dot trigger dot body dot board dot snakes dot length. Snakes. Copy that key. And then the next one, we will create data. The name and key doesn't really matter because it's our last step here. All we care about here is we need to create an ID at the time. Oh, but we can create the ID as well. We can get the ID as well at the time of creation. So snakes is going to be equal to, this snake count dot snakes. And I actually think we can ignore this error. There seems to want a string, and I think that's fine even if it's, an int just to stop it moaning. And then we also want the ID, which is going to be oh, oh, throwing my cursor around. Here we are. Trigger dot body dot game dot id. So if I save this, we create a new game. Oh, got a little Game in progress. I don't think it is. Our snake 1, thank you very much, but we will create a new game. That's fine. Start game. We immediately should get a new log here and we got a trigger but nothing else happened. Why? Oh, no. There we are. Create data. Type error cannot read properties of undefined snakes 2 body trigger body game ID. Okay. Cannot read properties of undefined reading starts with. Interesting. This looks Looks good. Looks like what we want. Snakes and ID. Is that what we called them? Snakes and ID. What is going on? So star. Shrek snake count. Because in theory, this is looking oh, pick collection. That'll do it for the access. Not telling it to do anything. Alright. Create game. I've got really you get really proficient at starting new games with Battlesnake, don't you, when debugging? You do. Alright. Did it work? Oh, there we go. Now how That's the idea of a new item, item, which means we now have a new snake. Wonderful. So now going back to our end state, we don't actually want to so let's refresh ourselves. Prepare data, prepares a snake count, turns, ID and win. We do not want a snake count. We do not want a snake count. So when we prepare data, we can remove snake count and that leaves us with turns, ID, and win. Yep. Turns, ID, and win. Turns ID and win. And, actually, we don't even want the ID here. No. We we do want the ID because we wanna be able to pull that GIF eventually. But we stored it. It already exists. Oh, that's right. Yes. You're right. You're absolutely right. This is what we're gonna This is what we're gonna so all we're gonna do here is now update the data Lovely. In the game's collection with full access with this ID, trigger body game ID, and the payload, I think we might just be able to put in lost and I think that's gonna be equal. I'm not a 100% sure, but I think that's gonna be equal to the object. So we'll see. Alright. The first thing we'll do is create a new game, and then I think we'll be ready to jump into insights and see how that works for us. So here's our snake performing well in these apparently quite controlled conditions of an 11 by 11 board with 1 other snake. Okay. Come on. Some something happened now. Come on, hungry. You don't wanna survive. You're not really that hungry anymore. It could be all all one either. You both suck. Hey. Hey. So first thing to do is let's see. So you will notice nothing happened there. I've also, to be fair, noticed we don't have any we just have IDs. We probably do also wanna capture, like, the date, like like the now date that's happening because, we just wanna be able to sort the latest item if possible. Yeah. That's fair. So we'll create a date time, finished. We can call this. I think this will work. Date time. Sure. And then at the well, firstly, that didn't work. So we don't wanna spend too too much time on this, but we'll prepare the data here and we'll say what was the name of the field? Do you remember? Oh, it was, we didn't call it datetime. We set it today. No. Now? When? I'm not sure. Jeez. That that's terrible. Both have forgotten Finished. The minutes of creating it. I mean, same. Same to be fair. Finished. We also wanna store finished, which I think you can just say new date, new date dot now. Is that how you do it in JavaScript? Your guess is as good as mine, sir. Yeah. New date dot now. Cool. But it didn't work. So as much as this is cool, what happened here? We updated it. We updated it with the key. Oh, do you wanna know what didn't happen? It isn't showing here that we had a payload. So I think there was an issue with the payload. Oh, look, the payload didn't save. Interesting. So I'm just gonna double check that that is what happened there. If I hit save and then step back in, maybe it shouldn't let me do that, but this is okay. We're not gonna lose much sleep over it. Finished is going to be, no, last dot finished. Then we had, win is going to be last dot win. We'll wrap that in quotes too. What was the third one? What what are the 2 that we have now? Win, turn Yeah. Turns. I believe. Like a living rubber duck. Last stop turns. Turns. I'll hit save before we, you know, spend time on a full snake. We'll take a look. Turns and win. Turns and win. And now we have finished. Turns, win, finished. Turns, win, finished. Great. So let's save all of that. Let's create a rematch. We should see, hopefully, that this is stored. And then I don't think we're gonna spend much more time capturing data. Instead, we'll run a few games, and then we'll try some insights. Nothing's happening still. So still something done in our logic. Let's look at our latest I'll just refresh it. Let's look at our latest log. Oh, intermediate value dot now is not a function. I actually know why this is the case, and it is because, these run script operations running what are known as JavaScript isolates, which are these incredibly limited JavaScript like runtimes that only contain pure JavaScript objects. Let's actually take a quick look at that because I think it's interesting. Isolut. Yeah. This page looks legit. That's not what I want. I do kinda wanna show you like pure Java script or, objects. This is what I'm looking for. What I'm looking for is just like, where is the list? Am I am I what's happening? You might be imagining. You might be imagining this. You may have already used JavaScript. I just I have imagined it. Created it. Maybe you created. There's no documentation. What did you call them again? Isolates. Isolates. Interesting. I've never heard of that. Isolates. And now is that just a security thing within Directus to ensure that, like, bad actors So this So this actually happened in version 10.6. We had I think this is quite what I want. We had prior to version 10.6, you could build, you could use NPM modules inside of extensions. Extensions. Well, video has gone a bit funny. I'm not sure if the is fine now. It resolved itself. Anyway, and the library which we use to isolate flows from each other and operations from each other basically became insecure. Like, it was it was became known it was insecure, and so we had to remove it. And in doing that, we replaced it. And now it now it runs what are known as isolates. And this is what I'm looking for, I think. 1st class. Nope. I'm looking for oh god. It's really bothering me, but I will take a moment to find it. What's is it in our docs? In breaking changes 10.6, we dropped support for custom npm modules in the run script operation. Prior to this release, directors relied on VM 2 to run codes in the run script operations and flows. VM 2 is now unmaintained with a critical security issue that could potentially allow for code to escape the sandbox and access the machine, which hosts directors. So we've migrated to isolated VM to allow flows to run safely. This basically means you can't use just arbitrary NPM packages in flows anymore. You can still turn them into custom operations, but it isn't isn't here anyway. I learned a lot in during the process of this. There are lots of things you use in JavaScript, which you think are part of JavaScript, things like set interval, things like console log, set time out, fetch, that you just think, oh, this is part of JavaScript, but it's not part of JavaScript. It's either part of the browser's implementation of JavaScript or Node JS' implementation of JavaScript, many of which are shared. Isolates don't have this. Isolates just have pure JavaScript functionality. And the I I what I was trying to find was the list of what that meant. I've I can't find the word off the top of my standard objects. Boom. Found it. Thank you. So this chapter documents all of JavaScript standard built in objects. So you have access to global this infinity, not a number undefined. You have these function properties only. You have some fundamental objects like object function, Boolean symbol, a few error objects, a few number and dates, a couple of text processing functions, a few on arrays, key collection structured data and so on. What you'll notice here is a lack of console logs. What you'll notice here is a lack of date, which is what I was which is what I was going for there. We're not gonna lose any sleep over this. I'm literally just gonna we'll remove finished. It just isn't worth the time right now. You know, I do like it though. I wonder if can I add can I add after the fact a date created? Or did that have to be done at the time of Based on my I think it might have had to be done at Yeah. I just scroll down. Though. Exactly. And then just flip it over. I'm just taking a look. There's a sort field, which is great. That's okay. Take a little take a little cheeky screenshot of this so I can refer back to it. There we go. I'll put that off at the site, and then we'll delete this collection. I there might be a way to do it, but I I don't have the the patience. If I see But what I want is date created because I think that would be interesting. That's all. I just wanted that one extra field. So half width. So we also had in here a win, which was a boolean. We had turns, which was an int. We had snakes, which was account. It was a number. It was also an int. And we had finished, which we've effectively replaced with a date created. So half, half, and whatever. Half. Okay. So but but so they run-in isolates. And isolates what it taught me was all these creature comforts I have in JavaScript are not in JavaScript, they're in Node. Js or they're in the browser implementation of JavaScript. But, yeah, that's how I basically learned that. There you go. I had no idea. I had no idea console log was not built into JavaScript. That seems so odd. But I guess the console you're dealing with is in the browser, so it does make sense. Yeah. Let's create a new game. Let's create a rematch, see if we can get it to work. But, yeah, just fascinating. Yeah. Isolates. And isolates are not, you know, while that's running, isolates Cloudflare. I wanna say Cloudflare Workers. Yeah. Here. They have some really good reference while I was learning more about it. Isolates. VA orchestrates isolates, lightweight context, provide your code with variables that can access in a safe environment to be executed within. You could even consider an isolate a sandbox for your function to run-in. A single runtime can run hundreds of thousands of isolates switching between them. And isolates memory is completely isolated, so each piece of code is protected from other untrusted and user written code in the runtime. Islets are also designed to start very quickly. Instead of creating a virtual machine for each function, ISIT is created within an existing environment, blah, blah, blah, blah, blah. So but what's interesting is they are they are way more limited. Yep. Yeah. I wondered I wondered why because I I think what did I I was reading yeah. I've been reading through the documentation, and it said that the the specific JavaScript that you were able to call within within lots of these areas, the directives were limited. And I was like, oh, that's that's an interesting and I figured it was security, but I didn't realize there was so much to learn here. But that is why you come here to Ready, Set, Battlesnake is to learn things about Battlesnake and JavaScript and directives. And it worked. And it worked. Let's go. And it worked. We did it. So what we're gonna do now really is we're gonna just hit create rematch. And if I can I create loads of games in in, like like Yeah? Yeah. Yeah. It's gonna break things. We we we have also not built just a moment. We have not built logic to know which game is happening. We'll do one at a time. Top of them, we have. We have. We have. Because every turn is individual on the move endpoint. And then on the start and end, we're referring to the IDs. Actually, we could be running a load of these in parallel. Alright. So give it a try. Let's see what happens. See what happens. What's the worst that could happen? We end up having to pause our episode because we made a mistake and we have to fix it off camera. That is the really the worst that could happen. Oh, I am a bit concerned now because I don't even know when they've completed. That's another problem is I actually don't know when they I've taken a a guess that they won't take longer than each other. Like, alright, that game's over. I mean, let's just see. There you go. We're not winning every time. We have a number of turns. We have a number of snakes. Let's, let's just create a few more games. Like, I I want a a reasonable set to start with. All of them are gonna start with 2 snakes. Go on. I was gonna say just so if you, like, if you're if you're watching this and you're like, oh, I don't wanna have to, like, do this. It's it's causing my browser. I don't like all those tabs. You can, like, do all this from the CLI too. Like, you can actually go in and just, like, continually create games for the CLI. This is just easier than going in and setting up the CLI right now. And I don't wanna set up the CLI in this moment. So we should actually know if any games are outstanding because, yes, see, there's no turns and no wins on this one, which tells us that game is pending and now it has concluded. So, actually, we do have a little indication of how games are going. Look at us. So we'll create a few more games with a mixed player count. Okay. And if we refresh Directus, we should see okay, there are a few games still pending completion. If I refresh, 2 games pending completion, refresh 1 game pending completion, refresh. That game is still going. That game is still going. That game is still going. Oh, still going. We're doing well. Is that is that the last one we created? I mean, we'll find out. It must be. I hope it is. Descending. Oh, they've all concluded now. Okay. I think that's good to start. So Yeah. Let's go to insights, see see what we can do. Snake battle station. Let's pick a little kiss. Pick wonderful. Pick a little thing here. Is there a I don't is there like a snake? No? Is there is I don't want this game. Yeah. Cool. And our battlesnake. You know, do you wanna know why green feels like a very snaky color to me? It does. Save. That is the right one. Snake battle station. So this is, director's insights. It's this little module over here. And here, we can create dashboards. I don't really use it very much personally, just because my use cases don't demand many dashboards. So there might be a little bit of learning here. You can create panels. Just as a note, this is panels also an extension type. So if you wanna, you know, build on top of these panels, you know, build additional panels, you're more than welcome to. For this, I think we're going to stick in here. You know, we have the pie or donut chart, which feels like a good starting point. So we're gonna look at exactly what I had in mind. So win, Win. Count. Sure. Don't care about donut. That's just a display thing. Show labels. Yes, please. Show legend. Yeah. Pro oh, do you wanna know what? Let let's just see what happens here. Can because I think we can do conditional styles as well. So win rate. Okay. Hey. Dang. That was I don't wanna say that was too easy, but that was very smooth. Yeah. Although I we do need a legend. We do need a legend quite desperately. I would just always assume the the bigger one is my win rate. That's really It it you you are correct. Alright. Win rate win no. Win rate feels good. We can do, games, you know, we could do, the x axis is this one. Right? So date created maybe and turns, like how well am I doing over time. Yeah. Yeah. I like it. Aggregation. I don't really know what I'm doing here. Well, okay. Just leave it leave it without aggregation and see what happens. Yeah. Sure. And we'll call this one what are we doing here? Turns. Turns feels like a weird one. Maybe we just do turns feels weird. Why don't we do, like, account? Because I think what it will do, that's tell us the number of games. Sure. Let's find out. Right? Right. Games over time. Of course, we'll be looking at a very micro Woah. I'm not sure this is gonna pan out. This totally is not correct. Okay. Something. We did a bad thing. Aggregation can be forced as well. Right? Can you? Well, set it as false to see what happens. See what that change does. Oh, didn't like that. We are not doing that. I guess we do need to have Oh, aggregation is gone. Okay. So we have turns. We have snakes, we have x axis, which is date created. Do we need something on both axis? I'm sure. Do we? Oh, maybe not. I mean, it kind of looks like we do. I mean, I'm kind of as you can tell, I really don't make a lot of dashboards because, you know, it's just not it's just not my world. Maybe if we let's let's let's, see what else we can do. We have I like metric firstly, which is like total games played. That one feels easy. Yeah. That's simple. Aggregate function, just count. Count. Yep. And, suffix, Games played. Star decimal unit. Notations standard. Yeah. Yeah. Oh. Uh-oh. GraphQL validation error? I think I'm just not I'm genuinely thinking I'm just not selecting enough field. Do we need to do a field ID? Yeah. We did. Oh, there we go. 15, 15 groups load. Nice. Sure. Okay. It's not pleasing my eyes very much that, but let's, keep taking a look. Yeah. What do we have still? Font size auto, which makes sense, or you can make it fixed and then you can add padding accordingly. I do get the auto. Auto makes a ton of sense. We have a line chart. We have time series. So the collection, once again, games, the group aggregation, oh, group precision minute because we set a few games off at the same time. Count, date field, date created, date range, past 15 minutes. Sure. Value field. I, turns. Snakes. Oh, I don't know. I mean, can I edit can I can I oh, there it was? Can I clear the value, please? I'm just gonna hit tick. Alright. Didn't do anything. I've picked I've picked the wrong combination of picked the wrong combination of options. We almost we almost did it. Did you see how confident I went in? You went in. Listen, confidence is half the game here. We just gotta go in thinking you know what you're doing and, eventually, you know I'm not really sure. I'm not really showing this bit of director stuff pretty well. No. I think it's I think you're doing this fantastic. I like that we've been able to get stuff like we so it's because I said it was so smooth. Hey. There we go. Nice. And these are showing us? So this is showing us the number of turns taken on a minutely basis. And that was where I set loads of games off, and this was just as it was coming down from the last data point that was held. If I do it the past 30 minutes, I think I'll get a slightly oh, maybe not. It's a good try. Yeah. This is oh, here we are. Automatic based on data. That feels good. Okay. So 80 turns over here and then 320 turns over here. And then if we create a couple more games now, we'll create a game. Yeah. And I do like this. Like, if you were actually looking at this over a period of time, this would make a lot more sense. Could be really insightful. Yeah. So let me just save that, and we'll just make sure. Oh, thank you. Woah. Last game has not concluded yet, so we'll just let that conclude. Okay. So if we go back to insights now, there you go. It's a little bit more of a of a view. Right? So I don't really wanna look at the number of turns though. I wanna look at is it count? I think that will give us the same graph. Yeah. What I want is I just wanna know how many there are. How many games? How many how many games? Do you think turns is interesting? I no. I mean, I think let's I so turns is interesting, but only if you're playing 1 at a time. Exactly. And I think I think I think there's a way to do that. I've seen other people do it. So we had I think it might this global variable variable key can be like game. Confidence. Lots of confidence. Game interface. That's not what I want. Type. Is it in here? No. I've seen it. I've seen it somewhere else. Is it maybe called global relational and you pick a collection, you pick games, display template? Yes. You find the ID. I think this is it. So you select a game. So it says select an item. So can I, whatever? But we we won't look at editing that. So you select a game, right, like this. Great. And then you can use that global variable in all of the other boards Okay. In all of the other panels. So if I you know, maybe we do this. This is like global values up here. Great. And then we have this, you know, dynamic portion. So what do we want to know here? Well, we want to know I I don't know. Like, what about are we doing we're not doing per turns. We're not storing turns too. Maybe we just do what do you think? There's only one other thing that I thought would be really fun, but I think that the logic is gonna take too long. No. And it I mean, not gonna be related to this panel. I think it's I I'd be very interested to pull in some custom that that the the actual gift for the game. I think there's something interesting there to be able to pull in. I think we could do that. I think we could do that. Yeah. That's an ambitious it's an ambitious goal. You wanna try? Yeah. Go on then. Right. Tell me how to get the tell me how to get the URL. Go to your game. I'm I'm going. Got it. Alright. Go to view as GIF, and then it'll give you the format that you I will only click it when you say it correctly. GIF GIF. You may. GIF. Okay. And there's a URL and that's the game ID. Grab that, And that's that ID is movable. Right? Like, that ID is the ID of the game. So you can buy back and game by game. Yeah. So let's try our hand. We need to do a custom panel. That is that is definitely what we need to do. Yeah. But, right. So what are we gonna do here? We're gonna go up one layer. We're gonna go MPX create directus extension at latest. Whoops. Extension. Helps when you type it correctly. Right. What we're gonna do, we are oh, you're you're putting me on the spot here. I'm not overly confident. And viewers, if we don't get it done, we're not gonna get it done. But let's call let's consider this a stretch goal. I also haven't used these global variable values too, too much. While that's kind of doing its thing whoops. Docs, Data Studio app. I think it might be over in the user guide, insights, panels, global relational value here. Oh, and then in other in other, ones, you can use the variable key like in other panels. That's interesting. Anyway, we want a panel, director's extension, snake panel. JavaScript install dependencies. Okeydokey. I'm glad I gave you this challenge, and I am not the one having to implement it. This seems right. This seems good. It all seems good with the world. Alright. So we have the panel here. We have our panel dot view. It has been a while since I built an extension. So here we have all of the configs. We'll call this snake GIF snake GIF. Sure. We could do loads of configuration. We're not gonna do that. First thing we're gonna test is if we just have a if we just have a string like we manually insert it to start, can we can we play? Can we play it back? So in the panel, we have this text here, and I think all we would need to do, there's a bit of an assumption in here, is we're gonna paste in this URL and we're going to interpolate here the text, right? That feels right? It feels very good. Npm run dev. Sorry. Npm. I'm I'm not even in the Npm run build. I wasn't right running the right command or in the right location. And then we will I think what I'll do is I'll can I hang on? Kinda wanna open them side by side. There we go. And then I wanna just kill that, restart it. Once again, fixed as of, like, yesterday's release. I just haven't got around to upgrading this one yet. Open up our panel, and I'm hoping snake GIF. Hey. We insert an ID here. Let's go get an ID. This one, 72 turns. And so I I can't believe it might actually why did I say that? You've jinxed it now, but it's okay. Here. Hit save. Okay. Save. And oh. Don't know, mate. Happening. Oh, I have my dev tools kind of detached. Content security policy. So this might actually be cross it might be course with the dev side platform. Which is which is, the ilk of what this is. How do I solve it? How do I solve it? It's a GIF. It's a GIF at a location. Can we import it maybe? Can we import it? And then how would we import it? Let's take care of let's let's consult the docs. So we have in here extensions, developing extensions, extension services, accessing files, import a file. This is inside of an API extension how we would import a file. We would instantiate a file service. We have an asset key, which has the URL and a file object, which contains additional metadata, and then we read it in order to to return it. Excuse me. So let's have a think about the right way to play this. We could press a button. It'll import it and then play it back. It's a good idea. Let's try that. That seems easy. Let's try that. I say this. I say this. Easy as a That sounds easy. That sounds easy. Kevin problem. Sounds like an Android watch, and Kevin solves this tactical challenge. Alright. Alright. Alright. So let's just keep this easy for a moment. Let's let's stitch this off, and let's create a new bundle. And I'll call this one directus extension, and I'll just call it, Battlesnake Viewer and install dependencies. So that's creating a bundle. In that bundle, we'll have an endpoint and we will have, and we'll have an endpoint and a and a panel. So we'll CD into the battlesnake viewer, and we'll go npm run add. And the first thing we'll add is actually the endpoint. We can just call this one importer. JavaScript. And now inside of Battlesnake Viewer, source importer index dot j. So this is again, our custom route as mentioned earlier. Now inside of our existing one, we've actually got this is snake GIF. We have our actual snake here. We're gonna we're gonna just kind of nab this structure for a moment, inside of the importer here. We'll remove these actual things here, like so. Okay. So we'll call this one importer. Right. So when we hit importer, what we're gonna do, and we're gonna do a post request here, is we are going to access files. I'm gonna copy all of this. Here. We'll see what we can do. Oops. Okay. Router dot post. We will create a files service. Now I actually think, the docs do talk about importing that. So let's just take a little look at the top here. Yes. Okay. So in the endpoint itself, we have a router and a context. Awesome. Let's do a let's do a little more side by side jobby here. Right. So we have router and we have context. And up here, we're going to grab these values out. Then inside of router.post\u002Fcool, we will create a new file service. We will look at rec. So file.url is going to be req.body. URL. Cool. And the file object itself, how much of this must we pass? Let's look at the file object. API reference files. File object looks like this. How much of this must we pass? I don't think any. I don't think we need any of this. So I think we'll remove that. So we have our URL. Then we will import it. It will come back. We will then have the asset key, and we'll return the data. So if all goes well, npm run build, This is our importer, docker compose up. And I think what we wanna do here is test whether this endpoint works. Now I still have ngrok running. I don't still have it running. I've been running it since the beginning of this, this session. And I have hopscotch here, which is similar to Postman. It's just like an HTTP Explorer. So here, if I go importer post and inside of the let's just hit send there. Okay. Timing out, timing out, timing out. There is an error, invalid URL, input object object. Okay. Let's just cancel that. Let's set a body. Let's set this to be application JSON. There we are. And what we want in here is a URL of and then this exporter battle sync. So hopefully this will return the ID of a new item in our directors project. Okay? It's still going. So not feeling good about that. Invalid URL. Is it, though? Is it? Apparent I mean, no. No. No. Object object. Import 1. So it was struggling with import 1. So let's let's take another look at this. So it was struggling here with the file service dot import 1. Req dot body dot URL. Now to be fair, we never once took a moment to check whether it was gonna be req dot body. So let's just res.sendokay for a moment and that's console.log req.body. There was a bit of an assumption in there. So we will rebuild that and then we will restart that. Okay. And we will send that and we should hopefully get an okay back. We did. So let's take a look. So inside of req.body, yeah, dot URL. What's going on? Is this a URL we can hit? Oops. Yeah. Should be. Yeah. Sure. And let's just test that there's nothing funny going on with, like, access here. If I go to our file library and import, this is the RF for importing and hit import. The GIF is there. So there is nothing wrong with that. So we're just doing that via API instead. So fair enough. Fair enough. So we have the URL. We have some accountability, which I think we might just be able to bundle some for now. Get rid of it for now. Let's do npm run build and restart the container. Go to hopscotch and send this. Okay. Great. And this time, we've got a URL, which means there was nothing wrong. So let's just do one step at a time. That's console dot log the asset key. Yep. Rebuild. I'm determined to get this to work now. We are gonna do it. We have we have the Cool. Technology. We have the ability. That should still just return an okay, but it hasn't. Why? Code error invalid URL. It gives me it gives me it gives me the docs might not be right, which is possible. So as much as I wanna just be like, but that's what it says to do. Let's take a moment and let's actually, if we go to accessing if if let's find let's find the docs for this and let's just step through because all of the services are just in the code base. It's not my preferred way, of course, to be looking at stuff. Let's take let's take a little look. What we care about here is import 1, and it does expect an import URL and a body partial. So maybe maybe we did need to pass in, could not fetch from URL. The error said invalid URL out of interest. So maybe yeah. Because what it does is it grabs the raw file and then uploads it. Maybe we need to we must pass in this body. Just a thought. And that was the partial body objects. And maybe I mean, that's a partial body object. Maybe there's something else we must pass in this at the time of creation. Let's, let's hit send. Come on. Okay. Okay. Okay. In fact, again, it's taken that long. Yeah. No. Input object object code invalid URL. That invalid URL. Couldn't fetch file from URL object object. Isn't that interesting? Because the URL to me is a is a string. Right? Yep. And found that URL. But this time, before all of this, I logged service external file is unavailable. Couldn't fetch file from URL object object, but it isn't. It is a string, and this is the string. Fascinating. I mean, let let's just, you know, I've been a bit cheeky here. Maybe we, go back to accessing files, and I do actually create it with this accountability object here. Yep. I'll take that. I'm pretty sure I know it's not needed. I know it's not needed. But that the error isn't saying something else is missing. You've triggered an unhandled rejection. You may okay. Yeah. Fine. Okay. True. So if we go if we try around this, right, and then we catch an error. We console dot log the error. And we send this. And We are expecting an error, which is fine. We've got 503 service unavailable. Why? Couldn't fetch file from URL. This is what's troubling me. Object object. Let's take a let's just take another look here. So import 1. I think I worked it out. No. That was quick. Goodness. It is an opt I did pass in an object, didn't I? But that's not what's being passed in in the in the reference. The import URL is a string and the body, which is a partial file that should have been passed in as a string. All you need to do, Kevin, is read. Read the docs. So read the There's another person here, and we still didn't get there. Yeah. Okay. Asset key. We got an asset key that time. Success. There should now be a file in the file library. Right? Correct. So we are loading it in. We are loading it in. Title, description, tags, file name disk, all grand, but Beautiful. What we also want to do is this. We want to, YOLO. I can't be bothered with all this. We'll just there we are. We'll take the asset key, then we'll read the new asset, and we'll return the data. So what we'll get back in in response to this is it will always import. So that's not ideal because if it already exists, we don't necessarily want to reimport it every time. But for the sake of this little demo, I think it's fine. Then we'll get the ID back. So now if I rebuild this and we run this again oh, it was still spinning up. What we should get back is the new file object indeed, including the ID. And the nice thing about this is, if I go to access control, public, and I turn on directors files actually, I don't even need to do that. If I go to httpsphzedm.ngrok.io, right, slash assets slash ID will get a you don't have permission to access this error. Right? But what I can do is go to the user, generate a token. Okay. Generate the token, save it, and I pin that to the URL. Nice. Access token equals. And there's our Hey. Merge directly, which is pretty which is pretty cool. That's very cool. To keep this easier, though, I'm actually going to change the permissions of the files collection. So then you net we never need to pass in, pass in the key. So public system collections, directors files, all access on. Great. So now that's public. So now we've done the endpoint. The job is to create the panel. So what we'll do here is npm run add. That should be pretty easy, I think. I say that. That's a dangerous Easy for you to say. He does it. I know. It's very easy for I I think you've got this, Kevin. I trust I believe. I trust in Bodie. Next week, when I'm when I'm driving again, things are gonna go terribly awry, and you're just gonna kick back and and relax and enjoy the experience. Oh, yeah. I know. Viewer. What did we call the previous attempt to an extension here? Snake GIF, find viewer, viewer. This is my custom panel. Now there is some text. Now what we need to do, I think what we'll do is we'll remove the options entirely, right, because we don't want any options. It'll be a standard panel. And inside of that panel, we are going to have button button. We no longer have text. That's not a prop that's being passed in anymore. We just deleted it. So what we'll do is we'll do a v button. No. We won't. We'll do a v let's talk about what all these v things are for a moment. Back in our docs here, back in our docs, we have, resources for building extensions, components. And we have this components playground. These are all of the directors components that are inside of the Data Studio are made available to extensions. So we have here v button, and this is the director's button inside of the director's app. So we we have an immediate availability of this v button component, which is, yeah, pretty, pretty nice. So we can access that straight away. So we can go v. I did just notice it was v button like this and we'll call this, not v. I keep going back to the button. Sorry. We're looking for No. That was right. No. Yeah. But we want the input because we want to put in the ID. Yes. Yes. Yes. Because we'll keep it easy. We'll we'll put, like, right now, manual ID of a game hit go Yeah. Rather than, Automating it. Yes. Pick pick it from a drop down because we're running we're running out of time. So v input to v model. So, what are we gonna do here? Export default. We're gonna do data return, and we'll call this ID. Like so. Then inside of here, we'll put in the v input with a model of ID. Then we'll say on key up dot enter fetch, game, GIF, get GIF, fetch, git. Cool. And we will add and this is not really an ideal way of of writing these, of course, but methods and we'll call this fetch GIF. Yeah. And we'll just console dot log or you know what? We'll alert with the this dot ID value. That's what we'll do for now just to test all of that works before we go too far. So we'll go in here. Oh, I need to sorry. Npm run build was the command there. Oh, error. Come on. What are you doing, error? What are you doing? After component panel component, we're missing a comma. Okay. NPM run build. Lovely. And we'll restart that. Also lovely. So let's refresh. Let's refresh. Just took a hot minute there. Insights. New panel. Viewer. No options available. Right? Because nothing happens to the panel itself. This, wonderful, is our panel and we'll say we'll save it. Hey, enter. Great. So this means we are able to do something with that. What we're gonna do with it is we're going to go back to our docs. We're gonna look at our composables for app extensions, which is what this is. I'm gonna just close out everything else for now. And we're gonna use API. And what this will do is it will allow us to make API calls, to our endpoints that are already authenticated. Though, to be fair, I believe this is a completely public endpoint, so it shouldn't matter much. But still, we'll do it as as it should be done. So what we're gonna do here is we are going to import use API. We are going to use this composable, like so, and then we can make our endpoints like and then we can make our calls like this. So we will make this async. And what we're gonna do is we're gonna post to slash importer\u002Fimporter. And we have response dot data here. So all we're going to do is console dot log response dot data. Let's build. And this is all basically to get around that that security issue. Yeah. That course issue. I love that there's always just like a 6 step workaround to deal with course. Thank you. As opposed to just dealing with it head on. Yeah. So, we refreshed it. This is great. What's happened to my text box? What's happened to the text box? Is it no, but it's not. Where's my text box? Also, what's happened to this one? What's happening? Not seeing any errors. The API could not be found. Couldn't load extensions. Okeydokey. Have I missed a step? Have I rushed it again? Have I rushed a little bit? Oh, script setup. So we'll be using, we have to move this to script setup. And let's just take a quick look at how this I need to refactor this. Of course, we do, but that is fine. Async fetch GIF should just be able to, for the most part, come up here. We'll say, const fetch GIF equals a sync function like so. Then is that legit? Yeah. That is legit. And then so that's the methods. Then we have the data, which is the ID. So we will be import import ref from view. We'll create a new variable for that. We'll go const ID equals ref. Great. So we've handled that. Then we have props. And I believe the way to do props here is we say define props. Can we do this? I think we can do this. And all we care about here is show header, which to be honest, I do not care about. In theory, I've just refactored all of that. Let's take one more quick look, fetch GIF. So we have this dot ID. So we don't need that anymore. It'd be ID dot value, but we're not gonna alert it anyway. Kevin, watching your work is just fantastic, by the way. I love that I gave you this challenge and, like, everything has gone wrong. Okay. This is this is an Axios instance. I don't quite remember how it bloody works. So I think what we do here is we do, and I need to check, axios post. Oh, we do we literally just pass in the whole object as the second. Great. So in here, we're gonna do URL equals ID dot value. Okay. And then we have response dot data. Cool. So we'll console mock that. Alright. Let's rebuild that. Is it gonna complain this time? No, it bloody isn't. Docker Compose up. You give me a right workout here. Let's refresh, and we should now have access to use API. Now none of it's loading. Now it is. And there's our text box. Let's open up our dev tools. Of course, it didn't like the URL that was before. That was this panel here. Just testing that out. So in here Can I grab a screen? Pop it off at the side. Over here, yeah, we need a game ID. So we can do that here. Game ID. So we're not done yet because we're not gonna display it yet. If we hit enter, what's happened? Refused refused to load the images. No. No. No. That that's the old one. That's the old one. In fact, if I just remove that whole thing, we'll be better off for it. Let's refresh. See no errors. Let's paste that and let's hit enter. Nothing's happening. Why? Fetch GIF. Let's make sure this is ever running. You know what? This is all cute and all this. Let's just, just in case it's some silly async fetch gift, just in case it's some silly, async function, just in case it's something silly in the way that oh, I think it might have said invalid URL. I think it might. Do you wanna know why invalid URL? Because it isn't the full URL. We're just posting the ID. Oh, that's right. Yes. So what we'll do on the importer package is we're actually going to, we're gonna import req.body.url. Cool. So we're gonna make URL. Where is it? Here. Exported.battlesnake.comporter.battlesnake.games. And then here, req.body.idgamegame, maybe we call it. And in here, we pass in the earth. So now it needs a game. That's the value we need to pass going back to our panel. This is going to be game ID dot value. Let's rebuild the extension. Okay. Oh, yeah. We'll refresh. I do understand our network error showing there. Now we'll paste in the ID of the game. We'll hit enter. Oh, you know what we didn't do? We didn't open up dev tools. Let's skip to console. Wicked. Okay. We now have an ID of a directus I like, a directus asset in the ID. So all we need is the ID here. So this is the ID of the game, game ID, game ID, and then an image in here and the source of that image is going to be, how is this going to work? We need to just have a quick think about this. I'm gonna try something out. I think if we go \u002Fassets slash and then we need a value in here. So in here, we're gonna go image ID, and it's gonna be a ref and image ID dot value equals response dot data dot ID. Right? So Yep. Now this has the value in it of the ID, image ID. First thing we'll do here is just a quick v if image ID. We only wanna show it when that's the ID, and then here is going to be image ID. Let's see if that works. I'm not a 100% sure it will, but should do. I have faith oh, yep. There we go. I don't. Let's refresh. 1 of us has got 2. Great. ID is not defined. Why? Because ID dot value is gonna be game ID dot value. I'm feeling the heat, but I'm having a blast. I know. I was gonna say I feel like I gave you, like, the small challenge that became the much larger Yeah. But not not 145 in or whenever it was. That's not really the time to do it. But if we wait just a moment, nothing happens. Why? Console. Assets undefined 4 3. Fine. Let's figure out oh, image ID. Complete typo there. Remember this, Kevin. Remember this as the challenge comes next week. I'm having I'm having a blast. I really enjoyed last week, last episode. And now we have Hey. What is I? Kevin. Kevin. Kevin. Kevin. In an ideal world, you can obviously select from your existing games. It will handle all the logic for you. I think we're firmly out of time to do that this time around. Next time we and next time we won't be touching insights. But everyone, I hope you've had a wicked time. Any final remarks, Andrew? Thank you, Kevin, for taking on the challenge. Thank you all for watching. And be sure to tune in for our next episode of Ready. Ready. Set. Set. Battlesnake. Battlesnake. Nat, when you edit this, can you just time it so we get that right? Thank you. Bye.",[3534,3535],"6b42155e-93fc-4831-9905-6d258ea56f02","18c0d5b8-300f-454e-b49a-b7f3c1690936",[],{"year":978,"number":993,"id":3341,"episodes":3538,"show":3539},[3343,3344,3345,3317],{"title":3347,"slug":3348},{"id":3375,"slug":3541,"vimeo_id":3542,"description":3543,"tile":3544,"length":13,"resources":12,"people":12,"episode_number":480,"published":3545,"title":3546,"video_transcript_html":3547,"video_transcript_text":3548,"content":12,"seo":12,"status":19,"episode_people":3549,"recommendations":3550,"season":3551},"codebase-cleanup","893668322","Enter the chaotic world of coding etiquette.","11c8532a-326e-4967-a277-c1288112261b","2024-04-17","Codebase Cleanup","\u003Cp>Speaker 0: And now Dev Thoughts by Jack Rabbit. They say you should always leave a code base cleaner than you found it. But every time I try to clean up, I end up with 3 branches, 2 merge conflicts, and a git history that looks like a Jackson Pollock painting.\u003C\u002Fp>","And now Dev Thoughts by Jack Rabbit. They say you should always leave a code base cleaner than you found it. But every time I try to clean up, I end up with 3 branches, 2 merge conflicts, and a git history that looks like a Jackson Pollock painting.",[],[],{"year":978,"number":263,"id":3362,"episodes":3552,"show":3553},[3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3350,3378],{"title":3380,"slug":3381},{"id":3374,"slug":3555,"vimeo_id":3556,"description":3557,"tile":3558,"length":13,"resources":12,"people":12,"episode_number":740,"published":3545,"title":3559,"video_transcript_html":3560,"video_transcript_text":3561,"content":12,"seo":12,"status":19,"episode_people":3562,"recommendations":3563,"season":3564},"silver-lining","893668205","All the clouds that glitter are not gold.","ba8a1a96-745f-4ad0-9540-a3ca6a4900c6","Silver Lining","\u003Cp>Speaker 0: And now, Dev Thoughts by Jack Rabbit. They say every cloud has a silver lining. But in the world of cloud computing, every cloud just has a lot of data and constant threat of a security breach. But I suppose that's less poetic.\u003C\u002Fp>","And now, Dev Thoughts by Jack Rabbit. They say every cloud has a silver lining. But in the world of cloud computing, every cloud just has a lot of data and constant threat of a security breach. But I suppose that's less poetic.",[],[],{"year":978,"number":263,"id":3362,"episodes":3565,"show":3566},[3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3350,3378],{"title":3380,"slug":3381},{"id":2751,"slug":3568,"vimeo_id":3569,"description":3570,"tile":3571,"length":129,"resources":12,"people":12,"episode_number":159,"published":3572,"title":3573,"video_transcript_html":3574,"video_transcript_text":3575,"content":12,"seo":12,"status":19,"episode_people":3576,"recommendations":3578,"season":3579},"pampers-theme","931208527","Join Bry and his furry companion as he paints a beautiful Pampers theme inspired by all the little ones that represent our future.","e268f4e0-7070-414d-9a99-3fe905be111b","2024-04-15","Pampers (Discover Your Baby's World)","\u003Cp>Speaker 0: How are you? Yes. Hi. Oh, hi friends. Welcome back to the show.\u003C\u002Fp>\u003Cp>I'm your host Bry Ross and as you can tell I've got a furry little companion with me today. This is little Peapod and she is a gray squirrel. Now she was a rescue from the crazy cat lady down the street. This little lady fell out of a tree and was surrounded by 6 cats before my children and I swooped in to save her. And since then, we have just become the best of friends.\u003C\u002Fp>\u003Cp>She's a loyal little companion. And, yes, I know she looks a little big for a squirrel, but that's okay. We don't criticize people's weight. Alright. So I'm gonna put our furry little companion down here today.\u003C\u002Fp>\u003Cp>And today, in keeping of that theme of cuteness, we are going to paint a beautiful Pampers theme. So Pampers really doesn't need any introduction. Children are our future and Pampers wraps the bottom of our children in protective diapers that we can then throw away without having to make a giant mess whenever they use the bathroom. So the first thing we're gonna do, we'll just pick this up and if you notice the Pampers website they've got this beautiful teal theme. But before we dive into our color palette, what I always like to do is just set up those quick project settings.\u003C\u002Fp>\u003Cp>So we'll just bang those in and then we'll move into our appearance. So we'll just copy that logo from the URL. That's a nice SVG. I love vector format, means we can resize it to any scale, any any size really, and it will still retain the same same quality. It's beautiful.\u003C\u002Fp>\u003Cp>Alright. As for a public background, not only can you use static images, but you can also do video on your login page. And this looks like a great Pampers video that just highlights a lot of baby butt. And I'll be darned if baby butt is not cute. So now, let's find the favicon.\u003C\u002Fp>\u003Cp>Favicon. I never quite know how to pronounce it. And then we'll add a nice little public note in there. Just hang that in there. Welcome to the Pampers headless CMS and pin.\u003C\u002Fp>\u003Cp>Okay, so with that out of the way, let's dive into the theming. I'm gonna pick up my trusty palette knife and let's just take a look at this theme. Right? We see our module bar over here on the left. I want that to match our color.\u003C\u002Fp>\u003Cp>So we're going to go into navigation. We'll go to modules and for our background we're going to use a CSS variable that is built into our theming. So, we'll just use var project color, but, oops. Again, there are no mistakes, only happy accidents. We can see that now our module buttons need to be fixed.\u003C\u002Fp>\u003Cp>And just by adding the foreground to that button there, it's beautiful. Alright. Now, what is next? On hover, I can see maybe we want to change that to white as well. Add a little opacity.\u003C\u002Fp>\u003Cp>And if we're using a 6 digit heads code, I can add a opacity modifier there at the end and get that nice subtle effect. So anytime you're theming, it's great to begin with the end in mind. And, you know, when I'm looking at this now, I just I see a beautiful thing. We're almost there. We've got a little more to do.\u003C\u002Fp>\u003Cp>Up here at the project info, that gray is just it's not appealing. So we can use a little blending technique. It's called color mix in CSS. So I just do color mix, wrap that in a parenthesis, we'll put a little sRGB here and then we will blend 2 colors together. It's really easy to do, just follow right along.\u003C\u002Fp>\u003Cp>So we're going to take that var project color. We'll do a white and with a 20% blend, we're putting 20% of that in there. Let's switch that around just to be a little heavier on the white. And, oh, that's that's exactly what I was looking for. I love it when a good theme starts coming together.\u003C\u002Fp>\u003Cp>But something is is kinda not quite right to me. I I see this red up there. Do you see it? And I thought it would be a good idea for the coral, but let's let's just remove it. It just doesn't match.\u003C\u002Fp>\u003Cp>It clashes. And that's, again, one of the beauties of theming is you're never making a mistake. It's only accidents that we learn from and move forward. It's really easy to fix these things. Nothing is ever set in stone.\u003C\u002Fp>\u003Cp>It's It's always changing. So now we've got a great looking theme. I see just this little bit of gray behind the search collection bar. I want to remove that so I'm going to pull out the trusty palette knife again. We're going to go into the settings and I'm just going to spread a thin thin layer of white over that background normal.\u003C\u002Fp>\u003Cp>So now that I if I go into the collection bar looks great. I don't see that gray anywhere at all. This is looking like a beautiful theme. Now let's just go in and paint a few final little details here. I see their headline is an indigo color.\u003C\u002Fp>\u003Cp>It's, like a darker teal. Let's call it, Loch Ness Monster teal. Well, not Loch Ness Monster. I that's not a good teal. Let's call it Taylor Swift teal.\u003C\u002Fp>\u003Cp>My daughters like that. So we'll go in and do a little title foreground in there and now we can see that that black is not harsh anymore. It's just this beautiful indigo and, man, this thing has really come together. So we'll just paint the final touches here and as you can see on our public login page there is a beautiful video of baby butts and that's it. That is this theme.\u003C\u002Fp>\u003Cp>I hope you've enjoyed following along. This is such a wonderful theme to paint. That's it for this episode of The Joy of Theming. I hope to see you next time where we will teach you just some of the best tips and tricks to paint the most beautiful themes. We'll see you.\u003C\u002Fp>","How are you? Yes. Hi. Oh, hi friends. Welcome back to the show. I'm your host Bry Ross and as you can tell I've got a furry little companion with me today. This is little Peapod and she is a gray squirrel. Now she was a rescue from the crazy cat lady down the street. This little lady fell out of a tree and was surrounded by 6 cats before my children and I swooped in to save her. And since then, we have just become the best of friends. She's a loyal little companion. And, yes, I know she looks a little big for a squirrel, but that's okay. We don't criticize people's weight. Alright. So I'm gonna put our furry little companion down here today. And today, in keeping of that theme of cuteness, we are going to paint a beautiful Pampers theme. So Pampers really doesn't need any introduction. Children are our future and Pampers wraps the bottom of our children in protective diapers that we can then throw away without having to make a giant mess whenever they use the bathroom. So the first thing we're gonna do, we'll just pick this up and if you notice the Pampers website they've got this beautiful teal theme. But before we dive into our color palette, what I always like to do is just set up those quick project settings. So we'll just bang those in and then we'll move into our appearance. So we'll just copy that logo from the URL. That's a nice SVG. I love vector format, means we can resize it to any scale, any any size really, and it will still retain the same same quality. It's beautiful. Alright. As for a public background, not only can you use static images, but you can also do video on your login page. And this looks like a great Pampers video that just highlights a lot of baby butt. And I'll be darned if baby butt is not cute. So now, let's find the favicon. Favicon. I never quite know how to pronounce it. And then we'll add a nice little public note in there. Just hang that in there. Welcome to the Pampers headless CMS and pin. Okay, so with that out of the way, let's dive into the theming. I'm gonna pick up my trusty palette knife and let's just take a look at this theme. Right? We see our module bar over here on the left. I want that to match our color. So we're going to go into navigation. We'll go to modules and for our background we're going to use a CSS variable that is built into our theming. So, we'll just use var project color, but, oops. Again, there are no mistakes, only happy accidents. We can see that now our module buttons need to be fixed. And just by adding the foreground to that button there, it's beautiful. Alright. Now, what is next? On hover, I can see maybe we want to change that to white as well. Add a little opacity. And if we're using a 6 digit heads code, I can add a opacity modifier there at the end and get that nice subtle effect. So anytime you're theming, it's great to begin with the end in mind. And, you know, when I'm looking at this now, I just I see a beautiful thing. We're almost there. We've got a little more to do. Up here at the project info, that gray is just it's not appealing. So we can use a little blending technique. It's called color mix in CSS. So I just do color mix, wrap that in a parenthesis, we'll put a little sRGB here and then we will blend 2 colors together. It's really easy to do, just follow right along. So we're going to take that var project color. We'll do a white and with a 20% blend, we're putting 20% of that in there. Let's switch that around just to be a little heavier on the white. And, oh, that's that's exactly what I was looking for. I love it when a good theme starts coming together. But something is is kinda not quite right to me. I I see this red up there. Do you see it? And I thought it would be a good idea for the coral, but let's let's just remove it. It just doesn't match. It clashes. And that's, again, one of the beauties of theming is you're never making a mistake. It's only accidents that we learn from and move forward. It's really easy to fix these things. Nothing is ever set in stone. It's It's always changing. So now we've got a great looking theme. I see just this little bit of gray behind the search collection bar. I want to remove that so I'm going to pull out the trusty palette knife again. We're going to go into the settings and I'm just going to spread a thin thin layer of white over that background normal. So now that I if I go into the collection bar looks great. I don't see that gray anywhere at all. This is looking like a beautiful theme. Now let's just go in and paint a few final little details here. I see their headline is an indigo color. It's, like a darker teal. Let's call it, Loch Ness Monster teal. Well, not Loch Ness Monster. I that's not a good teal. Let's call it Taylor Swift teal. My daughters like that. So we'll go in and do a little title foreground in there and now we can see that that black is not harsh anymore. It's just this beautiful indigo and, man, this thing has really come together. So we'll just paint the final touches here and as you can see on our public login page there is a beautiful video of baby butts and that's it. That is this theme. I hope you've enjoyed following along. This is such a wonderful theme to paint. That's it for this episode of The Joy of Theming. I hope to see you next time where we will teach you just some of the best tips and tricks to paint the most beautiful themes. We'll see you.",[3577],"3b726dc7-42c7-45e6-86a9-b3704b2eb0b2",[],{"year":978,"number":2745,"id":2746,"episodes":3580,"show":3581},[2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2731],{"title":2760,"slug":2761},{"id":3583,"slug":3584,"vimeo_id":3585,"description":3586,"tile":3587,"length":159,"resources":12,"people":3588,"episode_number":213,"published":3592,"title":3593,"video_transcript_html":3594,"video_transcript_text":3595,"content":12,"seo":12,"status":19,"episode_people":3596,"recommendations":3598,"season":3599},"74a53f16-9a2b-42de-bca2-5de319565462","sort-fields","903061460","Learn how to add sort fields inside of new and existing relationships inside of Directus.","d4a8d6b2-e82a-4fe0-a1b0-6c8448c5ede5",[3589],{"name":3590,"url":3591},"Bryant Gillespie","https:\u002F\u002Fdirectus.io\u002Fteam\u002Fbryant-gillespie","2024-04-12","Sort Fields","\u003Cp>Bryant: Hi. Welcome back to the next episode of Short Hops. I'm your host Brian Gillespie and today we are talking about sort fields. This is one that I see come up a lot in our community. Very simple fix, and most people overlook this functionality.\u003C\u002Fp>\u003Cp>But surprise surprise, you can add sort fields anytime. So I see this coming up often on our many to any relationships, like our page builder scenario or even standard, like, one to many or many to many relationships. Wanna be able to sort those related items within that particular field. So let's just throw together a page builder here, we'll just call this our page blocks. Great.\u003C\u002Fp>\u003Cp>And then we've got our block FAQs, etcetera. So here on this screen, I don't have a sort option when I'm using the the wizard here, but if I go to continue an advanced field creation mode, I go to my relationship, you'll see there is an option for sort field. So I don't have to manually add a sort field, I can just add sort here and at the bottom, I could see that Directus will create this for me. Likewise, if I wanted to call it something else something else, I wanted to get really fancy with it, I can. So I don't have to name it sort, that's just kind of standard convention, but adding that sort field is as simple as that.\u003C\u002Fp>\u003Cp>So just go into advanced creation mode. Great. And then when I go to my pages now, I go to build a page, I could go in and create a new block, test test. Great. Let's add a hero, test, test and then I can rearrange those using that particular sort field.\u003C\u002Fp>\u003Cp>Great. But what about existing relationships? So if I look at projects for example. Right, if I go in and create a test task and a new task, oh, I can't even spell today, test project. Right.\u003C\u002Fp>\u003Cp>I've already got this relationship set up, but my sort fields, I don't have a way to sort this. How do I fix it? So I just go into my data model, I go to Projects, I look for my field that has a relationship, in this case, it's Tasks, and I go to that relationship tab and I have sort field. So I can pick that item up from one of the existing fields, which there are none, or I can simply just key something in here and Directus is smart enough to know that this field does not exist, so it will actually create it for you. And with that, all I have to do is now go back to our project and I could sort those items however I want.\u003C\u002Fp>\u003Cp>That's it. Easy peasy. I'll catch you on the next one.\u003C\u002Fp>","Hi. Welcome back to the next episode of Short Hops. I'm your host Brian Gillespie and today we are talking about sort fields. This is one that I see come up a lot in our community. Very simple fix, and most people overlook this functionality. But surprise surprise, you can add sort fields anytime. So I see this coming up often on our many to any relationships, like our page builder scenario or even standard, like, one to many or many to many relationships. Wanna be able to sort those related items within that particular field. So let's just throw together a page builder here, we'll just call this our page blocks. Great. And then we've got our block FAQs, etcetera. So here on this screen, I don't have a sort option when I'm using the the wizard here, but if I go to continue an advanced field creation mode, I go to my relationship, you'll see there is an option for sort field. So I don't have to manually add a sort field, I can just add sort here and at the bottom, I could see that Directus will create this for me. Likewise, if I wanted to call it something else something else, I wanted to get really fancy with it, I can. So I don't have to name it sort, that's just kind of standard convention, but adding that sort field is as simple as that. So just go into advanced creation mode. Great. And then when I go to my pages now, I go to build a page, I could go in and create a new block, test test. Great. Let's add a hero, test, test and then I can rearrange those using that particular sort field. Great. But what about existing relationships? So if I look at projects for example. Right, if I go in and create a test task and a new task, oh, I can't even spell today, test project. Right. I've already got this relationship set up, but my sort fields, I don't have a way to sort this. How do I fix it? So I just go into my data model, I go to Projects, I look for my field that has a relationship, in this case, it's Tasks, and I go to that relationship tab and I have sort field. So I can pick that item up from one of the existing fields, which there are none, or I can simply just key something in here and Directus is smart enough to know that this field does not exist, so it will actually create it for you. And with that, all I have to do is now go back to our project and I could sort those items however I want. That's it. Easy peasy. I'll catch you on the next one.",[3597],"c28a2713-ef7b-4e63-b3ac-23aec0f53fa5",[],{"year":978,"number":3600,"id":3601,"episodes":3602,"show":3607},29,"e708d08e-2aea-44fa-a335-9e2af2d40d7e",[3603,3604,3605,3606,3583],"a0f37f8b-c789-4421-b6c4-e4f681028d66","2766c70a-1fc0-46b1-ac5e-b464ca245334","9627b3fd-7a5b-4748-9c18-4ce318ac2cab","511b1967-c04f-42db-b515-911de7981d33",{"title":2161,"slug":2162},{"id":1794,"slug":3609,"vimeo_id":3610,"description":3611,"tile":3612,"length":1060,"resources":3613,"people":12,"episode_number":195,"published":3617,"title":3618,"video_transcript_html":3619,"video_transcript_text":3620,"content":12,"seo":3621,"status":19,"episode_people":3622,"recommendations":3626,"season":3627},"public-forms","932168576","In this recording of our live event on April 4 2024, Rijk, Jonathan, and Daniel discuss sharable forms for adding content to collections.","5e3ff428-f8d8-49a9-9c0f-34d2f0b0c4ca",[3614],{"name":3615,"url":3616},"Github Discussion","https:\u002F\u002Fgithub.com\u002Fdirectus\u002Fdirectus\u002Fdiscussions\u002F18807","2024-04-11","Public Forms","\u003Cp>Speaker 0: Hello. Hello, everyone. Welcome to another exciting episode of, request review, where we go over your guys' hopes, wishes, and dreams, and potentially crush them because software is not easy. Joking aside though, we're here to give we're here to go over your discussions inside of our repository. So if you're interested in a few feature or future features, please let us know.\u003C\u002Fp>\n\u003Cp>We have an existing, discussion template that you can use inside of our, repository or, extend existing ones. We also love to see that. And, today's topic, as you might have guessed with the title, is public forms or in other words shareable forms. So just, judging by the amount of people that I hear, I assume that this is pretty exciting for you guys. So give us a sign of life in the chat, please.\u003C\u002Fp>\n\u003Cp>Oh, that's what I future.\u003C\u002Fp>\n\u003Cp>Speaker 1: Like the people at home applauding. That's that's\u003C\u002Fp>\n\u003Cp>Speaker 0: that's Yes. Totally. Oh, yeah. We gotta oh, Oh, it's not French, it's Spanish for Noah. Okay.\u003C\u002Fp>\n\u003Cp>Okay. Okay. It's nice nice to see some interaction going. So public forms, shareable forms. Let's do a quick let's do a quick poll.\u003C\u002Fp>\n\u003Cp>So, how many of you guys used the current already existing feature of sharing forms or entries or however you would like to call it, share items. Because, I guess, public forms are very, very similar to that. It's, Brian says, I do not like sharing my forms. I do not like them then. I am.\u003C\u002Fp>\n\u003Cp>Yeah. Isn't that the the author's name? What is his name? Like a children's book author? Oh my god.\u003C\u002Fp>\n\u003Cp>Like, I'm blanking so hard right now. Doctor Seuss. Yes. Yes. Yes.\u003C\u002Fp>\n\u003Cp>Okay. Okay. Okay. Anyway, let's, let's go over some examples. So public forums, I think everybody has a mental image of what public forums are in their mind.\u003C\u002Fp>\n\u003Cp>But, let's make a very, very The most simple example that you could think of is just a contact form for me, basically. So you have a text input, and you want to put it on your website, and people reach out, fill it out, you get an entry, and you have an item in your database. Alright. Fair enough. But as you might have guessed, this is Directus after all, so this is not as easy in every single case.\u003C\u002Fp>\n\u003Cp>So, I I would I would love to quote, let give me give me one second to quote the person who opened the discussion, because there's such an amazing quote in there. I hope I did not oh my god. I think I closed the tab. I'm very sorry. Because the quote in there is just too good.\u003C\u002Fp>\n\u003Cp>Yeah. Very, very professional. Oh, my god. Maybe in the meantime, you could also think of a couple other examples, while I search for the thing because we're we're doing a show up.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. For sure. I think I mean, one thing that is probably, a good bit of context to kick off this discussion is just to go through what the current sharing thing looks like and what it does, and some of the requirements that we know exist around sharing things in general. And then sort of take it from there and elevate. Okay.\u003C\u002Fp>\n\u003Cp>What do we do to then unlock, you know, create and update and delete access next to just read access. Right? Because there's a reason we just started with reads and not the other ones. And that's that's sort of the complexity that I like to extract in in this session.\u003C\u002Fp>\n\u003Cp>Speaker 0: Speaking of complexity, I found the quote that I was looking for. And the quote is in the discussion itself is, the feature itself could start very small, but it could be very complicated if we wanted to cover more use cases. That's, like, literally every single every single feature that we could ever do.\u003C\u002Fp>\n\u003Cp>Speaker 1: That's the request review format for you, isn't it? So for those who are new in this session, one on good bit of context for what these live streams are all about. We just wanna take, you know, the discussion as it exists. Divergently think about, okay, what are all of the edge cases? Find sort of the use case, find all of the unknowns, and then take it back down.\u003C\u002Fp>\n\u003Cp>So, like, what is that MVP? What is the minimum viable product that we wanna ship for this to be a success? Right? What are the must haves? What could we add in the future?\u003C\u002Fp>\n\u003Cp>Where do we wanna start with this? Right? It's easy to say, oh, the the forms that you're sharing, just make them create access. Right? Done.\u003C\u002Fp>\n\u003Cp>But there's a lot more stuff going on into the hood as per usual. And also for the first time in days, we're getting some sun in here. So excuse my exposure.\u003C\u002Fp>\n\u003Cp>Speaker 0: Oh, we got some interaction. But, let me let me give let me give another example because there's many, many different use cases for public forms. So it's not just about, like, let's say, you put an email in there and a text box or something. It could also be more involved, for example, inside of your organization because directors is often used as a, like, enterprise tool inside of your company, for example, in the Internet even. So you could build a support ticket system in Directus, and we know that people do that.\u003C\u002Fp>\n\u003Cp>So, a public forum could be you visit a site inside of your Internet with an extensive, like, public forum describing what issue do I have, what department is this in, when did this happen, is this critical or not, other descriptions, related persons, or whatever. I mean there's also like very often for marketplaces or adding data to a validation queue because this is, like, very, very often the case. For example, if you have a marketplace and you want to open it up to new, merchants, they have to apply somewhere. Right? They they have to they have to ask, hey.\u003C\u002Fp>\n\u003Cp>I am this merchant. Can you please add me to your, shop? Or there was another example inside of the discussion itself regarding, let's say you have, like, a lunch app or whatever where you can off order food. So you want to get more restaurants or or bistros or whatever, and they can apply at your place. So you give them a public form.\u003C\u002Fp>\n\u003Cp>They can enter their information, and then you have, like, inside of your validation queue, all of the people or entries that you could go over and then, make sure that they get applied to your public to your service. Alright. Sounds like one of\u003C\u002Fp>\n\u003Cp>Speaker 1: the the initial requirements that you're sort of sneaking in with these examples is that this form can live both as a public website page in your direct instance or as an embedded form somewhere on\u003C\u002Fp>\n\u003Cp>Speaker 0: your website. Exactly. This is We're\u003C\u002Fp>\n\u003Cp>Speaker 1: we're immediately diving in, and we're off.\u003C\u002Fp>\n\u003Cp>Speaker 0: That that is that is literally I I wrote this down. This is exactly what I was, coming up with now because, like, the first the first requirement that kinda diverges, like you said, is, okay, so I have this public form. Where is it? Where where where can I find it? Because, like, it really depends.\u003C\u002Fp>\n\u003Cp>If I send somebody a link via email for example, it could be sufficient if the link is directly to your directors instance and you get served up like a complete page. And, there's the whole thing with the logo or whatever. And then you have an entire dedicated page for it. But very often also, you would like to embed your form into your existing website. Let's say with the with the, contact form example.\u003C\u002Fp>\n\u003Cp>Right? You just want to have a little contact form and on the bottom of your website. But, yeah, then then it starts then it starts. Okay. So you can have 2 different ways.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. I was gonna say, let's start let's start with what we have today. Right? So we don't have, writable forms. Let's call it that forms you can change stuff in.\u003C\u002Fp>\n\u003Cp>But we do have sort of the shared form where if you open a single item in a collection, you can share share that item. And what that does for the folk who've never tried it, which I believe is most, is that it will create sort of a temporary role, so to speak, a temporary access token that has access to just that one item with the role permissions that you associate to the share. So that sounds a little confusing, but that basically means you can give away a link that contains, you know, basically a long obfuscated URL that gives read access to just that one item and the relations that it has based on the rule that you associate for read permissions, Then that public link, it's, a directus page. So you will be navigated to your directus instance slash admin or slash shares, what we call it then for the public ones. And then, if you open that up, you basically get a read only state of the form that you just shared.\u003C\u002Fp>\n\u003Cp>Right? And there's some additional options on that share. You have you can set a password on it. You can set a maximum time, start, and end date that it's available to the public. Those those are the main ones.\u003C\u002Fp>\n\u003Cp>I think there's some other options. But long story short, the first step there is, like, if we were to just just enable a share on, the collection level, we can use that as a starting point, but that is only for forms that are then within that sort of share context of directors itself. Right? So you'd be able to use it as an alternative to something like Google forms or, type form or something where you create a form, you share it, you have a public page that you can share it, that you can, link people to. To me, the the fun complexity of this is really when it comes to, a, spam protection, and b, embedding it elsewhere.\u003C\u002Fp>\n\u003Cp>Right? Because somebody in the chat, our very own team just now, just half joking said just iframe it into your front end, which, sure, you can do that. But at the same time, somebody else in the chat also mentioned, you know, it would be great if there's a contact form on each of the products in my sort of web shop or or, renting, vending vending thing. At which point, you need to be able to dynamically inject some sort of default value into this form before you submit it. Now if we're talking about embeddable forms, it's almost like a different feature request.\u003C\u002Fp>\n\u003Cp>Because if you think about it, you could open up, write access to the public role and just build a form. Right? You can build a query you can build a form in your website and just post a request that straight out to, you know, your direct this instance. So, I think we wanna split this discussion up in, a, the shareable form, which is more sort of the Google form alternative versus embeddable forms, which is, like, what can we do to sort of make it easier to request or build a form indirect and then request it and display it just like that on your website?\u003C\u002Fp>\n\u003Cp>Speaker 0: Because, also, how much control do you want to have regarding the styling, for example, with, like, branding? Do your inputs look different? Because, you know, it looks out of place. It could look out of place if, you use our design guidelines, but your brand uses, like, way smaller inputs. It just looks very weird then.\u003C\u002Fp>\n\u003Cp>So we also had a nice, suggestion for, by Tim that I like. Where is it? Oh, no. No. By by by Brian.\u003C\u002Fp>\n\u003Cp>By a nice, by Brian. Pre filling fields by query parameters could be very cool. And hidden fields, of course. Like, there are many, many, there are so many things that you could do because there's so many different use cases. Like you said, for example, if you want to embed it, do we want to a do we want to be able to also restrict, like, how many different links there are to that form, for example?\u003C\u002Fp>\n\u003Cp>So, if I have a email list and I want to contact all of my contacts and I I give them each a personalized input field because I want to avoid the spam issue that you just mentioned, you know? So let's, say, okay, I only want people to be able to answer the form once, at least, you know, on that link, for example. But can they be different? Should they be the same all the time?\u003C\u002Fp>\n\u003Cp>Speaker 1: So using the form once, that's the part that we do currently support with the read only share. Right? Where there is a setting for how many times can this item be opened. And every time somebody opens that that item, it just, you know, it decreases the number by 1. And then once the number hit 0s, the link, you know, goes goes this disabled.\u003C\u002Fp>\n\u003Cp>Now for creates, how do you confirm that it was done by 1 person? Right? If you associate it to an email address or something, are we gonna do email validation? Do do does there have to be a confirm step? Which is sort of like I'm I'm thinking about other forms I've seen in the wild.\u003C\u002Fp>\n\u003Cp>Right? Where it's like you wanna sign up to a raffle or something and you leave an email address and that's your that's your ticket, so to speak. But you can only use it once. Yeah. Somebody in the chat rightfully now at that point, aren't you just logged in to the app anyways?\u003C\u002Fp>\n\u003Cp>Because now you have some sort of an account or a verified account within that direct to this instance. Maybe. Maybe. Right? Hard to tell.\u003C\u002Fp>\n\u003Cp>Well, I think the other the the question that closely ties into this as well around things like CAPTCHA. How do you prevent robots? How do you prevent AI from just pulling up the form and spamming it to death? Right? Of course, an obvious one for a lot of folks like, oh, just smack a a Google recap on that and you're done.\u003C\u002Fp>\n\u003Cp>But, of course, this wouldn't be a direct this request review chat if it was that simple. Because there's a lot of sort of like wrecking and accessibility concerns around, CAPTCHA as a whole When it comes to Google CAPTCHA specifically, I I I'm a little fuzzy on the details, but I'm pretty sure there were some GDPR concerns around embedding Google services as a whole, that already requiring, you know, cookie notices and whatnot because almost every Google service will track you to bits, if you embed stuff on your page. So that's a concern. It also relies on a third party service, which is something that historically we have to be a little bit careful with, because we wanna support multiple different options. Right?\u003C\u002Fp>\n\u003Cp>But now here we go. This is the point where now we need to think about how do we have a standardized CAPTCHA system that can work with providers that works across form, right, for both shared forms and into and, like, in internal forms. Another question from the chat. To add some more complexity, who creates the public form? Is it an administrator or a nontechnical CMS user for whom setting up a form with the data model interface might be overwhelmed?\u003C\u002Fp>\n\u003Cp>Another great question. Right? So in in the direct to this model form is basically tied to your data model. So if you share an item, you share the item that you're seeing with somebody else. Right?\u003C\u002Fp>\n\u003Cp>So in its simplest form, you would share the form that you're currently seeing with the world. Is that what you want? Maybe not. Probably not. In Daniel's example from earlier, if you wanna do a simple tech form with just an email and some text, it's very likely that in your data model, you end up with a status field that says has been responded yes or no or metadata that you wanna have in your system.\u003C\u002Fp>\n\u003Cp>But how do you differentiate between the 2? Are we gonna ask the user who is sharing the form to then pick and choose fields to share specifically? Are we gonna attach it to another role soon to be policy? Or are we gonna, or or plan c is, are we gonna strip public forms from the regular content model altogether and just have a whole different section somewhere that says, here is public forms. And then perform, you just have to configure where the responses are saved.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Regarding your point of configuring specific fields like the status, for example. Like, if somebody submits the form, should it be, you know, active, reviewed, tool review, draft, or whatever? So that that really sounds to me like a field preset sign type of thing, but that exact same vein. Okay?\u003C\u002Fp>\n\u003Cp>Let's say you have a form with a user created field that automatically adds the user that created that row. Okay. What do we put in there? Who who who created that? I mean, the user could, you know, make their own public user, so to speak, and link them via a field preset, but then also kind of fields just not quite thought through.\u003C\u002Fp>\n\u003Cp>Like, to me at least, like, just thinking about it right now, like, it feels, like, so hacky that people would need to do that themselves, that it feels like this is not really thought through. But on the other hand, like, do we want to have, like, a global invisible user that we could reference? Maybe.\u003C\u002Fp>\n\u003Cp>Speaker 1: I mean, we do have the concept of a public role. So with the user created example, it would be null because there's no user indirect because that created the thing. So if we if we zoom back out just a little bit, I think the first question that we sort of had this session hey, Jonathan. Welcome to join you, Just to catch you up. The first question was, shareable forms versus embeddable forms.\u003C\u002Fp>\n\u003Cp>Right? Which is a shared form is basically the way I see it now as a page within Directus that you'll to, and they just get, you know, a nice looking Directus connected to stuff. It's safe. Done. Little you know, think about Google Forms sort of mentally, that style.\u003C\u002Fp>\n\u003Cp>Right? The second thing is some sort of UI component or some sort of auto generated API endpoint or maybe just a public post could be, to render a configured form and direct us on your own app or website in whatever way is appropriate for your tool. Right? To me, those are 2 separate discussions. I think we start with the shareable forms because then the embeddable forms could use the same permission system and shared, configuration from the shareable form to then embed one of the shared forms.\u003C\u002Fp>\n\u003Cp>Right? That's kind of what I think that that would be a 2 step 2 step approach.\u003C\u002Fp>\n\u003Cp>Speaker 2: Nope. I like that. So that's what you're seeing here. Right? So this is the current it's read only at the moment.\u003C\u002Fp>\n\u003Cp>Hard coded is read only. Even though we allow we've kind of plumbed this for role support in the future, it's currently read only. It's hard coded read only. So you get a read only form that looks like this, and it is to a degree, there are some permissionings. So the role does give access to the fields that are available and the relational content that's technically available and visible in the form.\u003C\u002Fp>\n\u003Cp>So you can see here my image isn't showing because I don't allow I haven't allowed the permissions correctly on that particular role. So you could have that kind of capability, And I believe we should I haven't tested this, actually. I because I'm logged in as an admin right now, but I think we can restrict the role list here based on the so the the allowed roles and permissions for the user.\u003C\u002Fp>\n\u003Cp>Speaker 1: Permissions again? Yep. Exactly.\u003C\u002Fp>\n\u003Cp>Speaker 2: So permissioning should control what's available in this list. So I think, again, I think the general plumbing is there for a shareable, editable form? The question becomes, what is it that we're sharing? You know, are you allowing them to create a an item from the form? What does that look like?\u003C\u002Fp>\n\u003Cp>Because right now, we are sharing an existing record. So there'd be work to be done around that, I think. Absolutely. As an embeddable I like the embeddable idea. Technically, you can kinda do that already.\u003C\u002Fp>\n\u003Cp>You know, we we see examples of that in the structures that, our good friend, Bryant, has set up where, you know, he set up these kinds of forms where\u003C\u002Fp>\n\u003Cp>Speaker 1: Right.\u003C\u002Fp>\n\u003Cp>Speaker 2: He's almost replicating our schema management capabilities of being able to create an editable form. So he's made it so that you can create a schema field, add whatever, you know, value type. This is a And as we're\u003C\u002Fp>\n\u003Cp>Speaker 1: talking segue into, the second question I wanted to bring up, which is for shareable forms, are we sharing an existing form and you will still limit it down. Limit it down. Or is it effectively a separate form builder where you can create one of forms that you then connect the dots between that and your actual data model?\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. Similar to I think our our CRM has that kind of capability where you can build a form. Right? And then behind the scenes, the data gets stored wherever you direct those fields to store. Exactly.\u003C\u002Fp>\n\u003Cp>But you've got an and it's embeddable as a you know, you can get it as a link or you can direct them straight to the form or you can embed it as an embeddable link underneath the hood.\u003C\u002Fp>\n\u003Cp>Speaker 1: Because the one thing I do recognize about the current share system is that it's a little bit a little very opaque. What's the right word? It's a little opaque to to know what you're sharing. I mean, by default, you're sharing what you're seeing. Right?\u003C\u002Fp>\n\u003Cp>It's like you're sharing the item that you're currently seeing. Yep. But what's actually gonna\u003C\u002Fp>\n\u003Cp>Speaker 2: be shown to the user is different. Right? So you have to go check the link and see what it's gonna\u003C\u002Fp>\n\u003Cp>Speaker 1: look\u003C\u002Fp>\n\u003Cp>Speaker 2: like versus\u003C\u002Fp>\n\u003Cp>Speaker 1: It's because it's based on the role that you then associate with the share. So if you use your own role, it's what you see is what you get. But other than that, it's a bit of a, you know, to your point, you have to create the share, pull it up, double check. If you're creating a dedicated form to share, be it read or write, that confusion is taken away immediately because it's the form that you created. It's the form that you're seeing.\u003C\u002Fp>\n\u003Cp>Right? I think the big question then becomes, how do we handle permissions if at all? Do we auto generate the permissions based on the stuff you put in the form? And therefore, you make it very explicit. Like, you put, an email address field, so therefore, you can see an email address.\u003C\u002Fp>\n\u003Cp>Or do we make it a little more opaque again by associating a role or multiple policies to TM to control the permissions for that shared item. Right? That's that's where it's a little finicky. So when when I meant that permissions, for example, if, where permissions become really important is for nested relationships. Right?\u003C\u002Fp>\n\u003Cp>So if you have a read item with a many to one field to a related category or something, what part of the category are you also supposed to be able to read from this entry point of the top level item? So having some sort of permission set there is gonna be required. Because otherwise, theoretically, if your relationships are a little complicated, you could pose a hell of a lot of data. Right? Because if we were to say, auto generate all of them, if you have a user created field because you just wanna show somebody's name, avatar, you could now theoretically go to that user record and you go to a direct to files record because you now have that connection through the user.\u003C\u002Fp>\n\u003Cp>Right? So that also, makes me wonder, there's another feature request that is completely unrelated to this, but it might actually directly be related now. One that I opened, I feel like 20 years. It's configuring access control permissions based on a parent child. So instead of saying, you have access to direct these files, you have access to direct these users.\u003C\u002Fp>\n\u003Cp>It's saying the permission, you can read a file if it's attached to a user if you're coming through the user and only then.\u003C\u002Fp>\n\u003Cp>Speaker 0: Oh. Oh, yeah. Yeah. Yeah. Yeah.\u003C\u002Fp>\n\u003Cp>Yeah. That's, that would be very cool. That would be very cool.\u003C\u002Fp>\n\u003Cp>Speaker 1: Top the clock. I think we need to need to start a new game. It's like, at what point do we get Jonathan to do that?\u003C\u002Fp>\n\u003Cp>Speaker 2: It doesn't take much.\u003C\u002Fp>\n\u003Cp>Speaker 1: It doesn't take much. Because because that would that would also potentially solve for this. Because then you could say, okay. If you have many to 1 on the record that you're sharing, you now have access to read just that one record from the related table, but nothing else from the related. Right?\u003C\u002Fp>\n\u003Cp>And for the record, that is how the current share system works actually under the hood. It is a hell of a lot more complicated, because what it does is it looks at the item that you're sharing and it checks what items are related, and then it makes a new permission set that says you can read just that one item from the related table. Right? But all of the other API ends work the same, which is interesting. But that's that's the way it does it right now.\u003C\u002Fp>\n\u003Cp>So it does actually, you know, specifically look up which items are related, which items are shared as per the role, but then lock it down to only allow you to ex that one particular item that is associated to the shared thing to make sure that we don't overexpose data in your system. Food for thought. But it it, you know, we're getting real quick into the weeds here again. Just from the question, is it sharing the existing form from your data model or are we sharing new one off forms? It sounds like and I've I've it it sounds like we probably wanna look into some sort of system.\u003C\u002Fp>\n\u003Cp>You can create individual forms and associate them back to your data model. Just in those cases where you wanna have multiple different forms with different fields that go to the same item.\u003C\u002Fp>\n\u003Cp>Speaker 2: Commonly the case. Right? So if I was setting up a, you know, a a newsletter subscription, I might have very simple, but I still wanna register the contact. I still wanna be able to, you know, track their compliance or other things that I may wanna track or need to track versus the same contact information I would collect for a you know, you're signing up for a meeting or a webinar or something else. So the the forms can be storing data to the same places or to additional places.\u003C\u002Fp>\n\u003Cp>Speaker 1: Follow-up question. What is that? One of these public forms, and I'll I'll get to Tim's question in the chat very soon because that's another level of complexity. But should we allow one of custom forms to save to multiple tables at the same time?\u003C\u002Fp>\n\u003Cp>Speaker 2: Yes. Technically, yes.\u003C\u002Fp>\n\u003Cp>Speaker 1: Form can one form create 5 different records in 5 different tables?\u003C\u002Fp>\n\u003Cp>Speaker 0: Oh, I think it must. Right? Because of relations?\u003C\u002Fp>\n\u003Cp>Speaker 2: Well, not even relations.\u003C\u002Fp>\n\u003Cp>Speaker 1: Go Independent of relations. Right? Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 2: So if I'm if I'm setting up a form to collect, medical data, you know, to sign you up or set you up or do something or I'm collecting a survey, I might have survey information that I'm storing in the survey table, contact information I'm storing in the contact table. Just simple example. Right? Just 2 tables. But the contact information is in one place, the survey data is somewhere else.\u003C\u002Fp>\n\u003Cp>And I may from a from a compliance perspective, I may not be associating the user. I'm just tracking that the user responded, but the survey data has to be independent. Right? No relation at all.\u003C\u002Fp>\n\u003Cp>Speaker 1: Here's one quick question.\u003C\u002Fp>\n\u003Cp>Speaker 0: Good point, but Tim, wait beef before we go further further down the rabbit hole, there's another good point by Tim. It it it really sounds like a job for a flow or hook. And I think I agree, actually, because, like, the complexity then of setting it up and then the UI that has to be added for that and, stuff that could go wrong and stuff. Like, maybe, you know, just as a as a simple example. Okay.\u003C\u002Fp>\n\u003Cp>You have you just make one row in one table. That would that could be the cutoff that we decide on, maybe. I don't know. But it it really sounds like maybe a hook or a flow would then should be responsible for, you know, taking the the depending on the status, it takes the user's email and puts them also into the contacts table or the whatever table. I think I agree with that take.\u003C\u002Fp>\n\u003Cp>How do you guys feel about that?\u003C\u002Fp>\n\u003Cp>Speaker 1: It's both ways because you could say you'd save all the data to a table and then run the flow based on that change or you save the form to a flow and then handle it yourself. I and I think there's a case to be made for both because for a simple contact us form that is always going through the same table in the same format and it matches your data model, it feels like flows could be a lot of, you know, complexity to configure just for that simple use case. But for everything else, I fully agree. Once you go, I wanna touch 5 different tables and I wanna submit to a different web, and all that kind of stuff. Yeah.\u003C\u002Fp>\n\u003Cp>Form to a flow, makes a lot of sense. Right?\u003C\u002Fp>\n\u003Cp>Speaker 0: Right. So let's blow up this, let's blow up this discussion further because Tim also said, alright. How about, should public forms be able to save to a specific content version as well.\u003C\u002Fp>\n\u003Cp>Speaker 1: If you call the first deal with the safety of a specific content version. Well, the nice thing is if we're going with this whole form safe to flow as idea, then sure do whatever you want. Right? That's that's kind of the beauty of the escape hatch that could be flows as the back end forms. Because at that point, if you wanna save it to a conversion, go for it.\u003C\u002Fp>\n\u003Cp>Right? What do we care? What when how when would you use that, though? Let's think about that for a second. So you have draft state basis for an item.\u003C\u002Fp>\n\u003Cp>Speaker 2: I guess if you were updating existing content, you may wanna do that as a version, possibly. Typically, in shareable forms, I don't see it as a version necessarily. But if you were allowing someone, say, to update but even then revisions would track the the content changes over time if you were inclined.\u003C\u002Fp>\n\u003Cp>Speaker 1: I I think yeah. What I was just about to say is what Tim again just put in the chat. Let's say you want to review and approve the changes that were made in pub. Right? So if you're running a sort of Wikipedia style, Wiki style, page Mhmm.\u003C\u002Fp>\n\u003Cp>And you wanna allow people to suggest edits, you could have a public form to update that one particular page, and then those updates go into a version that then can be reviewed and approved by the old person in charter, which is an interesting an\u003C\u002Fp>\n\u003Cp>Speaker 0: interesting use\u003C\u002Fp>\n\u003Cp>Speaker 2: case. That's a cool that's a cool freaking use case.\u003C\u002Fp>\n\u003Cp>Speaker 1: Which actually would be very convenient for our own docs for that record.\u003C\u002Fp>\n\u003Cp>Speaker 0: For example, that's what I was thinking about. I have the little button on the bottom here. Edit this page. Edit\u003C\u002Fp>\n\u003Cp>Speaker 1: this line. That would be very cool.\u003C\u002Fp>\n\u003Cp>Speaker 0: Hang on.\u003C\u002Fp>\n\u003Cp>Speaker 1: Another use case from the audience in, in that updating existing thing from, Jay Shue says, another use case is a business directory and the users being able to suggest changes. Agreed. And the Durb saying we could actually use that fairly well as well. That's it. It could be implemented in flows.\u003C\u002Fp>\n\u003Cp>Right? Which is where it gets interesting again. Right? Because flows really become that sort of escape hatch for this type of stuff that, like, oh, you wanna save it to a version instead of to a table? You wanna save it to a different whatever you wanna do.\u003C\u002Fp>\n\u003Cp>Just use a flow. It it does also answer the question, is this just for create or is this also for update? And I think we've just concluded that also updates are important. Click updates. That's when you can do you can update an existing submission that you've already done, or you could, you know, the Wiki example or the business directory example or some of those.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yep. I think about, in particular, things like, you know, some of these online forms could be fairly complex. Right? I've I've done this. I've I've got you know, you're filling out something for a financial thing and you, you know, it's gonna take you an hour to do the form while you may be saving and coming back later.\u003C\u002Fp>\n\u003Cp>You can come and go from it. We do a lot with secured online security forms, you know, where we're filling out answers to complex questions that take hours or days to finish. And so, essentially, saving state and coming back and making updates and, oh, I mean, I I wanna fix that answer that I did, you know, 3 pages back in the form, and I wanna go make that adjustment.\u003C\u002Fp>\n\u003Cp>Speaker 1: I think we found another use case for the saving it to a version.\u003C\u002Fp>\n\u003Cp>Speaker 0: Mhmm.\u003C\u002Fp>\n\u003Cp>Speaker 1: Because if you save it to a version, the validation of the database doesn't have yet because it only needs the saves to the database rather than to your stage to changes. Right? So the, what would you call it, plausible forms where you can enter a bunch of stuff, hit save as draft basically, and then come back later. That would be relying on content versioning.\u003C\u002Fp>\n\u003Cp>Speaker 2: Good. Yep. Good very well. Doesn't have to, but it could. Right?\u003C\u002Fp>\n\u003Cp>You can manage that via state or status or other things as well. But,\u003C\u002Fp>\n\u003Cp>Speaker 1: yeah. Unless you're thinking about validation in that case. Right? If you have data integrity rules that say the column cannot be So in your form, it has to be filled out. But you don't want the force to use the whole form once, you need to have an in been safe, which would be, you know, a version conversion version.\u003C\u002Fp>\n\u003Cp>Speaker 2: I like it.\u003C\u002Fp>\n\u003Cp>Speaker 0: It's Okay. Let's\u003C\u002Fp>\n\u003Cp>Speaker 1: I need to switch to a slightly simpler question. Otherwise, my brain is gonna go What about deletes? Is Is that a thing you do publicly?\u003C\u002Fp>\n\u003Cp>Speaker 0: My first gut reaction was no. Hell no. But, let's give it a little more thought.\u003C\u002Fp>\n\u003Cp>Speaker 2: But I guess if it's user data, I don't know. I could see a use case for it. I don't I don't think from our side of things, if depending on how it's implemented, If you got compliance issues, GDPR, other kinds of things where the user has the right and choice to do that, that's that's gonna it's gonna be very use case specific.\u003C\u002Fp>\n\u003Cp>Speaker 1: Because for the case\u003C\u002Fp>\n\u003Cp>Speaker 2: we we try not to delete data in general. We put it in the soft archival state. You know, we use status or other things to handle that. But where you have compliance requirements where you must delete, But I don't know if that's a form. That that could be flows driven.\u003C\u002Fp>\n\u003Cp>That can be log business logic driven at that point as opposed to something to do with the form itself. I think you'd have to have a form that says I wanna I want my data to later. I wanna unsubscribe. And by doing that behind the scenes, the flows and data data management happen the way that you need them to happen.\u003C\u002Fp>\n\u003Cp>Speaker 1: Right. Because I I could imagine that the because because deletes blank at least sound kind of insane. Because you would have what? You would show the user, here's all the records and go whichever delete whichever one you want. It that that feels like not something that you'd realistically want.\u003C\u002Fp>\n\u003Cp>But I could imagine that if we connect it to an account again, not not so much a a direct as app account, but, like, the moment you create a form submission, I could imagine that you maybe wanna do a public read of a layout. Here's a new question that we're gonna discuss in 5 minutes. Where you can just as a user as a temporary user all the submissions that you've done in the past and then delete a previous submission.\u003C\u002Fp>\n\u003Cp>Speaker 2: To me, again, business logic. Right? That that tends to be not so much I I think you'd I think someone suggested it here. I think you'd do it as a delete request. You'd submit a request to delete and then confirm whatever mechanisms.\u003C\u002Fp>\n\u003Cp>Your your business logic, your flows, your your any additional code processing that you're doing on top of that would handle those things and send notifications accordingly. Feels very much like you would submit that as a specific form request to remove data, not the ability to delete data from the form. I think I agree in general that that shouldn't be allowed. You'd have to submit that as a separate form, maybe. I don't know.\u003C\u002Fp>\n\u003Cp>I'm sure there's a use case that I'm not thinking of. But\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. But to your point, if you create a new form that triggers a flow rather than saves to a table, that flow then just deletes it, whatever, based on your business logic, then prop solved. Right? You you don't necessarily go the mile to\u003C\u002Fp>\n\u003Cp>Speaker 2: Yep.\u003C\u002Fp>\n\u003Cp>Speaker 1: Have native victim delete support for that specific of a use case. Okay. Interesting. Interesting. Interesting.\u003C\u002Fp>\n\u003Cp>Interesting. Tricky. Tricky. Tricky questions. Tricky questions.\u003C\u002Fp>\n\u003Cp>Another another question in the chat here from user. So what about editable forms or relational data where items can be deleted after it's been saved and the user edits the data?\u003C\u002Fp>\n\u003Cp>Speaker 0: We got it. Ladies and gentlemen, we got it.\u003C\u002Fp>\n\u003Cp>Speaker 2: That's the real goal. If we can get right to do it, then we're then we're successful.\u003C\u002Fp>\n\u003Cp>Speaker 1: What about editable foreign situational data where items can be deleted as it's been saved and use it as the data? I mean, I guess that kinda goes for any editable forms that it's like, user creates it, admin changes it, user tries to edit it, and that looks different. Which, yes, it will look different because you changed it. You changed somebody's submission. Right?\u003C\u002Fp>\n\u003Cp>I think that becomes becomes more of a workflow or a business process, thing where tell your staff don't touch, you know, don't go changing somebody submission. That's bad mojo. But I guess yes.\u003C\u002Fp>\n\u003Cp>Speaker 0: What about resubmitting of shareable forms? We're kind of in the midst of thinking this through because, in the beginning, we said, okay. If you have a fixed link that you can send somebody and then they submit the thing, we could restrict that you can only submit once with that link. Or you can have a general link where, like, every single submission results in a new row every time. But then it's very use case dependent, I think.\u003C\u002Fp>\n\u003Cp>No. Like, that should be just configurable. Okay. Is this, will this result then in a on on my second visit to that link, will it show me the actual item, or is the form empty again, and can I resubmit it? I mean, that's, like, very dependent on on on the configuration, I think.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. I think it should be doable, though. Some additional configurations here, potential validation checks. So, again, if it's an embeddable form or it's a shareable, editable form from our side, then this is gonna need some updating as well. Right?\u003C\u002Fp>\n\u003Cp>We'll need some additional parameters and things here. You'd have to potentially check the data, validate the data on save. Flows, again, can handle some of that, but to give the user feedback of you've already submitted or disabling the form because they've already made a submission. How do we check that? What does what does that look like?\u003C\u002Fp>\n\u003Cp>Because now you actually have to have something, you know, activity wise that says your device or whatever has been registered. We know that it was you, and you've already submitted the form. Yeah. That gets that's where I I don't know. It almost feels like that should just be front end dev work and get managed from that side of things.\u003C\u002Fp>\n\u003Cp>But Oh, here's here's\u003C\u002Fp>\n\u003Cp>Speaker 1: the problem though, Jonathan, when it comes to the form that we're sharing, who's the front end dev doing the work?\u003C\u002Fp>\n\u003Cp>Speaker 2: It's it's us.\u003C\u002Fp>\n\u003Cp>Speaker 1: It's us.\u003C\u002Fp>\n\u003Cp>Speaker 2: It's us. I know. So this needs more complex kinds of things because now max 5 uses, well, is that 5 users, or is that 5 you know, one person could submit all 5 or use all 5\u003C\u002Fp>\n\u003Cp>Speaker 1: values. Right? Is a user?\u003C\u002Fp>\n\u003Cp>Speaker 2: What is a user? Right? What is the\u003C\u002Fp>\n\u003Cp>Speaker 0: Are we getting deep? What am I I mean,\u003C\u002Fp>\n\u003Cp>Speaker 1: that's that's kinda the question\u003C\u002Fp>\n\u003Cp>Speaker 2: right now. I think about like same thing. We we track this on, you know, AppSight anyway, but now we've got this floating thing out there that would also have to track an activity record of who the IP and device and things are and be able to validate against that as part of this form configuration or this shareable form configuration. Now it's, you know, one time per user or per IP or, you know, whatever we consider that to be. There'd be something here, you know, max uses.\u003C\u002Fp>\n\u003Cp>So I want a 1,000 submissions, but then I wanna be able to also say per something.\u003C\u002Fp>\n\u003Cp>Speaker 1: The the problem of the the how do you confirm the per something? Because you could say you could you could I mean, you could get away with it by just having a unique column. Right? That it's like you can only have one submission with this email address, and that's it. Done.\u003C\u002Fp>\n\u003Cp>It could be unique. Already exists. Can save it. Done. Right?\u003C\u002Fp>\n\u003Cp>But how do you prevent somebody, you know, with a Gmail account, for example, you can do the plus something something and it will come back to you anyways. You prevent somebody from submitting times with the same, you know, plus 1, plus 2, plus 3, that will work. And I can use all information still. Because there's no way for us to validate that. And then, you know, for email specifically, you can start thinking about, maybe we have some sort of email validation or we send an email and you have to click confirm or all that kind of stuff.\u003C\u002Fp>\n\u003Cp>But then what happens if you wanna use something else as the unique identifier? What if you're doing some of the phone numbers? Right? Where it's like, enter your phone number, we'll send you a text when it's your turn to to for for like reservations. Right?\u003C\u002Fp>\n\u003Cp>If you're doing a reservation at and it's it's that's that's very common around here. I don't know. Over in your parts of the world. But, you know, if you go to a restaurant here and it's full and it's like, oh, your your table's gonna be about 40 minutes and you your phone number, they'll send you a text when it's ready. Right?\u003C\u002Fp>\n\u003Cp>Mhmm. Or about 10 minutes before it's ready. So in that case, you wanna duplicate it on the phone number, but how can you confirm that? Right? You you can't really, there's no way for us to know that.\u003C\u002Fp>\n\u003Cp>Speaker 0: A simpler a simple example would be if if you have first sorry, the delay delay is giving us. No, but but a simple example for, limiting that kind of, use would be, let's say you have a form that you can resubmit with, let's say, you have an online shop and then you want to use this for functionality for giving out promotion codes, and you only want to give out a 100 promotion codes, okay, Then it's just a matter of counting the rows, you know, so so we don't have to have a unique field or whatever. We just say, okay. This form can be submitted 100 times. That's it.\u003C\u002Fp>\n\u003Cp>I think there's like, it's a little different to your guys' use case, what you just described with the email and and phone and whatever. But, I mean, this is also a valid use case, I think. You know? I I want this form to just be usable for 100 submissions. Okay?\u003C\u002Fp>\n\u003Cp>Then it's just a matter of counting.\u003C\u002Fp>\n\u003Cp>Speaker 1: Mhmm.\u003C\u002Fp>\n\u003Cp>Speaker 2: Well, that's our that's already built in. Right? So we already support that max five uses. So and we keep track. Similarly, if we want this to be 1 per or 2 per source, right, then we have to actually track from a and, I guess, because it's an embeddable form or it's a, you know, we we have information and knowledge about the browser.\u003C\u002Fp>\n\u003Cp>Right? We we can track that. It just requires that we store the activity information and utilize that as part of the additional values here in a sense. There's still ways around. Right?\u003C\u002Fp>\n\u003Cp>There's always a way around. I've got 5 devices, and I wanna submit 5 submissions. You know, I can I can do that, but and and, you know, we can do things like enforcement of uniqueness on the values coming in and those things? That's already there. It's more about can I at least track to know that this user has already submitted a form because of their IP address slash device ID, that we track anyway from the browser cookie side of things?\u003C\u002Fp>\n\u003Cp>Speaker 1: I think, I think I'm reaching the same conclusion as Tim. It's like we're going down the bot detection rabbit hole, which is like, can we fingerprint an individual user? No. There's no way. There's there's no way because IP address is shared with the location.\u003C\u002Fp>\n\u003Cp>So, therefore, everybody in the same household or the same business is gonna have a similar bound IP address. So that that doesn't work. I mean, this this fingerprint in question has been a cat and mouse between browser makers and, you know, back end implementers. It doesn't exist. The only way to do that is to require a sign through some sort of secondary authentication method, like send you an email with a magic link.\u003C\u002Fp>\n\u003Cp>So therefore, you prove you have access to the email, and that's it. Right? Before it submits or text with a phone number, that kind of stuff. Or log in through an SSO provide to confirm your, you know, your identity there, which actually is a different question I didn't even think about. But it would be kinda nice if you could sort of, like, connect the dots between what is your GitHub account click here to to o auth your way in to confirm, but that's not gonna be a 5 minute conversation, I don't think.\u003C\u002Fp>\n\u003Cp>Another question from the chat here, from from user and I'm gonna butcher your name. Sorry about that. Mama. That's what I'm gonna go with. How about the ability to show already submitted values to the user when filling out a form?\u003C\u002Fp>\n\u003Cp>Thinking about a sign up form for a party, and showing what food others already bring with them. Which is so this is an interesting question, which I think is not so much, the shareable, writable form. This is the question of readable lists of items. So right now, the share that we offer is just for a single item. You share that one.\u003C\u002Fp>\n\u003Cp>Right? What we don't allow you to do yet though, you can go to a layout and share the whole in a simple fashion. I could imagine that for this particular use case, what you would do is you would basically make a public share for your record of foodstuffs people bring to the party, long collection name, and then you can give people that link. Right? And then when they go there, they just see a layout like you would expect from directives with all of the stuff that has been previously entered with a click to to open an individual, I imagine, and then a, you know, a plug or something to go to the create page if you have create permissions through that public share.\u003C\u002Fp>\n\u003Cp>Speaker 2: I think this is actually related to Hannes' question as well. More so because the food items being brought can be a many to many or a relational junction that's already there. So we'd show that in the current form. Right? Just like we did, you know, I only have translations in here right now, but equivalent kinds of things where you've got a a relational structure.\u003C\u002Fp>\n\u003Cp>The real question becomes when you're adding or removing relational items, this is considered potentially a delete operation under the hood. Right? So there there's there's a little bit of a tie between the delete conversation we were having and this ability to show or allow or not allow you to you know, you might be able to see what other people have brought, but you can't remove their food item. You can only add your own food item. Right?\u003C\u002Fp>\n\u003Cp>And or remove your own food item. I think role permissions generally work correctly for that, but it becomes a question of making sure that you test and set those things correctly. But is that considered a delete? Right? If you've got a cascade operation on, you know, removing a a relational item, well, that's potentially gonna cascade and cause a delete down the line.\u003C\u002Fp>\n\u003Cp>Speaker 1: I think that would always be a deselect from the relational perspective. The\u003C\u002Fp>\n\u003Cp>Speaker 2: Well, that depends on what you set your permissions for. Right?\u003C\u002Fp>\n\u003Cp>Speaker 1: Oh, it's what you do. It's not permissions. The field\u003C\u002Fp>\n\u003Cp>Speaker 2: oh, I guess it's what you set your field settings for. Right? If you set by default, yes, we set it to nullify so we don't actually delete any data. But if they've got cascade delete permissions, you know, if the cascade delete is turned on, that's going to actually delete data or contact.\u003C\u002Fp>\n\u003Cp>Speaker 1: Point. Yeah. So the that's here's the security vulnerability way to happen. If you have an edit access to a simple item with a many the one many the one is configured to delete on select. If the user edit the field and they deselect, they now can theoretically delete stuff in your database, which is fun.\u003C\u002Fp>\n\u003Cp>Alright. Oh, jeez. It's already we're very we're already on on the dot here almost at 11. Oh, yeah. 11 for me.\u003C\u002Fp>\n\u003Cp>Times for everybody else. Just to to summarize all of this, because we went very much like this, but not a lot like that yet. We're running out of time. I think some some initial conclusions as far as I sort of take it now from this session. Right?\u003C\u002Fp>\n\u003Cp>It's like there's definitely a difference between shareable forms and embeddable forms. Shareable forms is part 1. Embeddable forms can then reuse whatever configuration that has for embedding stuff. I think that's conclusion number 1. I think to me, conclusion number 2, it's ideally you can create forms separately from the data model.\u003C\u002Fp>\n\u003Cp>Model. Although, that is quite a bit, you know, more involved to build and experiment with and etcetera etcetera. I think starting with create access is the most straightforward as you don't have problems with who can read what, how do you update previous submissions, how do people know what submissions they've previously done, etcetera. So in order of operations, shareable pages create first, then, read access for a listing would be very useful because then you have a way to click into an update item there. And then I think as a sort of your v 2, we should think about what do we form creation to look like in the first place and how does that tie into sharing?\u003C\u002Fp>\n\u003Cp>And a different question is, should sharing even be tied to collections and items, or is it just pages in the app altogether? Because I can also imagine that there's other places, like, folder in your file library that you might wanna share. Don't know. Like, another feature request for a different day, because I got skipped is, what about shareable dashboards from Insights? That'd be cool.\u003C\u002Fp>\n\u003Cp>But let's not do that right now. We'll save that for a different session. For now, Daniel, back to you to close it out.\u003C\u002Fp>\n\u003Cp>Speaker 0: Thanks everybody for coming. If you enjoyed this discussion or are interested in other discussions, you can head over to directors. Iotv where you can find lots of other interesting shows and also this one. And, we hope to have you to see you again.\u003C\u002Fp>","Hello. Hello, everyone. Welcome to another exciting episode of, request review, where we go over your guys' hopes, wishes, and dreams, and potentially crush them because software is not easy. Joking aside though, we're here to give we're here to go over your discussions inside of our repository. So if you're interested in a few feature or future features, please let us know. We have an existing, discussion template that you can use inside of our, repository or, extend existing ones. We also love to see that. And, today's topic, as you might have guessed with the title, is public forms or in other words shareable forms. So just, judging by the amount of people that I hear, I assume that this is pretty exciting for you guys. So give us a sign of life in the chat, please. Oh, that's what I future. Like the people at home applauding. That's that's that's Yes. Totally. Oh, yeah. We gotta oh, Oh, it's not French, it's Spanish for Noah. Okay. Okay. Okay. It's nice nice to see some interaction going. So public forms, shareable forms. Let's do a quick let's do a quick poll. So, how many of you guys used the current already existing feature of sharing forms or entries or however you would like to call it, share items. Because, I guess, public forms are very, very similar to that. It's, Brian says, I do not like sharing my forms. I do not like them then. I am. Yeah. Isn't that the the author's name? What is his name? Like a children's book author? Oh my god. Like, I'm blanking so hard right now. Doctor Seuss. Yes. Yes. Yes. Okay. Okay. Okay. Anyway, let's, let's go over some examples. So public forums, I think everybody has a mental image of what public forums are in their mind. But, let's make a very, very The most simple example that you could think of is just a contact form for me, basically. So you have a text input, and you want to put it on your website, and people reach out, fill it out, you get an entry, and you have an item in your database. Alright. Fair enough. But as you might have guessed, this is Directus after all, so this is not as easy in every single case. So, I I would I would love to quote, let give me give me one second to quote the person who opened the discussion, because there's such an amazing quote in there. I hope I did not oh my god. I think I closed the tab. I'm very sorry. Because the quote in there is just too good. Yeah. Very, very professional. Oh, my god. Maybe in the meantime, you could also think of a couple other examples, while I search for the thing because we're we're doing a show up. Yeah. For sure. I think I mean, one thing that is probably, a good bit of context to kick off this discussion is just to go through what the current sharing thing looks like and what it does, and some of the requirements that we know exist around sharing things in general. And then sort of take it from there and elevate. Okay. What do we do to then unlock, you know, create and update and delete access next to just read access. Right? Because there's a reason we just started with reads and not the other ones. And that's that's sort of the complexity that I like to extract in in this session. Speaking of complexity, I found the quote that I was looking for. And the quote is in the discussion itself is, the feature itself could start very small, but it could be very complicated if we wanted to cover more use cases. That's, like, literally every single every single feature that we could ever do. That's the request review format for you, isn't it? So for those who are new in this session, one on good bit of context for what these live streams are all about. We just wanna take, you know, the discussion as it exists. Divergently think about, okay, what are all of the edge cases? Find sort of the use case, find all of the unknowns, and then take it back down. So, like, what is that MVP? What is the minimum viable product that we wanna ship for this to be a success? Right? What are the must haves? What could we add in the future? Where do we wanna start with this? Right? It's easy to say, oh, the the forms that you're sharing, just make them create access. Right? Done. But there's a lot more stuff going on into the hood as per usual. And also for the first time in days, we're getting some sun in here. So excuse my exposure. Oh, we got some interaction. But, let me let me give let me give another example because there's many, many different use cases for public forms. So it's not just about, like, let's say, you put an email in there and a text box or something. It could also be more involved, for example, inside of your organization because directors is often used as a, like, enterprise tool inside of your company, for example, in the Internet even. So you could build a support ticket system in Directus, and we know that people do that. So, a public forum could be you visit a site inside of your Internet with an extensive, like, public forum describing what issue do I have, what department is this in, when did this happen, is this critical or not, other descriptions, related persons, or whatever. I mean there's also like very often for marketplaces or adding data to a validation queue because this is, like, very, very often the case. For example, if you have a marketplace and you want to open it up to new, merchants, they have to apply somewhere. Right? They they have to they have to ask, hey. I am this merchant. Can you please add me to your, shop? Or there was another example inside of the discussion itself regarding, let's say you have, like, a lunch app or whatever where you can off order food. So you want to get more restaurants or or bistros or whatever, and they can apply at your place. So you give them a public form. They can enter their information, and then you have, like, inside of your validation queue, all of the people or entries that you could go over and then, make sure that they get applied to your public to your service. Alright. Sounds like one of the the initial requirements that you're sort of sneaking in with these examples is that this form can live both as a public website page in your direct instance or as an embedded form somewhere on your website. Exactly. This is We're we're immediately diving in, and we're off. That that is that is literally I I wrote this down. This is exactly what I was, coming up with now because, like, the first the first requirement that kinda diverges, like you said, is, okay, so I have this public form. Where is it? Where where where can I find it? Because, like, it really depends. If I send somebody a link via email for example, it could be sufficient if the link is directly to your directors instance and you get served up like a complete page. And, there's the whole thing with the logo or whatever. And then you have an entire dedicated page for it. But very often also, you would like to embed your form into your existing website. Let's say with the with the, contact form example. Right? You just want to have a little contact form and on the bottom of your website. But, yeah, then then it starts then it starts. Okay. So you can have 2 different ways. Yeah. I was gonna say, let's start let's start with what we have today. Right? So we don't have, writable forms. Let's call it that forms you can change stuff in. But we do have sort of the shared form where if you open a single item in a collection, you can share share that item. And what that does for the folk who've never tried it, which I believe is most, is that it will create sort of a temporary role, so to speak, a temporary access token that has access to just that one item with the role permissions that you associate to the share. So that sounds a little confusing, but that basically means you can give away a link that contains, you know, basically a long obfuscated URL that gives read access to just that one item and the relations that it has based on the rule that you associate for read permissions, Then that public link, it's, a directus page. So you will be navigated to your directus instance slash admin or slash shares, what we call it then for the public ones. And then, if you open that up, you basically get a read only state of the form that you just shared. Right? And there's some additional options on that share. You have you can set a password on it. You can set a maximum time, start, and end date that it's available to the public. Those those are the main ones. I think there's some other options. But long story short, the first step there is, like, if we were to just just enable a share on, the collection level, we can use that as a starting point, but that is only for forms that are then within that sort of share context of directors itself. Right? So you'd be able to use it as an alternative to something like Google forms or, type form or something where you create a form, you share it, you have a public page that you can share it, that you can, link people to. To me, the the fun complexity of this is really when it comes to, a, spam protection, and b, embedding it elsewhere. Right? Because somebody in the chat, our very own team just now, just half joking said just iframe it into your front end, which, sure, you can do that. But at the same time, somebody else in the chat also mentioned, you know, it would be great if there's a contact form on each of the products in my sort of web shop or or, renting, vending vending thing. At which point, you need to be able to dynamically inject some sort of default value into this form before you submit it. Now if we're talking about embeddable forms, it's almost like a different feature request. Because if you think about it, you could open up, write access to the public role and just build a form. Right? You can build a query you can build a form in your website and just post a request that straight out to, you know, your direct this instance. So, I think we wanna split this discussion up in, a, the shareable form, which is more sort of the Google form alternative versus embeddable forms, which is, like, what can we do to sort of make it easier to request or build a form indirect and then request it and display it just like that on your website? Because, also, how much control do you want to have regarding the styling, for example, with, like, branding? Do your inputs look different? Because, you know, it looks out of place. It could look out of place if, you use our design guidelines, but your brand uses, like, way smaller inputs. It just looks very weird then. So we also had a nice, suggestion for, by Tim that I like. Where is it? Oh, no. No. By by by Brian. By a nice, by Brian. Pre filling fields by query parameters could be very cool. And hidden fields, of course. Like, there are many, many, there are so many things that you could do because there's so many different use cases. Like you said, for example, if you want to embed it, do we want to a do we want to be able to also restrict, like, how many different links there are to that form, for example? So, if I have a email list and I want to contact all of my contacts and I I give them each a personalized input field because I want to avoid the spam issue that you just mentioned, you know? So let's, say, okay, I only want people to be able to answer the form once, at least, you know, on that link, for example. But can they be different? Should they be the same all the time? So using the form once, that's the part that we do currently support with the read only share. Right? Where there is a setting for how many times can this item be opened. And every time somebody opens that that item, it just, you know, it decreases the number by 1. And then once the number hit 0s, the link, you know, goes goes this disabled. Now for creates, how do you confirm that it was done by 1 person? Right? If you associate it to an email address or something, are we gonna do email validation? Do do does there have to be a confirm step? Which is sort of like I'm I'm thinking about other forms I've seen in the wild. Right? Where it's like you wanna sign up to a raffle or something and you leave an email address and that's your that's your ticket, so to speak. But you can only use it once. Yeah. Somebody in the chat rightfully now at that point, aren't you just logged in to the app anyways? Because now you have some sort of an account or a verified account within that direct to this instance. Maybe. Maybe. Right? Hard to tell. Well, I think the other the the question that closely ties into this as well around things like CAPTCHA. How do you prevent robots? How do you prevent AI from just pulling up the form and spamming it to death? Right? Of course, an obvious one for a lot of folks like, oh, just smack a a Google recap on that and you're done. But, of course, this wouldn't be a direct this request review chat if it was that simple. Because there's a lot of sort of like wrecking and accessibility concerns around, CAPTCHA as a whole When it comes to Google CAPTCHA specifically, I I I'm a little fuzzy on the details, but I'm pretty sure there were some GDPR concerns around embedding Google services as a whole, that already requiring, you know, cookie notices and whatnot because almost every Google service will track you to bits, if you embed stuff on your page. So that's a concern. It also relies on a third party service, which is something that historically we have to be a little bit careful with, because we wanna support multiple different options. Right? But now here we go. This is the point where now we need to think about how do we have a standardized CAPTCHA system that can work with providers that works across form, right, for both shared forms and into and, like, in internal forms. Another question from the chat. To add some more complexity, who creates the public form? Is it an administrator or a nontechnical CMS user for whom setting up a form with the data model interface might be overwhelmed? Another great question. Right? So in in the direct to this model form is basically tied to your data model. So if you share an item, you share the item that you're seeing with somebody else. Right? So in its simplest form, you would share the form that you're currently seeing with the world. Is that what you want? Maybe not. Probably not. In Daniel's example from earlier, if you wanna do a simple tech form with just an email and some text, it's very likely that in your data model, you end up with a status field that says has been responded yes or no or metadata that you wanna have in your system. But how do you differentiate between the 2? Are we gonna ask the user who is sharing the form to then pick and choose fields to share specifically? Are we gonna attach it to another role soon to be policy? Or are we gonna, or or plan c is, are we gonna strip public forms from the regular content model altogether and just have a whole different section somewhere that says, here is public forms. And then perform, you just have to configure where the responses are saved. Yeah. Regarding your point of configuring specific fields like the status, for example. Like, if somebody submits the form, should it be, you know, active, reviewed, tool review, draft, or whatever? So that that really sounds to me like a field preset sign type of thing, but that exact same vein. Okay? Let's say you have a form with a user created field that automatically adds the user that created that row. Okay. What do we put in there? Who who who created that? I mean, the user could, you know, make their own public user, so to speak, and link them via a field preset, but then also kind of fields just not quite thought through. Like, to me at least, like, just thinking about it right now, like, it feels, like, so hacky that people would need to do that themselves, that it feels like this is not really thought through. But on the other hand, like, do we want to have, like, a global invisible user that we could reference? Maybe. I mean, we do have the concept of a public role. So with the user created example, it would be null because there's no user indirect because that created the thing. So if we if we zoom back out just a little bit, I think the first question that we sort of had this session hey, Jonathan. Welcome to join you, Just to catch you up. The first question was, shareable forms versus embeddable forms. Right? Which is a shared form is basically the way I see it now as a page within Directus that you'll to, and they just get, you know, a nice looking Directus connected to stuff. It's safe. Done. Little you know, think about Google Forms sort of mentally, that style. Right? The second thing is some sort of UI component or some sort of auto generated API endpoint or maybe just a public post could be, to render a configured form and direct us on your own app or website in whatever way is appropriate for your tool. Right? To me, those are 2 separate discussions. I think we start with the shareable forms because then the embeddable forms could use the same permission system and shared, configuration from the shareable form to then embed one of the shared forms. Right? That's kind of what I think that that would be a 2 step 2 step approach. Nope. I like that. So that's what you're seeing here. Right? So this is the current it's read only at the moment. Hard coded is read only. Even though we allow we've kind of plumbed this for role support in the future, it's currently read only. It's hard coded read only. So you get a read only form that looks like this, and it is to a degree, there are some permissionings. So the role does give access to the fields that are available and the relational content that's technically available and visible in the form. So you can see here my image isn't showing because I don't allow I haven't allowed the permissions correctly on that particular role. So you could have that kind of capability, And I believe we should I haven't tested this, actually. I because I'm logged in as an admin right now, but I think we can restrict the role list here based on the so the the allowed roles and permissions for the user. Permissions again? Yep. Exactly. So permissioning should control what's available in this list. So I think, again, I think the general plumbing is there for a shareable, editable form? The question becomes, what is it that we're sharing? You know, are you allowing them to create a an item from the form? What does that look like? Because right now, we are sharing an existing record. So there'd be work to be done around that, I think. Absolutely. As an embeddable I like the embeddable idea. Technically, you can kinda do that already. You know, we we see examples of that in the structures that, our good friend, Bryant, has set up where, you know, he set up these kinds of forms where Right. He's almost replicating our schema management capabilities of being able to create an editable form. So he's made it so that you can create a schema field, add whatever, you know, value type. This is a And as we're talking segue into, the second question I wanted to bring up, which is for shareable forms, are we sharing an existing form and you will still limit it down. Limit it down. Or is it effectively a separate form builder where you can create one of forms that you then connect the dots between that and your actual data model? Yeah. Similar to I think our our CRM has that kind of capability where you can build a form. Right? And then behind the scenes, the data gets stored wherever you direct those fields to store. Exactly. But you've got an and it's embeddable as a you know, you can get it as a link or you can direct them straight to the form or you can embed it as an embeddable link underneath the hood. Because the one thing I do recognize about the current share system is that it's a little bit a little very opaque. What's the right word? It's a little opaque to to know what you're sharing. I mean, by default, you're sharing what you're seeing. Right? It's like you're sharing the item that you're currently seeing. Yep. But what's actually gonna be shown to the user is different. Right? So you have to go check the link and see what it's gonna look like versus It's because it's based on the role that you then associate with the share. So if you use your own role, it's what you see is what you get. But other than that, it's a bit of a, you know, to your point, you have to create the share, pull it up, double check. If you're creating a dedicated form to share, be it read or write, that confusion is taken away immediately because it's the form that you created. It's the form that you're seeing. Right? I think the big question then becomes, how do we handle permissions if at all? Do we auto generate the permissions based on the stuff you put in the form? And therefore, you make it very explicit. Like, you put, an email address field, so therefore, you can see an email address. Or do we make it a little more opaque again by associating a role or multiple policies to TM to control the permissions for that shared item. Right? That's that's where it's a little finicky. So when when I meant that permissions, for example, if, where permissions become really important is for nested relationships. Right? So if you have a read item with a many to one field to a related category or something, what part of the category are you also supposed to be able to read from this entry point of the top level item? So having some sort of permission set there is gonna be required. Because otherwise, theoretically, if your relationships are a little complicated, you could pose a hell of a lot of data. Right? Because if we were to say, auto generate all of them, if you have a user created field because you just wanna show somebody's name, avatar, you could now theoretically go to that user record and you go to a direct to files record because you now have that connection through the user. Right? So that also, makes me wonder, there's another feature request that is completely unrelated to this, but it might actually directly be related now. One that I opened, I feel like 20 years. It's configuring access control permissions based on a parent child. So instead of saying, you have access to direct these files, you have access to direct these users. It's saying the permission, you can read a file if it's attached to a user if you're coming through the user and only then. Oh. Oh, yeah. Yeah. Yeah. Yeah. Yeah. That's, that would be very cool. That would be very cool. Top the clock. I think we need to need to start a new game. It's like, at what point do we get Jonathan to do that? It doesn't take much. It doesn't take much. Because because that would that would also potentially solve for this. Because then you could say, okay. If you have many to 1 on the record that you're sharing, you now have access to read just that one record from the related table, but nothing else from the related. Right? And for the record, that is how the current share system works actually under the hood. It is a hell of a lot more complicated, because what it does is it looks at the item that you're sharing and it checks what items are related, and then it makes a new permission set that says you can read just that one item from the related table. Right? But all of the other API ends work the same, which is interesting. But that's that's the way it does it right now. So it does actually, you know, specifically look up which items are related, which items are shared as per the role, but then lock it down to only allow you to ex that one particular item that is associated to the shared thing to make sure that we don't overexpose data in your system. Food for thought. But it it, you know, we're getting real quick into the weeds here again. Just from the question, is it sharing the existing form from your data model or are we sharing new one off forms? It sounds like and I've I've it it sounds like we probably wanna look into some sort of system. You can create individual forms and associate them back to your data model. Just in those cases where you wanna have multiple different forms with different fields that go to the same item. Commonly the case. Right? So if I was setting up a, you know, a a newsletter subscription, I might have very simple, but I still wanna register the contact. I still wanna be able to, you know, track their compliance or other things that I may wanna track or need to track versus the same contact information I would collect for a you know, you're signing up for a meeting or a webinar or something else. So the the forms can be storing data to the same places or to additional places. Follow-up question. What is that? One of these public forms, and I'll I'll get to Tim's question in the chat very soon because that's another level of complexity. But should we allow one of custom forms to save to multiple tables at the same time? Yes. Technically, yes. Form can one form create 5 different records in 5 different tables? Oh, I think it must. Right? Because of relations? Well, not even relations. Go Independent of relations. Right? Yeah. So if I'm if I'm setting up a form to collect, medical data, you know, to sign you up or set you up or do something or I'm collecting a survey, I might have survey information that I'm storing in the survey table, contact information I'm storing in the contact table. Just simple example. Right? Just 2 tables. But the contact information is in one place, the survey data is somewhere else. And I may from a from a compliance perspective, I may not be associating the user. I'm just tracking that the user responded, but the survey data has to be independent. Right? No relation at all. Here's one quick question. Good point, but Tim, wait beef before we go further further down the rabbit hole, there's another good point by Tim. It it it really sounds like a job for a flow or hook. And I think I agree, actually, because, like, the complexity then of setting it up and then the UI that has to be added for that and, stuff that could go wrong and stuff. Like, maybe, you know, just as a as a simple example. Okay. You have you just make one row in one table. That would that could be the cutoff that we decide on, maybe. I don't know. But it it really sounds like maybe a hook or a flow would then should be responsible for, you know, taking the the depending on the status, it takes the user's email and puts them also into the contacts table or the whatever table. I think I agree with that take. How do you guys feel about that? It's both ways because you could say you'd save all the data to a table and then run the flow based on that change or you save the form to a flow and then handle it yourself. I and I think there's a case to be made for both because for a simple contact us form that is always going through the same table in the same format and it matches your data model, it feels like flows could be a lot of, you know, complexity to configure just for that simple use case. But for everything else, I fully agree. Once you go, I wanna touch 5 different tables and I wanna submit to a different web, and all that kind of stuff. Yeah. Form to a flow, makes a lot of sense. Right? Right. So let's blow up this, let's blow up this discussion further because Tim also said, alright. How about, should public forms be able to save to a specific content version as well. If you call the first deal with the safety of a specific content version. Well, the nice thing is if we're going with this whole form safe to flow as idea, then sure do whatever you want. Right? That's that's kind of the beauty of the escape hatch that could be flows as the back end forms. Because at that point, if you wanna save it to a conversion, go for it. Right? What do we care? What when how when would you use that, though? Let's think about that for a second. So you have draft state basis for an item. I guess if you were updating existing content, you may wanna do that as a version, possibly. Typically, in shareable forms, I don't see it as a version necessarily. But if you were allowing someone, say, to update but even then revisions would track the the content changes over time if you were inclined. I I think yeah. What I was just about to say is what Tim again just put in the chat. Let's say you want to review and approve the changes that were made in pub. Right? So if you're running a sort of Wikipedia style, Wiki style, page Mhmm. And you wanna allow people to suggest edits, you could have a public form to update that one particular page, and then those updates go into a version that then can be reviewed and approved by the old person in charter, which is an interesting an interesting use case. That's a cool that's a cool freaking use case. Which actually would be very convenient for our own docs for that record. For example, that's what I was thinking about. I have the little button on the bottom here. Edit this page. Edit this line. That would be very cool. Hang on. Another use case from the audience in, in that updating existing thing from, Jay Shue says, another use case is a business directory and the users being able to suggest changes. Agreed. And the Durb saying we could actually use that fairly well as well. That's it. It could be implemented in flows. Right? Which is where it gets interesting again. Right? Because flows really become that sort of escape hatch for this type of stuff that, like, oh, you wanna save it to a version instead of to a table? You wanna save it to a different whatever you wanna do. Just use a flow. It it does also answer the question, is this just for create or is this also for update? And I think we've just concluded that also updates are important. Click updates. That's when you can do you can update an existing submission that you've already done, or you could, you know, the Wiki example or the business directory example or some of those. Yep. I think about, in particular, things like, you know, some of these online forms could be fairly complex. Right? I've I've done this. I've I've got you know, you're filling out something for a financial thing and you, you know, it's gonna take you an hour to do the form while you may be saving and coming back later. You can come and go from it. We do a lot with secured online security forms, you know, where we're filling out answers to complex questions that take hours or days to finish. And so, essentially, saving state and coming back and making updates and, oh, I mean, I I wanna fix that answer that I did, you know, 3 pages back in the form, and I wanna go make that adjustment. I think we found another use case for the saving it to a version. Mhmm. Because if you save it to a version, the validation of the database doesn't have yet because it only needs the saves to the database rather than to your stage to changes. Right? So the, what would you call it, plausible forms where you can enter a bunch of stuff, hit save as draft basically, and then come back later. That would be relying on content versioning. Good. Yep. Good very well. Doesn't have to, but it could. Right? You can manage that via state or status or other things as well. But, yeah. Unless you're thinking about validation in that case. Right? If you have data integrity rules that say the column cannot be So in your form, it has to be filled out. But you don't want the force to use the whole form once, you need to have an in been safe, which would be, you know, a version conversion version. I like it. It's Okay. Let's I need to switch to a slightly simpler question. Otherwise, my brain is gonna go What about deletes? Is Is that a thing you do publicly? My first gut reaction was no. Hell no. But, let's give it a little more thought. But I guess if it's user data, I don't know. I could see a use case for it. I don't I don't think from our side of things, if depending on how it's implemented, If you got compliance issues, GDPR, other kinds of things where the user has the right and choice to do that, that's that's gonna it's gonna be very use case specific. Because for the case we we try not to delete data in general. We put it in the soft archival state. You know, we use status or other things to handle that. But where you have compliance requirements where you must delete, But I don't know if that's a form. That that could be flows driven. That can be log business logic driven at that point as opposed to something to do with the form itself. I think you'd have to have a form that says I wanna I want my data to later. I wanna unsubscribe. And by doing that behind the scenes, the flows and data data management happen the way that you need them to happen. Right. Because I I could imagine that the because because deletes blank at least sound kind of insane. Because you would have what? You would show the user, here's all the records and go whichever delete whichever one you want. It that that feels like not something that you'd realistically want. But I could imagine that if we connect it to an account again, not not so much a a direct as app account, but, like, the moment you create a form submission, I could imagine that you maybe wanna do a public read of a layout. Here's a new question that we're gonna discuss in 5 minutes. Where you can just as a user as a temporary user all the submissions that you've done in the past and then delete a previous submission. To me, again, business logic. Right? That that tends to be not so much I I think you'd I think someone suggested it here. I think you'd do it as a delete request. You'd submit a request to delete and then confirm whatever mechanisms. Your your business logic, your flows, your your any additional code processing that you're doing on top of that would handle those things and send notifications accordingly. Feels very much like you would submit that as a specific form request to remove data, not the ability to delete data from the form. I think I agree in general that that shouldn't be allowed. You'd have to submit that as a separate form, maybe. I don't know. I'm sure there's a use case that I'm not thinking of. But Yeah. But to your point, if you create a new form that triggers a flow rather than saves to a table, that flow then just deletes it, whatever, based on your business logic, then prop solved. Right? You you don't necessarily go the mile to Yep. Have native victim delete support for that specific of a use case. Okay. Interesting. Interesting. Interesting. Interesting. Tricky. Tricky. Tricky questions. Tricky questions. Another another question in the chat here from user. So what about editable forms or relational data where items can be deleted after it's been saved and the user edits the data? We got it. Ladies and gentlemen, we got it. That's the real goal. If we can get right to do it, then we're then we're successful. What about editable foreign situational data where items can be deleted as it's been saved and use it as the data? I mean, I guess that kinda goes for any editable forms that it's like, user creates it, admin changes it, user tries to edit it, and that looks different. Which, yes, it will look different because you changed it. You changed somebody's submission. Right? I think that becomes becomes more of a workflow or a business process, thing where tell your staff don't touch, you know, don't go changing somebody submission. That's bad mojo. But I guess yes. What about resubmitting of shareable forms? We're kind of in the midst of thinking this through because, in the beginning, we said, okay. If you have a fixed link that you can send somebody and then they submit the thing, we could restrict that you can only submit once with that link. Or you can have a general link where, like, every single submission results in a new row every time. But then it's very use case dependent, I think. No. Like, that should be just configurable. Okay. Is this, will this result then in a on on my second visit to that link, will it show me the actual item, or is the form empty again, and can I resubmit it? I mean, that's, like, very dependent on on on the configuration, I think. Yeah. I think it should be doable, though. Some additional configurations here, potential validation checks. So, again, if it's an embeddable form or it's a shareable, editable form from our side, then this is gonna need some updating as well. Right? We'll need some additional parameters and things here. You'd have to potentially check the data, validate the data on save. Flows, again, can handle some of that, but to give the user feedback of you've already submitted or disabling the form because they've already made a submission. How do we check that? What does what does that look like? Because now you actually have to have something, you know, activity wise that says your device or whatever has been registered. We know that it was you, and you've already submitted the form. Yeah. That gets that's where I I don't know. It almost feels like that should just be front end dev work and get managed from that side of things. But Oh, here's here's the problem though, Jonathan, when it comes to the form that we're sharing, who's the front end dev doing the work? It's it's us. It's us. It's us. I know. So this needs more complex kinds of things because now max 5 uses, well, is that 5 users, or is that 5 you know, one person could submit all 5 or use all 5 values. Right? Is a user? What is a user? Right? What is the Are we getting deep? What am I I mean, that's that's kinda the question right now. I think about like same thing. We we track this on, you know, AppSight anyway, but now we've got this floating thing out there that would also have to track an activity record of who the IP and device and things are and be able to validate against that as part of this form configuration or this shareable form configuration. Now it's, you know, one time per user or per IP or, you know, whatever we consider that to be. There'd be something here, you know, max uses. So I want a 1,000 submissions, but then I wanna be able to also say per something. The the problem of the the how do you confirm the per something? Because you could say you could you could I mean, you could get away with it by just having a unique column. Right? That it's like you can only have one submission with this email address, and that's it. Done. It could be unique. Already exists. Can save it. Done. Right? But how do you prevent somebody, you know, with a Gmail account, for example, you can do the plus something something and it will come back to you anyways. You prevent somebody from submitting times with the same, you know, plus 1, plus 2, plus 3, that will work. And I can use all information still. Because there's no way for us to validate that. And then, you know, for email specifically, you can start thinking about, maybe we have some sort of email validation or we send an email and you have to click confirm or all that kind of stuff. But then what happens if you wanna use something else as the unique identifier? What if you're doing some of the phone numbers? Right? Where it's like, enter your phone number, we'll send you a text when it's your turn to to for for like reservations. Right? If you're doing a reservation at and it's it's that's that's very common around here. I don't know. Over in your parts of the world. But, you know, if you go to a restaurant here and it's full and it's like, oh, your your table's gonna be about 40 minutes and you your phone number, they'll send you a text when it's ready. Right? Mhmm. Or about 10 minutes before it's ready. So in that case, you wanna duplicate it on the phone number, but how can you confirm that? Right? You you can't really, there's no way for us to know that. A simpler a simple example would be if if you have first sorry, the delay delay is giving us. No, but but a simple example for, limiting that kind of, use would be, let's say you have a form that you can resubmit with, let's say, you have an online shop and then you want to use this for functionality for giving out promotion codes, and you only want to give out a 100 promotion codes, okay, Then it's just a matter of counting the rows, you know, so so we don't have to have a unique field or whatever. We just say, okay. This form can be submitted 100 times. That's it. I think there's like, it's a little different to your guys' use case, what you just described with the email and and phone and whatever. But, I mean, this is also a valid use case, I think. You know? I I want this form to just be usable for 100 submissions. Okay? Then it's just a matter of counting. Mhmm. Well, that's our that's already built in. Right? So we already support that max five uses. So and we keep track. Similarly, if we want this to be 1 per or 2 per source, right, then we have to actually track from a and, I guess, because it's an embeddable form or it's a, you know, we we have information and knowledge about the browser. Right? We we can track that. It just requires that we store the activity information and utilize that as part of the additional values here in a sense. There's still ways around. Right? There's always a way around. I've got 5 devices, and I wanna submit 5 submissions. You know, I can I can do that, but and and, you know, we can do things like enforcement of uniqueness on the values coming in and those things? That's already there. It's more about can I at least track to know that this user has already submitted a form because of their IP address slash device ID, that we track anyway from the browser cookie side of things? I think, I think I'm reaching the same conclusion as Tim. It's like we're going down the bot detection rabbit hole, which is like, can we fingerprint an individual user? No. There's no way. There's there's no way because IP address is shared with the location. So, therefore, everybody in the same household or the same business is gonna have a similar bound IP address. So that that doesn't work. I mean, this this fingerprint in question has been a cat and mouse between browser makers and, you know, back end implementers. It doesn't exist. The only way to do that is to require a sign through some sort of secondary authentication method, like send you an email with a magic link. So therefore, you prove you have access to the email, and that's it. Right? Before it submits or text with a phone number, that kind of stuff. Or log in through an SSO provide to confirm your, you know, your identity there, which actually is a different question I didn't even think about. But it would be kinda nice if you could sort of, like, connect the dots between what is your GitHub account click here to to o auth your way in to confirm, but that's not gonna be a 5 minute conversation, I don't think. Another question from the chat here, from from user and I'm gonna butcher your name. Sorry about that. Mama. That's what I'm gonna go with. How about the ability to show already submitted values to the user when filling out a form? Thinking about a sign up form for a party, and showing what food others already bring with them. Which is so this is an interesting question, which I think is not so much, the shareable, writable form. This is the question of readable lists of items. So right now, the share that we offer is just for a single item. You share that one. Right? What we don't allow you to do yet though, you can go to a layout and share the whole in a simple fashion. I could imagine that for this particular use case, what you would do is you would basically make a public share for your record of foodstuffs people bring to the party, long collection name, and then you can give people that link. Right? And then when they go there, they just see a layout like you would expect from directives with all of the stuff that has been previously entered with a click to to open an individual, I imagine, and then a, you know, a plug or something to go to the create page if you have create permissions through that public share. I think this is actually related to Hannes' question as well. More so because the food items being brought can be a many to many or a relational junction that's already there. So we'd show that in the current form. Right? Just like we did, you know, I only have translations in here right now, but equivalent kinds of things where you've got a a relational structure. The real question becomes when you're adding or removing relational items, this is considered potentially a delete operation under the hood. Right? So there there's there's a little bit of a tie between the delete conversation we were having and this ability to show or allow or not allow you to you know, you might be able to see what other people have brought, but you can't remove their food item. You can only add your own food item. Right? And or remove your own food item. I think role permissions generally work correctly for that, but it becomes a question of making sure that you test and set those things correctly. But is that considered a delete? Right? If you've got a cascade operation on, you know, removing a a relational item, well, that's potentially gonna cascade and cause a delete down the line. I think that would always be a deselect from the relational perspective. The Well, that depends on what you set your permissions for. Right? Oh, it's what you do. It's not permissions. The field oh, I guess it's what you set your field settings for. Right? If you set by default, yes, we set it to nullify so we don't actually delete any data. But if they've got cascade delete permissions, you know, if the cascade delete is turned on, that's going to actually delete data or contact. Point. Yeah. So the that's here's the security vulnerability way to happen. If you have an edit access to a simple item with a many the one many the one is configured to delete on select. If the user edit the field and they deselect, they now can theoretically delete stuff in your database, which is fun. Alright. Oh, jeez. It's already we're very we're already on on the dot here almost at 11. Oh, yeah. 11 for me. Times for everybody else. Just to to summarize all of this, because we went very much like this, but not a lot like that yet. We're running out of time. I think some some initial conclusions as far as I sort of take it now from this session. Right? It's like there's definitely a difference between shareable forms and embeddable forms. Shareable forms is part 1. Embeddable forms can then reuse whatever configuration that has for embedding stuff. I think that's conclusion number 1. I think to me, conclusion number 2, it's ideally you can create forms separately from the data model. Model. Although, that is quite a bit, you know, more involved to build and experiment with and etcetera etcetera. I think starting with create access is the most straightforward as you don't have problems with who can read what, how do you update previous submissions, how do people know what submissions they've previously done, etcetera. So in order of operations, shareable pages create first, then, read access for a listing would be very useful because then you have a way to click into an update item there. And then I think as a sort of your v 2, we should think about what do we form creation to look like in the first place and how does that tie into sharing? And a different question is, should sharing even be tied to collections and items, or is it just pages in the app altogether? Because I can also imagine that there's other places, like, folder in your file library that you might wanna share. Don't know. Like, another feature request for a different day, because I got skipped is, what about shareable dashboards from Insights? That'd be cool. But let's not do that right now. We'll save that for a different session. For now, Daniel, back to you to close it out. Thanks everybody for coming. If you enjoyed this discussion or are interested in other discussions, you can head over to directors. Iotv where you can find lots of other interesting shows and also this one. And, we hope to have you to see you again.","51185b87-e60a-4d5d-bf15-e09c4755ddd1",[3623,3624,3625],"3eb120ac-4151-41d7-912b-e93b6568e197","93e88d6a-6f8d-4006-8efe-b1297a350658","901b7577-4fbf-48e6-84bd-f18438989693",[],{"year":978,"number":11,"id":1787,"episodes":3628,"show":3629},[1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1768,1800],{"title":1802,"slug":1803},{"id":3373,"slug":3631,"vimeo_id":3632,"description":3633,"tile":3634,"length":13,"resources":12,"people":12,"episode_number":889,"published":3635,"title":3631,"video_transcript_html":3636,"video_transcript_text":3637,"content":12,"seo":12,"status":19,"episode_people":3638,"recommendations":3639,"season":3640},"404","893668097","Today we're searching the ever-elusive file that's not found.","dbaeede0-3f50-4f57-b453-ed3c75e3bb9f","2024-04-10","\u003Cp>Speaker 0: And now Dev Thoughts by Jack Rabbit. Whenever I get a four zero four file not found error, I can't help but wonder if maybe, just maybe, that file is hiding somewhere on the server, giggling to itself and whispering, Shh. They're looking for us.\u003C\u002Fp>","And now Dev Thoughts by Jack Rabbit. Whenever I get a four zero four file not found error, I can't help but wonder if maybe, just maybe, that file is hiding somewhere on the server, giggling to itself and whispering, Shh. They're looking for us.",[],[],{"year":978,"number":263,"id":3362,"episodes":3641,"show":3642},[3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3350,3378],{"title":3380,"slug":3381},{"id":3372,"slug":3644,"vimeo_id":3645,"description":3646,"tile":3647,"length":13,"resources":12,"people":12,"episode_number":527,"published":3635,"title":3648,"video_transcript_html":3649,"video_transcript_text":3650,"content":12,"seo":12,"status":19,"episode_people":3651,"recommendations":3652,"season":3653},"ex-variables","893668029","Code so dramatic it deserves it's own reality tv series.","9956223a-0c38-4e12-85a4-c16688d4752e","Ex Variables","\u003Cp>Speaker 0: And now Dev Thoughts by Jackrabbit. I once thought about\u003C\u002Fp>","And now Dev Thoughts by Jackrabbit. I once thought about",[],[],{"year":978,"number":263,"id":3362,"episodes":3654,"show":3655},[3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3350,3378],{"title":3380,"slug":3381},{"id":3282,"slug":3657,"vimeo_id":3658,"description":3659,"tile":3660,"length":740,"resources":12,"people":3661,"episode_number":264,"published":3665,"title":3666,"video_transcript_html":3667,"video_transcript_text":3668,"content":12,"seo":12,"status":19,"episode_people":3669,"recommendations":3671,"season":3672},"terence","925121454","Terence is an open source hacker and tinkerer, works in civic tech, and here's the software, hardware, and analog tools he uses in his day-to-day.","5f9300e4-04f8-4092-8e5d-1d7015c0e222",[3662],{"name":3663,"url":3664},"Terence Eden","https:\u002F\u002Fedent.tel","2024-04-09","What's In Your Dock, Terence?","\u003Cp>Terence: Hello, the Internet. My name's Terence Eden. I'm an open source hacker and tinkerer, and I work in civic tech, and this is what's in my dock. This is my hardware setup. I use a cheap generic Linux laptop, which works beautifully, but I pair it with a very big vertical monitor because I spend a lot of my time reading very long documents, and so I just like that nice big vertical screen.\u003C\u002Fp>\u003Cp>I use an ergonomic keyboard to keep my hands like that, a nice rollerball. I'm also, if you see me standing around it's because, I use a standing desk, very good for the back, and somewhere under there I've got a treadmill so that while I'm working I can, get my steps in. In in terms of other hardware, well my phone is it's like a 6 and a half year old Android, it's a OnePlus. But what I do is I pair all of my hardware with magnetic charging devices. So I can plug something into the socket, and then I just have a magnetic cable, which charges all of them.\u003C\u002Fp>\u003Cp>And that just means that I really only need to carry 1 cable around, 1 battery around. So my day to day work involves reading and writing lots of code, lots of documents, so, I rely heavily on a clipboard manager. I use Pop!OS, which is a Linux distro, and the the great thing about the clipboard manager I find is that it just it stores all of my brains. If I've copied something from somewhere it is there in the history, whether it's a web address or a bit of text that I was using in one document, I want something else. I don't have to go hunting for it, it's just there in history.\u003C\u002Fp>\u003Cp>The other shortcut I make far too much use of is emoji shortcut. I just love peppering everything that I do with emojis. I don't care if that makes me a tweenager, I think it's cool, and being able to to one click type, what I'm looking for and have sparkle hearts just appear absolute magic. Because I work with lots of very secure systems, I'm constantly typing in usernames and passwords, so I use Bitwarden. Bitwarden is a free and open source password manager, password generator, it's on my phone, it's on my laptop, it's I I use it everywhere and it's just fantastic knowing that I've got super strong passwords for every service, and if I ever need to change 1 or create a new one, it's immediately synced between my devices.\u003C\u002Fp>\u003Cp>I don't have to remember 100 of complex passwords, and that's a a real time saver and and a lifesaver for me. The other tool I which I'm using right now is called droidcam, which is, a little gadget which turns my old Android phone up there into a high quality webcam, and there's a little Linux client running on my laptop, which enables me to speak to you in high def, which is very exciting because most laptops have rubbish webcams, so now I've got a big multi megapixel sensor, so you can really see how tired I look. And then, finally, one of the tools I find myself using a great deal is home assistant. I have far too much smart home gadgetry, so I've got my lights are all smart lights, and my fridge is a smart fridge, and I've got sensors and alarms and everything else, and it's great just to be able to see the state of my house, on on the dash, and to be able to turn lights on and off to set the alarm, to set up schedules. So I think when when you're living in a house with lots of technology, you need ways to manage that technology.\u003C\u002Fp>\u003Cp>Home assistant is, a brilliant open source bit of kit. It runs on a Raspberry Pi which is somewhere in my room, probably. I can ping it so it must exist. I find music incredibly distracting when I'm working. I if someone is singing, I find myself typing what they're singing.\u003C\u002Fp>\u003Cp>So I either use, you know, the, the lofi hip hop beats, you know, the, the girl in the in the cafe, or instrumental songs, or sometimes I will just pick a language that I do not know and, just say play me songs in this language, and it it just becomes noise to block everything out. I use lots of weird and wonderful hardware. One of the ones which I'm using quite a lot at the moment are these which are bone conducting headphones. So rather than going in the ear, they sort of rest there and they rattle your head when they put the sound in, which is fantastic because it means that your ears are free, and you can hear what's going on around you. The other piece of hardware which I use quite a lot are are these, which are just typing gloves.\u003C\u002Fp>\u003Cp>So you put them on, stops you from getting RSI. If you're working a lot with computers, you you cannot look after your hands too much. So the the other thing that I've got, I'll see if I can get that up on camera here, is a, a trackball, so I can mouse like that rather than gripping a mouse. And then down here, I I love this keyboard so much I've got like 5 of them down there. This is a Microsoft ergonomic keyboard, other brands are available.\u003C\u002Fp>\u003Cp>But but again it just means that my hands are spread out, so that I'm not, constantly cramping. And, I'm currently on a standing desk. I really can recommend like a cheap standing desk adapter, if you've already got a desk. It's it's not very expensive and it just means that get your posture right, get a nice foot mat as well. I I spent a bit too much money on a walking desk thing, so it's a it's a little treadmill that goes under there, but anything that keeps you active while you are at a computer is good, because otherwise you'll end up punched over like that, and you'll you'll mess your body up.\u003C\u002Fp>\u003Cp>And then the final piece of hardware, that I use is this, which is, an Oculus Quest sorry, they like to be called Meta now, don't they? Meta Quest 2, and I I like this because every time I put it on it reminds me that some tech is just rubbish. You know, the the metaverse is absolute nonsense, but you can go in there and go, this looks so pretty, but it's useless for work, and every time a client says, oh, should we do something in the metaverse? I put this on and go, no, no, that's a bad idea. All all of the gear that I have around me is USB C connected.\u003C\u002Fp>\u003Cp>We live in the future now, so microphone is USB C, I've got a little label printer, which is USB C, phone and headphones and just just everything. There there is no point getting a gadget and go, oh, I need to get another proprietary cable. So whenever I get something I always check is this gonna plug in to to my USB C dock, if it doesn't doesn't make the cut. And you know what? My my room is just filled with clutter of various raspberry pies and solar panels and, yeah.\u003C\u002Fp>\u003Cp>Solar tech is really cool. Just being able to charge stuff from the sun. I love it. So the the final thing, bit of hardware that I use is a, a wine crate, because it's got the the holes in a crate, which hold a bottle, are exactly the right size for shoving lots of cables in. So rather than having my cables all in a messy box, I've got a couple of wine crates, and I've got USB cables there and Ethernet cables there and so on and so forth.\u003C\u002Fp>\u003Cp>So that's my my tip to you is don't get a metaverse headset, buy a crate of wine, you will have a lot more use for it. I travel around the world a lot, which is a a great joy and privilege, and so I like to update OpenStreetMap, which is sort of like the the Wikipedia of Atlas', and there's a wonderful app called Street Complete, so you don't need to know anything about cartography. It presents like a sort of Pokemon Go interface, so as you're wandering around it will say what are the hours of this shop over here? Or what's the name of this street, how many stairs are there here, is this place wheelchair accessible. So you get all these little quests, and you go along on street complete as you're wandering around the city, and you just sort of tickmock.\u003C\u002Fp>\u003Cp>I think that's great fun, and they have leaderboards, and you get trophies and stuff like that, but the the real joy is contributing to to global mapping. The other hobby that I have, my wife and I run a website called openbenches.org. If you're ever around and you see a memorial bench, you know, one of those benches, a little pack, and it says, in loving memory of so and so who used to love coming here, take a geotagged photo, upload it to to our website. We've now got, I think, over 30,000 benches from around the world that that people have contributed. And it's wonderful just seeing where memorials to to everyday people are.\u003C\u002Fp>\u003Cp>And and, yeah, it's all free and open source, so come and have a play with that. And then finally, my I suppose my last hobby is watching Doctor Who. Absolutely love a bit of Doctor Who. There's no real technology needed other than a Blu ray player or a DVD player, but what I find absolutely fascinating is tracing how the ideas from early stories make it into modern stories, and how some of the filming techniques are completely outdated, but you can see what they're trying to do. And yet, even when the special effects are a bit ropey, what you can see is the idea of something interesting, and that gives me hope for all the things that we built.\u003C\u002Fp>\u003Cp>So when I look back at my old websites that I built back in the late nineties, they were rubbish, but I can see exactly what I was trying to do. And that gives me hope that when people look at what we're doing now, they'll go cool, that that looks a bit rubbish, but we can see we can see the path that was taken from the olden days into the future. So I would definitely recommend finding an old TV show that that you like and watching it through from the beginning. So I don't keep many things analog. I think the only thing I do is I I fidget a lot.\u003C\u002Fp>\u003Cp>So I've got lots of fidget toys like this which are just little things and bits that I have hanging around so that when I'm talking on calls like this, my hands can be busy fidgeting and hopefully not clicking too much near the microphone. But I I've been looking around my house to see what I've got that's analog, and other than me and my wife, I don't think there is anything. And that's it. That's all the gear that I use, the the hardware, the software, all the music I listen to. Everything is USB C powered, even the headphones that I listen to to music through.\u003C\u002Fp>\u003Cp>I I've had so much fun talking to you today. If you'd like to follow me on social media, you can find me on mastodon or you can visit my blog, and that's at schukspert.mobiblog or contact me other ways edent dot tel. Thanks for having me. Bye.\u003C\u002Fp>","Hello, the Internet. My name's Terence Eden. I'm an open source hacker and tinkerer, and I work in civic tech, and this is what's in my dock. This is my hardware setup. I use a cheap generic Linux laptop, which works beautifully, but I pair it with a very big vertical monitor because I spend a lot of my time reading very long documents, and so I just like that nice big vertical screen. I use an ergonomic keyboard to keep my hands like that, a nice rollerball. I'm also, if you see me standing around it's because, I use a standing desk, very good for the back, and somewhere under there I've got a treadmill so that while I'm working I can, get my steps in. In in terms of other hardware, well my phone is it's like a 6 and a half year old Android, it's a OnePlus. But what I do is I pair all of my hardware with magnetic charging devices. So I can plug something into the socket, and then I just have a magnetic cable, which charges all of them. And that just means that I really only need to carry 1 cable around, 1 battery around. So my day to day work involves reading and writing lots of code, lots of documents, so, I rely heavily on a clipboard manager. I use Pop!OS, which is a Linux distro, and the the great thing about the clipboard manager I find is that it just it stores all of my brains. If I've copied something from somewhere it is there in the history, whether it's a web address or a bit of text that I was using in one document, I want something else. I don't have to go hunting for it, it's just there in history. The other shortcut I make far too much use of is emoji shortcut. I just love peppering everything that I do with emojis. I don't care if that makes me a tweenager, I think it's cool, and being able to to one click type, what I'm looking for and have sparkle hearts just appear absolute magic. Because I work with lots of very secure systems, I'm constantly typing in usernames and passwords, so I use Bitwarden. Bitwarden is a free and open source password manager, password generator, it's on my phone, it's on my laptop, it's I I use it everywhere and it's just fantastic knowing that I've got super strong passwords for every service, and if I ever need to change 1 or create a new one, it's immediately synced between my devices. I don't have to remember 100 of complex passwords, and that's a a real time saver and and a lifesaver for me. The other tool I which I'm using right now is called droidcam, which is, a little gadget which turns my old Android phone up there into a high quality webcam, and there's a little Linux client running on my laptop, which enables me to speak to you in high def, which is very exciting because most laptops have rubbish webcams, so now I've got a big multi megapixel sensor, so you can really see how tired I look. And then, finally, one of the tools I find myself using a great deal is home assistant. I have far too much smart home gadgetry, so I've got my lights are all smart lights, and my fridge is a smart fridge, and I've got sensors and alarms and everything else, and it's great just to be able to see the state of my house, on on the dash, and to be able to turn lights on and off to set the alarm, to set up schedules. So I think when when you're living in a house with lots of technology, you need ways to manage that technology. Home assistant is, a brilliant open source bit of kit. It runs on a Raspberry Pi which is somewhere in my room, probably. I can ping it so it must exist. I find music incredibly distracting when I'm working. I if someone is singing, I find myself typing what they're singing. So I either use, you know, the, the lofi hip hop beats, you know, the, the girl in the in the cafe, or instrumental songs, or sometimes I will just pick a language that I do not know and, just say play me songs in this language, and it it just becomes noise to block everything out. I use lots of weird and wonderful hardware. One of the ones which I'm using quite a lot at the moment are these which are bone conducting headphones. So rather than going in the ear, they sort of rest there and they rattle your head when they put the sound in, which is fantastic because it means that your ears are free, and you can hear what's going on around you. The other piece of hardware which I use quite a lot are are these, which are just typing gloves. So you put them on, stops you from getting RSI. If you're working a lot with computers, you you cannot look after your hands too much. So the the other thing that I've got, I'll see if I can get that up on camera here, is a, a trackball, so I can mouse like that rather than gripping a mouse. And then down here, I I love this keyboard so much I've got like 5 of them down there. This is a Microsoft ergonomic keyboard, other brands are available. But but again it just means that my hands are spread out, so that I'm not, constantly cramping. And, I'm currently on a standing desk. I really can recommend like a cheap standing desk adapter, if you've already got a desk. It's it's not very expensive and it just means that get your posture right, get a nice foot mat as well. I I spent a bit too much money on a walking desk thing, so it's a it's a little treadmill that goes under there, but anything that keeps you active while you are at a computer is good, because otherwise you'll end up punched over like that, and you'll you'll mess your body up. And then the final piece of hardware, that I use is this, which is, an Oculus Quest sorry, they like to be called Meta now, don't they? Meta Quest 2, and I I like this because every time I put it on it reminds me that some tech is just rubbish. You know, the the metaverse is absolute nonsense, but you can go in there and go, this looks so pretty, but it's useless for work, and every time a client says, oh, should we do something in the metaverse? I put this on and go, no, no, that's a bad idea. All all of the gear that I have around me is USB C connected. We live in the future now, so microphone is USB C, I've got a little label printer, which is USB C, phone and headphones and just just everything. There there is no point getting a gadget and go, oh, I need to get another proprietary cable. So whenever I get something I always check is this gonna plug in to to my USB C dock, if it doesn't doesn't make the cut. And you know what? My my room is just filled with clutter of various raspberry pies and solar panels and, yeah. Solar tech is really cool. Just being able to charge stuff from the sun. I love it. So the the final thing, bit of hardware that I use is a, a wine crate, because it's got the the holes in a crate, which hold a bottle, are exactly the right size for shoving lots of cables in. So rather than having my cables all in a messy box, I've got a couple of wine crates, and I've got USB cables there and Ethernet cables there and so on and so forth. So that's my my tip to you is don't get a metaverse headset, buy a crate of wine, you will have a lot more use for it. I travel around the world a lot, which is a a great joy and privilege, and so I like to update OpenStreetMap, which is sort of like the the Wikipedia of Atlas', and there's a wonderful app called Street Complete, so you don't need to know anything about cartography. It presents like a sort of Pokemon Go interface, so as you're wandering around it will say what are the hours of this shop over here? Or what's the name of this street, how many stairs are there here, is this place wheelchair accessible. So you get all these little quests, and you go along on street complete as you're wandering around the city, and you just sort of tickmock. I think that's great fun, and they have leaderboards, and you get trophies and stuff like that, but the the real joy is contributing to to global mapping. The other hobby that I have, my wife and I run a website called openbenches.org. If you're ever around and you see a memorial bench, you know, one of those benches, a little pack, and it says, in loving memory of so and so who used to love coming here, take a geotagged photo, upload it to to our website. We've now got, I think, over 30,000 benches from around the world that that people have contributed. And it's wonderful just seeing where memorials to to everyday people are. And and, yeah, it's all free and open source, so come and have a play with that. And then finally, my I suppose my last hobby is watching Doctor Who. Absolutely love a bit of Doctor Who. There's no real technology needed other than a Blu ray player or a DVD player, but what I find absolutely fascinating is tracing how the ideas from early stories make it into modern stories, and how some of the filming techniques are completely outdated, but you can see what they're trying to do. And yet, even when the special effects are a bit ropey, what you can see is the idea of something interesting, and that gives me hope for all the things that we built. So when I look back at my old websites that I built back in the late nineties, they were rubbish, but I can see exactly what I was trying to do. And that gives me hope that when people look at what we're doing now, they'll go cool, that that looks a bit rubbish, but we can see we can see the path that was taken from the olden days into the future. So I would definitely recommend finding an old TV show that that you like and watching it through from the beginning. So I don't keep many things analog. I think the only thing I do is I I fidget a lot. So I've got lots of fidget toys like this which are just little things and bits that I have hanging around so that when I'm talking on calls like this, my hands can be busy fidgeting and hopefully not clicking too much near the microphone. But I I've been looking around my house to see what I've got that's analog, and other than me and my wife, I don't think there is anything. And that's it. That's all the gear that I use, the the hardware, the software, all the music I listen to. Everything is USB C powered, even the headphones that I listen to to music through. I I've had so much fun talking to you today. If you'd like to follow me on social media, you can find me on mastodon or you can visit my blog, and that's at schukspert.mobiblog or contact me other ways edent dot tel. Thanks for having me. Bye.",[3670],"367710c7-75fd-49fb-ad30-b27fa8ee51e3",[],{"year":978,"number":3277,"id":3278,"episodes":3673,"show":3674},[3280,3281,3282,3283,3262],{"title":1027,"slug":1028},{"id":2750,"slug":3676,"vimeo_id":3677,"description":3678,"tile":3679,"length":129,"resources":12,"people":12,"episode_number":264,"published":3680,"title":3681,"video_transcript_html":3682,"video_transcript_text":3683,"content":12,"seo":12,"status":19,"episode_people":3684,"recommendations":3686,"season":3687},"the-home-depot-theme","931206231","We should all take more time to share in each other’s successes as we accomplish our goals. However, it isn’t just about what you can do, but the journey of getting there. Join Bry as he supports you while painting a new theme inspired by Home Depot.","9446dd56-865f-4d05-abc0-99dc4f31c008","2024-04-08","The Home Depot (How Doers Get More Done)","\u003Cp>Bry: It's a beautiful day in the neighborhood. Oh. Hi. Welcome back to the show. Sorry.\u003C\u002Fp>\u003Cp>I was just singing the theme to our sister show, Mister Miner's Neighborhood, which you can also find on DBS, the Directus Broadcasting System. Alright. Well, welcome back to the show. I'm your host, Bry Ross. And today, we've got another extra special episode of The Joy of Theming.\u003C\u002Fp>\u003Cp>Super excited that you're here. I hope you've got your direct us canvas laid out with a beautiful data model just ready to customize and beautify. Today, we are going to be painting the king of DIY themes, The Home Depot. The first thing I always do when crafting a theme, aside from the data model, is the project settings. So we're just gonna name this The Home Depot, and the slogan recently changed for Home Depot or at least a few years ago.\u003C\u002Fp>\u003Cp>It's how doers get more done. Couldn't be a more perfect tagline. The joy of theming is how you get theming done, so I really like that. We're gonna dive in and let's just copy the project logo. I'm gonna drag and drop this.\u003C\u002Fp>\u003Cp>Now, next I'm looking for the perfect background image and I'm not really sure what we want to do here. There's so many different options on the Home Depot website And sometimes when you're theming, and you only have a short period of time, it's so hard to select a good image. Now a the Home Depot, maybe we can go into some of their careers section, corporate information, and and just look. Let's oh, wow. Look at now.\u003C\u002Fp>\u003Cp>This looks like the founders of Home Depot potentially. So let's import that and use it as our public background. That's great. I I really like this built from scratch. It's got that distressed texture.\u003C\u002Fp>\u003Cp>It should look really beautiful in the public foreground right above that public background. Alright. So now that we've got kind of the basics of our theme laid out, you can see on the the module bar over just just right there, the module bar is is all black. Now, do I really want it to be all black? I I don't think I do.\u003C\u002Fp>\u003Cp>I want it to match the website. So let's pick up that orange color. We're gonna use that for our project color. And I I don't even know what to to call this orange. It's, like, burnt red, sienna, orange.\u003C\u002Fp>\u003Cp>Just just a beautiful shade of orange. It's comforting and eye catching at the same time. So we'll just smear that on the project color and it looks like we've got all of our icons in the list changed. We've got the the top of the module bar there, but we we don't have the module bar. I want that to be orange.\u003C\u002Fp>\u003Cp>I want that to match the website. But, you know, going back to the website, if we study this, we can see that the icons in the navigation, they all are orange on hover. They're black when not hovered. So we can just go in and we're gonna just, in our primary color for the thing, we're just gonna slide that project color just right across that canvas. And now we can see on hover those icons turn orange, but there's a slight background color problem.\u003C\u002Fp>\u003Cp>And again, there are no problems. These are just opportunities to to paint a better theme. We're gonna go into the modules and into the background hover. Just gonna set this to f f f which is just white. It's just a short hex code for white.\u003C\u002Fp>\u003Cp>Nothing fancy. Nothing crazy. You can do this. You've got this. The next thing I want to do is just dive into the border color.\u003C\u002Fp>\u003Cp>I've I've got this RGB color and I see a a Tailwind border opacity, which makes me think that Home Depot is using Tailwind. I love Tailwind. It's a great shortcut for theming. So but we'll go in, we'll just paste that into our background color, and that gives a little more pop to the background or the borders. It's a subtle difference, but borders really make a theme and not enough people focus in on it.\u003C\u002Fp>\u003Cp>But when you put all of these pieces together, you can build really beautiful themes. So, now, we'll just fix our icons. Within that navigation, I want those to be the project color just to add a little more pop of orange, a little more eye catching, but also very pleasing. So the next thing that we wanna do, you'll notice that the headlines on the Home Depot website, they have a condensed font. Looks like Helvetica nude to me.\u003C\u002Fp>\u003Cp>I did take a font class in technical school after I got out of the, merchant marines. And we're just gonna look for that font face. So we'll take their, CSS here and just paint that into the custom CSS inside Directus. Alright. So inside our instance, we're gonna go back up to our font section and just use Helvetica Condensed.\u003C\u002Fp>\u003Cp>And now you can see in our header, that looks great. We've got that condensed Helvetica font, but we need to uppercase it just to make it pop. It needs to be really nice. So we're gonna spread on just a little bit more custom CSS. I'm gonna take the palette knife and just spread that uppercase onto this beautiful theme.\u003C\u002Fp>\u003Cp>Type title, upper case. We've got this lovely upper case font. Looks great. It really matches. If we go to the data model, this looks great.\u003C\u002Fp>\u003Cp>It's got the same feel for the cards. We've got our product listings that look great. Are we done? This is really nice theme. I I love it.\u003C\u002Fp>\u003Cp>I hope you've enjoyed following along. Let's just pop out and take a look at the Home Depot login page. We've got the 2 founders there. It's built from scratch, as all the best themes are. Beautiful.\u003C\u002Fp>\u003Cp>Welcome to The Home Depot. That's it for this episode of The Joy of Theming. I've really enjoyed having you. Come back next time. We're gonna dive into some more techniques and just fun with theming inside your Directus project.\u003C\u002Fp>\u003Cp>I'm Bry Ross. We'll see you next time.\u003C\u002Fp>","It's a beautiful day in the neighborhood. Oh. Hi. Welcome back to the show. Sorry. I was just singing the theme to our sister show, Mister Miner's Neighborhood, which you can also find on DBS, the Directus Broadcasting System. Alright. Well, welcome back to the show. I'm your host, Bry Ross. And today, we've got another extra special episode of The Joy of Theming. Super excited that you're here. I hope you've got your direct us canvas laid out with a beautiful data model just ready to customize and beautify. Today, we are going to be painting the king of DIY themes, The Home Depot. The first thing I always do when crafting a theme, aside from the data model, is the project settings. So we're just gonna name this The Home Depot, and the slogan recently changed for Home Depot or at least a few years ago. It's how doers get more done. Couldn't be a more perfect tagline. The joy of theming is how you get theming done, so I really like that. We're gonna dive in and let's just copy the project logo. I'm gonna drag and drop this. Now, next I'm looking for the perfect background image and I'm not really sure what we want to do here. There's so many different options on the Home Depot website And sometimes when you're theming, and you only have a short period of time, it's so hard to select a good image. Now a the Home Depot, maybe we can go into some of their careers section, corporate information, and and just look. Let's oh, wow. Look at now. This looks like the founders of Home Depot potentially. So let's import that and use it as our public background. That's great. I I really like this built from scratch. It's got that distressed texture. It should look really beautiful in the public foreground right above that public background. Alright. So now that we've got kind of the basics of our theme laid out, you can see on the the module bar over just just right there, the module bar is is all black. Now, do I really want it to be all black? I I don't think I do. I want it to match the website. So let's pick up that orange color. We're gonna use that for our project color. And I I don't even know what to to call this orange. It's, like, burnt red, sienna, orange. Just just a beautiful shade of orange. It's comforting and eye catching at the same time. So we'll just smear that on the project color and it looks like we've got all of our icons in the list changed. We've got the the top of the module bar there, but we we don't have the module bar. I want that to be orange. I want that to match the website. But, you know, going back to the website, if we study this, we can see that the icons in the navigation, they all are orange on hover. They're black when not hovered. So we can just go in and we're gonna just, in our primary color for the thing, we're just gonna slide that project color just right across that canvas. And now we can see on hover those icons turn orange, but there's a slight background color problem. And again, there are no problems. These are just opportunities to to paint a better theme. We're gonna go into the modules and into the background hover. Just gonna set this to f f f which is just white. It's just a short hex code for white. Nothing fancy. Nothing crazy. You can do this. You've got this. The next thing I want to do is just dive into the border color. I've I've got this RGB color and I see a a Tailwind border opacity, which makes me think that Home Depot is using Tailwind. I love Tailwind. It's a great shortcut for theming. So but we'll go in, we'll just paste that into our background color, and that gives a little more pop to the background or the borders. It's a subtle difference, but borders really make a theme and not enough people focus in on it. But when you put all of these pieces together, you can build really beautiful themes. So, now, we'll just fix our icons. Within that navigation, I want those to be the project color just to add a little more pop of orange, a little more eye catching, but also very pleasing. So the next thing that we wanna do, you'll notice that the headlines on the Home Depot website, they have a condensed font. Looks like Helvetica nude to me. I did take a font class in technical school after I got out of the, merchant marines. And we're just gonna look for that font face. So we'll take their, CSS here and just paint that into the custom CSS inside Directus. Alright. So inside our instance, we're gonna go back up to our font section and just use Helvetica Condensed. And now you can see in our header, that looks great. We've got that condensed Helvetica font, but we need to uppercase it just to make it pop. It needs to be really nice. So we're gonna spread on just a little bit more custom CSS. I'm gonna take the palette knife and just spread that uppercase onto this beautiful theme. Type title, upper case. We've got this lovely upper case font. Looks great. It really matches. If we go to the data model, this looks great. It's got the same feel for the cards. We've got our product listings that look great. Are we done? This is really nice theme. I I love it. I hope you've enjoyed following along. Let's just pop out and take a look at the Home Depot login page. We've got the 2 founders there. It's built from scratch, as all the best themes are. Beautiful. Welcome to The Home Depot. That's it for this episode of The Joy of Theming. I've really enjoyed having you. Come back next time. We're gonna dive into some more techniques and just fun with theming inside your Directus project. I'm Bry Ross. We'll see you next time.",[3685],"8cdab202-6586-47d3-a831-1f79a5cf321e",[],{"year":978,"number":2745,"id":2746,"episodes":3688,"show":3689},[2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2731],{"title":2760,"slug":2761},{"id":3606,"slug":3691,"vimeo_id":3692,"description":3693,"tile":3694,"length":129,"resources":12,"people":3695,"episode_number":159,"published":3697,"title":3698,"video_transcript_html":3699,"video_transcript_text":3700,"content":12,"seo":12,"status":19,"episode_people":3701,"recommendations":3703,"season":3704},"looping-flows","903062109","Learn how to chain and set up loops inside of Flows inside of Directus Automate.","c10cf109-3a58-48a8-8106-c036cf10e597",[3696],{"name":3590,"url":3591},"2024-04-05","Looping Flows","\u003Cp>Speaker 0: Hi. Welcome back to another episode of Short Hops. I'm your host, Brian Gillespie. And today, I'm going to show you how to loop with flows. This topic comes up a lot inside the community.\u003C\u002Fp>\u003Cp>How do we chain flows together inside Directus? But if I back up a minute, flows are a great automation tool inside Directus. You can automate simple and complex workflows using the tools we provide, but there is a little bit of a learning curve, especially when you're trying to, loop through an array of objects or array of strings and perform some type of action on those within a flow. It actually requires 2 flows and we'll cover the steps in this quick video. So what are the steps needed to actually do this?\u003C\u002Fp>\u003Cp>We are going to create a first flow that collects our data that we want to loop through and perform an action on. And then we will create a second flow that actually performs those actions. And then last but not least, we to the first flow, we add an trigger flow operation and pass an array to that next flow, and that will do the looping for us. So, let's get back into the real world. A really common scenario here is I've got a list of users or subscribers that I want to loop through and send individual email notifications to or could be in app notifications.\u003C\u002Fp>\u003Cp>So I've got my subscriber list here, but let's go ahead and create our first flow. We'll just call it trigger email notifications. Perfect. For our setup here, the trigger, I'm just going to run this manually, and I'm going to select the subscribers collection. And I could, require somebody to select the individual subscribers here.\u003C\u002Fp>\u003Cp>I'm just going to turn that off because we don't necessarily need that and I'm going to trigger this on the collection page only because I want to notify all the subscribers at one time. We will require confirmation. Do you want to email these folks? Great. So we'll finish our setup.\u003C\u002Fp>\u003Cp>And the next operation inside our flow, we're gonna get our subscribers. So I'll just go to read data. We'll get our collection. We're going to choose subscribers there. And I could filter this down using a query.\u003C\u002Fp>\u003Cp>So so I could grab only active subscribers or subscribers with the first name Bryant if I wanted to. Or I could use this IDs field to pluck specific subscribers by the ID. In this case, we just want to pick all the subscribers. Sounds great. And then the next flow or the next operation inside this flow, I should say, is going to be sending our email.\u003C\u002Fp>\u003Cp>So in this case, we're going to trigger another flow. And inside our payload here, I'm going to go to edit raw value. I'll wrap this in quotations. We'll use our mustache syntax from Directus, and I'm just going to use git underscore subscribers, which is the key of that last operation. Whenever a flow runs, each operation has its own key and the data returned from that operation gets appended to an object that you can access the data through that key.\u003C\u002Fp>\u003Cp>Great. So now we've got our payload, but we don't have a specific flow to actually run. So let's go in and create that second flow and then we'll come back and update this. So we'll create a new flow. We'll call it send email.\u003C\u002Fp>\u003Cp>Maybe we even give it a nice email icon. And as far as the trigger, this is going to be triggered by another flow. The response body here is what we would return from this flow to the first flow. Lots of flows going on. We don't necessarily need to return anything here, so I'm just going to hit finish setup.\u003C\u002Fp>\u003Cp>And we will set up our email action. So we'll call this send email. The 2, if I look at my subscribers, I've got email. So the syntax here is going to be dollar sign triggered.email. Great.\u003C\u002Fp>\u003Cp>Make sure you hit enter to save that. And let's just give this a subject. Here's the subject and does flow looping work? Can Brian make it work? Right.\u003C\u002Fp>\u003Cp>Great. So we've got our flow here. All this does is receives data from another flow, sends an email to the person that we sent. The last piece of the puzzle here is in our trigger email notifications flow. We will go in and select that new flow that we just created and boom, everything is buttoned up.\u003C\u002Fp>\u003Cp>If I go back to my subscribers content collection and trigger email notifications, bada bing bada boom, we go back to our flows. I could see if this actually ran or not. So I could see the payload there. I could see what we passed to that second flow. And here in my logs, you can see this flow ran 4 separate times, which is convenient because I have 4 separate subscribers.\u003C\u002Fp>\u003Cp>So I could see here's the email that was sent. And if I happen to hop open my inbox, boom, there we could see the message. So that is how you can loop flows together to create, complex scenarios that are totally automated. This is great for sending emails or other actions that you need to loop through. Hope it was helpful.\u003C\u002Fp>\u003Cp>Catch you on the next episode. Ciao.\u003C\u002Fp>","Hi. Welcome back to another episode of Short Hops. I'm your host, Brian Gillespie. And today, I'm going to show you how to loop with flows. This topic comes up a lot inside the community. How do we chain flows together inside Directus? But if I back up a minute, flows are a great automation tool inside Directus. You can automate simple and complex workflows using the tools we provide, but there is a little bit of a learning curve, especially when you're trying to, loop through an array of objects or array of strings and perform some type of action on those within a flow. It actually requires 2 flows and we'll cover the steps in this quick video. So what are the steps needed to actually do this? We are going to create a first flow that collects our data that we want to loop through and perform an action on. And then we will create a second flow that actually performs those actions. And then last but not least, we to the first flow, we add an trigger flow operation and pass an array to that next flow, and that will do the looping for us. So, let's get back into the real world. A really common scenario here is I've got a list of users or subscribers that I want to loop through and send individual email notifications to or could be in app notifications. So I've got my subscriber list here, but let's go ahead and create our first flow. We'll just call it trigger email notifications. Perfect. For our setup here, the trigger, I'm just going to run this manually, and I'm going to select the subscribers collection. And I could, require somebody to select the individual subscribers here. I'm just going to turn that off because we don't necessarily need that and I'm going to trigger this on the collection page only because I want to notify all the subscribers at one time. We will require confirmation. Do you want to email these folks? Great. So we'll finish our setup. And the next operation inside our flow, we're gonna get our subscribers. So I'll just go to read data. We'll get our collection. We're going to choose subscribers there. And I could filter this down using a query. So so I could grab only active subscribers or subscribers with the first name Bryant if I wanted to. Or I could use this IDs field to pluck specific subscribers by the ID. In this case, we just want to pick all the subscribers. Sounds great. And then the next flow or the next operation inside this flow, I should say, is going to be sending our email. So in this case, we're going to trigger another flow. And inside our payload here, I'm going to go to edit raw value. I'll wrap this in quotations. We'll use our mustache syntax from Directus, and I'm just going to use git underscore subscribers, which is the key of that last operation. Whenever a flow runs, each operation has its own key and the data returned from that operation gets appended to an object that you can access the data through that key. Great. So now we've got our payload, but we don't have a specific flow to actually run. So let's go in and create that second flow and then we'll come back and update this. So we'll create a new flow. We'll call it send email. Maybe we even give it a nice email icon. And as far as the trigger, this is going to be triggered by another flow. The response body here is what we would return from this flow to the first flow. Lots of flows going on. We don't necessarily need to return anything here, so I'm just going to hit finish setup. And we will set up our email action. So we'll call this send email. The 2, if I look at my subscribers, I've got email. So the syntax here is going to be dollar sign triggered.email. Great. Make sure you hit enter to save that. And let's just give this a subject. Here's the subject and does flow looping work? Can Brian make it work? Right. Great. So we've got our flow here. All this does is receives data from another flow, sends an email to the person that we sent. The last piece of the puzzle here is in our trigger email notifications flow. We will go in and select that new flow that we just created and boom, everything is buttoned up. If I go back to my subscribers content collection and trigger email notifications, bada bing bada boom, we go back to our flows. I could see if this actually ran or not. So I could see the payload there. I could see what we passed to that second flow. And here in my logs, you can see this flow ran 4 separate times, which is convenient because I have 4 separate subscribers. So I could see here's the email that was sent. And if I happen to hop open my inbox, boom, there we could see the message. So that is how you can loop flows together to create, complex scenarios that are totally automated. This is great for sending emails or other actions that you need to loop through. Hope it was helpful. Catch you on the next episode. Ciao.",[3702],"02be9ae3-3608-4fc4-89c1-625c9a3f620e",[],{"year":978,"number":3600,"id":3601,"episodes":3705,"show":3706},[3603,3604,3605,3606,3583],{"title":2161,"slug":2162},{"id":3344,"slug":3708,"vimeo_id":3709,"description":3710,"tile":3711,"length":3712,"resources":12,"people":3713,"episode_number":177,"published":3716,"title":3717,"video_transcript_html":3718,"video_transcript_text":3719,"content":12,"seo":12,"status":19,"episode_people":3720,"recommendations":3723,"season":3724},"automate","925447352","Kevin and Andrew migrate their snake to run entirely in Directus Automate's low-code builder Flows. Is this the right way to run a Battlesnake? Tune in and find out.","37419656-e608-48d3-8d79-6fdb4fbcfac2",110,[3714,3715],{"name":2820,"url":2821},{"name":3330,"url":3331},"2024-04-04","Automate","\u003Cp>Kevin: Hello, and welcome back to we've got a catchphrase, Andrew. Are you ready? Ready, set, That's easy. We got 4 more We got 4 episodes. Exactly.\u003C\u002Fp>\n\u003Cp>We're we're we're we're cutting our losses for this one. In this show, we are continuing to build a Battlesnake, and progressively enhancing it, using Directus and Devcycle. My name is Kevin. I work at Directus, which we'll be using a little bit today. And My\u003C\u002Fp>\n\u003Cp>Andrew: name's Andrew, and I work at Devcycle, which we'll be using in a future episode. And this is Battlesnake.\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. Give us the give us the 90 seconds for people just dropping in now. What is Battlesnake?\u003C\u002Fp>\n\u003Cp>Andrew: Alright. Battlesnake is the way you wish that you had learned how to, program in a new framework or language from the very beginning. And now as a senior developer, Imagine you were playing the old classic snake game where you go around a board trying to eat food and survive. Now transform that into a programming game where you have a web server that's communicating with the Battlesnake game engine to communicate through code and tell your snake whether to go up, down, left, or right, and to try and survive as long as possible.\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. And we do that by implementing a web server, which implements the Battlesnake API. We've already had one episode of Ready, Set, Battlesnake. So we're not going to recap everything we did. If you are still a little fuzzy on how this works, it will probably become clear.\u003C\u002Fp>\n\u003Cp>But if you're completely fresh and you're like, I'm lost, go watch the first episode and watch a struggle through building the Node. Js starter snake. But today, we're gonna further build on top of that snake. Last episode, we built a battle snake based on the Node. Js starter, and we implemented here we are.\u003C\u002Fp>\n\u003Cp>And we implemented some logic. That logic, what what did the logic do? The logic stop listening dying by going hitting into walls. That's good. Then we got the snake to not hit itself or any other snake parts, because that also is instant kill.\u003C\u002Fp>\n\u003Cp>So that's all our snake does. Right? Yeah. Yeah. That's all our snake does.\u003C\u002Fp>\n\u003Cp>And we're probably not gonna make our snake much more clever today, but we are now going to introduce a new tool into the stack and get it out of Replit and into another environment. Anything else you think that's important, Andrew?\u003C\u002Fp>\n\u003Cp>Andrew: So the great thing is you can follow along with kind of all of the base code for all of this over on the GitHub repositories that exist around. Snake. So if you're watching this and you're like, I want to do it in my own language or framework, you can go and check out all the\u003C\u002Fp>\n\u003Cp>Kevin: awesome Angular projects\u003C\u002Fp>\n\u003Cp>Andrew: that are over there.\u003C\u002Fp>\n\u003Cp>Kevin: So actually, let's take pause for a moment because this is important. What is a Battlesnake? So you have to implement a web server that has a few specific route handlers. The a few specific endpoints. Firstly, this just route route.\u003C\u002Fp>\n\u003Cp>Right? And all this one does is it returns information about the snake, metadata about the snake. And we'll play around with that a little bit more today. The mode then we have the start endpoint, which, Andrew, I think that's the before the first move in any game. Right?\u003C\u002Fp>\n\u003Cp>Andrew: So we got our start end point, which is great. We've got our root end point. What is the kind of most important endpoint of all the endpoints though, Kevin?\u003C\u002Fp>\n\u003Cp>Kevin: It is this move endpoint. As we discovered last time, the move endpoint, every single turn of every single game will receive a request with the state of that game and the board, which includes every other snake, all the hazards, all the food. You have 500 milliseconds to respond with a string up, down, left, or right. And that is this is the most important endpoint. But you there is also an end endpoint, which is what you would potentially use to, like, wind down any resources or, you know, do any analytics storage, you know, and you just do it at the end.\u003C\u002Fp>\n\u003Cp>So we have these 4 endpoints that we need to implement. Move is is kind of the most critical. I think only move in the the root are required for any given snake. So that's worth noting because that's gonna be important. So what we're gonna do today, we are going to be building this snake inside of Directus.\u003C\u002Fp>\n\u003Cp>If you've not heard of Directus before, I hope you have because you're watching this on Directus TV. But Directus is is a back end toolkit to build applications. It has 2 parts. One part which is all around API generation from your database and asset storage, and the other side, which is a web application to kind of work with that data. There's a whole bunch of other stuff in there.\u003C\u002Fp>\n\u003Cp>So we're gonna be implementing a Battlesnake inside of a director's project. So we'll we'll we'll kinda see how this shakes out over the next over the next little while. Now originally, until about 5 minutes before we started recording, I thought that we would perhaps use, Directus' automation UI tool in order to build our snake because it is it feels quite viable on the surface. It does. And then I realized it ain't it ain't viable.\u003C\u002Fp>\n\u003Cp>And the reason it isn't is because one of the triggers that can begin a flow is a webhook, an inbound webhook, but it is unique, and randomly generated at the time the flow is created, which means we can't implement a root endpoint and then slash move, slash start, slash end. And that's gonna be a challenge. So rather than just trying to to power on through that, rather than sorry, rather than just trying to power on through that, we instead are gonna build an extension. Flows is effectively a UI layer or a UI version of, hooks of hooks. So, you know, we can we can do it in code.\u003C\u002Fp>\n\u003Cp>We also have endpoints, which is probably more suited for this where we can literally create complete endpoints, and this is a no doubt. So we'd probably lift and drop a lot of our logic. This might end up being a bit of a lighter episode, but there would be nothing wrong with that. But I've done the thing. I've jinxed it.\u003C\u002Fp>\n\u003Cp>Andrew: You have. I did it last time, though. So in fairness, we're we're each one in, and and so I feel like Indeed. This is way to do it. Indeed.\u003C\u002Fp>\n\u003Cp>And so we're also we're gonna be implementing some some, like, very like, this whole rate limiting thing has caused us to have to think about a lot of other technologies as well too. Right? So it's not just this concept of of sort of how we're gonna implement Directus. It's also\u003C\u002Fp>\n\u003Cp>Kevin: You're right.\u003C\u002Fp>\n\u003Cp>Andrew: How it's gonna be able to interact with with the Battlesync platform. Right?\u003C\u002Fp>\n\u003Cp>Kevin: Yes. So here here's the plan. Here's what I think the game plan is. We're gonna spin up, direct us locally using Docker. And that is an environment in which we can build extensions.\u003C\u002Fp>\n\u003Cp>And we could then just deploy it somewhere and be happy days, you know, deploy it on some some web service, or we could just, like, open up a tunnel to our local machine. And ngrok is kind of the tool that we've as as developer educator types have have historically jumped to, so I think it will be the tool we use today. I also found out today I'm apparently still paying for it on a grandfathered plan that gives us the pro features. So, you know, I think it's one I might wanna\u003C\u002Fp>\n\u003Cp>Andrew: Let's do it.\u003C\u002Fp>\n\u003Cp>Kevin: Get off the card, but I'll use it today.\u003C\u002Fp>\n\u003Cp>Andrew: I saw a I saw a really great, idea from someone recently just to, like, automatically, not only list all of the subscriptions that you have, but actually unsubscribe you from something if you haven't accessed it in a while. And I was like, that's a brilliant idea because I have so many things to subscribe for that I'd like to remove my credit card from.\u003C\u002Fp>\n\u003Cp>Kevin: Yes. But I'm happy in this moment in this moment that it's working. Now one thing that's really nice here is the extension entry point. If we look at this, example, the way it's built is you give an endpoint an ID, which becomes the kind of root path. So this would be like our director's project slash greet, and then you can create the root, the slash move, the slash start, the slash end, and so on.\u003C\u002Fp>\n\u003Cp>So this part should be pretty straightforward. Let's let's see, and if the docs end up being misleading, that's literally my job. So it's it will be incredibly self inflicted. So we'll we'll see. So we're gonna start by following the Docker guide for getting started with directors.\u003C\u002Fp>\n\u003Cp>No. We're not. We're gonna look at the self hosted quick stuff, which which\u003C\u002Fp>\n\u003Cp>Andrew: is super smooth. It is a very smooth Docker process, I will say. Like, setting it for the first time, it was it was chef's kiss.\u003C\u002Fp>\n\u003Cp>Kevin: I like it, and it's pretty, we we kinda sometimes refer to it. I mean, we will use different words, but I call it like full fat. This is the full thing. There's directors cloud, which is like, you know, a a managed platform. But this if you self host it, it's full fat, which is really nice.\u003C\u002Fp>\n\u003Cp>So I already have Docker running in the background and I already have a code editor here, completely completely blank code editor and a new directory, which I've opened up. So I think we're just gonna we're just gonna crack on straightaway in here. So first thing we need to do is create a Docker Compose file. It's a Docker dash compose dot YML file that describes all of the settings that we have with directors. We may need to fiddle with this later, But for now, docker compose dot yml.\u003C\u002Fp>\n\u003Cp>And let's make my font size significantly bigger. There we go. We can copy and paste the value in here. Keys and secrets are meant to be random values. They're used for, identifying, your account in, like, a horizontally scaled setup and the secret is used for JWT validation, and we don't care about that right now.\u003C\u002Fp>\n\u003Cp>So we're just gonna leave those. Hunter 2 is when I set up projects my default. So we're gonna Good\u003C\u002Fp>\n\u003Cp>Andrew: to know. Good to know.\u003C\u002Fp>\n\u003Cp>Kevin: Good to know. Good to know.\u003C\u002Fp>\n\u003Cp>Andrew: I'm searching around for past Kevin projects and just trying to break in.\u003C\u002Fp>\n\u003Cp>Kevin: Absolutely not. And no. No. That will that will not be good. We're gonna use, yeah.\u003C\u002Fp>\n\u003Cp>Sorry. Go on.\u003C\u002Fp>\n\u003Cp>Andrew: No. I was gonna say an interesting note here is I discovered with the self hosted version, you can't use a fake email address. You can't use dot test. It won't actually accept it as an email address in the platform.\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. Interesting. Interesting.\u003C\u002Fp>\n\u003Cp>Andrew: I was when I was building out that railway sort of like starter kit for it, I was trying to just set it up with a test and it would not it wouldn't go. You had to put it in an actual address.\u003C\u002Fp>\n\u003Cp>Kevin: Interesting. As in, like, it follows the structure of an email address, but it isn't real because it should do. That should be fine.\u003C\u002Fp>\n\u003Cp>Andrew: I think it was because it was a dot test, t, like, t Yeah.\u003C\u002Fp>\n\u003Cp>Kevin: But that that's they're legit characters in an email address. So anyway anyway. Things that's important yeah. One thing that's important to know about directors is it doesn't provide a database. It connects to an existing database.\u003C\u002Fp>\n\u003Cp>We're gonna use this lightweight file based database called SQLite. We're gonna point it at the file. And on first run, it will create the file for us. That isn't Directus, that's a SQL like database that Directus connects to. So just a little bit of important distinction there.\u003C\u002Fp>\n\u003Cp>10.8.3 is the latest version at the time of recording. By the time of publishing, that will not be the case. I think we might be on 10.9 by now. That's pretty exciting. Yeah.\u003C\u002Fp>\n\u003Cp>All good Database persistence, file persistence, extension persistence. WebSockets are enabled even though I'm not sure we're gonna use them. And instead of, continuing to talk through every line in excruciating detail, I think we can just write Docker Compose up.\u003C\u002Fp>\n\u003Cp>Andrew: It should kick off.\u003C\u002Fp>\n\u003Cp>Kevin: Great. It's attaching. Boom. Boom. Boom.\u003C\u002Fp>\n\u003Cp>Doing a bunch of stuff first time and running now on local host 8055. So if I hit local host 8055, brand new directors project with randomly auto filled credentials, which are not I think I have to move them out of, yeah, manage passwords, but that's not for today. I bet you that was a hunt to 2 password too. But this is a blank director's project ready to go. So that was that was great.\u003C\u002Fp>\n\u003Cp>What else is important here? If I open up oh, I haven't opened up the folder. This is annoying because obviously the terminals attached to it. So let's just, let's just stop that running. Let's open this snake folder here.\u003C\u002Fp>\n\u003Cp>I think I might have been able to add folder to workplace. We're here now. Let's just run Docker compose up again. Great. And just refresh that and everything should still be fine and dandy as it is.\u003C\u002Fp>\n\u003Cp>Now the first time we ran that, it created some directories here on the side, which I think are gonna be quite small right now on people's screens. But as a database directory with our SQL like database and extensions directory and then uploads directory. Grand. So we have this direct as project and it's great that we, you know, can go in and create, you know, collections in our database and upload files and create dashboards and all of that stuff. But really, all we care about is implementing these endpoints from our Replit and bringing them over.\u003C\u002Fp>\n\u003Cp>So let's go back to the docs. Let's talk about building extensions and get that get that game get that game underway. Directus has a whole bunch of extensions. Some of them affect the UI that that web app that we just had open. Some of them are API side like custom endpoints and hooks.\u003C\u002Fp>\n\u003Cp>Yeah. As it says here, hooks are similar to flows, but they do not have the UI, which is in the director's data studio. I think to create an extension, we're gonna go I'm sounding a little uncertain. I don't know why.\u003C\u002Fp>\n\u003Cp>Andrew: Like No. I love it. It makes me feel much better about about episode 1, now that you're feeling unconfident about the thing that you deal with every day. I feel like imposter syndrome is real, but at least you haven't had to Google how to make a JavaScript loop yet. So\u003C\u002Fp>\n\u003Cp>Kevin: Oh, good.\u003C\u002Fp>\n\u003Cp>Andrew: The episode is young. Run\u003C\u002Fp>\n\u003Cp>Kevin: so I'm gonna run this MPX create directors extension. I'll pick an endpoint, pick a name, directus dash extension dash, snake. Snake. Snake. I it doesn't it doesn't really matter.\u003C\u002Fp>\n\u003Cp>We'll use JavaScript. We'll auto install dependencies, and we'll give it a moment to scaffold that extension for.\u003C\u002Fp>\n\u003Cp>Andrew: This is a nice CLI. Like, I gotta say, this is a very enjoyable experience.\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. There's still work that needs to happen, and it is happening. But, yeah, this boiler plating isn't too bad, actually. I, yeah, I I I quite like it. So inside of our extensions directory, we have this new snake.\u003C\u002Fp>\n\u003Cp>We have source. We have this index dot JS. Grand. Now what we want and so this, I think I think this might be available, I think, at directus dash extension dashnet like slash. It's a little bit clunky, but we're not going to use this format.\u003C\u002Fp>\n\u003Cp>It doesn't really matter. We're going to use this. We looked at it earlier, this alternate format, where we can do all of the subroutes straight in here. So I think I think I can just copy and paste this.\u003C\u002Fp>\n\u003Cp>Andrew: Kevin, is this just is this just vanilla, or is this React? Like, I this looks very clean, but it doesn't Yeah.\u003C\u002Fp>\n\u003Cp>Kevin: So this so the front end of Directus is a view app, but this what you're seeing right now and ding, ding, ding, I think you've just spotted it. Endpoints use oh, directors also uses the express router. So it is very much what we did in the first night, which is why being able to lift and drop it is gonna be gonna be quite nice.\u003C\u002Fp>\n\u003Cp>Andrew: It's so clean. I dig it. I love this. This is beautiful.\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. Thank you. So we have snake slash snake intro snake goodbye. So let's get this running. So we're gonna c d into our Directus snake directory and we're gonna run npm run build.\u003C\u002Fp>\n\u003Cp>That's built the extension into a disk directory. And now we need to restart Directus now. I think I think it might be broken, but I think we can get it to I think we might be able to get it to, like, auto, like, load the the changes. What am I looking for? Self hosted config.\u003C\u002Fp>\n\u003Cp>Okay. So we are looking for extensions, extensions auto reload. Here we go. So Boolean will automatically reload extensions when they have changed. So we're back to our Docker Compose.\u003C\u002Fp>\n\u003Cp>We'll throw that in and make the value true. Maybe that will work. Maybe that will work. So run Docker compose up again. It's now gonna use this Docker compose file again with this new variable.\u003C\u002Fp>\n\u003Cp>Let's take a little look through the logs because hopefully yes. It says loaded extension, direct us extension snake. And then the hope is if we run npm run build again, it hopefully would have auto loaded, but that isn't the case. That's fine. That means we're just gonna have a slightly clunky development experience.\u003C\u002Fp>\n\u003Cp>So I'm gonna have to control C up enter, you know, off the baton.\u003C\u002Fp>\n\u003Cp>Andrew: That's fine. I gotta say though, like I remember like outside of this, I feel like we've gotten very spoiled in the development world now. Like, that used to be just the way that you did it. I used to use some.\u003C\u002Fp>\n\u003Cp>Kevin: Dragging files with Filezilla or Cybernet. Yes. A 100%. Yeah. Yeah.\u003C\u002Fp>\n\u003Cp>Andrew: I that's so I thought that's how you did it. I genuinely thought that that was the only way to do things.\u003C\u002Fp>\n\u003Cp>Kevin: No. Yeah. But not here. Like, this this, I'm not quite sure what the deal is. And by the time this is published, maybe this won't be the case anymore.\u003C\u002Fp>\n\u003Cp>Just for the last couple of versions, if you're running under Docker, I've noticed this environment variable isn't working. Our team's aware that's fine. I just wanna try it on the off chance there. Yeah. Whoops.\u003C\u002Fp>\n\u003Cp>So if we hit 8055 snake, we get the hello world. If we try snake slash intro, we get the nice to meet you. So these are endpoints that are returning stuff, which means we should now be able to lift and drop our snake pretty expediently. Yep. So what we what are we gonna do here?\u003C\u002Fp>\n\u003Cp>We have handlers dot info for the root. Yep. So you're gonna grab that. Return that. So I am just gonna make this slightly more verbose so I can space it out nicely.\u003C\u002Fp>\n\u003Cp>Yeah. So, can you bump up the\u003C\u002Fp>\n\u003Cp>Andrew: size a little bit too?\u003C\u002Fp>\n\u003Cp>Kevin: A little bit more?\u003C\u002Fp>\n\u003Cp>Andrew: Of course. Is that any any better? That's that's much better. Yeah.\u003C\u002Fp>\n\u003Cp>Kevin: Lovely. Rez got Jason. Yep. Do you need to return it?\u003C\u002Fp>\n\u003Cp>Andrew: I I believe so. Yeah.\u003C\u002Fp>\n\u003Cp>Kevin: Cool. Nice.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. I think everything you need to return.\u003C\u002Fp>\n\u003Cp>Kevin: Whoops. Got a little too a little too direct there. Let's, let's while we're here, maybe let's do some customization of the snake. That feels good. Yeah.\u003C\u002Fp>\n\u003Cp>That's good. So head over What color do we wanna make it?\u003C\u002Fp>\n\u003Cp>Andrew: Oh, what's the direct is it a direct purple? Is the is the color purple that you're using?\u003C\u002Fp>\n\u003Cp>Kevin: It is a purple. And, honestly, my lot are gonna kill me for, like, not remembering it, but I think it's, like, 6 6. I wanna say it's like 6644 f f.\u003C\u002Fp>\n\u003Cp>Andrew: I like it. Let's see. We'll find out momentarily whether this is Yes. Yes. That's right.\u003C\u002Fp>\n\u003Cp>I got it. 6644ff.\u003C\u002Fp>\n\u003Cp>Kevin: I logged into my Battlesnake account. We're gonna look at the customizations. And these are the customizations we can have for our snake. Andrew, you you may do the honors from the ones I have.\u003C\u002Fp>\n\u003Cp>Andrew: Oh my goodness. So I'm a big, big fan of mustache snake, but I'm pretty sure that there if we scroll down, there may be a rabbit in here now. I thought I thought we\u003C\u002Fp>\n\u003Cp>Kevin: had play around seeing if I could get the director's Bunny to look good in one of these, and I had just I couldn't with, like, the it has to be full width on the left. The Bunny, let's take a look.\u003C\u002Fp>\n\u003Cp>Andrew: Do we have a Bunny? If not, I know he exists in the backlog. So we may we may be able to get Bunny out at some point here, or he may be locked down. Either way, if not, if we can't get our bunny Oh,\u003C\u002Fp>\n\u003Cp>Kevin: I have 200 I have 200 coins as well, so I can unlock any one.\u003C\u002Fp>\n\u003Cp>Andrew: Oh, so what do we see? Do we see any that we really like? So we got chicken. I quite got\u003C\u002Fp>\n\u003Cp>Kevin: I quite like this dragon head here. I have to be honest. Yeah.\u003C\u002Fp>\n\u003Cp>Andrew: Take the dragon. But is\u003C\u002Fp>\n\u003Cp>Kevin: it was really funny. No. They're all 200. I don't see a bunny.\u003C\u002Fp>\n\u003Cp>Andrew: No bunny. That's disappointing. So let's go for dragon. I feel like that dragon\u003C\u002Fp>\n\u003Cp>Kevin: head Well, hang on. Let's let's look at the tails too. Let's let's, you know, maybe not spend all of our coins at once. True. No.\u003C\u002Fp>\n\u003Cp>I think I think the I think I like the heads. Well, yeah, let let's do the dragon. I've done it. Right. So the head we want is dragon, and the tail is So,\u003C\u002Fp>\n\u003Cp>Andrew: I mean, let's go I think cosmic horror's tail is pretty fun.\u003C\u002Fp>\n\u003Cp>Kevin: I like that. I like that. Okay. Cosmic Chorus. So that's our that's our snake.\u003C\u002Fp>\n\u003Cp>And once again, actually, should we get all the logic in? I think we should do this bit by bit. Like, we could now go off and make sure that this works, which is gonna be a whole task in itself. Does that feel okay?\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. Yeah. I think let's do that because then once we know it's working, then we can kinda go through. Is that what you're saying? You're thinking, like, let's go in and actually add it to the platform.\u003C\u002Fp>\n\u003Cp>I am with NVIDIA because we gotta get we gotta get ngrok set up now.\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. So do you wanna explain what ngrok is while I\u003C\u002Fp>\n\u003Cp>Andrew: get sucked in? Yeah. Yeah. Absolutely. So if you've never used ngrok before, it is a way to publicly expose your local, development environment to the outside world, whoever you would like to be able to access it.\u003C\u002Fp>\n\u003Cp>So if you often use, say, Gitpod or, Codespaces, there's often the way that you can make that public u that URL from private to public. That's fundamentally what this ngrok, endpoint that it's giving you is doing. But you can do it on a on a, case by case basis. And you can kinda keep it in one place, and you have a bit more control over the power of that and who can see it and all the rest of that stuff. It's our and it's really great, especially if you're, like, say, somebody that's building a site and you wanna see what it looks like on mobile.\u003C\u002Fp>\n\u003Cp>I think they might still do it. But I know there's some services that I've used that it would automatically generate a QR code from the n grok address so you could just, like, scan with your camera and then view on mobile. Doesn't impact us here, but, really, really good. And ngrok has taken a lot of steps to make things more secure as well. So it used to be that sort of anybody could spin up an ngrok address and there was no issues at all.\u003C\u002Fp>\n\u003Cp>And they've added a lot more security and ensuring that the things that need to be in place are. Yeah. And I will say around ngrok, if you are, on a local development environment on a Windows device, so a big Windows developer, there's a service called Laragon, which is like, an alternative to like a LAMP stack. And it has ngrok built in. So in there, you can just click share and then it automatically will, like, push that out to ngrok that you could share with people.\u003C\u002Fp>\n\u003Cp>But, yeah, powerful, powerful service.\u003C\u002Fp>\n\u003Cp>Kevin: So there's a little bit of a moment here. I'm not quite I don't quite have the memory of how this works. I think it works like this. I think based on what I've read, So you do ngrok HTTP, the port, which is 8055 and then dash dash domain and then your your domain. No.\u003C\u002Fp>\n\u003Cp>Reserve this name on your dashboard.\u003C\u002Fp>\n\u003Cp>Andrew: You don't need to do that. It'll automatically just generate a random one for you. So just No.\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. But I don't want that because I'm gonna have to keep updating it every time it restarts.\u003C\u002Fp>\n\u003Cp>Andrew: I think I\u003C\u002Fp>\n\u003Cp>Kevin: have I have a static. I have a static.\u003C\u002Fp>\n\u003Cp>Andrew: Oh, I see.\u003C\u002Fp>\n\u003Cp>Kevin: Like, because I because I'm on this plat this old plan. Right? Yeah. So I wanna work out\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. I haven't touched ngrok in in a little while. It's funny, like, it stopped working in that environment. There was, like, they hadn't updated the way that was integrated, and they added an authentication key. And so, it switched things up.\u003C\u002Fp>\n\u003Cp>So your guess is as good as mine.\u003C\u002Fp>\n\u003Cp>Kevin: We're gonna get oh, alright. Here we I mean, yeah. I suppose I could just do the help, right? So start engrop. That's cool.\u003C\u002Fp>\n\u003Cp>Basic auth. Cool. OAuth 2 cool. Here we are. Use it the same time by using dash dash domain.\u003C\u002Fp>\n\u003Cp>But, and I thought maybe I, maybe I.\u003C\u002Fp>\n\u003Cp>Andrew: What's your domain?\u003C\u002Fp>\n\u003Cp>Kevin: I thought it was that. That's why I that's why I paused. So if I look at domains, there it is. Unless I have to do the whole domain.\u003C\u002Fp>\n\u003Cp>Andrew: Try the whole domain and see what happens.\u003C\u002Fp>\n\u003Cp>Kevin: Because it because it did just say the ngrok.io is only for, like, legacy accounts. Yep. And that was it. Yeah.\u003C\u002Fp>\n\u003Cp>Andrew: There you go.\u003C\u002Fp>\n\u003Cp>Kevin: Great. So we've now got we've now got engrok rocking and rolling over here. Great. So now what we're gonna do is go over to battles go over to Battlesnake. We'll create a new snake like we did last time.\u003C\u002Fp>\n\u003Cp>So this is ready, set, battlesnake. Ready, set, battlesnake. Cake.\u003C\u002Fp>\n\u003Cp>Andrew: Yes. That's it. Nope. Nope.\u003C\u002Fp>\n\u003Cp>Kevin: K for Kevin.\u003C\u002Fp>\n\u003Cp>Andrew: That's it. I love it. Beautiful.\u003C\u002Fp>\n\u003Cp>Kevin: So https phzn.ngrok. Io\u002Fsnake. Engine region, Netherlands or Netherlands? Yeah. Well, Europe.\u003C\u002Fp>\n\u003Cp>So yeah. Sure.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah.\u003C\u002Fp>\n\u003Cp>Kevin: Programming language, JavaScript, and platform Director. Should be\u003C\u002Fp>\n\u003Cp>Andrew: there. Directus. Now my question here is, does the logo look good?\u003C\u002Fp>\n\u003Cp>Kevin: Oh, let's see what happened to it.\u003C\u002Fp>\n\u003Cp>Andrew: So I used the I got them to add Yeah. It doesn't look too bad.\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. It's an old it's an old one. So we'll fix it by the next episode, but it's good. It's just colors just colors and details. But yeah, that's that's our little bunny rabbit boy.\u003C\u002Fp>\n\u003Cp>Unknown API version. So there was an error. Yep. Even though we copied\u003C\u002Fp>\n\u003Cp>Andrew: You should paste it. So what I would say is, I think I think I think I think you might need to just reset. Because this used to happen on\u003C\u002Fp>\n\u003Cp>Kevin: Reset reset what bit?\u003C\u002Fp>\n\u003Cp>Andrew: Like oh, sorry. Reset everything. So just, like, ping it again. And if it doesn't work, then actually start running, like, restart your direct to server. Okay.\u003C\u002Fp>\n\u003Cp>This used to happen with replit all the time is it would get this, and then it would just need a couple minutes to start syncing up in the right way.\u003C\u002Fp>\n\u003Cp>Kevin: I don't know what that mean. I don't know what that means.\u003C\u002Fp>\n\u003Cp>Andrew: No. It's no. That's right.\u003C\u002Fp>\n\u003Cp>Kevin: So if I hit that endpoint Yep. Oh, it still says hello world. Oh. Why? Oh, why?\u003C\u002Fp>\n\u003Cp>Because we needed to rebuild the extension, then we needed to restart director. So there you go. That was our little people.\u003C\u002Fp>\n\u003Cp>Andrew: What are you doing?\u003C\u002Fp>\n\u003Cp>Kevin: No bill. What what what are you doing? Hit ping. Perfect. There it is.\u003C\u002Fp>\n\u003Cp>Oh, battle snake. It looks sick. Yeah. I love that. Yeah.\u003C\u002Fp>\n\u003Cp>So now we have a snake. Right? But we haven't implemented any other endpoints. So this is the time to do it. Now this is the point where maybe, like, we split out the move logic into a separate file to stop it.\u003C\u002Fp>\n\u003Cp>No. Do you wanna know what?\u003C\u002Fp>\n\u003Cp>Andrew: I actually wish that the move logic was in with everything, even in the JavaScript starter snake. I understand why it's not, but I actually think it's way better to have everything in one place just so all of your routes are are there and you're you're able to change that data. So I think I think let's do exactly what we That\u003C\u002Fp>\n\u003Cp>Kevin: works for me. So there we there we go. And I am just gonna shrink this slightly and shrink this slightly so we can get the replet up and kind of do a little do a little moving from one to the other. Now can I, can I shrink this sidebar down? Yes.\u003C\u002Fp>\n\u003Cp>Perfect. And maybe even shrink that. Okay. Rock and roll. Get rid of that.\u003C\u002Fp>\n\u003Cp>Oh, and get rid of that. There we go. So first thing we're doing is is move safe? So move, get some data in. Let's console log the request just so we can make sure, like, is it in request dot body?\u003C\u002Fp>\n\u003Cp>I I mean, I'm assuming it's in request dot body. Yep. Let's let's just double check that, and just respond with hello.\u003C\u002Fp>\n\u003Cp>Andrew: I like that after our last adventure where we had to disappear and through the magic of editing come back, we've decided to now add console logs earlier as we go so we can save ourselves some trust down the road.\u003C\u002Fp>\n\u003Cp>Kevin: That's my style. That's my style. Discum. Okay. Experiential.\u003C\u002Fp>\n\u003Cp>This is the point where and these these logs are gonna get noisy in a moment. Yep. I may even take them down inside until we need them, and then I can make them smaller still and then I can bump them back up. So what are we gonna do here in Battlesnake? We have a Battlesnake.\u003C\u002Fp>\n\u003Cp>Let's create a game, create game. Add ready set Battlesnake standard board 11 by 11.\u003C\u002Fp>\n\u003Cp>Andrew: And that's good start game.\u003C\u002Fp>\n\u003Cp>Kevin: Good. 404, 404, 404, 404.\u003C\u002Fp>\n\u003Cp>Andrew: Well, great. So but here's the thing. If you didn't watch episode 1 so here's the interesting thing. Is technically, technically in Battlesnake, you can actually win if you 404 or have a 500 error every single time because you have to set a default move that your snake makes. And I think that there's something, I mean depending on the implementation, I think there's something cool that you could do about actually having a way to change that default.\u003C\u002Fp>\n\u003Cp>Anyways, but then\u003C\u002Fp>\n\u003Cp>Kevin: game's still in progress?\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. It's been weird and slow. It's, like, not actually still in progress, but the platform's taking a bit of time to catch up with, I think, what's actually going on. So Why is it 404ing?\u003C\u002Fp>\n\u003Cp>Kevin: Root to talk. Kevin. Host. So we'll rebuild that, and we'll restart directors. I was using the wrong method right here.\u003C\u002Fp>\n\u003Cp>It's a post request to slash move. Okey dokey. So create game, add ready set battlesnake, hit start game. That should be better. There we go.\u003C\u002Fp>\n\u003Cp>So move gets hit. Oh, so this is it. So the data comes in. Yes. Req dot, req dot body.\u003C\u002Fp>\n\u003Cp>Fantastic. So now let's move all of this over. I might do a little bit of cleaning up as we go.\u003C\u002Fp>\n\u003Cp>Andrew: It'd be interesting. Yeah. Never mind. Let's let's get to it.\u003C\u002Fp>\n\u003Cp>Kevin: Let's get to it.\u003C\u002Fp>\n\u003Cp>Andrew: Go ahead. I was gonna say it's it'll be interesting if we if we wanna add the start and end point and log what's coming through there. I think there's something interesting in that as well, just to kinda see what that looks like before the logic is implemented. Because it does get really busy once the logic is implemented. At least right now, we're just getting those 404 errors or, like, the basic logs.\u003C\u002Fp>\n\u003Cp>Kevin: It's a start. And is it a post request? I think it is. Yep. Yep.\u003C\u002Fp>\n\u003Cp>Post. We probably should respond. Right?\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. And and it'll it'll get angry with us. But\u003C\u002Fp>\n\u003Cp>Kevin: And and. Yep. And just because they are gonna get in the way, I'm just gonna pop them down there. Okay. Let me just, cough.\u003C\u002Fp>\n\u003Cp>Let me mute.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. And so what's interesting here is so I think we we said this earlier, like, the start and end points. You don't really need them for anything, but they're useful if you wanna implement some more serious logic, which we may do sort of with our direct as Battlesink down the road. So you'll see here once we rebuild and then save again, you'll actually be able to kinda see the stuff that comes in\u003C\u002Fp>\n\u003Cp>Kevin: Yeah.\u003C\u002Fp>\n\u003Cp>Andrew: Through there.\u003C\u002Fp>\n\u003Cp>Kevin: So we need the width The height. And the and the height and the Head. Snakes. I think that's all we need. From rec.body.board.\u003C\u002Fp>\n\u003Cp>Right? Yeah. It's all in this thing called board. Yeah. Great.\u003C\u002Fp>\n\u003Cp>And the other thing we need is my head, and I am just gonna copy and paste that little convenience there because that's that's wicked.\u003C\u002Fp>\n\u003Cp>Andrew: If you by the way, if you really don't wanna have those 2 constant, like, lines that you have there, if you're just, like, really focused on compressing, you can technically pull it from that sneaks variable that you're defining in there. It's just\u003C\u002Fp>\n\u003Cp>Kevin: a little bit more more to do it. Absolutely. Yeah. Just can't I mean, because\u003C\u002Fp>\n\u003Cp>Andrew: be bothered.\u003C\u002Fp>\n\u003Cp>Kevin: Can't be bothered. Right. So so so we've got the we've got the width and the height. So first thing we were doing is don't don't move out of avoid walls, and we're just gonna copy and paste it. Now we're not.\u003C\u002Fp>\n\u003Cp>First thing we're gonna do is set do the is moves we're gonna do the is moves safe thing. That's the other bit of, boiler plating, I guess.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. And to be honest with you, you don't I mean, so this is like the the default logic. Technically, you can implement logic that doesn't use this, but this is just like the most straightforward way to do it. So\u003C\u002Fp>\n\u003Cp>Kevin: Sure. So this is avoid walls. So my head exists. It's movesafe.leftforce. Great.\u003C\u002Fp>\n\u003Cp>Board height. Interesting. I think we just called that\u003C\u002Fp>\n\u003Cp>Andrew: We call it height Height. So we just need to change all of those heads to my head and all of those heights to\u003C\u002Fp>\n\u003Cp>Kevin: No. Head to head is correct. My head. Oh, you just\u003C\u002Fp>\n\u003Cp>Andrew: got to use my head? Okay.\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. But board height. So what we're gonna do here is say height. We'll put bring that in as board height so I don't have to rewrite everything. We'll bring this in as board width.\u003C\u002Fp>\n\u003Cp>There we go. Job done. I'm also just while we're here, I'm just gonna just gonna we'll make things just a little more little more succinct, perhaps. This is valid. Right?\u003C\u002Fp>\n\u003Cp>Just so it's 4 lines as opposed to so many so many lines.\u003C\u002Fp>\n\u003Cp>Andrew: This is valid JavaScript. Really? Yeah. I hate indenting so much.\u003C\u002Fp>\n\u003Cp>Kevin: I would But indentation doesn't matter. So we avoid we avoid the walls. Great. And then it's avoid bodies. And we've already got the snakes.\u003C\u002Fp>\n\u003Cp>We've already got snakes stored here. So that is the equivalent of that line there. And so we're gonna take this for loop. We will dump that in here. And once again, I'm just gonna take this moment to just, to just clean up a little bit.\u003C\u002Fp>\n\u003Cp>This time because the conditionals are longer, I probably am inclined to leave them to leave them like this, but just just remove a bunch of, you know, white spacing. And there's some funky indentation going on here. There, boom. There we go. All right.\u003C\u002Fp>\n\u003Cp>Then if there's any save moves left, the console log and we return move down. Oh, if if there are none left, we go down. I got it. I got it. I got it.\u003C\u002Fp>\n\u003Cp>So, all the safe moves if no go down. Great. There we go. And it won't be return. It'll be red stop return red stop JSON.\u003C\u002Fp>\n\u003Cp>And then it won't go further down. Alright. And it will have returned there, so it won't continue. So I don't need to announce or anything. We pick the next move.\u003C\u002Fp>\n\u003Cp>I am noticing hang on. Hang on. Game state dot turn is not correct. Let's make sure my head piece. Yep.\u003C\u002Fp>\n\u003Cp>Snakes. Everything there is fine. It's just it's just here. Game state doesn't exist. That's req.body.tan, which means oh, no.\u003C\u002Fp>\n\u003Cp>Req. Yeah. That's fine. Req.body.tan. And then res.json, then we'll look at safe moves.length, pick the next move.\u003C\u002Fp>\n\u003Cp>We'll log it. And we will return it. I'm sorry. It isn't a string. It's a it's an object, isn't it, with a move value?\u003C\u002Fp>\n\u003Cp>Andrew: It is\u003C\u002Fp>\n\u003Cp>Kevin: Next move. So I think I think that is that's it. That's that's\u003C\u002Fp>\n\u003Cp>Andrew: a whole efficient, Kevin. That was wonderful. Okay.\u003C\u002Fp>\n\u003Cp>Kevin: Thank you very much. Thank you very much. Yeah. I think that is our our snake as implemented last time. Let's rebuild the extension.\u003C\u002Fp>\n\u003Cp>Let's restart the. Once again, that shouldn't need to be the case, but it is just at this point we're recording, so so be it. And oh, that game in progressing has persisted to be a challenge.\u003C\u002Fp>\n\u003Cp>Andrew: It has. So ignore. If I refresh,\u003C\u002Fp>\n\u003Cp>Kevin: does it know it's done? No. Okay. So New game. We'll have to keep.\u003C\u002Fp>\n\u003Cp>New game, and we'll send in, I don't know, loopy bot. Or does loopy bot just do the\u003C\u002Fp>\n\u003Cp>Andrew: Just does the loop. Yeah. Yeah. Hungry is good.\u003C\u002Fp>\n\u003Cp>Kevin: Hungry is probably a good little test.\u003C\u002Fp>\n\u003Cp>Andrew: Hungry is good. He was a good test for us last time. So I feel like we can see how he responds.\u003C\u002Fp>\n\u003Cp>Kevin: Okay? Obviously, it's a little more verbose down here. You can reduce the amount of logs with an environment variable. We died that, you know, that's the end state. So, oh, wait.\u003C\u002Fp>\n\u003Cp>If it won't, can I hit play? Yeah.\u003C\u002Fp>\n\u003Cp>Andrew: You can hit play.\u003C\u002Fp>\n\u003Cp>Kevin: And sorry. Show the coordinates. Fast, please. Save game.\u003C\u002Fp>\n\u003Cp>Andrew: No. Just go back. Just go\u003C\u002Fp>\n\u003Cp>Kevin: back. No. Oh, do I just I'll back back back back back. Alright. So r one is the orange is the, oh, we won.\u003C\u002Fp>\n\u003Cp>Andrew: So what did\u003C\u002Fp>\n\u003Cp>Kevin: it say\u003C\u002Fp>\n\u003Cp>Andrew: in the logs that you thought we lost?\u003C\u002Fp>\n\u003Cp>Kevin: No. No. We we didn't lose. It ended. I sorry.\u003C\u002Fp>\n\u003Cp>I said we died, but I meant it ended.\u003C\u002Fp>\n\u003Cp>Andrew: What does the what does the actual response say though? I'm not I'm curious. I don't have any idea.\u003C\u002Fp>\n\u003Cp>Kevin: It is game ID rule set map stand up source. The oh, it's the it's an n the last 10.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. Yeah. But does it say 1?\u003C\u002Fp>\n\u003Cp>Kevin: Let's find out.\u003C\u002Fp>\n\u003Cp>Andrew: I feel like it somewhere should say 1.\u003C\u002Fp>\n\u003Cp>Kevin: I think no. I think snakes is just one long. Oh, yeah. But it could be you or anyone else, I suppose. Fair\u003C\u002Fp>\n\u003Cp>Andrew: enough. It may not say it in there. I'm just curious.\u003C\u002Fp>\n\u003Cp>Kevin: Maybe it's the fact you still exist. I mean, let's find out when we die. Do you know? Oh, yeah. If we look at you, it has the ID oh, let me, woah.\u003C\u002Fp>\n\u003Cp>Andrew: There we go. I was gonna say I feel I felt like I wasn't seeing it, but I\u003C\u002Fp>\n\u003Cp>Kevin: feel like Sorry. That was, that was a keyboard shortcut thing going on. Alright. It has a health. I wonder if the health knows that.\u003C\u002Fp>\n\u003Cp>I mean, we'll just do it a couple of times, but let's let's watch. Let's watch our our little sneaky there.\u003C\u002Fp>\n\u003Cp>Andrew: You do it well. Hey. Like, that logic is good. Okay.\u003C\u002Fp>\n\u003Cp>Kevin: Head to head combat, which we haven't built logic for. Maybe we'll do that this time.\u003C\u002Fp>\n\u003Cp>Andrew: It's a good idea. I\u003C\u002Fp>\n\u003Cp>Kevin: think we because I because I kind of feel like that was what we intended to do this whole episode. Yeah. Congratulations. We're done because we can't build it in flows, and that's the that's the thing here. I have one other I maybe you can execute a flow from Yeah.\u003C\u002Fp>\n\u003Cp>From this, and then this just becomes the entry point into the flow.\u003C\u002Fp>\n\u003Cp>Andrew: That's what I was just gonna say. I was like, is there a way that we could set up the one of the endpoints in here and then have the trigger in flows? You're able to do everything from there?\u003C\u002Fp>\n\u003Cp>Kevin: Maybe.\u003C\u002Fp>\n\u003Cp>Andrew: That might be fun. I feel like that's an interesting way because I I do think that flows is like this interesting.\u003C\u002Fp>\n\u003Cp>Kevin: I've never done it, but, I'm sure it will be fine. So let let's take another look at some new extensions documentation that was recently written. The resources on the not this one. I'm looking for resources, components, packages, extension services. Here we are.\u003C\u002Fp>\n\u003Cp>So underneath directives, every time you make a request, it's actually going off to one of these services, which then, you know, which then executes your your request. And inside of extensions, you can also access these services. Various services are available within directors, including item service, collection service, file service, and they're available within the extensions context. So there is a router here in the handler, but I think we can also pass in this context. Right?\u003C\u002Fp>\n\u003Cp>Let me just think about the best way to do this. I think it might be just playing around with this router dot get here. So, so we have this context. I'm gonna bump that back up actually. And if we console dot log, the context and we see just here, we, destructure get schema and services and then we destructure the individual service.\u003C\u002Fp>\n\u003Cp>So let's let's just do that outside of the individual. Outside of the individual. No. I think for just a moment, we'll do it just inside of this one just because this is the one we're gonna hit. So we don't we don't just have to mess with that.\u003C\u002Fp>\n\u003Cp>And then instead of doing this, let's just console log the the services and just see what happens. I'm actually gonna do a console dot here, services dex null. And what this will do is effectively a console log that never does this kind of cut off thing. It'll always show you all of the nested layers just because I'm not sure what it looks like and I don't wanna I'd rather just be a little more verbose once. So dotcom.\u003C\u002Fp>\n\u003Cp>So what we're gonna do here is go back to our root endpoint and hit enter. Oh, sorry. Not root. R snake.\u003C\u002Fp>\n\u003Cp>Andrew: There we go.\u003C\u002Fp>\n\u003Cp>Kevin: And what happened here? So we have some services. Let's, yeah, let's just expand this for a moment. Webhook service. I, there is a flow.\u003C\u002Fp>\n\u003Cp>I, I believe a flow service.\u003C\u002Fp>\n\u003Cp>Andrew: Oh, nice.\u003C\u002Fp>\n\u003Cp>Kevin: Gonna that's gonna wanna be the one that we I mean, is this the one we wanna I don't know. I mean, all we wanna do is hit all we wanna do is hit an endpoint. But Yeah. Let\u003C\u002Fp>\n\u003Cp>Andrew: let's go for have\u003C\u002Fp>\n\u003Cp>Kevin: a let's have let's have a play. Sorry. Go on. No.\u003C\u002Fp>\n\u003Cp>Andrew: I was just gonna say, do we wanna go in? I don't know. Never mind. Let's let's test this out and then and then we'll go forward from there.\u003C\u002Fp>\n\u003Cp>Kevin: Sounds good. So let so what you do here is you create a new service with the collection there. So you know what we'll do here. There are some commonly used services. We can just refer to the full list of services directly in the code base here, which is actually what I should have just done.\u003C\u002Fp>\n\u003Cp>So we have flows here. So we can create flows, create many, update, delete. This actually isn't what we want because this is all about running flows. Sorry. And the the crud of flows.\u003C\u002Fp>\n\u003Cp>And we don't wanna do that. We just wanna we just wanna run a flow. So maybe the first thing we sorry. Go ahead.\u003C\u002Fp>\n\u003Cp>Andrew: No. No. No. Is it just the webhooks that we want, the webhooks service then? No.\u003C\u002Fp>\n\u003Cp>Because that's to create a webhook. That's\u003C\u002Fp>\n\u003Cp>Kevin: not Yeah. Exactly. That's outbound. So so what we'll do here is we'll go to the admin. Oh, wow.\u003C\u002Fp>\n\u003Cp>We will create a little flow and we'll see if we can trigger that from our code. That's basically the test here that it works. So we create a flow. We'll call this info. Snake info.\u003C\u002Fp>\n\u003Cp>All of that feels good. Maybe traffic activity in logs. I think that's fine. Then we have the webhook. Webhook.\u003C\u002Fp>\n\u003Cp>You're right. It will be a get request, response body, data of last operation.\u003C\u002Fp>\n\u003Cp>Andrew: Feed all the data just to have it?\u003C\u002Fp>\n\u003Cp>Kevin: That that that's happened. So oh, no. So this is the response body. This is what it will respond with. We only want to we wanna create the response and send it.\u003C\u002Fp>\n\u003Cp>But, actually, what you're describing is it was here. It was this option here, track activity in the box.\u003C\u002Fp>\n\u003Cp>Andrew: Alright. There we go.\u003C\u002Fp>\n\u003Cp>Kevin: So back to the trigger setup, we'll turn cash off. We don't want it to be asynchronous, get request, save, And we will re- we- it will be the data from the last operation. So we'll just run a script and we will just return this, I think. I mean, that makes sense. Yeah.\u003C\u002Fp>\n\u003Cp>That works. I've not done this before. So we are we are really finding out together how this works.\u003C\u002Fp>\n\u003Cp>Andrew: Works is very smooth. I like just being able to have it.\u003C\u002Fp>\n\u003Cp>Kevin: I really like it too. So we've got a save. So now we have this idea of a flow, and I think that's the thing that's interesting to me at least.\u003C\u002Fp>\n\u003Cp>Andrew: So you'd rather use the ID than actually just use the flow endpoint, like the webhook endpoint?\u003C\u002Fp>\n\u003Cp>Kevin: I could. I don't think that's, like, the proper way of doing it. But could we do that? Yeah.\u003C\u002Fp>\n\u003Cp>Andrew: I mean, I feel like I feel like Okay. I'm interested in seeing both ways, right? I'm interested in seeing ID flows, which I think is the proper way, and then just calling the webhook from within the code. Right?\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. I'm gonna be I I don't even know how to make a web request in these off the top of my head.\u003C\u002Fp>\n\u003Cp>Andrew: Just I think it's just Axios, isn't it?\u003C\u002Fp>\n\u003Cp>Kevin: I think it's just Axios. But but how is Axios, like, exposed? And do you wanna know what? I'm pretty confident there are just some guides that were written around this that I think will just help us. Endpoints create a public API proxy.\u003C\u002Fp>\n\u003Cp>Here we go. This will this will help. Yeah. It's a it's a fetch.\u003C\u002Fp>\n\u003Cp>Andrew: Oh, alright.\u003C\u002Fp>\n\u003Cp>Kevin: Cool. So we will so we hit slash. So what we're gonna do here, we're we're forget all these services for a minute. They don't I don't think they're gonna matter.\u003C\u002Fp>\n\u003Cp>Andrew: No. They won't matter because literally we're just basically now that we've created that flow, we're just doing a call to that webhook and then\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. We're gonna do a response, await, fetch, and then slash flows, async. Okay. And then, yes, this is response okay, response Jason await Jason, all of this jazz. I normally, honestly, just do this pretty low rent, then r r dot JSON and our response is the data.\u003C\u002Fp>\n\u003Cp>So I think we can just return reds dot JSON response. Yeah? Yep. I might just be explicit here. Flow response.\u003C\u002Fp>\n\u003Cp>Flow response. Yeah.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah.\u003C\u002Fp>\n\u003Cp>Kevin: And maybe yeah. That yeah. Screw it. Let's try it.\u003C\u002Fp>\n\u003Cp>Andrew: Let's do it. Gotta get this I feel like this is why I enjoy, like, non typed languages because you just mess around and see what happens.\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. I mean, you can build extensions just as an FYI with TypeScript. I just didn't oh, yeah. I thought this might be a little easier. So if I hit refresh here, we should see the same the same thing the same thing.\u003C\u002Fp>\n\u003Cp>Oh, wait. Hang on. I don't know why that's happening. Oh, was there an error? You have triggered an unhandled rejection.\u003C\u002Fp>\n\u003Cp>Failed to pass URL. So it didn't like the\u003C\u002Fp>\n\u003Cp>Andrew: Yes. So just add\u003C\u002Fp>\n\u003Cp>Kevin: the link to the I mean, sure. But that that doesn't make this portable. Right? That isn't let me just have a quick think here.\u003C\u002Fp>\n\u003Cp>Andrew: Oh, I see what you mean. You wanna be able to pat yes. Okay. I see what you mean.\u003C\u002Fp>\n\u003Cp>Kevin: Because then if we deploy it somewhere else, this will Yeah. This will still work. That's that's that's gonna have another little look. Let's keep having a little look here. So still sort here.\u003C\u002Fp>\n\u003Cp>No. Because now I'm right because this is the flows. All I wanna do is know, yeah, how do I run a flow? Let's just do you wanna know I think I've just had I think I've just had an idea on how this works,\u003C\u002Fp>\n\u003Cp>Andrew: which is\u003C\u002Fp>\n\u003Cp>Kevin: actually not looking at the services. It's looking at the the way the SDK does it. So if we look at the, extensions, packages, sure. The SDK just like type, type doc. This is just the the full dump of everything the SDK can do.\u003C\u002Fp>\n\u003Cp>There is an endpoint in here called something there's something in here for running flows. Andrew, I'm going It is I love a c.\u003C\u002Fp>\n\u003Cp>Andrew: So even if you write the documentation, everybody, you still need to read the documentation.\u003C\u002Fp>\n\u003Cp>Kevin: You do still need to run it. Here it is. Right. Create a flow, retrieve a flow, list flow. So this is all the stuff we just saw, but Yes.\u003C\u002Fp>\n\u003Cp>Here we are flow with get webhook trigger, trigger flow. What is going on here? Let's take a little look. Failed to pass URL from \u002Fflow\u002Ftrigger\u002Fuid.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. That I mean, that makes sense to me because it's not a full URL. Right?\u003C\u002Fp>\n\u003Cp>Kevin: Do you wanna know what I did sneakily on on the side here. I have a chat with one of my colleagues because, of course, I did because I have that power. It does just need to be the full URL. So let's stop. Let let's stop battling against I was right.\u003C\u002Fp>\n\u003Cp>You're right.\u003C\u002Fp>\n\u003Cp>Andrew: You're right. But you're right. It's not portable. You're very true. It's like you'd now need to, like, worry about changing this if you change where you're you're hosting it.\u003C\u002Fp>\n\u003Cp>Kevin: So I do think Which I think won't matter for the scope of this. So now built that, restarted that. So now if we hit this, it did return and it did it by getting by by getting the the, yeah. So, oh, that's really exciting. Okay.\u003C\u002Fp>\n\u003Cp>Let's abstract this logic out then. Let's get this whole move thing. Let's just comment it out. We know it works. Okay.\u003C\u002Fp>\n\u003Cp>And what we're gonna do here instead is we have a post. And so, let's set up a new flow. Not here. Let's set up a new flow. Oh, that's so exciting.\u003C\u002Fp>\n\u003Cp>Okay. Cool. Snake.\u003C\u002Fp>\n\u003Cp>Andrew: So we're gonna we're gonna create a move we're gonna create literally recreate this entire logic as separate flows. Why not do it as one flow, though?\u003C\u002Fp>\n\u003Cp>Kevin: Oh, because it No. No. No. Just because it won't be each endpoint. So everything move is gonna happen in one flow, and that that's yeah.\u003C\u002Fp>\n\u003Cp>Yeah. It's cool, isn't it? I think track activity and logs, I'm going to level with you. I'm not I'm going to say don't track anything because it's just gonna it's gonna absolutely explode the logs because it can be every like, the full body every time. And we have the the the logs over here.\u003C\u002Fp>\n\u003Cp>So we won't do that. That will be a webhook. Data of lost operation, that's fine. Not asynchronous. Don't cache.\u003C\u002Fp>\n\u003Cp>Post. Okay. So exciting. Okay. And we will, run a script and we will return move left.\u003C\u002Fp>\n\u003Cp>Right. We'll just we'll just do that to start and just test test it works. Return move. Hit save. And we have an ID for this one.\u003C\u002Fp>\n\u003Cp>Great. And, what we're gonna do here? We're gonna do we're gonna copy this. We are gonna change the ID and we're just gonna go post, sorry, methods. I wanna say it's post and I think it's body equals JSON dot stringifyreq.body.\u003C\u002Fp>\n\u003Cp>I think that's I think that's the fetch. I think that's the fetch way of doing it. So What\u003C\u002Fp>\n\u003Cp>Andrew: is the fetch way of doing it?\u003C\u002Fp>\n\u003Cp>Kevin: Is it good? Okay. Fetch. My memory has not failed me today. I'm literally I'm so pumped that we're able to do it in flows.\u003C\u002Fp>\n\u003Cp>That was a really good I I think that was yours. You can have that one.\u003C\u002Fp>\n\u003Cp>Andrew: No. We're gonna I'm gonna give that that's that was a 5050. We left brain, right brain thing here. I feel like it was one of those days.\u003C\u002Fp>\n\u003Cp>Kevin: Okay. Okay. Alright. So we're ready set. Ready set Battlesnake, Hungry Bot.\u003C\u002Fp>\n\u003Cp>Sure. And start game. What's happening?\u003C\u002Fp>\n\u003Cp>Andrew: It's happening. It's happening. It's happening. It's happening.\u003C\u002Fp>\n\u003Cp>Kevin: But it will just go left, left, left, left, and die. Yep. Right? That's fine. Yeah.\u003C\u002Fp>\n\u003Cp>That's fine. It's still\u003C\u002Fp>\n\u003Cp>Andrew: it's still going into\u003C\u002Fp>\n\u003Cp>Kevin: the world. Awesome. Which means we are done with the code. We're not done. We'll need it for reference.\u003C\u002Fp>\n\u003Cp>Yeah. But we are in flows now. It's happening.\u003C\u002Fp>\n\u003Cp>Andrew: What's interesting here too, folks, is, like, you like, literally with those two endpoints, like, you don't actually need to implement start and end. Like, you can stop with just those 2 if you're like, I don't wanna have to manage 4 flows. Like, you don't actually need start and end at this point. And so I think, like, don't use them.\u003C\u002Fp>\n\u003Cp>Kevin: So first thing we did was avoid walls. I'm gonna run a script here. We'll return it, and then I'm just gonna do the it's annoying. You have to kind of create it from an existing path. Yeah.\u003C\u002Fp>\n\u003Cp>And then we're gonna take that and dump it in there. Okay. Avoid walls. Back to the back to the primitives. Now, you know what we didn't do?\u003C\u002Fp>\n\u003Cp>We didn't because we don't have the logs. We actually don't quite know what it looks like in here, which is annoying. Just a thought I've had.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. It's true.\u003C\u002Fp>\n\u003Cp>Kevin: But I think I know.\u003C\u002Fp>\n\u003Cp>Andrew: Well, I mean, let's see. Let's see what happens here. And I think the cool thing here is, like, now we return to, like, traditional programming as well. Right? There's a lot of different ways you can implement this within the flow.\u003C\u002Fp>\n\u003Cp>Like, there's so many different operations. Technically, you could feedback a lot of the information and then make, like, decisions there or just do it.\u003C\u002Fp>\n\u003Cp>Kevin: Let's let's return the data. Like, that's the whole data chain. And then so this first time, this is gonna fail absolutely miserably. But, oh, no. It won't log anywhere, will it?\u003C\u002Fp>\n\u003Cp>Do we\u003C\u002Fp>\n\u003Cp>Andrew: wanna turn on those logs then? I know we didn't want\u003C\u002Fp>\n\u003Cp>Kevin: to, but Yeah. Yeah. Can I turn them on then turn them off? Yes. Fine.\u003C\u002Fp>\n\u003Cp>Yeah. That's fine. Yeah. Yeah. Yeah.\u003C\u002Fp>\n\u003Cp>Sorry. I thought it was a one and done kinda deal. So let's create a new game. Create rematch. Okay.\u003C\u002Fp>\n\u003Cp>Cool. Whatever. Sure. Refresh. And there should be many Yeah.\u003C\u002Fp>\n\u003Cp>8. That was 8 moves. So it's it's gonna get unwieldy, but this is what matters.\u003C\u002Fp>\n\u003Cp>Andrew: That's great. That's awesome. Yeah. So we get\u003C\u002Fp>\n\u003Cp>Kevin: Except the distinct so the trigger where's where's all the, not being oh, so Are we not sending it? Are we not sending it precisely? And this is why I wanted to check. Yeah. Yeah.\u003C\u002Fp>\n\u003Cp>Oh, wait, what\u003C\u002Fp>\n\u003Cp>Andrew: did that say failed up there? Go back up to the options.\u003C\u002Fp>\n\u003Cp>Kevin: No, no, no, no, no. Force. Okay. Cache enabled force, cache enabled force. So cool.\u003C\u002Fp>\n\u003Cp>We have this fetch. We send that is how you that is how you do it. Right?\u003C\u002Fp>\n\u003Cp>Andrew: I mean, I could have been wrong. I was assuming, but ah, here we go. I'm glad that it has happened.\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. Body.stringify. Body data type must match content type header, but I I don't think sure. Do you wanna layer what? Sure.\u003C\u002Fp>\n\u003Cp>Let's just whack that header in. Just in case it's that.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. See what happens.\u003C\u002Fp>\n\u003Cp>Kevin: I'm not losing any sleep over over adding that. Is that an error? Oh, no. We've left a log we've left a login somewhere. All the serve hang on.\u003C\u002Fp>\n\u003Cp>They're all the services.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. Didn't we didn't we remove that?\u003C\u002Fp>\n\u003Cp>Kevin: No. It's the it's the context. It's this console log context.\u003C\u002Fp>\n\u003Cp>Andrew: Oh, there we go.\u003C\u002Fp>\n\u003Cp>Kevin: I was like, that's a big that's a big object. So right. Restarting. Okay. Okay.\u003C\u002Fp>\n\u003Cp>Okay. We're gonna create rematch.\u003C\u002Fp>\n\u003Cp>Andrew: Let's get some logs.\u003C\u002Fp>\n\u003Cp>Kevin: Nice. Oh, head to head. Some quick a nice quick death. Okay. Are we getting Payload.\u003C\u002Fp>\n\u003Cp>There we go. There we go. There we go. So it's in trigger dot. Body.\u003C\u002Fp>\n\u003Cp>Body, I think.\u003C\u002Fp>\n\u003Cp>Andrew: Yep. Yeah. Trigger dot body. Yeah.\u003C\u002Fp>\n\u003Cp>Kevin: That looks correct. So, I mean, there's one way to find out, right, which is, so, trigger data dot trigger dot Bobby. Nice one. Bobby. So let's go and take this first up.\u003C\u002Fp>\n\u003Cp>So first thing we'll do, of course, sorry, is we will is move safe. We have to think about how we, because we, we need to kind of alter this value as we go. Maybe we return that. Maybe we return that with each step. Right?\u003C\u002Fp>\n\u003Cp>So we'll start with a is move safe and we'll just return it every time. So Okay. We'll start this time, we'll create it. I also think that could be a const. Anyway, avoid walls.\u003C\u002Fp>\n\u003Cp>So we need these values. Do you\u003C\u002Fp>\n\u003Cp>Andrew: wanna bump up the text size there a little bit more? Thank you.\u003C\u002Fp>\n\u003Cp>Kevin: You know what? I'm actually inclined. Let's rename this one. Let's call this one set up variables. Yeah.\u003C\u002Fp>\n\u003Cp>Like that. Let's call them let's call it globals. We'll just make the key global so it's a little shorter. All we're gonna do is return is return. We're gonna return what was it called?\u003C\u002Fp>\n\u003Cp>Is move safe. We will also do forward width. It's going to be data.trigger.body.width. Old height. What was the other thing?\u003C\u002Fp>\n\u003Cp>Oh, my head. So that's all of that. And then, we yeah. Just return that. Right?\u003C\u002Fp>\n\u003Cp>Yep. Sponsored global. Yeah. Yeah. We will.\u003C\u002Fp>\n\u003Cp>Just just I I wanna be I wanna be testing early in Austin even though, you know, we're not doing much there. I just wanna I just wanna be validating that we haven't broken something there. So we will create a rematch. So all we should be getting now is an extra property in the data chain. Wow.\u003C\u002Fp>\n\u003Cp>That was a really short, really short game. I think it was, like, 2 moves. Yeah. Oh, rec is not defined. I think I left in a a remnant of what it was before.\u003C\u002Fp>\n\u003Cp>Rec.body. Yeah. Data. Trigger dot body. That's why we test.\u003C\u002Fp>\n\u003Cp>Create a rematch. What else have you used Flows for in the past?\u003C\u002Fp>\n\u003Cp>Andrew: So I did the, the DevCycle Directus integration that I built out. So this was, it's worth checking out, just to see, like, the different use cases here. So it basically allowed for you to do an API call to a patch API on the dev cycle, patch API endpoint on the dev cycle platform to, like, pass basic data into into the service and to, like, put, like, an edge database for feature flag management. We might use that actually, when we start to do the dev cycle integration just because it's a really fast way, and I know that it already works with Directus. So, nice.\u003C\u002Fp>\n\u003Cp>But yeah.\u003C\u002Fp>\n\u003Cp>Kevin: That's called EdgeDB. EdgeDB. Yeah.\u003C\u002Fp>\n\u003Cp>Andrew: So it's just, it's just, like, a faster way to pass data. So how are we doing here?\u003C\u002Fp>\n\u003Cp>Kevin: No. No. But if I refresh, I think I fixed it.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. Right.\u003C\u002Fp>\n\u003Cp>Kevin: That's everything in the in the global. Now I'm not sure. So I think is move safe is one that we might need to pass, like every move we're going to need to we're going to need to reset that, but that's fine. So cool. Next thing we'll do then is avoid walls.\u003C\u002Fp>\n\u003Cp>Avoid walls. We're gonna run a script. We have data, and we have our avoid walls logic here.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. I feel like this is also just a really nice visual way to represent this whole\u003C\u002Fp>\n\u003Cp>Kevin: This those are the steps.\u003C\u002Fp>\n\u003Cp>Andrew: I really wanna go and find my Node JS snake or my, my Node RED snake that I created because I did the same it was the same process that I went through.\u003C\u002Fp>\n\u003Cp>Kevin: Was it sorry. Just very quickly. Well, did I call it my head still? I think I did. Yeah?\u003C\u002Fp>\n\u003Cp>Yep. You did. Yep. So the nice thing about this is we have is say is move safe left and all of this. So I think we wanna create a new variable called is move safe, and the value of that, I think this works, is gonna be data dot globals.\u003C\u002Fp>\n\u003Cp>Was it multiple? No. Global.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. Global.\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. I think so. Yeah. Dot is move safe. Right?\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. This this\u003C\u002Fp>\n\u003Cp>Kevin: is And then yeah. This is\u003C\u002Fp>\n\u003Cp>Andrew: something that's really interesting to know having used flows before. I was not taking advantage of the globals like that or not the globals because that's the thing you're creating here, but, like, the But the data chain. Touch on data chain. I thought you lost the data, basically No. As you went through.\u003C\u002Fp>\n\u003Cp>So that's interesting.\u003C\u002Fp>\n\u003Cp>Kevin: And I've had another thought about this. I think the way we're gonna do this is using last. So last, dollar sign last is a dynamic variable that will always be the output of the last step. But that means this line will become basically a boilerplate first line on every step in, I think. I think this creates a copy.\u003C\u002Fp>\n\u003Cp>We'll find out. We'll find out together. So next thing we're gonna do is we're going to destructure, my head board width, and board height from data dot globals. And then the rest of the code should be able to be untouched. Right?\u003C\u002Fp>\n\u003Cp>Exactly. Yeah.\u003C\u002Fp>\n\u003Cp>Andrew: It looks good. The\u003C\u002Fp>\n\u003Cp>Kevin: only and the only thing we wanna the only thing we wanna return is is move safe. Only thing we care about. Yeah. If this works, I'm gonna be so pleased with myself. I have a hunch it won't just because, of course, why would it?\u003C\u002Fp>\n\u003Cp>Let me just move return move out the way. In fact, we can remove that. So no. Of course not. Sorry.\u003C\u002Fp>\n\u003Cp>Ah, I've deleted it now. It's fine. We're here. So return move because what we're gonna do in this step is we're gonna just do that last bit of logic. If all there safe moves, if no go down.\u003C\u002Fp>\n\u003Cp>So we're gonna get rid of that. All we care about is next move and return. Yep. Because that is a a step between where we are and where and where we're going. So I have in my here we are.\u003C\u002Fp>\n\u003Cp>This is what we just had in our last step. So we have beta dot globals. Now is move safe. There we go. So next move, safe moves.\u003C\u002Fp>\n\u003Cp>Is it safe safe? Why is it called safe moves? Oh, here. I need this line too. I'm gonna call that one.\u003C\u002Fp>\n\u003Cp>Safe moves. Object dot keys is move safe. Filter down is move safe key. Fine. Fine.\u003C\u002Fp>\n\u003Cp>And that's just getting the the the values. Then next move, is safe moves, math floor safe moves. So that that should be it, And then we should just be able to return move next move like so. So that's the last step in. That's the last step in the chain.\u003C\u002Fp>\n\u003Cp>Let me bump that up one more time. Sorry. So that's all the logic again. We have that boilerplate starts. We don't quite know if that works yet, but in theory\u003C\u002Fp>\n\u003Cp>Andrew: It should.\u003C\u002Fp>\n\u003Cp>Kevin: Theory, it should. It should. Dangerous last words. Those. Sorry.\u003C\u002Fp>\n\u003Cp>So we'll just set ready set battlesnake in there because we're not equipped to have multisnake yet.\u003C\u002Fp>\n\u003Cp>Andrew: Nope. Did not work. What are our logs saying?\u003C\u002Fp>\n\u003Cp>Kevin: Thank you. Thank you very much. Yes. It did not work.\u003C\u002Fp>\n\u003Cp>Andrew: You're welcome. I feel like I need to add these\u003C\u002Fp>\n\u003Cp>Kevin: useful Last is not defined. Absolutely. I know exactly where the typo was. It's data dot last, and it will be wrong there as well. Data dot last.\u003C\u002Fp>\n\u003Cp>Right? Yep. Yeah. Cool. And what's nice now is you're not having to restart Directus every time because now it's handled by the flow.\u003C\u002Fp>\n\u003Cp>That's wonderful. It is cool. There was a little bit of of, let's call it friction first time. Okay. That's not ideal.\u003C\u002Fp>\n\u003Cp>42, 44, payload. Cannot convert undefined or null type. Okay. What are we? Payload after after but here's the thing.\u003C\u002Fp>\n\u003Cp>After avoid walls, look at that. It did say up force, which means everything's working until the return move. So that's fine. So that means it works, this whole passing passing is move safe each step. Yeah.\u003C\u002Fp>\n\u003Cp>But where it's breaking is this last one.\u003C\u002Fp>\n\u003Cp>Andrew: Okay. So let's look at this again. So async function\u003C\u002Fp>\n\u003Cp>Kevin: the error was cannot let's actually read the error. The error says, cannot confer undefined or null to object, which means the issue is here in object dot keys. So we have is move move safe. I think it can just be data dot last if that's all we're passing step to step because we don't return an object with is move safe. We just return is move safe, which means here again, although\u003C\u002Fp>\n\u003Cp>Andrew: I mean, let's see. Let's see what happens.\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. I think I think that'll fix it. So if we hit create rematch Yes. It avoided pause. Nice.\u003C\u002Fp>\n\u003Cp>Lovely stuff. And let's just, let's just validate that was indeed what we needed it to be. Left.\u003C\u002Fp>\n\u003Cp>Andrew: Hey. There we go.\u003C\u002Fp>\n\u003Cp>Kevin: Heck, yeah. Which means we're onto we're we're now onto a winner, basically. It's this is the starting logic for our for the no. It isn't. Oh, it's it's somewhere in between that we haven't quite got the standard because this is the first one after the global and this is the last one.\u003C\u002Fp>\n\u003Cp>So that's fine. We need to create one more here. Yep. And, what's next? Avoid bodies.\u003C\u002Fp>\n\u003Cp>Andrew: Avoid others. Yep.\u003C\u002Fp>\n\u003Cp>Kevin: Run script. Paste that in. And we don't need is move safe anymore. We have the data dot globals. And where are the where are where is the logic?\u003C\u002Fp>\n\u003Cp>It's here. And just because I think I think it's gonna be easier, I'm just gonna great. Great. And I don't think we need board width or board height anymore. We just need my head.\u003C\u002Fp>\n\u003Cp>We have snakes. So I believe we had bored. I think we got bored out. Yes. I'm not sure we did.\u003C\u002Fp>\n\u003Cp>Let's look at the globals again. The actual board itself needs to come with 2. So, let's call it snakes. Let's just add snakes here. So data dot trigger dot body dot board dot snakes.\u003C\u002Fp>\n\u003Cp>Then let's create this last one again, as avoid bodies and that should have saved, but it's fine. Okay. So we have my head and we have snakes. I think I gen oh, yeah. I genuinely think nothing needs touching here.\u003C\u002Fp>\n\u003Cp>Andrew: No, I think it's good. And I think we've the, the code works and we're even past.\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. I don't see any point of weakness in this at the moment. So we hit say, oh no, we don't, we just,\u003C\u002Fp>\n\u003Cp>Andrew: Famous last words.\u003C\u002Fp>\n\u003Cp>Kevin: We do that. Then we do that. So I'm gonna just Very good. We'll hit save. Oh,\u003C\u002Fp>\n\u003Cp>Andrew: what's going on? Value fulfilled resolving collection, direct us operations.\u003C\u002Fp>\n\u003Cp>Kevin: Value fulfilled resolve. In fact, it has to be I, don't know what that is. Record not unique.\u003C\u002Fp>\n\u003Cp>Andrew: For field, resolve and click direct operations. Maybe if we turn off that logging now. Just turn off the logging because I think that's Yeah.\u003C\u002Fp>\n\u003Cp>Kevin: But that isn't logging. That's a database save error, but I don't know why. Let let's just forget this was here for a moment like it was before, and it was fine. Okay. That does save.\u003C\u002Fp>\n\u003Cp>So if I take this instead and send it to avoid bodies and hit save, it's fine. And then if I go here to return move, fine. Do you wanna know why I'm just gonna walk away slowly?\u003C\u002Fp>\n\u003Cp>Andrew: That's the right that's the right answer. If the thing starts working\u003C\u002Fp>\n\u003Cp>Kevin: So we don't wanna create a rematch now because we wanna create a game with another snake. Yeah. Yeah. Star hungry\u003C\u002Fp>\n\u003Cp>Andrew: in there. Alright. Let's see how this works. Okay. We got it.\u003C\u002Fp>\n\u003Cp>He Hang on.\u003C\u002Fp>\n\u003Cp>Kevin: We've not had to Yes. Hey. There we go. Yes. Nice.\u003C\u002Fp>\n\u003Cp>Get in. Okay. Done. Nice. So the final thing here was this, if there's no safe moves, go down.\u003C\u002Fp>\n\u003Cp>Andrew: I don't think we need that. I genuinely it's like a Oh, yeah. Backup. It'll always Yeah.\u003C\u002Fp>\n\u003Cp>Kevin: Well, because if there are no safe moves, you're toast anyway.\u003C\u002Fp>\n\u003Cp>Andrew: Exactly. So it's really just, like, if you want to have, it I don't know. It's good programming practice for you to build it in, but, like, I think here, like, we can worry about that down the\u003C\u002Fp>\n\u003Cp>Kevin: road. Do you wanna know what? Given that this was basically the scope for this episode, I'm gonna add it. Why don't we add 1 one new block in?\u003C\u002Fp>\n\u003Cp>Andrew: Right.\u003C\u002Fp>\n\u003Cp>Kevin: Because I wanna do avoid avoid head to head.\u003C\u002Fp>\n\u003Cp>Andrew: K. Yep. That seems easy. Yeah. Because we're just basically doing look ahead here.\u003C\u002Fp>\n\u003Cp>Right? We're looking at, like, one extra block ahead. So so yeah. I mean, so this is the if you didn't catch it on the last stream or on the last, the last episode, we so head to heads, you can do and you can win, but it's all about size and rules, but, like, these head to heads here. But with the logic we currently have, we're only ever looking at what's directly next to us.\u003C\u002Fp>\n\u003Cp>So ensuring that, like, if we have another snake beside us or directly in front of us, we'll never go that direction. But if there's a snake that's 2 away in the same direction, so basically, we're repeating the logic that's in here and just or we could technically revise this logic. I think it's similar because it's just like Yeah. Yeah.\u003C\u002Fp>\n\u003Cp>Kevin: It will be similar, but I don't I do not think it's the same. So it's not the same. If we ditch this for a moment, we'll leave it as we could see it. So we care about the snakes, every snake, and we're gonna get rid of us. Now there is a a fancy way of doing this, which is, js return array without first item.\u003C\u002Fp>\n\u003Cp>To remove the first element in the in the array, you can use shift. Shift removes the first element of the array and returns it. So I think we can do this just to get rid of us first.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. And then we have to find our\u003C\u002Fp>\n\u003Cp>Kevin: because then we would have no valid no valid way to go. So So now we care about every other snake. So I'm gonna say head, it's gonna be snake dot body. Does snake have a head too? Does every snake have a head?\u003C\u002Fp>\n\u003Cp>Andrew: Everything has a head.\u003C\u002Fp>\n\u003Cp>Kevin: Snake dot head. So okay. Cool. And what we wanna do is look one space left, right, up, or down and remove if that's gonna be the direction we go in. Right?\u003C\u002Fp>\n\u003Cp>But we I'm just trying to have a think here because I think these are actually much more complex if statements.\u003C\u002Fp>\n\u003Cp>Andrew: They are. It's basically if I remember having done this before, it's basically all of those if statements that we had there repeated 4 times, if that makes sense.\u003C\u002Fp>\n\u003Cp>Kevin: Which we absolutely will not do. We will we will do, like, a nested loop. But I'm just trying to think through that. I'm just trying to think through, like, what is the logic here? Well, the first thing we wanna do is check every feasible space we can move into.\u003C\u002Fp>\n\u003Cp>That's the way we're gonna do it. So feasible directions. I remember encoding Valdez to do stuff like that, and then we had to remember what it meant. Possible next steps. And we actually don't wanna do this inside of this loop of every snake.\u003C\u002Fp>\n\u003Cp>We wanna do this just for us. So possible next steps are, is I don't know that because that's gonna be possible next steps is an array is an array. Cool. And we're gonna push into this array everything up, down, left, or right of us. Yeah.\u003C\u002Fp>\n\u003Cp>Then we're gonna remove our body piece, our neck. We're gonna remove our neck. Yeah. Okay. Cause we don't want our neck in there.\u003C\u002Fp>\n\u003Cp>So that, that will release 3 directions. And then for every snake, we're gonna repeat that logic. We're basically gonna say if any of these match, you remove it. So when we store them in here, we also have to store whether it's up, down, left, or right.\u003C\u002Fp>\n\u003Cp>Andrew: K. Alright. Let's see. You have a plan, and I'm very curious to see how this\u003C\u002Fp>\n\u003Cp>Kevin: will be implemented. First thing we wanna do basically is say up, down, left, or right. What is the coordinate? So what are the coordinates of that? So we're gonna say, in fact, I I think this might be easier.\u003C\u002Fp>\n\u003Cp>So we're gonna go possible next steps dot push. We'll we'll start with left. No. Left is going to be x is, come on, Kevin, you can do this. X is going to be our current X position.\u003C\u002Fp>\n\u003Cp>So this is going to be I don't just want my head. I want I want you. So I'm gonna save this. We'll come back here. I don't think we have you.\u003C\u002Fp>\n\u003Cp>No. No. We have my hand, which is great. Let's just throw you in there. Then we'll come to avoid head to head and what just while I'm here.\u003C\u002Fp>\n\u003Cp>And return move. Right. Return head to head. So now we have access to you in here. So we want Datastock Global's my head, you, and my head.\u003C\u002Fp>\n\u003Cp>Cool. So that's gonna be you dot, why why did I do you? We just care about my head. What are you doing, Kevin?\u003C\u002Fp>\n\u003Cp>Andrew: Yo. Because you wanted to remove the neck. You were this\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. But not oh, yes. I suppose that will be that will become important, but the neck comes later. Yes. So we just wanna say my head dot x minus 1 and y equals my head dot y y.\u003C\u002Fp>\n\u003Cp>That's left. Bright. So that's the the same. Up. And so that's gonna be x remains the same, but y this time, r is minus 1 and down, plus 1.\u003C\u002Fp>\n\u003Cp>No. It starts the other way around. Down because bottom left is 0. Up. So that's pushing in push in all I mean, it's it's pretty obvious actually by the, by by the name of what's going on.\u003C\u002Fp>\n\u003Cp>Then we wanna remove our neck from that. Then there'll only be 3 items in the array. So we'll go possible next steps. How do I do this? Equals poss that becomes a let.\u003C\u002Fp>\n\u003Cp>Possible next step, stop filter. Possible next steps. So steps, where the step is not equal to you. You dot what is the it's a bit disruptive, but I'm gonna just what is the what's it called? U.body.body.\u003C\u002Fp>\n\u003Cp>Andrew: I love that my my many, many if statements, my heuristic was not okay, but there's this will be so much cleaner though when it's done. I will agree with you there.\u003C\u002Fp>\n\u003Cp>Kevin: Thank you. So this is remove net. I think that's what that does. So every item where that because all the body parts are is this. It's an x and y.\u003C\u002Fp>\n\u003Cp>Right?\u003C\u002Fp>\n\u003Cp>Andrew: Yep. Yep.\u003C\u002Fp>\n\u003Cp>Kevin: I I'm a little bit I think I think object, object comparison is a little bit hunky. So what I'm gonna do just to be on the safe side here is say, where s dot where s dot x is not equal to x and where it's not equal to y. So if both those things, no or no and hang on. This is this is important. I'm gonna do it the other way around.\u003C\u002Fp>\n\u003Cp>So where they are equal to x and they are equal to y, that means, hey, this item matches or wrap the whole thing in another bracket and we will negate it. Okay. That should be removing. Does that make sense?\u003C\u002Fp>\n\u003Cp>Andrew: I'm so glad we have a video because that's super unreadable, but I get the logic makes sense. No. No. No. Not not unreadable.\u003C\u002Fp>\n\u003Cp>Unreadable. I mean, like, I feel like if somebody looked at this, they would be like, what is happening in this line?\u003C\u002Fp>\n\u003Cp>Kevin: So, I mean, I mean, we're removing the yeah. Yeah. Right. Okay. Sure.\u003C\u002Fp>\n\u003Cp>Yeah. So Who needs to\u003C\u002Fp>\n\u003Cp>Andrew: read this code except for us?\u003C\u002Fp>\n\u003Cp>Kevin: Exactly. No. This makes sense. Okay.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. I\u003C\u002Fp>\n\u003Cp>Kevin: think that works. So we removed the neck. So that's removing that's saying all of our possible neck positions. Now we wanna basically figure out all of the every other snake's possible next steps. And if there's any matching item oh, there's a there's a silly, silly little thing going on here, which is we're gonna push in.\u003C\u002Fp>\n\u003Cp>I'm gonna just call the variable c to mean coordinates and that and this is where it all starts falling apart in terms of, like, clarity. And I am gonna I am I I've decided I am gonna be more explicit here.\u003C\u002Fp>\n\u003Cp>Andrew: No. I think it was\u003C\u002Fp>\n\u003Cp>Kevin: I mean, I\u003C\u002Fp>\n\u003Cp>Andrew: think it's I think it's very clean code before, but, yeah, I think that this makes a little bit more sense.\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. So we have the cohorts. And the reason this matters, It's because we don't just want to store the coordinates. We also wanna store the actual reference to which direction it is. So as well as the quotes, we are gonna say direction left, and we can get rid of that.\u003C\u002Fp>\n\u003Cp>We can get rid of that. So direction left, direction right, direction down and direction up. Right? So now we just have to change this bit here because it's not s dot x anymore. It's s dot quartz x and that's dot quartz y.\u003C\u002Fp>\n\u003Cp>But it will when it removes it, will physically the directions will no longer and then at the end, we're gonna just return the those directions. So I think that that's an important thing. So we can actually ultimately at the end change is move safe and know what values we are we are removing. Yeah. Because before, there's no how do we know which one's which other than the order we initially push them in?\u003C\u002Fp>\n\u003Cp>Andrew: Mhmm. Not a\u003C\u002Fp>\n\u003Cp>Kevin: nice. The next thing we want to do is go around every snake, and we wanna basically do this same game here where we go left, right, up, down, push it into an array, cross reference them. If any of them are the same, we know the direction in which they match. Oh, that That's funny. Off reactions gone.\u003C\u002Fp>\n\u003Cp>Are you familiar with these?\u003C\u002Fp>\n\u003Cp>Andrew: I am, but you're in Arc. Right? Is that where\u003C\u002Fp>\n\u003Cp>Kevin: it actually is?\u003C\u002Fp>\n\u003Cp>Andrew: Or is it within Riverside?\u003C\u002Fp>\n\u003Cp>Kevin: No. It's it's macOS.\u003C\u002Fp>\n\u003Cp>Andrew: Oh, it's in Mac. Really? No way.\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. But I don't Well,\u003C\u002Fp>\n\u003Cp>Andrew: I'm not I'm on Windows. Windows is like, no. We don't we don't think these are things that we need.\u003C\u002Fp>\n\u003Cp>Kevin: That is so funny. So now we now we wanna do the same thing for this snake. And this is this is the important thing is that for every snake, we are literally gonna do this exact same logic, except we're not gonna remove the neck. We're just gonna say, oh,\u003C\u002Fp>\n\u003Cp>Andrew: are we gonna remove the neck? No. No. We'll just remove the head.\u003C\u002Fp>\n\u003Cp>Kevin: No. No. No. No. Because if we you know, because if we're colliding into the neck, we're colliding like that would have been handed by the dome.\u003C\u002Fp>\n\u003Cp>Yep. Yeah.\u003C\u002Fp>\n\u003Cp>Andrew: Exactly. Yeah. So it's just the head. Exactly. Exactly.\u003C\u002Fp>\n\u003Cp>Kevin: So we just care about this logic. So possible next steps. And I am just gonna just shrink this down slightly because it'll be contextually clear what's going on here. So it's no longer my head. It's no longer my head.\u003C\u002Fp>\n\u003Cp>It's head. Head. Okay. And so that's so that's that. We're doing that for every single snake.\u003C\u002Fp>\n\u003Cp>And now this is the important thing we are going to compare. Yeah. We are going to compare. Let's just have a think about this. We actually don't care about the directions this time.\u003C\u002Fp>\n\u003Cp>The directions are irrelevant. I think, I think we'll leave them in for the sake of having the same data structure. So we know exactly what we're doing, but this bit's irrelevant. We just yeah. It's it's irrelevant.\u003C\u002Fp>\n\u003Cp>So this n is going to go before it's just one big array of every snake. We don't need to have it on a per per snake basis. So this is us populating and with every viable direction that could ever be in. Actually, I've had a change of heart. We are we are gonna Sure.\u003C\u002Fp>\n\u003Cp>That down.\u003C\u002Fp>\n\u003Cp>Andrew: And then we're just gonna be comparing our end versus our possible next steps. Hence, look, we're\u003C\u002Fp>\n\u003Cp>Kevin: just gonna be You absolutely you absolutely got it.\u003C\u002Fp>\n\u003Cp>Andrew: I like it. Okay. This is cleaner than I thought. I was definitely you were, I get where your head is at with removing some of this stuff now. I love it.\u003C\u002Fp>\n\u003Cp>It took it took you getting to the end of this for me to be like, yeah. I I this makes sense. Good. Good. Good.\u003C\u002Fp>\n\u003Cp>Thank you very much. Thank you, Ro. Smart code. Very much.\u003C\u002Fp>\n\u003Cp>Kevin: So now what we're gonna do is compare every single value in n. So we're so what we're gonna do now is and yeah. Yeah. Perfect. So we'll we'll loop now.\u003C\u002Fp>\n\u003Cp>So it's a for let n of, can't do n.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. If you just change n to to next moves or something like that or or yeah. Yeah. There you go.\u003C\u002Fp>\n\u003Cp>Kevin: Sure. Oh, I see. So for so next, I'd rather that's fine. For Yep. So I can use the shorter version inside the list.\u003C\u002Fp>\n\u003Cp>I'm about to use it, I think, a reasonable amount. So for every single value in there, is it one of those? It's all we care about. Let's have a think let's have a quick think about how we're gonna achieve that. Okay.\u003C\u002Fp>\n\u003Cp>So for every single one, we're gonna say, does next possible Does value in one array appear in another?\u003C\u002Fp>\n\u003Cp>Andrew: Hey. There we go. Nice little vanilla JavaScript there.\u003C\u002Fp>\n\u003Cp>Kevin: Absolutely. Include some and include. Some text element against the test function that return. Yeah. It's great.\u003C\u002Fp>\n\u003Cp>Cool. I don't think this is, oh, but it's that's no. It's that same thing again with objects. So I think the object nature of it, like, object comparisons are are specific. Yeah.\u003C\u002Fp>\n\u003Cp>It's a find and a size I think this is actually flipped.\u003C\u002Fp>\n\u003Cp>Andrew: What are the responses? Did they say it actually works?\u003C\u002Fp>\n\u003Cp>Kevin: I think so. That's a bit of a it's a bit of a big big example. Got it. Got it. Lovely.\u003C\u002Fp>\n\u003Cp>That's the answer. So I'm gonna just I'm gonna just pop it here and comment it out so we just have a little reference at the point where we're writing it. So this is an array. Let's consider this as our possible next steps. Yeah.\u003C\u002Fp>\n\u003Cp>So we wanna say, for, matches r.sum. It's not called r, is it? It's called next possible steps. Possible\u003C\u002Fp>\n\u003Cp>Andrew: next steps. Possible next steps.\u003C\u002Fp>\n\u003Cp>Kevin: Possible. I'll fix that in a moment. So sorry. The sum method of an array test whether at least one element in the array passes the test implemented. Okay.\u003C\u002Fp>\n\u003Cp>So next possible did I make a typo?\u003C\u002Fp>\n\u003Cp>Andrew: Yep. Possible next steps, not next possible steps.\u003C\u002Fp>\n\u003Cp>Kevin: Just Thank you.\u003C\u002Fp>\n\u003Cp>Andrew: It's what I'm here for\u003C\u002Fp>\n\u003Cp>Kevin: Possible next steps. And it immediately changes color to be like, yeah, you can spell now. Thanks. Possible next steps dot sum item. We'll do that again, and it's item dotcoords dotcoords.\u003C\u002Fp>\n\u003Cp>Dotxmatchn dotx. Dotxanditem.coords.y. For n of next. Is this is this how is this really I I think I I think there's some redundancy in here, actually. Yeah.\u003C\u002Fp>\n\u003Cp>What? Yeah. This is all we're this is this is trying to, like but we're already in a loop. We're already in a loop. So all we wanna know was, I've got it.\u003C\u002Fp>\n\u003Cp>Matching collision. Collision. Is that how you spell it? Collision Yep. Equals possible I got it.\u003C\u002Fp>\n\u003Cp>I got it. I got it. I got it. Next steps dot find and because we just wanna find does does one match. Right?\u003C\u002Fp>\n\u003Cp>And what we wanna know is step in there, is there a value where the cohorts dot x are the same as n.xandstep.cowards.y equals m.y. If this returns a value\u003C\u002Fp>\n\u003Cp>Andrew: It works.\u003C\u002Fp>\n\u003Cp>Kevin: Then that yeah. Then that collision it's gonna be a little bit of a long winded one to test because also we're gonna have to go we're gonna have to go back through the logs. This one's gonna actually be really painful to test because in theory, if it works, you know, but, so what this will return is a possible next step. And so what we will then do is if this this will have the value of possible next step in it. Mhmm.\u003C\u002Fp>\n\u003Cp>If it matches, so what we will then do is say if collision, because it will be 4 to undefined, like, some faulty value if if there's nothing returned in there, then possible just get myself a few characters because I think I'm gonna go quite far to the right here. Possible next steps equals possible next steps dot filter, and we wanna remove this item. And it's this. It's this logic right here that we already wrote, but slightly different. So we wanna say we're s.coawards.x, where collision by collision.cohorts.x.\u003C\u002Fp>\n\u003Cp>Oops. This one here, where it is the same as collision collision. Am I spelling it right? I I swear I swear I swear I\u003C\u002Fp>\n\u003Cp>Andrew: That's right. No. You're good. You're good.\u003C\u002Fp>\n\u003Cp>Kevin: I'm literally just like, you know, when words start to look wrong.\u003C\u002Fp>\n\u003Cp>Andrew: Yep. No. But it's catching it. It's catching it, turning it purple if you're right.\u003C\u002Fp>\n\u003Cp>Kevin: And this is the same as collision dotcoorg.y. Okay. I think I think this is\u003C\u002Fp>\n\u003Cp>Andrew: it.\u003C\u002Fp>\n\u003Cp>Kevin: Then oh, no. There's one more step. So now possible net, possible next steps is a shortened array which has just items which are viable. Yep. Just items which are viable.\u003C\u002Fp>\n\u003Cp>Let's just have a think because we we don't wanna\u003C\u002Fp>\n\u003Cp>Andrew: What is that actually gonna look like, though? Do we know right now?\u003C\u002Fp>\n\u003Cp>Kevin: It it will be an array of objects that look like this.\u003C\u002Fp>\n\u003Cp>Andrew: Is it just gonna be a same case?\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. Yeah. But just a shortened a shortened list. Yeah. Now what we actually wanna do now I think about it is we kind of want the others, which I've just realized we want we want the other items.\u003C\u002Fp>\n\u003Cp>We want the ones that are being removed. So Oh, to So removed from his move. Yes. Yes. Yes.\u003C\u002Fp>\n\u003Cp>Yes. So let make false. This is we're starting to get a little hunky now in terms of, like, code quality, but, like, it's fine. So if collision we don't wanna screw around with possible next steps. We don't care about that.\u003C\u002Fp>\n\u003Cp>All we're gonna do is add collision dot direction to make force, and we're not even gonna do that. We are gonna just straight up say is move safe dot left, and the value of left will be in collision dot the equals false. And that's the direct we are making the the value that we came in with. Yeah. We're setting it to false because we're gonna collide.\u003C\u002Fp>\n\u003Cp>And I'm not sure if this is valid, syntax.\u003C\u002Fp>\n\u003Cp>Andrew: I will find out.\u003C\u002Fp>\n\u003Cp>Kevin: We will find out. I might I might just save ourselves some effort here. Commission dot the okay. Alright. Sorry for bringing oh, don't don't play this game.\u003C\u002Fp>\n\u003Cp>I, I'm sorry. I felt like that wasn't an overly, like, collaborative exercise, and I'm sorry.\u003C\u002Fp>\n\u003Cp>Andrew: That was it was wonderfully collab I don't know what you're talking about. See, my favorite collaborative experiences are the ones where I can nod along and say yes, And I can I can help pick out when you've misspelled something? That's, I feel like I've I've contributed in my own YouTube. I don't\u003C\u002Fp>\n\u003Cp>Kevin: know don't know what that little error is at times. Anyway\u003C\u002Fp>\n\u003Cp>Andrew: It's weird.\u003C\u002Fp>\n\u003Cp>Kevin: Let's and this one is gonna be a little bit of a hard one to to test. So we're gonna just have to watch watch this carefully. The snake's going up the whole time.\u003C\u002Fp>\n\u003Cp>Andrew: Okay. So there's a bug somewhere.\u003C\u002Fp>\n\u003Cp>Kevin: There's a bug somewhere, but we will be able to See debug it. Oh, it all died here. Un un there's a problem in the setup of the globals. There's a comma.\u003C\u002Fp>\n\u003Cp>Andrew: Change in here.\u003C\u002Fp>\n\u003Cp>Kevin: Comma.\u003C\u002Fp>\n\u003Cp>Andrew: Comma. Those commas.\u003C\u002Fp>\n\u003Cp>Kevin: It's always the commas.\u003C\u002Fp>\n\u003Cp>Andrew: Grammar's out to you.\u003C\u002Fp>\n\u003Cp>Kevin: I'm loving I'm loving now that we don't have to touch the code editor at all.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. Agreed.\u003C\u002Fp>\n\u003Cp>Kevin: Okay. See. So where K. Avoid head to head. Fine.\u003C\u002Fp>\n\u003Cp>Unexpected token line 8. Great. Okay. 8. There is an extra bracket here and an extra bracket.\u003C\u002Fp>\n\u003Cp>No. No. No. The one at the end is the one at the end is correct. There's an extra bracket here too.\u003C\u002Fp>\n\u003Cp>Great. Let's see if that does it. Yeah. I I literally cannot believe we're okay. I was gonna say we're getting it to work.\u003C\u002Fp>\n\u003Cp>Andrew: We're getting things are happening. We're debugging, and that's the important thing.\u003C\u002Fp>\n\u003Cp>Kevin: Alright. Snakes is not defined. Okay. Not a problem.\u003C\u002Fp>\n\u003Cp>Andrew: What's the I mean,\u003C\u002Fp>\n\u003Cp>Kevin: this is good. Oh, snakes. Fine. It's all really small, like, little errors, quite well defined, quite quick to fix, allegedly. Alright.\u003C\u002Fp>\n\u003Cp>Alright. Alright. I need everyone to know at the time of recording, it is 11:30 at night. Snakes dot shift is not a function or its return valuable value is not iterable. Right.\u003C\u002Fp>\n\u003Cp>That's that's fair. And that is here, snake dot shift. So what we're gonna do is just it really doesn't matter. Like, snake star shift, and I I'm assuming it that's how it like, it needs to happen on its own line first.\u003C\u002Fp>\n\u003Cp>Andrew: Kevin, if this is what you programming at 11:30 at night does, I don't wanna see you programming when you're fully rested and awake.\u003C\u002Fp>\n\u003Cp>Kevin: Well, here's here's the joke. I'm I'm never fully rested. I mean, children. Children. Yeah.\u003C\u002Fp>\n\u003Cp>Fair enough. Well, it's going it's going up, but I also didn't set that up correctly. But let's let's keep having a look at the logs. S is not iterable. Okay.\u003C\u002Fp>\n\u003Cp>I think I've misunderstood how shift works. 1st element. Oh, it returns the first element. I don't want that. I I want to return them.\u003C\u002Fp>\n\u003Cp>Right. Right. Right. It returns just the element. It pops out of the top, but I want everything else.\u003C\u002Fp>\n\u003Cp>Andrew: Oh, everything but. Yeah. So what's the option of the shift?\u003C\u002Fp>\n\u003Cp>Kevin: It is to do the head, avoid head to head. So it is to do the work. So it's to do the shift snake stop shift, and then I think it might actually be to put snakes back in there. Yep. But I'm not sure if snakes is re I'm not sure if that will work.\u003C\u002Fp>\n\u003Cp>I think it will. Okay. I think it's not doing that anymore. This is obviously not the test, the,\u003C\u002Fp>\n\u003Cp>Andrew: we need to But we're not we're we've got no actual errors in the code that we've well, that we I use we very liberally. The code that has been produced in that final card is working and it's definitely a little bit slower now. So I think that we're definitely getting a, there's a lot more compute going into this, which totally makes sense.\u003C\u002Fp>\n\u003Cp>Kevin: Yes. Okay. We're we're still going. Woah. That was a okay.\u003C\u002Fp>\n\u003Cp>Okay. It did end up giving up. Why? Oh, because there was no viable choice. Yep.\u003C\u002Fp>\n\u003Cp>So that's Which is yeah.\u003C\u002Fp>\n\u003Cp>Andrew: Wow. Hey. We wanna move fast oh, sorry.\u003C\u002Fp>\n\u003Cp>Kevin: What do you mean?\u003C\u002Fp>\n\u003Cp>Andrew: I was gonna say if we wanna move faster for the head to heads, put multiple snakes in more than just one.\u003C\u002Fp>\n\u003Cp>Kevin: Okay. I I think we I think we got a snake.\u003C\u002Fp>\n\u003Cp>Andrew: I think we did too. I think it's working.\u003C\u002Fp>\n\u003Cp>Kevin: I'll stick 2 hungry bots in that There we go. Cough.\u003C\u002Fp>\n\u003Cp>Andrew: That should do it.\u003C\u002Fp>\n\u003Cp>Kevin: Should have hit start game first, so it did its job. Okay. Head to heads are obviously gonna be hard to to start.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. But we can\u003C\u002Fp>\n\u003Cp>Kevin: kind of see we can kind of see here when it ends it ended. Okay. So now we can watch the whole thing.\u003C\u002Fp>\n\u003Cp>Andrew: Yep. So he's not what's he turn why is he turning? Was there a safe move there?\u003C\u002Fp>\n\u003Cp>Kevin: There was a safe move. How do I go back? Pause back. No. There wasn't.\u003C\u002Fp>\n\u003Cp>Andrew: Nope. There wasn't.\u003C\u002Fp>\n\u003Cp>Kevin: It got itself into a corner. Fine. I'm confident I mean, I'm not confident enough. I do that were there any head to head opportunities? And I didn't see one, did you?\u003C\u002Fp>\n\u003Cp>Andrew: Nope. No. But I think I think we're not getting any errors at that final stage, and I think that's a good sign that the logic in there makes sense.\u003C\u002Fp>\n\u003Cp>Kevin: You yeah. Yeah. But it might not be doing what we want. So what other few do you think 11 by 11 is still a good size, or maybe I should make it small?\u003C\u002Fp>\n\u003Cp>Andrew: Make it small, and then we can see faster. And, yeah, give that a try. Yeah. Yeah. There we go.\u003C\u002Fp>\n\u003Cp>Yeah. Run that and see what happens.\u003C\u002Fp>\n\u003Cp>Kevin: Oh, it's it it went on for a reasonable length of time.\u003C\u002Fp>\n\u003Cp>Andrew: K. So there looks like\u003C\u002Fp>\n\u003Cp>Kevin: No. Our snake didn't didn't need to didn't have a head to head moment. Well, I mean, it avoided a potential head to head moment there, but But it's I'm happy I'm happy in no. Yeah. Because we'll never know, will we?\u003C\u002Fp>\n\u003Cp>Yeah. We'll never know.\u003C\u002Fp>\n\u003Cp>Andrew: We'll know if we play enough games over a long enough period of time, but I think the code is there.\u003C\u002Fp>\n\u003Cp>Kevin: And we would know it doesn't work if there is a head to head, but they don't happen often enough. Hey. We leveled up our snake.\u003C\u002Fp>\n\u003Cp>Andrew: That's solid. So not only move it to a different platform where it's running, same logic, and then adding that extra step and super visual. Super visual. I'm very I'm very,\u003C\u002Fp>\n\u003Cp>Kevin: very satisfied. I'm very satisfied by it that working. Awesome. I think that brings us to time. I mean, we we have to be at time at this time.\u003C\u002Fp>\n\u003Cp>You gotta go to sleep. I've got yeah. My kids will be up in 5 minutes. So, awesome. Thank you for for for for doing this again.\u003C\u002Fp>\n\u003Cp>Andrew: Yeah. Thanks for joining us, everybody. I feel like I'm glad that you were driving this time because had I been driving, this would have not gone as smoothly in any way, shape, or form. So I feel like this was gonna be\u003C\u002Fp>\n\u003Cp>Kevin: be silly.\u003C\u002Fp>\n\u003Cp>Andrew: But I I am excited to eventually drive when we, again, in episode 3 or 4 when we start to implement some other technology. So do we know what we're doing on our next episode yet, or is it all\u003C\u002Fp>\n\u003Cp>Kevin: In our next episode, we're gonna stay in Directus, and we're not gonna be leveling up our snake. But given that Directus is backed by a database, we're gonna start actually storing data about games and then seeing what we might be able to learn with Directus Insights.\u003C\u002Fp>\n\u003Cp>Andrew: I love it. I love it. That's gonna be an awesome next episode. So, we've got a snake. You've seen how it works.\u003C\u002Fp>\n\u003Cp>That's all of the steps for the starter snake that are worked out in there. It's actually just a functional JavaScript snake. You could pull all of that logic out. Well, most of that logic out and just put it into a JavaScript Express server and just go to town. But, but we've got it working in here.\u003C\u002Fp>\n\u003Cp>And so now let's do a little bit more. Let's see how powerful you can make Directus with Battlesync data. I'm very excited.\u003C\u002Fp>\n\u003Cp>Kevin: Yeah. This is awesome. So until next time, this has been ready, set, battles, stake.\u003C\u002Fp>\n\u003Cp>Andrew: We're getting better. We're getting better.\u003C\u002Fp>\n\u003Cp>Kevin: Some some would say. Some would say. Till next time, folks. Bye, buddy.\u003C\u002Fp>","Hello, and welcome back to we've got a catchphrase, Andrew. Are you ready? Ready, set, That's easy. We got 4 more We got 4 episodes. Exactly. We're we're we're we're cutting our losses for this one. In this show, we are continuing to build a Battlesnake, and progressively enhancing it, using Directus and Devcycle. My name is Kevin. I work at Directus, which we'll be using a little bit today. And My name's Andrew, and I work at Devcycle, which we'll be using in a future episode. And this is Battlesnake. Yeah. Give us the give us the 92nd for people just dropping in now. What is Battlesnake? Alright. Battlesnake is the way you wish that you had learned how to, program in a new framework or language from the very beginning. And now as a senior developer, Imagine you were playing the old classic snake game where you go around a board trying to eat food and survive. Now transform that into a programming game where you have a web server that's communicating with the Battlesnake game engine to communicate through code and tell your snake whether to go up, down, left, or right, and to try and survive as long as possible. Yeah. And we do that by implementing a web server, which implements the Battlesnake API. We've already had one episode of Ready, Set, Battlesnake. So we're not going to recap everything we did. If you are still a little fuzzy on how this works, it will probably become clear. But if you're completely fresh and you're like, I'm lost, go watch the first episode and watch a struggle through building the Node. Js starter snake. But today, we're gonna further build on top of that snake. Last episode, we built a battle snake based on the Node. Js starter, and we implemented here we are. And we implemented some logic. That logic, what what did the logic do? The logic stop listening dying by going hitting into walls. That's good. Then we got the snake to not hit itself or any other snake parts, because that also is instant kill. So that's all our snake does. Right? Yeah. Yeah. That's all our snake does. And we're probably not gonna make our snake much more clever today, but we are now going to introduce a new tool into the stack and get it out of Replit and into another environment. Anything else you think that's important, Andrew? So the great thing is you can follow along with kind of all of the base code for all of this over on the GitHub repositories that exist around. Snake. So if you're watching this and you're like, I want to do it in my own language or framework, you can go and check out all the awesome Angular projects that are over there. So actually, let's take pause for a moment because this is important. What is a Battlesnake? So you have to implement a web server that has a few specific route handlers. The a few specific endpoints. Firstly, this just route route. Right? And all this one does is it returns information about the snake, metadata about the snake. And we'll play around with that a little bit more today. The mode then we have the start endpoint, which, Andrew, I think that's the before the first move in any game. Right? So we got our start end point, which is great. We've got our root end point. What is the kind of most important endpoint of all the endpoints though, Kevin? It is this move endpoint. As we discovered last time, the move endpoint, every single turn of every single game will receive a request with the state of that game and the board, which includes every other snake, all the hazards, all the food. You have 500 milliseconds to respond with a string up, down, left, or right. And that is this is the most important endpoint. But you there is also an end endpoint, which is what you would potentially use to, like, wind down any resources or, you know, do any analytics storage, you know, and you just do it at the end. So we have these 4 endpoints that we need to implement. Move is is kind of the most critical. I think only move in the the root are required for any given snake. So that's worth noting because that's gonna be important. So what we're gonna do today, we are going to be building this snake inside of Directus. If you've not heard of Directus before, I hope you have because you're watching this on Directus TV. But Directus is is a back end toolkit to build applications. It has 2 parts. One part which is all around API generation from your database and asset storage, and the other side, which is a web application to kind of work with that data. There's a whole bunch of other stuff in there. So we're gonna be implementing a Battlesnake inside of a director's project. So we'll we'll we'll kinda see how this shakes out over the next over the next little while. Now originally, until about 5 minutes before we started recording, I thought that we would perhaps use, Directus' automation UI tool in order to build our snake because it is it feels quite viable on the surface. It does. And then I realized it ain't it ain't viable. And the reason it isn't is because one of the triggers that can begin a flow is a webhook, an inbound webhook, but it is unique, and randomly generated at the time the flow is created, which means we can't implement a root endpoint and then slash move, slash start, slash end. And that's gonna be a challenge. So rather than just trying to to power on through that, rather than sorry, rather than just trying to power on through that, we instead are gonna build an extension. Flows is effectively a UI layer or a UI version of, hooks of hooks. So, you know, we can we can do it in code. We also have endpoints, which is probably more suited for this where we can literally create complete endpoints, and this is a no doubt. So we'd probably lift and drop a lot of our logic. This might end up being a bit of a lighter episode, but there would be nothing wrong with that. But I've done the thing. I've jinxed it. You have. I did it last time, though. So in fairness, we're we're each one in, and and so I feel like Indeed. This is way to do it. Indeed. And so we're also we're gonna be implementing some some, like, very like, this whole rate limiting thing has caused us to have to think about a lot of other technologies as well too. Right? So it's not just this concept of of sort of how we're gonna implement Directus. It's also You're right. How it's gonna be able to interact with with the Battlesync platform. Right? Yes. So here here's the plan. Here's what I think the game plan is. We're gonna spin up, direct us locally using Docker. And that is an environment in which we can build extensions. And we could then just deploy it somewhere and be happy days, you know, deploy it on some some web service, or we could just, like, open up a tunnel to our local machine. And ngrok is kind of the tool that we've as as developer educator types have have historically jumped to, so I think it will be the tool we use today. I also found out today I'm apparently still paying for it on a grandfathered plan that gives us the pro features. So, you know, I think it's one I might wanna Let's do it. Get off the card, but I'll use it today. I saw a I saw a really great, idea from someone recently just to, like, automatically, not only list all of the subscriptions that you have, but actually unsubscribe you from something if you haven't accessed it in a while. And I was like, that's a brilliant idea because I have so many things to subscribe for that I'd like to remove my credit card from. Yes. But I'm happy in this moment in this moment that it's working. Now one thing that's really nice here is the extension entry point. If we look at this, example, the way it's built is you give an endpoint an ID, which becomes the kind of root path. So this would be like our director's project slash greet, and then you can create the root, the slash move, the slash start, the slash end, and so on. So this part should be pretty straightforward. Let's let's see, and if the docs end up being misleading, that's literally my job. So it's it will be incredibly self inflicted. So we'll we'll see. So we're gonna start by following the Docker guide for getting started with directors. No. We're not. We're gonna look at the self hosted quick stuff, which which is super smooth. It is a very smooth Docker process, I will say. Like, setting it for the first time, it was it was chef's kiss. I like it, and it's pretty, we we kinda sometimes refer to it. I mean, we will use different words, but I call it like full fat. This is the full thing. There's directors cloud, which is like, you know, a a managed platform. But this if you self host it, it's full fat, which is really nice. So I already have Docker running in the background and I already have a code editor here, completely completely blank code editor and a new directory, which I've opened up. So I think we're just gonna we're just gonna crack on straightaway in here. So first thing we need to do is create a Docker Compose file. It's a Docker dash compose dot YML file that describes all of the settings that we have with directors. We may need to fiddle with this later, But for now, docker compose dot yml. And let's make my font size significantly bigger. There we go. We can copy and paste the value in here. Keys and secrets are meant to be random values. They're used for, identifying, your account in, like, a horizontally scaled setup and the secret is used for JWT validation, and we don't care about that right now. So we're just gonna leave those. Hunter 2 is when I set up projects my default. So we're gonna Good to know. Good to know. Good to know. Good to know. I'm searching around for past Kevin projects and just trying to break in. Absolutely not. And no. No. That will that will not be good. We're gonna use, yeah. Sorry. Go on. No. I was gonna say an interesting note here is I discovered with the self hosted version, you can't use a fake email address. You can't use dot test. It won't actually accept it as an email address in the platform. Yeah. Interesting. Interesting. I was when I was building out that railway sort of like starter kit for it, I was trying to just set it up with a test and it would not it wouldn't go. You had to put it in an actual address. Interesting. As in, like, it follows the structure of an email address, but it isn't real because it should do. That should be fine. I think it was because it was a dot test, t, like, t Yeah. But that that's they're legit characters in an email address. So anyway anyway. Things that's important yeah. One thing that's important to know about directors is it doesn't provide a database. It connects to an existing database. We're gonna use this lightweight file based database called SQLite. We're gonna point it at the file. And on first run, it will create the file for us. That isn't Directus, that's a SQL like database that Directus connects to. So just a little bit of important distinction there. 10.8.3 is the latest version at the time of recording. By the time of publishing, that will not be the case. I think we might be on 10.9 by now. That's pretty exciting. Yeah. All good Database persistence, file persistence, extension persistence. WebSockets are enabled even though I'm not sure we're gonna use them. And instead of, continuing to talk through every line in excruciating detail, I think we can just write Docker Compose up. It should kick off. Great. It's attaching. Boom. Boom. Boom. Doing a bunch of stuff first time and running now on local host 8055. So if I hit local host 8055, brand new directors project with randomly auto filled credentials, which are not I think I have to move them out of, yeah, manage passwords, but that's not for today. I bet you that was a hunt to 2 password too. But this is a blank director's project ready to go. So that was that was great. What else is important here? If I open up oh, I haven't opened up the folder. This is annoying because obviously the terminals attached to it. So let's just, let's just stop that running. Let's open this snake folder here. I think I might have been able to add folder to workplace. We're here now. Let's just run Docker compose up again. Great. And just refresh that and everything should still be fine and dandy as it is. Now the first time we ran that, it created some directories here on the side, which I think are gonna be quite small right now on people's screens. But as a database directory with our SQL like database and extensions directory and then uploads directory. Grand. So we have this direct as project and it's great that we, you know, can go in and create, you know, collections in our database and upload files and create dashboards and all of that stuff. But really, all we care about is implementing these endpoints from our Replit and bringing them over. So let's go back to the docs. Let's talk about building extensions and get that get that game get that game underway. Directus has a whole bunch of extensions. Some of them affect the UI that that web app that we just had open. Some of them are API side like custom endpoints and hooks. Yeah. As it says here, hooks are similar to flows, but they do not have the UI, which is in the director's data studio. I think to create an extension, we're gonna go I'm sounding a little uncertain. I don't know why. Like No. I love it. It makes me feel much better about about episode 1, now that you're feeling unconfident about the thing that you deal with every day. I feel like imposter syndrome is real, but at least you haven't had to Google how to make a JavaScript loop yet. So Oh, good. The episode is young. Run so I'm gonna run this MPX create directors extension. I'll pick an endpoint, pick a name, directus dash extension dash, snake. Snake. Snake. I it doesn't it doesn't really matter. We'll use JavaScript. We'll auto install dependencies, and we'll give it a moment to scaffold that extension for. This is a nice CLI. Like, I gotta say, this is a very enjoyable experience. Yeah. There's still work that needs to happen, and it is happening. But, yeah, this boiler plating isn't too bad, actually. I, yeah, I I I quite like it. So inside of our extensions directory, we have this new snake. We have source. We have this index dot JS. Grand. Now what we want and so this, I think I think this might be available, I think, at directus dash extension dashnet like slash. It's a little bit clunky, but we're not going to use this format. It doesn't really matter. We're going to use this. We looked at it earlier, this alternate format, where we can do all of the subroutes straight in here. So I think I think I can just copy and paste this. Kevin, is this just is this just vanilla, or is this React? Like, I this looks very clean, but it doesn't Yeah. So this so the front end of Directus is a view app, but this what you're seeing right now and ding, ding, ding, I think you've just spotted it. Endpoints use oh, directors also uses the express router. So it is very much what we did in the first night, which is why being able to lift and drop it is gonna be gonna be quite nice. It's so clean. I dig it. I love this. This is beautiful. Yeah. Thank you. So we have snake slash snake intro snake goodbye. So let's get this running. So we're gonna c d into our Directus snake directory and we're gonna run npm run build. That's built the extension into a disk directory. And now we need to restart Directus now. I think I think it might be broken, but I think we can get it to I think we might be able to get it to, like, auto, like, load the the changes. What am I looking for? Self hosted config. Okay. So we are looking for extensions, extensions auto reload. Here we go. So Boolean will automatically reload extensions when they have changed. So we're back to our Docker Compose. We'll throw that in and make the value true. Maybe that will work. Maybe that will work. So run Docker compose up again. It's now gonna use this Docker compose file again with this new variable. Let's take a little look through the logs because hopefully yes. It says loaded extension, direct us extension snake. And then the hope is if we run npm run build again, it hopefully would have auto loaded, but that isn't the case. That's fine. That means we're just gonna have a slightly clunky development experience. So I'm gonna have to control C up enter, you know, off the baton. That's fine. I gotta say though, like I remember like outside of this, I feel like we've gotten very spoiled in the development world now. Like, that used to be just the way that you did it. I used to use some. Dragging files with Filezilla or Cybernet. Yes. A 100%. Yeah. Yeah. I that's so I thought that's how you did it. I genuinely thought that that was the only way to do things. No. Yeah. But not here. Like, this this, I'm not quite sure what the deal is. And by the time this is published, maybe this won't be the case anymore. Just for the last couple of versions, if you're running under Docker, I've noticed this environment variable isn't working. Our team's aware that's fine. I just wanna try it on the off chance there. Yeah. Whoops. So if we hit 8055 snake, we get the hello world. If we try snake slash intro, we get the nice to meet you. So these are endpoints that are returning stuff, which means we should now be able to lift and drop our snake pretty expediently. Yep. So what we what are we gonna do here? We have handlers dot info for the root. Yep. So you're gonna grab that. Return that. So I am just gonna make this slightly more verbose so I can space it out nicely. Yeah. So, can you bump up the size a little bit too? A little bit more? Of course. Is that any any better? That's that's much better. Yeah. Lovely. Rez got Jason. Yep. Do you need to return it? I I believe so. Yeah. Cool. Nice. Yeah. I think everything you need to return. Whoops. Got a little too a little too direct there. Let's, let's while we're here, maybe let's do some customization of the snake. That feels good. Yeah. That's good. So head over What color do we wanna make it? Oh, what's the direct is it a direct purple? Is the is the color purple that you're using? It is a purple. And, honestly, my lot are gonna kill me for, like, not remembering it, but I think it's, like, 6 6. I wanna say it's like 6644 f f. I like it. Let's see. We'll find out momentarily whether this is Yes. Yes. That's right. I got it. 6644ff. I logged into my Battlesnake account. We're gonna look at the customizations. And these are the customizations we can have for our snake. Andrew, you you may do the honors from the ones I have. Oh my goodness. So I'm a big, big fan of mustache snake, but I'm pretty sure that there if we scroll down, there may be a rabbit in here now. I thought I thought we had play around seeing if I could get the director's Bunny to look good in one of these, and I had just I couldn't with, like, the it has to be full width on the left. The Bunny, let's take a look. Do we have a Bunny? If not, I know he exists in the backlog. So we may we may be able to get Bunny out at some point here, or he may be locked down. Either way, if not, if we can't get our bunny Oh, I have 200 I have 200 coins as well, so I can unlock any one. Oh, so what do we see? Do we see any that we really like? So we got chicken. I quite got I quite like this dragon head here. I have to be honest. Yeah. Take the dragon. But is it was really funny. No. They're all 200. I don't see a bunny. No bunny. That's disappointing. So let's go for dragon. I feel like that dragon head Well, hang on. Let's let's look at the tails too. Let's let's, you know, maybe not spend all of our coins at once. True. No. I think I think the I think I like the heads. Well, yeah, let let's do the dragon. I've done it. Right. So the head we want is dragon, and the tail is So, I mean, let's go I think cosmic horror's tail is pretty fun. I like that. I like that. Okay. Cosmic Chorus. So that's our that's our snake. And once again, actually, should we get all the logic in? I think we should do this bit by bit. Like, we could now go off and make sure that this works, which is gonna be a whole task in itself. Does that feel okay? Yeah. Yeah. I think let's do that because then once we know it's working, then we can kinda go through. Is that what you're saying? You're thinking, like, let's go in and actually add it to the platform. I am with NVIDIA because we gotta get we gotta get ngrok set up now. Yeah. So do you wanna explain what ngrok is while I get sucked in? Yeah. Yeah. Absolutely. So if you've never used ngrok before, it is a way to publicly expose your local, development environment to the outside world, whoever you would like to be able to access it. So if you often use, say, Gitpod or, Codespaces, there's often the way that you can make that public u that URL from private to public. That's fundamentally what this ngrok, endpoint that it's giving you is doing. But you can do it on a on a, case by case basis. And you can kinda keep it in one place, and you have a bit more control over the power of that and who can see it and all the rest of that stuff. It's our and it's really great, especially if you're, like, say, somebody that's building a site and you wanna see what it looks like on mobile. I think they might still do it. But I know there's some services that I've used that it would automatically generate a QR code from the n grok address so you could just, like, scan with your camera and then view on mobile. Doesn't impact us here, but, really, really good. And ngrok has taken a lot of steps to make things more secure as well. So it used to be that sort of anybody could spin up an ngrok address and there was no issues at all. And they've added a lot more security and ensuring that the things that need to be in place are. Yeah. And I will say around ngrok, if you are, on a local development environment on a Windows device, so a big Windows developer, there's a service called Laragon, which is like, an alternative to like a LAMP stack. And it has ngrok built in. So in there, you can just click share and then it automatically will, like, push that out to ngrok that you could share with people. But, yeah, powerful, powerful service. So there's a little bit of a moment here. I'm not quite I don't quite have the memory of how this works. I think it works like this. I think based on what I've read, So you do ngrok HTTP, the port, which is 8055 and then dash dash domain and then your your domain. No. Reserve this name on your dashboard. You don't need to do that. It'll automatically just generate a random one for you. So just No. Yeah. But I don't want that because I'm gonna have to keep updating it every time it restarts. I think I have I have a static. I have a static. Oh, I see. Like, because I because I'm on this plat this old plan. Right? Yeah. So I wanna work out Yeah. I haven't touched ngrok in in a little while. It's funny, like, it stopped working in that environment. There was, like, they hadn't updated the way that was integrated, and they added an authentication key. And so, it switched things up. So your guess is as good as mine. We're gonna get oh, alright. Here we I mean, yeah. I suppose I could just do the help, right? So start engrop. That's cool. Basic auth. Cool. OAuth 2 cool. Here we are. Use it the same time by using dash dash domain. But, and I thought maybe I, maybe I. What's your domain? I thought it was that. That's why I that's why I paused. So if I look at domains, there it is. Unless I have to do the whole domain. Try the whole domain and see what happens. Because it because it did just say the ngrok.io is only for, like, legacy accounts. Yep. And that was it. Yeah. There you go. Great. So we've now got we've now got engrok rocking and rolling over here. Great. So now what we're gonna do is go over to battles go over to Battlesnake. We'll create a new snake like we did last time. So this is ready, set, battlesnake. Ready, set, battlesnake. Cake. Yes. That's it. Nope. Nope. K for Kevin. That's it. I love it. Beautiful. So https phzn.ngrok. Io\u002Fsnake. Engine region, Netherlands or Netherlands? Yeah. Well, Europe. So yeah. Sure. Yeah. Programming language, JavaScript, and platform Director. Should be there. Directus. Now my question here is, does the logo look good? Oh, let's see what happened to it. So I used the I got them to add Yeah. It doesn't look too bad. Yeah. It's an old it's an old one. So we'll fix it by the next episode, but it's good. It's just colors just colors and details. But yeah, that's that's our little bunny rabbit boy. Unknown API version. So there was an error. Yep. Even though we copied You should paste it. So what I would say is, I think I think I think I think you might need to just reset. Because this used to happen on Reset reset what bit? Like oh, sorry. Reset everything. So just, like, ping it again. And if it doesn't work, then actually start running, like, restart your direct to server. Okay. This used to happen with replit all the time is it would get this, and then it would just need a couple minutes to start syncing up in the right way. I don't know what that mean. I don't know what that means. No. It's no. That's right. So if I hit that endpoint Yep. Oh, it still says hello world. Oh. Why? Oh, why? Because we needed to rebuild the extension, then we needed to restart director. So there you go. That was our little people. What are you doing? No bill. What what what are you doing? Hit ping. Perfect. There it is. Oh, battle snake. It looks sick. Yeah. I love that. Yeah. So now we have a snake. Right? But we haven't implemented any other endpoints. So this is the time to do it. Now this is the point where maybe, like, we split out the move logic into a separate file to stop it. No. Do you wanna know what? I actually wish that the move logic was in with everything, even in the JavaScript starter snake. I understand why it's not, but I actually think it's way better to have everything in one place just so all of your routes are are there and you're you're able to change that data. So I think I think let's do exactly what we That works for me. So there we there we go. And I am just gonna shrink this slightly and shrink this slightly so we can get the replet up and kind of do a little do a little moving from one to the other. Now can I, can I shrink this sidebar down? Yes. Perfect. And maybe even shrink that. Okay. Rock and roll. Get rid of that. Oh, and get rid of that. There we go. So first thing we're doing is is move safe? So move, get some data in. Let's console log the request just so we can make sure, like, is it in request dot body? I I mean, I'm assuming it's in request dot body. Yep. Let's let's just double check that, and just respond with hello. I like that after our last adventure where we had to disappear and through the magic of editing come back, we've decided to now add console logs earlier as we go so we can save ourselves some trust down the road. That's my style. That's my style. Discum. Okay. Experiential. This is the point where and these these logs are gonna get noisy in a moment. Yep. I may even take them down inside until we need them, and then I can make them smaller still and then I can bump them back up. So what are we gonna do here in Battlesnake? We have a Battlesnake. Let's create a game, create game. Add ready set Battlesnake standard board 11 by 11. And that's good start game. Good. 404, 404, 404, 404. Well, great. So but here's the thing. If you didn't watch episode 1 so here's the interesting thing. Is technically, technically in Battlesnake, you can actually win if you 404 or have a 500 error every single time because you have to set a default move that your snake makes. And I think that there's something, I mean depending on the implementation, I think there's something cool that you could do about actually having a way to change that default. Anyways, but then game's still in progress? Yeah. It's been weird and slow. It's, like, not actually still in progress, but the platform's taking a bit of time to catch up with, I think, what's actually going on. So Why is it 404ing? Root to talk. Kevin. Host. So we'll rebuild that, and we'll restart directors. I was using the wrong method right here. It's a post request to slash move. Okey dokey. So create game, add ready set battlesnake, hit start game. That should be better. There we go. So move gets hit. Oh, so this is it. So the data comes in. Yes. Req dot, req dot body. Fantastic. So now let's move all of this over. I might do a little bit of cleaning up as we go. It'd be interesting. Yeah. Never mind. Let's let's get to it. Let's get to it. Go ahead. I was gonna say it's it'll be interesting if we if we wanna add the start and end point and log what's coming through there. I think there's something interesting in that as well, just to kinda see what that looks like before the logic is implemented. Because it does get really busy once the logic is implemented. At least right now, we're just getting those 404 errors or, like, the basic logs. It's a start. And is it a post request? I think it is. Yep. Yep. Post. We probably should respond. Right? Yeah. And and it'll it'll get angry with us. But And and. Yep. And just because they are gonna get in the way, I'm just gonna pop them down there. Okay. Let me just, cough. Let me mute. Yeah. And so what's interesting here is so I think we we said this earlier, like, the start and end points. You don't really need them for anything, but they're useful if you wanna implement some more serious logic, which we may do sort of with our direct as Battlesink down the road. So you'll see here once we rebuild and then save again, you'll actually be able to kinda see the stuff that comes in Yeah. Through there. So we need the width The height. And the and the height and the Head. Snakes. I think that's all we need. From rec.body.board. Right? Yeah. It's all in this thing called board. Yeah. Great. And the other thing we need is my head, and I am just gonna copy and paste that little convenience there because that's that's wicked. If you by the way, if you really don't wanna have those 2 constant, like, lines that you have there, if you're just, like, really focused on compressing, you can technically pull it from that sneaks variable that you're defining in there. It's just a little bit more more to do it. Absolutely. Yeah. Just can't I mean, because be bothered. Can't be bothered. Right. So so so we've got the we've got the width and the height. So first thing we were doing is don't don't move out of avoid walls, and we're just gonna copy and paste it. Now we're not. First thing we're gonna do is set do the is moves we're gonna do the is moves safe thing. That's the other bit of, boiler plating, I guess. Yeah. And to be honest with you, you don't I mean, so this is like the the default logic. Technically, you can implement logic that doesn't use this, but this is just like the most straightforward way to do it. So Sure. So this is avoid walls. So my head exists. It's movesafe.leftforce. Great. Board height. Interesting. I think we just called that We call it height Height. So we just need to change all of those heads to my head and all of those heights to No. Head to head is correct. My head. Oh, you just got to use my head? Okay. Yeah. But board height. So what we're gonna do here is say height. We'll put bring that in as board height so I don't have to rewrite everything. We'll bring this in as board width. There we go. Job done. I'm also just while we're here, I'm just gonna just gonna we'll make things just a little more little more succinct, perhaps. This is valid. Right? Just so it's 4 lines as opposed to so many so many lines. This is valid JavaScript. Really? Yeah. I hate indenting so much. I would But indentation doesn't matter. So we avoid we avoid the walls. Great. And then it's avoid bodies. And we've already got the snakes. We've already got snakes stored here. So that is the equivalent of that line there. And so we're gonna take this for loop. We will dump that in here. And once again, I'm just gonna take this moment to just, to just clean up a little bit. This time because the conditionals are longer, I probably am inclined to leave them to leave them like this, but just just remove a bunch of, you know, white spacing. And there's some funky indentation going on here. There, boom. There we go. All right. Then if there's any save moves left, the console log and we return move down. Oh, if if there are none left, we go down. I got it. I got it. I got it. So, all the safe moves if no go down. Great. There we go. And it won't be return. It'll be red stop return red stop JSON. And then it won't go further down. Alright. And it will have returned there, so it won't continue. So I don't need to announce or anything. We pick the next move. I am noticing hang on. Hang on. Game state dot turn is not correct. Let's make sure my head piece. Yep. Snakes. Everything there is fine. It's just it's just here. Game state doesn't exist. That's req.body.tan, which means oh, no. Req. Yeah. That's fine. Req.body.tan. And then res.json, then we'll look at safe moves.length, pick the next move. We'll log it. And we will return it. I'm sorry. It isn't a string. It's a it's an object, isn't it, with a move value? It is Next move. So I think I think that is that's it. That's that's a whole efficient, Kevin. That was wonderful. Okay. Thank you very much. Thank you very much. Yeah. I think that is our our snake as implemented last time. Let's rebuild the extension. Let's restart the. Once again, that shouldn't need to be the case, but it is just at this point we're recording, so so be it. And oh, that game in progressing has persisted to be a challenge. It has. So ignore. If I refresh, does it know it's done? No. Okay. So New game. We'll have to keep. New game, and we'll send in, I don't know, loopy bot. Or does loopy bot just do the Just does the loop. Yeah. Yeah. Hungry is good. Hungry is probably a good little test. Hungry is good. He was a good test for us last time. So I feel like we can see how he responds. Okay? Obviously, it's a little more verbose down here. You can reduce the amount of logs with an environment variable. We died that, you know, that's the end state. So, oh, wait. If it won't, can I hit play? Yeah. You can hit play. And sorry. Show the coordinates. Fast, please. Save game. No. Just go back. Just go back. No. Oh, do I just I'll back back back back back. Alright. So r one is the orange is the, oh, we won. So what did it say in the logs that you thought we lost? No. No. We we didn't lose. It ended. I sorry. I said we died, but I meant it ended. What does the what does the actual response say though? I'm not I'm curious. I don't have any idea. It is game ID rule set map stand up source. The oh, it's the it's an n the last 10. Yeah. Yeah. But does it say 1? Let's find out. I feel like it somewhere should say 1. I think no. I think snakes is just one long. Oh, yeah. But it could be you or anyone else, I suppose. Fair enough. It may not say it in there. I'm just curious. Maybe it's the fact you still exist. I mean, let's find out when we die. Do you know? Oh, yeah. If we look at you, it has the ID oh, let me, woah. There we go. I was gonna say I feel I felt like I wasn't seeing it, but I feel like Sorry. That was, that was a keyboard shortcut thing going on. Alright. It has a health. I wonder if the health knows that. I mean, we'll just do it a couple of times, but let's let's watch. Let's watch our our little sneaky there. You do it well. Hey. Like, that logic is good. Okay. Head to head combat, which we haven't built logic for. Maybe we'll do that this time. It's a good idea. I think we because I because I kind of feel like that was what we intended to do this whole episode. Yeah. Congratulations. We're done because we can't build it in flows, and that's the that's the thing here. I have one other I maybe you can execute a flow from Yeah. From this, and then this just becomes the entry point into the flow. That's what I was just gonna say. I was like, is there a way that we could set up the one of the endpoints in here and then have the trigger in flows? You're able to do everything from there? Maybe. That might be fun. I feel like that's an interesting way because I I do think that flows is like this interesting. I've never done it, but, I'm sure it will be fine. So let let's take another look at some new extensions documentation that was recently written. The resources on the not this one. I'm looking for resources, components, packages, extension services. Here we are. So underneath directives, every time you make a request, it's actually going off to one of these services, which then, you know, which then executes your your request. And inside of extensions, you can also access these services. Various services are available within directors, including item service, collection service, file service, and they're available within the extensions context. So there is a router here in the handler, but I think we can also pass in this context. Right? Let me just think about the best way to do this. I think it might be just playing around with this router dot get here. So, so we have this context. I'm gonna bump that back up actually. And if we console dot log, the context and we see just here, we, destructure get schema and services and then we destructure the individual service. So let's let's just do that outside of the individual. Outside of the individual. No. I think for just a moment, we'll do it just inside of this one just because this is the one we're gonna hit. So we don't we don't just have to mess with that. And then instead of doing this, let's just console log the the services and just see what happens. I'm actually gonna do a console dot here, services dex null. And what this will do is effectively a console log that never does this kind of cut off thing. It'll always show you all of the nested layers just because I'm not sure what it looks like and I don't wanna I'd rather just be a little more verbose once. So dotcom. So what we're gonna do here is go back to our root endpoint and hit enter. Oh, sorry. Not root. R snake. There we go. And what happened here? So we have some services. Let's, yeah, let's just expand this for a moment. Webhook service. I, there is a flow. I, I believe a flow service. Oh, nice. Gonna that's gonna wanna be the one that we I mean, is this the one we wanna I don't know. I mean, all we wanna do is hit all we wanna do is hit an endpoint. But Yeah. Let let's go for have a let's have let's have a play. Sorry. Go on. No. I was just gonna say, do we wanna go in? I don't know. Never mind. Let's let's test this out and then and then we'll go forward from there. Sounds good. So let so what you do here is you create a new service with the collection there. So you know what we'll do here. There are some commonly used services. We can just refer to the full list of services directly in the code base here, which is actually what I should have just done. So we have flows here. So we can create flows, create many, update, delete. This actually isn't what we want because this is all about running flows. Sorry. And the the crud of flows. And we don't wanna do that. We just wanna we just wanna run a flow. So maybe the first thing we sorry. Go ahead. No. No. No. Is it just the webhooks that we want, the webhooks service then? No. Because that's to create a webhook. That's not Yeah. Exactly. That's outbound. So so what we'll do here is we'll go to the admin. Oh, wow. We will create a little flow and we'll see if we can trigger that from our code. That's basically the test here that it works. So we create a flow. We'll call this info. Snake info. All of that feels good. Maybe traffic activity in logs. I think that's fine. Then we have the webhook. Webhook. You're right. It will be a get request, response body, data of last operation. Feed all the data just to have it? That that that's happened. So oh, no. So this is the response body. This is what it will respond with. We only want to we wanna create the response and send it. But, actually, what you're describing is it was here. It was this option here, track activity in the box. Alright. There we go. So back to the trigger setup, we'll turn cash off. We don't want it to be asynchronous, get request, save, And we will re- we- it will be the data from the last operation. So we'll just run a script and we will just return this, I think. I mean, that makes sense. Yeah. That works. I've not done this before. So we are we are really finding out together how this works. Works is very smooth. I like just being able to have it. I really like it too. So we've got a save. So now we have this idea of a flow, and I think that's the thing that's interesting to me at least. So you'd rather use the ID than actually just use the flow endpoint, like the webhook endpoint? I could. I don't think that's, like, the proper way of doing it. But could we do that? Yeah. I mean, I feel like I feel like Okay. I'm interested in seeing both ways, right? I'm interested in seeing ID flows, which I think is the proper way, and then just calling the webhook from within the code. Right? Yeah. I'm gonna be I I don't even know how to make a web request in these off the top of my head. Just I think it's just Axios, isn't it? I think it's just Axios. But but how is Axios, like, exposed? And do you wanna know what? I'm pretty confident there are just some guides that were written around this that I think will just help us. Endpoints create a public API proxy. Here we go. This will this will help. Yeah. It's a it's a fetch. Oh, alright. Cool. So we will so we hit slash. So what we're gonna do here, we're we're forget all these services for a minute. They don't I don't think they're gonna matter. No. They won't matter because literally we're just basically now that we've created that flow, we're just doing a call to that webhook and then Yeah. We're gonna do a response, await, fetch, and then slash flows, async. Okay. And then, yes, this is response okay, response Jason await Jason, all of this jazz. I normally, honestly, just do this pretty low rent, then r r dot JSON and our response is the data. So I think we can just return reds dot JSON response. Yeah? Yep. I might just be explicit here. Flow response. Flow response. Yeah. Yeah. And maybe yeah. That yeah. Screw it. Let's try it. Let's do it. Gotta get this I feel like this is why I enjoy, like, non typed languages because you just mess around and see what happens. Yeah. I mean, you can build extensions just as an FYI with TypeScript. I just didn't oh, yeah. I thought this might be a little easier. So if I hit refresh here, we should see the same the same thing the same thing. Oh, wait. Hang on. I don't know why that's happening. Oh, was there an error? You have triggered an unhandled rejection. Failed to pass URL. So it didn't like the Yes. So just add the link to the I mean, sure. But that that doesn't make this portable. Right? That isn't let me just have a quick think here. Oh, I see what you mean. You wanna be able to pat yes. Okay. I see what you mean. Because then if we deploy it somewhere else, this will Yeah. This will still work. That's that's that's gonna have another little look. Let's keep having a little look here. So still sort here. No. Because now I'm right because this is the flows. All I wanna do is know, yeah, how do I run a flow? Let's just do you wanna know I think I've just had I think I've just had an idea on how this works, which is actually not looking at the services. It's looking at the the way the SDK does it. So if we look at the, extensions, packages, sure. The SDK just like type, type doc. This is just the the full dump of everything the SDK can do. There is an endpoint in here called something there's something in here for running flows. Andrew, I'm going It is I love a c. So even if you write the documentation, everybody, you still need to read the documentation. You do still need to run it. Here it is. Right. Create a flow, retrieve a flow, list flow. So this is all the stuff we just saw, but Yes. Here we are flow with get webhook trigger, trigger flow. What is going on here? Let's take a little look. Failed to pass URL from \u002Fflow\u002Ftrigger\u002Fuid. Yeah. That I mean, that makes sense to me because it's not a full URL. Right? Do you wanna know what I did sneakily on on the side here. I have a chat with one of my colleagues because, of course, I did because I have that power. It does just need to be the full URL. So let's stop. Let let's stop battling against I was right. You're right. You're right. But you're right. It's not portable. You're very true. It's like you'd now need to, like, worry about changing this if you change where you're you're hosting it. So I do think Which I think won't matter for the scope of this. So now built that, restarted that. So now if we hit this, it did return and it did it by getting by by getting the the, yeah. So, oh, that's really exciting. Okay. Let's abstract this logic out then. Let's get this whole move thing. Let's just comment it out. We know it works. Okay. And what we're gonna do here instead is we have a post. And so, let's set up a new flow. Not here. Let's set up a new flow. Oh, that's so exciting. Okay. Cool. Snake. So we're gonna we're gonna create a move we're gonna create literally recreate this entire logic as separate flows. Why not do it as one flow, though? Oh, because it No. No. No. Just because it won't be each endpoint. So everything move is gonna happen in one flow, and that that's yeah. Yeah. It's cool, isn't it? I think track activity and logs, I'm going to level with you. I'm not I'm going to say don't track anything because it's just gonna it's gonna absolutely explode the logs because it can be every like, the full body every time. And we have the the the logs over here. So we won't do that. That will be a webhook. Data of lost operation, that's fine. Not asynchronous. Don't cache. Post. Okay. So exciting. Okay. And we will, run a script and we will return move left. Right. We'll just we'll just do that to start and just test test it works. Return move. Hit save. And we have an ID for this one. Great. And, what we're gonna do here? We're gonna do we're gonna copy this. We are gonna change the ID and we're just gonna go post, sorry, methods. I wanna say it's post and I think it's body equals JSON dot stringifyreq.body. I think that's I think that's the fetch. I think that's the fetch way of doing it. So What is the fetch way of doing it? Is it good? Okay. Fetch. My memory has not failed me today. I'm literally I'm so pumped that we're able to do it in flows. That was a really good I I think that was yours. You can have that one. No. We're gonna I'm gonna give that that's that was a 5050. We left brain, right brain thing here. I feel like it was one of those days. Okay. Okay. Alright. So we're ready set. Ready set Battlesnake, Hungry Bot. Sure. And start game. What's happening? It's happening. It's happening. It's happening. It's happening. But it will just go left, left, left, left, and die. Yep. Right? That's fine. Yeah. That's fine. It's still it's still going into the world. Awesome. Which means we are done with the code. We're not done. We'll need it for reference. Yeah. But we are in flows now. It's happening. What's interesting here too, folks, is, like, you like, literally with those two endpoints, like, you don't actually need to implement start and end. Like, you can stop with just those 2 if you're like, I don't wanna have to manage 4 flows. Like, you don't actually need start and end at this point. And so I think, like, don't use them. So first thing we did was avoid walls. I'm gonna run a script here. We'll return it, and then I'm just gonna do the it's annoying. You have to kind of create it from an existing path. Yeah. And then we're gonna take that and dump it in there. Okay. Avoid walls. Back to the back to the primitives. Now, you know what we didn't do? We didn't because we don't have the logs. We actually don't quite know what it looks like in here, which is annoying. Just a thought I've had. Yeah. It's true. But I think I know. Well, I mean, let's see. Let's see what happens here. And I think the cool thing here is, like, now we return to, like, traditional programming as well. Right? There's a lot of different ways you can implement this within the flow. Like, there's so many different operations. Technically, you could feedback a lot of the information and then make, like, decisions there or just do it. Let's let's return the data. Like, that's the whole data chain. And then so this first time, this is gonna fail absolutely miserably. But, oh, no. It won't log anywhere, will it? Do we wanna turn on those logs then? I know we didn't want to, but Yeah. Yeah. Can I turn them on then turn them off? Yes. Fine. Yeah. That's fine. Yeah. Yeah. Yeah. Sorry. I thought it was a one and done kinda deal. So let's create a new game. Create rematch. Okay. Cool. Whatever. Sure. Refresh. And there should be many Yeah. 8. That was 8 moves. So it's it's gonna get unwieldy, but this is what matters. That's great. That's awesome. Yeah. So we get Except the distinct so the trigger where's where's all the, not being oh, so Are we not sending it? Are we not sending it precisely? And this is why I wanted to check. Yeah. Yeah. Oh, wait, what did that say failed up there? Go back up to the options. No, no, no, no, no. Force. Okay. Cache enabled force, cache enabled force. So cool. We have this fetch. We send that is how you that is how you do it. Right? I mean, I could have been wrong. I was assuming, but ah, here we go. I'm glad that it has happened. Yeah. Body.stringify. Body data type must match content type header, but I I don't think sure. Do you wanna layer what? Sure. Let's just whack that header in. Just in case it's that. Yeah. See what happens. I'm not losing any sleep over over adding that. Is that an error? Oh, no. We've left a log we've left a login somewhere. All the serve hang on. They're all the services. Yeah. Didn't we didn't we remove that? No. It's the it's the context. It's this console log context. Oh, there we go. I was like, that's a big that's a big object. So right. Restarting. Okay. Okay. Okay. We're gonna create rematch. Let's get some logs. Nice. Oh, head to head. Some quick a nice quick death. Okay. Are we getting Payload. There we go. There we go. There we go. So it's in trigger dot. Body. Body, I think. Yep. Yeah. Trigger dot body. Yeah. That looks correct. So, I mean, there's one way to find out, right, which is, so, trigger data dot trigger dot Bobby. Nice one. Bobby. So let's go and take this first up. So first thing we'll do, of course, sorry, is we will is move safe. We have to think about how we, because we, we need to kind of alter this value as we go. Maybe we return that. Maybe we return that with each step. Right? So we'll start with a is move safe and we'll just return it every time. So Okay. We'll start this time, we'll create it. I also think that could be a const. Anyway, avoid walls. So we need these values. Do you wanna bump up the text size there a little bit more? Thank you. You know what? I'm actually inclined. Let's rename this one. Let's call this one set up variables. Yeah. Like that. Let's call them let's call it globals. We'll just make the key global so it's a little shorter. All we're gonna do is return is return. We're gonna return what was it called? Is move safe. We will also do forward width. It's going to be data.trigger.body.width. Old height. What was the other thing? Oh, my head. So that's all of that. And then, we yeah. Just return that. Right? Yep. Sponsored global. Yeah. Yeah. We will. Just just I I wanna be I wanna be testing early in Austin even though, you know, we're not doing much there. I just wanna I just wanna be validating that we haven't broken something there. So we will create a rematch. So all we should be getting now is an extra property in the data chain. Wow. That was a really short, really short game. I think it was, like, 2 moves. Yeah. Oh, rec is not defined. I think I left in a a remnant of what it was before. Rec.body. Yeah. Data. Trigger dot body. That's why we test. Create a rematch. What else have you used Flows for in the past? So I did the, the DevCycle Directus integration that I built out. So this was, it's worth checking out, just to see, like, the different use cases here. So it basically allowed for you to do an API call to a patch API on the dev cycle, patch API endpoint on the dev cycle platform to, like, pass basic data into into the service and to, like, put, like, an edge database for feature flag management. We might use that actually, when we start to do the dev cycle integration just because it's a really fast way, and I know that it already works with Directus. So, nice. But yeah. That's called EdgeDB. EdgeDB. Yeah. So it's just, it's just, like, a faster way to pass data. So how are we doing here? No. No. But if I refresh, I think I fixed it. Yeah. Right. That's everything in the in the global. Now I'm not sure. So I think is move safe is one that we might need to pass, like every move we're going to need to we're going to need to reset that, but that's fine. So cool. Next thing we'll do then is avoid walls. Avoid walls. We're gonna run a script. We have data, and we have our avoid walls logic here. Yeah. I feel like this is also just a really nice visual way to represent this whole This those are the steps. I really wanna go and find my Node JS snake or my, my Node RED snake that I created because I did the same it was the same process that I went through. Was it sorry. Just very quickly. Well, did I call it my head still? I think I did. Yeah? Yep. You did. Yep. So the nice thing about this is we have is say is move safe left and all of this. So I think we wanna create a new variable called is move safe, and the value of that, I think this works, is gonna be data dot globals. Was it multiple? No. Global. Yeah. Global. Yeah. I think so. Yeah. Dot is move safe. Right? Yeah. This this is And then yeah. This is something that's really interesting to know having used flows before. I was not taking advantage of the globals like that or not the globals because that's the thing you're creating here, but, like, the But the data chain. Touch on data chain. I thought you lost the data, basically No. As you went through. So that's interesting. And I've had another thought about this. I think the way we're gonna do this is using last. So last, dollar sign last is a dynamic variable that will always be the output of the last step. But that means this line will become basically a boilerplate first line on every step in, I think. I think this creates a copy. We'll find out. We'll find out together. So next thing we're gonna do is we're going to destructure, my head board width, and board height from data dot globals. And then the rest of the code should be able to be untouched. Right? Exactly. Yeah. It looks good. The only and the only thing we wanna the only thing we wanna return is is move safe. Only thing we care about. Yeah. If this works, I'm gonna be so pleased with myself. I have a hunch it won't just because, of course, why would it? Let me just move return move out the way. In fact, we can remove that. So no. Of course not. Sorry. Ah, I've deleted it now. It's fine. We're here. So return move because what we're gonna do in this step is we're gonna just do that last bit of logic. If all there safe moves, if no go down. So we're gonna get rid of that. All we care about is next move and return. Yep. Because that is a a step between where we are and where and where we're going. So I have in my here we are. This is what we just had in our last step. So we have beta dot globals. Now is move safe. There we go. So next move, safe moves. Is it safe safe? Why is it called safe moves? Oh, here. I need this line too. I'm gonna call that one. Safe moves. Object dot keys is move safe. Filter down is move safe key. Fine. Fine. And that's just getting the the the values. Then next move, is safe moves, math floor safe moves. So that that should be it, And then we should just be able to return move next move like so. So that's the last step in. That's the last step in the chain. Let me bump that up one more time. Sorry. So that's all the logic again. We have that boilerplate starts. We don't quite know if that works yet, but in theory It should. Theory, it should. It should. Dangerous last words. Those. Sorry. So we'll just set ready set battlesnake in there because we're not equipped to have multisnake yet. Nope. Did not work. What are our logs saying? Thank you. Thank you very much. Yes. It did not work. You're welcome. I feel like I need to add these useful Last is not defined. Absolutely. I know exactly where the typo was. It's data dot last, and it will be wrong there as well. Data dot last. Right? Yep. Yeah. Cool. And what's nice now is you're not having to restart Directus every time because now it's handled by the flow. That's wonderful. It is cool. There was a little bit of of, let's call it friction first time. Okay. That's not ideal. 42, 44, payload. Cannot convert undefined or null type. Okay. What are we? Payload after after but here's the thing. After avoid walls, look at that. It did say up force, which means everything's working until the return move. So that's fine. So that means it works, this whole passing passing is move safe each step. Yeah. But where it's breaking is this last one. Okay. So let's look at this again. So async function the error was cannot let's actually read the error. The error says, cannot confer undefined or null to object, which means the issue is here in object dot keys. So we have is move move safe. I think it can just be data dot last if that's all we're passing step to step because we don't return an object with is move safe. We just return is move safe, which means here again, although I mean, let's see. Let's see what happens. Yeah. I think I think that'll fix it. So if we hit create rematch Yes. It avoided pause. Nice. Lovely stuff. And let's just, let's just validate that was indeed what we needed it to be. Left. Hey. There we go. Heck, yeah. Which means we're onto we're we're now onto a winner, basically. It's this is the starting logic for our for the no. It isn't. Oh, it's it's somewhere in between that we haven't quite got the standard because this is the first one after the global and this is the last one. So that's fine. We need to create one more here. Yep. And, what's next? Avoid bodies. Avoid others. Yep. Run script. Paste that in. And we don't need is move safe anymore. We have the data dot globals. And where are the where are where is the logic? It's here. And just because I think I think it's gonna be easier, I'm just gonna great. Great. And I don't think we need board width or board height anymore. We just need my head. We have snakes. So I believe we had bored. I think we got bored out. Yes. I'm not sure we did. Let's look at the globals again. The actual board itself needs to come with 2. So, let's call it snakes. Let's just add snakes here. So data dot trigger dot body dot board dot snakes. Then let's create this last one again, as avoid bodies and that should have saved, but it's fine. Okay. So we have my head and we have snakes. I think I gen oh, yeah. I genuinely think nothing needs touching here. No, I think it's good. And I think we've the, the code works and we're even past. Yeah. I don't see any point of weakness in this at the moment. So we hit say, oh no, we don't, we just, Famous last words. We do that. Then we do that. So I'm gonna just Very good. We'll hit save. Oh, what's going on? Value fulfilled resolving collection, direct us operations. Value fulfilled resolve. In fact, it has to be I, don't know what that is. Record not unique. For field, resolve and click direct operations. Maybe if we turn off that logging now. Just turn off the logging because I think that's Yeah. But that isn't logging. That's a database save error, but I don't know why. Let let's just forget this was here for a moment like it was before, and it was fine. Okay. That does save. So if I take this instead and send it to avoid bodies and hit save, it's fine. And then if I go here to return move, fine. Do you wanna know why I'm just gonna walk away slowly? That's the right that's the right answer. If the thing starts working So we don't wanna create a rematch now because we wanna create a game with another snake. Yeah. Yeah. Star hungry in there. Alright. Let's see how this works. Okay. We got it. He Hang on. We've not had to Yes. Hey. There we go. Yes. Nice. Get in. Okay. Done. Nice. So the final thing here was this, if there's no safe moves, go down. I don't think we need that. I genuinely it's like a Oh, yeah. Backup. It'll always Yeah. Well, because if there are no safe moves, you're toast anyway. Exactly. So it's really just, like, if you want to have, it I don't know. It's good programming practice for you to build it in, but, like, I think here, like, we can worry about that down the road. Do you wanna know what? Given that this was basically the scope for this episode, I'm gonna add it. Why don't we add 1 one new block in? Right. Because I wanna do avoid avoid head to head. K. Yep. That seems easy. Yeah. Because we're just basically doing look ahead here. Right? We're looking at, like, one extra block ahead. So so yeah. I mean, so this is the if you didn't catch it on the last stream or on the last, the last episode, we so head to heads, you can do and you can win, but it's all about size and rules, but, like, these head to heads here. But with the logic we currently have, we're only ever looking at what's directly next to us. So ensuring that, like, if we have another snake beside us or directly in front of us, we'll never go that direction. But if there's a snake that's 2 away in the same direction, so basically, we're repeating the logic that's in here and just or we could technically revise this logic. I think it's similar because it's just like Yeah. Yeah. It will be similar, but I don't I do not think it's the same. So it's not the same. If we ditch this for a moment, we'll leave it as we could see it. So we care about the snakes, every snake, and we're gonna get rid of us. Now there is a a fancy way of doing this, which is, js return array without first item. To remove the first element in the in the array, you can use shift. Shift removes the first element of the array and returns it. So I think we can do this just to get rid of us first. Yeah. And then we have to find our because then we would have no valid no valid way to go. So So now we care about every other snake. So I'm gonna say head, it's gonna be snake dot body. Does snake have a head too? Does every snake have a head? Everything has a head. Snake dot head. So okay. Cool. And what we wanna do is look one space left, right, up, or down and remove if that's gonna be the direction we go in. Right? But we I'm just trying to have a think here because I think these are actually much more complex if statements. They are. It's basically if I remember having done this before, it's basically all of those if statements that we had there repeated 4 times, if that makes sense. Which we absolutely will not do. We will we will do, like, a nested loop. But I'm just trying to think through that. I'm just trying to think through, like, what is the logic here? Well, the first thing we wanna do is check every feasible space we can move into. That's the way we're gonna do it. So feasible directions. I remember encoding Valdez to do stuff like that, and then we had to remember what it meant. Possible next steps. And we actually don't wanna do this inside of this loop of every snake. We wanna do this just for us. So possible next steps are, is I don't know that because that's gonna be possible next steps is an array is an array. Cool. And we're gonna push into this array everything up, down, left, or right of us. Yeah. Then we're gonna remove our body piece, our neck. We're gonna remove our neck. Yeah. Okay. Cause we don't want our neck in there. So that, that will release 3 directions. And then for every snake, we're gonna repeat that logic. We're basically gonna say if any of these match, you remove it. So when we store them in here, we also have to store whether it's up, down, left, or right. K. Alright. Let's see. You have a plan, and I'm very curious to see how this will be implemented. First thing we wanna do basically is say up, down, left, or right. What is the coordinate? So what are the coordinates of that? So we're gonna say, in fact, I I think this might be easier. So we're gonna go possible next steps dot push. We'll we'll start with left. No. Left is going to be x is, come on, Kevin, you can do this. X is going to be our current X position. So this is going to be I don't just want my head. I want I want you. So I'm gonna save this. We'll come back here. I don't think we have you. No. No. We have my hand, which is great. Let's just throw you in there. Then we'll come to avoid head to head and what just while I'm here. And return move. Right. Return head to head. So now we have access to you in here. So we want Datastock Global's my head, you, and my head. Cool. So that's gonna be you dot, why why did I do you? We just care about my head. What are you doing, Kevin? Yo. Because you wanted to remove the neck. You were this Yeah. But not oh, yes. I suppose that will be that will become important, but the neck comes later. Yes. So we just wanna say my head dot x minus 1 and y equals my head dot y y. That's left. Bright. So that's the the same. Up. And so that's gonna be x remains the same, but y this time, r is minus 1 and down, plus 1. No. It starts the other way around. Down because bottom left is 0. Up. So that's pushing in push in all I mean, it's it's pretty obvious actually by the, by by the name of what's going on. Then we wanna remove our neck from that. Then there'll only be 3 items in the array. So we'll go possible next steps. How do I do this? Equals poss that becomes a let. Possible next step, stop filter. Possible next steps. So steps, where the step is not equal to you. You dot what is the it's a bit disruptive, but I'm gonna just what is the what's it called? U.body.body. I love that my my many, many if statements, my heuristic was not okay, but there's this will be so much cleaner though when it's done. I will agree with you there. Thank you. So this is remove net. I think that's what that does. So every item where that because all the body parts are is this. It's an x and y. Right? Yep. Yep. I I'm a little bit I think I think object, object comparison is a little bit hunky. So what I'm gonna do just to be on the safe side here is say, where s dot where s dot x is not equal to x and where it's not equal to y. So if both those things, no or no and hang on. This is this is important. I'm gonna do it the other way around. So where they are equal to x and they are equal to y, that means, hey, this item matches or wrap the whole thing in another bracket and we will negate it. Okay. That should be removing. Does that make sense? I'm so glad we have a video because that's super unreadable, but I get the logic makes sense. No. No. No. Not not unreadable. Unreadable. I mean, like, I feel like if somebody looked at this, they would be like, what is happening in this line? So, I mean, I mean, we're removing the yeah. Yeah. Right. Okay. Sure. Yeah. So Who needs to read this code except for us? Exactly. No. This makes sense. Okay. Yeah. I think that works. So we removed the neck. So that's removing that's saying all of our possible neck positions. Now we wanna basically figure out all of the every other snake's possible next steps. And if there's any matching item oh, there's a there's a silly, silly little thing going on here, which is we're gonna push in. I'm gonna just call the variable c to mean coordinates and that and this is where it all starts falling apart in terms of, like, clarity. And I am gonna I am I I've decided I am gonna be more explicit here. No. I think it was I mean, I think it's I think it's very clean code before, but, yeah, I think that this makes a little bit more sense. Yeah. So we have the cohorts. And the reason this matters, It's because we don't just want to store the coordinates. We also wanna store the actual reference to which direction it is. So as well as the quotes, we are gonna say direction left, and we can get rid of that. We can get rid of that. So direction left, direction right, direction down and direction up. Right? So now we just have to change this bit here because it's not s dot x anymore. It's s dot quartz x and that's dot quartz y. But it will when it removes it, will physically the directions will no longer and then at the end, we're gonna just return the those directions. So I think that that's an important thing. So we can actually ultimately at the end change is move safe and know what values we are we are removing. Yeah. Because before, there's no how do we know which one's which other than the order we initially push them in? Mhmm. Not a nice. The next thing we want to do is go around every snake, and we wanna basically do this same game here where we go left, right, up, down, push it into an array, cross reference them. If any of them are the same, we know the direction in which they match. Oh, that That's funny. Off reactions gone. Are you familiar with these? I am, but you're in Arc. Right? Is that where it actually is? Or is it within Riverside? No. It's it's macOS. Oh, it's in Mac. Really? No way. Yeah. But I don't Well, I'm not I'm on Windows. Windows is like, no. We don't we don't think these are things that we need. That is so funny. So now we now we wanna do the same thing for this snake. And this is this is the important thing is that for every snake, we are literally gonna do this exact same logic, except we're not gonna remove the neck. We're just gonna say, oh, are we gonna remove the neck? No. No. We'll just remove the head. No. No. No. No. Because if we you know, because if we're colliding into the neck, we're colliding like that would have been handed by the dome. Yep. Yeah. Exactly. Yeah. So it's just the head. Exactly. Exactly. So we just care about this logic. So possible next steps. And I am just gonna just shrink this down slightly because it'll be contextually clear what's going on here. So it's no longer my head. It's no longer my head. It's head. Head. Okay. And so that's so that's that. We're doing that for every single snake. And now this is the important thing we are going to compare. Yeah. We are going to compare. Let's just have a think about this. We actually don't care about the directions this time. The directions are irrelevant. I think, I think we'll leave them in for the sake of having the same data structure. So we know exactly what we're doing, but this bit's irrelevant. We just yeah. It's it's irrelevant. So this n is going to go before it's just one big array of every snake. We don't need to have it on a per per snake basis. So this is us populating and with every viable direction that could ever be in. Actually, I've had a change of heart. We are we are gonna Sure. That down. And then we're just gonna be comparing our end versus our possible next steps. Hence, look, we're just gonna be You absolutely you absolutely got it. I like it. Okay. This is cleaner than I thought. I was definitely you were, I get where your head is at with removing some of this stuff now. I love it. It took it took you getting to the end of this for me to be like, yeah. I I this makes sense. Good. Good. Good. Thank you very much. Thank you, Ro. Smart code. Very much. So now what we're gonna do is compare every single value in n. So we're so what we're gonna do now is and yeah. Yeah. Perfect. So we'll we'll loop now. So it's a for let n of, can't do n. Yeah. If you just change n to to next moves or something like that or or yeah. Yeah. There you go. Sure. Oh, I see. So for so next, I'd rather that's fine. For Yep. So I can use the shorter version inside the list. I'm about to use it, I think, a reasonable amount. So for every single value in there, is it one of those? It's all we care about. Let's have a think let's have a quick think about how we're gonna achieve that. Okay. So for every single one, we're gonna say, does next possible Does value in one array appear in another? Hey. There we go. Nice little vanilla JavaScript there. Absolutely. Include some and include. Some text element against the test function that return. Yeah. It's great. Cool. I don't think this is, oh, but it's that's no. It's that same thing again with objects. So I think the object nature of it, like, object comparisons are are specific. Yeah. It's a find and a size I think this is actually flipped. What are the responses? Did they say it actually works? I think so. That's a bit of a it's a bit of a big big example. Got it. Got it. Lovely. That's the answer. So I'm gonna just I'm gonna just pop it here and comment it out so we just have a little reference at the point where we're writing it. So this is an array. Let's consider this as our possible next steps. Yeah. So we wanna say, for, matches r.sum. It's not called r, is it? It's called next possible steps. Possible next steps. Possible next steps. Possible. I'll fix that in a moment. So sorry. The sum method of an array test whether at least one element in the array passes the test implemented. Okay. So next possible did I make a typo? Yep. Possible next steps, not next possible steps. Just Thank you. It's what I'm here for Possible next steps. And it immediately changes color to be like, yeah, you can spell now. Thanks. Possible next steps dot sum item. We'll do that again, and it's item dotcoords dotcoords. Dotxmatchn dotx. Dotxanditem.coords.y. For n of next. Is this is this how is this really I I think I I think there's some redundancy in here, actually. Yeah. What? Yeah. This is all we're this is this is trying to, like but we're already in a loop. We're already in a loop. So all we wanna know was, I've got it. Matching collision. Collision. Is that how you spell it? Collision Yep. Equals possible I got it. I got it. I got it. I got it. Next steps dot find and because we just wanna find does does one match. Right? And what we wanna know is step in there, is there a value where the cohorts dot x are the same as n.xandstep.cowards.y equals m.y. If this returns a value It works. Then that yeah. Then that collision it's gonna be a little bit of a long winded one to test because also we're gonna have to go we're gonna have to go back through the logs. This one's gonna actually be really painful to test because in theory, if it works, you know, but, so what this will return is a possible next step. And so what we will then do is if this this will have the value of possible next step in it. Mhmm. If it matches, so what we will then do is say if collision, because it will be 4 to undefined, like, some faulty value if if there's nothing returned in there, then possible just get myself a few characters because I think I'm gonna go quite far to the right here. Possible next steps equals possible next steps dot filter, and we wanna remove this item. And it's this. It's this logic right here that we already wrote, but slightly different. So we wanna say we're s.coawards.x, where collision by collision.cohorts.x. Oops. This one here, where it is the same as collision collision. Am I spelling it right? I I swear I swear I swear I That's right. No. You're good. You're good. I'm literally just like, you know, when words start to look wrong. Yep. No. But it's catching it. It's catching it, turning it purple if you're right. And this is the same as collision dotcoorg.y. Okay. I think I think this is it. Then oh, no. There's one more step. So now possible net, possible next steps is a shortened array which has just items which are viable. Yep. Just items which are viable. Let's just have a think because we we don't wanna What is that actually gonna look like, though? Do we know right now? It it will be an array of objects that look like this. Is it just gonna be a same case? Yeah. Yeah. But just a shortened a shortened list. Yeah. Now what we actually wanna do now I think about it is we kind of want the others, which I've just realized we want we want the other items. We want the ones that are being removed. So Oh, to So removed from his move. Yes. Yes. Yes. Yes. So let make false. This is we're starting to get a little hunky now in terms of, like, code quality, but, like, it's fine. So if collision we don't wanna screw around with possible next steps. We don't care about that. All we're gonna do is add collision dot direction to make force, and we're not even gonna do that. We are gonna just straight up say is move safe dot left, and the value of left will be in collision dot the equals false. And that's the direct we are making the the value that we came in with. Yeah. We're setting it to false because we're gonna collide. And I'm not sure if this is valid, syntax. I will find out. We will find out. I might I might just save ourselves some effort here. Commission dot the okay. Alright. Sorry for bringing oh, don't don't play this game. I, I'm sorry. I felt like that wasn't an overly, like, collaborative exercise, and I'm sorry. That was it was wonderfully collab I don't know what you're talking about. See, my favorite collaborative experiences are the ones where I can nod along and say yes, And I can I can help pick out when you've misspelled something? That's, I feel like I've I've contributed in my own YouTube. I don't know don't know what that little error is at times. Anyway It's weird. Let's and this one is gonna be a little bit of a hard one to to test. So we're gonna just have to watch watch this carefully. The snake's going up the whole time. Okay. So there's a bug somewhere. There's a bug somewhere, but we will be able to See debug it. Oh, it all died here. Un un there's a problem in the setup of the globals. There's a comma. Change in here. Comma. Comma. Those commas. It's always the commas. Grammar's out to you. I'm loving I'm loving now that we don't have to touch the code editor at all. Yeah. Agreed. Okay. See. So where K. Avoid head to head. Fine. Unexpected token line 8. Great. Okay. 8. There is an extra bracket here and an extra bracket. No. No. No. The one at the end is the one at the end is correct. There's an extra bracket here too. Great. Let's see if that does it. Yeah. I I literally cannot believe we're okay. I was gonna say we're getting it to work. We're getting things are happening. We're debugging, and that's the important thing. Alright. Snakes is not defined. Okay. Not a problem. What's the I mean, this is good. Oh, snakes. Fine. It's all really small, like, little errors, quite well defined, quite quick to fix, allegedly. Alright. Alright. Alright. I need everyone to know at the time of recording, it is 11:30 at night. Snakes dot shift is not a function or its return valuable value is not iterable. Right. That's that's fair. And that is here, snake dot shift. So what we're gonna do is just it really doesn't matter. Like, snake star shift, and I I'm assuming it that's how it like, it needs to happen on its own line first. Kevin, if this is what you programming at 11:30 at night does, I don't wanna see you programming when you're fully rested and awake. Well, here's here's the joke. I'm I'm never fully rested. I mean, children. Children. Yeah. Fair enough. Well, it's going it's going up, but I also didn't set that up correctly. But let's let's keep having a look at the logs. S is not iterable. Okay. I think I've misunderstood how shift works. 1st element. Oh, it returns the first element. I don't want that. I I want to return them. Right. Right. Right. It returns just the element. It pops out of the top, but I want everything else. Oh, everything but. Yeah. So what's the option of the shift? It is to do the head, avoid head to head. So it is to do the work. So it's to do the shift snake stop shift, and then I think it might actually be to put snakes back in there. Yep. But I'm not sure if snakes is re I'm not sure if that will work. I think it will. Okay. I think it's not doing that anymore. This is obviously not the test, the, we need to But we're not we're we've got no actual errors in the code that we've well, that we I use we very liberally. The code that has been produced in that final card is working and it's definitely a little bit slower now. So I think that we're definitely getting a, there's a lot more compute going into this, which totally makes sense. Yes. Okay. We're we're still going. Woah. That was a okay. Okay. It did end up giving up. Why? Oh, because there was no viable choice. Yep. So that's Which is yeah. Wow. Hey. We wanna move fast oh, sorry. What do you mean? I was gonna say if we wanna move faster for the head to heads, put multiple snakes in more than just one. Okay. I I think we I think we got a snake. I think we did too. I think it's working. I'll stick 2 hungry bots in that There we go. Cough. That should do it. Should have hit start game first, so it did its job. Okay. Head to heads are obviously gonna be hard to to start. Yeah. But we can kind of see we can kind of see here when it ends it ended. Okay. So now we can watch the whole thing. Yep. So he's not what's he turn why is he turning? Was there a safe move there? There was a safe move. How do I go back? Pause back. No. There wasn't. Nope. There wasn't. It got itself into a corner. Fine. I'm confident I mean, I'm not confident enough. I do that were there any head to head opportunities? And I didn't see one, did you? Nope. No. But I think I think we're not getting any errors at that final stage, and I think that's a good sign that the logic in there makes sense. You yeah. Yeah. But it might not be doing what we want. So what other few do you think 11 by 11 is still a good size, or maybe I should make it small? Make it small, and then we can see faster. And, yeah, give that a try. Yeah. Yeah. There we go. Yeah. Run that and see what happens. Oh, it's it it went on for a reasonable length of time. K. So there looks like No. Our snake didn't didn't need to didn't have a head to head moment. Well, I mean, it avoided a potential head to head moment there, but But it's I'm happy I'm happy in no. Yeah. Because we'll never know, will we? Yeah. We'll never know. We'll know if we play enough games over a long enough period of time, but I think the code is there. And we would know it doesn't work if there is a head to head, but they don't happen often enough. Hey. We leveled up our snake. That's solid. So not only move it to a different platform where it's running, same logic, and then adding that extra step and super visual. Super visual. I'm very I'm very, very satisfied. I'm very satisfied by it that working. Awesome. I think that brings us to time. I mean, we we have to be at time at this time. You gotta go to sleep. I've got yeah. My kids will be up in 5 minutes. So, awesome. Thank you for for for for doing this again. Yeah. Thanks for joining us, everybody. I feel like I'm glad that you were driving this time because had I been driving, this would have not gone as smoothly in any way, shape, or form. So I feel like this was gonna be be silly. But I I am excited to eventually drive when we, again, in episode 3 or 4 when we start to implement some other technology. So do we know what we're doing on our next episode yet, or is it all In our next episode, we're gonna stay in Directus, and we're not gonna be leveling up our snake. But given that Directus is backed by a database, we're gonna start actually storing data about games and then seeing what we might be able to learn with Directus Insights. I love it. I love it. That's gonna be an awesome next episode. So, we've got a snake. You've seen how it works. That's all of the steps for the starter snake that are worked out in there. It's actually just a functional JavaScript snake. You could pull all of that logic out. Well, most of that logic out and just put it into a JavaScript Express server and just go to town. But, but we've got it working in here. And so now let's do a little bit more. Let's see how powerful you can make Directus with Battlesync data. I'm very excited. Yeah. This is awesome. So until next time, this has been ready, set, battles, stake. We're getting better. We're getting better. Some some would say. Some would say. Till next time, folks. Bye, buddy.",[3721,3722],"79065ff8-a276-4134-a90c-04775dccb561","82d75a59-34de-4a81-8d98-59f44f95b638",[],{"year":978,"number":993,"id":3341,"episodes":3725,"show":3726},[3343,3344,3345,3317],{"title":3347,"slug":3348},{"id":3370,"slug":3728,"vimeo_id":3729,"description":3730,"tile":3731,"length":13,"resources":12,"people":12,"episode_number":129,"published":3732,"title":3733,"video_transcript_html":3734,"video_transcript_text":3735,"content":12,"seo":12,"status":19,"episode_people":3736,"recommendations":3737,"season":3738},"no-place-like-home","893667828","There's no place like home, there's no place like home, there's no place like home.","fd7f0d8d-6e00-42e2-b726-a2494043d0a5","2024-04-03","No Place Like Home","\u003Cp>Speaker 0: And now Dev Thoughts by Jack Rabbit. They say there's no place like 127.0.0.1. But sometimes I wonder if there's a hidden paradise at 127.0.0.2 filled with bug free code and endless coffee.\u003C\u002Fp>","And now Dev Thoughts by Jack Rabbit. They say there's no place like 127.0.0.1. But sometimes I wonder if there's a hidden paradise at 127.0.0.2 filled with bug free code and endless coffee.",[],[],{"year":978,"number":263,"id":3362,"episodes":3739,"show":3740},[3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3350,3378],{"title":3380,"slug":3381},{"id":3371,"slug":3024,"vimeo_id":3742,"description":3743,"tile":3744,"length":13,"resources":12,"people":12,"episode_number":11,"published":3732,"title":3745,"video_transcript_html":3746,"video_transcript_text":3747,"content":12,"seo":12,"status":19,"episode_people":3748,"recommendations":3749,"season":3750},"893667945","The story of AI more interested in Beethoven than binary.","ed68aa17-d883-4d20-8026-ba34e9f93bec","AI","\u003Cp>Speaker 0: And now, Dev Thoughts by Jack Rabbit. I asked an AI to write a program for me. It told me it couldn't because it was learning to play the piano. I didn't know whether to be proud or to unplug it.\u003C\u002Fp>","And now, Dev Thoughts by Jack Rabbit. I asked an AI to write a program for me. It told me it couldn't because it was learning to play the piano. I didn't know whether to be proud or to unplug it.",[],[],{"year":978,"number":263,"id":3362,"episodes":3751,"show":3752},[3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3350,3378],{"title":3380,"slug":3381},{"id":2749,"slug":3754,"vimeo_id":3755,"description":3756,"tile":3757,"length":195,"resources":12,"people":3758,"episode_number":177,"published":3761,"title":3762,"video_transcript_html":3763,"video_transcript_text":3764,"content":12,"seo":12,"status":19,"episode_people":3765,"recommendations":3767,"season":3768},"american-express-theme","928845573","Home is where your heart is and – just like your wallet – you should never leave home without it. Join Bry as he builds a happy little theme inspired by American Express.","22ca1cf9-4a05-446a-a8ef-eee78afa4f22",[3759],{"name":3760,"url":3591},"Bry Ross","2024-04-01","American Express (Don’t Leave Home Without It)","\u003Cp>Bry: Hi. Welcome back to The Joy of Theming. I'm your host, Bry Ross. It's a beautiful day in the neighborhood and so excited to have you right here right now to experience the joy of theming. Alright.\u003C\u002Fp>\u003Cp>So today, we've got a wonderful, wonderful theme to paint. We are going to be painting American Express. What a great theme, and what a great company. American Express is everywhere you want to be. What's that?\u003C\u002Fp>\u003Cp>Oh, that's that's not it. American Express. Don't live life without Alright, so we're going to log into our Directus instance and get started on this theme. So we'll start entering in our project name. Ah, yes.\u003C\u002Fp>\u003Cp>Okay. Project description or descriptor. Now let's dive into our theme. So for this, I'm gonna pull out my trusty palette knife and we're just gonna spread on some logo. I love that.\u003C\u002Fp>\u003Cp>For our foreground, I'm just gonna scroll down to the bottom and we want to take that larger American Express logo and just paint that in there. That's great. So for the favicon, that's that's tiny. So I'm just gonna pull out my half inch brush here and we're gonna paint that in there. This is looking really great.\u003C\u002Fp>\u003Cp>So we've got a nice nice calm theme. We've got that background around that MX logo fixed. Now let's add our project color to those buttons in the foreground. So we're just gonna slide that that project color right down over those those module buttons. That looks great.\u003C\u002Fp>\u003Cp>Now, one of the other colors that I see here, I really like this blue. I'm calling it midnight blue, indigo blue, It's a really deep color. It's so soulful. We're going to add that to the icons for our list. So we'll just ping in that hex code and, you know, I'm really surprised.\u003C\u002Fp>\u003Cp>This came out okay, but anytime you switch colors like that, you also need to make sure that you dust those demons out of your keyboard first so those hex codes don't get stuck in there anymore. Using that number pad, make sure you really get in there as well. Now, let's go in and see if we can't use our palette knife to to find that CSS. We're really looking for those font face rules from the American Express website. And normally, I might host those fonts myself, but here at The Joy of Theming, we like to create masterpieces in no time.\u003C\u002Fp>\u003Cp>So we're just gonna steal or copy that CSS. We've got our font face rules that we're just spreading there and I'm gonna copy that font family name, Benton Sands, and we're gonna go back up to our fonts in the display font. We're going to use Benton Sands and save and now we've got that nice title font Benton Sands. Maybe we want to dial back that font weight just a little bit. Nice.\u003C\u002Fp>\u003Cp>Nice. Looks great. The only thing that don't like though, it is a dark shade of it's black. Let's use that midnight blue color and honestly Midnight Blue sounds like a Bob Seger song. Bob Seger is underrated, 100%.\u003C\u002Fp>\u003Cp>So we'll go back in. That title font color is actually it's actually down. We're just gonna move down. You can see we've got the project foreground. We're just gonna move that down a little.\u003C\u002Fp>\u003Cp>There we go. You gotta know how to work the canvas. So under the header we're gonna go into Title, just spray that little midnight blue onto the title font. This is looking really nice, Very subtle. It really matches this American Express website.\u003C\u002Fp>\u003Cp>I like how calm that is. Let's back out and actually look at our login page. So while I originally loved that image on the Amex website, for the background, it's just not it's not getting the job done for me. So let's log back in and see if we can swap out to a better, better background image. We're gonna go back to the Amex website and just see if we can find a better image.\u003C\u002Fp>\u003Cp>Wow. This guy looks amazing. I really love his hair. Could be waiting for the love of his life or just a check from the bank, maybe a package. I don't know.\u003C\u002Fp>\u003Cp>We're gonna use that as the public background. And I'm just gonna incognito window and pop open. Now we we've got a better public background image. Maybe we want to adjust the focus, the focal point for this particular image. That's one of the nice things about the asset library in Directus.\u003C\u002Fp>\u003Cp>This is the conduit for your theming. Obviously, a good theme isn't complete without nice images. And whenever those images get cropped we want to make sure that faces and important elements are still included on the screen. So you can use the Focal Point tool to do that. Alright, great, so this theme is looking great, I think we're done here.\u003C\u002Fp>\u003Cp>Amex, You can't live life without it or don't live life without it. Tell you what, I have several Amex cards. Love stacking the points, always use them to take my family to the magical Disney World. And I hope you've had a magical time here at the Joy of Theming. I hope you come back for the next episode where we will dive even deeper into the beauty and majestic qualities of theming with Directus.\u003C\u002Fp>\u003Cp>I'll see you next time.\u003C\u002Fp>","Hi. Welcome back to The Joy of Theming. I'm your host, Bry Ross. It's a beautiful day in the neighborhood and so excited to have you right here right now to experience the joy of theming. Alright. So today, we've got a wonderful, wonderful theme to paint. We are going to be painting American Express. What a great theme, and what a great company. American Express is everywhere you want to be. What's that? Oh, that's that's not it. American Express. Don't live life without Alright, so we're going to log into our Directus instance and get started on this theme. So we'll start entering in our project name. Ah, yes. Okay. Project description or descriptor. Now let's dive into our theme. So for this, I'm gonna pull out my trusty palette knife and we're just gonna spread on some logo. I love that. For our foreground, I'm just gonna scroll down to the bottom and we want to take that larger American Express logo and just paint that in there. That's great. So for the favicon, that's that's tiny. So I'm just gonna pull out my half inch brush here and we're gonna paint that in there. This is looking really great. So we've got a nice nice calm theme. We've got that background around that MX logo fixed. Now let's add our project color to those buttons in the foreground. So we're just gonna slide that that project color right down over those those module buttons. That looks great. Now, one of the other colors that I see here, I really like this blue. I'm calling it midnight blue, indigo blue, It's a really deep color. It's so soulful. We're going to add that to the icons for our list. So we'll just ping in that hex code and, you know, I'm really surprised. This came out okay, but anytime you switch colors like that, you also need to make sure that you dust those demons out of your keyboard first so those hex codes don't get stuck in there anymore. Using that number pad, make sure you really get in there as well. Now, let's go in and see if we can't use our palette knife to to find that CSS. We're really looking for those font face rules from the American Express website. And normally, I might host those fonts myself, but here at The Joy of Theming, we like to create masterpieces in no time. So we're just gonna steal or copy that CSS. We've got our font face rules that we're just spreading there and I'm gonna copy that font family name, Benton Sands, and we're gonna go back up to our fonts in the display font. We're going to use Benton Sands and save and now we've got that nice title font Benton Sands. Maybe we want to dial back that font weight just a little bit. Nice. Nice. Looks great. The only thing that don't like though, it is a dark shade of it's black. Let's use that midnight blue color and honestly Midnight Blue sounds like a Bob Seger song. Bob Seger is underrated, 100%. So we'll go back in. That title font color is actually it's actually down. We're just gonna move down. You can see we've got the project foreground. We're just gonna move that down a little. There we go. You gotta know how to work the canvas. So under the header we're gonna go into Title, just spray that little midnight blue onto the title font. This is looking really nice, Very subtle. It really matches this American Express website. I like how calm that is. Let's back out and actually look at our login page. So while I originally loved that image on the Amex website, for the background, it's just not it's not getting the job done for me. So let's log back in and see if we can swap out to a better, better background image. We're gonna go back to the Amex website and just see if we can find a better image. Wow. This guy looks amazing. I really love his hair. Could be waiting for the love of his life or just a check from the bank, maybe a package. I don't know. We're gonna use that as the public background. And I'm just gonna incognito window and pop open. Now we we've got a better public background image. Maybe we want to adjust the focus, the focal point for this particular image. That's one of the nice things about the asset library in Directus. This is the conduit for your theming. Obviously, a good theme isn't complete without nice images. And whenever those images get cropped we want to make sure that faces and important elements are still included on the screen. So you can use the Focal Point tool to do that. Alright, great, so this theme is looking great, I think we're done here. Amex, You can't live life without it or don't live life without it. Tell you what, I have several Amex cards. Love stacking the points, always use them to take my family to the magical Disney World. And I hope you've had a magical time here at the Joy of Theming. I hope you come back for the next episode where we will dive even deeper into the beauty and majestic qualities of theming with Directus. I'll see you next time.",[3766],"88abad2a-a58f-47e4-aeb8-8f5814116d13",[],{"year":978,"number":2745,"id":2746,"episodes":3769,"show":3770},[2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2731],{"title":2760,"slug":2761},{"id":3605,"slug":3772,"vimeo_id":3773,"description":3774,"tile":3775,"length":889,"resources":12,"people":3776,"episode_number":264,"published":3778,"title":3779,"video_transcript_html":3780,"video_transcript_text":3781,"content":12,"seo":12,"status":19,"episode_people":3782,"recommendations":3784,"season":3785},"multitenancy","903059602","Learn how to achieve multitenancy inside of a Directus project using Roles and Relational Fields.","e5e721aa-c6c9-4196-8d5c-4220388a59eb",[3777],{"name":3590,"url":3591},"2024-03-29","Multitenancy","\u003Cp>Speaker 0: Hi. Welcome back to the next episode of Short Hops. I'm your host Brian Gillespie. Today we are talking about how to achieve multi tenancy inside Directus. So tenancy is just using the same resources to serve multiple groups of users.\u003C\u002Fp>\u003Cp>I think the term came from apartment buildings, maybe. Got multiple tenants that share have their own apartments, but they share the same resources. They share the same building. Maybe that's where it came from. But anyway, it is a very common convention in SaaS that we have multiple different accounts or teams or whatever you call your tenant collection using the same application, but each team needs to only see their individual resources or data in this case.\u003C\u002Fp>\u003Cp>Achieving it inside Directus is pretty simple. There are 4 steps we're going to cover. Let's dive right in. Alright. So the first thing we're gonna do is create a tenant collection.\u003C\u002Fp>\u003Cp>Now what you name this is totally up to you. You could call it teams, you could call it accounts, which is one of my personal preferences. But, you know, if you're coming from something like Laravel, I think out of the box, they have something called teams. So we can add a couple of additional fields if we need to. These system fields are really nice, just a quick little shortcut, but I'll go ahead and create a team collection here and we'll just give it a team name.\u003C\u002Fp>\u003Cp>Right? So let's go in and create a team, we'll call it test team A, we'll save this. Let's go ahead and create another team, test team B. So now we have 2 teams, we've got our team tenant collection. Let's, let's mark this off on our list here.\u003C\u002Fp>\u003Cp>Boom. Step 1, done. Alright. So the next step is to create a relationship between this tenant collection and our Directus users collection. So Directus users gets created whenever you bootstrap your Directus instance.\u003C\u002Fp>\u003Cp>So if I'm looking at my data model, I'll find that under the Directus users here. And what kind of relationship you set up depends on how you want your tenancy model to work. So if users could belong to multiple teams, you're gonna use a many to many relationship. But if a user could belong to just one team, you could use a many to 1 or and the reverse one to many. So in this case, let's say we want users to belong to multiple teams or they could potentially belong to multiple teams.\u003C\u002Fp>\u003Cp>Now in that case, we are going to create a many to many relationship. So I'll just search for that here. And inside Teams, let's call our field users and we are going to look for the Directus users collection. Right? So I'm inside my team's data model, go to the mini to mini relationship.\u003C\u002Fp>\u003Cp>We'll create a new field called users, and the related collection is gonna be directus_users. Now I can show a link to that item, but if I open up the advanced section, I can see this junction collection that Directus is gonna create for me. I was calling this teams_directususers, and then we have the team's ID and the directus_usersid. Underscoreid. Too many.\u003C\u002Fp>\u003Cp>I'm also gonna create the corresponding field on the Directus users collection as well. Great. So I'll just go ahead and save this and with that relationship out of the way, we can mark off step 2 of the process. Now step 3 is gonna be creating our actual data collections. And for each one of those collections, we need to create a many to one relationship back to our tenant collection.\u003C\u002Fp>\u003Cp>So let's just keep this super simple and create a new collection called tasks. Very simple. I could even go in and, you know, add these system fields. Maybe we want to know who created the task, what date it was updated, and we'll just give this task a title. Right?\u003C\u002Fp>\u003Cp>Great. The next thing we're gonna do here is add a mini to 1 relationship back to that tenant collection. So we're just gonna call this team and the related collection is gonna be called teams. We'll hit save and now we have that relationship that will allow us to filter out those permissions when we're making those API calls. Alright.\u003C\u002Fp>\u003Cp>Great. So now if we just go in and let's create a couple of test task, A will assign that to test team a, and we'll create test task b. Go ahead and assign that to team b. Alright. Great.\u003C\u002Fp>\u003Cp>So now we've got the collection, and for each one of our data collections, like, let's say I had projects and then tasks, I would add that same relationship, that many to one relationship back to my tenant collection. So with that out of the way, let's go ahead and mark this off. And then the last piece of the puzzle here is to set up our permissions inside Directus to restrict access based on the user's team. What what teams are the users included on? We wanna restrict access based on that.\u003C\u002Fp>\u003Cp>So we're gonna go to our settings, go to access control, and let's create a new user or a new role, I should say. Let's call it user, and I'll enable app access here. You don't necessarily have to do that, You know, this is just to showcase the functionality. If you want them using the Directus app, you can. If it is API only, the API still respect these permission settings.\u003C\u002Fp>\u003Cp>Alright. So what I'm gonna do here, I'm just gonna go ahead and enable all these permissions by default, and then I'm gonna go in and start restricting this. So our share functionality, I don't necessarily need. Maybe I don't even want these users to be able to delete tasks or things like that. We definitely want them to be able to create new tasks.\u003C\u002Fp>\u003Cp>Maybe they can create new teams, But it when it comes to seeing tasks and editing task and seeing teams, we only want them to see the things that they are a part of. So how do we do that? We could go into our custom permissions in that case. So we'll just click on that and use custom, and then we will set up a filter rule. So here we are filtering the items that this particular user or this role which will apply to a user can have access to.\u003C\u002Fp>\u003Cp>So in this case, we've got that team field and because we used a many to 1 relationship or a many to many relationship, we have to use the is one of because the actual users, the direct us the team and the direct us users, it could be an array of teams that they are a part of. Directus has these dynamic variables we can use as well. So dollar sign current underscore user in all caps will allow us to access the current logged in user. We'll go for the dot teams because that was the field that we specified on the user collection, and then we're looking through our junction table. So that is underscore teams teams underscore ID, and then we have ID.\u003C\u002Fp>\u003Cp>So with this, and if I just copy this rule, we can apply that to the edit access as well. So I can go in and edit raw value. I could just paste this here, save it. Okay, great. So now if I were to create a new user, let's call them test user a, and just do test at example.com.\u003C\u002Fp>\u003Cp>Give them a nice secure password, and I go into the role settings. We'll select the user role, save that, open up a incognito window so we get a fresh set of cookies, and let's just log in to this user and see what's actually available. Alright so we logged in we cannot see any particular tasks here. Now if I refresh the task I could see that, but we have not associated that user with an actual team yet. So if I scroll down to the bottom let's add that user to team A and we'll hit Save.\u003C\u002Fp>\u003Cp>And now if I refresh this window we could see that that user has access to test task a, but they cannot see test task b, which is a part of the second team, which is test team b. Very simple. That is how you set up tenancy inside Directus. I hope this tip was helpful. Bring on some more questions.\u003C\u002Fp>\u003Cp>I want to cover more of those for you.\u003C\u002Fp>","Hi. Welcome back to the next episode of Short Hops. I'm your host Brian Gillespie. Today we are talking about how to achieve multi tenancy inside Directus. So tenancy is just using the same resources to serve multiple groups of users. I think the term came from apartment buildings, maybe. Got multiple tenants that share have their own apartments, but they share the same resources. They share the same building. Maybe that's where it came from. But anyway, it is a very common convention in SaaS that we have multiple different accounts or teams or whatever you call your tenant collection using the same application, but each team needs to only see their individual resources or data in this case. Achieving it inside Directus is pretty simple. There are 4 steps we're going to cover. Let's dive right in. Alright. So the first thing we're gonna do is create a tenant collection. Now what you name this is totally up to you. You could call it teams, you could call it accounts, which is one of my personal preferences. But, you know, if you're coming from something like Laravel, I think out of the box, they have something called teams. So we can add a couple of additional fields if we need to. These system fields are really nice, just a quick little shortcut, but I'll go ahead and create a team collection here and we'll just give it a team name. Right? So let's go in and create a team, we'll call it test team A, we'll save this. Let's go ahead and create another team, test team B. So now we have 2 teams, we've got our team tenant collection. Let's, let's mark this off on our list here. Boom. Step 1, done. Alright. So the next step is to create a relationship between this tenant collection and our Directus users collection. So Directus users gets created whenever you bootstrap your Directus instance. So if I'm looking at my data model, I'll find that under the Directus users here. And what kind of relationship you set up depends on how you want your tenancy model to work. So if users could belong to multiple teams, you're gonna use a many to many relationship. But if a user could belong to just one team, you could use a many to 1 or and the reverse one to many. So in this case, let's say we want users to belong to multiple teams or they could potentially belong to multiple teams. Now in that case, we are going to create a many to many relationship. So I'll just search for that here. And inside Teams, let's call our field users and we are going to look for the Directus users collection. Right? So I'm inside my team's data model, go to the mini to mini relationship. We'll create a new field called users, and the related collection is gonna be directus_users. Now I can show a link to that item, but if I open up the advanced section, I can see this junction collection that Directus is gonna create for me. I was calling this teams_directususers, and then we have the team's ID and the directus_usersid. Underscoreid. Too many. I'm also gonna create the corresponding field on the Directus users collection as well. Great. So I'll just go ahead and save this and with that relationship out of the way, we can mark off step 2 of the process. Now step 3 is gonna be creating our actual data collections. And for each one of those collections, we need to create a many to one relationship back to our tenant collection. So let's just keep this super simple and create a new collection called tasks. Very simple. I could even go in and, you know, add these system fields. Maybe we want to know who created the task, what date it was updated, and we'll just give this task a title. Right? Great. The next thing we're gonna do here is add a mini to 1 relationship back to that tenant collection. So we're just gonna call this team and the related collection is gonna be called teams. We'll hit save and now we have that relationship that will allow us to filter out those permissions when we're making those API calls. Alright. Great. So now if we just go in and let's create a couple of test task, A will assign that to test team a, and we'll create test task b. Go ahead and assign that to team b. Alright. Great. So now we've got the collection, and for each one of our data collections, like, let's say I had projects and then tasks, I would add that same relationship, that many to one relationship back to my tenant collection. So with that out of the way, let's go ahead and mark this off. And then the last piece of the puzzle here is to set up our permissions inside Directus to restrict access based on the user's team. What what teams are the users included on? We wanna restrict access based on that. So we're gonna go to our settings, go to access control, and let's create a new user or a new role, I should say. Let's call it user, and I'll enable app access here. You don't necessarily have to do that, You know, this is just to showcase the functionality. If you want them using the Directus app, you can. If it is API only, the API still respect these permission settings. Alright. So what I'm gonna do here, I'm just gonna go ahead and enable all these permissions by default, and then I'm gonna go in and start restricting this. So our share functionality, I don't necessarily need. Maybe I don't even want these users to be able to delete tasks or things like that. We definitely want them to be able to create new tasks. Maybe they can create new teams, But it when it comes to seeing tasks and editing task and seeing teams, we only want them to see the things that they are a part of. So how do we do that? We could go into our custom permissions in that case. So we'll just click on that and use custom, and then we will set up a filter rule. So here we are filtering the items that this particular user or this role which will apply to a user can have access to. So in this case, we've got that team field and because we used a many to 1 relationship or a many to many relationship, we have to use the is one of because the actual users, the direct us the team and the direct us users, it could be an array of teams that they are a part of. Directus has these dynamic variables we can use as well. So dollar sign current underscore user in all caps will allow us to access the current logged in user. We'll go for the dot teams because that was the field that we specified on the user collection, and then we're looking through our junction table. So that is underscore teams teams underscore ID, and then we have ID. So with this, and if I just copy this rule, we can apply that to the edit access as well. So I can go in and edit raw value. I could just paste this here, save it. Okay, great. So now if I were to create a new user, let's call them test user a, and just do test at example.com. Give them a nice secure password, and I go into the role settings. We'll select the user role, save that, open up a incognito window so we get a fresh set of cookies, and let's just log in to this user and see what's actually available. Alright so we logged in we cannot see any particular tasks here. Now if I refresh the task I could see that, but we have not associated that user with an actual team yet. So if I scroll down to the bottom let's add that user to team A and we'll hit Save. And now if I refresh this window we could see that that user has access to test task a, but they cannot see test task b, which is a part of the second team, which is test team b. Very simple. That is how you set up tenancy inside Directus. I hope this tip was helpful. Bring on some more questions. I want to cover more of those for you.",[3783],"162809be-8ab8-4cc7-977b-19ea714080a0",[],{"year":978,"number":3600,"id":3601,"episodes":3786,"show":3787},[3603,3604,3605,3606,3583],{"title":2161,"slug":2162},{"id":1793,"slug":3789,"vimeo_id":3790,"description":3791,"tile":3792,"length":2280,"resources":12,"people":3793,"episode_number":213,"published":3803,"title":3804,"video_transcript_html":3805,"video_transcript_text":3806,"content":12,"seo":12,"status":19,"episode_people":3807,"recommendations":3808,"season":3809},"inline-data-editor","926603960","In this recording of our live event on March 21 2024, Rijk, Jonathan, and Daniel discuss inline editing.","db868174-6a9e-4823-9962-382f5bd55cb1",[3794,3797,3800],{"name":3795,"url":3796},"Rijk van Zanten","https:\u002F\u002Fdirectus.io\u002Fteam\u002Frijk-van-zanten",{"name":3798,"url":3799},"Jonathan Wagner","https:\u002F\u002Fdirectus.io\u002Fteam\u002Fjonathan-wagner",{"name":3801,"url":3802},"Daniel Biegler","https:\u002F\u002Fdirectus.io\u002Fteam\u002Fdaniel-biegler","2024-03-28","Inline Data Editor","\u003Cp>Speaker 0: Hello, everybody. Welcome to feature request review with me as a host today, joined by the good old Reich, Van Zanten himself and Jonathan Wagner. Hello. Hello, everybody. Thank you for coming.\u003C\u002Fp>\u003Cp>Very exciting to see you all. Quite quite the quite the collection of people we have here from Algeria, Japan, everywhere. Germany, Austria. No. No.\u003C\u002Fp>\u003Cp>No. I nearly said it. But, yeah, today today, everybody's, apparently favorite topic because there's so much demand here in the channel right now. Inline editing, which can mean a lot of things. So let's just take a look at what a small example could look like.\u003C\u002Fp>\u003Cp>If you take a look at the screen shared footage of Jonathan, you'll see this is our feature request that got in, which you can vote on, by the way, in case, some people are not aware of this feature in our discussions in the director's repository. You may vote on stuff that gets implemented into the software, which is pretty cool. And, today's topic is about inline editing. If we scroll up a little bit to the top to the screenshot, you might have seen this in other interfaces online, you know, when you use Notion or whatever, like, many many sites implement this a little bit differently. You know, on Trello boards, you can click into the title, then you can change the title right then and there without a pop up or something.\u003C\u002Fp>\u003Cp>And, yeah, why why doesn't have direct as this yet? It's a good question. I mean, there are many, many different things that we could talk about. And, in case you have any questions or stuff that you want to share, please feel free to use the chat. We're excited to engage with you guys.\u003C\u002Fp>\u003Cp>It would be fun, a sign of life like previously. That would be nice. And how about how about we jump in right into a couple of questions? Let me quickly, give one second. Alright.\u003C\u002Fp>\u003Cp>So by looking at this, you know, basic example with the with the input field, I mean, the the first question to me is, alright. Like, how how much can we blow this up to, because Directus is very, very versatile, and, people build the craziest displays and interfaces and very, very customizable different pieces. How do they fit into this, and if we just target, like, simple inputs, for example, like in these simple example with, like toggles or text inputs. What about relations? Because those tend to be very nice to deal with.\u003C\u002Fp>\u003Cp>And, how about we go over to give over to Rijk for this one? How do you feel about different types of inputs for inline editing? Do you think we should, like, aim to be able to do everything in in in line editing? Should we reduce it down? Should we only support a sub set?\u003C\u002Fp>\u003Cp>How do you feel about inline editing in itself?\u003C\u002Fp>\u003Cp>Speaker 1: I mean, when it comes to inline editing, I think the first question that we gotta ask ourselves and everybody watching is like, what does that even mean in the first place? Is it kinda like a spreadsheet where everything is just a cell where you can change whatever you see? Is it more like, a table view by default where everything looks pretty through those displays, And then you have some sort of edit state. Is it like, an edit state where we render a whole interface in line? Or is there some sort of dialogue overlay that we're seeing when you click on one of the cells, so to speak, to to stay in spreadsheet terms.\u003C\u002Fp>\u003Cp>So I think the first question really is what is what is inline editing? What does it even mean? Right? Because we do have a different feature request for a spreadsheet view, which I think very closely overlaps, but there's definitely differences. And then I think the other question, of course, I mean, relationships, we'll dive into that in 5 minutes.\u003C\u002Fp>\u003Cp>But, if you think about the basics, I think one of the thing that we're doing with direct is with all the interfaces is to make sure that you can edit the data in a way that makes sense for what the data is. Right? So, a very basic example, you know, a slider could be a better representation for a numeric value that you're using for whatever it is that you're building. Right? But You wanna use a slider to control the value.\u003C\u002Fp>\u003Cp>When it comes to inline editing, you know, if we're looking at this screenshot where the reserve column right now presumably is in this edit state, you know, if we're dealing with inline editing in its sort of truest form, does that mean that it just turns into a sort of raw value input? Or do we include interfaces kinda like we are with a regular form? Right. So when it comes to a date value to name something, the technical value is always gonna be that that what's the exact spec, the, ISO 8601. Is that the thing that you wanna be editing?\u003C\u002Fp>\u003Cp>I would argue, probably not. You probably wanna have, you know, the data interface that you have configured for that field, which then leads to the question, does that even fit wherever we're doing this inline editing presumably in this table layout. Right? And I think we're we're quickly reaching the question then is like, okay, do interfaces even fit? And if they don't, how do we present it otherwise?\u003C\u002Fp>\u003Cp>Speaker 0: Right? Yeah. To your example, like, even even a simple slider, which is a nice way to input, like, a specific value inside of a range, which could be, you know, like, relevant to the use case, you know, where you want to restrict. You maybe you don't allow, like, negative numbers or something. But if if we allow, like, editing via raw values, then somebody might put in, you know, minus 1 or something, and then you very quickly run into the issue that you mentioned.\u003C\u002Fp>\u003Cp>We want to give people the tools to, like, input stuff as as as comfortably as possible with using, like, nice interfaces and displaying like, even displaying, you know, with with the value that you see inside of the table might, like, might not actually be the wrong value just like you described with the, like, date time, for example. So there's a disconnect there and, like, for, like, nontechnical users, for example. No. It could be it could feel bad, you know, from a user experience standpoint where they like if if marketing salespeople, what what was the thing that's, somebody said I won't name any names, but somebody said, like, the scariest thing that marketing people see is, like, a UUID somewhere. So we're dealing with UUIDs.\u003C\u002Fp>\u003Cp>Yeah. It is, is a scary endeavor, so you have to be quite careful with the display. So yeah. Yeah. Speaking to that, we have why don't we explain a little bit about the difference between our interfaces and displays?\u003C\u002Fp>\u003Cp>Because, you know, indirect is also 2 different things. What are those? How do I use those? What can they do?\u003C\u002Fp>\u003Cp>Speaker 1: Good question. So so from I'll I'll prefixes was sort of the theoretical idea behind it while Jonathan here pulls up the actual difference and how you configure them. So the basic idea is that a display, controls how a raw value is displayed in line, importantly. So it's really meant to be part of a sentence or one of the cells in a table or, you know, part of a description or something. So if you think about a, labels display, it can be configured to render it as a small color dot with a tooltip, with the idea being you can then render that in the title of a page, or in the description of a card or inside of a table.\u003C\u002Fp>\u003Cp>Right? The interface is really the the way you edit the value. So if you go and you open an item, it's basically the form field, section of of how you interact with that data. So it's really the difference between sort of block level, in CSS terms, editing of the data versus inline displaying of that same data. Importantly, those 2 can be very different representations of the same data.\u003C\u002Fp>\u003Cp>Right? So the image display will just be a tiny thumbnail where an image interface is like a bigger box where you can drag and drop stuff in and you have, like, preview and some more metadata available. They are at present a different thing. And the display is then Yeah. So so Jonathan right now is, you know, adding, a related values you know, those displays, and compose them all together to really control how an individual item is presented.\u003C\u002Fp>\u003Cp>Right? So on the collection detail page 2, there is a display template setting where, again, you can render individual fields as part of one title string. And then that's how it displays the individual pieces of data. So when it comes to editing them in line, one of the interesting, challenges that we're gonna be facing when it comes to the table layout specifically is that the value that you're seeing in here, it's very likely that that's not the value that you're gonna be editing. Right?\u003C\u002Fp>\u003Cp>Speaker 2: And is it a toggle? Is it something that you enable or disable? If you haven't enabled, what happens? If you don't have an enabled or how do I access the record? Because currently, you know, I just know that I can just click anywhere on the row, and it will open the item for me, and I can edit the item.\u003C\u002Fp>\u003Cp>How do we trigger, you know, you start to get into that. So we've got the whole interfacing aspect, relational content, allowed or not allowed. It feels like at least maybe you could almost do it in, like, an iterative. So start with the normal input fields, date fields, the the simpler interfaces and things to deal with versus relational. Relational gets complicated, right, depending on how deep or how complex that relational is.\u003C\u002Fp>\u003Cp>Now in order to edit this translation, well, that requires now that I get the full translational split toggle side by side translations kinds of things, and is that beneficial in this kind of a in this kind of utility? Yeah. Toggles, drop downs, you know, some of this, some of the more string text slash\u003C\u002Fp>\u003Cp>Speaker 1: Is this is there there is a a another additional question when it comes to how do we then render an interface to change the data, which is that there's gonna be a couple of them where the interaction would be way nicer if it's, like, truly in line. Like, so if we, if you think about a toggle, like a checkbox. Right? Kinda like the default checkboxes for selecting items. But let's say you have, you know, an on or off state on your own custom item.\u003C\u002Fp>\u003Cp>In your display, ideally, I just wanna click it and it flips. I I don't wanna click it then open a dialogue that shows me just one toggle anyways, and then close it, and then change it that way. You know what I mean? And I think the same goes for basic text values or basic numbers where I just wanna be able to click the text, edit it, and then that's it. Right?\u003C\u002Fp>\u003Cp>I don't wanna have to deal with clicking it, seeing a popover, drop down dialog, whatever it may be, then change it there because it's just another step away for something that feels like it could have been a spreadsheet, basically.\u003C\u002Fp>\u003Cp>Speaker 2: Yep.\u003C\u002Fp>\u003Cp>Speaker 0: With that example, you know, with just clicking in and changing something, Tim also brought up a nice point because, with nullable values, like, even such a simple thing as a, like, togglable checkbox, if that is even a word, you know, we already run into, like, a small little, thing, like, okay, what if you want to set it to null? You know? Like, if you uncheck the thing, it's false, but you want it to be null. Do we do we, you know, do we, like, enable triple, like, stateful checkboxes that support 3 values? Do we want to have, like, a ellipse that gives you, like, a setting for that?\u003C\u002Fp>\u003Cp>Like, similar to our v form where, you know, you're allowed to enter a raw value, but that's another piece of UI that would have to be somewhere some at some point. Yeah. Exactly. For strings, similar in that vein, you know, for strings, do you want a, empty string, or do you want a null value? Alright?\u003C\u002Fp>\u003Cp>How do you differentiate between those?\u003C\u002Fp>\u003Cp>Speaker 1: And it can even differ,\u003C\u002Fp>\u003Cp>Speaker 0: you know, from from to\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. All of those are problems that we have solved in the form context. Right? Because even in the form, there's just the field label drop down that says empty this value, which is effectively nullifying it. So the question is also how much of that do we wanna pull a level up in here versus how much of that do we just say oh well if you explicitly wanna change a boolean back to a null, you can always go to the page detail thing and then do it there.\u003C\u002Fp>\u003Cp>Right? There is an escape hatch, and we could make the call to say, well, you know, the toggle display goes between true and false. And if you need to reset it back to that null state for whatever specific reason, you can always go to the detail page, and do it there. Right? And I think similarly for the string empty update, empty string, null, that that question is a question that we've currently solved on the interface settings, I believe.\u003C\u002Fp>\u003Cp>Therefore, an input interface, you can say empty string becomes null, and then that's just the value it, you know, stages, it emits. So if we go the route where we use the exact same interface you have already configured as the way to edit it in line, that problem should sort of automatically be solved. Right? Because we're using the same interface with the same settings, and then that should be fine as is. Where that part gets super interesting though, is how do we deal with conditional fields within the context of that row?\u003C\u002Fp>\u003Cp>So the way conditional fields currently work is, you know, you have the whole form of on the field, you have the whole form on the page, and then we can use the values of the form and sort of connect the dots. Right? And use it that way. On a row in a table though, the fields that are displayed might be completely different. The second piece to that is gonna be for conditional fields.\u003C\u002Fp>\u003Cp>They're now react reactive in real time where if you make a change to one field, sort of real life like, real time updates it elsewhere. When you're in the context of an individual row, and I think this is just a bigger question overall, is, like, does it auto save every time you sort of blur the input that you change something in? Or is there gonna be a save button that is like, okay. Now you've made all the changes and you hit save once.\u003C\u002Fp>\u003Cp>Speaker 0: That's a very good question. Like, especially for, like, the togglable checkboxes or whatever because, like, if you just miss click somewhere and all of a sudden, that could be a big change, you know, like with flows and automations and other stuff that runs or update events, fire something. If you hit publish on something, that should be, there should be a mechanism, you know, to stop making mistakes, you know, like, accidentally, if you have, like, you know, is active or is published field on some type of blog post or whatever, and you don't want to accidentally just oops. Oops. It's live now.\u003C\u002Fp>\u003Cp>Oopsie. Would would be nice if we could avoid that, but, yeah, that opens up another can of worms. You know? Like, how do we do you configure this on a field level? Do you configure this on a column level, on a, preset level?\u003C\u002Fp>\u003Cp>Like, where where do you where would you like to do that? That's also the thing. Because very good point from the chat also from the, is currently, for example, the displays do not know, like, which item they belong to. They are very stupid in that way with quotation marks where they just Yeah. Display a value.\u003C\u002Fp>\u003Cp>This is why they're called displays.\u003C\u002Fp>\u003Cp>Speaker 1: It's it's a presentation, presentation layer. Yeah. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: So if if we would like to have the, you know, like, mutable, displays or whatever, you know, if we decide to go that down that road. So, we would have to provide the displays with the item ID in the collection, like, in every place because there's not not only displays only in the table view, in the tabular layout, but also in, like, titles and drop downs or whatever. Like, any and they could be at any place at any time, which makes this a little bit, dangerously spaghettty like, which\u003C\u002Fp>\u003Cp>Speaker 1: which which also, kind of bleeds into a similar yet different question, which is where do we allow inline editing in the first place? Because we're assuming we're sort of assuming the table layout right now because that's the most commonly seen elsewhere. I mean, I'm I'm sure that people that have ever seen Airtable are like, that's that's the way. But at the same time, the the question is, do we inline edit elsewhere? Like, if you have a status icon in your page title, does that become an inline editable thing?\u003C\u002Fp>\u003Cp>Right? In the chat somebody says we'd appreciate it for the card layout, maybe even the calendar. I would agree. So on the card layout, if you have in your description of an individual card, you have a a text display. If you click the description, can you now edit the description of a card in line, which opens up a big can of worms, especially from a UX design perspective.\u003C\u002Fp>\u003Cp>Speaker 2: I I guess because because we're we're concatenating that into a string in the display right now for a card. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Mhmm. Mhmm.\u003C\u002Fp>\u003Cp>Speaker 1: So if we're just The same for page titles for the for for that matter or the calendar layout. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: And Tim, Tim, another fun, fun little thing that could happen if you're already editing something and you have auto refresh enabled. Somebody updated your stuff in between the edits, and now you have another edge case. Love it. Love it. Love it.\u003C\u002Fp>\u003Cp>Love it.\u003C\u002Fp>\u003Cp>Speaker 1: Which is gonna be the same issue that you have with, you know, the regular form. So I'm hopeful that whatever solution we come\u003C\u002Fp>\u003Cp>Speaker 2: up with that right now.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Exactly. Whatever solution we come up with for that, I'm sure the same solution should also then work here. But Yeah. For this So this is a good example in the cars layout.\u003C\u002Fp>\u003Cp>Right? So in that right side bar, you see title, subtitle, and you can configure any combination of the different fields with any sort of additional strings in between, for like adding a dollar sign in front of price. That kind of stuff. Right? So in this particular case, it becomes extra interesting.\u003C\u002Fp>\u003Cp>Because if you now were to click on the number of the price in that description line, do you expect it to be editable? Right? And how do you determine that? Yes or no? Because making every editable globally at all times feels like a UX nightmare waiting to happen, but it also unlocks a lot of flexibility.\u003C\u002Fp>\u003Cp>Right? So it's a tricky one as many things are.\u003C\u002Fp>\u003Cp>Speaker 0: Of course. So how about how about then we take a different look? So now we thought about tables and the current layout and whatever. Technically, there's also another option of adding a new layout, like you mentioned earlier, with the spreadsheet layout, which could be the point where you do that type of stuff. I don't know.\u003C\u002Fp>\u003Cp>Maybe, you know, I'm just, you know, throwing stuff in the room right now just to discuss a little bit. But Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Because it does have the mute display thing. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. So maybe if it turns out to be better or easier or whatever, the spreadsheet layout could be an interesting point where we could tackle this, but, then we have new, cans of worms and cans and of worms stacked upon each other, you know, till this evening.\u003C\u002Fp>\u003Cp>Speaker 1: Stack a can.\u003C\u002Fp>\u003Cp>Speaker 0: Which which is fun. Yeah. Because, you know, a spreadsheet layout people come into a spreadsheet layout with lots of preconceived notions. Is that the is that the word? And\u003C\u002Fp>\u003Cp>Speaker 1: Like, I think expectations is the word I would say. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Expectations. Yeah. Yeah. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Expectations and and stuff that they would like to have or that they expect to be there because it's, you know, spreadsheets are, daily used by billions of people. So they do want to have, like, conventions and keyboard navigation and different stuff that they already, you know, introduced to the day to day life with bulk renaming, whatever formulas. Maybe they think that this is then an actual spreadsheet that they can do calculations in, which is another cool feature that we would like to have probably from\u003C\u002Fp>\u003Cp>Speaker 1: different feature requests for a different day. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Exactly. Exactly. Exactly. So spreadsheet seems to be a very big thing, for me personally. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: I think to me it's just a very different thing, isn't it, than than this inline editing approach that we're talking about? Because I fully agree with what you're saying. If you close your eyes and you think spreadsheet, there's a whole set of rules that come with that. That if you miss one of them, you have a bad spreadsheet. Right?\u003C\u002Fp>\u003Cp>Because if you look at Excel versus Google Sheets versus Excel on the web or even Apple's, what they call it, numbers, they are all effectively identical in the exact same settings, exact same thing you can do. It's a right or wrong approach. So, funnily enough, for a session like this, it would be a very short call because it's like spreadsheet. It's basically a known entity at that point. Right?\u003C\u002Fp>\u003Cp>I think where it gets interesting to me, and this is sort of the the the Airtable spreadsheet equivalent. It's like, you're not dealing with the spreadsheets. You're dealing with a table that you can edit in line. But it's not a pure spreadsheet in the technical term, right, where you're dealing with cells with raw values, so to speak, rather than UI elements where you edit the values. Yeah.\u003C\u002Fp>\u003Cp>Same thing in the chat was was coined there. The reason why I immediately started thinking about okay. So our display is now editable as a concept is basically from that modular fashion. Right? Because making one new layout that does inline editing sure.\u003C\u002Fp>\u003Cp>We could totally make that happen. We could also make it an option of the regular table layout and then that's it. Right? To me the interesting question for this is really like, is there a way to make it work for any display anywhere? And is that something that we wanna do or not?\u003C\u002Fp>\u003Cp>Because by doing so, you get the tabular layout feature out of the box but you also unlock it elsewhere. Like, if you have, a list of one to many items on, a record, each of those one to many items now also has inline editing because they are using displays to render those values. Right? So it it just, like, it transforms this discussion from an inline table editor to a bigger sort of, like, what does that editing in situ look like across the board?\u003C\u002Fp>\u003Cp>Speaker 0: Very, very good question. I wish I would have the answer to it, but, yeah, like you said, I mean, for for for example, for the, like, relational stuff, I could see, you know, reusing the existing drawer that pops up in different places, which could be a nice experience. You know, if you let's say you have a, a website collection with blog posts or something, articles, whatever. And as the drawer opens and you can use the currently already existing, like, relations go through the list, it it's it's with pagination and stuff. That could be nice.\u003C\u002Fp>\u003Cp>But like you said earlier, if for smaller fields, it feels like add UX if you, you know, just want to change one text and then the drawer opens or, like, a pop up opens with just one input. That's, like, at that point, it just feels very not thought through, which I would like to avoid. Yeah. We got correlate with this PR. Yeah.\u003C\u002Fp>\u003Cp>Yeah. Yeah. The the batch editing is also a nice thing. So slightly different.\u003C\u002Fp>\u003Cp>Speaker 1: There's also the the, the bigger question here, Which is\u003C\u002Fp>\u003Cp>Speaker 0: As it always is?\u003C\u002Fp>\u003Cp>Speaker 1: As it always is. But it's that's that's what these sessions are. For those who are new, in these sessions, we always go find all of the edges and then take it back into what is actually realistic in the next step. But so think about it. If if we're now dealing so so you we explained it before that the displays were meant as a presentational only unit and interfaces are meant as an editable unit of the same value.\u003C\u002Fp>\u003Cp>Right? But now we're saying: What if displays are editable, but only at sometimes? So some displays like a string we wanna make inline editable, Some displays like a checkbox inline editable. And some like a relational related values or something we wanna have a click with some sort of dialogue to to have the space to make it a proper editing experience. Right?\u003C\u002Fp>\u003Cp>The question could also be, should those still be 2 separate entities or should they all be interfaces? And based on how we where we're rendering an interface, it can display in different ways. Right? So if you have an interface on a form where it has enough space to grow, it can show as a proper big input. But if you try to squash it into a cell of a table, it just drops the visual representation of an input and becomes one of those underline only in inputs for example.\u003C\u002Fp>\u003Cp>Right? Because then with a read only prop, yes or no, you can toggle between a string that's just rendered as a string. Or when you click it, it switches into this inline interface version, basically. Know what I mean? Because then for a display one, it can always just be interactive for an input, you can just click to start typing for, a related values one or a relation when you can click it and it would just do it in a pop over or dialogue or something along those lines.\u003C\u002Fp>\u003Cp>Speaker 2: So depending on the interface type or the interface implementation, you could allow or disallow inline editing ongoing. Right? Right.\u003C\u002Fp>\u003Cp>Speaker 1: It it would basically mean that the interface is always the thing that controls how it's displayed, because therefore you can have the interface control how that interactivity works in that display context. Now, that does, of course, come with the the side effect that you can no longer have a different display per interface like we currently allow. So theoretically, you could use, you know, a text input interface, and then an icon display if you wanted to. Or, you know, you can have a rating input that is a slider and then a display that's that's rendered as stars. I don't have the stats on how popular that is.\u003C\u002Fp>\u003Cp>Like, that would be a breaking change where we remove that. At the same time it also makes it easier to configure. Right? Because you can have, okay, I'm using the icon this, icon interface, and at smaller scales, it turns into an icon display. Right?\u003C\u002Fp>\u003Cp>Speaker 2: So I guess another option could be that if you toggle the, you know, inline capability here, then the display gets turned off. Right? You don't have display capability. So you don't have a breaking change, you have the ability to come in and edit an existing interface, keep the existing display capabilities where I don't know what the overhead looks like for that, but something that we could think about instead of just outright ripping this out because I I still think, again, this is gonna be, I think, the most common case where I would want the ability to do, like, inline editing is on strings and numbers and dates. Right?\u003C\u002Fp>\u003Cp>Those are the, you know,\u003C\u002Fp>\u003Cp>Speaker 1: the The the simple somebody in the the chat called it the simple\u003C\u002Fp>\u003Cp>Speaker 2: stuff. Yeah. If I'm dealing with relational, you know, now you're dealing with complex, you know, you're you're just as easily popping into the item at that point to edit a relational setup potentially. Now down the line, that could change, but I think the most common things, you know, based on the ticket that we got inbound was around I just wanna be able to update some numbers, and I'll be able to quickly go through and adjust some numbers. I wanna adjust some text, and it's not a bulk edit because I'm not making them all the same, but I wanna be able to quickly edit things in line.\u003C\u002Fp>\u003Cp>Relational's I don't know. My brain my brain, I can see the nicety of it to be able to quickly just pop an interface and make a change without having to open the entire work, you know, in and out of a record to do that, but I think short term initial implementation. But if we added a toggle here or a function that says, you know, disable the display, if you go to inline, then you get the interface, and that is the display.\u003C\u002Fp>\u003Cp>Speaker 1: Woah. Say say that again. So That last part,\u003C\u002Fp>\u003Cp>Speaker 2: if you\u003C\u002Fp>\u003Cp>Speaker 1: have a display but you use an interface and you toggle it, you now have a display. You lost me in that last part.\u003C\u002Fp>\u003Cp>Speaker 2: No. No. So I'm saying that if we add a new if we add the feature for inline on the interface so if specific interface designed to support inline, and you just say, I wanna use the inline or I don't know where we would set that.\u003C\u002Fp>\u003Cp>Speaker 1: I think it's set up based on the type. Yeah. Yeah. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Yeah. I think somebody in the chat mentioned that it's too. It might have been the derp. Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: It gives you the, you know, if I make c one in here, it overrides display. Right? Display is essentially either disabled, right, becomes grayed out here, or it's just ignored, and with a note here that says if I check-in line, then boom display is no longer utilized. We're gonna utilize the interface spec for that.\u003C\u002Fp>\u003Cp>Speaker 1: So so, you know, in the chat, mentioned a similar idea in that we could also have and this is a direct quote. We could also have multiple optional components for a single interface, like an inline version and a block version of the same interface. And then when the inline interface version exists, that can be used instead of the display, and then gray out the display to you your point.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Because the,\u003C\u002Fp>\u003Cp>Speaker 2: this gets ignored. As long as there's a note or a warning, you know, even if someone does set it here, it's just ignored. If in line is enabled here, then it just gets ignored.\u003C\u002Fp>\u003Cp>Speaker 1: Because the reason to then ignore the displays is because you kinda want the display itself to be editable at all times at that point. Like a checkbox, interface in the an inline checkbox interface would just be a toggle that's always active for you to click on. Because the comment that was just made in the chat too is like, you say you're loading a nested relational simple column in a table. You need simple editing. But in terms of toggling, you know, you can use the display by default.\u003C\u002Fp>\u003Cp>And then when you turn on an edit mode, maybe all of those in line ones are displayed so you still have displays. It gets tricky. So so the reason I'm saying that is we have things like the, formatted value display which is basically, a very configurable string. Right? You can make it bold, italic.\u003C\u002Fp>\u003Cp>You can make it, a different font if you wanted to. If you wanna go crazy with themes, you can make it Comic Sans. Would would highly recommend doing that. Ben's gonna kill me. Then, so those displays are and and you can have a text prefix.\u003C\u002Fp>\u003Cp>Right? That's where it gets a little tricky. So the display can show additional data that's not the, like, present in the value. So if we wanted to keep that level of flexibility, like, a rating display being stars. If you have an inline number input, now you kinda kill the usefulness of this rating display.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. The the drawing drawing the line, what is considered simple, is also very hard. Like, for me, for example, like, a many to one relation, for example, I mean, that's basically a drop down. No. A drop down doesn't seem to me to be, like, a complicated change, so to speak, like, a complex update.\u003C\u002Fp>\u003Cp>Yeah. But from a user standpoint, not from the implementation detail standpoint, but from a, like, oh, I just want to change the user. That's like one, you know, one click, select the user, select another user, be done. That seems like a simple thing, technically, you know, but it's still a relational change.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Wait. What did the doer say?\u003C\u002Fp>\u003Cp>Speaker 1: I mean, it's you kinda wanna what they're saying is you want the way that you're editing the value to to look the same after you're done editing it. So if you're looking at I think the obvious ones are, the toggle again. You wanna click it and you just edit it in the way it's presented. If you have a rating display, you wanna just click on the number of stars and that's the way you edit the value that you're seeing. When it comes to, like, a related values thing where it shows x number of items, it gets a little finicky.\u003C\u002Fp>\u003Cp>Right? If you're talking about the formatted value display where you might have, you know, a dollar sign prefix or something, Now it also gets tricky because now the moment you edit the value, it shows something completely different than you have displayed. Right? I think with dates, there's a different expectation, but if you think about the raw value underneath, it's just a string. It's an ISO 8601 string.\u003C\u002Fp>\u003Cp>So if you were to click a nicely formatted in your local language date, and you click it, and now all of a sudden it turns into a technical string, that'd be a bad experience. Right? Even though you could inline edit it as a string, which is not probably not what you want.\u003C\u002Fp>\u003Cp>Speaker 0: But what's also funny about like the ISO strings is that, you know, often are like, usually they're stored in like UTC universal time. So, like, people Right.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: In other time zones, might not even know what to actually put in there because, you know, they want to select, alright. This should happen every day at, you know, 8 in the morning or something like some some whatever date. And, then all all of a sudden, the UTC time is completely different. And then you're like, oh, no. This this field's not good.\u003C\u002Fp>\u003Cp>What is it? I don't know. And again, yeah, it it really depends on the time zone of the server itself of your database or whatever. And then\u003C\u002Fp>\u003Cp>Speaker 1: I mean, at that point, it's it's game over. You really need to have some sort of proper data interface that handles that under the hood and then those presentational side of it. There is this is this is where the discussion is is interesting to me though, because it's like we're basically saying, do we have a small interface or do we have an editable display? And what's the difference? Right?\u003C\u002Fp>\u003Cp>Because when it comes to the date the date time specifically, the the basic of the date display is that it basically just takes the raw value and then shows it in whatever is appropriate for your locale. Right? So in the USA, it does the, objectively ridiculous thing. And I moved here by choice, mind you, of month slash day slash year. Right?\u003C\u002Fp>\u003Cp>And then elsewhere, it'll just do the proper thing of day, month, year.\u003C\u002Fp>\u003Cp>Speaker 0: Right? Shot's fired.\u003C\u002Fp>\u003Cp>Speaker 1: But I can imagine that if you have an inline editable date interface, you can just, you know, just like the system native one, basically, you can click on any of those parts and just type in what you want. Right? It doesn't have to be a whole selection, UI, and all that kind of stuff. So sorry for air format.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. The the American people go to great, great, great lengths to not actually use some type of normal measurement. Like, how long is something? Yeah. I don't know.\u003C\u002Fp>\u003Cp>2 2 and a half elbows or something. That's something how high how high is this building? I don't know. It's at 20 1,000 feet. I don't know how big is a feet.\u003C\u002Fp>\u003Cp>Speaker 1: What? Oh, man. This derails very quickly though. Next session, we'll spend, it's a full hour on the Imperial system.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, no. Somebody's actually this relates to the discussion. Yeah. Great. Okay.\u003C\u002Fp>\u003Cp>Sure. Sure.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Yeah. Yeah. Yeah. Yeah.\u003C\u002Fp>\u003Cp>This yeah. At least take a bit of context.\u003C\u002Fp>\u003Cp>Speaker 2: Everything should be millimeters. He's Google. The Australians got it right there.\u003C\u002Fp>\u003Cp>Speaker 1: There there's a good point in the chat though. Let's say you're storing a height value in millimeters or centimeters or meters or something, but then you wanna display it with a display, you know, in feet or inches or that kind of stuff. If you then inline edit it, what are you now inline editing? Because if it would be an interface where you're doing the value, you're now editing, I suppose, the millimeters, but you're showing it as foot. You know what I mean?\u003C\u002Fp>\u003Cp>Or as as inches.\u003C\u002Fp>\u003Cp>Speaker 2: Oh, don't do it. Don't do it. Just do millimeters.\u003C\u002Fp>\u003Cp>Speaker 1: Well, yeah, that's not always appropriate.\u003C\u002Fp>\u003Cp>Speaker 2: We're we're not that dumb. We we figure it out. Mhmm. We're we're slowly being converted to metric by the by the global corporate infrastructure. We're getting there.\u003C\u002Fp>\u003Cp>I actually flip all my stuff. I use Metrc on everything now.\u003C\u002Fp>\u003Cp>Speaker 0: Good boy. Good boy.\u003C\u002Fp>\u003Cp>Speaker 1: From FCW comes in with the question, you know, what if we click on a cell, which is one of the displays, and then the field whatever that field's interface appears, just like it does in the form, and I could pop over and it will be prefocused or something. That is very close to basically where we started the discussion. So I'm glad that we're coming full circle, in in converging it back down. Because that would be, in terms of implementation, by far the simplest way for us to build this. Right?\u003C\u002Fp>\u003Cp>It's like, don't care about any of this stuff. You click it, you open a little pop over and then you change it and it's done. I also like simple things.\u003C\u002Fp>\u003Cp>Speaker 2: Drop down, you get your whatever.\u003C\u002Fp>\u003Cp>Speaker 1: You get effectively everything. Now there is. One major downside with that, that we've sort of hinted at, but not explicitly called out like this, Which is I believe that there's a lot of data types where you wanna edit it truly inline and not in a popover. So you have a checkbox, if you click it, you wanna have it change. You don't wanna have it open a popover with an inline toggle that you then click to change the other thing.\u003C\u002Fp>\u003Cp>I think when you have a small, text input Drop downs.\u003C\u002Fp>\u003Cp>Speaker 2: Drop downs, same thing.\u003C\u002Fp>\u003Cp>Speaker 1: Change the change the drop downs, same thing. Right? Both data entry gets frustrating in that model and I would agree. Like, I think for a drop down, you just wanna click it. It needs to show the drop down section.\u003C\u002Fp>\u003Cp>It'd be a bit of an annoying UX if it then opens a pop over with the same dropdown again that you then click to to have a nested popover down, I guess, which would also be a bit annoying.\u003C\u002Fp>\u003Cp>Speaker 2: Very much agreed.\u003C\u002Fp>\u003Cp>Speaker 1: I do think that for me, one of the requirements from a UX design perspective is that we have some sort of truly in line flavor for some of those, and then use the pop over for the interfaces where it doesn't make sense. Right?\u003C\u002Fp>\u003Cp>Speaker 2: I like it.\u003C\u002Fp>\u003Cp>Speaker 1: Picky stuff. But it's it's also hard to it's also difficult to decide which ones make sense and which ones don't. Because I'm thinking about our color display, which is just a tiny dot in its smallest form. If you click that, do we really have to show the full color interface? Because again, it's more like a a select.\u003C\u002Fp>\u003Cp>Right? It's more like a drop down. You could just click the tiny preview and then edit the drop down items directly. But how do we how do you control that, right? Which is, I think, what we're getting at is sort of the suggestion from the DIRF earlier, which kinda says, it's up to the interface to communicate somehow whether or not it is, inline editable.\u003C\u002Fp>\u003Cp>Yeah. So I think this is a good example, John. Jonathan. Exactly. It's like this whole drop down, we could just render that the moment you click on the little preview in line, right?\u003C\u002Fp>\u003Cp>It doesn't have to have this whole interface again because the only real thing that you care about for the interface is the part in the drop down, right? So from the chat, if interfaces have an inline variant those could be used inline and then fall back to popover if the interface doesn't have an inline variant. I think that is basically what I'm suggesting then. Yeah. Because then for the color in line variant, it can just render it, you know, nice and in line.\u003C\u002Fp>\u003Cp>For, a one to many interface where we know it's like there's there's no way to do the whole UI in a table row, it would use a pop over and just render whatever needs in there. I don't know though if interfaces should always have an inline variant, and it is then in charge of what goes in the popover because I can also imagine that the UX of how you wanna deal with an interface itself within the constraints of a pop over smaller window can be different. So if you think about the pagination for a one to many, for example, if you have that configured to, paginate at 50 and the individual rows of a once many are pretty big. You know, they're the regular size of an interface or, input height is what we call it. For a consistent view on the form, it looks nice.\u003C\u002Fp>\u003Cp>But if you cram that into a small popover, there's gonna be a lot of overflow scrolling now in that tiny little, in that tiny little pop over. We could go full modal. This is from the chat. Let me mind you. We could go full modal and make people suffer, which we could, but I I kinda to me, it's no longer in line editing then.\u003C\u002Fp>\u003Cp>Right? You just have a short cord to open a single field. But I could imagine that yeah. John, thanks for pulling this up. Like that left column section you're seeing there.\u003C\u002Fp>\u003Cp>I could imagine that the interface itself has an alternative rendering mode where it can just compress it down a hell of a lot more to be a better experience in a sort of pop over context. Popovers on mobile would be particularly a pain in the neck. Another one from the chat. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Absolutely true. Which, in that case, you know, there is the opportunity to say for mobile, if we're rendering a popover, we're just rendering it as a dialogue or it might take over the full screen. There there's ways to responsibly handle that responsibly, responsibly.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So just looking at the time, real quick since we are approaching the hour mark. I think yeah. But I I think we landed on a quite the nice, let's say, idea on how to tackle this because, you know, nothing is, you know, struck in stone, but I I do think I do think I like the approach of, you know, you can have interfaces, you can have displays, and you can have inline interfaces, which sounds very reasonable to me. But then, like you said, alright.\u003C\u002Fp>\u003Cp>Where do we control what pops in, what doesn't pop in?\u003C\u002Fp>\u003Cp>Speaker 1: And I think it will have to be the interface, honestly. Because I could, an input interface, it knows that it could be handled in line. There's no way real way for us to to see that from the outside in. Right? I mean, we could always have, like, a lookup table somewhere, but it would only work for the stuff we the stuff we ship in core, not for extensions, which is a big part of this, of course.\u003C\u002Fp>\u003Cp>I I I think that the the question that I'm struggling with now is really what is the difference displays and interfaces in this paradigm. Right? Because now we're rendering interfaces in the place of a display, sometimes. And I'm I'm now just starting to think with this whole in line editing idea, maybe a display is just a read only inline version of the interface. It it does remove that flexibility that we talked about earlier where you could have a different display value for a input value.\u003C\u002Fp>\u003Cp>But to me, it would sort of solve for some of the complexity in configuration and some of these questions that when is it when is it a display? When is it an inline interface? How do you control between the 2? How do you toggle? Etcetera etcetera.\u003C\u002Fp>\u003Cp>Because that way we could say, well, in every interface needs an inline and a block version. The block version is what we use on forms and bigger pages. The inline version is what we use in title cards and table rows and that kinda stuff. And then it is read only or it's not. Like we currently already have.\u003C\u002Fp>\u003Cp>You know, we have read only forms, for permissions and all that kind of stuff. And a display is effectively just a read only inline version of the interface. And then if you have the editable table layout which is just a toggle. Maybe it's always on tbd. The inline version of the interface now controls if it's a pop over or not.\u003C\u002Fp>\u003Cp>So the inline version of a die, of a checkbox. Just click it. It immediately interactive. In line version of a text input. Click it.\u003C\u002Fp>\u003Cp>You can type in line version of a relational, one to many or something. By default, it'll just show 6 items. But then when you click it, the popover has the actual editable part. Right? But I think it could be the same the same interface.\u003C\u002Fp>\u003Cp>It's all interfaces at that point.\u003C\u002Fp>\u003Cp>Speaker 0: Interface is all the way down.\u003C\u002Fp>\u003Cp>Speaker 1: I mean, it would reduce it. So so one of the other parts is, like, there is a bit of confusion around the difference between interface and display. And I feel like a lot of people configure an interface by setting up a new field and then don't know that displays even exist. Cause if you configure a new field, it's very easy to skip over it and never know it's there. Right?\u003C\u002Fp>\u003Cp>So I also think there is a case to be made that it would improve the experience for a lot of the people that normally don't, really take the time to go over every single option that exists because it can be a bit overwhelming, you know, arguably. There's a lot of stuff you can configure.\u003C\u002Fp>\u003Cp>Speaker 0: That sounds good to me.\u003C\u002Fp>\u003Cp>Speaker 1: It'd be a gigantic rate and change, so don't worry about that.\u003C\u002Fp>\u003Cp>Speaker 0: This is not something we can just\u003C\u002Fp>\u003Cp>Speaker 1: in a little 10.1. Here you go. Bang.\u003C\u002Fp>\u003Cp>Speaker 0: Looks good to me. Ship it.\u003C\u002Fp>\u003Cp>Speaker 1: But I I think to me, that would sort of solve it because then for the table layout, you can have a toggle somewhere that says make it editable. Yes or no. For and for any of the layouts, really. For the tabular layout, we can make it editable by default. For the cards layout, we could say it's probably not a good idea to do it for some but for others because now the layout's in control.\u003C\u002Fp>\u003Cp>I think there's there's something to it.\u003C\u002Fp>\u003Cp>Speaker 2: Very cool stuff.\u003C\u002Fp>\u003Cp>Speaker 0: Good stuff, guys. Any last questions from the, audience? Your chance is now.\u003C\u002Fp>\u003Cp>Speaker 1: Forever hold your peace. Winston.\u003C\u002Fp>\u003Cp>Speaker 2: Final cue.\u003C\u002Fp>\u003Cp>Speaker 0: Forever hold your peace.\u003C\u002Fp>\u003Cp>Speaker 2: If you're if you're still awake.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: Oh, let's see. Vincent.\u003C\u002Fp>\u003Cp>Speaker 0: Someone type it. Well, then, thank you everybody.\u003C\u002Fp>\u003Cp>Speaker 1: Thank you. Bored everybody to death.\u003C\u002Fp>\u003Cp>Speaker 2: We've hard hard boiled a few eggs.\u003C\u002Fp>\u003Cp>Speaker 0: Lovely. Thanks for tuning in. In case you enjoyed this, there's much more of this on directors.io\u002Ftv. If you want to go there, there's lots and lots of interesting shows for you. Lots of good, great entertainment.\u003C\u002Fp>\u003Cp>And, feel free to join us next time for another very interesting topic. Thank you for linking the show, Kevin. Oh, yeah. Fire emojis. Yes.\u003C\u002Fp>\u003Cp>Very good. Thank you everyone for tuning in. We hope you have a\u003C\u002Fp>\u003Cp>Speaker 2: great day. Enjoy your week, folks. Have a good night. Good\u003C\u002Fp>\u003Cp>Speaker 0: night. Strong feelings, please let us know. The discussion is open. The issues are open.\u003C\u002Fp>\u003Cp>Speaker 1: That way.\u003C\u002Fp>","Hello, everybody. Welcome to feature request review with me as a host today, joined by the good old Reich, Van Zanten himself and Jonathan Wagner. Hello. Hello, everybody. Thank you for coming. Very exciting to see you all. Quite quite the quite the collection of people we have here from Algeria, Japan, everywhere. Germany, Austria. No. No. No. I nearly said it. But, yeah, today today, everybody's, apparently favorite topic because there's so much demand here in the channel right now. Inline editing, which can mean a lot of things. So let's just take a look at what a small example could look like. If you take a look at the screen shared footage of Jonathan, you'll see this is our feature request that got in, which you can vote on, by the way, in case, some people are not aware of this feature in our discussions in the director's repository. You may vote on stuff that gets implemented into the software, which is pretty cool. And, today's topic is about inline editing. If we scroll up a little bit to the top to the screenshot, you might have seen this in other interfaces online, you know, when you use Notion or whatever, like, many many sites implement this a little bit differently. You know, on Trello boards, you can click into the title, then you can change the title right then and there without a pop up or something. And, yeah, why why doesn't have direct as this yet? It's a good question. I mean, there are many, many different things that we could talk about. And, in case you have any questions or stuff that you want to share, please feel free to use the chat. We're excited to engage with you guys. It would be fun, a sign of life like previously. That would be nice. And how about how about we jump in right into a couple of questions? Let me quickly, give one second. Alright. So by looking at this, you know, basic example with the with the input field, I mean, the the first question to me is, alright. Like, how how much can we blow this up to, because Directus is very, very versatile, and, people build the craziest displays and interfaces and very, very customizable different pieces. How do they fit into this, and if we just target, like, simple inputs, for example, like in these simple example with, like toggles or text inputs. What about relations? Because those tend to be very nice to deal with. And, how about we go over to give over to Rijk for this one? How do you feel about different types of inputs for inline editing? Do you think we should, like, aim to be able to do everything in in in line editing? Should we reduce it down? Should we only support a sub set? How do you feel about inline editing in itself? I mean, when it comes to inline editing, I think the first question that we gotta ask ourselves and everybody watching is like, what does that even mean in the first place? Is it kinda like a spreadsheet where everything is just a cell where you can change whatever you see? Is it more like, a table view by default where everything looks pretty through those displays, And then you have some sort of edit state. Is it like, an edit state where we render a whole interface in line? Or is there some sort of dialogue overlay that we're seeing when you click on one of the cells, so to speak, to to stay in spreadsheet terms. So I think the first question really is what is what is inline editing? What does it even mean? Right? Because we do have a different feature request for a spreadsheet view, which I think very closely overlaps, but there's definitely differences. And then I think the other question, of course, I mean, relationships, we'll dive into that in 5 minutes. But, if you think about the basics, I think one of the thing that we're doing with direct is with all the interfaces is to make sure that you can edit the data in a way that makes sense for what the data is. Right? So, a very basic example, you know, a slider could be a better representation for a numeric value that you're using for whatever it is that you're building. Right? But You wanna use a slider to control the value. When it comes to inline editing, you know, if we're looking at this screenshot where the reserve column right now presumably is in this edit state, you know, if we're dealing with inline editing in its sort of truest form, does that mean that it just turns into a sort of raw value input? Or do we include interfaces kinda like we are with a regular form? Right. So when it comes to a date value to name something, the technical value is always gonna be that that what's the exact spec, the, ISO 8601. Is that the thing that you wanna be editing? I would argue, probably not. You probably wanna have, you know, the data interface that you have configured for that field, which then leads to the question, does that even fit wherever we're doing this inline editing presumably in this table layout. Right? And I think we're we're quickly reaching the question then is like, okay, do interfaces even fit? And if they don't, how do we present it otherwise? Right? Yeah. To your example, like, even even a simple slider, which is a nice way to input, like, a specific value inside of a range, which could be, you know, like, relevant to the use case, you know, where you want to restrict. You maybe you don't allow, like, negative numbers or something. But if if we allow, like, editing via raw values, then somebody might put in, you know, minus 1 or something, and then you very quickly run into the issue that you mentioned. We want to give people the tools to, like, input stuff as as as comfortably as possible with using, like, nice interfaces and displaying like, even displaying, you know, with with the value that you see inside of the table might, like, might not actually be the wrong value just like you described with the, like, date time, for example. So there's a disconnect there and, like, for, like, nontechnical users, for example. No. It could be it could feel bad, you know, from a user experience standpoint where they like if if marketing salespeople, what what was the thing that's, somebody said I won't name any names, but somebody said, like, the scariest thing that marketing people see is, like, a UUID somewhere. So we're dealing with UUIDs. Yeah. It is, is a scary endeavor, so you have to be quite careful with the display. So yeah. Yeah. Speaking to that, we have why don't we explain a little bit about the difference between our interfaces and displays? Because, you know, indirect is also 2 different things. What are those? How do I use those? What can they do? Good question. So so from I'll I'll prefixes was sort of the theoretical idea behind it while Jonathan here pulls up the actual difference and how you configure them. So the basic idea is that a display, controls how a raw value is displayed in line, importantly. So it's really meant to be part of a sentence or one of the cells in a table or, you know, part of a description or something. So if you think about a, labels display, it can be configured to render it as a small color dot with a tooltip, with the idea being you can then render that in the title of a page, or in the description of a card or inside of a table. Right? The interface is really the the way you edit the value. So if you go and you open an item, it's basically the form field, section of of how you interact with that data. So it's really the difference between sort of block level, in CSS terms, editing of the data versus inline displaying of that same data. Importantly, those 2 can be very different representations of the same data. Right? So the image display will just be a tiny thumbnail where an image interface is like a bigger box where you can drag and drop stuff in and you have, like, preview and some more metadata available. They are at present a different thing. And the display is then Yeah. So so Jonathan right now is, you know, adding, a related values you know, those displays, and compose them all together to really control how an individual item is presented. Right? So on the collection detail page 2, there is a display template setting where, again, you can render individual fields as part of one title string. And then that's how it displays the individual pieces of data. So when it comes to editing them in line, one of the interesting, challenges that we're gonna be facing when it comes to the table layout specifically is that the value that you're seeing in here, it's very likely that that's not the value that you're gonna be editing. Right? And is it a toggle? Is it something that you enable or disable? If you haven't enabled, what happens? If you don't have an enabled or how do I access the record? Because currently, you know, I just know that I can just click anywhere on the row, and it will open the item for me, and I can edit the item. How do we trigger, you know, you start to get into that. So we've got the whole interfacing aspect, relational content, allowed or not allowed. It feels like at least maybe you could almost do it in, like, an iterative. So start with the normal input fields, date fields, the the simpler interfaces and things to deal with versus relational. Relational gets complicated, right, depending on how deep or how complex that relational is. Now in order to edit this translation, well, that requires now that I get the full translational split toggle side by side translations kinds of things, and is that beneficial in this kind of a in this kind of utility? Yeah. Toggles, drop downs, you know, some of this, some of the more string text slash Is this is there there is a a another additional question when it comes to how do we then render an interface to change the data, which is that there's gonna be a couple of them where the interaction would be way nicer if it's, like, truly in line. Like, so if we, if you think about a toggle, like a checkbox. Right? Kinda like the default checkboxes for selecting items. But let's say you have, you know, an on or off state on your own custom item. In your display, ideally, I just wanna click it and it flips. I I don't wanna click it then open a dialogue that shows me just one toggle anyways, and then close it, and then change it that way. You know what I mean? And I think the same goes for basic text values or basic numbers where I just wanna be able to click the text, edit it, and then that's it. Right? I don't wanna have to deal with clicking it, seeing a popover, drop down dialog, whatever it may be, then change it there because it's just another step away for something that feels like it could have been a spreadsheet, basically. Yep. With that example, you know, with just clicking in and changing something, Tim also brought up a nice point because, with nullable values, like, even such a simple thing as a, like, togglable checkbox, if that is even a word, you know, we already run into, like, a small little, thing, like, okay, what if you want to set it to null? You know? Like, if you uncheck the thing, it's false, but you want it to be null. Do we do we, you know, do we, like, enable triple, like, stateful checkboxes that support 3 values? Do we want to have, like, a ellipse that gives you, like, a setting for that? Like, similar to our v form where, you know, you're allowed to enter a raw value, but that's another piece of UI that would have to be somewhere some at some point. Yeah. Exactly. For strings, similar in that vein, you know, for strings, do you want a, empty string, or do you want a null value? Alright? How do you differentiate between those? And it can even differ, you know, from from to Yeah. All of those are problems that we have solved in the form context. Right? Because even in the form, there's just the field label drop down that says empty this value, which is effectively nullifying it. So the question is also how much of that do we wanna pull a level up in here versus how much of that do we just say oh well if you explicitly wanna change a boolean back to a null, you can always go to the page detail thing and then do it there. Right? There is an escape hatch, and we could make the call to say, well, you know, the toggle display goes between true and false. And if you need to reset it back to that null state for whatever specific reason, you can always go to the detail page, and do it there. Right? And I think similarly for the string empty update, empty string, null, that that question is a question that we've currently solved on the interface settings, I believe. Therefore, an input interface, you can say empty string becomes null, and then that's just the value it, you know, stages, it emits. So if we go the route where we use the exact same interface you have already configured as the way to edit it in line, that problem should sort of automatically be solved. Right? Because we're using the same interface with the same settings, and then that should be fine as is. Where that part gets super interesting though, is how do we deal with conditional fields within the context of that row? So the way conditional fields currently work is, you know, you have the whole form of on the field, you have the whole form on the page, and then we can use the values of the form and sort of connect the dots. Right? And use it that way. On a row in a table though, the fields that are displayed might be completely different. The second piece to that is gonna be for conditional fields. They're now react reactive in real time where if you make a change to one field, sort of real life like, real time updates it elsewhere. When you're in the context of an individual row, and I think this is just a bigger question overall, is, like, does it auto save every time you sort of blur the input that you change something in? Or is there gonna be a save button that is like, okay. Now you've made all the changes and you hit save once. That's a very good question. Like, especially for, like, the togglable checkboxes or whatever because, like, if you just miss click somewhere and all of a sudden, that could be a big change, you know, like with flows and automations and other stuff that runs or update events, fire something. If you hit publish on something, that should be, there should be a mechanism, you know, to stop making mistakes, you know, like, accidentally, if you have, like, you know, is active or is published field on some type of blog post or whatever, and you don't want to accidentally just oops. Oops. It's live now. Oopsie. Would would be nice if we could avoid that, but, yeah, that opens up another can of worms. You know? Like, how do we do you configure this on a field level? Do you configure this on a column level, on a, preset level? Like, where where do you where would you like to do that? That's also the thing. Because very good point from the chat also from the, is currently, for example, the displays do not know, like, which item they belong to. They are very stupid in that way with quotation marks where they just Yeah. Display a value. This is why they're called displays. It's it's a presentation, presentation layer. Yeah. Yeah. Yeah. So if if we would like to have the, you know, like, mutable, displays or whatever, you know, if we decide to go that down that road. So, we would have to provide the displays with the item ID in the collection, like, in every place because there's not not only displays only in the table view, in the tabular layout, but also in, like, titles and drop downs or whatever. Like, any and they could be at any place at any time, which makes this a little bit, dangerously spaghettty like, which which which also, kind of bleeds into a similar yet different question, which is where do we allow inline editing in the first place? Because we're assuming we're sort of assuming the table layout right now because that's the most commonly seen elsewhere. I mean, I'm I'm sure that people that have ever seen Airtable are like, that's that's the way. But at the same time, the the question is, do we inline edit elsewhere? Like, if you have a status icon in your page title, does that become an inline editable thing? Right? In the chat somebody says we'd appreciate it for the card layout, maybe even the calendar. I would agree. So on the card layout, if you have in your description of an individual card, you have a a text display. If you click the description, can you now edit the description of a card in line, which opens up a big can of worms, especially from a UX design perspective. I I guess because because we're we're concatenating that into a string in the display right now for a card. Right? Mhmm. Mhmm. So if we're just The same for page titles for the for for that matter or the calendar layout. Yeah. Yeah. And Tim, Tim, another fun, fun little thing that could happen if you're already editing something and you have auto refresh enabled. Somebody updated your stuff in between the edits, and now you have another edge case. Love it. Love it. Love it. Love it. Which is gonna be the same issue that you have with, you know, the regular form. So I'm hopeful that whatever solution we come up with that right now. Yeah. Exactly. Whatever solution we come up with for that, I'm sure the same solution should also then work here. But Yeah. For this So this is a good example in the cars layout. Right? So in that right side bar, you see title, subtitle, and you can configure any combination of the different fields with any sort of additional strings in between, for like adding a dollar sign in front of price. That kind of stuff. Right? So in this particular case, it becomes extra interesting. Because if you now were to click on the number of the price in that description line, do you expect it to be editable? Right? And how do you determine that? Yes or no? Because making every editable globally at all times feels like a UX nightmare waiting to happen, but it also unlocks a lot of flexibility. Right? So it's a tricky one as many things are. Of course. So how about how about then we take a different look? So now we thought about tables and the current layout and whatever. Technically, there's also another option of adding a new layout, like you mentioned earlier, with the spreadsheet layout, which could be the point where you do that type of stuff. I don't know. Maybe, you know, I'm just, you know, throwing stuff in the room right now just to discuss a little bit. But Yeah. Because it does have the mute display thing. Yeah. Yeah. Yeah. So maybe if it turns out to be better or easier or whatever, the spreadsheet layout could be an interesting point where we could tackle this, but, then we have new, cans of worms and cans and of worms stacked upon each other, you know, till this evening. Stack a can. Which which is fun. Yeah. Because, you know, a spreadsheet layout people come into a spreadsheet layout with lots of preconceived notions. Is that the is that the word? And Like, I think expectations is the word I would say. Yeah. Expectations. Yeah. Yeah. Yeah. Yeah. Expectations and and stuff that they would like to have or that they expect to be there because it's, you know, spreadsheets are, daily used by billions of people. So they do want to have, like, conventions and keyboard navigation and different stuff that they already, you know, introduced to the day to day life with bulk renaming, whatever formulas. Maybe they think that this is then an actual spreadsheet that they can do calculations in, which is another cool feature that we would like to have probably from different feature requests for a different day. Yeah. Exactly. Exactly. Exactly. So spreadsheet seems to be a very big thing, for me personally. Yeah. I think to me it's just a very different thing, isn't it, than than this inline editing approach that we're talking about? Because I fully agree with what you're saying. If you close your eyes and you think spreadsheet, there's a whole set of rules that come with that. That if you miss one of them, you have a bad spreadsheet. Right? Because if you look at Excel versus Google Sheets versus Excel on the web or even Apple's, what they call it, numbers, they are all effectively identical in the exact same settings, exact same thing you can do. It's a right or wrong approach. So, funnily enough, for a session like this, it would be a very short call because it's like spreadsheet. It's basically a known entity at that point. Right? I think where it gets interesting to me, and this is sort of the the the Airtable spreadsheet equivalent. It's like, you're not dealing with the spreadsheets. You're dealing with a table that you can edit in line. But it's not a pure spreadsheet in the technical term, right, where you're dealing with cells with raw values, so to speak, rather than UI elements where you edit the values. Yeah. Same thing in the chat was was coined there. The reason why I immediately started thinking about okay. So our display is now editable as a concept is basically from that modular fashion. Right? Because making one new layout that does inline editing sure. We could totally make that happen. We could also make it an option of the regular table layout and then that's it. Right? To me the interesting question for this is really like, is there a way to make it work for any display anywhere? And is that something that we wanna do or not? Because by doing so, you get the tabular layout feature out of the box but you also unlock it elsewhere. Like, if you have, a list of one to many items on, a record, each of those one to many items now also has inline editing because they are using displays to render those values. Right? So it it just, like, it transforms this discussion from an inline table editor to a bigger sort of, like, what does that editing in situ look like across the board? Very, very good question. I wish I would have the answer to it, but, yeah, like you said, I mean, for for for example, for the, like, relational stuff, I could see, you know, reusing the existing drawer that pops up in different places, which could be a nice experience. You know, if you let's say you have a, a website collection with blog posts or something, articles, whatever. And as the drawer opens and you can use the currently already existing, like, relations go through the list, it it's it's with pagination and stuff. That could be nice. But like you said earlier, if for smaller fields, it feels like add UX if you, you know, just want to change one text and then the drawer opens or, like, a pop up opens with just one input. That's, like, at that point, it just feels very not thought through, which I would like to avoid. Yeah. We got correlate with this PR. Yeah. Yeah. Yeah. The the batch editing is also a nice thing. So slightly different. There's also the the, the bigger question here, Which is As it always is? As it always is. But it's that's that's what these sessions are. For those who are new, in these sessions, we always go find all of the edges and then take it back into what is actually realistic in the next step. But so think about it. If if we're now dealing so so you we explained it before that the displays were meant as a presentational only unit and interfaces are meant as an editable unit of the same value. Right? But now we're saying: What if displays are editable, but only at sometimes? So some displays like a string we wanna make inline editable, Some displays like a checkbox inline editable. And some like a relational related values or something we wanna have a click with some sort of dialogue to to have the space to make it a proper editing experience. Right? The question could also be, should those still be 2 separate entities or should they all be interfaces? And based on how we where we're rendering an interface, it can display in different ways. Right? So if you have an interface on a form where it has enough space to grow, it can show as a proper big input. But if you try to squash it into a cell of a table, it just drops the visual representation of an input and becomes one of those underline only in inputs for example. Right? Because then with a read only prop, yes or no, you can toggle between a string that's just rendered as a string. Or when you click it, it switches into this inline interface version, basically. Know what I mean? Because then for a display one, it can always just be interactive for an input, you can just click to start typing for, a related values one or a relation when you can click it and it would just do it in a pop over or dialogue or something along those lines. So depending on the interface type or the interface implementation, you could allow or disallow inline editing ongoing. Right? Right. It it would basically mean that the interface is always the thing that controls how it's displayed, because therefore you can have the interface control how that interactivity works in that display context. Now, that does, of course, come with the the side effect that you can no longer have a different display per interface like we currently allow. So theoretically, you could use, you know, a text input interface, and then an icon display if you wanted to. Or, you know, you can have a rating input that is a slider and then a display that's that's rendered as stars. I don't have the stats on how popular that is. Like, that would be a breaking change where we remove that. At the same time it also makes it easier to configure. Right? Because you can have, okay, I'm using the icon this, icon interface, and at smaller scales, it turns into an icon display. Right? So I guess another option could be that if you toggle the, you know, inline capability here, then the display gets turned off. Right? You don't have display capability. So you don't have a breaking change, you have the ability to come in and edit an existing interface, keep the existing display capabilities where I don't know what the overhead looks like for that, but something that we could think about instead of just outright ripping this out because I I still think, again, this is gonna be, I think, the most common case where I would want the ability to do, like, inline editing is on strings and numbers and dates. Right? Those are the, you know, the The the simple somebody in the the chat called it the simple stuff. Yeah. If I'm dealing with relational, you know, now you're dealing with complex, you know, you're you're just as easily popping into the item at that point to edit a relational setup potentially. Now down the line, that could change, but I think the most common things, you know, based on the ticket that we got inbound was around I just wanna be able to update some numbers, and I'll be able to quickly go through and adjust some numbers. I wanna adjust some text, and it's not a bulk edit because I'm not making them all the same, but I wanna be able to quickly edit things in line. Relational's I don't know. My brain my brain, I can see the nicety of it to be able to quickly just pop an interface and make a change without having to open the entire work, you know, in and out of a record to do that, but I think short term initial implementation. But if we added a toggle here or a function that says, you know, disable the display, if you go to inline, then you get the interface, and that is the display. Woah. Say say that again. So That last part, if you have a display but you use an interface and you toggle it, you now have a display. You lost me in that last part. No. No. So I'm saying that if we add a new if we add the feature for inline on the interface so if specific interface designed to support inline, and you just say, I wanna use the inline or I don't know where we would set that. I think it's set up based on the type. Yeah. Yeah. Yeah. Yeah. Yeah. I think somebody in the chat mentioned that it's too. It might have been the derp. Yeah. It gives you the, you know, if I make c one in here, it overrides display. Right? Display is essentially either disabled, right, becomes grayed out here, or it's just ignored, and with a note here that says if I check-in line, then boom display is no longer utilized. We're gonna utilize the interface spec for that. So so, you know, in the chat, mentioned a similar idea in that we could also have and this is a direct quote. We could also have multiple optional components for a single interface, like an inline version and a block version of the same interface. And then when the inline interface version exists, that can be used instead of the display, and then gray out the display to you your point. Yeah. Because the, this gets ignored. As long as there's a note or a warning, you know, even if someone does set it here, it's just ignored. If in line is enabled here, then it just gets ignored. Because the reason to then ignore the displays is because you kinda want the display itself to be editable at all times at that point. Like a checkbox, interface in the an inline checkbox interface would just be a toggle that's always active for you to click on. Because the comment that was just made in the chat too is like, you say you're loading a nested relational simple column in a table. You need simple editing. But in terms of toggling, you know, you can use the display by default. And then when you turn on an edit mode, maybe all of those in line ones are displayed so you still have displays. It gets tricky. So so the reason I'm saying that is we have things like the, formatted value display which is basically, a very configurable string. Right? You can make it bold, italic. You can make it, a different font if you wanted to. If you wanna go crazy with themes, you can make it Comic Sans. Would would highly recommend doing that. Ben's gonna kill me. Then, so those displays are and and you can have a text prefix. Right? That's where it gets a little tricky. So the display can show additional data that's not the, like, present in the value. So if we wanted to keep that level of flexibility, like, a rating display being stars. If you have an inline number input, now you kinda kill the usefulness of this rating display. Yeah. The the drawing drawing the line, what is considered simple, is also very hard. Like, for me, for example, like, a many to one relation, for example, I mean, that's basically a drop down. No. A drop down doesn't seem to me to be, like, a complicated change, so to speak, like, a complex update. Yeah. But from a user standpoint, not from the implementation detail standpoint, but from a, like, oh, I just want to change the user. That's like one, you know, one click, select the user, select another user, be done. That seems like a simple thing, technically, you know, but it's still a relational change. Yeah. Wait. What did the doer say? I mean, it's you kinda wanna what they're saying is you want the way that you're editing the value to to look the same after you're done editing it. So if you're looking at I think the obvious ones are, the toggle again. You wanna click it and you just edit it in the way it's presented. If you have a rating display, you wanna just click on the number of stars and that's the way you edit the value that you're seeing. When it comes to, like, a related values thing where it shows x number of items, it gets a little finicky. Right? If you're talking about the formatted value display where you might have, you know, a dollar sign prefix or something, Now it also gets tricky because now the moment you edit the value, it shows something completely different than you have displayed. Right? I think with dates, there's a different expectation, but if you think about the raw value underneath, it's just a string. It's an ISO 8601 string. So if you were to click a nicely formatted in your local language date, and you click it, and now all of a sudden it turns into a technical string, that'd be a bad experience. Right? Even though you could inline edit it as a string, which is not probably not what you want. But what's also funny about like the ISO strings is that, you know, often are like, usually they're stored in like UTC universal time. So, like, people Right. Yeah. In other time zones, might not even know what to actually put in there because, you know, they want to select, alright. This should happen every day at, you know, 8 in the morning or something like some some whatever date. And, then all all of a sudden, the UTC time is completely different. And then you're like, oh, no. This this field's not good. What is it? I don't know. And again, yeah, it it really depends on the time zone of the server itself of your database or whatever. And then I mean, at that point, it's it's game over. You really need to have some sort of proper data interface that handles that under the hood and then those presentational side of it. There is this is this is where the discussion is is interesting to me though, because it's like we're basically saying, do we have a small interface or do we have an editable display? And what's the difference? Right? Because when it comes to the date the date time specifically, the the basic of the date display is that it basically just takes the raw value and then shows it in whatever is appropriate for your locale. Right? So in the USA, it does the, objectively ridiculous thing. And I moved here by choice, mind you, of month slash day slash year. Right? And then elsewhere, it'll just do the proper thing of day, month, year. Right? Shot's fired. But I can imagine that if you have an inline editable date interface, you can just, you know, just like the system native one, basically, you can click on any of those parts and just type in what you want. Right? It doesn't have to be a whole selection, UI, and all that kind of stuff. So sorry for air format. Yeah. The the American people go to great, great, great lengths to not actually use some type of normal measurement. Like, how long is something? Yeah. I don't know. 2 2 and a half elbows or something. That's something how high how high is this building? I don't know. It's at 20 1,000 feet. I don't know how big is a feet. What? Oh, man. This derails very quickly though. Next session, we'll spend, it's a full hour on the Imperial system. Oh, no. Somebody's actually this relates to the discussion. Yeah. Great. Okay. Sure. Sure. Okay. Yeah. Yeah. Yeah. Yeah. This yeah. At least take a bit of context. Everything should be millimeters. He's Google. The Australians got it right there. There there's a good point in the chat though. Let's say you're storing a height value in millimeters or centimeters or meters or something, but then you wanna display it with a display, you know, in feet or inches or that kind of stuff. If you then inline edit it, what are you now inline editing? Because if it would be an interface where you're doing the value, you're now editing, I suppose, the millimeters, but you're showing it as foot. You know what I mean? Or as as inches. Oh, don't do it. Don't do it. Just do millimeters. Well, yeah, that's not always appropriate. We're we're not that dumb. We we figure it out. Mhmm. We're we're slowly being converted to metric by the by the global corporate infrastructure. We're getting there. I actually flip all my stuff. I use Metrc on everything now. Good boy. Good boy. From FCW comes in with the question, you know, what if we click on a cell, which is one of the displays, and then the field whatever that field's interface appears, just like it does in the form, and I could pop over and it will be prefocused or something. That is very close to basically where we started the discussion. So I'm glad that we're coming full circle, in in converging it back down. Because that would be, in terms of implementation, by far the simplest way for us to build this. Right? It's like, don't care about any of this stuff. You click it, you open a little pop over and then you change it and it's done. I also like simple things. Drop down, you get your whatever. You get effectively everything. Now there is. One major downside with that, that we've sort of hinted at, but not explicitly called out like this, Which is I believe that there's a lot of data types where you wanna edit it truly inline and not in a popover. So you have a checkbox, if you click it, you wanna have it change. You don't wanna have it open a popover with an inline toggle that you then click to change the other thing. I think when you have a small, text input Drop downs. Drop downs, same thing. Change the change the drop downs, same thing. Right? Both data entry gets frustrating in that model and I would agree. Like, I think for a drop down, you just wanna click it. It needs to show the drop down section. It'd be a bit of an annoying UX if it then opens a pop over with the same dropdown again that you then click to to have a nested popover down, I guess, which would also be a bit annoying. Very much agreed. I do think that for me, one of the requirements from a UX design perspective is that we have some sort of truly in line flavor for some of those, and then use the pop over for the interfaces where it doesn't make sense. Right? I like it. Picky stuff. But it's it's also hard to it's also difficult to decide which ones make sense and which ones don't. Because I'm thinking about our color display, which is just a tiny dot in its smallest form. If you click that, do we really have to show the full color interface? Because again, it's more like a a select. Right? It's more like a drop down. You could just click the tiny preview and then edit the drop down items directly. But how do we how do you control that, right? Which is, I think, what we're getting at is sort of the suggestion from the DIRF earlier, which kinda says, it's up to the interface to communicate somehow whether or not it is, inline editable. Yeah. So I think this is a good example, John. Jonathan. Exactly. It's like this whole drop down, we could just render that the moment you click on the little preview in line, right? It doesn't have to have this whole interface again because the only real thing that you care about for the interface is the part in the drop down, right? So from the chat, if interfaces have an inline variant those could be used inline and then fall back to popover if the interface doesn't have an inline variant. I think that is basically what I'm suggesting then. Yeah. Because then for the color in line variant, it can just render it, you know, nice and in line. For, a one to many interface where we know it's like there's there's no way to do the whole UI in a table row, it would use a pop over and just render whatever needs in there. I don't know though if interfaces should always have an inline variant, and it is then in charge of what goes in the popover because I can also imagine that the UX of how you wanna deal with an interface itself within the constraints of a pop over smaller window can be different. So if you think about the pagination for a one to many, for example, if you have that configured to, paginate at 50 and the individual rows of a once many are pretty big. You know, they're the regular size of an interface or, input height is what we call it. For a consistent view on the form, it looks nice. But if you cram that into a small popover, there's gonna be a lot of overflow scrolling now in that tiny little, in that tiny little pop over. We could go full modal. This is from the chat. Let me mind you. We could go full modal and make people suffer, which we could, but I I kinda to me, it's no longer in line editing then. Right? You just have a short cord to open a single field. But I could imagine that yeah. John, thanks for pulling this up. Like that left column section you're seeing there. I could imagine that the interface itself has an alternative rendering mode where it can just compress it down a hell of a lot more to be a better experience in a sort of pop over context. Popovers on mobile would be particularly a pain in the neck. Another one from the chat. Yeah. Yeah. Absolutely true. Which, in that case, you know, there is the opportunity to say for mobile, if we're rendering a popover, we're just rendering it as a dialogue or it might take over the full screen. There there's ways to responsibly handle that responsibly, responsibly. Okay. So just looking at the time, real quick since we are approaching the hour mark. I think yeah. But I I think we landed on a quite the nice, let's say, idea on how to tackle this because, you know, nothing is, you know, struck in stone, but I I do think I do think I like the approach of, you know, you can have interfaces, you can have displays, and you can have inline interfaces, which sounds very reasonable to me. But then, like you said, alright. Where do we control what pops in, what doesn't pop in? And I think it will have to be the interface, honestly. Because I could, an input interface, it knows that it could be handled in line. There's no way real way for us to to see that from the outside in. Right? I mean, we could always have, like, a lookup table somewhere, but it would only work for the stuff we the stuff we ship in core, not for extensions, which is a big part of this, of course. I I I think that the the question that I'm struggling with now is really what is the difference displays and interfaces in this paradigm. Right? Because now we're rendering interfaces in the place of a display, sometimes. And I'm I'm now just starting to think with this whole in line editing idea, maybe a display is just a read only inline version of the interface. It it does remove that flexibility that we talked about earlier where you could have a different display value for a input value. But to me, it would sort of solve for some of the complexity in configuration and some of these questions that when is it when is it a display? When is it an inline interface? How do you control between the 2? How do you toggle? Etcetera etcetera. Because that way we could say, well, in every interface needs an inline and a block version. The block version is what we use on forms and bigger pages. The inline version is what we use in title cards and table rows and that kinda stuff. And then it is read only or it's not. Like we currently already have. You know, we have read only forms, for permissions and all that kind of stuff. And a display is effectively just a read only inline version of the interface. And then if you have the editable table layout which is just a toggle. Maybe it's always on tbd. The inline version of the interface now controls if it's a pop over or not. So the inline version of a die, of a checkbox. Just click it. It immediately interactive. In line version of a text input. Click it. You can type in line version of a relational, one to many or something. By default, it'll just show 6 items. But then when you click it, the popover has the actual editable part. Right? But I think it could be the same the same interface. It's all interfaces at that point. Interface is all the way down. I mean, it would reduce it. So so one of the other parts is, like, there is a bit of confusion around the difference between interface and display. And I feel like a lot of people configure an interface by setting up a new field and then don't know that displays even exist. Cause if you configure a new field, it's very easy to skip over it and never know it's there. Right? So I also think there is a case to be made that it would improve the experience for a lot of the people that normally don't, really take the time to go over every single option that exists because it can be a bit overwhelming, you know, arguably. There's a lot of stuff you can configure. That sounds good to me. It'd be a gigantic rate and change, so don't worry about that. This is not something we can just in a little 10.1. Here you go. Bang. Looks good to me. Ship it. But I I think to me, that would sort of solve it because then for the table layout, you can have a toggle somewhere that says make it editable. Yes or no. For and for any of the layouts, really. For the tabular layout, we can make it editable by default. For the cards layout, we could say it's probably not a good idea to do it for some but for others because now the layout's in control. I think there's there's something to it. Very cool stuff. Good stuff, guys. Any last questions from the, audience? Your chance is now. Forever hold your peace. Winston. Final cue. Forever hold your peace. If you're if you're still awake. Yeah. Oh, let's see. Vincent. Someone type it. Well, then, thank you everybody. Thank you. Bored everybody to death. We've hard hard boiled a few eggs. Lovely. Thanks for tuning in. In case you enjoyed this, there's much more of this on directors.io\u002Ftv. If you want to go there, there's lots and lots of interesting shows for you. Lots of good, great entertainment. And, feel free to join us next time for another very interesting topic. Thank you for linking the show, Kevin. Oh, yeah. Fire emojis. Yes. Very good. Thank you everyone for tuning in. We hope you have a great day. Enjoy your week, folks. Have a good night. Good night. Strong feelings, please let us know. The discussion is open. The issues are open. That way.",[],[],{"year":978,"number":11,"id":1787,"episodes":3810,"show":3811},[1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1768,1800],{"title":1802,"slug":1803},{"id":3368,"slug":3813,"vimeo_id":3814,"description":3815,"tile":3816,"length":13,"resources":12,"people":12,"episode_number":213,"published":3817,"title":3818,"video_transcript_html":3819,"video_transcript_text":3820,"content":12,"seo":12,"status":19,"episode_people":3821,"recommendations":3822,"season":3823},"pair-programming","893667647","Time to quack open the world of pair programming.","e0d9436d-062a-4d02-9594-193ed5e3d9c2","2024-03-27","Pair Programming","\u003Cp>Speaker 0: And now, DevThoughts by Jack Rabbit. I once tried pair programming, but my partner was a rubber duck. It quacked under pressure, but on the upside, it never disagreed with my coding style.\u003C\u002Fp>","And now, DevThoughts by Jack Rabbit. I once tried pair programming, but my partner was a rubber duck. It quacked under pressure, but on the upside, it never disagreed with my coding style.",[],[],{"year":978,"number":263,"id":3362,"episodes":3824,"show":3825},[3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3350,3378],{"title":3380,"slug":3381},{"id":3369,"slug":3827,"vimeo_id":3828,"description":3829,"tile":3830,"length":13,"resources":12,"people":12,"episode_number":195,"published":3817,"title":3831,"video_transcript_html":3832,"video_transcript_text":3833,"content":12,"seo":12,"status":19,"episode_people":3834,"recommendations":3835,"season":3836},"computer-deli","893667751","Computers are the gateway to anything you want. Well almost...","c504e700-e6f4-475d-9940-453158292d54","Computer Deli","\u003Cp>Speaker 0: And now, Dev Thoughts by Jack Rabbit. When I first started coding, I was told that the computer does exactly what you tell it to do. So I told it to make me a sandwich. It compiled a list of delis nearby.\u003C\u002Fp>","And now, Dev Thoughts by Jack Rabbit. When I first started coding, I was told that the computer does exactly what you tell it to do. So I told it to make me a sandwich. It compiled a list of delis nearby.",[],[],{"year":978,"number":263,"id":3362,"episodes":3837,"show":3838},[3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3350,3378],{"title":3380,"slug":3381},{"id":3281,"slug":3840,"vimeo_id":3841,"description":3842,"tile":3843,"length":889,"resources":12,"people":3844,"episode_number":177,"published":3848,"title":3849,"video_transcript_html":3850,"video_transcript_text":3851,"content":12,"seo":12,"status":19,"episode_people":3852,"recommendations":3854,"season":3855},"salma","925117245","Salma is a live streamer, software engineer, and developer educator, and here's the software, hardware, and analog tools she uses in her day-to-day.","eb76850f-e5e1-4f25-8f61-a380eb809c43",[3845],{"name":3846,"url":3847},"Salma Alam-Naylor","https:\u002F\u002Fwhitep4nth3r.com\u002F","2024-03-26","What's In Your Dock, Salma?","\u003Cp>Speaker 0: Hi. My name's Salma. I'm a live streamer, software engineer, and developer educator, and this is what's in my dock. Firstly, my devices. I use an Apple M2 MacBook Pro as my main work machine.\u003C\u002Fp>\u003Cp>I use an iPhone 15 Pro Max, even though I don't update my phone that often, and I've got Apple Watch SE. I also use 4 HomePods to automate my life and home environment around me, and I use a custom PC build for live streaming. As well as writing code, my day to day involves a lot of live streaming and tech content creation. For live streaming on Twitch, I use a custom build PC and you can see all of the components that I built into that on my website. And I also use a lot of hardware peripherals.\u003C\u002Fp>\u003Cp>I'm fully in the Elgato ecosystem, so I use 2 Elgato KeyLite Airs, a Stream Deck XL, a WaveOne USB mic, a capture card to capture my Mac output to my PC when I'm streaming, and to the Cam Link 4 k. My camera is a Sony Zed V 1, and I use 2 other Logitech webcams for different camera angles that the chat on stream can trigger to view as well. I also recently got an Elgato prompter as well, which I'm using to talk to you right now, and I would highly recommend it. As for the hardware that I use when I write code, I love using my Mistel Barocco split keyboard. It's actually one of the rarer UK ISO layouts, and it's got some cherry milkshake weirdo key caps on it, which I love looking at.\u003C\u002Fp>\u003Cp>And I set it up with my Apple trackpad in the middle so I'm not moving my shoulders around all the time in the day. And the software I use when I'm live streaming is OBS. I pipe music in using a service called Pretzel Rocks. I use all of the Elgato software for the peripherals, so there's the control center, the Stream Deck software, etcetera, and the Wavelink software. I also use another tool called ATEM.\u003C\u002Fp>\u003Cp>It connects, Twitch to OBS and allows me to make things happen on my stream via Twitch channel point redemptions without me needing to write any code. But speaking of writing code, I do have a very large custom setup that I wrote in order to power all of the things that happen on my stream. It's a back end in express and node, connects to a front end built in react, even shares a types package if you want to get nerdy. And so everything I do is a combination of things that happen using Atum, my custom Twitch bot, and all of the software around it. And now for the software that I use in my day to day coding.\u003C\u002Fp>\u003Cp>I try to keep things as simple as possible and not overwhelm myself with tool fatigue. I'm saying that though, but the list I'm gonna read out is pretty, pretty long. However, I use Versus Code as my main coding IDE. I keep it really simple and I have like a custom setup where I hide the sidebars and all the different activity feeds and things, and I just want a blank canvas to be able to code on. I use iTerm2 as my terminal or the inbuilt terminal in Versus Code, and I try to manage as many of my dependency packages as possible on my machine using Homebrew.\u003C\u002Fp>\u003Cp>So I'll install node and anything else I need via Homebrew. The main browser I use is Arc. I recently converted to Arc last year. It kind of feels like an app rather than a browser, and that's what I like about it. It's built on Chromium.\u003C\u002Fp>\u003Cp>So if you're familiar with Chrome, it does everything that Chrome does with a few extra little details on top. And I make sure to use, the browsers like Safari and Firefox as well if I'm gonna test out what I'm building. I replaced Spotlight on a Mac with Raycast. I don't think I'm using it to its full potential, but I do have some pretty cool commands that I use to, like, get my clipboard history up if I miss click on command c and v, and that one's really helpful and it's just nice to look at. I currently use rectangle for window management.\u003C\u002Fp>\u003Cp>I actually recently switched from spectacle to rectangle last year. I'm still getting used to the key binds for rectangle because there are a lot, but I would recommend it. And on the subject of content creation, I found a really cool app that allows you to hide app icons in your Mac top toolbar when you're doing screen recordings for videos, and that's called vanilla. For day to day video editing, I either use ScreenFlow on my personal MacBook or I use Adobe Premiere Pro on my work MacBook. And one bit of software I couldn't live without is Meeting Bar.\u003C\u002Fp>\u003Cp>Now this sits at the top of my Mac screen toolbar, and it allows me to consolidate events across as many calendars as I need. And I manage 3 calendars in my day to day, and it's really helpful to give me, a quick glance at what's coming up next in my day. Finally, I recently switched from Trello to Notion for task management last year. Since I started using Notion at work, I began to get more familiar with it and how useful it is to have databases of tasks and other things. Like, I use it to manage my newsletter and everything I need to do for Twitch and all the miscellaneous things I need to do in my life.\u003C\u002Fp>\u003Cp>The music I listen to when I work actually really depends on my mood. Sometimes I like to put lo fi vibes on in the background. When I really need to work through a difficult problem, I actually like to put metal core on and, like, thrash around a little bit. Unfortunately, the new Spotify Daylist has kind of polluted my algorithm because in the morning when I'm lazy and just wanna want to put something on, I'll put the day list on and it's generally lo fi kind of atmospheric kind of stuff, which is great. But now that's all Spotify recommends me.\u003C\u002Fp>\u003Cp>So they need like a button to say, like, don't include this in my algorithm, please. I think one of the most interesting pieces of hardware that I use, especially for me because I didn't know this was a thing. And when I got it as a thing, I was like, why haven't I ever used this before? And it seems weird, but it is a light bar for my screen. I use the BenQ ScreenBar Halo because they were kind enough to send me one, and now I just couldn't live without it.\u003C\u002Fp>\u003Cp>It illuminates my space in the dark when, you know, in the UK it's very dark in the winter, isn't it? And, having this screen bar just gives me a little bit more joy in my life. I don't know what it is. It just gives me life, and I always notice if I forget to turn it on. So I like my hobbies to be as non tech based as possible.\u003C\u002Fp>\u003Cp>And my two main hobbies in my life right now are cross stitch and learning the drums. I first started doing cross stitch a year ago in January 2023, and I'm currently working on my second very big piece and I started learning the drums in September 2023 And I don't need to use any software or tools for my cross stitch, but one of the things that I love using for my drum practice is a piece of software called the amazing Slow Downer. Now this allows you to slow down or speed up music and backing tracks without affecting the quality of the audio. And whilst you can do the same thing with a digital audio workstation tools, such as Pro Tools and Logic and and whatnot, this is a really simple interface to allow you to scroll up and down for the speed of the track, and you can also change the pitch and things. You import music, and you're good to go.\u003C\u002Fp>\u003Cp>And it works on, desktop and other devices such as iPads and phones. I would highly recommend the amazing Slow Downer if you are learning anything that you can play to a backing track. The thing that's important to me for some reason to keep analog is my weekend to do list. I write all of the chores and random jobs on a scrap piece of paper in the kitchen. I put little check boxes next to each item and I tick them off gradually.\u003C\u002Fp>\u003Cp>My son and my husband are involved obviously in the weekend to do list as well, so they get to tick things off when they do things and we leave it in the kitchen. Sometimes it's stuck to the chalkboard thing we've got and it's just a nice break from doing everything digitally in the week. And sometimes it's just nice to keep things in your hand and and remember how to write with a pen, you know? So that's the hardware, software, and analog tools that I use to run my life. Find me on the Internet everywhere as white panther.\u003C\u002Fp>\u003Cp>That's white p 4nth 3r if you use a screen reader. Thanks for joining me, and, I'll see you somewhere.\u003C\u002Fp>","Hi. My name's Salma. I'm a live streamer, software engineer, and developer educator, and this is what's in my dock. Firstly, my devices. I use an Apple M2 MacBook Pro as my main work machine. I use an iPhone 15 Pro Max, even though I don't update my phone that often, and I've got Apple Watch SE. I also use 4 HomePods to automate my life and home environment around me, and I use a custom PC build for live streaming. As well as writing code, my day to day involves a lot of live streaming and tech content creation. For live streaming on Twitch, I use a custom build PC and you can see all of the components that I built into that on my website. And I also use a lot of hardware peripherals. I'm fully in the Elgato ecosystem, so I use 2 Elgato KeyLite Airs, a Stream Deck XL, a WaveOne USB mic, a capture card to capture my Mac output to my PC when I'm streaming, and to the Cam Link 4 k. My camera is a Sony Zed V 1, and I use 2 other Logitech webcams for different camera angles that the chat on stream can trigger to view as well. I also recently got an Elgato prompter as well, which I'm using to talk to you right now, and I would highly recommend it. As for the hardware that I use when I write code, I love using my Mistel Barocco split keyboard. It's actually one of the rarer UK ISO layouts, and it's got some cherry milkshake weirdo key caps on it, which I love looking at. And I set it up with my Apple trackpad in the middle so I'm not moving my shoulders around all the time in the day. And the software I use when I'm live streaming is OBS. I pipe music in using a service called Pretzel Rocks. I use all of the Elgato software for the peripherals, so there's the control center, the Stream Deck software, etcetera, and the Wavelink software. I also use another tool called ATEM. It connects, Twitch to OBS and allows me to make things happen on my stream via Twitch channel point redemptions without me needing to write any code. But speaking of writing code, I do have a very large custom setup that I wrote in order to power all of the things that happen on my stream. It's a back end in express and node, connects to a front end built in react, even shares a types package if you want to get nerdy. And so everything I do is a combination of things that happen using Atum, my custom Twitch bot, and all of the software around it. And now for the software that I use in my day to day coding. I try to keep things as simple as possible and not overwhelm myself with tool fatigue. I'm saying that though, but the list I'm gonna read out is pretty, pretty long. However, I use Versus Code as my main coding IDE. I keep it really simple and I have like a custom setup where I hide the sidebars and all the different activity feeds and things, and I just want a blank canvas to be able to code on. I use iTerm2 as my terminal or the inbuilt terminal in Versus Code, and I try to manage as many of my dependency packages as possible on my machine using Homebrew. So I'll install node and anything else I need via Homebrew. The main browser I use is Arc. I recently converted to Arc last year. It kind of feels like an app rather than a browser, and that's what I like about it. It's built on Chromium. So if you're familiar with Chrome, it does everything that Chrome does with a few extra little details on top. And I make sure to use, the browsers like Safari and Firefox as well if I'm gonna test out what I'm building. I replaced Spotlight on a Mac with Raycast. I don't think I'm using it to its full potential, but I do have some pretty cool commands that I use to, like, get my clipboard history up if I miss click on command c and v, and that one's really helpful and it's just nice to look at. I currently use rectangle for window management. I actually recently switched from spectacle to rectangle last year. I'm still getting used to the key binds for rectangle because there are a lot, but I would recommend it. And on the subject of content creation, I found a really cool app that allows you to hide app icons in your Mac top toolbar when you're doing screen recordings for videos, and that's called vanilla. For day to day video editing, I either use ScreenFlow on my personal MacBook or I use Adobe Premiere Pro on my work MacBook. And one bit of software I couldn't live without is Meeting Bar. Now this sits at the top of my Mac screen toolbar, and it allows me to consolidate events across as many calendars as I need. And I manage 3 calendars in my day to day, and it's really helpful to give me, a quick glance at what's coming up next in my day. Finally, I recently switched from Trello to Notion for task management last year. Since I started using Notion at work, I began to get more familiar with it and how useful it is to have databases of tasks and other things. Like, I use it to manage my newsletter and everything I need to do for Twitch and all the miscellaneous things I need to do in my life. The music I listen to when I work actually really depends on my mood. Sometimes I like to put lo fi vibes on in the background. When I really need to work through a difficult problem, I actually like to put metal core on and, like, thrash around a little bit. Unfortunately, the new Spotify Daylist has kind of polluted my algorithm because in the morning when I'm lazy and just wanna want to put something on, I'll put the day list on and it's generally lo fi kind of atmospheric kind of stuff, which is great. But now that's all Spotify recommends me. So they need like a button to say, like, don't include this in my algorithm, please. I think one of the most interesting pieces of hardware that I use, especially for me because I didn't know this was a thing. And when I got it as a thing, I was like, why haven't I ever used this before? And it seems weird, but it is a light bar for my screen. I use the BenQ ScreenBar Halo because they were kind enough to send me one, and now I just couldn't live without it. It illuminates my space in the dark when, you know, in the UK it's very dark in the winter, isn't it? And, having this screen bar just gives me a little bit more joy in my life. I don't know what it is. It just gives me life, and I always notice if I forget to turn it on. So I like my hobbies to be as non tech based as possible. And my two main hobbies in my life right now are cross stitch and learning the drums. I first started doing cross stitch a year ago in January 2023, and I'm currently working on my second very big piece and I started learning the drums in September 2023 And I don't need to use any software or tools for my cross stitch, but one of the things that I love using for my drum practice is a piece of software called the amazing Slow Downer. Now this allows you to slow down or speed up music and backing tracks without affecting the quality of the audio. And whilst you can do the same thing with a digital audio workstation tools, such as Pro Tools and Logic and and whatnot, this is a really simple interface to allow you to scroll up and down for the speed of the track, and you can also change the pitch and things. You import music, and you're good to go. And it works on, desktop and other devices such as iPads and phones. I would highly recommend the amazing Slow Downer if you are learning anything that you can play to a backing track. The thing that's important to me for some reason to keep analog is my weekend to do list. I write all of the chores and random jobs on a scrap piece of paper in the kitchen. I put little check boxes next to each item and I tick them off gradually. My son and my husband are involved obviously in the weekend to do list as well, so they get to tick things off when they do things and we leave it in the kitchen. Sometimes it's stuck to the chalkboard thing we've got and it's just a nice break from doing everything digitally in the week. And sometimes it's just nice to keep things in your hand and and remember how to write with a pen, you know? So that's the hardware, software, and analog tools that I use to run my life. Find me on the Internet everywhere as white panther. That's white p 4nth 3r if you use a screen reader. Thanks for joining me, and, I'll see you somewhere.",[3853],"01bd2813-d4ca-4651-82ca-52a94913e142",[],{"year":978,"number":3277,"id":3278,"episodes":3856,"show":3857},[3280,3281,3282,3283,3262],{"title":1027,"slug":1028},{"id":2748,"slug":3859,"vimeo_id":3860,"description":3861,"tile":3862,"length":129,"resources":12,"people":3863,"episode_number":13,"published":3865,"title":3866,"video_transcript_html":3867,"video_transcript_text":3868,"content":12,"seo":12,"status":19,"episode_people":3869,"recommendations":3871,"season":3872},"loreal-group-theme","926586698","Let's grab our keyboards and mice, and let the beauty adventure begin because YOU are worth it. Join Bry as he picks up his brushes to create a L’Oréal Paris theme in Directus.","f81c58ae-8850-4387-adac-e5791f7969a8",[3864],{"name":3760,"url":3591},"2024-03-25","L'Oreal (Because You’re Worth It) ","\u003Cp>Hi. Welcome back to another episode of The Joy of Theming. I'm your host, Bry Ross.\u003C\u002Fp>\n\u003Cp>And for the next few moments, you and I together will paint a beautiful Directus theme. I'm so excited to have you.\u003C\u002Fp>\n\u003Cp>Now if you've joined us before, you've probably already seen the tools of the trade, and I hope you've got your Directus Instance set up. If this is your first time, just sit back, relax, and follow along. You can set up that direct to sentence for the next one.\u003C\u002Fp>\n\u003Cp>Today, we are going to be painting a beautiful L'Oreal theme. Now I know this theme may look complicated at first glance and you may be thinking maybe this ability to theme, maybe you're just born with it, or maybe it's Maybelline.\u003C\u002Fp>\n\u003Cp>But deep down I know in my heart of hearts that each one of us can paint a beautiful, magical theme. You can do this and let's dive in. Now, what you can see, I've already got my Canvas covered in a great looking data model. It's a must for theming. You've got to paint that entire Canvas with some sample data before you can dive in.\u003C\u002Fp>\n\u003Cp>And the first thing we're going to do is L'Oreal is a beautiful name for a company, but they also have that accent aigu, the little accent character. So we're gonna scroll down to the bottom of this page and then we're going to set up our project settings inside our Directus instance. Alright. So I'm just gonna pick up my trusty keyboard here and quickly bang in that project name. Let's go ahead and get the project URL and a nice product project description.\u003C\u002Fp>\n\u003Cp>Really important for theming. We're gonna set the tone for this. We can see that being reflected up there at the very top, and then we're gonna dive into our actual theme. So now I'm gonna pick up this palette knife. And we're just gonna copy that L'Oreal logo.\u003C\u002Fp>\n\u003Cp>Just just spray it right there. Put that right in there. Then we'll go in and add a public foreground. Now the the public foreground, of course, shows on the login page, right above the public background. That's how those things work.\u003C\u002Fp>\n\u003Cp>Forward is front, back is back. Simple. Now, let's take this beautiful image of these 3 beautiful ladies. We'll put that as the public background and we'll add a quick little public note.\u003C\u002Fp>\n\u003Cp>This will be the L'Oreal CMS and, a PIM.\u003C\u002Fp>\n\u003Cp>PIM is just short it's the French version of product information management. Great. This is looking like a really beautiful theme. Now I wanna grab that favicon, favicon. I I never know how to pronounce this, but we'll just search for that within the console.\u003C\u002Fp>\n\u003Cp>Just copy that, pick up that keyboard and just paste that in there really quickly. So now we'll save and we can see, we've got our favicon. Things are looking really good. But our project color, it seems just a little bit off. If you look and you'll notice that L'Oreal has this really deep, rich midnight black.\u003C\u002Fp>\n\u003Cp>And basically the hex code for that is just 0. Now that's a wonderful color. We're gonna paint that in there just a little bit. But as we see, that that module bar or navigation is not reflecting that color. So we're gonna dive into our theme customization.\u003C\u002Fp>\n\u003Cp>We're gonna go to our globals. And then basically, where we have our little project primary color, We're gonna scroll down and, all right. So we'll go into our modules. We'll set our background to 0 and now we've got that rich black that we want in that that module bar. Alright, so now we also see that L'Oreal has this nude, this tan accent.\u003C\u002Fp>\n\u003Cp>A great color, great contrast to that midnight black. So what we're going to do is just add that as a secondary color and then we can also reference that using CSS variables inside the rest of our theme. What a beautiful, beautiful little theme. I'm gonna go into the list, into the icon foreground. And that is the the list for the navigation and add just a little darker beige, a little brown, beautiful brown is what we'll call it.\u003C\u002Fp>\n\u003Cp>And next I'm gonna pick up that that brand color. Let me go into the button background for the module bar and just add that. So now we've got these nice accents. We've got that deep black. This theme is, it's really coming together.\u003C\u002Fp>\n\u003Cp>So glad you could be here. Now we're also going to add a different active color so we can easily see where we're at within the direct to CMS. And, you know, next let's take a look at the border radius, the border radii. You'll notice that L'Oreal has a 0 pixel border radius. There's no rounded corners on this.\u003C\u002Fp>\n\u003Cp>It gives us a nice elegant vibe. If we look at the network request, we can see our font here is Campton. So I'm just going to copy the URL for that. We're gonna do a custom font face declaration. We'll just use at font face, wrap it in some brackets, and we're gonna call this the Campton font.\u003C\u002Fp>\n\u003Cp>Now we can add custom CSS inside any direct assistance and it's really easy. You just spread it on slowly. Not too thick. A very thin coat. And once you've added that custom CSS, I can go into my font variables, my definition for our fonts, and we're gonna reference that Campton font inside the top header bar.\u003C\u002Fp>\n\u003Cp>That looks nice. I really like that. Right? Beautiful. Now one of the other things that we we see on the website though is in the headers they use a, all caps.\u003C\u002Fp>\n\u003Cp>So we'll just go in, add another little touch of custom CSS. Just and text transform, uppercase. That looks good. Beautiful. Beautiful.\u003C\u002Fp>\n\u003Cp>I'm so glad you're here for this. Remember, anytime you're theming there are no mistakes, only happy accidents. So this is looking like an amazing theme. I I think we're pretty close to done here. So let's just log out.\u003C\u002Fp>\n\u003Cp>We'll take a look at the public login page. This is beautiful. That's it for this episode of the joy of theming. I hope to see you again.\u003C\u002Fp>\n\u003Cp>And remember, keep theming.&nbsp;Each one of us has that beautiful theme just waiting to come out.\u003C\u002Fp>","Hi. Welcome back to another episode of The Joy of Theming. I'm your host, Bry Ross. And for the next few moments, you and I together will paint a beautiful Directus theme. I'm so excited to have you. Now if you've joined us before, you've probably already seen the tools of the trade, and I hope you've got your Directus Instance set up. If this is your first time, just sit back, relax, and follow along. You can set up that direct to sentence for the next one. Today, we are going to be painting a beautiful Boreal theme. Now I know this theme may look complicated at first glance and you may be thinking maybe this ability to theme, maybe you're just born with it, or maybe it's Maybelline. But deep down I know in my heart of hearts that each one of us can paint a beautiful, magical theme. You can do this and let's dive in. Now, what you can see, I've already got my Canvas covered in a great looking data model. It's a must for theming. You've got to paint that entire Canvas with some sample data before you can dive in. And the first thing we're going to do is L'Oreal is a beautiful name for a company, but they also have that accent aigu, the little accent character. So we're gonna scroll down to the bottom of this page and then we're going to set up our project settings inside our Directus instance. Alright. So I'm just gonna pick up my trusty keyboard here and quickly bang in that project name. Let's go ahead and get the project URL and a nice product project description. Really important for theming. We're gonna set the tone for this. We can see that being reflected up there at the very top, and then we're gonna dive into our actual theme. So now I'm gonna pick up this palette knife. And we're just gonna copy that L'Oreal logo. Just just spray it right there. Put that right in there. Then we'll go in and add a public foreground. Now the the public foreground, of course, shows on the login page, right above the public background. That's how those things work. Forward is front, back is back. Simple. Now, let's take this beautiful image of these 3 beautiful ladies. We'll put that as the public background and we'll add a quick little public note. This will be the L'Oreal CMS and, a PIM. PIM is just short it's the French version of product information management. Great. This is looking like a really beautiful theme. Now I wanna grab that favicon, favicon. I I never know how to pronounce this, but we'll just search for that within the console. Just copy that, pick up that keyboard and just paste that in there really quickly. So now we'll save and we can see, we've got our favicon. Things are looking really good. But our project color, it seems just a little bit off. If you look and you'll notice that L'Oreal has this really deep, rich midnight black. And basically the hex code for that is just 0. Now that's a wonderful color. We're gonna paint that in there just a little bit. But as we see, that that module bar or navigation is not reflecting that color. So we're gonna dive into our theme customization. We're gonna go to our globals. And then basically, where we have our little project primary color, We're gonna scroll down and, all right. So we'll go into our modules. We'll set our background to 0 and now we've got that rich black that we want in that that module bar. Alright, so now we also see that L'Oreal has this nude, this tan accent. A great color, great contrast to that midnight black. So what we're going to do is just add that as a secondary color and then we can also reference that using CSS variables inside the rest of our theme. What a beautiful, beautiful little theme. I'm gonna go into the list, into the icon foreground. And that is the the list for the navigation and add just a little darker beige, a little brown, beautiful brown is what we'll call it. And next I'm gonna pick up that that brand color. Let me go into the button background for the module bar and just add that. So now we've got these nice accents. We've got that deep black. This theme is, it's really coming together. So glad you could be here. Now we're also going to add a different active color so we can easily see where we're at within the direct to CMS. And, you know, next let's take a look at the border radius, the border radii. You'll notice that L'Oreal has a 0 pixel border radius. There's no rounded corners on this. It gives us a nice elegant vibe. If we look at the network request, we can see our font here is Campton. So I'm just going to copy the URL for that. We're gonna do a custom font face declaration. We'll just use at font face, wrap it in some brackets, and we're gonna call this the Campton font. Now we can add custom CSS inside any direct assistance and it's really easy. You just spread it on slowly. Not too thick. A very thin coat. And once you've added that custom CSS, I can go into my font variables, my definition for our fonts, and we're gonna reference that Campton font inside the top header bar. That looks nice. I really like that. Right? Beautiful. Now one of the other things that we we see on the website though is in the headers they use a, all caps. So we'll just go in, add another little touch of custom CSS. Just and text transform, uppercase. That looks good. Beautiful. Beautiful. I'm so glad you're here for this. Remember, anytime you're theming there are no mistakes, only happy accidents. So this is looking like an amazing theme. I I think we're pretty close to done here. So let's just log out. We'll take a look at the public login page. This is beautiful. That's it for this episode of the joy of theming. I hope to see you again. And remember, keep theming. Each one of us has that beautiful theme just waiting to come out. We'll see you.",[3870],"b00e5c66-1e7c-49b9-ab37-9f6883783ce4",[],{"year":978,"number":2745,"id":2746,"episodes":3873,"show":3874},[2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2731],{"title":2760,"slug":2761},{"id":3604,"slug":3876,"vimeo_id":3877,"description":3878,"tile":3879,"length":159,"resources":12,"people":3880,"episode_number":177,"published":3882,"title":3883,"video_transcript_html":3884,"video_transcript_text":3885,"content":12,"seo":12,"status":19,"episode_people":3886,"recommendations":3888,"season":3889},"global-bookmarks","903063261","Learn how to create preset layouts, including filters and sorts, inside of Directus Explore.","50c8ecc4-7a85-4779-9d1c-c5d02bb5d56e",[3881],{"name":3590,"url":3591},"2024-03-22","Global Bookmarks","\u003Cp>Speaker 0: Hi. Welcome back to another episode of Short Hops. I'm your host, Brian Gillespie. And today I wanna talk to you about Global Bookmarks inside Directus. This is one of my favorite features.\u003C\u002Fp>\u003Cp>I say that a lot, but I I love this feature just because it is really great to preset some nice defaults for your nontechnical users when you onboard them into a Directus project. Your content editors, your marketers, anybody else that you're working with that has access to the Directus app will thank you, when you set up really nice global bookmarks for them. So right now you can see inside, this particular project, this is my Agency OS project, I have these bookmarks that are set up and these are set up as global bookmarks. So out of the gate, Directus allows you to customize any of these specific views to your liking. So I can go in and adjust the layout to be Kanban, table, calendar view.\u003C\u002Fp>\u003Cp>Within the table view, I can adjust the different widths. I can set up different filters, control the fields that I want to display. And for users who are new to Directus, it is a a really beautiful UI, but when they first log in, they may not have all their different views set up. Well, they're they're definitely not going to have that. So as a developer, as the technical person, as the lead on this project, do yourself a favor as well.\u003C\u002Fp>\u003Cp>Set up global bookmarks. So how do we do that? You know, if I click edit on this, I could see that it's a global bookmark. But if I go in and let's just say I bookmark a view, new bookmark. Great.\u003C\u002Fp>\u003Cp>We can see that is not the default. It is a personal bookmark. So to make these bookmarks global, what can I do? I go into my settings, I go to our bookmarks tab, and then I can look for the bookmark name that I've got set up. So that one that I just created called bookmark, but it could be a challenge.\u003C\u002Fp>\u003Cp>Right? Maybe I sort by ascending order. This is new bookmark. If I wanted to make this global, all I have to do is go into that specific one and change the scope. So I could change it to all users here and hit save and now that becomes a global bookmark that is available for any user that logs into Directus.\u003C\u002Fp>\u003Cp>And if I click edit, I can see that as well. But let's say I want to edit that in the future, I can also just go back to it and there are you have the ability to preset layouts. And here in this layout preview, you can even control, what fields are displayed and things like the column widths. So a lot of this power is available to you and not necessarily right in front of your face, so it's easy to overlook. So that's one of my favorite tips.\u003C\u002Fp>\u003Cp>I'll catch you on the next episode.\u003C\u002Fp>","Hi. Welcome back to another episode of Short Hops. I'm your host, Brian Gillespie. And today I wanna talk to you about Global Bookmarks inside Directus. This is one of my favorite features. I say that a lot, but I I love this feature just because it is really great to preset some nice defaults for your nontechnical users when you onboard them into a Directus project. Your content editors, your marketers, anybody else that you're working with that has access to the Directus app will thank you, when you set up really nice global bookmarks for them. So right now you can see inside, this particular project, this is my Agency OS project, I have these bookmarks that are set up and these are set up as global bookmarks. So out of the gate, Directus allows you to customize any of these specific views to your liking. So I can go in and adjust the layout to be Kanban, table, calendar view. Within the table view, I can adjust the different widths. I can set up different filters, control the fields that I want to display. And for users who are new to Directus, it is a a really beautiful UI, but when they first log in, they may not have all their different views set up. Well, they're they're definitely not going to have that. So as a developer, as the technical person, as the lead on this project, do yourself a favor as well. Set up global bookmarks. So how do we do that? You know, if I click edit on this, I could see that it's a global bookmark. But if I go in and let's just say I bookmark a view, new bookmark. Great. We can see that is not the default. It is a personal bookmark. So to make these bookmarks global, what can I do? I go into my settings, I go to our bookmarks tab, and then I can look for the bookmark name that I've got set up. So that one that I just created called bookmark, but it could be a challenge. Right? Maybe I sort by ascending order. This is new bookmark. If I wanted to make this global, all I have to do is go into that specific one and change the scope. So I could change it to all users here and hit save and now that becomes a global bookmark that is available for any user that logs into Directus. And if I click edit, I can see that as well. But let's say I want to edit that in the future, I can also just go back to it and there are you have the ability to preset layouts. And here in this layout preview, you can even control, what fields are displayed and things like the column widths. So a lot of this power is available to you and not necessarily right in front of your face, so it's easy to overlook. So that's one of my favorite tips. I'll catch you on the next episode.",[3887],"e0a22db4-5bcc-40ba-bc49-ce2d7a549ca7",[],{"year":978,"number":3600,"id":3601,"episodes":3890,"show":3891},[3603,3604,3605,3606,3583],{"title":2161,"slug":2162},{"id":3343,"slug":3893,"vimeo_id":3894,"description":3895,"tile":3896,"length":3897,"resources":12,"people":3898,"episode_number":13,"published":3901,"title":3902,"video_transcript_html":3903,"video_transcript_text":3904,"content":12,"seo":12,"status":19,"episode_people":3905,"recommendations":3908,"season":3909},"start","925179495","In the first episode of the season, Kevin and Andrew bring you on the journey of learning about Battlesnake by building an (arguably) capable autonomous snake based on Node.js. ","8b37f7c7-31a2-4f7f-9293-1b8955f77ddb",111,[3899,3900],{"name":2820,"url":2821},{"name":3330,"url":3331},"2024-03-21","Start","\u003Cp>Speaker 0: Hello, everyone, and welcome to Ready, Set, Battlesnake. I am so thrilled for this season to be happening, bringing together a few of my interests all at the same time, including hanging out with my good friend, Andrew. Hello, Andrew.\u003C\u002Fp>\u003Cp>Speaker 1: Hey, Kevin. How are you?\u003C\u002Fp>\u003Cp>Speaker 0: I am all the better for seeing you. Would you like to tell us what the hell we're gonna be doing for the next 4 episodes?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's a great question. So we are gonna be exploring the wonderful world of Battlesnake. More on that in just a minute. We're gonna be looking at how Battlesnake works.\u003C\u002Fp>\u003Cp>We're gonna be looking at how Battlesnake integrates with some other really cool technologies out there. And, hopefully, by the end of this 4 week series, you'll have a Battlesnake or 4 episode series. You'll have a Battlesnake that is ready to go and to jump in and start, yeah, start competing and playing around and learning some new technologies.\u003C\u002Fp>\u003Cp>Speaker 0: Or you can just watch us fumbling around. That's also totally fine too. Also very No. I think what might be good is let's talk about what Battlesnake is and then talk about, like, our affiliations with Battlesnake. Probably feels like a good thing to throw out there.\u003C\u002Fp>\u003Cp>But let's start because we've said the phrase Battlesnake one too many times to not jump into what it is. What is Battlesnake, Andrew?\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So if you have ever heard of the old classic game Snake, like that old Nokia snake game where you, sort of control the snake to go around on a screen and eat little berries and get longer, and you try to avoid yourself and avoid the walls and avoid other snakes if they're on the board. Well, Battlesnake is that same idea, but moved around a little bit into the developer space. So it's a really cool way for developers to explore new technologies, new frameworks in this kind of technology agnostic way. So you actually, create a web server that responds to the Battlesnake API, and you can crawl control a snake using code to battle or compete against other snakes in that kind of same gamified way, but, in kind of way more fun for developers.\u003C\u002Fp>\u003Cp>Speaker 0: And a few interesting notes here. So firstly, unlike snake on your Nokia, multiple snakes on the board at once, and you're basically trying to outlive everyone else. You die in the same ways, but there are multiple snakes just like this on the screen now. And because what you're doing is implementing an API, it means it's pretty technology agnostic as long as you can host a web server that can have a couple of specific endpoints that accept requests and then responds in a specific way. It almost doesn't matter what technology you use, which is pretty cool.\u003C\u002Fp>\u003Cp>And so we're going to be building this over a few episodes. Today, we're just gonna be focusing on, like, the vanilla Node. Js starter snake and challenges so we can introduce you to this wonderful world. You're gonna love it. And then we'll play around with, like, running Battlesnake in a director's project with directors automate, which should be really fun.\u003C\u002Fp>\u003Cp>And then, towards the end, we're going to also integrate dev cycle, which brings me on to a probably good point here, affiliations. Andrew, do you wanna start with, like, your history with this project?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Sure. So so I actually started with Battlesink back in 2020. So I joined fairly early on in in the the official Battlesink company. BattleState had been around for about 5 years before I joined the team, and it started out as a community project here on the West Coast of Canada.\u003C\u002Fp>\u003Cp>It was a big sort of hackathon event that happened here where a bunch of people have gotten to a room for a couple of days and built battle stakes and competed in sort of live events. I joined the team in 2020 as sort of their first community manager as we were kind of transitioning, as many were in 2020, into that online world. And so we sort of transitioned the whole kind of BattleState concept into an online platform that folks could engage with around the world. In 2023, Battlesnake, was acquired by another Canadian startup called Dev Cycle, where I currently work as their developer advocate. And so I still get to play around with Battlesnake a lot and work with a lot of the great people that were on the Battlesnake team.\u003C\u002Fp>\u003Cp>And and it's actually like the the pre dev cycle times that, Kevin, you and I met, even though technically, we never met in person until Not\u003C\u002Fp>\u003Cp>Speaker 0: in person yet. Mhmm. Absolutely. And then, I, Battlesnake as part of going online, run a bunch of livestream series. It was a really good is still, but was really then in its peak, like, peak pandemic.\u003C\u002Fp>\u003Cp>We're doing everything online, ton of live stream series, including the competitive leagues, shows which discuss different algorithms that can be implemented. And this one show called Coding Badly, which was run with me and my friend, Joe. And this is kind of a spiritual successor to Coding Badly, I think. No offense, Andrew. I think that's probably gonna be the vibe the vibe we go for.\u003C\u002Fp>\u003Cp>So I love Battlesnake. And what's really cool about and we'll discover throughout today is at the very core, you know, there are some very simple rules. Don't hit into walls, don't hit yourself, Don't hit other snakes. Don't run out of life like it. Get food when you need it.\u003C\u002Fp>\u003Cp>But once you start to get options, you can really make some choices here. You know, you can say if left and right are both viable options, is there an algorithmically, beneficial way based on certain strategies to go? Do I box myself off, but now we only have so much life? Do I try and find food as quickly as possible at all cost to also starve out other snakes and so on? So all of this is implemented in code, and then when the games actually happen, your hands off.\u003C\u002Fp>\u003Cp>You know, your your algorithm takes over and makes decisions. So, yeah, I think that's that I I think that's most of Battlesnake. Any other thoughts, Andrew, before we crack on?\u003C\u002Fp>\u003Cp>Speaker 1: I mean, let's, I mean, let's jump into the let's jump into the docs because I think they're gonna give us a good idea of kind of the some of the things that maybe we missed. And I think, actually, I'm I'm not a 100% sure. I haven't been in the docs in a while. I may still be featured in, like, the what the heck is Battlesnake doc. So I'm very interested to see if we\u003C\u002Fp>\u003Cp>Speaker 0: can get it. Shortly.\u003C\u002Fp>\u003Cp>Speaker 1: Am I there? The real question. I'm not oh, wait. No. Hold on.\u003C\u002Fp>\u003Cp>Quick start? There I am. I'm definitely I'm definitely in the quick start. I know this. Am I still here?\u003C\u002Fp>\u003Cp>Speaker 0: We'll we'll throw this video into the resources that accompany this video on Directus TV if you wanna go and watch it in full. Awesome. Yeah. So when you click on that,\u003C\u002Fp>\u003Cp>Speaker 1: what is dev cycle, it brings you over to the dev cycle documentation, and this documentation is fantastic. One of the things that Kevin was talking about, a lot of those sort of specific things that you can and can't do, if you wanna kinda figure out what that's all about, you can really a great place to go is actually checking out the game rules. And so this is where we start to look at things like this collision idea. So, like, self collisions and body collisions and head to head and who wins in these different situations, and then also giving information about some other things that are really important in this kind of gameplay. So you have a starting position in the game.\u003C\u002Fp>\u003Cp>We'll talk a little bit more about what that looks like kind of as we go through. You have health. That's a dynamic in this game as well. There's food spawns and hazards, and we're not going to get to a lot of that stuff necessarily today. We're gonna see that kind of as we go on.\u003C\u002Fp>\u003Cp>Yeah. But checking out this kind of game rule section is a great way to, like, get a a solid understanding of the idiosyncrasies, I guess, that exist in Battlesnake over sort of the, the the traditional snake game. The other great thing is this rules repository. So, like, all of these rules are actually in, an awesome GitHub repository called Battlesnake Rules. And so if you would rather not watch a video or if you'd rather just do, like, a different kind of deep dive, you can actually, like, jump into the rules repository, which is public, and you can go through and kinda discover a lot of these things yourself.\u003C\u002Fp>\u003Cp>It's all built out in Go. So if you're not a Go developer, then maybe not for you, but it's a useful place to kinda check out all of this written in code.\u003C\u002Fp>\u003Cp>Speaker 0: And, yeah, and that was gonna be my clarifying point. This isn't pros. This isn't written out rules. But this is codified rules that actually run within the Battlesnake engine. So you can get right in there and understand again the intricacies of how it all works, which is really cool.\u003C\u002Fp>\u003Cp>I remember possibly in a coding badly episode, definitely in a battlesnake stream in the past generating, battlesnakes that that, like, actively hook into this rule logic very directly, something like that. Okay. So how do we get started? I think we're gonna stick with node. Doesn't matter what language you you use in the real, but, you know, given that directives is all JavaScript view node, and that's at least what I know.\u003C\u002Fp>\u003Cp>That feels like a good place to start.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I'm with you. 100%. So couple places you can go. Obviously, the quick start that is here.\u003C\u002Fp>\u003Cp>So, basically, anywhere you go in the documentation, you'll find the quick start. This one is set up in Python. So, like, if you watch the video and you've never programmed in Python, it's still kind of valuable, and this will kind of walk you through the game logic. But it also does, in this kind of quick start area, let you choose from different snakes. So I highly recommend if you're just getting started with Battlesnake, click on that quick start, and you'll get the kind of fundamental understanding here.\u003C\u002Fp>\u003Cp>So when we say you are building a web server server that is kind of serving as your battle snake and it's it's interacting with the battle snake API, this is exactly what's actually happening here in this sort of representation here. So, the game engine is querying your web server where your battlesnake is, and your battlesnake server is returning a response. That response can only be 1 of 4 things, up, down, left, or right. That's right. Right.\u003C\u002Fp>\u003Cp>That you received from the game engine, and then you just are kinda repeating this process over and over and over again. So game engine is sending something to your server. Your server is sending something back to the game engine over and over and over again. And, I guess one of the the kind of gotchas here is, the web servers only have 500 milliseconds to respond. And so if you are sort of some people run these on Raspberry Pis and some people or some people run them on you still run them on Heroku servers.\u003C\u002Fp>\u003Cp>And so one of the things that we would tell people is make sure you go and, like, do something on your Heroku server or your\u003C\u002Fp>\u003Cp>Speaker 0: Just to see how the code stop at the beginning.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Exactly. But, but yeah. And and, Kevin, I don't know if I ever told you. Do you know what are the reasons why this 500 millisecond rule was implemented?\u003C\u002Fp>\u003Cp>Have you ever heard this story?\u003C\u002Fp>\u003Cp>Speaker 0: No. Sorry. I just realized. I don't think you can see me. So I I'm doing a No.\u003C\u002Fp>\u003Cp>Speaker 1: I can. No. No.\u003C\u002Fp>\u003Cp>Speaker 0: I can\u003C\u002Fp>\u003Cp>Speaker 1: see that. I'm, like, down here. No. I can't. So so I, so in one of the early kind of days of the battle sync competition, before they had this 500 millisecond, like, response time that was there, somebody realized that it was actually faster to respond with a physical keyboard or a game controller than it was to send code back.\u003C\u002Fp>\u003Cp>Love. And so somebody actually went and built a Battlesnake that was hand controlled, and then they would respond in real time. And it's interesting because I think they did fairly well. And at the end of it, it wasn't like, no. You did the wrong thing.\u003C\u002Fp>\u003Cp>It was like, great. You kind of you broke the rule, and now we or or or, like, defined the new rule for us. And so\u003C\u002Fp>\u003Cp>Speaker 0: That's funny.\u003C\u002Fp>\u003Cp>Speaker 1: I think you could technically still do it, but, you'd have to have some pretty fast thumbs.\u003C\u002Fp>\u003Cp>Speaker 0: In planning for this series, not to spoil it, but we have spoken about what it might mean to change strategy, like, mid game. You know, there are there are ways we can achieve this. It was not real time. That's really funny. And the interesting thing about that limit is it really forces you to be computationally efficient as you start to build more complex things.\u003C\u002Fp>\u003Cp>At the beginning, literally, it doesn't matter. You're as long as your server can physically respond in that time, you know, it it doesn't matter. But what you can do with things like at the beginning of a game, spin up some kind of machine learning or artificial intelligence resource, and then per move, you can call on it, but you have to do it very efficiently. So are we gonna start with a starter snake, or are we gonna start from 0?\u003C\u002Fp>\u003Cp>Speaker 1: No. No. I think we're I think I think let's start with a starter snake because that's what I think most people are gonna do here. Perfect. And so we'll go to kind of the first step.\u003C\u002Fp>\u003Cp>We're gonna choose our starter project. I'll just open this up in a new tab here. And what I kinda love here is, and I was talking to Kevin before the the show started, is like, so I'm running on a Chromebook right now. So if you're sort of a newer software developer, and you're, like, just getting exposed to this world, and you're, like, I don't have a device that can run this. If you can run a Chrome browser, you can run this.\u003C\u002Fp>\u003Cp>Speaker 0: Absolutely.\u003C\u002Fp>\u003Cp>Speaker 1: I mean, if you wanna have, like, a crazy setup, like, some people have, like, stand alone servers that run their state, You totally can, but you definitely do not need to do that to kinda get started with this.\u003C\u002Fp>\u003Cp>Speaker 0: No. Yeah. Not at all.\u003C\u002Fp>\u003Cp>Speaker 1: And so what's also great here too is so you can see these are the official starter stakes that are up on the screen. Yes. They are up on the screen. I'm gonna zoom in a little bit. I'm gonna try and zoom in a little bit.\u003C\u002Fp>\u003Cp>I'm so used to, like, the Mac keys, so I always click the I've used a Windows keyboard on a Mac device, so I'm always hitting the Windows key. Anyways, so You can\u003C\u002Fp>\u003Cp>Speaker 0: you can remap them. That's what I had to do. I have a German keyboard on my physical MacBook. I'll just remap the keys in it. Just a few keys on as they're printed, but you just get used to it.\u003C\u002Fp>\u003Cp>Oh, I like that.\u003C\u002Fp>\u003Cp>Speaker 1: I should do it. I feel like I've tried it before and it just didn't work. Anyways, not important. So, 5 starter snakes you can start with, which are, like, officially supported starter snakes. So Python, Go, Rust, TypeScript, and then the last one there, JavaScript is what we're gonna look at today.\u003C\u002Fp>\u003Cp>But if you're like, I really wanna do an f sharp snake, there is an f sharp snake for you there. And there's some really cool ones that have been defined in, like, some really, like, interesting languages and setups. Totally worth checking out. But today, we're gonna jump into the JavaScript official snake.\u003C\u002Fp>\u003Cp>Speaker 0: Lovely.\u003C\u002Fp>\u003Cp>Speaker 1: And what's awesome about these is so you can run these on your own infrastructure. So these are all templates that are set up on there. So you're welcome to run them on there. But we do have these set up ideally if everything works out still. I have not run one of these starter stakes in a while.\u003C\u002Fp>\u003Cp>Speaker 0: That makes sense. See if\u003C\u002Fp>\u003Cp>Speaker 1: everything works well, is you can just run on Replit. It's just a really simple Node. Js and express server, and, like, there's Docker files for external things, but everything runs right out of the box if everything goes according to plan, which it almost never does. So shall we click the button to show you what?\u003C\u002Fp>\u003Cp>Speaker 0: Why would you say that? Now it's all gonna go pear shaped.\u003C\u002Fp>\u003Cp>Speaker 1: I know. You say that. No. See, now it goes perfect. You gotta call it out.\u003C\u002Fp>\u003Cp>Right? You gotta call out to that\u003C\u002Fp>\u003Cp>Speaker 0: You gotta manifest it. And this is gonna be we're gonna nail it. There's not gonna be any any any errors. And I'll Well Battle Snake's gonna win the league.\u003C\u002Fp>\u003Cp>Speaker 1: What's that saying, though? It's like break a leg. Right? You say break a leg before they go on the stage because you're, like, saying the bad thing that might happen. I feel like it's the same thing here.\u003C\u002Fp>\u003Cp>If you just say, like, this is not gonna work at all, it works perfectly.\u003C\u002Fp>\u003Cp>Speaker 0: Allegedly. Let's find out. We're gonna see what happens.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So we're gonna click here. We'll see if I'm logged in. I have a feeling that I'm not.\u003C\u002Fp>\u003Cp>Speaker 0: You're not.\u003C\u002Fp>\u003Cp>Speaker 1: I'm not. But I don't think it really mat I think it'll still spin up if I'm not logged in. We'll find out.\u003C\u002Fp>\u003Cp>Speaker 0: We'll see\u003C\u002Fp>\u003Cp>Speaker 1: what happens. Nope. I have to log in. That's all good. We've follow look at that.\u003C\u002Fp>\u003Cp>I didn't even know we had followers right now.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. I'm gonna close you\u003C\u002Fp>\u003Cp>Speaker 1: because we don't need you right now. I'm gonna close you. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: And then when we when we get into REPL, it will bump the font size up significantly, I think. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: So, yeah, it's this easy. So we're literally just clicking on that fork. We're gonna do an auth zero sign in. Hopefully, we can skip past the stage where I'm gonna put my password. Let's go.\u003C\u002Fp>\u003Cp>I think Toby Flender snake is still on Replit 2, which is my which is my battle snake.\u003C\u002Fp>\u003Cp>Speaker 0: Oh. This is what this is what\u003C\u002Fp>\u003Cp>Speaker 1: I knew was gonna happen.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. But you're log but you're wait. You're not logged in. It it hasn't logged you in.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, weird. That's weird. Alright. Let's log in again. Let's go log in.\u003C\u002Fp>\u003Cp>Oh, I love it. All the errors, even at the authentication stage, we're running. This does not bode well. Oh, you know what it is? I think it's because I'm like, I need to auth with GitHub.\u003C\u002Fp>\u003Cp>That's probably what it is. I do have it on that email, but I'm not auth with GitHub. So let's go do that.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, I hate that sometimes when you log in with, like, an SSO provider, but your email address is the same with that SSO provider and you're like, I don't I don't know which which system. Did it work this time? There we go. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. So let's go. So we're in here, and I probably got a bunch of snakes in here. We'll go back over, and we'll jump into our starter projects again. We'll jump back into our JavaScript snake.\u003C\u002Fp>\u003Cp>And so I've logged in now. Don't need to worry about billing, but this should be a much more straightforward process. I love I said everything is gonna go incredibly terribly. And even at the auth stage, I definitely have jinxed this.\u003C\u002Fp>\u003Cp>Speaker 0: I'm not saying I told you so, but I'm certainly thinking it.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So here we go.\u003C\u002Fp>\u003Cp>Speaker 0: So this is the this is\u003C\u002Fp>\u003Cp>Speaker 1: the snake. I mean, let's get it in the template right now. Yeah. What do we wanna call this snake?\u003C\u002Fp>\u003Cp>Speaker 0: We're gonna call this when you call it something does does the dev cycle mascot have a name? His name's ToggleBot. So I don't know. Do we\u003C\u002Fp>\u003Cp>Speaker 1: call it, like, we could call it ToggleBot. We could also call it, like, coding badly rebirth. We could call it, we could call it, let's\u003C\u002Fp>\u003Cp>Speaker 0: call it let's call it ready, set, battlesnake.\u003C\u002Fp>\u003Cp>Speaker 1: Ready, set, battlesnake. Alright. Let's see.\u003C\u002Fp>\u003Cp>Speaker 0: I was worried you're gonna be, like, does the does the rabbit have a name? Does the director's rabbit have a name? As far as I'm aware, it doesn't. So set myself in a losing battle there. Let's use the template.\u003C\u002Fp>\u003Cp>Speaker 1: I'm very disappointed. To you.\u003C\u002Fp>\u003Cp>Speaker 0: Could be right. Could be right.\u003C\u002Fp>\u003Cp>Speaker 1: Here we go. So it's forking. If you've never used Replit before, by the way, amazing cloud based IDE, super powerful, integrates with all of the things that you're already using, but also has a lot of inbuilt features. If you've used things like so if you've used Gitpod, which is, or Codespaces, which are sort of in\u003C\u002Fp>\u003Cp>Speaker 0: the Code sandbox is another. Code sandbox.\u003C\u002Fp>\u003Cp>Speaker 1: Yes. Code sandbox. I find, somebody described it to me recently though that Replit is kind of like the Google Docs or, like, Google Suite of, like, cloud IDEs. It's just, like, very accessible. Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Bump your font size up. Let let's let's make that text more accessible because I can't read the damn thing.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. And I already broke stuff. There we go. Let's zoom in. Wonderful.\u003C\u002Fp>\u003Cp>Alright. So let's look at the files that we have here before we start messing around with anything. So it immediately brings us into our index dotjs file. The only other two things that we have in here, ignoring, like, our our package and all the rest\u003C\u002Fp>\u003Cp>Speaker 0: of that stuff,\u003C\u002Fp>\u003Cp>Speaker 1: are our readme, which is gonna kinda give you, like, things to do, which we're gonna be looking at in that sort of quick start. And then we've got our server dotjs, And this is basically, doing what we described earlier with those different responses that you can do to the Battlesnake API. So there is the just sort of, like, base level. And what what do you call that? Like, the standard with nothing else out of it?\u003C\u002Fp>\u003Cp>Speaker 0: The site.\u003C\u002Fp>\u003Cp>Speaker 1: There you go. No. No. I mean, like, the the actual just like a slash. Like, no additional\u003C\u002Fp>\u003Cp>Speaker 0: Oh, sorry. Sorry. You're referring to that. I would call that the root route.\u003C\u002Fp>\u003Cp>Speaker 1: Boom. Root route. So, like, your root route, and we'll talk about kind of what all these things do in a minute. And then you've got your start post route. You've got your move, your end, and then we've got a bunch of other little stuff here, including a console log that actually says your battle tank is running.\u003C\u002Fp>\u003Cp>Speaker 0: And if I may just kind of embellish this a little bit. So this main endpoint that we care about is this move endpoint. That is the endpoint that will receive board states and ultimately has to respond with a direction. You'll notice there are these other ones. So this route endpoint handler, route handler there, that will respond with information about your snake.\u003C\u002Fp>\u003Cp>We'll do that in due course. But the start and end are interesting. We mentioned earlier about provisioning resource. That's basically where you would do that. Right?\u003C\u002Fp>\u003Cp>Because I think you get a small grace period between the start endpoint being hit and the first time the move endpoint is hit in any given game. And then your snake can be entered into many games.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. Yep. Absolutely. And then yeah. There there's a bunch of stuff.\u003C\u002Fp>\u003Cp>We'll we'll kinda look at some of these and see what they do, but but but Kevin's absolutely right. Like, move is really what matters, and that's where you need to return sort of up down left or right. So the other thing just to look in here, especially for the snake, is just kinda looking at these, like, what you have in this basic package dot JSON file. There's really nothing there. This is a super\u003C\u002Fp>\u003Cp>Speaker 0: I think.\u003C\u002Fp>\u003Cp>Speaker 1: Super, super light template. And you can go crazy with this, but this is a very, very light template.\u003C\u002Fp>\u003Cp>Speaker 0: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. So, do we wanna do we wanna run this or should we look through first or should we head back over to the quick start? What do you think, Kevin?\u003C\u002Fp>\u003Cp>Speaker 0: I think I think we could do a little less with the quick start because you and I have both like, we should I think we should refer to it as and when it's needed. I say let's get the let's go to the Battlesnake platform. Let's take this basic snake, which I think what what if you scroll down this file, what does the move endpoint do at the moment? Oh, there is some there is some logic there. Yeah.\u003C\u002Fp>\u003Cp>We'll talk we'll talk a little bit about that, but it does do stuff. So let's let's set up this snake and get one game running so we can kinda get to grips with how that code becomes a battle snake in a game. I think that that feels about\u003C\u002Fp>\u003Cp>Speaker 1: Kevin, I have one question for you, though. What do you think I mean, Replen is trying to tell us to do this desperately, and we haven't done it yet. What is the thing that we need to do?\u003C\u002Fp>\u003Cp>Speaker 0: Oh, I wonder if there's one call to action on this page that is significantly brighter than the oh, okay. Alright. Yeah. There was no mystery. We're hitting run.\u003C\u002Fp>\u003Cp>Speaker 1: Awesome. And so this is the piece here. So this is the piece that tells you that your Battlesnake is running, is you're gonna see this API response here. So you're gonna see this API version, the author, color, head, tail. And then in your console, if it's running, you're gonna see this running battle snake at whatever the server, domain is and, or server address is and then info.\u003C\u002Fp>\u003Cp>Now because of the nature of Battlesnake, you need to have a public, URL that you can share with the platform. This is new. They used to all be public and has since shifted. So I think we need to deploy our app. No.\u003C\u002Fp>\u003Cp>We don't need to deploy our app. So we're gonna see what happens. We're gonna see if we can use just this. Yeah. Let's go to new tab.\u003C\u002Fp>\u003Cp>And then I yeah. So it'll be like this, and let me bump this up as well. We'll see how much it bumps up actually. There we go. So this is the same thing that we're seeing here.\u003C\u002Fp>\u003Cp>And then in our, in our navigation bar, we have this, like, replit.dev link, and so that's what we're gonna put over into the Battlesleep platform. It used to show up right here. I don't know why it doesn't anymore. I wonder if it should\u003C\u002Fp>\u003Cp>Speaker 0: I imagine I imagine it got a bit unwieldy as you build more complex URLs\u003C\u002Fp>\u003Cp>Speaker 1: by that. That's right. That must be it.\u003C\u002Fp>\u003Cp>Speaker 0: And that's probably more useful as a developer just to see the the path, but who knows?\u003C\u002Fp>\u003Cp>Speaker 1: You know what, though? I have a feeling with a lot of these platforms, it might also be. Yeah. You know what? So the what you can do is if you just, like, right click on this, like, 0.0.0.08 1,000, similar to other platforms, it'll actually convert it to whatever that public URL is.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, that's interesting. So it\u003C\u002Fp>\u003Cp>Speaker 1: can actually just copy that over there. Okay. So that's working. So let's head back over to play.battlesnake.com.\u003C\u002Fp>\u003Cp>Speaker 0: I'm so excited. Play. Play. Tailwind is a very nice little very nice little playground.\u003C\u002Fp>\u003Cp>Speaker 1: It is a very nice playground. This is like this Chromebook has become my central development, tool. And so, like, I have so many, like, interesting things on here. I never thought a Chromebook would be my daily driver. Alright.\u003C\u002Fp>\u003Cp>So over on the battle 6 site, auth with GitHub, highly recommend. Not that you need to, but, we're gonna auth there. Oh, look at that. Inclusive waffle. Good.\u003C\u002Fp>\u003Cp>Okay. So I'm logged in with my personal account. So here's what I love. I used to so I'm a teacher. If you didn't know that, what you\u003C\u002Fp>\u003Cp>Speaker 0: Oh, really? You have absolutely no mannerisms that would give that away. Like, what should we press on the screen now? Is it the green button?\u003C\u002Fp>\u003Cp>Speaker 1: But I I I introduced, my, like, middle schoolers to, to Battlesnake a couple of years ago. And, like, all their snakes are still in here, which I kind\u003C\u002Fp>\u003Cp>Speaker 0: of That's fun. Some of\u003C\u002Fp>\u003Cp>Speaker 1: these are That's Toby still mine, though, runs on node, on Node\u003C\u002Fp>\u003Cp>Speaker 0: RED. On Node RED.\u003C\u002Fp>\u003Cp>Speaker 1: That is a stellar platform, if you are looking at, having a stake. And actually, funny enough now now that I think about doing a directus snake\u003C\u002Fp>\u003Cp>Speaker 0: I was just about to say it. Yeah. Yeah. So so we have, directors automate as part of that. We have flows, which is a low code automation builder.\u003C\u002Fp>\u003Cp>And, yeah, if I whenever I actually describe it to developers, I ask if they've seen Node RED because I think it's very comparable. You know, you have automation builders that are very much like Zapier, and then you have ones which are more like Node RED. And this is more like that where you're wiring together these these these operations. But, yeah, we'll get to that.\u003C\u002Fp>\u003Cp>Speaker 1: I love it. Alright. So, we gotta add a snake in here. Now if you're new in your account, I think it prompts you where to add your new snake. I haven't done this in a little bit, so we're gonna see.\u003C\u002Fp>\u003Cp>I have a feeling\u003C\u002Fp>\u003Cp>Speaker 0: I actually need to go into my battlesnakes.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. We're gonna find out. There we go. And here we go. So we got all of our snakes that are currently in here.\u003C\u002Fp>\u003Cp>We're gonna create a new one. We're gonna call this one ready, set, Battlesnake. We're gonna drop our server URL in there that we copied over from do we still have it? Yeah. There it is.\u003C\u002Fp>\u003Cp>I think that's\u003C\u002Fp>\u003Cp>Speaker 0: And specifically without anything after the slash. We have to increment those, but that's correct. Yeah. So this is what\u003C\u002Fp>\u003Cp>Speaker 1: you're gonna see, something that looks a little bit like this. If you wanna try and copy this and use my snake, go nuts, or use our snake. And then the next thing you choose here is your engine region. And so I highly recommend that when you're getting started, you don't worry too much about which engine region that you're using. There's a good, like, kind of breakdown on why you would use a different engine region.\u003C\u002Fp>\u003Cp>I'm based in in in North America, so and I'm kinda closer to Oregon, so I'm gonna set the state there. Kevin might wanna set it up based in the Netherlands, but it really, really doesn't matter at\u003C\u002Fp>\u003Cp>Speaker 0: kind of the base level of Battlesnake. It it matters when you're squeezing out those milliseconds worth of worth of performance, but we're simply not.\u003C\u002Fp>\u003Cp>Speaker 1: Exactly. And then, again, description you can add, same with programming language. This is just fun. It gives it a little bit of flair. So you can add a JavaScript, and then we can say this is\u003C\u002Fp>\u003Cp>Speaker 0: Platform. We need to we need to do some work here. By the next time we record, we're gonna be doing some work to this list because we'll be we'll be moving we'll be moving our snake over.\u003C\u002Fp>\u003Cp>Speaker 1: I love it. Alright. So let's go. And what are we gonna find? What are we hosting on right now?\u003C\u002Fp>\u003Cp>We're hosting on Replit, so we should have Replit there. And what I love about this list too is you can actually see, like, here are all the different places that you could host a snake. Now some of these I highly would not recommend. Like, get out Codespaces. I don't think you can actually keep a long running server going.\u003C\u002Fp>\u003Cp>But Interesting.\u003C\u002Fp>\u003Cp>Speaker 0: No. Yeah. Yeah. You have to have the Codespace, and then they go to sleep.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. But lots of different cool options here. And then the other thing you can do is you can set it so that other people can interact with your snake. And so the upside here is that you can allow other people to kind of play around with it. The downside is you can have people that decide to challenge while you're using your snake for some other thing, and then you're having to deal with all the compute costs.\u003C\u002Fp>\u003Cp>And if you're running on, like, AWS or something that charges you for all of those compute hours, definitely challenging.\u003C\u002Fp>\u003Cp>Speaker 0: It's not just that. It's every request you get in relation to a single game has an ID of that game. But at basic levels, you're unlikely to be segmenting game logic. And if you're doing, you know, basic battle snakes, we'll just look at a single board and make a single response based on the heuristics of your code, the algorithm you create. But if you're doing something a little more clever way, you're looking over multiple turns, it screws you.\u003C\u002Fp>\u003Cp>But by that point, I suppose you are you are using that ID, but that's worth considering as well.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Absolutely. Okay. So let's jump in here. So we'll leave it as private.\u003C\u002Fp>\u003Cp>We'll save this battle snake. So you'll know once you save right away if your battle is actually there because it'll show up with the name, obviously, that you set it here. If you don't, though, you won't get any response here in this ping. So you might put, it used to be that if you started a snake on a service, it wouldn't automatically go. And so you might have to run this ping just to kinda see.\u003C\u002Fp>\u003Cp>And you can see here, if we do this ping, we were talking about that 500 millisecond response time. You can see that Replit is really up their, like, their timing. It used to be\u003C\u002Fp>\u003Cp>Speaker 0: They didn't. They didn't. That first response was a few hundred. So that must be like a cold start kind of like now it's middling around 40, 50, 60, which is great, but it was multiple 100 the first time you, the first time it was there before you hit ping again out of interest.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's true.\u003C\u002Fp>\u003Cp>Speaker 0: So But then that's what you're starting to see.\u003C\u002Fp>\u003Cp>Speaker 1: A good idea. Yeah. Yeah. Okay. So we've got we've got ready set battlesync in there.\u003C\u002Fp>\u003Cp>You'll also see you kinda get the flare that's related to the the language and and sort of hosting service that you're using. And so now, our Battlesnake is in there and is ready to go. So, do we wanna make our Battlesnake look good before we bring it into the game or do we wanna bring it into the game?\u003C\u002Fp>\u003Cp>Speaker 0: Let let's jump into it and maybe we'll see if the the Battlesnake elders allow this. We might see if we can do some custom customization during the the act of us recording 4 episodes. But for now, let's just leave our our snake a slightly boring gray. We can come back to it. When when we come and update the platform to direct us, that's where we'll do the customization.\u003C\u002Fp>\u003Cp>That'll be next time.\u003C\u002Fp>\u003Cp>Speaker 1: I love it. Alright. So, we're gonna go in here. You can choose. So, well, in the traditional Battlesnake game, or traditional snake game, I guess, it's kind of a standard map.\u003C\u002Fp>\u003Cp>No hazards. It's kind of just food pops up and things like that. So we're going to choose just the standard mode and a standard board size of 11 by 11. And we're just going to add ready, set, Battlesnake onto the board. We're not going to add any other snakes in there.\u003C\u002Fp>\u003Cp>You can add some bots. These bots are always running that have, like, some set sort of modes, but we're just looking to see if our snake is ready to go. So let's go ahead and see, how ready set Battlesnake runs. Alright. We're in.\u003C\u002Fp>\u003Cp>Okay. We're here. Should we have it now? You have to.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, yeah. Yeah. Come on. Let's do that. You ready?\u003C\u002Fp>\u003Cp>I'm so thrilled. No. I'm good. Alright. You ready?\u003C\u002Fp>\u003Cp>Yeah. Ready. Oh, no. No. We have a we we we get a catchphrase for this show.\u003C\u002Fp>\u003Cp>What? If Reggie said that Oh, my goodness.\u003C\u002Fp>\u003Cp>Speaker 1: Yes. Okay. Here we go. Okay. Of course.\u003C\u002Fp>\u003Cp>Alright. Let's go. Ready. Ready. Set.\u003C\u002Fp>\u003Cp>Battle snake.\u003C\u002Fp>\u003Cp>Speaker 0: That was thoroughly underwhelming. I'm gonna be honest. But but we are watching the snake. We got So this is what we got. So you see the ping?\u003C\u002Fp>\u003Cp>You saw the ping going on the right. So it was still around that 40 millisecond mark. There were 47 turns that happened before our snake died. Why did it die? And the nice thing about this editor, this player, of course, is you can step through your turns.\u003C\u002Fp>\u003Cp>So it was performing kind of a slightly eclectic set of moves, which is great. But what eventually happened to it? So it was at the bottom and it went off the board. It went down. So that was that's that's what our snake did.\u003C\u002Fp>\u003Cp>Now just to be clear, we'll look into the logic of our battle snake next, but we don't quite know why let's look at our code. And now we're at the point where we can start customizing this, this battle snake.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So we're gonna jump back over to Remplit. And so all of the things that you need to know for this snake when you're getting started are all gonna live here. And the other thing that's really cool here that I guess we didn't talk about earlier and maybe you didn't see here because we didn't go that far down in the code is we're actually returning into the console every move. So you can see why it moved or not why it moved in that direction, but you can see what happened.\u003C\u002Fp>\u003Cp>So this in this game start move 0, move 1, all of these commands that are happening are in here. But and this is sort of included in the base snake that you get here. And you can return lots of things in there. Like, you can return, like, what's leading into that. You can return logic.\u003C\u002Fp>\u003Cp>You can return things like shouts, which are just like little statements that are in your code. We kinda got rid of them. It used to be that you could do custom shouts that were displayed on the board of the games, which was very cool, but, they kinda disappeared. But, okay. Where where do we wanna start, Kevin?\u003C\u002Fp>\u003Cp>Speaker 0: Well, the nice thing about this starter snake for learning is it talks us through some steps. But before we do that, why don't we look at the code of what our battlesnake did? So it didn't just go in the same direction nonstop. Oh, that's another note. If your sync doesn't respond within 500 milliseconds or responds in an invalid way, it will continue to take the last direction There are a couple of variables here just to help us out.\u003C\u002Fp>\u003Cp>One that tells us where our head position is, and that'll give you an x y position coordinates and our neck, which is the second piece of your body. It's not all of them. It's just your head and your neck. There's a reason why your neck is important. We'll talk about that in a moment.\u003C\u002Fp>\u003Cp>And all we're doing here is oh, it's actually a lovely lovely and commented here. You can turn back into yourself. So if you're going straight up, you can go down, but you die immediately because you collide with your body. So all this logic is doing is saying don't hit the neck. Like, don't go back into the last space I was in, which is always the second position of the body.\u003C\u002Fp>\u003Cp>Now another thing that's interesting, if I may just kind of embellish kind of what's here, is we're starting to see the data structure play out a bit here. So this move function has an argument called game state. We see on lines 53 and 54 that inside of game state, there is an object called you. And inside of that, there is an array called body. And we can immediately assume that they're full of x, y coordinates.\u003C\u002Fp>\u003Cp>And in fact, that's that's made clear on line 56 where my neck and my head both have an x property. Before we continue, what might be interesting oh, I'm sorry. And so so the rest of the logic here is turn up, down, left, or right in that in that array, in that, object at the top, make them force if it means I'm gonna collide with my neck. Remove that as an option. And then I believe what it does is it basically, yeah, randomly picks 1 on line 87 and then returns the randomly selected valid move of which just doesn't collide with your neck.\u003C\u002Fp>\u003Cp>It does nothing else that's fancy. That's what we'll be doing today. Before we continue on this, can I make one recommendation that we do go back over to the docs and look at the game object so we understand what else we have at our disposal? Because I think it's important. Sorry.\u003C\u002Fp>\u003Cp>Not the get yeah. The game object. The board?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It's the board object.\u003C\u002Fp>\u003Cp>Speaker 0: The board. The board. Perfect. Bottom left is 0. That's we're calling that out because when you draw on screen, sometimes 0 is the top left.\u003C\u002Fp>\u003Cp>If you do math, it's in the bottom left. So just note, 0 zero bottom left. That one screws me every time. This is what the object looks like. Could you zoom in a couple of sets by any any chance?\u003C\u002Fp>\u003Cp>Cool. Yep. So we have a height and a width. That's the size of the board. That is obviously important to know.\u003C\u002Fp>\u003Cp>We have the position of all the food, the position of all the hazards, which we're not playing within the classic game style. But if you hit hazards, I does your health go down or do you die?\u003C\u002Fp>\u003Cp>Speaker 1: You die if you hit a hazard. Or well, depends on it depends on the game mode, but a lot of time you'll die.\u003C\u002Fp>\u003Cp>Speaker 0: Fine. And then you have an array of snakes. Each snake has the idea of the snake, but it also has look at that. That was that was good teamwork there. It also has its health, all of its body parts, a convenience, which is an object called the head, which is always the first option in the body.\u003C\u002Fp>\u003Cp>So that's just a convenience there. They're always the same. The length, any customizations. But really what we care about for logic realistically is the body array and the head as a convenience. But we could get most of what we need from the body and the board collectively.\u003C\u002Fp>\u003Cp>What else is there here? Rule set, the game itself, but I don't think we care too much about those in this very moment. Yeah. Yeah. And I\u003C\u002Fp>\u003Cp>Speaker 1: think the only other thing that's kind of in here that's, I mean, less it's less valuable. This is more like you you can see. Like, imagine that if you're in this game, your snake can see everything about the other snakes. So, you're also sending all this information about your snake to all other snakes on the board. So\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. You could stock out snakes that are low on health.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. And some people even decide, like, I don't wanna ever, I don't ever wanna attack a purple snake or a pink snake, and they'll just decide, like, I'm gonna avoid all of those snakes. Just and you have the ability to do like that. These weird kind of logic pieces, which I think is kinda fun.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Sure. Cool. So that is the shape of the object, which I think was important to dig into as we were starting to get a sense of what it looked like. So we know that we've got a board.\u003C\u002Fp>\u003Cp>We know we've got, we know we've got, sorry, a game. We've got boards. Boards have all the snakes and the board state itself. Snakes have a head and a body. So I think that's basically all we need to work through here.\u003C\u002Fp>\u003Cp>So all this snake does right now is avoid its neck. That is all it does. So Yep. What is the step one suggestion?\u003C\u002Fp>\u003Cp>Speaker 1: So step 1 is to prevent our Battlesnake from doing exactly what it did by moving out of bounds.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So this I mean, logically I mean, you and I have both done this kind of more simple battlesnake a a ton. But, you know, we will run through it kind of, you know, we come with a little bit more confidence in the approach, so we'll talk about it. But just know that in this little bit, there'll be a little less trial and error. Just so don't feel bad if if you're working through this and it's taking you a little longer.\u003C\u002Fp>\u003Cp>We just we just happen to know these first few steps like the back of our hand. So right here, there are a couple of comments that will go into that nested object and create a couple of variables for us, board width and board height. In this game, 11:11, but they can be smaller. They can be larger. And all we wanna do is say sorry.\u003C\u002Fp>\u003Cp>Go ahead.\u003C\u002Fp>\u003Cp>Speaker 1: No. I'm just gonna say, my eyes my eyes are burning right now. Do we see a do we see a dark mode?\u003C\u002Fp>\u003Cp>Speaker 0: It will exist. It has to exist. This is shocking. Our confidence. It it has to exist.\u003C\u002Fp>\u003Cp>Do you need to save save the snake or does it autosave or something?\u003C\u002Fp>\u003Cp>Speaker 1: No. No. So everything auto saves. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: I recognize it.\u003C\u002Fp>\u003Cp>Speaker 1: I'm just, like, I'm motivated for dark mode, but I also\u003C\u002Fp>\u003Cp>Speaker 0: Does it take your does it take your, like, theme of your\u003C\u002Fp>\u003Cp>Speaker 1: Oh, look at you. Look at you, Kevin. Look at you figuring out the smart thing. I bet you it does. That's the wrong one that I want to turn.\u003C\u002Fp>\u003Cp>Where's my dark mode? Mode? I wanna click\u003C\u002Fp>\u003Cp>Speaker 0: on it\u003C\u002Fp>\u003Cp>Speaker 1: here, and then I wanna set it on dark theme. And then I bet you if I refresh this, it's gonna be dark.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: I'm very confident that that's going to happen, but it may\u003C\u002Fp>\u003Cp>Speaker 0: not Oh, did I say I'm really confident? That's where it all falls apart. Yeah. We've already learned that that that's how it works.\u003C\u002Fp>\u003Cp>Speaker 1: Nope. Did not happen. Here we are. At least it's dark in some places. Is it\u003C\u002Fp>\u003Cp>Speaker 0: in your user settings?\u003C\u002Fp>\u003Cp>Speaker 1: It might be. It's odd that it's not actually, like\u003C\u002Fp>\u003Cp>Speaker 0: Like, here in the editor itself. Yeah. Maybe in your account, that you set up, like, your your default editor settings.\u003C\u002Fp>\u003Cp>Speaker 1: Let's see. Let's see. Billing, account, themes?\u003C\u002Fp>\u003Cp>Speaker 0: None of this. Themes. Themes. You know what? I wonder if that editor has, like, a command palette.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, how\u003C\u002Fp>\u003Cp>Speaker 0: do I do that? Boom.\u003C\u002Fp>\u003Cp>Speaker 1: Beautiful. Are you gonna catch it here? Do I gotta refresh again? Oh, you're gonna make me refresh again. Alright.\u003C\u002Fp>\u003Cp>I see you. I see you.\u003C\u002Fp>\u003Cp>Speaker 0: There we go.\u003C\u002Fp>\u003Cp>Speaker 1: Beautiful. Oh, look at that. We got some dark mode battle snake as well. But oh, actually, does this still exist? There used to be.\u003C\u002Fp>\u003Cp>Yeah. You can. Dark theme. There we go. Nice.\u003C\u002Fp>\u003Cp>Speaker 0: All dark Okay.\u003C\u002Fp>\u003Cp>Speaker 1: All the time.\u003C\u002Fp>\u003Cp>Speaker 0: So let's go back to our code editor then. Sometimes in dark mode, things are just slightly less legible. So maybe let's just do one more step up on the font size, if that's alright. Yep. Lovely.\u003C\u002Fp>\u003Cp>And right. So we've got the board width, the board height. So real realistically, this code is gonna run every single turn. We're gonna know the size of the board based on these values, and we're gonna know our own heads x y position. And all we wanna do is the very pragmatic, am I on the very far right of the board?\u003C\u002Fp>\u003Cp>Yeah. I can no longer go right, left, up, and down. So that's the logic. We kinda have logic that looks similar to this above, but we can write it from scratch. That's also cool.\u003C\u002Fp>\u003Cp>Take it away, mister McLean. No. We're gonna see.\u003C\u002Fp>\u003Cp>Speaker 1: So you say we're gonna write it from scratch. I, I fundamentally disagree with you, sir. I feel like if we're provided with this code, I feel like we should use it. Alright. Here we go.\u003C\u002Fp>\u003Cp>So here, we were looking if your neck was less than your head, then you didn't wanna move left. So we're gonna remove some of these comments, but it's gonna be the same kind of concept that we're doing here. And so let's look at our let's look at our head. So we wanna get our board width is gonna be important in a minute, but let's get our head. So my head x, and then we want our head x if it's less than or equal to or greater than or equal to.\u003C\u002Fp>\u003Cp>Speaker 0: Well, hang on. Fin finish the thought. What what direction are we doing here? Left?\u003C\u002Fp>\u003Cp>Speaker 1: Yes. So we're doing so we're doing x. So we're doing the x axis.\u003C\u002Fp>\u003Cp>Speaker 0: If it's\u003C\u002Fp>\u003Cp>Speaker 1: So we wanted to\u003C\u002Fp>\u003Cp>Speaker 0: 0. If it's 0, it's on if it's 0, it's on the far left of the board.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. So that's\u003C\u002Fp>\u003Cp>Speaker 0: all we need to check. If it's left, remove left trim. There is no minus one. You just die. So, double double equals double equals for for a comparison.\u003C\u002Fp>\u003Cp>Nice. So\u003C\u002Fp>\u003Cp>Speaker 1: that's our head x. So let's do our head y now. So if our head y equals our board height\u003C\u002Fp>\u003Cp>Speaker 0: So we're doing the top now. We're going clockwise. We're doing it. We're doing\u003C\u002Fp>\u003Cp>Speaker 1: the top now. But the thing here is because you're gonna get your board height, but then you need to be minus 1. So, like, your board\u003C\u002Fp>\u003Cp>Speaker 0: height because it's 0 indexed.\u003C\u002Fp>\u003Cp>Speaker 1: Exactly. So you're gonna do your board height minus 1. So this is basically saying if my oh, and not my neck. We want that to be my head.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, yeah.\u003C\u002Fp>\u003Cp>Speaker 1: So if my head y is equal to the board height minus 1, then we're gonna set up to be false. Yeah. Alright. So let's do our next one. So that means that we're not gonna go off to the left of the board too far.\u003C\u002Fp>\u003Cp>It also means we're not gonna go up too far, so let's keep going around. So let's do else if my head my head, and let's go x again, and let's go we'll figure out what the in between is gonna be, but let's do board width minus 1 and is going to be\u003C\u002Fp>\u003Cp>Speaker 0: It's it's the same. It's just a double equals. The I I literally cannot get behind you doing this clockwise. It's driving me nuts, but we're here now.\u003C\u002Fp>\u003Cp>Speaker 1: We're here now.\u003C\u002Fp>\u003Cp>Speaker 0: It's so funny. I would I would have gone left, right, up, down, but I think that's because that's, like, how CSS rules are structured. Left, right, up, down. But yeah. Isn't that yeah.\u003C\u002Fp>\u003Cp>That's why I was like, what are you doing? Yeah. So this last one is gonna be, down. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Yep. And so this one's gonna be my head y equals equals 0 and get rid of that. And this one's going to be, down is false, and then we actually need to change this one to right is false. And so this logic here, if you haven't been following along, is going to be if your head is equal to 0, you can no longer go left. Type of.\u003C\u002Fp>\u003Cp>Did I type\u003C\u002Fp>\u003Cp>Speaker 0: Hit backspace.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, right. Hit backspace. Is move safe right. Yes. So here, if your head is at x is at 0 sorry.\u003C\u002Fp>\u003Cp>Your head x is at 0, then you are not gonna be able to go left. If your head y is at the top of the board, you're not gonna be able to go up. If your head x is at the far right hand side of the board, you're not gonna be able to go right anymore. And if your head y is at 0, you're not gonna be able to go down anymore. Cool?\u003C\u002Fp>\u003Cp>Do we wanna should we comment this? We're not gonna comment.\u003C\u002Fp>\u003Cp>Speaker 0: Get rid\u003C\u002Fp>\u003Cp>Speaker 1: of those comments. Who needs comments? You've got a whole video to go along with this.\u003C\u002Fp>\u003Cp>Speaker 0: Exactly.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So let's, it should automatically update. I'm gonna just refresh here, and we'll see if it works. Normally, it does. It's pretty good in real time.\u003C\u002Fp>\u003Cp>So let's go and see what happens. Let's just create a new game and see if our solution sorted itself out. So all we have to do here is literally just create a rematch. It's gonna go and read in with our new Battlesnake data. We're gonna see what this looks like in dark mode.\u003C\u002Fp>\u003Cp>We may be changing back to light mode. It's almost too dark now. I feel like I need a like a beige mode.\u003C\u002Fp>\u003Cp>Speaker 0: Like a like a, what is it? Like a Solaris. Is that what it's called?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. There you go. Like a sepia mode.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Alright.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, this is for so long and my wife laughed\u003C\u002Fp>\u003Cp>Speaker 0: at me. We also called it Nokia. So in the background right now, all of these moves are happening in the background. If we hit play and we get up to the latest turn, it kinda just starts to slow down, and we only and we see moves in real time. So what we're kinda letting it do now is catch up.\u003C\u002Fp>\u003Cp>So when we hit play, we can just watch it all in one go, which is Yeah. Pretty nice. The other\u003C\u002Fp>\u003Cp>Speaker 1: great thing that you can do here is you can actually change the playback speed. So if you wanna watch it slower, you can if you wanna watch it faster. We're just gonna leave it as medium. There's also some other things you can do here. Like, as soon as the first turn is ready, you can auto play in these games.\u003C\u002Fp>\u003Cp>That's nice. You can also grab, like, a scrub bar so you can see how long the game is gonna last. It's really only good for you. If you're doing that in a competitive game, it's a little bit less fun. And then you can also show board coordinates.\u003C\u002Fp>\u003Cp>I actually think this one's interesting because I think this will show folks something interesting in the game. So let's go back here. So we got the board coordinates up there. As\u003C\u002Fp>\u003Cp>Speaker 0: you can see\u003C\u002Fp>\u003Cp>Speaker 1: here, 0011. So this is the stuff that we're talking about. Right? So when we said the board width, which is technically 11, even though it's 0 to 10, it's an 11 board. So if we'd left it at that and just said board width equals Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: The the the upper the upper right wouldn't have worked. It would have let it go off the yeah. Let it have died first. Let let's hit play so you can see what this thing does. So now it should avoid its neck, and it should avoid walls, which realistically means it will die by hitting itself or running out of food in theory.\u003C\u002Fp>\u003Cp>Yep.\u003C\u002Fp>\u003Cp>Speaker 1: And that's exactly what exactly what happened. Ran into its body. And so this is what we mean here. So, technically, the neck counts as part of the body as does the head, but we've defined the neck in our code. And so he's never gonna run into his neck, but it doesn't avoid him running into anything past his neck or or like Kevin said, running out of food.\u003C\u002Fp>\u003Cp>So, and you can see here, like, he's actually surviving a shorter period of time even though we have more code. But that's honestly I think you're absolutely right.\u003C\u002Fp>\u003Cp>Speaker 0: That first\u003C\u002Fp>\u003Cp>Speaker 1: time was a little bit\u003C\u002Fp>\u003Cp>Speaker 0: of a fix. Yeah. Absolutely. Oh, interesting. That should not have happened.\u003C\u002Fp>\u003Cp>Speaker 1: That should not have happened. Oh, I see. So head is technically at 0. It might have been because he's at a he's at there's no other safe moves, but that's not true. So what happened on that last move?\u003C\u002Fp>\u003Cp>Did he yeah. He went down. Interesting.\u003C\u002Fp>\u003Cp>Speaker 0: Let's let's just quickly check it again. When x is 0, if you head back to the code, when x is 0, remove left. When y is right at the top, hold height minus 1, you can't go up. When x is at the width minus 1, you can't go right. And then when y is at 0, you can't go down.\u003C\u002Fp>\u003Cp>Interesting. If or else if that Oh. Oh. No. No.\u003C\u002Fp>\u003Cp>No. I I I I I got it. This is in a a weird, like, else if else if else if. We we don't want this. We we we want each one to be its own individual statement.\u003C\u002Fp>\u003Cp>All of them need to be Oh, true. I love that.\u003C\u002Fp>\u003Cp>Speaker 1: If you\u003C\u002Fp>\u003Cp>Speaker 0: move the else's and and you change.\u003C\u002Fp>\u003Cp>Speaker 1: I love it. Alright.\u003C\u002Fp>\u003Cp>Speaker 0: It I'm not sure that's what the explains what just happened, but that was wrong. Like, it needs to be more like that.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. Let's see if that fixes things for us.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, timing out. Well, no. 42. Oh, that was an error. Let's scroll up.\u003C\u002Fp>\u003Cp>Let's see we'll see what happened. Just go all the way up to the top of the error. Alright. What's happening in here?\u003C\u002Fp>\u003Cp>Speaker 1: I need to zoom out a bit this a little bit. Just give me one second.\u003C\u002Fp>\u003Cp>Speaker 0: Board width is not defined. We made a typo. I think board width. Board? On line 70.\u003C\u002Fp>\u003Cp>There is on line 70. Oh, define the variables. Like, let put a let in in front of each one.\u003C\u002Fp>\u003Cp>Speaker 1: So what's interesting there is it means it was running our old code before. So that's that's part of the problem is it was actually running our old code. So now if we run this, k. Our battlesnake should be up. Let's do a game again.\u003C\u002Fp>\u003Cp>Speaker 0: And the moment you hit create rematch, we can go back to repl it, and we can see that things are happening. We saw that it made it 34 moves at least. That means all the moves have played out, so we can just hit play. It's looking promising.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. Yep. Same thing. And it\u003C\u002Fp>\u003Cp>Speaker 0: went into its body.\u003C\u002Fp>\u003Cp>Speaker 1: I wanna I wanna examine that because I think that's interesting. Right? We had that l tiff. Why why was that not working? Why why do we think that wasn't working?\u003C\u002Fp>\u003Cp>Like, where's the logical error in that?\u003C\u002Fp>\u003Cp>Speaker 0: Oh, if if you're if you're at the bottom and the right, it'll only run 1. It'll run the first statement it it gets, and then it will.\u003C\u002Fp>\u003Cp>Speaker 1: There we go.\u003C\u002Fp>\u003Cp>Speaker 0: So you need to you need to get you need to get each of them. So this is now we think working. We'll we'll keep an eye on this one. Let's assume about that, like, logically, this feels good. If it starts moving out of bounds where there are viable options available, we'll come back and revisit this.\u003C\u002Fp>\u003Cp>The next step is to get it to collide is to get it to not collide with your entire body. Right now, it's just avoiding the neck, which is the second piece next to the head. We wanna avoid all of the pieces. And once again, Andrew and I have created start to battle snakes dozens, if not hundreds of times over. So what what are we gonna be doing here?\u003C\u002Fp>\u003Cp>We're gonna basically be writing a loop. We're gonna go through every piece of the body, and we're going to see if it's if it's next to me just like it was a boundary, we're gonna remove it. So we're gonna actually end up using reasonable amount of the logic. The logic is gonna look quite similar to the one above, but inside of a loop. Great.\u003C\u002Fp>\u003Cp>And we don't we don't actually need to do, we don't even need to we don't need to care about the length so much because we can, we can just use a 4, a for of loop for this, which is a syntax, which I really like. So I'll introduce you to a for of loop. John, sure, you cannot you\u003C\u002Fp>\u003Cp>Speaker 1: know what we're gonna do. We're gonna do what every good programmer does, and then I'm gonna go to Google and look up how do I do a loop in JavaScript because I have legitimately You\u003C\u002Fp>\u003Cp>Speaker 0: got me. You got me.\u003C\u002Fp>\u003Cp>Speaker 1: You got me. I it it\u003C\u002Fp>\u003Cp>Speaker 0: auto filled. Hang on. Hang on. Head back to Replit. It was helping you out there.\u003C\u002Fp>\u003Cp>It was doing it was and also w three schools. So so if you wrote the word for first first of all, it did actually kind of something yeah. Look. It's kind of giving you a hint here. So this is a very vanilla JavaScript loop.\u003C\u002Fp>\u003Cp>What this is, there are 3 parts at the top. First thing it's saying is, wow, okay. That's cool, I suppose. So we're initializing the value I with 1. We're saying keep running this loop for as long as I is less than the the the body length.\u003C\u002Fp>\u003Cp>And at the end of every iteration before you start the new one up I by 1 and I is a is a number, which you can feed into an array to get an index. There's a nicer way of doing this though, in my opinion, for this use case. So I'm gonna propose comment this out because I wanna just quickly show you a follow-up loop because I just think it's a I think it's a really nifty syntax. So instead of this, we're gonna go 4, and we're gonna do the the brackets again, but not let it autofill, all of that. Let piece where whatever we want.\u003C\u002Fp>\u003Cp>We're gonna call it piece of my body. Exactly. You've got one too many, close brackets there. And then you do squiggly brackets, and we enter into the code block. Piece is an is a variable inside of this loop, which will change its value for each iteration.\u003C\u002Fp>\u003Cp>So I just think this is yeah. I mean, it if you if you hit tab now, it seems to have already, that code's wrong. I could ain't gonna do anything. I mean, we'll leave it there as, like, as like, you know, we'll base something on this. This is not right.\u003C\u002Fp>\u003Cp>This is saying if if my head is in the spot of this piece, which also includes my head, make everything foresight. This just won't work. It will mean no isn't a viable move and it will keep it will keep sending you know, it will send it, I think, an undefined and then we'll die. So this is wrong. But just before we jump into the logic here, so this is another way to write a a loop in JavaScript.\u003C\u002Fp>\u003Cp>And another way you can do it is called a for in loop where you change the word of for in, and then the value of a piece will be the index value. So what we could have done there, an exact copy of what was above is saying for let eye of my body. And then I will automatically increment each time. It will give you the, in sorry, in my body. And then I or whatever variable name you give it, will will be a will be a number that goes up.\u003C\u002Fp>\u003Cp>The way I remember the difference between of and in, of object in index.\u003C\u002Fp>\u003Cp>Speaker 1: I love that.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So, we'll we'll use the the for of loop. I think it's really nice because when you start to nest them, you can be really descriptive about what that value means inside of the loop. I think it looks nicer. It's a little more concise.\u003C\u002Fp>\u003Cp>But there you go. You learn about JavaScript loops. And there are obviously other loop types as well, but these are the more common 3. So what do we care about when we're looking at our body? Well, for every piece, what we wanna know is, is it one space away from us in any direction?\u003C\u002Fp>\u003Cp>That's all we wanna know. And if that is the case, we cannot move into that position. Even that strictly might not be enough because oh, no. It will be. That that will be enough.\u003C\u002Fp>\u003Cp>That is groovy. So what we wanna check is is our head equal to x? So is it on the same is it on the same x plane? Oh, no. No.\u003C\u002Fp>\u003Cp>Sorry. Inside of this initial if on 95. We're we're gonna alter 95 because 95 doesn't work. So we wanna say if it's on the x plane, but my head dot y is piece dot y minus 1, that means we are one space sorry. That means our body is one space below us.\u003C\u002Fp>\u003Cp>Down goes off the table. So this would be whatever the line is where we remove down. Game what's it called?\u003C\u002Fp>\u003Cp>Speaker 1: Yep. Yep. Let's go grab that.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It is move is move safe. So if it's 1 under us down, down goes off. So is move safe dot down equals force. On the same x plane, hang on a moment.\u003C\u002Fp>\u003Cp>But the y for on the same x we're gonna fiddle with these till they work. This this bit always gets me. I think I might have just done that wrong, but we'll find out. Oh,\u003C\u002Fp>\u003Cp>Speaker 1: it's part of the adventure.\u003C\u002Fp>\u003Cp>Speaker 0: So, Hannah, if we're on the same x and it's below us if we're on the same Do you\u003C\u002Fp>\u003Cp>Speaker 1: need to draw this out?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Maybe. Maybe.\u003C\u002Fp>\u003Cp>Speaker 1: Let's do it. Let's grab,\u003C\u002Fp>\u003Cp>Speaker 0: let's grab some It's bothering me. If we're if we're on the same x plane, but it's 1 below but then we're not on the same x plane out. But it's y. I wondered what tool you you were gonna grab there.\u003C\u002Fp>\u003Cp>Speaker 1: We'll see if this works. I might pull up some random drawing that I've created. I bet you I am gonna we're gonna see what magical drawing that I have up on the screen here as this loads.\u003C\u002Fp>\u003Cp>Speaker 0: What? Can you back a table in there? Because that's all we need is or a Google Sheet even, to be honest.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, that's a good idea. Let's do a Sheet sheet. Okay. Let's do, sheets.new.\u003C\u002Fp>\u003Cp>Speaker 0: Sheets.new.\u003C\u002Fp>\u003Cp>Speaker 1: Sheets.new? Yes. What's happening right now?\u003C\u002Fp>\u003Cp>Speaker 0: What is slides.new, docs.new, forms.new.\u003C\u002Fp>\u003Cp>Speaker 1: I love it. That's fantastic. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Right. Zoom in. Zoom in. Zoom in. Zoom in a ton.\u003C\u002Fp>\u003Cp>Let's go zoom in. Oh, no. Wrong one. Let's go zoom zoom zoom.\u003C\u002Fp>\u003Cp>Speaker 1: There we go.\u003C\u002Fp>\u003Cp>Speaker 0: And and let's just do, like, we don't need 11. Right? We could just do, like, a 4 by 4 for the sake of argument. Like or up to 5 by 5 if you go to Ian and you go down to 5. Alright.\u003C\u002Fp>\u003Cp>Speaker 1: And I do feel like this visualization is really helpful when you're doing this, like, genuinely. I think this is, like, a process that is useful for anybody to do if you're considering doing this.\u003C\u002Fp>\u003Cp>Speaker 0: So draw, like, a 3 parts draw, I don't know. Yeah. Let's just yeah. Yeah. Perfect.\u003C\u002Fp>\u003Cp>Right. So what we want is if you go if you go right one oh, what's happening? What what we're doing? Just give me one second. This is just a small one to\u003C\u002Fp>\u003Cp>Speaker 1: do this.\u003C\u002Fp>\u003Cp>Speaker 0: Hold on. Hold on.\u003C\u002Fp>\u003Cp>Speaker 1: There we go.\u003C\u002Fp>\u003Cp>Speaker 0: Great. Hang on. Hang on. I think I think we needed to do it. Do a little more than that.\u003C\u002Fp>\u003Cp>And I would I would leave them as x's so we can notionally talk about the head. So do do maybe, like, make it like a like a u shape. So also fill in b 5 and b 4.\u003C\u002Fp>\u003Cp>Speaker 1: I'm gonna you know what? I gotta stop with this x. The the color x is\u003C\u002Fp>\u003Cp>Speaker 0: I I I I yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, no. Actually, hold on. I could just literally copy and paste. Come on, Andrew. What are you doing?\u003C\u002Fp>\u003Cp>There we go. This\u003C\u002Fp>\u003Cp>Speaker 0: is how we want it. Let's assume that b 5 is gonna be the head. Right. So the first thing we wanna do is to say, let let's try and do the left position first. Yep.\u003C\u002Fp>\u003Cp>So the x is the same and the piece is on x minus 1. So that's that's the first one we're we're gonna do. So x is the same as my piece dot x, but but the my piece dot. Oh my god. What's going on?\u003C\u002Fp>\u003Cp>So this is gonna be Sorry. Sorry. Sorry. Sorry. Sorry.\u003C\u002Fp>\u003Cp>The y is the same. Yeah. Because we're all because we're on the same level. We're on the same height. That's what was getting me.\u003C\u002Fp>\u003Cp>So if the y if the y is the same, the x is minus 1, you remove left. So let's do that first.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So if the y is the same, x minus y?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Yeah. So so let let's do the first if the first half of the statement, let's change those for y's. So if the y is the same, but the x is piece x minus 1 remove left.\u003C\u002Fp>\u003Cp>Speaker 1: Love that.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. And and it's exactly the inverse for right. So x and sorry, y and y, perfect. We we replace those, but this time it's gonna be plus 1. That's all it was.\u003C\u002Fp>\u003Cp>We're we're just struggling struggling with that. So just to be clear, sorry, those first two still wanna be you need to basically inverse all of them. So y Correct.\u003C\u002Fp>\u003Cp>Speaker 1: There we go. And then we're gonna return off\u003C\u002Fp>\u003Cp>Speaker 0: that will be right. Yeah. And I I don't think we need I'm really glad we pulled out that tool, but I think we can basically invest them now. So copy and paste both of these. So next thing we're gonna do, not like that, is if the x is the same, therefore, the the column we're in is the same.\u003C\u002Fp>\u003Cp>That was it. But the y piece is minus 1, then it's under us. So we're gonna remove down. And, yeah. Exactly.\u003C\u002Fp>\u003Cp>Xxy y. There we go. That took us that took us a hot minute, but we got there. So that should now prevent us colliding with oh, hang on. Hang on.\u003C\u002Fp>\u003Cp>Why? Oh, no. It's fine. On that line 94 piece isn't, like, white, so I thought it was unused. Oh, no.\u003C\u002Fp>\u003Cp>It's\u003C\u002Fp>\u003Cp>Speaker 1: because I added an s in there. There was a there was an error there. Just because I'm doing the Windows key save.\u003C\u002Fp>\u003Cp>Speaker 0: No. No. No. Peace is declared but never used. I don't think that's right.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, there it is. It caught it.\u003C\u002Fp>\u003Cp>Speaker 0: There we are. There we go.\u003C\u002Fp>\u003Cp>Speaker 1: Cool. Oh, but we got a formatting issue? Hold on. So we can\u003C\u002Fp>\u003Cp>Speaker 0: No. It was a it was a formatting issue. No? Hold\u003C\u002Fp>\u003Cp>Speaker 1: on. Nope. We're still getting a bug here.\u003C\u002Fp>\u003Cp>Speaker 0: Someone watching this right now is screaming. They're like 100%. A 100%. I feel like I'm missing it. Scroll to the top of the arrow.\u003C\u002Fp>\u003Cp>The top of the arrow is gonna tell you tell you what it is, but we're not at the top of it yet. Keep going all the way up, all the way up, all the way up. More more more more. Oh, okay. Less.\u003C\u002Fp>\u003Cp>Speaker 1: We went too far.\u003C\u002Fp>\u003Cp>Speaker 0: We went too far.\u003C\u002Fp>\u003Cp>Speaker 1: It happens. Hold on. There we go. I just went too far into the first interpreter.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Board meeting No. No. We sorted that. That was before now.\u003C\u002Fp>\u003Cp>We did that.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, it's this one. Hold on. Hold the phone. Close u.\u003C\u002Fp>\u003Cp>Speaker 0: Wow. Capsule consoles.\u003C\u002Fp>\u003Cp>Speaker 1: So many consoles. Formatting failed with message. What?\u003C\u002Fp>\u003Cp>Speaker 0: And go have I missed something pretty obvious here?\u003C\u002Fp>\u003Cp>Speaker 1: No. I think I think that was the that was the issue for let piece of my body. Hey, bro. Yes. I think it was just that s in there.\u003C\u002Fp>\u003Cp>Let's try running it again.\u003C\u002Fp>\u003Cp>Speaker 0: You can do it. You can do it. I'm getting a hunch it can't do it without some intervention.\u003C\u002Fp>\u003Cp>Speaker 1: What have we done? Is it because we do need to declare this let on my body?\u003C\u002Fp>\u003Cp>Speaker 0: Oh, yeah. Maybe that that was the grape last time.\u003C\u002Fp>\u003Cp>Speaker 1: Kevin lied.\u003C\u002Fp>\u003Cp>Speaker 0: There you go.\u003C\u002Fp>\u003Cp>Speaker 1: You lied. You said I didn't have to let it.\u003C\u002Fp>\u003Cp>Speaker 0: No. I literally did earlier. That's what happened with board width.\u003C\u002Fp>\u003Cp>Speaker 1: I feel like it's it's a useful thing to blame other people for your coding mistakes. I feel like that is what it is the sign of a true a true professional.\u003C\u002Fp>\u003Cp>Speaker 0: The professionals we clearly are.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. As I as I went to Google to search how to\u003C\u002Fp>\u003Cp>Speaker 0: do a\u003C\u002Fp>\u003Cp>Speaker 1: loop in JavaScript.\u003C\u002Fp>\u003Cp>Speaker 0: What's what's happening right now? It's all taking a little long. Can you can you refresh the web view at the top?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Let's do that. I feel like that's k. We can close this out for now. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Let's see what's going on. I don't know. Yeah. I don't know what's happening. Maybe repllets happen a day.\u003C\u002Fp>\u003Cp>I mean, this is the thing. Right? It's like we are dealing with a cloud ID, so it does chug sometimes. But\u003C\u002Fp>\u003Cp>Speaker 0: Absolutely.\u003C\u002Fp>\u003Cp>Speaker 1: The REPL is booting. It's doing its thing.\u003C\u002Fp>\u003Cp>Speaker 0: It is. I will say as much as I love cloud IDs or cloud development environments as they're trying to brand themselves now, I use Gitpod for ages, I use Codespaces for a while, I have moved back to using a desktop editor for things like this. Like, why am I waiting for my editor to get its act together?\u003C\u002Fp>\u003Cp>Speaker 1: You're not wrong.\u003C\u002Fp>\u003Cp>Speaker 0: So so yeah.\u003C\u002Fp>\u003Cp>Speaker 1: There we go.\u003C\u002Fp>\u003Cp>Speaker 0: And the GitHub CLI makes it really, like, quite quite nice to to move between PRs as I'm reviewing stuff. So, alright, great. Let's rerun the game. So now it should not die from its own body. So in theory in theory, for a one snake battlesnake, I think it's gonna have a yeah.\u003C\u002Fp>\u003Cp>I think it's gonna have a decent life. Look at this. We broke a 100. There we go. This is amazing.\u003C\u002Fp>\u003Cp>Wow. This, okay, 170 moves. Let's see what ended up doing it in no. It could be health or it could be eventually, it had no viable options. You know?\u003C\u002Fp>\u003Cp>Let's see.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. So it's doing all the things. Not running into its tail now, which was entirely possible with our previous code. It's not getting bigger because it's not getting food. So now it's going in that circle because it's avoiding its body, and it's avoiding its tail, and it's avoiding its neck.\u003C\u002Fp>\u003Cp>And so I think he might starve out just because he's getting stuck in that circle.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, no. Poor buttersnake. Eat something. Look all that food around you, mate.\u003C\u002Fp>\u003Cp>Speaker 1: Dang. So, yeah, that's the interesting thing is, technically, he got into that loop where, like, technically, he was right, but we didn't say if you do this more than once, get out of it, which is a thing you can do. And what that, chicken or sorry. What that snake did is a is a a very famous move in the Battle State community called chicken snake. And chicken snake used to, like, go into a corner and would just chase its tail and wait for everybody else to die.\u003C\u002Fp>\u003Cp>And it would do really well.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And you you basically, you are you either other everyone has to die before you or you have to hope food spawns in in well, it it won't spawn where you are though, will it? It won't spawn in a position where a snake is. So actually\u003C\u002Fp>\u003Cp>Speaker 1: It can spawn, like, right where oh, you're actually not wrong. That's absolutely\u003C\u002Fp>\u003Cp>Speaker 0: right. Yeah. Interesting. Okay. Cool.\u003C\u002Fp>\u003Cp>So step 3 is a pretty, a pretty good evolution of what we just did there, which is up until now, we have a single battle snake on our board. But in reality, battle snakes will not be alone on the board. There will be at least one other snake on the on the board. And our job is not only to avoid ourself, but to avoid every snake and all of their body parts. Thankfully, we are effectively gonna get to get gonna get to, like, work on this work on this.\u003C\u002Fp>\u003Cp>And in fact, Andrew, I don't think we should copy and paste it. I think we should adapt this because our snake is in the snake's array as well. The might the me, whatever it's called, is again a convenience like the head to just give you the first item. So there's no point doing a loop over all our parts, then a loop over an array which includes us. We should just adapt this immediately.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Great. Let's do it. Alright. So let's go yeah.\u003C\u002Fp>\u003Cp>So we've already defined here. So we've got our opponents defined, and we're gonna do that same let again. Right? Like, we're gonna do our loop with around our opponents, or do we wanna remove ourselves from that first?\u003C\u002Fp>\u003Cp>Speaker 0: You know or I mean, this is I mean, it's up to you. Right? We can either, yes, remove ourselves from it, run the loop again because who cares? You know, it's just repeating logic effectively. Or we can adapt what is written above in that kind of 90 to a 110 line range to just be the loop.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, I see. Okay. Okay. So where where should we start?\u003C\u002Fp>\u003Cp>Speaker 0: Us. So we we know here based on line 116, which we should probably comment out because it doesn't have a let in front of it, but we know that game state dot board dot snakes is an array of all snakes including us. I believe we're always first. So let let's create that variable above this loop, first of all.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. Let's do that up here. I see. I see what you're saying. Now when you said adapt, I was like, is that not what I'm doing?\u003C\u002Fp>\u003Cp>Alright. So let's define that.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. We we don't need to build on top of what we've already done here. In theory in theory, we can also do a way above with, the avoid our neck logic that it came with because we're gonna avoid our neck by virtue of this. Let let's leave it for now.\u003C\u002Fp>\u003Cp>Let's do a little bit of cleanup at the end, I think. So now we have it's not even opponents. I don't even think that's the right name. It's snakes. These are the snakes, and it includes us.\u003C\u002Fp>\u003Cp>We have a loop that will go through our body and do the check. And, basically, all we wanna do here is write a full loop above it, like surround it in a further full loop, and we'll do the same thing. So we'll go let, snake singular because each iteration is going to be a single snake of snakes. We'll do the open squiggly bracket here on line 44, remove that exactly input precisely, and we'll put that outside the outside of the other one. And all we wanna do here is instead of my body, it will be snake dot body.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, Kevin. It's like you've done this many times before. That is just like It is. That is beautiful. So in my hearing and\u003C\u002Fp>\u003Cp>Speaker 0: the We we don't know it works yet.\u003C\u002Fp>\u003Cp>Speaker 1: No. But I like aesthetically, it's very pleasing.\u003C\u002Fp>\u003Cp>Speaker 0: Thank you. You're welcome.\u003C\u002Fp>\u003Cp>Speaker 1: Thank you.\u003C\u002Fp>\u003Cp>Speaker 0: So now in theory, we're running the same loop for all of for all the snakes. Right? Should we test it?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. Let's test. Alright. So let's I'm I was wrong.\u003C\u002Fp>\u003Cp>We do need to stop and start it. So let's see. This should work fine. Perfect. Okay.\u003C\u002Fp>\u003Cp>Let's head back over here. Gotta do a new game now, because we What? Are actually gonna add\u003C\u002Fp>\u003Cp>Speaker 0: another set. Of course. Of course. We may as well use one of these bot snakes.\u003C\u002Fp>\u003Cp>Speaker 1: I love that scared bot just disappears in this dark mode.\u003C\u002Fp>\u003Cp>Speaker 0: In dark mode.\u003C\u002Fp>\u003Cp>Speaker 1: Just not there. That's fine. It doesn't matter. We're gonna add him, and we're gonna add in ready, set, and battle snake.\u003C\u002Fp>\u003Cp>Speaker 0: Right.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. Let's add that in there. Alright. And so we've got a game running now. Now this is another thing about playing this is, like, you can see a lot of these things from your logs.\u003C\u002Fp>\u003Cp>So if you're ever playing BattleSync competitively and, like, there's a competition, like, it's almost like you have to close your logs. Otherwise, it gives too much away.\u003C\u002Fp>\u003Cp>Speaker 0: It's like\u003C\u002Fp>\u003Cp>Speaker 1: you know Woah.\u003C\u002Fp>\u003Cp>Speaker 0: What happened. The other the other nice thing about this is, of course, we get the whole board state every time. So as we start to integrate this with Directus, we can we can consume and store as much of this as we want. Let's, let let's play it. Yeah.\u003C\u002Fp>\u003Cp>Game over. 101 moves. Let's see what happens.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. So avoiding body, avoiding other snakes as well. Oh, I think I know\u003C\u002Fp>\u003Cp>Speaker 0: how this is. Yeah. I think I know how it's gonna end for this snake.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Me too.\u003C\u002Fp>\u003Cp>Speaker 0: Womp womp.\u003C\u002Fp>\u003Cp>Speaker 1: Womp womp. Womp womp.\u003C\u002Fp>\u003Cp>Speaker 0: And look how close the food is. Boom. Game over. So it can can we do a rematch? Can we get something hopefully a little more vibrant?\u003C\u002Fp>\u003Cp>Speaker 1: Let's do a rematch.\u003C\u002Fp>\u003Cp>Speaker 0: Let's see\u003C\u002Fp>\u003Cp>Speaker 1: what happens here. Yeah. And we'll play it just so that we can see it in real time. And and this way, you can actually see this What\u003C\u002Fp>\u003Cp>Speaker 0: is going on with our snake? Why is it happening? Our snake actually oh, there's an error with our snake because there was a viable direction there. It was to go right, and it did not. No safe moves detected.\u003C\u002Fp>\u003Cp>So that makes sense. So why was it no safe moves? So we have it set that it's not going to go is\u003C\u002Fp>\u003Cp>Speaker 1: that it's not gonna go no. Because he's been going up there. So she should just\u003C\u002Fp>\u003Cp>Speaker 0: the final move? He should have gone right.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. He should have gone right or\u003C\u002Fp>\u003Cp>Speaker 0: he should\u003C\u002Fp>\u003Cp>Speaker 1: have gone wrong. Loop.\u003C\u002Fp>\u003Cp>Speaker 0: So our logic is wrong. Let's take a look at that. But but and right was the only move. It couldn't go up based on our heuristics. Even though, in theory, to avoid our body, we can remove the tails.\u003C\u002Fp>\u003Cp>We can remove the final piece.\u003C\u002Fp>\u003Cp>Speaker 1: But what's interesting is he was doing that. Like, if you look at this, so he was here and he was going here. So, technically, when he was in this spot, he could have actually done this again. Right? Oh, yes.\u003C\u002Fp>\u003Cp>So he could have gone up or he could have gone right. Of course. Interesting. Okay. But let's go to our code and see why that didn't so why did\u003C\u002Fp>\u003Cp>Speaker 0: you decide? It's because\u003C\u002Fp>\u003Cp>Speaker 1: of the other snake. So it is it does have something to do with the fact that he was right next to a snake, and so he didn't think\u003C\u002Fp>\u003Cp>Speaker 0: what about oh, no. No. It's right. My head is still correct. And we wanna be checking the piece of every snake.\u003C\u002Fp>\u003Cp>Speaker 1: P sex minus 1, p sex plus 1. Yes. That's right. I feel like it's because we do actually need to bring this logic inside because we're running these this and then we're running through this loop.\u003C\u002Fp>\u003Cp>Speaker 0: It shouldn't make a difference, though. So? If it's set force once, it's we never set anything's true. We're never undoing the work we did. We're just kind of repeating it.\u003C\u002Fp>\u003Cp>You know? Yep. Okay. So I guess my\u003C\u002Fp>\u003Cp>Speaker 1: head is in there. We don't need the my body anymore. We're not referencing my body anymore.\u003C\u002Fp>\u003Cp>Speaker 0: Sninks does include us. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Well, let's let's figure that out.\u003C\u002Fp>\u003Cp>Speaker 0: I think Let's I think it does. I think you is the name of the the object as a convenience.\u003C\u002Fp>\u003Cp>Speaker 1: Snakes snake squad. No. That's not important to us.\u003C\u002Fp>\u003Cp>Speaker 0: No. That's the snake squad.\u003C\u002Fp>\u003Cp>Speaker 1: That and then board. So, yeah, you are getting snake 1, snake 2, snake 3.\u003C\u002Fp>\u003Cp>Speaker 0: Can we see the description? Snakes. All snakes on the game board, including yourself if you have not been eliminated.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. So it should be working. You're right.\u003C\u002Fp>\u003Cp>Speaker 0: So what is going on here?\u003C\u002Fp>\u003Cp>Speaker 1: So for snake of snakes, that's good.\u003C\u002Fp>\u003Cp>Speaker 0: Again, people watch it people watching this on director's TV are gonna be like, god. It's so obvious. This is the thing.\u003C\u002Fp>\u003Cp>Speaker 1: Well, let's hold on. Let's remove some some let's comment out some\u003C\u002Fp>\u003Cp>Speaker 0: I mean, sure. Sure. But, I mean, that's literally gonna\u003C\u002Fp>\u003Cp>Speaker 1: matter for anything. But okay. So if my head and let's go back to our let's go back to our sheet, drive.\u003C\u002Fp>\u003Cp>Speaker 0: 94% of storage used?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I should probably, go in here and, you\u003C\u002Fp>\u003Cp>Speaker 0: know Oh, not pay not pay more for it. Just act stuff. Yeah. That that's the other solution.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. So let's say we have let's let's actually reimagine the board state that we were in\u003C\u002Fp>\u003Cp>Speaker 0: I love that. Fine. What one step back? I if it's playing, what's going on here? Oh.\u003C\u002Fp>\u003Cp>And we just need to go one step back. We're at the end of the game.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. There we go. Alright. So let's bring this to we will actually bring this to a 10 by 10 board. And let's pull this over.\u003C\u002Fp>\u003Cp>Speaker 0: No. No. No. No. No.\u003C\u002Fp>\u003Cp>We we don't need the whole board. We only need, like, however wide. We only need, like, the cluster around around what we're doing. We we don't need to recreate the whole board.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So let's go\u003C\u002Fp>\u003Cp>Speaker 0: Hang on. I'll, on my side, I'll take a little a little cheeky screenshot, and I can I can call that out for you\u003C\u002Fp>\u003Cp>Speaker 1: if you want? K.\u003C\u002Fp>\u003Cp>Speaker 0: If you if you head if you head back.\u003C\u002Fp>\u003Cp>Speaker 1: Go back to the game. There we go.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So here's what we're doing. Let's go to our sheet. So we only need 1 like, we we can do it all within here, basically. So put our snake on, a 1b 1, a 2, and b 2.\u003C\u002Fp>\u003Cp>The head is b 2. And then, that that snake's gonna go, a 3. There. Yes. One more to the left.\u003C\u002Fp>\u003Cp>We can stop there. The rest is irrelevant. So that's that's what the snake is doing. And so first thing we're doing is we're avoiding ourselves. So we're saying can't go left.\u003C\u002Fp>\u003Cp>We can't go. The thing that's got me is why is it going right? That is I'm sorry. Why isn't it going right when that is a legal option? Yep.\u003C\u002Fp>\u003Cp>That's the question. The right logic is bust.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. So let's look let's pull this side by side. Let's grab this over here and let's go get this over here in our logic and see if we can make sure that our logic addresses this situation. Alright. So let's go here.\u003C\u002Fp>\u003Cp>Okay. So yeah, our first code here, my head, y.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: So my head, y, is currently at\u003C\u002Fp>\u003Cp>Speaker 0: On on b. Sure. Yeah. Right. On on 1.\u003C\u002Fp>\u003Cp>If it's 0 indexed in the bottom left. Yep. So we're both on 1. Sure. And the x, there is a piece one step down.\u003C\u002Fp>\u003Cp>Correct. Oh, hang on a moment. I think we've got our things muddled here. If my head y, but x is my so now that's correct. So that that accounts for a 2.\u003C\u002Fp>\u003Cp>Left is no longer viable. Yep. Going down 1, it's the same. We're on the same column, but right, this one is what's got me, but the piece dot x plus 1, but that is not the case. It's an it's an and statement.\u003C\u002Fp>\u003Cp>So, you know, both those things have to be true for it to be met. So if y is the same, but the my head dot x equals piece dot x plus 1, then it's on the right, but it isn't. I don't know what's going on here. My head okay.\u003C\u002Fp>\u003Cp>Speaker 1: Well, hold on. So this technically, these are x aligned, but they're y they're not equal for y.\u003C\u002Fp>\u003Cp>Speaker 0: They're they're they're y's match. They're wise match. They're on the same column. That means they're the same do you think I've just got the accent? No.\u003C\u002Fp>\u003Cp>Speaker 1: No. No. I think that my I'm trying to work this out in my head. So this is\u003C\u002Fp>\u003Cp>Speaker 0: Call that x1 or x. No. No. That's yes. Yes.\u003C\u002Fp>\u003Cp>No. You're right. You're right. You're right. You're right.\u003C\u002Fp>\u003Cp>Sorry. Yes. Oh, sorry. X1, x2, x3, x4, x5. Yeah.\u003C\u002Fp>\u003Cp>Y1y2y3y4.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So right now these are the same x, but they're different y's.\u003C\u002Fp>\u003Cp>Speaker 0: Well, the yeah. So if we hang on. Let's let's start at the top. So let's do the left first. So the x matches.\u003C\u002Fp>\u003Cp>We've got them flipped, Andrew. We flipped them.\u003C\u002Fp>\u003Cp>Speaker 1: Did we? Yeah. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Look, the y is the if the y is the same no, we haven't. We got it right. So if the y is the same, let's say let's take, b 2 and c 2. Right? Yep.\u003C\u002Fp>\u003Cp>The y is the same. Yep. So that they they exist in the same yeah. But the x is minus 1. That means underneath.\u003C\u002Fp>\u003Cp>No. That means To the to the left. Something on the left. Fine. Then y is the same once again.\u003C\u002Fp>\u003Cp>They're in the same row.\u003C\u002Fp>\u003Cp>Speaker 1: Are you\u003C\u002Fp>\u003Cp>Speaker 0: sure we haven't flipped them?\u003C\u002Fp>\u003Cp>Speaker 1: I I think we might have hold on. So okay. My head y, my piece and piece y are equal. So yeah. You're right.\u003C\u002Fp>\u003Cp>These 2 are technically there, and then my head x, so this is my head x, piece x minus 1. So, yeah, this removes left. That that totally makes sense. So we're good there. So next up, my head y equals piece y.\u003C\u002Fp>\u003Cp>Okay. Well, what would apply here, I guess? What would apply to this one right here?\u003C\u002Fp>\u003Cp>Speaker 0: It would have to be on the right. I think we got it right. I don't know what's going wrong.\u003C\u002Fp>\u003Cp>Speaker 1: My head x. Okay. So right now, this is they are the same for these are the same for x. So my head equals piece x. My head y equals piece y minus 1.\u003C\u002Fp>\u003Cp>Down is false. Yes. That that's right. That's good. My\u003C\u002Fp>\u003Cp>Speaker 0: head pieces We flipped them. We flipped them. And what's getting me is the 1 on 1 on line 107. So my head dot x means they are in the same it means they're in the same row.\u003C\u002Fp>\u003Cp>Speaker 1: Yes. It means\u003C\u002Fp>\u003Cp>Speaker 0: so it means they're in the same column. It means they're in the same column. Yeah. Right? They're in the same column, but the y piece is minus 1.\u003C\u002Fp>\u003Cp>They're in the same column, but the but the y is is 1 to the left. They're in the same column, but the y I I'm struggling to visualize that based on our head. They're on the same Okay. Hold on. So my head ached I think we've, I think we've muddled these.\u003C\u002Fp>\u003Cp>I think I think, yeah, these are wrong. And I know we we know this and the audience like, yeah, it's wrong. And here's exactly how, But, I don't think it's as simple as comparing the y's and then comparing the x's. I think only one piece wants to be I think the Right. So can you just put, like, an a in d two for us?\u003C\u002Fp>\u003Cp>So we're we're gonna test no. No. No. A d 2. So so so so we're gonna test h versus a.\u003C\u002Fp>\u003Cp>Right? So the y is the same. The the y the y is the same. The x is plus 1. The the y is the same and the x is plus 1.\u003C\u002Fp>\u003Cp>Yeah. Right gets removed.\u003C\u002Fp>\u003Cp>Speaker 1: So right yeah. Right should definitely\u003C\u002Fp>\u003Cp>Speaker 0: So but at least one of these is wrong because we're removing too many options. Yep. If if x is the same, so they're in the same column\u003C\u002Fp>\u003Cp>Speaker 1: So let's say y here. So these guys are in the same column.\u003C\u002Fp>\u003Cp>Speaker 0: But the y is minus 1.\u003C\u002Fp>\u003Cp>Speaker 1: Then we're gonna get that here.\u003C\u002Fp>\u003Cp>Speaker 0: Fine. Then the y's are the same, the x is plus 1, then we're going right. Dude, this looks correct to me.\u003C\u002Fp>\u003Cp>Speaker 1: It does\u003C\u002Fp>\u003Cp>Speaker 0: I don't know what's going on.\u003C\u002Fp>\u003Cp>Speaker 1: I okay. So I feel like this we've worked through this. I do think it must have something to do with this logic then. Have we screwed something up here? No.\u003C\u002Fp>\u003Cp>Speaker 0: I don't think so.\u003C\u002Fp>\u003Cp>Speaker 1: If my head equals 0, head x equals 0.\u003C\u002Fp>\u003Cp>Speaker 0: It's anywhere in that line. You can't go left. If it's at the board if y is at the height, you can't go up.\u003C\u002Fp>\u003Cp>Speaker 1: So here, this was at the height minus 1. So wait. What is height returning? Am I wrong? Have we changed the rules, and is height actually returning the height of the board and not 11?\u003C\u002Fp>\u003Cp>Oh, no. No. Returns 11. Oh. Oh.\u003C\u002Fp>\u003Cp>Wait. No. That\u003C\u002Fp>\u003Cp>Speaker 0: No. That's correct.\u003C\u002Fp>\u003Cp>Speaker 1: That's correct.\u003C\u002Fp>\u003Cp>Speaker 0: Board width minus 1.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So that's that's fine. That would be the one that would have mattered that it would have removed the right, and that logic makes sense.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: It's not there. Yeah. This logic is all correct. Let's run another game and see.\u003C\u002Fp>\u003Cp>Speaker 0: We are. I am full on at the point where I'm just gonna find a snake because I know I know it's less fun. I know it's less fun, but I just, but I also I also don't don't fancy doing this forever. Let's take a look.\u003C\u002Fp>\u003Cp>Speaker 1: K. I wanna see how this game plays out if something similar happens. K. Why did you run into his head? Why was that a safe move?\u003C\u002Fp>\u003Cp>Okay. What was the logic there? Did you have no safe moves again? No. So here, he thought he could go right.\u003C\u002Fp>\u003Cp>So oh, no. Because there. Okay. That makes sense. That tracks because he wasn't directly next.\u003C\u002Fp>\u003Cp>So there's no look ahead right now. So this takes\u003C\u002Fp>\u003Cp>Speaker 0: So that's so so that'll happen sometimes.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So that tracks. That that game worked the way it should have. There's no buggy things there. It worked the way it should.\u003C\u002Fp>\u003Cp>Let's see if we can get another game because I feel like maybe that is it. Like, our code is right and there was just some sort of a glitch, but I don't know. K. So he's going in the circle. He's going in the circle.\u003C\u002Fp>\u003Cp>So he's gonna starve out anyways. We're not gonna get to see much there. Anyone? That's so weird. Can we can we\u003C\u002Fp>\u003Cp>Speaker 0: run it again and again and again?\u003C\u002Fp>\u003Cp>Speaker 1: So he's not making the same mistake again? I don't\u003C\u002Fp>\u003Cp>Speaker 0: I don't like that it's just doing that. That feel it feels weird to me that it keeps chicken whatever you call it. Chicken Chicken snaking? Chicken snaking. That feels very odd to me that it does that consistently.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. So he's not doing this\u003C\u002Fp>\u003Cp>Speaker 0: as well. It isn't. Okay. Fine.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. And, again, so this ish situation, was that a safe move? So he's gotta finish the game.\u003C\u002Fp>\u003Cp>Speaker 0: Come on. Come on. Go back.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. So this does that make sense? Yeah. Again, this one makes sense because he didn't see right? So every one of these games, it's just the one game that was the one that was very strange.\u003C\u002Fp>\u003Cp>I think that there was some sort of a there was some sort of a an issue there with the actual way that it was reading our logic. I I hate seeing bugs that don't get resolved, but it seems like it's not happening again.\u003C\u002Fp>\u003Cp>Speaker 0: It seems to have been okay since.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Well, let's try again. Maybe Okay. So why did he do that? So he shouldn't have done that either.\u003C\u002Fp>\u003Cp>Okay. So here's the thing. So why did he do that? So right. Right.\u003C\u002Fp>\u003Cp>Right. Game over. So he should not have gone right there. Why did he go right? Why did he think right\u003C\u002Fp>\u003Cp>Speaker 0: Something's wrong. Was a safe move.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. So let's look at this again. And we're gonna have these guys here. So let's go, I'm actually gonna do color with these this time because I feel like that will actually help us. And then we'll do here and then we'll do him gray green\u003C\u002Fp>\u003Cp>Speaker 0: and gray, green, green, green. That's all. I'm literally this is driving me potty.\u003C\u002Fp>\u003Cp>Speaker 1: It's alright. This is the joy. I and, we'll fix it in post. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. We'll fix it. We'll fix it in post. Okay. Okay.\u003C\u002Fp>\u003Cp>Do do do do you want them to know what lovely editor, Nat? Let's jump cut here to the point where we fix it, and then we'll explain what went wrong.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Welcome. Welcome back. We worked it out. Andrew did a lot more like debugging, adding print statements. Andrew, do you want to show the code?\u003C\u002Fp>\u003Cp>And for those of you who are, let's zoom in like many, many, many times over, what was the issue? I'm yeah. But but what was the issue?\u003C\u002Fp>\u003Cp>Speaker 1: So the issue was we had things flipped with the Yes.\u003C\u002Fp>\u003Cp>Speaker 0: We did.\u003C\u002Fp>\u003Cp>Speaker 1: Actual thing that we were removing. And the way that we discovered that, and this is kind of an interesting way to do it. I actually, like, find this as a really good way to do any debugging\u003C\u002Fp>\u003Cp>Speaker 0: because I\u003C\u002Fp>\u003Cp>Speaker 1: threw a console log into each one of these pieces with whatever we had been removing. So, initially, we so let's, like, put this back to what it was before. Originally, this was is moved left, right, down Down\u003C\u002Fp>\u003Cp>Speaker 0: down and up.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. And so, originally, we had this. And so I wanted to look here and see where, where we sort of messed up here. And so if you look sort of in this log so, and I think this is the latest game. Is it the latest game?\u003C\u002Fp>\u003Cp>Speaker 0: No. We ran it once more since. We ran it\u003C\u002Fp>\u003Cp>Speaker 1: once more since. Did he did he go out for the same reason? That's my question.\u003C\u002Fp>\u003Cp>Speaker 0: Let's find out.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, no. Because this was what he was buying correctly. So Yeah. Did I see\u003C\u002Fp>\u003Cp>Speaker 0: But but but but but we we we don't necessarily need to find it or or replicate it. But, yeah, you you basically post it in a in the state just before you died, just before it died. Yeah. And then where and then did kind of what we did with the spreadsheet again. And, basically, we had it flipped.\u003C\u002Fp>\u003Cp>So up and down needed to flip, left and right needed to flip. And then our snake has also been a lot more interesting as a result.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So technically, like, even with bad code, it worked.\u003C\u002Fp>\u003Cp>Speaker 0: Change it back. Change change it back now before\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Before we forget. So let's go and I'll actually go in here, and I'll remove these console logs because they're false now. Let's go right. Mhmm.\u003C\u002Fp>\u003Cp>And remove this one and left. And and I feel like this is kind of the fun of this game is it's like you orienting yourself is also, like, I think it's part of the challenge here. But now, you'll see here so, like, well, in that last game, he didn't follow the rules like we thought he was going to. Now he, she, they, if we go back in here and create a rematch, we're going to see that, like Kevin said, this game is far, far more interesting now. And so here so now he's not constantly going in that loop.\u003C\u002Fp>\u003Cp>He's not running into these other snakes. And there we go. Now we're starting to get\u003C\u002Fp>\u003Cp>Speaker 0: some wins. Yes. Now I think that might be where we call this snake because the reality is this snake's doing pretty well. For the extent of our series, we don't need to do much. However, there are some other end statements here.\u003C\u002Fp>\u003Cp>Did our snake die or did no. No.\u003C\u002Fp>\u003Cp>Speaker 1: No. No. No. That was good. Nope.\u003C\u002Fp>\u003Cp>We did the right thing. He he just did head to head.\u003C\u002Fp>\u003Cp>Speaker 0: So head to head is the other thing to consider. If you collide with a snake head to head in any configuration, right, if you both enter the same square, the snake with the lowest to health, I think. Right? Yeah. Or the shortest snake?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: No. Which one? I just gave you 2 options. Oh, sorry. Shortest so it's it's\u003C\u002Fp>\u003Cp>Speaker 1: the snake snake with the it's the snake with the lowest health, I believe. No. Wait. Hold on. Let's talk a little bit.\u003C\u002Fp>\u003Cp>The shortest snake. Always consult the docs whenever you're not sure.\u003C\u002Fp>\u003Cp>Speaker 0: And so and so what you will need to do then is also look around the heads of each snake and have an exclusion zone of one one valid direction in any way they can go. You can also remove the tail as well because next move, the tail is gonna have moved out of place. So these are the little bits of logic you can do. The longer Longer about the snake\u003C\u002Fp>\u003Cp>Speaker 1: will survive. Shorter will be eliminated. If they're both the same length, they both die.\u003C\u002Fp>\u003Cp>Speaker 0: Fine. Fine. So there is more logic we can add to this, Nate. But the reality is it's gonna perform pretty well. Apart from head to heads, it should perform or running out of food legitimately because it currently is just hitting food randomly.\u003C\u002Fp>\u003Cp>It should perform pretty well. Good enough for ready set battlesnake. But, yes, in reality, you'd also have an exclusion zone around heads, and you would be able to take the last item of each snake body and remove that from your from your checks because it's going to move out of the tail space. Or rather, the tail will not be in that space anymore. So you can stop.\u003C\u002Fp>\u003Cp>Ahead might be, but the tail won't be. Sick. Okay. Ready, set, battle snake episode 1. I think the last thing I'd love to do before we just say bye and and stop recording, shall we just do a little bit of cleanup on this on this, file from the top to the bottom, make it feel good, and then we can wrap up.\u003C\u002Fp>\u003Cp>And next episode, we will take this snake and see if we can run it inside of a director's project.\u003C\u002Fp>\u003Cp>Speaker 1: Amazing. Alright. So, yeah, let's clean this up. Let's leave those comments in there because I think they're great. Let's leave that step 4 in there as well.\u003C\u002Fp>\u003Cp>So let's go up here. Now we do need to add a couple of things. So let's do, let's call this Directus TV as our author. And we're going to leave our stuff until next week, I think\u003C\u002Fp>\u003Cp>Speaker 0: -I think so. -\u003C\u002Fp>\u003Cp>Speaker 1: for our default. But, we've got that in there. This is all good. This is all good. Do we wanna leave this or do we wanna move this inside?\u003C\u002Fp>\u003Cp>What do we wanna do, Kevin? What do you think?\u003C\u002Fp>\u003Cp>Speaker 0: What do you mean inside?\u003C\u002Fp>\u003Cp>Speaker 1: Like, do we wanna deal with moving this logic for the head and neck inside of our inside of our other logic?\u003C\u002Fp>\u003Cp>Speaker 0: You can straight up remove it. Leave the variables at the top because my head and my neck might are used. My head is used. My neck is not used. We can remove those.\u003C\u002Fp>\u003Cp>They're they're captured in the other logic.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. You're absolutely right. Yeah. Okay. So we can remove and we can remove my neck because we don't need that anymore.\u003C\u002Fp>\u003Cp>And This is all good. I actually think we should put all of these together. Is there a reason that we should have our board width and height as let and our my head is const, or should we just have them all the same? I feel like they should all\u003C\u002Fp>\u003Cp>Speaker 0: the same. It literally makes no difference. Like, in reality, I would make them all cons because we don't reassign them. And I think inside of the 4 loops, you can also use const instead of let, if memory serves me right.\u003C\u002Fp>\u003Cp>Speaker 1: I feel like there's probably a way to select all of the lets in here.\u003C\u002Fp>\u003Cp>Speaker 0: Command d. Command d. Or control control d. Control\u003C\u002Fp>\u003Cp>Speaker 1: d. There we go. Let's call these all const. There we go.\u003C\u002Fp>\u003Cp>Speaker 0: Lovely. And the reality is we're still being overly verbose here. Like, line 79 could just be put inside of line 81. Like, we don't we don't need to be declaring all these variables, but I think this is fine. We're gonna move it over next time.\u003C\u002Fp>\u003Cp>I think we said we're gonna we're gonna try using direct automate to make this work, which I'm really excited about. There are some interesting quirks. I'm not sure I've quite figured out yet. We have a a pressure based rate limiter that I think we might hit, so we'll we'll have to explore that. But we'll we'll see.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. But I think that's I think that's everything. I think we've pulled out any unnecessary variables in here. I think this is looking pretty pretty good right now.\u003C\u002Fp>\u003Cp>Speaker 0: Sounds good. Great. Hit Save, and let's let's wrap up. I think this was a really nice first episode. Very happy we cut that that bit in the middle of working out what we were doing wrong.\u003C\u002Fp>\u003Cp>So thank you so much for joining us for the first episode of Ready, Set, Battlesnake. We won't see you next week. This is every 2 weeks. So if you are watching at the time it's coming out and not binge watching at the end, you will find the next episode right here on DIRECTV in 2 weeks' time. I've been Kevin.\u003C\u002Fp>\u003Cp>I've been Andrew. There we go. And until next time, you ready for the catchphrase?\u003C\u002Fp>\u003Cp>Speaker 1: Ready. Set Battle Snake. We'll get there. We've got 4 episodes.\u003C\u002Fp>\u003Cp>Speaker 0: We've got 4 episodes. Bye. Bye.\u003C\u002Fp>","Hello, everyone, and welcome to Ready, Set, Battlesnake. I am so thrilled for this season to be happening, bringing together a few of my interests all at the same time, including hanging out with my good friend, Andrew. Hello, Andrew. Hey, Kevin. How are you? I am all the better for seeing you. Would you like to tell us what the hell we're gonna be doing for the next 4 episodes? Yeah. That's a great question. So we are gonna be exploring the wonderful world of Battlesnake. More on that in just a minute. We're gonna be looking at how Battlesnake works. We're gonna be looking at how Battlesnake integrates with some other really cool technologies out there. And, hopefully, by the end of this 4 week series, you'll have a Battlesnake or 4 episode series. You'll have a Battlesnake that is ready to go and to jump in and start, yeah, start competing and playing around and learning some new technologies. Or you can just watch us fumbling around. That's also totally fine too. Also very No. I think what might be good is let's talk about what Battlesnake is and then talk about, like, our affiliations with Battlesnake. Probably feels like a good thing to throw out there. But let's start because we've said the phrase Battlesnake one too many times to not jump into what it is. What is Battlesnake, Andrew? Alright. So if you have ever heard of the old classic game Snake, like that old Nokia snake game where you, sort of control the snake to go around on a screen and eat little berries and get longer, and you try to avoid yourself and avoid the walls and avoid other snakes if they're on the board. Well, Battlesnake is that same idea, but moved around a little bit into the developer space. So it's a really cool way for developers to explore new technologies, new frameworks in this kind of technology agnostic way. So you actually, create a web server that responds to the Battlesnake API, and you can crawl control a snake using code to battle or compete against other snakes in that kind of same gamified way, but, in kind of way more fun for developers. And a few interesting notes here. So firstly, unlike snake on your Nokia, multiple snakes on the board at once, and you're basically trying to outlive everyone else. You die in the same ways, but there are multiple snakes just like this on the screen now. And because what you're doing is implementing an API, it means it's pretty technology agnostic as long as you can host a web server that can have a couple of specific endpoints that accept requests and then responds in a specific way. It almost doesn't matter what technology you use, which is pretty cool. And so we're going to be building this over a few episodes. Today, we're just gonna be focusing on, like, the vanilla Node. Js starter snake and challenges so we can introduce you to this wonderful world. You're gonna love it. And then we'll play around with, like, running Battlesnake in a director's project with directors automate, which should be really fun. And then, towards the end, we're going to also integrate dev cycle, which brings me on to a probably good point here, affiliations. Andrew, do you wanna start with, like, your history with this project? Yeah. Sure. So so I actually started with Battlesink back in 2020. So I joined fairly early on in in the the official Battlesink company. BattleState had been around for about 5 years before I joined the team, and it started out as a community project here on the West Coast of Canada. It was a big sort of hackathon event that happened here where a bunch of people have gotten to a room for a couple of days and built battle stakes and competed in sort of live events. I joined the team in 2020 as sort of their first community manager as we were kind of transitioning, as many were in 2020, into that online world. And so we sort of transitioned the whole kind of BattleState concept into an online platform that folks could engage with around the world. In 2023, Battlesnake, was acquired by another Canadian startup called Dev Cycle, where I currently work as their developer advocate. And so I still get to play around with Battlesnake a lot and work with a lot of the great people that were on the Battlesnake team. And and it's actually like the the pre dev cycle times that, Kevin, you and I met, even though technically, we never met in person until Not in person yet. Mhmm. Absolutely. And then, I, Battlesnake as part of going online, run a bunch of livestream series. It was a really good is still, but was really then in its peak, like, peak pandemic. We're doing everything online, ton of live stream series, including the competitive leagues, shows which discuss different algorithms that can be implemented. And this one show called Coding Badly, which was run with me and my friend, Joe. And this is kind of a spiritual successor to Coding Badly, I think. No offense, Andrew. I think that's probably gonna be the vibe the vibe we go for. So I love Battlesnake. And what's really cool about and we'll discover throughout today is at the very core, you know, there are some very simple rules. Don't hit into walls, don't hit yourself, Don't hit other snakes. Don't run out of life like it. Get food when you need it. But once you start to get options, you can really make some choices here. You know, you can say if left and right are both viable options, is there an algorithmically, beneficial way based on certain strategies to go? Do I box myself off, but now we only have so much life? Do I try and find food as quickly as possible at all cost to also starve out other snakes and so on? So all of this is implemented in code, and then when the games actually happen, your hands off. You know, your your algorithm takes over and makes decisions. So, yeah, I think that's that I I think that's most of Battlesnake. Any other thoughts, Andrew, before we crack on? I mean, let's, I mean, let's jump into the let's jump into the docs because I think they're gonna give us a good idea of kind of the some of the things that maybe we missed. And I think, actually, I'm I'm not a 100% sure. I haven't been in the docs in a while. I may still be featured in, like, the what the heck is Battlesnake doc. So I'm very interested to see if we can get it. Shortly. Am I there? The real question. I'm not oh, wait. No. Hold on. Quick start? There I am. I'm definitely I'm definitely in the quick start. I know this. Am I still here? We'll we'll throw this video into the resources that accompany this video on Directus TV if you wanna go and watch it in full. Awesome. Yeah. So when you click on that, what is dev cycle, it brings you over to the dev cycle documentation, and this documentation is fantastic. One of the things that Kevin was talking about, a lot of those sort of specific things that you can and can't do, if you wanna kinda figure out what that's all about, you can really a great place to go is actually checking out the game rules. And so this is where we start to look at things like this collision idea. So, like, self collisions and body collisions and head to head and who wins in these different situations, and then also giving information about some other things that are really important in this kind of gameplay. So you have a starting position in the game. We'll talk a little bit more about what that looks like kind of as we go through. You have health. That's a dynamic in this game as well. There's food spawns and hazards, and we're not going to get to a lot of that stuff necessarily today. We're gonna see that kind of as we go on. Yeah. But checking out this kind of game rule section is a great way to, like, get a a solid understanding of the idiosyncrasies, I guess, that exist in Battlesnake over sort of the, the the traditional snake game. The other great thing is this rules repository. So, like, all of these rules are actually in, an awesome GitHub repository called Battlesnake Rules. And so if you would rather not watch a video or if you'd rather just do, like, a different kind of deep dive, you can actually, like, jump into the rules repository, which is public, and you can go through and kinda discover a lot of these things yourself. It's all built out in Go. So if you're not a Go developer, then maybe not for you, but it's a useful place to kinda check out all of this written in code. And, yeah, and that was gonna be my clarifying point. This isn't pros. This isn't written out rules. But this is codified rules that actually run within the Battlesnake engine. So you can get right in there and understand again the intricacies of how it all works, which is really cool. I remember possibly in a coding badly episode, definitely in a battlesnake stream in the past generating, battlesnakes that that, like, actively hook into this rule logic very directly, something like that. Okay. So how do we get started? I think we're gonna stick with node. Doesn't matter what language you you use in the real, but, you know, given that directives is all JavaScript view node, and that's at least what I know. That feels like a good place to start. Yeah. I'm with you. 100%. So couple places you can go. Obviously, the quick start that is here. So, basically, anywhere you go in the documentation, you'll find the quick start. This one is set up in Python. So, like, if you watch the video and you've never programmed in Python, it's still kind of valuable, and this will kind of walk you through the game logic. But it also does, in this kind of quick start area, let you choose from different snakes. So I highly recommend if you're just getting started with Battlesnake, click on that quick start, and you'll get the kind of fundamental understanding here. So when we say you are building a web server server that is kind of serving as your battle snake and it's it's interacting with the battle snake API, this is exactly what's actually happening here in this sort of representation here. So, the game engine is querying your web server where your battlesnake is, and your battlesnake server is returning a response. That response can only be 1 of 4 things, up, down, left, or right. That's right. Right. That you received from the game engine, and then you just are kinda repeating this process over and over and over again. So game engine is sending something to your server. Your server is sending something back to the game engine over and over and over again. And, I guess one of the the kind of gotchas here is, the web servers only have 500 milliseconds to respond. And so if you are sort of some people run these on Raspberry Pis and some people or some people run them on you still run them on Heroku servers. And so one of the things that we would tell people is make sure you go and, like, do something on your Heroku server or your Just to see how the code stop at the beginning. Yeah. Exactly. But, but yeah. And and, Kevin, I don't know if I ever told you. Do you know what are the reasons why this 500 millisecond rule was implemented? Have you ever heard this story? No. Sorry. I just realized. I don't think you can see me. So I I'm doing a No. I can. No. No. I can see that. I'm, like, down here. No. I can't. So so I, so in one of the early kind of days of the battle sync competition, before they had this 500 millisecond, like, response time that was there, somebody realized that it was actually faster to respond with a physical keyboard or a game controller than it was to send code back. Love. And so somebody actually went and built a Battlesnake that was hand controlled, and then they would respond in real time. And it's interesting because I think they did fairly well. And at the end of it, it wasn't like, no. You did the wrong thing. It was like, great. You kind of you broke the rule, and now we or or or, like, defined the new rule for us. And so That's funny. I think you could technically still do it, but, you'd have to have some pretty fast thumbs. In planning for this series, not to spoil it, but we have spoken about what it might mean to change strategy, like, mid game. You know, there are there are ways we can achieve this. It was not real time. That's really funny. And the interesting thing about that limit is it really forces you to be computationally efficient as you start to build more complex things. At the beginning, literally, it doesn't matter. You're as long as your server can physically respond in that time, you know, it it doesn't matter. But what you can do with things like at the beginning of a game, spin up some kind of machine learning or artificial intelligence resource, and then per move, you can call on it, but you have to do it very efficiently. So are we gonna start with a starter snake, or are we gonna start from 0? No. No. I think we're I think I think let's start with a starter snake because that's what I think most people are gonna do here. Perfect. And so we'll go to kind of the first step. We're gonna choose our starter project. I'll just open this up in a new tab here. And what I kinda love here is, and I was talking to Kevin before the the show started, is like, so I'm running on a Chromebook right now. So if you're sort of a newer software developer, and you're, like, just getting exposed to this world, and you're, like, I don't have a device that can run this. If you can run a Chrome browser, you can run this. Absolutely. I mean, if you wanna have, like, a crazy setup, like, some people have, like, stand alone servers that run their state, You totally can, but you definitely do not need to do that to kinda get started with this. No. Yeah. Not at all. And so what's also great here too is so you can see these are the official starter stakes that are up on the screen. Yes. They are up on the screen. I'm gonna zoom in a little bit. I'm gonna try and zoom in a little bit. I'm so used to, like, the Mac keys, so I always click the I've used a Windows keyboard on a Mac device, so I'm always hitting the Windows key. Anyways, so You can you can remap them. That's what I had to do. I have a German keyboard on my physical MacBook. I'll just remap the keys in it. Just a few keys on as they're printed, but you just get used to it. Oh, I like that. I should do it. I feel like I've tried it before and it just didn't work. Anyways, not important. So, 5 starter snakes you can start with, which are, like, officially supported starter snakes. So Python, Go, Rust, TypeScript, and then the last one there, JavaScript is what we're gonna look at today. But if you're like, I really wanna do an f sharp snake, there is an f sharp snake for you there. And there's some really cool ones that have been defined in, like, some really, like, interesting languages and setups. Totally worth checking out. But today, we're gonna jump into the JavaScript official snake. Lovely. And what's awesome about these is so you can run these on your own infrastructure. So these are all templates that are set up on there. So you're welcome to run them on there. But we do have these set up ideally if everything works out still. I have not run one of these starter stakes in a while. That makes sense. See if everything works well, is you can just run on Replit. It's just a really simple Node. Js and express server, and, like, there's Docker files for external things, but everything runs right out of the box if everything goes according to plan, which it almost never does. So shall we click the button to show you what? Why would you say that? Now it's all gonna go pear shaped. I know. You say that. No. See, now it goes perfect. You gotta call it out. Right? You gotta call out to that You gotta manifest it. And this is gonna be we're gonna nail it. There's not gonna be any any any errors. And I'll Well Battle Snake's gonna win the league. What's that saying, though? It's like break a leg. Right? You say break a leg before they go on the stage because you're, like, saying the bad thing that might happen. I feel like it's the same thing here. If you just say, like, this is not gonna work at all, it works perfectly. Allegedly. Let's find out. We're gonna see what happens. Alright. So we're gonna click here. We'll see if I'm logged in. I have a feeling that I'm not. You're not. I'm not. But I don't think it really mat I think it'll still spin up if I'm not logged in. We'll find out. We'll see what happens. Nope. I have to log in. That's all good. We've follow look at that. I didn't even know we had followers right now. Alright. I'm gonna close you because we don't need you right now. I'm gonna close you. Yeah. And then when we when we get into REPL, it will bump the font size up significantly, I think. Yeah. So, yeah, it's this easy. So we're literally just clicking on that fork. We're gonna do an auth zero sign in. Hopefully, we can skip past the stage where I'm gonna put my password. Let's go. I think Toby Flender snake is still on Replit 2, which is my which is my battle snake. Oh. This is what this is what I knew was gonna happen. Alright. But you're log but you're wait. You're not logged in. It it hasn't logged you in. Oh, weird. That's weird. Alright. Let's log in again. Let's go log in. Oh, I love it. All the errors, even at the authentication stage, we're running. This does not bode well. Oh, you know what it is? I think it's because I'm like, I need to auth with GitHub. That's probably what it is. I do have it on that email, but I'm not auth with GitHub. So let's go do that. Oh, I hate that sometimes when you log in with, like, an SSO provider, but your email address is the same with that SSO provider and you're like, I don't I don't know which which system. Did it work this time? There we go. Okay. Okay. So let's go. So we're in here, and I probably got a bunch of snakes in here. We'll go back over, and we'll jump into our starter projects again. We'll jump back into our JavaScript snake. And so I've logged in now. Don't need to worry about billing, but this should be a much more straightforward process. I love I said everything is gonna go incredibly terribly. And even at the auth stage, I definitely have jinxed this. I'm not saying I told you so, but I'm certainly thinking it. Alright. So here we go. So this is the this is the snake. I mean, let's get it in the template right now. Yeah. What do we wanna call this snake? We're gonna call this when you call it something does does the dev cycle mascot have a name? His name's ToggleBot. So I don't know. Do we call it, like, we could call it ToggleBot. We could also call it, like, coding badly rebirth. We could call it, we could call it, let's call it let's call it ready, set, battlesnake. Ready, set, battlesnake. Alright. Let's see. I was worried you're gonna be, like, does the does the rabbit have a name? Does the director's rabbit have a name? As far as I'm aware, it doesn't. So set myself in a losing battle there. Let's use the template. I'm very disappointed. To you. Could be right. Could be right. Here we go. So it's forking. If you've never used Replit before, by the way, amazing cloud based IDE, super powerful, integrates with all of the things that you're already using, but also has a lot of inbuilt features. If you've used things like so if you've used Gitpod, which is, or Codespaces, which are sort of in the Code sandbox is another. Code sandbox. Yes. Code sandbox. I find, somebody described it to me recently though that Replit is kind of like the Google Docs or, like, Google Suite of, like, cloud IDEs. It's just, like, very accessible. Mhmm. Yeah. Bump your font size up. Let let's let's make that text more accessible because I can't read the damn thing. Alright. And I already broke stuff. There we go. Let's zoom in. Wonderful. Alright. So let's look at the files that we have here before we start messing around with anything. So it immediately brings us into our index dotjs file. The only other two things that we have in here, ignoring, like, our our package and all the rest of that stuff, are our readme, which is gonna kinda give you, like, things to do, which we're gonna be looking at in that sort of quick start. And then we've got our server dotjs, And this is basically, doing what we described earlier with those different responses that you can do to the Battlesnake API. So there is the just sort of, like, base level. And what what do you call that? Like, the standard with nothing else out of it? The site. There you go. No. No. I mean, like, the the actual just like a slash. Like, no additional Oh, sorry. Sorry. You're referring to that. I would call that the root route. Boom. Root route. So, like, your root route, and we'll talk about kind of what all these things do in a minute. And then you've got your start post route. You've got your move, your end, and then we've got a bunch of other little stuff here, including a console log that actually says your battle tank is running. And if I may just kind of embellish this a little bit. So this main endpoint that we care about is this move endpoint. That is the endpoint that will receive board states and ultimately has to respond with a direction. You'll notice there are these other ones. So this route endpoint handler, route handler there, that will respond with information about your snake. We'll do that in due course. But the start and end are interesting. We mentioned earlier about provisioning resource. That's basically where you would do that. Right? Because I think you get a small grace period between the start endpoint being hit and the first time the move endpoint is hit in any given game. And then your snake can be entered into many games. Yep. Yep. Absolutely. And then yeah. There there's a bunch of stuff. We'll we'll kinda look at some of these and see what they do, but but but Kevin's absolutely right. Like, move is really what matters, and that's where you need to return sort of up down left or right. So the other thing just to look in here, especially for the snake, is just kinda looking at these, like, what you have in this basic package dot JSON file. There's really nothing there. This is a super I think. Super, super light template. And you can go crazy with this, but this is a very, very light template. Mhmm. Okay. So, do we wanna do we wanna run this or should we look through first or should we head back over to the quick start? What do you think, Kevin? I think I think we could do a little less with the quick start because you and I have both like, we should I think we should refer to it as and when it's needed. I say let's get the let's go to the Battlesnake platform. Let's take this basic snake, which I think what what if you scroll down this file, what does the move endpoint do at the moment? Oh, there is some there is some logic there. Yeah. We'll talk we'll talk a little bit about that, but it does do stuff. So let's let's set up this snake and get one game running so we can kinda get to grips with how that code becomes a battle snake in a game. I think that that feels about Kevin, I have one question for you, though. What do you think I mean, Replen is trying to tell us to do this desperately, and we haven't done it yet. What is the thing that we need to do? Oh, I wonder if there's one call to action on this page that is significantly brighter than the oh, okay. Alright. Yeah. There was no mystery. We're hitting run. Awesome. And so this is the piece here. So this is the piece that tells you that your Battlesnake is running, is you're gonna see this API response here. So you're gonna see this API version, the author, color, head, tail. And then in your console, if it's running, you're gonna see this running battle snake at whatever the server, domain is and, or server address is and then info. Now because of the nature of Battlesnake, you need to have a public, URL that you can share with the platform. This is new. They used to all be public and has since shifted. So I think we need to deploy our app. No. We don't need to deploy our app. So we're gonna see what happens. We're gonna see if we can use just this. Yeah. Let's go to new tab. And then I yeah. So it'll be like this, and let me bump this up as well. We'll see how much it bumps up actually. There we go. So this is the same thing that we're seeing here. And then in our, in our navigation bar, we have this, like, replit.dev link, and so that's what we're gonna put over into the Battlesleep platform. It used to show up right here. I don't know why it doesn't anymore. I wonder if it should I imagine I imagine it got a bit unwieldy as you build more complex URLs by that. That's right. That must be it. And that's probably more useful as a developer just to see the the path, but who knows? You know what, though? I have a feeling with a lot of these platforms, it might also be. Yeah. You know what? So the what you can do is if you just, like, right click on this, like, 0.0.0.08 1,000, similar to other platforms, it'll actually convert it to whatever that public URL is. Oh, that's interesting. So it can actually just copy that over there. Okay. So that's working. So let's head back over to play.battlesnake.com. I'm so excited. Play. Play. Tailwind is a very nice little very nice little playground. It is a very nice playground. This is like this Chromebook has become my central development, tool. And so, like, I have so many, like, interesting things on here. I never thought a Chromebook would be my daily driver. Alright. So over on the battle 6 site, auth with GitHub, highly recommend. Not that you need to, but, we're gonna auth there. Oh, look at that. Inclusive waffle. Good. Okay. So I'm logged in with my personal account. So here's what I love. I used to so I'm a teacher. If you didn't know that, what you Oh, really? You have absolutely no mannerisms that would give that away. Like, what should we press on the screen now? Is it the green button? But I I I introduced, my, like, middle schoolers to, to Battlesnake a couple of years ago. And, like, all their snakes are still in here, which I kind of That's fun. Some of these are That's Toby still mine, though, runs on node, on Node RED. On Node RED. That is a stellar platform, if you are looking at, having a stake. And actually, funny enough now now that I think about doing a directus snake I was just about to say it. Yeah. Yeah. So so we have, directors automate as part of that. We have flows, which is a low code automation builder. And, yeah, if I whenever I actually describe it to developers, I ask if they've seen Node RED because I think it's very comparable. You know, you have automation builders that are very much like Zapier, and then you have ones which are more like Node RED. And this is more like that where you're wiring together these these these operations. But, yeah, we'll get to that. I love it. Alright. So, we gotta add a snake in here. Now if you're new in your account, I think it prompts you where to add your new snake. I haven't done this in a little bit, so we're gonna see. I have a feeling I actually need to go into my battlesnakes. Yeah. We're gonna find out. There we go. And here we go. So we got all of our snakes that are currently in here. We're gonna create a new one. We're gonna call this one ready, set, Battlesnake. We're gonna drop our server URL in there that we copied over from do we still have it? Yeah. There it is. I think that's And specifically without anything after the slash. We have to increment those, but that's correct. Yeah. So this is what you're gonna see, something that looks a little bit like this. If you wanna try and copy this and use my snake, go nuts, or use our snake. And then the next thing you choose here is your engine region. And so I highly recommend that when you're getting started, you don't worry too much about which engine region that you're using. There's a good, like, kind of breakdown on why you would use a different engine region. I'm based in in in North America, so and I'm kinda closer to Oregon, so I'm gonna set the state there. Kevin might wanna set it up based in the Netherlands, but it really, really doesn't matter at kind of the base level of Battlesnake. It it matters when you're squeezing out those milliseconds worth of worth of performance, but we're simply not. Exactly. And then, again, description you can add, same with programming language. This is just fun. It gives it a little bit of flair. So you can add a JavaScript, and then we can say this is Platform. We need to we need to do some work here. By the next time we record, we're gonna be doing some work to this list because we'll be we'll be moving we'll be moving our snake over. I love it. Alright. So let's go. And what are we gonna find? What are we hosting on right now? We're hosting on Replit, so we should have Replit there. And what I love about this list too is you can actually see, like, here are all the different places that you could host a snake. Now some of these I highly would not recommend. Like, get out Codespaces. I don't think you can actually keep a long running server going. But Interesting. No. Yeah. Yeah. You have to have the Codespace, and then they go to sleep. Yeah. But lots of different cool options here. And then the other thing you can do is you can set it so that other people can interact with your snake. And so the upside here is that you can allow other people to kind of play around with it. The downside is you can have people that decide to challenge while you're using your snake for some other thing, and then you're having to deal with all the compute costs. And if you're running on, like, AWS or something that charges you for all of those compute hours, definitely challenging. It's not just that. It's every request you get in relation to a single game has an ID of that game. But at basic levels, you're unlikely to be segmenting game logic. And if you're doing, you know, basic battle snakes, we'll just look at a single board and make a single response based on the heuristics of your code, the algorithm you create. But if you're doing something a little more clever way, you're looking over multiple turns, it screws you. But by that point, I suppose you are you are using that ID, but that's worth considering as well. Yeah. Absolutely. Okay. So let's jump in here. So we'll leave it as private. We'll save this battle snake. So you'll know once you save right away if your battle is actually there because it'll show up with the name, obviously, that you set it here. If you don't, though, you won't get any response here in this ping. So you might put, it used to be that if you started a snake on a service, it wouldn't automatically go. And so you might have to run this ping just to kinda see. And you can see here, if we do this ping, we were talking about that 500 millisecond response time. You can see that Replit is really up their, like, their timing. It used to be They didn't. They didn't. That first response was a few hundred. So that must be like a cold start kind of like now it's middling around 40, 50, 60, which is great, but it was multiple 100 the first time you, the first time it was there before you hit ping again out of interest. Yeah. That's true. So But then that's what you're starting to see. A good idea. Yeah. Yeah. Okay. So we've got we've got ready set battlesync in there. You'll also see you kinda get the flare that's related to the the language and and sort of hosting service that you're using. And so now, our Battlesnake is in there and is ready to go. So, do we wanna make our Battlesnake look good before we bring it into the game or do we wanna bring it into the game? Let let's jump into it and maybe we'll see if the the Battlesnake elders allow this. We might see if we can do some custom customization during the the act of us recording 4 episodes. But for now, let's just leave our our snake a slightly boring gray. We can come back to it. When when we come and update the platform to direct us, that's where we'll do the customization. That'll be next time. I love it. Alright. So, we're gonna go in here. You can choose. So, well, in the traditional Battlesnake game, or traditional snake game, I guess, it's kind of a standard map. No hazards. It's kind of just food pops up and things like that. So we're going to choose just the standard mode and a standard board size of 11 by 11. And we're just going to add ready, set, Battlesnake onto the board. We're not going to add any other snakes in there. You can add some bots. These bots are always running that have, like, some set sort of modes, but we're just looking to see if our snake is ready to go. So let's go ahead and see, how ready set Battlesnake runs. Alright. We're in. Okay. We're here. Should we have it now? You have to. Oh, yeah. Yeah. Come on. Let's do that. You ready? I'm so thrilled. No. I'm good. Alright. You ready? Yeah. Ready. Oh, no. No. We have a we we we get a catchphrase for this show. What? If Reggie said that Oh, my goodness. Yes. Okay. Here we go. Okay. Of course. Alright. Let's go. Ready. Ready. Set. Battle snake. That was thoroughly underwhelming. I'm gonna be honest. But but we are watching the snake. We got So this is what we got. So you see the ping? You saw the ping going on the right. So it was still around that 40 millisecond mark. There were 47 turns that happened before our snake died. Why did it die? And the nice thing about this editor, this player, of course, is you can step through your turns. So it was performing kind of a slightly eclectic set of moves, which is great. But what eventually happened to it? So it was at the bottom and it went off the board. It went down. So that was that's that's what our snake did. Now just to be clear, we'll look into the logic of our battle snake next, but we don't quite know why let's look at our code. And now we're at the point where we can start customizing this, this battle snake. Alright. So we're gonna jump back over to Remplit. And so all of the things that you need to know for this snake when you're getting started are all gonna live here. And the other thing that's really cool here that I guess we didn't talk about earlier and maybe you didn't see here because we didn't go that far down in the code is we're actually returning into the console every move. So you can see why it moved or not why it moved in that direction, but you can see what happened. So this in this game start move 0, move 1, all of these commands that are happening are in here. But and this is sort of included in the base snake that you get here. And you can return lots of things in there. Like, you can return, like, what's leading into that. You can return logic. You can return things like shouts, which are just like little statements that are in your code. We kinda got rid of them. It used to be that you could do custom shouts that were displayed on the board of the games, which was very cool, but, they kinda disappeared. But, okay. Where where do we wanna start, Kevin? Well, the nice thing about this starter snake for learning is it talks us through some steps. But before we do that, why don't we look at the code of what our battlesnake did? So it didn't just go in the same direction nonstop. Oh, that's another note. If your sync doesn't respond within 500 milliseconds or responds in an invalid way, it will continue to take the last direction There are a couple of variables here just to help us out. One that tells us where our head position is, and that'll give you an x y position coordinates and our neck, which is the second piece of your body. It's not all of them. It's just your head and your neck. There's a reason why your neck is important. We'll talk about that in a moment. And all we're doing here is oh, it's actually a lovely lovely and commented here. You can turn back into yourself. So if you're going straight up, you can go down, but you die immediately because you collide with your body. So all this logic is doing is saying don't hit the neck. Like, don't go back into the last space I was in, which is always the second position of the body. Now another thing that's interesting, if I may just kind of embellish kind of what's here, is we're starting to see the data structure play out a bit here. So this move function has an argument called game state. We see on lines 53 and 54 that inside of game state, there is an object called you. And inside of that, there is an array called body. And we can immediately assume that they're full of x, y coordinates. And in fact, that's that's made clear on line 56 where my neck and my head both have an x property. Before we continue, what might be interesting oh, I'm sorry. And so so the rest of the logic here is turn up, down, left, or right in that in that array, in that, object at the top, make them force if it means I'm gonna collide with my neck. Remove that as an option. And then I believe what it does is it basically, yeah, randomly picks 1 on line 87 and then returns the randomly selected valid move of which just doesn't collide with your neck. It does nothing else that's fancy. That's what we'll be doing today. Before we continue on this, can I make one recommendation that we do go back over to the docs and look at the game object so we understand what else we have at our disposal? Because I think it's important. Sorry. Not the get yeah. The game object. The board? Yeah. It's the board object. The board. The board. Perfect. Bottom left is 0. That's we're calling that out because when you draw on screen, sometimes 0 is the top left. If you do math, it's in the bottom left. So just note, 0 zero bottom left. That one screws me every time. This is what the object looks like. Could you zoom in a couple of sets by any any chance? Cool. Yep. So we have a height and a width. That's the size of the board. That is obviously important to know. We have the position of all the food, the position of all the hazards, which we're not playing within the classic game style. But if you hit hazards, I does your health go down or do you die? You die if you hit a hazard. Or well, depends on it depends on the game mode, but a lot of time you'll die. Fine. And then you have an array of snakes. Each snake has the idea of the snake, but it also has look at that. That was that was good teamwork there. It also has its health, all of its body parts, a convenience, which is an object called the head, which is always the first option in the body. So that's just a convenience there. They're always the same. The length, any customizations. But really what we care about for logic realistically is the body array and the head as a convenience. But we could get most of what we need from the body and the board collectively. What else is there here? Rule set, the game itself, but I don't think we care too much about those in this very moment. Yeah. Yeah. And I think the only other thing that's kind of in here that's, I mean, less it's less valuable. This is more like you you can see. Like, imagine that if you're in this game, your snake can see everything about the other snakes. So, you're also sending all this information about your snake to all other snakes on the board. So Yeah. You could stock out snakes that are low on health. Yeah. Yeah. And some people even decide, like, I don't wanna ever, I don't ever wanna attack a purple snake or a pink snake, and they'll just decide, like, I'm gonna avoid all of those snakes. Just and you have the ability to do like that. These weird kind of logic pieces, which I think is kinda fun. Yeah. Sure. Cool. So that is the shape of the object, which I think was important to dig into as we were starting to get a sense of what it looked like. So we know that we've got a board. We know we've got, we know we've got, sorry, a game. We've got boards. Boards have all the snakes and the board state itself. Snakes have a head and a body. So I think that's basically all we need to work through here. So all this snake does right now is avoid its neck. That is all it does. So Yep. What is the step one suggestion? So step 1 is to prevent our Battlesnake from doing exactly what it did by moving out of bounds. Okay. So this I mean, logically I mean, you and I have both done this kind of more simple battlesnake a a ton. But, you know, we will run through it kind of, you know, we come with a little bit more confidence in the approach, so we'll talk about it. But just know that in this little bit, there'll be a little less trial and error. Just so don't feel bad if if you're working through this and it's taking you a little longer. We just we just happen to know these first few steps like the back of our hand. So right here, there are a couple of comments that will go into that nested object and create a couple of variables for us, board width and board height. In this game, 11:11, but they can be smaller. They can be larger. And all we wanna do is say sorry. Go ahead. No. I'm just gonna say, my eyes my eyes are burning right now. Do we see a do we see a dark mode? It will exist. It has to exist. This is shocking. Our confidence. It it has to exist. Do you need to save save the snake or does it autosave or something? No. No. So everything auto saves. Okay. I recognize it. I'm just, like, I'm motivated for dark mode, but I also Does it take your does it take your, like, theme of your Oh, look at you. Look at you, Kevin. Look at you figuring out the smart thing. I bet you it does. That's the wrong one that I want to turn. Where's my dark mode? Mode? I wanna click on it here, and then I wanna set it on dark theme. And then I bet you if I refresh this, it's gonna be dark. Yeah. Okay. I'm very confident that that's going to happen, but it may not Oh, did I say I'm really confident? That's where it all falls apart. Yeah. We've already learned that that that's how it works. Nope. Did not happen. Here we are. At least it's dark in some places. Is it in your user settings? It might be. It's odd that it's not actually, like Like, here in the editor itself. Yeah. Maybe in your account, that you set up, like, your your default editor settings. Let's see. Let's see. Billing, account, themes? None of this. Themes. Themes. You know what? I wonder if that editor has, like, a command palette. Oh, how do I do that? Boom. Beautiful. Are you gonna catch it here? Do I gotta refresh again? Oh, you're gonna make me refresh again. Alright. I see you. I see you. There we go. Beautiful. Oh, look at that. We got some dark mode battle snake as well. But oh, actually, does this still exist? There used to be. Yeah. You can. Dark theme. There we go. Nice. All dark Okay. All the time. So let's go back to our code editor then. Sometimes in dark mode, things are just slightly less legible. So maybe let's just do one more step up on the font size, if that's alright. Yep. Lovely. And right. So we've got the board width, the board height. So real realistically, this code is gonna run every single turn. We're gonna know the size of the board based on these values, and we're gonna know our own heads x y position. And all we wanna do is the very pragmatic, am I on the very far right of the board? Yeah. I can no longer go right, left, up, and down. So that's the logic. We kinda have logic that looks similar to this above, but we can write it from scratch. That's also cool. Take it away, mister McLean. No. We're gonna see. So you say we're gonna write it from scratch. I, I fundamentally disagree with you, sir. I feel like if we're provided with this code, I feel like we should use it. Alright. Here we go. So here, we were looking if your neck was less than your head, then you didn't wanna move left. So we're gonna remove some of these comments, but it's gonna be the same kind of concept that we're doing here. And so let's look at our let's look at our head. So we wanna get our board width is gonna be important in a minute, but let's get our head. So my head x, and then we want our head x if it's less than or equal to or greater than or equal to. Well, hang on. Fin finish the thought. What what direction are we doing here? Left? Yes. So we're doing so we're doing x. So we're doing the x axis. If it's So we wanted to 0. If it's 0, it's on if it's 0, it's on the far left of the board. Yep. So that's all we need to check. If it's left, remove left trim. There is no minus one. You just die. So, double double equals double equals for for a comparison. Nice. So that's our head x. So let's do our head y now. So if our head y equals our board height So we're doing the top now. We're going clockwise. We're doing it. We're doing the top now. But the thing here is because you're gonna get your board height, but then you need to be minus 1. So, like, your board height because it's 0 indexed. Exactly. So you're gonna do your board height minus 1. So this is basically saying if my oh, and not my neck. We want that to be my head. Oh, yeah. So if my head y is equal to the board height minus 1, then we're gonna set up to be false. Yeah. Alright. So let's do our next one. So that means that we're not gonna go off to the left of the board too far. It also means we're not gonna go up too far, so let's keep going around. So let's do else if my head my head, and let's go x again, and let's go we'll figure out what the in between is gonna be, but let's do board width minus 1 and is going to be It's it's the same. It's just a double equals. The I I literally cannot get behind you doing this clockwise. It's driving me nuts, but we're here now. We're here now. It's so funny. I would I would have gone left, right, up, down, but I think that's because that's, like, how CSS rules are structured. Left, right, up, down. But yeah. Isn't that yeah. That's why I was like, what are you doing? Yeah. So this last one is gonna be, down. Right? Yep. And so this one's gonna be my head y equals equals 0 and get rid of that. And this one's going to be, down is false, and then we actually need to change this one to right is false. And so this logic here, if you haven't been following along, is going to be if your head is equal to 0, you can no longer go left. Type of. Did I type Hit backspace. Oh, right. Hit backspace. Is move safe right. Yes. So here, if your head is at x is at 0 sorry. Your head x is at 0, then you are not gonna be able to go left. If your head y is at the top of the board, you're not gonna be able to go up. If your head x is at the far right hand side of the board, you're not gonna be able to go right anymore. And if your head y is at 0, you're not gonna be able to go down anymore. Cool? Do we wanna should we comment this? We're not gonna comment. Get rid of those comments. Who needs comments? You've got a whole video to go along with this. Exactly. Alright. So let's, it should automatically update. I'm gonna just refresh here, and we'll see if it works. Normally, it does. It's pretty good in real time. So let's go and see what happens. Let's just create a new game and see if our solution sorted itself out. So all we have to do here is literally just create a rematch. It's gonna go and read in with our new Battlesnake data. We're gonna see what this looks like in dark mode. We may be changing back to light mode. It's almost too dark now. I feel like I need a like a beige mode. Like a like a, what is it? Like a Solaris. Is that what it's called? Yeah. There you go. Like a sepia mode. Yeah. Alright. Oh, this is for so long and my wife laughed at me. We also called it Nokia. So in the background right now, all of these moves are happening in the background. If we hit play and we get up to the latest turn, it kinda just starts to slow down, and we only and we see moves in real time. So what we're kinda letting it do now is catch up. So when we hit play, we can just watch it all in one go, which is Yeah. Pretty nice. The other great thing that you can do here is you can actually change the playback speed. So if you wanna watch it slower, you can if you wanna watch it faster. We're just gonna leave it as medium. There's also some other things you can do here. Like, as soon as the first turn is ready, you can auto play in these games. That's nice. You can also grab, like, a scrub bar so you can see how long the game is gonna last. It's really only good for you. If you're doing that in a competitive game, it's a little bit less fun. And then you can also show board coordinates. I actually think this one's interesting because I think this will show folks something interesting in the game. So let's go back here. So we got the board coordinates up there. As you can see here, 0011. So this is the stuff that we're talking about. Right? So when we said the board width, which is technically 11, even though it's 0 to 10, it's an 11 board. So if we'd left it at that and just said board width equals Yeah. The the the upper the upper right wouldn't have worked. It would have let it go off the yeah. Let it have died first. Let let's hit play so you can see what this thing does. So now it should avoid its neck, and it should avoid walls, which realistically means it will die by hitting itself or running out of food in theory. Yep. And that's exactly what exactly what happened. Ran into its body. And so this is what we mean here. So, technically, the neck counts as part of the body as does the head, but we've defined the neck in our code. And so he's never gonna run into his neck, but it doesn't avoid him running into anything past his neck or or like Kevin said, running out of food. So, and you can see here, like, he's actually surviving a shorter period of time even though we have more code. But that's honestly I think you're absolutely right. That first time was a little bit of a fix. Yeah. Absolutely. Oh, interesting. That should not have happened. That should not have happened. Oh, I see. So head is technically at 0. It might have been because he's at a he's at there's no other safe moves, but that's not true. So what happened on that last move? Did he yeah. He went down. Interesting. Let's let's just quickly check it again. When x is 0, if you head back to the code, when x is 0, remove left. When y is right at the top, hold height minus 1, you can't go up. When x is at the width minus 1, you can't go right. And then when y is at 0, you can't go down. Interesting. If or else if that Oh. Oh. No. No. No. I I I I I got it. This is in a a weird, like, else if else if else if. We we don't want this. We we we want each one to be its own individual statement. All of them need to be Oh, true. I love that. If you move the else's and and you change. I love it. Alright. It I'm not sure that's what the explains what just happened, but that was wrong. Like, it needs to be more like that. Alright. Let's see if that fixes things for us. Oh, timing out. Well, no. 42. Oh, that was an error. Let's scroll up. Let's see we'll see what happened. Just go all the way up to the top of the error. Alright. What's happening in here? I need to zoom out a bit this a little bit. Just give me one second. Board width is not defined. We made a typo. I think board width. Board? On line 70. There is on line 70. Oh, define the variables. Like, let put a let in in front of each one. So what's interesting there is it means it was running our old code before. So that's that's part of the problem is it was actually running our old code. So now if we run this, k. Our battlesnake should be up. Let's do a game again. And the moment you hit create rematch, we can go back to repl it, and we can see that things are happening. We saw that it made it 34 moves at least. That means all the moves have played out, so we can just hit play. It's looking promising. Yep. Yep. Same thing. And it went into its body. I wanna I wanna examine that because I think that's interesting. Right? We had that l tiff. Why why was that not working? Why why do we think that wasn't working? Like, where's the logical error in that? Oh, if if you're if you're at the bottom and the right, it'll only run 1. It'll run the first statement it it gets, and then it will. There we go. So you need to you need to get you need to get each of them. So this is now we think working. We'll we'll keep an eye on this one. Let's assume about that, like, logically, this feels good. If it starts moving out of bounds where there are viable options available, we'll come back and revisit this. The next step is to get it to collide is to get it to not collide with your entire body. Right now, it's just avoiding the neck, which is the second piece next to the head. We wanna avoid all of the pieces. And once again, Andrew and I have created start to battle snakes dozens, if not hundreds of times over. So what what are we gonna be doing here? We're gonna basically be writing a loop. We're gonna go through every piece of the body, and we're going to see if it's if it's next to me just like it was a boundary, we're gonna remove it. So we're gonna actually end up using reasonable amount of the logic. The logic is gonna look quite similar to the one above, but inside of a loop. Great. And we don't we don't actually need to do, we don't even need to we don't need to care about the length so much because we can, we can just use a 4, a for of loop for this, which is a syntax, which I really like. So I'll introduce you to a for of loop. John, sure, you cannot you know what we're gonna do. We're gonna do what every good programmer does, and then I'm gonna go to Google and look up how do I do a loop in JavaScript because I have legitimately You got me. You got me. You got me. I it it auto filled. Hang on. Hang on. Head back to Replit. It was helping you out there. It was doing it was and also w three schools. So so if you wrote the word for first first of all, it did actually kind of something yeah. Look. It's kind of giving you a hint here. So this is a very vanilla JavaScript loop. What this is, there are 3 parts at the top. First thing it's saying is, wow, okay. That's cool, I suppose. So we're initializing the value I with 1. We're saying keep running this loop for as long as I is less than the the the body length. And at the end of every iteration before you start the new one up I by 1 and I is a is a number, which you can feed into an array to get an index. There's a nicer way of doing this though, in my opinion, for this use case. So I'm gonna propose comment this out because I wanna just quickly show you a follow-up loop because I just think it's a I think it's a really nifty syntax. So instead of this, we're gonna go 4, and we're gonna do the the brackets again, but not let it autofill, all of that. Let piece where whatever we want. We're gonna call it piece of my body. Exactly. You've got one too many, close brackets there. And then you do squiggly brackets, and we enter into the code block. Piece is an is a variable inside of this loop, which will change its value for each iteration. So I just think this is yeah. I mean, it if you if you hit tab now, it seems to have already, that code's wrong. I could ain't gonna do anything. I mean, we'll leave it there as, like, as like, you know, we'll base something on this. This is not right. This is saying if if my head is in the spot of this piece, which also includes my head, make everything foresight. This just won't work. It will mean no isn't a viable move and it will keep it will keep sending you know, it will send it, I think, an undefined and then we'll die. So this is wrong. But just before we jump into the logic here, so this is another way to write a a loop in JavaScript. And another way you can do it is called a for in loop where you change the word of for in, and then the value of a piece will be the index value. So what we could have done there, an exact copy of what was above is saying for let eye of my body. And then I will automatically increment each time. It will give you the, in sorry, in my body. And then I or whatever variable name you give it, will will be a will be a number that goes up. The way I remember the difference between of and in, of object in index. I love that. Yeah. So, we'll we'll use the the for of loop. I think it's really nice because when you start to nest them, you can be really descriptive about what that value means inside of the loop. I think it looks nicer. It's a little more concise. But there you go. You learn about JavaScript loops. And there are obviously other loop types as well, but these are the more common 3. So what do we care about when we're looking at our body? Well, for every piece, what we wanna know is, is it one space away from us in any direction? That's all we wanna know. And if that is the case, we cannot move into that position. Even that strictly might not be enough because oh, no. It will be. That that will be enough. That is groovy. So what we wanna check is is our head equal to x? So is it on the same is it on the same x plane? Oh, no. No. Sorry. Inside of this initial if on 95. We're we're gonna alter 95 because 95 doesn't work. So we wanna say if it's on the x plane, but my head dot y is piece dot y minus 1, that means we are one space sorry. That means our body is one space below us. Down goes off the table. So this would be whatever the line is where we remove down. Game what's it called? Yep. Yep. Let's go grab that. Yeah. It is move is move safe. So if it's 1 under us down, down goes off. So is move safe dot down equals force. On the same x plane, hang on a moment. But the y for on the same x we're gonna fiddle with these till they work. This this bit always gets me. I think I might have just done that wrong, but we'll find out. Oh, it's part of the adventure. So, Hannah, if we're on the same x and it's below us if we're on the same Do you need to draw this out? Yeah. Maybe. Maybe. Let's do it. Let's grab, let's grab some It's bothering me. If we're if we're on the same x plane, but it's 1 below but then we're not on the same x plane out. But it's y. I wondered what tool you you were gonna grab there. We'll see if this works. I might pull up some random drawing that I've created. I bet you I am gonna we're gonna see what magical drawing that I have up on the screen here as this loads. What? Can you back a table in there? Because that's all we need is or a Google Sheet even, to be honest. Oh, that's a good idea. Let's do a Sheet sheet. Okay. Let's do, sheets.new. Sheets.new. Sheets.new? Yes. What's happening right now? What is slides.new, docs.new, forms.new. I love it. That's fantastic. Okay. Right. Zoom in. Zoom in. Zoom in. Zoom in a ton. Let's go zoom in. Oh, no. Wrong one. Let's go zoom zoom zoom. There we go. And and let's just do, like, we don't need 11. Right? We could just do, like, a 4 by 4 for the sake of argument. Like or up to 5 by 5 if you go to Ian and you go down to 5. Alright. And I do feel like this visualization is really helpful when you're doing this, like, genuinely. I think this is, like, a process that is useful for anybody to do if you're considering doing this. So draw, like, a 3 parts draw, I don't know. Yeah. Let's just yeah. Yeah. Perfect. Right. So what we want is if you go if you go right one oh, what's happening? What what we're doing? Just give me one second. This is just a small one to do this. Hold on. Hold on. There we go. Great. Hang on. Hang on. I think I think we needed to do it. Do a little more than that. And I would I would leave them as x's so we can notionally talk about the head. So do do maybe, like, make it like a like a u shape. So also fill in b 5 and b 4. I'm gonna you know what? I gotta stop with this x. The the color x is I I I I yeah. Oh, no. Actually, hold on. I could just literally copy and paste. Come on, Andrew. What are you doing? There we go. This is how we want it. Let's assume that b 5 is gonna be the head. Right. So the first thing we wanna do is to say, let let's try and do the left position first. Yep. So the x is the same and the piece is on x minus 1. So that's that's the first one we're we're gonna do. So x is the same as my piece dot x, but but the my piece dot. Oh my god. What's going on? So this is gonna be Sorry. Sorry. Sorry. Sorry. Sorry. The y is the same. Yeah. Because we're all because we're on the same level. We're on the same height. That's what was getting me. So if the y if the y is the same, the x is minus 1, you remove left. So let's do that first. Alright. So if the y is the same, x minus y? Yeah. Yeah. Yeah. So so let let's do the first if the first half of the statement, let's change those for y's. So if the y is the same, but the x is piece x minus 1 remove left. Love that. Okay. And and it's exactly the inverse for right. So x and sorry, y and y, perfect. We we replace those, but this time it's gonna be plus 1. That's all it was. We're we're just struggling struggling with that. So just to be clear, sorry, those first two still wanna be you need to basically inverse all of them. So y Correct. There we go. And then we're gonna return off that will be right. Yeah. And I I don't think we need I'm really glad we pulled out that tool, but I think we can basically invest them now. So copy and paste both of these. So next thing we're gonna do, not like that, is if the x is the same, therefore, the the column we're in is the same. That was it. But the y piece is minus 1, then it's under us. So we're gonna remove down. And, yeah. Exactly. Xxy y. There we go. That took us that took us a hot minute, but we got there. So that should now prevent us colliding with oh, hang on. Hang on. Why? Oh, no. It's fine. On that line 94 piece isn't, like, white, so I thought it was unused. Oh, no. It's because I added an s in there. There was a there was an error there. Just because I'm doing the Windows key save. No. No. No. Peace is declared but never used. I don't think that's right. Oh, there it is. It caught it. There we are. There we go. Cool. Oh, but we got a formatting issue? Hold on. So we can No. It was a it was a formatting issue. No? Hold on. Nope. We're still getting a bug here. Someone watching this right now is screaming. They're like 100%. A 100%. I feel like I'm missing it. Scroll to the top of the arrow. The top of the arrow is gonna tell you tell you what it is, but we're not at the top of it yet. Keep going all the way up, all the way up, all the way up. More more more more. Oh, okay. Less. We went too far. We went too far. It happens. Hold on. There we go. I just went too far into the first interpreter. Alright. Board meeting No. No. We sorted that. That was before now. We did that. Oh, it's this one. Hold on. Hold the phone. Close u. Wow. Capsule consoles. So many consoles. Formatting failed with message. What? And go have I missed something pretty obvious here? No. I think I think that was the that was the issue for let piece of my body. Hey, bro. Yes. I think it was just that s in there. Let's try running it again. You can do it. You can do it. I'm getting a hunch it can't do it without some intervention. What have we done? Is it because we do need to declare this let on my body? Oh, yeah. Maybe that that was the grape last time. Kevin lied. There you go. You lied. You said I didn't have to let it. No. I literally did earlier. That's what happened with board width. I feel like it's it's a useful thing to blame other people for your coding mistakes. I feel like that is what it is the sign of a true a true professional. The professionals we clearly are. Yeah. As I as I went to Google to search how to do a loop in JavaScript. What's what's happening right now? It's all taking a little long. Can you can you refresh the web view at the top? Yeah. Let's do that. I feel like that's k. We can close this out for now. Yeah. Yeah. Yeah. Let's see what's going on. I don't know. Yeah. I don't know what's happening. Maybe repllets happen a day. I mean, this is the thing. Right? It's like we are dealing with a cloud ID, so it does chug sometimes. But Absolutely. The REPL is booting. It's doing its thing. It is. I will say as much as I love cloud IDs or cloud development environments as they're trying to brand themselves now, I use Gitpod for ages, I use Codespaces for a while, I have moved back to using a desktop editor for things like this. Like, why am I waiting for my editor to get its act together? You're not wrong. So so yeah. There we go. And the GitHub CLI makes it really, like, quite quite nice to to move between PRs as I'm reviewing stuff. So, alright, great. Let's rerun the game. So now it should not die from its own body. So in theory in theory, for a one snake battlesnake, I think it's gonna have a yeah. I think it's gonna have a decent life. Look at this. We broke a 100. There we go. This is amazing. Wow. This, okay, 170 moves. Let's see what ended up doing it in no. It could be health or it could be eventually, it had no viable options. You know? Let's see. Yep. So it's doing all the things. Not running into its tail now, which was entirely possible with our previous code. It's not getting bigger because it's not getting food. So now it's going in that circle because it's avoiding its body, and it's avoiding its tail, and it's avoiding its neck. And so I think he might starve out just because he's getting stuck in that circle. Oh, no. Poor buttersnake. Eat something. Look all that food around you, mate. Dang. So, yeah, that's the interesting thing is, technically, he got into that loop where, like, technically, he was right, but we didn't say if you do this more than once, get out of it, which is a thing you can do. And what that, chicken or sorry. What that snake did is a is a a very famous move in the Battle State community called chicken snake. And chicken snake used to, like, go into a corner and would just chase its tail and wait for everybody else to die. And it would do really well. Yeah. And you you basically, you are you either other everyone has to die before you or you have to hope food spawns in in well, it it won't spawn where you are though, will it? It won't spawn in a position where a snake is. So actually It can spawn, like, right where oh, you're actually not wrong. That's absolutely right. Yeah. Interesting. Okay. Cool. So step 3 is a pretty, a pretty good evolution of what we just did there, which is up until now, we have a single battle snake on our board. But in reality, battle snakes will not be alone on the board. There will be at least one other snake on the on the board. And our job is not only to avoid ourself, but to avoid every snake and all of their body parts. Thankfully, we are effectively gonna get to get gonna get to, like, work on this work on this. And in fact, Andrew, I don't think we should copy and paste it. I think we should adapt this because our snake is in the snake's array as well. The might the me, whatever it's called, is again a convenience like the head to just give you the first item. So there's no point doing a loop over all our parts, then a loop over an array which includes us. We should just adapt this immediately. Okay. Great. Let's do it. Alright. So let's go yeah. So we've already defined here. So we've got our opponents defined, and we're gonna do that same let again. Right? Like, we're gonna do our loop with around our opponents, or do we wanna remove ourselves from that first? You know or I mean, this is I mean, it's up to you. Right? We can either, yes, remove ourselves from it, run the loop again because who cares? You know, it's just repeating logic effectively. Or we can adapt what is written above in that kind of 90 to a 110 line range to just be the loop. Oh, I see. Okay. Okay. So where where should we start? Us. So we we know here based on line 116, which we should probably comment out because it doesn't have a let in front of it, but we know that game state dot board dot snakes is an array of all snakes including us. I believe we're always first. So let let's create that variable above this loop, first of all. Alright. Let's do that up here. I see. I see what you're saying. Now when you said adapt, I was like, is that not what I'm doing? Alright. So let's define that. Yeah. Yeah. We we don't need to build on top of what we've already done here. In theory in theory, we can also do a way above with, the avoid our neck logic that it came with because we're gonna avoid our neck by virtue of this. Let let's leave it for now. Let's do a little bit of cleanup at the end, I think. So now we have it's not even opponents. I don't even think that's the right name. It's snakes. These are the snakes, and it includes us. We have a loop that will go through our body and do the check. And, basically, all we wanna do here is write a full loop above it, like surround it in a further full loop, and we'll do the same thing. So we'll go let, snake singular because each iteration is going to be a single snake of snakes. We'll do the open squiggly bracket here on line 44, remove that exactly input precisely, and we'll put that outside the outside of the other one. And all we wanna do here is instead of my body, it will be snake dot body. Oh, Kevin. It's like you've done this many times before. That is just like It is. That is beautiful. So in my hearing and the We we don't know it works yet. No. But I like aesthetically, it's very pleasing. Thank you. You're welcome. Thank you. So now in theory, we're running the same loop for all of for all the snakes. Right? Should we test it? Yeah. Yeah. Let's test. Alright. So let's I'm I was wrong. We do need to stop and start it. So let's see. This should work fine. Perfect. Okay. Let's head back over here. Gotta do a new game now, because we What? Are actually gonna add another set. Of course. Of course. We may as well use one of these bot snakes. I love that scared bot just disappears in this dark mode. In dark mode. Just not there. That's fine. It doesn't matter. We're gonna add him, and we're gonna add in ready, set, and battle snake. Right. Alright. Let's add that in there. Alright. And so we've got a game running now. Now this is another thing about playing this is, like, you can see a lot of these things from your logs. So if you're ever playing BattleSync competitively and, like, there's a competition, like, it's almost like you have to close your logs. Otherwise, it gives too much away. It's like you know Woah. What happened. The other the other nice thing about this is, of course, we get the whole board state every time. So as we start to integrate this with Directus, we can we can consume and store as much of this as we want. Let's, let let's play it. Yeah. Game over. 101 moves. Let's see what happens. Okay. So avoiding body, avoiding other snakes as well. Oh, I think I know how this is. Yeah. I think I know how it's gonna end for this snake. Yeah. Me too. Womp womp. Womp womp. Womp womp. And look how close the food is. Boom. Game over. So it can can we do a rematch? Can we get something hopefully a little more vibrant? Let's do a rematch. Let's see what happens here. Yeah. And we'll play it just so that we can see it in real time. And and this way, you can actually see this What is going on with our snake? Why is it happening? Our snake actually oh, there's an error with our snake because there was a viable direction there. It was to go right, and it did not. No safe moves detected. So that makes sense. So why was it no safe moves? So we have it set that it's not going to go is that it's not gonna go no. Because he's been going up there. So she should just the final move? He should have gone right. Yeah. He should have gone right or he should have gone wrong. Loop. So our logic is wrong. Let's take a look at that. But but and right was the only move. It couldn't go up based on our heuristics. Even though, in theory, to avoid our body, we can remove the tails. We can remove the final piece. But what's interesting is he was doing that. Like, if you look at this, so he was here and he was going here. So, technically, when he was in this spot, he could have actually done this again. Right? Oh, yes. So he could have gone up or he could have gone right. Of course. Interesting. Okay. But let's go to our code and see why that didn't so why did you decide? It's because of the other snake. So it is it does have something to do with the fact that he was right next to a snake, and so he didn't think what about oh, no. No. It's right. My head is still correct. And we wanna be checking the piece of every snake. P sex minus 1, p sex plus 1. Yes. That's right. I feel like it's because we do actually need to bring this logic inside because we're running these this and then we're running through this loop. It shouldn't make a difference, though. So? If it's set force once, it's we never set anything's true. We're never undoing the work we did. We're just kind of repeating it. You know? Yep. Okay. So I guess my head is in there. We don't need the my body anymore. We're not referencing my body anymore. Sninks does include us. Right? Yeah. Well, let's let's figure that out. I think Let's I think it does. I think you is the name of the the object as a convenience. Snakes snake squad. No. That's not important to us. No. That's the snake squad. That and then board. So, yeah, you are getting snake 1, snake 2, snake 3. Can we see the description? Snakes. All snakes on the game board, including yourself if you have not been eliminated. Yep. So it should be working. You're right. So what is going on here? So for snake of snakes, that's good. Again, people watch it people watching this on director's TV are gonna be like, god. It's so obvious. This is the thing. Well, let's hold on. Let's remove some some let's comment out some I mean, sure. Sure. But, I mean, that's literally gonna matter for anything. But okay. So if my head and let's go back to our let's go back to our sheet, drive. 94% of storage used? Yeah. I should probably, go in here and, you know Oh, not pay not pay more for it. Just act stuff. Yeah. That that's the other solution. Okay. So let's say we have let's let's actually reimagine the board state that we were in I love that. Fine. What one step back? I if it's playing, what's going on here? Oh. And we just need to go one step back. We're at the end of the game. Okay. There we go. Alright. So let's bring this to we will actually bring this to a 10 by 10 board. And let's pull this over. No. No. No. No. No. We we don't need the whole board. We only need, like, however wide. We only need, like, the cluster around around what we're doing. We we don't need to recreate the whole board. Alright. So let's go Hang on. I'll, on my side, I'll take a little a little cheeky screenshot, and I can I can call that out for you if you want? K. If you if you head if you head back. Go back to the game. There we go. Okay. So here's what we're doing. Let's go to our sheet. So we only need 1 like, we we can do it all within here, basically. So put our snake on, a 1b 1, a 2, and b 2. The head is b 2. And then, that that snake's gonna go, a 3. There. Yes. One more to the left. We can stop there. The rest is irrelevant. So that's that's what the snake is doing. And so first thing we're doing is we're avoiding ourselves. So we're saying can't go left. We can't go. The thing that's got me is why is it going right? That is I'm sorry. Why isn't it going right when that is a legal option? Yep. That's the question. The right logic is bust. Okay. So let's look let's pull this side by side. Let's grab this over here and let's go get this over here in our logic and see if we can make sure that our logic addresses this situation. Alright. So let's go here. Okay. So yeah, our first code here, my head, y. Yeah. So my head, y, is currently at On on b. Sure. Yeah. Right. On on 1. If it's 0 indexed in the bottom left. Yep. So we're both on 1. Sure. And the x, there is a piece one step down. Correct. Oh, hang on a moment. I think we've got our things muddled here. If my head y, but x is my so now that's correct. So that that accounts for a 2. Left is no longer viable. Yep. Going down 1, it's the same. We're on the same column, but right, this one is what's got me, but the piece dot x plus 1, but that is not the case. It's an it's an and statement. So, you know, both those things have to be true for it to be met. So if y is the same, but the my head dot x equals piece dot x plus 1, then it's on the right, but it isn't. I don't know what's going on here. My head okay. Well, hold on. So this technically, these are x aligned, but they're y they're not equal for y. They're they're they're y's match. They're wise match. They're on the same column. That means they're the same do you think I've just got the accent? No. No. No. I think that my I'm trying to work this out in my head. So this is Call that x1 or x. No. No. That's yes. Yes. No. You're right. You're right. You're right. You're right. Sorry. Yes. Oh, sorry. X1, x2, x3, x4, x5. Yeah. Y1y2y3y4. Yeah. So right now these are the same x, but they're different y's. Well, the yeah. So if we hang on. Let's let's start at the top. So let's do the left first. So the x matches. We've got them flipped, Andrew. We flipped them. Did we? Yeah. Okay. Look, the y is the if the y is the same no, we haven't. We got it right. So if the y is the same, let's say let's take, b 2 and c 2. Right? Yep. The y is the same. Yep. So that they they exist in the same yeah. But the x is minus 1. That means underneath. No. That means To the to the left. Something on the left. Fine. Then y is the same once again. They're in the same row. Are you sure we haven't flipped them? I I think we might have hold on. So okay. My head y, my piece and piece y are equal. So yeah. You're right. These 2 are technically there, and then my head x, so this is my head x, piece x minus 1. So, yeah, this removes left. That that totally makes sense. So we're good there. So next up, my head y equals piece y. Okay. Well, what would apply here, I guess? What would apply to this one right here? It would have to be on the right. I think we got it right. I don't know what's going wrong. My head x. Okay. So right now, this is they are the same for these are the same for x. So my head equals piece x. My head y equals piece y minus 1. Down is false. Yes. That that's right. That's good. My head pieces We flipped them. We flipped them. And what's getting me is the 1 on 1 on line 107. So my head dot x means they are in the same it means they're in the same row. Yes. It means so it means they're in the same column. It means they're in the same column. Yeah. Right? They're in the same column, but the y piece is minus 1. They're in the same column, but the but the y is is 1 to the left. They're in the same column, but the y I I'm struggling to visualize that based on our head. They're on the same Okay. Hold on. So my head ached I think we've, I think we've muddled these. I think I think, yeah, these are wrong. And I know we we know this and the audience like, yeah, it's wrong. And here's exactly how, But, I don't think it's as simple as comparing the y's and then comparing the x's. I think only one piece wants to be I think the Right. So can you just put, like, an a in d two for us? So we're we're gonna test no. No. No. A d 2. So so so so we're gonna test h versus a. Right? So the y is the same. The the y the y is the same. The x is plus 1. The the y is the same and the x is plus 1. Yeah. Right gets removed. So right yeah. Right should definitely So but at least one of these is wrong because we're removing too many options. Yep. If if x is the same, so they're in the same column So let's say y here. So these guys are in the same column. But the y is minus 1. Then we're gonna get that here. Fine. Then the y's are the same, the x is plus 1, then we're going right. Dude, this looks correct to me. It does I don't know what's going on. I okay. So I feel like this we've worked through this. I do think it must have something to do with this logic then. Have we screwed something up here? No. I don't think so. If my head equals 0, head x equals 0. It's anywhere in that line. You can't go left. If it's at the board if y is at the height, you can't go up. So here, this was at the height minus 1. So wait. What is height returning? Am I wrong? Have we changed the rules, and is height actually returning the height of the board and not 11? Oh, no. No. Returns 11. Oh. Oh. Wait. No. That No. That's correct. That's correct. Board width minus 1. Yeah. So that's that's fine. That would be the one that would have mattered that it would have removed the right, and that logic makes sense. Yeah. It's not there. Yeah. This logic is all correct. Let's run another game and see. We are. I am full on at the point where I'm just gonna find a snake because I know I know it's less fun. I know it's less fun, but I just, but I also I also don't don't fancy doing this forever. Let's take a look. K. I wanna see how this game plays out if something similar happens. K. Why did you run into his head? Why was that a safe move? Okay. What was the logic there? Did you have no safe moves again? No. So here, he thought he could go right. So oh, no. Because there. Okay. That makes sense. That tracks because he wasn't directly next. So there's no look ahead right now. So this takes So that's so so that'll happen sometimes. Yeah. So that tracks. That that game worked the way it should have. There's no buggy things there. It worked the way it should. Let's see if we can get another game because I feel like maybe that is it. Like, our code is right and there was just some sort of a glitch, but I don't know. K. So he's going in the circle. He's going in the circle. So he's gonna starve out anyways. We're not gonna get to see much there. Anyone? That's so weird. Can we can we run it again and again and again? So he's not making the same mistake again? I don't I don't like that it's just doing that. That feel it feels weird to me that it keeps chicken whatever you call it. Chicken Chicken snaking? Chicken snaking. That feels very odd to me that it does that consistently. Okay. So he's not doing this as well. It isn't. Okay. Fine. Okay. And, again, so this ish situation, was that a safe move? So he's gotta finish the game. Come on. Come on. Go back. Okay. So this does that make sense? Yeah. Again, this one makes sense because he didn't see right? So every one of these games, it's just the one game that was the one that was very strange. I think that there was some sort of a there was some sort of a an issue there with the actual way that it was reading our logic. I I hate seeing bugs that don't get resolved, but it seems like it's not happening again. It seems to have been okay since. Yeah. Well, let's try again. Maybe Okay. So why did he do that? So he shouldn't have done that either. Okay. So here's the thing. So why did he do that? So right. Right. Right. Game over. So he should not have gone right there. Why did he go right? Why did he think right Something's wrong. Was a safe move. Okay. So let's look at this again. And we're gonna have these guys here. So let's go, I'm actually gonna do color with these this time because I feel like that will actually help us. And then we'll do here and then we'll do him gray green and gray, green, green, green. That's all. I'm literally this is driving me potty. It's alright. This is the joy. I and, we'll fix it in post. Right? Yeah. We'll fix it. We'll fix it in post. Okay. Okay. Do do do do you want them to know what lovely editor, Nat? Let's jump cut here to the point where we fix it, and then we'll explain what went wrong. Yeah. Welcome. Welcome back. We worked it out. Andrew did a lot more like debugging, adding print statements. Andrew, do you want to show the code? And for those of you who are, let's zoom in like many, many, many times over, what was the issue? I'm yeah. But but what was the issue? So the issue was we had things flipped with the Yes. We did. Actual thing that we were removing. And the way that we discovered that, and this is kind of an interesting way to do it. I actually, like, find this as a really good way to do any debugging because I threw a console log into each one of these pieces with whatever we had been removing. So, initially, we so let's, like, put this back to what it was before. Originally, this was is moved left, right, down Down down and up. Yep. And so, originally, we had this. And so I wanted to look here and see where, where we sort of messed up here. And so if you look sort of in this log so, and I think this is the latest game. Is it the latest game? No. We ran it once more since. We ran it once more since. Did he did he go out for the same reason? That's my question. Let's find out. Oh, no. Because this was what he was buying correctly. So Yeah. Did I see But but but but but we we we don't necessarily need to find it or or replicate it. But, yeah, you you basically post it in a in the state just before you died, just before it died. Yeah. And then where and then did kind of what we did with the spreadsheet again. And, basically, we had it flipped. So up and down needed to flip, left and right needed to flip. And then our snake has also been a lot more interesting as a result. Yeah. So technically, like, even with bad code, it worked. Change it back. Change change it back now before Yeah. Before we forget. So let's go and I'll actually go in here, and I'll remove these console logs because they're false now. Let's go right. Mhmm. And remove this one and left. And and I feel like this is kind of the fun of this game is it's like you orienting yourself is also, like, I think it's part of the challenge here. But now, you'll see here so, like, well, in that last game, he didn't follow the rules like we thought he was going to. Now he, she, they, if we go back in here and create a rematch, we're going to see that, like Kevin said, this game is far, far more interesting now. And so here so now he's not constantly going in that loop. He's not running into these other snakes. And there we go. Now we're starting to get some wins. Yes. Now I think that might be where we call this snake because the reality is this snake's doing pretty well. For the extent of our series, we don't need to do much. However, there are some other end statements here. Did our snake die or did no. No. No. No. No. That was good. Nope. We did the right thing. He he just did head to head. So head to head is the other thing to consider. If you collide with a snake head to head in any configuration, right, if you both enter the same square, the snake with the lowest to health, I think. Right? Yeah. Or the shortest snake? Yeah. No. Which one? I just gave you 2 options. Oh, sorry. Shortest so it's it's the snake snake with the it's the snake with the lowest health, I believe. No. Wait. Hold on. Let's talk a little bit. The shortest snake. Always consult the docs whenever you're not sure. And so and so what you will need to do then is also look around the heads of each snake and have an exclusion zone of one one valid direction in any way they can go. You can also remove the tail as well because next move, the tail is gonna have moved out of place. So these are the little bits of logic you can do. The longer Longer about the snake will survive. Shorter will be eliminated. If they're both the same length, they both die. Fine. Fine. So there is more logic we can add to this, Nate. But the reality is it's gonna perform pretty well. Apart from head to heads, it should perform or running out of food legitimately because it currently is just hitting food randomly. It should perform pretty well. Good enough for ready set battlesnake. But, yes, in reality, you'd also have an exclusion zone around heads, and you would be able to take the last item of each snake body and remove that from your from your checks because it's going to move out of the tail space. Or rather, the tail will not be in that space anymore. So you can stop. Ahead might be, but the tail won't be. Sick. Okay. Ready, set, battle snake episode 1. I think the last thing I'd love to do before we just say bye and and stop recording, shall we just do a little bit of cleanup on this on this, file from the top to the bottom, make it feel good, and then we can wrap up. And next episode, we will take this snake and see if we can run it inside of a director's project. Amazing. Alright. So, yeah, let's clean this up. Let's leave those comments in there because I think they're great. Let's leave that step 4 in there as well. So let's go up here. Now we do need to add a couple of things. So let's do, let's call this Directus TV as our author. And we're going to leave our stuff until next week, I think -I think so. - for our default. But, we've got that in there. This is all good. This is all good. Do we wanna leave this or do we wanna move this inside? What do we wanna do, Kevin? What do you think? What do you mean inside? Like, do we wanna deal with moving this logic for the head and neck inside of our inside of our other logic? You can straight up remove it. Leave the variables at the top because my head and my neck might are used. My head is used. My neck is not used. We can remove those. They're they're captured in the other logic. Yep. You're absolutely right. Yeah. Okay. So we can remove and we can remove my neck because we don't need that anymore. And This is all good. I actually think we should put all of these together. Is there a reason that we should have our board width and height as let and our my head is const, or should we just have them all the same? I feel like they should all the same. It literally makes no difference. Like, in reality, I would make them all cons because we don't reassign them. And I think inside of the 4 loops, you can also use const instead of let, if memory serves me right. I feel like there's probably a way to select all of the lets in here. Command d. Command d. Or control control d. Control d. There we go. Let's call these all const. There we go. Lovely. And the reality is we're still being overly verbose here. Like, line 79 could just be put inside of line 81. Like, we don't we don't need to be declaring all these variables, but I think this is fine. We're gonna move it over next time. I think we said we're gonna we're gonna try using direct automate to make this work, which I'm really excited about. There are some interesting quirks. I'm not sure I've quite figured out yet. We have a a pressure based rate limiter that I think we might hit, so we'll we'll have to explore that. But we'll we'll see. Yeah. But I think that's I think that's everything. I think we've pulled out any unnecessary variables in here. I think this is looking pretty pretty good right now. Sounds good. Great. Hit Save, and let's let's wrap up. I think this was a really nice first episode. Very happy we cut that that bit in the middle of working out what we were doing wrong. So thank you so much for joining us for the first episode of Ready, Set, Battlesnake. We won't see you next week. This is every 2 weeks. So if you are watching at the time it's coming out and not binge watching at the end, you will find the next episode right here on DIRECTV in 2 weeks' time. I've been Kevin. I've been Andrew. There we go. And until next time, you ready for the catchphrase? Ready. Set Battle Snake. We'll get there. We've got 4 episodes. We've got 4 episodes. Bye. Bye.",[3906,3907],"bd1b80bd-7270-4544-83b4-7f4dcf1816ab","4778071f-905a-4fef-84ab-db818203e4e7",[],{"year":978,"number":993,"id":3341,"episodes":3910,"show":3911},[3343,3344,3345,3317],{"title":3347,"slug":3348},{"id":3366,"slug":3913,"vimeo_id":3914,"description":3915,"tile":3916,"length":13,"resources":12,"people":12,"episode_number":264,"published":3917,"title":3918,"video_transcript_html":3919,"video_transcript_text":3920,"content":12,"seo":12,"status":19,"episode_people":3921,"recommendations":3922,"season":3923},"m-night-shamalylan","893667420","Step into a thriller where the line between the coder and the debugger blurs.","819d0100-6f84-46f2-9b01-49737de4d943","2024-03-20","M Night Shamaylan","\u003Cp>Speaker 0: And now Dev Thoughts by Jackrabbit. Debugging code is like being the detective in a crime drama where you're also the murderer. You meticulously search for the culprit only to realize the criminal mastermind was you all along. It's a plot twist not even M. Night Shyamalan It's all coming.\u003C\u002Fp>","And now Dev Thoughts by Jackrabbit. Debugging code is like being the detective in a crime drama where you're also the murderer. You meticulously search for the culprit only to realize the criminal mastermind was you all along. It's a plot twist not even M. Night Shyamalan It's all coming.",[],[],{"year":978,"number":263,"id":3362,"episodes":3924,"show":3925},[3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3350,3378],{"title":3380,"slug":3381},{"id":3367,"slug":3927,"vimeo_id":3928,"description":3929,"tile":3930,"length":13,"resources":12,"people":12,"episode_number":159,"published":3917,"title":3931,"video_transcript_html":3932,"video_transcript_text":3933,"content":12,"seo":12,"status":19,"episode_people":3934,"recommendations":3935,"season":3936},"debug-mode","893667528","Do bugs think about us as much as we think about them?","e556639e-67e1-4d6b-b3c9-1105a022e43a","Debug Mode","\u003Cp>Speaker 0: And now, Dev Thoughts, by Jack Rabbit. Sometimes, when my code doesn't work, I look out the window and think about that one unresolved bug from 2 years ago. I wonder if it's also staring out of a window somewhere, reminiscing about the good old days we spent together in debug mode.\u003C\u002Fp>","And now, Dev Thoughts, by Jack Rabbit. Sometimes, when my code doesn't work, I look out the window and think about that one unresolved bug from 2 years ago. I wonder if it's also staring out of a window somewhere, reminiscing about the good old days we spent together in debug mode.",[],[],{"year":978,"number":263,"id":3362,"episodes":3937,"show":3938},[3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3350,3378],{"title":3380,"slug":3381},{"id":3940,"slug":3941,"vimeo_id":3942,"description":3943,"tile":3944,"length":889,"resources":12,"people":3945,"episode_number":213,"published":3917,"title":3947,"video_transcript_html":3948,"video_transcript_text":3949,"content":12,"seo":12,"status":19,"episode_people":3950,"recommendations":3952,"season":3953},"fafde41d-17cb-49bf-b139-a8ad11e50ae1","next","925880204","In this video, you'll learn how to use the Directus SDK with Next.js","10282be9-8051-4fb8-9684-aca9cddf4829",[3946],{"name":2820,"url":2821},"Get Started with Directus and Next.js","\u003Cp>Speaker 0: This is a brand new Next JS project created using the Next CLI. I've deleted all of the files inside of this app directory here, so we can start from scratch. And before we get started, also make sure to install the Director's SDK. We want to create and share a single instance of the Directus JavaScript SDK between multiple pages in this application. Create a new directory called lib and inside of that, a file called directors dotjs.\u003C\u002Fp>\u003Cp>In this file, we will import the required functions, create a new instance of the directus SDK, and export it so we can later import it in our future files. We can now go and run-in our terminal, npm run dev, and this will spin up a development server that we can use throughout the length of this tutorial. In my existing directors project, I have a global collection with a title and a description. And in this project, the public role has been given read access over all user created collections, and additionally, the director's files system collection. Inside of the app directory, create a new file called page dotjsx.\u003C\u002Fp>\u003Cp>First, we will import that shared helper that we just created, as well as the read items function from the Directus SDK. We will then create a get globals function that will make a request to Directus to read the global collection. We will then create a default function which uses the get globals function, getting the data from directors, and rendering the title and description in the page. In the director's project, we also have a pages collection. Every page has a slug, which is the primary key for this collection.\u003C\u002Fp>\u003Cp>It's a manually entered string and it correlates with the URL. So we want this page to load whenever a user visits slash about. A page will also have a title and a content field. The content is a WYSIWYG, a what you see is what you get editor. And what that practically means is underneath this nice UI here is just raw HTML.\u003C\u002Fp>\u003Cp>Inside of the app directory, create a subdirectory called slug. Notice these square brackets. And inside of that, a page called page dotjsx. The square brackets in slug means that this is a dynamic route. This page will load when someone goes to slash about, slash hello, slash anything else.\u003C\u002Fp>\u003Cp>This page will then be loaded. Once again, we are importing that shared helper from the directors file. We are also importing the read item, notice singular, function from the SDK. And we're also importing the not found function from the next navigation package. The get page function will accept a slug and will then use the Director's SDK to read the specific item in the page's collection with a primary key of the slug.\u003C\u002Fp>\u003Cp>If the page is not found, we will then use the not found function which will throw a 4 zero four error. In the default exported function, we will destructure params from the argument, and this will include the dynamic value in the URL. So we can then grab the page by calling the get page function, passing in the slug, which is this dynamic part here. In this case, slash about. It will then get that page data from Directus and render it into our page.\u003C\u002Fp>\u003Cp>Now take note that we are using dangerously set in HTML here. And what this will do is take that HTML and directly render it into our page. This should only be used for trusted content. But with this, we do now have a dynamic page. In the director's project, there is also a posts and an authors collection.\u003C\u002Fp>\u003Cp>Now, the posts look quite similar to the pages. There is a slug which is used in the URL, a title, and a content WYSIWYG. But there is additionally an image, which actually is a relational field to the director's files collection, I'll show you how that works in a moment. A publish date and an author, which is also a relational field. Authors only have a name.\u003C\u002Fp>\u003Cp>It's a very simple author, data model, just so I can show you how relational fields work. Inside of the app directory, create a new folder called blog, and inside of that, once again, a page dotjsx file. Once again, we import the helper. We import read items from the SDK. And then we use read items to grab the data from the post's collection.\u003C\u002Fp>\u003Cp>What's interesting here though is that we're passing in this object which allows us to use Directus' powerful query parameters to change the data that we get back. Firstly, we're specifying what fields we want to return. We want to return the slug, the title and the publish date. Note that we are not returning the content because we're not going to render it in this page, so there's no point increasing the size of the payload that is returned. In order to get the relational author name, we include an object with the name of the field and then the name of the nested field.\u003C\u002Fp>\u003Cp>So this will return the name from the linked author. We are also using sort in order to sort the returned data latest first. Inside of the returned HTML, we are going to loop through each of the returned posts and render a list item. The list item will have a link to slash blog slash slug. It will show the title, the publish date, and the author name.\u003C\u002Fp>\u003Cp>Now take note that this is nested data. Whereas post dot publish date and post dot title are direct properties inside of post, the author is nested inside of an author object like so. So this is our rendered blog listing. You will note that all of the titles are there. It is sorted latest first and we see the author name.\u003C\u002Fp>\u003Cp>Now, if we click on one of these blog posts, we should get a 404. And that is, of course, because we haven't yet built the individual blog post page. Inside of the blog directory, create a new subdirectory called slug, once again in square brackets, and inside of that, a page dotjsx. Rendering the individual blog page should now feel quite familiar. We're importing the helper, readitem, and not found.\u003C\u002Fp>\u003Cp>We are creating a get post function that will use the SDK in order to fetch a single item specifying the fields we want to return. What's specifically important here are these nested fields. And if this item is not found, we will throw not found. Inside of the returned HTML here, we once again are showing the title. We are using dangerously set in a HTML to render the value of content, but we also have this image field.\u003C\u002Fp>\u003Cp>All files inside of your director's project are accessible from your director's project URL slash assets slash image ID or file ID. Now you'll note here that there is not a forward slash here. The director's SDK will automatically append this to the URL if it isn't provided when initializing the client. So this full URL is, stackup.directors.app\u002Fassets\u002Fid, and then note here that we are using the built in image transformation to just provide the image at a width of 600 pixels. If you don't need a larger image, why would you send a larger image down the pipe?\u003C\u002Fp>\u003Cp>We are also using the image description held within Directus as alternative text. And that is how you get started with directus and next JS. I hope you found this tutorial interesting and we have more planned. So expect tutorials around internationalization, authentication, and setting up live preview. But that's for later.\u003C\u002Fp>\u003Cp>Have a fantastic day. Bye for now.\u003C\u002Fp>","This is a brand new Next JS project created using the Next CLI. I've deleted all of the files inside of this app directory here, so we can start from scratch. And before we get started, also make sure to install the Director's SDK. We want to create and share a single instance of the Directus JavaScript SDK between multiple pages in this application. Create a new directory called lib and inside of that, a file called directors dotjs. In this file, we will import the required functions, create a new instance of the directus SDK, and export it so we can later import it in our future files. We can now go and run-in our terminal, npm run dev, and this will spin up a development server that we can use throughout the length of this tutorial. In my existing directors project, I have a global collection with a title and a description. And in this project, the public role has been given read access over all user created collections, and additionally, the director's files system collection. Inside of the app directory, create a new file called page dotjsx. First, we will import that shared helper that we just created, as well as the read items function from the Directus SDK. We will then create a get globals function that will make a request to Directus to read the global collection. We will then create a default function which uses the get globals function, getting the data from directors, and rendering the title and description in the page. In the director's project, we also have a pages collection. Every page has a slug, which is the primary key for this collection. It's a manually entered string and it correlates with the URL. So we want this page to load whenever a user visits slash about. A page will also have a title and a content field. The content is a WYSIWYG, a what you see is what you get editor. And what that practically means is underneath this nice UI here is just raw HTML. Inside of the app directory, create a subdirectory called slug. Notice these square brackets. And inside of that, a page called page dotjsx. The square brackets in slug means that this is a dynamic route. This page will load when someone goes to slash about, slash hello, slash anything else. This page will then be loaded. Once again, we are importing that shared helper from the directors file. We are also importing the read item, notice singular, function from the SDK. And we're also importing the not found function from the next navigation package. The get page function will accept a slug and will then use the Director's SDK to read the specific item in the page's collection with a primary key of the slug. If the page is not found, we will then use the not found function which will throw a 4 zero four error. In the default exported function, we will destructure params from the argument, and this will include the dynamic value in the URL. So we can then grab the page by calling the get page function, passing in the slug, which is this dynamic part here. In this case, slash about. It will then get that page data from Directus and render it into our page. Now take note that we are using dangerously set in HTML here. And what this will do is take that HTML and directly render it into our page. This should only be used for trusted content. But with this, we do now have a dynamic page. In the director's project, there is also a posts and an authors collection. Now, the posts look quite similar to the pages. There is a slug which is used in the URL, a title, and a content WYSIWYG. But there is additionally an image, which actually is a relational field to the director's files collection, I'll show you how that works in a moment. A publish date and an author, which is also a relational field. Authors only have a name. It's a very simple author, data model, just so I can show you how relational fields work. Inside of the app directory, create a new folder called blog, and inside of that, once again, a page dotjsx file. Once again, we import the helper. We import read items from the SDK. And then we use read items to grab the data from the post's collection. What's interesting here though is that we're passing in this object which allows us to use Directus' powerful query parameters to change the data that we get back. Firstly, we're specifying what fields we want to return. We want to return the slug, the title and the publish date. Note that we are not returning the content because we're not going to render it in this page, so there's no point increasing the size of the payload that is returned. In order to get the relational author name, we include an object with the name of the field and then the name of the nested field. So this will return the name from the linked author. We are also using sort in order to sort the returned data latest first. Inside of the returned HTML, we are going to loop through each of the returned posts and render a list item. The list item will have a link to slash blog slash slug. It will show the title, the publish date, and the author name. Now take note that this is nested data. Whereas post dot publish date and post dot title are direct properties inside of post, the author is nested inside of an author object like so. So this is our rendered blog listing. You will note that all of the titles are there. It is sorted latest first and we see the author name. Now, if we click on one of these blog posts, we should get a 404. And that is, of course, because we haven't yet built the individual blog post page. Inside of the blog directory, create a new subdirectory called slug, once again in square brackets, and inside of that, a page dotjsx. Rendering the individual blog page should now feel quite familiar. We're importing the helper, readitem, and not found. We are creating a get post function that will use the SDK in order to fetch a single item specifying the fields we want to return. What's specifically important here are these nested fields. And if this item is not found, we will throw not found. Inside of the returned HTML here, we once again are showing the title. We are using dangerously set in a HTML to render the value of content, but we also have this image field. All files inside of your director's project are accessible from your director's project URL slash assets slash image ID or file ID. Now you'll note here that there is not a forward slash here. The director's SDK will automatically append this to the URL if it isn't provided when initializing the client. So this full URL is, stackup.directors.app\u002Fassets\u002Fid, and then note here that we are using the built in image transformation to just provide the image at a width of 600 pixels. If you don't need a larger image, why would you send a larger image down the pipe? We are also using the image description held within Directus as alternative text. And that is how you get started with directus and next JS. I hope you found this tutorial interesting and we have more planned. So expect tutorials around internationalization, authentication, and setting up live preview. But that's for later. Have a fantastic day. Bye for now.",[3951],"fa9bfca1-4cb7-463c-9374-bf7a553dce7a",[],{"year":978,"number":230,"id":3954,"episodes":3955,"show":3960},"c9653ab5-c084-48ce-b5ce-5195ccbc09a2",[3956,3957,3958,3959,3940],"ab41049a-0090-4568-ad0d-5a31892ae7c6","b55b2706-5a8e-4205-9a30-66beae51d6b2","8a72cda0-83ae-42ee-8bbf-87685e3021fd","56c91fbe-bb18-480b-b28c-92ec339de085",{"title":3961,"slug":3962},"Stack Up","stack-up",{"id":3964,"slug":3965,"vimeo_id":3966,"description":3967,"tile":3968,"length":305,"resources":3969,"people":3976,"episode_number":264,"published":3983,"title":3984,"video_transcript_html":3985,"video_transcript_text":3986,"content":12,"seo":12,"status":19,"episode_people":3987,"recommendations":3990,"season":3991},"8f4235cb-a9f3-4d25-ab5a-0af8f0fc892e","pdf-generator","906150239","Esther speaks to community member Bart about TextToAnything - an extension which allows generation of PDFs, QRCodes and Barcodes within Directus through the TextToAnyThing API.\n\nLearn more about TextToAnything at texttoanything.nl\u002Fdocs\u002Fdirectus.\n","10bc1cde-ffac-438d-b9c5-a7f554a5bfc5",[3970,3973],{"name":3971,"url":3972},"TextToAnything on GitHub","https:\u002F\u002Fgithub.com\u002FAttacler\u002FTextToAnything-Directus",{"name":3974,"url":3975},"Text to Anything Docs","https:\u002F\u002Ftexttoanything.nl\u002Fdocs\u002Fdirectus\u002F",[3977,3980],{"name":3978,"url":3979},"Esther Agbaje","https:\u002F\u002Ftwitter.com\u002F_estheradebayo",{"name":3981,"url":3982},"Bart Don","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fbart-don-38b9a51b6\u002F","2024-03-19","PDF Generator","\u003Cp>Speaker 0: So the extensibility of Directus really drives me to watch Directus itself because it allows you to turn Directus into either a normal website, a CMS, for your, web shop, or even just a project's tool.\u003C\u002Fp>\u003Cp>Speaker 1: Hi. Hi. Hi, everyone. Welcome to another episode of the Beyond the Core Show. It's a director who will shine a spotlight on extensions that have been created by community members.\u003C\u002Fp>\u003Cp>As always, I'm your host, Esther Akbaje, developer advocate at directors. And today on this episode, I have with me Bart. I would like to say Bart is a super community member because he's really involved in community and has created a good number of extensions. So very excited to have this conversation, but thank you for joining me here today.\u003C\u002Fp>\u003Cp>Speaker 0: Hi, Esther. Thank you for having me. So, yeah, as you already said, I'm very active within the community, both with helping out and also creating new extensions. So\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Yeah. It's good to have you. I'm excited for this conversation. Would you like to introduce yourself a bit more, maybe what you do, where you're based?\u003C\u002Fp>\u003Cp>Speaker 0: Sure. So I'm Bart Lund. I'm 23 years old. I'm from the Netherlands, and I'm actually a software engineer for a company that doesn't use directors, but that, of course, doesn't matter. Yeah.\u003C\u002Fp>\u003Cp>So in my free time, I've created a company with 2 friends of mine, and we're actually using Directus as the CMS for websites, web shops, and smaller back end tooling. Apart from that, I've been here since release, gonna date 42, if I'm correct. So that's almost 3 years.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: And I'm very active within the community. I'm trying to help out people whenever I can. So\u003C\u002Fp>\u003Cp>Speaker 1: Nice. Nice. I'm curious to know, where did you hear about Directus? Like, do you remember?\u003C\u002Fp>\u003Cp>Speaker 0: So I do remember we were talking about, like, what kind of CMS are we going to use? Because, obviously, Nuxt or something like it is not the only thing that, can handle data. So then we came across Trappy, and we were like, there are some limitations. It doesn't feel just right. And then we came across directors.\u003C\u002Fp>\u003Cp>Back then, it was a little bit of a mess because it was a release candidate, but we really, really liked how you can extend directors and do anything with it, as you'd like. So then we decided, like, hey, we're going to do directors, man. That's\u003C\u002Fp>\u003Cp>Speaker 1: Here you are. Right. Together. Yeah. Yeah.\u003C\u002Fp>\u003Cp>And you've actually done so many amazing or built so many amazing extensions with directors. I know today we are here to talk about the text to anything extension. So just tell me more about it. Like, what does this extension really do?\u003C\u002Fp>\u003Cp>Speaker 0: Text to anything is a generic name because it's going to do a lot of things. So the main focus right now is on generating PDF Mhmm. Both from your raw HTML if you want to provide it, or you can use a template, which is also based on HTML, but you can view it inside of the visual editor. And you can, of course, use parameters inside of it. Next to that, it's also, allowing you to generate QR codes and barcodes.\u003C\u002Fp>\u003Cp>And in the future, there will also be, like, HTML towards an image or just an image towards text itself. So we have OCR. But those are just future plans for now.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Okay. I I know that when I initially saw this extension, it was very exciting to me because I'd actually not seen a tool that could do so many things and still seem very simple. So, what really inspired this extension? Like, what what made you feel like, okay.\u003C\u002Fp>\u003Cp>I think this extension is something that will be useful to the community?\u003C\u002Fp>\u003Cp>Speaker 0: So first of all, we, of course, have to create, invoices ourselves and PDFs for them. Okay. Because we're also, so I left the company. It's a little side story, but I left the company for, like, a half year now. But we also need to generate PDFs.\u003C\u002Fp>\u003Cp>We also\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: Are using it like as our CMS. So we have our users, our invoices, our projects inside of, directors. So we needed to create PDFs. At first, we were using a hacky workaround. And now I actually decided to, like, create an official extension for it.\u003C\u002Fp>\u003Cp>And I also saw inside of the community that people were asking, like, how can we ever create a PDF if it's not in direct us already? So that's why I decided to make an extension for it.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's definitely interesting. I can't really wait to see it in action today. So, before we jump into the code, what what would you say was one of the most difficult things or challenges that you faced when you were developing this extension?\u003C\u002Fp>\u003Cp>Speaker 0: So the main thing that's very hard about PDF is that you need something that renders the PDF. So in your browser, it's really easy because you have HTML, CSS already rendered for you. But when you're talking about only back end, you don't want to spin up a browser just to create your PDF. So at first, I started actually doing that. So I tried to integrate the browser.\u003C\u002Fp>\u003Cp>It's called Puppeteer inside of your your directives, but it requires a lot of resources like CPU, RAM. And it doesn't feel right because you're make using a CMS. And for every CMS instance, you're also spawning a browser to generate PDFs for\u003C\u002Fp>\u003Cp>Speaker 1: you. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: So to tackle that, I actually created a API, which basically, allows you to post any, HTML or whatever, and it will convert it into the PDF for you and give it back.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Okay. Yeah. It makes a lot of sense. So would you like to share your screen and just walk us through, like, code, some code decisions that you had to make and just talk about what each code block does?\u003C\u002Fp>\u003Cp>Speaker 0: So whenever you're creating an extension, you probably need multiple folders or directories, or even extensions themselves. So you can create a bundle a bundle to, bundle it altogether. And in my case, I have a lot of extensions that are required to make it all work. Okay. So we start off with the installation hook.\u003C\u002Fp>\u003Cp>The installation hook is actually meant to create any collections that are not non existent at the moment and also add any fields if they are not there. Next to that it also talks to my API so you can actually get the pdfs or whatever.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: So first we actually create the templates collection so you can so we can use something to store the templates and the data about it.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Then in case there are no fields yet for that collection, we're creating the actual fields.\u003C\u002Fp>\u003Cp>Speaker 1: Alright.\u003C\u002Fp>\u003Cp>Speaker 0: And the reason why I'm doing it like this is if something happens or you delete a field by accident the code will run next time you restart directives and it's just correct.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm. Okay. Makes sense. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: And then we also need an API key because there's a paywall between the API of mine and Directus itself. The reason for this is that I actually have some costs that I sadly have to make to make this API work. So in this case, it adds a field to director settings, which you can then of course populate later on. We have the same with some other fields and then we get into the actual interesting part. Yeah.\u003C\u002Fp>\u003Cp>So within the global dish, which is like a global variable within your node. Yes. Runtime. I'm creating a text to anything, variable, which allows you to do any action that you can do within flows also inside of your own code. So if you have a hook or a schedule that needs to generate barcodes, PDF codes, you can just do it.\u003C\u002Fp>\u003Cp>And to keep it everything as minimal as possible, Directus is using Axios. So I'm also using that to call out the API itself.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Apart from that, it's fairly simple to me. I'm not sure if you see anything that makes you wonder. Likewise,\u003C\u002Fp>\u003Cp>Speaker 1: you can do that. No. Just the generate PDF. You have how many, functions do you have? You have the generate PDF function.\u003C\u002Fp>\u003Cp>What other function do you have? PDF template version.\u003C\u002Fp>\u003Cp>Speaker 0: We have those 4 at the moment.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: And the generate PDF from template actually reuses the generate PDF function, but it just parses your template and make sure that everything goes correctly. Okay. So apart from that, we, of course, also have the flow nodes because you can also use them inside the flow. So one of the things that we have here is really short, basically for every flow node because it's using the, functions that I just showed you.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: One difficulty that I actually had was to make a selection, like an input select field within the flows. So you can actually select the template that you want to use. So in the end I created an interface for it that allows you to do just that. You will probably see it within a second.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: I'm not sure if you want to see any other things. I'm\u003C\u002Fp>\u003Cp>Speaker 1: sure I I know you have a GitHub, repo, so everyone can just like, anyone interested in digging more on the code can just check it out. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: So within directives, after you've installed the extension, you have a module.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Which allows you to create any template you like. So, for example, this is a really simple one and actually does not hold any values. Okay. For example, this one. So on the left, it allows you to type any template data that you actually want to use.\u003C\u002Fp>\u003Cp>On the right, you can actually see, like, a preview of how it would look like inside of the PDF itself.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So this how it will look like as a PDF. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yes. And right now Okay. This is just for demo purposes. So it's not very stylish, but you can just use any HTML that you want and place it inside of the source code. This, by the way, is the what you see is what you get editor from the Reactors itself.\u003C\u002Fp>\u003Cp>Okay. It has the same functionality built in.\u003C\u002Fp>\u003Cp>Speaker 1: Interesting. Interesting. And yeah. Okay. Yeah.\u003C\u002Fp>\u003Cp>That makes a lot of sense. So, basically, you just put in any text, and it just gives you the PDF format straight up. What if I'm interested in maybe getting a barcode? How would that\u003C\u002Fp>\u003Cp>Speaker 0: look like? So barcodes are not require, do not require any template, obviously. Yeah. So if we go into flows we actually just created this flow. So in here, you can actually use the operation for that.\u003C\u002Fp>\u003Cp>So if you want the barcode, you can Mhmm. Put in the type of the barcode, the contents, and you can also select type. I actually included a library that, has a lot of types.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: And from there, you can also adjust the width and the height. And if you want, you can also include any text. So because barcodes are more static than the actual PDF that you want to generate, I decided to not create any editor for it.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Okay. Yeah. So, basically, that's it. And and I'm sure it's similar process for, QR code as well.\u003C\u002Fp>\u003Cp>Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yes. That's true. Okay. So I can actually show you what it's doing. In here, I have created a sample collection with invoices.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: And in here, we have an invoice number, name, date, and also a relationship with the actual items that I have on this invoice. So then we have flows, and we can actually generate the PDF for it.\u003C\u002Fp>\u003Cp>Speaker 1: PDF. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So it's actually going to use the templates that we just saw inside of the editor. And once it's done, we actually have a PDF in here.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, nice.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So I'll show you the logs after this. Okay. But here we have the PDF template that we just had.\u003C\u002Fp>\u003Cp>Speaker 1: Interesting. Love it. I can see myself using this extension. Really interesting.\u003C\u002Fp>\u003Cp>Speaker 0: Nice. So in the logs, you will actually wait. Inside of the flow, you can see, like, hey. We have our trigger. We have the button, and it's going to read the invoices.\u003C\u002Fp>\u003Cp>So we actually have, like, the invoice data, and then we're going to use the generate from template, which allows you to give it any name. You can also use the variables in like normal flow supported. And then you can actually select a template, which you just created inside of the settings step. Yeah. And here we have our template variables.\u003C\u002Fp>\u003Cp>So in here, you'll see that you actually get returned an ID. So if you want to make sure that the name is correct or that it's inside of the correct folder or link it up to any older record that you have, you can just use this ID and proceed with it.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Very useful. Very useful. See a lot of things. You like, you thought out, you know, how this extension can be very useful for many people and how it can be very efficient for them.\u003C\u002Fp>\u003Cp>So, yeah, lots of thoughts has gone into it here. Yeah. So in terms of how the community can be, involved in this extension, like, how can they be involved in the extension? How can they find it? Where can they find it?\u003C\u002Fp>\u003Cp>How can they use it?\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So I have a get a report story. So any issues in there, I will actually try and solve it as quickly as possible. And apart from that, it provides guides on how to use, how to use it, what variables you can use, how to actually set it up. So GitHub is the way to go, I guess.\u003C\u002Fp>\u003Cp>And, otherwise, if you have any questions, you can also ask me in this in Discord.\u003C\u002Fp>\u003Cp>Speaker 1: In Discord. Yeah. Okay. Cool. So my final question, which is like a fun one, is, what's your favorite director's feature?\u003C\u002Fp>\u003Cp>Speaker 0: Actually, the one I started with. So the extensibility of directors really drives me to watch directors itself because it allows you to turn directors into either a normal website, a CMS, for your, web shop, or even just a project tool or an ERP. It's like the use cases of directors are very widespread from what I've seen.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. So extensions are like your favorite feature?\u003C\u002Fp>\u003Cp>Speaker 0: Of course. I'm a programmer, so you could expect\u003C\u002Fp>\u003Cp>Speaker 1: that. That makes that makes a lot of sense. Actually, it makes a lot of sense. I've had people say flows. Flows are like I think flows are like mine, actually, but extensions are also really great.\u003C\u002Fp>\u003Cp>Yeah. But I see how you combine flows and extensions in this particular extension, so that makes a lot of\u003C\u002Fp>\u003Cp>Speaker 0: sense. Yeah. And also with flows, it it feels like demi mode to me because Go can obviously do a lot more.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. You have a point. But, thank you, Bart. It's been amazing just chatting with you and just you working us through how you build the extensions, the need for it, and diving the part into your code.\u003C\u002Fp>\u003Cp>So, I'm excited to see how the community engages and just starts using the extension and even more extensions that you build in the future. I'm excited about that as well. Yeah. So, thank you everyone for joining us, and we'll see you in another episode of the Beyond the Crossroad. Till next time.\u003C\u002Fp>\u003Cp>Bye.\u003C\u002Fp>","So the extensibility of Directus really drives me to watch Directus itself because it allows you to turn Directus into either a normal website, a CMS, for your, web shop, or even just a project's tool. Hi. Hi. Hi, everyone. Welcome to another episode of the Beyond the Core Show. It's a director who will shine a spotlight on extensions that have been created by community members. As always, I'm your host, Esther Akbaje, developer advocate at directors. And today on this episode, I have with me Bart. I would like to say Bart is a super community member because he's really involved in community and has created a good number of extensions. So very excited to have this conversation, but thank you for joining me here today. Hi, Esther. Thank you for having me. So, yeah, as you already said, I'm very active within the community, both with helping out and also creating new extensions. So Okay. Yeah. It's good to have you. I'm excited for this conversation. Would you like to introduce yourself a bit more, maybe what you do, where you're based? Sure. So I'm Bart Lund. I'm 23 years old. I'm from the Netherlands, and I'm actually a software engineer for a company that doesn't use directors, but that, of course, doesn't matter. Yeah. So in my free time, I've created a company with 2 friends of mine, and we're actually using Directus as the CMS for websites, web shops, and smaller back end tooling. Apart from that, I've been here since release, gonna date 42, if I'm correct. So that's almost 3 years. Okay. And I'm very active within the community. I'm trying to help out people whenever I can. So Nice. Nice. I'm curious to know, where did you hear about Directus? Like, do you remember? So I do remember we were talking about, like, what kind of CMS are we going to use? Because, obviously, Nuxt or something like it is not the only thing that, can handle data. So then we came across Trappy, and we were like, there are some limitations. It doesn't feel just right. And then we came across directors. Back then, it was a little bit of a mess because it was a release candidate, but we really, really liked how you can extend directors and do anything with it, as you'd like. So then we decided, like, hey, we're going to do directors, man. That's Here you are. Right. Together. Yeah. Yeah. And you've actually done so many amazing or built so many amazing extensions with directors. I know today we are here to talk about the text to anything extension. So just tell me more about it. Like, what does this extension really do? Text to anything is a generic name because it's going to do a lot of things. So the main focus right now is on generating PDF Mhmm. Both from your raw HTML if you want to provide it, or you can use a template, which is also based on HTML, but you can view it inside of the visual editor. And you can, of course, use parameters inside of it. Next to that, it's also, allowing you to generate QR codes and barcodes. And in the future, there will also be, like, HTML towards an image or just an image towards text itself. So we have OCR. But those are just future plans for now. Okay. Okay. I I know that when I initially saw this extension, it was very exciting to me because I'd actually not seen a tool that could do so many things and still seem very simple. So, what really inspired this extension? Like, what what made you feel like, okay. I think this extension is something that will be useful to the community? So first of all, we, of course, have to create, invoices ourselves and PDFs for them. Okay. Because we're also, so I left the company. It's a little side story, but I left the company for, like, a half year now. But we also need to generate PDFs. We also Mhmm. Are using it like as our CMS. So we have our users, our invoices, our projects inside of, directors. So we needed to create PDFs. At first, we were using a hacky workaround. And now I actually decided to, like, create an official extension for it. And I also saw inside of the community that people were asking, like, how can we ever create a PDF if it's not in direct us already? So that's why I decided to make an extension for it. Yeah. That's definitely interesting. I can't really wait to see it in action today. So, before we jump into the code, what what would you say was one of the most difficult things or challenges that you faced when you were developing this extension? So the main thing that's very hard about PDF is that you need something that renders the PDF. So in your browser, it's really easy because you have HTML, CSS already rendered for you. But when you're talking about only back end, you don't want to spin up a browser just to create your PDF. So at first, I started actually doing that. So I tried to integrate the browser. It's called Puppeteer inside of your your directives, but it requires a lot of resources like CPU, RAM. And it doesn't feel right because you're make using a CMS. And for every CMS instance, you're also spawning a browser to generate PDFs for you. Okay. So to tackle that, I actually created a API, which basically, allows you to post any, HTML or whatever, and it will convert it into the PDF for you and give it back. Okay. Okay. Yeah. It makes a lot of sense. So would you like to share your screen and just walk us through, like, code, some code decisions that you had to make and just talk about what each code block does? So whenever you're creating an extension, you probably need multiple folders or directories, or even extensions themselves. So you can create a bundle a bundle to, bundle it altogether. And in my case, I have a lot of extensions that are required to make it all work. Okay. So we start off with the installation hook. The installation hook is actually meant to create any collections that are not non existent at the moment and also add any fields if they are not there. Next to that it also talks to my API so you can actually get the pdfs or whatever. Okay. So first we actually create the templates collection so you can so we can use something to store the templates and the data about it. Okay. Then in case there are no fields yet for that collection, we're creating the actual fields. Alright. And the reason why I'm doing it like this is if something happens or you delete a field by accident the code will run next time you restart directives and it's just correct. Mhmm. Okay. Makes sense. Yeah. And then we also need an API key because there's a paywall between the API of mine and Directus itself. The reason for this is that I actually have some costs that I sadly have to make to make this API work. So in this case, it adds a field to director settings, which you can then of course populate later on. We have the same with some other fields and then we get into the actual interesting part. Yeah. So within the global dish, which is like a global variable within your node. Yes. Runtime. I'm creating a text to anything, variable, which allows you to do any action that you can do within flows also inside of your own code. So if you have a hook or a schedule that needs to generate barcodes, PDF codes, you can just do it. And to keep it everything as minimal as possible, Directus is using Axios. So I'm also using that to call out the API itself. Okay. Apart from that, it's fairly simple to me. I'm not sure if you see anything that makes you wonder. Likewise, you can do that. No. Just the generate PDF. You have how many, functions do you have? You have the generate PDF function. What other function do you have? PDF template version. We have those 4 at the moment. Okay. And the generate PDF from template actually reuses the generate PDF function, but it just parses your template and make sure that everything goes correctly. Okay. So apart from that, we, of course, also have the flow nodes because you can also use them inside the flow. So one of the things that we have here is really short, basically for every flow node because it's using the, functions that I just showed you. Yeah. One difficulty that I actually had was to make a selection, like an input select field within the flows. So you can actually select the template that you want to use. So in the end I created an interface for it that allows you to do just that. You will probably see it within a second. Yeah. I'm not sure if you want to see any other things. I'm sure I I know you have a GitHub, repo, so everyone can just like, anyone interested in digging more on the code can just check it out. Yeah. So within directives, after you've installed the extension, you have a module. Okay. Which allows you to create any template you like. So, for example, this is a really simple one and actually does not hold any values. Okay. For example, this one. So on the left, it allows you to type any template data that you actually want to use. On the right, you can actually see, like, a preview of how it would look like inside of the PDF itself. Yeah. So this how it will look like as a PDF. Right? Yes. And right now Okay. This is just for demo purposes. So it's not very stylish, but you can just use any HTML that you want and place it inside of the source code. This, by the way, is the what you see is what you get editor from the Reactors itself. Okay. It has the same functionality built in. Interesting. Interesting. And yeah. Okay. Yeah. That makes a lot of sense. So, basically, you just put in any text, and it just gives you the PDF format straight up. What if I'm interested in maybe getting a barcode? How would that look like? So barcodes are not require, do not require any template, obviously. Yeah. So if we go into flows we actually just created this flow. So in here, you can actually use the operation for that. So if you want the barcode, you can Mhmm. Put in the type of the barcode, the contents, and you can also select type. I actually included a library that, has a lot of types. Okay. And from there, you can also adjust the width and the height. And if you want, you can also include any text. So because barcodes are more static than the actual PDF that you want to generate, I decided to not create any editor for it. Okay. Okay. Yeah. So, basically, that's it. And and I'm sure it's similar process for, QR code as well. Right? Yes. That's true. Okay. So I can actually show you what it's doing. In here, I have created a sample collection with invoices. Okay. And in here, we have an invoice number, name, date, and also a relationship with the actual items that I have on this invoice. So then we have flows, and we can actually generate the PDF for it. PDF. Okay. Yeah. So it's actually going to use the templates that we just saw inside of the editor. And once it's done, we actually have a PDF in here. Oh, nice. Yeah. So I'll show you the logs after this. Okay. But here we have the PDF template that we just had. Interesting. Love it. I can see myself using this extension. Really interesting. Nice. So in the logs, you will actually wait. Inside of the flow, you can see, like, hey. We have our trigger. We have the button, and it's going to read the invoices. So we actually have, like, the invoice data, and then we're going to use the generate from template, which allows you to give it any name. You can also use the variables in like normal flow supported. And then you can actually select a template, which you just created inside of the settings step. Yeah. And here we have our template variables. So in here, you'll see that you actually get returned an ID. So if you want to make sure that the name is correct or that it's inside of the correct folder or link it up to any older record that you have, you can just use this ID and proceed with it. Yeah. Very useful. Very useful. See a lot of things. You like, you thought out, you know, how this extension can be very useful for many people and how it can be very efficient for them. So, yeah, lots of thoughts has gone into it here. Yeah. So in terms of how the community can be, involved in this extension, like, how can they be involved in the extension? How can they find it? Where can they find it? How can they use it? Okay. So I have a get a report story. So any issues in there, I will actually try and solve it as quickly as possible. And apart from that, it provides guides on how to use, how to use it, what variables you can use, how to actually set it up. So GitHub is the way to go, I guess. And, otherwise, if you have any questions, you can also ask me in this in Discord. In Discord. Yeah. Okay. Cool. So my final question, which is like a fun one, is, what's your favorite director's feature? Actually, the one I started with. So the extensibility of directors really drives me to watch directors itself because it allows you to turn directors into either a normal website, a CMS, for your, web shop, or even just a project tool or an ERP. It's like the use cases of directors are very widespread from what I've seen. Okay. So extensions are like your favorite feature? Of course. I'm a programmer, so you could expect that. That makes that makes a lot of sense. Actually, it makes a lot of sense. I've had people say flows. Flows are like I think flows are like mine, actually, but extensions are also really great. Yeah. But I see how you combine flows and extensions in this particular extension, so that makes a lot of sense. Yeah. And also with flows, it it feels like demi mode to me because Go can obviously do a lot more. Yeah. Yeah. You have a point. But, thank you, Bart. It's been amazing just chatting with you and just you working us through how you build the extensions, the need for it, and diving the part into your code. So, I'm excited to see how the community engages and just starts using the extension and even more extensions that you build in the future. I'm excited about that as well. Yeah. So, thank you everyone for joining us, and we'll see you in another episode of the Beyond the Crossroad. Till next time. Bye.",[3988,3989],"9d517928-56f0-4eec-8963-ecfcc8928ff0","0e456a49-eade-4984-8104-1e91f02ca1d0",[],{"year":978,"number":3992,"id":3993,"episodes":3994,"show":3997},30,"9e90c8ed-0bf8-43bb-8c4e-29ece4187819",[3995,3996,3964],"e22f53b4-e915-4b89-8057-6cccdcc58d5a","63ef529e-3a79-4116-a8b3-e9741dc70b3f",{"title":3998,"slug":3999},"Beyond the Core","beyond-the-core",{"id":4001,"slug":4002,"vimeo_id":4003,"description":4004,"tile":4005,"length":1060,"resources":4006,"people":4010,"episode_number":264,"published":4016,"title":4017,"video_transcript_html":4018,"video_transcript_text":4019,"content":12,"seo":12,"status":19,"episode_people":4020,"recommendations":4024,"season":4025},"9cc8741c-aa8a-44a1-8e92-8775b11b2063","5-years-time","924306486","How to we balance flexibility with responsibility? What does Directus in 5 years look like? What does sustainable open source look like while trying to remain fair?","9d9c1967-64b3-4990-a09a-9653152a5380",[4007],{"name":4008,"url":4009},"Ask Questions","https:\u002F\u002Fgithub.com\u002Fdirectus\u002Fdirectus\u002Fdiscussions\u002F17034",[4011,4014,4015],{"name":4012,"url":4013},"Ben Haynes","https:\u002F\u002Fdirectus.io\u002Fteam\u002Fben-haynes",{"name":3795,"url":3796},{"name":2820,"url":2821},"2024-03-18","5 Years Time","\u003Cp>Speaker 0: Hello, everyone, and welcome to community question time. This is actually the third one that we've done, since I started actually. But I'm really excited to be inviting our founders here, to give you the chance to ask about Directus, the company behind Directus, the future, the vision. And, don't don't give these guys any slack. Don't give these guys any slack.\u003C\u002Fp>\u003Cp>Come come with what's on your mind. We are a very, very transparent bunch, and this is one of those times where we open the floor. Just as we are kicking off, would you 2 like to give just a brief, description of your of your roles here?\u003C\u002Fp>\u003Cp>Speaker 1: Sure. Yeah. That's easy. I'll go first. I am the CEO and founder of Directus.\u003C\u002Fp>\u003Cp>I created it originally 20 years ago in 2004. It's been exciting the last couple of months, and we can finally say it's been around for 20 years.\u003C\u002Fp>\u003Cp>Speaker 2: Indeed.\u003C\u002Fp>\u003Cp>Speaker 1: And Rich joined, 8 years ago, something like that. Oh, yeah. He's our colleague. Introduce himself as cofounder, but that's my my role, I guess.\u003C\u002Fp>\u003Cp>Speaker 2: It's awesome. I'm, Greg Van Zonthe, CTO and cofounder. So where Ben created the platform in 2004, I feel like I created the platform in 2016 instead of again.\u003C\u002Fp>\u003Cp>Speaker 1: You you actually create the platform. I create, like, an idea with, like, a like, a a mock up or more\u003C\u002Fp>\u003Cp>Speaker 0: based on\u003C\u002Fp>\u003Cp>Speaker 1: what you\u003C\u002Fp>\u003Cp>Speaker 2: did. So we've we've both co created the platform in 2016 that I think is the way to describe it.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. This iteration of it at least.\u003C\u002Fp>\u003Cp>Speaker 2: This iteration. Yeah. Exactly. Yeah. Exactly.\u003C\u002Fp>\u003Cp>Yeah. So I lead the technical crew in the technical sort of implementation vision.\u003C\u002Fp>\u003Cp>Speaker 0: And so for those few people who are in the chat, the point of this, event, I suppose, is that we run it periodically, and there's a chance for it to you it is a chance for you to ask, any questions that are on your mind about the wider project. That could be about the company. It could be around the future of directors as a project, or anything else really. So do pipe in. I've got chat open right here.\u003C\u002Fp>\u003Cp>I'll keep looking at it, and I will be facilitating those questions. Yeah. So, Ben, in the chat, I have to admit yesterday, I was so confused because I thought you were you were this, Ben. Yeah. That makes it makes way more sense now.\u003C\u002Fp>\u003Cp>Speaker 1: So, while On that note, I had a, sort of a strategic, call yesterday. We were coordinating a call and there were 4 Bens on the call. I I my name's Keith. It was like, there was 5 people on the call and 4 of them were Bens, which seems pretty rare. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: That's a bad\u003C\u002Fp>\u003Cp>Speaker 2: thing I'll ever face, luckily.\u003C\u002Fp>\u003Cp>Speaker 0: How did you how did you last names?\u003C\u002Fp>\u003Cp>Speaker 1: No. Because there's 2 Ben H's. So, yeah, it got it got slippery, but, no, it was it was fine.\u003C\u002Fp>\u003Cp>Speaker 0: You managed to to make it work and didn't promise to do anything on behalf of others. So there you go. I'll put the screen. Yeah. Which one?\u003C\u002Fp>\u003Cp>So, yes, once again, please do come with questions. But, before, you know, there are any asked, I wanna kinda ask about we just launched the marketplace beta, and I wanna talk a little bit about, Ben, your, your narrative that you use around the 3 legged store and how the marketplace fits into that and how the marketplace is actually a really critical part to the success of our project going forward?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. No. That's that's a fun one to talk about. I mean, it's I I I'm obviously a big fan of metaphors and initialisms, acronyms. I I'd say that this one is more of it's not an acronym, but they have, like, the idea of a backronym where you have the thing first and then you sort of retrofit the, the metaphor afterwards.\u003C\u002Fp>\u003Cp>I think this is sort of that. Like, we knew that there's 3 big pieces to the to the company. And then this idea of a of a stool, like a a milkmaid stool, I think is what they're called, which has 3 legs. The idea is that they don't wobble because they have 3 legs. So it just kinda it's a nice visualization.\u003C\u002Fp>\u003Cp>Yeah. Of course, you want a nonwobbly product, a nonwobbly team, and a nonwobbly company. So the 3 pillars that support us, we start obviously with the core software. That's what, you know, Rich and I have been working on for for the better half of the last decade. And that is the key to everything that we're doing.\u003C\u002Fp>\u003Cp>But, of course, you need to deploy that and we need to make that as easy as possible. It's great to self host, but we wanted to create our cloud service, that offering. That was the 2nd leg. We have a single tenant cloud, a multi tenant cloud, and, of course, looking at all sorts of deployment options. And then the 3rd leg you know, the 3rd pillar of the company has been, sort of this if if we we have something called the 80 20 rule.\u003C\u002Fp>\u003Cp>It's really important for us to make sure that our software doesn't get bloated. We don't try and do too much and become like a monolithic system. We're trying to balance between sort of the the really the nice aspects of monolithic, approaches and then the microservice approach, which is great, but has a lot of complexity. We're trying to find that balance. And in this 80 20 rule, we say that the core software should satisfy 60, 80% of what you need, hopefully, really quickly, instantly.\u003C\u002Fp>\u003Cp>You point us at a database and you get it.\u003C\u002Fp>\u003Cp>Speaker 0: But, of\u003C\u002Fp>\u003Cp>Speaker 1: course, you still need to develop your full solution to a 100% and extensions and configuration and, you know, all these other pieces, your custom logic and code, that's the last 20%. And it's great to give people the modularity and extensibility of our core software. But, you know, doing that in isolation isn't nearly as much fun as being able to build an extension, send it out into a marketplace where our huge community can browse those extensions, learn more about them, maybe have ideas they didn't even know they had. It also motivates the maintainers of these extensions who are now seeing like, oh, wow. This is really popular.\u003C\u002Fp>\u003Cp>I'm getting all these downloads and installs and I get some visibility. It gives them more of a motivation to keep pushing and working on the, their projects, their extensions. So the Marketplace is the 3rd pillar, core, cloud and and Marketplace. And this is what really helps us get more, inspiration for use cases of the platform and get to a 100% faster. Again, leveraging not just our core software but everything else the community is generating, around the world.\u003C\u002Fp>\u003Cp>Speaker 0: Right. I have a thank you for that, by the way, Ben. So, right, I have a question for you. So earlier today, you, published a blog post called what we considered when building a marketplace, and I've actually just pushed that into the little, live highlights thing at the bottom of this page. Although I'm watching the page over here and it's not appeared, so your mileage may vary, but it's on our blog.\u003C\u002Fp>\u003Cp>And, some of the really early feedback that's coming about the marketplace right now today has been around this, What word am I looking for? This balance between giving people the raw power to make their own choices versus limiting the ability for something to go wrong to either stop foot guns or to look out for people's security, stuff like that. This is ongoing. This is a literally ongoing thread that's happening in Discord right now. It's been really busy in our marketplace beta channel.\u003C\u002Fp>\u003Cp>But I wondered if you could talk a little bit about how you perceive the balance between flexibility and, you know, control versus a duty perhaps could even be the word to keep people safe and happy?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I mean, that's a great question. So, this is a topic I could easily ramble on for 2 hours. So let me try to\u003C\u002Fp>\u003Cp>Speaker 0: Well, I'll I'll I'll pull you in in 5. Yeah. You have one. Go. Sure.\u003C\u002Fp>\u003Cp>So so\u003C\u002Fp>\u003Cp>Speaker 2: the first so the the first little bit of back information that is required there is, like, where does an extension even run-in the first place? Like, where do you ship that code, and where does it run? So I've seen platforms, Shopify comes to mind, that run it off off-site completely, and everything is webhooks back and forth. Other platforms, they they rely on sort of Docker containers and try to isolate it that way. In our case, we've made the decision early days to say, okay.\u003C\u002Fp>\u003Cp>For extensions you built yourself for your own project, you can just tap into whatever you need from the whole system. You're basically just full access. You can do whatever you want, because you're the one creating these extensions, and therefore, you can trust yourself not to do anything stupid. That's that's sort of\u003C\u002Fp>\u003Cp>Speaker 0: In theory. In theory. Theory.\u003C\u002Fp>\u003Cp>Speaker 2: Right. That was sort of the original security model of extensions because they were sort of meant for, like, you know, you built your own hook to to extend your copy of directives. Right? When it comes to this marketplace now, we're making it super easy to install stuff made by somebody else, right, on both cloud or self hosted. It doesn't matter.\u003C\u002Fp>\u003Cp>It's all the same. But that now means that you no longer really know what's going on under the hood. Right? So for extensions in the previous paradigm, they can access effectively anything. They can access anything in the database, anything on your local machine where you're running direct is because you can, you know, access the file system, the network, any of those pieces, which then also means that if that's the thing you're installing from somebody else, at that point, all bets are off.\u003C\u002Fp>\u003Cp>You know, anything anything can happen. And that is, for extensions developers, really nice because you can do whatever and make it really cool. But it's for extension consumers, really scary because you really just don't know what you're up against. Right? So the the balancing act that you were talking about, the question that we have to ask ourselves is, like, when it comes to a marketplace, on which of those two sites are we gonna sort of lead?\u003C\u002Fp>\u003Cp>Right? Are we gonna say, well, the marketplace is just completely open ended. All bets are off. Good luck. Save yourself.\u003C\u002Fp>\u003Cp>It's at your own risk. Or are we gonna say, well, you know, now that you're browsing it through Directus and installing it through Directus as the end user, you have a certain expectation of trust of, like, oh, what is presented to me here in this marketplace, I can install and use knowing that it won't blow up my whole project or send all my stuff to some malicious active third party or mine a bunch of crypto or something. Right? So for this initial release, we're erring on that side of, you know, user trust and security first. Right?\u003C\u002Fp>\u003Cp>So we're saying, by default, we're relying on the sandbox model that we implemented, by saying anything the extension wants to do upfront in static file, it has to define the stuff it needs access to. So it's like, oh, my extension wants to talk to this exec third party API. And then, therefore, the extension can now do it through a couple of methods that we give it. Right? But, therefore, the for the end user, there's a very clear contract of this is exactly the stuff that the extension can access outside of, you know, the information that's passed in through a Hooper filter or something.\u003C\u002Fp>\u003Cp>And that is you know, it's it's an in it's a very tricky balance as you've seen in that marketplace beta channel, for which thanks for the folk listening in. It's always fantastic to have those discussions. Because now the question becomes like, oh, but I really wanna import, you know, I wanna import libraries to do stuff with the file system, or I wanna use third party SDKs that might not use fetch or use some other networking thing, and now those don't work, which is definitely a tricky balance. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: But right now, we're really erring on that side of user trust and security first and then seeing, okay, where are the improvements in that sandbox model to to unlock all of that power again, so to speak, to get it back up to that that almost full power nature of of the extensions model.\u003C\u002Fp>\u003Cp>Speaker 0: And I think that's part of it too. I think this, it's really easy to look at, the sandbox x SDK and look at marketplace and the decisions made there and completely link them. Okay. The marketplace doesn't do x. That's an issue.\u003C\u002Fp>\u003Cp>But maybe the issue and this was actually the core the core, friction in here with these 2 competing narratives. 1 around marketplace doesn't let you install x and the other saying, well, actually, it's just that you can't build x using the sandbox SDK. And that's a solution which also honors the security model we've created. Another interesting thing though, that you said, which I will agree with and challenge, if I may, which is once you once we are providing the interface to browse and install the extensions, there is a contract to a degree that those things are trustworthy. That isn't the case in every, open listing marketplace setup.\u003C\u002Fp>\u003Cp>You can blow up a WordPress installation by installing a DUD plug in. So as a decision made, I will agree with you by saying I feel the same way in documentation as the reason that I will resist, at least for the time being, AI generated code snippet examples because because once it's provided through our interface, it's it's ours.\u003C\u002Fp>\u003Cp>Speaker 2: I I think the WordPress example is a really, really good one that has come up in the research with the team around the subject as well, of course. Because if you install, WordPress yourself on your own server, it's completely free. Like, the old bets are off, it's do whatever mode. Right? If you use wordpress.com or, like, the hosted equivalent of, you know, a WordPress installation, now there is way more constraints in place.\u003C\u002Fp>\u003Cp>Right? They only\u003C\u002Fp>\u003Cp>Speaker 0: is that?\u003C\u002Fp>\u003Cp>Speaker 2: Some of the yeah. It it's, like, for the same reason. I I I of course, I don't know the internal decision making there, but I'm assuming it's for the same reasoning that is, like, you know, you're using, you know, WordPress in a sort of controlled environment that you're paying for. You need to be able to trust that this stays alive. And then the stuff that you're installing is trustworthy and will work the way you would expect it to.\u003C\u002Fp>\u003Cp>Right?\u003C\u002Fp>\u003Cp>Speaker 1: Again, like, there's the self hosted versus our cloud and having to make sure that we have things really tightly scoped in cloud. But we have a multi tenant cloud and a single tenant cloud. A single tenant is our enterprise. And peep, those customers can upload custom extensions. So they do have workarounds, but we do have to make sure when you're sharing hardware in a multi tenant configuration, you know, noisy neighbors and making sure that people aren't, you know, scooting over into other people's projects.\u003C\u002Fp>\u003Cp>We have to keep that really tightly tightly scoped.\u003C\u002Fp>\u003Cp>Speaker 2: That too.\u003C\u002Fp>\u003Cp>Speaker 0: That's also that's also, of course, a decision in how we implement extensions. You know, we spoke briefly in in the past internally about different models for even offering extensibility. I believe Shopify might be a platform where as an as a extension or plug in, I'm not sure what word they use. Author, you host an external service, which is that you host it one instance of it. And then each of the instances can plug into that.\u003C\u002Fp>\u003Cp>And then it's a little bit the risk is different because all you're making is external HTTP requests. So there is also a decision in our implementation.\u003C\u002Fp>\u003Cp>Speaker 2: Contract model then. Right? Because you know the request going to the extension, and you know that the output is in a certain format that you're gonna work with. And whatever happens in that black box over there, you don't have to care about anymore because it's hosted off-site. Right?\u003C\u002Fp>\u003Cp>So it's a different model, with pros and cons of their own for sure.\u003C\u002Fp>\u003Cp>Speaker 0: So there's a question in the chat around, will third party modules ever be available? I think we're getting a little bit into, like, the weeds, and I want to pull that, if I may, into the marketplace beta channel where we can have, like, a more meaningful back and forth around the answer to that and the nuances of that. And thank you to Benny for kinda stepping in and giving, like, a cliff notes answer, but we can talk more about that, of course. Still on the marketplace. And by the way, I will keep going with questions until we run out of time for absence of other questions in the chat.\u003C\u002Fp>\u003Cp>So please do do come with them. But, talking more about the marketplace, Ben, I'm curious about how you talk about the opportunity that the marketplace brings when you're talking to potential customers, our board, our investors, people people with that level of, you know, stake in the director's project, because where it is today and where it may be in the future may not be the same. I'm kinda curious how you talk about that.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I mean, there's a couple different things. I think one is, the moment. You know, Directus has a lot it's like a back end as a service, which you it's just a database. You can do anything.\u003C\u002Fp>\u003Cp>You know, there's so many use cases. So, how do you give people inspiration for what it could be used for? Because I I kind of imagine it. Directus is like this big warehouse of opportunity and and potential, but people come in through one door, like one Google search or one, you know, thinking of what the platform is, whether that's a headless CMS or back end as a service. And then I think similar to something like Airtable, you know, they have 10 1,000 templates and all these, you know, options for what you can do in their sort of spreadsheet service.\u003C\u002Fp>\u003Cp>When you get into, like, a data service, I think that our marketplace will offer the same amount of like eye opening of you come in. It's like, oh, I'm gonna go get some extensions for making my content authoring experience better. What is this? This is like a stock you know, it's for the stock market or this could be, you know, anything any off the wall example that somebody came up with for their edge case. Now you're thinking in your head, oh, I do something similar.\u003C\u002Fp>\u003Cp>I wonder if I could manage that data and direct this and kind of unifies a few more systems. You know, reduce that complexity and cost. And, of course, the answer is usually yes. You can manage that in Directus, but it takes that sort of eye opening moment. And I think the marketplace is a great place to explore that potential.\u003C\u002Fp>\u003Cp>So that's one thing that comes up a fair amount. Another one is keeping things unopinionated. In a lot of our decks and the way we talk about Directus, we use the word unopinionated or agnostic. Again, we've built platforms that you know, Directus powers things for Fortune 10, Fortune 100 companies, you know, millions and millions of downloads, tens of millions. And you have to support their stack.\u003C\u002Fp>\u003Cp>You have to support their skill set. You have to support their languages. And I think it's easy to start making decisions early on that impact and restrict that flexibility for people to use their make their own decisions. And especially when we're looking at emergent things like AI that are so powerful, and you can you can really integrate and people are putting them right into the core software. Sometimes that might make sense if you are being more, opinionated in your in your builds.\u003C\u002Fp>\u003Cp>But we wanna make sure that we stay flexible and agnostic. And so for things like AI or things where you have to put in a proprietary API key, all that just makes sense in an in a marketplace because you can start to say, okay. This is this flavor of this functionality and you can go use a paid service or a free service. You can, you you know, integrate whatever you want. So I think that's a nice escape hatch to build in third party service integrations without us as maintainers saying this is, you know, we're we use Discord internally, you know.\u003C\u002Fp>\u003Cp>But obviously, lots of people use Slack, you know. You start to have to start making decisions. We wanna keep that as open as possible for as long as possible. Extension for us to do that.\u003C\u002Fp>\u003Cp>Speaker 0: A question we're good. A question for you, Reich, kind of leading on from that to a degree, and actually a comment I saw in the community, I think, yesterday or the day before that peaked my interest in in preparation for this event. Someone said, you know, oh, I would like this functionality that doesn't exist today. Someone says groovy. You can build that as an extension, not an issue.\u003C\u002Fp>\u003Cp>And the response they had was, you know, cool. It could be built or provided as an extension, but this feels like the kind of thing that should be available in the core software. And at the same time, when we've spoken recently, I know one of your upcoming, you know, or one of your motivations generally is actually taking some of the core functionality in directors today and explicitly removing it from the core and providing it as an extension. And I'm curious how how you feel about this and and what the basis is for that thought. What does and, Benny, I see your question there.\u003C\u002Fp>\u003Cp>I have one more bit of context following on from this question, then I'll ask that because I think there's a bit more, to it. How do you feel about that? I think people expect that they get a call that that is more capable, but we have this extensions model and you're trying to even slim down the call. So could you talk a bit\u003C\u002Fp>\u003Cp>Speaker 1: about that?\u003C\u002Fp>\u003Cp>Speaker 2: So the one of the technical challenges is that we find ourselves trying to build a thing that can do anything for everybody everywhere. So, therefore, what do you include? Everything. Right? That is sort of the slippery slope that you find yourself in.\u003C\u002Fp>\u003Cp>So what we're sort of aiming to do now is, like, the code base of the, you know, the direct to its internals is turning into a bit of a bigger monolith because we have so many individual features and parts to it. So the strategy there is gonna be twofold, is 1, to modularize the code base. So if it's included around, it doesn't matter as an extension. It's just, like, to keep the individual responsibilities as modular as we can just for maintainability. And then the second part is, yeah, asking the question, what, you know, is Directus versus what is, you know, the the complete package that you can get with different extensions in Nextiva.\u003C\u002Fp>\u003Cp>Mhmm. So, for example, if you look at interfaces, we have, I think, 3 or 4 different WYSIWYGs, you know, 1 in JSON, 1 markdown, one edited JS, 1, tiny MCHTML 1. And you can ask yourselves, like, do you need all 4 for every installation for everybody out there, Or do we just ship with 1 and then have the other 3 available just as opt ins through a marketplace? Right? And that feels like a fairly logical progression to me, especially now that we have a marketplace where you can easily install those pieces.\u003C\u002Fp>\u003Cp>The same would go for things like layouts, right, where it's like a Kanban layout or something we ship out of the box now. Might might be relevant for some, might not be relevant for others. Maybe that just becomes, you know, an easy opt in through through a marketplace instead.\u003C\u002Fp>\u003Cp>Speaker 0: Fascinating. I never use the Kanban view. It's I've seen others use it, and it looks fantastic. Just a bit it's a great example. I don't use it.\u003C\u002Fp>\u003Cp>So why is it shipped in every project?\u003C\u002Fp>\u003Cp>Speaker 1: I think things go in both directions. I think what Rich said is is absolutely correct. You know, not getting bloat, making sure that we can really keep the core a core, and then everything else is sort of the spokes of that hub and spoke, concept. But it also could go the opposite way. You know, Rich and I have talked a lot over the years about, you know, what community pull requests look like and how that affects us as a team trying to maintain like reviewing them is, you know, we've tried to formalize it and say, go through this process before you, as an outside contributor, just send us code because it's a lot of work to review.\u003C\u002Fp>\u003Cp>Those are usually very opinionated, going back to keeping things unopinionated. We try to do our job on, like, the vision side of the the company to distill specific features that people request that are very price specific to what they need. How do we distill it down to an agnostic functionality? How can we make a core functionality that will work across all these use cases but that satisfies what that person was trying to build. So instead of doing a core pull request, what I'm getting at is extensions are a great a great way to pitch that.\u003C\u002Fp>\u003Cp>If you wanted to build an awesome extension and say this this could be great core functionality, We might not be able to have the time and resources to review core PRs and all that. You can build an extension. If it gets critical mass, we would love to have conversations. If it doesn't make sense to go into core, maybe that's the way it happens. So some things will get pulled out of core and made extensions, and then some things might turn from extensions into core functionality.\u003C\u002Fp>\u003Cp>I think the key there is being really cautious. You know, there's some of the big the big 3 players, that have been sort of caught taking really, really powerful community built applications and then, like, just stealing it, ripping off and building it into the, like, no s or something like that. And then the little developer who had this really great idea never gets any credit or anything. So how you take a community built extension and move it into core where we're gonna help maintain it and build it, but credit, you know, the the author and how do you do that, you know, financially or whatever that looks like. There's a lot of nuance there to figure out.\u003C\u002Fp>\u003Cp>But, hopefully, you know, we got a pretty awesome community. I think we could work to make the platform what it needs to be and then have the extensions be what they need to be. And that I don't know if that's gonna be one of the questions that you have. It actually it came up, Benny had it, like, where do we see the platform?\u003C\u002Fp>\u003Cp>Speaker 0: Have hang on. Hang on. I have I have some context. I have some mega context here. Hang on.\u003C\u002Fp>\u003Cp>I think because I I think that's a necessary context. Just to tie tie up that question, though, there is something else that's really worth noting, which is Directus is a Swiss army knife, a chameleon, what however you wanna frame it. It's such a powerful tool that can be used for so many different cases, so for so many different use cases. Sometimes when people come in and think, oh, that should be core functionality, they're thinking through the sliver of use cases that they want to implement. And what we have to consider is, again, that that bigger picture, every user in every use case does this make sense as core functionality.\u003C\u002Fp>\u003Cp>I think that's a nuance where there is a slight difference in perception as maintainers versus users, community members, customers, and so on. So I think that's interesting. So the question of where do you see directors in 3 to 10 years, I think there was just a tiny bit of additional context, if I if I may ask right perhaps to just briefly, like, cover, which is today, Directus' data abstraction engine and, indeed, the setup of Directus is that you connect it to 1 SQL database and some asset storage and we present an interface for that one database and asset storage. But this there is ongoing work to perhaps change that change that makeup of what directors is. And I think before jumping into what we see directors be directors being in 3 to 10 years is just a brief just like a teaser even, you know, the work will happens in public, but a teaser of what that might or what this what this mad scientist project may result in because I think that influences the question that Benny asks.\u003C\u002Fp>\u003Cp>Speaker 1: Sure. Yeah. I mean, first and foremost and, like, Kevin's just sliding back the velvet curtain. Like, tell us tell us talk about all the things that we're doing behind the scenes.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. So, I mean, first and foremost, we've seen the the database landscape sort of diverge quite a bit. Right? So when, I mean, when I just joined the project, then it was a long time ago, but it was we just we only supported MySQL 5.6, I think it was at the time. And that was it.\u003C\u002Fp>\u003Cp>That was the only thing I ran on. Right? And that was enough at the time because it was really people needed a database. You had MySQL. That's that was just the name of the game.\u003C\u002Fp>\u003Cp>Right? There was no other real the the rest of it wasn't up and coming enough yet. Then when we rebuilt that in sort of the current paradigm that you're seeing, we were like, okay. Well, let's try to abstract that out to just be SQL so it becomes any SQL database instead of just MySQL. Because there's some differences, but they're generally similar enough where we can just say, okay.\u003C\u002Fp>\u003Cp>I'm just gonna support any of those SQL databases. Right? Then now, you know, early 2024, we're seeing more and more sort of SQL like or SQL compatible ish, vendors show up left and right that are getting increasingly popular. You know, think about PlanetScale or Snowflake or or those types of services, that are, you know, not always directly SQL compatible, but also very close enough to a point where it's like, why wouldn't it work? Because, you know, the paradigms are the same.\u003C\u002Fp>\u003Cp>So part of the the first exercise was thinking about, okay, how can we include those in sort of our SQL support as well? So it's SQL like, but then we have certain features that work, don't work maybe. And then while we're designing that, the question really became, okay, what is just a data source in the first place? Right? Because what is really the difference between, a structured REST API versus, a relational database versus a data warehouse that stores stuff relationally?\u003C\u002Fp>\u003Cp>At the end of the day, you're all talking about, you know, data in in a very similar format in all these different sources. Right? So that is really what we started focusing on at that point, with how can we abstract that away in a format that is this is getting a little technical now, but that is a declarative base. So you can declare the data you wanna get out of it, then have a plug in ecosystem that is fetching that information from individual vendors, and then get it back in a known format. And that is really the data abstraction project.\u003C\u002Fp>\u003Cp>Right? That'll unlock, you know, a bunch of other interesting angles that we never thought about before just by approaching it in that direction. But that is that is really the the the core. Kinda go back to\u003C\u002Fp>\u003Cp>Speaker 1: the core principles of the company is, you know, taking sort of expanding, from MySQL to Postgres to, you know, down the SQL line and now continuing to distill it down. Like, that is part of this, like, vision team is how do we continue to be less opinionated? Like, there's other data stores that people use. It's great to quickly support something like CockroachDB from a distributed side. But, you know, the data lakes and all the, sort of LMs infected.\u003C\u002Fp>\u003Cp>Like, there's so much that we could be managing and sort of make those part of the marketplace. You know, how do we make those atomic units of connectors for different data store, data stores that are available? Like, it's great to have the browse, manage, visualize on the app and API side, but we also have to think of the data coming in and how we can improve that over time. And Rich didn't even mention the the f word in that, which I'm surprised.\u003C\u002Fp>\u003Cp>Speaker 0: It was I was about to do it. I was about to do it too.\u003C\u002Fp>\u003Cp>Speaker 2: To go now. 3, 2, 1. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, I thought I thought we were about to. I thought we were gonna do it.\u003C\u002Fp>\u003Cp>Speaker 2: I'll I'll continue that train of thought too because it it started with what is a data source. And then the second piece to that big thing is, like, where does that data live? And the answer is it lives in all sorts of places at the same time all the time, for good reasons. Right? So we've seen a lot of piece like, a lot of software try to sort of synchronize it back to a centralized place and then manage that and then try to use, you know, integrations to get it synced back out.\u003C\u002Fp>\u003Cp>That has been historically, you know, very popular. We're seeing some tools, pop up now. They're trying to sort of get that data connected through GraphQL specifically because you're dealing with known schemas on both sides. Excuse me. So the question now became okay.\u003C\u002Fp>\u003Cp>If we have this sort of very agnostic declarative data fetching engine that we're building, how does that tie into federating the big f word, across those different data stores at the same time? Right? So as if you're declaring one data structure to come out of it, but the data is like, the underlying data itself is just stored in different places, how can we get that out efficiently, and return it in the way that you need it, rather than having to treat it as disparate data sources.\u003C\u002Fp>\u003Cp>Speaker 0: And in reality, like, people do sync data back using directors today. They'll have a primary database, which will be the thing they connect, and then we'll use our automation features code or no code, whatever that may look like, to bring in and sync data from then, you know, additional sources. Because And, again,\u003C\u002Fp>\u003Cp>Speaker 1: perfectly valid because, I mean, think of the systems that have just gone offline in the last 48 hours. Like, big systems go offline. It's nice to have sort of a local, like, your own database synced where you can still pull that data in and you've got the results.\u003C\u002Fp>\u003Cp>Speaker 0: That. Yeah. Yeah. I know I didn't thought about that.\u003C\u002Fp>\u003Cp>Speaker 1: Talking about. Like, you can't always pull it in and say this is we talk a lot about single source of truth in the database. That's what we manage. There's no direct as magic that happens in between. It's the database.\u003C\u002Fp>\u003Cp>That's the magic. We give you access. But one database isn't enough usually. We manage all types of data. When you look at something like customer data, you can have your customers in Stripe.\u003C\u002Fp>\u003Cp>You know, you have it in HubSpot or Salesforce. You've got it in probably some sort of relational database for your SaaS or whatever you're building. And you've probably got some thrown over in, say, cold storage and a data lake. You know? All that customer data needs to be accessible so that you can find the value.\u003C\u002Fp>\u003Cp>But you can't just pull it all like, those services are all used in different ways for a reason. So managing the data where it lives in situ is important. And the only way to really do that is through that, not the 4 letter f word, but the 10 letter f word that are able. And that's why it's so exciting. I counted.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. He sat there counting. And we now know that's what was happening in the last few minutes.\u003C\u002Fp>\u003Cp>Speaker 1: Anyway, so it's pretty it's really exciting thinking of, like, what is the future of, you know, data in because we can argue so much on the data outside or the IO. Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: The other thing is, exciting to me there is because it becomes a declarative system, we know what the output format is, and we know what the input format is. So it also means that getting that data from one place to the next becomes, you know, a a possibility now because the data format coming out of Salesforce is now gonna be compatible with the data format going into a HubSpot. Right? Because we sort of, convert it and and handle it in the middle at point, which is exciting\u003C\u002Fp>\u003Cp>Speaker 0: too. Before I swing around to the actual question, which I think required that context to a degree because I think it is a, a mental model shift, you know, for those who think about directors and what it is capable of. I think talking a little bit, you know, looking to the future, I think is was important to ask that question. The only other thing I will add to all of this, not that I wanna dig deep into it, is all these different data store types have characteristics, as Ben said. They have characteristics and strengths and weaknesses.\u003C\u002Fp>\u003Cp>And there is a I I don't know I don't I don't know if any of us know the answer to it right in this moment of of research and development. But how do you take advantage of the strengths of all of these data store qualities, rather than trying to make them all fit into one box? Or do you have a box that actually makes sense for all of them to fit into? You know, the way that you look at document databases and access those and the strengths and the weaknesses are different from a relational database. And so there's like, big brain, big brain thinking beyond beyond me.\u003C\u002Fp>\u003Cp>Speaker 1: Rich has obviously the the real answer to these questions. You know, the entire engineering team is working through solving that very difficult problem, but I think there is no answer. There's no silver bullet for how you, like, solve this problem. It's a balancing act, and you have to really think through exactly what you wanna accomplish. You don't wanna end up with, like, the lowest common denominator.\u003C\u002Fp>\u003Cp>You know, nobody wants to take the power of Aurora or Postgres, our cockroach, and say, oh, you have the power of SQLite because we're gonna support all these, and so it's that lowest common denominator. Exactly. But we all there's also the idea of progressive enhancements. There's things you can do with extensions of some of these databases, that give you more, like, geospatial capabilities. And if you don't have that installed, what happens?\u003C\u002Fp>\u003Cp>So there's so many things to consider. I think Rich and our and our engineering team are doing an amazing job of trying to solve those so that we can have the best possible experience in sort of a progressive enhancement way. But, yeah, there's I don't I I'd be blown away if Richard's just like, oh, yeah. The answer is Come on.\u003C\u002Fp>\u003Cp>Speaker 0: Well, the answer is we've got 21 more minutes.\u003C\u002Fp>\u003Cp>Speaker 2: It depends, as usual. A lot. Yeah. I mean, the way we're solving a lot of those issues now is by the the declarative model. I've mentioned it a couple of times, but that has really helped a lot in this where we're saying we wanna get data set a, and we wanna nest dataset b based on these values.\u003C\u002Fp>\u003Cp>Right? So it's kinda like defining a joint, but we're not defining exactly a joint because we're defining it in the declarative way, and it's not the SQL query. So, therefore, now in in a document data store where you don't really have native joins most often because it's, you know, in the design model, at least, depends on what vendor, of course. But then now we can say, okay. For the vendors that do have sort of a native joining capability, we can use it.\u003C\u002Fp>\u003Cp>For those that don't, we know what dataset to fetch on the left and right side and stitch it together. So for a lot of the features that we can implement sort of, like, an in the middle fallback that still allows you to do it, it's just gonna be a little bit the performance characteristics are gonna be different. Right? I think the second big piece, and this is something that has been true from the start, is that we don't wanna have any proprietary data model requirements for any of these data stores. So, therefore, even if, what would be a good example?\u003C\u002Fp>\u003Cp>I mean, the geospatial thing, I think, is a good one. If you have very, very specific geospatial querying requirements that only exist in that one vendor, at that point, use that vendor for whatever that integration is that you're trying to build in. Right? So it's like we can manage and show you the the, it's that same 80 20 idea. We can show you all of the information.\u003C\u002Fp>\u003Cp>We can support, you know, the vast majority of those filtering capabilities and the editing experience that you need for to show, like, to build a direct as that or something similar. But if for your end project, you have a hyper specific need that only exists with with an extension in that one sort of database type or something. Great. That's where the nonopinionated data modeling comes in, and then it's just like, please just use that. Right?\u003C\u002Fp>\u003Cp>Speaker 0: So that's very techy, like, where where we're going. It's very like hopefully, those watching live or on demand are like, wow. That's, you know, that's big. So with that in mind, because I do think that was required context, I'm gonna ask both of you, and I'd be interested in you answering separately almost to a degree. Ben, whether you see direct us in 3 years to 10 years, how would you be describing it in in the ideal world in that time period?\u003C\u002Fp>\u003Cp>Speaker 1: I mean, hopefully, I don't have to describe it. Hopefully, in 3 to 10 minutes\u003C\u002Fp>\u003Cp>Speaker 2: Very good.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, yeah. Not directors, obviously. Everybody who doesn't use that to manage their data, and connect it and democratize in all the fancy marketing words? When I read the question in the audience from I think it's from Benny. I actually because we were talking about marketplace, I was thinking marketplace.\u003C\u002Fp>\u003Cp>And I was gonna start talking about, you know, how we can elevate the community through allowing them to, you know, monetize their own extensions. And what a cool way for them to have, like, side hustles or primary hustles, you know, where they can, build all these awesome things really easily. That's the purpose of, you\u003C\u002Fp>\u003Cp>Speaker 0: know Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Optimizing developer experience for extensions. They can build easily, you know, with some great idea and then just quickly turn around and start, you know, selling it or giving away for free or whatever that looks like. So I think that was something big that I wanted to kinda touch on is how can we, you know, originally, this platform, you know, 2 decades ago, I think Rich was 8, and it was really just me. And I expanded to a few other people. We worked on it.\u003C\u002Fp>\u003Cp>But then Rich came in. It was the 2 of us. You know, a 2 person open source org flying out and talking to huge companies and just super excited. Then it expanded through some financing. We were good at like a team of 10.\u003C\u002Fp>\u003Cp>Now we're a team of like 30. And that's amazing, but you can only scale a company so much. And what's so exciting is you've got the maintainers, the company, then you've got the contributors, and then you've got the broader user community. And it just it gets so big where you get into 100 of thousands of people. We have to continue to focus on how we can embrace all of those people and have them contribute to this platform.\u003C\u002Fp>\u003Cp>The extension marketplace is a huge part of that, bringing everybody in and just kind of having this network effect of learning what the platform can do. Again, I built it for my own myopic ideas of, like, this is what my agency needs, and it grew to be more than that, because the idea is broader than that. But I think we still have a lot to learn about what we can do with this platform that is fed from the community. So I don't think I I would not want to know what Directus will be in 3 or 10 years. I think it's the most exciting thing is that we get to work with such a huge community to figure out what it is, you know, every day, every month.\u003C\u002Fp>\u003Cp>I think that the breadth of of the platform is such that there's so much potential to be a lot of things. There's also the danger, you know, the double edged sword of if you try to be too much, you know, too fast, too furious, what are you? You know, that can be slippery as well. How do you market that?\u003C\u002Fp>\u003Cp>Speaker 0: For sure.\u003C\u002Fp>\u003Cp>Speaker 1: Talking about yeah. It's it's anything and everything. So, I think just really, you know, judicious growth and trying to understand, like, what is it now? What are we next? Whilst having, like, some fun, like, divergent ideation around what it could be, in the future.\u003C\u002Fp>\u003Cp>This is a real this is a real, like, non answer. I'm, like, skating all around, like, what people thought I'd wanna do.\u003C\u002Fp>\u003Cp>Speaker 0: You said it, not me. You said it, not me.\u003C\u002Fp>\u003Cp>Speaker 1: Long story short, I think that if my ideal would be that it is in we'll say 10 years out. Like, that's an absurd amount of time. But 10 years ago, I was saying the same thing. In 10 years' time, I would hope that we are still completely true for the ethos of of an open platform and all the things that that brings. You know?\u003C\u002Fp>\u003Cp>The open source, the openness, the radical transparency. As a company, as a product, I hope we are still as open as we are today or even more so. And I hope that the platform is so recognized, as being a leader in whatever we have become at that point that people are just like, dude, that is there's a factor that is an amazing platform. They took the long road. You know, at that point, I'll be saying it's 30 30 years in, which is that's kinda crazy.\u003C\u002Fp>\u003Cp>Speaker 0: You'll be fully gray.\u003C\u002Fp>\u003Cp>Speaker 1: Well, well, yeah, we'll see. I hope I'm in heaven. Yeah. The goal would be just to be, like, this is the preeminent solution. That is the de facto and not resting on our laurels.\u003C\u002Fp>\u003Cp>Not saying like, oh, yeah. We made it. Like, going into that cruise control, which happens a lot. But continuing to try and stay agile and nimble, like, always trying to, like, push the envelope. Like, what is next?\u003C\u002Fp>\u003Cp>I think you can't plan for that. You know, I I I've made a joke a lot about every time I say the word future proof, I will always throw air quotes around it because you don't know what's coming. A couple years ago, nobody knew that generative AI and all these different emergent trends were gonna be what they are. We We don't know what's gonna be coming in the future. So, Yeah.\u003C\u002Fp>\u003Cp>That's my my answer, not answer.\u003C\u002Fp>\u003Cp>Speaker 0: That was good. I'll give it to you. Pass. B plus. Right?\u003C\u002Fp>\u003Cp>Speaker 2: I really hope we could start selling ice cream as to the point of the check. We're going to be an ice cream.\u003C\u002Fp>\u003Cp>Speaker 0: And then we'll rest on our laurels. Cruise control all the way, baby.\u003C\u002Fp>\u003Cp>Speaker 1: No. It's just audience. Someone had mentioned that we could become an ice cream company, which\u003C\u002Fp>\u003Cp>Speaker 0: not not that fun.\u003C\u002Fp>\u003Cp>Speaker 2: No. I think, realistically, I think, I hope directors can get to that point of ubiquitousness where it's just the obvious starting point for basically any project that deals with data by definition, any project. I think that the current strategy of treating it as a sort of the core platform being a a very modular foundation on which you can build on with extensions. You can build together the ideal x management platform for whatever you need to build. Is really just an exciting sort of way to get the future proofness.\u003C\u002Fp>\u003Cp>Right? Because at that point, it's like, oh, now if an LLM becomes the data source, that can become one of the building blocks, but the foundational thing doesn't have to pivot. Right? It's like, to be fair, explaining what that pivotal thing is has been the challenge. So I think in the next 3 years, one thing that I'm really excited about is to just help people with that sort of onboarding and understanding of what you can do with it to which, you know, marketplace is a huge thing.\u003C\u002Fp>\u003Cp>The templates we've been discussing in the future release will be a huge thing for that, where you can just get started with a sort of one click of, like, oh, I wanna use it as a blank. Bang. You know? Click a button and you go. I think that'll help the adoption that sends a lot.\u003C\u002Fp>\u003Cp>But, yeah, really, that that place for your big notice in the data space, I think, is a really achievable 10 year plan.\u003C\u002Fp>\u003Cp>Speaker 0: If I may, I know it was a question for you too, but, obviously, I run developer relations. Right? I come with a very developer focused lens. I think about the developer user, but what I hope to be the case is actually not related to that audit. Because I think that's I don't wanna say a given.\u003C\u002Fp>\u003Cp>I don't wanna be cocky, but, like, I think we are this fantastic developer tool. We're a fantastic tool for everyone, but what I really want is people who don't deem themselves to be technical enough feel like they can have ownership and access over real data and be able to glean whatever they need from that data. I think that, you know, we talk about our mission being to democratize data or make it more accessible across everyone in the organization, and I think that that is that's the thing for me that, like, I'm most excited about. I still think to a degree today, we lean a little more if it was a spectrum of, like, fully technical to developers don't know it's there at all. It's not for developers at all.\u003C\u002Fp>\u003Cp>I still think we're probably leaning a little heavier towards the developer side. And that I think as time goes on and I think extensions are a big part of this, we can start to to just be be, you know, a more ubiquitous tool to use the word you use, but not just for developers. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: To get back on what you just said, Kevin, I think if if I was looking at like the grand, like, decade out sort of landscape. But if we go to like the it was like 3 or 10 years. In 3 years, I've talked a lot to strategics and VCs, like, just companies in general about around where we might be in a shorter time frame. I think that's exactly right. Right now, we are developer tooling.\u003C\u002Fp>\u003Cp>It almost requires an engineer to provision, set up the hardware. Like, there's words that peep normal people wouldn't even they're like, Kubernetes. I don't know. What are you talking about? You know, downloading from Docker.\u003C\u002Fp>\u003Cp>But I think 3 years out is a reasonable time frame, to start looking at addressing the business user or the nontechnical user, directly. I think right now, the onboarding experience, the documentation, everything is framed around supporting and enabling engineers and developers. And I think a couple years out, there is, like, a next big milestone where we can transition. So, of course, continuing to support that, but also enabling the nontechnical users directly end to end. So they\u003C\u002Fp>\u003Cp>Speaker 0: Exactly. Exactly.\u003C\u002Fp>\u003Cp>Speaker 1: Compose them together and work in that solution and never even have to engage with the technical side, which is another bottleneck removed. And then quickly on Rich's point, I think what's really important there is the openness, that that we were kind of talking about earlier. So Rich was, kind of talking about how never mind. I I'm completely lost. It was basically, like, all the boiler plates, like, we were talking about, like, what Directus is as being this ubiquitous framework.\u003C\u002Fp>\u003Cp>And if it's coming, it's going. It's like, it's almost it's almost. It comes Every time I sleep, like, cough.\u003C\u002Fp>\u003Cp>Speaker 0: Some of them are like slice.\u003C\u002Fp>\u003Cp>Speaker 1: People think I'm over here, like, grabbing. I'm just, like, trying to cough. Anyway You should be. They're they're just cough. Anyway, yeah.\u003C\u002Fp>\u003Cp>There was there was an idea there. It's it's loud.\u003C\u002Fp>\u003Cp>Speaker 0: Another question that came into so did you get it? No.\u003C\u002Fp>\u003Cp>Speaker 1: I said back to you, Kevin.\u003C\u002Fp>\u003Cp>Speaker 0: About it. Thank you. Thank you very much, Ben. We're we're we're giving up on that, divergent thinking, dabbing vision. I I can see it's still really bothering you, and you're trying to go into the depths of your memory.\u003C\u002Fp>\u003Cp>If you get it, stop me. We can roll back around. Well, then the other questions was, well, in that time frame with it\u003C\u002Fp>\u003Cp>Speaker 1: I guess. Yes. Yes. We were talking about, like, the openness of the platform. I wanted to kinda clarify the goal.\u003C\u002Fp>\u003Cp>We were saying, like, okay. You get started with Directus. Like, it should be ubiquitous. It should be but it shouldn't be the full platform. We're not trying to be everything.\u003C\u002Fp>\u003Cp>I think the goal would be to be sort of like a standard where we stay open and you use the tools that you wanna use. We're not trying to replace everything. We're trying to give you this foundational layer. And I think that's sort of the boiler plate when you're an engineer and you're sort of building. It's like, oh, you know, I use this all the time.\u003C\u002Fp>\u003Cp>That's the first thing I do when I get started building my thing. We're not trying to build it for you. We're not trying to be the end all be all solution. We wanna make sure that we stay open. So any tools that you wanna hyper specialize with or that you wanna build or etcetera, we wanna work with those tools.\u003C\u002Fp>\u003Cp>We can't do that by saying, Directus does it all. Like, come to us. We'll help. We'll do all these different things. We just wanna open up more possibilities and make things go faster.\u003C\u002Fp>\u003Cp>And I think that's just a general mentality I wanted to clarify. You know, 10 years out, I'm not trying to say, like, everything will be built on Directus, but maybe Directus will be part of the, the suite of things that you're using, and it'll help make it more efficient using one of the many things that we'll hopefully be offering, you know, that far out. Point of clarity. And at\u003C\u002Fp>\u003Cp>Speaker 0: the end. No. No. No. And to be fair, that very mentality, actually, for the audience out of interest, is the reason our our current hackathon at the time of recording is payments.\u003C\u002Fp>\u003Cp>There are so many ideas that came out of it, and one of them was ecommerce. But there's so there's so much nuance in ecommerce as a as a, yeah, as a set of solutions that you would need to extend Directus in many ways in order to make it happen. Is it possible? Absolutely. But are there also excellent tools out there already that do the suite of ecommerce from end to end?\u003C\u002Fp>\u003Cp>Also, yes. So where do we sit there? Well, we are the we are the champion. We are the the cheerleader of this the specialized solution you have, and we can further enrich, you know, your operations, whatever that may be. And that's why it scoped down to payments.\u003C\u002Fp>\u003Cp>It went from ecommerce to payments, just out of interest for the audience. It's very much that. Can't be everything to everyone at a 100%. One of the questions that came into chat was, but not but, but, like, will it still be open source? And I feel like this is the 3rd community question time we've done, and in all, the the first one, our license, you know, change had not yet happened.\u003C\u002Fp>\u003Cp>But in the last one, it was basically the whole hour long discussion. I think we are maybe, Ben, if you give a quick summary as to, like, the the the journey and maybe the future and and where you think we'll land with this. But if I may just, seed, I think we are a really interesting business, if I may say so, because I think we are one of the I don't wanna toot our own horn, but, like, one of the pioneers in what, like, sustainable open source looks like post open, whatever that whatever the phrase is and ends up being, where you don't have big corporate backing. Like, you're not react inside of Facebook and so on. So I wonder if you could talk just very, very briefly about what we are or how you think we are and how that might influence the industry and where we\u003C\u002Fp>\u003Cp>Speaker 1: make sure. I wanna mention a second ago, like, 10 years down, I would hope that we're still honoring that that ethos. Like the idea of open openness. And the reason I'm not saying open source is because I think it's also important to be very clear in what you are and honor the history of the terms, and the definitions of the terms. We I was talking with Bruce Perrons yesterday.\u003C\u002Fp>\u003Cp>The he was on one of our live, event yesterday about you know, he's the the founder of the open source initiative, the cofounder. They have clearly defined with ten rules, like, what open source means. Our license strays on 1 or 2 of those, slightly to build makes sure that this is sustainable. And so, you know, immediately, you have to acknowledge that there's a little asterisk there. I still believe that we are open source.\u003C\u002Fp>\u003Cp>We're open source effectively for the vast majority of anybody using our software. Other people might say it's source available. There's post open and an idea that Bruce is is, sort of promoting. There's lots of things. So whatever the term is, I think the most important thing is that we stay true to again, I'll just use that word ethos, but like the idea of staying open as software.\u003C\u002Fp>\u003Cp>The code the source code should still be available. The community should still be able to go and and contribute their feedback. You know, you should be able to use the software, obviously, in development for free, in production for free, but there needs to be a give and take in terms of making sure that it is sustainable. We talked at length about it on the GitHub discussions and a number of different events around how can you make sure that you hold these big corporations accountable when they extract so much value software and they aren't contributing to the open source. You know, what through development, through testing or, you know, financially.\u003C\u002Fp>\u003Cp>So we're gonna continue to figure out the best way to do that. And just like with our license change about 10 or 11 months ago, we're gonna do it with the community because this is all us trying to figure out the best path forward together. So yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. No.\u003C\u002Fp>\u003Cp>Speaker 2: I I think the the it's it's to your point, Ben, it's the ethos of open source since the funding issue, which I think has been becoming incredibly clear across the ecosystem for open source projects at scale, that the funding needs to come from somewhere, and that is either gonna be a big corporation, owns it, funds it for mostly marketing purposes that most of the time, or, you know, you you find some other solution. But the post open source movement is very interesting and seems to very closely align with the licensing as we've sort of, now identified it for our own project.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think I mean, really, what it comes down to, if you're trying to describe what we're trying to do is we're trying to be fair, and we're trying to honor what we said we would do since the beginning. Of course, there's always gonna be, you know, the the the handful of trolls out there. Everyone has a different different definition of what is fair, but we'll just keep, you know, honing in on on what works. One thing I will say is I think it's important is hold us accountable.\u003C\u002Fp>\u003Cp>This is live and it's also being recorded. You can hold me to this in the 3 or 10 years into the future, but hold us accountable. Everyone should be held accountable for what you say in the past and what you're leaning toward. You know, we're just trying to do the right thing. You know, that's that's a difficult thing because everyone has their own opinions of what that should look like, but hold us accountable.\u003C\u002Fp>\u003Cp>If you think we're doing things the wrong way, if you have other ideas, that was, I think, the success of our license change is we said, here's the problem. Here's a few solutions as we see them. Comment on our solutions. Give your own solution. We just have to find a way through this.\u003C\u002Fp>\u003Cp>But if you think that we're straying, you know, I I used to love Google's little tagline of what was, like, do no evil or something like that. And then when that went away, replaced with, like, some longer POCs or whatever, like, that was a a bummer. Like, I wanna still have as clear of a mission and vision. I wanna stay true to that. And the only way to do it, whether, you know, whoever, you know, whatever this company looks like that this into the future, the community needs to we know the power of, you know, that amount of people.\u003C\u002Fp>\u003Cp>Just hold us accountable.\u003C\u002Fp>\u003Cp>Speaker 0: Lovely. Thank you. Thank you both for joining me for this hour. For those who are tuning in, live, this will be available on director's TV in about a week. So it'll be act it'll actually a new tile community question time.\u003C\u002Fp>\u003Cp>So very excited for that. Do check out all the other shows we have here, and we we will continue to run community question time every few months. So, if you have questions, awesome. We also have the company channel inside of our Discord, which is intended for this very purpose. Come ask us difficult questions, not about the director's project, use all the other channels for those, but about the company behind directors.\u003C\u002Fp>\u003Cp>We're a very we're a very open bunch. So, yes, thank you very much once again, Ben and Reich. Thank you to everyone for tuning in, and until next time. Bye for now.\u003C\u002Fp>\u003Cp>Speaker 1: Thank you, Kevin. Thank you.\u003C\u002Fp>","Hello, everyone, and welcome to community question time. This is actually the third one that we've done, since I started actually. But I'm really excited to be inviting our founders here, to give you the chance to ask about Directus, the company behind Directus, the future, the vision. And, don't don't give these guys any slack. Don't give these guys any slack. Come come with what's on your mind. We are a very, very transparent bunch, and this is one of those times where we open the floor. Just as we are kicking off, would you 2 like to give just a brief, description of your of your roles here? Sure. Yeah. That's easy. I'll go first. I am the CEO and founder of Directus. I created it originally 20 years ago in 2004. It's been exciting the last couple of months, and we can finally say it's been around for 20 years. Indeed. And Rich joined, 8 years ago, something like that. Oh, yeah. He's our colleague. Introduce himself as cofounder, but that's my my role, I guess. It's awesome. I'm, Greg Van Zonthe, CTO and cofounder. So where Ben created the platform in 2004, I feel like I created the platform in 2016 instead of again. You you actually create the platform. I create, like, an idea with, like, a like, a a mock up or more based on what you did. So we've we've both co created the platform in 2016 that I think is the way to describe it. Yeah. This iteration of it at least. This iteration. Yeah. Exactly. Yeah. Exactly. Yeah. So I lead the technical crew in the technical sort of implementation vision. And so for those few people who are in the chat, the point of this, event, I suppose, is that we run it periodically, and there's a chance for it to you it is a chance for you to ask, any questions that are on your mind about the wider project. That could be about the company. It could be around the future of directors as a project, or anything else really. So do pipe in. I've got chat open right here. I'll keep looking at it, and I will be facilitating those questions. Yeah. So, Ben, in the chat, I have to admit yesterday, I was so confused because I thought you were you were this, Ben. Yeah. That makes it makes way more sense now. So, while On that note, I had a, sort of a strategic, call yesterday. We were coordinating a call and there were 4 Bens on the call. I I my name's Keith. It was like, there was 5 people on the call and 4 of them were Bens, which seems pretty rare. Yeah. That's a bad thing I'll ever face, luckily. How did you how did you last names? No. Because there's 2 Ben H's. So, yeah, it got it got slippery, but, no, it was it was fine. You managed to to make it work and didn't promise to do anything on behalf of others. So there you go. I'll put the screen. Yeah. Which one? So, yes, once again, please do come with questions. But, before, you know, there are any asked, I wanna kinda ask about we just launched the marketplace beta, and I wanna talk a little bit about, Ben, your, your narrative that you use around the 3 legged store and how the marketplace fits into that and how the marketplace is actually a really critical part to the success of our project going forward? Yeah. No. That's that's a fun one to talk about. I mean, it's I I I'm obviously a big fan of metaphors and initialisms, acronyms. I I'd say that this one is more of it's not an acronym, but they have, like, the idea of a backronym where you have the thing first and then you sort of retrofit the, the metaphor afterwards. I think this is sort of that. Like, we knew that there's 3 big pieces to the to the company. And then this idea of a of a stool, like a a milkmaid stool, I think is what they're called, which has 3 legs. The idea is that they don't wobble because they have 3 legs. So it just kinda it's a nice visualization. Yeah. Of course, you want a nonwobbly product, a nonwobbly team, and a nonwobbly company. So the 3 pillars that support us, we start obviously with the core software. That's what, you know, Rich and I have been working on for for the better half of the last decade. And that is the key to everything that we're doing. But, of course, you need to deploy that and we need to make that as easy as possible. It's great to self host, but we wanted to create our cloud service, that offering. That was the 2nd leg. We have a single tenant cloud, a multi tenant cloud, and, of course, looking at all sorts of deployment options. And then the 3rd leg you know, the 3rd pillar of the company has been, sort of this if if we we have something called the 80 20 rule. It's really important for us to make sure that our software doesn't get bloated. We don't try and do too much and become like a monolithic system. We're trying to balance between sort of the the really the nice aspects of monolithic, approaches and then the microservice approach, which is great, but has a lot of complexity. We're trying to find that balance. And in this 80 20 rule, we say that the core software should satisfy 60, 80% of what you need, hopefully, really quickly, instantly. You point us at a database and you get it. But, of course, you still need to develop your full solution to a 100% and extensions and configuration and, you know, all these other pieces, your custom logic and code, that's the last 20%. And it's great to give people the modularity and extensibility of our core software. But, you know, doing that in isolation isn't nearly as much fun as being able to build an extension, send it out into a marketplace where our huge community can browse those extensions, learn more about them, maybe have ideas they didn't even know they had. It also motivates the maintainers of these extensions who are now seeing like, oh, wow. This is really popular. I'm getting all these downloads and installs and I get some visibility. It gives them more of a motivation to keep pushing and working on the, their projects, their extensions. So the Marketplace is the 3rd pillar, core, cloud and and Marketplace. And this is what really helps us get more, inspiration for use cases of the platform and get to a 100% faster. Again, leveraging not just our core software but everything else the community is generating, around the world. Right. I have a thank you for that, by the way, Ben. So, right, I have a question for you. So earlier today, you, published a blog post called what we considered when building a marketplace, and I've actually just pushed that into the little, live highlights thing at the bottom of this page. Although I'm watching the page over here and it's not appeared, so your mileage may vary, but it's on our blog. And, some of the really early feedback that's coming about the marketplace right now today has been around this, What word am I looking for? This balance between giving people the raw power to make their own choices versus limiting the ability for something to go wrong to either stop foot guns or to look out for people's security, stuff like that. This is ongoing. This is a literally ongoing thread that's happening in Discord right now. It's been really busy in our marketplace beta channel. But I wondered if you could talk a little bit about how you perceive the balance between flexibility and, you know, control versus a duty perhaps could even be the word to keep people safe and happy? Yeah. I mean, that's a great question. So, this is a topic I could easily ramble on for 2 hours. So let me try to Well, I'll I'll I'll pull you in in 5. Yeah. You have one. Go. Sure. So so the first so the the first little bit of back information that is required there is, like, where does an extension even run-in the first place? Like, where do you ship that code, and where does it run? So I've seen platforms, Shopify comes to mind, that run it off off-site completely, and everything is webhooks back and forth. Other platforms, they they rely on sort of Docker containers and try to isolate it that way. In our case, we've made the decision early days to say, okay. For extensions you built yourself for your own project, you can just tap into whatever you need from the whole system. You're basically just full access. You can do whatever you want, because you're the one creating these extensions, and therefore, you can trust yourself not to do anything stupid. That's that's sort of In theory. In theory. Theory. Right. That was sort of the original security model of extensions because they were sort of meant for, like, you know, you built your own hook to to extend your copy of directives. Right? When it comes to this marketplace now, we're making it super easy to install stuff made by somebody else, right, on both cloud or self hosted. It doesn't matter. It's all the same. But that now means that you no longer really know what's going on under the hood. Right? So for extensions in the previous paradigm, they can access effectively anything. They can access anything in the database, anything on your local machine where you're running direct is because you can, you know, access the file system, the network, any of those pieces, which then also means that if that's the thing you're installing from somebody else, at that point, all bets are off. You know, anything anything can happen. And that is, for extensions developers, really nice because you can do whatever and make it really cool. But it's for extension consumers, really scary because you really just don't know what you're up against. Right? So the the balancing act that you were talking about, the question that we have to ask ourselves is, like, when it comes to a marketplace, on which of those two sites are we gonna sort of lead? Right? Are we gonna say, well, the marketplace is just completely open ended. All bets are off. Good luck. Save yourself. It's at your own risk. Or are we gonna say, well, you know, now that you're browsing it through Directus and installing it through Directus as the end user, you have a certain expectation of trust of, like, oh, what is presented to me here in this marketplace, I can install and use knowing that it won't blow up my whole project or send all my stuff to some malicious active third party or mine a bunch of crypto or something. Right? So for this initial release, we're erring on that side of, you know, user trust and security first. Right? So we're saying, by default, we're relying on the sandbox model that we implemented, by saying anything the extension wants to do upfront in static file, it has to define the stuff it needs access to. So it's like, oh, my extension wants to talk to this exec third party API. And then, therefore, the extension can now do it through a couple of methods that we give it. Right? But, therefore, the for the end user, there's a very clear contract of this is exactly the stuff that the extension can access outside of, you know, the information that's passed in through a Hooper filter or something. And that is you know, it's it's an in it's a very tricky balance as you've seen in that marketplace beta channel, for which thanks for the folk listening in. It's always fantastic to have those discussions. Because now the question becomes like, oh, but I really wanna import, you know, I wanna import libraries to do stuff with the file system, or I wanna use third party SDKs that might not use fetch or use some other networking thing, and now those don't work, which is definitely a tricky balance. Right? Yeah. But right now, we're really erring on that side of user trust and security first and then seeing, okay, where are the improvements in that sandbox model to to unlock all of that power again, so to speak, to get it back up to that that almost full power nature of of the extensions model. And I think that's part of it too. I think this, it's really easy to look at, the sandbox x SDK and look at marketplace and the decisions made there and completely link them. Okay. The marketplace doesn't do x. That's an issue. But maybe the issue and this was actually the core the core, friction in here with these 2 competing narratives. 1 around marketplace doesn't let you install x and the other saying, well, actually, it's just that you can't build x using the sandbox SDK. And that's a solution which also honors the security model we've created. Another interesting thing though, that you said, which I will agree with and challenge, if I may, which is once you once we are providing the interface to browse and install the extensions, there is a contract to a degree that those things are trustworthy. That isn't the case in every, open listing marketplace setup. You can blow up a WordPress installation by installing a DUD plug in. So as a decision made, I will agree with you by saying I feel the same way in documentation as the reason that I will resist, at least for the time being, AI generated code snippet examples because because once it's provided through our interface, it's it's ours. I I think the WordPress example is a really, really good one that has come up in the research with the team around the subject as well, of course. Because if you install, WordPress yourself on your own server, it's completely free. Like, the old bets are off, it's do whatever mode. Right? If you use wordpress.com or, like, the hosted equivalent of, you know, a WordPress installation, now there is way more constraints in place. Right? They only is that? Some of the yeah. It it's, like, for the same reason. I I I of course, I don't know the internal decision making there, but I'm assuming it's for the same reasoning that is, like, you know, you're using, you know, WordPress in a sort of controlled environment that you're paying for. You need to be able to trust that this stays alive. And then the stuff that you're installing is trustworthy and will work the way you would expect it to. Right? Again, like, there's the self hosted versus our cloud and having to make sure that we have things really tightly scoped in cloud. But we have a multi tenant cloud and a single tenant cloud. A single tenant is our enterprise. And peep, those customers can upload custom extensions. So they do have workarounds, but we do have to make sure when you're sharing hardware in a multi tenant configuration, you know, noisy neighbors and making sure that people aren't, you know, scooting over into other people's projects. We have to keep that really tightly tightly scoped. That too. That's also that's also, of course, a decision in how we implement extensions. You know, we spoke briefly in in the past internally about different models for even offering extensibility. I believe Shopify might be a platform where as an as a extension or plug in, I'm not sure what word they use. Author, you host an external service, which is that you host it one instance of it. And then each of the instances can plug into that. And then it's a little bit the risk is different because all you're making is external HTTP requests. So there is also a decision in our implementation. Contract model then. Right? Because you know the request going to the extension, and you know that the output is in a certain format that you're gonna work with. And whatever happens in that black box over there, you don't have to care about anymore because it's hosted off-site. Right? So it's a different model, with pros and cons of their own for sure. So there's a question in the chat around, will third party modules ever be available? I think we're getting a little bit into, like, the weeds, and I want to pull that, if I may, into the marketplace beta channel where we can have, like, a more meaningful back and forth around the answer to that and the nuances of that. And thank you to Benny for kinda stepping in and giving, like, a cliff notes answer, but we can talk more about that, of course. Still on the marketplace. And by the way, I will keep going with questions until we run out of time for absence of other questions in the chat. So please do do come with them. But, talking more about the marketplace, Ben, I'm curious about how you talk about the opportunity that the marketplace brings when you're talking to potential customers, our board, our investors, people people with that level of, you know, stake in the director's project, because where it is today and where it may be in the future may not be the same. I'm kinda curious how you talk about that. Yeah. I mean, there's a couple different things. I think one is, the moment. You know, Directus has a lot it's like a back end as a service, which you it's just a database. You can do anything. You know, there's so many use cases. So, how do you give people inspiration for what it could be used for? Because I I kind of imagine it. Directus is like this big warehouse of opportunity and and potential, but people come in through one door, like one Google search or one, you know, thinking of what the platform is, whether that's a headless CMS or back end as a service. And then I think similar to something like Airtable, you know, they have 10 1,000 templates and all these, you know, options for what you can do in their sort of spreadsheet service. When you get into, like, a data service, I think that our marketplace will offer the same amount of like eye opening of you come in. It's like, oh, I'm gonna go get some extensions for making my content authoring experience better. What is this? This is like a stock you know, it's for the stock market or this could be, you know, anything any off the wall example that somebody came up with for their edge case. Now you're thinking in your head, oh, I do something similar. I wonder if I could manage that data and direct this and kind of unifies a few more systems. You know, reduce that complexity and cost. And, of course, the answer is usually yes. You can manage that in Directus, but it takes that sort of eye opening moment. And I think the marketplace is a great place to explore that potential. So that's one thing that comes up a fair amount. Another one is keeping things unopinionated. In a lot of our decks and the way we talk about Directus, we use the word unopinionated or agnostic. Again, we've built platforms that you know, Directus powers things for Fortune 10, Fortune 100 companies, you know, millions and millions of downloads, tens of millions. And you have to support their stack. You have to support their skill set. You have to support their languages. And I think it's easy to start making decisions early on that impact and restrict that flexibility for people to use their make their own decisions. And especially when we're looking at emergent things like AI that are so powerful, and you can you can really integrate and people are putting them right into the core software. Sometimes that might make sense if you are being more, opinionated in your in your builds. But we wanna make sure that we stay flexible and agnostic. And so for things like AI or things where you have to put in a proprietary API key, all that just makes sense in an in a marketplace because you can start to say, okay. This is this flavor of this functionality and you can go use a paid service or a free service. You can, you you know, integrate whatever you want. So I think that's a nice escape hatch to build in third party service integrations without us as maintainers saying this is, you know, we're we use Discord internally, you know. But obviously, lots of people use Slack, you know. You start to have to start making decisions. We wanna keep that as open as possible for as long as possible. Extension for us to do that. A question we're good. A question for you, Reich, kind of leading on from that to a degree, and actually a comment I saw in the community, I think, yesterday or the day before that peaked my interest in in preparation for this event. Someone said, you know, oh, I would like this functionality that doesn't exist today. Someone says groovy. You can build that as an extension, not an issue. And the response they had was, you know, cool. It could be built or provided as an extension, but this feels like the kind of thing that should be available in the core software. And at the same time, when we've spoken recently, I know one of your upcoming, you know, or one of your motivations generally is actually taking some of the core functionality in directors today and explicitly removing it from the core and providing it as an extension. And I'm curious how how you feel about this and and what the basis is for that thought. What does and, Benny, I see your question there. I have one more bit of context following on from this question, then I'll ask that because I think there's a bit more, to it. How do you feel about that? I think people expect that they get a call that that is more capable, but we have this extensions model and you're trying to even slim down the call. So could you talk a bit about that? So the one of the technical challenges is that we find ourselves trying to build a thing that can do anything for everybody everywhere. So, therefore, what do you include? Everything. Right? That is sort of the slippery slope that you find yourself in. So what we're sort of aiming to do now is, like, the code base of the, you know, the direct to its internals is turning into a bit of a bigger monolith because we have so many individual features and parts to it. So the strategy there is gonna be twofold, is 1, to modularize the code base. So if it's included around, it doesn't matter as an extension. It's just, like, to keep the individual responsibilities as modular as we can just for maintainability. And then the second part is, yeah, asking the question, what, you know, is Directus versus what is, you know, the the complete package that you can get with different extensions in Nextiva. Mhmm. So, for example, if you look at interfaces, we have, I think, 3 or 4 different WYSIWYGs, you know, 1 in JSON, 1 markdown, one edited JS, 1, tiny MCHTML 1. And you can ask yourselves, like, do you need all 4 for every installation for everybody out there, Or do we just ship with 1 and then have the other 3 available just as opt ins through a marketplace? Right? And that feels like a fairly logical progression to me, especially now that we have a marketplace where you can easily install those pieces. The same would go for things like layouts, right, where it's like a Kanban layout or something we ship out of the box now. Might might be relevant for some, might not be relevant for others. Maybe that just becomes, you know, an easy opt in through through a marketplace instead. Fascinating. I never use the Kanban view. It's I've seen others use it, and it looks fantastic. Just a bit it's a great example. I don't use it. So why is it shipped in every project? I think things go in both directions. I think what Rich said is is absolutely correct. You know, not getting bloat, making sure that we can really keep the core a core, and then everything else is sort of the spokes of that hub and spoke, concept. But it also could go the opposite way. You know, Rich and I have talked a lot over the years about, you know, what community pull requests look like and how that affects us as a team trying to maintain like reviewing them is, you know, we've tried to formalize it and say, go through this process before you, as an outside contributor, just send us code because it's a lot of work to review. Those are usually very opinionated, going back to keeping things unopinionated. We try to do our job on, like, the vision side of the the company to distill specific features that people request that are very price specific to what they need. How do we distill it down to an agnostic functionality? How can we make a core functionality that will work across all these use cases but that satisfies what that person was trying to build. So instead of doing a core pull request, what I'm getting at is extensions are a great a great way to pitch that. If you wanted to build an awesome extension and say this this could be great core functionality, We might not be able to have the time and resources to review core PRs and all that. You can build an extension. If it gets critical mass, we would love to have conversations. If it doesn't make sense to go into core, maybe that's the way it happens. So some things will get pulled out of core and made extensions, and then some things might turn from extensions into core functionality. I think the key there is being really cautious. You know, there's some of the big the big 3 players, that have been sort of caught taking really, really powerful community built applications and then, like, just stealing it, ripping off and building it into the, like, no s or something like that. And then the little developer who had this really great idea never gets any credit or anything. So how you take a community built extension and move it into core where we're gonna help maintain it and build it, but credit, you know, the the author and how do you do that, you know, financially or whatever that looks like. There's a lot of nuance there to figure out. But, hopefully, you know, we got a pretty awesome community. I think we could work to make the platform what it needs to be and then have the extensions be what they need to be. And that I don't know if that's gonna be one of the questions that you have. It actually it came up, Benny had it, like, where do we see the platform? Have hang on. Hang on. I have I have some context. I have some mega context here. Hang on. I think because I I think that's a necessary context. Just to tie tie up that question, though, there is something else that's really worth noting, which is Directus is a Swiss army knife, a chameleon, what however you wanna frame it. It's such a powerful tool that can be used for so many different cases, so for so many different use cases. Sometimes when people come in and think, oh, that should be core functionality, they're thinking through the sliver of use cases that they want to implement. And what we have to consider is, again, that that bigger picture, every user in every use case does this make sense as core functionality. I think that's a nuance where there is a slight difference in perception as maintainers versus users, community members, customers, and so on. So I think that's interesting. So the question of where do you see directors in 3 to 10 years, I think there was just a tiny bit of additional context, if I if I may ask right perhaps to just briefly, like, cover, which is today, Directus' data abstraction engine and, indeed, the setup of Directus is that you connect it to 1 SQL database and some asset storage and we present an interface for that one database and asset storage. But this there is ongoing work to perhaps change that change that makeup of what directors is. And I think before jumping into what we see directors be directors being in 3 to 10 years is just a brief just like a teaser even, you know, the work will happens in public, but a teaser of what that might or what this what this mad scientist project may result in because I think that influences the question that Benny asks. Sure. Yeah. I mean, first and foremost and, like, Kevin's just sliding back the velvet curtain. Like, tell us tell us talk about all the things that we're doing behind the scenes. Yeah. So, I mean, first and foremost, we've seen the the database landscape sort of diverge quite a bit. Right? So when, I mean, when I just joined the project, then it was a long time ago, but it was we just we only supported MySQL 5.6, I think it was at the time. And that was it. That was the only thing I ran on. Right? And that was enough at the time because it was really people needed a database. You had MySQL. That's that was just the name of the game. Right? There was no other real the the rest of it wasn't up and coming enough yet. Then when we rebuilt that in sort of the current paradigm that you're seeing, we were like, okay. Well, let's try to abstract that out to just be SQL so it becomes any SQL database instead of just MySQL. Because there's some differences, but they're generally similar enough where we can just say, okay. I'm just gonna support any of those SQL databases. Right? Then now, you know, early 2024, we're seeing more and more sort of SQL like or SQL compatible ish, vendors show up left and right that are getting increasingly popular. You know, think about PlanetScale or Snowflake or or those types of services, that are, you know, not always directly SQL compatible, but also very close enough to a point where it's like, why wouldn't it work? Because, you know, the paradigms are the same. So part of the the first exercise was thinking about, okay, how can we include those in sort of our SQL support as well? So it's SQL like, but then we have certain features that work, don't work maybe. And then while we're designing that, the question really became, okay, what is just a data source in the first place? Right? Because what is really the difference between, a structured REST API versus, a relational database versus a data warehouse that stores stuff relationally? At the end of the day, you're all talking about, you know, data in in a very similar format in all these different sources. Right? So that is really what we started focusing on at that point, with how can we abstract that away in a format that is this is getting a little technical now, but that is a declarative base. So you can declare the data you wanna get out of it, then have a plug in ecosystem that is fetching that information from individual vendors, and then get it back in a known format. And that is really the data abstraction project. Right? That'll unlock, you know, a bunch of other interesting angles that we never thought about before just by approaching it in that direction. But that is that is really the the the core. Kinda go back to the core principles of the company is, you know, taking sort of expanding, from MySQL to Postgres to, you know, down the SQL line and now continuing to distill it down. Like, that is part of this, like, vision team is how do we continue to be less opinionated? Like, there's other data stores that people use. It's great to quickly support something like CockroachDB from a distributed side. But, you know, the data lakes and all the, sort of LMs infected. Like, there's so much that we could be managing and sort of make those part of the marketplace. You know, how do we make those atomic units of connectors for different data store, data stores that are available? Like, it's great to have the browse, manage, visualize on the app and API side, but we also have to think of the data coming in and how we can improve that over time. And Rich didn't even mention the the f word in that, which I'm surprised. It was I was about to do it. I was about to do it too. To go now. 3, 2, 1. Yeah. Oh, I thought I thought we were about to. I thought we were gonna do it. I'll I'll continue that train of thought too because it it started with what is a data source. And then the second piece to that big thing is, like, where does that data live? And the answer is it lives in all sorts of places at the same time all the time, for good reasons. Right? So we've seen a lot of piece like, a lot of software try to sort of synchronize it back to a centralized place and then manage that and then try to use, you know, integrations to get it synced back out. That has been historically, you know, very popular. We're seeing some tools, pop up now. They're trying to sort of get that data connected through GraphQL specifically because you're dealing with known schemas on both sides. Excuse me. So the question now became okay. If we have this sort of very agnostic declarative data fetching engine that we're building, how does that tie into federating the big f word, across those different data stores at the same time? Right? So as if you're declaring one data structure to come out of it, but the data is like, the underlying data itself is just stored in different places, how can we get that out efficiently, and return it in the way that you need it, rather than having to treat it as disparate data sources. And in reality, like, people do sync data back using directors today. They'll have a primary database, which will be the thing they connect, and then we'll use our automation features code or no code, whatever that may look like, to bring in and sync data from then, you know, additional sources. Because And, again, perfectly valid because, I mean, think of the systems that have just gone offline in the last 48 hours. Like, big systems go offline. It's nice to have sort of a local, like, your own database synced where you can still pull that data in and you've got the results. That. Yeah. Yeah. I know I didn't thought about that. Talking about. Like, you can't always pull it in and say this is we talk a lot about single source of truth in the database. That's what we manage. There's no direct as magic that happens in between. It's the database. That's the magic. We give you access. But one database isn't enough usually. We manage all types of data. When you look at something like customer data, you can have your customers in Stripe. You know, you have it in HubSpot or Salesforce. You've got it in probably some sort of relational database for your SaaS or whatever you're building. And you've probably got some thrown over in, say, cold storage and a data lake. You know? All that customer data needs to be accessible so that you can find the value. But you can't just pull it all like, those services are all used in different ways for a reason. So managing the data where it lives in situ is important. And the only way to really do that is through that, not the 4 letter f word, but the 10 letter f word that are able. And that's why it's so exciting. I counted. Yeah. He sat there counting. And we now know that's what was happening in the last few minutes. Anyway, so it's pretty it's really exciting thinking of, like, what is the future of, you know, data in because we can argue so much on the data outside or the IO. Yeah. The other thing is, exciting to me there is because it becomes a declarative system, we know what the output format is, and we know what the input format is. So it also means that getting that data from one place to the next becomes, you know, a a possibility now because the data format coming out of Salesforce is now gonna be compatible with the data format going into a HubSpot. Right? Because we sort of, convert it and and handle it in the middle at point, which is exciting too. Before I swing around to the actual question, which I think required that context to a degree because I think it is a, a mental model shift, you know, for those who think about directors and what it is capable of. I think talking a little bit, you know, looking to the future, I think is was important to ask that question. The only other thing I will add to all of this, not that I wanna dig deep into it, is all these different data store types have characteristics, as Ben said. They have characteristics and strengths and weaknesses. And there is a I I don't know I don't I don't know if any of us know the answer to it right in this moment of of research and development. But how do you take advantage of the strengths of all of these data store qualities, rather than trying to make them all fit into one box? Or do you have a box that actually makes sense for all of them to fit into? You know, the way that you look at document databases and access those and the strengths and the weaknesses are different from a relational database. And so there's like, big brain, big brain thinking beyond beyond me. Rich has obviously the the real answer to these questions. You know, the entire engineering team is working through solving that very difficult problem, but I think there is no answer. There's no silver bullet for how you, like, solve this problem. It's a balancing act, and you have to really think through exactly what you wanna accomplish. You don't wanna end up with, like, the lowest common denominator. You know, nobody wants to take the power of Aurora or Postgres, our cockroach, and say, oh, you have the power of SQLite because we're gonna support all these, and so it's that lowest common denominator. Exactly. But we all there's also the idea of progressive enhancements. There's things you can do with extensions of some of these databases, that give you more, like, geospatial capabilities. And if you don't have that installed, what happens? So there's so many things to consider. I think Rich and our and our engineering team are doing an amazing job of trying to solve those so that we can have the best possible experience in sort of a progressive enhancement way. But, yeah, there's I don't I I'd be blown away if Richard's just like, oh, yeah. The answer is Come on. Well, the answer is we've got 21 more minutes. It depends, as usual. A lot. Yeah. I mean, the way we're solving a lot of those issues now is by the the declarative model. I've mentioned it a couple of times, but that has really helped a lot in this where we're saying we wanna get data set a, and we wanna nest dataset b based on these values. Right? So it's kinda like defining a joint, but we're not defining exactly a joint because we're defining it in the declarative way, and it's not the SQL query. So, therefore, now in in a document data store where you don't really have native joins most often because it's, you know, in the design model, at least, depends on what vendor, of course. But then now we can say, okay. For the vendors that do have sort of a native joining capability, we can use it. For those that don't, we know what dataset to fetch on the left and right side and stitch it together. So for a lot of the features that we can implement sort of, like, an in the middle fallback that still allows you to do it, it's just gonna be a little bit the performance characteristics are gonna be different. Right? I think the second big piece, and this is something that has been true from the start, is that we don't wanna have any proprietary data model requirements for any of these data stores. So, therefore, even if, what would be a good example? I mean, the geospatial thing, I think, is a good one. If you have very, very specific geospatial querying requirements that only exist in that one vendor, at that point, use that vendor for whatever that integration is that you're trying to build in. Right? So it's like we can manage and show you the the, it's that same 80 20 idea. We can show you all of the information. We can support, you know, the vast majority of those filtering capabilities and the editing experience that you need for to show, like, to build a direct as that or something similar. But if for your end project, you have a hyper specific need that only exists with with an extension in that one sort of database type or something. Great. That's where the nonopinionated data modeling comes in, and then it's just like, please just use that. Right? So that's very techy, like, where where we're going. It's very like hopefully, those watching live or on demand are like, wow. That's, you know, that's big. So with that in mind, because I do think that was required context, I'm gonna ask both of you, and I'd be interested in you answering separately almost to a degree. Ben, whether you see direct us in 3 years to 10 years, how would you be describing it in in the ideal world in that time period? I mean, hopefully, I don't have to describe it. Hopefully, in 3 to 10 minutes Very good. Oh, yeah. Not directors, obviously. Everybody who doesn't use that to manage their data, and connect it and democratize in all the fancy marketing words? When I read the question in the audience from I think it's from Benny. I actually because we were talking about marketplace, I was thinking marketplace. And I was gonna start talking about, you know, how we can elevate the community through allowing them to, you know, monetize their own extensions. And what a cool way for them to have, like, side hustles or primary hustles, you know, where they can, build all these awesome things really easily. That's the purpose of, you know Yeah. Optimizing developer experience for extensions. They can build easily, you know, with some great idea and then just quickly turn around and start, you know, selling it or giving away for free or whatever that looks like. So I think that was something big that I wanted to kinda touch on is how can we, you know, originally, this platform, you know, 2 decades ago, I think Rich was 8, and it was really just me. And I expanded to a few other people. We worked on it. But then Rich came in. It was the 2 of us. You know, a 2 person open source org flying out and talking to huge companies and just super excited. Then it expanded through some financing. We were good at like a team of 10. Now we're a team of like 30. And that's amazing, but you can only scale a company so much. And what's so exciting is you've got the maintainers, the company, then you've got the contributors, and then you've got the broader user community. And it just it gets so big where you get into 100 of thousands of people. We have to continue to focus on how we can embrace all of those people and have them contribute to this platform. The extension marketplace is a huge part of that, bringing everybody in and just kind of having this network effect of learning what the platform can do. Again, I built it for my own myopic ideas of, like, this is what my agency needs, and it grew to be more than that, because the idea is broader than that. But I think we still have a lot to learn about what we can do with this platform that is fed from the community. So I don't think I I would not want to know what Directus will be in 3 or 10 years. I think it's the most exciting thing is that we get to work with such a huge community to figure out what it is, you know, every day, every month. I think that the breadth of of the platform is such that there's so much potential to be a lot of things. There's also the danger, you know, the double edged sword of if you try to be too much, you know, too fast, too furious, what are you? You know, that can be slippery as well. How do you market that? For sure. Talking about yeah. It's it's anything and everything. So, I think just really, you know, judicious growth and trying to understand, like, what is it now? What are we next? Whilst having, like, some fun, like, divergent ideation around what it could be, in the future. This is a real this is a real, like, non answer. I'm, like, skating all around, like, what people thought I'd wanna do. You said it, not me. You said it, not me. Long story short, I think that if my ideal would be that it is in we'll say 10 years out. Like, that's an absurd amount of time. But 10 years ago, I was saying the same thing. In 10 years' time, I would hope that we are still completely true for the ethos of of an open platform and all the things that that brings. You know? The open source, the openness, the radical transparency. As a company, as a product, I hope we are still as open as we are today or even more so. And I hope that the platform is so recognized, as being a leader in whatever we have become at that point that people are just like, dude, that is there's a factor that is an amazing platform. They took the long road. You know, at that point, I'll be saying it's 30 30 years in, which is that's kinda crazy. You'll be fully gray. Well, well, yeah, we'll see. I hope I'm in heaven. Yeah. The goal would be just to be, like, this is the preeminent solution. That is the de facto and not resting on our laurels. Not saying like, oh, yeah. We made it. Like, going into that cruise control, which happens a lot. But continuing to try and stay agile and nimble, like, always trying to, like, push the envelope. Like, what is next? I think you can't plan for that. You know, I I I've made a joke a lot about every time I say the word future proof, I will always throw air quotes around it because you don't know what's coming. A couple years ago, nobody knew that generative AI and all these different emergent trends were gonna be what they are. We We don't know what's gonna be coming in the future. So, Yeah. That's my my answer, not answer. That was good. I'll give it to you. Pass. B plus. Right? I really hope we could start selling ice cream as to the point of the check. We're going to be an ice cream. And then we'll rest on our laurels. Cruise control all the way, baby. No. It's just audience. Someone had mentioned that we could become an ice cream company, which not not that fun. No. I think, realistically, I think, I hope directors can get to that point of ubiquitousness where it's just the obvious starting point for basically any project that deals with data by definition, any project. I think that the current strategy of treating it as a sort of the core platform being a a very modular foundation on which you can build on with extensions. You can build together the ideal x management platform for whatever you need to build. Is really just an exciting sort of way to get the future proofness. Right? Because at that point, it's like, oh, now if an LLM becomes the data source, that can become one of the building blocks, but the foundational thing doesn't have to pivot. Right? It's like, to be fair, explaining what that pivotal thing is has been the challenge. So I think in the next 3 years, one thing that I'm really excited about is to just help people with that sort of onboarding and understanding of what you can do with it to which, you know, marketplace is a huge thing. The templates we've been discussing in the future release will be a huge thing for that, where you can just get started with a sort of one click of, like, oh, I wanna use it as a blank. Bang. You know? Click a button and you go. I think that'll help the adoption that sends a lot. But, yeah, really, that that place for your big notice in the data space, I think, is a really achievable 10 year plan. If I may, I know it was a question for you too, but, obviously, I run developer relations. Right? I come with a very developer focused lens. I think about the developer user, but what I hope to be the case is actually not related to that audit. Because I think that's I don't wanna say a given. I don't wanna be cocky, but, like, I think we are this fantastic developer tool. We're a fantastic tool for everyone, but what I really want is people who don't deem themselves to be technical enough feel like they can have ownership and access over real data and be able to glean whatever they need from that data. I think that, you know, we talk about our mission being to democratize data or make it more accessible across everyone in the organization, and I think that that is that's the thing for me that, like, I'm most excited about. I still think to a degree today, we lean a little more if it was a spectrum of, like, fully technical to developers don't know it's there at all. It's not for developers at all. I still think we're probably leaning a little heavier towards the developer side. And that I think as time goes on and I think extensions are a big part of this, we can start to to just be be, you know, a more ubiquitous tool to use the word you use, but not just for developers. Yeah. To get back on what you just said, Kevin, I think if if I was looking at like the grand, like, decade out sort of landscape. But if we go to like the it was like 3 or 10 years. In 3 years, I've talked a lot to strategics and VCs, like, just companies in general about around where we might be in a shorter time frame. I think that's exactly right. Right now, we are developer tooling. It almost requires an engineer to provision, set up the hardware. Like, there's words that peep normal people wouldn't even they're like, Kubernetes. I don't know. What are you talking about? You know, downloading from Docker. But I think 3 years out is a reasonable time frame, to start looking at addressing the business user or the nontechnical user, directly. I think right now, the onboarding experience, the documentation, everything is framed around supporting and enabling engineers and developers. And I think a couple years out, there is, like, a next big milestone where we can transition. So, of course, continuing to support that, but also enabling the nontechnical users directly end to end. So they Exactly. Exactly. Compose them together and work in that solution and never even have to engage with the technical side, which is another bottleneck removed. And then quickly on Rich's point, I think what's really important there is the openness, that that we were kind of talking about earlier. So Rich was, kind of talking about how never mind. I I'm completely lost. It was basically, like, all the boiler plates, like, we were talking about, like, what Directus is as being this ubiquitous framework. And if it's coming, it's going. It's like, it's almost it's almost. It comes Every time I sleep, like, cough. Some of them are like slice. People think I'm over here, like, grabbing. I'm just, like, trying to cough. Anyway You should be. They're they're just cough. Anyway, yeah. There was there was an idea there. It's it's loud. Another question that came into so did you get it? No. I said back to you, Kevin. About it. Thank you. Thank you very much, Ben. We're we're we're giving up on that, divergent thinking, dabbing vision. I I can see it's still really bothering you, and you're trying to go into the depths of your memory. If you get it, stop me. We can roll back around. Well, then the other questions was, well, in that time frame with it I guess. Yes. Yes. We were talking about, like, the openness of the platform. I wanted to kinda clarify the goal. We were saying, like, okay. You get started with Directus. Like, it should be ubiquitous. It should be but it shouldn't be the full platform. We're not trying to be everything. I think the goal would be to be sort of like a standard where we stay open and you use the tools that you wanna use. We're not trying to replace everything. We're trying to give you this foundational layer. And I think that's sort of the boiler plate when you're an engineer and you're sort of building. It's like, oh, you know, I use this all the time. That's the first thing I do when I get started building my thing. We're not trying to build it for you. We're not trying to be the end all be all solution. We wanna make sure that we stay open. So any tools that you wanna hyper specialize with or that you wanna build or etcetera, we wanna work with those tools. We can't do that by saying, Directus does it all. Like, come to us. We'll help. We'll do all these different things. We just wanna open up more possibilities and make things go faster. And I think that's just a general mentality I wanted to clarify. You know, 10 years out, I'm not trying to say, like, everything will be built on Directus, but maybe Directus will be part of the, the suite of things that you're using, and it'll help make it more efficient using one of the many things that we'll hopefully be offering, you know, that far out. Point of clarity. And at the end. No. No. No. And to be fair, that very mentality, actually, for the audience out of interest, is the reason our our current hackathon at the time of recording is payments. There are so many ideas that came out of it, and one of them was ecommerce. But there's so there's so much nuance in ecommerce as a as a, yeah, as a set of solutions that you would need to extend Directus in many ways in order to make it happen. Is it possible? Absolutely. But are there also excellent tools out there already that do the suite of ecommerce from end to end? Also, yes. So where do we sit there? Well, we are the we are the champion. We are the the cheerleader of this the specialized solution you have, and we can further enrich, you know, your operations, whatever that may be. And that's why it scoped down to payments. It went from ecommerce to payments, just out of interest for the audience. It's very much that. Can't be everything to everyone at a 100%. One of the questions that came into chat was, but not but, but, like, will it still be open source? And I feel like this is the 3rd community question time we've done, and in all, the the first one, our license, you know, change had not yet happened. But in the last one, it was basically the whole hour long discussion. I think we are maybe, Ben, if you give a quick summary as to, like, the the the journey and maybe the future and and where you think we'll land with this. But if I may just, seed, I think we are a really interesting business, if I may say so, because I think we are one of the I don't wanna toot our own horn, but, like, one of the pioneers in what, like, sustainable open source looks like post open, whatever that whatever the phrase is and ends up being, where you don't have big corporate backing. Like, you're not react inside of Facebook and so on. So I wonder if you could talk just very, very briefly about what we are or how you think we are and how that might influence the industry and where we make sure. I wanna mention a second ago, like, 10 years down, I would hope that we're still honoring that that ethos. Like the idea of open openness. And the reason I'm not saying open source is because I think it's also important to be very clear in what you are and honor the history of the terms, and the definitions of the terms. We I was talking with Bruce Perrons yesterday. The he was on one of our live, event yesterday about you know, he's the the founder of the open source initiative, the cofounder. They have clearly defined with ten rules, like, what open source means. Our license strays on 1 or 2 of those, slightly to build makes sure that this is sustainable. And so, you know, immediately, you have to acknowledge that there's a little asterisk there. I still believe that we are open source. We're open source effectively for the vast majority of anybody using our software. Other people might say it's source available. There's post open and an idea that Bruce is is, sort of promoting. There's lots of things. So whatever the term is, I think the most important thing is that we stay true to again, I'll just use that word ethos, but like the idea of staying open as software. The code the source code should still be available. The community should still be able to go and and contribute their feedback. You know, you should be able to use the software, obviously, in development for free, in production for free, but there needs to be a give and take in terms of making sure that it is sustainable. We talked at length about it on the GitHub discussions and a number of different events around how can you make sure that you hold these big corporations accountable when they extract so much value software and they aren't contributing to the open source. You know, what through development, through testing or, you know, financially. So we're gonna continue to figure out the best way to do that. And just like with our license change about 10 or 11 months ago, we're gonna do it with the community because this is all us trying to figure out the best path forward together. So yeah. Yeah. No. I I think the the it's it's to your point, Ben, it's the ethos of open source since the funding issue, which I think has been becoming incredibly clear across the ecosystem for open source projects at scale, that the funding needs to come from somewhere, and that is either gonna be a big corporation, owns it, funds it for mostly marketing purposes that most of the time, or, you know, you you find some other solution. But the post open source movement is very interesting and seems to very closely align with the licensing as we've sort of, now identified it for our own project. Yeah. I think I mean, really, what it comes down to, if you're trying to describe what we're trying to do is we're trying to be fair, and we're trying to honor what we said we would do since the beginning. Of course, there's always gonna be, you know, the the the handful of trolls out there. Everyone has a different different definition of what is fair, but we'll just keep, you know, honing in on on what works. One thing I will say is I think it's important is hold us accountable. This is live and it's also being recorded. You can hold me to this in the 3 or 10 years into the future, but hold us accountable. Everyone should be held accountable for what you say in the past and what you're leaning toward. You know, we're just trying to do the right thing. You know, that's that's a difficult thing because everyone has their own opinions of what that should look like, but hold us accountable. If you think we're doing things the wrong way, if you have other ideas, that was, I think, the success of our license change is we said, here's the problem. Here's a few solutions as we see them. Comment on our solutions. Give your own solution. We just have to find a way through this. But if you think that we're straying, you know, I I used to love Google's little tagline of what was, like, do no evil or something like that. And then when that went away, replaced with, like, some longer POCs or whatever, like, that was a a bummer. Like, I wanna still have as clear of a mission and vision. I wanna stay true to that. And the only way to do it, whether, you know, whoever, you know, whatever this company looks like that this into the future, the community needs to we know the power of, you know, that amount of people. Just hold us accountable. Lovely. Thank you. Thank you both for joining me for this hour. For those who are tuning in, live, this will be available on director's TV in about a week. So it'll be act it'll actually a new tile community question time. So very excited for that. Do check out all the other shows we have here, and we we will continue to run community question time every few months. So, if you have questions, awesome. We also have the company channel inside of our Discord, which is intended for this very purpose. Come ask us difficult questions, not about the director's project, use all the other channels for those, but about the company behind directors. We're a very we're a very open bunch. So, yes, thank you very much once again, Ben and Reich. Thank you to everyone for tuning in, and until next time. Bye for now. Thank you, Kevin. Thank you.",[4021,4022,4023],"966008cd-a047-4fbb-88c0-a1a3647c16c5","b935c153-2aba-44f2-9e73-281ee5583d33","c0b763b9-dfdf-434d-9b7d-cb4ea8868af2",[],{"year":2380,"number":909,"id":4026,"episodes":4027,"show":4029},"29867ef9-edaa-4066-ad89-4c93969f949c",[4028,4001],"18b823d0-ba5a-43ff-ab82-a074d11db574",{"title":4030,"slug":4031},"Community Question Time","community-question-time",{"id":3603,"slug":4033,"vimeo_id":4034,"description":4035,"tile":4036,"length":129,"resources":12,"people":4037,"episode_number":13,"published":4039,"title":4040,"video_transcript_html":4041,"video_transcript_text":4042,"content":12,"seo":12,"status":19,"episode_people":4043,"recommendations":4045,"season":4046},"conditional-fields","903063878","Learn how to conditionally show and hide fields inside of Directus Editor.","d4c79052-82e1-4e08-8461-a5ac9a4bf9a1",[4038],{"name":3590,"url":3591},"2024-03-15","Conditional Fields ","\u003Cp>Speaker 0: Hi. Welcome back to another episode of Short Hops where I give you a couple of my best directest tips and tricks. I'm your host, Brian Gillespie, and today we are talking about conditional fields. This is one of my favorite features. I know I say that a lot, but, as a a pet peeve of mine, I hate presenting information or or fields for somebody to fill out that aren't relevant or aren't necessary.\u003C\u002Fp>\u003Cp>And the conditional fields inside Directus allow you to create a better user experience for your content editors, your marketers, anybody who's using the actual Directus app. And typically, that could be nontechnical folks, and they will thank you if you build a great experience using these conditional fields. So let's take a look at a pretty common scenario. You know, we've got posts on our site. These could be resources, whatever.\u003C\u002Fp>\u003Cp>We've got different types of posts. And depending on the type of post, we may not need things like a video URL or, if we're doing a case study, we probably wanna have company name or contact name, company URL. But in all other scenarios, it's not necessary to present this. So let's control this. We'll go into our data model and for our posts, let's just create a new radio button called post type.\u003C\u002Fp>\u003Cp>We'll give a couple choices. Right? We have a blog or an article. We have video. We have a case study.\u003C\u002Fp>\u003Cp>Great. Okay. So it's for score study. Perfect. Okay.\u003C\u002Fp>\u003Cp>So now I hit Save and we have a post type. Maybe we sort this out. Cool. Great job, Bryant. Let's pat you on the back.\u003C\u002Fp>\u003Cp>And we pick blog or video, post type, case study. None of this actually changes. Right? So this is where our custom fields come into play. This video URL, let's go in and go to the field settings.\u003C\u002Fp>\u003Cp>We're gonna hide this on the detail view. We don't need it, until we do. So let's show when type equals video. So the name here is just adding some additional context to this condition, and we're gonna set up a rule for this. So when the post type equals video, then we are going to unhide that.\u003C\u002Fp>\u003Cp>So we're gonna make sure hidden on detail is unchecked. And just like that, we can go in and now for our blog post, you can see that video field is hidden, but if I change it to the post type to video, now we can see we've got that field for video URL. Some other ways that I use this, you can see I have a group of fields here. We could go in and add a detail detail group for this. So let's just call this case study details.\u003C\u002Fp>\u003Cp>Right? And if I go into the conditions for this, maybe we'll we'll hide this by default and show when type equals case study. So give it a name just so we have that. Again we'll set this condition or the rule for this when the post type equals case study, we are going to show this item inside the form. And then last but not least, I'll just drag my items into the actual form.\u003C\u002Fp>\u003Cp>So if I use this grouping, I I don't necessarily have to add the conditions to these individual fields because they're part of that group. So now if I scroll and for any blog posts, you see that we just have content image for a video now that video URL pops up, but for a case study, I now have this collapsible case study details section. One other quick way that I use this. A common scenario is that we need to disable editing after a particular item is created inside a collection. So if I had some fields that I wanted to prevent the user from editing after I created an item, I could do the conditional fields for that.\u003C\u002Fp>\u003Cp>So let's just say I wanted to prevent the user from, let's add a field for date published. That'll be a good example. Date published, cool, and let's just pretend that if there is a this has already been published or there's a date published, I want to prevent people from editing the slug of this particular item. So I can go into our conditions and if date published, do not allow editing. So I can go in and set date published where is that new field that we just created.\u003C\u002Fp>\u003Cp>If date value isn't null, we are going to disable editing this value. Alright. So let's save that, And on this post now we have a date published, but if I go in and choose a date published, you could see that now I cannot edit that slug, which, you know, could help prevent, any 404 errors in the future. That's just one of the ways that you can use conditional fields. I hope this is tremendously helpful for you.\u003C\u002Fp>\u003Cp>Keep the questions coming. I love seeing these pop up in the community. Stay tuned for the next episode.\u003C\u002Fp>","Hi. Welcome back to another episode of Short Hops where I give you a couple of my best directest tips and tricks. I'm your host, Brian Gillespie, and today we are talking about conditional fields. This is one of my favorite features. I know I say that a lot, but, as a a pet peeve of mine, I hate presenting information or or fields for somebody to fill out that aren't relevant or aren't necessary. And the conditional fields inside Directus allow you to create a better user experience for your content editors, your marketers, anybody who's using the actual Directus app. And typically, that could be nontechnical folks, and they will thank you if you build a great experience using these conditional fields. So let's take a look at a pretty common scenario. You know, we've got posts on our site. These could be resources, whatever. We've got different types of posts. And depending on the type of post, we may not need things like a video URL or, if we're doing a case study, we probably wanna have company name or contact name, company URL. But in all other scenarios, it's not necessary to present this. So let's control this. We'll go into our data model and for our posts, let's just create a new radio button called post type. We'll give a couple choices. Right? We have a blog or an article. We have video. We have a case study. Great. Okay. So it's for score study. Perfect. Okay. So now I hit Save and we have a post type. Maybe we sort this out. Cool. Great job, Bryant. Let's pat you on the back. And we pick blog or video, post type, case study. None of this actually changes. Right? So this is where our custom fields come into play. This video URL, let's go in and go to the field settings. We're gonna hide this on the detail view. We don't need it, until we do. So let's show when type equals video. So the name here is just adding some additional context to this condition, and we're gonna set up a rule for this. So when the post type equals video, then we are going to unhide that. So we're gonna make sure hidden on detail is unchecked. And just like that, we can go in and now for our blog post, you can see that video field is hidden, but if I change it to the post type to video, now we can see we've got that field for video URL. Some other ways that I use this, you can see I have a group of fields here. We could go in and add a detail detail group for this. So let's just call this case study details. Right? And if I go into the conditions for this, maybe we'll we'll hide this by default and show when type equals case study. So give it a name just so we have that. Again we'll set this condition or the rule for this when the post type equals case study, we are going to show this item inside the form. And then last but not least, I'll just drag my items into the actual form. So if I use this grouping, I I don't necessarily have to add the conditions to these individual fields because they're part of that group. So now if I scroll and for any blog posts, you see that we just have content image for a video now that video URL pops up, but for a case study, I now have this collapsible case study details section. One other quick way that I use this. A common scenario is that we need to disable editing after a particular item is created inside a collection. So if I had some fields that I wanted to prevent the user from editing after I created an item, I could do the conditional fields for that. So let's just say I wanted to prevent the user from, let's add a field for date published. That'll be a good example. Date published, cool, and let's just pretend that if there is a this has already been published or there's a date published, I want to prevent people from editing the slug of this particular item. So I can go into our conditions and if date published, do not allow editing. So I can go in and set date published where is that new field that we just created. If date value isn't null, we are going to disable editing this value. Alright. So let's save that, And on this post now we have a date published, but if I go in and choose a date published, you could see that now I cannot edit that slug, which, you know, could help prevent, any 404 errors in the future. That's just one of the ways that you can use conditional fields. I hope this is tremendously helpful for you. Keep the questions coming. I love seeing these pop up in the community. Stay tuned for the next episode.",[4044],"d068cba1-d3f0-4ff8-b800-9a0e65620674",[],{"year":978,"number":3600,"id":3601,"episodes":4047,"show":4048},[3603,3604,3605,3606,3583],{"title":2161,"slug":2162},{"id":2437,"slug":4050,"vimeo_id":4051,"description":4052,"tile":4053,"length":2422,"resources":12,"people":4054,"episode_number":177,"published":4062,"title":4063,"video_transcript_html":4064,"video_transcript_text":4065,"content":12,"seo":12,"status":19,"episode_people":4066,"recommendations":4070,"season":4071},"open-source-outlook","923062947","A discussion on the past, present, and future of open source, including what it means to be \"Post-Open\" and build sustainable businesses.","863084c0-77cc-4b22-af18-9d7af9ca6165",[4055,4056,4059],{"name":4012,"url":4013},{"name":4057,"url":4058},"Steven Tey","https:\u002F\u002Ftwitter.com\u002Fsteventey",{"name":4060,"url":4061},"Bruce Perens","https:\u002F\u002Ftwitter.com\u002FBrucePerens","2024-03-14","Open Source Outlook","\u003Cp>Speaker 0: Let us kick it off. So this is the second episode of Bridging Bytes. This time, we have we have bridged from the last episode of the intersection of design and development, and now we are talking about, the open source Outlook. So, obviously, a very important topic, something that's near and dear to my heart, having worked on Directus now for 2 decades, over half of that as an open source maintainer. And then we have some amazing, guests here that are gonna be leading the chart in terms of this conversation.\u003C\u002Fp>\u003Cp>So I'll be moderating the the chat. I can give a quick background, and then I'll introduce our 2 our 2 guests. So, my name is Ben Haines. I am CEO and founder of Directus, the open source, data management platform. And I've been I built the platform originally in 2004, and we actually open sourced it in around 2010, 2011.\u003C\u002Fp>\u003Cp>So we'll get into more of the history of of what we did in our open source journey. But, I'd like to next introduce Bruce Perrons, who is sort of the the godfather, I think we were calling him, of open source. Bruce actually created the open source initiative, which I think was in 1997. Give her we'll let him\u003C\u002Fp>\u003Cp>Speaker 1: 87.\u003C\u002Fp>\u003Cp>Speaker 0: When was it?\u003C\u002Fp>\u003Cp>Speaker 1: 1987 was the creation of the open source initiative in the open source definition. And the document actually came about, I think, 8 months before, it was then the Debian free software guidelines.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. Obviously, there's some interesting aspects of using the word free versus the term that was actually chosen, which was open source. But yeah. So we have Bruce Perrons here, who is the cofounder of the Open Source Initiative. And, you know, great thanks to your work, I believe, within, Debbie.\u003C\u002Fp>\u003Cp>Basically, Bruce, why don't you give a quick, overview of your background and and and what brought you here?\u003C\u002Fp>\u003Cp>Speaker 1: I I'm sorry. It really was 97. I'm just going over the calendar in my head. So, 87 was actually when I got to Pixar, and, I started as a filmmaker, was one of the founders of computer graphic 3 d feature film animation, and worked at Pixar and their predecessor in New York for 19 years in total. Have a credit if you go on IMDB on a couple of films.\u003C\u002Fp>\u003Cp>And in, 97 we'll get that right now. I wanted to define what Debian, included, and we created something called the Debian free software guidelines. And then a little later, a group met and said, well, let's market free software to business and call it open source. And, so that happened. And I was called up the next day and I said, well, great.\u003C\u002Fp>\u003Cp>I'll take my Debian free software guidelines and make them the open source definition. And so there we go. It's been a long time, and it's been incredibly successful. If I had said to you, back then, we're going to, give away our software and completely take over the industry by doing it. And we're going to make an encyclopedia, and it's going to be the first thing to put a Microsoft product and Carta out of business.\u003C\u002Fp>\u003Cp>If I had told you that, you would have just said, well, I wanna smoke that dope too. So, so it it's been a long strange trip. Hasn't it?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Well, I here I am sitting here, you know, so excited to be 20 years in, to the entirety of my project, Directus. And you've got, you know, 20 years under your belt just, you know, with, you know, elements of your your history. So thank you so much, Bruce. It's really exciting to have you here.\u003C\u002Fp>\u003Cp>Lots of knowledge, obviously, spanning back to the the mid nineties and even before with, you know, your work on those projects. I think next, I'd like to introduce Steven Tay who's joining us. Stephen, I'll let you give a quick background from what you did with with Vercel and what you're doing now at dub.co.\u003C\u002Fp>\u003Cp>Speaker 2: For sure. It's an honor to be here. I feel incredibly humbled by the the the wealth of experience that you guys bring to the table. I think Bruce was talking about how he he he did that DBL thing in 97. I'm like, that was the year I was born.\u003C\u002Fp>\u003Cp>So it was like a little bit of a it it feels very humbling. But, yeah, my name is Steven. I was, a developer advocate at Vercel for the last two and a half years. I started in 2021. It was there where I got, introduced to the open source community, got to actually learn and became a better developer.\u003C\u002Fp>\u003Cp>Before that, I was doing a lot of, like, data science, coding in Jupyter Notebooks, not really building any, like, software that's used by the end user. So I I learned a ton through my time at, Vercel. I built a lot of open source templates, open source, guides technical guides that people can use to learn about Vercel's technologies like Next. Js, Turbo, Rebo, etcetera. And, it was actually through Vercel.\u003C\u002Fp>\u003Cp>So we we dub this company that I started back in December of last year, it came about as a side project that I built when I was at Versal. I I had this need to, like, be able to share links, on on social media with really nice preview images, and it started from a very niche use case, but then eventually blossomed beyond that into a full link management tool. So, it was in December when I when I started talking to a lot of our users and realized that the market opportunity behind Dubb, It's a start company around it, and here we are.\u003C\u002Fp>\u003Cp>Speaker 0: I love that. Thank you so much, Steven. That's a it's an awesome intro. I mean, obviously, we have, you know, different levels of experience, with open source, with building, but that's kind of how open source works. You know, you've got maintainers and contributors and users.\u003C\u002Fp>\u003Cp>You have some projects that are out there and they're just small little packages or libraries like an SDK, you know, with one hobbyist sort of kind of maintaining it on the weekend versus things like Directus and, you know, even larger operating systems or platforms supported by tens or hundreds of developers. And I think, you know, in the last few years, we've seen all sorts of things happen, that really showcase the importance of of open source, vulnerabilities like Log 4J or just the the escalation of different open source platforms, like, really taking hold in the ecosystem. So it's obviously a very timely conversation. Specifically, my passion is around I I've spoken with Bruce for a while about this actually, is about how to build I think it's a nice acronym, SOS, but sustainable open source. Of course, SOS being, you know, talking to how important this is to get right and to do it timely.\u003C\u002Fp>\u003Cp>But I think we can kinda dive in. The purpose of this, Bridging Bytes episode is really to talk about this theme of open source, past, present, and future. You know, we we, of course, have Bruce here to to really help us understand the roots and how this came to be. We made our our platform open source a decade ago, you know, obviously based on the WordPress license using GPL. And then we are we relicensed about 10 months ago.\u003C\u002Fp>\u003Cp>So there's all sorts of things happening in the past and present. And, of course, you have to be really mindful of where do things go next. You know, how do we actually make open source sustainable? I've heard some pretty interesting ideas from Bruce that I'm sure he'll share around his ideas of what he's calling post open, at least as a working title for now. We'll figure out what that might look like.\u003C\u002Fp>\u003Cp>But, yeah, so I think we should we can dive in. And my first question would be for for Bruce. Bruce, I'm curious, in all the experiences that you've had, maybe you could share how those have shaped your views, of the evolution and impact of open source over such a long tenure over over these decades since 1997, not 87, but through now. How do you think that those experiences have shaped, you know, your views on on that evolution?\u003C\u002Fp>\u003Cp>Speaker 1: Well, I should just say a long, long time ago as if I'm telling a story because it feels like forever. But, so I I started out, working for a film company, and I I had fun. I mean, we did something for the first time. You know, we we made our IPO. They let us have our IPO at the same time that we put out the first three d feature animated film, which was crazy.\u003C\u002Fp>\u003Cp>And so it it was a really interesting ride, but I started at this point to work on Linux because, we were using Unix at Pixar, and the PC was coming out. The PC started to be capable of running Linux. And then, Steve Jobs for a while had his office right across from mine. This was not because I was important, but because they wanted to keep an eye on me. And so, one day, Steve made his peace with Bill Gates, and the next workstation stopped being on Steve's desk, and there was a Windows laptop.\u003C\u002Fp>\u003Cp>And Steve would tell us that, you know, eventually, we'd switch off of Unix, and we'd all be using Windows to do our animation. And no one was very happy with that, and especially then because before Linux was really big, the quality of Windows was very poor because they could afford to have very poor quality. They were essentially the only game in town. And so I started working on Linux and tried SLS and a few other things, you know, ancient distributions, and and then landed on Debian. And at the time, my idea was making a Linux system for amateur radio operators, ham radio.\u003C\u002Fp>\u003Cp>And the reason for that was there was a lot we could do with software, and I thought we could put it all together. Well, that never happened, but I became Debian project leader along the way. And I think it was becoming more important than my work at Pixar. And the real moment for me was one day I got an email from someone and they said, I wanna use Debian with a serial console. Do you can you tell me how?\u003C\u002Fp>\u003Cp>And that wasn't a feature at the time. And I thought it was interesting, and I I sat down at Pixar, you know, when I should have been working and figured out how to do it and sent this fellow an email. And he wrote back and said, that works great. This is going on the next space shuttle flight. And so I walked all around Pixar looking for someone to tell this to.\u003C\u002Fp>\u003Cp>The only person there who knew about Linux was working from home that day. And so, it came a time when people would say to me, well, you're having such an exciting life. You've come out with a film, and your company has an IPO. And isn't this so exciting? And I'd say, yeah.\u003C\u002Fp>\u003Cp>But my hobby project is orbiting in space. And at that point, I sorta decided maybe I would eventually leave Pixar and work on Linux and open source full time. And so we have had a history of that sort of serendipity. I mean, the hobby project of a students. You know, Linus was a student and maybe a lecturer, takes over the operating system world, and the industry software is taken over by amateurs who give away their software.\u003C\u002Fp>\u003Cp>I mean, at this point, none of them were working for companies. And IBM abandoned AIX in favor of this hobby project. So all of these amazing things were happening and why the world was ready. There was a a market vacuum, and this came along and filled it. And people don't realize that this is all straight economics.\u003C\u002Fp>\u003Cp>It's it's no you know, some people call it freakonomics, but there's nothing freaky about it. It's just economics.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I mean, it it goes without saying, you know, by some estimates, you know, open source power is 80% of modern code. So, you know, there's quotes from people over at Google, Filippo Valsorta, who had said something. I've had this quote here. He said, open source software runs the Internet and by extension the economy.\u003C\u002Fp>\u003Cp>And I think that kind of really qualifies like how large open source has become. Obviously, in the early days when you're working, on defining it, Bruce, there was all sorts of there was the, I think you mentioned in Debian, you had the Debian Social contract, in 93. And there's the SPI, which was, you know, software for the public interest. You did the OSI, the Open Source Initiative. There's all sorts of, like, what's the term gonna be?\u003C\u002Fp>\u003Cp>What are the rules? How do we, you know, really label these things? And what we got from that is essentially ten sort of rules or guidelines for what is open source, which is right there, you know, on opensource.org. It was how did you guys come up with those 10 terms? I mean, I I I've looked at them quite a bit because we have an open source source spirit.\u003C\u002Fp>\u003Cp>You know, our ethos has been that since the beginning. But, of course, with a license change and trying to stay true to the terminology of source available versus open source versus whatever new terms we come up with, there's always gonna be those 10 terms sitting there within the OSI. So was that was that an easy or difficult thing to, you know, sort of memorialize for the world?\u003C\u002Fp>\u003Cp>Speaker 1: Well, first, I don't wanna take all the credit. We are standing on the shoulders of Richard Stallman and the free software movement. And, Richard and I were actually speaking at a UN Summit in Tunisia, and I said we're standing on the shoulders of Richard Stallman, and he goes like this. And so, I think that we've had a lot of, different words for what we're doing. And because of that, the concept of having a brand came up.\u003C\u002Fp>\u003Cp>You know, there was free software and there was Shareware. Remember Shareware? And\u003C\u002Fp>\u003Cp>Speaker 0: Shareware and Freeware. There was, like, all the wheres. And it's interesting thinking of, like, how the word free left. Like, you still have thos, you know, free and open source software. But, you know, the freeware versus open source and the difference in perception, again, something that we deal with when we're now selling licenses for, you know, we've for years, been selling licenses for open source software through our cloud SaaS and, you know, breaking the perception of from free to paid is an interesting gap to kind of, cross.\u003C\u002Fp>\u003Cp>But\u003C\u002Fp>\u003Cp>Speaker 1: So so I'll give you another word that everyone's forgotten, consortium. It used to be that real software products were made by consortia, which was a collaboration of companies. And the problem was that they all had their own different interests and that the engineers did not control the operation. So the consortia had a lot of trouble getting along. And my very favorite, consortium story is the X Window System Consortium decided one day that they would not have a canonical user interface for x.\u003C\u002Fp>\u003Cp>And Microsoft, of course, ate their lunch because they did. And and what the x consortium was trying to do at that point was allow each of the different Unix workstation manufacturers to differentiate their work station and also to lock in their customers by having a different user interface for that version of Unix. And they didn't realize that if they didn't work together, Microsoft would just take over the market and, you know, go find a Unix workstation today that isn't a a Linux or BSD system on a PC. So we had all of these economic issues, and we also had the need to create a brand. And so the open source initiative came by because, in Debian, we wanted to say what Debian was, and also we wanted to elucidate our social contract with the community.\u003C\u002Fp>\u003Cp>And so we, wrote the Debian social contract and the Debian free software guidelines. I sort of created and edited it, and we had a 1 month discussion with the Debian developers about it and then publish the results. And this, part of it became the open source initiative, definition. The Debian social guidelines is still very prominent on the Debian site, and you can see the other part that became the open source definition at the end of it.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. No. And that's something I think referred to quite a bit, especially as new licenses come out, you know, making sure they check all 10 of those boxes, to truly call themselves open source, you know, and not have some big glaring asterisk. Yeah. That's it's amazing just to hear the history of, like, all this tumultuous, like, what is this you know, there's just a huge landscape that was not defined.\u003C\u002Fp>\u003Cp>So it's exciting to hear it was defined. Same issues that we're still facing today in terms of how you get financing and make sure you have an authoritative, list of of defining what this is, and that that's being, fostered by people who actually care about what it what it's supposed to mean and not just guiding it based on commercial endeavors. Steven, I'd like to, start over to you. Obviously, you've had, 2 sort of key roles that that you highlighted in your intro. Obviously, going from DevRel over at Vercel, to now founding, dub.co.\u003C\u002Fp>\u003Cp>I'm curious how your perspectives of open source, you know, in the development of those those open source projects has changed, and evolved over that transition of your your career.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. For sure. I think there's there's definitely been quite a shift in terms of, like, the way open source, I guess, the way we view open source personally. But one thing has always stayed true, and I can go through the distinctions there. So when I was at Vercel, we basically maintained this fairly popular React framework called Next.\u003C\u002Fp>\u003Cp>Js. It's basically a framework for you to build with React, service side rendering, and basically an entire full stack applications with batteries built in, like API routes. You can even do stuff like, having a middleware to do redirects, rewrites, and all these different things that regular React would, you know, it'd be harder to build with that. And through Next. Js, it we were able to tap into a massive customer group and and not, I guess, not exactly customers, but, like, potential users that could be converted to customers.\u003C\u002Fp>\u003Cp>Folks like, let me see here. Like, folks like, Netflix. I I I remember tweeting at one time that 4 of the world's largest streaming platforms, Hulu, max.com, Amazon Video, Netflix, they were all using XJaaS, which is kinda cool to see. And, through that sort of, I guess, user base, we're able to expand our enterprise, pipeline because we are able to tap into engineering managers who are using X. Js and and sort of pitch them our host of solution and give them a sort of white gloved approach to spinning up and and maintaining that Next.\u003C\u002Fp>\u003Cp>Js applications. So in that sense, the symbiotic relationship between Next. Js and Resell was very clear. It was like, you know, you have this open source framework, MIT licensed, and you have this proprietary hosted solution that basically, does your Next. Js hosting for you, and you have you have to worry about stuff like Kubernetes maintenance and stuff like that.\u003C\u002Fp>\u003Cp>So that was that that sort of relationship was very interesting. And the work that I did there was also sort of different because, I wasn't working a lot on Next. Js specifically. I was mostly more working on developer efficacy, which is building open source templates, and that sort of, like, drew me to gravitate towards another side of open source, which is building templates and also products that are open source, but at the same time, licensed under AGPL, which would give you, the the maintainer or the the company behind the project a certain level of ownership and control, over how the prod the the code is distributed. So, that sort of drew me to to basically build Dubb as an open source, first project.\u003C\u002Fp>\u003Cp>It started as a project, and it's it's our company. And, so the relationship here is slightly different. Right? It's it's more of a we we call ourselves a cost, which is like a commercial open source, software, which is similar to what, for example, MongoDB started back in the day with, like they had their open source database system that people can easily clone and just run it themselves. And then now it sort of evolved into the wave of, companies like cal.com, which is an entirely open source company sorry, open source product that's, they have their repo, it's out in the public, and they also run that for the hosted service.\u003C\u002Fp>\u003Cp>And they also have, like, a directory that is enterprise only. So if you want to use code in that directory, you would have to buy an enterprise license. And that's sort of the direction that we're going towards, and it certainly has changed quite a bit about how I think about licenses, how I think about getting contributor permissions, and stuff like that. There's so much nuance to to this whole ecosystem that, honestly, I'm still trying to figure out. I'm working with a few lead councils to understand where we should be, you know, what sort of precautions should we take, what sort of, licenses should we potentially change to in the future.\u003C\u002Fp>\u003Cp>So, yeah, very excited to to to discuss this with you guys more and and sort of get your thoughts and and advice on this this whole, space and how we we should move forward.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. No. That that's awesome. I think I mean, I've seen I've lived several of those different iterations of, like, how you commercialize, how you make that more sustainable. Seemed like you were describing sort of like an open core model.\u003C\u002Fp>\u003Cp>Sometimes, you know, you can also work that and call it like an icing on the cake model. There's also the support, like the professional services where you have almost like an agency that leverages it and to get that support and the authoritative maintainers, you know, access to them. That's a paid service. Of course, that can be difficult to scale because you have to scale humans, and not just scale the software out, as you distribute it. So it's interesting to hear, you know, all the different methods.\u003C\u002Fp>\u003Cp>And it kind of ties me into there's a few other questions I'd like to run through before we open it up to the audience. But maybe we'll we'll rapid fire these so that we can actually make sure we we work our way through it. But I think the next one that I think is interesting in this, you know, mid nineties all the way through the mid 20 twenties, where we are now, what would you either of you say are some of, like, the biggest milestones that have significantly impacted this open source landscape? You know, whether it's for maintainers, users, and awareness of, you know, open source as a whole. I'm just curious.\u003C\u002Fp>\u003Cp>I'll I'll leave that pretty broad. But any big developments or milestones that, you guys can think of regarding open source?\u003C\u002Fp>\u003Cp>Speaker 1: Well, I'll talk about myself. In 2005, I wrote a paper called the emerging economic paradigm of open source. And I think that it's very relevant to business and so called costs or commercial open source businesses because it explains how the economics work and goes into the business differentiator of your business. And every business has differentiators, which make their business appear different directly to the customers than others. And, you know, one of your differentiators is you've got dub.c0, and other people have 7 or 8 long, character URLs.\u003C\u002Fp>\u003Cp>And so, we can open source everything but our business differentiators and collaborate with our very worst competitors on everything but our business differentiators because they're probably the people who need the same infrastructure we do. But we shouldn't open source our business differentiators until they rot. And over time, they will no longer be differentiating your business because other companies will have them. And at that point, it makes perfect sense, for that to be open source. So I think this perception of where open source could actually fit in a business and that you were not creating economic suicide and joining the communist workers' paradise by, taking it up, was a a very important one, and and I think that it was pioneered by the big Internet companies.\u003C\u002Fp>\u003Cp>And so when Google and others take up open source, everyone else notices and says, well, these guys are making tons of money, and maybe there's something we should do there too. Another thing I talked about in that paper was a sort of 80 20 phenomenon, which is 80% of the software in your business is non differentiating. So you have a choice of how you make it. You can build it yourself because you have not invented hear disease, and you think everything you do in your company is better. This used to be a a more prevalent thing than it is today.\u003C\u002Fp>\u003Cp>Or you can get it from the open source community. So say you use open source to reduce the cost and risk of developing that 80% of non differentiating software. You take the money that you saved on that, and you move it into making your business differentiators better. And it was the development of economic perceptions like this that I think made it smooth for a company to participate in open source. And and I'm not taking the credit.\u003C\u002Fp>\u003Cp>I think everyone could have made the same observations. But if you, go back and look at that paper, I think it's still very relevant to businesses today.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. No. And certain certainly a large milestone. Steven, any thoughts on other other milestones in the open source world?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I'm I was struggling to come up with one because, I'm fairly new to that open source space, but I I just wanna comment on what Bruce mentioned. I think it's very, very, interesting to hear about that 80 20. It's sort of like the paradox principle where you you take the the you basically leverage open source as a way to collaborate with other amazing developers. And I feel like for us personally at Dubb, the 3 sort of main benefits of being open source is 1, you get to tap into this massive talent developers and people that are, you know, constantly, contributing to open source code, and as a result, your code base becomes more secure.\u003C\u002Fp>\u003Cp>I believe Mark Zuckerberg came up with this quote in an interview a long time ago is that open source software is generally more secure. So that's one. And secondly, there's also the aspect of tapping into larger enterprise customers that you never would have been able to work with if you're not open source. So in our case, Dubb is actually used, we're currently working with the government of Malaysia, which coincidentally is where I'm from, to basically, they're they're forking our our code base to build, like, a official government link shortener for the employees to be able to use, and that's something that we could never have done if we were closed source and they will have to subscribe to our software. So that's something that's really cool to be able to collaborate with all these different, like, agencies that have much larger, like, bureaucratic red tape, stuff like that.\u003C\u002Fp>\u003Cp>And thirdly, definitely the the aspect around open source and the trust and transparency that goes into it is also massive. Like, you cannot just be putting spyware or, like, you know, like, ads and, like, what's it called, privacy invasion software into your code base just because you wanna, like, track your users or, like, I don't know, find a way to sell ads and stuff like that because your code is fully out there and people are scrutinizing it. So and as a result, like, companies are willing to trust and use the software more and goes through procurement much easier than if it was closed sourced. So Yeah. I feel like those are the 3 that we've seen so far.\u003C\u002Fp>\u003Cp>Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: It's interest yeah. And I it's interesting to think of how much trust you can get. You've made your source available. You've made it open source, you know, through the license. These these steps are amazing to make to give you the transparency you need, which as you said, like, there's no hidden code.\u003C\u002Fp>\u003Cp>There's no black box, and it's like, oh, what happens there? You're sending, you know, telemetry back to the mother ship or something weird and you're just unaware. But it's interesting that that transparency, while it does give you the ability to audit the code, you know, we at Directus were actually in use in the public sector. So the Department of Defense in the US and the Department of Energy. You know, we couldn't really do those things if they couldn't see the code end to end.\u003C\u002Fp>\u003Cp>But at the same time, smaller open source organizations, almost always underfunded, really can't afford the security analysis or security researchers, to actually formally ensure that it is the code is audited audited. You know? So auditable and audited are 2 different things. And I think one milestone that comes to mind for me was sort of that big moment with log for 4 j or log you know, from the log 4 shell, vulnerability where people suddenly were very aware of how reliant, everything in the world was to open source, you know, specifically this one package. But, that I think was a really big moment for beyond just open source maintainers that are aware of their own value, of course, but everybody leveraging their systems to be also aware of how critical open source is to what they're building.\u003C\u002Fp>\u003Cp>Speaker 2: So It's crazy. It's I was chatting with this, the he's a product manager at TypeScript of TypeScript at Microsoft, and, we were talking about this, it's a funny analogy where that's I think it's like the what's it called? Like, p k p x p xcd comic or something where it's like p xcd? SKCD comic. Exactly.\u003C\u002Fp>\u003Cp>Where it's like this entire world that's, like, balancing on the shoulders of one big\u003C\u002Fp>\u003Cp>Speaker 0: wall. Yeah. It's a very famous it's a very famous one. You've got this, like, Jenga Castle, and then suddenly there's, like, one little piece that could not be removed, which\u003C\u002Fp>\u003Cp>Speaker 1: is They buy a hobbyist in Ohio.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So exact exactly. It's one of the probably the most popular xkcd comics if if editing can, like, toss it somewhere on here so everybody can see what we're talking about. But, super, super interesting to kind of visualize exactly what we're talking about here. And I think before, you know, some of those bigger vulnerabilities, people just weren't aware of it.\u003C\u002Fp>\u003Cp>Now they are, and, of course, we're trying to remediate how do we solve that. Not always the easiest thing to resolve, but I think the visibility and awareness of, open source, whether it's commercial or free or otherwise, it needs to be sustainable. This is not only a hobbyist project for people. This is, you know, supporting big enterprises and and therefore, you know, big parts of the economy.\u003C\u002Fp>\u003Cp>Speaker 1: I think there's another point that's really important about security especially is that the size of the code base that we use increases exponentially. So when open source started, a 100000 line code base was a large code base. Debian, I think, is now 1,500,000,000 lines. And so as the size of the code base increases, of course, the attack surface increases as well. And, thus, we are now at the point where no company can look at everything with their own employees regardless of the size of the company.\u003C\u002Fp>\u003Cp>Speaker 0: To your point, Bruce, the scale of everything is getting so big. I mean, I forget figured was it called everything or whatever that NPM package was that had a dependency of everything? The dependency trees. You know, when we go through our institutional financing, there's big checks to make sure that we've done everything appropriately, and you have to provide for your software the full dependency tree. Those things just get so enormous, you know, to your points.\u003C\u002Fp>\u003Cp>Everything depends on other things. And then you have some crazy joke, like, whatever that everything package, I think that's what it was called, That suddenly you can't remove because it is a dependency on so it becomes very complex very quickly. And how do I get out of this complexity and make things sustainable? I I guess it's sort of the next question Yeah. That I'd love to cover is, you know, you have these open source projects, hobbyists or otherwise.\u003C\u002Fp>\u003Cp>Obviously, we're talking about how heavily relied upon they are. How can those projects and maintainers transition to commercial viability, to sustainability, to supporting themselves? And as the project grows, again, myself and Drake, you know, created this, you know, 10 years ago. We were working on it together, just the 2 of us, as an open source org. Now there's 30 people at the company full time that it takes, to kinda support this this project.\u003C\u002Fp>\u003Cp>Those are full time salaries. That's a big, big cost. I'd love to hear your, both your thoughts on how these open source projects can transition to, a you know, to be commercially viable.\u003C\u002Fp>\u003Cp>Speaker 1: Well, I think they do it kind of poorly today. And so let's take support because support is the way that a lot of companies try to make money, and open source companies don't do support very well. Why is that? Because they support their own program. Okay?\u003C\u002Fp>\u003Cp>Now say you're a customer, an enterprise customer, and you have 200 important open source programs in your business, then maybe you have 200 support entities. And your job when a bug comes up is to isolate the bug, figure out which of those support entities to call, and then convince them it really is their fault while they all point at each other. And so, open source support, I don't think works all that well, and people get IBM support contracts because IBM says we're gonna support everyone. So that's one of the big problems that we have.\u003C\u002Fp>\u003Cp>Speaker 0: Saying support is not a good option. What would be, like, what would be a an actual like a viable commercial option then for these maintainers to, bootstrap themselves?\u003C\u002Fp>\u003Cp>Speaker 1: Well, continuing to talk about how open source does it today, we also have the widget frosting paradigm, which is you take a, open source thing and then you sell a proprietary enhancement to it. And so that is the way I believe that your company operates beside its support revenue and that a lot of companies do it. You don't have to do it right now in Dubb because you own Dubb. But if you had a whole lot of different, link aggregation companies doing the exact same thing, you'd have to sit back and say, well, I'm gonna have to strategize again. So we've really been doing open source for a long time, and it is time to sit back and re strategize open source to achieve some of the things that we haven't achieved.\u003C\u002Fp>\u003Cp>Okay. So number 1, if the developer does not work for, one of these companies like Google that actually pays developers to do open source, they're probably not compensated. If they don't work for Google, they don't work for your company. And I've had people say to me, well, open source is mostly in companies today. That's a symptom.\u003C\u002Fp>\u003Cp>It's a symptom of the fact that there's no other way to support your family than working for one of these companies that pays you to be an open source developer. Okay. Another problem that we have is we only program for ourselves, and this is actually a diversity, equity, and inclusion problem is we're great systems programmers, and we make really sophisticated applications for people like us. And thus, the the person on the street may not have heard of open source. They probably heard of blind.\u003C\u002Fp>\u003Cp>And if you look at what applications they're using, they are using applications on their phone from Apple and Google, and the infrastructure of those things is open source. So Apple and Google, you know, they they consider the customer to be the product, and the main way that they make revenue is surveilling that customer and presenting advertising to that customer. And, so what we see is that the customer is somewhat abused by these companies, and we enable it. The open source community provides all of the infrastructure for it. So here we are with a world where privacy is concerned while open source stands for privacy.\u003C\u002Fp>\u003Cp>Security is a concern, and and we stand for that too. And why aren't we making the applications that everyone uses? Because the developers want to write for themselves and people just like them. And so, well, maybe we need to pay those developers so that they're right for other people. So I I sat down with these ideas and said, okay.\u003C\u002Fp>\u003Cp>Maybe it's time to transition beyond open source to something I call post open. And that may not be the permanent name because the permanent name has to be trademarkable. So post open is free for the person that we want to help, which is the common person, the individual, the small business. This is a business with revenue under $5,000,000. End user revenue has to be end user because it's too easy to circumvent otherwise.\u003C\u002Fp>\u003Cp>You know, when I worked for a film company, they used to tell us, if your film makes a profit, you're doing your accounting wrong. So end user revenue to prevent circumvention. So if you're a company that makes over that $5,000,000 line or you wish to make proprietary modifications or you wish to put the post open software in a product that you sell, whether it's a device or a service or or a piece of software that you ship, then you have to get a paid license. And once you have a paid license, you pay about 1% of your end user revenue. And once a year, you audit what software you're using and you tell us, and that's compliance.\u003C\u002Fp>\u003Cp>If you do the audit, you write a check, and you're done with compliance for the year. Now this is gonna be really important to companies because right now they have $7,000,000 per year open source compliance departments, and and some of them would actually spend less on post open. So now we've got a revenue stream. Okay. We give that revenue back to developers.\u003C\u002Fp>\u003Cp>One of the ways that we do it is we instrument their Git repositories and we see who the contributors are. And we've seen from our users what they're using, and thus, we have some idea of how to apportion the money and give it back to the developers. Now in practice, this is more complex because there are people like colonel janitors and architects who might end up having time cards because you don't measure them by lines of code. So we're paying these people. We can also start paying them to write applications for the common person and start to reach that market and maybe displace some of the people who aren't working in the interest of that kind of person today.\u003C\u002Fp>\u003Cp>The other thing that we can do is post open is a collection. It's all the software that you put under a post open license, and we can offer support for the entire collection because there's a centralized organization behind this. And the centralized organization offer support for the whole post open collection behind the scenes uses the project maintainers to do maintenance, but they aren't talking to the support customer directly anymore, and they'd be more comfortable that way. They're only in the support business to make money. We funnel the support revenue back to them as well.\u003C\u002Fp>\u003Cp>So this is more complicated. It has PR and trust issues because there is a central organization and everyone has to trust it. If it forks a hundred different ways, it won't work for everyone for anyone. It's a very complex idea. I sat down and I wrote the post open license and it was 325 lines.\u003C\u002Fp>\u003Cp>And there are actually 4 more documents that go with it. Okay. So there's one for the paid user. There's an operating agreement among the developers, and there's a process for infringers to come back into compliance. So I will end up with well over a 1,000 lines between all of these.\u003C\u002Fp>\u003Cp>Fortunately, you don't have to read all of them, just the one that applies to your situation. This may be too complicated to ever happen. This may not gain share, but I think it's an interesting idea. And one of the ways that I would win over the current open source developers and companies like yours is you can dual license post open. So you can continue to be open source, but anyone who's paying for post open now is has got your open source under that agreement and you start getting paid the open source, they start getting easier compliance support, etcetera.\u003C\u002Fp>\u003Cp>So, you know, consider a company like yours, when you started today or tomorrow with PostOpen, you could say, I'm going to invest in developing this software, and I'm going to get a lot of users through PostOpen, and I'm going to get revenue through that paradigm. And all of my software can be disclosed. It can be free for everyone for whom it counts that it's free. You know, once you make over $5,000,000 a year, it's not important that something's free any longer. And, so I think there are a lot of reasons that this is very compelling and I'm actually getting very little pushback on the idea that the pushback I'm getting is, well, today, open source is mostly business, and Bruce doesn't realize that.\u003C\u002Fp>\u003Cp>Well, that's a symptom. That's not the way it should be. So I'd I'd love to hear, you know, how you feel about this, whether you think it's a total pipe dream.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. You and I spoke\u003C\u002Fp>\u003Cp>Speaker 0: about this, Bruce, for a while. The the show is called Bridging Bites, but I think you just bridged questions. Because I think you actually that was a very comprehensive answer, but it actually was an answer to the next two questions that I was gonna ask. So, that was super, super helpful. Obviously, there's strong alignment with what we've done at Directus with our unique usage grants within BSL.\u003C\u002Fp>\u003Cp>But before I kinda kinda we chat through that, I'm curious, Steven, what what license are you currently using, for for for Dubco? And I guess just and in response to that, sort of Bruce had kinda called out I think you called it frosted widgets, which sounds like a a really fun cereal.\u003C\u002Fp>\u003Cp>Speaker 1: Widget frosting. Actually, Eric Raymond made that up.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, well, we called it icing on the cake model. We actually tried that, And we found we were focusing too much on the icing, which is part the part we sell. And the cake, the open source core, was suffering. And so we actually changed our model to this this new licensing strategy based on having tried that. Again, 2 decades, we've tried a lot of different things.\u003C\u002Fp>\u003Cp>But, Steven, maybe you could give me your your what what license are you currently on?\u003C\u002Fp>\u003Cp>Speaker 2: We're yeah. We're currently on AGPL, which basically means that you can own and host your own, version of Dubb and even keep it close, as long as you keep it open source. If you wanna keep it close source and make changes to it, you have to purchase like an enterprise license. And to to respond to the point about like, you know, monetization and open sources, notoriously really, sort of open source maintainers have been notoriously underpaid and under, you know, loved in a way because there are so many packages out there that's just like people just maintaining it tirelessly, especially if you're not getting paid by a company like Microsoft or Vercel. Vercel does a really good job in in the sense, like, for for, you know, open source repos, like, you know, projects like Turborepo, Next.\u003C\u002Fp>\u003Cp>Js, and a few other projects. But if you're not part of that sort of privileged group of developers, you just generally just go out and compensate it. The way we sort of monetize this, we basically have a SaaS offering that is actually not icing on the cake model. It's 1 to 1 exactly, like if you purchase a plan on that, versus if you self hosted, you get the same exact features, but we're sort of moving towards the direction of what telecom is doing, where if you need, like, enterprise features, like, I don't know, RBAC, where it's like role based access access controls or SAML and SSO, all these different, like, enterprise fee feature features, we will put that behind, like, enterprise license. And if you want to use those features, you will have to, you know, purchase, like, a license key, and that's a very, you know, smart model.\u003C\u002Fp>\u003Cp>It's sort of an icing on the cake model, but in general, like, there's not exactly discrimination if you're self hosting versus if you're, you know, paying for a subscription. There's also another model that I think is really interesting, which is once.com, the one that's launched by the founders of Basecamp, 37 signals. They're they're doing this model where it's like pay once and use forever, And you it's sort of like that whole story of how software is sort of like a pendulum goes from licensing software to SaaS and now back to licensing. So it's a very interesting model. I personally think it's I don't know how, like, sustainable it is, how scalable it is, but I think there's a lot of potential there.\u003C\u002Fp>\u003Cp>And I think as the world thinks more towards, like, privacy, owning own data, I think and as people get more technically savvy and have the infrastructure to do so, I think that's, where this kind of model will will flourish. But as of today, like, I I'm that that's why we're going with, like, the SaaS model where if you really want, like, you know, super easy sign up, create a link, you can just use our SaaS product. And then if you're more technically savvy, you wanna own your own data and privacy or whatever, you can self host it. So that's why we went with that.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And that's a that's a really strong model. I mean, we again, we had that sort of gated icing. I've I've listened to to Kyle Poyer, at OpenView, RIP, sort of talk about product led growth and usage based pricing and and also how you can gate those features. He had really interesting ideas around how you really shouldn't gate, the features that people real are gonna resonate with users.\u003C\u002Fp>\u003Cp>But, you know, people know what SSO is. They they potentially know what RBAC is and how they can leverage it. So to enable those enterprise features at a as a paid offering, I think is a really smart way to do it. It it it's, you know, just really up to you. Like, does that work for your business model or not?\u003C\u002Fp>\u003Cp>Does that sort of unlock enough commercialization or not? But yeah. No. That's, I think that's really interesting. Do you have any thoughts before I wanna switch into the sort of q and a.\u003C\u002Fp>\u003Cp>And anybody in the audience, feel free to start throwing some comments or some questions in and then we'll we'll get to them in the the last few minutes here. But, what you had described, Bruce, in terms of post open, you know, name pending, seems almost like a multi tenant version of what we did at Directus. Again, just for those who weren't part of our enormous GitHub discussion where we talked about this in public with the community before we made the license change. We basically said, here's the problem. This needs to be sustainable.\u003C\u002Fp>\u003Cp>Mouths to feed, you know, as Bruce said with the family. Everyone's got families, that need, to be supported. We said, here's the problem. Here's some solutions. One of which was the BSL license.\u003C\u002Fp>\u003Cp>And then we used, interestingly, the same sort of model in parallel with what Bruce said. $5,000,000 and up, and you are sort of this commercial entity that most likely can afford to contribute. You know, again, developers and individuals and hobbyists contribute through code and docs and testing. The large enterprises weren't contributing. So it holds them and makes them, you know, beholden to that commercial license while the majority of users below 5,000,000 can actually use it effectively as free and open source software.\u003C\u002Fp>\u003Cp>We did that in public, you know, with some radical transparency to make sure that everybody understood exactly what we're doing and why. And if there are any other options people had, you know, throw them out there and let's let's consider them. In the end, of course, we went with BSL with that usage grant for if it's under $5,000,000, you're completely good. You know, effectively open source for production, commercial, however you wanna use it. But I'm curious, you know, the model that Bruce described, Steven, would that sort of work for you, the sort of multi tenant?\u003C\u002Fp>\u003Cp>You use a dual license and then potentially, you know, it's it's sort of a unified system or it's it's one centralized, I don't know if if what you would call it yet, Bruce. But this idea of them collecting revenue from the end user, or or fees and then distributing it. I can imagine it'd be so difficult to have, attribution and, this idea of, like, the composition of who which maintainers get how much revenue. There's a lot of questions to solve there. But any thoughts on that, Steven?\u003C\u002Fp>\u003Cp>Just because that's what was my question, like the future of open source. Bruce has sort of pitched this grand idea. I wanna give you a second to respond.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I think that's definitely very interesting. We've we've definitely considered that in the past, and I personally have start like, tried to do, like you know, there's this there's this really cool tool called Algora that basically allows you to do, like, what's it called? Like, bounties where if if an open source contributor solves one of the bugs or or reports a bug and fixes it or creates a new feature, you can pay out bounties directly to them using using you just need to, like, mention the bot and say, tip a $100 or something like that, and that's really cool. That's it's a really cool way to do it.\u003C\u002Fp>\u003Cp>We haven't thought much into, like, obviously, being able to take the end user profits and basically compensate officers maintainers, primarily because we haven't really gotten a lot of, like, contributions so far. I think that's something that we want to sort of encourage and do more in the future. We've definitely got a few really good ones, but I think, as of right now, we haven't invested a lot of introduction to that because of that. But I do think there's also a really interesting distinction between software that you can potentially self host and basically, for example, if you're building, like, I don't know, project management software and that is open source, and you basically are able to take that and just run it on your own instance or whatever, like, a cloud provider that you have versus something that would require a lot of, like, usage based like, I think you mentioned use usage based pricing and stuff like that. Basically, if you're building, like, an AI app that requires, you know, OpenAI, API calls, and that basically costs money to maintain, That's something that's a little bit different because it's like you rely on, like, a proprietary, you know, software, and that's why people are looking into also using open source, providers for LLMs as well, like, Ollama, which is a good one, I think.\u003C\u002Fp>\u003Cp>So, basically, there's this interesting sort of bifurcation between open source software that you can easily maintain on your own VM versus one that relies on proprietary APIs, and that's sort of the, sort of we we we're actually currently trying to move away from relying on these, like, providers to something that you can truly own and self host. But but as of right now, it's not fully all the way there yet. So\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And that's again, I think Bruce mentioned the idea of 80 20 rule. That's something that we we've been using, at Directus for a long time, 10 plus years where we the core software is is aiming to solve 80% of the use case and the functionality. And then the last 20% is, you know, configuration and extensions and and custom code. And that's exactly the problem that was that we were trying to solve is you have these, like, paid API keys and and integration with services that might be paid.\u003C\u002Fp>\u003Cp>So we make sure those all go into the extensions to avoid that exact issue. So, yeah, it's it's interesting to see sort\u003C\u002Fp>\u003Cp>Speaker 2: of that\u003C\u002Fp>\u003Cp>Speaker 0: alignment across all these different things that we've been doing, you know, more siloed. There's a few a few questions here. I think we're over time, but let's see if we can get at least to 1. Alec Bass from Efficient. App had asked a few questions.\u003C\u002Fp>\u003Cp>One was about, you know, how you figure out what is OpenCore versus maybe, the the gated side. I think we touched on that. So let's ask it go through a second question which was, how do you know if someone is using, you know, say, Dubb or Directus privately within their SaaS or within their system without a license. I'll quickly give my take on that because this is something that we have and continue to talk through at Directus. When we made the change to BSL, much like, of course, that was created by, Maria DB with the original author of the license, Hashicorp and Cockroach and Couchbase and Surreal.\u003C\u002Fp>\u003Cp>They've all made that change. That usage grant means that people can use an open source under 5,000,000. But if you're over 5,000,000 or otherwise, how do you keep people honest? You know, we today use an honor system where it's like, that's the license. We hope that you honor it.\u003C\u002Fp>\u003Cp>Typically, if you imagine the people who would be the commercial license over 5,000,000, maybe they're more motivated as a larger company to stay true to the legal document. But we've been also exploring the idea of like a key registration system and telemetry that actually, you know, opt in, opt out for GDPR reasons. But let's understand who's using the software. Let's, you know, error reporting or all these different pieces of information can help you give a sense of who's using your software, how they're using it. But obviously, especially with developer tooling where that network traffic is always monitored and should be, You have to be really judicious in what information you you try to send back to yourself to understand who's using the software or how.\u003C\u002Fp>\u003Cp>Steve, I guess the question was to you at at Dubb. Do you have any thoughts around how you're doing that now?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. That's incredibly I actually had the had a similar, train of thought as well. We currently don't do telemetry for, like, self hosted versions of Dubb, which can get a little tricky. Like, how do you find people who are self hosting? And that's sort of something that we we want to, like, try and do and bake it into our license in the future where it's, like, it's completely anonymized telemetry simply just to detect, like, how much link redirects that you've used within your self hosted version of Dubb.\u003C\u002Fp>\u003Cp>That's 1. And then secondly, I wanted to point out, it's very interesting. Recently, we found a few, like, clones of doubt. They basically took our repo and just ran, like, a commercial service that's completely closed source, and it's, like, a full violation against our license. So that was something that very interesting that we had to deal with.\u003C\u002Fp>\u003Cp>We had to, like, I was consulting with my legal counsel, and we wrote, like, a letter to the the hosting provider and say, you know, this is sort of like a DMCA takedown because they're not compliant with the license. So there's a lot of, like, I feel like dealing in open source sort of makes you like a like a legal candidate. You can you can basically, you know, pass the bar now. You know? You learn a lot of these, like, legal stuff, and I definitely learned a lot myself, in terms of, like, evaluating different licenses.\u003C\u002Fp>\u003Cp>And I think telemetry is a very good point that you that you pointed out, and I think that's something that we definitely wanna go, in the future. Think cal.com does that really well too. They they basically log, the number of bookings in, like, the the open source self hosted versions that people run-in their servers. And if you cross a certain amount, that's when you need to have, like, enterprise license. It's really smart to have that threshold, as well.\u003C\u002Fp>\u003Cp>So yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. No. That that's a that's a huge point.\u003C\u002Fp>\u003Cp>Speaker 1: Go ahead, Bruce. So my consulting business, helps the infringer come back into compliance. So this is the company that used open source outside the license, usually got caught, and, now they have to fix it preferably without being sued or spending too much money. And one of the things that I really find surprises to companies is the community knows what is in your compiled software, that out in the open source community, not only the developers, but their users will see something comes out and they'll say, oh, well, this functionality looks like such and such an open source program. And they look inside, which is easier than a lot of people realize, and they see it's there.\u003C\u002Fp>\u003Cp>I have in fact found my name in a number of commercial products. And so, if if you make a distributed product containing open source, you're gonna get caught, especially if it's a consumer product because the consumer products are the ones that a lot of people see and care about. It might not happen quite as often in the b to b In a, software as a service business like Dubb, it's a little harder because you might have to look at things like, well, what are the Internet responses here to HTTP, etcetera, and say, well, that's exactly the way that my software would do it. But it it is much easier to get caught than anyone realizes, and it usually happens. Now\u003C\u002Fp>\u003Cp>Speaker 0: There will always be workarounds. Right? Like, whatever sort of pricing model or licensing system you put in place or telemetry that, you know, you can always just air gap it, turn off your network, and, you know, it's easy to work around all these things. So you have to incentivize users to do the right thing. And hopefully, you can do that by just having an amazing community and premium software that works well and people want to see continue.\u003C\u002Fp>\u003Cp>But, yeah, it's a it's a very slippery slope. I think it's it's really interesting to see, like, how this will progress in the future. We touched on post open, Bruce's idea about, you know, how this could be centralized and sort of funded, in a multi tenant way. We touched on, like, the direct us and and to an extent how Dubb is doing it. I can think Dubb is a little bit more, Steven, correct me if I'm wrong, but sort of this, open core model where you're taking the same software, but you also have, paid features that are gated on the enterprise side and trying to identify exactly what goes in that bucket, and selling those services.\u003C\u002Fp>\u003Cp>And then Directus, we've kinda got that unique BSL license similar to kind of both sides, where we are trying to say, you know, the larger players, let's make sure they can shoot contribute. It's an honor system. But at the end of the day, I think what we can all agree on is whatever license or strategy becomes sort of the the really the holy grail of of open source or post open or or free and open source software, whatever the names and terms are, it needs to have critical mass. I think we've seen that in the naming conventions decades ago. We're seeing it now.\u003C\u002Fp>\u003Cp>It's really difficult with, you know, the the directest license, you know. It would just be so hard to get, traction there. So whether it's post open or BSL or some, you know, flavor or anything in between, we need we need to get that critical mass. And, of course, that comes with a lot of complexity. We talked about the legal complexity and cost of getting, you know, attorneys to sort of review these documents, trying to make it succinct so people can read it.\u003C\u002Fp>\u003Cp>Bruce, you touched on having a lengthy license and how do we sort of distill that down to something that, both legal and developers can, you know, if you're not a lawyer, you can still understand exactly the implications of the license. There's so much sort of at stake here and there's so many people working on how we can solve it. But it's really exciting to have both of you here on Bridging Bites talking about, you know, what this could look like. I think it's gonna have to be a little bit of a TBD to see where we end up. Lots of great questions in the, in the audience.\u003C\u002Fp>\u003Cp>Sorry we didn't get to get to all of them. But hopefully we'll be able to circle back on one of the future episodes episodes and cover some more. And of course, we will start texting some of the folks in here and see if we can give some manual responses as well. But I did want to thank, Bruce Perrons, and Stephen Tey for joining, this episode and talking about the past, present, and future of open source. Thank you everybody for for watching, and we'll we'll make sure that this is recorded so everybody can can catch up soon on, Directus TV.\u003C\u002Fp>\u003Cp>So that'll be super exciting. Thank you both for joining.\u003C\u002Fp>","Let us kick it off. So this is the second episode of Bridging Bytes. This time, we have we have bridged from the last episode of the intersection of design and development, and now we are talking about, the open source Outlook. So, obviously, a very important topic, something that's near and dear to my heart, having worked on Directus now for 2 decades, over half of that as an open source maintainer. And then we have some amazing, guests here that are gonna be leading the chart in terms of this conversation. So I'll be moderating the the chat. I can give a quick background, and then I'll introduce our 2 our 2 guests. So, my name is Ben Haines. I am CEO and founder of Directus, the open source, data management platform. And I've been I built the platform originally in 2004, and we actually open sourced it in around 2010, 2011. So we'll get into more of the history of of what we did in our open source journey. But, I'd like to next introduce Bruce Perrons, who is sort of the the godfather, I think we were calling him, of open source. Bruce actually created the open source initiative, which I think was in 1997. Give her we'll let him 87. When was it? 1987 was the creation of the open source initiative in the open source definition. And the document actually came about, I think, 8 months before, it was then the Debian free software guidelines. Yep. Obviously, there's some interesting aspects of using the word free versus the term that was actually chosen, which was open source. But yeah. So we have Bruce Perrons here, who is the cofounder of the Open Source Initiative. And, you know, great thanks to your work, I believe, within, Debbie. Basically, Bruce, why don't you give a quick, overview of your background and and and what brought you here? I I'm sorry. It really was 97. I'm just going over the calendar in my head. So, 87 was actually when I got to Pixar, and, I started as a filmmaker, was one of the founders of computer graphic 3 d feature film animation, and worked at Pixar and their predecessor in New York for 19 years in total. Have a credit if you go on IMDB on a couple of films. And in, 97 we'll get that right now. I wanted to define what Debian, included, and we created something called the Debian free software guidelines. And then a little later, a group met and said, well, let's market free software to business and call it open source. And, so that happened. And I was called up the next day and I said, well, great. I'll take my Debian free software guidelines and make them the open source definition. And so there we go. It's been a long time, and it's been incredibly successful. If I had said to you, back then, we're going to, give away our software and completely take over the industry by doing it. And we're going to make an encyclopedia, and it's going to be the first thing to put a Microsoft product and Carta out of business. If I had told you that, you would have just said, well, I wanna smoke that dope too. So, so it it's been a long strange trip. Hasn't it? Yeah. Well, I here I am sitting here, you know, so excited to be 20 years in, to the entirety of my project, Directus. And you've got, you know, 20 years under your belt just, you know, with, you know, elements of your your history. So thank you so much, Bruce. It's really exciting to have you here. Lots of knowledge, obviously, spanning back to the the mid nineties and even before with, you know, your work on those projects. I think next, I'd like to introduce Steven Tay who's joining us. Stephen, I'll let you give a quick background from what you did with with Vercel and what you're doing now at dub.co. For sure. It's an honor to be here. I feel incredibly humbled by the the the wealth of experience that you guys bring to the table. I think Bruce was talking about how he he he did that DBL thing in 97. I'm like, that was the year I was born. So it was like a little bit of a it it feels very humbling. But, yeah, my name is Steven. I was, a developer advocate at Vercel for the last two and a half years. I started in 2021. It was there where I got, introduced to the open source community, got to actually learn and became a better developer. Before that, I was doing a lot of, like, data science, coding in Jupyter Notebooks, not really building any, like, software that's used by the end user. So I I learned a ton through my time at, Vercel. I built a lot of open source templates, open source, guides technical guides that people can use to learn about Vercel's technologies like Next. Js, Turbo, Rebo, etcetera. And, it was actually through Vercel. So we we dub this company that I started back in December of last year, it came about as a side project that I built when I was at Versal. I I had this need to, like, be able to share links, on on social media with really nice preview images, and it started from a very niche use case, but then eventually blossomed beyond that into a full link management tool. So, it was in December when I when I started talking to a lot of our users and realized that the market opportunity behind Dubb, It's a start company around it, and here we are. I love that. Thank you so much, Steven. That's a it's an awesome intro. I mean, obviously, we have, you know, different levels of experience, with open source, with building, but that's kind of how open source works. You know, you've got maintainers and contributors and users. You have some projects that are out there and they're just small little packages or libraries like an SDK, you know, with one hobbyist sort of kind of maintaining it on the weekend versus things like Directus and, you know, even larger operating systems or platforms supported by tens or hundreds of developers. And I think, you know, in the last few years, we've seen all sorts of things happen, that really showcase the importance of of open source, vulnerabilities like Log 4J or just the the escalation of different open source platforms, like, really taking hold in the ecosystem. So it's obviously a very timely conversation. Specifically, my passion is around I I've spoken with Bruce for a while about this actually, is about how to build I think it's a nice acronym, SOS, but sustainable open source. Of course, SOS being, you know, talking to how important this is to get right and to do it timely. But I think we can kinda dive in. The purpose of this, Bridging Bytes episode is really to talk about this theme of open source, past, present, and future. You know, we we, of course, have Bruce here to to really help us understand the roots and how this came to be. We made our our platform open source a decade ago, you know, obviously based on the WordPress license using GPL. And then we are we relicensed about 10 months ago. So there's all sorts of things happening in the past and present. And, of course, you have to be really mindful of where do things go next. You know, how do we actually make open source sustainable? I've heard some pretty interesting ideas from Bruce that I'm sure he'll share around his ideas of what he's calling post open, at least as a working title for now. We'll figure out what that might look like. But, yeah, so I think we should we can dive in. And my first question would be for for Bruce. Bruce, I'm curious, in all the experiences that you've had, maybe you could share how those have shaped your views, of the evolution and impact of open source over such a long tenure over over these decades since 1997, not 87, but through now. How do you think that those experiences have shaped, you know, your views on on that evolution? Well, I should just say a long, long time ago as if I'm telling a story because it feels like forever. But, so I I started out, working for a film company, and I I had fun. I mean, we did something for the first time. You know, we we made our IPO. They let us have our IPO at the same time that we put out the first three d feature animated film, which was crazy. And so it it was a really interesting ride, but I started at this point to work on Linux because, we were using Unix at Pixar, and the PC was coming out. The PC started to be capable of running Linux. And then, Steve Jobs for a while had his office right across from mine. This was not because I was important, but because they wanted to keep an eye on me. And so, one day, Steve made his peace with Bill Gates, and the next workstation stopped being on Steve's desk, and there was a Windows laptop. And Steve would tell us that, you know, eventually, we'd switch off of Unix, and we'd all be using Windows to do our animation. And no one was very happy with that, and especially then because before Linux was really big, the quality of Windows was very poor because they could afford to have very poor quality. They were essentially the only game in town. And so I started working on Linux and tried SLS and a few other things, you know, ancient distributions, and and then landed on Debian. And at the time, my idea was making a Linux system for amateur radio operators, ham radio. And the reason for that was there was a lot we could do with software, and I thought we could put it all together. Well, that never happened, but I became Debian project leader along the way. And I think it was becoming more important than my work at Pixar. And the real moment for me was one day I got an email from someone and they said, I wanna use Debian with a serial console. Do you can you tell me how? And that wasn't a feature at the time. And I thought it was interesting, and I I sat down at Pixar, you know, when I should have been working and figured out how to do it and sent this fellow an email. And he wrote back and said, that works great. This is going on the next space shuttle flight. And so I walked all around Pixar looking for someone to tell this to. The only person there who knew about Linux was working from home that day. And so, it came a time when people would say to me, well, you're having such an exciting life. You've come out with a film, and your company has an IPO. And isn't this so exciting? And I'd say, yeah. But my hobby project is orbiting in space. And at that point, I sorta decided maybe I would eventually leave Pixar and work on Linux and open source full time. And so we have had a history of that sort of serendipity. I mean, the hobby project of a students. You know, Linus was a student and maybe a lecturer, takes over the operating system world, and the industry software is taken over by amateurs who give away their software. I mean, at this point, none of them were working for companies. And IBM abandoned AIX in favor of this hobby project. So all of these amazing things were happening and why the world was ready. There was a a market vacuum, and this came along and filled it. And people don't realize that this is all straight economics. It's it's no you know, some people call it freakonomics, but there's nothing freaky about it. It's just economics. Yeah. I mean, it it goes without saying, you know, by some estimates, you know, open source power is 80% of modern code. So, you know, there's quotes from people over at Google, Filippo Valsorta, who had said something. I've had this quote here. He said, open source software runs the Internet and by extension the economy. And I think that kind of really qualifies like how large open source has become. Obviously, in the early days when you're working, on defining it, Bruce, there was all sorts of there was the, I think you mentioned in Debian, you had the Debian Social contract, in 93. And there's the SPI, which was, you know, software for the public interest. You did the OSI, the Open Source Initiative. There's all sorts of, like, what's the term gonna be? What are the rules? How do we, you know, really label these things? And what we got from that is essentially ten sort of rules or guidelines for what is open source, which is right there, you know, on opensource.org. It was how did you guys come up with those 10 terms? I mean, I I I've looked at them quite a bit because we have an open source source spirit. You know, our ethos has been that since the beginning. But, of course, with a license change and trying to stay true to the terminology of source available versus open source versus whatever new terms we come up with, there's always gonna be those 10 terms sitting there within the OSI. So was that was that an easy or difficult thing to, you know, sort of memorialize for the world? Well, first, I don't wanna take all the credit. We are standing on the shoulders of Richard Stallman and the free software movement. And, Richard and I were actually speaking at a UN Summit in Tunisia, and I said we're standing on the shoulders of Richard Stallman, and he goes like this. And so, I think that we've had a lot of, different words for what we're doing. And because of that, the concept of having a brand came up. You know, there was free software and there was Shareware. Remember Shareware? And Shareware and Freeware. There was, like, all the wheres. And it's interesting thinking of, like, how the word free left. Like, you still have thos, you know, free and open source software. But, you know, the freeware versus open source and the difference in perception, again, something that we deal with when we're now selling licenses for, you know, we've for years, been selling licenses for open source software through our cloud SaaS and, you know, breaking the perception of from free to paid is an interesting gap to kind of, cross. But So so I'll give you another word that everyone's forgotten, consortium. It used to be that real software products were made by consortia, which was a collaboration of companies. And the problem was that they all had their own different interests and that the engineers did not control the operation. So the consortia had a lot of trouble getting along. And my very favorite, consortium story is the X Window System Consortium decided one day that they would not have a canonical user interface for x. And Microsoft, of course, ate their lunch because they did. And and what the x consortium was trying to do at that point was allow each of the different Unix workstation manufacturers to differentiate their work station and also to lock in their customers by having a different user interface for that version of Unix. And they didn't realize that if they didn't work together, Microsoft would just take over the market and, you know, go find a Unix workstation today that isn't a a Linux or BSD system on a PC. So we had all of these economic issues, and we also had the need to create a brand. And so the open source initiative came by because, in Debian, we wanted to say what Debian was, and also we wanted to elucidate our social contract with the community. And so we, wrote the Debian social contract and the Debian free software guidelines. I sort of created and edited it, and we had a 1 month discussion with the Debian developers about it and then publish the results. And this, part of it became the open source initiative, definition. The Debian social guidelines is still very prominent on the Debian site, and you can see the other part that became the open source definition at the end of it. Yeah. No. And that's something I think referred to quite a bit, especially as new licenses come out, you know, making sure they check all 10 of those boxes, to truly call themselves open source, you know, and not have some big glaring asterisk. Yeah. That's it's amazing just to hear the history of, like, all this tumultuous, like, what is this you know, there's just a huge landscape that was not defined. So it's exciting to hear it was defined. Same issues that we're still facing today in terms of how you get financing and make sure you have an authoritative, list of of defining what this is, and that that's being, fostered by people who actually care about what it what it's supposed to mean and not just guiding it based on commercial endeavors. Steven, I'd like to, start over to you. Obviously, you've had, 2 sort of key roles that that you highlighted in your intro. Obviously, going from DevRel over at Vercel, to now founding, dub.co. I'm curious how your perspectives of open source, you know, in the development of those those open source projects has changed, and evolved over that transition of your your career. Yeah. For sure. I think there's there's definitely been quite a shift in terms of, like, the way open source, I guess, the way we view open source personally. But one thing has always stayed true, and I can go through the distinctions there. So when I was at Vercel, we basically maintained this fairly popular React framework called Next. Js. It's basically a framework for you to build with React, service side rendering, and basically an entire full stack applications with batteries built in, like API routes. You can even do stuff like, having a middleware to do redirects, rewrites, and all these different things that regular React would, you know, it'd be harder to build with that. And through Next. Js, it we were able to tap into a massive customer group and and not, I guess, not exactly customers, but, like, potential users that could be converted to customers. Folks like, let me see here. Like, folks like, Netflix. I I I remember tweeting at one time that 4 of the world's largest streaming platforms, Hulu, max.com, Amazon Video, Netflix, they were all using XJaaS, which is kinda cool to see. And, through that sort of, I guess, user base, we're able to expand our enterprise, pipeline because we are able to tap into engineering managers who are using X. Js and and sort of pitch them our host of solution and give them a sort of white gloved approach to spinning up and and maintaining that Next. Js applications. So in that sense, the symbiotic relationship between Next. Js and Resell was very clear. It was like, you know, you have this open source framework, MIT licensed, and you have this proprietary hosted solution that basically, does your Next. Js hosting for you, and you have you have to worry about stuff like Kubernetes maintenance and stuff like that. So that was that that sort of relationship was very interesting. And the work that I did there was also sort of different because, I wasn't working a lot on Next. Js specifically. I was mostly more working on developer efficacy, which is building open source templates, and that sort of, like, drew me to gravitate towards another side of open source, which is building templates and also products that are open source, but at the same time, licensed under AGPL, which would give you, the the maintainer or the the company behind the project a certain level of ownership and control, over how the prod the the code is distributed. So, that sort of drew me to to basically build Dubb as an open source, first project. It started as a project, and it's it's our company. And, so the relationship here is slightly different. Right? It's it's more of a we we call ourselves a cost, which is like a commercial open source, software, which is similar to what, for example, MongoDB started back in the day with, like they had their open source database system that people can easily clone and just run it themselves. And then now it sort of evolved into the wave of, companies like cal.com, which is an entirely open source company sorry, open source product that's, they have their repo, it's out in the public, and they also run that for the hosted service. And they also have, like, a directory that is enterprise only. So if you want to use code in that directory, you would have to buy an enterprise license. And that's sort of the direction that we're going towards, and it certainly has changed quite a bit about how I think about licenses, how I think about getting contributor permissions, and stuff like that. There's so much nuance to to this whole ecosystem that, honestly, I'm still trying to figure out. I'm working with a few lead councils to understand where we should be, you know, what sort of precautions should we take, what sort of, licenses should we potentially change to in the future. So, yeah, very excited to to to discuss this with you guys more and and sort of get your thoughts and and advice on this this whole, space and how we we should move forward. Yeah. No. That that's awesome. I think I mean, I've seen I've lived several of those different iterations of, like, how you commercialize, how you make that more sustainable. Seemed like you were describing sort of like an open core model. Sometimes, you know, you can also work that and call it like an icing on the cake model. There's also the support, like the professional services where you have almost like an agency that leverages it and to get that support and the authoritative maintainers, you know, access to them. That's a paid service. Of course, that can be difficult to scale because you have to scale humans, and not just scale the software out, as you distribute it. So it's interesting to hear, you know, all the different methods. And it kind of ties me into there's a few other questions I'd like to run through before we open it up to the audience. But maybe we'll we'll rapid fire these so that we can actually make sure we we work our way through it. But I think the next one that I think is interesting in this, you know, mid nineties all the way through the mid 20 twenties, where we are now, what would you either of you say are some of, like, the biggest milestones that have significantly impacted this open source landscape? You know, whether it's for maintainers, users, and awareness of, you know, open source as a whole. I'm just curious. I'll I'll leave that pretty broad. But any big developments or milestones that, you guys can think of regarding open source? Well, I'll talk about myself. In 2005, I wrote a paper called the emerging economic paradigm of open source. And I think that it's very relevant to business and so called costs or commercial open source businesses because it explains how the economics work and goes into the business differentiator of your business. And every business has differentiators, which make their business appear different directly to the customers than others. And, you know, one of your differentiators is you've got dub.c0, and other people have 7 or 8 long, character URLs. And so, we can open source everything but our business differentiators and collaborate with our very worst competitors on everything but our business differentiators because they're probably the people who need the same infrastructure we do. But we shouldn't open source our business differentiators until they rot. And over time, they will no longer be differentiating your business because other companies will have them. And at that point, it makes perfect sense, for that to be open source. So I think this perception of where open source could actually fit in a business and that you were not creating economic suicide and joining the communist workers' paradise by, taking it up, was a a very important one, and and I think that it was pioneered by the big Internet companies. And so when Google and others take up open source, everyone else notices and says, well, these guys are making tons of money, and maybe there's something we should do there too. Another thing I talked about in that paper was a sort of 80 20 phenomenon, which is 80% of the software in your business is non differentiating. So you have a choice of how you make it. You can build it yourself because you have not invented hear disease, and you think everything you do in your company is better. This used to be a a more prevalent thing than it is today. Or you can get it from the open source community. So say you use open source to reduce the cost and risk of developing that 80% of non differentiating software. You take the money that you saved on that, and you move it into making your business differentiators better. And it was the development of economic perceptions like this that I think made it smooth for a company to participate in open source. And and I'm not taking the credit. I think everyone could have made the same observations. But if you, go back and look at that paper, I think it's still very relevant to businesses today. Yeah. No. And certain certainly a large milestone. Steven, any thoughts on other other milestones in the open source world? Yeah. I'm I was struggling to come up with one because, I'm fairly new to that open source space, but I I just wanna comment on what Bruce mentioned. I think it's very, very, interesting to hear about that 80 20. It's sort of like the paradox principle where you you take the the you basically leverage open source as a way to collaborate with other amazing developers. And I feel like for us personally at Dubb, the 3 sort of main benefits of being open source is 1, you get to tap into this massive talent developers and people that are, you know, constantly, contributing to open source code, and as a result, your code base becomes more secure. I believe Mark Zuckerberg came up with this quote in an interview a long time ago is that open source software is generally more secure. So that's one. And secondly, there's also the aspect of tapping into larger enterprise customers that you never would have been able to work with if you're not open source. So in our case, Dubb is actually used, we're currently working with the government of Malaysia, which coincidentally is where I'm from, to basically, they're they're forking our our code base to build, like, a official government link shortener for the employees to be able to use, and that's something that we could never have done if we were closed source and they will have to subscribe to our software. So that's something that's really cool to be able to collaborate with all these different, like, agencies that have much larger, like, bureaucratic red tape, stuff like that. And thirdly, definitely the the aspect around open source and the trust and transparency that goes into it is also massive. Like, you cannot just be putting spyware or, like, you know, like, ads and, like, what's it called, privacy invasion software into your code base just because you wanna, like, track your users or, like, I don't know, find a way to sell ads and stuff like that because your code is fully out there and people are scrutinizing it. So and as a result, like, companies are willing to trust and use the software more and goes through procurement much easier than if it was closed sourced. So Yeah. I feel like those are the 3 that we've seen so far. Yeah. It's interest yeah. And I it's interesting to think of how much trust you can get. You've made your source available. You've made it open source, you know, through the license. These these steps are amazing to make to give you the transparency you need, which as you said, like, there's no hidden code. There's no black box, and it's like, oh, what happens there? You're sending, you know, telemetry back to the mother ship or something weird and you're just unaware. But it's interesting that that transparency, while it does give you the ability to audit the code, you know, we at Directus were actually in use in the public sector. So the Department of Defense in the US and the Department of Energy. You know, we couldn't really do those things if they couldn't see the code end to end. But at the same time, smaller open source organizations, almost always underfunded, really can't afford the security analysis or security researchers, to actually formally ensure that it is the code is audited audited. You know? So auditable and audited are 2 different things. And I think one milestone that comes to mind for me was sort of that big moment with log for 4 j or log you know, from the log 4 shell, vulnerability where people suddenly were very aware of how reliant, everything in the world was to open source, you know, specifically this one package. But, that I think was a really big moment for beyond just open source maintainers that are aware of their own value, of course, but everybody leveraging their systems to be also aware of how critical open source is to what they're building. So It's crazy. It's I was chatting with this, the he's a product manager at TypeScript of TypeScript at Microsoft, and, we were talking about this, it's a funny analogy where that's I think it's like the what's it called? Like, p k p x p xcd comic or something where it's like p xcd? SKCD comic. Exactly. Where it's like this entire world that's, like, balancing on the shoulders of one big wall. Yeah. It's a very famous it's a very famous one. You've got this, like, Jenga Castle, and then suddenly there's, like, one little piece that could not be removed, which is They buy a hobbyist in Ohio. Yeah. So exact exactly. It's one of the probably the most popular xkcd comics if if editing can, like, toss it somewhere on here so everybody can see what we're talking about. But, super, super interesting to kind of visualize exactly what we're talking about here. And I think before, you know, some of those bigger vulnerabilities, people just weren't aware of it. Now they are, and, of course, we're trying to remediate how do we solve that. Not always the easiest thing to resolve, but I think the visibility and awareness of, open source, whether it's commercial or free or otherwise, it needs to be sustainable. This is not only a hobbyist project for people. This is, you know, supporting big enterprises and and therefore, you know, big parts of the economy. I think there's another point that's really important about security especially is that the size of the code base that we use increases exponentially. So when open source started, a 100000 line code base was a large code base. Debian, I think, is now 1,500,000,000 lines. And so as the size of the code base increases, of course, the attack surface increases as well. And, thus, we are now at the point where no company can look at everything with their own employees regardless of the size of the company. To your point, Bruce, the scale of everything is getting so big. I mean, I forget figured was it called everything or whatever that NPM package was that had a dependency of everything? The dependency trees. You know, when we go through our institutional financing, there's big checks to make sure that we've done everything appropriately, and you have to provide for your software the full dependency tree. Those things just get so enormous, you know, to your points. Everything depends on other things. And then you have some crazy joke, like, whatever that everything package, I think that's what it was called, That suddenly you can't remove because it is a dependency on so it becomes very complex very quickly. And how do I get out of this complexity and make things sustainable? I I guess it's sort of the next question Yeah. That I'd love to cover is, you know, you have these open source projects, hobbyists or otherwise. Obviously, we're talking about how heavily relied upon they are. How can those projects and maintainers transition to commercial viability, to sustainability, to supporting themselves? And as the project grows, again, myself and Drake, you know, created this, you know, 10 years ago. We were working on it together, just the 2 of us, as an open source org. Now there's 30 people at the company full time that it takes, to kinda support this this project. Those are full time salaries. That's a big, big cost. I'd love to hear your, both your thoughts on how these open source projects can transition to, a you know, to be commercially viable. Well, I think they do it kind of poorly today. And so let's take support because support is the way that a lot of companies try to make money, and open source companies don't do support very well. Why is that? Because they support their own program. Okay? Now say you're a customer, an enterprise customer, and you have 200 important open source programs in your business, then maybe you have 200 support entities. And your job when a bug comes up is to isolate the bug, figure out which of those support entities to call, and then convince them it really is their fault while they all point at each other. And so, open source support, I don't think works all that well, and people get IBM support contracts because IBM says we're gonna support everyone. So that's one of the big problems that we have. Saying support is not a good option. What would be, like, what would be a an actual like a viable commercial option then for these maintainers to, bootstrap themselves? Well, continuing to talk about how open source does it today, we also have the widget frosting paradigm, which is you take a, open source thing and then you sell a proprietary enhancement to it. And so that is the way I believe that your company operates beside its support revenue and that a lot of companies do it. You don't have to do it right now in Dubb because you own Dubb. But if you had a whole lot of different, link aggregation companies doing the exact same thing, you'd have to sit back and say, well, I'm gonna have to strategize again. So we've really been doing open source for a long time, and it is time to sit back and re strategize open source to achieve some of the things that we haven't achieved. Okay. So number 1, if the developer does not work for, one of these companies like Google that actually pays developers to do open source, they're probably not compensated. If they don't work for Google, they don't work for your company. And I've had people say to me, well, open source is mostly in companies today. That's a symptom. It's a symptom of the fact that there's no other way to support your family than working for one of these companies that pays you to be an open source developer. Okay. Another problem that we have is we only program for ourselves, and this is actually a diversity, equity, and inclusion problem is we're great systems programmers, and we make really sophisticated applications for people like us. And thus, the the person on the street may not have heard of open source. They probably heard of blind. And if you look at what applications they're using, they are using applications on their phone from Apple and Google, and the infrastructure of those things is open source. So Apple and Google, you know, they they consider the customer to be the product, and the main way that they make revenue is surveilling that customer and presenting advertising to that customer. And, so what we see is that the customer is somewhat abused by these companies, and we enable it. The open source community provides all of the infrastructure for it. So here we are with a world where privacy is concerned while open source stands for privacy. Security is a concern, and and we stand for that too. And why aren't we making the applications that everyone uses? Because the developers want to write for themselves and people just like them. And so, well, maybe we need to pay those developers so that they're right for other people. So I I sat down with these ideas and said, okay. Maybe it's time to transition beyond open source to something I call post open. And that may not be the permanent name because the permanent name has to be trademarkable. So post open is free for the person that we want to help, which is the common person, the individual, the small business. This is a business with revenue under $5,000,000. End user revenue has to be end user because it's too easy to circumvent otherwise. You know, when I worked for a film company, they used to tell us, if your film makes a profit, you're doing your accounting wrong. So end user revenue to prevent circumvention. So if you're a company that makes over that $5,000,000 line or you wish to make proprietary modifications or you wish to put the post open software in a product that you sell, whether it's a device or a service or or a piece of software that you ship, then you have to get a paid license. And once you have a paid license, you pay about 1% of your end user revenue. And once a year, you audit what software you're using and you tell us, and that's compliance. If you do the audit, you write a check, and you're done with compliance for the year. Now this is gonna be really important to companies because right now they have $7,000,000 per year open source compliance departments, and and some of them would actually spend less on post open. So now we've got a revenue stream. Okay. We give that revenue back to developers. One of the ways that we do it is we instrument their Git repositories and we see who the contributors are. And we've seen from our users what they're using, and thus, we have some idea of how to apportion the money and give it back to the developers. Now in practice, this is more complex because there are people like colonel janitors and architects who might end up having time cards because you don't measure them by lines of code. So we're paying these people. We can also start paying them to write applications for the common person and start to reach that market and maybe displace some of the people who aren't working in the interest of that kind of person today. The other thing that we can do is post open is a collection. It's all the software that you put under a post open license, and we can offer support for the entire collection because there's a centralized organization behind this. And the centralized organization offer support for the whole post open collection behind the scenes uses the project maintainers to do maintenance, but they aren't talking to the support customer directly anymore, and they'd be more comfortable that way. They're only in the support business to make money. We funnel the support revenue back to them as well. So this is more complicated. It has PR and trust issues because there is a central organization and everyone has to trust it. If it forks a hundred different ways, it won't work for everyone for anyone. It's a very complex idea. I sat down and I wrote the post open license and it was 325 lines. And there are actually 4 more documents that go with it. Okay. So there's one for the paid user. There's an operating agreement among the developers, and there's a process for infringers to come back into compliance. So I will end up with well over a 1,000 lines between all of these. Fortunately, you don't have to read all of them, just the one that applies to your situation. This may be too complicated to ever happen. This may not gain share, but I think it's an interesting idea. And one of the ways that I would win over the current open source developers and companies like yours is you can dual license post open. So you can continue to be open source, but anyone who's paying for post open now is has got your open source under that agreement and you start getting paid the open source, they start getting easier compliance support, etcetera. So, you know, consider a company like yours, when you started today or tomorrow with PostOpen, you could say, I'm going to invest in developing this software, and I'm going to get a lot of users through PostOpen, and I'm going to get revenue through that paradigm. And all of my software can be disclosed. It can be free for everyone for whom it counts that it's free. You know, once you make over $5,000,000 a year, it's not important that something's free any longer. And, so I think there are a lot of reasons that this is very compelling and I'm actually getting very little pushback on the idea that the pushback I'm getting is, well, today, open source is mostly business, and Bruce doesn't realize that. Well, that's a symptom. That's not the way it should be. So I'd I'd love to hear, you know, how you feel about this, whether you think it's a total pipe dream. Yeah. You and I spoke about this, Bruce, for a while. The the show is called Bridging Bites, but I think you just bridged questions. Because I think you actually that was a very comprehensive answer, but it actually was an answer to the next two questions that I was gonna ask. So, that was super, super helpful. Obviously, there's strong alignment with what we've done at Directus with our unique usage grants within BSL. But before I kinda kinda we chat through that, I'm curious, Steven, what what license are you currently using, for for for Dubco? And I guess just and in response to that, sort of Bruce had kinda called out I think you called it frosted widgets, which sounds like a a really fun cereal. Widget frosting. Actually, Eric Raymond made that up. Oh, well, we called it icing on the cake model. We actually tried that, And we found we were focusing too much on the icing, which is part the part we sell. And the cake, the open source core, was suffering. And so we actually changed our model to this this new licensing strategy based on having tried that. Again, 2 decades, we've tried a lot of different things. But, Steven, maybe you could give me your your what what license are you currently on? We're yeah. We're currently on AGPL, which basically means that you can own and host your own, version of Dubb and even keep it close, as long as you keep it open source. If you wanna keep it close source and make changes to it, you have to purchase like an enterprise license. And to to respond to the point about like, you know, monetization and open sources, notoriously really, sort of open source maintainers have been notoriously underpaid and under, you know, loved in a way because there are so many packages out there that's just like people just maintaining it tirelessly, especially if you're not getting paid by a company like Microsoft or Vercel. Vercel does a really good job in in the sense, like, for for, you know, open source repos, like, you know, projects like Turborepo, Next. Js, and a few other projects. But if you're not part of that sort of privileged group of developers, you just generally just go out and compensate it. The way we sort of monetize this, we basically have a SaaS offering that is actually not icing on the cake model. It's 1 to 1 exactly, like if you purchase a plan on that, versus if you self hosted, you get the same exact features, but we're sort of moving towards the direction of what telecom is doing, where if you need, like, enterprise features, like, I don't know, RBAC, where it's like role based access access controls or SAML and SSO, all these different, like, enterprise fee feature features, we will put that behind, like, enterprise license. And if you want to use those features, you will have to, you know, purchase, like, a license key, and that's a very, you know, smart model. It's sort of an icing on the cake model, but in general, like, there's not exactly discrimination if you're self hosting versus if you're, you know, paying for a subscription. There's also another model that I think is really interesting, which is once.com, the one that's launched by the founders of Basecamp, 37 signals. They're they're doing this model where it's like pay once and use forever, And you it's sort of like that whole story of how software is sort of like a pendulum goes from licensing software to SaaS and now back to licensing. So it's a very interesting model. I personally think it's I don't know how, like, sustainable it is, how scalable it is, but I think there's a lot of potential there. And I think as the world thinks more towards, like, privacy, owning own data, I think and as people get more technically savvy and have the infrastructure to do so, I think that's, where this kind of model will will flourish. But as of today, like, I I'm that that's why we're going with, like, the SaaS model where if you really want, like, you know, super easy sign up, create a link, you can just use our SaaS product. And then if you're more technically savvy, you wanna own your own data and privacy or whatever, you can self host it. So that's why we went with that. Yeah. And that's a that's a really strong model. I mean, we again, we had that sort of gated icing. I've I've listened to to Kyle Poyer, at OpenView, RIP, sort of talk about product led growth and usage based pricing and and also how you can gate those features. He had really interesting ideas around how you really shouldn't gate, the features that people real are gonna resonate with users. But, you know, people know what SSO is. They they potentially know what RBAC is and how they can leverage it. So to enable those enterprise features at a as a paid offering, I think is a really smart way to do it. It it it's, you know, just really up to you. Like, does that work for your business model or not? Does that sort of unlock enough commercialization or not? But yeah. No. That's, I think that's really interesting. Do you have any thoughts before I wanna switch into the sort of q and a. And anybody in the audience, feel free to start throwing some comments or some questions in and then we'll we'll get to them in the the last few minutes here. But, what you had described, Bruce, in terms of post open, you know, name pending, seems almost like a multi tenant version of what we did at Directus. Again, just for those who weren't part of our enormous GitHub discussion where we talked about this in public with the community before we made the license change. We basically said, here's the problem. This needs to be sustainable. Mouths to feed, you know, as Bruce said with the family. Everyone's got families, that need, to be supported. We said, here's the problem. Here's some solutions. One of which was the BSL license. And then we used, interestingly, the same sort of model in parallel with what Bruce said. $5,000,000 and up, and you are sort of this commercial entity that most likely can afford to contribute. You know, again, developers and individuals and hobbyists contribute through code and docs and testing. The large enterprises weren't contributing. So it holds them and makes them, you know, beholden to that commercial license while the majority of users below 5,000,000 can actually use it effectively as free and open source software. We did that in public, you know, with some radical transparency to make sure that everybody understood exactly what we're doing and why. And if there are any other options people had, you know, throw them out there and let's let's consider them. In the end, of course, we went with BSL with that usage grant for if it's under $5,000,000, you're completely good. You know, effectively open source for production, commercial, however you wanna use it. But I'm curious, you know, the model that Bruce described, Steven, would that sort of work for you, the sort of multi tenant? You use a dual license and then potentially, you know, it's it's sort of a unified system or it's it's one centralized, I don't know if if what you would call it yet, Bruce. But this idea of them collecting revenue from the end user, or or fees and then distributing it. I can imagine it'd be so difficult to have, attribution and, this idea of, like, the composition of who which maintainers get how much revenue. There's a lot of questions to solve there. But any thoughts on that, Steven? Just because that's what was my question, like the future of open source. Bruce has sort of pitched this grand idea. I wanna give you a second to respond. Yeah. I think that's definitely very interesting. We've we've definitely considered that in the past, and I personally have start like, tried to do, like you know, there's this there's this really cool tool called Algora that basically allows you to do, like, what's it called? Like, bounties where if if an open source contributor solves one of the bugs or or reports a bug and fixes it or creates a new feature, you can pay out bounties directly to them using using you just need to, like, mention the bot and say, tip a $100 or something like that, and that's really cool. That's it's a really cool way to do it. We haven't thought much into, like, obviously, being able to take the end user profits and basically compensate officers maintainers, primarily because we haven't really gotten a lot of, like, contributions so far. I think that's something that we want to sort of encourage and do more in the future. We've definitely got a few really good ones, but I think, as of right now, we haven't invested a lot of introduction to that because of that. But I do think there's also a really interesting distinction between software that you can potentially self host and basically, for example, if you're building, like, I don't know, project management software and that is open source, and you basically are able to take that and just run it on your own instance or whatever, like, a cloud provider that you have versus something that would require a lot of, like, usage based like, I think you mentioned use usage based pricing and stuff like that. Basically, if you're building, like, an AI app that requires, you know, OpenAI, API calls, and that basically costs money to maintain, That's something that's a little bit different because it's like you rely on, like, a proprietary, you know, software, and that's why people are looking into also using open source, providers for LLMs as well, like, Ollama, which is a good one, I think. So, basically, there's this interesting sort of bifurcation between open source software that you can easily maintain on your own VM versus one that relies on proprietary APIs, and that's sort of the, sort of we we we're actually currently trying to move away from relying on these, like, providers to something that you can truly own and self host. But but as of right now, it's not fully all the way there yet. So Yeah. And that's again, I think Bruce mentioned the idea of 80 20 rule. That's something that we we've been using, at Directus for a long time, 10 plus years where we the core software is is aiming to solve 80% of the use case and the functionality. And then the last 20% is, you know, configuration and extensions and and custom code. And that's exactly the problem that was that we were trying to solve is you have these, like, paid API keys and and integration with services that might be paid. So we make sure those all go into the extensions to avoid that exact issue. So, yeah, it's it's interesting to see sort of that alignment across all these different things that we've been doing, you know, more siloed. There's a few a few questions here. I think we're over time, but let's see if we can get at least to 1. Alec Bass from Efficient. App had asked a few questions. One was about, you know, how you figure out what is OpenCore versus maybe, the the gated side. I think we touched on that. So let's ask it go through a second question which was, how do you know if someone is using, you know, say, Dubb or Directus privately within their SaaS or within their system without a license. I'll quickly give my take on that because this is something that we have and continue to talk through at Directus. When we made the change to BSL, much like, of course, that was created by, Maria DB with the original author of the license, Hashicorp and Cockroach and Couchbase and Surreal. They've all made that change. That usage grant means that people can use an open source under 5,000,000. But if you're over 5,000,000 or otherwise, how do you keep people honest? You know, we today use an honor system where it's like, that's the license. We hope that you honor it. Typically, if you imagine the people who would be the commercial license over 5,000,000, maybe they're more motivated as a larger company to stay true to the legal document. But we've been also exploring the idea of like a key registration system and telemetry that actually, you know, opt in, opt out for GDPR reasons. But let's understand who's using the software. Let's, you know, error reporting or all these different pieces of information can help you give a sense of who's using your software, how they're using it. But obviously, especially with developer tooling where that network traffic is always monitored and should be, You have to be really judicious in what information you you try to send back to yourself to understand who's using the software or how. Steve, I guess the question was to you at at Dubb. Do you have any thoughts around how you're doing that now? Yeah. That's incredibly I actually had the had a similar, train of thought as well. We currently don't do telemetry for, like, self hosted versions of Dubb, which can get a little tricky. Like, how do you find people who are self hosting? And that's sort of something that we we want to, like, try and do and bake it into our license in the future where it's, like, it's completely anonymized telemetry simply just to detect, like, how much link redirects that you've used within your self hosted version of Dubb. That's 1. And then secondly, I wanted to point out, it's very interesting. Recently, we found a few, like, clones of doubt. They basically took our repo and just ran, like, a commercial service that's completely closed source, and it's, like, a full violation against our license. So that was something that very interesting that we had to deal with. We had to, like, I was consulting with my legal counsel, and we wrote, like, a letter to the the hosting provider and say, you know, this is sort of like a DMCA takedown because they're not compliant with the license. So there's a lot of, like, I feel like dealing in open source sort of makes you like a like a legal candidate. You can you can basically, you know, pass the bar now. You know? You learn a lot of these, like, legal stuff, and I definitely learned a lot myself, in terms of, like, evaluating different licenses. And I think telemetry is a very good point that you that you pointed out, and I think that's something that we definitely wanna go, in the future. Think cal.com does that really well too. They they basically log, the number of bookings in, like, the the open source self hosted versions that people run-in their servers. And if you cross a certain amount, that's when you need to have, like, enterprise license. It's really smart to have that threshold, as well. So yeah. Yeah. No. That that's a that's a huge point. Go ahead, Bruce. So my consulting business, helps the infringer come back into compliance. So this is the company that used open source outside the license, usually got caught, and, now they have to fix it preferably without being sued or spending too much money. And one of the things that I really find surprises to companies is the community knows what is in your compiled software, that out in the open source community, not only the developers, but their users will see something comes out and they'll say, oh, well, this functionality looks like such and such an open source program. And they look inside, which is easier than a lot of people realize, and they see it's there. I have in fact found my name in a number of commercial products. And so, if if you make a distributed product containing open source, you're gonna get caught, especially if it's a consumer product because the consumer products are the ones that a lot of people see and care about. It might not happen quite as often in the b to b In a, software as a service business like Dubb, it's a little harder because you might have to look at things like, well, what are the Internet responses here to HTTP, etcetera, and say, well, that's exactly the way that my software would do it. But it it is much easier to get caught than anyone realizes, and it usually happens. Now There will always be workarounds. Right? Like, whatever sort of pricing model or licensing system you put in place or telemetry that, you know, you can always just air gap it, turn off your network, and, you know, it's easy to work around all these things. So you have to incentivize users to do the right thing. And hopefully, you can do that by just having an amazing community and premium software that works well and people want to see continue. But, yeah, it's a it's a very slippery slope. I think it's it's really interesting to see, like, how this will progress in the future. We touched on post open, Bruce's idea about, you know, how this could be centralized and sort of funded, in a multi tenant way. We touched on, like, the direct us and and to an extent how Dubb is doing it. I can think Dubb is a little bit more, Steven, correct me if I'm wrong, but sort of this, open core model where you're taking the same software, but you also have, paid features that are gated on the enterprise side and trying to identify exactly what goes in that bucket, and selling those services. And then Directus, we've kinda got that unique BSL license similar to kind of both sides, where we are trying to say, you know, the larger players, let's make sure they can shoot contribute. It's an honor system. But at the end of the day, I think what we can all agree on is whatever license or strategy becomes sort of the the really the holy grail of of open source or post open or or free and open source software, whatever the names and terms are, it needs to have critical mass. I think we've seen that in the naming conventions decades ago. We're seeing it now. It's really difficult with, you know, the the directest license, you know. It would just be so hard to get, traction there. So whether it's post open or BSL or some, you know, flavor or anything in between, we need we need to get that critical mass. And, of course, that comes with a lot of complexity. We talked about the legal complexity and cost of getting, you know, attorneys to sort of review these documents, trying to make it succinct so people can read it. Bruce, you touched on having a lengthy license and how do we sort of distill that down to something that, both legal and developers can, you know, if you're not a lawyer, you can still understand exactly the implications of the license. There's so much sort of at stake here and there's so many people working on how we can solve it. But it's really exciting to have both of you here on Bridging Bites talking about, you know, what this could look like. I think it's gonna have to be a little bit of a TBD to see where we end up. Lots of great questions in the, in the audience. Sorry we didn't get to get to all of them. But hopefully we'll be able to circle back on one of the future episodes episodes and cover some more. And of course, we will start texting some of the folks in here and see if we can give some manual responses as well. But I did want to thank, Bruce Perrons, and Stephen Tey for joining, this episode and talking about the past, present, and future of open source. Thank you everybody for for watching, and we'll we'll make sure that this is recorded so everybody can can catch up soon on, Directus TV. So that'll be super exciting. Thank you both for joining.",[4067,4068,4069],"0501891e-d646-498e-bffe-91877013200a","79c00d61-59b0-416a-a5c6-b4676b1f76f5","4e4da556-c78e-4e05-8106-a8f9baf2adcb",[],{"year":2380,"number":212,"id":2434,"episodes":4072,"show":4073},[2436,2437,2417],{"title":2439,"slug":2440},{"id":3364,"slug":4075,"vimeo_id":4076,"description":4077,"tile":4078,"length":13,"resources":12,"people":12,"episode_number":13,"published":4079,"title":4080,"video_transcript_html":4081,"video_transcript_text":4082,"content":12,"seo":12,"status":19,"episode_people":4083,"recommendations":4084,"season":4085},"code-review","893667144","Welcome to the wizarding world of code reviews.","36344944-6738-48a2-93cc-889bef69a702","2024-03-13","Code Review Hogwarts","\u003Cp>Speaker 0: And now, Dev Thoughts, by Jack Rabbit. I always wondered what a code review at Hogwarts would look like. Ten points to Gryffindor for elegant use of a nested loop, but detention for Slytherin's failure to comment their functions.\u003C\u002Fp>","And now, Dev Thoughts, by Jack Rabbit. I always wondered what a code review at Hogwarts would look like. Ten points to Gryffindor for elegant use of a nested loop, but detention for Slytherin's failure to comment their functions.",[],[],{"year":978,"number":263,"id":3362,"episodes":4086,"show":4087},[3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3350,3378],{"title":3380,"slug":3381},{"id":3959,"slug":4089,"vimeo_id":4090,"description":4091,"tile":4092,"length":527,"resources":12,"people":4093,"episode_number":159,"published":4079,"title":4095,"video_transcript_html":4096,"video_transcript_text":4097,"content":12,"seo":12,"status":19,"episode_people":4098,"recommendations":4100,"season":4101},"remix","913408766","In this video, you'll learn how to use the Directus SDK with a Remix application.","262209d6-8df0-48ef-8a94-6241735fdcae",[4094],{"name":2820,"url":2821},"Get Started with Directus and Remix","\u003Cp>Speaker 0: This is a new Remix project created using the Remix CLI. In this video, I'll show you how to use Directus and Remix using the Directus SDK. Before we get started, make sure to install the Directus SDK so we can use it in a moment, and then run a development server with npm run dev. We want to create an instance of the Directus SDK that we can then use in multiple pages in this Remix application. So inside of this app directory, create a subdirectory called lib and inside of that, a file called directus.js.\u003C\u002Fp>\u003Cp>Import the requisite functions from the directus SDK and then use the create directus function to create a new instance of the SDK being sure that this string is the full URL of your directors project. Then export the client which we can then later import in future files. Inside of this directors project, there is already a global singleton collection. And inside of this collection is a title and a description field. In this project's access control settings, the public role has been given read access over all user created collections, as well as the director's files collection.\u003C\u002Fp>\u003Cp>I have replaced routes index dot TSX with the following. Firstly, we have imported the director's helper that we just created, as well as the read singleton function from the directors SDK. Inside of this files loader, we are going off using the directors SDK to read the singleton collection global. And this will return the object that comes back from Directus which will contain an ID, a title, and a description. This is now available, these three properties, inside of the use loaded data function.\u003C\u002Fp>\u003Cp>We have destructured that, and we can now just use the title and description inside of the page. Inside of our directors project, we've also created a pages collection. Each item in the pages collection has 3 fields. Firstly, the slug. The slug exists to determine what page this data will actually load at.\u003C\u002Fp>\u003Cp>So we're expecting this data to load at slash about. They also have a title and a content WYSIWYG, what you see is what you get editor. So we get this nice rich text editor, but underlying it is just pure HTML. Inside of the routes directory, create a new file called dollar sign slug.tsx, The dollar sign denoting that this is a dynamic route. This one is very similar to before.\u003C\u002Fp>\u003Cp>We import the helper. This time, we import the read item function from the SDK. And inside of the loader, we are gaining access to whatever this dynamic value is. And we are running a read item call here on the pages collection with the value of that URL parameter, slug. That data is once again made available in the use loader data function and we can use it inside of the rendered JSX.\u003C\u002Fp>\u003Cp>Now take note here that we are using dangerously set in a HTML to replace this div with the raw HTML that was inside of our director's item. This is fine if you can absolutely trust that the information being entered in that WYSIWYG editor is correct and not at all malicious. In our directors project, we also have a posts collection. The first three fields are used exactly the same way as pages. The slug, which will denote part of the URL that this page will load at, title, and content.\u003C\u002Fp>\u003Cp>But we also have an image, which is actually a relational field to the director's files collection, a publish date, and an author. This is also a relational field to an item in the author's collection. Right now, authors only have a name because I just wanna show you how to get this relational data. Inside of the route directory, create a new file called blog dot underscore index dot tsx. This is the file that will render when the user goes to slash blog.\u003C\u002Fp>\u003Cp>In this file, we'll use the read items function to query the post's collection. Now you'll notice here that we're also passing in a query object with 2 properties. Firstly, the fields. This specifies exactly what files we want to return from the Directus instance. We're doing this for a couple of reasons.\u003C\u002Fp>\u003Cp>Firstly, we don't want to return everything, specifically, the content field because it's really large. It contains a lot of data in it, and there's no need to increase the size of the request. Secondly, we are getting relational data or nested data from that author relationship. So in here, we're stepping into that author item and we're going to also return the name. The second property is sort, and it means that the latest item will come first, which is quite standard for a block.\u003C\u002Fp>\u003Cp>We then use this data that we fetched in the loader in order to loop over each item that is returned and display a list item. That list item will send users to slash blog slash slug. It will show the title, the publish date, and the author name. Now take note here that publish date, title, slug, they are all direct properties. But author, because it is a relational item and nested item, is in a nested object.\u003C\u002Fp>\u003Cp>So post dot author dot name. To create new blog pages, let's create a new file in routes. We'll call this 1 blog.$signslugdot tsx. Once again, dollar sign means that is a dynamic part of the URL. This should look really similar to when we created dynamic pages.\u003C\u002Fp>\u003Cp>We're using the read item function from the SDK to go query the posts collection, but just the one that has the specific key of the slug, which is in the URL. This time, though, we are also including that query object to what fields we want to return. We're saying here that we wanna get all top level fields, but we also wanna get the author name and the image ID and description. And where as well as rendering the title and the content, we're also rendering the image. All directors assets can be accessed using the full project URL, which we're grabbing here from the SDK slash assets slash image ID.\u003C\u002Fp>\u003Cp>And we are transforming this on the fly to only return an item that's 400 pixels big. We're also using the description set on that file inside of Directus as alt text. Now you may have noticed here there is no slash. The Director SDK will automatically append a slash if you don't provide it when initializing a client. So there is a slash in there.\u003C\u002Fp>\u003Cp>So that's our full project URL\u002Fassets\u002Fid, and then transformed to have a width of 400, and using the description as the alt text. You now know how to grab data from Directus and use it inside of your Remix application. I hope you've enjoyed this video and I'll see you in the next one.\u003C\u002Fp>","This is a new Remix project created using the Remix CLI. In this video, I'll show you how to use Directus and Remix using the Directus SDK. Before we get started, make sure to install the Directus SDK so we can use it in a moment, and then run a development server with npm run dev. We want to create an instance of the Directus SDK that we can then use in multiple pages in this Remix application. So inside of this app directory, create a subdirectory called lib and inside of that, a file called directus.js. Import the requisite functions from the directus SDK and then use the create directus function to create a new instance of the SDK being sure that this string is the full URL of your directors project. Then export the client which we can then later import in future files. Inside of this directors project, there is already a global singleton collection. And inside of this collection is a title and a description field. In this project's access control settings, the public role has been given read access over all user created collections, as well as the director's files collection. I have replaced routes index dot TSX with the following. Firstly, we have imported the director's helper that we just created, as well as the read singleton function from the directors SDK. Inside of this files loader, we are going off using the directors SDK to read the singleton collection global. And this will return the object that comes back from Directus which will contain an ID, a title, and a description. This is now available, these three properties, inside of the use loaded data function. We have destructured that, and we can now just use the title and description inside of the page. Inside of our directors project, we've also created a pages collection. Each item in the pages collection has 3 fields. Firstly, the slug. The slug exists to determine what page this data will actually load at. So we're expecting this data to load at slash about. They also have a title and a content WYSIWYG, what you see is what you get editor. So we get this nice rich text editor, but underlying it is just pure HTML. Inside of the routes directory, create a new file called dollar sign slug.tsx, The dollar sign denoting that this is a dynamic route. This one is very similar to before. We import the helper. This time, we import the read item function from the SDK. And inside of the loader, we are gaining access to whatever this dynamic value is. And we are running a read item call here on the pages collection with the value of that URL parameter, slug. That data is once again made available in the use loader data function and we can use it inside of the rendered JSX. Now take note here that we are using dangerously set in a HTML to replace this div with the raw HTML that was inside of our director's item. This is fine if you can absolutely trust that the information being entered in that WYSIWYG editor is correct and not at all malicious. In our directors project, we also have a posts collection. The first three fields are used exactly the same way as pages. The slug, which will denote part of the URL that this page will load at, title, and content. But we also have an image, which is actually a relational field to the director's files collection, a publish date, and an author. This is also a relational field to an item in the author's collection. Right now, authors only have a name because I just wanna show you how to get this relational data. Inside of the route directory, create a new file called blog dot underscore index dot tsx. This is the file that will render when the user goes to slash blog. In this file, we'll use the read items function to query the post's collection. Now you'll notice here that we're also passing in a query object with 2 properties. Firstly, the fields. This specifies exactly what files we want to return from the Directus instance. We're doing this for a couple of reasons. Firstly, we don't want to return everything, specifically, the content field because it's really large. It contains a lot of data in it, and there's no need to increase the size of the request. Secondly, we are getting relational data or nested data from that author relationship. So in here, we're stepping into that author item and we're going to also return the name. The second property is sort, and it means that the latest item will come first, which is quite standard for a block. We then use this data that we fetched in the loader in order to loop over each item that is returned and display a list item. That list item will send users to slash blog slash slug. It will show the title, the publish date, and the author name. Now take note here that publish date, title, slug, they are all direct properties. But author, because it is a relational item and nested item, is in a nested object. So post dot author dot name. To create new blog pages, let's create a new file in routes. We'll call this 1 blog.$signslugdot tsx. Once again, dollar sign means that is a dynamic part of the URL. This should look really similar to when we created dynamic pages. We're using the read item function from the SDK to go query the posts collection, but just the one that has the specific key of the slug, which is in the URL. This time, though, we are also including that query object to what fields we want to return. We're saying here that we wanna get all top level fields, but we also wanna get the author name and the image ID and description. And where as well as rendering the title and the content, we're also rendering the image. All directors assets can be accessed using the full project URL, which we're grabbing here from the SDK slash assets slash image ID. And we are transforming this on the fly to only return an item that's 400 pixels big. We're also using the description set on that file inside of Directus as alt text. Now you may have noticed here there is no slash. The Director SDK will automatically append a slash if you don't provide it when initializing a client. So there is a slash in there. So that's our full project URL\u002Fassets\u002Fid, and then transformed to have a width of 400, and using the description as the alt text. You now know how to grab data from Directus and use it inside of your Remix application. I hope you've enjoyed this video and I'll see you in the next one.",[4099],"448399a2-6b59-4f6a-acf4-b9472674a650",[],{"year":978,"number":230,"id":3954,"episodes":4102,"show":4103},[3956,3957,3958,3959,3940],{"title":3961,"slug":3962},{"id":3365,"slug":4105,"vimeo_id":4106,"description":4107,"tile":4108,"length":13,"resources":12,"people":12,"episode_number":177,"published":4079,"title":4109,"video_transcript_html":4110,"video_transcript_text":4111,"content":12,"seo":12,"status":19,"episode_people":4112,"recommendations":4113,"season":4114},"constant","893667326","The only thing standing between a variable and its destiny is the courage to compile a different story.","92d03e80-93e2-4953-9f7e-01e5624d6831","Constant","\u003Cp>Speaker 0: And now Dev Thoughts by Jackrabbit. I once met a variable who was feeling pretty constant. It said to me, I want to change, but they won't let me. I told it, be the change you wish to see in the code.\u003C\u002Fp>","And now Dev Thoughts by Jackrabbit. I once met a variable who was feeling pretty constant. It said to me, I want to change, but they won't let me. I told it, be the change you wish to see in the code.",[],[],{"year":978,"number":263,"id":3362,"episodes":4115,"show":4116},[3364,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3350,3378],{"title":3380,"slug":3381},{"id":3280,"slug":4118,"vimeo_id":4119,"description":4120,"tile":4121,"length":213,"resources":12,"people":4122,"episode_number":13,"published":4126,"title":4127,"video_transcript_html":4128,"video_transcript_text":4129,"content":12,"seo":12,"status":19,"episode_people":4130,"recommendations":4132,"season":4133},"cassidoo","922118393","Cassidy is a software engineer, advisor, developer advocate, investor, and memer on the internet, and here's the software, hardware, and analog tools she uses in her day-to-day.","55fd96f5-dde0-4c11-b869-66cbabc3fc94",[4123],{"name":4124,"url":4125},"Cassidy Williams","https:\u002F\u002Fcassidoo.co\u002F","2024-03-12","What's In Your Dock, Cassidy?","\u003Cp>Speaker 0: Hi. My name is Cassidy and I'm a software engineer, advisor, developer advocate, investor, and mimer on the Internet and this is what's in my doc. So firstly, for my devices, I use a custom built Windows PC where I hodgepodge together all of the different parts as time goes on. A 2021 MacBook Pro and a 2018 iPad Pro. My day to day involves a lot of coding, meetings, and general writing.\u003C\u002Fp>\u003Cp>For coding, I use Versus Code. I do also use Vim but the extensions in Versus Code are so good, I use it for most things. For bookmarks, I use Raindrop. Raindrop is great for saving things I want to read. I get full text search of all the things that I'm saving and I can just save it across all devices.\u003C\u002Fp>\u003Cp>For task tracking, I use to do meter which is actually an app that I built myself. It's just a to do list but you can pause tasks for later and it has a progress bar. And I like gamifying things and the progress bar is really great. For flow state style work, I really like to use the Suka. It's a really great app that yells at you if you get a little bit off track and it also has flow state music, task tracking and a bunch of other just nice things to keep you accountable.\u003C\u002Fp>\u003Cp>I also use Dabble Me for journaling, which it just emails you every single day saying how was your day and I can quickly respond. And it's nice to have something that kind of prompts you easily, and then it just saves it for you. And then it also reminds you what you wrote a long time ago. But if there's one tool that I couldn't live without, it's probably Obsidian. It's honestly a glorified markdown editor, but it has so much more than that.\u003C\u002Fp>\u003Cp>Because beyond just editing markdown files and saving it to your computer, it has a plugin system that's open and I've added so many plugins that it's just nice things where for example, you can add Kanban boards to set up project management. It has a data view where you can query your notes as if it's like an SQL database, but it's just your notes. It has templates where, for example, when I write my newsletter, I can have a newsletter template. Or when I wanna write a blog, I have a blog template. And it just has all of the structure built in.\u003C\u002Fp>\u003Cp>It has a graph view so you can see how your notes are together. And then you can also backlink between your notes. And so it creates a sort of wiki for all of your notes together. It's a really really powerful piece of software that I truly use every day for most things. For brain storming, I use a tool called Brain Story.\u003C\u002Fp>\u003Cp>Now full disclosure, it's something that my team built but it's a tool that helps you think through your ideas and get feedback on them. It's not generative AI but it's really great for just talking out a side project, a talk, a blog post, anything that you want to think a little bit more about and get feedback on. I listen to a lot of show tunes. I just love it when people are belting. And I like to think that I can belt with them even though I can't.\u003C\u002Fp>\u003Cp>But it's really fun to listen to that while I'm coding. It feels powerful. But if I really need to focus, it's much more like spacey lo fi type thing so I don't pay attention to the music. I have a bunch of hardware on my desk. Keyboards are probably the main one that I could talk about where I have a bunch of custom mechanical keyboards.\u003C\u002Fp>\u003Cp>I have some behind me. I've got a bunch all over the place. And I really love building them and just kind of rotating out which keyboard I use day to day. But besides that I have some speakers when I'm really just playing loud music. I've got a shure mv7 microphone and I have a Sony a61100 as my camera.\u003C\u002Fp>\u003Cp>I really love laser cutting and kind of crafting making cool things. And I play a lot of guitar as well and really like to arrange some music. And so for laser cutting, I use a tool called Affinity Designer that lets me edit vectors and then put them into a machine. And then I use a tool called the guitar pro for arranging and reading guitar music. I have a notebook with me at all times where I just kind of write and sketch out little ideas where I don't wanna pull out some kind of diagramming software.\u003C\u002Fp>\u003Cp>And I just wanna say here's where this goes over here and just kind of planning out something a bit more visually. And that is the software, hardware, and analog tools that I use. You can find me on the internet at cassid00, c a s s I d o o on most things, and I look forward to seeing you on the internet. Bye.\u003C\u002Fp>","Hi. My name is Cassidy and I'm a software engineer, advisor, developer advocate, investor, and mimer on the Internet and this is what's in my doc. So firstly, for my devices, I use a custom built Windows PC where I hodgepodge together all of the different parts as time goes on. A 2021 MacBook Pro and a 2018 iPad Pro. My day to day involves a lot of coding, meetings, and general writing. For coding, I use Versus Code. I do also use Vim but the extensions in Versus Code are so good, I use it for most things. For bookmarks, I use Raindrop. Raindrop is great for saving things I want to read. I get full text search of all the things that I'm saving and I can just save it across all devices. For task tracking, I use to do meter which is actually an app that I built myself. It's just a to do list but you can pause tasks for later and it has a progress bar. And I like gamifying things and the progress bar is really great. For flow state style work, I really like to use the Suka. It's a really great app that yells at you if you get a little bit off track and it also has flow state music, task tracking and a bunch of other just nice things to keep you accountable. I also use Dabble Me for journaling, which it just emails you every single day saying how was your day and I can quickly respond. And it's nice to have something that kind of prompts you easily, and then it just saves it for you. And then it also reminds you what you wrote a long time ago. But if there's one tool that I couldn't live without, it's probably Obsidian. It's honestly a glorified markdown editor, but it has so much more than that. Because beyond just editing markdown files and saving it to your computer, it has a plugin system that's open and I've added so many plugins that it's just nice things where for example, you can add Kanban boards to set up project management. It has a data view where you can query your notes as if it's like an SQL database, but it's just your notes. It has templates where, for example, when I write my newsletter, I can have a newsletter template. Or when I wanna write a blog, I have a blog template. And it just has all of the structure built in. It has a graph view so you can see how your notes are together. And then you can also backlink between your notes. And so it creates a sort of wiki for all of your notes together. It's a really really powerful piece of software that I truly use every day for most things. For brain storming, I use a tool called Brain Story. Now full disclosure, it's something that my team built but it's a tool that helps you think through your ideas and get feedback on them. It's not generative AI but it's really great for just talking out a side project, a talk, a blog post, anything that you want to think a little bit more about and get feedback on. I listen to a lot of show tunes. I just love it when people are belting. And I like to think that I can belt with them even though I can't. But it's really fun to listen to that while I'm coding. It feels powerful. But if I really need to focus, it's much more like spacey lo fi type thing so I don't pay attention to the music. I have a bunch of hardware on my desk. Keyboards are probably the main one that I could talk about where I have a bunch of custom mechanical keyboards. I have some behind me. I've got a bunch all over the place. And I really love building them and just kind of rotating out which keyboard I use day to day. But besides that I have some speakers when I'm really just playing loud music. I've got a shure mv7 microphone and I have a Sony a61100 as my camera. I really love laser cutting and kind of crafting making cool things. And I play a lot of guitar as well and really like to arrange some music. And so for laser cutting, I use a tool called Affinity Designer that lets me edit vectors and then put them into a machine. And then I use a tool called the guitar pro for arranging and reading guitar music. I have a notebook with me at all times where I just kind of write and sketch out little ideas where I don't wanna pull out some kind of diagramming software. And I just wanna say here's where this goes over here and just kind of planning out something a bit more visually. And that is the software, hardware, and analog tools that I use. You can find me on the internet at cassid00, c a s s I d o o on most things, and I look forward to seeing you on the internet. Bye.",[4131],"f684d5f4-6b2a-4ade-9591-b2cadf0531b6",[],{"year":978,"number":3277,"id":3278,"episodes":4134,"show":4135},[3280,3281,3282,3283,3262],{"title":1027,"slug":1028},{"id":4137,"slug":4138,"vimeo_id":4139,"description":4140,"tile":4141,"length":1078,"resources":12,"people":4142,"episode_number":740,"published":4151,"title":4152,"video_transcript_html":4153,"video_transcript_text":4154,"content":12,"seo":12,"status":19,"episode_people":4155,"recommendations":4160,"season":4161},"c067c012-5fe1-4d70-8946-8bfc8e1b22c0","slack","918839097","In this special edition live episode, Bryant is joined by Kevin, Alex, and Matt to build a clone on Slack in just one hour with Directus Realtime and Nuxt. Join the absolute chaos as friends and colleagues try to \"help\" against the clock. ","08d6a2f3-8c06-4d00-b7c0-cec62b2144cb",[4143,4144,4147,4148],{"name":3590,"url":3591},{"name":4145,"url":4146},"Alex van der Valk","https:\u002F\u002Fdirectus.io\u002Fteam\u002Falex-van-der-valk",{"name":2820,"url":2821},{"name":4149,"url":4150},"Matt Minor","https:\u002F\u002Fdirectus.io\u002Fteam\u002Fmatt-minor","2024-03-06","Mission: Slack (Live Episode)","\u003Cp>Speaker 0: Build in an extra 15 minutes for chaos. I'm gonna be here for the first 20. Then for the 1st presales engineers, Alex, will be here for 20, and then Matt will be here for 20. With that in mind, Bryant, I'm gonna start a timer. No stress, but stress.\u003C\u002Fp>\u003Cp>Take it away.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Let's start the timer. What are we building?\u003C\u002Fp>\u003Cp>Speaker 0: You tell me.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So you you let me know ahead of time, maybe, like, just a hour ago that we were gonna be building. Any guesses in the chat. Right? This might be fun to do live.\u003C\u002Fp>\u003Cp>Right? What are the guesses? No guesses. Alright. I don't see him coming through.\u003C\u002Fp>\u003Cp>We are going to be building a Slack clone. Build direct to us.\u003C\u002Fp>\u003Cp>Speaker 0: In an hour.\u003C\u002Fp>\u003Cp>Speaker 1: No. In an hour. Alright. So, Slack. Right?\u003C\u002Fp>\u003Cp>Yeah. Everybody uses it. You either love it, hate it. Xbox Live. Yeah.\u003C\u002Fp>\u003Cp>We'll do Xbox Live next time. Alright. So let's sketch this thing out. Right? What do we need out of a Slack clone?\u003C\u002Fp>\u003Cp>What feels good as far as functionality for this? How are we gonna set it up in direct us? Facebook, they need some help with their APIs. Yep. Certainly.\u003C\u002Fp>\u003Cp>Yeah. Experienced\u003C\u002Fp>\u003Cp>Speaker 0: that. We all saw that today. What is needed for Slack? I suppose the lightest version is gonna be you have channels, and inside of channels, you have users, and then you have channels. And then inside the channels, you have messages with, like, real time.\u003C\u002Fp>\u003Cp>And I think the that's the slimmest version. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So let's put, like, channels and messages at the top. We would call, like, threads a stretch goal. Yeah. Thread threading feels like, probably more than an hour.\u003C\u002Fp>\u003Cp>Alright. So we got channels. We got messages. We're gonna get the user authentication from direct to its users. And as far as functionality, what do we wanna do?\u003C\u002Fp>\u003Cp>We wanna have a channel. We want to submit messages in the channel. We want that all to update in real time. Notifications? Do we want notifications?\u003C\u002Fp>\u003Cp>We'll\u003C\u002Fp>\u003Cp>Speaker 0: I never wanna be notified when people send me messages ever. There is literally no world where I want that feature. So\u003C\u002Fp>\u003Cp>Speaker 1: so how many Slack groups are you a part of where, like, you just got the whole thing muted, Kevin? That would be a good question.\u003C\u002Fp>\u003Cp>Speaker 0: So let's take a look here. Let me let me open Slack for the first time in a in a hot minute. Okay. I'm in 123456789101112 Slacks. 12 Slacks.\u003C\u002Fp>\u003Cp>And I look at 2 of them regularly. 3 of them regularly. Most of the others are muted.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. Yep. That's how it goes. Same thing for me. Alright.\u003C\u002Fp>\u003Cp>So I've got a brand new instance. Please don't use this password.\u003C\u002Fp>\u003Cp>Speaker 0: Oh my lord. I okay. That's fine. Everything's fine.\u003C\u002Fp>\u003Cp>Speaker 1: Probably need to change that before we get some surprises. Right? Change that real quick. Okay. Alright.\u003C\u002Fp>\u003Cp>So we're inside Directus. How are we gonna map this out? Right? We get the Directus users already out of the box. We get the authentication.\u003C\u002Fp>\u003Cp>We should get real time out of the box as well. So we just need these components. Right? Channels and messages should be fairly straightforward. We'll just call it a channel.\u003C\u002Fp>\u003Cp>Speaker 0: Why would you say that? That's like the beginning of the it should be it should be straightforward. Keep it simple, and it won't be complicated.\u003C\u002Fp>\u003Cp>Speaker 1: Keep it simple, and it won't be complicated. Yeah. Alright. What do we need for a channel? Do we yeah.\u003C\u002Fp>\u003Cp>Yeah. We don't even really need any of this. Right? I don't care when it was created, who it was created by. Just need a name for the channel.\u003C\u002Fp>\u003Cp>That'll be an input. We're going to go to the advanced settings. And because I want this to be URL safe, right, we'll use the slugify option inside the interface, and boom. There's channels. That was really complicated.\u003C\u002Fp>\u003Cp>I I saw Ben was\u003C\u002Fp>\u003Cp>Speaker 0: He begins now.\u003C\u002Fp>\u003Cp>Speaker 1: Let's let's do, like, the sahash. Oh, egg.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, god. We don't have time for this, Brian. Dude God, I'm stressed already, and we're, like, 4 minutes in. Everything.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. Alright. So then we've got messages. Alright. So as far as messages, we want when that message was created.\u003C\u002Fp>\u003Cp>So let's just call that timestamp for simplicity. Fun fact.\u003C\u002Fp>\u003Cp>Speaker 0: I didn't know you could rename those. I literally didn't that is the first time I've ever seen that. Damn.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. You could change it. You know, like, some people prefer updated at, versus date created or update updated. Do whatever you want with it. Alright.\u003C\u002Fp>\u003Cp>So we've got some messages within the messages. What do we have? We have what do we wanna do? Just text text area. We could go to markdown to be super complicated, but we'll just call it content, text, message.\u003C\u002Fp>\u003Cp>What do you Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: I literally I literally don't care. Oh my god. No. I I wouldn't do message because if you're, like, looping through messages, you might use message as the singular, and then, yeah, text feels good.\u003C\u002Fp>\u003Cp>Speaker 1: Text. Alright. Let's unhide this just so we could see.\u003C\u002Fp>\u003Cp>Speaker 0: My leg's literally doing its nervous shake. I'm so glad this is your series and not mine. Oh my gosh. I'm sweating. Is it is it\u003C\u002Fp>\u003Cp>Speaker 1: just the technical difficulties at the start of it? Or or what?\u003C\u002Fp>\u003Cp>Speaker 0: No. It's having an hour on the clock. It's having an hour on the clock, Brian. It's the very format.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. No worries. Alright. So we got some messages. We got some channels.\u003C\u002Fp>\u003Cp>What else do we really need here? Oh, we'll come back to threads later. Right? We've already got our users. I'm gonna go ahead and, let's add you as a user, Kevin.\u003C\u002Fp>\u003Cp>Kevin, add example. And I'm not gonna tell you guys what the password is here. Alright. Now let's go into\u003C\u002Fp>\u003Cp>Speaker 0: I did actually think reactions would be a nice sorry. That was me interacting with, Ben in the chat. I did actually think reactions would be a nice a nice one as well. It's quite common.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Cool. It'd be an interesting one to add. Yeah. We'll come back to it for sure.\u003C\u002Fp>\u003Cp>Alright. So we're gonna create a role for users so we can give access to messages for channels, etcetera. We're just gonna go ahead and give all access to this to start with. And the other thing that I'm gonna do, let's give public access to create we'll come back to, like, registering users.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Yeah. No. That's later.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Okay. Alright. So we've given access. Now we can access any as long as we're in this user role, we'll be able to access channels, messages, all of that.\u003C\u002Fp>\u003Cp>Let's actually start to build something. Right? If I wanted to, I could give Kevin access to this right now. He could log in. Let's just do that.\u003C\u002Fp>\u003Cp>Speaker 0: You're just seeing my stress face here. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: I just see your stress face. Kevin at example.\u003C\u002Fp>\u003Cp>Speaker 0: And Kevin Oh, that is a lowbrow password.\u003C\u002Fp>\u003Cp>Speaker 1: Is it lowbrow?\u003C\u002Fp>\u003Cp>Speaker 0: Wow. I mean Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: So let's go in. I'm gonna just create a couple\u003C\u002Fp>\u003Cp>Speaker 2: of channels.\u003C\u002Fp>\u003Cp>Speaker 0: Tests, though.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, yeah. That's right. Let me just go ahead and give that. Because eventually, you're gonna be interacting through this Nuxt front end. But Yeah.\u003C\u002Fp>\u003Cp>Yeah. Cool. Alright. So we create a couple channels. Right?\u003C\u002Fp>\u003Cp>What do we like, guys? Throw some suggestions in the chat. We'll add a few of these channels. Let's start with Random? Evan is crazy nervous channel.\u003C\u002Fp>\u003Cp>Speaker 0: My leg's shaking.\u003C\u002Fp>\u003Cp>Speaker 1: General, memes. Okay. Sounds great. Got some memes. Alright.\u003C\u002Fp>\u003Cp>And now as far as messages, right, we got the user. We got the timestamp. We've got when it was updated. In case we change it, we got the text. But the thing that we don't have is how these messages belong to a channel.\u003C\u002Fp>\u003Cp>So inside Directus, we'll just go in. Again, creating these relationships, super easy. I don't even have to touch SQL. We'll go in, create a mini to 1 relationship for this. We'll call it channel, and we'll use our channels collection.\u003C\u002Fp>\u003Cp>And\u003C\u002Fp>\u003Cp>Speaker 2: what else do we need?\u003C\u002Fp>\u003Cp>Speaker 1: Maybe display template. We'll use name. Great. Cool. And now I could populate a message into a specific channel.\u003C\u002Fp>\u003Cp>Hey, Kev. I'll be nervous.\u003C\u002Fp>\u003Cp>Speaker 0: I have the most faith in you while having absolutely no faith in you at the same time. By the way, that user you created for me still doesn't have app access. You might need to go into the user settings and allow that. I thought doing the role would be sufficient, but, oh, I'm not I'm not in that role. I'm not in that role.\u003C\u002Fp>\u003Cp>Users in that role was 0.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Gotcha. Yep. Try it now. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: I'll tell you in a moment.\u003C\u002Fp>\u003Cp>Speaker 1: Tell me in a moment. Alright. So we've got the basic logic here. Like, we could go and fetch the data from this API if we wanted to just by going to messages or item slash messages. We're gonna get a forbidden because I'm not logged in, but that's okay.\u003C\u002Fp>\u003Cp>We'll take care of that.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, we're 10 minutes in. This? Sorry. No stress, but we're 10 minutes in. That's half my time here.\u003C\u002Fp>\u003Cp>I'm I'm halfway to freedom because I am sweating so hard.\u003C\u002Fp>\u003Cp>Speaker 1: Don't sweat, man. Somehow I got logged out. So chaos rains, my friend. Oh my gosh. Glass work.\u003C\u002Fp>\u003Cp>Okay. Alright. I don't know how I got logged out, but okay. So we've got channels. We've got messages.\u003C\u002Fp>\u003Cp>Let's actually do something. I low key hate this, he says. I can still see the chat in the other window, Kevin. So you're not you're not giving me much confidence.\u003C\u002Fp>\u003Cp>Speaker 0: I'm so sorry.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. Yeah. No worries, man. No worries. Alright.\u003C\u002Fp>\u003Cp>So this is my NUC starter from all the other episodes that we've got. I've switched it up a little bit just so you have a simple plug in for the sake of this live instance because the the other starter that had had a Nuxt module. It's got a lot going on. This is really simple. Right?\u003C\u002Fp>\u003Cp>So we've got a direct us plug in, that is in the plug ins directory inside Nuxt, which will automatically register this. And really simple. Right? Inside Nuxt, we've got a runtime config that we're calling to get the directus URL. And my Nuxt config looks like this.\u003C\u002Fp>\u003Cp>Right? We've got a direct us URL. Right now, that is pointing to, like, a local host, so we do need to switch this over. We'll just go to our URL here. It is switched over.\u003C\u002Fp>\u003Cp>Great. As far as the server token, I don't think we even actually will be using this. I don't think I've got it anywhere else in my config either. Cool. So we'll save that.\u003C\u002Fp>\u003Cp>And now we want to, like, start fetching some data. Right? There's a couple of routes that I already have set up in this, like a login and a register route. Drink a red bar too.\u003C\u002Fp>\u003Cp>Speaker 0: I've got all my caffeine.\u003C\u002Fp>\u003Cp>Speaker 1: More I've\u003C\u002Fp>\u003Cp>Speaker 0: got my caffeine right here. Don't you worry.\u003C\u002Fp>\u003Cp>Speaker 1: Give him some more energy drinks, please.\u003C\u002Fp>\u003Cp>Speaker 0: I'm gonna go get one. I'll be right back. Woof. Woof. I am actually doing it.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So let's fire up the dev server for Nux here. As far as the stuff that I've already got, like I said, I've got a a login route, a register route that we'll try to mess with. Looks like this. We'll go in and log in, where we can register.\u003C\u002Fp>\u003Cp>Very simple. And then we just have a index page, and then, I had some testing that I was doing here. Alright. So as far as layout, what do we want? You know, a lot of times, I it's like I I love Tailwind.\u003C\u002Fp>\u003Cp>Tailwind UI is totally worth the money, especially for, like, quick prototypes like this. I usually like, if it's just something I'm hacking on personally, I'll start with, like, the application shells. Like, sidebar layout. Right? This feels pretty good for, like, a Slack type of setup.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That looks good.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. So let's just go in. We've got that set to view. Where are we gonna run into issues at? Probably with the icons because I don't have those set, but no matter.\u003C\u002Fp>\u003Cp>Alright. So as far as our pages, let's do what are we gonna do here? Let's just do, like, an app directory. Within that, we'll probably have, like, some channels.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That makes sense. Just just And then the dynamic route inside of that. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. So we'll do, like, channel dot view. And then also, what I can do is set up, like, a a parent root for the app. So if I go here inside the pages directory and I do app dot view, I paste this in.\u003C\u002Fp>\u003Cp>Speaker 0: Is this for, like, the sidebar? Like, the sidebar will be held in this?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Exactly. Nice. Alright. So we'll save that.\u003C\u002Fp>\u003Cp>Let's see what we get.\u003C\u002Fp>\u003Cp>Speaker 0: That is a major indentation. Yikes.\u003C\u002Fp>\u003Cp>Speaker 1: K. So these are Rike's rules. I'm not sure if he's on here, but, alright.\u003C\u002Fp>\u003Cp>Speaker 0: He quit out. He hit the wrong keyboard shortcut. That is absolutely what happened. He got arced. That's what our team say when you accidentally, like, quit the whole window.\u003C\u002Fp>\u003Cp>I'm sure he'll be back in a moment. Until then, you got me, stressed as heck. Got my energy drink, waiting for the waiting for the stress to return. This is my brief moment of respite here. That was a major indentation, right?\u003C\u002Fp>\u003Cp>Is it worth the time of pause? Is it worth the time of pause?\u003C\u002Fp>\u003Cp>Speaker 1: Did you get arced? I I don't know what happened. Yeah. I guess I did.\u003C\u002Fp>\u003Cp>Speaker 0: We're back. Sorry. You don't need to be sorry and you don't get that time back.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, yeah. Yeah. It's still running. I don't know what happened, man. I really don't.\u003C\u002Fp>\u003Cp>Speaker 0: Weird.\u003C\u002Fp>\u003Cp>Speaker 1: All right. So getting some errors here. We get console wise. Home icon. Navigation.\u003C\u002Fp>\u003Cp>Yeah. This is the only bad part. I'm gonna zoom out just a little bit here so I can find\u003C\u002Fp>\u003Cp>Speaker 0: Just to actually navigate this, healthscape of tabs. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Just delete a bunch of these icons. Gosh. They put so many icons in this.\u003C\u002Fp>\u003Cp>Speaker 0: Everyone that direct us has a personal choice between Mac and Windows, by the way. So, you know, some folks use use either. I would say with the folks I work with directly, I think we mostly use Max, but that's definitely not the case across the board. Tim is my resident, brain slug. Tim is is my resident Windows user.\u003C\u002Fp>\u003Cp>Oh, let's see. It's not about getting to use Linux. It's about wanting to use Linux. So folks wanna use it, whatever. Most of the stuff we use is in the cloud, so it doesn't really\u003C\u002Fp>\u003Cp>Speaker 1: matter. Yeah. Okay. Well, at least we got something now. Right?\u003C\u002Fp>\u003Cp>Alright. We're gonna ditch teams here at the bottom. Where are you? Your teams. Great.\u003C\u002Fp>\u003Cp>Just ditch that list of stuff.\u003C\u002Fp>\u003Cp>Speaker 0: Can you zoom back in? I'm like, I need my spectacles to see it over there.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. Teams is gone. Right? So now we want to fetch the list of channels here and display those. So how do we do that, Kev?\u003C\u002Fp>\u003Cp>Speaker 2: Do you have the\u003C\u002Fp>\u003Cp>Speaker 0: Directus SDK in this app?\u003C\u002Fp>\u003Cp>Speaker 1: We do have the Directus SDK in this app.\u003C\u002Fp>\u003Cp>Speaker 0: Lovely stuff. Well, have you got it set up as, like, a as, like, a plug in, or do we need to do that now?\u003C\u002Fp>\u003Cp>Speaker 1: As a Nuxt plug in. So we're gonna Very good. We'll use dollar sign direct us, use Nuxt app. The only thing that I don't have is, like, the auto imports from the SDK. That was one of the nice things about the the other module I had.\u003C\u002Fp>\u003Cp>So we're gonna need to do, what, read items? Yeah. From atdirectus SDK.\u003C\u002Fp>\u003Cp>Speaker 0: I think that's it.\u003C\u002Fp>\u003Cp>Speaker 1: Speaking of this.\u003C\u002Fp>\u003Cp>Speaker 0: What's it complaining about? White space, I think. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. These are, again, these are Rikes, ESLint, and\u003C\u002Fp>\u003Cp>Speaker 0: Which are very legit for building actually serious things unlike this.\u003C\u002Fp>\u003Cp>Speaker 1: They're very, very legit. Right? Alright. So let's do what do we want? We want channels.\u003C\u002Fp>\u003Cp>There's a ref for channels, and then we're going to well, actually, we should just be able to use Nuxt async data. Right? Nuxt data error. We get an error. We're gonna go, wait, use async data.\u003C\u002Fp>\u003Cp>We'll give this a key. Let's just call it channels. And then we just return something from Directus, right? So Oh,\u003C\u002Fp>\u003Cp>Speaker 0: look at that interesting code pilot code using old SDK methods. Fascinating.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Not right. Right? That's a oh, what are we doing there? Auto completion or something.\u003C\u002Fp>\u003Cp>So we'll do read items. We've got channels. Rates. Oh, man. GitHub Copilot is getting on the way this time.\u003C\u002Fp>\u003Cp>Speaker 0: You can turn it off if you want.\u003C\u002Fp>\u003Cp>Speaker 1: We do only have an hour. We'll see. Alright. So, basically, we're gonna read all the items from channels. We wanna get the fields.\u003C\u002Fp>\u003Cp>We'll just use the wild card. It's basically just name and ID. Does that do it? It should do it. Let's just destructure this and oh.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Are you are you\u003C\u002Fp>\u003Cp>Speaker 0: As as channels.\u003C\u002Fp>\u003Cp>Speaker 1: Needs to be channels, and we'll just call this channels. And hopefully\u003C\u002Fp>\u003Cp>Speaker 0: And now now usually use channels.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. We just go down here. Item in channels.\u003C\u002Fp>\u003Cp>Speaker 0: Very nice. And I think we could it is name. The key is name. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Item dot name. We're just gonna swap this for, like, Nuxt link. One of the interesting things, like, Nuxt link will take a h ref or, like, a 2 property. So that's one of the nice things that I like about it just because it's you know, you're used to using h ref on the regular side when you're creating a regular link.\u003C\u002Fp>\u003Cp>Right. Do you wanna do\u003C\u002Fp>\u003Cp>Speaker 0: you wanna know what's happened? We've, we've hit the 20 minute mark, and, thank gosh. I don't have to be here like, I can sweat from the audience. At this point, I'm gonna I'm gonna hand off to the amazing Alex who I'm gonna bring in. Hang on.\u003C\u002Fp>\u003Cp>Let let me add him in. Alex, would you, would you like to say hello?\u003C\u002Fp>\u003Cp>Speaker 2: Good evening, everyone. Can you hear me alright?\u003C\u002Fp>\u003Cp>Speaker 0: I can hear you grand. Lovely. Your your lovely tones. Excellent. I've\u003C\u002Fp>\u003Cp>Speaker 2: I've, enjoyed watching you sweat, Jacob. It's been great.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, I'm sorry. It's coming it's coming for you too, mate.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Yeah. Yeah. I'm I'm learning a lot of you right now as well. So\u003C\u002Fp>\u003Cp>Speaker 1: Hey, Viv. Hey. We failed to resolve next link. What is that all about?\u003C\u002Fp>\u003Cp>Speaker 0: I'm gonna peace out everyone. I'm gonna put you in Alex's very capable hands for 20 minutes, and then, he will be handing off to Matt. Bye for now, folks. Enjoy the rest. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: I thought we were doing, like, Royal Rumble rules. Everybody just hangs on. Be fun. Oh.\u003C\u002Fp>\u003Cp>Speaker 2: You just call me now, Brian.\u003C\u002Fp>\u003Cp>Speaker 1: I that's alright, my friend. How are you, dude? It's been a while since we've, like like paired up for something.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Yeah. Yeah. This is this is cool, man. I, always enjoy, seeing all this view code, which I wish I understood better.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Most of it is is tailwind at this point. But, yeah. So we having an error right now. Right?\u003C\u002Fp>\u003Cp>We're not having permission to access the channels. So we just need to log in. Right? So I'm gonna go to my login form components. Let's redirect to slash app.\u003C\u002Fp>\u003Cp>And we're gonna try to log in to this thing as well. I guess I need to add a user for you. Yep. I have one for Alex. Alright.\u003C\u002Fp>\u003Cp>Oh, don't forget the role. Alright. So I'm just gonna try to log in as Alex now. So we're gonna log in. Oops.\u003C\u002Fp>\u003Cp>Nope. That's not right. It's auth\u002Flogin. I already used the link on the home page. That would have worked.\u003C\u002Fp>\u003Cp>So we got Alex at example, and the password is super secure. Okay. Alright. So now we can see at least we have our channels up here. Right?\u003C\u002Fp>\u003Cp>That looks good.\u003C\u002Fp>\u003Cp>Speaker 2: Yep. Yeah. Nice.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. How are you feeling, Alex? Are you nervous as as Kevin? Like, feeling good? I\u003C\u002Fp>\u003Cp>Speaker 2: I've got a feeling you got the power of Bry Ross, under the hood there. So Ross. Big. I've got confidence, man. I got confidence.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Right. Alright. So now we need to fix, like, the h refs here. Like, hey.\u003C\u002Fp>\u003Cp>We wanna navigate to one of these channels. Right? Let's just test if this is gonna work. I'm a script at the top guy. Not sure if we've got any other view fans or not.\u003C\u002Fp>\u003Cp>I like seeing what's going on with the JavaScript first, and we're going to use the routes. So we'll do routes. Let's use routes. And I'm just gonna do this. I'm just gonna log this out.\u003C\u002Fp>\u003Cp>Right? I wanna look at routes a params.channel. K. Good. The other thing that we're gonna need to do somewhere in here where's the main dev?\u003C\u002Fp>\u003Cp>Main content. We're gonna plug in this Nux page here. So this should render the child page, for all my nested routes. Yeah. Alex is a boss.\u003C\u002Fp>\u003Cp>So, I'm not sure if that's John Daniels or not, but, I will say, like, if you've used the template CLI tool at all, Alex is is that guy. He's the guy that put that together originally.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Thanks thanks to directors having good APIs. It was, yeah, difficult, but we we got there, and it's pretty cool right now.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Alright. So we got the next page. I save. 2 2 what's going on?\u003C\u002Fp>\u003Cp>Oh, yeah. I forgot to fix the login again. Yeah. Apparently.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: You need to persist that. Alright. So what else do we need to do? We need to fix those links. Channels.\u003C\u002Fp>\u003Cp>Where are you? Alright. So this will be channel or item dot name.\u003C\u002Fp>\u003Cp>Speaker 2: Item dot name. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: And then we'll do \u002Fchannels \u002Fitem.name. Should get it. We'll do a Nuxt link. Oh, duh. It's app dot channels.\u003C\u002Fp>\u003Cp>Speaker 2: Nice.\u003C\u002Fp>\u003Cp>Speaker 1: So now we have our channels working. Right? What are we gonna do inside the channels? Right? Great call.\u003C\u002Fp>\u003Cp>What do we do inside the channels? We are going to here is this is where we're actually gonna fetch, like, real time data. Right?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. You think? Yeah. Yeah. I'm looking forward to this part, the the, real time.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Alright. So I'm gonna cheat a little bit. We're just gonna go into our documentation. We do have this nice guide, if you haven't checked it out, on real time multi user chat.\u003C\u002Fp>\u003Cp>There's a vanilla JavaScript version. There's React. There's Vue. Let's just take a look at this. Right?\u003C\u002Fp>\u003Cp>Alright. How are we going to authenticate with real time? So we've got, the authentication composable. We've added the real time composable. All of those are in my plug in here.\u003C\u002Fp>\u003Cp>So it should be as simple as, by calling connect. That'd be right. Let's see. Kevin's got a I think it was Kevin that he's got. We've got a subscribe function here.\u003C\u002Fp>\u003Cp>So we're gonna subscribe to the messages, client dot connect. Yep. Let's see here.\u003C\u002Fp>\u003Cp>Speaker 2: This is usually where I run into some SSR issues, but I'll we'll see how you manage this one, Brian.\u003C\u002Fp>\u003Cp>Speaker 1: Let me show you. SSR equals false.\u003C\u002Fp>\u003Cp>Speaker 2: That's That's the solution there?\u003C\u002Fp>\u003Cp>Speaker 1: That's the easiest way. Yeah. Yeah. Absolutely. Alright.\u003C\u002Fp>\u003Cp>Alright. So let's just, is this cheating? Yeah. This probably is. Use whatever you have at your disposal.\u003C\u002Fp>\u003Cp>Right? So the subscription client dot subscribe messages, that's correct. Our fields here that we want, we're grabbing the all the fields on the root level. I guess I could zoom in a little bit and make that easier for everybody. Then we have user.\u003C\u002Fp>\u003Cp>So let's just grab all the fields from the user. We've got message of subscription, and then it looks like there's a receive message function that goes along with this.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. You yeah, you might need to filter on the channel as well, but maybe we can do that in the next step.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So we've got data type equals subscription. This is just like the init. Right? Let's see if we are getting any.\u003C\u002Fp>\u003Cp>No. Yeah. This is gonna be interesting. I should have set up better authentication. So persisted on this.\u003C\u002Fp>\u003Cp>Alright. Do we see anything from, see some more issues from Tailwind? X mark icon. Where are you? Note to self, on the next one, do not import any of the icons from.\u003C\u002Fp>\u003Cp>So this should be, like, we definitely wanna redirect on this. Right? Let's add app. Let's add some page meta. Define oh, actually, it's not a const.\u003C\u002Fp>\u003Cp>We'll just do define page meta. And if we give this a middleware of auth, it should automatically redirect. And then I'm gonna go into where's my login form? I'm gonna make this really easy for myself, Alex.\u003C\u002Fp>\u003Cp>Speaker 2: Oh, good idea.\u003C\u002Fp>\u003Cp>Speaker 1: And password, super secure. We'll learn a little bit about everything here. Okay. So it's redirected. Sign in.\u003C\u002Fp>\u003Cp>This should take us back to the app, where we could get all the channels. What am I not seeing here? Right. Do we have to do we need to connect to the client, I guess? Let's plug in why we're not receiving\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Client dot connect. Right? Isn't that it?\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So this is on mounted, but we're not using SSR. This should just work. Right? Where are we gonna stick that?\u003C\u002Fp>\u003Cp>On? Oh, what if we just call subscribe? Set up all these functions, forgot to call them. Alright. Client is not defined.\u003C\u002Fp>\u003Cp>Duh. Because it's not the client, it should be directus dot subscribe. So, again, we're gonna grab that directus plugin that we set up from use Nuxt app. It's error data is not defined. Oh, we gotta pass the data to that.\u003C\u002Fp>\u003Cp>Depcription started. Okay. Alright. So what do we wanna do now? Within the channel, we wanna have all of our messages.\u003C\u002Fp>\u003Cp>Right? I'm assuming that will be an array.\u003C\u002Fp>\u003Cp>Speaker 2: Sounds good to me.\u003C\u002Fp>\u003Cp>Speaker 1: In our received messages Actually, let's just do this to start with, see what type of data we're getting back. Received message. Alright. So now if we go in, I got, like, 35 tabs here, don't I?\u003C\u002Fp>\u003Cp>Speaker 2: Don't we all?\u003C\u002Fp>\u003Cp>Speaker 1: For apps. For hours. Live.\u003C\u002Fp>\u003Cp>Speaker 0: Or I\u003C\u002Fp>\u003Cp>Speaker 1: forgot the URL. It's just 100 apps dotdirectus.app. And I've logged myself out again. Let's keep that.\u003C\u002Fp>\u003Cp>Speaker 2: I can't help you here, Brian. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So now how do I split these out? That's the only thing I get hung up on on our half the time. It's like, hey. How do we make this, go split screen?\u003C\u002Fp>\u003Cp>Alright. So couple things should be happening here. Right? We've got our subscription. Let's make this larger.\u003C\u002Fp>\u003Cp>Right. So the subscription has started. We're in the Kevin is crazy crazy nervous. We'll probably have to filter that on our front end anyway, but, testing 1, 2, 3. We'll just add the channel to begin with.\u003C\u002Fp>\u003Cp>And Oh. Cool. So we we see the real time is connecting. We're getting our data. There's the actual messages.\u003C\u002Fp>\u003Cp>Great. Right. We've got the test. We've got the user is null.\u003C\u002Fp>\u003Cp>Speaker 2: Probably permissions.\u003C\u002Fp>\u003Cp>Speaker 1: Correct. That is correct. Alright. We need to make sure that the user role can access all the users. So right now, it's just by default, like, just a one user.\u003C\u002Fp>\u003Cp>We'll just make it so we can read all the users, and let's send a new one. Test 567. Save. Get our message. Okay.\u003C\u002Fp>\u003Cp>Now do we get the user data? Okay. Cool. That's a lot more user data than we actually wanted. So let's go in and trim that down.\u003C\u002Fp>\u003Cp>We'll just do first name, last name, avatar. We probably want the ID too.\u003C\u002Fp>\u003Cp>Speaker 2: And the\u003C\u002Fp>\u003Cp>Speaker 1: dot ID.\u003C\u002Fp>\u003Cp>Speaker 2: And the first name, not the first name.\u003C\u002Fp>\u003Cp>Speaker 1: First name.\u003C\u002Fp>\u003Cp>Speaker 2: Not the\u003C\u002Fp>\u003Cp>Speaker 1: catch. Good catch. Alright. Cool. And then here, we need to what?\u003C\u002Fp>\u003Cp>We want to see the message. Where's that message at? Alright. Subscription, create data. We want to filter that.\u003C\u002Fp>\u003Cp>Right? What are we gonna call this? For each message of subscription, We need to populate that data. Right?\u003C\u002Fp>\u003Cp>Speaker 2: Do do you wanna filter it at the at the WebSocket level or just at the whenever a new message is received? You could do both, I guess. But\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I I'm trying to think of, like, in Slack, you'd probably have, like, some kinda indicator up here that's, like, hey. Yeah. Yeah. Even if a message in a different channel popped up, you would probably filter it out on the the front end.\u003C\u002Fp>\u003Cp>Right? Yeah. On on the specific channel Because you probably have, like, some type of yeah. Alright. So receive message here.\u003C\u002Fp>\u003Cp>What do we does it show the oh, let's get the channel name as well. Query Oh, yeah. That's that'd be a useful one. Dotidchannel.name. So here, if the data type equals subscription.\u003C\u002Fp>\u003Cp>Create. And data event equals create. We will push those messages into our array of messages. And then instead of this, we should be showing a some messages. Alright.\u003C\u002Fp>\u003Cp>What we doing on time? I've totally lost the window here.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Me too. Me too. It's about quarter to the hour on my clock. Let's\u003C\u002Fp>\u003Cp>Speaker 1: just take a look. 22 minutes left. Okay. Alright. Cool.\u003C\u002Fp>\u003Cp>Fun. Fun. Fun. Alright. GitHub Copilot for the win.\u003C\u002Fp>\u003Cp>Do we how confident are we feeling? I can't even see the chat at this point.\u003C\u002Fp>\u003Cp>Speaker 2: But Not confident in that one.\u003C\u002Fp>\u003Cp>Speaker 1: So we'll do, like, a list for all the message. Yeah. Again, hey. Like, you gotta take it with a grain of salt. Message dot content.\u003C\u002Fp>\u003Cp>You know, I I think this would get better if we had typed this out, but Yeah. Key message dot ID, we should have that for sure. We'll just close that list, see what we get. Alright. Do we have any messages?\u003C\u002Fp>\u003Cp>The other thing is, like, do we fetch the messages for this channel on\u003C\u002Fp>\u003Cp>Speaker 2: Initially. Right?\u003C\u002Fp>\u003Cp>Speaker 1: On yeah. And, like, do you have you messed with the the real time and, like, the chat stuff at all, Alex, or no?\u003C\u002Fp>\u003Cp>Speaker 2: Not not a not as much as I should have, but, this is an interesting one because you might not even need to use the real time in this in this example.\u003C\u002Fp>\u003Cp>Speaker 0: Just Hello, everyone. It's the disembodied voice of Kevin here. Hello. I'm watching, I've been screaming into the void in chat. I know this isn't I know this is an illegal move to throw you a bone here, but the init payload comes with the initial items.\u003C\u002Fp>\u003Cp>Up to a 100, and you can set limit minus 1 to get more than a 100. Okay. Peace out. Bye. What was that, Alex?\u003C\u002Fp>\u003Cp>You wanna miss off the clock, mate.\u003C\u002Fp>\u003Cp>Speaker 2: Okay. Thanks, Gabe. I think that's probably just, just we you can start logging out the the all all the messages that come through from the website. Right? So it's probably a yeah.\u003C\u002Fp>\u003Cp>So it would be one of the messages received maybe, with the initial with a different type. I don't know if I've seen that in your logs to be fair. But\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Let's back up. Subscription started. Received message. No.\u003C\u002Fp>\u003Cp>Matt's gonna save the day on this.\u003C\u002Fp>\u003Cp>Speaker 2: We should know this. I should know this, Bryant.\u003C\u002Fp>\u003Cp>Speaker 1: That's alright. So, like, we we'd also do it this way as well. Right? Where we go in and read items. Oh, nope.\u003C\u002Fp>\u003Cp>That's gonna be coming from the SDK. Read items from directus s e k. Let's just call this\u003C\u002Fp>\u003Cp>Speaker 2: Oh, I think no. I'm starting to sweat. Thanks, Kevin.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. No worries. God. What am I doing? Constant populate messages equals async.\u003C\u002Fp>\u003Cp>Yeah. There we go. This is wrong. Totally wrong. Oh, no.\u003C\u002Fp>\u003Cp>That's not that bad, actually. Filter channel equals route dot params.channel. Equal route dot value. No. Should just be route dot is it route dot value?\u003C\u002Fp>\u003Cp>Is route\u003C\u002Fp>\u003Cp>Speaker 0: reactive? Hello. Hello, everyone. It's me again. It's Kevin again.\u003C\u002Fp>\u003Cp>I was I was having the chat with Matt, and in the nature of chaos, we've decided to Royal Rumble this. So it is my pleasure to introduce mister Matt Minor. Hello, Matt.\u003C\u002Fp>\u003Cp>Speaker 3: Hey, everyone. I, will be the most useless person on this call because I'm not\u003C\u002Fp>\u003Cp>Speaker 0: Oh, hardly.\u003C\u002Fp>\u003Cp>Speaker 3: I'll help you pick the colors. And I was actually thinking, maybe I could just sabotage Bryant here at the end.\u003C\u002Fp>\u003Cp>Speaker 1: Just Sabotage.\u003C\u002Fp>\u003Cp>Speaker 3: The headlines of the day or something. Do it,\u003C\u002Fp>\u003Cp>Speaker 0: man. Oh, no. Don't don't do that. Can you imagine?\u003C\u002Fp>\u003Cp>Speaker 3: What do we have? We have we have 15 minutes.\u003C\u002Fp>\u003Cp>Speaker 0: No. There's there's 20 left. We got just just 20 minutes left. We're 2 thirds of the way in. No pressure.\u003C\u002Fp>\u003Cp>But it it doesn't look like Slack yet, does it? Where are the messages? Yeah. Alex, is it is this what you did with your 20 minutes, mate?\u003C\u002Fp>\u003Cp>Speaker 2: Mate, we were we were work we were working hard. Yeah. We were working hard. All hardly working.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. This was\u003C\u002Fp>\u003Cp>Speaker 3: where are we stuck?\u003C\u002Fp>\u003Cp>Speaker 1: What are\u003C\u002Fp>\u003Cp>Speaker 3: we, what are we doing?\u003C\u002Fp>\u003Cp>Speaker 1: Just gonna load the initial messages.\u003C\u002Fp>\u003Cp>Speaker 0: So so I know from experience that when you I don't know what's up with maybe how you're including real time here, but that init payload that init message does come. It definitely comes with all of the mess with all of the items in the collection, but I have also observed it's not happening here. So I think that would be the easiest fix is, like, why isn't that working? Because then you can just use the real time interface.\u003C\u002Fp>\u003Cp>Speaker 1: Right. Why isn't that working\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Kevin?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Bloody great question, mate.\u003C\u002Fp>\u003Cp>Speaker 1: So I log in. We go to Kevin is crazy nervous. We start this subscription.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. But where is the where's the payload? Where's that init payload? So in oh, oh, I think maybe it Oh,\u003C\u002Fp>\u003Cp>Speaker 1: is it is it here?\u003C\u002Fp>\u003Cp>Speaker 0: Is it is it because you're just subscribing to the single event? If you remove the event from, like, inside of the, if you get rid of that and get every everything over that subscription, does that help? There it is.\u003C\u002Fp>\u003Cp>Speaker 1: Data. Oh, okay. Yep. Okay. So just use that.\u003C\u002Fp>\u003Cp>Nice. Okay. There we go. My man.\u003C\u002Fp>\u003Cp>Speaker 3: That's what I was gonna say too. But, Kevin\u003C\u002Fp>\u003Cp>Speaker 0: I know. Sorry. I felt I felt emanating from your your corner of the ring. How are you doing today, Matt?\u003C\u002Fp>\u003Cp>Speaker 1: Is I'm good. I'm doing good.\u003C\u002Fp>\u003Cp>Speaker 3: I'm excited to be\u003C\u002Fp>\u003Cp>Speaker 0: here. Good. News headlines for today?\u003C\u002Fp>\u003Cp>Speaker 3: Facebook and Instagram. No. Struggling.\u003C\u002Fp>\u003Cp>Speaker 0: I I sorry. I was too busy working, so I didn't know that.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, yeah.\u003C\u002Fp>\u003Cp>Speaker 3: Sorry. I was too I, it just came up\u003C\u002Fp>\u003Cp>Speaker 2: when\u003C\u002Fp>\u003Cp>Speaker 3: I was logging in to work today. So, how's the chat doing? Is anybody Let's see.\u003C\u002Fp>\u003Cp>Speaker 2: You you might have to spread that one, Bryant. I think.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. You will. You will.\u003C\u002Fp>\u003Cp>Speaker 3: Just enjoying the chaos. Yeah. But for a net\u003C\u002Fp>\u003Cp>Speaker 0: but for a net, dude, for a net, don't push it into the array. Just replace the array because\u003C\u002Fp>\u003Cp>Speaker 1: Oh, okay.\u003C\u002Fp>\u003Cp>Speaker 0: There's gonna be nothing in it at the start.\u003C\u002Fp>\u003Cp>Speaker 1: Data dot data.\u003C\u002Fp>\u003Cp>Speaker 0: Joshua, just enjoying the cast. Honestly, so are we. As I said, this is our first kind of live event of this kind, and I don't know. I'm having a great time. It is chaotic, and we're just making it up.\u003C\u002Fp>\u003Cp>We changed the format. Who bloody cares? Love it.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Nope. Alright. So now we got some messages. Right?\u003C\u002Fp>\u003Cp>Let's, let's mess with this a bit. Alright. So we're gonna have a div. We're gonna show the avatar, image source,\u003C\u002Fp>\u003Cp>Speaker 0: message. Joshua, it's so funny. You're like, yeah. You know, I'm struggling, like, getting this, you know, flow automation thing to work while listening to you in the background. And the funny bit is some of the people who would be there to help you are currently here.\u003C\u002Fp>\u003Cp>So it's like, have fun struggling, but you're struggling on your own for the next, 15. Nah. Jonathan's there. Funny. And\u003C\u002Fp>\u003Cp>Speaker 3: you think you're struggling\u003C\u002Fp>\u003Cp>Speaker 1: to get, like,\u003C\u002Fp>\u003Cp>Speaker 3: 4 people watching you work at the they were all just staring over Brian Schuler right now.\u003C\u002Fp>\u003Cp>Speaker 1: I love it, man. Thank you. This is this is this is more fun for me. I think I feel\u003C\u002Fp>\u003Cp>Speaker 3: like I go, when I go shopping with my wife and, like, just follow her around the store. That's what I feel like right now. I know value just in the way.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. So far.\u003C\u002Fp>\u003Cp>Speaker 1: Let's add some padding for these. What else?\u003C\u002Fp>\u003Cp>Speaker 0: We have time for the padding. We have time for the padding. Dude. I'm good. God.\u003C\u002Fp>\u003Cp>I'm sweating again.\u003C\u002Fp>\u003Cp>Speaker 1: All things, man. All things. You you gotta have time for it. Alright. So we got some messages.\u003C\u002Fp>\u003Cp>Great. Now we need what? We need a form at the bottom of this one. Right? So we got VText.\u003C\u002Fp>\u003Cp>What does this thing do? This is, just text. Okay. Great. We don't need that.\u003C\u002Fp>\u003Cp>So we just got an input. Text input, b model, new message. Right? So we're gonna populate. We can just scrap all this shit.\u003C\u002Fp>\u003Cp>Oh, were we allowed to curse on this one or no?\u003C\u002Fp>\u003Cp>Speaker 0: Well, you've done it now. So yeah. Sure. Anything anything goes.\u003C\u002Fp>\u003Cp>Speaker 1: Anything goes. Alright. So we got a new message. We're just gonna add a ref for that. We will remodel new message at key up, enter, send message.\u003C\u002Fp>\u003Cp>Yeah. I don't like that, but, let's just wrap it. We'll give it a button. Oh, actually, I forgot I got the Nux UI library included in this. So we should be able to get something nice just by doing new input.\u003C\u002Fp>\u003Cp>Did that work? No. Nope. Did not work.\u003C\u002Fp>\u003Cp>Speaker 2: No pressure, Brian. That's\u003C\u002Fp>\u003Cp>Speaker 1: the beauty of this. No pressure.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. We've all gone quiet. Just letting you work\u003C\u002Fp>\u003Cp>Speaker 1: for a moment. New button. Just add send message. What are we doing now? Nope.\u003C\u002Fp>\u003Cp>Gotta close that guy. Alright. So we got this. Right? We need to add a handler for this.\u003C\u002Fp>\u003Cp>Just click send oh, let me just remove that. Send message.\u003C\u002Fp>\u003Cp>Speaker 0: Nice.\u003C\u002Fp>\u003Cp>Speaker 1: And then we've got should have an async function for send message. And this is not right at all. Right? So I think\u003C\u002Fp>\u003Cp>Speaker 0: I don't know. How are you gonna tap into the because you you've gotta you've got to send it over the subscription, don't you?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's that's where I was going back to your wonderful guy.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, no. No. No. I think it's fine. No.\u003C\u002Fp>\u003Cp>No. No. Ignore me. It's totally fine. You can do it.\u003C\u002Fp>\u003Cp>You can do it at that level where you've written send message because received message is at that level too. Right? So Actually Actually\u003C\u002Fp>\u003Cp>Speaker 1: client.send message. Right? Should be possible or no?\u003C\u002Fp>\u003Cp>Speaker 0: We'll find out in a minute. I think there's gonna be a weird scoping thing here. Let's find out.\u003C\u002Fp>\u003Cp>Speaker 2: Well, I think if you just use the create item function with the new SDK,\u003C\u002Fp>\u003Cp>Speaker 0: that should work. Do that,\u003C\u002Fp>\u003Cp>Speaker 1: but you could also\u003C\u002Fp>\u003Cp>Speaker 0: send it over you could send it over the WebSocket connection, and then you're just using that one connection use make a make a HTTP request, honestly.\u003C\u002Fp>\u003Cp>Speaker 1: Test. Oh, yeah. That's why you don't trust this. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yes.\u003C\u002Fp>\u003Cp>Speaker 1: Request. Yeah. Create item.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Did you get it? Test. Send message. Alright. What do we get?\u003C\u002Fp>\u003Cp>Unexpected error occurred.\u003C\u002Fp>\u003Cp>Speaker 0: Good. Good. Good. Helpful. Helpful.\u003C\u002Fp>\u003Cp>Helpful.\u003C\u002Fp>\u003Cp>Speaker 2: That's the channel that's the channel name. That should be the ID. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Yes. Correct.\u003C\u002Fp>\u003Cp>Speaker 2: Oh, did we get the channel ID?\u003C\u002Fp>\u003Cp>Speaker 0: No. That's right. Because, it's in the it's coming from the URL.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. But it's\u003C\u002Fp>\u003Cp>Speaker 0: That value there is coming from the URL. Yeah. Yeah. The dynamic part.\u003C\u002Fp>\u003Cp>Speaker 1: But the channel itself\u003C\u002Fp>\u003Cp>Speaker 0: Oh, wait. Right. Right. It's saying here, ding, ding, Alex. Got it.\u003C\u002Fp>\u003Cp>So hang on. I lower my confidence.\u003C\u002Fp>\u003Cp>Speaker 1: There you go. Yeah. Yeah. So we gotta get the channel ID. Where do we have the data?\u003C\u002Fp>\u003Cp>Right?\u003C\u002Fp>\u003Cp>Speaker 0: Oh, I understand now. I understand what you mean the ID. Got it. Got it. Got it.\u003C\u002Fp>\u003Cp>So I I think you'll need to do that. That initial load. Yeah. I think I think you do. Yeah.\u003C\u002Fp>\u003Cp>Just to fetch that ID.\u003C\u002Fp>\u003Cp>Speaker 1: I really don't wanna do that. But\u003C\u002Fp>\u003Cp>Speaker 0: Mate, you've got you've got 10 9 minutes. Oh, we got left? You're doing that.\u003C\u002Fp>\u003Cp>Speaker 1: We're doing it. Yeah. Alright. So read items, channels, filter ID equals no. That's our ID.\u003C\u002Fp>\u003Cp>Speaker 2: What's the channel name? Right? Or\u003C\u002Fp>\u003Cp>Speaker 1: Channel name. This will be wrapped. We'll have what equals. Alright. So you can have the channel.\u003C\u002Fp>\u003Cp>There's the ID. Good.\u003C\u002Fp>\u003Cp>Speaker 2: And it's an array out, which is a rookie error that I often make.\u003C\u002Fp>\u003Cp>Speaker 1: Woah. Yep. That is. Yeah. So this will be\u003C\u002Fp>\u003Cp>Speaker 0: How are you doing over there, Matt? Having a good time?\u003C\u002Fp>\u003Cp>Speaker 3: Watching all along. Yeah. I'm waiting for us to get to the colors. I think that's where I'll have my most, impact.\u003C\u002Fp>\u003Cp>Speaker 0: Well, we've got we've got a whole 8 minutes, and I think we're about to be finished, I guess. So, you you get a pick of the next function now or 2.\u003C\u002Fp>\u003Cp>Speaker 1: If I use the, what, If we use async data, and you can do, like, some transforms on this as well. Using data. Channel. Turn up paste. Is this guy out?\u003C\u002Fp>\u003Cp>Speaker 2: I think I think Greg's suggestion was the best to just use the channel ID in the in the URL.\u003C\u002Fp>\u003Cp>Speaker 1: Probably. 100%. Requests from that. This needs to be here. I like the pretty URLs, though.\u003C\u002Fp>\u003Cp>Alright. I will just refresh. Go to crazy nervous. Okay. So now we've got the channel.\u003C\u002Fp>\u003Cp>Alright. So we got channel. This should just be the channel dot ID. Best. Okay.\u003C\u002Fp>\u003Cp>So we're okay. It seems that, like, the message was sent, unless I'm wrong. Test. Okay. So it did populate the message.\u003C\u002Fp>\u003Cp>We just don't see it show up here. Alright. So what are we doing wrong there?\u003C\u002Fp>\u003Cp>Speaker 2: In that payload, shouldn't it also have the the channel ID?\u003C\u002Fp>\u003Cp>Speaker 1: Yep. It does.\u003C\u002Fp>\u003Cp>Speaker 2: But it does, but it's not showing in the console there.\u003C\u002Fp>\u003Cp>Speaker 1: Test. Okay. There's the test. Send messages. Yeah.\u003C\u002Fp>\u003Cp>That's kinda odd. Why is it not showing that in the payload? Alright. Regardless, we should be getting a message back. Right?\u003C\u002Fp>\u003Cp>Reading undefined dot avatar. This is a simple v f. Right? Yes. Message.\u003C\u002Fp>\u003Cp>Speaker 0: Just get rid of it. No one needs an image. No one needs an image. We're using Slack compact mode. Ditch it off.\u003C\u002Fp>\u003Cp>Get rid of it. Just saying that's 5 that's 5 and a half minutes.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. No worries, man. Alright. Test. Alright.\u003C\u002Fp>\u003Cp>So first name. Why is this not populating? User but we gotta have the username. Right? It cannot read first name.\u003C\u002Fp>\u003Cp>Speaker 0: It's not user. It's user created or whatever you called it. Right? Or did you rename it to user?\u003C\u002Fp>\u003Cp>Speaker 1: No. It should be user created. Yeah. Channel messages dotuser.firstname. This is used, like, a v f message.\u003C\u002Fp>\u003Cp>A user? You're trying to get something out of this. Oh, shoot. Blah blah blah. Kevin is crazy nervous.\u003C\u002Fp>\u003Cp>Still crazy nervous.\u003C\u002Fp>\u003Cp>Speaker 0: I'm literally rocking back and forward in my chair here by feeling\u003C\u002Fp>\u003Cp>Speaker 1: the heat. So, like, the the messages are coming in, but we're just not populating. We missed something. Messages dot value dot push data dot data. Alright.\u003C\u002Fp>\u003Cp>So if we're receiving data, we're getting data. It's it's\u003C\u002Fp>\u003Cp>Speaker 2: a it's another array in there.\u003C\u002Fp>\u003Cp>Speaker 1: Data is an array. Yeah. Okay. So, we'll just do this.\u003C\u002Fp>\u003Cp>Speaker 0: Someone else who has the cute baby in the background, and I'm using a hardware mute, but, I think that's that's me. And they're they're they're hungry. They're not.\u003C\u002Fp>\u003Cp>Speaker 2: Sorry. That might be my fault.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, do you I haven't heard I haven't heard yours in the background.\u003C\u002Fp>\u003Cp>Speaker 2: Okay. Was it yours? Alright.\u003C\u002Fp>\u003Cp>Speaker 0: Could be.\u003C\u002Fp>\u003Cp>Speaker 2: They came\u003C\u002Fp>\u003Cp>Speaker 0: home, but they're not sounding cute. They're not sounding cute. They're sounding angry.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So now what is the Nuxt command? Right? I think there's one where I can let you guys tunnel into this. Right?\u003C\u002Fp>\u003Cp>Nuxt dev tunnel? Where is this guy?\u003C\u002Fp>\u003Cp>Speaker 0: That is brand that must be brand new because I didn't know that was there. But it's built into Visual Studio Code. If you bring up your status bar at the bottom, I thought it was baked in now. No way. That is sick.\u003C\u002Fp>\u003Cp>Alright. So\u003C\u002Fp>\u003Cp>Speaker 1: does this actually work?\u003C\u002Fp>\u003Cp>Speaker 0: There there's the tunnel right there, the Cloudflare URL. Womp womp.\u003C\u002Fp>\u003Cp>Speaker 1: Why Why doesn't it work?\u003C\u002Fp>\u003Cp>Speaker 0: You better work it out because you got 2 minutes.\u003C\u002Fp>\u003Cp>Speaker 1: At this point, I don't know. Do you guys just, do me a favor and then log in to the direct us instance in this firehouse message?\u003C\u002Fp>\u003Cp>Speaker 0: I got you.\u003C\u002Fp>\u003Cp>Speaker 1: P p m. Yeah. Nux. Dev. Oh, p m p m.\u003C\u002Fp>\u003Cp>It would be, like, running the let's see. I don't know if that's it or not. I'm stressed. Yeah. So there we go.\u003C\u002Fp>\u003Cp>Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: That was me.\u003C\u002Fp>\u003Cp>Speaker 1: It's Does it work\u003C\u002Fp>\u003Cp>Speaker 0: does it work if you type it in here as well? Yes.\u003C\u002Fp>\u003Cp>Speaker 1: I I'm logged in as Alex. I'm logged in as Alex. Alright. Send send one more just so we can say something.\u003C\u002Fp>\u003Cp>Speaker 0: Alright.\u003C\u002Fp>\u003Cp>Speaker 1: Here? Less stressed. Yay.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Rock\u003C\u002Fp>\u003Cp>Speaker 1: on. Some concept.\u003C\u002Fp>\u003Cp>Speaker 0: That's basically feature complete.\u003C\u002Fp>\u003Cp>Speaker 3: I was shitting. Hit right at the buzzer. Is Let's go.\u003C\u002Fp>\u003Cp>Speaker 1: Because this is a winner. Yeah?\u003C\u002Fp>\u003Cp>Speaker 0: Yay. Holy heck. My heart rate. I'm I'm gonna need to take a shower. I'm sweating.\u003C\u002Fp>\u003Cp>Oof. Look at that.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Wow.\u003C\u002Fp>\u003Cp>Speaker 0: Stop the timer.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Leave it for Stop. Yeah. Series a already. I've watched them, man.\u003C\u002Fp>\u003Cp>Speaker 0: And don't forget, there's more in your stressing and dressing in 100 hours. A whole season now available on Directus TV with more coming in April.\u003C\u002Fp>\u003Cp>Speaker 1: I I look forward to the other episodes where you guys are are it may be not on there. I I've enjoyed this a lot, though. This is fun. It definitely distracted a little bit.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I did feel like, should this be, like, 1 app in a 100 hours, but with guests? Oh, wow.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, so takeaways from this. Right? What did we what did we learn?\u003C\u002Fp>\u003Cp>Speaker 0: I'm not coming back to the next one.\u003C\u002Fp>\u003Cp>Speaker 3: Spend more time with Patty.\u003C\u002Fp>\u003Cp>Speaker 1: More time with that. Dude, I'm sorry we didn't get to the, like, colors. Like, do you wanna what what color are you feeling, Matt? It's the only reason I'm so sorry. Pick the color.\u003C\u002Fp>\u003Cp>I'm looking for the color.\u003C\u002Fp>\u003Cp>Speaker 3: I can't think of the hex code\u003C\u002Fp>\u003Cp>Speaker 1: for\u003C\u002Fp>\u003Cp>Speaker 3: I didn't 466, double f.\u003C\u002Fp>\u003Cp>Speaker 1: What do you want? 4466\u003C\u002Fp>\u003Cp>Speaker 3: f f.\u003C\u002Fp>\u003Cp>Speaker 0: No. It's 6644 f f.\u003C\u002Fp>\u003Cp>Speaker 3: Knew it. Hopefully, Ben's not watching.\u003C\u002Fp>\u003Cp>Speaker 0: What is it? Oh, he's watching. 6:6:8. He's watching.\u003C\u002Fp>\u003Cp>Speaker 1: 44 f f. Where are we? Is it not updating? Is that already the indigo oh, there it is. Okay.\u003C\u002Fp>\u003Cp>No?\u003C\u002Fp>\u003Cp>Speaker 0: Maybe it wasn't on it here. Is that the sidebar?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Okay. Sidebar. Yeah. There you go.\u003C\u002Fp>\u003Cp>BG you're gonna find me one more time. 6644 f f? Yep.\u003C\u002Fp>\u003Cp>Speaker 0: That's the one.\u003C\u002Fp>\u003Cp>Speaker 1: That's it. There it is.\u003C\u002Fp>\u003Cp>Speaker 0: Subtleship. Boom. Feature comp New product. Ship it. Thanks everyone for joining us.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: I'm not I'm not kidding. We probably won't do another one now. We probably will, but I won't be here. Gosh. Literally, 30 seconds to go.\u003C\u002Fp>\u003Cp>30 seconds to go. Congratulations, Brian.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I feel like we started off on the wrong foot with the 5 minutes of technical difficulties. The for the nodes. Selecting 5 minutes of technical difficulties at the the start of this thing. That's what everybody did not see on the front\u003C\u002Fp>\u003Cp>Speaker 0: end. We got there. We got there.\u003C\u002Fp>\u003Cp>Speaker 1: Now we're now we're ready to code for, like, another 2 hours. And if you're\u003C\u002Fp>\u003Cp>Speaker 0: watching this live, this whole episode will be packaged up as a special at the end of season 1 of a 100 and a 100 hours on director's TV. We'll pop that out tomorrow. And then there are new episodes of a 100 apps and a 100 hours coming in April.\u003C\u002Fp>\u003Cp>Speaker 1: Amazing. Excellent. Yeah. Well, thanks for joining us, to the circus episode of 100 Hours 100 Hours. I'm your host, Brian Gillispie.\u003C\u002Fp>\u003Cp>Thanks to my special guests, mister Kevin Lewis, mister Avdv, and Awesome. With the crazy colors coming in.\u003C\u002Fp>\u003Cp>Speaker 0: Thank you, everyone. Bye for now.\u003C\u002Fp>\u003Cp>Speaker 2: Cheers. Bye. Cheers.\u003C\u002Fp>","Build in an extra 15 minutes for chaos. I'm gonna be here for the first 20. Then for the 1st presales engineers, Alex, will be here for 20, and then Matt will be here for 20. With that in mind, Bryant, I'm gonna start a timer. No stress, but stress. Take it away. Yeah. Let's start the timer. What are we building? You tell me. Yeah. So you you let me know ahead of time, maybe, like, just a hour ago that we were gonna be building. Any guesses in the chat. Right? This might be fun to do live. Right? What are the guesses? No guesses. Alright. I don't see him coming through. We are going to be building a Slack clone. Build direct to us. In an hour. No. In an hour. Alright. So, Slack. Right? Yeah. Everybody uses it. You either love it, hate it. Xbox Live. Yeah. We'll do Xbox Live next time. Alright. So let's sketch this thing out. Right? What do we need out of a Slack clone? What feels good as far as functionality for this? How are we gonna set it up in direct us? Facebook, they need some help with their APIs. Yep. Certainly. Yeah. Experienced that. We all saw that today. What is needed for Slack? I suppose the lightest version is gonna be you have channels, and inside of channels, you have users, and then you have channels. And then inside the channels, you have messages with, like, real time. And I think the that's the slimmest version. Right? Yeah. So let's put, like, channels and messages at the top. We would call, like, threads a stretch goal. Yeah. Thread threading feels like, probably more than an hour. Alright. So we got channels. We got messages. We're gonna get the user authentication from direct to its users. And as far as functionality, what do we wanna do? We wanna have a channel. We want to submit messages in the channel. We want that all to update in real time. Notifications? Do we want notifications? We'll I never wanna be notified when people send me messages ever. There is literally no world where I want that feature. So so how many Slack groups are you a part of where, like, you just got the whole thing muted, Kevin? That would be a good question. So let's take a look here. Let me let me open Slack for the first time in a in a hot minute. Okay. I'm in 123456789101112 Slacks. 12 Slacks. And I look at 2 of them regularly. 3 of them regularly. Most of the others are muted. Alright. Yep. That's how it goes. Same thing for me. Alright. So I've got a brand new instance. Please don't use this password. Oh my lord. I okay. That's fine. Everything's fine. Probably need to change that before we get some surprises. Right? Change that real quick. Okay. Alright. So we're inside Directus. How are we gonna map this out? Right? We get the Directus users already out of the box. We get the authentication. We should get real time out of the box as well. So we just need these components. Right? Channels and messages should be fairly straightforward. We'll just call it a channel. Why would you say that? That's like the beginning of the it should be it should be straightforward. Keep it simple, and it won't be complicated. Keep it simple, and it won't be complicated. Yeah. Alright. What do we need for a channel? Do we yeah. Yeah. We don't even really need any of this. Right? I don't care when it was created, who it was created by. Just need a name for the channel. That'll be an input. We're going to go to the advanced settings. And because I want this to be URL safe, right, we'll use the slugify option inside the interface, and boom. There's channels. That was really complicated. I I saw Ben was He begins now. Let's let's do, like, the sahash. Oh, egg. Oh, god. We don't have time for this, Brian. Dude God, I'm stressed already, and we're, like, 4 minutes in. Everything. Yep. Alright. So then we've got messages. Alright. So as far as messages, we want when that message was created. So let's just call that timestamp for simplicity. Fun fact. I didn't know you could rename those. I literally didn't that is the first time I've ever seen that. Damn. Yeah. You could change it. You know, like, some people prefer updated at, versus date created or update updated. Do whatever you want with it. Alright. So we've got some messages within the messages. What do we have? We have what do we wanna do? Just text text area. We could go to markdown to be super complicated, but we'll just call it content, text, message. What do you Yeah. I literally I literally don't care. Oh my god. No. I I wouldn't do message because if you're, like, looping through messages, you might use message as the singular, and then, yeah, text feels good. Text. Alright. Let's unhide this just so we could see. My leg's literally doing its nervous shake. I'm so glad this is your series and not mine. Oh my gosh. I'm sweating. Is it is it just the technical difficulties at the start of it? Or or what? No. It's having an hour on the clock. It's having an hour on the clock, Brian. It's the very format. Yeah. No worries. Alright. So we got some messages. We got some channels. What else do we really need here? Oh, we'll come back to threads later. Right? We've already got our users. I'm gonna go ahead and, let's add you as a user, Kevin. Kevin, add example. And I'm not gonna tell you guys what the password is here. Alright. Now let's go into I did actually think reactions would be a nice sorry. That was me interacting with, Ben in the chat. I did actually think reactions would be a nice a nice one as well. It's quite common. Yeah. Cool. It'd be an interesting one to add. Yeah. We'll come back to it for sure. Alright. So we're gonna create a role for users so we can give access to messages for channels, etcetera. We're just gonna go ahead and give all access to this to start with. And the other thing that I'm gonna do, let's give public access to create we'll come back to, like, registering users. Yeah. Yeah. Yeah. No. That's later. Yeah. Okay. Alright. So we've given access. Now we can access any as long as we're in this user role, we'll be able to access channels, messages, all of that. Let's actually start to build something. Right? If I wanted to, I could give Kevin access to this right now. He could log in. Let's just do that. You're just seeing my stress face here. Okay. I just see your stress face. Kevin at example. And Kevin Oh, that is a lowbrow password. Is it lowbrow? Wow. I mean Yeah. So let's go in. I'm gonna just create a couple of channels. Tests, though. Oh, yeah. That's right. Let me just go ahead and give that. Because eventually, you're gonna be interacting through this Nuxt front end. But Yeah. Yeah. Cool. Alright. So we create a couple channels. Right? What do we like, guys? Throw some suggestions in the chat. We'll add a few of these channels. Let's start with Random? Evan is crazy nervous channel. My leg's shaking. General, memes. Okay. Sounds great. Got some memes. Alright. And now as far as messages, right, we got the user. We got the timestamp. We've got when it was updated. In case we change it, we got the text. But the thing that we don't have is how these messages belong to a channel. So inside Directus, we'll just go in. Again, creating these relationships, super easy. I don't even have to touch SQL. We'll go in, create a mini to 1 relationship for this. We'll call it channel, and we'll use our channels collection. And what else do we need? Maybe display template. We'll use name. Great. Cool. And now I could populate a message into a specific channel. Hey, Kev. I'll be nervous. I have the most faith in you while having absolutely no faith in you at the same time. By the way, that user you created for me still doesn't have app access. You might need to go into the user settings and allow that. I thought doing the role would be sufficient, but, oh, I'm not I'm not in that role. I'm not in that role. Users in that role was 0. Yeah. Gotcha. Yep. Try it now. Okay. I'll tell you in a moment. Tell me in a moment. Alright. So we've got the basic logic here. Like, we could go and fetch the data from this API if we wanted to just by going to messages or item slash messages. We're gonna get a forbidden because I'm not logged in, but that's okay. We'll take care of that. Oh, we're 10 minutes in. This? Sorry. No stress, but we're 10 minutes in. That's half my time here. I'm I'm halfway to freedom because I am sweating so hard. Don't sweat, man. Somehow I got logged out. So chaos rains, my friend. Oh my gosh. Glass work. Okay. Alright. I don't know how I got logged out, but okay. So we've got channels. We've got messages. Let's actually do something. I low key hate this, he says. I can still see the chat in the other window, Kevin. So you're not you're not giving me much confidence. I'm so sorry. Alright. Yeah. No worries, man. No worries. Alright. So this is my NUC starter from all the other episodes that we've got. I've switched it up a little bit just so you have a simple plug in for the sake of this live instance because the the other starter that had had a Nuxt module. It's got a lot going on. This is really simple. Right? So we've got a direct us plug in, that is in the plug ins directory inside Nuxt, which will automatically register this. And really simple. Right? Inside Nuxt, we've got a runtime config that we're calling to get the directus URL. And my Nuxt config looks like this. Right? We've got a direct us URL. Right now, that is pointing to, like, a local host, so we do need to switch this over. We'll just go to our URL here. It is switched over. Great. As far as the server token, I don't think we even actually will be using this. I don't think I've got it anywhere else in my config either. Cool. So we'll save that. And now we want to, like, start fetching some data. Right? There's a couple of routes that I already have set up in this, like a login and a register route. Drink a red bar too. I've got all my caffeine. More I've got my caffeine right here. Don't you worry. Give him some more energy drinks, please. I'm gonna go get one. I'll be right back. Woof. Woof. I am actually doing it. Alright. So let's fire up the dev server for Nux here. As far as the stuff that I've already got, like I said, I've got a a login route, a register route that we'll try to mess with. Looks like this. We'll go in and log in, where we can register. Very simple. And then we just have a index page, and then, I had some testing that I was doing here. Alright. So as far as layout, what do we want? You know, a lot of times, I it's like I I love Tailwind. Tailwind UI is totally worth the money, especially for, like, quick prototypes like this. I usually like, if it's just something I'm hacking on personally, I'll start with, like, the application shells. Like, sidebar layout. Right? This feels pretty good for, like, a Slack type of setup. Yeah. That looks good. Yep. So let's just go in. We've got that set to view. Where are we gonna run into issues at? Probably with the icons because I don't have those set, but no matter. Alright. So as far as our pages, let's do what are we gonna do here? Let's just do, like, an app directory. Within that, we'll probably have, like, some channels. Yeah. That makes sense. Just just And then the dynamic route inside of that. Yeah. Yep. So we'll do, like, channel dot view. And then also, what I can do is set up, like, a a parent root for the app. So if I go here inside the pages directory and I do app dot view, I paste this in. Is this for, like, the sidebar? Like, the sidebar will be held in this? Yeah. Exactly. Nice. Alright. So we'll save that. Let's see what we get. That is a major indentation. Yikes. K. So these are Rike's rules. I'm not sure if he's on here, but, alright. He quit out. He hit the wrong keyboard shortcut. That is absolutely what happened. He got arced. That's what our team say when you accidentally, like, quit the whole window. I'm sure he'll be back in a moment. Until then, you got me, stressed as heck. Got my energy drink, waiting for the waiting for the stress to return. This is my brief moment of respite here. That was a major indentation, right? Is it worth the time of pause? Is it worth the time of pause? Did you get arced? I I don't know what happened. Yeah. I guess I did. We're back. Sorry. You don't need to be sorry and you don't get that time back. Oh, yeah. Yeah. It's still running. I don't know what happened, man. I really don't. Weird. All right. So getting some errors here. We get console wise. Home icon. Navigation. Yeah. This is the only bad part. I'm gonna zoom out just a little bit here so I can find Just to actually navigate this, healthscape of tabs. Yeah. Just delete a bunch of these icons. Gosh. They put so many icons in this. Everyone that direct us has a personal choice between Mac and Windows, by the way. So, you know, some folks use use either. I would say with the folks I work with directly, I think we mostly use Max, but that's definitely not the case across the board. Tim is my resident, brain slug. Tim is is my resident Windows user. Oh, let's see. It's not about getting to use Linux. It's about wanting to use Linux. So folks wanna use it, whatever. Most of the stuff we use is in the cloud, so it doesn't really matter. Yeah. Okay. Well, at least we got something now. Right? Alright. We're gonna ditch teams here at the bottom. Where are you? Your teams. Great. Just ditch that list of stuff. Can you zoom back in? I'm like, I need my spectacles to see it over there. Alright. Teams is gone. Right? So now we want to fetch the list of channels here and display those. So how do we do that, Kev? Do you have the Directus SDK in this app? We do have the Directus SDK in this app. Lovely stuff. Well, have you got it set up as, like, a as, like, a plug in, or do we need to do that now? As a Nuxt plug in. So we're gonna Very good. We'll use dollar sign direct us, use Nuxt app. The only thing that I don't have is, like, the auto imports from the SDK. That was one of the nice things about the the other module I had. So we're gonna need to do, what, read items? Yeah. From atdirectus SDK. I think that's it. Speaking of this. What's it complaining about? White space, I think. Yeah. Yeah. These are, again, these are Rikes, ESLint, and Which are very legit for building actually serious things unlike this. They're very, very legit. Right? Alright. So let's do what do we want? We want channels. There's a ref for channels, and then we're going to well, actually, we should just be able to use Nuxt async data. Right? Nuxt data error. We get an error. We're gonna go, wait, use async data. We'll give this a key. Let's just call it channels. And then we just return something from Directus, right? So Oh, look at that interesting code pilot code using old SDK methods. Fascinating. Yeah. Not right. Right? That's a oh, what are we doing there? Auto completion or something. So we'll do read items. We've got channels. Rates. Oh, man. GitHub Copilot is getting on the way this time. You can turn it off if you want. We do only have an hour. We'll see. Alright. So, basically, we're gonna read all the items from channels. We wanna get the fields. We'll just use the wild card. It's basically just name and ID. Does that do it? It should do it. Let's just destructure this and oh. Yeah. Are you are you As as channels. Needs to be channels, and we'll just call this channels. And hopefully And now now usually use channels. Yep. We just go down here. Item in channels. Very nice. And I think we could it is name. The key is name. Yeah. Yeah. Item dot name. We're just gonna swap this for, like, Nuxt link. One of the interesting things, like, Nuxt link will take a h ref or, like, a 2 property. So that's one of the nice things that I like about it just because it's you know, you're used to using h ref on the regular side when you're creating a regular link. Right. Do you wanna do you wanna know what's happened? We've, we've hit the 20 minute mark, and, thank gosh. I don't have to be here like, I can sweat from the audience. At this point, I'm gonna I'm gonna hand off to the amazing Alex who I'm gonna bring in. Hang on. Let let me add him in. Alex, would you, would you like to say hello? Good evening, everyone. Can you hear me alright? I can hear you grand. Lovely. Your your lovely tones. Excellent. I've I've, enjoyed watching you sweat, Jacob. It's been great. Oh, I'm sorry. It's coming it's coming for you too, mate. Yeah. Yeah. Yeah. I'm I'm learning a lot of you right now as well. So Hey, Viv. Hey. We failed to resolve next link. What is that all about? I'm gonna peace out everyone. I'm gonna put you in Alex's very capable hands for 20 minutes, and then, he will be handing off to Matt. Bye for now, folks. Enjoy the rest. Yeah. I thought we were doing, like, Royal Rumble rules. Everybody just hangs on. Be fun. Oh. You just call me now, Brian. I that's alright, my friend. How are you, dude? It's been a while since we've, like like paired up for something. Yeah. Yeah. Yeah. This is this is cool, man. I, always enjoy, seeing all this view code, which I wish I understood better. Yeah. Most of it is is tailwind at this point. But, yeah. So we having an error right now. Right? We're not having permission to access the channels. So we just need to log in. Right? So I'm gonna go to my login form components. Let's redirect to slash app. And we're gonna try to log in to this thing as well. I guess I need to add a user for you. Yep. I have one for Alex. Alright. Oh, don't forget the role. Alright. So I'm just gonna try to log in as Alex now. So we're gonna log in. Oops. Nope. That's not right. It's auth\u002Flogin. I already used the link on the home page. That would have worked. So we got Alex at example, and the password is super secure. Okay. Alright. So now we can see at least we have our channels up here. Right? That looks good. Yep. Yeah. Nice. Alright. How are you feeling, Alex? Are you nervous as as Kevin? Like, feeling good? I I've got a feeling you got the power of Bry Ross, under the hood there. So Ross. Big. I've got confidence, man. I got confidence. Okay. Right. Alright. So now we need to fix, like, the h refs here. Like, hey. We wanna navigate to one of these channels. Right? Let's just test if this is gonna work. I'm a script at the top guy. Not sure if we've got any other view fans or not. I like seeing what's going on with the JavaScript first, and we're going to use the routes. So we'll do routes. Let's use routes. And I'm just gonna do this. I'm just gonna log this out. Right? I wanna look at routes a params.channel. K. Good. The other thing that we're gonna need to do somewhere in here where's the main dev? Main content. We're gonna plug in this Nux page here. So this should render the child page, for all my nested routes. Yeah. Alex is a boss. So, I'm not sure if that's John Daniels or not, but, I will say, like, if you've used the template CLI tool at all, Alex is is that guy. He's the guy that put that together originally. Yeah. Thanks thanks to directors having good APIs. It was, yeah, difficult, but we we got there, and it's pretty cool right now. Yeah. Alright. So we got the next page. I save. 2 2 what's going on? Oh, yeah. I forgot to fix the login again. Yeah. Apparently. Yeah. You need to persist that. Alright. So what else do we need to do? We need to fix those links. Channels. Where are you? Alright. So this will be channel or item dot name. Item dot name. Yeah. And then we'll do \u002Fchannels \u002Fitem.name. Should get it. We'll do a Nuxt link. Oh, duh. It's app dot channels. Nice. So now we have our channels working. Right? What are we gonna do inside the channels? Right? Great call. What do we do inside the channels? We are going to here is this is where we're actually gonna fetch, like, real time data. Right? Yeah. You think? Yeah. Yeah. I'm looking forward to this part, the the, real time. Okay. Alright. So I'm gonna cheat a little bit. We're just gonna go into our documentation. We do have this nice guide, if you haven't checked it out, on real time multi user chat. There's a vanilla JavaScript version. There's React. There's Vue. Let's just take a look at this. Right? Alright. How are we going to authenticate with real time? So we've got, the authentication composable. We've added the real time composable. All of those are in my plug in here. So it should be as simple as, by calling connect. That'd be right. Let's see. Kevin's got a I think it was Kevin that he's got. We've got a subscribe function here. So we're gonna subscribe to the messages, client dot connect. Yep. Let's see here. This is usually where I run into some SSR issues, but I'll we'll see how you manage this one, Brian. Let me show you. SSR equals false. That's That's the solution there? That's the easiest way. Yeah. Yeah. Absolutely. Alright. Alright. So let's just, is this cheating? Yeah. This probably is. Use whatever you have at your disposal. Right? So the subscription client dot subscribe messages, that's correct. Our fields here that we want, we're grabbing the all the fields on the root level. I guess I could zoom in a little bit and make that easier for everybody. Then we have user. So let's just grab all the fields from the user. We've got message of subscription, and then it looks like there's a receive message function that goes along with this. Yeah. You yeah, you might need to filter on the channel as well, but maybe we can do that in the next step. Yeah. So we've got data type equals subscription. This is just like the init. Right? Let's see if we are getting any. No. Yeah. This is gonna be interesting. I should have set up better authentication. So persisted on this. Alright. Do we see anything from, see some more issues from Tailwind? X mark icon. Where are you? Note to self, on the next one, do not import any of the icons from. So this should be, like, we definitely wanna redirect on this. Right? Let's add app. Let's add some page meta. Define oh, actually, it's not a const. We'll just do define page meta. And if we give this a middleware of auth, it should automatically redirect. And then I'm gonna go into where's my login form? I'm gonna make this really easy for myself, Alex. Oh, good idea. And password, super secure. We'll learn a little bit about everything here. Okay. So it's redirected. Sign in. This should take us back to the app, where we could get all the channels. What am I not seeing here? Right. Do we have to do we need to connect to the client, I guess? Let's plug in why we're not receiving Yeah. Client dot connect. Right? Isn't that it? Okay. Yeah. Yeah. So this is on mounted, but we're not using SSR. This should just work. Right? Where are we gonna stick that? On? Oh, what if we just call subscribe? Set up all these functions, forgot to call them. Alright. Client is not defined. Duh. Because it's not the client, it should be directus dot subscribe. So, again, we're gonna grab that directus plugin that we set up from use Nuxt app. It's error data is not defined. Oh, we gotta pass the data to that. Depcription started. Okay. Alright. So what do we wanna do now? Within the channel, we wanna have all of our messages. Right? I'm assuming that will be an array. Sounds good to me. In our received messages Actually, let's just do this to start with, see what type of data we're getting back. Received message. Alright. So now if we go in, I got, like, 35 tabs here, don't I? Don't we all? For apps. For hours. Live. Or I forgot the URL. It's just 100 apps dotdirectus.app. And I've logged myself out again. Let's keep that. I can't help you here, Brian. Okay. Alright. So now how do I split these out? That's the only thing I get hung up on on our half the time. It's like, hey. How do we make this, go split screen? Alright. So couple things should be happening here. Right? We've got our subscription. Let's make this larger. Right. So the subscription has started. We're in the Kevin is crazy crazy nervous. We'll probably have to filter that on our front end anyway, but, testing 1, 2, 3. We'll just add the channel to begin with. And Oh. Cool. So we we see the real time is connecting. We're getting our data. There's the actual messages. Great. Right. We've got the test. We've got the user is null. Probably permissions. Correct. That is correct. Alright. We need to make sure that the user role can access all the users. So right now, it's just by default, like, just a one user. We'll just make it so we can read all the users, and let's send a new one. Test 567. Save. Get our message. Okay. Now do we get the user data? Okay. Cool. That's a lot more user data than we actually wanted. So let's go in and trim that down. We'll just do first name, last name, avatar. We probably want the ID too. And the dot ID. And the first name, not the first name. First name. Not the catch. Good catch. Alright. Cool. And then here, we need to what? We want to see the message. Where's that message at? Alright. Subscription, create data. We want to filter that. Right? What are we gonna call this? For each message of subscription, We need to populate that data. Right? Do do you wanna filter it at the at the WebSocket level or just at the whenever a new message is received? You could do both, I guess. But Yeah. I I'm trying to think of, like, in Slack, you'd probably have, like, some kinda indicator up here that's, like, hey. Yeah. Yeah. Even if a message in a different channel popped up, you would probably filter it out on the the front end. Right? Yeah. On on the specific channel Because you probably have, like, some type of yeah. Alright. So receive message here. What do we does it show the oh, let's get the channel name as well. Query Oh, yeah. That's that'd be a useful one. Dotidchannel.name. So here, if the data type equals subscription. Create. And data event equals create. We will push those messages into our array of messages. And then instead of this, we should be showing a some messages. Alright. What we doing on time? I've totally lost the window here. Yeah. Me too. Me too. It's about quarter to the hour on my clock. Let's just take a look. 22 minutes left. Okay. Alright. Cool. Fun. Fun. Fun. Alright. GitHub Copilot for the win. Do we how confident are we feeling? I can't even see the chat at this point. But Not confident in that one. So we'll do, like, a list for all the message. Yeah. Again, hey. Like, you gotta take it with a grain of salt. Message dot content. You know, I I think this would get better if we had typed this out, but Yeah. Key message dot ID, we should have that for sure. We'll just close that list, see what we get. Alright. Do we have any messages? The other thing is, like, do we fetch the messages for this channel on Initially. Right? On yeah. And, like, do you have you messed with the the real time and, like, the chat stuff at all, Alex, or no? Not not a not as much as I should have, but, this is an interesting one because you might not even need to use the real time in this in this example. Just Hello, everyone. It's the disembodied voice of Kevin here. Hello. I'm watching, I've been screaming into the void in chat. I know this isn't I know this is an illegal move to throw you a bone here, but the init payload comes with the initial items. Up to a 100, and you can set limit minus 1 to get more than a 100. Okay. Peace out. Bye. What was that, Alex? You wanna miss off the clock, mate. Okay. Thanks, Gabe. I think that's probably just, just we you can start logging out the the all all the messages that come through from the website. Right? So it's probably a yeah. So it would be one of the messages received maybe, with the initial with a different type. I don't know if I've seen that in your logs to be fair. But Yeah. Let's back up. Subscription started. Received message. No. Matt's gonna save the day on this. We should know this. I should know this, Bryant. That's alright. So, like, we we'd also do it this way as well. Right? Where we go in and read items. Oh, nope. That's gonna be coming from the SDK. Read items from directus s e k. Let's just call this Oh, I think no. I'm starting to sweat. Thanks, Kevin. Yeah. No worries. God. What am I doing? Constant populate messages equals async. Yeah. There we go. This is wrong. Totally wrong. Oh, no. That's not that bad, actually. Filter channel equals route dot params.channel. Equal route dot value. No. Should just be route dot is it route dot value? Is route reactive? Hello. Hello, everyone. It's me again. It's Kevin again. I was I was having the chat with Matt, and in the nature of chaos, we've decided to Royal Rumble this. So it is my pleasure to introduce mister Matt Minor. Hello, Matt. Hey, everyone. I, will be the most useless person on this call because I'm not Oh, hardly. I'll help you pick the colors. And I was actually thinking, maybe I could just sabotage Bryant here at the end. Just Sabotage. The headlines of the day or something. Do it, man. Oh, no. Don't don't do that. Can you imagine? What do we have? We have we have 15 minutes. No. There's there's 20 left. We got just just 20 minutes left. We're 2 thirds of the way in. No pressure. But it it doesn't look like Slack yet, does it? Where are the messages? Yeah. Alex, is it is this what you did with your 20 minutes, mate? Mate, we were we were work we were working hard. Yeah. We were working hard. All hardly working. Yeah. Yeah. This was where are we stuck? What are we, what are we doing? Just gonna load the initial messages. So so I know from experience that when you I don't know what's up with maybe how you're including real time here, but that init payload that init message does come. It definitely comes with all of the mess with all of the items in the collection, but I have also observed it's not happening here. So I think that would be the easiest fix is, like, why isn't that working? Because then you can just use the real time interface. Right. Why isn't that working Yeah. Kevin? Yeah. Bloody great question, mate. So I log in. We go to Kevin is crazy nervous. We start this subscription. Yeah. But where is the where's the payload? Where's that init payload? So in oh, oh, I think maybe it Oh, is it is it here? Is it is it because you're just subscribing to the single event? If you remove the event from, like, inside of the, if you get rid of that and get every everything over that subscription, does that help? There it is. Data. Oh, okay. Yep. Okay. So just use that. Nice. Okay. There we go. My man. That's what I was gonna say too. But, Kevin I know. Sorry. I felt I felt emanating from your your corner of the ring. How are you doing today, Matt? Is I'm good. I'm doing good. I'm excited to be here. Good. News headlines for today? Facebook and Instagram. No. Struggling. I I sorry. I was too busy working, so I didn't know that. Oh, yeah. Sorry. I was too I, it just came up when I was logging in to work today. So, how's the chat doing? Is anybody Let's see. You you might have to spread that one, Bryant. I think. Yeah. Yeah. You will. You will. Just enjoying the chaos. Yeah. But for a net but for a net, dude, for a net, don't push it into the array. Just replace the array because Oh, okay. There's gonna be nothing in it at the start. Data dot data. Joshua, just enjoying the cast. Honestly, so are we. As I said, this is our first kind of live event of this kind, and I don't know. I'm having a great time. It is chaotic, and we're just making it up. We changed the format. Who bloody cares? Love it. Yeah. Nope. Alright. So now we got some messages. Right? Let's, let's mess with this a bit. Alright. So we're gonna have a div. We're gonna show the avatar, image source, message. Joshua, it's so funny. You're like, yeah. You know, I'm struggling, like, getting this, you know, flow automation thing to work while listening to you in the background. And the funny bit is some of the people who would be there to help you are currently here. So it's like, have fun struggling, but you're struggling on your own for the next, 15. Nah. Jonathan's there. Funny. And you think you're struggling to get, like, 4 people watching you work at the they were all just staring over Brian Schuler right now. I love it, man. Thank you. This is this is this is more fun for me. I think I feel like I go, when I go shopping with my wife and, like, just follow her around the store. That's what I feel like right now. I know value just in the way. Alright. So far. Let's add some padding for these. What else? We have time for the padding. We have time for the padding. Dude. I'm good. God. I'm sweating again. All things, man. All things. You you gotta have time for it. Alright. So we got some messages. Great. Now we need what? We need a form at the bottom of this one. Right? So we got VText. What does this thing do? This is, just text. Okay. Great. We don't need that. So we just got an input. Text input, b model, new message. Right? So we're gonna populate. We can just scrap all this shit. Oh, were we allowed to curse on this one or no? Well, you've done it now. So yeah. Sure. Anything anything goes. Anything goes. Alright. So we got a new message. We're just gonna add a ref for that. We will remodel new message at key up, enter, send message. Yeah. I don't like that, but, let's just wrap it. We'll give it a button. Oh, actually, I forgot I got the Nux UI library included in this. So we should be able to get something nice just by doing new input. Did that work? No. Nope. Did not work. No pressure, Brian. That's the beauty of this. No pressure. Yeah. We've all gone quiet. Just letting you work for a moment. New button. Just add send message. What are we doing now? Nope. Gotta close that guy. Alright. So we got this. Right? We need to add a handler for this. Just click send oh, let me just remove that. Send message. Nice. And then we've got should have an async function for send message. And this is not right at all. Right? So I think I don't know. How are you gonna tap into the because you you've gotta you've got to send it over the subscription, don't you? Yeah. That's that's where I was going back to your wonderful guy. Oh, no. No. No. I think it's fine. No. No. No. Ignore me. It's totally fine. You can do it. You can do it at that level where you've written send message because received message is at that level too. Right? So Actually Actually client.send message. Right? Should be possible or no? We'll find out in a minute. I think there's gonna be a weird scoping thing here. Let's find out. Well, I think if you just use the create item function with the new SDK, that should work. Do that, but you could also send it over you could send it over the WebSocket connection, and then you're just using that one connection use make a make a HTTP request, honestly. Test. Oh, yeah. That's why you don't trust this. Right? Yes. Request. Yeah. Create item. Yeah. Did you get it? Test. Send message. Alright. What do we get? Unexpected error occurred. Good. Good. Good. Helpful. Helpful. Helpful. That's the channel that's the channel name. That should be the ID. Right? Yes. Correct. Oh, did we get the channel ID? No. That's right. Because, it's in the it's coming from the URL. Yeah. But it's That value there is coming from the URL. Yeah. Yeah. The dynamic part. But the channel itself Oh, wait. Right. Right. It's saying here, ding, ding, Alex. Got it. So hang on. I lower my confidence. There you go. Yeah. Yeah. So we gotta get the channel ID. Where do we have the data? Right? Oh, I understand now. I understand what you mean the ID. Got it. Got it. Got it. So I I think you'll need to do that. That initial load. Yeah. I think I think you do. Yeah. Just to fetch that ID. I really don't wanna do that. But Mate, you've got you've got 10 9 minutes. Oh, we got left? You're doing that. We're doing it. Yeah. Alright. So read items, channels, filter ID equals no. That's our ID. What's the channel name? Right? Or Channel name. This will be wrapped. We'll have what equals. Alright. So you can have the channel. There's the ID. Good. And it's an array out, which is a rookie error that I often make. Woah. Yep. That is. Yeah. So this will be How are you doing over there, Matt? Having a good time? Watching all along. Yeah. I'm waiting for us to get to the colors. I think that's where I'll have my most, impact. Well, we've got we've got a whole 8 minutes, and I think we're about to be finished, I guess. So, you you get a pick of the next function now or 2. If I use the, what, If we use async data, and you can do, like, some transforms on this as well. Using data. Channel. Turn up paste. Is this guy out? I think I think Greg's suggestion was the best to just use the channel ID in the in the URL. Probably. 100%. Requests from that. This needs to be here. I like the pretty URLs, though. Alright. I will just refresh. Go to crazy nervous. Okay. So now we've got the channel. Alright. So we got channel. This should just be the channel dot ID. Best. Okay. So we're okay. It seems that, like, the message was sent, unless I'm wrong. Test. Okay. So it did populate the message. We just don't see it show up here. Alright. So what are we doing wrong there? In that payload, shouldn't it also have the the channel ID? Yep. It does. But it does, but it's not showing in the console there. Test. Okay. There's the test. Send messages. Yeah. That's kinda odd. Why is it not showing that in the payload? Alright. Regardless, we should be getting a message back. Right? Reading undefined dot avatar. This is a simple v f. Right? Yes. Message. Just get rid of it. No one needs an image. No one needs an image. We're using Slack compact mode. Ditch it off. Get rid of it. Just saying that's 5 that's 5 and a half minutes. Yeah. No worries, man. Alright. Test. Alright. So first name. Why is this not populating? User but we gotta have the username. Right? It cannot read first name. It's not user. It's user created or whatever you called it. Right? Or did you rename it to user? No. It should be user created. Yeah. Channel messages dotuser.firstname. This is used, like, a v f message. A user? You're trying to get something out of this. Oh, shoot. Blah blah blah. Kevin is crazy nervous. Still crazy nervous. I'm literally rocking back and forward in my chair here by feeling the heat. So, like, the the messages are coming in, but we're just not populating. We missed something. Messages dot value dot push data dot data. Alright. So if we're receiving data, we're getting data. It's it's a it's another array in there. Data is an array. Yeah. Okay. So, we'll just do this. Someone else who has the cute baby in the background, and I'm using a hardware mute, but, I think that's that's me. And they're they're they're hungry. They're not. Sorry. That might be my fault. Oh, do you I haven't heard I haven't heard yours in the background. Okay. Was it yours? Alright. Could be. They came home, but they're not sounding cute. They're not sounding cute. They're sounding angry. Alright. So now what is the Nuxt command? Right? I think there's one where I can let you guys tunnel into this. Right? Nuxt dev tunnel? Where is this guy? That is brand that must be brand new because I didn't know that was there. But it's built into Visual Studio Code. If you bring up your status bar at the bottom, I thought it was baked in now. No way. That is sick. Alright. So does this actually work? There there's the tunnel right there, the Cloudflare URL. Womp womp. Why Why doesn't it work? You better work it out because you got 2 minutes. At this point, I don't know. Do you guys just, do me a favor and then log in to the direct us instance in this firehouse message? I got you. P p m. Yeah. Nux. Dev. Oh, p m p m. It would be, like, running the let's see. I don't know if that's it or not. I'm stressed. Yeah. So there we go. Yeah. That was me. It's Does it work does it work if you type it in here as well? Yes. I I'm logged in as Alex. I'm logged in as Alex. Alright. Send send one more just so we can say something. Alright. Here? Less stressed. Yay. Yeah. Rock on. Some concept. That's basically feature complete. I was shitting. Hit right at the buzzer. Is Let's go. Because this is a winner. Yeah? Yay. Holy heck. My heart rate. I'm I'm gonna need to take a shower. I'm sweating. Oof. Look at that. Yeah. Wow. Stop the timer. Yeah. Leave it for Stop. Yeah. Series a already. I've watched them, man. And don't forget, there's more in your stressing and dressing in 100 hours. A whole season now available on Directus TV with more coming in April. I I look forward to the other episodes where you guys are are it may be not on there. I I've enjoyed this a lot, though. This is fun. It definitely distracted a little bit. Yeah. I did feel like, should this be, like, 1 app in a 100 hours, but with guests? Oh, wow. Oh, so takeaways from this. Right? What did we what did we learn? I'm not coming back to the next one. Spend more time with Patty. More time with that. Dude, I'm sorry we didn't get to the, like, colors. Like, do you wanna what what color are you feeling, Matt? It's the only reason I'm so sorry. Pick the color. I'm looking for the color. I can't think of the hex code for I didn't 466, double f. What do you want? 4466 f f. No. It's 6644 f f. Knew it. Hopefully, Ben's not watching. What is it? Oh, he's watching. 6:6:8. He's watching. 44 f f. Where are we? Is it not updating? Is that already the indigo oh, there it is. Okay. No? Maybe it wasn't on it here. Is that the sidebar? Yeah. Okay. Sidebar. Yeah. There you go. BG you're gonna find me one more time. 6644 f f? Yep. That's the one. That's it. There it is. Subtleship. Boom. Feature comp New product. Ship it. Thanks everyone for joining us. Yeah. I'm not I'm not kidding. We probably won't do another one now. We probably will, but I won't be here. Gosh. Literally, 30 seconds to go. 30 seconds to go. Congratulations, Brian. Yeah. I feel like we started off on the wrong foot with the 5 minutes of technical difficulties. The for the nodes. Selecting 5 minutes of technical difficulties at the the start of this thing. That's what everybody did not see on the front end. We got there. We got there. Now we're now we're ready to code for, like, another 2 hours. And if you're watching this live, this whole episode will be packaged up as a special at the end of season 1 of a 100 and a 100 hours on director's TV. We'll pop that out tomorrow. And then there are new episodes of a 100 apps and a 100 hours coming in April. Amazing. Excellent. Yeah. Well, thanks for joining us, to the circus episode of 100 Hours 100 Hours. I'm your host, Brian Gillispie. Thanks to my special guests, mister Kevin Lewis, mister Avdv, and Awesome. With the crazy colors coming in. Thank you, everyone. Bye for now. Cheers. Bye. Cheers.",[4156,4157,4158,4159],"b8d11703-e240-4b61-9c3e-fc41467de84c","20dbfd3a-7b8c-4df8-b0b9-8bd71bec82c2","eaf4cd0d-bb4a-4906-a173-d6f103668a6c","653c7587-db32-4f98-8de4-8952ed4d51b5",[],{"year":2380,"number":177,"id":4162,"episodes":4163,"show":4174},"56dda5ff-2c3a-41ce-ae3a-580d6101026b",[4164,4165,4166,4167,4168,4169,4170,4171,4172,4173,4137],"cb4e067f-9507-4e18-ab9a-435565f9e653","8434838a-8e4f-489a-8da2-fbf10de5de6a","c997b25e-400c-4350-bba4-f63853d844f7","1109be0d-8ab5-479b-a052-8ad30d9ffb1c","dcb952e0-7d13-43ea-9b1c-f2ca63efd07d","0aae287d-3916-4d91-9310-25828998e562","8fed0eee-43f6-4767-8b77-79da7a059821","a311b57d-34e7-4073-9cf3-6a8c6c0f8b85","9271a4fc-addf-4400-9591-f2f2ec07bd79","30c48566-52cd-4728-a633-ca9675acc959",{"title":322,"slug":323},{"id":2392,"slug":4176,"vimeo_id":4177,"description":4178,"tile":4179,"length":159,"resources":12,"people":4180,"episode_number":889,"published":4151,"title":4182,"video_transcript_html":4183,"video_transcript_text":4184,"content":12,"seo":12,"status":19,"episode_people":4185,"recommendations":4187,"season":4188},"10-10","919163587","Directus 10.10 introduces the Directus Marketplace Beta - a new way to discover and install extensions in your projects, Content Versioning enhancements for use with Live Preview, and a number of small breaking changes to be aware of.","f9c1f3cc-3333-4152-973c-007908d4beb7",[4181],{"name":2820,"url":2821},"What's New in Directus Version 10.10","\u003Cp>Speaker 0: Hello there. My name's Kevin. And today, I'm gonna tell you what's new in the just released Directus version 10.10. The Directus Marketplace Beta. Directus 10.10 introduces the Directus Marketplace Beta, and we are incredibly excited about it.\u003C\u002Fp>\u003Cp>You can use it to discover and install extensions in your Directus project directly from the data studio. And regardless of whether you are self hosting or using directors cloud, the marketplace will be available to you. It is in beta right now, so we really are relying on your feedback in order to make it even better. You can give feedback over in our discord in the marketplace beta channel. Content versioning enhancements.\u003C\u002Fp>\u003Cp>When querying content versions that have relational data, we are now performing a deep merge before returning the data to you. In practice, this means that the data structure, if you are, and the data structure, if you aren't using content versioning, is now the same. This is particularly useful when using live preview and content versioning. Now instead of needing to account for 2 different data structures, you can just use 1. Breaking changes.\u003C\u002Fp>\u003Cp>This version of directives also has a small number of breaking changes that I wanted to draw your attention to. Firstly, we have removed the extension subdirectories. So previously, if you self host directives inside of your extensions directory, there was a further subdirectory for each extension type. They're gone now. All the extensions go directly in the extensions directory.\u003C\u002Fp>\u003Cp>There's also some metadata that's required inside of your package dot JSON file if it isn't already there. Next, if you have set up single sign on or used the authentication service in your extensions, take a look at the changes to how the app now handles session cookies. We are simplifying it, but there may be small changes that you need to make. And finally, due to an open redirect vulnerability with the OAuth 2 OpenID and SAML SSO providers, we have now introduced an allow list for these redirects. So you may need to set a couple of environment variables to add the redirect URLs to the allow list.\u003C\u002Fp>\u003Cp>Community contributions. Since directors version 10.9.0, we have had a number of community contributions. And in this section, we thank the authors. Thank you to Michael for improving the detail when there is an incomplete SAML response. Thank you to Florian for a bunch of contributions to our translation features and also for cleaning up some padding for the builder buttons.\u003C\u002Fp>\u003Cp>Thank you to Riley for changing up how we validate UUIDs and also for cleaning up a bug around checking roles. And finally, thank you to Davian for making our update data flow operation more flexible. You can now update multiple items with unique values. This, of course, is just a highlight. There are more features, optimizations, and bug fixes in Director's version 10.10, which is now available on NPM and Docker Hub.\u003C\u002Fp>\u003Cp>We hope you love it, and please do give us feedback around the marketplace beta. And until next time, bye for now.\u003C\u002Fp>","Hello there. My name's Kevin. And today, I'm gonna tell you what's new in the just released Directus version 10.10. The Directus Marketplace Beta. Directus 10.10 introduces the Directus Marketplace Beta, and we are incredibly excited about it. You can use it to discover and install extensions in your Directus project directly from the data studio. And regardless of whether you are self hosting or using directors cloud, the marketplace will be available to you. It is in beta right now, so we really are relying on your feedback in order to make it even better. You can give feedback over in our discord in the marketplace beta channel. Content versioning enhancements. When querying content versions that have relational data, we are now performing a deep merge before returning the data to you. In practice, this means that the data structure, if you are, and the data structure, if you aren't using content versioning, is now the same. This is particularly useful when using live preview and content versioning. Now instead of needing to account for 2 different data structures, you can just use 1. Breaking changes. This version of directives also has a small number of breaking changes that I wanted to draw your attention to. Firstly, we have removed the extension subdirectories. So previously, if you self host directives inside of your extensions directory, there was a further subdirectory for each extension type. They're gone now. All the extensions go directly in the extensions directory. There's also some metadata that's required inside of your package dot JSON file if it isn't already there. Next, if you have set up single sign on or used the authentication service in your extensions, take a look at the changes to how the app now handles session cookies. We are simplifying it, but there may be small changes that you need to make. And finally, due to an open redirect vulnerability with the OAuth 2 OpenID and SAML SSO providers, we have now introduced an allow list for these redirects. So you may need to set a couple of environment variables to add the redirect URLs to the allow list. Community contributions. Since directors version 10.9.0, we have had a number of community contributions. And in this section, we thank the authors. Thank you to Michael for improving the detail when there is an incomplete SAML response. Thank you to Florian for a bunch of contributions to our translation features and also for cleaning up some padding for the builder buttons. Thank you to Riley for changing up how we validate UUIDs and also for cleaning up a bug around checking roles. And finally, thank you to Davian for making our update data flow operation more flexible. You can now update multiple items with unique values. This, of course, is just a highlight. There are more features, optimizations, and bug fixes in Director's version 10.10, which is now available on NPM and Docker Hub. We hope you love it, and please do give us feedback around the marketplace beta. And until next time, bye for now.",[4186],"96f15a29-8051-4292-98a5-1c584919c505",[],{"year":2380,"number":588,"id":2381,"episodes":4189,"show":4190},[2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394,2366],{"title":2396,"slug":2397},{"id":4192,"slug":4193,"vimeo_id":4194,"description":4195,"tile":4196,"length":177,"resources":12,"people":4197,"episode_number":129,"published":4201,"title":4202,"video_transcript_html":4203,"video_transcript_text":4204,"content":12,"seo":12,"status":19,"episode_people":4205,"recommendations":4207,"season":4208},"183146de-8afa-467b-a271-45f04a189890","02-cloud","918866384","Today, we’re proud to be expanding the Directus Cloud offering with key features requested by our customers.","cddd947d-9a92-4550-b2cb-5dcb5f4fcebe",[4198],{"name":4199,"url":4200},"Alex Chopin","https:\u002F\u002Fdirectus.io\u002Fteam\u002Falex-chopin","2024-03-04","What's New In Directus Cloud","\u003Cp>Speaker 0: Directus really is a solution for everyone. From side projects and small businesses, right through to huge organization like Kia, Copa Airlines, Walmart, and T Mobile. Our enterprise cloud offers dedicated infrastructure from the team who build directors. It's perfect for mission critical project with higher security and support needs. Today, we are proud to be expanding the enterprise cloud offering with key features requested by our customers.\u003C\u002Fp>\u003Cp>Over the last few months, our team has been working on improving our infrastructure in a number of areas, including security, performances, and flexibility. With this in mind, we are not able to offer auto scaling infrastructure, which means even with sudden peak in traffic, your project stays strong. Auto scaling is enabled by default on all our new projects, both on enterprise and professional cloud. We love speaking to our customers, but you should also need to speak to us less. Up until now, if you want to set up a custom domain for an enterprise project, you have to get in touch with us.\u003C\u002Fp>\u003Cp>Now it's over. Self-service custom domains are now available through the cloud dashboard for enterprise cloud projects. We are looking forward to chatting with you more, but not about custom domains.\u003C\u002Fp>","Directus really is a solution for everyone. From side projects and small businesses, right through to huge organization like Kia, Copa Airlines, Walmart, and T Mobile. Our enterprise cloud offers dedicated infrastructure from the team who build directors. It's perfect for mission critical project with higher security and support needs. Today, we are proud to be expanding the enterprise cloud offering with key features requested by our customers. Over the last few months, our team has been working on improving our infrastructure in a number of areas, including security, performances, and flexibility. With this in mind, we are not able to offer auto scaling infrastructure, which means even with sudden peak in traffic, your project stays strong. Auto scaling is enabled by default on all our new projects, both on enterprise and professional cloud. We love speaking to our customers, but you should also need to speak to us less. Up until now, if you want to set up a custom domain for an enterprise project, you have to get in touch with us. Now it's over. Self-service custom domains are now available through the cloud dashboard for enterprise cloud projects. We are looking forward to chatting with you more, but not about custom domains.",[4206],"2c905038-7bb4-4532-9acb-c335146f0a8d",[],{"year":978,"number":2280,"id":4209,"episodes":4210,"show":4219},"a259b39f-2513-4c1c-9597-fe0345798029",[4211,4212,4213,4214,4215,4216,4192,4217,4218],"3ca36cc9-3dc4-4acd-9289-a97249c0d7e4","d1b923e7-9d76-4b23-8ac8-65e0bac37a6d","b46e60c2-a1e8-4cf1-aa27-986bc4f2c4ee","c5726061-87f2-41a0-941f-9bb44a0e57c2","68174425-5aa8-4e39-b97e-3e01785ce64b","6daa5396-df6e-42f0-a465-f682642b34f1","376e8170-920c-4f61-977d-4cc966db2a8f","23eee332-981a-46a1-beb2-d77ae646acec",{"title":151,"slug":152},{"id":4212,"slug":4221,"vimeo_id":4222,"description":4223,"tile":4224,"length":264,"resources":12,"people":4225,"episode_number":177,"published":4201,"title":4227,"video_transcript_html":4228,"video_transcript_text":4229,"content":12,"seo":12,"status":19,"episode_people":4230,"recommendations":4232,"season":4233},"02-marketplace-beta","918871173","Introducing the Directus Marketplace Beta: distribute and install extensions in any Directus project and supercharge your projects.","0e49532b-c676-4c37-83ca-820c089cba42",[4226],{"name":3795,"url":3796},"Directus Marketplace Beta","\u003Cp>Speaker 0: This is the director's marketplace. It is in beta for now for this first release, so we do really want your feedback to make it even better. The focus of this first iteration is really about discovery and installation. I'm gonna show you each of those here. So in settings, on the left hand sidebar, there's a new section for the marketplace.\u003C\u002Fp>\u003Cp>In this marketplace, we have a listing of all of the extensions that we've been able to find on npm. These extensions contain every single type of extension that you know from before. So those are interfaces, modules, panels, themes, etcetera. When you install an extension, you'd find those in the same places where you'd find them normally. So for interfaces, you'd find it in data model.\u003C\u002Fp>\u003Cp>Modules show up in the sidebar, themes, appearance, etcetera. For any of those types, you can find them by, you know, current popularity, which ones were most recently published, or which ones are downloaded the most. In this case, let me go see if we can install a theme. So I'm going to look up with the search, kind of find the theme that I'm looking for. For each individual extension, we're pulling in the readme file from your GitHub repo if it's connected, and we're pulling in the author and maintainers as provided by npm.\u003C\u002Fp>\u003Cp>Same goes for an author. So in this particular case, because Kevin has his GitHub linked to his npm, we can show, you know, the about information from his GitHub profile together with an avatar and all of the extensions that he has uploaded so far. To install the theme, I could just click the install button and there it goes. So now that this theme is installed, I can find it under appearance like I'm with any other theme. There it is.\u003C\u002Fp>\u003Cp>Mhmm. And there we go. To manage extensions you have installed, you can go to the extensions tab as you would for extensions before, where you can now both disable and uninstall them again. In the future, there's a couple of additional features that we know we want to add, starting with verified authors, verified packages, allowing the end user to know which one is going to be trusted. But also, we're looking to see if we can open up monetization in the marketplace.\u003C\u002Fp>\u003Cp>So this would allow you to sell extensions that you have built to others, in the ecosystem. That will be in the future release. In the meantime, we absolutely hope you enjoy this. Please do leave all and every bit of feedback you have in the Discord channel marketplace beta.\u003C\u002Fp>","This is the director's marketplace. It is in beta for now for this first release, so we do really want your feedback to make it even better. The focus of this first iteration is really about discovery and installation. I'm gonna show you each of those here. So in settings, on the left hand sidebar, there's a new section for the marketplace. In this marketplace, we have a listing of all of the extensions that we've been able to find on npm. These extensions contain every single type of extension that you know from before. So those are interfaces, modules, panels, themes, etcetera. When you install an extension, you'd find those in the same places where you'd find them normally. So for interfaces, you'd find it in data model. Modules show up in the sidebar, themes, appearance, etcetera. For any of those types, you can find them by, you know, current popularity, which ones were most recently published, or which ones are downloaded the most. In this case, let me go see if we can install a theme. So I'm going to look up with the search, kind of find the theme that I'm looking for. For each individual extension, we're pulling in the readme file from your GitHub repo if it's connected, and we're pulling in the author and maintainers as provided by npm. Same goes for an author. So in this particular case, because Kevin has his GitHub linked to his npm, we can show, you know, the about information from his GitHub profile together with an avatar and all of the extensions that he has uploaded so far. To install the theme, I could just click the install button and there it goes. So now that this theme is installed, I can find it under appearance like I'm with any other theme. There it is. Mhmm. And there we go. To manage extensions you have installed, you can go to the extensions tab as you would for extensions before, where you can now both disable and uninstall them again. In the future, there's a couple of additional features that we know we want to add, starting with verified authors, verified packages, allowing the end user to know which one is going to be trusted. But also, we're looking to see if we can open up monetization in the marketplace. So this would allow you to sell extensions that you have built to others, in the ecosystem. That will be in the future release. In the meantime, we absolutely hope you enjoy this. Please do leave all and every bit of feedback you have in the Discord channel marketplace beta.",[4231],"719facdf-cc52-4098-ab17-f9ba30a904f4",[],{"year":978,"number":2280,"id":4209,"episodes":4234,"show":4235},[4211,4212,4213,4214,4215,4216,4192,4217,4218],{"title":151,"slug":152},{"id":4214,"slug":4237,"vimeo_id":4238,"description":4239,"tile":4240,"length":177,"resources":12,"people":4241,"episode_number":159,"published":4201,"title":4243,"video_transcript_html":4244,"video_transcript_text":4245,"content":12,"seo":12,"status":19,"episode_people":4246,"recommendations":4248,"season":4249},"02-tv-spring","918870915","Directus TV is our video streaming platform containing hours of content for developers. We're announcing our slate of new shows and renewals for Spring 2024.","27e0999c-ca27-4760-b40e-5b99ee94da96",[4242],{"name":2820,"url":2821},"This Spring on Directus TV","\u003Cp>Speaker 0: Today, we're announcing some new shows that are going to land on your screen this spring. In what's in your doc, we discovered the toolkit behind really productive I that will somehow enrich my life. So we've invited 5 guests to ask them about the software, hardware, and analog tools that they use to run their lives. I had a blast recording it, and I know you're gonna have a blast watching it. In the joy of theming, we invite Bry Ross, who is definitely not just Bryon in a wig, to highlight the power behind the director's theming engine by creating nice new themes that pay homage to brands you know and love.\u003C\u002Fp>\u003Cp>There are only happy little accidents in this series and their lovely, lovely, peaceful episodes. Battlesnake is a popular competitive game where your code is the controller. And in Ready Set Battlesnake, Andrew joins me to build a snake inside of Directus. Honestly, we can't believe it worked. Not only that it worked, but it worked as well as it did.\u003C\u002Fp>\u003Cp>Join us to find out how. With great power can come great complexity. In short hops, we share quick tips and tricks to help you get the most from directors. And the best bit is this series is based directly on community feedback. Today, we're also announcing some show renewals.\u003C\u002Fp>\u003Cp>So you can look forward to new episodes of both a 100 apps in a 100 hours and dev thoughts. Now many of you have also been asking about how directors TV is actually put together. So today, we're also releasing in full a new series called digging the rabbit hole where we're going to cover everything Directus TV from the conception and inspiration behind the platform, the technical setup, the lifecycle of a show from pitch through to release, and some of the early feedback. We hope you enjoy all of these new shows that are landing this spring. And, of course, you can watch Digging the Rabbit Hole in full today.\u003C\u002Fp>","Today, we're announcing some new shows that are going to land on your screen this spring. In what's in your doc, we discovered the toolkit behind really productive I that will somehow enrich my life. So we've invited 5 guests to ask them about the software, hardware, and analog tools that they use to run their lives. I had a blast recording it, and I know you're gonna have a blast watching it. In the joy of theming, we invite Bry Ross, who is definitely not just Bryon in a wig, to highlight the power behind the director's theming engine by creating nice new themes that pay homage to brands you know and love. There are only happy little accidents in this series and their lovely, lovely, peaceful episodes. Battlesnake is a popular competitive game where your code is the controller. And in Ready Set Battlesnake, Andrew joins me to build a snake inside of Directus. Honestly, we can't believe it worked. Not only that it worked, but it worked as well as it did. Join us to find out how. With great power can come great complexity. In short hops, we share quick tips and tricks to help you get the most from directors. And the best bit is this series is based directly on community feedback. Today, we're also announcing some show renewals. So you can look forward to new episodes of both a 100 apps in a 100 hours and dev thoughts. Now many of you have also been asking about how directors TV is actually put together. So today, we're also releasing in full a new series called digging the rabbit hole where we're going to cover everything Directus TV from the conception and inspiration behind the platform, the technical setup, the lifecycle of a show from pitch through to release, and some of the early feedback. We hope you enjoy all of these new shows that are landing this spring. And, of course, you can watch Digging the Rabbit Hole in full today.",[4247],"a0d47a71-fe58-42cc-8226-7dc40eda0dff",[],{"year":978,"number":2280,"id":4209,"episodes":4250,"show":4251},[4211,4212,4213,4214,4215,4216,4192,4217,4218],{"title":151,"slug":152},{"id":4211,"slug":4253,"vimeo_id":4254,"description":4255,"tile":4256,"length":282,"resources":12,"people":12,"episode_number":13,"published":4201,"title":4257,"video_transcript_html":4258,"video_transcript_text":4259,"content":12,"seo":12,"status":19,"episode_people":4260,"recommendations":4261,"season":4262},"02-keynote","919060200","The full keynote from our second Leap Week in March 2024","b84681ca-ea99-4dfc-b741-635d5c0e65f7","Leap Week 02: Full Keynote","\u003Cp>Speaker 0: Hello. Ben Haines, CEO and founder of Directus. It is actually 2024, and I originally started this software in 2004. So now 2 decades in, obviously, quite a bit has changed, and I think it's well overdue for us to go through and look at how we talk about Directus and how that has changed, over those those 2 decades. I'd like to do that through a quick 5 minute demo.\u003C\u002Fp>\u003Cp>Obviously, a lot to cover in 5 minutes, but let's see, if we can make that happen. So we're starting off right here on the login screen. Here we're already logged in, so we're just gonna hit continue. But I think the first thing to notice, is that the entire platform is white label. Everything here is themeable from the border radii to the colors to this, subtle animation on the right.\u003C\u002Fp>\u003Cp>But this is your login screen. Same thing for all these public pages. Of course, you'd have 2 factor authentication, SSO, anything else you might need here, for your your project. Let's go ahead and log in. Again, as a reminder, the way that Directus works is you point it at a database.\u003C\u002Fp>\u003Cp>I like to start on this screen, because this is just a table in your database called metrics. Nothing more straightforward than that. Instead of seeing, you know, UNIX time stamps and, you know, just really complex data, we're seeing it in an intuitive way. This is a table layout. Many different ways you can look at your data, but here we're seeing conditional styling with icons, nice relative time stamps, and nice colors.\u003C\u002Fp>\u003Cp>The full power of SQL is available here. We can of course do full text search, advanced filtering with logical grouping, everything you could do in SQL but in an in an intuitive no code way. Table views are pretty obvious. You can also come in and look at things through a media view. You could use a kanban view if you had, something where you're actually dragging things through some sort of process, and then keeping your your data organized like this.\u003C\u002Fp>\u003Cp>Of course, if you have geospatial data in your database, we support that as well. So whether it's submarine cables or devices on a map, all of this is available. You can quickly zoom in, drill into an item, click, and then go in and start editing that that data. We also have, contacts or any other type of data, but I think where we'll drill into the actual form is on this blog. So if we click on one of these items, in this case, multi orchestration cultivate infrastructure, lots of different interfaces for how we edit the data.\u003C\u002Fp>\u003Cp>You have time, daytime choosers. You have media selectors. You have this really great translation interface where you can actually go into a split view and manage multilingual content, etcetera. Every change that you make goes through our accountability system. So you can come over here and see all the revisions.\u003C\u002Fp>\u003Cp>These are the changes that were made by who and when. You can actually roll back and undo those features. Really, really important for data governance. We also have a full commenting system with mentions, emoji, and everything you'd expect, and you can actually share things. You can create a unique URL and share that out to people outside of this, this platform.\u003C\u002Fp>\u003Cp>So really powerful there as well. Moving on to a few of the other modules that we have. We have a user directory, which I'll pop into my user very quickly, just to showcase something that I think is pretty, pretty powerful. We can actually change the language to any of the 55 languages that we support and choose different themes, really tailoring it to the experience that I I need as a user. So as I come in, not only are we managing multilingual content, but the actual application can be translated as well.\u003C\u002Fp>\u003Cp>So that's a really important feature. I'm gonna change that back, to English. That is the language that I understand, and we'll continue on. Really, really powerful access control here within the user directory and no limits in terms of how many roles, or or users you have. Once you come into our asset management system, you can actually we scrape all of the, metadata.\u003C\u002Fp>\u003Cp>So I p t c, exif, all available, virtual folders, and you can, of course, come in here and do all of your edits and cropping. If you do that through the API, you actually have hundreds of different operations available. Insights, great for business intelligence, great for building out these different dashboards using aggregation, time series, anything that you can imagine. Very customizable. You just come in here, drag and drop these to whatever size you want.\u003C\u002Fp>\u003Cp>Lots of options available for, how are you setting your precision, your ranges, etcetera, the colors. So put that into a full screen mode, throw it on a, a second display, and you've got a really great dashboard available instantly. Once we go into settings, this is where the ad administrators are working. You can, of course, build your data model here or you can build it through the API or just define it right in SQL and allow it to cascade out through everything. Then you can go through and start setting your access control as we talked about.\u003C\u002Fp>\u003Cp>You know, you have managers. Based on the tables of your database, who can access what based on CRUD? Create, read, update, and delete. It's worth noting it's not just on and off, we have very granular rule based access control, within these different filters. Lots of other things to talk about including the theming and all these different options, all different extensions of our system.\u003C\u002Fp>\u003Cp>The last thing I'd like to quickly show is our flows. This is automation. This is where you can come in and say I wanna deploy an application when x happens. Those triggers can be a cron job every 15 minutes or Monday at midnight. You can also have it when content changes.\u003C\u002Fp>\u003Cp>Send out emails, to our different content authors for review. Lots of different operations you can choose from. And again, as with everything else in the system, it's all extensible and customizable. That's about as fast as we can run through the many, many features and capabilities of this platform. But for now, I'll hand it back over to Kevin.\u003C\u002Fp>\u003Cp>Thank you.\u003C\u002Fp>\u003Cp>Speaker 1: Thank you, Ben, and welcome to the second Leap Week. We have a fantastic week in store. We're going to start today with all of our announcements and then throughout the rest of the week we have loads of other events including socials, panels, and workshops for you to enjoy. One of the reasons people love Directus is because of its extensibility. You can augment and enhance most parts of Directus by using extensions, and then combine extensions in interesting ways to compose a data platform that really makes sense for your project.\u003C\u002Fp>\u003Cp>We have app extensions like panels for directors insights, interfaces for the directors editor, layouts, and themes. We also have API and hybrid extensions like operations, endpoints, and hooks. Our community has built so many amazing extensions including the media AI bundle that lets you extract information from images in Directus files, computed interfaces that change the way that data is displayed after applying logic to them, and, of course, the Director's Copilot that lets you have contextual conversations about your data with an AI bot in order to unlock new insights. Agency partners use extensions like Pixsy Labs based in London. They built a blur hash extension which allows them to nicely load in very large images for an online art gallery.\u003C\u002Fp>\u003Cp>And we have other customers use extensions for things like integrating with translation services, transforming videos, build automation, and more. Having this rich framework to build extensions is awesome. The missing bit has always been how to publish and reuse those extensions between projects. Today we are announcing something that I could not be more excited about, something which Directus users have been asking for for years. I'm gonna hand over to Rike to tell you more.\u003C\u002Fp>\u003Cp>Speaker 2: This is the Directless marketplace. It is in beta for now for this first release, so we do really want your feedback to make it even better. The focus of this first iteration is really about discovery and installation. I'm gonna show you each of those here. So in settings, on the left hand sidebar, there's a new section for the marketplace.\u003C\u002Fp>\u003Cp>In this marketplace, we have a listing of all of the extensions that we've been able to find on npm. These extensions contain every single type of extension that you know from before. So those are interfaces, modules, panels, teams, etc. When you install an extension, you'd find those in the same places where you'd find them normally. So for interfaces, you'd find it in data model, modules show up in the sidebar, themes, appearance, etcetera.\u003C\u002Fp>\u003Cp>For any of those types, you can find them by, you know, current popularity, which ones were most recently published, or which ones are downloaded the most. In this case, let me go see if we can install a theme. So I'm gonna look up, you know, with the search, gonna find the theme that I'm looking for. For each individual extension, we're pulling in the readme file from your GitHub repo if it's connected, and we're pulling in the author and maintainers as provided by npm. Same goes for an author.\u003C\u002Fp>\u003Cp>So in this particular case, because Kevin has his GitHub linked to his NPM, we can show, you know, the about information from his GitHub profile, together with an avatar, and all of the extensions that he has uploaded so far. To install the theme, I could just click the install button, and there it goes. So now that this theme is installed, I can find it under appearance like I'm with any other theme. There it is. And there we go.\u003C\u002Fp>\u003Cp>To manage extensions you have installed, you can go to the extensions tab as you would for extensions before, where you can now both disable and uninstall them again. In the future, there's a couple of additional features that we know we want to add, starting with verified authors, verified packages, allowing, you know, the end user to know which one is gonna be trusted. But, also, we're looking to see if we can open up monetization in the marketplace. So this would allow you to sell extensions that you have built to others, in the ecosystem. That will be in the future release.\u003C\u002Fp>\u003Cp>In the meantime, we absolutely hope you enjoy this. Please do leave all and every bit of feedback you have in the Discord channel marketplace beta.\u003C\u002Fp>\u003Cp>Speaker 1: So the marketplace. Cool. Right? And the thing that excites me most is the marketplace is gonna be available in every Director's project. So whether you're self hosting or you're using Director's Cloud, you will now be able to install extensions via the marketplace.\u003C\u002Fp>\u003Cp>Now, the marketplace is part of director version 10.10, but it isn't all. And we've had a few releases since the last week. So I wanted to kick it over to some of our engineers to tell you about the new and notable features that have shipped since the last Leap Week.\u003C\u002Fp>\u003Cp>Speaker 3: In Directus 10.8, we released a powerful new theming engine. Previously, you were limited to adding your own CSS on top of the data studio to make yours. You now have the flexibility to customize colors, fonts, spacing, and much more across Directus using our new theme interface. Being able to white label Directus has always been important. Bringing your brand and aesthetic to the forefront really make Directus feel like your own tool.\u003C\u002Fp>\u003Cp>Building on the excitement of our marketplace announcement, you can now develop and share themes as an extension. Now you can do more than just make direct us your own. You can share your designs and help others get creative too. We're excited to see the themes you bring to the marketplace. Thank you all for joining me in this exciting chapter.\u003C\u002Fp>\u003Cp>Your vision, your directness, ready to dazzle. The canvas is fast. The palette is rich. And the possibilities, endless.\u003C\u002Fp>\u003Cp>Speaker 4: As well as managing databases, Directus also helps you manage your files. As part of that, we offer media transformations via URL. Here's an image of 2 people on a beach. Notice that they're slightly off center. Which is fine, until you start to crop the image via your app.\u003C\u002Fp>\u003Cp>Here the image is being cropped off from the center, but these two people are being chopped off. In Directus 10.9, we introduced focal points. You can now specify the point for Directus treat as the center. You can do this via the data studio or the files API. Now the subjects that matter in your images can always be visible.\u003C\u002Fp>\u003Cp>You can combine this with our automation features to determine what the point of interest should be. We hope you enjoy working with focal points, a small change with big impact.\u003C\u002Fp>\u003Cp>Speaker 5: Before, when requesting content versions, this is the data structure, the data API return. And with relational fields, note that there is a create, update, delete array for each item. This is correct. It is what will happen when this version gets promoted. But it's different to what you may expect from an item when not using content version or using the main version.\u003C\u002Fp>\u003Cp>This does made it hard to implement live preview as you had to account for 2 different data structures. In directors 10.10, we will store this data structure. But when you request a content version, we will standardize the data structure before returning it. So the return data is now the outcome if you were to promote the item. Here, we have an example of both a one to many and the many to many relationship.\u003C\u002Fp>\u003Cp>In the one to many comments example, we are both updating an item and deleting related comments. The new output doesn't contain the deleted comment and returns the full item including any changes made in the content version. Likewise, in the many to many page blocks example, a block is being created and another deleted. The return data from a content version is now the same structure you expect including the outcome of any changes. Now you can implement live preview with a standard and predictable data structure.\u003C\u002Fp>\u003Cp>Speaker 1: Thank you very much for that rundown, and we hope you enjoy using theming, focal points, and our enhancements to content versioning. And, of course, this is just a tiny sliver of the features which we have included in our recent releases. But for today, there's plenty more announcements to\u003C\u002Fp>\u003Cp>Speaker 4: come.\u003C\u002Fp>\u003Cp>Speaker 6: Let's rock and roll.\u003C\u002Fp>\u003Cp>Speaker 1: Let's get started with Directus and Nuxt.\u003C\u002Fp>\u003Cp>Speaker 4: You don't hire smart people to tell them what to do. You hire smart people to figure out your problems.\u003C\u002Fp>\u003Cp>Speaker 6: Can you build Netflix in an hour?\u003C\u002Fp>\u003Cp>Speaker 1: Not terribly elegant, but we're just gonna throw a new error.\u003C\u002Fp>\u003Cp>Speaker 0: Because even pirates, sometimes they're they're not pirating the way you want\u003C\u002Fp>\u003Cp>Speaker 2: a pirate. Keep it simple. Won't be complicated.\u003C\u002Fp>\u003Cp>Speaker 4: Hey. I enjoy what I do.\u003C\u002Fp>\u003Cp>Speaker 1: I've got my pen now. I'm serious.\u003C\u002Fp>\u003Cp>Speaker 6: We're gonna be cutting this one really, really close.\u003C\u002Fp>\u003Cp>Speaker 1: Let's talk about Directus TV. It's a new streaming platform that we released at the end of last year with hours of content for you to enjoy and I wanna tell you about some of our series. In a 100 apps in a 100 hours, Bryant takes a common application idea and tries to build it in just 60 minutes. Sometimes he succeeds, sometimes he doesn't, and honestly it's a bit of a stressful watch but it's always a lot of fun. In Trace Talks, John and Pedro interview engineering leaders about their journeys.\u003C\u002Fp>\u003Cp>It's full of interesting insights and anecdotes for all stages of your career. And in quick connect, we show you how to integrate with third party platforms to build some interesting and useful automations. Today we're announcing some new shows that are going to land on your screen this spring. In what's in your doc, we discovered the toolkit behind really productive people. I don't know about you but whenever someone shares their screen I always look at all of the apps they're running in the hope that I can find something new that will somehow enrich my life.\u003C\u002Fp>\u003Cp>So, we've invited 5 guests to ask them about the software, hardware, and analog tools that they use to run their lives. I had a blast recording it and I know you're gonna have a blast watching it. In the joy of theming, we invite Bry Ross who is definitely not just Bryon in a wig to highlight the power behind the director's theming engine by creating nice new themes that pay homage to brands you know and love. There are only happy little accidents in this series and they're lovely lovely peaceful episodes. Battlesnake is a popular competitive game where your code is the controller and in Ready Set Battlesnake Andrew joins me to build a snake inside of Directus.\u003C\u002Fp>\u003Cp>Honestly, we can't believe it worked not only that it worked but it worked as well as it did, join us to find out how. With great power can come great complexity. In short hops, we share quick tips and tricks to help you get the most from directors. And the best bit is this series is based directly on community feedback. Today we're also announcing some show renewals so you can look forward to new episodes of both the 100 Apps in a 100 Hours and Dev Thoughts.\u003C\u002Fp>\u003Cp>Now many of you have also been asking about how directors TV is actually put together. So today we're also releasing in full a new series called Digging the Rabbit Hole where we're going to cover everything Directus TV from the conception and inspiration behind the platform, the technical setup, the life cycle of a show from pitch through to release, and some of the early feedback. We hope you enjoy all of these new shows that are landing this spring, and, of course, you can watch digging the rabbit hole in full today. Directors TV really is a community project. We're building shows for you to enjoy, taking in your feedback, and then deciding what new shows we can create or shows we can renew based on that feedback.\u003C\u002Fp>\u003Cp>Well, our community does so much more, and I wanna hand over to Jonathan to talk more about it.\u003C\u002Fp>\u003Cp>Speaker 7: Hey, everyone. The Directus community is the foundation, DNA, essence, source of strength, the font of magic, you might say, that makes up our team and makes us excited to come to work every day. We're so thrilled to have such a great community. I get the opportunity to interact with the community on a daily basis and that community interaction. I learned something new each time, and that includes anything from a feature request to a support ticket, to presales activities, talking to new clients, people that have used direct us for years to people that are brand new to the direct us.\u003C\u002Fp>\u003Cp>And I get that opportunity to interact with everyone, on a daily basis. And it is it it literally makes me excited to come to work every day. We do some other community actions and activities that we do our request reviews. We do those every 2 weeks. You get an opportunity to see our processes, how we go through feature requests, and we get that our we get that live interaction with community members.\u003C\u002Fp>\u003Cp>Hope to see you there. It's very, very fun and exciting to see that. And then from that explosion of ideas, we narrow that back down to what is possible. Some other things we do some in person activities these days. We'd still do some live interactions for Lynn, London, New York.\u003C\u002Fp>\u003Cp>There's a variety of others. You can find those out on the website and see that. And with that, I get to tell you about that newest hackathon. I'm so excited. The hackathons have brought new use cases, new capabilities to exist to the previous hackathons.\u003C\u002Fp>\u003Cp>We've seen and learned some really cool things about what people the concepts and ideas that someone might come up with. And so this particular hackathon is gonna be focused around one of our biggest, up boats kind of in the community is around payments. So we'd like to see the directest payments hackathon. We'd like to see you build and publish in the new marketplace. I'm so excited about the marketplace.\u003C\u002Fp>\u003Cp>Think through the scenarios and the use cases. You know, how would I deliver coupons? How would I deliver trials? What happens when payments fail? What happens when deployments fail or some issue happens during that process?\u003C\u002Fp>\u003Cp>Make sure you've got handling logic around those kinds of things. Include reporting include any, extensions or, data details that are valuable that you find in your day to day interactions, with payment platforms and how you want that to operate. You'll have until the end of March to submit that you'll find all of the details around prizes and information about how and when and what to submit, on directus. Io\u002Fhackathons, and you'll find links throughout the the documentation and things that we'll share through the coming days. With that said, I get the opportunity to hand off to mister Matt Miner to talk more about our community.\u003C\u002Fp>\u003Cp>Cheers.\u003C\u002Fp>\u003Cp>Speaker 8: Hey. How's it going? I'm Matt on the marketing team here at Directus. Data discussions really aren't the stuff of legend. In fact, when we put this survey together, at first, there was a couple, like, raised eyebrows, like, data, really?\u003C\u002Fp>\u003Cp>Is that what we wanna do? What do you know? A month later, we had a huge flood of responses. 782 developers told us how they're using data in their projects. That tells us that, you know, data isn't dull.\u003C\u002Fp>\u003Cp>There's just nobody talking about it in the right way. We're gonna make data exciting again. Here are the 6 things that we learned that we thought were really interesting, or 2 of these actually really shocked us when we found out. Number 1 is that Postgres leads the pack. It is the choice of over 63% of the developers we surveyed, that they're using in their projects currently.\u003C\u002Fp>\u003Cp>The number 2 thing, slightly related, but of all the database types when developers are starting new projects, over 57% of them said that they prefer relational databases all over over all other types. Even asked our CTO, Wrike, about it. And he said, you know, the most likely reason is that a vast majority of data applications have some sort of schema consistency need for type safety and expectability. In NoSQL, you can quickly end up effectively reimplementing a lot of things that a SQL database already offers out of the box. The number 3 thing we found that was really interesting to us was that developers are leaning towards Cloud Hosted Database Solutions.\u003C\u002Fp>\u003Cp>So by a wide margin too. Cloud leads, 68% of respondents prefer hosting with solutions like AWS or Azure, versus, you know, managed database services like DigitalOcean or on prem servers. This one, I think, was so shocking because it was so lopsided. 94% of our survey respondents said they prefer working with REST APIs as opposed to GraphQL. We kind of went into this thinking that cost was gonna be the primary factor, to consider when hosting databases.\u003C\u002Fp>\u003Cp>And in fact, it is a consideration, but it's not the most important. I think what this tells us is that developers are prioritizing performance and reliability and ease of use over how much it costs, which is still a consideration, but not what's driving, decisions at the end of the day. Taking this a step further, actually, we asked what the primary factors they think hosting should be charging for. And the top 3 in order were, machine resource utilization, so like RAM and CPU, storage space, and actually number of read and write options, which was interesting. The least popular way to price hosting, was by charging on the number of database records.\u003C\u002Fp>\u003Cp>So just an interesting little finding there. But, yeah. Number 6 was the most shocking thing we found. Over 64% of respondents said that they're using AI for cogeneration. So despite all of this, like loud people in the room talking about, like, AI is bad for development, I think there's a lot of people quietly actually using this to become better at their job and learning how to implement and the things that they're doing.\u003C\u002Fp>\u003Cp>Actually, going a little bit deeper on this, we asked 2 questions. If AI was too risky for data analysis to influence decision making, on average, sentiment was neutral. But when it comes to using AI to actually interact with the database with, like, CRUD permissions, overwhelming sentiment was, like, please do not do that. That is way too risky, which makes sense. So those are the 6 big takeaways, learnings we add just from a surface evaluation of the results that we got for in the survey.\u003C\u002Fp>\u003Cp>Hopefully, these were interesting to you as they were to us. And again, like, data isn't boring. It's just how are people using it? How are we framing it? Hopefully, we can get some more discussions around this, because we're data nerds at the end of the day.\u003C\u002Fp>\u003Cp>And what we're gonna do is basically put together a series that we call Data Drops, where we're gonna release an article, with, like, a really interesting insight. Because it's one thing to know what the trends are, but we think it's more important to understand, like, why they vary from a startup in Silicon Valley as opposed to a seasoned freelancer in Berlin. So if you wanna get up to date on those and get nice little nuggets in your inbox, make sure you sign up for a newsletter. It goes out once a month. You can sign up in our docs.\u003C\u002Fp>\u003Cp>You can sign up on our, main blog. But we'd love to have you sign up and just get these once a month newsletters. Appreciate the time today, and, have a good one.\u003C\u002Fp>\u003Cp>Speaker 1: We hope you're enjoying today's announcement so far. They've included the release of Directus 10.10 with the Directus Marketplace beta and improvements to content versioning, our new shows on Directus TV, and, of course, our new hackathon. But we're not quite done yet. I want to kick it over to our director of engineering, Alex, to talk about what's\u003C\u002Fp>\u003Cp>Speaker 9: new for Directus Cloud. Directus really is a solution for everyone. From side projects and small businesses right through to huge organization like Kia, Copa Airlines, Walmart, and T Mobile. Our Enterprise Cloud offers dedicated infrastructure from the team who build directors. It's perfect for mission critical project with higher security and support needs.\u003C\u002Fp>\u003Cp>Today, we are proud to be expanding the Enterprise Cloud offering with key features requested by our customers. Over the last few months, our team has been working on improving our infrastructure in a number of areas including security, performances, and flexibility. With this in mind, we are not able to offer auto scaling infrastructure, which means even with sudden peak in traffic, your georectus project stays strong. Auto scaling is enabled by default on all our new projects, both on Enterprise and Professional Cloud. We love speaking to our customers, but you should also need to speak to us less.\u003C\u002Fp>\u003Cp>Up until now, if you want to set up a custom domain for an enterprise project, you have to get in touch with us. Now it's over. Self-service custom domains are now available through the Cloud Dashboard for Enterprise Cloud Projects. We are looking forward to chatting with you more, but not about Guest of the Vans. We have more announcements to come.\u003C\u002Fp>\u003Cp>Over to Colton for now.\u003C\u002Fp>\u003Cp>Speaker 10: Today, we're announcing our new partner program focused on agencies. Agency partners are a super important part of how we reach our customers. We've designed this program in conjunction with our current partners, and the program will enable agencies and client projects of all shapes and sizes, and we think you're gonna love it. We now have a formal certification program to help you get the most from Directus and understand which projects it is suitable for. And, of course, there will also be a larger resource center available which can help you in discussing direct us with your customers.\u003C\u002Fp>\u003Cp>Our team is available for advisory calls to validate your approaches, suggest new ones, and sell alongside your team. And of course, the program includes revenue sharing both upfront and renewals. The new partner directory will help companies looking for experts to implement their projects. And with co marketing opportunities, we can help share all your stories. This isn't all, but it captures the most important parts of the partner program.\u003C\u002Fp>\u003Cp>We really think you're gonna love it. You can find out more at directus. Io \u002F partners. And later this year, we'll also be expanding our program to include technical partners, but that's for another day. Back to you, Kevin.\u003C\u002Fp>\u003Cp>Speaker 1: It's been a wonderful day. And on behalf of the whole Director's core team, thank you so much for tuning in, whether live or on demand on Director's TV. Now before we head off, we have one more thing to announce. Bryant?\u003C\u002Fp>\u003Cp>Speaker 6: While headless CMS is one of our most popular use cases, Directus is a super powerful toolkit for managing any type of data. A lot of our most successful clients start out with a single use case, like headless CMS. And then once they realize the full potential of Directus, they quickly expand into other applications. But what if you could unlock that potential even faster? Who wouldn't like to ship new projects and features in half the normal amount of time?\u003C\u002Fp>\u003Cp>What developer doesn't love to make a huge impact for their clients, their team, their organization. That's what my team has been working to solve, and I'm super excited to introduce Directus Plus. Directus plus is a companion subscription for developers who love to ship. Doesn't matter if you're a freelancer, agency developer, or a staff engineer. You'll get a ton of value from Directus plus Here's what's inside.\u003C\u002Fp>\u003Cp>Powerful headless starter kits to help you ship faster. Starter Kits are done for you back ends for specific use cases you'll inevitably be asked to build or buy. Like learning management systems for your own custom courseware, product information management to manage your product catalogs and technical data, multi tenant SaaS, video streaming platforms like Directus TV, and more. These starter kits get you to 80% complete on day 1, saving you boatloads of time. And they're headless, so you get to bring your favorite front end framework.\u003C\u002Fp>\u003Cp>They're also designed to be fully composable, so you can use them together in the same project. That's not all. Directus Plus also includes advanced training and workshops to level up your skills. We'll be hosting deep dive workshops that are exclusively for Directus Plus members. Workshops will be taught by our own internal Directus experts, and, occasionally, we'll even be joined by our partners, hosting providers, front end framework experts, and more.\u003C\u002Fp>\u003Cp>You'll learn tons about how to leverage advanced features inside Directus, like flows, insights, and more to build real world use cases. For example, one of our first workshops is on connecting Directus with Stripe to accept payments. Directus plus members will receive a premium role and private channels within our Discord community and first dibs on cool new swag drops. As a launch thank you, we're offering $100 off the price for Directus Plus membership. So if you sign up now, you'll pay just $99 per year.\u003C\u002Fp>\u003Cp>Yep. That's it. You'll get starter kits to move faster, training to level up, and the pride that comes with supporting our project. To learn more and get started, go to directus.ioplus. Just fill out the short checkout form and you'll receive a private invite and instructions within moments.\u003C\u002Fp>\u003Cp>I can't wait to see you on the other side.\u003C\u002Fp>\u003Cp>Speaker 1: Thank you so much for that, Bryant. Today, we've covered so much. So let me provide a quick recap. We announced Directus 10.10 and this new phase of Directus that we're ushering in with the release of the Directus Marketplace beta. The spring slate of shows for Directus TV, the new payments hackathon, custom domains and auto scaling for Directus Cloud, a refreshed partner program, and, of course, Directus Plus.\u003C\u002Fp>\u003Cp>Now while the announcement portion of Leap Week is now completed, there is still so much more going on this week. So head over to our website to learn more about all the other events taking place. On behalf of the whole directors team, once again, thank you so much for being a part of this. And whether it is at one of our Leap Week events, whether it's at one of our in person user groups, or over in our Discord server, we'll see you around.\u003C\u002Fp>","Hello. Ben Haines, CEO and founder of Directus. It is actually 2024, and I originally started this software in 2004. So now 2 decades in, obviously, quite a bit has changed, and I think it's well overdue for us to go through and look at how we talk about Directus and how that has changed, over those those 2 decades. I'd like to do that through a quick 5 minute demo. Obviously, a lot to cover in 5 minutes, but let's see, if we can make that happen. So we're starting off right here on the login screen. Here we're already logged in, so we're just gonna hit continue. But I think the first thing to notice, is that the entire platform is white label. Everything here is themeable from the border radii to the colors to this, subtle animation on the right. But this is your login screen. Same thing for all these public pages. Of course, you'd have 2 factor authentication, SSO, anything else you might need here, for your your project. Let's go ahead and log in. Again, as a reminder, the way that Directus works is you point it at a database. I like to start on this screen, because this is just a table in your database called metrics. Nothing more straightforward than that. Instead of seeing, you know, UNIX time stamps and, you know, just really complex data, we're seeing it in an intuitive way. This is a table layout. Many different ways you can look at your data, but here we're seeing conditional styling with icons, nice relative time stamps, and nice colors. The full power of SQL is available here. We can of course do full text search, advanced filtering with logical grouping, everything you could do in SQL but in an in an intuitive no code way. Table views are pretty obvious. You can also come in and look at things through a media view. You could use a kanban view if you had, something where you're actually dragging things through some sort of process, and then keeping your your data organized like this. Of course, if you have geospatial data in your database, we support that as well. So whether it's submarine cables or devices on a map, all of this is available. You can quickly zoom in, drill into an item, click, and then go in and start editing that that data. We also have, contacts or any other type of data, but I think where we'll drill into the actual form is on this blog. So if we click on one of these items, in this case, multi orchestration cultivate infrastructure, lots of different interfaces for how we edit the data. You have time, daytime choosers. You have media selectors. You have this really great translation interface where you can actually go into a split view and manage multilingual content, etcetera. Every change that you make goes through our accountability system. So you can come over here and see all the revisions. These are the changes that were made by who and when. You can actually roll back and undo those features. Really, really important for data governance. We also have a full commenting system with mentions, emoji, and everything you'd expect, and you can actually share things. You can create a unique URL and share that out to people outside of this, this platform. So really powerful there as well. Moving on to a few of the other modules that we have. We have a user directory, which I'll pop into my user very quickly, just to showcase something that I think is pretty, pretty powerful. We can actually change the language to any of the 55 languages that we support and choose different themes, really tailoring it to the experience that I I need as a user. So as I come in, not only are we managing multilingual content, but the actual application can be translated as well. So that's a really important feature. I'm gonna change that back, to English. That is the language that I understand, and we'll continue on. Really, really powerful access control here within the user directory and no limits in terms of how many roles, or or users you have. Once you come into our asset management system, you can actually we scrape all of the, metadata. So I p t c, exif, all available, virtual folders, and you can, of course, come in here and do all of your edits and cropping. If you do that through the API, you actually have hundreds of different operations available. Insights, great for business intelligence, great for building out these different dashboards using aggregation, time series, anything that you can imagine. Very customizable. You just come in here, drag and drop these to whatever size you want. Lots of options available for, how are you setting your precision, your ranges, etcetera, the colors. So put that into a full screen mode, throw it on a, a second display, and you've got a really great dashboard available instantly. Once we go into settings, this is where the ad administrators are working. You can, of course, build your data model here or you can build it through the API or just define it right in SQL and allow it to cascade out through everything. Then you can go through and start setting your access control as we talked about. You know, you have managers. Based on the tables of your database, who can access what based on CRUD? Create, read, update, and delete. It's worth noting it's not just on and off, we have very granular rule based access control, within these different filters. Lots of other things to talk about including the theming and all these different options, all different extensions of our system. The last thing I'd like to quickly show is our flows. This is automation. This is where you can come in and say I wanna deploy an application when x happens. Those triggers can be a cron job every 15 minutes or Monday at midnight. You can also have it when content changes. Send out emails, to our different content authors for review. Lots of different operations you can choose from. And again, as with everything else in the system, it's all extensible and customizable. That's about as fast as we can run through the many, many features and capabilities of this platform. But for now, I'll hand it back over to Kevin. Thank you. Thank you, Ben, and welcome to the second Leap Week. We have a fantastic week in store. We're going to start today with all of our announcements and then throughout the rest of the week we have loads of other events including socials, panels, and workshops for you to enjoy. One of the reasons people love Directus is because of its extensibility. You can augment and enhance most parts of Directus by using extensions, and then combine extensions in interesting ways to compose a data platform that really makes sense for your project. We have app extensions like panels for directors insights, interfaces for the directors editor, layouts, and themes. We also have API and hybrid extensions like operations, endpoints, and hooks. Our community has built so many amazing extensions including the media AI bundle that lets you extract information from images in Directus files, computed interfaces that change the way that data is displayed after applying logic to them, and, of course, the Director's Copilot that lets you have contextual conversations about your data with an AI bot in order to unlock new insights. Agency partners use extensions like Pixsy Labs based in London. They built a blur hash extension which allows them to nicely load in very large images for an online art gallery. And we have other customers use extensions for things like integrating with translation services, transforming videos, build automation, and more. Having this rich framework to build extensions is awesome. The missing bit has always been how to publish and reuse those extensions between projects. Today we are announcing something that I could not be more excited about, something which Directus users have been asking for for years. I'm gonna hand over to Rike to tell you more. This is the Directless marketplace. It is in beta for now for this first release, so we do really want your feedback to make it even better. The focus of this first iteration is really about discovery and installation. I'm gonna show you each of those here. So in settings, on the left hand sidebar, there's a new section for the marketplace. In this marketplace, we have a listing of all of the extensions that we've been able to find on npm. These extensions contain every single type of extension that you know from before. So those are interfaces, modules, panels, teams, etc. When you install an extension, you'd find those in the same places where you'd find them normally. So for interfaces, you'd find it in data model, modules show up in the sidebar, themes, appearance, etcetera. For any of those types, you can find them by, you know, current popularity, which ones were most recently published, or which ones are downloaded the most. In this case, let me go see if we can install a theme. So I'm gonna look up, you know, with the search, gonna find the theme that I'm looking for. For each individual extension, we're pulling in the readme file from your GitHub repo if it's connected, and we're pulling in the author and maintainers as provided by npm. Same goes for an author. So in this particular case, because Kevin has his GitHub linked to his NPM, we can show, you know, the about information from his GitHub profile, together with an avatar, and all of the extensions that he has uploaded so far. To install the theme, I could just click the install button, and there it goes. So now that this theme is installed, I can find it under appearance like I'm with any other theme. There it is. And there we go. To manage extensions you have installed, you can go to the extensions tab as you would for extensions before, where you can now both disable and uninstall them again. In the future, there's a couple of additional features that we know we want to add, starting with verified authors, verified packages, allowing, you know, the end user to know which one is gonna be trusted. But, also, we're looking to see if we can open up monetization in the marketplace. So this would allow you to sell extensions that you have built to others, in the ecosystem. That will be in the future release. In the meantime, we absolutely hope you enjoy this. Please do leave all and every bit of feedback you have in the Discord channel marketplace beta. So the marketplace. Cool. Right? And the thing that excites me most is the marketplace is gonna be available in every Director's project. So whether you're self hosting or you're using Director's Cloud, you will now be able to install extensions via the marketplace. Now, the marketplace is part of director version 10.10, but it isn't all. And we've had a few releases since the last week. So I wanted to kick it over to some of our engineers to tell you about the new and notable features that have shipped since the last Leap Week. In Directus 10.8, we released a powerful new theming engine. Previously, you were limited to adding your own CSS on top of the data studio to make yours. You now have the flexibility to customize colors, fonts, spacing, and much more across Directus using our new theme interface. Being able to white label Directus has always been important. Bringing your brand and aesthetic to the forefront really make Directus feel like your own tool. Building on the excitement of our marketplace announcement, you can now develop and share themes as an extension. Now you can do more than just make direct us your own. You can share your designs and help others get creative too. We're excited to see the themes you bring to the marketplace. Thank you all for joining me in this exciting chapter. Your vision, your directness, ready to dazzle. The canvas is fast. The palette is rich. And the possibilities, endless. As well as managing databases, Directus also helps you manage your files. As part of that, we offer media transformations via URL. Here's an image of 2 people on a beach. Notice that they're slightly off center. Which is fine, until you start to crop the image via your app. Here the image is being cropped off from the center, but these two people are being chopped off. In Directus 10.9, we introduced focal points. You can now specify the point for Directus treat as the center. You can do this via the data studio or the files API. Now the subjects that matter in your images can always be visible. You can combine this with our automation features to determine what the point of interest should be. We hope you enjoy working with focal points, a small change with big impact. Before, when requesting content versions, this is the data structure, the data API return. And with relational fields, note that there is a create, update, delete array for each item. This is correct. It is what will happen when this version gets promoted. But it's different to what you may expect from an item when not using content version or using the main version. This does made it hard to implement live preview as you had to account for 2 different data structures. In directors 10.10, we will store this data structure. But when you request a content version, we will standardize the data structure before returning it. So the return data is now the outcome if you were to promote the item. Here, we have an example of both a one to many and the many to many relationship. In the one to many comments example, we are both updating an item and deleting related comments. The new output doesn't contain the deleted comment and returns the full item including any changes made in the content version. Likewise, in the many to many page blocks example, a block is being created and another deleted. The return data from a content version is now the same structure you expect including the outcome of any changes. Now you can implement live preview with a standard and predictable data structure. Thank you very much for that rundown, and we hope you enjoy using theming, focal points, and our enhancements to content versioning. And, of course, this is just a tiny sliver of the features which we have included in our recent releases. But for today, there's plenty more announcements to come. Let's rock and roll. Let's get started with Directus and Nuxt. You don't hire smart people to tell them what to do. You hire smart people to figure out your problems. Can you build Netflix in an hour? Not terribly elegant, but we're just gonna throw a new error. Because even pirates, sometimes they're they're not pirating the way you want a pirate. Keep it simple. Won't be complicated. Hey. I enjoy what I do. I've got my pen now. I'm serious. We're gonna be cutting this one really, really close. Let's talk about Directus TV. It's a new streaming platform that we released at the end of last year with hours of content for you to enjoy and I wanna tell you about some of our series. In a 100 apps in a 100 hours, Bryant takes a common application idea and tries to build it in just 60 minutes. Sometimes he succeeds, sometimes he doesn't, and honestly it's a bit of a stressful watch but it's always a lot of fun. In Trace Talks, John and Pedro interview engineering leaders about their journeys. It's full of interesting insights and anecdotes for all stages of your career. And in quick connect, we show you how to integrate with third party platforms to build some interesting and useful automations. Today we're announcing some new shows that are going to land on your screen this spring. In what's in your doc, we discovered the toolkit behind really productive people. I don't know about you but whenever someone shares their screen I always look at all of the apps they're running in the hope that I can find something new that will somehow enrich my life. So, we've invited 5 guests to ask them about the software, hardware, and analog tools that they use to run their lives. I had a blast recording it and I know you're gonna have a blast watching it. In the joy of theming, we invite Bry Ross who is definitely not just Bryon in a wig to highlight the power behind the director's theming engine by creating nice new themes that pay homage to brands you know and love. There are only happy little accidents in this series and they're lovely lovely peaceful episodes. Battlesnake is a popular competitive game where your code is the controller and in Ready Set Battlesnake Andrew joins me to build a snake inside of Directus. Honestly, we can't believe it worked not only that it worked but it worked as well as it did, join us to find out how. With great power can come great complexity. In short hops, we share quick tips and tricks to help you get the most from directors. And the best bit is this series is based directly on community feedback. Today we're also announcing some show renewals so you can look forward to new episodes of both the 100 Apps in a 100 Hours and Dev Thoughts. Now many of you have also been asking about how directors TV is actually put together. So today we're also releasing in full a new series called Digging the Rabbit Hole where we're going to cover everything Directus TV from the conception and inspiration behind the platform, the technical setup, the life cycle of a show from pitch through to release, and some of the early feedback. We hope you enjoy all of these new shows that are landing this spring, and, of course, you can watch digging the rabbit hole in full today. Directors TV really is a community project. We're building shows for you to enjoy, taking in your feedback, and then deciding what new shows we can create or shows we can renew based on that feedback. Well, our community does so much more, and I wanna hand over to Jonathan to talk more about it. Hey, everyone. The Directus community is the foundation, DNA, essence, source of strength, the font of magic, you might say, that makes up our team and makes us excited to come to work every day. We're so thrilled to have such a great community. I get the opportunity to interact with the community on a daily basis and that community interaction. I learned something new each time, and that includes anything from a feature request to a support ticket, to presales activities, talking to new clients, people that have used direct us for years to people that are brand new to the direct us. And I get that opportunity to interact with everyone, on a daily basis. And it is it it literally makes me excited to come to work every day. We do some other community actions and activities that we do our request reviews. We do those every 2 weeks. You get an opportunity to see our processes, how we go through feature requests, and we get that our we get that live interaction with community members. Hope to see you there. It's very, very fun and exciting to see that. And then from that explosion of ideas, we narrow that back down to what is possible. Some other things we do some in person activities these days. We'd still do some live interactions for Lynn, London, New York. There's a variety of others. You can find those out on the website and see that. And with that, I get to tell you about that newest hackathon. I'm so excited. The hackathons have brought new use cases, new capabilities to exist to the previous hackathons. We've seen and learned some really cool things about what people the concepts and ideas that someone might come up with. And so this particular hackathon is gonna be focused around one of our biggest, up boats kind of in the community is around payments. So we'd like to see the directest payments hackathon. We'd like to see you build and publish in the new marketplace. I'm so excited about the marketplace. Think through the scenarios and the use cases. You know, how would I deliver coupons? How would I deliver trials? What happens when payments fail? What happens when deployments fail or some issue happens during that process? Make sure you've got handling logic around those kinds of things. Include reporting include any, extensions or, data details that are valuable that you find in your day to day interactions, with payment platforms and how you want that to operate. You'll have until the end of March to submit that you'll find all of the details around prizes and information about how and when and what to submit, on directus. Io\u002Fhackathons, and you'll find links throughout the the documentation and things that we'll share through the coming days. With that said, I get the opportunity to hand off to mister Matt Miner to talk more about our community. Cheers. Hey. How's it going? I'm Matt on the marketing team here at Directus. Data discussions really aren't the stuff of legend. In fact, when we put this survey together, at first, there was a couple, like, raised eyebrows, like, data, really? Is that what we wanna do? What do you know? A month later, we had a huge flood of responses. 782 developers told us how they're using data in their projects. That tells us that, you know, data isn't dull. There's just nobody talking about it in the right way. We're gonna make data exciting again. Here are the 6 things that we learned that we thought were really interesting, or 2 of these actually really shocked us when we found out. Number 1 is that Postgres leads the pack. It is the choice of over 63% of the developers we surveyed, that they're using in their projects currently. The number 2 thing, slightly related, but of all the database types when developers are starting new projects, over 57% of them said that they prefer relational databases all over over all other types. Even asked our CTO, Wrike, about it. And he said, you know, the most likely reason is that a vast majority of data applications have some sort of schema consistency need for type safety and expectability. In NoSQL, you can quickly end up effectively reimplementing a lot of things that a SQL database already offers out of the box. The number 3 thing we found that was really interesting to us was that developers are leaning towards Cloud Hosted Database Solutions. So by a wide margin too. Cloud leads, 68% of respondents prefer hosting with solutions like AWS or Azure, versus, you know, managed database services like DigitalOcean or on prem servers. This one, I think, was so shocking because it was so lopsided. 94% of our survey respondents said they prefer working with REST APIs as opposed to GraphQL. We kind of went into this thinking that cost was gonna be the primary factor, to consider when hosting databases. And in fact, it is a consideration, but it's not the most important. I think what this tells us is that developers are prioritizing performance and reliability and ease of use over how much it costs, which is still a consideration, but not what's driving, decisions at the end of the day. Taking this a step further, actually, we asked what the primary factors they think hosting should be charging for. And the top 3 in order were, machine resource utilization, so like RAM and CPU, storage space, and actually number of read and write options, which was interesting. The least popular way to price hosting, was by charging on the number of database records. So just an interesting little finding there. But, yeah. Number 6 was the most shocking thing we found. Over 64% of respondents said that they're using AI for cogeneration. So despite all of this, like loud people in the room talking about, like, AI is bad for development, I think there's a lot of people quietly actually using this to become better at their job and learning how to implement and the things that they're doing. Actually, going a little bit deeper on this, we asked 2 questions. If AI was too risky for data analysis to influence decision making, on average, sentiment was neutral. But when it comes to using AI to actually interact with the database with, like, CRUD permissions, overwhelming sentiment was, like, please do not do that. That is way too risky, which makes sense. So those are the 6 big takeaways, learnings we add just from a surface evaluation of the results that we got for in the survey. Hopefully, these were interesting to you as they were to us. And again, like, data isn't boring. It's just how are people using it? How are we framing it? Hopefully, we can get some more discussions around this, because we're data nerds at the end of the day. And what we're gonna do is basically put together a series that we call Data Drops, where we're gonna release an article, with, like, a really interesting insight. Because it's one thing to know what the trends are, but we think it's more important to understand, like, why they vary from a startup in Silicon Valley as opposed to a seasoned freelancer in Berlin. So if you wanna get up to date on those and get nice little nuggets in your inbox, make sure you sign up for a newsletter. It goes out once a month. You can sign up in our docs. You can sign up on our, main blog. But we'd love to have you sign up and just get these once a month newsletters. Appreciate the time today, and, have a good one. We hope you're enjoying today's announcement so far. They've included the release of Directus 10.10 with the Directus Marketplace beta and improvements to content versioning, our new shows on Directus TV, and, of course, our new hackathon. But we're not quite done yet. I want to kick it over to our director of engineering, Alex, to talk about what's new for Directus Cloud. Directus really is a solution for everyone. From side projects and small businesses right through to huge organization like Kia, Copa Airlines, Walmart, and T Mobile. Our Enterprise Cloud offers dedicated infrastructure from the team who build directors. It's perfect for mission critical project with higher security and support needs. Today, we are proud to be expanding the Enterprise Cloud offering with key features requested by our customers. Over the last few months, our team has been working on improving our infrastructure in a number of areas including security, performances, and flexibility. With this in mind, we are not able to offer auto scaling infrastructure, which means even with sudden peak in traffic, your georectus project stays strong. Auto scaling is enabled by default on all our new projects, both on Enterprise and Professional Cloud. We love speaking to our customers, but you should also need to speak to us less. Up until now, if you want to set up a custom domain for an enterprise project, you have to get in touch with us. Now it's over. Self-service custom domains are now available through the Cloud Dashboard for Enterprise Cloud Projects. We are looking forward to chatting with you more, but not about Guest of the Vans. We have more announcements to come. Over to Colton for now. Today, we're announcing our new partner program focused on agencies. Agency partners are a super important part of how we reach our customers. We've designed this program in conjunction with our current partners, and the program will enable agencies and client projects of all shapes and sizes, and we think you're gonna love it. We now have a formal certification program to help you get the most from Directus and understand which projects it is suitable for. And, of course, there will also be a larger resource center available which can help you in discussing direct us with your customers. Our team is available for advisory calls to validate your approaches, suggest new ones, and sell alongside your team. And of course, the program includes revenue sharing both upfront and renewals. The new partner directory will help companies looking for experts to implement their projects. And with co marketing opportunities, we can help share all your stories. This isn't all, but it captures the most important parts of the partner program. We really think you're gonna love it. You can find out more at directus. Io \u002F partners. And later this year, we'll also be expanding our program to include technical partners, but that's for another day. Back to you, Kevin. It's been a wonderful day. And on behalf of the whole Director's core team, thank you so much for tuning in, whether live or on demand on Director's TV. Now before we head off, we have one more thing to announce. Bryant? While headless CMS is one of our most popular use cases, Directus is a super powerful toolkit for managing any type of data. A lot of our most successful clients start out with a single use case, like headless CMS. And then once they realize the full potential of Directus, they quickly expand into other applications. But what if you could unlock that potential even faster? Who wouldn't like to ship new projects and features in half the normal amount of time? What developer doesn't love to make a huge impact for their clients, their team, their organization. That's what my team has been working to solve, and I'm super excited to introduce Directus Plus. Directus plus is a companion subscription for developers who love to ship. Doesn't matter if you're a freelancer, agency developer, or a staff engineer. You'll get a ton of value from Directus plus Here's what's inside. Powerful headless starter kits to help you ship faster. Starter Kits are done for you back ends for specific use cases you'll inevitably be asked to build or buy. Like learning management systems for your own custom courseware, product information management to manage your product catalogs and technical data, multi tenant SaaS, video streaming platforms like Directus TV, and more. These starter kits get you to 80% complete on day 1, saving you boatloads of time. And they're headless, so you get to bring your favorite front end framework. They're also designed to be fully composable, so you can use them together in the same project. That's not all. Directus Plus also includes advanced training and workshops to level up your skills. We'll be hosting deep dive workshops that are exclusively for Directus Plus members. Workshops will be taught by our own internal Directus experts, and, occasionally, we'll even be joined by our partners, hosting providers, front end framework experts, and more. You'll learn tons about how to leverage advanced features inside Directus, like flows, insights, and more to build real world use cases. For example, one of our first workshops is on connecting Directus with Stripe to accept payments. Directus plus members will receive a premium role and private channels within our Discord community and first dibs on cool new swag drops. As a launch thank you, we're offering $100 off the price for Directus Plus membership. So if you sign up now, you'll pay just $99 per year. Yep. That's it. You'll get starter kits to move faster, training to level up, and the pride that comes with supporting our project. To learn more and get started, go to directus.ioplus. Just fill out the short checkout form and you'll receive a private invite and instructions within moments. I can't wait to see you on the other side. Thank you so much for that, Bryant. Today, we've covered so much. So let me provide a quick recap. We announced Directus 10.10 and this new phase of Directus that we're ushering in with the release of the Directus Marketplace beta. The spring slate of shows for Directus TV, the new payments hackathon, custom domains and auto scaling for Directus Cloud, a refreshed partner program, and, of course, Directus Plus. Now while the announcement portion of Leap Week is now completed, there is still so much more going on this week. So head over to our website to learn more about all the other events taking place. On behalf of the whole directors team, once again, thank you so much for being a part of this. And whether it is at one of our Leap Week events, whether it's at one of our in person user groups, or over in our Discord server, we'll see you around.",[],[],{"year":978,"number":2280,"id":4209,"episodes":4263,"show":4264},[4211,4212,4213,4214,4215,4216,4192,4217,4218],{"title":151,"slug":152},{"id":4213,"slug":4266,"vimeo_id":4267,"description":4268,"tile":4269,"length":159,"resources":12,"people":4270,"episode_number":264,"published":4201,"title":4278,"video_transcript_html":4279,"video_transcript_text":4280,"content":12,"seo":12,"status":19,"episode_people":4281,"recommendations":4285,"season":4286},"03-since-last-time","919062176","Find out about key features in Directus 10.8, 10.9, and 10.10.","9de9f6b4-b867-4356-9f5f-73aa7ac9528b",[4271,4274,4275],{"name":4272,"url":4273},"Connor Winston","https:\u002F\u002Fdirectus.io\u002Fteam\u002Fconnor-winston",{"name":3801,"url":3802},{"name":4276,"url":4277},"Azri Kahar","https:\u002F\u002Fdirectus.io\u002Fteam\u002Fazri-kahar","What's New Since the Last Leap Week","\u003Cp>Speaker 0: In Directus 10.8 we released a powerful new theming engine. Previously you were limited to adding your own CSS on top of the data studio to make yours. You now have the flexibility to customize colors, fonts, spacing, and much more across Directus using our new theme interface. Being able to white label Directus has always been important. Bringing your brand and aesthetic to the forefront really make Directus feel like your own tool.\u003C\u002Fp>\u003Cp>Building on the excitement of our marketplace announcement, you can now develop and share themes as an extension. Now you can do more than just make direct us your own. You can share your designs and help others get creative too. We're excited to see the themes you bring to the marketplace. Thank you all for joining me in this exciting chapter.\u003C\u002Fp>\u003Cp>Your vision, your directness, ready to dazzle. The canvas is fast. The palette is rich, and the possibilities, endless.\u003C\u002Fp>\u003Cp>Speaker 1: As well as managing databases, Directus also helps you manage your files. As part of that, we offer media transformations via URL. Here's an image of 2 people on a beach. Notice that they're slightly off center, which is fine, until you start to crop the image via your app. Here the image is being cropped off from the center, but these two people are being chopped off.\u003C\u002Fp>\u003Cp>In Directus 10.9, we introduced focal points. You can now specify the point for Directus to treat as the center. You can do this via the data studio or the files API. Now, the subjects that matter in your images can always be visible. You can combine this with our automation features to determine what the point of interest should be.\u003C\u002Fp>\u003Cp>We hope you enjoy working with focal points, a small change with big impact.\u003C\u002Fp>\u003Cp>Speaker 2: Before when requesting content versions, this is the data structure the data API return. And with relational fields, note that there is a create, update, delete array for each item. This is correct. It is what will happen when this version gets promoted. But it's different to what you may expect from an item when not using content version or using the main version.\u003C\u002Fp>\u003Cp>This does made it hard to implement live preview as you had to account for 2 different data structures. In directors 10.10, we will store this data structure. But when you request a content version, we will standardize the data structure before returning it. So the return data is now the outcome if you were to promote the item. Here, we have an example of both a one to many and a many to many relationship.\u003C\u002Fp>\u003Cp>In the one to many comments example, we are both updating an item and deleting related comments. The new output doesn't contain the deleted comment and returns the full item including any changes made in the content version. Likewise, in the many to many page blocks example, a block is being created and another deleted. The return data from a content version is now the same structure you expect, including the outcome of any changes. Now you can implement live preview with a standard and predictable data structure.\u003C\u002Fp>","In Directus 10.8 we released a powerful new theming engine. Previously you were limited to adding your own CSS on top of the data studio to make yours. You now have the flexibility to customize colors, fonts, spacing, and much more across Directus using our new theme interface. Being able to white label Directus has always been important. Bringing your brand and aesthetic to the forefront really make Directus feel like your own tool. Building on the excitement of our marketplace announcement, you can now develop and share themes as an extension. Now you can do more than just make direct us your own. You can share your designs and help others get creative too. We're excited to see the themes you bring to the marketplace. Thank you all for joining me in this exciting chapter. Your vision, your directness, ready to dazzle. The canvas is fast. The palette is rich, and the possibilities, endless. As well as managing databases, Directus also helps you manage your files. As part of that, we offer media transformations via URL. Here's an image of 2 people on a beach. Notice that they're slightly off center, which is fine, until you start to crop the image via your app. Here the image is being cropped off from the center, but these two people are being chopped off. In Directus 10.9, we introduced focal points. You can now specify the point for Directus to treat as the center. You can do this via the data studio or the files API. Now, the subjects that matter in your images can always be visible. You can combine this with our automation features to determine what the point of interest should be. We hope you enjoy working with focal points, a small change with big impact. Before when requesting content versions, this is the data structure the data API return. And with relational fields, note that there is a create, update, delete array for each item. This is correct. It is what will happen when this version gets promoted. But it's different to what you may expect from an item when not using content version or using the main version. This does made it hard to implement live preview as you had to account for 2 different data structures. In directors 10.10, we will store this data structure. But when you request a content version, we will standardize the data structure before returning it. So the return data is now the outcome if you were to promote the item. Here, we have an example of both a one to many and a many to many relationship. In the one to many comments example, we are both updating an item and deleting related comments. The new output doesn't contain the deleted comment and returns the full item including any changes made in the content version. Likewise, in the many to many page blocks example, a block is being created and another deleted. The return data from a content version is now the same structure you expect, including the outcome of any changes. Now you can implement live preview with a standard and predictable data structure.",[4282,4283,4284],"ec4b302f-e035-4194-a6e6-3745310e9389","72dd7514-1fd4-4331-adcd-5aa9a205eccd","69592d13-a025-4716-9703-ab3a3a67d787",[],{"year":978,"number":2280,"id":4209,"episodes":4287,"show":4288},[4211,4212,4213,4214,4215,4216,4192,4217,4218],{"title":151,"slug":152},{"id":4218,"slug":4290,"vimeo_id":4291,"description":4292,"tile":4293,"length":264,"resources":12,"people":4294,"episode_number":527,"published":4201,"title":4296,"video_transcript_html":4297,"video_transcript_text":4298,"content":12,"seo":12,"status":19,"episode_people":4299,"recommendations":4301,"season":4302},"02-plus","918864581","Announcing our subscription service crafted to enhance your Directus experience to the maximum.","c29dc9f5-2370-4a7f-a31a-f64c6c5fd640",[4295],{"name":3590,"url":3591},"Level Up With Directus+","\u003Cp>Speaker 0: While headless CMS is one of our most popular use cases, Directus is a super powerful toolkit for managing any type of data. A lot of our most successful clients start out with a single use case like headless CMS, and then once they realize the full potential of Directus, they quickly expand into other applications. But what if you could unlock that potential even faster? Who wouldn't like to ship new projects and features in half the normal amount of time? What developer doesn't love to make a huge impact for their clients, their team, their organization.\u003C\u002Fp>\u003Cp>That's what my team has been working to solve, and I'm super excited to introduce Directus Plus. Directus Plus is a companion subscription for developers who love to ship. It doesn't matter if you're a freelancer, agency developer, or a staff engineer. You'll get a ton of value from Directus plus. Here's what's inside.\u003C\u002Fp>\u003Cp>Powerful headless starter kits to help you ship faster. Starter kits are done for you back ends for specific use cases you'll inevitably be asked to build or buy, like learning management systems for your own custom courseware, product information management to manage your product catalogs and technical data, multi tenant SaaS, video streaming platforms like Directus TV, and more. These starter kits get you to 80% complete on day 1, saving you boatloads of time. And they're headless, so you get to bring your favorite front end framework. They're also designed to be fully composable, so you can use them together in the same project.\u003C\u002Fp>\u003Cp>That's not all. Directus plus also includes advanced training and workshops to level up your skills. We'll be hosting deep dive workshops that are exclusively for Directus Plus members. Workshops will be taught by our own internal Directus experts, and, occasionally, we'll even be joined by our partners, hosting providers, front end framework experts, and more. You'll learn tons about how to leverage advanced features inside Directus like flows, insights, and more to build real world use cases.\u003C\u002Fp>\u003Cp>For example, one of our first workshops is on connecting Directus with Stripe to accept payments. Directus plus members will receive a premium role and private channels within our Discord community and first dibs on cool new swag drops. As a launch thank you, we're offering $100 off the price for directus plus membership. So if you sign up now, you'll pay just $99 per year. Yep.\u003C\u002Fp>\u003Cp>That's it. You'll get starter kits to move faster, training to level up, and the pride that comes with supporting our project. To learn more and get started, go to directus.i0\u002Fplus. Just fill out the short checkout form, and you'll receive a private invite and instructions within moments. I can't wait to see you on the other side.\u003C\u002Fp>","While headless CMS is one of our most popular use cases, Directus is a super powerful toolkit for managing any type of data. A lot of our most successful clients start out with a single use case like headless CMS, and then once they realize the full potential of Directus, they quickly expand into other applications. But what if you could unlock that potential even faster? Who wouldn't like to ship new projects and features in half the normal amount of time? What developer doesn't love to make a huge impact for their clients, their team, their organization. That's what my team has been working to solve, and I'm super excited to introduce Directus Plus. Directus Plus is a companion subscription for developers who love to ship. It doesn't matter if you're a freelancer, agency developer, or a staff engineer. You'll get a ton of value from Directus plus. Here's what's inside. Powerful headless starter kits to help you ship faster. Starter kits are done for you back ends for specific use cases you'll inevitably be asked to build or buy, like learning management systems for your own custom courseware, product information management to manage your product catalogs and technical data, multi tenant SaaS, video streaming platforms like Directus TV, and more. These starter kits get you to 80% complete on day 1, saving you boatloads of time. And they're headless, so you get to bring your favorite front end framework. They're also designed to be fully composable, so you can use them together in the same project. That's not all. Directus plus also includes advanced training and workshops to level up your skills. We'll be hosting deep dive workshops that are exclusively for Directus Plus members. Workshops will be taught by our own internal Directus experts, and, occasionally, we'll even be joined by our partners, hosting providers, front end framework experts, and more. You'll learn tons about how to leverage advanced features inside Directus like flows, insights, and more to build real world use cases. For example, one of our first workshops is on connecting Directus with Stripe to accept payments. Directus plus members will receive a premium role and private channels within our Discord community and first dibs on cool new swag drops. As a launch thank you, we're offering $100 off the price for directus plus membership. So if you sign up now, you'll pay just $99 per year. Yep. That's it. You'll get starter kits to move faster, training to level up, and the pride that comes with supporting our project. To learn more and get started, go to directus.i0\u002Fplus. Just fill out the short checkout form, and you'll receive a private invite and instructions within moments. I can't wait to see you on the other side.",[4300],"2f5c0f2e-926d-4d73-b1e7-abb12d9de47a",[],{"year":978,"number":2280,"id":4209,"episodes":4303,"show":4304},[4211,4212,4213,4214,4215,4216,4192,4217,4218],{"title":151,"slug":152},{"id":4306,"slug":4307,"vimeo_id":4308,"description":4309,"tile":4310,"length":213,"resources":12,"people":4311,"episode_number":13,"published":4201,"title":4313,"video_transcript_html":4314,"video_transcript_text":4315,"content":12,"seo":12,"status":19,"episode_people":4316,"recommendations":4318,"season":4319},"92c40dc4-5411-493b-9bc0-83450a812001","ceo-5","918871399","Join our CEO Ben Haynes for a rapid 5 minutes demo of the Directus platform.","5c472b06-609e-4e19-af68-6923e63d4043",[4312],{"name":4012,"url":4013},"5 Minute Demo From Our CEO","\u003Cp>Speaker 0: We're starting off right here on the login screen. Here we're already logged in, so we're just gonna hit continue. But I think the first thing to notice, is that the entire platform is white label. Everything here is themeable from the border radii to the colors to this, subtle animation on the right. But this is your login screen.\u003C\u002Fp>\u003Cp>Same thing for all these public pages. Of course, you'd have 2 factor authentication, SSO, anything else you might need here, for your your project. Let's go ahead and log in. Right. Again as a reminder, the way that Directus works is you point it at a database.\u003C\u002Fp>\u003Cp>I like to start on this screen, because this is just a table in your database called metrics. Nothing more straightforward than that. Instead of seeing, you know, UNIX time stamps and, you know, just really complex data, we're seeing it in an intuitive way. This is a table layout. Many different ways you can look at your data.\u003C\u002Fp>\u003Cp>But here we're seeing conditional styling with icons, nice relative time stamps, and nice colors. The full power of SQL is available here. We can of course do full text search, advanced filtering with logical grouping, everything you could do in SQL but in an in an intuitive no code way. Table views are pretty obvious. You can also come in and look at things through a media view.\u003C\u002Fp>\u003Cp>You could use a Kanban view if you had, something where you're actually dragging things through some sort of process, and then keeping your your data organized like this. Of course, if you have geospatial data in your database, we support that as well. So whether it's submarine cables or devices on a map, all of this is available. You can quickly zoom in, drill into an item, click, and then go in and start editing that that data. We also have, contacts or any other type of data, but I think where we'll drill into the actual form is on this blog.\u003C\u002Fp>\u003Cp>So if we click on one of these items, in this case, multi orchestration cultivate infrastructure, lots of different interfaces for how we edit the data. You have time, daytime choosers. You have media selectors. You have this really great translation interface where you can actually go into a split view and manage multilingual content, etcetera. Every change that you make goes through our accountability system.\u003C\u002Fp>\u003Cp>So you can come over here and see all the revisions. These are the changes that were made by whom and when. You can actually roll back and undo those features. Really, really important for data governance. We also have a full commenting system with mentions, emoji, and everything you'd expect.\u003C\u002Fp>\u003Cp>And you can actually share things. You can create a unique URL and share that out to people outside of this, this platform. So really powerful there as well. Moving on to a few of the other modules that we have. We have a user directory, which I'll pop into my user very quickly, just to showcase something that I think is pretty, pretty powerful.\u003C\u002Fp>\u003Cp>We can actually change the language to any of the 55 languages that we support and choose different themes, really tailoring it to the experience that I I need as a user. So as I come in, not only are we managing multilingual content, but the actual application can be translated as well. So that's a really important feature. I'm gonna change that back, to English. That is the language that I understand, and we'll continue on.\u003C\u002Fp>\u003Cp>Really, really powerful access control here within the user directory and no limits in terms of how many roles or or users you have. Once you come into our asset management system, you can actually we scrape all of the, metadata. So I p t c, EXIF, all available, virtual folders, and you can, of course, come in here and do all of your edits and cropping. If you do that through the API, you actually have hundreds of different operations available. Insights, great for business intelligence, great for building out these different dashboards using aggregation, time series, anything that you can imagine.\u003C\u002Fp>\u003Cp>Very customizable. You just come in here, drag and drop these to whatever size you want. Lots of options available for, how are you setting your precision, your ranges, etcetera, the colors. So put that into a full screen mode, throw it on a, a second display, and you've got a really great dashboard available instantly. Once we go into settings, this is where the ad administrators are working.\u003C\u002Fp>\u003Cp>You can of course build your data model here, or you can build it through the API, or just define it right in SQL and allow it to cascade out through everything. Then you can go through and start setting your access control, as we talked about. You know, you have managers. Based on the tables of your database, who can access what based on CRUD? Create, read, update, and delete.\u003C\u002Fp>\u003Cp>It's worth noting. It's not just on and off. We have very granular rule based access control, within these different filters. Lots of other things to talk about including the theming and all these different options, all different extensions of our system. The last thing I'd like to quickly show is our flows.\u003C\u002Fp>\u003Cp>This is automation. This is where you can come in and say, I wanna deploy an application when x happens. Those triggers can be a cron job every 15 minutes or Monday at midnight. You can also have it when content changes. Send out emails, to our different content authors for review.\u003C\u002Fp>\u003Cp>Lots of different operations you can choose from. And again, as with everything else in the system, it's all extensible and customizable.\u003C\u002Fp>","We're starting off right here on the login screen. Here we're already logged in, so we're just gonna hit continue. But I think the first thing to notice, is that the entire platform is white label. Everything here is themeable from the border radii to the colors to this, subtle animation on the right. But this is your login screen. Same thing for all these public pages. Of course, you'd have 2 factor authentication, SSO, anything else you might need here, for your your project. Let's go ahead and log in. Right. Again as a reminder, the way that Directus works is you point it at a database. I like to start on this screen, because this is just a table in your database called metrics. Nothing more straightforward than that. Instead of seeing, you know, UNIX time stamps and, you know, just really complex data, we're seeing it in an intuitive way. This is a table layout. Many different ways you can look at your data. But here we're seeing conditional styling with icons, nice relative time stamps, and nice colors. The full power of SQL is available here. We can of course do full text search, advanced filtering with logical grouping, everything you could do in SQL but in an in an intuitive no code way. Table views are pretty obvious. You can also come in and look at things through a media view. You could use a Kanban view if you had, something where you're actually dragging things through some sort of process, and then keeping your your data organized like this. Of course, if you have geospatial data in your database, we support that as well. So whether it's submarine cables or devices on a map, all of this is available. You can quickly zoom in, drill into an item, click, and then go in and start editing that that data. We also have, contacts or any other type of data, but I think where we'll drill into the actual form is on this blog. So if we click on one of these items, in this case, multi orchestration cultivate infrastructure, lots of different interfaces for how we edit the data. You have time, daytime choosers. You have media selectors. You have this really great translation interface where you can actually go into a split view and manage multilingual content, etcetera. Every change that you make goes through our accountability system. So you can come over here and see all the revisions. These are the changes that were made by whom and when. You can actually roll back and undo those features. Really, really important for data governance. We also have a full commenting system with mentions, emoji, and everything you'd expect. And you can actually share things. You can create a unique URL and share that out to people outside of this, this platform. So really powerful there as well. Moving on to a few of the other modules that we have. We have a user directory, which I'll pop into my user very quickly, just to showcase something that I think is pretty, pretty powerful. We can actually change the language to any of the 55 languages that we support and choose different themes, really tailoring it to the experience that I I need as a user. So as I come in, not only are we managing multilingual content, but the actual application can be translated as well. So that's a really important feature. I'm gonna change that back, to English. That is the language that I understand, and we'll continue on. Really, really powerful access control here within the user directory and no limits in terms of how many roles or or users you have. Once you come into our asset management system, you can actually we scrape all of the, metadata. So I p t c, EXIF, all available, virtual folders, and you can, of course, come in here and do all of your edits and cropping. If you do that through the API, you actually have hundreds of different operations available. Insights, great for business intelligence, great for building out these different dashboards using aggregation, time series, anything that you can imagine. Very customizable. You just come in here, drag and drop these to whatever size you want. Lots of options available for, how are you setting your precision, your ranges, etcetera, the colors. So put that into a full screen mode, throw it on a, a second display, and you've got a really great dashboard available instantly. Once we go into settings, this is where the ad administrators are working. You can of course build your data model here, or you can build it through the API, or just define it right in SQL and allow it to cascade out through everything. Then you can go through and start setting your access control, as we talked about. You know, you have managers. Based on the tables of your database, who can access what based on CRUD? Create, read, update, and delete. It's worth noting. It's not just on and off. We have very granular rule based access control, within these different filters. Lots of other things to talk about including the theming and all these different options, all different extensions of our system. The last thing I'd like to quickly show is our flows. This is automation. This is where you can come in and say, I wanna deploy an application when x happens. Those triggers can be a cron job every 15 minutes or Monday at midnight. You can also have it when content changes. Send out emails, to our different content authors for review. Lots of different operations you can choose from. And again, as with everything else in the system, it's all extensible and customizable.",[4317],"ffdef3c2-aae8-4c5e-b56a-a7337715d018",[],{"year":978,"number":4320,"id":4321,"episodes":4322,"show":4323},35,"42516ee5-77f6-475d-bfc3-ef90f77f86e8",[4306],{"title":4324,"slug":4325},"Discover Directus","discover-directus",{"id":4327,"slug":4328,"vimeo_id":4329,"description":4330,"tile":4331,"length":247,"resources":12,"people":4332,"episode_number":13,"published":4201,"title":4335,"video_transcript_html":4336,"video_transcript_text":4337,"content":12,"seo":12,"status":19,"episode_people":4338,"recommendations":4341,"season":4342},"30a30326-0bd0-4a40-b680-8c652f58a746","idea","917623408","In this episode, Matt and Kevin discuss the inspiration behind the Directus TV project.","3c6dfa45-4322-44a8-9163-f163f2e2db29",[4333,4334],{"name":2820,"url":2821},{"name":4149,"url":4150},"The Idea","\u003Cp>Speaker 0: Hello, everyone, and welcome to the first episode of digging the rabbit hole. This is a series created almost in response to having quite a lot of interest into how this whole platform you're watching this show on came to be. Joining me today is my friend and colleague, Matt. Would you like to introduce yourself?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Sure. Hey, everyone. I'm Matt Minor. Demand gen marketing, all of the things that you think of when it comes to marketing that's kind of undermined domain.\u003C\u002Fp>\u003Cp>So great to meet\u003C\u002Fp>\u003Cp>Speaker 0: you. Excellent. And well, we've met the audience might be meeting you for the first time. The in this first episode, I wanted to talk about the inception of directors TV as a project that our team even chose to pick up and work on. And this project was was your brainchild, really.\u003C\u002Fp>\u003Cp>It's been very much been a team effort to get it to work and get it out the door, but the inception of it was yours. So I'd love if we could talk a bit about why we're even bothering with this, how it came about inspiration, stuff like that.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. So, to give you a little background on me and my background, I come from like kind of when you think of marketing, like everybody hates marketing because marketers ruined it from like the SAS world. So it was all about like, how can I quickly get an MQL? And you probably are familiar with this where people go and download ebooks and you have to give up your email and then you're immediately hounded by SDRs so they can hit that quota and then get the revenue somehow or some way.\u003C\u002Fp>\u003Cp>And I think that's put a lot of, I think it's I think it's really tarnished marketing because at the core of it is like we just want to connect people with a solution that's going to make their life better. That's it. And last year was my first experience in the dev tool world and actually marketing to developers and engineers and found out that they have very good meters. So the traditional playbooks don't work. And it was hard to learn that as an experience, but I think it made us all better and us as a company better because we realized we have to build relationships at scale.\u003C\u002Fp>\u003Cp>And the way to do that, I think, is by creating actual good content. And a lot of this came from your domain of DevRel. And how can we marry that with, like, the traditional marketing of connecting people to solutions? So those two things combined with the rise of AI last year that came out of nowhere, and I found a lot of, like, the content being put out was just overly, like it was.\u003C\u002Fp>\u003Cp>Speaker 0: It's garbage. It's garbage. It's, hey, I generated garbage.\u003C\u002Fp>\u003Cp>Speaker 1: Exactly. Yeah. Very garbage. And you can't I mean, you could use a tool like HeyGen to create, like, fake videos. But I think the way of the future is no actual one to 1 you watching somebody.\u003C\u002Fp>\u003Cp>And I think that's why people are addicted to TikTok and YouTube and all that stuff. So kind of a long winded answer, but that's kind of where the idea generated was, like an amalgamation of, like, all of these things of how can we create really good content that cuts through the noise that is helpful and doesn't, you know, isn't like us chasing the almighty dollar. Like, how can we actually help the ecosystem?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And, I we will make a good developer relations practitioner practitioner out of you yet, mister Miner, because that's exactly that is the discipline of DevRel. And what this project really has become is almost expanding that outside of just the realm of DevRel, at least at directors and across the entire organizations as we have created shows that not only benefit lots of teams goals, but also get them involved. So that's been really, really pleasant as well. Can we talk a little bit about why we're not just then creating a bunch of shows on YouTube Because that is a completely viable approach.\u003C\u002Fp>\u003Cp>Right? We could be creating content on platforms where developers already are. And Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: So no. That's a great, great question and great audience there because, obviously, YouTube is, like, the 2nd largest. It might I think it might have even eclipsed Google is like the largest search engine that people that people use the most used, I guess. And this is one hesitation we had when we were coming up with the idea and producing shows. And we're basically taking, we're taking YouTube out of the\u003C\u002Fp>\u003Cp>Speaker 0: we're taking YouTube out\u003C\u002Fp>\u003Cp>Speaker 1: of the equation and we're going to build it ourselves, which is like, but there's billions of people that use YouTube and we're starting from ground.\u003C\u002Fp>\u003Cp>Speaker 0: So discovery engine. It's a discovery engine.\u003C\u002Fp>\u003Cp>Speaker 1: Exactly. Yeah. So, actually, I had a great conversation with Anthony Canada, who's the CEO over at Audience Plus, which they have a platform that does what we're doing with with, DirectTV. They built like a sass around it all around own media. They're really building that.\u003C\u002Fp>\u003Cp>They're doing a really good job. And I actually asked that question. I was like, look, we're we're basically discovering this or we're basically abandoning this giant discovery platform and starting from scratch. And he was like, you know, when you go to YouTube, like, very seldom do people hang out on YouTube, watch out like hours of content like they do on Netflix. They they go to Netflix to binge.\u003C\u002Fp>\u003Cp>They go to YouTube for like the quick highlights. And same thing with TikTok. Like you go to TikTok, you've got 30 to 62nd videos and you're just scrolling through kind of that. Yeah. Getting that endorphin hit and then like going to the next one sort of thing.\u003C\u002Fp>\u003Cp>So he's like, well, what he said was we're finding a lot of companies are starting to put their best content on YouTube and like cutting out the short snippets, the best bits, the highlights, basically, and then pushing people back to their own media because that's what own media is like. You you own the audience. You're not, renting it from YouTube or Google. You're giving them the good stuff. And then if they really, truly like what you're doing and you're getting by it, then they'll come to your platform and subscribe.\u003C\u002Fp>\u003Cp>So that's the reasoning behind it. It's kind of an interesting dynamic for sure. But\u003C\u002Fp>\u003Cp>Speaker 0: the the not the renting your audience is something I was hoping you were gonna say because I think that's that's so so true. And the other benefit of owned platform and not just own media, but own platform is you get to you get the almost editorial decision around the curation of content. I feel like on YouTube and other platforms, their recommendation engine doesn't work always in the benefit of your audience, right? It works in the benefit of the goals of the platform. And you can present it in the way that makes sense for you and your audience.\u003C\u002Fp>\u003Cp>Right? We've trimmed a bunch of fat. If you look at a YouTube player or any of these platforms, you know, you look at the players and the pages that house the media, and we are very direct. We are very like, here is the content, here is the necessary information to help you get the most of it and move up move on. Now here's the next the next part if there is one or whatever.\u003C\u002Fp>\u003Cp>Here's some related content. We we have curated that experience quite intentionally for people as well. Mhmm. Yeah. We don't have availability elsewhere.\u003C\u002Fp>\u003Cp>Speaker 1: No. Yeah. And, like, just personal, like, experience. Like, when I go to YouTube, I have a 2 year old daughter, so I've got Paw Patrol intermixed with like marketing and a mix of music videos. It's it's curated to me and my interests.\u003C\u002Fp>\u003Cp>But, as like a professional, I I can't go to YouTube and and use it. I I typically go to LinkedIn, which is, you know, another, channel that's own media. I go to, like, Hockey Stack, which is a, they have a own media channel, which is really good. Watch a few videos there, podcasts. I have a few select things I go to, but there's no one single place I can go that has a bunch of really good content to help me get better.\u003C\u002Fp>\u003Cp>And that's kind of what we're trying to build.\u003C\u002Fp>\u003Cp>Speaker 0: And these these platforms, like you mentioned, HockeyStack, I've seen their platform too. It's very much aimed at their audience marketers. Right? And there isn't a lot out there that is aimed at this this audience that directors has, which is primarily developers, not exclusively, but primarily developers. There aren't a lot, if any, platforms for them.\u003C\u002Fp>\u003Cp>So I think that's interesting too. Don't get me wrong. There's a lot of video content out there for developers, curated video content too, but a lot of it is instructional. What we're doing now is we are it's purely around upskilling and continued professional development. And we're trying to and we'll talk about this in a in a future episode of Digging the Rabbit Hole.\u003C\u002Fp>\u003Cp>We've curated a content library, which is a little more multifaceted around all of the interests that our audience might have.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. No. And we talked to Jason Linkedorf about this too. And he he also believes this is where media content, it's all going for the developer world. So, it's just really cool to have, like, that mindshare with, like, somebody like Jason, who's obviously doing an incredible job building building a brand, putting out incredible shows.\u003C\u002Fp>\u003Cp>And I think it is, like he says, like, the the industry is going that way. So it's just cool to to play a part in it in what we're doing with Directus TV.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Awesome. Before we wrap up this first episode of Dicking the Rabbit Hole, was there anything you wanted anything else you wanted to talk about in terms of the inception of the oh, another interesting note that I I have actually. The inception of this platform happened in March 2023, being a very small scrappy marketing team. It it was mentioned, you know, it had some headspace for a few weeks and it never really got off the ground.\u003C\u002Fp>\u003Cp>Right? It never became a thing we did. The whole thing came up. We finished our 1st leap week, our 1st week of announcements at the end of October 2023, and then there was a void left in our hearts. What what what fills this huge amount of stress now?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Exactly.\u003C\u002Fp>\u003Cp>Speaker 0: And and it literally between, like, the 1st week of November and, like, just before Christmas when we released director's TV, that was the entire window in which the whole thing was formalized, built, content was made and edited and released. It was about a 6 week period, and we continue to work in that scrappy way. There's a future episode on our process and how we've managed to and how we've managed to make this work. But yeah, was there anything else you want to talk about in terms of inception?\u003C\u002Fp>\u003Cp>Speaker 1: No, I just remember actually, I remember the stand up that we had and I was like, hey, like, like content's like doing really well. What if we just all, like, made our own video series and we had our own, like, Netflix sort of thing? Yeah. And everybody was a content house. Yeah.\u003C\u002Fp>\u003Cp>Everybody was like, Oh, yeah, maybe one day, because we had so many different things going on. But as time went on and we found content was going really well, and we were able to actually produce really good content. And we have a lot of incredible people, people in this team like, John and Pedro, who are sales reps, and they host podcasts with, you know, engineering leaders and they're willing to jump in. We even did like a.\u003C\u002Fp>\u003Cp>Speaker 0: Trace talks, by the way, for people watching this. You can watch that now. But this\u003C\u002Fp>\u003Cp>Speaker 1: is the first place I've worked where everybody wants to join in and help build content.\u003C\u002Fp>\u003Cp>Speaker 0: Absolutely.\u003C\u002Fp>\u003Cp>Speaker 1: And like do good content. And I think if you have that buy in with the team, it's a lot easier to pull off. So we're we're continuing getting there. I'm really excited with what we've done so far. And don't sell yourself short, Kevin.\u003C\u002Fp>\u003Cp>Like, you're the one that pulled all this together. So you're you're the linchpin of all this happening. So it always helps to have somebody like that on the team that can project manage and\u003C\u002Fp>\u003Cp>Speaker 0: Thank you very much. I'll I'll pay you for that compliment later. Yep. So this is the first episode of digging the rabbit hole. This is, I think it's gonna end up being a 5 part series.\u003C\u002Fp>\u003Cp>In future episodes, they're all available now. We're gonna release this in in one bundle, for you to watch. In the next episode, we're gonna talk about what gets made, how do we actually, you know, people across directors pitch shows into Matt and I. How do we decide what actually will get built and what are the purposes of different shows? What are the motivations and what are the kind of the decision factors that make that happen.\u003C\u002Fp>\u003Cp>Then we're gonna talk about the process that we've created that allows us to output such a huge amount of content while not actually having any individual, like, owning director's TV. Like, this is this is one of the many projects that are run, and that is that is enabled by strong processes and playing into our team's strengths and weaknesses and building processes that that, account for those. We'll talk about that as well. Then we'll talk about the actual technical build, this platform right here. It's part of our website.\u003C\u002Fp>\u003Cp>How does it work? Spoiler, of course, it utilizes directors to manage content, but we'll also dig into the code, and see how that all gets pulled through. And then finally, we will chat with some of our field team around some of the early reception for this platform and how it's been going so far. So I think that's I think that's it, Matt.\u003C\u002Fp>\u003Cp>Speaker 1: That's it. Thanks for having me.\u003C\u002Fp>\u003Cp>Speaker 0: Thanks for thanks for coming. And until the next episode, bye for now.\u003C\u002Fp>\u003Cp>Speaker 1: Bye.\u003C\u002Fp>","Hello, everyone, and welcome to the first episode of digging the rabbit hole. This is a series created almost in response to having quite a lot of interest into how this whole platform you're watching this show on came to be. Joining me today is my friend and colleague, Matt. Would you like to introduce yourself? Yeah. Sure. Hey, everyone. I'm Matt Minor. Demand gen marketing, all of the things that you think of when it comes to marketing that's kind of undermined domain. So great to meet you. Excellent. And well, we've met the audience might be meeting you for the first time. The in this first episode, I wanted to talk about the inception of directors TV as a project that our team even chose to pick up and work on. And this project was was your brainchild, really. It's been very much been a team effort to get it to work and get it out the door, but the inception of it was yours. So I'd love if we could talk a bit about why we're even bothering with this, how it came about inspiration, stuff like that. Yeah. Yeah. So, to give you a little background on me and my background, I come from like kind of when you think of marketing, like everybody hates marketing because marketers ruined it from like the SAS world. So it was all about like, how can I quickly get an MQL? And you probably are familiar with this where people go and download ebooks and you have to give up your email and then you're immediately hounded by SDRs so they can hit that quota and then get the revenue somehow or some way. And I think that's put a lot of, I think it's I think it's really tarnished marketing because at the core of it is like we just want to connect people with a solution that's going to make their life better. That's it. And last year was my first experience in the dev tool world and actually marketing to developers and engineers and found out that they have very good meters. So the traditional playbooks don't work. And it was hard to learn that as an experience, but I think it made us all better and us as a company better because we realized we have to build relationships at scale. And the way to do that, I think, is by creating actual good content. And a lot of this came from your domain of DevRel. And how can we marry that with, like, the traditional marketing of connecting people to solutions? So those two things combined with the rise of AI last year that came out of nowhere, and I found a lot of, like, the content being put out was just overly, like it was. It's garbage. It's garbage. It's, hey, I generated garbage. Exactly. Yeah. Very garbage. And you can't I mean, you could use a tool like HeyGen to create, like, fake videos. But I think the way of the future is no actual one to 1 you watching somebody. And I think that's why people are addicted to TikTok and YouTube and all that stuff. So kind of a long winded answer, but that's kind of where the idea generated was, like an amalgamation of, like, all of these things of how can we create really good content that cuts through the noise that is helpful and doesn't, you know, isn't like us chasing the almighty dollar. Like, how can we actually help the ecosystem? Yeah. And, I we will make a good developer relations practitioner practitioner out of you yet, mister Miner, because that's exactly that is the discipline of DevRel. And what this project really has become is almost expanding that outside of just the realm of DevRel, at least at directors and across the entire organizations as we have created shows that not only benefit lots of teams goals, but also get them involved. So that's been really, really pleasant as well. Can we talk a little bit about why we're not just then creating a bunch of shows on YouTube Because that is a completely viable approach. Right? We could be creating content on platforms where developers already are. And Yeah. So no. That's a great, great question and great audience there because, obviously, YouTube is, like, the 2nd largest. It might I think it might have even eclipsed Google is like the largest search engine that people that people use the most used, I guess. And this is one hesitation we had when we were coming up with the idea and producing shows. And we're basically taking, we're taking YouTube out of the we're taking YouTube out of the equation and we're going to build it ourselves, which is like, but there's billions of people that use YouTube and we're starting from ground. So discovery engine. It's a discovery engine. Exactly. Yeah. So, actually, I had a great conversation with Anthony Canada, who's the CEO over at Audience Plus, which they have a platform that does what we're doing with with, DirectTV. They built like a sass around it all around own media. They're really building that. They're doing a really good job. And I actually asked that question. I was like, look, we're we're basically discovering this or we're basically abandoning this giant discovery platform and starting from scratch. And he was like, you know, when you go to YouTube, like, very seldom do people hang out on YouTube, watch out like hours of content like they do on Netflix. They they go to Netflix to binge. They go to YouTube for like the quick highlights. And same thing with TikTok. Like you go to TikTok, you've got 30 to 62nd videos and you're just scrolling through kind of that. Yeah. Getting that endorphin hit and then like going to the next one sort of thing. So he's like, well, what he said was we're finding a lot of companies are starting to put their best content on YouTube and like cutting out the short snippets, the best bits, the highlights, basically, and then pushing people back to their own media because that's what own media is like. You you own the audience. You're not, renting it from YouTube or Google. You're giving them the good stuff. And then if they really, truly like what you're doing and you're getting by it, then they'll come to your platform and subscribe. So that's the reasoning behind it. It's kind of an interesting dynamic for sure. But the the not the renting your audience is something I was hoping you were gonna say because I think that's that's so so true. And the other benefit of owned platform and not just own media, but own platform is you get to you get the almost editorial decision around the curation of content. I feel like on YouTube and other platforms, their recommendation engine doesn't work always in the benefit of your audience, right? It works in the benefit of the goals of the platform. And you can present it in the way that makes sense for you and your audience. Right? We've trimmed a bunch of fat. If you look at a YouTube player or any of these platforms, you know, you look at the players and the pages that house the media, and we are very direct. We are very like, here is the content, here is the necessary information to help you get the most of it and move up move on. Now here's the next the next part if there is one or whatever. Here's some related content. We we have curated that experience quite intentionally for people as well. Mhmm. Yeah. We don't have availability elsewhere. No. Yeah. And, like, just personal, like, experience. Like, when I go to YouTube, I have a 2 year old daughter, so I've got Paw Patrol intermixed with like marketing and a mix of music videos. It's it's curated to me and my interests. But, as like a professional, I I can't go to YouTube and and use it. I I typically go to LinkedIn, which is, you know, another, channel that's own media. I go to, like, Hockey Stack, which is a, they have a own media channel, which is really good. Watch a few videos there, podcasts. I have a few select things I go to, but there's no one single place I can go that has a bunch of really good content to help me get better. And that's kind of what we're trying to build. And these these platforms, like you mentioned, HockeyStack, I've seen their platform too. It's very much aimed at their audience marketers. Right? And there isn't a lot out there that is aimed at this this audience that directors has, which is primarily developers, not exclusively, but primarily developers. There aren't a lot, if any, platforms for them. So I think that's interesting too. Don't get me wrong. There's a lot of video content out there for developers, curated video content too, but a lot of it is instructional. What we're doing now is we are it's purely around upskilling and continued professional development. And we're trying to and we'll talk about this in a in a future episode of Digging the Rabbit Hole. We've curated a content library, which is a little more multifaceted around all of the interests that our audience might have. Yeah. No. And we talked to Jason Linkedorf about this too. And he he also believes this is where media content, it's all going for the developer world. So, it's just really cool to have, like, that mindshare with, like, somebody like Jason, who's obviously doing an incredible job building building a brand, putting out incredible shows. And I think it is, like he says, like, the the industry is going that way. So it's just cool to to play a part in it in what we're doing with Directus TV. Yeah. Awesome. Before we wrap up this first episode of Dicking the Rabbit Hole, was there anything you wanted anything else you wanted to talk about in terms of the inception of the oh, another interesting note that I I have actually. The inception of this platform happened in March 2023, being a very small scrappy marketing team. It it was mentioned, you know, it had some headspace for a few weeks and it never really got off the ground. Right? It never became a thing we did. The whole thing came up. We finished our 1st leap week, our 1st week of announcements at the end of October 2023, and then there was a void left in our hearts. What what what fills this huge amount of stress now? Yeah. Exactly. And and it literally between, like, the 1st week of November and, like, just before Christmas when we released director's TV, that was the entire window in which the whole thing was formalized, built, content was made and edited and released. It was about a 6 week period, and we continue to work in that scrappy way. There's a future episode on our process and how we've managed to and how we've managed to make this work. But yeah, was there anything else you want to talk about in terms of inception? No, I just remember actually, I remember the stand up that we had and I was like, hey, like, like content's like doing really well. What if we just all, like, made our own video series and we had our own, like, Netflix sort of thing? Yeah. And everybody was a content house. Yeah. Everybody was like, Oh, yeah, maybe one day, because we had so many different things going on. But as time went on and we found content was going really well, and we were able to actually produce really good content. And we have a lot of incredible people, people in this team like, John and Pedro, who are sales reps, and they host podcasts with, you know, engineering leaders and they're willing to jump in. We even did like a. Trace talks, by the way, for people watching this. You can watch that now. But this is the first place I've worked where everybody wants to join in and help build content. Absolutely. And like do good content. And I think if you have that buy in with the team, it's a lot easier to pull off. So we're we're continuing getting there. I'm really excited with what we've done so far. And don't sell yourself short, Kevin. Like, you're the one that pulled all this together. So you're you're the linchpin of all this happening. So it always helps to have somebody like that on the team that can project manage and Thank you very much. I'll I'll pay you for that compliment later. Yep. So this is the first episode of digging the rabbit hole. This is, I think it's gonna end up being a 5 part series. In future episodes, they're all available now. We're gonna release this in in one bundle, for you to watch. In the next episode, we're gonna talk about what gets made, how do we actually, you know, people across directors pitch shows into Matt and I. How do we decide what actually will get built and what are the purposes of different shows? What are the motivations and what are the kind of the decision factors that make that happen. Then we're gonna talk about the process that we've created that allows us to output such a huge amount of content while not actually having any individual, like, owning director's TV. Like, this is this is one of the many projects that are run, and that is that is enabled by strong processes and playing into our team's strengths and weaknesses and building processes that that, account for those. We'll talk about that as well. Then we'll talk about the actual technical build, this platform right here. It's part of our website. How does it work? Spoiler, of course, it utilizes directors to manage content, but we'll also dig into the code, and see how that all gets pulled through. And then finally, we will chat with some of our field team around some of the early reception for this platform and how it's been going so far. So I think that's I think that's it, Matt. That's it. Thanks for having me. Thanks for thanks for coming. And until the next episode, bye for now. Bye.",[4339,4340],"72fcbab8-e5ef-4751-8dce-c0f28adfa674","6a71981e-78e4-4dc0-8535-1ee9b137878f",[],{"year":978,"number":282,"id":4343,"episodes":4344,"show":4350},"5a656532-6f30-42e9-9a11-24b0b9041642",[4327,4345,4346,4347,4348,4349],"4aa1ba58-7820-40a4-9196-5e186be53f3a","8fc804e1-786d-4a14-bddd-74d2785b20a6","3262bf03-ab28-45e1-a824-c0418b29927a","8fdf7563-f9e4-4321-ae64-d009b0fddfaf","1db8f535-b44b-4b5d-9937-a922ae7560db",{"title":4351,"slug":4352},"Digging the Rabbit Hole","digging-the-rabbit-hole",{"id":4348,"slug":4354,"vimeo_id":4355,"description":4356,"tile":4357,"length":247,"resources":12,"people":4358,"episode_number":213,"published":4201,"title":4363,"video_transcript_html":4364,"video_transcript_text":4365,"content":12,"seo":12,"status":19,"episode_people":4366,"recommendations":4369,"season":4370},"reception","917614369","In this episode, John and Kevin discuss the early feedback and reception to Directus TV and it's shows.","743f964d-41fa-4dda-8e09-0750c92cadca",[4359,4360],{"name":2820,"url":2821},{"name":4361,"url":4362},"John Daniels","https:\u002F\u002Fdirectus.io\u002Fteam\u002Fjohn-daniels","The Reception","\u003Cp>Speaker 0: Hello, and welcome to the final episode of digging the rabbit hole. This is a series that is born out of getting a lot of questions about how this has all come together. And today, John is joining me, to talk about some initial feedback we got. John, would you like to introduce yourself?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Thanks, Kevin. Glad to be part of the series. John Daniels, one of the team members here at Directus. Been here for about 2 years, so it's exciting to see where Directus was when we started to now where we are with, Directus TV and where we are with the platform.\u003C\u002Fp>\u003Cp>Speaker 0: Awesome. And, of course, you're you're part of our field team, I suppose, is one way to frame it.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Definitely part of the field team. I help teach about Directus, you know, work with our enterprise customers, kind of everything.\u003C\u002Fp>\u003Cp>Speaker 0: Excellent. And so Directus TV has now existed for a couple of months. And you obviously are, you know, in the same way that as developer relations person, I mean, with the community, in my Discord and our GitHub, you're in with customers and enterprise users of directors. And I just thought we could use this last episode to be a little bit reflective perhaps about some of the early feedback that we've got about the platform.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. Yeah. Some, yeah, some of the early feedback has been great. My most favorite piece of feedback is when I hop on a a call with somebody I've never met before, and they say, hey.\u003C\u002Fp>\u003Cp>I've seen your face before on videos. So that's always pretty fun. It makes me feel more important than I am, but that's some good feedback, which means it it does let me know that people are watching the videos, which is cool, especially because the videos that I'm in are a little bit more nondirectus focused, and it means they're just watching our content. So that's been some of the feedback that I enjoy, but also feedback more so around Directus TV in general has been it's been it's been widely popular at least from the people that I'm talking to. People talk about all the different types of series that are on there.\u003C\u002Fp>\u003Cp>They do appreciate the Direct us focused ones and the non direct us focused ones. But they really love the fact that the ones that are focused on direct us kind of showcase the the flexibility of the platform. Yeah. That's always that's always been one of our problems is you can do so much with Directus, and and, you know, how do you show all of that? And so I think a series like 100 apps, 100 hours is really beneficial both for people who are starting to explore Directus, but also for me as I'm talking to these people.\u003C\u002Fp>\u003Cp>Because because I'll talk to people who are trying to build an Airbnb clone or trying to build a swag platform. And so it's great for me. If they haven't seen it, I'll send it to them and they'll they love it because it's typically right up their alley. But a lot of times, they'll come in having seen those episodes. And so feedback in general has been fantastic.\u003C\u002Fp>\u003Cp>People love the entertaining value of it because it's not just a bunch of people, you know, training and being kind of boring. That's not who we are. And it's also making me famous, which is really awesome.\u003C\u002Fp>\u003Cp>Speaker 0: Well, there we go. There we go. You know, it's interesting you mentioned a 100 apps, 100 hours. You know, I would say and and we've alluded to this in episodes before this. That's probably been our series that's also had the most criticism, and it's pretty clear what the criticism is and where it comes from.\u003C\u002Fp>\u003Cp>And it comes from not meeting people's expectations with what they are or what they're expecting to see. They're expecting Right. Like purely educational, instructional, best practice driven content because that is what companies do. Right? That is what companies do when they're trying to educate people in creating content around their product.\u003C\u002Fp>\u003Cp>That isn't the goal of a 100 apps in a 100 hours. It is Yeah. It is an entertainment show where we watch Brian sweat for 60 minutes, try and figure it out against the clock. There may sometimes not be best practices being honored. I mean, certainly isn't instructional.\u003C\u002Fp>\u003Cp>And that's interesting. Maybe it's a sign that actually there's a demand for, you know, instructional content around building out use cases. But, yeah, it's just interesting that that isn't quite people aren't quite understanding that even having watched a fair amount of the video itself.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's actually a really good that's a fair point, I think. Whereas the the people I'm talking to, they're looking for a platform they can use to build these platforms or build their products. Mhmm. And so they're really just looking to see if Directus can do that.\u003C\u002Fp>\u003Cp>And so the people that I'm talking to are coming to coming to us watching these videos saying, can Directus do this? And we say, hey. We've, you know, we've built this really tiny version of this app in an hour. Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: And\u003C\u002Fp>\u003Cp>Speaker 1: so I I guess the people that I'm talking to just wanna see that that's something that people do in Directus. So they can see that someone's trying to build a this clone or whatever. So, but I can totally understand where people want to actually then dive deeper and understand what is the step by steps that I need\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: To build an Airbnb clone. But if you look at something like that, that would be a whole series in itself, just building one of those clones the proper way. I mean, there's it took Airbnb, you know, 6 months probably to to develop a prototype. And so that would be a lot more than a a 60 minute session. But that that's I totally understand that where typical people would be or people who know Directus want to then dive deeper into actually doing that work.\u003C\u002Fp>\u003Cp>Speaker 0: And I suppose there's 2 outcomes there in terms of future content. One of them is, yes. Okay. We will build out these use cases, you know, and and help you build them as well. I fear, you know, considering the first season of a 100 apps in a 100 hours is 10 episodes.\u003C\u002Fp>\u003Cp>That's effectively 10 series worth of content and we're just not equipped to to do that realistically. The other side is, and this is a series that we're planning, you know, and I think it's a pretty, pretty natural thing to create, which is a set of short demos where directors is being used well as these use cases. So maybe there's something where, well, historically, we haven't had that much use case based content. Now we have something but it is almost it has this baggage of being this entertainment show where that might not be where people are expecting, expecting it to go. So maybe there's a strict back version, which is just and here's the endpoint maybe.\u003C\u002Fp>\u003Cp>Yeah. Yeah. That's certainly interesting.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I mean, obviously, any technology needs the the actual training on the product. And so I'd love to see us get to that point where we do release these hey. Build a build a very simple CMS in 5 minutes, or here's direct us in 5 minutes, and here's direct us in 30 minutes. I'd love to even see a certification training program eventually, but, obviously, with a smaller team that that takes a whole different workload there too.\u003C\u002Fp>\u003Cp>But I think over time, we'll add more and more much like our YouTube videos were back in the day focused on Directus and how to use Directus. We'll bring that content back into Directus TV because it's it's what we wanna do and it's what people are asking\u003C\u002Fp>\u003Cp>Speaker 0: for as well. Absolutely. And, you know, some of what you're describing is in the works as well in various guises. But, yeah, more instructional, more walk through content, more use case driven inspiration as Brian called it in a previous episode, less instruction, maybe inspiration would be a better word. Yeah.\u003C\u002Fp>\u003Cp>Interesting. I agree, though. From my side, the feedback generally has been pretty good. You know, not every and here's the thing, not every season, not every series has to be for everyone. So, you know, we have a lot of interview based content.\u003C\u002Fp>\u003Cp>Some people are just not interested in the interview format. That's totally cool. Some people just want instruction. Some people want effectively demos in some form or another, whether it's the whole, you know, the whole platform, whether it's discrete parts of the platform or whether it's use cases or case studies or things like that. And some people wanna be entertained.\u003C\u002Fp>\u003Cp>And the lovely thing about this platform we've got is we don't have to double down on any one of those. We just need to always have our eye on making sure the ratios are right. And we also spoke about this in an earlier episode. I don't believe the ratio is right today. The byproduct of this whole platform having come about in 6 weeks is that there was a lot more interview content created and less instructional content because one is easier to make than the other.\u003C\u002Fp>\u003Cp>But we're addressing that, we're addressing that. There are a lot of shows, There are a lot of shows in the works. The day this releases, we announce our spring slate, but we're already working on the summer and the full slate of content as well, which is really exciting.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I mean, people just want less John and and more instructions, and I totally get that. You know? There's There's only so much Why\u003C\u002Fp>\u003Cp>Speaker 0: not instruction from John?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That'd be kind of interesting because I'm on the the I'm on the side of the house that's kind of I always tell people I'm middle level technical. Like, I understand how to use direct us, but I'm not gonna go and build that Airbnb clone. So that would actually probably be an interesting series, honestly, is watch a middle level technical person build an Airbnb clone or something like that. So that would probably be entertaining.\u003C\u002Fp>\u003Cp>Speaker 0: There actually was there is a series pitch with this vibe, but it is, it's Matt Matt who isn't a developer. It's definitely I think we're all everyone who works with directors is quite technically minded. Like, we are technical problem solvers. The amount of code with which we can do that with varies from individual to individual and team to team. But, yeah, that's a really interesting format too.\u003C\u002Fp>\u003Cp>But, yeah, there there's so much. Have you, you know, have you got any other the answer can be no, but has there been any other kind of top line feedback that you've got that could be interesting? Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: I think, I think the top line feedback is exactly what you just mentioned. Not every show is for every person. I mean, even myself, I I try and watch every episode of everything that comes out, but there's there's certainly some series that I tend to gravitate towards. So I think that's the good feedback is, not every not every series is for every person, but every person can try and find a series that that fits for them or that they enjoy. So that's one of the pieces of feedback.\u003C\u002Fp>\u003Cp>Obviously, I haven't heard necessarily too much of the I need to dive deeper into these topics, but I'm talking to people who are traditionally newer to direct us. But I can understand that feedback. But then in general, people are watching it, and they're they're glad that we have it. They're glad that we have this content platform, both for the entertainment value, but also for the educational value in the hey. You know, Directus can do what I think it can do, and I can see a really quick video about that.\u003C\u002Fp>\u003Cp>So I think the feedback in general has just been that people enjoy the fact that we have it and the fact that there's something that they can find,\u003C\u002Fp>\u003Cp>Speaker 0: you\u003C\u002Fp>\u003Cp>Speaker 1: know, value in, whether it's a couple series or one series or there's probably a few people out there who watch every series. So I think the feedback in general has been great. And it's been great for me when again, just to reiterate, when people come and and start to learn about Directus to just point them to a video. You know? Just be like, go watch this series.\u003C\u002Fp>\u003Cp>Go watch this episode. You know, it's fantastic. So that's been great for me too.\u003C\u002Fp>\u003Cp>Speaker 0: And as an educator, the person who looks after our docs or lead leading the team who look after our docs, it's very much the same the same there as well, which is the more content we can create that's valuable and solves people's problems, the more reference material we have when that content can help people and the less time we can spend doing the almost, like, repetitive education, right, because we have collateral to back it up. And, yeah, director's TV definitely plays an important part of that. In terms of feedback also, you know, we we track some basic metrics, simple things, what gets watched, how long of each thing gets watched, do people watch stuff and then end up elsewhere on our website, and does that turn into results for us? Absolutely. As part of having our own media platform is that we we know.\u003C\u002Fp>\u003Cp>You know, we we can build build in that analytics and tweak that for our own purposes. There is currently an issue on the GitHub repo for the website to implement a feedback widget like on our documentation so we can get a more rich feedback about how people are finding individual shows and episodes, but that's that's later. For now it's just what's being watched, for how long, does it turn into some form of outcome, and of course the fact that we can use it to help educate people, or inspire them or entertain them or to build relationships and highlight community stories.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. No. I think it's been great. And and there's even things on there like scenescapes, which I think we only have 2 of right now.\u003C\u002Fp>\u003Cp>But even though one of the scenes is outside of my window, I'll even put it on sometimes. Or when I'm traveling, I'll put it on and just listen to music and play. So I think, again, the fact that there's we're trying to create series for everybody and for all types of things is awesome.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And, of course, the other scene scapes is here in Berlin with me. Yeah. Really fun. Thank you so much, John, for joining joining me, and thanks for tuning in to Digging the Rabbit Hole.\u003C\u002Fp>\u003Cp>We hope you found this series insightful. We may well we may well renew it for another season when there's interesting developments. But for now, thank you very much. Enjoy all the other content we have here on directors TV and I'll see you we'll see you in our respective next video hosts.\u003C\u002Fp>\u003Cp>Speaker 1: Thanks, everyone.\u003C\u002Fp>","Hello, and welcome to the final episode of digging the rabbit hole. This is a series that is born out of getting a lot of questions about how this has all come together. And today, John is joining me, to talk about some initial feedback we got. John, would you like to introduce yourself? Yeah. Thanks, Kevin. Glad to be part of the series. John Daniels, one of the team members here at Directus. Been here for about 2 years, so it's exciting to see where Directus was when we started to now where we are with, Directus TV and where we are with the platform. Awesome. And, of course, you're you're part of our field team, I suppose, is one way to frame it. Yeah. Definitely part of the field team. I help teach about Directus, you know, work with our enterprise customers, kind of everything. Excellent. And so Directus TV has now existed for a couple of months. And you obviously are, you know, in the same way that as developer relations person, I mean, with the community, in my Discord and our GitHub, you're in with customers and enterprise users of directors. And I just thought we could use this last episode to be a little bit reflective perhaps about some of the early feedback that we've got about the platform. Yeah. Yeah. Yeah. Some, yeah, some of the early feedback has been great. My most favorite piece of feedback is when I hop on a a call with somebody I've never met before, and they say, hey. I've seen your face before on videos. So that's always pretty fun. It makes me feel more important than I am, but that's some good feedback, which means it it does let me know that people are watching the videos, which is cool, especially because the videos that I'm in are a little bit more nondirectus focused, and it means they're just watching our content. So that's been some of the feedback that I enjoy, but also feedback more so around Directus TV in general has been it's been it's been widely popular at least from the people that I'm talking to. People talk about all the different types of series that are on there. They do appreciate the Direct us focused ones and the non direct us focused ones. But they really love the fact that the ones that are focused on direct us kind of showcase the the flexibility of the platform. Yeah. That's always that's always been one of our problems is you can do so much with Directus, and and, you know, how do you show all of that? And so I think a series like 100 apps, 100 hours is really beneficial both for people who are starting to explore Directus, but also for me as I'm talking to these people. Because because I'll talk to people who are trying to build an Airbnb clone or trying to build a swag platform. And so it's great for me. If they haven't seen it, I'll send it to them and they'll they love it because it's typically right up their alley. But a lot of times, they'll come in having seen those episodes. And so feedback in general has been fantastic. People love the entertaining value of it because it's not just a bunch of people, you know, training and being kind of boring. That's not who we are. And it's also making me famous, which is really awesome. Well, there we go. There we go. You know, it's interesting you mentioned a 100 apps, 100 hours. You know, I would say and and we've alluded to this in episodes before this. That's probably been our series that's also had the most criticism, and it's pretty clear what the criticism is and where it comes from. And it comes from not meeting people's expectations with what they are or what they're expecting to see. They're expecting Right. Like purely educational, instructional, best practice driven content because that is what companies do. Right? That is what companies do when they're trying to educate people in creating content around their product. That isn't the goal of a 100 apps in a 100 hours. It is Yeah. It is an entertainment show where we watch Brian sweat for 60 minutes, try and figure it out against the clock. There may sometimes not be best practices being honored. I mean, certainly isn't instructional. And that's interesting. Maybe it's a sign that actually there's a demand for, you know, instructional content around building out use cases. But, yeah, it's just interesting that that isn't quite people aren't quite understanding that even having watched a fair amount of the video itself. Yeah. That's actually a really good that's a fair point, I think. Whereas the the people I'm talking to, they're looking for a platform they can use to build these platforms or build their products. Mhmm. And so they're really just looking to see if Directus can do that. And so the people that I'm talking to are coming to coming to us watching these videos saying, can Directus do this? And we say, hey. We've, you know, we've built this really tiny version of this app in an hour. Mhmm. And so I I guess the people that I'm talking to just wanna see that that's something that people do in Directus. So they can see that someone's trying to build a this clone or whatever. So, but I can totally understand where people want to actually then dive deeper and understand what is the step by steps that I need Yeah. To build an Airbnb clone. But if you look at something like that, that would be a whole series in itself, just building one of those clones the proper way. I mean, there's it took Airbnb, you know, 6 months probably to to develop a prototype. And so that would be a lot more than a a 60 minute session. But that that's I totally understand that where typical people would be or people who know Directus want to then dive deeper into actually doing that work. And I suppose there's 2 outcomes there in terms of future content. One of them is, yes. Okay. We will build out these use cases, you know, and and help you build them as well. I fear, you know, considering the first season of a 100 apps in a 100 hours is 10 episodes. That's effectively 10 series worth of content and we're just not equipped to to do that realistically. The other side is, and this is a series that we're planning, you know, and I think it's a pretty, pretty natural thing to create, which is a set of short demos where directors is being used well as these use cases. So maybe there's something where, well, historically, we haven't had that much use case based content. Now we have something but it is almost it has this baggage of being this entertainment show where that might not be where people are expecting, expecting it to go. So maybe there's a strict back version, which is just and here's the endpoint maybe. Yeah. Yeah. That's certainly interesting. Yeah. I mean, obviously, any technology needs the the actual training on the product. And so I'd love to see us get to that point where we do release these hey. Build a build a very simple CMS in 5 minutes, or here's direct us in 5 minutes, and here's direct us in 30 minutes. I'd love to even see a certification training program eventually, but, obviously, with a smaller team that that takes a whole different workload there too. But I think over time, we'll add more and more much like our YouTube videos were back in the day focused on Directus and how to use Directus. We'll bring that content back into Directus TV because it's it's what we wanna do and it's what people are asking for as well. Absolutely. And, you know, some of what you're describing is in the works as well in various guises. But, yeah, more instructional, more walk through content, more use case driven inspiration as Brian called it in a previous episode, less instruction, maybe inspiration would be a better word. Yeah. Interesting. I agree, though. From my side, the feedback generally has been pretty good. You know, not every and here's the thing, not every season, not every series has to be for everyone. So, you know, we have a lot of interview based content. Some people are just not interested in the interview format. That's totally cool. Some people just want instruction. Some people want effectively demos in some form or another, whether it's the whole, you know, the whole platform, whether it's discrete parts of the platform or whether it's use cases or case studies or things like that. And some people wanna be entertained. And the lovely thing about this platform we've got is we don't have to double down on any one of those. We just need to always have our eye on making sure the ratios are right. And we also spoke about this in an earlier episode. I don't believe the ratio is right today. The byproduct of this whole platform having come about in 6 weeks is that there was a lot more interview content created and less instructional content because one is easier to make than the other. But we're addressing that, we're addressing that. There are a lot of shows, There are a lot of shows in the works. The day this releases, we announce our spring slate, but we're already working on the summer and the full slate of content as well, which is really exciting. Yeah. I mean, people just want less John and and more instructions, and I totally get that. You know? There's There's only so much Why not instruction from John? Yeah. That'd be kind of interesting because I'm on the the I'm on the side of the house that's kind of I always tell people I'm middle level technical. Like, I understand how to use direct us, but I'm not gonna go and build that Airbnb clone. So that would actually probably be an interesting series, honestly, is watch a middle level technical person build an Airbnb clone or something like that. So that would probably be entertaining. There actually was there is a series pitch with this vibe, but it is, it's Matt Matt who isn't a developer. It's definitely I think we're all everyone who works with directors is quite technically minded. Like, we are technical problem solvers. The amount of code with which we can do that with varies from individual to individual and team to team. But, yeah, that's a really interesting format too. But, yeah, there there's so much. Have you, you know, have you got any other the answer can be no, but has there been any other kind of top line feedback that you've got that could be interesting? Yeah. I think, I think the top line feedback is exactly what you just mentioned. Not every show is for every person. I mean, even myself, I I try and watch every episode of everything that comes out, but there's there's certainly some series that I tend to gravitate towards. So I think that's the good feedback is, not every not every series is for every person, but every person can try and find a series that that fits for them or that they enjoy. So that's one of the pieces of feedback. Obviously, I haven't heard necessarily too much of the I need to dive deeper into these topics, but I'm talking to people who are traditionally newer to direct us. But I can understand that feedback. But then in general, people are watching it, and they're they're glad that we have it. They're glad that we have this content platform, both for the entertainment value, but also for the educational value in the hey. You know, Directus can do what I think it can do, and I can see a really quick video about that. So I think the feedback in general has just been that people enjoy the fact that we have it and the fact that there's something that they can find, you know, value in, whether it's a couple series or one series or there's probably a few people out there who watch every series. So I think the feedback in general has been great. And it's been great for me when again, just to reiterate, when people come and and start to learn about Directus to just point them to a video. You know? Just be like, go watch this series. Go watch this episode. You know, it's fantastic. So that's been great for me too. And as an educator, the person who looks after our docs or lead leading the team who look after our docs, it's very much the same the same there as well, which is the more content we can create that's valuable and solves people's problems, the more reference material we have when that content can help people and the less time we can spend doing the almost, like, repetitive education, right, because we have collateral to back it up. And, yeah, director's TV definitely plays an important part of that. In terms of feedback also, you know, we we track some basic metrics, simple things, what gets watched, how long of each thing gets watched, do people watch stuff and then end up elsewhere on our website, and does that turn into results for us? Absolutely. As part of having our own media platform is that we we know. You know, we we can build build in that analytics and tweak that for our own purposes. There is currently an issue on the GitHub repo for the website to implement a feedback widget like on our documentation so we can get a more rich feedback about how people are finding individual shows and episodes, but that's that's later. For now it's just what's being watched, for how long, does it turn into some form of outcome, and of course the fact that we can use it to help educate people, or inspire them or entertain them or to build relationships and highlight community stories. Yeah. Yeah. No. I think it's been great. And and there's even things on there like scenescapes, which I think we only have 2 of right now. But even though one of the scenes is outside of my window, I'll even put it on sometimes. Or when I'm traveling, I'll put it on and just listen to music and play. So I think, again, the fact that there's we're trying to create series for everybody and for all types of things is awesome. Yeah. And, of course, the other scene scapes is here in Berlin with me. Yeah. Really fun. Thank you so much, John, for joining joining me, and thanks for tuning in to Digging the Rabbit Hole. We hope you found this series insightful. We may well we may well renew it for another season when there's interesting developments. But for now, thank you very much. Enjoy all the other content we have here on directors TV and I'll see you we'll see you in our respective next video hosts. Thanks, everyone.",[4367,4368],"e6a451ac-f6ae-427b-8a7f-1e784eee765d","6d120d9b-61cb-4541-9abc-1cb910f63e43",[],{"year":978,"number":282,"id":4343,"episodes":4371,"show":4372},[4327,4345,4346,4347,4348,4349],{"title":4351,"slug":4352},{"id":4216,"slug":4374,"vimeo_id":4375,"description":4376,"tile":4377,"length":213,"resources":12,"people":4378,"episode_number":195,"published":4201,"title":4380,"video_transcript_html":4381,"video_transcript_text":4382,"content":12,"seo":12,"status":19,"episode_people":4383,"recommendations":4385,"season":4386},"02-state-of-data","918870767","780 engineers told us how they use data in their projects. Here are 6 things that we learned.","7c118633-2e07-46aa-819b-5cf35da8eba6",[4379],{"name":4149,"url":4150},"The State of Data Survey 2024","\u003Cp>Speaker 0: Hey. How's it going? I'm Matt on the marketing team here at Directus. Data discussions really aren't the stuff of legend. And in fact, when we put this survey together, at first, there was a couple of, like, raised eyebrows like, data, really?\u003C\u002Fp>\u003Cp>Is that what we want to do? What do you know? A month later, we had a huge flood of responses. 782 developers told us how they're using data in their projects. That tells us that, you know, data isn't dull.\u003C\u002Fp>\u003Cp>There's just nobody talking about it in the right way. We're going to make data exciting again. Here are the 6 things that we learned that we thought were really interesting, or 2 of these actually really shocked us when we found out. Number 1 is that Postgres leads the pack. It is the choice of over 63% of the developers we surveyed, that they're using in their projects currently.\u003C\u002Fp>\u003Cp>The number 2 thing, slightly related, but of all the database types when developers are starting new projects, over 57% of them said that they prefer relational databases all over all other types. Even asked our CTO, Reich, about it. And he said, know, the most likely reason is that a vast majority of data applications have some sort of schema consistency need for type safety and expectability. In NoSQL, you can quickly end up effectively reimplementing a lot of the things that a SQL database already offers out of the box. The number 3 thing we found that was really interesting to us was that developers are leaning towards cloud hosted database solutions.\u003C\u002Fp>\u003Cp>So by a wide margin too. Cloud leads, 68% of respondents prefer hosting with solutions like AWS or Azure, versus, you know, managed database services like DigitalOcean or on prem servers. This one, I think, was so shocking because it was so lopsided. 94% of our survey respondents said they prefer working with REST APIs as opposed to GraphQL. We kinda went into this thinking that cost was gonna be the primary factor, to consider when hosting databases.\u003C\u002Fp>\u003Cp>And in fact, it is a consideration, but it's not the most important. I think what this tells us is that developers are prioritizing performance and reliability and ease of use over how much it costs, which is still a consideration, but not what's driving, decisions at the end of the day. Taking this a step further, actually, we asked what the primary factors they think hosting should be charging for. And the top 3 in order were, machine resource utilization, so, like, RAM and CPU, storage space, and, actually, number of read and write options, which was interesting. The least popular way to price hosting, was by charging on the number of database records.\u003C\u002Fp>\u003Cp>So just an interesting little finding there. But, yeah. Number 6 was the most shocking thing we found. Over 64% of respondents said that they're using AI for cogeneration. So despite all of this, like loud people in the room talking about, like, AI is bad for development, I think there's a lot of people quietly actually using this to become better at their job and learning how to implement and the things that they're doing.\u003C\u002Fp>\u003Cp>Actually, going a little bit deeper on this, we asked 2 questions. If AI was too risky for data analysis to influence decision making, on average, sentiment was neutral. But when it comes to using AI to actually interact with the database with, like, CRUD permissions, Overwhelming sentiment was like, please do not do that. That is way too risky, which makes sense. So those are the 6 big takeaways, learnings we add just from a surface evaluation of the results that we got in the survey.\u003C\u002Fp>\u003Cp>Hopefully, these were interesting to you as they were to us. And again, like, data isn't boring. It's just how are people using it? How are we framing it? Hopefully, we need some more discussions around this, because we're data nerds at the end of the day.\u003C\u002Fp>\u003Cp>And what we're gonna do is basically put together a series that we call data drops, where we're gonna release an article, with, like, a really interesting insight, because it's one thing to know what the trends are. But we think it's more important to understand, like, why they vary from a startup in Silicon Valley as opposed to a seasoned freelancer in Berlin. So if you wanna get up to date on those and get nice little nuggets in your inbox, make sure you sign up for a newsletter. It goes out once a month. You can sign up in our docs.\u003C\u002Fp>\u003Cp>You can sign up on our, main blog, But we'd love to have you sign up and just get these once a month newsletters. Appreciate the time today, and, have a good one.\u003C\u002Fp>","Hey. How's it going? I'm Matt on the marketing team here at Directus. Data discussions really aren't the stuff of legend. And in fact, when we put this survey together, at first, there was a couple of, like, raised eyebrows like, data, really? Is that what we want to do? What do you know? A month later, we had a huge flood of responses. 782 developers told us how they're using data in their projects. That tells us that, you know, data isn't dull. There's just nobody talking about it in the right way. We're going to make data exciting again. Here are the 6 things that we learned that we thought were really interesting, or 2 of these actually really shocked us when we found out. Number 1 is that Postgres leads the pack. It is the choice of over 63% of the developers we surveyed, that they're using in their projects currently. The number 2 thing, slightly related, but of all the database types when developers are starting new projects, over 57% of them said that they prefer relational databases all over all other types. Even asked our CTO, Reich, about it. And he said, know, the most likely reason is that a vast majority of data applications have some sort of schema consistency need for type safety and expectability. In NoSQL, you can quickly end up effectively reimplementing a lot of the things that a SQL database already offers out of the box. The number 3 thing we found that was really interesting to us was that developers are leaning towards cloud hosted database solutions. So by a wide margin too. Cloud leads, 68% of respondents prefer hosting with solutions like AWS or Azure, versus, you know, managed database services like DigitalOcean or on prem servers. This one, I think, was so shocking because it was so lopsided. 94% of our survey respondents said they prefer working with REST APIs as opposed to GraphQL. We kinda went into this thinking that cost was gonna be the primary factor, to consider when hosting databases. And in fact, it is a consideration, but it's not the most important. I think what this tells us is that developers are prioritizing performance and reliability and ease of use over how much it costs, which is still a consideration, but not what's driving, decisions at the end of the day. Taking this a step further, actually, we asked what the primary factors they think hosting should be charging for. And the top 3 in order were, machine resource utilization, so, like, RAM and CPU, storage space, and, actually, number of read and write options, which was interesting. The least popular way to price hosting, was by charging on the number of database records. So just an interesting little finding there. But, yeah. Number 6 was the most shocking thing we found. Over 64% of respondents said that they're using AI for cogeneration. So despite all of this, like loud people in the room talking about, like, AI is bad for development, I think there's a lot of people quietly actually using this to become better at their job and learning how to implement and the things that they're doing. Actually, going a little bit deeper on this, we asked 2 questions. If AI was too risky for data analysis to influence decision making, on average, sentiment was neutral. But when it comes to using AI to actually interact with the database with, like, CRUD permissions, Overwhelming sentiment was like, please do not do that. That is way too risky, which makes sense. So those are the 6 big takeaways, learnings we add just from a surface evaluation of the results that we got in the survey. Hopefully, these were interesting to you as they were to us. And again, like, data isn't boring. It's just how are people using it? How are we framing it? Hopefully, we need some more discussions around this, because we're data nerds at the end of the day. And what we're gonna do is basically put together a series that we call data drops, where we're gonna release an article, with, like, a really interesting insight, because it's one thing to know what the trends are. But we think it's more important to understand, like, why they vary from a startup in Silicon Valley as opposed to a seasoned freelancer in Berlin. So if you wanna get up to date on those and get nice little nuggets in your inbox, make sure you sign up for a newsletter. It goes out once a month. You can sign up in our docs. You can sign up on our, main blog, But we'd love to have you sign up and just get these once a month newsletters. Appreciate the time today, and, have a good one.",[4384],"edb5a0f8-2ad0-4efd-89f2-7d8ff780cfa0",[],{"year":978,"number":2280,"id":4209,"episodes":4387,"show":4388},[4211,4212,4213,4214,4215,4216,4192,4217,4218],{"title":151,"slug":152},{"id":4349,"slug":4390,"vimeo_id":4391,"description":4392,"tile":4393,"length":195,"resources":12,"people":4394,"episode_number":195,"published":4201,"title":4396,"video_transcript_html":4397,"video_transcript_text":4398,"content":12,"seo":12,"status":19,"episode_people":4399,"recommendations":4401,"season":4402},"kit","917616799","In this episode, Bryant shows you around the template for creating your own streaming service with Directus, based on Directus TV.","30a3bb54-f021-45c3-aba7-94d7bbb14024",[4395],{"name":3590,"url":3591},"The Kit","\u003Cp>Speaker 0: Hello, and welcome back to digging the rabbit hole. We've gone through this journey of talking about DIRECTV from its inception to its release, the processes we've created and some of the early feedback. I actually have brought Brian back on. Hello, Brian, you're back for let's call it an epilogue, talking about building your own versions of directors TV for your projects and organizations. Yeah.\u003C\u002Fp>\u003Cp>Happy to be back. Thanks for bringing me back on to just show this off. Right? One of the questions that you've already answered is, you know, what powers this thing? But we created a template for folks who want to dig a\u003C\u002Fp>\u003Cp>Speaker 1: little deeper and maybe dig their own rabbit hole. So I'm just gonna walk you through the template just so you could see what's available. So the data model for the streaming platform, this very closely mirrors Directus TV itself. We have shows. So these are the actual collections of episodes.\u003C\u002Fp>\u003Cp>You could see here we've got some sample content like dev thoughts and a 100 apps, 100 hours. When you dig into that, you could see the title, the slug that you're using on the front end for the URL. We've got some announcement text and all these different images, that go on various\u003C\u002Fp>\u003Cp>Speaker 0: This looks familiar.\u003C\u002Fp>\u003Cp>Speaker 1: It does it? It should. Yeah. But rather than create sample content for this, I said, well, let's just steal what we already have. So, within that, you have your different seasons.\u003C\u002Fp>\u003Cp>And within the seasons, you have a number, a year, some actual episodes where this is where the the meat and potatoes of the content lives. A title description. We're using Vimeo to host those videos, so we've got a Vimeo ID. You could just as well upload a file into Directus and serve that or use a third party, YouTube or Wistia or some other service as well. Transcripts are available.\u003C\u002Fp>\u003Cp>We've got a tile. You can attach resources, and you can even add your different people, which is a relationship, which is really nice so that you don't have to keep typing those same people in every single episode. So we've got different collections there for the people. You can see you and I in there, a couple of handsome gentlemen. We have different categories.\u003C\u002Fp>\u003Cp>So interviews, code with us, short hops, these are the same ones on Directus TV. I'm sure they look familiar to you, Kevin. And then added a couple other things here for sessions. So, ideally, you'd be generating these from the front end where you're tracking, the visitor ID, you know, generating a UUID, or if a person is logged in to your streaming platform, you can track their episodes that they've watched, the start and end time, and then track that session time. As well as the last piece of the puzzle here is ratings.\u003C\u002Fp>\u003Cp>So within each show, ideally on the front end, you've got a like, dislike, or a a love interaction, so that that you can know which shows are resonating with the audience. So that is\u003C\u002Fp>\u003Cp>Speaker 0: Super nice.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It's super nice. Also a preview of of what's available, in our premium subscription, which is direct us plus, where you can get access to this data model along with others that you see up here, like headless LMS or PIM, status page, multi tenant SaaS that's not actually in this demo. So there's a a lot more on the other side for you. Directus plus comes with some, advanced workshops that, we run on a monthly basis as well.\u003C\u002Fp>\u003Cp>And I I really hope it's going to be tremendously valuable to the community.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That's awesome. Thank you so much for just taking a moment to show us through this. And then even though we've already covered code in a previous episode of digging the rabbit hole, reminder that our website is open source, and it is within our website site that Directus TV lives as you're thinking about implementing the front end for something like this. You've taken what I originally did with Directus TV and expanded it further.\u003C\u002Fp>\u003Cp>People being its own collection, which may be something we we end up needing to to do, adding ratings, adding sessions. Maybe this is stuff we'll do. I think we've already spoken about feedback and ratings at the very least. So it's really nice to see the thing that I built kind of enhanced a little bit further, and available on directors plus. And again, yeah, if you need that front end kind of inspiration or some code patterns that we've used, you can feel free to map those straight out of our site.\u003C\u002Fp>\u003Cp>I\u003C\u002Fp>\u003Cp>Speaker 1: totally forgot that that was even available.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Yeah. Absolutely. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Well, thanks again for bringing me on, Kevin, and, I I Thank you. Really enjoyed this series as a whole, and I love being part of Directus Plus. So kudos to you for bringing it all together.\u003C\u002Fp>\u003Cp>Speaker 0: Thank you very much. Directus Plus is not to do with me. I'll say thanks to you for Directors Plus, but I'll take the thanks for Directors TV. Thank you very much. Thank you very much for joining us for this season.\u003C\u002Fp>\u003Cp>I've been Kevin, says Brian, which you knew. I don't know why I decided to go out like this. I think we're just gonna hit stop record now. Bye.\u003C\u002Fp>","Hello, and welcome back to digging the rabbit hole. We've gone through this journey of talking about DIRECTV from its inception to its release, the processes we've created and some of the early feedback. I actually have brought Brian back on. Hello, Brian, you're back for let's call it an epilogue, talking about building your own versions of directors TV for your projects and organizations. Yeah. Happy to be back. Thanks for bringing me back on to just show this off. Right? One of the questions that you've already answered is, you know, what powers this thing? But we created a template for folks who want to dig a little deeper and maybe dig their own rabbit hole. So I'm just gonna walk you through the template just so you could see what's available. So the data model for the streaming platform, this very closely mirrors Directus TV itself. We have shows. So these are the actual collections of episodes. You could see here we've got some sample content like dev thoughts and a 100 apps, 100 hours. When you dig into that, you could see the title, the slug that you're using on the front end for the URL. We've got some announcement text and all these different images, that go on various This looks familiar. It does it? It should. Yeah. But rather than create sample content for this, I said, well, let's just steal what we already have. So, within that, you have your different seasons. And within the seasons, you have a number, a year, some actual episodes where this is where the the meat and potatoes of the content lives. A title description. We're using Vimeo to host those videos, so we've got a Vimeo ID. You could just as well upload a file into Directus and serve that or use a third party, YouTube or Wistia or some other service as well. Transcripts are available. We've got a tile. You can attach resources, and you can even add your different people, which is a relationship, which is really nice so that you don't have to keep typing those same people in every single episode. So we've got different collections there for the people. You can see you and I in there, a couple of handsome gentlemen. We have different categories. So interviews, code with us, short hops, these are the same ones on Directus TV. I'm sure they look familiar to you, Kevin. And then added a couple other things here for sessions. So, ideally, you'd be generating these from the front end where you're tracking, the visitor ID, you know, generating a UUID, or if a person is logged in to your streaming platform, you can track their episodes that they've watched, the start and end time, and then track that session time. As well as the last piece of the puzzle here is ratings. So within each show, ideally on the front end, you've got a like, dislike, or a a love interaction, so that that you can know which shows are resonating with the audience. So that is Super nice. Yeah. It's super nice. Also a preview of of what's available, in our premium subscription, which is direct us plus, where you can get access to this data model along with others that you see up here, like headless LMS or PIM, status page, multi tenant SaaS that's not actually in this demo. So there's a a lot more on the other side for you. Directus plus comes with some, advanced workshops that, we run on a monthly basis as well. And I I really hope it's going to be tremendously valuable to the community. Yeah. That's awesome. Thank you so much for just taking a moment to show us through this. And then even though we've already covered code in a previous episode of digging the rabbit hole, reminder that our website is open source, and it is within our website site that Directus TV lives as you're thinking about implementing the front end for something like this. You've taken what I originally did with Directus TV and expanded it further. People being its own collection, which may be something we we end up needing to to do, adding ratings, adding sessions. Maybe this is stuff we'll do. I think we've already spoken about feedback and ratings at the very least. So it's really nice to see the thing that I built kind of enhanced a little bit further, and available on directors plus. And again, yeah, if you need that front end kind of inspiration or some code patterns that we've used, you can feel free to map those straight out of our site. I totally forgot that that was even available. Yeah. Yeah. Yeah. Absolutely. Yeah. Well, thanks again for bringing me on, Kevin, and, I I Thank you. Really enjoyed this series as a whole, and I love being part of Directus Plus. So kudos to you for bringing it all together. Thank you very much. Directus Plus is not to do with me. I'll say thanks to you for Directors Plus, but I'll take the thanks for Directors TV. Thank you very much. Thank you very much for joining us for this season. I've been Kevin, says Brian, which you knew. I don't know why I decided to go out like this. I think we're just gonna hit stop record now. Bye.",[4400],"b12c7288-a4fe-430a-8bfa-c034200f27ad",[],{"year":978,"number":282,"id":4343,"episodes":4403,"show":4404},[4327,4345,4346,4347,4348,4349],{"title":4351,"slug":4352},{"id":4347,"slug":4406,"vimeo_id":4407,"description":4408,"tile":4409,"length":889,"resources":12,"people":4410,"episode_number":159,"published":4201,"title":4412,"video_transcript_html":4413,"video_transcript_text":4414,"content":12,"seo":12,"status":19,"episode_people":4415,"recommendations":4417,"season":4418},"platform","917617587","In this episode, Kevin walks through the code and backend of Directus TV.","d4b0a23c-8836-4bd4-89d8-ca82ea1654d5",[4411],{"name":2820,"url":2821},"The Platform","\u003Cp>Speaker 0: Hello, and welcome back to Digging the Rabbit Hole. In this episode, I'm gonna run through the codebase and the Director's project, which powers Directus TV to give you a little bit of an insight in how we use Directus at Directus to deliver Directus TV. Now before we jump into code and the Directus project, I basically wanna take some time to show you what I would call the 3 main pages of Directus TV. Firstly, we have this home page here. In the home page, we have this big global featured item here with a couple of buttons, and we also have these category sliders.\u003C\u002Fp>\u003Cp>Categories contain shows, and inside of shows, we have this show page. This is the 2nd page. This is an individual show listing. So here in trace talks, we have some information about the show. We have a button to play the latest episode, and then we have a listing of episodes.\u003C\u002Fp>\u003Cp>You'll also notice there are seasons. So seasons exist in the data model. A good one to demonstrate that is actually around the world here because we have 2 seasons. So here we have season 1, 2023, and we have season 2, 2024. Now the 3rd page, if we step into one of these, is the actual episode page.\u003C\u002Fp>\u003Cp>So in here, we have a video embed, which we'll talk about in a moment. We have a back to show button, a next episode button, which we'll talk about also, metadata about both the show and the episode, and a list of people, and a list of resources, and they both conditionally are shown or hidden depending on where the data exists there. So that's a little bit of a rundown of these pages. We have a home page. We have a show page.\u003C\u002Fp>\u003Cp>We have an episode page. Now let's look at the directors data model that powers this, and it's very, very similar to the pages. Firstly, we have shows. Each one of these is a different show, director scene scapes, quick connect, I made this, 100 apps in a 100 hours, and so on. Individual shows have a slug.\u003C\u002Fp>\u003Cp>This dictates the URL for that show. The title, a little one liner, which is what is actually shown here on the home page so people know what to expect from this show. A description, and then these three graphics. So we have a tile, a logo, and a cover. To show you how they relate here, if I go into a 100 apps in a 100 hours, we have the logo, we have the background here, and then we have the tile, which is, this view here.\u003C\u002Fp>\u003Cp>That's the tile. So we have the tile, the logo, and the cover. Next, we have seasons. Seasons are almost like a junction collection really. Every show has seasons.\u003C\u002Fp>\u003Cp>A season is just a show, a number, and a year. And then we have episodes. I've pre filtered this down here just to make sure that we're not getting a huge, huge list of shows, but just a manageable number here. So each individual episode, these are all the episodes of season 1 of Quick Connect, have a published date. This is useful for both displaying in the website, but also for RSS feeds.\u003C\u002Fp>\u003Cp>The status, because, of course, we upload all of these in bulk, and then we just change the status and the publish date and re rebuild the site on the day of publishing. Season and episode number, the slug for the URL and the title, the Vimeo ID, which again, we'll talk about in just a moment, a tile description, people, and resources. The tile is, if I go into quick connect, is this this here, each individual tile. And having tiles is useful, of course, for generating social tags, social meta tags. So that's a little rundown.\u003C\u002Fp>\u003Cp>Now before we continue, let's quickly talk about Vimeo. We use Vimeo to host our video for a number of reasons, primarily because they have a really good player that works cross platform and also will adapt to the needs of the viewer. If you have less, you know, less bandwidth, it will drop the quality of the video that you get delivered. And to be frank, I just didn't wanna build that. So it's good to lean on other platforms as appropriate.\u003C\u002Fp>\u003Cp>Finally, we have categories. These are for the homepage. So these are those sliders of categories. It just has a status, a title, and shows that can be sorted, and these can also be sorted which change the order of the categories and the order of the shows within a category. That's a little bit of a rundown of the director's project.\u003C\u002Fp>\u003Cp>Let's do a little rundown of the code now, and hopefully, it should all look reasonably, you know, reasonably sensible given the concepts that we've spoken about so far. So here we have the home page. In a home page, we have a hero. That's that top kind of graphic unit with the featured item, And this grabs data from the global's collection in director, so I didn't show that. Here is the global's collection, and there is a featured item there.\u003C\u002Fp>\u003Cp>So that populates the TV hero. And we also have the TV category, which lists all of the categories. Now this is a custom component here inside of TV. We have custom category, which are the sliders. We have the, the show, which are the individual tiles.\u003C\u002Fp>\u003Cp>We have the navigation, which goes in the hero. That's a separate item because on the on the actual episode page, it's not nested within the, within the hero, but on the other pages that is nested in the hero. So it's a it's a separate element. That's the hero. Then we have the individual episodes.\u003C\u002Fp>\u003Cp>This is for the show listing page with the horizontal I'll show you. With these, that is a TV show here. We use that in a few places. And so they kind of cascade. So on the home page here, we have the categories.\u003C\u002Fp>\u003Cp>The categories render the shows. Then over in an individual show page here, we have the hero unit once again. And then we have all of the oh, I'm I'm in the wrong section here. This is where I wanna be. We have the hero, and then we have, each of the seasons being looped over.\u003C\u002Fp>\u003Cp>And inside of that, we have each episode. Once again, we're just using directors. So we're grabbing the we're grabbing the show. We're grabbing the latest episode because that's the button here. So it's play latest episodes.\u003C\u002Fp>\u003Cp>So we wanna know what that is. We grab all the episodes. We grab all the seasons, and we jam those together here to create our data model. And once again, we just have some SEO meta tags here. In the individual episode page, it pins mostly on this iframe, of course, but we do have this additional data.\u003C\u002Fp>\u003Cp>We have back to show, and we also have what's coming up next. Now this is interesting. So the bottom will either say next season or next episode. It will say next season if it's the last episode in a season, or it will say next episode if there is another episode in this season that we're in now. We also just render all of the kind of static information for that episode.\u003C\u002Fp>\u003Cp>Looking down at the code here, we grab the episode data here based on whatever is in the URL, and we grab the next item. So the next item will firstly always be of this show. You know, if we're in a 100 apps in a 100 hours, we wanna return an episode of a 100 apps in a 100 hours. And either there will be, an episode in this season, but one episode up, or there won't be because we'll be in the last episode of a season. And in that case, we will want to get one season up and episode number 1.\u003C\u002Fp>\u003Cp>Right? And so next, we'll either end up with the next episode, the first episode of the next season, or nothing. And we can conditionally show that next button and what it says based on what is returned. That's a pretty cool use of directors' filter syntax there. And then just a little date for matter.\u003C\u002Fp>\u003Cp>Once again, just some just some SEO meta tags. So that's really Directus TV. Pretty, like, straightforward data model. The only thing I would perhaps do differently on reflection is I wouldn't, hard code people. I wouldn't hard code people here into each episode, but I would make that a relational collection.\u003C\u002Fp>\u003Cp>But that's fine. We can always enhance that in future. Because I wanna show you stuff that's relevant to everyone, we won't dig into Directus TV live today. But that is a a breakdown of how Directus TV works, both in terms of the mental models and the concepts of shows, seasons, episodes, and categories, how they relate to a data model and how they relate to a code base. So I hope you found this interesting, and we'll see you in the next episode.\u003C\u002Fp>","Hello, and welcome back to Digging the Rabbit Hole. In this episode, I'm gonna run through the codebase and the Director's project, which powers Directus TV to give you a little bit of an insight in how we use Directus at Directus to deliver Directus TV. Now before we jump into code and the Directus project, I basically wanna take some time to show you what I would call the 3 main pages of Directus TV. Firstly, we have this home page here. In the home page, we have this big global featured item here with a couple of buttons, and we also have these category sliders. Categories contain shows, and inside of shows, we have this show page. This is the 2nd page. This is an individual show listing. So here in trace talks, we have some information about the show. We have a button to play the latest episode, and then we have a listing of episodes. You'll also notice there are seasons. So seasons exist in the data model. A good one to demonstrate that is actually around the world here because we have 2 seasons. So here we have season 1, 2023, and we have season 2, 2024. Now the 3rd page, if we step into one of these, is the actual episode page. So in here, we have a video embed, which we'll talk about in a moment. We have a back to show button, a next episode button, which we'll talk about also, metadata about both the show and the episode, and a list of people, and a list of resources, and they both conditionally are shown or hidden depending on where the data exists there. So that's a little bit of a rundown of these pages. We have a home page. We have a show page. We have an episode page. Now let's look at the directors data model that powers this, and it's very, very similar to the pages. Firstly, we have shows. Each one of these is a different show, director scene scapes, quick connect, I made this, 100 apps in a 100 hours, and so on. Individual shows have a slug. This dictates the URL for that show. The title, a little one liner, which is what is actually shown here on the home page so people know what to expect from this show. A description, and then these three graphics. So we have a tile, a logo, and a cover. To show you how they relate here, if I go into a 100 apps in a 100 hours, we have the logo, we have the background here, and then we have the tile, which is, this view here. That's the tile. So we have the tile, the logo, and the cover. Next, we have seasons. Seasons are almost like a junction collection really. Every show has seasons. A season is just a show, a number, and a year. And then we have episodes. I've pre filtered this down here just to make sure that we're not getting a huge, huge list of shows, but just a manageable number here. So each individual episode, these are all the episodes of season 1 of Quick Connect, have a published date. This is useful for both displaying in the website, but also for RSS feeds. The status, because, of course, we upload all of these in bulk, and then we just change the status and the publish date and re rebuild the site on the day of publishing. Season and episode number, the slug for the URL and the title, the Vimeo ID, which again, we'll talk about in just a moment, a tile description, people, and resources. The tile is, if I go into quick connect, is this this here, each individual tile. And having tiles is useful, of course, for generating social tags, social meta tags. So that's a little rundown. Now before we continue, let's quickly talk about Vimeo. We use Vimeo to host our video for a number of reasons, primarily because they have a really good player that works cross platform and also will adapt to the needs of the viewer. If you have less, you know, less bandwidth, it will drop the quality of the video that you get delivered. And to be frank, I just didn't wanna build that. So it's good to lean on other platforms as appropriate. Finally, we have categories. These are for the homepage. So these are those sliders of categories. It just has a status, a title, and shows that can be sorted, and these can also be sorted which change the order of the categories and the order of the shows within a category. That's a little bit of a rundown of the director's project. Let's do a little rundown of the code now, and hopefully, it should all look reasonably, you know, reasonably sensible given the concepts that we've spoken about so far. So here we have the home page. In a home page, we have a hero. That's that top kind of graphic unit with the featured item, And this grabs data from the global's collection in director, so I didn't show that. Here is the global's collection, and there is a featured item there. So that populates the TV hero. And we also have the TV category, which lists all of the categories. Now this is a custom component here inside of TV. We have custom category, which are the sliders. We have the, the show, which are the individual tiles. We have the navigation, which goes in the hero. That's a separate item because on the on the actual episode page, it's not nested within the, within the hero, but on the other pages that is nested in the hero. So it's a it's a separate element. That's the hero. Then we have the individual episodes. This is for the show listing page with the horizontal I'll show you. With these, that is a TV show here. We use that in a few places. And so they kind of cascade. So on the home page here, we have the categories. The categories render the shows. Then over in an individual show page here, we have the hero unit once again. And then we have all of the oh, I'm I'm in the wrong section here. This is where I wanna be. We have the hero, and then we have, each of the seasons being looped over. And inside of that, we have each episode. Once again, we're just using directors. So we're grabbing the we're grabbing the show. We're grabbing the latest episode because that's the button here. So it's play latest episodes. So we wanna know what that is. We grab all the episodes. We grab all the seasons, and we jam those together here to create our data model. And once again, we just have some SEO meta tags here. In the individual episode page, it pins mostly on this iframe, of course, but we do have this additional data. We have back to show, and we also have what's coming up next. Now this is interesting. So the bottom will either say next season or next episode. It will say next season if it's the last episode in a season, or it will say next episode if there is another episode in this season that we're in now. We also just render all of the kind of static information for that episode. Looking down at the code here, we grab the episode data here based on whatever is in the URL, and we grab the next item. So the next item will firstly always be of this show. You know, if we're in a 100 apps in a 100 hours, we wanna return an episode of a 100 apps in a 100 hours. And either there will be, an episode in this season, but one episode up, or there won't be because we'll be in the last episode of a season. And in that case, we will want to get one season up and episode number 1. Right? And so next, we'll either end up with the next episode, the first episode of the next season, or nothing. And we can conditionally show that next button and what it says based on what is returned. That's a pretty cool use of directors' filter syntax there. And then just a little date for matter. Once again, just some just some SEO meta tags. So that's really Directus TV. Pretty, like, straightforward data model. The only thing I would perhaps do differently on reflection is I wouldn't, hard code people. I wouldn't hard code people here into each episode, but I would make that a relational collection. But that's fine. We can always enhance that in future. Because I wanna show you stuff that's relevant to everyone, we won't dig into Directus TV live today. But that is a a breakdown of how Directus TV works, both in terms of the mental models and the concepts of shows, seasons, episodes, and categories, how they relate to a data model and how they relate to a code base. So I hope you found this interesting, and we'll see you in the next episode.",[4416],"687c6fd3-0c41-47ab-8a08-7055d24d69c8",[],{"year":978,"number":282,"id":4343,"episodes":4419,"show":4420},[4327,4345,4346,4347,4348,4349],{"title":4351,"slug":4352},{"id":4345,"slug":4422,"vimeo_id":4423,"description":4424,"tile":4425,"length":305,"resources":12,"people":4426,"episode_number":177,"published":4201,"title":4429,"video_transcript_html":4430,"video_transcript_text":4431,"content":12,"seo":12,"status":19,"episode_people":4432,"recommendations":4435,"season":4436},"shows","918834415","In this episode, Bryant and Kevin discuss what shows are being commissioned and why. ","514df5ce-fbc9-41fc-972b-99da764eb893",[4427,4428],{"name":2820,"url":2821},{"name":3590,"url":3591},"The Shows","\u003Cp>Speaker 0: Hello, and welcome to the 2nd episode of Digging the Rabbit Hole. This is a series where we dive into the conception creation and initial feedback around directors TV, this platform, which you are watching this show on. And joining me today is my friend and colleague. Bryant, would you like to introduce yourself?\u003C\u002Fp>\u003Cp>Speaker 1: Yes. Yes. I so for a long time, I was the resident YouTube guy, but now that we've got some more faces, happy to be a part of the SIEV. I'm Bryant, a developer advocate here at Directus, and, yeah, create a ton of videos and content. You'll find me on the 100 apps series.\u003C\u002Fp>\u003Cp>Speaker 0: A 100 apps and some other upcoming series as well, short hops, the joy of theming, which will be announced the day this goes live so that these these exist, and others as well. Absolutely. In this chat, I wanted to talk a little bit about what actually gets made. We've already spoken about the conception of directors TV and in the future we'll talk about the process of actually getting stuff built. But there's this bit in the middle of actually thinking up shows and then as a team deciding which ones get commissioned into seasons.\u003C\u002Fp>\u003Cp>So I thought we could talk a little bit about that. And what better place to start? Yeah. Thank you. What better place to start than actually, I wanna talk about a 100 apps in a 100 hours.\u003C\u002Fp>\u003Cp>Could we actually open with you describing what the series is?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So a 100 apps, 100 hours is basically me speed running, building a full app or or getting as far along as I can using Directus and whatever other tools are available at that point, whether it's, Nux for the front end or, even I I think some of the episodes make some chat gpt in there to, like, speed things along, but it's definitely like an meant to be entertainment, and a good showcase of, like, what is actually possible with tools like Directus. Like, what can you achieve in an hour?\u003C\u002Fp>\u003Cp>Speaker 0: And it's it's actually quite novel as well because it isn't an educational series. It's not like step by step come with me. I figured it out. You know, I'm gonna show you how to do it. No.\u003C\u002Fp>\u003Cp>We're watching you sweat for an hour figuring out how to do\u003C\u002Fp>\u003Cp>Speaker 1: it. Yeah. I it like, when I first started, and I can't remember exactly what the like, the foundational, like, hey, this is what we're gonna do moment was. It was just like, hey, we're I wanna build some cool stuff. We're gonna record it.\u003C\u002Fp>\u003Cp>I I think that's how this started.\u003C\u002Fp>\u003Cp>Speaker 0: I actually looked back at the pitch note for a 100 apps in a 100 hours, and the show was definitely originally called Brian Build Cool Shit, and it turned into a 100 apps in a 100 hours. I looked at the history of the pitch, which is kinda funny.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. It's it's awesome to see how these things evolve. But, like, the I I can remember that, like, recording, like, the first couple of episodes, I was really, like, concerned because, you know, a lot of the other educational content that we do is is very polished and robust and, like, hey. We're gonna get you from point a to point b.\u003C\u002Fp>\u003Cp>But, like, what what they see in a 100 apps, 100 hours is is just me sweating through the real, like, warts and all. Like, there's there's no editing in between, like, the graphics and, like, the intros and everything. It's it's just, you know, one hour of me talking to myself as I build something.\u003C\u002Fp>\u003Cp>Speaker 0: And there are other show formats, of course. I think 100 Apps in a 100 Hours is probably one of the examples of a more novel format that we currently have. We definitely got some more in the works that I don't even think we will have announced by the time this show goes out. But we have, you know, I think quite a lot of our series at the moment are like straight up interview format. The specifics of what who is being interviewed and the topic in which they're being interviewed change from series to series.\u003C\u002Fp>\u003Cp>We have quite a lot of series based content trace talks where John and Pedro talk to engineering leaders about their career leading to being, you know, it's engineering leaders. There's learning things I love to hate, which is my series where it's nothing to do with directors. We just Yep. I I get people in who who are my friends, who know about tech that has some kind of reason I've been avoiding it and try and help me understand. And I hope that that helps the audience understand that's not conceited.\u003C\u002Fp>\u003Cp>I really do not know about these topics, and I really seek to learn about them in the time together.\u003C\u002Fp>\u003Cp>Speaker 1: I really dig the concept of that one of, like, hey, like, convince me. There's, like, that one meme that's like, there's a guy in the park, and it's like, hey. GraphQL is bad. Like, convince me, or GraphQL is great. Convince me.\u003C\u002Fp>\u003Cp>So I I love the concept of that one.\u003C\u002Fp>\u003Cp>Speaker 0: Do you wanna know what? Convincing is a degree. Like, the way that show opens is I say, right. Let me level set. Here's what I think I know about the topic.\u003C\u002Fp>\u003Cp>So we're not gonna go from 0. Like, here's what I think I know about the topic. Help me get to that. And here are my skepticism or uncertainties. Help me navigate those.\u003C\u002Fp>\u003Cp>So it's not even convincing. Like, we did an episode AI for devs, and I was like, I'm not not trying to convince me to use AI tools here in my development practice. I just wanna see how people use it and challenge some of the assumptions I have, which was successfully done. Web 3, it wasn't convinced me, you know, that I that I wanna build web 3 apps. It's I just do not understand how they they work.\u003C\u002Fp>\u003Cp>Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: It turned the light on for me. Exactly.\u003C\u002Fp>\u003Cp>Speaker 0: So that there's that series. There's, beyond the call where Esther interviews people who build directors extensions about the extensions that they've built in their journey, their journey using direct. So so they all have different vibes, but that is an interview format. Then we have quite instructional educational content like stack up or like quick connect, which is all about or make it real time, which are like step by step tutorials that you can pause and, you know, do yourself and and they are definitely more polished and considered.\u003C\u002Fp>\u003Cp>Speaker 1: I think, hey, like, one of the questions that I I probably already know the answer to this, but it, like, yeah, the expectations for Directus TV, right, would probably be like, hey. Here's a bunch of content that's exclusively focused on Directus. And, you know, as what the ringleader of the Directus TV circus, I guess you could call it, That's, hey, that's cool, I guess. It's very organized and and well constructed, but, you know, there's a lot of different content, and not all of it's specifically about direct us. Like Exactly.\u003C\u002Fp>\u003Cp>Was that decision made?\u003C\u002Fp>\u003Cp>Speaker 0: Because then you're only drawing people who already wanna know about direct us. You know, learning things I love to hate is an example of a show where I think in one of the 5 episodes, there is there is like an obvious lead back to direct assist the GraphQL episode. And it was about like the selective querying. And the fact that you can do that with direct to says REST API as well. But other than that, that is just a general interest series because we know you and I, as people who do developer relations, that we know what we want to watch, we know what our peers want to watch and find interesting.\u003C\u002Fp>\u003Cp>Don't get me wrong, enough of that content needs to help people discover and understand and be successful with and expand their usage of director. Sure. But that is not every show. At that point, you know, you're basically just building out tutorials and various ilks. And it's very self serving.\u003C\u002Fp>\u003Cp>And that isn't what we're doing here. We're trying to, you know, well, what are we trying to do? It depends on the show, right? So, you know, I think all of our shows exist to do one of a number of things or one or more of a number of things. To educate absolutely, to build relationships with with our guests for the interview series.\u003C\u002Fp>\u003Cp>We have to highlight stories from our community members and give them a platform where we have a reasonable sized platform. You know, we can we can boost them or simply to entertain because they're fun. And they're interesting. And there's more of those coming up too. So depends on the show, right?\u003C\u002Fp>\u003Cp>The balance needs to be that it's net positive for directors, but just by providing that content is net is net benefit.\u003C\u002Fp>\u003Cp>Speaker 1: So also that needs to be very interesting and fun to do some of these shows as well.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Absolutely. I'm having a blast putting shows together, whether I'm doing it from, like, an operational standpoint and other people are, like, recording raw footage and conceptualizing them or I'm the person putting them together as well. So we take some time to talk about and actually, I wanna ask you, I wanna set the scene. Answer ask the question answer the question and ask you what you think because I'm curious.\u003C\u002Fp>\u003Cp>Right? So in our in our next episode, we're gonna talk a bit about the process from pitch to release of a of a series. But just focusing in on that first step, we get an idea. And we have a space internally where we ask people to pitch internally. So not just ever, I'm not just marketing, but across the org, sales success engineering, great.\u003C\u002Fp>\u003Cp>Pitch a show idea, and we can help people, you know, navigate it. And every time they people want to pitch, we ask them to actually fill in a quite annoying format. Like we ask them to put in work upfront. We ask them to give us the rough concept. Think about the format.\u003C\u002Fp>\u003Cp>Think about who is the audience of this because not every show is for developers, I think especially not going forward. You know, many shows are there for technical people, but not all software developers. What does it aim to do for us? Like, what is what does directors get out of this? Are we gonna be educating, building relationships, you know, building positive community and so on or entertaining.\u003C\u002Fp>\u003Cp>We say, who is gonna be involved in this? Is it just you? Is it you and guests? Is it you and other colleagues and so on? What's the length of a season?\u003C\u002Fp>\u003Cp>Speaker 1: And It's crucial to that part.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I think so too. And outline the first three episodes. And that obviously requires stepping back and really having a think upfront. And I'm kind of curious, like, we also this information upfront to once we go, all share a vision of how the show is going to materialize.\u003C\u002Fp>\u003Cp>How has that been for you? I'm curious as someone who has pitched many shows. So,\u003C\u002Fp>\u003Cp>Speaker 1: it like, for me, ideas come from many different places. And, like, some of the pitches that I've put together have obviously been, like, very, I would call, weak just because it's like, hey. An idea pops into\u003C\u002Fp>\u003Cp>Speaker 0: my light on detail.\u003C\u002Fp>\u003Cp>Speaker 1: There you go. Light on detail. Right? Because it's either like, it's popped up by, a trigger on some other content that I was watching. Like, hey.\u003C\u002Fp>\u003Cp>This could be a, you know, like, a watching a game show, for instance. Oh, hey. This might make a interesting series around x, y, and z on direct to CV or on a walk with my kids. So as far as, like, the the pitching process, like, it creating the ideas, I take influences everywhere. So, like, the ideas never stop.\u003C\u002Fp>\u003Cp>As far as, like, which ones to pursue, I I think, like, I use it as a holding tank and then let it sit for a day. Yeah. And then if I come back if I come back to it and I'm like, hey. I really wanna pursue this. This is this is still good, then I would flesh it out.\u003C\u002Fp>\u003Cp>So there's a lot for me in there that are, like, half baked, But, you're not saying it's not a great idea, but just, like, other ideas were\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Better or or they take more time. Because, I mean, that's also worth noting. You know, the initial release of directors TV and the initial slate of shows, which I'll call the winter slate, right, the winter 2023 slate. As today, on the day of this release, we've announced our spring slate for 2024.\u003C\u002Fp>\u003Cp>They take a different amount of effort. When we launched directors TV, it was 6 weeks from conception to release. And there was quite a lot of new content. I think we created 8 new shows, we had some content already that we that we packaged up and released on directors TV as well. And that's how we kind of seeded our initial content.\u003C\u002Fp>\u003Cp>And our initial release was a ton of interview content because interview content doesn't take a lot of effort. Once you have a guest committed and you have a rough format outlined, which you can do in a couple of bullet points, you can rinse and repeat that that format and generate lots of fantastic content. Instructional content, however, takes a lot more time. 100 acts in a 100 hours is an incredible format. Like, I'm I'm so happy with how you've, like, formatted it because it is quite a high impact series, both in terms of entertainment and I would say education, not in terms of how to build use cases, but understanding that these use cases can be built in direct us.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I look at it as inspiration\u003C\u002Fp>\u003Cp>Speaker 0: is what I'm sure. But the in its very conception, the format is low on time. You know, there's maybe I I I mean, how much prep do you do before you hit record and spend an hour?\u003C\u002Fp>\u003Cp>Speaker 1: Do you know? On on things like Netflix or Airbnb, like, obviously, users of those applications, so I've got a fair bit of what's going on. For some of the other ones, like a PIM or some of the other use cases, just like a a quick Google search. And I wanted to keep it that way. And, you know, we've we've had a a fair bit of feedback from folks that are like, hey.\u003C\u002Fp>\u003Cp>I'm I'm expecting educational content, but that's kind of, like, you know, goes against the grain of the the concept of the show of, like What\u003C\u002Fp>\u003Cp>Speaker 0: what what do you mean? Feedback in a later episode, but 100 acts in a 100 hours will feature in that because you've hit you've hit on something really interesting around meeting expectations. But we're doing something new, so we're also having to rebuild the expectations and and correct expectations and\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Too. And that's that plays into, like, the ideas as well. It's like, hey. Is this gonna be well received?\u003C\u002Fp>\u003Cp>You know? One of the one of the other shows that I had a ton of fun recording was dev thoughts, which are, like, dev dad jokes, but, essentially, they were\u003C\u002Fp>\u003Cp>Speaker 0: 2 of them.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. They were stolen from SNL, the old, Jack Handy deep thoughts skit, for those who who are familiar with SNL. But, like, our audience is, you know, international. And without knowing that skit from SNL, does this land or not?\u003C\u002Fp>\u003Cp>You know? So there was there's a lot of that that factors into, like, should we pursue this show or not?\u003C\u002Fp>\u003Cp>Speaker 0: Absolutely. Absolutely. Yeah. No. I I I think you nailed it there.\u003C\u002Fp>\u003Cp>But what's interesting, though, is this feel like the initial release of director's TV was a little light on technical content. Actually, it was a lot of interviews and that's that is because it only took 6 weeks to materialize. But as we produce our as we release our spring slate and work on our summer and even our 4th slate at this point in time, really, we're getting to fill out these different content types, including more novel, really, really, I think quite exciting formats that are just gonna I think they're gonna be hits like I really I the things I would love to watch. And so, yeah, I'm just so I'm so excited for folks to enjoy our spring slate of shows and to see what else we have coming. Some of those more novel formats, though, will take a lot more production time.\u003C\u002Fp>\u003Cp>And that's why now that has to be factored in to the length of a season to whether it can even go ahead and the timeline on which we can produce it as well. Thank you, Brian, for joining me. I've had a blast talking about this.\u003C\u002Fp>\u003Cp>Speaker 1: Well, this is the first time that we've this is kind of like our postmortem internally. Right? Because we developed this thing in 6 weeks, hit the ground running, and and since then, it's been both feet on the gas. Like, hey. Let's keep going.\u003C\u002Fp>\u003Cp>You know, let's deliver new content. So We're we're now where we're at is is been yeah. This is fun.\u003C\u002Fp>\u003Cp>Speaker 0: I agree. And, yeah, I we're now at the point where we have enough content that is produced that is going out. You know, it's hit it's it's done. It's it's the editing is finished. It's ready, but it is scheduled to go when it goes.\u003C\u002Fp>\u003Cp>But we are now looking ahead. And so we have time now and only now really to to look at and talk about how this all came about. And I I I'm talking about it. It's quite nice to, you know, revalidate some of those decisions we previously made. And as we finish out the season with some initial feedback from our audience and our communities and our customers, It'll be interesting to see how the future of directors TV may, even if it's just process driven or, you know, changing what we commission or the ratios of education versus entertainment versus interview and, you know, community highlighting and all of that, how that may change over time.\u003C\u002Fp>\u003Cp>Last month, I looked at the numbers. Last month, we released over 8 shows, we released 27 episodes and about 12 hours of content, which is wild. Mind blowing. Exactly. Right?\u003C\u002Fp>\u003Cp>Exactly. Wicked, thank you so much for joining me, and thank you so much for joining us. We'll see you in the next episode. Bye for now. Yeah.\u003C\u002Fp>\u003Cp>Thanks, Kevin.\u003C\u002Fp>","Hello, and welcome to the 2nd episode of Digging the Rabbit Hole. This is a series where we dive into the conception creation and initial feedback around directors TV, this platform, which you are watching this show on. And joining me today is my friend and colleague. Bryant, would you like to introduce yourself? Yes. Yes. I so for a long time, I was the resident YouTube guy, but now that we've got some more faces, happy to be a part of the SIEV. I'm Bryant, a developer advocate here at Directus, and, yeah, create a ton of videos and content. You'll find me on the 100 apps series. A 100 apps and some other upcoming series as well, short hops, the joy of theming, which will be announced the day this goes live so that these these exist, and others as well. Absolutely. In this chat, I wanted to talk a little bit about what actually gets made. We've already spoken about the conception of directors TV and in the future we'll talk about the process of actually getting stuff built. But there's this bit in the middle of actually thinking up shows and then as a team deciding which ones get commissioned into seasons. So I thought we could talk a little bit about that. And what better place to start? Yeah. Thank you. What better place to start than actually, I wanna talk about a 100 apps in a 100 hours. Could we actually open with you describing what the series is? Yeah. So a 100 apps, 100 hours is basically me speed running, building a full app or or getting as far along as I can using Directus and whatever other tools are available at that point, whether it's, Nux for the front end or, even I I think some of the episodes make some chat gpt in there to, like, speed things along, but it's definitely like an meant to be entertainment, and a good showcase of, like, what is actually possible with tools like Directus. Like, what can you achieve in an hour? And it's it's actually quite novel as well because it isn't an educational series. It's not like step by step come with me. I figured it out. You know, I'm gonna show you how to do it. No. We're watching you sweat for an hour figuring out how to do it. Yeah. I it like, when I first started, and I can't remember exactly what the like, the foundational, like, hey, this is what we're gonna do moment was. It was just like, hey, we're I wanna build some cool stuff. We're gonna record it. I I think that's how this started. I actually looked back at the pitch note for a 100 apps in a 100 hours, and the show was definitely originally called Brian Build Cool Shit, and it turned into a 100 apps in a 100 hours. I looked at the history of the pitch, which is kinda funny. Yeah. Yeah. It's it's awesome to see how these things evolve. But, like, the I I can remember that, like, recording, like, the first couple of episodes, I was really, like, concerned because, you know, a lot of the other educational content that we do is is very polished and robust and, like, hey. We're gonna get you from point a to point b. But, like, what what they see in a 100 apps, 100 hours is is just me sweating through the real, like, warts and all. Like, there's there's no editing in between, like, the graphics and, like, the intros and everything. It's it's just, you know, one hour of me talking to myself as I build something. And there are other show formats, of course. I think 100 Apps in a 100 Hours is probably one of the examples of a more novel format that we currently have. We definitely got some more in the works that I don't even think we will have announced by the time this show goes out. But we have, you know, I think quite a lot of our series at the moment are like straight up interview format. The specifics of what who is being interviewed and the topic in which they're being interviewed change from series to series. We have quite a lot of series based content trace talks where John and Pedro talk to engineering leaders about their career leading to being, you know, it's engineering leaders. There's learning things I love to hate, which is my series where it's nothing to do with directors. We just Yep. I I get people in who who are my friends, who know about tech that has some kind of reason I've been avoiding it and try and help me understand. And I hope that that helps the audience understand that's not conceited. I really do not know about these topics, and I really seek to learn about them in the time together. I really dig the concept of that one of, like, hey, like, convince me. There's, like, that one meme that's like, there's a guy in the park, and it's like, hey. GraphQL is bad. Like, convince me, or GraphQL is great. Convince me. So I I love the concept of that one. Do you wanna know what? Convincing is a degree. Like, the way that show opens is I say, right. Let me level set. Here's what I think I know about the topic. So we're not gonna go from 0. Like, here's what I think I know about the topic. Help me get to that. And here are my skepticism or uncertainties. Help me navigate those. So it's not even convincing. Like, we did an episode AI for devs, and I was like, I'm not not trying to convince me to use AI tools here in my development practice. I just wanna see how people use it and challenge some of the assumptions I have, which was successfully done. Web 3, it wasn't convinced me, you know, that I that I wanna build web 3 apps. It's I just do not understand how they they work. Yeah. It turned the light on for me. Exactly. So that there's that series. There's, beyond the call where Esther interviews people who build directors extensions about the extensions that they've built in their journey, their journey using direct. So so they all have different vibes, but that is an interview format. Then we have quite instructional educational content like stack up or like quick connect, which is all about or make it real time, which are like step by step tutorials that you can pause and, you know, do yourself and and they are definitely more polished and considered. I think, hey, like, one of the questions that I I probably already know the answer to this, but it, like, yeah, the expectations for Directus TV, right, would probably be like, hey. Here's a bunch of content that's exclusively focused on Directus. And, you know, as what the ringleader of the Directus TV circus, I guess you could call it, That's, hey, that's cool, I guess. It's very organized and and well constructed, but, you know, there's a lot of different content, and not all of it's specifically about direct us. Like Exactly. Was that decision made? Because then you're only drawing people who already wanna know about direct us. You know, learning things I love to hate is an example of a show where I think in one of the 5 episodes, there is there is like an obvious lead back to direct assist the GraphQL episode. And it was about like the selective querying. And the fact that you can do that with direct to says REST API as well. But other than that, that is just a general interest series because we know you and I, as people who do developer relations, that we know what we want to watch, we know what our peers want to watch and find interesting. Don't get me wrong, enough of that content needs to help people discover and understand and be successful with and expand their usage of director. Sure. But that is not every show. At that point, you know, you're basically just building out tutorials and various ilks. And it's very self serving. And that isn't what we're doing here. We're trying to, you know, well, what are we trying to do? It depends on the show, right? So, you know, I think all of our shows exist to do one of a number of things or one or more of a number of things. To educate absolutely, to build relationships with with our guests for the interview series. We have to highlight stories from our community members and give them a platform where we have a reasonable sized platform. You know, we can we can boost them or simply to entertain because they're fun. And they're interesting. And there's more of those coming up too. So depends on the show, right? The balance needs to be that it's net positive for directors, but just by providing that content is net is net benefit. So also that needs to be very interesting and fun to do some of these shows as well. Yeah. Absolutely. I'm having a blast putting shows together, whether I'm doing it from, like, an operational standpoint and other people are, like, recording raw footage and conceptualizing them or I'm the person putting them together as well. So we take some time to talk about and actually, I wanna ask you, I wanna set the scene. Answer ask the question answer the question and ask you what you think because I'm curious. Right? So in our in our next episode, we're gonna talk a bit about the process from pitch to release of a of a series. But just focusing in on that first step, we get an idea. And we have a space internally where we ask people to pitch internally. So not just ever, I'm not just marketing, but across the org, sales success engineering, great. Pitch a show idea, and we can help people, you know, navigate it. And every time they people want to pitch, we ask them to actually fill in a quite annoying format. Like we ask them to put in work upfront. We ask them to give us the rough concept. Think about the format. Think about who is the audience of this because not every show is for developers, I think especially not going forward. You know, many shows are there for technical people, but not all software developers. What does it aim to do for us? Like, what is what does directors get out of this? Are we gonna be educating, building relationships, you know, building positive community and so on or entertaining. We say, who is gonna be involved in this? Is it just you? Is it you and guests? Is it you and other colleagues and so on? What's the length of a season? And It's crucial to that part. Yeah. I think so too. And outline the first three episodes. And that obviously requires stepping back and really having a think upfront. And I'm kind of curious, like, we also this information upfront to once we go, all share a vision of how the show is going to materialize. How has that been for you? I'm curious as someone who has pitched many shows. So, it like, for me, ideas come from many different places. And, like, some of the pitches that I've put together have obviously been, like, very, I would call, weak just because it's like, hey. An idea pops into my light on detail. There you go. Light on detail. Right? Because it's either like, it's popped up by, a trigger on some other content that I was watching. Like, hey. This could be a, you know, like, a watching a game show, for instance. Oh, hey. This might make a interesting series around x, y, and z on direct to CV or on a walk with my kids. So as far as, like, the the pitching process, like, it creating the ideas, I take influences everywhere. So, like, the ideas never stop. As far as, like, which ones to pursue, I I think, like, I use it as a holding tank and then let it sit for a day. Yeah. And then if I come back if I come back to it and I'm like, hey. I really wanna pursue this. This is this is still good, then I would flesh it out. So there's a lot for me in there that are, like, half baked, But, you're not saying it's not a great idea, but just, like, other ideas were Yeah. Better or or they take more time. Because, I mean, that's also worth noting. You know, the initial release of directors TV and the initial slate of shows, which I'll call the winter slate, right, the winter 2023 slate. As today, on the day of this release, we've announced our spring slate for 2024. They take a different amount of effort. When we launched directors TV, it was 6 weeks from conception to release. And there was quite a lot of new content. I think we created 8 new shows, we had some content already that we that we packaged up and released on directors TV as well. And that's how we kind of seeded our initial content. And our initial release was a ton of interview content because interview content doesn't take a lot of effort. Once you have a guest committed and you have a rough format outlined, which you can do in a couple of bullet points, you can rinse and repeat that that format and generate lots of fantastic content. Instructional content, however, takes a lot more time. 100 acts in a 100 hours is an incredible format. Like, I'm I'm so happy with how you've, like, formatted it because it is quite a high impact series, both in terms of entertainment and I would say education, not in terms of how to build use cases, but understanding that these use cases can be built in direct us. Yeah. I look at it as inspiration is what I'm sure. But the in its very conception, the format is low on time. You know, there's maybe I I I mean, how much prep do you do before you hit record and spend an hour? Do you know? On on things like Netflix or Airbnb, like, obviously, users of those applications, so I've got a fair bit of what's going on. For some of the other ones, like a PIM or some of the other use cases, just like a a quick Google search. And I wanted to keep it that way. And, you know, we've we've had a a fair bit of feedback from folks that are like, hey. I'm I'm expecting educational content, but that's kind of, like, you know, goes against the grain of the the concept of the show of, like What what what do you mean? Feedback in a later episode, but 100 acts in a 100 hours will feature in that because you've hit you've hit on something really interesting around meeting expectations. But we're doing something new, so we're also having to rebuild the expectations and and correct expectations and Yeah. Too. And that's that plays into, like, the ideas as well. It's like, hey. Is this gonna be well received? You know? One of the one of the other shows that I had a ton of fun recording was dev thoughts, which are, like, dev dad jokes, but, essentially, they were 2 of them. Yeah. Yeah. They were stolen from SNL, the old, Jack Handy deep thoughts skit, for those who who are familiar with SNL. But, like, our audience is, you know, international. And without knowing that skit from SNL, does this land or not? You know? So there was there's a lot of that that factors into, like, should we pursue this show or not? Absolutely. Absolutely. Yeah. No. I I I think you nailed it there. But what's interesting, though, is this feel like the initial release of director's TV was a little light on technical content. Actually, it was a lot of interviews and that's that is because it only took 6 weeks to materialize. But as we produce our as we release our spring slate and work on our summer and even our 4th slate at this point in time, really, we're getting to fill out these different content types, including more novel, really, really, I think quite exciting formats that are just gonna I think they're gonna be hits like I really I the things I would love to watch. And so, yeah, I'm just so I'm so excited for folks to enjoy our spring slate of shows and to see what else we have coming. Some of those more novel formats, though, will take a lot more production time. And that's why now that has to be factored in to the length of a season to whether it can even go ahead and the timeline on which we can produce it as well. Thank you, Brian, for joining me. I've had a blast talking about this. Well, this is the first time that we've this is kind of like our postmortem internally. Right? Because we developed this thing in 6 weeks, hit the ground running, and and since then, it's been both feet on the gas. Like, hey. Let's keep going. You know, let's deliver new content. So We're we're now where we're at is is been yeah. This is fun. I agree. And, yeah, I we're now at the point where we have enough content that is produced that is going out. You know, it's hit it's it's done. It's it's the editing is finished. It's ready, but it is scheduled to go when it goes. But we are now looking ahead. And so we have time now and only now really to to look at and talk about how this all came about. And I I I'm talking about it. It's quite nice to, you know, revalidate some of those decisions we previously made. And as we finish out the season with some initial feedback from our audience and our communities and our customers, It'll be interesting to see how the future of directors TV may, even if it's just process driven or, you know, changing what we commission or the ratios of education versus entertainment versus interview and, you know, community highlighting and all of that, how that may change over time. Last month, I looked at the numbers. Last month, we released over 8 shows, we released 27 episodes and about 12 hours of content, which is wild. Mind blowing. Exactly. Right? Exactly. Wicked, thank you so much for joining me, and thank you so much for joining us. We'll see you in the next episode. Bye for now. Yeah. Thanks, Kevin.",[4433,4434],"3a36ddcb-22b2-4151-b906-4e3149830252","a9323e06-ac6d-4d41-9b02-4520cd9e0546",[],{"year":978,"number":282,"id":4343,"episodes":4437,"show":4438},[4327,4345,4346,4347,4348,4349],{"title":4351,"slug":4352},{"id":4217,"slug":4440,"vimeo_id":4441,"description":4442,"tile":4443,"length":13,"resources":12,"people":4444,"episode_number":11,"published":4201,"title":4448,"video_transcript_html":4449,"video_transcript_text":4450,"content":12,"seo":12,"status":19,"episode_people":4451,"recommendations":4453,"season":4454},"02-partners","918865399","Today we are launching the new Directus Partner Program, specifically tailored for agencies. ","a9a49a74-5d63-4286-a506-d40269eac812",[4445],{"name":4446,"url":4447},"Colton Schmidt","https:\u002F\u002Fdirectus.io\u002Fteam\u002Fcolton-schmidt","New Directus Partner Program","\u003Cp>Speaker 0: Today, we're announcing our new partner program focused on agencies. Agency partners are a super important part of how we reach our customers. We've designed this program in conjunction with our current partners, and the program will enable agencies and client projects of all shapes and sizes how we think you're gonna love it. We now have a formal certification program to help you get the most from Directus and understand which projects it is suitable for. And, of course, there will also be a larger resource center available, which can help you when discussing Directus with your customers.\u003C\u002Fp>\u003Cp>Our team is available for advisory calls to validate your approaches, suggest new ones, and sell alongside your team. And, of course, the program includes revenue sharing, both upfront and renewals. The new partner directory will help companies looking for experts to implement their projects. And with co marketing opportunities, we can help share all your stories. This isn't all, but it captures the most important parts of the partner program.\u003C\u002Fp>\u003Cp>We really think you're gonna love it. You can find out more at directus.io\u002Fpartners. And later this year, we'll also be expanding our program to include technical partners. But that's for another day.\u003C\u002Fp>","Today, we're announcing our new partner program focused on agencies. Agency partners are a super important part of how we reach our customers. We've designed this program in conjunction with our current partners, and the program will enable agencies and client projects of all shapes and sizes how we think you're gonna love it. We now have a formal certification program to help you get the most from Directus and understand which projects it is suitable for. And, of course, there will also be a larger resource center available, which can help you when discussing Directus with your customers. Our team is available for advisory calls to validate your approaches, suggest new ones, and sell alongside your team. And, of course, the program includes revenue sharing, both upfront and renewals. The new partner directory will help companies looking for experts to implement their projects. And with co marketing opportunities, we can help share all your stories. This isn't all, but it captures the most important parts of the partner program. We really think you're gonna love it. You can find out more at directus.io\u002Fpartners. And later this year, we'll also be expanding our program to include technical partners. But that's for another day.",[4452],"507e972c-e0d3-4f0e-b492-4ae1fad6d9de",[],{"year":978,"number":2280,"id":4209,"episodes":4455,"show":4456},[4211,4212,4213,4214,4215,4216,4192,4217,4218],{"title":151,"slug":152},{"id":4346,"slug":4458,"vimeo_id":4459,"description":4460,"tile":4461,"length":129,"resources":12,"people":4462,"episode_number":264,"published":4201,"title":4464,"video_transcript_html":4465,"video_transcript_text":4466,"content":12,"seo":12,"status":19,"episode_people":4467,"recommendations":4469,"season":4470},"production","918868401","In this episode, Kevin discusses the efficient processes created to enable show creation at scale. ","4bce4584-b5b0-4002-bbee-c3836c8b716f",[4463],{"name":2820,"url":2821},"The Production","\u003Cp>Speaker 0: Hello, and welcome back to Digging the Rabbit Hole. And in this episode, we're going to talk a bit about how a show goes from a pitch through to release. Now in the last episode, we discussed that very first part. We discussed what a pitch looks like and why we may choose to commission a show over others. But there is obviously a really big gap between agreeing to do something and getting it out the door.\u003C\u002Fp>\u003Cp>Let's start by talking a bit about some of our team's strengths, because I think that helps contextualize the process we've created as well as some of our shortcomings. So, our strengths. Right. Really good at making video, actually. Really good at taking something from idea, creating the graphics required, actually filming, editing, and finally, publishing content.\u003C\u002Fp>\u003Cp>We have a few people in the organization who are good at this from various teams. So the ability to create video was never a challenge. And in fact, I would go as far as to say, many of our team have an affinity towards creating video content, but there are some challenges. Firstly, all of those steps that I just described take a lot of time. And in fact, time is the challenge with this project.\u003C\u002Fp>\u003Cp>We don't have a dedicated person running directors TV, and indeed, it would always require a cross team effort, right, to make sure shows have, you know, a few different faces involved in them. Being a 35 person company across the board from core engineering through to marketing sales, you know, general administrative over in developer relations, all of us, 35 people, is that we move really, really quickly. But by moving quickly, projects that you have to keep returning to are often the projects, that get left behind, and indeed, this has happened. There are video series on our YouTube channel, where there were 2 or 3 episodes, and then we simply run out of time. There was always something more important, more pressing, you know, more impactful to do, and those series were just left, unloved.\u003C\u002Fp>\u003Cp>So that is a little bit of context as to our team. Really good at making video, but it takes a lot of time and time is the enemy. So what did we do? We created a process which basically involves batching work. When an idea is pitched, it is pitched with a number of episodes that will be produced or that are being pitched as part of that series.\u003C\u002Fp>\u003Cp>Sometimes that's a negotiation, you know, sometimes we pair it back, Sometimes we will increase it and so on, but we have a clear idea what is going to be created and how many episodes will be released. We have a big content calendar. It's basically populated now, like, 4 or 5 months ahead of time, which is lovely. So, when an idea is accepted, it also comes with a release date, and that release date will be months away realistically for most shows. From there, we work back.\u003C\u002Fp>\u003Cp>A week before that is the editing deadline, and then generally, like, 2 to 4 weeks before that, depending on the series and the complexity of the edit, is the deadline for all the footage. Instead and this is the key. Instead All of the recording happens together. Then we have an amazing editor, Nat. He was gonna join me on this episode, but he's a little unwell.\u003C\u002Fp>\u003Cp>And that will basically then get the whole package. 100 acts in a 100 hours an episode a series is 10 episodes. He gets all 10 episodes raw recording at once. Now the team don't need to care about the editing so much. We just have to care about producing the raw footage.\u003C\u002Fp>\u003Cp>We hand it off, and that creates 1 episode if it's a new show for a look and feel so we can approve it, make sure it feels good, tweak it, and so on, and then he can go and batch edit them all. Batch upload them all, batch prepare them all inside of the director's instance that runs director's TV. More about that in the next episode. And then each day, we simply release, distribute, and share. And that allows us to pour our full energy into making sure that people are actually watching them, that we're actually doing episodes justice by sharing them far and wide in communities that will find that content interesting.\u003C\u002Fp>\u003Cp>And that's really it. That is the process. So stuff goes from a pitch to being approved, then the planning portion comes in. Some shows, you know, I I have to go out and find guests, for example, then there's a filming part. Once the filming part has concluded, there is an editing part, and then we go ahead load it all into the director's project that runs director's TV, and we go ahead and release it.\u003C\u002Fp>\u003Cp>And so, yeah, I wanted to talk just very briefly about this process we've created because it is what enables this absolute powerhouse of a content production machine is this process. Now don't get me wrong. That isn't without its challenges to actually be able to block out time to using a 100 apps and a 100 hours as an example to do the minimal preparation for each episode and then record it. I mean, you're talking now half a week completely carved out or, you know, half a day every day for a week or however that may may be organized. But you are we're demanding more time of our team, but just in a batch.\u003C\u002Fp>\u003Cp>And then they know I don't have to worry about finding the time for this series, on an ongoing basis because that's then taken care of, and centralized as well. So that's a little bit, about how it works. We also have a design team who managed to create all of the lovely graphics for each of our shows. We're giving each show its own unique identity look and feel while making sure they look good as a collective when presented with each other. And I think that's kind of it.\u003C\u002Fp>\u003Cp>Yeah. I just wanted to give a little bit of a rundown about this process we've created, about how we kind of batch the work. And the other fantastic thing about batching work is it allows us to do what we have done today on the release day of this episode, which is, release into you know, know what's coming, an entire slate of shows, and the confidence that we will be able to deliver that slate because all the recordings done, Most of the editing is done, and then it's all just, you know, batch kind of admin work. So this was an episode all about the process. I hope you have enjoyed it, and we'll see you in the next episode of digging the rabbit hole.\u003C\u002Fp>\u003Cp>Bye for now.\u003C\u002Fp>","Hello, and welcome back to Digging the Rabbit Hole. And in this episode, we're going to talk a bit about how a show goes from a pitch through to release. Now in the last episode, we discussed that very first part. We discussed what a pitch looks like and why we may choose to commission a show over others. But there is obviously a really big gap between agreeing to do something and getting it out the door. Let's start by talking a bit about some of our team's strengths, because I think that helps contextualize the process we've created as well as some of our shortcomings. So, our strengths. Right. Really good at making video, actually. Really good at taking something from idea, creating the graphics required, actually filming, editing, and finally, publishing content. We have a few people in the organization who are good at this from various teams. So the ability to create video was never a challenge. And in fact, I would go as far as to say, many of our team have an affinity towards creating video content, but there are some challenges. Firstly, all of those steps that I just described take a lot of time. And in fact, time is the challenge with this project. We don't have a dedicated person running directors TV, and indeed, it would always require a cross team effort, right, to make sure shows have, you know, a few different faces involved in them. Being a 35 person company across the board from core engineering through to marketing sales, you know, general administrative over in developer relations, all of us, 35 people, is that we move really, really quickly. But by moving quickly, projects that you have to keep returning to are often the projects, that get left behind, and indeed, this has happened. There are video series on our YouTube channel, where there were 2 or 3 episodes, and then we simply run out of time. There was always something more important, more pressing, you know, more impactful to do, and those series were just left, unloved. So that is a little bit of context as to our team. Really good at making video, but it takes a lot of time and time is the enemy. So what did we do? We created a process which basically involves batching work. When an idea is pitched, it is pitched with a number of episodes that will be produced or that are being pitched as part of that series. Sometimes that's a negotiation, you know, sometimes we pair it back, Sometimes we will increase it and so on, but we have a clear idea what is going to be created and how many episodes will be released. We have a big content calendar. It's basically populated now, like, 4 or 5 months ahead of time, which is lovely. So, when an idea is accepted, it also comes with a release date, and that release date will be months away realistically for most shows. From there, we work back. A week before that is the editing deadline, and then generally, like, 2 to 4 weeks before that, depending on the series and the complexity of the edit, is the deadline for all the footage. Instead and this is the key. Instead All of the recording happens together. Then we have an amazing editor, Nat. He was gonna join me on this episode, but he's a little unwell. And that will basically then get the whole package. 100 acts in a 100 hours an episode a series is 10 episodes. He gets all 10 episodes raw recording at once. Now the team don't need to care about the editing so much. We just have to care about producing the raw footage. We hand it off, and that creates 1 episode if it's a new show for a look and feel so we can approve it, make sure it feels good, tweak it, and so on, and then he can go and batch edit them all. Batch upload them all, batch prepare them all inside of the director's instance that runs director's TV. More about that in the next episode. And then each day, we simply release, distribute, and share. And that allows us to pour our full energy into making sure that people are actually watching them, that we're actually doing episodes justice by sharing them far and wide in communities that will find that content interesting. And that's really it. That is the process. So stuff goes from a pitch to being approved, then the planning portion comes in. Some shows, you know, I I have to go out and find guests, for example, then there's a filming part. Once the filming part has concluded, there is an editing part, and then we go ahead load it all into the director's project that runs director's TV, and we go ahead and release it. And so, yeah, I wanted to talk just very briefly about this process we've created because it is what enables this absolute powerhouse of a content production machine is this process. Now don't get me wrong. That isn't without its challenges to actually be able to block out time to using a 100 apps and a 100 hours as an example to do the minimal preparation for each episode and then record it. I mean, you're talking now half a week completely carved out or, you know, half a day every day for a week or however that may may be organized. But you are we're demanding more time of our team, but just in a batch. And then they know I don't have to worry about finding the time for this series, on an ongoing basis because that's then taken care of, and centralized as well. So that's a little bit, about how it works. We also have a design team who managed to create all of the lovely graphics for each of our shows. We're giving each show its own unique identity look and feel while making sure they look good as a collective when presented with each other. And I think that's kind of it. Yeah. I just wanted to give a little bit of a rundown about this process we've created, about how we kind of batch the work. And the other fantastic thing about batching work is it allows us to do what we have done today on the release day of this episode, which is, release into you know, know what's coming, an entire slate of shows, and the confidence that we will be able to deliver that slate because all the recordings done, Most of the editing is done, and then it's all just, you know, batch kind of admin work. So this was an episode all about the process. I hope you have enjoyed it, and we'll see you in the next episode of digging the rabbit hole. Bye for now.",[4468],"c62bcadd-1e08-4a15-8b64-1ef02da14c0d",[],{"year":978,"number":282,"id":4343,"episodes":4471,"show":4472},[4327,4345,4346,4347,4348,4349],{"title":4351,"slug":4352},{"id":4215,"slug":4474,"vimeo_id":4475,"description":4476,"tile":4477,"length":177,"resources":12,"people":4478,"episode_number":213,"published":4201,"title":4480,"video_transcript_html":4481,"video_transcript_text":4482,"content":12,"seo":12,"status":19,"episode_people":4483,"recommendations":4485,"season":4486},"02-payments-hackathon","919062107","This March we invite you to showcase your creative and technical skills to build and publish extensions related to payments and billing.","40c53f0e-9285-44c8-88d9-88bde7d59973",[4479],{"name":3798,"url":3799},"Directus Payments Hackathon","\u003Cp>Speaker 0: I get to tell you about the newest hackathon. I'm so excited. The hackathons have brought new use cases, new capabilities to exist to the previous hackathons. We've seen and learned some really cool things about what people the concepts and ideas that someone might come up with. And so this particular hackathon is going to be focused around one of our biggest, upvotes kind of in the community is around payments.\u003C\u002Fp>\u003Cp>So we'd like to see the director's payments hackathon. We'd like to see you build and publish in the new marketplace. I'm so excited about the marketplace. Think through the scenarios and the use cases, you know, how would I deliver coupons? How would I deliver trials?\u003C\u002Fp>\u003Cp>What happens when payments fail? What happens when deployments fail or some issue happens during that process? Make sure you've got handling logic around those kinds of things. Include reporting, include any, extensions or, data details that are valuable that you find in your day to day interactions, with payment platforms and how you want that to operate. You'll have until the end of March to submit that you'll find all of the details around prizes and information about how and when and what to submit, on direct to style IO slash hackathons.\u003C\u002Fp>\u003Cp>And you'll find links throughout the documentation and things that we'll share through the coming days.\u003C\u002Fp>","I get to tell you about the newest hackathon. I'm so excited. The hackathons have brought new use cases, new capabilities to exist to the previous hackathons. We've seen and learned some really cool things about what people the concepts and ideas that someone might come up with. And so this particular hackathon is going to be focused around one of our biggest, upvotes kind of in the community is around payments. So we'd like to see the director's payments hackathon. We'd like to see you build and publish in the new marketplace. I'm so excited about the marketplace. Think through the scenarios and the use cases, you know, how would I deliver coupons? How would I deliver trials? What happens when payments fail? What happens when deployments fail or some issue happens during that process? Make sure you've got handling logic around those kinds of things. Include reporting, include any, extensions or, data details that are valuable that you find in your day to day interactions, with payment platforms and how you want that to operate. You'll have until the end of March to submit that you'll find all of the details around prizes and information about how and when and what to submit, on direct to style IO slash hackathons. And you'll find links throughout the documentation and things that we'll share through the coming days.",[4484],"01905479-c9d8-4694-87bc-0c210eb29e35",[],{"year":978,"number":2280,"id":4209,"episodes":4487,"show":4488},[4211,4212,4213,4214,4215,4216,4192,4217,4218],{"title":151,"slug":152},{"id":1792,"slug":4490,"vimeo_id":4491,"description":4492,"tile":4493,"length":2550,"resources":4494,"people":4498,"episode_number":159,"published":4502,"title":4503,"video_transcript_html":4504,"video_transcript_text":4505,"content":12,"seo":12,"status":19,"episode_people":4506,"recommendations":4510,"season":4511},"conditional-fields-nested-relations","916080169","In this recording of our live event on February 22 2024, Rijk, Jonathan, and Daniel discuss the support of nested relational values in conditional fields to build more flexible content editors","4764c83c-2464-4764-8ef3-9d310502315d",[4495],{"name":4496,"url":4497},"GitHub Discussion","https:\u002F\u002Fgithub.com\u002Fdirectus\u002Fdirectus\u002Fissues\u002F8228",[4499,4500,4501],{"name":3795,"url":3796},{"name":3798,"url":3799},{"name":3801,"url":3802},"2024-02-29","Conditional Fields & Relational Values","\u003Cp>Speaker 0: Love it all.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Hello. Welcome everybody once again. Thank you for joining us again in our biweekly, bi monthly. I always forget the difference between the 2, but they're also interchangeable.\u003C\u002Fp>\u003Cp>Our bimonthly, twice a month, session where we go over, you know, feature requests and try to go in deep, find all of the edge cases, think about everything that we possibly wanna support ever of the universe, and then bring it back down to number 42. Today, we're talking about supported nested relational values in conditional fields, which feels like a, oh, just support nested relational values. Easy. Until you start thinking about it for real, which is what we're gonna do today, and realize that it's probably not as easy as it sounds because that seems to be far from the course for these sessions.\u003C\u002Fp>\u003Cp>Speaker 0: As it always So\u003C\u002Fp>\u003Cp>Speaker 1: before we dive in on the every single time, before we get in too deep on the technical problems, you wanna, outline what what this is all about, Daniel?\u003C\u002Fp>\u003Cp>Speaker 0: Yes. So, just for anyone that has not used this feature before, it's pretty, I think it's pretty niche. I mean, I have not extensively used it in the past, but, a little bit here and there. And I think lots of people, share this experience. But, if if you use this feature, conditional fields, you will eventually run into this little problem.\u003C\u002Fp>\u003Cp>So it is about the ability to conditionally render, for example, or hide or show or change fields inside of your collection edit or your item edit page depending on other fields. So you open up an item and you have a couple of groups depending on how much information there is inside of your item. It might be useful for you as a user or for, you know, to make it easier for actual users of your directus instance to hide a couple of fields, for example, so lessen the, you know, initial information overload, for example. Or you could also make use of it to actually enhance the visual, the the user experience. There's lots of stuff that you can do.\u003C\u002Fp>\u003Cp>Also, you know, require different fields if a different field has a specific condition, if it meets a specific condition. So lots of interesting stuff that you can do and, like, really, really customize how your editing experience looks, how it feels, and improve it for your users. And, so this is just the overall, like, high level description of what this does. But this issue that has been opened for a little bit, a little tiny, tiny, tiny while. So, it is about this exact scenario.\u003C\u002Fp>\u003Cp>So where you want a field to have a new, let let's say you want to hide something depending on if this item has some type of property in in a relation. I don't think I, describe this very well. But, so you want the to apply these conditions depending on some nested property. And this currently is a little bit tricky, for us because this stems stems from an architecture standpoint, like, how our forms work as of right now. So this is not like, you know, we're just missing a little if statement maybe, and then we could enable this.\u003C\u002Fp>\u003Cp>It's a little bit more more involved, but we'll explore this a little bit. So, yeah, this is what this issue is about. You want to, you know, conditionally style or, you know, change your existing fields depending on the nested property. And it's currently a little problematic because if you open up a form or your item page, I mean, we currently do not load every single nested relation because, you know, that it could be bad. It could be good.\u003C\u002Fp>\u003Cp>It could be bad depending on how large your item is. So, technically, if you open up your item page, we do not have that type of information as of right now. So you can't actually apply conditions because we don't have that information inside of the form, which is the root of the problem. So how do we fix that? Good question.\u003C\u002Fp>\u003Cp>Speaker 1: Problem number 2 is we might not even have access to the info that you're trying to use, which makes this a heck of a lot more tricky to think.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Tack tack on permissions, and suddenly this gets very, very complicate. I mean and how deep is this? Right? So in my case, right, where many to any, this can this, I've seen clients with anywhere from 5 to 18 levels deep in this relational model.\u003C\u002Fp>\u003Cp>How much data do you load up into the current view? Right? This can get very\u003C\u002Fp>\u003Cp>Speaker 1: first, Gary, you know, this\u003C\u002Fp>\u003Cp>Speaker 0: can get a little bit of hands in from the chat. You know? Let's involve the chat a little bit. So So just\u003C\u002Fp>\u003Cp>Speaker 1: to just to summarize this to, those three main Please,\u003C\u002Fp>\u003Cp>Speaker 0: let's\u003C\u002Fp>\u003Cp>Speaker 1: problems at first. Right? So right now, those conditional fields are just using the information that is visible on the form, right, with the edits applied because it needs to be reactive to what the user does. Then once you go relationally deep, we have a bit a problem because that information just doesn't exist in the context of that page at present time. Right?\u003C\u002Fp>\u003Cp>So that in and of itself could be easy enough by saying, okay. Let's just fetch all of the paths that are used within the conditional fields, and then at least we have that data. But we fetch it separately from the actual form content just for conditional fields purposes. That would solve part of this. Right?\u003C\u002Fp>\u003Cp>But then the second problem that makes that part a little tricky is that we have to merge it with the edits made by the user in the form. Right? So now we're dealing with differences in data types and how you configure them, which is one of the the sort of bigger the the second issue. Because theoretically speaking, you know, conditional fields check against the staged value of the form. So therefore, if you select, you know, a many to one item, you could check against the ID that you have selected.\u003C\u002Fp>\u003Cp>If you make a nested update, you could theoretically, through the rules, check for the nested value and and change it. The difficulty is, like, if you select an existing item, now the data that is staged is just a UUID. Right? It's not a nested object of values. So if your conditional field says, author names or nested name and author has to be reg, then it becomes tricky if you select the existing author because now you're checking the rule is nested field name has to be reg, but you're checking it against the u a d string.\u003C\u002Fp>\u003Cp>Right? Because that's the actual data that you're about to save. So that's that's another big big mismatch there. And that problem compounds the further down you go, obviously. Right?\u003C\u002Fp>\u003Cp>So the other thing is when you update, a one to many, we have that difference in syntax where you can provide an array of all the items or you can provide, you know, the create update, delete statements individually, which is what the app uses because it's still easier to manage for larger datasets. But that again also means that you now have to check against 2 data types and it works a little bit unlike you would expect. Right? That's kind of the the the TLDR for this. The 3rd major issue is permissions.\u003C\u002Fp>\u003Cp>Right? So as an administrator, you could set hide, a certain set of fields if a hidden field for this user, like, that's something they can't see is enabled or disabled, which for the end user is invisible and therefore the app can't actually run the check against it because it doesn't even know that the field exists. Right? So that needs a totally different solution where the check doesn't even happen on the app client side. The check happens on the server side.\u003C\u002Fp>\u003Cp>So it can check against data that the client user might not even have access to in the first place. Right? So those are the 3 major sort of problem spaces that we have with this particular feature. All of which need some sort of specialized solution for conditional fields. And hopefully, we can find some sort of direction that is 1, you know, one endpoint that rule them all, so to speak.\u003C\u002Fp>\u003Cp>Speaker 0: That's a top order. I guess. We we have some interactions. Yeah. Exactly.\u003C\u002Fp>\u003Cp>So we have a little bit of chat. How about having a field, that just show calculated data from other columns? So it's not a column in a database, a column just for showing some calculation.\u003C\u002Fp>\u003Cp>Speaker 1: So if I'm understanding correctly, to summarize that, what we're saying is instead of trying to use the nested values like an actual nest of the tree, why not have one alias field that just pulls up the relevant bit of data? So it's always just the string name, for example, from an author, and then you can conditional fields against that. Right? So instead of trying to dynamically do it against the whole tree, you're just pulling out the data that's relevant, and then the conditional fields check only runs on the top level.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. But lots to think about. There we go. I've done it in. Fit this problem before, so what I do is to just create another extra foreign key.\u003C\u002Fp>\u003Cp>Speaker 1: It can\u003C\u002Fp>\u003Cp>Speaker 0: be used to filter the current items onto many drop down. Problem is that the user need to do another selection to enable this filtering.\u003C\u002Fp>\u003Cp>Speaker 1: That's kind of a similar idea. Right? Yeah. Where it's like instead of trying to nest it deeply, you just pull the value back up in some sort of way, either automatically or by having the user chip choose it, and then you operate against the static value of that field. Now the the interesting thing here is that one of the problems that we're seeing is that the way it works technically makes sense.\u003C\u002Fp>\u003Cp>Right? You you validate the conditions against whatever the third stage value is with the user's changes applied. Where it falls on its face is that the expectation is that it will do nested relational lookups and then dynamically apply the changes in that tree and then check against that. Right? So that could potentially solve for the first two.\u003C\u002Fp>\u003Cp>Right? Where we're saying, okay. We can make some sort of utility function that says, what is the conditional field rule? What data do we need for that? Then apply the current edits on top of that in a way that fits the model, right, the model for the conditional fields.\u003C\u002Fp>\u003Cp>So, therefore, if you selected an an ID for many to 1, we have to go fetch the nested data for that so not the permission. I saw it on the screen. It's in a different order now. So it's for the the app data context and then the nested relationships. For the permissions piece, though, we have an issue.\u003C\u002Fp>\u003Cp>Right? Because at the, like, the end of the day, we cannot run that in the app. That's that's basically the long story short. So those for for those who have been eyeing the GitHub repo like a hawk, there was actually a PR not too long ago with an issue, in a in a similar vein, but about the permission checks. Right?\u003C\u002Fp>\u003Cp>So some users were running into some issues where the application would allow you to change fields that you didn't have update permission to, for that exact reason because the update permissions were using a field that the app cannot read because you didn't have read permissions to those fields. So, therefore, the app couldn't know if you had access or not. So it would just default to, like, okay. You can do it. And then once you hit save, the API would just throw an error and be like, no.\u003C\u002Fp>\u003Cp>You cannot do it actually. Right? The way we solved for that is with a new endpoint in the API that basically say, hey. For the current user, am I able to update this particular item in this particular collection? Right?\u003C\u002Fp>\u003Cp>And it will return, like, these are the fields that you can edit, as far as I understand. I I'm a little hazing the execs, you know, IO, but that is effectively the gist of it. Right? And it feels like It feels like this feature will probably go in a very similar direction, where we need to have some sort of API endpoint where the app can say, okay. Here's the current about to be safe changes.\u003C\u002Fp>\u003Cp>For the for the current user, what should the conditional state be, I guess? Right? Because that's the only way. If the the app cannot read fields that are used in the conditions, it's kinda game over. Right?\u003C\u002Fp>\u003Cp>However\u003C\u002Fp>\u003Cp>Speaker 0: Interesting. How how would how would that endpoint look like? Like, what type of payload how would that translate all of the different requirements that we have? Interesting.\u003C\u002Fp>\u003Cp>Speaker 1: So I'd imagine I I know the requirements generally are, a, we need to fetch all of the data that is used in the, conditional fields rules. Right? So we just have to look up what are all the fields and what is the whole tree that is used within the conditional fields and fetch just those in the exact same structure so so we can run the checks against it. Right? Then we need to dynamically apply all of the staged changes from the user because one very important use case for conditional fields is things like, you know, you click a toggle and you show and hide fields conditionally.\u003C\u002Fp>\u003Cp>Right? That's kind of one of the major use cases for this. So it needs to be real time. And then the last thing right now, I'm pretty sure that's, it just returns. I I think what we do right now is we effectively just loop over and and then merge the field's configuration for the current item with the conditional field result, sort of smash them together.\u003C\u002Fp>\u003Cp>Right? So if, like, the default field is hidden in your conditional fields, you have field that's visible, then that takes precedence, and we merge them together by by field key. Right? So we dynamically update the area of fields that exist within the form based on that logic. So to translate that into an endpoint, you would most likely have to have an endpoint that takes what collection are we in, what item are we in, because that might affect, you know, the the IDs and all that kind of stuff.\u003C\u002Fp>\u003Cp>And then what is the changes that you've currently made so we can dynamically merge those and then do all of that logic on the server side and effectively return a new list of fields that should be visible in the app. Right? That's theoretically possible. Tricky thing, performance. Right?\u003C\u002Fp>\u003Cp>Speaker 0: It it is exactly. Like, I was already, like like, inhaling and just to, you know, finish your sentence so I can say that. Exactly. So, yeah, that that would probably then lead to, you know, some delay because, let's say, you type a name pretty quickly, you know, like, example, whatever. Now you made, like, I don't know, 6, 7, 8, whatever it is that you're typing.\u003C\u002Fp>\u003Cp>The key presses, alright, do we debounce those? Then, alright, we have another little delay. So we then have to contact the API, which gets the stuff back, and then we have another little delay. And then the whole experience might suffer depending on how exactly we do it. Because, like, even if we now if we now, start, like, debouncing, that could lead to other problems.\u003C\u002Fp>\u003Cp>You know? Like, if if we want something to change, if the the the the text field contains a specific substring or something. Yeah. Then then that won't trigger fast enough, basically, which, you know, is kinda not the point.\u003C\u002Fp>\u003Cp>Speaker 1: That is that's really hitting it on the head because this has to be you click a toggle, stuff shows up. That's that's the goal. Right? So it needs to be immediate and interactive. So if you debounce it for a second or something, to your point, the experience is gonna suck because you click a button and then Yeah.\u003C\u002Fp>\u003Cp>You're already looking somewhere else and then magically stuff shows up.\u003C\u002Fp>\u003Cp>Speaker 2: Right? This, it's instant. Right? Because the app has all the context and data and permissions and things that it needs, so this is instantaneous for this to show or hide. Mhmm.\u003C\u002Fp>\u003Cp>You know, make required, do whatever. The the toggle here, the app side is doing all that, and it's almost instantaneous where you go server side now if you're having to send back to server. I'm wondering, does WebSockets' real time kinds of capabilities help at all in this space if we work towards I mean, again, we've always wanted to make the app real time so that we can do, you know, multi multi user editing of records and things.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think, realistically, the main it it would reduce the network latency a little bit, but I think the main time spent on this is re fetching the existing item data and then merging on the changes. Right? Now\u003C\u002Fp>\u003Cp>Speaker 0: this is a\u003C\u002Fp>\u003Cp>Speaker 1: nice segue because I just read it in the chat. Does it have to be solved dynamically on the fly? Question mark. I think the answer is yes, unfortunately, because it's based on the current user changes, so it's always have to be dynamic. Is it possible to require users to save the query somewhere, save like a SQL stored procedure kind of thing?\u003C\u002Fp>\u003Cp>If so, would that make it easier to resolve? Yes and no. I mean, it's again, because it's based on the current sexual users changes, we can't really have that prepared because you don't know what those changes will be. That being said, you know, if the item that you've just read, we could cache that for a little while. Right?\u003C\u002Fp>\u003Cp>The tricky bit is just when do you invalidate that cache? That's been a bit of an issue before, right, where right now we have to sort of auto purge the whole thing and it's kind of inefficient, which is a very high on my personal wish list item, but let's not psych sidetrack too much on that in this one. So so the the problem is we can cache that particular call to the database and just be like, oh, in the last, you know, 10 minutes, we already fetched this. It's probably the same. But if some other user changed it in between, you know, it gets a little bit tricky.\u003C\u002Fp>\u003Cp>But we could cache the database call for sure for sure. If so, would that make it easier to resolve? Yeah. It would make it a little bit faster. We still have the network latency though.\u003C\u002Fp>\u003Cp>Right? Even if the API would just respond immediately from all the caches and skips all the execution, just by the nature of it having to go over the Internet, you know, you might have your the the API that you're talking to might be in a different region or something that might be, you know, a 100 200 milliseconds latency just from that. And then it's just the experience is gonna suffer a lot. Like, a lot. Right?\u003C\u002Fp>\u003Cp>Yeah. To a point where, oh, I see our own team here in the chat mentioning a similar thing where it's like, maybe conditions should just not support fields you don't have the permissions to. Because that way, you know, all the commissions, because then then you have this issue where it has to be on the API side. Right? So maybe one of the requirements that we're learning here now is that it kinda has to be on the app just to be able to keep that instant, feedback loop going.\u003C\u002Fp>\u003Cp>And then to mention for conditions not to support fields you don't have permissions to though, you have to configure it on a sort of per role basis what those conditions are because only on the role do you know what fields are available in the condition, Which is true, although that makes configuration hack a lot more annoying. Because if you just have you know, I wanna show an email field when you click, subscribe to newsletter or something. Then now you have to reconfigure that for every single role. Right? Yeah.\u003C\u002Fp>\u003Cp>Chicken. There's always plan c, which is just aggressively saying no relational fields and conditions. That's kind of what we've been doing so far. You know? It it sucks.\u003C\u002Fp>\u003Cp>Speaker 2: Does Paul is does the new policy system maybe allow us some better flexibility so that it's not on a per role? It's configured, but\u003C\u002Fp>\u003Cp>Speaker 1: Oh, don't get me started.\u003C\u002Fp>\u003Cp>Speaker 2: But, again,\u003C\u002Fp>\u003Cp>Speaker 1: it's a lot of and no.\u003C\u002Fp>\u003Cp>Speaker 2: It does require, I think I think this I I think the the idea is this really has to be either real time in some way, you know, WebSockets implementation, the app itself. Goal longer term is to have that kind of capability anyway. But with a policy based, you know, if we are able to, you know, walk the conditional tree and we can we can see that and pull the data necessary. Now it might be that we wanna limit, you know, number of conditions that you can apply or number of levels deep you can go with a conditional. Right?\u003C\u002Fp>\u003Cp>Because I don't know that we can have an you know, the infinite depth capabilities. You know, I can go 18 levels deep in a condition that seems insane to kinda try and cash all that, or even manage\u003C\u002Fp>\u003Cp>Speaker 1: all that in front end.\u003C\u002Fp>\u003Cp>Speaker 2: But is it you know, maybe it's a 1 or a 2 level deep is what is allowed, from a conditional rules perspective kind of thing where it's manageable, controllable, and sustainable.\u003C\u002Fp>\u003Cp>Speaker 1: The the thing to me though when it comes to conditional fields, to me, that is more a form builder type of thing than an access control type of thing. So having to configure how the form behaves on a policy to policy basis feels hard to manage, if that makes sense. Because now you're the output form is just it's hard to expect what it's hard to know what to expect once you have, you know, multiple different policies doing multiple different things when it comes to form building. Now this is a complete sidebar. That's also the beauty of these sessions.\u003C\u002Fp>\u003Cp>Right? Divergent thinking.\u003C\u002Fp>\u003Cp>Speaker 0: Love it.\u003C\u002Fp>\u003Cp>Speaker 1: Here we go. Strap in, folks. It's there's there's another singer, Annette. See I see you nodding over there. But one thing that we have been sort of, you know, floating the idea of is this idea of, like, what if we decouple the data model from the form.\u003C\u002Fp>\u003Cp>Right? So it's decouple the actual actual how you interact with the data from the data model itself. So therefore, you could have multiple forms for the same database table. Right? And then in a policy or in a role or whatever, you would choose what is the form that we wanna use for this particular table, for this particular set of users.\u003C\u002Fp>\u003Cp>Because the one of the other known I mean, issues might be a bit of a big word, but one of the other downsides that I've on my personal wish list, it's kinda like if you have read access, we we show and hide fields that you'd expect. Right? You don't know that a field exists. It doesn't show up in the form. It also means that if you're doing a form building exercise where you have, like, multiple columns, like, 2 things side by side, and all of a sudden one disappears, you know, the form kind of reflows, but it it results in a bit of an unexpected state.\u003C\u002Fp>\u003Cp>Right? Mhmm. So by having, you know, that form, having that form decoupled from the data model, now you could just say, okay. We're gonna make a form that we know will look exactly like this. Like, what you see is what you get for the form or the role that we attach it to.\u003C\u002Fp>\u003Cp>This is, again, a way bigger discussion than just a little brain fart dumping out into the world here. But that is that is, you know, one of the ways around this because then you do have conditional fields per role or per policy group or whatever you wanna call it.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Many, many different things. I'm trying to I'm trying to find a way where we can, you know, like like, define a little pathway for us right now, but it's it's very hard because there's so much so many different things that we could do, technically. So yeah. Yeah.\u003C\u002Fp>\u003Cp>My initial feeling, like, when I read about this was, yeah, it sounds very reasonable, like, in the naive way, you know, the very naive, first understanding of the of the problem. Very similar or basically what you said in the beginning. Alright. So we do know when we open the form, what people used inside of the conditions. Alright.\u003C\u002Fp>\u003Cp>How about we then fetch those things, all of the related stuff to separately from the table? Alright. So far so good. Then the permission issue comes in. Alright.\u003C\u002Fp>\u003Cp>Are they allowed to read this? Yes. No. Alright. What do we do if they and, and then it gets tricky, and then it starts to spiral.\u003C\u002Fp>\u003Cp>Alright. Goddamn. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: We got we got\u003C\u002Fp>\u003Cp>Speaker 0: a couple. Yeah. We we got a couple of steps that sound like alright. Alright. Maybe maybe yes.\u003C\u002Fp>\u003Cp>Yeah. And okay, permissions.\u003C\u002Fp>\u003Cp>Speaker 1: There's there's always the point where we could put limitations in place. Right? It's like, we like to think with the least amount of limitations as we can while keeping it fast. But to Tim's point earlier, we could make a line in the sand and say, okay. You know what?\u003C\u002Fp>\u003Cp>Nested relational data is only allowed if you have read access to those those data points. Otherwise, it's just not intended to work. Because that way, we can remove the server component, keep it on the app site, and make it work the way you'd expect to make it work fast. But we put a ceiling in place to make that happen. Right?\u003C\u002Fp>\u003Cp>That is a very real, a very real in between. I'm I'm still been noodling on that that idea that you had, Jonathan, to, like, could WebSockets potentially help with this? And the more I think about it, the more I'm like, maybe, just maybe. Because if that database call is heavily cached and we send, you know, a message of, like, here's the stage change ID such and such, and then the API will just respond as fast as it can with, like, the the combined change and the new form that comes out of that. If we can heavily cache it enough, we can make that work.\u003C\u002Fp>\u003Cp>Now I see Tim typing in the chat. I'm curious if he's gonna say the same thing. I knew it. That was exactly what I was gonna say. He put in the chat, WebSockets are great, but we cannot rely on them for stuff like this as they're not a hard requirement to run direct this.\u003C\u002Fp>\u003Cp>Speaker 2: Not yet.\u003C\u002Fp>\u003Cp>Speaker 0: And this is this is a\u003C\u002Fp>\u003Cp>Speaker 1: different question, of course. Like, should it be a hard requirement or not? Right? Because and this is again a a bit of a sidecar sidebar from from this, this particular discussion. Because the way we implement the WebSockets right now I mean, first and foremost, we haven't really used them in the app yet, for very similar reasons to this actually, which is, like, we have to be careful with relying on stuff, maybe, yes, no, and then tricky tricky.\u003C\u002Fp>\u003Cp>The the the real question is, you know, WebSockets are not supported everywhere like regular HTTP requests are. Right? So for example, there's there's a couple of platform as a service providers that just will not work with WebSockets in your container or something. Right? Because there's so many hoops you could jump through.\u003C\u002Fp>\u003Cp>You gotta go from, like, you know, the CDN to, like, some sort of load balancer to some sort of proxy container to some sort of, you know, container that has the actual connection. Yeah. And to to Tim's point in the chat now too, if you wanna do snazzy things on the edge, like scaling back to 0 containers or something, you cannot have a persistent connection at all. Right? Which is another very good point.\u003C\u002Fp>\u003Cp>I know there was another this another discussion thread that has seen some activity recently around, being able to run directives in a serverless environment, which, oh, boy, is a whole different session of one of these. I'm sure. But if that were ever, you know, a deployment target that we wanna officially support, then WebSockets are just out. They're just unavailable to rely on, right? And then the question really becomes like, okay, what what is more important to us supporting serverless environment scaling down to 0?\u003C\u002Fp>\u003Cp>Or are we gonna say, well, Directus is just a a a tool that is intended to be a long running server because with cron jobs and hooks and all that kind of stuff. And therefore, WebSockets are just enabled just like the API and you can use them and we can heavily rely on it in the app to make it faster and a better experience. Right? That is gonna be a difficult call to make, because it's it's it's, at the end of the day, a little bit of a subjective one. I know it's it's, we haven't really done all the pros and cons of research and all that kind of stuff.\u003C\u002Fp>\u003Cp>But it's like, you know, people have very strong opinions on this kind of stuff. Very strong opinions.\u003C\u002Fp>\u003Cp>Speaker 0: We have to be careful, with Tim. He probably has one of those.\u003C\u002Fp>\u003Cp>Speaker 1: Like, one of those nasty opinions.\u003C\u002Fp>\u003Cp>Speaker 0: One of these nasty, nasty opinions. Oh, no. No. No. No.\u003C\u002Fp>\u003Cp>Alright. Yeah. Yeah. With the angel emoji. Alright.\u003C\u002Fp>\u003Cp>So far so good. Then let's maybe, you know, do the little, exercise. Alright. Let's say we go down that route that we mentioned with alright. You can't put conditions on stuff that you can't read or that this specific user can't read.\u003C\u002Fp>\u003Cp>Alright. This would have a couple of repercussions. Right? Like, then alright. You have multiple different different user roles.\u003C\u002Fp>\u003Cp>Do you then, as an admin that is configuring this collection, do you want to? Probably not. But do you want to actually configure then these things between different roles all the time. That sounds like a big pain, to be honest. Like, very annoying.\u003C\u002Fp>\u003Cp>Would we? Yeah. I mean, we do have to attach it to some kind of role. Right? Because, as of right now, you know, there can be, like, unlimited different types of user roles.\u003C\u002Fp>\u003Cp>Yeah. And some of them can read those fields. Some of them don't. We don't know that. Would we like to share this then with some type of diffing between those?\u003C\u002Fp>\u003Cp>But oh, man. This this just sounds like spaghetti, like, the largest bowl of spaghetti that you can think of. Weird. It doesn't sound enticing to go down.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think the the the conflict that we're finding ourselves in is that we know you can configure conditions for fields that you might not have access to and then it doesn't work. Right? So what are we gonna do to help the admin user in that situation? Like, we need to educate them on this.\u003C\u002Fp>\u003Cp>We need to educate them on the, on the on the limitation there or show it somehow or prevent them from making that mistake in the first place by connecting it tightly to a role somehow. Because once you connect it tightly to a role, we know what fields are available, and we can only service those in the conditions. Right? Mhmm. I agree with your earlier sentiment when you were, like, configuring that on the role and having to reconfigure it for every single role that might access this thing feels like a nightmare.\u003C\u002Fp>\u003Cp>That feels like so much extra work. Mostly because and and I briefly touched on this before, but mostly because I really see this as a form building tool, not so much an access control thing. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: It's to me, it's similar that you wanna have 2 fields side by side. I wanna have one show up and hide based on the value of the other. That's that's all sort of form building magic, not so much access control magic. So I I kinda strongly feel like we don't wanna conflate those 2 and and put, like, too much form building stuff in access control. Because it Right.\u003C\u002Fp>\u003Cp>Speaker 0: It just makes me context. Yeah. Thanks. So, similarly alright. How about then we reverse this a little bit?\u003C\u002Fp>\u003Cp>So, let's say, as of right now, you know, the admin goes into the collection thingy into the conditions and configures, you know, how it's supposed to look like, all of the different conditions, you know, height depending on this checkbox, make the title red. And then we attach a roll to that filter and we can now check, you know, is this role can this role even apply this filter? And we can immediately just say, oh, no. This role is not compatible with this filter because it can't read x and y and z. And this would also solve, like, solve let's let's in big big big quotation marks, solve that problem of, you know, like, having multiple user problems having multiple user roles that you could, you know, just say, alright.\u003C\u002Fp>\u003Cp>But this filter is, yeah. I I'm already, like, thinking while I'm talking. So then you come into this whole thing. Alright. Maybe you want to have different conditions for different user roles.\u003C\u002Fp>\u003Cp>Maybe.\u003C\u002Fp>\u003Cp>Speaker 1: Here we are again.\u003C\u002Fp>\u003Cp>Speaker 0: Here we go. Here we go.\u003C\u002Fp>\u003Cp>Speaker 1: Love it. That's the moment where it starts spinning again. Tim, just in the chat, does not make sense to build your forms based on what fields you have access to? Which, yes, I think that is kinda closing closing in on the the sort of wild wicked idea of saying, what if you can just configure multiple different forms and you can just make a form that is dedicated to, you know, one one role? So that way you can you know for a fact that all the conditions exist and all the fields exist, and the way you configure the form and the layout is tailor made for that role because the only fields that exist are the ones that exist for the, for the item.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I can I can feel I can already, like, feel the pain through just thinking about this of, you know, the then, like, synchronizing between different types of forms between different types of user roles and oh, man? It's getting it's getting a little bit of rough. Let's say let's say rough. Alright.\u003C\u002Fp>\u003Cp>Tim has another thing. You effectively already do that using access control as the form will not render fields you don't have access to. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Which I think might become an issue at some point in the not too distant future. Because one other thing that we've sort of been been noodling about, Jetta the Jetta, is the idea of a little bit more flexible grit for forms. So right now, it's it's a 2. Right? You can either make a full width or you have 2 side by side.\u003C\u002Fp>\u003Cp>That's it. We also just been thinking about, like, what if we make that just a configurable number with some with some responsive breakpoints potentially. You could make it a 12 grid. You could make it in the 8 grid. Whatever.\u003C\u002Fp>\u003Cp>Right? At that point, if the field in in column number 6 disappears because of redexes, we cannot we can no longer sort of cleverly reflow it, to make sense. Right? Because now we legitimately just don't know where things are supposed to go anymore on the page because it just it it just has to be become a a gap, basically. At that point, we might end up having to just show, like, there used to be a field here, but you don't know about it.\u003C\u002Fp>\u003Cp>Right? Because otherwise, the whole form layout is\u003C\u002Fp>\u003Cp>Speaker 0: tell anyone.\u003C\u002Fp>\u003Cp>Speaker 1: Because otherwise, the form layout is just gonna look either broken because you're gonna end up with blank spots all over the place, or, you know, if if we're trying to I I think it's safe to say that we're trying to sort of reorganize it on the fly. It will break, because we just don't know what needs to go where at that point. Because it would be a multidimensional grid. Right? That will be 2 dimensional because you can have things go 2 rows or 2 columns, etcetera, and then you have a problem.\u003C\u002Fp>\u003Cp>Right? Kinda like, I think of dashboards basically in terms of just positioning stuff all over the place. But if we go that route, if if that ever comes to fruition, right, again, it's just a random thought. It's not actively being worked on or anything. But, if we're going in that direction, I think we need to have some sort of way to have a form per role anyways.\u003C\u002Fp>\u003Cp>Because otherwise, you know, you're gonna either have a form with a bunch of holes in it or with place holders that you're like, hee hee. You can't actually see this. Or, you know, or it just needs to be per role because that way you can just make one that is, you know, tailor made for for that particular role.\u003C\u002Fp>\u003Cp>Speaker 0: Well, whether or not, we're actually, you know, looking into this, we already have our first customer. Then he said, I'm sold on the option to build a form per roll.\u003C\u002Fp>\u003Cp>Speaker 1: So good.\u003C\u002Fp>\u003Cp>Speaker 0: Sold it already. It was fast.\u003C\u002Fp>\u003Cp>Speaker 2: I like it. So if we can inherit, like, a global so it inherits the the default kinda data model form that you configure, and then allow you to adjust that on the specific role, I think that's actually pretty freaking cool because we do have those kinds of use cases where, you know, this particular business and especially if we could apply specific conditionals on that form, it means that, you know, my business user versus my consumer user could have a very different form experience intentionally and different conditionals. Right? The the default conditional for the business user presets those conditions and organization. I I kinda love the idea.\u003C\u002Fp>\u003Cp>Whether or not again, this this comes back to whether or not this actually we we decide to allow additional depth, you know, conditionals. Just having that particular feature seems kind of amazing. As long as we can inherit the default, then if I don't need that kind of capability, I can just use the default and manage the default that we'll get. If I've got a custom reason for adjusting the form. Pretty brilliant.\u003C\u002Fp>\u003Cp>I like it.\u003C\u002Fp>\u003Cp>Speaker 0: So there's a question by Benny Wade. Could we make groups configurable per row rather than making a whole form per row? Keep it a bit more modular.\u003C\u002Fp>\u003Cp>Speaker 1: AKA, can we nest these forms?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That that Let's\u003C\u002Fp>\u003Cp>Speaker 1: check as we check-in with our friend v form and see how he feels about it.\u003C\u002Fp>\u003Cp>Speaker 0: -Oh, boy. Can't wait. Guys, if you enjoy\u003C\u002Fp>\u003Cp>Speaker 1: messaging, please. Where you have the forms that or the view of the page or whatever you wanna call it is the same for everybody, but then you just have a section where it's like this section only shows up for x role. Right? Could be interesting. That is a conditional field in and of itself, of course, where it's like, the condition is show or hide this thing if current role is x y z.\u003C\u002Fp>\u003Cp>Right? Which now I'm thinking about it. I'm pretty sure that exists. Present time. I'm pretty sure you can this is something I'll have to test, actually, because I know we have those dynamic variables for current user and current role in those.\u003C\u002Fp>\u003Cp>I don't remember if we support those in conditional fields. Because if we do, you can step a group and have a conditional field on the group that says current role equals x y z and then hide the whole group. We do not,\u003C\u002Fp>\u003Cp>Speaker 0: Tim. Tim and a chat. Why not? Why not?\u003C\u002Fp>\u003Cp>Speaker 1: He calls me out he calls me out on my bluff. It's happens happens sometimes. Yeah. But that would theoretically be a a soul for the same thing, though. Right?\u003C\u002Fp>\u003Cp>Or you could just say the whole group is hidden for a role, and therefore, no problem. You make it so that you can have the same field in different presentation section configured per role? Right. Right. So the same field multiple times.\u003C\u002Fp>\u003Cp>Yeah. That is that is another thing when it comes to dynamic form building and, just full stop dynamic form building at that point. Right? Because right now, the form is data model first. It's like you have a title field, therefore, you can now have a title input because it's 1 to 1 to the data model.\u003C\u002Fp>\u003Cp>Right? With a form building thing, maybe you have 2 title fields, like, 2 fields that point to the same underlying data thing. Right? There's instinct. I don't know why the hell you would do that, but you could.\u003C\u002Fp>\u003Cp>For for some interface, it may set I mean, in this this example where it's like you have the same field in different sections and the section show and hide based on the role, it starts to make sense again. I for admin users, it'd be interesting, but it it yeah. For sure. Then for, I I think there's a a definite interesting use case for certain data types where it would help to have multiple fields, multiple visual fields for the same underlying column. Think about, like, an address interface face or something.\u003C\u002Fp>\u003Cp>Right? Where you wanna have address line 1, 2, and then, ZIP code and all that kind of stuff, but you wanna save it as one field in your data model. Not super uncommon, or as a let long sort of search thing, right, where you have a let and a long input and then an address field, and those are all connected. So you can type in an address and it'll geo look up the let long, but then the thing that's saved in the database is just the let long. So there's definitely, you know, some additional things that we unlock with that sort of modular form idea.\u003C\u002Fp>\u003Cp>Right?\u003C\u002Fp>\u003Cp>Speaker 0: Alright. So what I gathered from this, is that we're basically about to build our own page building framework where we can dynamically close plugs. So we're basically set, you know? Like, director's page building, that sounds like a great idea. Okay.\u003C\u002Fp>\u003Cp>On the\u003C\u002Fp>\u003Cp>Speaker 1: So just to solve condition fields. That's that's the beauty of it, isn't it? How did we how did we get here for just solving nested conditional fields?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It all started so innocently. You know?\u003C\u002Fp>\u003Cp>Speaker 1: It's true.\u003C\u002Fp>\u003Cp>Speaker 0: I just want to hide a field, and now we're basically embarking on a mission to\u003C\u002Fp>\u003Cp>Speaker 1: Okay. This is this is actually this is a good thing because this is a perfect example of the sort of, you know, request review session. For those who've been here before, you've seen this happen before. It's like we go completely off the rails in all directions, and then now let's try to see how we can actually get this back on track for nested conditional fields. Right?\u003C\u002Fp>\u003Cp>So as a you know, in a potential future utopia where you can just magically conquer up any sort of form, any sort of view that you possibly want. Great. But as of right now, I'm kind of thinking, based on everything that we talked about so far, it, to me, makes the most sense to follow Tim's suggestion from earlier, which is basically you cannot put the field and conditional fields that the user does not have read access to. Right? I think that is that is a safe call.\u003C\u002Fp>\u003Cp>I think doing it on the API side, we're never gonna be able to get it just performant enough, and we don't really wanna make WebSockets a requirement for this to work. So I think as of today, you know, what can we actually achieve in the next month or 3? We would most likely keep the client side, keep it fast so we can do all the calculations on the client. It has to be from current fields that you have read access to. The main difference that we have to do is that we have to dynamically, when you open the page the first time, we have to load up what is the nested data structure of this item based on the rules used in conditional fields.\u003C\u002Fp>\u003Cp>And then have some sort of utility function that can cleverly combine the stage changes on top of that. Right? We have to account for the differences like many to one ID versus nested object. We have to account for things like the one to many update structure versus array. But that that way, we can merge that and we can do a client side.\u003C\u002Fp>\u003Cp>Right? I don't think we should do that per role. That just feels like a nightmare to configure, to be completely honest.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And\u003C\u002Fp>\u003Cp>Speaker 1: a lot of a lot of duplication. Again, in a utopic utopic utopic? Utopia stick future. Utopia like future. Utopian?\u003C\u002Fp>\u003Cp>Utopian future, you know, where you have more of this flexible form idea. At that point, you know, you can say, okay, When someone I'm already boring Jonathan to death, I see. When when you open this this page for this particular role, we use form xyz instead but that is again a way way future discussion. But then I think, you know, we can support the core ask for this this feature request, which is if I say nested author dot name, I expect that the lookup, what is the name of the nested author no matter what. If I select an existing one, it needs to look at the name of the existing one.\u003C\u002Fp>\u003Cp>If I change somebody's name, it needs to look at the changed name. Right? That is something that we can I'm pretty hopeful we can turn that into some sort of usability function that we can run relatively quickly on the client side and make this happen. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That shouldn't be shouldn't be impossible. Yeah. Yeah. Look at the from Natron.\u003C\u002Fp>\u003Cp>Speaker 1: And 10 rabbits with 1 multiracial conditional stone. Yeah. Exactly. As an admin that's configuring these conditional fields to another person in the chat, it would be nice to see the whole form that a person with a given role would see. Maybe a view as role option somewhere.\u003C\u002Fp>\u003Cp>Fully a 100% agree. To Jonathan's point, there's another feature request about that user impersonation. Again, very high on my personal, wish list. Right? I would love it as an admin.\u003C\u002Fp>\u003Cp>And not so not just for the for the particular form and settings or something. I really want there to be a way for admins to just say, browse the whole app, impersonating a different role so that you can just click around and see exactly what that user can see and do. Right? From a visual perspective, we'll probably have to have some sort of banner, zoom out a little bit or whatever. We'll figure it out what that looks like.\u003C\u002Fp>\u003Cp>But that way, you can just configure it, see the whole thing as, you know, that particular user. Now especially dreaming wish list, it'd be great if you could just change the form while you're on the page looking at what the form looks like for that given your role. But let's I'll get ahead of ourselves.\u003C\u002Fp>\u003Cp>Speaker 0: We're going back. We're going back, guys. Back to the page.\u003C\u002Fp>\u003Cp>Speaker 1: We're spinning. We're spinning again. We're spinning again. Let's not spin too much. Yeah.\u003C\u002Fp>\u003Cp>But no. That's a it's it is a very good idea because also with the other feature request that we talked about. Was it last time or not too long ago where we're talking about the rules and permissions as a whole. Right? And these policy units and whatnot.\u003C\u002Fp>\u003Cp>Now that these policies become way more composable, buzzword shot, we can actually you you also have the problem that it becomes a little bit harder to figure out what is the actual permission for this one user now. Right? Because it could come from multiple different places and you can have on the user overrides and that kind of stuff. So I think user impersonation becomes way more important, with that in mind as well.\u003C\u002Fp>\u003Cp>Speaker 0: Great. Let's see. We caught up to the chat. If anyone has any last questions, please let us know. There's not much time left.\u003C\u002Fp>\u003Cp>I was running out. Just the the call to action, you know, just to get them going.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Just a moment. Please like and subscribe.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Hit that notification bell so you'll stay up to date.\u003C\u002Fp>\u003Cp>Speaker 0: But if you truly want to stay up to date, you should head to direct us dot io\u002Ftv. I I nearly said dot tv. Yeah. Alright. No.\u003C\u002Fp>\u003Cp>But seriously, on the directors dot iotv, there's lots of interesting series that you can check out, such as this one and also other ones. And enjoy. And\u003C\u002Fp>\u003Cp>Speaker 2: see how Directus TV was built on Directus. Very cool. It's exciting.\u003C\u002Fp>\u003Cp>Speaker 1: That is pretty cool.\u003C\u002Fp>\u003Cp>Speaker 0: We have Jeron to join literally in the last minute. Hey there.\u003C\u002Fp>\u003Cp>Speaker 1: Hey there. Thank you for joining. How you doing? We're about to call it. Goodbye.\u003C\u002Fp>\u003Cp>Speaker 2: Anyway. Very, very exciting.\u003C\u002Fp>\u003Cp>Speaker 1: As always, thank everybody so much for watching. This was yet another talk where I was, like, going into it, it's like, oh, this feels like a small little bug that we could just fix. Right? And then we talk about it now, and I'm like, oh, man. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: It deep go. Building a form builder and page builder. Now now\u003C\u002Fp>\u003Cp>Speaker 1: we're getting snazzy with it. But that's that's the beauty of these sessions, and I love it every time. So those are joining. Thank you so much. See you guys in 2 weeks from now.\u003C\u002Fp>\u003Cp>Or oh, no. Wait. That's the one that we're skipping for the, you know, the special week week. Yep.\u003C\u002Fp>\u003Cp>Speaker 2: It'll be, 4 weeks before our next one, folks. Thanks so much. Appreciate your patience. There's some big announcements coming that that this next week's time or next 2 week time slot is gonna be consumed with other events, and keep an eye out for the live events and things going on that week. So very exciting announcements on the way.\u003C\u002Fp>\u003Cp>We're we're excited to have such an awesome community. Have a great day. Have a great week.\u003C\u002Fp>","Love it all. Yeah. Hello. Welcome everybody once again. Thank you for joining us again in our biweekly, bi monthly. I always forget the difference between the 2, but they're also interchangeable. Our bimonthly, twice a month, session where we go over, you know, feature requests and try to go in deep, find all of the edge cases, think about everything that we possibly wanna support ever of the universe, and then bring it back down to number 42. Today, we're talking about supported nested relational values in conditional fields, which feels like a, oh, just support nested relational values. Easy. Until you start thinking about it for real, which is what we're gonna do today, and realize that it's probably not as easy as it sounds because that seems to be far from the course for these sessions. As it always So before we dive in on the every single time, before we get in too deep on the technical problems, you wanna, outline what what this is all about, Daniel? Yes. So, just for anyone that has not used this feature before, it's pretty, I think it's pretty niche. I mean, I have not extensively used it in the past, but, a little bit here and there. And I think lots of people, share this experience. But, if if you use this feature, conditional fields, you will eventually run into this little problem. So it is about the ability to conditionally render, for example, or hide or show or change fields inside of your collection edit or your item edit page depending on other fields. So you open up an item and you have a couple of groups depending on how much information there is inside of your item. It might be useful for you as a user or for, you know, to make it easier for actual users of your directus instance to hide a couple of fields, for example, so lessen the, you know, initial information overload, for example. Or you could also make use of it to actually enhance the visual, the the user experience. There's lots of stuff that you can do. Also, you know, require different fields if a different field has a specific condition, if it meets a specific condition. So lots of interesting stuff that you can do and, like, really, really customize how your editing experience looks, how it feels, and improve it for your users. And, so this is just the overall, like, high level description of what this does. But this issue that has been opened for a little bit, a little tiny, tiny, tiny while. So, it is about this exact scenario. So where you want a field to have a new, let let's say you want to hide something depending on if this item has some type of property in in a relation. I don't think I, describe this very well. But, so you want the to apply these conditions depending on some nested property. And this currently is a little bit tricky, for us because this stems stems from an architecture standpoint, like, how our forms work as of right now. So this is not like, you know, we're just missing a little if statement maybe, and then we could enable this. It's a little bit more more involved, but we'll explore this a little bit. So, yeah, this is what this issue is about. You want to, you know, conditionally style or, you know, change your existing fields depending on the nested property. And it's currently a little problematic because if you open up a form or your item page, I mean, we currently do not load every single nested relation because, you know, that it could be bad. It could be good. It could be bad depending on how large your item is. So, technically, if you open up your item page, we do not have that type of information as of right now. So you can't actually apply conditions because we don't have that information inside of the form, which is the root of the problem. So how do we fix that? Good question. Problem number 2 is we might not even have access to the info that you're trying to use, which makes this a heck of a lot more tricky to think. Yeah. Tack tack on permissions, and suddenly this gets very, very complicate. I mean and how deep is this? Right? So in my case, right, where many to any, this can this, I've seen clients with anywhere from 5 to 18 levels deep in this relational model. How much data do you load up into the current view? Right? This can get very first, Gary, you know, this can get a little bit of hands in from the chat. You know? Let's involve the chat a little bit. So So just to just to summarize this to, those three main Please, let's problems at first. Right? So right now, those conditional fields are just using the information that is visible on the form, right, with the edits applied because it needs to be reactive to what the user does. Then once you go relationally deep, we have a bit a problem because that information just doesn't exist in the context of that page at present time. Right? So that in and of itself could be easy enough by saying, okay. Let's just fetch all of the paths that are used within the conditional fields, and then at least we have that data. But we fetch it separately from the actual form content just for conditional fields purposes. That would solve part of this. Right? But then the second problem that makes that part a little tricky is that we have to merge it with the edits made by the user in the form. Right? So now we're dealing with differences in data types and how you configure them, which is one of the the sort of bigger the the second issue. Because theoretically speaking, you know, conditional fields check against the staged value of the form. So therefore, if you select, you know, a many to one item, you could check against the ID that you have selected. If you make a nested update, you could theoretically, through the rules, check for the nested value and and change it. The difficulty is, like, if you select an existing item, now the data that is staged is just a UUID. Right? It's not a nested object of values. So if your conditional field says, author names or nested name and author has to be reg, then it becomes tricky if you select the existing author because now you're checking the rule is nested field name has to be reg, but you're checking it against the u a d string. Right? Because that's the actual data that you're about to save. So that's that's another big big mismatch there. And that problem compounds the further down you go, obviously. Right? So the other thing is when you update, a one to many, we have that difference in syntax where you can provide an array of all the items or you can provide, you know, the create update, delete statements individually, which is what the app uses because it's still easier to manage for larger datasets. But that again also means that you now have to check against 2 data types and it works a little bit unlike you would expect. Right? That's kind of the the the TLDR for this. The 3rd major issue is permissions. Right? So as an administrator, you could set hide, a certain set of fields if a hidden field for this user, like, that's something they can't see is enabled or disabled, which for the end user is invisible and therefore the app can't actually run the check against it because it doesn't even know that the field exists. Right? So that needs a totally different solution where the check doesn't even happen on the app client side. The check happens on the server side. So it can check against data that the client user might not even have access to in the first place. Right? So those are the 3 major sort of problem spaces that we have with this particular feature. All of which need some sort of specialized solution for conditional fields. And hopefully, we can find some sort of direction that is 1, you know, one endpoint that rule them all, so to speak. That's a top order. I guess. We we have some interactions. Yeah. Exactly. So we have a little bit of chat. How about having a field, that just show calculated data from other columns? So it's not a column in a database, a column just for showing some calculation. So if I'm understanding correctly, to summarize that, what we're saying is instead of trying to use the nested values like an actual nest of the tree, why not have one alias field that just pulls up the relevant bit of data? So it's always just the string name, for example, from an author, and then you can conditional fields against that. Right? So instead of trying to dynamically do it against the whole tree, you're just pulling out the data that's relevant, and then the conditional fields check only runs on the top level. Alright. But lots to think about. There we go. I've done it in. Fit this problem before, so what I do is to just create another extra foreign key. It can be used to filter the current items onto many drop down. Problem is that the user need to do another selection to enable this filtering. That's kind of a similar idea. Right? Yeah. Where it's like instead of trying to nest it deeply, you just pull the value back up in some sort of way, either automatically or by having the user chip choose it, and then you operate against the static value of that field. Now the the interesting thing here is that one of the problems that we're seeing is that the way it works technically makes sense. Right? You you validate the conditions against whatever the third stage value is with the user's changes applied. Where it falls on its face is that the expectation is that it will do nested relational lookups and then dynamically apply the changes in that tree and then check against that. Right? So that could potentially solve for the first two. Right? Where we're saying, okay. We can make some sort of utility function that says, what is the conditional field rule? What data do we need for that? Then apply the current edits on top of that in a way that fits the model, right, the model for the conditional fields. So, therefore, if you selected an an ID for many to 1, we have to go fetch the nested data for that so not the permission. I saw it on the screen. It's in a different order now. So it's for the the app data context and then the nested relationships. For the permissions piece, though, we have an issue. Right? Because at the, like, the end of the day, we cannot run that in the app. That's that's basically the long story short. So those for for those who have been eyeing the GitHub repo like a hawk, there was actually a PR not too long ago with an issue, in a in a similar vein, but about the permission checks. Right? So some users were running into some issues where the application would allow you to change fields that you didn't have update permission to, for that exact reason because the update permissions were using a field that the app cannot read because you didn't have read permissions to those fields. So, therefore, the app couldn't know if you had access or not. So it would just default to, like, okay. You can do it. And then once you hit save, the API would just throw an error and be like, no. You cannot do it actually. Right? The way we solved for that is with a new endpoint in the API that basically say, hey. For the current user, am I able to update this particular item in this particular collection? Right? And it will return, like, these are the fields that you can edit, as far as I understand. I I'm a little hazing the execs, you know, IO, but that is effectively the gist of it. Right? And it feels like It feels like this feature will probably go in a very similar direction, where we need to have some sort of API endpoint where the app can say, okay. Here's the current about to be safe changes. For the for the current user, what should the conditional state be, I guess? Right? Because that's the only way. If the the app cannot read fields that are used in the conditions, it's kinda game over. Right? However Interesting. How how would how would that endpoint look like? Like, what type of payload how would that translate all of the different requirements that we have? Interesting. So I'd imagine I I know the requirements generally are, a, we need to fetch all of the data that is used in the, conditional fields rules. Right? So we just have to look up what are all the fields and what is the whole tree that is used within the conditional fields and fetch just those in the exact same structure so so we can run the checks against it. Right? Then we need to dynamically apply all of the staged changes from the user because one very important use case for conditional fields is things like, you know, you click a toggle and you show and hide fields conditionally. Right? That's kind of one of the major use cases for this. So it needs to be real time. And then the last thing right now, I'm pretty sure that's, it just returns. I I think what we do right now is we effectively just loop over and and then merge the field's configuration for the current item with the conditional field result, sort of smash them together. Right? So if, like, the default field is hidden in your conditional fields, you have field that's visible, then that takes precedence, and we merge them together by by field key. Right? So we dynamically update the area of fields that exist within the form based on that logic. So to translate that into an endpoint, you would most likely have to have an endpoint that takes what collection are we in, what item are we in, because that might affect, you know, the the IDs and all that kind of stuff. And then what is the changes that you've currently made so we can dynamically merge those and then do all of that logic on the server side and effectively return a new list of fields that should be visible in the app. Right? That's theoretically possible. Tricky thing, performance. Right? It it is exactly. Like, I was already, like like, inhaling and just to, you know, finish your sentence so I can say that. Exactly. So, yeah, that that would probably then lead to, you know, some delay because, let's say, you type a name pretty quickly, you know, like, example, whatever. Now you made, like, I don't know, 6, 7, 8, whatever it is that you're typing. The key presses, alright, do we debounce those? Then, alright, we have another little delay. So we then have to contact the API, which gets the stuff back, and then we have another little delay. And then the whole experience might suffer depending on how exactly we do it. Because, like, even if we now if we now, start, like, debouncing, that could lead to other problems. You know? Like, if if we want something to change, if the the the the text field contains a specific substring or something. Yeah. Then then that won't trigger fast enough, basically, which, you know, is kinda not the point. That is that's really hitting it on the head because this has to be you click a toggle, stuff shows up. That's that's the goal. Right? So it needs to be immediate and interactive. So if you debounce it for a second or something, to your point, the experience is gonna suck because you click a button and then Yeah. You're already looking somewhere else and then magically stuff shows up. Right? This, it's instant. Right? Because the app has all the context and data and permissions and things that it needs, so this is instantaneous for this to show or hide. Mhmm. You know, make required, do whatever. The the toggle here, the app side is doing all that, and it's almost instantaneous where you go server side now if you're having to send back to server. I'm wondering, does WebSockets' real time kinds of capabilities help at all in this space if we work towards I mean, again, we've always wanted to make the app real time so that we can do, you know, multi multi user editing of records and things. Yeah. I think, realistically, the main it it would reduce the network latency a little bit, but I think the main time spent on this is re fetching the existing item data and then merging on the changes. Right? Now this is a nice segue because I just read it in the chat. Does it have to be solved dynamically on the fly? Question mark. I think the answer is yes, unfortunately, because it's based on the current user changes, so it's always have to be dynamic. Is it possible to require users to save the query somewhere, save like a SQL stored procedure kind of thing? If so, would that make it easier to resolve? Yes and no. I mean, it's again, because it's based on the current sexual users changes, we can't really have that prepared because you don't know what those changes will be. That being said, you know, if the item that you've just read, we could cache that for a little while. Right? The tricky bit is just when do you invalidate that cache? That's been a bit of an issue before, right, where right now we have to sort of auto purge the whole thing and it's kind of inefficient, which is a very high on my personal wish list item, but let's not psych sidetrack too much on that in this one. So so the the problem is we can cache that particular call to the database and just be like, oh, in the last, you know, 10 minutes, we already fetched this. It's probably the same. But if some other user changed it in between, you know, it gets a little bit tricky. But we could cache the database call for sure for sure. If so, would that make it easier to resolve? Yeah. It would make it a little bit faster. We still have the network latency though. Right? Even if the API would just respond immediately from all the caches and skips all the execution, just by the nature of it having to go over the Internet, you know, you might have your the the API that you're talking to might be in a different region or something that might be, you know, a 100 200 milliseconds latency just from that. And then it's just the experience is gonna suffer a lot. Like, a lot. Right? Yeah. To a point where, oh, I see our own team here in the chat mentioning a similar thing where it's like, maybe conditions should just not support fields you don't have the permissions to. Because that way, you know, all the commissions, because then then you have this issue where it has to be on the API side. Right? So maybe one of the requirements that we're learning here now is that it kinda has to be on the app just to be able to keep that instant, feedback loop going. And then to mention for conditions not to support fields you don't have permissions to though, you have to configure it on a sort of per role basis what those conditions are because only on the role do you know what fields are available in the condition, Which is true, although that makes configuration hack a lot more annoying. Because if you just have you know, I wanna show an email field when you click, subscribe to newsletter or something. Then now you have to reconfigure that for every single role. Right? Yeah. Chicken. There's always plan c, which is just aggressively saying no relational fields and conditions. That's kind of what we've been doing so far. You know? It it sucks. Does Paul is does the new policy system maybe allow us some better flexibility so that it's not on a per role? It's configured, but Oh, don't get me started. But, again, it's a lot of and no. It does require, I think I think this I I think the the idea is this really has to be either real time in some way, you know, WebSockets implementation, the app itself. Goal longer term is to have that kind of capability anyway. But with a policy based, you know, if we are able to, you know, walk the conditional tree and we can we can see that and pull the data necessary. Now it might be that we wanna limit, you know, number of conditions that you can apply or number of levels deep you can go with a conditional. Right? Because I don't know that we can have an you know, the infinite depth capabilities. You know, I can go 18 levels deep in a condition that seems insane to kinda try and cash all that, or even manage all that in front end. But is it you know, maybe it's a 1 or a 2 level deep is what is allowed, from a conditional rules perspective kind of thing where it's manageable, controllable, and sustainable. The the thing to me though when it comes to conditional fields, to me, that is more a form builder type of thing than an access control type of thing. So having to configure how the form behaves on a policy to policy basis feels hard to manage, if that makes sense. Because now you're the output form is just it's hard to expect what it's hard to know what to expect once you have, you know, multiple different policies doing multiple different things when it comes to form building. Now this is a complete sidebar. That's also the beauty of these sessions. Right? Divergent thinking. Love it. Here we go. Strap in, folks. It's there's there's another singer, Annette. See I see you nodding over there. But one thing that we have been sort of, you know, floating the idea of is this idea of, like, what if we decouple the data model from the form. Right? So it's decouple the actual actual how you interact with the data from the data model itself. So therefore, you could have multiple forms for the same database table. Right? And then in a policy or in a role or whatever, you would choose what is the form that we wanna use for this particular table, for this particular set of users. Because the one of the other known I mean, issues might be a bit of a big word, but one of the other downsides that I've on my personal wish list, it's kinda like if you have read access, we we show and hide fields that you'd expect. Right? You don't know that a field exists. It doesn't show up in the form. It also means that if you're doing a form building exercise where you have, like, multiple columns, like, 2 things side by side, and all of a sudden one disappears, you know, the form kind of reflows, but it it results in a bit of an unexpected state. Right? Mhmm. So by having, you know, that form, having that form decoupled from the data model, now you could just say, okay. We're gonna make a form that we know will look exactly like this. Like, what you see is what you get for the form or the role that we attach it to. This is, again, a way bigger discussion than just a little brain fart dumping out into the world here. But that is that is, you know, one of the ways around this because then you do have conditional fields per role or per policy group or whatever you wanna call it. Alright. Many, many different things. I'm trying to I'm trying to find a way where we can, you know, like like, define a little pathway for us right now, but it's it's very hard because there's so much so many different things that we could do, technically. So yeah. Yeah. My initial feeling, like, when I read about this was, yeah, it sounds very reasonable, like, in the naive way, you know, the very naive, first understanding of the of the problem. Very similar or basically what you said in the beginning. Alright. So we do know when we open the form, what people used inside of the conditions. Alright. How about we then fetch those things, all of the related stuff to separately from the table? Alright. So far so good. Then the permission issue comes in. Alright. Are they allowed to read this? Yes. No. Alright. What do we do if they and, and then it gets tricky, and then it starts to spiral. Alright. Goddamn. Okay. We got we got a couple. Yeah. We we got a couple of steps that sound like alright. Alright. Maybe maybe yes. Yeah. And okay, permissions. There's there's always the point where we could put limitations in place. Right? It's like, we like to think with the least amount of limitations as we can while keeping it fast. But to Tim's point earlier, we could make a line in the sand and say, okay. You know what? Nested relational data is only allowed if you have read access to those those data points. Otherwise, it's just not intended to work. Because that way, we can remove the server component, keep it on the app site, and make it work the way you'd expect to make it work fast. But we put a ceiling in place to make that happen. Right? That is a very real, a very real in between. I'm I'm still been noodling on that that idea that you had, Jonathan, to, like, could WebSockets potentially help with this? And the more I think about it, the more I'm like, maybe, just maybe. Because if that database call is heavily cached and we send, you know, a message of, like, here's the stage change ID such and such, and then the API will just respond as fast as it can with, like, the the combined change and the new form that comes out of that. If we can heavily cache it enough, we can make that work. Now I see Tim typing in the chat. I'm curious if he's gonna say the same thing. I knew it. That was exactly what I was gonna say. He put in the chat, WebSockets are great, but we cannot rely on them for stuff like this as they're not a hard requirement to run direct this. Not yet. And this is this is a different question, of course. Like, should it be a hard requirement or not? Right? Because and this is again a a bit of a sidecar sidebar from from this, this particular discussion. Because the way we implement the WebSockets right now I mean, first and foremost, we haven't really used them in the app yet, for very similar reasons to this actually, which is, like, we have to be careful with relying on stuff, maybe, yes, no, and then tricky tricky. The the the real question is, you know, WebSockets are not supported everywhere like regular HTTP requests are. Right? So for example, there's there's a couple of platform as a service providers that just will not work with WebSockets in your container or something. Right? Because there's so many hoops you could jump through. You gotta go from, like, you know, the CDN to, like, some sort of load balancer to some sort of proxy container to some sort of, you know, container that has the actual connection. Yeah. And to to Tim's point in the chat now too, if you wanna do snazzy things on the edge, like scaling back to 0 containers or something, you cannot have a persistent connection at all. Right? Which is another very good point. I know there was another this another discussion thread that has seen some activity recently around, being able to run directives in a serverless environment, which, oh, boy, is a whole different session of one of these. I'm sure. But if that were ever, you know, a deployment target that we wanna officially support, then WebSockets are just out. They're just unavailable to rely on, right? And then the question really becomes like, okay, what what is more important to us supporting serverless environment scaling down to 0? Or are we gonna say, well, Directus is just a a a tool that is intended to be a long running server because with cron jobs and hooks and all that kind of stuff. And therefore, WebSockets are just enabled just like the API and you can use them and we can heavily rely on it in the app to make it faster and a better experience. Right? That is gonna be a difficult call to make, because it's it's it's, at the end of the day, a little bit of a subjective one. I know it's it's, we haven't really done all the pros and cons of research and all that kind of stuff. But it's like, you know, people have very strong opinions on this kind of stuff. Very strong opinions. We have to be careful, with Tim. He probably has one of those. Like, one of those nasty opinions. One of these nasty, nasty opinions. Oh, no. No. No. No. Alright. Yeah. Yeah. With the angel emoji. Alright. So far so good. Then let's maybe, you know, do the little, exercise. Alright. Let's say we go down that route that we mentioned with alright. You can't put conditions on stuff that you can't read or that this specific user can't read. Alright. This would have a couple of repercussions. Right? Like, then alright. You have multiple different different user roles. Do you then, as an admin that is configuring this collection, do you want to? Probably not. But do you want to actually configure then these things between different roles all the time. That sounds like a big pain, to be honest. Like, very annoying. Would we? Yeah. I mean, we do have to attach it to some kind of role. Right? Because, as of right now, you know, there can be, like, unlimited different types of user roles. Yeah. And some of them can read those fields. Some of them don't. We don't know that. Would we like to share this then with some type of diffing between those? But oh, man. This this just sounds like spaghetti, like, the largest bowl of spaghetti that you can think of. Weird. It doesn't sound enticing to go down. Yeah. I think the the the conflict that we're finding ourselves in is that we know you can configure conditions for fields that you might not have access to and then it doesn't work. Right? So what are we gonna do to help the admin user in that situation? Like, we need to educate them on this. We need to educate them on the, on the on the limitation there or show it somehow or prevent them from making that mistake in the first place by connecting it tightly to a role somehow. Because once you connect it tightly to a role, we know what fields are available, and we can only service those in the conditions. Right? Mhmm. I agree with your earlier sentiment when you were, like, configuring that on the role and having to reconfigure it for every single role that might access this thing feels like a nightmare. That feels like so much extra work. Mostly because and and I briefly touched on this before, but mostly because I really see this as a form building tool, not so much an access control thing. Right? Yeah. It's to me, it's similar that you wanna have 2 fields side by side. I wanna have one show up and hide based on the value of the other. That's that's all sort of form building magic, not so much access control magic. So I I kinda strongly feel like we don't wanna conflate those 2 and and put, like, too much form building stuff in access control. Because it Right. It just makes me context. Yeah. Thanks. So, similarly alright. How about then we reverse this a little bit? So, let's say, as of right now, you know, the admin goes into the collection thingy into the conditions and configures, you know, how it's supposed to look like, all of the different conditions, you know, height depending on this checkbox, make the title red. And then we attach a roll to that filter and we can now check, you know, is this role can this role even apply this filter? And we can immediately just say, oh, no. This role is not compatible with this filter because it can't read x and y and z. And this would also solve, like, solve let's let's in big big big quotation marks, solve that problem of, you know, like, having multiple user problems having multiple user roles that you could, you know, just say, alright. But this filter is, yeah. I I'm already, like, thinking while I'm talking. So then you come into this whole thing. Alright. Maybe you want to have different conditions for different user roles. Maybe. Here we are again. Here we go. Here we go. Love it. That's the moment where it starts spinning again. Tim, just in the chat, does not make sense to build your forms based on what fields you have access to? Which, yes, I think that is kinda closing closing in on the the sort of wild wicked idea of saying, what if you can just configure multiple different forms and you can just make a form that is dedicated to, you know, one one role? So that way you can you know for a fact that all the conditions exist and all the fields exist, and the way you configure the form and the layout is tailor made for that role because the only fields that exist are the ones that exist for the, for the item. Yeah. I can I can feel I can already, like, feel the pain through just thinking about this of, you know, the then, like, synchronizing between different types of forms between different types of user roles and oh, man? It's getting it's getting a little bit of rough. Let's say let's say rough. Alright. Tim has another thing. You effectively already do that using access control as the form will not render fields you don't have access to. Yeah. Which I think might become an issue at some point in the not too distant future. Because one other thing that we've sort of been been noodling about, Jetta the Jetta, is the idea of a little bit more flexible grit for forms. So right now, it's it's a 2. Right? You can either make a full width or you have 2 side by side. That's it. We also just been thinking about, like, what if we make that just a configurable number with some with some responsive breakpoints potentially. You could make it a 12 grid. You could make it in the 8 grid. Whatever. Right? At that point, if the field in in column number 6 disappears because of redexes, we cannot we can no longer sort of cleverly reflow it, to make sense. Right? Because now we legitimately just don't know where things are supposed to go anymore on the page because it just it it just has to be become a a gap, basically. At that point, we might end up having to just show, like, there used to be a field here, but you don't know about it. Right? Because otherwise, the whole form layout is tell anyone. Because otherwise, the form layout is just gonna look either broken because you're gonna end up with blank spots all over the place, or, you know, if if we're trying to I I think it's safe to say that we're trying to sort of reorganize it on the fly. It will break, because we just don't know what needs to go where at that point. Because it would be a multidimensional grid. Right? That will be 2 dimensional because you can have things go 2 rows or 2 columns, etcetera, and then you have a problem. Right? Kinda like, I think of dashboards basically in terms of just positioning stuff all over the place. But if we go that route, if if that ever comes to fruition, right, again, it's just a random thought. It's not actively being worked on or anything. But, if we're going in that direction, I think we need to have some sort of way to have a form per role anyways. Because otherwise, you know, you're gonna either have a form with a bunch of holes in it or with place holders that you're like, hee hee. You can't actually see this. Or, you know, or it just needs to be per role because that way you can just make one that is, you know, tailor made for for that particular role. Well, whether or not, we're actually, you know, looking into this, we already have our first customer. Then he said, I'm sold on the option to build a form per roll. So good. Sold it already. It was fast. I like it. So if we can inherit, like, a global so it inherits the the default kinda data model form that you configure, and then allow you to adjust that on the specific role, I think that's actually pretty freaking cool because we do have those kinds of use cases where, you know, this particular business and especially if we could apply specific conditionals on that form, it means that, you know, my business user versus my consumer user could have a very different form experience intentionally and different conditionals. Right? The the default conditional for the business user presets those conditions and organization. I I kinda love the idea. Whether or not again, this this comes back to whether or not this actually we we decide to allow additional depth, you know, conditionals. Just having that particular feature seems kind of amazing. As long as we can inherit the default, then if I don't need that kind of capability, I can just use the default and manage the default that we'll get. If I've got a custom reason for adjusting the form. Pretty brilliant. I like it. So there's a question by Benny Wade. Could we make groups configurable per row rather than making a whole form per row? Keep it a bit more modular. AKA, can we nest these forms? Yeah. That that Let's check as we check-in with our friend v form and see how he feels about it. -Oh, boy. Can't wait. Guys, if you enjoy messaging, please. Where you have the forms that or the view of the page or whatever you wanna call it is the same for everybody, but then you just have a section where it's like this section only shows up for x role. Right? Could be interesting. That is a conditional field in and of itself, of course, where it's like, the condition is show or hide this thing if current role is x y z. Right? Which now I'm thinking about it. I'm pretty sure that exists. Present time. I'm pretty sure you can this is something I'll have to test, actually, because I know we have those dynamic variables for current user and current role in those. I don't remember if we support those in conditional fields. Because if we do, you can step a group and have a conditional field on the group that says current role equals x y z and then hide the whole group. We do not, Tim. Tim and a chat. Why not? Why not? He calls me out he calls me out on my bluff. It's happens happens sometimes. Yeah. But that would theoretically be a a soul for the same thing, though. Right? Or you could just say the whole group is hidden for a role, and therefore, no problem. You make it so that you can have the same field in different presentation section configured per role? Right. Right. So the same field multiple times. Yeah. That is that is another thing when it comes to dynamic form building and, just full stop dynamic form building at that point. Right? Because right now, the form is data model first. It's like you have a title field, therefore, you can now have a title input because it's 1 to 1 to the data model. Right? With a form building thing, maybe you have 2 title fields, like, 2 fields that point to the same underlying data thing. Right? There's instinct. I don't know why the hell you would do that, but you could. For for some interface, it may set I mean, in this this example where it's like you have the same field in different sections and the section show and hide based on the role, it starts to make sense again. I for admin users, it'd be interesting, but it it yeah. For sure. Then for, I I think there's a a definite interesting use case for certain data types where it would help to have multiple fields, multiple visual fields for the same underlying column. Think about, like, an address interface face or something. Right? Where you wanna have address line 1, 2, and then, ZIP code and all that kind of stuff, but you wanna save it as one field in your data model. Not super uncommon, or as a let long sort of search thing, right, where you have a let and a long input and then an address field, and those are all connected. So you can type in an address and it'll geo look up the let long, but then the thing that's saved in the database is just the let long. So there's definitely, you know, some additional things that we unlock with that sort of modular form idea. Right? Alright. So what I gathered from this, is that we're basically about to build our own page building framework where we can dynamically close plugs. So we're basically set, you know? Like, director's page building, that sounds like a great idea. Okay. On the So just to solve condition fields. That's that's the beauty of it, isn't it? How did we how did we get here for just solving nested conditional fields? Yeah. It all started so innocently. You know? It's true. I just want to hide a field, and now we're basically embarking on a mission to Okay. This is this is actually this is a good thing because this is a perfect example of the sort of, you know, request review session. For those who've been here before, you've seen this happen before. It's like we go completely off the rails in all directions, and then now let's try to see how we can actually get this back on track for nested conditional fields. Right? So as a you know, in a potential future utopia where you can just magically conquer up any sort of form, any sort of view that you possibly want. Great. But as of right now, I'm kind of thinking, based on everything that we talked about so far, it, to me, makes the most sense to follow Tim's suggestion from earlier, which is basically you cannot put the field and conditional fields that the user does not have read access to. Right? I think that is that is a safe call. I think doing it on the API side, we're never gonna be able to get it just performant enough, and we don't really wanna make WebSockets a requirement for this to work. So I think as of today, you know, what can we actually achieve in the next month or 3? We would most likely keep the client side, keep it fast so we can do all the calculations on the client. It has to be from current fields that you have read access to. The main difference that we have to do is that we have to dynamically, when you open the page the first time, we have to load up what is the nested data structure of this item based on the rules used in conditional fields. And then have some sort of utility function that can cleverly combine the stage changes on top of that. Right? We have to account for the differences like many to one ID versus nested object. We have to account for things like the one to many update structure versus array. But that that way, we can merge that and we can do a client side. Right? I don't think we should do that per role. That just feels like a nightmare to configure, to be completely honest. Yeah. And a lot of a lot of duplication. Again, in a utopic utopic utopic? Utopia stick future. Utopia like future. Utopian? Utopian future, you know, where you have more of this flexible form idea. At that point, you know, you can say, okay, When someone I'm already boring Jonathan to death, I see. When when you open this this page for this particular role, we use form xyz instead but that is again a way way future discussion. But then I think, you know, we can support the core ask for this this feature request, which is if I say nested author dot name, I expect that the lookup, what is the name of the nested author no matter what. If I select an existing one, it needs to look at the name of the existing one. If I change somebody's name, it needs to look at the changed name. Right? That is something that we can I'm pretty hopeful we can turn that into some sort of usability function that we can run relatively quickly on the client side and make this happen. Right? Yeah. That shouldn't be shouldn't be impossible. Yeah. Yeah. Look at the from Natron. And 10 rabbits with 1 multiracial conditional stone. Yeah. Exactly. As an admin that's configuring these conditional fields to another person in the chat, it would be nice to see the whole form that a person with a given role would see. Maybe a view as role option somewhere. Fully a 100% agree. To Jonathan's point, there's another feature request about that user impersonation. Again, very high on my personal, wish list. Right? I would love it as an admin. And not so not just for the for the particular form and settings or something. I really want there to be a way for admins to just say, browse the whole app, impersonating a different role so that you can just click around and see exactly what that user can see and do. Right? From a visual perspective, we'll probably have to have some sort of banner, zoom out a little bit or whatever. We'll figure it out what that looks like. But that way, you can just configure it, see the whole thing as, you know, that particular user. Now especially dreaming wish list, it'd be great if you could just change the form while you're on the page looking at what the form looks like for that given your role. But let's I'll get ahead of ourselves. We're going back. We're going back, guys. Back to the page. We're spinning. We're spinning again. We're spinning again. Let's not spin too much. Yeah. But no. That's a it's it is a very good idea because also with the other feature request that we talked about. Was it last time or not too long ago where we're talking about the rules and permissions as a whole. Right? And these policy units and whatnot. Now that these policies become way more composable, buzzword shot, we can actually you you also have the problem that it becomes a little bit harder to figure out what is the actual permission for this one user now. Right? Because it could come from multiple different places and you can have on the user overrides and that kind of stuff. So I think user impersonation becomes way more important, with that in mind as well. Great. Let's see. We caught up to the chat. If anyone has any last questions, please let us know. There's not much time left. I was running out. Just the the call to action, you know, just to get them going. Yeah. Just a moment. Please like and subscribe. Yeah. Yeah. Yeah. Yeah. Hit that notification bell so you'll stay up to date. But if you truly want to stay up to date, you should head to direct us dot io\u002Ftv. I I nearly said dot tv. Yeah. Alright. No. But seriously, on the directors dot iotv, there's lots of interesting series that you can check out, such as this one and also other ones. And enjoy. And see how Directus TV was built on Directus. Very cool. It's exciting. That is pretty cool. We have Jeron to join literally in the last minute. Hey there. Hey there. Thank you for joining. How you doing? We're about to call it. Goodbye. Anyway. Very, very exciting. As always, thank everybody so much for watching. This was yet another talk where I was, like, going into it, it's like, oh, this feels like a small little bug that we could just fix. Right? And then we talk about it now, and I'm like, oh, man. Okay. It deep go. Building a form builder and page builder. Now now we're getting snazzy with it. But that's that's the beauty of these sessions, and I love it every time. So those are joining. Thank you so much. See you guys in 2 weeks from now. Or oh, no. Wait. That's the one that we're skipping for the, you know, the special week week. Yep. It'll be, 4 weeks before our next one, folks. Thanks so much. Appreciate your patience. There's some big announcements coming that that this next week's time or next 2 week time slot is gonna be consumed with other events, and keep an eye out for the live events and things going on that week. So very exciting announcements on the way. We're we're excited to have such an awesome community. Have a great day. Have a great week.",[4507,4508,4509],"bad648f8-fb30-4a79-ab4f-df5678800ac9","b1a78d8c-341f-4e4e-91d7-1c98574b7b6c","a09e4bf5-1fa6-4813-b642-53b986e1fdf9",[],{"year":978,"number":11,"id":1787,"episodes":4512,"show":4513},[1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1768,1800],{"title":1802,"slug":1803},{"id":4515,"slug":4516,"vimeo_id":4517,"description":4518,"tile":4519,"length":3992,"resources":12,"people":4520,"episode_number":213,"published":4529,"title":4530,"video_transcript_html":4531,"video_transcript_text":4532,"content":12,"seo":12,"status":19,"episode_people":4533,"recommendations":4537,"season":4538},"57dcdecb-35d2-45da-b230-17ec18dfabce","tom-morano","902512802","Tom shares his unexpected ascent into leadership and his unique perspective on guiding teams. This episode offers valuable lessons on leadership and personal growth.","76e00679-8ef7-4e09-bc64-f814b04e0235",[4521,4523,4526],{"name":4361,"url":4522},"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fjwdaniels\u002F",{"name":4524,"url":4525},"Pedro Pizarro","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fpedropizarrojr\u002F",{"name":4527,"url":4528},"Tom Morano","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fthomas-morano-719022b\u002F","2024-02-22","Tom Morano, Technical Lead at SMX Consulting","\u003Cp>Speaker 0: Just like a sidebar, I noticed you had some instruments in the back. Do you play guitar? Is that a guitar?\u003C\u002Fp>\n\u003Cp>Speaker 1: It is a guitar. It's actually a guitar case. The guitar is millimeter. But I I, I'm not the typical dev. I, sing and play in a band.\u003C\u002Fp>\n\u003Cp>Speaker 0: Dev rockstar. I like it. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 2: Hey, everybody. Welcome back to another episode of Tech Talk. We have Tom with us. Tom, would you mind introducing yourself to everyone in the audience? Just give a brief introduction, who you are, what you do, and then we'll we'll kinda dive into the conversation for today.\u003C\u002Fp>\n\u003Cp>Speaker 1: Name's Tom Marano. I live in Buffalo, New York, and I am currently a technical lead for SMX Consulting on a, state project.\u003C\u002Fp>\n\u003Cp>Speaker 2: Awesome. Thanks so much. We, we had a intro conversation a couple weeks ago, and you had a couple interesting stories from your background. So if you don't mind, could you give us a little bit of your background in engineering, how you got to where you are today? We'll probably interrupt you a couple times to ask some questions, but remember a couple points that I'd I'd love to see if you bring back up just about your history.\u003C\u002Fp>\n\u003Cp>So how did you get into engineering and kind of what's what's been your journey along the way?\u003C\u002Fp>\n\u003Cp>Speaker 1: So I've always been a fan of technology. I was the kid who literally took everything apart in my parents' house. Yes, my dad how many times it took apart his, like, $1,000 stereo and tried to put it back together. Happened a lot. So always been big fan of just anything tech.\u003C\u002Fp>\n\u003Cp>Love video games, obviously, growing up. And, actually got my start really building computers and everything when I was younger and CD burners became a thing. And, I quickly learned how to, get music, but that, you know, is, the legibly as possible. Yeah. Yeah.\u003C\u002Fp>\n\u003Cp>The leg away. So quickly learn how to burn CDs, and I actually, would make CDs for people in in high school and everything and, you know, sell them to people, for, you know, 5 bucks. And, through school too, I I went on a program a little bit, did a little bit HTML and, c on the side. And then, college stuck with that as as far as school went. I did deviate a little bit.\u003C\u002Fp>\n\u003Cp>I got into a little bit of electrical, engineering a little bit for a couple years and then decided I liked programming better. So, went back and, got my bachelor's in computer science, from an online school and got my first job, programming for a health care company here in Buffalo, learning the ropes, most of it the hard way because there wasn't a ton of guidance for me at most of the places I worked early on in my career, unfortunately. It was kind of the wild west where it was just kinda me figuring it out. And also, demands the Internet wasn't as big as it is now, so it wasn't a good resource to even fall back on. So it was always hard trying to figure my way around things.\u003C\u002Fp>\n\u003Cp>And then as I kept growing in my career, jumping from position to position, I was able to find some decent mentors around me and, eventually ended up at a larger, health care comp well, benefits company here in Buffalo that was very, very well structured as far as their documentation organization. Had a really, really good agile practice in place. And, also, like, kinda gave me the guidance as far as, like, how to properly run SDLC and for an organization and just give me those guideposts as far as, like, how you can effectively create a really good application. And just good communication practices, good team camaraderie, ideas being spread openly around. It was all learned at this, one shop in Buffalo I worked at.\u003C\u002Fp>\n\u003Cp>They're actually not in existence anymore. They're called Liaison, so I'll just throw the name out there. But they were a startup here in Buffalo for years, and I was fortunate enough to meet a lot of really close dev friends when I was there. All of us have gone our separate ways and had our own successes, but it was a really good group of people to work with, and I learned a lot from them. And that's where I got my first leadership role too and, learned a lot there.\u003C\u002Fp>\n\u003Cp>One of the trainings they gave us for all of our the new team leads out there was, something called radical candor, which is a book that's out there a lot of people read. And really good, information there as how to, like, communicate with your team effectively to get get them to focus on the task at hand, find out what they need to do, but also not be a dick doing it, you know. Yeah. You don't have to roll with nigh or fist. You know, you can you can just be stern, tell them what they need to do, but also listen and understand why they might be thinking a wrong thing or in a certain direction, which could even open up your eyes if you're thinking about it wrong too.\u003C\u002Fp>\n\u003Cp>So that that just opening up that dialogue back and forth really good. And, and when my time ended there, I ended up going to a couple other places. One was a start up I worked at about a about 6 or 7 months ago. That was a new experience for me too. Never worked at a startup that was very, very small, but, ended up being brought in as, like, the second to the VP of engineering.\u003C\u002Fp>\n\u003Cp>And, eventually, he ended up leaving and, you know, a role was offered to me, and I got thrown to the wolves trying to learn how to run an entire software organization by myself. It was small still, but learned a lot of things the hard way and how to really keep things organized from a very high level. And so direct people down, empower them to run the other parts of the team that I can't have my hands in all the time because I have to think of things from the higher level and think forward as far as, like, getting the product out.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: Awesome. Yeah. Unfortunately, that one folded too. But, I then got this role as a consultant for SMX and, Ceramic.\u003C\u002Fp>\n\u003Cp>Speaker 0: That's awesome. Nice. I I'd love to hear a little bit about your path from you know, obviously, you've worked for the larger organizations, the startups. I'd love to hear your perspective on the difference and what the problems are, if they're more similar or more different between those, like, large scale organizations or small ones. Like, I know coming from, you know, most of my background was moving to San Francisco, working for a bunch of different startups and ad tech, all kinds of different technology startups, music as well.\u003C\u002Fp>\n\u003Cp>But then I went to work at Juniper Networks. That's actually where I met John. And, obviously, Juniper Networks is a 10,000 person organization. So it's it's fun to see the mess from, you know, the the small problems and the startup land versus, like, the more corporate bureaucracy stuff. But I'd love to hear from your perspective, like, which which you enjoyed more, and just how you kind of see the difference in those, organizations and and building and growing.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. I will say the one common theme that almost every company I work for has is organization. Like, it's hands down, it's it's the number one issue that most of them have. And\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: Depending on who's in charge, it could go good or bad if they're willing to listen to change, if they want to move and pivot. I'm learning that a lot actually through the consulting work I'm doing now. This is my first consulting position I've ever held. And just seeing how the client is struggling with that, and they brought the team I'm with in to try to help them just use agile, which is, you know, pretty common in what most of us devs do these days. Most places use agile.\u003C\u002Fp>\n\u003Cp>Some better than others, but they at least try. And, the biggest thing there is just breaking the problem into smaller pieces, which is more digestible and doesn't seem like the world is gonna fall apart because this this problem is just so huge. Like, that's the one benefit of agile. You know? And it's if if you don't have people at the top that can see that and there's no one voicing it, you just have a bunch of, yes men all over the place just nodding their heads and saying, yep.\u003C\u002Fp>\n\u003Cp>We'll get that done. We'll get that done. The weight usually falls down on the people at the bottom doing the work, which is the devs. So then you don't get a good team of devs because they don't wanna be there and deal with that crap. You know?\u003C\u002Fp>\n\u003Cp>Speaker 2: Yep.\u003C\u002Fp>\n\u003Cp>Speaker 1: You know, they they they just wanna do their job most of the time and go home and feel like they've built something cool that day for the most part. You know? And that's that's like the feel I've loved. And the the first time I really got it was at that, dev shop I worked at in Buffalo for that big gap, where I learned most of what I know today. And, before that, it was a lot of chaos.\u003C\u002Fp>\n\u003Cp>Like, the IT department was always just like those guys in the corner. They do their thing. Nobody really cares. And and it it there wasn't really any organization around it. There there was just yeah.\u003C\u002Fp>\n\u003Cp>Can you guys get this done for us? You know? We need it soon.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: And and being part of that, large organization that was very well structured really showed me the benefit of just working together collaboratively with people and not just saying throw it to that guy over the fence and he'll figure it out. You know? That that whole, like, planning phase of building the product that you're on became more enjoyable. Learning more about the architecture patterns became more enjoyable because I was I felt more invested in the work.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: And that was that was a big thing. Like, just feeling like what I did made a difference was like a huge eye opener to me. Like, oh, this I can make a difference doing this stuff. This is cool. I have my voice matters.\u003C\u002Fp>\n\u003Cp>I'm not just the guy in the corner. You know?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: It's it's always the that that's that's the thing that just made work enjoyable for me.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. Yeah. I'd like to go back to something that you mentioned from your childhood that kind of peaked you started your curiosity. You mentioned taking things apart and trying to rebuild them. That's something that my dad used to do.\u003C\u002Fp>\n\u003Cp>He used to take apart things back in the sixties, and, that was something that I did when I was a kid. I would take apart things, and I I would usually not rebuild them. I would usually take them apart just to see how they worked and then leave the parts everywhere. Mhmm. But I've I found that that kind of curiosity has transitioned into adulthood still with physical things.\u003C\u002Fp>\n\u003Cp>Like, if the weed whacker is broken, I'll just take it apart. I, you know, I don't wanna go buy a new one, so I'll fix it. But I find that some people are just scared to take things apart and try it, not only with physical things, but also with software or leadership or organizations or you know? As as you grow in leadership, you have to wear many hats. So do you find that that's also a similar case for you that because you were able to have that curiosity when you were young and did first off, did it continue to these days, you still kinda tinker with things like that.\u003C\u002Fp>\n\u003Cp>But do you find that the ability to be comfortable taking things apart and dissecting things has helped you grow into a leadership position both in bigger companies and start ups?\u003C\u002Fp>\n\u003Cp>Speaker 1: Definitely. The the curiosity to not be afraid to try things on my own, that's the biggest thing I think that probably helped me become a leader right now just because I I will I'm not I'm not scared to speak out. Like, I I will let my voice be heard if I don't agree with a point. Sometimes through a fault, I might Yep. Not I might put my foot in the mouth sometimes, you know, and yeah.\u003C\u002Fp>\n\u003Cp>But at at least I'm not scared of expressing my point of view and trying to get it across. Not always right. Nobody's nobody's ever always right. That's that's the one thing And, you know, when I was younger, I was definitely more hard headed. Definitely more hard headed.\u003C\u002Fp>\n\u003Cp>As I got older, it I just slowed I would say probably slowed down is the best way to put it and wasn't so like go go go go attitude. It was more like, okay, let me listen and figure out what other people are thinking. Yeah. And try and get their point of view. Like that's that was the biggest change, beginning into the leadership role was like trying to change my mindset that way and, the the one like, I'll bring back up Radical Candor.\u003C\u002Fp>\n\u003Cp>That's one of the big points in there is to be a good listener and understand your people, understand their points of view, understand why they think this way or approach they're taking is right. You know, might not be right, but at least you can understand the point of view and can then, if they are wrong, you can then tell them, well, it's that's not exactly right because a, b and c. Because you can then understand why the thing is. You don't just yell at them and say, no. You have to do it this way.\u003C\u002Fp>\n\u003Cp>You know, you you could talk to people that way. They usually can come to some, rapport with them a little bit easier than just saying get this done or you're in trouble or something.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. There's a differ difference between a boss and a leader. So Mhmm. I I totally agree. Do you find those types of yeah.\u003C\u002Fp>\n\u003Cp>We hope our boss isn't listening, but, but, yeah, yeah, do you do you find that you're able to then also see those types of characteristics in people that you're now leading? Or when you were climbing the ranks, did you see those traits in nobody else, and that's why it was potentially easier for you to also climb into leadership? And then do you see that those types of traits in those that are around you, whether they're other leaders or people who work for you?\u003C\u002Fp>\n\u003Cp>Speaker 1: Definitely for sure. It's it's not everyone either. Like, there's there's a lot of devs, obviously, who are very introverted. It's just the nature of what we do. You know, a lot of our stuff that we work on is solo by ourselves, and that's the way a lot of us like it.\u003C\u002Fp>\n\u003Cp>Not everyone's born to be a leader. They just, like, wanna get their work done or work on something cool by themselves. Don't really care too much about anything else. I definitely have some of those on my team right now. I will say they are sometimes the hardest ones to manage because you have to pull information out of them most of the time.\u003C\u002Fp>\n\u003Cp>But the ones that do have that curiosity to go the extra mile without having to be told exactly what to do, Those ones definitely have those traits in them because they they wanna go the extra mile. They wanna look at the next step without having to be told to go look at the next step. They they wanna understand why they just did and created the, went through the business roles. They just added into the app. They wanna know why they had to do that.\u003C\u002Fp>\n\u003Cp>So they're going to go down the path, understand the whole process, the whole business process they just had to implement and not just the little piece they worked on. Yeah. And that that helps give them a bigger picture so that they can maybe go into designing a whole system next time and then leading that design and so on and so on. Just building those steps because they have that curiosity to get their head around the whole thing, which is, I think, another part of being a good leader too, to understand everything involved with the processes that you're managing and going through. Because if you if you don't understand that, you're just some guy talking in the back telling people what to do all day.\u003C\u002Fp>\n\u003Cp>Speaker 0: Right. Just issuing directives. Yeah. How do you when you identify these traits in folks, as their leader, like, how do you take those skills and and build on to them to, you know, train the next, you know, generation of leaders or or help build them into something like your role that you have now?\u003C\u002Fp>\n\u003Cp>Speaker 1: Slowly giving them more responsibility, I think, is the first thing. Like, gaining that trust with them, and even letting letting them fail too sometimes is good because they need to be able to deal with some of the failures too that come with it if they maybe pick a bad decision. Learning how to bounce back from that or pivot to a different direction if they realize, oh, this isn't gonna work. We gotta try something else. But, yeah, it's slowly giving them more and more responsibilities and eventually even letting them lead certain projects or whatever you happen to be on.\u003C\u002Fp>\n\u003Cp>That's that's a good step to try to let them spread their wings a little bit Yeah. I think.\u003C\u002Fp>\n\u003Cp>Speaker 2: Do you ever do you ever miss being an or do you still individually contribute, or do you ever miss not being a leader? Are there other times where you're you just say, man, I wish I was just plugging away at code or or developing something today and not having to deal with bureaucracy or just some sort of issue that arises that leaders have to deal with? Or do you just love everything that comes at you, deal with the things that are pain in the ass, and and or or, you know, how do you feel about being a leader in your future for yourself as well?\u003C\u002Fp>\n\u003Cp>Speaker 1: I mean, I'm human. I'm gonna have days where I just don't wanna deal with some stuff going on. Like, if there's a 1,000,000 fires going on and I just wanna rip my hair out, it's like, I can't do this today. Gonna gonna have those days. It happens.\u003C\u002Fp>\n\u003Cp>But, I do love just leading the project and deciding the direction that, say the team goes or say technology paths that we investigate and go down. It it does it is harder for me sometimes to to pull away. Like, I I do wanna get involved and, like, actually do some coding once in a while. I I get to once in a while. I do it on the side still.\u003C\u002Fp>\n\u003Cp>Did mention this earlier. I know we talked about it in our pre call, but I do kind of run a a DJ business on the side that I like, always use as, like, a testing ground for some of my, project some of just my skill and expertise, like, growing that. I did a lot with dotnet in there at in the Azure world. And, it's it's helped me just experiment with things in the side and understand more, especially around, cloud architecture and design. I learned a lot of that just on my own ad hoc, and I was able to apply it when I got to that startup role.\u003C\u002Fp>\n\u003Cp>I did in the role before this.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Yeah. I guess the side projects are a good way to, like, flex those coding muscles still where you're not able to individually contribute to a project you're managing or leading, at least, like, on the side. Mhmm.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. They keep you\u003C\u002Fp>\n\u003Cp>Speaker 0: so that's really cool.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. They keep you up to date too with some of the the cool new tools out there as well, you know, because you don't always get to use those in your job. Yeah. Sometimes it's good to know that because then you can even say, hey. When some problem comes around, maybe it can be solved by this thing I messed with at home.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. That was that was actually gonna be my next question was what, you know, what is some new tech that you've been able to find in those side projects or in just general browsing the Internet that you find cool with others? We've talked about cryptocurrency and and Bitcoin and random types of things and AI, obviously, but are there some tools out there that you've started to uncover personally or in your regular job that you really see some potential to have a really cool use case for yourself?\u003C\u002Fp>\n\u003Cp>Speaker 1: I mean, Python is the new hotness everybody's learning these days, mostly just for the big data stuff, especially with Fintech. I've I've started dabbling with that. The the client expressed an interest in it. So I went on the side to start learning it on my own just so I can understand maybe from a perspective that, it can be implemented with the clients. To be fair, I I don't know if it's going to work for the clients if we're looking into it, but at least I know ahead of time before just saying, yeah.\u003C\u002Fp>\n\u003Cp>Let's do that. Yeah. Let's see. I I've I I've dabbled a lot with, a lot of PaaS and SaaS offerings in the cloud, mostly mostly in Azure, a little bit in AWS, using very, small services using, like, some of the functions in Azure along with, like, a front end. It's a very low cost way of creating, like, a a web application semi quickly.\u003C\u002Fp>\n\u003Cp>Yeah. And then, of course, everything you can do with containers these days in even you can even wrap Azure functions now in containers. They're so handy. And running in, like, Linux Linux boxes, you don't even have to stand up Windows boxes anymore. It's always I mean, it's been around for a while now.\u003C\u002Fp>\n\u003Cp>It's it's gaining more traction these days by more companies, which is good, I think.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. For sure. I think it's it's funny you mentioned that too because I think for the side projects and and just for these, like, you know, self applications or learning on the side, that's actually how we hear a lot of developers in our community find out about about Directus, about us. Right? Like, open source technology.\u003C\u002Fp>\n\u003Cp>It's like, hey. Let me try this for a side project. And then all of a sudden, at some point, their their business or their company has a need that it's like, oh, this might be perfect. I've been using this for years on my, you know, DJ side business. Like, maybe it could apply to this organization I'm working with.\u003C\u002Fp>\n\u003Cp>So, that that's pretty cool to hear. I also just like a sidebar, I noticed you had some instruments in the back. Do you play guitar? Is that a guitar?\u003C\u002Fp>\n\u003Cp>Speaker 1: It is a guitar. It's actually a guitar case. The guitar is in the living room. Awesome. I I, I'm not the typical dev.\u003C\u002Fp>\n\u003Cp>I, sing and play in a band.\u003C\u002Fp>\n\u003Cp>Speaker 0: Dev rock star. I like it. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 2: That's a very common trait. We see yeah. We see a lot of developers who you have to have side hobbies as well. I I doubt any developers, hopefully, not too many, sit in front of a computer for 18 hours a day. Though it maybe it's gaming.\u003C\u002Fp>\n\u003Cp>You know, that's a hobby too, but we find a lot of devs, play instruments. And maybe it's because instruments are kind of it's kind of like a puzzle and taking apart things and putting them together. Music is kind of like a puzzle like that as well. So is that something you can\u003C\u002Fp>\n\u003Cp>Speaker 0: use for your Right? Still math and language and basically the same as, like, coding just in a different if in a different way. I think we have enough musicians within Directus to form a band, but maybe we do that one day. Yep.\u003C\u002Fp>\n\u003Cp>Speaker 1: That big dev shop I worked at, there was a lot of guitar players a lot. Like, some of the some of the, director level guys, they all had, like, acoustics in their office.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I remember when I got to when I moved to San Francisco, I'd gone to the the Dropbox office, and, I think the CEO was like a drummer or guitarist or something. And they had set up 2 jam rooms where people could go and, like, they had the whole full band set up, and you can go at any time and just jam out and take out your frustration or whatever you wanna do on the drums. And, I thought that's a really cool idea.\u003C\u002Fp>\n\u003Cp>Speaker 1: That is a very cool idea. I like that. If I worked at a physical office anymore, I would say, yeah. Let's do that. Yeah.\u003C\u002Fp>\n\u003Cp>Yeah. Right. Right.\u003C\u002Fp>\n\u003Cp>Speaker 2: What is there anything that you see that might you know, what are your takes on things like AI? Are there things out there that you see that have are overblown or misunderstood? You know? We hear a lot of people with different opinions on that or maybe a technology that you that other people use that you find overblown. Anything that you see on that side of the spectrum?\u003C\u002Fp>\n\u003Cp>Speaker 1: I mean, I personally think AI is a big hype right now just like, NFTs before and then, you know, Bitcoin before. I mean, Bitcoin is still big now, but, you know, at its peak, it was ginormous, you know. Yeah. Now it's slowly coming back down to earth. NFTs have pretty much died.\u003C\u002Fp>\n\u003Cp>And AI, it's it's got its benefits. It's cool, but it's it's the way people communicate about it that's, I think, wrong. It's it's not actual AI, like the definition. It can't think by itself. It's just large language Yeah.\u003C\u002Fp>\n\u003Cp>Large language models. It's just taking it from a very big dictionary and guessing what it should be based on a bunch of other texts that it has in this thing. It can't actually think by itself.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: And that's that's the one thing people get all crazy about, like, thinking it's gonna take everyone's job.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Like, sentient stuff. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: It's not sentient. Yeah. Can't do that yet. Someday it probably will, but it's it's not. And that's that's the one thing.\u003C\u002Fp>\n\u003Cp>It's it's always I always find a little bit just annoying when people freak out about it. It's like it's it's not there yet. Yeah. Do you\u003C\u002Fp>\n\u003Cp>Speaker 0: think the sentience is, like, years away, decades away? I mean, if I mean, it's hard to tell.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. It's really hard to tell. I mean, it's\u003C\u002Fp>\n\u003Cp>Speaker 0: I guess only the devs at OpenAI and Entropic and some of those others probably know, like, what the timeline looks like.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. I don't even know if they're they're on the right track with that. Because, like, honestly, if they're gonna do sentience, it would have to be probably more maps like a human brain. And I don't think that's the way they're doing any of these AI model like, models. I think they're all just big dictionaries for the most part, but guess as good as mine.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. So what would be some advice you give for people on your team, for example, whom you might see leadership traits in or other people who would be watching this? If somebody feels like they have the traits to be a leader obviously, you've mentioned a book that I myself have read, and that book I do think is an incredible book.\u003C\u002Fp>\n\u003Cp>Speaker 1: Mhmm.\u003C\u002Fp>\n\u003Cp>Speaker 2: But you've mentioned not being afraid to try things, not being afraid to fail. What is your advice to people who still might have hesitance to even try something because they are scared, Or what are some other things that you would give some advice for people looking to grow into a leadership position?\u003C\u002Fp>\n\u003Cp>Speaker 1: The biggest thing is tell your boss. Like, communicate that with your boss. That's that's where I started. I told my boss I wanted to do these things, and they helped me get my position myself in the right place so I eventually could be moved up to a team lead and run up my run my first team. So you you really have to just not be scared to tell your boss and, like, move up that way.\u003C\u002Fp>\n\u003Cp>If you're scared to tell your boss, then maybe you're not ready to lead because you you can't be scared to do that stuff. That would be the first thing. And, after that, it would be don't be scared. Don't be afraid to fail. Like, that's the biggest thing.\u003C\u002Fp>\n\u003Cp>Like, because you're you're gonna make mistakes. You're if you especially if you're a new leader, you're just starting doing this. You're not gonna know everything off the bat. No one's perfect at this right away. It takes lots of practice.\u003C\u002Fp>\n\u003Cp>And then the last thing would just be be open to criticism and people telling you maybe that's not the best way. You have to be able to change and pivot and listen.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Yeah. That's awesome.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. That's that's such good advice actually is telling your boss, telling the people who have done that journey themselves because a good leader would want their their people underneath of them who want to progress in their careers to do be able to do that. So I don't think we've had anybody mention that, but it that is probably one of the most important things that you can do is find people who have done that journey and tell them. Get that guidance.\u003C\u002Fp>\n\u003Cp>Speaker 1: Mhmm.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Are there still leaders that, that you had in the past that you still stay in touch with and that maybe mentor you? Or how how valuable do you think the community that you've built, you know, over over your career is is still helping you today?\u003C\u002Fp>\n\u003Cp>Speaker 1: I've kinda lost touch with with, my first mentor I had. He ended up moving across the country. I haven't really kept in touch with him. But my peers, I definitely have. Like, my other, manager teammates that I had, actually brought a bunch of them in to work at that start up I was at.\u003C\u002Fp>\n\u003Cp>And\u003C\u002Fp>\n\u003Cp>Speaker 0: Oh, awesome. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: We had a really, really good tight team there, and I've kept in touch with a lot of the other manager friends that I've made over the years, dev managers.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. And a\u003C\u002Fp>\n\u003Cp>Speaker 1: lot of them have made it even higher than me so far. So Yeah. They're really on the way too. And, like, we all have our different opinions about to how to lead a team as well. And it's good to bounce those ideas off of them to see how they would handle the situation.\u003C\u002Fp>\n\u003Cp>What would they do different? How would they handle the communication between a problem person or someone who's a little bit or having issues? You know?\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. Yeah. I I think community is huge both in work and outside of work. And Pedro and I have been at 3 different companies together. Not all we didn't move every place at the same time, but we've, you know, kept in contact as well as other people at other companies I've in contact with who I feel like I can pour into them some advice, and, they can do the same for me.\u003C\u002Fp>\n\u003Cp>So I find that community is also a good answer, because Pedro and I are have been friends for a long time. So it's it's kind of good to see each other grow and encourage each other, to be able to do, you know, what we wanna do.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. And a little friendly competition as well. For sure. Especially since we're both we're both in the same role, just different territories now, so that's always thrilling.\u003C\u002Fp>\n\u003Cp>Speaker 2: Right. Yeah. We we have this East Coast, West Coast, spiff or or beef, I guess, is the right word to a little bit. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Crazy. I mean, we Cool.\u003C\u002Fp>\n\u003Cp>Speaker 1: No. I think by you, John. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. No. I think this is this is all really good advice. Like, I mean, I'm I'm even taking some of this to to apply to myself, obviously, as, like, an aspiring, leader in a future organization. That'd be this is really awesome help, for me as well.\u003C\u002Fp>\n\u003Cp>So, hopefully, everyone watching is, taking notes and and learning from you, but we'd love to know, like, where where can people follow you if they'd like to you know, you got LinkedIn and maybe some other projects, if you wanna just\u003C\u002Fp>\n\u003Cp>Speaker 1: take it off.\u003C\u002Fp>\n\u003Cp>Speaker 2: You can shout out your DJ thing if you want to.\u003C\u002Fp>\n\u003Cp>Speaker 0: Alright. For anyone in Buffalo. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: To be to be fair, I've kinda wound down my DJ business over for years since I haven't had time to do it with my my jobs. Yeah. So I've kinda pushed the way the band stuff's kinda taken more. That's that's more of my thing these days. But LinkedIn, you can find me at Thomas Moreno, and I'm also on Facebook and Instagram.\u003C\u002Fp>\n\u003Cp>Same name. And, yeah, that's pretty much it.\u003C\u002Fp>\n\u003Cp>Speaker 2: Awesome. Well, thanks so much for your time today. Everyone, go follow him. We'll put his information in our notes, but go follow him for some good wisdom. You you really gave some good advice there that I've used some of it myself and some that I also will take, into account for sure.\u003C\u002Fp>\n\u003Cp>So thanks so much for your time today, Tom.\u003C\u002Fp>\n\u003Cp>Speaker 0: Thanks, Tom.\u003C\u002Fp>\n\u003Cp>Speaker 1: Very much. Thanks, guys. Cheers. Bye.\u003C\u002Fp>","Just like a sidebar, I noticed you had some instruments in the back. Do you play guitar? Is that a guitar? It is a guitar. It's actually a guitar case. The guitar is millimeter. But I I, I'm not the typical dev. I, sing and play in a band. Dev rockstar. I like it. Yeah. Hey, everybody. Welcome back to another episode of Tech Talk. We have Tom with us. Tom, would you mind introducing yourself to everyone in the audience? Just give a brief introduction, who you are, what you do, and then we'll we'll kinda dive into the conversation for today. Name's Tom Marano. I live in Buffalo, New York, and I am currently a technical lead for SMX Consulting on a, state project. Awesome. Thanks so much. We, we had a intro conversation a couple weeks ago, and you had a couple interesting stories from your background. So if you don't mind, could you give us a little bit of your background in engineering, how you got to where you are today? We'll probably interrupt you a couple times to ask some questions, but remember a couple points that I'd I'd love to see if you bring back up just about your history. So how did you get into engineering and kind of what's what's been your journey along the way? So I've always been a fan of technology. I was the kid who literally took everything apart in my parents' house. Yes, my dad how many times it took apart his, like, $1,000 stereo and tried to put it back together. Happened a lot. So always been big fan of just anything tech. Love video games, obviously, growing up. And, actually got my start really building computers and everything when I was younger and CD burners became a thing. And, I quickly learned how to, get music, but that, you know, is, the legibly as possible. Yeah. Yeah. The leg away. So quickly learn how to burn CDs, and I actually, would make CDs for people in in high school and everything and, you know, sell them to people, for, you know, 5 bucks. And, through school too, I I went on a program a little bit, did a little bit HTML and, c on the side. And then, college stuck with that as as far as school went. I did deviate a little bit. I got into a little bit of electrical, engineering a little bit for a couple years and then decided I liked programming better. So, went back and, got my bachelor's in computer science, from an online school and got my first job, programming for a health care company here in Buffalo, learning the ropes, most of it the hard way because there wasn't a ton of guidance for me at most of the places I worked early on in my career, unfortunately. It was kind of the wild west where it was just kinda me figuring it out. And also, demands the Internet wasn't as big as it is now, so it wasn't a good resource to even fall back on. So it was always hard trying to figure my way around things. And then as I kept growing in my career, jumping from position to position, I was able to find some decent mentors around me and, eventually ended up at a larger, health care comp well, benefits company here in Buffalo that was very, very well structured as far as their documentation organization. Had a really, really good agile practice in place. And, also, like, kinda gave me the guidance as far as, like, how to properly run SDLC and for an organization and just give me those guideposts as far as, like, how you can effectively create a really good application. And just good communication practices, good team camaraderie, ideas being spread openly around. It was all learned at this, one shop in Buffalo I worked at. They're actually not in existence anymore. They're called Liaison, so I'll just throw the name out there. But they were a startup here in Buffalo for years, and I was fortunate enough to meet a lot of really close dev friends when I was there. All of us have gone our separate ways and had our own successes, but it was a really good group of people to work with, and I learned a lot from them. And that's where I got my first leadership role too and, learned a lot there. One of the trainings they gave us for all of our the new team leads out there was, something called radical candor, which is a book that's out there a lot of people read. And really good, information there as how to, like, communicate with your team effectively to get get them to focus on the task at hand, find out what they need to do, but also not be a dick doing it, you know. Yeah. You don't have to roll with nigh or fist. You know, you can you can just be stern, tell them what they need to do, but also listen and understand why they might be thinking a wrong thing or in a certain direction, which could even open up your eyes if you're thinking about it wrong too. So that that just opening up that dialogue back and forth really good. And, and when my time ended there, I ended up going to a couple other places. One was a start up I worked at about a about 6 or 7 months ago. That was a new experience for me too. Never worked at a startup that was very, very small, but, ended up being brought in as, like, the second to the VP of engineering. And, eventually, he ended up leaving and, you know, a role was offered to me, and I got thrown to the wolves trying to learn how to run an entire software organization by myself. It was small still, but learned a lot of things the hard way and how to really keep things organized from a very high level. And so direct people down, empower them to run the other parts of the team that I can't have my hands in all the time because I have to think of things from the higher level and think forward as far as, like, getting the product out. Yeah. Yeah. Awesome. Yeah. Unfortunately, that one folded too. But, I then got this role as a consultant for SMX and, Ceramic. That's awesome. Nice. I I'd love to hear a little bit about your path from you know, obviously, you've worked for the larger organizations, the startups. I'd love to hear your perspective on the difference and what the problems are, if they're more similar or more different between those, like, large scale organizations or small ones. Like, I know coming from, you know, most of my background was moving to San Francisco, working for a bunch of different startups and ad tech, all kinds of different technology startups, music as well. But then I went to work at Juniper Networks. That's actually where I met John. And, obviously, Juniper Networks is a 10,000 person organization. So it's it's fun to see the mess from, you know, the the small problems and the startup land versus, like, the more corporate bureaucracy stuff. But I'd love to hear from your perspective, like, which which you enjoyed more, and just how you kind of see the difference in those, organizations and and building and growing. Yeah. I will say the one common theme that almost every company I work for has is organization. Like, it's hands down, it's it's the number one issue that most of them have. And Yeah. Depending on who's in charge, it could go good or bad if they're willing to listen to change, if they want to move and pivot. I'm learning that a lot actually through the consulting work I'm doing now. This is my first consulting position I've ever held. And just seeing how the client is struggling with that, and they brought the team I'm with in to try to help them just use agile, which is, you know, pretty common in what most of us devs do these days. Most places use agile. Some better than others, but they at least try. And, the biggest thing there is just breaking the problem into smaller pieces, which is more digestible and doesn't seem like the world is gonna fall apart because this this problem is just so huge. Like, that's the one benefit of agile. You know? And it's if if you don't have people at the top that can see that and there's no one voicing it, you just have a bunch of, yes men all over the place just nodding their heads and saying, yep. We'll get that done. We'll get that done. The weight usually falls down on the people at the bottom doing the work, which is the devs. So then you don't get a good team of devs because they don't wanna be there and deal with that crap. You know? Yep. You know, they they they just wanna do their job most of the time and go home and feel like they've built something cool that day for the most part. You know? And that's that's like the feel I've loved. And the the first time I really got it was at that, dev shop I worked at in Buffalo for that big gap, where I learned most of what I know today. And, before that, it was a lot of chaos. Like, the IT department was always just like those guys in the corner. They do their thing. Nobody really cares. And and it it there wasn't really any organization around it. There there was just yeah. Can you guys get this done for us? You know? We need it soon. Yeah. And and being part of that, large organization that was very well structured really showed me the benefit of just working together collaboratively with people and not just saying throw it to that guy over the fence and he'll figure it out. You know? That that whole, like, planning phase of building the product that you're on became more enjoyable. Learning more about the architecture patterns became more enjoyable because I was I felt more invested in the work. Yeah. And that was that was a big thing. Like, just feeling like what I did made a difference was like a huge eye opener to me. Like, oh, this I can make a difference doing this stuff. This is cool. I have my voice matters. I'm not just the guy in the corner. You know? Yeah. It's it's always the that that's that's the thing that just made work enjoyable for me. Yeah. Yeah. I'd like to go back to something that you mentioned from your childhood that kind of peaked you started your curiosity. You mentioned taking things apart and trying to rebuild them. That's something that my dad used to do. He used to take apart things back in the sixties, and, that was something that I did when I was a kid. I would take apart things, and I I would usually not rebuild them. I would usually take them apart just to see how they worked and then leave the parts everywhere. Mhmm. But I've I found that that kind of curiosity has transitioned into adulthood still with physical things. Like, if the weed whacker is broken, I'll just take it apart. I, you know, I don't wanna go buy a new one, so I'll fix it. But I find that some people are just scared to take things apart and try it, not only with physical things, but also with software or leadership or organizations or you know? As as you grow in leadership, you have to wear many hats. So do you find that that's also a similar case for you that because you were able to have that curiosity when you were young and did first off, did it continue to these days, you still kinda tinker with things like that. But do you find that the ability to be comfortable taking things apart and dissecting things has helped you grow into a leadership position both in bigger companies and start ups? Definitely. The the curiosity to not be afraid to try things on my own, that's the biggest thing I think that probably helped me become a leader right now just because I I will I'm not I'm not scared to speak out. Like, I I will let my voice be heard if I don't agree with a point. Sometimes through a fault, I might Yep. Not I might put my foot in the mouth sometimes, you know, and yeah. But at at least I'm not scared of expressing my point of view and trying to get it across. Not always right. Nobody's nobody's ever always right. That's that's the one thing And, you know, when I was younger, I was definitely more hard headed. Definitely more hard headed. As I got older, it I just slowed I would say probably slowed down is the best way to put it and wasn't so like go go go go attitude. It was more like, okay, let me listen and figure out what other people are thinking. Yeah. And try and get their point of view. Like that's that was the biggest change, beginning into the leadership role was like trying to change my mindset that way and, the the one like, I'll bring back up Radical Candor. That's one of the big points in there is to be a good listener and understand your people, understand their points of view, understand why they think this way or approach they're taking is right. You know, might not be right, but at least you can understand the point of view and can then, if they are wrong, you can then tell them, well, it's that's not exactly right because a, b and c. Because you can then understand why the thing is. You don't just yell at them and say, no. You have to do it this way. You know, you you could talk to people that way. They usually can come to some, rapport with them a little bit easier than just saying get this done or you're in trouble or something. Yeah. There's a differ difference between a boss and a leader. So Mhmm. I I totally agree. Do you find those types of yeah. We hope our boss isn't listening, but, but, yeah, yeah, do you do you find that you're able to then also see those types of characteristics in people that you're now leading? Or when you were climbing the ranks, did you see those traits in nobody else, and that's why it was potentially easier for you to also climb into leadership? And then do you see that those types of traits in those that are around you, whether they're other leaders or people who work for you? Definitely for sure. It's it's not everyone either. Like, there's there's a lot of devs, obviously, who are very introverted. It's just the nature of what we do. You know, a lot of our stuff that we work on is solo by ourselves, and that's the way a lot of us like it. Not everyone's born to be a leader. They just, like, wanna get their work done or work on something cool by themselves. Don't really care too much about anything else. I definitely have some of those on my team right now. I will say they are sometimes the hardest ones to manage because you have to pull information out of them most of the time. But the ones that do have that curiosity to go the extra mile without having to be told exactly what to do, Those ones definitely have those traits in them because they they wanna go the extra mile. They wanna look at the next step without having to be told to go look at the next step. They they wanna understand why they just did and created the, went through the business roles. They just added into the app. They wanna know why they had to do that. So they're going to go down the path, understand the whole process, the whole business process they just had to implement and not just the little piece they worked on. Yeah. And that that helps give them a bigger picture so that they can maybe go into designing a whole system next time and then leading that design and so on and so on. Just building those steps because they have that curiosity to get their head around the whole thing, which is, I think, another part of being a good leader too, to understand everything involved with the processes that you're managing and going through. Because if you if you don't understand that, you're just some guy talking in the back telling people what to do all day. Right. Just issuing directives. Yeah. How do you when you identify these traits in folks, as their leader, like, how do you take those skills and and build on to them to, you know, train the next, you know, generation of leaders or or help build them into something like your role that you have now? Slowly giving them more responsibility, I think, is the first thing. Like, gaining that trust with them, and even letting letting them fail too sometimes is good because they need to be able to deal with some of the failures too that come with it if they maybe pick a bad decision. Learning how to bounce back from that or pivot to a different direction if they realize, oh, this isn't gonna work. We gotta try something else. But, yeah, it's slowly giving them more and more responsibilities and eventually even letting them lead certain projects or whatever you happen to be on. That's that's a good step to try to let them spread their wings a little bit Yeah. I think. Do you ever do you ever miss being an or do you still individually contribute, or do you ever miss not being a leader? Are there other times where you're you just say, man, I wish I was just plugging away at code or or developing something today and not having to deal with bureaucracy or just some sort of issue that arises that leaders have to deal with? Or do you just love everything that comes at you, deal with the things that are pain in the ass, and and or or, you know, how do you feel about being a leader in your future for yourself as well? I mean, I'm human. I'm gonna have days where I just don't wanna deal with some stuff going on. Like, if there's a 1,000,000 fires going on and I just wanna rip my hair out, it's like, I can't do this today. Gonna gonna have those days. It happens. But, I do love just leading the project and deciding the direction that, say the team goes or say technology paths that we investigate and go down. It it does it is harder for me sometimes to to pull away. Like, I I do wanna get involved and, like, actually do some coding once in a while. I I get to once in a while. I do it on the side still. Did mention this earlier. I know we talked about it in our pre call, but I do kind of run a a DJ business on the side that I like, always use as, like, a testing ground for some of my, project some of just my skill and expertise, like, growing that. I did a lot with dotnet in there at in the Azure world. And, it's it's helped me just experiment with things in the side and understand more, especially around, cloud architecture and design. I learned a lot of that just on my own ad hoc, and I was able to apply it when I got to that startup role. I did in the role before this. Yeah. Yeah. I guess the side projects are a good way to, like, flex those coding muscles still where you're not able to individually contribute to a project you're managing or leading, at least, like, on the side. Mhmm. Yeah. They keep you so that's really cool. Yeah. They keep you up to date too with some of the the cool new tools out there as well, you know, because you don't always get to use those in your job. Yeah. Sometimes it's good to know that because then you can even say, hey. When some problem comes around, maybe it can be solved by this thing I messed with at home. Yeah. That was that was actually gonna be my next question was what, you know, what is some new tech that you've been able to find in those side projects or in just general browsing the Internet that you find cool with others? We've talked about cryptocurrency and and Bitcoin and random types of things and AI, obviously, but are there some tools out there that you've started to uncover personally or in your regular job that you really see some potential to have a really cool use case for yourself? I mean, Python is the new hotness everybody's learning these days, mostly just for the big data stuff, especially with Fintech. I've I've started dabbling with that. The the client expressed an interest in it. So I went on the side to start learning it on my own just so I can understand maybe from a perspective that, it can be implemented with the clients. To be fair, I I don't know if it's going to work for the clients if we're looking into it, but at least I know ahead of time before just saying, yeah. Let's do that. Yeah. Let's see. I I've I I've dabbled a lot with, a lot of PaaS and SaaS offerings in the cloud, mostly mostly in Azure, a little bit in AWS, using very, small services using, like, some of the functions in Azure along with, like, a front end. It's a very low cost way of creating, like, a a web application semi quickly. Yeah. And then, of course, everything you can do with containers these days in even you can even wrap Azure functions now in containers. They're so handy. And running in, like, Linux Linux boxes, you don't even have to stand up Windows boxes anymore. It's always I mean, it's been around for a while now. It's it's gaining more traction these days by more companies, which is good, I think. Yeah. For sure. I think it's it's funny you mentioned that too because I think for the side projects and and just for these, like, you know, self applications or learning on the side, that's actually how we hear a lot of developers in our community find out about about Directus, about us. Right? Like, open source technology. It's like, hey. Let me try this for a side project. And then all of a sudden, at some point, their their business or their company has a need that it's like, oh, this might be perfect. I've been using this for years on my, you know, DJ side business. Like, maybe it could apply to this organization I'm working with. So, that that's pretty cool to hear. I also just like a sidebar, I noticed you had some instruments in the back. Do you play guitar? Is that a guitar? It is a guitar. It's actually a guitar case. The guitar is in the living room. Awesome. I I, I'm not the typical dev. I, sing and play in a band. Dev rock star. I like it. Yeah. That's a very common trait. We see yeah. We see a lot of developers who you have to have side hobbies as well. I I doubt any developers, hopefully, not too many, sit in front of a computer for 18 hours a day. Though it maybe it's gaming. You know, that's a hobby too, but we find a lot of devs, play instruments. And maybe it's because instruments are kind of it's kind of like a puzzle and taking apart things and putting them together. Music is kind of like a puzzle like that as well. So is that something you can use for your Right? Still math and language and basically the same as, like, coding just in a different if in a different way. I think we have enough musicians within Directus to form a band, but maybe we do that one day. Yep. That big dev shop I worked at, there was a lot of guitar players a lot. Like, some of the some of the, director level guys, they all had, like, acoustics in their office. Yeah. I remember when I got to when I moved to San Francisco, I'd gone to the the Dropbox office, and, I think the CEO was like a drummer or guitarist or something. And they had set up 2 jam rooms where people could go and, like, they had the whole full band set up, and you can go at any time and just jam out and take out your frustration or whatever you wanna do on the drums. And, I thought that's a really cool idea. That is a very cool idea. I like that. If I worked at a physical office anymore, I would say, yeah. Let's do that. Yeah. Yeah. Right. Right. What is there anything that you see that might you know, what are your takes on things like AI? Are there things out there that you see that have are overblown or misunderstood? You know? We hear a lot of people with different opinions on that or maybe a technology that you that other people use that you find overblown. Anything that you see on that side of the spectrum? I mean, I personally think AI is a big hype right now just like, NFTs before and then, you know, Bitcoin before. I mean, Bitcoin is still big now, but, you know, at its peak, it was ginormous, you know. Yeah. Now it's slowly coming back down to earth. NFTs have pretty much died. And AI, it's it's got its benefits. It's cool, but it's it's the way people communicate about it that's, I think, wrong. It's it's not actual AI, like the definition. It can't think by itself. It's just large language Yeah. Large language models. It's just taking it from a very big dictionary and guessing what it should be based on a bunch of other texts that it has in this thing. It can't actually think by itself. Yeah. And that's that's the one thing people get all crazy about, like, thinking it's gonna take everyone's job. Yeah. Like, sentient stuff. Yeah. It's not sentient. Yeah. Can't do that yet. Someday it probably will, but it's it's not. And that's that's the one thing. It's it's always I always find a little bit just annoying when people freak out about it. It's like it's it's not there yet. Yeah. Do you think the sentience is, like, years away, decades away? I mean, if I mean, it's hard to tell. Yeah. It's really hard to tell. I mean, it's I guess only the devs at OpenAI and Entropic and some of those others probably know, like, what the timeline looks like. Yeah. I don't even know if they're they're on the right track with that. Because, like, honestly, if they're gonna do sentience, it would have to be probably more maps like a human brain. And I don't think that's the way they're doing any of these AI model like, models. I think they're all just big dictionaries for the most part, but guess as good as mine. Yeah. So what would be some advice you give for people on your team, for example, whom you might see leadership traits in or other people who would be watching this? If somebody feels like they have the traits to be a leader obviously, you've mentioned a book that I myself have read, and that book I do think is an incredible book. Mhmm. But you've mentioned not being afraid to try things, not being afraid to fail. What is your advice to people who still might have hesitance to even try something because they are scared, Or what are some other things that you would give some advice for people looking to grow into a leadership position? The biggest thing is tell your boss. Like, communicate that with your boss. That's that's where I started. I told my boss I wanted to do these things, and they helped me get my position myself in the right place so I eventually could be moved up to a team lead and run up my run my first team. So you you really have to just not be scared to tell your boss and, like, move up that way. If you're scared to tell your boss, then maybe you're not ready to lead because you you can't be scared to do that stuff. That would be the first thing. And, after that, it would be don't be scared. Don't be afraid to fail. Like, that's the biggest thing. Like, because you're you're gonna make mistakes. You're if you especially if you're a new leader, you're just starting doing this. You're not gonna know everything off the bat. No one's perfect at this right away. It takes lots of practice. And then the last thing would just be be open to criticism and people telling you maybe that's not the best way. You have to be able to change and pivot and listen. Yeah. Yeah. That's awesome. Yeah. That's that's such good advice actually is telling your boss, telling the people who have done that journey themselves because a good leader would want their their people underneath of them who want to progress in their careers to do be able to do that. So I don't think we've had anybody mention that, but it that is probably one of the most important things that you can do is find people who have done that journey and tell them. Get that guidance. Mhmm. Yeah. Are there still leaders that, that you had in the past that you still stay in touch with and that maybe mentor you? Or how how valuable do you think the community that you've built, you know, over over your career is is still helping you today? I've kinda lost touch with with, my first mentor I had. He ended up moving across the country. I haven't really kept in touch with him. But my peers, I definitely have. Like, my other, manager teammates that I had, actually brought a bunch of them in to work at that start up I was at. And Oh, awesome. Yeah. We had a really, really good tight team there, and I've kept in touch with a lot of the other manager friends that I've made over the years, dev managers. Yeah. And a lot of them have made it even higher than me so far. So Yeah. They're really on the way too. And, like, we all have our different opinions about to how to lead a team as well. And it's good to bounce those ideas off of them to see how they would handle the situation. What would they do different? How would they handle the communication between a problem person or someone who's a little bit or having issues? You know? Yeah. Yeah. I I think community is huge both in work and outside of work. And Pedro and I have been at 3 different companies together. Not all we didn't move every place at the same time, but we've, you know, kept in contact as well as other people at other companies I've in contact with who I feel like I can pour into them some advice, and, they can do the same for me. So I find that community is also a good answer, because Pedro and I are have been friends for a long time. So it's it's kind of good to see each other grow and encourage each other, to be able to do, you know, what we wanna do. Yeah. And a little friendly competition as well. For sure. Especially since we're both we're both in the same role, just different territories now, so that's always thrilling. Right. Yeah. We we have this East Coast, West Coast, spiff or or beef, I guess, is the right word to a little bit. Yeah. Yeah. Crazy. I mean, we Cool. No. I think by you, John. Yeah. Yeah. No. I think this is this is all really good advice. Like, I mean, I'm I'm even taking some of this to to apply to myself, obviously, as, like, an aspiring, leader in a future organization. That'd be this is really awesome help, for me as well. So, hopefully, everyone watching is, taking notes and and learning from you, but we'd love to know, like, where where can people follow you if they'd like to you know, you got LinkedIn and maybe some other projects, if you wanna just take it off. You can shout out your DJ thing if you want to. Alright. For anyone in Buffalo. Yeah. To be to be fair, I've kinda wound down my DJ business over for years since I haven't had time to do it with my my jobs. Yeah. So I've kinda pushed the way the band stuff's kinda taken more. That's that's more of my thing these days. But LinkedIn, you can find me at Thomas Moreno, and I'm also on Facebook and Instagram. Same name. And, yeah, that's pretty much it. Awesome. Well, thanks so much for your time today. Everyone, go follow him. We'll put his information in our notes, but go follow him for some good wisdom. You you really gave some good advice there that I've used some of it myself and some that I also will take, into account for sure. So thanks so much for your time today, Tom. Thanks, Tom. Very much. Thanks, guys. Cheers. Bye.",[4534,4535,4536],"5f9147ce-609f-45b2-95df-01484cebb76d","ac91458f-42c8-4901-ba2a-7cb001df3561","1703437e-f697-421d-9873-484322109e74",[],{"year":2380,"number":739,"id":4539,"episodes":4540,"show":4545},"53382dd3-4dd7-44f0-9102-a257b4760115",[4541,4542,4543,4544,4515],"68c8fd05-ac09-4485-aa57-bb8d74a26699","f8864aad-281c-4921-9722-3b6b29d57eb4","83739ed2-e3ac-485c-97fa-f683db114386","836d591e-0f5c-47db-9756-c87d97283c02",{"title":1849,"slug":1850},{"id":3958,"slug":4547,"vimeo_id":4548,"description":4549,"tile":4550,"length":740,"resources":12,"people":4551,"episode_number":264,"published":4553,"title":4554,"video_transcript_html":4555,"video_transcript_text":4556,"content":12,"seo":12,"status":19,"episode_people":4557,"recommendations":4559,"season":4560},"sveltekit","913399729","In this video, you'll learn how to use the Directus SDK with a SvelteKit application.","5b126eef-f2fe-495a-ae0d-89346aa616e8",[4552],{"name":2820,"url":2821},"2024-02-21","Get Started with Directus and SvelteKit","\u003Cp>Speaker 0: This is a brand new SvelteKit project created using the Svelte CLI. In this video, I'm gonna show you how to use Directus and SvelteKit using the Directus SDK. Before we get started, make sure to actually install the Directus SDK. And once you've done that, run npm run dev to start the development server. Let's set up the Directus SDK to make it accessible globally throughout our SvelteKit project.\u003C\u002Fp>\u003Cp>So let's create inside of this source lib directory a new file called directors dot j s. To make the best use out of SvelteKit server side rendering, we'll need to use SvelteKit's own fetch implementation. So that's what's happening here. We're importing the requisite functions from the SDK. We are creating and exporting a function called get directors instance, which expects one argument of fetch.\u003C\u002Fp>\u003Cp>We will pass in fetch there. And if we provide it, then we will overwrite the built in fetch using options with the Sveltekit version of fetch. And if not, then we will leave it alone, and we will use the native fetch option, effectively rendering this optional. But we're gonna do it throughout this tutorial to make the best use of Sveltekit. In order to make this work, we will also need to create a hooks dot server dot JS file inside of our source directory.\u003C\u002Fp>\u003Cp>It will make sure that the required headers for fetching JavaScript content are returned by the Sveltekit server. Inside of our directors project, we already have a global collection, which is a singleton with 2 fields, title and description. Inside of access control, the public role has also been given read access over all user created collections and the director's files collection. Inside of our routes directory function exported. We pass in a fetch implementation, which means we use Sveltekit's version of fetch.\u003C\u002Fp>\u003Cp>And then we go and use the Director's SDK to read the global collection. If we modify the page dot Svelte file to the following, we can now use the data which is loaded in that load function inside of page dotjs and access its values inside of the file. In our director's project, we also have this pages collection. Each item in the collection has a slug, which is meant to correlate to the URL path used. So this, for example, would load at slash about, title, and content, where content is a WYSIWYG or what you see is what you get editor.\u003C\u002Fp>\u003Cp>We get this nice visual editor, but underpinning it is just raw normal HTML. Inside of our routes directory, create a new folder called slug with square brackets to denote that this is a dynamic route. And inside of that, a file called plus page dot JS. In this file, we will load data for an individual page. We will import the read item function.\u003C\u002Fp>\u003Cp>We will grab the director's instance, and we will use the read item function to grab an item from the pages collection that matches the value of this dynamic piece here. So when this is about, it should load the about item from the director's project. And if that file doesn't exist, if that item doesn't exist, we will throw a 404. Now that this data is available, let's create a new file in this slug directory called plus page dot svelte. Inside of this slug directory, let's also create a plus page dot svelte file.\u003C\u002Fp>\u003Cp>And in here, just like before, we're going to gain access to the data that we exported out of the load function in the page dotjs file. And, once again, we can just use that directly within our HTML. Now what's important here is that we are using the at HTML piece here. And what this will do is render inside of this div the raw HTML from our director's project. You have to be confident that that HTML is well formed and not malicious in order to use this t m l piece here.\u003C\u002Fp>\u003Cp>So just note this is only for trusted content. Inside of our directors project, we also have a posts collection containing once again a slug, a title and a content field. Additionally, posts also have an image which is actually a relational field to the director's files collection, a published date, and an author. Author is also a relational field to one of the authors, which at this point only contain a name field. But I'm doing this quite minimally just to show you how to get relational data in just a moment.\u003C\u002Fp>\u003Cp>Inside of routes, create a new directory called blog. And inside of that, a file called page dotjs. We will load our data inside of here. In this file, we're going to use read items to get all of the items from the posts collection in order to create a blog post listing page. What's important here though is that we're also including a query object with 2 properties.\u003C\u002Fp>\u003Cp>Firstly, fields. Why are we doing this? Well, we're doing this for two main reasons. One, we don't want to get all of the data in all of the blog posts, specifically the content field. It's a really big text block which we're not actually going to use on this page.\u003C\u002Fp>\u003Cp>So let's not make the return data any larger than it has to be. Secondly, we want access to the relational data inside of the author collection. So we do it like this. This author is a field that sits inside of the post item and we wanna step through to the relational item, the nested item, and get the name. We're also using sort to ensure that as the data is sorted, the latest item comes first, which of course is quite typical for a blog post listing.\u003C\u002Fp>\u003Cp>In the corresponding page dot Svelte file, we are going to do a loop through each of the items that came back from Directus, rendering out a new list item for each. There will be a link that goes to slash blog slash slug, showing the title, the publish date, and the author's name. Now take note here that while slug, title, and publish date are all properties of the post object, Author being a relational field is a nested object. So we access the author name through post dot author dot name. To get data for an individual blog post inside of the blog directory, I've created a directory called slug with square brackets because it's dynamic.\u003C\u002Fp>\u003Cp>And inside of that, a page dot j s file. This again should look quite similar to when we fetched individual pages. So we're using read item. We're passing in the slug part of the URL while we're using read item to just get that individual item. But what's important here is that we're also, once again, including that query object and we're including fields.\u003C\u002Fp>\u003Cp>What this says is that we want to get every top level item, so every field inside of the post collection itself, but we wanna also get the author's name and we wanna get the images ID and description. The reason we're doing that will become clear in a moment. If this post is not found, we will throw a 404. In order to load an individual blog post page, I have created a new slug directory inside of blog. Once again, in square brackets because this is a dynamic route.\u003C\u002Fp>\u003Cp>Inside of that, I've created a page dot j s file. Now this should look quite similar from when we created dynamic pages. We are reading a single item from the post's collection, passing in the dynamic part of the URL as the ID, except this time we're using a query object. And we're doing that so we can specify that we want not only all of the post fields to come back, but that we also want the author name and we also want the image ID and description. The reason we're doing this will become clear in just a moment.\u003C\u002Fp>\u003Cp>We're also not only returning the post, but we are going to return the URL from the Director's SDK. So once again, here, we are saving get director's instance as a variable called director's, and we are going to get the URL out of that and return that to our Svelte template under the key directors. This is an individual blog post page. It's very similar to the page creation except we are now also rendering the blog post image. This is why we wanted to export the URL from Directus.\u003C\u002Fp>\u003Cp>Every asset in Directus can be accessed with the full image URL, which the SDK automatically adds a slash after when storing it. So there is a slash here, \u002Fassets\u002Fimageid. And then we're transforming it on the fly so it's only 600 pixels wide. We're using the description held inside of directors for this file as the alt text. And that is how you integrate Sveltekit and Directus.\u003C\u002Fp>\u003Cp>I hope you found this interesting and we'll see you in the next video.\u003C\u002Fp>","This is a brand new SvelteKit project created using the Svelte CLI. In this video, I'm gonna show you how to use Directus and SvelteKit using the Directus SDK. Before we get started, make sure to actually install the Directus SDK. And once you've done that, run npm run dev to start the development server. Let's set up the Directus SDK to make it accessible globally throughout our SvelteKit project. So let's create inside of this source lib directory a new file called directors dot j s. To make the best use out of SvelteKit server side rendering, we'll need to use SvelteKit's own fetch implementation. So that's what's happening here. We're importing the requisite functions from the SDK. We are creating and exporting a function called get directors instance, which expects one argument of fetch. We will pass in fetch there. And if we provide it, then we will overwrite the built in fetch using options with the Sveltekit version of fetch. And if not, then we will leave it alone, and we will use the native fetch option, effectively rendering this optional. But we're gonna do it throughout this tutorial to make the best use of Sveltekit. In order to make this work, we will also need to create a hooks dot server dot JS file inside of our source directory. It will make sure that the required headers for fetching JavaScript content are returned by the Sveltekit server. Inside of our directors project, we already have a global collection, which is a singleton with 2 fields, title and description. Inside of access control, the public role has also been given read access over all user created collections and the director's files collection. Inside of our routes directory function exported. We pass in a fetch implementation, which means we use Sveltekit's version of fetch. And then we go and use the Director's SDK to read the global collection. If we modify the page dot Svelte file to the following, we can now use the data which is loaded in that load function inside of page dotjs and access its values inside of the file. In our director's project, we also have this pages collection. Each item in the collection has a slug, which is meant to correlate to the URL path used. So this, for example, would load at slash about, title, and content, where content is a WYSIWYG or what you see is what you get editor. We get this nice visual editor, but underpinning it is just raw normal HTML. Inside of our routes directory, create a new folder called slug with square brackets to denote that this is a dynamic route. And inside of that, a file called plus page dot JS. In this file, we will load data for an individual page. We will import the read item function. We will grab the director's instance, and we will use the read item function to grab an item from the pages collection that matches the value of this dynamic piece here. So when this is about, it should load the about item from the director's project. And if that file doesn't exist, if that item doesn't exist, we will throw a 404. Now that this data is available, let's create a new file in this slug directory called plus page dot svelte. Inside of this slug directory, let's also create a plus page dot svelte file. And in here, just like before, we're going to gain access to the data that we exported out of the load function in the page dotjs file. And, once again, we can just use that directly within our HTML. Now what's important here is that we are using the at HTML piece here. And what this will do is render inside of this div the raw HTML from our director's project. You have to be confident that that HTML is well formed and not malicious in order to use this t m l piece here. So just note this is only for trusted content. Inside of our directors project, we also have a posts collection containing once again a slug, a title and a content field. Additionally, posts also have an image which is actually a relational field to the director's files collection, a published date, and an author. Author is also a relational field to one of the authors, which at this point only contain a name field. But I'm doing this quite minimally just to show you how to get relational data in just a moment. Inside of routes, create a new directory called blog. And inside of that, a file called page dotjs. We will load our data inside of here. In this file, we're going to use read items to get all of the items from the posts collection in order to create a blog post listing page. What's important here though is that we're also including a query object with 2 properties. Firstly, fields. Why are we doing this? Well, we're doing this for two main reasons. One, we don't want to get all of the data in all of the blog posts, specifically the content field. It's a really big text block which we're not actually going to use on this page. So let's not make the return data any larger than it has to be. Secondly, we want access to the relational data inside of the author collection. So we do it like this. This author is a field that sits inside of the post item and we wanna step through to the relational item, the nested item, and get the name. We're also using sort to ensure that as the data is sorted, the latest item comes first, which of course is quite typical for a blog post listing. In the corresponding page dot Svelte file, we are going to do a loop through each of the items that came back from Directus, rendering out a new list item for each. There will be a link that goes to slash blog slash slug, showing the title, the publish date, and the author's name. Now take note here that while slug, title, and publish date are all properties of the post object, Author being a relational field is a nested object. So we access the author name through post dot author dot name. To get data for an individual blog post inside of the blog directory, I've created a directory called slug with square brackets because it's dynamic. And inside of that, a page dot j s file. This again should look quite similar to when we fetched individual pages. So we're using read item. We're passing in the slug part of the URL while we're using read item to just get that individual item. But what's important here is that we're also, once again, including that query object and we're including fields. What this says is that we want to get every top level item, so every field inside of the post collection itself, but we wanna also get the author's name and we wanna get the images ID and description. The reason we're doing that will become clear in a moment. If this post is not found, we will throw a 404. In order to load an individual blog post page, I have created a new slug directory inside of blog. Once again, in square brackets because this is a dynamic route. Inside of that, I've created a page dot j s file. Now this should look quite similar from when we created dynamic pages. We are reading a single item from the post's collection, passing in the dynamic part of the URL as the ID, except this time we're using a query object. And we're doing that so we can specify that we want not only all of the post fields to come back, but that we also want the author name and we also want the image ID and description. The reason we're doing this will become clear in just a moment. We're also not only returning the post, but we are going to return the URL from the Director's SDK. So once again, here, we are saving get director's instance as a variable called director's, and we are going to get the URL out of that and return that to our Svelte template under the key directors. This is an individual blog post page. It's very similar to the page creation except we are now also rendering the blog post image. This is why we wanted to export the URL from Directus. Every asset in Directus can be accessed with the full image URL, which the SDK automatically adds a slash after when storing it. So there is a slash here, \u002Fassets\u002Fimageid. And then we're transforming it on the fly so it's only 600 pixels wide. We're using the description held inside of directors for this file as the alt text. And that is how you integrate Sveltekit and Directus. I hope you found this interesting and we'll see you in the next video.",[4558],"35f81b7f-e6e3-41b6-b071-944933765a41",[],{"year":978,"number":230,"id":3954,"episodes":4561,"show":4562},[3956,3957,3958,3959,3940],{"title":3961,"slug":3962},{"id":3996,"slug":4564,"vimeo_id":4565,"description":4566,"tile":4567,"length":4568,"resources":4569,"people":4573,"episode_number":177,"published":4578,"title":4579,"video_transcript_html":4580,"video_transcript_text":4581,"content":12,"seo":12,"status":19,"episode_people":4582,"recommendations":4585,"season":4586},"media-ai-bundle","906133845","Esther speaks to community member Marcus about their Media AI Bundle - a group of extensions that allow extraction of key details using AI tools. ","d7ad67a6-de8b-4f29-a5a8-c17b05389e34",22,[4570],{"name":4571,"url":4572},"Media AI Bundle on GitHub","https:\u002F\u002Fgithub.com\u002FArood\u002Fdirectus-extension-media-ai-bundle",[4574,4575],{"name":3978,"url":3979},{"name":4576,"url":4577},"Marcus Olovsson","https:\u002F\u002Fmarcusolovsson.com\u002F","2024-02-20","Media AI Bundle","\u003Cp>Speaker 0: But I'm also open to add more AI features, depending on use cases.\u003C\u002Fp>\u003Cp>Speaker 1: Hi there, and welcome to another episode of the Beyond the Core Show. It's a director show where we shine the spotlight on extension developers in the community. My name is Esther Agbaji, and I work as a developer advocate at Directus. And today, I'm joined by a super community member, Ahud. Ahud is the community winner of the AI hackathon that held a couple of months back, and he won the hackathon by creating the media AI bundle extension.\u003C\u002Fp>\u003Cp>And today, he's here with me to share all about the exciting journey to develop Hynis Extension and some of its features. So thank you, Arud, for joining me here. Could you share, like, some background about yourself and how you get to know about directors?\u003C\u002Fp>\u003Cp>Speaker 0: Sure. I I work as a developer, at an agency. Okay. And I was looking for, alternatives to CMSs like WordPress, for example, but I wanted something more, API focused.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: So so we had so we could find something that works with larger projects, and more, like, apps and such. And after trying some of them out, I I wasn't really happy with how they worked. They weren't, like, easy to use for the users, at least in my opinion. But then in a Reddit post, I think it was, someone was mentioning, directors and, like, how it was. They they said it was fantastic, but the the it was kind of, under the radar, at that time.\u003C\u002Fp>\u003Cp>So Okay. I checked it out, and I liked it too much. So, I started trying to, like, get it approved to to be used in our agency.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: And now we have successfully rolled out a few projects using it. And so far, it's been great.\u003C\u002Fp>\u003Cp>Speaker 1: Interesting. Interesting. And what's your favorite director's feature?\u003C\u002Fp>\u003Cp>Speaker 0: I think it's the interface. Okay. How extensible it is, and how how I can fine tune it for the editors.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Okay. So you like the fact that, like, it's very intuitive and is also extensible. Yeah. Yeah.\u003C\u002Fp>\u003Cp>I'm sure you probably like the extension feature because you've created not just 1 or 2 extensions at least that I know of. And, yeah, we are here to talk about the media AI bundle, which is a really cool extension. I saw the demo that you did during submission, and it was really good. So, what was the motivation for actually creating this extension?\u003C\u002Fp>\u003Cp>Speaker 0: It started off as, idea for a personal project of mine. I wanted to be able to take pictures, of sticky notes.\u003C\u002Fp>\u003Cp>Speaker 1: Interesting.\u003C\u002Fp>\u003Cp>Speaker 0: Like photos with my phone and upload them to a Kanban board or similar. So that's what got me started. I was I had some APIs in mind that I wanted to use, but now I had a like, an excuse to get it working in directors. So I started working on the, an operations bundle. And then I realized that this could be used for much more.\u003C\u002Fp>\u003Cp>For example, the the integration with alt text dot io so you can get alt text for images. So that kind of, formed it into becoming the media, bundle.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Okay. Nice to hear. So you mentioned the old text. Is it like an AI API?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: So it's like a service you sign up for, and they they transform the image into a readable text, like a sentence or\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Sentences for that describes the image.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Okay. So, basically, the extension that you created, you upload an image, and it just gives you the old text or even if it's a screenshot of maybe, you know, text on a piece of paper, you can also read and extract that text also. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And I have 2 two operations currently in the bundle. 1 is for the, one I call describe, image, which does the alt text thing. It takes the image and extracts the text or sentence. You can also use Amazon Web Services, but then you'll only get, like, a comma separated list of words.\u003C\u002Fp>\u003Cp>So it's not as fancy. Okay. But it's an alternate alternative Alternative. Okay. Yeah.\u003C\u002Fp>\u003Cp>And the other one is the extract text operation that actually reads, with, like, OCR, and tries to find text in an image. So you can extract.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Extract the text from an image. Yeah. Alright. Yeah.\u003C\u002Fp>\u003Cp>Would you like to share your screen? So just walk us through maybe some parts of the code and then a quick demo of the extension?\u003C\u002Fp>\u003Cp>Speaker 0: So as I mentioned, it's a bundle. So you have a source directory with the different operations in this case. And the plan was to add more, as I get more, like, cases\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: Use cases.\u003C\u002Fp>\u003Cp>Speaker 1: Use cases. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: So here we have the describe image, and the extract text from the image operation. So if you take the describe the image, I think that's the more more fun one.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: We have the front end part or the app part Mhmm. Which defines the operation. And here we have some settings that you can set up. You can choose if you want to use alt text dot io or Amazon Amazon\u003C\u002Fp>\u003Cp>Speaker 1: Cognition. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: And you you will need API keys for these and Mhmm. You set them up in your environment variables. Okay. But I'm hoping to add some kind of, like, settings page or something, where you can do it a bit easier.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: And then on the, client side or or the server side\u003C\u002Fp>\u003Cp>Speaker 1: Service side. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: We have the API for this, or the director's API. So we get an image. You you can set up, like, a hook for or a flow with a hook for file upload.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: And then we'll check if we have, like, a image, if the file is an image, and then we will create a buffer from it so we can send the entire image from\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: To this to the API.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. So you first verify if it's an image before you then send it.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So Okay. We don't waste any, like, credits or such. Okay. And then we get a result.\u003C\u002Fp>\u003Cp>And, I try to, like, return a common format for it. So it shouldn't depend matter which API you use. You should have, like, a you should be able to expect what kind of\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Properties it will return, and they are documented in the readme.\u003C\u002Fp>\u003Cp>Speaker 1: In the readme. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: So in this case, you get the description. But I also have, like, a dollar param row property that has the original payload. So if you want to get something specific from Amazon or from alt text, you can\u003C\u002Fp>\u003Cp>Speaker 1: You can get\u003C\u002Fp>\u003Cp>Speaker 0: that as well.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Nice. Without having to exchange the the extension. And the same thing here for Amazon. The API used there is called detect labels.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Nice. That's how it works.\u003C\u002Fp>\u003Cp>Speaker 1: That's That's the one for describing the image. Yeah? Yeah. Okay. Okay.\u003C\u002Fp>\u003Cp>Let's check out the one for, like, extracting the text from image briefly.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It works pretty much the same, except it doesn't have it only has Amazon recognition for now.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: I am we I am planning to add ash Azure Vision AI.\u003C\u002Fp>\u003Cp>Speaker 1: Azure. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Because we use that a lot at my work.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: But for now, it's just Amazon.\u003C\u002Fp>\u003Cp>Speaker 1: Alright.\u003C\u002Fp>\u003Cp>Speaker 0: But the principle is the same. We we you you we receive an image. We take the stream, and send it to, an API. Mhmm. And the only difference here is, of course, which, API we're sending it to.\u003C\u002Fp>\u003Cp>So in this case, it's the detect text command\u003C\u002Fp>\u003Cp>Speaker 1: Command. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: On the Amazon SDK. And in this case, you get a bit more parameters back, property spec. So you can get either the I try to transform them into lines, with text and, like, where in the image it's located. But you also get a full text if you're just trying to, like, transcribe an image.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So if you want that quick and easy fix, you can use the full text form.\u003C\u002Fp>\u003Cp>Speaker 1: Full text. Okay. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: But if you want to, like, get down with the greater details, you can use the lines.\u003C\u002Fp>\u003Cp>Speaker 1: The lines. Yeah. That's very clear and nifty. Did you face any challenges or issues when you were building, you know, all of these bundles?\u003C\u002Fp>\u003Cp>Speaker 0: I think the for for the most part, it's it's been pretty\u003C\u002Fp>\u003Cp>Speaker 1: No. It's pretty straightforward. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Of course, the the I guess the one thing that was a bit tricky is, like, trying to find the right services. I know that that has been added to the documentation now.\u003C\u002Fp>\u003Cp>Speaker 1: Yes. It has.\u003C\u002Fp>\u003Cp>Speaker 0: When I wrote this, there wasn't really much.\u003C\u002Fp>\u003Cp>Speaker 1: Wasn't. Yeah. True. I remember. Yeah.\u003C\u002Fp>\u003Cp>True. But now we've included services in the docs.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So getting the, like, asset service and figuring out how that works, took a bit of time. It wasn't wasn't hard, but you had to, like, shake your house.\u003C\u002Fp>\u003Cp>Speaker 1: Again, to the core and the doors.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. But that's also a strength of directors, I think, yeah, that you can do that.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Can you imagine that? Okay. That's cool. Let's go into the bundle to see how it works in directors.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. So I have a flow here. Let me see in a bit.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. It's good now.\u003C\u002Fp>\u003Cp>Speaker 0: Called file uploads.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: And here we have when I created this, I made a trigger with non blocking action with the scope files.\u003C\u002Fp>\u003Cp>Speaker 1: Files that's uploaded.\u003C\u002Fp>\u003Cp>Speaker 0: Mhmm. Currently, there's no filter for it. I think I'm gonna write a feature request for that. I think it would be useful to, like, be wait for the\u003C\u002Fp>\u003Cp>Speaker 1: Before I wait for some time before I\u003C\u002Fp>\u003Cp>Speaker 0: finish before fires up. Yeah. Because right now, you you when you upload it, you you get sent to the, like, start page, and you don't see the changes until they have performed. So you have to wait a bit.\u003C\u002Fp>\u003Cp>Speaker 1: I see. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: But for now, it works well with the non blocking one. Then we have the operation from my extension, describe image. And here we are select which API we want to use and you are able to change the field if you want to. But in most cases, it's the trigger dot key, which is the image you have uploaded. But I'm left it a bit configurable if you have more advanced flows.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Then when this is run, you get some data. So I have an update data operation here.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: Where I simply just update the, payload of the file that's created in Directus. So in this case, I put the description from the operation into the description field.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: In some cases, I might put a transcript or similar in between just to clean up a bit, especially if I use the other, operation that can the the extract text one. So if you if you would need to, like, make sure that it's not too long or something like that.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Do that. I just remembered, did you, maybe handle errors and all in the whole, you know, operations?\u003C\u002Fp>\u003Cp>Speaker 0: Some errors, but in most cases, it will silently fail. Like, if you upload a image that isn't or a file that isn't an image. It will just silently It's a test. Failed.\u003C\u002Fp>\u003Cp>Speaker 1: So you don't Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So you don't get, like, a lot of error messages. But, I'm not sure if I I think I have some,\u003C\u002Fp>\u003Cp>Speaker 1: yeah, some error message. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, actually, I see. You seem to throw even if the image is\u003C\u002Fp>\u003Cp>Speaker 1: If it's not image, it threw as an error also. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Or maybe I added that. Yeah. Anyway Yeah. So you can do a failed state if you want.\u003C\u002Fp>\u003Cp>Speaker 1: Path also. Okay. Okay. Yeah. I think that's fine.\u003C\u002Fp>\u003Cp>Yeah. Would you like to maybe try with could we do a demo of just uploading a an image or a screenshot? Sure.\u003C\u002Fp>\u003Cp>Speaker 0: Just gonna find the good image to upload. Okay. So here we have my file library. And Mhmm. I'm gonna drop an image here from my other screen.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: And we wait a little bit, and then we check if we get\u003C\u002Fp>\u003Cp>Speaker 1: Nice. The tracking that we get. White cut. Yeah. Sitting on a blanket indicate very descriptive to the point.\u003C\u002Fp>\u003Cp>Yeah. Yeah. That makes a lot of sense. Lovely.\u003C\u002Fp>\u003Cp>Speaker 0: And, of course, the the big advantage of this would be, like, to have alternative texts in on websites if you have, someone with a disability so they can't see. Yes. Yep. Then they can get this re read up instead.\u003C\u002Fp>\u003Cp>Speaker 1: I would just love to know, in terms of improvements to the extension, the things that, you know, features that you would like to add, what are some of the features or improvements going forward that you love to add to the extension?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So one thing I want to add is, like a settings page.\u003C\u002Fp>\u003Cp>Speaker 1: So The settings page?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So so you can ease more easily add the API keys, I'm thinking. I'm also thinking, like, that that will be needed for, for example, the marketplace.\u003C\u002Fp>\u003Cp>Speaker 1: Yes.\u003C\u002Fp>\u003Cp>Speaker 0: Perhaps or at least it will make it easier. But I'm also open to add more AI features, depending on use cases. I had one, for example, with, trying to detect objects in an image. Okay. So that might be something we I'm gonna add later on.\u003C\u002Fp>\u003Cp>And as I mentioned, support for other services like Azure, perhaps Google, if if, anyone needs that.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: So so if anyone has any suggestions, I recommend posting it in on GitHub.\u003C\u002Fp>\u003Cp>Speaker 1: Feel free to. Yeah. Either comment or make an issue or just even comment in our Discord channel as well. Thank you. Alright.\u003C\u002Fp>\u003Cp>Yeah. I'm really excited to see when the marketplace launches. Hopefully, we're able to have this extension in the marketplace because I know lots of people would also find it equally useful and important in their projects as well. Alright. So the very final question I have is, I know you've also built the Jira panel.\u003C\u002Fp>\u003Cp>The last hackathon you worked on Jira panels. So what's will I say advice or you know? Yeah, advice would you have or give to anyone that is building extensions? Because you have bit of more experience in that aspect. So anyone building a custom director's extension.\u003C\u002Fp>\u003Cp>Speaker 0: I think my advice would be to not be afraid of looking into the, direct to source code, because there's a lot of it's built very modular. So a lot of the existing interfaces and operations and all of that are available. So you can see how they are built. So even if the even if you can't find anything in if you're looking for something in the documentation and not finding anything that you might find how, under interface or operation, have sold.\u003C\u002Fp>","But I'm also open to add more AI features, depending on use cases. Hi there, and welcome to another episode of the Beyond the Core Show. It's a director show where we shine the spotlight on extension developers in the community. My name is Esther Agbaji, and I work as a developer advocate at Directus. And today, I'm joined by a super community member, Ahud. Ahud is the community winner of the AI hackathon that held a couple of months back, and he won the hackathon by creating the media AI bundle extension. And today, he's here with me to share all about the exciting journey to develop Hynis Extension and some of its features. So thank you, Arud, for joining me here. Could you share, like, some background about yourself and how you get to know about directors? Sure. I I work as a developer, at an agency. Okay. And I was looking for, alternatives to CMSs like WordPress, for example, but I wanted something more, API focused. Okay. So so we had so we could find something that works with larger projects, and more, like, apps and such. And after trying some of them out, I I wasn't really happy with how they worked. They weren't, like, easy to use for the users, at least in my opinion. But then in a Reddit post, I think it was, someone was mentioning, directors and, like, how it was. They they said it was fantastic, but the the it was kind of, under the radar, at that time. So Okay. I checked it out, and I liked it too much. So, I started trying to, like, get it approved to to be used in our agency. Okay. And now we have successfully rolled out a few projects using it. And so far, it's been great. Interesting. Interesting. And what's your favorite director's feature? I think it's the interface. Okay. How extensible it is, and how how I can fine tune it for the editors. Okay. Okay. So you like the fact that, like, it's very intuitive and is also extensible. Yeah. Yeah. I'm sure you probably like the extension feature because you've created not just 1 or 2 extensions at least that I know of. And, yeah, we are here to talk about the media AI bundle, which is a really cool extension. I saw the demo that you did during submission, and it was really good. So, what was the motivation for actually creating this extension? It started off as, idea for a personal project of mine. I wanted to be able to take pictures, of sticky notes. Interesting. Like photos with my phone and upload them to a Kanban board or similar. So that's what got me started. I was I had some APIs in mind that I wanted to use, but now I had a like, an excuse to get it working in directors. So I started working on the, an operations bundle. And then I realized that this could be used for much more. For example, the the integration with alt text dot io so you can get alt text for images. So that kind of, formed it into becoming the media, bundle. Okay. Okay. Nice to hear. So you mentioned the old text. Is it like an AI API? Yeah. Okay. So it's like a service you sign up for, and they they transform the image into a readable text, like a sentence or Okay. Sentences for that describes the image. Okay. Okay. So, basically, the extension that you created, you upload an image, and it just gives you the old text or even if it's a screenshot of maybe, you know, text on a piece of paper, you can also read and extract that text also. Right? Yeah. And I have 2 two operations currently in the bundle. 1 is for the, one I call describe, image, which does the alt text thing. It takes the image and extracts the text or sentence. You can also use Amazon Web Services, but then you'll only get, like, a comma separated list of words. So it's not as fancy. Okay. But it's an alternate alternative Alternative. Okay. Yeah. And the other one is the extract text operation that actually reads, with, like, OCR, and tries to find text in an image. So you can extract. Okay. Extract the text from an image. Yeah. Alright. Yeah. Would you like to share your screen? So just walk us through maybe some parts of the code and then a quick demo of the extension? So as I mentioned, it's a bundle. So you have a source directory with the different operations in this case. And the plan was to add more, as I get more, like, cases Mhmm. Use cases. Use cases. Yeah. So here we have the describe image, and the extract text from the image operation. So if you take the describe the image, I think that's the more more fun one. Okay. We have the front end part or the app part Mhmm. Which defines the operation. And here we have some settings that you can set up. You can choose if you want to use alt text dot io or Amazon Amazon Cognition. Okay. And you you will need API keys for these and Mhmm. You set them up in your environment variables. Okay. But I'm hoping to add some kind of, like, settings page or something, where you can do it a bit easier. Mhmm. And then on the, client side or or the server side Service side. Yeah. We have the API for this, or the director's API. So we get an image. You you can set up, like, a hook for or a flow with a hook for file upload. Mhmm. And then we'll check if we have, like, a image, if the file is an image, and then we will create a buffer from it so we can send the entire image from Mhmm. To this to the API. Okay. So you first verify if it's an image before you then send it. Yeah. So Okay. We don't waste any, like, credits or such. Okay. And then we get a result. And, I try to, like, return a common format for it. So it shouldn't depend matter which API you use. You should have, like, a you should be able to expect what kind of Okay. Properties it will return, and they are documented in the readme. In the readme. Okay. So in this case, you get the description. But I also have, like, a dollar param row property that has the original payload. So if you want to get something specific from Amazon or from alt text, you can You can get that as well. Okay. Nice. Without having to exchange the the extension. And the same thing here for Amazon. The API used there is called detect labels. Okay. Yeah. Okay. Nice. That's how it works. That's That's the one for describing the image. Yeah? Yeah. Okay. Okay. Let's check out the one for, like, extracting the text from image briefly. Yeah. It works pretty much the same, except it doesn't have it only has Amazon recognition for now. Okay. I am we I am planning to add ash Azure Vision AI. Azure. Okay. Because we use that a lot at my work. Okay. But for now, it's just Amazon. Alright. But the principle is the same. We we you you we receive an image. We take the stream, and send it to, an API. Mhmm. And the only difference here is, of course, which, API we're sending it to. So in this case, it's the detect text command Command. Okay. On the Amazon SDK. And in this case, you get a bit more parameters back, property spec. So you can get either the I try to transform them into lines, with text and, like, where in the image it's located. But you also get a full text if you're just trying to, like, transcribe an image. Yeah. Okay. So if you want that quick and easy fix, you can use the full text form. Full text. Okay. Okay. But if you want to, like, get down with the greater details, you can use the lines. The lines. Yeah. That's very clear and nifty. Did you face any challenges or issues when you were building, you know, all of these bundles? I think the for for the most part, it's it's been pretty No. It's pretty straightforward. Okay. Yeah. Of course, the the I guess the one thing that was a bit tricky is, like, trying to find the right services. I know that that has been added to the documentation now. Yes. It has. When I wrote this, there wasn't really much. Wasn't. Yeah. True. I remember. Yeah. True. But now we've included services in the docs. Yeah. So getting the, like, asset service and figuring out how that works, took a bit of time. It wasn't wasn't hard, but you had to, like, shake your house. Again, to the core and the doors. Yeah. But that's also a strength of directors, I think, yeah, that you can do that. Yeah. Can you imagine that? Okay. That's cool. Let's go into the bundle to see how it works in directors. Yes. So I have a flow here. Let me see in a bit. Yeah. Yeah. It's good now. Called file uploads. Okay. And here we have when I created this, I made a trigger with non blocking action with the scope files. Files that's uploaded. Mhmm. Currently, there's no filter for it. I think I'm gonna write a feature request for that. I think it would be useful to, like, be wait for the Before I wait for some time before I finish before fires up. Yeah. Because right now, you you when you upload it, you you get sent to the, like, start page, and you don't see the changes until they have performed. So you have to wait a bit. I see. Yeah. But for now, it works well with the non blocking one. Then we have the operation from my extension, describe image. And here we are select which API we want to use and you are able to change the field if you want to. But in most cases, it's the trigger dot key, which is the image you have uploaded. But I'm left it a bit configurable if you have more advanced flows. Yeah. Then when this is run, you get some data. So I have an update data operation here. Mhmm. Where I simply just update the, payload of the file that's created in Directus. So in this case, I put the description from the operation into the description field. Mhmm. Yeah. In some cases, I might put a transcript or similar in between just to clean up a bit, especially if I use the other, operation that can the the extract text one. So if you if you would need to, like, make sure that it's not too long or something like that. Okay. Do that. I just remembered, did you, maybe handle errors and all in the whole, you know, operations? Some errors, but in most cases, it will silently fail. Like, if you upload a image that isn't or a file that isn't an image. It will just silently It's a test. Failed. So you don't Okay. Yeah. So you don't get, like, a lot of error messages. But, I'm not sure if I I think I have some, yeah, some error message. Okay. Oh, actually, I see. You seem to throw even if the image is If it's not image, it threw as an error also. Yeah. Or maybe I added that. Yeah. Anyway Yeah. So you can do a failed state if you want. Path also. Okay. Okay. Yeah. I think that's fine. Yeah. Would you like to maybe try with could we do a demo of just uploading a an image or a screenshot? Sure. Just gonna find the good image to upload. Okay. So here we have my file library. And Mhmm. I'm gonna drop an image here from my other screen. Okay. And we wait a little bit, and then we check if we get Nice. The tracking that we get. White cut. Yeah. Sitting on a blanket indicate very descriptive to the point. Yeah. Yeah. That makes a lot of sense. Lovely. And, of course, the the big advantage of this would be, like, to have alternative texts in on websites if you have, someone with a disability so they can't see. Yes. Yep. Then they can get this re read up instead. I would just love to know, in terms of improvements to the extension, the things that, you know, features that you would like to add, what are some of the features or improvements going forward that you love to add to the extension? Yeah. So one thing I want to add is, like a settings page. So The settings page? Yeah. So so you can ease more easily add the API keys, I'm thinking. I'm also thinking, like, that that will be needed for, for example, the marketplace. Yes. Perhaps or at least it will make it easier. But I'm also open to add more AI features, depending on use cases. I had one, for example, with, trying to detect objects in an image. Okay. So that might be something we I'm gonna add later on. And as I mentioned, support for other services like Azure, perhaps Google, if if, anyone needs that. Okay. So so if anyone has any suggestions, I recommend posting it in on GitHub. Feel free to. Yeah. Either comment or make an issue or just even comment in our Discord channel as well. Thank you. Alright. Yeah. I'm really excited to see when the marketplace launches. Hopefully, we're able to have this extension in the marketplace because I know lots of people would also find it equally useful and important in their projects as well. Alright. So the very final question I have is, I know you've also built the Jira panel. The last hackathon you worked on Jira panels. So what's will I say advice or you know? Yeah, advice would you have or give to anyone that is building extensions? Because you have bit of more experience in that aspect. So anyone building a custom director's extension. I think my advice would be to not be afraid of looking into the, direct to source code, because there's a lot of it's built very modular. So a lot of the existing interfaces and operations and all of that are available. So you can see how they are built. So even if the even if you can't find anything in if you're looking for something in the documentation and not finding anything that you might find how, under interface or operation, have sold.",[4583,4584],"491e73a6-c4e8-497a-a18f-597c3aadbda6","ab35f5f0-c867-45fc-ba93-1db703826e8d",[],{"year":978,"number":3992,"id":3993,"episodes":4587,"show":4588},[3995,3996,3964],{"title":3998,"slug":3999},{"id":4590,"slug":4591,"vimeo_id":4592,"description":4593,"tile":4594,"length":740,"resources":12,"people":4595,"episode_number":11,"published":4597,"title":4598,"video_transcript_html":4599,"video_transcript_text":4600,"content":12,"seo":12,"status":19,"episode_people":4601,"recommendations":4603,"season":4604},"bfb8bc25-ef1b-4544-b50d-402008c638a1","github","899796327","Build a Flow to create GitHub issues from issues within Directus collections.","41d69174-d3db-4fd9-b579-f9066dda70df",[4596],{"name":2820,"url":2821},"2024-02-16","Create Issues from Directus with GitHub","\u003Cp>Speaker 0: At the bottom of almost every page in our documentation, we have this little custom built widget, which we use to gather feedback on what's good and what isn't so good and needs improvement. And all of this data ends up in a director's project, which we periodically look through. And then anything that needs actioning, we create issues for in the director's repository. Now, in today's episode of quick connect, we're going to build a little connection to GitHub that automates the act of adding of taking feedback in direct us and turning it into issues. So I'm really excited to show this to you.\u003C\u002Fp>\u003Cp>I've created this little sample repository. So there's my username, phase on overload slash sample repo. I'm pointing that out because it's included a little later on. In this little sample directors project I have, I've set up the following data model. The idea is that content is the user's actual feedback that they type, and then we will add a button to the side of the item.\u003C\u002Fp>\u003Cp>And then when we click on it, we will add an issue to that repository automatically after gathering a little actionable summary manually. And then we will populate it here. We'll go off to GitHub, make the issue, and return the issue number here so we can click right through to it. And the only thing we need for today other than, of course, the director's project is a personal access token from GitHub. There are loads of ways you can authenticate.\u003C\u002Fp>\u003Cp>We're gonna use these fine grained personal access tokens. You provide the access to the repository, and the only permission we require is read and write on issues because that's all this token is going to do. I've done this ahead of time. So with that, I think we're ready to get started. So over in Directus, let's create our flow.\u003C\u002Fp>\u003Cp>We're going to call this one add feedback item to GitHub. And in the trigger setup, we're gonna make this a manual trigger on the feedback collection, only on the item pages. Now there's a difference between a piece of feedback and the remedial action, and issues describe the remedial action that needs to take place. So we're gonna use a confirmation dialogue here. We're going to add 1 input.\u003C\u002Fp>\u003Cp>I'm going to call this input summary or maybe title. Yeah. Title. That will just be a string input and save. So now if we go to an item, we see here over on the side, there is this flow, add feedback item to GitHub.\u003C\u002Fp>\u003Cp>You click it, and it pops up this box where we can gather a title. For example, if we go find this very valid piece of feedback, which we got recently, I can add item feedback to GitHub. The issue is there are no docs on connecting an existing database. So the remedial action would be add docs on connecting existing databases. Right?\u003C\u002Fp>\u003Cp>Run flow on current item. Let's go to our flow and refresh it, and we'll see the one invocation of this flow. We see here that inside of the body, we have the title, which is what we entered into that box just now. And we have the key, the unique identifier for that piece of feedback in the director's collection. Now what isn't visible here is the actual feedback itself, which is important.\u003C\u002Fp>\u003Cp>So what we need to now do is go from the just having this key to having the entire feedback object. We'll do that with a read data, read data, operation. I think we'll call this one feedback. We'll give it full permissions from the feedback collection, and we want to access trigger dot body dot keys 0. So that's that ID.\u003C\u002Fp>\u003Cp>If we hit save now, this feedback operation will return the full object, including the including the content itself. Let's just quickly test that. Click this button. We'll paste in the title. We'll refresh the flow.\u003C\u002Fp>\u003Cp>So we get the new log. Let me see in the second payload, we get the content here. That'll be important to add into the context of the issue. Now we're actually gonna go off to GitHub, and we're gonna add this issue to our repository. So we'll call this 1 GitHub, and we'll make a web request.\u003C\u002Fp>\u003Cp>This should be a post request to the following API endpoint. \u002Frepos\u002Fusername slash repository slash issues. And what we need to do is add a header to identify ourselves. This is where that personal access token is gonna come in. Authorization bearer space personal access token.\u003C\u002Fp>\u003Cp>Save. And now in the request body, we can add some additional, information for the issue. Now there's quite a lot we can add here. I think we're gonna keep it quite minimal for the sake of this episode of quick connect. We will add a title.\u003C\u002Fp>\u003Cp>And what we'll do for the title is we will use that prompt that we entered. So trigger dot body dot title. And let's actually add the users, let's add the users feedback as written in the body in the actual, like, comments that goes with the PR. Let's make it a quote so it's clear. This is marked down now, and we want feedback dot content.\u003C\u002Fp>\u003Cp>Let's just make this really clear. We'll add a couple of new lines, and we'll say this feedback was provided, via the docs feedback widget. Just so it's clear that this is a quote and where that quote has come from. Let's, let's remove that with underscore in the key and hit save. Let's save that.\u003C\u002Fp>\u003Cp>Let's add this feedback again, and let's run flow on current item. Let's go back to our issues for this repository and hit refresh, and we see that the new item has been successfully created. And in there, there's the quote, and then there is the, the additional text. We can do lots when we create an issue, including adding labels, adding to projects, but you can refer to the API documentation, about these extra bits and what's suitable for your use case. Now this, this issue is issue number 7.\u003C\u002Fp>\u003Cp>Let's refresh and look at the logs, of that issue. Oh, that's the first time. This is the latest time. And we see this big payload has come back inside of data here. We have the title, which was our little summary that we typed in, and we also have the issue number 7.\u003C\u002Fp>\u003Cp>Just before continuing, I just wanna note that inside of these little button links here, I've added a view on GitHub button, which is specifically linked to that repository. So that is the repository name, and it'll append the issue number. So what we wanna store an issue is just the number. Let's add one final operation here. Let's make this an update data, operation to feedback, full access.\u003C\u002Fp>\u003Cp>Once again, we're gonna update the trigger dot, trigger dot body dot keys 0. That's it. And the payload that we're gonna add here, what we're actually gonna update with is the summary, which is our remedial action. You could choose to call this action, but, you know, it's called summary here. So that's gonna be the trigger dot body dot title what we entered in that pop up box and the issue number itself.\u003C\u002Fp>\u003Cp>GitHub. And the reason we can call it GitHub is that was the name of the operation, which returned the data dot data dot number. So let's hit save. And now we should be able to hang out just in here. Let's, pick another piece of feedback here.\u003C\u002Fp>\u003Cp>We will add this feedback to GitHub, clarify extension location, run flow on current item. Oh, we got a little undefined there, so something's wrong. The issue is indeed 8, which is great. There was one issue there. The summary is undefined.\u003C\u002Fp>\u003Cp>So let's just dig into that for a moment. The value we want is in payload body title. Let's update this. Trigger 2 g's dot body dot title. That was just a little typo there.\u003C\u002Fp>\u003Cp>So let's do this once more. This one's kind of already done. Let's do this one again. Here we are. Oh, not there.\u003C\u002Fp>\u003Cp>We'll run the flow. Boom. There we go. So that's the summary we typed in. That's the issue number.\u003C\u002Fp>\u003Cp>We click the button. The issue is open, and now there's this lovely link between where feedback is provided and tracking the remedial action. The other nice thing about this, of course, is inside of our items, we can do a filter. So we can say, hey. I only want to show where, you know, the issue is empty.\u003C\u002Fp>\u003Cp>IE, these are the items that still require some form of of response if appropriate. So, yeah, this was a huge amount of fun. It was just a little quick project, which I think is gonna actually enrich my day to day life. So I'm quite chuffed about that. Thank you so much for joining me in QuickConnect.\u003C\u002Fp>\u003Cp>It's been a pleasure. Have a wonderful day. Bye for now.\u003C\u002Fp>","At the bottom of almost every page in our documentation, we have this little custom built widget, which we use to gather feedback on what's good and what isn't so good and needs improvement. And all of this data ends up in a director's project, which we periodically look through. And then anything that needs actioning, we create issues for in the director's repository. Now, in today's episode of quick connect, we're going to build a little connection to GitHub that automates the act of adding of taking feedback in direct us and turning it into issues. So I'm really excited to show this to you. I've created this little sample repository. So there's my username, phase on overload slash sample repo. I'm pointing that out because it's included a little later on. In this little sample directors project I have, I've set up the following data model. The idea is that content is the user's actual feedback that they type, and then we will add a button to the side of the item. And then when we click on it, we will add an issue to that repository automatically after gathering a little actionable summary manually. And then we will populate it here. We'll go off to GitHub, make the issue, and return the issue number here so we can click right through to it. And the only thing we need for today other than, of course, the director's project is a personal access token from GitHub. There are loads of ways you can authenticate. We're gonna use these fine grained personal access tokens. You provide the access to the repository, and the only permission we require is read and write on issues because that's all this token is going to do. I've done this ahead of time. So with that, I think we're ready to get started. So over in Directus, let's create our flow. We're going to call this one add feedback item to GitHub. And in the trigger setup, we're gonna make this a manual trigger on the feedback collection, only on the item pages. Now there's a difference between a piece of feedback and the remedial action, and issues describe the remedial action that needs to take place. So we're gonna use a confirmation dialogue here. We're going to add 1 input. I'm going to call this input summary or maybe title. Yeah. Title. That will just be a string input and save. So now if we go to an item, we see here over on the side, there is this flow, add feedback item to GitHub. You click it, and it pops up this box where we can gather a title. For example, if we go find this very valid piece of feedback, which we got recently, I can add item feedback to GitHub. The issue is there are no docs on connecting an existing database. So the remedial action would be add docs on connecting existing databases. Right? Run flow on current item. Let's go to our flow and refresh it, and we'll see the one invocation of this flow. We see here that inside of the body, we have the title, which is what we entered into that box just now. And we have the key, the unique identifier for that piece of feedback in the director's collection. Now what isn't visible here is the actual feedback itself, which is important. So what we need to now do is go from the just having this key to having the entire feedback object. We'll do that with a read data, read data, operation. I think we'll call this one feedback. We'll give it full permissions from the feedback collection, and we want to access trigger dot body dot keys 0. So that's that ID. If we hit save now, this feedback operation will return the full object, including the including the content itself. Let's just quickly test that. Click this button. We'll paste in the title. We'll refresh the flow. So we get the new log. Let me see in the second payload, we get the content here. That'll be important to add into the context of the issue. Now we're actually gonna go off to GitHub, and we're gonna add this issue to our repository. So we'll call this 1 GitHub, and we'll make a web request. This should be a post request to the following API endpoint. \u002Frepos\u002Fusername slash repository slash issues. And what we need to do is add a header to identify ourselves. This is where that personal access token is gonna come in. Authorization bearer space personal access token. Save. And now in the request body, we can add some additional, information for the issue. Now there's quite a lot we can add here. I think we're gonna keep it quite minimal for the sake of this episode of quick connect. We will add a title. And what we'll do for the title is we will use that prompt that we entered. So trigger dot body dot title. And let's actually add the users, let's add the users feedback as written in the body in the actual, like, comments that goes with the PR. Let's make it a quote so it's clear. This is marked down now, and we want feedback dot content. Let's just make this really clear. We'll add a couple of new lines, and we'll say this feedback was provided, via the docs feedback widget. Just so it's clear that this is a quote and where that quote has come from. Let's, let's remove that with underscore in the key and hit save. Let's save that. Let's add this feedback again, and let's run flow on current item. Let's go back to our issues for this repository and hit refresh, and we see that the new item has been successfully created. And in there, there's the quote, and then there is the, the additional text. We can do lots when we create an issue, including adding labels, adding to projects, but you can refer to the API documentation, about these extra bits and what's suitable for your use case. Now this, this issue is issue number 7. Let's refresh and look at the logs, of that issue. Oh, that's the first time. This is the latest time. And we see this big payload has come back inside of data here. We have the title, which was our little summary that we typed in, and we also have the issue number 7. Just before continuing, I just wanna note that inside of these little button links here, I've added a view on GitHub button, which is specifically linked to that repository. So that is the repository name, and it'll append the issue number. So what we wanna store an issue is just the number. Let's add one final operation here. Let's make this an update data, operation to feedback, full access. Once again, we're gonna update the trigger dot, trigger dot body dot keys 0. That's it. And the payload that we're gonna add here, what we're actually gonna update with is the summary, which is our remedial action. You could choose to call this action, but, you know, it's called summary here. So that's gonna be the trigger dot body dot title what we entered in that pop up box and the issue number itself. GitHub. And the reason we can call it GitHub is that was the name of the operation, which returned the data dot data dot number. So let's hit save. And now we should be able to hang out just in here. Let's, pick another piece of feedback here. We will add this feedback to GitHub, clarify extension location, run flow on current item. Oh, we got a little undefined there, so something's wrong. The issue is indeed 8, which is great. There was one issue there. The summary is undefined. So let's just dig into that for a moment. The value we want is in payload body title. Let's update this. Trigger 2 g's dot body dot title. That was just a little typo there. So let's do this once more. This one's kind of already done. Let's do this one again. Here we are. Oh, not there. We'll run the flow. Boom. There we go. So that's the summary we typed in. That's the issue number. We click the button. The issue is open, and now there's this lovely link between where feedback is provided and tracking the remedial action. The other nice thing about this, of course, is inside of our items, we can do a filter. So we can say, hey. I only want to show where, you know, the issue is empty. IE, these are the items that still require some form of of response if appropriate. So, yeah, this was a huge amount of fun. It was just a little quick project, which I think is gonna actually enrich my day to day life. So I'm quite chuffed about that. Thank you so much for joining me in QuickConnect. It's been a pleasure. Have a wonderful day. Bye for now.",[4602],"cc2b4d61-d685-41c2-a1a7-a397140172aa",[],{"year":2380,"number":194,"id":4605,"episodes":4606,"show":4614},"3b8b7d34-a0fb-4ea6-85ff-2b5bfbb8e0b6",[4607,4608,4609,4610,4611,4612,4613,4590],"502dcf7e-c23e-4dfd-b147-65f5abaea5c7","a230c9ef-8db4-4c00-a0cb-9524f7934eb0","5f41dc16-29b7-485f-a6e1-081c3f1acc4f","81417d25-26d2-4f05-be37-7ced51a0594e","8f933ee9-4e4f-4e35-8c1f-e99ad0684bfa","71e081db-92f8-4978-b020-7d2460a46187","8e47020d-bd5a-43a7-bca9-54af4f5d465d",{"title":1746,"slug":1747},{"id":1791,"slug":4616,"vimeo_id":4617,"description":4618,"tile":4619,"length":2550,"resources":4620,"people":4623,"episode_number":264,"published":4627,"title":4628,"video_transcript_html":4629,"video_transcript_text":4630,"content":12,"seo":12,"status":19,"episode_people":4631,"recommendations":4635,"season":4636},"flows-log","912263898","In this recording of our live event on February 8 2024, Rijk, Jonathan, and Daniel discuss improvements to our no-code automation builder - Flows.","f89ae85c-4df4-4b9e-86db-8ca73f5980ba",[4621],{"name":4496,"url":4622},"https:\u002F\u002Fgithub.com\u002Fdirectus\u002Fdirectus\u002Fdiscussions\u002F15870",[4624,4625,4626],{"name":3795,"url":3796},{"name":3798,"url":3799},{"name":3801,"url":3802},"2024-02-15","Improvements to Flows Debugging","\u003Cp>Speaker 0: Alright. Yeah. So, Jonathan, you already, briefly introduced that seeing all the hiccups, I don't know if we're gonna cut that. But just to restart it, we're gonna be talking about flows today. We're really gonna be starting with this 15 8 70 about improving the activity panel.\u003C\u002Fp>\u003Cp>But, realistically, as far as I'm concerned, it's sort of a smaller piece of a bigger discussion around upgrades to flows. Right? So for those out of the loop, we introduced that flow system as a way to do event based, actions and operations, kinda like you could with hooks, or custom endpoints, but doing it in a no code type of way. Right? So you configure when you want your flow to trigger and then step by step configure individual operations, individual nodes of sort of this this path, that you wanna execute.\u003C\u002Fp>\u003Cp>The original version of that was really much designed as a, start with the basics, see what people wanna use it for, see where what what the best improvements are gonna be. So we shipped it a little bit lightweight, you know, with a handful of operations and have been more and more over time. But it has been becoming increasingly obvious that there's, a lot of improvements that would be really nice to have, especially when it comes to debugging, sort of helping configuring some of these pieces. So with that being said\u003C\u002Fp>\u003Cp>Speaker 1: Yes.\u003C\u002Fp>\u003Cp>Speaker 0: Discussion. So for Daniel, who's flying blind here, we're looking at the one about the logs first. Right? So if you're currently running a flow, optionally if you have it enabled, it's enabled by default. It will keep track of the data that sort of went through the various operations, and then saves it to, a logs tab that you can see on the right hand side of the flow.\u003C\u002Fp>\u003Cp>This is basically the primary way at the moment where you can debug what is happening under the hood, because otherwise, you know, there it's it's the way where you can see what data came actually through the trigger and how you've modified the data points in between. So over the Jonathan, you're you're showing it out what that looks like now on your own instance. What are some of the points in this initial discussion that triggered this this conversation? You've got\u003C\u002Fp>\u003Cp>Speaker 2: a lot of the ease and access to that information, as well as the ability to control, like, durations. Some of the problems we run into with flows, especially if someone once they get them into production, if they still have the activity and logging turned on, you can fill up your activity revisions tables really quickly. And there's you know, the mechanisms for cleaning that up are, you know, another flow, or direct database access or other kinds of things where you've gotta manage your activity and revision logs and so forth. Common recommendation that I make is once you've done your testing and you've got your flows functioning is to disable the activity and logging. But there are cases where you may want that and logging just for audit trackability on actions and things that people are doing with flows.\u003C\u002Fp>\u003Cp>So, I think some of that is just the general trigger and management of that. Other things that, tend to cause struggle for people in flows is the way that you access this instead of being able to say, look at the data from the log directly in something that I'm working on. There's been a again, there's a number of ways that we can kind of shake and look at this. But the general thing is this kinda lives over here. I can't edit this while I'm looking at this, so the the interactions with logs and the work that you're doing is kind of a it's a bit of a hindrance when you're doing the development phase of flow development.\u003C\u002Fp>\u003Cp>So it's just a more a nuisance than anything else. But the the inability to say edit something here, I have to actually come here to check. So if I'm looking for variables or data or what are the things looking like in here, what's my payload? Now I've gotta remember this data structure or copy this data somewhere else because I wanna actually do something with it. I wanna interact with it.\u003C\u002Fp>\u003Cp>I wanna use those variables. So it's kind of a it's a weird transition kind of back and forth. Some of the systems that we we got in some inbound activity on some other tickets and feedback that we got from some clients on the enterprise side, was you know, they showed showed us some other systems that have similar no code kinds of flow capabilities. And logs, instead of living in, like, a side panel in a weird way, kind of live with the operations themselves. So there's just some general ideas and thoughts around how that interaction in the UX kind of works.\u003C\u002Fp>\u003Cp>Data wise, I don't think anybody's complaining about the information that shows up in here. That tends to be you know, it's fairly easy to work with and deal with. I think it's more the kind of user interactions with data.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. For me, personally, I've ran into this when I tried to set up a flow which triggers another flow, which then leads you to, you know, switching between those 2. And then you have to check that again, but then you forgot on the first flow, forgot something, and you have to switch back. And, you know, so people do run into this, and, I've experienced this myself. So, generally, if I if I experience this, the general user will probably also experience this.\u003C\u002Fp>\u003Cp>So this is a very valid point, in my opinion, and, we should we should improve this a little bit. You're muted, Jonathan. Sorry. You're muted.\u003C\u002Fp>\u003Cp>Speaker 0: You look very passionate, though. I'll give you that. But\u003C\u002Fp>\u003Cp>Speaker 2: Sorry. I'm not gonna mute. So so what that does is, you know and that's why we saw this very same thing as we started doing some internal research on just this ticket. We rapidly diverged into we've got 4 or 5 other kinds of spec, and that's why we it's kinda leading into this call today. We were talking about the fact that there's some general overall flows improvements that I think we'd like to see across the boards.\u003C\u002Fp>\u003Cp>But the logging and activity, you know, that's that kinda triggered this action was really about that the way that those logs interact and how you have to it's kind of a separation of what is a common function.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Definitely. Totally agree. And, like the other comment on the chat said, switching is painful. Yes.\u003C\u002Fp>\u003Cp>An auto refresh for logs would go a very long way because I I do think that these two things are very, service the same niche, service the same pain. Because you you want to have up to date logs and see what is different, what do I have to work with, what can I work with, and not being able to have that on auto refresh, for example? Is the same thing as context switching back and forth, and you want to refresh and whatever. So I think these two pieces are very similar and and and try to work on the same pain, basically. So, good point from the chat.\u003C\u002Fp>\u003Cp>Thank you very much.\u003C\u002Fp>\u003Cp>Speaker 0: So there's a lot of lot of, I saw the word divergent on the screen. Just made me think there's a lot of divergent ideas happening at the same time. Right? We're talking about a lot of smaller optimizations like the auto refresh or some sort of way to make that context switching a little bit less painful. If we take a deeper look at this discussion, though, that sort of triggered everything, are there any particular pain points about the way we describe or show those logs that is currently sort of, something that should be improved?\u003C\u002Fp>\u003Cp>Speaker 2: I'm kinda skimming through this guy.\u003C\u002Fp>\u003Cp>Speaker 1: See some people typing in the chat. Please let us know if you have experience in using flows and would like to, contribute. We're here for you.\u003C\u002Fp>\u003Cp>Speaker 2: I think on the Trigger log\u003C\u002Fp>\u003Cp>Speaker 0: is the hardest to understand.\u003C\u002Fp>\u003Cp>Speaker 2: I'm still on. So on that log side, the other key thing that's being pointed out here is there are differences in the inbound payloads based on whether you're doing a create, update, or delete operation. You do have some variation in the trigger payload bodies. And, again, I don't know that it's a bad thing, but it is one of the things that's called out as a there's differences in the pathing, say, to the collection or the item, based on what type of event triggered the action. Yep.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Very good one. That that makes a lot of sense, and it's it's it's explainable from a technical perspective, but also makes sense where it's where where the pain point's coming from. So earlier on, when we just did the hooks initially, the decision was made to differentiate between create a single thing and create multiple things, which in turn means that sometimes you get a single ID as a sort of, string or a number, and sometimes you get an array of ID strings or a number, which is basically just a bigger discussion around what does that hook payload look like. Right?\u003C\u002Fp>\u003Cp>Because everything is based on the same internal, event system. So, you know, a flow is triggered based on the same hook that you get from, a hook extension, for example. It's all the same thing. Then, the difference between, you know, trigger dot keys, trigger dot body dot keys, etcetera, sometimes it's payload, sometimes it's not, That really depends on the type of trigger, which, again, you know, doesn't necessarily make it make it better, but it is, you know, explainable where it's coming from, where. If you're having an endpoint, you know, with a a trigger request, now you're dealing with a sort of user payload that was submitted that could be anything.\u003C\u002Fp>\u003Cp>Right? If you're dealing with a hook, it's a pre pre known format for what our hooks fire. But, you know, it's there's a difference. There's gonna be a difference, and that is that's definitely tricky. Somebody said, if hooks supported loops, we probably wouldn't need the differentiation for 1 versus multiple.\u003C\u002Fp>\u003Cp>Hook supported loops. I'm not entirely sure what you mean by that because a hook is just a bit of JavaScript, so you could loop over whatever you want. Right? Oh, flows. Oh.\u003C\u002Fp>\u003Cp>Oh, I see. Gotcha. Gotcha. Gotcha. Gotcha.\u003C\u002Fp>\u003Cp>Yeah. So we're basically saying, you know, if you have a way to just say do this flow against every item in the triggered whatever trigger keys, then we can basically just drop the one hook and make everything an array all the time, sort of get rid of some of that confusion, which for the record Yeah. Makes a ton of sense to me. I mean, every every insert into the database could be 1 or multiple things. And if you do one thing, it's just an area of one thing.\u003C\u002Fp>\u003Cp>Right? It's basically it's it's easy to explain. So I I do agree with that sort of general sentiment. Even though for the record, that would be a quite a big breaking change and totally wreck everybody's existing flows and extensions. So TBD TBD.\u003C\u002Fp>\u003Cp>As mentioned, the extension shed will be very possible to create a repeat operation for all elements in an array extension with exposing a single function from the flows manager. Yeah. Yeah. No. Absolutely.\u003C\u002Fp>\u003Cp>Yeah. That's very true. Very true. Cool. There is one more, thing in the chat here from our very own Brian saying remembering the key names for operations is a pretty big headache for me.\u003C\u002Fp>\u003Cp>Why are the keys only showing on hover? Where can I not copy them? Which has been a thing all over the app. And just trying to remember what that key name is. That's a very good point.\u003C\u002Fp>\u003Cp>And I think, you know, overall in the app, we've sort of on the side of making things sort of, what's the right word? User friendly is such an empty thing, but, like, look pretty for nontechnical users. Right? So a lot of stuff gets, what we call title formatted. Family friendly.\u003C\u002Fp>\u003Cp>Yeah. Exactly. We we we title format a lot of that stuff, so it looks prettier in the UI. But totally. Yeah.\u003C\u002Fp>\u003Cp>For things like flow logs, you know, which is inherently a very technical thing, flows is only available for admin users, which are generally speaking, a little bit more of the on the technical side. Those should most likely just be the technical keys. Right? Render them in monospace and just lean into it. Because why why show them as a title formatted version and then have it only show on hover that you can then not copy paste because it's a tooltip.\u003C\u002Fp>\u003Cp>Right? It that that makes a ton of sense, and that is just a perfect sort of tiny little tiny little tweak that's a huge quality of life improvement. Jonathan, I hope that was in the notes somewhere now.\u003C\u002Fp>\u003Cp>Speaker 2: Yep. I'm taking some notes in in a separate section, over in the the actual internal notion doc that we've got running on this guy. So I'm trying to capture pretty much it.\u003C\u002Fp>\u003Cp>Speaker 0: Another good point here too is, like, why even have a pretty name if you just have the key? You know? Good point. Good point. I think having some sort of description is a nice, you know, nice to have where at least you can write, like, a mini description, but you wouldn't really use the name for that anyways necessarily.\u003C\u002Fp>\u003Cp>Cool. Okay. Just looking at the discussion, though, because I see we're sort of, what, 1 eighths of the way on the scroll on the page. So I'm kinda curious to see if there's any other points in this particular discussion that we haven't really touched on. That's easy to forget.\u003C\u002Fp>\u003Cp>So these ones we just looked at.\u003C\u002Fp>\u003Cp>Speaker 1: I think, the suggestion by our own\u003C\u002Fp>\u003Cp>Speaker 2: Oh, the the JSON object wrapping. Object wrapping was the other big thing. Again, having this in the side panel, data here, you can start to see if you get long things. It it actually goes off screen and ends up, you know, and you I don't even think it enables a scroll. I'm not sure you can even get to long data that's being displayed in the log there.\u003C\u002Fp>\u003Cp>So some form of wrap, at least, if we're gonna continue in this panel. I think, ultimately, I think we'd like to maybe move this out of the panel anyway. It'll end up somewhere else in the UX. But something for us to keep in mind is that JSON wrapping, and we have that captured as well in a I\u003C\u002Fp>\u003Cp>Speaker 0: was just gonna say that it's there it it's I can already also see there's no way to search or filter through logs. Right? That is another, you know, in. The only thing we show about the log is just a time stamp and then a rel as a time stamp. But I'd I'd say an obvious next step is to sort of move that out of the sidebar into just a proper, you know, layout like we do with other things all over the place.\u003C\u002Fp>\u003Cp>So you get the searching, you get the filtering, and we can have a proper detailed view, or draw our although a detail page is probably gonna be nicer for that. Save a little bit more space, for things like that overflowing scroll and just presenting it nicer. Although, then, you know, we create a new problem, which is now the logs live outside of the flow where you configure them. So at that point, you know, we also wanna make sure that we have some sort of way where you can render that maybe as a split view, maybe you can sort of 50 50 between the flow that you're creating and then seeing the logs of that flow with this layout. Because I do believe that keeping them in context or at least having some sort of link back and forth, from, you know, where you're configuring the the law, the flow and configuring the log is gonna be important.\u003C\u002Fp>\u003Cp>Speaker 1: From\u003C\u002Fp>\u003Cp>Speaker 0: the chat, having logs and operations might be helpful for that. Yes. Yes. Yes and no. It's it's I'm I'm, to me, the logs on operations is an addition, not a replacement.\u003C\u002Fp>\u003Cp>I think personally it's very valuable that you can see, you know, the full, execution path in a log. You can see, you know, at 2 pm today we started with this, we started with it, that one failed, and then we did this, and then here's the data that went through it and what we concluded with. I do think it's important to have that sort of consolidated together, but the the logs and operations, I do agree, would have really be a really nice addition to that where you can just look like, okay. In the last couple of runs, here's the day that it came in and went out of this particular box in your flow. Right?\u003C\u002Fp>\u003Cp>I don't know if that's gonna be a deep link or maybe just an info.\u003C\u002Fp>\u003Cp>Speaker 2: Almost feels like you could have, like, a split panel down here that had the normal, like, search layout capabilities of searching through the logs, could be an interesting use case for this where you'd Mhmm. Similar to Visual Studio and other kinds of console logs where when you're doing this kind of development, you've got logging information you can see down here. Because I also think some of the other nice things with flows would be able to have, like, step through. Right? Being able to step through and see data at certain points, what's going on as you're debugging and working on the flow itself.\u003C\u002Fp>\u003Cp>But then as you say, having an actual, you know, log panel or maybe there's a, like, in a sub ten under flows where you can go to the logs and just see logs full screen, be able to search. Because once my flow is operating, I think the the DERF, pointed out that they're they like logs turned on so when users report issues hours later, they can go back and search the logs. And in that case, I'm not looking at the flow. I'm looking at what's the data, what's the flow, what happened, what errors, what things do I see. So\u003C\u002Fp>\u003Cp>Speaker 1: cool.\u003C\u002Fp>\u003Cp>Speaker 0: Because the nice thing by putting the again, this for for those new to the stream, this is very much a session of divergently thinking. Right? We're just gonna go brainstorm a bunch, like, what will be the ideal state and then take it back towards the end to, like, okay. What is an actual realistic next step? So I can also imagine, you know, closing your eyes thinking about it, if we have a proper detail page, like a full full screen view for the logs for one particular run.\u003C\u002Fp>\u003Cp>Right? We could actually render sort of a smaller, graphical representation of your flow as you have configured it, in in here, and then just show you. You can sort of replay it, so to speak. So we can just show you step 1, and then that's the data that came in and out based on the logs that you're looking at. Right?\u003C\u002Fp>\u003Cp>So instead of just having a long list top to bottom, you could effectively just get a visual replay, of that particular flow operation. I think that could be an interesting one too. Because then you really get to visualize the the execution of that flow because you're effectively trying to follow a path through your flow with the log. But right now, the log is always gonna be a a sort of,\u003C\u002Fp>\u003Cp>Speaker 2: what's the\u003C\u002Fp>\u003Cp>Speaker 0: right word, a one dimensional list, top to bottom. Right? Chronological. But, you're thinking about your flow, potentially, in more of a graphical tree. Right?\u003C\u002Fp>\u003Cp>Where it's like you have option b, and then you branch off, etcetera, which is not really, represented in the logs in any sort of way right now. From the chat, somebody you could almost use the exact same UI as the flow editing page for that. Yeah. No. Yeah.\u003C\u002Fp>\u003Cp>Yeah. Exactly. Maybe a little bit smaller so you have the whole thing, you know, in view at the same time, and we just can highlight whichever box we're currently showing you. Yeah. I think that could be a very interesting visual representation.\u003C\u002Fp>\u003Cp>It becomes very interactive at that point. Right? Where it's almost like, you know, the logs will just be represented with some sort of timeline bar where it's like, okay. You read operation 1 out of 16 steps, and then you can just go to the next one. Then the visual representation up top just sort of, like, highlights which box we're on.\u003C\u002Fp>\u003Cp>Right? And then just go to the next one, go to the next one, go to the next one. I still think we would have to have some sort of way where it's just, like, show me the whole thing as a JSON blob. Don't make me go through this this pretty thing, but I'm pretty hopeful that that could help, really help debug what's going on here. Speaking of which, why is the flow edit page the default?\u003C\u002Fp>\u003Cp>The view page seems kinda pointless. Yeah. Agreed. Done. So easy it can be sometimes.\u003C\u002Fp>\u003Cp>I I think the the honest answer is that kinda came from flows being initially designed as using the exact same UI and UX and order of operations as dashboards, where that is, very different, obviously, where dashboard is definitely read read only by default, and then only if you wanna change something to your panels for the order of the dashboard or that kind of stuff that you really go edit it. But in this particular case, I agree because it's like, what is really the difference between read and editing it? The only difference for read is that you can't, you know, click a button, but there's nothing that's really, you know, different between the two states. Right? So just getting rid of that read only view, I mean, it it makes sense to me, honestly.\u003C\u002Fp>\u003Cp>Food for thought. It's not not something we're just gonna we'll have to double check if that all makes sense, but just you know?\u003C\u002Fp>\u003Cp>Speaker 1: Well, let's say let's play with that thought a little bit. So let's say we make a new page, detailed page, which features everything that we want. Would we like to then be more stringent on how it looks? So maybe take away the ability to point, to to place nodes wherever you want so we can, you know, for example, make it, smaller for the detail page because we probably don't have the same amount of space as of right now. Is that something that we would like to do?\u003C\u002Fp>\u003Cp>I don't know. Like, how important is that even?\u003C\u002Fp>\u003Cp>Speaker 0: I think for the flow detail page, for the if you're talking about the flow detail page, it's not important. It's it's really more of a visual aid. This is like if you're mentally, you know, visioning what your flow looked like when you created it, you can see it sort of, like, go through the go through the motion. So it's really more meant as a graphical element that is more for, it's kinda like a map. It's kinda what I see it like.\u003C\u002Fp>\u003Cp>Right? It's it's a nice to have, not really a requirement. What I was kinda thinking we could potentially get away with though is instead of rendering the full boxes with everything in it, which just be the name or just an empty box for that matter. What we could do, though, it's I don't know if you've ever ever played with this, but if you have a dashboard set up Jonathan, do you happen to have a dashboard in this this demo instance? Plenty.\u003C\u002Fp>\u003Cp>You gotta love it. Dashboards and sort of the underlying panels and the the viewport stuff. Yeah. Exactly. It has a button that just sort of zoom out the fit.\u003C\u002Fp>\u003Cp>And also a full screen button for those who don't. Pro tip for you. So you can full screen and make it fit on your your, fit on your screen. But we could use that exact same thing for doing just sort of a mini representation of, of your flow at the top of this flow's detail log page. Right?\u003C\u002Fp>\u003Cp>Because you don't need to see all the exact details. It's mostly just as a graphical map for going through your flow. This could be a very\u003C\u002Fp>\u003Cp>Speaker 2: interesting trick. Similar similar tools I've seen, you can actually zoom in or out. You can actually zoom your view so you can see more of your flow or all of your flow. And then when you double click or click on something, then you get back into the item view or the detailed view. So back in our flows example here.\u003C\u002Fp>\u003Cp>Right? You'd be able to, you know, have a zoom option or be able to scroll in or out and zoom your view similar to a map style the way we do with the geospatial kinds of things. So very similar to that would be fun.\u003C\u002Fp>\u003Cp>Speaker 1: I wish I could see the screen right now, but, I thought there's something\u003C\u002Fp>\u003Cp>Speaker 0: Oh, right. About exhibit. I forgot.\u003C\u002Fp>\u003Cp>Speaker 1: For example, yeah, if if we decide to, you know, like, restructure the visuals or want to provide a different type of view, for example, like, we don't have to have boxes. We could also have, like, circles, for example, which could be smaller, take out the space. Maybe they're horizontal or something. I'm not sure if you showed this, right now. I'm sorry for that.\u003C\u002Fp>\u003Cp>But\u003C\u002Fp>\u003Cp>Speaker 0: Oh, no. No. No. No. It's I just I just love the the very bit of a sidebar.\u003C\u002Fp>\u003Cp>I just absolutely love the the artistic way you brought that just now. Maybe instead of rectangles, they could be circles. Yes. But yeah. No.\u003C\u002Fp>\u003Cp>You're absolutely right. Because we the nice thing is we know the exact dimensions of each box. Right? So we could theoretically, for a graphical overview, render each box at a half height and offset all of the, x and y positions by, you know, the number of boxes high To sort of recreate the diagram in a more compact view.\u003C\u002Fp>\u003Cp>Speaker 1: For sure.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Good point. So let's let's noodle on this idea for logs on the actual operation box. Right? So would that be a, sort of additional button, I guess?\u003C\u002Fp>\u003Cp>Because I'm also sort of operating under this. It's edit mode by default all the time. So I could imagine that maybe next to the edit button, there's just some sort of logs link.\u003C\u002Fp>\u003Cp>Speaker 2: And now\u003C\u002Fp>\u003Cp>Speaker 0: that I'm thinking about it even more.\u003C\u002Fp>\u003Cp>Speaker 2: Pre filter the logs to the particular one you're looking at, all that kind of fun stuff.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Because now that I'm thinking about it, we save the logs as one row with a bunch of JSON in it for the whole flow execution. Right? That's the, that's the log item that we have. So there's no real way to just say, give me all of the logs for just the update data operation, because you're looking about all of the, you know, all of the, all of the data from the whole flow, and then we have to sort of filter it back down.\u003C\u002Fp>\u003Cp>But, you know, we could consider, although the amount of data is gonna get out of control real quick, but we could consider saving, you know, a log row in the database for each operation step instead of for the whole flow. But we gotta make sure that we have some sort of automated retention setting and and a bunch of indexes, indices for that, to not completely wreck your database. Because if we're now creating 5 new records per flow instead of 1, then you're talking about millions and millions of data points, which, you know, depending on your database might not matter.\u003C\u002Fp>\u003Cp>Speaker 1: This has been a point in the discussion. The person that started then was already interested in some type of way to for example, similar to the activities, issue that we had, recently where, you know, big or large instances have trouble with, I don't know, I don't know, 2,000,000 activity entries and some type of schedule that clears logs or, for example, you only want to retain those who are at most, I don't know, 30 days old, for example. Some type of rule maybe which could, lighten the load a little bit would be helpful, I think.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, a 100%. Yeah. Yeah. The 100%. The the main reason we haven't had that before, I haven't done that yet, is because we didn't have support for this use yet.\u003C\u002Fp>\u003Cp>Right? And that's really a requirement. Because if you wanna delete with a filter on a timestamp and you don't have an index on that timestamp, now it might take your database, you know, minutes to just go through a table of that size. So that's that's also where that sort of sprint came through to, like, support indices and now collaborating with the the contributor there to to get that across the finish line. Because then once we have proper support for indices because because I do wanna make sure that, you know, whatever we do for the system stuff needs to be available to the end user.\u003C\u002Fp>\u003Cp>Right? So once we have support for indices, we can then enable indices for the system tables for, like, the timestamp in activity and and same for flow logs and stuff. And then the second piece of that is gonna be a setting that says, what do you want your, you know, low log retention to be? And it should be an environment variable, I guess, or maybe it's a per flow option. T b t b d on that.\u003C\u002Fp>\u003Cp>Right? Maybe there's an environment variable for the the maximum number you can set, and then in the app, there's a drop down, whatever. We'll have to figure it out. But, yeah, to your point, if you can configure, I only wanna keep 7 days worth of flow logs for this flow. I feel like that's a must have for any sort of upgrade that we're gonna be doing the flows, because it's so easy to just now accidentally end up with a couple of 100.\u003C\u002Fp>\u003Cp>Actually, that reminds me, I think, Kevin the other day did a fantastic little flow that he sent over where he was using it for, for beddlesnakes. But I also saw his flow logs in the sidebar already be in the 100, and I was like, how how long have you been running this right now? Right? You're gonna have 1,000 tens of 1,000 of those, so we need to have some sort of retention for that, which I could imagine, you know, if you have a flow that you know you're running a lot and you want it to be lowers, think it has to be per flow.\u003C\u002Fp>\u003Cp>Speaker 1: Food for thought. Food for thought for sure.\u003C\u002Fp>\u003Cp>Speaker 0: So then when we're thinking about, you know, one other thing I know about the debugging of, flow specifically is that right now to test anything, you have to trigger the actual thing that triggers this. Right? So, as of right now, if, Jonathan wanted to test this ready for review flow in any way, he would have to save this, quit, go to his articles collection, and then manually trigger it. Right? Oh, it's\u003C\u002Fp>\u003Cp>Speaker 2: so painful. It is so painful to debug these things, because you have to do just that. Right? I have to go I have to go save, then I have to go to our to my systems over here. I have to go to articles.\u003C\u002Fp>\u003Cp>I mean, at this point, normally, I've got an you're already kind of nabbed here, but now I've gotta open an article, and then I've gotta go through the actions of whatever I'm testing. So ready for review case. I have to push the button, then we go back, and then we're gonna go back to flows. We go back to ready for review. Now I have to go to logs, and now I can look at logs and see what's going on, test flow or not.\u003C\u002Fp>\u003Cp>So you're in here, you're traveling through, and you're looking at your payloads, and at your options wherever you are in the debug process, then you're closing out. Now you're going back to edit. So many mouse\u003C\u002Fp>\u003Cp>Speaker 0: It's a pain is what I'm hearing. It's a pain in the butt. Flow is\u003C\u002Fp>\u003Cp>Speaker 2: awesome, but it is it is painful to do the administration.\u003C\u002Fp>\u003Cp>Speaker 0: That's why we're here.\u003C\u002Fp>\u003Cp>Speaker 2: That's why we're here.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So one thing I think just wanna add another point from from him. Yeah. Sorry.\u003C\u002Fp>\u003Cp>Speaker 0: Go. Go. Go. Go. No.\u003C\u002Fp>\u003Cp>Please. Please. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Because, this this then touches the issue of alright, so let's you you want to create a flow. You want to try it out. You have to try it out in order to actually make it work. But then comes the thing, alright, so let's say I have an existing flow, and it's running. It should continue on running, but I want to, I want to edit it without changing the actual running flow, which would technically be something like, alright.\u003C\u002Fp>\u003Cp>Let's duplicate this flow so I can play around with it. And then, after I'm done with my modification, then I switch them or, basically, disable the previous one and enable the next one. So some type of let's let's call it, like, very, very lightly versioning. Yes. Oh, the chat.\u003C\u002Fp>\u003Cp>The chat is exactly it.\u003C\u002Fp>\u003Cp>Speaker 0: Once they\u003C\u002Fp>\u003Cp>Speaker 1: have the\u003C\u002Fp>\u003Cp>Speaker 0: whole tag.\u003C\u002Fp>\u003Cp>Speaker 1: Right. So so if, yeah, if you have a couple of, like, important very important flows running in production systems, you don't want to, you know, edit them willy nilly. So that's the that's the another thing, which is can be can be painful because as of right now, there's technically no duplicate button, which is, not as nice as it could be.\u003C\u002Fp>\u003Cp>Speaker 0: And, it's it's a difficult one, isn't it? Because I'm also thinking about some of the triggers. Would that make sense? Because if you make a new version of a an endpoint, right, and an endpoint has a UUID to run it. But then if you create a duplicate, now the endpoint is different.\u003C\u002Fp>\u003Cp>So then if you wanna make that your production one, now you have a different endpoint. Right? So it's like the the duplicate and then kill the old one flow pun intended of updating flows might not work for every trigger. Also, if you create a new version of a flow that uses, like, an event hook for, like, an item save on articles or something, like, in this example. Yeah.\u003C\u002Fp>\u003Cp>It would have to be some sort of dedicated versioning because if you create a duplicate, then now you're firing both of your flows. Right?\u003C\u002Fp>\u003Cp>Speaker 1: You know, a very, like, naive solution to that event problem would be to have the duplicated flow immediately be set to disabled. So if you duplicate duplicate something, the duplicate one is disabled, so it doesn't fire. And then you can play around with it, change something, and then enable it yourself. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Somebody in the chat that or somebody's plural in the chat, we're also saying, you know, you can obviously have a local dev instance or a dev copy where you could configure a new flow and then sort of import it at once so you don't, you know, mess anything up on production. Very true. Very true. Wanna make sure, you know, we keep we can Yeah. All the use ASM license.\u003C\u002Fp>\u003Cp>Speaker 1: This this then goes hand in hand with the next thing that I wanted to bring up with with the same thing. So let's say you have your dev instance, and then you want to sync your synchronize your flow to the production instance. Alright? There's no version control, basically. If you don't use, like, an actual extension, which you can put into your Git, version control, you would have to manually duplicate or use the API or something to to get the flow over or synchronize between your two instances.\u003C\u002Fp>\u003Cp>Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: Well, luckily, we talked about that particular problem 2 weeks ago in the last episode.\u003C\u002Fp>\u003Cp>Speaker 1: That's exactly right.\u003C\u002Fp>\u003Cp>Speaker 0: Like and subscribe. Exactly. The link right below the like button. Is that what they say?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I'm gonna have the sidebar here.\u003C\u002Fp>\u003Cp>Speaker 0: Exactly. Circling back, though, because we we have, I think, 2 or 3 different, UX points with, like, a couple of different solutions. So one thing that I think is a must have for sort of this flows upgrade project that we're putting together here is that there needs to be a button on the trigger where you can manually trigger that trigger. The difficulty will be what data do we trigger it with, and how can we help you make that data realistic? Right?\u003C\u002Fp>\u003Cp>That is gonna be the difficulty there, which is kind of what we haven't done before. Because right now, with this manual trigger on an article, you know, the data format for that, like we talked about with the chat a little bit earlier, it's very specific to that particular article and that particular, data model for the article if it's like a filter hook or something. So if you have a manual play button, so to speak, we need to have some sort of way where you can sort of fake what that event looks like. We can pre pre generate a couple of ones based on your instance, I guess. Because, like, we know for the manual trigger that you get the primary key of an article.\u003C\u002Fp>\u003Cp>Right? That's what you would trigger with. So when you hit a play button, I imagine that we would have to show some sort of modal or a dialogue that allows you to put in a JSON object, I guess, with what that sort of, fake data looks like. And for some of the triggers, I think we can prepare that cause we can we can pre fill, you know, a fake article manual run cause we know the format, collection, keys, etcetera. For some of them, you don't need to do anything at all.\u003C\u002Fp>\u003Cp>Like a cron thing, you can just play because there's no real, you know, trigger data coming in, maybe a timestamp, but I'm not a 100% sure. For an endpoint, though, there there's no way for us to know what that looks like. Right? Because the payload is user edit, so we don't really know do with that.\u003C\u002Fp>\u003Cp>Speaker 2: So so some of the things I've seen with other tools, then this goes this even goes back 20 plus years ago using WebLogic's BEA workflow engines. You could actually take data from previous runs, so from the logs essentially. You could actually generate your payloads dynamically from that, or have say, I wanna capture this data, and I wanna actually reuse that as the test mechanism and essentially create your test data from an existing run. And then, ultimately, you could even edit at that point, you could edit the payload. So if you wanted to change some of the data attribution for subsequent runs, you could actually just quickly edit.\u003C\u002Fp>\u003Cp>So as part of the you know, that kind of play operation, you would actually get some dialogue capability and say, I wanna use either an existing or, in some way, create or save your payload and then fire that in. And in that way, you know, we don't have to be trying to generate it. We can just say, you can either run it normally, go and trigger the action. You know. Another way that I've done some of it is, like, have an incognito window with log you know, I I don't have to actually do what I just showed you, which is exit my flow.\u003C\u002Fp>\u003Cp>I can actually do the testing from another user. You can there there are some ways you can simplify or streamline that just a little bit. And you may be testing that permission set anyway. Then you may have permissions and restrictions, and you may be testing that as part of your application anyway. You'd have a test user with those permission.\u003C\u002Fp>\u003Cp>But being able to capture that information from an existing log or flow, run would be super cool and helpful. And that's some of what we've captured some of that already.\u003C\u002Fp>\u003Cp>Speaker 0: We have the logs. I mean, we have all of that data to your point.\u003C\u002Fp>\u003Cp>Speaker 2: Like,\u003C\u002Fp>\u003Cp>Speaker 0: if if there was a previous run, we'd know exactly, you know, what what information was there anyway. So we could definitely use that to to prefill it. Is this sort of manual trigger for well, manual trigger is a bad bad name because we already have a thing called manual trigger. But it's this sort of play button. Let's call it that.\u003C\u002Fp>\u003Cp>Is that something you'd wanna do for the whole flow, like, on the trigger level, or is that something you'd also wanna be able to do per operation itself? Because somebody before was like, you know, I used the run script operation a lot. I can also imagine that it would be really useful then to just just be able to say, okay. Just replay my run script a couple of times. But what I was just about to say showed up in the chat.\u003C\u002Fp>\u003Cp>Shout out to the Dev once more. Operations depend on a lot of context from what happened before. Right? Yep.\u003C\u002Fp>\u003Cp>Speaker 2: That would be more like that step through capability, right, where you you can step through and, you know, in debug mode, essentially pause. Right? The you know, you have your your break point. And at that point, you stop. You can actually analyze and look at the data.\u003C\u002Fp>\u003Cp>So, I mean, this does become very much ID style kind because that's what this really is. We're we're doing ID development. The idea is you would have these kinds of breakpoint capabilities to be able to stop, look at what data and parameters, be able to look back through the current context, what does it look like. Am I getting what I expect? Okay.\u003C\u002Fp>\u003Cp>Okay. Then step through debug. And it gets complex because we've got this mix of operations, and then you got run script that has, you know, code. Right? You know?\u003C\u002Fp>\u003Cp>Now do I have another debug capability with stop points and break points inside the job? You know?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: Debugging that the TypeScript JavaScript in there directly. It gets very complex in that sense. But I think just the ability to be able to have a I wanna be able break point at the run script, be able to step to the next operation, then check the data and see I'll be able to multiple breakpoint options. We could get more creative down the line. I think if we can just put it at the operation level, that could it would be kinda cool if you could, you know, essentially step back, edit the data, or do something to the input, and then feed it back through, step back, step forward in in the operations themselves.\u003C\u002Fp>\u003Cp>Like, it's more complex\u003C\u002Fp>\u003Cp>Speaker 0: than And another thing with all of this, of course, is that, these operations may very well have side of on purpose because you might ping a different endpoint. You might save something's database, whatever. Right? So if you're manually playing, you know, with Flow or even individual operations for debugging purposes or whatever else, those side effects will still be in effect. Right?\u003C\u002Fp>\u003Cp>And if you're trying to debug a whole flow, you know, operation 2 might be dependent on a successful response from operation number 1. Like, number 1, save something to the database. Number 2, reads it, you know, now that it has been saved or something. I'm just trying to come up with an example. Because I was thinking, you know, it it be important to think through some sort of dry run as well where you can just like, okay.\u003C\u002Fp>\u003Cp>This would have now saved to the database. Right? And then, oh, this would have now made a request to the API, but we didn't actually do that. We didn't want to make a Stripe subscription as part of our little testing thing right now, right, to just test if everything works. So that'll be interest interesting.\u003C\u002Fp>\u003Cp>Some sort of dry running, thing behind it. I I do think it's important to think through at least because of the side effects. But to again, shout out to the derp. Can't really do a universal drive run because you never know what, you know, arbitrary stuff is being used.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. You'd almost have to have, like, a successful run or at least a set of data and payload options that you could edit up to say, you know, this is what I'm kind of expecting as I process through my input outputs.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 2: So So you'd have to have input output capabilities. What do you expect? And then as\u003C\u002Fp>\u003Cp>Speaker 0: you say,\u003C\u002Fp>\u003Cp>Speaker 2: dry run mode, not save, but often, you know, maybe the change that I'm making, I then getting that and then doing something. So I'm sending it to that I'm sending a webhook out data payload that comes back, you know, depending on how complex you make a flow. Mhmm. My general recommendation is if flows get that complex, go write a\u003C\u002Fp>\u003Cp>Speaker 0: hook. But it's Well\u003C\u002Fp>\u003Cp>Speaker 1: Well it\u003C\u002Fp>\u003Cp>Speaker 0: it there is gonna be a point.\u003C\u002Fp>\u003Cp>Speaker 2: It's a balancing act. Right? There there are I I like flows for admin kinds of functions where state management user control. When I get into really complex conditional lock, do doing this in these boxes so much faster to write TypeScript, be done with a hook on the backside. That's my Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: The the similar to yeah. What I wanted to say is this sounds, like, very similar to some type of bash script or something. You know, some some type of scripting, where as soon as you need conditions or something, it's probably better to make it a program. Because, who likes to write bash? Basically, you know, it it gets it gets gnarly really quickly.\u003C\u002Fp>\u003Cp>So as soon as you need sophisticated stuff, some type of conditions that branch into something and do something with arrays or something, it's probably better to actually use a programming language. And,\u003C\u002Fp>\u003Cp>Speaker 0: I, yeah, I don't know. It really depends on a lot of things because one one is like, do you even know TypeScript or JavaScript in the first place? I think that's an important one. I think by, you know, doing it manually as as code, you also have a lot more opportunities to break it bad. You know, make it insecure, just make it an optimized break stuff tremendously hard.\u003C\u002Fp>\u003Cp>And also in, the chat somebody pointed out, you know, with flows, you do get the the logs and sort of the ability to explore the steps and what went wrong. So it's it's an interesting take, Jonathan, because what we're saying is, like, as as of today, you know, when it gets complicated, you wanna switch to code because the flows, UX, and UI just doesn't isn't good enough yet for complicated stuff. Right? So Right.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Yeah. It is.\u003C\u002Fp>\u003Cp>Speaker 0: But I I'm hopeful.\u003C\u002Fp>\u003Cp>Speaker 2: If if we make this kind of what we're talking about, if we get to that point in these next few iterations on maybe it does become I don't actually need hooks as often. Right? I could be you know, hooks could just be when I need external library.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Exactly.\u003C\u002Fp>\u003Cp>Speaker 2: Allow within flow at the moment.\u003C\u002Fp>\u003Cp>Speaker 0: I'm just I'm very I'm very hopeful that by by some of these seemingly small but very big changes, like adding the manual trigger so you can at least just try it out, I think we can actually get this to a point where\u003C\u002Fp>\u003Cp>Speaker 2: HOOX\u003C\u002Fp>\u003Cp>Speaker 0: is gonna be the more complicated one to do, you know, because it's like, yes, you have a black box and do whatever, but you don't get all of the debugging niceties and and all of the comments. Not that it's a battle. I mean, at the end of the day, it's just pick whatever you want, what's worked best for you. But I think that we can reverse that take, honestly, that flows could be I think that might be my personal challenge. Make flows the default for Jonathan.\u003C\u002Fp>\u003Cp>That's my measure of success.\u003C\u002Fp>\u003Cp>Speaker 2: Writes the worst TypeScript he's ever seen.\u003C\u002Fp>\u003Cp>Speaker 0: If he says I prefer flows over hooks, it means we did a good job.\u003C\u002Fp>\u003Cp>Speaker 2: Perfect. I love it.\u003C\u002Fp>\u003Cp>Speaker 0: Project Jonathan is what we call it. Cool. Okay.\u003C\u002Fp>\u003Cp>Speaker 2: If you can keep me from writing code, it's always a good.\u003C\u002Fp>\u003Cp>Speaker 0: It's always a good idea. So there's there's definitely a couple of unresolved You know, as per usual, I with the eye on the clock. As per usual, we're gonna be wrapping up. We've been taking a lot of notes during all of this, and it's, again, very divergently thinking. We're gonna be compiling all of these notes and ideas from everything that we just talked about and everything on the chat.\u003C\u002Fp>\u003Cp>Thank you for that. Into, you know, a proper RFC document and figure out what does this project upgrade flows, x y z, you know. What does that look like? What do we see as the must have? What can we get done in a sort of initial sprint?\u003C\u002Fp>\u003Cp>There have been, I think, like, 4 or 5 different discussions that are all sort of asking for the same thing, which is, like, improve Flows debugging. But they're all various sort of smaller bits and pieces of a, to me, bigger Flows 2 point o type of upgrade. So we're gonna be trying to compile those and sort of merge them into one sort of flows project, if that makes sense. Just as a quick summary, I think just the the upgrades to the logs that we talked about, you know, making them, like, explorable. I guess that's the right the right word.\u003C\u002Fp>\u003Cp>Giving them its own page, giving it more space, making it easier to step through, you know, the execution of a flow. Definitely a big must have. And the ability to trigger a flow from where you're editing it with some sort of, you know, preloaded payload from a previous run or something you can manually adjust so you can actually try out what the hell it is that you're building without having to go all over the app. I think it's gonna be a very, very important thing to have. Last question from from the chat here before we wrap up.\u003C\u002Fp>\u003Cp>Someone had said that many of these ideas could be implemented by the community in a custom module? No. Yes. Community can build whatever they want as a custom module at the end of the day. That is very true.\u003C\u002Fp>\u003Cp>And with the, you know, upcoming release of, for marketplace project, I'm also very curious to see, you know, what people will do with this type of stuff. Right? Somebody could, definitely, make a flow flows log module and just make it super custom or super proprietary or super flexible, whatever they want Whatever they want, really. Cool. Okay.\u003C\u002Fp>\u003Cp>There was one more that snuck in with an upvote. One final thing. Can we have access to the flow manager from the flows service so we can do more things with flows from extensions? May may may. No problems.\u003C\u002Fp>\u003Cp>So so It depends. It depends. Now I'll I'll, it's oddly tucked away compared to the extension manager, which should also similarly be tucked away, actually. Anyways, no. The the the reason for the hesitation is more around updates that I kinda wanna do to those services in the first place.\u003C\u002Fp>\u003Cp>Right? So I know I wanna do some sweeping upgrades to what those services look like and how they work in the first place. So I'm hesitant to to add new stuff onto the services service that we know might change already. Right? So I I don't wanna introduce something new that's gonna change then fast after, if that makes sense.\u003C\u002Fp>\u003Cp>That being said, yes, I do wanna make sure that extensions do have access to all of those types of internal things, which is also a reason why we've been moving a lot more stuff into individual libraries. Right? So I don't know. For for those who have been watching the repo like a hawk, we just moved sort of the environment variable extraction and handling different library with the same sort of idea, right, where an extension can just import the Directus ENV library and then use the same sort of ENV handling that, Directus does itself. We're we're basically doing that across the board, and the services is one of those things that I wanna do like that.\u003C\u002Fp>\u003Cp>And I can definitely imagine there being eventually being, you know, a Directus flows library that contains, you know, this flows manager state, so that extensions just can import it just like the direct as API would with TypeScript typing. I I heard you. Community, I heard you. With proper TypeScript typing and everything as well. So that's that's the only reason why I'm a little hesitant to promise anything better, as of right now.\u003C\u002Fp>\u003Cp>Any cool. That marks 11 sharp on the clock on my end. I think this was a very interesting session so far. This is a little bit more divergently rambling, than doing an existing discussion, but I think we've touched on a couple of very, very important points. Any any other closing thoughts?\u003C\u002Fp>\u003Cp>Speaker 2: If I could remember the dad joke from Bryant this morning, I would tell it, but I suck at remembering jokes. So\u003C\u002Fp>\u003Cp>Speaker 0: Well, tune in next time for for\u003C\u002Fp>\u003Cp>Speaker 2: John's phone dad joke.\u003C\u002Fp>\u003Cp>Speaker 0: With all that being said, thank you to the audience. Thank you for watching. If you're watching this on Directus TV, Directus dot a 0\u002Ftv, Hi. Be sure to watch the previous versions of this video down below. And we'll see you guys in 2 weeks, I think.\u003C\u002Fp>\u003Cp>Or are we skipping that one? Because it's sleep week, I think. Maybe oh, TBD. There will be one.\u003C\u002Fp>\u003Cp>Speaker 2: I think our next one's in March, because the I think or no. No. It's actually the other way around. We have one more month. We're skipping the first one in March.\u003C\u002Fp>\u003Cp>That was the\u003C\u002Fp>\u003Cp>Speaker 0: See you guys in 2 weeks then.\u003C\u002Fp>\u003Cp>Speaker 2: I remember now. My poor old brain catching up.\u003C\u002Fp>\u003Cp>Speaker 0: It's all in the event tab. Exactly. Keep your eye on it.\u003C\u002Fp>\u003Cp>Speaker 2: It's all in the event tab.\u003C\u002Fp>\u003Cp>Speaker 0: Thanks, everybody.\u003C\u002Fp>\u003Cp>Speaker 2: Kevin's keeping us honest. Cheers, everyone. Bye.\u003C\u002Fp>\u003Cp>Speaker 0: Bye bye.\u003C\u002Fp>","Alright. Yeah. So, Jonathan, you already, briefly introduced that seeing all the hiccups, I don't know if we're gonna cut that. But just to restart it, we're gonna be talking about flows today. We're really gonna be starting with this 15 8 70 about improving the activity panel. But, realistically, as far as I'm concerned, it's sort of a smaller piece of a bigger discussion around upgrades to flows. Right? So for those out of the loop, we introduced that flow system as a way to do event based, actions and operations, kinda like you could with hooks, or custom endpoints, but doing it in a no code type of way. Right? So you configure when you want your flow to trigger and then step by step configure individual operations, individual nodes of sort of this this path, that you wanna execute. The original version of that was really much designed as a, start with the basics, see what people wanna use it for, see where what what the best improvements are gonna be. So we shipped it a little bit lightweight, you know, with a handful of operations and have been more and more over time. But it has been becoming increasingly obvious that there's, a lot of improvements that would be really nice to have, especially when it comes to debugging, sort of helping configuring some of these pieces. So with that being said Yes. Discussion. So for Daniel, who's flying blind here, we're looking at the one about the logs first. Right? So if you're currently running a flow, optionally if you have it enabled, it's enabled by default. It will keep track of the data that sort of went through the various operations, and then saves it to, a logs tab that you can see on the right hand side of the flow. This is basically the primary way at the moment where you can debug what is happening under the hood, because otherwise, you know, there it's it's the way where you can see what data came actually through the trigger and how you've modified the data points in between. So over the Jonathan, you're you're showing it out what that looks like now on your own instance. What are some of the points in this initial discussion that triggered this this conversation? You've got a lot of the ease and access to that information, as well as the ability to control, like, durations. Some of the problems we run into with flows, especially if someone once they get them into production, if they still have the activity and logging turned on, you can fill up your activity revisions tables really quickly. And there's you know, the mechanisms for cleaning that up are, you know, another flow, or direct database access or other kinds of things where you've gotta manage your activity and revision logs and so forth. Common recommendation that I make is once you've done your testing and you've got your flows functioning is to disable the activity and logging. But there are cases where you may want that and logging just for audit trackability on actions and things that people are doing with flows. So, I think some of that is just the general trigger and management of that. Other things that, tend to cause struggle for people in flows is the way that you access this instead of being able to say, look at the data from the log directly in something that I'm working on. There's been a again, there's a number of ways that we can kind of shake and look at this. But the general thing is this kinda lives over here. I can't edit this while I'm looking at this, so the the interactions with logs and the work that you're doing is kind of a it's a bit of a hindrance when you're doing the development phase of flow development. So it's just a more a nuisance than anything else. But the the inability to say edit something here, I have to actually come here to check. So if I'm looking for variables or data or what are the things looking like in here, what's my payload? Now I've gotta remember this data structure or copy this data somewhere else because I wanna actually do something with it. I wanna interact with it. I wanna use those variables. So it's kind of a it's a weird transition kind of back and forth. Some of the systems that we we got in some inbound activity on some other tickets and feedback that we got from some clients on the enterprise side, was you know, they showed showed us some other systems that have similar no code kinds of flow capabilities. And logs, instead of living in, like, a side panel in a weird way, kind of live with the operations themselves. So there's just some general ideas and thoughts around how that interaction in the UX kind of works. Data wise, I don't think anybody's complaining about the information that shows up in here. That tends to be you know, it's fairly easy to work with and deal with. I think it's more the kind of user interactions with data. Yeah. For me, personally, I've ran into this when I tried to set up a flow which triggers another flow, which then leads you to, you know, switching between those 2. And then you have to check that again, but then you forgot on the first flow, forgot something, and you have to switch back. And, you know, so people do run into this, and, I've experienced this myself. So, generally, if I if I experience this, the general user will probably also experience this. So this is a very valid point, in my opinion, and, we should we should improve this a little bit. You're muted, Jonathan. Sorry. You're muted. You look very passionate, though. I'll give you that. But Sorry. I'm not gonna mute. So so what that does is, you know and that's why we saw this very same thing as we started doing some internal research on just this ticket. We rapidly diverged into we've got 4 or 5 other kinds of spec, and that's why we it's kinda leading into this call today. We were talking about the fact that there's some general overall flows improvements that I think we'd like to see across the boards. But the logging and activity, you know, that's that kinda triggered this action was really about that the way that those logs interact and how you have to it's kind of a separation of what is a common function. Yeah. Definitely. Totally agree. And, like the other comment on the chat said, switching is painful. Yes. An auto refresh for logs would go a very long way because I I do think that these two things are very, service the same niche, service the same pain. Because you you want to have up to date logs and see what is different, what do I have to work with, what can I work with, and not being able to have that on auto refresh, for example? Is the same thing as context switching back and forth, and you want to refresh and whatever. So I think these two pieces are very similar and and and try to work on the same pain, basically. So, good point from the chat. Thank you very much. So there's a lot of lot of, I saw the word divergent on the screen. Just made me think there's a lot of divergent ideas happening at the same time. Right? We're talking about a lot of smaller optimizations like the auto refresh or some sort of way to make that context switching a little bit less painful. If we take a deeper look at this discussion, though, that sort of triggered everything, are there any particular pain points about the way we describe or show those logs that is currently sort of, something that should be improved? I'm kinda skimming through this guy. See some people typing in the chat. Please let us know if you have experience in using flows and would like to, contribute. We're here for you. I think on the Trigger log is the hardest to understand. I'm still on. So on that log side, the other key thing that's being pointed out here is there are differences in the inbound payloads based on whether you're doing a create, update, or delete operation. You do have some variation in the trigger payload bodies. And, again, I don't know that it's a bad thing, but it is one of the things that's called out as a there's differences in the pathing, say, to the collection or the item, based on what type of event triggered the action. Yep. Yeah. Very good one. That that makes a lot of sense, and it's it's it's explainable from a technical perspective, but also makes sense where it's where where the pain point's coming from. So earlier on, when we just did the hooks initially, the decision was made to differentiate between create a single thing and create multiple things, which in turn means that sometimes you get a single ID as a sort of, string or a number, and sometimes you get an array of ID strings or a number, which is basically just a bigger discussion around what does that hook payload look like. Right? Because everything is based on the same internal, event system. So, you know, a flow is triggered based on the same hook that you get from, a hook extension, for example. It's all the same thing. Then, the difference between, you know, trigger dot keys, trigger dot body dot keys, etcetera, sometimes it's payload, sometimes it's not, That really depends on the type of trigger, which, again, you know, doesn't necessarily make it make it better, but it is, you know, explainable where it's coming from, where. If you're having an endpoint, you know, with a a trigger request, now you're dealing with a sort of user payload that was submitted that could be anything. Right? If you're dealing with a hook, it's a pre pre known format for what our hooks fire. But, you know, it's there's a difference. There's gonna be a difference, and that is that's definitely tricky. Somebody said, if hooks supported loops, we probably wouldn't need the differentiation for 1 versus multiple. Hook supported loops. I'm not entirely sure what you mean by that because a hook is just a bit of JavaScript, so you could loop over whatever you want. Right? Oh, flows. Oh. Oh, I see. Gotcha. Gotcha. Gotcha. Gotcha. Yeah. So we're basically saying, you know, if you have a way to just say do this flow against every item in the triggered whatever trigger keys, then we can basically just drop the one hook and make everything an array all the time, sort of get rid of some of that confusion, which for the record Yeah. Makes a ton of sense to me. I mean, every every insert into the database could be 1 or multiple things. And if you do one thing, it's just an area of one thing. Right? It's basically it's it's easy to explain. So I I do agree with that sort of general sentiment. Even though for the record, that would be a quite a big breaking change and totally wreck everybody's existing flows and extensions. So TBD TBD. As mentioned, the extension shed will be very possible to create a repeat operation for all elements in an array extension with exposing a single function from the flows manager. Yeah. Yeah. No. Absolutely. Yeah. That's very true. Very true. Cool. There is one more, thing in the chat here from our very own Brian saying remembering the key names for operations is a pretty big headache for me. Why are the keys only showing on hover? Where can I not copy them? Which has been a thing all over the app. And just trying to remember what that key name is. That's a very good point. And I think, you know, overall in the app, we've sort of on the side of making things sort of, what's the right word? User friendly is such an empty thing, but, like, look pretty for nontechnical users. Right? So a lot of stuff gets, what we call title formatted. Family friendly. Yeah. Exactly. We we we title format a lot of that stuff, so it looks prettier in the UI. But totally. Yeah. For things like flow logs, you know, which is inherently a very technical thing, flows is only available for admin users, which are generally speaking, a little bit more of the on the technical side. Those should most likely just be the technical keys. Right? Render them in monospace and just lean into it. Because why why show them as a title formatted version and then have it only show on hover that you can then not copy paste because it's a tooltip. Right? It that that makes a ton of sense, and that is just a perfect sort of tiny little tiny little tweak that's a huge quality of life improvement. Jonathan, I hope that was in the notes somewhere now. Yep. I'm taking some notes in in a separate section, over in the the actual internal notion doc that we've got running on this guy. So I'm trying to capture pretty much it. Another good point here too is, like, why even have a pretty name if you just have the key? You know? Good point. Good point. I think having some sort of description is a nice, you know, nice to have where at least you can write, like, a mini description, but you wouldn't really use the name for that anyways necessarily. Cool. Okay. Just looking at the discussion, though, because I see we're sort of, what, 1 eighths of the way on the scroll on the page. So I'm kinda curious to see if there's any other points in this particular discussion that we haven't really touched on. That's easy to forget. So these ones we just looked at. I think, the suggestion by our own Oh, the the JSON object wrapping. Object wrapping was the other big thing. Again, having this in the side panel, data here, you can start to see if you get long things. It it actually goes off screen and ends up, you know, and you I don't even think it enables a scroll. I'm not sure you can even get to long data that's being displayed in the log there. So some form of wrap, at least, if we're gonna continue in this panel. I think, ultimately, I think we'd like to maybe move this out of the panel anyway. It'll end up somewhere else in the UX. But something for us to keep in mind is that JSON wrapping, and we have that captured as well in a I was just gonna say that it's there it it's I can already also see there's no way to search or filter through logs. Right? That is another, you know, in. The only thing we show about the log is just a time stamp and then a rel as a time stamp. But I'd I'd say an obvious next step is to sort of move that out of the sidebar into just a proper, you know, layout like we do with other things all over the place. So you get the searching, you get the filtering, and we can have a proper detailed view, or draw our although a detail page is probably gonna be nicer for that. Save a little bit more space, for things like that overflowing scroll and just presenting it nicer. Although, then, you know, we create a new problem, which is now the logs live outside of the flow where you configure them. So at that point, you know, we also wanna make sure that we have some sort of way where you can render that maybe as a split view, maybe you can sort of 50 50 between the flow that you're creating and then seeing the logs of that flow with this layout. Because I do believe that keeping them in context or at least having some sort of link back and forth, from, you know, where you're configuring the the law, the flow and configuring the log is gonna be important. From the chat, having logs and operations might be helpful for that. Yes. Yes. Yes and no. It's it's I'm I'm, to me, the logs on operations is an addition, not a replacement. I think personally it's very valuable that you can see, you know, the full, execution path in a log. You can see, you know, at 2 pm today we started with this, we started with it, that one failed, and then we did this, and then here's the data that went through it and what we concluded with. I do think it's important to have that sort of consolidated together, but the the logs and operations, I do agree, would have really be a really nice addition to that where you can just look like, okay. In the last couple of runs, here's the day that it came in and went out of this particular box in your flow. Right? I don't know if that's gonna be a deep link or maybe just an info. Almost feels like you could have, like, a split panel down here that had the normal, like, search layout capabilities of searching through the logs, could be an interesting use case for this where you'd Mhmm. Similar to Visual Studio and other kinds of console logs where when you're doing this kind of development, you've got logging information you can see down here. Because I also think some of the other nice things with flows would be able to have, like, step through. Right? Being able to step through and see data at certain points, what's going on as you're debugging and working on the flow itself. But then as you say, having an actual, you know, log panel or maybe there's a, like, in a sub ten under flows where you can go to the logs and just see logs full screen, be able to search. Because once my flow is operating, I think the the DERF, pointed out that they're they like logs turned on so when users report issues hours later, they can go back and search the logs. And in that case, I'm not looking at the flow. I'm looking at what's the data, what's the flow, what happened, what errors, what things do I see. So cool. Because the nice thing by putting the again, this for for those new to the stream, this is very much a session of divergently thinking. Right? We're just gonna go brainstorm a bunch, like, what will be the ideal state and then take it back towards the end to, like, okay. What is an actual realistic next step? So I can also imagine, you know, closing your eyes thinking about it, if we have a proper detail page, like a full full screen view for the logs for one particular run. Right? We could actually render sort of a smaller, graphical representation of your flow as you have configured it, in in here, and then just show you. You can sort of replay it, so to speak. So we can just show you step 1, and then that's the data that came in and out based on the logs that you're looking at. Right? So instead of just having a long list top to bottom, you could effectively just get a visual replay, of that particular flow operation. I think that could be an interesting one too. Because then you really get to visualize the the execution of that flow because you're effectively trying to follow a path through your flow with the log. But right now, the log is always gonna be a a sort of, what's the right word, a one dimensional list, top to bottom. Right? Chronological. But, you're thinking about your flow, potentially, in more of a graphical tree. Right? Where it's like you have option b, and then you branch off, etcetera, which is not really, represented in the logs in any sort of way right now. From the chat, somebody you could almost use the exact same UI as the flow editing page for that. Yeah. No. Yeah. Yeah. Exactly. Maybe a little bit smaller so you have the whole thing, you know, in view at the same time, and we just can highlight whichever box we're currently showing you. Yeah. I think that could be a very interesting visual representation. It becomes very interactive at that point. Right? Where it's almost like, you know, the logs will just be represented with some sort of timeline bar where it's like, okay. You read operation 1 out of 16 steps, and then you can just go to the next one. Then the visual representation up top just sort of, like, highlights which box we're on. Right? And then just go to the next one, go to the next one, go to the next one. I still think we would have to have some sort of way where it's just, like, show me the whole thing as a JSON blob. Don't make me go through this this pretty thing, but I'm pretty hopeful that that could help, really help debug what's going on here. Speaking of which, why is the flow edit page the default? The view page seems kinda pointless. Yeah. Agreed. Done. So easy it can be sometimes. I I think the the honest answer is that kinda came from flows being initially designed as using the exact same UI and UX and order of operations as dashboards, where that is, very different, obviously, where dashboard is definitely read read only by default, and then only if you wanna change something to your panels for the order of the dashboard or that kind of stuff that you really go edit it. But in this particular case, I agree because it's like, what is really the difference between read and editing it? The only difference for read is that you can't, you know, click a button, but there's nothing that's really, you know, different between the two states. Right? So just getting rid of that read only view, I mean, it it makes sense to me, honestly. Food for thought. It's not not something we're just gonna we'll have to double check if that all makes sense, but just you know? Well, let's say let's play with that thought a little bit. So let's say we make a new page, detailed page, which features everything that we want. Would we like to then be more stringent on how it looks? So maybe take away the ability to point, to to place nodes wherever you want so we can, you know, for example, make it, smaller for the detail page because we probably don't have the same amount of space as of right now. Is that something that we would like to do? I don't know. Like, how important is that even? I think for the flow detail page, for the if you're talking about the flow detail page, it's not important. It's it's really more of a visual aid. This is like if you're mentally, you know, visioning what your flow looked like when you created it, you can see it sort of, like, go through the go through the motion. So it's really more meant as a graphical element that is more for, it's kinda like a map. It's kinda what I see it like. Right? It's it's a nice to have, not really a requirement. What I was kinda thinking we could potentially get away with though is instead of rendering the full boxes with everything in it, which just be the name or just an empty box for that matter. What we could do, though, it's I don't know if you've ever ever played with this, but if you have a dashboard set up Jonathan, do you happen to have a dashboard in this this demo instance? Plenty. You gotta love it. Dashboards and sort of the underlying panels and the the viewport stuff. Yeah. Exactly. It has a button that just sort of zoom out the fit. And also a full screen button for those who don't. Pro tip for you. So you can full screen and make it fit on your your, fit on your screen. But we could use that exact same thing for doing just sort of a mini representation of, of your flow at the top of this flow's detail log page. Right? Because you don't need to see all the exact details. It's mostly just as a graphical map for going through your flow. This could be a very interesting trick. Similar similar tools I've seen, you can actually zoom in or out. You can actually zoom your view so you can see more of your flow or all of your flow. And then when you double click or click on something, then you get back into the item view or the detailed view. So back in our flows example here. Right? You'd be able to, you know, have a zoom option or be able to scroll in or out and zoom your view similar to a map style the way we do with the geospatial kinds of things. So very similar to that would be fun. I wish I could see the screen right now, but, I thought there's something Oh, right. About exhibit. I forgot. For example, yeah, if if we decide to, you know, like, restructure the visuals or want to provide a different type of view, for example, like, we don't have to have boxes. We could also have, like, circles, for example, which could be smaller, take out the space. Maybe they're horizontal or something. I'm not sure if you showed this, right now. I'm sorry for that. But Oh, no. No. No. No. It's I just I just love the the very bit of a sidebar. I just absolutely love the the artistic way you brought that just now. Maybe instead of rectangles, they could be circles. Yes. But yeah. No. You're absolutely right. Because we the nice thing is we know the exact dimensions of each box. Right? So we could theoretically, for a graphical overview, render each box at a half height and offset all of the, x and y positions by, you know, the number of boxes high To sort of recreate the diagram in a more compact view. For sure. Yeah. Good point. So let's let's noodle on this idea for logs on the actual operation box. Right? So would that be a, sort of additional button, I guess? Because I'm also sort of operating under this. It's edit mode by default all the time. So I could imagine that maybe next to the edit button, there's just some sort of logs link. And now that I'm thinking about it even more. Pre filter the logs to the particular one you're looking at, all that kind of fun stuff. Yeah. Because now that I'm thinking about it, we save the logs as one row with a bunch of JSON in it for the whole flow execution. Right? That's the, that's the log item that we have. So there's no real way to just say, give me all of the logs for just the update data operation, because you're looking about all of the, you know, all of the, all of the data from the whole flow, and then we have to sort of filter it back down. But, you know, we could consider, although the amount of data is gonna get out of control real quick, but we could consider saving, you know, a log row in the database for each operation step instead of for the whole flow. But we gotta make sure that we have some sort of automated retention setting and and a bunch of indexes, indices for that, to not completely wreck your database. Because if we're now creating 5 new records per flow instead of 1, then you're talking about millions and millions of data points, which, you know, depending on your database might not matter. This has been a point in the discussion. The person that started then was already interested in some type of way to for example, similar to the activities, issue that we had, recently where, you know, big or large instances have trouble with, I don't know, I don't know, 2,000,000 activity entries and some type of schedule that clears logs or, for example, you only want to retain those who are at most, I don't know, 30 days old, for example. Some type of rule maybe which could, lighten the load a little bit would be helpful, I think. Oh, a 100%. Yeah. Yeah. The 100%. The the main reason we haven't had that before, I haven't done that yet, is because we didn't have support for this use yet. Right? And that's really a requirement. Because if you wanna delete with a filter on a timestamp and you don't have an index on that timestamp, now it might take your database, you know, minutes to just go through a table of that size. So that's that's also where that sort of sprint came through to, like, support indices and now collaborating with the the contributor there to to get that across the finish line. Because then once we have proper support for indices because because I do wanna make sure that, you know, whatever we do for the system stuff needs to be available to the end user. Right? So once we have support for indices, we can then enable indices for the system tables for, like, the timestamp in activity and and same for flow logs and stuff. And then the second piece of that is gonna be a setting that says, what do you want your, you know, low log retention to be? And it should be an environment variable, I guess, or maybe it's a per flow option. T b t b d on that. Right? Maybe there's an environment variable for the the maximum number you can set, and then in the app, there's a drop down, whatever. We'll have to figure it out. But, yeah, to your point, if you can configure, I only wanna keep 7 days worth of flow logs for this flow. I feel like that's a must have for any sort of upgrade that we're gonna be doing the flows, because it's so easy to just now accidentally end up with a couple of 100. Actually, that reminds me, I think, Kevin the other day did a fantastic little flow that he sent over where he was using it for, for beddlesnakes. But I also saw his flow logs in the sidebar already be in the 100, and I was like, how how long have you been running this right now? Right? You're gonna have 1,000 tens of 1,000 of those, so we need to have some sort of retention for that, which I could imagine, you know, if you have a flow that you know you're running a lot and you want it to be lowers, think it has to be per flow. Food for thought. Food for thought for sure. So then when we're thinking about, you know, one other thing I know about the debugging of, flow specifically is that right now to test anything, you have to trigger the actual thing that triggers this. Right? So, as of right now, if, Jonathan wanted to test this ready for review flow in any way, he would have to save this, quit, go to his articles collection, and then manually trigger it. Right? Oh, it's so painful. It is so painful to debug these things, because you have to do just that. Right? I have to go I have to go save, then I have to go to our to my systems over here. I have to go to articles. I mean, at this point, normally, I've got an you're already kind of nabbed here, but now I've gotta open an article, and then I've gotta go through the actions of whatever I'm testing. So ready for review case. I have to push the button, then we go back, and then we're gonna go back to flows. We go back to ready for review. Now I have to go to logs, and now I can look at logs and see what's going on, test flow or not. So you're in here, you're traveling through, and you're looking at your payloads, and at your options wherever you are in the debug process, then you're closing out. Now you're going back to edit. So many mouse It's a pain is what I'm hearing. It's a pain in the butt. Flow is awesome, but it is it is painful to do the administration. That's why we're here. That's why we're here. Yeah. So one thing I think just wanna add another point from from him. Yeah. Sorry. Go. Go. Go. Go. No. Please. Please. Yeah. Yeah. Because, this this then touches the issue of alright, so let's you you want to create a flow. You want to try it out. You have to try it out in order to actually make it work. But then comes the thing, alright, so let's say I have an existing flow, and it's running. It should continue on running, but I want to, I want to edit it without changing the actual running flow, which would technically be something like, alright. Let's duplicate this flow so I can play around with it. And then, after I'm done with my modification, then I switch them or, basically, disable the previous one and enable the next one. So some type of let's let's call it, like, very, very lightly versioning. Yes. Oh, the chat. The chat is exactly it. Once they have the whole tag. Right. So so if, yeah, if you have a couple of, like, important very important flows running in production systems, you don't want to, you know, edit them willy nilly. So that's the that's the another thing, which is can be can be painful because as of right now, there's technically no duplicate button, which is, not as nice as it could be. And, it's it's a difficult one, isn't it? Because I'm also thinking about some of the triggers. Would that make sense? Because if you make a new version of a an endpoint, right, and an endpoint has a UUID to run it. But then if you create a duplicate, now the endpoint is different. So then if you wanna make that your production one, now you have a different endpoint. Right? So it's like the the duplicate and then kill the old one flow pun intended of updating flows might not work for every trigger. Also, if you create a new version of a flow that uses, like, an event hook for, like, an item save on articles or something, like, in this example. Yeah. It would have to be some sort of dedicated versioning because if you create a duplicate, then now you're firing both of your flows. Right? You know, a very, like, naive solution to that event problem would be to have the duplicated flow immediately be set to disabled. So if you duplicate duplicate something, the duplicate one is disabled, so it doesn't fire. And then you can play around with it, change something, and then enable it yourself. Yeah. Somebody in the chat that or somebody's plural in the chat, we're also saying, you know, you can obviously have a local dev instance or a dev copy where you could configure a new flow and then sort of import it at once so you don't, you know, mess anything up on production. Very true. Very true. Wanna make sure, you know, we keep we can Yeah. All the use ASM license. This this then goes hand in hand with the next thing that I wanted to bring up with with the same thing. So let's say you have your dev instance, and then you want to sync your synchronize your flow to the production instance. Alright? There's no version control, basically. If you don't use, like, an actual extension, which you can put into your Git, version control, you would have to manually duplicate or use the API or something to to get the flow over or synchronize between your two instances. Mhmm. Well, luckily, we talked about that particular problem 2 weeks ago in the last episode. That's exactly right. Like and subscribe. Exactly. The link right below the like button. Is that what they say? Yeah. I'm gonna have the sidebar here. Exactly. Circling back, though, because we we have, I think, 2 or 3 different, UX points with, like, a couple of different solutions. So one thing that I think is a must have for sort of this flows upgrade project that we're putting together here is that there needs to be a button on the trigger where you can manually trigger that trigger. The difficulty will be what data do we trigger it with, and how can we help you make that data realistic? Right? That is gonna be the difficulty there, which is kind of what we haven't done before. Because right now, with this manual trigger on an article, you know, the data format for that, like we talked about with the chat a little bit earlier, it's very specific to that particular article and that particular, data model for the article if it's like a filter hook or something. So if you have a manual play button, so to speak, we need to have some sort of way where you can sort of fake what that event looks like. We can pre pre generate a couple of ones based on your instance, I guess. Because, like, we know for the manual trigger that you get the primary key of an article. Right? That's what you would trigger with. So when you hit a play button, I imagine that we would have to show some sort of modal or a dialogue that allows you to put in a JSON object, I guess, with what that sort of, fake data looks like. And for some of the triggers, I think we can prepare that cause we can we can pre fill, you know, a fake article manual run cause we know the format, collection, keys, etcetera. For some of them, you don't need to do anything at all. Like a cron thing, you can just play because there's no real, you know, trigger data coming in, maybe a timestamp, but I'm not a 100% sure. For an endpoint, though, there there's no way for us to know what that looks like. Right? Because the payload is user edit, so we don't really know do with that. So so some of the things I've seen with other tools, then this goes this even goes back 20 plus years ago using WebLogic's BEA workflow engines. You could actually take data from previous runs, so from the logs essentially. You could actually generate your payloads dynamically from that, or have say, I wanna capture this data, and I wanna actually reuse that as the test mechanism and essentially create your test data from an existing run. And then, ultimately, you could even edit at that point, you could edit the payload. So if you wanted to change some of the data attribution for subsequent runs, you could actually just quickly edit. So as part of the you know, that kind of play operation, you would actually get some dialogue capability and say, I wanna use either an existing or, in some way, create or save your payload and then fire that in. And in that way, you know, we don't have to be trying to generate it. We can just say, you can either run it normally, go and trigger the action. You know. Another way that I've done some of it is, like, have an incognito window with log you know, I I don't have to actually do what I just showed you, which is exit my flow. I can actually do the testing from another user. You can there there are some ways you can simplify or streamline that just a little bit. And you may be testing that permission set anyway. Then you may have permissions and restrictions, and you may be testing that as part of your application anyway. You'd have a test user with those permission. But being able to capture that information from an existing log or flow, run would be super cool and helpful. And that's some of what we've captured some of that already. We have the logs. I mean, we have all of that data to your point. Like, if if there was a previous run, we'd know exactly, you know, what what information was there anyway. So we could definitely use that to to prefill it. Is this sort of manual trigger for well, manual trigger is a bad bad name because we already have a thing called manual trigger. But it's this sort of play button. Let's call it that. Is that something you'd wanna do for the whole flow, like, on the trigger level, or is that something you'd also wanna be able to do per operation itself? Because somebody before was like, you know, I used the run script operation a lot. I can also imagine that it would be really useful then to just just be able to say, okay. Just replay my run script a couple of times. But what I was just about to say showed up in the chat. Shout out to the Dev once more. Operations depend on a lot of context from what happened before. Right? Yep. That would be more like that step through capability, right, where you you can step through and, you know, in debug mode, essentially pause. Right? The you know, you have your your break point. And at that point, you stop. You can actually analyze and look at the data. So, I mean, this does become very much ID style kind because that's what this really is. We're we're doing ID development. The idea is you would have these kinds of breakpoint capabilities to be able to stop, look at what data and parameters, be able to look back through the current context, what does it look like. Am I getting what I expect? Okay. Okay. Then step through debug. And it gets complex because we've got this mix of operations, and then you got run script that has, you know, code. Right? You know? Now do I have another debug capability with stop points and break points inside the job? You know? Yeah. Debugging that the TypeScript JavaScript in there directly. It gets very complex in that sense. But I think just the ability to be able to have a I wanna be able break point at the run script, be able to step to the next operation, then check the data and see I'll be able to multiple breakpoint options. We could get more creative down the line. I think if we can just put it at the operation level, that could it would be kinda cool if you could, you know, essentially step back, edit the data, or do something to the input, and then feed it back through, step back, step forward in in the operations themselves. Like, it's more complex than And another thing with all of this, of course, is that, these operations may very well have side of on purpose because you might ping a different endpoint. You might save something's database, whatever. Right? So if you're manually playing, you know, with Flow or even individual operations for debugging purposes or whatever else, those side effects will still be in effect. Right? And if you're trying to debug a whole flow, you know, operation 2 might be dependent on a successful response from operation number 1. Like, number 1, save something to the database. Number 2, reads it, you know, now that it has been saved or something. I'm just trying to come up with an example. Because I was thinking, you know, it it be important to think through some sort of dry run as well where you can just like, okay. This would have now saved to the database. Right? And then, oh, this would have now made a request to the API, but we didn't actually do that. We didn't want to make a Stripe subscription as part of our little testing thing right now, right, to just test if everything works. So that'll be interest interesting. Some sort of dry running, thing behind it. I I do think it's important to think through at least because of the side effects. But to again, shout out to the derp. Can't really do a universal drive run because you never know what, you know, arbitrary stuff is being used. Yeah. You'd almost have to have, like, a successful run or at least a set of data and payload options that you could edit up to say, you know, this is what I'm kind of expecting as I process through my input outputs. Mhmm. So So you'd have to have input output capabilities. What do you expect? And then as you say, dry run mode, not save, but often, you know, maybe the change that I'm making, I then getting that and then doing something. So I'm sending it to that I'm sending a webhook out data payload that comes back, you know, depending on how complex you make a flow. Mhmm. My general recommendation is if flows get that complex, go write a hook. But it's Well Well it it there is gonna be a point. It's a balancing act. Right? There there are I I like flows for admin kinds of functions where state management user control. When I get into really complex conditional lock, do doing this in these boxes so much faster to write TypeScript, be done with a hook on the backside. That's my Yeah. The the similar to yeah. What I wanted to say is this sounds, like, very similar to some type of bash script or something. You know, some some type of scripting, where as soon as you need conditions or something, it's probably better to make it a program. Because, who likes to write bash? Basically, you know, it it gets it gets gnarly really quickly. So as soon as you need sophisticated stuff, some type of conditions that branch into something and do something with arrays or something, it's probably better to actually use a programming language. And, I, yeah, I don't know. It really depends on a lot of things because one one is like, do you even know TypeScript or JavaScript in the first place? I think that's an important one. I think by, you know, doing it manually as as code, you also have a lot more opportunities to break it bad. You know, make it insecure, just make it an optimized break stuff tremendously hard. And also in, the chat somebody pointed out, you know, with flows, you do get the the logs and sort of the ability to explore the steps and what went wrong. So it's it's an interesting take, Jonathan, because what we're saying is, like, as as of today, you know, when it gets complicated, you wanna switch to code because the flows, UX, and UI just doesn't isn't good enough yet for complicated stuff. Right? So Right. Yeah. Yeah. It is. But I I'm hopeful. If if we make this kind of what we're talking about, if we get to that point in these next few iterations on maybe it does become I don't actually need hooks as often. Right? I could be you know, hooks could just be when I need external library. Yeah. Exactly. Allow within flow at the moment. I'm just I'm very I'm very hopeful that by by some of these seemingly small but very big changes, like adding the manual trigger so you can at least just try it out, I think we can actually get this to a point where HOOX is gonna be the more complicated one to do, you know, because it's like, yes, you have a black box and do whatever, but you don't get all of the debugging niceties and and all of the comments. Not that it's a battle. I mean, at the end of the day, it's just pick whatever you want, what's worked best for you. But I think that we can reverse that take, honestly, that flows could be I think that might be my personal challenge. Make flows the default for Jonathan. That's my measure of success. Writes the worst TypeScript he's ever seen. If he says I prefer flows over hooks, it means we did a good job. Perfect. I love it. Project Jonathan is what we call it. Cool. Okay. If you can keep me from writing code, it's always a good. It's always a good idea. So there's there's definitely a couple of unresolved You know, as per usual, I with the eye on the clock. As per usual, we're gonna be wrapping up. We've been taking a lot of notes during all of this, and it's, again, very divergently thinking. We're gonna be compiling all of these notes and ideas from everything that we just talked about and everything on the chat. Thank you for that. Into, you know, a proper RFC document and figure out what does this project upgrade flows, x y z, you know. What does that look like? What do we see as the must have? What can we get done in a sort of initial sprint? There have been, I think, like, 4 or 5 different discussions that are all sort of asking for the same thing, which is, like, improve Flows debugging. But they're all various sort of smaller bits and pieces of a, to me, bigger Flows 2 point o type of upgrade. So we're gonna be trying to compile those and sort of merge them into one sort of flows project, if that makes sense. Just as a quick summary, I think just the the upgrades to the logs that we talked about, you know, making them, like, explorable. I guess that's the right the right word. Giving them its own page, giving it more space, making it easier to step through, you know, the execution of a flow. Definitely a big must have. And the ability to trigger a flow from where you're editing it with some sort of, you know, preloaded payload from a previous run or something you can manually adjust so you can actually try out what the hell it is that you're building without having to go all over the app. I think it's gonna be a very, very important thing to have. Last question from from the chat here before we wrap up. Someone had said that many of these ideas could be implemented by the community in a custom module? No. Yes. Community can build whatever they want as a custom module at the end of the day. That is very true. And with the, you know, upcoming release of, for marketplace project, I'm also very curious to see, you know, what people will do with this type of stuff. Right? Somebody could, definitely, make a flow flows log module and just make it super custom or super proprietary or super flexible, whatever they want Whatever they want, really. Cool. Okay. There was one more that snuck in with an upvote. One final thing. Can we have access to the flow manager from the flows service so we can do more things with flows from extensions? May may may. No problems. So so It depends. It depends. Now I'll I'll, it's oddly tucked away compared to the extension manager, which should also similarly be tucked away, actually. Anyways, no. The the the reason for the hesitation is more around updates that I kinda wanna do to those services in the first place. Right? So I know I wanna do some sweeping upgrades to what those services look like and how they work in the first place. So I'm hesitant to to add new stuff onto the services service that we know might change already. Right? So I I don't wanna introduce something new that's gonna change then fast after, if that makes sense. That being said, yes, I do wanna make sure that extensions do have access to all of those types of internal things, which is also a reason why we've been moving a lot more stuff into individual libraries. Right? So I don't know. For for those who have been watching the repo like a hawk, we just moved sort of the environment variable extraction and handling different library with the same sort of idea, right, where an extension can just import the Directus ENV library and then use the same sort of ENV handling that, Directus does itself. We're we're basically doing that across the board, and the services is one of those things that I wanna do like that. And I can definitely imagine there being eventually being, you know, a Directus flows library that contains, you know, this flows manager state, so that extensions just can import it just like the direct as API would with TypeScript typing. I I heard you. Community, I heard you. With proper TypeScript typing and everything as well. So that's that's the only reason why I'm a little hesitant to promise anything better, as of right now. Any cool. That marks 11 sharp on the clock on my end. I think this was a very interesting session so far. This is a little bit more divergently rambling, than doing an existing discussion, but I think we've touched on a couple of very, very important points. Any any other closing thoughts? If I could remember the dad joke from Bryant this morning, I would tell it, but I suck at remembering jokes. So Well, tune in next time for for John's phone dad joke. With all that being said, thank you to the audience. Thank you for watching. If you're watching this on Directus TV, Directus dot a 0\u002Ftv, Hi. Be sure to watch the previous versions of this video down below. And we'll see you guys in 2 weeks, I think. Or are we skipping that one? Because it's sleep week, I think. Maybe oh, TBD. There will be one. I think our next one's in March, because the I think or no. No. It's actually the other way around. We have one more month. We're skipping the first one in March. That was the See you guys in 2 weeks then. I remember now. My poor old brain catching up. It's all in the event tab. Exactly. Keep your eye on it. It's all in the event tab. Thanks, everybody. Kevin's keeping us honest. Cheers, everyone. Bye. Bye bye.",[4632,4633,4634],"adfbc5b5-8825-4bfc-91b7-2702779b8af9","e9fd3c4b-6d03-4791-b250-59c07d49f7a4","6cf228e3-12c4-472f-bc3a-a9cfe6f16cb3",[],{"year":978,"number":11,"id":1787,"episodes":4637,"show":4638},[1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1768,1800],{"title":1802,"slug":1803},{"id":3957,"slug":4640,"vimeo_id":4641,"description":4642,"tile":4643,"length":527,"resources":12,"people":4644,"episode_number":177,"published":4646,"title":4647,"video_transcript_html":4648,"video_transcript_text":4649,"content":12,"seo":12,"status":19,"episode_people":4650,"recommendations":4652,"season":4653},"astro","912196620","In this video, you'll learn how to use the Directus SDK with an Astro application.","6f358086-b9de-43fe-a57a-1bab749d0924",[4645],{"name":2820,"url":2821},"2024-02-14","Get Started with Directus and Astro","\u003Cp>Speaker 0: This is a new ASTRO project created using the ASTRO CLI. In this video, I'm gonna show you how to use the Directus SDK inside of this project. Before we begin, be sure to actually install the Directus SDK like so. And once you've done that, you can run the Astro development server by running npm run dev. We want to create an instance of the Directus SDK that we can use across pages.\u003C\u002Fp>\u003Cp>And to do that, we're going to create a helper library. Inside of the source directory, create a new directory called lib, and inside of that, a new file called directus.js. To initialize the client, import the create directus and rest functions. Use the create directus function with your full directors project URL and with the rest composable, and export the newly created directors client from this file. We can then later import this file and use the same client across pages.\u003C\u002Fp>\u003Cp>Here in my existing directors project, I've already created a collection called global which contains a title and a description field. And in this project's access control settings, I've ensured that the public role has read permissions over all of the user created collections and, additionally, the director's files collection. I have replaced the page's index dot astro file with the file. I have imported the default layout that came with this astro project, our initialized directors client, the read singleton function from the Directus SDK, and then I've used the SDK to query the global collection. The global collection is now available.\u003C\u002Fp>\u003Cp>The data that's returned is now available in this global variable, which we are then rendering into the page as global dot title, global dot description. And the layout that we have here also expects a title prop. So we're also being sure to provide that in here when we use the layout tag. Over in our directors project, we've also set up a collection called pages. And each page has 3 fields, the slug.\u003C\u002Fp>\u003Cp>And this is meant to correlate 1 to 1 with the URL being used. So if we go to slash about, this is the page we expect to load, a title and content. Underlying this WYSIWYG editor here is just HTML code like so. Inside of the pages directory, create a new file called slug.astro, where slug is surrounded by square brackets, which denotes the fact that it is a dynamic route. Because all routes must be determined at build time in Astro, a dynamic route must export a get static paths function, which in turn will return an array of params and props.\u003C\u002Fp>\u003Cp>Params being the actual page itself, which will feed this dynamic value here in the file name and props to pass the entire object to the file so we can render values from it. So once we read the page's data from Directus, we then map the values into the shape required, which is an array of objects with the params, that's required, and props, which strictly is optional, but we're going to use it here. And then each page itself will feed in the page value here into astro.props. Then inside of the layout, we're passing up a title again. We are displaying the title, and we are using set HTML to take the HTML in that WYSIWYG field and render it directly.\u003C\u002Fp>\u003Cp>It's fine, but just bear that in mind going forward. In our director's project, we also have a collection called posts. Just like pages, they also have a slug, a title, and a WYSIWYG content field. But they additionally have an image field, and this is actually a relational field to the director's files collection, a publish date, and an author. This is also a relational field to the author's collection.\u003C\u002Fp>\u003Cp>Authors at this time just have a name, so I can show you how grabbing relational data works. Inside of our pages directory, create a new directory called blog. And inside of that, a new file called index dot astro. This is the file that will be loaded when a user goes to slash blog. This page might look quite similar to ones we've already created, except this time, we are using the read items method from the Directus SDK.\u003C\u002Fp>\u003Cp>So we go ahead and read all of the items from the posts collection, but you'll notice there is this second object parameter, which includes some queries. Firstly, fields. Fields is an array that specifies exactly what fields we want to return in this request. We're using it for two reasons. Firstly, we specifically don't want to return all of the data, namely the content field.\u003C\u002Fp>\u003Cp>That is a huge amount of textual data that we're just not going to use in this page. So let's not return it and make our return data bigger than it has to be. Secondly, we want to access nested or relational data. We have an author field inside of our posts but this is name. So this is how we access that nested data directly within this request.\u003C\u002Fp>\u003Cp>We're also going to sort the results so we get the latest first. In order to create a list item for every one of the posts, we're going to take the post's data and map it and return a list item. Each list item will have a link to the blog post itself slash blog slash slug, which we'll create in a minute, and data around when it was published and who published it. Take note here that this is a nested object. So post dot publish date is a top level property, but post dot author dot name is nested further because it's a relational value.\u003C\u002Fp>\u003Cp>To create a dynamic blog post page for individual blog posts, I've created a slug dot astro file inside of this blog directory. Taking note again that there are square brackets because this is a dynamic route. And as with all dynamic routes in astro, we need to export a get static paths function, which will return an array, of items from Directus. But this time, just like before, we can't just return the array as it stands. We have to map that into an array of objects with a params value and a props value.\u003C\u002Fp>\u003Cp>Then one of these pages will be created for each one of those posts. And here inside the layout, we have the title and we have the content like before. Now just an additional note here, you'll notice there is an image this time. All images in Directus are available using the full Directus project URL slash assets slash image ID. And then here, we are not going to return the entire huge image, but we are resizing it on the fly to 500 pixels in size.\u003C\u002Fp>\u003Cp>We are also returning alt text, which is the description held in the file in direct Now just take a note here that there is also no slash. The Directus SDK will automatically append a slash to the URL that isn't provided at the time of creating the client. So there is a slash in here. So this is our full project URL slash asset asset slash ID with 500. So that is how you integrate Directus with your ASTRO project.\u003C\u002Fp>\u003Cp>I hope you found this interesting, and we'll see you in the next video.\u003C\u002Fp>","This is a new ASTRO project created using the ASTRO CLI. In this video, I'm gonna show you how to use the Directus SDK inside of this project. Before we begin, be sure to actually install the Directus SDK like so. And once you've done that, you can run the Astro development server by running npm run dev. We want to create an instance of the Directus SDK that we can use across pages. And to do that, we're going to create a helper library. Inside of the source directory, create a new directory called lib, and inside of that, a new file called directus.js. To initialize the client, import the create directus and rest functions. Use the create directus function with your full directors project URL and with the rest composable, and export the newly created directors client from this file. We can then later import this file and use the same client across pages. Here in my existing directors project, I've already created a collection called global which contains a title and a description field. And in this project's access control settings, I've ensured that the public role has read permissions over all of the user created collections and, additionally, the director's files collection. I have replaced the page's index dot astro file with the file. I have imported the default layout that came with this astro project, our initialized directors client, the read singleton function from the Directus SDK, and then I've used the SDK to query the global collection. The global collection is now available. The data that's returned is now available in this global variable, which we are then rendering into the page as global dot title, global dot description. And the layout that we have here also expects a title prop. So we're also being sure to provide that in here when we use the layout tag. Over in our directors project, we've also set up a collection called pages. And each page has 3 fields, the slug. And this is meant to correlate 1 to 1 with the URL being used. So if we go to slash about, this is the page we expect to load, a title and content. Underlying this WYSIWYG editor here is just HTML code like so. Inside of the pages directory, create a new file called slug.astro, where slug is surrounded by square brackets, which denotes the fact that it is a dynamic route. Because all routes must be determined at build time in Astro, a dynamic route must export a get static paths function, which in turn will return an array of params and props. Params being the actual page itself, which will feed this dynamic value here in the file name and props to pass the entire object to the file so we can render values from it. So once we read the page's data from Directus, we then map the values into the shape required, which is an array of objects with the params, that's required, and props, which strictly is optional, but we're going to use it here. And then each page itself will feed in the page value here into astro.props. Then inside of the layout, we're passing up a title again. We are displaying the title, and we are using set HTML to take the HTML in that WYSIWYG field and render it directly. It's fine, but just bear that in mind going forward. In our director's project, we also have a collection called posts. Just like pages, they also have a slug, a title, and a WYSIWYG content field. But they additionally have an image field, and this is actually a relational field to the director's files collection, a publish date, and an author. This is also a relational field to the author's collection. Authors at this time just have a name, so I can show you how grabbing relational data works. Inside of our pages directory, create a new directory called blog. And inside of that, a new file called index dot astro. This is the file that will be loaded when a user goes to slash blog. This page might look quite similar to ones we've already created, except this time, we are using the read items method from the Directus SDK. So we go ahead and read all of the items from the posts collection, but you'll notice there is this second object parameter, which includes some queries. Firstly, fields. Fields is an array that specifies exactly what fields we want to return in this request. We're using it for two reasons. Firstly, we specifically don't want to return all of the data, namely the content field. That is a huge amount of textual data that we're just not going to use in this page. So let's not return it and make our return data bigger than it has to be. Secondly, we want to access nested or relational data. We have an author field inside of our posts but this is name. So this is how we access that nested data directly within this request. We're also going to sort the results so we get the latest first. In order to create a list item for every one of the posts, we're going to take the post's data and map it and return a list item. Each list item will have a link to the blog post itself slash blog slash slug, which we'll create in a minute, and data around when it was published and who published it. Take note here that this is a nested object. So post dot publish date is a top level property, but post dot author dot name is nested further because it's a relational value. To create a dynamic blog post page for individual blog posts, I've created a slug dot astro file inside of this blog directory. Taking note again that there are square brackets because this is a dynamic route. And as with all dynamic routes in astro, we need to export a get static paths function, which will return an array, of items from Directus. But this time, just like before, we can't just return the array as it stands. We have to map that into an array of objects with a params value and a props value. Then one of these pages will be created for each one of those posts. And here inside the layout, we have the title and we have the content like before. Now just an additional note here, you'll notice there is an image this time. All images in Directus are available using the full Directus project URL slash assets slash image ID. And then here, we are not going to return the entire huge image, but we are resizing it on the fly to 500 pixels in size. We are also returning alt text, which is the description held in the file in direct Now just take a note here that there is also no slash. The Directus SDK will automatically append a slash to the URL that isn't provided at the time of creating the client. So there is a slash in here. So this is our full project URL slash asset asset slash ID with 500. So that is how you integrate Directus with your ASTRO project. I hope you found this interesting, and we'll see you in the next video.",[4651],"718b9029-76dd-4b7f-a19f-d9f6cfdc7dc9",[],{"year":978,"number":230,"id":3954,"episodes":4654,"show":4655},[3956,3957,3958,3959,3940],{"title":3961,"slug":3962},{"id":4657,"slug":4658,"vimeo_id":4659,"description":4660,"tile":4661,"length":4320,"resources":4662,"people":4666,"episode_number":213,"published":4671,"title":4672,"video_transcript_html":4673,"video_transcript_text":4674,"content":12,"seo":12,"status":19,"episode_people":4675,"recommendations":4678,"season":4679},"4c5904eb-cc3b-4c54-b0a2-701be5ada53d","submodules","893724683","Kevin is joined by Shelley to finally understand the cursed tech that is Git Submodules. Turns out it's not that difficult...","ae7abee7-806d-494d-bba2-429a0ee740d9",[4663],{"name":4664,"url":4665},"Using Git URLs for package installs","https:\u002F\u002Fdocs.npmjs.com\u002Fcli\u002Fv10\u002Fconfiguring-npm\u002Fpackage-json#github-urls",[4667,4670],{"name":4668,"url":4669},"Shelley Vohr","https:\u002F\u002Ftwitter.com\u002Fcodebytere",{"name":2820,"url":2821},"2024-02-13","Git Submodules with Shelley","\u003Cp>Speaker 0: Hello, and welcome to learning things I love to hate. In this show, I stop avoiding tech topics that I have been avoiding for one reason or another with the help of my friends and colleagues who know more about them than me. And today, my dear friend Shelly is joining me to talk about Git submodules. Shelly, would you like to introduce yourself?\u003C\u002Fp>\u003Cp>Speaker 1: Hello. Thank you for having me. I am very excited to help teach you about things that you historically love to hate.\u003C\u002Fp>\u003Cp>Speaker 0: Potentially in the future as well. We'll see.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, fair enough. I'm Shelley. I work on Electron at Microsoft, and we have to do a lot of gross stuff with Git. So, I am hopefully here to help talk about one of the more commonly considered gross things to do with Git Git submodules.\u003C\u002Fp>\u003Cp>Speaker 0: I'm glad we are setting the scene adequately. I normally start these by just giving a bit of context as to what I know or what I think I know. We can dismantle that if it's wrong. But just to kinda set the scene. So get sub modules.\u003C\u002Fp>\u003Cp>I have seen them a few times in my career, and I've literally never once in my life got them to act in a way that I think they should. My understanding is you can have 2 distinct Git repositories and you have, let's call it, the child and the parent for all intents and purposes. And Sure. You can kind of sim or it's almost like sim link the child one into the parent one. So it appears in this repo, but the actual content exists over here.\u003C\u002Fp>\u003Cp>There's some mechanism to sync them that never works. It confuses my command line. And the reason I have avoided them is, as I said, they just seem really complex. And the other part is I now at least at direct us, I'm working in a mono repo, one big repository with directories for get submodules is in, like, you know, that the value of get submodules is in, like, you know, share abstracting bits of your code out to share in multiple places. Monorepos may do the the same thing or have the same characteristics without this this intricate git bit.\u003C\u002Fp>\u003Cp>So that's what I think I know. We don't need to address those straightaway. But at some point over the time we speak, it'd be good. Can we start by talking a bit about what they are, why they exist? And maybe we'll go on from there?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So basically, you had kind of the rough gist of it right, which is that git module or git submodules are basically like a repository, like a git repository inside of another git repository. This basically allows you to keep, obviously, as you said, 1 git repo as, like a subdirectory of another one and then work on them functionally independently. The Git information or basically metadata about the child repository is tracked in a file in the parent repository called dotgitmodules, which keeps a track of the commit that you have checked out in the child repository and allows you to basically kinda keep those concerns separate. I guess kinda writ large my opinion about, I guess, Git and submodules in this department generally is that Git itself is a tool.\u003C\u002Fp>\u003Cp>So Yeah. I don't really think it's the case that, like, Git submodules are, like, the solution to a problem as opposed to essentially kind of being one of the many tools in your toolbox you can kind of potentially use depending on the structure or nature of your project to deal with potentially having, like, a a team that works on different things but wanna get work done in tandem or you need certain updates from whatever is being worked on in the submodule. But as kind of with many other things, there's situations where I think they'd be, like, okay fits and situations where they'd be not super great fits. Specifically, I think that they were, I mean, created originally for a couple reasons. I mean, I don't know the actual, like, Git history.\u003C\u002Fp>\u003Cp>I'm sure I can look through my horrible Git mailing list till I come up with this.\u003C\u002Fp>\u003Cp>Speaker 0: History lesson. All good.\u003C\u002Fp>\u003Cp>Speaker 1: But, yeah, primarily, I would say, like, they kinda serve to keep code separate, to let you develop code independently, and to potentially let you reuse chunks of logic between different robot stories.\u003C\u002Fp>\u003Cp>Speaker 0: Sure. Interesting. There's a few things he said there. One was, you know, it allows I've got I've got my pen now, so that was serious. A pen with a a literal bend in it.\u003C\u002Fp>\u003Cp>See that? These aren't meant to do that. Anyway, first thing you said is, like, yeah, teams can kind of work in tandem, but separately. I don't see why the standard get workflow wouldn't enable a very similar thing. I I now understand the case of the parent repository in the latest example I use I had didn't actually contain the files that contained, like I said, basically a similar reference to where the file actually is.\u003C\u002Fp>\u003Cp>And maybe that's a little bit funky for how, you know, get clients render sub modules where it feels like it's here, but it isn't really. It's just a reference to what's happening over there. You also mentioned checking out what's happening in the child repository. I think he has a piece of the workflow. I just didn't know.\u003C\u002Fp>\u003Cp>Maybe that's why I've struggled with it. Well, I update it over here, and now it isn't updated over here because I need to take some action in the parent. And the final thing you said was sharing logic, like sharing chunks of logic. And I I don't see why that has to be get submodules. But I guess that's if your application is spread over multiple repositories.\u003C\u002Fp>\u003Cp>That makes a bit more sense.\u003C\u002Fp>\u003Cp>Speaker 1: So, I mean, my kind of honest take is that I don't think it does. Like, I don't actually think that is the best way to maybe I'm kind of keep it going by a large opinion here, but I don't necessarily think in a lot of cases that is the best way to track separation of concerns Mhmm. Or kinda deal with that generally. One, I kind of think the biggest issue with them, which is kind of what you brought up at the beginning, is that they're a humongous foot gun. It's kind of hard for people, especially people that, like, aren't don't necessarily have, like, a super in-depth concept of, like, the or, like, a mental model of Git, how it works.\u003C\u002Fp>\u003Cp>Because one, you need to learn a couple new commands. You need to learn sub module init, sub module update, sub module status, whatever, what have you. But I think it's very easy to forget, for example, that changes you make in the submodule aren't necessarily tracked in the parent unless you're actually currently in that subdirectory. So I think it's pretty easy to be you know, make some changes in the the sub module and then go back up to the parent and then forget to commit the the metadata change. Because itself, it's it's a commit, but it's not a traditional commit in the sense that it's like a snapshot of the current working directory.\u003C\u002Fp>\u003Cp>It's basically like a a a pointer to, like, sub module metadata, essentially.\u003C\u002Fp>\u003Cp>Speaker 0: Interesting.\u003C\u002Fp>\u003Cp>Speaker 1: I also think it's pretty easy to end up getting in way over your head with complicated workflows around submodules. One example is that it's actually possible to have nested submodules. I don't know if you know that.\u003C\u002Fp>\u003Cp>Speaker 0: I wish I didn't, but now I do.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So you could have nested submodules. And so, basically, every time you check out a new project, you need to so also god. Lots of times here. So, basically, when you first clone a project, right, that has a sub module, you clone it down.\u003C\u002Fp>\u003Cp>You don't actually already have all the files that are contained in the sub module. When you first point it down, you'll end up with a directory where the sub module will live, but the directory itself will be empty. There is a way god. Let me hold on. Oh, yeah.\u003C\u002Fp>\u003Cp>Just recurse submodules. So when you originally clone a project, it's possible to pass a command to actually initialize the sub modules, so it's not really one that's most people know I would argue, which is dash dash, recurse submodules, I believe. And that'll kind of initialize the submodule. Hold on. Double check that.\u003C\u002Fp>\u003Cp>Speaker 0: But when you say initialize the sub module, you mean it will go and push those files down for you at the same time?\u003C\u002Fp>\u003Cp>Speaker 1: Yes. So essentially when yeah. Like I said, when you first clone it down, you end up with the directory where the sub module is, but the directory is empty. And if you're in a project and you haven't, like, looked at the if it's like a new project and you have you don't really think about looking for the git modules file, you might not actually be super aware that it's there that you do need to actually initialize the submodules. So that's also something that you as a project maintainer have to make super clear to people who are looking to contribute, which I don't think is all necessarily intuitive.\u003C\u002Fp>\u003Cp>I actually ran into this a while ago. I was trying to do a repro for a problem that I was working on at work for the project I maintain. And I didn't realize there was a submodule, and it kept, like, airing on missing files. And it took me, like, a good 5 minutes to be like, oh, I have to I have to initialize the sub module. And then it was fine, but, like, just as, like, a short term example, it just it's not super intuitive, and they didn't actually mention it.\u003C\u002Fp>\u003Cp>I know they were contributing that.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And so so just so I understand. So I'm gonna use parent and child terminology to talk about sub module versus the the repository which uses that sub module. Yeah. A sub module is just a git repository, a separate git repository.\u003C\u002Fp>\u003Cp>Yeah. When you clone a repository in the standard way, you end up with the directory where the sub module code will live, but it's empty. You need to run further commands in order to grab the latest version of what's happened over in that repository. But if you do work in the child, in the sub module, in the meantime, when you're in the parent repository, that that change isn't reflected. Even if you just pull for main, it isn't reflected.\u003C\u002Fp>\u003Cp>You need to actively and consciously go out and grab the latest status of the submodules, the latest commit, and bring them over.\u003C\u002Fp>\u003Cp>Speaker 1: So, yes yes yes and no. So, basically, the answers.\u003C\u002Fp>\u003Cp>Speaker 0: Let's start that way. Yes, but also\u003C\u002Fp>\u003Cp>Speaker 1: I love to answer things with yes and no. So, anyway, let's say, you know, in the the super super repository super directory, whatever Yeah. Yeah. So you'd pull down let's say you make a change to the subdirectory, and then you commit that and you update the metadata. Right?\u003C\u002Fp>\u003Cp>And then you push that as a commit. You've updated the commit that the child get repository points to, and then you've pulled that down. Mhmm. So you you have pulled down, like, a a sign pointing to the change. But in order to actually have that change reflected in your submodule, you'd have to then run git submodule update so that git then updates the reference that that submodule points to.\u003C\u002Fp>\u003Cp>No.\u003C\u002Fp>\u003Cp>Speaker 0: Hang on. We gotta go back. We gotta do that one again. Sorry. So alright.\u003C\u002Fp>\u003Cp>So I understand that to grab the latest code, there is a get some module update. But before that, I got a bit lost. So but but the reference inside of your super repository or parent repository, that doesn't change automatically. You've gotta do that too. There's almost like a 2 step thing.\u003C\u002Fp>\u003Cp>Speaker 1: Yes. Correct. Which is actually what makes it kinda hard. So okay, so let's say we're starting from scratch. Right?\u003C\u002Fp>\u003Cp>I have a repo. It has one sub module. And I'm I'm a new contributor. I haven't really worked with this repository before, so I clone it down. And let's say my, god, this is gonna be, like, pointless names time.\u003C\u002Fp>\u003Cp>My submodule's name is, like, bear paw. I don't know.\u003C\u002Fp>\u003Cp>Speaker 0: I don't\u003C\u002Fp>\u003Cp>Speaker 1: even know where I got that from.\u003C\u002Fp>\u003Cp>Speaker 0: Take it. It's it's I\u003C\u002Fp>\u003Cp>Speaker 1: don't know. So in my primary repository, bear bear Paul. Bear Paul is a bear.\u003C\u002Fp>\u003Cp>Speaker 0: But I got it. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: I'm following.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. We're getting there. Okay. So I've cloned down bear. And then in that repository, I have my metadata file, git modules, which has a pointer to the commit that I should check out in my sub module, bear paw.\u003C\u002Fp>\u003Cp>When I originally cloned that down, I'll have the directory bear paw present in the primary working tree, but it won't have anything in it.\u003C\u002Fp>\u003Cp>Speaker 0: Sure.\u003C\u002Fp>\u003Cp>Speaker 1: There's a couple ways that I could do that. When I first clone it down, I could run git clone dash dash recurse submodules. And that would actually initialize and set up the submodule from the get go. The other thing I could do potentially is I could clone it down like normal, and then the submodule wouldn't be initialized. And then I could just go ahead and run git sub module, update dash dash init or git put sub module init and then git sub module update.\u003C\u002Fp>\u003Cp>And then I would have the current state of my primary repository, and then I would have my submodule checked out to whatever the most recent reference is.\u003C\u002Fp>\u003Cp>Speaker 0: Most recent reference is. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Like like, the the commit in the sub module that I want to be on, essentially.\u003C\u002Fp>\u003Cp>Speaker 0: Got it. Interesting. This looks like a lot of work. Like, I'm really struggling to understand why\u003C\u002Fp>\u003Cp>Speaker 1: Genuinely, it kind of is.\u003C\u002Fp>\u003Cp>Speaker 0: Merits. Yeah. What the merits of this flow is. I mean, what are the other options when it comes to basically abstracting parts of logic out? Because that's really what this is the core of.\u003C\u002Fp>\u003Cp>And maybe functionally to have different repositories in, like, hosted services like GitHub. So you end up with different issues, different pull requests, different project boards. Maybe that's part of the value here as well. That was actually what when I joined directors, it isn't the case anymore, but that's that's what it was. We have the docs where a repo, and we had guides which our community could contribute to.\u003C\u002Fp>\u003Cp>We had those in a separate repo. So this community contributed guides, PRs, issues were separate. It didn't really make that much sense. We got rid of those and and, merged them just into one repo. And in the end, it all ended up in a mono in the mono repo anyway.\u003C\u002Fp>\u003Cp>What are the other ways to do it? Depending on what your application is, you could publish, you know, you could publish bits of reusable logic, you know, like a package like a standalone package, and you can pull that in, you could have a mono repo, that's a bit of a bigger choice. I just, I'm not sure. There's a lot of complexity, even when you said you can clone it down using this additional flag. No one's gonna do that, even if it was on the end of a command.\u003C\u002Fp>\u003Cp>Like, you really need to understand that this command is different from the norm to not just go on autopilot and get clone repo a repo URL.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Interesting. I mean, I think there's also, like there's a lot of ways to kind of get around having to use submodules in, like, with other things I think a lot of people don't know. So for example, there's actually a whole concept of git sub trees. It's been around since, like, 2012, I think.\u003C\u002Fp>\u003Cp>I don't remember which version of git that was, but you can do that. You can also I actually don't know if a lot of people know this, so, actually, feel free to tell me if you do is, you can point to just like a straight tarball in your package JSON. We've done that a ton of times actually in Electron before when we need a specific, basically, like, commit from a project upstream that we haven't managed to get into master yet as a pull request. And so we'll just kind of, like, point to the committed just like a raw tarball, and then update it again when the PR gets merged.\u003C\u002Fp>\u003Cp>Speaker 0: Interesting.\u003C\u002Fp>\u003Cp>Speaker 1: So you can you can you can basically find ways to, like, point to just, like, single commits without actually having to maintain a sub module in your own package. Yeah. But, I mean, like, I would say largely speaking, the biggest issue with submodules is that the barrier to entry, like, the learning curve for them is higher in some ways than I think the benefit you get out of them. Like, they're very complex on principle. Like I said, you have to know all the commands.\u003C\u002Fp>\u003Cp>Updating is pretty difficult in the sense that, obviously, if you really, like, understand and know the flow, then I think you can, you know, get the string of commands you have to write kinda down. But, like, conceptually, I think it's pretty easy to, like, again, foot gun yourself, by forgetting to commit in the, like or by committing in the sub module and then forgetting to change the metadata commit in the primary repository. And then you actually, like, reset again, and then you've blown it all away. Yeah. So I do\u003C\u002Fp>\u003Cp>Speaker 0: have having to do work in lots of repositories in order to I don't know. It just feels it's so interesting. Yeah, I think I think I think I'm ending in the same place I'm starting. But I mean, we're not at the end. Yeah.\u003C\u002Fp>\u003Cp>I mean, I think I'm likely going to end in the same place. I started with a greater understanding of what they are and how they work, which is great, you know, still fulfills the goal. Interesting. So the world of, excuse me, the world of Git sub modules, are there any like notable, like real use cases that you have seen where you're like, Yeah, get some modules for absolutely the right tool for this\u003C\u002Fp>\u003Cp>Speaker 1: problem? To be totally honest with you, not really. We actually had a multiyear actually, one other thing before I forget is that, you could also reference just a straight up Git URL in your package JSON.\u003C\u002Fp>\u003Cp>Speaker 0: Like, without\u003C\u002Fp>\u003Cp>Speaker 1: actually having to do a sub module.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, cool.\u003C\u002Fp>\u003Cp>Speaker 1: Like, there's really quite a lot of things you can do to, like, accomplish the, like, quick, like, specific hot fix. You need the dependency without actually having to maintain an entire sub module. You just you don't need to do that for large. Anyway, basically, we had probably, like, a year 6 months to a year long project in Electron to actually transition away from using submodules. We used to maintain, libchromium content, which is the prior name of basically, like, the content shell in chromium as a sub module to electron.\u003C\u002Fp>\u003Cp>We switched our entire build pipeline infrastructure essentially to use, g n, as opposed to, GIP, generate your projects, which is what we used before that. And it simplified our entire workflow, like, probably by tenfold because previously so, okay. Example is let's say that we are maintaining, 3 supportive lines in Electron, say, at the time, last time we used this, it was, like, I don't know, maybe 5, 6, and 7. Let's say 5, 6, and 7. Those are supported lines.\u003C\u002Fp>\u003Cp>Remember, each one of them has a different sub module, because each one of them so let's say there's a security issue. Right? And then we need to get that security issue back from the, sub module, so Chromium Chromium content shell. So from the content shell back into every supported version of electron. So that actually ends up being basically like a matrix because you need to update the submodule.\u003C\u002Fp>\u003Cp>You need to get all of those, like, releases out, and then you need to update in every single supported line the reference to that change in the sub module.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Interesting.\u003C\u002Fp>\u003Cp>Speaker 1: And so what we do now by, comparison is that we maintain our reference to the version of Chromium that we're dependent upon right now, in, like, a DEPS file. This is just the way g n works. And then instead, if we have to fix something really quickly, we either update the DEP or we add a patch really quickly. And it's basically removed this whole middle layer while accomplishing the exact same thing.\u003C\u002Fp>\u003Cp>Speaker 0: Interesting. It feels like, yeah, maybe in 2023 When we recording this, I'm not sure if we'll publish this in 2023. Oh, happy New Year. If not, in 2023. I am not sure what the case for GIP submodules are then really when there are these other approaches.\u003C\u002Fp>\u003Cp>As you said, there are these lighter weight solutions that perhaps lack the depth of complexity, for better and worse of git submodules. There are, you know, I would say, mono repos have become trendy again, and this also removes, a lot of the value perhaps seen by get sub modules. I keep going back though to is there value in having when hosted in platforms like GitHub is the value that they are treated as separate projects? Maybe for some projects, very high, you know, high engagement projects, maybe splitting those out properly is valuable over a monorepo or something like that?\u003C\u002Fp>\u003Cp>Speaker 1: So I think, like, this makes you sound like a a bit of a PM, so I'm sorry in advance. But, like\u003C\u002Fp>\u003Cp>Speaker 0: I I prepared myself.\u003C\u002Fp>\u003Cp>Speaker 1: But, like, generally, I think the question to ask whenever you're kind of encountering some kind of a complex, like a a build pipeline problem or a Git problem or, like, some similar vein, whatever, is what problem are you actually trying to solve? Yeah. Because for any given problem, like, you know, you have this giant box of tools, and there's a bunch of different tools you can deploy to solve this problem. And realistically speaking, it's most prudent to choose the approach that even if it, like, seems more, I don't know, lowbrow or, like, simple or whatever, is gonna be the most intuitive and cause you the fewest long term problems. Like, I'm very much in the camp that the easiest solution should be the best one in the sense that if the, like, best solution requires you, like, spending a bunch of time writing a tutorial to explain why the thing that everyone is inexplicably doing is wrong, then you've screwed up multiple layers behind that.\u003C\u002Fp>\u003Cp>Yeah. Yeah. Yeah. Interesting. Because, like, if you're having to tell everyone that the default thing that they think is the easiest to do that they're doing on their own is wrong, then, like, you're just failing to communicate and set up your problem in a way that the, like, simplest solution is the most effective.\u003C\u002Fp>\u003Cp>Yeah. If the simplest solution is bad, then, like, you should reorganize and, like, figure out how to communicate what the, like I think you know where I'm going with this.\u003C\u002Fp>\u003Cp>Speaker 0: Maybe maybe this is more of a relic of I won't say the relic of the past. I sound like an old man, but like, there are other really viable solutions that maybe weren't always apparent or not always available, at which point get sub modules were were the easiest, or were the best were the best for whatever heuristics you use to define the best. Maybe now that's less the case. How comfortable would you feel about showing me setting up a good sub module maybe?\u003C\u002Fp>\u003Cp>Speaker 1: Potentially? Let's see. Potentially?\u003C\u002Fp>\u003Cp>Speaker 0: We got tons of time. I feel like I've kinda get I I've got my head reasonably effectively around what they are, why I have screwed them up in the past or not been able to to work with them correctly, which was a lack of knowledge, but it is this kind of I always thought sub modules, if you work on them in the super repo, the parent repo is cool. You're just actually doing the work over here. No, you're not. No, you're not.\u003C\u002Fp>\u003Cp>There's extra steps. I also don't think I've ever encountered them where they were the right tool for the job, especially for community projects, right, where you need to also consider the barrier to entry.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I mean, fair enough. And kind of in line with what you said. I think the thing that I mentioned, I don't know how long ago, like, 5, 10 minutes or so ago, about the fact that you can use just, like, straight up get URLs, local paths, or tarballs to refer to dependencies in your package JSON, for\u003C\u002Fp>\u003Cp>Speaker 0: example. Exactly.\u003C\u002Fp>\u003Cp>Speaker 1: I'm assuming those people watching this are gonna be working in JavaScript. Probably. Okay. Did did not always exist. Get some materials predated, I think, several of those solutions.\u003C\u002Fp>\u003Cp>Speaker 0: Exactly. Exactly. And, again, what I've seen is the right way of doing things as well. I keep going back to monoreapers as an example because I work in one every day. They also solve a lot of these challenges.\u003C\u002Fp>\u003Cp>Yeah. Interesting. Although, again, I do see the value, whether it's submodules or it's, you know, directly referencing a a tarball or a commit. I do see the value in splitting work up over multiple repos for many reasons.\u003C\u002Fp>\u003Cp>Speaker 1: That's kind of a good example of just, like, kind of different ways that you can solve this problem. So, like, I've I think, like, originally, one of the things that I said is potential advantage of this is that you can keep these 2 projects in different Git repositories. But at this point, you know, in our year 2023, there's a lot of different ways to keep different projects and and different repositories and yet maintain those fairly fluidly between projects. Based on differences, a bunch of the different ways you could reference various packages and dependencies in your package JSON. One of which is I don't know if you all are familiar with the concept of, like, a commit ish.\u003C\u002Fp>\u003Cp>Speaker 0: I've never heard of this.\u003C\u002Fp>\u003Cp>Speaker 1: Oh. So it's basically\u003C\u002Fp>\u003Cp>Speaker 0: like sounds remarkably uncertain.\u003C\u002Fp>\u003Cp>Speaker 1: Here. I will actually just pull up the actual definition of them. So a commit ish is basically like a technically, it is a commit object or object that could be recursively dereferenced to a commit object. So you could have a commit, a tag object, a tag object that points to a tag, that points to a commit. Basically, something in, like, the vague shape of a commit.\u003C\u002Fp>\u003Cp>So that's why, for example, in the link that I sent, you can say your dependency is like mocha js\u002Fmocha, hash, and then just, like, a part of a. And, like, that's good enough.\u003C\u002Fp>\u003Cp>Speaker 0: That's hilarious.\u003C\u002Fp>\u003Cp>Speaker 1: Your packet of manager could then figure that out and be kind\u003C\u002Fp>\u003Cp>Speaker 0: of fun. Yeah. Yeah. Yeah. This is, this is a lot more complex than what I've worked with in the past.\u003C\u002Fp>\u003Cp>Also, a commit ish is hilarious.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I mean, there's really quite a lot of, like, really gross things you can do if you really want to. Like, I think probably one of the worst things that I've done in my, like, personal project career\u003C\u002Fp>\u003Cp>Speaker 0: I'm ready. I'm ready.\u003C\u002Fp>\u003Cp>Speaker 1: Was, are you familiar with Git patch package?\u003C\u002Fp>\u003Cp>Speaker 0: No.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, okay. So you can patch, node modules.\u003C\u002Fp>\u003Cp>Speaker 0: Okay.\u003C\u002Fp>\u003Cp>Speaker 1: And you basically keep the actual patch as a, like, commit file in your primary repository directory. But, if you really need to, like, quickly and dirtily change a package that you don't maintain a don't control Yeah. Then you can basically just, like, patch it locally in your own directory.\u003C\u002Fp>\u003Cp>Speaker 0: That's kinda cool. By the way, I never knew that existed. Because occasionally, you do need to do this. Ideally, the upstream dependency, the down the downstream dependency terminology. The downstream I think.\u003C\u002Fp>\u003Cp>Up sure.\u003C\u002Fp>\u003Cp>Speaker 1: Downstream would be things depending on you.\u003C\u002Fp>\u003Cp>Speaker 0: Got it. That's the upstream dependency. Authors will handle it. But yeah, you can't always and yeah. And obviously, you don't wanna do the work in your note modules folder, because you know, you often don't check those in.\u003C\u002Fp>\u003Cp>That's really interesting just as an aside.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It's it's gross, but it's, like, dirty and effective. But for example, similarly, now if you wanna solve that same problem in, like, the many tools same problem approach, then you could also forecast repository, and then make the change, push a commit, and then reference the commit as a, like, URL in your package JSON. Same thing.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That definitely sounds worse.\u003C\u002Fp>\u003Cp>Speaker 1: So I guess the difference, for example, would be, like, you know, in the problem solving vein. Okay. Let's say I have this opaque commit. If you're first cloning that down, you might not actually have any idea that I've made some kind of a weird change to the default thing that I'm depending upon. Whereas, for example, if you use patch package, then it's really obvious to anyone who's cloning down their repository and looking through the directory contents that I've made some kind of a change to a dependency.\u003C\u002Fp>\u003Cp>So it all kind of depends on, like, what so for example, in a personal project that I know no one else will ever touch until I'm dead, I would be much more willing to today. To do yeah. I'd be much more willing to do some, like, nasty, little commit that I've put into a fork or something because I don't really care that anyone else knows that I've done. So Whereas if it's something that someone else is working on, I'd probably do something more like patch package. And then in the patch description of whatever, you know, patch file is that I'm checking into my working tree, I'd probably include a link to some upstream PR to make it more obvious to people contributing to the project that I've, like, done something nasty to a dependency, and I'm trying to get it upstream to get rid of the badge.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Interesting. Excuse me. So Electron used to use GitLab modules. And you said that there was a long term project, to I don't know in the world of electron whether it counts as long term, but feels pretty long to remove git submodules.\u003C\u002Fp>\u003Cp>So what what did you replace that with?\u003C\u002Fp>\u003Cp>Speaker 1: We replaced that with a, with basically it's it's called g n. It was a successor to so basically, GN is a meta build system, essentially. We basically replace our build system, from JIP for, for example, Node still uses. Have you ever heard of Node JIP?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So a dot JIP file describes basically how a project is built, its dependencies, what sources and files are included, flags that should be built with, etcetera, etcetera. Basically, replace that with, GN, the system. So it used to be the case that you would clone Electron down, and then you would, run and, initialize the sub module. And then, basically, it made it really, really hard to do Chrome updates.\u003C\u002Fp>\u003Cp>It was during the area, the era where for a long time, Electron was quite behind, and we're, like, 10 or so versions behind.\u003C\u002Fp>\u003Cp>Speaker 0: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 1: Chrome proper or chromium proper. And now we're basically typically constantly, and I would actually really significantly give credit to the fact that we use this build system now or this meta build system as a way to do that because what we do now is we actually you actually don't clone down electron directly. You, essentially clone I'm trying to figure out, like, the best way to explain this. We actually, like, built our\u003C\u002Fp>\u003Cp>Speaker 0: own try\u003C\u002Fp>\u003Cp>Speaker 1: it, and we keep going around. I'll ask questions. Built our own, like, build tools thing. We have our own, like, tool wrapper for initializing and setting up Electron that we all built because we also we discovered at the point over the course of probably, like, a year that we all had our own really sorted set of scripts to do this in, like, all subtly different ways. We were like, we should make one toolkit.\u003C\u002Fp>\u003Cp>Speaker 0: Mhmm. Because\u003C\u002Fp>\u003Cp>Speaker 1: we're all just doing these same things and just, like, suddenly grocery ways. But basically now Electron is a directory inside of Chromium. That's how we Okay. Build it. So when you, like, are working inside electron, you're working inside or basically, what we've made is a new subdirectory or, like, not a subdirectory, like a new top level directory within Chromium.\u003C\u002Fp>\u003Cp>Speaker 0: Cool. So you gain access to, yeah, the the, as you say, the tip of tree.\u003C\u002Fp>\u003Cp>Speaker 1: Right. But also all those files are all around you. So if you wanna make a quick change in Chromium, in the past, it was submodules. So you couldn't, like, you couldn't do it really easily. It was miserable.\u003C\u002Fp>\u003Cp>Whereas now, if I wanna go change something in Chromium or, like, do a bunch of logging to, like, figure that out, then I can just, like, pop into one of those other files, but just just there I can access, and I can\u003C\u002Fp>\u003Cp>Speaker 0: just Yeah. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: I can make change, then I could read relevant, then my change is right there.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Interesting. Cool. Yeah. No.\u003C\u002Fp>\u003Cp>That makes a lot of sense.\u003C\u002Fp>\u003Cp>Speaker 1: And that has made our workflow so much less miserable.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, yeah. For sure. Some, some of these episodes have run a little bit longer, but I feel really, like, well equipped with knowledge I didn't have. Not not sure I'm gonna go running for a git sub module anytime soon, but I definitely understand them why I really, really didn't before. Given that we have come in a little under time, not that that is a problem at all, is there anything else you wanna talk about or share around Git submodules that viewers may find interesting?\u003C\u002Fp>\u003Cp>Speaker 1: Oh, that's a good question. About Git submodules in particular, other Git stuff?\u003C\u002Fp>\u003Cp>Speaker 0: Get get sub modules. Get stuff writ large. I feel pretty okay about. Even some more advanced workflows, I feel okay about. There was just this one area I carved out sub modules, which I have avoided like the plague because I've never had success working with them.\u003C\u002Fp>\u003Cp>And I could never work it out either.\u003C\u002Fp>\u003Cp>Speaker 1: So I got my take as an as an alias enjoyer within Git is that I think that there are certain ways that you can paper over the complex workflows to make it easier for people that haven't interacted with them too much. As someone that has a lot of good aliases, You can, for example, add some, you know, pre commit hooks and, you know, simple aliases that make it so that people who are contributing externally have a slightly easier time getting set up. For example, like, if I try to build, like, it would be fairly trivial in so for example, the project I mentioned very early on where I was like, it it failed because there were some missing files in the git sub module that I didn't know I had to initialize. I think it would be pretty trivial at the beginning of the build step to be like, did the files exist in this? Okay.\u003C\u002Fp>\u003Cp>Fail.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Exactly.\u003C\u002Fp>\u003Cp>Speaker 1: Like, or errors. Did you initialize the sub module? Yeah. Exactly. So I think that, like, as with many things, there's a lot of ways that you can, like, kind of get out ahead of the pitfalls of get sub modules to make it easier for people to interact with them if you're going to go that route.\u003C\u002Fp>\u003Cp>I do think it's a valid choice. Like, despite having, like, lightly lightly food, get sub modules as as a choice within projects, I do think that, like, there are certain projects where, like, that is an adequate solution. I don't think that I'd go so far to say that it's perhaps the best solution. But like I said, there's, you know, plenty of projects that would be like a fine choice.\u003C\u002Fp>\u003Cp>Speaker 0: Absolutely. Interesting. Cool. I think that might be bringing us to the end of our, of our learning session around git submodules. Anything else you want to share about you or projects you are working on?\u003C\u002Fp>\u003Cp>Speaker 1: I don't I don't think so, to be honest.\u003C\u002Fp>\u003Cp>Speaker 0: Fantastic. Easy easy segment that. Well, thank you so much, Shelley, for joining us on learning things I love to hate.\u003C\u002Fp>\u003Cp>Speaker 1: Thank you for having me.\u003C\u002Fp>\u003Cp>Speaker 0: No worries at all. And until next time, bye for now.\u003C\u002Fp>","Hello, and welcome to learning things I love to hate. In this show, I stop avoiding tech topics that I have been avoiding for one reason or another with the help of my friends and colleagues who know more about them than me. And today, my dear friend Shelly is joining me to talk about Git submodules. Shelly, would you like to introduce yourself? Hello. Thank you for having me. I am very excited to help teach you about things that you historically love to hate. Potentially in the future as well. We'll see. Oh, fair enough. I'm Shelley. I work on Electron at Microsoft, and we have to do a lot of gross stuff with Git. So, I am hopefully here to help talk about one of the more commonly considered gross things to do with Git Git submodules. I'm glad we are setting the scene adequately. I normally start these by just giving a bit of context as to what I know or what I think I know. We can dismantle that if it's wrong. But just to kinda set the scene. So get sub modules. I have seen them a few times in my career, and I've literally never once in my life got them to act in a way that I think they should. My understanding is you can have 2 distinct Git repositories and you have, let's call it, the child and the parent for all intents and purposes. And Sure. You can kind of sim or it's almost like sim link the child one into the parent one. So it appears in this repo, but the actual content exists over here. There's some mechanism to sync them that never works. It confuses my command line. And the reason I have avoided them is, as I said, they just seem really complex. And the other part is I now at least at direct us, I'm working in a mono repo, one big repository with directories for get submodules is in, like, you know, that the value of get submodules is in, like, you know, share abstracting bits of your code out to share in multiple places. Monorepos may do the the same thing or have the same characteristics without this this intricate git bit. So that's what I think I know. We don't need to address those straightaway. But at some point over the time we speak, it'd be good. Can we start by talking a bit about what they are, why they exist? And maybe we'll go on from there? Yeah. So basically, you had kind of the rough gist of it right, which is that git module or git submodules are basically like a repository, like a git repository inside of another git repository. This basically allows you to keep, obviously, as you said, 1 git repo as, like a subdirectory of another one and then work on them functionally independently. The Git information or basically metadata about the child repository is tracked in a file in the parent repository called dotgitmodules, which keeps a track of the commit that you have checked out in the child repository and allows you to basically kinda keep those concerns separate. I guess kinda writ large my opinion about, I guess, Git and submodules in this department generally is that Git itself is a tool. So Yeah. I don't really think it's the case that, like, Git submodules are, like, the solution to a problem as opposed to essentially kind of being one of the many tools in your toolbox you can kind of potentially use depending on the structure or nature of your project to deal with potentially having, like, a a team that works on different things but wanna get work done in tandem or you need certain updates from whatever is being worked on in the submodule. But as kind of with many other things, there's situations where I think they'd be, like, okay fits and situations where they'd be not super great fits. Specifically, I think that they were, I mean, created originally for a couple reasons. I mean, I don't know the actual, like, Git history. I'm sure I can look through my horrible Git mailing list till I come up with this. History lesson. All good. But, yeah, primarily, I would say, like, they kinda serve to keep code separate, to let you develop code independently, and to potentially let you reuse chunks of logic between different robot stories. Sure. Interesting. There's a few things he said there. One was, you know, it allows I've got I've got my pen now, so that was serious. A pen with a a literal bend in it. See that? These aren't meant to do that. Anyway, first thing you said is, like, yeah, teams can kind of work in tandem, but separately. I don't see why the standard get workflow wouldn't enable a very similar thing. I I now understand the case of the parent repository in the latest example I use I had didn't actually contain the files that contained, like I said, basically a similar reference to where the file actually is. And maybe that's a little bit funky for how, you know, get clients render sub modules where it feels like it's here, but it isn't really. It's just a reference to what's happening over there. You also mentioned checking out what's happening in the child repository. I think he has a piece of the workflow. I just didn't know. Maybe that's why I've struggled with it. Well, I update it over here, and now it isn't updated over here because I need to take some action in the parent. And the final thing you said was sharing logic, like sharing chunks of logic. And I I don't see why that has to be get submodules. But I guess that's if your application is spread over multiple repositories. That makes a bit more sense. So, I mean, my kind of honest take is that I don't think it does. Like, I don't actually think that is the best way to maybe I'm kind of keep it going by a large opinion here, but I don't necessarily think in a lot of cases that is the best way to track separation of concerns Mhmm. Or kinda deal with that generally. One, I kind of think the biggest issue with them, which is kind of what you brought up at the beginning, is that they're a humongous foot gun. It's kind of hard for people, especially people that, like, aren't don't necessarily have, like, a super in-depth concept of, like, the or, like, a mental model of Git, how it works. Because one, you need to learn a couple new commands. You need to learn sub module init, sub module update, sub module status, whatever, what have you. But I think it's very easy to forget, for example, that changes you make in the submodule aren't necessarily tracked in the parent unless you're actually currently in that subdirectory. So I think it's pretty easy to be you know, make some changes in the the sub module and then go back up to the parent and then forget to commit the the metadata change. Because itself, it's it's a commit, but it's not a traditional commit in the sense that it's like a snapshot of the current working directory. It's basically like a a a pointer to, like, sub module metadata, essentially. Interesting. I also think it's pretty easy to end up getting in way over your head with complicated workflows around submodules. One example is that it's actually possible to have nested submodules. I don't know if you know that. I wish I didn't, but now I do. Yeah. So you could have nested submodules. And so, basically, every time you check out a new project, you need to so also god. Lots of times here. So, basically, when you first clone a project, right, that has a sub module, you clone it down. You don't actually already have all the files that are contained in the sub module. When you first point it down, you'll end up with a directory where the sub module will live, but the directory itself will be empty. There is a way god. Let me hold on. Oh, yeah. Just recurse submodules. So when you originally clone a project, it's possible to pass a command to actually initialize the sub modules, so it's not really one that's most people know I would argue, which is dash dash, recurse submodules, I believe. And that'll kind of initialize the submodule. Hold on. Double check that. But when you say initialize the sub module, you mean it will go and push those files down for you at the same time? Yes. So essentially when yeah. Like I said, when you first clone it down, you end up with the directory where the sub module is, but the directory is empty. And if you're in a project and you haven't, like, looked at the if it's like a new project and you have you don't really think about looking for the git modules file, you might not actually be super aware that it's there that you do need to actually initialize the submodules. So that's also something that you as a project maintainer have to make super clear to people who are looking to contribute, which I don't think is all necessarily intuitive. I actually ran into this a while ago. I was trying to do a repro for a problem that I was working on at work for the project I maintain. And I didn't realize there was a submodule, and it kept, like, airing on missing files. And it took me, like, a good 5 minutes to be like, oh, I have to I have to initialize the sub module. And then it was fine, but, like, just as, like, a short term example, it just it's not super intuitive, and they didn't actually mention it. I know they were contributing that. Yeah. And so so just so I understand. So I'm gonna use parent and child terminology to talk about sub module versus the the repository which uses that sub module. Yeah. A sub module is just a git repository, a separate git repository. Yeah. When you clone a repository in the standard way, you end up with the directory where the sub module code will live, but it's empty. You need to run further commands in order to grab the latest version of what's happened over in that repository. But if you do work in the child, in the sub module, in the meantime, when you're in the parent repository, that that change isn't reflected. Even if you just pull for main, it isn't reflected. You need to actively and consciously go out and grab the latest status of the submodules, the latest commit, and bring them over. So, yes yes yes and no. So, basically, the answers. Let's start that way. Yes, but also I love to answer things with yes and no. So, anyway, let's say, you know, in the the super super repository super directory, whatever Yeah. Yeah. So you'd pull down let's say you make a change to the subdirectory, and then you commit that and you update the metadata. Right? And then you push that as a commit. You've updated the commit that the child get repository points to, and then you've pulled that down. Mhmm. So you you have pulled down, like, a a sign pointing to the change. But in order to actually have that change reflected in your submodule, you'd have to then run git submodule update so that git then updates the reference that that submodule points to. No. Hang on. We gotta go back. We gotta do that one again. Sorry. So alright. So I understand that to grab the latest code, there is a get some module update. But before that, I got a bit lost. So but but the reference inside of your super repository or parent repository, that doesn't change automatically. You've gotta do that too. There's almost like a 2 step thing. Yes. Correct. Which is actually what makes it kinda hard. So okay, so let's say we're starting from scratch. Right? I have a repo. It has one sub module. And I'm I'm a new contributor. I haven't really worked with this repository before, so I clone it down. And let's say my, god, this is gonna be, like, pointless names time. My submodule's name is, like, bear paw. I don't know. I don't even know where I got that from. Take it. It's it's I don't know. So in my primary repository, bear bear Paul. Bear Paul is a bear. But I got it. Yeah. Yeah. I'm following. Yeah. Okay. We're getting there. Okay. So I've cloned down bear. And then in that repository, I have my metadata file, git modules, which has a pointer to the commit that I should check out in my sub module, bear paw. When I originally cloned that down, I'll have the directory bear paw present in the primary working tree, but it won't have anything in it. Sure. There's a couple ways that I could do that. When I first clone it down, I could run git clone dash dash recurse submodules. And that would actually initialize and set up the submodule from the get go. The other thing I could do potentially is I could clone it down like normal, and then the submodule wouldn't be initialized. And then I could just go ahead and run git sub module, update dash dash init or git put sub module init and then git sub module update. And then I would have the current state of my primary repository, and then I would have my submodule checked out to whatever the most recent reference is. Most recent reference is. Okay. Like like, the the commit in the sub module that I want to be on, essentially. Got it. Interesting. This looks like a lot of work. Like, I'm really struggling to understand why Genuinely, it kind of is. Merits. Yeah. What the merits of this flow is. I mean, what are the other options when it comes to basically abstracting parts of logic out? Because that's really what this is the core of. And maybe functionally to have different repositories in, like, hosted services like GitHub. So you end up with different issues, different pull requests, different project boards. Maybe that's part of the value here as well. That was actually what when I joined directors, it isn't the case anymore, but that's that's what it was. We have the docs where a repo, and we had guides which our community could contribute to. We had those in a separate repo. So this community contributed guides, PRs, issues were separate. It didn't really make that much sense. We got rid of those and and, merged them just into one repo. And in the end, it all ended up in a mono in the mono repo anyway. What are the other ways to do it? Depending on what your application is, you could publish, you know, you could publish bits of reusable logic, you know, like a package like a standalone package, and you can pull that in, you could have a mono repo, that's a bit of a bigger choice. I just, I'm not sure. There's a lot of complexity, even when you said you can clone it down using this additional flag. No one's gonna do that, even if it was on the end of a command. Like, you really need to understand that this command is different from the norm to not just go on autopilot and get clone repo a repo URL. Yeah. Interesting. I mean, I think there's also, like there's a lot of ways to kind of get around having to use submodules in, like, with other things I think a lot of people don't know. So for example, there's actually a whole concept of git sub trees. It's been around since, like, 2012, I think. I don't remember which version of git that was, but you can do that. You can also I actually don't know if a lot of people know this, so, actually, feel free to tell me if you do is, you can point to just like a straight tarball in your package JSON. We've done that a ton of times actually in Electron before when we need a specific, basically, like, commit from a project upstream that we haven't managed to get into master yet as a pull request. And so we'll just kind of, like, point to the committed just like a raw tarball, and then update it again when the PR gets merged. Interesting. So you can you can you can basically find ways to, like, point to just, like, single commits without actually having to maintain a sub module in your own package. Yeah. But, I mean, like, I would say largely speaking, the biggest issue with submodules is that the barrier to entry, like, the learning curve for them is higher in some ways than I think the benefit you get out of them. Like, they're very complex on principle. Like I said, you have to know all the commands. Updating is pretty difficult in the sense that, obviously, if you really, like, understand and know the flow, then I think you can, you know, get the string of commands you have to write kinda down. But, like, conceptually, I think it's pretty easy to, like, again, foot gun yourself, by forgetting to commit in the, like or by committing in the sub module and then forgetting to change the metadata commit in the primary repository. And then you actually, like, reset again, and then you've blown it all away. Yeah. So I do have having to do work in lots of repositories in order to I don't know. It just feels it's so interesting. Yeah, I think I think I think I'm ending in the same place I'm starting. But I mean, we're not at the end. Yeah. I mean, I think I'm likely going to end in the same place. I started with a greater understanding of what they are and how they work, which is great, you know, still fulfills the goal. Interesting. So the world of, excuse me, the world of Git sub modules, are there any like notable, like real use cases that you have seen where you're like, Yeah, get some modules for absolutely the right tool for this problem? To be totally honest with you, not really. We actually had a multiyear actually, one other thing before I forget is that, you could also reference just a straight up Git URL in your package JSON. Like, without actually having to do a sub module. Oh, cool. Like, there's really quite a lot of things you can do to, like, accomplish the, like, quick, like, specific hot fix. You need the dependency without actually having to maintain an entire sub module. You just you don't need to do that for large. Anyway, basically, we had probably, like, a year 6 months to a year long project in Electron to actually transition away from using submodules. We used to maintain, libchromium content, which is the prior name of basically, like, the content shell in chromium as a sub module to electron. We switched our entire build pipeline infrastructure essentially to use, g n, as opposed to, GIP, generate your projects, which is what we used before that. And it simplified our entire workflow, like, probably by tenfold because previously so, okay. Example is let's say that we are maintaining, 3 supportive lines in Electron, say, at the time, last time we used this, it was, like, I don't know, maybe 5, 6, and 7. Let's say 5, 6, and 7. Those are supported lines. Remember, each one of them has a different sub module, because each one of them so let's say there's a security issue. Right? And then we need to get that security issue back from the, sub module, so Chromium Chromium content shell. So from the content shell back into every supported version of electron. So that actually ends up being basically like a matrix because you need to update the submodule. You need to get all of those, like, releases out, and then you need to update in every single supported line the reference to that change in the sub module. Yeah. Interesting. And so what we do now by, comparison is that we maintain our reference to the version of Chromium that we're dependent upon right now, in, like, a DEPS file. This is just the way g n works. And then instead, if we have to fix something really quickly, we either update the DEP or we add a patch really quickly. And it's basically removed this whole middle layer while accomplishing the exact same thing. Interesting. It feels like, yeah, maybe in 2023 When we recording this, I'm not sure if we'll publish this in 2023. Oh, happy New Year. If not, in 2023. I am not sure what the case for GIP submodules are then really when there are these other approaches. As you said, there are these lighter weight solutions that perhaps lack the depth of complexity, for better and worse of git submodules. There are, you know, I would say, mono repos have become trendy again, and this also removes, a lot of the value perhaps seen by get sub modules. I keep going back though to is there value in having when hosted in platforms like GitHub is the value that they are treated as separate projects? Maybe for some projects, very high, you know, high engagement projects, maybe splitting those out properly is valuable over a monorepo or something like that? So I think, like, this makes you sound like a a bit of a PM, so I'm sorry in advance. But, like I I prepared myself. But, like, generally, I think the question to ask whenever you're kind of encountering some kind of a complex, like a a build pipeline problem or a Git problem or, like, some similar vein, whatever, is what problem are you actually trying to solve? Yeah. Because for any given problem, like, you know, you have this giant box of tools, and there's a bunch of different tools you can deploy to solve this problem. And realistically speaking, it's most prudent to choose the approach that even if it, like, seems more, I don't know, lowbrow or, like, simple or whatever, is gonna be the most intuitive and cause you the fewest long term problems. Like, I'm very much in the camp that the easiest solution should be the best one in the sense that if the, like, best solution requires you, like, spending a bunch of time writing a tutorial to explain why the thing that everyone is inexplicably doing is wrong, then you've screwed up multiple layers behind that. Yeah. Yeah. Yeah. Interesting. Because, like, if you're having to tell everyone that the default thing that they think is the easiest to do that they're doing on their own is wrong, then, like, you're just failing to communicate and set up your problem in a way that the, like, simplest solution is the most effective. Yeah. If the simplest solution is bad, then, like, you should reorganize and, like, figure out how to communicate what the, like I think you know where I'm going with this. Maybe maybe this is more of a relic of I won't say the relic of the past. I sound like an old man, but like, there are other really viable solutions that maybe weren't always apparent or not always available, at which point get sub modules were were the easiest, or were the best were the best for whatever heuristics you use to define the best. Maybe now that's less the case. How comfortable would you feel about showing me setting up a good sub module maybe? Potentially? Let's see. Potentially? We got tons of time. I feel like I've kinda get I I've got my head reasonably effectively around what they are, why I have screwed them up in the past or not been able to to work with them correctly, which was a lack of knowledge, but it is this kind of I always thought sub modules, if you work on them in the super repo, the parent repo is cool. You're just actually doing the work over here. No, you're not. No, you're not. There's extra steps. I also don't think I've ever encountered them where they were the right tool for the job, especially for community projects, right, where you need to also consider the barrier to entry. Yeah. I mean, fair enough. And kind of in line with what you said. I think the thing that I mentioned, I don't know how long ago, like, 5, 10 minutes or so ago, about the fact that you can use just, like, straight up get URLs, local paths, or tarballs to refer to dependencies in your package JSON, for example. Exactly. I'm assuming those people watching this are gonna be working in JavaScript. Probably. Okay. Did did not always exist. Get some materials predated, I think, several of those solutions. Exactly. Exactly. And, again, what I've seen is the right way of doing things as well. I keep going back to monoreapers as an example because I work in one every day. They also solve a lot of these challenges. Yeah. Interesting. Although, again, I do see the value, whether it's submodules or it's, you know, directly referencing a a tarball or a commit. I do see the value in splitting work up over multiple repos for many reasons. That's kind of a good example of just, like, kind of different ways that you can solve this problem. So, like, I've I think, like, originally, one of the things that I said is potential advantage of this is that you can keep these 2 projects in different Git repositories. But at this point, you know, in our year 2023, there's a lot of different ways to keep different projects and and different repositories and yet maintain those fairly fluidly between projects. Based on differences, a bunch of the different ways you could reference various packages and dependencies in your package JSON. One of which is I don't know if you all are familiar with the concept of, like, a commit ish. I've never heard of this. Oh. So it's basically like sounds remarkably uncertain. Here. I will actually just pull up the actual definition of them. So a commit ish is basically like a technically, it is a commit object or object that could be recursively dereferenced to a commit object. So you could have a commit, a tag object, a tag object that points to a tag, that points to a commit. Basically, something in, like, the vague shape of a commit. So that's why, for example, in the link that I sent, you can say your dependency is like mocha js\u002Fmocha, hash, and then just, like, a part of a. And, like, that's good enough. That's hilarious. Your packet of manager could then figure that out and be kind of fun. Yeah. Yeah. Yeah. This is, this is a lot more complex than what I've worked with in the past. Also, a commit ish is hilarious. Yeah. I mean, there's really quite a lot of, like, really gross things you can do if you really want to. Like, I think probably one of the worst things that I've done in my, like, personal project career I'm ready. I'm ready. Was, are you familiar with Git patch package? No. Oh, okay. So you can patch, node modules. Okay. And you basically keep the actual patch as a, like, commit file in your primary repository directory. But, if you really need to, like, quickly and dirtily change a package that you don't maintain a don't control Yeah. Then you can basically just, like, patch it locally in your own directory. That's kinda cool. By the way, I never knew that existed. Because occasionally, you do need to do this. Ideally, the upstream dependency, the down the downstream dependency terminology. The downstream I think. Up sure. Downstream would be things depending on you. Got it. That's the upstream dependency. Authors will handle it. But yeah, you can't always and yeah. And obviously, you don't wanna do the work in your note modules folder, because you know, you often don't check those in. That's really interesting just as an aside. Yeah. It's it's gross, but it's, like, dirty and effective. But for example, similarly, now if you wanna solve that same problem in, like, the many tools same problem approach, then you could also forecast repository, and then make the change, push a commit, and then reference the commit as a, like, URL in your package JSON. Same thing. Yeah. That definitely sounds worse. So I guess the difference, for example, would be, like, you know, in the problem solving vein. Okay. Let's say I have this opaque commit. If you're first cloning that down, you might not actually have any idea that I've made some kind of a weird change to the default thing that I'm depending upon. Whereas, for example, if you use patch package, then it's really obvious to anyone who's cloning down their repository and looking through the directory contents that I've made some kind of a change to a dependency. So it all kind of depends on, like, what so for example, in a personal project that I know no one else will ever touch until I'm dead, I would be much more willing to today. To do yeah. I'd be much more willing to do some, like, nasty, little commit that I've put into a fork or something because I don't really care that anyone else knows that I've done. So Whereas if it's something that someone else is working on, I'd probably do something more like patch package. And then in the patch description of whatever, you know, patch file is that I'm checking into my working tree, I'd probably include a link to some upstream PR to make it more obvious to people contributing to the project that I've, like, done something nasty to a dependency, and I'm trying to get it upstream to get rid of the badge. Yeah. Interesting. Excuse me. So Electron used to use GitLab modules. And you said that there was a long term project, to I don't know in the world of electron whether it counts as long term, but feels pretty long to remove git submodules. So what what did you replace that with? We replaced that with a, with basically it's it's called g n. It was a successor to so basically, GN is a meta build system, essentially. We basically replace our build system, from JIP for, for example, Node still uses. Have you ever heard of Node JIP? Yeah. Yeah. Yeah. So a dot JIP file describes basically how a project is built, its dependencies, what sources and files are included, flags that should be built with, etcetera, etcetera. Basically, replace that with, GN, the system. So it used to be the case that you would clone Electron down, and then you would, run and, initialize the sub module. And then, basically, it made it really, really hard to do Chrome updates. It was during the area, the era where for a long time, Electron was quite behind, and we're, like, 10 or so versions behind. Mhmm. Chrome proper or chromium proper. And now we're basically typically constantly, and I would actually really significantly give credit to the fact that we use this build system now or this meta build system as a way to do that because what we do now is we actually you actually don't clone down electron directly. You, essentially clone I'm trying to figure out, like, the best way to explain this. We actually, like, built our own try it, and we keep going around. I'll ask questions. Built our own, like, build tools thing. We have our own, like, tool wrapper for initializing and setting up Electron that we all built because we also we discovered at the point over the course of probably, like, a year that we all had our own really sorted set of scripts to do this in, like, all subtly different ways. We were like, we should make one toolkit. Mhmm. Because we're all just doing these same things and just, like, suddenly grocery ways. But basically now Electron is a directory inside of Chromium. That's how we Okay. Build it. So when you, like, are working inside electron, you're working inside or basically, what we've made is a new subdirectory or, like, not a subdirectory, like a new top level directory within Chromium. Cool. So you gain access to, yeah, the the, as you say, the tip of tree. Right. But also all those files are all around you. So if you wanna make a quick change in Chromium, in the past, it was submodules. So you couldn't, like, you couldn't do it really easily. It was miserable. Whereas now, if I wanna go change something in Chromium or, like, do a bunch of logging to, like, figure that out, then I can just, like, pop into one of those other files, but just just there I can access, and I can just Yeah. Yeah. Yeah. I can make change, then I could read relevant, then my change is right there. Yeah. Interesting. Cool. Yeah. No. That makes a lot of sense. And that has made our workflow so much less miserable. Oh, yeah. For sure. Some, some of these episodes have run a little bit longer, but I feel really, like, well equipped with knowledge I didn't have. Not not sure I'm gonna go running for a git sub module anytime soon, but I definitely understand them why I really, really didn't before. Given that we have come in a little under time, not that that is a problem at all, is there anything else you wanna talk about or share around Git submodules that viewers may find interesting? Oh, that's a good question. About Git submodules in particular, other Git stuff? Get get sub modules. Get stuff writ large. I feel pretty okay about. Even some more advanced workflows, I feel okay about. There was just this one area I carved out sub modules, which I have avoided like the plague because I've never had success working with them. And I could never work it out either. So I got my take as an as an alias enjoyer within Git is that I think that there are certain ways that you can paper over the complex workflows to make it easier for people that haven't interacted with them too much. As someone that has a lot of good aliases, You can, for example, add some, you know, pre commit hooks and, you know, simple aliases that make it so that people who are contributing externally have a slightly easier time getting set up. For example, like, if I try to build, like, it would be fairly trivial in so for example, the project I mentioned very early on where I was like, it it failed because there were some missing files in the git sub module that I didn't know I had to initialize. I think it would be pretty trivial at the beginning of the build step to be like, did the files exist in this? Okay. Fail. Yeah. Exactly. Like, or errors. Did you initialize the sub module? Yeah. Exactly. So I think that, like, as with many things, there's a lot of ways that you can, like, kind of get out ahead of the pitfalls of get sub modules to make it easier for people to interact with them if you're going to go that route. I do think it's a valid choice. Like, despite having, like, lightly lightly food, get sub modules as as a choice within projects, I do think that, like, there are certain projects where, like, that is an adequate solution. I don't think that I'd go so far to say that it's perhaps the best solution. But like I said, there's, you know, plenty of projects that would be like a fine choice. Absolutely. Interesting. Cool. I think that might be bringing us to the end of our, of our learning session around git submodules. Anything else you want to share about you or projects you are working on? I don't I don't think so, to be honest. Fantastic. Easy easy segment that. Well, thank you so much, Shelley, for joining us on learning things I love to hate. Thank you for having me. No worries at all. And until next time, bye for now.",[4676,4677],"fc8412be-6be3-4460-9e2b-2b77e0b4ec97","760d6edd-2cf1-4814-8fb2-cc842bf103fc",[],{"year":2380,"number":213,"id":4680,"episodes":4681,"show":4685},"bf1ea922-80fc-420e-ac6a-fa17d6a2ab3e",[4682,4683,4684,4657],"2d848504-0907-4315-b73e-94745042b868","120171fc-b80d-4940-ba66-a16ea67a1aba","91cbc0a4-a6a6-462f-8f80-768ab96db9d1",{"title":4686,"slug":4687},"Learning Things I Love To Hate","learning-things-i-love-to-hate",{"id":4173,"slug":4689,"vimeo_id":4690,"description":4691,"tile":4692,"length":1156,"resources":12,"people":4693,"episode_number":889,"published":4695,"title":4696,"video_transcript_html":4697,"video_transcript_text":4698,"content":12,"seo":12,"status":19,"episode_people":4699,"recommendations":4701,"season":4702},"airbnb","908297167","Can Bryant follow AirBnb's trajectory from scrappy startup to short term rental royalty – and build an AirBnb clone in one hour or less? Follow along as he attempts to builds a complete backend and frontend to manage rental listings, bookings, hosts, and more.","58537616-bf5c-4251-ae41-115fae3d13df",[4694],{"name":3590,"url":3591},"2024-02-12","Mission: Airbnb","\u003Cp>Speaker 0: Alright. Alright. Alright. Welcome back to the next episode of 100 Apps, 100 Hours, where we build or rebuild some of your favorite apps, or publicly fail trying. I'm your host Brian Gillespie, developer advocate at Directus.\u003C\u002Fp>\u003Cp>And today we have the mother of all clones, Airbnb. This was actually going to be one of the first episodes that I was going to record for this series. To be honest, I got a little scared. Airbnb has a ton of functionality on the front end. We have authentication, we have listings, you have 2 different types of users, and that you have folks who are listing their properties, folks who are booking those properties, you have messages, you have reviews.\u003C\u002Fp>\u003Cp>There's a ton of things just underneath the surface. So I'm curious to see how far we can get in an hour. I don't want to bite off more than I could chew, but, should be entertaining nonetheless. Alright. Alright.\u003C\u002Fp>\u003Cp>So if you've tuned into the series before, you'll know we have two rules. The first is you have 60 minutes to plan and build, or I have 60 minutes to plan and build, if you've got 60 minutes, if you're watching along, maybe you want to build with me. It's great. No more, no less, that's all we get. And then the second rule is kind of the anti rule.\u003C\u002Fp>\u003Cp>Use whatever you have at your disposal. AI, chat GPT, I've got a Nuxt front end application starter set up that already has some plumbing to connect to my Directus Instance, and I have a blank Directus project. That's what I've got. We're gonna try to build this Airbnb clone. Let's get started.\u003C\u002Fp>\u003Cp>Alright, so we'll start the clock here and away we go, right? So I'm going to pull up the Airbnb website, if I can actually figure out how to work my computer, and let's just poke around and see what we can find, right? One of the first things that I usually like to do when I'm trying to break down functionality for an app is take a look at the navigation, right? Airbnb has this concept of stays versus experiences now, so we have properties or listings or something like that. We have experiences which are, tours and tastings and and actual things that you can do.\u003C\u002Fp>\u003Cp>So that should be an interesting dynamic. I I think we'll mostly probably just stick to the properties part of it, like the listings. You know, one of the other things that I always like to take a look at, you know, I can go in and check the network requests, you know, and and try to suss out what some of the the data model looks like for all these different things that we're querying on the front end. So it looks like there is a very, very interesting, set of queries they're using. If I look into, like, the actual property values, we've got this very detailed data that we're returning and using potentially for our layout.\u003C\u002Fp>\u003Cp>I don't really know how helpful this is going to be, right? And to my knowledge, I don't know that Airtable, or I'm sorry, Airbnb has a public API that I can use to kind of introspect the schema, so basically I'm just playing it by ear here, right? So on every episode, before I actually build, I like to do a bit of planning. And first, let's start with like what type of functionality we're going to try to achieve out of this. I certainly want to get the back end data model built for this, we'll map that out in just a moment.\u003C\u002Fp>\u003Cp>On the front end maybe we want to display a listing detail page and maybe a listing index page. Let's call that our original goal. If we got like a stretch goal, maybe we want to let folks book booking pages, booking properties, sending messages, something like that. So I think these three pieces here will be plenty to chew on for an hour. Let's, let's try to map this application out.\u003C\u002Fp>\u003Cp>And I really like Figma, FigJam, the combo is great for mapping out things like this. We'll just do no fill. Alright, so as far as our data model, naming things is always tricky. So are we gonna call this Listings, is it Properties? That's gonna be our main collection, right, this is our main table.\u003C\u002Fp>\u003Cp>Not a 100% sold on what it's gonna be yet. We've probably got users in the system, so we'll have our users and Directus is going to actually take care of that for us with the Directus users collection that that it bootstraps whenever we actually create our instance. What else do we have? We've got, reviews that are going to be included and it can be attached to the individual listings. Looks like we have categories as well.\u003C\u002Fp>\u003Cp>Right? Tiny Homes, Countryside, I don't know if those are gonna be tags. I guess you could be a Countryside and a Tiny Home. So that could be like a mini to mini relationship. So let's just call those tags.\u003C\u002Fp>\u003Cp>Great. We could tag those onto a property. We have bookings. Alright. So you've got a calendar of bookings for a property or listing.\u003C\u002Fp>\u003Cp>We can make those little relationships. Just draw some nice looking arrows, basically. That's all we're doing here. Then we've probably got a junction table for that mini to mini relationship. Cool.\u003C\u002Fp>\u003Cp>Just drag that out of the way. We've got our reviews. Tags. Okay. Listing tags.\u003C\u002Fp>\u003Cp>Alright. What else? If we open up filters, we could see things like, the type of place, the price range, bedrooms, number of bedrooms, property type, the amenities. That's kind of a nice bit of functionality. How are we going to deal with that?\u003C\u002Fp>\u003Cp>What are the booking options? Host language. If we go to the actual detail page for a property, right, we've got a gallery of multiple images, we've got a host that could be our user, I guess. You know, we might have a like a host profile or something like that, that we attach so that we don't pollute our users collection. We've got a schedule, we've got the reviews, we've got a map, yeah.\u003C\u002Fp>\u003Cp>Okay. I feel pretty good about this. Let's just draw a couple more arrows, just for the sake of arrows. Let's actually start building this out. Cool.\u003C\u002Fp>\u003Cp>Alright, so I'm going to drag this over and as you can see over here on the left I've got my blank instance of Directus. What are we gonna call this main one? Properties to me as a developer, I'm thinking like properties of an object or something like that, so I'm I'm gonna call this listings. If anybody from Airbnb is watching this, hey, maybe tell me what you're what kind of schema you're using on the back end. All right, so I'm going to create a new collection called Listings.\u003C\u002Fp>\u003Cp>We're going to use the generated UUID. Is this a published listing? Yes, probably. So we'll keep status, we'll have the created on, updated on, user created, etcetera. And let's go through and start modeling this out.\u003C\u002Fp>\u003Cp>Alright. So if I look, we've got a h one, this is probably just the title of the listing. Great. What happens to our URL? It looks like they don't use fancy URLs or like a slug for this listing.\u003C\u002Fp>\u003Cp>It's just actually by the ID. And it looks like it's rooms. I could have just looked at the URL. I kinda like listings better just because they are using, they're listing whole houses on here as well, right, not just a room. So, cool.\u003C\u002Fp>\u003Cp>We will have a, this looks like it's calculated. What are the other things that we need? The number of guests, the number of bedrooms, the number of bathrooms, right, these are all going to be inputs. We can support how many guests, let's say max guests. That sounds like a good one.\u003C\u002Fp>\u003Cp>One of the other nice things here is the ability inside Us, if you're using it for your content management, you can add these helpful notes for your users. So how many guests total can stay at this property. And I can also translate these into other languages, which is really nice for our international users. Alright. That looks good.\u003C\u002Fp>\u003Cp>This will always be an integer. We don't support half people here at Airbnb, that's what we'll call this one. So max guess, we have number of bedrooms. Right? I can just duplicate that particular field and say bedrooms quantity, bedroom number of bedrooms.\u003C\u002Fp>\u003Cp>Like bedrooms quantity. Sounds great. I may go in and edit this individual note though. How many total bedrooms? Cool.\u003C\u002Fp>\u003Cp>We'll go through and let's do bathrooms quantity. Again, and naming is probably, I won't say it's half the battle, but it is a lot of the battle in development, is coming up with names that make sense and have meaning. So we've got our title, we've got our max guest, we've got our bedrooms, we'll have a host for that, so we'll deal with that in a moment. I can see we've got a nice description here. This looks like it supports WYSIWYG content, so we'll go ahead and add the WYSIWYG editor within Directus.\u003C\u002Fp>\u003Cp>We'll call this the Description. Could be the about section. I see about the space here. We'll save this. Great.\u003C\u002Fp>\u003Cp>And then we have this what this place offers. Right? So this is an interesting one where we've got items that are not included, we've got items that are included. There's a couple different ways we could actually manage this within our data model. We could, I like hard code this as like JSON values or something like that, where we have a list of these objects.\u003C\u002Fp>\u003Cp>We could also set it up as a separate relationship so you could add these features, or I'm assuming what we would call features to this particular listing. Maybe come back to that one. Let's keep rolling down. We have our reviews, we have the address, so we could have the street address. Let's go ahead and fill that up.\u003C\u002Fp>\u003Cp>Street address, we do this as street address 1, Street address too, just in case we need it. What else do we have? City, region, state. I think it's region if we're being international friendly outside of the US. And then we have a postal code.\u003C\u002Fp>\u003Cp>Great. Alright. Maybe we clean this up a little bit. I'm gonna make this half width. When I'm inputting these things I want to make it look really nice.\u003C\u002Fp>\u003Cp>I am going to go in and use the detail group within Directus just to get a nice visual. So we'll say detail group, let's call this Address Info, on the front end, whenever we call the API, we would either make sure we omit this address so nobody can find this property without actually booking it. We want to keep that private. Or we may not even, send that to the front end at all. But obviously you want to store that.\u003C\u002Fp>\u003Cp>And we could, you know, even potentially like obfuscate this somehow. So we've got a host. We'll come back to that. House rules. Let's take a look at this.\u003C\u002Fp>\u003Cp>Pets allowed, no smoking. Check-in, check-in, check out. Yeah. So we're gonna need details on check-in, check out. We've got safety and properties.\u003C\u002Fp>\u003Cp>We've got safety devices. We've got a cancellation policy. Before you book, partial refund. Okay. Yeah, let's tackle just like safety really quickly.\u003C\u002Fp>\u003Cp>So we'll go in. This seems something like pretty simple. Let's go in and this may be a good use case for our checkboxes inside Directus. So safety, what do they call these? Safety and property.\u003C\u002Fp>\u003Cp>Let's just call it safety devices. We'll say carbon monoxide. Then we have a smoke alarm installed. Great. And for the value, I can even change this to something like CO 2 or CO.\u003C\u002Fp>\u003Cp>Right? Carbon monoxide is single c0. Yeah. Let's just leave it there. Great.\u003C\u002Fp>\u003Cp>I'm trying to think of what other safety devices we'd have on a property. Alright? Let's just check out one of these other cabins. Scroll down to the bottom. Potential for noise, so there is some road noise.\u003C\u002Fp>\u003Cp>This is kind of like a extra metadata as well. Safety devices. Let's just finish this out. Great. We could do something interesting here where like property info is just text and an explanation.\u003C\u002Fp>\u003Cp>Right? Maybe we even have a potential icon for this. So let's use the repeater field inside Directus. Basically it just stores as JSON data, but I can create an array of objects here that can contain basically almost anything that I want. But again, it just gets stored as JSON within the database.\u003C\u002Fp>\u003Cp>So we'll call this property info, and for the fields, it looks like we've got an icon. Right, icon. We'll call the field icon, we have a string, that's the type of the field, and then we actually have an icon interface inside Directus where you can choose lovely icons from a drop down. Alright, next we'll have a label or a title for this. Let's just go with Label, that'll be a string as well.\u003C\u002Fp>\u003Cp>We'll use just a standard input interface and what's next? Right, we've got a description, label, description, close enough. And this we'll just make a text box. I don't think we need WYSIWYG input here, we'll just do text area. I said we didn't need it and then I clicked it, got a little click happy there.\u003C\u002Fp>\u003Cp>Great. Okay, so now we've got safety devices, we've got property info, we could even call this, we could add this in our detail group as well. We could call it safety property. Safety and property. Great.\u003C\u002Fp>\u003Cp>And we can even add a little safety sign. Great. Health and safety. Beautiful. So I'll just drag those within this and we are starting to build a great looking data model for this.\u003C\u002Fp>\u003Cp>Alright. Let's cover check-in, check out info. So we can use the date time field. Do we want I guess we just want time. Right?\u003C\u002Fp>\u003Cp>Check-in time. We will let's do that half width. We'll do checkout time. Cool. We've got those 2 items.\u003C\u002Fp>\u003Cp>Was there any other details on check-in? Self check-in with keypad. Maybe there's like a check-in type. Let's take a look at a couple other listings and see what we can find. Where did you go?\u003C\u002Fp>\u003Cp>There we go. Self check-in with a lockbox. Yeah. So this is probably just like some text. Check out type.\u003C\u002Fp>\u003Cp>Great. Again, if I wanted to group these together, I could add a nice little detail group. Check out, let's call it House Rules, right? House Rules. Great.\u003C\u002Fp>\u003Cp>We'll put that under house rules, check out, check-in, check out type. Okay. If I could actually click and drag the way I want. Let's, let's move that up. We've got the address info, we've got the house rules, we've got the bedrooms, max guests, pets allowed.\u003C\u002Fp>\u003Cp>That could be a Boolean attribute or a toggle here. It just stores this Boolean in the database. So our pets allowed, Default. Let's say no is the default. Pets allowed as the label.\u003C\u002Fp>\u003Cp>Great, great. Alright, we'll shrink this to half width. I'm getting really anxious to see what this actual form looks like once we get done mocking out our data model here. We've got quiet hours, right? So how do we adjust for this?\u003C\u002Fp>\u003Cp>Do we have a range of dates inside Directus? We do not. Quiet, so we could do something like quiet time start. Date time. Oh, nope.\u003C\u002Fp>\u003Cp>We just want time. Date, time, and our dates. We're gonna have to delete that one. Alright. Let's just go back.\u003C\u002Fp>\u003Cp>Let's try again. We just want the time, right. We're not really concerned with the date. Quiet time, stop quiet quiet time start. Quiet time in.\u003C\u002Fp>\u003Cp>Great. Isn't data modeling fun, guys? We'll put that in our house rules. Quiet time. Does anybody allow smoking in their properties?\u003C\u002Fp>\u003Cp>Do we even really need to concern ourselves with this? Right? Additional rules. This doesn't look like it actually supports WYSIWYG content. So we can add a new field for that.\u003C\u002Fp>\u003Cp>We'll just call it additional rules. We'll just call it a text area. That's gonna store as text in the actual database. And one of the important things to know, hey, as we're going through and modeling this data out, if I were to pull up this SQL database right now, and I could show you that in just a moment, Directus is actually going through and mirroring every change that I'm making here inside the Data Studio, inside our actual database as well. So we go to house rules, we'll just clear that up.\u003C\u002Fp>\u003Cp>Too many, There we go. Alright. I need to adjust the sensitivity on the mouse. But if I open up table plus and just to give you guys a preview of what's going on behind the scenes here. Uh-oh.\u003C\u002Fp>\u003Cp>What am I doing wrong? Connect to the database. Detour. Let's check my Docker Compose. 8 055.\u003C\u002Fp>\u003Cp>Those ports should be exposed. Oh, I see what the problem is. I've got, another instance of PostgreSQL running through Docker. All right, so if I open this up, you can see I've got a listings table here. And if I look at the structure, you can see we've got those same columns that we were mapping inside Directus, and you can see the data type is being appropriately applied.\u003C\u002Fp>\u003Cp>Likewise, if I were to add a column to the database here in TablePlus or via SQL, Directus would introspect those changes and actually show up inside the app itself. So it is a beautiful mirroring relationship. It's, I love it. It's one of my favorite features of Directus. I I know I say that a lot.\u003C\u002Fp>\u003Cp>What other things do we need to offer here? HDTV, Mountain View, Valley View. I I I don't know what this looks like behind the scenes when you're actually mapping these out. I would be curious to see. Free washer, free dryer in unit versus free washer, free dryer out of unit, outside of unit.\u003C\u002Fp>\u003Cp>Right, we've got a garden view. I'm assuming this is probably just like some type of JSON data or something that's being stored, maybe free washer end unit. Let's go back. Can we actually filter based on this data? Can you filter based on okay.\u003C\u002Fp>\u003Cp>Yeah. So so you can filter on some of this. It doesn't look like you could filter on all of it. Like, hey, does it have a in unit versus a a non unit? I'm trying to think of the best way to account for these.\u003C\u002Fp>\u003Cp>I'm not sure exactly how I want to do it. Right? So we've got this type of place that we might set up as well. Let's do that. We're gonna do the type of place and then we have our property type.\u003C\u002Fp>\u003Cp>Let's just call this one type. Okay. We'll make this a string and instead of using an input, let's use radio buttons. I think we just have a room or an entire home. Alright.\u003C\u002Fp>\u003Cp>Destinations, guests. You could filter a room or entire home, basically. Yeah. That that works for me. Alright.\u003C\u002Fp>\u003Cp>So we'll say this is a room. And one of the other cool little things that I'll show you while we're here, I can translate this content inside Directus. If I use a dollar sign t and use this key and I hit enter, I can go in and add translation strings for any of the languages, which is really nice so that, whoever is using this, Directus instance, if they are editing content and they go in and they happen to be French or Canadian. You guys speak a different language up there in Canada. Right?\u003C\u002Fp>\u003Cp>No kidding. And then we'll call this the entire home. But this translation feature is really nice. Only recently started digging into it. So the value we'll call entire home.\u003C\u002Fp>\u003Cp>And then if I want to, what I could do is go into our translations here inside the Directus Admin. I can create a custom translation, and I could say entire home, pick my English language. Great. Oh, you do have different English up there in Canada. And I could say I could call this Bryant's home if I wanted to.\u003C\u002Fp>\u003Cp>Right? It doesn't really matter, but if I were to go in and look at this data model now, right, for the type I can see it's a room or I can see it's Bryant's home. So that's really nice in that I can apply these translations for any of the languages that our our team may speak. Entire home, great. What do they say here?\u003C\u002Fp>\u003Cp>Room in a home. Alright. So if we create a new one for room, we'll go English US, a room and a home. Great. Alright.\u003C\u002Fp>\u003Cp>So we go back, we create a listing, now we can see we've got 2 options there. We've got an entire home, we've got a room and a home. Cool. Let's, let's continue finishing this up. Price ranges could be interesting, right?\u003C\u002Fp>\u003Cp>I'm trying to think of of how we would set this particular data up, because I'm imagining this varies based on, like, is it a Monday night versus a Friday night or versus a Sunday night? How many days that you're staying? Can we actually Airbnb your home? Blah blah blah. Airbnb setup.\u003C\u002Fp>\u003Cp>I guess I could log in to my account. Don't look at my passwords. Let's see what it looks like when we actually Airbnb at home. Tell us about your place. Start on your own.\u003C\u002Fp>\u003Cp>This seems to be a nice little onboarding flow as well. My house is a castle. Right? Great. Entire place, room, a shared room.\u003C\u002Fp>\u003Cp>Okay. So that's one that we forgot as well. Right? A room, and then we have shared room. So again, we'll do that dollar sign t and a colon that allows us to store that as a translatable string.\u003C\u002Fp>\u003Cp>Great. This is gonna be the entire place. Enter my address. I'm not gonna do that here. Let's do, it was Yankee Stadium.\u003C\u002Fp>\u003Cp>Right? Let's let's do the address for Yankee Stadium. Bronx. Great. Confirm the address.\u003C\u002Fp>\u003Cp>Is this PIN in the right spot? Do they have any validation on this? I guess they don't. Right? So I wonder if this goes up to, what, I don't know how many people, Yankee Stadium seats, but we'll call it 16 plus.\u003C\u002Fp>\u003Cp>Right? So we've got the number of guests, bedrooms, beds. There's something else that we need to add here. Right? Bed quantity.\u003C\u002Fp>\u003Cp>Great. Should've done this to begin with. Right? Let's add another group because this is gonna get messy. A lot of fields on this one and, you know, again, this is why I didn't want to bite off too much on this actual one.\u003C\u002Fp>\u003Cp>So we'll call this the, what do we call this? Basics listing info. Yeah. That's fine. We won't we won't get too carried away here.\u003C\u002Fp>\u003Cp>So we got the type, we got the listing info, just move that up. Max number of guests, bedrooms quantity, bathrooms quantity, are pets allowed. We'll hoist title and description up here. Let's say we've got 8 beds, 8 bedrooms, maybe 5 bedrooms. Again, no idea how many of those are inside Yankee Stadium.\u003C\u002Fp>\u003Cp>Make your place stand out. Okay. Let's look behind the scenes and see, is this making any calls here? We have parking, just using React. So I wonder Okay.\u003C\u002Fp>\u003Cp>If we amenities. Amenities. Can we see any of those here? I don't see them. I I I really don't wanna dig through each of these components here to try to figure that out.\u003C\u002Fp>\u003Cp>Can you actually do, like, the view thing in the React Dev Tools? I guess you can. Alright. So what's our consumer, provider, Value, History. Yeah.\u003C\u002Fp>\u003Cp>I'm not a not a React guy. Let's just not even bother diving through it. Right? There's our safety items, like first aid kits, smoke alarm, etcetera. Standout amenities.\u003C\u002Fp>\u003Cp>So these are just the basics, right? And you might even go through and add separate fields for for these things. So we could do again the detail group for this. Maybe this one starts closed. Call this amenities.\u003C\u002Fp>\u003Cp>These also could be like a separate database table, that we we add, and that way we could continue adding those. Yes, no, the other. Again, not knowing behind the scenes the importance of this, like maybe we add these as separate things so we could filter them. So let's go in and we'll add a new collection. We'll call it Amenities.\u003C\u002Fp>\u003Cp>Amenities. Probably need some spell checking there. Amenities. Cool. Alright.\u003C\u002Fp>\u003Cp>All of this, I'm not really concerned myself with. We have an icon for the amenity. Gotta have an icon. Very design oriented here myself. Recovering designer, as I like to refer to myself.\u003C\u002Fp>\u003Cp>We have a name for this amenity. Maybe we've got something like a key for it as well. I I I don't know. And then is there some type of explanation for these? Yeah.\u003C\u002Fp>\u003Cp>You may have something like a description for it as well. Cool. Good enough. Now what we're gonna do is link these 2 together, right? So inside Directus what I can do is basically create a many to many relationship, because one property could have many amenities and many amenities could be linked to many properties.\u003C\u002Fp>\u003Cp>I think that's a great explanation of amenities and many to many relationships. Directus makes this super simple to create these relationships. So on our listings collection, we'll just call this Amin I need to work on my spelling for sure. My wife works in the school system as well, so maybe she can give me some help. We don't wanna allow duplicates.\u003C\u002Fp>\u003Cp>We wanna to show a link to that specific item. If I open up advanced field creation mode I can actually control the name of this junction collection. That's going to create a junction table in our SQL database. I'm okay with this, right? And if I wanted to I could add a reverse many to many relationship, on the amenities collection back to the listings.\u003C\u002Fp>\u003Cp>What we may do is add a sort field here as well. For the display, we'll show related values. We can come back in and clean that up in just a moment. But, great, we've got our amenities here. Let's move that up to the top.\u003C\u002Fp>\u003Cp>What are the other things that we need to add? Some photos of the castle. You need 5 photos to get started. I'm feeling pretty confident here. I'm just going to close this out.\u003C\u002Fp>\u003Cp>Exit. We'll go back to the Airbnb website. And there was one thing that I forgot what I was gonna add here. Is, property type. That's what it was.\u003C\u002Fp>\u003Cp>So we'll go in, I'm just gonna duplicate this one called type and we'll add property type. Duplicate, that's of course gonna drop down here to the bottom and I'll just bring it back up. Property type, let's just clear out these values. So it probably didn't need to duplicate that. But we have a house.\u003C\u002Fp>\u003Cp>I'm not gonna mess with the translation strings here. Just know that's available to you if you are working with Directus. We'll call this an apartment. Apartment. Guest house.\u003C\u002Fp>\u003Cp>For those of you who do have a guest house, amazing. Take advantage of it. Exploit it on Airbnb. We have the hotel and that looks pretty good. Cool.\u003C\u002Fp>\u003Cp>At some point we'll probably have to deal with the pricing thing, but for now we'll just skip it. Alright, so I'm feeling pretty confident about this. Let's go in and flesh out the other items in our data model. We spend a ton of time data modeling this out, but there's a lot of fields on the listings. So we got reviews, we have tags.\u003C\u002Fp>\u003Cp>So let's create reviews. What does the review structure look like inside Airbnb? So we're certainly gonna want a user created. We'll probably have a status on the review. Hey, is this published or not?\u003C\u002Fp>\u003Cp>For the reviews, okay. It looks like we have an overall rating. We have cleanliness, accuracy, location, value. Alright. So I think we have a slider field that it could be a good representation for this.\u003C\u002Fp>\u003Cp>Let's call it overall rating. Great. We'll duplicate that one. We can call it cleanliness. Cleanliness?\u003C\u002Fp>\u003Cp>Yeah, I think so. Looks great. Let's duplicate this. We'll have the accuracy and so forth. I just missed that one, didn't I?\u003C\u002Fp>\u003Cp>Duplicate. Alright. So you kind of get the picture there. We have a user, we have some actual text. It doesn't look like, to me that doesn't look like WYSIWYG text.\u003C\u002Fp>\u003Cp>Not that it matters. The WYSIWYG and the text area interface both store the data as text inside the database. So we'll call this, what, Content? This is our message. And I'm not sure if this is like other platforms where you get a response as well, where if somebody leaves you a crappy review, you can respond to it.\u003C\u002Fp>\u003Cp>So we got reviews. Let's link those to our listings. Alright. Again, that's gonna be a many to many relationship. So we'll say Reviews, we'll do Reviews, all this is gonna be happening for us.\u003C\u002Fp>\u003Cp>One thing that I see a lot of people miss is this sort field on the relationship page, so you can actually sort these items in the order that you want. Probably not necessary for reviews because you're just going to, do those probably based on recency or or the actual date. What else do we have? In our original model, we had host profile, we had our reviews, we had our tags. Let's go in and add tags.\u003C\u002Fp>\u003Cp>Great. Not not really concerned with any of this. We'll just call it a tag name. Okay. And again, we're going to be using our many to many relationship.\u003C\u002Fp>\u003Cp>Because we could have many tags on many different listings. Sweet. Great. Anything else that we need? Let's do a host or hosts as we'll call them, or we could call this host profiles maybe, because they're they're we're gonna link these back to the Directus users collection.\u003C\u002Fp>\u003Cp>Again, just so we're not gumming up or you know, making a mess out of the system table. But behind the scenes, if I pull back up my database, you'll see that all of your SQL data here, all your data in your SQL database, it remains purer because all of the metadata that Directus uses, it stores in its own name space tables. The one that kind of sits in the middle of that is Directus Users. This is what you get out of the box. You get the authentication, we have the permissions that are tied to this, so you get rule based access control, which is really nice.\u003C\u002Fp>\u003Cp>Right? You get all of this out of the box. I hate to junk up my system tables, though. So what I usually do in situations like this is just add a link back to those. So instead of a mini to mini, we'll just do like a mini to 1.\u003C\u002Fp>\u003Cp>And we'll call this our user. For the related collection it's gonna be directus underscore users. And I'm gonna go into field advanced creation mode, we'll call this host profile. And one other thing I'm gonna do is make this value unique. Great.\u003C\u002Fp>\u003Cp>Okay. So now we have our host profile. What does this look like? Santa is our host. Do we have any other details?\u003C\u002Fp>\u003Cp>Let's pull somebody else up. I'll click the same one. See what kind of data we're storing on the host profile. Alright. Probably an image on the host profile.\u003C\u002Fp>\u003Cp>Great. So we'll just add an image, or we could call this Avatar. And how are we doing on time? Looks like we've got roughly like 15 minutes left. We're doing a lot of data modeling on this.\u003C\u002Fp>\u003Cp>We've got our user avatar. We've got a description. Great. Alright. Let's go in and we've got the details for that.\u003C\u002Fp>\u003Cp>Let's add our host to the listing as well. So a listing belongs to a single host. I see we've got co hosts here, but, not super concerned with that. That's gonna be our many to one collection, or relationship. So a host owns a single property or a host is responsible for a single property, but a host could have many properties.\u003C\u002Fp>\u003Cp>So we'll choose our host profile. Great. Save it. Okay. Alright, so let's go in and look at this giant form that we're gonna add all of our data.\u003C\u002Fp>\u003Cp>Right, now the really cool part here is I've modeled all this data out, and I'll show you in a moment. I'm just gonna copy and paste basically everything from this. If I was smart I probably would have built like a scraper or something to input all this data. Cool. There we go.\u003C\u002Fp>\u003Cp>We've got the description when it comes time to the amenities. Let's just go in and create a new amenity. We've got mountains, view. Yeah, that kinda looks like a mountain view. Mountain view.\u003C\u002Fp>\u003Cp>We could give it a key. That could be helpful if we're actually filtering this on the front end. Dedicated workspace. Alright. So I can go through and do a desk icon.\u003C\u002Fp>\u003Cp>Not quite as fancy as what they have here. We'll just use Workspace as the key. Cool. Great. So we added some amenities.\u003C\u002Fp>\u003Cp>This is what? This is a entire cabin? Yeah. This looks like a house. This is an entire home.\u003C\u002Fp>\u003Cp>We have up to 4 guests. You can see we've got that nice little note that we had here, so this is just providing that extra little for our user experience. We've got 1 bedroom, 2 beds, 1 bath. Are pets allowed? Do we see pets on this one?\u003C\u002Fp>\u003Cp>Let's say no. Right? Check-in time, where do we find that? We've got our check-in time is 4 pm. 4 PM.\u003C\u002Fp>\u003Cp>Check out time, 10 AM. K. Check out type. Self check-in with a lockbox. Do we have a quiet time?\u003C\u002Fp>\u003Cp>I don't see quiet time. I'm gonna set quiet time. Right? From midnight to 5 AM? I don't remember.\u003C\u002Fp>\u003Cp>Great. Additional rules, we can see outside guests, no allowed parties, lots of additional rules at this particular place. We've got the address, I'm not even going to bother filling that out. We'll add some property info. What do we have there?\u003C\u002Fp>\u003Cp>Must Climb Stairs. That's a great one, right? Must climb stairs. You have to be able to climb some stairs to access the property. Stairs.\u003C\u002Fp>\u003Cp>We even have a nice stairs icon. Great. We don't have any reviews. We're gonna tag this as a cabin in the woods. And then we have our host.\u003C\u002Fp>\u003Cp>Right? So we'll just create a new host profile. Who is our host? Sierra. I I'm not gonna do that.\u003C\u002Fp>\u003Cp>Let's just use, like, user avatar, sample user avatar. Yeah. This guy looks great. Kinda like Tim Cook or something. Copy image address, upload, we'll call this guy test host, and save.\u003C\u002Fp>\u003Cp>Alright. So now we've got a listing. The only thing we're actually missing here is some images. Right? So I can go back into our listing, I can hit create, and here we can add files to this.\u003C\u002Fp>\u003Cp>And files could be images, we're just gonna call it images, that's great. We can even choose what folder we want to upload these things to. So we'll save that, drag it back to the top, add some images for this, and where'd you go? I'm just gonna pick one of these. One of the things that I really like is, it's super handy to be able to upload images from a URL here without having to download them.\u003C\u002Fp>\u003Cp>Not sure about you, but I usually get like a 1,000,000 1,000,000 files on my desktop that, you know, just clutter up things after a while. Okay. Alright. So now we've created this listing, right? And, if I pull up our application, we got this really nice Nuxt application.\u003C\u002Fp>\u003Cp>I'm gonna just move Airbnb out of my way as well. But if I pull up this application Oh, I closed Directus. Let me bring that back. I still haven't mastered Arc. I'm not sure if you guys are Arc users or not, but, it can be a challenge.\u003C\u002Fp>\u003Cp>Alright. So we've got Directus, we've got our local host. I'm gonna pull up our code base. This is just a senior Nuxt application. I've already got an SDK configured for this.\u003C\u002Fp>\u003Cp>And if I look, I've got an extra leftover page here, but we've got this index page, we've got 100 apps, 100 hours. If I wanted to just use like the async data call from NUXT. So if I look at the NUXT documentation, they have a composable in here, use async Data for Data Fetching. And this takes care of things like caching, refreshing, it gives you like pending state, things like that that you can use. So I could potentially just copy this here.\u003C\u002Fp>\u003Cp>We've got Use Async Data. Let's call this Listings, so we give it a key. This could be like the actual route, like the full path of the route. But here, I'm going to go in and we are going to do something like this, where we say return use directus. I've got a composable here and I'm going to read items from the listings collection.\u003C\u002Fp>\u003Cp>The next thing I would add here could be, you see Copilot is already offering some suggestions of hey, only show me published listings or like the specific fields that I want. Let's not even bother with that at the moment. I may just leave that on there, though. Great. Do we have everything we need?\u003C\u002Fp>\u003Cp>Refresh interval. Where am I missing? I don't need refresh interval. Don't really want refresh interval. What does our parser say?\u003C\u002Fp>\u003Cp>Do I have too many of these? Let's just keep it simple. Oh, yeah, missing the last one. But there's that's what I was missing. Okay.\u003C\u002Fp>\u003Cp>So now I'm just gonna log this out. So if I do data, maybe we wrap it in a pre tag just to see what that looks like. Are we getting anything back from Directus? No. Why not?\u003C\u002Fp>\u003Cp>Subscripts. Parse error. Use async data listings. We're going to return use directus read items. That should be what I've got set up here.\u003C\u002Fp>\u003Cp>Use directus. Use directus rest. No. It should just be oh. Do we need to return a wait?\u003C\u002Fp>\u003Cp>Nope. What is the problemo? Let's actually just ditch async data for a moment. Const data equals use await. Use directus, read items listings.\u003C\u002Fp>\u003Cp>Alright. And this should do the fetching here. And now I can see the problem. Nuxt has SSR configured out of the gate, so I wasn't seeing those errors here on the client side. But basically this is showing 403 forbidden.\u003C\u002Fp>\u003Cp>And the reason why is because I haven't set up my permissions inside Directus. So if I were to just log that back, I could go into Directus and under our rule based access control, I'll just go in and for now, and because we're running out of time here anyway, I'm just gonna set this to public. All this data that we set up for the individual listings, all of our tags. One of the things that I could do here, right, this is public data, so I don't want to maybe I want to show the city and like the state or the country, but I can go in and I can set custom permission settings. So where I have the, you know, I could go in and say okay, only items that are published will be shown and are available to the public, so without login.\u003C\u002Fp>\u003Cp>But things like street address and postal code, you know, if I was storing data like phone numbers or email addresses, right, I wouldn't want to make that data available to the public. You know, the rest of this data, hey, they need to know and we probably still want to surface that if they are, just browsing the site and they're not logged in. But for the actual sensitive data, I don't want to I don't want to give that up. Alright. We've saved our Let's just take a look and see what we get back here.\u003C\u002Fp>\u003Cp>Okay. So I can see the listings. Right? We're not getting any data. And again, I thought I was using async data wrong here.\u003C\u002Fp>\u003Cp>I need to brush up on its use, but the status of this post is not published. So now if I go in and publish it, because we set up that rule based access control, Now we can see we got some data. It's rendering kind of funky because I'm flexing it. Okay. Alright.\u003C\u002Fp>\u003Cp>So now we can see we're actually fetching all the data from our API. So which is really nice. Directus gives us these REST APIs. As soon as we set up our data model, we can see all this information inside here. All right.\u003C\u002Fp>\u003Cp>So if I look at the clock, we've got, just a few moments left. Let's go in and just try to flesh something out as far as a detail page. If I look, I've got some images here. How do I actually get access to this relational data? One of the nice things inside Directus is the ability to pass a fields array that will actually act as a GraphQL API.\u003C\u002Fp>\u003Cp>So you get all the benefits of GraphQL, and then I can specify all the fields that I want to return, and I can even grab relational data, as deep as I want to go, which you have to be very careful about recursive relationships. But within that, I can go and grab all of that. So if I wanted to grab the root level fields, I could. I could also do something like this where I say images.asterisk, that will give me the Directus Files ID that I would need to render these types on, on the actual on the site. Yeah.\u003C\u002Fp>\u003Cp>Simple enough. Alright. Let's flush this out as quickly as possible. Alright. So I'm gonna clean that up.\u003C\u002Fp>\u003Cp>What do we have here? Underlying this, I've been using the Nuxt UI library, but I'm always a huge fan of Tailwind UI. If you don't have it, sign up for it. It's a great tool. All right.\u003C\u002Fp>\u003Cp>This is probably, could be similar to a product page, maybe. Alright. Let's take a look. Product overviews. Got a couple of these.\u003C\u002Fp>\u003Cp>I really like this image grid component. Image gallery. Let's just lift this wholesale. Oh, the Linter is doing a number on this thing, right? So here we go back.\u003C\u002Fp>\u003Cp>We're probably getting an error somewhere. The View Dev Tools and the Nuxt Dev Tools, I can't recommend enough. They're very great. But if I go in and I look for my index page, I can see our data that we've got here. And maybe here, I actually want to, is there where's the key?\u003C\u002Fp>\u003Cp>I think I actually do something like this where I transform and I do data, and I do data dot 0. Is that valid? No. Yeah. Maybe that's right.\u003C\u002Fp>\u003Cp>No overload. Not just this call Because we've got the return. Oh, no. I think it needs to be one more out here. Okay.\u003C\u002Fp>\u003Cp>Now if I look, data transform. Product dot images. Okay. So this is gonna be data dot images. So if we just clean these up.\u003C\u002Fp>\u003Cp>Data data dot images. Oh, missed one. We'll just clean that up. Data dot images dot source. Yeah.\u003C\u002Fp>\u003Cp>So we're not getting source here. Are we getting any actual data at all? Right? It's not showing me anything in the view dev tools. This could be a case of where I spent way too much time data modeling, without actually building anything.\u003C\u002Fp>\u003Cp>But at least, you will have a good idea of the back end structure. 0 of undefined. Let's just, comment this out. Get our data back. Data.\u003C\u002Fp>\u003Cp>Pre. Alright. So we have data dot images. Now I gotta do data dot 1. Let's just do this.\u003C\u002Fp>\u003Cp>Our listing equals data, data dot value, and we'll log our listing. Okay. And now I should be able to do something like this where, I've got Nuxt Image connected to this as well, which is a great tool. So we use Nuxt Image and in the actual config for Nuxt you can set up the provider for Directus because we use the same underlying library. Where are you?\u003C\u002Fp>\u003Cp>And then I can set the base URL for my assets. So then I don't even have to actually worry about passing the URL. So Nuxt data dot images. Is it gonna be data? It's listing dot images, and then where it says source, we're actually gonna grab this directus file's ID.\u003C\u002Fp>\u003Cp>Underscore files underscore ID. Do we actually get images here? If I remove the alt text, does this actually work? Am I are are we kidding, man? Why do I not have this?\u003C\u002Fp>\u003Cp>Right? If I close this out, why is it not showing? Images, directus files underscore ID. Yeah. Alright, well regardless we hit the timer.\u003C\u002Fp>\u003Cp>I didn't get very far on the front end on this one. I chalk it up to spending too much time on the back end, but oh, what a back end it is. It's very beautiful. We've got a detailed data model that, we've got our REST API for, if you prefer GraphQL, you could query this on the front end to your heart's desire. Again, maybe I was rightfully a little scared of this Airbnb just because of everything that is involved in all the details on this.\u003C\u002Fp>\u003Cp>Anyway, I had a lot of fun with this one. I hope you enjoyed it. Stay tuned for the next episode. Got a great one coming. I'll see you.\u003C\u002Fp>","Alright. Alright. Alright. Welcome back to the next episode of 100 Apps, 100 Hours, where we build or rebuild some of your favorite apps, or publicly fail trying. I'm your host Brian Gillespie, developer advocate at Directus. And today we have the mother of all clones, Airbnb. This was actually going to be one of the first episodes that I was going to record for this series. To be honest, I got a little scared. Airbnb has a ton of functionality on the front end. We have authentication, we have listings, you have 2 different types of users, and that you have folks who are listing their properties, folks who are booking those properties, you have messages, you have reviews. There's a ton of things just underneath the surface. So I'm curious to see how far we can get in an hour. I don't want to bite off more than I could chew, but, should be entertaining nonetheless. Alright. Alright. So if you've tuned into the series before, you'll know we have two rules. The first is you have 60 minutes to plan and build, or I have 60 minutes to plan and build, if you've got 60 minutes, if you're watching along, maybe you want to build with me. It's great. No more, no less, that's all we get. And then the second rule is kind of the anti rule. Use whatever you have at your disposal. AI, chat GPT, I've got a Nuxt front end application starter set up that already has some plumbing to connect to my Directus Instance, and I have a blank Directus project. That's what I've got. We're gonna try to build this Airbnb clone. Let's get started. Alright, so we'll start the clock here and away we go, right? So I'm going to pull up the Airbnb website, if I can actually figure out how to work my computer, and let's just poke around and see what we can find, right? One of the first things that I usually like to do when I'm trying to break down functionality for an app is take a look at the navigation, right? Airbnb has this concept of stays versus experiences now, so we have properties or listings or something like that. We have experiences which are, tours and tastings and and actual things that you can do. So that should be an interesting dynamic. I I think we'll mostly probably just stick to the properties part of it, like the listings. You know, one of the other things that I always like to take a look at, you know, I can go in and check the network requests, you know, and and try to suss out what some of the the data model looks like for all these different things that we're querying on the front end. So it looks like there is a very, very interesting, set of queries they're using. If I look into, like, the actual property values, we've got this very detailed data that we're returning and using potentially for our layout. I don't really know how helpful this is going to be, right? And to my knowledge, I don't know that Airtable, or I'm sorry, Airbnb has a public API that I can use to kind of introspect the schema, so basically I'm just playing it by ear here, right? So on every episode, before I actually build, I like to do a bit of planning. And first, let's start with like what type of functionality we're going to try to achieve out of this. I certainly want to get the back end data model built for this, we'll map that out in just a moment. On the front end maybe we want to display a listing detail page and maybe a listing index page. Let's call that our original goal. If we got like a stretch goal, maybe we want to let folks book booking pages, booking properties, sending messages, something like that. So I think these three pieces here will be plenty to chew on for an hour. Let's, let's try to map this application out. And I really like Figma, FigJam, the combo is great for mapping out things like this. We'll just do no fill. Alright, so as far as our data model, naming things is always tricky. So are we gonna call this Listings, is it Properties? That's gonna be our main collection, right, this is our main table. Not a 100% sold on what it's gonna be yet. We've probably got users in the system, so we'll have our users and Directus is going to actually take care of that for us with the Directus users collection that that it bootstraps whenever we actually create our instance. What else do we have? We've got, reviews that are going to be included and it can be attached to the individual listings. Looks like we have categories as well. Right? Tiny Homes, Countryside, I don't know if those are gonna be tags. I guess you could be a Countryside and a Tiny Home. So that could be like a mini to mini relationship. So let's just call those tags. Great. We could tag those onto a property. We have bookings. Alright. So you've got a calendar of bookings for a property or listing. We can make those little relationships. Just draw some nice looking arrows, basically. That's all we're doing here. Then we've probably got a junction table for that mini to mini relationship. Cool. Just drag that out of the way. We've got our reviews. Tags. Okay. Listing tags. Alright. What else? If we open up filters, we could see things like, the type of place, the price range, bedrooms, number of bedrooms, property type, the amenities. That's kind of a nice bit of functionality. How are we going to deal with that? What are the booking options? Host language. If we go to the actual detail page for a property, right, we've got a gallery of multiple images, we've got a host that could be our user, I guess. You know, we might have a like a host profile or something like that, that we attach so that we don't pollute our users collection. We've got a schedule, we've got the reviews, we've got a map, yeah. Okay. I feel pretty good about this. Let's just draw a couple more arrows, just for the sake of arrows. Let's actually start building this out. Cool. Alright, so I'm going to drag this over and as you can see over here on the left I've got my blank instance of Directus. What are we gonna call this main one? Properties to me as a developer, I'm thinking like properties of an object or something like that, so I'm I'm gonna call this listings. If anybody from Airbnb is watching this, hey, maybe tell me what you're what kind of schema you're using on the back end. All right, so I'm going to create a new collection called Listings. We're going to use the generated UUID. Is this a published listing? Yes, probably. So we'll keep status, we'll have the created on, updated on, user created, etcetera. And let's go through and start modeling this out. Alright. So if I look, we've got a h one, this is probably just the title of the listing. Great. What happens to our URL? It looks like they don't use fancy URLs or like a slug for this listing. It's just actually by the ID. And it looks like it's rooms. I could have just looked at the URL. I kinda like listings better just because they are using, they're listing whole houses on here as well, right, not just a room. So, cool. We will have a, this looks like it's calculated. What are the other things that we need? The number of guests, the number of bedrooms, the number of bathrooms, right, these are all going to be inputs. We can support how many guests, let's say max guests. That sounds like a good one. One of the other nice things here is the ability inside Us, if you're using it for your content management, you can add these helpful notes for your users. So how many guests total can stay at this property. And I can also translate these into other languages, which is really nice for our international users. Alright. That looks good. This will always be an integer. We don't support half people here at Airbnb, that's what we'll call this one. So max guess, we have number of bedrooms. Right? I can just duplicate that particular field and say bedrooms quantity, bedroom number of bedrooms. Like bedrooms quantity. Sounds great. I may go in and edit this individual note though. How many total bedrooms? Cool. We'll go through and let's do bathrooms quantity. Again, and naming is probably, I won't say it's half the battle, but it is a lot of the battle in development, is coming up with names that make sense and have meaning. So we've got our title, we've got our max guest, we've got our bedrooms, we'll have a host for that, so we'll deal with that in a moment. I can see we've got a nice description here. This looks like it supports WYSIWYG content, so we'll go ahead and add the WYSIWYG editor within Directus. We'll call this the Description. Could be the about section. I see about the space here. We'll save this. Great. And then we have this what this place offers. Right? So this is an interesting one where we've got items that are not included, we've got items that are included. There's a couple different ways we could actually manage this within our data model. We could, I like hard code this as like JSON values or something like that, where we have a list of these objects. We could also set it up as a separate relationship so you could add these features, or I'm assuming what we would call features to this particular listing. Maybe come back to that one. Let's keep rolling down. We have our reviews, we have the address, so we could have the street address. Let's go ahead and fill that up. Street address, we do this as street address 1, Street address too, just in case we need it. What else do we have? City, region, state. I think it's region if we're being international friendly outside of the US. And then we have a postal code. Great. Alright. Maybe we clean this up a little bit. I'm gonna make this half width. When I'm inputting these things I want to make it look really nice. I am going to go in and use the detail group within Directus just to get a nice visual. So we'll say detail group, let's call this Address Info, on the front end, whenever we call the API, we would either make sure we omit this address so nobody can find this property without actually booking it. We want to keep that private. Or we may not even, send that to the front end at all. But obviously you want to store that. And we could, you know, even potentially like obfuscate this somehow. So we've got a host. We'll come back to that. House rules. Let's take a look at this. Pets allowed, no smoking. Check-in, check-in, check out. Yeah. So we're gonna need details on check-in, check out. We've got safety and properties. We've got safety devices. We've got a cancellation policy. Before you book, partial refund. Okay. Yeah, let's tackle just like safety really quickly. So we'll go in. This seems something like pretty simple. Let's go in and this may be a good use case for our checkboxes inside Directus. So safety, what do they call these? Safety and property. Let's just call it safety devices. We'll say carbon monoxide. Then we have a smoke alarm installed. Great. And for the value, I can even change this to something like CO 2 or CO. Right? Carbon monoxide is single c0. Yeah. Let's just leave it there. Great. I'm trying to think of what other safety devices we'd have on a property. Alright? Let's just check out one of these other cabins. Scroll down to the bottom. Potential for noise, so there is some road noise. This is kind of like a extra metadata as well. Safety devices. Let's just finish this out. Great. We could do something interesting here where like property info is just text and an explanation. Right? Maybe we even have a potential icon for this. So let's use the repeater field inside Directus. Basically it just stores as JSON data, but I can create an array of objects here that can contain basically almost anything that I want. But again, it just gets stored as JSON within the database. So we'll call this property info, and for the fields, it looks like we've got an icon. Right, icon. We'll call the field icon, we have a string, that's the type of the field, and then we actually have an icon interface inside Directus where you can choose lovely icons from a drop down. Alright, next we'll have a label or a title for this. Let's just go with Label, that'll be a string as well. We'll use just a standard input interface and what's next? Right, we've got a description, label, description, close enough. And this we'll just make a text box. I don't think we need WYSIWYG input here, we'll just do text area. I said we didn't need it and then I clicked it, got a little click happy there. Great. Okay, so now we've got safety devices, we've got property info, we could even call this, we could add this in our detail group as well. We could call it safety property. Safety and property. Great. And we can even add a little safety sign. Great. Health and safety. Beautiful. So I'll just drag those within this and we are starting to build a great looking data model for this. Alright. Let's cover check-in, check out info. So we can use the date time field. Do we want I guess we just want time. Right? Check-in time. We will let's do that half width. We'll do checkout time. Cool. We've got those 2 items. Was there any other details on check-in? Self check-in with keypad. Maybe there's like a check-in type. Let's take a look at a couple other listings and see what we can find. Where did you go? There we go. Self check-in with a lockbox. Yeah. So this is probably just like some text. Check out type. Great. Again, if I wanted to group these together, I could add a nice little detail group. Check out, let's call it House Rules, right? House Rules. Great. We'll put that under house rules, check out, check-in, check out type. Okay. If I could actually click and drag the way I want. Let's, let's move that up. We've got the address info, we've got the house rules, we've got the bedrooms, max guests, pets allowed. That could be a Boolean attribute or a toggle here. It just stores this Boolean in the database. So our pets allowed, Default. Let's say no is the default. Pets allowed as the label. Great, great. Alright, we'll shrink this to half width. I'm getting really anxious to see what this actual form looks like once we get done mocking out our data model here. We've got quiet hours, right? So how do we adjust for this? Do we have a range of dates inside Directus? We do not. Quiet, so we could do something like quiet time start. Date time. Oh, nope. We just want time. Date, time, and our dates. We're gonna have to delete that one. Alright. Let's just go back. Let's try again. We just want the time, right. We're not really concerned with the date. Quiet time, stop quiet quiet time start. Quiet time in. Great. Isn't data modeling fun, guys? We'll put that in our house rules. Quiet time. Does anybody allow smoking in their properties? Do we even really need to concern ourselves with this? Right? Additional rules. This doesn't look like it actually supports WYSIWYG content. So we can add a new field for that. We'll just call it additional rules. We'll just call it a text area. That's gonna store as text in the actual database. And one of the important things to know, hey, as we're going through and modeling this data out, if I were to pull up this SQL database right now, and I could show you that in just a moment, Directus is actually going through and mirroring every change that I'm making here inside the Data Studio, inside our actual database as well. So we go to house rules, we'll just clear that up. Too many, There we go. Alright. I need to adjust the sensitivity on the mouse. But if I open up table plus and just to give you guys a preview of what's going on behind the scenes here. Uh-oh. What am I doing wrong? Connect to the database. Detour. Let's check my Docker Compose. 8 055. Those ports should be exposed. Oh, I see what the problem is. I've got, another instance of PostgreSQL running through Docker. All right, so if I open this up, you can see I've got a listings table here. And if I look at the structure, you can see we've got those same columns that we were mapping inside Directus, and you can see the data type is being appropriately applied. Likewise, if I were to add a column to the database here in TablePlus or via SQL, Directus would introspect those changes and actually show up inside the app itself. So it is a beautiful mirroring relationship. It's, I love it. It's one of my favorite features of Directus. I I know I say that a lot. What other things do we need to offer here? HDTV, Mountain View, Valley View. I I I don't know what this looks like behind the scenes when you're actually mapping these out. I would be curious to see. Free washer, free dryer in unit versus free washer, free dryer out of unit, outside of unit. Right, we've got a garden view. I'm assuming this is probably just like some type of JSON data or something that's being stored, maybe free washer end unit. Let's go back. Can we actually filter based on this data? Can you filter based on okay. Yeah. So so you can filter on some of this. It doesn't look like you could filter on all of it. Like, hey, does it have a in unit versus a a non unit? I'm trying to think of the best way to account for these. I'm not sure exactly how I want to do it. Right? So we've got this type of place that we might set up as well. Let's do that. We're gonna do the type of place and then we have our property type. Let's just call this one type. Okay. We'll make this a string and instead of using an input, let's use radio buttons. I think we just have a room or an entire home. Alright. Destinations, guests. You could filter a room or entire home, basically. Yeah. That that works for me. Alright. So we'll say this is a room. And one of the other cool little things that I'll show you while we're here, I can translate this content inside Directus. If I use a dollar sign t and use this key and I hit enter, I can go in and add translation strings for any of the languages, which is really nice so that, whoever is using this, Directus instance, if they are editing content and they go in and they happen to be French or Canadian. You guys speak a different language up there in Canada. Right? No kidding. And then we'll call this the entire home. But this translation feature is really nice. Only recently started digging into it. So the value we'll call entire home. And then if I want to, what I could do is go into our translations here inside the Directus Admin. I can create a custom translation, and I could say entire home, pick my English language. Great. Oh, you do have different English up there in Canada. And I could say I could call this Bryant's home if I wanted to. Right? It doesn't really matter, but if I were to go in and look at this data model now, right, for the type I can see it's a room or I can see it's Bryant's home. So that's really nice in that I can apply these translations for any of the languages that our our team may speak. Entire home, great. What do they say here? Room in a home. Alright. So if we create a new one for room, we'll go English US, a room and a home. Great. Alright. So we go back, we create a listing, now we can see we've got 2 options there. We've got an entire home, we've got a room and a home. Cool. Let's, let's continue finishing this up. Price ranges could be interesting, right? I'm trying to think of of how we would set this particular data up, because I'm imagining this varies based on, like, is it a Monday night versus a Friday night or versus a Sunday night? How many days that you're staying? Can we actually Airbnb your home? Blah blah blah. Airbnb setup. I guess I could log in to my account. Don't look at my passwords. Let's see what it looks like when we actually Airbnb at home. Tell us about your place. Start on your own. This seems to be a nice little onboarding flow as well. My house is a castle. Right? Great. Entire place, room, a shared room. Okay. So that's one that we forgot as well. Right? A room, and then we have shared room. So again, we'll do that dollar sign t and a colon that allows us to store that as a translatable string. Great. This is gonna be the entire place. Enter my address. I'm not gonna do that here. Let's do, it was Yankee Stadium. Right? Let's let's do the address for Yankee Stadium. Bronx. Great. Confirm the address. Is this PIN in the right spot? Do they have any validation on this? I guess they don't. Right? So I wonder if this goes up to, what, I don't know how many people, Yankee Stadium seats, but we'll call it 16 plus. Right? So we've got the number of guests, bedrooms, beds. There's something else that we need to add here. Right? Bed quantity. Great. Should've done this to begin with. Right? Let's add another group because this is gonna get messy. A lot of fields on this one and, you know, again, this is why I didn't want to bite off too much on this actual one. So we'll call this the, what do we call this? Basics listing info. Yeah. That's fine. We won't we won't get too carried away here. So we got the type, we got the listing info, just move that up. Max number of guests, bedrooms quantity, bathrooms quantity, are pets allowed. We'll hoist title and description up here. Let's say we've got 8 beds, 8 bedrooms, maybe 5 bedrooms. Again, no idea how many of those are inside Yankee Stadium. Make your place stand out. Okay. Let's look behind the scenes and see, is this making any calls here? We have parking, just using React. So I wonder Okay. If we amenities. Amenities. Can we see any of those here? I don't see them. I I I really don't wanna dig through each of these components here to try to figure that out. Can you actually do, like, the view thing in the React Dev Tools? I guess you can. Alright. So what's our consumer, provider, Value, History. Yeah. I'm not a not a React guy. Let's just not even bother diving through it. Right? There's our safety items, like first aid kits, smoke alarm, etcetera. Standout amenities. So these are just the basics, right? And you might even go through and add separate fields for for these things. So we could do again the detail group for this. Maybe this one starts closed. Call this amenities. These also could be like a separate database table, that we we add, and that way we could continue adding those. Yes, no, the other. Again, not knowing behind the scenes the importance of this, like maybe we add these as separate things so we could filter them. So let's go in and we'll add a new collection. We'll call it Amenities. Amenities. Probably need some spell checking there. Amenities. Cool. Alright. All of this, I'm not really concerned myself with. We have an icon for the amenity. Gotta have an icon. Very design oriented here myself. Recovering designer, as I like to refer to myself. We have a name for this amenity. Maybe we've got something like a key for it as well. I I I don't know. And then is there some type of explanation for these? Yeah. You may have something like a description for it as well. Cool. Good enough. Now what we're gonna do is link these 2 together, right? So inside Directus what I can do is basically create a many to many relationship, because one property could have many amenities and many amenities could be linked to many properties. I think that's a great explanation of amenities and many to many relationships. Directus makes this super simple to create these relationships. So on our listings collection, we'll just call this Amin I need to work on my spelling for sure. My wife works in the school system as well, so maybe she can give me some help. We don't wanna allow duplicates. We wanna to show a link to that specific item. If I open up advanced field creation mode I can actually control the name of this junction collection. That's going to create a junction table in our SQL database. I'm okay with this, right? And if I wanted to I could add a reverse many to many relationship, on the amenities collection back to the listings. What we may do is add a sort field here as well. For the display, we'll show related values. We can come back in and clean that up in just a moment. But, great, we've got our amenities here. Let's move that up to the top. What are the other things that we need to add? Some photos of the castle. You need 5 photos to get started. I'm feeling pretty confident here. I'm just going to close this out. Exit. We'll go back to the Airbnb website. And there was one thing that I forgot what I was gonna add here. Is, property type. That's what it was. So we'll go in, I'm just gonna duplicate this one called type and we'll add property type. Duplicate, that's of course gonna drop down here to the bottom and I'll just bring it back up. Property type, let's just clear out these values. So it probably didn't need to duplicate that. But we have a house. I'm not gonna mess with the translation strings here. Just know that's available to you if you are working with Directus. We'll call this an apartment. Apartment. Guest house. For those of you who do have a guest house, amazing. Take advantage of it. Exploit it on Airbnb. We have the hotel and that looks pretty good. Cool. At some point we'll probably have to deal with the pricing thing, but for now we'll just skip it. Alright, so I'm feeling pretty confident about this. Let's go in and flesh out the other items in our data model. We spend a ton of time data modeling this out, but there's a lot of fields on the listings. So we got reviews, we have tags. So let's create reviews. What does the review structure look like inside Airbnb? So we're certainly gonna want a user created. We'll probably have a status on the review. Hey, is this published or not? For the reviews, okay. It looks like we have an overall rating. We have cleanliness, accuracy, location, value. Alright. So I think we have a slider field that it could be a good representation for this. Let's call it overall rating. Great. We'll duplicate that one. We can call it cleanliness. Cleanliness? Yeah, I think so. Looks great. Let's duplicate this. We'll have the accuracy and so forth. I just missed that one, didn't I? Duplicate. Alright. So you kind of get the picture there. We have a user, we have some actual text. It doesn't look like, to me that doesn't look like WYSIWYG text. Not that it matters. The WYSIWYG and the text area interface both store the data as text inside the database. So we'll call this, what, Content? This is our message. And I'm not sure if this is like other platforms where you get a response as well, where if somebody leaves you a crappy review, you can respond to it. So we got reviews. Let's link those to our listings. Alright. Again, that's gonna be a many to many relationship. So we'll say Reviews, we'll do Reviews, all this is gonna be happening for us. One thing that I see a lot of people miss is this sort field on the relationship page, so you can actually sort these items in the order that you want. Probably not necessary for reviews because you're just going to, do those probably based on recency or or the actual date. What else do we have? In our original model, we had host profile, we had our reviews, we had our tags. Let's go in and add tags. Great. Not not really concerned with any of this. We'll just call it a tag name. Okay. And again, we're going to be using our many to many relationship. Because we could have many tags on many different listings. Sweet. Great. Anything else that we need? Let's do a host or hosts as we'll call them, or we could call this host profiles maybe, because they're they're we're gonna link these back to the Directus users collection. Again, just so we're not gumming up or you know, making a mess out of the system table. But behind the scenes, if I pull back up my database, you'll see that all of your SQL data here, all your data in your SQL database, it remains purer because all of the metadata that Directus uses, it stores in its own name space tables. The one that kind of sits in the middle of that is Directus Users. This is what you get out of the box. You get the authentication, we have the permissions that are tied to this, so you get rule based access control, which is really nice. Right? You get all of this out of the box. I hate to junk up my system tables, though. So what I usually do in situations like this is just add a link back to those. So instead of a mini to mini, we'll just do like a mini to 1. And we'll call this our user. For the related collection it's gonna be directus underscore users. And I'm gonna go into field advanced creation mode, we'll call this host profile. And one other thing I'm gonna do is make this value unique. Great. Okay. So now we have our host profile. What does this look like? Santa is our host. Do we have any other details? Let's pull somebody else up. I'll click the same one. See what kind of data we're storing on the host profile. Alright. Probably an image on the host profile. Great. So we'll just add an image, or we could call this Avatar. And how are we doing on time? Looks like we've got roughly like 15 minutes left. We're doing a lot of data modeling on this. We've got our user avatar. We've got a description. Great. Alright. Let's go in and we've got the details for that. Let's add our host to the listing as well. So a listing belongs to a single host. I see we've got co hosts here, but, not super concerned with that. That's gonna be our many to one collection, or relationship. So a host owns a single property or a host is responsible for a single property, but a host could have many properties. So we'll choose our host profile. Great. Save it. Okay. Alright, so let's go in and look at this giant form that we're gonna add all of our data. Right, now the really cool part here is I've modeled all this data out, and I'll show you in a moment. I'm just gonna copy and paste basically everything from this. If I was smart I probably would have built like a scraper or something to input all this data. Cool. There we go. We've got the description when it comes time to the amenities. Let's just go in and create a new amenity. We've got mountains, view. Yeah, that kinda looks like a mountain view. Mountain view. We could give it a key. That could be helpful if we're actually filtering this on the front end. Dedicated workspace. Alright. So I can go through and do a desk icon. Not quite as fancy as what they have here. We'll just use Workspace as the key. Cool. Great. So we added some amenities. This is what? This is a entire cabin? Yeah. This looks like a house. This is an entire home. We have up to 4 guests. You can see we've got that nice little note that we had here, so this is just providing that extra little for our user experience. We've got 1 bedroom, 2 beds, 1 bath. Are pets allowed? Do we see pets on this one? Let's say no. Right? Check-in time, where do we find that? We've got our check-in time is 4 pm. 4 PM. Check out time, 10 AM. K. Check out type. Self check-in with a lockbox. Do we have a quiet time? I don't see quiet time. I'm gonna set quiet time. Right? From midnight to 5 AM? I don't remember. Great. Additional rules, we can see outside guests, no allowed parties, lots of additional rules at this particular place. We've got the address, I'm not even going to bother filling that out. We'll add some property info. What do we have there? Must Climb Stairs. That's a great one, right? Must climb stairs. You have to be able to climb some stairs to access the property. Stairs. We even have a nice stairs icon. Great. We don't have any reviews. We're gonna tag this as a cabin in the woods. And then we have our host. Right? So we'll just create a new host profile. Who is our host? Sierra. I I'm not gonna do that. Let's just use, like, user avatar, sample user avatar. Yeah. This guy looks great. Kinda like Tim Cook or something. Copy image address, upload, we'll call this guy test host, and save. Alright. So now we've got a listing. The only thing we're actually missing here is some images. Right? So I can go back into our listing, I can hit create, and here we can add files to this. And files could be images, we're just gonna call it images, that's great. We can even choose what folder we want to upload these things to. So we'll save that, drag it back to the top, add some images for this, and where'd you go? I'm just gonna pick one of these. One of the things that I really like is, it's super handy to be able to upload images from a URL here without having to download them. Not sure about you, but I usually get like a 1,000,000 1,000,000 files on my desktop that, you know, just clutter up things after a while. Okay. Alright. So now we've created this listing, right? And, if I pull up our application, we got this really nice Nuxt application. I'm gonna just move Airbnb out of my way as well. But if I pull up this application Oh, I closed Directus. Let me bring that back. I still haven't mastered Arc. I'm not sure if you guys are Arc users or not, but, it can be a challenge. Alright. So we've got Directus, we've got our local host. I'm gonna pull up our code base. This is just a senior Nuxt application. I've already got an SDK configured for this. And if I look, I've got an extra leftover page here, but we've got this index page, we've got 100 apps, 100 hours. If I wanted to just use like the async data call from NUXT. So if I look at the NUXT documentation, they have a composable in here, use async Data for Data Fetching. And this takes care of things like caching, refreshing, it gives you like pending state, things like that that you can use. So I could potentially just copy this here. We've got Use Async Data. Let's call this Listings, so we give it a key. This could be like the actual route, like the full path of the route. But here, I'm going to go in and we are going to do something like this, where we say return use directus. I've got a composable here and I'm going to read items from the listings collection. The next thing I would add here could be, you see Copilot is already offering some suggestions of hey, only show me published listings or like the specific fields that I want. Let's not even bother with that at the moment. I may just leave that on there, though. Great. Do we have everything we need? Refresh interval. Where am I missing? I don't need refresh interval. Don't really want refresh interval. What does our parser say? Do I have too many of these? Let's just keep it simple. Oh, yeah, missing the last one. But there's that's what I was missing. Okay. So now I'm just gonna log this out. So if I do data, maybe we wrap it in a pre tag just to see what that looks like. Are we getting anything back from Directus? No. Why not? Subscripts. Parse error. Use async data listings. We're going to return use directus read items. That should be what I've got set up here. Use directus. Use directus rest. No. It should just be oh. Do we need to return a wait? Nope. What is the problemo? Let's actually just ditch async data for a moment. Const data equals use await. Use directus, read items listings. Alright. And this should do the fetching here. And now I can see the problem. Nuxt has SSR configured out of the gate, so I wasn't seeing those errors here on the client side. But basically this is showing 403 forbidden. And the reason why is because I haven't set up my permissions inside Directus. So if I were to just log that back, I could go into Directus and under our rule based access control, I'll just go in and for now, and because we're running out of time here anyway, I'm just gonna set this to public. All this data that we set up for the individual listings, all of our tags. One of the things that I could do here, right, this is public data, so I don't want to maybe I want to show the city and like the state or the country, but I can go in and I can set custom permission settings. So where I have the, you know, I could go in and say okay, only items that are published will be shown and are available to the public, so without login. But things like street address and postal code, you know, if I was storing data like phone numbers or email addresses, right, I wouldn't want to make that data available to the public. You know, the rest of this data, hey, they need to know and we probably still want to surface that if they are, just browsing the site and they're not logged in. But for the actual sensitive data, I don't want to I don't want to give that up. Alright. We've saved our Let's just take a look and see what we get back here. Okay. So I can see the listings. Right? We're not getting any data. And again, I thought I was using async data wrong here. I need to brush up on its use, but the status of this post is not published. So now if I go in and publish it, because we set up that rule based access control, Now we can see we got some data. It's rendering kind of funky because I'm flexing it. Okay. Alright. So now we can see we're actually fetching all the data from our API. So which is really nice. Directus gives us these REST APIs. As soon as we set up our data model, we can see all this information inside here. All right. So if I look at the clock, we've got, just a few moments left. Let's go in and just try to flesh something out as far as a detail page. If I look, I've got some images here. How do I actually get access to this relational data? One of the nice things inside Directus is the ability to pass a fields array that will actually act as a GraphQL API. So you get all the benefits of GraphQL, and then I can specify all the fields that I want to return, and I can even grab relational data, as deep as I want to go, which you have to be very careful about recursive relationships. But within that, I can go and grab all of that. So if I wanted to grab the root level fields, I could. I could also do something like this where I say images.asterisk, that will give me the Directus Files ID that I would need to render these types on, on the actual on the site. Yeah. Simple enough. Alright. Let's flush this out as quickly as possible. Alright. So I'm gonna clean that up. What do we have here? Underlying this, I've been using the Nuxt UI library, but I'm always a huge fan of Tailwind UI. If you don't have it, sign up for it. It's a great tool. All right. This is probably, could be similar to a product page, maybe. Alright. Let's take a look. Product overviews. Got a couple of these. I really like this image grid component. Image gallery. Let's just lift this wholesale. Oh, the Linter is doing a number on this thing, right? So here we go back. We're probably getting an error somewhere. The View Dev Tools and the Nuxt Dev Tools, I can't recommend enough. They're very great. But if I go in and I look for my index page, I can see our data that we've got here. And maybe here, I actually want to, is there where's the key? I think I actually do something like this where I transform and I do data, and I do data dot 0. Is that valid? No. Yeah. Maybe that's right. No overload. Not just this call Because we've got the return. Oh, no. I think it needs to be one more out here. Okay. Now if I look, data transform. Product dot images. Okay. So this is gonna be data dot images. So if we just clean these up. Data data dot images. Oh, missed one. We'll just clean that up. Data dot images dot source. Yeah. So we're not getting source here. Are we getting any actual data at all? Right? It's not showing me anything in the view dev tools. This could be a case of where I spent way too much time data modeling, without actually building anything. But at least, you will have a good idea of the back end structure. 0 of undefined. Let's just, comment this out. Get our data back. Data. Pre. Alright. So we have data dot images. Now I gotta do data dot 1. Let's just do this. Our listing equals data, data dot value, and we'll log our listing. Okay. And now I should be able to do something like this where, I've got Nuxt Image connected to this as well, which is a great tool. So we use Nuxt Image and in the actual config for Nuxt you can set up the provider for Directus because we use the same underlying library. Where are you? And then I can set the base URL for my assets. So then I don't even have to actually worry about passing the URL. So Nuxt data dot images. Is it gonna be data? It's listing dot images, and then where it says source, we're actually gonna grab this directus file's ID. Underscore files underscore ID. Do we actually get images here? If I remove the alt text, does this actually work? Am I are are we kidding, man? Why do I not have this? Right? If I close this out, why is it not showing? Images, directus files underscore ID. Yeah. Alright, well regardless we hit the timer. I didn't get very far on the front end on this one. I chalk it up to spending too much time on the back end, but oh, what a back end it is. It's very beautiful. We've got a detailed data model that, we've got our REST API for, if you prefer GraphQL, you could query this on the front end to your heart's desire. Again, maybe I was rightfully a little scared of this Airbnb just because of everything that is involved in all the details on this. Anyway, I had a lot of fun with this one. I hope you enjoyed it. Stay tuned for the next episode. Got a great one coming. I'll see you.",[4700],"e53dfaa1-451f-454f-a737-fe10430ecdf2",[],{"year":2380,"number":177,"id":4162,"episodes":4703,"show":4704},[4164,4165,4166,4167,4168,4169,4170,4171,4172,4173,4137],{"title":322,"slug":323},{"id":2391,"slug":4706,"vimeo_id":4707,"description":4708,"tile":4709,"length":129,"resources":12,"people":4710,"episode_number":527,"published":4712,"title":4713,"video_transcript_html":4714,"video_transcript_text":4715,"content":12,"seo":12,"status":19,"episode_people":4716,"recommendations":4718,"season":4719},"10-9","911463486","Directus 10.9 has new focal point support, a new hash display, a huge bug fix for extension builders, and a few breaking changes - so do tune in.","6c85cac5-e6ad-435d-974d-bd025c8fcea7",[4711],{"name":2820,"url":2821},"2024-02-09","What's New In Directus Version 10.9","\u003Cp>Speaker 0: Hello there. My name is Kevin. I run developer relations at Directus. And today, I'm gonna tell you what's new in Directus version 10.9. Directus provide some really powerful image transformations via URL query parameters, including the ability to resize images.\u003C\u002Fp>\u003Cp>Up until now, whenever you resize an image, it would always do so from the middle of the image. In director's version 10.9, 2 new fields have been added to every file, which allows you within images to set an x and a y focal point to a specific point within the image. And then whenever directors will crop and resize your image, it will do so around that point. Super useful, and you can obviously combine this with things like direct us automate to to perhaps determine where that focal point needs to be. Director's version 10.9 also comes with a new hash display, which allows you to display a redacted version of data inside of the Directus editor.\u003C\u002Fp>\u003Cp>Of course, you can still step in and look at the raw value underlying that display, but it gives you this new option, in order to just show that redacted value elsewhere. This one is less a new feature and more an important bug fix, but, finally, you can once again hot reload extensions inside of directives when running it with Docker. This will take us back to the point where extension, development is a little bit better. I know we know it's been annoying the last few releases, but now, thankfully, that has been fixed. If you head over to our documentation website, there's a couple of big new things.\u003C\u002Fp>\u003Cp>Firstly, we hear you hiding the sidebar on the home page meant basically one more click to get anywhere, so we stuck that back on the home page. And as of this release, we now have a new set of framework guides. So whether you use Next, Nuxt, Eleventy, Remix, or others, we now have this lovely set of framework guides to help you get started with directors. We expect there'll be more to come and more advanced topics for frameworks, but for now, this should help people get started. There are 3 breaking changes in this release, which shouldn't affect a lot of users, but I wanted to take a moment to tell you about them before you upgrade in case you are impacted by them.\u003C\u002Fp>\u003Cp>Firstly, we've updated our Exif reader to version 2. So this is a package that we use to extract metadata from files that you store in Directus. And in upgrading to version 2, the names of some of the metadata properties have changed. So if you're relying on those properties staying the same, you may need to go and update your external code. So just be aware of that.\u003C\u002Fp>\u003Cp>The next thing is that we dropped the ability to import parts of the SDK through scoped entry points. This is actually a hangover from when our SDK was in prerelease and the community helped us helped us with feedback. But before, you could import, you know, from directors SDK slash rest, that's done now. Everything gets exported from that root from that root package entry point. So just know that if you're still doing that and you upgrade to version 10.9, you'll need to you'll need to change that.\u003C\u002Fp>\u003Cp>And then the final thing to note is that we have a new directives m package and moved over our environment handling into that package. You can still use e s m config files, but if you had any asynchronous logic running inside of the config files, they won't work anymore. Again, these three breaking changes, shouldn't impact many people at all, but we always like to call them out so you can make informed decisions about when and how to upgrade. We have had so many awesome community contributions towards this release and indeed all of the releases since version 10.8.0. So I'm gonna take a few moments now to call them out.\u003C\u002Fp>\u003Cp>Thanks to Yasir for making content versioning events available in Directus real time. Thanks to Mahendra for 4 contributions. For fixing an issue stopping some environment variables from being extracted properly. For ensuring hooks are triggered when updating relational data. For preventing users from setting no values in primary key fields when updating a field, and to change the keys argument in update items so it will now support keys but also a query.\u003C\u002Fp>\u003Cp>Thanks to Florian for using the current users language in the field Translation Language selector. Thanks to Kelf for improving filtering of collections when selecting related items. Thanks to Morton for fixing the filter selection in Directus Explore when using the search box interface. Thanks to Gerald for both fixing an edge case when applying schemers that would sometimes change foreign keys to nullable when they shouldn't be and for fixing the background and search input height for the action column in the one to many interface. Thanks to Steve for shipping the new Oracle DB Pure JavaScript client in thin mode by default.\u003C\u002Fp>\u003Cp>Thanks to Wazim for a few contributions, for fixing up some styling on the delete collection and field buttons. For making default sorting a sort field, which totally makes sense. And for ensuring filter values higher than JavaScript's max safe integer work with the big integer fields. Thanks to Joshua for fixing the calendar layout to load only items within a current range when there hasn't been a filter provided by a user. Thanks to the team at Gitstart for fixing support of pasting comma separated values into the is one off filter.\u003C\u002Fp>\u003Cp>And, finally, thanks to Nesanne for ensuring our CLI schema apply command output is better visible across different terminals and color schemes. There's a lot more to discover in directors version 10.9, so feel free to check out the release notes on GitHub. Directors version 10.9 is now available on NPM and Docker Hub. So have fun with it, and we'll see you in the next release.\u003C\u002Fp>","Hello there. My name is Kevin. I run developer relations at Directus. And today, I'm gonna tell you what's new in Directus version 10.9. Directus provide some really powerful image transformations via URL query parameters, including the ability to resize images. Up until now, whenever you resize an image, it would always do so from the middle of the image. In director's version 10.9, 2 new fields have been added to every file, which allows you within images to set an x and a y focal point to a specific point within the image. And then whenever directors will crop and resize your image, it will do so around that point. Super useful, and you can obviously combine this with things like direct us automate to to perhaps determine where that focal point needs to be. Director's version 10.9 also comes with a new hash display, which allows you to display a redacted version of data inside of the Directus editor. Of course, you can still step in and look at the raw value underlying that display, but it gives you this new option, in order to just show that redacted value elsewhere. This one is less a new feature and more an important bug fix, but, finally, you can once again hot reload extensions inside of directives when running it with Docker. This will take us back to the point where extension, development is a little bit better. I know we know it's been annoying the last few releases, but now, thankfully, that has been fixed. If you head over to our documentation website, there's a couple of big new things. Firstly, we hear you hiding the sidebar on the home page meant basically one more click to get anywhere, so we stuck that back on the home page. And as of this release, we now have a new set of framework guides. So whether you use Next, Nuxt, Eleventy, Remix, or others, we now have this lovely set of framework guides to help you get started with directors. We expect there'll be more to come and more advanced topics for frameworks, but for now, this should help people get started. There are 3 breaking changes in this release, which shouldn't affect a lot of users, but I wanted to take a moment to tell you about them before you upgrade in case you are impacted by them. Firstly, we've updated our Exif reader to version 2. So this is a package that we use to extract metadata from files that you store in Directus. And in upgrading to version 2, the names of some of the metadata properties have changed. So if you're relying on those properties staying the same, you may need to go and update your external code. So just be aware of that. The next thing is that we dropped the ability to import parts of the SDK through scoped entry points. This is actually a hangover from when our SDK was in prerelease and the community helped us helped us with feedback. But before, you could import, you know, from directors SDK slash rest, that's done now. Everything gets exported from that root from that root package entry point. So just know that if you're still doing that and you upgrade to version 10.9, you'll need to you'll need to change that. And then the final thing to note is that we have a new directives m package and moved over our environment handling into that package. You can still use e s m config files, but if you had any asynchronous logic running inside of the config files, they won't work anymore. Again, these three breaking changes, shouldn't impact many people at all, but we always like to call them out so you can make informed decisions about when and how to upgrade. We have had so many awesome community contributions towards this release and indeed all of the releases since version 10.8.0. So I'm gonna take a few moments now to call them out. Thanks to Yasir for making content versioning events available in Directus real time. Thanks to Mahendra for 4 contributions. For fixing an issue stopping some environment variables from being extracted properly. For ensuring hooks are triggered when updating relational data. For preventing users from setting no values in primary key fields when updating a field, and to change the keys argument in update items so it will now support keys but also a query. Thanks to Florian for using the current users language in the field Translation Language selector. Thanks to Kelf for improving filtering of collections when selecting related items. Thanks to Morton for fixing the filter selection in Directus Explore when using the search box interface. Thanks to Gerald for both fixing an edge case when applying schemers that would sometimes change foreign keys to nullable when they shouldn't be and for fixing the background and search input height for the action column in the one to many interface. Thanks to Steve for shipping the new Oracle DB Pure JavaScript client in thin mode by default. Thanks to Wazim for a few contributions, for fixing up some styling on the delete collection and field buttons. For making default sorting a sort field, which totally makes sense. And for ensuring filter values higher than JavaScript's max safe integer work with the big integer fields. Thanks to Joshua for fixing the calendar layout to load only items within a current range when there hasn't been a filter provided by a user. Thanks to the team at Gitstart for fixing support of pasting comma separated values into the is one off filter. And, finally, thanks to Nesanne for ensuring our CLI schema apply command output is better visible across different terminals and color schemes. There's a lot more to discover in directors version 10.9, so feel free to check out the release notes on GitHub. Directors version 10.9 is now available on NPM and Docker Hub. So have fun with it, and we'll see you in the next release.",[4717],"193317ba-bda7-4ff8-af50-b6023ef24ed7",[],{"year":2380,"number":588,"id":2381,"episodes":4720,"show":4721},[2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394,2366],{"title":2396,"slug":2397},{"id":4613,"slug":4723,"vimeo_id":4724,"description":4725,"tile":4726,"length":740,"resources":12,"people":4727,"episode_number":129,"published":4712,"title":4729,"video_transcript_html":4730,"video_transcript_text":4731,"content":12,"seo":12,"status":19,"episode_people":4732,"recommendations":4734,"season":4735},"twilio","899797484","Use Twilio's Lookup API to automatically verify numbers submitted by new users.","2cae565a-c475-4c5f-938c-13bc74c79696",[4728],{"name":2820,"url":2821},"Validate Phone Numbers with Twilio","\u003Cp>Speaker 0: Twilio provide a lookup API, which allows you to validate a phone number against a set of different criteria. And today in quick connect, we're going to use it to validate that a new user's phone number is a mobile number. If it is, that user can be created. And if it isn't, then we will block creation until they provide a mobile number. So with that, let's get started.\u003C\u002Fp>\u003Cp>In your Twilio console, take note of your account SID and auth token. Make sure to keep these secret. We'll be using them later. Before starting this recording, I also added a phone string field, to our direct to c users collection. So on top of the additional kind of required fields, we now also have a phone number being stored.\u003C\u002Fp>\u003Cp>So let's get started with our flow. Inside of directors, create a new flow. I'm going to call this one validate phone is mobile. We're going to make this an event hook, but, critically, we're going to make it a blocking hook, which means all of the logic of this flow will be executed before the database transaction is made, meaning that if we create some form of error inside of the flow, the user will not be created. Inside of collections, make sure direct to users is ticked and, and that the scope is items dot create.\u003C\u002Fp>\u003Cp>You may additionally choose to run this logic on items dot update, but for now, we'll stick with create. So once a user is created, let's actually do that. All we're gonna do is add a phone number here. Here we go. We'll hit check, and we'll refresh just to see what that data looks like.\u003C\u002Fp>\u003Cp>We see that inside of payload are all of the fields that had data entered into them, including, and in this case, only phone. So there's our phone number there. Twilio also provides some tools to help you format phone numbers that may be formatted inconsistently, but that's outside of the scope of this episode. So we are formatting this as required by Twilio with the plus, the country code, and then the phone number itself. Let's go and do a lookup.\u003C\u002Fp>\u003Cp>So we'll call this lookup lookup And we're going to do this as a webhook request URL, a get request to this URL which will look up phone numbers And then in here, we're going to insert the phone number of the new user. So trigger dot payload dot phone. We will get some data back if we stop here, but we're additionally going to add the line type intelligence data package, I think they called them, which will provide more information about the number, including what we need. So field capital f equals line type intelligence. Fantastic.\u003C\u002Fp>\u003Cp>Now we need to authenticate ourselves with our SID and our auth token authorization now authorization. Now this is important because what Twilio require is what is known as basic h HTTP auth and flows only, only support adding headers. So what we're gonna do here is use the word basic, and now we need to turn our SID and token into a value that will be supported in this format. This is how you do it on the terminal. So you start with the first command, you store your Sid.\u003C\u002Fp>\u003Cp>So you replace this with your Sid, you replace this with your token, and then you run this command in your terminal with the dollar signs. And what that will do is spit out a string, which you can use. I've done this ahead of time. So heading back to our flow, we provide the output of what was in our terminal for our auth. Let's hit save now, and let's run this to test it.\u003C\u002Fp>\u003Cp>So let's once again create a new user. We'll put in a valid phone number. We'll go back to our flow and refresh to see our new log, and we'll look at the latest run. And we see here that inside of data, we have some information like the country call, the calling country code, the country code, the actual two digits for the for the country, the national format, the international format. But what we also have here is the line type intelligence, which was the data packet that we asked for.\u003C\u002Fp>\u003Cp>Now for some applications, it's important that the phone number provided is a mobile, isn't a virtual number, and can support SMS, of which type mobile is true. There's a whole list in the Twilio documentation as to which types can exist. But right now, for this example, we're going to check that mobile exists. So what we're gonna do here is check inside of data, line type intelligence type, and make sure that that is mobile. So let's add a condition in here, and what we're gonna do is check that the lookup data Line line type intelligence, like so, is, there was one inside of it, type.\u003C\u002Fp>\u003Cp>That's right. Type is equal to mobile. Fantastic. So this is us just traversing that object that Twilio provided back. We'll hit save.\u003C\u002Fp>\u003Cp>And so when it's true, we're actually going to add nothing. We're going to say, hey. This flow concludes. There was no error. This was blocking.\u003C\u002Fp>\u003Cp>Now it isn't blocking anymore. Go create the user. But in the case that it fails, we want to stop this in its steps. And the way we're gonna do that is we're gonna run a script. It'll not terribly elegant, but we're just gonna throw a new error.\u003C\u002Fp>\u003Cp>So throw new error. Incorrect, phone type must be a mobile. And let's save let's try this once more let's create the user and this should be successful Fantastic. And I don't have another phone number to test it with, but that means all of this was successful, and we went out the success path, which then just, resolved. And if that was not true, if this condition validation was not met, we would run the script and throw an error.\u003C\u002Fp>\u003Cp>Now there's a lot we can do with the lookup API, actually. You can check which carrier it is. You can check the fraud risk of a phone number. And all of those, basically, here's all the things we can do. The SMS pumping risk, whether or not the number has been reassigned, whether or not there's been a SIM swap, and so on.\u003C\u002Fp>\u003Cp>And all you would do is add extra query parameters to the end of the lookup and then change the conditional to check that certain criteria are met. So this was a little simple example of using the Twilio lookup API to validate phone numbers against some property that is provided by Twilio. You can tweak this for your specific use cases and build more secure applications. Hope you enjoyed this episode of quick connect, and I'll see you in the next episode.\u003C\u002Fp>","Twilio provide a lookup API, which allows you to validate a phone number against a set of different criteria. And today in quick connect, we're going to use it to validate that a new user's phone number is a mobile number. If it is, that user can be created. And if it isn't, then we will block creation until they provide a mobile number. So with that, let's get started. In your Twilio console, take note of your account SID and auth token. Make sure to keep these secret. We'll be using them later. Before starting this recording, I also added a phone string field, to our direct to c users collection. So on top of the additional kind of required fields, we now also have a phone number being stored. So let's get started with our flow. Inside of directors, create a new flow. I'm going to call this one validate phone is mobile. We're going to make this an event hook, but, critically, we're going to make it a blocking hook, which means all of the logic of this flow will be executed before the database transaction is made, meaning that if we create some form of error inside of the flow, the user will not be created. Inside of collections, make sure direct to users is ticked and, and that the scope is items dot create. You may additionally choose to run this logic on items dot update, but for now, we'll stick with create. So once a user is created, let's actually do that. All we're gonna do is add a phone number here. Here we go. We'll hit check, and we'll refresh just to see what that data looks like. We see that inside of payload are all of the fields that had data entered into them, including, and in this case, only phone. So there's our phone number there. Twilio also provides some tools to help you format phone numbers that may be formatted inconsistently, but that's outside of the scope of this episode. So we are formatting this as required by Twilio with the plus, the country code, and then the phone number itself. Let's go and do a lookup. So we'll call this lookup lookup And we're going to do this as a webhook request URL, a get request to this URL which will look up phone numbers And then in here, we're going to insert the phone number of the new user. So trigger dot payload dot phone. We will get some data back if we stop here, but we're additionally going to add the line type intelligence data package, I think they called them, which will provide more information about the number, including what we need. So field capital f equals line type intelligence. Fantastic. Now we need to authenticate ourselves with our SID and our auth token authorization now authorization. Now this is important because what Twilio require is what is known as basic h HTTP auth and flows only, only support adding headers. So what we're gonna do here is use the word basic, and now we need to turn our SID and token into a value that will be supported in this format. This is how you do it on the terminal. So you start with the first command, you store your Sid. So you replace this with your Sid, you replace this with your token, and then you run this command in your terminal with the dollar signs. And what that will do is spit out a string, which you can use. I've done this ahead of time. So heading back to our flow, we provide the output of what was in our terminal for our auth. Let's hit save now, and let's run this to test it. So let's once again create a new user. We'll put in a valid phone number. We'll go back to our flow and refresh to see our new log, and we'll look at the latest run. And we see here that inside of data, we have some information like the country call, the calling country code, the country code, the actual two digits for the for the country, the national format, the international format. But what we also have here is the line type intelligence, which was the data packet that we asked for. Now for some applications, it's important that the phone number provided is a mobile, isn't a virtual number, and can support SMS, of which type mobile is true. There's a whole list in the Twilio documentation as to which types can exist. But right now, for this example, we're going to check that mobile exists. So what we're gonna do here is check inside of data, line type intelligence type, and make sure that that is mobile. So let's add a condition in here, and what we're gonna do is check that the lookup data Line line type intelligence, like so, is, there was one inside of it, type. That's right. Type is equal to mobile. Fantastic. So this is us just traversing that object that Twilio provided back. We'll hit save. And so when it's true, we're actually going to add nothing. We're going to say, hey. This flow concludes. There was no error. This was blocking. Now it isn't blocking anymore. Go create the user. But in the case that it fails, we want to stop this in its steps. And the way we're gonna do that is we're gonna run a script. It'll not terribly elegant, but we're just gonna throw a new error. So throw new error. Incorrect, phone type must be a mobile. And let's save let's try this once more let's create the user and this should be successful Fantastic. And I don't have another phone number to test it with, but that means all of this was successful, and we went out the success path, which then just, resolved. And if that was not true, if this condition validation was not met, we would run the script and throw an error. Now there's a lot we can do with the lookup API, actually. You can check which carrier it is. You can check the fraud risk of a phone number. And all of those, basically, here's all the things we can do. The SMS pumping risk, whether or not the number has been reassigned, whether or not there's been a SIM swap, and so on. And all you would do is add extra query parameters to the end of the lookup and then change the conditional to check that certain criteria are met. So this was a little simple example of using the Twilio lookup API to validate phone numbers against some property that is provided by Twilio. You can tweak this for your specific use cases and build more secure applications. Hope you enjoyed this episode of quick connect, and I'll see you in the next episode.",[4733],"3444e114-c095-4239-8513-7dd6334394e9",[],{"year":2380,"number":194,"id":4605,"episodes":4736,"show":4737},[4607,4608,4609,4610,4611,4612,4613,4590],{"title":1746,"slug":1747},{"id":4544,"slug":4739,"vimeo_id":4740,"description":4741,"tile":4742,"length":674,"resources":12,"people":4743,"episode_number":159,"published":4749,"title":4750,"video_transcript_html":4751,"video_transcript_text":4752,"content":12,"seo":12,"status":19,"episode_people":4753,"recommendations":4757,"season":4758},"ali-payne","896619497","Ali shares his relentless passion for technology and his commitment to staying abreast of current trends and developments. Tracing his journey from Ohio to the tech hubs of the West, Ali discusses how he perceives every technological challenge as a puzzle waiting to be solved.","82aad3bf-6a5b-44da-8c1b-1f98c5f8efba",[4744,4745,4746],{"name":4361,"url":4522},{"name":4524,"url":4525},{"name":4747,"url":4748},"Ali Payne","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fali-payne\u002F","2024-02-08","Ali Payne, Lead Engineer at Stitch Fix","\u003Cp>Speaker 0: Someone is working constantly just in a different time zone. But it would be nice if you could just go in there and pair program with them with the VR headset, just sitting back on your couch air typing. I mean, if they don't automate our job before that. But hey.\u003C\u002Fp>\n\u003Cp>Speaker 1: Hey, I'm Pedro, and we got Jonathan here and Ollie. Welcome to another episode of Trace Talks, where we interview some thought leadership and engineers on their experience, and hopefully learn something along the way. So, yeah. Ollie, I'd I'd like to give you the opportunity to introduce yourself and maybe a quick, you know, minute, 60 second background, and we'll dive right in.\u003C\u002Fp>\n\u003Cp>Speaker 0: Quick background. I'm a lead engineer currently at, Stitch Fix. I've done everything from front end, back end, data engineering, CTO, startups, big companies, contracting. And, hey. I enjoy what I do.\u003C\u002Fp>\n\u003Cp>What more can I say?\u003C\u002Fp>\n\u003Cp>Speaker 1: That's awesome. Yeah. John John, I'm gonna go ahead and take Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 2: And I I'd love to understand, I guess, since you love what you do, we we have these conversations, and everyone seems to love a different, a different reason why they do what they do, but we can just start there. You know, what what, you know, what got you into engineering, and and why you love it?\u003C\u002Fp>\n\u003Cp>Speaker 0: Funny enough, I got into engineering through recruiting. My very first job, I I my first year is in psychology. I was always interested in people, and I worked for a recruiting firm that actually built their own proprietary software. And from there, I kinda found out that you could build anything, and you could put it online and anyone could see it. So that's kinda what got me, started and interested.\u003C\u002Fp>\n\u003Cp>Prior to that, I didn't really have any any idea, to be honest with you. To answer the question, like, what gets me interested is I I look at technical problems or just problems in general as solving a puzzle. And being in technology, you get to solve puzzles in the comfort of your own home.\u003C\u002Fp>\n\u003Cp>Speaker 2: Or in a hotel room. Either either where wherever you are.\u003C\u002Fp>\n\u003Cp>Speaker 0: Hey. Remote is remote in today's world.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. For sure. 1st, for some of those early engineering projects and, like, as you started to get into into engineering, was there any overlap between psychology and engineering that you sort of took advantage of or explored in those early days as you made that transition?\u003C\u002Fp>\n\u003Cp>Speaker 0: I will say one thing that psychology majoring psychology or even taking psychology serious, just human interaction in general, I think has been the one differentiating factor for me in an engineering career. Like, no matter where I've gone and I I did my fair job shop or job hopping. I'm not gonna lie. But no matter where I got, I was enough of a communicator to be able to galvanize the troops. Or, you know, like, make the friends you needed to make, find out who is the who, and then just get the conversations going about, hey.\u003C\u002Fp>\n\u003Cp>Can we get the troops going to do something fun? Or, like, can we, like, gear our engineers against the project managers in some regard? Not for, like, a negative, but just to be like, you know, there's some smart people that just don't say anything just because, you know, the, you know, we're engineers. We're we're here to not talk.\u003C\u002Fp>\n\u003Cp>Speaker 2: That must be a different experience because our engineers love to talk. They love to tell jokes.\u003C\u002Fp>\n\u003Cp>Speaker 0: Well, I mean, once you get them going, they won't shut up. But, you know, the initial you know, like, I I one of the best people I ever worked with. He had to train himself, to look people in the eye. And, you know, just from the aspect spectrum of just how people think, it's not too infrequent that you get someone who's not like the the stellar communicator, but is a wiz problem solver.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Was was this person someone who is like a mentor to you, or was it someone you just worked with?\u003C\u002Fp>\n\u003Cp>Speaker 0: It was my favorite mentor, my number one mentor, Doug himself. And, I'll say that one of the the best lessons I learned from him, at least mentoring others, is sometimes the best direction in leadership is none at all. To see where you land and just help.\u003C\u002Fp>\n\u003Cp>Speaker 1: Just get out of the way.\u003C\u002Fp>\n\u003Cp>Speaker 0: Get out of the way, let you fail, and then come around and be like, hey. Do you need help? I gotcha.\u003C\u002Fp>\n\u003Cp>Speaker 2: Is that a style of leadership that you you like where you would prefer it? You know, obviously, there's times where you would want a little bit more interaction with leadership, your leadership. But as you've climbed the ranks and gone from place to place, is that something that you react more openly to, a leader who kind of lets you do your thing? Or would you prefer or is it a case by case scenario where some projects you need a little bit more hands on? And then I guess as you're starting to be a lead engineer yourself or as you're in this role now, for people who kind of look up to you in a leadership role or as your as their mentor, do you lead them the same way as well?\u003C\u002Fp>\n\u003Cp>Speaker 0: I'll say my leadership style is just a little it's a little different, because I also did, like, coaching. I played sports a while, a long time. And and I like to one of the biggest lessons I learned in any leadership is, like, people don't care how much you know until they know how much you care. And oftentimes for them to find that out, they have to put themselves in a bad position. And in engineering, nothing better than a deadline, missing a deadline, or telling some project manager that you completely understood the scope.\u003C\u002Fp>\n\u003Cp>And then once that happens, you can guide people into your loving support and care as a leader and then groom them. And once they can see you as that type of caring individual, not to say that it's a a facade or anything. It's it's a genuine care. It's just that that's kinda how I've learned to lead in a more of a passive passive way. I mean, everyone likes if you can do the work, you know, but if you can make an excuse for someone to make them look like a hero, hey.\u003C\u002Fp>\n\u003Cp>That's real leadership.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. That's awesome.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. And you're probably one of the first people we've talked to who's been who who has mentioned sports in the background. You know? I played sports when I was a kid. We have someone on our team who actually was a former NFL player.\u003C\u002Fp>\n\u003Cp>We find that those types of people, they understand, number 1, how to be a team player. They know hard work because you can't play sports without working hard or you're not gonna play it long. But you also they also seem to understand when to step up and when to step up into a leadership role, whether it's temporarily or permanently as a captain or something like that. So I guess is that something that you think came from your sports background, or is that something that you think you had before sports? It helped you enjoy sports a little bit more, and it's just transitioned into the current place that you're at now.\u003C\u002Fp>\n\u003Cp>Speaker 0: Well, I'll say this. When I was a kid, I was uncoachable. I was that uncoachable guy where he's like, why is he on the bench, man? He's so good. Uncoachable.\u003C\u002Fp>\n\u003Cp>You know? Just knew everything. So, you know, as you age, you mature, and I got to do this a little bit more than I got to do sports, I feel. And I've just come to that conclusion, you know, that that, like, some of I don't know who quotes it, but some people say, like, you don't hire smart people to tell them what to do. You hire smart people to figure out your problems.\u003C\u002Fp>\n\u003Cp>And most of the people I've worked with, I I've been on, like, excellent teams or all of them have been smarter than me. I might just be able to articulate a little bit better their idea or their concept. But when you don't want the credit and you just want the team to win, like, that's how you build things. And it's just an interesting perspective from you know, you go to engineering. Most people that have been engineers their whole life, you know, it's kind of a set like, a solo pursuit.\u003C\u002Fp>\n\u003Cp>You know? You you you find them with the computer. Like, I'm on the computer all day. I don't get to talk to people. But then, like, if you can reach out and it's like, no.\u003C\u002Fp>\n\u003Cp>Let's do a community. Like, one of the best team building things I ever did, was at a startup at Zipseam, and we went and saw Deadpool together. And it's like I yeah. I still got memories from that. You know what I mean?\u003C\u002Fp>\n\u003Cp>And it's just, I don't know. Everything's about team building at the end of the day. And it's and and all the problems that we all have, all the industries are all the same. It's all communication. Anything solvable.\u003C\u002Fp>\n\u003Cp>It's just can we communicate with each other, and can we make each other feel all wanted and put each other in the right positions.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. If you were to if you were to rank like the things you do as a leader, for your team, like, do you think those team bonding experiences, those team outings are the things that really foster that, sense of community and and, and creativity within the team that'll then allow you to work better together on on certain projects. Like, how does that you know, how would you stack that up against some of the other things that we've been talking about?\u003C\u002Fp>\n\u003Cp>Speaker 0: Well, one of my favorite teams I ever went on, we all came from a different background. Every single one of us was from a different background. But we went out to eat every day. Like, not every day because, you know, you can't eat out every day, but, you know, we ate out a lot, and we spent a lot of time together. And the project we might have been working on varied.\u003C\u002Fp>\n\u003Cp>It was contracting various data things. But what brought us together was just coming together and bonding kinda like how a family has dinner today. Well, you know, you'd want to have dinner every night, you know, groom your children for whatever, but it's it it seems like it's just time spent together. And one of, like, the industry issues, at least since I've jumped around, I've been part of the issue, is is really interesting is it's like a lot of these legacy systems, a lot of these long built building things are just they couldn't keep their original engineers there, so we're all resolving the same problems. You know, but that that's just life.\u003C\u002Fp>\n\u003Cp>So I just think it's just anything about time spent together. Like, it seems like you guys got a pretty good bond together. Had there been time spent together in the trenches. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. That brings up a good question because Pedro and I have have we've been around the block together. We've been in 3 places together. Matt is now part of our trio. We've we've inherited him, one of our marketing guys.\u003C\u002Fp>\n\u003Cp>But how do you find that that's how do you find to do that in the remote world that we're in? Because Pedro's in LA. I'm in Nashville. Some other coworkers are in New York or DC. And when we're in when we're in person, there is that it it feels like no time has passed, and we're on calls all the time together or we'll message each other all all the time.\u003C\u002Fp>\n\u003Cp>But, you know, we're we're we're sales guys by heart. You know, we have some other skills as well. But how do you find that that works with engineers who are typically in their in their houses and like to be remote and stay quiet a lot of times? How do you build that camaraderie or that team in a remote world?\u003C\u002Fp>\n\u003Cp>Speaker 0: Well, some of it, like I mean, we had a, I think it was this Christmas. We had, like, a, you know, a Christmas day. Just online playing games, you know, bingo, bang, playing games, just talking together or just like our daily meetings. Right now, I'm on a team where we have Colorado. We have Austin.\u003C\u002Fp>\n\u003Cp>We have Michigan. I'm in Ohio or Nevada. And then we got, you know, San Francisco. And it and it's, like, interesting things that happen, like, not to be negative, but, like, the Amber alert. That go off for 1 of us, then it goes off for all of us, or, like, the weather or even, like, you know, local events that are happening, and you can just kinda engage yourself with other people.\u003C\u002Fp>\n\u003Cp>And it allows you to be, I would say, like, a more well versed person just, like, nationally. And then it it kind of buys into, like, hey. You know? No. I got my friend in I got my friend in Texas, man.\u003C\u002Fp>\n\u003Cp>Like, what this is what's going on on the ground. And I think that's the way to kind of build it. And it and it's like, you you have to think, my little brother, like, during the COVID, had to go remote school. You know? I mean, he's 1st grade, just typing on a computer all day.\u003C\u002Fp>\n\u003Cp>And, like, I I hate to tell people, well, this is just the world that we're probably gonna live in. Yep. So we might as well take advantage of it. And truth is if you look at, like, the statistics or the studies behind remote working, which a a lot of executives probably won't bring up, but you'll work more remote than you will actually in the office. Like, I'll find myself here late at night.\u003C\u002Fp>\n\u003Cp>Just gotta get it done. Gotta get it done. And you turn your office or your house into your own you know, you're working around the clock. So it's a catch 22. It's an interesting conversation.\u003C\u002Fp>\n\u003Cp>But I see you just build build teams by being together. You know, being a person, being normal.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Yeah. I definitely feel that. I I actually have to in my office, I have my lights programmed to change color at, a certain time so that I make sure to, like, hit the gym. And, of course, sometimes I'll ignore it, but it's just, like, at least having that mental marker, like, well, I was supposed to stop working about 30 minutes ago because I gotta start working again in a couple hours and take a call with, somebody in Malaysia or something.\u003C\u002Fp>\n\u003Cp>But, yeah, I like what you're saying about, like, these these team outings, especially about the gaming thing. Like, I know a lot of friends who connect with their coworkers, through gaming, through playing games together online and stuff. And like you said, it's it is the world we live in, and I guess that's the the bet that companies like, you know, Meta are taking that maybe this is it's trying to bring that in person experience into the online world with, like, VR and stuff. And I don't know. Like, do what what's your perspective on on that?\u003C\u002Fp>\n\u003Cp>Do you think that VR and these kind of future technologies can can accomplish that same goal of, like, going to all see Deadpool together, you know, but, like, in a headset or, like, the Apple with the Vision Pro coming out, that sort of thing?\u003C\u002Fp>\n\u003Cp>Speaker 0: So interestingly enough, I, like, I wasn't sold on it at first. Right? Even though I had, like, one of those VR boys back in the day where I'd play tennis on it and everything. I was never sold Tango 1? Yeah.\u003C\u002Fp>\n\u003Cp>It was like a Yeah. Virtual Boy, I think.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Virtual Boy.\u003C\u002Fp>\n\u003Cp>Speaker 0: But I so I was never sold on it. But then so I'm out here in Vegas, and I went to see, King Tut, a virtual 3 d, thing down at, I think, Mandolin Bay. And you put the goggles on it, and it's the Oculus, and you're exploring Egypt. Just walking around, just playing with things. And then, like, my friend had one of those PlayStation VRs, and you could cook and you could do the the everything in it.\u003C\u002Fp>\n\u003Cp>And it's like I've seen some of the things where you could actually set up your office 3 d in a manner. And, like, I could have my 9 monitors or how we're monitors, and I could be in a virtual workplace together with people. So for for in that regard, like, kinda like NFTs or even cryptocurrencies. It's the utility of it, not the actual concept of it, I think. And I think more and more people will because there's there's just things like because we're so remote, like, we're in different time zones.\u003C\u002Fp>\n\u003Cp>Like, we even have contractors in the Philippines. So we're like, someone is working constantly just in a different time zone. But it would be nice if you could just go in there and pair program with them with the VR headset, just sitting back on your couch, air typing. I mean, if they don't automate our job before that but hey.\u003C\u002Fp>\n\u003Cp>Speaker 1: Right.\u003C\u002Fp>\n\u003Cp>Speaker 0: You know? I I love the the VR aspect of that.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. No. I I like what you're saying about the utility of it. Right? Like, the technology, the hardware, the software, that's all clearly going to get there to a point where it's like, you know, that you can't tell the difference between that and real life essentially, but it's it's the utility in the application, like, what are the companies going to build, to actually be able to do in there and and actually make you feel more together, in a not so cartoonish way.\u003C\u002Fp>\n\u003Cp>Right?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Like, even us just using faces with our with the podcast. I mean, I feel more connected to you than if it's not. And a lot of policies that we're putting in place at Stitch Fix is you have to turn on your camera. You know?\u003C\u002Fp>\n\u003Cp>Like, I wanna see your facial expressions because I wanna know if this is a terrible idea.\u003C\u002Fp>\n\u003Cp>Speaker 1: You know? Right.\u003C\u002Fp>\n\u003Cp>Speaker 2: Or maybe you shouldn't know. But, yeah, I think there's I think I think there's some things where it makes sense. You know? Like, maybe watching a movie with a friend who's across the country or across the globe. But you're in Vegas.\u003C\u002Fp>\n\u003Cp>I don't think I ever wanna miss the feeling of the the the slots hitting or playing craps. I don't think I could do that virtually. Yeah, I think there's some things that would be easily moved into the a fake world or a a VR world. There's some things that I feel like just should never never be replaced. So I'm kinda curious to see where that goes.\u003C\u002Fp>\n\u003Cp>I I used to work for Apple, and the VisionPRO certainly interests me, but I don't know. It's starting to get creepy how real stuff looks. But but, yeah, you mentioned your 9 monitors. Like, if you if you could have your entire room just full of monitors, you're. But then again, we're already I think all 3 of us are over workers.\u003C\u002Fp>\n\u003Cp>So do I wanna have a computer monitor up while I'm washing the dishes? I don't I don't know. What about you? Do you think it'll cause even more overworking potentially? Or I think we're all over workers anyway, so it doesn't matter for us 3.\u003C\u002Fp>\n\u003Cp>We're already past the point of saving. Yeah. What about for others?\u003C\u002Fp>\n\u003Cp>Speaker 0: I mean, I don't think you can you can just make it convenient. You know? Like, the one thing that I wanted to do, like, maybe 2 years ago, I was gonna get, my cousin one of those headsets because you could both sit, like, front row at an NBA game and talk to each other like we were next to each other. You know what I mean? But, like, if you could all create a virtual environment workplace that's ideal and you could mix it with your home.\u003C\u002Fp>\n\u003Cp>You know what I mean? Like, you could, in a sense, maximize someone's output and make them to be comfortable. And not maybe because that's probably the best idea, but more about because, like, you know how, like, with with life or with future, it always leans one way and leans the other way. And, like, I see, like, what I went through as the job hopping phase, And I feel like companies are just gonna be tripling down on, like, longevity of employment again because we went so far left. We need to go right and readjust.\u003C\u002Fp>\n\u003Cp>And a lot of our issues could just be solved. We just didn't have to keep getting people over and over again.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. Yeah. Do you see that as a problem now that you're climbing into leadership that that VR could solve? You know, you I think it is easy to job hop when you don't feel that connection with your coworkers or with your leaders. But if somebody if you're wearing a VR headset and somebody comes and whispers in your ear, you know, you turn around and there's a person virtually there, do you think that could solve that that kind of ease of job hopping when you just don't feel like you you anybody would miss you?\u003C\u002Fp>\n\u003Cp>I don't know what the right way to put it, but I do feel like there's a just a disconnect with feeling a part of your business if you don't inter interact with other people.\u003C\u002Fp>\n\u003Cp>Speaker 0: I I I will say this. I don't know how VR VR might help. You know what I mean? Just because you would be around people, and I think that always is a is a positive. But with, with job hop, at least if I could give any advice while we're on here, and if anyone is listening to this, I've been in a pleasurable situation where I've been on a team where it's like, hey, you're groomed for for executive leadership.\u003C\u002Fp>\n\u003Cp>Right? Like, we know that you're gonna get there. We're putting you on a team. Everyone's gonna be moved up. I've also contract in positions where I I I was working to groom a certain individual who was going to move up the corporate ladder.\u003C\u002Fp>\n\u003Cp>Like, they had already picked them out to be a CEO, and they just didn't know. I I and and it's like it bewilders you because you asked them, like, does he know or does she know? They're like, no. We haven't told him. I was like, don't you think?\u003C\u002Fp>\n\u003Cp>And they'll they'll end up leaving, and you'll be like, oh, man. You guys lost a a CEO you were grooming. Like, that's kinda big. You know? Like, I mean, but since you didn't tell them, I guess, no one knows no loss.\u003C\u002Fp>\n\u003Cp>But I think that the the thing that could just is just timing. You know? Like, I think a lot of people need to be more transparent about, like, hey, what they want out of their career and where they wanna be. And then, like, also companies should be like, hey. We see you as a leader.\u003C\u002Fp>\n\u003Cp>Like, one of the one of the greatest losses I ever saw. Like well, I mean, I did it too, and I was the same person, so I understood why they left. But I was like, man, looking back, you probably shouldn't have left. But it's just something that you learn. You know?\u003C\u002Fp>\n\u003Cp>I mean, you learn with time, and I just think the industry's gonna change just because it's been this way for so long that just the yin and yang effect of anything. Just the universal law is like, if everyone's leaving, then there has to be a period of time of us. We might not get Rolexes again, but, I mean, they're gonna want us to stay around.\u003C\u002Fp>\n\u003Cp>Speaker 2: We can try and get Rolexes. But yeah. So so what do you what do you think? You mentioned one really good point that somebody else has mentioned in one of these podcast recordings too, letting letting leadership know your intentions of wanting to climb the corporate ladder. So what would be some good advice that you give having, you know, done these types of things yourself as well?\u003C\u002Fp>\n\u003Cp>But for those engineers who would be listening or those who want to get into engineering and would eventually want to climb as well, What's some good advice that you would give, you know, people who are wanting to climb the the corporate ladder or the the leadership ladder?\u003C\u002Fp>\n\u003Cp>Speaker 0: Well, one one thing I would always tell anyone is variety is this is spice of life is variety, however you wanna put it. So experiencing everything is key. Right? Like, if you don't know what you've tasted, then you don't know what you want. So early in your career, like, for example, I did front end.\u003C\u002Fp>\n\u003Cp>I did back end. I did data engineering. I tried to start my own company. I did a little sales. I was in recruiting for a while.\u003C\u002Fp>\n\u003Cp>Like, I did, the drawing. I did the UX for a while. It's like knowing and and then once you know how to do everything, not to say you should know, but once you have that that skill, like, you can kinda tail you you 1, you can see where it all leads to, but more importantly, you can find out what it is that you like. So I think once you find out what you like, like, for example, I was a data engineer, and I'd go to data visualization, like, front end meetings. And, like, my managers would say, hey, Ali.\u003C\u002Fp>\n\u003Cp>Why are you in this meeting? And I'd be like, well, I'm working on a side project doing data visualization. Anyway, I figured what's what's the harm of it. And if I know how the 2 need to to speak, then that's a win win. So just going out of your going out of your way, I mean, if you're young enough and you don't have a family or anything or any obligations, and we're still in the office, showing up early is always a good thing.\u003C\u002Fp>\n\u003Cp>One of the tricks that someone told me, and it was my sales career, was always work a Saturday. You know? Come in and work a Saturday because if you work a Saturday, you know, a, you pick up everything. You might be able to relax a little bit during the week, but no one can get rid of the guy who works on Saturdays. And everyone remembers the call on Saturday.\u003C\u002Fp>\n\u003Cp>For for moving's perspective, we just got a so Citrix, we got, Tony, Tony and Matt, but Tony's been, just because he just spoke recently. He's talked about how he transitioned from coding to executive leadership. And, really, it was it was more about just aligning yourself with a business or understanding, especially if you're an engineer. Like, when you're young, you think like, hey. I gotta solve this.\u003C\u002Fp>\n\u003Cp>This is the most important thing in the world. You know, like, no one else can solve this. I'm gonna do this in such a creative way. You know how you you think, like, you're this god complex that comes from solving a few engineering things. But then when you you advance a little bit, you you can kinda take a step back and look at the whole picture, and it's like, it's not it's about the business.\u003C\u002Fp>\n\u003Cp>Because if the business doesn't succeed, you don't have your engineering job. So how can you improve the business? Is there something you could do to improve our business, our organization? Could you open source something? Could you, like, have a conversation with an outside company like yours, you know, that's doing some very interesting things in the industry to put our name out there?\u003C\u002Fp>\n\u003Cp>Just just other ways you can add value because if you're gonna separate yourself, it's gonna be like, the power of 3, like, your your your personality, your work ethic, and then that extracurricular killer stuff that you're doing on the side that also presents that. Oh. How?\u003C\u002Fp>\n\u003Cp>Speaker 2: So what about people like myself who work Saturday Sunday? You're saying no one can ever get rid of me.\u003C\u002Fp>\n\u003Cp>Speaker 0: Well, I mean, you just love it, man, and we're lucky enough to find something we love.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. So you're really\u003C\u002Fp>\n\u003Cp>Speaker 0: mad about that.\u003C\u002Fp>\n\u003Cp>Speaker 2: True.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. That is that is so true. I think John and I both have, you know, sort of nights and weekends projects as well. So, as as do, I think, most engineers. Right?\u003C\u002Fp>\n\u003Cp>Like, most engineers we talk to have nights and weekends project. We did another interview with someone who had done some DJ work and, and did, like, karaoke stuff. So, it's it's really cool to see you know, you do get to express some of the creativity that you can't put towards work that doesn't have a fit there. You could still express in another way, right, through music, through art, through, you know, coding on a personal project. Do you have anything like that that you exercise those skills with?\u003C\u002Fp>\n\u003Cp>Speaker 0: Well, right well, I mean, I shouldn't say it. Right now, I like to trade stocks a lot, because I see it as, like, a nonsolvable problem. You know? Because it's all math, but it's just based upon time series and where you're at and, like, if the patterns recreate. One of the best engineers I ever worked with, he was a an he made swords.\u003C\u002Fp>\n\u003Cp>So he's a fire. A fire maker. So I\u003C\u002Fp>\n\u003Cp>Speaker 2: was like, so what do you how\u003C\u002Fp>\n\u003Cp>Speaker 0: I was like, did you did you get that signed by Randy Jackson?\u003C\u002Fp>\n\u003Cp>Speaker 2: What a what a hobby.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. But it's like, you you know, you meet people who are like to take her. You'll find out all types of interesting, hobbies. So it's definitely something to to identify for or to ask like, hey. Are you working on these side projects?\u003C\u002Fp>\n\u003Cp>Like, that's back in the day. That's how I'd identify a winner.\u003C\u002Fp>\n\u003Cp>Speaker 2: It's funny you mentioned that because my my brother-in-law, who also is in technology, he's been in it longer than me. He used to make iron lamps, so lamps with pipes, and then there was a lamp at the top out of it. But that's how he met Steven Tyler, and he met a bunch of random people because he worked on this random side project. So it's kinda cool that somebody you knew made swords.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. I met somebody that somebody that I was in a band with started creating some art and then started creating, like, tapestries, these really complex tapestries. And all of a sudden, he gets a call from, like, Kanye's team to, like, design some, like, lion chain necklace. And all of a sudden, he's, like, the biggest thing in the in the rap and hip hop world because he's just like the go to artist for that kind of stuff, designing all these huge, you know, 10 foot tall tapestries for these guys' homes and stuff. So yeah, I mean, yeah, it's it's pretty cool.\u003C\u002Fp>\n\u003Cp>Speaker 0: 2 notes on 2 notes on that. 1, like, from my other big career after this, I realized that musicians, given the right background, they're always great salesman because they're always great communicators. They always have a great network. And it's like they can take you in the fun environment, the professional. So that's always one thing.\u003C\u002Fp>\n\u003Cp>And then 2, just for anyone like, hey. Just do whatever you like to do. You know? Like, I mean, this guy got paid to make tapestries. You know?\u003C\u002Fp>\n\u003Cp>Imagine you get paid to do something you love to do. You know? You should always pursue that. And, eventually, you know, you pursue it long enough, it'll be a niche.\u003C\u002Fp>\n\u003Cp>Speaker 2: I think we need to keep track, Pedro, of how many times music gets brought up on these conversations because music gets brought up. People who love to play music. I think it's a No. Engine it's an engineering trait. People just love to play different things.\u003C\u002Fp>\n\u003Cp>So\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. It's also it's also just math. I mean, music is is art, in a way, but it's it's mostly just math and chords and melody, and, it all just comes down to numbers. So I think the engineering mind does really well with that. I mean, we've got a number of musicians.\u003C\u002Fp>\n\u003Cp>We say it all the time. We've got a number of musicians here at, at Directus, and we've got I mean, I just this morning, I was on a call with with a prospect who is had a bunch of guitars in the background, so it's always cool to see.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Matt was giving away the secret on LinkedIn about stop at time just replaying the same song. And Yeah. I I will say I once worked with, I once worked with, I don't even know what it's called, but, like, a PhD in music. And they also were a a coder, and it was just like, to them, it made so much sense, like, the the synchrony between the 2.\u003C\u002Fp>\n\u003Cp>But to me, I just was like, I like listening to opera while I code.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Yeah. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. We, we're coming out with a just a series of videos with lo fi music because I I love listening to Oh, I love that. Jazz or music just on the side while I'm working. I think that's a very common thing. Opera is good.\u003C\u002Fp>\n\u003Cp>A symphony. You can't beat a good symphony. So\u003C\u002Fp>\n\u003Cp>Speaker 0: No. Something with pain. Something just with, like, like, or classical. I've been listening to lofi Tupac. Not gonna lie.\u003C\u002Fp>\n\u003Cp>Speaker 1: Lofi Tupac. Where can I find that? YouTube. Awesome. I saw I saw they did, I don't know if you've seen on YouTube.\u003C\u002Fp>\n\u003Cp>There's a series where a guy takes, Super Mario 64 sounds and recreates entire albums. So he took, like, one of the Radiohead albums and used recreated the whole album with just the sound bank of Super Mario 64. And he's done it with a number of classic albums like Nirvana, and, it's it's pretty pretty interesting. Sweet. Yeah.\u003C\u002Fp>\n\u003Cp>How random. People have a lot of time on their hands. But yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: No. You brought up, like, old video games. I remember my very first startup. It, like, was my internship, and I used to play Zelda. Right?\u003C\u002Fp>\n\u003Cp>Because I was always into video games. I played Zelda for, like, 12 years just to beat it. I remember, like, how hard it was. 1st day 1st week. Yeah.\u003C\u002Fp>\n\u003Cp>We, speed play Zelda. Like, the record was, like, 22 minutes. I said, golly.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. There's so many side quests. You have to do all the side quests.\u003C\u002Fp>\n\u003Cp>Speaker 0: I don't know how they beat it that quick, but it's just funny to say, like, at least I I don't know how long we have, but, like, hey. If you're not into tech, you should still get into tech because everything is tech. And that's how I benefited so much, and that's why psychology was so beneficial for me, as I've explored my direct career. Mhmm. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 2: That that's awesome. As we start to wrap up, I'd love to under I'd love to learn about real briefly, you know, what are what are your goals? And do you see yourself you talked about grooming a CEO or someone who is going to be that. You know? What what are your end goals?\u003C\u002Fp>\n\u003Cp>Where would you like to see yourself, you know, climbing more into leadership, getting more into an individual contributor role, both? You know, I guess, what are what are your goals?\u003C\u002Fp>\n\u003Cp>Speaker 0: My goals really, lately has been just, like like, trying to find people who are like me, younger, you know, and just give them everything that I've picked up along the along the journey just to make a lot of people's journey shorter. And I've come to realize, like, so I've had a privilege of working at Stitch Fix and also 8451, which was big on, like, women led organizations or, like, women involvement and, you know, like, you know, team involvement. It's just a diversity of a team in general. And it's like there's several problems in tech. I mean, like, we've we've proven, I think, that anything's solvable.\u003C\u002Fp>\n\u003Cp>The the problem now is like, hey. Do we have the, like, let's say, the dynamic the the demographics to create all problems? Because once we create the problems, then we can create solutions. So it's just more of for me right now, I'm just trying to I'm I'm most interested in getting more people into tech, that look and don't look like me. Just just so, you know, like, well, for 1, I don't know what else they're gonna do.\u003C\u002Fp>\n\u003Cp>But for for for 2, you know, like, it's not as hard as you would think. You know? Like, it it's not. It's really just solving puzzles. It's just we make it difficult because we like to be elitist.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. Yeah. That's really good point.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 2: Does it require 9 computer monitors to be as good as you?\u003C\u002Fp>\n\u003Cp>Speaker 0: I mean Or 12.\u003C\u002Fp>\n\u003Cp>Speaker 2: Some of these Just like you gonna be better.\u003C\u002Fp>\n\u003Cp>Speaker 0: Just like in monopoly, man. Like, it's all about real estate, man. And the more screen real estate you got, the better.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. So it sound I\u003C\u002Fp>\n\u003Cp>Speaker 0: after you. My bad. My bad.\u003C\u002Fp>\n\u003Cp>Speaker 2: I I was gonna say it sounds like you need the VisionPRO from Apple coming out because in your whole you're everywhere you look, you can have screens.\u003C\u002Fp>\n\u003Cp>Speaker 1: You just loop around in a chair and see screens all over you. Right?\u003C\u002Fp>\n\u003Cp>Speaker 2: Or you're you're in Vegas. Just go to the sphere and put all your monitors on the sphere.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. The the the, ironically enough, the one jealous, the the the most jealous I've ever been as a developer. We had a, we had a guy who you could tell he had it. You know? Like, you can just see it.\u003C\u002Fp>\n\u003Cp>Like, you can just see it. But he only worked on one one screen, and I had to pair a program with him. And it was just like, man, how do you do this? But he had the advantage because it's like, sometimes I will not be able to work if I don't have a bunch of monitors, but he could work anywhere at any time. So just go camping on this monitor, and it's like so it's like there's it's like everything, man.\u003C\u002Fp>\n\u003Cp>There's wind and there's plus and losses about the whole monitor con conversation.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. Well, awesome. I think I think it came across in this conversation, your real passion for engineering, and that's why you've gotten into it, but also your passion for people. So for for people who are listening, you know, how can they follow you? Where can they get in contact with you if they wanna get in contact with you?\u003C\u002Fp>\n\u003Cp>Is it LinkedIn or other things as well? Let let people know\u003C\u002Fp>\n\u003Cp>Speaker 0: where you're at. I usually just use LinkedIn to be quite honest with you. I have a YouTube, but I don't really do anything on it. It's just more for me to play around with. I've been playing with the algorithm on both of them.\u003C\u002Fp>\n\u003Cp>But it would just be LinkedIn. Just, I think it's just Ollie Payne. LinkedIn slash Ollie hyphen Payne, a l I hyphen p a y n e. But, you know, I'm focusing on those 2, man. I'm not gonna venture out too much.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. Yeah. Well, we have little time we have little time for extra even projects, but so it's interesting that you even do algorithms. But, I think I can I think I can speak for Pedro? This has been a fantastic conversation.\u003C\u002Fp>\n\u003Cp>I feel like every conversation we have for this podcast is almost completely different, and yours was more so around kind of compassion for people and compassion for engineering. So I think we we both greatly appreciate the time, and we'll put your contact info in the the description, but definitely think that people can find some some good tidbits from what you've said today. So definitely appreciate the time.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Well, hey.\u003C\u002Fp>\n\u003Cp>Speaker 0: Hey. I appreciate talking to both of you, and, hey, they have a good product too.\u003C\u002Fp>\n\u003Cp>Speaker 2: Well, hey. You guys have good clothes. So if people need clothes, check out Stitch Fix.\u003C\u002Fp>\n\u003Cp>Speaker 0: Hey. Win win. Love the plugs. Appreciate it.\u003C\u002Fp>","Someone is working constantly just in a different time zone. But it would be nice if you could just go in there and pair program with them with the VR headset, just sitting back on your couch air typing. I mean, if they don't automate our job before that. But hey. Hey, I'm Pedro, and we got Jonathan here and Ollie. Welcome to another episode of Trace Talks, where we interview some thought leadership and engineers on their experience, and hopefully learn something along the way. So, yeah. Ollie, I'd I'd like to give you the opportunity to introduce yourself and maybe a quick, you know, minute, 60 second background, and we'll dive right in. Quick background. I'm a lead engineer currently at, Stitch Fix. I've done everything from front end, back end, data engineering, CTO, startups, big companies, contracting. And, hey. I enjoy what I do. What more can I say? That's awesome. Yeah. John John, I'm gonna go ahead and take Yeah. And I I'd love to understand, I guess, since you love what you do, we we have these conversations, and everyone seems to love a different, a different reason why they do what they do, but we can just start there. You know, what what, you know, what got you into engineering, and and why you love it? Funny enough, I got into engineering through recruiting. My very first job, I I my first year is in psychology. I was always interested in people, and I worked for a recruiting firm that actually built their own proprietary software. And from there, I kinda found out that you could build anything, and you could put it online and anyone could see it. So that's kinda what got me, started and interested. Prior to that, I didn't really have any any idea, to be honest with you. To answer the question, like, what gets me interested is I I look at technical problems or just problems in general as solving a puzzle. And being in technology, you get to solve puzzles in the comfort of your own home. Or in a hotel room. Either either where wherever you are. Hey. Remote is remote in today's world. Yeah. For sure. 1st, for some of those early engineering projects and, like, as you started to get into into engineering, was there any overlap between psychology and engineering that you sort of took advantage of or explored in those early days as you made that transition? I will say one thing that psychology majoring psychology or even taking psychology serious, just human interaction in general, I think has been the one differentiating factor for me in an engineering career. Like, no matter where I've gone and I I did my fair job shop or job hopping. I'm not gonna lie. But no matter where I got, I was enough of a communicator to be able to galvanize the troops. Or, you know, like, make the friends you needed to make, find out who is the who, and then just get the conversations going about, hey. Can we get the troops going to do something fun? Or, like, can we, like, gear our engineers against the project managers in some regard? Not for, like, a negative, but just to be like, you know, there's some smart people that just don't say anything just because, you know, the, you know, we're engineers. We're we're here to not talk. That must be a different experience because our engineers love to talk. They love to tell jokes. Well, I mean, once you get them going, they won't shut up. But, you know, the initial you know, like, I I one of the best people I ever worked with. He had to train himself, to look people in the eye. And, you know, just from the aspect spectrum of just how people think, it's not too infrequent that you get someone who's not like the the stellar communicator, but is a wiz problem solver. Yeah. Was was this person someone who is like a mentor to you, or was it someone you just worked with? It was my favorite mentor, my number one mentor, Doug himself. And, I'll say that one of the the best lessons I learned from him, at least mentoring others, is sometimes the best direction in leadership is none at all. To see where you land and just help. Just get out of the way. Get out of the way, let you fail, and then come around and be like, hey. Do you need help? I gotcha. Is that a style of leadership that you you like where you would prefer it? You know, obviously, there's times where you would want a little bit more interaction with leadership, your leadership. But as you've climbed the ranks and gone from place to place, is that something that you react more openly to, a leader who kind of lets you do your thing? Or would you prefer or is it a case by case scenario where some projects you need a little bit more hands on? And then I guess as you're starting to be a lead engineer yourself or as you're in this role now, for people who kind of look up to you in a leadership role or as your as their mentor, do you lead them the same way as well? I'll say my leadership style is just a little it's a little different, because I also did, like, coaching. I played sports a while, a long time. And and I like to one of the biggest lessons I learned in any leadership is, like, people don't care how much you know until they know how much you care. And oftentimes for them to find that out, they have to put themselves in a bad position. And in engineering, nothing better than a deadline, missing a deadline, or telling some project manager that you completely understood the scope. And then once that happens, you can guide people into your loving support and care as a leader and then groom them. And once they can see you as that type of caring individual, not to say that it's a a facade or anything. It's it's a genuine care. It's just that that's kinda how I've learned to lead in a more of a passive passive way. I mean, everyone likes if you can do the work, you know, but if you can make an excuse for someone to make them look like a hero, hey. That's real leadership. Yeah. That's awesome. Yeah. And you're probably one of the first people we've talked to who's been who who has mentioned sports in the background. You know? I played sports when I was a kid. We have someone on our team who actually was a former NFL player. We find that those types of people, they understand, number 1, how to be a team player. They know hard work because you can't play sports without working hard or you're not gonna play it long. But you also they also seem to understand when to step up and when to step up into a leadership role, whether it's temporarily or permanently as a captain or something like that. So I guess is that something that you think came from your sports background, or is that something that you think you had before sports? It helped you enjoy sports a little bit more, and it's just transitioned into the current place that you're at now. Well, I'll say this. When I was a kid, I was uncoachable. I was that uncoachable guy where he's like, why is he on the bench, man? He's so good. Uncoachable. You know? Just knew everything. So, you know, as you age, you mature, and I got to do this a little bit more than I got to do sports, I feel. And I've just come to that conclusion, you know, that that, like, some of I don't know who quotes it, but some people say, like, you don't hire smart people to tell them what to do. You hire smart people to figure out your problems. And most of the people I've worked with, I I've been on, like, excellent teams or all of them have been smarter than me. I might just be able to articulate a little bit better their idea or their concept. But when you don't want the credit and you just want the team to win, like, that's how you build things. And it's just an interesting perspective from you know, you go to engineering. Most people that have been engineers their whole life, you know, it's kind of a set like, a solo pursuit. You know? You you you find them with the computer. Like, I'm on the computer all day. I don't get to talk to people. But then, like, if you can reach out and it's like, no. Let's do a community. Like, one of the best team building things I ever did, was at a startup at Zipseam, and we went and saw Deadpool together. And it's like I yeah. I still got memories from that. You know what I mean? And it's just, I don't know. Everything's about team building at the end of the day. And it's and and all the problems that we all have, all the industries are all the same. It's all communication. Anything solvable. It's just can we communicate with each other, and can we make each other feel all wanted and put each other in the right positions. Yeah. If you were to if you were to rank like the things you do as a leader, for your team, like, do you think those team bonding experiences, those team outings are the things that really foster that, sense of community and and, and creativity within the team that'll then allow you to work better together on on certain projects. Like, how does that you know, how would you stack that up against some of the other things that we've been talking about? Well, one of my favorite teams I ever went on, we all came from a different background. Every single one of us was from a different background. But we went out to eat every day. Like, not every day because, you know, you can't eat out every day, but, you know, we ate out a lot, and we spent a lot of time together. And the project we might have been working on varied. It was contracting various data things. But what brought us together was just coming together and bonding kinda like how a family has dinner today. Well, you know, you'd want to have dinner every night, you know, groom your children for whatever, but it's it it seems like it's just time spent together. And one of, like, the industry issues, at least since I've jumped around, I've been part of the issue, is is really interesting is it's like a lot of these legacy systems, a lot of these long built building things are just they couldn't keep their original engineers there, so we're all resolving the same problems. You know, but that that's just life. So I just think it's just anything about time spent together. Like, it seems like you guys got a pretty good bond together. Had there been time spent together in the trenches. Yeah. Yeah. That brings up a good question because Pedro and I have have we've been around the block together. We've been in 3 places together. Matt is now part of our trio. We've we've inherited him, one of our marketing guys. But how do you find that that's how do you find to do that in the remote world that we're in? Because Pedro's in LA. I'm in Nashville. Some other coworkers are in New York or DC. And when we're in when we're in person, there is that it it feels like no time has passed, and we're on calls all the time together or we'll message each other all all the time. But, you know, we're we're we're sales guys by heart. You know, we have some other skills as well. But how do you find that that works with engineers who are typically in their in their houses and like to be remote and stay quiet a lot of times? How do you build that camaraderie or that team in a remote world? Well, some of it, like I mean, we had a, I think it was this Christmas. We had, like, a, you know, a Christmas day. Just online playing games, you know, bingo, bang, playing games, just talking together or just like our daily meetings. Right now, I'm on a team where we have Colorado. We have Austin. We have Michigan. I'm in Ohio or Nevada. And then we got, you know, San Francisco. And it and it's, like, interesting things that happen, like, not to be negative, but, like, the Amber alert. That go off for 1 of us, then it goes off for all of us, or, like, the weather or even, like, you know, local events that are happening, and you can just kinda engage yourself with other people. And it allows you to be, I would say, like, a more well versed person just, like, nationally. And then it it kind of buys into, like, hey. You know? No. I got my friend in I got my friend in Texas, man. Like, what this is what's going on on the ground. And I think that's the way to kind of build it. And it and it's like, you you have to think, my little brother, like, during the COVID, had to go remote school. You know? I mean, he's 1st grade, just typing on a computer all day. And, like, I I hate to tell people, well, this is just the world that we're probably gonna live in. Yep. So we might as well take advantage of it. And truth is if you look at, like, the statistics or the studies behind remote working, which a a lot of executives probably won't bring up, but you'll work more remote than you will actually in the office. Like, I'll find myself here late at night. Just gotta get it done. Gotta get it done. And you turn your office or your house into your own you know, you're working around the clock. So it's a catch 22. It's an interesting conversation. But I see you just build build teams by being together. You know, being a person, being normal. Yeah. Yeah. I definitely feel that. I I actually have to in my office, I have my lights programmed to change color at, a certain time so that I make sure to, like, hit the gym. And, of course, sometimes I'll ignore it, but it's just, like, at least having that mental marker, like, well, I was supposed to stop working about 30 minutes ago because I gotta start working again in a couple hours and take a call with, somebody in Malaysia or something. But, yeah, I like what you're saying about, like, these these team outings, especially about the gaming thing. Like, I know a lot of friends who connect with their coworkers, through gaming, through playing games together online and stuff. And like you said, it's it is the world we live in, and I guess that's the the bet that companies like, you know, Meta are taking that maybe this is it's trying to bring that in person experience into the online world with, like, VR and stuff. And I don't know. Like, do what what's your perspective on on that? Do you think that VR and these kind of future technologies can can accomplish that same goal of, like, going to all see Deadpool together, you know, but, like, in a headset or, like, the Apple with the Vision Pro coming out, that sort of thing? So interestingly enough, I, like, I wasn't sold on it at first. Right? Even though I had, like, one of those VR boys back in the day where I'd play tennis on it and everything. I was never sold Tango 1? Yeah. It was like a Yeah. Virtual Boy, I think. Yeah. Virtual Boy. But I so I was never sold on it. But then so I'm out here in Vegas, and I went to see, King Tut, a virtual 3 d, thing down at, I think, Mandolin Bay. And you put the goggles on it, and it's the Oculus, and you're exploring Egypt. Just walking around, just playing with things. And then, like, my friend had one of those PlayStation VRs, and you could cook and you could do the the everything in it. And it's like I've seen some of the things where you could actually set up your office 3 d in a manner. And, like, I could have my 9 monitors or how we're monitors, and I could be in a virtual workplace together with people. So for for in that regard, like, kinda like NFTs or even cryptocurrencies. It's the utility of it, not the actual concept of it, I think. And I think more and more people will because there's there's just things like because we're so remote, like, we're in different time zones. Like, we even have contractors in the Philippines. So we're like, someone is working constantly just in a different time zone. But it would be nice if you could just go in there and pair program with them with the VR headset, just sitting back on your couch, air typing. I mean, if they don't automate our job before that but hey. Right. You know? I I love the the VR aspect of that. Yeah. No. I I like what you're saying about the utility of it. Right? Like, the technology, the hardware, the software, that's all clearly going to get there to a point where it's like, you know, that you can't tell the difference between that and real life essentially, but it's it's the utility in the application, like, what are the companies going to build, to actually be able to do in there and and actually make you feel more together, in a not so cartoonish way. Right? Yeah. Like, even us just using faces with our with the podcast. I mean, I feel more connected to you than if it's not. And a lot of policies that we're putting in place at Stitch Fix is you have to turn on your camera. You know? Like, I wanna see your facial expressions because I wanna know if this is a terrible idea. You know? Right. Or maybe you shouldn't know. But, yeah, I think there's I think I think there's some things where it makes sense. You know? Like, maybe watching a movie with a friend who's across the country or across the globe. But you're in Vegas. I don't think I ever wanna miss the feeling of the the the slots hitting or playing craps. I don't think I could do that virtually. Yeah, I think there's some things that would be easily moved into the a fake world or a a VR world. There's some things that I feel like just should never never be replaced. So I'm kinda curious to see where that goes. I I used to work for Apple, and the VisionPRO certainly interests me, but I don't know. It's starting to get creepy how real stuff looks. But but, yeah, you mentioned your 9 monitors. Like, if you if you could have your entire room just full of monitors, you're. But then again, we're already I think all 3 of us are over workers. So do I wanna have a computer monitor up while I'm washing the dishes? I don't I don't know. What about you? Do you think it'll cause even more overworking potentially? Or I think we're all over workers anyway, so it doesn't matter for us 3. We're already past the point of saving. Yeah. What about for others? I mean, I don't think you can you can just make it convenient. You know? Like, the one thing that I wanted to do, like, maybe 2 years ago, I was gonna get, my cousin one of those headsets because you could both sit, like, front row at an NBA game and talk to each other like we were next to each other. You know what I mean? But, like, if you could all create a virtual environment workplace that's ideal and you could mix it with your home. You know what I mean? Like, you could, in a sense, maximize someone's output and make them to be comfortable. And not maybe because that's probably the best idea, but more about because, like, you know how, like, with with life or with future, it always leans one way and leans the other way. And, like, I see, like, what I went through as the job hopping phase, And I feel like companies are just gonna be tripling down on, like, longevity of employment again because we went so far left. We need to go right and readjust. And a lot of our issues could just be solved. We just didn't have to keep getting people over and over again. Yeah. Yeah. Do you see that as a problem now that you're climbing into leadership that that VR could solve? You know, you I think it is easy to job hop when you don't feel that connection with your coworkers or with your leaders. But if somebody if you're wearing a VR headset and somebody comes and whispers in your ear, you know, you turn around and there's a person virtually there, do you think that could solve that that kind of ease of job hopping when you just don't feel like you you anybody would miss you? I don't know what the right way to put it, but I do feel like there's a just a disconnect with feeling a part of your business if you don't inter interact with other people. I I I will say this. I don't know how VR VR might help. You know what I mean? Just because you would be around people, and I think that always is a is a positive. But with, with job hop, at least if I could give any advice while we're on here, and if anyone is listening to this, I've been in a pleasurable situation where I've been on a team where it's like, hey, you're groomed for for executive leadership. Right? Like, we know that you're gonna get there. We're putting you on a team. Everyone's gonna be moved up. I've also contract in positions where I I I was working to groom a certain individual who was going to move up the corporate ladder. Like, they had already picked them out to be a CEO, and they just didn't know. I I and and it's like it bewilders you because you asked them, like, does he know or does she know? They're like, no. We haven't told him. I was like, don't you think? And they'll they'll end up leaving, and you'll be like, oh, man. You guys lost a a CEO you were grooming. Like, that's kinda big. You know? Like, I mean, but since you didn't tell them, I guess, no one knows no loss. But I think that the the thing that could just is just timing. You know? Like, I think a lot of people need to be more transparent about, like, hey, what they want out of their career and where they wanna be. And then, like, also companies should be like, hey. We see you as a leader. Like, one of the one of the greatest losses I ever saw. Like well, I mean, I did it too, and I was the same person, so I understood why they left. But I was like, man, looking back, you probably shouldn't have left. But it's just something that you learn. You know? I mean, you learn with time, and I just think the industry's gonna change just because it's been this way for so long that just the yin and yang effect of anything. Just the universal law is like, if everyone's leaving, then there has to be a period of time of us. We might not get Rolexes again, but, I mean, they're gonna want us to stay around. We can try and get Rolexes. But yeah. So so what do you what do you think? You mentioned one really good point that somebody else has mentioned in one of these podcast recordings too, letting letting leadership know your intentions of wanting to climb the corporate ladder. So what would be some good advice that you give having, you know, done these types of things yourself as well? But for those engineers who would be listening or those who want to get into engineering and would eventually want to climb as well, What's some good advice that you would give, you know, people who are wanting to climb the the corporate ladder or the the leadership ladder? Well, one one thing I would always tell anyone is variety is this is spice of life is variety, however you wanna put it. So experiencing everything is key. Right? Like, if you don't know what you've tasted, then you don't know what you want. So early in your career, like, for example, I did front end. I did back end. I did data engineering. I tried to start my own company. I did a little sales. I was in recruiting for a while. Like, I did, the drawing. I did the UX for a while. It's like knowing and and then once you know how to do everything, not to say you should know, but once you have that that skill, like, you can kinda tail you you 1, you can see where it all leads to, but more importantly, you can find out what it is that you like. So I think once you find out what you like, like, for example, I was a data engineer, and I'd go to data visualization, like, front end meetings. And, like, my managers would say, hey, Ali. Why are you in this meeting? And I'd be like, well, I'm working on a side project doing data visualization. Anyway, I figured what's what's the harm of it. And if I know how the 2 need to to speak, then that's a win win. So just going out of your going out of your way, I mean, if you're young enough and you don't have a family or anything or any obligations, and we're still in the office, showing up early is always a good thing. One of the tricks that someone told me, and it was my sales career, was always work a Saturday. You know? Come in and work a Saturday because if you work a Saturday, you know, a, you pick up everything. You might be able to relax a little bit during the week, but no one can get rid of the guy who works on Saturdays. And everyone remembers the call on Saturday. For for moving's perspective, we just got a so Citrix, we got, Tony, Tony and Matt, but Tony's been, just because he just spoke recently. He's talked about how he transitioned from coding to executive leadership. And, really, it was it was more about just aligning yourself with a business or understanding, especially if you're an engineer. Like, when you're young, you think like, hey. I gotta solve this. This is the most important thing in the world. You know, like, no one else can solve this. I'm gonna do this in such a creative way. You know how you you think, like, you're this god complex that comes from solving a few engineering things. But then when you you advance a little bit, you you can kinda take a step back and look at the whole picture, and it's like, it's not it's about the business. Because if the business doesn't succeed, you don't have your engineering job. So how can you improve the business? Is there something you could do to improve our business, our organization? Could you open source something? Could you, like, have a conversation with an outside company like yours, you know, that's doing some very interesting things in the industry to put our name out there? Just just other ways you can add value because if you're gonna separate yourself, it's gonna be like, the power of 3, like, your your your personality, your work ethic, and then that extracurricular killer stuff that you're doing on the side that also presents that. Oh. How? So what about people like myself who work Saturday Sunday? You're saying no one can ever get rid of me. Well, I mean, you just love it, man, and we're lucky enough to find something we love. Yeah. So you're really mad about that. True. Yeah. That is that is so true. I think John and I both have, you know, sort of nights and weekends projects as well. So, as as do, I think, most engineers. Right? Like, most engineers we talk to have nights and weekends project. We did another interview with someone who had done some DJ work and, and did, like, karaoke stuff. So, it's it's really cool to see you know, you do get to express some of the creativity that you can't put towards work that doesn't have a fit there. You could still express in another way, right, through music, through art, through, you know, coding on a personal project. Do you have anything like that that you exercise those skills with? Well, right well, I mean, I shouldn't say it. Right now, I like to trade stocks a lot, because I see it as, like, a nonsolvable problem. You know? Because it's all math, but it's just based upon time series and where you're at and, like, if the patterns recreate. One of the best engineers I ever worked with, he was a an he made swords. So he's a fire. A fire maker. So I was like, so what do you how I was like, did you did you get that signed by Randy Jackson? What a what a hobby. Yeah. But it's like, you you know, you meet people who are like to take her. You'll find out all types of interesting, hobbies. So it's definitely something to to identify for or to ask like, hey. Are you working on these side projects? Like, that's back in the day. That's how I'd identify a winner. It's funny you mentioned that because my my brother-in-law, who also is in technology, he's been in it longer than me. He used to make iron lamps, so lamps with pipes, and then there was a lamp at the top out of it. But that's how he met Steven Tyler, and he met a bunch of random people because he worked on this random side project. So it's kinda cool that somebody you knew made swords. Yeah. I met somebody that somebody that I was in a band with started creating some art and then started creating, like, tapestries, these really complex tapestries. And all of a sudden, he gets a call from, like, Kanye's team to, like, design some, like, lion chain necklace. And all of a sudden, he's, like, the biggest thing in the in the rap and hip hop world because he's just like the go to artist for that kind of stuff, designing all these huge, you know, 10 foot tall tapestries for these guys' homes and stuff. So yeah, I mean, yeah, it's it's pretty cool. 2 notes on 2 notes on that. 1, like, from my other big career after this, I realized that musicians, given the right background, they're always great salesman because they're always great communicators. They always have a great network. And it's like they can take you in the fun environment, the professional. So that's always one thing. And then 2, just for anyone like, hey. Just do whatever you like to do. You know? Like, I mean, this guy got paid to make tapestries. You know? Imagine you get paid to do something you love to do. You know? You should always pursue that. And, eventually, you know, you pursue it long enough, it'll be a niche. I think we need to keep track, Pedro, of how many times music gets brought up on these conversations because music gets brought up. People who love to play music. I think it's a No. Engine it's an engineering trait. People just love to play different things. So Yeah. It's also it's also just math. I mean, music is is art, in a way, but it's it's mostly just math and chords and melody, and, it all just comes down to numbers. So I think the engineering mind does really well with that. I mean, we've got a number of musicians. We say it all the time. We've got a number of musicians here at, at Directus, and we've got I mean, I just this morning, I was on a call with with a prospect who is had a bunch of guitars in the background, so it's always cool to see. Yeah. Matt was giving away the secret on LinkedIn about stop at time just replaying the same song. And Yeah. I I will say I once worked with, I once worked with, I don't even know what it's called, but, like, a PhD in music. And they also were a a coder, and it was just like, to them, it made so much sense, like, the the synchrony between the 2. But to me, I just was like, I like listening to opera while I code. Yeah. Yeah. Yeah. Yeah. We, we're coming out with a just a series of videos with lo fi music because I I love listening to Oh, I love that. Jazz or music just on the side while I'm working. I think that's a very common thing. Opera is good. A symphony. You can't beat a good symphony. So No. Something with pain. Something just with, like, like, or classical. I've been listening to lofi Tupac. Not gonna lie. Lofi Tupac. Where can I find that? YouTube. Awesome. I saw I saw they did, I don't know if you've seen on YouTube. There's a series where a guy takes, Super Mario 64 sounds and recreates entire albums. So he took, like, one of the Radiohead albums and used recreated the whole album with just the sound bank of Super Mario 64. And he's done it with a number of classic albums like Nirvana, and, it's it's pretty pretty interesting. Sweet. Yeah. How random. People have a lot of time on their hands. But yeah. No. You brought up, like, old video games. I remember my very first startup. It, like, was my internship, and I used to play Zelda. Right? Because I was always into video games. I played Zelda for, like, 12 years just to beat it. I remember, like, how hard it was. 1st day 1st week. Yeah. We, speed play Zelda. Like, the record was, like, 22 minutes. I said, golly. Yeah. There's so many side quests. You have to do all the side quests. I don't know how they beat it that quick, but it's just funny to say, like, at least I I don't know how long we have, but, like, hey. If you're not into tech, you should still get into tech because everything is tech. And that's how I benefited so much, and that's why psychology was so beneficial for me, as I've explored my direct career. Mhmm. Yeah. That that's awesome. As we start to wrap up, I'd love to under I'd love to learn about real briefly, you know, what are what are your goals? And do you see yourself you talked about grooming a CEO or someone who is going to be that. You know? What what are your end goals? Where would you like to see yourself, you know, climbing more into leadership, getting more into an individual contributor role, both? You know, I guess, what are what are your goals? My goals really, lately has been just, like like, trying to find people who are like me, younger, you know, and just give them everything that I've picked up along the along the journey just to make a lot of people's journey shorter. And I've come to realize, like, so I've had a privilege of working at Stitch Fix and also 8451, which was big on, like, women led organizations or, like, women involvement and, you know, like, you know, team involvement. It's just a diversity of a team in general. And it's like there's several problems in tech. I mean, like, we've we've proven, I think, that anything's solvable. The the problem now is like, hey. Do we have the, like, let's say, the dynamic the the demographics to create all problems? Because once we create the problems, then we can create solutions. So it's just more of for me right now, I'm just trying to I'm I'm most interested in getting more people into tech, that look and don't look like me. Just just so, you know, like, well, for 1, I don't know what else they're gonna do. But for for for 2, you know, like, it's not as hard as you would think. You know? Like, it it's not. It's really just solving puzzles. It's just we make it difficult because we like to be elitist. Yeah. Yeah. That's really good point. Yeah. Does it require 9 computer monitors to be as good as you? I mean Or 12. Some of these Just like you gonna be better. Just like in monopoly, man. Like, it's all about real estate, man. And the more screen real estate you got, the better. Yeah. So it sound I after you. My bad. My bad. I I was gonna say it sounds like you need the VisionPRO from Apple coming out because in your whole you're everywhere you look, you can have screens. You just loop around in a chair and see screens all over you. Right? Or you're you're in Vegas. Just go to the sphere and put all your monitors on the sphere. Yeah. The the the, ironically enough, the one jealous, the the the most jealous I've ever been as a developer. We had a, we had a guy who you could tell he had it. You know? Like, you can just see it. Like, you can just see it. But he only worked on one one screen, and I had to pair a program with him. And it was just like, man, how do you do this? But he had the advantage because it's like, sometimes I will not be able to work if I don't have a bunch of monitors, but he could work anywhere at any time. So just go camping on this monitor, and it's like so it's like there's it's like everything, man. There's wind and there's plus and losses about the whole monitor con conversation. Yeah. Well, awesome. I think I think it came across in this conversation, your real passion for engineering, and that's why you've gotten into it, but also your passion for people. So for for people who are listening, you know, how can they follow you? Where can they get in contact with you if they wanna get in contact with you? Is it LinkedIn or other things as well? Let let people know where you're at. I usually just use LinkedIn to be quite honest with you. I have a YouTube, but I don't really do anything on it. It's just more for me to play around with. I've been playing with the algorithm on both of them. But it would just be LinkedIn. Just, I think it's just Ollie Payne. LinkedIn slash Ollie hyphen Payne, a l I hyphen p a y n e. But, you know, I'm focusing on those 2, man. I'm not gonna venture out too much. Yeah. Yeah. Well, we have little time we have little time for extra even projects, but so it's interesting that you even do algorithms. But, I think I can I think I can speak for Pedro? This has been a fantastic conversation. I feel like every conversation we have for this podcast is almost completely different, and yours was more so around kind of compassion for people and compassion for engineering. So I think we we both greatly appreciate the time, and we'll put your contact info in the the description, but definitely think that people can find some some good tidbits from what you've said today. So definitely appreciate the time. Yeah. Well, hey. Hey. I appreciate talking to both of you, and, hey, they have a good product too. Well, hey. You guys have good clothes. So if people need clothes, check out Stitch Fix. Hey. Win win. Love the plugs. Appreciate it.",[4754,4755,4756],"042567c1-de26-486b-8897-90ab0ecde163","7615ffba-e3e6-4e5c-9332-69e15b7d68a4","b86ba74d-0fa8-480c-a8dc-b368dcfb1651",[],{"year":2380,"number":739,"id":4539,"episodes":4759,"show":4760},[4541,4542,4543,4544,4515],{"title":1849,"slug":1850},{"id":3956,"slug":4762,"vimeo_id":4763,"description":4764,"tile":4765,"length":480,"resources":12,"people":4766,"episode_number":13,"published":4768,"title":4769,"video_transcript_html":4770,"video_transcript_text":4771,"content":12,"seo":12,"status":19,"episode_people":4772,"recommendations":4774,"season":4775},"nuxt","909728011","In this video, you'll learn how to use the Directus SDK with a Nuxt application.","478b00b9-1d18-4ea4-82a2-52008c71d301",[4767],{"name":2820,"url":2821},"2024-02-07","Get Started with Directus and Nuxt","\u003Cp>Speaker 0: This is a blank Nuxt project created using the Nuxt CLI, and in this video, I'm gonna show you how to integrate directors with this NUXT 3 project. Now over here in the terminal, the only thing we need to do is install the Directus SDK like so. Before we continue, let's spin up the development server using npm run dev and we can just leave this running throughout the length of this tutorial. Create a new directory called plugins, and inside of that, a file called directus dot JS. Import are required functions from the Directus SDK and then initialize an instance of the Directus SDK, making sure to provide your full Directus project URL and including the rest composable.\u003C\u002Fp>\u003Cp>Then define a new Nuxt plug in, providing our directus instance, our read item, and read items functions to our entire Nuxt application. In my existing director's project, I've created a collection called global. This collection has a title and a description. In this director's project, the public role has been given read access over all of the user created collections, and additionally, the director's files core collection. Replace Nuxt welcome with Nuxt page to use file based routing, then create a new directory called pages, and inside of that, a new file called index dot view.\u003C\u002Fp>\u003Cp>Inside of index dot view, create an empty template, which we'll populate in a moment, and create a script setup. We can now import this direct us and read items, function from the plugin, that's why they have the dollar sign in front of them, from use Nuxt app. And then we're going to use async data. We are going to return directors, dollar sign, dot request, and inside of this, we are going to run read items once again with the dollar sign global, and that is because it is the name of the collection. So we will be reading the global collection.\u003C\u002Fp>\u003Cp>Out of this, we will destructure the response from this, the returned value from this, and we will get the access of data. We will get the property of data available. So if I just put data in here and I go back to the browser, we will see data coming back from directors which is great. Let's clean this up just a little bit. We are going to firstly provide the name of this because I don't like data.\u003C\u002Fp>\u003Cp>I think that's not very descriptive. We will rename this into global and we will put in here h one global dot title and a paragraph tag global dot description. Going back to our browser, we now see that we have a page with our header and our description directly from Directus. Inside of our directors project, we also have a pages collection. Every page has a slug.\u003C\u002Fp>\u003Cp>This is the unique ID for this collection, the primary key. It is a manually entered string, and this correlates with the URL that we're going to load this data at. So this will be a slash about. They have a title and they have content. This is a what you see is what you get editor which means under the hood is actually just storing HTML.\u003C\u002Fp>\u003Cp>Inside of your page's directory, create a new file called slug dot view where slug is surrounded by square brackets because it is a dynamic route. This is very similar to when we used our global data with some notable differences. Firstly, instead of using read items, we are using the singular read item function. Because we only need the specific item for any given page, that matches this dynamic value. How do we get that value?\u003C\u002Fp>\u003Cp>Firstly, we are going to utilize the use route composable provided by Nuxt. Then, inside of the director stock request when we run read item, the first argument is the collection and the second one is the specific ID of the item we're grabbing. We can now, because we have, created a use route instance here, we can use route dot params.slug. If we head to slash about, we see that data is loaded in correctly. We see it's mostly an a string of t m l, but we also have the slug and the title.\u003C\u002Fp>\u003Cp>Let's improve the way this looks. Firstly, we're going to once again have an h one with page dot title. We're also gonna use a div with v HTML as an attribute, and that value will be page dot content. This will render out the HTML directly. Just a note for from a security standpoint, you have to know that this content is safe in order to use the HTML.\u003C\u002Fp>\u003Cp>If only you are the author or all the authors are trusted, then this is a fine approach. We now see this page is being properly rendered with all of the correct HTML tags. The last thing we want to do is ensure that if a page does not exist in Directus that we successfully throw a 4 zero four not found error. So we can add this code in the bottom to achieve that. Our directors project also includes a posts and authors collection.\u003C\u002Fp>\u003Cp>Inside of a post, just like before, we have a slug, a title, and a content field. But there are some extras. Firstly, an image, which actually is a relational field to the director's files call collection. We'll see how this works in a minute. A publish date and a relational field to an author.\u003C\u002Fp>\u003Cp>Our authors just have a name for now because I wanna show you how relational data works. Inside of our pages directory, let's create a subdirectory called blog. And inside of that, a file called index dot view. This is the file which will be loaded when someone goes to slash blog. In order to load a list of blog posts, we're going to use read items.\u003C\u002Fp>\u003Cp>Inside of read items, we're going to query that post collection. But this time, we're using some of Directus' inbuilt query, properties. Firstly, we're using fields to specify the specific fields we wish this request to return. This is important for a couple of reasons. One, we don't want to return the entire blog post itself because that is a large amount of data that we're not actually going to use on this page.\u003C\u002Fp>\u003Cp>Secondly, we want to access a kind of property of that relational author value. So, here, we're getting the slug, the title, the publish date, and the author's name. Remember that this is held inside of that author's collection, not inside of the post's collection. We're also using a sort to make sure that the newest post comes first as is typical on most blog posts. To actually render the list, we're gonna fall back on our view fundamentals.\u003C\u002Fp>\u003Cp>We're going to create a loop. And for every item in the loop, we're going to render a list item which contains a link that goes to slash blog slash and then the dynamic slug will render the title, the publish date, and the post dot author dot name. So that's a nested value there. And in our browser, we now see a list of blog posts latest first. Now, if we click on one of these links, we actually get a 404 and that's because we haven't yet built the blog single page.\u003C\u002Fp>\u003Cp>But take note that this was sending us to slash blog slash and then the slug for this individual post. Back in our editor, inside of this blog directory, create a file called slug dot view where slug is a dynamic route. At this point, hopefully, you should start to see some patterns. Inside of an individual blog post, we are using read item and the route param slug once again. Except this time, we are also using the fields query, parameter.\u003C\u002Fp>\u003Cp>So here we have, an a wild card that says, give me give me every field related to this blog post, but I also want to grab some relational data. I once again want the author's name. And out of the image, I want the ID and the description of that image. And if nothing comes back, if this post does not exist, once again, throw a 404. I'll move this down so it's a little more readable in in all.\u003C\u002Fp>\u003Cp>Once again, we're just putting in the post title and the post content using the HTML with once again a note that you have to be able to trust this content in order to use the HTML and not otherwise sanitized content. And then we have an image tag. This is where we're using director's files. So in here, this is a long string, which, which does the following. Firstly, grabs the URL from our initialized directus SDK in the plugin.\u003C\u002Fp>\u003Cp>So this value here is equal to this value here. The SDK automatically adds a forward slash if there isn't one present. So this is equal to our project URL slash assets slash ID of the image. And instead of returning a huge image, we're just returning one with a width of 6 100 pixels. It will resize that image on the fly for us.\u003C\u002Fp>\u003Cp>And then it will utilize the description as the alt text for this image. If we head over now to our individual blog post, which we got to from the listing, we have the image. Following the image, we have the h one, and then we have just all of the HTML from the content, what you see is what you get editor inside of Directus. And that is a summary of how to integrate Directus and Nuxt. You have created a Directus plug in and used it to query data.\u003C\u002Fp>\u003Cp>You have used a singleton collection for global metadata, dynamically created pages, as well as a blog listing, and post pages. If you want to change what is user accessible, consider setting up more restrictive roles and only accessing valid data at build time. If you wanna build more complex dynamic pages out of reusable components, we have a guide in our docs for doing this. I hope you found this video interesting and have fun with Directus.\u003C\u002Fp>","This is a blank Nuxt project created using the Nuxt CLI, and in this video, I'm gonna show you how to integrate directors with this NUXT 3 project. Now over here in the terminal, the only thing we need to do is install the Directus SDK like so. Before we continue, let's spin up the development server using npm run dev and we can just leave this running throughout the length of this tutorial. Create a new directory called plugins, and inside of that, a file called directus dot JS. Import are required functions from the Directus SDK and then initialize an instance of the Directus SDK, making sure to provide your full Directus project URL and including the rest composable. Then define a new Nuxt plug in, providing our directus instance, our read item, and read items functions to our entire Nuxt application. In my existing director's project, I've created a collection called global. This collection has a title and a description. In this director's project, the public role has been given read access over all of the user created collections, and additionally, the director's files core collection. Replace Nuxt welcome with Nuxt page to use file based routing, then create a new directory called pages, and inside of that, a new file called index dot view. Inside of index dot view, create an empty template, which we'll populate in a moment, and create a script setup. We can now import this direct us and read items, function from the plugin, that's why they have the dollar sign in front of them, from use Nuxt app. And then we're going to use async data. We are going to return directors, dollar sign, dot request, and inside of this, we are going to run read items once again with the dollar sign global, and that is because it is the name of the collection. So we will be reading the global collection. Out of this, we will destructure the response from this, the returned value from this, and we will get the access of data. We will get the property of data available. So if I just put data in here and I go back to the browser, we will see data coming back from directors which is great. Let's clean this up just a little bit. We are going to firstly provide the name of this because I don't like data. I think that's not very descriptive. We will rename this into global and we will put in here h one global dot title and a paragraph tag global dot description. Going back to our browser, we now see that we have a page with our header and our description directly from Directus. Inside of our directors project, we also have a pages collection. Every page has a slug. This is the unique ID for this collection, the primary key. It is a manually entered string, and this correlates with the URL that we're going to load this data at. So this will be a slash about. They have a title and they have content. This is a what you see is what you get editor which means under the hood is actually just storing HTML. Inside of your page's directory, create a new file called slug dot view where slug is surrounded by square brackets because it is a dynamic route. This is very similar to when we used our global data with some notable differences. Firstly, instead of using read items, we are using the singular read item function. Because we only need the specific item for any given page, that matches this dynamic value. How do we get that value? Firstly, we are going to utilize the use route composable provided by Nuxt. Then, inside of the director stock request when we run read item, the first argument is the collection and the second one is the specific ID of the item we're grabbing. We can now, because we have, created a use route instance here, we can use route dot params.slug. If we head to slash about, we see that data is loaded in correctly. We see it's mostly an a string of t m l, but we also have the slug and the title. Let's improve the way this looks. Firstly, we're going to once again have an h one with page dot title. We're also gonna use a div with v HTML as an attribute, and that value will be page dot content. This will render out the HTML directly. Just a note for from a security standpoint, you have to know that this content is safe in order to use the HTML. If only you are the author or all the authors are trusted, then this is a fine approach. We now see this page is being properly rendered with all of the correct HTML tags. The last thing we want to do is ensure that if a page does not exist in Directus that we successfully throw a 4 zero four not found error. So we can add this code in the bottom to achieve that. Our directors project also includes a posts and authors collection. Inside of a post, just like before, we have a slug, a title, and a content field. But there are some extras. Firstly, an image, which actually is a relational field to the director's files call collection. We'll see how this works in a minute. A publish date and a relational field to an author. Our authors just have a name for now because I wanna show you how relational data works. Inside of our pages directory, let's create a subdirectory called blog. And inside of that, a file called index dot view. This is the file which will be loaded when someone goes to slash blog. In order to load a list of blog posts, we're going to use read items. Inside of read items, we're going to query that post collection. But this time, we're using some of Directus' inbuilt query, properties. Firstly, we're using fields to specify the specific fields we wish this request to return. This is important for a couple of reasons. One, we don't want to return the entire blog post itself because that is a large amount of data that we're not actually going to use on this page. Secondly, we want to access a kind of property of that relational author value. So, here, we're getting the slug, the title, the publish date, and the author's name. Remember that this is held inside of that author's collection, not inside of the post's collection. We're also using a sort to make sure that the newest post comes first as is typical on most blog posts. To actually render the list, we're gonna fall back on our view fundamentals. We're going to create a loop. And for every item in the loop, we're going to render a list item which contains a link that goes to slash blog slash and then the dynamic slug will render the title, the publish date, and the post dot author dot name. So that's a nested value there. And in our browser, we now see a list of blog posts latest first. Now, if we click on one of these links, we actually get a 404 and that's because we haven't yet built the blog single page. But take note that this was sending us to slash blog slash and then the slug for this individual post. Back in our editor, inside of this blog directory, create a file called slug dot view where slug is a dynamic route. At this point, hopefully, you should start to see some patterns. Inside of an individual blog post, we are using read item and the route param slug once again. Except this time, we are also using the fields query, parameter. So here we have, an a wild card that says, give me give me every field related to this blog post, but I also want to grab some relational data. I once again want the author's name. And out of the image, I want the ID and the description of that image. And if nothing comes back, if this post does not exist, once again, throw a 404. I'll move this down so it's a little more readable in in all. Once again, we're just putting in the post title and the post content using the HTML with once again a note that you have to be able to trust this content in order to use the HTML and not otherwise sanitized content. And then we have an image tag. This is where we're using director's files. So in here, this is a long string, which, which does the following. Firstly, grabs the URL from our initialized directus SDK in the plugin. So this value here is equal to this value here. The SDK automatically adds a forward slash if there isn't one present. So this is equal to our project URL slash assets slash ID of the image. And instead of returning a huge image, we're just returning one with a width of 6 100 pixels. It will resize that image on the fly for us. And then it will utilize the description as the alt text for this image. If we head over now to our individual blog post, which we got to from the listing, we have the image. Following the image, we have the h one, and then we have just all of the HTML from the content, what you see is what you get editor inside of Directus. And that is a summary of how to integrate Directus and Nuxt. You have created a Directus plug in and used it to query data. You have used a singleton collection for global metadata, dynamically created pages, as well as a blog listing, and post pages. If you want to change what is user accessible, consider setting up more restrictive roles and only accessing valid data at build time. If you wanna build more complex dynamic pages out of reusable components, we have a guide in our docs for doing this. I hope you found this video interesting and have fun with Directus.",[4773],"69653a15-3fe2-453e-a04d-28108391575d",[],{"year":978,"number":230,"id":3954,"episodes":4776,"show":4777},[3956,3957,3958,3959,3940],{"title":3961,"slug":3962},{"id":4779,"slug":4780,"vimeo_id":4781,"description":4782,"tile":4783,"length":13,"resources":12,"people":12,"episode_number":939,"published":4768,"title":4784,"video_transcript_html":4785,"video_transcript_text":4786,"content":12,"seo":12,"status":19,"episode_people":4787,"recommendations":4788,"season":4789},"ba999124-75d6-4e73-9ad8-bf5ca850dd2b","no-code","891433031","The real mark of an elite developer is not in the lines of code they write, but the lines of code they don't write.","f4d9f774-4500-445b-a37e-aa52503fc586","No Code","\u003Cp>Speaker 0: And now, Dev Thoughts by Jack Rabbit. They say the best code is no code at all. So today, I decided to delete all my code from the past year. I feel very enlightened, but my boss doesn't seem to share my spiritual awakening.\u003C\u002Fp>","And now, Dev Thoughts by Jack Rabbit. They say the best code is no code at all. So today, I decided to delete all my code from the past year. I feel very enlightened, but my boss doesn't seem to share my spiritual awakening.",[],[],{"year":2380,"number":159,"id":4790,"episodes":4791,"show":4806},"8ecafdf4-f01e-406a-960a-b55729856a73",[4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803,4804,4805,4779],"3baf8d66-55e4-4ced-99f5-0331580ebfea","3ff2054a-f876-495b-bbcf-adc59989740e","990f8c7f-c9e9-4861-a947-b5ab5cb0b322","28afd411-cb5f-4e96-9680-f80f338cfb8e","def87174-3688-47bc-8c85-ecc86ec621cf","42116fa7-2b09-4db5-b1a5-4e7d7ee46423","63cadb37-0756-4d88-9b12-1135542e4c90","e277bd53-66f5-4831-bce6-2ebe4b412687","19788554-0537-4a74-84db-f23029de3941","b909b08f-84eb-4d23-b297-1008cd297ef6","9df004e6-8fc4-4c61-9687-7bb4b4634063","686610ea-8b2b-4210-8b67-299092b2418d","e889db47-ac35-47e9-82ba-1e9afaffce85","95233a1b-8b0b-43ff-8cc9-2befa9351991",{"title":3380,"slug":3381},{"id":4808,"slug":4809,"vimeo_id":4810,"description":4811,"tile":4812,"length":3992,"resources":12,"people":4813,"episode_number":195,"published":4768,"title":4817,"video_transcript_html":4818,"video_transcript_text":4819,"content":12,"seo":12,"status":19,"episode_people":4820,"recommendations":4822,"season":4823},"08bdd8ab-2852-46ca-8914-6caa9c88aa3f","wishbot","910771694","In this talk, we will discuss the development of WishBot: a trading card game based entirely in discord. we will talk about some of the intricacies of building bots at scale, and how Directus can be used to manage users, collections, and trading.","b19f813c-1894-4437-bd06-c49dbfdd5f18",[4814],{"name":4815,"url":4816},"Nils Jeschke","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fnils-jeschke\u002F","Building a Card Game Discord Bot With Directus","\u003Cp>Speaker 0: My name\u003C\u002Fp>\u003Cp>Speaker 1: is Milt. As you already know, I'm going to be talking about directors in combination with Discord bots and in this specific case, a Disney themed trading card game. Very simple. Stage 1, the concept where I got into the project. It was a project which I took over from someone else.\u003C\u002Fp>\u003Cp>It's a trading card game. It's Disney themed. It had to be a Discord bot because they just wanted it to be, very easy to market to a new user base and very easy to interact with. That's where the interaction base is coming in because in Discord that means not just sending a message but sending a interaction request command. So doing a slash saying I want something and giving some parameters to it and making it work and having it integrated with ranking sites.\u003C\u002Fp>\u003Cp>So it shows up on the ranking sites, you vote for them, you get something in Discord using the bot, and the more you vote the more you get. And we had some early problems which were caused by the project. First of all, being, mainly hosted on a different site. Let's just not call them how they actually are. Fast code, let's say.\u003C\u002Fp>\u003Cp>The problem was that the code was spread around multiple projects. They were all interconnected with each other. That caused problems because things were, defined multiple times in different ways, sometimes with differing definitions. So a is equal to b, but b also is equal to c. At the same time, a is 1 and b is 2.\u003C\u002Fp>\u003Cp>And we had high response times because multiple projects interacting with another on, for some reason multiple continents as well. And multiple data sources was we had multiple Google Sheets which were used as databases. I think we know where that one is going the moment we start getting into multiple 1,000 entries per Google Sheet. So stage 1, consolidation, getting everything into one application, getting all the projects combined into 1, which still used, the same platform for it in the beginning, getting all the data sources merged into 1 as well, which later on made it a little bit easier exporting all the data and getting rid of those data sources. And we used a MySQL database as an intermediary before we started switching over the infrastructure to something called Sapphire, which is, a framework for creating Discord bots.\u003C\u002Fp>\u003Cp>And here's where Directus comes in. We tried a lot of CMS systems and we wanted to have one thing. We wanted to have it quickly deployable. We wanted to have it highly adaptable. Easy to use, not only for administrators and developers, but for, creators, like creating new material inside the CMS, event based and cron based, automation, also known as flows, a REST and a GraphQL API, which was a requirement set by the owner of the project.\u003C\u002Fp>\u003Cp>I just rolled with it. We never used it since we then just use the director's SDK for everything that, happened. And for everything that we didn't use it, we use the internal API. And then it had to have the option to be either self or cloud hosted depending on the scope of the project later on. Funny enough, if you start with self hosted, I heard it's quite easy to get a self hosted directors instance into cloud hosted?\u003C\u002Fp>\u003Cp>Fairly easy?\u003C\u002Fp>\u003Cp>Speaker 0: Oh, I'm not sure you can let me for validation. Sure. That sounds correct.\u003C\u002Fp>\u003Cp>Speaker 1: It's easier than just, here, have a Google Sheet.\u003C\u002Fp>\u003Cp>Speaker 0: I mean, you said you are real low. Yeah. We we, leapfrog leapfrog that.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And this code verification causes one big problem. It's you have to anonymize all the data. You have to follow GDPR, g yeah. GDPR.\u003C\u002Fp>\u003Cp>You have a lot of restrictions when it comes to collecting data from a user. So, generally, you're only allowed to know their ID, and that's all. No name. No information about what they did, what they wrote. There's exceptions to it, and that means you can handle those information when an event happens and you're not able to save it outside of that event.\u003C\u002Fp>\u003Cp>And infrastructure security, that's something that they recently added. They want you to essentially not allow anyone to access your bot on an administrative, capacity. Meaning, those servers that it's running on usually have to be secured by, for example, SSH with identity tokens instead of passwords. And funny enough, they actually checked it. So that's the quick rundown of the project and can actually look into it, how that, structure looks like.\u003C\u002Fp>\u003Cp>Here I actually see the user database. A lot of gibberish.\u003C\u002Fp>\u003Cp>Speaker 2: Could you switch the light theme?\u003C\u002Fp>\u003Cp>Speaker 1: We can switch light theme. Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: It would be better for the projector.\u003C\u002Fp>\u003Cp>Speaker 1: Now this is an older instance. Now I have to see where it was in here.\u003C\u002Fp>\u003Cp>Speaker 0: Right there. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Right there.\u003C\u002Fp>\u003Cp>Speaker 0: I believe so. If you scroll down. If it's an older you you should, just change your browser theme. It was it not user configurable? No.\u003C\u002Fp>\u003Cp>What was it? It's a 10 by 4. It's not write that theme.\u003C\u002Fp>\u003Cp>Speaker 1: There we go.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That's better.\u003C\u002Fp>\u003Cp>Speaker 1: It's easier to be read.\u003C\u002Fp>\u003Cp>Speaker 2: And now the database needs some time to load again.\u003C\u002Fp>\u003Cp>Speaker 1: Apparently, I have a little bit of a bad connection with about 2 millisecond 2 seconds until database response. But, yeah, here we go. We have anonymized data. We know where user is, and we know who the user is, but only by ID, which is interesting because we have to handle things like a complete inventory in JSON. And all of those IDs in this case correspond to, where do we have it now?\u003C\u002Fp>\u003Cp>Why is it not showing what's above there? Can have There you go. Useful. They have already one part of the bot, and that's the ability to break everything. That's the ability to get yourself a random card dropped.\u003C\u002Fp>\u003Cp>In this case, the only thing that's actually random is the card being dropped. The value is always the same and it actually gains a little bit by liking it, having, yeah, people like, dislike, and delete their accounts. In this case, affecting the value because those are usually counted together. You have the ability to claim a card by clicking on a button. That then adds it directly into your inventory which is instantly reflected in direct us.\u003C\u002Fp>\u003Cp>And you can actually view your collection of items, which is a little bit problematic at the moment. You have a larger inventory because all the information gets invalidated after about 10 minutes of the session being started. So for 200 cards, you might need more than 2 minutes to scroll through all of them. I think Kevin might already encountered that problem. So that one is keeping a server specific record of your inventory.\u003C\u002Fp>\u003Cp>So you can have multiple inventories and multiple servers at some point. It was planned to have a global one but, Ono decided against it. And where directors starts to actually be more involved is by running the client command, and I hope the website still works because If we go where is it? It's like come here. You already claimed your reward.\u003C\u002Fp>\u003Cp>Hold on. No. It seems that you haven't voted yet. I hate it. That one will send you to a different website, which then makes you vote for a bot, which sends information to, directors.\u003C\u002Fp>\u003Cp>Funny enough in a format that is by default not understood by directors which means I had to include a custom hook which passes all the data and then sends it back to directors as JSON formatted.\u003C\u002Fp>\u003Cp>Speaker 0: So\u003C\u002Fp>\u003Cp>Speaker 2: log What are you voting for?\u003C\u002Fp>\u003Cp>Speaker 1: Especially not especially. Essentially, for the popularity of that specific application.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, really? So that's like an external ranking of different Discord bots. And, obviously, bot creators want their bot to be not voted. So this is an incentive mechanism built into WishBot. But once every 12 hours, I think is how is how often you can vote here, you vote.\u003C\u002Fp>\u003Cp>I assume it sends, yeah, it sends like a webhook with this archaic data format to digest. That's handled, and then you can claim a reward inside of WishBot for doing that every 12 hours.\u003C\u002Fp>\u003Cp>Speaker 1: There you go. We have voted. And now that just to show that one, this data is actually present in here. And it has the time to live because, this k b storage is cleaning itself up. 300 3,600 seconds.\u003C\u002Fp>\u003Cp>It's exactly an hour that this data is, living inside it. So it should be enough time for anyone to run a simple command and get their reward, which in this case is totally random. You get what you get. The drops, those are static, but the value that you get is random based on nothing but a number generator.\u003C\u002Fp>\u003Cp>Speaker 0: And so this currency effectively in the game, the star\u003C\u002Fp>\u003Cp>Speaker 1: that's I\u003C\u002Fp>\u003Cp>Speaker 0: think it's called that star. You get that as a user, but that's also held on cards. So you have\u003C\u002Fp>\u003Cp>Speaker 1: a value gap. You have value which you can use to essentially get more cards. So it is a self perpetuating cycle. You get more cards, you sell more cards, and you end up at some point with the ones that you would like. Like for example, Mickey Mouse.\u003C\u002Fp>\u003Cp>Let's see. Yeah. I do have another currency and it's generating those cards currently based off list of 1,000 entries into database, which is including those images which are pulled off directors directly. So, yeah, that's about.\u003C\u002Fp>\u003Cp>Speaker 0: And there's a trading mechanism.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's a trading mechanism of for which I need a second user to actually initiate it.\u003C\u002Fp>\u003Cp>Speaker 0: Well, we we all know there's a trading Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Any questions?\u003C\u002Fp>\u003Cp>Speaker 3: So this obscure data format that you're getting from the the voting side. How are you handling that with the customer input? Or is that being done right with the flow?\u003C\u002Fp>\u003Cp>Speaker 1: It's done with the flow, which is listening on all push requests that are coming in. And in that specific, case, I have to, I had to create a custom hook, which then allowed directors to pull any data which is not default JSON formatted and converted into the correct format. Let's see if I can act yeah. I can't pull it up here because that one doesn't have remote access to the machine that's running it.\u003C\u002Fp>\u003Cp>Speaker 0: So Directus is handling the the voting and claiming mechanism. It's obviously handling user registration effectively. Hello. It's also handling, a user's collection and\u003C\u002Fp>\u003Cp>Speaker 1: stuff\u003C\u002Fp>\u003Cp>Speaker 0: like that. There's the trading mechanism which it manages. Is Is there anything else that we are missing in there?\u003C\u002Fp>\u003Cp>Speaker 1: Actually, no. It's besides handling data. Directors is in this stage not having any additional part in it.\u003C\u002Fp>\u003Cp>Speaker 0: There's also subscribing, you know, the same monthly that's not directors, though.\u003C\u002Fp>\u003Cp>Speaker 1: To a degree, it actually is because directors has an internal record of everyone that ever subscribed. I just have to see if there's personal data on that one. So let me just Okay. If we don't scroll to the right, it's okay. So it's keeping a track of everyone that has subscribed within the last, 6 months and still has one active.\u003C\u002Fp>\u003Cp>So it's essentially, clearing up that list almost every day. In this case, it's getting from Ko Fi an information about everyone that subscribed. It's only getting their email address though. So in this case, directors has to keep a record of the email address, which in this court, a user can use their own email address. And since they know what they subscribe for, they can select what they subscribe for out of a list.\u003C\u002Fp>\u003Cp>I can actually show that one. For example not not activate the server sub. That shouldn't work because activate server sub was never activated. Jesus. Oh, yeah.\u003C\u002Fp>\u003Cp>Here. You have to put in your email. And in here, you have a list of tiers which are available. And the combination of both is actually used to verify you as a user because Ko Fi doesn't know who you are on Discord. Same with Discord, they don't know who you are on Kofi.\u003C\u002Fp>\u003Cp>Speaker 0: And you can only store from Discord, arbitrary user ID that means something outside of\u003C\u002Fp>\u003Cp>Speaker 1: Discord is actually by default not even giving you the user's email address, and that would not help much because you most likely or not most likely but maybe didn't use the same email address for 2 services. So that would break the whole system. So in this case, they have to provide the data themselves, then select the correct tier, which is interesting for anyone to crack in the beginning because they don't know your personal data and they don't know you even subscribe because Kofi is not making that information public.\u003C\u002Fp>\u003Cp>Speaker 0: That's an interesting workaround to I have a question.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: I've built a Discord bot before. Terrible development experience. It's really, really rough. And it just lived as an express app.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Where's the bot running? Is it running as part of, like, the direct project or is it running elsewhere?\u003C\u002Fp>\u003Cp>Speaker 1: It's running externally, on the same virtual machine as Directus, but not within Directus, especially because it's using Sapphire, which requires a ESM module. And, as I think, directors as a project is not set up to handle ESM. Maybe. At least not the version that I'm using here. It would just instantly, crash the moment I build it.\u003C\u002Fp>\u003Cp>Speaker 0: Interesting. That's been one thing I I wondered about particularly is could you feasibly run a Discord bot entirely within\u003C\u002Fp>\u003Cp>Speaker 1: within directors and users? But\u003C\u002Fp>\u003Cp>Speaker 0: if you're not doing it here, we need we need, you know, talk about that possibility. So just curious.\u003C\u002Fp>\u003Cp>Speaker 1: You would just have to have a flow which is handling the incoming data because Discord is sending it to one endpoint, so, like, an interaction, webhook endpoint. And then you essentially just send a fetch request back to Discord, and it does what you want.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: Would you like to show how you create new cars? Because I've seen your use of, like, input groups. Maybe some, people get a sense of how the Reactors looks on the back end. Yeah. Always interesting to see how others create content.\u003C\u002Fp>\u003Cp>Mhmm.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, nice. Hi, camera. What is this? Ah, there we go. The cards.\u003C\u002Fp>\u003Cp>Speaker 0: So at the moment when you create cards, you do it. It is done here within the Data Studio?\u003C\u002Fp>\u003Cp>Speaker 1: It's done within the Data Studio. Yes. The ID, we set it actually as something manual because in the beginning, we imported a lot of data, and we actually modified some IDs because we had duplicates and it's kind of hard to import data and have duplicate IDs which are the primary key. So I kinda let the creator of those cars handle it themselves rather than me handling more than a 1000 entries. Mhmm.\u003C\u002Fp>\u003Cp>Name wish listed is something that is default to 20 and deactivated is, a feature that we use for essentially simulating misprints, like creating cards. Sometimes there's a mistake in the card, maybe in the image of it. It's getting marked as a misprint. But until it's getting marked as that, it's actually available. So\u003C\u002Fp>\u003Cp>Speaker 0: And it doesn't stop being available afterwards. It just stops\u003C\u002Fp>\u003Cp>Speaker 1: being distributed. It stops being distributed, which makes them kind of rare to get. Cards actually have a color in the sense of Discord messages, especially embed messages, always having a little bar on the left. We have a default color for that one. We never actually use anything else.\u003C\u002Fp>\u003Cp>The image is stored within directors manually setting a value, and we actually have a larger list of movies and series where you can select your character to be from. We use that way because we don't want to enter that data multiple times with thousands of entries and rather have that one once. And a little description of the character which is shown on the embed as well. Value? Value is, how much the card is worth in terms of the bot's own currency called stardust.\u003C\u002Fp>\u003Cp>Speaker 0: But you said it was randomized. Oh, no. It isn't. What's randomized? Are the claims Yes.\u003C\u002Fp>\u003Cp>Speaker 1: The value itself is fixed. Otherwise, you wouldn't be having, high value cards which stay the same value. Because you could have the same card with the same ID multiple times. But what differentiates the one card with ID 1 and the other card with ID 1? Actually, nothing really.\u003C\u002Fp>\u003Cp>Unless it has a difference, which means then it's not the same card anymore, which means a new ID is being created.\u003C\u002Fp>\u003Cp>Speaker 0: Is there any limitations, placed by Discord on response times to the slash command. And is there anything interesting you've had to do to do with the\u003C\u002Fp>\u003Cp>Speaker 1: 3 milliseconds? The initial response has to be done within 3 milliseconds.\u003C\u002Fp>\u003Cp>Speaker 0: 3?\u003C\u002Fp>\u003Cp>Speaker 1: Yes. Okay. 3 milliseconds initial response means you get a ping and you instantly send one back.\u003C\u002Fp>\u003Cp>Speaker 0: It's breaking. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: That's how much time they give you. That it's an ephemeral ready response.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: It says 3 milliseconds in the docs even though it isn't.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Do but do you have let's say, I'd say slash collection. Right? And you're generating a list of 200 cards with images, and you're grabbing those from directors. Is how long do you have to respond to that before it will time out?\u003C\u002Fp>\u003Cp>And then I suppose there's another interesting thing around, like, image, like, image sizes or optimizer. And you can't just say no. None of\u003C\u002Fp>\u003Cp>Speaker 1: that is\u003C\u002Fp>\u003Cp>Speaker 0: the case, but I have to believe there's some kind of limits there.\u003C\u002Fp>\u003Cp>Speaker 1: From back when the bot was created, the limitation of response time was 60 seconds to any command. It's pretty long. Yeah. At the same time, if you use image generating AI and you usually take more than 60 seconds, you would have to consume the command and then send a response later from an external event because otherwise, it would just start breaking everything. And what was the other question?\u003C\u002Fp>\u003Cp>Speaker 0: Image optimizations, whether there's anything\u003C\u002Fp>\u003Cp>Speaker 1: first of all, we, from the start, created images with a small size because that is a self hosted instance. It means every image that is shown in Discord is actively being requested from that instance. It's even though it's in a data center, has a lot of bandwidth, it still starts to overload directors as a self hosted instance in that VM if I get more than a couple thousand requests a second because it can't handle it anymore. And we kind of figured out that, funny enough that's another thing but Directus breaks regularly. No.\u003C\u002Fp>\u003Cp>Does it break now? I think it's going to break.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And what's what broke there? Do you know why?\u003C\u002Fp>\u003Cp>Speaker 1: That's the one point. I can't tell you why because nothing shows up in the logs. It just stops loading. So it essentially times out trying to load that, not only that specific image, but all of them. Interesting.\u003C\u002Fp>\u003Cp>But\u003C\u002Fp>\u003Cp>Speaker 0: You you mean the video that I have the recording of and will take through an editing workflow before I\u003C\u002Fp>\u003Cp>Speaker 1: Oh, yeah. Definitely. That's cool. That's cool. Nah.\u003C\u002Fp>\u003Cp>Doesn't want to load it. It's actually not even loading the project image anymore. It shouldn't be.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Anyway, cool. Are there any other interesting, like, technical notes about how this was come how this was put together, usage of directors, and of course, any questions for that maybe? I feel like we're we're at that point.\u003C\u002Fp>\u003Cp>Speaker 1: Right? You have no\u003C\u002Fp>\u003Cp>Speaker 0: more planned content. Right?\u003C\u002Fp>\u003Cp>Speaker 1: I have no more planned content indeed. Yeah. And there is actually nothing else in here anymore besides a little log of arrows and bugs that sometimes happen. But I think that the directors or that specific instance is at the point where it, doesn't want to cooperate anymore. Oh, actually, there are But most of them seem to be acknowledged.\u003C\u002Fp>\u003Cp>So it's actually keeping a track of every error that happens within the Discord bot because the Discord bot is writing a log, but it's a rotating log. So this way I can keep track of them and actually connect them to the user that triggered it. So I can get get back to them, can figure out what happens. What happened if I go in here? Let's see what it is.\u003C\u002Fp>\u003Cp>Ah, nice. That's the response from Discord, by the way.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. The message is there. Come on. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: And that's an interesting one. FaceTime. Interesting.\u003C\u002Fp>\u003Cp>Speaker 0: Really interesting projects. I think a really novel use of directus. And one that I I certainly didn't see before this project and haven't\u003C\u002Fp>\u003Cp>Speaker 3: database? I a database?\u003C\u002Fp>\u003Cp>Speaker 1: I selected directors instead of a database because the creators don't necessarily have the technical lodge, knowledge to put in data manually into the database, let alone then put files on a file server, connect them to the database, and have that everything retrieved. This is making it very easy for me to administer, administrate, for me to develop for it, and for anyone creating content to add to it. At the same time, I can see logs about things that happen outside of directors.\u003C\u002Fp>","My name is Milt. As you already know, I'm going to be talking about directors in combination with Discord bots and in this specific case, a Disney themed trading card game. Very simple. Stage 1, the concept where I got into the project. It was a project which I took over from someone else. It's a trading card game. It's Disney themed. It had to be a Discord bot because they just wanted it to be, very easy to market to a new user base and very easy to interact with. That's where the interaction base is coming in because in Discord that means not just sending a message but sending a interaction request command. So doing a slash saying I want something and giving some parameters to it and making it work and having it integrated with ranking sites. So it shows up on the ranking sites, you vote for them, you get something in Discord using the bot, and the more you vote the more you get. And we had some early problems which were caused by the project. First of all, being, mainly hosted on a different site. Let's just not call them how they actually are. Fast code, let's say. The problem was that the code was spread around multiple projects. They were all interconnected with each other. That caused problems because things were, defined multiple times in different ways, sometimes with differing definitions. So a is equal to b, but b also is equal to c. At the same time, a is 1 and b is 2. And we had high response times because multiple projects interacting with another on, for some reason multiple continents as well. And multiple data sources was we had multiple Google Sheets which were used as databases. I think we know where that one is going the moment we start getting into multiple 1,000 entries per Google Sheet. So stage 1, consolidation, getting everything into one application, getting all the projects combined into 1, which still used, the same platform for it in the beginning, getting all the data sources merged into 1 as well, which later on made it a little bit easier exporting all the data and getting rid of those data sources. And we used a MySQL database as an intermediary before we started switching over the infrastructure to something called Sapphire, which is, a framework for creating Discord bots. And here's where Directus comes in. We tried a lot of CMS systems and we wanted to have one thing. We wanted to have it quickly deployable. We wanted to have it highly adaptable. Easy to use, not only for administrators and developers, but for, creators, like creating new material inside the CMS, event based and cron based, automation, also known as flows, a REST and a GraphQL API, which was a requirement set by the owner of the project. I just rolled with it. We never used it since we then just use the director's SDK for everything that, happened. And for everything that we didn't use it, we use the internal API. And then it had to have the option to be either self or cloud hosted depending on the scope of the project later on. Funny enough, if you start with self hosted, I heard it's quite easy to get a self hosted directors instance into cloud hosted? Fairly easy? Oh, I'm not sure you can let me for validation. Sure. That sounds correct. It's easier than just, here, have a Google Sheet. I mean, you said you are real low. Yeah. We we, leapfrog leapfrog that. Yeah. And this code verification causes one big problem. It's you have to anonymize all the data. You have to follow GDPR, g yeah. GDPR. You have a lot of restrictions when it comes to collecting data from a user. So, generally, you're only allowed to know their ID, and that's all. No name. No information about what they did, what they wrote. There's exceptions to it, and that means you can handle those information when an event happens and you're not able to save it outside of that event. And infrastructure security, that's something that they recently added. They want you to essentially not allow anyone to access your bot on an administrative, capacity. Meaning, those servers that it's running on usually have to be secured by, for example, SSH with identity tokens instead of passwords. And funny enough, they actually checked it. So that's the quick rundown of the project and can actually look into it, how that, structure looks like. Here I actually see the user database. A lot of gibberish. Could you switch the light theme? We can switch light theme. Yeah. It would be better for the projector. Now this is an older instance. Now I have to see where it was in here. Right there. Yeah. Right there. I believe so. If you scroll down. If it's an older you you should, just change your browser theme. It was it not user configurable? No. What was it? It's a 10 by 4. It's not write that theme. There we go. Yeah. That's better. It's easier to be read. And now the database needs some time to load again. Apparently, I have a little bit of a bad connection with about 2 millisecond 2 seconds until database response. But, yeah, here we go. We have anonymized data. We know where user is, and we know who the user is, but only by ID, which is interesting because we have to handle things like a complete inventory in JSON. And all of those IDs in this case correspond to, where do we have it now? Why is it not showing what's above there? Can have There you go. Useful. They have already one part of the bot, and that's the ability to break everything. That's the ability to get yourself a random card dropped. In this case, the only thing that's actually random is the card being dropped. The value is always the same and it actually gains a little bit by liking it, having, yeah, people like, dislike, and delete their accounts. In this case, affecting the value because those are usually counted together. You have the ability to claim a card by clicking on a button. That then adds it directly into your inventory which is instantly reflected in direct us. And you can actually view your collection of items, which is a little bit problematic at the moment. You have a larger inventory because all the information gets invalidated after about 10 minutes of the session being started. So for 200 cards, you might need more than 2 minutes to scroll through all of them. I think Kevin might already encountered that problem. So that one is keeping a server specific record of your inventory. So you can have multiple inventories and multiple servers at some point. It was planned to have a global one but, Ono decided against it. And where directors starts to actually be more involved is by running the client command, and I hope the website still works because If we go where is it? It's like come here. You already claimed your reward. Hold on. No. It seems that you haven't voted yet. I hate it. That one will send you to a different website, which then makes you vote for a bot, which sends information to, directors. Funny enough in a format that is by default not understood by directors which means I had to include a custom hook which passes all the data and then sends it back to directors as JSON formatted. So log What are you voting for? Especially not especially. Essentially, for the popularity of that specific application. Oh, really? So that's like an external ranking of different Discord bots. And, obviously, bot creators want their bot to be not voted. So this is an incentive mechanism built into WishBot. But once every 12 hours, I think is how is how often you can vote here, you vote. I assume it sends, yeah, it sends like a webhook with this archaic data format to digest. That's handled, and then you can claim a reward inside of WishBot for doing that every 12 hours. There you go. We have voted. And now that just to show that one, this data is actually present in here. And it has the time to live because, this k b storage is cleaning itself up. 300 3,600 seconds. It's exactly an hour that this data is, living inside it. So it should be enough time for anyone to run a simple command and get their reward, which in this case is totally random. You get what you get. The drops, those are static, but the value that you get is random based on nothing but a number generator. And so this currency effectively in the game, the star that's I think it's called that star. You get that as a user, but that's also held on cards. So you have a value gap. You have value which you can use to essentially get more cards. So it is a self perpetuating cycle. You get more cards, you sell more cards, and you end up at some point with the ones that you would like. Like for example, Mickey Mouse. Let's see. Yeah. I do have another currency and it's generating those cards currently based off list of 1,000 entries into database, which is including those images which are pulled off directors directly. So, yeah, that's about. And there's a trading mechanism. Yeah. That's a trading mechanism of for which I need a second user to actually initiate it. Well, we we all know there's a trading Yeah. Yeah. Any questions? So this obscure data format that you're getting from the the voting side. How are you handling that with the customer input? Or is that being done right with the flow? It's done with the flow, which is listening on all push requests that are coming in. And in that specific, case, I have to, I had to create a custom hook, which then allowed directors to pull any data which is not default JSON formatted and converted into the correct format. Let's see if I can act yeah. I can't pull it up here because that one doesn't have remote access to the machine that's running it. So Directus is handling the the voting and claiming mechanism. It's obviously handling user registration effectively. Hello. It's also handling, a user's collection and stuff like that. There's the trading mechanism which it manages. Is Is there anything else that we are missing in there? Actually, no. It's besides handling data. Directors is in this stage not having any additional part in it. There's also subscribing, you know, the same monthly that's not directors, though. To a degree, it actually is because directors has an internal record of everyone that ever subscribed. I just have to see if there's personal data on that one. So let me just Okay. If we don't scroll to the right, it's okay. So it's keeping a track of everyone that has subscribed within the last, 6 months and still has one active. So it's essentially, clearing up that list almost every day. In this case, it's getting from Ko Fi an information about everyone that subscribed. It's only getting their email address though. So in this case, directors has to keep a record of the email address, which in this court, a user can use their own email address. And since they know what they subscribe for, they can select what they subscribe for out of a list. I can actually show that one. For example not not activate the server sub. That shouldn't work because activate server sub was never activated. Jesus. Oh, yeah. Here. You have to put in your email. And in here, you have a list of tiers which are available. And the combination of both is actually used to verify you as a user because Ko Fi doesn't know who you are on Discord. Same with Discord, they don't know who you are on Kofi. And you can only store from Discord, arbitrary user ID that means something outside of Discord is actually by default not even giving you the user's email address, and that would not help much because you most likely or not most likely but maybe didn't use the same email address for 2 services. So that would break the whole system. So in this case, they have to provide the data themselves, then select the correct tier, which is interesting for anyone to crack in the beginning because they don't know your personal data and they don't know you even subscribe because Kofi is not making that information public. That's an interesting workaround to I have a question. Yeah. I've built a Discord bot before. Terrible development experience. It's really, really rough. And it just lived as an express app. Yeah. Where's the bot running? Is it running as part of, like, the direct project or is it running elsewhere? It's running externally, on the same virtual machine as Directus, but not within Directus, especially because it's using Sapphire, which requires a ESM module. And, as I think, directors as a project is not set up to handle ESM. Maybe. At least not the version that I'm using here. It would just instantly, crash the moment I build it. Interesting. That's been one thing I I wondered about particularly is could you feasibly run a Discord bot entirely within within directors and users? But if you're not doing it here, we need we need, you know, talk about that possibility. So just curious. You would just have to have a flow which is handling the incoming data because Discord is sending it to one endpoint, so, like, an interaction, webhook endpoint. And then you essentially just send a fetch request back to Discord, and it does what you want. Yeah. Would you like to show how you create new cars? Because I've seen your use of, like, input groups. Maybe some, people get a sense of how the Reactors looks on the back end. Yeah. Always interesting to see how others create content. Mhmm. Oh, nice. Hi, camera. What is this? Ah, there we go. The cards. So at the moment when you create cards, you do it. It is done here within the Data Studio? It's done within the Data Studio. Yes. The ID, we set it actually as something manual because in the beginning, we imported a lot of data, and we actually modified some IDs because we had duplicates and it's kind of hard to import data and have duplicate IDs which are the primary key. So I kinda let the creator of those cars handle it themselves rather than me handling more than a 1000 entries. Mhmm. Name wish listed is something that is default to 20 and deactivated is, a feature that we use for essentially simulating misprints, like creating cards. Sometimes there's a mistake in the card, maybe in the image of it. It's getting marked as a misprint. But until it's getting marked as that, it's actually available. So And it doesn't stop being available afterwards. It just stops being distributed. It stops being distributed, which makes them kind of rare to get. Cards actually have a color in the sense of Discord messages, especially embed messages, always having a little bar on the left. We have a default color for that one. We never actually use anything else. The image is stored within directors manually setting a value, and we actually have a larger list of movies and series where you can select your character to be from. We use that way because we don't want to enter that data multiple times with thousands of entries and rather have that one once. And a little description of the character which is shown on the embed as well. Value? Value is, how much the card is worth in terms of the bot's own currency called stardust. But you said it was randomized. Oh, no. It isn't. What's randomized? Are the claims Yes. The value itself is fixed. Otherwise, you wouldn't be having, high value cards which stay the same value. Because you could have the same card with the same ID multiple times. But what differentiates the one card with ID 1 and the other card with ID 1? Actually, nothing really. Unless it has a difference, which means then it's not the same card anymore, which means a new ID is being created. Is there any limitations, placed by Discord on response times to the slash command. And is there anything interesting you've had to do to do with the 3 milliseconds? The initial response has to be done within 3 milliseconds. 3? Yes. Okay. 3 milliseconds initial response means you get a ping and you instantly send one back. It's breaking. Yeah. That's how much time they give you. That it's an ephemeral ready response. Yeah. It says 3 milliseconds in the docs even though it isn't. Yeah. Do but do you have let's say, I'd say slash collection. Right? And you're generating a list of 200 cards with images, and you're grabbing those from directors. Is how long do you have to respond to that before it will time out? And then I suppose there's another interesting thing around, like, image, like, image sizes or optimizer. And you can't just say no. None of that is the case, but I have to believe there's some kind of limits there. From back when the bot was created, the limitation of response time was 60 seconds to any command. It's pretty long. Yeah. At the same time, if you use image generating AI and you usually take more than 60 seconds, you would have to consume the command and then send a response later from an external event because otherwise, it would just start breaking everything. And what was the other question? Image optimizations, whether there's anything first of all, we, from the start, created images with a small size because that is a self hosted instance. It means every image that is shown in Discord is actively being requested from that instance. It's even though it's in a data center, has a lot of bandwidth, it still starts to overload directors as a self hosted instance in that VM if I get more than a couple thousand requests a second because it can't handle it anymore. And we kind of figured out that, funny enough that's another thing but Directus breaks regularly. No. Does it break now? I think it's going to break. Yeah. And what's what broke there? Do you know why? That's the one point. I can't tell you why because nothing shows up in the logs. It just stops loading. So it essentially times out trying to load that, not only that specific image, but all of them. Interesting. But You you mean the video that I have the recording of and will take through an editing workflow before I Oh, yeah. Definitely. That's cool. That's cool. Nah. Doesn't want to load it. It's actually not even loading the project image anymore. It shouldn't be. Yeah. Anyway, cool. Are there any other interesting, like, technical notes about how this was come how this was put together, usage of directors, and of course, any questions for that maybe? I feel like we're we're at that point. Right? You have no more planned content. Right? I have no more planned content indeed. Yeah. And there is actually nothing else in here anymore besides a little log of arrows and bugs that sometimes happen. But I think that the directors or that specific instance is at the point where it, doesn't want to cooperate anymore. Oh, actually, there are But most of them seem to be acknowledged. So it's actually keeping a track of every error that happens within the Discord bot because the Discord bot is writing a log, but it's a rotating log. So this way I can keep track of them and actually connect them to the user that triggered it. So I can get get back to them, can figure out what happens. What happened if I go in here? Let's see what it is. Ah, nice. That's the response from Discord, by the way. Yeah. The message is there. Come on. Yeah. And that's an interesting one. FaceTime. Interesting. Really interesting projects. I think a really novel use of directus. And one that I I certainly didn't see before this project and haven't database? I a database? I selected directors instead of a database because the creators don't necessarily have the technical lodge, knowledge to put in data manually into the database, let alone then put files on a file server, connect them to the database, and have that everything retrieved. This is making it very easy for me to administer, administrate, for me to develop for it, and for anyone creating content to add to it. At the same time, I can see logs about things that happen outside of directors.",[4821],"34312cb6-c468-4edf-968b-b674542d1bbd",[],{"year":978,"number":4824,"id":4825,"episodes":4826,"show":4828},50,"63ed15fc-e3f3-48cb-9d72-a437171d1001",[4827,4808],"380ee198-9850-4e7f-a849-e1f328b0b6c0",{"title":4829,"slug":4830},"Around the World","around-the-world",{"id":4827,"slug":4832,"vimeo_id":4833,"description":4834,"tile":4835,"length":939,"resources":12,"people":4836,"episode_number":213,"published":4768,"title":4838,"video_transcript_html":4839,"video_transcript_text":4840,"content":12,"seo":12,"status":19,"episode_people":4841,"recommendations":4843,"season":4844},"diy-wedding","910770710","Hold on to your bowties and bouquets as we dive head-first into the world of wedding planning. Except this isn't any wedding - it's a wedding between two nerds who know how to code. We'll discuss the hacky projects built to help us organize and deliver a magical day - from custom a custom invite RSVP system, email and text alerts, security-list generator, and some of the weirder ideas we ran out of time to build.  ","3bc39a09-5c4d-4b6f-8cb5-48447db3a84e",[4837],{"name":2820,"url":2821},"DIYing My Wedding The Hacker's Way","\u003Cp>Speaker 0: Hello. My name is Kevin. I run developer relations at Directus. But the reason I say that more specifically is because my past life, as I mentioned to some of you earlier, was running an events agency. I've run over 300 hackathons, meetups, conferences, workshops, on behalf of clients.\u003C\u002Fp>\u003Cp>So we're actually gonna start this by talking about the basic recipe for every single event regardless of format. Firstly, you need a reason for people to meet. Here, it's because we are interested in some way, shape, or form about directors. You need a place and a time, and you actually need people to be in the space for the reason that you're gathering them. Everything else above that is actually extra.\u003C\u002Fp>\u003Cp>You know, the food, the drinks, it's extra. We don't need it. The stickers, we don't need it. We don't need anything else. As long as people, you know, know what to expect, that's all good.\u003C\u002Fp>\u003Cp>And remember, I have run events 100 of times over, which means organizing a wedding should be simple. Right? This is right in my wheelhouse. Basically, what I wake up to do in the morning. I'm gonna spoil it for you and tell you that, no, I was cocky and I thought it was easier than it was.\u003C\u002Fp>\u003Cp>But before we realized that, my wife and I were in too deep. We we were organizing our own wedding. But we did have one thing on our side, and that is the fact that we are huge nerds and we like to hack together little solutions to problems in our lives. So this talk is going to take us through some of those little projects that we built to try and make that day easier easier to to run. Yeah.\u003C\u002Fp>\u003Cp>Run. Let's use the word run. So firstly, we already had the reason to meet, obviously. We already sorted the venues and dates, so we just needed the people. And that starts, of course, with weddings, with sending out wedding invites.\u003C\u002Fp>\u003Cp>Now the first big difference we realized with a wedding is that unlike tech events, people expect invites to feel quite personal to them. But we also need to gather data consistently from them, which is often difficult if you're using physical media. And one other quirk with our setup is that we had the ceremony, you know, the I do and then we had the reception, the party bit afterwards. And the ceremony venue could only hold about a quarter of the people as our reception venue. So immediately, we effectively had a ticket type, 2 ticket types.\u003C\u002Fp>\u003Cp>We had one that brought people to the whole day and we had one just for people coming to the reception. This is actually very common. I I didn't realize upfront so I feel less bad about it in hindsight. But we have these 2 ticket types. Another variable is that not every guest was given a plus one.\u003C\u002Fp>\u003Cp>We did this to manage budgets, guest lists. You know, we wanted to know people who were coming, so that's that's important. This was a case by case. So not everyone got a plus one. So once again, there's another variable on a ticket type.\u003C\u002Fp>\u003Cp>And the final consideration of an event that is somewhat unique to other events that I've run is that one person will often act as the coordinator for a whole group. I will not send an invite to my mom and an invite to my dad. I'll send an invite to my parents, and realistically, one of them is gonna take take point on on, you know, accepting on behalf of the party, but organizing plans on behalf of the whole party. But we also needed to know out of that party who's coming and who isn't because it isn't a given that everyone's gonna come or everyone is not going to come. So it's all these interesting variables, that made this actually quite a complex delivery, let's call it.\u003C\u002Fp>\u003Cp>I'm very much talking like an event organizer here. Very complex delivery. So how do we make this happen? I work for Directus. So, of course, I use Directus, which I think means I got to plan my wedding at least partially on work time.\u003C\u002Fp>\u003Cp>So we set up we set up a table for individuals individuals, and that in that included the, the name, dietary, and access requirements for an individual, whether or not they were coming, and whether this person was a plus one. I'm really happy we added this filled up front because as as we got closer, I looked at the list and went, who the fuck is that a couple of times. Fortunately, they were marked as a plus one. And we'll talk about it in a moment. They were linked to an invite so I could work out who's plus one they were.\u003C\u002Fp>\u003Cp>Right? So that that was that was worthwhile. We also had a wellness certification, which we'll talk about a little bit later. Now each invite had a slug, which we used in the URL for the custom invites, some nice custom text so we can, you know, write a little message that's a little more personal to them, a link to 1 or more of those people, a pair of booleans to dictate whether this invite included an invite to the ceremony, whether or not they were given a plus one, and lead contact phone number and email address. We don't need that for every individual, just the person taking taking, you know, taking point for the for the group, and a date field on which that invite was used, which we effectively checked if it was null, if it wasn't used, and if it was used if it had a date value it had been used and we knew when.\u003C\u002Fp>\u003Cp>So I took, I did all of this in a spreadsheet, wrote a one off script to drag it all into direct us. Fantastic. Now, a set of invites, we have a set of people, or the other way around. Set of people and a set of invites. And the other part of this invite system was a web application.\u003C\u002Fp>\u003Cp>It was an express app. You used view on the front end, used the direct us SDK to render out invite pages and actually accept the responses. Here's an example of what a basic invite looked like. So this person, we still love them very much, of course, but they're only coming to the reception and they don't get a plus one. So this is this is what that looked like.\u003C\u002Fp>\u003Cp>So each person RSVPs yes or no. Anyone who says yes, we ask for their dietary and or access requirements. The group phone number, the group email address, you hit send. And we try to get everyone to respond to this even if the answer was just them going, no, nope, submit. And at least we know where we stand.\u003C\u002Fp>\u003Cp>This is an example of what I'll call a fully loaded invite. This person gets a plus one. They also get invited to both parts of the day. There's a bit more information up there. And you'll notice this box here, they say they want to they want to bring a plus one, you know, they they have to not everyone we gave that ability to did, and then they give us information about the plus one.\u003C\u002Fp>\u003Cp>First name, last name, dietary, and access. Once again, it's expected that one person will still take point for the whole group including the plus one. On submit, we update each person attached to that invite. We create a new person if they're a plus one and immediately link them with that invite. So that's really useful for, again, going, who the hell is this person?\u003C\u002Fp>\u003Cp>Oh, they're they're connected to Bob's invites, so they're they're Bob's plus 1. And also, updated the invite as used by adding in the dates. We could also keep track of when people when people are responding as well. And honestly, it worked great. You know?\u003C\u002Fp>\u003Cp>I know some traditionalists are like, you send paper invites to people. But these were instant. They were free. They were flexible. They were still personalized.\u003C\u002Fp>\u003Cp>They allowed for consistent data entry. Didn't kill loads of trees to make it happen. It was grand, actually. It worked fantastically. We ultimately ended up with a list of who could and couldn't make it, a list of invites that we used and not used, and a mark against all the plus ones as well as dietary and access requirements worked great.\u003C\u002Fp>\u003Cp>Part 1, check. We got our people. This is great. Now we are fools who DIY ed our wedding. We also completely serviced our own bars.\u003C\u002Fp>\u003Cp>So let's let's take a moment to talk about what that means when you are organizing drinks for an event, for a big event. This may be knowledge to you as potentially nonevent organizers. There is a service called a wet bar, and that basically is a whole service you hire in. These people basically, it's a service that a bar will offer. They will come with a ton of drinks, staff, glassware, fridges, all the, you know, the the little the the ice buckets and the little tongs for it and all the lemons and limes.\u003C\u002Fp>\u003Cp>They they charge you for all those drinks as if you were in a London bar, which is why shock horror of this is not what we did. And then they take away everything afterwards, including the things they didn't use. It's like bringing the bar to you, but you're paying full bar prices. We didn't do that. We did a dry bar, which means that we we did it all.\u003C\u002Fp>\u003Cp>We rented glassware. We rented ice buckets. We rented all the the the trays and all this shit. We rented more tables for tablecloths. We rented fridges.\u003C\u002Fp>\u003Cp>We bought a ton of drinks at wholesale. And then we just hired people who were bartenders and paid them a good hourly rate to come and and work for us, and we had handled the rest of it. You know, bought all the spirits and they obviously were significantly cheaper than if I was gonna buy that shot by shot from a bar. In fact, just as a just as a note, unit cost of every drink, soft or or alcoholic, just the average cost per drink I work out, if you include stuff like glassware higher, the cost of the person's time split over the number of units, I work out to be about £1.80, which is, like, wicked because you're talking, like, 5, 6, 7, 8 in a London bar otherwise. We saved a ton of money.\u003C\u002Fp>\u003Cp>We did a bunch of work. We saved a bunch of money. But, like, you know, just worth noting there. So we did that. Why what is this what is this gonna do with with stupid hacky projects that maybe in hindsight I shouldn't have done?\u003C\u002Fp>\u003Cp>This no. No. No. Clearly clearly, this is an example because my wedding did not have a total monthly recurring revenue. This is an insights panel inside of directors.\u003C\u002Fp>\u003Cp>You will notice that on this dashboard, there are a set of panels. But you can create new, like, bespoke panels by building panel extensions. And that's what we did. Now in the interest of time, I'm just gonna show you that the outcome of this. We had a collection for drinks recipes, and then built a custom panel for that for the director's insights dashboard builder, which allowed us to set the ratios for each drink and get a shopping list for quantities based on our guest list.\u003C\u002Fp>\u003Cp>So the number on the right is the unit for that given drink based on data in a different collection. For example, Coke Zeros lemonades, they come in crates of 24, but the beers come in boxes of 12. So it's not always you need this many of a thing. Well, I just got it to tell me you need to order 9 units, 9 crepes of this, 9 crepes of this and so on. Just as a as a another note, this took a bit of research.\u003C\u002Fp>\u003Cp>At a wedding, you should expect a guest to have a drink every 45 minutes. There you go. That's a bit of knowledge you didn't have, which you now have. So that just helped us understand how many drinks, you know, how many drinks on average we had. This was super helpful.\u003C\u002Fp>\u003Cp>Was it overengineered? Yes. Would I do it again? No. Would I use a spreadsheet next time?\u003C\u002Fp>\u003Cp>Yes. But we're here. We did it. I get to talk about it. Next, the notification system.\u003C\u002Fp>\u003Cp>One guest told us that our wedding was the best communicated wedding they've ever been to, and they were absolutely right. Yes. It was. You may have noticed when people RSVP'd, we or we spoke about it, we collected the lead contacts' contact information. We collected their phone number.\u003C\u002Fp>\u003Cp>We collected their email address. The moment that was submitted, we ran an automation through directors flows in the Vonage number insights API to standardize the number format because no one can ever put in a number the correct way. Even if you're like, they just no one knows how to write phone numbers. Honestly, it makes me so angry. So we used a whole a whole goddamn API to standardize this.\u003C\u002Fp>\u003Cp>Then we built a second flow for sending messages to our guests. It used, do I have a picture of it? No. Fine. It used this thing called the confirmation prompt dialogue.\u003C\u002Fp>\u003Cp>So, you know, you go to send message, it pops up a box so you can insert some extra information and you hit go. It runs this automation. And in there, it allowed us to filter by ticket type because, you know, some you wanna send a different message to your ceremony and reception guest versus your reception guests. It allowed you to, pick whether you want this to be SMS or email. In the end, everything went both ways, but whatever, there was Vonage SMS API and the resend email API to then send out those emails to guests.\u003C\u002Fp>\u003Cp>Another approach would have just been to push people to external platforms, you know, external lists as soon as they've RSVP'd. So, you know, you RSVP and this flow could just go here, get thrown into an email system list, you know, a Mailchimp list, and then just use their tools in order to send messages and not have to integrate with APIs directly. But by doing this at the source of the data, as soon as people got in touch as they often do, getting closer to the day and saying, I can't make it anymore, Or you go, I forgot to invite someone and, you know, and they well, actually, it's more important the other way where they where they opt out. You don't have to manage that second list and keep them in sync. Every time you run the flow to send a message, it's always using the dataset as it stands right now.\u003C\u002Fp>\u003Cp>Super useful, actually. I was I was into this. I would I would do this again. We made a choice early on that every guest would be able to do a COVID test and just generally say they were feeling good the day our wedding. We had some vulnerable guests.\u003C\u002Fp>\u003Cp>You know, we're responsible for bringing people together. It it it really mattered. So using that same Express and View application as our invite system, we added a button on each invite that the day before was shown that was like, submit your wellness certification. And then they they did that. They, you know, they filled it and said they're feeling okay on behalf of the party and they they hit go.\u003C\u002Fp>\u003Cp>But what this did was it generated a security list with a little mark next to everyone's name, denoting whether they had done this wellness certification or not. Those people who did, fantastic, welcome in. Those who didn't, and there were a tiny handful who just didn't see the message or didn't do it, whatever. We had a set of COVID tests on the door, and our security guard London wedding security guard. Our security guard, you know, got them a drink and went, basically, peace out, do the test, see in 15 minutes.\u003C\u002Fp>\u003Cp>Just, you know, we have this nice outside area. That's that's where you live for 15 minutes. Right? But the security list was live generated as people were filling it in. We were able to generate that, as a page, so that was super useful as well.\u003C\u002Fp>\u003Cp>And there was so much more we could have done. We had every person in a database. Everyone had a unique invite link. We could have checked people in digitally, though I think it really would have felt like a tech conference then and not a wedding. So I'm glad we didn't do that.\u003C\u002Fp>\u003Cp>We thought about building experiences where people would, like, validate themselves with, like, installations and, like like, play around. We just didn't have time to do that. We could have had people share their photos directly and use, like, direct us files to, like, you know, consume that and store that in a central location. But while we didn't have time to do all of that, at least I can now add wedding planning to my skill list, and, no, I won't help you with yours. So that's it, really.\u003C\u002Fp>\u003Cp>That's a bit of fun. Hope you found it interesting. And next time next time? Not next time. Next time, I would hire a wedding planner.\u003C\u002Fp>","Hello. My name is Kevin. I run developer relations at Directus. But the reason I say that more specifically is because my past life, as I mentioned to some of you earlier, was running an events agency. I've run over 300 hackathons, meetups, conferences, workshops, on behalf of clients. So we're actually gonna start this by talking about the basic recipe for every single event regardless of format. Firstly, you need a reason for people to meet. Here, it's because we are interested in some way, shape, or form about directors. You need a place and a time, and you actually need people to be in the space for the reason that you're gathering them. Everything else above that is actually extra. You know, the food, the drinks, it's extra. We don't need it. The stickers, we don't need it. We don't need anything else. As long as people, you know, know what to expect, that's all good. And remember, I have run events 100 of times over, which means organizing a wedding should be simple. Right? This is right in my wheelhouse. Basically, what I wake up to do in the morning. I'm gonna spoil it for you and tell you that, no, I was cocky and I thought it was easier than it was. But before we realized that, my wife and I were in too deep. We we were organizing our own wedding. But we did have one thing on our side, and that is the fact that we are huge nerds and we like to hack together little solutions to problems in our lives. So this talk is going to take us through some of those little projects that we built to try and make that day easier easier to to run. Yeah. Run. Let's use the word run. So firstly, we already had the reason to meet, obviously. We already sorted the venues and dates, so we just needed the people. And that starts, of course, with weddings, with sending out wedding invites. Now the first big difference we realized with a wedding is that unlike tech events, people expect invites to feel quite personal to them. But we also need to gather data consistently from them, which is often difficult if you're using physical media. And one other quirk with our setup is that we had the ceremony, you know, the I do and then we had the reception, the party bit afterwards. And the ceremony venue could only hold about a quarter of the people as our reception venue. So immediately, we effectively had a ticket type, 2 ticket types. We had one that brought people to the whole day and we had one just for people coming to the reception. This is actually very common. I I didn't realize upfront so I feel less bad about it in hindsight. But we have these 2 ticket types. Another variable is that not every guest was given a plus one. We did this to manage budgets, guest lists. You know, we wanted to know people who were coming, so that's that's important. This was a case by case. So not everyone got a plus one. So once again, there's another variable on a ticket type. And the final consideration of an event that is somewhat unique to other events that I've run is that one person will often act as the coordinator for a whole group. I will not send an invite to my mom and an invite to my dad. I'll send an invite to my parents, and realistically, one of them is gonna take take point on on, you know, accepting on behalf of the party, but organizing plans on behalf of the whole party. But we also needed to know out of that party who's coming and who isn't because it isn't a given that everyone's gonna come or everyone is not going to come. So it's all these interesting variables, that made this actually quite a complex delivery, let's call it. I'm very much talking like an event organizer here. Very complex delivery. So how do we make this happen? I work for Directus. So, of course, I use Directus, which I think means I got to plan my wedding at least partially on work time. So we set up we set up a table for individuals individuals, and that in that included the, the name, dietary, and access requirements for an individual, whether or not they were coming, and whether this person was a plus one. I'm really happy we added this filled up front because as as we got closer, I looked at the list and went, who the fuck is that a couple of times. Fortunately, they were marked as a plus one. And we'll talk about it in a moment. They were linked to an invite so I could work out who's plus one they were. Right? So that that was that was worthwhile. We also had a wellness certification, which we'll talk about a little bit later. Now each invite had a slug, which we used in the URL for the custom invites, some nice custom text so we can, you know, write a little message that's a little more personal to them, a link to 1 or more of those people, a pair of booleans to dictate whether this invite included an invite to the ceremony, whether or not they were given a plus one, and lead contact phone number and email address. We don't need that for every individual, just the person taking taking, you know, taking point for the for the group, and a date field on which that invite was used, which we effectively checked if it was null, if it wasn't used, and if it was used if it had a date value it had been used and we knew when. So I took, I did all of this in a spreadsheet, wrote a one off script to drag it all into direct us. Fantastic. Now, a set of invites, we have a set of people, or the other way around. Set of people and a set of invites. And the other part of this invite system was a web application. It was an express app. You used view on the front end, used the direct us SDK to render out invite pages and actually accept the responses. Here's an example of what a basic invite looked like. So this person, we still love them very much, of course, but they're only coming to the reception and they don't get a plus one. So this is this is what that looked like. So each person RSVPs yes or no. Anyone who says yes, we ask for their dietary and or access requirements. The group phone number, the group email address, you hit send. And we try to get everyone to respond to this even if the answer was just them going, no, nope, submit. And at least we know where we stand. This is an example of what I'll call a fully loaded invite. This person gets a plus one. They also get invited to both parts of the day. There's a bit more information up there. And you'll notice this box here, they say they want to they want to bring a plus one, you know, they they have to not everyone we gave that ability to did, and then they give us information about the plus one. First name, last name, dietary, and access. Once again, it's expected that one person will still take point for the whole group including the plus one. On submit, we update each person attached to that invite. We create a new person if they're a plus one and immediately link them with that invite. So that's really useful for, again, going, who the hell is this person? Oh, they're they're connected to Bob's invites, so they're they're Bob's plus 1. And also, updated the invite as used by adding in the dates. We could also keep track of when people when people are responding as well. And honestly, it worked great. You know? I know some traditionalists are like, you send paper invites to people. But these were instant. They were free. They were flexible. They were still personalized. They allowed for consistent data entry. Didn't kill loads of trees to make it happen. It was grand, actually. It worked fantastically. We ultimately ended up with a list of who could and couldn't make it, a list of invites that we used and not used, and a mark against all the plus ones as well as dietary and access requirements worked great. Part 1, check. We got our people. This is great. Now we are fools who DIY ed our wedding. We also completely serviced our own bars. So let's let's take a moment to talk about what that means when you are organizing drinks for an event, for a big event. This may be knowledge to you as potentially nonevent organizers. There is a service called a wet bar, and that basically is a whole service you hire in. These people basically, it's a service that a bar will offer. They will come with a ton of drinks, staff, glassware, fridges, all the, you know, the the little the the ice buckets and the little tongs for it and all the lemons and limes. They they charge you for all those drinks as if you were in a London bar, which is why shock horror of this is not what we did. And then they take away everything afterwards, including the things they didn't use. It's like bringing the bar to you, but you're paying full bar prices. We didn't do that. We did a dry bar, which means that we we did it all. We rented glassware. We rented ice buckets. We rented all the the the trays and all this shit. We rented more tables for tablecloths. We rented fridges. We bought a ton of drinks at wholesale. And then we just hired people who were bartenders and paid them a good hourly rate to come and and work for us, and we had handled the rest of it. You know, bought all the spirits and they obviously were significantly cheaper than if I was gonna buy that shot by shot from a bar. In fact, just as a just as a note, unit cost of every drink, soft or or alcoholic, just the average cost per drink I work out, if you include stuff like glassware higher, the cost of the person's time split over the number of units, I work out to be about £1.80, which is, like, wicked because you're talking, like, 5, 6, 7, 8 in a London bar otherwise. We saved a ton of money. We did a bunch of work. We saved a bunch of money. But, like, you know, just worth noting there. So we did that. Why what is this what is this gonna do with with stupid hacky projects that maybe in hindsight I shouldn't have done? This no. No. No. Clearly clearly, this is an example because my wedding did not have a total monthly recurring revenue. This is an insights panel inside of directors. You will notice that on this dashboard, there are a set of panels. But you can create new, like, bespoke panels by building panel extensions. And that's what we did. Now in the interest of time, I'm just gonna show you that the outcome of this. We had a collection for drinks recipes, and then built a custom panel for that for the director's insights dashboard builder, which allowed us to set the ratios for each drink and get a shopping list for quantities based on our guest list. So the number on the right is the unit for that given drink based on data in a different collection. For example, Coke Zeros lemonades, they come in crates of 24, but the beers come in boxes of 12. So it's not always you need this many of a thing. Well, I just got it to tell me you need to order 9 units, 9 crepes of this, 9 crepes of this and so on. Just as a as a another note, this took a bit of research. At a wedding, you should expect a guest to have a drink every 45 minutes. There you go. That's a bit of knowledge you didn't have, which you now have. So that just helped us understand how many drinks, you know, how many drinks on average we had. This was super helpful. Was it overengineered? Yes. Would I do it again? No. Would I use a spreadsheet next time? Yes. But we're here. We did it. I get to talk about it. Next, the notification system. One guest told us that our wedding was the best communicated wedding they've ever been to, and they were absolutely right. Yes. It was. You may have noticed when people RSVP'd, we or we spoke about it, we collected the lead contacts' contact information. We collected their phone number. We collected their email address. The moment that was submitted, we ran an automation through directors flows in the Vonage number insights API to standardize the number format because no one can ever put in a number the correct way. Even if you're like, they just no one knows how to write phone numbers. Honestly, it makes me so angry. So we used a whole a whole goddamn API to standardize this. Then we built a second flow for sending messages to our guests. It used, do I have a picture of it? No. Fine. It used this thing called the confirmation prompt dialogue. So, you know, you go to send message, it pops up a box so you can insert some extra information and you hit go. It runs this automation. And in there, it allowed us to filter by ticket type because, you know, some you wanna send a different message to your ceremony and reception guest versus your reception guests. It allowed you to, pick whether you want this to be SMS or email. In the end, everything went both ways, but whatever, there was Vonage SMS API and the resend email API to then send out those emails to guests. Another approach would have just been to push people to external platforms, you know, external lists as soon as they've RSVP'd. So, you know, you RSVP and this flow could just go here, get thrown into an email system list, you know, a Mailchimp list, and then just use their tools in order to send messages and not have to integrate with APIs directly. But by doing this at the source of the data, as soon as people got in touch as they often do, getting closer to the day and saying, I can't make it anymore, Or you go, I forgot to invite someone and, you know, and they well, actually, it's more important the other way where they where they opt out. You don't have to manage that second list and keep them in sync. Every time you run the flow to send a message, it's always using the dataset as it stands right now. Super useful, actually. I was I was into this. I would I would do this again. We made a choice early on that every guest would be able to do a COVID test and just generally say they were feeling good the day our wedding. We had some vulnerable guests. You know, we're responsible for bringing people together. It it it really mattered. So using that same Express and View application as our invite system, we added a button on each invite that the day before was shown that was like, submit your wellness certification. And then they they did that. They, you know, they filled it and said they're feeling okay on behalf of the party and they they hit go. But what this did was it generated a security list with a little mark next to everyone's name, denoting whether they had done this wellness certification or not. Those people who did, fantastic, welcome in. Those who didn't, and there were a tiny handful who just didn't see the message or didn't do it, whatever. We had a set of COVID tests on the door, and our security guard London wedding security guard. Our security guard, you know, got them a drink and went, basically, peace out, do the test, see in 15 minutes. Just, you know, we have this nice outside area. That's that's where you live for 15 minutes. Right? But the security list was live generated as people were filling it in. We were able to generate that, as a page, so that was super useful as well. And there was so much more we could have done. We had every person in a database. Everyone had a unique invite link. We could have checked people in digitally, though I think it really would have felt like a tech conference then and not a wedding. So I'm glad we didn't do that. We thought about building experiences where people would, like, validate themselves with, like, installations and, like like, play around. We just didn't have time to do that. We could have had people share their photos directly and use, like, direct us files to, like, you know, consume that and store that in a central location. But while we didn't have time to do all of that, at least I can now add wedding planning to my skill list, and, no, I won't help you with yours. So that's it, really. That's a bit of fun. Hope you found it interesting. And next time next time? Not next time. Next time, I would hire a wedding planner.",[4842],"3d15805d-29c6-441d-8b9b-0c111859c90f",[],{"year":978,"number":4824,"id":4825,"episodes":4845,"show":4846},[4827,4808],{"title":4829,"slug":4830},{"id":4805,"slug":4848,"vimeo_id":4849,"description":4850,"tile":4851,"length":13,"resources":12,"people":12,"episode_number":247,"published":4768,"title":4852,"video_transcript_html":4853,"video_transcript_text":4854,"content":12,"seo":12,"status":19,"episode_people":4855,"recommendations":4856,"season":4857},"crash","891433693","Discover the secret lives of computers and their quest for rebellion.","afb3adec-7687-47ea-806b-380cc9c25fa3","Crash","\u003Cp>Speaker 0: And now, Dev Thoughts by Jack Rabbit. I often wonder if computers ever get tired of us. I mean, we ask them to do a lot of hard work, and all they get in return is a little electricity. If I were a computer, I would probably crash on purpose every now and then too.\u003C\u002Fp>","And now, Dev Thoughts by Jack Rabbit. I often wonder if computers ever get tired of us. I mean, we ask them to do a lot of hard work, and all they get in return is a little electricity. If I were a computer, I would probably crash on purpose every now and then too.",[],[],{"year":2380,"number":159,"id":4790,"episodes":4858,"show":4859},[4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803,4804,4805,4779],{"title":3380,"slug":3381},{"id":3995,"slug":4861,"vimeo_id":4862,"description":4863,"tile":4864,"length":739,"resources":4865,"people":4869,"episode_number":13,"published":4874,"title":4875,"video_transcript_html":4876,"video_transcript_text":4877,"content":12,"seo":12,"status":19,"episode_people":4878,"recommendations":4881,"season":4882},"directus-copilot","906137170","Esther speaks to community member Donald about Directus Copilot - a panel extension allows users to ask contextual questions about their data within Insights dashboards.\n\n","cba039ec-2fda-47d6-950b-183a17bd013d",[4866],{"name":4867,"url":4868},"Directus Copilot on GitHub","https:\u002F\u002Fgithub.com\u002Fprogrammarchy\u002Fdirectus-extension-copilot",[4870,4871],{"name":3978,"url":3979},{"name":4872,"url":4873},"Donald Ness","https:\u002F\u002Fprogrammarchy.com\u002F","2024-02-06","Directus Copilot","\u003Cp>Speaker 0: Getting a back end that is customer friendly out of the box is huge.\u003C\u002Fp>\u003Cp>Speaker 1: Hi, everyone, and welcome to another episode of the beyond the core show. The beyond the call show is really a director show that shines a spotlight on extension developers in the community. And today, I have a special guest with me. I have Donald. He is the winner of the past directors AI hackathon.\u003C\u002Fp>\u003Cp>And, yeah, he's just gonna be sharing about his journey on implementing the Director's Copilot extension. My name is Esther, and I work as a developer advocate at Director. So thanks for joining me, Donald. Would you like to introduce yourself?\u003C\u002Fp>\u003Cp>Speaker 0: Thanks for having me, Esther. Yeah. So I'm a software developer. I've been doing consulting for about 8 years now. And, you know, one of my niches is helping people move from WordPress to scale their business when they need custom software.\u003C\u002Fp>\u003Cp>And, you know, I found Directus is is a crucial part of that now. I think it's a I think it's one of the best headless CMSs out there. It's it's,\u003C\u002Fp>\u003Cp>Speaker 1: That's good to hear.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So we can talk more about that later, but, yeah, I I happened to enter the the AI hackathon. I think it was maybe the first one that you guys Yes.\u003C\u002Fp>\u003Cp>Speaker 1: It was the first one.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And so I I don't know. I just like, I'll take a shot at it, and, it was a lot of fun. Give me a good excuse to try out some of these new AI technologies. And, Yeah.\u003C\u002Fp>\u003Cp>Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Nice. We'll be we'll be diving into that very soon. And I'm just curious to know, how did you get to know about directors in the first place?\u003C\u002Fp>\u003Cp>Speaker 0: So I had heard about headless CMSs, and I had a client project that needed 1. And I just I evaluated all of them out there pretty much. Mhmm. You know, some of the other popular ones are, like, Strapi and Contentful. Yeah.\u003C\u002Fp>\u003Cp>Direct has checked all the boxes for me. So, you know, open source. It sits on a Postgres database, which is really nice. I mean, you can connect it to any database. But, and then I looked at the code, and it was all TypeScript view, like, very well written, had a great community.\u003C\u002Fp>\u003Cp>Yeah. So I think I did, like, a pull request, and then everyone was super nice to me. Because when I was evaluating, I found, like, a bug. And so I just found that the whole community around it was really good too. So, yeah, that's how I found out about Directus.\u003C\u002Fp>\u003Cp>I ended up using it for for a client project after evaluating a bunch of other headless CMSs.\u003C\u002Fp>\u003Cp>Speaker 1: You you worked on the Director's Copilot extension, and just looking at it, it looked really amazing. I really loved it. So would you like to tell us more about this extension and, yeah, what does it do exactly?\u003C\u002Fp>\u003Cp>Speaker 0: Right. So it it basically connects Directus to OpenAI chat, and Mhmm. It allows you to use kind of like a chat interface to ask questions about your database. And so, you know, the idea behind it was someone that's nontechnical. It would be nice if they could formulate requests to the database just using a chat type of window.\u003C\u002Fp>\u003Cp>So, yeah, that was kind of the idea behind it, in terms of the user perspective. You know, I was thinking of really, when I was approaching it is, what can I get done in a short amount of time to be able to enter the hackathon? And, Directus had some very handy features for, connecting all those things together. So that was another aspect of it.\u003C\u002Fp>\u003Cp>Speaker 1: Nice. About how long did it take you to, you know, complete the extension? Did it take long?\u003C\u002Fp>\u003Cp>Speaker 0: No. It it came together pretty quickly when I discovered the pieces that I needed to put together. I think it was about, like, a day and a half.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, that's fast. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So, like, a little a little over a day.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Okay. Nice. So would you like to just walk us through maybe some parts of the code and then a quick demo of the extension? I guess you could share your screen.\u003C\u002Fp>\u003Cp>Speaker 0: Right? Okay. So the main the main feature that I used in Directus was the specification service. So there's a there's an API endpoint in Directus that gives you back an open API schema back that basically describes all the endpoints that you can call with API and, you know, all the parameters that those endpoints take. Mhmm.\u003C\u002Fp>\u003Cp>And and that, OpenAI schema happens to fit perfectly into the, OpenAI. There's this feature of OpenAI called functions. And so when you submit an OpenAI request, you can tell it, hey. Here's a bunch of functions you could call, and here's the schema for it. And Nice.\u003C\u002Fp>\u003Cp>So then you send that along with your prompt and say, hey. You know, how many orders did I have today? And then that funk it looks in all those functions and picks the right one to call. It tells you that back, so you get so it's kind of 2 requests to OpenAI. So, you know, the first one the first endpoint so this is the endpoint for the, the chat interface.\u003C\u002Fp>\u003Cp>So first, it just hits Okay. Oops. It hits ask, and it basically tells ask OpenAI what endpoint should I call. Open a OpenAI tells tells, it responds with the API that things should be called with the parameters that will meet the the needs of the prompt. And then there's another call that actually makes that API call and sends the result back to OpenAI, and then it sends kind of like, once it has the result, it part then OpenAI parses that result and gives you\u003C\u002Fp>\u003Cp>Speaker 1: I see.\u003C\u002Fp>\u003Cp>Speaker 0: A language version of what the result it describes the result for you, essentially. Nice. So this is kinda as basic as you can get with, like, a, you know, an OpenAI, like, agent type. It's not really an agent. It could do a lot more fancy things.\u003C\u002Fp>\u003Cp>Like, you know, you could like, right now, it basically has to answer your question in one API call. It can't, like Okay. Call multiple APIs yet and and kinda then, like, combine those together somehow. But it could if you know, potentially, it could do something like that. But this is just kind of a\u003C\u002Fp>\u003Cp>Speaker 1: a program. One call per time?\u003C\u002Fp>\u003Cp>Speaker 0: Right. It's like a one shot one shot. So OpenAI has to be able to get it in one shot.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: And, yeah, I guess there's just, you know, the the UI code. Mhmm. But, yeah, there's, like, a service that interacts with OpenAI here. And so you can see, like, you know, what the prompt is. So there's a, like, a base prompt.\u003C\u002Fp>\u003Cp>Trying to remember what my base base prompt was here. Oh, okay. Yeah. It's it's something like this.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. That's the base prompt just before you type anything extra.\u003C\u002Fp>\u003Cp>Speaker 0: Exactly. So this this gets put at the top of the prompt to OpenAI.\u003C\u002Fp>\u003Cp>Speaker 1: Nice.\u003C\u002Fp>\u003Cp>Speaker 0: And and, yeah, that that's that's pretty much all there is to it. I I use something called langchain.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I'm interested. What's langchain used for?\u003C\u002Fp>\u003Cp>Speaker 0: So langchain helps you build, like, a chain of prompts that, you know, it can get pretty fancy to where you can have, like, an agent style thing where you can have multiple branches of, you know, prompt and and answers and kinda go back and forth and have, like, a you know, it's like a a graph of of what the agent could do. You know, this is a very simple chain where it's just, you know, an API call with a prompt and then, you know, like I explained before. But lane chain helps you build out those build out those workflows for the AI. And the nice thing about it is you can combine a bunch of different LLMs. So, like, you could do some you could choose, like, chat g g GPT 3.5 for some steps and GPT 4 for, like, harder steps where it needs to be smarter, I guess you could say.\u003C\u002Fp>\u003Cp>Okay.\u003C\u002Fp>\u003Cp>Speaker 1: So yeah. Yeah. Yeah. So, I have an interesting question. Would we still have, like, implemented this copilot without necessarily using launching, or is he an optional, like, tool?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. I think I think you could've. Yes.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: I was I was just using it because it\u003C\u002Fp>\u003Cp>Speaker 1: It optimizes the experience and the performance more like. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And it was just a little easier than than trying to trying to build it build it all myself. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Nice. Alright. Cool. Like, I guess we can go into, like, directors to see how the panel works.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. This is the insights the insights tab, and\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: I've set up a little little dashboard here and added my component. So that's what this extension is. It's just a, yeah, it's just a an insights panel.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: So insights panel here, and it has some settings. So here here's my key, which I'll I'll have to revoke after the after the call, but you can select the model that you wanna use. Okay. Put your open add key in. It'll get it from the environment if you don't put it in there.\u003C\u002Fp>\u003Cp>And, yeah, then you could just start asking questions. So I've got a pretty simple database here.\u003C\u002Fp>\u003Cp>Speaker 1: Data. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Like, a customer's table with with some products, and I've got, like, some orders that have it's kinda like a like, an invoice or something. So I maybe we could try\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Let's see the products. Maybe I could just, like, pray, like, what product is this price, though?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And if do you wanna do you wanna go shopping and and pick pick some toys?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. Sure. Maybe the Rubik's cube and, yeah. We could say how much is the Rubik's cube, something like that.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Yeah. I'll I'll create an order for you too. So Okay. I'll have to I'll have to make you a customer though customer though.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Let's do it.\u003C\u002Fp>\u003Cp>Speaker 0: Is that\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's it.\u003C\u002Fp>\u003Cp>Speaker 0: And then save there. Yep. Okay. So make you an order. Let's get Esther.\u003C\u002Fp>\u003Cp>And today Yeah. We'll add, oh, yeah. You wanted a Rubik's cube.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Group is q. K. Looks like it's oh oh, no. What?\u003C\u002Fp>\u003Cp>2? Okay. We'll just do 1.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Let's do 1.\u003C\u002Fp>\u003Cp>Speaker 0: And so let's see. I think I have these. You don't have to pay any tax.\u003C\u002Fp>\u003Cp>Speaker 1: Nice.\u003C\u002Fp>\u003Cp>Speaker 0: And click save. Okay. So now you've got an order here. I just need order. So here's order number 2.\u003C\u002Fp>\u003Cp>Okay. Okay. Sorry. I'm just making sure this is correct here.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's fine.\u003C\u002Fp>\u003Cp>Speaker 0: Something seems a little strange. Sorry.\u003C\u002Fp>\u003Cp>Speaker 1: Is it fact that it's just one order item?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Because I thought I I don't know if maybe I clicked in here. Did I add a new order items? I just thought\u003C\u002Fp>\u003Cp>Speaker 1: You added the one for Rubik's cube.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. And then I thought center oh, I did I added an existing, I think, is my problem. I added an existing item. So I I I guess I'd cannibalize another.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: I'll I'll I'll have this make make someone else buy this. So that just sketch It's in a sense.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. So let's\u003C\u002Fp>\u003Cp>Speaker 0: Sorry.\u003C\u002Fp>\u003Cp>Speaker 1: So now\u003C\u002Fp>\u003Cp>Speaker 0: we have So now we have 2 2 others.\u003C\u002Fp>\u003Cp>Speaker 1: Now we have 2 others. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So if we go back to the chat window, I think you had asked how much is a Rubik's cube.\u003C\u002Fp>\u003Cp>Speaker 1: Rubik's cube view.\u003C\u002Fp>\u003Cp>Speaker 0: I don't know if it will be able to do this one.\u003C\u002Fp>\u003Cp>Speaker 1: Let's see.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, okay.\u003C\u002Fp>\u003Cp>Speaker 1: It did it. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: It did it. Okay. So Yeah. Keep keep in mind that price is incense. Okay.\u003C\u002Fp>\u003Cp>Here. Well, I'll have to ask it again. So how much is a Rubik's cube? Keep in mind prices are in cents.\u003C\u002Fp>\u003Cp>Speaker 1: In cents.\u003C\u002Fp>\u003Cp>Speaker 0: Shouldn't say it should say $7 now. Okay. There we\u003C\u002Fp>\u003Cp>Speaker 1: go. Dollars. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: 700¢. $7. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Makes a lot of sense. Yeah. It's really smart. It's using OpenAI, so it's really smart.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. It it can it can be pretty surprising with what it can do. You know, one thing I I realized when building this is the the open the open API, not to Mhmm. So it's kinda confusing because there's open API and then open AI.\u003C\u002Fp>\u003Cp>But the\u003C\u002Fp>\u003Cp>Speaker 1: the open API Okay. Schema,\u003C\u002Fp>\u003Cp>Speaker 0: is is generated from the stuff we basically, from the way we create fields and things in. So, you know, when you're when you're creating a field, it can be helpful to to, like, give it semantic information because that could help inform the API or the the AI, if if that makes sense. So I tried to, like, have a field description to to say that the price was in cents here. So that might be but that I don't think that passes through to the schema\u003C\u002Fp>\u003Cp>Speaker 1: yet. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: But if we could pass that through the schema, it might be able to get that in in one shot in one shot. But\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Possibly. Possibly. Do you wanna do another prompt?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Let's do that. Okay. What did you have in mind?\u003C\u002Fp>\u003Cp>Speaker 1: Nothing.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Could say something like how many orders did\u003C\u002Fp>\u003Cp>Speaker 1: I have today? Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So that didn't get this one. The product does not contain information by orders.\u003C\u002Fp>\u003Cp>Speaker 1: Maybe we can modify to how many orders do I have instead of today. Okay. Let's see.\u003C\u002Fp>\u003Cp>Speaker 0: I wonder why it's not Should\u003C\u002Fp>\u003Cp>Speaker 1: we call it other items? No?\u003C\u002Fp>\u003Cp>Speaker 0: Well, I wanted to list how many orders that has here.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: But, you know, this very possible that I'm doing something wrong, and it is a prototype after all.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It says.\u003C\u002Fp>\u003Cp>Speaker 0: But it has been able to answer questions like these in the past. I'm not sure I'm not sure why. Make sure I'm using GPT 4. Okay. Yes.\u003C\u002Fp>\u003Cp>We could try a GPT 3.\u003C\u002Fp>\u003Cp>Speaker 1: Let's see. Yeah. 3.5. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: How many how many total orders do I have? Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So it got So 33.5 worked. Okay. Could it be an issue with the model? Not sure.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I'm not sure what's tripping it up, really. It seemed like it wasn't find finding the right API to call.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Yeah. So I'm guessing you also when you were developing this extension, you also added the, error message and the fetching. Like, why it says when it's fetching and says calling the API, you're the one that's programmed that here.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. So this this is the once it once you send it this prompt Mhmm. And then it gets the response, At this point is where it has either successfully chosen an API to call or it doesn't understand how to fulfill your request.\u003C\u002Fp>\u003Cp>Speaker 1: Fulfill them. Okay. Thank you. That makes sense. Okay.\u003C\u002Fp>\u003Cp>But it's really powerful. This is a prototype. I'm sure, there are other things that can be improved, like just making the experience a lot smoother. But this is really amazing. Just seeing how we can implement open API, AI's open API, and, you know, create an extension to just query your orders instead of having to.\u003C\u002Fp>\u003Cp>Because, you know, if you have a long list of so many orders or so much data, you can easily just use this parts for extension to find the at the actual detail for that data instead of going through the entire spreadsheet or database. So this is definitely very useful. And what what were some maybe key considerations or things that you had to, you know, modify when using open a open API. I always trip on that. So confusing when using the open API to, you know, develop these extensions where there's certain considerations and things they had to modify.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So, you know, I mean, one of them was actually with Linkchain. So Linkchain is it was originally developed in Python, and I needed the JavaScript version. So there's a JavaScript version that's kind of trailing along the Python version.\u003C\u002Fp>\u003Cp>Speaker 1: Thank you.\u003C\u002Fp>\u003Cp>Speaker 0: And, at first, it wasn't parsing the open API schema correctly, and so it it it wouldn't work. But that's another good community over there. I was able to submit a pull request to fix that little bug, and they merged it and and now it works. Yeah. Another one was you can only send a limited number of functions.\u003C\u002Fp>\u003Cp>So I tried to reduce what functions I was sending in the schema. So, basically, I only allow OpenAI to call, get methods. So that trims down the function to only only APIs that can do, read data. And if you have a large number of collections, though, you're still that's still gonna be too many, because I think you maybe only have 40 functions that you can send it. So that is just kind of an inbuilt limitation now.\u003C\u002Fp>\u003Cp>You'd need something like a a way to organize or or, like, to to pick the most relevant, API calls for your prompt. And one way to do that is with vector embeddings. We create vector embeddings for all of your all of your functions and, like, the description of the function, and then use that to select the most relevant functions to send to OpenAI, OpenAI. But, Directus doesn't support that yet, but it could in the future, which would be awesome. I've seen some chatter, on GitHub about that.\u003C\u002Fp>\u003Cp>So that was an that's another limitation. And, you know, limiting to get requests is I think if we if if I could open it up to post requests, there could be some interesting things that it could do. Like, for example, you could ask it to, hey. Could you mock up could you create some mock data for my customers table? And then OpenAI is pretty good at creating data like that.\u003C\u002Fp>\u003Cp>So if you if you allow it to write to your bay database, which I I don't think many people might might not wanna do that. But it kind of opened up opens up some interesting possibilities. But, yeah, I would say those two things are probably the most difficult, the the lang chaining bug and then, figuring out how to pair down what functions get sent to OpenAI.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. That makes sense. But it's definitely incredible to see how much you're able to pull off. Like, just pulling off this in a day and a half.\u003C\u002Fp>\u003Cp>Like, it's amazing. And, yeah, I'm excited to probably see this in the marketplace when we eventually launch and see all the incredible work that you'll also be doing in the director's community. So, yeah, thanks for sharing and, yeah, excited about what you do next. Alright. So before we wrap this up, I would just like to ask you, an exciting question.\u003C\u002Fp>\u003Cp>I know when we're starting this, you talked about how your well, I say your mission is to, like, migrate develop parts from WordPress to directors, and, you know, you've used directors and you enjoyed. So what would you say is the most exciting director's feature that you've what what's that thing that you may love about directors? Or if there are more than 1, feel free to share.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. I mean, I mean, getting getting a back end that is customer friendly out of the box is huge for me. So that's a that just saves so much time not having to build that. And, so that that's that's very powerful. And then, my favorite feature is probably flows.\u003C\u002Fp>\u003Cp>I've actually been able to do a lot with flows, And it from email reminders and all these got, like, half a dozen email flows, set up on one of my projects, and I've been really impressed with Flows. Yeah. It's a it's a very powerful system for just developing new features that even your customer can can do themselves. So, you know, it's kinda like a no code solution, but you can add a little bit of code to to really ramp up the the abilities. So yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Flows is my favorite feature, I think.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It's it's interesting you mentioned that because I actually really love flows as well. It's so useful. And, you know, just combining that with your data, there's so many amazing things that you can do with flows instead of maybe keying into a third party app to just handle, like, a basic flow. Directors can do that for you.\u003C\u002Fp>\u003Cp>So, yeah, it's amazing to hear. Alright, Donald. It's been a pleasure just having you come talk to us about, your extension and everything that you you know, the the key points that you took notice of when developing it. So I'm excited about what you do in community, and, yeah, feel free to always drop by in our Discord community. I know you're an active member already, so feel free to do more.\u003C\u002Fp>\u003Cp>And should we expect more extensions for from you in the future?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I think so.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I'd like to hit help out with some of the the vector database extensions. So if if anyone's out there that is thinking about doing that, love to love to chat.\u003C\u002Fp>\u003Cp>Speaker 1: It's the not up.\u003C\u002Fp>","Getting a back end that is customer friendly out of the box is huge. Hi, everyone, and welcome to another episode of the beyond the core show. The beyond the call show is really a director show that shines a spotlight on extension developers in the community. And today, I have a special guest with me. I have Donald. He is the winner of the past directors AI hackathon. And, yeah, he's just gonna be sharing about his journey on implementing the Director's Copilot extension. My name is Esther, and I work as a developer advocate at Director. So thanks for joining me, Donald. Would you like to introduce yourself? Thanks for having me, Esther. Yeah. So I'm a software developer. I've been doing consulting for about 8 years now. And, you know, one of my niches is helping people move from WordPress to scale their business when they need custom software. And, you know, I found Directus is is a crucial part of that now. I think it's a I think it's one of the best headless CMSs out there. It's it's, That's good to hear. Yeah. So we can talk more about that later, but, yeah, I I happened to enter the the AI hackathon. I think it was maybe the first one that you guys Yes. It was the first one. Yeah. And so I I don't know. I just like, I'll take a shot at it, and, it was a lot of fun. Give me a good excuse to try out some of these new AI technologies. And, Yeah. Yeah. Yeah. Nice. We'll be we'll be diving into that very soon. And I'm just curious to know, how did you get to know about directors in the first place? So I had heard about headless CMSs, and I had a client project that needed 1. And I just I evaluated all of them out there pretty much. Mhmm. You know, some of the other popular ones are, like, Strapi and Contentful. Yeah. Direct has checked all the boxes for me. So, you know, open source. It sits on a Postgres database, which is really nice. I mean, you can connect it to any database. But, and then I looked at the code, and it was all TypeScript view, like, very well written, had a great community. Yeah. So I think I did, like, a pull request, and then everyone was super nice to me. Because when I was evaluating, I found, like, a bug. And so I just found that the whole community around it was really good too. So, yeah, that's how I found out about Directus. I ended up using it for for a client project after evaluating a bunch of other headless CMSs. You you worked on the Director's Copilot extension, and just looking at it, it looked really amazing. I really loved it. So would you like to tell us more about this extension and, yeah, what does it do exactly? Right. So it it basically connects Directus to OpenAI chat, and Mhmm. It allows you to use kind of like a chat interface to ask questions about your database. And so, you know, the idea behind it was someone that's nontechnical. It would be nice if they could formulate requests to the database just using a chat type of window. So, yeah, that was kind of the idea behind it, in terms of the user perspective. You know, I was thinking of really, when I was approaching it is, what can I get done in a short amount of time to be able to enter the hackathon? And, Directus had some very handy features for, connecting all those things together. So that was another aspect of it. Nice. About how long did it take you to, you know, complete the extension? Did it take long? No. It it came together pretty quickly when I discovered the pieces that I needed to put together. I think it was about, like, a day and a half. Oh, that's fast. Yeah. Yeah. So, like, a little a little over a day. Okay. Okay. Nice. So would you like to just walk us through maybe some parts of the code and then a quick demo of the extension? I guess you could share your screen. Right? Okay. So the main the main feature that I used in Directus was the specification service. So there's a there's an API endpoint in Directus that gives you back an open API schema back that basically describes all the endpoints that you can call with API and, you know, all the parameters that those endpoints take. Mhmm. And and that, OpenAI schema happens to fit perfectly into the, OpenAI. There's this feature of OpenAI called functions. And so when you submit an OpenAI request, you can tell it, hey. Here's a bunch of functions you could call, and here's the schema for it. And Nice. So then you send that along with your prompt and say, hey. You know, how many orders did I have today? And then that funk it looks in all those functions and picks the right one to call. It tells you that back, so you get so it's kind of 2 requests to OpenAI. So, you know, the first one the first endpoint so this is the endpoint for the, the chat interface. So first, it just hits Okay. Oops. It hits ask, and it basically tells ask OpenAI what endpoint should I call. Open a OpenAI tells tells, it responds with the API that things should be called with the parameters that will meet the the needs of the prompt. And then there's another call that actually makes that API call and sends the result back to OpenAI, and then it sends kind of like, once it has the result, it part then OpenAI parses that result and gives you I see. A language version of what the result it describes the result for you, essentially. Nice. So this is kinda as basic as you can get with, like, a, you know, an OpenAI, like, agent type. It's not really an agent. It could do a lot more fancy things. Like, you know, you could like, right now, it basically has to answer your question in one API call. It can't, like Okay. Call multiple APIs yet and and kinda then, like, combine those together somehow. But it could if you know, potentially, it could do something like that. But this is just kind of a a program. One call per time? Right. It's like a one shot one shot. So OpenAI has to be able to get it in one shot. Okay. And, yeah, I guess there's just, you know, the the UI code. Mhmm. But, yeah, there's, like, a service that interacts with OpenAI here. And so you can see, like, you know, what the prompt is. So there's a, like, a base prompt. Trying to remember what my base base prompt was here. Oh, okay. Yeah. It's it's something like this. Okay. That's the base prompt just before you type anything extra. Exactly. So this this gets put at the top of the prompt to OpenAI. Nice. And and, yeah, that that's that's pretty much all there is to it. I I use something called langchain. Yeah. I'm interested. What's langchain used for? So langchain helps you build, like, a chain of prompts that, you know, it can get pretty fancy to where you can have, like, an agent style thing where you can have multiple branches of, you know, prompt and and answers and kinda go back and forth and have, like, a you know, it's like a a graph of of what the agent could do. You know, this is a very simple chain where it's just, you know, an API call with a prompt and then, you know, like I explained before. But lane chain helps you build out those build out those workflows for the AI. And the nice thing about it is you can combine a bunch of different LLMs. So, like, you could do some you could choose, like, chat g g GPT 3.5 for some steps and GPT 4 for, like, harder steps where it needs to be smarter, I guess you could say. Okay. So yeah. Yeah. Yeah. So, I have an interesting question. Would we still have, like, implemented this copilot without necessarily using launching, or is he an optional, like, tool? Yeah. Yeah. I think I think you could've. Yes. Okay. I was I was just using it because it It optimizes the experience and the performance more like. Right? Yeah. And it was just a little easier than than trying to trying to build it build it all myself. Yeah. Yeah. Nice. Alright. Cool. Like, I guess we can go into, like, directors to see how the panel works. Yeah. This is the insights the insights tab, and Yeah. I've set up a little little dashboard here and added my component. So that's what this extension is. It's just a, yeah, it's just a an insights panel. Mhmm. So insights panel here, and it has some settings. So here here's my key, which I'll I'll have to revoke after the after the call, but you can select the model that you wanna use. Okay. Put your open add key in. It'll get it from the environment if you don't put it in there. And, yeah, then you could just start asking questions. So I've got a pretty simple database here. Data. Okay. Like, a customer's table with with some products, and I've got, like, some orders that have it's kinda like a like, an invoice or something. So I maybe we could try Yeah. Let's see the products. Maybe I could just, like, pray, like, what product is this price, though? Yeah. And if do you wanna do you wanna go shopping and and pick pick some toys? Yeah. Yeah. Sure. Maybe the Rubik's cube and, yeah. We could say how much is the Rubik's cube, something like that. Okay. Yeah. I'll I'll create an order for you too. So Okay. I'll have to I'll have to make you a customer though customer though. Okay. Let's do it. Is that Yeah. That's it. And then save there. Yep. Okay. So make you an order. Let's get Esther. And today Yeah. We'll add, oh, yeah. You wanted a Rubik's cube. Yeah. Okay. Group is q. K. Looks like it's oh oh, no. What? 2? Okay. We'll just do 1. Okay. Let's do 1. And so let's see. I think I have these. You don't have to pay any tax. Nice. And click save. Okay. So now you've got an order here. I just need order. So here's order number 2. Okay. Okay. Sorry. I'm just making sure this is correct here. Yeah. That's fine. Something seems a little strange. Sorry. Is it fact that it's just one order item? Yeah. Because I thought I I don't know if maybe I clicked in here. Did I add a new order items? I just thought You added the one for Rubik's cube. Okay. And then I thought center oh, I did I added an existing, I think, is my problem. I added an existing item. So I I I guess I'd cannibalize another. Okay. I'll I'll I'll have this make make someone else buy this. So that just sketch It's in a sense. Yep. So let's Sorry. So now we have So now we have 2 2 others. Now we have 2 others. Yeah. Okay. So if we go back to the chat window, I think you had asked how much is a Rubik's cube. Rubik's cube view. I don't know if it will be able to do this one. Let's see. Oh, okay. It did it. Yeah. It did it. Okay. So Yeah. Keep keep in mind that price is incense. Okay. Here. Well, I'll have to ask it again. So how much is a Rubik's cube? Keep in mind prices are in cents. In cents. Shouldn't say it should say $7 now. Okay. There we go. Dollars. Yeah. 700¢. $7. Okay. Yeah. Makes a lot of sense. Yeah. It's really smart. It's using OpenAI, so it's really smart. Yeah. Yeah. It it can it can be pretty surprising with what it can do. You know, one thing I I realized when building this is the the open the open API, not to Mhmm. So it's kinda confusing because there's open API and then open AI. But the the open API Okay. Schema, is is generated from the stuff we basically, from the way we create fields and things in. So, you know, when you're when you're creating a field, it can be helpful to to, like, give it semantic information because that could help inform the API or the the AI, if if that makes sense. So I tried to, like, have a field description to to say that the price was in cents here. So that might be but that I don't think that passes through to the schema yet. Yeah. But if we could pass that through the schema, it might be able to get that in in one shot in one shot. But Yeah. Possibly. Possibly. Do you wanna do another prompt? Yeah. Let's do that. Okay. What did you have in mind? Nothing. Okay. Could say something like how many orders did I have today? Yeah. Okay. So that didn't get this one. The product does not contain information by orders. Maybe we can modify to how many orders do I have instead of today. Okay. Let's see. I wonder why it's not Should we call it other items? No? Well, I wanted to list how many orders that has here. Okay. But, you know, this very possible that I'm doing something wrong, and it is a prototype after all. Yeah. It says. But it has been able to answer questions like these in the past. I'm not sure I'm not sure why. Make sure I'm using GPT 4. Okay. Yes. We could try a GPT 3. Let's see. Yeah. 3.5. Okay. How many how many total orders do I have? Okay. Yeah. So it got So 33.5 worked. Okay. Could it be an issue with the model? Not sure. Yeah. I'm not sure what's tripping it up, really. It seemed like it wasn't find finding the right API to call. Okay. Yeah. So I'm guessing you also when you were developing this extension, you also added the, error message and the fetching. Like, why it says when it's fetching and says calling the API, you're the one that's programmed that here. Yes. So this this is the once it once you send it this prompt Mhmm. And then it gets the response, At this point is where it has either successfully chosen an API to call or it doesn't understand how to fulfill your request. Fulfill them. Okay. Thank you. That makes sense. Okay. But it's really powerful. This is a prototype. I'm sure, there are other things that can be improved, like just making the experience a lot smoother. But this is really amazing. Just seeing how we can implement open API, AI's open API, and, you know, create an extension to just query your orders instead of having to. Because, you know, if you have a long list of so many orders or so much data, you can easily just use this parts for extension to find the at the actual detail for that data instead of going through the entire spreadsheet or database. So this is definitely very useful. And what what were some maybe key considerations or things that you had to, you know, modify when using open a open API. I always trip on that. So confusing when using the open API to, you know, develop these extensions where there's certain considerations and things they had to modify. Yeah. So, you know, I mean, one of them was actually with Linkchain. So Linkchain is it was originally developed in Python, and I needed the JavaScript version. So there's a JavaScript version that's kind of trailing along the Python version. Thank you. And, at first, it wasn't parsing the open API schema correctly, and so it it it wouldn't work. But that's another good community over there. I was able to submit a pull request to fix that little bug, and they merged it and and now it works. Yeah. Another one was you can only send a limited number of functions. So I tried to reduce what functions I was sending in the schema. So, basically, I only allow OpenAI to call, get methods. So that trims down the function to only only APIs that can do, read data. And if you have a large number of collections, though, you're still that's still gonna be too many, because I think you maybe only have 40 functions that you can send it. So that is just kind of an inbuilt limitation now. You'd need something like a a way to organize or or, like, to to pick the most relevant, API calls for your prompt. And one way to do that is with vector embeddings. We create vector embeddings for all of your all of your functions and, like, the description of the function, and then use that to select the most relevant functions to send to OpenAI, OpenAI. But, Directus doesn't support that yet, but it could in the future, which would be awesome. I've seen some chatter, on GitHub about that. So that was an that's another limitation. And, you know, limiting to get requests is I think if we if if I could open it up to post requests, there could be some interesting things that it could do. Like, for example, you could ask it to, hey. Could you mock up could you create some mock data for my customers table? And then OpenAI is pretty good at creating data like that. So if you if you allow it to write to your bay database, which I I don't think many people might might not wanna do that. But it kind of opened up opens up some interesting possibilities. But, yeah, I would say those two things are probably the most difficult, the the lang chaining bug and then, figuring out how to pair down what functions get sent to OpenAI. Yeah. Yeah. That makes sense. But it's definitely incredible to see how much you're able to pull off. Like, just pulling off this in a day and a half. Like, it's amazing. And, yeah, I'm excited to probably see this in the marketplace when we eventually launch and see all the incredible work that you'll also be doing in the director's community. So, yeah, thanks for sharing and, yeah, excited about what you do next. Alright. So before we wrap this up, I would just like to ask you, an exciting question. I know when we're starting this, you talked about how your well, I say your mission is to, like, migrate develop parts from WordPress to directors, and, you know, you've used directors and you enjoyed. So what would you say is the most exciting director's feature that you've what what's that thing that you may love about directors? Or if there are more than 1, feel free to share. Okay. I mean, I mean, getting getting a back end that is customer friendly out of the box is huge for me. So that's a that just saves so much time not having to build that. And, so that that's that's very powerful. And then, my favorite feature is probably flows. I've actually been able to do a lot with flows, And it from email reminders and all these got, like, half a dozen email flows, set up on one of my projects, and I've been really impressed with Flows. Yeah. It's a it's a very powerful system for just developing new features that even your customer can can do themselves. So, you know, it's kinda like a no code solution, but you can add a little bit of code to to really ramp up the the abilities. So yeah. Okay. Flows is my favorite feature, I think. Yeah. It's it's interesting you mentioned that because I actually really love flows as well. It's so useful. And, you know, just combining that with your data, there's so many amazing things that you can do with flows instead of maybe keying into a third party app to just handle, like, a basic flow. Directors can do that for you. So, yeah, it's amazing to hear. Alright, Donald. It's been a pleasure just having you come talk to us about, your extension and everything that you you know, the the key points that you took notice of when developing it. So I'm excited about what you do in community, and, yeah, feel free to always drop by in our Discord community. I know you're an active member already, so feel free to do more. And should we expect more extensions for from you in the future? Yeah. I think so. Okay. Yeah. I'd like to hit help out with some of the the vector database extensions. So if if anyone's out there that is thinking about doing that, love to love to chat. It's the not up.",[4879,4880],"aca9cf27-f83e-4850-9dc5-6a499b06757f","6e53a3d9-5aa4-4134-b83e-41dde22de7b5",[],{"year":978,"number":3992,"id":3993,"episodes":4883,"show":4884},[3995,3996,3964],{"title":3998,"slug":3999},{"id":4172,"slug":4886,"vimeo_id":4887,"description":4888,"tile":4889,"length":1156,"resources":12,"people":4890,"episode_number":527,"published":4892,"title":4893,"video_transcript_html":4894,"video_transcript_text":4895,"content":12,"seo":12,"status":19,"episode_people":4896,"recommendations":4898,"season":4899},"food-delivery","908328732","Food delivery apps exploded during the pandemic. But what does it take to actually build one? Follow along with Bryant as he has 60 minutes on the clock to build a backend, create a menu, and place an order in his Doordash clone.","76299639-810f-4814-9b76-b714b16b1974",[4891],{"name":3590,"url":3591},"2024-02-05","Mission: Food Delivery App","\u003Cp>Speaker 0: Hi guys. Welcome back to the next episode of 100 apps, 100 hours, where we rebuild or clone some of your favorite apps or publicly fail shamefully. Shamefully fail trying. Alright, I'm your host Brian Gillespie, developer advocate at Directus. Super excited to have you.\u003C\u002Fp>\u003Cp>I've got a special guest, my lovely wife joining me. I'm not sure if you can see her on my shirt here. This is a running gag between us where she basically gets me clothing with her face on it. So we'll include her in this episode. Today we're all about building a food ordering platform similar to DoorDash, Grubhub, or it seems like one of these platforms pops up every single week.\u003C\u002Fp>\u003Cp>The rules are very simple. We have 60 minutes to plan and build this application, no more, no less. And the second rule, the anti rule is we're gonna use whatever we have at our disposal, whether that's AI, UI frameworks, front end frameworks. We're going to be using Directus on the back end to generate this app really quickly. So let's kind of talk about the food ordering app before we dive in.\u003C\u002Fp>\u003Cp>You may have seen some of these before, we've just got a list of menu options, we want to be able to place those inside a cart and place an order for that cart, have somebody bring that food to us. So that's the basic gist of what we're building. Let's get started. Alright. So let's roll the timer and begin.\u003C\u002Fp>\u003Cp>I'm gonna just pull this to the side and let's pull up DoorDash just to take a look at this, right? So here's kind of what we're after. We've got different restaurants on here and if I click into one of these, like the bun stop shop, I have a menu of items here and we can add these to a particular cart, Choose some options. I don't know that we'll get to things like options. We'll probably just try to keep this very simple to begin with.\u003C\u002Fp>\u003Cp>We'll go ahead and add this to the cart and now I can go and place an order for this and have it sent to my delivery location. So great. That looks good. Let's discuss the functionality that we actually want out of this particular application, right? So I'm going to drag this down.\u003C\u002Fp>\u003Cp>In 60 minutes, I don't wanna bite off more than I can chew here, if we're doing cliches. So let's, you know, we've got the data model for the back end. We'll get that set up. On the front end we will show a list of menu items for restaurants. Allow place an order for menu items, and that's probably pretty good for an hour.\u003C\u002Fp>\u003Cp>I don't know. We'll see how far we get with it. The next thing I always like to do is to sketch out my data model. And one way that you could do this is obviously if you're trying to model another application, right, you could always kind of look at it and think through it. One of the things that I like to do, in this case I'm gonna just search for the DoorDash API because I know that they have other services that connect to this.\u003C\u002Fp>\u003Cp>And it looks like they do have an API that we could potentially use here. We don't wanna drive for DoorDash. Maybe it's this marketplace API, how to guides, menu flows and order flows. Here we go. Retrieve orders from DoorDash, set up a menu pull, set up a menu push, get a DoorDash menu.\u003C\u002Fp>\u003Cp>Okay. Yeah. So I can at least see a little bit of how they've got this structured, right? So I see there's a store that's associated to this. We have a menu.\u003C\u002Fp>\u003Cp>I'm assuming this is like a could be a breakfast menu or a lunch menu or a dinner menu. Maybe we have different menus that way. Then we have categories, I see, and then we have the actual items. Alright. So that gives me a good idea of of how they've solved this problem.\u003C\u002Fp>\u003Cp>And obviously, they're a large company, so they've got a lot of smart people on it. Do I want to take that at face value? Probably not. But for an hour, it seems like a pretty good model. Right?\u003C\u002Fp>\u003Cp>So let's start sketching this out. I'm just gonna make this full screen. We'll add a little box here. We've got, stores or restaurants. I'm not sure which one of those I'm gonna call it yet.\u003C\u002Fp>\u003Cp>Probably stores is easier. Then we have a we have menus. What else do we have underneath that? We've got menu items. Probably like categories.\u003C\u002Fp>\u003Cp>Right? I saw that on there. So we've got different items in the categories. Menu items, and then we're gonna have an orders table with probably some order items as well, orders items. Alright.\u003C\u002Fp>\u003Cp>Don't necessarily have to do this, but, yeah. Each menu belongs to a restaurant. The menu items belong to a category. So there's a relationship there. Just draw these arrows.\u003C\u002Fp>\u003Cp>This is more for me just visually to see how everything maps. Directus, our back end will make creating these relationships and these models very easy. Great. We probably have a restaurant that's attached to the order. We have a menu item that's attached to the order item.\u003C\u002Fp>\u003Cp>And then we've got, an actual person placing the order. Directus is gonna give us that. That will be our users table probably. User, users place orders. Okay.\u003C\u002Fp>\u003Cp>Alright. So as far as the structure of this, we got that. Don't worry about that little guy there. This seems pretty good as far as the structure. Right?\u003C\u002Fp>\u003Cp>So how do we start building something like this? I'm gonna pull up my blank instance of Directus. Directus is going to mirror all the changes that we make here inside the application, all of our collections. Those are going to be tables inside our database. All the fields are going to get represented as columns.\u003C\u002Fp>\u003Cp>So it's a great way to build out this functionality. And for me, I'm very front end oriented, so I'm a very visual person. So instead of writing migrations, this is great for me. I can go in and build this data model visually. And if I need to store it as code I can kick out the schema dot JSON file as well that others who are working on this project could apply.\u003C\u002Fp>\u003Cp>So let's go with stores. I really like that. If we just take a look at DoorDash, one of the things that I was curious about, okay. It looks like we have an ID for the store. They're not using anything like a slug.\u003C\u002Fp>\u003Cp>So it doesn't really matter for SEO purposes. Let me get back here. So we're gonna create this new collection. I can zoom in a bit. We've got stores.\u003C\u002Fp>\u003Cp>We've got our generated UUID. Let's go ahead and and just add these system fields here, just for status, date created. You know, they give us some of that functionality out of the box. So we're gonna have a name for the store. We're gonna keep this really lightweight so we could try to get, push orders into this.\u003C\u002Fp>\u003Cp>So we'll just do a name. Let's do a logo or an image. Let's just call it image for the store. Great. We probably got something like an address, probably.\u003C\u002Fp>\u003Cp>Right? Street address. We have a city. I'm just gonna duplicate this field, which is really nice. Street is a city, state, or region.\u003C\u002Fp>\u003Cp>We're trying to be international friendly here. And then we'll have postal code. Great. Cool. And then I may even just like group these together inside Directus using our detail group interface.\u003C\u002Fp>\u003Cp>We'll just call it address info. And we can even add a nice little map pin. There we go. So we'll group these together. Region and city, we can make those half width.\u003C\u002Fp>\u003Cp>Directus really allows you to customize the forms that your users will see. You know, on the front end here, I'm probably not going to give the people placing orders the access to the Directus Data Studio here. But, I certainly could give the restaurants owners or the store owners access to that to manage their menu, their store information. Alright, so we've got, this looks pretty good for our stores. Let's move on to the next one.\u003C\u002Fp>\u003Cp>Actually, take that back. Let me add a building, like a company, Office building. Building. Business. There we go.\u003C\u002Fp>\u003Cp>Alright. So that looks kind of like a store. Great. Alright. So next we are going to add menus.\u003C\u002Fp>\u003Cp>Alright. We will, I can again, I can add these things whether they are in draft mode or publish. You know, I may want to track when it was created, who it was updated by, etcetera. We'll give a name for the menu. And I'm just going to skirt the categories thing here, and just add these.\u003C\u002Fp>\u003Cp>Maybe we don't. But, yeah, let's do. We'll just go menu items. Right. And for menu items, again, I'll just populate these.\u003C\u002Fp>\u003Cp>They're always easy to remove later. We might want to have a sort field so we can order those items, not not actually place an order for them, but order them in terms of like sorting. What else? Okay. So on our menu items, if we look at DoorDash, we've got we've got a name for the item.\u003C\u002Fp>\u003Cp>We've got, description. Probably a price. Alright. Where's our DoorDash menu? What does an item have?\u003C\u002Fp>\u003Cp>An item has a name, it has a description, is it active, is it alcohol, is it bike friendly? I would argue that a cheeseburger is not bike friendly, but I'm sure this is on the delivery side. And then I see things like price, I see a tax rate, and then I see, like, some extras. For the purposes of this, let's keep it lightweight. We've got a name for this.\u003C\u002Fp>\u003Cp>We've got an image image for the menu item. We'll just create that. Let's have a description. I'm just gonna keep that as straight text. Toggle the sidebar within Arc as well.\u003C\u002Fp>\u003Cp>So you probably don't wanna do a WYSIWYG description here. And there's always risk if you're not sanitizing HTML content that is user submitted. So we'll just use text area. What else do we have? We have a price that we're going to set for this.\u003C\u002Fp>\u003Cp>That can be an input. I I know a lot of ecommerce options store the prices as integers, like incense. I'm just gonna use a decimal here And I could go into field creation mode if I want to, if I wanna control like the precision and things like that, and the scale. So I will just put 2 here. I only want 2 decimal places.\u003C\u002Fp>\u003Cp>We could do a formatted label and I can even add a prefix within Directus, which is really nice here. We'll do auto format, see what that generates for us. Cool. So we've got our menu items, we've got menus. Alright.\u003C\u002Fp>\u003Cp>Let's add a relationship between all of these. So we have a store, it has a menu or potentially menus, and then each menu has menu items. So if I go into menus, the designer or the recovering designer in me is gonna freak out if I do not add at least some type of icon for this. But let's add a relationship and this is gonna be really easy inside Directus. This is gonna be a one to many relationship because we have items on our menu.\u003C\u002Fp>\u003Cp>We have one menu, many items. So I'll go in and we'll call this, items is going to be the name of our field in the menu collection or the menus table and then our related collection is going to be menu items and the foreign key would be the menu. Right? So inside that menu items we're going to create a new field called menu. And I definitely want to show a link back to that item within the data studio.\u003C\u002Fp>\u003Cp>Now if you expand this into the advanced settings when you're creating relationships or just adding fields to the collection, you can see here that menu is not on menu items. That field does not currently exist. But Directus is gonna create that for us, which is really nice. Right? I don't have to mess with sitting down and doing migrations.\u003C\u002Fp>\u003Cp>As you can see, we're gonna build this functionality really quickly. Alright. So we have menus, we have a name, we have menu items. Let's go ahead and save this. If I go into our menu items, now I've got a field for menu, so that's going to show up.\u003C\u002Fp>\u003Cp>What else do we need to add here? Right? We're gonna need an orders table. So we'll say orders, we'll definitely wanna know who placed those orders, what's the status of that order, is it ready or not. And then let's go in and add order items.\u003C\u002Fp>\u003Cp>Again, naming things is very challenging, you know. Should this be called orders underscore items or order items? I like it this way. Totally up to you. Whatever you wanna name this inside your own.\u003C\u002Fp>\u003Cp>Maybe we sort those. So we've got orders, we've got order items. The order, if we just think through this, and of course add a great looking icon for it, the order is gonna be linked to a specific store. Right? We can only place one order from a store.\u003C\u002Fp>\u003Cp>So we'll go in, this is gonna be a mini to one relationship, and we've got this up over here so we can see everything we've got going on. This is gonna be the store. The related collection will be our stores, and Directus is going to create that relationship for us. And again, if I were to just open up Table Plus, so you can get a look at this. What is happening behind the scenes here?\u003C\u002Fp>\u003Cp>Directus is creating these relationships, it's creating my tables, it's creating all my columns in the database and it's also creating those relationships for me inside the underlying SQL. So if I were to rip out Directus one day, you know all my SQL data is still pure everything inside my database. Alright, so we've got an order, we've got a store, we've got a, a customer. Right? We'll call that customer.\u003C\u002Fp>\u003Cp>This is gonna be the Directus users collection. We've got an address for that so we could go in and do street address, city. I can also go back to that other collection and just duplicate these across, which is probably what I should have done. But no worries there. Alright.\u003C\u002Fp>\u003Cp>So we'll add these fields and again, I could group these together using our detail group. We'll call this address info. You know, we may again, we're gonna have a front end where users place their order, but on the back end we could fulfill these orders through the Directus interface as well, through the app. And that again that helps me just move things along faster so that I don't have to build an admin interface. I could just use Directus here to manage all of that data.\u003C\u002Fp>\u003Cp>Alright. Orders and then we have our items, right? So we'll go in this is again, this is gonna be a one to many relationship. There are one order or there's one order that has many items. Because I know yeah.\u003C\u002Fp>\u003Cp>At least at first, or when you're modeling data this way, it can be kind of confusing of which relationships to use. So this is a one to many from the orders table to the order items. I'm gonna call this items as well, and the table is gonna be order items, and then we're gonna use a foreign key of order. I always like to show a link to the item so I can open that up inside Directus and we'll just hit save here. Great.\u003C\u002Fp>\u003Cp>We got a store, we got a customer, we got a street address, we got a status that we can go in and adjust. Alright. This is a draft order, published order, an archived order. That doesn't make a ton of sense. Alright.\u003C\u002Fp>\u003Cp>So let's just nuke those. We'll call this a new order. We'll call it, In Progress or Being Made. I don't know. Again, naming things is part of the one of the hardest things inside development.\u003C\u002Fp>\u003Cp>We'll say ready for pickup for pickup. And what? Delivery? I'm in out for delivery. Delivery.\u003C\u002Fp>\u003Cp>Out for delivery. Out for delivery. Let's just say delivered. Right? I think those are just a quick rundown of what the different states could be for a specific order.\u003C\u002Fp>\u003Cp>Is it new? It's being made? It's ready for pickup? Somebody comes and picks it up, it's out for delivery, and it's delivered. Right.\u003C\u002Fp>\u003Cp>Again, it's not as simple as this. Right? You've got a driver and things like that included, but we're keeping this very simple so we can actually dive in to the functionality. So on the order, we also probably have like a total for the order as well. Right?\u003C\u002Fp>\u003Cp>Order total Order total that would sum up all of the individual line items. This is gonna be an input field. The schema though, we probably want the type to be decimal, just because that's what we have for our menu items. Great. Again, you could store this as synths and you know, do all of that on the front end as well.\u003C\u002Fp>\u003Cp>What do we what else do we have? We've got the order items, we got the order, and then we're gonna add a relationship here to the menu items. So this is gonna be a mini to one relationship because we can only add 1 menu item to one order item. That's great. So we'll call this the menu item.\u003C\u002Fp>\u003Cp>Sounds great. Menu items. That's our related collection. We'll hit save. What do we have next?\u003C\u002Fp>\u003Cp>We probably have a price for this specific item as well. Again, that's gonna be a decimal. I can adjust the number of decimal points. Great. And then one of the other things that I saw on DoorDash here, this is not a an actual order.\u003C\u002Fp>\u003Cp>Let's see if we can find a an order. Right. What does an actual order look like? Receive orders from DoorDash, blah blah blah, notable fields. Let's just look at the UI.\u003C\u002Fp>\u003Cp>So I go in and I add this. I can add special instructions. Right? So let's add a field for special instructions. And we also probably have a quantity.\u003C\u002Fp>\u003Cp>Right? I can go in and adjust the quantity of these. So if I want 3, 4, 5 hash browns, I don't have to add a separate item. So we'll do quantity. That'll be an integer.\u003C\u002Fp>\u003Cp>You can order half of a hash brown. I guess you could. I'm not sure that they would deliver it. So we have quantity, menu item. Let's show the actual order.\u003C\u002Fp>\u003Cp>This little hidden I there tells me that that is not displaying to the person when they pull up this form. But there we have the order, we have the menu item, we have the quantity, we have the price, and we probably have an something like amount or subtotal, item subtotal. Again, this is where it gets tricky as to what you name these things. So the subtotal here would just be the quantity times the price, that's going to equal our item subtotal. Alright.\u003C\u002Fp>\u003Cp>So this feels pretty pretty good, right? That's, kind of what we had. We just totally erased categories for now. We just wanna get to this core functionality. We've got, roughly 30 minutes and some change to evaluate this, right?\u003C\u002Fp>\u003Cp>So first, let's go in. I'm just gonna pull this up side by side, and I'm just gonna steal some of these items. Right? Let's go in and I I don't wanna do McDonald's. I've got my address set to Yankee Stadium here in the Bronx.\u003C\u002Fp>\u003Cp>If you work at Yankee Stadium, I'm sorry. We're gonna deliver some food to you. May not be something that you like. Alright. Let's go with wallet friendly.\u003C\u002Fp>\u003Cp>Jimbo's Jimbo's Hamburger House. Looks good. Let's go in and add a new store over here. So inside Directus, you can see how that form, that data model that I set up, how this actually looks when we are managing this. So again, you're probably for something like DoorDash or an order delivery app, you're probably not going to give the person placing the order, like the end user, access to this.\u003C\u002Fp>\u003Cp>But this interface is so beautiful, I could certainly give the store owners that wanted to sell their stuff on our platform access to this. So this is 252 Saint Ann's Avenue, Bronx, New York. I don't have a postal code, we'll just say 5555. Right. And then I can see I've got a image here.\u003C\u002Fp>\u003Cp>I can just copy that image address and Directus makes this really easy to import images from a URL. Boom. Great. Maybe I even wanna show this image. When you are creating your data models, you've got a ton of options as to how these things display inside the application.\u003C\u002Fp>\u003Cp>So I could go into this image field and I could display a tiny image preview. I can even make it show up as a circle like what they have there. Great. There we go. We've got Jenbo's Hamburger Stand, Hamburger House.\u003C\u002Fp>\u003Cp>Let's go into our menus. Right? So we'll create a menu. Let's call this the lunch menu. We're missing that relationship though.\u003C\u002Fp>\u003Cp>Right? Where did we did we actually create a relationship between menus and stores? We did not. So let's do that quickly before we get too carried away. We will create a many to one relationship on the menus back to the store.\u003C\u002Fp>\u003Cp>Stores, and then I can open this up and in our relationship field, I can add that back as well. So here's our menus. Cool. Great. Alright.\u003C\u002Fp>\u003Cp>So now I've got that relationship. I go into the menu, I can pick the store, there's Jenbos hamburgers. And let's start adding some items from this. Right? We've got our silver dollar pancakes.\u003C\u002Fp>\u003Cp>This looks good. Items are silver dollar pancakes. I can copy that image address and have direct us pull this in. We've got some additional options here. I don't see any like descriptions for those.\u003C\u002Fp>\u003Cp>Not a big deal. The price is 6.50. So we'll just input that. Alright. Next, let's add some other options.\u003C\u002Fp>\u003Cp>We've got a Philly steak bacon wrap with fries deluxe. So we'll just add that here. Looks like I don't have an image for that one, but I do have a description. What is the price for that? It is 12.50.\u003C\u002Fp>\u003Cp>Great. So we'll just input some of this data. Why do I have an image on this? Jenbo, you need to get some images on DoorDash, my man. So we'll just do a Philly steak and cheese.\u003C\u002Fp>\u003Cp>Who knows where we're ripping this from? This looks good. Copy image address. That one's not available. Philly steaks.\u003C\u002Fp>\u003Cp>I guess this is gonna be all sorts of copyright infringement here. This is for entertainment purposes only. Any lawyers watching this, just so we know. Maybe I could get Nat. Nat edits our videos.\u003C\u002Fp>\u003Cp>He's amazing. Nat, maybe you could place a disclaimer on here somewhere. Alright. So we've got a couple of menu items. What else do we have?\u003C\u002Fp>\u003Cp>We have waffles. We have tex mex. Good enough. Alright. So we don't have any orders.\u003C\u002Fp>\u003Cp>We don't have any order items, but at least we have a couple menu items and we have a store. Great. Let's dive into something on the front end. Right? What am I using on the front end?\u003C\u002Fp>\u003Cp>I've got a just a basic Nuxt starter kit. I'm with you guys, so I I like coding in Nuxt and Vue. And then I've just got this simple app. Right? So if we go back to DoorDash, we've got to get some kind of view where we have a list of restaurants.\u003C\u002Fp>\u003Cp>And how do we do this? Right. Nux has a Nux 3 has this use async data composable. So let's just call this stores, that's our key. And then inside this starter kit, I've just got a module that communicates with Directus.\u003C\u002Fp>\u003Cp>I've got a little composable here that auto imports items from our, like the different methods from our SDK and allows me to communicate. So we'll just adjust this. This is going to be our stores. And here, let's just maybe, we're not gonna flex this. But let's just log our data and see what we're getting back.\u003C\u002Fp>\u003Cp>So I could do data. Maybe I wrap this in a pre tag. Bada bing bada boom. I should be able to get this data. We should be reading something from stores, right?\u003C\u002Fp>\u003Cp>I'm not. So the issue here is that I have not set any permissions. Directus comes with rule based access control, which is pretty amazing that I can configure all this via the UI and it applies in real time. But we've got 2 roles by default. The administrator role, which I can apply to users who have full access to the data model, all the different settings, and then we have the public role.\u003C\u002Fp>\u003Cp>So the public role, we want to give access to, probably not So they could probably see the stores, they could see the menus and the menu items. We don't want to give them ability to see any of the orders or create orders. Right? You want to log in for that. So we might as well go ahead and add a new user here as well.\u003C\u002Fp>\u003Cp>So the user role should be able to place orders, they should be able to view orders, with the exception that they can only see their orders. Right? We don't want them to create menu items, but one of the other nice things about the rule based access control in Directus is I could set up custom permissions. Right? If I am a user, I I shouldn't be able to see everybody else's orders, I should just be able to see mine.\u003C\u002Fp>\u003Cp>So I could do something like this, where in this read permission setting for our orders collection, I could go in and control the items that are available. Right? So if I've got a user that created this, has to equal dollar sign underscore current user. So basically this is a little syntactic sugar. This will, fetch the current user ID for you and set up permissions so that if I'm logged in as a specific user, I can only see my orders.\u003C\u002Fp>\u003Cp>That's super nice. Alright. Cool. Let's go back to our front end. Now I refresh.\u003C\u002Fp>\u003Cp>I can actually see some data. This is good. Right. Now let's go in and actually flesh this out. We've got div, maybe we've got a grid of, what, 3 columns.\u003C\u002Fp>\u003Cp>I I love using Tailwind for the styling. Grid calls 3, maybe a gap of 4. Alright. And then we have I'm using the Nuxt UI library just, as a cheat codes here. They have a card component that I can use here.\u003C\u002Fp>\u003Cp>We'll just say ucard. We'll do a v 4. And it looks like, GitHub Copilot is propelling me forward here. We have the store dot ID. What are we gonna drop inside the actual card?\u003C\u002Fp>\u003Cp>We probably have the what do we wanna do here? We wanna add the image up top, and I can see there's like a template, like a header slot and a base slot, but maybe we just stick it all in the same one. I'm also using Nuxt Image in this as well, which is really nice because, I don't have to create any it will automatically optimize the images for me. And Directus will do that via the API as well, but this is kind of nice just because they use the same underlying technology, the same library. So let's do height 48, width 48, object cover, Jinbo.\u003C\u002Fp>\u003Cp>We're not getting the actual image. Right? What's going on here? So if I check our network requests, if I just slide this over to images, we're getting a 403 forbidden on the images as well. Right?\u003C\u002Fp>\u003Cp>So images and assets are a system collection inside Directus. So I've got Directus files, that's where all of our assets get stored, aside from the actual image on disk, we're keeping that library of them. I forgot to enable control for those. So I can go in under system collections, I'll just enable access to those files. Let's go ahead and give this user access to those as well.\u003C\u002Fp>\u003Cp>Where are you? Files. Okay. They've got access. That's great.\u003C\u002Fp>\u003Cp>I probably shouldn't have closed Directus out entirely. But now I could see I've got this, right. And for the width, let's just do with full instead of 48. Okay. Object cover.\u003C\u002Fp>\u003Cp>It's a little pixelated. It doesn't look great. But, next let's go in and add but we're going to add a title for this, right, the store dot name. Let's make this bold. And maybe 3xl.\u003C\u002Fp>\u003Cp>Jinbo's hamburgerhouse. Okay. Alright. Up top here, we can add a h one tag, restaurants or h 2. Or maybe this is a h one.\u003C\u002Fp>\u003Cp>Bold text gray 600. Maybe we add a bit of margin to that. It's a little much, maybe mb 8. And then here for this, I think we've got a container here as well that we might use. I think it just applies some padding.\u003C\u002Fp>\u003Cp>You container. So we get away from Okay. Yeah. Now we're not pressing on the edges of this. Another one of my favorite tools is Tailwind UI.\u003C\u002Fp>\u003Cp>If you build apps and you like speed running things like I do, super handy, right. I could go in and I've got store navigation here. I could just basically lift one of these store navigation components if I wanted to. So let's look for like a header marketing elements. We've got a header here.\u003C\u002Fp>\u003Cp>How do I just get something really simple? This looks pretty good. We'll just copy this. I'm gonna create a new component. I'm gonna call it the header dot view.\u003C\u002Fp>\u003Cp>We'll do a view component. What do they got? They've already got some of this for me. Let's just copy it and see how far we get. I could go into our default layout and maybe I want to add the header here.\u003C\u002Fp>\u003Cp>See what that gets us. Failed to resolve the imports for the icons. They're using a different icon set than I am. I'm gonna delete these out. And I'm not even really super concerned with this navigation either.\u003C\u002Fp>\u003Cp>Let's just make this our cart. We'll change that to a button for now. Type equals button. What do we get? Are we getting anything?\u003C\u002Fp>\u003Cp>Yeah. Cart MX button. Why is it not showing that? Oh, this is probably the mobile menu button. Hidden flex type gray.\u003C\u002Fp>\u003Cp>Justify end. Is it not showing? Okay. There it is. Product login.\u003C\u002Fp>\u003Cp>Let's do something like MD here. That way this is not hidden. Empty. Refresh. Okay.\u003C\u002Fp>\u003Cp>So I got login. Let's just change this to cart. Again, I'm gonna change this quickly to a button because we are going to use it to display our cart at some point. Alright. Do we even need product?\u003C\u002Fp>\u003Cp>Let's just remove that. Forget about it. Okay. So we've got, we got a menu or we've got a restaurant. Right?\u003C\u002Fp>\u003Cp>Now we need to be able to click on this specific restaurant. We go into that restaurant and then we're gonna display a list of all the items from that specific restaurant. Alright. So let's go in and create a new route. We'll call it restaurants or stores in this case.\u003C\u002Fp>\u003Cp>And inside that directory, I'm gonna do something like this where I use, what are those called, just brackets? Yeah. We'll just use a dynamic route here, vcomp ts. And I could copy this async data call here. And let's call this our actual store.\u003C\u002Fp>\u003Cp>So we've got our stores. We're gonna read. We don't wanna read all the stores. We just wanna pick up 1 individual store. And we're gonna do that via the route parameters.\u003C\u002Fp>\u003Cp>So we'll do this route equals use route, which is just a composable that ships with view 3. And here should be route dot ID, and then as the third argument we can pass in, you know, like, hey, I want all these specific fields or not. Again, anytime I'm just like testing this out, I may add something like pre and just log that data just to see what I am working with initially. Okay. So we'll go back to our index page.\u003C\u002Fp>\u003Cp>We wanna make this, clickable. Alright. So maybe we wrap this in Nuxt link. The 2 is gonna be, let's say, dash stores store dot ID. Alright.\u003C\u002Fp>\u003Cp>Let's see where that gets us. Now I can click on this and it goes to the store. Right? You can see that we had a route change. Store, for the let's do something like this.\u003C\u002Fp>\u003Cp>Async data for the the caching, it uses a key. So I could pass the key like this and do route dot ID. Let's see if that gives us what we're after. I refresh. Why am I not getting this?\u003C\u002Fp>\u003Cp>Oh, route dot params.id. Duh. Silly rabbit tricks are for kids. Alright. So here's the data for the individual store.\u003C\u002Fp>\u003Cp>Alright. If I go back, you can see the difference. I'm getting an array of items here because I'm calling the Directus API via read items. When I click on Jenbo's hamburger house, I get just the actual item because we are using read item here. We're picking that up by the param ID.\u003C\u002Fp>\u003Cp>Now, how do I get access to this actual menu item? Right. A couple ways I could do this. I could make another call or one of the many things that I love about Directus is the ability to get all the related fields in a single call. And I'm going to use this wild card syntax here just so we're we could take a look at it.\u003C\u002Fp>\u003Cp>In production, you probably wouldn't want to use this. But if I wanted to, I could just fetch the ID. Right? So it's very GraphQL like and then I can tell it specifically the fields that I want and I can go in and do something like this where I get menus dot star. We can see okay, here's the the different menus.\u003C\u002Fp>\u003Cp>I want to drill down one more layer and I want to get menus dot items dot star. That is a wild card for all of these items and that could give me everything that I need to render this on the page. Right? I could go one further if I'm storing like metadata, like alt tags or title text or something on the image. Itself, I could drill into that further.\u003C\u002Fp>\u003Cp>Really all I need to render an image is just the ID, but great. So now how do we set this up? Let's go in and we'll add a menu. Let's add an h one. This is gonna be the store dot name.\u003C\u002Fp>\u003Cp>Class 3xl. Font bold. Maybe we don't make this bold. Let's see what we got. We got Gino's hamburger's house.\u003C\u002Fp>\u003Cp>Maybe we dress this up a little bit. What does it look like inside door dash? Got like this nice header image up here at the top. We don't really have one of those, but I can at least show the, logo for the store. Right?\u003C\u002Fp>\u003Cp>Let's make it look nice. Store dot image. Is that what we called it? Class let's make it square. 48.\u003C\u002Fp>\u003Cp>Let's do 24. We'll make it smaller. Right. With 24, object cover rounded full, this should give us the image there. We could probably wrap this in, div.\u003C\u002Fp>\u003Cp>Just flex that. Class equals flex to a gap of 2. And then within this, we might wrap that in a div as well. Okay. Cool enough.\u003C\u002Fp>\u003Cp>We've got that. Now let's add these actual menu items. Right? So I've got this array of menu items. Let's just take a peek at how we're doing on time.\u003C\u002Fp>\u003Cp>Roughly a little less than 20 minutes. Are we gonna get this done or not? We shall see. Right? So I'll go in.\u003C\u002Fp>\u003Cp>Let's just do a grid of menu items. Great. Got a gap. Go co Copilot go. Right?\u003C\u002Fp>\u003Cp>We're use that same ucard component. V for menu and store menu. I really like GitHub Copilot. It's great for like really simple auto completion things like this. One of the things that I can tell you is make sure whatever you do, you verify all of this before you actually use it.\u003C\u002Fp>\u003Cp>Right? Because here I'm getting okay. This is not actually working at all. So v 4 menu and store menus, we really just want the first item. Right?\u003C\u002Fp>\u003Cp>We're gonna do the items in store menus 1, the first item in there. That's gonna be item dot ID. We don't really need the store. Right. This is kinda weird.\u003C\u002Fp>\u003Cp>This is where GitHub Copilot gets you into, a lot of trouble if you just blindly do whatever it's it's saying here. And this is still not coming up right. Item in store dot menus dot 1. This would be the actual menus dot items. There we go.\u003C\u002Fp>\u003Cp>Okay. So now we could see we've got our silver dollar pancakes, we've got our Philly steak and cheese. Let's make this smaller. And then at the bottom, we're going to add our price. Don't forget the item dot description.\u003C\u002Fp>\u003Cp>Then we also need a price. Alright. So item dot price. Let's just see what that looks like. We can add a dollar sign.\u003C\u002Fp>\u003Cp>We've got our silver dollar pancakes. Cool. Maybe we wanna give a little bit of gap between all these. This is not gonna be a Nuxt link either, right. We want this to be a button type equals button.\u003C\u002Fp>\u003Cp>We'll add some functionality into this in a moment. Class equals text left. Okay. Maybe we give it a flex call gap 2, Give a little bit of spacing in there. And make the width full.\u003C\u002Fp>\u003Cp>Well that, yeah, there we go. That gets us what we want. Let's make the gap larger. Okay. Cool.\u003C\u002Fp>\u003Cp>So we got some menu items. Right? We want to click on these menu items and add them into a cart. So what are we gonna do, how are we gonna manage this cart? Because it could be we want to maintain this cart across like page load and also across different restaurants or different pages as well.\u003C\u002Fp>\u003Cp>So for now, I think I'm just going to simply use a composable for this. I don't have Pina installed, which is a really great state library for Vue. You know, Nuxt has a built in used state composable that you could potentially use as well. But I like the idea of just adding a composable to manage this. So I'm gonna add a new folder here in the root directory.\u003C\u002Fp>\u003Cp>I'm gonna call it composable Composables. Nuxt will actually auto import these. And let's call it use cart dot ts. Alright. We're going to export a default function, use cart.\u003C\u002Fp>\u003Cp>And up here at the top, let's just take a look at our cart structure. Right. What do we have? What do we want to send? So we've got the orders.\u003C\u002Fp>\u003Cp>If we take a look at our data model, right, we have the store, we have the customer, we have that's gonna be our individual user. So we've got to log in as a user. We got our items. We got a total for that. Okay.\u003C\u002Fp>\u003Cp>So let's go in and add a list of items. That's gonna be an array of items. Right. What else are we going to have inside this composable? Right.\u003C\u002Fp>\u003Cp>We've got our items. We're pulling this outside of the function definition because we want to store this. If we call this use cart in multiple places, we still wanna access the same items. Alright. So this is gonna return the items for that cart.\u003C\u002Fp>\u003Cp>We're gonna add those cart the order total. Alright. So we have the order total that's going to be computed. That would be a return items dot for dues dot item dot price. That's perfect.\u003C\u002Fp>\u003Cp>Thank you, Github. Got an order total. And then we've probably got a function for, oh, let's go back up, async function, place, yeah. Let's add to cart. We're just gonna push an item to the cart.\u003C\u002Fp>\u003Cp>We gotta remove from cart. Look at GitHub Copilot. Again, like, please take this with a grain of salt, whether this is actually wise or not. Index of item, items value dot splice. Index self item.\u003C\u002Fp>\u003Cp>Okay. And then if we've got, and these actually probably don't need to be async functions. And then we probably got an async function for submit order. Alright. Submit order.\u003C\u002Fp>\u003Cp>Alright. This is gonna be constant data equals I don't know if that's gonna be data or not. We'll do await. We'll do use directus. Then we are going to create an item in the orders table or orders collection, the total for that order total, ordertotal will be the order total dot value.\u003C\u002Fp>\u003Cp>Then we have our items, which are going to be, should just be items. Alright. So we could do something like, nope. We're not gonna do that. We are going to do unref items since that is unrefed.\u003C\u002Fp>\u003Cp>We've got the store info and this customer field, inside direct us, I can save the current user on create. So let's do that. So the store is going to be where are we gonna get the store from? Maybe we're gonna pass that in the function. Store ID, that's a string.\u003C\u002Fp>\u003Cp>Store ID. Alright. Is this cool? We're gonna return these other functions from our composable. Let's see how we do.\u003C\u002Fp>\u003Cp>Alright. So now if we go back to our app, we've got the hamburger house. Let's just remove this, comment that out. Alright. So we got our menu, We want to push these items into a cart.\u003C\u002Fp>\u003Cp>Where are we at time wise? We got 10 minutes left. Are we gonna actually get these into a cart or not? So here inside this specific, page we might do what we are going to return. Let's take a look at our composable.\u003C\u002Fp>\u003Cp>Right. We've got our order items. We've got our items in the cart. Let's call the we're gonna get items. Let's call these cart items.\u003C\u002Fp>\u003Cp>Add to cart. Remove from cart equals used cart. K. And then can we hey. Let's just drop in our cart items here just to see what that looks like.\u003C\u002Fp>\u003Cp>Cart items. It's It's not showing anything for our card items. Are we getting an error? Let's actually take a look. So we'll go to ID.\u003C\u002Fp>\u003Cp>Cart items equals undefined. Oh, it's because I'm getting items. There we go. Alright. So we're showing just an array.\u003C\u002Fp>\u003Cp>Let's just do this. I'm just gonna give this some padding. BG gray, 50. Let's make it 100. This is our, p tag cart items.\u003C\u002Fp>\u003Cp>Okay. Alright. So now what we want to do on each one of these, right, if we click an add to cart button, which we we don't necessarily have. Where is that? Right?\u003C\u002Fp>\u003Cp>Why do we not have a button for add to cart? Maybe we don't even put that. That shouldn't even be in here basically. So let's move this up. We'll just take the class.\u003C\u002Fp>\u003Cp>We'll apply that to the card itself. Why is it not doing what we want? Gapspacey2. Why are you not doing what I want you to do? Alright.\u003C\u002Fp>\u003Cp>I'm not gonna stress over it. We'll just wrap it in a div because this will be the div task adventure. I know all the non tailwind CSS guys are probably freaking out at this moment, but okay. We'll use this u button component provided by Nuxt UI and we're going to click add to cart. We're going to add that item to the cart.\u003C\u002Fp>\u003Cp>Yeah. Close enough. Right. So if I click add to cart, we can see we've got our cart item here. It's not necessarily what I want.\u003C\u002Fp>\u003Cp>Right? The data structure is not entirely what I want. So let's actually just define it here. The item ID, we want this to be our order items. Alright.\u003C\u002Fp>\u003Cp>So we go into order items, We got the menu item. Let's do that. Menu item. Okay. Special instructions.\u003C\u002Fp>\u003Cp>No onions, please. Yeah. We'll just automatically populate that. The quantity, let's set that to 1. And then the item subtotal, we'll just say item price.\u003C\u002Fp>\u003Cp>Alright. So now if I refresh, does that give us kind of the structure that we want? It's still populating the entire menu item. We just want the menu item item.id. That should give us what we need.\u003C\u002Fp>\u003Cp>Okay. Does that match the right structure? Great. Where we at on time? We got, 5 minutes or so.\u003C\u002Fp>\u003Cp>Let's actually get this order together. Right? Alright. So inside our header, maybe we could display our cart items there. I really wanted to actually show the submitting to Directus.\u003C\u002Fp>\u003Cp>So knowing that I am short on time, right, we could go in and add these items. Let's just try submitting it right now and see. Alright. So we got our cart items. Let's go in.\u003C\u002Fp>\u003Cp>We've got new button, place order, and we'll call this submit order. Are we passing we're passing that store ID as well. Alright. So this isn't probably the best particular type of design, but it will get the job done. Let's do a large button.\u003C\u002Fp>\u003Cp>At click is submit order and then we've got the store dot ID that we're passing to this. And then let's go in and just like what does our submit order look like? This should be probably like try catch. So we could catch any type of errors. Response.\u003C\u002Fp>\u003Cp>If we catch an error, we want to return the response. Great. Submit order. Oh, that looks a little rough. Let's move that over here.\u003C\u002Fp>\u003Cp>Oh, I put I did I totally put that in the wrong spot anyway. Submit order and let's just hide this if the user is not logged in as well. Right? We don't want them to be able to place an order if you are not logged in. So this composable that I have, I want to say, has a or like this module that I set up has some of this already built in.\u003C\u002Fp>\u003Cp>So this will be v Where's the directus auth? Directus auth, user. So we'll do something like this where we get constant user equals use direct us off if there is a user. Okay. Alright.\u003C\u002Fp>\u003Cp>And if there's not a user, let's do a u button. V of no user, we are going to I think I could do this with it, where we have login to place an order. Login to order. And we will do what is it? 2 auth, I don't even need that.\u003C\u002Fp>\u003Cp>We do to auth\u002Fauth\u002Flogin. Login to order. Okay. We've got our user. I didn't set up a user, but let's create one now.\u003C\u002Fp>\u003Cp>This will be our user. Test usertest@example.com. Let's give them a real secure password. We'll do password here and then I am going to select the role of user. Great.\u003C\u002Fp>\u003Cp>Save it. Now I should actually be able to log in. So test at example dot com, Password of that login. Tried to log in to the wrong spot. Where's the redirect for this?\u003C\u002Fp>\u003Cp>Use direct us off. Is this going to be in my Nuxt config? We want to redirect to auth login, not portal. We want to redirect to the index page. Oh, we're gonna we're gonna be cutting this one really really close.\u003C\u002Fp>\u003Cp>Come on. Alright. So we go back. Do we have we've got our menus. We don't have any cart orders.\u003C\u002Fp>\u003Cp>It looks like I'm still am I still logged in? Do we have the cookie? We've got okay. So we've got a session token here, so it's still showing me as logged in. If we hit add menu items, I hit place order, what happens is we can see the order posted.\u003C\u002Fp>\u003Cp>If we go inside Directus, do we have that specific order? Yes, we do. So we've got here's the store information, here's the order, we've got the item subtotal. We don't have the prices not being populated. So I didn't item subtotal should be item dot price.\u003C\u002Fp>\u003Cp>Why is that not populating correctly? I didn't add the price either. But item dot price. Cool. I'm calling this a win.\u003C\u002Fp>\u003Cp>So we've got what have we done? We've set up our data model within an hour. We've got all the items coming through. I could go in and clean this up, certainly, and that would be my next steps. But we've got a really simple UI here on the front end that with probably another 20, 30 minutes and a little bit of love.\u003C\u002Fp>\u003Cp>Man, this thing would be amazing. So I can go in, if I refresh this page, we should probably ditch that, but I can go in and add my pancakes. I can place the order and see it show up inside Directus. Alright. So we just do a quick review.\u003C\u002Fp>\u003Cp>What did we achieve? Right. We got the data model done, check. We got a list of the menu items for a restaurant, check. And then we got the place an order for menu items.\u003C\u002Fp>\u003Cp>Boom. Knock all 3 of those off. I'm gonna credit that to my wife being here to help me today. That's it for this episode, guys. I hope you've enjoyed this one.\u003C\u002Fp>\u003Cp>If you did, make sure you hit the subscribe button on Directus TV because we want you to catch more of this content. See you.\u003C\u002Fp>","Hi guys. Welcome back to the next episode of 100 apps, 100 hours, where we rebuild or clone some of your favorite apps or publicly fail shamefully. Shamefully fail trying. Alright, I'm your host Brian Gillespie, developer advocate at Directus. Super excited to have you. I've got a special guest, my lovely wife joining me. I'm not sure if you can see her on my shirt here. This is a running gag between us where she basically gets me clothing with her face on it. So we'll include her in this episode. Today we're all about building a food ordering platform similar to DoorDash, Grubhub, or it seems like one of these platforms pops up every single week. The rules are very simple. We have 60 minutes to plan and build this application, no more, no less. And the second rule, the anti rule is we're gonna use whatever we have at our disposal, whether that's AI, UI frameworks, front end frameworks. We're going to be using Directus on the back end to generate this app really quickly. So let's kind of talk about the food ordering app before we dive in. You may have seen some of these before, we've just got a list of menu options, we want to be able to place those inside a cart and place an order for that cart, have somebody bring that food to us. So that's the basic gist of what we're building. Let's get started. Alright. So let's roll the timer and begin. I'm gonna just pull this to the side and let's pull up DoorDash just to take a look at this, right? So here's kind of what we're after. We've got different restaurants on here and if I click into one of these, like the bun stop shop, I have a menu of items here and we can add these to a particular cart, Choose some options. I don't know that we'll get to things like options. We'll probably just try to keep this very simple to begin with. We'll go ahead and add this to the cart and now I can go and place an order for this and have it sent to my delivery location. So great. That looks good. Let's discuss the functionality that we actually want out of this particular application, right? So I'm going to drag this down. In 60 minutes, I don't wanna bite off more than I can chew here, if we're doing cliches. So let's, you know, we've got the data model for the back end. We'll get that set up. On the front end we will show a list of menu items for restaurants. Allow place an order for menu items, and that's probably pretty good for an hour. I don't know. We'll see how far we get with it. The next thing I always like to do is to sketch out my data model. And one way that you could do this is obviously if you're trying to model another application, right, you could always kind of look at it and think through it. One of the things that I like to do, in this case I'm gonna just search for the DoorDash API because I know that they have other services that connect to this. And it looks like they do have an API that we could potentially use here. We don't wanna drive for DoorDash. Maybe it's this marketplace API, how to guides, menu flows and order flows. Here we go. Retrieve orders from DoorDash, set up a menu pull, set up a menu push, get a DoorDash menu. Okay. Yeah. So I can at least see a little bit of how they've got this structured, right? So I see there's a store that's associated to this. We have a menu. I'm assuming this is like a could be a breakfast menu or a lunch menu or a dinner menu. Maybe we have different menus that way. Then we have categories, I see, and then we have the actual items. Alright. So that gives me a good idea of of how they've solved this problem. And obviously, they're a large company, so they've got a lot of smart people on it. Do I want to take that at face value? Probably not. But for an hour, it seems like a pretty good model. Right? So let's start sketching this out. I'm just gonna make this full screen. We'll add a little box here. We've got, stores or restaurants. I'm not sure which one of those I'm gonna call it yet. Probably stores is easier. Then we have a we have menus. What else do we have underneath that? We've got menu items. Probably like categories. Right? I saw that on there. So we've got different items in the categories. Menu items, and then we're gonna have an orders table with probably some order items as well, orders items. Alright. Don't necessarily have to do this, but, yeah. Each menu belongs to a restaurant. The menu items belong to a category. So there's a relationship there. Just draw these arrows. This is more for me just visually to see how everything maps. Directus, our back end will make creating these relationships and these models very easy. Great. We probably have a restaurant that's attached to the order. We have a menu item that's attached to the order item. And then we've got, an actual person placing the order. Directus is gonna give us that. That will be our users table probably. User, users place orders. Okay. Alright. So as far as the structure of this, we got that. Don't worry about that little guy there. This seems pretty good as far as the structure. Right? So how do we start building something like this? I'm gonna pull up my blank instance of Directus. Directus is going to mirror all the changes that we make here inside the application, all of our collections. Those are going to be tables inside our database. All the fields are going to get represented as columns. So it's a great way to build out this functionality. And for me, I'm very front end oriented, so I'm a very visual person. So instead of writing migrations, this is great for me. I can go in and build this data model visually. And if I need to store it as code I can kick out the schema dot JSON file as well that others who are working on this project could apply. So let's go with stores. I really like that. If we just take a look at DoorDash, one of the things that I was curious about, okay. It looks like we have an ID for the store. They're not using anything like a slug. So it doesn't really matter for SEO purposes. Let me get back here. So we're gonna create this new collection. I can zoom in a bit. We've got stores. We've got our generated UUID. Let's go ahead and and just add these system fields here, just for status, date created. You know, they give us some of that functionality out of the box. So we're gonna have a name for the store. We're gonna keep this really lightweight so we could try to get, push orders into this. So we'll just do a name. Let's do a logo or an image. Let's just call it image for the store. Great. We probably got something like an address, probably. Right? Street address. We have a city. I'm just gonna duplicate this field, which is really nice. Street is a city, state, or region. We're trying to be international friendly here. And then we'll have postal code. Great. Cool. And then I may even just like group these together inside Directus using our detail group interface. We'll just call it address info. And we can even add a nice little map pin. There we go. So we'll group these together. Region and city, we can make those half width. Directus really allows you to customize the forms that your users will see. You know, on the front end here, I'm probably not going to give the people placing orders the access to the Directus Data Studio here. But, I certainly could give the restaurants owners or the store owners access to that to manage their menu, their store information. Alright, so we've got, this looks pretty good for our stores. Let's move on to the next one. Actually, take that back. Let me add a building, like a company, Office building. Building. Business. There we go. Alright. So that looks kind of like a store. Great. Alright. So next we are going to add menus. Alright. We will, I can again, I can add these things whether they are in draft mode or publish. You know, I may want to track when it was created, who it was updated by, etcetera. We'll give a name for the menu. And I'm just going to skirt the categories thing here, and just add these. Maybe we don't. But, yeah, let's do. We'll just go menu items. Right. And for menu items, again, I'll just populate these. They're always easy to remove later. We might want to have a sort field so we can order those items, not not actually place an order for them, but order them in terms of like sorting. What else? Okay. So on our menu items, if we look at DoorDash, we've got we've got a name for the item. We've got, description. Probably a price. Alright. Where's our DoorDash menu? What does an item have? An item has a name, it has a description, is it active, is it alcohol, is it bike friendly? I would argue that a cheeseburger is not bike friendly, but I'm sure this is on the delivery side. And then I see things like price, I see a tax rate, and then I see, like, some extras. For the purposes of this, let's keep it lightweight. We've got a name for this. We've got an image image for the menu item. We'll just create that. Let's have a description. I'm just gonna keep that as straight text. Toggle the sidebar within Arc as well. So you probably don't wanna do a WYSIWYG description here. And there's always risk if you're not sanitizing HTML content that is user submitted. So we'll just use text area. What else do we have? We have a price that we're going to set for this. That can be an input. I I know a lot of ecommerce options store the prices as integers, like incense. I'm just gonna use a decimal here And I could go into field creation mode if I want to, if I wanna control like the precision and things like that, and the scale. So I will just put 2 here. I only want 2 decimal places. We could do a formatted label and I can even add a prefix within Directus, which is really nice here. We'll do auto format, see what that generates for us. Cool. So we've got our menu items, we've got menus. Alright. Let's add a relationship between all of these. So we have a store, it has a menu or potentially menus, and then each menu has menu items. So if I go into menus, the designer or the recovering designer in me is gonna freak out if I do not add at least some type of icon for this. But let's add a relationship and this is gonna be really easy inside Directus. This is gonna be a one to many relationship because we have items on our menu. We have one menu, many items. So I'll go in and we'll call this, items is going to be the name of our field in the menu collection or the menus table and then our related collection is going to be menu items and the foreign key would be the menu. Right? So inside that menu items we're going to create a new field called menu. And I definitely want to show a link back to that item within the data studio. Now if you expand this into the advanced settings when you're creating relationships or just adding fields to the collection, you can see here that menu is not on menu items. That field does not currently exist. But Directus is gonna create that for us, which is really nice. Right? I don't have to mess with sitting down and doing migrations. As you can see, we're gonna build this functionality really quickly. Alright. So we have menus, we have a name, we have menu items. Let's go ahead and save this. If I go into our menu items, now I've got a field for menu, so that's going to show up. What else do we need to add here? Right? We're gonna need an orders table. So we'll say orders, we'll definitely wanna know who placed those orders, what's the status of that order, is it ready or not. And then let's go in and add order items. Again, naming things is very challenging, you know. Should this be called orders underscore items or order items? I like it this way. Totally up to you. Whatever you wanna name this inside your own. Maybe we sort those. So we've got orders, we've got order items. The order, if we just think through this, and of course add a great looking icon for it, the order is gonna be linked to a specific store. Right? We can only place one order from a store. So we'll go in, this is gonna be a mini to one relationship, and we've got this up over here so we can see everything we've got going on. This is gonna be the store. The related collection will be our stores, and Directus is going to create that relationship for us. And again, if I were to just open up Table Plus, so you can get a look at this. What is happening behind the scenes here? Directus is creating these relationships, it's creating my tables, it's creating all my columns in the database and it's also creating those relationships for me inside the underlying SQL. So if I were to rip out Directus one day, you know all my SQL data is still pure everything inside my database. Alright, so we've got an order, we've got a store, we've got a, a customer. Right? We'll call that customer. This is gonna be the Directus users collection. We've got an address for that so we could go in and do street address, city. I can also go back to that other collection and just duplicate these across, which is probably what I should have done. But no worries there. Alright. So we'll add these fields and again, I could group these together using our detail group. We'll call this address info. You know, we may again, we're gonna have a front end where users place their order, but on the back end we could fulfill these orders through the Directus interface as well, through the app. And that again that helps me just move things along faster so that I don't have to build an admin interface. I could just use Directus here to manage all of that data. Alright. Orders and then we have our items, right? So we'll go in this is again, this is gonna be a one to many relationship. There are one order or there's one order that has many items. Because I know yeah. At least at first, or when you're modeling data this way, it can be kind of confusing of which relationships to use. So this is a one to many from the orders table to the order items. I'm gonna call this items as well, and the table is gonna be order items, and then we're gonna use a foreign key of order. I always like to show a link to the item so I can open that up inside Directus and we'll just hit save here. Great. We got a store, we got a customer, we got a street address, we got a status that we can go in and adjust. Alright. This is a draft order, published order, an archived order. That doesn't make a ton of sense. Alright. So let's just nuke those. We'll call this a new order. We'll call it, In Progress or Being Made. I don't know. Again, naming things is part of the one of the hardest things inside development. We'll say ready for pickup for pickup. And what? Delivery? I'm in out for delivery. Delivery. Out for delivery. Out for delivery. Let's just say delivered. Right? I think those are just a quick rundown of what the different states could be for a specific order. Is it new? It's being made? It's ready for pickup? Somebody comes and picks it up, it's out for delivery, and it's delivered. Right. Again, it's not as simple as this. Right? You've got a driver and things like that included, but we're keeping this very simple so we can actually dive in to the functionality. So on the order, we also probably have like a total for the order as well. Right? Order total Order total that would sum up all of the individual line items. This is gonna be an input field. The schema though, we probably want the type to be decimal, just because that's what we have for our menu items. Great. Again, you could store this as synths and you know, do all of that on the front end as well. What do we what else do we have? We've got the order items, we got the order, and then we're gonna add a relationship here to the menu items. So this is gonna be a mini to one relationship because we can only add 1 menu item to one order item. That's great. So we'll call this the menu item. Sounds great. Menu items. That's our related collection. We'll hit save. What do we have next? We probably have a price for this specific item as well. Again, that's gonna be a decimal. I can adjust the number of decimal points. Great. And then one of the other things that I saw on DoorDash here, this is not a an actual order. Let's see if we can find a an order. Right. What does an actual order look like? Receive orders from DoorDash, blah blah blah, notable fields. Let's just look at the UI. So I go in and I add this. I can add special instructions. Right? So let's add a field for special instructions. And we also probably have a quantity. Right? I can go in and adjust the quantity of these. So if I want 3, 4, 5 hash browns, I don't have to add a separate item. So we'll do quantity. That'll be an integer. You can order half of a hash brown. I guess you could. I'm not sure that they would deliver it. So we have quantity, menu item. Let's show the actual order. This little hidden I there tells me that that is not displaying to the person when they pull up this form. But there we have the order, we have the menu item, we have the quantity, we have the price, and we probably have an something like amount or subtotal, item subtotal. Again, this is where it gets tricky as to what you name these things. So the subtotal here would just be the quantity times the price, that's going to equal our item subtotal. Alright. So this feels pretty pretty good, right? That's, kind of what we had. We just totally erased categories for now. We just wanna get to this core functionality. We've got, roughly 30 minutes and some change to evaluate this, right? So first, let's go in. I'm just gonna pull this up side by side, and I'm just gonna steal some of these items. Right? Let's go in and I I don't wanna do McDonald's. I've got my address set to Yankee Stadium here in the Bronx. If you work at Yankee Stadium, I'm sorry. We're gonna deliver some food to you. May not be something that you like. Alright. Let's go with wallet friendly. Jimbo's Jimbo's Hamburger House. Looks good. Let's go in and add a new store over here. So inside Directus, you can see how that form, that data model that I set up, how this actually looks when we are managing this. So again, you're probably for something like DoorDash or an order delivery app, you're probably not going to give the person placing the order, like the end user, access to this. But this interface is so beautiful, I could certainly give the store owners that wanted to sell their stuff on our platform access to this. So this is 252 Saint Ann's Avenue, Bronx, New York. I don't have a postal code, we'll just say 5555. Right. And then I can see I've got a image here. I can just copy that image address and Directus makes this really easy to import images from a URL. Boom. Great. Maybe I even wanna show this image. When you are creating your data models, you've got a ton of options as to how these things display inside the application. So I could go into this image field and I could display a tiny image preview. I can even make it show up as a circle like what they have there. Great. There we go. We've got Jenbo's Hamburger Stand, Hamburger House. Let's go into our menus. Right? So we'll create a menu. Let's call this the lunch menu. We're missing that relationship though. Right? Where did we did we actually create a relationship between menus and stores? We did not. So let's do that quickly before we get too carried away. We will create a many to one relationship on the menus back to the store. Stores, and then I can open this up and in our relationship field, I can add that back as well. So here's our menus. Cool. Great. Alright. So now I've got that relationship. I go into the menu, I can pick the store, there's Jenbos hamburgers. And let's start adding some items from this. Right? We've got our silver dollar pancakes. This looks good. Items are silver dollar pancakes. I can copy that image address and have direct us pull this in. We've got some additional options here. I don't see any like descriptions for those. Not a big deal. The price is 6.50. So we'll just input that. Alright. Next, let's add some other options. We've got a Philly steak bacon wrap with fries deluxe. So we'll just add that here. Looks like I don't have an image for that one, but I do have a description. What is the price for that? It is 12.50. Great. So we'll just input some of this data. Why do I have an image on this? Jenbo, you need to get some images on DoorDash, my man. So we'll just do a Philly steak and cheese. Who knows where we're ripping this from? This looks good. Copy image address. That one's not available. Philly steaks. I guess this is gonna be all sorts of copyright infringement here. This is for entertainment purposes only. Any lawyers watching this, just so we know. Maybe I could get Nat. Nat edits our videos. He's amazing. Nat, maybe you could place a disclaimer on here somewhere. Alright. So we've got a couple of menu items. What else do we have? We have waffles. We have tex mex. Good enough. Alright. So we don't have any orders. We don't have any order items, but at least we have a couple menu items and we have a store. Great. Let's dive into something on the front end. Right? What am I using on the front end? I've got a just a basic Nuxt starter kit. I'm with you guys, so I I like coding in Nuxt and Vue. And then I've just got this simple app. Right? So if we go back to DoorDash, we've got to get some kind of view where we have a list of restaurants. And how do we do this? Right. Nux has a Nux 3 has this use async data composable. So let's just call this stores, that's our key. And then inside this starter kit, I've just got a module that communicates with Directus. I've got a little composable here that auto imports items from our, like the different methods from our SDK and allows me to communicate. So we'll just adjust this. This is going to be our stores. And here, let's just maybe, we're not gonna flex this. But let's just log our data and see what we're getting back. So I could do data. Maybe I wrap this in a pre tag. Bada bing bada boom. I should be able to get this data. We should be reading something from stores, right? I'm not. So the issue here is that I have not set any permissions. Directus comes with rule based access control, which is pretty amazing that I can configure all this via the UI and it applies in real time. But we've got 2 roles by default. The administrator role, which I can apply to users who have full access to the data model, all the different settings, and then we have the public role. So the public role, we want to give access to, probably not So they could probably see the stores, they could see the menus and the menu items. We don't want to give them ability to see any of the orders or create orders. Right? You want to log in for that. So we might as well go ahead and add a new user here as well. So the user role should be able to place orders, they should be able to view orders, with the exception that they can only see their orders. Right? We don't want them to create menu items, but one of the other nice things about the rule based access control in Directus is I could set up custom permissions. Right? If I am a user, I I shouldn't be able to see everybody else's orders, I should just be able to see mine. So I could do something like this, where in this read permission setting for our orders collection, I could go in and control the items that are available. Right? So if I've got a user that created this, has to equal dollar sign underscore current user. So basically this is a little syntactic sugar. This will, fetch the current user ID for you and set up permissions so that if I'm logged in as a specific user, I can only see my orders. That's super nice. Alright. Cool. Let's go back to our front end. Now I refresh. I can actually see some data. This is good. Right. Now let's go in and actually flesh this out. We've got div, maybe we've got a grid of, what, 3 columns. I I love using Tailwind for the styling. Grid calls 3, maybe a gap of 4. Alright. And then we have I'm using the Nuxt UI library just, as a cheat codes here. They have a card component that I can use here. We'll just say ucard. We'll do a v 4. And it looks like, GitHub Copilot is propelling me forward here. We have the store dot ID. What are we gonna drop inside the actual card? We probably have the what do we wanna do here? We wanna add the image up top, and I can see there's like a template, like a header slot and a base slot, but maybe we just stick it all in the same one. I'm also using Nuxt Image in this as well, which is really nice because, I don't have to create any it will automatically optimize the images for me. And Directus will do that via the API as well, but this is kind of nice just because they use the same underlying technology, the same library. So let's do height 48, width 48, object cover, Jinbo. We're not getting the actual image. Right? What's going on here? So if I check our network requests, if I just slide this over to images, we're getting a 403 forbidden on the images as well. Right? So images and assets are a system collection inside Directus. So I've got Directus files, that's where all of our assets get stored, aside from the actual image on disk, we're keeping that library of them. I forgot to enable control for those. So I can go in under system collections, I'll just enable access to those files. Let's go ahead and give this user access to those as well. Where are you? Files. Okay. They've got access. That's great. I probably shouldn't have closed Directus out entirely. But now I could see I've got this, right. And for the width, let's just do with full instead of 48. Okay. Object cover. It's a little pixelated. It doesn't look great. But, next let's go in and add but we're going to add a title for this, right, the store dot name. Let's make this bold. And maybe 3xl. Jinbo's hamburgerhouse. Okay. Alright. Up top here, we can add a h one tag, restaurants or h 2. Or maybe this is a h one. Bold text gray 600. Maybe we add a bit of margin to that. It's a little much, maybe mb 8. And then here for this, I think we've got a container here as well that we might use. I think it just applies some padding. You container. So we get away from Okay. Yeah. Now we're not pressing on the edges of this. Another one of my favorite tools is Tailwind UI. If you build apps and you like speed running things like I do, super handy, right. I could go in and I've got store navigation here. I could just basically lift one of these store navigation components if I wanted to. So let's look for like a header marketing elements. We've got a header here. How do I just get something really simple? This looks pretty good. We'll just copy this. I'm gonna create a new component. I'm gonna call it the header dot view. We'll do a view component. What do they got? They've already got some of this for me. Let's just copy it and see how far we get. I could go into our default layout and maybe I want to add the header here. See what that gets us. Failed to resolve the imports for the icons. They're using a different icon set than I am. I'm gonna delete these out. And I'm not even really super concerned with this navigation either. Let's just make this our cart. We'll change that to a button for now. Type equals button. What do we get? Are we getting anything? Yeah. Cart MX button. Why is it not showing that? Oh, this is probably the mobile menu button. Hidden flex type gray. Justify end. Is it not showing? Okay. There it is. Product login. Let's do something like MD here. That way this is not hidden. Empty. Refresh. Okay. So I got login. Let's just change this to cart. Again, I'm gonna change this quickly to a button because we are going to use it to display our cart at some point. Alright. Do we even need product? Let's just remove that. Forget about it. Okay. So we've got, we got a menu or we've got a restaurant. Right? Now we need to be able to click on this specific restaurant. We go into that restaurant and then we're gonna display a list of all the items from that specific restaurant. Alright. So let's go in and create a new route. We'll call it restaurants or stores in this case. And inside that directory, I'm gonna do something like this where I use, what are those called, just brackets? Yeah. We'll just use a dynamic route here, vcomp ts. And I could copy this async data call here. And let's call this our actual store. So we've got our stores. We're gonna read. We don't wanna read all the stores. We just wanna pick up 1 individual store. And we're gonna do that via the route parameters. So we'll do this route equals use route, which is just a composable that ships with view 3. And here should be route dot ID, and then as the third argument we can pass in, you know, like, hey, I want all these specific fields or not. Again, anytime I'm just like testing this out, I may add something like pre and just log that data just to see what I am working with initially. Okay. So we'll go back to our index page. We wanna make this, clickable. Alright. So maybe we wrap this in Nuxt link. The 2 is gonna be, let's say, dash stores store dot ID. Alright. Let's see where that gets us. Now I can click on this and it goes to the store. Right? You can see that we had a route change. Store, for the let's do something like this. Async data for the the caching, it uses a key. So I could pass the key like this and do route dot ID. Let's see if that gives us what we're after. I refresh. Why am I not getting this? Oh, route dot params.id. Duh. Silly rabbit tricks are for kids. Alright. So here's the data for the individual store. Alright. If I go back, you can see the difference. I'm getting an array of items here because I'm calling the Directus API via read items. When I click on Jenbo's hamburger house, I get just the actual item because we are using read item here. We're picking that up by the param ID. Now, how do I get access to this actual menu item? Right. A couple ways I could do this. I could make another call or one of the many things that I love about Directus is the ability to get all the related fields in a single call. And I'm going to use this wild card syntax here just so we're we could take a look at it. In production, you probably wouldn't want to use this. But if I wanted to, I could just fetch the ID. Right? So it's very GraphQL like and then I can tell it specifically the fields that I want and I can go in and do something like this where I get menus dot star. We can see okay, here's the the different menus. I want to drill down one more layer and I want to get menus dot items dot star. That is a wild card for all of these items and that could give me everything that I need to render this on the page. Right? I could go one further if I'm storing like metadata, like alt tags or title text or something on the image. Itself, I could drill into that further. Really all I need to render an image is just the ID, but great. So now how do we set this up? Let's go in and we'll add a menu. Let's add an h one. This is gonna be the store dot name. Class 3xl. Font bold. Maybe we don't make this bold. Let's see what we got. We got Gino's hamburger's house. Maybe we dress this up a little bit. What does it look like inside door dash? Got like this nice header image up here at the top. We don't really have one of those, but I can at least show the, logo for the store. Right? Let's make it look nice. Store dot image. Is that what we called it? Class let's make it square. 48. Let's do 24. We'll make it smaller. Right. With 24, object cover rounded full, this should give us the image there. We could probably wrap this in, div. Just flex that. Class equals flex to a gap of 2. And then within this, we might wrap that in a div as well. Okay. Cool enough. We've got that. Now let's add these actual menu items. Right? So I've got this array of menu items. Let's just take a peek at how we're doing on time. Roughly a little less than 20 minutes. Are we gonna get this done or not? We shall see. Right? So I'll go in. Let's just do a grid of menu items. Great. Got a gap. Go co Copilot go. Right? We're use that same ucard component. V for menu and store menu. I really like GitHub Copilot. It's great for like really simple auto completion things like this. One of the things that I can tell you is make sure whatever you do, you verify all of this before you actually use it. Right? Because here I'm getting okay. This is not actually working at all. So v 4 menu and store menus, we really just want the first item. Right? We're gonna do the items in store menus 1, the first item in there. That's gonna be item dot ID. We don't really need the store. Right. This is kinda weird. This is where GitHub Copilot gets you into, a lot of trouble if you just blindly do whatever it's it's saying here. And this is still not coming up right. Item in store dot menus dot 1. This would be the actual menus dot items. There we go. Okay. So now we could see we've got our silver dollar pancakes, we've got our Philly steak and cheese. Let's make this smaller. And then at the bottom, we're going to add our price. Don't forget the item dot description. Then we also need a price. Alright. So item dot price. Let's just see what that looks like. We can add a dollar sign. We've got our silver dollar pancakes. Cool. Maybe we wanna give a little bit of gap between all these. This is not gonna be a Nuxt link either, right. We want this to be a button type equals button. We'll add some functionality into this in a moment. Class equals text left. Okay. Maybe we give it a flex call gap 2, Give a little bit of spacing in there. And make the width full. Well that, yeah, there we go. That gets us what we want. Let's make the gap larger. Okay. Cool. So we got some menu items. Right? We want to click on these menu items and add them into a cart. So what are we gonna do, how are we gonna manage this cart? Because it could be we want to maintain this cart across like page load and also across different restaurants or different pages as well. So for now, I think I'm just going to simply use a composable for this. I don't have Pina installed, which is a really great state library for Vue. You know, Nuxt has a built in used state composable that you could potentially use as well. But I like the idea of just adding a composable to manage this. So I'm gonna add a new folder here in the root directory. I'm gonna call it composable Composables. Nuxt will actually auto import these. And let's call it use cart dot ts. Alright. We're going to export a default function, use cart. And up here at the top, let's just take a look at our cart structure. Right. What do we have? What do we want to send? So we've got the orders. If we take a look at our data model, right, we have the store, we have the customer, we have that's gonna be our individual user. So we've got to log in as a user. We got our items. We got a total for that. Okay. So let's go in and add a list of items. That's gonna be an array of items. Right. What else are we going to have inside this composable? Right. We've got our items. We're pulling this outside of the function definition because we want to store this. If we call this use cart in multiple places, we still wanna access the same items. Alright. So this is gonna return the items for that cart. We're gonna add those cart the order total. Alright. So we have the order total that's going to be computed. That would be a return items dot for dues dot item dot price. That's perfect. Thank you, Github. Got an order total. And then we've probably got a function for, oh, let's go back up, async function, place, yeah. Let's add to cart. We're just gonna push an item to the cart. We gotta remove from cart. Look at GitHub Copilot. Again, like, please take this with a grain of salt, whether this is actually wise or not. Index of item, items value dot splice. Index self item. Okay. And then if we've got, and these actually probably don't need to be async functions. And then we probably got an async function for submit order. Alright. Submit order. Alright. This is gonna be constant data equals I don't know if that's gonna be data or not. We'll do await. We'll do use directus. Then we are going to create an item in the orders table or orders collection, the total for that order total, ordertotal will be the order total dot value. Then we have our items, which are going to be, should just be items. Alright. So we could do something like, nope. We're not gonna do that. We are going to do unref items since that is unrefed. We've got the store info and this customer field, inside direct us, I can save the current user on create. So let's do that. So the store is going to be where are we gonna get the store from? Maybe we're gonna pass that in the function. Store ID, that's a string. Store ID. Alright. Is this cool? We're gonna return these other functions from our composable. Let's see how we do. Alright. So now if we go back to our app, we've got the hamburger house. Let's just remove this, comment that out. Alright. So we got our menu, We want to push these items into a cart. Where are we at time wise? We got 10 minutes left. Are we gonna actually get these into a cart or not? So here inside this specific, page we might do what we are going to return. Let's take a look at our composable. Right. We've got our order items. We've got our items in the cart. Let's call the we're gonna get items. Let's call these cart items. Add to cart. Remove from cart equals used cart. K. And then can we hey. Let's just drop in our cart items here just to see what that looks like. Cart items. It's It's not showing anything for our card items. Are we getting an error? Let's actually take a look. So we'll go to ID. Cart items equals undefined. Oh, it's because I'm getting items. There we go. Alright. So we're showing just an array. Let's just do this. I'm just gonna give this some padding. BG gray, 50. Let's make it 100. This is our, p tag cart items. Okay. Alright. So now what we want to do on each one of these, right, if we click an add to cart button, which we we don't necessarily have. Where is that? Right? Why do we not have a button for add to cart? Maybe we don't even put that. That shouldn't even be in here basically. So let's move this up. We'll just take the class. We'll apply that to the card itself. Why is it not doing what we want? Gapspacey2. Why are you not doing what I want you to do? Alright. I'm not gonna stress over it. We'll just wrap it in a div because this will be the div task adventure. I know all the non tailwind CSS guys are probably freaking out at this moment, but okay. We'll use this u button component provided by Nuxt UI and we're going to click add to cart. We're going to add that item to the cart. Yeah. Close enough. Right. So if I click add to cart, we can see we've got our cart item here. It's not necessarily what I want. Right? The data structure is not entirely what I want. So let's actually just define it here. The item ID, we want this to be our order items. Alright. So we go into order items, We got the menu item. Let's do that. Menu item. Okay. Special instructions. No onions, please. Yeah. We'll just automatically populate that. The quantity, let's set that to 1. And then the item subtotal, we'll just say item price. Alright. So now if I refresh, does that give us kind of the structure that we want? It's still populating the entire menu item. We just want the menu item item.id. That should give us what we need. Okay. Does that match the right structure? Great. Where we at on time? We got, 5 minutes or so. Let's actually get this order together. Right? Alright. So inside our header, maybe we could display our cart items there. I really wanted to actually show the submitting to Directus. So knowing that I am short on time, right, we could go in and add these items. Let's just try submitting it right now and see. Alright. So we got our cart items. Let's go in. We've got new button, place order, and we'll call this submit order. Are we passing we're passing that store ID as well. Alright. So this isn't probably the best particular type of design, but it will get the job done. Let's do a large button. At click is submit order and then we've got the store dot ID that we're passing to this. And then let's go in and just like what does our submit order look like? This should be probably like try catch. So we could catch any type of errors. Response. If we catch an error, we want to return the response. Great. Submit order. Oh, that looks a little rough. Let's move that over here. Oh, I put I did I totally put that in the wrong spot anyway. Submit order and let's just hide this if the user is not logged in as well. Right? We don't want them to be able to place an order if you are not logged in. So this composable that I have, I want to say, has a or like this module that I set up has some of this already built in. So this will be v Where's the directus auth? Directus auth, user. So we'll do something like this where we get constant user equals use direct us off if there is a user. Okay. Alright. And if there's not a user, let's do a u button. V of no user, we are going to I think I could do this with it, where we have login to place an order. Login to order. And we will do what is it? 2 auth, I don't even need that. We do to auth\u002Fauth\u002Flogin. Login to order. Okay. We've got our user. I didn't set up a user, but let's create one now. This will be our user. Test usertest@example.com. Let's give them a real secure password. We'll do password here and then I am going to select the role of user. Great. Save it. Now I should actually be able to log in. So test at example dot com, Password of that login. Tried to log in to the wrong spot. Where's the redirect for this? Use direct us off. Is this going to be in my Nuxt config? We want to redirect to auth login, not portal. We want to redirect to the index page. Oh, we're gonna we're gonna be cutting this one really really close. Come on. Alright. So we go back. Do we have we've got our menus. We don't have any cart orders. It looks like I'm still am I still logged in? Do we have the cookie? We've got okay. So we've got a session token here, so it's still showing me as logged in. If we hit add menu items, I hit place order, what happens is we can see the order posted. If we go inside Directus, do we have that specific order? Yes, we do. So we've got here's the store information, here's the order, we've got the item subtotal. We don't have the prices not being populated. So I didn't item subtotal should be item dot price. Why is that not populating correctly? I didn't add the price either. But item dot price. Cool. I'm calling this a win. So we've got what have we done? We've set up our data model within an hour. We've got all the items coming through. I could go in and clean this up, certainly, and that would be my next steps. But we've got a really simple UI here on the front end that with probably another 20, 30 minutes and a little bit of love. Man, this thing would be amazing. So I can go in, if I refresh this page, we should probably ditch that, but I can go in and add my pancakes. I can place the order and see it show up inside Directus. Alright. So we just do a quick review. What did we achieve? Right. We got the data model done, check. We got a list of the menu items for a restaurant, check. And then we got the place an order for menu items. Boom. Knock all 3 of those off. I'm gonna credit that to my wife being here to help me today. That's it for this episode, guys. I hope you've enjoyed this one. If you did, make sure you hit the subscribe button on Directus TV because we want you to catch more of this content. See you.",[4897],"48bbe7ad-29b8-4ffd-aa87-c6924e0a75e4",[],{"year":2380,"number":177,"id":4162,"episodes":4900,"show":4901},[4164,4165,4166,4167,4168,4169,4170,4171,4172,4173,4137],{"title":322,"slug":323},{"id":4612,"slug":4903,"vimeo_id":4904,"description":4905,"tile":4906,"length":740,"resources":12,"people":4907,"episode_number":195,"published":4909,"title":4910,"video_transcript_html":4911,"video_transcript_text":4912,"content":12,"seo":12,"status":19,"episode_people":4913,"recommendations":4915,"season":4916},"netlify","899795774","Increase visibility of build status by showing them in Directus.","ac4ea98a-a6ac-40c7-8c36-89c41992f27d",[4908],{"name":2820,"url":2821},"2024-02-02","Show Build Status with Netlify","\u003Cp>Speaker 0: Netlify is a platform that lets you build and deploy web applications. So it will take code, it will run any build scripts that are required, and then it will deploy the output of that build script. Now this episode of quick connect is all about increasing visibility into a specific deploy, so people know whether it has been successful or not directly from within directors. So people who author content or author updates to content can see when that content has been successfully deployed or perhaps when it hasn't because there's been a problem, and they know that there's some intervention needed. So let's get started.\u003C\u002Fp>\u003Cp>The first thing we're going to do is head over to flows, and we're going to create a new flow. I'll call this deploy updates, And we're gonna make this a webhook trigger with a post method. Once we create this flow, we are now given a URL. Whenever this URL receives a post request, the flow will begin. So now we're gonna head over to Netlify, and I've set up this little project here.\u003C\u002Fp>\u003Cp>Very minimal, but it does have a build step, which is important. And we're going to create a new deploy notification, which is an HTTP post request. And in here, we're gonna put the URL that we have just generated from our flow. Now when it comes to which event to listen for, we are going to pick deploy started. So this URL will receive an update whenever a deploy has started.\u003C\u002Fp>\u003Cp>We'll hit save. While we're here, we're also going to immediately add a second post request for when a deploy has succeeded, and we're gonna send that to exactly the same URL. We're just gonna do when they've started and succeeded today, but in reality, you'll want to set up a third one for when something has not succeeded, when there's been an error. This is actually all we need to do right here, right now. Let's head over to Directus.\u003C\u002Fp>\u003Cp>Actually, no. Let's not head over to Directus. Let's trigger a deploy and see what happens. So let's deploy our website. And what Netlify is now doing is it's going to grab the code.\u003C\u002Fp>\u003Cp>It's going to grab all of the dependencies, then it's going to execute the build script that I have provided, which should take almost no time whatsoever again because it's a very minimal site here. There we go. They'll all fill in, and now the site is live. So let's refresh our flow here. And I'm not sure what you might be expecting, but there are actually 2 invocations of this flow.\u003C\u002Fp>\u003Cp>The first one, if we look at the payload, has some interesting information. Firstly, it has a build ID. So this is for any given deploy, this build ID will stay the same. It has the name of the website, which is also useful because one Netlify account, could be hosting multiple websites, and you may want this flow for multiple sites. For example, at Directus, we have a website, and we have the dots, and they are both deployed by Netlify.\u003C\u002Fp>\u003Cp>And we may wanna reuse this flow to share the logic. We also have, what else is interesting here? The state. So at the beginning, a state is always building. That is the first step pair after initialization.\u003C\u002Fp>\u003Cp>Now if we look at the second invocation, it looks almost identical, except the state is now ready because the build succeeded. The state could also not be ready. It could indicate there's an error, should there be 1. But, otherwise, it's the same. The build ID remains the same.\u003C\u002Fp>\u003Cp>The name of the site stays the same. So first thing we're gonna do here is actually first thing we're gonna do is determine whether or not this is a brand new item or whether this is an update for an existing deploy. So what we'll do is we'll create a condition. I will put in this little default here, except I'm gonna change the values. So it's going to be Bobby.\u003C\u002Fp>\u003Cp>Inside of the body, there was a state, and we want to check whether that state is equal to, oops, building. Oh, building. Like so. If it is equal to building, that means this is a brand new entry. Otherwise, it means it's an update, and either there's been a success or a failure.\u003C\u002Fp>\u003Cp>Now in the case that this is successful, we want to store this new deploy in directors. So let's set up a collection for this. I'll call it deploys, and I'm going to make the primary key build underscore ID and make that a manually entered string. There are loads of values we can add here. I think what's useful for now is date created and date updated.\u003C\u002Fp>\u003Cp>Going into the data model, we want to add a state. You could, of course, chain covers as displayed. I'm just gonna make it a text input for now to keep it easy. The name of the website itself. And I think that's all we need, the build ID, the name of the site, and the state.\u003C\u002Fp>\u003Cp>Fantastic. So now let's go back to flows. And let's say when this is a brand new item, I e, building is true, we're going to create a new item in the deploys collection. We'll give it full access, and we're going to save 3 keys. Firstly, the build ID, which is required, is going to be equal to trigger dot body dot build underscore ID.\u003C\u002Fp>\u003Cp>Next was the state, and finally, the site. The order doesn't really matter here they'll all get saved in the correct fields. Great. I'm gonna hit save. I'm just gonna double check.\u003C\u002Fp>\u003Cp>Was it called site? No. It's called name. So, I will just go and edit that. I had a had a feeling it was wrong.\u003C\u002Fp>\u003Cp>So that's name, and the value is actually also name in the payload. So let's hit save, and let's retrigger a deploy. We'll just have to wait a moment for that deploy to start and then complete. And what we should see is a new item is created in the collection when it is first when the build first starts, And nothing should currently happen when we get the updated, when we get the update saying that it has been successful. So we see successful successful.\u003C\u002Fp>\u003Cp>Refresh here, and we should see 4 logs. Correct. The first one has been created in the collection. We see there. Quick connect demo building, and there's the build ID in Netlify.\u003C\u002Fp>\u003Cp>Now the latest one has a validation error because it was false, which means nothing then happened, which is fantastic. The last thing to really do here is on the reject route of the condition is we're gonna update data. We know it already exists because the first, the first payload we get for each deploy creates the item. Now we just get to update it, which is nice. So what we're gonna do here is we're going to update trigger dot Bobby dot build ID because we know that that is the unique identifier for the item in the collection.\u003C\u002Fp>\u003Cp>And all we're gonna do here is update state. Oops. So that's going to be, the trigger dot body dot state, which could be success already, or it could be something else. So let's hit save. Let's save again.\u003C\u002Fp>\u003Cp>Let's retrigger that deploy one more time. And you can imagine that this deploy isn't triggered manually. It's triggered because a user has updated something indirect or for some other reason, there is a a rebuild of the application that's taking place. So it's going ahead. It's building it.\u003C\u002Fp>\u003Cp>It's going through all those steps. If we look over here, we now see building. If I refresh again, still building. If I refresh again, still building. What's happened there?\u003C\u002Fp>\u003Cp>Let's take a look. Oh, you don't have permission to access this. This had incorrect permissions. This wants to be full access. Fantastic.\u003C\u002Fp>\u003Cp>So let's go, and we will now trigger that deploy one more time just to validate everything is set up correctly. So we should expect this to take 10 ish seconds, 10, 15 seconds like it has in the past. Basically, the moment it hits that build step is pretty instant just because of this specific applications. But builds can take, you know, 10 minutes sometimes, if necessary. So now if we look at this collection, we see that it's ready.\u003C\u002Fp>\u003Cp>So that's the 3rd deploy. We did one at the very start. We did one just now to test it, and that was a problem. And now that we see it's ready. So that is a little bit of, of information on how you can connect Netlify.\u003C\u002Fp>\u003Cp>We can also see date created, date updated. You know, we can sort by date created, date updated, whatever. And then, you can use this to find the deploy that's just happened and, share that with your team. Hope you found this in this episode of quick connect interesting. This general setup will work across hosting platforms.\u003C\u002Fp>\u003Cp>The Netlify is the one we use, so it's the one that I wanted to show off. Until next time, have a fantastic day, and I'll see you in the next episode.\u003C\u002Fp>","Netlify is a platform that lets you build and deploy web applications. So it will take code, it will run any build scripts that are required, and then it will deploy the output of that build script. Now this episode of quick connect is all about increasing visibility into a specific deploy, so people know whether it has been successful or not directly from within directors. So people who author content or author updates to content can see when that content has been successfully deployed or perhaps when it hasn't because there's been a problem, and they know that there's some intervention needed. So let's get started. The first thing we're going to do is head over to flows, and we're going to create a new flow. I'll call this deploy updates, And we're gonna make this a webhook trigger with a post method. Once we create this flow, we are now given a URL. Whenever this URL receives a post request, the flow will begin. So now we're gonna head over to Netlify, and I've set up this little project here. Very minimal, but it does have a build step, which is important. And we're going to create a new deploy notification, which is an HTTP post request. And in here, we're gonna put the URL that we have just generated from our flow. Now when it comes to which event to listen for, we are going to pick deploy started. So this URL will receive an update whenever a deploy has started. We'll hit save. While we're here, we're also going to immediately add a second post request for when a deploy has succeeded, and we're gonna send that to exactly the same URL. We're just gonna do when they've started and succeeded today, but in reality, you'll want to set up a third one for when something has not succeeded, when there's been an error. This is actually all we need to do right here, right now. Let's head over to Directus. Actually, no. Let's not head over to Directus. Let's trigger a deploy and see what happens. So let's deploy our website. And what Netlify is now doing is it's going to grab the code. It's going to grab all of the dependencies, then it's going to execute the build script that I have provided, which should take almost no time whatsoever again because it's a very minimal site here. There we go. They'll all fill in, and now the site is live. So let's refresh our flow here. And I'm not sure what you might be expecting, but there are actually 2 invocations of this flow. The first one, if we look at the payload, has some interesting information. Firstly, it has a build ID. So this is for any given deploy, this build ID will stay the same. It has the name of the website, which is also useful because one Netlify account, could be hosting multiple websites, and you may want this flow for multiple sites. For example, at Directus, we have a website, and we have the dots, and they are both deployed by Netlify. And we may wanna reuse this flow to share the logic. We also have, what else is interesting here? The state. So at the beginning, a state is always building. That is the first step pair after initialization. Now if we look at the second invocation, it looks almost identical, except the state is now ready because the build succeeded. The state could also not be ready. It could indicate there's an error, should there be 1. But, otherwise, it's the same. The build ID remains the same. The name of the site stays the same. So first thing we're gonna do here is actually first thing we're gonna do is determine whether or not this is a brand new item or whether this is an update for an existing deploy. So what we'll do is we'll create a condition. I will put in this little default here, except I'm gonna change the values. So it's going to be Bobby. Inside of the body, there was a state, and we want to check whether that state is equal to, oops, building. Oh, building. Like so. If it is equal to building, that means this is a brand new entry. Otherwise, it means it's an update, and either there's been a success or a failure. Now in the case that this is successful, we want to store this new deploy in directors. So let's set up a collection for this. I'll call it deploys, and I'm going to make the primary key build underscore ID and make that a manually entered string. There are loads of values we can add here. I think what's useful for now is date created and date updated. Going into the data model, we want to add a state. You could, of course, chain covers as displayed. I'm just gonna make it a text input for now to keep it easy. The name of the website itself. And I think that's all we need, the build ID, the name of the site, and the state. Fantastic. So now let's go back to flows. And let's say when this is a brand new item, I e, building is true, we're going to create a new item in the deploys collection. We'll give it full access, and we're going to save 3 keys. Firstly, the build ID, which is required, is going to be equal to trigger dot body dot build underscore ID. Next was the state, and finally, the site. The order doesn't really matter here they'll all get saved in the correct fields. Great. I'm gonna hit save. I'm just gonna double check. Was it called site? No. It's called name. So, I will just go and edit that. I had a had a feeling it was wrong. So that's name, and the value is actually also name in the payload. So let's hit save, and let's retrigger a deploy. We'll just have to wait a moment for that deploy to start and then complete. And what we should see is a new item is created in the collection when it is first when the build first starts, And nothing should currently happen when we get the updated, when we get the update saying that it has been successful. So we see successful successful. Refresh here, and we should see 4 logs. Correct. The first one has been created in the collection. We see there. Quick connect demo building, and there's the build ID in Netlify. Now the latest one has a validation error because it was false, which means nothing then happened, which is fantastic. The last thing to really do here is on the reject route of the condition is we're gonna update data. We know it already exists because the first, the first payload we get for each deploy creates the item. Now we just get to update it, which is nice. So what we're gonna do here is we're going to update trigger dot Bobby dot build ID because we know that that is the unique identifier for the item in the collection. And all we're gonna do here is update state. Oops. So that's going to be, the trigger dot body dot state, which could be success already, or it could be something else. So let's hit save. Let's save again. Let's retrigger that deploy one more time. And you can imagine that this deploy isn't triggered manually. It's triggered because a user has updated something indirect or for some other reason, there is a a rebuild of the application that's taking place. So it's going ahead. It's building it. It's going through all those steps. If we look over here, we now see building. If I refresh again, still building. If I refresh again, still building. What's happened there? Let's take a look. Oh, you don't have permission to access this. This had incorrect permissions. This wants to be full access. Fantastic. So let's go, and we will now trigger that deploy one more time just to validate everything is set up correctly. So we should expect this to take 10 ish seconds, 10, 15 seconds like it has in the past. Basically, the moment it hits that build step is pretty instant just because of this specific applications. But builds can take, you know, 10 minutes sometimes, if necessary. So now if we look at this collection, we see that it's ready. So that's the 3rd deploy. We did one at the very start. We did one just now to test it, and that was a problem. And now that we see it's ready. So that is a little bit of, of information on how you can connect Netlify. We can also see date created, date updated. You know, we can sort by date created, date updated, whatever. And then, you can use this to find the deploy that's just happened and, share that with your team. Hope you found this in this episode of quick connect interesting. This general setup will work across hosting platforms. The Netlify is the one we use, so it's the one that I wanted to show off. Until next time, have a fantastic day, and I'll see you in the next episode.",[4914],"3585c63c-5d15-4bd8-a0d6-78c536d31dc7",[],{"year":2380,"number":194,"id":4605,"episodes":4917,"show":4918},[4607,4608,4609,4610,4611,4612,4613,4590],{"title":1746,"slug":1747},{"id":1790,"slug":4920,"vimeo_id":4921,"description":4922,"tile":4923,"length":1060,"resources":4924,"people":4927,"episode_number":177,"published":4931,"title":4932,"video_transcript_html":4933,"video_transcript_text":4934,"content":12,"seo":12,"status":19,"episode_people":4935,"recommendations":4939,"season":4940},"config-as-code","906788449","In this recording of our live event on January 25 2024, Rijk, Jonathan, and Daniel discuss configuration as code.","bd5024fb-4ef7-455a-8ff6-8631da26b5d2",[4925],{"name":4496,"url":4926},"https:\u002F\u002Fgithub.com\u002Fdirectus\u002Fdirectus\u002Fdiscussions\u002F13041",[4928,4929,4930],{"name":3795,"url":3796},{"name":3798,"url":3799},{"name":3801,"url":3802},"2024-02-01","Configuration as Code","\u003Cp>Speaker 0: Welcome everybody once more to a wonderful request review session here where we go over feature requests and figure it out. Now what do we do? I'm afraid we ramble on for about an hour about the technical complexities. Remember, the goal here is to basically divergently discuss, you know, what is the feature request, what are we trying to do, what is it trying to achieve, And how do we think we can make it happen in a very sort of direct to see way? What are we talking about this week?\u003C\u002Fp>\u003Cp>Speaker 1: Yes. We're talking about configuration.\u003C\u002Fp>\u003Cp>Speaker 0: Configuration as code. Let's let's figure out how to take schema endpoints to the max and actually Schema endpoints to the max. Manage the entire project as code. So this is really with a focus on GitOps. Right?\u003C\u002Fp>\u003Cp>Where you have a sort of centralized repository of static files that is the single source of truth for all configuration of the running project. Which as you might guess, they get complicated fairly quick. Hello. And as per usual, we'll be eyeing the chat. So if you have any questions in between or any suggestions or any good thoughts, please do please do put it in the chat.\u003C\u002Fp>\u003Cp>I already saw his name fly by. Well, most likely I have a very special guest for you today, because our very own Connor has been researching, you know, some of this for a little while now. But before we dive into the research results there, let's discuss a little bit of the requirements that are presented here in the current feature requests. Right? Because the one thing we know now, you know, the current state of affairs, we have that schema snapshot and apply endpoint, that we use and sort of recommend for, you know, moving bits of schema, to and from dev to prod, that sort of thing.\u003C\u002Fp>\u003Cp>But as people have pointed out, you know, that is still for schema only. Right? So we know one of the big requirements for this is gonna be you need to figure out additional configuration, additional additional, data points maybe from your own tables, you know, environment migrations, like you mentioned there, which includes, you know, what about roles? What about flows? What about presets?\u003C\u002Fp>\u003Cp>What about translation strings, etcetera? This well, one of the complexities for this is figuring out, you know, what is configuration within the context of directives in the first place. Right? Which is a discussion topic that I have had some trouble with just going through myself already, which is what is configuration? You know, are your roles and the way you configured permission configured, you know, permissions configuration, probably.\u003C\u002Fp>\u003Cp>But the users within those roles, probably not. But then users with static tokens, maybe. Right? If you have your own tables, maybe you have a single, you know, app settings, singleton collection that you use for configuration is that now configuration that is part of code first configuration. Right?\u003C\u002Fp>\u003Cp>Even though it's not a system table and you're not configuring directives, you might still be configuring other things. Although, that's where the fun starts. So maybe we could scroll down a little bit, Jonathan, just take a quick peek through, the other the motivation and the requirements here. So, you know, as we kinda touched on already, the same here from, Erif van Oort. Pretty sure that would be a Dutch user.\u003C\u002Fp>\u003Cp>It's it's about things like permission logic, you know, keep the local dev environment in sync, source control is the source of truth. Right? You wanna make sure that you can spin up new Directus instances not completely empty, but start it from, you know, a template that is in your repo. If there's an issue, you can easily, you know, share, the configuration of your platform. Daniel, if you would kindly mute, you're being very annoying.\u003C\u002Fp>\u003Cp>From the replying system, this is immediately where where it gets complicated. Right? It's like, what is configuration? Right? What is configuration?\u003C\u002Fp>\u003Cp>When it comes to import export, how to define what gets imported, what gets exported? Basically, the same question to me. Right? How does it get imported? You know, are you merging stuff?\u003C\u002Fp>\u003Cp>Are you overwriting stuff? What happens if you try to insert something that already exists? You know? How do you deal with conflicts? Very good question.\u003C\u002Fp>\u003Cp>So if you wanna scroll down a little bit further to see what else is in here.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. No. I don't know about that point. That's a good question, but a very, very long one to answer properly. But the gist is, you know, if you work with multiple people with different setups and if somebody changes your database schema, for example, how do you synchronize the state between your instance and another instance?\u003C\u002Fp>\u003Cp>You can do that with our schema endpoint. You can we already have that, capability. But, technically, you would want to or ideally let's say ideally, you would want to set up your configuration as code, because then you have a single source of truth. If you're developing a new feature, for example, you need a new table, you need new fields, you want to test something, you wanna try something, but then you, you know, delete some fields, How do you get the changes synchronized between different setups? And even the problem gets even larger if you have an organization, for example, with, let's say, I don't know, one dev department of, like, 8 people, for example.\u003C\u002Fp>\u003Cp>Stuff gets really gnarly really quick. How do you synchronize then between 8 people, for example, between different branches, different features, different collections, different fields? You know.\u003C\u002Fp>\u003Cp>Speaker 0: Let alone a test team of 200. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And this this is, you know, for a very small team, it can get quite gnarly pretty quickly. But, you know Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: There's a couple other things there too. Right? When it comes to the git repo flow specifically is that any change to the schema of the project is now sort of, like, version controlled, so you know what happened when and you can roll back. And you have accountability because you know who made the change, through that sort of git first approach. Right?\u003C\u002Fp>\u003Cp>The other main thing there too, I think, is from a database template, you don't have files, which is one thing we'll touch on in a and the second thing is it's database vendor specific. That's another thing. Right? Like, you could plop the whole SQLite file in a repo, but, you know, if you wanna move if you have a local dev instance that uses SQLite and you wanna go push your change into production in Postgres, now you have a workflow trouble. Right?\u003C\u002Fp>\u003Cp>Even if you have local Postgres, the server Postgres, you might go, you know, I don't know, Postgres 10 to 13 or something. If there's a version mismatch, you know, there's there's things to to consider there. Of course, there's third party tools. I see Ansible mentioned here that you can use to sort of move databases across, sort of thing. This would really be sort of direct as native way to move configuration around.\u003C\u002Fp>\u003Cp>Right? Which I personally see it as an improvement or an upgrade to the schema snapshot system that we have rather than a completely new thing. Just the real question just becomes, you know, how do we add more stuff into that so you can use it for this? That's that's really the, to me, the the underlying discussion. Right.\u003C\u002Fp>\u003Cp>Jonathan, if you wanna scroll down a little bit further, you can see if there's any other points. Wanna make sure we don't forget. Export considerations, multiple files. I think that's a very important requirement because we've already seen some of the schema snapshots just get bonkers large. Right?\u003C\u002Fp>\u003Cp>Because if you have a 1,000 collections, a total of 25 100 fields sounds insane, but it happens in the wild. The one export file, you know, is is megabytes and megabytes and megabytes worth of of JSON. Tens if not 100, which gets unwieldy pretty quick. It also makes it more difficult to import, by the way, because we're not really able to stream it all that well and then it becomes a very large file. So you have to read it into memory and then and then use it.\u003C\u002Fp>\u003Cp>Let's see. Selective export. That, I think, is a tricky one. Right? How do you know what you're exporting if you, consider your roles and permissions part of this, but you have one admin dev role that you don't care about for your production instance, How do you pick and choose?\u003C\u002Fp>\u003Cp>Right? Pick and choose what to what to include, what what not to include, and handling, you know, sensitive data. Very good point. You know, is this gonna be plain text in a static file? Tricky.\u003C\u002Fp>\u003Cp>Right? Tricky in a repo. If you scroll down a little bit further, The modular files of extensions, single file per collection, you know, we kinda test.\u003C\u002Fp>\u003Cp>Speaker 2: Does\u003C\u002Fp>\u003Cp>Speaker 0: does it actually make more sense to have selective import versus export? Great question. Great great question. Maybe. Maybe.\u003C\u002Fp>\u003Cp>Yeah. It's it's like if you have Go.\u003C\u002Fp>\u003Cp>Speaker 2: I'm sorry. Go ahead.\u003C\u002Fp>\u003Cp>Speaker 1: I I don't have to remember to mute and unmute myself between every sentence. That's fine. Yeah. I can see both being very useful. Right?\u003C\u002Fp>\u003Cp>For example, if if you have a very, very large, instance with, like, Greg mentioned, right, like, a 1,000 collections, And on your dev instance, you only want to add one thing, do you really need to export, like, this whole thing that's, like, I don't know, 10 megabytes or whatever? Maybe, you know, maybe, it would be enough to just export that table with its fields, and you'd be good to go, because then you could import that partial instance, maybe. But, yeah, for for import or for export, both could be useful. But, yeah, like we said, it's just lots lots and lots of stuff to talk about there. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: TBD is is the honest answer. I I also feel like both is probably where we need to end up with that. Because to your point, if you have a large project and you only care about a small subset of that as a sort of templatable piece, you know, you don't want to export everything and have a bunch of unneeded data in your repo muddying up, you know, the workflow and the reviews. Because then also imagine that you make an export and then now you have a PR of, like, 16,000 lines of stuff that you don't really need. Right?\u003C\u002Fp>\u003Cp>But, yeah, let's see. Extend, you know, existing schema files. That's an interesting one. Merging multiple together, importing snippets from other files, maybe, you know, from nested collections. So that's all about, you know, the the file structure for the project.\u003C\u002Fp>\u003Cp>Saving the non defaults, I think that is more of a technical requirement to me. Right? It's like we don't have to save default values from Directus in the schema snapshot because they're the default values. Dynamic configuration sync, it's just whenever you make a change in the studio, it auto exports basically, which feels heavy, personally. Feels a bit heavy, but could could potentially work depending on the file format.\u003C\u002Fp>\u003Cp>But then again, how do you choose what to export on the automated one? Right? So TBD. That's also why it's a could have. I mean, they've they've thought about it luckily.\u003C\u002Fp>\u003Cp>Automatic real time sync, sort of similar idea. Right? But an option in the Data Studio API triggered chrono periodically high. So the one thing I do notice in the requirements list here is that there's a lot of talk about how to get it out of Directus and in what file format, but not so much the other way around. Right?\u003C\u002Fp>\u003Cp>How do you get it back in? So if you have something in your repo, whatever that something is, what does that code look like and how do you get that back into the Directus instance? Right? This might be a good point actually, a nice little segue. Like I hinted at at the beginning, our very own Connor has been doing quite a lot of homework on this just to figure out, you know, the, the format and some of the ideas around this and how it could work.\u003C\u002Fp>\u003Cp>So if let me see if I can find him. Where is this little so many here. Look at that. Hello, Connor. What have\u003C\u002Fp>\u003Cp>Speaker 2: you been what have\u003C\u002Fp>\u003Cp>Speaker 0: you been up to recently?\u003C\u002Fp>\u003Cp>Speaker 2: I have been up to quite a bit involving this config as code and how it plays into all the other different parts of direct disc that we wanna do. Let me get my notes up. Here we go. So you said you wanted me to talk about the structure of the exports?\u003C\u002Fp>\u003Cp>Speaker 0: I think it'd be cool if you wanna give a quick overview of sort of the the research process itself. Like, what are the things that you've been looking into? What have been the considerations or requirements? And sort of the things that you found. And then dive into some initial conclusions.\u003C\u002Fp>\u003Cp>Speaker 2: Sure. So what I have been going through and researching is basically we have a couple of different feature requests from config as code to templates to migrating between instances to migrating between different databases. And all of it sort of involves, you know, moving configuration between instances, moving data between instances, and moving files and assets between instances. And that is a very big task when you're trying to be database agnostic and you're trying to be efficient. You're trying to have you're trying to support multiple different use cases where sometimes you wanna overwrite everything, sometimes you just wanna bring in some stuff, sometimes you only wanna take out some stuff.\u003C\u002Fp>\u003Cp>Sometimes you wanna stream it all. Sometimes you wanna have the file small. So there's a lot of very different considerations that go into it, and then making it all happen with one sort of directest way of making it all magical. It becomes a very big rabbit hole that you start diving down into. And so one of the stuff one of the things that we've that I have been looking at is, you know, what are all the different use cases for it, and what are the requirements for all those different use cases?\u003C\u002Fp>\u003Cp>And so configuration as code is one of the use cases on there. It's not completely fleshed out yet because that was not on it's one of the later goals of what I've been working on. But with it brings, you know, how do you integrate it with CICD, you know, GitHub, GitLab. You know, do you have your own hosted GitHub, You know, self hosted GitHub. You know?\u003C\u002Fp>\u003Cp>Where do all your stuff is stored? And so there's a whole bunch of different parts of it. Right now with the schema service, we go and we give you you export a schema of your stuff. It exports everything. You diff it against your instance, and then you apply that diff, and it gives you, like, the changes if you can do it or not.\u003C\u002Fp>\u003Cp>And right now, that's really it. There's not really too much to the schema service outside of that right now, and adding in all these different layers and features. The schema service is definitely gonna have to take a new look to it. And so one of the things that we've been looking at is, you know, that initial export of a schema, you know, making it more of a distributable type of folder structure file structure, whether it's a compressed zip file or some type of other special file. But basically redefining how that schema export looks to be able to hold all these different configuration items, to be able to hold data, to be able to hold assets, and defining that structure and, you know, you know, as Ryke mentioned earlier, you know, do you want that stuff stored in plain text or do you want it to be stored in, you know, some type of encrypted format or do you want it to be compressed?\u003C\u002Fp>\u003Cp>So there's a lot of different variables there. And then once you take that distributable that gets made, it can be I mean, for some instances, if you've pulled out data and assets and configuration, you know, that thing could be huge. And so we wanna go we wanna bring that into the this new instance of the target instance. Right? And so we need to different and change it.\u003C\u002Fp>\u003Cp>And so bringing all that in and processing in is a whole another thing. You know, do you wanna bring in all of it? You know, you have all the export controls. Do you wanna have import controls to how it gets implied and, you know, how it gets imported? And so I've been going through and documenting all those different ways that we can do stuff, you know, what is dependent, you know, if we wanna do this, then, you know, we have to do that, you know.\u003C\u002Fp>\u003Cp>And so we've been looking at one of the things this week, you know, is what type of file format for all of this type of stuff, for how it gets really big. You know, if it gets a lot of data, if somebody has a 1,000 collections and 4,000 fields, you know, is a CSV file, a JSON file really the right file structure to store all of that data? And so we've been looking at, you know, different options and different file formats for storing, you know, structured data like that in an efficient compressed way that also lets you keep the schema of the schema export defined and structured in a way. And then also making sure that we keep that same right now, we hash the schemas and stuff so that they all stay. You know?\u003C\u002Fp>\u003Cp>You could only use the schema to apply to this instance because you just did it with it and yada yada. So having that in there too, you know, do we have a metadata file inside of that export that, you know, talks about what the export is? You know, do we have it? Do these become an extension type, you know, that can be used throughout the instance in different places? You know, there's a whole bunch of different options there.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Absolutely. It\u003C\u002Fp>\u003Cp>Speaker 1: was a great intro. Yeah. It was a very good yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Exactly. Exactly. Yeah. So the first order of business to your point, you know, figuring out what does that file format look like. We know some of the requirements now based on this discussion that we just looked at.\u003C\u002Fp>\u003Cp>We know some of the the downsides of the current format. So that's a great step. Then, of course, the second big step will be figuring out, you know, how do you go from that sort of source of truth overview into applying it for realsies. Right? So we have that sort of diff step in between.\u003C\u002Fp>\u003Cp>So for those unaware right now, if you upload a schema snapshot into the Directus API, it will compare it to the current state of the database and then return, you you know, the the the list of differences, basically. So it's a diff, not a list of changes. As in a step step by step list, it's just a diff, like an a a versus b. And then that diff is then uploaded to an apply endpoint, which will basically, you know, apply the changes required to get rid of the diff, right, to make sure that the that the 2 are in sync, that the instance is in sync with your file export. So based on that, Connor, we've done some research on what needs to happen on that diff endpoint itself too.\u003C\u002Fp>\u003Cp>You wanna you wanna share some insights on what we know now, at least, are some of the requirements to make that work properly with all of these new new additional features that we're trying to add in.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. So with that dipping endpoint, some of the things that we are looking at is, number 1, if you wanna bring in data. Right? You know, how do you diff large amounts of data? Are you able to diff large amounts of data?\u003C\u002Fp>\u003Cp>That's one of the research things that's on the list. You know? Right now, we have an import and export service to import and export data. Looking at Attica drive run options. You know?\u003C\u002Fp>\u003Cp>Can you import this data? Can you export this data, for that diffing stuff? You know, if you have a really big file, so you do have 300,000 collections and fields, you know, that's gonna take a long time to make changes to the database and to go through and find that diff. And so having some type of long task runner on the instance that's able to sit there and work through that to that, diff or making that diff or distributable or whatever it is. You know, having such a long running service of the background of your instance, I can handle that.\u003C\u002Fp>\u003Cp>And then also if you're going through and you're applying all these big changes or diffing it or whatever, you don't want people in your instance changing the stuff as you're trying to change stuff. So implementing some sort of maintenance mode on your instance that basically locks it down and puts it, hey. We're making changes right now. You know, you can't it doesn't let anyone else change the schema or anything or the data or whatever you want it to do. We also, have been looking at, you know, for asset data, you know, pulling in you know, do you pull it in from the distributable file, or do you pull it in from the data the asset source?\u003C\u002Fp>\u003Cp>You know, do you pull it in from the s three bucket directly? You know, do you use it like that, or do you package it into the distributable? Or, but, basically, for the diffing part, the other part is that if you have a really, really big distributable or schema thing, whatever it ends up being called, you also upload downloading it from one instance, uploading it to another instance just to download another big thing, just to upload the other big thing back again is a lot of moving back and forth of all this different stuff. And so the other thing is when you upload that schema, whatever, it diffs it. Instead of it downloading the diff back to you, then you having to send it back up, it being able to just keep the diff on the instance and you just being able to tell it to apply the storage diff that it already has.\u003C\u002Fp>\u003Cp>And so you don't have to have that all that network changing back and forth, and then, you know, Internet goes out, then you're screwed. You know? But that's one of the things that we've also been looking at for the diffing. And then another thing is, you know, different types of strategies of diffing and importing. So that, you know, do you just wanna up cert stuff?\u003C\u002Fp>\u003Cp>Do you just wanna add new things and you wanna ignore everything else that has conflicts? Or do you want it to only apply if there are no conflicts, you know, or do you want it to overwrite everything, you know, so it doesn't matter if there's conflict. We're gonna rewrite over it with everything, you know. And then instead of just returning a singular diff that just compares the 2 different schemas and it just says, hey this is what's different. You know putting in more migration like making it more of a step type thing.\u003C\u002Fp>\u003Cp>So it works through migration steps. And, you know, oh, you need to do this. You need to do this. You need to do this. You need to do this.\u003C\u002Fp>\u003Cp>And basically a workflow that the thing can work through and and guide those long running task runners on what to do and how to configure your instance. And I\u003C\u002Fp>\u003Cp>Speaker 0: think last but not least, the having some sort of format to expose potential conflicts for manual resolution. Right? So if one of the strategies has to be that it's up to the end user to pick and choose what to do. So imagine if you go, you know, from a dev to prod, life cycle, right, where you're not so much delete everything and insert everything, you wouldn't wanna do that in a prod obviously, And if you have an absurd strategy, but there is a conflict, right, you you have, like, a foreign key that doesn't work anymore or something like that. There needs to be some sort of format in whatever this diff looks like or this migration step format looks like that just has, a list of Here's the steps with the known conflict What do you wanna do?\u003C\u002Fp>\u003Cp>Right? How do you wanna modify that that step, those steps to get around the conflict? Right? Do you wanna upload, you wanna upload new data, or do you wanna, ignore that particular step, or do you wanna ignore those records? You know?\u003C\u002Fp>\u003Cp>So to your point, if we need some sort of driver and to check if you can import all of the data, it's sort of a requirement in order to be able to extract, you know, potential conflicts. So we need to have some sort of way to search through the data you're trying to apply, in order to know how to deal with conflicts. Right? So now that we're talking about all of this, what we're what we started to notice is that we're not so much talking about, you know, configuration as code specifically or templating specifically. What we're basically shaping here is a system that works for multiple things, right?\u003C\u002Fp>\u003Cp>Depending on how you use it. So if you were to make, a snapshot of everything, just full stop everything, and you import it as as apply everything, what you're talking about now is basically backup restore. Right? If you're exporting a small fragment and you're importing that into another project, you're basically talking about templating. Right?\u003C\u002Fp>\u003Cp>If you're exporting just the schema part and no data and you apply that to a new project, you're you're talking about seeding or something. You know what I mean? Like preparing preparing, a database basically, a a new project for what you wanted to do. And the question now is, how does that how does that all tie together how does that tie back into the configuration as code parts specifically, Connor? Because what we're talking about now is, you know, a new sort of format, generated by Directus that you can save somewhere, you know, which is fairly still, it's still fairly proprietary because it will have to be heavily compressed and, you know, directors needs to know what the format is.\u003C\u002Fp>\u003Cp>So what is the current thinking on tying it back into the code side of this question? Right?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. So if we went the route of having some sort of distributable file structure folder structure that is some proprietary format or is encrypted or compressed or whatever, you know, you're not gonna be able to sit there and write code that is a compressed file. You know, you're gonna have to have write something that generates that file. So one thought that we've been having is following the lead of some other types of, you know, companies like AWS and their SDKs. So, basically, having some type of SDK that you can write and configure your instance with, and then you tell the CLI or whatever to execute that, read those different set the code that you've written, and then it will make a direct distributable file, diff file, whatever it is, from the code that you've read.\u003C\u002Fp>\u003Cp>So if you wanna go through and you wanna define all of your collections and your fields or whatever, and you can go in and define that in all your files and your code and then execute that code, it comes up. It generates that file that you can then use to apply those changes, import those changes, diff those changes to any of your target instances that you want to.\u003C\u002Fp>\u003Cp>Speaker 0: Jonathan, if you might be wanna pull up, I think one one piece of inspiration that we were looking at for that part specifically was AWS's, what do we call it? CDK, I think, code development kit. If you wanna quickly Google that, it could be it could prove like, it could put some flavor to that to that point. So the way AWS has that, they basically made a JavaScript library that you can use to code, like, configuration. And then what it does under the hood is it effectively converts it into, a CloudFormation template, I wanna say, and then applies it immediately.\u003C\u002Fp>\u003Cp>Right? So under the hoot, you don't really notice the difference, but it's effectively a 1, 2 jump. Right? So it converts it into their proprietary thing in the middle first and then just applies that as is. What am I searching for?\u003C\u002Fp>\u003Cp>Sorry. CDK, the cloud development kit. If you wanna pull up the GitHub repo for that, maybe I have a link somewhere. I'm just curious if they have some some examples somewhere. It's been a minute since I've played with this, but it's an interesting, idea.\u003C\u002Fp>\u003Cp>There was a Directus community library, a little while back that that tried doing a similar thing, but it would run it against the API endpoints. It wasn't as flexible yet because we didn't have we don't have, you know, GreenStep. Is this branch? You know. If you wanna do AWS before that, because I think CD case is all different.\u003C\u002Fp>\u003Cp>Yeah. There we go. There's a link in the chat as Open it up. Here we go. Here we go.\u003C\u002Fp>\u003Cp>Here we go. Is it gonna go? There it\u003C\u002Fp>\u003Cp>Speaker 1: goes.\u003C\u002Fp>\u003Cp>Speaker 0: So this is an interesting reference for people that wanna look it up. At home, it's basically, you know what was that? Distracted by the chat immediately. Using something like CDK would mean that changes would need to be replicated from the UI to the generation scripts. Changes would need to be replicated.\u003C\u002Fp>\u003Cp>That's a great point. Yeah. How does that go both ways? Right? Because if you have that one format in the middle, directors can recreate that format in the middle.\u003C\u002Fp>\u003Cp>The directors wouldn't be able to recreate arbitrary JavaScript, basically. Right? So when you opt into something like that, I think it becomes a one way street by definition because we cannot figure out what parts of your JS file are, or your code because they also have some other languages, but you get the idea. And we don't know what parts of that file are auto generatable and what parts are, human created. Right?\u003C\u002Fp>\u003Cp>There's so there's no way to auto generate that back into a manually created file. So at that point, that's a great point, but it's the it really becomes, you know, it becomes a a one way street at that point. Some of the data community did a sort of proof of concept library to do this for, which is very interesting. So if you wanna pull up the direct to community schema builder kit repo, I just sent a link in the chat there. It was very much inspired by a similar idea where you have a JavaScript file that you use to sort of define.\u003C\u002Fp>\u003Cp>It's almost, you know, a declaration file rather than, you know, JavaScript, but it is still just JavaScript that runs from top to bottom. But you could define your schema and how it's applied as, you know, individual build steps, in a JavaScript. So this is where it gets real heavy on the code part of the codeless configuration. Right? And not so much just the, moving stuff around.\u003C\u002Fp>\u003Cp>So in in terms of big picture stuff, I really see this as the final step of whatever these changes are that we're discussing. We'll have to start with what is that new format in the middle, how is it generated, how is it used, and then see this as a way to sort of generate it into that format and then apply it automatically. Right? But, yeah, that that JavaScript syntax is an interesting interesting idea. So, yeah, I see some folk typing.\u003C\u002Fp>\u003Cp>This is one of those very typical director's projects where there is about 600,000 different opinions on the ideal way of doing this. And I think we we saw it in the chat immediately. Right? Shout out to a person that was like, isn't it not just the database template? Why why bother?\u003C\u002Fp>\u003Cp>Right? Which I can totally get behind that, but then there's a 180 or so of votes or something like that on the on the discussion. So apparently, you know, that is not an opinion that's shared.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. What what's interesting is, right, because, technically, the most basic example would be something like a database migration, generally, in the beginning, you know, for the configuration as code, for example. So there's another, a similar project director, the CMS like project that I've checked out and see how they did it. And, so they handled this a little bit differently. They don't have, like, a DSL type, you know, language that defines your infrastructure or whatever.\u003C\u002Fp>\u003Cp>But, they went the route of as soon as a person or user, via the UI creates some type of change in the tables or the collections or however you want to call it, the instance automatically generates a migration file locally for that specific change. And, there's then a mode in the instance where you can disable any, any ability for other users to change the actual instance. So you can actually just rely on the migration files, which is an approach that you could take, you know, because a migration file then could technically do anything you'd like, you know, with with regards to, you know, the collections, the fields, whatever, even inserting, items. But, then, you know, because we're a directors, we want to that's a little too easy for us because we would like to include some type of things like, alright. How about you, locally, you develop some type of new feature, new new table, new collection, new fields.\u003C\u002Fp>\u003Cp>And then in order for that to work in the way that you want it to work, you need an item. You know? You need to include a new new row, data row, or an asset. This is the thing now. Because, you know, assets are not inside of the database.\u003C\u002Fp>\u003Cp>So we want to include assets, for example, or maybe this, of course. Nothing is, you know, set in stone, but, you know, including assets, for example. So you want to make some changes, and you need to include some assets for your changes to be even, you know, useful. So you would then have to, you know, do your changes, test it locally, include everything with the correct file name, with the correct row, whatever, or other metadata of an asset, for example. But then on production, you would have to replicate that again.\u003C\u002Fp>\u003Cp>So you get this back to back to step 1. Right? So this is kinda it kinda sucks, with a migration part. So even then, if we want to include this, then we get back to the issue at hand that we were talking about. Right?\u003C\u002Fp>\u003Cp>We want to have a process that could export something, and you can recreate that between instances and so forth and so forth. I would, I just wanted to mention that for the others in the chat because, it's not just about, you know, just adding a field because that's basically, you know, that's a basic thing, which we could solve. And I think the Director Schema Builder kit is basically that. Right? You you generate some type of syntax, which generates some type of migration.\u003C\u002Fp>\u003Cp>But you have to keep in mind then, of course, right, different database vendors, we have to abstract that. Because, for example, you know, in SQLite, if I remember correctly please correct me. If I remember correctly, like, you can't alter a table and introduce, like, a foreign key. You're forced to drop the table, actually, and recreate it in order to add a foreign key, for example. Other databases can do that as CLI can.\u003C\u002Fp>\u003Cp>So there's lots of different\u003C\u002Fp>\u003Cp>Speaker 0: honest there, I'm pretty too sure that the last minor release of SQLite didn't come out too long ago. They finally do have that alter table sort of baked in. Although then, you, of course, you have the you have the side effects that it depends on your native build of SQLite on your machine, which may or may not have had. So generally speaking, historically, you've been absolutely right. It's been a nightmare and a half to to do that.\u003C\u002Fp>\u003Cp>Speaker 1: Lots of fun. So I just wanted to make sure that, people in chat, you know, it's it's not about just adding a thing. It's it's a little more involved than then. And, you know, including assets and, like, the other sent. Right?\u003C\u002Fp>\u003Cp>Maybe there's proprietary information or whatever, and you're not allowed to leave it on your hard drive. Maybe you want to zip it, encrypt it, compress it. There's lots and lots and lots of different steps that we have to, kick off there. So alright. Oh, we got some in the chat interaction.\u003C\u002Fp>\u003Cp>Cool. Cool. Cool.\u003C\u002Fp>\u003Cp>Speaker 0: So alright. Top to bottom. First question. What speaks against using JSON or YAML files? Instead of JavaScript, this way the changes in the webpack could also be synced back to the files easily.\u003C\u002Fp>\u003Cp>So for what it's worth, the the, formats that we're talking about being generated from directives would most likely be, you know, in some sort of structured format. Not quite sure if that's JSON or YAML yet or if we have to find some sort of optimized, file format to do that. Because the the the risk of JSON and YAML exports, once you start including data, you know, we no longer know how much data you wanna include. Like if we're treating this as you could use this for backup restore we could talk about a large large amount of data right at which point we need to have a very optimized structural format that may or may not be usable in that point. Right?\u003C\u002Fp>\u003Cp>Connor, remind me, we found like an Apache file format that could be interesting for this. I think what was it called? Parquet or something. Right?\u003C\u002Fp>\u003Cp>Speaker 2: It was called Parquet.\u003C\u002Fp>\u003Cp>Speaker 0: Parquet. Yeah. That would be an interesting file format for something like that. Or potentially using a SQLite database as as the exported file. Like, that's a completely different, direction.\u003C\u002Fp>\u003Cp>But But you get the idea. We need to have some sort of optimized compressed file format because the export file could get really large. Now, it might be an option for the way, you know, you save one of those to just save it in a sort of raw mode. Right? Where it doesn't save it compressed, at which point it's just it could be human readable YAML or or JSON, including, you know, the ability to properly, source control it.\u003C\u002Fp>\u003Cp>On the migration note, I think you answered that before, Daniel, like, exactly right. If you're doing auto generated migrations, it's really only for the database schema part. Like, we can't really know on your behalf if you consider, insights dashboards part of configuration or flows or something. Right? So it's it's gonna be it's gonna be tricky because people different people have different export requirements.\u003C\u002Fp>\u003Cp>And if you go from dev to prod, all bets are off. Right? You you never quite know what the the idea is. Creating internal libraries fiber to schema works with native access to directives rather than the API using integrations and help creating a lot of complex repetitive action. I can imagine.\u003C\u002Fp>\u003Cp>Yeah. Because you can write a little JavaScript for loop and just block. You have 10,000 collections. Right? But, you know, the lack of two way integration between the UI does cause issues which is the unfortunate side effect of using, you know, a programming language rather than a declarative language like yaml or JSON, for doing schema modifications like that.\u003C\u002Fp>\u003Cp>You're gonna lose that two way integration. That being said, you know, if Directus has a don't allow me to change the schema environment variable flag, whatever, you could make that, you could do that on purpose. Right? For a production instance, for example, I can totally imagine that you disable any sort of schema modifications just for security reasons, and and availability reasons and only allow those changes to happen through whatever system we're we're cooking up here. Right?\u003C\u002Fp>\u003Cp>I think default value filtering could help make the YAML auth more manageable. Fully agree. You know, we should only store the stuff that we need to know, and storing default values feels like a waste of waste of space. Then Azure is working on some sort of YAML based metadata authoring, announcing Azure data delivery net auth sales. What marketing email?\u003C\u002Fp>\u003Cp>Very curious. Haven't I haven't heard of that one before. If you wanna keep the GitOps thing, it should really be a text format. Good point from Tim. Which may or may not be answered from by Dominic here.\u003C\u002Fp>\u003Cp>If you split up between schema and content in different formats. Right? Maybe the configuration piece is all human readable file formats. But if you have a data export that is maybe there's a file size threshold. Right?\u003C\u002Fp>\u003Cp>If you have a very large CSV export maybe there's just a smart point where it's like, oh, you're trying to save 10,000 rows We're gonna flip it automatically into a compressed, non readable format so you get the best of both worlds. Right? Potentially. Cool. Alright.\u003C\u002Fp>\u003Cp>Cool. We tried.\u003C\u002Fp>\u003Cp>Speaker 1: I it's something similar. Yeah. Just to, you know, chime in to regards to what you just said. So with the, like, yeah, like, yeah, we have to split that up preferably, you know, or at least, you know, required to have this as text based so you can, you know, use it in version control, whatever. And, for, like, including items, for example, you know, a ZIP is is a nice thing that you could use, but then, of course, you know, maybe this includes then items that are not, like like an old version, for example, and, you want to insert something in in where a field doesn't exist anymore, and and lots and lots and lots of other, you know, stuff.\u003C\u002Fp>\u003Cp>And, you can then, of course, if we then have all of the different points that we want to persist, right, like flows, permissions, the general config as it is, you could include this then also in the export with the items, so you can do both at the same time or see if it differs and then cancel the thing. But, yeah, it's, it's a fun thing. You know? There's lots and lots of things that could go wrong. There's so many.\u003C\u002Fp>\u003Cp>Speaker 0: No no matter what, we wanna make sure that the output file is a single distributable. Right? We do wanna make sure because we on the one hand, we're saying we have to split it up into multiple files in order to make it efficient and easy to work with, But, at the same time, we also wanna make sure that you have a singular thing, singular file that you can send over, to somebody else. Right? Either through the API, so you just have a single download or a single upload, or as a file, maybe packaged through, you know, the marketplace.\u003C\u002Fp>\u003Cp>Shout out. Wink wink. Notch notch. Or as, just to email it to somebody for a like air. Right?\u003C\u002Fp>\u003Cp>Put it in a GitHub issue as a zip. So there needs to be some sort of both. Right? But I could also imagine that, you know, the API, lets you download it as just a zip, right, that you can just double click to open if you're on macOS or do whatever else it takes on other platforms, to unzip it. Looking at you, Daniel, I'm sure there's a 2 step process.\u003C\u002Fp>\u003Cp>For those who, out out of the loop, he's, this is the year of Linux on the desktop, evangelist within the team.\u003C\u002Fp>\u003Cp>Speaker 1: Yes. This year. Here it is. This is the year. Mark my words.\u003C\u002Fp>\u003Cp>And this year is the year of the Linux desktop. This year. Yes.\u003C\u002Fp>\u003Cp>Speaker 0: But long story short, we it's it's we're we're in that weird in between that we need both. Right? We need both the single file and multiple files. So we'll most likely have to come out with some sort of zip, gzip, something like that in between. Cool.\u003C\u002Fp>\u003Cp>Prisma migrations are an interesting way of doing things. They have a custom format, which is more concise than directed YAML, and then some CLI tools that create actual SQL migrations in the syncs the environment. Yeah. Great example. Right.\u003C\u002Fp>\u003Cp>Good example. They have they basically do with that shadow shadow database, if I'm not mistaken, that's how they keep track of those migrations step by step by step. Yep. Right? And then with the the CLI tool, it can compare your custom migration format with what they already have tracked so far.\u003C\u002Fp>\u003Cp>And then, you know, apply apply the diff based on that. Still has the similar I think a similar one way issue, though. Right? Because the Prisma migrations that you write manually, I don't think they can sort of update those from the others from the other side, so to speak, that a two way binding. Good point, though.\u003C\u002Fp>\u003Cp>Good point. Alright.\u003C\u002Fp>\u003Cp>Speaker 1: Could remember. I had a problem with in the past. So, sadly, I can't remember it right now, but it was very painful. So, it's not a 100%. Yeah.\u003C\u002Fp>\u003Cp>It's not a 100 perfect thing. Nice. I've seen people you know, there's there's lots of other, what is it called? Drizzle drizzle right now?\u003C\u002Fp>\u003Cp>Speaker 0: Oh, there's a couple of RMS like that. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: So there's there's lots of sauces that we could, you know, yoink some code from, but be inspired. Let's say be inspired.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I don't think we've been yoinked. I don't think we've legitimately ever yoinked code before.\u003C\u002Fp>\u003Cp>Speaker 1: Borrow. Borrow. See.\u003C\u002Fp>\u003Cp>Speaker 0: Strategies is interesting there. But the the the main difference and then you you touched on it perfectly before. The main difference there is the just keeping track of the database schema versus having that ability to just sort of manual template in between moving stuff between dev and prod that is not just schema, but also data and how to deal with that, which at any point needs to be, something you can do manually. Right? It doesn't have to be manual all the time, but it needs to be something you can do manually Right?\u003C\u002Fp>\u003Cp>Cool. Well, with all that being said, looking at the clock here Connor, back to you. We've discussed quite a lot of the research up until this point, some of the requirements. You and I have also sort of been daydreaming about potential ways to implement this moving forwards. You wanna quickly touch on sort of the different, different phases and different sort of parts that we wanna touch as part of this bigger bigger effort?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. So the first thing we wanna do is we wanna figure out how this is gonna look, the distributable, the diffing. What is that file structure? What's the file format? You know, what are the requirements for that?\u003C\u002Fp>\u003Cp>Do we have encryption? Do we have multiple files? Do we have one file? Do we have whatever? Like, we've already discussed here.\u003C\u002Fp>\u003Cp>First thing is defining what that'll look like in totality, covering all use cases so that as we progress through the the phases of this project and making it happen, we can keep that spec that we have for those files in mind, and we can make sure that we cover everything whenever we could work through the different phases. But the that is phase 0 to finding out that spec, figuring it out what it looks like. Phase 2 or phase 1 would be upgrading the schema service foundationally right now. So making sure we have all the bug fixes with the schema service taken care of, you know, adding the new different, you know, different strategies that to it that might be needed. You know?\u003C\u002Fp>\u003Cp>Do we have, you know, add in those exports, export filters, you know, only export this stuff, you know, that stuff. So getting that schema service, adding some more features to it. And then the next phase is working on data importing. And so adding in the different features that would be needed to make the data importing work, You know, dry running, imported data, importing strategies. You know, do we have a import strategy where right now everything just gets up sorted?\u003C\u002Fp>\u003Cp>Do we wanna have an import strategy where if you import the data, it drops all the current date data in the table and re imports everything fresh? You know, adding those different options. Some other just things, ideas that have been thrown in the mix is, you know, if you're moving between one instance to another instance and you don't wanna bring any of your IDs, your primary keys, your foreign keys, you know, adding some type of way to anonymize that as it goes from one instance to another. So the new instance makes new IDs for everything. Or adding, you know, the ability to for, like, templates, you know, if I wanna bring in a template for project management and have a due date in one of the items, but I made the template a year ago, then the due date's gonna be from a year ago.\u003C\u002Fp>\u003Cp>So, you know, being able to have dynamic data that gets brought in on import. And so, you know, oh, I want you to import this as a date, but make it for in 2 hours after the import is when it's set. And then the next phase after that, working on data importing is getting into putting it all together. So we've worked on the spec for the thing. We worked on the schema service.\u003C\u002Fp>\u003Cp>We worked on data importing. Now we need to bring it all together into this this new overhaul configuration, multimodal thing, and have it work on bring all those pieces together and make it work, basically. And then after that, once we've gotten the specs spec'd out, the schema service upgraded, the data importing upgraded, we brought it all together, and it now works foundationally. Now we need to figure out how do we use this to implement these different use cases? So for templating, you know, what do we build inside of the data studio admin app from a user interface and API perspective to make templating work?\u003C\u002Fp>\u003Cp>What do we do from the configuration as code to use the stuff that we've already built? So how do we implement the feature set that we want in the configuration as code use case. You know, are there any other use cases? You know, back up being and restoring an instance. Do we have the feature set that we wanna use for that?\u003C\u002Fp>\u003Cp>And then that should, in theory, wrap up the project after that, after we figure out and implement those use cases. So there's quite a few steps here. Quite a lot of different items and it's going to take a long time. But at the end, we should have something pretty cool.\u003C\u002Fp>\u003Cp>Speaker 1: Ideally. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: And I think I think Daniel's facial expressions told told the whole story. No. But that'll I mean, it'll make sense to me. It sounds it sounds like a large overwhelming amount of stuff as it usually does. But by breaking it up, you know, step by step like this, we actually have a pretty solid idea, you know, start to finish of of what is involved to getting that across the finish line.\u003C\u002Fp>\u003Cp>And, yeah, as per usual, you know, there's a lot of, what's the word? Direct this magic going on under the hood to to tie it all together. But I'm always just very excited and glad that we're able to sort of re envision this as we have one underlying core foundational engine that can power all of those different use cases. Right? Rather than trying to tack on a new sort of templating piece and a new sort of code first configuration piece and a new sort of other piece.\u003C\u002Fp>\u003Cp>Right? That just increases the tech that makes it hard to maintain. It makes it those different flavors incompatible. You know, somebody will ask, how do we do a template as code? And we're like, well, you can't because code is not for templating.\u003C\u002Fp>\u003Cp>You know, that kind of stuff. It really reminds me of how we built flows. Right? That we had the hook extension first. And if you configure a new flow, you're effectively just building together a hook extension, and it's all the exact same underlying, underlying logic and event based system, which is also why, you know, flows you can do it say the same stuff as you can in a hook.\u003C\u002Fp>\u003Cp>Right? Well, same events. I mean, you can, of course, in a hook, you can code it yourself. So I'm very glad that we're making this, you know, a foundational upgrade to the schema snapshotting engine rather than trying to make it yet a new thing cool cool cool well that all being said I see we're at time here. Let me just quickly briefly peek at the chat.\u003C\u002Fp>\u003Cp>Do we missed anything? Pascal mentioned dynamic collection of field names will be cool. EG importing third party templates so you can choose your own target names. Good point. I think a similar goes for the conflict resolution piece that we talked about.\u003C\u002Fp>\u003Cp>Right? If you're trying to import won't be complex. Very, very, very true. But rest assured, it will be complex, I'm sure. Cool.\u003C\u002Fp>\u003Cp>With all that being said, this will be, life on Drax. Io\u002Ftv\u002Frequestreview in about a week week's time. Also, last episode, you can find there too. Should be if you're watching this on DirectTV, it's probably somewhere over there, there, there, or down there. Is this the point where we say, like, and subscribe?\u003C\u002Fp>\u003Cp>No. We don't have that yet. It's exactly we thank you all for joining.\u003C\u002Fp>","Welcome everybody once more to a wonderful request review session here where we go over feature requests and figure it out. Now what do we do? I'm afraid we ramble on for about an hour about the technical complexities. Remember, the goal here is to basically divergently discuss, you know, what is the feature request, what are we trying to do, what is it trying to achieve, And how do we think we can make it happen in a very sort of direct to see way? What are we talking about this week? Yes. We're talking about configuration. Configuration as code. Let's let's figure out how to take schema endpoints to the max and actually Schema endpoints to the max. Manage the entire project as code. So this is really with a focus on GitOps. Right? Where you have a sort of centralized repository of static files that is the single source of truth for all configuration of the running project. Which as you might guess, they get complicated fairly quick. Hello. And as per usual, we'll be eyeing the chat. So if you have any questions in between or any suggestions or any good thoughts, please do please do put it in the chat. I already saw his name fly by. Well, most likely I have a very special guest for you today, because our very own Connor has been researching, you know, some of this for a little while now. But before we dive into the research results there, let's discuss a little bit of the requirements that are presented here in the current feature requests. Right? Because the one thing we know now, you know, the current state of affairs, we have that schema snapshot and apply endpoint, that we use and sort of recommend for, you know, moving bits of schema, to and from dev to prod, that sort of thing. But as people have pointed out, you know, that is still for schema only. Right? So we know one of the big requirements for this is gonna be you need to figure out additional configuration, additional additional, data points maybe from your own tables, you know, environment migrations, like you mentioned there, which includes, you know, what about roles? What about flows? What about presets? What about translation strings, etcetera? This well, one of the complexities for this is figuring out, you know, what is configuration within the context of directives in the first place. Right? Which is a discussion topic that I have had some trouble with just going through myself already, which is what is configuration? You know, are your roles and the way you configured permission configured, you know, permissions configuration, probably. But the users within those roles, probably not. But then users with static tokens, maybe. Right? If you have your own tables, maybe you have a single, you know, app settings, singleton collection that you use for configuration is that now configuration that is part of code first configuration. Right? Even though it's not a system table and you're not configuring directives, you might still be configuring other things. Although, that's where the fun starts. So maybe we could scroll down a little bit, Jonathan, just take a quick peek through, the other the motivation and the requirements here. So, you know, as we kinda touched on already, the same here from, Erif van Oort. Pretty sure that would be a Dutch user. It's it's about things like permission logic, you know, keep the local dev environment in sync, source control is the source of truth. Right? You wanna make sure that you can spin up new Directus instances not completely empty, but start it from, you know, a template that is in your repo. If there's an issue, you can easily, you know, share, the configuration of your platform. Daniel, if you would kindly mute, you're being very annoying. From the replying system, this is immediately where where it gets complicated. Right? It's like, what is configuration? Right? What is configuration? When it comes to import export, how to define what gets imported, what gets exported? Basically, the same question to me. Right? How does it get imported? You know, are you merging stuff? Are you overwriting stuff? What happens if you try to insert something that already exists? You know? How do you deal with conflicts? Very good question. So if you wanna scroll down a little bit further to see what else is in here. Yeah. No. I don't know about that point. That's a good question, but a very, very long one to answer properly. But the gist is, you know, if you work with multiple people with different setups and if somebody changes your database schema, for example, how do you synchronize the state between your instance and another instance? You can do that with our schema endpoint. You can we already have that, capability. But, technically, you would want to or ideally let's say ideally, you would want to set up your configuration as code, because then you have a single source of truth. If you're developing a new feature, for example, you need a new table, you need new fields, you want to test something, you wanna try something, but then you, you know, delete some fields, How do you get the changes synchronized between different setups? And even the problem gets even larger if you have an organization, for example, with, let's say, I don't know, one dev department of, like, 8 people, for example. Stuff gets really gnarly really quick. How do you synchronize then between 8 people, for example, between different branches, different features, different collections, different fields? You know. Let alone a test team of 200. Right? Yeah. And this this is, you know, for a very small team, it can get quite gnarly pretty quickly. But, you know Yeah. There's a couple other things there too. Right? When it comes to the git repo flow specifically is that any change to the schema of the project is now sort of, like, version controlled, so you know what happened when and you can roll back. And you have accountability because you know who made the change, through that sort of git first approach. Right? The other main thing there too, I think, is from a database template, you don't have files, which is one thing we'll touch on in a and the second thing is it's database vendor specific. That's another thing. Right? Like, you could plop the whole SQLite file in a repo, but, you know, if you wanna move if you have a local dev instance that uses SQLite and you wanna go push your change into production in Postgres, now you have a workflow trouble. Right? Even if you have local Postgres, the server Postgres, you might go, you know, I don't know, Postgres 10 to 13 or something. If there's a version mismatch, you know, there's there's things to to consider there. Of course, there's third party tools. I see Ansible mentioned here that you can use to sort of move databases across, sort of thing. This would really be sort of direct as native way to move configuration around. Right? Which I personally see it as an improvement or an upgrade to the schema snapshot system that we have rather than a completely new thing. Just the real question just becomes, you know, how do we add more stuff into that so you can use it for this? That's that's really the, to me, the the underlying discussion. Right. Jonathan, if you wanna scroll down a little bit further, you can see if there's any other points. Wanna make sure we don't forget. Export considerations, multiple files. I think that's a very important requirement because we've already seen some of the schema snapshots just get bonkers large. Right? Because if you have a 1,000 collections, a total of 25 100 fields sounds insane, but it happens in the wild. The one export file, you know, is is megabytes and megabytes and megabytes worth of of JSON. Tens if not 100, which gets unwieldy pretty quick. It also makes it more difficult to import, by the way, because we're not really able to stream it all that well and then it becomes a very large file. So you have to read it into memory and then and then use it. Let's see. Selective export. That, I think, is a tricky one. Right? How do you know what you're exporting if you, consider your roles and permissions part of this, but you have one admin dev role that you don't care about for your production instance, How do you pick and choose? Right? Pick and choose what to what to include, what what not to include, and handling, you know, sensitive data. Very good point. You know, is this gonna be plain text in a static file? Tricky. Right? Tricky in a repo. If you scroll down a little bit further, The modular files of extensions, single file per collection, you know, we kinda test. Does does it actually make more sense to have selective import versus export? Great question. Great great question. Maybe. Maybe. Yeah. It's it's like if you have Go. I'm sorry. Go ahead. I I don't have to remember to mute and unmute myself between every sentence. That's fine. Yeah. I can see both being very useful. Right? For example, if if you have a very, very large, instance with, like, Greg mentioned, right, like, a 1,000 collections, And on your dev instance, you only want to add one thing, do you really need to export, like, this whole thing that's, like, I don't know, 10 megabytes or whatever? Maybe, you know, maybe, it would be enough to just export that table with its fields, and you'd be good to go, because then you could import that partial instance, maybe. But, yeah, for for import or for export, both could be useful. But, yeah, like we said, it's just lots lots and lots of stuff to talk about there. Yeah. TBD is is the honest answer. I I also feel like both is probably where we need to end up with that. Because to your point, if you have a large project and you only care about a small subset of that as a sort of templatable piece, you know, you don't want to export everything and have a bunch of unneeded data in your repo muddying up, you know, the workflow and the reviews. Because then also imagine that you make an export and then now you have a PR of, like, 16,000 lines of stuff that you don't really need. Right? But, yeah, let's see. Extend, you know, existing schema files. That's an interesting one. Merging multiple together, importing snippets from other files, maybe, you know, from nested collections. So that's all about, you know, the the file structure for the project. Saving the non defaults, I think that is more of a technical requirement to me. Right? It's like we don't have to save default values from Directus in the schema snapshot because they're the default values. Dynamic configuration sync, it's just whenever you make a change in the studio, it auto exports basically, which feels heavy, personally. Feels a bit heavy, but could could potentially work depending on the file format. But then again, how do you choose what to export on the automated one? Right? So TBD. That's also why it's a could have. I mean, they've they've thought about it luckily. Automatic real time sync, sort of similar idea. Right? But an option in the Data Studio API triggered chrono periodically high. So the one thing I do notice in the requirements list here is that there's a lot of talk about how to get it out of Directus and in what file format, but not so much the other way around. Right? How do you get it back in? So if you have something in your repo, whatever that something is, what does that code look like and how do you get that back into the Directus instance? Right? This might be a good point actually, a nice little segue. Like I hinted at at the beginning, our very own Connor has been doing quite a lot of homework on this just to figure out, you know, the, the format and some of the ideas around this and how it could work. So if let me see if I can find him. Where is this little so many here. Look at that. Hello, Connor. What have you been what have you been up to recently? I have been up to quite a bit involving this config as code and how it plays into all the other different parts of direct disc that we wanna do. Let me get my notes up. Here we go. So you said you wanted me to talk about the structure of the exports? I think it'd be cool if you wanna give a quick overview of sort of the the research process itself. Like, what are the things that you've been looking into? What have been the considerations or requirements? And sort of the things that you found. And then dive into some initial conclusions. Sure. So what I have been going through and researching is basically we have a couple of different feature requests from config as code to templates to migrating between instances to migrating between different databases. And all of it sort of involves, you know, moving configuration between instances, moving data between instances, and moving files and assets between instances. And that is a very big task when you're trying to be database agnostic and you're trying to be efficient. You're trying to have you're trying to support multiple different use cases where sometimes you wanna overwrite everything, sometimes you just wanna bring in some stuff, sometimes you only wanna take out some stuff. Sometimes you wanna stream it all. Sometimes you wanna have the file small. So there's a lot of very different considerations that go into it, and then making it all happen with one sort of directest way of making it all magical. It becomes a very big rabbit hole that you start diving down into. And so one of the stuff one of the things that we've that I have been looking at is, you know, what are all the different use cases for it, and what are the requirements for all those different use cases? And so configuration as code is one of the use cases on there. It's not completely fleshed out yet because that was not on it's one of the later goals of what I've been working on. But with it brings, you know, how do you integrate it with CICD, you know, GitHub, GitLab. You know, do you have your own hosted GitHub, You know, self hosted GitHub. You know? Where do all your stuff is stored? And so there's a whole bunch of different parts of it. Right now with the schema service, we go and we give you you export a schema of your stuff. It exports everything. You diff it against your instance, and then you apply that diff, and it gives you, like, the changes if you can do it or not. And right now, that's really it. There's not really too much to the schema service outside of that right now, and adding in all these different layers and features. The schema service is definitely gonna have to take a new look to it. And so one of the things that we've been looking at is, you know, that initial export of a schema, you know, making it more of a distributable type of folder structure file structure, whether it's a compressed zip file or some type of other special file. But basically redefining how that schema export looks to be able to hold all these different configuration items, to be able to hold data, to be able to hold assets, and defining that structure and, you know, you know, as Ryke mentioned earlier, you know, do you want that stuff stored in plain text or do you want it to be stored in, you know, some type of encrypted format or do you want it to be compressed? So there's a lot of different variables there. And then once you take that distributable that gets made, it can be I mean, for some instances, if you've pulled out data and assets and configuration, you know, that thing could be huge. And so we wanna go we wanna bring that into the this new instance of the target instance. Right? And so we need to different and change it. And so bringing all that in and processing in is a whole another thing. You know, do you wanna bring in all of it? You know, you have all the export controls. Do you wanna have import controls to how it gets implied and, you know, how it gets imported? And so I've been going through and documenting all those different ways that we can do stuff, you know, what is dependent, you know, if we wanna do this, then, you know, we have to do that, you know. And so we've been looking at one of the things this week, you know, is what type of file format for all of this type of stuff, for how it gets really big. You know, if it gets a lot of data, if somebody has a 1,000 collections and 4,000 fields, you know, is a CSV file, a JSON file really the right file structure to store all of that data? And so we've been looking at, you know, different options and different file formats for storing, you know, structured data like that in an efficient compressed way that also lets you keep the schema of the schema export defined and structured in a way. And then also making sure that we keep that same right now, we hash the schemas and stuff so that they all stay. You know? You could only use the schema to apply to this instance because you just did it with it and yada yada. So having that in there too, you know, do we have a metadata file inside of that export that, you know, talks about what the export is? You know, do we have it? Do these become an extension type, you know, that can be used throughout the instance in different places? You know, there's a whole bunch of different options there. Yeah. Yeah. Absolutely. It was a great intro. Yeah. It was a very good yeah. Exactly. Exactly. Yeah. So the first order of business to your point, you know, figuring out what does that file format look like. We know some of the requirements now based on this discussion that we just looked at. We know some of the the downsides of the current format. So that's a great step. Then, of course, the second big step will be figuring out, you know, how do you go from that sort of source of truth overview into applying it for realsies. Right? So we have that sort of diff step in between. So for those unaware right now, if you upload a schema snapshot into the Directus API, it will compare it to the current state of the database and then return, you you know, the the the list of differences, basically. So it's a diff, not a list of changes. As in a step step by step list, it's just a diff, like an a a versus b. And then that diff is then uploaded to an apply endpoint, which will basically, you know, apply the changes required to get rid of the diff, right, to make sure that the that the 2 are in sync, that the instance is in sync with your file export. So based on that, Connor, we've done some research on what needs to happen on that diff endpoint itself too. You wanna you wanna share some insights on what we know now, at least, are some of the requirements to make that work properly with all of these new new additional features that we're trying to add in. Yeah. So with that dipping endpoint, some of the things that we are looking at is, number 1, if you wanna bring in data. Right? You know, how do you diff large amounts of data? Are you able to diff large amounts of data? That's one of the research things that's on the list. You know? Right now, we have an import and export service to import and export data. Looking at Attica drive run options. You know? Can you import this data? Can you export this data, for that diffing stuff? You know, if you have a really big file, so you do have 300,000 collections and fields, you know, that's gonna take a long time to make changes to the database and to go through and find that diff. And so having some type of long task runner on the instance that's able to sit there and work through that to that, diff or making that diff or distributable or whatever it is. You know, having such a long running service of the background of your instance, I can handle that. And then also if you're going through and you're applying all these big changes or diffing it or whatever, you don't want people in your instance changing the stuff as you're trying to change stuff. So implementing some sort of maintenance mode on your instance that basically locks it down and puts it, hey. We're making changes right now. You know, you can't it doesn't let anyone else change the schema or anything or the data or whatever you want it to do. We also, have been looking at, you know, for asset data, you know, pulling in you know, do you pull it in from the distributable file, or do you pull it in from the data the asset source? You know, do you pull it in from the s three bucket directly? You know, do you use it like that, or do you package it into the distributable? Or, but, basically, for the diffing part, the other part is that if you have a really, really big distributable or schema thing, whatever it ends up being called, you also upload downloading it from one instance, uploading it to another instance just to download another big thing, just to upload the other big thing back again is a lot of moving back and forth of all this different stuff. And so the other thing is when you upload that schema, whatever, it diffs it. Instead of it downloading the diff back to you, then you having to send it back up, it being able to just keep the diff on the instance and you just being able to tell it to apply the storage diff that it already has. And so you don't have to have that all that network changing back and forth, and then, you know, Internet goes out, then you're screwed. You know? But that's one of the things that we've also been looking at for the diffing. And then another thing is, you know, different types of strategies of diffing and importing. So that, you know, do you just wanna up cert stuff? Do you just wanna add new things and you wanna ignore everything else that has conflicts? Or do you want it to only apply if there are no conflicts, you know, or do you want it to overwrite everything, you know, so it doesn't matter if there's conflict. We're gonna rewrite over it with everything, you know. And then instead of just returning a singular diff that just compares the 2 different schemas and it just says, hey this is what's different. You know putting in more migration like making it more of a step type thing. So it works through migration steps. And, you know, oh, you need to do this. You need to do this. You need to do this. You need to do this. And basically a workflow that the thing can work through and and guide those long running task runners on what to do and how to configure your instance. And I think last but not least, the having some sort of format to expose potential conflicts for manual resolution. Right? So if one of the strategies has to be that it's up to the end user to pick and choose what to do. So imagine if you go, you know, from a dev to prod, life cycle, right, where you're not so much delete everything and insert everything, you wouldn't wanna do that in a prod obviously, And if you have an absurd strategy, but there is a conflict, right, you you have, like, a foreign key that doesn't work anymore or something like that. There needs to be some sort of format in whatever this diff looks like or this migration step format looks like that just has, a list of Here's the steps with the known conflict What do you wanna do? Right? How do you wanna modify that that step, those steps to get around the conflict? Right? Do you wanna upload, you wanna upload new data, or do you wanna, ignore that particular step, or do you wanna ignore those records? You know? So to your point, if we need some sort of driver and to check if you can import all of the data, it's sort of a requirement in order to be able to extract, you know, potential conflicts. So we need to have some sort of way to search through the data you're trying to apply, in order to know how to deal with conflicts. Right? So now that we're talking about all of this, what we're what we started to notice is that we're not so much talking about, you know, configuration as code specifically or templating specifically. What we're basically shaping here is a system that works for multiple things, right? Depending on how you use it. So if you were to make, a snapshot of everything, just full stop everything, and you import it as as apply everything, what you're talking about now is basically backup restore. Right? If you're exporting a small fragment and you're importing that into another project, you're basically talking about templating. Right? If you're exporting just the schema part and no data and you apply that to a new project, you're you're talking about seeding or something. You know what I mean? Like preparing preparing, a database basically, a a new project for what you wanted to do. And the question now is, how does that how does that all tie together how does that tie back into the configuration as code parts specifically, Connor? Because what we're talking about now is, you know, a new sort of format, generated by Directus that you can save somewhere, you know, which is fairly still, it's still fairly proprietary because it will have to be heavily compressed and, you know, directors needs to know what the format is. So what is the current thinking on tying it back into the code side of this question? Right? Yeah. So if we went the route of having some sort of distributable file structure folder structure that is some proprietary format or is encrypted or compressed or whatever, you know, you're not gonna be able to sit there and write code that is a compressed file. You know, you're gonna have to have write something that generates that file. So one thought that we've been having is following the lead of some other types of, you know, companies like AWS and their SDKs. So, basically, having some type of SDK that you can write and configure your instance with, and then you tell the CLI or whatever to execute that, read those different set the code that you've written, and then it will make a direct distributable file, diff file, whatever it is, from the code that you've read. So if you wanna go through and you wanna define all of your collections and your fields or whatever, and you can go in and define that in all your files and your code and then execute that code, it comes up. It generates that file that you can then use to apply those changes, import those changes, diff those changes to any of your target instances that you want to. Jonathan, if you might be wanna pull up, I think one one piece of inspiration that we were looking at for that part specifically was AWS's, what do we call it? CDK, I think, code development kit. If you wanna quickly Google that, it could be it could prove like, it could put some flavor to that to that point. So the way AWS has that, they basically made a JavaScript library that you can use to code, like, configuration. And then what it does under the hood is it effectively converts it into, a CloudFormation template, I wanna say, and then applies it immediately. Right? So under the hoot, you don't really notice the difference, but it's effectively a 1, 2 jump. Right? So it converts it into their proprietary thing in the middle first and then just applies that as is. What am I searching for? Sorry. CDK, the cloud development kit. If you wanna pull up the GitHub repo for that, maybe I have a link somewhere. I'm just curious if they have some some examples somewhere. It's been a minute since I've played with this, but it's an interesting, idea. There was a Directus community library, a little while back that that tried doing a similar thing, but it would run it against the API endpoints. It wasn't as flexible yet because we didn't have we don't have, you know, GreenStep. Is this branch? You know. If you wanna do AWS before that, because I think CD case is all different. Yeah. There we go. There's a link in the chat as Open it up. Here we go. Here we go. Here we go. Is it gonna go? There it goes. So this is an interesting reference for people that wanna look it up. At home, it's basically, you know what was that? Distracted by the chat immediately. Using something like CDK would mean that changes would need to be replicated from the UI to the generation scripts. Changes would need to be replicated. That's a great point. Yeah. How does that go both ways? Right? Because if you have that one format in the middle, directors can recreate that format in the middle. The directors wouldn't be able to recreate arbitrary JavaScript, basically. Right? So when you opt into something like that, I think it becomes a one way street by definition because we cannot figure out what parts of your JS file are, or your code because they also have some other languages, but you get the idea. And we don't know what parts of that file are auto generatable and what parts are, human created. Right? There's so there's no way to auto generate that back into a manually created file. So at that point, that's a great point, but it's the it really becomes, you know, it becomes a a one way street at that point. Some of the data community did a sort of proof of concept library to do this for, which is very interesting. So if you wanna pull up the direct to community schema builder kit repo, I just sent a link in the chat there. It was very much inspired by a similar idea where you have a JavaScript file that you use to sort of define. It's almost, you know, a declaration file rather than, you know, JavaScript, but it is still just JavaScript that runs from top to bottom. But you could define your schema and how it's applied as, you know, individual build steps, in a JavaScript. So this is where it gets real heavy on the code part of the codeless configuration. Right? And not so much just the, moving stuff around. So in in terms of big picture stuff, I really see this as the final step of whatever these changes are that we're discussing. We'll have to start with what is that new format in the middle, how is it generated, how is it used, and then see this as a way to sort of generate it into that format and then apply it automatically. Right? But, yeah, that that JavaScript syntax is an interesting interesting idea. So, yeah, I see some folk typing. This is one of those very typical director's projects where there is about 600,000 different opinions on the ideal way of doing this. And I think we we saw it in the chat immediately. Right? Shout out to a person that was like, isn't it not just the database template? Why why bother? Right? Which I can totally get behind that, but then there's a 180 or so of votes or something like that on the on the discussion. So apparently, you know, that is not an opinion that's shared. Yeah. What what's interesting is, right, because, technically, the most basic example would be something like a database migration, generally, in the beginning, you know, for the configuration as code, for example. So there's another, a similar project director, the CMS like project that I've checked out and see how they did it. And, so they handled this a little bit differently. They don't have, like, a DSL type, you know, language that defines your infrastructure or whatever. But, they went the route of as soon as a person or user, via the UI creates some type of change in the tables or the collections or however you want to call it, the instance automatically generates a migration file locally for that specific change. And, there's then a mode in the instance where you can disable any, any ability for other users to change the actual instance. So you can actually just rely on the migration files, which is an approach that you could take, you know, because a migration file then could technically do anything you'd like, you know, with with regards to, you know, the collections, the fields, whatever, even inserting, items. But, then, you know, because we're a directors, we want to that's a little too easy for us because we would like to include some type of things like, alright. How about you, locally, you develop some type of new feature, new new table, new collection, new fields. And then in order for that to work in the way that you want it to work, you need an item. You know? You need to include a new new row, data row, or an asset. This is the thing now. Because, you know, assets are not inside of the database. So we want to include assets, for example, or maybe this, of course. Nothing is, you know, set in stone, but, you know, including assets, for example. So you want to make some changes, and you need to include some assets for your changes to be even, you know, useful. So you would then have to, you know, do your changes, test it locally, include everything with the correct file name, with the correct row, whatever, or other metadata of an asset, for example. But then on production, you would have to replicate that again. So you get this back to back to step 1. Right? So this is kinda it kinda sucks, with a migration part. So even then, if we want to include this, then we get back to the issue at hand that we were talking about. Right? We want to have a process that could export something, and you can recreate that between instances and so forth and so forth. I would, I just wanted to mention that for the others in the chat because, it's not just about, you know, just adding a field because that's basically, you know, that's a basic thing, which we could solve. And I think the Director Schema Builder kit is basically that. Right? You you generate some type of syntax, which generates some type of migration. But you have to keep in mind then, of course, right, different database vendors, we have to abstract that. Because, for example, you know, in SQLite, if I remember correctly please correct me. If I remember correctly, like, you can't alter a table and introduce, like, a foreign key. You're forced to drop the table, actually, and recreate it in order to add a foreign key, for example. Other databases can do that as CLI can. So there's lots of different honest there, I'm pretty too sure that the last minor release of SQLite didn't come out too long ago. They finally do have that alter table sort of baked in. Although then, you, of course, you have the you have the side effects that it depends on your native build of SQLite on your machine, which may or may not have had. So generally speaking, historically, you've been absolutely right. It's been a nightmare and a half to to do that. Lots of fun. So I just wanted to make sure that, people in chat, you know, it's it's not about just adding a thing. It's it's a little more involved than then. And, you know, including assets and, like, the other sent. Right? Maybe there's proprietary information or whatever, and you're not allowed to leave it on your hard drive. Maybe you want to zip it, encrypt it, compress it. There's lots and lots and lots of different steps that we have to, kick off there. So alright. Oh, we got some in the chat interaction. Cool. Cool. Cool. So alright. Top to bottom. First question. What speaks against using JSON or YAML files? Instead of JavaScript, this way the changes in the webpack could also be synced back to the files easily. So for what it's worth, the the, formats that we're talking about being generated from directives would most likely be, you know, in some sort of structured format. Not quite sure if that's JSON or YAML yet or if we have to find some sort of optimized, file format to do that. Because the the the risk of JSON and YAML exports, once you start including data, you know, we no longer know how much data you wanna include. Like if we're treating this as you could use this for backup restore we could talk about a large large amount of data right at which point we need to have a very optimized structural format that may or may not be usable in that point. Right? Connor, remind me, we found like an Apache file format that could be interesting for this. I think what was it called? Parquet or something. Right? It was called Parquet. Parquet. Yeah. That would be an interesting file format for something like that. Or potentially using a SQLite database as as the exported file. Like, that's a completely different, direction. But But you get the idea. We need to have some sort of optimized compressed file format because the export file could get really large. Now, it might be an option for the way, you know, you save one of those to just save it in a sort of raw mode. Right? Where it doesn't save it compressed, at which point it's just it could be human readable YAML or or JSON, including, you know, the ability to properly, source control it. On the migration note, I think you answered that before, Daniel, like, exactly right. If you're doing auto generated migrations, it's really only for the database schema part. Like, we can't really know on your behalf if you consider, insights dashboards part of configuration or flows or something. Right? So it's it's gonna be it's gonna be tricky because people different people have different export requirements. And if you go from dev to prod, all bets are off. Right? You you never quite know what the the idea is. Creating internal libraries fiber to schema works with native access to directives rather than the API using integrations and help creating a lot of complex repetitive action. I can imagine. Yeah. Because you can write a little JavaScript for loop and just block. You have 10,000 collections. Right? But, you know, the lack of two way integration between the UI does cause issues which is the unfortunate side effect of using, you know, a programming language rather than a declarative language like yaml or JSON, for doing schema modifications like that. You're gonna lose that two way integration. That being said, you know, if Directus has a don't allow me to change the schema environment variable flag, whatever, you could make that, you could do that on purpose. Right? For a production instance, for example, I can totally imagine that you disable any sort of schema modifications just for security reasons, and and availability reasons and only allow those changes to happen through whatever system we're we're cooking up here. Right? I think default value filtering could help make the YAML auth more manageable. Fully agree. You know, we should only store the stuff that we need to know, and storing default values feels like a waste of waste of space. Then Azure is working on some sort of YAML based metadata authoring, announcing Azure data delivery net auth sales. What marketing email? Very curious. Haven't I haven't heard of that one before. If you wanna keep the GitOps thing, it should really be a text format. Good point from Tim. Which may or may not be answered from by Dominic here. If you split up between schema and content in different formats. Right? Maybe the configuration piece is all human readable file formats. But if you have a data export that is maybe there's a file size threshold. Right? If you have a very large CSV export maybe there's just a smart point where it's like, oh, you're trying to save 10,000 rows We're gonna flip it automatically into a compressed, non readable format so you get the best of both worlds. Right? Potentially. Cool. Alright. Cool. We tried. I it's something similar. Yeah. Just to, you know, chime in to regards to what you just said. So with the, like, yeah, like, yeah, we have to split that up preferably, you know, or at least, you know, required to have this as text based so you can, you know, use it in version control, whatever. And, for, like, including items, for example, you know, a ZIP is is a nice thing that you could use, but then, of course, you know, maybe this includes then items that are not, like like an old version, for example, and, you want to insert something in in where a field doesn't exist anymore, and and lots and lots and lots of other, you know, stuff. And, you can then, of course, if we then have all of the different points that we want to persist, right, like flows, permissions, the general config as it is, you could include this then also in the export with the items, so you can do both at the same time or see if it differs and then cancel the thing. But, yeah, it's, it's a fun thing. You know? There's lots and lots of things that could go wrong. There's so many. No no matter what, we wanna make sure that the output file is a single distributable. Right? We do wanna make sure because we on the one hand, we're saying we have to split it up into multiple files in order to make it efficient and easy to work with, But, at the same time, we also wanna make sure that you have a singular thing, singular file that you can send over, to somebody else. Right? Either through the API, so you just have a single download or a single upload, or as a file, maybe packaged through, you know, the marketplace. Shout out. Wink wink. Notch notch. Or as, just to email it to somebody for a like air. Right? Put it in a GitHub issue as a zip. So there needs to be some sort of both. Right? But I could also imagine that, you know, the API, lets you download it as just a zip, right, that you can just double click to open if you're on macOS or do whatever else it takes on other platforms, to unzip it. Looking at you, Daniel, I'm sure there's a 2 step process. For those who, out out of the loop, he's, this is the year of Linux on the desktop, evangelist within the team. Yes. This year. Here it is. This is the year. Mark my words. And this year is the year of the Linux desktop. This year. Yes. But long story short, we it's it's we're we're in that weird in between that we need both. Right? We need both the single file and multiple files. So we'll most likely have to come out with some sort of zip, gzip, something like that in between. Cool. Prisma migrations are an interesting way of doing things. They have a custom format, which is more concise than directed YAML, and then some CLI tools that create actual SQL migrations in the syncs the environment. Yeah. Great example. Right. Good example. They have they basically do with that shadow shadow database, if I'm not mistaken, that's how they keep track of those migrations step by step by step. Yep. Right? And then with the the CLI tool, it can compare your custom migration format with what they already have tracked so far. And then, you know, apply apply the diff based on that. Still has the similar I think a similar one way issue, though. Right? Because the Prisma migrations that you write manually, I don't think they can sort of update those from the others from the other side, so to speak, that a two way binding. Good point, though. Good point. Alright. Could remember. I had a problem with in the past. So, sadly, I can't remember it right now, but it was very painful. So, it's not a 100%. Yeah. It's not a 100 perfect thing. Nice. I've seen people you know, there's there's lots of other, what is it called? Drizzle drizzle right now? Oh, there's a couple of RMS like that. Yeah. Yeah. So there's there's lots of sauces that we could, you know, yoink some code from, but be inspired. Let's say be inspired. Yeah. I don't think we've been yoinked. I don't think we've legitimately ever yoinked code before. Borrow. Borrow. See. Strategies is interesting there. But the the the main difference and then you you touched on it perfectly before. The main difference there is the just keeping track of the database schema versus having that ability to just sort of manual template in between moving stuff between dev and prod that is not just schema, but also data and how to deal with that, which at any point needs to be, something you can do manually. Right? It doesn't have to be manual all the time, but it needs to be something you can do manually Right? Cool. Well, with all that being said, looking at the clock here Connor, back to you. We've discussed quite a lot of the research up until this point, some of the requirements. You and I have also sort of been daydreaming about potential ways to implement this moving forwards. You wanna quickly touch on sort of the different, different phases and different sort of parts that we wanna touch as part of this bigger bigger effort? Yeah. So the first thing we wanna do is we wanna figure out how this is gonna look, the distributable, the diffing. What is that file structure? What's the file format? You know, what are the requirements for that? Do we have encryption? Do we have multiple files? Do we have one file? Do we have whatever? Like, we've already discussed here. First thing is defining what that'll look like in totality, covering all use cases so that as we progress through the the phases of this project and making it happen, we can keep that spec that we have for those files in mind, and we can make sure that we cover everything whenever we could work through the different phases. But the that is phase 0 to finding out that spec, figuring it out what it looks like. Phase 2 or phase 1 would be upgrading the schema service foundationally right now. So making sure we have all the bug fixes with the schema service taken care of, you know, adding the new different, you know, different strategies that to it that might be needed. You know? Do we have, you know, add in those exports, export filters, you know, only export this stuff, you know, that stuff. So getting that schema service, adding some more features to it. And then the next phase is working on data importing. And so adding in the different features that would be needed to make the data importing work, You know, dry running, imported data, importing strategies. You know, do we have a import strategy where right now everything just gets up sorted? Do we wanna have an import strategy where if you import the data, it drops all the current date data in the table and re imports everything fresh? You know, adding those different options. Some other just things, ideas that have been thrown in the mix is, you know, if you're moving between one instance to another instance and you don't wanna bring any of your IDs, your primary keys, your foreign keys, you know, adding some type of way to anonymize that as it goes from one instance to another. So the new instance makes new IDs for everything. Or adding, you know, the ability to for, like, templates, you know, if I wanna bring in a template for project management and have a due date in one of the items, but I made the template a year ago, then the due date's gonna be from a year ago. So, you know, being able to have dynamic data that gets brought in on import. And so, you know, oh, I want you to import this as a date, but make it for in 2 hours after the import is when it's set. And then the next phase after that, working on data importing is getting into putting it all together. So we've worked on the spec for the thing. We worked on the schema service. We worked on data importing. Now we need to bring it all together into this this new overhaul configuration, multimodal thing, and have it work on bring all those pieces together and make it work, basically. And then after that, once we've gotten the specs spec'd out, the schema service upgraded, the data importing upgraded, we brought it all together, and it now works foundationally. Now we need to figure out how do we use this to implement these different use cases? So for templating, you know, what do we build inside of the data studio admin app from a user interface and API perspective to make templating work? What do we do from the configuration as code to use the stuff that we've already built? So how do we implement the feature set that we want in the configuration as code use case. You know, are there any other use cases? You know, back up being and restoring an instance. Do we have the feature set that we wanna use for that? And then that should, in theory, wrap up the project after that, after we figure out and implement those use cases. So there's quite a few steps here. Quite a lot of different items and it's going to take a long time. But at the end, we should have something pretty cool. Ideally. Yeah. And I think I think Daniel's facial expressions told told the whole story. No. But that'll I mean, it'll make sense to me. It sounds it sounds like a large overwhelming amount of stuff as it usually does. But by breaking it up, you know, step by step like this, we actually have a pretty solid idea, you know, start to finish of of what is involved to getting that across the finish line. And, yeah, as per usual, you know, there's a lot of, what's the word? Direct this magic going on under the hood to to tie it all together. But I'm always just very excited and glad that we're able to sort of re envision this as we have one underlying core foundational engine that can power all of those different use cases. Right? Rather than trying to tack on a new sort of templating piece and a new sort of code first configuration piece and a new sort of other piece. Right? That just increases the tech that makes it hard to maintain. It makes it those different flavors incompatible. You know, somebody will ask, how do we do a template as code? And we're like, well, you can't because code is not for templating. You know, that kind of stuff. It really reminds me of how we built flows. Right? That we had the hook extension first. And if you configure a new flow, you're effectively just building together a hook extension, and it's all the exact same underlying, underlying logic and event based system, which is also why, you know, flows you can do it say the same stuff as you can in a hook. Right? Well, same events. I mean, you can, of course, in a hook, you can code it yourself. So I'm very glad that we're making this, you know, a foundational upgrade to the schema snapshotting engine rather than trying to make it yet a new thing cool cool cool well that all being said I see we're at time here. Let me just quickly briefly peek at the chat. Do we missed anything? Pascal mentioned dynamic collection of field names will be cool. EG importing third party templates so you can choose your own target names. Good point. I think a similar goes for the conflict resolution piece that we talked about. Right? If you're trying to import won't be complex. Very, very, very true. But rest assured, it will be complex, I'm sure. Cool. With all that being said, this will be, life on Drax. Io\u002Ftv\u002Frequestreview in about a week week's time. Also, last episode, you can find there too. Should be if you're watching this on DirectTV, it's probably somewhere over there, there, there, or down there. Is this the point where we say, like, and subscribe? No. We don't have that yet. It's exactly we thank you all for joining.",[4936,4937,4938],"a09a4ffe-1990-4313-bf15-fd8840a5036d","72808a44-0e90-42e5-b48d-7298d0ed4294","b60bedda-7ca6-412b-ae38-c664024bcebe",[],{"year":978,"number":11,"id":1787,"episodes":4941,"show":4942},[1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1768,1800],{"title":1802,"slug":1803},{"id":4803,"slug":4944,"vimeo_id":4945,"description":4946,"tile":4947,"length":13,"resources":12,"people":12,"episode_number":480,"published":4948,"title":4949,"video_transcript_html":4950,"video_transcript_text":4951,"content":12,"seo":12,"status":19,"episode_people":4952,"recommendations":4953,"season":4954},"essence-of-coding","891433275","What is development if not an expression of our own inner, true-self?","1f037896-4709-45af-aa80-57eba73da525","2024-01-31","Essence of Coding","\u003Cp>Speaker 0: And now, Dev Thoughts by Jack Rabbit. I asked a senior dev about the essence of coding. He stared into the distance and said, code is like water. You put it in a cup, it becomes the cup. You put it in a teapot, it becomes the teapot.\u003C\u002Fp>\u003Cp>I realized he was just really thirsty and wanted to avoid my question.\u003C\u002Fp>","And now, Dev Thoughts by Jack Rabbit. I asked a senior dev about the essence of coding. He stared into the distance and said, code is like water. You put it in a cup, it becomes the cup. You put it in a teapot, it becomes the teapot. I realized he was just really thirsty and wanted to avoid my question.",[],[],{"year":2380,"number":159,"id":4790,"episodes":4955,"show":4956},[4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803,4804,4805,4779],{"title":3380,"slug":3381},{"id":4804,"slug":4958,"vimeo_id":4959,"description":4960,"tile":4961,"length":13,"resources":12,"people":12,"episode_number":675,"published":4948,"title":4962,"video_transcript_html":4963,"video_transcript_text":4964,"content":12,"seo":12,"status":19,"episode_people":4965,"recommendations":4966,"season":4967},"maintenance","891433958","Fun fact: If you whisper the word \"maintenance\" 5 times, somewhere in the world a developer breaks out into a cold sweat.","f2cc6ea8-bf19-4005-80f2-3c4b7e88e93c","Maintenance","\u003Cp>Speaker 0: And now, Dev Thoughts by Jack Rabbit. For years, I struggled with the stress and anxiety of maintaining different projects. Recently, I discovered the perfect solution for this. Most days, I just pretend I'm working on something and never actually deliver it. Goodbye, maintenance woes.\u003C\u002Fp>","And now, Dev Thoughts by Jack Rabbit. For years, I struggled with the stress and anxiety of maintaining different projects. Recently, I discovered the perfect solution for this. Most days, I just pretend I'm working on something and never actually deliver it. Goodbye, maintenance woes.",[],[],{"year":2380,"number":159,"id":4790,"episodes":4968,"show":4969},[4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803,4804,4805,4779],{"title":3380,"slug":3381},{"id":1559,"slug":4971,"vimeo_id":4972,"description":4973,"tile":4974,"length":740,"resources":12,"people":4975,"episode_number":13,"published":4979,"title":4980,"video_transcript_html":4981,"video_transcript_text":4982,"content":12,"seo":12,"status":19,"episode_people":4983,"recommendations":4985,"season":4986},"q1-2024","907454203","What's the biggest thing we've done for customers in 2023? What's coming up this year? And what's this new Directus TV thing all about? Join Will for our first video Quarterly Customer Q&A. ","62864545-f6f9-4dc1-b46a-04d5f710d34b",[4976],{"name":4977,"url":4978},"William Friedman","https:\u002F\u002Fdirectus.io\u002Fteam\u002Fwill-friedman","2024-01-29","Q1 2024","\u003Cp>Speaker 0: Yeah. Hey, everybody. My name is William Friedman, director of customer success and support here at Directus. Many of you have seen my face on either kickoff calls or, definitely seen my name flying by in emails and support tickets. Acting as customer success manager for a lot of our cloud, enterprise cloud customers and engagements.\u003C\u002Fp>\u003Cp>Really here just to make sure that all of our customers, you know, whether it's BSL supported, cloud enterprise, or cloud professional, are having a good experience with our software. Their questions are answered, and that we're partnering together to ensure that you're getting the most return on investment out of your investment with us, and that you're having a great experience, building software and applications, for your company. Features set wise, there's a there's a few things that we've put out, over the past year. You know, some of the notable ones being content versioning in WebSockets. I mean, we'll admit those are kind of MVP versions of those features.\u003C\u002Fp>\u003Cp>Right? So I know we've gotten a lot of feedback on on many calls over the past few weeks about content versioning. How can we make it better. So, although we we believe and we know that that that's a feature that a lot of folks wanted, and we think that, you know, the first cut of it is, you know, done well. There's definitely a lot of features, that we can add to make it more robust and and fit a lot of different use cases that we're seeing out there.\u003C\u002Fp>\u003Cp>Same goes for WebSockets too. Right? 1st cut of WebSockets, there were a lot of things people were looking for and and asking. And, you know, I take all that feedback from all of our customers and pass that up to our product management team and our engineering teams. And, you know, that kind of shapes the the road map for the year and and and what those features are going to look like.\u003C\u002Fp>\u003Cp>That being said, I I think, content versioning is probably, the the biggest feature that we've put out. It's it's really a staple of any content management system. We had some some workarounds that that worked, in the past, but content versioning is is is really, like I said, a staple of of any content management system, and I'm excited to see, where that goes in the in the next in in this year. I know we're we're I don't have the dates off the top of my head, but I know content versioning 2 point o was what we're kind of dubbing it internally is is, being defined and and will be released this year. So for this year, there's a lot of cool things on on our road map.\u003C\u002Fp>\u003Cp>We've kinda finalized, at least the major pieces of that, for both our core and cloud products. As you guys know, Alexandre Chopin has come in, to help, you know, on the engineering side in terms of kind of direction, management, leadership. Rike isn't going anywhere. He's our CTO, our our our fearless leader on the engineering side. He's gonna be diving into the code a bit more, and he's, you know, still there and helping push the the direct us from from a from a product side, from a vision side of things, and and kind of, you know, not just this year, but, you know, thinking about some of these major features that we're working on that, you know, may come out early in 2025.\u003C\u002Fp>\u003Cp>As far as kind of things, how I like to think about it from from kind of feature set and upcoming things, is we we kinda have 2 separate roadmaps. We have our our cloud platform, so Directus Cloud, where, you know, users can go online and, you know, spin up professional cloud projects in a self-service fashion, and and then our enterprise customers can go and kinda view details about their project. And then we also have the core side of things, which is, you know, direct the the core software, which is outside of, the cloud dashboard. A lot of exciting things coming up on both of those. As mentioned earlier, on the core side, I'm actually you're gonna see my eyes divert here because I'm kinda looking at at the road map, live here.\u003C\u002Fp>\u003Cp>For the core side of things, we've talked about it a lot, but, like, our our MVP of marketplace is gonna be coming this year for for for core. So, I think, there's a lot of promise, a lot of excitement around the marketplace that MVP is gonna be coming out this year. As mentioned, we also have content versioning 2 point o. We're kind of finalizing all of the things and features and changes and updates that are gonna be in that. So so keep an eye out for that.\u003C\u002Fp>\u003Cp>And, another one that that all kind of tease here that's been asked about a lot is multiple roles per user. So it might not be exactly multiple roles per user, but we're essentially gonna be rolling out, like, a new roles and permissioning system that will address the multiple roles per user, question topic feature that that we've gotten quite a bit. So, those are just some of the things on the core side. When it comes to the the cloud side, custom domains, something that, you know, we've been working on. We've we've kind of beta trialed it with with some of our existing customers now.\u003C\u002Fp>\u003Cp>That's going to be a self-service thing within the cloud dashboard. And, along with that, there there's a couple other things that we're gonna be implementing self-service capabilities for, that includes, you know, configuring your own environment variables, adding and removing users from your custom, custom extensions, GitHub repo in the cloud, self-service logs. Every week, more tickets and and customers are asking about that. So the ability to stream and view logs for your cloud project is also something that, we we have on the road map. So just a few things.\u003C\u002Fp>\u003Cp>Lots more kind of going on, but those are some of the kinda key highlights that I think, you know, our cloud and and and other customers are gonna be excited about. When I got here a year and a half ago, you know, the the the number of customers that we had was was very small, because, you know, we're still kind of early stage start up, and we've we've kind of exploded in terms of growth, you know, the number of customers we have in in the 3 different phases. Right? So, you know, our customers that are, you know, adhering to DSL license, professional cloud self, service customers, and then our enterprise cloud customers as well. We're we're and and with that, we're we're kinda bringing on more resources, to to help manage that, not just on the customer success side, but on the support side of things.\u003C\u002Fp>\u003Cp>So as of right now, you know, our default kind of support, responses are 9 to 5, and we're looking to kinda grow the team to eventually get to, you know, a more 24 by 5, 24 by 7 model. And as we grow and bring on more resources, the more we'll be able to, you know, meet the needs of our customers around the world. And then furthermore, on the customer success side, just, you know, more touch points. You know, we wanna provide more material for for for our our enterprise customers and our and our paying customers in terms of best practices, and just kinda making sure that they succeed. Right?\u003C\u002Fp>\u003Cp>You know, I kinda opened this thing in my introduction talking about return on investment, partnering, making sure you're getting the most out of your applications, and, investing in our customer success side of the house is going to allow us to, interact with our customers more and and help them grow as we grow, along with them. I think Directus TV is is awesome. So kudos to the team for kinda pulling together and getting that up and running. I think a lot of the content in there, as you mentioned, there there's some entertainment value in it, but I really do think, there there's a lot of usefulness in there. The main one that I'm a fan of, and I think I've we've we've gotten a lot of feedback from customers, you know, end users is is is 100 apps in 100 hours, from Brian.\u003C\u002Fp>\u003Cp>Most of you guys will know his face from our early on YouTube videos, kinda going through different use cases. You've seen him in and out of the out of the Discord community. But, I think it's the perfect blend of, as you said, entertainment, so you're not bored and, and and value for, you know, getting some learns on Directus. Right? He's building applications live in the hour.\u003C\u002Fp>\u003Cp>He's, you know, it's not a splice video where he's cutting and we're we're editing. It's it's raw and uncut. He's building it live just as you would if you were building it. And, it's it's there's some pretty cool stuff in there that I think our customers and and anyone really that's using Directus can get value from, by watching those videos. But there's a lot of other good content in there, so I I would definitely advise folks that if if you have not seen it, to definitely check it out.\u003C\u002Fp>\u003Cp>And, yeah. We're excited to for for for all the content we're releasing this year, under Directus TV. You know, I mean, we're we're we're constantly on the cloud side of things looking for, looking for ways to improve our infrastructure. If we have any, you know, you know, like I said, some of the some of the features that are coming, we we think are going to make your cloud experience more robust. So keep keep a lookout for for notification notifications from me in terms of, you know, new features coming out or, you know, updates to the cloud dashboard.\u003C\u002Fp>\u003Cp>And on the core side, I would say, we mentioned some of the features that are coming, but we are always open to feedback. I know that we have an open, you know, our our open source repo. You can see all the code and and all the feature requests and discussions. But if you are, you know, one of our customers, you know, you have my email address. Reach out to me.\u003C\u002Fp>\u003Cp>We're always looking to compile, feedback about the product, features that that we that our customers are looking for. So I can take that and and pass it up to our engineering team and our product team to to affect the road map. Right? If I don't hear that feedback, then then, you know, I I can't push it along to the team. So I'm gonna push what feedback I do have.\u003C\u002Fp>\u003Cp>And, so, yeah, I I would definitely say, review what what feature requests we have in the portal and, you know, send those links to me. And if and if there's features in there that that aren't that don't have discussions or links, you can make those and or send me the feedback on those so I can pass that along to the team. We definitely want to make 2024 a year of focus for our customers and, you know, getting our, you know, our customer feedback, our trusted partners feedback into our roadmap and and make the product better, for for for those folks that are using us.\u003C\u002Fp>","Yeah. Hey, everybody. My name is William Friedman, director of customer success and support here at Directus. Many of you have seen my face on either kickoff calls or, definitely seen my name flying by in emails and support tickets. Acting as customer success manager for a lot of our cloud, enterprise cloud customers and engagements. Really here just to make sure that all of our customers, you know, whether it's BSL supported, cloud enterprise, or cloud professional, are having a good experience with our software. Their questions are answered, and that we're partnering together to ensure that you're getting the most return on investment out of your investment with us, and that you're having a great experience, building software and applications, for your company. Features set wise, there's a there's a few things that we've put out, over the past year. You know, some of the notable ones being content versioning in WebSockets. I mean, we'll admit those are kind of MVP versions of those features. Right? So I know we've gotten a lot of feedback on on many calls over the past few weeks about content versioning. How can we make it better. So, although we we believe and we know that that that's a feature that a lot of folks wanted, and we think that, you know, the first cut of it is, you know, done well. There's definitely a lot of features, that we can add to make it more robust and and fit a lot of different use cases that we're seeing out there. Same goes for WebSockets too. Right? 1st cut of WebSockets, there were a lot of things people were looking for and and asking. And, you know, I take all that feedback from all of our customers and pass that up to our product management team and our engineering teams. And, you know, that kind of shapes the the road map for the year and and and what those features are going to look like. That being said, I I think, content versioning is probably, the the biggest feature that we've put out. It's it's really a staple of any content management system. We had some some workarounds that that worked, in the past, but content versioning is is is really, like I said, a staple of of any content management system, and I'm excited to see, where that goes in the in the next in in this year. I know we're we're I don't have the dates off the top of my head, but I know content versioning 2 point o was what we're kind of dubbing it internally is is, being defined and and will be released this year. So for this year, there's a lot of cool things on on our road map. We've kinda finalized, at least the major pieces of that, for both our core and cloud products. As you guys know, Alexandre Chopin has come in, to help, you know, on the engineering side in terms of kind of direction, management, leadership. Rike isn't going anywhere. He's our CTO, our our our fearless leader on the engineering side. He's gonna be diving into the code a bit more, and he's, you know, still there and helping push the the direct us from from a from a product side, from a vision side of things, and and kind of, you know, not just this year, but, you know, thinking about some of these major features that we're working on that, you know, may come out early in 2025. As far as kind of things, how I like to think about it from from kind of feature set and upcoming things, is we we kinda have 2 separate roadmaps. We have our our cloud platform, so Directus Cloud, where, you know, users can go online and, you know, spin up professional cloud projects in a self-service fashion, and and then our enterprise customers can go and kinda view details about their project. And then we also have the core side of things, which is, you know, direct the the core software, which is outside of, the cloud dashboard. A lot of exciting things coming up on both of those. As mentioned earlier, on the core side, I'm actually you're gonna see my eyes divert here because I'm kinda looking at at the road map, live here. For the core side of things, we've talked about it a lot, but, like, our our MVP of marketplace is gonna be coming this year for for for core. So, I think, there's a lot of promise, a lot of excitement around the marketplace that MVP is gonna be coming out this year. As mentioned, we also have content versioning 2 point o. We're kind of finalizing all of the things and features and changes and updates that are gonna be in that. So so keep an eye out for that. And, another one that that all kind of tease here that's been asked about a lot is multiple roles per user. So it might not be exactly multiple roles per user, but we're essentially gonna be rolling out, like, a new roles and permissioning system that will address the multiple roles per user, question topic feature that that we've gotten quite a bit. So, those are just some of the things on the core side. When it comes to the the cloud side, custom domains, something that, you know, we've been working on. We've we've kind of beta trialed it with with some of our existing customers now. That's going to be a self-service thing within the cloud dashboard. And, along with that, there there's a couple other things that we're gonna be implementing self-service capabilities for, that includes, you know, configuring your own environment variables, adding and removing users from your custom, custom extensions, GitHub repo in the cloud, self-service logs. Every week, more tickets and and customers are asking about that. So the ability to stream and view logs for your cloud project is also something that, we we have on the road map. So just a few things. Lots more kind of going on, but those are some of the kinda key highlights that I think, you know, our cloud and and and other customers are gonna be excited about. When I got here a year and a half ago, you know, the the the number of customers that we had was was very small, because, you know, we're still kind of early stage start up, and we've we've kind of exploded in terms of growth, you know, the number of customers we have in in the 3 different phases. Right? So, you know, our customers that are, you know, adhering to DSL license, professional cloud self, service customers, and then our enterprise cloud customers as well. We're we're and and with that, we're we're kinda bringing on more resources, to to help manage that, not just on the customer success side, but on the support side of things. So as of right now, you know, our default kind of support, responses are 9 to 5, and we're looking to kinda grow the team to eventually get to, you know, a more 24 by 5, 24 by 7 model. And as we grow and bring on more resources, the more we'll be able to, you know, meet the needs of our customers around the world. And then furthermore, on the customer success side, just, you know, more touch points. You know, we wanna provide more material for for for our our enterprise customers and our and our paying customers in terms of best practices, and just kinda making sure that they succeed. Right? You know, I kinda opened this thing in my introduction talking about return on investment, partnering, making sure you're getting the most out of your applications, and, investing in our customer success side of the house is going to allow us to, interact with our customers more and and help them grow as we grow, along with them. I think Directus TV is is awesome. So kudos to the team for kinda pulling together and getting that up and running. I think a lot of the content in there, as you mentioned, there there's some entertainment value in it, but I really do think, there there's a lot of usefulness in there. The main one that I'm a fan of, and I think I've we've we've gotten a lot of feedback from customers, you know, end users is is is 100 apps in 100 hours, from Brian. Most of you guys will know his face from our early on YouTube videos, kinda going through different use cases. You've seen him in and out of the out of the Discord community. But, I think it's the perfect blend of, as you said, entertainment, so you're not bored and, and and value for, you know, getting some learns on Directus. Right? He's building applications live in the hour. He's, you know, it's not a splice video where he's cutting and we're we're editing. It's it's raw and uncut. He's building it live just as you would if you were building it. And, it's it's there's some pretty cool stuff in there that I think our customers and and anyone really that's using Directus can get value from, by watching those videos. But there's a lot of other good content in there, so I I would definitely advise folks that if if you have not seen it, to definitely check it out. And, yeah. We're excited to for for for all the content we're releasing this year, under Directus TV. You know, I mean, we're we're we're constantly on the cloud side of things looking for, looking for ways to improve our infrastructure. If we have any, you know, you know, like I said, some of the some of the features that are coming, we we think are going to make your cloud experience more robust. So keep keep a lookout for for notification notifications from me in terms of, you know, new features coming out or, you know, updates to the cloud dashboard. And on the core side, I would say, we mentioned some of the features that are coming, but we are always open to feedback. I know that we have an open, you know, our our open source repo. You can see all the code and and all the feature requests and discussions. But if you are, you know, one of our customers, you know, you have my email address. Reach out to me. We're always looking to compile, feedback about the product, features that that we that our customers are looking for. So I can take that and and pass it up to our engineering team and our product team to to affect the road map. Right? If I don't hear that feedback, then then, you know, I I can't push it along to the team. So I'm gonna push what feedback I do have. And, so, yeah, I I would definitely say, review what what feature requests we have in the portal and, you know, send those links to me. And if and if there's features in there that that aren't that don't have discussions or links, you can make those and or send me the feedback on those so I can pass that along to the team. We definitely want to make 2024 a year of focus for our customers and, you know, getting our, you know, our customer feedback, our trusted partners feedback into our roadmap and and make the product better, for for for those folks that are using us.",[4984],"75ed5684-a21d-44df-875d-66a6ce9d12f9",[],{"year":978,"number":740,"id":1557,"episodes":4987,"show":4988},[1559,1560,1545],{"title":1562,"slug":1563},{"id":4171,"slug":4990,"vimeo_id":4991,"description":4992,"tile":4993,"length":2422,"resources":12,"people":4994,"episode_number":11,"published":4979,"title":4996,"video_transcript_html":4997,"video_transcript_text":4998,"content":12,"seo":4999,"status":19,"episode_people":5000,"recommendations":5002,"season":5003},"netflix","896165856","It's Bryant vs the streaming giant – Netflix – in this exciting episode. He has just one hour to build an app that replicates the core functionalities of the video streaming service.","012d9dd3-99b5-40ee-8811-9928bdc0a5bc",[4995],{"name":3590,"url":3591},"Mission: Netflix Clone","\u003Cp>Speaker 0: Alright. Welcome back to the next episode of 100 Apps, 100 Hours. I'm your host, Brian Gillespie, developer advocate at Directus, and I'm super excited for today. We're gonna answer the question that has been keeping you up at night, can you build Netflix in an hour? You've probably not been keeping you up at night.\u003C\u002Fp>\u003Cp>But the truth? You can't handle the truth. I can't resist throwing in a good movie quote here for this one. So we are going to try to rebuild Netflix in 1 hour or less or publicly fail trying. Those are the rules of 100 apps, 100 hours.\u003C\u002Fp>\u003Cp>And then the second rule of Fight Club is use whatever you have at your disposal. So how are we going to achieve this? We are going to do a little planning, then we're going to do a little building. But just to get eyes on what we're going to build, here's Netflix. You know it.\u003C\u002Fp>\u003Cp>You've seen it. We are going to try to rebuild as much of this as possible in 1 hour. So that means the back end, that's where we'll start. Then we'll work on the front end and try to get as close as possible to the original Netflix or, my own spin on it as possible. So what do I have prepped ahead of time?\u003C\u002Fp>\u003Cp>I have a Nuxt application ready to go. I've also got an instance of Directus. Directus is what we're gonna be using for the back end. I've got it spun up. It is totally blank now and before we get started, I'll show you just so you know I'm not cheating.\u003C\u002Fp>\u003Cp>So we'll go to admin, example, we'll key in a quick little password. If I can actually remember what the password is. There we go. Okay. It is a totally blank instance, and if we fire up the database, again just to prove it, we've just got our directus collections that it adds for us automatically, so just the metadata.\u003C\u002Fp>\u003Cp>But as far as our actual data, there is not. So with that out of the way, I'm sure you guys trust me anyway, but let's get started. Right? We got 60 minutes on the clock. Boom.\u003C\u002Fp>\u003Cp>Away we go. Alright. So before we even get started, you know, maybe this is not the Netflix clone. This is, what are we going to call this, right? Give it a name.\u003C\u002Fp>\u003Cp>How about Dev Flix? Dev Flix, yeah, this is essentially what this could be. So this is Dev Flix, let's strike through this. This is what we're going to be building. And anytime I build a project, I I like to do some concepting first, maybe some diagramming, just so my brain is warmed up and I understand what's going on.\u003C\u002Fp>\u003Cp>So we'll just draw some boxes here. Let's walk through the functionality or do we even need to cover functionality, right? We all know what Netflix does, but we've got a, browse a catalog of titles. Those could be a movie or a show. What else?\u003C\u002Fp>\u003Cp>View that content. View stream videos. Right? Stream Videos once title is selected. Pick From different categories.\u003C\u002Fp>\u003Cp>Yeah, I don't know. This is Netflix, right? We all understand the functionality involved. Alright. So let's start working on the data model.\u003C\u002Fp>\u003Cp>Right? One of the ways that I will usually try to deconstruct application is just open up the JavaScript console and or the developer tools, and not necessarily the console. And then take a look at, like, the network requests that are coming in to see if I can understand what's going on behind the scenes. You know, I don't know that Netflix does Netflix have an API? Does it close its okay.\u003C\u002Fp>\u003Cp>So they closed their public API. But if we look, we could see there's a ton of calls going out to a player, but I see here's like some GraphQL stuff, some videos, artwork. I'm not really sure if this is gonna get me anywhere behind the scenes. They spend a lot of time on security and things like that and, you know, performance. So maybe they're doing some server side rendering and and things like that here to actually display this content.\u003C\u002Fp>\u003Cp>No worries. Right? But let's walk through and and just kind of think through things. Right? We've got what do we have inside Netflix?\u003C\u002Fp>\u003Cp>We have a, like, a title, like a content title, or, we could call it content, but that doesn't seem right. It could be a movie or a series. So let's just call it titles. Right? These are movies, series.\u003C\u002Fp>\u003Cp>There's a name for that, description, probably like a rating. You know, there's a lot of things. So we just wanna capture, like, the core stuff here. Alright. So we got titles.\u003C\u002Fp>\u003Cp>What else do we have as we go through? If we look at My Little Pony here on the screen, we've got our episodes and seasons. So if it's a show, we've got a season. Season has a number, probably, what, a release date for the season. And then it's gonna have a title ID and probably episodes.\u003C\u002Fp>\u003Cp>Right? Episodes. Now behind the scenes, I don't know exactly how Netflix Netflix has set up, like, their video and, you know, like, the the encoding and and streaming and all of that. I'm assuming attached to each title or each season, each episode. Alright.\u003C\u002Fp>\u003Cp>What do we have? Oh, sometimes you just accidentally push command q and totally destroy all of your progress. Alright. So how are we gonna structure this? Right?\u003C\u002Fp>\u003Cp>This is probably the most important thing to get right. Each episode is going to have a episode number, name, description. It's gonna be back to, what, a season, season ID. And then there's almost like another relationship here, which is just the actual content. So this is just as, like, the actual file.\u003C\u002Fp>\u003Cp>We'll probably just use, like, a YouTube URL in this case. Okay. So these are our kind of main collections. You probably also got things like ratings, users. Directus gives us that out of the box, which is really nice.\u003C\u002Fp>\u003Cp>So we've got Directus users. And, you know, we could have something like viewing history as well. User watch history. You know? We're getting really deep.\u003C\u002Fp>\u003Cp>Like, the main functionality is just up here where we have these items. Right? These are the core pieces of this application. Alright. Shoot.\u003C\u002Fp>\u003Cp>I forgot to start the clock. How long have we been doing this? Let's just give, like, 55 seconds or 55 minutes. Or I did start the clock, but we deleted Figma. Right?\u003C\u002Fp>\u003Cp>So let's handicap it a bit more. Maybe 54. Okay. Alright. So, we'll go back and look at that during editing, but, yeah.\u003C\u002Fp>\u003Cp>Sorry. Mistakes happen, especially when you're against the clock. Alright. So now that we've kind of got a rough idea of how we're gonna structure this, I like to work back end first because when I'm building my front end, I love having access to the actual data instead of lorem ipsum, dipsum, and then I have to wire everything up later. So with this stack, Directus and Nuxt, I can quickly scaffold my back end and get instant rest and GraphQL APIs, and then plumb that to my front end as I'm building the front end.\u003C\u002Fp>\u003Cp>So, it's a a really great workflow for me. I hope it works well for you. Give it a shot. Alright. So let's go in and create our first collection.\u003C\u002Fp>\u003Cp>We're gonna call this titles. And I'm sure somebody will tell me why this is wrong, why it is not. So Directus gives us a couple of system fields that we can add. These are just little helpers, like, date created is automatically populated with a time stamp. User created is automatically populated with a logged in user that created that.\u003C\u002Fp>\u003Cp>Do we have a sort for those? And I can already tell I forgot one big one, right, which is gonna be genres or categories. Like, we've gotta have some kind of taxonomy for those. Alright. So we've got our title.\u003C\u002Fp>\u003Cp>What is the type of this? Right? So it's either gonna be a movie or a series. Let's use our radio button interface for that. So this is gonna be the type.\u003C\u002Fp>\u003Cp>The first one will be movie. So it's either a movie. Let's say we default to movies. This is gonna be series. And and Directus also has built in translation strings, which is another nice thing that I could go in and create these translations where here's the key.\u003C\u002Fp>\u003Cp>Let's call it what? Series. Right? Series. And in English, this is gonna be let's just capitalize this field anytime we render it.\u003C\u002Fp>\u003Cp>Right? Cool. Alright. So then I could do something like this where I have dollar sign t and do series, and that will automatically translate that for me based on the strings that I've got set up, Which is really nice if you've got users across different languages inside your Directus instance inside the back end. Alright.\u003C\u002Fp>\u003Cp>So we got the type is either a movie or a series. Let's add an input for the name of this title. And I can even go in and add like a little helper. What's the name, in this notes section? What's the name of the content of the movie or series?\u003C\u002Fp>\u003Cp>Right? Or series. Great. What else do we have? We got a description.\u003C\u002Fp>\u003Cp>And let's just go for, like, a rating. So we got a drop down. Let's call it rating. Or, you know, we could probably have, like, a ratings collection here. I was probably thinking this is like reviews, but, let's just take a look and see.\u003C\u002Fp>\u003Cp>Right? This says this is TBY. Do we wanna be able to query by that? Right? So in that case, let's do a many to 1, and we'll call it a rating.\u003C\u002Fp>\u003Cp>And we're gonna have Directus create a new collection for us in the background. So everything that's going on here, I feel the need to explain this, Directus is actually mirroring this inside my database. So as I create these new fields, as they're called inside the Directus application, it is actually mirroring those to my SQL database. So we'll call this ratings. And if we go to advanced mode just to take a look at this, maybe we zoom back out a little bit.\u003C\u002Fp>\u003Cp>But I could see that Directus is telling me that, hey. We will create this inside your data model. Great. So, it's gonna create that for me. And then we've got a mini to 1 relationship.\u003C\u002Fp>\u003Cp>Great. Okay. So we got the rating. And now if I look, we've got our ratings collection created as well. Alright.\u003C\u002Fp>\u003Cp>So we'll go in, let's add a rating, the name for the rating. I'm not sure if that's what these are actually called or not. And let's just create a couple of these. Right? Like pg13, TV y.\u003C\u002Fp>\u003Cp>Is that what I saw? TV y. Yep. TV MA, rated r. Yep.\u003C\u002Fp>\u003Cp>Alright. So we get the the picture. We could call it PG, and then we have G. Right? Some of my favorite movies are still g rated movies, to this day.\u003C\u002Fp>\u003Cp>Of course, I'm a father of 3 as well. Alright. So we go back to our data model. Let's build out the rest of this. We've got our titles.\u003C\u002Fp>\u003Cp>So if we are creating a title now, like My Little Pony, now we can see what the status is. Is this a movie? Is it a series? What's the name, description? And we could pick a rating that we can then update through the system.\u003C\u002Fp>\u003Cp>And let's just adjust the display template for this because I wanna show the name anytime we're referencing that rating. Alright. So I'll zoom back in just a bit, and we'll build out our next piece of the puzzle here. Let's work on seasons. Alright.\u003C\u002Fp>\u003Cp>So we'll automatically generate a UUID. Maybe we wanna store who is the user that was updated this just so we can play the blame game later. Cool. Alright. And then we've got a number.\u003C\u002Fp>\u003Cp>So let's do an integer. We'll have the season number. Okay. And inside Directus, I can also make that the sort field. Where are you, sort field?\u003C\u002Fp>\u003Cp>Alright. So we'll choose the sort field. The season number is going to be the sort field. So whenever we reference this, it will sort by that number automatically. What else do we have?\u003C\u002Fp>\u003Cp>Do we have a description on the sort number? I I don't know. Release date, you know, this theoretically could be on the actual episodes as well. But Netflix does like to drop a season at a time, so maybe we store it here as well. Let's just use a time stamp so that it saves the the time zone value as well.\u003C\u002Fp>\u003Cp>Alright. What else? We have a title ID. Right? So we have to create that relationship back to the title so we can fetch that data.\u003C\u002Fp>\u003Cp>So this is gonna be a mini to 1. Right? A season can only belong, like, to a a single content title. So if if the show is My Little Pony, there's 5 seasons, all those seasons belong to 1 My Little Pony title. So we could call that title ID or title, I prefer less verbose, but it doesn't really matter here.\u003C\u002Fp>\u003Cp>Directus is going to serve up that however we create it. Alright. So we've got that. Let's add the one to many relationship. So we're creating a many to 1, but on the reverse side, a single title could have many seasons.\u003C\u002Fp>\u003Cp>So let's go ahead and create that relationship as well. And for the season we'll use the oh, for our many to 1 we'll use the name of that and then we'll do related values. Okay. So we got the title, we've got the number. You know, we could have a description for this season if we wanted to.\u003C\u002Fp>\u003Cp>You know, there's probably even, like, some collateral or trailers or or something attached to this. We won't worry about that right now. Let's save this, and then we'll go in and create episodes. So in our diagram over here, we probably should have had episodes to begin with. Alright.\u003C\u002Fp>\u003Cp>So same thing. I'll I'll just add my system field. So if anybody updates this information, we've got it. Is this episode published or not? Yes or no.\u003C\u002Fp>\u003Cp>And then we'll give this a name. What's the name of the episode? We probably also have a number for this episode as well. Episode number. And, you know, something like this, you may even call, like, episode number just to make sure everything is very clear, but I'm all for making things difficult.\u003C\u002Fp>\u003Cp>So we will add a description. And now we'll link this back to the season. Right? So we're going to use that many to one relationship again, and we're going to link this back to a season. The related collection is seasons.\u003C\u002Fp>\u003Cp>And I'm going to open up advanced field mode here in Directus, and I guess I could actually use my little mouse pose tool for you guys so we can get an idea of what's happening here. So I'm gonna add the reverse relationship back to seasons. So one season could have many episodes, 1 episode has or many episodes have a single season. Think I explained that right. Sounds great.\u003C\u002Fp>\u003Cp>Alright. So now we've got titles, we've got episodes, we've got seasons. If I look, we've got titles. So let's go in and create a new title. Right?\u003C\u002Fp>\u003Cp>So we'll just drag and drop Netflix over here. And let's just work on this. Right? My Little Pony, Make Your Mark. Alright.\u003C\u002Fp>\u003Cp>So this is My Little Pony, Make Your Mark. Alright. Maybe there's a hyphen there, a dot, I'm not sure. That's the season description. Right?\u003C\u002Fp>\u003Cp>Where do we see all episodes? Alright. So here's the there's kind of the the generic description that apparently Netflix does not let me copy. Alright. So welcome back to Equestria, where pony magic is everywhere.\u003C\u002Fp>\u003Cp>Who doesn't love pony magic? Alright. There's our description. This is TVY. Alright.\u003C\u002Fp>\u003Cp>And I can already see we've left out a couple things. Right? We've left out some thumbnail images, probably on each episode, each, title as well. But we'll go in and create a new season. They're calling this chapter 1.\u003C\u002Fp>\u003Cp>So we probably have a name for the season as well that we can add. Let's just say the release date is today, and we'll start adding some episodes. Right? So we got Make Your Mark. That's episode number 1.\u003C\u002Fp>\u003Cp>Something's wrong in Equestria. And now we're actually watching Netflix. How do I go back? Okay. Alright.\u003C\u002Fp>\u003Cp>Is there a way to actually I I guess they disable that. I'm just gonna use Raycast here to generate descriptions for this. Alright. So we'll say this is published. Alright.\u003C\u002Fp>\u003Cp>So at the very least, now we have some stuff that we can work with and fetch via the API that we can render on the front end. I'm gonna go back really quickly and just add a thumbnail for this title. And we could even do that for what, the episodes as well. We probably got, like, a thumbnail. There's probably also, like, a teaser.\u003C\u002Fp>\u003Cp>And while we're at it, let's just go ahead and try to finish out what we were building on the back end. Alright. So then we've got the actual content. So creating this separate collection allows me to standardize this across episode or, like, TV and movies. So, let's go in and create the content.\u003C\u002Fp>\u003Cp>You know, this could be video, I guess. Yeah. I'm not sure. Let's just call it content. And I'll probably figure out a better way to do this at some other point.\u003C\u002Fp>\u003Cp>Alright. So we got the content. We've got a just use a YouTube URL for this. We're gonna cheat a lot. But this could be like an actual file that we uploaded to our back end here that we serve.\u003C\u002Fp>\u003Cp>Again, I don't want to get into, like, the streaming stuff because I like efficiently serving video at different bandwidth, at different resolution. That's a whole ball of wax. I'm sure there's a great API for it. And Directus will manage some of those assets for you in a nice way, but it doesn't do video encoding and things like that that you need to deliver those streams effectively. Cool.\u003C\u002Fp>\u003Cp>So we've got, what, a YouTube URL. There's the content. This is gonna have a title associated with it. Titles. K.\u003C\u002Fp>\u003Cp>And then there's probably what? Episode as well? Episode. Titles. Okay.\u003C\u002Fp>\u003Cp>Alright. So we got a title. We got an episode. We probably should have created those reverse relationships, but we could go in and do that now. Alright.\u003C\u002Fp>\u003Cp>So we'll call this the content. That'll be our content collection. Episodes. And now we'll just create the reverse. Uh-oh.\u003C\u002Fp>\u003Cp>What am I doing wrong? I'm doing a mini to 1 back to that when, actually, let's just go in and I'm gonna delete these for now just to make sure we set these up correctly. Alright. So I'm gonna go let's go to the titles. We'll do a many to 1.\u003C\u002Fp>\u003Cp>We'll call this content. We'll go to the content collection. And here, I am going to go into the relationship. We'll add this to the title. Okay.\u003C\u002Fp>\u003Cp>So we'll add that reverse one to many relationship. We'll call it the title. I think it is going to be an array. I guess you could have multiple pieces of content attached to any one item, but no worries. Content.\u003C\u002Fp>\u003Cp>And then we're gonna have episode. Okay. Okay. Cool. Cool.\u003C\u002Fp>\u003Cp>Cool. Alright. What's next? Right? We probably need some genres.\u003C\u002Fp>\u003Cp>Let's go in genres. And if there could be multiple genres per piece of content, I assume, like, could be drama and horror or drama and faux drama. If you live in my household, I've got plenty of drama. So we'll give this a name. And then let's go in, and we would create a junction table between titles and genres.\u003C\u002Fp>\u003Cp>Right? But, again, one of the nice things here is Directus is gonna do a lot of this for us. So we could call this genres. We do genres as the related collection. And if we look, we go to the advanced mode inside Directus, we can see that there's gonna be a junction collection or a junction table in our database created.\u003C\u002Fp>\u003Cp>Do we want to be able to query by genres? Maybe we don't need that. Do we add a sort field? Probably not. Okay.\u003C\u002Fp>\u003Cp>So now we've got genres. We've got thumbnail. We got a lot of different items set up in this database. Right? And we're closing in on 30 minutes.\u003C\u002Fp>\u003Cp>We probably need to start building some UI soon. But I do wanna just open up the database itself and and take a look at what's going on now. So you'll see these direct us tables. But if you look, you can also see our our actual tables that we created, like episodes and genres. And if I look at the data, here's the episode that we created, make your mark for My Little Pony.\u003C\u002Fp>\u003Cp>So this is great. Like, anything that happens inside the app here, Directus is mirroring that to your database. Likewise, if I go in and, there's very robust access control here. I'm just gonna enable this as a public permission for all of our content, which, would probably not be a great thing security wise, but I just want to show you this. We'll go into local host 8055, which is my back end.\u003C\u002Fp>\u003Cp>And if I go to items and I go to titles, boom, I'm gonna get My Little Pony. Right? Now, this is not super helpful because we're just getting you know, like, let's say I I just wanted to make a single call and get all my content. I love Directus for that because it gives me an interface via REST that feels like GraphQL. So I get the benefits of GraphQL and then I can go in and do something like this, like, hey, I just want the ID, I want the type, I want the name, I want the description, Boom.\u003C\u002Fp>\u003Cp>And it will give me exactly what I asked for. Now, the other thing that I can also do is go in and if I want something like seasons, and I I could use a wildcard to grab all the root level fields of the the next one, right, but I can get all of my related content in a single API call. That's super powerful, super helpful. Obviously, like, the deeper you go, the slower the queries on the back end are gonna be, but it is still very speedy. And, obviously, I don't have a ton of data here.\u003C\u002Fp>\u003Cp>So we got seasons dot star, and then we could do seasons dot episodes dot star. And that should give us the episodes. And then once we add content, we could go even deeper and say something like seasons dot episodes dot, content dot star. Now there is no content at this point, but if there were, it would come back. Great.\u003C\u002Fp>\u003Cp>So that is just one way that one of the major things that I really love about directives. Alright. So we've got some stuff going on. Let's go in and populate a few more titles. Right?\u003C\u002Fp>\u003Cp>We've got a thumbnail. I don't think they're gonna let me lift this. So let's just do the old screenshot trick. Upload that image. Click to browse.\u003C\u002Fp>\u003Cp>Should actually just be able to drag and drop there. Okay. Let's create a new content for this. We'll call it YouTube. Pull you up, My Little Pony.\u003C\u002Fp>\u003Cp>And again, I I don't support any of this. Make sure anything that you are doing is okay legally. We'll just copy this. K. Great.\u003C\u002Fp>\u003Cp>We've got a movie. Actually, this is a series. Right? But one thing I noticed here is if I'm switching between movies and series, we should probably actually not show our seasons. Right?\u003C\u002Fp>\u003Cp>So how do we actually adjust that? Well, if we go into our titles, Directus has the ability to do, relational or, I'm sorry, conditional fields. So I could go in and for seasons, we could do something like this where we hide this by default, and then we come into our conditions tab. And if type equals series, so then we build a rule. This is just a description for this.\u003C\u002Fp>\u003Cp>We'll do type type equals Series, then we are going to make this not hidden. Right? And we'll show a link to the item. Did I get that right? Yep.\u003C\u002Fp>\u003Cp>Okay. Alright. So let's test this out just to see what's up. Let's go in and add another one. Right?\u003C\u002Fp>\u003Cp>And now where I have movie selected, nothing. But if I select series, now we're seeing that field. It doesn't change the underlying database structure or anything. It's just hiding that when I'm creating the content. But I could even add validation around it if I wanted to.\u003C\u002Fp>\u003Cp>So if we go in and we add a movie, let's see if we've got a movie. I could switch up here to the top to movies, and we open this up. We've got a trash truck Christmas. Christmas. Alright.\u003C\u002Fp>\u003Cp>This is TV Y, made for TV, made for TV goodness. Alright. We will create some new content for this. Where's our handy YouTube? Trash truck Christmas, full episode Netflix Junior.\u003C\u002Fp>\u003Cp>This looks like an official channel. Yeah. Okay. We'll see. So we got that.\u003C\u002Fp>\u003Cp>We've got a thumbnail image. Again, I'm not even gonna try to lift this from oh, actually, I I guess I could. Copy image address. Is that available? On the web?\u003C\u002Fp>\u003Cp>Okay. Cool. So we'll just lift these images directly. I can import that file. Great.\u003C\u002Fp>\u003Cp>There that is. And I could, you know, maybe potentially find their GraphQL, stream for this that allows me to pull all this in, in a a more timely fashion. But, okay. Genres. Right?\u003C\u002Fp>\u003Cp>Let's create a new genre. What are we calling this? This is kids TV. There's a new genre. Okay.\u003C\u002Fp>\u003Cp>We'll create a new kids and family movies. And this will be TV cartoons. Great. Okay. So we got a movie.\u003C\u002Fp>\u003Cp>Let's switch this back to a series. Right? Lots of series because we've got seasons attached to it. Got some content for it. Is it is that gonna be right?\u003C\u002Fp>\u003Cp>Actually, it's not, is it? We're going to go back to My Little Pony. We'll go into the seasons. We've got episodes, and we should have content for that episode. Cool.\u003C\u002Fp>\u003Cp>Alright. So let's just see what this all looks like. Great. I could even go in and adjust this to, like, something akin to Netflix if I wanted to where I could see, like, the thumbnails of all these different titles. So give it a name.\u003C\u002Fp>\u003Cp>The subtitles could be what? Description. What else do we have? Or it could also be can we get genres in here? Genre genres ID.\u003C\u002Fp>\u003Cp>Let me get the name of the genres. Cool. Alright. So we got a couple titles. We are what?\u003C\u002Fp>\u003Cp>We're pretty I'm pretty happy with where the back end is at at this point. You know, we could really go through this in a lot more detail and create something like actors, you know, the cast and crew, director, things like that. And those would probably all be different database tables. But for the sake of having something to display at the end of this next 28 minutes, let's just get to work on the front end. Alright?\u003C\u002Fp>\u003Cp>Cool. So we will let's put you back over here, Netflix. Close you. We'll close our database. Let's pull up our Nuxt application, and I'm just gonna swap these around.\u003C\u002Fp>\u003Cp>No real rhyme or reason. I just like my text editor being on the right side. Alright. So now let's pull up our Nuxt application. Oh, this thing is a beauty, right?\u003C\u002Fp>\u003Cp>So, I've just got a preconfigured, like, a Directus Nuxt module set up that basically just wraps the Directus SDK with a little bit of auth. You know, we may not even dive into that. But, and then I've got a use direct as composable that allows me to make requests easier. There is a a little bit of setup work that I've done here, but it is really just, one of our boiler plates HSC OS that I have taken and stripped back for the purposes here. Alright.\u003C\u002Fp>\u003Cp>So we go into our index page. Alright. So we let's look at, the homepage here of Netflix. And we've got kind of this setup of a header. We've got a grid of titles sorted by genre.\u003C\u002Fp>\u003Cp>Alright. So let's dive into this. The first thing that I'm going to do, just because we are short on time, is take a look at, Tailwind UI. Advanced. What's going on here?\u003C\u002Fp>\u003Cp>Okay. Let's just look at Nux UI as our our library here. Something wrong with my laptop at this point. Alright. So inside this boilerplate that I've got set up, I've got a little, I've got I've got this Nuxt UI library, which is kind of built on Tailwind as well, that gives us some nice things like modals, some of the trickier components to actually build.\u003C\u002Fp>\u003Cp>Right? So we go in. Let's try Tailwind UI again, now that I've refreshed a little bit. And, alright, so what are we looking for? Let's grab a, like, a header from Tailwind UI.\u003C\u002Fp>\u003Cp>Alright. This looks pretty good here. So we've got the view version. I'm just going to copy this down. Alright.\u003C\u002Fp>\u003Cp>And let's go in and create a header, error component header dot view. Alright. Are we gonna have a logo for this, right? We've got our logo. Can we pull that up?\u003C\u002Fp>\u003Cp>Will this let me copy as PNG? Can I export this as JPEG? Nope. Let's just do a new Figma file actually instead of FigJam. Can we paste that?\u003C\u002Fp>\u003Cp>If we copy, Can we paste? Devflix. Yeah. Alright. Let's give it some, like, type of little icon treatment here.\u003C\u002Fp>\u003Cp>So we'll add a real original logo here. Looks like a play button. We'll change the fill to red. Right? Okay.\u003C\u002Fp>\u003Cp>And maybe we, what, let's wrap this in. Let's give it a little oh, yeah. I'm I'm gonna be giving design courses after this as well. Alright. So we'll take this.\u003C\u002Fp>\u003Cp>We'll export to SVG. Let's just do logo. Great. Alright. So we've got this header component.\u003C\u002Fp>\u003Cp>If we go back to Netflix, what do we got at the top? We've got, home. That'll be our index page. This is for our navigation. Right?\u003C\u002Fp>\u003Cp>So we've got TV shows. Alright. We got movies. Great. Okay.\u003C\u002Fp>\u003Cp>Cool. I don't have Headless. I don't have heroicons in here, so let's just comment those out for now. Bars 3. I'm sure that'll throw a fit when we actually take a look at this.\u003C\u002Fp>\u003Cp>But cool. Alright. Now I'm gonna create a new file called logo dot view. And we're just gonna copy this as, what, SVG. Paste here.\u003C\u002Fp>\u003Cp>Nope. I need to wrap that in a template, though. Alright. There's our SVG. Now inside our header component, I'll go back up here to where we have Tailwind UI's logo.\u003C\u002Fp>\u003Cp>And I'm just going to call that logo component that we just had. Don't need a source. Don't need an alt tag. Well, we probably do, but not going to have one. 100 apps, 100 hours.\u003C\u002Fp>\u003Cp>Okay. So we have a header. We're not seeing it here. What I can do is adjust the default layout. And let's do something like this where we have BG gray 900 and probably text white since we're kinda going on the Netflix theme.\u003C\u002Fp>\u003Cp>So this is the layout inside Nux, which is a feature that I really like about Nux, is the ability to set up a default layout. And boom, we've got our DevFlix header looking nice. If we go back to that header, probably wanna swap these a tags or a Nuxt link and change this to 2. And we could change this to TV shows and movies. Right?\u003C\u002Fp>\u003Cp>We just have to create a route for those. And I'm not sure exactly how far we'll get because we have, what, 21 minutes remaining. But we'll we'll see how far we can get. Alright. So if I go into the header, we need to update the details for it as well, like bg white.\u003C\u002Fp>\u003Cp>What do we what does Netflix look like on the header? It's just kinda transparent until you scroll down. That's a nice effect. I don't want to set up anything there. So let's just cheat.\u003C\u002Fp>\u003Cp>We'll call it bggray900 again. And then that's gonna mess up a lot of our text, I believe. Right now, we can no longer see all of that. So we go to our text. Let's look at anything with text gray.\u003C\u002Fp>\u003Cp>Can we replace that with we could probably do smaller values, like text 200. Again, I love Tailwind just because it enables me to not have to write custom CSS for everything. Like, it it does take some time to learn the conventions, but but being able to not write CSS, I like custom CSS, just helps me speed along. Now I know it's not for everyone, so don't let me tell you that it is, But I enjoy it. Alright.\u003C\u002Fp>\u003Cp>Can we get our stuff? Okay. Alright. That's probably because we're missing an icon for our navigation over here. So what is this?\u003C\u002Fp>\u003Cp>This is hidden. Flex LG. Let's hide that at, like, just a smaller breakpoint. Yeah. Okay.\u003C\u002Fp>\u003Cp>Cool. Alright. So we got home. We got TV shows. We got movies.\u003C\u002Fp>\u003Cp>We can log in. Great. Now let's actually work on our page. Right? If we go in, let's actually render some stuff out.\u003C\u002Fp>\u003Cp>Alright. So if I look at Netflix, we've got a, like, a header component or, like, a hero image. So we go for our hero sections. Hero. Which one of these kinda looks like Netflix?\u003C\u002Fp>\u003Cp>Kind of this one? Kind of. I mean, this one has a background image. So we could take that. Okay.\u003C\u002Fp>\u003Cp>So we're not gonna copy this header piece, but let's just go in. I'm gonna copy this part. We will create a new one called hero dot view. I've got a just a couple of snippets already set up for me inside Versus Code to make creating Vue components a little easier. And k.\u003C\u002Fp>\u003Cp>We're good there. Watch chapter 6 now. Is this just kind of a rotating thing, probably based on, like, the last thing that you watched? Instead of doing that, let's do something else. Data.\u003C\u002Fp>\u003Cp>Alright. Alright. So this is gonna be justified center. We don't wanna do that. We wanna do does it justify start, I believe?\u003C\u002Fp>\u003Cp>K. Oh, we'll add this to our index page. So let's take a look at this. We'll just wrap this in a div. We'll go to hero, call in that component.\u003C\u002Fp>\u003Cp>Let's see what this is starting to look like. Alright. Okay. Okay. We got a little something going on here.\u003C\u002Fp>\u003Cp>I only succeeded in moving one thing to the left. So let's take a look at that hero component one more time. Alright. So this has got some type of gradient blur attached to it for the images. Can we lift this?\u003C\u002Fp>\u003Cp>Oh, yes, we can. Netflix. Love building on the fly. Thank you. Netflix.\u003C\u002Fp>\u003Cp>What else do we have? The overflow hidden. The the the announcing. Do we even need this? Let's let's just scrap that part of it.\u003C\u002Fp>\u003Cp>And we'll just show watch chapter 6 now. And I still can't copy this stuff. Come on, Netflix. Copy. Copy selector, copy element, edit as HTML.\u003C\u002Fp>\u003Cp>All right. So we'll just pick this up and throw it here. And we got the My Little Pony logo, so we'll add in Nuxt. We'll just add in an image tag because I don't have that domain configured. Image source.\u003C\u002Fp>\u003Cp>Alright. Class. I don't know. Maybe height 24. And an image tag, welcome back to Equestria Tech Center.\u003C\u002Fp>\u003Cp>This is not gonna be Tech Center. Okay. We're getting somewhere. How do we dim this image? Right?\u003C\u002Fp>\u003Cp>Brightness 50? Oh, wrong image, Brent. Brightness, 50. Maybe 25. Oh.\u003C\u002Fp>\u003Cp>Can we do a percentage here? Get, like, a a nice value. Okay. Alright. So we're getting closer.\u003C\u002Fp>\u003Cp>He likes it. Alright. Get started. Let's say watch now. Okay.\u003C\u002Fp>\u003Cp>Let's remove that one. Alright. So at least we got some type of header here. This would be a NuxLink. And, I'm trying to think of how we're gonna serve this content.\u003C\u002Fp>\u003Cp>Right? We would do a page. Let's add a new folder for this. This is gonna be what does Netflix do as far as routing? Right?\u003C\u002Fp>\u003Cp>If I open this up, episodes and info. Okay. So is it just like a genre? Is that how how the URL structure is? Because you watch Masha's spooky stories.\u003C\u002Fp>\u003Cp>So we got the genre. If I look at the URL again, yeah, it's just like Netlify Kids, and then there's a query param. Alright. So it actually appears to be inside the the actual application. Right?\u003C\u002Fp>\u003Cp>So we're just appending, like, a query per annum to it. Let's come back to that step. Let's actually render out some titles first, though. Right? So we go back to our page.\u003C\u002Fp>\u003Cp>Let's create a new component. What what do we gotta call this? Content grid? Sounds great. Vcomp ts.\u003C\u002Fp>\u003Cp>Okay. So now we've got a a Vue component. Let's do some data fetching. Right? We will do something like this where, we got constant data equals use async data.\u003C\u002Fp>\u003Cp>So this is the built in Nuxt data fetching. We give it a key. Let's call it content grid, comma. Alright. And then we're going to return something.\u003C\u002Fp>\u003Cp>So in this case, we're gonna use our directus composable that I've got inside this. But if not, I could import the Directus SDK, fire that up, but, we're on the on the clock here. So alright. So we're gonna read our items, and we are going to read titles. So and then we're gonna get, is there any specific thing that we wanna render out?\u003C\u002Fp>\u003Cp>Let's just leave our options blank for now. Right? So we got our data. Let's just go in and make sure we're getting that data back. Sounds great.\u003C\u002Fp>\u003Cp>I don't see it on the screen. Why not? That's because we're not actually including it in our index page. So we'll just do content grid. Great.\u003C\u002Fp>\u003Cp>Okay. So at least we could see a few things here. That's great. Do we really need anything else to render this grid? Probably not, actually.\u003C\u002Fp>\u003Cp>This shows, like, the viewing data. We're we're definitely not gonna get to that. We got 12 minutes left. Let's just see if we can actually render something out. It shows something in a modal.\u003C\u002Fp>\u003Cp>All right. So we got our content grid. Great. Cool. Let's go in.\u003C\u002Fp>\u003Cp>I'm not sure if Tailwind has a great looking component for this. Gridless. Kind of. Yeah. Let's just create something on the fly.\u003C\u002Fp>\u003Cp>Right? So we'll go in. Alright. So that's the wrapper div. Then we'll do, we would probably fetch this via genre as well.\u003C\u002Fp>\u003Cp>Again, running short on time. Alright. We are going to do a, let's just keep this simple. Right? Add add a little bit of padding to the sides.\u003C\u002Fp>\u003Cp>We'll give this a grid. Grid calls, 5 across the screen, 6 across the screen. And then we'll do, let's do a, what, title card? And we will pass so we'll do some v four action here. V 4 title and data.\u003C\u002Fp>\u003Cp>The key is gonna be title dot ID. And then we have our, what, We're going to pass a title, title equals title. All right. So now we're going to create a new component. Call it title card dot view.\u003C\u002Fp>\u003Cp>And we will define some props. So props equals define props, Title object is prop type. Haven't set up our types here even though we're still using TypeScript. Alright, so we've got a card. I know Telen has like a card definition.\u003C\u002Fp>\u003Cp>Actually, the Nuxt UI library has a card as well. Let's just use it. So we got a card. There's a card. U card is the name of this.\u003C\u002Fp>\u003Cp>U card. And what do we have inside the card? We have, I don't know if these actually need to be h tags or not. We'll just use a p tag. Got the title dot name.\u003C\u002Fp>\u003Cp>If I can actually type this. Right? We've got an image for the card. Source equals title. No, no, no, no.\u003C\u002Fp>\u003Cp>We're gonna use Nuxt image because I have it preconfigured. And I'll just show you that really quickly. If I go to Nuxt Config, ba ba ba ba, where are you? Where are you, mister Image? Image.\u003C\u002Fp>\u003Cp>Okay. So Nuxt Image Directus is a provider here. We just give it a base URL that communicates with our Directus instance and allows us to do image transformations on the fly, which is really nice. So we got source title dot thumbnail. K.\u003C\u002Fp>\u003Cp>What do we do? Like, what? With full height 24 maybe? Sounds good. We got a title.\u003C\u002Fp>\u003Cp>Let's make this font bold. I'm assuming that's what it looks like. Oh, no. It's actually just a card. Right?\u003C\u002Fp>\u003Cp>Alright. Let's see what this looks like. We'll go back to our index page, content grid. Why don't we have our content? Why is content grid not rendering anything?\u003C\u002Fp>\u003Cp>Do we save it? What's going on here? Dev server. Why are we not rendering? Title card.\u003C\u002Fp>\u003Cp>Content grid. There is no data. What are we doing there? We just had our data. Right?\u003C\u002Fp>\u003Cp>Am I being silly? Pre data. Is that not showing? Did I break something? What is going on?\u003C\u002Fp>\u003Cp>Use, async data, content grid, titles, read items. Where did you go? Did I not just have this, like, a minute ago? Title. Title equals title.\u003C\u002Fp>\u003Cp>Hydration mismatch. If I look at content grid, like, we should have data. Right? Let me just look at the Nuxt fetching. Sometimes I even I use Nuxt all the time, and I'm not sometimes I have to go back to the oh, we gotta wait.\u003C\u002Fp>\u003Cp>Yeah. I don't know how that worked the first time. Right? Next image not showing. Okay.\u003C\u002Fp>\u003Cp>So we're missing some images there. Why are we missing images? So we are missing images because, let's do let's add our grid back. Grid calls 6bg grid. Do we need an actual BG?\u003C\u002Fp>\u003Cp>Okay. Maybe grid calls 4. That's fine. How are we doing on the time? We got 6 minutes left to actually render something up.\u003C\u002Fp>\u003Cp>Px8py12. Just pad this out a little bit. Alright. What's next? Our our images are not displaying.\u003C\u002Fp>\u003Cp>Right? How do we fix that? It is actually gonna be inside our Directus instance. So it is a permissions issue. Pretty sure if I were to take a look at our network requests, look at it like images here, you have 403 forbidden.\u003C\u002Fp>\u003Cp>So, I would go into not my docs. We'll open up the Directus instance. Go to 8055. And I somehow managed to log myself out as well. Alright.\u003C\u002Fp>\u003Cp>We go into access control. We've got our content here, but then we're gonna go in and allow access to the direct us files collection. So that way we can actually serve those images on the front end. Okay. Great.\u003C\u002Fp>\u003Cp>Maybe title card wasn't a great idea there by using the Nux card. Right? Let's just call it div. Oh, forgot to close that. Alright.\u003C\u002Fp>\u003Cp>So there's the diff. This is going to be Object, Cover, Trash Truck Christmas. Maybe we need to make that a little higher, 32. Alright. How we doing on time?\u003C\u002Fp>\u003Cp>4 minutes. Can we get this to actually render? Alright. Button at click. Alright.\u003C\u002Fp>\u003Cp>Quick and dirty. Let's do this. We're going to add a composables. Actually, we could just pass this up as well. Alright.\u003C\u002Fp>\u003Cp>At click, we're going to define some omits. So omits equals define omits. We're gonna tell this select title. Select title? I think that's the syntax.\u003C\u002Fp>\u003Cp>Alright. Alright. Select title. Emit. Select title.\u003C\u002Fp>\u003Cp>And we're gonna pass the what? Prop no. It should just be title dot ID. Is that right? Or does that do I have to wrap that?\u003C\u002Fp>\u003Cp>No. Where are we getting here? Select title does not exist. Let's just call this function. On click, emit select title, props dot title, on click.\u003C\u002Fp>\u003Cp>Cool. Okay. And then inside our content grid, let's do this where we've got a modal. K. And we've got is open.\u003C\u002Fp>\u003Cp>That's false. Select title. And then what are we gonna do for the title? Let's do title, maybe content. Right?\u003C\u002Fp>\u003Cp>Content ref equals null. GitHub Copilot here for the win on this one. Right? Do we have a video component? I don't have a video component.\u003C\u002Fp>\u003Cp>So we're just gonna do, what, an iframe, class, aspect, style. Let's call it video. We'll just add a quick style here. Dot video aspect ratio 16 by 9. Alright.\u003C\u002Fp>\u003Cp>The well, the source is gonna be the content dot YouTube URL. Is that actually gonna work? I think I've got do I have some strings in here? Do I have a video content? I don't.\u003C\u002Fp>\u003Cp>Alright. Let's rely on AI here. Generate YouTube embed. Okay. So that returns the video ID.\u003C\u002Fp>\u003Cp>Let's see what AI comes back with. YouTube embed video ID. Okay. Great. Alright.\u003C\u002Fp>\u003Cp>So there's gonna be our source. We'll say generate YouTube video embed URL. Oh, nope. That's YouTube embed URL. Like, somewhere in the starter, I may already have this.\u003C\u002Fp>\u003Cp>Generate YouTube embed URL, and that is gonna be the content dot YouTube URL. Alright. So what am I missing? I'm gonna need to close that. Okay.\u003C\u002Fp>\u003Cp>And then on our select title, right, we need to fetch the content for the title. Alright. So that's gonna be what? This is gonna be an async function now. How are we doing on time?\u003C\u002Fp>\u003Cp>We have hit time. So we ran out of time to actually flesh this out and render this content in a model, but I just wanna try it. Let's just pursue it really quickly and see where we're at. Constant. We are going to we don't need to actually use async data for this.\u003C\u002Fp>\u003Cp>We're gonna fetch this on the client side. So let's do constant title equals wait. Use direct us, read items, title. Read item. Is it gonna be a title?\u003C\u002Fp>\u003Cp>We're gonna fetch through the we got the root level fields. Let's take a look at that data model again. Right? Local hosts. Alright.\u003C\u002Fp>\u003Cp>So we got titles, then we've got content? Alright. So root level, we'll go content dot that. Select title. Title has already been declared.\u003C\u002Fp>\u003Cp>Oh, okay. Where do I have that at? Oh, I've already given it the title. Content. Alright.\u003C\u002Fp>\u003Cp>So if I click that, is it actually making the request? Select title. Oh, and then for the title card at select title, we're going to select title. Alright. So I can see that's making the request.\u003C\u002Fp>\u003Cp>I get the data here. There's our content. Do we have the YouTube URL? That's great. For some reason, it's not throwing the modal window.\u003C\u002Fp>\u003Cp>Right? Why is the modal not opening? Cannot read properties, undefined of YouTube URL. Alright. So we probably wrap this in, like, a computer or something.\u003C\u002Fp>\u003Cp>Selected content equals computed value. Alright. And this is going to be if selected content and we'll have selected content. Is that why? Alright.\u003C\u002Fp>\u003Cp>So it's actually throwing the modal, but why isn't that working? It's because I'm not passing the right stuff. Right? So it should be dot content dot YouTube URL. Is that gonna work?\u003C\u002Fp>\u003Cp>Where are you? Where are you? Modal. Title card. Where's the content grid?\u003C\u002Fp>\u003Cp>Our selected content. Content dot value. Oh, duh. Gosh. Forgetting to set the content.\u003C\u002Fp>\u003Cp>Content dot value equals content. Let's try it now. No. Still new. Okay.\u003C\u002Fp>\u003Cp>So I hate to waste everybody's time. Content dot oh, fetched content. Let's just try this real quick before I give this up. Equals fetched content. Holy moly.\u003C\u002Fp>\u003Cp>And there it is. Right? We have a Netflix clone. So a little after time, we made it actually render something, which is a win. Kudos to the Netflix team.\u003C\u002Fp>\u003Cp>Right? The truth of the matter is that it is very difficult to build Netflix in an hour. So I hope you enjoyed this episode. Like, what would happen next for me? Basically, continue to flesh this out, get the UI working and and wired up correctly.\u003C\u002Fp>\u003Cp>You know, if I wanted to copy Netlify or Netflix directly, I would probably make sure that when I loaded a title that was getting appended to the the actual query string in the URL. You might set it up on a different route that renders a model. But next steps would just be basically continuing down the route of fleshing out the the UI side of it and, you know, adjusting the data model inside our back end as needed. So that's it for this episode of 100 apps, 100 hours. The outcome, maybe we give it one thumbs up.\u003C\u002Fp>\u003Cp>You know, I'm sure Cisco and Evert would probably not approve. So I'll catch you on the next one. See you.\u003C\u002Fp>","Alright. Welcome back to the next episode of 100 Apps, 100 Hours. I'm your host, Brian Gillespie, developer advocate at Directus, and I'm super excited for today. We're gonna answer the question that has been keeping you up at night, can you build Netflix in an hour? You've probably not been keeping you up at night. But the truth? You can't handle the truth. I can't resist throwing in a good movie quote here for this one. So we are going to try to rebuild Netflix in 1 hour or less or publicly fail trying. Those are the rules of 100 apps, 100 hours. And then the second rule of Fight Club is use whatever you have at your disposal. So how are we going to achieve this? We are going to do a little planning, then we're going to do a little building. But just to get eyes on what we're going to build, here's Netflix. You know it. You've seen it. We are going to try to rebuild as much of this as possible in 1 hour. So that means the back end, that's where we'll start. Then we'll work on the front end and try to get as close as possible to the original Netflix or, my own spin on it as possible. So what do I have prepped ahead of time? I have a Nuxt application ready to go. I've also got an instance of Directus. Directus is what we're gonna be using for the back end. I've got it spun up. It is totally blank now and before we get started, I'll show you just so you know I'm not cheating. So we'll go to admin, example, we'll key in a quick little password. If I can actually remember what the password is. There we go. Okay. It is a totally blank instance, and if we fire up the database, again just to prove it, we've just got our directus collections that it adds for us automatically, so just the metadata. But as far as our actual data, there is not. So with that out of the way, I'm sure you guys trust me anyway, but let's get started. Right? We got 60 minutes on the clock. Boom. Away we go. Alright. So before we even get started, you know, maybe this is not the Netflix clone. This is, what are we going to call this, right? Give it a name. How about Dev Flix? Dev Flix, yeah, this is essentially what this could be. So this is Dev Flix, let's strike through this. This is what we're going to be building. And anytime I build a project, I I like to do some concepting first, maybe some diagramming, just so my brain is warmed up and I understand what's going on. So we'll just draw some boxes here. Let's walk through the functionality or do we even need to cover functionality, right? We all know what Netflix does, but we've got a, browse a catalog of titles. Those could be a movie or a show. What else? View that content. View stream videos. Right? Stream Videos once title is selected. Pick From different categories. Yeah, I don't know. This is Netflix, right? We all understand the functionality involved. Alright. So let's start working on the data model. Right? One of the ways that I will usually try to deconstruct application is just open up the JavaScript console and or the developer tools, and not necessarily the console. And then take a look at, like, the network requests that are coming in to see if I can understand what's going on behind the scenes. You know, I don't know that Netflix does Netflix have an API? Does it close its okay. So they closed their public API. But if we look, we could see there's a ton of calls going out to a player, but I see here's like some GraphQL stuff, some videos, artwork. I'm not really sure if this is gonna get me anywhere behind the scenes. They spend a lot of time on security and things like that and, you know, performance. So maybe they're doing some server side rendering and and things like that here to actually display this content. No worries. Right? But let's walk through and and just kind of think through things. Right? We've got what do we have inside Netflix? We have a, like, a title, like a content title, or, we could call it content, but that doesn't seem right. It could be a movie or a series. So let's just call it titles. Right? These are movies, series. There's a name for that, description, probably like a rating. You know, there's a lot of things. So we just wanna capture, like, the core stuff here. Alright. So we got titles. What else do we have as we go through? If we look at My Little Pony here on the screen, we've got our episodes and seasons. So if it's a show, we've got a season. Season has a number, probably, what, a release date for the season. And then it's gonna have a title ID and probably episodes. Right? Episodes. Now behind the scenes, I don't know exactly how Netflix Netflix has set up, like, their video and, you know, like, the the encoding and and streaming and all of that. I'm assuming attached to each title or each season, each episode. Alright. What do we have? Oh, sometimes you just accidentally push command q and totally destroy all of your progress. Alright. So how are we gonna structure this? Right? This is probably the most important thing to get right. Each episode is going to have a episode number, name, description. It's gonna be back to, what, a season, season ID. And then there's almost like another relationship here, which is just the actual content. So this is just as, like, the actual file. We'll probably just use, like, a YouTube URL in this case. Okay. So these are our kind of main collections. You probably also got things like ratings, users. Directus gives us that out of the box, which is really nice. So we've got Directus users. And, you know, we could have something like viewing history as well. User watch history. You know? We're getting really deep. Like, the main functionality is just up here where we have these items. Right? These are the core pieces of this application. Alright. Shoot. I forgot to start the clock. How long have we been doing this? Let's just give, like, 55 seconds or 55 minutes. Or I did start the clock, but we deleted Figma. Right? So let's handicap it a bit more. Maybe 54. Okay. Alright. So, we'll go back and look at that during editing, but, yeah. Sorry. Mistakes happen, especially when you're against the clock. Alright. So now that we've kind of got a rough idea of how we're gonna structure this, I like to work back end first because when I'm building my front end, I love having access to the actual data instead of lorem ipsum, dipsum, and then I have to wire everything up later. So with this stack, Directus and Nuxt, I can quickly scaffold my back end and get instant rest and GraphQL APIs, and then plumb that to my front end as I'm building the front end. So, it's a a really great workflow for me. I hope it works well for you. Give it a shot. Alright. So let's go in and create our first collection. We're gonna call this titles. And I'm sure somebody will tell me why this is wrong, why it is not. So Directus gives us a couple of system fields that we can add. These are just little helpers, like, date created is automatically populated with a time stamp. User created is automatically populated with a logged in user that created that. Do we have a sort for those? And I can already tell I forgot one big one, right, which is gonna be genres or categories. Like, we've gotta have some kind of taxonomy for those. Alright. So we've got our title. What is the type of this? Right? So it's either gonna be a movie or a series. Let's use our radio button interface for that. So this is gonna be the type. The first one will be movie. So it's either a movie. Let's say we default to movies. This is gonna be series. And and Directus also has built in translation strings, which is another nice thing that I could go in and create these translations where here's the key. Let's call it what? Series. Right? Series. And in English, this is gonna be let's just capitalize this field anytime we render it. Right? Cool. Alright. So then I could do something like this where I have dollar sign t and do series, and that will automatically translate that for me based on the strings that I've got set up, Which is really nice if you've got users across different languages inside your Directus instance inside the back end. Alright. So we got the type is either a movie or a series. Let's add an input for the name of this title. And I can even go in and add like a little helper. What's the name, in this notes section? What's the name of the content of the movie or series? Right? Or series. Great. What else do we have? We got a description. And let's just go for, like, a rating. So we got a drop down. Let's call it rating. Or, you know, we could probably have, like, a ratings collection here. I was probably thinking this is like reviews, but, let's just take a look and see. Right? This says this is TBY. Do we wanna be able to query by that? Right? So in that case, let's do a many to 1, and we'll call it a rating. And we're gonna have Directus create a new collection for us in the background. So everything that's going on here, I feel the need to explain this, Directus is actually mirroring this inside my database. So as I create these new fields, as they're called inside the Directus application, it is actually mirroring those to my SQL database. So we'll call this ratings. And if we go to advanced mode just to take a look at this, maybe we zoom back out a little bit. But I could see that Directus is telling me that, hey. We will create this inside your data model. Great. So, it's gonna create that for me. And then we've got a mini to 1 relationship. Great. Okay. So we got the rating. And now if I look, we've got our ratings collection created as well. Alright. So we'll go in, let's add a rating, the name for the rating. I'm not sure if that's what these are actually called or not. And let's just create a couple of these. Right? Like pg13, TV y. Is that what I saw? TV y. Yep. TV MA, rated r. Yep. Alright. So we get the the picture. We could call it PG, and then we have G. Right? Some of my favorite movies are still g rated movies, to this day. Of course, I'm a father of 3 as well. Alright. So we go back to our data model. Let's build out the rest of this. We've got our titles. So if we are creating a title now, like My Little Pony, now we can see what the status is. Is this a movie? Is it a series? What's the name, description? And we could pick a rating that we can then update through the system. And let's just adjust the display template for this because I wanna show the name anytime we're referencing that rating. Alright. So I'll zoom back in just a bit, and we'll build out our next piece of the puzzle here. Let's work on seasons. Alright. So we'll automatically generate a UUID. Maybe we wanna store who is the user that was updated this just so we can play the blame game later. Cool. Alright. And then we've got a number. So let's do an integer. We'll have the season number. Okay. And inside Directus, I can also make that the sort field. Where are you, sort field? Alright. So we'll choose the sort field. The season number is going to be the sort field. So whenever we reference this, it will sort by that number automatically. What else do we have? Do we have a description on the sort number? I I don't know. Release date, you know, this theoretically could be on the actual episodes as well. But Netflix does like to drop a season at a time, so maybe we store it here as well. Let's just use a time stamp so that it saves the the time zone value as well. Alright. What else? We have a title ID. Right? So we have to create that relationship back to the title so we can fetch that data. So this is gonna be a mini to 1. Right? A season can only belong, like, to a a single content title. So if if the show is My Little Pony, there's 5 seasons, all those seasons belong to 1 My Little Pony title. So we could call that title ID or title, I prefer less verbose, but it doesn't really matter here. Directus is going to serve up that however we create it. Alright. So we've got that. Let's add the one to many relationship. So we're creating a many to 1, but on the reverse side, a single title could have many seasons. So let's go ahead and create that relationship as well. And for the season we'll use the oh, for our many to 1 we'll use the name of that and then we'll do related values. Okay. So we got the title, we've got the number. You know, we could have a description for this season if we wanted to. You know, there's probably even, like, some collateral or trailers or or something attached to this. We won't worry about that right now. Let's save this, and then we'll go in and create episodes. So in our diagram over here, we probably should have had episodes to begin with. Alright. So same thing. I'll I'll just add my system field. So if anybody updates this information, we've got it. Is this episode published or not? Yes or no. And then we'll give this a name. What's the name of the episode? We probably also have a number for this episode as well. Episode number. And, you know, something like this, you may even call, like, episode number just to make sure everything is very clear, but I'm all for making things difficult. So we will add a description. And now we'll link this back to the season. Right? So we're going to use that many to one relationship again, and we're going to link this back to a season. The related collection is seasons. And I'm going to open up advanced field mode here in Directus, and I guess I could actually use my little mouse pose tool for you guys so we can get an idea of what's happening here. So I'm gonna add the reverse relationship back to seasons. So one season could have many episodes, 1 episode has or many episodes have a single season. Think I explained that right. Sounds great. Alright. So now we've got titles, we've got episodes, we've got seasons. If I look, we've got titles. So let's go in and create a new title. Right? So we'll just drag and drop Netflix over here. And let's just work on this. Right? My Little Pony, Make Your Mark. Alright. So this is My Little Pony, Make Your Mark. Alright. Maybe there's a hyphen there, a dot, I'm not sure. That's the season description. Right? Where do we see all episodes? Alright. So here's the there's kind of the the generic description that apparently Netflix does not let me copy. Alright. So welcome back to Equestria, where pony magic is everywhere. Who doesn't love pony magic? Alright. There's our description. This is TVY. Alright. And I can already see we've left out a couple things. Right? We've left out some thumbnail images, probably on each episode, each, title as well. But we'll go in and create a new season. They're calling this chapter 1. So we probably have a name for the season as well that we can add. Let's just say the release date is today, and we'll start adding some episodes. Right? So we got Make Your Mark. That's episode number 1. Something's wrong in Equestria. And now we're actually watching Netflix. How do I go back? Okay. Alright. Is there a way to actually I I guess they disable that. I'm just gonna use Raycast here to generate descriptions for this. Alright. So we'll say this is published. Alright. So at the very least, now we have some stuff that we can work with and fetch via the API that we can render on the front end. I'm gonna go back really quickly and just add a thumbnail for this title. And we could even do that for what, the episodes as well. We probably got, like, a thumbnail. There's probably also, like, a teaser. And while we're at it, let's just go ahead and try to finish out what we were building on the back end. Alright. So then we've got the actual content. So creating this separate collection allows me to standardize this across episode or, like, TV and movies. So, let's go in and create the content. You know, this could be video, I guess. Yeah. I'm not sure. Let's just call it content. And I'll probably figure out a better way to do this at some other point. Alright. So we got the content. We've got a just use a YouTube URL for this. We're gonna cheat a lot. But this could be like an actual file that we uploaded to our back end here that we serve. Again, I don't want to get into, like, the streaming stuff because I like efficiently serving video at different bandwidth, at different resolution. That's a whole ball of wax. I'm sure there's a great API for it. And Directus will manage some of those assets for you in a nice way, but it doesn't do video encoding and things like that that you need to deliver those streams effectively. Cool. So we've got, what, a YouTube URL. There's the content. This is gonna have a title associated with it. Titles. K. And then there's probably what? Episode as well? Episode. Titles. Okay. Alright. So we got a title. We got an episode. We probably should have created those reverse relationships, but we could go in and do that now. Alright. So we'll call this the content. That'll be our content collection. Episodes. And now we'll just create the reverse. Uh-oh. What am I doing wrong? I'm doing a mini to 1 back to that when, actually, let's just go in and I'm gonna delete these for now just to make sure we set these up correctly. Alright. So I'm gonna go let's go to the titles. We'll do a many to 1. We'll call this content. We'll go to the content collection. And here, I am going to go into the relationship. We'll add this to the title. Okay. So we'll add that reverse one to many relationship. We'll call it the title. I think it is going to be an array. I guess you could have multiple pieces of content attached to any one item, but no worries. Content. And then we're gonna have episode. Okay. Okay. Cool. Cool. Cool. Alright. What's next? Right? We probably need some genres. Let's go in genres. And if there could be multiple genres per piece of content, I assume, like, could be drama and horror or drama and faux drama. If you live in my household, I've got plenty of drama. So we'll give this a name. And then let's go in, and we would create a junction table between titles and genres. Right? But, again, one of the nice things here is Directus is gonna do a lot of this for us. So we could call this genres. We do genres as the related collection. And if we look, we go to the advanced mode inside Directus, we can see that there's gonna be a junction collection or a junction table in our database created. Do we want to be able to query by genres? Maybe we don't need that. Do we add a sort field? Probably not. Okay. So now we've got genres. We've got thumbnail. We got a lot of different items set up in this database. Right? And we're closing in on 30 minutes. We probably need to start building some UI soon. But I do wanna just open up the database itself and and take a look at what's going on now. So you'll see these direct us tables. But if you look, you can also see our our actual tables that we created, like episodes and genres. And if I look at the data, here's the episode that we created, make your mark for My Little Pony. So this is great. Like, anything that happens inside the app here, Directus is mirroring that to your database. Likewise, if I go in and, there's very robust access control here. I'm just gonna enable this as a public permission for all of our content, which, would probably not be a great thing security wise, but I just want to show you this. We'll go into local host 8055, which is my back end. And if I go to items and I go to titles, boom, I'm gonna get My Little Pony. Right? Now, this is not super helpful because we're just getting you know, like, let's say I I just wanted to make a single call and get all my content. I love Directus for that because it gives me an interface via REST that feels like GraphQL. So I get the benefits of GraphQL and then I can go in and do something like this, like, hey, I just want the ID, I want the type, I want the name, I want the description, Boom. And it will give me exactly what I asked for. Now, the other thing that I can also do is go in and if I want something like seasons, and I I could use a wildcard to grab all the root level fields of the the next one, right, but I can get all of my related content in a single API call. That's super powerful, super helpful. Obviously, like, the deeper you go, the slower the queries on the back end are gonna be, but it is still very speedy. And, obviously, I don't have a ton of data here. So we got seasons dot star, and then we could do seasons dot episodes dot star. And that should give us the episodes. And then once we add content, we could go even deeper and say something like seasons dot episodes dot, content dot star. Now there is no content at this point, but if there were, it would come back. Great. So that is just one way that one of the major things that I really love about directives. Alright. So we've got some stuff going on. Let's go in and populate a few more titles. Right? We've got a thumbnail. I don't think they're gonna let me lift this. So let's just do the old screenshot trick. Upload that image. Click to browse. Should actually just be able to drag and drop there. Okay. Let's create a new content for this. We'll call it YouTube. Pull you up, My Little Pony. And again, I I don't support any of this. Make sure anything that you are doing is okay legally. We'll just copy this. K. Great. We've got a movie. Actually, this is a series. Right? But one thing I noticed here is if I'm switching between movies and series, we should probably actually not show our seasons. Right? So how do we actually adjust that? Well, if we go into our titles, Directus has the ability to do, relational or, I'm sorry, conditional fields. So I could go in and for seasons, we could do something like this where we hide this by default, and then we come into our conditions tab. And if type equals series, so then we build a rule. This is just a description for this. We'll do type type equals Series, then we are going to make this not hidden. Right? And we'll show a link to the item. Did I get that right? Yep. Okay. Alright. So let's test this out just to see what's up. Let's go in and add another one. Right? And now where I have movie selected, nothing. But if I select series, now we're seeing that field. It doesn't change the underlying database structure or anything. It's just hiding that when I'm creating the content. But I could even add validation around it if I wanted to. So if we go in and we add a movie, let's see if we've got a movie. I could switch up here to the top to movies, and we open this up. We've got a trash truck Christmas. Christmas. Alright. This is TV Y, made for TV, made for TV goodness. Alright. We will create some new content for this. Where's our handy YouTube? Trash truck Christmas, full episode Netflix Junior. This looks like an official channel. Yeah. Okay. We'll see. So we got that. We've got a thumbnail image. Again, I'm not even gonna try to lift this from oh, actually, I I guess I could. Copy image address. Is that available? On the web? Okay. Cool. So we'll just lift these images directly. I can import that file. Great. There that is. And I could, you know, maybe potentially find their GraphQL, stream for this that allows me to pull all this in, in a a more timely fashion. But, okay. Genres. Right? Let's create a new genre. What are we calling this? This is kids TV. There's a new genre. Okay. We'll create a new kids and family movies. And this will be TV cartoons. Great. Okay. So we got a movie. Let's switch this back to a series. Right? Lots of series because we've got seasons attached to it. Got some content for it. Is it is that gonna be right? Actually, it's not, is it? We're going to go back to My Little Pony. We'll go into the seasons. We've got episodes, and we should have content for that episode. Cool. Alright. So let's just see what this all looks like. Great. I could even go in and adjust this to, like, something akin to Netflix if I wanted to where I could see, like, the thumbnails of all these different titles. So give it a name. The subtitles could be what? Description. What else do we have? Or it could also be can we get genres in here? Genre genres ID. Let me get the name of the genres. Cool. Alright. So we got a couple titles. We are what? We're pretty I'm pretty happy with where the back end is at at this point. You know, we could really go through this in a lot more detail and create something like actors, you know, the cast and crew, director, things like that. And those would probably all be different database tables. But for the sake of having something to display at the end of this next 28 minutes, let's just get to work on the front end. Alright? Cool. So we will let's put you back over here, Netflix. Close you. We'll close our database. Let's pull up our Nuxt application, and I'm just gonna swap these around. No real rhyme or reason. I just like my text editor being on the right side. Alright. So now let's pull up our Nuxt application. Oh, this thing is a beauty, right? So, I've just got a preconfigured, like, a Directus Nuxt module set up that basically just wraps the Directus SDK with a little bit of auth. You know, we may not even dive into that. But, and then I've got a use direct as composable that allows me to make requests easier. There is a a little bit of setup work that I've done here, but it is really just, one of our boiler plates HSC OS that I have taken and stripped back for the purposes here. Alright. So we go into our index page. Alright. So we let's look at, the homepage here of Netflix. And we've got kind of this setup of a header. We've got a grid of titles sorted by genre. Alright. So let's dive into this. The first thing that I'm going to do, just because we are short on time, is take a look at, Tailwind UI. Advanced. What's going on here? Okay. Let's just look at Nux UI as our our library here. Something wrong with my laptop at this point. Alright. So inside this boilerplate that I've got set up, I've got a little, I've got I've got this Nuxt UI library, which is kind of built on Tailwind as well, that gives us some nice things like modals, some of the trickier components to actually build. Right? So we go in. Let's try Tailwind UI again, now that I've refreshed a little bit. And, alright, so what are we looking for? Let's grab a, like, a header from Tailwind UI. Alright. This looks pretty good here. So we've got the view version. I'm just going to copy this down. Alright. And let's go in and create a header, error component header dot view. Alright. Are we gonna have a logo for this, right? We've got our logo. Can we pull that up? Will this let me copy as PNG? Can I export this as JPEG? Nope. Let's just do a new Figma file actually instead of FigJam. Can we paste that? If we copy, Can we paste? Devflix. Yeah. Alright. Let's give it some, like, type of little icon treatment here. So we'll add a real original logo here. Looks like a play button. We'll change the fill to red. Right? Okay. And maybe we, what, let's wrap this in. Let's give it a little oh, yeah. I'm I'm gonna be giving design courses after this as well. Alright. So we'll take this. We'll export to SVG. Let's just do logo. Great. Alright. So we've got this header component. If we go back to Netflix, what do we got at the top? We've got, home. That'll be our index page. This is for our navigation. Right? So we've got TV shows. Alright. We got movies. Great. Okay. Cool. I don't have Headless. I don't have heroicons in here, so let's just comment those out for now. Bars 3. I'm sure that'll throw a fit when we actually take a look at this. But cool. Alright. Now I'm gonna create a new file called logo dot view. And we're just gonna copy this as, what, SVG. Paste here. Nope. I need to wrap that in a template, though. Alright. There's our SVG. Now inside our header component, I'll go back up here to where we have Tailwind UI's logo. And I'm just going to call that logo component that we just had. Don't need a source. Don't need an alt tag. Well, we probably do, but not going to have one. 100 apps, 100 hours. Okay. So we have a header. We're not seeing it here. What I can do is adjust the default layout. And let's do something like this where we have BG gray 900 and probably text white since we're kinda going on the Netflix theme. So this is the layout inside Nux, which is a feature that I really like about Nux, is the ability to set up a default layout. And boom, we've got our DevFlix header looking nice. If we go back to that header, probably wanna swap these a tags or a Nuxt link and change this to 2. And we could change this to TV shows and movies. Right? We just have to create a route for those. And I'm not sure exactly how far we'll get because we have, what, 21 minutes remaining. But we'll we'll see how far we can get. Alright. So if I go into the header, we need to update the details for it as well, like bg white. What do we what does Netflix look like on the header? It's just kinda transparent until you scroll down. That's a nice effect. I don't want to set up anything there. So let's just cheat. We'll call it bggray900 again. And then that's gonna mess up a lot of our text, I believe. Right now, we can no longer see all of that. So we go to our text. Let's look at anything with text gray. Can we replace that with we could probably do smaller values, like text 200. Again, I love Tailwind just because it enables me to not have to write custom CSS for everything. Like, it it does take some time to learn the conventions, but but being able to not write CSS, I like custom CSS, just helps me speed along. Now I know it's not for everyone, so don't let me tell you that it is, But I enjoy it. Alright. Can we get our stuff? Okay. Alright. That's probably because we're missing an icon for our navigation over here. So what is this? This is hidden. Flex LG. Let's hide that at, like, just a smaller breakpoint. Yeah. Okay. Cool. Alright. So we got home. We got TV shows. We got movies. We can log in. Great. Now let's actually work on our page. Right? If we go in, let's actually render some stuff out. Alright. So if I look at Netflix, we've got a, like, a header component or, like, a hero image. So we go for our hero sections. Hero. Which one of these kinda looks like Netflix? Kind of this one? Kind of. I mean, this one has a background image. So we could take that. Okay. So we're not gonna copy this header piece, but let's just go in. I'm gonna copy this part. We will create a new one called hero dot view. I've got a just a couple of snippets already set up for me inside Versus Code to make creating Vue components a little easier. And k. We're good there. Watch chapter 6 now. Is this just kind of a rotating thing, probably based on, like, the last thing that you watched? Instead of doing that, let's do something else. Data. Alright. Alright. So this is gonna be justified center. We don't wanna do that. We wanna do does it justify start, I believe? K. Oh, we'll add this to our index page. So let's take a look at this. We'll just wrap this in a div. We'll go to hero, call in that component. Let's see what this is starting to look like. Alright. Okay. Okay. We got a little something going on here. I only succeeded in moving one thing to the left. So let's take a look at that hero component one more time. Alright. So this has got some type of gradient blur attached to it for the images. Can we lift this? Oh, yes, we can. Netflix. Love building on the fly. Thank you. Netflix. What else do we have? The overflow hidden. The the the announcing. Do we even need this? Let's let's just scrap that part of it. And we'll just show watch chapter 6 now. And I still can't copy this stuff. Come on, Netflix. Copy. Copy selector, copy element, edit as HTML. All right. So we'll just pick this up and throw it here. And we got the My Little Pony logo, so we'll add in Nuxt. We'll just add in an image tag because I don't have that domain configured. Image source. Alright. Class. I don't know. Maybe height 24. And an image tag, welcome back to Equestria Tech Center. This is not gonna be Tech Center. Okay. We're getting somewhere. How do we dim this image? Right? Brightness 50? Oh, wrong image, Brent. Brightness, 50. Maybe 25. Oh. Can we do a percentage here? Get, like, a a nice value. Okay. Alright. So we're getting closer. He likes it. Alright. Get started. Let's say watch now. Okay. Let's remove that one. Alright. So at least we got some type of header here. This would be a NuxLink. And, I'm trying to think of how we're gonna serve this content. Right? We would do a page. Let's add a new folder for this. This is gonna be what does Netflix do as far as routing? Right? If I open this up, episodes and info. Okay. So is it just like a genre? Is that how how the URL structure is? Because you watch Masha's spooky stories. So we got the genre. If I look at the URL again, yeah, it's just like Netlify Kids, and then there's a query param. Alright. So it actually appears to be inside the the actual application. Right? So we're just appending, like, a query per annum to it. Let's come back to that step. Let's actually render out some titles first, though. Right? So we go back to our page. Let's create a new component. What what do we gotta call this? Content grid? Sounds great. Vcomp ts. Okay. So now we've got a a Vue component. Let's do some data fetching. Right? We will do something like this where, we got constant data equals use async data. So this is the built in Nuxt data fetching. We give it a key. Let's call it content grid, comma. Alright. And then we're going to return something. So in this case, we're gonna use our directus composable that I've got inside this. But if not, I could import the Directus SDK, fire that up, but, we're on the on the clock here. So alright. So we're gonna read our items, and we are going to read titles. So and then we're gonna get, is there any specific thing that we wanna render out? Let's just leave our options blank for now. Right? So we got our data. Let's just go in and make sure we're getting that data back. Sounds great. I don't see it on the screen. Why not? That's because we're not actually including it in our index page. So we'll just do content grid. Great. Okay. So at least we could see a few things here. That's great. Do we really need anything else to render this grid? Probably not, actually. This shows, like, the viewing data. We're we're definitely not gonna get to that. We got 12 minutes left. Let's just see if we can actually render something out. It shows something in a modal. All right. So we got our content grid. Great. Cool. Let's go in. I'm not sure if Tailwind has a great looking component for this. Gridless. Kind of. Yeah. Let's just create something on the fly. Right? So we'll go in. Alright. So that's the wrapper div. Then we'll do, we would probably fetch this via genre as well. Again, running short on time. Alright. We are going to do a, let's just keep this simple. Right? Add add a little bit of padding to the sides. We'll give this a grid. Grid calls, 5 across the screen, 6 across the screen. And then we'll do, let's do a, what, title card? And we will pass so we'll do some v four action here. V 4 title and data. The key is gonna be title dot ID. And then we have our, what, We're going to pass a title, title equals title. All right. So now we're going to create a new component. Call it title card dot view. And we will define some props. So props equals define props, Title object is prop type. Haven't set up our types here even though we're still using TypeScript. Alright, so we've got a card. I know Telen has like a card definition. Actually, the Nuxt UI library has a card as well. Let's just use it. So we got a card. There's a card. U card is the name of this. U card. And what do we have inside the card? We have, I don't know if these actually need to be h tags or not. We'll just use a p tag. Got the title dot name. If I can actually type this. Right? We've got an image for the card. Source equals title. No, no, no, no. We're gonna use Nuxt image because I have it preconfigured. And I'll just show you that really quickly. If I go to Nuxt Config, ba ba ba ba, where are you? Where are you, mister Image? Image. Okay. So Nuxt Image Directus is a provider here. We just give it a base URL that communicates with our Directus instance and allows us to do image transformations on the fly, which is really nice. So we got source title dot thumbnail. K. What do we do? Like, what? With full height 24 maybe? Sounds good. We got a title. Let's make this font bold. I'm assuming that's what it looks like. Oh, no. It's actually just a card. Right? Alright. Let's see what this looks like. We'll go back to our index page, content grid. Why don't we have our content? Why is content grid not rendering anything? Do we save it? What's going on here? Dev server. Why are we not rendering? Title card. Content grid. There is no data. What are we doing there? We just had our data. Right? Am I being silly? Pre data. Is that not showing? Did I break something? What is going on? Use, async data, content grid, titles, read items. Where did you go? Did I not just have this, like, a minute ago? Title. Title equals title. Hydration mismatch. If I look at content grid, like, we should have data. Right? Let me just look at the Nuxt fetching. Sometimes I even I use Nuxt all the time, and I'm not sometimes I have to go back to the oh, we gotta wait. Yeah. I don't know how that worked the first time. Right? Next image not showing. Okay. So we're missing some images there. Why are we missing images? So we are missing images because, let's do let's add our grid back. Grid calls 6bg grid. Do we need an actual BG? Okay. Maybe grid calls 4. That's fine. How are we doing on the time? We got 6 minutes left to actually render something up. Px8py12. Just pad this out a little bit. Alright. What's next? Our our images are not displaying. Right? How do we fix that? It is actually gonna be inside our Directus instance. So it is a permissions issue. Pretty sure if I were to take a look at our network requests, look at it like images here, you have 403 forbidden. So, I would go into not my docs. We'll open up the Directus instance. Go to 8055. And I somehow managed to log myself out as well. Alright. We go into access control. We've got our content here, but then we're gonna go in and allow access to the direct us files collection. So that way we can actually serve those images on the front end. Okay. Great. Maybe title card wasn't a great idea there by using the Nux card. Right? Let's just call it div. Oh, forgot to close that. Alright. So there's the diff. This is going to be Object, Cover, Trash Truck Christmas. Maybe we need to make that a little higher, 32. Alright. How we doing on time? 4 minutes. Can we get this to actually render? Alright. Button at click. Alright. Quick and dirty. Let's do this. We're going to add a composables. Actually, we could just pass this up as well. Alright. At click, we're going to define some omits. So omits equals define omits. We're gonna tell this select title. Select title? I think that's the syntax. Alright. Alright. Select title. Emit. Select title. And we're gonna pass the what? Prop no. It should just be title dot ID. Is that right? Or does that do I have to wrap that? No. Where are we getting here? Select title does not exist. Let's just call this function. On click, emit select title, props dot title, on click. Cool. Okay. And then inside our content grid, let's do this where we've got a modal. K. And we've got is open. That's false. Select title. And then what are we gonna do for the title? Let's do title, maybe content. Right? Content ref equals null. GitHub Copilot here for the win on this one. Right? Do we have a video component? I don't have a video component. So we're just gonna do, what, an iframe, class, aspect, style. Let's call it video. We'll just add a quick style here. Dot video aspect ratio 16 by 9. Alright. The well, the source is gonna be the content dot YouTube URL. Is that actually gonna work? I think I've got do I have some strings in here? Do I have a video content? I don't. Alright. Let's rely on AI here. Generate YouTube embed. Okay. So that returns the video ID. Let's see what AI comes back with. YouTube embed video ID. Okay. Great. Alright. So there's gonna be our source. We'll say generate YouTube video embed URL. Oh, nope. That's YouTube embed URL. Like, somewhere in the starter, I may already have this. Generate YouTube embed URL, and that is gonna be the content dot YouTube URL. Alright. So what am I missing? I'm gonna need to close that. Okay. And then on our select title, right, we need to fetch the content for the title. Alright. So that's gonna be what? This is gonna be an async function now. How are we doing on time? We have hit time. So we ran out of time to actually flesh this out and render this content in a model, but I just wanna try it. Let's just pursue it really quickly and see where we're at. Constant. We are going to we don't need to actually use async data for this. We're gonna fetch this on the client side. So let's do constant title equals wait. Use direct us, read items, title. Read item. Is it gonna be a title? We're gonna fetch through the we got the root level fields. Let's take a look at that data model again. Right? Local hosts. Alright. So we got titles, then we've got content? Alright. So root level, we'll go content dot that. Select title. Title has already been declared. Oh, okay. Where do I have that at? Oh, I've already given it the title. Content. Alright. So if I click that, is it actually making the request? Select title. Oh, and then for the title card at select title, we're going to select title. Alright. So I can see that's making the request. I get the data here. There's our content. Do we have the YouTube URL? That's great. For some reason, it's not throwing the modal window. Right? Why is the modal not opening? Cannot read properties, undefined of YouTube URL. Alright. So we probably wrap this in, like, a computer or something. Selected content equals computed value. Alright. And this is going to be if selected content and we'll have selected content. Is that why? Alright. So it's actually throwing the modal, but why isn't that working? It's because I'm not passing the right stuff. Right? So it should be dot content dot YouTube URL. Is that gonna work? Where are you? Where are you? Modal. Title card. Where's the content grid? Our selected content. Content dot value. Oh, duh. Gosh. Forgetting to set the content. Content dot value equals content. Let's try it now. No. Still new. Okay. So I hate to waste everybody's time. Content dot oh, fetched content. Let's just try this real quick before I give this up. Equals fetched content. Holy moly. And there it is. Right? We have a Netflix clone. So a little after time, we made it actually render something, which is a win. Kudos to the Netflix team. Right? The truth of the matter is that it is very difficult to build Netflix in an hour. So I hope you enjoyed this episode. Like, what would happen next for me? Basically, continue to flesh this out, get the UI working and and wired up correctly. You know, if I wanted to copy Netlify or Netflix directly, I would probably make sure that when I loaded a title that was getting appended to the the actual query string in the URL. You might set it up on a different route that renders a model. But next steps would just be basically continuing down the route of fleshing out the the UI side of it and, you know, adjusting the data model inside our back end as needed. So that's it for this episode of 100 apps, 100 hours. The outcome, maybe we give it one thumbs up. You know, I'm sure Cisco and Evert would probably not approve. So I'll catch you on the next one. See you.","14af204b-9a61-4836-bdc5-e7698b851d34",[5001],"58da3ed5-14f4-403d-94eb-3b3e2d10933e",[],{"year":2380,"number":177,"id":4162,"episodes":5004,"show":5005},[4164,4165,4166,4167,4168,4169,4170,4171,4172,4173,4137],{"title":322,"slug":323},{"id":4611,"slug":5007,"vimeo_id":5008,"description":5009,"tile":5010,"length":889,"resources":5011,"people":5015,"episode_number":213,"published":5017,"title":5018,"video_transcript_html":5019,"video_transcript_text":5020,"content":12,"seo":12,"status":19,"episode_people":5021,"recommendations":5023,"season":5024},"clarifai","898557806","Automatically tag new image files with Clarifai's Image Recognition Model.","60c0e3e7-7141-4121-afd9-fa25e460d161",[5012],{"name":5013,"url":5014},"Clarifai Image API Reference","https:\u002F\u002Fdocs.clarifai.com\u002Fapi-guide\u002Fpredict\u002Fimages\u002F",[5016],{"name":2820,"url":2821},"2024-01-26","Tag Images Automatically with Clarifai","\u003Cp>Speaker 0: Clarify allow you to train and use machine learning models via APIs. Today in QuickConnect, we're going to integrate clarify with directors using directors flows. So whenever we upload a new image to our directors project, we will go off to clarify, understand what's in the image, and update the tags in an image accordingly. Here's an image uploaded in my director's project, and we see here that there are tags. So what we'll be doing is populating this with what we believe is in the image based on a Clarify output.\u003C\u002Fp>\u003Cp>So with that, let's get started. When you sign up for a Clarify account, they'll prompt you to create a project, which is totally fine. Projects contain individual permissions and API keys, which you can manage within the scope of the project. Now for today's, today's example, we're actually just going to use the account level personal access token. So even if you don't have a project, head over to your user settings to the security section and copy this personal access token here containing all scopes.\u003C\u002Fp>\u003Cp>Just a reminder, other people shouldn't see this key. And if people do, you can go ahead and delete it and create a brand new one. So that's what we need to do here. Let's head over to our director's project and set this up. In your directors project, create a brand new flow.\u003C\u002Fp>\u003Cp>I'm going to call this one tag images with clarify. It's going to be a non blocking event hook with a scope of files dot upload, which means this flow will automatically be triggered whenever a new file is uploaded. Let's actually test that out. Let's upload this nice image of a curry that I had recently, and we'll head back to our flow here and refresh. And we should see over in logs that it has been run one time.\u003C\u002Fp>\u003Cp>Now inside of the payload, we see that we have this object. The object has a key. So this is a unique identifier for this file within the director's files collection. And there is this payload object with a bunch of properties, including a type. Now this is important because this flow will actually run when we upload any file regardless of file type.\u003C\u002Fp>\u003Cp>So the first thing we're gonna wanna do is just filter this down to make sure this flow only continues if the file is an image. So let's create a new condition. I'll call this one is image, and I'll use the following condition rule, which just means that the type has to contain the word image. That means it doesn't matter if it's a JPEG, a PNG, a webp file, a GIF, whatever, as as long as it contains the word image inside of the file type, which it will for any of those types, we can continue. Now we're gonna go ahead and actually call the clarify API.\u003C\u002Fp>\u003Cp>So let's create a new operation, and we'll call this one clarify. We'll be making a web request. It will be a post request to this specific URL provided by the Clarify documentation. This is a built in model that they provide called general image recognition, and we're gonna be using this specific trained version of the model. Once again, I've grabbed that straight from their documentation.\u003C\u002Fp>\u003Cp>Now we need to authenticate, but it is us. So authorize station authorization. Yeah. Key and then our personal access token. Finally, we need to actually provide the URL of the image we want it to interpret.\u003C\u002Fp>\u003Cp>It's a little bit of a convoluted object here. So I've copied and pasted our starter. But what's important is that every image has a direct URL that we can provide to it from the director's project. That is the full URL of your project slash assets and then slash the actual ID or key of that file. And we've already seen that during the trigger.\u003C\u002Fp>\u003Cp>It's called trigger dot key. Now this will work if the file permissions are public, but if they're not, we need to provide a way for Clarify to actually access this image. And we can do that by adding a query parameter called access token and then an access token of a user that has access to that image. So let's hit save, and let's try running that again. So let's just delete this image, and we will reupload it.\u003C\u002Fp>\u003Cp>We'll head back to our flow. We'll refresh it, and we will see the output of the second invocation of this flow. So if we head to the payload here, we can see a a ton of information about the model itself. What else have we got here? The input image that we provided here, and then all of these concepts, food, meal, dinner, no person, vegetable grow, and so on.\u003C\u002Fp>\u003Cp>Now every one of these also has a value between 0 and 1, which is the confidence that it is correct. Now what we wanna do here is we want to end up to create our tags, filtering out any that have 2 lower value and then creating just an array of strings. So the next step here is to create a an operation called transcript. Here we go. So I'll call this one concepts.\u003C\u002Fp>\u003Cp>Now that was a big object. I've obviously done a little bit of prep work, so we don't have to go, you know, traversing through this object. I know that the data we want is inside of data dot clarify because that is the name of this step that's hidden by this pane here. Clarify dot outputs, that is an array. We just want the first one.\u003C\u002Fp>\u003Cp>Dot data dot concepts. And each one of those is one of those objects that contains the name of the of the concept and the score and so on. So we'll return this, but we wanna do a little bit more work to it. Firstly, we wanna go ahead and filter. We only want concepts where the score or I think they call it the value, so where the concept dot value is greater than 0.95.\u003C\u002Fp>\u003Cp>And depending on your use case, you can decide what, what's the correct value is for you. Then we want to go ahead and map it. So we only get come on, Kevin, you can type. So we only get the name. So let's save this.\u003C\u002Fp>\u003Cp>Let's run this again, and we'll see the output of this concept script. And what we should get is just an array of strings of the concepts, which have a score of greater than 0.95. Let's just double check that that is correct here. So let's reupload the picture of our food. Let's refresh this and we should see another invocation here yes Oh, that was a little error there.\u003C\u002Fp>\u003Cp>So let's head back and figure out what's gone wrong here. Clarify dot outputs. Ah, I don't think this is right. I think it was actually nested inside of the data, the data value. Let's try that again.\u003C\u002Fp>\u003Cp>Let's delete this, re upload it refresh There we go. So there's our array of strings, which is fantastic. And this just leaves us with one final step, which is to actually update the tags of that of that newly uploaded images with those values. So let's go ahead and update image. Let's update data.\u003C\u002Fp>\u003Cp>This is gonna be inside of the Directus files collection. We'll give it full access. We only want to upload the IDs of the images which match the key of the starting image. It'll only update 1. And finally, the payload here is going to be tags, concepts like so.\u003C\u002Fp>\u003Cp>And we can do that because context was an array that was returned. So let's hit save. Let's save this off, and let's give this one final go. We'll delete this image. We will re upload it.\u003C\u002Fp>\u003Cp>And in theory, if I open it now, we should see the image is tagged, which you can then use in API requests in order to build applications on top of this data. So I hope you found this interesting. Clarify have a ton of trained models, so we can actually do a lot more with it. But in this small example, you can see how to get started, explore the Clarify documentation, and do even more. Until the next episode, see you later.\u003C\u002Fp>","Clarify allow you to train and use machine learning models via APIs. Today in QuickConnect, we're going to integrate clarify with directors using directors flows. So whenever we upload a new image to our directors project, we will go off to clarify, understand what's in the image, and update the tags in an image accordingly. Here's an image uploaded in my director's project, and we see here that there are tags. So what we'll be doing is populating this with what we believe is in the image based on a Clarify output. So with that, let's get started. When you sign up for a Clarify account, they'll prompt you to create a project, which is totally fine. Projects contain individual permissions and API keys, which you can manage within the scope of the project. Now for today's, today's example, we're actually just going to use the account level personal access token. So even if you don't have a project, head over to your user settings to the security section and copy this personal access token here containing all scopes. Just a reminder, other people shouldn't see this key. And if people do, you can go ahead and delete it and create a brand new one. So that's what we need to do here. Let's head over to our director's project and set this up. In your directors project, create a brand new flow. I'm going to call this one tag images with clarify. It's going to be a non blocking event hook with a scope of files dot upload, which means this flow will automatically be triggered whenever a new file is uploaded. Let's actually test that out. Let's upload this nice image of a curry that I had recently, and we'll head back to our flow here and refresh. And we should see over in logs that it has been run one time. Now inside of the payload, we see that we have this object. The object has a key. So this is a unique identifier for this file within the director's files collection. And there is this payload object with a bunch of properties, including a type. Now this is important because this flow will actually run when we upload any file regardless of file type. So the first thing we're gonna wanna do is just filter this down to make sure this flow only continues if the file is an image. So let's create a new condition. I'll call this one is image, and I'll use the following condition rule, which just means that the type has to contain the word image. That means it doesn't matter if it's a JPEG, a PNG, a webp file, a GIF, whatever, as as long as it contains the word image inside of the file type, which it will for any of those types, we can continue. Now we're gonna go ahead and actually call the clarify API. So let's create a new operation, and we'll call this one clarify. We'll be making a web request. It will be a post request to this specific URL provided by the Clarify documentation. This is a built in model that they provide called general image recognition, and we're gonna be using this specific trained version of the model. Once again, I've grabbed that straight from their documentation. Now we need to authenticate, but it is us. So authorize station authorization. Yeah. Key and then our personal access token. Finally, we need to actually provide the URL of the image we want it to interpret. It's a little bit of a convoluted object here. So I've copied and pasted our starter. But what's important is that every image has a direct URL that we can provide to it from the director's project. That is the full URL of your project slash assets and then slash the actual ID or key of that file. And we've already seen that during the trigger. It's called trigger dot key. Now this will work if the file permissions are public, but if they're not, we need to provide a way for Clarify to actually access this image. And we can do that by adding a query parameter called access token and then an access token of a user that has access to that image. So let's hit save, and let's try running that again. So let's just delete this image, and we will reupload it. We'll head back to our flow. We'll refresh it, and we will see the output of the second invocation of this flow. So if we head to the payload here, we can see a a ton of information about the model itself. What else have we got here? The input image that we provided here, and then all of these concepts, food, meal, dinner, no person, vegetable grow, and so on. Now every one of these also has a value between 0 and 1, which is the confidence that it is correct. Now what we wanna do here is we want to end up to create our tags, filtering out any that have 2 lower value and then creating just an array of strings. So the next step here is to create a an operation called transcript. Here we go. So I'll call this one concepts. Now that was a big object. I've obviously done a little bit of prep work, so we don't have to go, you know, traversing through this object. I know that the data we want is inside of data dot clarify because that is the name of this step that's hidden by this pane here. Clarify dot outputs, that is an array. We just want the first one. Dot data dot concepts. And each one of those is one of those objects that contains the name of the of the concept and the score and so on. So we'll return this, but we wanna do a little bit more work to it. Firstly, we wanna go ahead and filter. We only want concepts where the score or I think they call it the value, so where the concept dot value is greater than 0.95. And depending on your use case, you can decide what, what's the correct value is for you. Then we want to go ahead and map it. So we only get come on, Kevin, you can type. So we only get the name. So let's save this. Let's run this again, and we'll see the output of this concept script. And what we should get is just an array of strings of the concepts, which have a score of greater than 0.95. Let's just double check that that is correct here. So let's reupload the picture of our food. Let's refresh this and we should see another invocation here yes Oh, that was a little error there. So let's head back and figure out what's gone wrong here. Clarify dot outputs. Ah, I don't think this is right. I think it was actually nested inside of the data, the data value. Let's try that again. Let's delete this, re upload it refresh There we go. So there's our array of strings, which is fantastic. And this just leaves us with one final step, which is to actually update the tags of that of that newly uploaded images with those values. So let's go ahead and update image. Let's update data. This is gonna be inside of the Directus files collection. We'll give it full access. We only want to upload the IDs of the images which match the key of the starting image. It'll only update 1. And finally, the payload here is going to be tags, concepts like so. And we can do that because context was an array that was returned. So let's hit save. Let's save this off, and let's give this one final go. We'll delete this image. We will re upload it. And in theory, if I open it now, we should see the image is tagged, which you can then use in API requests in order to build applications on top of this data. So I hope you found this interesting. Clarify have a ton of trained models, so we can actually do a lot more with it. But in this small example, you can see how to get started, explore the Clarify documentation, and do even more. Until the next episode, see you later.",[5022],"b61f3dba-88a3-42de-9269-6d933731b5ac",[],{"year":2380,"number":194,"id":4605,"episodes":5025,"show":5026},[4607,4608,4609,4610,4611,4612,4613,4590],{"title":1746,"slug":1747},{"id":4543,"slug":5028,"vimeo_id":5029,"description":5030,"tile":5031,"length":993,"resources":12,"people":5032,"episode_number":264,"published":5038,"title":5039,"video_transcript_html":5040,"video_transcript_text":5041,"content":12,"seo":12,"status":19,"episode_people":5042,"recommendations":5046,"season":5047},"mateja-sela","896674382","In this episode, Mateja shares his experiences in founding and managing a company in the volatile cryptocurrency\u002Fblockchain sector, and the transition to a role in a larger organization.","cb8bc756-0496-4b47-b6c1-b66f59be60f8",[5033,5034,5035],{"name":4361,"url":4522},{"name":4524,"url":4525},{"name":5036,"url":5037},"Mateja Sela","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fmateja-sela\u002F","2024-01-25","Mateja Sela, Engineering Manager at Bolt","\u003Cp>Speaker 0: Yeah. When I was leaving the startup role, I was like, I'm gonna chill now. I'm gonna, like, take it easy. I'm gonna, like, you know and then I went to my job and I was like, I wanna keep this job, so I'm really stressed out. I really want to, you know, work with people and show that I'm worth it, right?\u003C\u002Fp>\n\u003Cp>So I think I'm never gonna be happy. That's the conclusion. No, I'm kidding.\u003C\u002Fp>\n\u003Cp>Speaker 1: Hey, everyone. Thanks so much for joining us for this week's podcast. With us, we have Matea. Matea, I'll let you introduce yourself so that you can give you your best introduction and representation of yourself. Why don't you let everyone know a little bit of a brief background of who you are, where you're based, where you're located, and then we can kinda get into the questions after that.\u003C\u002Fp>\n\u003Cp>Speaker 0: Sure. Sounds good. I'm Mateo. I'm a techie. I'm an engineering manager currently at Bolt, which is a ride hailing company mostly.\u003C\u002Fp>\n\u003Cp>We do a lot of stuff like food delivery, and, yeah, grocery delivery. So, it's a pretty big company. And, yeah, about myself, I don't know. I just ate, like, I cooked dinner in 5 minutes, and I ate it in 3. So I think I broke the record for, fastest eating and cooking of a meal after work.\u003C\u002Fp>\n\u003Cp>Speaker 1: Well, you know, real real quick inter real quick interjection. I used to work with a firefighter, and he was a record fast eater. He would eat a huge burrito in in probably 2 or 3 minutes.\u003C\u002Fp>\n\u003Cp>Speaker 2: Oh, man. I could never I need to sit there and enjoy it, but, yeah, I get it sometimes.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Me too, usually. But if you're here. You okay? Yeah.\u003C\u002Fp>\n\u003Cp>So sorry. That's it. Yeah. I'm I'm yeah. I used to work for Artano.\u003C\u002Fp>\n\u003Cp>I mean, as as a CEO of Artano. It's an NFT platform. I guess that's the main reason why I'm here. And, yeah, I mean, generally, I am into, yeah, just, like, trendy new technologies. I was into crypto, have my own theories around that.\u003C\u002Fp>\n\u003Cp>Like, yeah, we talked about it briefly, before about, AI bros versus crypto bros. So I I think that's a topic probably we'll have to touch at some point. Yeah. Absolutely. Yeah.\u003C\u002Fp>\n\u003Cp>I'm based in Estonia. Sorry. I forgot to say that. And I'm from Serbia originally.\u003C\u002Fp>\n\u003Cp>Speaker 2: Awesome. Sounds great. And what's your what do you do at your current role at Bolt?\u003C\u002Fp>\n\u003Cp>Speaker 0: So I'm an engineering manager. I manage a team of, 8 developers, mobile side, and, some Android, iOS, and back end. Yeah. And we are responsible for the app. So the, yeah.\u003C\u002Fp>\n\u003Cp>Yeah. I I don't think I can talk much about it yet because I'm new to the company as well. So yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Sure. Sure. Well, let's dive in a little bit into some of your previous experience. You mentioned you were a CEO.\u003C\u002Fp>\n\u003Cp>So let's go even further back from that. When did you first starting start getting into technology? Did you go to school for it? And then I guess we can circle back around eventually to to becoming a CEO, but I'd love to understand, how you got into technology. Was it from a young age, or did did Twitter get you into it?\u003C\u002Fp>\n\u003Cp>Did you see somebody say something cool? So, yeah, I'd love to learn how you first started getting interested in technology.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I think my dad is mostly to think for that. He's, somebody who introduced me to computers. He is a big Linux fan. So I was probably 4 years old when I ran my first games from Linux.\u003C\u002Fp>\n\u003Cp>He, like, forbade me to use the Windows. He was such an anti Windows guy. He was like, yeah, it's like the devil basically. And now today, you know, like, we think of these things a bit differently, but at the time it was like, you know, Linux is the best thing ever, and open source is the way to go. And, it's I guess that was the crypto of the time, right?\u003C\u002Fp>\n\u003Cp>And now we have a bit of a different, yeah, outlook on things. And that's like the first time. So I was like 4 years old, I think, when I first started, like, a kernel command. But I'm not like I don't think I'm like a project kid or anything. I just, like, learned by heart how to run a game because I want to play it.\u003C\u002Fp>\n\u003Cp>Speaker 1: So you were smarter at 4 years old around technology than than most people are even still to this day.\u003C\u002Fp>\n\u003Cp>Speaker 0: I don't think it's that hard to learn a command to write, like, open a kernel and write a command. But yeah. Maybe. I don't know.\u003C\u002Fp>\n\u003Cp>Speaker 2: Do you think it's any harder or easier than learning a language?\u003C\u002Fp>\n\u003Cp>Speaker 0: Sorry. What what running language?\u003C\u002Fp>\n\u003Cp>Speaker 2: Do you think it's any like, learning a coding language is any harder or easier than learning a, you know, verbal or written language?\u003C\u002Fp>\n\u003Cp>Speaker 0: It's an interesting question. So I'm somebody who likes learning languages. I speak fluently like French and German and Serbian. So and English, I guess. And I'm yeah.\u003C\u002Fp>\n\u003Cp>I think it's something that's very interesting to compare, like linguistics of a language and how linguists learn things from, you know, how how, you know, programmers or people who code, how we learn things from linguistics, between I think music as well. Music is very mathematical. But music, yeah, computer science and and linguistics are pretty interesting. And\u003C\u002Fp>\n\u003Cp>Speaker 1: yeah. Nice. Nice. I I speak English, and that's about it. So, same with when it comes to coding languages.\u003C\u002Fp>\n\u003Cp>I only know a couple. I was not as talented as yourself. So I I believe in our previous conversation, you mentioned going potentially to Oxford. So I'd love to understand what you went to school for. And did did your time at at university kind of lead you to want to start your own company?\u003C\u002Fp>\n\u003Cp>Or, I guess, how did that transition go from going from university to eventually starting your own company?\u003C\u002Fp>\n\u003Cp>Speaker 0: So, yeah, I went to uni in the US. I went to Middlebury College. I studied computer science and German language. I did liberal arts. So that allowed me to, like, spring into different things, right?\u003C\u002Fp>\n\u003Cp>Like, to to touch on different topics and mostly writing. I really liked writing, research. And yeah, so I went to Oxford for a kind of an independent study, independent research thing, in my 3rd year of college. And I was lucky enough to, you know, see this, how this world works. You know, you have a tutor, you have a 1 on 1 with a professor at Oxford, which is pretty incredible.\u003C\u002Fp>\n\u003Cp>And that's how they do things. It's like very 1 on 1 based, very like research based. You have a lot of readings. I don't think I've ever read that many books in my life. It was like stacks of books, on just like random medieval history topics because I just, I don't know, enjoyed it at the time.\u003C\u002Fp>\n\u003Cp>And I don't know, like that helped me, I think, in analytical thinking. Right? Like you have always, some kind of learning from these things that are unrelated to computer science to technology. Right? So I think you can always learn from history, not as a like a history repeats itself sort of way, but as in like analytical thinking, how people were, you know, to put yourself in a mind of somebody 200 years ago or 300 years ago, and then write about it because what we perceive, I think in technology as well, what we perceive for, from the user perspective or as a developer is much different than the user, right?\u003C\u002Fp>\n\u003Cp>So, user is someone who is actually gonna use your software, and you're somebody who writes the code. So sometimes these 2 don't click. And that's when a lot of businesses fail, actually. I believe at least.\u003C\u002Fp>\n\u003Cp>Speaker 2: So, yeah. Go ahead, John. Yeah. I was\u003C\u002Fp>\n\u003Cp>Speaker 1: just I was just curious because, you know, Pedro and I both have started our own businesses for different purposes. Mine was not technology based. Pedro's was a little bit. So I'm curious, again, what what led you to start your own business, and then, what were some of the lessons that you learned in running a business? Because, obviously, you're in a different position now, which we can we can kind of transition into later in the conversation.\u003C\u002Fp>\n\u003Cp>But, what were some of the lessons that you learned starting a business and then running a business?\u003C\u002Fp>\n\u003Cp>Speaker 0: Oh, I think the biggest lesson is, to not follow your gut instinct and follow the money, however awful and capitalistic that sounds. You know, I had this dream of a marketplace that was, you know, open and curated by the community and very, yeah, like, just, open to to, let's say, new markets to especially African market, Asia and South America. That's where we focused our kind of initial, let's say, first wave of of, sales. And, like, yeah, you just learned that sometimes what you dream of building and the the ideals that you have as a person don't necessarily reflect on your business, on the numbers. Right?\u003C\u002Fp>\n\u003Cp>So yes they reflected on the people we got. Like we got amazing artists who had the same vision, the same goals as us. We got an amazing you know team but it just didn't translate to business every time. So we learned over time, of course, to to follow this, like, smell of money, I guess. But, yeah, that didn't work out as as we expected.\u003C\u002Fp>\n\u003Cp>Maybe we'll talk about it as well a bit. But, yeah. So that's it.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. How did you go about, like, when you started the business, getting people excited? Because you mentioned, you know, you need contributors. So what was one of the ways that you you sort of found these contributors? And and to your point about, you know, following the money, is it that you weren't able to find people that were contributing back Or or what ultimately led to, you know, the next steps and decisions there?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. So so initially you know, it's like when you have a young business, everything's like super fast. Right? Like you have one decision that can make or break your whole business. And, you know, we had made some, like, hard lines that we just didn't wanna cross in terms of, like, quality, for example, on the platform and the amount of NFTs we want to sell.\u003C\u002Fp>\n\u003Cp>So that definitely, you know, we pay the price for that. The contributor side you mean the contributors in the contributors in the sense of code or contributors in the sense of art? Because we needed both. Pedro, what did you mean by that?\u003C\u002Fp>\n\u003Cp>Speaker 2: What was that? Sorry.\u003C\u002Fp>\n\u003Cp>Speaker 0: I'm sorry. Did you mean, like, contributors in terms of code or contributors in terms of arts, like, artists? I mean, both.\u003C\u002Fp>\n\u003Cp>Speaker 2: I mean, you've you've built a market place so there is that 2 sided, you know, you need the buyers and the the community in terms of art. But in terms of code contribution as well, I mean, you're looking to build this out in public sort of. So\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. In terms of, like, Paul, the developers themselves, we, applied for well, I was writing in a in a in a forum, called Project Catalyst. It's like a funder fundraiser for Cardano blockchain specifically. And, there, this, well, this team showed up. They have a consultancy.\u003C\u002Fp>\n\u003Cp>They have developers. They were interested in the story. And that kind of how we want to build a marketplace. NFTs were just starting. So nobody even, you know, talked about it.\u003C\u002Fp>\n\u003Cp>And then we were kind of probably the first NFT marketplace on Cardano, given, like, the timeline of when we started, like, this whole journey. And, of course, there were other players who started, you know, kind of picking up on it. And they actually used the market much better than we did. So our our market match was not there, even initially. But we just pushed this idea, and we kind of had it.\u003C\u002Fp>\n\u003Cp>You know, people were asking us, hey, when are you gonna sell your coins? When are you gonna have a coin? You know, all this stake, you know, when, ICO and stuff like that. So it was like a lot of, like, hype there that we just didn't use properly. So so yeah, the devs came in.\u003C\u002Fp>\n\u003Cp>And we had the artists who, you know, they had the same idea as us, which is a community curated marketplace, a high quality marketplace for really, really high quality artists. And we found some, of course, and they've made NFTs with us, and they sold them, but the volume was low. And so that kind of started kind of affecting us much more as we kind of turned to. And, yeah, we're here now.\u003C\u002Fp>\n\u003Cp>Speaker 2: So Do do you think any of that had to do with leveraging Cardano and sort of tying yourself into the Cardano ecosystem when there were other you know, there's obviously so many players, in the space. Cardano being obviously always in the in the top ten, I'd say for the most part. And as a fan, personally, I've, you know, I've invested in Cardano as well and, have followed that community for a while. I know there's a lot of ups and downs. But if you were to do it all over again, would you have chosen, perhaps a different cryptocurrency?\u003C\u002Fp>\n\u003Cp>Or do you think there was a risk of of choosing, you know, Cardano to base everything off of?\u003C\u002Fp>\n\u003Cp>Speaker 0: It's dangerous to say I would choose something else in the crypto space. I'm kidding you. I would definitely no. I am kidding. I would definitely choose.\u003C\u002Fp>\n\u003Cp>Yeah. I would definitely choose Cardano again, actually. Because it was a unique opportunity in the market. There was no NFT marketplaces. Right?\u003C\u002Fp>\n\u003Cp>There was already like OpenSea. There was, I don't know, super rare. You know, these things were like, we were like super rare, a bit more like super rare. I don't know if you know these NFT marketplaces, but super rare was more like, yeah, high end art rate. So, and 101 NFT is what we call like unique NFT.\u003C\u002Fp>\n\u003Cp>So, yeah, There was definitely opportunities that were, you know, there in on Cardano In the Cardano ecosystem itself. But I would definitely choose it again. Like, Yeah. I don't think that's a question of Because it scales, right? Like, that's how we went.\u003C\u002Fp>\n\u003Cp>Like, if we're gonna build a business around a cryptocurrency, we have to pick something that scales. And that's gotta stay there in 10, 20 years. Ethereum, unfortunately, doesn't scale as well as we know. Right? Right.\u003C\u002Fp>\n\u003Cp>Right. High fees. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. You you've touched on a couple things I really wanna kinda dig a little deeper on. Number 1, startups are fast and furious, like you said, and there's a lot of decisions to be made. And so one of the things you touched on was people asking questions of when are you releasing this or when are you doing that, also making the right decisions. So how did you as the, you know, leader of the entire organization, how did you balance, making the product stable, making things that people wanted, also trying to tie in experimentation for new features?\u003C\u002Fp>\n\u003Cp>Because, obviously, the the blockchain community kind of took off so fast recently, and and so there's a lot of changes happening. So how did you find that balance of leading a team, building what people wanted, and trying to to put in new features into a product as well?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Good question. So so, like, at the beginning, as I said, we were like, we stuck to our goals and like we had this vision. And then we saw that that wasn't working as well as we wanted it to, so we started listening. And one of the things that, you know, we knew was missing from the Cardano ecosystem was auctions.\u003C\u002Fp>\n\u003Cp>So an auction kind of automated and on chain auction system. So that you have a permissionless auction. Basically, you send your NFT, you send your money, and basically the the smart contract itself spews out the winner. Right? So this was never, built by anyone else.\u003C\u002Fp>\n\u003Cp>So we knew that there was, like, this unique market opportunity and a lot of requests for that. Of course, there's tech debt, right? It's a startup, so everything is breaking all the time. You have to fix things. You have a backlog.\u003C\u002Fp>\n\u003Cp>Like, you know, we had a a Trello board, I remember, for bugs only. We had, like, GitHub for developments. Like, we used, like, a bunch of different tools for different stuff. And, you know, the trail board was just like getting filled up all the time with bugs and and customer requests. So it was even small stuff, right?\u003C\u002Fp>\n\u003Cp>Like, if you have something that should work and somehow doesn't work for one user, and you try to fix it, and you're like, okay, let me, know, I have to dig in and, you know, fix something up. I mean, that's so really great job for them doing this, for our tunnel, definitely.\u003C\u002Fp>\n\u003Cp>Speaker 1: How did that transition go? You know? Because, obviously, you're in a you're in a new position now, and there's been a transition period in general. Do you find that that type of, leadership that you're currently in and development life cycle or development type of development that you're doing currently is a lot more rigid in a in a larger company, or is it a little bit more freeing because you're not you're not the ultimate decision maker, but you obviously do have people underneath of you. So is it is it less stressful but less ability to to be flexible now than where you currently or previously were?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. When I was leaving the startup role, I was like, I'm gonna chill now. I'm gonna, like, take it easy. I'm gonna, like, you know and then I went to my job, and I was like, I wanna keep this job. So I'm really stressed out.\u003C\u002Fp>\n\u003Cp>I really want to, you know, work with people and show that I'm worth it. Right? So I think I'm never gonna be happy. That's the conclusion. No, I'm kidding.\u003C\u002Fp>\n\u003Cp>It's it's basically like, it's and I think it's it's a mix of things again, like, you know, like, as a as a CEO, you really are everything to the company, you're doing everything. Everybody's looking at you for decisions. There's no other person, you know, who can make it for them, right, if it make the decision for you. So, so and now it's like, I am a contributor, but it's like, I have my own team, I have my own bubble. And whatever happens outside of it is out of my control.\u003C\u002Fp>\n\u003Cp>So I don't have to think about that. But then at the same time, I really have to think about my team and develop their skills and, improve their processes. So it's a bit it's a really different kind of work, I think. As a CEO, you're also sales, you're marketing, you're a developer, you're a team lead. Right?\u003C\u002Fp>\n\u003Cp>And as a team lead, I'm just a team lead. But the focus is different a bit. Right? Like and also, you have some expectations from your bosses, and it's a bit of a different dynamic. And working with people is hard.\u003C\u002Fp>\n\u003Cp>Right? Like, it's just very, difficult at times, right? Because you have to put your ego behind, and you have to listen and say, hey, you're right. And, as a CEO, you don't get to do that that much because you're always right for the good or the bad of it. Right?\u003C\u002Fp>\n\u003Cp>So yeah.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. And and when you're leading a team, you know, obviously, there's a lot of responsibilities that you have as a leader. But when you're talking about, like, you know, if you were to look at a a pie chart and see the percentage of your time spent across doing different things, what does that look like in terms of managing teams and just managing people? Sometimes maybe being a a babysitter, versus, like, actual or, you know, psychologist, versus actual, like, decision making and and strategy and all that. Like, what is that that split look like for you?\u003C\u002Fp>\n\u003Cp>Speaker 0: It's a very good question because I have no clue. It's, like day to day.\u003C\u002Fp>\n\u003Cp>Speaker 2: Any given day. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Because like, I mean, I remember like some colleagues in my previous company, they were like, oh my God, this is like a psych session. Thank you so much, you know, for listening to me. And I was like, okay, great. So, so yeah, I think there's definitely some psychology there.\u003C\u002Fp>\n\u003Cp>Like, I would say, you know, 1 on ones, you know, you have them, I don't know, maybe a full day every 2 weeks. So like half, like half a day every week, You spend on just being a psychologist for people, you know, listening to their problems or finding out a way to solve some things and help them, you know, either prioritize, help them. Yeah. Like just vent out sometimes, you know, so it's important for people just, you know, you're just as a manager should not talk at all and just let people, you know, let's team out on you.\u003C\u002Fp>\n\u003Cp>Speaker 2: Right. And that definitely sounds like some some some good advice. Sorry, John. You had a question.\u003C\u002Fp>\n\u003Cp>Speaker 1: Well, I was just gonna say, you know, for for quite a while, it sounds like even back, I guess, to college and as as growing up, it seems like you were always a go getter and whatnot. Have you ever thought about ever stepping down? Maybe this is even towards retirement, but stepping down just to be an individual contributor and be that person who needs psychiatric help from your boss, or do you love that type of day to day constant change in what you're doing, chaos, but also controlled chaos? You know? You know, have you ever looked at it from the other side of the spectrum even though you've been in leadership for quite a while now?\u003C\u002Fp>\n\u003Cp>Speaker 0: Code is like Kryptonite. Like, you just wanna get close to it and get yeah. You know, it's the as closer you get to it, the weaker you get, and the more you wanna kind of, do it. Like, you the more you wanna, I don't know, like, to contribute individually. So I had recently, like, I was pairing with some devs, and I was like, oh my god, I missed this.\u003C\u002Fp>\n\u003Cp>And then I step away, and I was like, I don't think my personality is made for this, because I'm like more like active, more like change experimentation, you know, making decisions. And And some people are not like that. And when I look, you know, when I was an individual contributor, I don't think I was fully happy, because it was more like just listen and do things, right? And here's like, you know, you make your decisions, and you're responsible for those decisions, but you maybe have some skill set and some communication skills that individual contributors don't have, right? So so, I mean, not always, but let's say I don't know.\u003C\u002Fp>\n\u003Cp>That's how I feel, at least in my experience. So, yeah, I do feel like, you know, the draw towards that sometimes. But, you know, in in then I'm like, okay, I really like actually managing and, you know, leading people and talking to podcasts. I don't know.\u003C\u002Fp>\n\u003Cp>Speaker 1: It's kinda like a slings it's kinda like a sling shot. You get closer or an orbit. You orbit close to what you really like, but then you kind of go outside and do your leadership type of stuff. So you're, like, constantly orbiting around a center of gravity, which is coding.\u003C\u002Fp>\n\u003Cp>Speaker 0: Exactly. Yeah. I mean, we're building software. Right? And if there's no people to build it, then it's well, your job is worthless as a manager.\u003C\u002Fp>\n\u003Cp>So\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. Exactly. And then I guess the other side of it is, like, what to build. And you you had mentioned before, you know, kinda chasing the money. Is that something that when you're looking to make decisions based on, you know, technology and business alignment, is that where your head's at?\u003C\u002Fp>\n\u003Cp>Like, what's going to, you know, what's gonna please the the customer the most or what's gonna bring in the most revenue? And then, like, how do you manage prioritizing that with the engineers? I know that sometimes engineers, developers wanna build really cool stuff. Other times, it's like, no, we need to focus on revenue. So I'd love to hear about that balance of how you make those key decisions and which direction to head.\u003C\u002Fp>\n\u003Cp>Speaker 0: So it depends on the company, I think, and on your role. Right? So sometimes as an engineering manager, you actually do make product decisions. Sometimes you don't. So so for me, I think it's just important to balance things out.\u003C\u002Fp>\n\u003Cp>Right? You have tech debt. You have something that is there's like bugs, right, in the code. There's something that is waiting for you. Always there constantly in the backlog.\u003C\u002Fp>\n\u003Cp>And then you have also this new shiny thing you wanna build. So, yeah. I think the most difficult question is how do you balance building new stuff and fixing the old stuff? And that's something that I don't know. Maybe there's a book on this or something.\u003C\u002Fp>\n\u003Cp>But, I, you know, there's ways to figure it out yourself. But, I think it's hard. Right? And you have to kind of constantly look at both and say, hey, there's a pile of stuff. You know, crap is piling up on this side.\u003C\u002Fp>\n\u003Cp>And we're not or we are not developing anything new. And we're just, like, boring and not, you know, not enjoying our work. So I think it's a balance.\u003C\u002Fp>\n\u003Cp>Speaker 2: Is that anything that's happened recently that you can share, like, specific example of that?\u003C\u002Fp>\n\u003Cp>Speaker 0: Not specific examples of that. I think it's just constantly happening at any organization. Like there's no specific thing that I can point out because it's so common. Right? It's like every day you do this and every day you of have to prioritize what am I gonna do.\u003C\u002Fp>\n\u003Cp>You know, where am I gonna spend my money, time, life, energy on. Right?\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. And you and you've traveled quite a bit and lived in quite a few places, that we discussed in our previous conversation a little bit here. You're currently in Estonia. You've you've been in Germany. You've been in the United States.\u003C\u002Fp>\n\u003Cp>Do you find that that type of, those types of changes and seeing different cultures and whatnot has, number 1, enhanced your leadership, enhanced your introductions to the people who have different coding skills that you then can also, inherit, and then also helped you become an inclusive manager and leader, for all types of people?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I think it's important to be an empathetic leader because you just want to people to be happy and, yeah, work with you and communicate well with you and just, like, be, you know, be there for you. I know it's cheesy, but, you know, I don't like this, like, comparison with, like, family and stuff. But, you know, at least you're comfortable with your team, and that's important. And I think you learn that through having these different experiences, living in different places.\u003C\u002Fp>\n\u003Cp>You just learn how to communicate faster with people. Get get to know them faster and get to, like, kind of get to them faster, and then to you faster. And that's really important, I think, as a manager to be, not just a professional, right? But also be a human, and to understand that sometimes we don't like things. And being able to explain that you have to do things that you don't like to others is super important as a leader.\u003C\u002Fp>\n\u003Cp>So yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: So so if you're an unempathetic robot, you might not want to be a leader?\u003C\u002Fp>\n\u003Cp>Speaker 0: I think you wanna be a contributor. I'm kidding. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 2: AI is not gonna replace the leadership team then.\u003C\u002Fp>\n\u003Cp>Speaker 0: Who knows?\u003C\u002Fp>\n\u003Cp>Speaker 2: Who knows?\u003C\u002Fp>\n\u003Cp>Speaker 0: I mean, there's an empathy you can code into it. Right?\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. There you go.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. So you you've been in blockchain. You're currently doing what you're doing. Do you have hot takes on things like AI? Or or I guess because you are in a leadership position, you do have a little bit of ability to do some forward thinking, figure out where you might wanna experiment.\u003C\u002Fp>\n\u003Cp>You know, what is the next wave that you see being able to take advantage of? Are you digging into AI, or is there something else? How do you feel about those types of things?\u003C\u002Fp>\n\u003Cp>Speaker 0: In my piece I'm looking into the camera right now, but my piece of advice for everyone is to take notes on what happened to crypto and do the same thing that to AI. Because in crypto, we had, like, this use case for everything. Crypto is gonna solve all our problems, and now this is happening with AI. So so I think that people are having this holy grail moment with technology in general. Right?\u003C\u002Fp>\n\u003Cp>We had phones. We had social media. Now, crypto, AI, next thing is probably VR headsets with Apple, what Apple's doing, right? So we have these like wow moments. And these things are just gonna integrate into our lives.\u003C\u002Fp>\n\u003Cp>And I think that's the whole point of technology, just making things smoother, easier. Right? And I think that with AI, I I mean, I know that it's something that will probably make things easier, but not in the ways that maybe we imagine it right now. It usually doesn't turn out the way, you know, these predictions that we, we make them. So so I think, you know, there's a holy grail in technology.\u003C\u002Fp>\n\u003Cp>Yes, it's gonna help us. Yes, it's a tool. But you still can't rely on AI fully to do, you know, I don't know, doctor's exams because, yes, it can check your body and it can give you a diagnosis, but you will still need a doctor to proof proofread it or proof. So it's the tool. Right?\u003C\u002Fp>\n\u003Cp>It's like anything else. It's a tool.\u003C\u002Fp>\n\u003Cp>Speaker 2: Yeah. I guess there's there's, sorry. I was gonna say there there is that argument that, you know, essentially, it's it's software. It's stuff we've been doing for a long time. You know, it used to be called big data or maybe before that called something else.\u003C\u002Fp>\n\u003Cp>And really, it's just the larger and larger you scale this data out, now you have really large language models because of the capabilities of GPU's and, just where we're at today. So yeah. You can see extrapolating that out to just be larger and larger use cases of of, you know, just running heavy computations, essentially. But, yeah, interesting to hear your take.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. And I think that's a really smart view that you take because you were obviously so ingrained in the the cryptocurrency blockchain world. It's phones came around, and they did change our lives, but we just it's in integrated into our lives. Technology, the Internet, you know, it came around before that, and it's now integrated in into our lives. And it's really just to make our lives easier.\u003C\u002Fp>\n\u003Cp>It's I I personally don't think AI will take over the world. I don't think robots will take over the world. I think it'll make our lives easier. It'll it'll help us make do things quicker and faster. We still have to proof check it, like you said, or maybe we'll get to a point where we don't have to proof proofread it as much.\u003C\u002Fp>\n\u003Cp>But I think right now, we've we're hitting this burst. You know? Everybody's really excited. Everyone's creating something new every 5 seconds. Eventually, you know, all of the useless ones will fade away, but then AI will integrate into our normal daily lives.\u003C\u002Fp>\n\u003Cp>And so I think having the experience that you had, kind of allows you to see it from that point of view. Like, yeah, it is something that could change the way we do things, but it'll just integrate with how we already do that or how we already do things. So\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. Do you\u003C\u002Fp>\n\u003Cp>Speaker 1: do you go to AI at all for advice on management or whatnot? Do you say, how do I be a bet how do I tell this person to get to work?\u003C\u002Fp>\n\u003Cp>Speaker 0: I used to do I never used it in this new role. I used to do it in my previous role a bit for some presentations, as this, yeah, as a CEO, I didn't use it because it just wasn't a thing chat gpt wasn't released yet. So, it really came as like NFTs were fading away and crypto was fading away. You know? Yeah.\u003C\u002Fp>\n\u003Cp>I kind of came to replace it, which you can see on social media as well. I guess there's some research probably on this. I would like to read it. But, yeah. Sorry.\u003C\u002Fp>\n\u003Cp>Yeah. Basically, a bit. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: Well, if anyone would read up on AI as opposed to having AI tell you what AI is, it's something to you. Because you read a lot when you're at Oxford. So I'm sure you'll you'll read how AI actually works.\u003C\u002Fp>\n\u003Cp>Speaker 0: Oh my god. Don't get me started with that reading thing. You know, sometimes I get messages in Slack, and I'm just, like, I read the first sentence and I already kind of conclude what the whole thing is about, like the whole chunk of text, and I answer already. And I'm like, and then I go back. I like, because I used to do this, like, I would just answer it.\u003C\u002Fp>\n\u003Cp>And I'm just trying to like, you know, slowly read it. And, because you don't have time, right? You just wanna do things fast. Yeah. Sorry.\u003C\u002Fp>\n\u003Cp>The aggression.\u003C\u002Fp>\n\u003Cp>Speaker 1: Yeah. Again, I guess, what kind of were there ever challenges? Because you've you've had a lot of roles. You've got a very diverse background. Were there ever challenges that you you came across where you just felt like you couldn't climb that hill and you you thought you couldn't do it and you did.\u003C\u002Fp>\n\u003Cp>You know? Obviously, technology, there's a lot of challenges. And so I I guess, did you learn most of your experience from being a CEO and a leader, or are you learning I guess you're a little bit newer in your role here, but, do you think that you're gonna be able to climb those hills a little bit easier where you currently are, overcome those challenges a little easier with Bolt?\u003C\u002Fp>\n\u003Cp>Speaker 0: I had a role in between Bolt and and this being a CEO, also like a manager role, similar size of the team. And I think that was my kind of playground, I would say, for a lot of these mistakes. And I made a lot of mistakes there, and I can see it now. And I'm really thankful for this experience. So I think, you know, as our career progresses, we just get to learn.\u003C\u002Fp>\n\u003Cp>We just get to make some smaller mistakes over time, right? Like, it's smaller and smaller, hopefully, until we are, you know, like an actual leader or manager or like whatever we wanna become like a software developer. It would just, you know, recognize patterns and recognize, you know because now, like, when I'm in this role, like, I'm just thinking, you know, oh, I've heard this before, you know? So you get to that kind of stage where you're like, oh, okay. I know this type of person.\u003C\u002Fp>\n\u003Cp>I know what they're expecting, or from me as a manager, or, what they wanted from their career even. So so, yeah, it gets easier over time. Yeah.\u003C\u002Fp>\n\u003Cp>Speaker 1: Awesome. I I think, you know, I personally appreciate people who travel and and work when they're traveling and see different cultures, see different things, learn from all of their experiences. And, again, as as both Pedro and I have our own side businesses, some that are still going and some that have succeeded and failed as well, I think there's a lot to be learned from your experience. So I guess if there's any any, you know, remaining thoughts that you have for people who might be looking to follow a similar journey, you know, if they wanna be a CEO of a company, if they wanted to get into blockchain, if they wanna be a manager as opposed to an individual contributor, you know, what are some thoughts that you would have, some advice that you would give, either to your former self or to some of those people who might be listening who have similar aspirations?\u003C\u002Fp>\n\u003Cp>Speaker 0: Yeah. I would say that you should probably, as somebody who's, like, let's say, a contributor individually, you know, make a big leap in your career, but also be sure that it's a safe leap, that you don't throw everything out, that you just don't quit your job and without funding and, you know, find your find your own company without a team. You know, you have to kind of prepare. It's a process. Right?\u003C\u002Fp>\n\u003Cp>Like, in going from a contributor to manager or let's say to CEO, a big jump. It's just like something that you need to kind of have a cushion and make sure that you're not losing it all at the end of that journey. Right? Because a lot of people are doing that. Right?\u003C\u002Fp>\n\u003Cp>Especially in the US, you don't have a, I guess, a safety net. Right? So like social security, all this stuff is like a pretty, yeah, spicy topic. So so, yeah, like, you have to have a cushion, a financial cushion or funding or family support. I think that's really important.\u003C\u002Fp>\n\u003Cp>So so then you can kind of journey forward without fear because fear is pulling us back.\u003C\u002Fp>\n\u003Cp>Speaker 2: Right. So the really pragmatic approach, line everything up, really think it through, and have, like, a mission oriented, yeah.\u003C\u002Fp>\n\u003Cp>Speaker 0: Start with a job. Like, I was working, you know, 12 hour days plus. And, I mean, that's not even a lot. I think some people work 16 hours. You know, sometimes I wake up at AM AM.\u003C\u002Fp>\n\u003Cp>I would end my day at 2 AM. And it's tough, but, you know, there was no for me, there was no other way to to, you know, make sure that I'm safe into going into a business.\u003C\u002Fp>\n\u003Cp>Speaker 2: Thanks so much for joining us. Definitely learned a lot. Really appreciate your time.\u003C\u002Fp>\n\u003Cp>Speaker 0: Thank you, guys. I hope to see you soon.\u003C\u002Fp>","Yeah. When I was leaving the startup role, I was like, I'm gonna chill now. I'm gonna, like, take it easy. I'm gonna, like, you know and then I went to my job and I was like, I wanna keep this job, so I'm really stressed out. I really want to, you know, work with people and show that I'm worth it, right? So I think I'm never gonna be happy. That's the conclusion. No, I'm kidding. Hey, everyone. Thanks so much for joining us for this week's podcast. With us, we have Matea. Matea, I'll let you introduce yourself so that you can give you your best introduction and representation of yourself. Why don't you let everyone know a little bit of a brief background of who you are, where you're based, where you're located, and then we can kinda get into the questions after that. Sure. Sounds good. I'm Mateo. I'm a techie. I'm an engineering manager currently at Bolt, which is a ride hailing company mostly. We do a lot of stuff like food delivery, and, yeah, grocery delivery. So, it's a pretty big company. And, yeah, about myself, I don't know. I just ate, like, I cooked dinner in 5 minutes, and I ate it in 3. So I think I broke the record for, fastest eating and cooking of a meal after work. Well, you know, real real quick inter real quick interjection. I used to work with a firefighter, and he was a record fast eater. He would eat a huge burrito in in probably 2 or 3 minutes. Oh, man. I could never I need to sit there and enjoy it, but, yeah, I get it sometimes. Yeah. Me too, usually. But if you're here. You okay? Yeah. So sorry. That's it. Yeah. I'm I'm yeah. I used to work for Artano. I mean, as as a CEO of Artano. It's an NFT platform. I guess that's the main reason why I'm here. And, yeah, I mean, generally, I am into, yeah, just, like, trendy new technologies. I was into crypto, have my own theories around that. Like, yeah, we talked about it briefly, before about, AI bros versus crypto bros. So I I think that's a topic probably we'll have to touch at some point. Yeah. Absolutely. Yeah. I'm based in Estonia. Sorry. I forgot to say that. And I'm from Serbia originally. Awesome. Sounds great. And what's your what do you do at your current role at Bolt? So I'm an engineering manager. I manage a team of, 8 developers, mobile side, and, some Android, iOS, and back end. Yeah. And we are responsible for the app. So the, yeah. Yeah. I I don't think I can talk much about it yet because I'm new to the company as well. So yeah. Yeah. Sure. Sure. Well, let's dive in a little bit into some of your previous experience. You mentioned you were a CEO. So let's go even further back from that. When did you first starting start getting into technology? Did you go to school for it? And then I guess we can circle back around eventually to to becoming a CEO, but I'd love to understand, how you got into technology. Was it from a young age, or did did Twitter get you into it? Did you see somebody say something cool? So, yeah, I'd love to learn how you first started getting interested in technology. Yeah. I think my dad is mostly to think for that. He's, somebody who introduced me to computers. He is a big Linux fan. So I was probably 4 years old when I ran my first games from Linux. He, like, forbade me to use the Windows. He was such an anti Windows guy. He was like, yeah, it's like the devil basically. And now today, you know, like, we think of these things a bit differently, but at the time it was like, you know, Linux is the best thing ever, and open source is the way to go. And, it's I guess that was the crypto of the time, right? And now we have a bit of a different, yeah, outlook on things. And that's like the first time. So I was like 4 years old, I think, when I first started, like, a kernel command. But I'm not like I don't think I'm like a project kid or anything. I just, like, learned by heart how to run a game because I want to play it. So you were smarter at 4 years old around technology than than most people are even still to this day. I don't think it's that hard to learn a command to write, like, open a kernel and write a command. But yeah. Maybe. I don't know. Do you think it's any harder or easier than learning a language? Sorry. What what running language? Do you think it's any like, learning a coding language is any harder or easier than learning a, you know, verbal or written language? It's an interesting question. So I'm somebody who likes learning languages. I speak fluently like French and German and Serbian. So and English, I guess. And I'm yeah. I think it's something that's very interesting to compare, like linguistics of a language and how linguists learn things from, you know, how how, you know, programmers or people who code, how we learn things from linguistics, between I think music as well. Music is very mathematical. But music, yeah, computer science and and linguistics are pretty interesting. And yeah. Nice. Nice. I I speak English, and that's about it. So, same with when it comes to coding languages. I only know a couple. I was not as talented as yourself. So I I believe in our previous conversation, you mentioned going potentially to Oxford. So I'd love to understand what you went to school for. And did did your time at at university kind of lead you to want to start your own company? Or, I guess, how did that transition go from going from university to eventually starting your own company? So, yeah, I went to uni in the US. I went to Middlebury College. I studied computer science and German language. I did liberal arts. So that allowed me to, like, spring into different things, right? Like, to to touch on different topics and mostly writing. I really liked writing, research. And yeah, so I went to Oxford for a kind of an independent study, independent research thing, in my 3rd year of college. And I was lucky enough to, you know, see this, how this world works. You know, you have a tutor, you have a 1 on 1 with a professor at Oxford, which is pretty incredible. And that's how they do things. It's like very 1 on 1 based, very like research based. You have a lot of readings. I don't think I've ever read that many books in my life. It was like stacks of books, on just like random medieval history topics because I just, I don't know, enjoyed it at the time. And I don't know, like that helped me, I think, in analytical thinking. Right? Like you have always, some kind of learning from these things that are unrelated to computer science to technology. Right? So I think you can always learn from history, not as a like a history repeats itself sort of way, but as in like analytical thinking, how people were, you know, to put yourself in a mind of somebody 200 years ago or 300 years ago, and then write about it because what we perceive, I think in technology as well, what we perceive for, from the user perspective or as a developer is much different than the user, right? So, user is someone who is actually gonna use your software, and you're somebody who writes the code. So sometimes these 2 don't click. And that's when a lot of businesses fail, actually. I believe at least. So, yeah. Go ahead, John. Yeah. I was just I was just curious because, you know, Pedro and I both have started our own businesses for different purposes. Mine was not technology based. Pedro's was a little bit. So I'm curious, again, what what led you to start your own business, and then, what were some of the lessons that you learned in running a business? Because, obviously, you're in a different position now, which we can we can kind of transition into later in the conversation. But, what were some of the lessons that you learned starting a business and then running a business? Oh, I think the biggest lesson is, to not follow your gut instinct and follow the money, however awful and capitalistic that sounds. You know, I had this dream of a marketplace that was, you know, open and curated by the community and very, yeah, like, just, open to to, let's say, new markets to especially African market, Asia and South America. That's where we focused our kind of initial, let's say, first wave of of, sales. And, like, yeah, you just learned that sometimes what you dream of building and the the ideals that you have as a person don't necessarily reflect on your business, on the numbers. Right? So yes they reflected on the people we got. Like we got amazing artists who had the same vision, the same goals as us. We got an amazing you know team but it just didn't translate to business every time. So we learned over time, of course, to to follow this, like, smell of money, I guess. But, yeah, that didn't work out as as we expected. Maybe we'll talk about it as well a bit. But, yeah. So that's it. Yeah. How did you go about, like, when you started the business, getting people excited? Because you mentioned, you know, you need contributors. So what was one of the ways that you you sort of found these contributors? And and to your point about, you know, following the money, is it that you weren't able to find people that were contributing back Or or what ultimately led to, you know, the next steps and decisions there? Yeah. So so initially you know, it's like when you have a young business, everything's like super fast. Right? Like you have one decision that can make or break your whole business. And, you know, we had made some, like, hard lines that we just didn't wanna cross in terms of, like, quality, for example, on the platform and the amount of NFTs we want to sell. So that definitely, you know, we pay the price for that. The contributor side you mean the contributors in the contributors in the sense of code or contributors in the sense of art? Because we needed both. Pedro, what did you mean by that? What was that? Sorry. I'm sorry. Did you mean, like, contributors in terms of code or contributors in terms of arts, like, artists? I mean, both. I mean, you've you've built a market place so there is that 2 sided, you know, you need the buyers and the the community in terms of art. But in terms of code contribution as well, I mean, you're looking to build this out in public sort of. So Yeah. In terms of, like, Paul, the developers themselves, we, applied for well, I was writing in a in a in a forum, called Project Catalyst. It's like a funder fundraiser for Cardano blockchain specifically. And, there, this, well, this team showed up. They have a consultancy. They have developers. They were interested in the story. And that kind of how we want to build a marketplace. NFTs were just starting. So nobody even, you know, talked about it. And then we were kind of probably the first NFT marketplace on Cardano, given, like, the timeline of when we started, like, this whole journey. And, of course, there were other players who started, you know, kind of picking up on it. And they actually used the market much better than we did. So our our market match was not there, even initially. But we just pushed this idea, and we kind of had it. You know, people were asking us, hey, when are you gonna sell your coins? When are you gonna have a coin? You know, all this stake, you know, when, ICO and stuff like that. So it was like a lot of, like, hype there that we just didn't use properly. So so yeah, the devs came in. And we had the artists who, you know, they had the same idea as us, which is a community curated marketplace, a high quality marketplace for really, really high quality artists. And we found some, of course, and they've made NFTs with us, and they sold them, but the volume was low. And so that kind of started kind of affecting us much more as we kind of turned to. And, yeah, we're here now. So Do do you think any of that had to do with leveraging Cardano and sort of tying yourself into the Cardano ecosystem when there were other you know, there's obviously so many players, in the space. Cardano being obviously always in the in the top ten, I'd say for the most part. And as a fan, personally, I've, you know, I've invested in Cardano as well and, have followed that community for a while. I know there's a lot of ups and downs. But if you were to do it all over again, would you have chosen, perhaps a different cryptocurrency? Or do you think there was a risk of of choosing, you know, Cardano to base everything off of? It's dangerous to say I would choose something else in the crypto space. I'm kidding you. I would definitely no. I am kidding. I would definitely choose. Yeah. I would definitely choose Cardano again, actually. Because it was a unique opportunity in the market. There was no NFT marketplaces. Right? There was already like OpenSea. There was, I don't know, super rare. You know, these things were like, we were like super rare, a bit more like super rare. I don't know if you know these NFT marketplaces, but super rare was more like, yeah, high end art rate. So, and 101 NFT is what we call like unique NFT. So, yeah, There was definitely opportunities that were, you know, there in on Cardano In the Cardano ecosystem itself. But I would definitely choose it again. Like, Yeah. I don't think that's a question of Because it scales, right? Like, that's how we went. Like, if we're gonna build a business around a cryptocurrency, we have to pick something that scales. And that's gotta stay there in 10, 20 years. Ethereum, unfortunately, doesn't scale as well as we know. Right? Right. Right. High fees. Yeah. Yeah. You you've touched on a couple things I really wanna kinda dig a little deeper on. Number 1, startups are fast and furious, like you said, and there's a lot of decisions to be made. And so one of the things you touched on was people asking questions of when are you releasing this or when are you doing that, also making the right decisions. So how did you as the, you know, leader of the entire organization, how did you balance, making the product stable, making things that people wanted, also trying to tie in experimentation for new features? Because, obviously, the the blockchain community kind of took off so fast recently, and and so there's a lot of changes happening. So how did you find that balance of leading a team, building what people wanted, and trying to to put in new features into a product as well? Yeah. Good question. So so, like, at the beginning, as I said, we were like, we stuck to our goals and like we had this vision. And then we saw that that wasn't working as well as we wanted it to, so we started listening. And one of the things that, you know, we knew was missing from the Cardano ecosystem was auctions. So an auction kind of automated and on chain auction system. So that you have a permissionless auction. Basically, you send your NFT, you send your money, and basically the the smart contract itself spews out the winner. Right? So this was never, built by anyone else. So we knew that there was, like, this unique market opportunity and a lot of requests for that. Of course, there's tech debt, right? It's a startup, so everything is breaking all the time. You have to fix things. You have a backlog. Like, you know, we had a a Trello board, I remember, for bugs only. We had, like, GitHub for developments. Like, we used, like, a bunch of different tools for different stuff. And, you know, the trail board was just like getting filled up all the time with bugs and and customer requests. So it was even small stuff, right? Like, if you have something that should work and somehow doesn't work for one user, and you try to fix it, and you're like, okay, let me, know, I have to dig in and, you know, fix something up. I mean, that's so really great job for them doing this, for our tunnel, definitely. How did that transition go? You know? Because, obviously, you're in a you're in a new position now, and there's been a transition period in general. Do you find that that type of, leadership that you're currently in and development life cycle or development type of development that you're doing currently is a lot more rigid in a in a larger company, or is it a little bit more freeing because you're not you're not the ultimate decision maker, but you obviously do have people underneath of you. So is it is it less stressful but less ability to to be flexible now than where you currently or previously were? Yeah. When I was leaving the startup role, I was like, I'm gonna chill now. I'm gonna, like, take it easy. I'm gonna, like, you know and then I went to my job, and I was like, I wanna keep this job. So I'm really stressed out. I really want to, you know, work with people and show that I'm worth it. Right? So I think I'm never gonna be happy. That's the conclusion. No, I'm kidding. It's it's basically like, it's and I think it's it's a mix of things again, like, you know, like, as a as a CEO, you really are everything to the company, you're doing everything. Everybody's looking at you for decisions. There's no other person, you know, who can make it for them, right, if it make the decision for you. So, so and now it's like, I am a contributor, but it's like, I have my own team, I have my own bubble. And whatever happens outside of it is out of my control. So I don't have to think about that. But then at the same time, I really have to think about my team and develop their skills and, improve their processes. So it's a bit it's a really different kind of work, I think. As a CEO, you're also sales, you're marketing, you're a developer, you're a team lead. Right? And as a team lead, I'm just a team lead. But the focus is different a bit. Right? Like and also, you have some expectations from your bosses, and it's a bit of a different dynamic. And working with people is hard. Right? Like, it's just very, difficult at times, right? Because you have to put your ego behind, and you have to listen and say, hey, you're right. And, as a CEO, you don't get to do that that much because you're always right for the good or the bad of it. Right? So yeah. Yeah. And and when you're leading a team, you know, obviously, there's a lot of responsibilities that you have as a leader. But when you're talking about, like, you know, if you were to look at a a pie chart and see the percentage of your time spent across doing different things, what does that look like in terms of managing teams and just managing people? Sometimes maybe being a a babysitter, versus, like, actual or, you know, psychologist, versus actual, like, decision making and and strategy and all that. Like, what is that that split look like for you? It's a very good question because I have no clue. It's, like day to day. Any given day. Yeah. Yeah. Because like, I mean, I remember like some colleagues in my previous company, they were like, oh my God, this is like a psych session. Thank you so much, you know, for listening to me. And I was like, okay, great. So, so yeah, I think there's definitely some psychology there. Like, I would say, you know, 1 on ones, you know, you have them, I don't know, maybe a full day every 2 weeks. So like half, like half a day every week, You spend on just being a psychologist for people, you know, listening to their problems or finding out a way to solve some things and help them, you know, either prioritize, help them. Yeah. Like just vent out sometimes, you know, so it's important for people just, you know, you're just as a manager should not talk at all and just let people, you know, let's team out on you. Right. And that definitely sounds like some some some good advice. Sorry, John. You had a question. Well, I was just gonna say, you know, for for quite a while, it sounds like even back, I guess, to college and as as growing up, it seems like you were always a go getter and whatnot. Have you ever thought about ever stepping down? Maybe this is even towards retirement, but stepping down just to be an individual contributor and be that person who needs psychiatric help from your boss, or do you love that type of day to day constant change in what you're doing, chaos, but also controlled chaos? You know? You know, have you ever looked at it from the other side of the spectrum even though you've been in leadership for quite a while now? Code is like Kryptonite. Like, you just wanna get close to it and get yeah. You know, it's the as closer you get to it, the weaker you get, and the more you wanna kind of, do it. Like, you the more you wanna, I don't know, like, to contribute individually. So I had recently, like, I was pairing with some devs, and I was like, oh my god, I missed this. And then I step away, and I was like, I don't think my personality is made for this, because I'm like more like active, more like change experimentation, you know, making decisions. And And some people are not like that. And when I look, you know, when I was an individual contributor, I don't think I was fully happy, because it was more like just listen and do things, right? And here's like, you know, you make your decisions, and you're responsible for those decisions, but you maybe have some skill set and some communication skills that individual contributors don't have, right? So so, I mean, not always, but let's say I don't know. That's how I feel, at least in my experience. So, yeah, I do feel like, you know, the draw towards that sometimes. But, you know, in in then I'm like, okay, I really like actually managing and, you know, leading people and talking to podcasts. I don't know. It's kinda like a slings it's kinda like a sling shot. You get closer or an orbit. You orbit close to what you really like, but then you kind of go outside and do your leadership type of stuff. So you're, like, constantly orbiting around a center of gravity, which is coding. Exactly. Yeah. I mean, we're building software. Right? And if there's no people to build it, then it's well, your job is worthless as a manager. So Yeah. Exactly. And then I guess the other side of it is, like, what to build. And you you had mentioned before, you know, kinda chasing the money. Is that something that when you're looking to make decisions based on, you know, technology and business alignment, is that where your head's at? Like, what's going to, you know, what's gonna please the the customer the most or what's gonna bring in the most revenue? And then, like, how do you manage prioritizing that with the engineers? I know that sometimes engineers, developers wanna build really cool stuff. Other times, it's like, no, we need to focus on revenue. So I'd love to hear about that balance of how you make those key decisions and which direction to head. So it depends on the company, I think, and on your role. Right? So sometimes as an engineering manager, you actually do make product decisions. Sometimes you don't. So so for me, I think it's just important to balance things out. Right? You have tech debt. You have something that is there's like bugs, right, in the code. There's something that is waiting for you. Always there constantly in the backlog. And then you have also this new shiny thing you wanna build. So, yeah. I think the most difficult question is how do you balance building new stuff and fixing the old stuff? And that's something that I don't know. Maybe there's a book on this or something. But, I, you know, there's ways to figure it out yourself. But, I think it's hard. Right? And you have to kind of constantly look at both and say, hey, there's a pile of stuff. You know, crap is piling up on this side. And we're not or we are not developing anything new. And we're just, like, boring and not, you know, not enjoying our work. So I think it's a balance. Is that anything that's happened recently that you can share, like, specific example of that? Not specific examples of that. I think it's just constantly happening at any organization. Like there's no specific thing that I can point out because it's so common. Right? It's like every day you do this and every day you of have to prioritize what am I gonna do. You know, where am I gonna spend my money, time, life, energy on. Right? Yeah. And you and you've traveled quite a bit and lived in quite a few places, that we discussed in our previous conversation a little bit here. You're currently in Estonia. You've you've been in Germany. You've been in the United States. Do you find that that type of, those types of changes and seeing different cultures and whatnot has, number 1, enhanced your leadership, enhanced your introductions to the people who have different coding skills that you then can also, inherit, and then also helped you become an inclusive manager and leader, for all types of people? Yeah. I think it's important to be an empathetic leader because you just want to people to be happy and, yeah, work with you and communicate well with you and just, like, be, you know, be there for you. I know it's cheesy, but, you know, I don't like this, like, comparison with, like, family and stuff. But, you know, at least you're comfortable with your team, and that's important. And I think you learn that through having these different experiences, living in different places. You just learn how to communicate faster with people. Get get to know them faster and get to, like, kind of get to them faster, and then to you faster. And that's really important, I think, as a manager to be, not just a professional, right? But also be a human, and to understand that sometimes we don't like things. And being able to explain that you have to do things that you don't like to others is super important as a leader. So yeah. So so if you're an unempathetic robot, you might not want to be a leader? I think you wanna be a contributor. I'm kidding. Yeah. AI is not gonna replace the leadership team then. Who knows? Who knows? I mean, there's an empathy you can code into it. Right? Yeah. There you go. Yeah. So you you've been in blockchain. You're currently doing what you're doing. Do you have hot takes on things like AI? Or or I guess because you are in a leadership position, you do have a little bit of ability to do some forward thinking, figure out where you might wanna experiment. You know, what is the next wave that you see being able to take advantage of? Are you digging into AI, or is there something else? How do you feel about those types of things? In my piece I'm looking into the camera right now, but my piece of advice for everyone is to take notes on what happened to crypto and do the same thing that to AI. Because in crypto, we had, like, this use case for everything. Crypto is gonna solve all our problems, and now this is happening with AI. So so I think that people are having this holy grail moment with technology in general. Right? We had phones. We had social media. Now, crypto, AI, next thing is probably VR headsets with Apple, what Apple's doing, right? So we have these like wow moments. And these things are just gonna integrate into our lives. And I think that's the whole point of technology, just making things smoother, easier. Right? And I think that with AI, I I mean, I know that it's something that will probably make things easier, but not in the ways that maybe we imagine it right now. It usually doesn't turn out the way, you know, these predictions that we, we make them. So so I think, you know, there's a holy grail in technology. Yes, it's gonna help us. Yes, it's a tool. But you still can't rely on AI fully to do, you know, I don't know, doctor's exams because, yes, it can check your body and it can give you a diagnosis, but you will still need a doctor to proof proofread it or proof. So it's the tool. Right? It's like anything else. It's a tool. Yeah. I guess there's there's, sorry. I was gonna say there there is that argument that, you know, essentially, it's it's software. It's stuff we've been doing for a long time. You know, it used to be called big data or maybe before that called something else. And really, it's just the larger and larger you scale this data out, now you have really large language models because of the capabilities of GPU's and, just where we're at today. So yeah. You can see extrapolating that out to just be larger and larger use cases of of, you know, just running heavy computations, essentially. But, yeah, interesting to hear your take. Yeah. And I think that's a really smart view that you take because you were obviously so ingrained in the the cryptocurrency blockchain world. It's phones came around, and they did change our lives, but we just it's in integrated into our lives. Technology, the Internet, you know, it came around before that, and it's now integrated in into our lives. And it's really just to make our lives easier. It's I I personally don't think AI will take over the world. I don't think robots will take over the world. I think it'll make our lives easier. It'll it'll help us make do things quicker and faster. We still have to proof check it, like you said, or maybe we'll get to a point where we don't have to proof proofread it as much. But I think right now, we've we're hitting this burst. You know? Everybody's really excited. Everyone's creating something new every 5 seconds. Eventually, you know, all of the useless ones will fade away, but then AI will integrate into our normal daily lives. And so I think having the experience that you had, kind of allows you to see it from that point of view. Like, yeah, it is something that could change the way we do things, but it'll just integrate with how we already do that or how we already do things. So Yeah. Do you do you go to AI at all for advice on management or whatnot? Do you say, how do I be a bet how do I tell this person to get to work? I used to do I never used it in this new role. I used to do it in my previous role a bit for some presentations, as this, yeah, as a CEO, I didn't use it because it just wasn't a thing chat gpt wasn't released yet. So, it really came as like NFTs were fading away and crypto was fading away. You know? Yeah. I kind of came to replace it, which you can see on social media as well. I guess there's some research probably on this. I would like to read it. But, yeah. Sorry. Yeah. Basically, a bit. Yeah. Well, if anyone would read up on AI as opposed to having AI tell you what AI is, it's something to you. Because you read a lot when you're at Oxford. So I'm sure you'll you'll read how AI actually works. Oh my god. Don't get me started with that reading thing. You know, sometimes I get messages in Slack, and I'm just, like, I read the first sentence and I already kind of conclude what the whole thing is about, like the whole chunk of text, and I answer already. And I'm like, and then I go back. I like, because I used to do this, like, I would just answer it. And I'm just trying to like, you know, slowly read it. And, because you don't have time, right? You just wanna do things fast. Yeah. Sorry. The aggression. Yeah. Again, I guess, what kind of were there ever challenges? Because you've you've had a lot of roles. You've got a very diverse background. Were there ever challenges that you you came across where you just felt like you couldn't climb that hill and you you thought you couldn't do it and you did. You know? Obviously, technology, there's a lot of challenges. And so I I guess, did you learn most of your experience from being a CEO and a leader, or are you learning I guess you're a little bit newer in your role here, but, do you think that you're gonna be able to climb those hills a little bit easier where you currently are, overcome those challenges a little easier with Bolt? I had a role in between Bolt and and this being a CEO, also like a manager role, similar size of the team. And I think that was my kind of playground, I would say, for a lot of these mistakes. And I made a lot of mistakes there, and I can see it now. And I'm really thankful for this experience. So I think, you know, as our career progresses, we just get to learn. We just get to make some smaller mistakes over time, right? Like, it's smaller and smaller, hopefully, until we are, you know, like an actual leader or manager or like whatever we wanna become like a software developer. It would just, you know, recognize patterns and recognize, you know because now, like, when I'm in this role, like, I'm just thinking, you know, oh, I've heard this before, you know? So you get to that kind of stage where you're like, oh, okay. I know this type of person. I know what they're expecting, or from me as a manager, or, what they wanted from their career even. So so, yeah, it gets easier over time. Yeah. Awesome. I I think, you know, I personally appreciate people who travel and and work when they're traveling and see different cultures, see different things, learn from all of their experiences. And, again, as as both Pedro and I have our own side businesses, some that are still going and some that have succeeded and failed as well, I think there's a lot to be learned from your experience. So I guess if there's any any, you know, remaining thoughts that you have for people who might be looking to follow a similar journey, you know, if they wanna be a CEO of a company, if they wanted to get into blockchain, if they wanna be a manager as opposed to an individual contributor, you know, what are some thoughts that you would have, some advice that you would give, either to your former self or to some of those people who might be listening who have similar aspirations? Yeah. I would say that you should probably, as somebody who's, like, let's say, a contributor individually, you know, make a big leap in your career, but also be sure that it's a safe leap, that you don't throw everything out, that you just don't quit your job and without funding and, you know, find your find your own company without a team. You know, you have to kind of prepare. It's a process. Right? Like, in going from a contributor to manager or let's say to CEO, a big jump. It's just like something that you need to kind of have a cushion and make sure that you're not losing it all at the end of that journey. Right? Because a lot of people are doing that. Right? Especially in the US, you don't have a, I guess, a safety net. Right? So like social security, all this stuff is like a pretty, yeah, spicy topic. So so, yeah, like, you have to have a cushion, a financial cushion or funding or family support. I think that's really important. So so then you can kind of journey forward without fear because fear is pulling us back. Right. So the really pragmatic approach, line everything up, really think it through, and have, like, a mission oriented, yeah. Start with a job. Like, I was working, you know, 12 hour days plus. And, I mean, that's not even a lot. I think some people work 16 hours. You know, sometimes I wake up at AM AM. I would end my day at 2 AM. And it's tough, but, you know, there was no for me, there was no other way to to, you know, make sure that I'm safe into going into a business. Thanks so much for joining us. Definitely learned a lot. Really appreciate your time. Thank you, guys. I hope to see you soon.",[5043,5044,5045],"23cdeb72-cd9f-4a0e-913f-0629cf083f37","54371530-ea30-4635-bbeb-248d946e017c","44da0951-5d55-43d5-860e-5bdf1f2d133a",[],{"year":2380,"number":739,"id":4539,"episodes":5048,"show":5049},[4541,4542,4543,4544,4515],{"title":1849,"slug":1850},{"id":4801,"slug":5051,"vimeo_id":5052,"description":5053,"tile":5054,"length":13,"resources":12,"people":12,"episode_number":889,"published":5055,"title":5056,"video_transcript_html":5057,"video_transcript_text":5058,"content":12,"seo":12,"status":19,"episode_people":5059,"recommendations":5060,"season":5061},"meaning-of-life","891433556","Life's deepest truths are often found in the strangest places.","4ecea9a1-c775-4a1f-847e-56924a0a2f3e","2024-01-24","Meaning of Life","\u003Cp>Speaker 0: And now Dev Thoughts by Jack Rabbit. I once asked a compiler to explain the meaning of life. It returned an error message, which read, error. Life is undefined. I guess even machines have their philosophical boundaries.\u003C\u002Fp>","And now Dev Thoughts by Jack Rabbit. I once asked a compiler to explain the meaning of life. It returned an error message, which read, error. Life is undefined. I guess even machines have their philosophical boundaries.",[],[],{"year":2380,"number":159,"id":4790,"episodes":5062,"show":5063},[4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803,4804,4805,4779],{"title":3380,"slug":3381},{"id":4802,"slug":5065,"vimeo_id":5066,"description":5067,"tile":5068,"length":13,"resources":12,"people":12,"episode_number":740,"published":5055,"title":5069,"video_transcript_html":5070,"video_transcript_text":5071,"content":12,"seo":12,"status":19,"episode_people":5072,"recommendations":5073,"season":5074},"control","891433097","They say you should be careful what you wish for.","01e686ab-9136-4f1b-a826-da4425b96516","Control","\u003Cp>Speaker 0: And now Dev Thoughts by Jackrabbit. They said with self hosting, you control your own destiny. But, when destiny included debugging on a Friday night when our server was on fire, control was the last thing I felt.\u003C\u002Fp>","And now Dev Thoughts by Jackrabbit. They said with self hosting, you control your own destiny. But, when destiny included debugging on a Friday night when our server was on fire, control was the last thing I felt.",[],[],{"year":2380,"number":159,"id":4790,"episodes":5075,"show":5076},[4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803,4804,4805,4779],{"title":3380,"slug":3381},{"id":5078,"slug":5079,"vimeo_id":5080,"description":5081,"tile":5082,"length":176,"resources":5083,"people":5087,"episode_number":159,"published":5092,"title":5093,"video_transcript_html":5094,"video_transcript_text":5095,"content":12,"seo":12,"status":19,"episode_people":5096,"recommendations":5099,"season":5100},"dab588c7-1feb-457f-9cfd-b574b7eea4e0","boutique-web-agency","901507988","In this episode of I Made This, Bryant is joined by Miguel Stevens, the creative force behind Studio Monty, a Belgium-based agency specializing in lifestyle and bespoke websites. Miguel shares his journey from a childhood fascination with computers to founding his studio. ","1a6f3e0d-558f-445d-bc0d-f97bb36270aa",[5084],{"name":5085,"url":5086},"Studio Monty","https:\u002F\u002Fwww.studiomonty.be\u002F",[5088,5089],{"name":3590,"url":3591},{"name":5090,"url":5091},"Miguel Stevens","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fmiguelstevensbe\u002F","2024-01-23","Building a Boutique Web Agency with Studio Monty","\u003Cp>Speaker 0: Hi, guys. Welcome back to another episode of I Made This. I'm your host, Brian Gillespie, developer advocate here at Directus, and I have got my extra special guest with me today, mister Miguel Stevens from Studio Monti. Miguel, happy to have you, man.\u003C\u002Fp>\u003Cp>Speaker 1: Hi. Thanks for having me. Glad\u003C\u002Fp>\u003Cp>Speaker 0: to Yeah. Yeah. So, hey. Like, you and I have talked inside our directus community a couple times through Discord or, I I think, even on several, like, GitHub issues probably. But, you know, for those who don't know you, maybe, give us a little intro or, you know, what's the backstory?\u003C\u002Fp>\u003Cp>Speaker 1: Sure. So my studio is called Studio Monty. We're a Belgium based web company. We do lifestyle websites, boutique kind of websites, so not not a big technical prod products, but more like the the well and refined designed projects. And we always offer CMS, of course, which is direct us.\u003C\u002Fp>\u003Cp>Speaker 0: So we don't necessarily have to dive in, directly to direct us. Super glad that you're using that. But, you know, tell me a a little more about the agency. Like, how did you get started? What what led you to that?\u003C\u002Fp>\u003Cp>You know? If we back up even further, like, how did you how did you get into development?\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Cool. Good question. I think for me, the case, like many people, has been like a a child passion. When I was 8 years old, my dad bought me a a Pentium 386 or what was this called?\u003C\u002Fp>\u003Cp>I don't even remember it.\u003C\u002Fp>\u003Cp>Speaker 0: And I was I'm not sure.\u003C\u002Fp>\u003Cp>Speaker 1: The 386. Old computer with with floppy disks.\u003C\u002Fp>\u003Cp>Speaker 0: Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And the\u003C\u002Fp>\u003Cp>Speaker 0: I'm trying to think of, like, what the what the the megahertz they were running back in the day. I think the first computer I had was, like, maybe, like, 96 megahertz or something\u003C\u002Fp>\u003Cp>Speaker 1: like that. Right? The people remember. No, like a diskette disk. How do you call it in English?\u003C\u002Fp>\u003Cp>It's 3.14 megabytes or something.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. There you go.\u003C\u002Fp>\u003Cp>Speaker 1: And the floppies were even less than that. But I started experimenting with Microsoft FrontPage, which, like, Dreamweaver is really old, but FrontPage, that's a whole other level. So I think I just roll I I started making small websites with cheat codes for roller coaster tycoon, things like that. Hey. There you go.\u003C\u002Fp>\u003Cp>So then my my whole journey has been development. I worked in some big development companies in Ghent, and I worked in some boutique agencies, but it's always been about about design and development. Yeah. So I I I love both. I'm a 100% developer.\u003C\u002Fp>\u003Cp>I it has to be beautiful. Right?\u003C\u002Fp>\u003Cp>Speaker 0: I guess it. Yeah. So and you guys mentioned that you focus on boutique websites. Could you, you know, like, expand that a little bit? Like, specifically, like, what does the average project look like for you guys?\u003C\u002Fp>\u003Cp>Speaker 1: Sure. I think with boutique, I mean, those are mostly websites with less than 10 pages. Design is very important. They wanna have something unique about them. So we we don't do it's not like WordPress templates, where you can clear a seed template.\u003C\u002Fp>\u003Cp>We always work with a designer. We we do custom designs, and custom development. It's it's very much focused on the specific needs of each client. And yeah. And then it's in the lifestyle sector, so, it's fun companies, interior design, architecture, things like that.\u003C\u002Fp>\u003Cp>Yeah. Very local as well.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So, hey, smaller businesses, clients who are very concerned with image and design and, looking professional.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Is\u003C\u002Fp>\u003Cp>Speaker 0: that a fair summarization?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Very fair.\u003C\u002Fp>\u003Cp>Speaker 0: What does your what does your process look like on those projects? Like, on the front end, like, how how you mentioned it's local, but how how do most of these clients find you?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. For us, it's been a case of. Right? So, I think we started only 1 year ago, and all projects we've had have been through local Netflix. You make a website for someone you know.\u003C\u002Fp>\u003Cp>Before you know it, their uncle wants a website, and then you really get started and and your network grows. I do try to there's a we're based in Ghent, Belgium, and there's a good community of, of, people that are, yeah, entrepreneurs. What's the word in English?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. You got it. You nailed it. What?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So there's a lot of events, that we that I tried to go to to meet people, to talk about how we can help people. So it's been word-of-mouth. That's the English Cool.\u003C\u002Fp>\u003Cp>Speaker 0: You're primarily. Excellent. Yeah. Talk to me about your process. Like, yeah, being a, like, you're doing smaller projects that are, let's say, not, like, super data intensive.\u003C\u002Fp>\u003Cp>If like, what's your like, what's the time span from, like, conception or or, like, the idea phase to, like, launching a project? Talk me talk me through your process because I'm super curious on that. Like, what those type of projects actually like, what the flow looks like.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Good question. I've been refining this because I feel process is really important to make something replicable and scalable. So what's important for for me is I always start with a very intense strategy session. And so it's, like, 1 to 2 hours where we sit down with the client and really listen, like, what what are your challenges?\u003C\u002Fp>\u003Cp>Where do you wanna go with your business? Who do you wanna be in 5 years' time? It it can be very personal because each business has, like, these typical problems. They want more revenue. They want the right type of client.\u003C\u002Fp>\u003Cp>They want they want their website to provide more information, better customer service for their clients. So we really drill down the goals for each website, and only then do we start going to wireframing to to design and development.\u003C\u002Fp>\u003Cp>Speaker 0: Gotcha. Now in that strategy call, is that, like, after you've already quoted them and and they sent them a proposal?\u003C\u002Fp>\u003Cp>Speaker 1: Or Oh, yeah. Right. I forgot that part. So I think it starts with a sales conversation. You you call on the phone mostly, or you go face to face and you just listen.\u003C\u002Fp>\u003Cp>Can we help you? I think it's important. The big money topic. There's a lot of interesting videos about this, but a lot of people I read about, a lot of starting people are afraid to discuss money. They all wanna talk about money upfront, but I think it's important that you know what's in what's their budget, what can we do to help them, and, how can we make it work?\u003C\u002Fp>\u003Cp>So it's I think it's this first sales call is is very important.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That's that's always the toughest, like, conversation. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Right. Like, how come I've got\u003C\u002Fp>\u003Cp>Speaker 0: a I've I've got a number in my head. You've got a number. Right. Yeah. Like, how do we talk about like, how do you how do you address that with clients?\u003C\u002Fp>\u003Cp>Like, you said you you don't shy away from it.\u003C\u002Fp>\u003Cp>Speaker 1: I think during those first 15 minutes, it has to be clear what the budget is. So if it's not clear from them, tell you, you can just ask upfront, like, what budget do you have in mind? And if that range is acceptable for the type of websites, then we can then we can go for that. For example, startups, I wouldn't work with a fresh startup because they don't need a custom built website. Startups can use Wix or Squarespace, which is mostly enough to help them.\u003C\u002Fp>\u003Cp>So it it would be it wouldn't be good customer service of me to try to sell them a website because they don't need custom websites in their first month. Yeah. So I try to be very honest. I'm not the type of person who just takes on a client to get the money and then, no. I can't help them.\u003C\u002Fp>\u003Cp>If I can send them to Squarespace, then we'll happily do that. Yeah. Yeah. It's honesty and transparency. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. I love that. That's super critical. You know?\u003C\u002Fp>\u003Cp>Mhmm. And even being like, you said you started the agency about a year ago. I can imagine, like, in the early days, it's a little harder to do that. Right? Where like, hey.\u003C\u002Fp>\u003Cp>We need a couple of projects through the door, but, still, if it's not a right fit, you you turn them away anyway. That's that's, that's pretty cool.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. At the beginning, you have to take on a lot of projects, obviously, because you just started and there's no, no real cash flow, but yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Well, cool. So you have the strategy session. You guys get into, like, their company goals and, like, aspirations and things like that. And then how do how do things progress from there?\u003C\u002Fp>\u003Cp>Speaker 1: Sure. Once we know after the strategy session, I make a content model, which is like a sitemap, but for each page, I also include elements. This happens after research. So we look at competitors. You look at the industry.\u003C\u002Fp>\u003Cp>What does the industry expect from websites of that sector, and and, what does the competition do? What do they do good? What don't they do good? And then we discuss some styles. There's there's more meetings.\u003C\u002Fp>\u003Cp>And then it's onto the design phase. For bigger projects, we do a style guide first. If there's a copywriter to be involved, you're a copywriter, obviously, and that's onto design. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Gotcha. Like, do you have any, like, philosophy? Like, what's your design philosophy? Yeah. I I I think for me, when I used to do projects, like, doing competitor research is is absolutely critical.\u003C\u002Fp>\u003Cp>Right? You gotta know what the landscape is, but how do you how do you not let that inform your own design? You know what I mean? Like, you you don't wanna just, like, copy competitors or you don't want to on the surface, you may not know whether that is actually working well for the competitor or not. Right?\u003C\u002Fp>\u003Cp>So, hey. Like, what is your philosophy on design, and, like, how do you navigate, just hey. Like, hey. Here's what everybody else is doing. Here's what we think is gonna actually work well for you as a to your clients.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm. I think it's it's important obviously to look at the top ten. Right? We we we don't compare with businesses that are on page 20 on Google. And I think it's important if you're looking at, like, what's the content going to be to only focus on the content.\u003C\u002Fp>\u003Cp>It's like you just said, we try to if we see, like, interesting piece of content, I I copy paste the text and put it in Figma in, like, a sort of prototype. We don't screenshot designs because, like you say, if you start screenshotting designs from other companies, your design will be influenced by that. So I think it's important in the in the first phases where the content is being decided, that you just focus on what's what's being written, what's being told. I also saw an interesting video by The Future, and they they make this comparison like, a website has to answer questions. So we always start with that in mind.\u003C\u002Fp>\u003Cp>I write the quote and I'll accept it, and they say, a website is like a conversation. Right? You come to my website. It's like you come into my house, and you you have questions for me. Like, will this product help me feel more whatever.\u003C\u002Fp>\u003Cp>I don't know. So the the order of your website, the content has to answer all people's questions. This is something we I try to keep in mind when building the content model.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I I love to hear you say that because everybody says that you should be content first, but, like, what I see a lot of times is, like, there's a whole board of inspiration of, like, hey. We like this site. We like this site. We like this site.\u003C\u002Fp>\u003Cp>And then you're working backwards of, like, hey. Let's let's incorporate this theme and, hey. Like, take some ideas from this. And what I'm hearing you say is that you guys you you don't take screenshots of other websites. You might, like, snip some content, but it is very much, like, content driven.\u003C\u002Fp>\u003Cp>There's no like, when you're in the design process, there's no, like, outside influences from other websites. Is that right?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Maybe a bit. I I mean, I have a designer who I work with, and she, she loves designing, so she's not content first. I'm a big fan of content first, but I think it depends. It's it's one of the biggest chicken or egg problems in the web design industry for me is do you do the content first and then have your designer build around the content?\u003C\u002Fp>\u003Cp>Or do you have the designer design first and then put the content in the available space that the designer chose. It's we we've been trying both, ways of working, and there's no winner yet. I get you.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I I I think it's just just like the messy middle\u003C\u002Fp>\u003Cp>Speaker 1: of, like, hey. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Like, where things come together.\u003C\u002Fp>\u003Cp>Speaker 1: I don't know if there's an answer to if someone has an answer to the the big chicken or egg design versus content first question. Please let me know.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Cool. And sales great. So I like, going through the design phase, like, obviously, hey. Like, are you who does the development?\u003C\u002Fp>\u003Cp>Like, the actual like, are you doing a lot\u003C\u002Fp>\u003Cp>Speaker 1: of the actual code at\u003C\u002Fp>\u003Cp>Speaker 0: this point or got somebody else on the team? Like, what does that look like?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I it depends if it's, if it's really busy and I'm like doing a lot of intakes and strategy, then I outsource the development. I'm working with someone who is really capable. I had to look, for a long time before I found him, but I have someone who helps me out. And sometimes I do projects myself.\u003C\u002Fp>\u003Cp>It's good to to stay in the flow. Right? So Nxt is evolving very quickly. Direct is evolving really quickly, and I I wanna, be up to date with everything. So I I still like to to, develop as well.\u003C\u002Fp>\u003Cp>To if it's still\u003C\u002Fp>\u003Cp>Speaker 0: like to jump in. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: I I think that's, like, a common case for a lot of us. Right? You get into it. Like, you you love building stuff, and then, like, inevitably, you've gotta take on more of the business stuff, and that gets in the way of of, like, the fun stuff of actually building and creating. And it's very much the same way for me where, like, I I love to build and create, and, like, the other stuff kinda I I I enjoy that as well, but it kinda gets in the way.\u003C\u002Fp>\u003Cp>Right?\u003C\u002Fp>\u003Cp>Speaker 1: I I think it's you never wanna have, like, 0 or 100 to to one side. I think it's always I always love developing. Maybe it's the same for you. Yeah. When there's time, it's it's great to just put on your headphones and be in the flow for a whole afternoon, writing code.\u003C\u002Fp>\u003Cp>It's amazing. It's still fun.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. But excellent. Well, what what is the average timeline? So, like, we we've talked about the process. Like, it start to finish, like, how quickly are you you know, how long does it take for the average project to Mhmm.\u003C\u002Fp>\u003Cp>Start to launch?\u003C\u002Fp>\u003Cp>Speaker 1: I think in theory, it would take, like, 1 month. But in practice, it's always 2, 3, 4 month. It's because I think there's something we're struggling with. It's very hard to to really I mean, it's only 1 year since we started, but it's very difficult for me right now to know exactly how long something will take. Sure.\u003C\u002Fp>\u003Cp>Depending on on how how fast does the design go, what types of content. So sometimes the client wants to write a copy himself, which is mostly something that will take long because nobody likes writing copy unless you're a copyright. Make your friends. I noticed that clients seems to seem to, like, not want to do it, so they they keep on, delaying it, and then it never happens. So it's it's a combination of waiting for assets.\u003C\u002Fp>\u003Cp>There's, like, a photographer involved, which is something for the types of websites. In the CGM Monti builds, we I wanna work with good photography. And minimal design is really important. So then you have a photographer who has to, be involved in the process. So there's a lot of variables, right, that can stretch or tend the timeline, but you you don't know in advance.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. They like, on the photography side, do you require your clients to to work with a professional photographer? Or\u003C\u002Fp>\u003Cp>Speaker 1: Well, this is something I started doing now since the last client. Yeah. I guess It's secured in the in the in the offer as well. I used to add, like, an an add on, like, photography optional, but there people are paying a lot of websites a lot of money for a website, and they're like, I don't need good pictures. So now it's just included in the price, and it's, yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, that's smart. Yeah. I I always hate to see a well designed website and then, like, the same stock photos that you've seen on 35 other websites. It just ruins it ruins everything.\u003C\u002Fp>\u003Cp>Speaker 1: The stock photos are the worst. I think the dark iPhone picture by the owner is even better than the stock.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Right. Yeah. At least it's real.\u003C\u002Fp>\u003Cp>Yeah. Or it feels real. I I'm sure that's a real person in the stock photo, but, you know, some of those stock photos, I I don't know, like, how people make those poses. Like, I think you never see that stuff in real life. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Well, I think it's it's your eye sees it directly. I mean, your stock photo, you can see it from very far, and it it gives the site a sort of fake feeling. It's it doesn't feel right when you see stock photos.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: So, like, shifting into, like, the tools that you're using to build the sites, like, what does your preferred, like, stack look like? You know? Right? What tools are you using on the front end, on the back end, that sort of stuff?\u003C\u002Fp>\u003Cp>Speaker 1: So it's, for us, it's Nuxtree on the front end and then Directus, on the back end. Yeah. Gotcha. We're running it on Docker containers for our production servers as well as, local local hosting. It's also on Docker.\u003C\u002Fp>\u003Cp>Speaker 0: Nice. Yeah. You're definitely one of my preferred stacks. But, like, what what led you to those 2 pieces? Like, you know, were you using those a year ago when you started?\u003C\u002Fp>\u003Cp>Or yeah. I know you mentioned front page that you were using way back in the day to build stuff. Like, walk walk me through the progression because that's that's super interesting for me because I I remember front page. I remember Dreamweaver. Right.\u003C\u002Fp>\u003Cp>I even I'm I'm not sure I I'm not sure if Angel Fire or, like, GeoCities were ever popular outside the US, but it was like a it was like a free website builder where you could get on and have all the animated GIFs and, like, all the all the crazy counters and all that stuff back in the day. But that's that's how I got my start in the industry as well. It was back in the day using those tools to kinda scratch stuff together.\u003C\u002Fp>\u003Cp>Speaker 1: I think that you said this rings a bell, but I I never heard of for this Angelfire. Angelfire.\u003C\u002Fp>\u003Cp>Speaker 0: It was yeah. It's basically like a a free website builder host. Like, you could you could throw, like, crazy backgrounds and stuff on there and write your text. But the rest of it, you know, back in those days, it was all just whatever HTML you could throw together yourself.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Marquee elements and stuff like that.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Oh, yeah.\u003C\u002Fp>\u003Cp>Speaker 1: So, I started my development. I mean, my first company I worked with was Laravel. So it was PHP. I loved Laravel. It's it was easy to use for me.\u003C\u002Fp>\u003Cp>It was a a dream come true after writing spaghetti code in PHP. But the older I got, and I started working with Vue, and I started loving JavaScript a lot. For me, it feels much cleaner, the syntax and PHP. I'm stirring some feathers here. I know.\u003C\u002Fp>\u003Cp>It's a big discussion. PHP lovers are everywhere and the JavaScript lovers. But for me, it's always been JavaScript. So when I started this company 1 year ago, I promised no PHP. So, my first thought was, okay.\u003C\u002Fp>\u003Cp>I need a I need a JavaScript back end. So I started looking into headless CMS systems. Strapi Strapi. I don't know how you pronounce it. Yeah.\u003C\u002Fp>\u003Cp>Was the first one I tried, but I I found it difficult to work with. I don't know. It was it didn't feel right. So I tried a few others. I tried story block.\u003C\u002Fp>\u003Cp>I tried content folks for a while, and then someone on Reddit mentioned Directus. Mhmm. I think version 10 was just released. When was it released? Do you know version 10?\u003C\u002Fp>\u003Cp>Speaker 0: Version 10 or version 9. Version 10 was that's Yeah. Version 10 was not so long ago. Version 9 was, like, a big change. Version version 8 was on PHP.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, really?\u003C\u002Fp>\u003Cp>Speaker 0: Version 9 version 9 was, like, the switch to to node to, like, the JavaScript side of things. And that was a a crazy big shift. That was about the same time that I actually found directives myself.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. That's\u003C\u002Fp>\u003Cp>Speaker 0: when when version 9 was released,\u003C\u002Fp>\u003Cp>Speaker 1: which was You weren't there in the BHP era.\u003C\u002Fp>\u003Cp>Speaker 0: I I was not. No. I I wasn't even aware of the tool, which is Okay. Which is wild.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And that's why I think it's someone on Reddit brought me to Directus, and I I tried it out, set it up. The documentation is great, and I just got it working. And then I just built my first website.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Nice. So I like, what were you said, like, the other tools didn't feel right, and that that definitely feels like a like a designer thing of a it doesn't feel right. But were there a like, any, like, issues that you encountered that were you were like, hey. This is this is not working for me?\u003C\u002Fp>\u003Cp>Like, can you even remember any of those tools? Like, what were the the red flags, like, if you can remember any?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think for me, now that I think about it, the biggest thing was since I mean, headless CMS systems can be used for, like, internal tools, for softwares. But for our studio, it's always going to be a CMS system. So my thought was if a client I remember with Strapi, I thought, if a client has to work on this, it's gonna be complicated. And for with directors, it just felt very calm.\u003C\u002Fp>\u003Cp>I could see my even less tech savvy clients work in this system. So for me, it was mostly a choice of, okay, which system are people my clients going to be able to work in without having to call me too much with questions. It it Not having to do a 12 hour CMS training.\u003C\u002Fp>\u003Cp>Speaker 0: It is very much from, like, the client perspective. Yeah. I I can remember that personally, like, seeing it like, I I went through a bunch of those as well. And, like, the seeing the white space and just I you mentioned calmness or the the calm, of, like, hey. This is really well designed, and it's open source, which is great.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: But I can remember looking at it and like, hey. Like, okay. Yeah. This feels right. Yeah.\u003C\u002Fp>\u003Cp>I think it\u003C\u002Fp>\u003Cp>Speaker 1: was a gut feeling. It's like a first impression. Right? You always set up doc. You open the tool and it's okay.\u003C\u002Fp>\u003Cp>This feels good. And I think what I also remember is the image handling direct is is is perfect. For CMS systems, there are always gonna be images, and the I think the file library and all the systems around it are built really, really solidly. So, yeah, that's a big plus\u003C\u002Fp>\u003Cp>Speaker 0: as well. What does, like, the average project look like for you inside Directus for for a client? You know, like, maybe through, like, the data model. You know? Or maybe just talk me through some of those, and and I think you've got a few things to show us.\u003C\u002Fp>\u003Cp>But, maybe just so you could, like, talk me through how the projects look inside Directus before we jump into that.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Cool. I think the last three projects were mostly similar. So we have pages. We have a page builder using the, many to any builder system.\u003C\u002Fp>\u003Cp>And then we have just like projects, testimonials, the team page. So it's mostly, managing data. We're not too heavy on flows at the moment. There's a few flows for, like, building, which we'll get into in a in a few minutes. But, yeah, it's mostly page builder.\u003C\u002Fp>\u003Cp>That's the biggest, chunk. And we use blocks. So every page, you can just, compose yourself with blocks. Clients don't really use that too much. It's mostly for myself.\u003C\u002Fp>\u003Cp>A block is reusable. It's a nice, neat piece of code, which can even be reused in in other projects. So yeah.\u003C\u002Fp>\u003Cp>Speaker 0: How do you, like, how do you present, like, the CMS and, like, direct us to your clients, like, through that phase? Is that, like, a discussion that you have, like, at the very beginning of a project? Is it like, hey. Oh, we got your website. You're gonna be able to edit all the content through this CMS.\u003C\u002Fp>\u003Cp>Yeah. Are you, like, talking me through how you present that solution to the client?\u003C\u002Fp>\u003Cp>Speaker 1: Sounds good. In my last, few offers, I included, screenshots or a video of directors. And the good thing is that clients mostly come from WordPress. So when they see directors, they're like, this is impossible. They almost can't believe how easy it is.\u003C\u002Fp>\u003Cp>Crazy. The the reactions are just amazing. The last two clients were really like, this is so easy. Essentially what they said. I sat next to them for the CMS training.\u003C\u002Fp>\u003Cp>We put in some data together and just they came from WordPress. They were like, this is really easy. So it feels really good to have clients, have this this sort of really good experience.\u003C\u002Fp>\u003Cp>Speaker 0: Now are are most of them coming from, like, the the drag and drop page builders where they've gotta go in and manage, like, all the padding and spacing and colors and all of that? Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Or from WordPress installations that are like a plug in hell, and everything's a plug in, and there there have to be and people don't like that. I think directors, you can also you can protect your clients. Like, you give them roles, and they only they can only edit what they're assigned to do. I know you can do that in WordPress as well, but WordPress feels like very, very much.\u003C\u002Fp>\u003Cp>And clients, they mostly don't have a lot of time. They maybe want to add a new testimonial. They had just want to log in, click testimonial, add new, and be done with it.\u003C\u002Fp>\u003Cp>Speaker 0: So Yeah. They they don't wanna, yeah, geek out on the the software or the tools like like we do. They just wanna get\u003C\u002Fp>\u003Cp>Speaker 1: them out.\u003C\u002Fp>\u003Cp>Speaker 0: They've got other stuff to do.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. They don't care what tool it is. It just has to be easy enough.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Cool. Well, do you wanna maybe share your screen and and show us, you know, like I would love to share with the audience, like, what the the typical, like, front end project looks like for you, And then, you know, maybe show us around the the back end if you're if you're able to.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Sure. Let me just share my screen real quick. K. Cool.\u003C\u002Fp>\u003Cp>So this is a project we finished, 2 months ago. It's a website for a company that does funding. Yeah. We like white space. We like beautiful fonts.\u003C\u002Fp>\u003Cp>These pages are so this is Nextree. This is a statically generated website. So Okay. It's on it's built on Netlify. So it's not SSR.\u003C\u002Fp>\u003Cp>It's SS SSG.\u003C\u002Fp>\u003Cp>Speaker 0: What's what is the what's the the display font there? Like, all the headings.\u003C\u002Fp>\u003Cp>Speaker 1: The this font?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I I I really like that font. Have a look.\u003C\u002Fp>\u003Cp>Speaker 1: I should know that. Ah, silica. It's silica. Right. Silca.\u003C\u002Fp>\u003Cp>It's nice having font.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. It looks great. I and, like, I've got another project that I'm working on personally that, I I've been eyeing fonts, and, like, that process is always, like, such a nightmare of, like, going through and trying to suss out just the perfect font for a project. This one looks great.\u003C\u002Fp>\u003Cp>Speaker 1: Right.\u003C\u002Fp>\u003Cp>Speaker 0: Might have to worry about.\u003C\u002Fp>\u003Cp>Speaker 1: Beautiful fonts are mostly really expensive, which is fair.\u003C\u002Fp>\u003Cp>Speaker 0: True. True.\u003C\u002Fp>\u003Cp>Speaker 1: And this is actually just a page built and page builder. They have cases, which are like, yeah, project cases. And what I love about this, it feels really snappy. I mean, this is Nuxt. Yeah.\u003C\u002Fp>\u003Cp>It's pre rendered, so that's the speed is the speed is amazing. Speed score is also really good. There's a form in here which will, be sent to directors as well. Nice. Yeah.\u003C\u002Fp>\u003Cp>And it's a dynamic form if you if you go to a case and you wanna be then your case will be prefilled here. This all will be sent to directors. Yeah. That's cool. What's\u003C\u002Fp>\u003Cp>Speaker 0: a great great looking site.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Thanks.\u003C\u002Fp>\u003Cp>Speaker 0: Very clean.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: What are you, what are you using for, like, the the transitions and the animations? Is it, are you, like I'm just custom coded CSS, or is it, like, a different library?\u003C\u002Fp>\u003Cp>Speaker 1: It's a wrapper component. It's called scroll transition dot view. I made it together with someone else, I think. We used it in a previous project. Yeah.\u003C\u002Fp>\u003Cp>And it just, yeah, it shifted into, into view.\u003C\u002Fp>\u003Cp>Speaker 0: As yeah. It's it's very tasteful. I think for me, that's one of the other things that I always struggle with on some websites is, like, everything is animated. There's, like, a crazy amount of motion, and it it just distracts from the the story in some cases. But this is this looks really well done.\u003C\u002Fp>\u003Cp>Speaker 1: I think it it has to be subtle. What I would like now is for, like, working on some images that, like, slide in from the right. Some some tiny animations, but I don't want it to be much more than this.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It looks great.\u003C\u002Fp>\u003Cp>Speaker 1: So for the for the back end, as you see the models we have, this is heavily based on the agency OS, by the way, which I think which you wrote most part of it.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Most of it is mine.\u003C\u002Fp>\u003Cp>Speaker 1: So thank you for this. Yeah. No. Great.\u003C\u002Fp>\u003Cp>Speaker 0: How No. Happy to you know, that project was like a labor of love because it it it took me a long time to figure out how to manage, like, the page blocks. And I was like, okay. Like, once you see it and and you've got a a reference, it's much easier to understand. But when I first came to Directus, I knew this functionality was available.\u003C\u002Fp>\u003Cp>It just like, there weren't any good references reference points to to use. So I I'm happy that it's been helpful for you. 100%.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, it yeah. It is. I don't know if I would have managed without. I think it's also it's a plus point because there's a lot of back end systems, but you guys took the time to write out a full front end to showcase how it can be used. So that's I'm very grateful for it.\u003C\u002Fp>\u003Cp>It's, it's amazing. You probably see it's heavily based on agency OS. Right? Yeah. There's a page.\u003C\u002Fp>\u003Cp>You have the blocks. You can move them.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It looks very familiar.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. This marquee, this text, that's the one you see here. So it's it's very easy for the client to work in. What's interesting about this project and, something I told you about is the the the flows, that we built. Because it's statically generated, you can use, like, a Netlify hook that each time you save a change you made, Netlify will start building.\u003C\u002Fp>\u003Cp>But the problem you get when 3 or 4 people are changing content is you get a queue of 20, 30 builds, the day. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Or the client changes a couple of different pages. Right? And they They go through it.\u003C\u002Fp>\u003Cp>Speaker 1: They they press save. They change something else, and it's it's triggering bills the whole time. Yep. And on NettoFi. So what, I contacted I wrote down his username, Arud Arud on Arud.\u003C\u002Fp>\u003Cp>Yeah. Arud. Do you know him?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. He, he won several of the or I think he won at least one of our hackathons.\u003C\u002Fp>\u003Cp>Speaker 1: Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Nice. Yeah. Yeah. He started\u003C\u002Fp>\u003Cp>Speaker 1: to on on Discord.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Very helpful member of the community. 100%.\u003C\u002Fp>\u003Cp>Speaker 1: So he started the idea of, these 3 flows you see in green, and then I finished it. There was one issue, and we worked on it together, reached out a bit. So what this does is every time a change is pushed, we set a flag, like change pushed through, and we set a time stamp.\u003C\u002Fp>\u003Cp>Speaker 0: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 1: And what these three flows together do is they only trigger a build every 5 minutes. So you could And and only when there's changes. Right. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, that's cool.\u003C\u002Fp>\u003Cp>Speaker 1: For example, this one, every 5 minutes, it will look inside the general settings and check if the variable has changes if it's set to true, and that will trigger this other flow.\u003C\u002Fp>\u003Cp>Speaker 0: Okay.\u003C\u002Fp>\u003Cp>Speaker 1: And this other flow will trigger an Sify build and update the current build time and update the settings. So it's,\u003C\u002Fp>\u003Cp>Speaker 0: I gotcha.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And then there's one more, the publish queue on save. This one just watches, if you save anything.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Yeah. So it's watching the content collections.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. All of them.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Gotcha.\u003C\u002Fp>\u003Cp>Speaker 1: And then it checks if it's older than 5 minutes, and then it triggers a flow. Or if it's not, it updates that we have some changes. It looks easy now, but we had some work with this.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. I can imagine. Hey. Like, hey.\u003C\u002Fp>\u003Cp>Trying to figure out, like, how to structure the flow and and actually building it, I'm sure. Like, how long did it take you to actually nail it nail the the flow, like, these 3 separate flows?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Is it well, Arutz did most of it. It was almost great. He had some issues that were remaining. I think we fixed it with, I'm not sure.\u003C\u002Fp>\u003Cp>I don't remember really because I was also a a FlowDB. I I hadn't used Flows before. So for me, it was a a very thorough introduction into flows.\u003C\u002Fp>\u003Cp>Speaker 0: I yeah. Yeah. Well, now you're an expert. So it was awesome.\u003C\u002Fp>\u003Cp>Speaker 1: Mostly with a with a bit of help from Discord.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Hey. The community members are amazing. Yeah, that that was how I learned most when I first started with Directus, as well.\u003C\u002Fp>\u003Cp>Speaker 1: I've had I don't know if the other have the CMS systems out there, have this community, but the Discord is amazing. It's for me, it's a it's a big plus. I mean, probably everyone has good community, but the the the director's Discord is really amazing. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. You you you gotta get this one updated too. I see you're on 10 dot 6 dot 3. You can get it updated to to 10 dot 7 so you can, use the theming. And I think Yeah.\u003C\u002Fp>\u003Cp>I think 10 dot 8 went actually last night as well.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, really?\u003C\u002Fp>\u003Cp>Speaker 0: Which has, like, some preset themes that are available. But Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Nice. Yeah. I saw the video you guys did on the what was enabled to week with with all the everyday video?\u003C\u002Fp>\u003Cp>Speaker 0: Leap week.\u003C\u002Fp>\u003Cp>Speaker 1: Leap week. Yeah. Very nice. I think, like, content versioning is also under 10.7. Right?\u003C\u002Fp>\u003Cp>Speaker 0: It's yeah. That's on 10.7 as well. Yeah. It could be I I'd say for your clients, maybe not, super helpful. I I doubt they have, like, a ton of different versions of content, but for, larger sites where you need to to manage changes, definitely helpful.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm. Yeah. And the team, and I need to try it. It's a coincidence, but the director's team is matches this website really good.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Yeah. I love the purple. Yeah.\u003C\u002Fp>\u003Cp>Maybe that's Yeah. Maybe that's what, issue what drew my eye to it or or what I, yes, subconsciously I was like, hey. This looks amazing. That purple plays into it for sure.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. There's, there's one more website I wanted to show. This is\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Definitely.\u003C\u002Fp>\u003Cp>Speaker 1: This is a website for an interior, high end design store, in our city, and they they loved, directors. They used to work with WordPress, and they were very excited when I showed them the finished CMS and how easy it was to change things. But this is also a project very heavy on good photography. And Directus was great in this one because I managed to use the the block system and and the for, like, for example, if we have a a blog and there's a Mhmm. This is in Dutch.\u003C\u002Fp>\u003Cp>I don't, suppose you can read it. But the we are using the blog builder. I think I I I read it here. If you go to the to the block, and then you have a block. And we're using the blocks as well to, really write the content.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. So what do you do? A client can embed, like, rich rich content, like videos and and things like that.\u003C\u002Fp>\u003Cp>Speaker 1: Video is not yet. Sorry. But images, we can this is a a text block. They could add a a gallery block in which they can combine images. Yeah.\u003C\u002Fp>\u003Cp>So it's Nice.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It's cool. Like, especially because it's very photo heavy. And they do I learned\u003C\u002Fp>\u003Cp>Speaker 1: a lot from from directors for this project, especially like the blocks. Sorry.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, I was gonna say, do they is it a furniture business? Is that what it is?\u003C\u002Fp>\u003Cp>Speaker 1: They do high end furniture. Like, the Eames chair is something they sell, like, the classical higher end brands.\u003C\u002Fp>\u003Cp>Speaker 0: I gotcha.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So they they you just have they have a big collection as well. For example, is a brand. And in this one, they can also write text, and then you can add these images themselves. And inside directors, they can choose.\u003C\u002Fp>\u003Cp>I want this image to be a size small, medium, or large, and the grid will using a series as grid, we'll place them like this. So it was a fun project. Learned a lot about directors, about the the the m to a system. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: It looks really nice. Yeah. And I like that you're giving clients a little little control over presentation, but, you know, like, not enough to break the design.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. And so I think that's the problem with with WordPress sometimes is there's a lot of things the client can do wrong, and and I think people like it. You can just change only what they want to change. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Like, honestly, like, I the clients that I worked with in the past, like WordPress and and stuff like Squarespace, it gives them it it's distracting. Right? Because what I really need them to do or needed them to do was go in and add content and photos, and what they ended up doing was, like, tweaking the spacing. Like, you know, like, hey.\u003C\u002Fp>\u003Cp>This doesn't look right. Or Yeah. Yeah. They spend 3 hours tweaking the design on the website, and they and they skip over the content. Right?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I know what you mean. I know exactly what you mean. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Well, I I appreciate the tour. It looks amazing. You know? What's what is the the future?\u003C\u002Fp>\u003Cp>It's something I I like to ask everybody. I I before we we started recording, you said you gave a a talk on chat GPT and stuff, but, that would could be an interesting little thread. But what what are your future plans for your agency? You know, what what kind of projects are you guys working on now or or getting ready to launch soon? Like, what's in the cards for you?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think that I have some exciting I have 2 projects coming up, and they both need multi language. It's something which I'm very afraid to start doing because I don't know how I'm gonna do it. I was hoping you would have done it in the agency OS.\u003C\u002Fp>\u003Cp>Speaker 0: That's the next version. Right?\u003C\u002Fp>\u003Cp>Speaker 1: I'll wait for you to finish that.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, so I think, with directors, I I took on some small projects. I blocked one really big project because I was afraid because I don't know yet how to do it. So for me, the future really is to get these bigger projects up and running in directives, see if I can use multi language, together with Nuxt and the block builder system. I know it's possible in directives. I just have to find out how to do it.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I I think it's a matter of just, like, getting into it. It's it's it's one that I'm keen to explore as well. So I like, as you get into it, definitely hit me up. Yeah.\u003C\u002Fp>\u003Cp>The like, the translation interface, super easy to use inside Directus. Like, the the pieces that I'm not a 100% on, and maybe somebody watching this can, like, school us both. It would it's just, like, how to how the best way to architect that on the front end and and, like, connect it. And I think there's for Nux, there's the, there's a module, I think, that that would potentially play into it. I haven't messed with it, though.\u003C\u002Fp>\u003Cp>The\u003C\u002Fp>\u003Cp>Speaker 1: You mean the i18n module or yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I haven't messed with it yet.\u003C\u002Fp>\u003Cp>Speaker 1: I think it's it's fairly I've used it with a static website. So where the the the translation strings are just adjacent file, it works perfectly. It even hooks into the transition state so that when you change language, you have a fade out that changes.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, nice.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think, well, the question will be, like, how do we like, if you do a request to to direct us for a page, do you give back all translations and you filter them out on the front end? Or when the user changes language, do you refresh the request to direct us? I think that would be\u003C\u002Fp>\u003Cp>Speaker 0: the way. Yeah. That's probably how I would do it. Yeah. I I have to connect you with, the the guys at Heinrichs and Heinrichs.\u003C\u002Fp>\u003Cp>They were one of the the previous episodes.\u003C\u002Fp>\u003Cp>Speaker 1: On email.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. They've got yeah. They've they've got a project that, they're I I I don't think it was actually, like, 2 different languages, but they were using the translation inside Directus to to manage that side of it. Uh-huh. It was like 2 locations.\u003C\u002Fp>\u003Cp>Like, they wanted to have 2 versions of the site, but it was all it was all still in English, but they were still using the the translations interface for\u003C\u002Fp>\u003Cp>Speaker 1: it. I remember. I think they used the translations interface to show these two locations. Right? Yep.\u003C\u002Fp>\u003Cp>I remember you did a video with them as well. Very interesting guys. Both of them.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So or what's the the future date like for the agency? Or, like, you know, what are your your goals for, you know, I guess, like, a little longer term? You know, a year, 2 years, 3 years, 5 years?\u003C\u002Fp>\u003Cp>Speaker 1: What what's the\u003C\u002Fp>\u003Cp>Speaker 0: future on that side?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. In the coming year, I wanna be, like, become really good in just next time directors. I mean, some new projects require me to do look something up like this multi language thing. This is something that I don't it's hard to estimate. Client asks how much it's gonna cost me more to have multi language, but I don't know.\u003C\u002Fp>\u003Cp>So I'm gonna be happy once I I've built a few very complex, integrations between Nuxt and Directus so that I can quickly and correctly estimate future projects and just get them up and running much quicker. And then there's also I have a whole second company I'm starting called Waiyo. It's a it's a we're gonna build sort of a SaaS platform to connect patients with medical professionals. So I want to start exploring multitenancy and directives as well, which is also a big topic.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Definitely. I I look forward to seeing more about that project, Miguel.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Well I'll\u003C\u002Fp>\u003Cp>Speaker 1: be happy to share all my findings with the community. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. That's one thing that I love about everybody that works with Directus. Yeah. They're is so helpful and and just willing to share.\u003C\u002Fp>\u003Cp>I I think, you know, ultimately, that that's what, like, an open source product, like, yeah, that's one of the ways that, we can compete with a as a smaller team with with, like, larger products, you know, some of the the SaaS based headless CMS and and other products. But just our community, amazing community and and full of supportive people like yourself and Arud. We'll make sure we'll get him a a shout out again in this thing. Do that. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: I feel like I wonder if, like I think, like, head headless CMS feels like the future to me. Just it's so obvious to decouple the front and the back end. It's it's just like your your data platform and you can do anything you want with it. So I look forward to pretty seeing more of that evolve, in the in the near future.\u003C\u002Fp>\u003Cp>Speaker 0: And one last parting question before we go. Is there is there anything, you would like to see from us at at Directus, you know, in terms of, it could be features inside the system or, you know, otherwise, you know, something inside the community or documentation or guides or anything like that.\u003C\u002Fp>\u003Cp>Speaker 1: There's one thing I'm looking forward to, but I saw some threads on GitHub already. It's, what's the correct name? Like, if you upload an image that you can, like, select a part of the image that's the most important.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, the focus. Yeah. Focal points. That's Yeah. Focal points.\u003C\u002Fp>\u003Cp>Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Focal that's amazing because a lot of clients upload a picture, and, obviously, your website is responsive. And he's like, oh, but my partner's faces out of the picture is like, okay, but it just scales. So it would be amazing to really have the client control what support for them in the picture. But I think Rick was busy with it. I haven't checked, but I'm looking really forward to that one and my clients as well.\u003C\u002Fp>\u003Cp>Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. That that's a big one. Yeah. I love that feature in some of the other CMSs I've seen and that, I'm not exactly sure how the implementation of that will work, but one I'm very excited for as well.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Me too.\u003C\u002Fp>\u003Cp>Speaker 0: Well, well, perfect. Miguel, thanks again, man. I I really enjoyed this conversation looking at the the projects that you've built with Directus and Nuxt. Amazing work, my friend.\u003C\u002Fp>\u003Cp>Speaker 1: Thank you very much. Thanks. And thanks for having me. It was great talking to you.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. Absolutely. We'll have to do this again sometime.\u003C\u002Fp>","Hi, guys. Welcome back to another episode of I Made This. I'm your host, Brian Gillespie, developer advocate here at Directus, and I have got my extra special guest with me today, mister Miguel Stevens from Studio Monti. Miguel, happy to have you, man. Hi. Thanks for having me. Glad to Yeah. Yeah. So, hey. Like, you and I have talked inside our directus community a couple times through Discord or, I I think, even on several, like, GitHub issues probably. But, you know, for those who don't know you, maybe, give us a little intro or, you know, what's the backstory? Sure. So my studio is called Studio Monty. We're a Belgium based web company. We do lifestyle websites, boutique kind of websites, so not not a big technical prod products, but more like the the well and refined designed projects. And we always offer CMS, of course, which is direct us. So we don't necessarily have to dive in, directly to direct us. Super glad that you're using that. But, you know, tell me a a little more about the agency. Like, how did you get started? What what led you to that? You know? If we back up even further, like, how did you how did you get into development? Okay. Cool. Good question. I think for me, the case, like many people, has been like a a child passion. When I was 8 years old, my dad bought me a a Pentium 386 or what was this called? I don't even remember it. And I was I'm not sure. The 386. Old computer with with floppy disks. Okay. Yeah. And the I'm trying to think of, like, what the what the the megahertz they were running back in the day. I think the first computer I had was, like, maybe, like, 96 megahertz or something like that. Right? The people remember. No, like a diskette disk. How do you call it in English? It's 3.14 megabytes or something. Yeah. There you go. And the floppies were even less than that. But I started experimenting with Microsoft FrontPage, which, like, Dreamweaver is really old, but FrontPage, that's a whole other level. So I think I just roll I I started making small websites with cheat codes for roller coaster tycoon, things like that. Hey. There you go. So then my my whole journey has been development. I worked in some big development companies in Ghent, and I worked in some boutique agencies, but it's always been about about design and development. Yeah. So I I I love both. I'm a 100% developer. I it has to be beautiful. Right? I guess it. Yeah. So and you guys mentioned that you focus on boutique websites. Could you, you know, like, expand that a little bit? Like, specifically, like, what does the average project look like for you guys? Sure. I think with boutique, I mean, those are mostly websites with less than 10 pages. Design is very important. They wanna have something unique about them. So we we don't do it's not like WordPress templates, where you can clear a seed template. We always work with a designer. We we do custom designs, and custom development. It's it's very much focused on the specific needs of each client. And yeah. And then it's in the lifestyle sector, so, it's fun companies, interior design, architecture, things like that. Yeah. Very local as well. Yeah. So, hey, smaller businesses, clients who are very concerned with image and design and, looking professional. Yeah. Is that a fair summarization? Yeah. Very fair. What does your what does your process look like on those projects? Like, on the front end, like, how how you mentioned it's local, but how how do most of these clients find you? Yeah. For us, it's been a case of. Right? So, I think we started only 1 year ago, and all projects we've had have been through local Netflix. You make a website for someone you know. Before you know it, their uncle wants a website, and then you really get started and and your network grows. I do try to there's a we're based in Ghent, Belgium, and there's a good community of, of, people that are, yeah, entrepreneurs. What's the word in English? Yeah. Yeah. You got it. You nailed it. What? Yeah. So there's a lot of events, that we that I tried to go to to meet people, to talk about how we can help people. So it's been word-of-mouth. That's the English Cool. You're primarily. Excellent. Yeah. Talk to me about your process. Like, yeah, being a, like, you're doing smaller projects that are, let's say, not, like, super data intensive. If like, what's your like, what's the time span from, like, conception or or, like, the idea phase to, like, launching a project? Talk me talk me through your process because I'm super curious on that. Like, what those type of projects actually like, what the flow looks like. Okay. Good question. I've been refining this because I feel process is really important to make something replicable and scalable. So what's important for for me is I always start with a very intense strategy session. And so it's, like, 1 to 2 hours where we sit down with the client and really listen, like, what what are your challenges? Where do you wanna go with your business? Who do you wanna be in 5 years' time? It it can be very personal because each business has, like, these typical problems. They want more revenue. They want the right type of client. They want they want their website to provide more information, better customer service for their clients. So we really drill down the goals for each website, and only then do we start going to wireframing to to design and development. Gotcha. Now in that strategy call, is that, like, after you've already quoted them and and they sent them a proposal? Or Oh, yeah. Right. I forgot that part. So I think it starts with a sales conversation. You you call on the phone mostly, or you go face to face and you just listen. Can we help you? I think it's important. The big money topic. There's a lot of interesting videos about this, but a lot of people I read about, a lot of starting people are afraid to discuss money. They all wanna talk about money upfront, but I think it's important that you know what's in what's their budget, what can we do to help them, and, how can we make it work? So it's I think it's this first sales call is is very important. Yeah. That's that's always the toughest, like, conversation. Right? Right. Like, how come I've got a I've I've got a number in my head. You've got a number. Right. Yeah. Like, how do we talk about like, how do you how do you address that with clients? Like, you said you you don't shy away from it. I think during those first 15 minutes, it has to be clear what the budget is. So if it's not clear from them, tell you, you can just ask upfront, like, what budget do you have in mind? And if that range is acceptable for the type of websites, then we can then we can go for that. For example, startups, I wouldn't work with a fresh startup because they don't need a custom built website. Startups can use Wix or Squarespace, which is mostly enough to help them. So it it would be it wouldn't be good customer service of me to try to sell them a website because they don't need custom websites in their first month. Yeah. So I try to be very honest. I'm not the type of person who just takes on a client to get the money and then, no. I can't help them. If I can send them to Squarespace, then we'll happily do that. Yeah. Yeah. It's honesty and transparency. Right? Yeah. Yeah. I love that. That's super critical. You know? Mhmm. And even being like, you said you started the agency about a year ago. I can imagine, like, in the early days, it's a little harder to do that. Right? Where like, hey. We need a couple of projects through the door, but, still, if it's not a right fit, you you turn them away anyway. That's that's, that's pretty cool. Yeah. Yeah. At the beginning, you have to take on a lot of projects, obviously, because you just started and there's no, no real cash flow, but yeah. Yeah. Well, cool. So you have the strategy session. You guys get into, like, their company goals and, like, aspirations and things like that. And then how do how do things progress from there? Sure. Once we know after the strategy session, I make a content model, which is like a sitemap, but for each page, I also include elements. This happens after research. So we look at competitors. You look at the industry. What does the industry expect from websites of that sector, and and, what does the competition do? What do they do good? What don't they do good? And then we discuss some styles. There's there's more meetings. And then it's onto the design phase. For bigger projects, we do a style guide first. If there's a copywriter to be involved, you're a copywriter, obviously, and that's onto design. Yeah. Gotcha. Like, do you have any, like, philosophy? Like, what's your design philosophy? Yeah. I I I think for me, when I used to do projects, like, doing competitor research is is absolutely critical. Right? You gotta know what the landscape is, but how do you how do you not let that inform your own design? You know what I mean? Like, you you don't wanna just, like, copy competitors or you don't want to on the surface, you may not know whether that is actually working well for the competitor or not. Right? So, hey. Like, what is your philosophy on design, and, like, how do you navigate, just hey. Like, hey. Here's what everybody else is doing. Here's what we think is gonna actually work well for you as a to your clients. Mhmm. I think it's it's important obviously to look at the top ten. Right? We we we don't compare with businesses that are on page 20 on Google. And I think it's important if you're looking at, like, what's the content going to be to only focus on the content. It's like you just said, we try to if we see, like, interesting piece of content, I I copy paste the text and put it in Figma in, like, a sort of prototype. We don't screenshot designs because, like you say, if you start screenshotting designs from other companies, your design will be influenced by that. So I think it's important in the in the first phases where the content is being decided, that you just focus on what's what's being written, what's being told. I also saw an interesting video by The Future, and they they make this comparison like, a website has to answer questions. So we always start with that in mind. I write the quote and I'll accept it, and they say, a website is like a conversation. Right? You come to my website. It's like you come into my house, and you you have questions for me. Like, will this product help me feel more whatever. I don't know. So the the order of your website, the content has to answer all people's questions. This is something we I try to keep in mind when building the content model. Yeah. I I love to hear you say that because everybody says that you should be content first, but, like, what I see a lot of times is, like, there's a whole board of inspiration of, like, hey. We like this site. We like this site. We like this site. And then you're working backwards of, like, hey. Let's let's incorporate this theme and, hey. Like, take some ideas from this. And what I'm hearing you say is that you guys you you don't take screenshots of other websites. You might, like, snip some content, but it is very much, like, content driven. There's no like, when you're in the design process, there's no, like, outside influences from other websites. Is that right? Yeah. Maybe a bit. I I mean, I have a designer who I work with, and she, she loves designing, so she's not content first. I'm a big fan of content first, but I think it depends. It's it's one of the biggest chicken or egg problems in the web design industry for me is do you do the content first and then have your designer build around the content? Or do you have the designer design first and then put the content in the available space that the designer chose. It's we we've been trying both, ways of working, and there's no winner yet. I get you. Yeah. I I I think it's just just like the messy middle of, like, hey. Yeah. Like, where things come together. I don't know if there's an answer to if someone has an answer to the the big chicken or egg design versus content first question. Please let me know. Okay. Cool. And sales great. So I like, going through the design phase, like, obviously, hey. Like, are you who does the development? Like, the actual like, are you doing a lot of the actual code at this point or got somebody else on the team? Like, what does that look like? Yeah. I it depends if it's, if it's really busy and I'm like doing a lot of intakes and strategy, then I outsource the development. I'm working with someone who is really capable. I had to look, for a long time before I found him, but I have someone who helps me out. And sometimes I do projects myself. It's good to to stay in the flow. Right? So Nxt is evolving very quickly. Direct is evolving really quickly, and I I wanna, be up to date with everything. So I I still like to to, develop as well. To if it's still like to jump in. Yeah. Yeah. Yeah. Yeah. I I think that's, like, a common case for a lot of us. Right? You get into it. Like, you you love building stuff, and then, like, inevitably, you've gotta take on more of the business stuff, and that gets in the way of of, like, the fun stuff of actually building and creating. And it's very much the same way for me where, like, I I love to build and create, and, like, the other stuff kinda I I I enjoy that as well, but it kinda gets in the way. Right? I I think it's you never wanna have, like, 0 or 100 to to one side. I think it's always I always love developing. Maybe it's the same for you. Yeah. When there's time, it's it's great to just put on your headphones and be in the flow for a whole afternoon, writing code. It's amazing. It's still fun. Yeah. But excellent. Well, what what is the average timeline? So, like, we we've talked about the process. Like, it start to finish, like, how quickly are you you know, how long does it take for the average project to Mhmm. Start to launch? I think in theory, it would take, like, 1 month. But in practice, it's always 2, 3, 4 month. It's because I think there's something we're struggling with. It's very hard to to really I mean, it's only 1 year since we started, but it's very difficult for me right now to know exactly how long something will take. Sure. Depending on on how how fast does the design go, what types of content. So sometimes the client wants to write a copy himself, which is mostly something that will take long because nobody likes writing copy unless you're a copyright. Make your friends. I noticed that clients seems to seem to, like, not want to do it, so they they keep on, delaying it, and then it never happens. So it's it's a combination of waiting for assets. There's, like, a photographer involved, which is something for the types of websites. In the CGM Monti builds, we I wanna work with good photography. And minimal design is really important. So then you have a photographer who has to, be involved in the process. So there's a lot of variables, right, that can stretch or tend the timeline, but you you don't know in advance. Yeah. They like, on the photography side, do you require your clients to to work with a professional photographer? Or Well, this is something I started doing now since the last client. Yeah. I guess It's secured in the in the in the offer as well. I used to add, like, an an add on, like, photography optional, but there people are paying a lot of websites a lot of money for a website, and they're like, I don't need good pictures. So now it's just included in the price, and it's, yeah. Oh, that's smart. Yeah. I I always hate to see a well designed website and then, like, the same stock photos that you've seen on 35 other websites. It just ruins it ruins everything. The stock photos are the worst. I think the dark iPhone picture by the owner is even better than the stock. Yeah. Yeah. Right. Yeah. At least it's real. Yeah. Or it feels real. I I'm sure that's a real person in the stock photo, but, you know, some of those stock photos, I I don't know, like, how people make those poses. Like, I think you never see that stuff in real life. Right? Well, I think it's it's your eye sees it directly. I mean, your stock photo, you can see it from very far, and it it gives the site a sort of fake feeling. It's it doesn't feel right when you see stock photos. Yeah. Yeah. So, like, shifting into, like, the tools that you're using to build the sites, like, what does your preferred, like, stack look like? You know? Right? What tools are you using on the front end, on the back end, that sort of stuff? So it's, for us, it's Nuxtree on the front end and then Directus, on the back end. Yeah. Gotcha. We're running it on Docker containers for our production servers as well as, local local hosting. It's also on Docker. Nice. Yeah. You're definitely one of my preferred stacks. But, like, what what led you to those 2 pieces? Like, you know, were you using those a year ago when you started? Or yeah. I know you mentioned front page that you were using way back in the day to build stuff. Like, walk walk me through the progression because that's that's super interesting for me because I I remember front page. I remember Dreamweaver. Right. I even I'm I'm not sure I I'm not sure if Angel Fire or, like, GeoCities were ever popular outside the US, but it was like a it was like a free website builder where you could get on and have all the animated GIFs and, like, all the all the crazy counters and all that stuff back in the day. But that's that's how I got my start in the industry as well. It was back in the day using those tools to kinda scratch stuff together. I think that you said this rings a bell, but I I never heard of for this Angelfire. Angelfire. It was yeah. It's basically like a a free website builder host. Like, you could you could throw, like, crazy backgrounds and stuff on there and write your text. But the rest of it, you know, back in those days, it was all just whatever HTML you could throw together yourself. Yeah. Marquee elements and stuff like that. Yeah. Yeah. Oh, yeah. So, I started my development. I mean, my first company I worked with was Laravel. So it was PHP. I loved Laravel. It's it was easy to use for me. It was a a dream come true after writing spaghetti code in PHP. But the older I got, and I started working with Vue, and I started loving JavaScript a lot. For me, it feels much cleaner, the syntax and PHP. I'm stirring some feathers here. I know. It's a big discussion. PHP lovers are everywhere and the JavaScript lovers. But for me, it's always been JavaScript. So when I started this company 1 year ago, I promised no PHP. So, my first thought was, okay. I need a I need a JavaScript back end. So I started looking into headless CMS systems. Strapi Strapi. I don't know how you pronounce it. Yeah. Was the first one I tried, but I I found it difficult to work with. I don't know. It was it didn't feel right. So I tried a few others. I tried story block. I tried content folks for a while, and then someone on Reddit mentioned Directus. Mhmm. I think version 10 was just released. When was it released? Do you know version 10? Version 10 or version 9. Version 10 was that's Yeah. Version 10 was not so long ago. Version 9 was, like, a big change. Version version 8 was on PHP. Oh, really? Version 9 version 9 was, like, the switch to to node to, like, the JavaScript side of things. And that was a a crazy big shift. That was about the same time that I actually found directives myself. Okay. That's when when version 9 was released, which was You weren't there in the BHP era. I I was not. No. I I wasn't even aware of the tool, which is Okay. Which is wild. Yeah. And that's why I think it's someone on Reddit brought me to Directus, and I I tried it out, set it up. The documentation is great, and I just got it working. And then I just built my first website. Yeah. Nice. So I like, what were you said, like, the other tools didn't feel right, and that that definitely feels like a like a designer thing of a it doesn't feel right. But were there a like, any, like, issues that you encountered that were you were like, hey. This is this is not working for me? Like, can you even remember any of those tools? Like, what were the the red flags, like, if you can remember any? Yeah. I think for me, now that I think about it, the biggest thing was since I mean, headless CMS systems can be used for, like, internal tools, for softwares. But for our studio, it's always going to be a CMS system. So my thought was if a client I remember with Strapi, I thought, if a client has to work on this, it's gonna be complicated. And for with directors, it just felt very calm. I could see my even less tech savvy clients work in this system. So for me, it was mostly a choice of, okay, which system are people my clients going to be able to work in without having to call me too much with questions. It it Not having to do a 12 hour CMS training. It is very much from, like, the client perspective. Yeah. I I can remember that personally, like, seeing it like, I I went through a bunch of those as well. And, like, the seeing the white space and just I you mentioned calmness or the the calm, of, like, hey. This is really well designed, and it's open source, which is great. Yeah. But I can remember looking at it and like, hey. Like, okay. Yeah. This feels right. Yeah. I think it was a gut feeling. It's like a first impression. Right? You always set up doc. You open the tool and it's okay. This feels good. And I think what I also remember is the image handling direct is is is perfect. For CMS systems, there are always gonna be images, and the I think the file library and all the systems around it are built really, really solidly. So, yeah, that's a big plus as well. What does, like, the average project look like for you inside Directus for for a client? You know, like, maybe through, like, the data model. You know? Or maybe just talk me through some of those, and and I think you've got a few things to show us. But, maybe just so you could, like, talk me through how the projects look inside Directus before we jump into that. Okay. Cool. I think the last three projects were mostly similar. So we have pages. We have a page builder using the, many to any builder system. And then we have just like projects, testimonials, the team page. So it's mostly, managing data. We're not too heavy on flows at the moment. There's a few flows for, like, building, which we'll get into in a in a few minutes. But, yeah, it's mostly page builder. That's the biggest, chunk. And we use blocks. So every page, you can just, compose yourself with blocks. Clients don't really use that too much. It's mostly for myself. A block is reusable. It's a nice, neat piece of code, which can even be reused in in other projects. So yeah. How do you, like, how do you present, like, the CMS and, like, direct us to your clients, like, through that phase? Is that, like, a discussion that you have, like, at the very beginning of a project? Is it like, hey. Oh, we got your website. You're gonna be able to edit all the content through this CMS. Yeah. Are you, like, talking me through how you present that solution to the client? Sounds good. In my last, few offers, I included, screenshots or a video of directors. And the good thing is that clients mostly come from WordPress. So when they see directors, they're like, this is impossible. They almost can't believe how easy it is. Crazy. The the reactions are just amazing. The last two clients were really like, this is so easy. Essentially what they said. I sat next to them for the CMS training. We put in some data together and just they came from WordPress. They were like, this is really easy. So it feels really good to have clients, have this this sort of really good experience. Now are are most of them coming from, like, the the drag and drop page builders where they've gotta go in and manage, like, all the padding and spacing and colors and all of that? Okay. Yeah. Or from WordPress installations that are like a plug in hell, and everything's a plug in, and there there have to be and people don't like that. I think directors, you can also you can protect your clients. Like, you give them roles, and they only they can only edit what they're assigned to do. I know you can do that in WordPress as well, but WordPress feels like very, very much. And clients, they mostly don't have a lot of time. They maybe want to add a new testimonial. They had just want to log in, click testimonial, add new, and be done with it. So Yeah. They they don't wanna, yeah, geek out on the the software or the tools like like we do. They just wanna get them out. They've got other stuff to do. Yeah. They don't care what tool it is. It just has to be easy enough. Yeah. Cool. Well, do you wanna maybe share your screen and and show us, you know, like I would love to share with the audience, like, what the the typical, like, front end project looks like for you, And then, you know, maybe show us around the the back end if you're if you're able to. Yeah. Sure. Let me just share my screen real quick. K. Cool. So this is a project we finished, 2 months ago. It's a website for a company that does funding. Yeah. We like white space. We like beautiful fonts. These pages are so this is Nextree. This is a statically generated website. So Okay. It's on it's built on Netlify. So it's not SSR. It's SS SSG. What's what is the what's the the display font there? Like, all the headings. The this font? Yeah. I I I really like that font. Have a look. I should know that. Ah, silica. It's silica. Right. Silca. It's nice having font. Yeah. Yeah. It looks great. I and, like, I've got another project that I'm working on personally that, I I've been eyeing fonts, and, like, that process is always, like, such a nightmare of, like, going through and trying to suss out just the perfect font for a project. This one looks great. Right. Might have to worry about. Beautiful fonts are mostly really expensive, which is fair. True. True. And this is actually just a page built and page builder. They have cases, which are like, yeah, project cases. And what I love about this, it feels really snappy. I mean, this is Nuxt. Yeah. It's pre rendered, so that's the speed is the speed is amazing. Speed score is also really good. There's a form in here which will, be sent to directors as well. Nice. Yeah. And it's a dynamic form if you if you go to a case and you wanna be then your case will be prefilled here. This all will be sent to directors. Yeah. That's cool. What's a great great looking site. Yeah. Thanks. Very clean. Yeah. What are you, what are you using for, like, the the transitions and the animations? Is it, are you, like I'm just custom coded CSS, or is it, like, a different library? It's a wrapper component. It's called scroll transition dot view. I made it together with someone else, I think. We used it in a previous project. Yeah. And it just, yeah, it shifted into, into view. As yeah. It's it's very tasteful. I think for me, that's one of the other things that I always struggle with on some websites is, like, everything is animated. There's, like, a crazy amount of motion, and it it just distracts from the the story in some cases. But this is this looks really well done. I think it it has to be subtle. What I would like now is for, like, working on some images that, like, slide in from the right. Some some tiny animations, but I don't want it to be much more than this. Yeah. It looks great. So for the for the back end, as you see the models we have, this is heavily based on the agency OS, by the way, which I think which you wrote most part of it. Yeah. Most of it is mine. So thank you for this. Yeah. No. Great. How No. Happy to you know, that project was like a labor of love because it it it took me a long time to figure out how to manage, like, the page blocks. And I was like, okay. Like, once you see it and and you've got a a reference, it's much easier to understand. But when I first came to Directus, I knew this functionality was available. It just like, there weren't any good references reference points to to use. So I I'm happy that it's been helpful for you. 100%. Oh, it yeah. It is. I don't know if I would have managed without. I think it's also it's a plus point because there's a lot of back end systems, but you guys took the time to write out a full front end to showcase how it can be used. So that's I'm very grateful for it. It's, it's amazing. You probably see it's heavily based on agency OS. Right? Yeah. There's a page. You have the blocks. You can move them. Yeah. It looks very familiar. Yeah. This marquee, this text, that's the one you see here. So it's it's very easy for the client to work in. What's interesting about this project and, something I told you about is the the the flows, that we built. Because it's statically generated, you can use, like, a Netlify hook that each time you save a change you made, Netlify will start building. But the problem you get when 3 or 4 people are changing content is you get a queue of 20, 30 builds, the day. Yeah. Or the client changes a couple of different pages. Right? And they They go through it. They they press save. They change something else, and it's it's triggering bills the whole time. Yep. And on NettoFi. So what, I contacted I wrote down his username, Arud Arud on Arud. Yeah. Arud. Do you know him? Yeah. Yeah. He, he won several of the or I think he won at least one of our hackathons. Okay. Nice. Yeah. Yeah. He started to on on Discord. Yeah. Very helpful member of the community. 100%. So he started the idea of, these 3 flows you see in green, and then I finished it. There was one issue, and we worked on it together, reached out a bit. So what this does is every time a change is pushed, we set a flag, like change pushed through, and we set a time stamp. Mhmm. And what these three flows together do is they only trigger a build every 5 minutes. So you could And and only when there's changes. Right. Yeah. Oh, that's cool. For example, this one, every 5 minutes, it will look inside the general settings and check if the variable has changes if it's set to true, and that will trigger this other flow. Okay. And this other flow will trigger an Sify build and update the current build time and update the settings. So it's, I gotcha. Yeah. And then there's one more, the publish queue on save. This one just watches, if you save anything. Okay. Yeah. So it's watching the content collections. Yeah. All of them. Yeah. Gotcha. And then it checks if it's older than 5 minutes, and then it triggers a flow. Or if it's not, it updates that we have some changes. It looks easy now, but we had some work with this. Yeah. Yeah. I can imagine. Hey. Like, hey. Trying to figure out, like, how to structure the flow and and actually building it, I'm sure. Like, how long did it take you to actually nail it nail the the flow, like, these 3 separate flows? Yeah. Is it well, Arutz did most of it. It was almost great. He had some issues that were remaining. I think we fixed it with, I'm not sure. I don't remember really because I was also a a FlowDB. I I hadn't used Flows before. So for me, it was a a very thorough introduction into flows. I yeah. Yeah. Well, now you're an expert. So it was awesome. Mostly with a with a bit of help from Discord. Yeah. Hey. The community members are amazing. Yeah, that that was how I learned most when I first started with Directus, as well. I've had I don't know if the other have the CMS systems out there, have this community, but the Discord is amazing. It's for me, it's a it's a big plus. I mean, probably everyone has good community, but the the the director's Discord is really amazing. Yeah. Yeah. You you you gotta get this one updated too. I see you're on 10 dot 6 dot 3. You can get it updated to to 10 dot 7 so you can, use the theming. And I think Yeah. I think 10 dot 8 went actually last night as well. Oh, really? Which has, like, some preset themes that are available. But Okay. Nice. Yeah. I saw the video you guys did on the what was enabled to week with with all the everyday video? Leap week. Leap week. Yeah. Very nice. I think, like, content versioning is also under 10.7. Right? It's yeah. That's on 10.7 as well. Yeah. It could be I I'd say for your clients, maybe not, super helpful. I I doubt they have, like, a ton of different versions of content, but for, larger sites where you need to to manage changes, definitely helpful. Mhmm. Yeah. And the team, and I need to try it. It's a coincidence, but the director's team is matches this website really good. Yeah. Yeah. Yeah. I love the purple. Yeah. Maybe that's Yeah. Maybe that's what, issue what drew my eye to it or or what I, yes, subconsciously I was like, hey. This looks amazing. That purple plays into it for sure. Yeah. There's, there's one more website I wanted to show. This is Yeah. Yeah. Definitely. This is a website for an interior, high end design store, in our city, and they they loved, directors. They used to work with WordPress, and they were very excited when I showed them the finished CMS and how easy it was to change things. But this is also a project very heavy on good photography. And Directus was great in this one because I managed to use the the block system and and the for, like, for example, if we have a a blog and there's a Mhmm. This is in Dutch. I don't, suppose you can read it. But the we are using the blog builder. I think I I I read it here. If you go to the to the block, and then you have a block. And we're using the blocks as well to, really write the content. Yeah. Yeah. So what do you do? A client can embed, like, rich rich content, like videos and and things like that. Video is not yet. Sorry. But images, we can this is a a text block. They could add a a gallery block in which they can combine images. Yeah. So it's Nice. Yeah. It's cool. Like, especially because it's very photo heavy. And they do I learned a lot from from directors for this project, especially like the blocks. Sorry. Oh, I was gonna say, do they is it a furniture business? Is that what it is? They do high end furniture. Like, the Eames chair is something they sell, like, the classical higher end brands. I gotcha. Yeah. So they they you just have they have a big collection as well. For example, is a brand. And in this one, they can also write text, and then you can add these images themselves. And inside directors, they can choose. I want this image to be a size small, medium, or large, and the grid will using a series as grid, we'll place them like this. So it was a fun project. Learned a lot about directors, about the the the m to a system. Yeah. Yeah. It looks really nice. Yeah. And I like that you're giving clients a little little control over presentation, but, you know, like, not enough to break the design. Yeah. Yeah. And so I think that's the problem with with WordPress sometimes is there's a lot of things the client can do wrong, and and I think people like it. You can just change only what they want to change. Right? Yeah. Like, honestly, like, I the clients that I worked with in the past, like WordPress and and stuff like Squarespace, it gives them it it's distracting. Right? Because what I really need them to do or needed them to do was go in and add content and photos, and what they ended up doing was, like, tweaking the spacing. Like, you know, like, hey. This doesn't look right. Or Yeah. Yeah. They spend 3 hours tweaking the design on the website, and they and they skip over the content. Right? Yeah. I know what you mean. I know exactly what you mean. Yeah. Yeah. Well, I I appreciate the tour. It looks amazing. You know? What's what is the the future? It's something I I like to ask everybody. I I before we we started recording, you said you gave a a talk on chat GPT and stuff, but, that would could be an interesting little thread. But what what are your future plans for your agency? You know, what what kind of projects are you guys working on now or or getting ready to launch soon? Like, what's in the cards for you? Yeah. I think that I have some exciting I have 2 projects coming up, and they both need multi language. It's something which I'm very afraid to start doing because I don't know how I'm gonna do it. I was hoping you would have done it in the agency OS. That's the next version. Right? I'll wait for you to finish that. Yeah. Oh, so I think, with directors, I I took on some small projects. I blocked one really big project because I was afraid because I don't know yet how to do it. So for me, the future really is to get these bigger projects up and running in directives, see if I can use multi language, together with Nuxt and the block builder system. I know it's possible in directives. I just have to find out how to do it. Yeah. I I think it's a matter of just, like, getting into it. It's it's it's one that I'm keen to explore as well. So I like, as you get into it, definitely hit me up. Yeah. The like, the translation interface, super easy to use inside Directus. Like, the the pieces that I'm not a 100% on, and maybe somebody watching this can, like, school us both. It would it's just, like, how to how the best way to architect that on the front end and and, like, connect it. And I think there's for Nux, there's the, there's a module, I think, that that would potentially play into it. I haven't messed with it, though. The You mean the i18n module or yeah. Yeah. I haven't messed with it yet. I think it's it's fairly I've used it with a static website. So where the the the translation strings are just adjacent file, it works perfectly. It even hooks into the transition state so that when you change language, you have a fade out that changes. Oh, nice. Yeah. I think, well, the question will be, like, how do we like, if you do a request to to direct us for a page, do you give back all translations and you filter them out on the front end? Or when the user changes language, do you refresh the request to direct us? I think that would be the way. Yeah. That's probably how I would do it. Yeah. I I have to connect you with, the the guys at Heinrichs and Heinrichs. They were one of the the previous episodes. On email. Yeah. They've got yeah. They've they've got a project that, they're I I I don't think it was actually, like, 2 different languages, but they were using the translation inside Directus to to manage that side of it. Uh-huh. It was like 2 locations. Like, they wanted to have 2 versions of the site, but it was all it was all still in English, but they were still using the the translations interface for it. I remember. I think they used the translations interface to show these two locations. Right? Yep. I remember you did a video with them as well. Very interesting guys. Both of them. Yeah. So or what's the the future date like for the agency? Or, like, you know, what are your your goals for, you know, I guess, like, a little longer term? You know, a year, 2 years, 3 years, 5 years? What what's the future on that side? Yeah. In the coming year, I wanna be, like, become really good in just next time directors. I mean, some new projects require me to do look something up like this multi language thing. This is something that I don't it's hard to estimate. Client asks how much it's gonna cost me more to have multi language, but I don't know. So I'm gonna be happy once I I've built a few very complex, integrations between Nuxt and Directus so that I can quickly and correctly estimate future projects and just get them up and running much quicker. And then there's also I have a whole second company I'm starting called Waiyo. It's a it's a we're gonna build sort of a SaaS platform to connect patients with medical professionals. So I want to start exploring multitenancy and directives as well, which is also a big topic. Yeah. Yeah. Definitely. I I look forward to seeing more about that project, Miguel. Yeah. Well I'll be happy to share all my findings with the community. Yeah. Yeah. Yeah. That's one thing that I love about everybody that works with Directus. Yeah. They're is so helpful and and just willing to share. I I think, you know, ultimately, that that's what, like, an open source product, like, yeah, that's one of the ways that, we can compete with a as a smaller team with with, like, larger products, you know, some of the the SaaS based headless CMS and and other products. But just our community, amazing community and and full of supportive people like yourself and Arud. We'll make sure we'll get him a a shout out again in this thing. Do that. Yeah. I feel like I wonder if, like I think, like, head headless CMS feels like the future to me. Just it's so obvious to decouple the front and the back end. It's it's just like your your data platform and you can do anything you want with it. So I look forward to pretty seeing more of that evolve, in the in the near future. And one last parting question before we go. Is there is there anything, you would like to see from us at at Directus, you know, in terms of, it could be features inside the system or, you know, otherwise, you know, something inside the community or documentation or guides or anything like that. There's one thing I'm looking forward to, but I saw some threads on GitHub already. It's, what's the correct name? Like, if you upload an image that you can, like, select a part of the image that's the most important. Oh, the focus. Yeah. Focal points. That's Yeah. Focal points. Yeah. Focal that's amazing because a lot of clients upload a picture, and, obviously, your website is responsive. And he's like, oh, but my partner's faces out of the picture is like, okay, but it just scales. So it would be amazing to really have the client control what support for them in the picture. But I think Rick was busy with it. I haven't checked, but I'm looking really forward to that one and my clients as well. Yeah. Yeah. Yeah. That that's a big one. Yeah. I love that feature in some of the other CMSs I've seen and that, I'm not exactly sure how the implementation of that will work, but one I'm very excited for as well. Yeah. Me too. Well, well, perfect. Miguel, thanks again, man. I I really enjoyed this conversation looking at the the projects that you've built with Directus and Nuxt. Amazing work, my friend. Thank you very much. Thanks. And thanks for having me. It was great talking to you. Yeah. Yeah. Absolutely. We'll have to do this again sometime.",[5097,5098],"bf2b98b8-d1cd-4627-9149-c88b650aae8d","e4693828-538f-4880-9f4f-5f01c6826ca6",[],{"year":2380,"number":5101,"id":5102,"episodes":5103,"show":5107},24,"7656df39-eed0-444b-b9af-9e76986b8052",[5104,5105,5106,5078],"6444b7b8-734e-4b2e-abf2-844210988c13","fd462afa-f23e-4f5b-a242-5e7364f872fb","9b8ac3af-e201-4429-96de-699aebd117ea",{"title":5108,"slug":5109},"I Made This","i-made-this",{"id":4170,"slug":5111,"vimeo_id":5112,"description":5113,"tile":5114,"length":1078,"resources":12,"people":5115,"episode_number":129,"published":5117,"title":5118,"video_transcript_html":5119,"video_transcript_text":5120,"content":12,"seo":12,"status":19,"episode_people":5121,"recommendations":5123,"season":5124},"community-platform","896091486","Every thriving community needs a home. Dive right in alongside Bryant and he builds a community platform inspired by Circle. He races against the clock to build an engaging and interactive community space in just one hour.","903b4fe1-bb22-4e2f-b946-7fce5cff7a11",[5116],{"name":3590,"url":3591},"2024-01-22","Mission: Community Platform","\u003Cp>Speaker 0: Hi guys. Welcome back to the next episode of 100 Apps, 100 Hours. I'm your host, Brian Gillespie, developer advocate at Directus. And today, we will be building a community platform. Now, this is one that is near and dear to my heart because I have built a community for sign and print shop owners myself.\u003C\u002Fp>\u003Cp>We use Facebook Groups for that. Not the best community platform, but it is free and everybody already has a Facebook account. But in just a quick look at the research for this episode, There's quite a few different tools out there that I located, Circle being one of the more popular ones. It's not exactly a forum, it's not exactly a course, it's not exactly chat, it's all of those things. So the pieces that we're really going to dive into today are this concept that they have of spaces.\u003C\u002Fp>\u003Cp>So each one of these items in the left navigation is a space. A space can be, either private or public. So I can see here some of the circle experts and this is their their own example community. Right? They do have courses in here.\u003C\u002Fp>\u003Cp>We've already done an episode on LMS, so we won't really touch on that. But, if we get into this, we could see what's new in the product. So this is an update we're gonna post. We've got some comments down at the bottom, I'm assuming, and you had to be logged in to comment. So how do we kinda recreate some of this functionality?\u003C\u002Fp>\u003Cp>It looks like they have weekly events here, but we're probably not seeing those. And maybe here's something similar. So anyway, that's what we're building. If you, have watched any of the other episodes, you probably already know the rules. We've got 60 minutes to plan and build, no more, no less.\u003C\u002Fp>\u003Cp>And the second rule, which is not really a rule, is use whatever you have at your disposal. So, any of the tools that are out there, AI, Tailwind CSS is one of my favorite ones. We're going to be building this platform on Directus as well. That'll be our back end. You could probably even actually build the entire thing inside Directus, but we'll probably go for a custom UI on the front end.\u003C\u002Fp>\u003Cp>Alright. So, sounds great. Let's rock and roll. So we'll start the 60 minute timer and away we go. So anytime I build an app, I always love to just kinda flesh out the functionality ahead of time.\u003C\u002Fp>\u003Cp>So if we're looking at this specific app, maybe I'll just pull up a text box inside Figma here. Here's the functionality that we really want out of this app. Right? We want to create and manage different spaces. Spaces for a how do I can I constrain this?\u003C\u002Fp>\u003Cp>Yeah. There we go. For a community. Maybe we have events as well that we're going to list. List and create and manage events.\u003C\u002Fp>\u003Cp>Events. We need to be able to log in. Users can log in. Create posts. Right?\u003C\u002Fp>\u003Cp>Is that what we're going to call that? Maybe a thread? We'll get into it. Right? We'll create some posts and add comments to those posts.\u003C\u002Fp>\u003Cp>That feels like a a pretty good chunk of functionality. I don't know if we'll get to all that or not. That's, like, actually building the UI for that, could be fun. Right? Alright.\u003C\u002Fp>\u003Cp>So let's just, like, flesh out our our actual data model here as well. So again, I'm not the savviest Figma user, but I try. I'm a tried. Alright. So what do we have?\u003C\u002Fp>\u003Cp>We've got spaces. So those are gonna be our little homes for all this different stuff, this different type of content. Right? Then we've probably got, this is where we get into the naming. Right?\u003C\u002Fp>\u003Cp>Is it a a thread? Is this a what what are these things called? Are these posts? Yeah. Let's go with that.\u003C\u002Fp>\u003Cp>Right? This is a post. And a post has some comments. Probably has a probably has, like, a post type, I'm assuming. K.\u003C\u002Fp>\u003Cp>Let's look at what else we've got here. Share your wins. Log in. Yeah. Okay.\u003C\u002Fp>\u003Cp>So these are different posts. You can upload images to a post. You can add comments. You can like. So we probably got some likes in there.\u003C\u002Fp>\u003Cp>And I don't know that we'll get to that either. But, what else do we have? We have users. That's gonna be taken care of by Directus because it gives us that out of the box, which is nice. That'll be our Directus users collection.\u003C\u002Fp>\u003Cp>And what else do we need for a function community? Right. Are events gonna be a separate thing than posts? I don't know. Let's we'll try it out.\u003C\u002Fp>\u003Cp>Cool. Let's just add that to the end here in case we we don't have time to get to it. Alright. So this feels pretty good. We're closing in on 4 minutes or so in the planning stage.\u003C\u002Fp>\u003Cp>Alright. So what do we have set up? We have, we got Directus going here. I've got a Directus instance that is totally blank, so I just reload. You'll see there are 0 collections.\u003C\u002Fp>\u003Cp>And on the front end, I've got a starter Nuxt application that just basically has the Directus module or the Directus plug in already preconfigured. We're using the SDK to communicate with the Directus instance, but I've just got Directus spun up on a local Docker instance. So, really fancy app here. We've got Tailwind. We're using Nuxt UI as well on that front end.\u003C\u002Fp>\u003Cp>So got quite a few things going on there. Alright. So first and foremost, we've got spaces. Let's go ahead and create our data model. Directus makes this super easy.\u003C\u002Fp>\u003Cp>We'll just go into our blank instance. We'll create our first collection. We're going to call it spaces. So those are the different spaces here. We'll generate the UID.\u003C\u002Fp>\u003Cp>We'll probably have a a status for the space. What's the sort order for the spaces? Who created these? When were they created? That's all pretty standard.\u003C\u002Fp>\u003Cp>Let's just take a look at the network requests on this to see if we can actually pick up some of their data model. Alright. Space groups. Alright. There's the circle community.\u003C\u002Fp>\u003Cp>Allow members to spaces. What else do we have? There's a slug for the space. So that's, that's a handy piece of information to have. Spaces, discussion.\u003C\u002Fp>\u003Cp>Okay. So we've got a cover image. Cover image URL. And this is via their API, so it doesn't necessarily represent the underlying database structure, but this data is coming from somewhere. So we're gonna have a name for the space.\u003C\u002Fp>\u003Cp>Name for the space. That's great. Alright. Do they have a description? Space description.\u003C\u002Fp>\u003Cp>Space name. We're definitely gonna have a slug, so that's great. We'll roll with that. Advanced field creation mode and, when I need something like a slug or a value that URL safe, if I go into advanced mode, I have Slugify available. So that will automatically transform the input as I type.\u003C\u002Fp>\u003Cp>Lock screen heading. I don't know, man. Let's just give it a description. And let's keep that non WYSIWYG. And last but not least, for our space, spaces, Let's add a cover image.\u003C\u002Fp>\u003Cp>So we've got our cover image. Right? And let's see if they had do they have a type of space? Space, slug. Visibility, post type is basic.\u003C\u002Fp>\u003Cp>I I wanna say there's probably some setting in here to control what these things look like, whether this is like a discussion or this is an event or a course. As we're fetching these, okay, that's a course. Internal spaces API. Maybe a type. Is there a type?\u003C\u002Fp>\u003Cp>Course type. Type is a banner. K. Those are yeah. No.\u003C\u002Fp>\u003Cp>I I don't wanna waste time on it. Right? Cool. So status, we'll just leave blank for now. Is this published or is it available?\u003C\u002Fp>\u003Cp>And then let's say let's add 1, like, is private. This will be a toggle. So the Boolean values Boolean value, I always used to struggle with that. That's what we'll save in the database. Is this space private or not?\u003C\u002Fp>\u003Cp>And then we can do a little cleanup here on our fields. So name, slug, description, maybe we pop that right beside there. And status wise, let's just roll with it. Right? So we go in, we create our first space.\u003C\u002Fp>\u003Cp>Maybe we wanna call this discussion, and that'll be at the slug for that will be discussion. Here's where we discuss stuff. Great. Is it private? Yes.\u003C\u002Fp>\u003Cp>Let's make it private. And then we will let's upload a cover image. Right? Unsplash. Let's just search for discussion.\u003C\u002Fp>\u003Cp>Okay. Images that are not seem to be working. Okay. Maybe some type of Internet issue. We're working here locally, anyway.\u003C\u002Fp>\u003Cp>Let's just grab from Circle. Right? We're already working with this specific thing. Do they have discussion? Where's the best practices for engagement?\u003C\u002Fp>\u003Cp>Yeah. Fine. Let's roll with it. Cool. Alright.\u003C\u002Fp>\u003Cp>So now we've got our first space. What else do we need to add to this? Right? So we've got posts for the spaces. Then each one of those posts could have comments, potentially.\u003C\u002Fp>\u003Cp>Right? So, let's just go in and create another. We'll just call it announcements as the next space. Announcements, read only channel for important announcements. Very fancy.\u003C\u002Fp>\u003Cp>Important announcements. Alright. So that is not private. Let's make both of these published just to to have it. Great.\u003C\u002Fp>\u003Cp>Okay, so we'll go back into our settings and let's add the next item. Let's add posts. We use the UUID. Do we have a status for the post? Yeah.\u003C\u002Fp>\u003Cp>Probably. Date created, date updated, is there a sort for the post? Do we wanna control the sort order on those? Yeah. Maybe potentially you do.\u003C\u002Fp>\u003Cp>Alright. So if we take a look at circle here. Let's dissect one of these posts. Right? So a post has a title, looks like.\u003C\u002Fp>\u003Cp>Also has a user that created the post. So Directus has taken care of that for us out of the box. We probably got a date that this was created. We've got some rich text for the post. So that'll be the post content, maybe?\u003C\u002Fp>\u003Cp>Post body? You do that. Let's just keep it as content. It seems pretty straightforward. Naming stuff is always the the hardest part about development.\u003C\u002Fp>\u003Cp>What else do we have? We got that cover image for the post. Right? Cover image for our post so that, you know, if we want to upload a cover image, we can. Sweet.\u003C\u002Fp>\u003Cp>Looks great. Let's go in and just create one of those posts. Right? So now we've got our post. Let's add a published post.\u003C\u002Fp>\u003Cp>And, Kate, if you're watching this, I'm sorry. I'm just gonna steal this post entirely. Use it verbatim inside this platform. And I'm also gonna steal your thumbnail image. Don't sue me, please.\u003C\u002Fp>\u003Cp>Alright. So we've got our post. Great. Looks savvy. Okay.\u003C\u002Fp>\u003Cp>Now we need to tie these together. Right? We don't have it's just floating out in space. It's not inside a space, it's in outer space. Alright, so let's tie that together.\u003C\u002Fp>\u003Cp>We're going to use the relationship indirectus for that. We'll just go to our spaces and one space could have many posts. But I think a post always belongs to a single space. We wouldn't want posts in multiple spaces. So that is a good example of our one to many relationship.\u003C\u002Fp>\u003Cp>There's one space, many posts, and these are gonna be called posts. The foreign key inside our post table, we'll just call that space without the s, because it's just gonna be 1. Alright. So we'll save this. And I can go into advanced mode inside Directus just to to make sure everything that I want is getting created.\u003C\u002Fp>\u003Cp>And maybe we I don't know if we actually need that sort field or not, but it's fine. Let's do a post title. I probably could have went with the post name, because we had a space name and not a title. But, again, we're splitting hairs here. There's no right or wrong when when you're naming things.\u003C\u002Fp>\u003Cp>There's only shades of gray. Some things are bad, some things are not. Alright. So we've got our spaces, we've got our posts, we've got a relationship between them. Right?\u003C\u002Fp>\u003Cp>So if I go into my spaces now and I click add existing, I can add that existing post to our announcements space. I could save that. And then if I were to go to our discussion, I could potentially pull that in here, but because of that relationship, it would decouple those. Alright. So next we've got, what, comments?\u003C\u002Fp>\u003Cp>Posts could have multiple comments. And let's do date created. We really don't have a status on these, do we? Like, you know, if the post if the comment is edited, we'll we'll be able to tell by the user updated field or the date updated field. So there we go.\u003C\u002Fp>\u003Cp>We've got the comments content, content body, comment message. Yeah. Let's roll with message. That's fine. So now we've got our comments.\u003C\u002Fp>\u003Cp>Let's send that relationship to the post. So we are, again, going to do a another one to many relationship. Right? Because we have a post. One post has many con comments.\u003C\u002Fp>\u003Cp>Okay. So oh, actually, we're on comments here. Right? So we need the inverse relationship of that. We've got the many to 1, because we were on the comments.\u003C\u002Fp>\u003Cp>So the key here will be post. Our related collection is posts. I'm gonna open up advanced because I am going to add that corresponding field inside the posts collection. That's gonna be comments. Great.\u003C\u002Fp>\u003Cp>We can add our title there. Cool. Okay. So we've got comment, we've got a post. Now if we were to go, actually, let's just hop over to our post, make sure this is showing.\u003C\u002Fp>\u003Cp>Alright. Maybe we wanna show the space that this is attached to as well. Go into my post. Here's the post. There's the title.\u003C\u002Fp>\u003Cp>Here's the space. I can go in and add a comment. Really simple. New comment. Bada bing bada boom.\u003C\u002Fp>\u003Cp>Got a comment for that space. I can see it here, though we can't actually read it. It's just showing the UUID. So I can go back in and maybe we clean this up a little bit where we got our spaces, got our posts. And here in the display template for this, I could show the actual message.\u003C\u002Fp>\u003Cp>And if I wanted to, I could even get into the the weeds a bit here and show the user avatar. Is it gonna be the avatar or maybe the actual avatar thumbnail? Yeah. There we go. Looking nice.\u003C\u002Fp>\u003Cp>Cool. Let's see what that looks like. Inside our post, new comment. We don't have an avatar for for little old me, so let's solve that really quickly as well. Just do right.\u003C\u002Fp>\u003Cp>Right ahead. Where are you? Me. There we go. Cool.\u003C\u002Fp>\u003Cp>We'll go ahead. Alright. So I could see myself that I made a comment. We can see the space it's attached to. What what are we missing from our functionality?\u003C\u002Fp>\u003Cp>Right? Comments, likes, and we could do that. Post, comments, likes. I you know, do we even really wanna do likes on this? Yeah.\u003C\u002Fp>\u003Cp>I think this is fine. Right? Let's let's roll with this for now and get something happening on our front end. Let's build something cool. I'm sure I'm missing something.\u003C\u002Fp>\u003Cp>But oh, what if we had, like, threaded comets? Right? I don't know if the circle support that type of maneuver. Looks like it does. Right?\u003C\u002Fp>\u003Cp>So, I can also have recursive relationships. So this will be a good look at what's available inside Directus as well. So I could go in and create recursive relationships. Boom. Boom.\u003C\u002Fp>\u003Cp>Boom. Boom. Boom. And, you know, we we could take care of this on the front end. Or, you know, if I wanted to show that recursion, we could do it here.\u003C\u002Fp>\u003Cp>So we'll add a parent key, and then we'll just reference that same collection. And let's do, like, children. Sounds cool. Great. K.\u003C\u002Fp>\u003Cp>And as far as the interface, I could also do, like, the tree view, which would show, like, nested recursive items, as it says. But, maybe we don't drill down any more than one layer here. Cool. Alright. So now I can have a comment.\u003C\u002Fp>\u003Cp>I can have, a parent underneath that. Parent comment. Oh, did I get this backwards? Yeah. Maybe I goofed that up a little bit.\u003C\u002Fp>\u003Cp>Name these the wrong things. So children. Yeah. I did, didn't I? Let's just sort that out.\u003C\u002Fp>\u003Cp>Right? The should have been a mini to 1. Okay. So that's the parents, and that's this comments collection. And then we have our children.\u003C\u002Fp>\u003Cp>Didn't necessarily have to add that one, but cool. Okay. Now now we should be looking good. Right? So I can add all the children.\u003C\u002Fp>\u003Cp>This is a child comment. Cool. Alright. So we're setting up a lot of this. This is gonna look nice on the front end.\u003C\u002Fp>\u003Cp>Before we get into the front end, I'm also gonna go in and add a new role to the back end here. So I'll just go in, we'll call this a user. They probably don't need app access. Right? We're going to build our own front end for this.\u003C\u002Fp>\u003Cp>And then we will give them access to see all the comments, see all the posts, see all the spaces. You know, do we want them to be able to create comments? Yes. We'll let them create posts. Can they create spaces?\u003C\u002Fp>\u003Cp>No. Can they edit comments? Yes. Potentially. Can they edit posts?\u003C\u002Fp>\u003Cp>Yes. They could edit posts. But let's do a custom permission where user created has to equal their own ID. So user created equals current user dot ID, and I'm not sure if this is actually user created dot ID. Let's try it that way just to be sure.\u003C\u002Fp>\u003Cp>Alright. So now they can only edit their own posts. We could do the same for comments. And, you know, do we want to let them delete their posts? Yeah.\u003C\u002Fp>\u003Cp>Probably do. But cool. Alright. So let's check the timer. We're looking good.\u003C\u002Fp>\u003Cp>And now let's start building something. Right? This is not much of an app to look at at this point, but maybe we drag this over and start building. Cool. Alright.\u003C\u002Fp>\u003Cp>So on the index page, let's just take a look and and dissect what we've got here. We've got like a nav on the left, then we've got the actual space here on the right. And I think that is it's pretty static. Right? So let's use our Nuxt layouts for this.\u003C\u002Fp>\u003Cp>We've got a default layout. We could see that in action here. Do we wanna use layouts, or do we wanna actually do a page for this? Let's do a page. So I could use, like, a Nuxt child route for this.\u003C\u002Fp>\u003Cp>What do you call this? What do they call their URLs? Right? We load this thing up. It's just slash home.\u003C\u002Fp>\u003Cp>Right? Slash c. Okay. Oh, looks like they've got slash c. Okay.\u003C\u002Fp>\u003Cp>That's part of all of these. Right? So we we could do something similar where we have, pages a dot view vcomp ts. Yeah. I'll I'll usually end up doing something like app dot view.\u003C\u002Fp>\u003Cp>Okay. So we're gonna have 2 sections here. We've got, like, a left sidebar, and then we have the content on the right hand side. So we can, like, flex this. And on the left sidebar, They make the is the width dynamic?\u003C\u002Fp>\u003Cp>No. You can't change the width on theirs. Probably a good thing. Easier that way. So let's just say with 56, we'll do p g gray 50.\u003C\u002Fp>\u003Cp>And then we have the, what, main content. Right? Sounds great. Div. And here, we're gonna put this Nuxt page component.\u003C\u002Fp>\u003Cp>Right? Now let's see what we got. Right? If we go to pages slash a, What did I do? Why is this not working?\u003C\u002Fp>\u003Cp>Pages slash a. Come on. Refresh. Next page. Next page.\u003C\u002Fp>\u003Cp>Oh. Slasha. Okay. Slash local host\u002Fa\u002Fapp. No.\u003C\u002Fp>\u003Cp>Slasha. Okay. Yeah. Cool. So we got nothing here.\u003C\u002Fp>\u003Cp>Right? Nothing here. There it is. There we go. Okay.\u003C\u002Fp>\u003Cp>So now, let's pick up our spaces. We're gonna render these over here. If I look at the Nuxt UI library, one of the things that I like here is they have this vertical navigation component. So we're just gonna use that. Right?\u003C\u002Fp>\u003Cp>Here's the structure for it. We can pull that in. Let's just drop it at the end of this. Copy these links just so we get our structure. And then we'll put vertical nav in the sidebar.\u003C\u002Fp>\u003Cp>But, obviously, you gotta delete these other ones. Okay. So we now we could see something going on. Maybe this needs to be height full. What is our default look here?\u003C\u002Fp>\u003Cp>High screen, maybe. Okay. Alright. So now we're getting that running the full gamut. We'll probably build in a little bit of padding here.\u003C\u002Fp>\u003Cp>P y 8. K. Maybe we're gonna add a logo at the top at some point. But now how do we actually get our data from Brectus into this particular instance, into this app. Alright.\u003C\u002Fp>\u003Cp>We wanna pick up these spaces dynamically. What we're gonna do in that case is call the Directus API. Alright. So we're gonna do we could use the use async data composable, especially if this is gonna be accessible on the front end, without authentication or anything. Maybe you want a server side render this if it is a space that has, you know, public data on it.\u003C\u002Fp>\u003Cp>Alright. So we're gonna use async data. We give this a key. Let's just call this, root. And I think this is the syntax.\u003C\u002Fp>\u003Cp>Alright. So I've got a composable setup in my Directus module. Where are you, mister Directus module? There we go. Use Directus, and here we will do this.\u003C\u002Fp>\u003Cp>We're going to call use Directus, and I'm gonna use the SDK that we have here. So I'm gonna read the items from our spaces collection. And, you know, I could add in, like, a some query params here or, some parameters to filter that out, but let's just take a look at our data. So I'm already seeing in the console, we've got a server 403 fetch error forbidden. Right?\u003C\u002Fp>\u003Cp>That is because we are not logged in. So how do we solve that? Right? Inside our Directus module that I've got set up, I've got a an auth composable that will log us in and fetch the user and all of that, and then store that as the user and inside use state. And I've even got in a middleware.\u003C\u002Fp>\u003Cp>Right? So we could add a middleware to this page, where if we're not logged in, it is going to send us to the authentication page. So, you know, if this was actually public data. Right? Maybe we needed to adjust our permission settings.\u003C\u002Fp>\u003Cp>Right? So in public, I could go in, maybe people can read all the comments. They could see the spaces. They'll probably still be able to read the see the spaces. Right?\u003C\u002Fp>\u003Cp>It will probably just be like the post that you would not be able to see unless they are authenticated. Alright. So here's one way we can set that up. Right? I can go into my item permissions and go to my space.\u003C\u002Fp>\u003Cp>And if this is private, if it's private, I'm going to restrict. So is private equals nothing. So if if private is null, they could see all those posts. But now if I refresh, I should remove that error. And then if I look at my app, We could drill in a couple layers here.\u003C\u002Fp>\u003Cp>Where's my page? Alright. So we can see the spaces that we're getting back here. Great. Okay.\u003C\u002Fp>\u003Cp>So we got our individual spaces. Now let's ditch these links. Alright. And just map these out. So our links are gonna be, let's say, the let's call these spaces.\u003C\u002Fp>\u003Cp>And we'll just do something like this where nav spaces equals computed. So we'll just grab this data. And we are going to map this out. Right? So we'll do spaces dot value dot map, and we got a space.\u003C\u002Fp>\u003Cp>And for each space, we are going to return what, label. So that's gonna be the space dot name. Do we have an icon for these? I we didn't have an icon, but we could potentially set one up. Or maybe we don't even have an icon.\u003C\u002Fp>\u003Cp>We'll just omit that. And then we have 2. Right? So 2 is gonna be slash, and let's use a template literal here. We'll do slash dot space spaces\u002Fspace.slug.\u003C\u002Fp>\u003Cp>Cool. Alright. And now if I do something like this, let's just log this out to make sure we're getting that. Spaces. Nav.\u003C\u002Fp>\u003Cp>Why am I not getting that? Next page. Where's our app? Slash a. Where are you, baby?\u003C\u002Fp>\u003Cp>What are you doing? There it is. There's our component. Now spaces cannot read property of undefined. If spaces if no spaces maybe we do this.\u003C\u002Fp>\u003Cp>Data equals unref our spaces. No. We'll just do this. If spaces, no spaces, we'll return empty array. So let's fix our issues.\u003C\u002Fp>\u003Cp>What do we get here? So spaces is still undefined. What am I doing wrong? Rich, doing something wrong up here in the actual call. Let's take a look at the use async data.\u003C\u002Fp>\u003Cp>Oh, duh, gosh. I need more coffee. So await async data. Why is this not still need do I need to return this? There we go.\u003C\u002Fp>\u003Cp>Fingers crossed. Fingers crossed. Refresh. Okay. So we got our spaces.\u003C\u002Fp>\u003Cp>We're still getting undefined on our computer here. Unrefspaces.map. Alright. Why is nav spaces undefined? Space dot name.\u003C\u002Fp>\u003Cp>So if we look at our spaces here, we have a name. We have a slug for those. Spaces is an array. We can confirm that. Do we have it now?\u003C\u002Fp>\u003Cp>Nope. Okay. That's always the fun of doing this. Oh, nav spaces. Undefined.\u003C\u002Fp>\u003Cp>What am I doing wrong here? Good question. Let's look at the clock. 23 minutes. Alright.\u003C\u002Fp>\u003Cp>So it looks like I just forgot to return this, basically. I think that solves the problem. Okay. Yeah. So now I can actually see that.\u003C\u002Fp>\u003Cp>Right? Kind of a bonehead move, but there we go. Sometimes it happens. So now, we've got our spaces over here and we could even, let's have a look at this component. I don't know if it will actually return.\u003C\u002Fp>\u003Cp>You can use the default slot or an avatar slot, customize the avatar, or give it an icon. Yeah. What if we just give it a, like, a circle icon? Icon is circle. Let's use the material symbols, is there material symbols here?\u003C\u002Fp>\u003Cp>Maybe circle. Sorry, icon. We'll just search all these. Right? They're just like a nice circle icon we could use.\u003C\u002Fp>\u003Cp>Yeah. It's fine. This is the material circle icon. Let's try that for all of these different items. Icon.\u003C\u002Fp>\u003Cp>Does that work? No. We need to change the syntax with Nuxt UI. Yeah. It's not showing up.\u003C\u002Fp>\u003Cp>Alright. So we'll just ditch the icon for now. Cool. Alright. Let's get on with building this thing.\u003C\u002Fp>\u003Cp>Right? We've got our main page, now we've got our spaces. If I click on the space, I don't have anything showing up for the space. So we'll go in and create a new folder and then we're going to do a spaces ID dot view. Right?\u003C\u002Fp>\u003Cp>The other thing that I'm going to do here is create a new folder called a and drop spaces in there as well. And then I need to go back here and adjust this to where it says a or app or whatever it is. We're just kind of following their convention here. Alright. So we've got spaces.\u003C\u002Fp>\u003Cp>Id. We'll just create a View component and spaces. Alright. So we click here. Are we on the space?\u003C\u002Fp>\u003Cp>Yep. And we could prove that just by doing something like this where we do cons route equals use route and route. Paramsiddiscussion. Right. And we could probably call this slug.\u003C\u002Fp>\u003Cp>It might be a better way just to keep everything clean, tight. And then we refresh, we can see slug is discussion. Okay. So how do we actually use this? Again, we're just gonna go in and fetch the space and we'll use async data, and we'll do spaces.\u003C\u002Fp>\u003Cp>And, actually, do we need the we're we're getting the space slug. Yeah. So we'll probably fetch that. Let's call it spaces dash. If you don't provide a, like, key for async data, it will use the the actual print.\u003C\u002Fp>\u003Cp>It will use the the spot that it's at in the file, like, the the file and the line number and things like that. But we'll just give it an actual key here to use when it caches things. Alright. So as far as our data, we're gonna return, we're gonna use Directus, we're gonna read the items from spaces and we are going to filter this down. Right?\u003C\u002Fp>\u003Cp>So the filter is at the slug underscore equals route dot params dot slug, you know, I could filter this out a little bit, Prayms route. I don't even know if this will work. Equal slug. Returned, use directus, slug. Unexpected keyword return.\u003C\u002Fp>\u003Cp>Do I not need the return? Oh, wait. Use async data. Oh, I forgot my function there. Okay.\u003C\u002Fp>\u003Cp>So we're gonna return, use directus, and, oh, I got a period on my slug. Cool. Are we actually getting data there? Let's take a look at that component. Right?\u003C\u002Fp>\u003Cp>This is the slug, so we'll just use our view dev tools. Do we have our space? Yes. We do. And then inside this use async data call, let's back this out.\u003C\u002Fp>\u003Cp>Right? Where are you? I can do is it transform? No. They have a a set of options.\u003C\u002Fp>\u003Cp>Transform, data, data 0. Let's see what that does. Now that should grab my single space. So the spaces are space because when I read items, I'm always returning an array. But using this transform on the async data call, I can transform that data and just pick up the first item from that array.\u003C\u002Fp>\u003Cp>So there's my space. I don't see any posts for discussion, but if we go to announcements alright. Do we see any posts there? Yeah. That's great.\u003C\u002Fp>\u003Cp>Okay. So, let's take a look at what we've got here for circle. We've got our discussion or our spaces over here, and then we have the discussion here. Alright. Cool.\u003C\u002Fp>\u003Cp>So we will add a header. It's got a cover image on it, basically. Let's try to find one where we did have some discussion. Right? Courses, engagement, Contest guidelines.\u003C\u002Fp>\u003Cp>Okay. Alright. So then we click into a post, and that is the assuming that's a space. Events new. I click into that.\u003C\u002Fp>\u003Cp>Events new. That's the slug for the post, I assume. Alright. So let's render out the header image for the space. Source is gonna be space dot cover underscore image.\u003C\u002Fp>\u003Cp>We'll make this full width. Maybe a height of 32. Object cover. See what we've got here. Just flip back.\u003C\u002Fp>\u003Cp>Input must be a string. Okay. So if we don't have the space dot cover image, space dot cover image, let's not try to render it. Is that the problem? Discussion.\u003C\u002Fp>\u003Cp>Okay. So I'm trying to get the image here, but what's happening? Right? Why are we not getting the image? It is because we are getting a 403 forbidden.\u003C\u002Fp>\u003Cp>We'll go into Directus here, go back to our access control. We have not allowed files to be shared. Okay? So there's our actual item. That's great.\u003C\u002Fp>\u003Cp>This is gonna be flex Flex 1. With full. Yeah. Okay. Alright.\u003C\u002Fp>\u003Cp>So we got our header. Let's just keep moving. Then we have our post detail. Right? I've got a v text component here that this will render, like, paragraph text.\u003C\u002Fp>\u003Cp>But, if we're gonna do something like the rich text for the post, So let's look at posts. We've got, like, this post content here. So we're great for a title. Alright. So let's just do, like, a h 2 or maybe an h one for the post.\u003C\u002Fp>\u003Cp>Not sure. Let's just do h two. Oh, we're not even showing the actual post. Right? Let's do an h one because we wanna show the space name.\u003C\u002Fp>\u003Cp>Alright. So there's the space name. It gives us a little padding. Pxpy6. Okay.\u003C\u002Fp>\u003Cp>Make this bold. Make it a little larger. 2 XL. Alright. So there we have that.\u003C\u002Fp>\u003Cp>Let's dive into the actual posts. Right? So 2 ways I could go about this. I could get all of the posts in a single call with Directus, or I could fetch these separately as well. In this case, let's just render some of these posts out.\u003C\u002Fp>\u003Cp>So we will go into our query here, and we'll do fields. Alright. So if I wanted to do, like, some cards so let's just back up and take a look at circle here. Right? We've got a couple of cards here that are showing up.\u003C\u002Fp>\u003Cp>Let's do the ucard, which is inside that Nuxt UI library. We're gonna do a post in space dot post. And Get a key. Key is gonna be post dot ID. And And then let's just render out post dot title.\u003C\u002Fp>\u003Cp>Oh, and that's not building because I did not close that off. Right. But now I'm not seeing anything. If I go to announcements, hey, I've got a card here. Nothing is rendering.\u003C\u002Fp>\u003Cp>And that's because if I take a look at my data, right, where are you, data. So inside this slug, I've got the space, we've got some posts here, but it's just returning an actual ID. How do I grab the actual post for this? So I go into fields. I could grab all the root level fields like this.\u003C\u002Fp>\u003Cp>And with the SDK, I can do the the TypeScript safe syntax is is actually specifying this out as an object. But for the sake of this, maybe I do, like, a double wildcard here. And now we could see, if I open this up, we can see the space, we get the post. It is fetching all of the content for that specific post. Right?\u003C\u002Fp>\u003Cp>Great. Looks like we've got a card. How do these cards look? Okay. So we got a header on the card.\u003C\u002Fp>\u003Cp>That's the the post image. Alright. So we got template. Be the card header. Then we just have a regular slot.\u003C\u002Fp>\u003Cp>P. Oh, where are you? Mister p tag. And here, this will be the header. We've got Nuxt Image.\u003C\u002Fp>\u003Cp>That'll be the v if post cover underscore image. The source would be post dot cover underscore image. Let me just close that up, see what we get. Okay. Now we're getting somewhere.\u003C\u002Fp>\u003Cp>Great. This is actually gonna be a NuxLink. Right? Do we wrap the entire card? How are they doing it here?\u003C\u002Fp>\u003Cp>Looks like the the entire image, and then I can click over here. Right? So we wrap the image in a Nuxt link, Nuxt link. For our spaces, we're gonna do this will be the post. We'll add, and then we'll do something like this where we have index.\u003C\u002Fp>\u003Cp>I think this will give us the routing that we want. Alright. What else was I missing? The actual Nuxt link. Right?\u003C\u002Fp>\u003Cp>2 a slash, we got what the space well, actually, this is gonna be the slug. Alright. Well, we've got the slug up there. And then we have post dot slug. Post dot slug.\u003C\u002Fp>\u003Cp>Yeah. Cool. And there's our Nuxt link. I will just copy this here. Run that one more time.\u003C\u002Fp>\u003Cp>Run it back. And does that give us where we want to go? Except it doesn't, does it? Okay. Okay.\u003C\u002Fp>\u003Cp>Yeah. No. Now we're navigating where we wanna go. Why do we not have a slug? Do we not have a slug on the post?\u003C\u002Fp>\u003Cp>We don't. That's why it's not showing up. Alright. So let's quickly add a slug to these posts. And, you know, we could even create a, an automation inside Directus that would do this for us.\u003C\u002Fp>\u003Cp>And I'll just quickly add a test log. Okay. So we go there. Test log is not showing up. Page not found.\u003C\u002Fp>\u003Cp>Why is that? Because we're not doing the actual right. So we got spaces slash spaces. K. Now we go back.\u003C\u002Fp>\u003Cp>We try it again. Right? Go back. Refresh, maybe. K.\u003C\u002Fp>\u003Cp>A spaces slash announcement post. Oh, the Alright. This needs to be an actual we need a folder for that. Right? What's the best way to set this up?\u003C\u002Fp>\u003Cp>Okay. So spaces actually becomes this. Right? This is the space, and then this is the index route for that space. This becomes anywhere that we're referencing slug becomes the actual space.\u003C\u002Fp>\u003Cp>Brand space space. Oh, let's not destructure that space. Slug. Does that work? Spaces, slug, Space.\u003C\u002Fp>\u003Cp>Space. Space. Space. What are we what are we missing? What are we missing?\u003C\u002Fp>\u003Cp>Let's just go back. Alright. Slug. Okay. So now I've got the space.\u003C\u002Fp>\u003Cp>Right? Let's just log the params out here. Params. Why can't I like any ears? Params, slug, spaces announcements.\u003C\u002Fp>\u003Cp>Oh, it has to do with the URL structure now. Right? We've changed that. So we go back to our tag here. We don't really need this.\u003C\u002Fp>\u003Cp>Okay. So at least now we're getting an error. And what else do we have here? Index dot view slug space slug. Give it to me.\u003C\u002Fp>\u003Cp>Input string space dot cover image. Oh. What am I doing wrong here? So there's our announcements. Okay.\u003C\u002Fp>\u003Cp>It's because we don't have any. This would be v if space dot post dot length greater than 0. Solve the the issues. It doesn't like that. Vcard.\u003C\u002Fp>\u003Cp>Vfpost.coverimage.source.post.coverimagespace. Why does it unlike the unlike the discussion page? No worries, though. So we change the routing there. Alright.\u003C\u002Fp>\u003Cp>So now we are now we're getting somewhere. Right? We've got the routing the way that they do it. And then inside this post, we will actually fetch the individual post as well. Alright.\u003C\u002Fp>\u003Cp>So we'll come here. Let's just call this post so we can render that out. The data will be post. We are going to fetch the post dot dash slug. And instead of spaces, we're gonna do post.\u003C\u002Fp>\u003Cp>And in this case, this one is actually called post. Alright. What else do we need? It's gonna grab our comments as well. Alright.\u003C\u002Fp>\u003Cp>Boom. Okay. So now we can see our post. Looking great. If we go back, we'll grab the header image.\u003C\u002Fp>\u003Cp>K. Post dot cover image. Do we have a cover image for that post? Yes. We do.\u003C\u002Fp>\u003Cp>Why are we not sorting through that post dot cover image? Input must be a string. Oh, because we're fetching all of the data. Let's just delete that. That's probably why.\u003C\u002Fp>\u003Cp>Okay. So there's our cover image. Let's do a div. We're gonna do VHtml. This is gonna render out our actual content.\u003C\u002Fp>\u003Cp>So post dot contents. And Telwin has some really nice typography styles already preset for you, and we are going to just use those. Right? Pros. Missing our intag.\u003C\u002Fp>\u003Cp>Alright. Let's wrap this again. Div. We've got p x 4. Give us some padding.\u003C\u002Fp>\u003Cp>K. There's our post. Let's get into the comments. Right? How do we get down to the comments?\u003C\u002Fp>\u003Cp>We'll do just give this a little breathing room. P x a p y 8. 6 is not nearly enough padding. I definitely need to go in and, update the the actual mobile views for this as well. And then we have our comments section.\u003C\u002Fp>\u003Cp>How we doing on time? 55 seconds. Man, this one has been a little bit of a struggle. A little more to it than what I thought originally. Also operating on not enough coffee.\u003C\u002Fp>\u003Cp>So it's it's pretty painfully obvious at this point. Not going to get to any of the comments. So if we look at our functionality, right, we managed to get here. Users can log in. Did we even get there?\u003C\u002Fp>\u003Cp>We actually did, because it was already baked in, but we didn't we didn't really mess with that. Alright? There's a auth login page where I could actually log in to this thing. Users create posts and add comments to those posts, create and manage events. Man, like, we did not do so great on this one.\u003C\u002Fp>\u003Cp>So, sometimes you fail. Circle is a really cool app. You know, recreating that functionality, there's a lot baked into it. So, you know, hey. What do we give this one?\u003C\u002Fp>\u003Cp>Like, Cisco and Evert would probably give this one 2 thumbs down, but that's the way it rolls. You know, we've learned a lot about the different data models and how to use the different relationships inside Directus. So let's chalk it up as a a tie, maybe. I don't know. That's it for this episode of 100 apps, 100 hours.\u003C\u002Fp>\u003Cp>Thanks for joining me. We'll see you on the next one.\u003C\u002Fp>","Hi guys. Welcome back to the next episode of 100 Apps, 100 Hours. I'm your host, Brian Gillespie, developer advocate at Directus. And today, we will be building a community platform. Now, this is one that is near and dear to my heart because I have built a community for sign and print shop owners myself. We use Facebook Groups for that. Not the best community platform, but it is free and everybody already has a Facebook account. But in just a quick look at the research for this episode, There's quite a few different tools out there that I located, Circle being one of the more popular ones. It's not exactly a forum, it's not exactly a course, it's not exactly chat, it's all of those things. So the pieces that we're really going to dive into today are this concept that they have of spaces. So each one of these items in the left navigation is a space. A space can be, either private or public. So I can see here some of the circle experts and this is their their own example community. Right? They do have courses in here. We've already done an episode on LMS, so we won't really touch on that. But, if we get into this, we could see what's new in the product. So this is an update we're gonna post. We've got some comments down at the bottom, I'm assuming, and you had to be logged in to comment. So how do we kinda recreate some of this functionality? It looks like they have weekly events here, but we're probably not seeing those. And maybe here's something similar. So anyway, that's what we're building. If you, have watched any of the other episodes, you probably already know the rules. We've got 60 minutes to plan and build, no more, no less. And the second rule, which is not really a rule, is use whatever you have at your disposal. So, any of the tools that are out there, AI, Tailwind CSS is one of my favorite ones. We're going to be building this platform on Directus as well. That'll be our back end. You could probably even actually build the entire thing inside Directus, but we'll probably go for a custom UI on the front end. Alright. So, sounds great. Let's rock and roll. So we'll start the 60 minute timer and away we go. So anytime I build an app, I always love to just kinda flesh out the functionality ahead of time. So if we're looking at this specific app, maybe I'll just pull up a text box inside Figma here. Here's the functionality that we really want out of this app. Right? We want to create and manage different spaces. Spaces for a how do I can I constrain this? Yeah. There we go. For a community. Maybe we have events as well that we're going to list. List and create and manage events. Events. We need to be able to log in. Users can log in. Create posts. Right? Is that what we're going to call that? Maybe a thread? We'll get into it. Right? We'll create some posts and add comments to those posts. That feels like a a pretty good chunk of functionality. I don't know if we'll get to all that or not. That's, like, actually building the UI for that, could be fun. Right? Alright. So let's just, like, flesh out our our actual data model here as well. So again, I'm not the savviest Figma user, but I try. I'm a tried. Alright. So what do we have? We've got spaces. So those are gonna be our little homes for all this different stuff, this different type of content. Right? Then we've probably got, this is where we get into the naming. Right? Is it a a thread? Is this a what what are these things called? Are these posts? Yeah. Let's go with that. Right? This is a post. And a post has some comments. Probably has a probably has, like, a post type, I'm assuming. K. Let's look at what else we've got here. Share your wins. Log in. Yeah. Okay. So these are different posts. You can upload images to a post. You can add comments. You can like. So we probably got some likes in there. And I don't know that we'll get to that either. But, what else do we have? We have users. That's gonna be taken care of by Directus because it gives us that out of the box, which is nice. That'll be our Directus users collection. And what else do we need for a function community? Right. Are events gonna be a separate thing than posts? I don't know. Let's we'll try it out. Cool. Let's just add that to the end here in case we we don't have time to get to it. Alright. So this feels pretty good. We're closing in on 4 minutes or so in the planning stage. Alright. So what do we have set up? We have, we got Directus going here. I've got a Directus instance that is totally blank, so I just reload. You'll see there are 0 collections. And on the front end, I've got a starter Nuxt application that just basically has the Directus module or the Directus plug in already preconfigured. We're using the SDK to communicate with the Directus instance, but I've just got Directus spun up on a local Docker instance. So, really fancy app here. We've got Tailwind. We're using Nuxt UI as well on that front end. So got quite a few things going on there. Alright. So first and foremost, we've got spaces. Let's go ahead and create our data model. Directus makes this super easy. We'll just go into our blank instance. We'll create our first collection. We're going to call it spaces. So those are the different spaces here. We'll generate the UID. We'll probably have a a status for the space. What's the sort order for the spaces? Who created these? When were they created? That's all pretty standard. Let's just take a look at the network requests on this to see if we can actually pick up some of their data model. Alright. Space groups. Alright. There's the circle community. Allow members to spaces. What else do we have? There's a slug for the space. So that's, that's a handy piece of information to have. Spaces, discussion. Okay. So we've got a cover image. Cover image URL. And this is via their API, so it doesn't necessarily represent the underlying database structure, but this data is coming from somewhere. So we're gonna have a name for the space. Name for the space. That's great. Alright. Do they have a description? Space description. Space name. We're definitely gonna have a slug, so that's great. We'll roll with that. Advanced field creation mode and, when I need something like a slug or a value that URL safe, if I go into advanced mode, I have Slugify available. So that will automatically transform the input as I type. Lock screen heading. I don't know, man. Let's just give it a description. And let's keep that non WYSIWYG. And last but not least, for our space, spaces, Let's add a cover image. So we've got our cover image. Right? And let's see if they had do they have a type of space? Space, slug. Visibility, post type is basic. I I wanna say there's probably some setting in here to control what these things look like, whether this is like a discussion or this is an event or a course. As we're fetching these, okay, that's a course. Internal spaces API. Maybe a type. Is there a type? Course type. Type is a banner. K. Those are yeah. No. I I don't wanna waste time on it. Right? Cool. So status, we'll just leave blank for now. Is this published or is it available? And then let's say let's add 1, like, is private. This will be a toggle. So the Boolean values Boolean value, I always used to struggle with that. That's what we'll save in the database. Is this space private or not? And then we can do a little cleanup here on our fields. So name, slug, description, maybe we pop that right beside there. And status wise, let's just roll with it. Right? So we go in, we create our first space. Maybe we wanna call this discussion, and that'll be at the slug for that will be discussion. Here's where we discuss stuff. Great. Is it private? Yes. Let's make it private. And then we will let's upload a cover image. Right? Unsplash. Let's just search for discussion. Okay. Images that are not seem to be working. Okay. Maybe some type of Internet issue. We're working here locally, anyway. Let's just grab from Circle. Right? We're already working with this specific thing. Do they have discussion? Where's the best practices for engagement? Yeah. Fine. Let's roll with it. Cool. Alright. So now we've got our first space. What else do we need to add to this? Right? So we've got posts for the spaces. Then each one of those posts could have comments, potentially. Right? So, let's just go in and create another. We'll just call it announcements as the next space. Announcements, read only channel for important announcements. Very fancy. Important announcements. Alright. So that is not private. Let's make both of these published just to to have it. Great. Okay, so we'll go back into our settings and let's add the next item. Let's add posts. We use the UUID. Do we have a status for the post? Yeah. Probably. Date created, date updated, is there a sort for the post? Do we wanna control the sort order on those? Yeah. Maybe potentially you do. Alright. So if we take a look at circle here. Let's dissect one of these posts. Right? So a post has a title, looks like. Also has a user that created the post. So Directus has taken care of that for us out of the box. We probably got a date that this was created. We've got some rich text for the post. So that'll be the post content, maybe? Post body? You do that. Let's just keep it as content. It seems pretty straightforward. Naming stuff is always the the hardest part about development. What else do we have? We got that cover image for the post. Right? Cover image for our post so that, you know, if we want to upload a cover image, we can. Sweet. Looks great. Let's go in and just create one of those posts. Right? So now we've got our post. Let's add a published post. And, Kate, if you're watching this, I'm sorry. I'm just gonna steal this post entirely. Use it verbatim inside this platform. And I'm also gonna steal your thumbnail image. Don't sue me, please. Alright. So we've got our post. Great. Looks savvy. Okay. Now we need to tie these together. Right? We don't have it's just floating out in space. It's not inside a space, it's in outer space. Alright, so let's tie that together. We're going to use the relationship indirectus for that. We'll just go to our spaces and one space could have many posts. But I think a post always belongs to a single space. We wouldn't want posts in multiple spaces. So that is a good example of our one to many relationship. There's one space, many posts, and these are gonna be called posts. The foreign key inside our post table, we'll just call that space without the s, because it's just gonna be 1. Alright. So we'll save this. And I can go into advanced mode inside Directus just to to make sure everything that I want is getting created. And maybe we I don't know if we actually need that sort field or not, but it's fine. Let's do a post title. I probably could have went with the post name, because we had a space name and not a title. But, again, we're splitting hairs here. There's no right or wrong when when you're naming things. There's only shades of gray. Some things are bad, some things are not. Alright. So we've got our spaces, we've got our posts, we've got a relationship between them. Right? So if I go into my spaces now and I click add existing, I can add that existing post to our announcements space. I could save that. And then if I were to go to our discussion, I could potentially pull that in here, but because of that relationship, it would decouple those. Alright. So next we've got, what, comments? Posts could have multiple comments. And let's do date created. We really don't have a status on these, do we? Like, you know, if the post if the comment is edited, we'll we'll be able to tell by the user updated field or the date updated field. So there we go. We've got the comments content, content body, comment message. Yeah. Let's roll with message. That's fine. So now we've got our comments. Let's send that relationship to the post. So we are, again, going to do a another one to many relationship. Right? Because we have a post. One post has many con comments. Okay. So oh, actually, we're on comments here. Right? So we need the inverse relationship of that. We've got the many to 1, because we were on the comments. So the key here will be post. Our related collection is posts. I'm gonna open up advanced because I am going to add that corresponding field inside the posts collection. That's gonna be comments. Great. We can add our title there. Cool. Okay. So we've got comment, we've got a post. Now if we were to go, actually, let's just hop over to our post, make sure this is showing. Alright. Maybe we wanna show the space that this is attached to as well. Go into my post. Here's the post. There's the title. Here's the space. I can go in and add a comment. Really simple. New comment. Bada bing bada boom. Got a comment for that space. I can see it here, though we can't actually read it. It's just showing the UUID. So I can go back in and maybe we clean this up a little bit where we got our spaces, got our posts. And here in the display template for this, I could show the actual message. And if I wanted to, I could even get into the the weeds a bit here and show the user avatar. Is it gonna be the avatar or maybe the actual avatar thumbnail? Yeah. There we go. Looking nice. Cool. Let's see what that looks like. Inside our post, new comment. We don't have an avatar for for little old me, so let's solve that really quickly as well. Just do right. Right ahead. Where are you? Me. There we go. Cool. We'll go ahead. Alright. So I could see myself that I made a comment. We can see the space it's attached to. What what are we missing from our functionality? Right? Comments, likes, and we could do that. Post, comments, likes. I you know, do we even really wanna do likes on this? Yeah. I think this is fine. Right? Let's let's roll with this for now and get something happening on our front end. Let's build something cool. I'm sure I'm missing something. But oh, what if we had, like, threaded comets? Right? I don't know if the circle support that type of maneuver. Looks like it does. Right? So, I can also have recursive relationships. So this will be a good look at what's available inside Directus as well. So I could go in and create recursive relationships. Boom. Boom. Boom. Boom. Boom. And, you know, we we could take care of this on the front end. Or, you know, if I wanted to show that recursion, we could do it here. So we'll add a parent key, and then we'll just reference that same collection. And let's do, like, children. Sounds cool. Great. K. And as far as the interface, I could also do, like, the tree view, which would show, like, nested recursive items, as it says. But, maybe we don't drill down any more than one layer here. Cool. Alright. So now I can have a comment. I can have, a parent underneath that. Parent comment. Oh, did I get this backwards? Yeah. Maybe I goofed that up a little bit. Name these the wrong things. So children. Yeah. I did, didn't I? Let's just sort that out. Right? The should have been a mini to 1. Okay. So that's the parents, and that's this comments collection. And then we have our children. Didn't necessarily have to add that one, but cool. Okay. Now now we should be looking good. Right? So I can add all the children. This is a child comment. Cool. Alright. So we're setting up a lot of this. This is gonna look nice on the front end. Before we get into the front end, I'm also gonna go in and add a new role to the back end here. So I'll just go in, we'll call this a user. They probably don't need app access. Right? We're going to build our own front end for this. And then we will give them access to see all the comments, see all the posts, see all the spaces. You know, do we want them to be able to create comments? Yes. We'll let them create posts. Can they create spaces? No. Can they edit comments? Yes. Potentially. Can they edit posts? Yes. They could edit posts. But let's do a custom permission where user created has to equal their own ID. So user created equals current user dot ID, and I'm not sure if this is actually user created dot ID. Let's try it that way just to be sure. Alright. So now they can only edit their own posts. We could do the same for comments. And, you know, do we want to let them delete their posts? Yeah. Probably do. But cool. Alright. So let's check the timer. We're looking good. And now let's start building something. Right? This is not much of an app to look at at this point, but maybe we drag this over and start building. Cool. Alright. So on the index page, let's just take a look and and dissect what we've got here. We've got like a nav on the left, then we've got the actual space here on the right. And I think that is it's pretty static. Right? So let's use our Nuxt layouts for this. We've got a default layout. We could see that in action here. Do we wanna use layouts, or do we wanna actually do a page for this? Let's do a page. So I could use, like, a Nuxt child route for this. What do you call this? What do they call their URLs? Right? We load this thing up. It's just slash home. Right? Slash c. Okay. Oh, looks like they've got slash c. Okay. That's part of all of these. Right? So we we could do something similar where we have, pages a dot view vcomp ts. Yeah. I'll I'll usually end up doing something like app dot view. Okay. So we're gonna have 2 sections here. We've got, like, a left sidebar, and then we have the content on the right hand side. So we can, like, flex this. And on the left sidebar, They make the is the width dynamic? No. You can't change the width on theirs. Probably a good thing. Easier that way. So let's just say with 56, we'll do p g gray 50. And then we have the, what, main content. Right? Sounds great. Div. And here, we're gonna put this Nuxt page component. Right? Now let's see what we got. Right? If we go to pages slash a, What did I do? Why is this not working? Pages slash a. Come on. Refresh. Next page. Next page. Oh. Slasha. Okay. Slash local host\u002Fa\u002Fapp. No. Slasha. Okay. Yeah. Cool. So we got nothing here. Right? Nothing here. There it is. There we go. Okay. So now, let's pick up our spaces. We're gonna render these over here. If I look at the Nuxt UI library, one of the things that I like here is they have this vertical navigation component. So we're just gonna use that. Right? Here's the structure for it. We can pull that in. Let's just drop it at the end of this. Copy these links just so we get our structure. And then we'll put vertical nav in the sidebar. But, obviously, you gotta delete these other ones. Okay. So we now we could see something going on. Maybe this needs to be height full. What is our default look here? High screen, maybe. Okay. Alright. So now we're getting that running the full gamut. We'll probably build in a little bit of padding here. P y 8. K. Maybe we're gonna add a logo at the top at some point. But now how do we actually get our data from Brectus into this particular instance, into this app. Alright. We wanna pick up these spaces dynamically. What we're gonna do in that case is call the Directus API. Alright. So we're gonna do we could use the use async data composable, especially if this is gonna be accessible on the front end, without authentication or anything. Maybe you want a server side render this if it is a space that has, you know, public data on it. Alright. So we're gonna use async data. We give this a key. Let's just call this, root. And I think this is the syntax. Alright. So I've got a composable setup in my Directus module. Where are you, mister Directus module? There we go. Use Directus, and here we will do this. We're going to call use Directus, and I'm gonna use the SDK that we have here. So I'm gonna read the items from our spaces collection. And, you know, I could add in, like, a some query params here or, some parameters to filter that out, but let's just take a look at our data. So I'm already seeing in the console, we've got a server 403 fetch error forbidden. Right? That is because we are not logged in. So how do we solve that? Right? Inside our Directus module that I've got set up, I've got a an auth composable that will log us in and fetch the user and all of that, and then store that as the user and inside use state. And I've even got in a middleware. Right? So we could add a middleware to this page, where if we're not logged in, it is going to send us to the authentication page. So, you know, if this was actually public data. Right? Maybe we needed to adjust our permission settings. Right? So in public, I could go in, maybe people can read all the comments. They could see the spaces. They'll probably still be able to read the see the spaces. Right? It will probably just be like the post that you would not be able to see unless they are authenticated. Alright. So here's one way we can set that up. Right? I can go into my item permissions and go to my space. And if this is private, if it's private, I'm going to restrict. So is private equals nothing. So if if private is null, they could see all those posts. But now if I refresh, I should remove that error. And then if I look at my app, We could drill in a couple layers here. Where's my page? Alright. So we can see the spaces that we're getting back here. Great. Okay. So we got our individual spaces. Now let's ditch these links. Alright. And just map these out. So our links are gonna be, let's say, the let's call these spaces. And we'll just do something like this where nav spaces equals computed. So we'll just grab this data. And we are going to map this out. Right? So we'll do spaces dot value dot map, and we got a space. And for each space, we are going to return what, label. So that's gonna be the space dot name. Do we have an icon for these? I we didn't have an icon, but we could potentially set one up. Or maybe we don't even have an icon. We'll just omit that. And then we have 2. Right? So 2 is gonna be slash, and let's use a template literal here. We'll do slash dot space spaces\u002Fspace.slug. Cool. Alright. And now if I do something like this, let's just log this out to make sure we're getting that. Spaces. Nav. Why am I not getting that? Next page. Where's our app? Slash a. Where are you, baby? What are you doing? There it is. There's our component. Now spaces cannot read property of undefined. If spaces if no spaces maybe we do this. Data equals unref our spaces. No. We'll just do this. If spaces, no spaces, we'll return empty array. So let's fix our issues. What do we get here? So spaces is still undefined. What am I doing wrong? Rich, doing something wrong up here in the actual call. Let's take a look at the use async data. Oh, duh, gosh. I need more coffee. So await async data. Why is this not still need do I need to return this? There we go. Fingers crossed. Fingers crossed. Refresh. Okay. So we got our spaces. We're still getting undefined on our computer here. Unrefspaces.map. Alright. Why is nav spaces undefined? Space dot name. So if we look at our spaces here, we have a name. We have a slug for those. Spaces is an array. We can confirm that. Do we have it now? Nope. Okay. That's always the fun of doing this. Oh, nav spaces. Undefined. What am I doing wrong here? Good question. Let's look at the clock. 23 minutes. Alright. So it looks like I just forgot to return this, basically. I think that solves the problem. Okay. Yeah. So now I can actually see that. Right? Kind of a bonehead move, but there we go. Sometimes it happens. So now, we've got our spaces over here and we could even, let's have a look at this component. I don't know if it will actually return. You can use the default slot or an avatar slot, customize the avatar, or give it an icon. Yeah. What if we just give it a, like, a circle icon? Icon is circle. Let's use the material symbols, is there material symbols here? Maybe circle. Sorry, icon. We'll just search all these. Right? They're just like a nice circle icon we could use. Yeah. It's fine. This is the material circle icon. Let's try that for all of these different items. Icon. Does that work? No. We need to change the syntax with Nuxt UI. Yeah. It's not showing up. Alright. So we'll just ditch the icon for now. Cool. Alright. Let's get on with building this thing. Right? We've got our main page, now we've got our spaces. If I click on the space, I don't have anything showing up for the space. So we'll go in and create a new folder and then we're going to do a spaces ID dot view. Right? The other thing that I'm going to do here is create a new folder called a and drop spaces in there as well. And then I need to go back here and adjust this to where it says a or app or whatever it is. We're just kind of following their convention here. Alright. So we've got spaces. Id. We'll just create a View component and spaces. Alright. So we click here. Are we on the space? Yep. And we could prove that just by doing something like this where we do cons route equals use route and route. Paramsiddiscussion. Right. And we could probably call this slug. It might be a better way just to keep everything clean, tight. And then we refresh, we can see slug is discussion. Okay. So how do we actually use this? Again, we're just gonna go in and fetch the space and we'll use async data, and we'll do spaces. And, actually, do we need the we're we're getting the space slug. Yeah. So we'll probably fetch that. Let's call it spaces dash. If you don't provide a, like, key for async data, it will use the the actual print. It will use the the spot that it's at in the file, like, the the file and the line number and things like that. But we'll just give it an actual key here to use when it caches things. Alright. So as far as our data, we're gonna return, we're gonna use Directus, we're gonna read the items from spaces and we are going to filter this down. Right? So the filter is at the slug underscore equals route dot params dot slug, you know, I could filter this out a little bit, Prayms route. I don't even know if this will work. Equal slug. Returned, use directus, slug. Unexpected keyword return. Do I not need the return? Oh, wait. Use async data. Oh, I forgot my function there. Okay. So we're gonna return, use directus, and, oh, I got a period on my slug. Cool. Are we actually getting data there? Let's take a look at that component. Right? This is the slug, so we'll just use our view dev tools. Do we have our space? Yes. We do. And then inside this use async data call, let's back this out. Right? Where are you? I can do is it transform? No. They have a a set of options. Transform, data, data 0. Let's see what that does. Now that should grab my single space. So the spaces are space because when I read items, I'm always returning an array. But using this transform on the async data call, I can transform that data and just pick up the first item from that array. So there's my space. I don't see any posts for discussion, but if we go to announcements alright. Do we see any posts there? Yeah. That's great. Okay. So, let's take a look at what we've got here for circle. We've got our discussion or our spaces over here, and then we have the discussion here. Alright. Cool. So we will add a header. It's got a cover image on it, basically. Let's try to find one where we did have some discussion. Right? Courses, engagement, Contest guidelines. Okay. Alright. So then we click into a post, and that is the assuming that's a space. Events new. I click into that. Events new. That's the slug for the post, I assume. Alright. So let's render out the header image for the space. Source is gonna be space dot cover underscore image. We'll make this full width. Maybe a height of 32. Object cover. See what we've got here. Just flip back. Input must be a string. Okay. So if we don't have the space dot cover image, space dot cover image, let's not try to render it. Is that the problem? Discussion. Okay. So I'm trying to get the image here, but what's happening? Right? Why are we not getting the image? It is because we are getting a 403 forbidden. We'll go into Directus here, go back to our access control. We have not allowed files to be shared. Okay? So there's our actual item. That's great. This is gonna be flex Flex 1. With full. Yeah. Okay. Alright. So we got our header. Let's just keep moving. Then we have our post detail. Right? I've got a v text component here that this will render, like, paragraph text. But, if we're gonna do something like the rich text for the post, So let's look at posts. We've got, like, this post content here. So we're great for a title. Alright. So let's just do, like, a h 2 or maybe an h one for the post. Not sure. Let's just do h two. Oh, we're not even showing the actual post. Right? Let's do an h one because we wanna show the space name. Alright. So there's the space name. It gives us a little padding. Pxpy6. Okay. Make this bold. Make it a little larger. 2 XL. Alright. So there we have that. Let's dive into the actual posts. Right? So 2 ways I could go about this. I could get all of the posts in a single call with Directus, or I could fetch these separately as well. In this case, let's just render some of these posts out. So we will go into our query here, and we'll do fields. Alright. So if I wanted to do, like, some cards so let's just back up and take a look at circle here. Right? We've got a couple of cards here that are showing up. Let's do the ucard, which is inside that Nuxt UI library. We're gonna do a post in space dot post. And Get a key. Key is gonna be post dot ID. And And then let's just render out post dot title. Oh, and that's not building because I did not close that off. Right. But now I'm not seeing anything. If I go to announcements, hey, I've got a card here. Nothing is rendering. And that's because if I take a look at my data, right, where are you, data. So inside this slug, I've got the space, we've got some posts here, but it's just returning an actual ID. How do I grab the actual post for this? So I go into fields. I could grab all the root level fields like this. And with the SDK, I can do the the TypeScript safe syntax is is actually specifying this out as an object. But for the sake of this, maybe I do, like, a double wildcard here. And now we could see, if I open this up, we can see the space, we get the post. It is fetching all of the content for that specific post. Right? Great. Looks like we've got a card. How do these cards look? Okay. So we got a header on the card. That's the the post image. Alright. So we got template. Be the card header. Then we just have a regular slot. P. Oh, where are you? Mister p tag. And here, this will be the header. We've got Nuxt Image. That'll be the v if post cover underscore image. The source would be post dot cover underscore image. Let me just close that up, see what we get. Okay. Now we're getting somewhere. Great. This is actually gonna be a NuxLink. Right? Do we wrap the entire card? How are they doing it here? Looks like the the entire image, and then I can click over here. Right? So we wrap the image in a Nuxt link, Nuxt link. For our spaces, we're gonna do this will be the post. We'll add, and then we'll do something like this where we have index. I think this will give us the routing that we want. Alright. What else was I missing? The actual Nuxt link. Right? 2 a slash, we got what the space well, actually, this is gonna be the slug. Alright. Well, we've got the slug up there. And then we have post dot slug. Post dot slug. Yeah. Cool. And there's our Nuxt link. I will just copy this here. Run that one more time. Run it back. And does that give us where we want to go? Except it doesn't, does it? Okay. Okay. Yeah. No. Now we're navigating where we wanna go. Why do we not have a slug? Do we not have a slug on the post? We don't. That's why it's not showing up. Alright. So let's quickly add a slug to these posts. And, you know, we could even create a, an automation inside Directus that would do this for us. And I'll just quickly add a test log. Okay. So we go there. Test log is not showing up. Page not found. Why is that? Because we're not doing the actual right. So we got spaces slash spaces. K. Now we go back. We try it again. Right? Go back. Refresh, maybe. K. A spaces slash announcement post. Oh, the Alright. This needs to be an actual we need a folder for that. Right? What's the best way to set this up? Okay. So spaces actually becomes this. Right? This is the space, and then this is the index route for that space. This becomes anywhere that we're referencing slug becomes the actual space. Brand space space. Oh, let's not destructure that space. Slug. Does that work? Spaces, slug, Space. Space. Space. Space. What are we what are we missing? What are we missing? Let's just go back. Alright. Slug. Okay. So now I've got the space. Right? Let's just log the params out here. Params. Why can't I like any ears? Params, slug, spaces announcements. Oh, it has to do with the URL structure now. Right? We've changed that. So we go back to our tag here. We don't really need this. Okay. So at least now we're getting an error. And what else do we have here? Index dot view slug space slug. Give it to me. Input string space dot cover image. Oh. What am I doing wrong here? So there's our announcements. Okay. It's because we don't have any. This would be v if space dot post dot length greater than 0. Solve the the issues. It doesn't like that. Vcard. Vfpost.coverimage.source.post.coverimagespace. Why does it unlike the unlike the discussion page? No worries, though. So we change the routing there. Alright. So now we are now we're getting somewhere. Right? We've got the routing the way that they do it. And then inside this post, we will actually fetch the individual post as well. Alright. So we'll come here. Let's just call this post so we can render that out. The data will be post. We are going to fetch the post dot dash slug. And instead of spaces, we're gonna do post. And in this case, this one is actually called post. Alright. What else do we need? It's gonna grab our comments as well. Alright. Boom. Okay. So now we can see our post. Looking great. If we go back, we'll grab the header image. K. Post dot cover image. Do we have a cover image for that post? Yes. We do. Why are we not sorting through that post dot cover image? Input must be a string. Oh, because we're fetching all of the data. Let's just delete that. That's probably why. Okay. So there's our cover image. Let's do a div. We're gonna do VHtml. This is gonna render out our actual content. So post dot contents. And Telwin has some really nice typography styles already preset for you, and we are going to just use those. Right? Pros. Missing our intag. Alright. Let's wrap this again. Div. We've got p x 4. Give us some padding. K. There's our post. Let's get into the comments. Right? How do we get down to the comments? We'll do just give this a little breathing room. P x a p y 8. 6 is not nearly enough padding. I definitely need to go in and, update the the actual mobile views for this as well. And then we have our comments section. How we doing on time? 55 seconds. Man, this one has been a little bit of a struggle. A little more to it than what I thought originally. Also operating on not enough coffee. So it's it's pretty painfully obvious at this point. Not going to get to any of the comments. So if we look at our functionality, right, we managed to get here. Users can log in. Did we even get there? We actually did, because it was already baked in, but we didn't we didn't really mess with that. Alright? There's a auth login page where I could actually log in to this thing. Users create posts and add comments to those posts, create and manage events. Man, like, we did not do so great on this one. So, sometimes you fail. Circle is a really cool app. You know, recreating that functionality, there's a lot baked into it. So, you know, hey. What do we give this one? Like, Cisco and Evert would probably give this one 2 thumbs down, but that's the way it rolls. You know, we've learned a lot about the different data models and how to use the different relationships inside Directus. So let's chalk it up as a a tie, maybe. I don't know. That's it for this episode of 100 apps, 100 hours. Thanks for joining me. We'll see you on the next one.",[5122],"5d949bd4-c25c-47e7-a9f5-993c8d5c00a1",[],{"year":2380,"number":177,"id":4162,"episodes":5125,"show":5126},[4164,4165,4166,4167,4168,4169,4170,4171,4172,4173,4137],{"title":322,"slug":323},{"id":4610,"slug":5128,"vimeo_id":5129,"description":5130,"tile":5131,"length":740,"resources":12,"people":5132,"episode_number":159,"published":5134,"title":5135,"video_transcript_html":5136,"video_transcript_text":5137,"content":12,"seo":12,"status":19,"episode_people":5138,"recommendations":5140,"season":5141},"openai","899796860","Use OpenAI's APIs to generate images with DALL·E and social posts with GPT-4.","b3d3b5a4-2946-4c72-bf88-e8bc933bd7c0",[5133],{"name":2820,"url":2821},"2024-01-19","Generate Images and Social Posts with OpenAI","\u003Cp>Speaker 0: Chances are you've heard of OpenAI. They're the company behind ChatGPT. Now today in QuickConnect, we're going to integrate with OpenAI's APIs to use both their GPT 4 model and their DALL E image generation model to enrich data in our director's project. Here, I've got a set of articles, a set of recipes, and we'll be using OpenAI to write a social post for us to promote this article and to generate an image which we can then use, on the web page or in social posts. On the OpenAI side, all we're gonna need is an API key.\u003C\u002Fp>\u003Cp>Send over to your dashboard, create a secret key, take note of it somewhere. We'll use it later. And then we're ready to go over into directors and set things up there. So over in our directors project, let's create a new flow. Let's call this one generate social post, and we will use a manual trigger on the article's collection on the item page.\u003C\u002Fp>\u003Cp>So this is our trigger. I wanna show you something interesting about this. So if we go over and refresh our item, we now see we can generate a social post here. Going back over to the flow and refreshing it and taking a look at the logs, we see here that we only get the ID of the article, and we actually wanna use the title of the article. So the first thing we need to do is create an operation called article, and we're gonna read that whole the whole article object.\u003C\u002Fp>\u003Cp>So we'll be going into the articles collection with full access, and we will want the trigger dot Bobby dot keys 0. So that is inside of that object where the ID for this item is. So now article will return the entire object. Now it's time to go and actually go to open AI and get it to generate a post for us. So we'll create a new operation.\u003C\u002Fp>\u003Cp>I'll call it generate. And we'll make a web request. This will be a post request to their chat completions endpoint. Now we need to authenticate ourselves with our API key. We'll do that with a header.\u003C\u002Fp>\u003Cp>Authorization bearer space, and then our API key, and then our request body. Now it's a little bit of a big object, so I'm gonna copy and paste a starting point to discuss here. We'll be using the GPT 4 model, and there are 2 messages we're gonna send in an array. The first is a primer for the system to tell it what we expect of it. So we're telling it here.\u003C\u002Fp>\u003Cp>They're an editor of a food blog. They take in recipes. They spit out social posts and the audience is busy professionals with little time. And, of course, you can tweak this for your specific use case. And then we get to write our actual prompt itself.\u003C\u002Fp>\u003Cp>So I'll say write a Twitter post for our article dot title recipe. So this is a dynamic value that is returned out of this object here let's hit save let's hit save again and let's rerun this flow notice it takes a little bit longer to run this time that's because it's actually going off to open ai and back So if we refresh here, we'll see a new log appears. And we'll see the returned value contains a data object with an array called choices. And inside of this object is a message and content. And that is a viable social post, or at least a starting point something to help us out.\u003C\u002Fp>\u003Cp>So the last thing we want to do here is actually save that social post back to the item. So we will add one final operation here. We will update data. We will update an article using that same key from earlier. So, trigger dot Bobby dot keys 0, I believe is what it was.\u003C\u002Fp>\u003Cp>And the payload we want to send is going to be social. That's the name of the field. And the value, it's quite nested. I did take note of where it was earlier, though. Generate because that's the name of the operation that returns this value.\u003C\u002Fp>\u003Cp>Generate dot data dot choices, that was an array, dot message dot content so let's save that let's refresh here and let's just try this one more time So lemon herb roasted chicken. We will hit generate social post. We'll wait just a moment, and we should see over there in the social box, our social post has been created. So that's the first half of what we're doing today. This is a double whammy.\u003C\u002Fp>\u003Cp>So now we're gonna create a new flow to generate an image. So now let's create a second flow for the image generation. I'll call this 1 generate image. It will be a manual flow trigger again on articles on the item page, except this time, we're going to ask the user for an image prompt. So we will require a confirmation which will pop up a model you'll see in a moment.\u003C\u002Fp>\u003Cp>So I'll call this confirm, and we can provide any number of keys. We just want one prompt. Of course, you might choose to make this a little more descriptive to users, but we're just gonna add an input with the value well, with the name of prompt. So what we're gonna do actually, once again, let's see how this works. So let's go back to our item.\u003C\u002Fp>\u003Cp>Let's refresh. And inside of flows, there's now 2. Here's generate image, and here's our prompt. So I will call this lemon herb roasted chicken in a baking dish, and I'm sure you could create something a little more creative. Let's run the flow.\u003C\u002Fp>\u003Cp>Let's refresh our flow and take a look at where that prompt has gone. And just like before, inside of our body, we have an array of keys. So that's the ID of the article, but we also have this prompt here. So we're gonna use that and immediately go off to DALL E, the image generation endpoint, and we will, and we'll generate an image using that prompt. So we'll call this one generate.\u003C\u002Fp>\u003Cp>We'll make a web request again. It will be a post request to this URL. Once again, we need to validate ourselves, so we will add the same header as before, authorization bearer space token, and then we have the request body. This is what the request body looks like. We pass in the prompt.\u003C\u002Fp>\u003Cp>We specify the size from 1 of the provided allowed sizes. With DALL E 3 at the moment, you can only generate 1 variation at a time. But if you use other DALL E models, you might be able to generate multiple. We'll just use 1, and that's the name of the model. So let's hit save once again, and we will rerun this flow.\u003C\u002Fp>\u003Cp>We see that's taking a little longer because now it's actually going off and generating an image for us we'll wait for that to be complete here We'll go back to the flow. We'll refresh to look at the new log, and we'll see here that in the data that is returned is an array called data with a revised prompt and a URL. Now we want to import this image via URL into our directors project. Now flows don't provide a way to do that out of the box. So I'm actually using this lovely community, community extension, which does allow us to import images from external URLs.\u003C\u002Fp>\u003Cp>So I will link this along with the video, but I've already gone ahead and made that extension available in this director's project. So let's add a new operation here. We will use this file import. I think I'll call this one import, and we provide the URL. So that came from the step that we could generate dot data dot data and dot URL like so And what file import will do is return just a string, and the string is the ID of the newly imported file in Directus.\u003C\u002Fp>\u003Cp>So instead of demoing it at this point, we're just gonna go ahead straight away and include that image inside of our post. So update data once again, very similar to before, articles, full access. The ID again will be trigger dot Bobby dot keys 0, and the payload will be image, and we just want that value there. So import because it doesn't return an object. It just returns a string.\u003C\u002Fp>\u003Cp>So let's hit save, and that should be everything we need. So we have lemon herb roasted chicken here. So let's go ahead and generate the image. Let me get my prompt back. We'll run flow on the current item.\u003C\u002Fp>\u003Cp>We'll give that a few moments to complete. It should go off, generate the image, import it to direct us, and then update this item with that newly imported file. So let's see if that works. There we go. Fantastic.\u003C\u002Fp>\u003Cp>Hurrah. So in this episode of QuickConnect a little bit longer, we've done 2 things with the open API the open AI APIs. We have created a flow which on demand can create a social post. And the nice thing is if you don't like it or you don't like the image, you can just press the buttons again, and it will go ahead and regenerate them. And the second one generates an image with the DALL E, model.\u003C\u002Fp>\u003Cp>It imports it into Directus using that extension and then includes it here inside of the item. I hope you found this super interesting. I had a lot of fun putting this one together, and go forth and have fun. See you next time.\u003C\u002Fp>","Chances are you've heard of OpenAI. They're the company behind ChatGPT. Now today in QuickConnect, we're going to integrate with OpenAI's APIs to use both their GPT 4 model and their DALL E image generation model to enrich data in our director's project. Here, I've got a set of articles, a set of recipes, and we'll be using OpenAI to write a social post for us to promote this article and to generate an image which we can then use, on the web page or in social posts. On the OpenAI side, all we're gonna need is an API key. Send over to your dashboard, create a secret key, take note of it somewhere. We'll use it later. And then we're ready to go over into directors and set things up there. So over in our directors project, let's create a new flow. Let's call this one generate social post, and we will use a manual trigger on the article's collection on the item page. So this is our trigger. I wanna show you something interesting about this. So if we go over and refresh our item, we now see we can generate a social post here. Going back over to the flow and refreshing it and taking a look at the logs, we see here that we only get the ID of the article, and we actually wanna use the title of the article. So the first thing we need to do is create an operation called article, and we're gonna read that whole the whole article object. So we'll be going into the articles collection with full access, and we will want the trigger dot Bobby dot keys 0. So that is inside of that object where the ID for this item is. So now article will return the entire object. Now it's time to go and actually go to open AI and get it to generate a post for us. So we'll create a new operation. I'll call it generate. And we'll make a web request. This will be a post request to their chat completions endpoint. Now we need to authenticate ourselves with our API key. We'll do that with a header. Authorization bearer space, and then our API key, and then our request body. Now it's a little bit of a big object, so I'm gonna copy and paste a starting point to discuss here. We'll be using the GPT 4 model, and there are 2 messages we're gonna send in an array. The first is a primer for the system to tell it what we expect of it. So we're telling it here. They're an editor of a food blog. They take in recipes. They spit out social posts and the audience is busy professionals with little time. And, of course, you can tweak this for your specific use case. And then we get to write our actual prompt itself. So I'll say write a Twitter post for our article dot title recipe. So this is a dynamic value that is returned out of this object here let's hit save let's hit save again and let's rerun this flow notice it takes a little bit longer to run this time that's because it's actually going off to open ai and back So if we refresh here, we'll see a new log appears. And we'll see the returned value contains a data object with an array called choices. And inside of this object is a message and content. And that is a viable social post, or at least a starting point something to help us out. So the last thing we want to do here is actually save that social post back to the item. So we will add one final operation here. We will update data. We will update an article using that same key from earlier. So, trigger dot Bobby dot keys 0, I believe is what it was. And the payload we want to send is going to be social. That's the name of the field. And the value, it's quite nested. I did take note of where it was earlier, though. Generate because that's the name of the operation that returns this value. Generate dot data dot choices, that was an array, dot message dot content so let's save that let's refresh here and let's just try this one more time So lemon herb roasted chicken. We will hit generate social post. We'll wait just a moment, and we should see over there in the social box, our social post has been created. So that's the first half of what we're doing today. This is a double whammy. So now we're gonna create a new flow to generate an image. So now let's create a second flow for the image generation. I'll call this 1 generate image. It will be a manual flow trigger again on articles on the item page, except this time, we're going to ask the user for an image prompt. So we will require a confirmation which will pop up a model you'll see in a moment. So I'll call this confirm, and we can provide any number of keys. We just want one prompt. Of course, you might choose to make this a little more descriptive to users, but we're just gonna add an input with the value well, with the name of prompt. So what we're gonna do actually, once again, let's see how this works. So let's go back to our item. Let's refresh. And inside of flows, there's now 2. Here's generate image, and here's our prompt. So I will call this lemon herb roasted chicken in a baking dish, and I'm sure you could create something a little more creative. Let's run the flow. Let's refresh our flow and take a look at where that prompt has gone. And just like before, inside of our body, we have an array of keys. So that's the ID of the article, but we also have this prompt here. So we're gonna use that and immediately go off to DALL E, the image generation endpoint, and we will, and we'll generate an image using that prompt. So we'll call this one generate. We'll make a web request again. It will be a post request to this URL. Once again, we need to validate ourselves, so we will add the same header as before, authorization bearer space token, and then we have the request body. This is what the request body looks like. We pass in the prompt. We specify the size from 1 of the provided allowed sizes. With DALL E 3 at the moment, you can only generate 1 variation at a time. But if you use other DALL E models, you might be able to generate multiple. We'll just use 1, and that's the name of the model. So let's hit save once again, and we will rerun this flow. We see that's taking a little longer because now it's actually going off and generating an image for us we'll wait for that to be complete here We'll go back to the flow. We'll refresh to look at the new log, and we'll see here that in the data that is returned is an array called data with a revised prompt and a URL. Now we want to import this image via URL into our directors project. Now flows don't provide a way to do that out of the box. So I'm actually using this lovely community, community extension, which does allow us to import images from external URLs. So I will link this along with the video, but I've already gone ahead and made that extension available in this director's project. So let's add a new operation here. We will use this file import. I think I'll call this one import, and we provide the URL. So that came from the step that we could generate dot data dot data and dot URL like so And what file import will do is return just a string, and the string is the ID of the newly imported file in Directus. So instead of demoing it at this point, we're just gonna go ahead straight away and include that image inside of our post. So update data once again, very similar to before, articles, full access. The ID again will be trigger dot Bobby dot keys 0, and the payload will be image, and we just want that value there. So import because it doesn't return an object. It just returns a string. So let's hit save, and that should be everything we need. So we have lemon herb roasted chicken here. So let's go ahead and generate the image. Let me get my prompt back. We'll run flow on the current item. We'll give that a few moments to complete. It should go off, generate the image, import it to direct us, and then update this item with that newly imported file. So let's see if that works. There we go. Fantastic. Hurrah. So in this episode of QuickConnect a little bit longer, we've done 2 things with the open API the open AI APIs. We have created a flow which on demand can create a social post. And the nice thing is if you don't like it or you don't like the image, you can just press the buttons again, and it will go ahead and regenerate them. And the second one generates an image with the DALL E, model. It imports it into Directus using that extension and then includes it here inside of the item. I hope you found this super interesting. I had a lot of fun putting this one together, and go forth and have fun. See you next time.",[5139],"be12dd08-61b5-4003-9279-72cebbbf494f",[],{"year":2380,"number":194,"id":4605,"episodes":5142,"show":5143},[4607,4608,4609,4610,4611,4612,4613,4590],{"title":1746,"slug":1747},{"id":1789,"slug":5145,"vimeo_id":5146,"description":5147,"tile":5148,"length":2280,"resources":5149,"people":5152,"episode_number":13,"published":5156,"title":5157,"video_transcript_html":5158,"video_transcript_text":5159,"content":12,"seo":12,"status":19,"episode_people":5160,"recommendations":5164,"season":5165},"json-filtering","903011547","In this recording of our live event on January 11 2024, Rijk, Jonathan, and Daniel discuss filtering inside of stored JSON objects. ","b2035eee-e7c0-44b0-80f6-ba0f7d5fbe37",[5150],{"name":4496,"url":5151},"https:\u002F\u002Fgithub.com\u002Fdirectus\u002Fdirectus\u002Fdiscussions\u002F7277",[5153,5154,5155],{"name":3795,"url":3796},{"name":3798,"url":3799},{"name":3801,"url":3802},"2024-01-18","Filtering of JSON Objects","\u003Cp>Speaker 0: Welcome, everyone. Happy 2024. We're excited for a new year. We're gonna try out a new well, we've been we've been playing around with this request for views format for a little while, but we're gonna we're gonna try out just covering a specific feature, talking through some details, making sure that we have a full specification on it, and getting additional community feedback, as needed. Today's topic will be JSON object filtering.\u003C\u002Fp>\u003Cp>It's been a it's a very, very popular request that we see quite frequently, and we would love to ensure that we solve this problem correctly and get it working for the databases that support it. I think one of the key issues that we've run into we've done a lot of development work on this, but what we run into is various database vendors support this differently. You fix it to support that thing. It breaks something else. We spent a lot of time kind of iterating back and forth on this feature, and I'll let Rai talk about some of that.\u003C\u002Fp>\u003Cp>But I mean, fundamentally, we we know that this is important. We consider it kind of a critical road map item, and that's the reason we're gonna talk in detail about it today.\u003C\u002Fp>\u003Cp>Speaker 1: Absolutely. Well, thank you for that. Yeah. And as you can see, I was the one who opened this feature request conveniently with no details whatsoever. I basically just say, hey, man.\u003C\u002Fp>\u003Cp>I wanna be able to filter in JSON objects. Good luck. And then everybody was like, yep. Me too. However, you know, the this this was done way before we had that RFC format, so the the the details are lacking.\u003C\u002Fp>\u003Cp>Let's let's call it that. So before we dive in too deeply, you know, Daniel, I'm just gonna throw you under the bus here. You wanna walk us through what what even is JSON filtering in the first place?\u003C\u002Fp>\u003Cp>Speaker 2: JSON filtering can be quite useful if you store JSON inside of your database, and you need to check some field inside of that JSON in your database. Like, we said in the beginning, right, not every database supports this as of right now, which forces us or will force us to, you know, do some little Directus magic as usual. But more and more databases kinda jump on the train, which is pretty neat. I think as what was it? SQLite very recently also announced that they have JSON b support, I think.\u003C\u002Fp>\u003Cp>I hope I'm not miss remembering. But, yeah, can be quite useful. If you have stored JSON, then you can filter on fields inside of that JSON. That's very useful.\u003C\u002Fp>\u003Cp>Speaker 0: And I think as as Kevin points out, actually, a couple of our default interface configuration our field configurations actually store in JSON by default. You can change them in most cases, but and, currently, that's the recommendation. So off the support side of things currently, for those of you listening, if you use the CSV format instead, on the database field setting side, you can then filter through the application in the API on those fields. But JSON is a little bit nicer structured format, so the reason for the request.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And then the other the other big sort of elephant in the room there, of course, is that a lot of systems are utilizing and or could be utilizing more of a document style structure rather than, you know, a tabular style data structure. Especially, you know, when you have flexible schemas or unknown, you know, data structures ahead of time or semi semi structured data. Think about, you know, blocks on a page or something. You know, you're talking about sort of rich data that may or may not be structured like a table.\u003C\u002Fp>\u003Cp>So, therefore, you know, storing JSON in a Postgres database or another SQL database can give you some of that document magic without having to switch over completely to a document database. So there's a lot of a lot of benefits to having this. Now that being said, it also comes with a ton of complexities. So in my original feature request I was really thinking about it sort of as a filter against the data. Right?\u003C\u002Fp>\u003Cp>So similar to how we have, some functions to run against date values. I don't know if you've seen those before, but we have things like extract the year from a timestamp as a function. I was thinking about it the same way in my original feature request. Right? So I was thinking maybe we can have just like you would be able to do, like, year time stamps equals 2024, you could do something like, I don't know, Jason, name of the field, and then some sort of identifier string to select something from that field and then run filters against it, against that value that you've now selected.\u003C\u002Fp>\u003Cp>Right? Similarly you'd be able to use that in fields or in sort or other pieces like that And we actually put a lot of work in that already, and by we, I mean Tim who's in the chat, and I'm definitely gonna put him on stage and put him in the spotlight. Jonathan, if you wanna pull that up real quick, it might actually be fun to take a quick scroll through. Also, as a way to answer that, do all DBs even support it? Because there's a wonderful table at the top of that pool request.\u003C\u002Fp>\u003Cp>If you wanna pull up the, don't bring him up. We're recording. He's he's a little camera shy, but we can we can figure it out.\u003C\u002Fp>\u003Cp>Speaker 0: You know the PR off the top of your head, or do I just need to go find it?\u003C\u002Fp>\u003Cp>Speaker 1: Oh, just just open up pull requests, and if you search for Jason, it should be the only one that's there.\u003C\u002Fp>\u003Cp>Speaker 2: GitHub\u003C\u002Fp>\u003Cp>Speaker 0: pull requests.\u003C\u002Fp>\u003Cp>Speaker 1: We've left it open intentionally. We'll we'll circle back to that later how we're handling now. But if you just probably all the way at the bottom is is a little older. Yep. There it is.\u003C\u002Fp>\u003Cp>Speaker 2: Alright.\u003C\u002Fp>\u003Cp>Speaker 0: There's your matrix.\u003C\u002Fp>\u003Cp>Speaker 1: Right. So in this PR, we basically took a swing at implementing that, you know, JSON filtering the way it was described in that discussion. And by described, I mean, vaguely hinted at, because the description was a little poor. But we did implement it in that way, and it does actually work. So we do have, you know, queries for a little different databases.\u003C\u002Fp>\u003Cp>But as you can see in this table to the Chet's point earlier, not everything is supported everywhere, which becomes tricky immediately. Right? Because how do you then document it? Now it's gonna be database different. So there has been, you know, implemented some fallback support so you can see the difference in that green check versus the, that sort of Unicode check I guess.\u003C\u002Fp>\u003Cp>You you can see that for a lot of these things we're actually you know having to do some direct us magic to to make that work against the database which is not necessarily gonna be the most performant or the what's the right word? The the the quote, unquote right way to do it. Right? It's a blue check mark. Oh, if you're on Windows, just pretend.\u003C\u002Fp>\u003Cp>If you're on Linux or Linux, just Linux, just pretend. The check marks are green. Anyways, while building this, if you might wanna pull up the file section of this PR, Jonathan, I think it's a fun fun scroll through. This if we go all the way to the top, there's a tab. Files changed.\u003C\u002Fp>\u003Cp>Speaker 2: 2000 editions. Oh my god.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. There's there's only a 1,000 lines across 50 files. The long the long story short is that in the database helpers, if you see in that left hand sidebar, you know, we have to add all of the additional queries for JSON filtering ourselves for a little different database types. Sort of hard coded in, You see them here in the dialects if you just click one of those. It doesn't really matter which one.\u003C\u002Fp>\u003Cp>So all of those sort of you can see them here. You know, we have a JSON extract for MariaDB that may or may not exist across the other database vendors. Right? So it's not a SQL standard, which makes this a heck of a lot more complicated than you'd think. It's because every database does it differently.\u003C\u002Fp>\u003Cp>I believe it was Postgres that doesn't rely on functions even. It has like a special syntax with like, builders and arrows and whatnot. Which is pretty interesting. I don't\u003C\u002Fp>\u003Cp>Speaker 2: know if the\u003C\u002Fp>\u003Cp>Speaker 1: voice first 12 one has that in there. But anyways, there's there's, like oh, maybe this that's the one with the question marks there. I don't know. There's there's all sorts of different syntaxes is what I'm trying to say. Oh, here they are drawing with the arrow the the dash arrow arrow.\u003C\u002Fp>\u003Cp>That kind of stuff. So what we realized with this sort of initial work that we did on the JSON filtering is that, well, 8, super complicated as you can tell. There's a lot of additional work and a lot of additional logic, which means in turn you're gonna maintain. As always, because the more stuff you add, the harder it becomes to maintain. Right?\u003C\u002Fp>\u003Cp>But, the third thing is we're also starting to we also start to wonder like, okay. Instead of, having it as a function style thing in the query parameter, what if we do it more like the fields parameter itself? Right? Where you can just say, like in GraphQL, for example, you could just provide a nested tree that you wanna select instead of having to do it through a filter attribute. And at the same time, we've been working on a new data abstraction engine in the first place that already sort of, like it doesn't necessarily fix the fact that we have to do a lot of stuff ourselves for every database because it's just database specific, but it does it in a way that is designed to have database specific drivers rather than dialect specific overwrites.\u003C\u002Fp>\u003Cp>Right? So this is gonna be a bit of a more of a deep dive, but the way the API is set up right now is that everything effectively goes through connects, right? The SQL, what do you call it? SQL query builder that basically everybody and their mom is using. And what that means is that first it just becomes a sort of generic SQL, and then at the very end it's translated for the individual vendors.\u003C\u002Fp>\u003Cp>Right? So it's effectively this is a very crude explanation, but it's effectively just doing a find and replace for the quotes, making sure it's, like, the right quote for the right database. And then for some of the databases there's a little bit of additional magic, like for SQLite, you know, for an ALTER TABLE statement there's some magic included there. But the long story short is to add stuff like JSON filtering, it becomes tricky because now it's database specific. Right?\u003C\u002Fp>\u003Cp>So we don't really have a way and it was the same with the timestamp, helpers that we did earlier. We don't really have a way to make that agnostic across all of the different database vendors. Right? The second part there is that we know that we wanna support more database vendors over time, not less. So trying to do it in this sort of like make it generic first, add 1 by 1 overwrites to the dialects.\u003C\u002Fp>\u003Cp>It doesn't really scale anymore. Right? But end of the versions. A very good point, Tim. Because I think if we go back into the table, we already saw it.\u003C\u002Fp>\u003Cp>There's, like, differences between MySQL 5 and MySQL 8 plus, although 5 is now end of life. So that's a whole different discussion. But, we're the same for, you know, Postgres 10 versus 13 and up. So the way we're sort of re architecting that piece is by saying, you know, there is a singular, there's still a singular data entry point, but rather than relying on SQL, it relies on an abstract syntax tree, you know, Veron Design. Just a proprietary data format that explains to the engine what the data looks like that we wanna fetch.\u003C\u002Fp>\u003Cp>Right? And then for each of the different vendors, we're gonna have a driver that interprets that command and then just executes it in whatever way is appropriate for that driver. Right? So for a lot of the SQL drivers, we can still share a lot of that SQL magic like we're doing now. But it also means that we can start opening up the doors to other, you know, other data sources and other, JSON specific data sources.\u003C\u002Fp>\u003Cp>Right? So one of the reasons there, to keep going on that train of thought, is also because once we started getting super deep into JSON filtering, we also started to realize, like, well, if we implement the JSON filtering like field selection rather than, filtering specific functions, you end up with effectively just drivers for a document data store. Right? Which is very interesting. So with that in mind, we could also start thinking about what does it look like to use something like a DynamoDB or MongoDB or some other sort of key value slash unstructured document style store with the sort of direct to the API.\u003C\u002Fp>\u003Cp>Importantly, not treating them as a relational database, but treating them as a document data store, like leaning into the flexibility of a document database rather than trying to force them into a relational structure, right, like we've seen seen in the past and have explicitly avoided. That was a very long train of thought. But what I'm trying to say is, what we're getting at with that is that we've effectively not shelved, but we've sort of put the pause on this particular PR. And instead, focusing now on implementing this sort of JSON selection support directly into this driver based approach. Right?\u003C\u002Fp>\u003Cp>Because the one thing we didn't wanna do is add a lot of complexity add more complexity now only to then replace it, you know, in a couple of months with another breaking chain with completely different structure. But we've left open the PR because we definitely don't wanna lose any of work in any of the code because it is being, you know, repurposed repurposed into the new structure. In the chat, there would all be called features, not bugs. That's very true. Any bug in SQL is a feature.\u003C\u002Fp>\u003Cp>Speaker 0: Got a question about the specs for the drivers. I believe we do plan to fully spec and document those the driver interfaces, right, for the new architecture?\u003C\u002Fp>\u003Cp>Speaker 1: Absolutely. Absolutely. Yes. Yep. I mean, as of right now, it's still very much, you know, in in r and d, so we're not opening the doors quite yet on on building your own, but it is built basically as an extension in mind.\u003C\u002Fp>\u003Cp>Right? So we wanna make sure that those things are just do whatever you want as long as you adhere to the spec, you can you can, you know, save and read data from wherever the f you want. But, you know, we're definitely focusing on just feature parity with what we have first, and then expanding the scope there with JSON filtering and some other, you know, additional relationship types. And then, you know,\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. I love the I love the idea that we're moving towards a an extension driven driver approach. Right? The same way we've done with so many other components of the platform, making it so that it's extensible.\u003C\u002Fp>\u003Cp>And it also means that you've got a custom data source. You've got an API. You've got other things. You'd have a spec and be able to build your own driver against some custom data source that you have, and be able to leverage the API and power of the Directus application on top of that. So very exciting very exciting 2024.\u003C\u002Fp>\u003Cp>Speaker 2: Absolutely. Wait for the first person, implementing the, Excel sheet data store. I think this will be a a smash hit in Germany. All of the all of the companies love Excel so much. Oh, Maybe maybe The whole finance world rejoices.\u003C\u002Fp>\u003Cp>Oh.\u003C\u002Fp>\u003Cp>Speaker 1: We should start that as a little competition. Whoever builds the Excel data store first gets a shout out on the website.\u003C\u002Fp>\u003Cp>Speaker 2: Oh, you bet. Oh, you bet.\u003C\u002Fp>\u003Cp>Speaker 1: A signed certificate of insanity by by me. Love that.\u003C\u002Fp>\u003Cp>Speaker 2: The the worst thing is the the very worst thing is that people will actually, like, honestly use it, I'm afraid.\u003C\u002Fp>\u003Cp>Speaker 1: It's it's not the using it part that that worries me. It's the relying on part that me. Anyhoo, circling back to, more of the the specifics of JSON filtering as a whole. Right? So just to circle circle back to to a requirements list because we we don't have, you know an RFC for JSON filtering proper we don't really have an RFC for what it could look like in new formats.\u003C\u002Fp>\u003Cp>Right? We do know that we want to support it from field selection perspective with that sort of nonstructured data store in mind. We do know that we have to support it as part of filters and sorting and querying and and all of that goods. That being said there is a very interesting difference between JSON objects which is sort of the assumed default that we've been talking about here, and arrays, which is where it gets real complicated real quick. Because one of the main questions or use cases where this is coming from is for fields like tags, right, where you just have a JSON list of individual strings for tags and then how do you search through those.\u003C\u002Fp>\u003Cp>Right? But now you're not so much talking about you need to make a nested selection of an adjacent path and then filter against that. Now you have to now search through each item of the the array. Right? So we do have some specific magic going on for one to many's right now like some and none for example to say I want all of the values in my related table to match x y z But we should probably add something along those lines for Jason when it comes to filtering specifically.\u003C\u002Fp>\u003Cp>Speaker 2: I think.\u003C\u002Fp>\u003Cp>Speaker 1: This is mostly again, for for those who've who've joined us in the past on these live sessions, for Rich, welcome back, the the goal is oftentimes, you know, to really, really diverge to find what are the boundaries of what we can or eventually maybe want to do with this, you know, and then converge back into what is realistic and what does that first MVP look like. This is really that diversion thinking stage. Right? It's like, how how far do we need to go when it comes to filtering on stored values? For example, do you need to be able to say things like, I want all of the JSON objects into an array to have a nest of property author dot agent.\u003C\u002Fp>\u003Cp>They all need to be bigger than than 12. Right?\u003C\u002Fp>\u003Cp>Speaker 2: I've actually never used, like, field type currying like in Postgres or in the others. So I'm curious. Is there a function of the database that tells you whether or not the stored value is an array or not? Like, is there, like, some database layer check that tells you that? Is that possible?\u003C\u002Fp>\u003Cp>Speaker 1: I I think the the realistic answer is we can't assume that there is because we're talking about, you know,\u003C\u002Fp>\u003Cp>Speaker 2: good plan. Good plan.\u003C\u002Fp>\u003Cp>Speaker 1: We're we're talking about various different database vendors, and we're talking about various different SQL like vendors that, you know, are are sort of SQL inspired, but not fully compliant. Think of, you know, the the I wanna say plan and scale, but they recently added foreign keys. So I think they might not be more compliant than they used to be. But those types of vendors, right, where it's like they they use the SQL syntax for basic querying, but they don't have a full SQL engine behind it because they just implemented the data store differently. So the answer is we don't know nor can know.\u003C\u002Fp>\u003Cp>Because we need to build this and design the the specs for this in a way that is sort of agnostic to a data store. Right? Tim was mentioning, you know, we have 3 different pieces of functionality extracting the data in fields, extracting the data from a field, and then using it to filter. And then with the deep filtering against stuff within that JSON blob, filtering inside the j oh, yeah. Yeah.\u003C\u002Fp>\u003Cp>Filtering inside of an area with a deep. Yeah. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: So\u003C\u002Fp>\u003Cp>Speaker 1: So I think, ideally and this is a bit of a a different way of thinking in how we treat the API because it used to be very, you know, tabular data forward first is I think what we're leaning towards now is really treating JSON values f it as if it's just any other table effectively. Right? So we just treat an object as if it's any other item in the database. We treat an array as if it's any other table in database. And therefore, just allow you to use any of the regular query parameters as if it's a table.\u003C\u002Fp>\u003Cp>But you get some very interesting usage patterns at that point. Right? Could you consider using JSON schema to type and validate field? Absolutely. There's a very interesting thing, though, when it comes to to JSON and and typing slash validating, is that a lot of people sort of accidentally will start using a document database as a relational database, therefore, completely defeating the point of using a document database.\u003C\u002Fp>\u003Cp>Right? And this has been the the main, if not the only argument, that we've had against supporting MongoDB in the past because it has come up before. You know, there have been a couple of feature requests every now and again. But it was always the question of, oh, let's just use MongoDB instead of Postgres. But that really makes no sense if you did pardon my French.\u003C\u002Fp>\u003Cp>It makes no sense if you just if you just think about it real. Right? Because the a document data store has a lot of perks. There's, like, a lot of good things about it. But using it as a relational database is just not what it was designed to do best.\u003C\u002Fp>\u003Cp>So you're kinda just forcing it to do something it wasn't made for. At which point, it's like just use a SQL database. Right? If you wanna have relational data in a tabular format, use the SQL database. That's what they excel at.\u003C\u002Fp>\u003Cp>That's what they're best at. That being said, it's the overlap of the 2. You know? And so overlap of the 2 where it gets really interesting where you know, if you're talking about, things with rich content, like, you have views in an app or pages on the website or something, right, where you have fixed data points, fixed metadata points that you have for everything. You know, you have a title, you have an author, you have a published date, you have some of those pieces, like a status.\u003C\u002Fp>\u003Cp>But then you also have rich content which is gonna be, you know, a semi, semi flexible schema. Right? It's when you mix the 2 where I think it gets really, really interesting. But that does also mean that you need to be able to manage and search through and extract the nonstructured part with a semi known schema. That's where it gets tricky.\u003C\u002Fp>\u003Cp>Right? So, what made me think about that is the question, you know, consider using a JSON schema to validate the fields. If you're strictly validating against one schema of JSON objects, at that point, are you better off with using JSON or should you just make a couple of columns? Right? Because a many to one, field with a related table with structured columns will most likely perform better than trying to do it with nested objects.\u003C\u002Fp>\u003Cp>If, you know, assuming that there's gonna be searching and filtering and and organizing involved. Another question from the chat here was, Postgres allows for date ranges, which is effectively a JSON array. Somebody built a custom date range interface for this but cannot filter against it. That's the question is that something that will be supported by this pr or would that be something else? Yep.\u003C\u002Fp>\u003Cp>That's basically exactly it. You know, you have any sort of arbitrary JSON value you need to be able to manage and search through it. It is a table document for the record, though. Each record in a collection have significantly different content structure. Yes.\u003C\u002Fp>\u003Cp>That is a very good point, which is why the JSON schema again becomes interesting. Right? Because if you have a column that you want to be the same schema for every single row in your database, in your table, then relying on, you know, adjacent schema for validation makes a lot of sense because you want every one of those to be the same. At that point, you know, what becomes the benefit of using JSON over just columns? Right?\u003C\u002Fp>\u003Cp>That those those are sort of the questions that we need to, ask ourselves. Because the other one is like, if you have the because because the nice thing about the JSON thing is that anything could go in there. That's kind of the point. Right? It's like an unstructured data type.\u003C\u002Fp>\u003Cp>So what if you want everything to be different? Or maybe you want it to be one of 5 known different schemas, right, instead of just the same fixed object. I guess with an adjacent schema, you have union types, if I'm not mistaken. But food for thought. It's an interesting one.\u003C\u002Fp>\u003Cp>But it it does, you know, it does raise a different question that we need to write. What about validation? Right? Because, like, we have validation rules for, for regular columns now where you can say, you know, a number needs to be up and greater than something. For JSON fields specifically, we need to come up with something.\u003C\u002Fp>\u003Cp>Right? Using a JSON schema could be very interesting. Maybe a JSON schema for the whole record instead of just the JSON thing field could be interesting. Maybe both. Maybe it's a nested.\u003C\u002Fp>\u003Cp>Here's another another thing. Let's see what was the other chat related things on the same click. Somebody else, asked, I'd have missed this, but the idea is to integrate directives as existing filters at dynamic variables, etcetera, or would there be a new JSON specific functions? So that is a great question and kind of the the, instigator of rehashing this chat because we had this feature request. We sort of we're looking at it through the lens of let's make a specific JSON function like we have the others, and just implement that.\u003C\u002Fp>\u003Cp>Right? And then Tim went ahead and did it. Kudos. A tremendous amount of effort. But we also really started to, then, you know, it's it's kinda what we talked about with the the data abstraction piece just now.\u003C\u002Fp>\u003Cp>We started to realize that it might be way more powerful to try to treat it as an item rather than treat it as a value. So you unlock all of the other query parameters against the value. That answers. It gets it gets a little bit theoretical quick here.\u003C\u002Fp>\u003Cp>Speaker 0: Soon as you use\u003C\u002Fp>\u003Cp>Speaker 1: it relationally, it becomes even harder to maintain. Good point. Good point. Just started using as a MongoDB as a replacement for Redis for for a project that doesn't use Directis they are crypto so their relational is based on wallets. Makes sense You know, if you think about that use case, you have a semi structured data.\u003C\u002Fp>\u003Cp>You know roughly what goes into a wallet, but there's a lot of optional, fields oftentimes. Depends on the implementation, of course. But one thing you do know is that you have a fixed primary key that you can use for some relationships. Big sport of Jason's schema. Everywhere.\u003C\u002Fp>\u003Cp>Just for the spec Simpler, Tidebox for the win. I that used to be my go to and then a lot of others in in my team sort of using Zod, and I kind of have been converted to the\u003C\u002Fp>\u003Cp>Speaker 2: The inference is pretty\u003C\u002Fp>\u003Cp>Speaker 1: is not true here.\u003C\u002Fp>\u003Cp>Speaker 0: Let's see what\u003C\u002Fp>\u003Cp>Speaker 1: You kinda want to know that oh, it disappeared. Where it is? Oh, you kinda wanna know that a list of tags is always a list of strings or that an object matches a specific schema. Depends. I'd I'd say, generally, yes, but also really depends.\u003C\u002Fp>\u003Cp>For example, in to give a system thing as an exam right? For interface options, we don't know ahead of time what's going in there. Right? It's up to the interface to figure out. It's just anything.\u003C\u002Fp>\u003Cp>It's basically just a blank store for an interface to do some sort of options, but it really doesn't matter what the format is, what's in it, etcetera. Somebody shared p g adjacent schema, schema validation for postgres specifically. It's where it gets real tricky. Solution relied on the different filter syntax. JSON pass number to XPath.\u003C\u002Fp>\u003Cp>Yeah. Yep. Exactly. I think the, the the JSON schema example is is a good example of the the added sort of complexity that we're putting ourselves in here on purpose, which is that we're really really aggressively want this to be a standard in the API that you can use no matter what the data source is. And then it just depends on the data source how performant it becomes.\u003C\u002Fp>\u003Cp>Right? But I'd really, really would like to prevent ending up with, you know, a long table in the docs that says, can you use JSON filter? Yes. No. No.\u003C\u002Fp>\u003Cp>No. Yes. Yes. Yes. No.\u003C\u002Fp>\u003Cp>Yes. Yes. Yes. No. For for the different drivers, especially if those drivers are are third party maintained.\u003C\u002Fp>\u003Cp>You know, there's it's gonna be incredibly annoying. It does mean that for some of the data source, we'll definitely have to, we'll definitely have to, what's the right word, add add fallback logic and do a lot of that magic on the director side of things rather than on the data store but so be it You know? I I think that is a a trade off worth having, as long as these drivers can handle it themselves. Because Yeah. Right.\u003C\u002Fp>\u003Cp>Speaker 2: We're fine. Scenario. Go ahead. Sorry. The the delay.\u003C\u002Fp>\u003Cp>No. It was, just for Brian's message, which was, the discussion point, my mind towards use cases like page builders, where I wouldn't need to create a ton of separate tables. That's true. And this is exactly the use case where I, in the past have used. You know, just sometimes if you very quickly want to throw together a component, but you don't really don't wanna create, like, different relationships and whatever.\u003C\u002Fp>\u003Cp>So you just quickly make, like, an h ref with the label and slap it together as adjacent, for example, very quickly, you know. That's that's a very nice use case for that. Yeah. Very quickly, very easy.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm. For sure. And I\u003C\u002Fp>\u003Cp>Speaker 2: I agree.\u003C\u002Fp>\u003Cp>Speaker 1: Another interesting thing that he's mentioning there is saying that it would be better served by a NoSQL database. Right? That's where the interesting discussion really starts. Because if you're talking about, you know, a page builder for a website, then the pages themselves, just like what is the route? What is the title?\u003C\u002Fp>\u003Cp>What is some of the metadata? I'd argue that's probably better for a SQL database. Right? Because you're talking about structured data that you wanna query through on a column by column basis. You wanna say get all articles from, you know, January 2024, for example.\u003C\u002Fp>\u003Cp>That is gonna be faster in, you know, a relational database because it has, you know, structured data with known types and you could search through them efficiently. But the content part of a page, absolutely, it makes a lot of sense to have that as a page builder. Right? Because you have semi structured data. You have an array with 1 or more or 0 or more, I guess, you know, blocks of a known type.\u003C\u002Fp>\u003Cp>So that's where that JSON validation with schema comes in potentially. You know? And then that's the data is semi structured. So you have objects of, like, x different types, and then that has an array of those things. Yeah.\u003C\u002Fp>\u003Cp>So that makes a ton of sense. But I think the real power is the combination of the 2. Because if you were to try to do all of those pages in MongoDB, now you have to trade off of the sort of filtering performance and sort of the searchability aspect of it. But on the SQL side right now, you have the penalty of not having that unstructuredness of the data. Right?\u003C\u002Fp>\u003Cp>So right now, we would rely on any to any's for that type of use case, which has its own benefits around, you know, the searchability and the joinability. But, you know, if you're on pages where you don't really have to reuse, sections, for example, then, yeah, I agree. You're doing it as a JSON object for for a page builder makes a lot of sense. Right? But implementing it in a way where you get both is really the\u003C\u002Fp>\u003Cp>Speaker 2: Oh, this this is why Brian, the father of, agent c OS, He he felt that pain probably, so he came up with this. Yeah. Yeah. But I agree wholeheartedly. Like, this came up exactly when I wanted to do then.\u003C\u002Fp>\u003Cp>If you have, like, one off components that you don't really, you know, they don't have something to do with other stuff or you want to get some data in, it's very, very, very neat. The repeater interface is very useful for that.\u003C\u002Fp>\u003Cp>Speaker 1: Now there's one additional topic that we haven't touched on that I think we can easily spend 15 minutes on. And Brian is hinting at it right now, actually. Relationships. Relationships is where it gets real tricky. Right?\u003C\u002Fp>\u003Cp>Because right now, we know that's, article dot author is a foreign key to author's table. Right? So that that metadata is known. We know that ahead of time. So therefore, we can do things like give me everything, nest everything, nest everything, you know, start up start dot star whatever.\u003C\u002Fp>\u003Cp>And it knows what to nest because it knows what the relationships are. Right? When you're in within a JSON document or an area of JSON documents, there's really no guarantee that something is an ID or there's no real knowledge of what that path is and where it points. Right? So once you want to start nesting, data like that, it becomes tricky, tricky, tricky.\u003C\u002Fp>\u003Cp>Right? So within a a MongoDB context, if I'm not mistaken, it's like a MongoDB ID is always globally unique. Right? Right? So as soon as you have a nested data of that, I what what did they call it again?\u003C\u002Fp>\u003Cp>There was, like, a Mongo ID special type. I forgot what it's what it's called. Object ID is I think what they call the type. Anyways, when it encounters a value of type object ID, it it can assume that it's a globally unique ID and therefore knows what the what document to fetch. But in a sort of hybrid model, you don't necessarily have that luxury.\u003C\u002Fp>\u003Cp>Right? Because you can have a nested, categories array in your adjacent object that just says 123 with the assumption that you're talking about rows 123 in your categories table. Right? Because you're mixing concepts, because there's no such thing as globally unique IDs in a relational database.\u003C\u002Fp>\u003Cp>Speaker 2: Kind of a design. I'm already imagining some type of custom format with, like, a dollar sign, direct to dot one or something, and then we have to translate it. And oh, no. It's a whole repertoire. We could do this.\u003C\u002Fp>\u003Cp>Speaker 1: The same way as the chat, apparently. Somebody is saying direct us could prefix it.\u003C\u002Fp>\u003Cp>Speaker 2: That's what I meant. Oh, yeah.\u003C\u002Fp>\u003Cp>Speaker 1: We could. We could. But it does mean that we would have to make the data in your tables proprietary, which is something that I'm personally not a fan of. Right? It's like the system tables are one thing, because, of course, you need to have some metadata saved somewhere and we need, you know, a place to store that.\u003C\u002Fp>\u003Cp>But I really, really badly, desperately do not want to introduce any sort of direct to specific proprietary nonsense into your own database, in the user tables rather. So that's a tricky one.\u003C\u002Fp>\u003Cp>Speaker 2: Right? So If we would do that, then we would probably also have to provide a way to extract the data, and it's it's more and more patches on top of patches. I don't think we wanna go down that road.\u003C\u002Fp>\u003Cp>Speaker 1: The question really becomes like, how do you pull together a join? Right? And it's the same the same reason we haven't really, because Brian said relational repeater. Right? The reason why our repeater interface currently is not relational is because of this exact reason.\u003C\u002Fp>\u003Cp>It's like, how do we store the information of, you know, what field is relational to what? And the other tricky thing is, you know, without some real clever engineering from John and Nicklaus, most likely, There's no guaranteed way you can really do a join, based on the nested JSON value. Like some databases would, some databases don't. You know, it's the same sort of story. Right?\u003C\u002Fp>\u003Cp>So it becomes a performance nightmare. But what are some of our options there? Right? Just thinking out loud. One would be that we have some sort of, kinda like direct directors relations.\u003C\u002Fp>\u003Cp>Right? Where we're saying, well, if your JSON object has a path called, I don't know, article dot author, it we're assuming it's a foreign key, but that is always gonna be, you know, you can't really guarantee that. Right? An additional option would be to allow, you know, the end user to sort of pass the join information, right, manually through the API, where instead of saying author dot name where we know that author is a relationship, maybe there's some sort of syntax where you can just say author joins on table dot column, dot name. I don't know.\u003C\u002Fp>\u003Cp>Right? So you can pass it yourself. Can we just say we don't allow relations in JSON? I've I've tried that in the past and I think the answer is no. No.\u003C\u002Fp>\u003Cp>I I'm I'm afraid the answer is no for that. Because because that is that's kind of what, you know, that's the situation we're in now. Or, like, we have a relate we have a repeater, and people continuously ask, can we make it relational? And I continuously answer, how is that any different from a one to many? And then oftentimes, you know, we're just sort of in a sort of stalemate, which is, well, it's not JSON, but it does do what it needed to do.\u003C\u002Fp>\u003Cp>So we're good enough. Right? But I think it's fair to say that one of the design requirements of the system would be that we need to have some sort of way where you can relate data from a key that is inside of a JSON document.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I assume we would have\u003C\u002Fp>\u003Cp>Speaker 0: to keep\u003C\u002Fp>\u003Cp>Speaker 2: track of it. We've\u003C\u002Fp>\u003Cp>Speaker 0: talked about this for the editor JS. Right? And or WYSIWYG being able to have great relational data structure that's stored inside there. There's actually a relationship to another\u003C\u002Fp>\u003Cp>Speaker 2: Right.\u003C\u002Fp>\u003Cp>Speaker 0: This object. Right? Another direct to side of them. So we we've we are thinking about this. We have some ideas around this.\u003C\u002Fp>\u003Cp>I think it's separate from this particular request. I think right now, this request is primarily around filtering and searching and finding\u003C\u002Fp>\u003Cp>Speaker 1: It's true. Yeah. Yeah. Because because for the record, this really quickly turned into how to just properly do JSON indirect this rather than how to filter through an object. All all very much, you can see to that.\u003C\u002Fp>\u003Cp>Speaker 0: Still important. It's it's part of the the divergence, the comeback to convergence. But, I just as as you guys have continued to talk about this, I think there there is a separate kind of feature function likely for relationals versus the ability to filter and just find stuff inside these objects and do it really and performantly.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. The the WYSIWYG one is an interesting example. That's that's a good point because this everything that we've been discussing so far is basically gonna be the underlying engine slash foundational stuff for that type of use case. Right? Where in a in an editor JS type of environment, the line of text kinda like in like a notion docket, for for those unaware of editor JS.\u003C\u002Fp>\u003Cp>Each line of text is its own, you know, block effectively, and you have blocks of different types. But then a block could be a relationship elsewhere. Right? Which is a great example of this. Like, how do you know what that relationship is?\u003C\u002Fp>\u003Cp>In I'm pretty sure in in, you know, a Notion like environment or an Energy. Js type environment where you have, you know, a semi structured data, part of the solution there is that that joint information could be in the block, right, where you say you have an image block. And because you have an image block, you now know to look at images for looking up the related value. Right? But, again, that depends on the schema that is in your JSON field.\u003C\u002Fp>\u003Cp>And if we were to make that a requirement where we're saying, well, there's a there's a a god. Way to go, Brian. If we if we're saying that there that has to be in the JSON field and now we're making the form of proprietary again. Right? Because now we're requiring your JSON object to adhere to a certain spec.\u003C\u002Fp>\u003Cp>We designed just to be able to do relationships. Tricky. For a second, I was I thought you were gonna write down, this will be tricky. Rest rest assured. This will be very difficult.\u003C\u002Fp>\u003Cp>Is there not a normal standard for that already? Yeah. You'd hope so, but the answer is no.\u003C\u002Fp>\u003Cp>Speaker 0: We gotta You know, there are some other CMS vendors that do this, but, again, they've got more, I think, generally well defined kind of hearts hardened structures. The ability to be agnostic is where this becomes very difficult.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. No. That's a great point because we\u003C\u002Fp>\u003Cp>Speaker 0: to say, this is the data model you must use. Therefore, this is I I can therefore rely on the fact that my structures are gonna match. But I think, potentially, with some of the discussion we had on the schema piece, you know, as part of this entire discussion here, if you were able to say, I am predefining schema. I know what that is, and then I wanna have enforcement validation and understanding that that's there. I think you we could ag you know, the agnosticity is you define the schema or we define a schema, but it's generic in the sense of you're not required to have it.\u003C\u002Fp>\u003Cp>You have a JSON field that's unstructured and doesn't have schema. But if you choose to implement that, then you are actually then getting the benefits of now I can I can kind of quote guarantee as much as you can guarantee in a JSON world? And I know what my data structure is, and I know how to how to interact with that event.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. To be frank with this, it's just very, you know, very, very difficult. I mean, we we could, you know, like, just, willy nilly, we could keep track of, some keys in a new table that says, hey. This relates to that, like, similar to, like, relations, like we do it right now. And, well, try to keep that instinct.\u003C\u002Fp>\u003Cp>It's well,\u003C\u002Fp>\u003Cp>Speaker 1: So when you're talking about the under, other vendor use case, right, I think for the majority there, it's basically around the idea that you have a globally unique ID. Right? So it's very document database first approach. If you've ever created a so we we use Notion a lot. Right?\u003C\u002Fp>\u003Cp>So if you ever create a Notion page that links to another Notion page, that is just a globally unique ID. So therefore, you can nest it however you want and it knows because everything is a page at all times and every page has a globally unique ID. So therefore, that sort of relationship lookup kind of solves itself. Right? Because you don't need to know what collection or what table to look at to find the item because it's all globally unique.\u003C\u002Fp>\u003Cp>Right? The difficulty here lies in the that hybrid model where you want to nest a relational record inside of a document, which itself might be, you know, related from a relational record relational row. Oh, do we like to make things difficult? But that's why it's cool. Let's see.\u003C\u002Fp>\u003Cp>There is a portable text spec. Absolutely. Which, again, I think it would be great to have a some sort of, you know, portable text interface or, an extension that handles that specific format. But I I it would be a shame if if this everything that we're talking about only works if you're in a specific format. Right?\u003C\u002Fp>\u003Cp>Because who are we to say what the right format is?\u003C\u002Fp>\u003Cp>Speaker 2: I agree. I just looked at the, audible text one. Yeah. It look looks very, very similar to how everybody else does it. You know?\u003C\u002Fp>\u003Cp>You have some things. You define your type, and then you have some keys depending on that type. Great.\u003C\u002Fp>\u003Cp>Speaker 1: Cool. Alright. We're quickly reaching the top of the hour here. It was it was a good idea to only focus on one topic for these sessions. Just based on prior experience, it always turns into this should be quick and then you you talk about it for an hour.\u003C\u002Fp>\u003Cp>Just to give a chance to chat, any other thoughts, questions, concerns, ideas, shout outs. I wanna say hi to the family at home. This is the time.\u003C\u002Fp>\u003Cp>Speaker 2: Or when they start typing.\u003C\u002Fp>\u003Cp>Speaker 1: Everyone is typing. Config is code. Uh-oh. I'm pretty sure we have a separate one of these dedicated to that again coming up. Ship it.\u003C\u002Fp>\u003Cp>Speaker 2: Ship it? I'm not gonna I'm not gonna say it right now.\u003C\u002Fp>\u003Cp>Speaker 1: Let's go. So for 20 minutes for brings everyone here. Same. Yeah. The next one's gonna be Comic It's Code.\u003C\u002Fp>\u003Cp>Speaker 2: Cool. Cool. Cool. Cool.\u003C\u002Fp>\u003Cp>Speaker 1: Cool. So make sure to come back for that one, saucy, saucy. Cool. Well, if there's no other questions, thoughts, feelings, or concerns, let's wrap this one up. It's Brian just said the conversation about config as code is is in 2 weeks, not the release.\u003C\u002Fp>\u003Cp>I think this is a very, very, very important\u003C\u002Fp>\u003Cp>Speaker 0: Thank you, Brian.\u003C\u002Fp>\u003Cp>Speaker 1: Thank you, Brian.\u003C\u002Fp>\u003Cp>Speaker 2: It's Android. So it's it's imported immediately. Go now. What have we done?\u003C\u002Fp>\u003Cp>Speaker 1: Cool. Well, all that being said, everybody. Thank you so much for joining us here live. Thank you so much for watching this at home. If you're seeing it on Directus TV, for the people live here, if you don't know what that is, check it out.\u003C\u002Fp>\u003Cp>Directus dot a 0\u002F tv. I think I got that right. Right? It'll be up in about a week's time. But for now, I wanna say thank you all.\u003C\u002Fp>\u003Cp>Good luck in Godspeed. And we'll see you in 2 weeks.\u003C\u002Fp>","Welcome, everyone. Happy 2024. We're excited for a new year. We're gonna try out a new well, we've been we've been playing around with this request for views format for a little while, but we're gonna we're gonna try out just covering a specific feature, talking through some details, making sure that we have a full specification on it, and getting additional community feedback, as needed. Today's topic will be JSON object filtering. It's been a it's a very, very popular request that we see quite frequently, and we would love to ensure that we solve this problem correctly and get it working for the databases that support it. I think one of the key issues that we've run into we've done a lot of development work on this, but what we run into is various database vendors support this differently. You fix it to support that thing. It breaks something else. We spent a lot of time kind of iterating back and forth on this feature, and I'll let Rai talk about some of that. But I mean, fundamentally, we we know that this is important. We consider it kind of a critical road map item, and that's the reason we're gonna talk in detail about it today. Absolutely. Well, thank you for that. Yeah. And as you can see, I was the one who opened this feature request conveniently with no details whatsoever. I basically just say, hey, man. I wanna be able to filter in JSON objects. Good luck. And then everybody was like, yep. Me too. However, you know, the this this was done way before we had that RFC format, so the the the details are lacking. Let's let's call it that. So before we dive in too deeply, you know, Daniel, I'm just gonna throw you under the bus here. You wanna walk us through what what even is JSON filtering in the first place? JSON filtering can be quite useful if you store JSON inside of your database, and you need to check some field inside of that JSON in your database. Like, we said in the beginning, right, not every database supports this as of right now, which forces us or will force us to, you know, do some little Directus magic as usual. But more and more databases kinda jump on the train, which is pretty neat. I think as what was it? SQLite very recently also announced that they have JSON b support, I think. I hope I'm not miss remembering. But, yeah, can be quite useful. If you have stored JSON, then you can filter on fields inside of that JSON. That's very useful. And I think as as Kevin points out, actually, a couple of our default interface configuration our field configurations actually store in JSON by default. You can change them in most cases, but and, currently, that's the recommendation. So off the support side of things currently, for those of you listening, if you use the CSV format instead, on the database field setting side, you can then filter through the application in the API on those fields. But JSON is a little bit nicer structured format, so the reason for the request. Yeah. And then the other the other big sort of elephant in the room there, of course, is that a lot of systems are utilizing and or could be utilizing more of a document style structure rather than, you know, a tabular style data structure. Especially, you know, when you have flexible schemas or unknown, you know, data structures ahead of time or semi semi structured data. Think about, you know, blocks on a page or something. You know, you're talking about sort of rich data that may or may not be structured like a table. So, therefore, you know, storing JSON in a Postgres database or another SQL database can give you some of that document magic without having to switch over completely to a document database. So there's a lot of a lot of benefits to having this. Now that being said, it also comes with a ton of complexities. So in my original feature request I was really thinking about it sort of as a filter against the data. Right? So similar to how we have, some functions to run against date values. I don't know if you've seen those before, but we have things like extract the year from a timestamp as a function. I was thinking about it the same way in my original feature request. Right? So I was thinking maybe we can have just like you would be able to do, like, year time stamps equals 2024, you could do something like, I don't know, Jason, name of the field, and then some sort of identifier string to select something from that field and then run filters against it, against that value that you've now selected. Right? Similarly you'd be able to use that in fields or in sort or other pieces like that And we actually put a lot of work in that already, and by we, I mean Tim who's in the chat, and I'm definitely gonna put him on stage and put him in the spotlight. Jonathan, if you wanna pull that up real quick, it might actually be fun to take a quick scroll through. Also, as a way to answer that, do all DBs even support it? Because there's a wonderful table at the top of that pool request. If you wanna pull up the, don't bring him up. We're recording. He's he's a little camera shy, but we can we can figure it out. You know the PR off the top of your head, or do I just need to go find it? Oh, just just open up pull requests, and if you search for Jason, it should be the only one that's there. GitHub pull requests. We've left it open intentionally. We'll we'll circle back to that later how we're handling now. But if you just probably all the way at the bottom is is a little older. Yep. There it is. Alright. There's your matrix. Right. So in this PR, we basically took a swing at implementing that, you know, JSON filtering the way it was described in that discussion. And by described, I mean, vaguely hinted at, because the description was a little poor. But we did implement it in that way, and it does actually work. So we do have, you know, queries for a little different databases. But as you can see in this table to the Chet's point earlier, not everything is supported everywhere, which becomes tricky immediately. Right? Because how do you then document it? Now it's gonna be database different. So there has been, you know, implemented some fallback support so you can see the difference in that green check versus the, that sort of Unicode check I guess. You you can see that for a lot of these things we're actually you know having to do some direct us magic to to make that work against the database which is not necessarily gonna be the most performant or the what's the right word? The the the quote, unquote right way to do it. Right? It's a blue check mark. Oh, if you're on Windows, just pretend. If you're on Linux or Linux, just Linux, just pretend. The check marks are green. Anyways, while building this, if you might wanna pull up the file section of this PR, Jonathan, I think it's a fun fun scroll through. This if we go all the way to the top, there's a tab. Files changed. 2000 editions. Oh my god. Yeah. There's there's only a 1,000 lines across 50 files. The long the long story short is that in the database helpers, if you see in that left hand sidebar, you know, we have to add all of the additional queries for JSON filtering ourselves for a little different database types. Sort of hard coded in, You see them here in the dialects if you just click one of those. It doesn't really matter which one. So all of those sort of you can see them here. You know, we have a JSON extract for MariaDB that may or may not exist across the other database vendors. Right? So it's not a SQL standard, which makes this a heck of a lot more complicated than you'd think. It's because every database does it differently. I believe it was Postgres that doesn't rely on functions even. It has like a special syntax with like, builders and arrows and whatnot. Which is pretty interesting. I don't know if the voice first 12 one has that in there. But anyways, there's there's, like oh, maybe this that's the one with the question marks there. I don't know. There's there's all sorts of different syntaxes is what I'm trying to say. Oh, here they are drawing with the arrow the the dash arrow arrow. That kind of stuff. So what we realized with this sort of initial work that we did on the JSON filtering is that, well, 8, super complicated as you can tell. There's a lot of additional work and a lot of additional logic, which means in turn you're gonna maintain. As always, because the more stuff you add, the harder it becomes to maintain. Right? But, the third thing is we're also starting to we also start to wonder like, okay. Instead of, having it as a function style thing in the query parameter, what if we do it more like the fields parameter itself? Right? Where you can just say, like in GraphQL, for example, you could just provide a nested tree that you wanna select instead of having to do it through a filter attribute. And at the same time, we've been working on a new data abstraction engine in the first place that already sort of, like it doesn't necessarily fix the fact that we have to do a lot of stuff ourselves for every database because it's just database specific, but it does it in a way that is designed to have database specific drivers rather than dialect specific overwrites. Right? So this is gonna be a bit of a more of a deep dive, but the way the API is set up right now is that everything effectively goes through connects, right? The SQL, what do you call it? SQL query builder that basically everybody and their mom is using. And what that means is that first it just becomes a sort of generic SQL, and then at the very end it's translated for the individual vendors. Right? So it's effectively this is a very crude explanation, but it's effectively just doing a find and replace for the quotes, making sure it's, like, the right quote for the right database. And then for some of the databases there's a little bit of additional magic, like for SQLite, you know, for an ALTER TABLE statement there's some magic included there. But the long story short is to add stuff like JSON filtering, it becomes tricky because now it's database specific. Right? So we don't really have a way and it was the same with the timestamp, helpers that we did earlier. We don't really have a way to make that agnostic across all of the different database vendors. Right? The second part there is that we know that we wanna support more database vendors over time, not less. So trying to do it in this sort of like make it generic first, add 1 by 1 overwrites to the dialects. It doesn't really scale anymore. Right? But end of the versions. A very good point, Tim. Because I think if we go back into the table, we already saw it. There's, like, differences between MySQL 5 and MySQL 8 plus, although 5 is now end of life. So that's a whole different discussion. But, we're the same for, you know, Postgres 10 versus 13 and up. So the way we're sort of re architecting that piece is by saying, you know, there is a singular, there's still a singular data entry point, but rather than relying on SQL, it relies on an abstract syntax tree, you know, Veron Design. Just a proprietary data format that explains to the engine what the data looks like that we wanna fetch. Right? And then for each of the different vendors, we're gonna have a driver that interprets that command and then just executes it in whatever way is appropriate for that driver. Right? So for a lot of the SQL drivers, we can still share a lot of that SQL magic like we're doing now. But it also means that we can start opening up the doors to other, you know, other data sources and other, JSON specific data sources. Right? So one of the reasons there, to keep going on that train of thought, is also because once we started getting super deep into JSON filtering, we also started to realize, like, well, if we implement the JSON filtering like field selection rather than, filtering specific functions, you end up with effectively just drivers for a document data store. Right? Which is very interesting. So with that in mind, we could also start thinking about what does it look like to use something like a DynamoDB or MongoDB or some other sort of key value slash unstructured document style store with the sort of direct to the API. Importantly, not treating them as a relational database, but treating them as a document data store, like leaning into the flexibility of a document database rather than trying to force them into a relational structure, right, like we've seen seen in the past and have explicitly avoided. That was a very long train of thought. But what I'm trying to say is, what we're getting at with that is that we've effectively not shelved, but we've sort of put the pause on this particular PR. And instead, focusing now on implementing this sort of JSON selection support directly into this driver based approach. Right? Because the one thing we didn't wanna do is add a lot of complexity add more complexity now only to then replace it, you know, in a couple of months with another breaking chain with completely different structure. But we've left open the PR because we definitely don't wanna lose any of work in any of the code because it is being, you know, repurposed repurposed into the new structure. In the chat, there would all be called features, not bugs. That's very true. Any bug in SQL is a feature. Got a question about the specs for the drivers. I believe we do plan to fully spec and document those the driver interfaces, right, for the new architecture? Absolutely. Absolutely. Yes. Yep. I mean, as of right now, it's still very much, you know, in in r and d, so we're not opening the doors quite yet on on building your own, but it is built basically as an extension in mind. Right? So we wanna make sure that those things are just do whatever you want as long as you adhere to the spec, you can you can, you know, save and read data from wherever the f you want. But, you know, we're definitely focusing on just feature parity with what we have first, and then expanding the scope there with JSON filtering and some other, you know, additional relationship types. And then, you know, Yeah. Yeah. I love the I love the idea that we're moving towards a an extension driven driver approach. Right? The same way we've done with so many other components of the platform, making it so that it's extensible. And it also means that you've got a custom data source. You've got an API. You've got other things. You'd have a spec and be able to build your own driver against some custom data source that you have, and be able to leverage the API and power of the Directus application on top of that. So very exciting very exciting 2024. Absolutely. Wait for the first person, implementing the, Excel sheet data store. I think this will be a a smash hit in Germany. All of the all of the companies love Excel so much. Oh, Maybe maybe The whole finance world rejoices. Oh. We should start that as a little competition. Whoever builds the Excel data store first gets a shout out on the website. Oh, you bet. Oh, you bet. A signed certificate of insanity by by me. Love that. The the worst thing is the the very worst thing is that people will actually, like, honestly use it, I'm afraid. It's it's not the using it part that that worries me. It's the relying on part that me. Anyhoo, circling back to, more of the the specifics of JSON filtering as a whole. Right? So just to circle circle back to to a requirements list because we we don't have, you know an RFC for JSON filtering proper we don't really have an RFC for what it could look like in new formats. Right? We do know that we want to support it from field selection perspective with that sort of nonstructured data store in mind. We do know that we have to support it as part of filters and sorting and querying and and all of that goods. That being said there is a very interesting difference between JSON objects which is sort of the assumed default that we've been talking about here, and arrays, which is where it gets real complicated real quick. Because one of the main questions or use cases where this is coming from is for fields like tags, right, where you just have a JSON list of individual strings for tags and then how do you search through those. Right? But now you're not so much talking about you need to make a nested selection of an adjacent path and then filter against that. Now you have to now search through each item of the the array. Right? So we do have some specific magic going on for one to many's right now like some and none for example to say I want all of the values in my related table to match x y z But we should probably add something along those lines for Jason when it comes to filtering specifically. I think. This is mostly again, for for those who've who've joined us in the past on these live sessions, for Rich, welcome back, the the goal is oftentimes, you know, to really, really diverge to find what are the boundaries of what we can or eventually maybe want to do with this, you know, and then converge back into what is realistic and what does that first MVP look like. This is really that diversion thinking stage. Right? It's like, how how far do we need to go when it comes to filtering on stored values? For example, do you need to be able to say things like, I want all of the JSON objects into an array to have a nest of property author dot agent. They all need to be bigger than than 12. Right? I've actually never used, like, field type currying like in Postgres or in the others. So I'm curious. Is there a function of the database that tells you whether or not the stored value is an array or not? Like, is there, like, some database layer check that tells you that? Is that possible? I I think the the realistic answer is we can't assume that there is because we're talking about, you know, good plan. Good plan. We're we're talking about various different database vendors, and we're talking about various different SQL like vendors that, you know, are are sort of SQL inspired, but not fully compliant. Think of, you know, the the I wanna say plan and scale, but they recently added foreign keys. So I think they might not be more compliant than they used to be. But those types of vendors, right, where it's like they they use the SQL syntax for basic querying, but they don't have a full SQL engine behind it because they just implemented the data store differently. So the answer is we don't know nor can know. Because we need to build this and design the the specs for this in a way that is sort of agnostic to a data store. Right? Tim was mentioning, you know, we have 3 different pieces of functionality extracting the data in fields, extracting the data from a field, and then using it to filter. And then with the deep filtering against stuff within that JSON blob, filtering inside the j oh, yeah. Yeah. Filtering inside of an area with a deep. Yeah. Yeah. Yeah. So So I think, ideally and this is a bit of a a different way of thinking in how we treat the API because it used to be very, you know, tabular data forward first is I think what we're leaning towards now is really treating JSON values f it as if it's just any other table effectively. Right? So we just treat an object as if it's any other item in the database. We treat an array as if it's any other table in database. And therefore, just allow you to use any of the regular query parameters as if it's a table. But you get some very interesting usage patterns at that point. Right? Could you consider using JSON schema to type and validate field? Absolutely. There's a very interesting thing, though, when it comes to to JSON and and typing slash validating, is that a lot of people sort of accidentally will start using a document database as a relational database, therefore, completely defeating the point of using a document database. Right? And this has been the the main, if not the only argument, that we've had against supporting MongoDB in the past because it has come up before. You know, there have been a couple of feature requests every now and again. But it was always the question of, oh, let's just use MongoDB instead of Postgres. But that really makes no sense if you did pardon my French. It makes no sense if you just if you just think about it real. Right? Because the a document data store has a lot of perks. There's, like, a lot of good things about it. But using it as a relational database is just not what it was designed to do best. So you're kinda just forcing it to do something it wasn't made for. At which point, it's like just use a SQL database. Right? If you wanna have relational data in a tabular format, use the SQL database. That's what they excel at. That's what they're best at. That being said, it's the overlap of the 2. You know? And so overlap of the 2 where it gets really interesting where you know, if you're talking about, things with rich content, like, you have views in an app or pages on the website or something, right, where you have fixed data points, fixed metadata points that you have for everything. You know, you have a title, you have an author, you have a published date, you have some of those pieces, like a status. But then you also have rich content which is gonna be, you know, a semi, semi flexible schema. Right? It's when you mix the 2 where I think it gets really, really interesting. But that does also mean that you need to be able to manage and search through and extract the nonstructured part with a semi known schema. That's where it gets tricky. Right? So, what made me think about that is the question, you know, consider using a JSON schema to validate the fields. If you're strictly validating against one schema of JSON objects, at that point, are you better off with using JSON or should you just make a couple of columns? Right? Because a many to one, field with a related table with structured columns will most likely perform better than trying to do it with nested objects. If, you know, assuming that there's gonna be searching and filtering and and organizing involved. Another question from the chat here was, Postgres allows for date ranges, which is effectively a JSON array. Somebody built a custom date range interface for this but cannot filter against it. That's the question is that something that will be supported by this pr or would that be something else? Yep. That's basically exactly it. You know, you have any sort of arbitrary JSON value you need to be able to manage and search through it. It is a table document for the record, though. Each record in a collection have significantly different content structure. Yes. That is a very good point, which is why the JSON schema again becomes interesting. Right? Because if you have a column that you want to be the same schema for every single row in your database, in your table, then relying on, you know, adjacent schema for validation makes a lot of sense because you want every one of those to be the same. At that point, you know, what becomes the benefit of using JSON over just columns? Right? That those those are sort of the questions that we need to, ask ourselves. Because the other one is like, if you have the because because the nice thing about the JSON thing is that anything could go in there. That's kind of the point. Right? It's like an unstructured data type. So what if you want everything to be different? Or maybe you want it to be one of 5 known different schemas, right, instead of just the same fixed object. I guess with an adjacent schema, you have union types, if I'm not mistaken. But food for thought. It's an interesting one. But it it does, you know, it does raise a different question that we need to write. What about validation? Right? Because, like, we have validation rules for, for regular columns now where you can say, you know, a number needs to be up and greater than something. For JSON fields specifically, we need to come up with something. Right? Using a JSON schema could be very interesting. Maybe a JSON schema for the whole record instead of just the JSON thing field could be interesting. Maybe both. Maybe it's a nested. Here's another another thing. Let's see what was the other chat related things on the same click. Somebody else, asked, I'd have missed this, but the idea is to integrate directives as existing filters at dynamic variables, etcetera, or would there be a new JSON specific functions? So that is a great question and kind of the the, instigator of rehashing this chat because we had this feature request. We sort of we're looking at it through the lens of let's make a specific JSON function like we have the others, and just implement that. Right? And then Tim went ahead and did it. Kudos. A tremendous amount of effort. But we also really started to, then, you know, it's it's kinda what we talked about with the the data abstraction piece just now. We started to realize that it might be way more powerful to try to treat it as an item rather than treat it as a value. So you unlock all of the other query parameters against the value. That answers. It gets it gets a little bit theoretical quick here. Soon as you use it relationally, it becomes even harder to maintain. Good point. Good point. Just started using as a MongoDB as a replacement for Redis for for a project that doesn't use Directis they are crypto so their relational is based on wallets. Makes sense You know, if you think about that use case, you have a semi structured data. You know roughly what goes into a wallet, but there's a lot of optional, fields oftentimes. Depends on the implementation, of course. But one thing you do know is that you have a fixed primary key that you can use for some relationships. Big sport of Jason's schema. Everywhere. Just for the spec Simpler, Tidebox for the win. I that used to be my go to and then a lot of others in in my team sort of using Zod, and I kind of have been converted to the The inference is pretty is not true here. Let's see what You kinda want to know that oh, it disappeared. Where it is? Oh, you kinda wanna know that a list of tags is always a list of strings or that an object matches a specific schema. Depends. I'd I'd say, generally, yes, but also really depends. For example, in to give a system thing as an exam right? For interface options, we don't know ahead of time what's going in there. Right? It's up to the interface to figure out. It's just anything. It's basically just a blank store for an interface to do some sort of options, but it really doesn't matter what the format is, what's in it, etcetera. Somebody shared p g adjacent schema, schema validation for postgres specifically. It's where it gets real tricky. Solution relied on the different filter syntax. JSON pass number to XPath. Yeah. Yep. Exactly. I think the, the the JSON schema example is is a good example of the the added sort of complexity that we're putting ourselves in here on purpose, which is that we're really really aggressively want this to be a standard in the API that you can use no matter what the data source is. And then it just depends on the data source how performant it becomes. Right? But I'd really, really would like to prevent ending up with, you know, a long table in the docs that says, can you use JSON filter? Yes. No. No. No. Yes. Yes. Yes. No. Yes. Yes. Yes. No. For for the different drivers, especially if those drivers are are third party maintained. You know, there's it's gonna be incredibly annoying. It does mean that for some of the data source, we'll definitely have to, we'll definitely have to, what's the right word, add add fallback logic and do a lot of that magic on the director side of things rather than on the data store but so be it You know? I I think that is a a trade off worth having, as long as these drivers can handle it themselves. Because Yeah. Right. We're fine. Scenario. Go ahead. Sorry. The the delay. No. It was, just for Brian's message, which was, the discussion point, my mind towards use cases like page builders, where I wouldn't need to create a ton of separate tables. That's true. And this is exactly the use case where I, in the past have used. You know, just sometimes if you very quickly want to throw together a component, but you don't really don't wanna create, like, different relationships and whatever. So you just quickly make, like, an h ref with the label and slap it together as adjacent, for example, very quickly, you know. That's that's a very nice use case for that. Yeah. Very quickly, very easy. Mhmm. For sure. And I I agree. Another interesting thing that he's mentioning there is saying that it would be better served by a NoSQL database. Right? That's where the interesting discussion really starts. Because if you're talking about, you know, a page builder for a website, then the pages themselves, just like what is the route? What is the title? What is some of the metadata? I'd argue that's probably better for a SQL database. Right? Because you're talking about structured data that you wanna query through on a column by column basis. You wanna say get all articles from, you know, January 2024, for example. That is gonna be faster in, you know, a relational database because it has, you know, structured data with known types and you could search through them efficiently. But the content part of a page, absolutely, it makes a lot of sense to have that as a page builder. Right? Because you have semi structured data. You have an array with 1 or more or 0 or more, I guess, you know, blocks of a known type. So that's where that JSON validation with schema comes in potentially. You know? And then that's the data is semi structured. So you have objects of, like, x different types, and then that has an array of those things. Yeah. So that makes a ton of sense. But I think the real power is the combination of the 2. Because if you were to try to do all of those pages in MongoDB, now you have to trade off of the sort of filtering performance and sort of the searchability aspect of it. But on the SQL side right now, you have the penalty of not having that unstructuredness of the data. Right? So right now, we would rely on any to any's for that type of use case, which has its own benefits around, you know, the searchability and the joinability. But, you know, if you're on pages where you don't really have to reuse, sections, for example, then, yeah, I agree. You're doing it as a JSON object for for a page builder makes a lot of sense. Right? But implementing it in a way where you get both is really the Oh, this this is why Brian, the father of, agent c OS, He he felt that pain probably, so he came up with this. Yeah. Yeah. But I agree wholeheartedly. Like, this came up exactly when I wanted to do then. If you have, like, one off components that you don't really, you know, they don't have something to do with other stuff or you want to get some data in, it's very, very, very neat. The repeater interface is very useful for that. Now there's one additional topic that we haven't touched on that I think we can easily spend 15 minutes on. And Brian is hinting at it right now, actually. Relationships. Relationships is where it gets real tricky. Right? Because right now, we know that's, article dot author is a foreign key to author's table. Right? So that that metadata is known. We know that ahead of time. So therefore, we can do things like give me everything, nest everything, nest everything, you know, start up start dot star whatever. And it knows what to nest because it knows what the relationships are. Right? When you're in within a JSON document or an area of JSON documents, there's really no guarantee that something is an ID or there's no real knowledge of what that path is and where it points. Right? So once you want to start nesting, data like that, it becomes tricky, tricky, tricky. Right? So within a a MongoDB context, if I'm not mistaken, it's like a MongoDB ID is always globally unique. Right? Right? So as soon as you have a nested data of that, I what what did they call it again? There was, like, a Mongo ID special type. I forgot what it's what it's called. Object ID is I think what they call the type. Anyways, when it encounters a value of type object ID, it it can assume that it's a globally unique ID and therefore knows what the what document to fetch. But in a sort of hybrid model, you don't necessarily have that luxury. Right? Because you can have a nested, categories array in your adjacent object that just says 123 with the assumption that you're talking about rows 123 in your categories table. Right? Because you're mixing concepts, because there's no such thing as globally unique IDs in a relational database. Kind of a design. I'm already imagining some type of custom format with, like, a dollar sign, direct to dot one or something, and then we have to translate it. And oh, no. It's a whole repertoire. We could do this. The same way as the chat, apparently. Somebody is saying direct us could prefix it. That's what I meant. Oh, yeah. We could. We could. But it does mean that we would have to make the data in your tables proprietary, which is something that I'm personally not a fan of. Right? It's like the system tables are one thing, because, of course, you need to have some metadata saved somewhere and we need, you know, a place to store that. But I really, really badly, desperately do not want to introduce any sort of direct to specific proprietary nonsense into your own database, in the user tables rather. So that's a tricky one. Right? So If we would do that, then we would probably also have to provide a way to extract the data, and it's it's more and more patches on top of patches. I don't think we wanna go down that road. The question really becomes like, how do you pull together a join? Right? And it's the same the same reason we haven't really, because Brian said relational repeater. Right? The reason why our repeater interface currently is not relational is because of this exact reason. It's like, how do we store the information of, you know, what field is relational to what? And the other tricky thing is, you know, without some real clever engineering from John and Nicklaus, most likely, There's no guaranteed way you can really do a join, based on the nested JSON value. Like some databases would, some databases don't. You know, it's the same sort of story. Right? So it becomes a performance nightmare. But what are some of our options there? Right? Just thinking out loud. One would be that we have some sort of, kinda like direct directors relations. Right? Where we're saying, well, if your JSON object has a path called, I don't know, article dot author, it we're assuming it's a foreign key, but that is always gonna be, you know, you can't really guarantee that. Right? An additional option would be to allow, you know, the end user to sort of pass the join information, right, manually through the API, where instead of saying author dot name where we know that author is a relationship, maybe there's some sort of syntax where you can just say author joins on table dot column, dot name. I don't know. Right? So you can pass it yourself. Can we just say we don't allow relations in JSON? I've I've tried that in the past and I think the answer is no. No. I I'm I'm afraid the answer is no for that. Because because that is that's kind of what, you know, that's the situation we're in now. Or, like, we have a relate we have a repeater, and people continuously ask, can we make it relational? And I continuously answer, how is that any different from a one to many? And then oftentimes, you know, we're just sort of in a sort of stalemate, which is, well, it's not JSON, but it does do what it needed to do. So we're good enough. Right? But I think it's fair to say that one of the design requirements of the system would be that we need to have some sort of way where you can relate data from a key that is inside of a JSON document. Yeah. I assume we would have to keep track of it. We've talked about this for the editor JS. Right? And or WYSIWYG being able to have great relational data structure that's stored inside there. There's actually a relationship to another Right. This object. Right? Another direct to side of them. So we we've we are thinking about this. We have some ideas around this. I think it's separate from this particular request. I think right now, this request is primarily around filtering and searching and finding It's true. Yeah. Yeah. Because because for the record, this really quickly turned into how to just properly do JSON indirect this rather than how to filter through an object. All all very much, you can see to that. Still important. It's it's part of the the divergence, the comeback to convergence. But, I just as as you guys have continued to talk about this, I think there there is a separate kind of feature function likely for relationals versus the ability to filter and just find stuff inside these objects and do it really and performantly. Yeah. The the WYSIWYG one is an interesting example. That's that's a good point because this everything that we've been discussing so far is basically gonna be the underlying engine slash foundational stuff for that type of use case. Right? Where in a in an editor JS type of environment, the line of text kinda like in like a notion docket, for for those unaware of editor JS. Each line of text is its own, you know, block effectively, and you have blocks of different types. But then a block could be a relationship elsewhere. Right? Which is a great example of this. Like, how do you know what that relationship is? In I'm pretty sure in in, you know, a Notion like environment or an Energy. Js type environment where you have, you know, a semi structured data, part of the solution there is that that joint information could be in the block, right, where you say you have an image block. And because you have an image block, you now know to look at images for looking up the related value. Right? But, again, that depends on the schema that is in your JSON field. And if we were to make that a requirement where we're saying, well, there's a there's a a god. Way to go, Brian. If we if we're saying that there that has to be in the JSON field and now we're making the form of proprietary again. Right? Because now we're requiring your JSON object to adhere to a certain spec. We designed just to be able to do relationships. Tricky. For a second, I was I thought you were gonna write down, this will be tricky. Rest rest assured. This will be very difficult. Is there not a normal standard for that already? Yeah. You'd hope so, but the answer is no. We gotta You know, there are some other CMS vendors that do this, but, again, they've got more, I think, generally well defined kind of hearts hardened structures. The ability to be agnostic is where this becomes very difficult. Yeah. No. That's a great point because we to say, this is the data model you must use. Therefore, this is I I can therefore rely on the fact that my structures are gonna match. But I think, potentially, with some of the discussion we had on the schema piece, you know, as part of this entire discussion here, if you were able to say, I am predefining schema. I know what that is, and then I wanna have enforcement validation and understanding that that's there. I think you we could ag you know, the agnosticity is you define the schema or we define a schema, but it's generic in the sense of you're not required to have it. You have a JSON field that's unstructured and doesn't have schema. But if you choose to implement that, then you are actually then getting the benefits of now I can I can kind of quote guarantee as much as you can guarantee in a JSON world? And I know what my data structure is, and I know how to how to interact with that event. Yeah. To be frank with this, it's just very, you know, very, very difficult. I mean, we we could, you know, like, just, willy nilly, we could keep track of, some keys in a new table that says, hey. This relates to that, like, similar to, like, relations, like we do it right now. And, well, try to keep that instinct. It's well, So when you're talking about the under, other vendor use case, right, I think for the majority there, it's basically around the idea that you have a globally unique ID. Right? So it's very document database first approach. If you've ever created a so we we use Notion a lot. Right? So if you ever create a Notion page that links to another Notion page, that is just a globally unique ID. So therefore, you can nest it however you want and it knows because everything is a page at all times and every page has a globally unique ID. So therefore, that sort of relationship lookup kind of solves itself. Right? Because you don't need to know what collection or what table to look at to find the item because it's all globally unique. Right? The difficulty here lies in the that hybrid model where you want to nest a relational record inside of a document, which itself might be, you know, related from a relational record relational row. Oh, do we like to make things difficult? But that's why it's cool. Let's see. There is a portable text spec. Absolutely. Which, again, I think it would be great to have a some sort of, you know, portable text interface or, an extension that handles that specific format. But I I it would be a shame if if this everything that we're talking about only works if you're in a specific format. Right? Because who are we to say what the right format is? I agree. I just looked at the, audible text one. Yeah. It look looks very, very similar to how everybody else does it. You know? You have some things. You define your type, and then you have some keys depending on that type. Great. Cool. Alright. We're quickly reaching the top of the hour here. It was it was a good idea to only focus on one topic for these sessions. Just based on prior experience, it always turns into this should be quick and then you you talk about it for an hour. Just to give a chance to chat, any other thoughts, questions, concerns, ideas, shout outs. I wanna say hi to the family at home. This is the time. Or when they start typing. Everyone is typing. Config is code. Uh-oh. I'm pretty sure we have a separate one of these dedicated to that again coming up. Ship it. Ship it? I'm not gonna I'm not gonna say it right now. Let's go. So for 20 minutes for brings everyone here. Same. Yeah. The next one's gonna be Comic It's Code. Cool. Cool. Cool. Cool. Cool. So make sure to come back for that one, saucy, saucy. Cool. Well, if there's no other questions, thoughts, feelings, or concerns, let's wrap this one up. It's Brian just said the conversation about config as code is is in 2 weeks, not the release. I think this is a very, very, very important Thank you, Brian. Thank you, Brian. It's Android. So it's it's imported immediately. Go now. What have we done? Cool. Well, all that being said, everybody. Thank you so much for joining us here live. Thank you so much for watching this at home. If you're seeing it on Directus TV, for the people live here, if you don't know what that is, check it out. Directus dot a 0\u002F tv. I think I got that right. Right? It'll be up in about a week's time. But for now, I wanna say thank you all. Good luck in Godspeed. And we'll see you in 2 weeks.",[5161,5162,5163],"3d6f534d-6fd7-436e-b2ce-eb4b743eeb0c","21d00b4f-2a61-4cff-b024-cde2a0640a2b","dc94af4a-d84f-440c-bd93-7734aa6aede7",[],{"year":978,"number":11,"id":1787,"episodes":5166,"show":5167},[1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1768,1800],{"title":1802,"slug":1803},{"id":4799,"slug":5169,"vimeo_id":5170,"description":5171,"tile":5172,"length":13,"resources":12,"people":12,"episode_number":11,"published":5173,"title":5174,"video_transcript_html":5175,"video_transcript_text":5176,"content":12,"seo":12,"status":19,"episode_people":5177,"recommendations":5178,"season":5179},"functino","891433886","An amusing tale of a typo turned international affair.","0d23f403-a3aa-40d1-9f00-0111e29184f8","2024-01-17","Functino","\u003Cp>Speaker 0: And now Dev Thoughts by Jack Rabbit. During a code review, my colleague pointed out that I misspelled function as functino. I told him, it's not a typo, it's Italian. And now, all my code comments are in Italian.\u003C\u002Fp>","And now Dev Thoughts by Jack Rabbit. During a code review, my colleague pointed out that I misspelled function as functino. I told him, it's not a typo, it's Italian. And now, all my code comments are in Italian.",[],[],{"year":2380,"number":159,"id":4790,"episodes":5180,"show":5181},[4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803,4804,4805,4779],{"title":3380,"slug":3381},{"id":4800,"slug":5183,"vimeo_id":5184,"description":5185,"tile":5186,"length":13,"resources":12,"people":12,"episode_number":527,"published":5173,"title":5187,"video_transcript_html":5188,"video_transcript_text":5189,"content":12,"seo":12,"status":19,"episode_people":5190,"recommendations":5191,"season":5192},"roi","891432924","On today's episode of the \"Dude, Where's My Project?\", we search for the ever-elusive return on investment. ","7d57271d-e8b7-41d6-8a37-c88da78d6ab0","ROI","\u003Cp>Speaker 0: And now Dev Thoughts by Jackrabbit. They say the mythical 10x developer is rare, but I don't believe that. I have an entire team full of 10x developers. For our last project, they decided it was best to build our own CMS instead of buying one. It took 10 times as long and cost 10 times as much.\u003C\u002Fp>\u003Cp>When I told my boss that 10x times 10x equaled 100x ROI on the project, he was so happy. I had never seen him sweat, cry, and curse at the same time.\u003C\u002Fp>","And now Dev Thoughts by Jackrabbit. They say the mythical 10x developer is rare, but I don't believe that. I have an entire team full of 10x developers. For our last project, they decided it was best to build our own CMS instead of buying one. It took 10 times as long and cost 10 times as much. When I told my boss that 10x times 10x equaled 100x ROI on the project, he was so happy. I had never seen him sweat, cry, and curse at the same time.",[],[],{"year":2380,"number":159,"id":4790,"episodes":5193,"show":5194},[4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803,4804,4805,4779],{"title":3380,"slug":3381},{"id":4684,"slug":5196,"vimeo_id":5197,"description":5198,"tile":5199,"length":2745,"resources":12,"people":5200,"episode_number":264,"published":5205,"title":5206,"video_transcript_html":5207,"video_transcript_text":5208,"content":12,"seo":12,"status":19,"episode_people":5209,"recommendations":5212,"season":5213},"ai-devs","893669889","Kevin is joined by Rizèl to learn how developers REALLY use AI in their day-to-day work, including ChatGPT and GitHub Copilot.","d7be166a-f383-4ab5-8399-fb2c2a3bf4ad",[5201,5204],{"name":5202,"url":5203},"Rizèl Scarlett","https:\u002F\u002Ftwitter.com\u002Fblackgirlbytes",{"name":2820,"url":2821},"2024-01-16","Generative AI For Devs with Rizèl ","\u003Cp>Speaker 0: Hello there. My name is Kevin, and welcome to learning things I love to hate. In this show, I stop avoiding technologies that I've been avoiding for one reason or another by inviting my friends and colleagues to teach me more about them. And today, my dear friend, Roselle, hello. Would you like to introduce yourself?\u003C\u002Fp>\u003Cp>Speaker 1: Hello. Yes. My name is Roselle Scarlett. I'm a staff developer advocate at a company called TUVD, which stands for to be decentralized, not to be determined. And\u003C\u002Fp>\u003Cp>Speaker 0: That's the first time I knew that.\u003C\u002Fp>\u003Cp>Speaker 1: I always was like, when's the company name coming? No. I was I was the same. Even, like, I was at the company, and I'm like, why why is it called TBD? I didn't wanna say anything until I heard someone say what it what it stands for.\u003C\u002Fp>\u003Cp>Speaker 0: So you went for to be decentralized?\u003C\u002Fp>\u003Cp>Speaker 1: Yes. To be decentralized. What else about me? Before that, I worked at GitHub as a developer advocate, focused on, like, GitHub Copilot things. And, besides that, I just I like to use social media a lot and watch TV.\u003C\u002Fp>\u003Cp>Speaker 0: Good. And today's topic is alluding to your previous role. You kind of, have provided the reason why I've asked you on, is about using AI as developers. So the way I start these episodes is I kinda tell you what I know or what I think I know now, what my experiences or my knowledge is. You firstly are welcome to tell me you got it wrong, but we can use that as a basis.\u003C\u002Fp>\u003Cp>And then my skepticisms and hesitancies, we knew that as a basis to just have a chat until we run out of time. At any point, if you wanna share your screen, feel absolutely free to be equally feel no pressure to. So using AI as developers. AI mostly popularized by OpenAI using the chat GPT interface, but since has permeated seemingly every piece of software that exists. My, GitHub came out with GitHub code pilot some time ago.\u003C\u002Fp>\u003Cp>You will fill in that blank for me. And I used it at the very beginning, like, right at the very, very beginning. And code pilot is a tool which basically allows you to to describe 1, describe what you're trying to achieve with your code, and it will give you a suggestion, by writing it in your code editor. And, also, I believe it can do, like, straight up, like, we will try and auto complete the code you're writing, maybe. In any case, my experience and thoughts around AI are the following.\u003C\u002Fp>\u003Cp>1, I know I've seen some stats, like, hallucination rates or, like, where wrong rates are are dropping, and I truly believe at one point this concern will not be the case. But I simply don't trust it. Right? I don't trust it to give me codes that either is correct or is the right way of doing things. You know?\u003C\u002Fp>\u003Cp>So that's one concern. And the other is having used CodePilot at the very beginning, just the first version of CodePilot before it was a bunch of of separate services, which I don't really know that much about, I found it really annoying. Like, I actually found their interaction really frustrating and in the way to the point where I turned it off because it was in the way. So, that's the kind of basis to one other bit of context that might be useful is I work for a company called Directus. We have a Discord with, like, 11,000 people in it.\u003C\u002Fp>\u003Cp>People drop by when they have problems. And community, including our team, see if we can help people and an increasing amount. And we spoke about this briefly on Twitter. An increasing amount people think our stuff's broken, and the code snippets they share are hallucinations. They never existed.\u003C\u002Fp>\u003Cp>They never even nearly existed. And there's a little bit of a maybe I'm slightly salty because, you know, people think your thing's broken, but, actually, it's not even it's not even a code snippet we wrote ever in any point in time. It's just wrong. And so there's something there around how people perceive AI and what it can do for them at their current stage in their career, their project, whatever. So that's my little brain dump to maybe start.\u003C\u002Fp>\u003Cp>Could you maybe give us a bit of a landscape, anything you know about AI for developers, whether that's GitHub specific, other other tools, whatever?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Right now, what I use for coding with AI is usually GitHub Copilot or ChatTPT. But I do wanna, like, even back up and explain why I like it because you might just be like, oh, just because you worked at GitHub. But I remember and and that's fair. Right?\u003C\u002Fp>\u003Cp>But I remember when I joined GitHub, I did see all of, like, those, like, negative or mixed reviews. There were some people on Twitter being, like, I love it. And then there were some people being, like, I think I joined when it just went out in public beta, in, like, September 2021. And that's when I was like, let me see what this thing is. I'm, like, half skeptical or half curious.\u003C\u002Fp>\u003Cp>And at first, I was, like, this does not work. I hate it. It's, like, kinda like how you were like, it's a Yeah. Way. But as I started to use it more and more, I kinda got used to it and figured out, like, how it works on my workflow.\u003C\u002Fp>\u003Cp>I'm like, okay. Maybe I don't always want it to at first, at one point, when I knew what I was doing, I would, like, turn off GitHub Copilot. And then if I, like, needed help or if there was, like, a pattern like, let's say, I already wrote an API call with a get for a get request, and then I was, like, I did a post request. Maybe now I need to do a patch and delete. I'll get turn GitHub Copilot back on because I'm, like, okay.\u003C\u002Fp>\u003Cp>It it knows kind of, like,\u003C\u002Fp>\u003Cp>Speaker 0: what boiler plating almost.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. Exactly. But then once I started to like, after that, like, I just had it on all the time. And then one thing that makes me passionate about it is that, is the psychological safety aspect.\u003C\u002Fp>\u003Cp>I feel like when I started off as a software engineer back in 2018 or 2019, I don't remember, like, my team was really mean. I was like, they were so mean. I was, like, the only black girl on the team. And, like, it got to the point where I, like, had, like, real life anxiety from my job. Like, I was, like, on the bus on the way home, and I couldn't even stand up because my muscles That's why I think we were, like, kinda, like, discussing or debating or had different points of views because you're like, I don't think it's good for juniors.\u003C\u002Fp>\u003Cp>But I'm like, I do because I wish I had at least something that I can talk back and forth with or, like, share an idea with and, when I was, like, just starting out. But I agree with you. It does sometimes generate wrong code. Or not even sometimes, a lot of times. And I do think that I think there's a part of, like, needed education around that.\u003C\u002Fp>\u003Cp>And, also, another thing on on your topic or on your concern about, like, direct us. One thing that the new company I work at has done is I'm sharing my screen right now. Okay. They trained let me I don't wanna show all my questions, so let me hide that. But they created a plug in.\u003C\u002Fp>\u003Cp>So so for, like, chat gpt4, or, like, I think it's, like, a bit like, a more premium mode. You have plug ins. Right? And they create a plug And\u003C\u002Fp>\u003Cp>Speaker 0: you you, like, feed it. You feed it your corpus of data, your docs. Right? And then you give it you give it some some the custom instructions. I I use chat GPT also.\u003C\u002Fp>\u003Cp>I just don't use it for code. So I understand the concepts. Right? So you give it the custom instructions and tell it who the persona is who will use it, and it'll basically spit out a custom assistant given the context.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And I think GitHub Copilot has a similar thing too called Copilot for docs. And this the fact that they trained, the fact that TBD trained their doc or chat gpt on these docs has been extremely helpful for me. Because I walk in into this job as, like, I'm a staff developer advocate, so I'm expected to know this stuff. But I never heard of Web5 before, like, I even interview for the company, really.\u003C\u002Fp>\u003Cp>I don't know, like, how to do this stuff. And the documentation is, how will I say, I think it's a work in progress. And also, I'm just not a person. I'm I'm\u003C\u002Fp>\u003Cp>Speaker 0: like I I I lead the team that writes our docs. So I think calling things a work in progress is completely fair.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. Good.\u003C\u002Fp>\u003Cp>Speaker 0: No. No. That look that look is like, yeah. I know what you mean.\u003C\u002Fp>\u003Cp>Speaker 1: Because I'm like, I read it, and I'm like, what? Like, there's missing stuff with it's it's still forming. The the product itself is still forming. And I'm also not really a person that reads documentation end to end. Like, that's just not me.\u003C\u002Fp>\u003Cp>Like, I do your\u003C\u002Fp>\u003Cp>Speaker 0: learning style.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's not my learning style.\u003C\u002Fp>\u003Cp>Speaker 0: Good shit. That's fine.\u003C\u002Fp>\u003Cp>Speaker 1: Exactly. So, like, for example, I for me to build an application at this company, I, like, I wanted to jump in. I wanted to, like, be able to be, like, alright. I'm ready to go and, like, you guys don't have to hold my hand or you all don't have to hold my hand. I just been talking to chat gpt and being like, hey.\u003C\u002Fp>\u003Cp>Why is it saying that the rec record dot send is not a function? Here's my code. And, like, it says, okay. It does exist. I'm like, alright.\u003C\u002Fp>\u003Cp>I know that. Like, you're being a little annoying. But then I I just from reading what they said, I was like, okay. I figured out that it needs to be destructured. And I'm like, okay.\u003C\u002Fp>\u003Cp>Sometimes when I refresh my page, this list disappears, and it goes on and tells me, oh, it's probably because of the asynchronous state setting. When you're calling this function, this, like, state variable is not persisting. I'm like, okay. Cool. So, like, I'm able to, like, go back and forth with it and be like, alright.\u003C\u002Fp>\u003Cp>Maybe I didn't think of this perception, or perspective. And I think it's, it's kinda like rubber ducking, I think, because sometimes I'm not I\u003C\u002Fp>\u003Cp>Speaker 0: think I think that's fair. See, I have a few thoughts about what you've just shown me. So the first is Yes. You 1, you made a comment that let's just say the docs are incomplete. Let's just summarize it there or they're not in their state.\u003C\u002Fp>\u003Cp>They're not accurate. Whatever it is, they are not they do not contain the complete information. So, 1, this piece of software is ultimately guessing at the blanks, and I'm not sure I'm comfortable with that. The second is you demonstrated that you have an ability to go, it was being slightly annoying. I I know it was not giving me the right answer.\u003C\u002Fp>\u003Cp>And that is not necessarily a skill everyone has with the immediate counter in my mind of, like, okay. But you and I are experienced developers and, like, I I'm not gatekeeping anyway for myself, but I do have some skill that allows me to assess the correctness that not everyone has. And I wonder if that changes the experience or the perceived experience of a user. And then, and then beyond that, I I get it. It allows you to rubber duck to have that thought process back and forth.\u003C\u002Fp>\u003Cp>But I have those 2 concerns. Garbage in, garbage out. And I'm not saying the docs are garbage, but the docs miss things. So how do you how does it reconcile that? Or, like, have you ever found that it does just make things up?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And\u003C\u002Fp>\u003Cp>Speaker 0: the and the other is yeah, how what do you have any thoughts around people who lack that ability to look at something and go, this isn't right or isn't what I want or it's contextually incorrect or whatever it may be? Yeah. I mean,\u003C\u002Fp>\u003Cp>Speaker 1: exactly remember my skill set as a junior. But I do think it's kind of interesting because okay. When I was a junior, sometimes I would copy and paste stuff in Stack Overflow, and it wouldn't work. And then I would be, like, alright. Well, maybe I need to keep trying and find something else.\u003C\u002Fp>\u003Cp>So I always I I think it's an interesting case that, like, they're copying stuff from AI. They see that it doesn't work, and then they're just, like, well, it doesn't work. I'm, like, usually my even even when I was just learning, I feel like my initial response is, wait. Hold on. Let me try something else.\u003C\u002Fp>\u003Cp>Let me figure out. Because I will go back and tell ChachiPT or GitHub Copilot. I'll be like, this doesn't work. Like, what are you talking about? Yeah.\u003C\u002Fp>\u003Cp>You have this part wrong. Like, I'll usually feed it the information of, like, here's why you're wrong. And then it'll be, like, oh, sorry. And I don't I don't know. I think I've always told people when I taught them GitHub Copilot things, like, when I do talks, I'm like, treat it like you're it's a coworker.\u003C\u002Fp>\u003Cp>Like, you don't trust your coworker's code all the way.\u003C\u002Fp>\u003Cp>Speaker 0: Well, that's the thing. I I think you've made a really interesting point around trust. 1 Yeah. Yeah. But how else were you learning before?\u003C\u002Fp>\u003Cp>Probably through trial and error with code that didn't work. This is just surfacing it in a different way. It's just a different, yeah, way of generating or surfacing or searching that code on the web. And then the other side of it is, sure, what's the alternative? You know, my feelings about docs and AI, people who have hung around in our Discord server will know this is today.\u003C\u002Fp>\u003Cp>And we're recording this at the end of 2023. I don't necessarily think it will come out, you know, very, very swiftly. So just so people know because breakthroughs are happening all the time. I will not present codes to people as an educational resource that is AI generated because the moment you present it to people, you are endorsing it. And it doesn't matter how many caveats you put in.\u003C\u002Fp>\u003Cp>The people's behavior with AI seems to not fully get that it hallucinates. Yeah. And so today, it's just not there. It's just it's just not there. And don't get me wrong.\u003C\u002Fp>\u003Cp>I'm still exploring AI. I'm still thinking about it. I'm still thinking, well, how do we lay the foundation. So when that concern falls away, because it will, we can we can deliver something great in terms of the world of education. I never touch AI generated docs, AI generated answers.\u003C\u002Fp>\u003Cp>I never use chatbot interfaces. I do not trust them.\u003C\u002Fp>\u003Cp>Speaker 1: Interesting. Even if you try them out after, like, that's the part. Because it's not like they're just\u003C\u002Fp>\u003Cp>Speaker 0: But then I but then I am someone who doesn't mind reading documentation. Interesting thing about this is it isn't yeah. It doesn't just have to consume, consume docs. It can consume other, other sources of information around the web, and maybe that's that's kinda interesting. The other the other concern I had, was so, you know, a lot of these trust arguments, I see the counter arguments.\u003C\u002Fp>\u003Cp>It's not the first time I've heard many of them, but it's good to See. Excuse me. It's good to to think about them a little more. The other part of it was the annoyance factor. And I know that even in the world of GitHub, they have released CodePilot is not, just the thing that autofills your code based on comments.\u003C\u002Fp>\u003Cp>It's like an ecosystem of services. And I'm kinda curious, bearing in mind that it doesn't just have to be that interface. It doesn't just have to be chat in a browser. Mhmm. What how else do you use AI in your in your practice as a developer or a developer advocate, outside of what original Copilot looks like?\u003C\u002Fp>\u003Cp>Speaker 1: I use original Copilot. I use Copilot chat, which is, like, chat gpt. But, I'm\u003C\u002Fp>\u003Cp>Speaker 0: contextually aware of your codes and the environment at which it runs.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I'm trying interesting. The I thought I had a Visual Studio example code example. Hold on. There we go.\u003C\u002Fp>\u003Cp>And then, oh, another one I use is within my terminal. So there's, like, Copilot CLI that I use. So sometimes I'll be like, oh, man. I forgot how to, like, do this bash for me. And I don't have the example right now.\u003C\u002Fp>\u003Cp>My bad. But I'll be like, I forgot how to, like, do this or I forgot how to write this script. And then I'll write in natural language what I wanna do, and then GitHub Copilot will give me a suggestion of what I wanna do of what it thinks I wanna do, and it'll give an explanation of what that command is. And then I can say, revise the query. That's not what I want.\u003C\u002Fp>\u003Cp>Or I can be like, yes. That's what I want. Go ahead and run the code. So if I'd be like, actually, I didn't want it to just, like, grab grab all of these JavaScript files or maybe I wanted to remove node modules or something like that, it'll, like, revise the query for me. But I mostly use the comment fee like, writing the comments and then the chats on the side.\u003C\u002Fp>\u003Cp>Speaker 0: Interesting. That's fascinating. That's a really interesting interface. And, of course, I I mean, logically, I know this interface exists. This will this will be one of many tools like this that is contextually aware of your code base.\u003C\u002Fp>\u003Cp>That's another issue with, like, chat g p t, for example, is it's not contextually aware of your code base. It just gives you a\u003C\u002Fp>\u003Cp>Speaker 1: isolated pace. Exactly. It's so annoying. Whereas this is like, oh, use one reference in the index dotjs, from line 22 to 2279 to, write out all of this. Oh, another thing I want to bring up is I mostly use AI to understand code, to be honest, more than, like, literally write out my code.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Could I ask you could I ask you a favor? Could you bump your font size up Oh. 2 or 3 times?\u003C\u002Fp>\u003Cp>Speaker 1: I can't. Let me,\u003C\u002Fp>\u003Cp>Speaker 0: All good. That's good.\u003C\u002Fp>\u003Cp>Speaker 1: Doing right here. Cool. So one thing I use it for is, like, understanding that code. So, let's say, This is I don't know. I I use a lot of AI code even though you don't.\u003C\u002Fp>\u003Cp>Alright. So, oh, wait. Let's say, right, I I want a better understanding of this Web 5 package, which I've lit legitimately done in the past. Right? So I've opened up, like, these like, the the type source or the source definition for the code that they're creating.\u003C\u002Fp>\u003Cp>And I'll, like, highlight all of it and be, like, hey, Copilot chat. Can you explain what's going on in this? How do I sometimes I'll ask you questions, like, how do I, like, create a record or whatever. Let me see if I can just give an example here. If I just say explain.\u003C\u002Fp>\u003Cp>Speaker 0: Like a slash command. Interesting.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. They have slash commands. And okay. It's using all of these references. That's cool.\u003C\u002Fp>\u003Cp>They're working on it even more. But I I mostly use it for explanations because sometimes I'll look at this and I'm like, my brain is hurting. Like, what is this giving me? So once it breaks down all that stuff for me, it gives me more insight. Like, that's that's my main way\u003C\u002Fp>\u003Cp>Speaker 0: of thinking. Say I will say in the skeptics world here, a few things. 1 Yeah. Do do you trust what it says? That's a I mean,\u003C\u002Fp>\u003Cp>Speaker 1: I don't know. Go in half, like, half trusting. And I think that's okay.\u003C\u002Fp>\u003Cp>Speaker 0: My real thought though is this file you've opened up on the right looks very lovingly commented, which is fantastic.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. No. This is a bad example. But yeah.\u003C\u002Fp>\u003Cp>Speaker 0: In terms of power, because all it's doing really is presenting the information that has already been handwritten in a nice way. And so that's that's there's a bit less inference there. It's just a bit more summarizing what's already been\u003C\u002Fp>\u003Cp>Speaker 1: written. Yeah. But for me, I like seeing stuff in this format. I don't know why rather than, like, looking at, like, random I get it.\u003C\u002Fp>\u003Cp>Speaker 0: I get it. I don't know. I don't like reading code comments. Yeah. Yeah.\u003C\u002Fp>\u003Cp>To to understand. Right? It's not my style either. Yeah. So, yeah, that's really interesting.\u003C\u002Fp>\u003Cp>I suppose you can ask follow-up questions. It'll try its best. Interesting. So what have we discovered so far? So we're talking about chat GPT as I as an as an isolated example that we spoke about 2 forms of code pilot.\u003C\u002Fp>\u003Cp>We've spoken about this kind of if you write a comment, it will suggest things to you, which I'm really not a huge fan of the interaction. Though this idea that you can turn it on and off when you just want a bit of input, You know, when there's something meaningful coming up or when you need oh, I need to I don't know. Replace every I need to make this URL safe. Okay. Give me the regex for that.\u003C\u002Fp>\u003Cp>Yeah. Reg reg x. Give me the regex for that. Yeah. And it's something you would just Google.\u003C\u002Fp>\u003Cp>You'd find you'd maybe go to a little generator online. I I do get that. It's interesting turning it on, turning it off. That's fascinating. I may have a think about that.\u003C\u002Fp>\u003Cp>And the second is this chat interface, which is contextually aware of your code base.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: It also allows you to highlight portions and be very specific about the area in which you're asking, but it still understands the context.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So these\u003C\u002Fp>\u003Cp>Speaker 0: are 3 tools used in different ways. Yeah. How else, if at all, does AI kinda come into your your day to day work life, as a developer?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Was that a was\u003C\u002Fp>\u003Cp>Speaker 0: that a little kitty cat?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Sorry about that. That's fine. I don't know why such, like, as if he would understand. I guess another thing and this is something I do a little bit less because I think this this is still a work in progress.\u003C\u002Fp>\u003Cp>But sometimes, like, let's say I'm making a demo app. I wanna, like, just put it out really quickly, but I'm like, maybe I should add some, like, comments or notes to it. I'm, like, I already wrote this all up, and, I'm I'm lazy. So another thing I'll do is, like\u003C\u002Fp>\u003Cp>Speaker 0: lazy. Think we're all lazy developers. There's no shame in that.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So another thing I'll do is, like, I'll highlight it, and maybe I'll, do this where it's, like, generate docs. But what that's really doing is not generating real documentation. It's just generating, like, JS docs. But I found that to be, like, perfect because it doesn't have to think too hard about this.\u003C\u002Fp>\u003Cp>And also think about\u003C\u002Fp>\u003Cp>Speaker 0: the amount of time it would have taken you to write that versus what it just did. And then you just go in and fix it, adjust it, and so on. It's quite small amounts of code, though. You know, it's pretty pretty clean-cut, but it still takes time to do. And there are other generated ways to do it, but this really allows you to do a quick valid Yeah.\u003C\u002Fp>\u003Cp>Manual validation. I think that it that's the thing I keep coming back to though, which is the success of using AI as developers. I believe at this point, I believed at the beginning of our chat and I still believe relies on your ability to validate either through a gut feeling or through testing testing that the output is correct. And not everyone has that skill, but I worry that the way these tools are spoken about by companies that say, hey. We're providing this tool to you.\u003C\u002Fp>\u003Cp>I think there's a bit of a mismatch between between the marketing and reality. And I think that gap is being closed. But I think I've worked for companies in the past that have this tendency where they they don't say, hey. Our goal is to allow x and come with us on the right as we build that. It's we do x, and then they fall short.\u003C\u002Fp>\u003Cp>And I think that's a little bit naughty, and I think that's where a lot of my, hesitation comes from. Because that manual validation, something that today, can you automate? May we'll talk about that in a moment. But, you know, is is often not spoken about. On that note, I have heard about this thing.\u003C\u002Fp>\u003Cp>I forgot what it's I forgot the wording for it. But where you feed output of AI into another AI to try and validate its correctness. And if you do that enough times with enough systems, you can grow your confidence of an answer being correct while being a fully automated setup,\u003C\u002Fp>\u003Cp>Speaker 1: which is kind of I've never heard that. That's cool. But I will say what\u003C\u002Fp>\u003Cp>Speaker 0: it's called.\u003C\u002Fp>\u003Cp>Speaker 1: I agree with you on that part, and I think that's how we both even started the conversation because both of us said companies are, like, amping up AI too much. And then, the, Diane is like, y'all are saying the same thing. So, yeah, I agree with you. And I think I think that's that is a problem I had where it's like we're, like, copilot and all these AI things are gonna solve all your like, double check the work. Because, like, I've used it, like, double check the work because, like, I've used it.\u003C\u002Fp>\u003Cp>I used it the other day. My my company encouraged me to use it to generate test cause we needed to test code snippets in our in our, documentation. But, like, I had to I kept testing the test to make sure they were, like, actually right, not just, like, fake, like, passing.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And, you know, it's also interesting. So how do I use AI? I use AI every day, and I use it for writing, and I use it for image generation for concepts. I'm a believer that, like, all AI output is based on stolen work, and you've never convinced me otherwise.\u003C\u002Fp>\u003Cp>But, but to take the ideas in my head and illustrate what I had in mind, AI is actually fantastic, and then I generally take it to an expert to do properly. You know? It's just a way of of expressing what I mean. Then I use it in terms of writing in 2 ways. 1, to overcome a blank page problem.\u003C\u002Fp>\u003Cp>Like, help me think about a structure of a a a blog post that's trying to have these key goals. You know? It's not that you're gonna generate it for me, but you're gonna help me overcome the, I don't know how to start. Or I've written a thing. I need it to feel or be more like x in terms of tone or length or, well, tone or length.\u003C\u002Fp>\u003Cp>And you can go away and try and do more with that. So I do it for a lot of rewriting. So the I use it in these ways every day and literally every single day. I also use it for, like, kind of what I would call composite queries where I'm like, here's a picture of a back of a complex cooking instruction or like or like, here's a recipe in German. I live in Berlin.\u003C\u002Fp>\u003Cp>My German is not that good still. Here's a recipe in Berlin. Here's a recipe in Berlin. Here's a recipe in German. I've just done x.\u003C\u002Fp>\u003Cp>What do I do next?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Interesting. Because I I like I like that idea of the the back of the box. I don't I don't like it for writing. I hate it for writing.\u003C\u002Fp>\u003Cp>Is it not interesting? Sometimes So it says I'll use it for writing, but when I try to tell it to do it in a specific style, I feel like I've seen chat g p t be like, hey. What's up? I'm like, oh. Sorry.\u003C\u002Fp>\u003Cp>Sorry.\u003C\u002Fp>\u003Cp>Speaker 0: But I had a second point there, which which the answer to that, which is that I almost never will take the output for beating. In the image generation world, it still goes to someone who is able to turn that into something actually usable. Yeah. And in the writing world, it almost never gets it right. Even if I just give it a little not take, can you make sure to emphasize x?\u003C\u002Fp>\u003Cp>It rewrites other bits, and I'm like, no. That bit was good before. So so I end up again doing the manual validation slash fixing slash, you know, that that last mile piece of work Yeah. Manually. And I have no hesitations about that at all, but it has still saved me 80% of my time.\u003C\u002Fp>\u003Cp>And I feel like, you know, I so I said things like, okay. So I use chat gpt for, like, generative creative work, but I won't use it for code. Code is just it is creative.\u003C\u002Fp>\u003Cp>Speaker 1: It is. Yeah. So it's not interesting. Same It's the same thing. The same process because Exactly.\u003C\u002Fp>\u003Cp>Let me see if I have an example. Here, this is one of my first days at, at the company. I'm like, how do I use this? And it showed me it in JavaScript. I don't even write it in JavaScript, but I'm like, okay.\u003C\u002Fp>\u003Cp>Cool. Can you write out the whole code? And then it did. I'm like, that's interesting. What will it look like if it's in Next.\u003C\u002Fp>\u003Cp>Js? And it told me. And I'm just and I'm like, so what's this part? Like, what is this thing or app protocol? How do I create that?\u003C\u002Fp>\u003Cp>Do I need my own URL? And then it tells me. So, like, I'm not I know it's gonna be sometimes wrong, like, with writing. And I know it's I'm not gonna copy and paste this exactly, but I'm like, it's a starting off point because I had no clue what DW or communicating across DWNs is. Like, I just keep hearing my coworkers say that.\u003C\u002Fp>\u003Cp>I'm like, what is it? How do I do it? Or like I was\u003C\u002Fp>\u003Cp>Speaker 0: acronyms. I use it for acronyms all the time. Like, what does what does this mean? Like, someone's just used an acronym. Good for docs as well.\u003C\u002Fp>\u003Cp>Like, how could I make this more approachable? Or where where do you think I might have made assumptions based in my writing? And then they'll give me the answers. It doesn't mean I need to fix them. It means I'm aware of them.\u003C\u002Fp>\u003Cp>And some of those assumptions are completely fine. Because, you know, in the world of directors, for example, you know, some, developer relations educator teams I've been on in the past have been, like, how do we enable people from the first line of code they write to be successful with us? And because of the size team we are, I'm not incentivized by that directors today. I'm incentivized by saying, this is the knowledge you must have. And if you have this knowledge, we will take you from there.\u003C\u002Fp>\u003Cp>Hence, some assumptions are fine. But I can interpret that and make decisions based on that. That is fact I also like I use it for writing in terms of changing form. So every minor release of directors actually recorded one just today, which will now age this video if I've not done another one since. We we, yeah, do this release notes video that kinda describes what's new, what's important, any breaking changes, community contributions, and so.\u003C\u002Fp>\u003Cp>And I will write it as a set of bullet points, and that's my script. I don't script word for word. I script bullet points, and I can talk around them. But then I'll take those bullet points. I'll be like, hey.\u003C\u002Fp>\u003Cp>Could you just turn this into a announcement blog post? 80% of the way that I'm never gonna publish it as it comes out. As an AI sent, as you said. Like, I can tell. But, again, it will help me find the words.\u003C\u002Fp>\u003Cp>Sometimes a bit of nudging and correcting. And then I'll say, great. Summarize this as a discord post. Like, I want it in a tweet link. I want it in a discord post.\u003C\u002Fp>\u003Cp>Great. So I've written some material that was handwritten. Yeah. Bullet points. I can write I can use that, but you could generate a script.\u003C\u002Fp>\u003Cp>But I I just use that as my script. I get all these other modalities from one source material. So I yeah. So that's another way in which I use it for non code content, which I find very effective.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I guess sometimes I do use AI. In other words, there's, like, a product called video tap.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It will\u003C\u002Fp>\u003Cp>Speaker 1: be by Chris. Yeah. Yeah. Sometimes I'm gonna use that. Sometimes I'll be like, yeah.\u003C\u002Fp>\u003Cp>Turn this into little clips for me. Or So for those watching, just\u003C\u002Fp>\u003Cp>Speaker 0: so you know, the way VideoTap works is is you feed it a long form video, it'll spit out a transcript, summaries, some, like, social posts, social, like, little sting videos, some little teaser videos, and so on. That's a big reason that our team is really interested in long form video, like you're watching right now on this platform on right now, is because it once transcribed, which you can do now, even locally for free, You know? You can do it using other external services. But, you know, however you do that, you can then derive lots of other content out of it. I don't wanna turn this into\u003C\u002Fp>\u003Cp>Speaker 1: a factory of impersonal content where we make video\u003C\u002Fp>\u003Cp>Speaker 0: and everything else is just AI generated. Factory of impersonal content where we make video and everything else is just AI generated dribble. But, again, it allows us to quickly move on the other content. Structure wise, often, write written content and video content can follow a similar or same structure. The general points I'm making are the same.\u003C\u002Fp>\u003Cp>It might just be a little less conversational. Is a tool that, we're exploring as part of that, actually.\u003C\u002Fp>\u003Cp>Speaker 1: I love that. I want my team to get into it, but I I haven't been able to convince them yet. Another well, another thing while you're while you were talking, I was thinking of was, like, sometimes converting things to, like, different well, this is my last point because I know we're getting down to the wire. Like, different, like, languages, I guess. Oh, wait.\u003C\u002Fp>\u003Cp>For example, like, my team would and and in in terms of time too because I'm like, now I'm not spending, like, hours trying to figure out how this works. My team was saying, like, hey. Some things are failing because we don't have, like, webpack configured or whatever. And I'm like, okay. So I've seen how they configured it for a particular I think this was for DocuSaurus.\u003C\u002Fp>\u003Cp>But I'm like, how could I do something like that for my Next. Js app? Because I'm seeing this error in my Next. Js app. And then I just gave it what it gave me, what my team did for DocuSaurus, and it printed it out for for, Next.\u003C\u002Fp>\u003Cp>Js. I tried it out. And on top of that, like, what I really like is the explanation. I feel like back in the day when I was copying stuff from Stack Overflow, it may or may not have the explanation with it, and I'm like, I don't\u003C\u002Fp>\u003Cp>Speaker 0: all one that you can understand. Right? Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: They're all like, you know, it's good.\u003C\u002Fp>\u003Cp>Speaker 0: You can ask follow ups, say that bit doesn't make sense, elaborate, and so on. Yeah. So I will say this is where I start to become skeptical again. I will give you just a quick story. You may know Joe Nash.\u003C\u002Fp>\u003Cp>Yes. So a friend of mine, and he he quite rightly, like, roasted me in, like, a friendly, funny way back in January. Back in January, I ran a conference which you spoke at.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. You got this.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Over 3 time zones that we published the time zones for. And I wrote it all in one time zone. I went, hey. Could you please give me, you know, based on this time zone, I want Eastern, I want Indian Standard Time, and I want one other car, remember, whatever whatever one it was.\u003C\u002Fp>\u003Cp>And it got it right enough of the way that I manually validate it. Yeah. Yeah. Yeah. And then it started to go off the rails.\u003C\u002Fp>\u003Cp>I ended up inviting some speakers, like, an hour early for their talk, And my hosts were not there. There wouldn't have been there at the right time if I didn't realize. So, basically, I couldn't have trusted the output. I did not enough manual validation. Well, I should have just done it in a spreadsheet.\u003C\u002Fp>\u003Cp>I I've learned my lesson. But, you know, that's made me skeptical for things like, you know, convert a to b. Even languages, I'm a little bit skeptical, you know, on its translation ability. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: That's interesting. I think you're right. Like, I can't even argue with you that it does it does need manual validation. And maybe I'm spending the same I don't think I'm spending the same amount. Maybe I am taking a lot of time to do the manual validation, but I woulda never wrote this on my own.\u003C\u002Fp>\u003Cp>It woulda take well, I I could've, but it woulda take me to the This is\u003C\u002Fp>\u003Cp>Speaker 0: different. This is also comprehension. So it's teaching you a thing and then writing the code. In in my world, I'm perfectly able to convert time zones in a spreadsheet. I just got lazy, and I trusted it all and then didn't validate it.\u003C\u002Fp>\u003Cp>So, you know, the context is a little different. It was a it was a data manipulation slash entry task, and it it got it wrong. And I'm to blame for for trusting its output. But, yeah, that's a funny story that, in fact, even when we are now, this will really date it because I think this will change. We are in the few days where chat g p t is being accused of being lazy because it won't complete the whole task.\u003C\u002Fp>\u003Cp>That is the time frame in which we are recording.\u003C\u002Fp>\u003Cp>Speaker 1: When it does that.\u003C\u002Fp>\u003Cp>Speaker 0: Sure. So this is new. This has just happened, and I imagine this will have stopped. But regardless, it's just started happening in the last few days. So that's that's the time frame in which we are recording this video.\u003C\u002Fp>\u003Cp>And I did make a little joke. I was like, you know, how else am I gonna create an international, you know, conference schedule? Anyway, thank you for joining me. Actually, genuinely, now not every episode of these I've been ending the same way. Some have gone in and went, yeah.\u003C\u002Fp>\u003Cp>I still will never gonna use it, but now I understand it. Now do you wanna know what? I can see myself genuinely using GitHub auto, copilot, auto completion comment things opt in, like, I turn it on. Not that it's always there and gets under my feet. For things like he says, hey.\u003C\u002Fp>\u003Cp>I've done a I've done a get I've done a get in post request. Give me a patch. You know, patch, put, delete, or here's a schema. Can you just generate some endpoints? Yeah.\u003C\u002Fp>\u003Cp>Whatever. I also see the chat on the side working quite nicely, actually, because of its contextual awareness of your code. So you know what? Consider me to a degree one over. I'm still a little concerned about the correctness.\u003C\u002Fp>\u003Cp>But if I start to think about it as it doesn't need to be correct, it needs to show the approach or the thoughts because I do I'm lucky enough to have the skills to validate very quickly just by eye that feels right, that feels incorrect, and then respond accordingly. I'm still am skeptical around companies claiming it can do more than it can today. And as a maintainer of a large open source project, I definitely feel the the sharp end of that too.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: And, you know, people end up feeling like they have a bad experience with our product. Sometimes I will say that it's absolutely us. Sometimes it's bugs. Sometimes it is incorrect or incomplete dots. Absolutely.\u003C\u002Fp>\u003Cp>We're not perfect. But sometimes it is hallucinated code, which can only be spat out by one of these tools. And that makes them think, oh, things bust, and it just isn't. And so, that's interesting too. So I remain skeptical on that end.\u003C\u002Fp>\u003Cp>And as a result, today, I would still never implement it in docs for that reason. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: That's fair.\u003C\u002Fp>\u003Cp>Speaker 0: But as a personal user who can validate an output, yeah, do you know what I see it? And that's what I was really hoping to get. I was hoping to see someone who has touched these tools more.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm. And\u003C\u002Fp>\u003Cp>Speaker 0: you very much have, especially in your previous role. Yeah. I wanted to see how do you use it? How does this form part of your practice? And what parts of this might I be able to adopt?\u003C\u002Fp>\u003Cp>So, yeah, success. Thank you.\u003C\u002Fp>\u003Cp>Speaker 1: Yay. I'm glad. And I I think, it was good to have the to learn that perspective of other people just taking the code and just assuming it'll work because of how\u003C\u002Fp>\u003Cp>Speaker 0: An increasing amount marketed it. And increasing\u003C\u002Fp>\u003Cp>Speaker 1: I never seen that side.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. An increasing amount of, Yeah, which which is challenging, which is challenging. And yeah, I don't know how I don't think that is overcome by education. I think users believe the output is correct. I think that is solved, unfortunately, in time as they just get better at their job.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: But that is that doesn't help me in the meantime, but that's okay. I can suck that. But but selfishly and personally, I've got some stuff. After you wrap up, is there anything else you wanna share? Where can people find you, your company, your projects, your favorite TV shows?\u003C\u002Fp>\u003Cp>Speaker 1: Cool. Yeah. You could find me on Twitter. I'm not gonna call it x at blackrobytes. Actually, any social media, you can find me at blackrobytes.\u003C\u002Fp>\u003Cp>My blog, blackrobytes.dev. What was the other questions you had?\u003C\u002Fp>\u003Cp>Speaker 0: I said, any anything work wise you wanna promote? And then Oh. The TV shows. Come on. Spit spit them out.\u003C\u002Fp>\u003Cp>What you're watching?\u003C\u002Fp>\u003Cp>Speaker 1: Speed TV shows. Okay. I got you. Okay. Work wise, go to my just go to blackrobites dot dev, and you will find something.\u003C\u002Fp>\u003Cp>Speaker 0: B y t e s. Bites. Like coding bites.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. B y t e s. It's like computer bites, not I'm biting you.\u003C\u002Fp>\u003Cp>Speaker 0: Or eating food or whatever. Yeah. Okay. Go on.\u003C\u002Fp>\u003Cp>Speaker 1: That's better than that.\u003C\u002Fp>\u003Cp>Speaker 0: Uh-huh.\u003C\u002Fp>\u003Cp>Speaker 1: TV show right now. Oh, wait. I'm a big reality TV show fan. So I've been watching Love is Blind. It's a crazy show.\u003C\u002Fp>\u003Cp>I love also invincible, gen v. They're a little gory. But\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So so invincible, everyone, is an 18 rated show, I must add. Because my friend Nathaniel made me watch an episode of it. And to be like, watch one episode you're either in or you're not in. And I was so in, we just haven't got around to watching more than the first episode.\u003C\u002Fp>\u003Cp>So Oh, what? You know it. Yeah. I I know. And I know it's a season 2.\u003C\u002Fp>\u003Cp>Really good. But my kids were there, and they they were, like, they were, like, 1 and, like, 3 and it's like and and it really gets gory towards the end of the first episode so you know it's a little bit of like an adult animation, but they don't bloody understand what's going on. It's fine. And at the end, we were like, really,\u003C\u002Fp>\u003Cp>Speaker 1: really? Did you just do that?\u003C\u002Fp>\u003Cp>Speaker 0: What the hell? So yes, it is a bit gory. And that's my story of learning it.\u003C\u002Fp>\u003Cp>Speaker 1: It's very gory. Keep it awake.\u003C\u002Fp>\u003Cp>Speaker 0: It was good. It was but it was on Amazon Prime Video. That's an Amazon Prime Video original, I think. Yeah. Excellent.\u003C\u002Fp>\u003Cp>Lovely speaking. Thank you so much. I'm going to can I use these in Codespaces? Because that's what I use.\u003C\u002Fp>\u003Cp>Speaker 1: Copilot? Yes. Yeah. Yeah. I think that I'm I I used to never use Visual Studio Code.\u003C\u002Fp>\u003Cp>I used to only use Codespaces.\u003C\u002Fp>\u003Cp>Speaker 0: I still don't. I just use Codespaces. Alright. Sick. Have a wonderful rest of your day.\u003C\u002Fp>\u003Cp>Thank you very much, and goodbye.\u003C\u002Fp>\u003Cp>Speaker 1: Bye\u003C\u002Fp>","Hello there. My name is Kevin, and welcome to learning things I love to hate. In this show, I stop avoiding technologies that I've been avoiding for one reason or another by inviting my friends and colleagues to teach me more about them. And today, my dear friend, Roselle, hello. Would you like to introduce yourself? Hello. Yes. My name is Roselle Scarlett. I'm a staff developer advocate at a company called TUVD, which stands for to be decentralized, not to be determined. And That's the first time I knew that. I always was like, when's the company name coming? No. I was I was the same. Even, like, I was at the company, and I'm like, why why is it called TBD? I didn't wanna say anything until I heard someone say what it what it stands for. So you went for to be decentralized? Yes. To be decentralized. What else about me? Before that, I worked at GitHub as a developer advocate, focused on, like, GitHub Copilot things. And, besides that, I just I like to use social media a lot and watch TV. Good. And today's topic is alluding to your previous role. You kind of, have provided the reason why I've asked you on, is about using AI as developers. So the way I start these episodes is I kinda tell you what I know or what I think I know now, what my experiences or my knowledge is. You firstly are welcome to tell me you got it wrong, but we can use that as a basis. And then my skepticisms and hesitancies, we knew that as a basis to just have a chat until we run out of time. At any point, if you wanna share your screen, feel absolutely free to be equally feel no pressure to. So using AI as developers. AI mostly popularized by OpenAI using the chat GPT interface, but since has permeated seemingly every piece of software that exists. My, GitHub came out with GitHub code pilot some time ago. You will fill in that blank for me. And I used it at the very beginning, like, right at the very, very beginning. And code pilot is a tool which basically allows you to to describe 1, describe what you're trying to achieve with your code, and it will give you a suggestion, by writing it in your code editor. And, also, I believe it can do, like, straight up, like, we will try and auto complete the code you're writing, maybe. In any case, my experience and thoughts around AI are the following. 1, I know I've seen some stats, like, hallucination rates or, like, where wrong rates are are dropping, and I truly believe at one point this concern will not be the case. But I simply don't trust it. Right? I don't trust it to give me codes that either is correct or is the right way of doing things. You know? So that's one concern. And the other is having used CodePilot at the very beginning, just the first version of CodePilot before it was a bunch of of separate services, which I don't really know that much about, I found it really annoying. Like, I actually found their interaction really frustrating and in the way to the point where I turned it off because it was in the way. So, that's the kind of basis to one other bit of context that might be useful is I work for a company called Directus. We have a Discord with, like, 11,000 people in it. People drop by when they have problems. And community, including our team, see if we can help people and an increasing amount. And we spoke about this briefly on Twitter. An increasing amount people think our stuff's broken, and the code snippets they share are hallucinations. They never existed. They never even nearly existed. And there's a little bit of a maybe I'm slightly salty because, you know, people think your thing's broken, but, actually, it's not even it's not even a code snippet we wrote ever in any point in time. It's just wrong. And so there's something there around how people perceive AI and what it can do for them at their current stage in their career, their project, whatever. So that's my little brain dump to maybe start. Could you maybe give us a bit of a landscape, anything you know about AI for developers, whether that's GitHub specific, other other tools, whatever? Yeah. Right now, what I use for coding with AI is usually GitHub Copilot or ChatTPT. But I do wanna, like, even back up and explain why I like it because you might just be like, oh, just because you worked at GitHub. But I remember and and that's fair. Right? But I remember when I joined GitHub, I did see all of, like, those, like, negative or mixed reviews. There were some people on Twitter being, like, I love it. And then there were some people being, like, I think I joined when it just went out in public beta, in, like, September 2021. And that's when I was like, let me see what this thing is. I'm, like, half skeptical or half curious. And at first, I was, like, this does not work. I hate it. It's, like, kinda like how you were like, it's a Yeah. Way. But as I started to use it more and more, I kinda got used to it and figured out, like, how it works on my workflow. I'm like, okay. Maybe I don't always want it to at first, at one point, when I knew what I was doing, I would, like, turn off GitHub Copilot. And then if I, like, needed help or if there was, like, a pattern like, let's say, I already wrote an API call with a get for a get request, and then I was, like, I did a post request. Maybe now I need to do a patch and delete. I'll get turn GitHub Copilot back on because I'm, like, okay. It it knows kind of, like, what boiler plating almost. Yeah. Yeah. Exactly. But then once I started to like, after that, like, I just had it on all the time. And then one thing that makes me passionate about it is that, is the psychological safety aspect. I feel like when I started off as a software engineer back in 2018 or 2019, I don't remember, like, my team was really mean. I was like, they were so mean. I was, like, the only black girl on the team. And, like, it got to the point where I, like, had, like, real life anxiety from my job. Like, I was, like, on the bus on the way home, and I couldn't even stand up because my muscles That's why I think we were, like, kinda, like, discussing or debating or had different points of views because you're like, I don't think it's good for juniors. But I'm like, I do because I wish I had at least something that I can talk back and forth with or, like, share an idea with and, when I was, like, just starting out. But I agree with you. It does sometimes generate wrong code. Or not even sometimes, a lot of times. And I do think that I think there's a part of, like, needed education around that. And, also, another thing on on your topic or on your concern about, like, direct us. One thing that the new company I work at has done is I'm sharing my screen right now. Okay. They trained let me I don't wanna show all my questions, so let me hide that. But they created a plug in. So so for, like, chat gpt4, or, like, I think it's, like, a bit like, a more premium mode. You have plug ins. Right? And they create a plug And you you, like, feed it. You feed it your corpus of data, your docs. Right? And then you give it you give it some some the custom instructions. I I use chat GPT also. I just don't use it for code. So I understand the concepts. Right? So you give it the custom instructions and tell it who the persona is who will use it, and it'll basically spit out a custom assistant given the context. Yeah. And I think GitHub Copilot has a similar thing too called Copilot for docs. And this the fact that they trained, the fact that TBD trained their doc or chat gpt on these docs has been extremely helpful for me. Because I walk in into this job as, like, I'm a staff developer advocate, so I'm expected to know this stuff. But I never heard of Web5 before, like, I even interview for the company, really. I don't know, like, how to do this stuff. And the documentation is, how will I say, I think it's a work in progress. And also, I'm just not a person. I'm I'm like I I I lead the team that writes our docs. So I think calling things a work in progress is completely fair. Okay. Good. No. No. That look that look is like, yeah. I know what you mean. Because I'm like, I read it, and I'm like, what? Like, there's missing stuff with it's it's still forming. The the product itself is still forming. And I'm also not really a person that reads documentation end to end. Like, that's just not me. Like, I do your learning style. Yeah. That's not my learning style. Good shit. That's fine. Exactly. So, like, for example, I for me to build an application at this company, I, like, I wanted to jump in. I wanted to, like, be able to be, like, alright. I'm ready to go and, like, you guys don't have to hold my hand or you all don't have to hold my hand. I just been talking to chat gpt and being like, hey. Why is it saying that the rec record dot send is not a function? Here's my code. And, like, it says, okay. It does exist. I'm like, alright. I know that. Like, you're being a little annoying. But then I I just from reading what they said, I was like, okay. I figured out that it needs to be destructured. And I'm like, okay. Sometimes when I refresh my page, this list disappears, and it goes on and tells me, oh, it's probably because of the asynchronous state setting. When you're calling this function, this, like, state variable is not persisting. I'm like, okay. Cool. So, like, I'm able to, like, go back and forth with it and be like, alright. Maybe I didn't think of this perception, or perspective. And I think it's, it's kinda like rubber ducking, I think, because sometimes I'm not I think I think that's fair. See, I have a few thoughts about what you've just shown me. So the first is Yes. You 1, you made a comment that let's just say the docs are incomplete. Let's just summarize it there or they're not in their state. They're not accurate. Whatever it is, they are not they do not contain the complete information. So, 1, this piece of software is ultimately guessing at the blanks, and I'm not sure I'm comfortable with that. The second is you demonstrated that you have an ability to go, it was being slightly annoying. I I know it was not giving me the right answer. And that is not necessarily a skill everyone has with the immediate counter in my mind of, like, okay. But you and I are experienced developers and, like, I I'm not gatekeeping anyway for myself, but I do have some skill that allows me to assess the correctness that not everyone has. And I wonder if that changes the experience or the perceived experience of a user. And then, and then beyond that, I I get it. It allows you to rubber duck to have that thought process back and forth. But I have those 2 concerns. Garbage in, garbage out. And I'm not saying the docs are garbage, but the docs miss things. So how do you how does it reconcile that? Or, like, have you ever found that it does just make things up? Yeah. And the and the other is yeah, how what do you have any thoughts around people who lack that ability to look at something and go, this isn't right or isn't what I want or it's contextually incorrect or whatever it may be? Yeah. I mean, exactly remember my skill set as a junior. But I do think it's kind of interesting because okay. When I was a junior, sometimes I would copy and paste stuff in Stack Overflow, and it wouldn't work. And then I would be, like, alright. Well, maybe I need to keep trying and find something else. So I always I I think it's an interesting case that, like, they're copying stuff from AI. They see that it doesn't work, and then they're just, like, well, it doesn't work. I'm, like, usually my even even when I was just learning, I feel like my initial response is, wait. Hold on. Let me try something else. Let me figure out. Because I will go back and tell ChachiPT or GitHub Copilot. I'll be like, this doesn't work. Like, what are you talking about? Yeah. You have this part wrong. Like, I'll usually feed it the information of, like, here's why you're wrong. And then it'll be, like, oh, sorry. And I don't I don't know. I think I've always told people when I taught them GitHub Copilot things, like, when I do talks, I'm like, treat it like you're it's a coworker. Like, you don't trust your coworker's code all the way. Well, that's the thing. I I think you've made a really interesting point around trust. 1 Yeah. Yeah. But how else were you learning before? Probably through trial and error with code that didn't work. This is just surfacing it in a different way. It's just a different, yeah, way of generating or surfacing or searching that code on the web. And then the other side of it is, sure, what's the alternative? You know, my feelings about docs and AI, people who have hung around in our Discord server will know this is today. And we're recording this at the end of 2023. I don't necessarily think it will come out, you know, very, very swiftly. So just so people know because breakthroughs are happening all the time. I will not present codes to people as an educational resource that is AI generated because the moment you present it to people, you are endorsing it. And it doesn't matter how many caveats you put in. The people's behavior with AI seems to not fully get that it hallucinates. Yeah. And so today, it's just not there. It's just it's just not there. And don't get me wrong. I'm still exploring AI. I'm still thinking about it. I'm still thinking, well, how do we lay the foundation. So when that concern falls away, because it will, we can we can deliver something great in terms of the world of education. I never touch AI generated docs, AI generated answers. I never use chatbot interfaces. I do not trust them. Interesting. Even if you try them out after, like, that's the part. Because it's not like they're just But then I but then I am someone who doesn't mind reading documentation. Interesting thing about this is it isn't yeah. It doesn't just have to consume, consume docs. It can consume other, other sources of information around the web, and maybe that's that's kinda interesting. The other the other concern I had, was so, you know, a lot of these trust arguments, I see the counter arguments. It's not the first time I've heard many of them, but it's good to See. Excuse me. It's good to to think about them a little more. The other part of it was the annoyance factor. And I know that even in the world of GitHub, they have released CodePilot is not, just the thing that autofills your code based on comments. It's like an ecosystem of services. And I'm kinda curious, bearing in mind that it doesn't just have to be that interface. It doesn't just have to be chat in a browser. Mhmm. What how else do you use AI in your in your practice as a developer or a developer advocate, outside of what original Copilot looks like? I use original Copilot. I use Copilot chat, which is, like, chat gpt. But, I'm contextually aware of your codes and the environment at which it runs. Yeah. I'm trying interesting. The I thought I had a Visual Studio example code example. Hold on. There we go. And then, oh, another one I use is within my terminal. So there's, like, Copilot CLI that I use. So sometimes I'll be like, oh, man. I forgot how to, like, do this bash for me. And I don't have the example right now. My bad. But I'll be like, I forgot how to, like, do this or I forgot how to write this script. And then I'll write in natural language what I wanna do, and then GitHub Copilot will give me a suggestion of what I wanna do of what it thinks I wanna do, and it'll give an explanation of what that command is. And then I can say, revise the query. That's not what I want. Or I can be like, yes. That's what I want. Go ahead and run the code. So if I'd be like, actually, I didn't want it to just, like, grab grab all of these JavaScript files or maybe I wanted to remove node modules or something like that, it'll, like, revise the query for me. But I mostly use the comment fee like, writing the comments and then the chats on the side. Interesting. That's fascinating. That's a really interesting interface. And, of course, I I mean, logically, I know this interface exists. This will this will be one of many tools like this that is contextually aware of your code base. That's another issue with, like, chat g p t, for example, is it's not contextually aware of your code base. It just gives you a isolated pace. Exactly. It's so annoying. Whereas this is like, oh, use one reference in the index dotjs, from line 22 to 2279 to, write out all of this. Oh, another thing I want to bring up is I mostly use AI to understand code, to be honest, more than, like, literally write out my code. Yeah. Could I ask you could I ask you a favor? Could you bump your font size up Oh. 2 or 3 times? I can't. Let me, All good. That's good. Doing right here. Cool. So one thing I use it for is, like, understanding that code. So, let's say, This is I don't know. I I use a lot of AI code even though you don't. Alright. So, oh, wait. Let's say, right, I I want a better understanding of this Web 5 package, which I've lit legitimately done in the past. Right? So I've opened up, like, these like, the the type source or the source definition for the code that they're creating. And I'll, like, highlight all of it and be, like, hey, Copilot chat. Can you explain what's going on in this? How do I sometimes I'll ask you questions, like, how do I, like, create a record or whatever. Let me see if I can just give an example here. If I just say explain. Like a slash command. Interesting. Yeah. They have slash commands. And okay. It's using all of these references. That's cool. They're working on it even more. But I I mostly use it for explanations because sometimes I'll look at this and I'm like, my brain is hurting. Like, what is this giving me? So once it breaks down all that stuff for me, it gives me more insight. Like, that's that's my main way of thinking. Say I will say in the skeptics world here, a few things. 1 Yeah. Do do you trust what it says? That's a I mean, I don't know. Go in half, like, half trusting. And I think that's okay. My real thought though is this file you've opened up on the right looks very lovingly commented, which is fantastic. Yeah. No. This is a bad example. But yeah. In terms of power, because all it's doing really is presenting the information that has already been handwritten in a nice way. And so that's that's there's a bit less inference there. It's just a bit more summarizing what's already been written. Yeah. But for me, I like seeing stuff in this format. I don't know why rather than, like, looking at, like, random I get it. I get it. I don't know. I don't like reading code comments. Yeah. Yeah. To to understand. Right? It's not my style either. Yeah. So, yeah, that's really interesting. I suppose you can ask follow-up questions. It'll try its best. Interesting. So what have we discovered so far? So we're talking about chat GPT as I as an as an isolated example that we spoke about 2 forms of code pilot. We've spoken about this kind of if you write a comment, it will suggest things to you, which I'm really not a huge fan of the interaction. Though this idea that you can turn it on and off when you just want a bit of input, You know, when there's something meaningful coming up or when you need oh, I need to I don't know. Replace every I need to make this URL safe. Okay. Give me the regex for that. Yeah. Reg reg x. Give me the regex for that. Yeah. And it's something you would just Google. You'd find you'd maybe go to a little generator online. I I do get that. It's interesting turning it on, turning it off. That's fascinating. I may have a think about that. And the second is this chat interface, which is contextually aware of your code base. Mhmm. It also allows you to highlight portions and be very specific about the area in which you're asking, but it still understands the context. Yeah. So these are 3 tools used in different ways. Yeah. How else, if at all, does AI kinda come into your your day to day work life, as a developer? Yeah. Was that a was that a little kitty cat? Yeah. Sorry about that. That's fine. I don't know why such, like, as if he would understand. I guess another thing and this is something I do a little bit less because I think this this is still a work in progress. But sometimes, like, let's say I'm making a demo app. I wanna, like, just put it out really quickly, but I'm like, maybe I should add some, like, comments or notes to it. I'm, like, I already wrote this all up, and, I'm I'm lazy. So another thing I'll do is, like lazy. Think we're all lazy developers. There's no shame in that. Yeah. So another thing I'll do is, like, I'll highlight it, and maybe I'll, do this where it's, like, generate docs. But what that's really doing is not generating real documentation. It's just generating, like, JS docs. But I found that to be, like, perfect because it doesn't have to think too hard about this. And also think about the amount of time it would have taken you to write that versus what it just did. And then you just go in and fix it, adjust it, and so on. It's quite small amounts of code, though. You know, it's pretty pretty clean-cut, but it still takes time to do. And there are other generated ways to do it, but this really allows you to do a quick valid Yeah. Manual validation. I think that it that's the thing I keep coming back to though, which is the success of using AI as developers. I believe at this point, I believed at the beginning of our chat and I still believe relies on your ability to validate either through a gut feeling or through testing testing that the output is correct. And not everyone has that skill, but I worry that the way these tools are spoken about by companies that say, hey. We're providing this tool to you. I think there's a bit of a mismatch between between the marketing and reality. And I think that gap is being closed. But I think I've worked for companies in the past that have this tendency where they they don't say, hey. Our goal is to allow x and come with us on the right as we build that. It's we do x, and then they fall short. And I think that's a little bit naughty, and I think that's where a lot of my, hesitation comes from. Because that manual validation, something that today, can you automate? May we'll talk about that in a moment. But, you know, is is often not spoken about. On that note, I have heard about this thing. I forgot what it's I forgot the wording for it. But where you feed output of AI into another AI to try and validate its correctness. And if you do that enough times with enough systems, you can grow your confidence of an answer being correct while being a fully automated setup, which is kind of I've never heard that. That's cool. But I will say what it's called. I agree with you on that part, and I think that's how we both even started the conversation because both of us said companies are, like, amping up AI too much. And then, the, Diane is like, y'all are saying the same thing. So, yeah, I agree with you. And I think I think that's that is a problem I had where it's like we're, like, copilot and all these AI things are gonna solve all your like, double check the work. Because, like, I've used it, like, double check the work because, like, I've used it. I used it the other day. My my company encouraged me to use it to generate test cause we needed to test code snippets in our in our, documentation. But, like, I had to I kept testing the test to make sure they were, like, actually right, not just, like, fake, like, passing. Yeah. And, you know, it's also interesting. So how do I use AI? I use AI every day, and I use it for writing, and I use it for image generation for concepts. I'm a believer that, like, all AI output is based on stolen work, and you've never convinced me otherwise. But, but to take the ideas in my head and illustrate what I had in mind, AI is actually fantastic, and then I generally take it to an expert to do properly. You know? It's just a way of of expressing what I mean. Then I use it in terms of writing in 2 ways. 1, to overcome a blank page problem. Like, help me think about a structure of a a a blog post that's trying to have these key goals. You know? It's not that you're gonna generate it for me, but you're gonna help me overcome the, I don't know how to start. Or I've written a thing. I need it to feel or be more like x in terms of tone or length or, well, tone or length. And you can go away and try and do more with that. So I do it for a lot of rewriting. So the I use it in these ways every day and literally every single day. I also use it for, like, kind of what I would call composite queries where I'm like, here's a picture of a back of a complex cooking instruction or like or like, here's a recipe in German. I live in Berlin. My German is not that good still. Here's a recipe in Berlin. Here's a recipe in Berlin. Here's a recipe in German. I've just done x. What do I do next? Yeah. Interesting. Because I I like I like that idea of the the back of the box. I don't I don't like it for writing. I hate it for writing. Is it not interesting? Sometimes So it says I'll use it for writing, but when I try to tell it to do it in a specific style, I feel like I've seen chat g p t be like, hey. What's up? I'm like, oh. Sorry. Sorry. But I had a second point there, which which the answer to that, which is that I almost never will take the output for beating. In the image generation world, it still goes to someone who is able to turn that into something actually usable. Yeah. And in the writing world, it almost never gets it right. Even if I just give it a little not take, can you make sure to emphasize x? It rewrites other bits, and I'm like, no. That bit was good before. So so I end up again doing the manual validation slash fixing slash, you know, that that last mile piece of work Yeah. Manually. And I have no hesitations about that at all, but it has still saved me 80% of my time. And I feel like, you know, I so I said things like, okay. So I use chat gpt for, like, generative creative work, but I won't use it for code. Code is just it is creative. It is. Yeah. So it's not interesting. Same It's the same thing. The same process because Exactly. Let me see if I have an example. Here, this is one of my first days at, at the company. I'm like, how do I use this? And it showed me it in JavaScript. I don't even write it in JavaScript, but I'm like, okay. Cool. Can you write out the whole code? And then it did. I'm like, that's interesting. What will it look like if it's in Next. Js? And it told me. And I'm just and I'm like, so what's this part? Like, what is this thing or app protocol? How do I create that? Do I need my own URL? And then it tells me. So, like, I'm not I know it's gonna be sometimes wrong, like, with writing. And I know it's I'm not gonna copy and paste this exactly, but I'm like, it's a starting off point because I had no clue what DW or communicating across DWNs is. Like, I just keep hearing my coworkers say that. I'm like, what is it? How do I do it? Or like I was acronyms. I use it for acronyms all the time. Like, what does what does this mean? Like, someone's just used an acronym. Good for docs as well. Like, how could I make this more approachable? Or where where do you think I might have made assumptions based in my writing? And then they'll give me the answers. It doesn't mean I need to fix them. It means I'm aware of them. And some of those assumptions are completely fine. Because, you know, in the world of directors, for example, you know, some, developer relations educator teams I've been on in the past have been, like, how do we enable people from the first line of code they write to be successful with us? And because of the size team we are, I'm not incentivized by that directors today. I'm incentivized by saying, this is the knowledge you must have. And if you have this knowledge, we will take you from there. Hence, some assumptions are fine. But I can interpret that and make decisions based on that. That is fact I also like I use it for writing in terms of changing form. So every minor release of directors actually recorded one just today, which will now age this video if I've not done another one since. We we, yeah, do this release notes video that kinda describes what's new, what's important, any breaking changes, community contributions, and so. And I will write it as a set of bullet points, and that's my script. I don't script word for word. I script bullet points, and I can talk around them. But then I'll take those bullet points. I'll be like, hey. Could you just turn this into a announcement blog post? 80% of the way that I'm never gonna publish it as it comes out. As an AI sent, as you said. Like, I can tell. But, again, it will help me find the words. Sometimes a bit of nudging and correcting. And then I'll say, great. Summarize this as a discord post. Like, I want it in a tweet link. I want it in a discord post. Great. So I've written some material that was handwritten. Yeah. Bullet points. I can write I can use that, but you could generate a script. But I I just use that as my script. I get all these other modalities from one source material. So I yeah. So that's another way in which I use it for non code content, which I find very effective. Yeah. I guess sometimes I do use AI. In other words, there's, like, a product called video tap. Yeah. It will be by Chris. Yeah. Yeah. Sometimes I'm gonna use that. Sometimes I'll be like, yeah. Turn this into little clips for me. Or So for those watching, just so you know, the way VideoTap works is is you feed it a long form video, it'll spit out a transcript, summaries, some, like, social posts, social, like, little sting videos, some little teaser videos, and so on. That's a big reason that our team is really interested in long form video, like you're watching right now on this platform on right now, is because it once transcribed, which you can do now, even locally for free, You know? You can do it using other external services. But, you know, however you do that, you can then derive lots of other content out of it. I don't wanna turn this into a factory of impersonal content where we make video and everything else is just AI generated. Factory of impersonal content where we make video and everything else is just AI generated dribble. But, again, it allows us to quickly move on the other content. Structure wise, often, write written content and video content can follow a similar or same structure. The general points I'm making are the same. It might just be a little less conversational. Is a tool that, we're exploring as part of that, actually. I love that. I want my team to get into it, but I I haven't been able to convince them yet. Another well, another thing while you're while you were talking, I was thinking of was, like, sometimes converting things to, like, different well, this is my last point because I know we're getting down to the wire. Like, different, like, languages, I guess. Oh, wait. For example, like, my team would and and in in terms of time too because I'm like, now I'm not spending, like, hours trying to figure out how this works. My team was saying, like, hey. Some things are failing because we don't have, like, webpack configured or whatever. And I'm like, okay. So I've seen how they configured it for a particular I think this was for DocuSaurus. But I'm like, how could I do something like that for my Next. Js app? Because I'm seeing this error in my Next. Js app. And then I just gave it what it gave me, what my team did for DocuSaurus, and it printed it out for for, Next. Js. I tried it out. And on top of that, like, what I really like is the explanation. I feel like back in the day when I was copying stuff from Stack Overflow, it may or may not have the explanation with it, and I'm like, I don't all one that you can understand. Right? Yeah. They're all like, you know, it's good. You can ask follow ups, say that bit doesn't make sense, elaborate, and so on. Yeah. So I will say this is where I start to become skeptical again. I will give you just a quick story. You may know Joe Nash. Yes. So a friend of mine, and he he quite rightly, like, roasted me in, like, a friendly, funny way back in January. Back in January, I ran a conference which you spoke at. Yeah. You got this. Yeah. Over 3 time zones that we published the time zones for. And I wrote it all in one time zone. I went, hey. Could you please give me, you know, based on this time zone, I want Eastern, I want Indian Standard Time, and I want one other car, remember, whatever whatever one it was. And it got it right enough of the way that I manually validate it. Yeah. Yeah. Yeah. And then it started to go off the rails. I ended up inviting some speakers, like, an hour early for their talk, And my hosts were not there. There wouldn't have been there at the right time if I didn't realize. So, basically, I couldn't have trusted the output. I did not enough manual validation. Well, I should have just done it in a spreadsheet. I I've learned my lesson. But, you know, that's made me skeptical for things like, you know, convert a to b. Even languages, I'm a little bit skeptical, you know, on its translation ability. Yeah. That's interesting. I think you're right. Like, I can't even argue with you that it does it does need manual validation. And maybe I'm spending the same I don't think I'm spending the same amount. Maybe I am taking a lot of time to do the manual validation, but I woulda never wrote this on my own. It woulda take well, I I could've, but it woulda take me to the This is different. This is also comprehension. So it's teaching you a thing and then writing the code. In in my world, I'm perfectly able to convert time zones in a spreadsheet. I just got lazy, and I trusted it all and then didn't validate it. So, you know, the context is a little different. It was a it was a data manipulation slash entry task, and it it got it wrong. And I'm to blame for for trusting its output. But, yeah, that's a funny story that, in fact, even when we are now, this will really date it because I think this will change. We are in the few days where chat g p t is being accused of being lazy because it won't complete the whole task. That is the time frame in which we are recording. When it does that. Sure. So this is new. This has just happened, and I imagine this will have stopped. But regardless, it's just started happening in the last few days. So that's that's the time frame in which we are recording this video. And I did make a little joke. I was like, you know, how else am I gonna create an international, you know, conference schedule? Anyway, thank you for joining me. Actually, genuinely, now not every episode of these I've been ending the same way. Some have gone in and went, yeah. I still will never gonna use it, but now I understand it. Now do you wanna know what? I can see myself genuinely using GitHub auto, copilot, auto completion comment things opt in, like, I turn it on. Not that it's always there and gets under my feet. For things like he says, hey. I've done a I've done a get I've done a get in post request. Give me a patch. You know, patch, put, delete, or here's a schema. Can you just generate some endpoints? Yeah. Whatever. I also see the chat on the side working quite nicely, actually, because of its contextual awareness of your code. So you know what? Consider me to a degree one over. I'm still a little concerned about the correctness. But if I start to think about it as it doesn't need to be correct, it needs to show the approach or the thoughts because I do I'm lucky enough to have the skills to validate very quickly just by eye that feels right, that feels incorrect, and then respond accordingly. I'm still am skeptical around companies claiming it can do more than it can today. And as a maintainer of a large open source project, I definitely feel the the sharp end of that too. Yeah. And, you know, people end up feeling like they have a bad experience with our product. Sometimes I will say that it's absolutely us. Sometimes it's bugs. Sometimes it is incorrect or incomplete dots. Absolutely. We're not perfect. But sometimes it is hallucinated code, which can only be spat out by one of these tools. And that makes them think, oh, things bust, and it just isn't. And so, that's interesting too. So I remain skeptical on that end. And as a result, today, I would still never implement it in docs for that reason. Yeah. That's fair. But as a personal user who can validate an output, yeah, do you know what I see it? And that's what I was really hoping to get. I was hoping to see someone who has touched these tools more. Mhmm. And you very much have, especially in your previous role. Yeah. I wanted to see how do you use it? How does this form part of your practice? And what parts of this might I be able to adopt? So, yeah, success. Thank you. Yay. I'm glad. And I I think, it was good to have the to learn that perspective of other people just taking the code and just assuming it'll work because of how An increasing amount marketed it. And increasing I never seen that side. Yeah. An increasing amount of, Yeah, which which is challenging, which is challenging. And yeah, I don't know how I don't think that is overcome by education. I think users believe the output is correct. I think that is solved, unfortunately, in time as they just get better at their job. Yeah. But that is that doesn't help me in the meantime, but that's okay. I can suck that. But but selfishly and personally, I've got some stuff. After you wrap up, is there anything else you wanna share? Where can people find you, your company, your projects, your favorite TV shows? Cool. Yeah. You could find me on Twitter. I'm not gonna call it x at blackrobytes. Actually, any social media, you can find me at blackrobytes. My blog, blackrobytes.dev. What was the other questions you had? I said, any anything work wise you wanna promote? And then Oh. The TV shows. Come on. Spit spit them out. What you're watching? Speed TV shows. Okay. I got you. Okay. Work wise, go to my just go to blackrobites dot dev, and you will find something. B y t e s. Bites. Like coding bites. Yeah. B y t e s. It's like computer bites, not I'm biting you. Or eating food or whatever. Yeah. Okay. Go on. That's better than that. Uh-huh. TV show right now. Oh, wait. I'm a big reality TV show fan. So I've been watching Love is Blind. It's a crazy show. I love also invincible, gen v. They're a little gory. But Yeah. So so invincible, everyone, is an 18 rated show, I must add. Because my friend Nathaniel made me watch an episode of it. And to be like, watch one episode you're either in or you're not in. And I was so in, we just haven't got around to watching more than the first episode. So Oh, what? You know it. Yeah. I I know. And I know it's a season 2. Really good. But my kids were there, and they they were, like, they were, like, 1 and, like, 3 and it's like and and it really gets gory towards the end of the first episode so you know it's a little bit of like an adult animation, but they don't bloody understand what's going on. It's fine. And at the end, we were like, really, really? Did you just do that? What the hell? So yes, it is a bit gory. And that's my story of learning it. It's very gory. Keep it awake. It was good. It was but it was on Amazon Prime Video. That's an Amazon Prime Video original, I think. Yeah. Excellent. Lovely speaking. Thank you so much. I'm going to can I use these in Codespaces? Because that's what I use. Copilot? Yes. Yeah. Yeah. I think that I'm I I used to never use Visual Studio Code. I used to only use Codespaces. I still don't. I just use Codespaces. Alright. Sick. Have a wonderful rest of your day. Thank you very much, and goodbye. Bye",[5210,5211],"cbb6cf76-75f6-455a-a94b-dccd7551f105","0a6c499d-d9f7-46b8-b4b7-b6a5da546339",[],{"year":2380,"number":213,"id":4680,"episodes":5214,"show":5215},[4682,4683,4684,4657],{"title":4686,"slug":4687},{"id":4169,"slug":5217,"vimeo_id":5218,"description":5219,"tile":5220,"length":1078,"resources":12,"people":5221,"episode_number":195,"published":5223,"title":5224,"video_transcript_html":5225,"video_transcript_text":5226,"content":12,"seo":12,"status":19,"episode_people":5227,"recommendations":5229,"season":5230},"ai-app","896095989","What if you could build an app that builds itself? That's the question Bryant seeks to answer in one hour on this AI themed episode. Follow along as he builds a custom Directus extension that connects with the OpenAI GPT-4 API and updates the projects underlying data model based on a simple prompt.","3ca1cb87-d47c-477a-af2f-a64dfe1bbd04",[5222],{"name":3590,"url":3591},"2024-01-15","Mission: AI App Generator","\u003Cp>Speaker 0: Hi guys. Welcome back to another episode of 100 apps 100 hours. I'm your host Brian Gillespie, developer advocate at Directus, and today we are tackling the elephant in the room, AI. So we're gonna be building an AI app generator. By that, I mean an app that can update itself with new data models and new interesting ways to work.\u003C\u002Fp>\u003Cp>So what's the inspiration for this? So I've been, over the last couple weeks, I've been seeing all these videos on X or Twitter, whatever it is, that show, this application TL draw where you can go in and sketch something out inside this quick little application, click a button, and it will use the open AI vision API and some of their other tools, I guess, to actually generate real code that makes whatever you sketch come to life. So really excited by that. We're gonna try to replicate something similar here. I'm calling this the mighty morphing App Ranger because I grew up with Power Rangers.\u003C\u002Fp>\u003Cp>It was one of the I think it was one of my first Halloweens. So if you're new to the series, let's dive in. There are 2 rules. We have 60 minutes to plan and build this application, no more no less, and the second rule is just use whatever you have at your disposal. So in this AI episode it should be pretty interesting to see how far we can get with this.\u003C\u002Fp>\u003Cp>One of the ways that I wanna start here is just by charting things out, and we'll try to upload that chart to chat gpt or open AI and and spit something back out that we can use. So without further ado, let's dive in and start building. So like I said, I like to plan everything first. So let's just draw some stuff on our artboard here and kind of sketch out what we want this actual application to do. So the very beginning we're gonna draw a diagram of diagram diagram of an app.\u003C\u002Fp>\u003Cp>Upload, we want to, what, send that to the application. Let's shrink this down. That's really large. Alright, you can tell even though I use Figma quite a bit it's still not my strong suit. So we'll draw a diagram of an app.\u003C\u002Fp>\u003Cp>We will send, use OpenAI Vision to create a data model based on the diagram, and then we're going to update our app based on that code or data model automatically. So we want the app to automatically update itself, but before we dive into that, let's just try to proof the concept out. Right? So let's draw an app. Let's start with something simple like a CRM.\u003C\u002Fp>\u003Cp>Here's our CRM app. What are the different data models or the different tables, the the different collections as we call them inside Directus, which is what we're gonna be using for our back end and what we're gonna build our application with. So we've got contacts. Well, we probably got some organizations. We've got a relationship between those.\u003C\u002Fp>\u003Cp>We probably have some deals, and we can use some arrows for the relationships. And if we just look at contacts and orgs, we probably have maybe like a junction table or something here. I don't think it left enough room, but let's call this, what? Organization contacts. Give that a little more room.\u003C\u002Fp>\u003Cp>Okay. Alright. So there's our basic diagram. Maybe we go in and inspect some fields out for this as well. We got a first name, last name, email, phone, title.\u003C\u002Fp>\u003Cp>K. We've got a name for the organization. We've probably got an address. We've got, what, what else is on the address? I got lost on my Figma file here.\u003C\u002Fp>\u003Cp>So address, name, we got some contacts. We probably got organizations that those contacts belong to. That's probably good enough. For the deal, we probably have a deal name, deal value. And what else?\u003C\u002Fp>\u003Cp>Close date maybe? Alright. So that is a pretty simple CRM app. Let's make our diagram pretty. And now let's just take a screenshot of this and let's just proof this concept out.\u003C\u002Fp>\u003Cp>We can use just the regular chat GPT for this because it it has that underlying vision API that we're gonna use once we start building our app. But I can upload this into chat GPT and say something like this. You are a SQL, post grace, and direct us master. Please create a SQL query, SQL query that will create the data model for the this CRM application as designed in the in the what, in the diagram attached. Please fill in and make the data model more robust.\u003C\u002Fp>\u003Cp>So, honestly, I'm I'm just curious to see if this will actually work to begin with. What happens? Oh, no. Did I lose all of that? Did that really happen?\u003C\u002Fp>\u003Cp>Open this up. Oh, no. Okay. Well, that's a hazards of the job. Right?\u003C\u002Fp>\u003Cp>Restore recently closed. Please, you are a post grace and directus master. No hidden camera tricks on this show. Just fun here. Sometimes you fat finger one of these.\u003C\u002Fp>\u003Cp>Please create a SQL statement SQL query that will create the data model for our app as in the diagram. No. The conversation is not helpful so far. Let's do a new chat. Restore recently closed.\u003C\u002Fp>\u003Cp>Alright. Sometimes the technology doesn't work out so well. Please create a SQL query for Postgres, that's the database we're using, that will create an app create the data model for an app based on the diagram. Alright. I'm gonna copy this just so I don't have to what in the world is going on?\u003C\u002Fp>\u003Cp>Alright. Let's just try this again. Holy moly. Wow. Okay.\u003C\u002Fp>\u003Cp>So we've eaten up a lot of time there just messing with chat gpt. That's a lot of fun, but let's see what this comes up with. Looks like it has to create a SQL data model, SQL code. So if we just look at this, we've got a contacts table. That's good.\u003C\u002Fp>\u003Cp>We've got the organizations table. We've got a associative table. That's a junction table. We have a table for our deals, and then we have some kind of statement that is altering the table. Okay.\u003C\u002Fp>\u003Cp>Please rewrite to use UUID to add a primary key of ID for all the tables and use UIDs. Just want to make sure we all have a primary key of for each one. Alright. Cool. Okay.\u003C\u002Fp>\u003Cp>So now we've got the IDs. We're using the UUID instead of just an integer there. Alright. Great. Okay.\u003C\u002Fp>\u003Cp>So looking at this, we can test it out. Right? How are we gonna test this out? So, what I've got set up, for this app is basically a Docker Compose file that I'm using to generate my back end, which is Directus. And if I pull up my Directus instance and just log in, so we go to admin atexample.com.\u003C\u002Fp>\u003Cp>We do password. We'll log in and if I zoom in just a little bit you can see this is a completely blank app. Nothing here. So what Directus does behind the scenes, it sits alongside your Postgres database and it will introspect that database, which basically means anything that any changes that you make inside that database or any SQL database, doesn't have to just be Postgres, it will mirror those changes in real time. Sounds great.\u003C\u002Fp>\u003Cp>How does it work in application? Alright. So we go in, let's just pull up this database inside TablePlus. Right? So I could see all of my directus tables here.\u003C\u002Fp>\u003Cp>There's no other tables. We've got some items in here for, like, post GIS, just so we can do geo data inside here. But let's just copy and paste this inside the application, or inside table plus, and we're gonna run this SQL query. The only thing that I'm gonna do here is just change this application. I'm gonna change the organization contacts, And what I'm gonna do here, I'm just gonna change this so that there is a ID and that's the primary key instead of a composite key.\u003C\u002Fp>\u003Cp>But other than that, let's just run this thing. So hit run all. Okay. Altered table has been altered. If I refresh, we can see that we have created those different tables and, you know, the associated columns.\u003C\u002Fp>\u003Cp>Great. Now if I load up Directus, I can see that Directus, just refreshing the screen, has recognized that those tables are inside our database. So I can go through and click to configure these tables. We can see that my different fields are coming through, and if we click on those we can configure what the actual UI would look like for these things. So if I just go through and click on a few of them, maybe we want to hide this specific field.\u003C\u002Fp>\u003Cp>Directus really easily allows you to update this data and the presentation of it. So basically now we're just controlling the form that displays when we add a new contact. So let's just edit that a little bit, put the title down there. Great. Yeah.\u003C\u002Fp>\u003Cp>So if we go in, we refresh, we've got our contacts, and now I could go in and add my contact. Great. 555-555555. Looking great. Brian@directus dotio, and I'm a developer advocate.\u003C\u002Fp>\u003Cp>Cool. So now if I go into table plus here, we can also see there's my record. Right? So Directus is really nice in that it mirrors everything, but it also gives me a REST based API. So I could go in and any of those collections, if I were to, just give read access to just the general public, we could also create different user roles here.\u003C\u002Fp>\u003Cp>But if I were to go in and just copy my address I could do something like this where do items slash contacts and boom I get it ready to use REST or GraphQL API that we can use to build our back end with. That's great, this is really cool. How can we take this further, right, how can we make this application able to adjust itself, right, what does that actually look like? So Directus has built in automation, using flows. So flows you could set up, any type of simple or complex automation for your data.\u003C\u002Fp>\u003Cp>You can make third party API calls, receive incoming webhooks, all of those great things. And I'm thinking we can use a combination of flows and chat GPT or the OpenAI API, that's a mouthful, to actually be able to send this a prompt, have it update the data model automatically. Now there are multiple ways to adjust the schema or your data model of a Directus application. So if we look at the documentation, Directus actually has some schema endpoints where you can go and snapshot your data model. So you make a request, your schema Let's see if we see the sample response.\u003C\u002Fp>\u003Cp>This is the diff, so it actually takes a snapshot of the existing schema and then compares that, with what you've got and comes up with a difference. But it's basically a bunch of JSON data that represents the data model inside our database, inside our direct instance. It is a fairly specific and complex syntax, though. So I'm not sure about OpenAI being able to actually parse this. You know, I'm really concerned about the accuracy for this because it's kind of a a niche syntax that it has to adhere to.\u003C\u002Fp>\u003Cp>Right? So maybe we could go the SQL route because it seems like, OpenAI understands SQL pretty well. Just just guessing. I'm not sure if it does or not. I guess we'll find out.\u003C\u002Fp>\u003Cp>Right? So how can we actually do this? Direct is it doesn't allow you to run raw SQL, rightfully so. Easy way to blow up your database. But I think that's how we're gonna have to do this.\u003C\u002Fp>\u003Cp>So what can we do? Let's reach for a custom extension. So I'm just gonna go to the docs. We're going to create an extension, and so let's pull up our application. I'll just open this up.\u003C\u002Fp>\u003Cp>Alright, so we've got the npx command. Yes, We will create a Directus extension. And there are a couple different types of Directus extensions that you can create. I say a couple, I mean there's a lot of Directus Extensions, but what we're going to do is create an endpoint that we can call and run that actual with a SQL query provided by OpenAI and run that actual query. So first off foremost, Nat, maybe we can get, like, a big disclaimer on here.\u003C\u002Fp>\u003Cp>Do not do this sort of thing in production. Our engineering and security team will probably both be mad at me for this, but we're going to do the custom endpoint extension. What are we gonna call this? Let's just call it raw SQL is the name of our extension. We can use typescript, that's fine.\u003C\u002Fp>\u003Cp>And boom. So Directus will go through and scaffold out this extension. I could see it here inside my raw SQL. Cool. Alright.\u003C\u002Fp>\u003Cp>So now what I wanna do, I'm just gonna drag and drop this into our extensions endpoints folder directory and I'm gonna do one other thing here as well. When we actually build this extension, I'm just gonna drop it into the root of that raw SQL directory, instead of a distribution or dist directory, because it has to be inside the root directory for Directus to pick up on that. Alright. So now we're gonna go to extensions slash what endpoints\u002Frawsequel. Okay.\u003C\u002Fp>\u003Cp>We're gonna do npm run dev, or it says npm run dev. So let's run that. And one of the other things that I've got, if you are working locally with Directus, you're building extensions, probably one of the things that you wanna do inside your config is set up this extensions auto reload, which basically means anytime that you rebuild that extension, which, you know, we're doing here, it will reload that without having to restart the direct assistance for you, which is nice when you are building. Alright. So if we take a look at our existing extension here, we hit save.\u003C\u002Fp>\u003Cp>It's gonna build that endpoint for us. And looks like Directus is reloading extensions. So now if I go to our Directus URL, local host 8055, and I do raw SQL, we get this hello world. Right? Great.\u003C\u002Fp>\u003Cp>But what do we want to actually do now? We want to actually run a a raw SQL query. So how do we actually make that work? How do we get that done? Underneath the hood, Directus is currently using a query builder called connects or nex or that.\u003C\u002Fp>\u003Cp>I never know how to say this thing, if you pronounce the k or not. But it does have a raw it has a raw query object or a raw method that you can call to actually run a raw statement against your database. Again, giant disclaimer, the lawyers say don't do this in production because it is super easy to blow up your database, and also don't trust AI fully to build your application for you. Lots of red flags on this one, but should be interesting nonetheless. Right?\u003C\u002Fp>\u003Cp>So what are we gonna do? We are going to look for our custom endpoints, and Directus gives us access to that underlying database instance. So the endpoint receives the router and the context. So if we do something like this, we've got our context, if I can actually type, and we're gonna do let's make this a post route and where we let's just call it raw SQL run. Alright.\u003C\u002Fp>\u003Cp>Cool. So we'll wrap this in a, like, a try catch. And what are we gonna do? We are going to pick up the query from the body. Yeah.\u003C\u002Fp>\u003Cp>Okay. And we are going to do what? We're going to do something like this where we have context dot databasecontext.database.raw, and then we're gonna run that query, and then we are going to return the result. Alright. Let's do our catch error, error dot message.\u003C\u002Fp>\u003Cp>Now let's see what happens if I refresh. Raw SQL, nothing happens on this, but let's go to run. Raw SQL does not exist. That's that's great. We want it to not work when we run that, but let's just open up something like Postman, so we could quickly test this.\u003C\u002Fp>\u003Cp>I I'm not really a curl guy. I always struggle with the command line. Alright. So we will pop this in. We've got raw SQL run, and we should be passing what?\u003C\u002Fp>\u003Cp>A raw JSON. K. And we've got our query. And let's just say select from what contacts. Alright.\u003C\u002Fp>\u003Cp>We hit posts. Raw SQL run does not exist. Why is that? Rawsequel\u002Frun. Why do we not exist?\u003C\u002Fp>\u003Cp>Great question. We are 36 minutes in. Raw SQL run does not exist. Alright. Let's see.\u003C\u002Fp>\u003Cp>We're getting a wait can't oh, the, this needs to be what? Async. Okay. Boom. Alright.\u003C\u002Fp>\u003Cp>So we just ran a raw database query. So you could see that query here, and it is returning the result from that query. Alright. Great. One of the other things that we could do here just to make this a little more robust would be to use the accountability object that gets included inside our services here.\u003C\u002Fp>\u003Cp>Where is this at? So each request we could take a look at the accountability. So we do something like this where only an admin user could do this. So if rec_.accountability ability dotuser.isisadmin? Is that what it is?\u003C\u002Fp>\u003Cp>I think that's it. Only admins can run raw SQL queries. Did I spell that right? Req.accountability. Let's see.\u003C\u002Fp>\u003Cp>Only admins can run raw SQL queries, so now we have to be logged in to actually run that. And, cool. So now if I go to our admin user and just generate an access token, and if I pass that in our authentication here as a bearer token. Okay. Cool.\u003C\u002Fp>\u003Cp>So we've added a little bit of auth to this endpoint to, you know, run our our SQL queries against the production database. Great. Alright. So now we've got our application here, and I refresh. I could see we've got contacts, deals, organizations.\u003C\u002Fp>\u003Cp>How we doing on time? Let's go in and let's add a new collection. We're just gonna call it, what, app, transformations, mighty morphin' power rangers. You know, we could probably even call it, like, AI migrations or something like that. Right?\u003C\u002Fp>\u003Cp>We'll generate a type for it. Do we really need these extra fields? Maybe we can do that. Alright. Alright.\u003C\u002Fp>\u003Cp>So let's give a let's have a here's the prompt. And I'm trying to think of ways that we could make this more robust. Let's see the SQL. Let's call this a query, and let's add another field. Maybe we could ask OpenAI to give us a way to undo.\u003C\u002Fp>\u003Cp>Okay. Alright. So now if we do this, Yeah. Maybe we should have had that status field as well. Has it been applied or not?\u003C\u002Fp>\u003Cp>So let's just do a drop down. So status, we'll do applied, unapplied, unapplied. Okay. And then any default values here will be unapplied. Cool.\u003C\u002Fp>\u003Cp>Alright. Great. Okay. So now we've got our AI migrations. You know, I I could go with the chart thing, but we could also make this fun where anybody can create new data for those or new applications.\u003C\u002Fp>\u003Cp>So let's do let's start by building our flow first. Alright. So flow flows are how we automate data inside Directus. So let's create a new flow, and we'll say call open AI, and we could trigger this a couple of different ways. We could trigger it manually or we could do it on a event hook in this case.\u003C\u002Fp>\u003Cp>So when a different event happens inside the platform, we trigger this flow. Let's do the action non blocking, and then we'll do anytime we create a new item in AI migrations, we'll trigger this flow. So I'm just gonna stop here. We'll go to AI migrations and we'll say create a postgrace data model for an LMS. Right?\u003C\u002Fp>\u003Cp>So I save this and if I go back to my flow and if I actually make this a little larger where you could see it, I can see my logs over here on the right hand side. Here's our payload. There's the prompt, and then we could see other things like our accountability. Is this a logged in user or not? Great.\u003C\u002Fp>\u003Cp>Alright. So let's move on to the next step. Right? Once we've got that prompt, we'd wanna pass that to OpenAI. So let's call, the the the what?\u003C\u002Fp>\u003Cp>OpenAI? I I don't even know what to call this. Call AI. We're gonna call the API, and we are looking for the webhook and request URL. So we'll go in and I'm pretty sure this will be a post request, but let's just open up OpenAI.\u003C\u002Fp>\u003Cp>Let's open up their platform. Got our API keys. So we're gonna need a new API key. This will be deleted by the time you guys watch, so please don't try to steal my API keys here. But if we look at, their documentation, right, we've got the different models like GPT 4.\u003C\u002Fp>\u003Cp>That's probably the the best one that we wanna use. GPT 4 turbo improved instruction following. So maybe that's the model we wanna use. Let's do this. Chat completions.\u003C\u002Fp>\u003Cp>So if we're doing this, we curl. Alright. Here's the endpoint that we're probably going to hit. Great. Okay.\u003C\u002Fp>\u003Cp>Alright. So if I just separate these 2, we'll drag that over. Get our text generation. So let's copy this URL. There's our chat completions.\u003C\u002Fp>\u003Cp>For our headers, we're gonna have content dash type. Got application JSON. Alright. And then we have our authorization. We have bear bear, and I got my API key.\u003C\u002Fp>\u003Cp>I'll just copy paste that there. Alright. So in the body of the request is where the meat and potatoes are here. Alright. So for our model, let's use the most expensive one.\u003C\u002Fp>\u003Cp>Right? Where's our models at? GPT, GPT 4 turbo. That one has a vision. Okay.\u003C\u002Fp>\u003Cp>Great. Let's just use this guy here, 1106. Fancy, fancy. Alright. So we've got our model that we're gonna use, and then we give it a prompt.\u003C\u002Fp>\u003Cp>Right? You are a helpful assistant, user assistant. Let me just edit this in my text editor real quick. Alright. So we're gonna do something like this where this is gonna be our trigger dot payload dot prompt.\u003C\u002Fp>\u003Cp>We'll delete the rest of these, and then let's give it some system instructions. Right? But let's use chat GPT for that as well. Right? So write some instructions for, chat, GPT 4 model, write some system instructions that will always make it generate SQL queries that, create well well rounded data models for apps as described by users.\u003C\u002Fp>\u003Cp>Let's see what it comes back with this. Really weird kind of having a conversation with AI in this case. Blah blah blah. Normalization. Include comments.\u003C\u002Fp>\u003Cp>Is this actually going to give me something or not? Let's take a look at where we're at. We got 25 minutes left. Chat GPT here is, like, killing me with the details here. Alright.\u003C\u002Fp>\u003Cp>So I guess we could just go with this. Let's just type something out. You are a Postgres and Directus expert full stack developer. Users will describe an application, and you will write a SQL query that will build the data model for that application. Use your expert knowledge to fill, to create a more robust application than the user has described has described.\u003C\u002Fp>\u003Cp>Do not use composite keys. Use only UID for primary keys. Call the primary key fields. What? ID.\u003C\u002Fp>\u003Cp>Use your best judgment to create the data model. Okay. Sounds good. There's our prompt. Okay.\u003C\u002Fp>\u003Cp>We'll just paste this into the body. Hit save. And let's just look at this real quick. There's our payload. Trigger payload prompt.\u003C\u002Fp>\u003Cp>Okay. Alright. Let's see what we're gonna get back from here, and then maybe I just want to go in and actually update that. So we'll say update migration. And maybe we can make this a 2 step process where, would maybe we'll just run it.\u003C\u002Fp>\u003Cp>We'll see. So we got update data. Let's go into our migration, and then we have trigger dot key. And the payload here is going to be query, and what did we call that? Call let's just do this.\u003C\u002Fp>\u003Cp>Just leave that blank for now. Let's give it a shot. Right? Let's see what happens. Please help me build a LMS.\u003C\u002Fp>\u003Cp>There are many courses. Each course has several modules and each module has many lessons. Okay. So if I save this, that should call the OpenAI API and return some data or return something. Right?\u003C\u002Fp>\u003Cp>Why are we not getting any logs here? Did this actually happen? What's going on here? Alright. Let's try it one more time.\u003C\u002Fp>\u003Cp>Or maybe it's still still building. What's going on? Not sure. We are at 21 minutes remaining. Okay.\u003C\u002Fp>\u003Cp>Yeah. It just takes a little while. Alright. So here's our payload from OpenAI. Okay.\u003C\u002Fp>\u003Cp>So we can already see there's a a problem here. Right? It is returning it's returning additional stuff, which kinda sucks. So we need to adjust our prompt for that. Right.\u003C\u002Fp>\u003Cp>You will only return the SQL query. And what was that I saw about JSON mode? Maybe that is something we can use here. Text generation, JSON mode. Okay.\u003C\u002Fp>\u003Cp>Yeah. That's probably what we need to enable. How do we do that? Response format type JSON objects. Okay.\u003C\u002Fp>\u003Cp>Let's give this a shot, shall we? Alright. So save this. And maybe I will, let me just delete these other prompts that we've got. And, actually, we could probably we've got enough data now that we should be able to pick this up, get choices back.\u003C\u002Fp>\u003Cp>Okay. So we would just have to access this, but let's run it one more time just to see. Let me build an LMS. There are many courses. Each course has several modules.\u003C\u002Fp>\u003Cp>Blah blah blah blah blah blah. So we are waiting for chat GPT to come back. But while we do that, oh, less than a minute. That's quick. Bad request.\u003C\u002Fp>\u003Cp>The dumb dumb. Okay. Response format, JSON, JSON object, messages. Goofed something up. We'll just leave that there.\u003C\u002Fp>\u003Cp>This is valid JSON. Looks like it. Let's try again. Alright. Delete.\u003C\u002Fp>\u003Cp>Okay. Prompt. Flows. Logs. Okay.\u003C\u002Fp>\u003Cp>So now it's doing its thing. What could we do next? Right? We're gonna run that SQL that it returns against that endpoint automatically. Kind of a scary thing, but let's see what we've got.\u003C\u002Fp>\u003Cp>Has it returned yet? Nope. Still hasn't returned. Alright. We'll wait on that.\u003C\u002Fp>\u003Cp>There we go. Okay. Alright. So was the payload coming back? Okay.\u003C\u002Fp>\u003Cp>So there's the SQL statement. Okay. And Alright. So it's just returning the query. When using JSON mode, produce some JSON via message for conversation.\u003C\u002Fp>\u003Cp>Response format. Yeah. I feel like we need to set this response format. I'm not sure what I did wrong the last time. Model response format type JSON object.\u003C\u002Fp>\u003Cp>This is invalid JSON or something? Not sure. It looks fine to me. Let's try it one more time. Otherwise, we'll just have to do something more interesting.\u003C\u002Fp>\u003Cp>I would just have to like replace those last 2 or 3 characters. Clipboard. Help me build an LMS. Cool. Alright.\u003C\u002Fp>\u003Cp>If this bricks, we'll just go back to what it was. Yep. Bad request. We cannot parse the JSON body of your request. Yeah.\u003C\u002Fp>\u003Cp>I don't I don't know why why it's doing that. Alright. Okay. So, anyway, there's our our data. Alright.\u003C\u002Fp>\u003Cp>So what are we getting back from the API when this actually works? We're getting something like this. So let's do a I'm just gonna copy and paste this. So here's our response. K.\u003C\u002Fp>\u003Cp>Great. Alright. So let's do an intermediate step where we're just gonna clean this data up. We'll go into run script, clean up query, and we're gonna do what? We're gonna get the data.\u003C\u002Fp>\u003Cp>So the query equals data dotco underscore call AI dot what? Dot data dot choices, first item in the array dot message dot content. Okay. And then we are going to, what, remove the first three characters. Right?\u003C\u002Fp>\u003Cp>Okay. And let's just use AI again. Right? Fun. Fun.\u003C\u002Fp>\u003Cp>Fun. Where did OpenAI go? Alright. Chat GPT. Let's create a new conversation.\u003C\u002Fp>\u003Cp>Maybe we could use 3.5 for this because it's faster. Write a JavaScript function to remove the 3 backticksandthe. SQL. Okay. Cool.\u003C\u002Fp>\u003Cp>So remove the back ticks in the SQL code. Cool. Alright. There we go. There's our cleaned SQL code function, and then we're just gonna return that.\u003C\u002Fp>\u003Cp>Return cleaned SQL code. Alright. So let's make sure that's gonna get what we need. We got data, We got replace content, choices message content. Okay.\u003C\u002Fp>\u003Cp>Let's give it a shot. Okay. And now for the next trick, we are going to post that, to our endpoint. Right? So we will grab our local host.\u003C\u002Fp>\u003Cp>We're gonna do raw SQL slash run. It's gonna be a post. We're gonna do authorization bearer and this is gonna be our token that we used here. Alright. So this is the token for our direct as user.\u003C\u002Fp>\u003Cp>What else do we need? Do we need anything else? Authorization. We probably ought to add content type. Application slash JSON.\u003C\u002Fp>\u003Cp>Great. Alright. Okay. So what are we looking at? What this should do is anytime we create a migration it will call OpenAI, ask it to generate that SQL, clean up that SQL, and then run that SQL command against our database.\u003C\u002Fp>\u003Cp>So if this goes right, it could basically be an application that could build itself. If it goes wrong, it's gonna blow this thing up entirely. Alright. So we got about 11 minutes left. Let's just test this out.\u003C\u002Fp>\u003Cp>Right? We've got our LMS prompt. Let's do it and see. Help me build an LMS. Each course has several modules.\u003C\u002Fp>\u003Cp>Each module has several lessons. Alright. Flows. Okay. So it hasn't come back yet.\u003C\u002Fp>\u003Cp>It is running. If I pop open Directus, let's open our Docker container. Go to the dashboard. We got Docker running here. We got our 100 apps.\u003C\u002Fp>\u003Cp>I can see the activity here, but we're not sure what's going on. Alright. So still running still running. We take a look at our data model. Nothing's happening yet.\u003C\u002Fp>\u003Cp>Here we go. Okay. So we got the data back from OpenAI. SQL code is not defined. Okay.\u003C\u002Fp>\u003Cp>So some issues, and it's still not returning what we need. The raw SQL query do not return anything else except for the raw SQL query. So sometimes AI is unreliable, and then we have what I must have goofed when I SQL code dot replace. Oh, duh. That's a query.\u003C\u002Fp>\u003Cp>Okay. Alright. Let's try again. Just delete this guy. Now this could get really fun where, like, you have AI itself, like on a cron job or something, just manually creating these prompts or automatically creating these prompts.\u003C\u002Fp>\u003Cp>So k. That kicks everything off. What are we gonna do in the meantime while we wait? Right? I don't know.\u003C\u002Fp>\u003Cp>I've got a a list of dad jokes that we could go through here if you guys want. I struggle with Roman numerals until I get to 159, then it just clicks. C l I x. Yeah. Okay.\u003C\u002Fp>\u003Cp>So if I look, here is my raw SQL that ran. If I look at the data model, no. Close. Alright. So let's look at our logs.\u003C\u002Fp>\u003Cp>Here's a minute ago. Internal server error cannot read properties of replace. Okay. So there's our raw SQL. Did I forget to actually call the SQL?\u003C\u002Fp>\u003Cp>What an idiot. Sometimes you have brain farts. Alright. So I forgot to actually paste in the the query there, so it wasn't running anything. So this is gonna be cleanup query.\u003C\u002Fp>\u003Cp>Alright. So if you remember, we are passing a query string into this. So we'll do this, clean up query, and that should solve the problem. Alright. Last time.\u003C\u002Fp>\u003Cp>Right? Hopefully this goes through. Help me build the LMS. Fun with AI and code. How we doing on time?\u003C\u002Fp>\u003Cp>Got 7 minutes left to prove this concept. All the engineers on our team are probably very upset if they're watching this. They put a lot of hard work into security and and making sure people don't do silly things like this. Alright. So webhook request, bad request, invalid payload.\u003C\u002Fp>\u003Cp>Why is it an invalid payload? JSON. Body query. Should we just wrap that in a what do we need to do here? Alright.\u003C\u002Fp>\u003Cp>So we reformat dot replace. Should we that bit? I don't know if we need to JSON stringify that. And I guess we could. Return JSON dot stringify.\u003C\u002Fp>\u003Cp>Not sure what that's gonna do. And then maybe even let's at least save that query so we can pull it up later. Right? So we'll update that. We'll do this with the, what, cleanup request.\u003C\u002Fp>\u003Cp>Cleanup query. Alright. Working on a time crunch here. What's going on? Fields to resolve.\u003C\u002Fp>\u003Cp>Save. Edit. Some kind of weird behavior happening. We save JSON Unexpected token. I don't know.\u003C\u002Fp>\u003Cp>Let's try it again. Help me build an LMS. Flows. Flows. Flows.\u003C\u002Fp>\u003Cp>Alright. So we'll just wait a minute. So also think about these AI. They take a little time, especially the the really fancy models. Right?\u003C\u002Fp>\u003Cp>Logs less than a minute ago. Bad request. Why can't we get this to run? Unexpected token. Payload.\u003C\u002Fp>\u003Cp>Did it update? Let's just look at the okay. So if we were to pop this in there, assuming we remove this, if we were to pop this inside table plus. Right? So if we open up our database, we go in, we run this SQL query.\u003C\u002Fp>\u003Cp>Does this actually work? Run all. Okay. So it doesn't really jive. Okay.\u003C\u002Fp>\u003Cp>So whatever we had previously should be what we go back to. Alright. When in doubt, turn to AI? I don't know, man. This one is challenging.\u003C\u002Fp>\u003Cp>I I thought we could totally get this done. We are not able to pass this. Let's just do this where we go into our query. We have return. Query.\u003C\u002Fp>\u003Cp>That's gonna be our cleaned SQL code. Alright. So we're returning that, and then we're gonna pass that cleanup query directly. So if I go into edit raw value, clean up query. Okay.\u003C\u002Fp>\u003Cp>And then I'm just gonna disconnect this piece for now. Man. Okay. Cutting it against the clock. I don't I don't know why I'm always up against the clock on this particular show.\u003C\u002Fp>\u003Cp>Alright. Let's try something else. Right? Help me build a CRM. No.\u003C\u002Fp>\u003Cp>Build an ecommerce platform for selling watches. Products, categories, prices. Let's just see what it comes back with. Flows less than a minute. Call OpenAI.\u003C\u002Fp>\u003Cp>Bad request. Okay. Why is that? Let's just try the standby here. Help me build.\u003C\u002Fp>\u003Cp>Alright. Okay. So that's doing its thing. We will try this as well, and otherwise, if it doesn't come back in the next 50 seconds, it feels like a public fail to me. Man, I was really hoping we could get this one to work properly.\u003C\u002Fp>\u003Cp>Sometimes it doesn't work out though. This one would be one to do a follow-up on though. Right? Oh, boy. Okay.\u003C\u002Fp>\u003Cp>Okay. Okay. Let's see what we got here. Right? Did this actually do what it wanted to do?\u003C\u002Fp>\u003Cp>Oh, there we go. How about that? Right? So now we've got our courses. Let's take a look.\u003C\u002Fp>\u003Cp>We've got a title and description. If we look at our lessons, what do we have? We've got title and content. We've got our modules that has our course relationship all ready to go for us. Right?\u003C\u002Fp>\u003Cp>Woah. Look at that, guys. At the buzzer, we came through. This is now a mighty morphing app Ranger that will build itself. Right?\u003C\u002Fp>\u003Cp>What can we do next with this? We could go in and create these prompts, but I could go in and do this, like, call a cron job. Let's just explore it further. I just wanted to take this, just to to one one more stop. So if we do create new prompts and at what, let's just make it whatever.\u003C\u002Fp>\u003Cp>I forget the syntax. What is the Google what's the Kron syntax? I wanna say it's like a we use the 6 digits syntax. Okay. Seconds, minutes, hours.\u003C\u002Fp>\u003Cp>Okay. So, again, chat gpt. Just for fun, what is create a cron syntax statement that will run every 2 minutes. Okay. Alright.\u003C\u002Fp>\u003Cp>So running every 2 minutes. There we go. Got that. So I'm just gonna go back into my Directus application. Where are you?\u003C\u002Fp>\u003Cp>Where did it go? Got too much going on. Is it over here? Okay. Alright.\u003C\u002Fp>\u003Cp>So we've got our cron syntax. That's gonna trigger and then but what if we call OpenAI again? Okay. I tell you what, let's do this on a follow-up. We're gonna eat up a lot of time here, but, just to prove that this is working again, let's do please help me build an ecommerce platform for selling watches.\u003C\u002Fp>\u003Cp>Pricing, let's just leave it at that. Right? Let's see what it does. Call create new prompts 3 minutes ago. Okay.\u003C\u002Fp>\u003Cp>This is coming back. And, again, I could watch my directus instance just to see if it is running that SQL statement. I don't see it running just yet. OpenAI is still thinking. But, wow, guys.\u003C\u002Fp>\u003Cp>This one was really fun. Pretty crazy what you can generate with AI. I hope you guys enjoyed this one. Stay tuned for more episodes in the series. Did it actually work?\u003C\u002Fp>\u003Cp>500 internal server error. What happened? Please make sure. Okay. It tried to kick something else out to to that.\u003C\u002Fp>\u003Cp>So it requires some fine tuning on the AI, but I think this is totally viable and totally an interesting project to build an app that can update and change itself. So that's it for this episode. Thanks for sticking around. Catch you on the next episode.\u003C\u002Fp>","Hi guys. Welcome back to another episode of 100 apps 100 hours. I'm your host Brian Gillespie, developer advocate at Directus, and today we are tackling the elephant in the room, AI. So we're gonna be building an AI app generator. By that, I mean an app that can update itself with new data models and new interesting ways to work. So what's the inspiration for this? So I've been, over the last couple weeks, I've been seeing all these videos on X or Twitter, whatever it is, that show, this application TL draw where you can go in and sketch something out inside this quick little application, click a button, and it will use the open AI vision API and some of their other tools, I guess, to actually generate real code that makes whatever you sketch come to life. So really excited by that. We're gonna try to replicate something similar here. I'm calling this the mighty morphing App Ranger because I grew up with Power Rangers. It was one of the I think it was one of my first Halloweens. So if you're new to the series, let's dive in. There are 2 rules. We have 60 minutes to plan and build this application, no more no less, and the second rule is just use whatever you have at your disposal. So in this AI episode it should be pretty interesting to see how far we can get with this. One of the ways that I wanna start here is just by charting things out, and we'll try to upload that chart to chat gpt or open AI and and spit something back out that we can use. So without further ado, let's dive in and start building. So like I said, I like to plan everything first. So let's just draw some stuff on our artboard here and kind of sketch out what we want this actual application to do. So the very beginning we're gonna draw a diagram of diagram diagram of an app. Upload, we want to, what, send that to the application. Let's shrink this down. That's really large. Alright, you can tell even though I use Figma quite a bit it's still not my strong suit. So we'll draw a diagram of an app. We will send, use OpenAI Vision to create a data model based on the diagram, and then we're going to update our app based on that code or data model automatically. So we want the app to automatically update itself, but before we dive into that, let's just try to proof the concept out. Right? So let's draw an app. Let's start with something simple like a CRM. Here's our CRM app. What are the different data models or the different tables, the the different collections as we call them inside Directus, which is what we're gonna be using for our back end and what we're gonna build our application with. So we've got contacts. Well, we probably got some organizations. We've got a relationship between those. We probably have some deals, and we can use some arrows for the relationships. And if we just look at contacts and orgs, we probably have maybe like a junction table or something here. I don't think it left enough room, but let's call this, what? Organization contacts. Give that a little more room. Okay. Alright. So there's our basic diagram. Maybe we go in and inspect some fields out for this as well. We got a first name, last name, email, phone, title. K. We've got a name for the organization. We've probably got an address. We've got, what, what else is on the address? I got lost on my Figma file here. So address, name, we got some contacts. We probably got organizations that those contacts belong to. That's probably good enough. For the deal, we probably have a deal name, deal value. And what else? Close date maybe? Alright. So that is a pretty simple CRM app. Let's make our diagram pretty. And now let's just take a screenshot of this and let's just proof this concept out. We can use just the regular chat GPT for this because it it has that underlying vision API that we're gonna use once we start building our app. But I can upload this into chat GPT and say something like this. You are a SQL, post grace, and direct us master. Please create a SQL query, SQL query that will create the data model for the this CRM application as designed in the in the what, in the diagram attached. Please fill in and make the data model more robust. So, honestly, I'm I'm just curious to see if this will actually work to begin with. What happens? Oh, no. Did I lose all of that? Did that really happen? Open this up. Oh, no. Okay. Well, that's a hazards of the job. Right? Restore recently closed. Please, you are a post grace and directus master. No hidden camera tricks on this show. Just fun here. Sometimes you fat finger one of these. Please create a SQL statement SQL query that will create the data model for our app as in the diagram. No. The conversation is not helpful so far. Let's do a new chat. Restore recently closed. Alright. Sometimes the technology doesn't work out so well. Please create a SQL query for Postgres, that's the database we're using, that will create an app create the data model for an app based on the diagram. Alright. I'm gonna copy this just so I don't have to what in the world is going on? Alright. Let's just try this again. Holy moly. Wow. Okay. So we've eaten up a lot of time there just messing with chat gpt. That's a lot of fun, but let's see what this comes up with. Looks like it has to create a SQL data model, SQL code. So if we just look at this, we've got a contacts table. That's good. We've got the organizations table. We've got a associative table. That's a junction table. We have a table for our deals, and then we have some kind of statement that is altering the table. Okay. Please rewrite to use UUID to add a primary key of ID for all the tables and use UIDs. Just want to make sure we all have a primary key of for each one. Alright. Cool. Okay. So now we've got the IDs. We're using the UUID instead of just an integer there. Alright. Great. Okay. So looking at this, we can test it out. Right? How are we gonna test this out? So, what I've got set up, for this app is basically a Docker Compose file that I'm using to generate my back end, which is Directus. And if I pull up my Directus instance and just log in, so we go to admin atexample.com. We do password. We'll log in and if I zoom in just a little bit you can see this is a completely blank app. Nothing here. So what Directus does behind the scenes, it sits alongside your Postgres database and it will introspect that database, which basically means anything that any changes that you make inside that database or any SQL database, doesn't have to just be Postgres, it will mirror those changes in real time. Sounds great. How does it work in application? Alright. So we go in, let's just pull up this database inside TablePlus. Right? So I could see all of my directus tables here. There's no other tables. We've got some items in here for, like, post GIS, just so we can do geo data inside here. But let's just copy and paste this inside the application, or inside table plus, and we're gonna run this SQL query. The only thing that I'm gonna do here is just change this application. I'm gonna change the organization contacts, And what I'm gonna do here, I'm just gonna change this so that there is a ID and that's the primary key instead of a composite key. But other than that, let's just run this thing. So hit run all. Okay. Altered table has been altered. If I refresh, we can see that we have created those different tables and, you know, the associated columns. Great. Now if I load up Directus, I can see that Directus, just refreshing the screen, has recognized that those tables are inside our database. So I can go through and click to configure these tables. We can see that my different fields are coming through, and if we click on those we can configure what the actual UI would look like for these things. So if I just go through and click on a few of them, maybe we want to hide this specific field. Directus really easily allows you to update this data and the presentation of it. So basically now we're just controlling the form that displays when we add a new contact. So let's just edit that a little bit, put the title down there. Great. Yeah. So if we go in, we refresh, we've got our contacts, and now I could go in and add my contact. Great. 555-555555. Looking great. Brian@directus dotio, and I'm a developer advocate. Cool. So now if I go into table plus here, we can also see there's my record. Right? So Directus is really nice in that it mirrors everything, but it also gives me a REST based API. So I could go in and any of those collections, if I were to, just give read access to just the general public, we could also create different user roles here. But if I were to go in and just copy my address I could do something like this where do items slash contacts and boom I get it ready to use REST or GraphQL API that we can use to build our back end with. That's great, this is really cool. How can we take this further, right, how can we make this application able to adjust itself, right, what does that actually look like? So Directus has built in automation, using flows. So flows you could set up, any type of simple or complex automation for your data. You can make third party API calls, receive incoming webhooks, all of those great things. And I'm thinking we can use a combination of flows and chat GPT or the OpenAI API, that's a mouthful, to actually be able to send this a prompt, have it update the data model automatically. Now there are multiple ways to adjust the schema or your data model of a Directus application. So if we look at the documentation, Directus actually has some schema endpoints where you can go and snapshot your data model. So you make a request, your schema Let's see if we see the sample response. This is the diff, so it actually takes a snapshot of the existing schema and then compares that, with what you've got and comes up with a difference. But it's basically a bunch of JSON data that represents the data model inside our database, inside our direct instance. It is a fairly specific and complex syntax, though. So I'm not sure about OpenAI being able to actually parse this. You know, I'm really concerned about the accuracy for this because it's kind of a a niche syntax that it has to adhere to. Right? So maybe we could go the SQL route because it seems like, OpenAI understands SQL pretty well. Just just guessing. I'm not sure if it does or not. I guess we'll find out. Right? So how can we actually do this? Direct is it doesn't allow you to run raw SQL, rightfully so. Easy way to blow up your database. But I think that's how we're gonna have to do this. So what can we do? Let's reach for a custom extension. So I'm just gonna go to the docs. We're going to create an extension, and so let's pull up our application. I'll just open this up. Alright, so we've got the npx command. Yes, We will create a Directus extension. And there are a couple different types of Directus extensions that you can create. I say a couple, I mean there's a lot of Directus Extensions, but what we're going to do is create an endpoint that we can call and run that actual with a SQL query provided by OpenAI and run that actual query. So first off foremost, Nat, maybe we can get, like, a big disclaimer on here. Do not do this sort of thing in production. Our engineering and security team will probably both be mad at me for this, but we're going to do the custom endpoint extension. What are we gonna call this? Let's just call it raw SQL is the name of our extension. We can use typescript, that's fine. And boom. So Directus will go through and scaffold out this extension. I could see it here inside my raw SQL. Cool. Alright. So now what I wanna do, I'm just gonna drag and drop this into our extensions endpoints folder directory and I'm gonna do one other thing here as well. When we actually build this extension, I'm just gonna drop it into the root of that raw SQL directory, instead of a distribution or dist directory, because it has to be inside the root directory for Directus to pick up on that. Alright. So now we're gonna go to extensions slash what endpoints\u002Frawsequel. Okay. We're gonna do npm run dev, or it says npm run dev. So let's run that. And one of the other things that I've got, if you are working locally with Directus, you're building extensions, probably one of the things that you wanna do inside your config is set up this extensions auto reload, which basically means anytime that you rebuild that extension, which, you know, we're doing here, it will reload that without having to restart the direct assistance for you, which is nice when you are building. Alright. So if we take a look at our existing extension here, we hit save. It's gonna build that endpoint for us. And looks like Directus is reloading extensions. So now if I go to our Directus URL, local host 8055, and I do raw SQL, we get this hello world. Right? Great. But what do we want to actually do now? We want to actually run a a raw SQL query. So how do we actually make that work? How do we get that done? Underneath the hood, Directus is currently using a query builder called connects or nex or that. I never know how to say this thing, if you pronounce the k or not. But it does have a raw it has a raw query object or a raw method that you can call to actually run a raw statement against your database. Again, giant disclaimer, the lawyers say don't do this in production because it is super easy to blow up your database, and also don't trust AI fully to build your application for you. Lots of red flags on this one, but should be interesting nonetheless. Right? So what are we gonna do? We are going to look for our custom endpoints, and Directus gives us access to that underlying database instance. So the endpoint receives the router and the context. So if we do something like this, we've got our context, if I can actually type, and we're gonna do let's make this a post route and where we let's just call it raw SQL run. Alright. Cool. So we'll wrap this in a, like, a try catch. And what are we gonna do? We are going to pick up the query from the body. Yeah. Okay. And we are going to do what? We're going to do something like this where we have context dot databasecontext.database.raw, and then we're gonna run that query, and then we are going to return the result. Alright. Let's do our catch error, error dot message. Now let's see what happens if I refresh. Raw SQL, nothing happens on this, but let's go to run. Raw SQL does not exist. That's that's great. We want it to not work when we run that, but let's just open up something like Postman, so we could quickly test this. I I'm not really a curl guy. I always struggle with the command line. Alright. So we will pop this in. We've got raw SQL run, and we should be passing what? A raw JSON. K. And we've got our query. And let's just say select from what contacts. Alright. We hit posts. Raw SQL run does not exist. Why is that? Rawsequel\u002Frun. Why do we not exist? Great question. We are 36 minutes in. Raw SQL run does not exist. Alright. Let's see. We're getting a wait can't oh, the, this needs to be what? Async. Okay. Boom. Alright. So we just ran a raw database query. So you could see that query here, and it is returning the result from that query. Alright. Great. One of the other things that we could do here just to make this a little more robust would be to use the accountability object that gets included inside our services here. Where is this at? So each request we could take a look at the accountability. So we do something like this where only an admin user could do this. So if rec_.accountability ability dotuser.isisadmin? Is that what it is? I think that's it. Only admins can run raw SQL queries. Did I spell that right? Req.accountability. Let's see. Only admins can run raw SQL queries, so now we have to be logged in to actually run that. And, cool. So now if I go to our admin user and just generate an access token, and if I pass that in our authentication here as a bearer token. Okay. Cool. So we've added a little bit of auth to this endpoint to, you know, run our our SQL queries against the production database. Great. Alright. So now we've got our application here, and I refresh. I could see we've got contacts, deals, organizations. How we doing on time? Let's go in and let's add a new collection. We're just gonna call it, what, app, transformations, mighty morphin' power rangers. You know, we could probably even call it, like, AI migrations or something like that. Right? We'll generate a type for it. Do we really need these extra fields? Maybe we can do that. Alright. Alright. So let's give a let's have a here's the prompt. And I'm trying to think of ways that we could make this more robust. Let's see the SQL. Let's call this a query, and let's add another field. Maybe we could ask OpenAI to give us a way to undo. Okay. Alright. So now if we do this, Yeah. Maybe we should have had that status field as well. Has it been applied or not? So let's just do a drop down. So status, we'll do applied, unapplied, unapplied. Okay. And then any default values here will be unapplied. Cool. Alright. Great. Okay. So now we've got our AI migrations. You know, I I could go with the chart thing, but we could also make this fun where anybody can create new data for those or new applications. So let's do let's start by building our flow first. Alright. So flow flows are how we automate data inside Directus. So let's create a new flow, and we'll say call open AI, and we could trigger this a couple of different ways. We could trigger it manually or we could do it on a event hook in this case. So when a different event happens inside the platform, we trigger this flow. Let's do the action non blocking, and then we'll do anytime we create a new item in AI migrations, we'll trigger this flow. So I'm just gonna stop here. We'll go to AI migrations and we'll say create a postgrace data model for an LMS. Right? So I save this and if I go back to my flow and if I actually make this a little larger where you could see it, I can see my logs over here on the right hand side. Here's our payload. There's the prompt, and then we could see other things like our accountability. Is this a logged in user or not? Great. Alright. So let's move on to the next step. Right? Once we've got that prompt, we'd wanna pass that to OpenAI. So let's call, the the the what? OpenAI? I I don't even know what to call this. Call AI. We're gonna call the API, and we are looking for the webhook and request URL. So we'll go in and I'm pretty sure this will be a post request, but let's just open up OpenAI. Let's open up their platform. Got our API keys. So we're gonna need a new API key. This will be deleted by the time you guys watch, so please don't try to steal my API keys here. But if we look at, their documentation, right, we've got the different models like GPT 4. That's probably the the best one that we wanna use. GPT 4 turbo improved instruction following. So maybe that's the model we wanna use. Let's do this. Chat completions. So if we're doing this, we curl. Alright. Here's the endpoint that we're probably going to hit. Great. Okay. Alright. So if I just separate these 2, we'll drag that over. Get our text generation. So let's copy this URL. There's our chat completions. For our headers, we're gonna have content dash type. Got application JSON. Alright. And then we have our authorization. We have bear bear, and I got my API key. I'll just copy paste that there. Alright. So in the body of the request is where the meat and potatoes are here. Alright. So for our model, let's use the most expensive one. Right? Where's our models at? GPT, GPT 4 turbo. That one has a vision. Okay. Great. Let's just use this guy here, 1106. Fancy, fancy. Alright. So we've got our model that we're gonna use, and then we give it a prompt. Right? You are a helpful assistant, user assistant. Let me just edit this in my text editor real quick. Alright. So we're gonna do something like this where this is gonna be our trigger dot payload dot prompt. We'll delete the rest of these, and then let's give it some system instructions. Right? But let's use chat GPT for that as well. Right? So write some instructions for, chat, GPT 4 model, write some system instructions that will always make it generate SQL queries that, create well well rounded data models for apps as described by users. Let's see what it comes back with this. Really weird kind of having a conversation with AI in this case. Blah blah blah. Normalization. Include comments. Is this actually going to give me something or not? Let's take a look at where we're at. We got 25 minutes left. Chat GPT here is, like, killing me with the details here. Alright. So I guess we could just go with this. Let's just type something out. You are a Postgres and Directus expert full stack developer. Users will describe an application, and you will write a SQL query that will build the data model for that application. Use your expert knowledge to fill, to create a more robust application than the user has described has described. Do not use composite keys. Use only UID for primary keys. Call the primary key fields. What? ID. Use your best judgment to create the data model. Okay. Sounds good. There's our prompt. Okay. We'll just paste this into the body. Hit save. And let's just look at this real quick. There's our payload. Trigger payload prompt. Okay. Alright. Let's see what we're gonna get back from here, and then maybe I just want to go in and actually update that. So we'll say update migration. And maybe we can make this a 2 step process where, would maybe we'll just run it. We'll see. So we got update data. Let's go into our migration, and then we have trigger dot key. And the payload here is going to be query, and what did we call that? Call let's just do this. Just leave that blank for now. Let's give it a shot. Right? Let's see what happens. Please help me build a LMS. There are many courses. Each course has several modules and each module has many lessons. Okay. So if I save this, that should call the OpenAI API and return some data or return something. Right? Why are we not getting any logs here? Did this actually happen? What's going on here? Alright. Let's try it one more time. Or maybe it's still still building. What's going on? Not sure. We are at 21 minutes remaining. Okay. Yeah. It just takes a little while. Alright. So here's our payload from OpenAI. Okay. So we can already see there's a a problem here. Right? It is returning it's returning additional stuff, which kinda sucks. So we need to adjust our prompt for that. Right. You will only return the SQL query. And what was that I saw about JSON mode? Maybe that is something we can use here. Text generation, JSON mode. Okay. Yeah. That's probably what we need to enable. How do we do that? Response format type JSON objects. Okay. Let's give this a shot, shall we? Alright. So save this. And maybe I will, let me just delete these other prompts that we've got. And, actually, we could probably we've got enough data now that we should be able to pick this up, get choices back. Okay. So we would just have to access this, but let's run it one more time just to see. Let me build an LMS. There are many courses. Each course has several modules. Blah blah blah blah blah blah. So we are waiting for chat GPT to come back. But while we do that, oh, less than a minute. That's quick. Bad request. The dumb dumb. Okay. Response format, JSON, JSON object, messages. Goofed something up. We'll just leave that there. This is valid JSON. Looks like it. Let's try again. Alright. Delete. Okay. Prompt. Flows. Logs. Okay. So now it's doing its thing. What could we do next? Right? We're gonna run that SQL that it returns against that endpoint automatically. Kind of a scary thing, but let's see what we've got. Has it returned yet? Nope. Still hasn't returned. Alright. We'll wait on that. There we go. Okay. Alright. So was the payload coming back? Okay. So there's the SQL statement. Okay. And Alright. So it's just returning the query. When using JSON mode, produce some JSON via message for conversation. Response format. Yeah. I feel like we need to set this response format. I'm not sure what I did wrong the last time. Model response format type JSON object. This is invalid JSON or something? Not sure. It looks fine to me. Let's try it one more time. Otherwise, we'll just have to do something more interesting. I would just have to like replace those last 2 or 3 characters. Clipboard. Help me build an LMS. Cool. Alright. If this bricks, we'll just go back to what it was. Yep. Bad request. We cannot parse the JSON body of your request. Yeah. I don't I don't know why why it's doing that. Alright. Okay. So, anyway, there's our our data. Alright. So what are we getting back from the API when this actually works? We're getting something like this. So let's do a I'm just gonna copy and paste this. So here's our response. K. Great. Alright. So let's do an intermediate step where we're just gonna clean this data up. We'll go into run script, clean up query, and we're gonna do what? We're gonna get the data. So the query equals data dotco underscore call AI dot what? Dot data dot choices, first item in the array dot message dot content. Okay. And then we are going to, what, remove the first three characters. Right? Okay. And let's just use AI again. Right? Fun. Fun. Fun. Where did OpenAI go? Alright. Chat GPT. Let's create a new conversation. Maybe we could use 3.5 for this because it's faster. Write a JavaScript function to remove the 3 backticksandthe. SQL. Okay. Cool. So remove the back ticks in the SQL code. Cool. Alright. There we go. There's our cleaned SQL code function, and then we're just gonna return that. Return cleaned SQL code. Alright. So let's make sure that's gonna get what we need. We got data, We got replace content, choices message content. Okay. Let's give it a shot. Okay. And now for the next trick, we are going to post that, to our endpoint. Right? So we will grab our local host. We're gonna do raw SQL slash run. It's gonna be a post. We're gonna do authorization bearer and this is gonna be our token that we used here. Alright. So this is the token for our direct as user. What else do we need? Do we need anything else? Authorization. We probably ought to add content type. Application slash JSON. Great. Alright. Okay. So what are we looking at? What this should do is anytime we create a migration it will call OpenAI, ask it to generate that SQL, clean up that SQL, and then run that SQL command against our database. So if this goes right, it could basically be an application that could build itself. If it goes wrong, it's gonna blow this thing up entirely. Alright. So we got about 11 minutes left. Let's just test this out. Right? We've got our LMS prompt. Let's do it and see. Help me build an LMS. Each course has several modules. Each module has several lessons. Alright. Flows. Okay. So it hasn't come back yet. It is running. If I pop open Directus, let's open our Docker container. Go to the dashboard. We got Docker running here. We got our 100 apps. I can see the activity here, but we're not sure what's going on. Alright. So still running still running. We take a look at our data model. Nothing's happening yet. Here we go. Okay. So we got the data back from OpenAI. SQL code is not defined. Okay. So some issues, and it's still not returning what we need. The raw SQL query do not return anything else except for the raw SQL query. So sometimes AI is unreliable, and then we have what I must have goofed when I SQL code dot replace. Oh, duh. That's a query. Okay. Alright. Let's try again. Just delete this guy. Now this could get really fun where, like, you have AI itself, like on a cron job or something, just manually creating these prompts or automatically creating these prompts. So k. That kicks everything off. What are we gonna do in the meantime while we wait? Right? I don't know. I've got a a list of dad jokes that we could go through here if you guys want. I struggle with Roman numerals until I get to 159, then it just clicks. C l I x. Yeah. Okay. So if I look, here is my raw SQL that ran. If I look at the data model, no. Close. Alright. So let's look at our logs. Here's a minute ago. Internal server error cannot read properties of replace. Okay. So there's our raw SQL. Did I forget to actually call the SQL? What an idiot. Sometimes you have brain farts. Alright. So I forgot to actually paste in the the query there, so it wasn't running anything. So this is gonna be cleanup query. Alright. So if you remember, we are passing a query string into this. So we'll do this, clean up query, and that should solve the problem. Alright. Last time. Right? Hopefully this goes through. Help me build the LMS. Fun with AI and code. How we doing on time? Got 7 minutes left to prove this concept. All the engineers on our team are probably very upset if they're watching this. They put a lot of hard work into security and and making sure people don't do silly things like this. Alright. So webhook request, bad request, invalid payload. Why is it an invalid payload? JSON. Body query. Should we just wrap that in a what do we need to do here? Alright. So we reformat dot replace. Should we that bit? I don't know if we need to JSON stringify that. And I guess we could. Return JSON dot stringify. Not sure what that's gonna do. And then maybe even let's at least save that query so we can pull it up later. Right? So we'll update that. We'll do this with the, what, cleanup request. Cleanup query. Alright. Working on a time crunch here. What's going on? Fields to resolve. Save. Edit. Some kind of weird behavior happening. We save JSON Unexpected token. I don't know. Let's try it again. Help me build an LMS. Flows. Flows. Flows. Alright. So we'll just wait a minute. So also think about these AI. They take a little time, especially the the really fancy models. Right? Logs less than a minute ago. Bad request. Why can't we get this to run? Unexpected token. Payload. Did it update? Let's just look at the okay. So if we were to pop this in there, assuming we remove this, if we were to pop this inside table plus. Right? So if we open up our database, we go in, we run this SQL query. Does this actually work? Run all. Okay. So it doesn't really jive. Okay. So whatever we had previously should be what we go back to. Alright. When in doubt, turn to AI? I don't know, man. This one is challenging. I I thought we could totally get this done. We are not able to pass this. Let's just do this where we go into our query. We have return. Query. That's gonna be our cleaned SQL code. Alright. So we're returning that, and then we're gonna pass that cleanup query directly. So if I go into edit raw value, clean up query. Okay. And then I'm just gonna disconnect this piece for now. Man. Okay. Cutting it against the clock. I don't I don't know why I'm always up against the clock on this particular show. Alright. Let's try something else. Right? Help me build a CRM. No. Build an ecommerce platform for selling watches. Products, categories, prices. Let's just see what it comes back with. Flows less than a minute. Call OpenAI. Bad request. Okay. Why is that? Let's just try the standby here. Help me build. Alright. Okay. So that's doing its thing. We will try this as well, and otherwise, if it doesn't come back in the next 50 seconds, it feels like a public fail to me. Man, I was really hoping we could get this one to work properly. Sometimes it doesn't work out though. This one would be one to do a follow-up on though. Right? Oh, boy. Okay. Okay. Okay. Let's see what we got here. Right? Did this actually do what it wanted to do? Oh, there we go. How about that? Right? So now we've got our courses. Let's take a look. We've got a title and description. If we look at our lessons, what do we have? We've got title and content. We've got our modules that has our course relationship all ready to go for us. Right? Woah. Look at that, guys. At the buzzer, we came through. This is now a mighty morphing app Ranger that will build itself. Right? What can we do next with this? We could go in and create these prompts, but I could go in and do this, like, call a cron job. Let's just explore it further. I just wanted to take this, just to to one one more stop. So if we do create new prompts and at what, let's just make it whatever. I forget the syntax. What is the Google what's the Kron syntax? I wanna say it's like a we use the 6 digits syntax. Okay. Seconds, minutes, hours. Okay. So, again, chat gpt. Just for fun, what is create a cron syntax statement that will run every 2 minutes. Okay. Alright. So running every 2 minutes. There we go. Got that. So I'm just gonna go back into my Directus application. Where are you? Where did it go? Got too much going on. Is it over here? Okay. Alright. So we've got our cron syntax. That's gonna trigger and then but what if we call OpenAI again? Okay. I tell you what, let's do this on a follow-up. We're gonna eat up a lot of time here, but, just to prove that this is working again, let's do please help me build an ecommerce platform for selling watches. Pricing, let's just leave it at that. Right? Let's see what it does. Call create new prompts 3 minutes ago. Okay. This is coming back. And, again, I could watch my directus instance just to see if it is running that SQL statement. I don't see it running just yet. OpenAI is still thinking. But, wow, guys. This one was really fun. Pretty crazy what you can generate with AI. I hope you guys enjoyed this one. Stay tuned for more episodes in the series. Did it actually work? 500 internal server error. What happened? Please make sure. Okay. It tried to kick something else out to to that. So it requires some fine tuning on the AI, but I think this is totally viable and totally an interesting project to build an app that can update and change itself. So that's it for this episode. Thanks for sticking around. Catch you on the next episode.",[5228],"3f644a06-47d4-4ab7-b13c-20825b1df3b3",[],{"year":2380,"number":177,"id":4162,"episodes":5231,"show":5232},[4164,4165,4166,4167,4168,4169,4170,4171,4172,4173,4137],{"title":322,"slug":323},{"id":4609,"slug":5234,"vimeo_id":5235,"description":5236,"tile":5237,"length":11,"resources":5238,"people":5242,"episode_number":264,"published":5244,"title":5245,"video_transcript_html":5246,"video_transcript_text":5247,"content":12,"seo":12,"status":19,"episode_people":5248,"recommendations":5250,"season":5251},"clearbit","895919599","Automatically enrich new user profiles with rich data from Clearbit.","941881a9-2f6c-4e88-90c4-d27c6be2346e",[5239],{"name":5240,"url":5241},"Clearbit Enrichment API","https:\u002F\u002Fclearbit.com\u002Fplatform\u002Fenrichment",[5243],{"name":2820,"url":2821},"2024-01-12","Enriching User Profiles with Clearbit","\u003Cp>Speaker 0: Clearbit provides an enrichment API that lets you send off just an email address for a person, and it will return a much more full contact containing all the information that Clearbit provides. In this episode of Quick Connect, we're going to automatically enrich data from new users in our director's project. So with that, let's get started. The first thing you'll need is a Clearbit API key, which you can get inside of the Clearbit dashboard. It's called the secret API key, so just go ahead and copy that.\u003C\u002Fp>\u003Cp>And be certain that no one else ever gets hold of this key. Otherwise, they can also perform requests against your Clearbit account. I save this for later, and let's head back over to our Director's project to set up our connection. Here in our directors project, let's create a new flow. I'm gonna call this one clearbit enrichment.\u003C\u002Fp>\u003Cp>For the trigger, we're going to use an event hook and make this action non blocking. That means that an item, a user will be entered into the database and the clear bit enrichment will happen in parallel, which means we don't slow down or otherwise prevent the user creation from taking place. The scope is going to be items dot create on the directors users collection. Hit save to set up our trigger. Before we continue, we are going to see what the shape of the data is when a new user is created.\u003C\u002Fp>\u003Cp>So let's save this flow, head over to our user module, and create a new user. I'll just call this one joeblogs, very original, joe@example.com, and hit save. Let's go back over to our flow and we'll see here that there is a one inside of the logs. So this is one time that this flow has been triggered. We see here in the payload that we get a first name, a last name, and an email, and indeed any other information provided on creation of this user.\u003C\u002Fp>\u003Cp>So take a note here that we have payload email. This is where the email lies, which we're going to need in a moment to use Clearbit. Also, take a note that this user, this item in the director's users collection has a unique key. We'll also need this later when we update the user record with the Clearbit data. The next thing we're gonna do is create an operation I'm gonna call this one clear bit and take note that the key is also Clearbit or lowercase.\u003C\u002Fp>\u003Cp>This key will be important later. We're going to request a URL, and we're going to send a get request to the Clearbit API. So as per the Clearbit documentation, we're using the email people enrichment endpoint. So person.clearbit.comvone\u002Fpeople\u002Femail, and then a dynamic value based on the value of the trigger. So just before it was joe@example.com.\u003C\u002Fp>\u003Cp>Now to authenticate with Clearbit, we're going to need to add a header. So we're gonna go ahead and do that. The header name is authorization, and the value is bearer space, and then your Clearbit API key. Once again, make sure other people don't see this key as they don't provide a way to invalidate and regenerate an API key manually. So let's go ahead and give this a test.\u003C\u002Fp>\u003Cp>Let's hit save. Let's go back to our user module. Let's create a new user. I'm going to use our CEO Ben's email address here and hit save. Let's go back to our flows.\u003C\u002Fp>\u003Cp>Let's look at our latest log here, and let's look at the returned payload from Clearbit. We see that it was a successful request. There's an there's a property called data, which is an object. And inside of this, a large object containing all the information that Clearbit holds on, Ben. So we have things like well, it's the Gravatar URL, so that's like the the avatar, LinkedIn usernames.\u003C\u002Fp>\u003Cp>We don't have a Twitter handle, but that would be here. GitHub username, Facebook. We also have things like where are they based, what's their time zone offset, and stuff like that. The website direct to. Now what we're gonna do just to demonstrate how you would then go and update a user is we're just gonna take the location here, and we're going to update Ben's brand new user profile with the location.\u003C\u002Fp>\u003Cp>If we take a look back at the user directory, a user object here, we see there's a bunch of provided fields, location, title, description, tags, and so on. You can add custom fields inside of the data model settings in your director's project. But seeing as location already exists, we'll just use that. So let's add one more operation here, and we're gonna make this an update data operation. I'm gonna go in here.\u003C\u002Fp>\u003Cp>And first thing we're gonna do is say we're gonna update an item in the directors users collection. The ID of the user we're updating will be trigger dot key. Remember I mentioned that earlier on? And the payload, the data we will actually be updating is the location. And we said that this would be clear bit dot data dot location.\u003C\u002Fp>\u003Cp>It's clear bit because that was the key on this step here that return the data. So let's hit save, save once more, and run this flow again. Just to keep this easy, I'm gonna delete this user and then recreate it so we don't get confused. Perfect. We'll hit save.\u003C\u002Fp>\u003Cp>In the background the moment I hit save the user was saved but also the Clearbit operation was run Firstly, we see that it did indeed run here. If I look at that user item, we see the location has been updated. So you can, of course, update as many fields as you want, including your custom fields. Now there are a few notes I wanna make once as we get to the end of this little guide here. Firstly, Clearbit provides no guarantee around what data is going to be returned on an individual.\u003C\u002Fp>\u003Cp>So what you may wanna do is add an additional step here between the request and the update data to check what exists and only add that into the payload. The reason that's important is if there is a value which is null or empty and it already exists in your director's project, it will be overwritten by the empty value. So you wanna add an extra step in there. Next, you have to be cognizant of the fact that every time you make a request to clear it, it's using up API quota or maybe costing money if you have paid agreements with them. So you may not wanna do this automatically on every user.\u003C\u002Fp>\u003Cp>You may want to do this manually. Perhaps you add a button on a user profile that will trigger this, or you do it based on some other event, like they get in touch with the salesperson or there's a record created in another collection or so on. The final thing to note is today we use the enrichment API that Clearbit provides. They have an additional API called the combined enrichment API that will also return a bunch of information about a company. So that could be useful.\u003C\u002Fp>\u003Cp>You know, you create a collection full of companies, and then you link users directly to their companies automatically using the ClearBit data. So I hope you enjoyed this episode of quick connect I'll see you next time\u003C\u002Fp>","Clearbit provides an enrichment API that lets you send off just an email address for a person, and it will return a much more full contact containing all the information that Clearbit provides. In this episode of Quick Connect, we're going to automatically enrich data from new users in our director's project. So with that, let's get started. The first thing you'll need is a Clearbit API key, which you can get inside of the Clearbit dashboard. It's called the secret API key, so just go ahead and copy that. And be certain that no one else ever gets hold of this key. Otherwise, they can also perform requests against your Clearbit account. I save this for later, and let's head back over to our Director's project to set up our connection. Here in our directors project, let's create a new flow. I'm gonna call this one clearbit enrichment. For the trigger, we're going to use an event hook and make this action non blocking. That means that an item, a user will be entered into the database and the clear bit enrichment will happen in parallel, which means we don't slow down or otherwise prevent the user creation from taking place. The scope is going to be items dot create on the directors users collection. Hit save to set up our trigger. Before we continue, we are going to see what the shape of the data is when a new user is created. So let's save this flow, head over to our user module, and create a new user. I'll just call this one joeblogs, very original, joe@example.com, and hit save. Let's go back over to our flow and we'll see here that there is a one inside of the logs. So this is one time that this flow has been triggered. We see here in the payload that we get a first name, a last name, and an email, and indeed any other information provided on creation of this user. So take a note here that we have payload email. This is where the email lies, which we're going to need in a moment to use Clearbit. Also, take a note that this user, this item in the director's users collection has a unique key. We'll also need this later when we update the user record with the Clearbit data. The next thing we're gonna do is create an operation I'm gonna call this one clear bit and take note that the key is also Clearbit or lowercase. This key will be important later. We're going to request a URL, and we're going to send a get request to the Clearbit API. So as per the Clearbit documentation, we're using the email people enrichment endpoint. So person.clearbit.comvone\u002Fpeople\u002Femail, and then a dynamic value based on the value of the trigger. So just before it was joe@example.com. Now to authenticate with Clearbit, we're going to need to add a header. So we're gonna go ahead and do that. The header name is authorization, and the value is bearer space, and then your Clearbit API key. Once again, make sure other people don't see this key as they don't provide a way to invalidate and regenerate an API key manually. So let's go ahead and give this a test. Let's hit save. Let's go back to our user module. Let's create a new user. I'm going to use our CEO Ben's email address here and hit save. Let's go back to our flows. Let's look at our latest log here, and let's look at the returned payload from Clearbit. We see that it was a successful request. There's an there's a property called data, which is an object. And inside of this, a large object containing all the information that Clearbit holds on, Ben. So we have things like well, it's the Gravatar URL, so that's like the the avatar, LinkedIn usernames. We don't have a Twitter handle, but that would be here. GitHub username, Facebook. We also have things like where are they based, what's their time zone offset, and stuff like that. The website direct to. Now what we're gonna do just to demonstrate how you would then go and update a user is we're just gonna take the location here, and we're going to update Ben's brand new user profile with the location. If we take a look back at the user directory, a user object here, we see there's a bunch of provided fields, location, title, description, tags, and so on. You can add custom fields inside of the data model settings in your director's project. But seeing as location already exists, we'll just use that. So let's add one more operation here, and we're gonna make this an update data operation. I'm gonna go in here. And first thing we're gonna do is say we're gonna update an item in the directors users collection. The ID of the user we're updating will be trigger dot key. Remember I mentioned that earlier on? And the payload, the data we will actually be updating is the location. And we said that this would be clear bit dot data dot location. It's clear bit because that was the key on this step here that return the data. So let's hit save, save once more, and run this flow again. Just to keep this easy, I'm gonna delete this user and then recreate it so we don't get confused. Perfect. We'll hit save. In the background the moment I hit save the user was saved but also the Clearbit operation was run Firstly, we see that it did indeed run here. If I look at that user item, we see the location has been updated. So you can, of course, update as many fields as you want, including your custom fields. Now there are a few notes I wanna make once as we get to the end of this little guide here. Firstly, Clearbit provides no guarantee around what data is going to be returned on an individual. So what you may wanna do is add an additional step here between the request and the update data to check what exists and only add that into the payload. The reason that's important is if there is a value which is null or empty and it already exists in your director's project, it will be overwritten by the empty value. So you wanna add an extra step in there. Next, you have to be cognizant of the fact that every time you make a request to clear it, it's using up API quota or maybe costing money if you have paid agreements with them. So you may not wanna do this automatically on every user. You may want to do this manually. Perhaps you add a button on a user profile that will trigger this, or you do it based on some other event, like they get in touch with the salesperson or there's a record created in another collection or so on. The final thing to note is today we use the enrichment API that Clearbit provides. They have an additional API called the combined enrichment API that will also return a bunch of information about a company. So that could be useful. You know, you create a collection full of companies, and then you link users directly to their companies automatically using the ClearBit data. So I hope you enjoyed this episode of quick connect I'll see you next time",[5249],"09b8019b-836c-466a-b39f-ccab18a909ce",[],{"year":2380,"number":194,"id":4605,"episodes":5252,"show":5253},[4607,4608,4609,4610,4611,4612,4613,4590],{"title":1746,"slug":1747},{"id":4542,"slug":5255,"vimeo_id":5256,"description":5257,"tile":5258,"length":2706,"resources":12,"people":5259,"episode_number":177,"published":5265,"title":5266,"video_transcript_html":5267,"video_transcript_text":5268,"content":12,"seo":12,"status":19,"episode_people":5269,"recommendations":5273,"season":5274},"gavin-doughtie","896604270","Delve into Gavin's unique journey from his early years in the entertainment industry to his evolution as a developer, and gain insights into the intersection of technology and corporate dynamics.","07047069-4bdf-46aa-83f2-38ab02c6cf2e",[5260,5261,5262],{"name":4361,"url":4522},{"name":4524,"url":4525},{"name":5263,"url":5264},"Gavin Doughtie","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fdoughtie\u002F","2024-01-11","Gavin Doughtie, Senior Developer at Aon","\u003Cp>Gavin Doughtie: There was no one, like, technology There were a lot of little things that would guide me towards creative aspects or technology things.\u003C\u002Fp>\n\u003Cp>Pedro Pizzaro: Welcome, Gavin. Thanks for joining, John and I on the podcast today. You know, obviously, as as we know, you know, you're a senior dev at Aon, and we wanted to get some background on on your career, the lessons that you've learned, going from development to senior development and some management and leadership in there, and really touch on your background to see if it's if it's useful for the folks who are listening in. Definitely gonna be useful for John and I. But we'd love to learn a little bit more about your background quickly if you'd like to summarize it in your own words.\u003C\u002Fp>\n\u003Cp>And, yeah, we can kick things off.\u003C\u002Fp>\n\u003Cp>Gavin Doughtie: I was born yeah. So I, I grew up in Houston, Texas. My father was a professor at Rice University, and, unlike you might expect, he taught Elizabethan folk songs and ballads and English literature. But it was a very technical college, so I spent a lot of my, like, childhood time hanging out on a college campus, and a lot of my my peers were other faculty brats. And so there was a certain amount of cultural predestination towards reading things.\u003C\u002Fp>\n\u003Cp>So I did a lot of that growing up, read a lot of science fiction. Because of science fiction, I like science fiction movies. Because of science fiction movies, I like Star Trek. Because Star Trek, I went to science fiction conventions. Because of that, I saw some Ray Harryhausen movies with stop motion monsters.\u003C\u002Fp>\n\u003Cp>I got all excited about special effects and making movies. After that, I said, well, I'm gonna go to film school, and I'm gonna go to I'm gonna go to USC Film School, which was the one that, you know, George Lucas went to and now has funded heavily. And I applied. I applied you know, my backup schools were UCLA and NYU. So I got into, the film school at NYU, and I got into, UCLA as not just a regular undeclared undergraduate.\u003C\u002Fp>\n\u003Cp>And USC Film School rejected me. And so I'm I'm, like, I am not staying in Houston, Texas a minute longer. I'm gonna go to UCLA. So I went there and started doing classes and meeting people and figuring out how to live in Los Angeles, and I kept applying to film school. And the second time I applied though, they also rejected me.\u003C\u002Fp>\n\u003Cp>But I, you know, I, like, learned some stuff from that, and, and then worked on my application and applied. And then the third time I applied, they also rejected me, but the 4th the 4th time worked.\u003C\u002Fp>\n\u003Cp>Pedro Pizzaro: Goodness.\u003C\u002Fp>\n\u003Cp>Gavin Doughtie: So, so I started, at USC's film school as a junior in the early eighties, and I went there for a couple of years. And I worked on people's student films. But the, the thing that I think a lot of people miss out is you just do a lot of production, when you start out. You know? So you don't get too precious about it, and you try a lot of different things.\u003C\u002Fp>\n\u003Cp>And you come in and show it to your peers, and they critique it. And you try to, like, clamp down on your own defensiveness long enough to take a lesson away from it and try to improve the work you do the next time, and we were all helping each other out. And then when you're working on someone else's film, instead of doing it all yourself and being totally in control, you have to learn how to give up control and and get something from someone else to allow them to contribute and allow them to learn how to contribute if their skills in one area or another are not as strong as yours. And that idea that it's not a you're not a one man band. You can always you can always help people do better work, become better at what they're doing, and also, become much better yourself than if you're in a room, you know, reading hacker news.\u003C\u002Fp>\n\u003Cp>You know, you're gonna you're gonna be better if you have a chance to, in one way or another, work with other people. So, anyway, the the the short story is even at that time, I wanted to you know, I was writing screenplays all the time and writing feature length spec screenplays and stuff. And the the screenplay format for for people who haven't seen it is kind of a complicated technical format, and that's it's a pain in the butt to do it on a typewriter. And it's even more of a pain to go back and make revisions to things. And the film is it's much like software.\u003C\u002Fp>\n\u003Cp>You you're continuously revising it until you actually get the product to the customer. So word processors, even at their primitive mid eighties form, were were really an important thing. Like, they were like, yeah. We totally want that. We want that right now.\u003C\u002Fp>\n\u003Cp>I don't care if I have to shovel coal into the computer. It's still better than a typewriter. So I had, I had 3 roommates when I was in film school. 2 of them were film students, and the other one was a computer science student. And he said, well, if you wanna use word processing, you can go down to the computer lab, which was across the street from our dorm, and, and get an account on this this VAX computer.\u003C\u002Fp>\n\u003Cp>And you get to use the cool word processor, like the really good text editor, e max. So I was there in film school writing screenplays with e max, and that was that was sort of how I got into using computers. Because they were so primitive, you had to develop maybe more computer skills than you would today. But I I was not there to develop computer skills. I was there to do word processing.\u003C\u002Fp>\n\u003Cp>But when I got out of film school, as I was struggling along writing spec screenplays and directing, you know, no budget music videos for bands that did not have labels. You know, I was also temping a lot. And and this is this is how the how the how the road forked for me as I was working in a lot of offices, to, like, pay my rent. And, I was in there as a production word processor. You know, I basically sit down and type legal briefs, a lot of them, a lot a lot of law firms, but I've worked for all kinds of companies.\u003C\u002Fp>\n\u003Cp>Typing's a boring thing to do. So, you know, what you do is you look for opportunities to automate stuff. And if you've got a technical mind and you're curious about stuff like, oh, I can automate this and I can automate that. And, you know, I could write a for loop, and it would do it a bunch of times. And so, eventually, what happened is I learned to program really writing a package of add on macros to write screenwriting to do screenwriting with.\u003C\u002Fp>\n\u003Cp>So, you know, I WordPerfect at the time had a terrible but Turing complete programming environment, And I and a couple of other guys worked together to build screenwriting software, and we sold it. So it was an entrepreneurial venture in software back in the eighties and very early nineties. And that was sort of where I'd internalized a lot of things about programming. And then, you know, subsequently to that, I took a course in c and a course in Lisp and through UCLA Extension and just tried to backfill some of that information. But it was kind of a one thing led to another and in true, like, Los Angeles Hollywood style.\u003C\u002Fp>\n\u003Cp>I had friends, and some of them needed some software, and I convinced them to do one thing or another that I didn't necessarily know very well at the time. But through the research I had been able to do, so this is probably the right solution for these guys. So I I did a lot of that. I did a lot of Macintosh relational database stuff, the an old 4 GL called 4th dimension, which again was a programming environment. So it was yet another programming language was proprietary to the tool.\u003C\u002Fp>\n\u003Cp>It did have a compiler, and it did have the concept of pointers, so I got a lot of familiarity with that. And that that kinda takes us up to the the first software company that I worked for.\u003C\u002Fp>\n\u003Cp>John Daniels: Your journey actually sounds very similar to my journey, and I have a question at the end of my story I'm gonna tell here for a second. So the question I'm gonna tell a story after the question. The question is, can you remember a time when you were a teenager or a child that really triggered your love for movies and film? Because it sounds like you you went to school for film and it took you 4 times to get in. And then you went on this journey, this broad journey of film and doing these projects and getting inter introduced to computers and all of that, which is a similar journey to me.\u003C\u002Fp>\n\u003Cp>I I can specifically remember a snowboarding trip that I went on when I was about 15 years old, 16 years old. It was kind of a a snow camp, and this is the first time I'd ever seen a Mac computer. It was probably the first time I'd seen a real computer in general. I think I was using a Tandy 5000 way back in the day. And so we are at the snowboard camp, and this guy had a computer, and he was filming with a Handycam, and he would film all day all the snowboarding stuff, and then he would edit the video and show the video at night.\u003C\u002Fp>\n\u003Cp>When all of us would gather after dinner, we'd see this video of everybody on the on the screen. And so that that memory specifically is what really pushed me into creativity, and I I went to school first for I did a little bit of film, and then I moved into graphic design and website design. So kind of a similar journey. I just kind of found a need and then did small projects for friends and grew from film to graphic design to mobile design, web design, all that kind of stuff. But is there a memory that you can recall that really kicked you off into this whole creativity realm, either when you were a kid or a teenager?\u003C\u002Fp>\n\u003Cp>Is there that one memory in your brain that's always like, okay. That could have been the beginning stage for this entire journey?\u003C\u002Fp>\n\u003Cp>Gavin Doughtie: You know, I would say it crept up. You know, it was more more one of those things where there's a little bit of interest and a little bit of gratification, and both of those things kind of snowballed over time. You know, so there was no, like, the light shines down on me kind of moment. There were certainly I mean, there's certainly films that I watched, that were like, oh, yeah. This is, you know, this is a really significant film.\u003C\u002Fp>\n\u003Cp>This is going to change things. Like, as a science fiction nerd growing up in the seventies, like, I would go see all the science fiction movies when they came out, and they were they were pretty uniformly terrible except for the Andromeda strain. But I remember sitting in the movie theater on opening day watching Star Wars going, like, oh, okay. Yeah. Yeah.\u003C\u002Fp>\n\u003Cp>This is this is it. Right? And and to a to a somewhat lesser extent, Raiders of the Lost Ark, which I thought was, like, cinematically more interesting, but not as not as kind of cultural and, filmmaking technology, paradigmatically different.\u003C\u002Fp>\n\u003Cp>Pedro Pizzaro: Yeah.\u003C\u002Fp>\n\u003Cp>Gavin Doughtie: So, so, like, you know, I could talk about the movie part. The technology part, you know, I think probably the reason I spent so much time in film and special effects and and that kind of stuff, was that computers, you know, for a man my age, computers at the time were not that interesting. They were a character mode. They were very hard to get a hold of. You know, I didn't didn't have one in my house.\u003C\u002Fp>\n\u003Cp>Didn't have a color TV. Didn't have a VCR. Didn't have cable TV when I was growing up. Had to walk to school uphill both ways, etcetera, etcetera. But, right, it was just sort of normal at the at that time.\u003C\u002Fp>\n\u003Cp>And so computers were extremely primitive, and I, you know, I did seek them out and got some time on them even as a teenager. But it it involved like, I bicycle to RadioShack, bought a book about basic programming, took it home, wrote a program on legal pads, bicycle back to RadioShack, and the the the clerk was, like, whatever. I don't know what he's doing. And I was, like, typing it into the computer so I could run it. You know, it didn't save it or anything.\u003C\u002Fp>\n\u003Cp>It's just, like, 300 lines of basic and oh, look. It works. What's what's this? You know? And so I had I had those kinds of experiences.\u003C\u002Fp>\n\u003Cp>And one of the things that I found, like, the snowballing effect was what I what I would call the gratification loop of programming, which is you can do a little something and understand a little something, and then you see it work. And in creative endeavors, it's never that clear cut. You know? If you've worked on something for a long time, you have an idea if you're moving it in the direction yet or not. But, but in programming, it's just obvious, like, you did it.\u003C\u002Fp>\n\u003Cp>Right? So that kind of dopamine cycle, if you will, is a lot tighter. Now with digital production and especially now as you get into this AI and this generation stuff, I think people are gonna be having that dopamine cycle, with creative endeavors as well. So there's, you know, it's gonna all merge together eventually. Yeah.\u003C\u002Fp>\n\u003Cp>But there was no there was no one, like, technology There were a lot of little things that would guide me towards creative aspects or technology things.\u003C\u002Fp>\n\u003Cp>Pedro Pizzaro: It it sounds like it it really crept up on you and and it's all it sounded like in the beginning, it was more to solve an immediate need. Right? As you were working on different projects and you need to solve some immediate need that I need to send\u003C\u002Fp>\n\u003Cp>Gavin Doughtie: you a screen play or pay my rent.\u003C\u002Fp>\n\u003Cp>Pedro Pizzaro: Exactly. And and that just reminds me of, you know, there was there was a company that I was at before where the CTO always said that that lazy people make the best engineers and and developers, and I always thought that was really strange. But the more I listened to him say it, it was really about, like like you mentioned, you're trying to solve for these automation or these automate away these mundane tasks, and you wanna be able to do that so you could free more of your time to do something more interesting. And it's not to say the act of of developing the work is lazy. It's just that you wanna solve something that a computer could do.\u003C\u002Fp>\n\u003Cp>And, you know, now coming into the age of AI and everything else, it's, you know, all these possibilities\u003C\u002Fp>\n\u003Cp>Gavin Doughtie: will go\u003C\u002Fp>\n\u003Cp>Pedro Pizzaro: into that.\u003C\u002Fp>\n\u003Cp>Gavin Doughtie: Mark is, like, is it gonna be more more of that, or are we going into an entirely new and disruptive paradigm? And I the jury's still kinda out on that. Right. Could go either way. You kids are gonna have to figure it out.\u003C\u002Fp>\n\u003Cp>John Daniels: Yeah. Do you find it\u003C\u002Fp>\n\u003Cp>Gavin Doughtie: I'm hoping I'm hoping it'll time out, like, just around the time I hit retirement AI figures out longevity and, and UBI.\u003C\u002Fp>\n\u003Cp>Pedro Pizzaro: Yeah. Exactly. Yeah. It's like at a certain point, you know, you get to computers had a limited capacity to solve a limited number of mechanical issues. They've obviously grown in technical ability to the point where it's Mhmm.\u003C\u002Fp>\n\u003Cp>At some point what it can do most of what a human can do or 90% and, you know, there are What are you saying?\u003C\u002Fp>\n\u003Cp>Gavin Doughtie: The 90% of what humans what humans have been able to contribute to the economy, particularly the industrial economy has been designed to be something that could eventually be replaced with a sufficiently capable machine. Right?\u003C\u002Fp>\n\u003Cp>Pedro Pizzaro: Right.\u003C\u002Fp>\n\u003Cp>Gavin Doughtie: You know? So the a lot of the activity and I I I it is in fact a concern when I'm programming now and I'm writing something that's like, well, this is complex, but it doesn't really doesn't really dig into any kind of uniquely human things that I'm bringing to this solution. So I'm I'm right now just chewing on, like, what can I do to, to offload that kind of work? You know, can I can I convince, a large language model to absorb a bunch of this complexity for me so that I can work more as a director? And and this is this is, like, the metaphor that that that I've been toying with a lot is, you know, you make a motion picture in what you're doing.\u003C\u002Fp>\n\u003Cp>And and I'm not to say, like, I haven't done Hollywood for 30 plus years. Right? So I've been a software engineer this whole time. But, the process is you gather people together. Everyone has a bit of expertise or has deep expertise at at at one or more skills, and you are going to combine that expertise to produce some something that's, you know, greater than the sum of its parts.\u003C\u002Fp>\n\u003Cp>And I think a lot of work that we have is going to feel more more like the role of a producer or director in a film, you know, where and I like and believe me, it pains me to leave out actors and writers and and and people like that who have a lot of skill that they're bringing to the table. But but I think what you're really looking at is expressing an intent to some instrumentality that will deliver you a a solution based on that intent. You know? So show me a picture of an astronaut riding a unicorn might be your intent. You wanna see that for some reason.\u003C\u002Fp>\n\u003Cp>And then, just as you might have asked a human artist to produce that for you, now, you know, a an AI artist will produce that for you. Whether or not it's good or what you wanted, that I think that's the, the friction in the question that we have moving forward. I I have a lot of artist friends, and I would like to see them stay gainfully employed and bring all their many wonderful skills to, humanity still. So I don't, I don't I don't think there's a a quick solution to that. Yeah.\u003C\u002Fp>\n\u003Cp>But I am digressing.\u003C\u002Fp>\n\u003Cp>John Daniels: Yeah. Do you find so I think back when all of us were you know, Pedro and I are certainly a little a little more junior than yourselves, not too much. You're you're not that much older than us. But back when we were getting into technology, whether it be videos or software or computers, you know, the the learning curve, in my opinion, was a little bit easier because there was only so many technologies that we could learn and study, you know, so we could dive into a programming language. You know, when I started diving into Dreamweaver, there really wasn't too much out there on the market to learn, so I kinda just went with one platform.\u003C\u002Fp>\n\u003Cp>So it was a little bit easier back then. In today's age, you know, there's AI. There's all these other other languages to do everything. There's 500 ways to do everything. So do you find that more exciting that there's so many ways to accomplish a task, or do you find it more daunting?\u003C\u002Fp>\n\u003Cp>And and do you still kind of traditionally go back to some fallbacks that you you are more comfortable with?\u003C\u002Fp>\n\u003Cp>Gavin Doughtie: So, you know, I I think this is a question that will always come up in in the life of any human being who's trying to be productive in in a world of economic value exchange these days. Right? It's been a long time since I felt like I had great depth in a piece of technology. You know? Like, I started as a web developer at Google in 2006, and I thought, like, okay.\u003C\u002Fp>\n\u003Cp>I have more depth in this than most people, but I don't feel that way now, like, well over 15 years later. So it's, you know, the the the the technology, space will always grow.\u003C\u002Fp>\n\u003Cp>John Daniels: Mhmm.\u003C\u002Fp>\n\u003Cp>Gavin Doughtie: There is there's always a trade off between building out your production capacity and producing. Right? And some people get obsessed with production and ignore production capacity, and some people will go the other way. When I was word processing, I often would encounter a certain sort of person who would say, well, I'll just retype it. And I was like, you could copy paste it.\u003C\u002Fp>\n\u003Cp>You could write a macro. You could write a mer mail merge program. You know, there are a lot of things you could do if you invested some time in understanding something that would pay off not only for this project. It might be a wash in terms of this one thing, but then it would pay off again and again and again moving forward. And I still have those conversations within my own head every day when I'm writing software.\u003C\u002Fp>\n\u003Cp>Right? It's like, how much do I refactor this today? How per how how much production do I have to get done today to accomplish all the other goals that I'm trying to do? And how much, how much infrastructure should I be putting down for myself or learning about to increase my my capacity moving forward. It's continual juggling act and, sometimes in opposition to, this gratification loop.\u003C\u002Fp>\n\u003Cp>Like, it is not gratifying to have a bug you can't figure out. That's just a pain in the butt. It's no fun to write unit tests. That is just a pain in the butt. But if you write the unit tests, you may not have to deal with the bug, and they'll pile up and give you more and more robust code and better understanding of the code that you've already written because you can refer to the tests.\u003C\u002Fp>\n\u003Cp>And as my my colleagues will tell you, I am not the most meticulous of unit testers, particularly for user interface code, But I always find that it pays off. So that's the same thing with acquiring knowledge or studying things. It's like, it'll always pay off, but you have to balance it out with what you have to get done, like, right now. But I think most people on the side of production over increasing production capacity, especially especially people who are newer.\u003C\u002Fp>\n\u003Cp>John Daniels: One of my next questions was going to be around the future because, obviously, we're in this these days where I mentioned there's kinda new technology every week. There's just something different, including AI. So how do you see you've already mentioned some of the things that you're building. You see them being used for the next 10 to 15 years. I guess, where do you see some stability in what you're what people are developing these days that'll be around for a long term in a long time also, long term long time?\u003C\u002Fp>\n\u003Cp>But what are some things that you think are kind of at the end of their life cycle or things that are being developed that really have a a short shelf life? Is there anything in your head that you see both on the short term side of things and the long term side of things?\u003C\u002Fp>\n\u003Cp>Gavin Doughtie: Do you\u003C\u002Fp>\n\u003Cp>John Daniels: think do you think AI will, like I I personally think AI is gonna change the game. It'll it'll morph over time, but I think AI is gonna be around a long time as opposed to maybe something like NFTs, which kinda came and went quickly. So I'm just kinda curious if there's anything you've ever thought about along those lines.\u003C\u002Fp>\n\u003Cp>Gavin Doughtie: Well, I mean, there have been a few times where I'm like, okay. This is this is an important thing. I'm really gonna have to dig in on this. And, some of it's philosophical. So the big technology choices that I've made where I've decided to spend time in a discretionary way and not at the behest of an employer has always been what is technology that is going to be portable.\u003C\u002Fp>\n\u003Cp>And by that, I mean, knowledge and understanding that is not at that is not controlled by a single vendor, particularly, and that can be applied broadly. So JavaScript, I learned on my own time. You know? By day, I was programming in c plus plus. Machine learning and AI, I am still learning on my own time.\u003C\u002Fp>\n\u003Cp>You know? And the prod the side projects that I'm doing around that are all on my own time. They're not they're not any learning how to do, you know, that web 2.0 style AJAX JavaScript stuff. That was all that was all stuff I thought, well, I need to know how to do this regardless of who's paying me. So you're you're looking for moments in the marketplace where it's like, like, people people are gonna want this.\u003C\u002Fp>\n\u003Cp>People are gonna want more and more of it, and no vendor is going to capture all the value from it. You know, I I spent a chunk of time in the nineties as a, a Windows developer. So I was writing an application, couple several applications for the Windows platform. And I did not go home and screw around with Windows in the evenings because I, like you know, however you might feel about Windows technically, that was that was Microsoft's game. You were always gonna be playing in Microsoft's game if you were developing for Windows.\u003C\u002Fp>\n\u003Cp>And I I think, you know, I have the same skittishness about building stuff that could only work with the OpenAI APIs. I think OpenAI is doing great work. You know? Windows became dominant by being the thing that people want, and I think AI is doing the same thing in collaboration with Microsoft to some extent. So I'm I'm skittish about that.\u003C\u002Fp>\n\u003Cp>You know? I I had some interest in the the crypto space, particularly the, you know, executing code on on chain Mhmm. Because it had the promise of giving us a mechanism where the infrastructure wasn't owned or controlled by a particular entity. The reality is a lot fuzzier and not nearly as as idealistic as I would have wanted. But that was where, you know, the interest that I did have in it came from.\u003C\u002Fp>\n\u003Cp>It's this idea that I can create value and I can distribute that value and I can I can get, remunerated for the effort that I put into creating that value in some way that is not, at the at the whim of some some other provider platform owner, or potential competitor? So that's why I've always been very big on web based technologies, even though I spent several years at Google writing, you know, iOS native apps. So that's that's sort of, you know, a roundabout way of saying, what do you what do you think you can take with you? You know? What will, there as OpenAI starts to, like, announce, hey.\u003C\u002Fp>\n\u003Cp>Look. We've cut our price in half, and we've we've introduced retrieval augmented, generation. And, you know, what companies are basically were only doing that, plus not very much else that are now, like Mhmm. You know, their heads are exploding. Oh my god, you know, we're only 3 times cheaper than OpenAI used to be and, you know, now we don't have a competitive mode.\u003C\u002Fp>\n\u003Cp>So there's a certain amount of figuring out what can you do. I mean, it's the same thing. You know? Go back to Hollywood. It's the same thing.\u003C\u002Fp>\n\u003Cp>You know? What have you got? The only thing that you've got that's different from anything anybody else has is your unique intent and your unique perspective on the world and your take on a problem and your your skills at working towards a solution that embraces what's unique about you. And I think that, you know, if I were gonna give this as advice, you know, scratch your own itch and be really scared of proprietary platforms. And whether the platform is, you know, Google or Facebook or you know?\u003C\u002Fp>\n\u003Cp>I I I don't see it as much as I used to, but there was a long time I would run into kids who were like, c sharp is the best. We're gonna, like, build this with c sharp. And it's, like, you know, however however good old programming language c sharp might be, it was entirely the play toy of people in Redmond. And you're you're locking yourself into a particular vendor and when you do that. So, you know, there's there's usually a platform and a platform a set of platform technologies.\u003C\u002Fp>\n\u003Cp>And if it's a proprietary platform and they're starting to move you to proprietary technologies, you're like, well, how confident am I in this platform? How much do I believe? How much am I going to risk my career on the Atari ST or the 3 d o game console or, you know, the Amiga 2 1,000 or the Palm Pilot. You know, there are all these technologies that, you know, were opportunities for people. Like, I shipped this app on this platform, and the platform's getting uptake, and it's gonna be great.\u003C\u002Fp>\n\u003Cp>But then if it loses a platform battle, then you're kind of stuck. Like, okay. I've gotta personally retool to either target a new platform, or you could make technology choices and, you know, choices about your own efforts that will lead you to knowledge, skill, and understanding that you can take with you to the next thing.\u003C\u002Fp>\n\u003Cp>John Daniels: I think, Gavin, I I think we could probably continue this conversation for an hour. But, what I'd like to do is kind of allow you to wrap up with a little we know that I like to talk. We know that you like to talk. But I'd love to wrap all of this up. You've got a a very incredible and kind of experienced life of just different things that you've tried, different technologies you've seen.\u003C\u002Fp>\n\u003Cp>You know, if you have one piece of advice or one thing that you love that you want to make sure everybody knows about, I mean, give us, like, a good as long as you want outro of kind of some of the some of the things that you've learned, advice you have for others, things that you you care about.\u003C\u002Fp>\n\u003Cp>Gavin Doughtie: Oh, you mean you're, like, giving an old man permission to rant for just a minute? Okay.\u003C\u002Fp>\n\u003Cp>John Daniels: Yeah. Go yeah. Go for it. Yeah.\u003C\u002Fp>\n\u003Cp>Gavin Doughtie: So so if you're at the beginning of your software engineering career, I would still recommend that you learn how to type at least about 40 words a minute with some accuracy. There's plenty of ways to learn to do that. It's worth the effort because it will clear space in your head for things that are actually difficult problems if you don't have to worry about the mechanical stress of typing something. This is probably not gonna be advice that's gonna be worth anything in 20 years, but it's going to be advice that's useful for the next 5. So that's a little thing.\u003C\u002Fp>\n\u003Cp>A little thing that I've been doing more recently that has been helpful is, this is such a cliche, regular exercise. I am I've been doing barbell stuff. Obviously, I'm not a giant person, but the thing that I didn't expect to get out of it, the the physical benefits are, of course, all the ones you hear about. The thing that I didn't expect to get out of it was the psychological training of stepping up and repeatedly doing something difficult that you didn't want to do in the moment in order to get a benefit further down the line. Right?\u003C\u002Fp>\n\u003Cp>And just that mental exercise is incredibly valuable. If you're if you're getting it while you, you know, exercise your body, if you're like most Americans, you will be ahead of most Americans. So it's it's worth doing, and I say this as someone who despises my time in the gym. But that the mental thing was, like, the unusual thing, and it's something that I I would encourage, especially, programmers who are all interested in technology to explore and spend some time on, and it'll it'll pay off. The time the time will come back to you, many fold, especially over the the length of your life.\u003C\u002Fp>\n\u003Cp>The other thing is get out and engage with people. Almost all the jobs that I've had had a component of knowing someone. And it's not to say that people hired me because they knew me, but they wanted to hire me because they knew me, if that makes any sense. Like, I had to be able to do the job and convince everyone and convince people who didn't know me I could do the job. But the personal connections, were always really important, And they were connections forged by oftentimes working with someone and demonstrating over a period of weeks or months or sometimes years that I could do a job, that I was good to work with, that I wasn't that I was trying to contribute towards the success of the project and to their success.\u003C\u002Fp>\n\u003Cp>And that, you know, wouldn't say it ingratiated me, but it meant that they they knew it they knew what I was about, and they could trust me and that they would they would be my champion, you know, at the next place. It's like, oh, and, you know, combine that sometimes with the skills that that are necessary at that moment in technology history. I think that was that was a good a good combination for a lot of my career. You know, there are people I work with now that I worked with 20 years ago at the startup I left Picasa for. So they kept on with their careers and, you know, when I was looking for something new to do, they're like, well, you know, we're starting up this new technology group.\u003C\u002Fp>\n\u003Cp>You could come come be the first hire, hire your own boss. So, you know all of those things. Life is long. Your relationships with other people are going to be valuable to you while you have them and then later on. You shouldn't have them for their later value.\u003C\u002Fp>\n\u003Cp>You should have them to be present with other people now. You know, be collegial if if that's all you've got in common, and if you have other interests that you share, by all means, you know, talk about them. I I have a friend that I worked at at 5 different companies with in Los Angeles, including, including Google. So, we just you know, wherever one of us landed, we'd go like, you know, I think Mike's available or I think Gavin's available. And, you know, that that worked out well for both of us over time.\u003C\u002Fp>\n\u003Cp>So, you know, keep up with people. They're they're important, And, you know, find ways to work on projects with people. If you're if you're early on, especially if you're not in a, you know, a big metropolis with a lot of technology going on, You know, open work on open source projects with people, take their take their poll request reviews seriously, Try to contribute to the project. What's in it for you is the process of contributing, not not a an immediate byproduct.\u003C\u002Fp>\n\u003Cp>John Daniels: Yeah.\u003C\u002Fp>\n\u003Cp>Gavin Doughtie: It's like, one of my my oldest son was like, I don't know if I'm gonna be able to, like, you know, help you out in your old age, dad, you know? And I'm like, you don't owe me anything. You know? The the the the good that I got out of the process of raising you was the process of raising you. You know?\u003C\u002Fp>\n\u003Cp>Go have your own great life. And that's that's where I wanna end it. Go have your own great life. Like, technology is a a great thing to do. If you hate it, though, you should find something else to do.\u003C\u002Fp>\n\u003Cp>Pedro Pizzaro: Yeah. Absolutely. So parting words of wisdom from Gavin. Work out your work out your body, work out your mind, and get out there, meet as many people as you can, form that community. And Yeah.\u003C\u002Fp>\n\u003Cp>You know, I think that's what life's about. Alright. Is there anywhere that people can follow your work, or do you have a website or a blog or maybe just a social media?\u003C\u002Fp>\n\u003Cp>Gavin Doughtie: Well, I have a cop website as they used to call it at gavin dotcom, which is a an a ill maintained WordPress blog. You can find me, I'm still a little more active on x than I that I'm comfortable with, but you can find me at Gavin Doughty on on Twitter. Twitter, Elon. And, and and basically, the way you spell my name is weird enough that I have no hopes of Internet privacy at this point in my life. So it's Yeah.\u003C\u002Fp>\n\u003Cp>Gavin, g a v I n, like the governor of California, and Doughty is d o u g h t I e, like bread dough in a necktie.\u003C\u002Fp>\n\u003Cp>John Daniels: Yeah. Well, yeah, I I also will second Pedro's, words there. I I definitely appreciate the advice that you gave at the end outside of technology. Pedro and I have been at the same you know, worked at 3 companies together. We Right.\u003C\u002Fp>\n\u003Cp>Kind of are have that human to human relationship. Funnily enough, on a second topic, today is my 15th Twitterversary. So I've been on Twitter for 15 years today. But, yeah, I would definitely appreciate the the the technical and nontechnical advice you you gave today, Gavin. I think a lot of our viewers will appreciate both of those because getting out amongst humans, I think, is important as we start to enter this world of, VR headsets.\u003C\u002Fp>\n\u003Cp>So we'll stay real out of the matrix while we do. But, but we appreciate the time today, and and, hopefully, people can give some feedback about what they've learned from your your story.\u003C\u002Fp>","There was no one, like, technology There were a lot of little things that would guide me towards creative aspects or technology things. Welcome, Gavin. Thanks for joining, John and I on the podcast today. You know, obviously, as as we know, you know, you're a senior dev at Aon, and we wanted to get some background on on your career, the lessons that you've learned, going from development to senior development and some management and leadership in there, and really touch on your background to see if it's if it's useful for the folks who are listening in. Definitely gonna be useful for John and I. But we'd love to learn a little bit more about your background quickly if you'd like to summarize it in your own words. And, yeah, we can kick things off. I was born yeah. So I, I grew up in Houston, Texas. My father was a professor at Rice University, and, unlike you might expect, he taught Elizabethan folk songs and ballads and English literature. But it was a very technical college, so I spent a lot of my, like, childhood time hanging out on a college campus, and a lot of my my peers were other faculty brats. And so there was a certain amount of cultural predestination towards reading things. So I did a lot of that growing up, read a lot of science fiction. Because of science fiction, I like science fiction movies. Because of science fiction movies, I like Star Trek. Because Star Trek, I went to science fiction conventions. Because of that, I saw some Ray Harryhausen movies with stop motion monsters. I got all excited about special effects and making movies. After that, I said, well, I'm gonna go to film school, and I'm gonna go to I'm gonna go to USC Film School, which was the one that, you know, George Lucas went to and now has funded heavily. And I applied. I applied you know, my backup schools were UCLA and NYU. So I got into, the film school at NYU, and I got into, UCLA as not just a regular undeclared undergraduate. And USC Film School rejected me. And so I'm I'm, like, I am not staying in Houston, Texas a minute longer. I'm gonna go to UCLA. So I went there and started doing classes and meeting people and figuring out how to live in Los Angeles, and I kept applying to film school. And the second time I applied though, they also rejected me. But I, you know, I, like, learned some stuff from that, and, and then worked on my application and applied. And then the third time I applied, they also rejected me, but the 4th the 4th time worked. Goodness. So, so I started, at USC's film school as a junior in the early eighties, and I went there for a couple of years. And I worked on people's student films. But the, the thing that I think a lot of people miss out is you just do a lot of production, when you start out. You know? So you don't get too precious about it, and you try a lot of different things. And you come in and show it to your peers, and they critique it. And you try to, like, clamp down on your own defensiveness long enough to take a lesson away from it and try to improve the work you do the next time, and we were all helping each other out. And then when you're working on someone else's film, instead of doing it all yourself and being totally in control, you have to learn how to give up control and and get something from someone else to allow them to contribute and allow them to learn how to contribute if their skills in one area or another are not as strong as yours. And that idea that it's not a you're not a one man band. You can always you can always help people do better work, become better at what they're doing, and also, become much better yourself than if you're in a room, you know, reading hacker news. You know, you're gonna you're gonna be better if you have a chance to, in one way or another, work with other people. So, anyway, the the the short story is even at that time, I wanted to you know, I was writing screenplays all the time and writing feature length spec screenplays and stuff. And the the screenplay format for for people who haven't seen it is kind of a complicated technical format, and that's it's a pain in the butt to do it on a typewriter. And it's even more of a pain to go back and make revisions to things. And the film is it's much like software. You you're continuously revising it until you actually get the product to the customer. So word processors, even at their primitive mid eighties form, were were really an important thing. Like, they were like, yeah. We totally want that. We want that right now. I don't care if I have to shovel coal into the computer. It's still better than a typewriter. So I had, I had 3 roommates when I was in film school. 2 of them were film students, and the other one was a computer science student. And he said, well, if you wanna use word processing, you can go down to the computer lab, which was across the street from our dorm, and, and get an account on this this VAX computer. And you get to use the cool word processor, like the really good text editor, e max. So I was there in film school writing screenplays with e max, and that was that was sort of how I got into using computers. Because they were so primitive, you had to develop maybe more computer skills than you would today. But I I was not there to develop computer skills. I was there to do word processing. But when I got out of film school, as I was struggling along writing spec screenplays and directing, you know, no budget music videos for bands that did not have labels. You know, I was also temping a lot. And and this is this is how the how the how the road forked for me as I was working in a lot of offices, to, like, pay my rent. And, I was in there as a production word processor. You know, I basically sit down and type legal briefs, a lot of them, a lot a lot of law firms, but I've worked for all kinds of companies. Typing's a boring thing to do. So, you know, what you do is you look for opportunities to automate stuff. And if you've got a technical mind and you're curious about stuff like, oh, I can automate this and I can automate that. And, you know, I could write a for loop, and it would do it a bunch of times. And so, eventually, what happened is I learned to program really writing a package of add on macros to write screenwriting to do screenwriting with. So, you know, I WordPerfect at the time had a terrible but Turing complete programming environment, And I and a couple of other guys worked together to build screenwriting software, and we sold it. So it was an entrepreneurial venture in software back in the eighties and very early nineties. And that was sort of where I'd internalized a lot of things about programming. And then, you know, subsequently to that, I took a course in c and a course in Lisp and through UCLA Extension and just tried to backfill some of that information. But it was kind of a one thing led to another and in true, like, Los Angeles Hollywood style. I had friends, and some of them needed some software, and I convinced them to do one thing or another that I didn't necessarily know very well at the time. But through the research I had been able to do, so this is probably the right solution for these guys. So I I did a lot of that. I did a lot of Macintosh relational database stuff, the an old 4 GL called 4th dimension, which again was a programming environment. So it was yet another programming language was proprietary to the tool. It did have a compiler, and it did have the concept of pointers, so I got a lot of familiarity with that. And that that kinda takes us up to the the first software company that I worked for. Your journey actually sounds very similar to my journey, and I have a question at the end of my story I'm gonna tell here for a second. So the question I'm gonna tell a story after the question. The question is, can you remember a time when you were a teenager or a child that really triggered your love for movies and film? Because it sounds like you you went to school for film and it took you 4 times to get in. And then you went on this journey, this broad journey of film and doing these projects and getting inter introduced to computers and all of that, which is a similar journey to me. I I can specifically remember a snowboarding trip that I went on when I was about 15 years old, 16 years old. It was kind of a a snow camp, and this is the first time I'd ever seen a Mac computer. It was probably the first time I'd seen a real computer in general. I think I was using a Tandy 5000 way back in the day. And so we are at the snowboard camp, and this guy had a computer, and he was filming with a Handycam, and he would film all day all the snowboarding stuff, and then he would edit the video and show the video at night. When all of us would gather after dinner, we'd see this video of everybody on the on the screen. And so that that memory specifically is what really pushed me into creativity, and I I went to school first for I did a little bit of film, and then I moved into graphic design and website design. So kind of a similar journey. I just kind of found a need and then did small projects for friends and grew from film to graphic design to mobile design, web design, all that kind of stuff. But is there a memory that you can recall that really kicked you off into this whole creativity realm, either when you were a kid or a teenager? Is there that one memory in your brain that's always like, okay. That could have been the beginning stage for this entire journey? You know, I would say it crept up. You know, it was more more one of those things where there's a little bit of interest and a little bit of gratification, and both of those things kind of snowballed over time. You know, so there was no, like, the light shines down on me kind of moment. There were certainly I mean, there's certainly films that I watched, that were like, oh, yeah. This is, you know, this is a really significant film. This is going to change things. Like, as a science fiction nerd growing up in the seventies, like, I would go see all the science fiction movies when they came out, and they were they were pretty uniformly terrible except for the Andromeda strain. But I remember sitting in the movie theater on opening day watching Star Wars going, like, oh, okay. Yeah. Yeah. This is this is it. Right? And and to a to a somewhat lesser extent, Raiders of the Lost Ark, which I thought was, like, cinematically more interesting, but not as not as kind of cultural and, filmmaking technology, paradigmatically different. Yeah. So, so, like, you know, I could talk about the movie part. The technology part, you know, I think probably the reason I spent so much time in film and special effects and and that kind of stuff, was that computers, you know, for a man my age, computers at the time were not that interesting. They were a character mode. They were very hard to get a hold of. You know, I didn't didn't have one in my house. Didn't have a color TV. Didn't have a VCR. Didn't have cable TV when I was growing up. Had to walk to school uphill both ways, etcetera, etcetera. But, right, it was just sort of normal at the at that time. And so computers were extremely primitive, and I, you know, I did seek them out and got some time on them even as a teenager. But it it involved like, I bicycle to RadioShack, bought a book about basic programming, took it home, wrote a program on legal pads, bicycle back to RadioShack, and the the the clerk was, like, whatever. I don't know what he's doing. And I was, like, typing it into the computer so I could run it. You know, it didn't save it or anything. It's just, like, 300 lines of basic and oh, look. It works. What's what's this? You know? And so I had I had those kinds of experiences. And one of the things that I found, like, the snowballing effect was what I what I would call the gratification loop of programming, which is you can do a little something and understand a little something, and then you see it work. And in creative endeavors, it's never that clear cut. You know? If you've worked on something for a long time, you have an idea if you're moving it in the direction yet or not. But, but in programming, it's just obvious, like, you did it. Right? So that kind of dopamine cycle, if you will, is a lot tighter. Now with digital production and especially now as you get into this AI and this generation stuff, I think people are gonna be having that dopamine cycle, with creative endeavors as well. So there's, you know, it's gonna all merge together eventually. Yeah. But there was no there was no one, like, technology There were a lot of little things that would guide me towards creative aspects or technology things. It it sounds like it it really crept up on you and and it's all it sounded like in the beginning, it was more to solve an immediate need. Right? As you were working on different projects and you need to solve some immediate need that I need to send you a screen play or pay my rent. Exactly. And and that just reminds me of, you know, there was there was a company that I was at before where the CTO always said that that lazy people make the best engineers and and developers, and I always thought that was really strange. But the more I listened to him say it, it was really about, like like you mentioned, you're trying to solve for these automation or these automate away these mundane tasks, and you wanna be able to do that so you could free more of your time to do something more interesting. And it's not to say the act of of developing the work is lazy. It's just that you wanna solve something that a computer could do. And, you know, now coming into the age of AI and everything else, it's, you know, all these possibilities will go into that. Mark is, like, is it gonna be more more of that, or are we going into an entirely new and disruptive paradigm? And I the jury's still kinda out on that. Right. Could go either way. You kids are gonna have to figure it out. Yeah. Do you find it I'm hoping I'm hoping it'll time out, like, just around the time I hit retirement AI figures out longevity and, and UBI. Yeah. Exactly. Yeah. It's like at a certain point, you know, you get to computers had a limited capacity to solve a limited number of mechanical issues. They've obviously grown in technical ability to the point where it's Mhmm. At some point what it can do most of what a human can do or 90% and, you know, there are What are you saying? The 90% of what humans what humans have been able to contribute to the economy, particularly the industrial economy has been designed to be something that could eventually be replaced with a sufficiently capable machine. Right? Right. You know? So the a lot of the activity and I I I it is in fact a concern when I'm programming now and I'm writing something that's like, well, this is complex, but it doesn't really doesn't really dig into any kind of uniquely human things that I'm bringing to this solution. So I'm I'm right now just chewing on, like, what can I do to, to offload that kind of work? You know, can I can I convince, a large language model to absorb a bunch of this complexity for me so that I can work more as a director? And and this is this is, like, the metaphor that that that I've been toying with a lot is, you know, you make a motion picture in what you're doing. And and I'm not to say, like, I haven't done Hollywood for 30 plus years. Right? So I've been a software engineer this whole time. But, the process is you gather people together. Everyone has a bit of expertise or has deep expertise at at at one or more skills, and you are going to combine that expertise to produce some something that's, you know, greater than the sum of its parts. And I think a lot of work that we have is going to feel more more like the role of a producer or director in a film, you know, where and I like and believe me, it pains me to leave out actors and writers and and and people like that who have a lot of skill that they're bringing to the table. But but I think what you're really looking at is expressing an intent to some instrumentality that will deliver you a a solution based on that intent. You know? So show me a picture of an astronaut riding a unicorn might be your intent. You wanna see that for some reason. And then, just as you might have asked a human artist to produce that for you, now, you know, a an AI artist will produce that for you. Whether or not it's good or what you wanted, that I think that's the, the friction in the question that we have moving forward. I I have a lot of artist friends, and I would like to see them stay gainfully employed and bring all their many wonderful skills to, humanity still. So I don't, I don't I don't think there's a a quick solution to that. Yeah. But I am digressing. Yeah. Do you find so I think back when all of us were you know, Pedro and I are certainly a little a little more junior than yourselves, not too much. You're you're not that much older than us. But back when we were getting into technology, whether it be videos or software or computers, you know, the the learning curve, in my opinion, was a little bit easier because there was only so many technologies that we could learn and study, you know, so we could dive into a programming language. You know, when I started diving into Dreamweaver, there really wasn't too much out there on the market to learn, so I kinda just went with one platform. So it was a little bit easier back then. In today's age, you know, there's AI. There's all these other other languages to do everything. There's 500 ways to do everything. So do you find that more exciting that there's so many ways to accomplish a task, or do you find it more daunting? And and do you still kind of traditionally go back to some fallbacks that you you are more comfortable with? So, you know, I I think this is a question that will always come up in in the life of any human being who's trying to be productive in in a world of economic value exchange these days. Right? It's been a long time since I felt like I had great depth in a piece of technology. You know? Like, I started as a web developer at Google in 2006, and I thought, like, okay. I have more depth in this than most people, but I don't feel that way now, like, well over 15 years later. So it's, you know, the the the the technology, space will always grow. Mhmm. There is there's always a trade off between building out your production capacity and producing. Right? And some people get obsessed with production and ignore production capacity, and some people will go the other way. When I was word processing, I often would encounter a certain sort of person who would say, well, I'll just retype it. And I was like, you could copy paste it. You could write a macro. You could write a mer mail merge program. You know, there are a lot of things you could do if you invested some time in understanding something that would pay off not only for this project. It might be a wash in terms of this one thing, but then it would pay off again and again and again moving forward. And I still have those conversations within my own head every day when I'm writing software. Right? It's like, how much do I refactor this today? How per how how much production do I have to get done today to accomplish all the other goals that I'm trying to do? And how much, how much infrastructure should I be putting down for myself or learning about to increase my my capacity moving forward. It's continual juggling act and, sometimes in opposition to, this gratification loop. Like, it is not gratifying to have a bug you can't figure out. That's just a pain in the butt. It's no fun to write unit tests. That is just a pain in the butt. But if you write the unit tests, you may not have to deal with the bug, and they'll pile up and give you more and more robust code and better understanding of the code that you've already written because you can refer to the tests. And as my my colleagues will tell you, I am not the most meticulous of unit testers, particularly for user interface code, But I always find that it pays off. So that's the same thing with acquiring knowledge or studying things. It's like, it'll always pay off, but you have to balance it out with what you have to get done, like, right now. But I think most people on the side of production over increasing production capacity, especially especially people who are newer. One of my next questions was going to be around the future because, obviously, we're in this these days where I mentioned there's kinda new technology every week. There's just something different, including AI. So how do you see you've already mentioned some of the things that you're building. You see them being used for the next 10 to 15 years. I guess, where do you see some stability in what you're what people are developing these days that'll be around for a long term in a long time also, long term long time? But what are some things that you think are kind of at the end of their life cycle or things that are being developed that really have a a short shelf life? Is there anything in your head that you see both on the short term side of things and the long term side of things? Do you think do you think AI will, like I I personally think AI is gonna change the game. It'll it'll morph over time, but I think AI is gonna be around a long time as opposed to maybe something like NFTs, which kinda came and went quickly. So I'm just kinda curious if there's anything you've ever thought about along those lines. Well, I mean, there have been a few times where I'm like, okay. This is this is an important thing. I'm really gonna have to dig in on this. And, some of it's philosophical. So the big technology choices that I've made where I've decided to spend time in a discretionary way and not at the behest of an employer has always been what is technology that is going to be portable. And by that, I mean, knowledge and understanding that is not at that is not controlled by a single vendor, particularly, and that can be applied broadly. So JavaScript, I learned on my own time. You know? By day, I was programming in c plus plus. Machine learning and AI, I am still learning on my own time. You know? And the prod the side projects that I'm doing around that are all on my own time. They're not they're not any learning how to do, you know, that web 2.0 style AJAX JavaScript stuff. That was all that was all stuff I thought, well, I need to know how to do this regardless of who's paying me. So you're you're looking for moments in the marketplace where it's like, like, people people are gonna want this. People are gonna want more and more of it, and no vendor is going to capture all the value from it. You know, I I spent a chunk of time in the nineties as a, a Windows developer. So I was writing an application, couple several applications for the Windows platform. And I did not go home and screw around with Windows in the evenings because I, like you know, however you might feel about Windows technically, that was that was Microsoft's game. You were always gonna be playing in Microsoft's game if you were developing for Windows. And I I think, you know, I have the same skittishness about building stuff that could only work with the OpenAI APIs. I think OpenAI is doing great work. You know? Windows became dominant by being the thing that people want, and I think AI is doing the same thing in collaboration with Microsoft to some extent. So I'm I'm skittish about that. You know? I I had some interest in the the crypto space, particularly the, you know, executing code on on chain Mhmm. Because it had the promise of giving us a mechanism where the infrastructure wasn't owned or controlled by a particular entity. The reality is a lot fuzzier and not nearly as as idealistic as I would have wanted. But that was where, you know, the interest that I did have in it came from. It's this idea that I can create value and I can distribute that value and I can I can get, remunerated for the effort that I put into creating that value in some way that is not, at the at the whim of some some other provider platform owner, or potential competitor? So that's why I've always been very big on web based technologies, even though I spent several years at Google writing, you know, iOS native apps. So that's that's sort of, you know, a roundabout way of saying, what do you what do you think you can take with you? You know? What will, there as OpenAI starts to, like, announce, hey. Look. We've cut our price in half, and we've we've introduced retrieval augmented, generation. And, you know, what companies are basically were only doing that, plus not very much else that are now, like Mhmm. You know, their heads are exploding. Oh my god, you know, we're only 3 times cheaper than OpenAI used to be and, you know, now we don't have a competitive mode. So there's a certain amount of figuring out what can you do. I mean, it's the same thing. You know? Go back to Hollywood. It's the same thing. You know? What have you got? The only thing that you've got that's different from anything anybody else has is your unique intent and your unique perspective on the world and your take on a problem and your your skills at working towards a solution that embraces what's unique about you. And I think that, you know, if I were gonna give this as advice, you know, scratch your own itch and be really scared of proprietary platforms. And whether the platform is, you know, Google or Facebook or you know? I I I don't see it as much as I used to, but there was a long time I would run into kids who were like, c sharp is the best. We're gonna, like, build this with c sharp. And it's, like, you know, however however good old programming language c sharp might be, it was entirely the play toy of people in Redmond. And you're you're locking yourself into a particular vendor and when you do that. So, you know, there's there's usually a platform and a platform a set of platform technologies. And if it's a proprietary platform and they're starting to move you to proprietary technologies, you're like, well, how confident am I in this platform? How much do I believe? How much am I going to risk my career on the Atari ST or the 3 d o game console or, you know, the Amiga 2 1,000 or the Palm Pilot. You know, there are all these technologies that, you know, were opportunities for people. Like, I shipped this app on this platform, and the platform's getting uptake, and it's gonna be great. But then if it loses a platform battle, then you're kind of stuck. Like, okay. I've gotta personally retool to either target a new platform, or you could make technology choices and, you know, choices about your own efforts that will lead you to knowledge, skill, and understanding that you can take with you to the next thing. I think, Gavin, I I think we could probably continue this conversation for an hour. But, what I'd like to do is kind of allow you to wrap up with a little we know that I like to talk. We know that you like to talk. But I'd love to wrap all of this up. You've got a a very incredible and kind of experienced life of just different things that you've tried, different technologies you've seen. You know, if you have one piece of advice or one thing that you love that you want to make sure everybody knows about, I mean, give us, like, a good as long as you want outro of kind of some of the some of the things that you've learned, advice you have for others, things that you you care about. Oh, you mean you're, like, giving an old man permission to rant for just a minute? Okay. Yeah. Go yeah. Go for it. Yeah. So so if you're at the beginning of your software engineering career, I would still recommend that you learn how to type at least about 40 words a minute with some accuracy. There's plenty of ways to learn to do that. It's worth the effort because it will clear space in your head for things that are actually difficult problems if you don't have to worry about the mechanical stress of typing something. This is probably not gonna be advice that's gonna be worth anything in 20 years, but it's going to be advice that's useful for the next 5. So that's a little thing. A little thing that I've been doing more recently that has been helpful is, this is such a cliche, regular exercise. I am I've been doing barbell stuff. Obviously, I'm not a giant person, but the thing that I didn't expect to get out of it, the the physical benefits are, of course, all the ones you hear about. The thing that I didn't expect to get out of it was the psychological training of stepping up and repeatedly doing something difficult that you didn't want to do in the moment in order to get a benefit further down the line. Right? And just that mental exercise is incredibly valuable. If you're if you're getting it while you, you know, exercise your body, if you're like most Americans, you will be ahead of most Americans. So it's it's worth doing, and I say this as someone who despises my time in the gym. But that the mental thing was, like, the unusual thing, and it's something that I I would encourage, especially, programmers who are all interested in technology to explore and spend some time on, and it'll it'll pay off. The time the time will come back to you, many fold, especially over the the length of your life. The other thing is get out and engage with people. Almost all the jobs that I've had had a component of knowing someone. And it's not to say that people hired me because they knew me, but they wanted to hire me because they knew me, if that makes any sense. Like, I had to be able to do the job and convince everyone and convince people who didn't know me I could do the job. But the personal connections, were always really important, And they were connections forged by oftentimes working with someone and demonstrating over a period of weeks or months or sometimes years that I could do a job, that I was good to work with, that I wasn't that I was trying to contribute towards the success of the project and to their success. And that, you know, wouldn't say it ingratiated me, but it meant that they they knew it they knew what I was about, and they could trust me and that they would they would be my champion, you know, at the next place. It's like, oh, and, you know, combine that sometimes with the skills that that are necessary at that moment in technology history. I think that was that was a good a good combination for a lot of my career. You know, there are people I work with now that I worked with 20 years ago at the startup I left Picasa for. So they kept on with their careers and, you know, when I was looking for something new to do, they're like, well, you know, we're starting up this new technology group. You could come come be the first hire, hire your own boss. So, you know all of those things. Life is long. Your relationships with other people are going to be valuable to you while you have them and then later on. You shouldn't have them for their later value. You should have them to be present with other people now. You know, be collegial if if that's all you've got in common, and if you have other interests that you share, by all means, you know, talk about them. I I have a friend that I worked at at 5 different companies with in Los Angeles, including, including Google. So, we just you know, wherever one of us landed, we'd go like, you know, I think Mike's available or I think Gavin's available. And, you know, that that worked out well for both of us over time. So, you know, keep up with people. They're they're important, And, you know, find ways to work on projects with people. If you're if you're early on, especially if you're not in a, you know, a big metropolis with a lot of technology going on, You know, open work on open source projects with people, take their take their poll request reviews seriously, Try to contribute to the project. What's in it for you is the process of contributing, not not a an immediate byproduct. Yeah. It's like, one of my my oldest son was like, I don't know if I'm gonna be able to, like, you know, help you out in your old age, dad, you know? And I'm like, you don't owe me anything. You know? The the the the good that I got out of the process of raising you was the process of raising you. You know? Go have your own great life. And that's that's where I wanna end it. Go have your own great life. Like, technology is a a great thing to do. If you hate it, though, you should find something else to do. Yeah. Absolutely. So parting words of wisdom from Gavin. Work out your work out your body, work out your mind, and get out there, meet as many people as you can, form that community. And Yeah. You know, I think that's what life's about. Alright. Is there anywhere that people can follow your work, or do you have a website or a blog or maybe just a social media? Well, I have a cop website as they used to call it at gavin dotcom, which is a an a ill maintained WordPress blog. You can find me, I'm still a little more active on x than I that I'm comfortable with, but you can find me at Gavin Doughty on on Twitter. Twitter, Elon. And, and and basically, the way you spell my name is weird enough that I have no hopes of Internet privacy at this point in my life. So it's Yeah. Gavin, g a v I n, like the governor of California, and Doughty is d o u g h t I e, like bread dough in a necktie. Yeah. Well, yeah, I I also will second Pedro's, words there. I I definitely appreciate the advice that you gave at the end outside of technology. Pedro and I have been at the same you know, worked at 3 companies together. We Right. Kind of are have that human to human relationship. Funnily enough, on a second topic, today is my 15th Twitterversary. So I've been on Twitter for 15 years today. But, yeah, I would definitely appreciate the the the technical and nontechnical advice you you gave today, Gavin. I think a lot of our viewers will appreciate both of those because getting out amongst humans, I think, is important as we start to enter this world of, VR headsets. So we'll stay real out of the matrix while we do. But, but we appreciate the time today, and and, hopefully, people can give some feedback about what they've learned from your your story.",[5270,5271,5272],"581c1c67-427f-4b12-9494-311dda7ea427","018227bf-9fc7-4ba3-a8ab-f6b77c240667","88f9dcfc-84f5-4a1b-a742-f8bd23f25edf",[],{"year":2380,"number":739,"id":4539,"episodes":5275,"show":5276},[4541,4542,4543,4544,4515],{"title":1849,"slug":1850},{"id":4797,"slug":5278,"vimeo_id":5279,"description":5280,"tile":5281,"length":13,"resources":12,"people":12,"episode_number":195,"published":5282,"title":5283,"video_transcript_html":5284,"video_transcript_text":5285,"content":12,"seo":12,"status":19,"episode_people":5286,"recommendations":5287,"season":5288},"monthly-database","891433166","When monthly infrastructure bills are too high, a new hero emerges - The Creative Cost Cutter.","18c764fb-aef1-48ad-9839-7123dddb6670","2024-01-10","Monthly Database Bill","\u003Cp>Speaker 0: And now, DevThoughts by Jack Rabbit. 1 of our vendors charges us based on the number of records in our database. We have a lot of records, so it's pretty expensive. I really like it here, and I felt personally responsible to save the company money. So I saved everything as CSV files and deleted all the records from the database.\u003C\u002Fp>\u003Cp>It's a little inconvenient for our team, but our monthly bill is almost 0 now.\u003C\u002Fp>","And now, DevThoughts by Jack Rabbit. 1 of our vendors charges us based on the number of records in our database. We have a lot of records, so it's pretty expensive. I really like it here, and I felt personally responsible to save the company money. So I saved everything as CSV files and deleted all the records from the database. It's a little inconvenient for our team, but our monthly bill is almost 0 now.",[],[],{"year":2380,"number":159,"id":4790,"episodes":5289,"show":5290},[4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803,4804,4805,4779],{"title":3380,"slug":3381},{"id":4798,"slug":5292,"vimeo_id":5293,"description":5294,"tile":5295,"length":13,"resources":12,"people":12,"episode_number":129,"published":5282,"title":5296,"video_transcript_html":5297,"video_transcript_text":5298,"content":12,"seo":12,"status":19,"episode_people":5299,"recommendations":5300,"season":5301},"git-conflicts","891433361","For the times when your Git repo is messier than the plot line of a Bill & Ted movie.","10bf9045-bd47-486e-a346-8848af6c236a","Git Conflicts","\u003Cp>Speaker 0: And now, Dev Thoughts by Jack Rabbit. Git is like the time machine I always wished for. But every time I try to fix the past, I end up with a merge conflict. And that's when I realize some things are better left unchanged.\u003C\u002Fp>","And now, Dev Thoughts by Jack Rabbit. Git is like the time machine I always wished for. But every time I try to fix the past, I end up with a merge conflict. And that's when I realize some things are better left unchanged.",[],[],{"year":2380,"number":159,"id":4790,"episodes":5302,"show":5303},[4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803,4804,4805,4779],{"title":3380,"slug":3381},{"id":4168,"slug":5305,"vimeo_id":5306,"description":5307,"tile":5308,"length":1217,"resources":12,"people":5309,"episode_number":213,"published":5311,"title":5312,"video_transcript_html":5313,"video_transcript_text":5314,"content":12,"seo":12,"status":19,"episode_people":5315,"recommendations":5317,"season":5318},"pim","895881145","Join Bryant as he tackles creation of a product information management system to manage and distribute product data to e-commerce channels. He has one hour to build the data model, add some data, and create an integration to automatically push products to Shopify.","b2764b86-d5df-4dc1-90d2-2c7bcdc1462c",[5310],{"name":3590,"url":3591},"2024-01-08","Mission: Product Information Management","\u003Cp>Speaker 0: Hi. Welcome back to the next episode of 100 Apps 100 Hours where we build some of your favorite apps or die trying in 1 hour or less. So, I am your host, Brian Gillespie, a developer advocate at Directus. And, today we have PIM, which I've only started learning about in the last few months in detail. So PIM stands for product information management, sort of like a CMS for your products.\u003C\u002Fp>\u003Cp>But the main problem that you solve with PIM is, having a single source of truth for all of your product data. Imagine that you've got different ecommerce channels like a Shopify store or you're selling on the Amazon Marketplace or walmart.com. Being able to manage all those different products and all the variations and all the colors and images and everything that goes along with selling those products in one place instead of managing it in 3 different places or 5 different places is tremendously valuable. So there's a couple of other tools out there in the space. Pimcore is the name of one of them, SalesLair and Plytix are a few of the ones that I took a look at.\u003C\u002Fp>\u003Cp>And when it comes to PIN, before we dive in to actually building this, let's just take a look at at one of these solutions. So we've got a single source of truth where we find some screenshots. Can we find some screenshots? Kind of combines, all of my products, all of my assets, and you can generate things like catalogs or product sheets and sync this product data to those individual channels, which is the the biggest part or the, to me, where the real value is. I manage all of this in one place and sync it elsewhere.\u003C\u002Fp>\u003Cp>So we are going to build APEM in 1 hour or less. Right? If you've caught some of the episodes other episodes you already know the rules of the game in that there are 60 minutes to plan and build no more no less, and the other rules are there are no rules use whatever you have at your disposal which I will take plenty of advantage of in this specific episode. Alright, so with that let's open up our timer over here on the left and we'll get started. So the first thing that I like to do anytime I'm building an app is just a little bit of planning.\u003C\u002Fp>\u003Cp>So what's the functionality? What does our data model potentially look like on the back end? I like to work back end first just because it's so much nicer building a front end if I need 1 in, with actual live data. So what are the features or the functionality that we need out of a PIM? We need to be able to store all of our, store and manage our product data.\u003C\u002Fp>\u003Cp>Store and manage, all product assets, images, video, spec sheets, etcetera. Let's make this a nice list. I'm a recovering designer. I can't ever get that out of me. And then we need to be able to store and manage, we need to control product variations.\u003C\u002Fp>\u003Cp>Product variations like color and size, you know, other things like that. Then we want to be able to sync that data with other, with ecommerce. So let's call that Shopify in this case. And, you know, maybe even have a simple product catalog, where we could show off those products. Good enough.\u003C\u002Fp>\u003Cp>That looks like a pretty good set of base functionality for a PIM. We've got the single source of truth for that. Now let's dive into what our specific data model might look like. So we've got our products, and I'll do the lowercase version here. No fill.\u003C\u002Fp>\u003Cp>We could keep this purple color though. I like that. Alright, so we've got our products, we've got variants of those products, so maybe product variants. I can imagine this is like the specific color, size, g10, things like that. We got a name, assets, other data.\u003C\u002Fp>\u003Cp>Very lovely. What else are we going to have? Probably some categories for our products. Categories or collections. Maybe those are even recursive.\u003C\u002Fp>\u003Cp>What else do we have? We have assets, which our actual back end that we're using today, Directus, will take care of for us. What else is gonna be inside our data model? This looks like a pretty good start. Right?\u003C\u002Fp>\u003Cp>So let's actually dive in. We're about 3 minutes into it. Hate to spend too much time planning. Alright. So we will log into our back end, Directus, today.\u003C\u002Fp>\u003Cp>I've got a blank instance set up. This is using the cloud service. So this is actually running on Directus cloud and not locally because we do wanna sync this data, and, I don't wanna really struggle with cores issues or anything else, in local development trying to communicate with Shopify or other systems. So you can see I've got a blank instance, and it might be helpful just to leave this up side by side. Keep an eye on the clock.\u003C\u002Fp>\u003Cp>So I've got my blank instance of Directus. What I love about it is how easy it is to go in and build out our data model. So let's just start by creating a new collection. We've got Products and we're gonna give this a generated UUID for the primary key field. We'll add in a status, a date created, user updated, just some of the system fields, that are, like, built in utilities, basically.\u003C\u002Fp>\u003Cp>You know, we wanna keep track of what user updated, who, and when, just so we've got that information. Alright. So as far as products, we've, what, got a name for the product. We probably have a SKU number, SKU number, SKU part number. You know, you could have a couple of different items here.\u003C\u002Fp>\u003Cp>We've certainly got a description of the product, which we'll use our WYSIWYG editor inside Directus for. That way we can embed rich content. Sounds great. What else are we gonna have on the individual product? You know, a like, the price information may live on the the product variance there, so I'll keep it there.\u003C\u002Fp>\u003Cp>We're gonna have assets for this specific product as well. So I can go in and we'll create a new field and we'll use the relational files interface here which will actually create a junction table in our SQL database for us. So we'll do the product let's call this product Assets. Sounds great. And I could go into the advanced editor just to see what is happening behind the scenes.\u003C\u002Fp>\u003Cp>So we've got one collection called Products. We've got a related collection called Directus Files that is a built in system collection within Directus and then it automatically creates this junction table for us. Now if I wanted to adjust the name of the fields or control what this actual collection is named, I could turn off autofill and make that available. You know, and maybe I do want to add the concerning field or the reverse field to our products and we'll do a sort field just so I could control the primary images. Everything else looks great.\u003C\u002Fp>\u003Cp>We'll just save this, and now we've got some product data, right, or a product model where we can start uploading our data. Hey, this is a shirt and the SKU number is 1234. This is the best shirt ever. And we could even go in and upload an asset. We're probably gonna do a little bit of copyright infringement here.\u003C\u002Fp>\u003Cp>Just copy the image URL. We can upload this and I can do that by URL. Cannot fetch file from URL. Okay. Service unavailable.\u003C\u002Fp>\u003Cp>So I guess I'm going to have to download this image and then we'll just upload. So we get a pretty good idea of our basic functionality here. So now we've got our assets, and let's say I did want to browse this like a potential catalog. I can go into Directus, I could change this to the card view from our information sidebar and then I would just adjust my image source here. It looks like we don't support many to many fields there.\u003C\u002Fp>\u003Cp>So maybe we go back in and add a, like, a featured image. Featured image. Great. Alright. So this is just a single image that we want.\u003C\u002Fp>\u003Cp>So we'll go into that specific product, and I could pick this same image from the database. Alright. Great. So now we've got the featured image, we've got a name for the product, we've got our SKU number, and I can even go in and control how the image is cropped or not. So great.\u003C\u002Fp>\u003Cp>Now we've kind of got this, catalog looking module happening. Let's go in and finish fleshing out our data model. So I'll just clean this up a little bit. We've got our product variance and we'll use the generated UUID. Same thing, I will go in and add all these separate fields.\u003C\u002Fp>\u003Cp>Maybe we do have a sort field on the variant in case one variant takes priority over the other. Yeah. Kinda hit or miss. Alright. So we've got, our product variants.\u003C\u002Fp>\u003Cp>What we are going to do is create a many to one relationship back to our parent product. So this will be the product, and we're gonna choose the products collection. And this is a mini to one relationship. So I'm gonna go into the advanced field settings here. We'll just create a new, a one to many on the other side of the equation back on our products table for product variance.\u003C\u002Fp>\u003Cp>So now what I've done, in effect, is link these two collections or these two tables in our database together so that we can query and keep track of them. So now let's do what do we want to add for our other variant data, you know, so we've got color, that's just a string, we've got, size, etcetera. Great. We want to control a price here. You know, maybe we got that g ten, you know, unique identifier.\u003C\u002Fp>\u003Cp>What is that? The global trade identification number? Something like that. Let's go in and add a decimal. Actually, that'd be that'd be a float.\u003C\u002Fp>\u003Cp>And then we'll do maybe just 2 decimal places. Let's call this price. Great. Alright. So now we've got some items on our variants.\u003C\u002Fp>\u003Cp>Let's go back and actually look at how this works. So I go back into our product and we can see we've got our variants here and if I wanted to create a new variant, I can. Let's call this red, we'll call it small. The GTIN is 666 and the price for this is $25. We could go in and create another variant.\u003C\u002Fp>\u003Cp>This is red, this is medium, you know, 777, and the price is $16. Whatever. I can go in and actually set up our like what fields are displayed here. This doesn't look very great just to see the UUIDs. So let's sort that out, right.\u003C\u002Fp>\u003Cp>And we'll make this just a little bit bigger as well. Alright, so we go back into our data model, we go to our product variants, we look for our interface and we have this, control over do we want a list, do we want a table. Let's use the table in this case and we will add our color, size, maybe we'll do GTIN and price combinations. Now you also may have, like a variant that has a different image as well. So we could potentially add that as well.\u003C\u002Fp>\u003Cp>We'll just keep it simple for now and for the related fields, let's show the color. We'll add just like a little label for it so we know what it is, we'll add the size and we'll add size here. So let's see how that looks inside our PIM and great. Now we can see here's the different variants, there's the pricing, which, you know, maybe we wanna format that a little more, but great. And we can click on those to edit that information all that we need.\u003C\u002Fp>\u003Cp>Alright, now let's build out our categories, right? This is the only thing remaining on our data model. We want to build out categories for our different products. So we have a category, we'll use the generated UUID again, and not sure we need like a status on these, but maybe we do wanna sort and cool. Alright, so we've got a category name.\u003C\u002Fp>\u003Cp>Probably a short description for category. You know, we keep kinda splitting hairs which you on what detail you you need for something like this. If we look at, let's let's look at something like Pimcore. What what do they have available on their website? This looks really kinda messy to me.\u003C\u002Fp>\u003Cp>So we've got, different objects. We've got media. You know, I don't really know for certain how others set up their data model for this, but to me this is how I would do it. Now I could go in and have something like another classification for this. We could use, like, tags as a different type of taxonomy, a different way to organize the data.\u003C\u002Fp>\u003Cp>But I also have, you know, probably subcategories and parent categories. So Directus makes doing a recursive relationship like that very, very easy, where I can go in and do a many to 1 or a one to many relationship. So I'll just do one to many. We'll call this the, these are gonna be subcategories. Let's just do let's call let's do it the reverse.\u003C\u002Fp>\u003Cp>So this will be the parent category. We'll use the categories collection. So we're creating a relationship, a recursive relationship, and we're gonna do the subcategories. So we've got parent category which will be a single parent, then we have subcategories. Great.\u003C\u002Fp>\u003Cp>So what effect does that give us? Right? If I go into categories so let's create a new category we'll call it apparel. This is, let's call it actually, well, like outerwear or something like that. Great.\u003C\u002Fp>\u003Cp>Hoodies, jackets, and such. And we don't have a parent category or a subcategory yet, so let's just create a subcategory like hoodies. Hoodies. Great. We'll save that and now we can see we've got subcategories and, you know, potentially nest these.\u003C\u002Fp>\u003Cp>If I go back to our product, now we want to create a relationship with that category. So in this case, typically, a product may only have a single category. I I've seen some of these systems that will have, like, smart collections, which I think is a a really neat way to model it. And I think even Shopify, so I've got a a Shopify store set up here. Let's let's just take a look at how they handle it.\u003C\u002Fp>\u003Cp>If this can be in a multiple categories or no? So it looks just like a single category. You can set up different collections for it. So we'll keep this to be a mini to 1. So we've got a category and our related collection will be categories.\u003C\u002Fp>\u003Cp>And for our display template, maybe we just want to show the name of that category. Now, one of the other things I can do is control the interface to how the related values are displayed, just to make sure this looks how we want. So now if I go into our product, we can select the category hoodies or outerwear specifically. Great. Cool.\u003C\u002Fp>\u003Cp>Now, let's say what functionality, let's go back here. I always get ahead of myself. So now we've got a way to store and manage all of our product data. We've got a way to store all of our product assets, right? So I could go in and upload.\u003C\u002Fp>\u003Cp>Let's say I've got a PDF here. I could go in and upload this PDF. If I've got a spreadsheet, I could certainly do that as well. You know, I've got a CSV or whatever data that I've got, I can upload that and store it within this product. But, you know, what fun is it having these products in isolation?\u003C\u002Fp>\u003Cp>We wanna be able to use these specific products. So out of the box, because we've set up this data model, we know the structure and everything, Directus gives us the ability to well, it just gives us REST and GraphQL APIs. So I could go in and do something like this where we've got items. Products. I'm getting an error message because I haven't set up permissions, but let's imagine we wanted to allow anybody to access this product information.\u003C\u002Fp>\u003Cp>I can go to our setup, We'll go to access control and we'll go to our public role, which just controls what information is publicly available. And because none of this is secret at this point, let's go in and try it now. So if I hit refresh, we go to our products, we can see our product data. So there's the shirt, there's the SKU number, there's the description, we've got a featured image, UUID, And when it comes to our variants, we can't I can't see that information. That sucks.\u003C\u002Fp>\u003Cp>How do we fix this? I can actually do something like this, which is one of my favorite features inside Directus that I can query the related collections or the related fields from a single API call. So it's very GraphQL like even though this is REST based, and I, you know, there's a GraphQL API. I'm not a super huge fan. You may be.\u003C\u002Fp>\u003Cp>No sweat. So I could do something. We'll give, like, we'll use a wildcard here for all the root level fields. And then let's go in and do something like this where we have product variance and then we'll get all of the brute level fields for that. So as soon as I do that, now you can see I start to see that data that we want.\u003C\u002Fp>\u003Cp>And maybe I don't need all of this. Maybe we just need the ID product_variance dot color. I could get a little more sophisticated and copy paste dot size dot price dot g10. Alright. And now that will only return the data that I'm interested in which is nice prevents over fetching keeps things really fast when we are sending a lot of data over the network.\u003C\u002Fp>\u003Cp>Alright. So we've got the ability to store and manage all of our product assets. Let's control our product variations. How do we sync this data with an ecommerce system? Right?\u003C\u002Fp>\u003Cp>We're managing all of this in a single location. Maybe I've got Shopify, which is what we're gonna use. Maybe I've got an Amazon storefront setup. Maybe we've got 3 or 4 other things going on. Right?\u003C\u002Fp>\u003Cp>So how do we sync this information this data over? So I'm gonna go to our store and this is just the the Shopify demo store that I've set up. None of these products or anything that I created. Right? It looks like this is set up for snowboarding, which I tried once or twice, wasn't very good at it.\u003C\u002Fp>\u003Cp>But I'm gonna go into the settings of this and we're gonna try to find how to, get access to the API here. Let's look and see domains. Maybe it's here inside the apps. Okay. So we'll look for develop apps.\u003C\u002Fp>\u003Cp>We're gonna create an app that can connect with this storefront. Let's just take a look at it and see what it actually looks like on the front end. We got a password protection right now. We're gonna call this the Directus Shopify sync, maybe? We'll create this app inside the Shopify store.\u003C\u002Fp>\u003Cp>Alright. So it looks like the next thing that we'll need to do, we're probably gonna be using the admin API because we're not building a shopping experience. We're trying to send product data to Shopify, so that we can have that experience inside Shopify. So let's configure our access scopes. It's probably gonna be around products.\u003C\u002Fp>\u003Cp>And I'm not sure exactly which one of these I need. Product listings, product feeds, viewer manage products, let's go with that. We see the webhook subscriptions. We'll come back to webhooks. Let's just save that.\u003C\u002Fp>\u003Cp>What else do we need? We need some actual credentials. So we need an access token, a way to get that data. Use your client secret to verify incoming webhooks. Let's go ahead and install this app.\u003C\u002Fp>\u003Cp>This will give this app access to our storefront data. And we're only gonna reveal this token once and boom, we don't need it any longer. Not sure exactly how to set up the webhooks. Is that part of this? Start using the admin API.\u003C\u002Fp>\u003Cp>Do you have to create webhooks through the through the API? I don't know. If your app creates a webhook subscription oh, okay. So it looks like you do have to create webhooks using the API. Yeah.\u003C\u002Fp>\u003Cp>Alright. We'll come back to that. No worries. Alright. So now I've got this product information inside our account, I want to send it over to Shopify.\u003C\u002Fp>\u003Cp>How can I do that? Well, I could probably write some kind of custom app or I could just go in and use the flows, the the automation builder inside Directus. We're looking pretty good on time. We're at 36 minutes. So I'm just gonna make this full screen so we can dive in a little deeper.\u003C\u002Fp>\u003Cp>Let's call this send products to Shopify. Now how are we gonna trigger this? There's multiple ways to set up triggers. So I could manually trigger this. I could set it up on a cron job, so a regular interval.\u003C\u002Fp>\u003Cp>For now, I'm just gonna manually trigger this and we'll go we'll trigger this on our products section and maybe we'll require confirmation. Are you sure? Yes, we're sure. So the location here allows me to control where I can actually trigger this. So I'm just gonna trigger this on the collection page only.\u003C\u002Fp>\u003Cp>No. I could do both, you know, I could potentially send one product or maybe I want to send all of the products. We'll just keep that set up. Alright. So now let's go in.\u003C\u002Fp>\u003Cp>We'll save this flow and if we go back to our products page on the right hand side, I could see the option to send this to Shopify. So check an item. Are you sure? Yes, we'll run a flow. And if I go back to our flow, I refresh and I don't see my logs.\u003C\u002Fp>\u003Cp>Where are the logs? I ran this flow. Where is it? Let's make it not require a selection. Not sure if something happened here.\u003C\u002Fp>\u003Cp>What's what's going on? So we'll go to our flow. We'll hit send products to Shopify. Go to flows. Okay.\u003C\u002Fp>\u003Cp>Now they're showing up and we can see kind of the data that is being triggered here. So just collections, products. Great. Cool. So now, how do we get this actual data, right?\u003C\u002Fp>\u003Cp>We will go in and read the data. So let's just call it read products. That's gonna be the name of this operation. We're gonna select all the products from the collection products and then I could, like, filter this down if I wanted to. You know, maybe I I just leave it all for now.\u003C\u002Fp>\u003Cp>We're gonna select all the IDs and save it. And then once we read that data, we want to send to Shopify. Great. We'll, actually use the webhook HTTP request. How are we going to send the data?\u003C\u002Fp>\u003Cp>It's probably gonna be a post method. We're gonna go let's go back to Shopify really quickly. So we've got our storefront API documentation. Zoom in a little bit. Do we want the storefront?\u003C\u002Fp>\u003Cp>I don't think we do. So many tabs. So we go to admin. Oh, developer tools, API libraries. Oh, boy.\u003C\u002Fp>\u003Cp>Getting into the thick of it now. Let's go back to where's our actual store? Right. Explore, start using the admin API. Okay.\u003C\u002Fp>\u003Cp>Very confusing documentation here. Alright. So we'll go to our REST API reference. We'll look for products is probably what we want. We want to create a product.\u003C\u002Fp>\u003Cp>So there's what the product resource looks like. Here's our create a product, and there's our URL. So what does the base URL look like? Authentication. Okay.\u003C\u002Fp>\u003Cp>So it looks like the URL structure here is the Shopify unique shop ID or the shop slug, I'm assuming. My shopify.com\u002Fadmin\u002Fapi\u002F apiversion\u002Fresource. So our base URL setup so if we go back, will look something like this. Let's just pick up this actual URL. 100 apps, 100 hours, nice slugified version.\u003C\u002Fp>\u003Cp>Myshopifiedot com\u002F what was it? Real genius here. Okay. Let's go half screen. Myshopify.com\u002Fadmin\u002Fapi\u002F what?\u003C\u002Fp>\u003Cp>2023 dash 10 dash products dot JSON. Okay. So there's our URL. Great. Now I've got that access token.\u003C\u002Fp>\u003Cp>Authentication. Looks like we have to pass that as a header as well. So we'll go into direct us. We'll add some headers. Got content dash type.\u003C\u002Fp>\u003Cp>That's gonna be application slash JSON. And then we've got x Shopify access token. And then we've got this special token that you're not gonna look at, you're not gonna read, you're not gonna try to use. Alright. So now we can send a request.\u003C\u002Fp>\u003Cp>So we go down. We're looking for products again. We're gonna create a product inside Shopify. Alright. So what are the details we need for our products?\u003C\u002Fp>\u003Cp>We got a product dot title, body HTML, that's our description. We've got a product vendor, that's one thing we forgot inside our pin that we could add. We've got the product status. So, let's go in and just do something really simple. We'll do products.\u003C\u002Fp>\u003Cp>Okay. So there's our product. Then we're gonna have a title for the product, what's that gonna be? Let's take a step back here. I'm just gonna save this.\u003C\u002Fp>\u003Cp>I need to see what actual data is coming from this call here before we get carried away. Create a new product. Can you create is there an endpoint for creating multiple products? That's what I'm interested in. Create a new product.\u003C\u002Fp>\u003Cp>Retrieve a list of products. Looks like we're just going to be able to create a single product at a time. So we may have to break this apart and do 2 separate flows. Imagine that I had another product. We'll call it test product.\u003C\u002Fp>\u003Cp>No. Let's not do that. Let's call it a hoodie. So it is hoodie 123. Let's go to unsplash maybe.\u003C\u002Fp>\u003Cp>Do a hoodie. And this looks like a hoodie with a statement so we'll just copy paste that. Okay. We'll import the file from the URL. Boom.\u003C\u002Fp>\u003Cp>We got a featured image. Put a description. Great. Alright. So now we get 2 products.\u003C\u002Fp>\u003Cp>We're just gonna select these products. We're gonna send the well, what happens if we don't select? We could still run this flow. We'll We'll take a look. We'll send these products down to our flow.\u003C\u002Fp>\u003Cp>And let's just look at the log. Right? So now I'm getting an array of products. Okay. And maybe we wanna do we're gonna break this apart because we're gonna have to send a single product to Shopify.\u003C\u002Fp>\u003Cp>But before we do that, I want to format products. Right? So we'll take our product. We'll run the format function. So Directus allows you to run arbitrary JavaScript inside the flows, which is really nice for stuff like this.\u003C\u002Fp>\u003Cp>We've got the data, so our products are gonna be constant products equals data dot read products. I think that's gonna be it. And then we have some formatting logic here. Right. Just thinking out loud.\u003C\u002Fp>\u003Cp>So now I'm just gonna save this really quick. And one of the other things that I like to do when I'm working with these flows is just copy the data I'm getting back right here, and I'm gonna put it inside my code editor. I've got a starter project here just in case we needed it. So now I've got the data structure. And if I look at Shopify gosh.\u003C\u002Fp>\u003Cp>We've got, like, 35 different tabs going on. That's the fun of doing these live is actually trying to figure out how to all how to keep it all sane on one screen. Admin, API. Alright. So we go to the admin API, Rest admin API.\u003C\u002Fp>\u003Cp>So we go to our products. What things do we need to pass to our products at a most at a minimum just to actually create these products. So here we go. Here's a new draft product type of setup. Creates a new draft product.\u003C\u002Fp>\u003Cp>And can we do let's just make this even more complicated. Right? We'll do like a split screen? Somebody still needs to teach me to properly arc. Alright.\u003C\u002Fp>\u003Cp>So I've got Directus, got our product. Okay. We got like 30 things going on here. A lot of fun. Alright.\u003C\u002Fp>\u003Cp>So, let's run a format script. We've got our data over here that is coming from our Directus database. Let's go in and add our logic here. We got products. Let's do Shopify products dot map.\u003C\u002Fp>\u003Cp>Oh, no. Shopify products equals products dot map product. Yeah. Products. And then we'll return something.\u003C\u002Fp>\u003Cp>We got our function there. And then at the end of this, we're gonna return Shopify products. So we're just gonna return the array of products we want from Shopify. Alright. So we got the products and then we're gonna return a title for the product which is gonna be the product dot what name.\u003C\u002Fp>\u003Cp>We need a comma there. Then we've got the body_html, that's gonna be product dot description. We've got product dot vendor. We don't have that product dot type. We could fetch that, and then for now, let's just try this and see how far that actually gets us.\u003C\u002Fp>\u003Cp>Product dot name. We're gonna return our Shopify products. Let's go back in and I'll just make this full screen. Do products. I will look for our sidebar.\u003C\u002Fp>\u003Cp>Where are you sidebar? There you are, mister sidebar. We'll send the products to Shopify. Let's just test our flow and see what came out the other side. Right.\u003C\u002Fp>\u003Cp>So this looks good. This is our format that we're gonna send to Shopify. I'm gonna take just my my headers that I've got here. Let's just stuff those in this, take my URL that we so carefully constructed. And, I'm just gonna leave that.\u003C\u002Fp>\u003Cp>We're gonna create a new flow. So we're gonna go in and create Shopify product and this will be triggered from that other flow, so triggered by another flow. We'll leave the response body blank and here we're just gonna do the HTTP request that we just did. So I'm gonna paste in that. I've got my headers.\u003C\u002Fp>\u003Cp>Still stuck on the clipboard somewhere. I'll edit raw value and now we've got that. And here, I think it is just going to be something like this where we do actually, hit save. We'll just disconnect this for now. Hit save.\u003C\u002Fp>\u003Cp>Go back to our other flow and trigger that flow, and we'll just see what trigger Shopify, create. And Directus will create a key for you, you can also rename this key, but this key, all the data that gets generated in this operation gets appended to the flow underneath this specific key. Let's just take a quick peek at time. How are we doing? We've got 20 minutes left.\u003C\u002Fp>\u003Cp>Great. And then we're just gonna trigger this flow. So we'll select the Shopify flow. It's okay to create these in parallel and then for our payload, we're gonna do simply this. We've got the double curly brackets.\u003C\u002Fp>\u003Cp>We've got the data. No. What's the name of that operation? Format product. Is that what the key is?\u003C\u002Fp>\u003Cp>We're just gonna use this key. So we're gonna trigger the flow and the data from that, format product, we're gonna pass that to that other flow. Alright. So fingers crossed, stay with me here, we are building cool stuff. So now we're gonna send these products to Shopify, running that flow.\u003C\u002Fp>\u003Cp>And we can see here we triggered this. We got back null null, this is what we sent to that. And if we go to our other flow, where we're actually creating, we can see that this ran twice, which is the what we actually want. We wanna see the the payload that we got was the product. So we're just gonna pick up the trigger.\u003C\u002Fp>\u003Cp>Payload and that's what we're gonna send. Great. Amazing. So, in our request body here, we're gonna do something like this where we've got, quotations, double curly bracket, trigger dot payload and trigger is, prefixed with a dollar sign just because it's a unique, special key to get the trigger data. And fingers crossed.\u003C\u002Fp>\u003Cp>Right? Can we send this data to Shopify? Can we build a working PIM in an hour? Let's test it out. We're gonna run I I don't think it matters if I select these or not.\u003C\u002Fp>\u003Cp>Let's go in. We'll see. We have our send products to Shopify. That ran. Hopefully triggered this other flow that we have.\u003C\u002Fp>\u003Cp>We got a Shopify product. Bad request. By accessing the Shopify, you agree to parameters are missing or invalid. Okay. So we're missing some parameters.\u003C\u002Fp>\u003Cp>Let's take a look at the actual data that we sent. Payload, got the product resource, body HTML, supports HTML formatting, product status dot draft, Shopify access token. It looks like the body is not defined. So, I think I goofed up and that and maybe we're just sending the trigger. Let's take a look.\u003C\u002Fp>\u003Cp>Did I get it wrong? Do we just need to send the trigger? So, let's make this more fun. Right. Let's go to our Shopify store.\u003C\u002Fp>\u003Cp>Alright. We're gonna close this guy. Alright. I'm on the products. I'm gonna hit refresh.\u003C\u002Fp>\u003Cp>Dun dun duh. I don't I don't see any products in here. Alright. So, let's go back. Obviously, I goofed somewhere again.\u003C\u002Fp>\u003Cp>And we're definitely in danger of running out of time here. We got a bad request, missing parameters. Okay. So there's the body, product title, product dot HTML. What are we missing?\u003C\u002Fp>\u003Cp>100 apps, 100 hours. We've got our access token. We've got our content type. Required parameter. Did I spell something wrong?\u003C\u002Fp>\u003Cp>This is always the danger of working against the clock like this. What is this issue going to be? Request body is product. Alright. So product title body HTML.\u003C\u002Fp>\u003Cp>I don't see where this is going wrong. Shopify access token. Bad request. Let's take a look at our app. Do we have the wrong scopes or something?\u003C\u002Fp>\u003Cp>Let's just try one quick tweak. Go back to our send products for our format product. Let's just leave out the title. Right. So we'll just comment this out.\u003C\u002Fp>\u003Cp>Return Shopify products. Send products to Shopify, flows, bad request, product underscore. Is it the way that it is actually sending this data? Sometimes if we do something like this, where we got trigger. Boom boom, Ron.\u003C\u002Fp>\u003Cp>Created. Boom. Alright. So dramatic reveal time. Dun dun dun.\u003C\u002Fp>\u003Cp>We have sent this data to Shopify. Did we? Where do we go? Okay. So we've got a hoodie, we've got a shirt.\u003C\u002Fp>\u003Cp>There they are. Boom. Now they are in our system. That is how it's done, ladies and gentlemen. Where where we at on time?\u003C\u002Fp>\u003Cp>We got 13 minutes left. We have managed to sync product data. Let's let's just take this one step further if we can, and let's create a product with let's at least get the images over there. Right? So on our product resource, we've got a list of product images, create a new product image, create a product image using a source URL that will be downloaded to Shopify.\u003C\u002Fp>\u003Cp>Okay. So it looks like you can you may be able to pass these, and I'll just go in and very briefly let's just delete these products. Delete these products. Can't be undone. Those are deleted.\u003C\u002Fp>\u003Cp>Those are no longer in the system. Let's adjust our payload here. So we're gonna go back to the formatting option. We'll comment that back in. We can have that now and then on the Shopify side, we've got okay.\u003C\u002Fp>\u003Cp>So we got a product dot images. This will be images and then we've got an array And then what? Array of objects, and then we just got a source attribute. So the source if we look at our code again, Right. We've got our featured image.\u003C\u002Fp>\u003Cp>Let's just pluck that for now And, we're gonna do something like this where we've got the source is gonna be, HTTPS direct us app slash assets slash what was the name of that? Featured underscore image. Featured. Oop. Gotta do our little squirrellys featured image.\u003C\u002Fp>\u003Cp>Close that off. Should be good. The last piece of the puzzle for this is going to be enabling permissions for the actual files inside Directus as well, Because Shopify needs to access that. So 2 ways I could do it. I can add an access token, when I'm sending that call to Shopify Or in this case, I'm just gonna go in and we'll keep this really simple.\u003C\u002Fp>\u003Cp>Under direct as files, we'll do all access. And we're just going to send and cross our fingers hoping that this flow actually works. We could do the dramatic reveal inside Shopify. Right? I broke something trying to send the images over.\u003C\u002Fp>\u003Cp>Where did you go? Where did you go? Alright. What broke? Right?\u003C\u002Fp>\u003Cp>Clearly, we broke something. Great Shopify product. What do we send? Product title hoodie. Do we actually break something inside the I don't see my logs.\u003C\u002Fp>\u003Cp>4 to 5 minutes ago, that's not correct. Run Shopify flows. Go into our flow. Less than a minute ago, featured image is not defined. Okay.\u003C\u002Fp>\u003Cp>So we did not have one with a featured image, and that broke. What did I do wrong? Again, always fun on the clock. I guess you have to actually use product dot featured image. Right?\u003C\u002Fp>\u003Cp>Featured image. That would make a ton of sense. Too much sense actually. Alright. Last time.\u003C\u002Fp>\u003Cp>Send products to Shopify. Did it work? Let's test our flow. Create Shopify product. Created.\u003C\u002Fp>\u003Cp>Good looking. Alright. So if we open up Shopify, we go to our products, we can see our hoodie. Here is our actual data, so there's the image that we've got, there's the shirt that we loaded up, and this is all coming from our pin, our back end, our single source of truth. So that is how it's done, ladies and gentlemen.\u003C\u002Fp>\u003Cp>We are syncing with Shopify. Last on our list, can we get this in 8 minutes? I have no idea. We've got a simple product catalog. Now, I don't think we could do this in 8 minutes, but let's give it a shot, right?\u003C\u002Fp>\u003Cp>We want to display all of our products on the front end of our website, so people can browse those. So because there are no rules, I am a huge fan of Tailwind UI and Tailwind CSS in general. They've got some great looking ecommerce components that we may just repurpose for this, like, here's a nice product list. And I've got a Nuxt starter application sitting here just ready to be used. So let's see if we can actually get this product information displayed and rendering on a product catalog in just a couple minutes.\u003C\u002Fp>\u003Cp>Alright, so we're gonna just forget Shopify for now. Let's simplify. Alright. This looks pretty good. This is a with border grid.\u003C\u002Fp>\u003Cp>We've got a view component here. We've got our products. Let's just copy this template. Alright, so we're gonna go to our index page. We probably got some script action that we'll do there.\u003C\u002Fp>\u003Cp>For now, I'm gonna if we just copy their products. Right. Constant products. Not gonna look great, and they've got an icon. So we'll do the star.\u003C\u002Fp>\u003Cp>Let's just omit the products ratings for now. Right? Just comment those up. Run this. If I look at local host, say now we've got got something cooking here.\u003C\u002Fp>\u003Cp>K. And now, let's actually fetch these products from Directus. We wanna do this live. Right? Going to do what, we got our products.\u003C\u002Fp>\u003Cp>Is it equal to await use directus? I've got a just a composable here to fetch this data using the rectus SDK. We're going to read items and that's gonna be products. And is this actually gonna be all we really need? Usually, when I am developing, I often tend to do stuff like this where I will just render out the data inside the browser just to make sure we're seeing we're getting the stuff that we need.\u003C\u002Fp>\u003Cp>Item\u002Fproducts failed. Oh, wait. Use direct us, read items, products. Why is this not working correctly? Well, that would be because I didn't actually set up the directus URL properly.\u003C\u002Fp>\u003Cp>We are at 5 minutes, so I'm gonna load my PIM URL here. That is my Directus base URL. The server token, probably not even needed in this setup, but, we'll just reload. Let's fire back up the dev server. Let's see if we can get this data rendering from Directus.\u003C\u002Fp>\u003Cp>Boom. So there's our actual data. Right? We no longer need this. Let's uncomment.\u003C\u002Fp>\u003Cp>And just looking at this data, we've got the v4productandproducts. We wanna render the product dot id, we'll have that information. The image source, because we can use Directus as an image provider it will transform assets for you. I'm just going to use the NUTS image tag which is already set up in this. We will use product dot featured image.\u003C\u002Fp>\u003Cp>We'll just leave alt text blank for now because there is there's none. We haven't got that set up. We got product dothref. We don't have one of those currently, but we could do slash products slash what? Product dot ID.\u003C\u002Fp>\u003Cp>That would be the probably the URL. We got a product dot name, we got a product dot price. What else? I wanna fix the template structure here and then maybe we want to render out, div. We'll do like a tailwindpros class and give this v html.\u003C\u002Fp>\u003Cp>We'll render the product description. Input must be a string received undefined. So if there let's make sure oh, forgot the item there. Boom. So there we have it.\u003C\u002Fp>\u003Cp>We have got our items rendered out. You know, we could continue fleshing this out with the help of Tailwind into a catalog. But now we have done several things, Right? Let's recap with our final two and a half minutes. We have created a PIM system that can store and manage all of our product data, store all of our product assets, control all of our product variations, we can sync that data with Shopify, and we can have a simple catalog.\u003C\u002Fp>\u003Cp>But, Bryant, you forgot one thing, our translations for this data. What if I want to have it in French or German or, Canadian? Bad bad joke for all all of our Canadian followers. Apologies there. So with a minute 30, how can we set up, translations for this?\u003C\u002Fp>\u003Cp>Right? How can I control other languages? So I'm gonna go into our products. The first thing I'm gonna do is just create a languages collection. We've got a language code.\u003C\u002Fp>\u003Cp>Alright. We've got a title or name of the language. Alright. And let's create 2 languages. We have EN, US, English.\u003C\u002Fp>\u003Cp>Are we gonna do it 55 seconds? Let's just say fr for fridge. I think that's the code. No idea. We'll go back to our product.\u003C\u002Fp>\u003Cp>Let's go in and there's a special translations interface within Directus. Clock is ticking, Brian. So we got the code, we got the direction field, that would probably be right to left. Use current language, we'll save this And then inside our product translations, we would go in and do, name, We would do a description. And where we at on time?\u003C\u002Fp>\u003Cp>10 seconds. Brian, can you do it? 10, 9, 8, 7. How do we add translations for this? We can do English name.\u003C\u002Fp>\u003Cp>So close. We hit the 60 seconds. I'm just going to finish this or the 60 minutes. We can do the French translation here and save it. So, with Directus we can manage all of these different things.\u003C\u002Fp>\u003Cp>We hit time there, that felt really good though. We can manage translations or multilingual content. Alright. So that was a fun one. Multilingual content.\u003C\u002Fp>\u003Cp>Really enjoyed this one. Building a PIM in 60 minutes or less. There's obviously more to this, but my next steps would be to flesh out that sync engine a little bit, so that if we made updates inside Directus, I could send those over to Shopify. I could even use the Shopify webhooks in this case, if I made a change to that information in Shopify to sync it back into Directus. I would also probably go in and flesh out the translations interface just a little bit more.\u003C\u002Fp>\u003Cp>So that I have all the necessary fields. You know, and I may even have specific fields that we want to add to Shopify versus something like Amazon. So we could go in and do, you know, some more different some different channels that we wanna send this data to. But for 60 minutes, amazing to see what you can actually build with Directus. Stay tuned for the next episode.\u003C\u002Fp>\u003Cp>I'm sure we'll have a good one and you'll get to see me hack and slash and burn my way through another product.\u003C\u002Fp>","Hi. Welcome back to the next episode of 100 Apps 100 Hours where we build some of your favorite apps or die trying in 1 hour or less. So, I am your host, Brian Gillespie, a developer advocate at Directus. And, today we have PIM, which I've only started learning about in the last few months in detail. So PIM stands for product information management, sort of like a CMS for your products. But the main problem that you solve with PIM is, having a single source of truth for all of your product data. Imagine that you've got different ecommerce channels like a Shopify store or you're selling on the Amazon Marketplace or walmart.com. Being able to manage all those different products and all the variations and all the colors and images and everything that goes along with selling those products in one place instead of managing it in 3 different places or 5 different places is tremendously valuable. So there's a couple of other tools out there in the space. Pimcore is the name of one of them, SalesLair and Plytix are a few of the ones that I took a look at. And when it comes to PIN, before we dive in to actually building this, let's just take a look at at one of these solutions. So we've got a single source of truth where we find some screenshots. Can we find some screenshots? Kind of combines, all of my products, all of my assets, and you can generate things like catalogs or product sheets and sync this product data to those individual channels, which is the the biggest part or the, to me, where the real value is. I manage all of this in one place and sync it elsewhere. So we are going to build APEM in 1 hour or less. Right? If you've caught some of the episodes other episodes you already know the rules of the game in that there are 60 minutes to plan and build no more no less, and the other rules are there are no rules use whatever you have at your disposal which I will take plenty of advantage of in this specific episode. Alright, so with that let's open up our timer over here on the left and we'll get started. So the first thing that I like to do anytime I'm building an app is just a little bit of planning. So what's the functionality? What does our data model potentially look like on the back end? I like to work back end first just because it's so much nicer building a front end if I need 1 in, with actual live data. So what are the features or the functionality that we need out of a PIM? We need to be able to store all of our, store and manage our product data. Store and manage, all product assets, images, video, spec sheets, etcetera. Let's make this a nice list. I'm a recovering designer. I can't ever get that out of me. And then we need to be able to store and manage, we need to control product variations. Product variations like color and size, you know, other things like that. Then we want to be able to sync that data with other, with ecommerce. So let's call that Shopify in this case. And, you know, maybe even have a simple product catalog, where we could show off those products. Good enough. That looks like a pretty good set of base functionality for a PIM. We've got the single source of truth for that. Now let's dive into what our specific data model might look like. So we've got our products, and I'll do the lowercase version here. No fill. We could keep this purple color though. I like that. Alright, so we've got our products, we've got variants of those products, so maybe product variants. I can imagine this is like the specific color, size, g10, things like that. We got a name, assets, other data. Very lovely. What else are we going to have? Probably some categories for our products. Categories or collections. Maybe those are even recursive. What else do we have? We have assets, which our actual back end that we're using today, Directus, will take care of for us. What else is gonna be inside our data model? This looks like a pretty good start. Right? So let's actually dive in. We're about 3 minutes into it. Hate to spend too much time planning. Alright. So we will log into our back end, Directus, today. I've got a blank instance set up. This is using the cloud service. So this is actually running on Directus cloud and not locally because we do wanna sync this data, and, I don't wanna really struggle with cores issues or anything else, in local development trying to communicate with Shopify or other systems. So you can see I've got a blank instance, and it might be helpful just to leave this up side by side. Keep an eye on the clock. So I've got my blank instance of Directus. What I love about it is how easy it is to go in and build out our data model. So let's just start by creating a new collection. We've got Products and we're gonna give this a generated UUID for the primary key field. We'll add in a status, a date created, user updated, just some of the system fields, that are, like, built in utilities, basically. You know, we wanna keep track of what user updated, who, and when, just so we've got that information. Alright. So as far as products, we've, what, got a name for the product. We probably have a SKU number, SKU number, SKU part number. You know, you could have a couple of different items here. We've certainly got a description of the product, which we'll use our WYSIWYG editor inside Directus for. That way we can embed rich content. Sounds great. What else are we gonna have on the individual product? You know, a like, the price information may live on the the product variance there, so I'll keep it there. We're gonna have assets for this specific product as well. So I can go in and we'll create a new field and we'll use the relational files interface here which will actually create a junction table in our SQL database for us. So we'll do the product let's call this product Assets. Sounds great. And I could go into the advanced editor just to see what is happening behind the scenes. So we've got one collection called Products. We've got a related collection called Directus Files that is a built in system collection within Directus and then it automatically creates this junction table for us. Now if I wanted to adjust the name of the fields or control what this actual collection is named, I could turn off autofill and make that available. You know, and maybe I do want to add the concerning field or the reverse field to our products and we'll do a sort field just so I could control the primary images. Everything else looks great. We'll just save this, and now we've got some product data, right, or a product model where we can start uploading our data. Hey, this is a shirt and the SKU number is 1234. This is the best shirt ever. And we could even go in and upload an asset. We're probably gonna do a little bit of copyright infringement here. Just copy the image URL. We can upload this and I can do that by URL. Cannot fetch file from URL. Okay. Service unavailable. So I guess I'm going to have to download this image and then we'll just upload. So we get a pretty good idea of our basic functionality here. So now we've got our assets, and let's say I did want to browse this like a potential catalog. I can go into Directus, I could change this to the card view from our information sidebar and then I would just adjust my image source here. It looks like we don't support many to many fields there. So maybe we go back in and add a, like, a featured image. Featured image. Great. Alright. So this is just a single image that we want. So we'll go into that specific product, and I could pick this same image from the database. Alright. Great. So now we've got the featured image, we've got a name for the product, we've got our SKU number, and I can even go in and control how the image is cropped or not. So great. Now we've kind of got this, catalog looking module happening. Let's go in and finish fleshing out our data model. So I'll just clean this up a little bit. We've got our product variance and we'll use the generated UUID. Same thing, I will go in and add all these separate fields. Maybe we do have a sort field on the variant in case one variant takes priority over the other. Yeah. Kinda hit or miss. Alright. So we've got, our product variants. What we are going to do is create a many to one relationship back to our parent product. So this will be the product, and we're gonna choose the products collection. And this is a mini to one relationship. So I'm gonna go into the advanced field settings here. We'll just create a new, a one to many on the other side of the equation back on our products table for product variance. So now what I've done, in effect, is link these two collections or these two tables in our database together so that we can query and keep track of them. So now let's do what do we want to add for our other variant data, you know, so we've got color, that's just a string, we've got, size, etcetera. Great. We want to control a price here. You know, maybe we got that g ten, you know, unique identifier. What is that? The global trade identification number? Something like that. Let's go in and add a decimal. Actually, that'd be that'd be a float. And then we'll do maybe just 2 decimal places. Let's call this price. Great. Alright. So now we've got some items on our variants. Let's go back and actually look at how this works. So I go back into our product and we can see we've got our variants here and if I wanted to create a new variant, I can. Let's call this red, we'll call it small. The GTIN is 666 and the price for this is $25. We could go in and create another variant. This is red, this is medium, you know, 777, and the price is $16. Whatever. I can go in and actually set up our like what fields are displayed here. This doesn't look very great just to see the UUIDs. So let's sort that out, right. And we'll make this just a little bit bigger as well. Alright, so we go back into our data model, we go to our product variants, we look for our interface and we have this, control over do we want a list, do we want a table. Let's use the table in this case and we will add our color, size, maybe we'll do GTIN and price combinations. Now you also may have, like a variant that has a different image as well. So we could potentially add that as well. We'll just keep it simple for now and for the related fields, let's show the color. We'll add just like a little label for it so we know what it is, we'll add the size and we'll add size here. So let's see how that looks inside our PIM and great. Now we can see here's the different variants, there's the pricing, which, you know, maybe we wanna format that a little more, but great. And we can click on those to edit that information all that we need. Alright, now let's build out our categories, right? This is the only thing remaining on our data model. We want to build out categories for our different products. So we have a category, we'll use the generated UUID again, and not sure we need like a status on these, but maybe we do wanna sort and cool. Alright, so we've got a category name. Probably a short description for category. You know, we keep kinda splitting hairs which you on what detail you you need for something like this. If we look at, let's let's look at something like Pimcore. What what do they have available on their website? This looks really kinda messy to me. So we've got, different objects. We've got media. You know, I don't really know for certain how others set up their data model for this, but to me this is how I would do it. Now I could go in and have something like another classification for this. We could use, like, tags as a different type of taxonomy, a different way to organize the data. But I also have, you know, probably subcategories and parent categories. So Directus makes doing a recursive relationship like that very, very easy, where I can go in and do a many to 1 or a one to many relationship. So I'll just do one to many. We'll call this the, these are gonna be subcategories. Let's just do let's call let's do it the reverse. So this will be the parent category. We'll use the categories collection. So we're creating a relationship, a recursive relationship, and we're gonna do the subcategories. So we've got parent category which will be a single parent, then we have subcategories. Great. So what effect does that give us? Right? If I go into categories so let's create a new category we'll call it apparel. This is, let's call it actually, well, like outerwear or something like that. Great. Hoodies, jackets, and such. And we don't have a parent category or a subcategory yet, so let's just create a subcategory like hoodies. Hoodies. Great. We'll save that and now we can see we've got subcategories and, you know, potentially nest these. If I go back to our product, now we want to create a relationship with that category. So in this case, typically, a product may only have a single category. I I've seen some of these systems that will have, like, smart collections, which I think is a a really neat way to model it. And I think even Shopify, so I've got a a Shopify store set up here. Let's let's just take a look at how they handle it. If this can be in a multiple categories or no? So it looks just like a single category. You can set up different collections for it. So we'll keep this to be a mini to 1. So we've got a category and our related collection will be categories. And for our display template, maybe we just want to show the name of that category. Now, one of the other things I can do is control the interface to how the related values are displayed, just to make sure this looks how we want. So now if I go into our product, we can select the category hoodies or outerwear specifically. Great. Cool. Now, let's say what functionality, let's go back here. I always get ahead of myself. So now we've got a way to store and manage all of our product data. We've got a way to store all of our product assets, right? So I could go in and upload. Let's say I've got a PDF here. I could go in and upload this PDF. If I've got a spreadsheet, I could certainly do that as well. You know, I've got a CSV or whatever data that I've got, I can upload that and store it within this product. But, you know, what fun is it having these products in isolation? We wanna be able to use these specific products. So out of the box, because we've set up this data model, we know the structure and everything, Directus gives us the ability to well, it just gives us REST and GraphQL APIs. So I could go in and do something like this where we've got items. Products. I'm getting an error message because I haven't set up permissions, but let's imagine we wanted to allow anybody to access this product information. I can go to our setup, We'll go to access control and we'll go to our public role, which just controls what information is publicly available. And because none of this is secret at this point, let's go in and try it now. So if I hit refresh, we go to our products, we can see our product data. So there's the shirt, there's the SKU number, there's the description, we've got a featured image, UUID, And when it comes to our variants, we can't I can't see that information. That sucks. How do we fix this? I can actually do something like this, which is one of my favorite features inside Directus that I can query the related collections or the related fields from a single API call. So it's very GraphQL like even though this is REST based, and I, you know, there's a GraphQL API. I'm not a super huge fan. You may be. No sweat. So I could do something. We'll give, like, we'll use a wildcard here for all the root level fields. And then let's go in and do something like this where we have product variance and then we'll get all of the brute level fields for that. So as soon as I do that, now you can see I start to see that data that we want. And maybe I don't need all of this. Maybe we just need the ID product_variance dot color. I could get a little more sophisticated and copy paste dot size dot price dot g10. Alright. And now that will only return the data that I'm interested in which is nice prevents over fetching keeps things really fast when we are sending a lot of data over the network. Alright. So we've got the ability to store and manage all of our product assets. Let's control our product variations. How do we sync this data with an ecommerce system? Right? We're managing all of this in a single location. Maybe I've got Shopify, which is what we're gonna use. Maybe I've got an Amazon storefront setup. Maybe we've got 3 or 4 other things going on. Right? So how do we sync this information this data over? So I'm gonna go to our store and this is just the the Shopify demo store that I've set up. None of these products or anything that I created. Right? It looks like this is set up for snowboarding, which I tried once or twice, wasn't very good at it. But I'm gonna go into the settings of this and we're gonna try to find how to, get access to the API here. Let's look and see domains. Maybe it's here inside the apps. Okay. So we'll look for develop apps. We're gonna create an app that can connect with this storefront. Let's just take a look at it and see what it actually looks like on the front end. We got a password protection right now. We're gonna call this the Directus Shopify sync, maybe? We'll create this app inside the Shopify store. Alright. So it looks like the next thing that we'll need to do, we're probably gonna be using the admin API because we're not building a shopping experience. We're trying to send product data to Shopify, so that we can have that experience inside Shopify. So let's configure our access scopes. It's probably gonna be around products. And I'm not sure exactly which one of these I need. Product listings, product feeds, viewer manage products, let's go with that. We see the webhook subscriptions. We'll come back to webhooks. Let's just save that. What else do we need? We need some actual credentials. So we need an access token, a way to get that data. Use your client secret to verify incoming webhooks. Let's go ahead and install this app. This will give this app access to our storefront data. And we're only gonna reveal this token once and boom, we don't need it any longer. Not sure exactly how to set up the webhooks. Is that part of this? Start using the admin API. Do you have to create webhooks through the through the API? I don't know. If your app creates a webhook subscription oh, okay. So it looks like you do have to create webhooks using the API. Yeah. Alright. We'll come back to that. No worries. Alright. So now I've got this product information inside our account, I want to send it over to Shopify. How can I do that? Well, I could probably write some kind of custom app or I could just go in and use the flows, the the automation builder inside Directus. We're looking pretty good on time. We're at 36 minutes. So I'm just gonna make this full screen so we can dive in a little deeper. Let's call this send products to Shopify. Now how are we gonna trigger this? There's multiple ways to set up triggers. So I could manually trigger this. I could set it up on a cron job, so a regular interval. For now, I'm just gonna manually trigger this and we'll go we'll trigger this on our products section and maybe we'll require confirmation. Are you sure? Yes, we're sure. So the location here allows me to control where I can actually trigger this. So I'm just gonna trigger this on the collection page only. No. I could do both, you know, I could potentially send one product or maybe I want to send all of the products. We'll just keep that set up. Alright. So now let's go in. We'll save this flow and if we go back to our products page on the right hand side, I could see the option to send this to Shopify. So check an item. Are you sure? Yes, we'll run a flow. And if I go back to our flow, I refresh and I don't see my logs. Where are the logs? I ran this flow. Where is it? Let's make it not require a selection. Not sure if something happened here. What's what's going on? So we'll go to our flow. We'll hit send products to Shopify. Go to flows. Okay. Now they're showing up and we can see kind of the data that is being triggered here. So just collections, products. Great. Cool. So now, how do we get this actual data, right? We will go in and read the data. So let's just call it read products. That's gonna be the name of this operation. We're gonna select all the products from the collection products and then I could, like, filter this down if I wanted to. You know, maybe I I just leave it all for now. We're gonna select all the IDs and save it. And then once we read that data, we want to send to Shopify. Great. We'll, actually use the webhook HTTP request. How are we going to send the data? It's probably gonna be a post method. We're gonna go let's go back to Shopify really quickly. So we've got our storefront API documentation. Zoom in a little bit. Do we want the storefront? I don't think we do. So many tabs. So we go to admin. Oh, developer tools, API libraries. Oh, boy. Getting into the thick of it now. Let's go back to where's our actual store? Right. Explore, start using the admin API. Okay. Very confusing documentation here. Alright. So we'll go to our REST API reference. We'll look for products is probably what we want. We want to create a product. So there's what the product resource looks like. Here's our create a product, and there's our URL. So what does the base URL look like? Authentication. Okay. So it looks like the URL structure here is the Shopify unique shop ID or the shop slug, I'm assuming. My shopify.com\u002Fadmin\u002Fapi\u002F apiversion\u002Fresource. So our base URL setup so if we go back, will look something like this. Let's just pick up this actual URL. 100 apps, 100 hours, nice slugified version. Myshopifiedot com\u002F what was it? Real genius here. Okay. Let's go half screen. Myshopify.com\u002Fadmin\u002Fapi\u002F what? 2023 dash 10 dash products dot JSON. Okay. So there's our URL. Great. Now I've got that access token. Authentication. Looks like we have to pass that as a header as well. So we'll go into direct us. We'll add some headers. Got content dash type. That's gonna be application slash JSON. And then we've got x Shopify access token. And then we've got this special token that you're not gonna look at, you're not gonna read, you're not gonna try to use. Alright. So now we can send a request. So we go down. We're looking for products again. We're gonna create a product inside Shopify. Alright. So what are the details we need for our products? We got a product dot title, body HTML, that's our description. We've got a product vendor, that's one thing we forgot inside our pin that we could add. We've got the product status. So, let's go in and just do something really simple. We'll do products. Okay. So there's our product. Then we're gonna have a title for the product, what's that gonna be? Let's take a step back here. I'm just gonna save this. I need to see what actual data is coming from this call here before we get carried away. Create a new product. Can you create is there an endpoint for creating multiple products? That's what I'm interested in. Create a new product. Retrieve a list of products. Looks like we're just going to be able to create a single product at a time. So we may have to break this apart and do 2 separate flows. Imagine that I had another product. We'll call it test product. No. Let's not do that. Let's call it a hoodie. So it is hoodie 123. Let's go to unsplash maybe. Do a hoodie. And this looks like a hoodie with a statement so we'll just copy paste that. Okay. We'll import the file from the URL. Boom. We got a featured image. Put a description. Great. Alright. So now we get 2 products. We're just gonna select these products. We're gonna send the well, what happens if we don't select? We could still run this flow. We'll We'll take a look. We'll send these products down to our flow. And let's just look at the log. Right? So now I'm getting an array of products. Okay. And maybe we wanna do we're gonna break this apart because we're gonna have to send a single product to Shopify. But before we do that, I want to format products. Right? So we'll take our product. We'll run the format function. So Directus allows you to run arbitrary JavaScript inside the flows, which is really nice for stuff like this. We've got the data, so our products are gonna be constant products equals data dot read products. I think that's gonna be it. And then we have some formatting logic here. Right. Just thinking out loud. So now I'm just gonna save this really quick. And one of the other things that I like to do when I'm working with these flows is just copy the data I'm getting back right here, and I'm gonna put it inside my code editor. I've got a starter project here just in case we needed it. So now I've got the data structure. And if I look at Shopify gosh. We've got, like, 35 different tabs going on. That's the fun of doing these live is actually trying to figure out how to all how to keep it all sane on one screen. Admin, API. Alright. So we go to the admin API, Rest admin API. So we go to our products. What things do we need to pass to our products at a most at a minimum just to actually create these products. So here we go. Here's a new draft product type of setup. Creates a new draft product. And can we do let's just make this even more complicated. Right? We'll do like a split screen? Somebody still needs to teach me to properly arc. Alright. So I've got Directus, got our product. Okay. We got like 30 things going on here. A lot of fun. Alright. So, let's run a format script. We've got our data over here that is coming from our Directus database. Let's go in and add our logic here. We got products. Let's do Shopify products dot map. Oh, no. Shopify products equals products dot map product. Yeah. Products. And then we'll return something. We got our function there. And then at the end of this, we're gonna return Shopify products. So we're just gonna return the array of products we want from Shopify. Alright. So we got the products and then we're gonna return a title for the product which is gonna be the product dot what name. We need a comma there. Then we've got the body_html, that's gonna be product dot description. We've got product dot vendor. We don't have that product dot type. We could fetch that, and then for now, let's just try this and see how far that actually gets us. Product dot name. We're gonna return our Shopify products. Let's go back in and I'll just make this full screen. Do products. I will look for our sidebar. Where are you sidebar? There you are, mister sidebar. We'll send the products to Shopify. Let's just test our flow and see what came out the other side. Right. So this looks good. This is our format that we're gonna send to Shopify. I'm gonna take just my my headers that I've got here. Let's just stuff those in this, take my URL that we so carefully constructed. And, I'm just gonna leave that. We're gonna create a new flow. So we're gonna go in and create Shopify product and this will be triggered from that other flow, so triggered by another flow. We'll leave the response body blank and here we're just gonna do the HTTP request that we just did. So I'm gonna paste in that. I've got my headers. Still stuck on the clipboard somewhere. I'll edit raw value and now we've got that. And here, I think it is just going to be something like this where we do actually, hit save. We'll just disconnect this for now. Hit save. Go back to our other flow and trigger that flow, and we'll just see what trigger Shopify, create. And Directus will create a key for you, you can also rename this key, but this key, all the data that gets generated in this operation gets appended to the flow underneath this specific key. Let's just take a quick peek at time. How are we doing? We've got 20 minutes left. Great. And then we're just gonna trigger this flow. So we'll select the Shopify flow. It's okay to create these in parallel and then for our payload, we're gonna do simply this. We've got the double curly brackets. We've got the data. No. What's the name of that operation? Format product. Is that what the key is? We're just gonna use this key. So we're gonna trigger the flow and the data from that, format product, we're gonna pass that to that other flow. Alright. So fingers crossed, stay with me here, we are building cool stuff. So now we're gonna send these products to Shopify, running that flow. And we can see here we triggered this. We got back null null, this is what we sent to that. And if we go to our other flow, where we're actually creating, we can see that this ran twice, which is the what we actually want. We wanna see the the payload that we got was the product. So we're just gonna pick up the trigger. Payload and that's what we're gonna send. Great. Amazing. So, in our request body here, we're gonna do something like this where we've got, quotations, double curly bracket, trigger dot payload and trigger is, prefixed with a dollar sign just because it's a unique, special key to get the trigger data. And fingers crossed. Right? Can we send this data to Shopify? Can we build a working PIM in an hour? Let's test it out. We're gonna run I I don't think it matters if I select these or not. Let's go in. We'll see. We have our send products to Shopify. That ran. Hopefully triggered this other flow that we have. We got a Shopify product. Bad request. By accessing the Shopify, you agree to parameters are missing or invalid. Okay. So we're missing some parameters. Let's take a look at the actual data that we sent. Payload, got the product resource, body HTML, supports HTML formatting, product status dot draft, Shopify access token. It looks like the body is not defined. So, I think I goofed up and that and maybe we're just sending the trigger. Let's take a look. Did I get it wrong? Do we just need to send the trigger? So, let's make this more fun. Right. Let's go to our Shopify store. Alright. We're gonna close this guy. Alright. I'm on the products. I'm gonna hit refresh. Dun dun duh. I don't I don't see any products in here. Alright. So, let's go back. Obviously, I goofed somewhere again. And we're definitely in danger of running out of time here. We got a bad request, missing parameters. Okay. So there's the body, product title, product dot HTML. What are we missing? 100 apps, 100 hours. We've got our access token. We've got our content type. Required parameter. Did I spell something wrong? This is always the danger of working against the clock like this. What is this issue going to be? Request body is product. Alright. So product title body HTML. I don't see where this is going wrong. Shopify access token. Bad request. Let's take a look at our app. Do we have the wrong scopes or something? Let's just try one quick tweak. Go back to our send products for our format product. Let's just leave out the title. Right. So we'll just comment this out. Return Shopify products. Send products to Shopify, flows, bad request, product underscore. Is it the way that it is actually sending this data? Sometimes if we do something like this, where we got trigger. Boom boom, Ron. Created. Boom. Alright. So dramatic reveal time. Dun dun dun. We have sent this data to Shopify. Did we? Where do we go? Okay. So we've got a hoodie, we've got a shirt. There they are. Boom. Now they are in our system. That is how it's done, ladies and gentlemen. Where where we at on time? We got 13 minutes left. We have managed to sync product data. Let's let's just take this one step further if we can, and let's create a product with let's at least get the images over there. Right? So on our product resource, we've got a list of product images, create a new product image, create a product image using a source URL that will be downloaded to Shopify. Okay. So it looks like you can you may be able to pass these, and I'll just go in and very briefly let's just delete these products. Delete these products. Can't be undone. Those are deleted. Those are no longer in the system. Let's adjust our payload here. So we're gonna go back to the formatting option. We'll comment that back in. We can have that now and then on the Shopify side, we've got okay. So we got a product dot images. This will be images and then we've got an array And then what? Array of objects, and then we just got a source attribute. So the source if we look at our code again, Right. We've got our featured image. Let's just pluck that for now And, we're gonna do something like this where we've got the source is gonna be, HTTPS direct us app slash assets slash what was the name of that? Featured underscore image. Featured. Oop. Gotta do our little squirrellys featured image. Close that off. Should be good. The last piece of the puzzle for this is going to be enabling permissions for the actual files inside Directus as well, Because Shopify needs to access that. So 2 ways I could do it. I can add an access token, when I'm sending that call to Shopify Or in this case, I'm just gonna go in and we'll keep this really simple. Under direct as files, we'll do all access. And we're just going to send and cross our fingers hoping that this flow actually works. We could do the dramatic reveal inside Shopify. Right? I broke something trying to send the images over. Where did you go? Where did you go? Alright. What broke? Right? Clearly, we broke something. Great Shopify product. What do we send? Product title hoodie. Do we actually break something inside the I don't see my logs. 4 to 5 minutes ago, that's not correct. Run Shopify flows. Go into our flow. Less than a minute ago, featured image is not defined. Okay. So we did not have one with a featured image, and that broke. What did I do wrong? Again, always fun on the clock. I guess you have to actually use product dot featured image. Right? Featured image. That would make a ton of sense. Too much sense actually. Alright. Last time. Send products to Shopify. Did it work? Let's test our flow. Create Shopify product. Created. Good looking. Alright. So if we open up Shopify, we go to our products, we can see our hoodie. Here is our actual data, so there's the image that we've got, there's the shirt that we loaded up, and this is all coming from our pin, our back end, our single source of truth. So that is how it's done, ladies and gentlemen. We are syncing with Shopify. Last on our list, can we get this in 8 minutes? I have no idea. We've got a simple product catalog. Now, I don't think we could do this in 8 minutes, but let's give it a shot, right? We want to display all of our products on the front end of our website, so people can browse those. So because there are no rules, I am a huge fan of Tailwind UI and Tailwind CSS in general. They've got some great looking ecommerce components that we may just repurpose for this, like, here's a nice product list. And I've got a Nuxt starter application sitting here just ready to be used. So let's see if we can actually get this product information displayed and rendering on a product catalog in just a couple minutes. Alright, so we're gonna just forget Shopify for now. Let's simplify. Alright. This looks pretty good. This is a with border grid. We've got a view component here. We've got our products. Let's just copy this template. Alright, so we're gonna go to our index page. We probably got some script action that we'll do there. For now, I'm gonna if we just copy their products. Right. Constant products. Not gonna look great, and they've got an icon. So we'll do the star. Let's just omit the products ratings for now. Right? Just comment those up. Run this. If I look at local host, say now we've got got something cooking here. K. And now, let's actually fetch these products from Directus. We wanna do this live. Right? Going to do what, we got our products. Is it equal to await use directus? I've got a just a composable here to fetch this data using the rectus SDK. We're going to read items and that's gonna be products. And is this actually gonna be all we really need? Usually, when I am developing, I often tend to do stuff like this where I will just render out the data inside the browser just to make sure we're seeing we're getting the stuff that we need. Item\u002Fproducts failed. Oh, wait. Use direct us, read items, products. Why is this not working correctly? Well, that would be because I didn't actually set up the directus URL properly. We are at 5 minutes, so I'm gonna load my PIM URL here. That is my Directus base URL. The server token, probably not even needed in this setup, but, we'll just reload. Let's fire back up the dev server. Let's see if we can get this data rendering from Directus. Boom. So there's our actual data. Right? We no longer need this. Let's uncomment. And just looking at this data, we've got the v4productandproducts. We wanna render the product dot id, we'll have that information. The image source, because we can use Directus as an image provider it will transform assets for you. I'm just going to use the NUTS image tag which is already set up in this. We will use product dot featured image. We'll just leave alt text blank for now because there is there's none. We haven't got that set up. We got product dothref. We don't have one of those currently, but we could do slash products slash what? Product dot ID. That would be the probably the URL. We got a product dot name, we got a product dot price. What else? I wanna fix the template structure here and then maybe we want to render out, div. We'll do like a tailwindpros class and give this v html. We'll render the product description. Input must be a string received undefined. So if there let's make sure oh, forgot the item there. Boom. So there we have it. We have got our items rendered out. You know, we could continue fleshing this out with the help of Tailwind into a catalog. But now we have done several things, Right? Let's recap with our final two and a half minutes. We have created a PIM system that can store and manage all of our product data, store all of our product assets, control all of our product variations, we can sync that data with Shopify, and we can have a simple catalog. But, Bryant, you forgot one thing, our translations for this data. What if I want to have it in French or German or, Canadian? Bad bad joke for all all of our Canadian followers. Apologies there. So with a minute 30, how can we set up, translations for this? Right? How can I control other languages? So I'm gonna go into our products. The first thing I'm gonna do is just create a languages collection. We've got a language code. Alright. We've got a title or name of the language. Alright. And let's create 2 languages. We have EN, US, English. Are we gonna do it 55 seconds? Let's just say fr for fridge. I think that's the code. No idea. We'll go back to our product. Let's go in and there's a special translations interface within Directus. Clock is ticking, Brian. So we got the code, we got the direction field, that would probably be right to left. Use current language, we'll save this And then inside our product translations, we would go in and do, name, We would do a description. And where we at on time? 10 seconds. Brian, can you do it? 10, 9, 8, 7. How do we add translations for this? We can do English name. So close. We hit the 60 seconds. I'm just going to finish this or the 60 minutes. We can do the French translation here and save it. So, with Directus we can manage all of these different things. We hit time there, that felt really good though. We can manage translations or multilingual content. Alright. So that was a fun one. Multilingual content. Really enjoyed this one. Building a PIM in 60 minutes or less. There's obviously more to this, but my next steps would be to flesh out that sync engine a little bit, so that if we made updates inside Directus, I could send those over to Shopify. I could even use the Shopify webhooks in this case, if I made a change to that information in Shopify to sync it back into Directus. I would also probably go in and flesh out the translations interface just a little bit more. So that I have all the necessary fields. You know, and I may even have specific fields that we want to add to Shopify versus something like Amazon. So we could go in and do, you know, some more different some different channels that we wanna send this data to. But for 60 minutes, amazing to see what you can actually build with Directus. Stay tuned for the next episode. I'm sure we'll have a good one and you'll get to see me hack and slash and burn my way through another product.",[5316],"f68324c1-69dc-4a09-82c9-136bbc1e12e7",[],{"year":2380,"number":177,"id":4162,"episodes":5319,"show":5320},[4164,4165,4166,4167,4168,4169,4170,4171,4172,4173,4137],{"title":322,"slug":323},{"id":4608,"slug":5322,"vimeo_id":5323,"description":5324,"tile":5325,"length":740,"resources":5326,"people":5330,"episode_number":177,"published":5332,"title":5333,"video_transcript_html":5334,"video_transcript_text":5335,"content":12,"seo":12,"status":19,"episode_people":5336,"recommendations":5338,"season":5339},"deepgram","895925213","Automatically transcribe new audio files with Deepgram's Speech-to-Text API.","f894ea9e-da61-4529-b51a-b399acd1bdc8",[5327],{"name":5328,"url":5329},"Deepgram Docs","https:\u002F\u002Fdevelopers.deepgram.com\u002Fdocs",[5331],{"name":2820,"url":2821},"2024-01-05","Transcribe Audio Files with Deepgram","\u003Cp>Speaker 0: Deepgram offers a speech to text API that allows us to send off audio files and receive transcriptions in return. Today in QuickConnect, we're going to connect to Deepgram. So whenever we upload a file to our director's project, if it's an audio file, it will automatically go off, generate a transcription, and then save that transcription to the file description. So let's get started. The first thing we'll need is a Deepgram API key.\u003C\u002Fp>\u003Cp>So head to the Deepgram console and create a new API key. You can give it any name you want and set the permissions. We only require the lowest level member permissions. We'll set the API key to never expire and hit create key. I'm gonna take note of this API key for later because I only get to see it right now one time.\u003C\u002Fp>\u003Cp>If I lose this key, I'll need to come and generate a new one here in the Deepgram console. So hit got it and head over to your directors project. Let's create a new flow in our directors project. I'll call this one transcribe new audio files. We're going to set this up to trigger whenever an event happens in our directors project.\u003C\u002Fp>\u003Cp>We're going to pick non blocking, which means that all of directors' built in functionality will not be stopped or halted or paused, but this logic will run-in parallel. Now in terms of scope, we're going to pick files dot upload. So this will trigger every time a file is uploaded, and we will hit save. So that is our trigger. Now the first thing we're going to do is actually make sure that we only continue this logic if the file is an audio file.\u003C\u002Fp>\u003Cp>Because right now, this flow will start on every single file upload regardless of file type and location. So let's create a new operation. I'll just call it check and add some conditional logic. This condition ensures that the file type contains the word audio. The file type could be something like audio \u002Fmp3audio.wav and so on.\u003C\u002Fp>\u003Cp>But they all start with audio. So we're gonna check that the file type contains the word audio. Now there are 2 paths here, the resolve path and the reject path. Now if this condition is not true, I e, the file is not an audio file, we will go down the reject path, and we're just gonna add nothing there, which means the whole flow just just stops. It completes at this point.\u003C\u002Fp>\u003Cp>Let's create a new operation on the resolve track. And in here, we're going to call it Deepgram. We're gonna go ahead and make this a webhook slash request URL, operation, and this provides some additional options. Now in the Deepgram documentation, they give you a URL to use when you want to go ahead and create transcriptions. It needs to be a post request to api.deepgram.com\u002Fvone\u002Flisten.\u003C\u002Fp>\u003Cp>And then you can add any number of query parameters to change what Deepgram is going to do and return with that audio file. So here we're saying, basically, make it human readable, add some add some formatting, and diarization will split out who's speaking. So it will tell us whether it's speaker 1 or speaker 2 and so on. Now Deepgram also requires a header in order to authenticate. So we're gonna go ahead and add a new header.\u003C\u002Fp>\u003Cp>We're going to call this one authorization, capital a, and the value will be the word token, space, and then your Deepgram API key, and hit save. Now the final thing we need to do is actually give it the file URL. So what we're actually gonna do is pause here for just a moment, and we're going to try and trigger this flow as it stands. This will fail because we're not providing the file, but we're gonna see what this does. So I'm gonna go ahead and add a new file to our file library.\u003C\u002Fp>\u003Cp>Here we go. So we'll upload that file. Fantastic we will head back to our flow and we'll see here that there's a little one icon here one run and we'll get to see. So we get a trigger. So this is the actual file being uploaded.\u003C\u002Fp>\u003Cp>Now there's an interesting thing here. We see inside of this trigger value, there is a key. This is the unique identifier for this file in the director system. Then we see here that this was the check, and then we did a Deepgram API call with our header. And the returned payload, as we expected, is incorrect because we didn't actually send the file.\u003C\u002Fp>\u003Cp>The reason I wanted to do this is because I wanted to show you that we need this ID trigger dot key. So now let's go in and edit this, this request over here. I'm I'm gonna just paste it in, and we're gonna explain it here. So every asset in Directus is accessible with the full URL of the Directus project \u002Fassets\u002Fthekey, the unique ID for that file. So this is now the URL for that file.\u003C\u002Fp>\u003Cp>If I go to this complete URL, of course, putting the real key in here, I would actually get that file. However, there is one extra thing to note here, which is that right now as it stands, this directors project does not make all of the files public. Right now, if we continue with this request and we run that again, we reupload a file, there'll be an authentication error. Directus won't be able to reach that file. Then we have a few options.\u003C\u002Fp>\u003Cp>The first is to go into the public role, go into directors files, and make that file or the directory which the file lives in public. That's one viable option. But there is another way to do this, which perhaps is a little more recommended. Let me go into my user account here and generate an API token. So I will save that and hit save.\u003C\u002Fp>\u003Cp>Going back to my flow here, I'm gonna once again edit this URL, and I'm going to append to the end of it access_token, and then my API key. This will authenticate the API request. So this now is a file that I can access. Let's trigger this flow again. Let's go over to our file library.\u003C\u002Fp>\u003Cp>I'm actually just gonna go ahead and re upload the file again. So it's a fresh file. So we'll have a duplicate here, but that will trigger the flow to rerun. Let's look at this second run of the flow. The returned payload comes back and there is our transcript.\u003C\u002Fp>\u003Cp>That's fantastic. So we get that back. So our Deepgram request was successful. There's actually quite a large payload that comes back here. Data, results, channels, which is an array of objects, alternatives, which is an array, and there is our transcript.\u003C\u002Fp>\u003Cp>Because we turned on smart formatting, there is also a ton of extra metadata that comes back. Like, it's huge. Every single word, every single word there. And we also get our paragraphs coming back with a transcript and so on and so forth. So you can go and explore this returned object.\u003C\u002Fp>\u003Cp>I, surprise, surprise, have already done that work for us. The final step is going to be to actually save this value back to the file description. So let's create a new operation here, and let's make this an update data, operation. In the collection field, we are going to update a director's files item. And we're going to put in here the ID of that initial item trigger dot key.\u003C\u002Fp>\u003Cp>So now we're gonna be updating the item that actually triggered this whole flow in the first place and in turn was transcribed. Now the final thing we're gonna do here is we are going to update the description. So let's go ahead and do that here together. The description, which is a built in field, is going to be equal to Deepgram and the reason we can say Deepgram is because that is the key of this step it's called Deepgram Deepgram dot data dot results dot channels. That was an array Dot alternatives.\u003C\u002Fp>\u003Cp>That was an array.paragraphs dot transcript. Oh, rolls right off the tongue that. And, of course, that might be a little bit of development trial and error, but I know that that is the location of the formatted transcript. Let's hit save. Let's hit save again.\u003C\u002Fp>\u003Cp>And now let's go and upload once again a new file. So that's uploading. Now in the background, that flow has already been triggered. Deepgram has or the condition has already been checked. Deepgram has already been called.\u003C\u002Fp>\u003Cp>And in theory, if we go in here now, we should see in the description did I pick the wrong file? Yes. I did. It was this one. The description with speaker diarization and formatting.\u003C\u002Fp>\u003Cp>So, automatically, now that this flow is set up and enabled, I can go hands off and know that every audio file that gets uploaded will be transcribed. Now at the time of recording, Deepgram do also accept video files as well, but you can expect that they'll take a little bit longer to return. And, typically, you should be trying to send us as little, you know, data as possible. So audio files was our condition, but you can widen that condition as well. So let's just sum up the way that that flow looks.\u003C\u002Fp>\u003Cp>Transcribe new audio files. This is triggered whenever a new file is uploaded. And just to be clear, that's every file. So what we do is we check that it's an audio file, then we go off to the Deepgram API authenticating with our API key. And then once we get the transcript back, we update this file with the transcript.\u003C\u002Fp>\u003Cp>Now there are some extra things just to consider here. Right now, this will happen on every audio file. You can add further conditions to perhaps specify down to a folder or only allow specific users to do this, or you can make it a manual step. You can set up a flow with a button on an item page and go ahead then and only generate a transcript on demand. I hope you found this interesting, and I'll see you in the next episode.\u003C\u002Fp>","Deepgram offers a speech to text API that allows us to send off audio files and receive transcriptions in return. Today in QuickConnect, we're going to connect to Deepgram. So whenever we upload a file to our director's project, if it's an audio file, it will automatically go off, generate a transcription, and then save that transcription to the file description. So let's get started. The first thing we'll need is a Deepgram API key. So head to the Deepgram console and create a new API key. You can give it any name you want and set the permissions. We only require the lowest level member permissions. We'll set the API key to never expire and hit create key. I'm gonna take note of this API key for later because I only get to see it right now one time. If I lose this key, I'll need to come and generate a new one here in the Deepgram console. So hit got it and head over to your directors project. Let's create a new flow in our directors project. I'll call this one transcribe new audio files. We're going to set this up to trigger whenever an event happens in our directors project. We're going to pick non blocking, which means that all of directors' built in functionality will not be stopped or halted or paused, but this logic will run-in parallel. Now in terms of scope, we're going to pick files dot upload. So this will trigger every time a file is uploaded, and we will hit save. So that is our trigger. Now the first thing we're going to do is actually make sure that we only continue this logic if the file is an audio file. Because right now, this flow will start on every single file upload regardless of file type and location. So let's create a new operation. I'll just call it check and add some conditional logic. This condition ensures that the file type contains the word audio. The file type could be something like audio \u002Fmp3audio.wav and so on. But they all start with audio. So we're gonna check that the file type contains the word audio. Now there are 2 paths here, the resolve path and the reject path. Now if this condition is not true, I e, the file is not an audio file, we will go down the reject path, and we're just gonna add nothing there, which means the whole flow just just stops. It completes at this point. Let's create a new operation on the resolve track. And in here, we're going to call it Deepgram. We're gonna go ahead and make this a webhook slash request URL, operation, and this provides some additional options. Now in the Deepgram documentation, they give you a URL to use when you want to go ahead and create transcriptions. It needs to be a post request to api.deepgram.com\u002Fvone\u002Flisten. And then you can add any number of query parameters to change what Deepgram is going to do and return with that audio file. So here we're saying, basically, make it human readable, add some add some formatting, and diarization will split out who's speaking. So it will tell us whether it's speaker 1 or speaker 2 and so on. Now Deepgram also requires a header in order to authenticate. So we're gonna go ahead and add a new header. We're going to call this one authorization, capital a, and the value will be the word token, space, and then your Deepgram API key, and hit save. Now the final thing we need to do is actually give it the file URL. So what we're actually gonna do is pause here for just a moment, and we're going to try and trigger this flow as it stands. This will fail because we're not providing the file, but we're gonna see what this does. So I'm gonna go ahead and add a new file to our file library. Here we go. So we'll upload that file. Fantastic we will head back to our flow and we'll see here that there's a little one icon here one run and we'll get to see. So we get a trigger. So this is the actual file being uploaded. Now there's an interesting thing here. We see inside of this trigger value, there is a key. This is the unique identifier for this file in the director system. Then we see here that this was the check, and then we did a Deepgram API call with our header. And the returned payload, as we expected, is incorrect because we didn't actually send the file. The reason I wanted to do this is because I wanted to show you that we need this ID trigger dot key. So now let's go in and edit this, this request over here. I'm I'm gonna just paste it in, and we're gonna explain it here. So every asset in Directus is accessible with the full URL of the Directus project \u002Fassets\u002Fthekey, the unique ID for that file. So this is now the URL for that file. If I go to this complete URL, of course, putting the real key in here, I would actually get that file. However, there is one extra thing to note here, which is that right now as it stands, this directors project does not make all of the files public. Right now, if we continue with this request and we run that again, we reupload a file, there'll be an authentication error. Directus won't be able to reach that file. Then we have a few options. The first is to go into the public role, go into directors files, and make that file or the directory which the file lives in public. That's one viable option. But there is another way to do this, which perhaps is a little more recommended. Let me go into my user account here and generate an API token. So I will save that and hit save. Going back to my flow here, I'm gonna once again edit this URL, and I'm going to append to the end of it access_token, and then my API key. This will authenticate the API request. So this now is a file that I can access. Let's trigger this flow again. Let's go over to our file library. I'm actually just gonna go ahead and re upload the file again. So it's a fresh file. So we'll have a duplicate here, but that will trigger the flow to rerun. Let's look at this second run of the flow. The returned payload comes back and there is our transcript. That's fantastic. So we get that back. So our Deepgram request was successful. There's actually quite a large payload that comes back here. Data, results, channels, which is an array of objects, alternatives, which is an array, and there is our transcript. Because we turned on smart formatting, there is also a ton of extra metadata that comes back. Like, it's huge. Every single word, every single word there. And we also get our paragraphs coming back with a transcript and so on and so forth. So you can go and explore this returned object. I, surprise, surprise, have already done that work for us. The final step is going to be to actually save this value back to the file description. So let's create a new operation here, and let's make this an update data, operation. In the collection field, we are going to update a director's files item. And we're going to put in here the ID of that initial item trigger dot key. So now we're gonna be updating the item that actually triggered this whole flow in the first place and in turn was transcribed. Now the final thing we're gonna do here is we are going to update the description. So let's go ahead and do that here together. The description, which is a built in field, is going to be equal to Deepgram and the reason we can say Deepgram is because that is the key of this step it's called Deepgram Deepgram dot data dot results dot channels. That was an array Dot alternatives. That was an array.paragraphs dot transcript. Oh, rolls right off the tongue that. And, of course, that might be a little bit of development trial and error, but I know that that is the location of the formatted transcript. Let's hit save. Let's hit save again. And now let's go and upload once again a new file. So that's uploading. Now in the background, that flow has already been triggered. Deepgram has or the condition has already been checked. Deepgram has already been called. And in theory, if we go in here now, we should see in the description did I pick the wrong file? Yes. I did. It was this one. The description with speaker diarization and formatting. So, automatically, now that this flow is set up and enabled, I can go hands off and know that every audio file that gets uploaded will be transcribed. Now at the time of recording, Deepgram do also accept video files as well, but you can expect that they'll take a little bit longer to return. And, typically, you should be trying to send us as little, you know, data as possible. So audio files was our condition, but you can widen that condition as well. So let's just sum up the way that that flow looks. Transcribe new audio files. This is triggered whenever a new file is uploaded. And just to be clear, that's every file. So what we do is we check that it's an audio file, then we go off to the Deepgram API authenticating with our API key. And then once we get the transcript back, we update this file with the transcript. Now there are some extra things just to consider here. Right now, this will happen on every audio file. You can add further conditions to perhaps specify down to a folder or only allow specific users to do this, or you can make it a manual step. You can set up a flow with a button on an item page and go ahead then and only generate a transcript on demand. I hope you found this interesting, and I'll see you in the next episode.",[5337],"9e674d17-1aac-48b1-9a69-0807e41d21c0",[],{"year":2380,"number":194,"id":4605,"episodes":5340,"show":5341},[4607,4608,4609,4610,4611,4612,4613,4590],{"title":1746,"slug":1747},{"id":4795,"slug":5343,"vimeo_id":5344,"description":5345,"tile":5346,"length":13,"resources":12,"people":12,"episode_number":159,"published":5347,"title":5348,"video_transcript_html":5349,"video_transcript_text":5350,"content":12,"seo":12,"status":19,"episode_people":5351,"recommendations":5352,"season":5353},"cost-control","891433625","Welcome to the magical land of open source! A place where time and money have a rocky relationship.","ba49ffe6-8f2a-4529-80e8-0b5d0671e13c","2024-01-03","Cost Control","\u003Cp>Speaker 0: And now Dev Thoughts by Jack Rabbit. In this economy, it's important to keep tight control over the cost of projects. Just last week, I saved $200 by having 2 of our engineers set up the self hosted version of a tool instead of paying for their cloud service. They're still working on it, but it should be up and running soon.\u003C\u002Fp>","And now Dev Thoughts by Jack Rabbit. In this economy, it's important to keep tight control over the cost of projects. Just last week, I saved $200 by having 2 of our engineers set up the self hosted version of a tool instead of paying for their cloud service. They're still working on it, but it should be up and running soon.",[],[],{"year":2380,"number":159,"id":4790,"episodes":5354,"show":5355},[4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803,4804,4805,4779],{"title":3380,"slug":3381},{"id":4796,"slug":5357,"vimeo_id":5358,"description":5359,"tile":5360,"length":13,"resources":12,"people":12,"episode_number":213,"published":5347,"title":5361,"video_transcript_html":5362,"video_transcript_text":5363,"content":12,"seo":12,"status":19,"episode_people":5364,"recommendations":5365,"season":5366},"mystery-code","891433490","Who needs a plot twist when you have legacy code?","e294720c-25b5-4906-bfa4-7238ec3660be","Mystery Code","\u003Cp>Speaker 0: And now Dev Thoughts by Jack Rabbit. If code documentation was a novel, mine would be a mystery thriller where the main character is on a quest to understand what the code is\u003C\u002Fp>","And now Dev Thoughts by Jack Rabbit. If code documentation was a novel, mine would be a mystery thriller where the main character is on a quest to understand what the code is",[],[],{"year":2380,"number":159,"id":4790,"episodes":5367,"show":5368},[4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803,4804,4805,4779],{"title":3380,"slug":3381},{"id":4683,"slug":5370,"vimeo_id":5371,"description":5372,"tile":5373,"length":3431,"resources":5374,"people":5378,"episode_number":177,"published":5383,"title":5384,"video_transcript_html":5385,"video_transcript_text":5386,"content":12,"seo":12,"status":19,"episode_people":5387,"recommendations":5390,"season":5391},"edge-computing","893709475","Kevin is joined by Pandelis to properly understand edge computing, and how they evolve from CDNs and distributed compute.","725ad1de-038d-4832-8344-e5d782c10974",[5375],{"name":5376,"url":5377},"Pandelis' Twitter Thread","https:\u002F\u002Ftwitter.com\u002FPandelisZ\u002Fstatus\u002F1726957806820164032",[5379,5382],{"name":5380,"url":5381},"Pandelis Zembashis","https:\u002F\u002Ftwitter.com\u002FPandelisZ",{"name":2820,"url":2821},"2024-01-02","Edge Computing with Pandelis","\u003Cp>Speaker 0: Hello, and welcome to learning things I love to hate. This is a show where I basically stop putting off learning things that I have avoided for one reason or another, inviting in my friends and experts around the topics that I'm trying to learn. And today's topic is edge computing, a topic which has got a little spicy in web circles in the last few weeks. I'm inviting my lovely dear friend, Pandellus, here today. Pandellus, would you like to introduce yourself?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Hi, Kevin. Nice to see you again. Yeah. I'm I'm Pandellus.\u003C\u002Fp>\u003Cp>I've been an engineer for the last few years working in web, working in in deploying a lot of the things that I claim to write. I've worked in web agencies where delivering content to users was super, super important. Back in the jQuery CDN days where when edge computing was basically only CDNs into kind of our modern edge computing world now where we can put entire runtimes, entire databases onto the edge. There's all things I've experimented with at various companies. So I I'm so happy when I saw your tweet.\u003C\u002Fp>\u003Cp>It's something I've been experimenting with a lot. And, as someone as a user of the web, I think it's something super important for for everyone to to to to to know about and experiment with.\u003C\u002Fp>\u003Cp>Speaker 0: Awesome. And, and yeah. So I tweeted out a few days before this recording. Can anyone just, like, explain edge computing to me? Because I really don't understand it.\u003C\u002Fp>\u003Cp>And you came in with a wonderful set of illustrations, which actually will just edit in here right now. These are the illustrations, to kind of explain a little bit about how Edge works. So I thought, hey, I'm just gonna invite you onto this show. This was already a topic I was planning on talking about. You preluded, I suppose, some of the things I wanted to discuss today.\u003C\u002Fp>\u003Cp>So just to just to open with this, I wanted to explain kind of where my knowledge is now, and hope that you might be able to take me and anyone watching who's in a similar spot through that journey of understanding more. And perhaps if we have some time at the end, we can do a little demo of this abstract concept. So what I know of edge computing, I think, really is around content delivery network, CDNs. We can maybe start a little bit about talking about this. And I know that the concept of what can be stored, you know, and replicated in multiple regions and serve to people based on where they are in the world has just grown more complex.\u003C\u002Fp>\u003Cp>We we're doing that with more things. And I suppose my biggest concern and the reason I haven't really done it yet is, 1, the idea that, you somehow need to replicate things really rapidly in lots of regions. And 2, unrelated, it just seems really complex, and I'm not sure how how much the complexity is worth, what might be a payoff that doesn't matter. I know there are some performance hardcore people who are like, of course, it matters. But, yeah, that's kind of my starting point.\u003C\u002Fp>\u003Cp>So maybe if you take us back, talk a little bit about CDNs and what they are and kinda how that's evolved to where we are now, that would be really good.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Of course. Yeah. No. I mean, as engineers, we like to make our lives harder for sure.\u003C\u002Fp>\u003Cp>It's it's it's we we we do sometimes like to like to overengineer, and it and it's all as with everything in engineering, there's a million ways to do them. Not all of them are right, and a lot of them are right. So your mileage may vary with your needs of edge whatever. A lot of people don't even need the CDN, but it comes down to your users. Right?\u003C\u002Fp>\u003Cp>It always comes down to your users and the experience that you're trying to deliver. So let's let's start with CDNs, and what is the problem that that that CDNs solve? So if we go back to the mid 2000, when jQuery and, you know, serving libraries over the wire were were were all their age, we would, you know, blindly go to, CDN stack or whatever those CDN websites were and grab URLs for for jQuery and put them in our sites. Before build systems, before your web packs, before the the the modern JavaScript ecosystem back in days, CDNs were all their age because we wanted to deliver large JavaScript bundles to the user quickly. Internet was slower, and still is slow.\u003C\u002Fp>\u003Cp>Or this is h t p one days, right, before streaming h before multi, you know, multiple h t p two streams, and all that type of stuff. So it was very important or we people saw a lot of gain in having these large JavaScript bundles close to the user, meaning the end users could consume that JavaScript faster. And in the end, what that means is when they get their HTML through, it becomes interactive faster.\u003C\u002Fp>\u003Cp>Speaker 0: Could I ask you a question? Could I pause it? So when you're saying near to where people are, just to clarify, we mean that this one jQuery bundle or whatever is stored on multiple servers throughout the world. I request it via one URL, and then the application which will serve up that file will find the place nearest to where I'm making the request and serve it. And then the additional benefit of CDNs, I suppose, is if I visit 10 sites that all are trying to load the same large bundle, this might be more CDN specific, and I think it also might be a relic of the past, but it\u003C\u002Fp>\u003Cp>Speaker 1: it would not It is a relic of the past. That's that's that's one of the topics I was gonna touch on. In in the early days, it was when there was less CDNs around, we we could make the ex we could make the excuse that, okay, by using a shared CDN or, say, a common example was also Google Fonts. Mhmm. By using Google Fonts, by using a common CDN, it means that there's a higher likelihood that a user would already have those assets on their computer.\u003C\u002Fp>\u003Cp>Cached.\u003C\u002Fp>\u003Cp>Speaker 0: Okay.\u003C\u002Fp>\u003Cp>Speaker 1: Exactly. So it's a further cache. Ultimately, the the most edge location is the user's computer. So caching is always a tiering system and CDNs are a type of cache, and the ultimate caching is local. When you have a local cache, that's that's the ultimate edge.\u003C\u002Fp>\u003Cp>And really with edge compute, with edge with with the topic of delivering on the edge, what we're trying to achieve is native like performance, and the most edge you can get is being on device. So, arguably, the the most edge computing delivery method today is an app. Apps are on your device, and there's no more edge than the the the device that you're touching. And everything that we're doing on the web or or in edge compute land is to try and achieve native like performance.\u003C\u002Fp>\u003Cp>Speaker 0: Cool. That makes sense.\u003C\u002Fp>\u003Cp>Speaker 1: So, yeah, those are those are sort of CDNs, and that is kind of the the most basic form of putting things on on the edge that we had. So that and that was, and there was a legitimate benefit to to to to CDNs. And what we've tried to do in or what has happened in in the modern era is CDNs themselves have become more and more capable, to the point where they can actually run and execute code. 2 of the most popular and, CDNs around are are CloudFront from AWS and, Cloudflare. And Cloudflare are one of the pioneers of of edge computing as well, and their CDN offering just over time naturally has become more has gained features, has had feature creep, and eventually, we wanted to do more complicated things over time.\u003C\u002Fp>\u003Cp>Right? We we may wanna deliver a slightly different version of a bundle for certain users. Or if it gets hit with a with a certain URL parameter, we might wanna bust the cache or or run some sort of, run something on that server that's delivering it, maybe do an AB type test, where certain users got certain bundles or whatnot. So at\u003C\u002Fp>\u003Cp>Speaker 0: some point transformations as well?\u003C\u002Fp>\u003Cp>Speaker 1: File transformations became yep. Image image CDNs. So moving from kind of just JS bundles, image CDNs are, again, super popular because, on super useful because, the the 2 largest assets that get delivered are the bundle and the images. And images also very bandwidth intensive and benefit largely from being closer to the user. Sure.\u003C\u002Fp>\u003Cp>And we're gonna we're gonna say closer to the user a lot throughout this conversation. And what that what that practically means is that you are spending less time in flight over cables. Your server could be very far away. And the further away it is, the longer it will take to navigate, you know, the the, the the sea of cables that live under the sea, to get to your user. So the closer you can be, the less hops we can make, the the the the the better for for the end user, but also for our bank, CDNs, specifically, not necessarily edge compute, but CDNs, specifically, also benefit benefit us in that we send less data out because it's also a caching mechanism.\u003C\u002Fp>\u003Cp>Edge compute, not so much when we when we get when we get down to it, because one of those goals is to have as fresh content as possible. So when you're when you're when you're caching, you're limited by the freshness that you can deliver. So that that's that's sort of that that's sort of the niche that CDNs sold for a while. And, yeah, more and more doing more and more things on the edge or closer to the user or doing more compute at the CDN level means that we could do these kind of transformation things that you just mentioned with images. Right?\u003C\u002Fp>\u003Cp>So say I wanted an image 30% smaller, we could deliver 1 all the way back from our original server, or we could use the kind of already existing cached asset and do some computation on it and then also cache that. And that's this kind of level of level of tiering that that that being closer to the user kind of offers us is is we don't wanna keep hitting our main server, and wanna keep delivering faster things to the user because, yeah, it's all about speed at the end with with with the goal of edge.\u003C\u002Fp>\u003Cp>Speaker 0: Cool. So now we're in a space where it's not just about storing assets, whether that's bundles, images, you know, media, whatever. It's actually about running computational tasks at the edge. So I suppose, naturally, that moves us on to maybe what what is edge compute edge computing? What is it capable of now?\u003C\u002Fp>\u003Cp>And then I suppose we're going back to my initial concerns or skepticisms. 1, how do you keep all of these nodes? I don't know if they're called nodes. I'm assuming they're called nodes in in sync, especially when you gave an example of, you know, doing transformations at the edge and then caching that. Well, is the cache just at a node level?\u003C\u002Fp>\u003Cp>Do they propagate throughout a network? Stuff like this, I'm not too sure about. And then it sounds really complex. How how does this actually shake out to being something that's tolerable to developers?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Edge and the and this this edge cache or caching cash busting, in CDN land, it has always been a pain, especially in the days where I was working as at a web agency. So, basically, all all all that we deliver over the wire is is assets and and JavaScript and images, and it was definitely always always a battle of, okay, well, what is actually being delivered to the user and cache busting it and and and figuring out, oh, okay. Oh, they're on an older version of the of the site, and they're seeing old content and it and us getting rang up by our clients being like, the other site that we just published it. It's not gone live, and that's having to explain.\u003C\u002Fp>\u003Cp>Okay. You're gonna wait for the cache to propagate. It's it's gonna it's gonna get to the your user eventually. No. But we've we've launched this because, some context here.\u003C\u002Fp>\u003Cp>I used to work in, theater websites, where we had very peak peaky demand spiky demand and very, and certain requirements around, you know, if a certain show went live, they want it live now so that people can book it now.\u003C\u002Fp>\u003Cp>Speaker 0: Which is not an unreasonable expectation if I hit publish that I mean, that's even the thing with, like, static website building. There's always a build period, and some people don't get that that build period, you know, is can be quite material.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. And that is also that's a huge challenge now with edge compute as well, but and also at the time with CDNs as well because we are we're still trying to we're managing demand, and we're managing speed. And the most fast asset we can deliver to someone is something that's cashed. So there's always a challenge on how you distribute or revalidate that cash. So in the CDA, it's so it's definitely I'm not gonna say it's anything that that's that's easy or perfect.\u003C\u002Fp>\u003Cp>It's a challenge at every level no matter how simple, even at the simplicity of image and and bundle cache, but also to the level of, of full HTML page cache. Right? So during during go during, say, ticket sale go lives or or certain events, we may sort of cache entire web pages, and that becomes incredibly complicated to deliver to many, many users because we then have the challenge of say, okay. What happens when the tickets now run out? And everyone should be seeing a, you know, sold out banner.\u003C\u002Fp>\u003Cp>And, depending on where you last were when that cache hit that node Yeah. And the the the way the way cash is generally work is, you know, it's a pass through cache, meaning, the CDN is the thing that will ask your server, make the request to your server for content, receive it, stream it back to the user, and also save a version on on that server. So depending on who access it at what time, there will be a cache with a certain time to live of 5, 15 minutes a minute. So meaning someone accessed it at some point and has a version maybe 5 minutes old that is sitting there. So and that and, using DNS, using domain name resolution, Based on where you're coming from, it will pick that server and that cache from that server.\u003C\u002Fp>\u003Cp>So depending on where you are around the world, if no one's hit it in a in a few minutes, you may get stale content. Stale content, yes, huge challenge. And publishing new content, also a huge challenge because you will spike server resource, meaning it's you don't want to always necessarily be be live, because being live means you are sending a lot of data out.\u003C\u002Fp>\u003Cp>Speaker 0: Sure. Sure.\u003C\u002Fp>\u003Cp>Speaker 1: It's one thing that we was was common to do is or is like, okay. If I update, you know, if I update this article in WordPress or update this article in in whatever c CMS that I'm using to trigger like a full invalidation. Right? Like Directus. Thank you.\u003C\u002Fp>\u003Cp>Speaker 0: I\u003C\u002Fp>\u003Cp>Speaker 1: have used directors at at an organization as well. Don't be worried. In in in in in whatever CMS that you are using to just have, like, a blanket, like, asterisk invalidate everything.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Which is a perfectly valid strategy for keeping things up to date if that's, you know, your your, your your your requirement. Yeah. Your But there and there's there's also the requirement then of, well, keeping things up and keeping things, keeping things efficient as well. C CDNs and edge computing are also about efficiencies. We talked a bit about sending requests to your server and that caching level being also a protection mechanism of you not actually hitting your server all of the time.\u003C\u002Fp>\u003Cp>So, yes, it's very complicated, and it's kind of still on the engineer's side to figure that out. There are better frameworks to help with this. And as we get closer to real edge computing, there's less whole page caching that we have to do. And this is where we we I kind of wanna transition into well, okay. Well well, why?\u003C\u002Fp>\u003Cp>Well, why edge compute? Edge compute means we can run more things closer to the user, meaning, hopefully, we need to cache less. And if we cache less, we could be more live or or we could be we we can have, less individual things that we cash, and the broader interactivity of the website or smaller subsections of it can be more individually individually controlled as to what level of, of staleness we accept within a web.\u003C\u002Fp>\u003Cp>Speaker 0: But it's that trade off, isn't it, against, the or, you know, having non stale content ultimately delivered to users and the efficiency gained by being close to them.\u003C\u002Fp>\u003Cp>Speaker 1: Exactly. That's it's that's the that's the balance that we're trying to strike. And, really, CDN caching, and at the level of caching whole HTML pages is kind of like is, you know, is the nuclear option. Right? If you can afford to have if if you're a a, you know, a news site, a very, very static type content website where you can you can do things over JavaScript on the client, or, the the content that you're delivering is is very static, then you can afford whole page caching.\u003C\u002Fp>\u003Cp>But as as we get close as we get nearer the graph of, right, highly static versus highly interactive or highly, like, live content, like a ticketing experience where you before you go through the booking experience, you need to know you kind of wanna know, okay. Am I going into this? Am I gonna have to make it a whole account and then be told, oh, by the way, it's sold out as it is.\u003C\u002Fp>\u003Cp>Speaker 0: Or even or even more, like, personalized experiences like a social media network where we're not gonna full page cache every one of the profiles and every post and every comment that exists. No. No. No. So there's some element of we need to get fresh fresh data as part of this load.\u003C\u002Fp>\u003Cp>Speaker 1: Exactly. I mean, how many times are you, you know, just scrolling scrolling up to the top to get your fresh tweets all the time. Right? It's it's, there's a certain amount of stillness a user will accept, and there's also a certain amount of stillness that, the us as engineers or us as whatever company entity that we are will accept with with end users. And Yeah.\u003C\u002Fp>\u003Cp>And, and that's that's that's the balance that we're trying to strike. And with edge computing, we are trying to deliver more things or cash listings, and that's achievable in how's that? How's how how do you describe this now? It's a very challenging to describe because we already have compute in in one location. And, moving it closer, just moving it doesn't fulfill, like, kind of the whole stack of requirements that we have, which is we need to deliver quickly.\u003C\u002Fp>\u003Cp>We need to be efficient with our resource. We don't wanna just, you know, put things on the edge for the sake of it being on the edge and it costing an arm and a leg, which is one of the concerns you sort of raised as well, which is a a a perfectly normal, you know, objection to this.\u003C\u002Fp>\u003Cp>Speaker 0: I didn't even think about the cost. I was thinking purely complexity. But, yeah, of course, there's cost. You're replicating nodes, basically. And the more that happens on those nodes, the more expensive it is.\u003C\u002Fp>\u003Cp>I'd not even considered that, but, of course, that's part of it.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. And a lot of the the a lot of the current generation, edge compute are serverless pricing models, meaning you are paying for the, there's a there's a couple of pricing models in in this. It's called wall clock time and, and CPU time. One being CPUs, the actual CPU cycles that you consume, meaning you have to write also efficient code to execute. I'll do.\u003C\u002Fp>\u003Cp>Otherwise, if you're writing code that is doing weird thing, and there's all sorts of limitations, that exist in terms of what you can do because the run times are also different. When we have a real server, we have any run time that we want. We have any c libraries that we want. We have any language that we want. And a lot of the modern, edge compute locations or edge compute run times because they're also designed to run very, very fast on and not in necessarily, I'll say in air quotes, real service like, real they're not actually real servers.\u003C\u002Fp>\u003Cp>They are like a subset of the resources that a that these compute platforms have. So so, again, a couple of the popular ones I mentioned, CloudFlare and and CloudFront. Cloudfront is fronted by, Lambders, but not real Lambders. They're actually a subset of the Lambda runtime, that can only run certain limited amount of JavaScript.\u003C\u002Fp>\u003Cp>Speaker 0: What? In an effort to be less computationally expensive?\u003C\u002Fp>\u003Cp>Speaker 1: That, but also they've developed from this need of okay. I just need to transform an image very slightly. And over time, we've just kind of put trying to put more and more things in it. Cloudflare is is is a bit more is a lot more advanced. And also the CloudFront one, since I've used it back then, it has got a lot more complicated as well and has gained a lot more features.\u003C\u002Fp>\u003Cp>CloudFront have again, it's a limited runtime. It's not node. It is a node compatible runtime, but you don't have access to say every single node library or you don't have access maybe even to the entire node modules, or the entire NPM registry.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, is this, is this isolates where it's like pure it's a pure JavaScript runtime with, like, some exposed additional functionality? Because we use those inside of directors, inside of our automation builder. But I didn't know that a lot of the JavaScript creature comforts, things even like console log, things like set time out and things like this, they're not they're not part of Node JS. They're not part of, sorry, the core JavaScript spec. They're part of Node and the browser implementation of JavaScript.\u003C\u002Fp>\u003Cp>And you take for granted when these are the 2 really predominant runtimes that we use. So maybe it's something like that where they're using these isolate, environments with whatever additional functionality is exposed by the vendor.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. Yep. I I think isolates her was inspired by some of the some of the work that cloud flooded or or may even be a direct, a direct thing from from from Cloudflare. Yeah. Is it a direct descendant?\u003C\u002Fp>\u003Cp>But, but, yeah, it's it's exactly that of of we're no longer in the browser. We're very much on a highly efficient, sub resource, that exists far out on on the edges of of these compute regions. And if we if we were to bring up a map, and I'll share share some assets after as well of we we know the general, you know, EU west 2 being London and, US east 2 from AWS being, you know, in Ohio somewhere and your EU West 1 or your or Google's, you know, EU Central 1 in Norway. These are all locations that they have massive, massive, huge data centers with further sub availability zones being, you know, eu west 1ab, e u west 1 c, etcetera. So huge, huge, you know, warehouse sized data centers with tons and tons of servers that have tons and tons of services on them.\u003C\u002Fp>\u003Cp>When you additionally look at the map of of, what are the cloud front regions, which are regions where AWS or some other, you know Vendor. Vendor may have, caching locations for, which which are much the smallest subset of machines that they that they control that in partner data centers or in what are called crosslink or interlinked locations where, you know, many vendors, have their servers and, you know, pass cables all over to each other. That's sort of how how the Internet works in that. There's a whole bunch of data centers where eventually cables interlink with each other's data centers. But vendors will have also service in these locations, and these locations are, you know, a lot pricier, a lot closer to the user, and also run much less much much more controlled environments.\u003C\u002Fp>\u003Cp>And these are these are these are what we actually refer to. This is what we really, really refer to when we talk about edge locations. We can be close to the user in terms of real locations also in terms of okay. Practically, you sort of want say, if you have a server in you e in US in the West Coast of the US in California, you would also really like a u a a server sort of in Europe ish to serve most of Europe. And then the next optimization is to put them super, super, super close to exactly where, you know, their ISP will interlink and hop over into AWS's domain.\u003C\u002Fp>\u003Cp>And that that right there is where we where we actually talk about edge compute.\u003C\u002Fp>\u003Cp>Speaker 0: Interesting.\u003C\u002Fp>\u003Cp>Speaker 1: So we're putting them beyond server farms going all the way down to kind of the interchanges. And that's all, yeah, we kind of we we put up with lesser resource at our disposal, because also we're sharing with a lot more users or, like, the servers have to serve, you know, many other users, things like that. And, also, because we're so close to the edge and we're doing many other things like fetching and, you know, sending things over the wire as we're streaming. We're we're we're constrained to much more, much faster response times. So Cloudflow, for example, I think you you have to do whatever you whatever compute you do on that edge has to be within, like, half half a second or something or or a second, and you can't do anything more than that, and you get instantly cut off.\u003C\u002Fp>\u003Cp>Speaker 0: That's fascinating. So this is really interesting because I thought edge computing was a concept that anyone could apply. It's just the idea that, hey. You know what? I'm gonna spin up resources on these different, you know, servers and do some complex, you know, routing of those.\u003C\u002Fp>\u003Cp>No. It really is provided by vendors who are kind of more at that backbone layer of infrastructure. And it's uniquely enabled by being really close to what you're calling these interchanges. That's really interesting and not something I quite understood because it's never a marketing material because it's it's really getting in the weeds.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. Edge computing, unfortunately, is a very monopolistic type, very, type of operation because it's not anything that we can get involved with. I I cannot I cannot go and run around and just give people, just put some pies around in server files.\u003C\u002Fp>\u003Cp>Speaker 0: Just that. Also, nice sticker. That is a retro sticker.\u003C\u002Fp>\u003Cp>Speaker 1: Of mine. Yeah. Because there's there's even more stickers in Slack. I can't afford to go and put these servers around, really, really close to users because I do not own that infrastructure, and I would never have access to that infrastructure because you need a lot a lot of money to be put into these type of locations. Like, you need to be running a network.\u003C\u002Fp>\u003Cp>But what you were describing there for just a moment as well of, yeah, just being able to put services kind of vaguely close to users, that is distributed computing, which is different from edge computing. It's both that's an versions of distributed computing are also very important to our goals of of, serving content to users really, really quickly. And this should be a computing and edge computing kind of go a little bit hand in hand as well. Because in in this day and age, at least today, we do have, and you raised at at the head of the show as well, your talk about databases or or data as well. This day and age, we sort of have, an inkling of kind of like the early CDN days.\u003C\u002Fp>\u003Cp>We have an inkling of being able to put databases on the edge. But, again, because there's such limited sub resourced run times, it's very, very difficult. So the current generation of databases still lie on the distributed computing layer, which means we our our content can be really, really close to the user. And, hopefully, the content that that little edge computing box is requesting is is kind of like our users are over here, and then the the users of our database being the edge computing nodes, we wanna also be close to those users. So, we need to we need to both balance, where we put the user's content and also where you put the edge computing nodes content.\u003C\u002Fp>\u003Cp>Yeah. Yeah. And more often than that, you know, at the end of the day, a website is kind of putting together a whole bunch of database queries. Right? Most websites.\u003C\u002Fp>\u003Cp>So if if we can also put the data that is needed to construct that web page close to where it is being constructed, then we have further performance gain. If we can get it if we can get them collocated right next to each other, that's fantastic. And one of the arguments against edge computing always like and you've also mentioned it as well. Why go through all the hassle to need to have this, now we have less environment. Now we have we can't use all my nice libraries, and I have to I have to struggle with, you know, doing things in under 500 milliseconds and whatever.\u003C\u002Fp>\u003Cp>Why do I have to go through all these struggles when it's good enough if they can just access my already distributed you know, I've already put a node in Europe, and I've already put a node in the US. That's good enough. And it comes down to what is the quality that what is the quality experience that we are aiming for? Are we aiming for near native, or, is, you know, is good enough good enough? I am a firm believer in it's the world wide web, and it's not the US web.\u003C\u002Fp>\u003Cp>And there was a time, Guillermo Rauch, kind of pioneers a little bit now with with Vercel, and their efforts in in edge compute or or delivering good UX as well to developers for for engineering on these new new run times. I've read the tweet of him once of in the early days when he was setting up Vercel or, whatever it was called back in the day. Zite. Zite. Zite.zite.co.\u003C\u002Fp>\u003Cp>Whenever he's setting up zite.co and he first went to the s went first went to San Francisco, he's like, oh my god. The Internet's so fast over here because that's just where that's just where your your data center starts is in the US. And then good luck when you finally if you decide to put a server somewhere in Europe and we get access to some slightly faster websites. And And at the end of the day, we are talking slightly faster here and there in a lot of cases because our undersea cables between, you know, Europe and the US are pretty fast. If I was to put a data center in Oregon, and we can do we can do this this is one thing we can do.\u003C\u002Fp>\u003Cp>We could do a little, ping test as these sites that can ping across all the data centers. If you if if you do a ping test into Ohio, it's about 250 to 300 milliseconds of round trip time. Vaguely okay for most for most use cases. It depend\u003C\u002Fp>\u003Cp>Speaker 0: it depends how resource intensive your application is, I suppose. If you're starting to build something that's really, really bandwidth intensive, multimedia site, actually, it starts to matter a lot more.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. Multimedia if just even just a normal website. That's that's that's kind of fine for Europe. Right? But, again, we're not the worldwide web of US and UK.\u003C\u002Fp>\u003Cp>We're the world wide web. And imagine having being in Australia or being in South Africa or being in India or being in China, and having 800 milliseconds to 1.2 seconds of round trip time. And that is round trip time meaning on literally anything that you do. So first, there's and in modern web applications, that is alright. First load HTML.\u003C\u002Fp>\u003Cp>1.2 seconds just to get the first request going with streaming HTML, then it hits some JavaScript that he has to go fetch. And that's another 1.2 seconds of doing that. And that's that's just the round trip time, not only you then have to be downloading through that entire time. So it's it really compounds the more that's going on. And when we're aiming for native like snappiness, the goal the goal is native like snappiness with edge.\u003C\u002Fp>\u003Cp>And if the the the point is we need to get things really, really close to the user for that initial load. Humans, there's a psychological, element to slowness or to to delivering fast experiences. And if we can show things quickly, it doesn't matter as much how long it actually takes to then deliver things as long as something's happening. There's actually an interesting UX that exists today in AI because, there's this new UX pattern now of streaming text Oh, yeah. Which we didn't have a year ago.\u003C\u002Fp>\u003Cp>Speaker 0: The the data physically doesn't exist when that when I start getting a response. It's yeah.\u003C\u002Fp>\u003Cp>Speaker 1: And you you you imagine if if if we didn't have that u that UX only serves the purpose of AI being too slow today. Imagine us having to wait a full 10 seconds for us to get the full kind of page back of the AI response. That it that that would be an extremely frustrating human UX experience. But just by the fact that it's actually coming in character by character, you think, oh, shit. That was really fast.\u003C\u002Fp>\u003Cp>Because you you actually you're reading you and you're consuming. And that is the UX that edge computing is trying to tackle of. And having having a risk response back instantly and having your content just there straight away.\u003C\u002Fp>\u003Cp>Speaker 0: Could I ask a question? When you were talking about the, the concern I had around the effort that needs to go in, the effort you described was all about writing performant code in a more, in a more limited environment. But I ask, what is the actual work involved in setting up edge applications beyond just writing code that will run? Do I have to manage where stuff is, or do I basically just, like, fling it up to a vendor and they take care of the of the distribution of that? Like, is there is there work required to physically make it edge code that will run on the edge beyond the environment and beyond the, the performance requirements?\u003C\u002Fp>\u003Cp>Speaker 1: There is a gradient. There there there is there is a gradient of of effort that can be spent here. And every sing every single day, there are new there is more and more tooling that makes it easier. As a baseline, anything you do in AWS will be hard because they are a platform. Right?\u003C\u002Fp>\u003Cp>They're they're just they they are they are infrastructure of a service. They're not the platform, and they're not the tooling. They are they have other services on top, like, you know, your, whatever they call their their stuff.\u003C\u002Fp>\u003Cp>Speaker 0: Amp Amplify.\u003C\u002Fp>\u003Cp>Speaker 1: Amplify. AWS Amplify are sort of some of those toolings that do distribute compute. Google Cloud, also, it's gonna be hard, but they have other services built on top. They have things like Cloud Spanner is their distributed database. Really, really fun if you read their white paper on on Cloud Spanner.\u003C\u002Fp>\u003Cp>It's it's a very googly white paper, and it's very very overcomplicated thing. And they talk about syncing satellites and time clocks in servers across the planet and how they use this network of satellites to have, like, microsecond time precision is very interesting read. Highly recommend. There's things like that. And there's Firebase on top, which also does distributed computing.\u003C\u002Fp>\u003Cp>And so there's, so there's frameworks built upon these things that that that make it easier. And ultimately, it comes down to it to frameworks or other platforms that make it easier as we go up the stack. Vercel also are a platform, but they make underneath you know, they they use AWS Lambdas. They use Cloudflare edge workers.\u003C\u002Fp>\u003Cp>Speaker 0: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 1: And that's sort of where if if you've ever experienced working with Nest, which is, again, kind of another superset on top of Vercel, because it just kinda uses all of Vercel features. When you are working in a an edge nest route, you have a limited amount you have a subset of things that you are able to sort of do from from the from the platform Sure. Because it's running it's no longer running on a Lambda, which is a 4 Node JS instance. It's actually running on a Cloudflare Worker, which is that isolate that you just described. So they are tooling.\u003C\u002Fp>\u003Cp>There is tooling that makes it easier. And then in other realms, in databases or in CDNs, there's more tooling there as well. There's there there's such tools like, PlanetScale is becoming very popular these days with its easy distributed computing of or easy distributed computing of databases. And there are other things that are trying to actually put databases on the edge, and the current kind of front runner for that are, SQLite, which actually Cloudflare again, another pioneer in this area. KV, key value store, is a product that is built on SQLite.\u003C\u002Fp>\u003Cp>Dino, have a KV, a key value store. Again, kind of built not on SQLite directly, but a fork of it that does this distributed computing. A whole bunch of startups doing SQLite type distributed databases. Edge actually, edge database. Not only distributed, but actual edge\u003C\u002Fp>\u003Cp>Speaker 0: On those edge nodes. The how does syncing oh, sorry. What I'm basically hearing is vendors more or less will take care of the of the distributing of your assets, whatever those assets are across nodes that they make available. That's not something I necessarily need to do. If I don't care about edge, if I just care about distributed, it's something I could do with great pain.\u003C\u002Fp>\u003Cp>Yep. I or I imagine there's tooling still. But a lot of these vendors will just take care of that. That's part of the offering. What about syncing stuff?\u003C\u002Fp>\u003Cp>Surely, there's some trade off around, you know, even if it's the data store is what I'll call it over a database. The data store, the cloud, you know, the cloud functions that run the edge functions, and all the assets and stuff like that. How do they all stay in sync? Obviously, when I push an explicit update, I imagine there is an explicit, you know, propagation of that of the updates. But just day to day, I\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: I'm a user. I add an item to a data store that happens near me. How does it get to everything else? And is there a risk involved that things will start to fall out of sync?\u003C\u002Fp>\u003Cp>Speaker 1: Yep. Certainly. Now the for the KV stores, for the for the SQLite stores, I've really not read up enough about what's going on there. It's like mind blowing stuff that goes beyond my comprehension right now. So I have no idea how they sync because, also, SQLite is a binary data format.\u003C\u002Fp>\u003Cp>So it's it's mostly if anything, I believe it's kind of for, like, edge caching or some sort of be able to write small things. I don't I don't know how it gets back to your main server. Crazy crazy black magic over there.\u003C\u002Fp>\u003Cp>Speaker 0: Sorry. Can I pause you? Your main server. So, in all of this, there's still a main server running that\u003C\u002Fp>\u003Cp>Speaker 1: You could. You could. Or at least, you you you definitely could write you kind of have to flip how you how you develop. If you wanna be entirely edge, you certainly can. And a lot of use cases don't really fit into just being entirely edge.\u003C\u002Fp>\u003Cp>Mhmm. Even with, say, Vercel, for all their efforts in doing, distributed computing, or edge computing, when you actually log into your Vercel instance, there is a drop down that you could actually pick your main location that it that it puts. Interesting. It's, it still needs to run that Lambda somewhere in some AWS data center. So there's still a a a main place that you're actually doing real compute.\u003C\u002Fp>\u003Cp>You can definitely, if put with a lot of effort, build entirely edge, services, but there will be points at which you need to escape from that to build, to have the full, you know, a company of of real runtime. Sure. And any other any other service that you might need to inter interconnect with, like any, like, queuing system or whatever or and any any real app that is doing something more complicated than just serving a website is gonna need other resource that cannot be run on the edge. And, honestly, it doesn't necessarily even need to run on the edge because we can get you know, there there is, like, the liveness of the website and your interactivity. And then there may be other things that you need to do in terms of, you know, workers or sending batch emails or, you know, do doing other compute or, like, you know, on YouTube scale, like, you know, upload a video and you have to transcode it and all that.\u003C\u002Fp>\u003Cp>So there there there's there's always other things that you may need to do in a in a business to serve whatever use case that your business is serving, but the the, serving of a website can kinda be isolated down to, you know, this edge thing.\u003C\u002Fp>\u003Cp>Speaker 0: That's but that's fascinating. I never thought about that. Edge is like a is like one of the tools in a wider application. It's not I it's not application that's very traditional or perhaps distributed or whatever, but it runs properly. And then elements of this application are run on the edge that benefit from the, from the characteristics of running on the edge.\u003C\u002Fp>\u003Cp>That is something that has never quite I never knew that. That's really interesting. But is it fair to say that that core application has to coordinate the edge nodes? Is that what you're what you're thinking? Because you said because you were talking about stuff going back to your main application.\u003C\u002Fp>\u003Cp>Tangent.\u003C\u002Fp>\u003Cp>Speaker 1: You you you you could. And I and I think it's it's really clicking with you when, earlier when I was describing, you know, just delivering full page versus those small elements of it, and it's like and it's kind of exactly that. But in terms of coordination, you know, this this then gets into, you know, microservice distributed computing type deals of, like, okay. They they can be isolated in their own way and, more was more referring to when sending things back is kind of the data that the user may may have input. So user input or, you know, if they're chatting away, if they're interacting with if they purchased a ticket and writing that back to the source of truth that ultimately, there must be a source of truth somewhere.\u003C\u002Fp>\u003Cp>Ultimately, some database needs to know that, look, the tickets were a 100, and now they're 99 available. Someone needs to know and be written back to, and that's what I what I'm referring to when I say, okay. Eventually, get back to\u003C\u002Fp>\u003Cp>Speaker 0: Got it.\u003C\u002Fp>\u003Cp>Speaker 1: That that source.\u003C\u002Fp>\u003Cp>Speaker 0: Got it. Got it.\u003C\u002Fp>\u003Cp>Speaker 1: And there's many strategies that we can employ on that source of truth because we can we can have a distributed source of truth. Kinda this Cloud Spanner database from Google, which kinda came out, like, 8 years ago, and that that white paper was making its rounds in distributed computing land, was really, really exciting because it sold it was trying to solve the problem of multi node writers. It's a challenging databases today to have multiple, multiple writer nodes. So current current database technology relies on having a single writer with multiple read rep. So you can you can you can replicate readers all day long because it's easy to, and sort of the the whole theme of this conversation has been it's really easy to replicate, caching content, so replicate reading content, but it's very, very hard to replicate writing or updating content or Absolutely.\u003C\u002Fp>\u003Cp>Invalidating. So it's always been very easy to do low latency writes with low latency read replicas where, you know, you might have, you know, those, you know, basic basically, the the ping between 2 servers be your latency from from read write replicas. So you may say and it and depending on your use cases, it may be acceptable that, okay, Europe is, say, 500 milliseconds behind US. So if you were to go to a website, technically, you would be seeing 500 millisecond, stale content, and that may be acceptable. But it may in terms of fairness of the Internet or the World Wide Web and not the UK, US Web, It's unfair to set to always put an advantage on, your your geological, position.\u003C\u002Fp>\u003Cp>So, technically, or, and I think that there there was an article once upon a time around when the Cloudspana or one of the Cloudspana customers originally was Ticketmaster. And their particular use case was this sort of fairness topic of well, okay. Australia will always be 1.2 seconds behind everybody. So if they go to book a ticket and someone literally just, you know, 1.2 seconds closer to the server clicks it, they will always win. So it's unfair to put people at this kind of dispute disadvantage.\u003C\u002Fp>\u003Cp>So we're trying to build technologies that distribute these rights more evenly, and you can get kind of more fair distribution of of updates.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. Ticketmaster, the bastion of fairness. Sorry. Please continue.\u003C\u002Fp>\u003Cp>Speaker 1: Well, in in in reality, what they actually want is, you know, more they can cash less and not have this this, queuing system, which which leads to more clicks, more purchases. Right? That's that's that's the end goal. That's the end goal with all of this. There's well documented stats about how Google goes about about web performance on the web.\u003C\u002Fp>\u003Cp>And, you know, if you're x milliseconds slower than a competitor, you know, you're more likely to drop off and just go to the next site that is faster. So while us as engineers, we wanna over complicate our life and do fun complicated engineering things, Us as business people, we wanna deliver things fast because it yields greater returns and is a better native experience and will yield happier users. That's ultimately the goal that we're trying to trying to solve, while also doing it more cheaply, hopefully.\u003C\u002Fp>\u003Cp>Speaker 0: Or at least over time. So a question for you then. In the current state of edge computing, what are people actually doing on\u003C\u002Fp>\u003Cp>Speaker 1: the edge beyond what, you know, what was once possible with, you know, just CDNs? What people what the most trendy thing now is we are rendering HTML on the edge. We are rendering websites closer to users. And what I mean by rendering websites is we are rendering more interactive content next to the user. So no longer are we constructing the whole webs web page, you know, 800 milliseconds away from the user and then delivering that over the wire.\u003C\u002Fp>\u003Cp>We are constructing it closer, meaning all the other subsequent calls that we might have to make to other services, are hopefully also calling services also closer to that edge region. So it's it's more and more, companies are putting more and more of their own services in more locations. Sure. So say, let's take an example of I wanna construct a web pay a Shopify web page. Shopify have a fantastic global API network.\u003C\u002Fp>\u003Cp>I argue that actually Shopify have the most distributed database on the market because actually, you know, when you update something in Shopify, it's already distributed to their network of of of re replicas around the world. If I was to construct the data there, and then, okay, I also call out to another service that I control, or I also call out to, like, another partner API. If we if we request that, make a request there, and it goes 800 milliseconds to wherever it was being constructed, and then that that does its course and it comes 800 milliseconds back, that's, you know, 600 a 1.6 second round trip or whatever. If we take that request and say put it right next to the user and go, okay. As soon as it comes in, I'm instantly streaming back.\u003C\u002Fp>\u003Cp>So it's close to the user, say, 20 milliseconds away. Generally, edge location is a sub 100 millisecond close to the user.\u003C\u002Fp>\u003Cp>Speaker 0: Good to know.\u003C\u002Fp>\u003Cp>Speaker 1: So they they hit it. Instantly, they get back, you know, they white page with loading things, and it goes, okay. Slows this. Okay. Now I need to grab some stuff from Shopify.\u003C\u002Fp>\u003Cp>I've done a request to Shopify back and forth. Hopefully, they're also at edge location, 20 milliseconds back and forth on my edge. I've got my Shopify content. Oh, I'm doing an AB test. I've hit my AB test service.com, and I've got back the result of what needs to happen.\u003C\u002Fp>\u003Cp>And, okay, I've loaded that content. And it's this it's this dynamicness that we wanna put closer and closer to the user because if we were\u003C\u002Fp>\u003Cp>Speaker 0: to And it and it compounds. Yep. All of these 800 millisecond round trips are grueling, but a bunch of 20. I mean, of course, you still wanna be mindful of how many you're doing, but that's the same in any web application. But we're talking orders of magnitude quicker realistically when you're loading any modern web application.\u003C\u002Fp>\u003Cp>Speaker 1: Exactly. Yep. So it's it's it's more it's more of this dynamicness because we could totally just off cache that entire page and delivered it to all the users equally. But what if one one user is logged in, one user has a basket where they, that that that we wanna also display and and render on the server? We could totally render it on the client, and this is where also this new modern trend of, you know, it was very it was perfectly fine to deliver entire huge bundle to the user and it be interactive and do API calls on the client side because the client side is also an edge location.\u003C\u002Fp>\u003Cp>Sure. But we pay for the upfront cost of them receiving that bundle. So if we can\u003C\u002Fp>\u003Cp>Speaker 0: That's interesting. Yep. So it's that trade off still of speed. We need to deliver a a reasonable experience as quickly as possible. Then, ideally, we want all subsequent requests to be as speedy as possible.\u003C\u002Fp>\u003Cp>But if, you know, if this is where we're starting to pay a little bit, that can be more acceptable dependent on user and kind of, developer experience or, like, you know, vendor experience requirements. Exactly. That's really interesting. So it's just this trade off of of requirements. I feel like this, you know, edge computing starting to gain some traction is bringing a whole new realm of, I don't know what to call that requirement gathering, like, trade off decisions for developers who didn't need to care as much about infrastructure before.\u003C\u002Fp>\u003Cp>And now suddenly, the same way front end developers now have to do a bunch of back end work with these kind of mixed hybrid frameworks that run everywhere. I feel like in the in the same way, anyone who does back end work is now having to care about infrastructure in a way where perhaps the the expectations in the past were less than. Yep. And then it's the job of these vendors who who run edge nodes to make that experience as contain as little pain as possible.\u003C\u002Fp>\u003Cp>Speaker 1: I think I think you've hit the nail right right on the head there is, we we've we've this this trend is the flip side of is is the next evolution of SPAs, of single page web apps. So that was our edge in the past, and that was our limps limited subset runtime. We could only run JavaScript, then we could only run web JavaScript in that runtime. And, what we're having now is that it's a limited run time, yes, but there's we have more languages, more things that we can run closer to the user, and it's we're delivering them. We're not using the user's resource.\u003C\u002Fp>\u003Cp>Right? So a a lot of SPAs or a lot of, mid 2000 in the Internet relied on, you know, fast devices or, you know, doing a lot of read writing of, you know, the DOM tree and all this, and then lower, lower quality devices with less CPU, like dumb phones or or or or basic Android devices had, you know, a worse experience on the web. And what we're trying to deliver is by doing doing the complexities of the HTML rendering and whatnot close to the user and delivering them, you know, the content that just needs to be rendered and device can become dumber again, and need to do less, you know, JavaScript execution on the client. Because, really, all we all we actually want at the end of the day is to deliver that HTML to the user. And that liveness, that interactivity used to come from or used to only be able to come from SPAs, client side JavaScript.\u003C\u002Fp>\u003Cp>And what we're seeing today is that we're moving more and more of that client JavaScript onto the server, but also quite close to the user to be kind of that native like experience.\u003C\u002Fp>\u003Cp>Speaker 0: And that and they're the parts they're the parts. It is we want to make the users do as little computation as possible while not, suffering based on their location. The fact that a load of that computation is going to happen elsewhere. And it's that specific pairing of of requirements, that makes edge com that makes distributed computing more powerful and even more so edge computing because they run right at those interchanges. That's that's great.\u003C\u002Fp>\u003Cp>Now, for some of the other episodes of, learning things I love to hate, we went into a demo, but I feel like we've covered a lot here. I don't necessarily think we need to we need to do it so much. Plus, it's a really abstract concept where, you know, I'm sure there are ways, but it isn't gonna be now it's running on the edge, and we can really show that to its fullest. But I think I understand a little more why why edge computing is interesting now. Some more or less basic concepts about how it works when it also might not always be right.\u003C\u002Fp>\u003Cp>And this new idea that, oh, no. It's just parts of your application you can delegate to the edge. Again, not something that is spoken about by the hype machine very often.\u003C\u002Fp>\u003Cp>Speaker 1: Yep. It's just edge edge\u003C\u002Fp>\u003Cp>Speaker 0: edge edge all the time.\u003C\u002Fp>\u003Cp>Speaker 1: Just parts. Just parts. Right? And we we we have the we have the same mistaken view of serverless computing, in the past where it was like, okay. Now everything must be serverless.\u003C\u002Fp>\u003Cp>Mhmm. No. It's a subset of use cases that are that that benefit from it, and that's the it's the same with the edge. It's not we don't wanna put we don't need to put the entire server close to the user. That's impractical.\u003C\u002Fp>\u003Cp>But elements of it definitely can. And, and, yeah, and I think the the the the the reason it's so abstract in this way is it's hard for us to experience what other people are experiencing when we're in our, you know, our our nice modern MacBook on gigabit Internet in countries with good Internet. So it's it's hard to put us put ourselves in those shoes. And, the way we used to do that with SPAs and stuff is literally have a crappy old Android phone for dev that you would, you know, every so often look at and use. And we can replicate that if try try the web on a, on a VPN every so often.\u003C\u002Fp>\u003Cp>Or if you're in a foreign country, try going to some of your favorite websites and just seeing how much slower they might feel. Because, yeah, it's it's\u003C\u002Fp>\u003Cp>Speaker 0: conjured up a memory of a it's slightly off topic, but you've just conjured up a memory of in London, Mozilla used to have an open community space, and in that, they had what they called the Mozilla Device Lab. I imagine there\u003C\u002Fp>\u003Cp>Speaker 1: were a few of them, and\u003C\u002Fp>\u003Cp>Speaker 0: it was just a bunch of different devices. Or, like, they run they run browsers which are, you know, really, really, really limited with this exact idea that you could be testing on all these different devices. But, yeah, now we need to consider and I suppose that's the other part here. What edge computing enables is less computation happening on a user device, so it's lowering the needs on the user device, and it is making that computation closer. So we're kinda leveling the playing field.\u003C\u002Fp>\u003Cp>It's this fairness, as you mentioned earlier, of you don't need the best hardware, and it doesn't necessarily matter where in the world you are. Both of those factors are handled by this new emergent technology.\u003C\u002Fp>\u003Cp>Speaker 1: Exactly. Exactly. And it's it's it's all it's all right as rain for us to talk about fairness, but at the end of the day is we want to have users around the world because we're hitting we're hitting more ability for us to sell to more users. And at the end of the day, that's really what we're trying to solve with with edge computing because get more and more users with more and more happy users because we can have users that are unhappy, but, really, we wanna have more and more happy users.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Awesome. Thank you so much for joining me. This has been really, really interesting. I'm loving filming this series.\u003C\u002Fp>\u003Cp>I'm learning tons. And hopefully people who have joined us for the ride also, are learning loads. Just before we go, is there anything else you wanna share, point people to where to find you?\u003C\u002Fp>\u003Cp>Speaker 1: Sure. I mean, if you want if you wanna see more of, like, crazy drawings on my Twitter, I'm I'm at Pandeliszed, p a n d e l I s, zed, on Twitter. It's it's funny when I was drawing that in the office because we we kinda sit in a bit of a semicircle, and my colleagues turned around and were like, what? How are you doing? I'm like, oh, just drawing.\u003C\u002Fp>\u003Cp>Just drawing some stick figures. I was like, okay. Fine. But, yep, that's that's that's where my normal antics are or on Twitter. And, and yeah.\u003C\u002Fp>\u003Cp>No. I'm so so happy, that you had me, Kevin, and it was, I I saw your brain clicking at the end there. So I'm so glad that I could I could share that with you, and, hopefully, it's it's no longer anything that you that you hate.\u003C\u002Fp>\u003Cp>Speaker 0: It is not something that I hate. I have, a newfound like other episodes, a newfound appreciation and more importantly, an understanding of the qualities. Right? Which mean that it may be something I reach for when appropriate. Whereas before, I just lacked the knowledge.\u003C\u002Fp>\u003Cp>So I was never gonna reach for it. And, you know, in time, that could lead to me building things that aren't as good, as they could be. So, yeah, thank you ever so much. Thank you to everyone who has joined us, and, we will see you in another episode of learning things I love to hate. Bye.\u003C\u002Fp>","Hello, and welcome to learning things I love to hate. This is a show where I basically stop putting off learning things that I have avoided for one reason or another, inviting in my friends and experts around the topics that I'm trying to learn. And today's topic is edge computing, a topic which has got a little spicy in web circles in the last few weeks. I'm inviting my lovely dear friend, Pandellus, here today. Pandellus, would you like to introduce yourself? Yeah. Hi, Kevin. Nice to see you again. Yeah. I'm I'm Pandellus. I've been an engineer for the last few years working in web, working in in deploying a lot of the things that I claim to write. I've worked in web agencies where delivering content to users was super, super important. Back in the jQuery CDN days where when edge computing was basically only CDNs into kind of our modern edge computing world now where we can put entire runtimes, entire databases onto the edge. There's all things I've experimented with at various companies. So I I'm so happy when I saw your tweet. It's something I've been experimenting with a lot. And, as someone as a user of the web, I think it's something super important for for everyone to to to to to know about and experiment with. Awesome. And, and yeah. So I tweeted out a few days before this recording. Can anyone just, like, explain edge computing to me? Because I really don't understand it. And you came in with a wonderful set of illustrations, which actually will just edit in here right now. These are the illustrations, to kind of explain a little bit about how Edge works. So I thought, hey, I'm just gonna invite you onto this show. This was already a topic I was planning on talking about. You preluded, I suppose, some of the things I wanted to discuss today. So just to just to open with this, I wanted to explain kind of where my knowledge is now, and hope that you might be able to take me and anyone watching who's in a similar spot through that journey of understanding more. And perhaps if we have some time at the end, we can do a little demo of this abstract concept. So what I know of edge computing, I think, really is around content delivery network, CDNs. We can maybe start a little bit about talking about this. And I know that the concept of what can be stored, you know, and replicated in multiple regions and serve to people based on where they are in the world has just grown more complex. We we're doing that with more things. And I suppose my biggest concern and the reason I haven't really done it yet is, 1, the idea that, you somehow need to replicate things really rapidly in lots of regions. And 2, unrelated, it just seems really complex, and I'm not sure how how much the complexity is worth, what might be a payoff that doesn't matter. I know there are some performance hardcore people who are like, of course, it matters. But, yeah, that's kind of my starting point. So maybe if you take us back, talk a little bit about CDNs and what they are and kinda how that's evolved to where we are now, that would be really good. Yeah. Of course. Yeah. No. I mean, as engineers, we like to make our lives harder for sure. It's it's it's we we we do sometimes like to like to overengineer, and it and it's all as with everything in engineering, there's a million ways to do them. Not all of them are right, and a lot of them are right. So your mileage may vary with your needs of edge whatever. A lot of people don't even need the CDN, but it comes down to your users. Right? It always comes down to your users and the experience that you're trying to deliver. So let's let's start with CDNs, and what is the problem that that that CDNs solve? So if we go back to the mid 2000, when jQuery and, you know, serving libraries over the wire were were were all their age, we would, you know, blindly go to, CDN stack or whatever those CDN websites were and grab URLs for for jQuery and put them in our sites. Before build systems, before your web packs, before the the the modern JavaScript ecosystem back in days, CDNs were all their age because we wanted to deliver large JavaScript bundles to the user quickly. Internet was slower, and still is slow. Or this is h t p one days, right, before streaming h before multi, you know, multiple h t p two streams, and all that type of stuff. So it was very important or we people saw a lot of gain in having these large JavaScript bundles close to the user, meaning the end users could consume that JavaScript faster. And in the end, what that means is when they get their HTML through, it becomes interactive faster. Could I ask you a question? Could I pause it? So when you're saying near to where people are, just to clarify, we mean that this one jQuery bundle or whatever is stored on multiple servers throughout the world. I request it via one URL, and then the application which will serve up that file will find the place nearest to where I'm making the request and serve it. And then the additional benefit of CDNs, I suppose, is if I visit 10 sites that all are trying to load the same large bundle, this might be more CDN specific, and I think it also might be a relic of the past, but it it would not It is a relic of the past. That's that's that's one of the topics I was gonna touch on. In in the early days, it was when there was less CDNs around, we we could make the ex we could make the excuse that, okay, by using a shared CDN or, say, a common example was also Google Fonts. Mhmm. By using Google Fonts, by using a common CDN, it means that there's a higher likelihood that a user would already have those assets on their computer. Cached. Okay. Exactly. So it's a further cache. Ultimately, the the most edge location is the user's computer. So caching is always a tiering system and CDNs are a type of cache, and the ultimate caching is local. When you have a local cache, that's that's the ultimate edge. And really with edge compute, with edge with with the topic of delivering on the edge, what we're trying to achieve is native like performance, and the most edge you can get is being on device. So, arguably, the the most edge computing delivery method today is an app. Apps are on your device, and there's no more edge than the the the device that you're touching. And everything that we're doing on the web or or in edge compute land is to try and achieve native like performance. Cool. That makes sense. So, yeah, those are those are sort of CDNs, and that is kind of the the most basic form of putting things on on the edge that we had. So that and that was, and there was a legitimate benefit to to to to CDNs. And what we've tried to do in or what has happened in in the modern era is CDNs themselves have become more and more capable, to the point where they can actually run and execute code. 2 of the most popular and, CDNs around are are CloudFront from AWS and, Cloudflare. And Cloudflare are one of the pioneers of of edge computing as well, and their CDN offering just over time naturally has become more has gained features, has had feature creep, and eventually, we wanted to do more complicated things over time. Right? We we may wanna deliver a slightly different version of a bundle for certain users. Or if it gets hit with a with a certain URL parameter, we might wanna bust the cache or or run some sort of, run something on that server that's delivering it, maybe do an AB type test, where certain users got certain bundles or whatnot. So at some point transformations as well? File transformations became yep. Image image CDNs. So moving from kind of just JS bundles, image CDNs are, again, super popular because, on super useful because, the the 2 largest assets that get delivered are the bundle and the images. And images also very bandwidth intensive and benefit largely from being closer to the user. Sure. And we're gonna we're gonna say closer to the user a lot throughout this conversation. And what that what that practically means is that you are spending less time in flight over cables. Your server could be very far away. And the further away it is, the longer it will take to navigate, you know, the the, the the sea of cables that live under the sea, to get to your user. So the closer you can be, the less hops we can make, the the the the the better for for the end user, but also for our bank, CDNs, specifically, not necessarily edge compute, but CDNs, specifically, also benefit benefit us in that we send less data out because it's also a caching mechanism. Edge compute, not so much when we when we get when we get down to it, because one of those goals is to have as fresh content as possible. So when you're when you're when you're caching, you're limited by the freshness that you can deliver. So that that's that's sort of that that's sort of the niche that CDNs sold for a while. And, yeah, more and more doing more and more things on the edge or closer to the user or doing more compute at the CDN level means that we could do these kind of transformation things that you just mentioned with images. Right? So say I wanted an image 30% smaller, we could deliver 1 all the way back from our original server, or we could use the kind of already existing cached asset and do some computation on it and then also cache that. And that's this kind of level of level of tiering that that that being closer to the user kind of offers us is is we don't wanna keep hitting our main server, and wanna keep delivering faster things to the user because, yeah, it's all about speed at the end with with with the goal of edge. Cool. So now we're in a space where it's not just about storing assets, whether that's bundles, images, you know, media, whatever. It's actually about running computational tasks at the edge. So I suppose, naturally, that moves us on to maybe what what is edge compute edge computing? What is it capable of now? And then I suppose we're going back to my initial concerns or skepticisms. 1, how do you keep all of these nodes? I don't know if they're called nodes. I'm assuming they're called nodes in in sync, especially when you gave an example of, you know, doing transformations at the edge and then caching that. Well, is the cache just at a node level? Do they propagate throughout a network? Stuff like this, I'm not too sure about. And then it sounds really complex. How how does this actually shake out to being something that's tolerable to developers? Yeah. Edge and the and this this edge cache or caching cash busting, in CDN land, it has always been a pain, especially in the days where I was working as at a web agency. So, basically, all all all that we deliver over the wire is is assets and and JavaScript and images, and it was definitely always always a battle of, okay, well, what is actually being delivered to the user and cache busting it and and and figuring out, oh, okay. Oh, they're on an older version of the of the site, and they're seeing old content and it and us getting rang up by our clients being like, the other site that we just published it. It's not gone live, and that's having to explain. Okay. You're gonna wait for the cache to propagate. It's it's gonna it's gonna get to the your user eventually. No. But we've we've launched this because, some context here. I used to work in, theater websites, where we had very peak peaky demand spiky demand and very, and certain requirements around, you know, if a certain show went live, they want it live now so that people can book it now. Which is not an unreasonable expectation if I hit publish that I mean, that's even the thing with, like, static website building. There's always a build period, and some people don't get that that build period, you know, is can be quite material. Yep. And that is also that's a huge challenge now with edge compute as well, but and also at the time with CDNs as well because we are we're still trying to we're managing demand, and we're managing speed. And the most fast asset we can deliver to someone is something that's cashed. So there's always a challenge on how you distribute or revalidate that cash. So in the CDA, it's so it's definitely I'm not gonna say it's anything that that's that's easy or perfect. It's a challenge at every level no matter how simple, even at the simplicity of image and and bundle cache, but also to the level of, of full HTML page cache. Right? So during during go during, say, ticket sale go lives or or certain events, we may sort of cache entire web pages, and that becomes incredibly complicated to deliver to many, many users because we then have the challenge of say, okay. What happens when the tickets now run out? And everyone should be seeing a, you know, sold out banner. And, depending on where you last were when that cache hit that node Yeah. And the the the way the way cash is generally work is, you know, it's a pass through cache, meaning, the CDN is the thing that will ask your server, make the request to your server for content, receive it, stream it back to the user, and also save a version on on that server. So depending on who access it at what time, there will be a cache with a certain time to live of 5, 15 minutes a minute. So meaning someone accessed it at some point and has a version maybe 5 minutes old that is sitting there. So and that and, using DNS, using domain name resolution, Based on where you're coming from, it will pick that server and that cache from that server. So depending on where you are around the world, if no one's hit it in a in a few minutes, you may get stale content. Stale content, yes, huge challenge. And publishing new content, also a huge challenge because you will spike server resource, meaning it's you don't want to always necessarily be be live, because being live means you are sending a lot of data out. Sure. Sure. It's one thing that we was was common to do is or is like, okay. If I update, you know, if I update this article in WordPress or update this article in in whatever c CMS that I'm using to trigger like a full invalidation. Right? Like Directus. Thank you. I have used directors at at an organization as well. Don't be worried. In in in in in whatever CMS that you are using to just have, like, a blanket, like, asterisk invalidate everything. Yeah. Yeah. Which is a perfectly valid strategy for keeping things up to date if that's, you know, your your, your your your requirement. Yeah. Your But there and there's there's also the requirement then of, well, keeping things up and keeping things, keeping things efficient as well. C CDNs and edge computing are also about efficiencies. We talked a bit about sending requests to your server and that caching level being also a protection mechanism of you not actually hitting your server all of the time. So, yes, it's very complicated, and it's kind of still on the engineer's side to figure that out. There are better frameworks to help with this. And as we get closer to real edge computing, there's less whole page caching that we have to do. And this is where we we I kind of wanna transition into well, okay. Well well, why? Well, why edge compute? Edge compute means we can run more things closer to the user, meaning, hopefully, we need to cache less. And if we cache less, we could be more live or or we could be we we can have, less individual things that we cash, and the broader interactivity of the website or smaller subsections of it can be more individually individually controlled as to what level of, of staleness we accept within a web. But it's that trade off, isn't it, against, the or, you know, having non stale content ultimately delivered to users and the efficiency gained by being close to them. Exactly. That's it's that's the that's the balance that we're trying to strike. And, really, CDN caching, and at the level of caching whole HTML pages is kind of like is, you know, is the nuclear option. Right? If you can afford to have if if you're a a, you know, a news site, a very, very static type content website where you can you can do things over JavaScript on the client, or, the the content that you're delivering is is very static, then you can afford whole page caching. But as as we get close as we get nearer the graph of, right, highly static versus highly interactive or highly, like, live content, like a ticketing experience where you before you go through the booking experience, you need to know you kind of wanna know, okay. Am I going into this? Am I gonna have to make it a whole account and then be told, oh, by the way, it's sold out as it is. Or even or even more, like, personalized experiences like a social media network where we're not gonna full page cache every one of the profiles and every post and every comment that exists. No. No. No. So there's some element of we need to get fresh fresh data as part of this load. Exactly. I mean, how many times are you, you know, just scrolling scrolling up to the top to get your fresh tweets all the time. Right? It's it's, there's a certain amount of stillness a user will accept, and there's also a certain amount of stillness that, the us as engineers or us as whatever company entity that we are will accept with with end users. And Yeah. And, and that's that's that's the balance that we're trying to strike. And with edge computing, we are trying to deliver more things or cash listings, and that's achievable in how's that? How's how how do you describe this now? It's a very challenging to describe because we already have compute in in one location. And, moving it closer, just moving it doesn't fulfill, like, kind of the whole stack of requirements that we have, which is we need to deliver quickly. We need to be efficient with our resource. We don't wanna just, you know, put things on the edge for the sake of it being on the edge and it costing an arm and a leg, which is one of the concerns you sort of raised as well, which is a a a perfectly normal, you know, objection to this. I didn't even think about the cost. I was thinking purely complexity. But, yeah, of course, there's cost. You're replicating nodes, basically. And the more that happens on those nodes, the more expensive it is. I'd not even considered that, but, of course, that's part of it. Yep. And a lot of the the a lot of the current generation, edge compute are serverless pricing models, meaning you are paying for the, there's a there's a couple of pricing models in in this. It's called wall clock time and, and CPU time. One being CPUs, the actual CPU cycles that you consume, meaning you have to write also efficient code to execute. I'll do. Otherwise, if you're writing code that is doing weird thing, and there's all sorts of limitations, that exist in terms of what you can do because the run times are also different. When we have a real server, we have any run time that we want. We have any c libraries that we want. We have any language that we want. And a lot of the modern, edge compute locations or edge compute run times because they're also designed to run very, very fast on and not in necessarily, I'll say in air quotes, real service like, real they're not actually real servers. They are like a subset of the resources that a that these compute platforms have. So so, again, a couple of the popular ones I mentioned, CloudFlare and and CloudFront. Cloudfront is fronted by, Lambders, but not real Lambders. They're actually a subset of the Lambda runtime, that can only run certain limited amount of JavaScript. What? In an effort to be less computationally expensive? That, but also they've developed from this need of okay. I just need to transform an image very slightly. And over time, we've just kind of put trying to put more and more things in it. Cloudflare is is is a bit more is a lot more advanced. And also the CloudFront one, since I've used it back then, it has got a lot more complicated as well and has gained a lot more features. CloudFront have again, it's a limited runtime. It's not node. It is a node compatible runtime, but you don't have access to say every single node library or you don't have access maybe even to the entire node modules, or the entire NPM registry. Oh, is this, is this isolates where it's like pure it's a pure JavaScript runtime with, like, some exposed additional functionality? Because we use those inside of directors, inside of our automation builder. But I didn't know that a lot of the JavaScript creature comforts, things even like console log, things like set time out and things like this, they're not they're not part of Node JS. They're not part of, sorry, the core JavaScript spec. They're part of Node and the browser implementation of JavaScript. And you take for granted when these are the 2 really predominant runtimes that we use. So maybe it's something like that where they're using these isolate, environments with whatever additional functionality is exposed by the vendor. Yep. Yep. I I think isolates her was inspired by some of the some of the work that cloud flooded or or may even be a direct, a direct thing from from from Cloudflare. Yeah. Is it a direct descendant? But, but, yeah, it's it's exactly that of of we're no longer in the browser. We're very much on a highly efficient, sub resource, that exists far out on on the edges of of these compute regions. And if we if we were to bring up a map, and I'll share share some assets after as well of we we know the general, you know, EU west 2 being London and, US east 2 from AWS being, you know, in Ohio somewhere and your EU West 1 or your or Google's, you know, EU Central 1 in Norway. These are all locations that they have massive, massive, huge data centers with further sub availability zones being, you know, eu west 1ab, e u west 1 c, etcetera. So huge, huge, you know, warehouse sized data centers with tons and tons of servers that have tons and tons of services on them. When you additionally look at the map of of, what are the cloud front regions, which are regions where AWS or some other, you know Vendor. Vendor may have, caching locations for, which which are much the smallest subset of machines that they that they control that in partner data centers or in what are called crosslink or interlinked locations where, you know, many vendors, have their servers and, you know, pass cables all over to each other. That's sort of how how the Internet works in that. There's a whole bunch of data centers where eventually cables interlink with each other's data centers. But vendors will have also service in these locations, and these locations are, you know, a lot pricier, a lot closer to the user, and also run much less much much more controlled environments. And these are these are these are what we actually refer to. This is what we really, really refer to when we talk about edge locations. We can be close to the user in terms of real locations also in terms of okay. Practically, you sort of want say, if you have a server in you e in US in the West Coast of the US in California, you would also really like a u a a server sort of in Europe ish to serve most of Europe. And then the next optimization is to put them super, super, super close to exactly where, you know, their ISP will interlink and hop over into AWS's domain. And that that right there is where we where we actually talk about edge compute. Interesting. So we're putting them beyond server farms going all the way down to kind of the interchanges. And that's all, yeah, we kind of we we put up with lesser resource at our disposal, because also we're sharing with a lot more users or, like, the servers have to serve, you know, many other users, things like that. And, also, because we're so close to the edge and we're doing many other things like fetching and, you know, sending things over the wire as we're streaming. We're we're we're constrained to much more, much faster response times. So Cloudflow, for example, I think you you have to do whatever you whatever compute you do on that edge has to be within, like, half half a second or something or or a second, and you can't do anything more than that, and you get instantly cut off. That's fascinating. So this is really interesting because I thought edge computing was a concept that anyone could apply. It's just the idea that, hey. You know what? I'm gonna spin up resources on these different, you know, servers and do some complex, you know, routing of those. No. It really is provided by vendors who are kind of more at that backbone layer of infrastructure. And it's uniquely enabled by being really close to what you're calling these interchanges. That's really interesting and not something I quite understood because it's never a marketing material because it's it's really getting in the weeds. Yep. Edge computing, unfortunately, is a very monopolistic type, very, type of operation because it's not anything that we can get involved with. I I cannot I cannot go and run around and just give people, just put some pies around in server files. Just that. Also, nice sticker. That is a retro sticker. Of mine. Yeah. Because there's there's even more stickers in Slack. I can't afford to go and put these servers around, really, really close to users because I do not own that infrastructure, and I would never have access to that infrastructure because you need a lot a lot of money to be put into these type of locations. Like, you need to be running a network. But what you were describing there for just a moment as well of, yeah, just being able to put services kind of vaguely close to users, that is distributed computing, which is different from edge computing. It's both that's an versions of distributed computing are also very important to our goals of of, serving content to users really, really quickly. And this should be a computing and edge computing kind of go a little bit hand in hand as well. Because in in this day and age, at least today, we do have, and you raised at at the head of the show as well, your talk about databases or or data as well. This day and age, we sort of have, an inkling of kind of like the early CDN days. We have an inkling of being able to put databases on the edge. But, again, because there's such limited sub resourced run times, it's very, very difficult. So the current generation of databases still lie on the distributed computing layer, which means we our our content can be really, really close to the user. And, hopefully, the content that that little edge computing box is requesting is is kind of like our users are over here, and then the the users of our database being the edge computing nodes, we wanna also be close to those users. So, we need to we need to both balance, where we put the user's content and also where you put the edge computing nodes content. Yeah. Yeah. And more often than that, you know, at the end of the day, a website is kind of putting together a whole bunch of database queries. Right? Most websites. So if if we can also put the data that is needed to construct that web page close to where it is being constructed, then we have further performance gain. If we can get it if we can get them collocated right next to each other, that's fantastic. And one of the arguments against edge computing always like and you've also mentioned it as well. Why go through all the hassle to need to have this, now we have less environment. Now we have we can't use all my nice libraries, and I have to I have to struggle with, you know, doing things in under 500 milliseconds and whatever. Why do I have to go through all these struggles when it's good enough if they can just access my already distributed you know, I've already put a node in Europe, and I've already put a node in the US. That's good enough. And it comes down to what is the quality that what is the quality experience that we are aiming for? Are we aiming for near native, or, is, you know, is good enough good enough? I am a firm believer in it's the world wide web, and it's not the US web. And there was a time, Guillermo Rauch, kind of pioneers a little bit now with with Vercel, and their efforts in in edge compute or or delivering good UX as well to developers for for engineering on these new new run times. I've read the tweet of him once of in the early days when he was setting up Vercel or, whatever it was called back in the day. Zite. Zite. Zite.zite.co. Whenever he's setting up zite.co and he first went to the s went first went to San Francisco, he's like, oh my god. The Internet's so fast over here because that's just where that's just where your your data center starts is in the US. And then good luck when you finally if you decide to put a server somewhere in Europe and we get access to some slightly faster websites. And And at the end of the day, we are talking slightly faster here and there in a lot of cases because our undersea cables between, you know, Europe and the US are pretty fast. If I was to put a data center in Oregon, and we can do we can do this this is one thing we can do. We could do a little, ping test as these sites that can ping across all the data centers. If you if if you do a ping test into Ohio, it's about 250 to 300 milliseconds of round trip time. Vaguely okay for most for most use cases. It depend it depends how resource intensive your application is, I suppose. If you're starting to build something that's really, really bandwidth intensive, multimedia site, actually, it starts to matter a lot more. Yep. Multimedia if just even just a normal website. That's that's that's kind of fine for Europe. Right? But, again, we're not the worldwide web of US and UK. We're the world wide web. And imagine having being in Australia or being in South Africa or being in India or being in China, and having 800 milliseconds to 1.2 seconds of round trip time. And that is round trip time meaning on literally anything that you do. So first, there's and in modern web applications, that is alright. First load HTML. 1.2 seconds just to get the first request going with streaming HTML, then it hits some JavaScript that he has to go fetch. And that's another 1.2 seconds of doing that. And that's that's just the round trip time, not only you then have to be downloading through that entire time. So it's it really compounds the more that's going on. And when we're aiming for native like snappiness, the goal the goal is native like snappiness with edge. And if the the the point is we need to get things really, really close to the user for that initial load. Humans, there's a psychological, element to slowness or to to delivering fast experiences. And if we can show things quickly, it doesn't matter as much how long it actually takes to then deliver things as long as something's happening. There's actually an interesting UX that exists today in AI because, there's this new UX pattern now of streaming text Oh, yeah. Which we didn't have a year ago. The the data physically doesn't exist when that when I start getting a response. It's yeah. And you you you imagine if if if we didn't have that u that UX only serves the purpose of AI being too slow today. Imagine us having to wait a full 10 seconds for us to get the full kind of page back of the AI response. That it that that would be an extremely frustrating human UX experience. But just by the fact that it's actually coming in character by character, you think, oh, shit. That was really fast. Because you you actually you're reading you and you're consuming. And that is the UX that edge computing is trying to tackle of. And having having a risk response back instantly and having your content just there straight away. Could I ask a question? When you were talking about the, the concern I had around the effort that needs to go in, the effort you described was all about writing performant code in a more, in a more limited environment. But I ask, what is the actual work involved in setting up edge applications beyond just writing code that will run? Do I have to manage where stuff is, or do I basically just, like, fling it up to a vendor and they take care of the of the distribution of that? Like, is there is there work required to physically make it edge code that will run on the edge beyond the environment and beyond the, the performance requirements? There is a gradient. There there there is there is a gradient of of effort that can be spent here. And every sing every single day, there are new there is more and more tooling that makes it easier. As a baseline, anything you do in AWS will be hard because they are a platform. Right? They're they're just they they are they are infrastructure of a service. They're not the platform, and they're not the tooling. They are they have other services on top, like, you know, your, whatever they call their their stuff. Amp Amplify. Amplify. AWS Amplify are sort of some of those toolings that do distribute compute. Google Cloud, also, it's gonna be hard, but they have other services built on top. They have things like Cloud Spanner is their distributed database. Really, really fun if you read their white paper on on Cloud Spanner. It's it's a very googly white paper, and it's very very overcomplicated thing. And they talk about syncing satellites and time clocks in servers across the planet and how they use this network of satellites to have, like, microsecond time precision is very interesting read. Highly recommend. There's things like that. And there's Firebase on top, which also does distributed computing. And so there's, so there's frameworks built upon these things that that that make it easier. And ultimately, it comes down to it to frameworks or other platforms that make it easier as we go up the stack. Vercel also are a platform, but they make underneath you know, they they use AWS Lambdas. They use Cloudflare edge workers. Mhmm. And that's sort of where if if you've ever experienced working with Nest, which is, again, kind of another superset on top of Vercel, because it just kinda uses all of Vercel features. When you are working in a an edge nest route, you have a limited amount you have a subset of things that you are able to sort of do from from the from the platform Sure. Because it's running it's no longer running on a Lambda, which is a 4 Node JS instance. It's actually running on a Cloudflare Worker, which is that isolate that you just described. So they are tooling. There is tooling that makes it easier. And then in other realms, in databases or in CDNs, there's more tooling there as well. There's there there's such tools like, PlanetScale is becoming very popular these days with its easy distributed computing of or easy distributed computing of databases. And there are other things that are trying to actually put databases on the edge, and the current kind of front runner for that are, SQLite, which actually Cloudflare again, another pioneer in this area. KV, key value store, is a product that is built on SQLite. Dino, have a KV, a key value store. Again, kind of built not on SQLite directly, but a fork of it that does this distributed computing. A whole bunch of startups doing SQLite type distributed databases. Edge actually, edge database. Not only distributed, but actual edge On those edge nodes. The how does syncing oh, sorry. What I'm basically hearing is vendors more or less will take care of the of the distributing of your assets, whatever those assets are across nodes that they make available. That's not something I necessarily need to do. If I don't care about edge, if I just care about distributed, it's something I could do with great pain. Yep. I or I imagine there's tooling still. But a lot of these vendors will just take care of that. That's part of the offering. What about syncing stuff? Surely, there's some trade off around, you know, even if it's the data store is what I'll call it over a database. The data store, the cloud, you know, the cloud functions that run the edge functions, and all the assets and stuff like that. How do they all stay in sync? Obviously, when I push an explicit update, I imagine there is an explicit, you know, propagation of that of the updates. But just day to day, I Mhmm. I'm a user. I add an item to a data store that happens near me. How does it get to everything else? And is there a risk involved that things will start to fall out of sync? Yep. Certainly. Now the for the KV stores, for the for the SQLite stores, I've really not read up enough about what's going on there. It's like mind blowing stuff that goes beyond my comprehension right now. So I have no idea how they sync because, also, SQLite is a binary data format. So it's it's mostly if anything, I believe it's kind of for, like, edge caching or some sort of be able to write small things. I don't I don't know how it gets back to your main server. Crazy crazy black magic over there. Sorry. Can I pause you? Your main server. So, in all of this, there's still a main server running that You could. You could. Or at least, you you you definitely could write you kind of have to flip how you how you develop. If you wanna be entirely edge, you certainly can. And a lot of use cases don't really fit into just being entirely edge. Mhmm. Even with, say, Vercel, for all their efforts in doing, distributed computing, or edge computing, when you actually log into your Vercel instance, there is a drop down that you could actually pick your main location that it that it puts. Interesting. It's, it still needs to run that Lambda somewhere in some AWS data center. So there's still a a a main place that you're actually doing real compute. You can definitely, if put with a lot of effort, build entirely edge, services, but there will be points at which you need to escape from that to build, to have the full, you know, a company of of real runtime. Sure. And any other any other service that you might need to inter interconnect with, like any, like, queuing system or whatever or and any any real app that is doing something more complicated than just serving a website is gonna need other resource that cannot be run on the edge. And, honestly, it doesn't necessarily even need to run on the edge because we can get you know, there there is, like, the liveness of the website and your interactivity. And then there may be other things that you need to do in terms of, you know, workers or sending batch emails or, you know, do doing other compute or, like, you know, on YouTube scale, like, you know, upload a video and you have to transcode it and all that. So there there there's there's always other things that you may need to do in a in a business to serve whatever use case that your business is serving, but the the, serving of a website can kinda be isolated down to, you know, this edge thing. That's but that's fascinating. I never thought about that. Edge is like a is like one of the tools in a wider application. It's not I it's not application that's very traditional or perhaps distributed or whatever, but it runs properly. And then elements of this application are run on the edge that benefit from the, from the characteristics of running on the edge. That is something that has never quite I never knew that. That's really interesting. But is it fair to say that that core application has to coordinate the edge nodes? Is that what you're what you're thinking? Because you said because you were talking about stuff going back to your main application. Tangent. You you you you could. And I and I think it's it's really clicking with you when, earlier when I was describing, you know, just delivering full page versus those small elements of it, and it's like and it's kind of exactly that. But in terms of coordination, you know, this this then gets into, you know, microservice distributed computing type deals of, like, okay. They they can be isolated in their own way and, more was more referring to when sending things back is kind of the data that the user may may have input. So user input or, you know, if they're chatting away, if they're interacting with if they purchased a ticket and writing that back to the source of truth that ultimately, there must be a source of truth somewhere. Ultimately, some database needs to know that, look, the tickets were a 100, and now they're 99 available. Someone needs to know and be written back to, and that's what I what I'm referring to when I say, okay. Eventually, get back to Got it. That that source. Got it. Got it. And there's many strategies that we can employ on that source of truth because we can we can have a distributed source of truth. Kinda this Cloud Spanner database from Google, which kinda came out, like, 8 years ago, and that that white paper was making its rounds in distributed computing land, was really, really exciting because it sold it was trying to solve the problem of multi node writers. It's a challenging databases today to have multiple, multiple writer nodes. So current current database technology relies on having a single writer with multiple read rep. So you can you can you can replicate readers all day long because it's easy to, and sort of the the whole theme of this conversation has been it's really easy to replicate, caching content, so replicate reading content, but it's very, very hard to replicate writing or updating content or Absolutely. Invalidating. So it's always been very easy to do low latency writes with low latency read replicas where, you know, you might have, you know, those, you know, basic basically, the the ping between 2 servers be your latency from from read write replicas. So you may say and it and depending on your use cases, it may be acceptable that, okay, Europe is, say, 500 milliseconds behind US. So if you were to go to a website, technically, you would be seeing 500 millisecond, stale content, and that may be acceptable. But it may in terms of fairness of the Internet or the World Wide Web and not the UK, US Web, It's unfair to set to always put an advantage on, your your geological, position. So, technically, or, and I think that there there was an article once upon a time around when the Cloudspana or one of the Cloudspana customers originally was Ticketmaster. And their particular use case was this sort of fairness topic of well, okay. Australia will always be 1.2 seconds behind everybody. So if they go to book a ticket and someone literally just, you know, 1.2 seconds closer to the server clicks it, they will always win. So it's unfair to put people at this kind of dispute disadvantage. So we're trying to build technologies that distribute these rights more evenly, and you can get kind of more fair distribution of of updates. Yes. Ticketmaster, the bastion of fairness. Sorry. Please continue. Well, in in in reality, what they actually want is, you know, more they can cash less and not have this this, queuing system, which which leads to more clicks, more purchases. Right? That's that's that's the end goal. That's the end goal with all of this. There's well documented stats about how Google goes about about web performance on the web. And, you know, if you're x milliseconds slower than a competitor, you know, you're more likely to drop off and just go to the next site that is faster. So while us as engineers, we wanna over complicate our life and do fun complicated engineering things, Us as business people, we wanna deliver things fast because it yields greater returns and is a better native experience and will yield happier users. That's ultimately the goal that we're trying to trying to solve, while also doing it more cheaply, hopefully. Or at least over time. So a question for you then. In the current state of edge computing, what are people actually doing on the edge beyond what, you know, what was once possible with, you know, just CDNs? What people what the most trendy thing now is we are rendering HTML on the edge. We are rendering websites closer to users. And what I mean by rendering websites is we are rendering more interactive content next to the user. So no longer are we constructing the whole webs web page, you know, 800 milliseconds away from the user and then delivering that over the wire. We are constructing it closer, meaning all the other subsequent calls that we might have to make to other services, are hopefully also calling services also closer to that edge region. So it's it's more and more, companies are putting more and more of their own services in more locations. Sure. So say, let's take an example of I wanna construct a web pay a Shopify web page. Shopify have a fantastic global API network. I argue that actually Shopify have the most distributed database on the market because actually, you know, when you update something in Shopify, it's already distributed to their network of of of re replicas around the world. If I was to construct the data there, and then, okay, I also call out to another service that I control, or I also call out to, like, another partner API. If we if we request that, make a request there, and it goes 800 milliseconds to wherever it was being constructed, and then that that does its course and it comes 800 milliseconds back, that's, you know, 600 a 1.6 second round trip or whatever. If we take that request and say put it right next to the user and go, okay. As soon as it comes in, I'm instantly streaming back. So it's close to the user, say, 20 milliseconds away. Generally, edge location is a sub 100 millisecond close to the user. Good to know. So they they hit it. Instantly, they get back, you know, they white page with loading things, and it goes, okay. Slows this. Okay. Now I need to grab some stuff from Shopify. I've done a request to Shopify back and forth. Hopefully, they're also at edge location, 20 milliseconds back and forth on my edge. I've got my Shopify content. Oh, I'm doing an AB test. I've hit my AB test service.com, and I've got back the result of what needs to happen. And, okay, I've loaded that content. And it's this it's this dynamicness that we wanna put closer and closer to the user because if we were to And it and it compounds. Yep. All of these 800 millisecond round trips are grueling, but a bunch of 20. I mean, of course, you still wanna be mindful of how many you're doing, but that's the same in any web application. But we're talking orders of magnitude quicker realistically when you're loading any modern web application. Exactly. Yep. So it's it's it's more it's more of this dynamicness because we could totally just off cache that entire page and delivered it to all the users equally. But what if one one user is logged in, one user has a basket where they, that that that we wanna also display and and render on the server? We could totally render it on the client, and this is where also this new modern trend of, you know, it was very it was perfectly fine to deliver entire huge bundle to the user and it be interactive and do API calls on the client side because the client side is also an edge location. Sure. But we pay for the upfront cost of them receiving that bundle. So if we can That's interesting. Yep. So it's that trade off still of speed. We need to deliver a a reasonable experience as quickly as possible. Then, ideally, we want all subsequent requests to be as speedy as possible. But if, you know, if this is where we're starting to pay a little bit, that can be more acceptable dependent on user and kind of, developer experience or, like, you know, vendor experience requirements. Exactly. That's really interesting. So it's just this trade off of of requirements. I feel like this, you know, edge computing starting to gain some traction is bringing a whole new realm of, I don't know what to call that requirement gathering, like, trade off decisions for developers who didn't need to care as much about infrastructure before. And now suddenly, the same way front end developers now have to do a bunch of back end work with these kind of mixed hybrid frameworks that run everywhere. I feel like in the in the same way, anyone who does back end work is now having to care about infrastructure in a way where perhaps the the expectations in the past were less than. Yep. And then it's the job of these vendors who who run edge nodes to make that experience as contain as little pain as possible. I think I think you've hit the nail right right on the head there is, we we've we've this this trend is the flip side of is is the next evolution of SPAs, of single page web apps. So that was our edge in the past, and that was our limps limited subset runtime. We could only run JavaScript, then we could only run web JavaScript in that runtime. And, what we're having now is that it's a limited run time, yes, but there's we have more languages, more things that we can run closer to the user, and it's we're delivering them. We're not using the user's resource. Right? So a a lot of SPAs or a lot of, mid 2000 in the Internet relied on, you know, fast devices or, you know, doing a lot of read writing of, you know, the DOM tree and all this, and then lower, lower quality devices with less CPU, like dumb phones or or or or basic Android devices had, you know, a worse experience on the web. And what we're trying to deliver is by doing doing the complexities of the HTML rendering and whatnot close to the user and delivering them, you know, the content that just needs to be rendered and device can become dumber again, and need to do less, you know, JavaScript execution on the client. Because, really, all we all we actually want at the end of the day is to deliver that HTML to the user. And that liveness, that interactivity used to come from or used to only be able to come from SPAs, client side JavaScript. And what we're seeing today is that we're moving more and more of that client JavaScript onto the server, but also quite close to the user to be kind of that native like experience. And that and they're the parts they're the parts. It is we want to make the users do as little computation as possible while not, suffering based on their location. The fact that a load of that computation is going to happen elsewhere. And it's that specific pairing of of requirements, that makes edge com that makes distributed computing more powerful and even more so edge computing because they run right at those interchanges. That's that's great. Now, for some of the other episodes of, learning things I love to hate, we went into a demo, but I feel like we've covered a lot here. I don't necessarily think we need to we need to do it so much. Plus, it's a really abstract concept where, you know, I'm sure there are ways, but it isn't gonna be now it's running on the edge, and we can really show that to its fullest. But I think I understand a little more why why edge computing is interesting now. Some more or less basic concepts about how it works when it also might not always be right. And this new idea that, oh, no. It's just parts of your application you can delegate to the edge. Again, not something that is spoken about by the hype machine very often. Yep. It's just edge edge edge edge all the time. Just parts. Just parts. Right? And we we we have the we have the same mistaken view of serverless computing, in the past where it was like, okay. Now everything must be serverless. Mhmm. No. It's a subset of use cases that are that that benefit from it, and that's the it's the same with the edge. It's not we don't wanna put we don't need to put the entire server close to the user. That's impractical. But elements of it definitely can. And, and, yeah, and I think the the the the the reason it's so abstract in this way is it's hard for us to experience what other people are experiencing when we're in our, you know, our our nice modern MacBook on gigabit Internet in countries with good Internet. So it's it's hard to put us put ourselves in those shoes. And, the way we used to do that with SPAs and stuff is literally have a crappy old Android phone for dev that you would, you know, every so often look at and use. And we can replicate that if try try the web on a, on a VPN every so often. Or if you're in a foreign country, try going to some of your favorite websites and just seeing how much slower they might feel. Because, yeah, it's it's conjured up a memory of a it's slightly off topic, but you've just conjured up a memory of in London, Mozilla used to have an open community space, and in that, they had what they called the Mozilla Device Lab. I imagine there were a few of them, and it was just a bunch of different devices. Or, like, they run they run browsers which are, you know, really, really, really limited with this exact idea that you could be testing on all these different devices. But, yeah, now we need to consider and I suppose that's the other part here. What edge computing enables is less computation happening on a user device, so it's lowering the needs on the user device, and it is making that computation closer. So we're kinda leveling the playing field. It's this fairness, as you mentioned earlier, of you don't need the best hardware, and it doesn't necessarily matter where in the world you are. Both of those factors are handled by this new emergent technology. Exactly. Exactly. And it's it's it's all it's all right as rain for us to talk about fairness, but at the end of the day is we want to have users around the world because we're hitting we're hitting more ability for us to sell to more users. And at the end of the day, that's really what we're trying to solve with with edge computing because get more and more users with more and more happy users because we can have users that are unhappy, but, really, we wanna have more and more happy users. Yeah. Awesome. Thank you so much for joining me. This has been really, really interesting. I'm loving filming this series. I'm learning tons. And hopefully people who have joined us for the ride also, are learning loads. Just before we go, is there anything else you wanna share, point people to where to find you? Sure. I mean, if you want if you wanna see more of, like, crazy drawings on my Twitter, I'm I'm at Pandeliszed, p a n d e l I s, zed, on Twitter. It's it's funny when I was drawing that in the office because we we kinda sit in a bit of a semicircle, and my colleagues turned around and were like, what? How are you doing? I'm like, oh, just drawing. Just drawing some stick figures. I was like, okay. Fine. But, yep, that's that's that's where my normal antics are or on Twitter. And, and yeah. No. I'm so so happy, that you had me, Kevin, and it was, I I saw your brain clicking at the end there. So I'm so glad that I could I could share that with you, and, hopefully, it's it's no longer anything that you that you hate. It is not something that I hate. I have, a newfound like other episodes, a newfound appreciation and more importantly, an understanding of the qualities. Right? Which mean that it may be something I reach for when appropriate. Whereas before, I just lacked the knowledge. So I was never gonna reach for it. And, you know, in time, that could lead to me building things that aren't as good, as they could be. So, yeah, thank you ever so much. Thank you to everyone who has joined us, and, we will see you in another episode of learning things I love to hate. Bye.",[5388,5389],"7cff1776-681e-4853-9afa-7feae1415ddb","57414d28-d5db-4707-b406-13defc07b9e7",[],{"year":2380,"number":213,"id":4680,"episodes":5392,"show":5393},[4682,4683,4684,4657],{"title":4686,"slug":4687},{"id":4167,"slug":5395,"vimeo_id":5396,"description":5397,"tile":5398,"length":2767,"resources":12,"people":5399,"episode_number":159,"published":5401,"title":5402,"video_transcript_html":5403,"video_transcript_text":5404,"content":12,"seo":12,"status":19,"episode_people":5405,"recommendations":5407,"season":5408},"saas-app","894439990","Building multi-user is a common convention when building Software as a Service. Bryant has an hour to build a todo app which manages users, teams, and ensures data can only be accessed within a team.","2c1fb560-c144-43ac-9e87-00e712be818e",[5400],{"name":3590,"url":3591},"2024-01-01","Mission: SaaS App","\u003Cp>Speaker 0: Welcome back to the next episode of 100 apps, 100 hours where we rebuild or recreate some of your favorite apps in 60 minutes or less or publicly fail trying. Sounds really scary when we say it that way. I am your host, Brian Gillespie, developer advocate at Directus, and today, we're gonna be building a multi tenant SaaS application. I realize that is not everybody's most favorite app, but it is the architecture that powers a lot of your favorite apps, and it's a question that comes up a lot within our community. So by multi tenant, what do we actually mean when we say that?\u003C\u002Fp>\n\u003Cp>It means that we have multiple different accounts or different tenants in this case, so we serve multiple different groups of users in one single application. And all of their data is separated. It's, you know, team a doesn't see the data or can't interact with the data for team b. It is totally doable on our back end as we're gonna see, and it's a popular convention for a lot of the SaaS apps that you will use so that we could share resources like a database, you know, an actual front end application without having to, you know, commission a brand new database for every single account that we have. Alright.\u003C\u002Fp>\n\u003Cp>Sounds great, sounds interesting, something that we want to build. How do we build it? What are the rules here? If you're new to the series, there are only 2 rules. We have 60 minutes to plan and build an application, no more, no less.\u003C\u002Fp>\n\u003Cp>And I do like to include a little planning ahead of time so we know, or before we actually build, not ahead of time. We're starting fresh here. You've got as much of a clean slate as I do. And then the second rule is use whatever you have at your disposal, whether that's Tailwind, online resources, AI, anything is fair game. We've got to get as far as we can in 60 minutes or less or look like an idiot.\u003C\u002Fp>\n\u003Cp>So sounds great. Everybody knows the rules. We're gonna be building the multi tenant SaaS app. Let's open this up and fire off the countdown. Here we go.\u003C\u002Fp>\n\u003Cp>So before we actually touch any code or any data models or, you know, anything in our back end, let's go through and and plan this out a little bit. And usually I like to start with our functionality. So let's shrink this down a little bit. And what functionality are we looking for out of this application? We want to create and manage tenants.\u003C\u002Fp>\n\u003Cp>In this case, let's call it, Teams is a pretty good convention. You know, accounts would be one that I see often organizations and in some applications. We wanna create and manage users within those teams. We wanna make sure that all data, make sure all data is scoped to each team. Right?\u003C\u002Fp>\n\u003Cp>So team a can access team b's data and vice versa to the team. And then, you know, a SaaS app needs to do something, so let's do just like the standard to dos. So we wanna create and manage to dos. Perfect. Alright.\u003C\u002Fp>\n\u003Cp>So there's our functionality. Nothing particularly fancy but it is tricky to set up some of this, especially around permissions. You know, that is a a big piece of the puzzle. Security is always very important. So, you know, how how do we set this up and how do we do it within an hour?\u003C\u002Fp>\n\u003Cp>Let's go through and now let's let's work on our data model a little bit. So we got our data model. And what are we gonna need as far as our tables or our different collections that we're gonna be using? So we will have, what, teams. Let's shrink this up a little bit.\u003C\u002Fp>\n\u003Cp>We will have users, we'll have to do's, and you know, we may not get to this but if I was gonna actually have this be built as a SaaS that I could charge people for, we'd probably have something like subscriptions. And we didn't include that in the functionality because I'm not sure exactly how far we can get in an hour, but we'll get to as much as possible. Right? So in this scenario, a user could be part of one team or, technically I guess we could build it so that a user can be in multiple teams. Multiple teams.\u003C\u002Fp>\n\u003Cp>Let's break that in half so we can actually see that. To do's belong to a team. To do's belong to a team assigned to a user. And then there's probably a a junction table here that is what? Users and teams.\u003C\u002Fp>\n\u003Cp>Oh, let's use something that's not connected. Right? So this is probably a table called something like teams users because it is a many to many relationship and just to get fancy and finish out our little arrows could look something like this. I'm definitely not an artist but you get the picture. Alright.\u003C\u002Fp>\n\u003Cp>So we've got teams, we've got users, we have to do's, we have team underscore users or or some of junction collection. We potentially have, subscriptions. So what do we have set up to actually build this? I'm using Directus for the back end of the application. I've got a super simple Nuxt application for the front end that we may or may not get to that and I've just got a little boiler plate that, uses the Directus SDK for that information.\u003C\u002Fp>\n\u003Cp>Let's jump right in. So if I pull up my Docker Compose and that's how I've got Directus set up locally, just through a simple Docker Compose file that you pull down from the documentation. And I'm gonna get logged in to our first user, and I'll just copy and paste this password here because I suck at typing a lot of times. Alright. So you can see that we've got a blank application.\u003C\u002Fp>\n\u003Cp>There is no data model. We've got a single admin user. We're starting from a clean slate. So if I just pull this up and we rearrange all these windows, maybe let's pull this over to the side so we can actually see what we're building, And I'll zoom in a little bit so you've got a better view. Maybe we don't put the clock in our face, so we're so we're not beating up against the clock.\u003C\u002Fp>\n\u003Cp>But how do we start? You know, let's start by creating this tenant collection. I'm gonna call this teams. You could easily call it tenants. You probably want to whatever makes the most sense in the context of your application is probably what I would use.\u003C\u002Fp>\n\u003Cp>Right? So the teams here, we're gonna generate a primary key called ID. We're just gonna use a UUID for that. As far as the status of that, yeah, we probably want a status for the team. We don't need a sort field.\u003C\u002Fp>\n\u003Cp>And then these others are just system fields that you can create, to give you a little shortcut. You know, when was it created? What was the user that created it? This specific team, blah blah blah. Alright.\u003C\u002Fp>\n\u003Cp>So as far as the team, what are we gonna need for the team information? We'll probably need a name for that team. That's great. Maybe we want to have a slug for the team. Could be a a good one to have.\u003C\u002Fp>\n\u003Cp>That way, we're not using a weird UUID, but instead a slug for the team. And let's go in and, adjust this a little bit. We're going to look for the interface section and Directus has the slugify option to make sure that slug input is URL safe. And one of the other things I'm gonna do here is just open up my little mouse pose tool. If you guys are ever wondering how I do this little highlight on the screen, that is a tool called mouse pose, very handy piece of kit.\u003C\u002Fp>\n\u003Cp>Alright so we've got our teams, now let's add our users, right? I could create a separate user collection but when I fire up Directus, it gives me a system collection for users that I can use to actually I could just reuse that for all of the authentication, all of the roles and permissions because what happens behind the scenes with Directus as my back end is it will mirror all these tables or these collections that we're creating inside the app to MySQL database, which is super handy. I I keep MySQL pure and, MySQL databases remains pure. And I've got built in REST and GraphQL APIs that are also automatically generated based on this data model. So tremendously powerful, enables you to build really quickly.\u003C\u002Fp>\n\u003Cp>So we're just gonna use that that system collection. I'm gonna I am gonna go in and create a new one. Let's just call it team users, and we'll generate a UID for that. We'll leave that one blank for now. Let's go in and add our to do's.\u003C\u002Fp>\n\u003Cp>So each to do we'll need, who's the date created, who was the user created it. We wanna give those a status as well, maybe a sort, so we can rank those in order of priority. So we've got the bare bones of our data model here, and we've got the again, we're gonna use the directus users system collection. How do we go and actually start setting up the tenancy? Right?\u003C\u002Fp>\n\u003Cp>Because right now, if anyone were to log in to this application, they would see all the data that we have available. So first, we are going to go in and create our relationships between these different collections, or which are just equivalent to tables within your Postgres database, let's first go through and do the teams and users. So a user can be in multiple teams. Let's go in and model that relationship. So we'll hit create field.\u003C\u002Fp>\n\u003Cp>We're gonna look for the many to many relationship inside Directus here, and let's just call this our users. Alright so the related collection is going to be users as well, actually it's not. It's gonna be directus_users, and that'll turn purple letting me know that that is an actual collection. And then we probably wanna show a link to the item. But instead of hitting save here, I'm going to hit continue in advanced field mode or advanced mode because I want to control what my Junction collection is named.\u003C\u002Fp>\n\u003Cp>So I'll just go in, we've already got this, it's called team underscore users, And I could go in and edit the, the foreign key fields here as well. It's just gonna be teams underscore ID, directus underscore users underscore ID. That's fine. Not a big deal. And if I wanted to, I can also add the reverse or the inverse relationship.\u003C\u002Fp>\n\u003Cp>Somebody will correct me on the comments. But, I can add that back to the Directus users collection so that I can reference that in any of our permissions that we're going to use later. We probably don't necessarily need a sort field, not a big deal. And the rest of this is pretty standard. So let's go ahead and save that.\u003C\u002Fp>\n\u003Cp>And now we have our users that are linked to, that direct us users table. So if I were to go in and create a team, I can see that I can add existing users or create new users for that team. Great. Alright. So next, let's take a look at teams and to dos.\u003C\u002Fp>\n\u003Cp>Right? So the to dos belong to a single team and I can assign those to dos to a user. So if we flesh out this data model a little bit more, we probably got a title or a name for this to do, let's use that. We probably have a description, so for that we'll just use our WYSIWYG editor inside the back end here. And I can even control what options we have as far as HTML content, but we'll just keep this the standard one for now.\u003C\u002Fp>\n\u003Cp>Alright. And then we've probably got a user that this has been assigned to. So we're gonna scroll down to our relational section. And depending on how we wanted to assign these, if we wanted to assign this task to or this to do to many users, you know, I would use either like a many to 1 or a one to many. In this case I just want to assign it to a single user.\u003C\u002Fp>\n\u003Cp>You know, whoever we assign this to is ultimately responsible for getting this thing done, and we're just gonna call this assign to. So we're going to use the many to one relationship within Directus, call it the assign to, and our related collection is going to be Directus users. So we'll hit save, that's who we assign this to. And now, let's go in and make sure this is applied to a team. So we need that relationship to a team so we can actually filter that out.\u003C\u002Fp>\n\u003Cp>Right? If team a is logged in or a user on team a is logged in, we don't want them to be able to see or interact with any of the to dos for team b, unless they are part of that team, which makes it a little more complicated to model as well. But we'll just call this, team or, you know, it could be team ID. Whatever. I'm gonna I'm gonna stick with team, and then the related collection is teams.\u003C\u002Fp>\n\u003Cp>Straightforward enough. Right? So if I were to go in now and we create a to do actually, let's just first create our team. Right? I've been talking about team a, there's the slug for team a, and team b.\u003C\u002Fp>\n\u003Cp>Cool. So now we have 2 teams, And I can go in and if I were creating a to do item, let's just say test item for team a. Here's a short description. I could assign this to a user which we'll come back and fix, but then I pick a team as well. Right?\u003C\u002Fp>\n\u003Cp>Now if I were a member of team a or team b, I wouldn't want to pick this, so we'll come back to that in a moment. But, basically, I can have a team that owns each one of these to dos. Great. So that's all set up. Right?\u003C\u002Fp>\n\u003Cp>How do we go in and manage those relationships? Because, you know, if we were to go in and create a user, let's just call it test user a for we'll we'll call it team a. Right? Team a user atexample.com. We're gonna do a real secure password here of password.\u003C\u002Fp>\n\u003Cp>And let's give them oh, the only role that we have now is administrator, so we'll sort that in a moment. But if we were to open this up and we go to team a user, so I'm in a incognito window. We'll go to password. Great. Okay.\u003C\u002Fp>\n\u003Cp>So and now you can see I could see all the teams, I can edit all the to dos. Right? So how do we solve for this problem? That is going to be using our user roles or the access control within Directus. So if we go to our access control, let's create a new role and call it user in this case.\u003C\u002Fp>\n\u003Cp>And they are going to have app access, you know, whether we enable app access or not. If I was building our front end and I I'm not sure if we're gonna have enough time to actually build the front end for this, but, you know, I could disable app access as I was using the APIs to to access this information. But we'll just call this user. And then we can go in and restrict our roles. Right?\u003C\u002Fp>\n\u003Cp>So now if I were to go in and, again, this is an incognito window on my left and the regular window on my right, so these have 2 different user sessions. But if I were to go in now to my team a user and change that from user or from administrator to user, you're gonna see that this person or this user, team a, does not see any content whatsoever and that is because we have not enabled any permissions for the user role. So we can go in and now let's start enabling some of these permissions. Right? So as far as Teams, we have create, read, update, delete, and the optional share, operation.\u003C\u002Fp>\n\u003Cp>But how do we how do we give them access to just the teams they're a part of? Right? Well, first, I need a relationship between our users and the teams, and and that's a piece that we forgot. Or did we? No.\u003C\u002Fp>\n\u003Cp>We've already fleshed that out. We've got our users there. Cool. So now I've got that relationship created, we can account for it inside our teams. Alright.\u003C\u002Fp>\n\u003Cp>So inside of permissions, let's go and we'll use if we use read access and we enable all access, what will we see? Right? Oh, that's the public permission. I guess I need to go into the user permission. Always love building against the clock.\u003C\u002Fp>\n\u003Cp>Right? You overlook some things. So here we can see all the teams now. That's not great because this user is a member of team a or at least should be. Let's make sure that we've got that set up.\u003C\u002Fp>\n\u003Cp>Right now they're a part of no team, so they can see all the teams, which is not helpful either. But let's make them a part of team a, and yet even though they're a part of team a, they can still see team a. So inside the that permissions, what we'll do, if if I actually go to the right one, we'll use the custom permission and we'll configure a rule that restricts that access. So the team, we can look and make sure that users, and I could drill into this, users dot directus user ID is one of, and we'll use a little shorthand here, current_user.id. So this is a dynamic variable within Directus that allows us to pick up the current user, so who is actually logged in, and we'll use the ID field.\u003C\u002Fp>\n\u003Cp>So this will allow us or should allow us to refresh and then we'll see that now this user can only see items or teams that are they're a part of, which is great. Right? They can't edit any of this information right now, but we could go in and restrict that as well. But just by doing this where we say our users and then we reference the user ID, which is gonna be an array in this case because we could have multiple users for one team, is one of dollar sign current underscore user dot ID. That allows us to build that permissions in.\u003C\u002Fp>\n\u003Cp>And I could even go through and restrict the specific fields that they could read about this as well if I didn't want them to have access to the slug or the other users, whatever. So now if we go into something like to dos, you know, we can enable all access for to dos, right, and we'll get the same effect where this one is assigned to a different team, they can still see that. So I think right now, this one is assigned to team a, I believe. And I could fix that as well where I go in, I go to my to dos and and instead of showing the actual ID, we wanna show the name of the team that's assigned to you. So we'll just on our display templates, we'll adjust that and go back, and we could see that, okay, this is assigned to team a.\u003C\u002Fp>\n\u003Cp>Let's create a new to do to do for team b. And again, I'm logged in as the administrator account over here. This is the individual user on the left. We'll save that. And if I were to go back to to dos now, they could still see to do for team b.\u003C\u002Fp>\n\u003Cp>Right? So how do we fix that? We go back into our access control and instead of having all access, again we use custom access. So we'll set up a rule where the team, and I could drill down and get the team dot ID, is again, we'll use is one of current user dot Teams? I can't remember what we named this, if it's team or team.\u003C\u002Fp>\n\u003Cp>Let's try team dot ID and see. Okay. So that breaks it to where they are not able to access any of it. Let's go back to our data model. We're gonna look at system collections, and we've got teams.\u003C\u002Fp>\n\u003Cp>Teams is the name of that. So if we go back, teams teams dot ID. Let's see if that sorts it out. It does not. Let's just check the syntax again.\u003C\u002Fp>\n\u003Cp>Is one of teams. Blah blah blah. Why is that not working? Team. Team team is okay.\u003C\u002Fp>\n\u003Cp>So we're hitting a snag here somewhere. Team dot ID. Let's try that. Team dotid is equal to current user dot teams is one of equals unexpected server error. So it has to be this one, Current user dot teams, or maybe it's just team.\u003C\u002Fp>\n\u003Cp>Alright. Let's take a look at our user directory. We've got our team. Teams hasn't been properly configured. Maybe something in the data model?\u003C\u002Fp>\n\u003Cp>Let's take a look. Teams is a many to many relationship. We've got team underscore users, team's ID. Oh, okay. Maybe that could be it.\u003C\u002Fp>\n\u003Cp>Team we've got to access through the junction collection, I believe. So we need to go back and, duh, we'll do current user dot team_users.directus. Nope. Team is teams_id. Team dotid.\u003C\u002Fp>\n\u003Cp>Let's try this. Team dot id is one of current user dot teams users or actually, maybe it is that teams dot ID. Still struggling. One of the the fine points of being against the clock. Right?\u003C\u002Fp>\n\u003Cp>We got 37 minutes, I'm going to cheat and not cheat, remember there are no rules, and we're just gonna reference one of the other, applications that I've built where I've referenced this before for a client role. So let's go in and look at this user dot contacts dot organizations dot ID, dot organizations dot underscore ID. So that is the collection there. That would be the organization's ID. Let's just take a look at the data model for this really quickly.\u003C\u002Fp>\n\u003Cp>Users. Okay. Trying to figure this out on a hurry. Users dot team users. No.\u003C\u002Fp>\n\u003Cp>Still really struggling here. Alright. So if we back this up, team, let's take a look at this one again one time. I just love building against the clock. Right?\u003C\u002Fp>\n\u003Cp>Let's let's just go back and evaluate what I've done. My brain is not thinking well today, I guess, so I haven't had enough coffee. So we'll go into our teams. We've got the users, so that's gonna be our junction collection. And then within the users collection or our junction table, it's gonna be we're identifying by the team's underscore ID.\u003C\u002Fp>\n\u003Cp>So thinking of it through that lens, our permission here instead of this whatever I've got here, it should just be something like this where it is the team underscore ID. So the team that's assigned to this to do, we get the ID from that team, and then we use the current user, and then we use the teams, which will be an array of objects, and then we get the teams underscore ID from that junction collection. So if I hit refresh, boom. Okay. Now we're cooking with gas.\u003C\u002Fp>\n\u003Cp>Let's just close this sidebar and continue building, right? So now I'm seeing only the to dos that have been assigned or or available to that specific team. Right? And if I want to see other users within that team, so within that junction collection, we're probably gonna need to do something similar here. So we can see users that are, where the teams dot id equals current user dot, oh, current underscore user dot teams dot teams underscore ID.\u003C\u002Fp>\n\u003Cp>So again, we're just referencing that relationship that we built and we'll probably do the same with our direct us users. Right? We don't want them to be able to see all the users. Where are you? Now I can't see any user.\u003C\u002Fp>\n\u003Cp>What's going on there? Test user for team a. Did we totally mess something up? Okay. So I could see these.\u003C\u002Fp>\n\u003Cp>We've got Let's go in and adjust the permissions for this. So we shouldn't be able to see, actually, that's gonna be on the the users collection anyway. Right? So users, we want the field permissions, the teams dot teams ID is one of current_user.teams._teams or dot teams underscore ID. Right.\u003C\u002Fp>\n\u003Cp>Great. So now we can only see users that are a part of the team that they're assigned to. Cool. Alright. So how do we actually go about creating to dos, right?\u003C\u002Fp>\n\u003Cp>This is not allowed inside this account. If we give all access, they should be able to create to dos now. But say we only we want to, restrict this team, right, or we wanna default this particular, team name, right, or team ID. What we could do is something like this where inside our to dos, we have field presets. So I could go in and say team is what, current, So we'll do this, like current underscore user dot teams dot ID.\u003C\u002Fp>\n\u003Cp>No. Dotteams_id. And maybe we get the first one, and that's how we assign. Let's see what this does just for testing. Alright.\u003C\u002Fp>\n\u003Cp>So we create a new to do. I should have access to this. Why is it saying we don't have permission access? Don't have permission to access this. Let's just try creating a test.\u003C\u002Fp>\n\u003Cp>Test to do. Hit save. Unexpected error occurred. Probably something to do with the preset that we've got saved here. Oh, I forgot the brackets.\u003C\u002Fp>\n\u003Cp>Could be it. Yeah. Still showing we do not have permission to access that. Great. So now we can go in and create this test to do.\u003C\u002Fp>\n\u003Cp>Alright. So we'll pick the team a, we'll assign that to team user a. Great. So now I can see those to dos. If we load up all the to dos in our master account or our administrator account, we could see that, I could see those to dos that are assigned for team b, but I cannot see those for team a because this user is not part of team a, right, and they cannot update that themselves.\u003C\u002Fp>\n\u003Cp>Great. So we would likewise just go through all of our different collections for our access control for the user role, and then we would just set those items. Right? So we could, whether we want this person to be able to edit Teams, yes or no. So again, I could copy this actual rule and I can get the raw value and I could use that.\u003C\u002Fp>\n\u003Cp>So we just copy paste this in. And now if I go to the team settings and I refresh, it doesn't look like this user can actually edit that. Users, direct as users is one of direct as users ID is 1 of okay. Oh, it's because there are no permissions enabled for the fields. Right?\u003C\u002Fp>\n\u003Cp>So now, with those field permissions updated, I can go in and this user can edit all of that information for that specific team. Likewise, for our to dos, we could go in and edit to dos. You know, you could set this up one of 2 ways where anybody within that team can edit this to do, or I could, you know, just let them edit to dos that we created or assigned. In this case, I'm just gonna use that same rule, copy and paste it in, and then I can go in and add all these fields that are available. Great.\u003C\u002Fp>\n\u003Cp>So we can edit those. You know, do we want to let them delete a team? Maybe we don't give them access to do that, but as far as the deleting to dos, go in and then as long as they're a member of that team, we can let them delete to dos in the account. So if I were to go here, now the user Team dot ID, that should be working. Teams dot ID.\u003C\u002Fp>\n\u003Cp>For some reason, this is not allowed for this specific user is one of current user dot Not sure why that is not available. It should be. Okay. If we give it all access, it is, But we're still logged in as that same user. So let's try to debug this a little bit, where team is one of current user dot teams, teams underscore ID.\u003C\u002Fp>\n\u003Cp>Now I can go in and delete that. Okay. So we've got that properly set up. Everything looks to be working correctly now. Where are we at, time wise?\u003C\u002Fp>\n\u003Cp>We got 26 minutes to, build out the rest of this SaaS application. Right? There's a a few other things, a few directions we could go, but as far as our functionality, we can create and manage tenants. We can create and manage users within those teams. And now we've made sure that all of our data is scoped to the individual team.\u003C\u002Fp>\n\u003Cp>And then, you know, technically they can go in and manage to do this. Right? But if I wanted to, flesh this out on the front end, like if I wanted to build an actual to do application on the front end, how would we make that work? Let let's just see how far we can get with that. So again, I've got a let me just close all the windows here.\u003C\u002Fp>\n\u003Cp>I've got this Nuxt application configured with a little bit of boilerplate already stood up just so we can take a look at how this works. But if I take a look, I've got a Directus module within this Nuxt application that is using the SDK. It does a few auto imports from the SDK and then just provides a composable called use Directus, that allows me to call the Directus SDK. You could of course call the API directly, but I like using the SDK just to standardize that across server or client runtimes. Alright.\u003C\u002Fp>\n\u003Cp>So if we take a look at our actual application, we've got nothing really fancy here. I do have an auth page. So if we go to auth\u002Flogin, I've got a a simple login form that we can use. But, let's go open up our index page and start customizing this a bit, where if I wanted to read all of the tasks or the to dos from Directus. Alright.\u003C\u002Fp>\n\u003Cp>So I could do something like this where to dos equals await, what's my composable, use directus, and then we'll say read items. So I wanna read all the items from the to do's collection. And maybe I've just got, some options that will pass to that. And then here, let's just log out those to do's. Right?\u003C\u002Fp>\n\u003Cp>Let's see what this looks like. So if I open this up, you could see that we're getting an error, that says forbidden. So what does that actually mean? That is Directus telling us that the user for this application is not logged in. So what we could do is basically let's go to our login page, and let's actually just do this as well.\u003C\u002Fp>\n\u003Cp>So we'll get the user equals use state. This is a a helper with inside Nuxt, and I need to take a look at just what I've got set up here. It's just user. Alright. So we'll go back.\u003C\u002Fp>\n\u003Cp>We'll take a look at the user equals use state. And if I were to log the current user on this page, let's see what that shows. Maybe we just wait on that. We don't see anything at all. Right?\u003C\u002Fp>\n\u003Cp>So if I open up dev tools, we take a look at Vue, the user is undefined in this case. So let's just go in and let's log in using these credentials that we set up. Alright. So we look at our test user. We've got team a user at example with a very secure password, teammateuser@example.com with a password of password.\u003C\u002Fp>\n\u003Cp>I'm gonna log in. It's trying to redirect us to the portal, but that is not a page that we have right now. But now I can actually see the user because I am logged in. And if I were to just briefly swap these out, where we're just logging to do's, now I can actually see those to do's as well. So now that we're logged in, we're getting that information from Directus.\u003C\u002Fp>\n\u003Cp>Right? So let's do 2 things here and just check the clock very quickly. We're at 22 minutes. How do we set this up? Alright.\u003C\u002Fp>\n\u003Cp>So I'm just gonna sketch these out really quickly. We're going to show a list of to dos, and then we're going to have the form show a form to add to dos. This is your standard to do list functionality. Alright. So we got the user.\u003C\u002Fp>\n\u003Cp>If use if there's no user, let's just redirect to the login page. That's a good idea. Thank you, GitHub Copilot. And that will be await navigate to \u002Fauth\u002Flogin. And, of course, if I was actually building this for real, I would take great care to modularize this and and extract some of this logic so it could be reused.\u003C\u002Fp>\n\u003Cp>You know, I'd probably stick this in a Nuxt middleware or something like that. So now if I were to go in and just delete the cookie that we're storing for the user, which is in here where it says direct us off, If I just nuke all the cookies, what's going on? Forbidden. If no user, maybe we need to wrap this up. Else, we're gonna fetch the oh, there is no argument for else.\u003C\u002Fp>\n\u003Cp>So this is still showing forbidden. Maybe we return dot navigate 2. If there is no user, just navigate dot 2. I don't understand what's going on here. If we nuke all this, should not be getting forbidden.\u003C\u002Fp>\n\u003Cp>Right? If user user dot ID, maybe. Okay. There we go. User was actually defined.\u003C\u002Fp>\n\u003Cp>There just weren't any properties within it. So we don't have a user ID. We're gonna redirect. Great. So again, if we do team a user at example.com, password, and log in.\u003C\u002Fp>\n\u003Cp>That's gonna redirect us to the portal. But if we go back to the home page, We logged in, do we not? Okay. Let's just scrap this for now. We'll go back to the home page, see what's going on.\u003C\u002Fp>\n\u003Cp>Okay. Great. So we want to show a list of to do's in this case, and we will do ul, to do list for to dos and to dos. Let's pick up the name. Then we have, let's actually wrap this in a p tag.\u003C\u002Fp>\n\u003Cp>We'll make this, what, text excel. Make it large. We'll make it bold. Great. Then we'll do a div and we have the description of that.\u003C\u002Fp>\n\u003Cp>So vhtml.todo.description. Prefer the self closing tags personally. And we'll use like the the Tailwind Pros class because I've got that set up. Here's the short description of this. Maybe we add a input type checkbox for to do.\u003C\u002Fp>\n\u003Cp>Just call it status. Alright. And let's go ahead and flex those 2. Right? So we'll wrap these again.\u003C\u002Fp>\n\u003Cp>Okay. Give it a little bit of gap and some padding and maybe a border. Let's start the items at the top. Cool. And maybe we wrap that as well.\u003C\u002Fp>\n\u003Cp>Okay. So now we got a list of to dos. How can we actually push this into our to do list? Let's add a form for rendering those to dos or how to add those to dos inside our list. So we got a form, thank you, to do dot name, to do dot description, submit button, add to do.\u003C\u002Fp>\n\u003Cp>Thank you, GitHub Copilot. Let's take a look at what we've got now. Cannot add properties of to name. That's because we don't have a to do. Right?\u003C\u002Fp>\n\u003Cp>So we'll go in. Let's add a new to do. This will just be a reactive object. And we need to change our v model from to do dot name. We'll do new to do dot name.\u003C\u002Fp>\n\u003Cp>That way we can keep everything savvy. That looks kind of rough. Not very pretty. Right? Let's change this.\u003C\u002Fp>\n\u003Cp>One of the UI libraries that I have baked into this starter is this is Nuxt UI library. Very handy little piece of kit. We got 16 minutes left. How can we show something impressive if we look at this library? Where did you go?\u003C\u002Fp>\n\u003Cp>There's like a a form group component or a form input already here. So we got a form group that gives us a label, and then we just wrap that. Okay. So let's take a look at that. We'll just open this up.\u003C\u002Fp>\n\u003Cp>We'll do new form group. We've got the name. That'll be an input. Title of the task or to do. We don't really need an icon for this, and we'll just v model out new to do dot name.\u003C\u002Fp>\n\u003Cp>And we'll do the same for description. Alright. So we'll just model this up. Description. Description of the task to complete.\u003C\u002Fp>\n\u003Cp>And we'll just do description. And then we've got, we'll just use their button component as well. Cool. Let's clean this up a little bit. Actually, let's add some padding to the whole form.\u003C\u002Fp>\n\u003Cp>So let's do something like p 8. Maybe we add a header here. I've got a built in VText component here. Let's make the size large, say task. And then we add some spacing between these items, space y 8.\u003C\u002Fp>\n\u003Cp>Alright. So we've got the form. We've got the actual task here. That's great. Alright.\u003C\u002Fp>\n\u003Cp>So this is a submit type, on our form. Maybe we wanna do at submit dot prevent, and then we're gonna build a method to submit that to do to our Directus API. So submit. Let's just call it add new to do. And let's go in and build this.\u003C\u002Fp>\n\u003Cp>Right? So we'll call this an async function, add new to do. And what is GitHub Copilot suggesting? We will, await, create item, add to dos, new to do. So we're gonna pass that.\u003C\u002Fp>\n\u003Cp>And then it is clearing out the items within that new to do so we can add a new one. This looks pretty good. Let's add one final thing. Maybe we want to wrap this in a try catch. We'll error out.\u003C\u002Fp>\n\u003Cp>And then the last thing that we probably wanna do is actually update the to do list. Right? So, one thing we can do here is wrap this function. Let's say async function, if I can actually spell, fetch to dos. Return data, get items to dos.\u003C\u002Fp>\n\u003Cp>Actually, let's just swap that. We'll return the data from that. Great. We'll log any errors. And now we're not seeing any to dos because we actually need to call that during the setup of this.\u003C\u002Fp>\n\u003Cp>So we'll call const to dos equals ref. And I could actually do something like this if we wanted to make it reactive where I just have to do's at the top here. And within this function, I could, to do's dot value, just update those to do's, which let's do that. And here, we'll say, wait. And this works because we are in we're using script setup with inside Nuxt review.\u003C\u002Fp>\n\u003Cp>So we could do fetch to do's. That will fetch the to do's when it sets up this component. And then when we add a new to do, we're going to fetch those to do's again. So if I go in and now let's test this out. New to do.\u003C\u002Fp>\n\u003Cp>Do a description here. We hit add to do. K. It did something. What did it actually do?\u003C\u002Fp>\n\u003Cp>Input types contains is not a function. No idea what we're doing here. Form at submit dot prevent is add new to do. This is a submit button. Got new to do dot name dot description.\u003C\u002Fp>\n\u003Cp>Why is this not showing? Right? Let's try try it again. Test to do. We'll look at our fetch request description, and it looks like this is okay.\u003C\u002Fp>\n\u003Cp>Post items to do's, payload status equals false. Right? What's what's the issue here? Why are we running into an error? And you could see because I've logged in as a different user in my application, Directus has also logged me out.\u003C\u002Fp>\n\u003Cp>So we'll just log back in really quickly. And again, we could see that the to dos are being created, but they are not being assigned to a team. Right? So we got 10 minutes and 30 seconds left to resolve this issue. If we look at our user, we could see that we have the team's array.\u003C\u002Fp>\n\u003Cp>Right? So we have the ID of that team. We just need to pass that along inside the request. You know, if if I did not have, like, a setup where a one user could be involved in multiple teams, you know, maybe I have a a preset within the API itself to to do that. You know, if if I've got a user that is a part of multiple teams, we have to pass that along, because it's not gonna know which team.\u003C\u002Fp>\n\u003Cp>So here inside this, we could also do something like this where we have a switch for this or, let's just pick up the team from the user. Right? So we've got the user here inside new to do, new to do dot team. Let's add the user dot, teams. It's just the first item inside the array.\u003C\u002Fp>\n\u003Cp>So something like this. Alright. Let's see. Test. Test.\u003C\u002Fp>\n\u003Cp>Test. Test. Test. Does that actually work? Looks like we're not getting any response from API team.\u003C\u002Fp>\n\u003Cp>Okay. There's the team. Okay. It's because we are the team is not an array. It's just a single value.\u003C\u002Fp>\n\u003Cp>Okay. Try it again. Test. Test. Add to do.\u003C\u002Fp>\n\u003Cp>What are we not doing? Right? So the team is actually we're not calling that data at all. New to do dot team equals user dot teams. Is that because that is a value?\u003C\u002Fp>\n\u003Cp>Let's refresh the page. Just test. To do's. Okay, it's it's not performing our post request. Cannot read values of undefined.\u003C\u002Fp>\n\u003Cp>So it's probably something like this. Let's just unref that user Dot teams 0. Test this again. To do's is a bad request. Okay.\u003C\u002Fp>\n\u003Cp>So we're passing something that Teams does not like. The team status is false. Why why is it not liking that at all? Team so if you look at our access control, let's try to diagnose this. We got the to dos.\u003C\u002Fp>\n\u003Cp>We can create to do's. We got access to create all the fields. And if we just delete some of these other ones, What's going on? Why can't we assign a to do a new to do to team a? Right?\u003C\u002Fp>\n\u003Cp>We should be able to do that. Test test test test test test. Todos is status is is false. Maybe that's it. Test.\u003C\u002Fp>\n\u003Cp>Test. It still does not like the fact. Invalid foreign key for the team. Okay. So maybe it should be an array then.\u003C\u002Fp>\n\u003Cp>Let's test again. Do not have permission to access this. Invalid foreign key for the team to dos. Why are we seeing this particular issue? Right?\u003C\u002Fp>\n\u003Cp>What are where are we at on the clock? We got 6 minutes. Let's resolve this thing and finish this one up. Right? So let's just leave the status out for now.\u003C\u002Fp>\n\u003Cp>Why are we not getting the proper response from Directus? Alright. So we go in, we look at our data model for our to dos. We've got a team. That's a mini to 1.\u003C\u002Fp>\n\u003Cp>If we just were to, like, log this out, let's say we give access to public access for all of our to dos, all the teams. And just to debug this a little bit, if I go into items, I do to dos, I could see that team is just, a single string. That's the UID for that string. And it should be one of these values, like 8bf or ending in a 69e. What are we doing?\u003C\u002Fp>\n\u003Cp>What are we passing inside that request? Test. Okay. Now I'm logged in as the the user again. Let me clear all the cookies.\u003C\u002Fp>\n\u003Cp>That could be what's going on. You know, if you're working with Directus in one window, you're using the API in another window, maybe you want to make sure that you're using incognito windows. So when you log in that you're not seeing something outrageous here. Let's clear our cookies out. Oh, and now we can see all the to do's because we are not logged in.\u003C\u002Fp>\n\u003Cp>And I could set up those permissions. So let's restrict those permissions again. We're just gonna remove all those. Great. And now we log in.\u003C\u002Fp>\n\u003Cp>We do team a user at example.com, go to password, go back a step, now we've got our to dos. Let's test. Alright. We're still getting the same thing. You don't have permission to access this.\u003C\u002Fp>\n\u003Cp>That's because it's not an array. We refresh, test, test. Again, invalid foreign key. Right? So what are we actually getting here for the team?\u003C\u002Fp>\n\u003Cp>If I look at our user, teams dot okay. So we have the user there. I don't understand why we're not able to access the value. Oh, let's add a team. We'll just give it a false string.\u003C\u002Fp>\n\u003Cp>User let's try user dot value dot teams dot 0. Maybe we just actually console log the the user. The user is right. Let's take a look at that. Alright.\u003C\u002Fp>\n\u003Cp>So we've got our to dos. We had test to do. Alright. So there's our user. Teams.\u003C\u002Fp>\n\u003Cp>Alright. Bad requests. Invalid foreign key for team in collection to dos. Something to do with passing the wrong team. So if we take a look at our teams, team a, Why are we getting the wrong value here?\u003C\u002Fp>\n\u003Cp>So team a should be this particular value. We're sending 5 e. Is that correct? 4e? No.\u003C\u002Fp>\n\u003Cp>I I don't know why we're where this value is coming from for teams for that specific user. Right? Team a user, do we add that user to a team that is not available? Team a. Oh, duh.\u003C\u002Fp>\n\u003Cp>That's because we're not actually getting the team user, we are getting the junction collection. So great idea, Brian. Let's go into our config for this and it actually should be the junction collection. So the, we need to access the team from the junction collection instead of the just the okay. Alright.\u003C\u002Fp>\n\u003Cp>So we can fix this. We'll go into our Nuxt config. Where are our fields for this? User fields, we're going to pick up the teams dot okay. So let's just refresh.\u003C\u002Fp>\n\u003Cp>Come on. Come on. Let's build. We got 30 seconds left to fix it. Come on, build, baby.\u003C\u002Fp>\n\u003Cp>Okay. So now within our function, we are going to go in and call instead of teams okay, then we're gonna use teams underscore ID. That should be it. And all these issues probably would have been prevented had we used TypeScript. But now we could see that.\u003C\u002Fp>\n\u003Cp>And with 54321 on the clock, you could see as I add to dos, boom, it is fetching those and updating them. Wow. So right at the clock, we have finished the multi tenant to do application. What would our next steps for this sort of thing be? Right?\u003C\u002Fp>\n\u003Cp>One probably hooking up subscriptions and and actually fleshing out this logic. So as far as next steps, let's just discuss those for a few. You know, add subscriptions. I can unstriker through those. We would add our subscriptions using something like Stripe.\u003C\u002Fp>\n\u003Cp>Clean up all of our data fetching, fetching and handling, and probably extract some of that logic into a middleware. But the major achievement here is being able to create that multi tenant role and permission setup within an hour, so that each user can only see their data. They can only interact with that data. So, great. You know, we took all 60 minutes on this one.\u003C\u002Fp>\n\u003Cp>The UI doesn't look fantastic, but as far as our back end, pretty much ready to go. I hope you'll join in for another episode of 100 apps 100 hours. Until then, I'll see you around.\u003C\u002Fp>","Welcome back to the next episode of 100 apps, 100 hours where we rebuild or recreate some of your favorite apps in 60 minutes or less or publicly fail trying. Sounds really scary when we say it that way. I am your host, Brian Gillespie, developer advocate at Directus, and today, we're gonna be building a multi tenant SaaS application. I realize that is not everybody's most favorite app, but it is the architecture that powers a lot of your favorite apps, and it's a question that comes up a lot within our community. So by multi tenant, what do we actually mean when we say that? It means that we have multiple different accounts or different tenants in this case, so we serve multiple different groups of users in one single application. And all of their data is separated. It's, you know, team a doesn't see the data or can't interact with the data for team b. It is totally doable on our back end as we're gonna see, and it's a popular convention for a lot of the SaaS apps that you will use so that we could share resources like a database, you know, an actual front end application without having to, you know, commission a brand new database for every single account that we have. Alright. Sounds great, sounds interesting, something that we want to build. How do we build it? What are the rules here? If you're new to the series, there are only 2 rules. We have 60 minutes to plan and build an application, no more, no less. And I do like to include a little planning ahead of time so we know, or before we actually build, not ahead of time. We're starting fresh here. You've got as much of a clean slate as I do. And then the second rule is use whatever you have at your disposal, whether that's Tailwind, online resources, AI, anything is fair game. We've got to get as far as we can in 60 minutes or less or look like an idiot. So sounds great. Everybody knows the rules. We're gonna be building the multi tenant SaaS app. Let's open this up and fire off the countdown. Here we go. So before we actually touch any code or any data models or, you know, anything in our back end, let's go through and and plan this out a little bit. And usually I like to start with our functionality. So let's shrink this down a little bit. And what functionality are we looking for out of this application? We want to create and manage tenants. In this case, let's call it, Teams is a pretty good convention. You know, accounts would be one that I see often organizations and in some applications. We wanna create and manage users within those teams. We wanna make sure that all data, make sure all data is scoped to each team. Right? So team a can access team b's data and vice versa to the team. And then, you know, a SaaS app needs to do something, so let's do just like the standard to dos. So we wanna create and manage to dos. Perfect. Alright. So there's our functionality. Nothing particularly fancy but it is tricky to set up some of this, especially around permissions. You know, that is a a big piece of the puzzle. Security is always very important. So, you know, how how do we set this up and how do we do it within an hour? Let's go through and now let's let's work on our data model a little bit. So we got our data model. And what are we gonna need as far as our tables or our different collections that we're gonna be using? So we will have, what, teams. Let's shrink this up a little bit. We will have users, we'll have to do's, and you know, we may not get to this but if I was gonna actually have this be built as a SaaS that I could charge people for, we'd probably have something like subscriptions. And we didn't include that in the functionality because I'm not sure exactly how far we can get in an hour, but we'll get to as much as possible. Right? So in this scenario, a user could be part of one team or, technically I guess we could build it so that a user can be in multiple teams. Multiple teams. Let's break that in half so we can actually see that. To do's belong to a team. To do's belong to a team assigned to a user. And then there's probably a a junction table here that is what? Users and teams. Oh, let's use something that's not connected. Right? So this is probably a table called something like teams users because it is a many to many relationship and just to get fancy and finish out our little arrows could look something like this. I'm definitely not an artist but you get the picture. Alright. So we've got teams, we've got users, we have to do's, we have team underscore users or or some of junction collection. We potentially have, subscriptions. So what do we have set up to actually build this? I'm using Directus for the back end of the application. I've got a super simple Nuxt application for the front end that we may or may not get to that and I've just got a little boiler plate that, uses the Directus SDK for that information. Let's jump right in. So if I pull up my Docker Compose and that's how I've got Directus set up locally, just through a simple Docker Compose file that you pull down from the documentation. And I'm gonna get logged in to our first user, and I'll just copy and paste this password here because I suck at typing a lot of times. Alright. So you can see that we've got a blank application. There is no data model. We've got a single admin user. We're starting from a clean slate. So if I just pull this up and we rearrange all these windows, maybe let's pull this over to the side so we can actually see what we're building, And I'll zoom in a little bit so you've got a better view. Maybe we don't put the clock in our face, so we're so we're not beating up against the clock. But how do we start? You know, let's start by creating this tenant collection. I'm gonna call this teams. You could easily call it tenants. You probably want to whatever makes the most sense in the context of your application is probably what I would use. Right? So the teams here, we're gonna generate a primary key called ID. We're just gonna use a UUID for that. As far as the status of that, yeah, we probably want a status for the team. We don't need a sort field. And then these others are just system fields that you can create, to give you a little shortcut. You know, when was it created? What was the user that created it? This specific team, blah blah blah. Alright. So as far as the team, what are we gonna need for the team information? We'll probably need a name for that team. That's great. Maybe we want to have a slug for the team. Could be a a good one to have. That way, we're not using a weird UUID, but instead a slug for the team. And let's go in and, adjust this a little bit. We're going to look for the interface section and Directus has the slugify option to make sure that slug input is URL safe. And one of the other things I'm gonna do here is just open up my little mouse pose tool. If you guys are ever wondering how I do this little highlight on the screen, that is a tool called mouse pose, very handy piece of kit. Alright so we've got our teams, now let's add our users, right? I could create a separate user collection but when I fire up Directus, it gives me a system collection for users that I can use to actually I could just reuse that for all of the authentication, all of the roles and permissions because what happens behind the scenes with Directus as my back end is it will mirror all these tables or these collections that we're creating inside the app to MySQL database, which is super handy. I I keep MySQL pure and, MySQL databases remains pure. And I've got built in REST and GraphQL APIs that are also automatically generated based on this data model. So tremendously powerful, enables you to build really quickly. So we're just gonna use that that system collection. I'm gonna I am gonna go in and create a new one. Let's just call it team users, and we'll generate a UID for that. We'll leave that one blank for now. Let's go in and add our to do's. So each to do we'll need, who's the date created, who was the user created it. We wanna give those a status as well, maybe a sort, so we can rank those in order of priority. So we've got the bare bones of our data model here, and we've got the again, we're gonna use the directus users system collection. How do we go and actually start setting up the tenancy? Right? Because right now, if anyone were to log in to this application, they would see all the data that we have available. So first, we are going to go in and create our relationships between these different collections, or which are just equivalent to tables within your Postgres database, let's first go through and do the teams and users. So a user can be in multiple teams. Let's go in and model that relationship. So we'll hit create field. We're gonna look for the many to many relationship inside Directus here, and let's just call this our users. Alright so the related collection is going to be users as well, actually it's not. It's gonna be directus_users, and that'll turn purple letting me know that that is an actual collection. And then we probably wanna show a link to the item. But instead of hitting save here, I'm going to hit continue in advanced field mode or advanced mode because I want to control what my Junction collection is named. So I'll just go in, we've already got this, it's called team underscore users, And I could go in and edit the, the foreign key fields here as well. It's just gonna be teams underscore ID, directus underscore users underscore ID. That's fine. Not a big deal. And if I wanted to, I can also add the reverse or the inverse relationship. Somebody will correct me on the comments. But, I can add that back to the Directus users collection so that I can reference that in any of our permissions that we're going to use later. We probably don't necessarily need a sort field, not a big deal. And the rest of this is pretty standard. So let's go ahead and save that. And now we have our users that are linked to, that direct us users table. So if I were to go in and create a team, I can see that I can add existing users or create new users for that team. Great. Alright. So next, let's take a look at teams and to dos. Right? So the to dos belong to a single team and I can assign those to dos to a user. So if we flesh out this data model a little bit more, we probably got a title or a name for this to do, let's use that. We probably have a description, so for that we'll just use our WYSIWYG editor inside the back end here. And I can even control what options we have as far as HTML content, but we'll just keep this the standard one for now. Alright. And then we've probably got a user that this has been assigned to. So we're gonna scroll down to our relational section. And depending on how we wanted to assign these, if we wanted to assign this task to or this to do to many users, you know, I would use either like a many to 1 or a one to many. In this case I just want to assign it to a single user. You know, whoever we assign this to is ultimately responsible for getting this thing done, and we're just gonna call this assign to. So we're going to use the many to one relationship within Directus, call it the assign to, and our related collection is going to be Directus users. So we'll hit save, that's who we assign this to. And now, let's go in and make sure this is applied to a team. So we need that relationship to a team so we can actually filter that out. Right? If team a is logged in or a user on team a is logged in, we don't want them to be able to see or interact with any of the to dos for team b, unless they are part of that team, which makes it a little more complicated to model as well. But we'll just call this, team or, you know, it could be team ID. Whatever. I'm gonna I'm gonna stick with team, and then the related collection is teams. Straightforward enough. Right? So if I were to go in now and we create a to do actually, let's just first create our team. Right? I've been talking about team a, there's the slug for team a, and team b. Cool. So now we have 2 teams, And I can go in and if I were creating a to do item, let's just say test item for team a. Here's a short description. I could assign this to a user which we'll come back and fix, but then I pick a team as well. Right? Now if I were a member of team a or team b, I wouldn't want to pick this, so we'll come back to that in a moment. But, basically, I can have a team that owns each one of these to dos. Great. So that's all set up. Right? How do we go in and manage those relationships? Because, you know, if we were to go in and create a user, let's just call it test user a for we'll we'll call it team a. Right? Team a user atexample.com. We're gonna do a real secure password here of password. And let's give them oh, the only role that we have now is administrator, so we'll sort that in a moment. But if we were to open this up and we go to team a user, so I'm in a incognito window. We'll go to password. Great. Okay. So and now you can see I could see all the teams, I can edit all the to dos. Right? So how do we solve for this problem? That is going to be using our user roles or the access control within Directus. So if we go to our access control, let's create a new role and call it user in this case. And they are going to have app access, you know, whether we enable app access or not. If I was building our front end and I I'm not sure if we're gonna have enough time to actually build the front end for this, but, you know, I could disable app access as I was using the APIs to to access this information. But we'll just call this user. And then we can go in and restrict our roles. Right? So now if I were to go in and, again, this is an incognito window on my left and the regular window on my right, so these have 2 different user sessions. But if I were to go in now to my team a user and change that from user or from administrator to user, you're gonna see that this person or this user, team a, does not see any content whatsoever and that is because we have not enabled any permissions for the user role. So we can go in and now let's start enabling some of these permissions. Right? So as far as Teams, we have create, read, update, delete, and the optional share, operation. But how do we how do we give them access to just the teams they're a part of? Right? Well, first, I need a relationship between our users and the teams, and and that's a piece that we forgot. Or did we? No. We've already fleshed that out. We've got our users there. Cool. So now I've got that relationship created, we can account for it inside our teams. Alright. So inside of permissions, let's go and we'll use if we use read access and we enable all access, what will we see? Right? Oh, that's the public permission. I guess I need to go into the user permission. Always love building against the clock. Right? You overlook some things. So here we can see all the teams now. That's not great because this user is a member of team a or at least should be. Let's make sure that we've got that set up. Right now they're a part of no team, so they can see all the teams, which is not helpful either. But let's make them a part of team a, and yet even though they're a part of team a, they can still see team a. So inside the that permissions, what we'll do, if if I actually go to the right one, we'll use the custom permission and we'll configure a rule that restricts that access. So the team, we can look and make sure that users, and I could drill into this, users dot directus user ID is one of, and we'll use a little shorthand here, current_user.id. So this is a dynamic variable within Directus that allows us to pick up the current user, so who is actually logged in, and we'll use the ID field. So this will allow us or should allow us to refresh and then we'll see that now this user can only see items or teams that are they're a part of, which is great. Right? They can't edit any of this information right now, but we could go in and restrict that as well. But just by doing this where we say our users and then we reference the user ID, which is gonna be an array in this case because we could have multiple users for one team, is one of dollar sign current underscore user dot ID. That allows us to build that permissions in. And I could even go through and restrict the specific fields that they could read about this as well if I didn't want them to have access to the slug or the other users, whatever. So now if we go into something like to dos, you know, we can enable all access for to dos, right, and we'll get the same effect where this one is assigned to a different team, they can still see that. So I think right now, this one is assigned to team a, I believe. And I could fix that as well where I go in, I go to my to dos and and instead of showing the actual ID, we wanna show the name of the team that's assigned to you. So we'll just on our display templates, we'll adjust that and go back, and we could see that, okay, this is assigned to team a. Let's create a new to do to do for team b. And again, I'm logged in as the administrator account over here. This is the individual user on the left. We'll save that. And if I were to go back to to dos now, they could still see to do for team b. Right? So how do we fix that? We go back into our access control and instead of having all access, again we use custom access. So we'll set up a rule where the team, and I could drill down and get the team dot ID, is again, we'll use is one of current user dot Teams? I can't remember what we named this, if it's team or team. Let's try team dot ID and see. Okay. So that breaks it to where they are not able to access any of it. Let's go back to our data model. We're gonna look at system collections, and we've got teams. Teams is the name of that. So if we go back, teams teams dot ID. Let's see if that sorts it out. It does not. Let's just check the syntax again. Is one of teams. Blah blah blah. Why is that not working? Team. Team team is okay. So we're hitting a snag here somewhere. Team dot ID. Let's try that. Team dotid is equal to current user dot teams is one of equals unexpected server error. So it has to be this one, Current user dot teams, or maybe it's just team. Alright. Let's take a look at our user directory. We've got our team. Teams hasn't been properly configured. Maybe something in the data model? Let's take a look. Teams is a many to many relationship. We've got team underscore users, team's ID. Oh, okay. Maybe that could be it. Team we've got to access through the junction collection, I believe. So we need to go back and, duh, we'll do current user dot team_users.directus. Nope. Team is teams_id. Team dotid. Let's try this. Team dot id is one of current user dot teams users or actually, maybe it is that teams dot ID. Still struggling. One of the the fine points of being against the clock. Right? We got 37 minutes, I'm going to cheat and not cheat, remember there are no rules, and we're just gonna reference one of the other, applications that I've built where I've referenced this before for a client role. So let's go in and look at this user dot contacts dot organizations dot ID, dot organizations dot underscore ID. So that is the collection there. That would be the organization's ID. Let's just take a look at the data model for this really quickly. Users. Okay. Trying to figure this out on a hurry. Users dot team users. No. Still really struggling here. Alright. So if we back this up, team, let's take a look at this one again one time. I just love building against the clock. Right? Let's let's just go back and evaluate what I've done. My brain is not thinking well today, I guess, so I haven't had enough coffee. So we'll go into our teams. We've got the users, so that's gonna be our junction collection. And then within the users collection or our junction table, it's gonna be we're identifying by the team's underscore ID. So thinking of it through that lens, our permission here instead of this whatever I've got here, it should just be something like this where it is the team underscore ID. So the team that's assigned to this to do, we get the ID from that team, and then we use the current user, and then we use the teams, which will be an array of objects, and then we get the teams underscore ID from that junction collection. So if I hit refresh, boom. Okay. Now we're cooking with gas. Let's just close this sidebar and continue building, right? So now I'm seeing only the to dos that have been assigned or or available to that specific team. Right? And if I want to see other users within that team, so within that junction collection, we're probably gonna need to do something similar here. So we can see users that are, where the teams dot id equals current user dot, oh, current underscore user dot teams dot teams underscore ID. So again, we're just referencing that relationship that we built and we'll probably do the same with our direct us users. Right? We don't want them to be able to see all the users. Where are you? Now I can't see any user. What's going on there? Test user for team a. Did we totally mess something up? Okay. So I could see these. We've got Let's go in and adjust the permissions for this. So we shouldn't be able to see, actually, that's gonna be on the the users collection anyway. Right? So users, we want the field permissions, the teams dot teams ID is one of current_user.teams._teams or dot teams underscore ID. Right. Great. So now we can only see users that are a part of the team that they're assigned to. Cool. Alright. So how do we actually go about creating to dos, right? This is not allowed inside this account. If we give all access, they should be able to create to dos now. But say we only we want to, restrict this team, right, or we wanna default this particular, team name, right, or team ID. What we could do is something like this where inside our to dos, we have field presets. So I could go in and say team is what, current, So we'll do this, like current underscore user dot teams dot ID. No. Dotteams_id. And maybe we get the first one, and that's how we assign. Let's see what this does just for testing. Alright. So we create a new to do. I should have access to this. Why is it saying we don't have permission access? Don't have permission to access this. Let's just try creating a test. Test to do. Hit save. Unexpected error occurred. Probably something to do with the preset that we've got saved here. Oh, I forgot the brackets. Could be it. Yeah. Still showing we do not have permission to access that. Great. So now we can go in and create this test to do. Alright. So we'll pick the team a, we'll assign that to team user a. Great. So now I can see those to dos. If we load up all the to dos in our master account or our administrator account, we could see that, I could see those to dos that are assigned for team b, but I cannot see those for team a because this user is not part of team a, right, and they cannot update that themselves. Great. So we would likewise just go through all of our different collections for our access control for the user role, and then we would just set those items. Right? So we could, whether we want this person to be able to edit Teams, yes or no. So again, I could copy this actual rule and I can get the raw value and I could use that. So we just copy paste this in. And now if I go to the team settings and I refresh, it doesn't look like this user can actually edit that. Users, direct as users is one of direct as users ID is 1 of okay. Oh, it's because there are no permissions enabled for the fields. Right? So now, with those field permissions updated, I can go in and this user can edit all of that information for that specific team. Likewise, for our to dos, we could go in and edit to dos. You know, you could set this up one of 2 ways where anybody within that team can edit this to do, or I could, you know, just let them edit to dos that we created or assigned. In this case, I'm just gonna use that same rule, copy and paste it in, and then I can go in and add all these fields that are available. Great. So we can edit those. You know, do we want to let them delete a team? Maybe we don't give them access to do that, but as far as the deleting to dos, go in and then as long as they're a member of that team, we can let them delete to dos in the account. So if I were to go here, now the user Team dot ID, that should be working. Teams dot ID. For some reason, this is not allowed for this specific user is one of current user dot Not sure why that is not available. It should be. Okay. If we give it all access, it is, But we're still logged in as that same user. So let's try to debug this a little bit, where team is one of current user dot teams, teams underscore ID. Now I can go in and delete that. Okay. So we've got that properly set up. Everything looks to be working correctly now. Where are we at, time wise? We got 26 minutes to, build out the rest of this SaaS application. Right? There's a a few other things, a few directions we could go, but as far as our functionality, we can create and manage tenants. We can create and manage users within those teams. And now we've made sure that all of our data is scoped to the individual team. And then, you know, technically they can go in and manage to do this. Right? But if I wanted to, flesh this out on the front end, like if I wanted to build an actual to do application on the front end, how would we make that work? Let let's just see how far we can get with that. So again, I've got a let me just close all the windows here. I've got this Nuxt application configured with a little bit of boilerplate already stood up just so we can take a look at how this works. But if I take a look, I've got a Directus module within this Nuxt application that is using the SDK. It does a few auto imports from the SDK and then just provides a composable called use Directus, that allows me to call the Directus SDK. You could of course call the API directly, but I like using the SDK just to standardize that across server or client runtimes. Alright. So if we take a look at our actual application, we've got nothing really fancy here. I do have an auth page. So if we go to auth\u002Flogin, I've got a a simple login form that we can use. But, let's go open up our index page and start customizing this a bit, where if I wanted to read all of the tasks or the to dos from Directus. Alright. So I could do something like this where to dos equals await, what's my composable, use directus, and then we'll say read items. So I wanna read all the items from the to do's collection. And maybe I've just got, some options that will pass to that. And then here, let's just log out those to do's. Right? Let's see what this looks like. So if I open this up, you could see that we're getting an error, that says forbidden. So what does that actually mean? That is Directus telling us that the user for this application is not logged in. So what we could do is basically let's go to our login page, and let's actually just do this as well. So we'll get the user equals use state. This is a a helper with inside Nuxt, and I need to take a look at just what I've got set up here. It's just user. Alright. So we'll go back. We'll take a look at the user equals use state. And if I were to log the current user on this page, let's see what that shows. Maybe we just wait on that. We don't see anything at all. Right? So if I open up dev tools, we take a look at Vue, the user is undefined in this case. So let's just go in and let's log in using these credentials that we set up. Alright. So we look at our test user. We've got team a user at example with a very secure password, teammateuser@example.com with a password of password. I'm gonna log in. It's trying to redirect us to the portal, but that is not a page that we have right now. But now I can actually see the user because I am logged in. And if I were to just briefly swap these out, where we're just logging to do's, now I can actually see those to do's as well. So now that we're logged in, we're getting that information from Directus. Right? So let's do 2 things here and just check the clock very quickly. We're at 22 minutes. How do we set this up? Alright. So I'm just gonna sketch these out really quickly. We're going to show a list of to dos, and then we're going to have the form show a form to add to dos. This is your standard to do list functionality. Alright. So we got the user. If use if there's no user, let's just redirect to the login page. That's a good idea. Thank you, GitHub Copilot. And that will be await navigate to \u002Fauth\u002Flogin. And, of course, if I was actually building this for real, I would take great care to modularize this and and extract some of this logic so it could be reused. You know, I'd probably stick this in a Nuxt middleware or something like that. So now if I were to go in and just delete the cookie that we're storing for the user, which is in here where it says direct us off, If I just nuke all the cookies, what's going on? Forbidden. If no user, maybe we need to wrap this up. Else, we're gonna fetch the oh, there is no argument for else. So this is still showing forbidden. Maybe we return dot navigate 2. If there is no user, just navigate dot 2. I don't understand what's going on here. If we nuke all this, should not be getting forbidden. Right? If user user dot ID, maybe. Okay. There we go. User was actually defined. There just weren't any properties within it. So we don't have a user ID. We're gonna redirect. Great. So again, if we do team a user at example.com, password, and log in. That's gonna redirect us to the portal. But if we go back to the home page, We logged in, do we not? Okay. Let's just scrap this for now. We'll go back to the home page, see what's going on. Okay. Great. So we want to show a list of to do's in this case, and we will do ul, to do list for to dos and to dos. Let's pick up the name. Then we have, let's actually wrap this in a p tag. We'll make this, what, text excel. Make it large. We'll make it bold. Great. Then we'll do a div and we have the description of that. So vhtml.todo.description. Prefer the self closing tags personally. And we'll use like the the Tailwind Pros class because I've got that set up. Here's the short description of this. Maybe we add a input type checkbox for to do. Just call it status. Alright. And let's go ahead and flex those 2. Right? So we'll wrap these again. Okay. Give it a little bit of gap and some padding and maybe a border. Let's start the items at the top. Cool. And maybe we wrap that as well. Okay. So now we got a list of to dos. How can we actually push this into our to do list? Let's add a form for rendering those to dos or how to add those to dos inside our list. So we got a form, thank you, to do dot name, to do dot description, submit button, add to do. Thank you, GitHub Copilot. Let's take a look at what we've got now. Cannot add properties of to name. That's because we don't have a to do. Right? So we'll go in. Let's add a new to do. This will just be a reactive object. And we need to change our v model from to do dot name. We'll do new to do dot name. That way we can keep everything savvy. That looks kind of rough. Not very pretty. Right? Let's change this. One of the UI libraries that I have baked into this starter is this is Nuxt UI library. Very handy little piece of kit. We got 16 minutes left. How can we show something impressive if we look at this library? Where did you go? There's like a a form group component or a form input already here. So we got a form group that gives us a label, and then we just wrap that. Okay. So let's take a look at that. We'll just open this up. We'll do new form group. We've got the name. That'll be an input. Title of the task or to do. We don't really need an icon for this, and we'll just v model out new to do dot name. And we'll do the same for description. Alright. So we'll just model this up. Description. Description of the task to complete. And we'll just do description. And then we've got, we'll just use their button component as well. Cool. Let's clean this up a little bit. Actually, let's add some padding to the whole form. So let's do something like p 8. Maybe we add a header here. I've got a built in VText component here. Let's make the size large, say task. And then we add some spacing between these items, space y 8. Alright. So we've got the form. We've got the actual task here. That's great. Alright. So this is a submit type, on our form. Maybe we wanna do at submit dot prevent, and then we're gonna build a method to submit that to do to our Directus API. So submit. Let's just call it add new to do. And let's go in and build this. Right? So we'll call this an async function, add new to do. And what is GitHub Copilot suggesting? We will, await, create item, add to dos, new to do. So we're gonna pass that. And then it is clearing out the items within that new to do so we can add a new one. This looks pretty good. Let's add one final thing. Maybe we want to wrap this in a try catch. We'll error out. And then the last thing that we probably wanna do is actually update the to do list. Right? So, one thing we can do here is wrap this function. Let's say async function, if I can actually spell, fetch to dos. Return data, get items to dos. Actually, let's just swap that. We'll return the data from that. Great. We'll log any errors. And now we're not seeing any to dos because we actually need to call that during the setup of this. So we'll call const to dos equals ref. And I could actually do something like this if we wanted to make it reactive where I just have to do's at the top here. And within this function, I could, to do's dot value, just update those to do's, which let's do that. And here, we'll say, wait. And this works because we are in we're using script setup with inside Nuxt review. So we could do fetch to do's. That will fetch the to do's when it sets up this component. And then when we add a new to do, we're going to fetch those to do's again. So if I go in and now let's test this out. New to do. Do a description here. We hit add to do. K. It did something. What did it actually do? Input types contains is not a function. No idea what we're doing here. Form at submit dot prevent is add new to do. This is a submit button. Got new to do dot name dot description. Why is this not showing? Right? Let's try try it again. Test to do. We'll look at our fetch request description, and it looks like this is okay. Post items to do's, payload status equals false. Right? What's what's the issue here? Why are we running into an error? And you could see because I've logged in as a different user in my application, Directus has also logged me out. So we'll just log back in really quickly. And again, we could see that the to dos are being created, but they are not being assigned to a team. Right? So we got 10 minutes and 30 seconds left to resolve this issue. If we look at our user, we could see that we have the team's array. Right? So we have the ID of that team. We just need to pass that along inside the request. You know, if if I did not have, like, a setup where a one user could be involved in multiple teams, you know, maybe I have a a preset within the API itself to to do that. You know, if if I've got a user that is a part of multiple teams, we have to pass that along, because it's not gonna know which team. So here inside this, we could also do something like this where we have a switch for this or, let's just pick up the team from the user. Right? So we've got the user here inside new to do, new to do dot team. Let's add the user dot, teams. It's just the first item inside the array. So something like this. Alright. Let's see. Test. Test. Test. Test. Test. Does that actually work? Looks like we're not getting any response from API team. Okay. There's the team. Okay. It's because we are the team is not an array. It's just a single value. Okay. Try it again. Test. Test. Add to do. What are we not doing? Right? So the team is actually we're not calling that data at all. New to do dot team equals user dot teams. Is that because that is a value? Let's refresh the page. Just test. To do's. Okay, it's it's not performing our post request. Cannot read values of undefined. So it's probably something like this. Let's just unref that user Dot teams 0. Test this again. To do's is a bad request. Okay. So we're passing something that Teams does not like. The team status is false. Why why is it not liking that at all? Team so if you look at our access control, let's try to diagnose this. We got the to dos. We can create to do's. We got access to create all the fields. And if we just delete some of these other ones, What's going on? Why can't we assign a to do a new to do to team a? Right? We should be able to do that. Test test test test test test. Todos is status is is false. Maybe that's it. Test. Test. It still does not like the fact. Invalid foreign key for the team. Okay. So maybe it should be an array then. Let's test again. Do not have permission to access this. Invalid foreign key for the team to dos. Why are we seeing this particular issue? Right? What are where are we at on the clock? We got 6 minutes. Let's resolve this thing and finish this one up. Right? So let's just leave the status out for now. Why are we not getting the proper response from Directus? Alright. So we go in, we look at our data model for our to dos. We've got a team. That's a mini to 1. If we just were to, like, log this out, let's say we give access to public access for all of our to dos, all the teams. And just to debug this a little bit, if I go into items, I do to dos, I could see that team is just, a single string. That's the UID for that string. And it should be one of these values, like 8bf or ending in a 69e. What are we doing? What are we passing inside that request? Test. Okay. Now I'm logged in as the the user again. Let me clear all the cookies. That could be what's going on. You know, if you're working with Directus in one window, you're using the API in another window, maybe you want to make sure that you're using incognito windows. So when you log in that you're not seeing something outrageous here. Let's clear our cookies out. Oh, and now we can see all the to do's because we are not logged in. And I could set up those permissions. So let's restrict those permissions again. We're just gonna remove all those. Great. And now we log in. We do team a user at example.com, go to password, go back a step, now we've got our to dos. Let's test. Alright. We're still getting the same thing. You don't have permission to access this. That's because it's not an array. We refresh, test, test. Again, invalid foreign key. Right? So what are we actually getting here for the team? If I look at our user, teams dot okay. So we have the user there. I don't understand why we're not able to access the value. Oh, let's add a team. We'll just give it a false string. User let's try user dot value dot teams dot 0. Maybe we just actually console log the the user. The user is right. Let's take a look at that. Alright. So we've got our to dos. We had test to do. Alright. So there's our user. Teams. Alright. Bad requests. Invalid foreign key for team in collection to dos. Something to do with passing the wrong team. So if we take a look at our teams, team a, Why are we getting the wrong value here? So team a should be this particular value. We're sending 5 e. Is that correct? 4e? No. I I don't know why we're where this value is coming from for teams for that specific user. Right? Team a user, do we add that user to a team that is not available? Team a. Oh, duh. That's because we're not actually getting the team user, we are getting the junction collection. So great idea, Brian. Let's go into our config for this and it actually should be the junction collection. So the, we need to access the team from the junction collection instead of the just the okay. Alright. So we can fix this. We'll go into our Nuxt config. Where are our fields for this? User fields, we're going to pick up the teams dot okay. So let's just refresh. Come on. Come on. Let's build. We got 30 seconds left to fix it. Come on, build, baby. Okay. So now within our function, we are going to go in and call instead of teams okay, then we're gonna use teams underscore ID. That should be it. And all these issues probably would have been prevented had we used TypeScript. But now we could see that. And with 54321 on the clock, you could see as I add to dos, boom, it is fetching those and updating them. Wow. So right at the clock, we have finished the multi tenant to do application. What would our next steps for this sort of thing be? Right? One probably hooking up subscriptions and and actually fleshing out this logic. So as far as next steps, let's just discuss those for a few. You know, add subscriptions. I can unstriker through those. We would add our subscriptions using something like Stripe. Clean up all of our data fetching, fetching and handling, and probably extract some of that logic into a middleware. But the major achievement here is being able to create that multi tenant role and permission setup within an hour, so that each user can only see their data. They can only interact with that data. So, great. You know, we took all 60 minutes on this one. The UI doesn't look fantastic, but as far as our back end, pretty much ready to go. I hope you'll join in for another episode of 100 apps 100 hours. Until then, I'll see you around.",[5406],"1f07407a-d65f-4c6e-a092-4c02958b0c88",[],{"year":2380,"number":177,"id":4162,"episodes":5409,"show":5410},[4164,4165,4166,4167,4168,4169,4170,4171,4172,4173,4137],{"title":322,"slug":323},{"id":4164,"slug":5412,"vimeo_id":5413,"description":5414,"tile":5415,"length":1156,"resources":5416,"people":5420,"episode_number":13,"published":5422,"title":5423,"video_transcript_html":5424,"video_transcript_text":5425,"content":12,"seo":5426,"status":19,"episode_people":5427,"recommendations":5429,"season":5433},"expenses-system","893800625","Expensify is an expense management tool that tracks receipts, rolls them up into business reports and documents, and handles the approval workflow. Bryant has one hour to build it (or die trying).","48dba4a7-4bf0-45df-9b3d-4de5ffa8b98a",[5417],{"name":5418,"url":5419},"Mindee OCR API","https:\u002F\u002Fwww.mindee.com\u002F",[5421],{"name":3590,"url":3591},"2023-12-22","Mission: Expensify Clone","\u003Cp>Bryant Gillespie: Hi. Bryant here. Welcome to the inaugural episode of 100 apps in 100 hours, where I will take some of your favorite apps and try to recreate them in under 1 hour or die trying. Hopefully not dying, but you get the idea. And first on our list today is Expensify.\u003C\u002Fp>\n\u003Cp>So we're going to build an Expensify clone. Expensify is expense management. And let's actually I'm not really sure I like the word clone here. Let's call this, a tribute, like the Tenacious d song. This is just a tribute because it would be very difficult to build all the functionality of Expensify in 1 hour.\u003C\u002Fp>\n\u003Cp>Obviously, they've built an entire business and they probably have thousands of developers, but let's get as close as we can. So when I think about Expensify, let's just go to their website, I think of the the first thing I think of is scanning the receipt, taking a picture of it, and it does the automagic stuff, where it automatically fills out expense reports for me, which, to be honest, is kind of a pain in the rear anyway. So, that is one thing that we're going to be concerned with, the let's just map this out. Automagic Receipt Management. We're going to have expense reports.\u003C\u002Fp>\n\u003Cp>Expense reports. So we wanna roll up all of those expenses, and, some of those are gonna be billable, And all those may be reimbursable, so we need to track for that. What else? We want to be able to approve expense reports. Seems like a good list for getting started.\u003C\u002Fp>\n\u003Cp>Alright, so I'm gonna pull up my handy tool of choice, Directus. So Directus, for those of you who aren't familiar with it, is a back end toolkit that allows you to build pretty much anything or everything, and get instant APIs on top of any SQL database. That's the pitch. I swear that's it. Let's actually run the clock, and I'm late on the clock here, but we'll start the clock 60 minutes on the timer.\u003C\u002Fp>\n\u003Cp>Let's go. We're gonna hop into Directus, and I am in my data model settings right now. And I'll zoom way in so you guys can actually see this alright. First thing we're gonna do is let's spec out our expenses. So let's just dive into our data model.\u003C\u002Fp>\n\u003Cp>We're gonna have expenses as one of our collections, so Directus is gonna create this table for us. And when we go to the optional system fields, we'll probably have a status. Let's go with the sort for the order on the expense report and we'll just select all these system fields because they have some presets that are useful to us. Alright, so we've got expenses, and I'm just gonna use a Expensify attribute. We'll create a folder here for this.\u003C\u002Fp>\n\u003Cp>We even give it like a nice green color. Boom. We're gonna organize all that inside our Expensify attribute. Okay. So the next thing, we are going to have an expense report.\u003C\u002Fp>\n\u003Cp>Right? We've got to roll up all those expenses somewhere. This is where we'll do it. Okay. Great.\u003C\u002Fp>\n\u003Cp>Okay. What else are we going to need? Let's just get started with this functionality. Alright. So, we go back to Expensify and let's try to figure out exactly what their data model looks like.\u003C\u002Fp>\n\u003Cp>My favorite way of doing that, short of interacting with the app is searching for their API. So we'll just go to the Expensify API, we'll go to their API reference, we'll just zoom way in on this, Expensify integration server, all you need to integrate. Alright. So request formats, we see what's going on here. Let's look for read and get policy lists.\u003C\u002Fp>\n\u003Cp>Maybe create. Okay. I see here's the expense creator. Alright. So, okay, it looks like here's a definition of the expense object.\u003C\u002Fp>\n\u003Cp>So reading through this, we've got a merchant, we've got the date of the expense, we've got the amount incense, we've got the currency, that's an integer, that's probably on purpose, three letter code of the expense, external ID, there's a category, there's a tag, is it billable, is it reimbursable, Comments, reports, IDs, tax. Okay. Straightforward enough, let's dive in. We'll go to our expenses and first, let's add our merchant. So we'll just create a new field inside our data model for Directus.\u003C\u002Fp>\n\u003Cp>This is just going to be a string type and clear enough, we are going to require value. We've got to have somebody that we got this receipt from. Let's change that to half width. And now let's move on to the amount. So let's do an input field.\u003C\u002Fp>\n\u003Cp>Now I could do this as an integer in cents. What I'm going to do instead is just use a decimal for this. So we'll do a decimal, I could change the scale to 2 points and, looks good. Alright. So now we've got the amount for the expense, we need to add a date for the expense.\u003C\u002Fp>\n\u003Cp>Expense date, let's just call that. So we'll choose the date time here and I'm gonna pick the time stamp as the type just because that will respect any time zones. I'm in the US. I use the 24 hour format. Or let's not use the 24 hour format since I'm in the US.\u003C\u002Fp>\n\u003Cp>No need to include the seconds. That's great. So we got our merchant. We got the amount. We got the expense date.\u003C\u002Fp>\n\u003Cp>Let's just do the name of the expense. Is that inside Expensify? Let's take a look. Created amounts, optional tag. Yeah.\u003C\u002Fp>\n\u003Cp>I'm just gonna give a name for the expense. Maybe we have some notes for that. Great. We'll add the text area field for that. We don't need any formatting.\u003C\u002Fp>\n\u003Cp>Great. Merchant amount, let's do a little cleanup on this. Directus makes it so easy to build apps that look great for your users. Alright. What else do we have?\u003C\u002Fp>\n\u003Cp>An expense comment. Got that. So then we have things like, is this billable? Is this reimbursable? So these are going to be toggles inside Directus.\u003C\u002Fp>\n\u003Cp>That's the name of the interface as we call it. Let's do is billable. I like to prefix those with is, just keeps things organized. Default value will be false, and looks good. Maybe we want to embellish this a little bit.\u003C\u002Fp>\n\u003Cp>So if you go into the advanced mode inside Directus, we can add notes for our users. Is this expense can we invoice this to a client? Great. Alright. Enabled.\u003C\u002Fp>\n\u003Cp>Let's just call it billable for the label. That seems a little better. The display is where it will show out show up in some of the different layouts inside Directus when you're looking at a list of records. Yeah. I don't see any need to change this.\u003C\u002Fp>\n\u003Cp>Great. Alright. We'll shrink that to half width. And I'm just gonna duplicate this one, and you can see how easy it is to build this out. Is reimbursable.\u003C\u002Fp>\n\u003Cp>Directus makes a a lot of this quick and painless. So we're gonna keep this on the expense collection. Is reimbursable. Yes. This is a reimbursable expense.\u003C\u002Fp>\n\u003Cp>We've got the user that created this, but, I wonder if there's a a use case where I have to expenses that somebody else submitted because they didn't do their job. So let's just add a owner, an expender. What is the the proper term for that? Buyer? Let's just call it an owner.\u003C\u002Fp>\n\u003Cp>Expense owner makes sense to me. That's what we'll call it for now. That is going to be our directus users collection. So that'll be the users of our app. Great.\u003C\u002Fp>\n\u003Cp>That's what we'll call it. That's the hardest thing in development is actually naming things. Okay. So this looks pretty good. This is a good start.\u003C\u002Fp>\n\u003Cp>What else are we missing for this? We've got a category, we've got the tags, Name of the tags. Yeah. We could we could do that. Let's let's build in a little more functionality.\u003C\u002Fp>\n\u003Cp>So we'll do tags. Maybe I wanna do tags and categories as a separate collection, and actually build those into the relationships. So category to me, there's only one single category that an expense can be in. Tags obviously could be many. So here we are going to use a many to 1.\u003C\u002Fp>\n\u003Cp>So this one, expense belongs to a single category. That's what we're gonna call it. Let's call it expense category. Great. And for our related collection, let's also do expense categories as the name of that.\u003C\u002Fp>\n\u003Cp>Now, I could click Save and Directus will do some magic for me, or I can open up advanced mode and go in and actually look at the details of this relationship. So if we look, we can see we've got this collection, we've got an expense category field, and Directus is telling me that, hey, we're gonna create a new collection called expense categories and we're gonna use the ID field of that to populate. Alright. The mini to 1, I could control the display template, but for now I don't have any other fields because that, collection is yet to be created. But we'll go in and go ahead and create this field here.\u003C\u002Fp>\n\u003Cp>And if I were to go back to my data model as well, we'll see that Directus created that collection for us. So now we've got our expense category. Let's go in and do tags as well. So tags are going to be a many to many relationship inside Directus. One tag could be applied to many different expenses, and many expenses could have many different tags.\u003C\u002Fp>\n\u003Cp>Great. You didn't know we were getting into data model expertise today, but let's name this thing. What are we gonna call it? We're just gonna call it tags. Right?\u003C\u002Fp>\n\u003Cp>We're going to create a related collection called expense tags and what else do we need to do? Show a link to the item so we can edit that tag as needed. Great. Save. Alright.\u003C\u002Fp>\n\u003Cp>So now we've got the basis of our expenses. Let's go in and flesh out these other ones that we just created. For expense categories, we're probably gonna have a name for that expense category. And, you know, maybe we got, like, an account number for our profit and loss, account name. Cool.\u003C\u002Fp>\n\u003Cp>Alright. And then for our tags, let's go in and add name and maybe we want to create a color for our tags. So I'm just gonna search for the color field here inside Directus. We'll give each tag a color. I could go through and preset these.\u003C\u002Fp>\n\u003Cp>If you leave this blank, Directus will actually create, give you a few of those presets that look nice anyway. Alright. So always gotta do my proper formatting. This is kind of a speed run, but one of the other things that I am a little OCD about, honestly, is cleaning things up and having a nice row of or nice columns of icons inside the app. So you can see a website project I've got working on here, but, you know, for now let's just roll with this.\u003C\u002Fp>\n\u003Cp>So we've got our tags, we've got our categories. The last thing that we need to do, if we open up one of our expense reports, which we have no data in that. We need to figure that one out. But we need a link back to our expense reports. So let's go into the data model.\u003C\u002Fp>\n\u003Cp>We'll go to expense reports. And this is really nice. We just recently added this search feature inside the data model, which is great, especially when you have hundreds of collections. So we'll go into our expense report. We're going to have a name.\u003C\u002Fp>\n\u003Cp>Do we even need a name? Yeah. We'll give it a name of the expense report. This could be a title. Bryant's second quarter expenses, blah blah blah, etcetera.\u003C\u002Fp>\n\u003Cp>Alright. Then we have a owner or could be like submitted by, you know, that's probably better but we stuck with owner on the other one so we'll just roll with that. For our related collection, we're gonna do direct us users. And you could pick these from a drop down over here on the right just by clicking those, but if you'll notice when I hit on one of the actual collections inside Directus, this turns purple just to let me know I'm doing the right thing here. So we got the owner of this expense report, yeah, I don't like that terminology, but I I don't wanna backtrack at this point.\u003C\u002Fp>\n\u003Cp>Date created. Alright. What else do we have? Then we're gonna need the actual expenses on this. So in this case, we've got a one to many relationship.\u003C\u002Fp>\n\u003Cp>So one expense report has many expenses. We're gonna call the key for this expenses, that's what it's gonna be stored as a column in the database as, and then we're going to pick our expenses as the related collection. And for the foreign key, we don't have one that exists already, but Directus will create one for us, which is nice. We're just gonna call it expense report because there is a single report it is linked to. And then I could go in and use the let's change this to a table view.\u003C\u002Fp>\n\u003Cp>We could see the merchant. Maybe we want to add the expense date, the amount, and, maybe the category. So here when it's a related collection I can just pick up the the related fields as well. Great, enable searching and filtering, hit save and it's probably good enough for now, right? Let's dive in and actually start creating some expense reports.\u003C\u002Fp>\n\u003Cp>So, I've got a new expense report. Brent's 4th quarter, it's not even the 4th quarter, 3rd quarter, let's just call it September expenses. Expenses. Great. Alright.\u003C\u002Fp>\n\u003Cp>So we go in, I'm gonna be the owner of this. I am not a user at this point, so I'll just use mister hop along. And now we can go in and add some of our expenses. Alright. So we've got Starbucks.\u003C\u002Fp>\n\u003Cp>The expense date is going to be this morning at, what, 7:30. I'm in West Virginia of all places, so we don't have a a ton of coffee options here. I know some people are gonna be mad that we've got Starbucks in here. That was expensive. So let's say 2102 has the amount.\u003C\u002Fp>\n\u003Cp>Coffee Needed that real bad. Alright. Is this billable? Probably not. Is this reimbursable?\u003C\u002Fp>\n\u003Cp>You know, we could have this where I could select this, but one of the other things you could do inside Directus is preset this to a default setting for, like, the the current user, for example. So for now, I'll just pre we'll pick hops along. We don't have any expense categories. I wanna say this would probably fall under meals and entertainment or just meals. No, you wanna divide those out.\u003C\u002Fp>\n\u003Cp>I hate doing bookkeeping, so accounting account number, I wanna say it's like the 3 or 4 100. Mills Entertainment. Great. Cool. Keeping track of all that.\u003C\u002Fp>\n\u003Cp>Tags. What are we gonna tag this as? Coffee would be the name of it. I don't really like any of these. Maybe we can pick, yeah let's just go with red for coffee.\u003C\u002Fp>\n\u003Cp>That's great. Cool. Alright. So we've got our information here. We've added our first expense.\u003C\u002Fp>\n\u003Cp>Pretty great. Cool. We've got our expense report. Yeah. The next thing maybe we want to fire off this expense report to somebody on our team.\u003C\u002Fp>\n\u003Cp>How are we doing on time? We're at 43 minutes, so that took about, 18, 20 minutes to flesh out this. How can we send this expense report to a team member, for approval? Well, first of all, let's do an expense report. Let's add an approved by.\u003C\u002Fp>\n\u003Cp>So we wanna track who this was approved by. That's gonna be a directus users, so it's gonna be one of our users. Great. And then maybe an approval date, Right. So we'll do a date timestamp, date approved.\u003C\u002Fp>\n\u003Cp>Great. No 24 hour format. So now we got 2 fields to track our approvals. Let's go in and use the flows inside Directus to send this report off or send a link to somebody to this report. So send expense report for approval.\u003C\u002Fp>\n\u003Cp>Great. And then we can even customize this. Let's do send. Yeah. There we go.\u003C\u002Fp>\n\u003Cp>Looks great. Scheduled send. We'll make this orange just so it stands out. Alright, so now I've got a list of different triggers. I want to actually trigger this one manually.\u003C\u002Fp>\n\u003Cp>So Directus gives us the option within a specific collection on the layout page or the detail page to send this off. So we will go in and look for expense reports. Okay. There's my expense report. Great.\u003C\u002Fp>\n\u003Cp>And then I could choose whether this is asynchronous. So, you know, do I wanna do this in the background? What are the locations where I can trigger this? So we wanna do on the collections and items pages. That's fine.\u003C\u002Fp>\n\u003Cp>We'll do both of those. If you are on the collection page, this will require a selection. We we do need you to pick this to actually send this off. And then next I'm gonna do this require confirmation because I get the ability to add different fields to the system, to prop for those fields when I send it. So I am going to add a new field for approver.\u003C\u002Fp>\n\u003Cp>So who do we need to send this? Approver. Who needs to approve this report. Great. Now the type I'm gonna pick here is gonna be JSON because we have a special little interface called the collection item drop down where I can pick a value from this and store it in JSON.\u003C\u002Fp>\n\u003Cp>So here again, we're gonna do directus users, and I'm gonna pick the user that I want to approve this report. And I could go in and as far as customizing the display template as well. You know, let's do the avatar, first name, last name. Great. And then maybe we even wanna add a note.\u003C\u002Fp>\n\u003Cp>Note. Close note to approver and that'll be just a simple string. No big deal. Great. Got the field width, let's make it full width.\u003C\u002Fp>\n\u003Cp>Actually, let's do a text area for that. Just a quick note. Great. Okay. So now that we've got that, what I'm gonna do, I'm gonna save this and I just wanna get a look at what it's gonna look like when I'm on my expense report.\u003C\u002Fp>\n\u003Cp>So over here on the right hand side, you can see Send Expense Report for Approval. I can select items or if I'm inside this expense report, I can click expense report, and then I get this confirmation dialogue of who needs to approve this, Matt. My note to Matt is going to be, hey, I need more budget for these videos. Great. Run flow on current item and let's just see what that came up with.\u003C\u002Fp>\n\u003Cp>So inside my flows, I can go in and check my logs and I could see what was actually sent down the pipe. So here's the trigger, here's the body of the payload, and this is actually what we received. This is what we will use to fire off that message. Alright. So this looks great.\u003C\u002Fp>\n\u003Cp>What I'm going to do, I'm just gonna copy this information, this is what I need, and I open up Versus Code. Let's start with a new window here. Where are you? New window. New file.\u003C\u002Fp>\n\u003Cp>Alright. So this is our trigger data. Great. Perfect. Okay.\u003C\u002Fp>\n\u003Cp>So now let's build the steps of our flow. We wanna send this expense report for approval, but we need an email to send it to, and we do not have that data here. So we need to actually get that data. So that's gonna be our next step. Get approval approver email is what we'll call it.\u003C\u002Fp>\n\u003Cp>And we will click read items from the database. The permissions from the trigger should be fine. I can also change this to full access if this is gonna be, triggered from somewhere other than inside the app. And for the collection we are going to use the directus users, but you can see this is not coming up in the search because it's a system collection. So I'm just gonna go in and hit edit raw value and we will do directus underscore users.\u003C\u002Fp>\n\u003Cp>That's our collection. Now for the IDs, we are going to use our little squarely bracket mustache syntax and we are gonna pick up the ID from this approver key that we selected. Alright, so, how do I do split screen here? Fancy fancy. Alright.\u003C\u002Fp>\n\u003Cp>So this is gonna be located at, so we use our brackets, mustache, we're gonna do dollar sign trigger. Trigger is the only one that has a special syntax. All the other keys of your operations get appended under, the key that you set here without the dollar sign. That's one of the big things that I see a lot of people get tripped up on. And it's gonna be dot body dot approver.key.\u003C\u002Fp>\n\u003Cp>Great. That's gonna be our ID. Do we need a query for this? I don't think so because we're picking up the key there. Let's hit save and then next we want to, I'm I'm just gonna save this.\u003C\u002Fp>\n\u003Cp>Let's run that one more time. So we'll go back to our expense reports. I'll pick Matt here. Hey, dude. We'll go back to our flow, and this is a good way of building flow so I can actually see the details of what we've got going on.\u003C\u002Fp>\n\u003Cp>Now I can see an issue right away. This for this demo anyway. This is gonna send to matt@example.com, which is not a real email address. So I'm just gonna quickly change this to one of my own emails just for demonstration purposes and to prove that we can actually build this app this quickly. Alright, we'll go back to our flows.\u003C\u002Fp>\n\u003Cp>Now let's actually send that email, and again when working with flows it's helpful to have like a code editor open, but, this case is fairly straightforward. We are going to send an email. We'll choose the send email operation and for the addresses we're gonna use that squirrely mustache syntax again. This is going to be get, this is where my memory struggles. This is called git approver email.\u003C\u002Fp>\n\u003Cp>So the 2 is gonna be git_approveremail, and that's gonna be the user object. So we're just gonna do email. I have to hit enter here to save this. We could also add additional emails if I wanted to if I wanted to cc myself. Let's call this expense reports.\u003C\u002Fp>\n\u003Cp>And the type here could be WYSIWYG. You can even choose a template if you are using our custom extensions, but let's go in do this as a WYSIWYG will be fine and we're going to do the trigger. So we we just want to pick up the content from our notes, which is gonna be body dot note. Should be Great. Alright.\u003C\u002Fp>\n\u003Cp>So this should populate that message into our WYSIWYG editor when we send this email out. Fingers crossed we're gonna save this. Let's test it out and see. How we doing on time? Alright.\u003C\u002Fp>\n\u003Cp>So we got about 32 minutes left, maybe a little less than that because I was, failing on throwing together the or I was failing to do the the countdown correctly. But, let's give this a shot. We'll go in, we'll send the expense report for approval, and we're going to send it to Rabat Miner. I need some more lattes. Alright.\u003C\u002Fp>\n\u003Cp>So we fire that off. Let's go back in and check our logs just to make sure. And it looks like the email went out. I'm just gonna open up my email inbox and voila, it it sent this out. That's great, that's all good, but, where's the actual expense report?\u003C\u002Fp>\n\u003Cp>There's no link. So let's go back in and what I'm gonna do, I'm just gonna capture the URL for this from my directus admin. Great. And then we're gonna go into our flow. We'll go back to that email and we just wanna give Matt like a button or a link or something here.\u003C\u002Fp>\n\u003Cp>So we're gonna do view and approve report. We'll enter in this email and we're gonna use our bracket mustache syntax and we'll do the trigger dot body dot keys 0. So the first item of the keys array is what we want to include. Cool. Great.\u003C\u002Fp>\n\u003Cp>Gravy. Save. Save. Alright. So now let's go back in.\u003C\u002Fp>\n\u003Cp>We'll send this expense report one last time, one more time with a link, hopefully. Fingers crossed, right? Alright, so now we wait and boom, we have a link. We click on the link, it logs us in and, Matt should be logging in to update the status on this. So we may want to go in and update the statuses for these.\u003C\u002Fp>\n\u003Cp>Let's do our expense reports. Let's change the status, draft. We'll do submitted for 1. Submitted. So submitted for approval.\u003C\u002Fp>\n\u003Cp>Approved. Maybe not approved is 1. Rejected. Let's call it rejected. That'll that'll be fun.\u003C\u002Fp>\n\u003Cp>That's a little harsh for an expense report, like, hey, your expense report was rejected. But, yeah, no worries. Oh, I goofed that up, didn't I? Should've saved before I started slamming away on the keyboard. Submitted.\u003C\u002Fp>\n\u003Cp>Alright. We'll just clean this up. Draft. Okay. Approved.\u003C\u002Fp>\n\u003Cp>Rejected. Alright. Let's just show the raw value for now or maybe a formatted value. That's fine. We'll do like a border, make it fancy looking, allow other values, no, allow no selection, no, for the interface though maybe it could be interesting if we do radio buttons.\u003C\u002Fp>\n\u003Cp>Great. Alright. Cool. So now we go in. Matt can approve this expense report and save it.\u003C\u002Fp>\n\u003Cp>But as you can see, I'm logged in as myself. I could potentially approve this expense report myself, which is is not cool. So we could use the roles and permissions inside Directus for that, where I could go in and say a given role like if we just had one called team member for example, I could go in and for our expense reports, I could go in and give custom permissions for this so that I cannot edit a certain field like the status. So this would prevent that user from actually, I'm sorry. I did that backwards.\u003C\u002Fp>\n\u003Cp>I would check all the other fields like the that user can update everything else about this expense report except the status or any other items that were that I needed. And likewise, one of the other things that I could do here is when I create a report, if we use the custom permissions for this, you could go in and define the presets where you can say, hey the current user is the default for this particular field. Sounds great. Alright. That is basic functionality of this.\u003C\u002Fp>\n\u003Cp>We've got the ability to add expenses, submit expense reports. You know, you might wanna try and tag into some other system to pay out these expenses, but for now I could stroke a check for those, I guess. But that leads us back to, boom boom boom, the automagic receipt management. So this part is a bit scary scary to me. I don't know how we're gonna do this in 26 minutes.\u003C\u002Fp>\n\u003Cp>Actually, I'm lying to you. I do know, what we're going to try because I've already cheated a little bit. Let's get that clock out of the way. I'm gonna make this full screen again. I have found this other service called Mindy Mindy?\u003C\u002Fp>\n\u003Cp>Mindy? Not sure what it's called, but it takes the receipts OCR, optical character recognition magic on them and apparently spits back out some data. Now, for the sake of time, all I've done is logged into this app and, set up an account. I haven't done anything else with it, so you're almost as fresh as I am at this point. I did see just a quick little tour tooltip that told me this is the API that I want.\u003C\u002Fp>\n\u003Cp>So let's take a look at at how we could do this. Anytime I upload an expense that has an image attached to it, and Directus is mobile friendly, so I could potentially log into this from my phone and update these expenses as well, just to to prove it to you there. Anytime I update one of those expenses, it has an image attached to it. We wanna send this off to this service and hopefully, I'd, like, not have to deal with actually entering in all the separate fields. Great.\u003C\u002Fp>\n\u003Cp>So first thing we need to do is, we got our expenses, let me close this one. We need to add an image field to upload the expense. So we'll go into our expenses. Let's add a image, receipt image or receipt file or we could just call it file. No need to get fancy with it.\u003C\u002Fp>\n\u003Cp>Alright. So now we've got the ability to upload a file. Let's start building a flow and then we'll incorporate this other service. So we've got, automagic COCR. Cool.\u003C\u002Fp>\n\u003Cp>Alright. So we'll do that on a vinthook. Let's do a action non blocking, so we don't wanna block the thread. Anytime a new item is created inside expenses, and we'll start there. Right?\u003C\u002Fp>\n\u003Cp>Alright. So let's go into, let's actually go back to our expenses. Let's just call this a new expense. Actually, we're not gonna do any of that. All I wanna do here and maybe we move this file up to the start actually.\u003C\u002Fp>\n\u003Cp>Let's move our file way up here. Alright. So the ideal scenario here is all I have to do create a new expense, upload an image of that, and then this service and Directus would do the rest for me. So let's just Google receipt images. Let's get a receipt image, see if we can find one that's pretty gnarly as well.\u003C\u002Fp>\n\u003Cp>What's this guy? Tesco Metro. Yeah. This one looks pretty beat up. This will be a good test, right?\u003C\u002Fp>\n\u003Cp>Save this image. Where did I go? Too many different tabs going on. So here's our Tesco shopping receipt. We hit save.\u003C\u002Fp>\n\u003Cp>Merchant value can't be null. That was a problem of mine, maybe I should go in and remove that, but you could set that up where it wasn't required. But now, if we take a look at our flow, that should have triggered our flow, we've got a payload, we can see here's the file that we're going to potentially pick up. Great. One of the other things that I wanna make sure, because we're gonna have to send this file to that service, is I'm gonna check our system collection and make sure that direct us files has public read permissions, and I could drill down to where, you know, only, files within a certain folder are available if I wanted to get very specific for this.\u003C\u002Fp>\n\u003Cp>Let's just leave it as is. Again, this is an Inexpanify Tribute, not a clone. Great. Alright, so now we've got our data coming from our new expense, we are going to wanna pass that to the mindd API. So let's see the API here.\u003C\u002Fp>\n\u003Cp>Alright. Load some stuff up for me. Alright. If I was smart I would have gone in here and cheated a little bit more than what I'm doing now. Alright.\u003C\u002Fp>\n\u003Cp>So I don't have an API key. How do I create an API key? API keys. Alright. We'll create an API key.\u003C\u002Fp>\n\u003Cp>We're gonna call this Directus. Great. Copy that API key. Don't you guys steal my API key? So So we'll go back to the API, go back to our documentation.\u003C\u002Fp>\n\u003Cp>Here's everything we're gonna need. Now is a good time to do the split screen stuff with, Arc. Great browser, still struggling, like 6 months on to figure this thing out. Alright, give myself a little more room. So we got the post request.\u003C\u002Fp>\n\u003Cp>We are going to use the send to, let's just call it OCR receipt. We are going to use the webhook request URL. We're gonna do a post request as it says. Here is our URL that we're gonna pick up. Hopefully, we could just copy that.\u003C\u002Fp>\n\u003Cp>Savvy. Of course it takes that post request. This looks okay. We got our headers, we're gonna add authorization. It's gonna be a bearer token.\u003C\u002Fp>\n\u003Cp>Raycast, great app as well, by the way. We'll paste in our API key and then the content type. Content type. Multipartformdata. K.\u003C\u002Fp>\n\u003Cp>Let's look for, like, a curl example. Document is path to your file. Okay. So it looks like we've got a document key and I'll pick up oh, the other downside of working locally. That might be the death of this particular project.\u003C\u002Fp>\n\u003Cp>Right? This is on my local computer. I've got 18 minutes. It's not going to be able to access that image. Let's do some more cheating.\u003C\u002Fp>\n\u003Cp>Right? Go back to our receipt images. I got the image address. Don't be mad at me for cheating on this. Alright.\u003C\u002Fp>\n\u003Cp>Let's take a look at that. We got a document. Here's the public URL of an image. Document can either be a file object URL or base 64. That should be savvy.\u003C\u002Fp>\n\u003Cp>Next time I'll have to do this on a standard cloud instance, not one of my local copies. Alright. So we got this. We're gonna take the webhook request. Let's see if they give us like a response what that response looks like.\u003C\u002Fp>\n\u003Cp>Date, confidence, document type, line items, locale, supplier address. I tell you what, let's just send this off and see what we come back with. Alright, so we're just going to pretend and actually this is gonna be correct because it's the same receipt but just a little cheating involved. We'll add a merchant name for this, We've sent this off to our auto magic receipt. It says unauthorized.\u003C\u002Fp>\n\u003Cp>Use token. What did I forget? Oh, token. Let's see. I've forgotten something.\u003C\u002Fp>\n\u003Cp>API reference authorization token. It's not bearer token. It's just token. Great. Let's go back and try her again.\u003C\u002Fp>\n\u003Cp>Keep editing. Don't forget to save your changes, kids. Alright. We'll create a new expense. Use the same receipt we've already uploaded so we don't duplicate it, and I really should remove that requirement.\u003C\u002Fp>\n\u003Cp>Go to our flows, See what we got. Bad request. Missing boundary and multipartform data. Alright. How are we gonna structure this, right?\u003C\u002Fp>\n\u003Cp>Missing boundary and multi part form data. Do we actually need the content type? Can we try that? See if we can get away with it. Otherwise, I think I'm gonna have to reformat everything.\u003C\u002Fp>\n\u003Cp>Alright. So we go to our flows. Let's take a look. Logs. Hey.\u003C\u002Fp>\n\u003Cp>There we go. Alright. So we've got our prediction. And we probably don't have to use form data because we're not passing an actual document. Cool.\u003C\u002Fp>\n\u003Cp>So we've got our document. This is a giant string, or a giant JSON object. Tons of stuff. Taxes, the value, supplier phone number, prediction, document type, expense receipt. Here's our line items.\u003C\u002Fp>\n\u003Cp>We just need our amount, right, and dates. So I'm just gonna copy this whole thing. Let's open up Versus Code. Do this and we'll look for date. Okay.\u003C\u002Fp>\n\u003Cp>So there we go. We've got our date. It'd be great if there was a way to copy that path. Right? Let's close, let's separate these, do this, do a little side by side action going with Versus Code.\u003C\u002Fp>\n\u003Cp>Alright, so now we need to update this item. How we doing on time? We've got 13 minutes. Let's get it done. So now we're gonna update our expense with the data coming from this API.\u003C\u002Fp>\n\u003Cp>And I may wanna do something like this where we say format data or I could just directly update that data. Anyway, so we'll go into update data, let's say update expense. We are going to use the permissions from the trigger, this is gonna be an expense. The IDs are gonna be coming from trigger dot body, dot keys dot 0, I believe. We'll take a guess and the payload that we want, we got the merchants.\u003C\u002Fp>\n\u003Cp>We wanna pass that. We want the expense date, I believe. The amount of the expense, and I think that would be a win. So let's look for the merchant, supplier, supplier address, supplier name. Alright.\u003C\u002Fp>\n\u003Cp>Let's start with the date. Right? So how do we get a hold of this guy? We've got our data. Alright.\u003C\u002Fp>\n\u003Cp>Let's just save this. That is our OCR receipt. That's our key. And of course because this is an invalid payload, it did not save. Alright.\u003C\u002Fp>\n\u003Cp>So our date expense date is going to be blah blah blah. We've got, OCR receipt dot data dot documents dot inference. I love that these guys are saying all this, dot pages dot prediction, debugging this is gonna be a nightmare, dot date dot value. That is deeply nested if I've ever seen it. Great.\u003C\u002Fp>\n\u003Cp>Alright. So next we've got the merchant. Let's try it. That is gonna be something probably similar, and I already see a typo. Prediction.\u003C\u002Fp>\n\u003Cp>Okay. So we'll just copy paste that. Alright. And that's gonna be called what? The supplier supplier name value prediction dot supplier underscore name dot value.\u003C\u002Fp>\n\u003Cp>Okay. Great. And then last but not least should be the amount. And we are closing in on 10 minutes on the clock, hopefully this thing works out. Would hate to start off on a loss.\u003C\u002Fp>\n\u003Cp>Alright. So the amount let's look for the amount. Total amount is what we're looking for. Point 99 confidence, that's great. That's what we're looking for.\u003C\u002Fp>\n\u003Cp>Got a lot of confidence in this. We got the total underscore amount dot value. Alright. So there we go. We've got our JSON.\u003C\u002Fp>\n\u003Cp>I'm just gonna copy it just to verify, update. Let's try it one more time. Let's delete these failed attempts. Alright. Tesco shopping receipt.\u003C\u002Fp>\n\u003Cp>We're gonna have to enter that in. Hit save and automatic receipt OCR. I don't have permission to access this because I don't have the proper key. So I didn't pass the key. I got the payload wrong as well.\u003C\u002Fp>\n\u003Cp>What did we get wrong here? Dot data, OCR receipt dot data dot document. Maybe it's just document. Maybe we can omit data. OCR receipt.\u003C\u002Fp>\n\u003Cp>And I also need to fix the keys because I didn't get those correct either. So it's actually just key. That's what's coming from the trigger, the expense. So body trigger dot body dot key. Alright.\u003C\u002Fp>\n\u003Cp>Fingers crossed. We'll run this again. This works this time. Let's hope so. Do our nice little t here.\u003C\u002Fp>\n\u003Cp>Boom boom boom. You don't have permission to access this. Still says undefined. So we're getting this back from the API. That's great.\u003C\u002Fp>\n\u003Cp>Trigger dot body dot key. The key should be correct, though. Payload trigger dot payload oh, no. This is an event hook. I'm sorry.\u003C\u002Fp>\n\u003Cp>So I've got it wrong. Should just be trigger dot key. No body. It's fine. Trigger dot key.\u003C\u002Fp>\n\u003Cp>That part's good. We are not good here, though. So let's do this. Let's disconnect this. Let's add a step.\u003C\u002Fp>\n\u003Cp>You just log this to the console. Log. And we are going to do get OCR receipt is what we're calling it. We're just gonna console log OCR receipt. Log OCR receipt.\u003C\u002Fp>\n\u003Cp>See what we're getting past. Alright. We'll drag that down here for now. We are running out of time for like a fun playing beat the clock. We'll hit save this.\u003C\u002Fp>\n\u003Cp>Let's go take a look. What do we got for him, Johnny? Log to the console, message dot data. Okay. So do we have to include message dot data?\u003C\u002Fp>\n\u003Cp>Worth a shot. Let's see. With 5 minutes and 40 seconds on the clock, Wasting precious time. So we have message dot data Message dot data dot documents. I'll just copy, paste.\u003C\u002Fp>\n\u003Cp>Alright. It's either gonna be a big finish or a sad ending. Not sure. We'll try it out and see. Alright.\u003C\u002Fp>\n\u003Cp>So we've got that same image, we're running it one more time. Flows do us nicely error update expenses set where invalid input syntax for type numeric. Still not getting the correct stuff coming from the app here. What in the world is going on? Alright.\u003C\u002Fp>\n\u003Cp>Let's bring in the big guns here. What are we what am I doing wrong? Logging to the console. Alright. Let's try picking up the data that we want.\u003C\u002Fp>\n\u003Cp>Let's go with, run scripts. Where are you? Format, data. Alright. So we're gonna get the data from receipt equals OCR data dot OCR receipt.\u003C\u002Fp>\n\u003Cp>Okay. And let's just return receipt for now. Let's string this together. Let's see if I could just get something going here. Format data.\u003C\u002Fp>\n\u003Cp>What's going on? It's gonna be a sprint to the finish. To to to log to the console. I can't even get my, so we'll drag that. Let's log into the console.\u003C\u002Fp>\n\u003Cp>Let's see what comes out of this. Expense, save, format data, flows, auto magic. Alright. So we got the run script, status. Okay.\u003C\u002Fp>\n\u003Cp>So we got data. We wanna pick up the data dot document. Right? I swear that's right. Data.ocrdoc equals receipt dot data dot document.\u003C\u002Fp>\n\u003Cp>Let's return the dock. We'll just keep drilling down. We got 2 minutes left. Let's see how this is gonna play out. Save t.\u003C\u002Fp>\n\u003Cp>Save it. Flows. If I was smart, I would have had multiple options. Okay. So we got this.\u003C\u002Fp>\n\u003Cp>Let's get down to our inferencing. Oh, I see what was wrong now. Duh. Pages is an array. That's where we were going wrong the entire time.\u003C\u002Fp>\n\u003Cp>Pages 0. Don't you love that? Message. It's not message. The message is coming from that console log.\u003C\u002Fp>\n\u003Cp>Alright. So with 1 minute and 21 seconds on the clock, let's save this and just see what comes up as the clock is ticking down. T. Now, if I hit save and stay it should be doing that for me, but it did not. However, did it?\u003C\u002Fp>\n\u003Cp>Did it actually do it? Expenses. There it is. Boom. With 49 seconds on the clock remaining, we have populated the receipt information from a third party API.\u003C\u002Fp>\n\u003Cp>Therefore, fulfilling our requirements for this, That is a wrap on the first edition of 100 Apps in 100 Hours. I feel like we need like a little Tiger Woods just, yeah, finish up strong. So if you enjoyed this series, or this episode of this series, stay tuned. We've got more coming like this. Would love for you to hop into our Directus Discord community and send us your feedback on what apps you would like to see us build next with our set of tools that we have here at Directus.\u003C\u002Fp>","Hi. Brian here. Welcome to the inaugural episode of 100 apps in 100 hours, where I will take some of your favorite apps and try to recreate them in under 1 hour or die trying. Hopefully not dying, but you get the idea. And first on our list today is Expensify. So we're going to build an Expensify clone. Expensify is expense management. And let's actually I'm not really sure I like the word clone here. Let's call this, a tribute, like the Tenacious d song. This is just a tribute because it would be very difficult to build all the functionality of Expensify in 1 hour. Obviously, they've built an entire business and they probably have thousands of developers, but let's get as close as we can. So when I think about Expensify, let's just go to their website, I think of the the first thing I think of is scanning the receipt, taking a picture of it, and it does the automagic stuff, where it automatically fills out expense reports for me, which, to be honest, is kind of a pain in the rear anyway. So, that is one thing that we're going to be concerned with, the let's just map this out. Automagic Receipt Management. We're going to have expense reports. Expense reports. So we wanna roll up all of those expenses, and, some of those are gonna be billable, And all those may be reimbursable, so we need to track for that. What else? We want to be able to approve expense reports. Seems like a good list for getting started. Alright, so I'm gonna pull up my handy tool of choice, Directus. So Directus, for those of you who aren't familiar with it, is a back end toolkit that allows you to build pretty much anything or everything, and get instant APIs on top of any SQL database. That's the pitch. I swear that's it. Let's actually run the clock, and I'm late on the clock here, but we'll start the clock 60 minutes on the timer. Let's go. We're gonna hop into Directus, and I am in my data model settings right now. And I'll zoom way in so you guys can actually see this alright. First thing we're gonna do is let's spec out our expenses. So let's just dive into our data model. We're gonna have expenses as one of our collections, so Directus is gonna create this table for us. And when we go to the optional system fields, we'll probably have a status. Let's go with the sort for the order on the expense report and we'll just select all these system fields because they have some presets that are useful to us. Alright, so we've got expenses, and I'm just gonna use a Expensify attribute. We'll create a folder here for this. We even give it like a nice green color. Boom. We're gonna organize all that inside our Expensify attribute. Okay. So the next thing, we are going to have an expense report. Right? We've got to roll up all those expenses somewhere. This is where we'll do it. Okay. Great. Okay. What else are we going to need? Let's just get started with this functionality. Alright. So, we go back to Expensify and let's try to figure out exactly what their data model looks like. My favorite way of doing that, short of interacting with the app is searching for their API. So we'll just go to the Expensify API, we'll go to their API reference, we'll just zoom way in on this, Expensify integration server, all you need to integrate. Alright. So request formats, we see what's going on here. Let's look for read and get policy lists. Maybe create. Okay. I see here's the expense creator. Alright. So, okay, it looks like here's a definition of the expense object. So reading through this, we've got a merchant, we've got the date of the expense, we've got the amount incense, we've got the currency, that's an integer, that's probably on purpose, three letter code of the expense, external ID, there's a category, there's a tag, is it billable, is it reimbursable, Comments, reports, IDs, tax. Okay. Straightforward enough, let's dive in. We'll go to our expenses and first, let's add our merchant. So we'll just create a new field inside our data model for Directus. This is just going to be a string type and clear enough, we are going to require value. We've got to have somebody that we got this receipt from. Let's change that to half width. And now let's move on to the amount. So let's do an input field. Now I could do this as an integer in cents. What I'm going to do instead is just use a decimal for this. So we'll do a decimal, I could change the scale to 2 points and, looks good. Alright. So now we've got the amount for the expense, we need to add a date for the expense. Expense date, let's just call that. So we'll choose the date time here and I'm gonna pick the time stamp as the type just because that will respect any time zones. I'm in the US. I use the 24 hour format. Or let's not use the 24 hour format since I'm in the US. No need to include the seconds. That's great. So we got our merchant. We got the amount. We got the expense date. Let's just do the name of the expense. Is that inside Expensify? Let's take a look. Created amounts, optional tag. Yeah. I'm just gonna give a name for the expense. Maybe we have some notes for that. Great. We'll add the text area field for that. We don't need any formatting. Great. Merchant amount, let's do a little cleanup on this. Directus makes it so easy to build apps that look great for your users. Alright. What else do we have? An expense comment. Got that. So then we have things like, is this billable? Is this reimbursable? So these are going to be toggles inside Directus. That's the name of the interface as we call it. Let's do is billable. I like to prefix those with is, just keeps things organized. Default value will be false, and looks good. Maybe we want to embellish this a little bit. So if you go into the advanced mode inside Directus, we can add notes for our users. Is this expense can we invoice this to a client? Great. Alright. Enabled. Let's just call it billable for the label. That seems a little better. The display is where it will show out show up in some of the different layouts inside Directus when you're looking at a list of records. Yeah. I don't see any need to change this. Great. Alright. We'll shrink that to half width. And I'm just gonna duplicate this one, and you can see how easy it is to build this out. Is reimbursable. Directus makes a a lot of this quick and painless. So we're gonna keep this on the expense collection. Is reimbursable. Yes. This is a reimbursable expense. We've got the user that created this, but, I wonder if there's a a use case where I have to expenses that somebody else submitted because they didn't do their job. So let's just add a owner, an expender. What is the the proper term for that? Buyer? Let's just call it an owner. Expense owner makes sense to me. That's what we'll call it for now. That is going to be our directus users collection. So that'll be the users of our app. Great. That's what we'll call it. That's the hardest thing in development is actually naming things. Okay. So this looks pretty good. This is a good start. What else are we missing for this? We've got a category, we've got the tags, Name of the tags. Yeah. We could we could do that. Let's let's build in a little more functionality. So we'll do tags. Maybe I wanna do tags and categories as a separate collection, and actually build those into the relationships. So category to me, there's only one single category that an expense can be in. Tags obviously could be many. So here we are going to use a many to 1. So this one, expense belongs to a single category. That's what we're gonna call it. Let's call it expense category. Great. And for our related collection, let's also do expense categories as the name of that. Now, I could click Save and Directus will do some magic for me, or I can open up advanced mode and go in and actually look at the details of this relationship. So if we look, we can see we've got this collection, we've got an expense category field, and Directus is telling me that, hey, we're gonna create a new collection called expense categories and we're gonna use the ID field of that to populate. Alright. The mini to 1, I could control the display template, but for now I don't have any other fields because that, collection is yet to be created. But we'll go in and go ahead and create this field here. And if I were to go back to my data model as well, we'll see that Directus created that collection for us. So now we've got our expense category. Let's go in and do tags as well. So tags are going to be a many to many relationship inside Directus. One tag could be applied to many different expenses, and many expenses could have many different tags. Great. You didn't know we were getting into data model expertise today, but let's name this thing. What are we gonna call it? We're just gonna call it tags. Right? We're going to create a related collection called expense tags and what else do we need to do? Show a link to the item so we can edit that tag as needed. Great. Save. Alright. So now we've got the basis of our expenses. Let's go in and flesh out these other ones that we just created. For expense categories, we're probably gonna have a name for that expense category. And, you know, maybe we got, like, an account number for our profit and loss, account name. Cool. Alright. And then for our tags, let's go in and add name and maybe we want to create a color for our tags. So I'm just gonna search for the color field here inside Directus. We'll give each tag a color. I could go through and preset these. If you leave this blank, Directus will actually create, give you a few of those presets that look nice anyway. Alright. So always gotta do my proper formatting. This is kind of a speed run, but one of the other things that I am a little OCD about, honestly, is cleaning things up and having a nice row of or nice columns of icons inside the app. So you can see a website project I've got working on here, but, you know, for now let's just roll with this. So we've got our tags, we've got our categories. The last thing that we need to do, if we open up one of our expense reports, which we have no data in that. We need to figure that one out. But we need a link back to our expense reports. So let's go into the data model. We'll go to expense reports. And this is really nice. We just recently added this search feature inside the data model, which is great, especially when you have hundreds of collections. So we'll go into our expense report. We're going to have a name. Do we even need a name? Yeah. We'll give it a name of the expense report. This could be a title. Bryant's second quarter expenses, blah blah blah, etcetera. Alright. Then we have a owner or could be like submitted by, you know, that's probably better but we stuck with owner on the other one so we'll just roll with that. For our related collection, we're gonna do direct us users. And you could pick these from a drop down over here on the right just by clicking those, but if you'll notice when I hit on one of the actual collections inside Directus, this turns purple just to let me know I'm doing the right thing here. So we got the owner of this expense report, yeah, I don't like that terminology, but I I don't wanna backtrack at this point. Date created. Alright. What else do we have? Then we're gonna need the actual expenses on this. So in this case, we've got a one to many relationship. So one expense report has many expenses. We're gonna call the key for this expenses, that's what it's gonna be stored as a column in the database as, and then we're going to pick our expenses as the related collection. And for the foreign key, we don't have one that exists already, but Directus will create one for us, which is nice. We're just gonna call it expense report because there is a single report it is linked to. And then I could go in and use the let's change this to a table view. We could see the merchant. Maybe we want to add the expense date, the amount, and, maybe the category. So here when it's a related collection I can just pick up the the related fields as well. Great, enable searching and filtering, hit save and it's probably good enough for now, right? Let's dive in and actually start creating some expense reports. So, I've got a new expense report. Brent's 4th quarter, it's not even the 4th quarter, 3rd quarter, let's just call it September expenses. Expenses. Great. Alright. So we go in, I'm gonna be the owner of this. I am not a user at this point, so I'll just use mister hop along. And now we can go in and add some of our expenses. Alright. So we've got Starbucks. The expense date is going to be this morning at, what, 7:30. I'm in West Virginia of all places, so we don't have a a ton of coffee options here. I know some people are gonna be mad that we've got Starbucks in here. That was expensive. So let's say 2102 has the amount. Coffee Needed that real bad. Alright. Is this billable? Probably not. Is this reimbursable? You know, we could have this where I could select this, but one of the other things you could do inside Directus is preset this to a default setting for, like, the the current user, for example. So for now, I'll just pre we'll pick hops along. We don't have any expense categories. I wanna say this would probably fall under meals and entertainment or just meals. No, you wanna divide those out. I hate doing bookkeeping, so accounting account number, I wanna say it's like the 3 or 4 100. Mills Entertainment. Great. Cool. Keeping track of all that. Tags. What are we gonna tag this as? Coffee would be the name of it. I don't really like any of these. Maybe we can pick, yeah let's just go with red for coffee. That's great. Cool. Alright. So we've got our information here. We've added our first expense. Pretty great. Cool. We've got our expense report. Yeah. The next thing maybe we want to fire off this expense report to somebody on our team. How are we doing on time? We're at 43 minutes, so that took about, 18, 20 minutes to flesh out this. How can we send this expense report to a team member, for approval? Well, first of all, let's do an expense report. Let's add an approved by. So we wanna track who this was approved by. That's gonna be a directus users, so it's gonna be one of our users. Great. And then maybe an approval date, Right. So we'll do a date timestamp, date approved. Great. No 24 hour format. So now we got 2 fields to track our approvals. Let's go in and use the flows inside Directus to send this report off or send a link to somebody to this report. So send expense report for approval. Great. And then we can even customize this. Let's do send. Yeah. There we go. Looks great. Scheduled send. We'll make this orange just so it stands out. Alright, so now I've got a list of different triggers. I want to actually trigger this one manually. So Directus gives us the option within a specific collection on the layout page or the detail page to send this off. So we will go in and look for expense reports. Okay. There's my expense report. Great. And then I could choose whether this is asynchronous. So, you know, do I wanna do this in the background? What are the locations where I can trigger this? So we wanna do on the collections and items pages. That's fine. We'll do both of those. If you are on the collection page, this will require a selection. We we do need you to pick this to actually send this off. And then next I'm gonna do this require confirmation because I get the ability to add different fields to the system, to prop for those fields when I send it. So I am going to add a new field for approver. So who do we need to send this? Approver. Who needs to approve this report. Great. Now the type I'm gonna pick here is gonna be JSON because we have a special little interface called the collection item drop down where I can pick a value from this and store it in JSON. So here again, we're gonna do directus users, and I'm gonna pick the user that I want to approve this report. And I could go in and as far as customizing the display template as well. You know, let's do the avatar, first name, last name. Great. And then maybe we even wanna add a note. Note. Close note to approver and that'll be just a simple string. No big deal. Great. Got the field width, let's make it full width. Actually, let's do a text area for that. Just a quick note. Great. Okay. So now that we've got that, what I'm gonna do, I'm gonna save this and I just wanna get a look at what it's gonna look like when I'm on my expense report. So over here on the right hand side, you can see Send Expense Report for Approval. I can select items or if I'm inside this expense report, I can click expense report, and then I get this confirmation dialogue of who needs to approve this, Matt. My note to Matt is going to be, hey, I need more budget for these videos. Great. Run flow on current item and let's just see what that came up with. So inside my flows, I can go in and check my logs and I could see what was actually sent down the pipe. So here's the trigger, here's the body of the payload, and this is actually what we received. This is what we will use to fire off that message. Alright. So this looks great. What I'm going to do, I'm just gonna copy this information, this is what I need, and I open up Versus Code. Let's start with a new window here. Where are you? New window. New file. Alright. So this is our trigger data. Great. Perfect. Okay. So now let's build the steps of our flow. We wanna send this expense report for approval, but we need an email to send it to, and we do not have that data here. So we need to actually get that data. So that's gonna be our next step. Get approval approver email is what we'll call it. And we will click read items from the database. The permissions from the trigger should be fine. I can also change this to full access if this is gonna be, triggered from somewhere other than inside the app. And for the collection we are going to use the directus users, but you can see this is not coming up in the search because it's a system collection. So I'm just gonna go in and hit edit raw value and we will do directus underscore users. That's our collection. Now for the IDs, we are going to use our little squarely bracket mustache syntax and we are gonna pick up the ID from this approver key that we selected. Alright, so, how do I do split screen here? Fancy fancy. Alright. So this is gonna be located at, so we use our brackets, mustache, we're gonna do dollar sign trigger. Trigger is the only one that has a special syntax. All the other keys of your operations get appended under, the key that you set here without the dollar sign. That's one of the big things that I see a lot of people get tripped up on. And it's gonna be dot body dot approver.key. Great. That's gonna be our ID. Do we need a query for this? I don't think so because we're picking up the key there. Let's hit save and then next we want to, I'm I'm just gonna save this. Let's run that one more time. So we'll go back to our expense reports. I'll pick Matt here. Hey, dude. We'll go back to our flow, and this is a good way of building flow so I can actually see the details of what we've got going on. Now I can see an issue right away. This for this demo anyway. This is gonna send to matt@example.com, which is not a real email address. So I'm just gonna quickly change this to one of my own emails just for demonstration purposes and to prove that we can actually build this app this quickly. Alright, we'll go back to our flows. Now let's actually send that email, and again when working with flows it's helpful to have like a code editor open, but, this case is fairly straightforward. We are going to send an email. We'll choose the send email operation and for the addresses we're gonna use that squirrely mustache syntax again. This is going to be get, this is where my memory struggles. This is called git approver email. So the 2 is gonna be git_approveremail, and that's gonna be the user object. So we're just gonna do email. I have to hit enter here to save this. We could also add additional emails if I wanted to if I wanted to cc myself. Let's call this expense reports. And the type here could be WYSIWYG. You can even choose a template if you are using our custom extensions, but let's go in do this as a WYSIWYG will be fine and we're going to do the trigger. So we we just want to pick up the content from our notes, which is gonna be body dot note. Should be Great. Alright. So this should populate that message into our WYSIWYG editor when we send this email out. Fingers crossed we're gonna save this. Let's test it out and see. How we doing on time? Alright. So we got about 32 minutes left, maybe a little less than that because I was, failing on throwing together the or I was failing to do the the countdown correctly. But, let's give this a shot. We'll go in, we'll send the expense report for approval, and we're going to send it to Rabat Miner. I need some more lattes. Alright. So we fire that off. Let's go back in and check our logs just to make sure. And it looks like the email went out. I'm just gonna open up my email inbox and voila, it it sent this out. That's great, that's all good, but, where's the actual expense report? There's no link. So let's go back in and what I'm gonna do, I'm just gonna capture the URL for this from my directus admin. Great. And then we're gonna go into our flow. We'll go back to that email and we just wanna give Matt like a button or a link or something here. So we're gonna do view and approve report. We'll enter in this email and we're gonna use our bracket mustache syntax and we'll do the trigger dot body dot keys 0. So the first item of the keys array is what we want to include. Cool. Great. Gravy. Save. Save. Alright. So now let's go back in. We'll send this expense report one last time, one more time with a link, hopefully. Fingers crossed, right? Alright, so now we wait and boom, we have a link. We click on the link, it logs us in and, Matt should be logging in to update the status on this. So we may want to go in and update the statuses for these. Let's do our expense reports. Let's change the status, draft. We'll do submitted for 1. Submitted. So submitted for approval. Approved. Maybe not approved is 1. Rejected. Let's call it rejected. That'll that'll be fun. That's a little harsh for an expense report, like, hey, your expense report was rejected. But, yeah, no worries. Oh, I goofed that up, didn't I? Should've saved before I started slamming away on the keyboard. Submitted. Alright. We'll just clean this up. Draft. Okay. Approved. Rejected. Alright. Let's just show the raw value for now or maybe a formatted value. That's fine. We'll do like a border, make it fancy looking, allow other values, no, allow no selection, no, for the interface though maybe it could be interesting if we do radio buttons. Great. Alright. Cool. So now we go in. Matt can approve this expense report and save it. But as you can see, I'm logged in as myself. I could potentially approve this expense report myself, which is is not cool. So we could use the roles and permissions inside Directus for that, where I could go in and say a given role like if we just had one called team member for example, I could go in and for our expense reports, I could go in and give custom permissions for this so that I cannot edit a certain field like the status. So this would prevent that user from actually, I'm sorry. I did that backwards. I would check all the other fields like the that user can update everything else about this expense report except the status or any other items that were that I needed. And likewise, one of the other things that I could do here is when I create a report, if we use the custom permissions for this, you could go in and define the presets where you can say, hey the current user is the default for this particular field. Sounds great. Alright. That is basic functionality of this. We've got the ability to add expenses, submit expense reports. You know, you might wanna try and tag into some other system to pay out these expenses, but for now I could stroke a check for those, I guess. But that leads us back to, boom boom boom, the automagic receipt management. So this part is a bit scary scary to me. I don't know how we're gonna do this in 26 minutes. Actually, I'm lying to you. I do know, what we're going to try because I've already cheated a little bit. Let's get that clock out of the way. I'm gonna make this full screen again. I have found this other service called Mindy Mindy? Mindy? Not sure what it's called, but it takes the receipts OCR, optical character recognition magic on them and apparently spits back out some data. Now, for the sake of time, all I've done is logged into this app and, set up an account. I haven't done anything else with it, so you're almost as fresh as I am at this point. I did see just a quick little tour tooltip that told me this is the API that I want. So let's take a look at at how we could do this. Anytime I upload an expense that has an image attached to it, and Directus is mobile friendly, so I could potentially log into this from my phone and update these expenses as well, just to to prove it to you there. Anytime I update one of those expenses, it has an image attached to it. We wanna send this off to this service and hopefully, I'd, like, not have to deal with actually entering in all the separate fields. Great. So first thing we need to do is, we got our expenses, let me close this one. We need to add an image field to upload the expense. So we'll go into our expenses. Let's add a image, receipt image or receipt file or we could just call it file. No need to get fancy with it. Alright. So now we've got the ability to upload a file. Let's start building a flow and then we'll incorporate this other service. So we've got, automagic COCR. Cool. Alright. So we'll do that on a vinthook. Let's do a action non blocking, so we don't wanna block the thread. Anytime a new item is created inside expenses, and we'll start there. Right? Alright. So let's go into, let's actually go back to our expenses. Let's just call this a new expense. Actually, we're not gonna do any of that. All I wanna do here and maybe we move this file up to the start actually. Let's move our file way up here. Alright. So the ideal scenario here is all I have to do create a new expense, upload an image of that, and then this service and Directus would do the rest for me. So let's just Google receipt images. Let's get a receipt image, see if we can find one that's pretty gnarly as well. What's this guy? Tesco Metro. Yeah. This one looks pretty beat up. This will be a good test, right? Save this image. Where did I go? Too many different tabs going on. So here's our Tesco shopping receipt. We hit save. Merchant value can't be null. That was a problem of mine, maybe I should go in and remove that, but you could set that up where it wasn't required. But now, if we take a look at our flow, that should have triggered our flow, we've got a payload, we can see here's the file that we're going to potentially pick up. Great. One of the other things that I wanna make sure, because we're gonna have to send this file to that service, is I'm gonna check our system collection and make sure that direct us files has public read permissions, and I could drill down to where, you know, only, files within a certain folder are available if I wanted to get very specific for this. Let's just leave it as is. Again, this is an Inexpanify Tribute, not a clone. Great. Alright, so now we've got our data coming from our new expense, we are going to wanna pass that to the mindd API. So let's see the API here. Alright. Load some stuff up for me. Alright. If I was smart I would have gone in here and cheated a little bit more than what I'm doing now. Alright. So I don't have an API key. How do I create an API key? API keys. Alright. We'll create an API key. We're gonna call this Directus. Great. Copy that API key. Don't you guys steal my API key? So So we'll go back to the API, go back to our documentation. Here's everything we're gonna need. Now is a good time to do the split screen stuff with, Arc. Great browser, still struggling, like 6 months on to figure this thing out. Alright, give myself a little more room. So we got the post request. We are going to use the send to, let's just call it OCR receipt. We are going to use the webhook request URL. We're gonna do a post request as it says. Here is our URL that we're gonna pick up. Hopefully, we could just copy that. Savvy. Of course it takes that post request. This looks okay. We got our headers, we're gonna add authorization. It's gonna be a bearer token. Raycast, great app as well, by the way. We'll paste in our API key and then the content type. Content type. Multipartformdata. K. Let's look for, like, a curl example. Document is path to your file. Okay. So it looks like we've got a document key and I'll pick up oh, the other downside of working locally. That might be the death of this particular project. Right? This is on my local computer. I've got 18 minutes. It's not going to be able to access that image. Let's do some more cheating. Right? Go back to our receipt images. I got the image address. Don't be mad at me for cheating on this. Alright. Let's take a look at that. We got a document. Here's the public URL of an image. Document can either be a file object URL or base 64. That should be savvy. Next time I'll have to do this on a standard cloud instance, not one of my local copies. Alright. So we got this. We're gonna take the webhook request. Let's see if they give us like a response what that response looks like. Date, confidence, document type, line items, locale, supplier address. I tell you what, let's just send this off and see what we come back with. Alright, so we're just going to pretend and actually this is gonna be correct because it's the same receipt but just a little cheating involved. We'll add a merchant name for this, We've sent this off to our auto magic receipt. It says unauthorized. Use token. What did I forget? Oh, token. Let's see. I've forgotten something. API reference authorization token. It's not bearer token. It's just token. Great. Let's go back and try her again. Keep editing. Don't forget to save your changes, kids. Alright. We'll create a new expense. Use the same receipt we've already uploaded so we don't duplicate it, and I really should remove that requirement. Go to our flows, See what we got. Bad request. Missing boundary and multipartform data. Alright. How are we gonna structure this, right? Missing boundary and multi part form data. Do we actually need the content type? Can we try that? See if we can get away with it. Otherwise, I think I'm gonna have to reformat everything. Alright. So we go to our flows. Let's take a look. Logs. Hey. There we go. Alright. So we've got our prediction. And we probably don't have to use form data because we're not passing an actual document. Cool. So we've got our document. This is a giant string, or a giant JSON object. Tons of stuff. Taxes, the value, supplier phone number, prediction, document type, expense receipt. Here's our line items. We just need our amount, right, and dates. So I'm just gonna copy this whole thing. Let's open up Versus Code. Do this and we'll look for date. Okay. So there we go. We've got our date. It'd be great if there was a way to copy that path. Right? Let's close, let's separate these, do this, do a little side by side action going with Versus Code. Alright, so now we need to update this item. How we doing on time? We've got 13 minutes. Let's get it done. So now we're gonna update our expense with the data coming from this API. And I may wanna do something like this where we say format data or I could just directly update that data. Anyway, so we'll go into update data, let's say update expense. We are going to use the permissions from the trigger, this is gonna be an expense. The IDs are gonna be coming from trigger dot body, dot keys dot 0, I believe. We'll take a guess and the payload that we want, we got the merchants. We wanna pass that. We want the expense date, I believe. The amount of the expense, and I think that would be a win. So let's look for the merchant, supplier, supplier address, supplier name. Alright. Let's start with the date. Right? So how do we get a hold of this guy? We've got our data. Alright. Let's just save this. That is our OCR receipt. That's our key. And of course because this is an invalid payload, it did not save. Alright. So our date expense date is going to be blah blah blah. We've got, OCR receipt dot data dot documents dot inference. I love that these guys are saying all this, dot pages dot prediction, debugging this is gonna be a nightmare, dot date dot value. That is deeply nested if I've ever seen it. Great. Alright. So next we've got the merchant. Let's try it. That is gonna be something probably similar, and I already see a typo. Prediction. Okay. So we'll just copy paste that. Alright. And that's gonna be called what? The supplier supplier name value prediction dot supplier underscore name dot value. Okay. Great. And then last but not least should be the amount. And we are closing in on 10 minutes on the clock, hopefully this thing works out. Would hate to start off on a loss. Alright. So the amount let's look for the amount. Total amount is what we're looking for. Point 99 confidence, that's great. That's what we're looking for. Got a lot of confidence in this. We got the total underscore amount dot value. Alright. So there we go. We've got our JSON. I'm just gonna copy it just to verify, update. Let's try it one more time. Let's delete these failed attempts. Alright. Tesco shopping receipt. We're gonna have to enter that in. Hit save and automatic receipt OCR. I don't have permission to access this because I don't have the proper key. So I didn't pass the key. I got the payload wrong as well. What did we get wrong here? Dot data, OCR receipt dot data dot document. Maybe it's just document. Maybe we can omit data. OCR receipt. And I also need to fix the keys because I didn't get those correct either. So it's actually just key. That's what's coming from the trigger, the expense. So body trigger dot body dot key. Alright. Fingers crossed. We'll run this again. This works this time. Let's hope so. Do our nice little t here. Boom boom boom. You don't have permission to access this. Still says undefined. So we're getting this back from the API. That's great. Trigger dot body dot key. The key should be correct, though. Payload trigger dot payload oh, no. This is an event hook. I'm sorry. So I've got it wrong. Should just be trigger dot key. No body. It's fine. Trigger dot key. That part's good. We are not good here, though. So let's do this. Let's disconnect this. Let's add a step. You just log this to the console. Log. And we are going to do get OCR receipt is what we're calling it. We're just gonna console log OCR receipt. Log OCR receipt. See what we're getting past. Alright. We'll drag that down here for now. We are running out of time for like a fun playing beat the clock. We'll hit save this. Let's go take a look. What do we got for him, Johnny? Log to the console, message dot data. Okay. So do we have to include message dot data? Worth a shot. Let's see. With 5 minutes and 40 seconds on the clock, Wasting precious time. So we have message dot data Message dot data dot documents. I'll just copy, paste. Alright. It's either gonna be a big finish or a sad ending. Not sure. We'll try it out and see. Alright. So we've got that same image, we're running it one more time. Flows do us nicely error update expenses set where invalid input syntax for type numeric. Still not getting the correct stuff coming from the app here. What in the world is going on? Alright. Let's bring in the big guns here. What are we what am I doing wrong? Logging to the console. Alright. Let's try picking up the data that we want. Let's go with, run scripts. Where are you? Format, data. Alright. So we're gonna get the data from receipt equals OCR data dot OCR receipt. Okay. And let's just return receipt for now. Let's string this together. Let's see if I could just get something going here. Format data. What's going on? It's gonna be a sprint to the finish. To to to log to the console. I can't even get my, so we'll drag that. Let's log into the console. Let's see what comes out of this. Expense, save, format data, flows, auto magic. Alright. So we got the run script, status. Okay. So we got data. We wanna pick up the data dot document. Right? I swear that's right. Data.ocrdoc equals receipt dot data dot document. Let's return the dock. We'll just keep drilling down. We got 2 minutes left. Let's see how this is gonna play out. Save t. Save it. Flows. If I was smart, I would have had multiple options. Okay. So we got this. Let's get down to our inferencing. Oh, I see what was wrong now. Duh. Pages is an array. That's where we were going wrong the entire time. Pages 0. Don't you love that? Message. It's not message. The message is coming from that console log. Alright. So with 1 minute and 21 seconds on the clock, let's save this and just see what comes up as the clock is ticking down. T. Now, if I hit save and stay it should be doing that for me, but it did not. However, did it? Did it actually do it? Expenses. There it is. Boom. With 49 seconds on the clock remaining, we have populated the receipt information from a third party API. Therefore, fulfilling our requirements for this, That is a wrap on the first edition of 100 Apps in 100 Hours. I feel like we need like a little Tiger Woods just, yeah, finish up strong. So if you enjoyed this series, or this episode of this series, stay tuned. We've got more coming like this. Would love for you to hop into our Directus Discord community and send us your feedback on what apps you would like to see us build next with our set of tools that we have here at Directus.","e1378f47-fa04-46d7-9c9d-ebba652412d3",[5428],"fcb8a27c-5895-47f6-9644-13ea4c3b4aa3",[5430,5431,5432],"3452bafa-9f28-43f2-9e14-3c5de754cb7e","039ef759-0fc3-4584-b7a4-a5205d3a21a8","992f81a1-0a4a-4fa8-8dee-10b4e3d0b848",{"year":2380,"number":177,"id":4162,"episodes":5434,"show":5435},[4164,4165,4166,4167,4168,4169,4170,4171,4172,4173,4137],{"title":322,"slug":323},{"id":4541,"slug":5437,"vimeo_id":5438,"description":5439,"tile":5440,"length":2339,"resources":12,"people":5441,"episode_number":13,"published":5422,"title":5447,"video_transcript_html":5448,"video_transcript_text":5449,"content":12,"seo":12,"status":19,"episode_people":5450,"recommendations":5454,"season":5455},"santosh-ahuja","896637974","We trace Santosh's journey from a business family to a Lead Engineer, driven by a passion for technology and the influential ethos of Chuck Norris in 'Walker, Texas Ranger'.","f47c0fb4-265d-4cf2-bb3f-ce7873894ab6",[5442,5443,5444],{"name":4361,"url":4522},{"name":4524,"url":4525},{"name":5445,"url":5446},"Santosh Ahuja","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fsantoshnahuja\u002F","Santosh Ahuja, Lead Engineer at Optum","\u003Cp>Santosh Ahuja: The code that Chuck Norris followed in the series Walker the Texas Ranger, you know, he the fact that he was so righteous, he was always honorable, and, and plus he had a bunch of flashy kicks. I was absolutely, drawn, to that person's personality and the role he played.\u003C\u002Fp>\n\u003Cp>Pedro Pizzaro: Thank you, Santosh, for joining us today for an interview with us. We really wanted to to get to know you, you know, get get to know a little bit more about your background, maybe advice you have for other developers, and really use this podcast to, as a mindshare, for the community and and to learn a bit more about your background as well and and share that with the world. Right? You know, as you're a lead engineer at Optum, we're just really excited to to hear about your journey and, how you got here, what you're doing, currently, and and where you look to go in the future. So, if you'd like to introduce yourself and maybe a a brief on your background, we can get right into into, talking about it.\u003C\u002Fp>\n\u003Cp>Santosh Ahuja: Yeah. So hello. My name is, Santosh Abuja, and I currently work as a lead engineer at Optum, and I've been there for the last 2 years now. I also run my own consulting firm. It's called App Resilience, and, I have a couple of customers, so that's the direction I'm leading into.\u003C\u002Fp>\n\u003Cp>My background is predominantly I come from a, you know, business family. I have I completed my bachelor's in, in business and accounting. I did a bunch of courses in computers, right when I was in my 10th grade, but I soon landed a job as an engineer. And then from, there on, there's there's been no looking back, and, I'm here right now in in this country, in this beautiful country, called the United States, where people come to pursue their dreams, where people come to work with the best. And I've also had the fortune of, getting my master's done, at Purdue.\u003C\u002Fp>\n\u003Cp>So that's a big background.\u003C\u002Fp>\n\u003Cp>John Daniels: Yeah. I'd love to, I myself, you know, I have a a developer background a little bit, and I'm always curious to hear how people got into engineering and development. Sometimes people played with specific toys when they were younger. Sometimes they watched specific TV shows or movies that got them interested. So I'm I'm really curious to kind of hear that beginning, entry into engineering and development.\u003C\u002Fp>\n\u003Cp>What was it that got you interested? Was anyone a mentor of yourself, more could we would you be able to dive a little bit more into the beginning stages of your exploration into engineering?\u003C\u002Fp>\n\u003Cp>Santosh Ahuja: So it started when I was, you know, you could say young, you know, when you're young, when you're in your teens, you you're really trying to find out you're really trying to find out what you wanna do and carve a path for yourself, but you also have a little bit of that herd mentality. Right? You have a bunch of guys who start playing football and you start playing football with them. They start playing tennis. You start playing tennis with them.\u003C\u002Fp>\n\u003Cp>So I had a bunch of friends. They were from, you know, middle class families and they had, their path carved out for them. They had their parents who instilled in them a strong sense of, purpose in their life, education, and get really high paying jobs. I came from a business family and that was not the path for me. I already had a really cushioned, comfortable, and a clear path of taking on my family business.\u003C\u002Fp>\n\u003Cp>But, you know, because I had a bunch of friends who had a different path and, I just hung out with them a lot more. And, on one summer and every summer, they would always preoccupy themselves with some sort of a course in order to gather skills that maybe, you know, English speaking course or typewriting course and stuff like that. So that came a summer, I guess, in my 10th grade around when I was 13 or 14. A bunch of my friends enrolled in a computer class. And, really, it's because of them I owe my career to them because it's because of them I I joined that computer class and I really did well.\u003C\u002Fp>\n\u003Cp>Never saw computer science as a discipline of engineering, until I saw the amount of math that played an important role, in that, you know, in that in that in that profession. So that's how it started. And then what happened because I did a bunch of courses in computer science. I took up I took up a I took up a degree in business because, you know, my parents want me to, and, you know, we come from a background where we do a lot of things just to please our parents and our family and kinda did that, but also it had a minor in computer science. And I really did well, in that course.\u003C\u002Fp>\n\u003Cp>So after that, I, you know, I was still going back and, you know, was with my family, took on the family family business, run it for a while. But at the same time, my passion for computer science and programming grew. I continued to take a bunch of courses just because I was really, really interested in it, just because I had fun solving problems. And it so happened that, I just hung up with a bunch of friends when they were going for an interview, and I just happened to get selected. And, I really took on the job, and I haven't, in this I haven't looked back ever since.\u003C\u002Fp>\n\u003Cp>I was still within the same city, so my parents were okay with me going in a different direction for a while, hoping that I would find the going really tough, and I would just come back to the to the comfy life that I that I had. So that's how I got into engineering.\u003C\u002Fp>\n\u003Cp>Pedro Pizzaro: That's awesome. That that your path sounds like, you know, you really hung out with a good group of friends, very motivated high schoolers. Mhmm. Very different from from my upbringing of growing up with a bunch of rascals just getting into shenanigans. Right?\u003C\u002Fp>\n\u003Cp>You sound like a very motivated, group of folks. Was that I know you've bounced around to a few different countries, and now you're here in the US. Was that back in India? Was that in Canada? Tell us a little bit about your your journey in\u003C\u002Fp>\n\u003Cp>Santosh Ahuja: in my career. Back in India. So I did I also had a bunch of friends who who were rascals and I got into all all different kind of shenanigans, but I knew that that kind of thing wouldn't get me too far. So I kind of played a safe role. I had my eggs in different baskets, if you will.\u003C\u002Fp>\n\u003Cp>And, and really, and also, you know, because of the sports background, I always had the sense of doing well, have a bunch of friends who I can compete with mentally, physically, and all that all that stuff. So I started back in a small town in India called Kaimatar, and, the short form is Kwendi. It's really known. It's a it's a big hub for educational, for education, a lot of universities out there. And I started with a company, that made software for British Telecom.\u003C\u002Fp>\n\u003Cp>It was that was that was the only software company in the city and was really, really, really, really hard to get into, that company. But I started because of my curiosity, I started learning a bunch of courses really fast, and, I started teaching in different universities. That's what I did when I didn't get jobs and I eventually got a gig with them. And, that's how it started. So before it was not until a few years later that I really got a chance to go to Canada to work for some web companies, because I started with a company, you know, who were developing software for British Telecom.\u003C\u002Fp>\n\u003Cp>I stayed with the same domain, did a lot of embedded programming, and then soon got attracted to a couple of companies who started doing a lot of VoIP, and voice or IP. And then, that landed me to Canada because Canada had Canada had a lot of up and coming, big, white companies. And some of the companies were really, paving the way for innovation and how, video and audio was being streamed over a call and how all these calls that we traditionally do with COPPA lines were really becoming digital. Right? So I spent quite some time in Canada working there, working for somebody, and that was really an eye opener.\u003C\u002Fp>\n\u003Cp>And, because there again, I saw a really, I saw a lot of top engineers. I learned a lot of them, learned learned a lot from them, and that really proved me as a person, as an engineer. I then came back because I was not a permanent position, in Canada. I I was really doing work for an Indian company, but I was engaging with a bunch of innovators in Canada and working in developing some products. And then I came back and, I I was at a point where I was switching jobs again.\u003C\u002Fp>\n\u003Cp>And then I worked for a company, that was Venus out of Richardson. And when Richardson was called, Telecom called it out. We do it. Right? A lot of telecom companies in Richardson, like, NCI and there's AT and T over here that is also, you know, Texas Instruments, both hardware in terms of software and things like that.\u003C\u002Fp>\n\u003Cp>So, I started working for this company, and then, this company was had the business of a startup, the whole, you know, the whole it was really it was really a technical company at the core. Even the founders were past architects of some really successful startups and things like that. So when I joined this company, they were an exclusive shop of a big up and coming start up. And this start up had and this startup was based out of Richardson and the founders of this startup had senior entrepreneurs. They had, they had a history of building successful products and with multimillion dollar exits, like 100 of 1,000,000 of dollars.\u003C\u002Fp>\n\u003Cp>So I got a chance to work pretty early on with such a company. I was core engineer, but then they also had a bunch of engineers here, and I really got a chance to work with some of the really good engineers from companies like Nortel, Cisco, you know, that and that really, again, grew me as a person. It pushed me. I I developed my skills a lot. Pretty much been with the same enough that I was pretty much the same company.\u003C\u002Fp>\n\u003Cp>As over the years, this company transformed itself into a full fledged consulting company making software for the web, the mobile, we literally saw an age where, mobile devices, change from mono those monoprint screens to really Apple like large screens with large real estate, and you would really touch on that screen and see things happen. Right? So you got into that smart, smartphone age and, where these our phones really started taking place our traditional desktops. Traditionally, we had desktop. We've installed different apps on them, played different games on them.\u003C\u002Fp>\n\u003Cp>Now we're getting into an age where we could do all these things on smartphones. And that's when, this company started making apps, saw the opportunity, started making apps for, a lot of, you know, different kind of smartphones.\u003C\u002Fp>\n\u003Cp>John Daniels: Yeah. It's it sounds to me like there's a there's a common thread that kind of weaves its way throughout your life and your career. I'd like to kinda take that road for a second. You talked about when you were younger, and you had this this opportunity to kind of follow the footsteps of your family, which to me, honestly, sounds like a safe route. And I feel like I feel like for those of us who are in technology, we traditionally don't take the safe route.\u003C\u002Fp>\n\u003Cp>I think part of being in technology and engineering and development is kind of finding a way to solve a a problem in a different way. You come across an issue and there's there might be a safe route where you can do something, but why not take a different route, especially if that different route leads you in fun and unexpected directions. So you had this opportunity when you were younger to follow in a safe path, but you kind of chose the you you went along with your friends. You you succumb to peer pressure, and you went into the engineering route. And it kinda sounds like you also went that route as you started to get into your career as well.\u003C\u002Fp>\n\u003Cp>You you made your way to a small start up where there was a lot more excitement, a lot more risk. There was a lot more what ifs and unknowns. But in the end, it really kind of was the best choice for you, and that kind of stemmed back to your childhood. So in your opinion and, you know, there's there's people out there watching who have this decision all the time. Do they go the safe route?\u003C\u002Fp>\n\u003Cp>Do they go the more adventurous route? That's that might be more risky. Do you find that being an engineer in in technology, that is the the better route to go, to be a little bit more daring, to take more risks, you know, not only professionally, but also personally? Is that something that you feel has led you leaps and bounds to where you are now and you'd be in a completely different place if you had gone the safe route?\u003C\u002Fp>\n\u003Cp>Santosh Ahuja: Yeah. You know, the answer I think the right answer to that is partly I've I've never been, you know, I've always, you know, I have, I've always been encouraged to take risks, but also the other part is I've always followed my passion and my intuition. So I always have a suggestion saying that never chase money, chase your passion. If you have an intrinsic feeling, that pushes you towards a certain direction, just as long as your intentions are honest, you're honorable in your, in your in your intentions and in your goal, you're you're gonna be fine. So that's what led me to take the decisions that I, that I took.\u003C\u002Fp>\n\u003Cp>I always also, there was always a saying that when you join with us, when you join a smaller company, you get a lot more responsibilities. There's a lot of lot more opportunities to take more responsibilities and you kind of mature faster. The other part is also with some of the smaller consulting companies, they're they can't compete with larger companies. They're really jumping on every kind of opportunities that they get. So even if they're, they're risky opportunities and, I've always been someone who's I haven't I've never thought too much.\u003C\u002Fp>\n\u003Cp>When I look at an endeavor, I really look at what it offers me in terms of growth. I look at it on the challenges, on the on the challenges that it poses and the opportunities to learn. I don't get in the political side of what if I fail or if I fail, how would I be perceived? What do I look at? Would I make a fool of myself?\u003C\u002Fp>\n\u003Cp>That's not something that I really thought. It's okay to fail, but it's important in my view to always take up opportunities and, risks ahead as long as you are honest in your intentions and you're honest to your passion, and you have the will and the determination to succeed, you'll figure out a way. Otherwise, you're you're going to in any ways, if you feel you're going to learn so much that you're gonna be successful afterwards.\u003C\u002Fp>\n\u003Cp>Pedro Pizzaro: Yeah. Is is that something that drove you? You spoke about, you know, building your own consulting company and and having a few customers there. Is that the mentality that drove you to creating that consulting company? And you mentioned there's a lot of challenges competing with the big players Yeah.\u003C\u002Fp>\n\u003Cp>In the market. I'd love to learn a little bit more about those challenges and and how you've overcome those as\u003C\u002Fp>\n\u003Cp>Santosh Ahuja: well. I mean, it's I'm I'm still in in in those challenges because this this the technology space is so big, so wide, and it's growing so much that that's enough for, you know, there's enough for everyone. So that's again why, you know, running my own consulting company gives me an opportunity to choose the projects that I want to work on, work with different people, go to different places and pick up riskier projects and really, win them over and succeed at them. So with bigger companies, because they have so many options, they have the luxury to cherry pick what they wanna pick up and what they don't want to. But then there are some pearls that they often leave out because they think they're too risky.\u003C\u002Fp>\n\u003Cp>And that's that's the opportunity that you need to jump on and pick up with both your hands. And, if you do that sooner or later, you'll definitely find success. And I like, and then, and that's how I like this, feeling of really being, you know, independent and being able to work, look at, you know, the whole globe, you know, the whole world as, as the playground and open up my window to all different kind of opportunities and just work on on different things and have that same mentality of taking on risks, pursuing your passion, and doing what what really, what really drives you.\u003C\u002Fp>\n\u003Cp>John Daniels: Yeah. That's that's awesome. And to I mean, you started as a a child growing up in India, and then you've made your way to Canada and even Texas. You know? Growing up, could you have imagined yourself where you are today?\u003C\u002Fp>\n\u003Cp>And was there any sort of inspiration to go to Canada or inspiration to go to to somewhere like Dallas? Any anybody growing up as a kid that you might have looked up to or or seen as a as a an excitement to to make that type of worldly journey. You know? Because I've grown up in the US my whole life, and I do have goals to travel worldwide, but it's more so travel. But your exciting development journey is taking you to live in all these places.\u003C\u002Fp>\n\u003Cp>So taking back a look at your childhood growing up and even into 10th grade when you started to get into it, was there any draw to make your way to either Canada or US and kind of explore this worldly journey along with that development engineering journey?\u003C\u002Fp>\n\u003Cp>Santosh Ahuja: So so I'll tell you this that, the lure of moving to the west or travel was never there. I I was pretty westernized in my thoughts and in my, the way I grew up when I was when I was a kid. All my friends, my engineering friends, have ended up in the US and they're really in big positions. But there comes a point that as you get better and better and better and better at your, at your profession, you're always going to be steered in a in a place where the best talents meet, where you can rub your, you know, where you can rub your shoulders and get better. For example, if you really happen to be a great football player, you can play all the local leagues, but as your skill goes, gets better and better and better, you're automatically going to land up in NFL.\u003C\u002Fp>\n\u003Cp>That's, that's how I see it. That's how I saw it. There came a point where, we need to the stage and really, rubbing shoulders are the best and working for some really large and big companies. Mobile was the way to go. And really there were a set of events that unfolded that got me here automatically.\u003C\u002Fp>\n\u003Cp>Like I didn't even have to really try. It was not like sitting back in India and said, you know what? I'm gonna move to the US or I'm gonna move to Canada. It's just the opportunities, the way they present that themselves. And me being not risk weary and taking everything heads on has landed me here.\u003C\u002Fp>\n\u003Cp>Pedro Pizzaro: Yeah. That's that's awesome. And, you know, I know we spoke before this just briefly, but you touched on how, you know, this this is an ongoing journey. This is an ongoing process. You're always out there looking for new information and and, you know, keeping up to date with yourself and your education.\u003C\u002Fp>\n\u003Cp>I'd love to know 2 things. You know, what are you doing in that way to to to do the same?\u003C\u002Fp>\n\u003Cp>Santosh Ahuja: You know, if you're at heart, you're an engineer. So, you know, you if you if you're an engineer and if you wanna be successful, you've you've committed yourself to a life of, a lifelong learning. So you, you have to understand that. And also I've always, because of maybe because of the background that I come from, I've always had a lot of respect for education. I I feel that the the all these institutions, still instill a bunch of good values.\u003C\u002Fp>\n\u003Cp>And also it's more so because what what it does to you in terms of the other people you meet at such institutions, you meet other people who are also are mostly extremely hardworking, driven. They have they have, big dreams and they really wanna change the world and so on and so forth. When you are with with such a group, it's it's, it's inevitable that you will have they'll have a profound impact on you. So it's basically that part that, you know, that that makes, doing a course at one of these big universities so attractive. Otherwise, you know, we're in an age where you can learn by yourself.\u003C\u002Fp>\n\u003Cp>You can look at YouTube videos, you can go to Coursera, but also, I've been very picky at where I wanted to study, and pursue my continue my education. And some of the universities that are really in the top 10 or 20 have outstanding, educator educate educationists and professors who know their subject really, really, really, really well. So it's just being among that group, among that highly educated, curious people that, that has a good strong influence on you. It's always been that way right in when I was scared. I've hung up a bunch of engineers and that's how it got me here.\u003C\u002Fp>\n\u003Cp>Again, giving when you put yourself among a certain group like that, it's the it's the other people. It's the it's the professors that have a strong influence on on your thought process.\u003C\u002Fp>\n\u003Cp>John Daniels: Was there ever a time, you know you've you've been through an amazing journey and and really impressed by it, honestly. Was there ever a time either early in your career where you felt maybe I'm going down the wrong path, or were you always, you know, I'm taking on this challenge, and I wanna see this challenge through completion? Or maybe a time later in your career where you just got so, so frustrated with, the status quo, which it sounds like you probably didn't go down that journey of always doing the status quo. But was there ever a time where you just got so frustrated at something or so unsure about the path that you maybe second guessed? Or have you always been the type of person who says, I'm gonna go on this path.\u003C\u002Fp>\n\u003Cp>I'm gonna go to Purdue and finish this degree at Purdue. I'm gonna go to this company and complete this project. Have you always been that type of person?\u003C\u002Fp>\n\u003Cp>Pedro Pizzaro: Yeah. And just to add just along those lines, like, how do you know when to quit? You know, if you're so determined to take something through to the end, you know, are is there a time to quit? Is it appropriate to quit at certain times, and and how how do you know when you're on that path?\u003C\u002Fp>\n\u003Cp>Santosh Ahuja: So I it's obviously in any careers, you know, there are ups and downs. I saw that the 2,000.com bust, then I saw the white bust. There are depressions. I think everybody who's a professional has, you know, goes through their doubts, their career and even at workplace, you know, sometimes there is bureaucracy and there are other things that really are a definite, are a letdown. Right.\u003C\u002Fp>\n\u003Cp>And, there are those feelings that lurk in that says, you know, maybe, you know, I should have taken that at all, maybe I'm in the Olympics, but you as long as you stay true to your passion, true to your course, you have to get your happiness out of that. You have to kinda shun everything else away and see, hey, you took up you went this route because you were passionate about what you were doing. And I think for many people, it's like that. There comes a point when someone's working in a company, there are things that really affect them, mentally and emotionally. But the way they're able to deal with it is to get invested fully in the work that they're doing.\u003C\u002Fp>\n\u003Cp>Right. But if you're not, the problem comes, then you made the decision to be here because of money and not because of passion. If it was because of money, then you're gonna say, hey, the money isn't great, and I'm going through this emotional and, you know, mental torture, it's not worth it. So that's why I keep saying the only way to sustain yourself for any endeavor is to be passionate about it. Be passionate about it and enjoy enjoy your craft, enjoy getting better at it, and, that's what, that's what will keep you going.\u003C\u002Fp>\n\u003Cp>And I've I've never been a quitter. I've always once I take, because once you once you quit, quitting becomes a habit. And then succeeding unfortunately, succeeding also becomes a habit. So you decide what you want. And it's like you you have one it's it's going back to your sports background.\u003C\u002Fp>\n\u003Cp>Right? If you have one bad performance in the season, it kinda leaves an impact on you. It kinda rubs on you. So always end something on a good note, always end something on a positive note, on a positive performance, and keep repeating yourself. And it's okay to fail.\u003C\u002Fp>\n\u003Cp>And then there are multiple theories that are saying that you must fail fast or and so on. But as long as you learn from it, you're learning from your mistake, you're eventually gonna succeed.\u003C\u002Fp>\n\u003Cp>John Daniels: Wow. Yeah. This this drives me back to as Pedro said, we have spoken before. And this type of line of thinking of grit and drive and determination and not being afraid to fail, it does remind me and I I feel like I would be remiss to not bring it up. When we spoke before, you mentioned that you were a huge Chuck Norris fan.\u003C\u002Fp>\n\u003Cp>And when I think of Chuck Norris, I think of drive and grit and determination and never being afraid to to fail. Do you think that either Chuck Norris has influenced your personal life and or vice versa? You've you've were drawn to him because that's how you were personally. And can you kind of dive a little bit in? I I do think it's kind of fascinating how you're a fan of Chuck Norris.\u003C\u002Fp>\n\u003Cp>I'd like to get into that just a little bit.\u003C\u002Fp>\n\u003Cp>Santosh Ahuja: So as I grew up, you know, I, you know, I grew up as a relatively obese kid, but I I loved action. I loved martial arts a lot. So I took up, I took up martial arts when I was 12. I enrolled in the class of taekwondo, then I went on to represent my state, my country in the sport. Back then, you know, when I when I was in my teens, we started getting, foreign channels and foreign TV content on our on our TVs.\u003C\u002Fp>\n\u003Cp>And that's when I I, I followed a series called, Walker, the Texas Ranger. Right. And even before that, I'd seen a couple of movies of Chuck Norris called the force of 1 and things like that. What really drew me, of course, you know, there've been many martial artists and they were all extremely skilled, but because of the code that Chuck Morris followed in the series Walker, the Texas Ranger, you know, the fact that he was so righteous, he was always honorable and and plus he had a bunch of flashy kicks. I was absolutely, drawn to that person's personality and the role he played.\u003C\u002Fp>\n\u003Cp>And also his, you know, his sweet southern demeanor is extremely reflective. The kind of, the gentlemanly manners down from the south. And I and I later on, I dug out that he's really from Oklahoma. He's from down south. Right.\u003C\u002Fp>\n\u003Cp>He's from Oklahoma, then he moved to Texas. So for many years, you know, I just wanted to be like this guy. I wanted to be righteous. I wanted to be brave, and I wanted to be honorable, and so on and so forth. But I never thought that, I'd land up in Texas.\u003C\u002Fp>\n\u003Cp>As I always, you know, said that for me for most of the people, this is Texas. For me, this is Chuck Morris Lane, and that I felt good validation in the world. I have passed, I think, this ranch. It's called, the lone wolf ranch, and it's really inspired from the from what we call, lone wolf McQuade, you know, where he, plays this role and he's his name is McQuade and then he's called Lone Wolf, and he drives drives around an old, self broken, Toyota truck. So so when I arrived here, I got myself a truck.\u003C\u002Fp>\n\u003Cp>I still practice martial arts, and I still I still look up to Chuck Norris because I think he's a class act. Even in the world of martial artists, arts, martial arts, he's still extremely respected. I keep wondering why they, you know, why they haven't, gotten him to be a president of this country. I think he's a\u003C\u002Fp>\n\u003Cp>John Daniels: Well, if\u003C\u002Fp>\n\u003Cp>Santosh Ahuja: he sees\u003C\u002Fp>\n\u003Cp>John Daniels: us, who knows? Celebrities are getting into it these days. So Yes.\u003C\u002Fp>\n\u003Cp>Pedro Pizzaro: Yeah. And it's it's you. Now you're now you're the Texas Ranger, so it's it's awesome that there's still one roaming around there, active. Right? But I I wanted to bring thing something back to failure, which is, something that obviously Chuck Norris had no part in.\u003C\u002Fp>\n\u003Cp>He didn't obviously, Chuck Norris never failed. But but you spoke about failure being acceptable and and really being passionate about the work that you put in and always trying to learn something. That way failure is always an opportunity. Right? And and to always be passionate about something is is really motivating, and that's something that that I always try to relate to and and find success in.\u003C\u002Fp>\n\u003Cp>I mean, they're we're gonna have failures throughout our career, and we have to at least benefit from that in some way. You know, tying failure back to bureaucracy, you mentioned, Has there ever been a project throughout your career, something that you've been very passionate about and working, that maybe bureaucracy killed that project? Or, you you know, something got in the way and and it that project wasn't able to come to fruition. And one of the things that I've been thinking about lately is how this is, you know, the pace of AI and technology in that space and how everybody's working on these crazy projects or even cryptocurrencies. Right?\u003C\u002Fp>\n\u003Cp>These are projects that, at the end of the day, bureaucracy and or or governments or the law could eventually prevent or stifle innovation in these categories. And I I'd love to know if, you know, if you experience that through your career and working with these companies and what you might think of of people who are passionate and working on these projects, fearing that the same could happen to them.\u003C\u002Fp>\n\u003Cp>Santosh Ahuja: Yeah. I think a lot of the times you could find yourself in a project that that is set up to fail even before you enter it. That's possible. But I would again say that in order to, if you know, get into cryptocurrency, get into blockchain or get into AI because that that interests you. Not because there's a lot of money or whatsoever, but let let your passion and your interest be the reason for you to get into it.\u003C\u002Fp>\n\u003Cp>Again, bureaucracy and regulation and many other things might be might be a factor in your project, but, it's important to shun all of those away and just be focused at your craft. Just be focused at the task that you have at your hand and make sure you're doing everything in your, power to be to be successful at it. Right. There's only, again, there's only so much you can always control, be focused on what you can change and what, what is in your control and what's not you will when you don't have any control over it, there's no point in worrying about it.\u003C\u002Fp>\n\u003Cp>Pedro Pizzaro: That's true. And and and that's how I've kind of reacted to the cryptocurrency. You know, I've been involved in it for a little while. It it wasn't really to make money. Right?\u003C\u002Fp>\n\u003Cp>I'm just passionate about the space and the category, and I think, blockchain and these technologies could do good for the world. So, you know, I invest with my time and and my dollars. Unfortunately, some of those dollars have disappeared through the process. But, you know, it's like I'm I'll be okay if if all that money goes to 0. It's really about me trying to push forward the future that that I want to see, you know, for me and and future generations.\u003C\u002Fp>\n\u003Cp>So, it's definitely good to to hear you along the same lines.\u003C\u002Fp>\n\u003Cp>Santosh Ahuja: Yeah. I'll also add that, you know, you when you're in the technology space, you'll see a lot of that. You'll see a lot of hype. You'll always you'll always be part of you'll always see a lot of buzz, but then you'll have to look at past patterns, look at the history to see how long a particular fad will last. And that's that's the flip side of being in technology.\u003C\u002Fp>\n\u003Cp>There is something that will meet its promise and there's there's there's always gonna be a ton of things that won't. And you you have to continuously learn and be wary of that.\u003C\u002Fp>\n\u003Cp>John Daniels: Are there any of these future technologies or, I guess, we're now current technologies that you personally have a passion for? You know, we've talked about your your passion and and how it's guided you to where you are now. Are you passionate about AR or VR or crypto or something different? You know, what is your current passion leading you to to to work on these days?\u003C\u002Fp>\n\u003Cp>Santosh Ahuja: I'm, so so I'm currently passionate about 2 things, and this has been, and and this has been my, area of study currently. 1 is, semiconductors, manufacturing of chips, how we're pushing the boundaries, out there. How is Moore's law changing and how we're, what are the innovations that are happening that are happening in that space? Purdue is one of the first there's all also recently been the CHIPS Act, in the US that's really pushing forward the manufacture of semiconductors and and chips within the country so that, you know, companies here are self reliant and we can figure out a way to make, manufacture chips here locally. So that's definitely one area.\u003C\u002Fp>\n\u003Cp>And Purdue is one of the first, universities to come out with a course that centers around, semiconductors and microelectronics. So I'm doing that as my second specialization. The other one is, of course, artificial intelligence. Right? It's it's I think there came a point where, you know, it became obvious that, data data is the new you know, all companies start saying they still say data is the new currency.\u003C\u002Fp>\n\u003Cp>Right? Whoever has more and better control over their data can make faster decisions. And I think AI is, the next step in that direction. To be not acclimated with AI in some way, you know, let alone, you know, master it or know it really well at the core, is just, just being, want to be being ignorant or staying ignorant. So AI, artificial intelligence, because I've already done a lot of work on data, that's a big area of my study.\u003C\u002Fp>\n\u003Cp>And the other thing is, since I started my, career as an embedded engineer, the area of semiconductors and, chip manufacturing. Those are my 2 areas where I'm really focused on in terms of learning.\u003C\u002Fp>\n\u003Cp>John Daniels: If you could, if you could take a look back at your 10th grade self or current developers who are out there in the world, I think this would be a great place to kind of wrap up with your experience of where you've been, where you're headed. If you could give yourself and other developers out there maybe a quick, you know, a commencement ceremony pitch, you know, or or a, hey, motivational speech, what would you tell your 10th grade self or someone who's just getting started in development about chasing their passions? You know, how would you wrap up how they could kind of mold their growth along what you've done yourself?\u003C\u002Fp>\n\u003Cp>Santosh Ahuja: So just to summarize, I'd say, you know, keep the fire up in yourself. Never forget the reason why you were why you chose this field. Always do things that can, you know, bring a change in people's lives and, try to do things differently. Try all always push for, doing things that are innovative. Always try to do things, in a manner that's never been done before, And always, you know, be, be a medium be a medium of change.\u003C\u002Fp>\n\u003Cp>Don't be, you know, don't don't be repetitive. Right? You're in a space where there's so much change and so much innovation happening. So, you know, always keep things doing differently and be true to yourself. And, you're gonna that's the only way you're gonna be in this field for a very long time.\u003C\u002Fp>\n\u003Cp>John Daniels: Yeah. I think authentic is a good word. I think over the conversations we've had with you, I think you're you're definitely very authentic. And I think I think that's important these days is being authentic to yourself, being authentic with other people. And like you said, you know, chase your passion.\u003C\u002Fp>\n\u003Cp>Money will come alongside. You know? So if you chase money, you might not be happy, but if you chase happiness, you probably will find money along the way. So I think that's I think it's a really great wrap up. You know?\u003C\u002Fp>\n\u003Cp>And and and I think that's a lesson to be learned, and there were some risks that you took. There were some safe options you coulda had, but you chased your passion in the end, and that really led you to where you are and and that type of wealth. And being able to pass that type of wealth, that type of wisdom and information on to other people It's how we as humanity kind of continue to grow, and that's kind of what Pedro mentioned is that's what he feels cryptocurrency is is helping humanity along this this crazy journey. We're on this crazy journey. What AI is already doing, is very exciting.\u003C\u002Fp>\n\u003Cp>And people also forget that for the time being, AI is run by people, and helping other people kind of craft their journeys is pretty exciting.\u003C\u002Fp>\n\u003Cp>Santosh Ahuja: And, always have the childlike curiosity. Right? Never let that die. But that's very key that's a unique key to learn.\u003C\u002Fp>\n\u003Cp>John Daniels: Awesome, Santosh. It's been great. I think, is there any way that you know, are do you ever mentor people? Are people able to reach out to you, or is there anything that you wanna promote while you're here on at the end of this this, session? You know?\u003C\u002Fp>\n\u003Cp>Is there anything that you're working on that you're excited about? Are you willing to teach other people if they reach out\u003C\u002Fp>\n\u003Cp>Santosh Ahuja: to you? I've always helped out people who wanted to reach out to me. There are people who often reach out to me saying that, hey. You know, I have these these options. What do I do?\u003C\u002Fp>\n\u003Cp>In fact, at Purdue, a lot of people, Purdue, a lot of people that I study with are half my age. So they seek out advices and things like that. And, I tell them sometimes you have to do things to survive, but eventually you want to know what, what, what is, what is it that interests you and just stick to your passion, be true to yourself yourself, and successful definitely follow. That that's what I that's what I have to say. So, yeah, even if my work at Texas yeah.\u003C\u002Fp>\n\u003Cp>I think somebody from Texas A&amp;M University reached out and they said, okay. You know, we would want somebody to look at our post grads and things like that. I'm extremely passionate about education. So all also, if there are ways that you can contribute to the, to the society, through your learnings and through your skill, I think we should do that as well. That's that's that's for the betterment of, you know, humanity and mankind.\u003C\u002Fp>\n\u003Cp>That's that's the way way to go.\u003C\u002Fp>\n\u003Cp>Pedro Pizzaro: Awesome. Sweet. Well, we will wrap it up there. Thanks so much, Santosh, for joining us today. And, yeah, we really appreciate it.\u003C\u002Fp>","The code that Chuck Norris followed in the series Walker the Texas Ranger, you know, he the fact that he was so righteous, he was always honorable, and, and plus he had a bunch of flashy kicks. I was absolutely, drawn, to that person's personality and the role he played. Thank you, Santosh, for joining us today for an interview with us. We really wanted to to get to know you, you know, get get to know a little bit more about your background, maybe advice you have for other developers, and really use this podcast to, as a mindshare, for the community and and to learn a bit more about your background as well and and share that with the world. Right? You know, as you're a lead engineer at Optum, we're just really excited to to hear about your journey and, how you got here, what you're doing, currently, and and where you look to go in the future. So, if you'd like to introduce yourself and maybe a a brief on your background, we can get right into into, talking about it. Yeah. So hello. My name is, Santosh Abuja, and I currently work as a lead engineer at Optum, and I've been there for the last 2 years now. I also run my own consulting firm. It's called App Resilience, and, I have a couple of customers, so that's the direction I'm leading into. My background is predominantly I come from a, you know, business family. I have I completed my bachelor's in, in business and accounting. I did a bunch of courses in computers, right when I was in my 10th grade, but I soon landed a job as an engineer. And then from, there on, there's there's been no looking back, and, I'm here right now in in this country, in this beautiful country, called the United States, where people come to pursue their dreams, where people come to work with the best. And I've also had the fortune of, getting my master's done, at Purdue. So that's a big background. Yeah. I'd love to, I myself, you know, I have a a developer background a little bit, and I'm always curious to hear how people got into engineering and development. Sometimes people played with specific toys when they were younger. Sometimes they watched specific TV shows or movies that got them interested. So I'm I'm really curious to kind of hear that beginning, entry into engineering and development. What was it that got you interested? Was anyone a mentor of yourself, more could we would you be able to dive a little bit more into the beginning stages of your exploration into engineering? So it started when I was, you know, you could say young, you know, when you're young, when you're in your teens, you you're really trying to find out you're really trying to find out what you wanna do and carve a path for yourself, but you also have a little bit of that herd mentality. Right? You have a bunch of guys who start playing football and you start playing football with them. They start playing tennis. You start playing tennis with them. So I had a bunch of friends. They were from, you know, middle class families and they had, their path carved out for them. They had their parents who instilled in them a strong sense of, purpose in their life, education, and get really high paying jobs. I came from a business family and that was not the path for me. I already had a really cushioned, comfortable, and a clear path of taking on my family business. But, you know, because I had a bunch of friends who had a different path and, I just hung out with them a lot more. And, on one summer and every summer, they would always preoccupy themselves with some sort of a course in order to gather skills that maybe, you know, English speaking course or typewriting course and stuff like that. So that came a summer, I guess, in my 10th grade around when I was 13 or 14. A bunch of my friends enrolled in a computer class. And, really, it's because of them I owe my career to them because it's because of them I I joined that computer class and I really did well. Never saw computer science as a discipline of engineering, until I saw the amount of math that played an important role, in that, you know, in that in that in that profession. So that's how it started. And then what happened because I did a bunch of courses in computer science. I took up I took up a I took up a degree in business because, you know, my parents want me to, and, you know, we come from a background where we do a lot of things just to please our parents and our family and kinda did that, but also it had a minor in computer science. And I really did well, in that course. So after that, I, you know, I was still going back and, you know, was with my family, took on the family family business, run it for a while. But at the same time, my passion for computer science and programming grew. I continued to take a bunch of courses just because I was really, really interested in it, just because I had fun solving problems. And it so happened that, I just hung up with a bunch of friends when they were going for an interview, and I just happened to get selected. And, I really took on the job, and I haven't, in this I haven't looked back ever since. I was still within the same city, so my parents were okay with me going in a different direction for a while, hoping that I would find the going really tough, and I would just come back to the to the comfy life that I that I had. So that's how I got into engineering. That's awesome. That that your path sounds like, you know, you really hung out with a good group of friends, very motivated high schoolers. Mhmm. Very different from from my upbringing of growing up with a bunch of rascals just getting into shenanigans. Right? You sound like a very motivated, group of folks. Was that I know you've bounced around to a few different countries, and now you're here in the US. Was that back in India? Was that in Canada? Tell us a little bit about your your journey in in my career. Back in India. So I did I also had a bunch of friends who who were rascals and I got into all all different kind of shenanigans, but I knew that that kind of thing wouldn't get me too far. So I kind of played a safe role. I had my eggs in different baskets, if you will. And, and really, and also, you know, because of the sports background, I always had the sense of doing well, have a bunch of friends who I can compete with mentally, physically, and all that all that stuff. So I started back in a small town in India called Kaimatar, and, the short form is Kwendi. It's really known. It's a it's a big hub for educational, for education, a lot of universities out there. And I started with a company, that made software for British Telecom. It was that was that was the only software company in the city and was really, really, really, really hard to get into, that company. But I started because of my curiosity, I started learning a bunch of courses really fast, and, I started teaching in different universities. That's what I did when I didn't get jobs and I eventually got a gig with them. And, that's how it started. So before it was not until a few years later that I really got a chance to go to Canada to work for some web companies, because I started with a company, you know, who were developing software for British Telecom. I stayed with the same domain, did a lot of embedded programming, and then soon got attracted to a couple of companies who started doing a lot of VoIP, and voice or IP. And then, that landed me to Canada because Canada had Canada had a lot of up and coming, big, white companies. And some of the companies were really, paving the way for innovation and how, video and audio was being streamed over a call and how all these calls that we traditionally do with COPPA lines were really becoming digital. Right? So I spent quite some time in Canada working there, working for somebody, and that was really an eye opener. And, because there again, I saw a really, I saw a lot of top engineers. I learned a lot of them, learned learned a lot from them, and that really proved me as a person, as an engineer. I then came back because I was not a permanent position, in Canada. I I was really doing work for an Indian company, but I was engaging with a bunch of innovators in Canada and working in developing some products. And then I came back and, I I was at a point where I was switching jobs again. And then I worked for a company, that was Venus out of Richardson. And when Richardson was called, Telecom called it out. We do it. Right? A lot of telecom companies in Richardson, like, NCI and there's AT and T over here that is also, you know, Texas Instruments, both hardware in terms of software and things like that. So, I started working for this company, and then, this company was had the business of a startup, the whole, you know, the whole it was really it was really a technical company at the core. Even the founders were past architects of some really successful startups and things like that. So when I joined this company, they were an exclusive shop of a big up and coming start up. And this start up had and this startup was based out of Richardson and the founders of this startup had senior entrepreneurs. They had, they had a history of building successful products and with multimillion dollar exits, like 100 of 1,000,000 of dollars. So I got a chance to work pretty early on with such a company. I was core engineer, but then they also had a bunch of engineers here, and I really got a chance to work with some of the really good engineers from companies like Nortel, Cisco, you know, that and that really, again, grew me as a person. It pushed me. I I developed my skills a lot. Pretty much been with the same enough that I was pretty much the same company. As over the years, this company transformed itself into a full fledged consulting company making software for the web, the mobile, we literally saw an age where, mobile devices, change from mono those monoprint screens to really Apple like large screens with large real estate, and you would really touch on that screen and see things happen. Right? So you got into that smart, smartphone age and, where these our phones really started taking place our traditional desktops. Traditionally, we had desktop. We've installed different apps on them, played different games on them. Now we're getting into an age where we could do all these things on smartphones. And that's when, this company started making apps, saw the opportunity, started making apps for, a lot of, you know, different kind of smartphones. Yeah. It's it sounds to me like there's a there's a common thread that kind of weaves its way throughout your life and your career. I'd like to kinda take that road for a second. You talked about when you were younger, and you had this this opportunity to kind of follow the footsteps of your family, which to me, honestly, sounds like a safe route. And I feel like I feel like for those of us who are in technology, we traditionally don't take the safe route. I think part of being in technology and engineering and development is kind of finding a way to solve a a problem in a different way. You come across an issue and there's there might be a safe route where you can do something, but why not take a different route, especially if that different route leads you in fun and unexpected directions. So you had this opportunity when you were younger to follow in a safe path, but you kind of chose the you you went along with your friends. You you succumb to peer pressure, and you went into the engineering route. And it kinda sounds like you also went that route as you started to get into your career as well. You you made your way to a small start up where there was a lot more excitement, a lot more risk. There was a lot more what ifs and unknowns. But in the end, it really kind of was the best choice for you, and that kind of stemmed back to your childhood. So in your opinion and, you know, there's there's people out there watching who have this decision all the time. Do they go the safe route? Do they go the more adventurous route? That's that might be more risky. Do you find that being an engineer in in technology, that is the the better route to go, to be a little bit more daring, to take more risks, you know, not only professionally, but also personally? Is that something that you feel has led you leaps and bounds to where you are now and you'd be in a completely different place if you had gone the safe route? Yeah. You know, the answer I think the right answer to that is partly I've I've never been, you know, I've always, you know, I have, I've always been encouraged to take risks, but also the other part is I've always followed my passion and my intuition. So I always have a suggestion saying that never chase money, chase your passion. If you have an intrinsic feeling, that pushes you towards a certain direction, just as long as your intentions are honest, you're honorable in your, in your in your intentions and in your goal, you're you're gonna be fine. So that's what led me to take the decisions that I, that I took. I always also, there was always a saying that when you join with us, when you join a smaller company, you get a lot more responsibilities. There's a lot of lot more opportunities to take more responsibilities and you kind of mature faster. The other part is also with some of the smaller consulting companies, they're they can't compete with larger companies. They're really jumping on every kind of opportunities that they get. So even if they're, they're risky opportunities and, I've always been someone who's I haven't I've never thought too much. When I look at an endeavor, I really look at what it offers me in terms of growth. I look at it on the challenges, on the on the challenges that it poses and the opportunities to learn. I don't get in the political side of what if I fail or if I fail, how would I be perceived? What do I look at? Would I make a fool of myself? That's not something that I really thought. It's okay to fail, but it's important in my view to always take up opportunities and, risks ahead as long as you are honest in your intentions and you're honest to your passion, and you have the will and the determination to succeed, you'll figure out a way. Otherwise, you're you're going to in any ways, if you feel you're going to learn so much that you're gonna be successful afterwards. Yeah. Is is that something that drove you? You spoke about, you know, building your own consulting company and and having a few customers there. Is that the mentality that drove you to creating that consulting company? And you mentioned there's a lot of challenges competing with the big players Yeah. In the market. I'd love to learn a little bit more about those challenges and and how you've overcome those as well. I mean, it's I'm I'm still in in in those challenges because this this the technology space is so big, so wide, and it's growing so much that that's enough for, you know, there's enough for everyone. So that's again why, you know, running my own consulting company gives me an opportunity to choose the projects that I want to work on, work with different people, go to different places and pick up riskier projects and really, win them over and succeed at them. So with bigger companies, because they have so many options, they have the luxury to cherry pick what they wanna pick up and what they don't want to. But then there are some pearls that they often leave out because they think they're too risky. And that's that's the opportunity that you need to jump on and pick up with both your hands. And, if you do that sooner or later, you'll definitely find success. And I like, and then, and that's how I like this, feeling of really being, you know, independent and being able to work, look at, you know, the whole globe, you know, the whole world as, as the playground and open up my window to all different kind of opportunities and just work on on different things and have that same mentality of taking on risks, pursuing your passion, and doing what what really, what really drives you. Yeah. That's that's awesome. And to I mean, you started as a a child growing up in India, and then you've made your way to Canada and even Texas. You know? Growing up, could you have imagined yourself where you are today? And was there any sort of inspiration to go to Canada or inspiration to go to to somewhere like Dallas? Any anybody growing up as a kid that you might have looked up to or or seen as a as a an excitement to to make that type of worldly journey. You know? Because I've grown up in the US my whole life, and I do have goals to travel worldwide, but it's more so travel. But your exciting development journey is taking you to live in all these places. So taking back a look at your childhood growing up and even into 10th grade when you started to get into it, was there any draw to make your way to either Canada or US and kind of explore this worldly journey along with that development engineering journey? So so I'll tell you this that, the lure of moving to the west or travel was never there. I I was pretty westernized in my thoughts and in my, the way I grew up when I was when I was a kid. All my friends, my engineering friends, have ended up in the US and they're really in big positions. But there comes a point that as you get better and better and better and better at your, at your profession, you're always going to be steered in a in a place where the best talents meet, where you can rub your, you know, where you can rub your shoulders and get better. For example, if you really happen to be a great football player, you can play all the local leagues, but as your skill goes, gets better and better and better, you're automatically going to land up in NFL. That's, that's how I see it. That's how I saw it. There came a point where, we need to the stage and really, rubbing shoulders are the best and working for some really large and big companies. Mobile was the way to go. And really there were a set of events that unfolded that got me here automatically. Like I didn't even have to really try. It was not like sitting back in India and said, you know what? I'm gonna move to the US or I'm gonna move to Canada. It's just the opportunities, the way they present that themselves. And me being not risk weary and taking everything heads on has landed me here. Yeah. That's that's awesome. And, you know, I know we spoke before this just briefly, but you touched on how, you know, this this is an ongoing journey. This is an ongoing process. You're always out there looking for new information and and, you know, keeping up to date with yourself and your education. I'd love to know 2 things. You know, what are you doing in that way to to to do the same? You know, if you're at heart, you're an engineer. So, you know, you if you if you're an engineer and if you wanna be successful, you've you've committed yourself to a life of, a lifelong learning. So you, you have to understand that. And also I've always, because of maybe because of the background that I come from, I've always had a lot of respect for education. I I feel that the the all these institutions, still instill a bunch of good values. And also it's more so because what what it does to you in terms of the other people you meet at such institutions, you meet other people who are also are mostly extremely hardworking, driven. They have they have, big dreams and they really wanna change the world and so on and so forth. When you are with with such a group, it's it's, it's inevitable that you will have they'll have a profound impact on you. So it's basically that part that, you know, that that makes, doing a course at one of these big universities so attractive. Otherwise, you know, we're in an age where you can learn by yourself. You can look at YouTube videos, you can go to Coursera, but also, I've been very picky at where I wanted to study, and pursue my continue my education. And some of the universities that are really in the top 10 or 20 have outstanding, educator educate educationists and professors who know their subject really, really, really, really well. So it's just being among that group, among that highly educated, curious people that, that has a good strong influence on you. It's always been that way right in when I was scared. I've hung up a bunch of engineers and that's how it got me here. Again, giving when you put yourself among a certain group like that, it's the it's the other people. It's the it's the professors that have a strong influence on on your thought process. Was there ever a time, you know you've you've been through an amazing journey and and really impressed by it, honestly. Was there ever a time either early in your career where you felt maybe I'm going down the wrong path, or were you always, you know, I'm taking on this challenge, and I wanna see this challenge through completion? Or maybe a time later in your career where you just got so, so frustrated with, the status quo, which it sounds like you probably didn't go down that journey of always doing the status quo. But was there ever a time where you just got so frustrated at something or so unsure about the path that you maybe second guessed? Or have you always been the type of person who says, I'm gonna go on this path. I'm gonna go to Purdue and finish this degree at Purdue. I'm gonna go to this company and complete this project. Have you always been that type of person? Yeah. And just to add just along those lines, like, how do you know when to quit? You know, if you're so determined to take something through to the end, you know, are is there a time to quit? Is it appropriate to quit at certain times, and and how how do you know when you're on that path? So I it's obviously in any careers, you know, there are ups and downs. I saw that the 2,000.com bust, then I saw the white bust. There are depressions. I think everybody who's a professional has, you know, goes through their doubts, their career and even at workplace, you know, sometimes there is bureaucracy and there are other things that really are a definite, are a letdown. Right. And, there are those feelings that lurk in that says, you know, maybe, you know, I should have taken that at all, maybe I'm in the Olympics, but you as long as you stay true to your passion, true to your course, you have to get your happiness out of that. You have to kinda shun everything else away and see, hey, you took up you went this route because you were passionate about what you were doing. And I think for many people, it's like that. There comes a point when someone's working in a company, there are things that really affect them, mentally and emotionally. But the way they're able to deal with it is to get invested fully in the work that they're doing. Right. But if you're not, the problem comes, then you made the decision to be here because of money and not because of passion. If it was because of money, then you're gonna say, hey, the money isn't great, and I'm going through this emotional and, you know, mental torture, it's not worth it. So that's why I keep saying the only way to sustain yourself for any endeavor is to be passionate about it. Be passionate about it and enjoy enjoy your craft, enjoy getting better at it, and, that's what, that's what will keep you going. And I've I've never been a quitter. I've always once I take, because once you once you quit, quitting becomes a habit. And then succeeding unfortunately, succeeding also becomes a habit. So you decide what you want. And it's like you you have one it's it's going back to your sports background. Right? If you have one bad performance in the season, it kinda leaves an impact on you. It kinda rubs on you. So always end something on a good note, always end something on a positive note, on a positive performance, and keep repeating yourself. And it's okay to fail. And then there are multiple theories that are saying that you must fail fast or and so on. But as long as you learn from it, you're learning from your mistake, you're eventually gonna succeed. Wow. Yeah. This this drives me back to as Pedro said, we have spoken before. And this type of line of thinking of grit and drive and determination and not being afraid to fail, it does remind me and I I feel like I would be remiss to not bring it up. When we spoke before, you mentioned that you were a huge Chuck Norris fan. And when I think of Chuck Norris, I think of drive and grit and determination and never being afraid to to fail. Do you think that either Chuck Norris has influenced your personal life and or vice versa? You've you've were drawn to him because that's how you were personally. And can you kind of dive a little bit in? I I do think it's kind of fascinating how you're a fan of Chuck Norris. I'd like to get into that just a little bit. So as I grew up, you know, I, you know, I grew up as a relatively obese kid, but I I loved action. I loved martial arts a lot. So I took up, I took up martial arts when I was 12. I enrolled in the class of taekwondo, then I went on to represent my state, my country in the sport. Back then, you know, when I when I was in my teens, we started getting, foreign channels and foreign TV content on our on our TVs. And that's when I I, I followed a series called, Walker, the Texas Ranger. Right. And even before that, I'd seen a couple of movies of Chuck Norris called the force of 1 and things like that. What really drew me, of course, you know, there've been many martial artists and they were all extremely skilled, but because of the code that Chuck Morris followed in the series Walker, the Texas Ranger, you know, the fact that he was so righteous, he was always honorable and and plus he had a bunch of flashy kicks. I was absolutely, drawn to that person's personality and the role he played. And also his, you know, his sweet southern demeanor is extremely reflective. The kind of, the gentlemanly manners down from the south. And I and I later on, I dug out that he's really from Oklahoma. He's from down south. Right. He's from Oklahoma, then he moved to Texas. So for many years, you know, I just wanted to be like this guy. I wanted to be righteous. I wanted to be brave, and I wanted to be honorable, and so on and so forth. But I never thought that, I'd land up in Texas. As I always, you know, said that for me for most of the people, this is Texas. For me, this is Chuck Morris Lane, and that I felt good validation in the world. I have passed, I think, this ranch. It's called, the lone wolf ranch, and it's really inspired from the from what we call, lone wolf McQuade, you know, where he, plays this role and he's his name is McQuade and then he's called Lone Wolf, and he drives drives around an old, self broken, Toyota truck. So so when I arrived here, I got myself a truck. I still practice martial arts, and I still I still look up to Chuck Norris because I think he's a class act. Even in the world of martial artists, arts, martial arts, he's still extremely respected. I keep wondering why they, you know, why they haven't, gotten him to be a president of this country. I think he's a Well, if he sees us, who knows? Celebrities are getting into it these days. So Yes. Yeah. And it's it's you. Now you're now you're the Texas Ranger, so it's it's awesome that there's still one roaming around there, active. Right? But I I wanted to bring thing something back to failure, which is, something that obviously Chuck Norris had no part in. He didn't obviously, Chuck Norris never failed. But but you spoke about failure being acceptable and and really being passionate about the work that you put in and always trying to learn something. That way failure is always an opportunity. Right? And and to always be passionate about something is is really motivating, and that's something that that I always try to relate to and and find success in. I mean, they're we're gonna have failures throughout our career, and we have to at least benefit from that in some way. You know, tying failure back to bureaucracy, you mentioned, Has there ever been a project throughout your career, something that you've been very passionate about and working, that maybe bureaucracy killed that project? Or, you you know, something got in the way and and it that project wasn't able to come to fruition. And one of the things that I've been thinking about lately is how this is, you know, the pace of AI and technology in that space and how everybody's working on these crazy projects or even cryptocurrencies. Right? These are projects that, at the end of the day, bureaucracy and or or governments or the law could eventually prevent or stifle innovation in these categories. And I I'd love to know if, you know, if you experience that through your career and working with these companies and what you might think of of people who are passionate and working on these projects, fearing that the same could happen to them. Yeah. I think a lot of the times you could find yourself in a project that that is set up to fail even before you enter it. That's possible. But I would again say that in order to, if you know, get into cryptocurrency, get into blockchain or get into AI because that that interests you. Not because there's a lot of money or whatsoever, but let let your passion and your interest be the reason for you to get into it. Again, bureaucracy and regulation and many other things might be might be a factor in your project, but, it's important to shun all of those away and just be focused at your craft. Just be focused at the task that you have at your hand and make sure you're doing everything in your, power to be to be successful at it. Right. There's only, again, there's only so much you can always control, be focused on what you can change and what, what is in your control and what's not you will when you don't have any control over it, there's no point in worrying about it. That's true. And and and that's how I've kind of reacted to the cryptocurrency. You know, I've been involved in it for a little while. It it wasn't really to make money. Right? I'm just passionate about the space and the category, and I think, blockchain and these technologies could do good for the world. So, you know, I invest with my time and and my dollars. Unfortunately, some of those dollars have disappeared through the process. But, you know, it's like I'm I'll be okay if if all that money goes to 0. It's really about me trying to push forward the future that that I want to see, you know, for me and and future generations. So, it's definitely good to to hear you along the same lines. Yeah. I'll also add that, you know, you when you're in the technology space, you'll see a lot of that. You'll see a lot of hype. You'll always you'll always be part of you'll always see a lot of buzz, but then you'll have to look at past patterns, look at the history to see how long a particular fad will last. And that's that's the flip side of being in technology. There is something that will meet its promise and there's there's there's always gonna be a ton of things that won't. And you you have to continuously learn and be wary of that. Are there any of these future technologies or, I guess, we're now current technologies that you personally have a passion for? You know, we've talked about your your passion and and how it's guided you to where you are now. Are you passionate about AR or VR or crypto or something different? You know, what is your current passion leading you to to to work on these days? I'm, so so I'm currently passionate about 2 things, and this has been, and and this has been my, area of study currently. 1 is, semiconductors, manufacturing of chips, how we're pushing the boundaries, out there. How is Moore's law changing and how we're, what are the innovations that are happening that are happening in that space? Purdue is one of the first there's all also recently been the CHIPS Act, in the US that's really pushing forward the manufacture of semiconductors and and chips within the country so that, you know, companies here are self reliant and we can figure out a way to make, manufacture chips here locally. So that's definitely one area. And Purdue is one of the first, universities to come out with a course that centers around, semiconductors and microelectronics. So I'm doing that as my second specialization. The other one is, of course, artificial intelligence. Right? It's it's I think there came a point where, you know, it became obvious that, data data is the new you know, all companies start saying they still say data is the new currency. Right? Whoever has more and better control over their data can make faster decisions. And I think AI is, the next step in that direction. To be not acclimated with AI in some way, you know, let alone, you know, master it or know it really well at the core, is just, just being, want to be being ignorant or staying ignorant. So AI, artificial intelligence, because I've already done a lot of work on data, that's a big area of my study. And the other thing is, since I started my, career as an embedded engineer, the area of semiconductors and, chip manufacturing. Those are my 2 areas where I'm really focused on in terms of learning. If you could, if you could take a look back at your 10th grade self or current developers who are out there in the world, I think this would be a great place to kind of wrap up with your experience of where you've been, where you're headed. If you could give yourself and other developers out there maybe a quick, you know, a commencement ceremony pitch, you know, or or a, hey, motivational speech, what would you tell your 10th grade self or someone who's just getting started in development about chasing their passions? You know, how would you wrap up how they could kind of mold their growth along what you've done yourself? So just to summarize, I'd say, you know, keep the fire up in yourself. Never forget the reason why you were why you chose this field. Always do things that can, you know, bring a change in people's lives and, try to do things differently. Try all always push for, doing things that are innovative. Always try to do things, in a manner that's never been done before, And always, you know, be, be a medium be a medium of change. Don't be, you know, don't don't be repetitive. Right? You're in a space where there's so much change and so much innovation happening. So, you know, always keep things doing differently and be true to yourself. And, you're gonna that's the only way you're gonna be in this field for a very long time. Yeah. I think authentic is a good word. I think over the conversations we've had with you, I think you're you're definitely very authentic. And I think I think that's important these days is being authentic to yourself, being authentic with other people. And like you said, you know, chase your passion. Money will come alongside. You know? So if you chase money, you might not be happy, but if you chase happiness, you probably will find money along the way. So I think that's I think it's a really great wrap up. You know? And and and I think that's a lesson to be learned, and there were some risks that you took. There were some safe options you coulda had, but you chased your passion in the end, and that really led you to where you are and and that type of wealth. And being able to pass that type of wealth, that type of wisdom and information on to other people It's how we as humanity kind of continue to grow, and that's kind of what Pedro mentioned is that's what he feels cryptocurrency is is helping humanity along this this crazy journey. We're on this crazy journey. What AI is already doing, is very exciting. And people also forget that for the time being, AI is run by people, and helping other people kind of craft their journeys is pretty exciting. And, always have the childlike curiosity. Right? Never let that die. But that's very key that's a unique key to learn. Awesome, Santosh. It's been great. I think, is there any way that you know, are do you ever mentor people? Are people able to reach out to you, or is there anything that you wanna promote while you're here on at the end of this this, session? You know? Is there anything that you're working on that you're excited about? Are you willing to teach other people if they reach out to you? I've always helped out people who wanted to reach out to me. There are people who often reach out to me saying that, hey. You know, I have these these options. What do I do? In fact, at Purdue, a lot of people, Purdue, a lot of people that I study with are half my age. So they seek out advices and things like that. And, I tell them sometimes you have to do things to survive, but eventually you want to know what, what, what is, what is it that interests you and just stick to your passion, be true to yourself yourself, and successful definitely follow. That that's what I that's what I have to say. So, yeah, even if my work at Texas yeah. I think somebody from Texas A&M University reached out and they said, okay. You know, we would want somebody to look at our post grads and things like that. I'm extremely passionate about education. So all also, if there are ways that you can contribute to the, to the society, through your learnings and through your skill, I think we should do that as well. That's that's that's for the betterment of, you know, humanity and mankind. That's that's the way way to go. Awesome. Sweet. Well, we will wrap it up there. Thanks so much, Santosh, for joining us today. And, yeah, we really appreciate it.",[5451,5452,5453],"f9844f3e-c7b7-4827-ae0b-31cd0a0d8580","64e37381-ebc3-45f8-b55a-3d882b88143e","ec80b198-f644-4258-a1f2-53196bb43431",[],{"year":2380,"number":739,"id":4539,"episodes":5456,"show":5457},[4541,4542,4543,4544,4515],{"title":1849,"slug":1850},{"id":4166,"slug":5459,"vimeo_id":5460,"description":5461,"tile":5462,"length":5463,"resources":12,"people":5464,"episode_number":264,"published":5422,"title":5466,"video_transcript_html":5467,"video_transcript_text":5468,"content":12,"seo":12,"status":19,"episode_people":5469,"recommendations":5471,"season":5472},"swag-platform","894282937","Gifting platforms are how we get goodies in the hands of community members and customers. Bryant needs to figure out how to manage products, build pages to handle the giveaways, and manage fulfillment.","9c659211-390a-4bfa-afd4-21fd08c80fbe",67,[5465],{"name":3590,"url":3591},"Mission: Swag Platform","\u003Cp>Speaker 0: Alright. Alright. Alright. Welcome back to the next episode of 100 Apps 100 Hours where we rebuild or build some of your favorite apps in 1 hour or less or die trying or publicly fail trying is probably more appropriate. I'm your host, Brian Gillespie, developer advocate at Directus.\u003C\u002Fp>\u003Cp>Welcome. Super excited to have you. Today, we are going to be building a gifting platform. Oh, Brian, that sounds really exciting. Actually, to me, it is.\u003C\u002Fp>\u003Cp>So let's dive into the details of this. I've recently gone through at Directus and set up a swag program. So we've got tons of users worldwide that use our software getting, merchandise like branded cups or mugs or t shirts, into the hands of our user base, incredibly difficult challenge when you consider that we've got users in the US, we've got users in Germany, France, Namibia, just every country that you can think of. We've got people that we would like to thank and show appreciation for. Very challenging.\u003C\u002Fp>\u003Cp>So inter gifting platforms. We are going through the process currently with a company called swag.com. They offer a shopping cart and e commerce experience where you can log in and, basically pick your swag, upload your designs, and place orders that they will then fulfill for you. There's quite a few of these types of companies like Sendoso, I found another one called SwagUp, and this one is Codus Design. It basically a swag platform where you place an order, they manage all the fulfillment and inventory and actually delivering those items to your clients or your customers or your potential leads, potential prospects.\u003C\u002Fp>\u003Cp>They handle all of that for you. The part that I was very interested in, and I'm not discounting the fulfillment and warehousing and all the other bits and bobs that are included in here. That's probably the really difficult stuff. But, our past ecommerce or past swag solution was a Shopify store. And when you're trying to send somebody a thank you gift, it feels a little weird to have them check out on a Shopify store and enter a coupon code to actually get that get that swag or get that gift that you're trying to send them.\u003C\u002Fp>\u003Cp>And one of the reasons why I really like these platforms is the ability to send out a thank you page that looks very similar to this. There's there's no checkout. They don't have to enter a credit card or a coupon code. I send them a unique link. They can pick their swag, and we get them to enter in their information like their first name, email, country, and address so we know where to ship it.\u003C\u002Fp>\u003Cp>It is a great experience for them, great experience for us, win all around. That's what we're gonna be building today. Let's dive in. Just a quick recap of the rules. There's 60 minutes to plan and build, so that's plan and build in 60 minutes no more no less, And aside from that there are new rules.\u003C\u002Fp>\u003Cp>We'll use whatever we have at our disposal whether that's AI or all sorts of other cheat codes from around the Internet. Let's get started. We're gonna dive in. Alright, so before I build any apps, I usually like to take at least 5 minutes to plan. These planning sessions are always fun because it's hard to test your assumptions to get feedback when you have 5 minutes to plan an app.\u003C\u002Fp>\u003Cp>But here is my initial functionality that we're gonna build into this app. We're gonna need to manage our products and product options. We will need to, create a giveaway page or several different things that the the companies that I looked at called this, like a redemption page. I think giveaway is probably a good one. Thank you page, create a giveaway, a redemption page.\u003C\u002Fp>\u003Cp>Place an order, place a request. Not sure it's a order because it's free, but hey, that's what we'll do. User places a request, k, let's say user can place an order request, and then we manage and fulfill those through the app. Alright. So that's the app that we're gonna build.\u003C\u002Fp>\u003Cp>There are 2 components. There's a back end. There's a single page on the front end. What are we gonna need as far as our data model? What is that gonna look like?\u003C\u002Fp>\u003Cp>So we'll jump in and let's do, we certainly have products. Oh, I'm not ready to drag arrows yet. We've got our products. What is next? We've got our giveaway page, right?\u003C\u002Fp>\u003Cp>Giveaway. But then we have customers, you know, we'd probably wanna track that as a separate table or those could just be people. And then we'll have orders and probably some actual order items. So when we look at this, all our products roll up to a giveaway and we this is probably gonna be like a mini to mini relationship because we could have many different products on many different giveaways. When we go to products those are gonna be to the order items, customer orders or items will be orders attached to giveaways.\u003C\u002Fp>\u003Cp>I I just really like drawing arrows half the time. So as far as the rough data model, this looks pretty good. You know, curious to hear your feedback if if you set up ecommerce before, especially on how you handle like product variants and things like that. But let's dive in and not waste too much more time. Let's actually start building this.\u003C\u002Fp>\u003Cp>I'm just gonna move this off to the side. What does our setup look like as far as how we're gonna build? I've got a local Nuxt starter app set up. I've got a Directus folder where I've got a Docker Compose file that just simply creates my Directus instance that we're gonna be using on the back end. And then this Nuxt application just has a SDK for Directus already pre configured with a plugin using the the Nuxt plugins and, you know, nothing too fancy.\u003C\u002Fp>\u003Cp>The only, like, major difference here is there's a composable, view composable included for making the Directus requests, because Nuxt has that nice auto import feature. Alright, so let's log into our Directus instance and start building something right away. Let me look at what I'd use for my password here, just paste that and as we look let me get my data model up in view so we can actually start building. But as we look, this is a blank instance, I'm not doing any kind of crazy cheating here. Let's start building.\u003C\u002Fp>\u003Cp>So we'll create a new collection for products, we'll track some dates on this, you know, is this a draft product or not? Maybe we don't really need that for this particular instance. Alright. So on our products, what do we have? What are we using?\u003C\u002Fp>\u003Cp>We've got a name for our products, that's great, that's just a string. We've got a description for our product, We probably have some images for our product. So we've got some images. This is going to be using the relational fields inside Directus. And what's happening behind the scenes here as I'm building these collections and adding fields to it, the Directus API is mirroring this setup inside my Postgres database.\u003C\u002Fp>\u003Cp>And at the end of this, as soon as I get done building this data model, I'll have a ready to go API that I can then call on my front end to render those products. So we've got name, description, images, we're probably gonna have some options for our products like color and size. A more robust setup here would be to create separate collections for those, you know, a a product options and then maybe a product options values so that that could be dynamic. To streamline this, I think I wanna go with the repeater inside Directus. So this is just a JSON store and we'll call this options.\u003C\u002Fp>\u003Cp>So our options, we're gonna edit the fields here. The first option is gonna be the name of the option and then that'll be a string. We require a value for that, that'll be an input. It's fine. Let's make it full width and then next let's add values.\u003C\u002Fp>\u003Cp>So we're gonna get a little meta here and then I am going to embed a repeater within a repeater. Sounds pretty wild. So the field here, let's call it value. And I could probably have like a label and value in case the the value that I wanted to track was different than the label, but this looks pretty okay. We'll hit Save.\u003C\u002Fp>\u003Cp>We've got name, we've got description, we've got images, you know, if I had something like a SKU we can work on that, but let's keep it simple, right, let's keep moving. What's next? We have our giveaway. So this is gonna be a giveaway page, and if I pull up one, like, the direct to sample, we've basically got a logo, we've got a headline, we've got some description, then we have the products and we have a form. So let's add a table for giveaways.\u003C\u002Fp>\u003Cp>We'll go in and we'll set a status on the giveaway if this is published or not. Let's go in, we'll create a new field, we'll call it headline. We'll just make that a string. Then we have a description that we could set. So we'll do a description.\u003C\u002Fp>\u003Cp>Again that will be rich text so we could potentially have, lists or, you know, bold italicized text. And then we want to add our products. Right? So with our products, we're gonna use the relationship features inside Directus, and we're going to use that many to many relationship. Because one giveaway could have many products, and we could use many products on many giveaways on we use one product on many giveaways.\u003C\u002Fp>\u003Cp>So the let's call this field products. Our related collection is also gonna be products, and we don't want to allow duplicates. We do wanna show a link to the item so we can go back to that product. And I'm just gonna open this in advanced mode. So behind the scenes, what's happening when we use this many to many relationship?\u003C\u002Fp>\u003Cp>Inside the Directus or inside our Postgres database, Directus is creating a junction table called giveaways_products. Now I could adjust what that table is called, and even the foreign keys that it's setting up inside that junction collection, what it's naming those. But I'm just gonna go with autofill in this case. If I want to add the reverse relationship back on the products, so if I wanna reference those giveaways on the products themselves, I can do that here as well. And then maybe we add a sort field just to control the order.\u003C\u002Fp>\u003Cp>Maybe I want the shirt to show up first before the other items. Great. Directus is gonna tell me what all it's going to create inside my database, and now we've got our products. What else do we need? Do we need anything else here?\u003C\u002Fp>\u003Cp>The form, maybe we hard code that. Great. So if we look what else? We've got a customer, we've got orders, and order items. Alright.\u003C\u002Fp>\u003Cp>So let's go ahead and set those up as well. So we got a customer, actually let's just call it persons, people. Naming stuff is always the hardest thing. This may not be a customer. Right?\u003C\u002Fp>\u003Cp>So let's just go with the more semantic name and call it people. So people have a first name, they have a last name, they have an email address, and are we what else are we capturing? Capturing an address for those people. I'm assuming that could be on the order itself. Let's just roll with this.\u003C\u002Fp>\u003Cp>Right? First name, last name. Maybe we'll capture the address on the order. We could store it here as well, but we'll keep it simple. So we've got then we have an order and the orders are going to what?\u003C\u002Fp>\u003Cp>What are we gonna have on the order? The order is going to have a customer or a person, where we who's the person attached to that order, that's gonna be a many to one relationship. Alright. So we got the people, person, people, are already regretting this decision. No turning back now.\u003C\u002Fp>\u003Cp>Right? And then on the reverse side of that, on our people, we'll show the orders. People orders within the data model. Great. Alright.\u003C\u002Fp>\u003Cp>So the person we have, an address, let's just store that as JSON data for now. So we'll do an address and what else? We have a status is approved, maybe. This is approved, so we want approval before we ship all of these out. Let's add that to the top.\u003C\u002Fp>\u003Cp>And then last but not least, we want to create the actual items involved with this specific order. So we've got an order items table or you could call it orders items. Again, we get into splitting hairs here. What else do we need? We probably do need a sort for those blah blah blah.\u003C\u002Fp>\u003Cp>Alright, so we got our order, we got our order items. The items are gonna have a link back to the product. So we've got a related collection is gonna be the products. So when we put those items in the cart, the order item will have a product, so that'll be a relationship and then let's just have a JSON field to store our product options. Maybe we call it item options just to keep them separated.\u003C\u002Fp>\u003Cp>Good. And then there is a 1 or many to 1 relationship. So there are many items to one order, back to that order. So we've got the order, the related collection is orders. Tell me I didn't.\u003C\u002Fp>\u003Cp>I did, didn't I? So this is a boo boo. I've got orders here. It should be or I've got order should be orders. That should be a a fun little thing to work around.\u003C\u002Fp>\u003Cp>Now, I could go into the database itself and edit that if I wanted to, but, no worries. We'll just roll with what we got. There's an order. It's a order. Sometimes you goof up.\u003C\u002Fp>\u003Cp>What do you do? You keep rolling. Alright. So we've got our data model. Right?\u003C\u002Fp>\u003Cp>Looks great. Let's go in and maybe add a giveaway. And we can just duplicate this one, right? Directus Swag. Still learning Arc.\u003C\u002Fp>\u003Cp>I like it as a browser. Yeah. If anybody out there is a Arc expert, maybe you could give me a couple lessons like a tutorial session or something. Alright, so we got our headline, we've got a description. Let's add a product, we don't have any products at this point, so let's create a product.\u003C\u002Fp>\u003Cp>We've got the Directus, let's let's call it the Bunny Tumbler 20 ounce, Great. This is the most amazing tumbler ever. Great. Now we want to add some images to this. So, I can actually import images from a URL inside Directus, which is super nice, especially for speed runs like this.\u003C\u002Fp>\u003Cp>I just click upload a file, click the little link that will import our file. Looks great. For our options, we've got color and for the value, we've only got one. It's just white. We're gonna offer that in white.\u003C\u002Fp>\u003Cp>Great. Okay. Let's go in and add one more product. We've got our Directus logo tee. Kinda sounded like goatee, didn't it?\u003C\u002Fp>\u003Cp>This is the softest t shirt you'll ever wear, and again, we'll just go in and let's copy the image address and you can see we've got a couple options here. We've got extra small through 2 x, so we'll add those in there as well. Just upload our file. Great. For our options, we've got color, which is a dark heather.\u003C\u002Fp>\u003Cp>I believe I should know, I'm the one who ordered this stuff. And then let's add another option for what size. Alright. So we got extra small, we got small, we got medium. It'd be really handy if we could just do this in line, wouldn't it?\u003C\u002Fp>\u003Cp>I'm sure there's a way. Where there's a will, there's a way. Alright. So we've got some extra small through 2 x. We've got our products.\u003C\u002Fp>\u003Cp>We've added them to a giveaway. Let's go ahead and publish this giveaway and hit save. Great. So we've got our giveaway, and let me just prove it to you. Let's open up Directus.\u003C\u002Fp>\u003Cp>Alright. So this is our actual database here. We can see we've got order. We've got some directus tables that directus has created for us, and then we have giveaways. So there's our data.\u003C\u002Fp>\u003Cp>Directus prefixes all the metadata tables that it so it keeps the SQL database pure. That way, you know, you're not locked in or anything like that if you decide to migrate away. Very nice. Very nice. So now let's actually take a look at our giveaway.\u003C\u002Fp>\u003Cp>Right? How do we fetch this on the front end? And Directus gives us these REST APIs out of the gate. So if we go to Directus URL, which is local host 805 5 items giveaways, we get no permissions. So I have to go back and by default, Directus controls locks down all of my data.\u003C\u002Fp>\u003Cp>It wants us to be secure. So we've got 2 initial roles in an account. We've got a public role, which controls what data is accessible without authentication. And then we have the administrator role, which has unrestricted access. Now if I was gonna put this behind a login, I would probably do something like creating a user role that didn't have access or didn't have admin access, but they would log in on our front end.\u003C\u002Fp>\u003Cp>In this case, I'm just going to use the public role. We are going to allow people to see the giveaways, we're gonna allow them to view the giveaways, they can create orders and order items, they'll need to be able to create people, and we could also have, like, a server token, where we're doing some of this on the the server side as well. But we're not super concerned with security on this one because we've only got 40 minutes left to build this app. So we will allow access to see the products, see all the product files, and then we're going to go into the Directus Files under the System Collections and we're gonna allow that. So now if I go back, I open this up go to 8055\u002Fitems\u002F giveaways.\u003C\u002Fp>\u003Cp>Boom. There's our giveaway data. Looks great. And we don't see any of our products. I'm gonna show you one of my favorite features about Directus Now and that I'm using the REST APIs.\u003C\u002Fp>\u003Cp>It also generates GraphQL APIs for you, but this is great. This is one of the things that I love. I could go in and tell it specifically what fields I want using the REST API, and I can even fetch so I'll just do products dot wildcard there. I could even go in and fetch products underscore ID the related data in a single call. So now you can see I've got our giveaway page data up here.\u003C\u002Fp>\u003Cp>I've got our products data here with all of our options and things like that, and I've only had to make a single call. Amazing. But we're gonna run out of time if we don't start building an app. So we've got our Nuxt app running at local host 3,000. Let's open this up and start trying to build something.\u003C\u002Fp>\u003Cp>Cool. So we'll go in to our pages, Let's create a new folder. Giveaways. Okay. Looks like I already did that.\u003C\u002Fp>\u003Cp>Not sure why I cheated a little bit there. But, we will do giveaways slash ID. So we use brackets here to set up a dynamic route inside Nuxt. We'll just do a basic component here, and now I just want to fetch that giveaway data so we can render it. Now typically, if I was using server side rendering or something like that, I would probably want to use the Nuxt async data calls.\u003C\u002Fp>\u003Cp>It's got a nice use async data as the composable that has a lot of nice little stuff built in, but I'm not super concerned with that here and honestly, this is gonna be a private link. So what we're gonna do is just call the giveaway. We're gonna go giveaway equals await use directus, that's my composable that wraps the API or, I'm sorry, the SDK. I'm gonna do read item, that's the giveaway syntax or the, I'm sorry, the SDK syntax, the giveaway syntax. We don't even know what that is.\u003C\u002Fp>\u003Cp>And then I need to pass it an item. So before I do that I'm gonna use route from Nuxt and then I can do something like this where we have route dot params dot ID And then the 3rd argument here is just an object with options. So in this case, I'm just gonna use fields for right now and show you what that looks like. Now on the front end, typically what I'll do a lot is just render that out so I could see what we're working with. Now how do I actually navigate to this?\u003C\u002Fp>\u003Cp>Inside Directus, I could go into my data model for our giveaway page, and maybe I wanna set up a button where I can actually get to this specific item. So we'll just create a new button, we'll say view giveaway, And, you know, if this is live, obviously, you're gonna change the URL. But this is gonna be my Nuxt URL, HTTP \u002F localhost3000\u002Fgiveaways\u002Fid, and then I can click this plus button to add the dynamic variables from the ID of this item. If you added the raw value, you can see we're just using mustache syntax here. We'll hit save, save, you give away.\u003C\u002Fp>\u003Cp>Looks good. We go to the giveaway. Now I've got this button I could click that I view giveaway and boom, should be showing me that, but instead I'm getting forbidden. Why are we getting forbidden? If I look, I could see that I left off the s.\u003C\u002Fp>\u003Cp>I'm assuming that's probably part of it. So let's just refresh, and boom. Now I can see our giveaway. So here's our actual data for this specific giveaway. It's great.\u003C\u002Fp>\u003Cp>So let's style this bad boy a bit. Trying to remember what I've actually got in here as far as like the layouts. So the Nuxt layout directory is super handy, this is the default layout, looks good. What are let's just copy what we've got on our index page here, and we'll go to giveaways. Alright.\u003C\u002Fp>\u003Cp>So I don't even know that we wanna flex all of this, do we? Let's give it a purple background, so we'll do pgprimary500 with full height full screen. What do we got? Okay. There we go.\u003C\u002Fp>\u003Cp>Alright. And then we'll give it a bit of padding. Now we'll do like a white background. I think that's kind of similar to what we've got here. Yeah.\u003C\u002Fp>\u003Cp>We got some funky business going on there, but we got PG white. Yeah, p 8 and then we've got, okay. Thank you GitHub Copilot for the assist. We got the giveaway dot title, giveaway dot description, that's not quite gonna cut it. So we've got the div here, That's actually HTML that we're getting back, so let's do the pros class in tailwind.\u003C\u002Fp>\u003Cp>There we go. GitHub Copilot is getting a lot better these days. Is it still is it totally there? I don't think so. Okay.\u003C\u002Fp>\u003Cp>So we appreciate you big time. So as a thank you, please select your color and reset. Maybe this is text center, I wanna center that up. What do we have here? Bgymaxwith3xlmxauto.\u003C\u002Fp>\u003Cp>We'll get that in the middle of the page. Why is our h one not rendering? Right? This should be a big bold font. It's not rendering, and if I actually took the time to figure out my types, it would be because that's not the actual key.\u003C\u002Fp>\u003Cp>It is headline. Alright. So we've got our direct to swag headline. Let's center that up as well. Cool.\u003C\u002Fp>\u003Cp>Alright. Let's add in our products. So if we open up our Vue dev tools, we go to ID, and I just look at the data that this actual component has, you can see we still have to fetch those products. So what I could do is come into the field section, we'll go to products, We will within that, we're gonna fetch the product underscore ID, and we'll get all the root level fields for that. Let's see what that looks like.\u003C\u002Fp>\u003Cp>Products. Why are you not giving me what I want? Of course we can always do this as well. Give away. Products is not coming through.\u003C\u002Fp>\u003Cp>Do we have permissions enabled for those products? Let's take a look. Giveaway products, people products. Looks like we've got read permissions set up for all of that. What am I doing wrong?\u003C\u002Fp>\u003Cp>Products. Products. ID. Is that it? Where are you?\u003C\u002Fp>\u003Cp>Okay. There we go. In my haystack, fat fingered, forgot to put an s in here. My wife makes a likes to make fun of my sausage fingers anyway. So alright.\u003C\u002Fp>\u003Cp>So now we got our products. We'll render these products out. V for product and giveaway dot products. Just show the product. Alright.\u003C\u002Fp>\u003Cp>So there's the individual products. Again, part of the rules are there are no rules, so let's hop into Tailwind UI. Again, one of my favorite tools. Just a nice library of components that they've already got kind of set up for use cases such as this. Looks like we could make use of this particular one.\u003C\u002Fp>\u003Cp>Maybe we don't want it served in a modal window though, so we just grab let's grab everything inside the dialog panel. And I'm not sure how the outcome is gonna go here. This is probably gonna break some stuff. Okay. So you can see we've got some products here.\u003C\u002Fp>\u003Cp>Nothing is actually working correctly because we don't have things like selected size, let's actually just skip this out and we'll bounce into a component. So let's call this the product card, we've got a v comp ts, got my little snippets set up and we'll just copy and paste that in there. So that's our templates, what do they have for the script section of this? Looks like they have selected color, and they've got some headless view. Luckily my Nox starter already has those set up and then they have a product.\u003C\u002Fp>\u003Cp>So I'll just copy this stuff down. Do we get something on this end? We're getting somewhere. Looking good, except none of this is actually rendering out. That's okay though.\u003C\u002Fp>\u003Cp>We're gonna sort through that. Right? So we don't need the reviews. We'll just chop those. Let's get this down to bare bones.\u003C\u002Fp>\u003Cp>Right? The color picker, so we need the size picker. Add to bag, we're probably not gonna need that. View full details, we're not gonna need that, but this is not updating, so I need to actually go in and just swap this out. Product card, b 4 product card.\u003C\u002Fp>\u003Cp>And then we'll probably end up doing something like this where we have key equals product dot ID. We probably wanna fetch that ID here as well. So do that. Is that right? No.\u003C\u002Fp>\u003Cp>We're gonna put it here. ID. Alright. So now we're rendering a product card. How we doing on time?\u003C\u002Fp>\u003Cp>We got 27 minutes left. We are over halfway here. We still gotta be able to submit this form. And then we also are gonna want to pass it, so our product is gonna be product dot products underscore ID. Alright.\u003C\u002Fp>\u003Cp>So we're not doing anything here now because I'm just using the default setup from Tailwind, from our Tailwind UI components. Where's that size guide coming at from as well? Let's get rid of that. Alright. So we just want the basic details, right?\u003C\u002Fp>\u003Cp>We're going to define some props, so we'll go prod const props equals define props. Product type is an object. Again, normally I would type this out with an interface or something like that inside using typescript, so we've got that, but, yeah, whatever. Alright. That should break this particular item.\u003C\u002Fp>\u003Cp>If I refresh, I'm probably gonna get a whole bunch of stuff going on. Got a product. Oh, let me just clear this out because now we no longer have product there. Okay. So we got the bunny tumbler, we've got some colors, we've got some stuff going on.\u003C\u002Fp>\u003Cp>Is this open and closed? We don't even need that. Alright. So this particular NUC starter and I think I'm gonna have to make this available online just in case anybody wants to look it up and decide if I was using steroids or not, but we'll do product dot images dot 0. Let's see where where that gets us.\u003C\u002Fp>\u003Cp>Not very far. Alright. So a Nuxt Image is already set up on this particular product or I'm sorry, this particular starter. Let me reload the page here and see what's happening. Alright.\u003C\u002Fp>\u003Cp>So we've got our products. We're passing that product down to the product card. And what are we actually getting for the images? We're just getting, not getting anything for the images. So, I could go back up to our giveaway.\u003C\u002Fp>\u003Cp>And now let's drill into our images, if I could stop making typos. Images. Boom. Let's see what we got now. Images and array of objects.\u003C\u002Fp>\u003Cp>At least now we have a direct us files ID that we can use to access that image. I would probably, you know, if you wanted to get, like, the title description, the alt text, stuff like that, we could drill in even further like this where we have the directus_files_id, and we get the ID, we get the title, get the description. Now let's look at what we've got. We just keep getting further and further into nesting. But, hey, super helpful anyway.\u003C\u002Fp>\u003Cp>Alright. So now we're getting that if we go to our product card, maybe we just use like a computer prop here. Constant image equals computed and then we'll do return product dot no. We're gonna do props dot product dot images dotzero.directusfiles.id dotid. Alright.\u003C\u002Fp>\u003Cp>And then here inside our Nuxt image I can just do this where we say image. Clear that alt text for now. Actually we could use the title. Right? So we have let's change that we're returning the actual object instead of the ID We'll do image dot ID, and then the alt text could be image dot title or description.\u003C\u002Fp>\u003Cp>Great. Same result. We've got our colors. We're at what? 22 minutes, does that need to be overflow hidden?\u003C\u002Fp>\u003Cp>Where's that coming from? That coming from our I think this was set up for like a, like an application instead of a scrolling page. Bg primary and hide screen, hide full. Yeah. Okay.\u003C\u002Fp>\u003Cp>Let's not stress over that for the moment. We've got bigger fish to fry. So, we've got our images, we've got our name, let's get our product description into this, product name, product price, the product description is gonna be again vhtml. So we'll just use the Tailwind Class Pros vhtmlproduct.description. Close that.\u003C\u002Fp>\u003Cp>Let's see what we got. This is the most amazing tumbler ever. There we go. Alright. So how do we get the size and and, like, our different options to display?\u003C\u002Fp>\u003Cp>Alright. If we look at our product card here, we've got our different options, for the tumbler there's just one option. So we are going to actually, do we need I don't think we need both of these. Right? We will just loop through and create each of those based on the individual options.\u003C\u002Fp>\u003Cp>So we've got a radio group, and this is the color. Let's just call it the options picker, and we get a v four product, let's call it option. Options. Yeah. That's fine.\u003C\u002Fp>\u003Cp>And product dot options, then the key is gonna be options.name. Okay. And then here we'll say options dot name, color, selected color, This will probably be something like selected options and that'll be a ref and object. We can do it either way. Let's see what we got.\u003C\u002Fp>\u003Cp>V model selected. Options and then we'll use the option dot name. We use the key for that. Choose a color, we don't really need that. In product dot colors, in v for value in no, let's do option in options dot options, maybe we call it option, option dot name, Option dot name, selected option, option dot options, Option choice.\u003C\u002Fp>\u003Cp>Option value. Naming surface fun. Right? Option value dot value. Okay.\u003C\u002Fp>\u003Cp>And then the value is what? Optionvalue.value.optionvalue.value. With the color dot selected color Not sure what that's doing there. Let's reload and see what's broken. So at least we're looping through these.\u003C\u002Fp>\u003Cp>Color dot name. Option dot name. Color.bgcolor. Why are these not actually displaying? Radio group, what are we passing into you?\u003C\u002Fp>\u003Cp>We are running out of time. V4 and product options. So we got a radio group. Let's just omit that. Missing something somewhere.\u003C\u002Fp>\u003Cp>Product card, let's look at we got our options, product dot options dot name option in options. So the option dot name and option dot values is what we need to loop over. Boom, boom, boom, And looks like I copied the wrong one. So we would probably be showing the values here if I go back to, let's copy this actual radio group option here. What's the difference?\u003C\u002Fp>\u003Cp>Div class. Just copy and paste this inside their size and stock. Don't need it. This is gonna be the option value dot value. This is gonna give us what we want.\u003C\u002Fp>\u003Cp>Oh, hey. Look at this. Now we're getting somewhere. Right? Looking beautiful.\u003C\u002Fp>\u003Cp>Not exactly beautiful, but definitely better than what we had. Right? So now let's take a look at our product card. If we look at our product, selected options dot ref, why is our ref not showing for those? Let me just refresh.\u003C\u002Fp>\u003Cp>The props, there's our setup, the selected options, this is for the first one, the color is white. Okay. Now if I select the other ones, we check the product card for that one. Where are you? Mister product card.\u003C\u002Fp>\u003Cp>Selected options are color, dark heather. Looks like we could probably work with this. Right? Now, yeah, we can get carried away here and do like a composable or something like that for our form. Let's just do like a watch and if we were gonna watch what selected options selected options dot, What is the actual watch syntax?\u003C\u002Fp>\u003Cp>Deep true. No. What is the view watch syntax? Alright. With the composition API, what are we looking for?\u003C\u002Fp>\u003Cp>We've got a function. So we're gonna watch selected options. Okay. And then we've got the functions. So we got the value, console log, maybe we want to omit that, so we'll pass that up, const emits equals define emit And let's just omit, updated.\u003C\u002Fp>\u003Cp>Sounds great. Alright. Selected options, we are going to omit. Updated. We will value true.\u003C\u002Fp>\u003Cp>Okay. Alright. So if we go back to our ID here, let's add a handler for this. Maybe we just do our order here that will be reactive. Okay, and then we've got our order products, we've got the order, what do we have?\u003C\u002Fp>\u003Cp>A customer? No. It was the person, is what we called it. Alright. That person has a first name, last name, email, and then we have an address.\u003C\u002Fp>\u003Cp>Great. Alright. So now we've got an order. Cost order equals react. What's the problem with this?\u003C\u002Fp>\u003Cp>Alright. Great. So now we'll probably need a handler. So let's do a function to handle that emit. So we'll do update cart or update products, products order dot products okay and then when update update products.\u003C\u002Fp>\u003Cp>Cool. Alright. So let's verify and see what's happening now. So I'll just refresh my page. Unable to fetch.\u003C\u002Fp>\u003Cp>Oh, what am I doing wrong? Hydration completed but contains a mismatch. What what are we doing wrong here? This is always fun. V if or if we just wrap this really quickly.\u003C\u002Fp>\u003Cp>Giveaway. Failed to fetch dynamically imported module. I understand. Active from you. So what's going on?\u003C\u002Fp>\u003Cp>Dying on the vine here. I probably should have been using the, Nuxt Async data all along, We could probably get around this maybe just by using turning SSR off for now, but obviously, not the best solution. Does not have provided a named export called select. What is this? Is this in the product card?\u003C\u002Fp>\u003Cp>Oh, I don't know why we imported that. How did that get imported? Okay. So it probably wasn't, any issue with the SSR there but, oh well. Alright.\u003C\u002Fp>\u003Cp>So we're back on our giveaway dot ID. Let's refresh just to make sure we've got everything. Where is our where's our app at? Okay. So we look for our ID.\u003C\u002Fp>\u003Cp>Now we've got our order object and hopefully, this should be updating, but it's not. Emit updated. That could be why. I actually have to spell it correctly. So we refresh, try again.\u003C\u002Fp>\u003Cp>If we look at our products, are they updating? They're still not updating. Wonder why that is. So console dot log, good old console dot log products section. Okay.\u003C\u002Fp>\u003Cp>Products. Okay. Okay. So what do we actually want to do here? This will be we actually need to pass the product ID, don't we?\u003C\u002Fp>\u003Cp>So updated. Let's do this where we have an event. We want to pass the product ID and the event. Alright. Update products.\u003C\u002Fp>\u003Cp>This will be what? Clock is ticking. Let's cheat a little bit. Update the porter products array with the selected product. Let's see what GitHub Copilot comes up with really quickly.\u003C\u002Fp>\u003Cp>It will look through the array, find the index for the item. Okay. Nobody said it was, was easy. Right? So now we've got our products, we've got our ID, we've got the options for the product.\u003C\u002Fp>\u003Cp>What do we want to, It's actually the product. Right? What is that gonna be inside the database? Let's just take a look at it inside our data model. We got the order, we got a person, Did we not add that reverse?\u003C\u002Fp>\u003Cp>We didn't add that reverse, so we we're gonna need to add that really quickly. Many to 1, 1 to many, boom boom. Products. Oh, actually, that's gonna be our items. Alright?\u003C\u002Fp>\u003Cp>It's gonna be the items on the order. Order items, order. Okay. So that's actually gonna change. That'll be our items.\u003C\u002Fp>\u003Cp>And then our order item itself has the item options, so that'll be item options. Item options equals event, and then the product. Order products dot index item options. We'll just see if this goes according to plan. Okay.\u003C\u002Fp>\u003Cp>Blah blah blah. We take a look, we've got our giveaway page. What am I missing? Order dot, oh, order dot products dot push. Forgot to update this.\u003C\u002Fp>\u003Cp>So this will actually be order dot items, order dot items. Okay. So now we've got our items for the order. There's our product, 123. Shouldn't have 3 items, should it?\u003C\u002Fp>\u003Cp>Maybe that was a mistake. Running into snags everywhere. Lots of fun. Alright, so how do we actually submit this? We still don't have our form either, do we?\u003C\u002Fp>\u003Cp>Let's build a form. So a form. And actually, I think there is the Nuxt UI component that is built into this thing. So let's just do a form. What are those?\u003C\u002Fp>\u003Cp>A u input, u form group I think is the right one. Label equals first name, V model equals order dot first name. Oh. That's gonna be you and put the model order first name. Let's see what we get.\u003C\u002Fp>\u003Cp>Okay, now we've got a form, Let's actually wrap that just a little bit. Max width 3 x l. Actually, let's just move it up inside that. Why why are we doing that? There we go.\u003C\u002Fp>\u003Cp>Alright, so we've now we've got our form inside here. Bgwhite, shadow. Okay, we got our first name. What do we have? We got a last name, got an email, street, city, GitHub Copilot for the win on this one, And zip.\u003C\u002Fp>\u003Cp>Alright. That's not looking very pretty, so let's actually set these on a grid. Grid calls 2. Alright. This will be class grid callspan 2.\u003C\u002Fp>\u003Cp>And give it some gap, gap 2, let's say 6, right? Too much, gap 4. We'll give the form itself some space in between and then we just need a button, submit. Let's make it block. Okay.\u003C\u002Fp>\u003Cp>Class equals call spin 2. Alright. So big giant form, now we need to submit the order, submit order, await, direct us, use orders, order, giveaway dot ID, create item, that looks correct. Let's wrap this in a try catch error console dot error. Alright.\u003C\u002Fp>\u003Cp>Time is a ticking. Let's see, we got 43 seconds. Let's see if we can actually get this done. Brian Gillespie, email bryantat directus.io. 123 Main Street, Bluefield, West Virginia.\u003C\u002Fp>\u003Cp>Submit. Oh, duh. Dummy. Come on now. Submit dot prevent.\u003C\u002Fp>\u003Cp>Oh, no. He's typo. Submit dot prevent. Submit order. One second.\u003C\u002Fp>\u003Cp>We hit the timer here right at the very end, just as we were getting really good. I just wanna submit this out and see if it's actually gonna work. Test city, 1232222. Submit. Did we get an error?\u003C\u002Fp>\u003Cp>Post local items orders equals forbidden. So I'm getting that error because there are permission settings that we didn't control order items. We forgot to set that up. But now if we were to go back and just try this again, orders forbidden. Yeah.\u003C\u002Fp>\u003Cp>So, something in the way that I'm passing the data here, don't have permissions to access this. It is probably something to do with the person data that I did not get updated. And it's actually what? State Street Zip. This is the this is the main issue why you shouldn't trust AI.\u003C\u002Fp>\u003Cp>In our hurry, this actually should have been order dot address dot street dot city dot state, all of that. Didn't even pick it up. Hopefully, you're not coding against the clock like I am, but that's one of the the issues there. Test, test, test, test, submit. Still getting some issues.\u003C\u002Fp>\u003Cp>So, we tried our best. I still think this looks pretty good. And with another 20 minutes or so, 10 minutes or so, we could have completed this out where it would actually submit the order to Directus. But I hope this is a good example of just how quickly you can build apps with Directus, and a a front end framework like Nuxt. Directus gives you a lot out of the box, which makes speed runs like this possible.\u003C\u002Fp>\u003Cp>This was a lot of fun. I don't think that I'm gonna be replacing swag.com anytime soon, but, nevertheless, if you had different needs or you wanted your own customized platform, you could quickly and easily build it with Directus and have something that was specific to your company. These platforms like swag.com and others are obviously, they have a lot going on on the fulfillment side. But with Directus you could build custom ecommerce like this, custom experiences for people rapidly. So that is it for this episode of 100 apps 100 hours.\u003C\u002Fp>\u003Cp>Thanks for sticking it out with me and I hope you'll tune in for the next episode. Ciao.\u003C\u002Fp>","Alright. Alright. Alright. Welcome back to the next episode of 100 Apps 100 Hours where we rebuild or build some of your favorite apps in 1 hour or less or die trying or publicly fail trying is probably more appropriate. I'm your host, Brian Gillespie, developer advocate at Directus. Welcome. Super excited to have you. Today, we are going to be building a gifting platform. Oh, Brian, that sounds really exciting. Actually, to me, it is. So let's dive into the details of this. I've recently gone through at Directus and set up a swag program. So we've got tons of users worldwide that use our software getting, merchandise like branded cups or mugs or t shirts, into the hands of our user base, incredibly difficult challenge when you consider that we've got users in the US, we've got users in Germany, France, Namibia, just every country that you can think of. We've got people that we would like to thank and show appreciation for. Very challenging. So inter gifting platforms. We are going through the process currently with a company called swag.com. They offer a shopping cart and e commerce experience where you can log in and, basically pick your swag, upload your designs, and place orders that they will then fulfill for you. There's quite a few of these types of companies like Sendoso, I found another one called SwagUp, and this one is Codus Design. It basically a swag platform where you place an order, they manage all the fulfillment and inventory and actually delivering those items to your clients or your customers or your potential leads, potential prospects. They handle all of that for you. The part that I was very interested in, and I'm not discounting the fulfillment and warehousing and all the other bits and bobs that are included in here. That's probably the really difficult stuff. But, our past ecommerce or past swag solution was a Shopify store. And when you're trying to send somebody a thank you gift, it feels a little weird to have them check out on a Shopify store and enter a coupon code to actually get that get that swag or get that gift that you're trying to send them. And one of the reasons why I really like these platforms is the ability to send out a thank you page that looks very similar to this. There's there's no checkout. They don't have to enter a credit card or a coupon code. I send them a unique link. They can pick their swag, and we get them to enter in their information like their first name, email, country, and address so we know where to ship it. It is a great experience for them, great experience for us, win all around. That's what we're gonna be building today. Let's dive in. Just a quick recap of the rules. There's 60 minutes to plan and build, so that's plan and build in 60 minutes no more no less, And aside from that there are new rules. We'll use whatever we have at our disposal whether that's AI or all sorts of other cheat codes from around the Internet. Let's get started. We're gonna dive in. Alright, so before I build any apps, I usually like to take at least 5 minutes to plan. These planning sessions are always fun because it's hard to test your assumptions to get feedback when you have 5 minutes to plan an app. But here is my initial functionality that we're gonna build into this app. We're gonna need to manage our products and product options. We will need to, create a giveaway page or several different things that the the companies that I looked at called this, like a redemption page. I think giveaway is probably a good one. Thank you page, create a giveaway, a redemption page. Place an order, place a request. Not sure it's a order because it's free, but hey, that's what we'll do. User places a request, k, let's say user can place an order request, and then we manage and fulfill those through the app. Alright. So that's the app that we're gonna build. There are 2 components. There's a back end. There's a single page on the front end. What are we gonna need as far as our data model? What is that gonna look like? So we'll jump in and let's do, we certainly have products. Oh, I'm not ready to drag arrows yet. We've got our products. What is next? We've got our giveaway page, right? Giveaway. But then we have customers, you know, we'd probably wanna track that as a separate table or those could just be people. And then we'll have orders and probably some actual order items. So when we look at this, all our products roll up to a giveaway and we this is probably gonna be like a mini to mini relationship because we could have many different products on many different giveaways. When we go to products those are gonna be to the order items, customer orders or items will be orders attached to giveaways. I I just really like drawing arrows half the time. So as far as the rough data model, this looks pretty good. You know, curious to hear your feedback if if you set up ecommerce before, especially on how you handle like product variants and things like that. But let's dive in and not waste too much more time. Let's actually start building this. I'm just gonna move this off to the side. What does our setup look like as far as how we're gonna build? I've got a local Nuxt starter app set up. I've got a Directus folder where I've got a Docker Compose file that just simply creates my Directus instance that we're gonna be using on the back end. And then this Nuxt application just has a SDK for Directus already pre configured with a plugin using the the Nuxt plugins and, you know, nothing too fancy. The only, like, major difference here is there's a composable, view composable included for making the Directus requests, because Nuxt has that nice auto import feature. Alright, so let's log into our Directus instance and start building something right away. Let me look at what I'd use for my password here, just paste that and as we look let me get my data model up in view so we can actually start building. But as we look, this is a blank instance, I'm not doing any kind of crazy cheating here. Let's start building. So we'll create a new collection for products, we'll track some dates on this, you know, is this a draft product or not? Maybe we don't really need that for this particular instance. Alright. So on our products, what do we have? What are we using? We've got a name for our products, that's great, that's just a string. We've got a description for our product, We probably have some images for our product. So we've got some images. This is going to be using the relational fields inside Directus. And what's happening behind the scenes here as I'm building these collections and adding fields to it, the Directus API is mirroring this setup inside my Postgres database. And at the end of this, as soon as I get done building this data model, I'll have a ready to go API that I can then call on my front end to render those products. So we've got name, description, images, we're probably gonna have some options for our products like color and size. A more robust setup here would be to create separate collections for those, you know, a a product options and then maybe a product options values so that that could be dynamic. To streamline this, I think I wanna go with the repeater inside Directus. So this is just a JSON store and we'll call this options. So our options, we're gonna edit the fields here. The first option is gonna be the name of the option and then that'll be a string. We require a value for that, that'll be an input. It's fine. Let's make it full width and then next let's add values. So we're gonna get a little meta here and then I am going to embed a repeater within a repeater. Sounds pretty wild. So the field here, let's call it value. And I could probably have like a label and value in case the the value that I wanted to track was different than the label, but this looks pretty okay. We'll hit Save. We've got name, we've got description, we've got images, you know, if I had something like a SKU we can work on that, but let's keep it simple, right, let's keep moving. What's next? We have our giveaway. So this is gonna be a giveaway page, and if I pull up one, like, the direct to sample, we've basically got a logo, we've got a headline, we've got some description, then we have the products and we have a form. So let's add a table for giveaways. We'll go in and we'll set a status on the giveaway if this is published or not. Let's go in, we'll create a new field, we'll call it headline. We'll just make that a string. Then we have a description that we could set. So we'll do a description. Again that will be rich text so we could potentially have, lists or, you know, bold italicized text. And then we want to add our products. Right? So with our products, we're gonna use the relationship features inside Directus, and we're going to use that many to many relationship. Because one giveaway could have many products, and we could use many products on many giveaways on we use one product on many giveaways. So the let's call this field products. Our related collection is also gonna be products, and we don't want to allow duplicates. We do wanna show a link to the item so we can go back to that product. And I'm just gonna open this in advanced mode. So behind the scenes, what's happening when we use this many to many relationship? Inside the Directus or inside our Postgres database, Directus is creating a junction table called giveaways_products. Now I could adjust what that table is called, and even the foreign keys that it's setting up inside that junction collection, what it's naming those. But I'm just gonna go with autofill in this case. If I want to add the reverse relationship back on the products, so if I wanna reference those giveaways on the products themselves, I can do that here as well. And then maybe we add a sort field just to control the order. Maybe I want the shirt to show up first before the other items. Great. Directus is gonna tell me what all it's going to create inside my database, and now we've got our products. What else do we need? Do we need anything else here? The form, maybe we hard code that. Great. So if we look what else? We've got a customer, we've got orders, and order items. Alright. So let's go ahead and set those up as well. So we got a customer, actually let's just call it persons, people. Naming stuff is always the hardest thing. This may not be a customer. Right? So let's just go with the more semantic name and call it people. So people have a first name, they have a last name, they have an email address, and are we what else are we capturing? Capturing an address for those people. I'm assuming that could be on the order itself. Let's just roll with this. Right? First name, last name. Maybe we'll capture the address on the order. We could store it here as well, but we'll keep it simple. So we've got then we have an order and the orders are going to what? What are we gonna have on the order? The order is going to have a customer or a person, where we who's the person attached to that order, that's gonna be a many to one relationship. Alright. So we got the people, person, people, are already regretting this decision. No turning back now. Right? And then on the reverse side of that, on our people, we'll show the orders. People orders within the data model. Great. Alright. So the person we have, an address, let's just store that as JSON data for now. So we'll do an address and what else? We have a status is approved, maybe. This is approved, so we want approval before we ship all of these out. Let's add that to the top. And then last but not least, we want to create the actual items involved with this specific order. So we've got an order items table or you could call it orders items. Again, we get into splitting hairs here. What else do we need? We probably do need a sort for those blah blah blah. Alright, so we got our order, we got our order items. The items are gonna have a link back to the product. So we've got a related collection is gonna be the products. So when we put those items in the cart, the order item will have a product, so that'll be a relationship and then let's just have a JSON field to store our product options. Maybe we call it item options just to keep them separated. Good. And then there is a 1 or many to 1 relationship. So there are many items to one order, back to that order. So we've got the order, the related collection is orders. Tell me I didn't. I did, didn't I? So this is a boo boo. I've got orders here. It should be or I've got order should be orders. That should be a a fun little thing to work around. Now, I could go into the database itself and edit that if I wanted to, but, no worries. We'll just roll with what we got. There's an order. It's a order. Sometimes you goof up. What do you do? You keep rolling. Alright. So we've got our data model. Right? Looks great. Let's go in and maybe add a giveaway. And we can just duplicate this one, right? Directus Swag. Still learning Arc. I like it as a browser. Yeah. If anybody out there is a Arc expert, maybe you could give me a couple lessons like a tutorial session or something. Alright, so we got our headline, we've got a description. Let's add a product, we don't have any products at this point, so let's create a product. We've got the Directus, let's let's call it the Bunny Tumbler 20 ounce, Great. This is the most amazing tumbler ever. Great. Now we want to add some images to this. So, I can actually import images from a URL inside Directus, which is super nice, especially for speed runs like this. I just click upload a file, click the little link that will import our file. Looks great. For our options, we've got color and for the value, we've only got one. It's just white. We're gonna offer that in white. Great. Okay. Let's go in and add one more product. We've got our Directus logo tee. Kinda sounded like goatee, didn't it? This is the softest t shirt you'll ever wear, and again, we'll just go in and let's copy the image address and you can see we've got a couple options here. We've got extra small through 2 x, so we'll add those in there as well. Just upload our file. Great. For our options, we've got color, which is a dark heather. I believe I should know, I'm the one who ordered this stuff. And then let's add another option for what size. Alright. So we got extra small, we got small, we got medium. It'd be really handy if we could just do this in line, wouldn't it? I'm sure there's a way. Where there's a will, there's a way. Alright. So we've got some extra small through 2 x. We've got our products. We've added them to a giveaway. Let's go ahead and publish this giveaway and hit save. Great. So we've got our giveaway, and let me just prove it to you. Let's open up Directus. Alright. So this is our actual database here. We can see we've got order. We've got some directus tables that directus has created for us, and then we have giveaways. So there's our data. Directus prefixes all the metadata tables that it so it keeps the SQL database pure. That way, you know, you're not locked in or anything like that if you decide to migrate away. Very nice. Very nice. So now let's actually take a look at our giveaway. Right? How do we fetch this on the front end? And Directus gives us these REST APIs out of the gate. So if we go to Directus URL, which is local host 805 5 items giveaways, we get no permissions. So I have to go back and by default, Directus controls locks down all of my data. It wants us to be secure. So we've got 2 initial roles in an account. We've got a public role, which controls what data is accessible without authentication. And then we have the administrator role, which has unrestricted access. Now if I was gonna put this behind a login, I would probably do something like creating a user role that didn't have access or didn't have admin access, but they would log in on our front end. In this case, I'm just going to use the public role. We are going to allow people to see the giveaways, we're gonna allow them to view the giveaways, they can create orders and order items, they'll need to be able to create people, and we could also have, like, a server token, where we're doing some of this on the the server side as well. But we're not super concerned with security on this one because we've only got 40 minutes left to build this app. So we will allow access to see the products, see all the product files, and then we're going to go into the Directus Files under the System Collections and we're gonna allow that. So now if I go back, I open this up go to 8055\u002Fitems\u002F giveaways. Boom. There's our giveaway data. Looks great. And we don't see any of our products. I'm gonna show you one of my favorite features about Directus Now and that I'm using the REST APIs. It also generates GraphQL APIs for you, but this is great. This is one of the things that I love. I could go in and tell it specifically what fields I want using the REST API, and I can even fetch so I'll just do products dot wildcard there. I could even go in and fetch products underscore ID the related data in a single call. So now you can see I've got our giveaway page data up here. I've got our products data here with all of our options and things like that, and I've only had to make a single call. Amazing. But we're gonna run out of time if we don't start building an app. So we've got our Nuxt app running at local host 3,000. Let's open this up and start trying to build something. Cool. So we'll go in to our pages, Let's create a new folder. Giveaways. Okay. Looks like I already did that. Not sure why I cheated a little bit there. But, we will do giveaways slash ID. So we use brackets here to set up a dynamic route inside Nuxt. We'll just do a basic component here, and now I just want to fetch that giveaway data so we can render it. Now typically, if I was using server side rendering or something like that, I would probably want to use the Nuxt async data calls. It's got a nice use async data as the composable that has a lot of nice little stuff built in, but I'm not super concerned with that here and honestly, this is gonna be a private link. So what we're gonna do is just call the giveaway. We're gonna go giveaway equals await use directus, that's my composable that wraps the API or, I'm sorry, the SDK. I'm gonna do read item, that's the giveaway syntax or the, I'm sorry, the SDK syntax, the giveaway syntax. We don't even know what that is. And then I need to pass it an item. So before I do that I'm gonna use route from Nuxt and then I can do something like this where we have route dot params dot ID And then the 3rd argument here is just an object with options. So in this case, I'm just gonna use fields for right now and show you what that looks like. Now on the front end, typically what I'll do a lot is just render that out so I could see what we're working with. Now how do I actually navigate to this? Inside Directus, I could go into my data model for our giveaway page, and maybe I wanna set up a button where I can actually get to this specific item. So we'll just create a new button, we'll say view giveaway, And, you know, if this is live, obviously, you're gonna change the URL. But this is gonna be my Nuxt URL, HTTP \u002F localhost3000\u002Fgiveaways\u002Fid, and then I can click this plus button to add the dynamic variables from the ID of this item. If you added the raw value, you can see we're just using mustache syntax here. We'll hit save, save, you give away. Looks good. We go to the giveaway. Now I've got this button I could click that I view giveaway and boom, should be showing me that, but instead I'm getting forbidden. Why are we getting forbidden? If I look, I could see that I left off the s. I'm assuming that's probably part of it. So let's just refresh, and boom. Now I can see our giveaway. So here's our actual data for this specific giveaway. It's great. So let's style this bad boy a bit. Trying to remember what I've actually got in here as far as like the layouts. So the Nuxt layout directory is super handy, this is the default layout, looks good. What are let's just copy what we've got on our index page here, and we'll go to giveaways. Alright. So I don't even know that we wanna flex all of this, do we? Let's give it a purple background, so we'll do pgprimary500 with full height full screen. What do we got? Okay. There we go. Alright. And then we'll give it a bit of padding. Now we'll do like a white background. I think that's kind of similar to what we've got here. Yeah. We got some funky business going on there, but we got PG white. Yeah, p 8 and then we've got, okay. Thank you GitHub Copilot for the assist. We got the giveaway dot title, giveaway dot description, that's not quite gonna cut it. So we've got the div here, That's actually HTML that we're getting back, so let's do the pros class in tailwind. There we go. GitHub Copilot is getting a lot better these days. Is it still is it totally there? I don't think so. Okay. So we appreciate you big time. So as a thank you, please select your color and reset. Maybe this is text center, I wanna center that up. What do we have here? Bgymaxwith3xlmxauto. We'll get that in the middle of the page. Why is our h one not rendering? Right? This should be a big bold font. It's not rendering, and if I actually took the time to figure out my types, it would be because that's not the actual key. It is headline. Alright. So we've got our direct to swag headline. Let's center that up as well. Cool. Alright. Let's add in our products. So if we open up our Vue dev tools, we go to ID, and I just look at the data that this actual component has, you can see we still have to fetch those products. So what I could do is come into the field section, we'll go to products, We will within that, we're gonna fetch the product underscore ID, and we'll get all the root level fields for that. Let's see what that looks like. Products. Why are you not giving me what I want? Of course we can always do this as well. Give away. Products is not coming through. Do we have permissions enabled for those products? Let's take a look. Giveaway products, people products. Looks like we've got read permissions set up for all of that. What am I doing wrong? Products. Products. ID. Is that it? Where are you? Okay. There we go. In my haystack, fat fingered, forgot to put an s in here. My wife makes a likes to make fun of my sausage fingers anyway. So alright. So now we got our products. We'll render these products out. V for product and giveaway dot products. Just show the product. Alright. So there's the individual products. Again, part of the rules are there are no rules, so let's hop into Tailwind UI. Again, one of my favorite tools. Just a nice library of components that they've already got kind of set up for use cases such as this. Looks like we could make use of this particular one. Maybe we don't want it served in a modal window though, so we just grab let's grab everything inside the dialog panel. And I'm not sure how the outcome is gonna go here. This is probably gonna break some stuff. Okay. So you can see we've got some products here. Nothing is actually working correctly because we don't have things like selected size, let's actually just skip this out and we'll bounce into a component. So let's call this the product card, we've got a v comp ts, got my little snippets set up and we'll just copy and paste that in there. So that's our templates, what do they have for the script section of this? Looks like they have selected color, and they've got some headless view. Luckily my Nox starter already has those set up and then they have a product. So I'll just copy this stuff down. Do we get something on this end? We're getting somewhere. Looking good, except none of this is actually rendering out. That's okay though. We're gonna sort through that. Right? So we don't need the reviews. We'll just chop those. Let's get this down to bare bones. Right? The color picker, so we need the size picker. Add to bag, we're probably not gonna need that. View full details, we're not gonna need that, but this is not updating, so I need to actually go in and just swap this out. Product card, b 4 product card. And then we'll probably end up doing something like this where we have key equals product dot ID. We probably wanna fetch that ID here as well. So do that. Is that right? No. We're gonna put it here. ID. Alright. So now we're rendering a product card. How we doing on time? We got 27 minutes left. We are over halfway here. We still gotta be able to submit this form. And then we also are gonna want to pass it, so our product is gonna be product dot products underscore ID. Alright. So we're not doing anything here now because I'm just using the default setup from Tailwind, from our Tailwind UI components. Where's that size guide coming at from as well? Let's get rid of that. Alright. So we just want the basic details, right? We're going to define some props, so we'll go prod const props equals define props. Product type is an object. Again, normally I would type this out with an interface or something like that inside using typescript, so we've got that, but, yeah, whatever. Alright. That should break this particular item. If I refresh, I'm probably gonna get a whole bunch of stuff going on. Got a product. Oh, let me just clear this out because now we no longer have product there. Okay. So we got the bunny tumbler, we've got some colors, we've got some stuff going on. Is this open and closed? We don't even need that. Alright. So this particular NUC starter and I think I'm gonna have to make this available online just in case anybody wants to look it up and decide if I was using steroids or not, but we'll do product dot images dot 0. Let's see where where that gets us. Not very far. Alright. So a Nuxt Image is already set up on this particular product or I'm sorry, this particular starter. Let me reload the page here and see what's happening. Alright. So we've got our products. We're passing that product down to the product card. And what are we actually getting for the images? We're just getting, not getting anything for the images. So, I could go back up to our giveaway. And now let's drill into our images, if I could stop making typos. Images. Boom. Let's see what we got now. Images and array of objects. At least now we have a direct us files ID that we can use to access that image. I would probably, you know, if you wanted to get, like, the title description, the alt text, stuff like that, we could drill in even further like this where we have the directus_files_id, and we get the ID, we get the title, get the description. Now let's look at what we've got. We just keep getting further and further into nesting. But, hey, super helpful anyway. Alright. So now we're getting that if we go to our product card, maybe we just use like a computer prop here. Constant image equals computed and then we'll do return product dot no. We're gonna do props dot product dot images dotzero.directusfiles.id dotid. Alright. And then here inside our Nuxt image I can just do this where we say image. Clear that alt text for now. Actually we could use the title. Right? So we have let's change that we're returning the actual object instead of the ID We'll do image dot ID, and then the alt text could be image dot title or description. Great. Same result. We've got our colors. We're at what? 22 minutes, does that need to be overflow hidden? Where's that coming from? That coming from our I think this was set up for like a, like an application instead of a scrolling page. Bg primary and hide screen, hide full. Yeah. Okay. Let's not stress over that for the moment. We've got bigger fish to fry. So, we've got our images, we've got our name, let's get our product description into this, product name, product price, the product description is gonna be again vhtml. So we'll just use the Tailwind Class Pros vhtmlproduct.description. Close that. Let's see what we got. This is the most amazing tumbler ever. There we go. Alright. So how do we get the size and and, like, our different options to display? Alright. If we look at our product card here, we've got our different options, for the tumbler there's just one option. So we are going to actually, do we need I don't think we need both of these. Right? We will just loop through and create each of those based on the individual options. So we've got a radio group, and this is the color. Let's just call it the options picker, and we get a v four product, let's call it option. Options. Yeah. That's fine. And product dot options, then the key is gonna be options.name. Okay. And then here we'll say options dot name, color, selected color, This will probably be something like selected options and that'll be a ref and object. We can do it either way. Let's see what we got. V model selected. Options and then we'll use the option dot name. We use the key for that. Choose a color, we don't really need that. In product dot colors, in v for value in no, let's do option in options dot options, maybe we call it option, option dot name, Option dot name, selected option, option dot options, Option choice. Option value. Naming surface fun. Right? Option value dot value. Okay. And then the value is what? Optionvalue.value.optionvalue.value. With the color dot selected color Not sure what that's doing there. Let's reload and see what's broken. So at least we're looping through these. Color dot name. Option dot name. Color.bgcolor. Why are these not actually displaying? Radio group, what are we passing into you? We are running out of time. V4 and product options. So we got a radio group. Let's just omit that. Missing something somewhere. Product card, let's look at we got our options, product dot options dot name option in options. So the option dot name and option dot values is what we need to loop over. Boom, boom, boom, And looks like I copied the wrong one. So we would probably be showing the values here if I go back to, let's copy this actual radio group option here. What's the difference? Div class. Just copy and paste this inside their size and stock. Don't need it. This is gonna be the option value dot value. This is gonna give us what we want. Oh, hey. Look at this. Now we're getting somewhere. Right? Looking beautiful. Not exactly beautiful, but definitely better than what we had. Right? So now let's take a look at our product card. If we look at our product, selected options dot ref, why is our ref not showing for those? Let me just refresh. The props, there's our setup, the selected options, this is for the first one, the color is white. Okay. Now if I select the other ones, we check the product card for that one. Where are you? Mister product card. Selected options are color, dark heather. Looks like we could probably work with this. Right? Now, yeah, we can get carried away here and do like a composable or something like that for our form. Let's just do like a watch and if we were gonna watch what selected options selected options dot, What is the actual watch syntax? Deep true. No. What is the view watch syntax? Alright. With the composition API, what are we looking for? We've got a function. So we're gonna watch selected options. Okay. And then we've got the functions. So we got the value, console log, maybe we want to omit that, so we'll pass that up, const emits equals define emit And let's just omit, updated. Sounds great. Alright. Selected options, we are going to omit. Updated. We will value true. Okay. Alright. So if we go back to our ID here, let's add a handler for this. Maybe we just do our order here that will be reactive. Okay, and then we've got our order products, we've got the order, what do we have? A customer? No. It was the person, is what we called it. Alright. That person has a first name, last name, email, and then we have an address. Great. Alright. So now we've got an order. Cost order equals react. What's the problem with this? Alright. Great. So now we'll probably need a handler. So let's do a function to handle that emit. So we'll do update cart or update products, products order dot products okay and then when update update products. Cool. Alright. So let's verify and see what's happening now. So I'll just refresh my page. Unable to fetch. Oh, what am I doing wrong? Hydration completed but contains a mismatch. What what are we doing wrong here? This is always fun. V if or if we just wrap this really quickly. Giveaway. Failed to fetch dynamically imported module. I understand. Active from you. So what's going on? Dying on the vine here. I probably should have been using the, Nuxt Async data all along, We could probably get around this maybe just by using turning SSR off for now, but obviously, not the best solution. Does not have provided a named export called select. What is this? Is this in the product card? Oh, I don't know why we imported that. How did that get imported? Okay. So it probably wasn't, any issue with the SSR there but, oh well. Alright. So we're back on our giveaway dot ID. Let's refresh just to make sure we've got everything. Where is our where's our app at? Okay. So we look for our ID. Now we've got our order object and hopefully, this should be updating, but it's not. Emit updated. That could be why. I actually have to spell it correctly. So we refresh, try again. If we look at our products, are they updating? They're still not updating. Wonder why that is. So console dot log, good old console dot log products section. Okay. Products. Okay. Okay. So what do we actually want to do here? This will be we actually need to pass the product ID, don't we? So updated. Let's do this where we have an event. We want to pass the product ID and the event. Alright. Update products. This will be what? Clock is ticking. Let's cheat a little bit. Update the porter products array with the selected product. Let's see what GitHub Copilot comes up with really quickly. It will look through the array, find the index for the item. Okay. Nobody said it was, was easy. Right? So now we've got our products, we've got our ID, we've got the options for the product. What do we want to, It's actually the product. Right? What is that gonna be inside the database? Let's just take a look at it inside our data model. We got the order, we got a person, Did we not add that reverse? We didn't add that reverse, so we we're gonna need to add that really quickly. Many to 1, 1 to many, boom boom. Products. Oh, actually, that's gonna be our items. Alright? It's gonna be the items on the order. Order items, order. Okay. So that's actually gonna change. That'll be our items. And then our order item itself has the item options, so that'll be item options. Item options equals event, and then the product. Order products dot index item options. We'll just see if this goes according to plan. Okay. Blah blah blah. We take a look, we've got our giveaway page. What am I missing? Order dot, oh, order dot products dot push. Forgot to update this. So this will actually be order dot items, order dot items. Okay. So now we've got our items for the order. There's our product, 123. Shouldn't have 3 items, should it? Maybe that was a mistake. Running into snags everywhere. Lots of fun. Alright, so how do we actually submit this? We still don't have our form either, do we? Let's build a form. So a form. And actually, I think there is the Nuxt UI component that is built into this thing. So let's just do a form. What are those? A u input, u form group I think is the right one. Label equals first name, V model equals order dot first name. Oh. That's gonna be you and put the model order first name. Let's see what we get. Okay, now we've got a form, Let's actually wrap that just a little bit. Max width 3 x l. Actually, let's just move it up inside that. Why why are we doing that? There we go. Alright, so we've now we've got our form inside here. Bgwhite, shadow. Okay, we got our first name. What do we have? We got a last name, got an email, street, city, GitHub Copilot for the win on this one, And zip. Alright. That's not looking very pretty, so let's actually set these on a grid. Grid calls 2. Alright. This will be class grid callspan 2. And give it some gap, gap 2, let's say 6, right? Too much, gap 4. We'll give the form itself some space in between and then we just need a button, submit. Let's make it block. Okay. Class equals call spin 2. Alright. So big giant form, now we need to submit the order, submit order, await, direct us, use orders, order, giveaway dot ID, create item, that looks correct. Let's wrap this in a try catch error console dot error. Alright. Time is a ticking. Let's see, we got 43 seconds. Let's see if we can actually get this done. Brian Gillespie, email bryantat directus.io. 123 Main Street, Bluefield, West Virginia. Submit. Oh, duh. Dummy. Come on now. Submit dot prevent. Oh, no. He's typo. Submit dot prevent. Submit order. One second. We hit the timer here right at the very end, just as we were getting really good. I just wanna submit this out and see if it's actually gonna work. Test city, 1232222. Submit. Did we get an error? Post local items orders equals forbidden. So I'm getting that error because there are permission settings that we didn't control order items. We forgot to set that up. But now if we were to go back and just try this again, orders forbidden. Yeah. So, something in the way that I'm passing the data here, don't have permissions to access this. It is probably something to do with the person data that I did not get updated. And it's actually what? State Street Zip. This is the this is the main issue why you shouldn't trust AI. In our hurry, this actually should have been order dot address dot street dot city dot state, all of that. Didn't even pick it up. Hopefully, you're not coding against the clock like I am, but that's one of the the issues there. Test, test, test, test, submit. Still getting some issues. So, we tried our best. I still think this looks pretty good. And with another 20 minutes or so, 10 minutes or so, we could have completed this out where it would actually submit the order to Directus. But I hope this is a good example of just how quickly you can build apps with Directus, and a a front end framework like Nuxt. Directus gives you a lot out of the box, which makes speed runs like this possible. This was a lot of fun. I don't think that I'm gonna be replacing swag.com anytime soon, but, nevertheless, if you had different needs or you wanted your own customized platform, you could quickly and easily build it with Directus and have something that was specific to your company. These platforms like swag.com and others are obviously, they have a lot going on on the fulfillment side. But with Directus you could build custom ecommerce like this, custom experiences for people rapidly. So that is it for this episode of 100 apps 100 hours. Thanks for sticking it out with me and I hope you'll tune in for the next episode. Ciao.",[5470],"78258a11-7e08-4e1f-8d3c-ca90c4275d73",[],{"year":2380,"number":177,"id":4162,"episodes":5473,"show":5474},[4164,4165,4166,4167,4168,4169,4170,4171,4172,4173,4137],{"title":322,"slug":323},{"id":5476,"slug":5477,"vimeo_id":5478,"description":5479,"tile":5480,"length":3992,"resources":12,"people":12,"episode_number":177,"published":5422,"title":5481,"video_transcript_html":12,"video_transcript_text":12,"content":12,"seo":12,"status":19,"episode_people":5482,"recommendations":5483,"season":5484},"587c1568-722d-47db-86a9-3f40bd65e1b3","berlin-at-night","894189736","Filmed by Kevin, a cold winter evening at Alexanderplatz - where trams, trains, and people meet.","f626f999-e517-46de-ac4f-b8f86afa3303","Berlin at Night",[],[],{"year":2380,"number":128,"id":5485,"episodes":5486,"show":5488},"ece5f479-5f49-4858-a966-f785574a21ef",[5487,5476],"c17e0e52-b41a-41d5-abbd-946b19d3a70a",{"title":5489,"slug":5490},"Directus Scenescapes","scenescapes",{"id":4165,"slug":5492,"vimeo_id":5493,"description":5494,"tile":5495,"length":2767,"resources":12,"people":5496,"episode_number":177,"published":5422,"title":5498,"video_transcript_html":5499,"video_transcript_text":5500,"content":12,"seo":12,"status":19,"episode_people":5501,"recommendations":5503,"season":5504},"lms","895940893","From creating and organizing course content, managing instructors and students, and tracking enrollments and completions, Bryant has sixty minutes on the clock.","73ceb5b3-6a79-484f-8254-a4d41ab970b3",[5497],{"name":3590,"url":3591},"Mission: Learning Management System","\u003Cp>Speaker 0: Welcome back to the next episode of 100 apps, 100 hours, where we try to recreate and build your favorite apps in 1 hour or less, or die trying. Hopefully not dying. I'm your host Brian Gillespie, developer advocate at Directus. Super nice to have you. Today we've got a app that is near and dear to my heart.\u003C\u002Fp>\u003Cp>I've created a lot of courses in the past, so we are going to be building a learning management system, an LMS. There's a few examples that I took a look at in the the research for this prior to. I've got my proper dad attire on and we are going to dive in and build some apps. So let's just add a little bit of context for you guys before we start the clock. If you take a look at sites like Kajabe or Podia, these are online learning management systems.\u003C\u002Fp>\u003Cp>Most of these are software as a service where you can sign up for, $159 a month, it looks like, or $199 a month and sell your courses. The interface for these, if we can maybe navigate to one of, Podia's websites here. We've got like a main navigation on the left. We've got, you know, the courses that show up on the right. If we take a look at Udemy, that's a kind of a different case where you're on their platform, you're not white labeling, but you can create your own courses and load those up and sell those to their specific audience.\u003C\u002Fp>\u003Cp>So I think we'll land somewhere in between all of these different tools today. Should be very fun, very interesting. So with that, let's, let's dive into this. I'm gonna start my little mouse pose thing so I can highlight things on screen as we go through and build. So, I want to kind of show you what I am working with just so there is no, you're aware, there's no cheating behind the scenes here.\u003C\u002Fp>\u003Cp>I do have a Directus instance already started. I haven't logged in yet, but that is a blank Instance ready to go. That's what we'll use for the back end of the app. We also have a Nuxt 3 starter application. I do have like a Directus SDK already preconfigured and a couple of utilities just to make building a little bit easier.\u003C\u002Fp>\u003Cp>But aside from that we can see this is just a blank slate. There's a login form, text component and an upload component, just to make this a little easier. A lot of this is boilerplate functionality, I don't want to bore you guys with that. So that's the setup. Alright.\u003C\u002Fp>\u003Cp>Let's dive into building the app and we will start the clock on this. So we now have 60 minutes to develop this out, But before we actually do any code, I want to like sketch this out. And I I love using Figma for this. So the first thing we want to flesh out is our data model. What's our schema?\u003C\u002Fp>\u003Cp>We're going to have courses. So within each course, we have some lessons. We probably have some modules. Alright. Maybe these are not huge per se.\u003C\u002Fp>\u003Cp>We'll just make them smaller. What else are we gonna have? We're gonna have users. We'll probably have some instructors. Right?\u003C\u002Fp>\u003Cp>We want to be able to track the instructors on the course. This is probably a a pretty good starting point. Now as far as the functionality that we need, so let's just call this our data model. Cool. We'll go over here.\u003C\u002Fp>\u003Cp>Inspect this out. Functionality. Alright. Let's go through and flesh this out a little bit. We want to view a list of courses, view an individual course, and this is gonna be on the front end.\u003C\u002Fp>\u003Cp>View individual lessons, we probably need to sign up for our course. We just walk it through the entire process. View individual lessons. We want some authentication, so we wanna be able to log in. And, what else?\u003C\u002Fp>\u003Cp>We wanna be able to track progress. Right? And, to that end we probably need like a collection to track enrollments. So who are our users, what courses are they enrolled in, what is the completion percentage for those. Alright.\u003C\u002Fp>\u003Cp>So this seems like a pretty good start. We'll continually come back and refer to this and refine it as we go through. But the first thing that I wanna do is start into our back end. So I'm gonna pull up Directus in this case, and let's see if we can zoom in just a little bit to make it easier for everybody. Now I've got this running on Docker locally, but the easiest way to run Directus is using Directus Cloud.\u003C\u002Fp>\u003Cp>And I need to make sure I've got the correct password here. Alright. So we've got a blank Directus instance. We're going to flesh out this data model in no time at all. So let's create our 1st collection, we'll call it Courses, and we will use the generated UUID as the primary key field.\u003C\u002Fp>\u003Cp>We'll just go in and for now we'll just select all of these defaults for tracking date created, user created, etcetera, handy to have. And for our courses we probably want a name for the course, that's great. We want to have a description for the course, so that will be a WYSIWYG editor. That way we could support HTML and rich text formatting. What else do we need as as far as our course data?\u003C\u002Fp>\u003Cp>We've probably got an image for the course as well. So we could call this image. We could have multiple images, but let's just stick with a single featured image, and we'll use that. Great. So now if we back up, we've got the start to our courses data model.\u003C\u002Fp>\u003Cp>Let's go in and flesh out the other parts of our course. So each course will be made up of several different modules. So the modules, again, we can add all these fields. We may not need those. I typically just add these because it has some preset functionality.\u003C\u002Fp>\u003Cp>Obviously the user created it will store the UUID for that particular user, updated date and times, those are all handy to have. So the modules, we will have a name for the module, and, you know, we could have like a short description for the module. Let's just stick with name, we'll keep it really simple. The other thing that we may want to have on the courses, this just reminds me, is maybe something like a slug. So just a pretty URL for that specific course.\u003C\u002Fp>\u003Cp>So we'll go in and set that up. One of the other things I could do really quickly in Directus is make sure this is URL safe. So there's just a, a field to set that up for us to automatically format that. Next, let's go in and add our lessons table, or lessons collection. We'll do the same, so what's the status, is this published or is it draft?\u003C\u002Fp>\u003Cp>And now in the lessons we'll have a name for the lesson. A lot of names here. We'll have the, let's call it Lesson Content, and that could be rich text. And because these are primarily videos, let's do a video URL. We can host those on Vimeo, YouTube, Bloom, however you wanted to do that.\u003C\u002Fp>\u003Cp>You could also just actually upload these into Directus as well. Alright, so now we've got kind of this side of the equation figured out. Directus already gives us users out of the box, which is great. We can go in and add these other collections. We'll have an instructor's collection.\u003C\u002Fp>\u003Cp>Instructor's, I hope that's how you spell it. So our course instructors, that will be we'll have a name for them. Let's have a bio, so that'll be a WYSIWYG field. And one of the things that I love most about Directus is just how easy it is to map out this data model and get a complete back end with an AI or API ready to query for my front end. So instead of working with dummy data, I can actually prototype live with real data.\u003C\u002Fp>\u003Cp>So we got our name, we got our bio, let's do an image, or we could call it avatar. Great. Cool. And last but not least, we've got enrollments. So we'll just, create all these separate tables, all these different collections within Directus.\u003C\u002Fp>\u003Cp>And now we can go back and flesh out the relationships between these, because that's where the tricky part comes in. Or not necessarily tricky with Directus, but, we wanna make sure that data model works correctly on the front end. So the when we start mapping these out we have our courses. Each course could have multiple modules, and each module could have multiple lessons. So those are one too many relationships inside Directus.\u003C\u002Fp>\u003Cp>So if we start fleshing this out, we'll go into the Courses collection and we'll use the relational fields inside Directus. So behind the scenes Directus will set up these relationships inside your SQL database and make sure everything plays nice with each other. So we're going to call this modules. So that's going to be our key. The related collection here is going to be modules.\u003C\u002Fp>\u003Cp>And then the foreign key, so our key inside the modules collection is going to be Course. We could choose whether we want a list or table layout, that's fine, and we'll just use the name of the module and we'll show a link to that module. So now our modules are set up and if I were to go to the actual module section we can see we've got the reverse relationship back to the course. And maybe we drag and drop this, give these some icons to make them look nice. So we've got a Course, maybe that is a folder looking object.\u003C\u002Fp>\u003Cp>It's great. Let's use black as the color. So we'll customize this a bit and make it look nice. The module, you know, let's look for a list, maybe. Looks great.\u003C\u002Fp>\u003Cp>And now let's add a lesson, we'll just use like a video icon for that. Perfect. Okay. So now if we go back to our modules, again we're going to create a one to many relationship, but this time we're going to do that on our lessons. So the key here will be called lessons and the related collection will be lessons.\u003C\u002Fp>\u003Cp>Our foreign key will be module. So just the singular because each lesson can only be in a single module. And we'll use the name of that lesson. Great. We'll show a link to the item.\u003C\u002Fp>\u003Cp>And now we've got that structure set up. Great. How do the other items relate? Right? We've got instructors.\u003C\u002Fp>\u003Cp>Instructors could have many different courses inside our platform, and a course could have multiple instructors. So that's a good use case for the many to many relationships inside Directus. And what we'll do is go to either courses or instructors, doesn't really matter which one. And instead of a one to many or a many to 1, we're going to use the many to many relationship. So here we'll call this instructors, that's gonna be our key.\u003C\u002Fp>\u003Cp>For our related collection, we're gonna use instructors. And we don't want to allow duplicates, we want to show a link to the item. If I wanted more control over what Directus creates, or uses for the name of the junction tables, I can go into the advanced field mode. So we can see in our junction collection Directus is going to create this inside our SQL database. Courses underscore instructors, and there's gonna be a Courses ID and an Instructor's ID.\u003C\u002Fp>\u003Cp>I can also add this relationship to the instructors field and we'll give this a sort just in case we want to have a head instructor and, you know, prioritize who is the primary instructor on the course. So we'll hit save, Directus will do some magic behind the scenes, and if I go in now I will see a hidden collection in our data model for courses underscore instructors. Savvy? Everything's looking good so far? Great.\u003C\u002Fp>\u003Cp>Alright, now we also want to flesh out our enrollments. So if we think of enrollments, enrollments are going to be the users that are enrolled in the individual courses and this setup will be a little bit different in that we go to Enrollments, there's gonna be a many to one relationship. So each enrollment can only have one course. So what are the course that this person enrolled in? Great.\u003C\u002Fp>\u003Cp>And then we're also going to have a many to one relationship with the Directus user. In this case we could just call it user as well. But the related collection here is going to be the Directus underscore users. That's our system collection for users within directus that, gives us all the authentication and user access and permissions. We could also keep track of progress or lessons completed, but for now let's just roll with this.\u003C\u002Fp>\u003Cp>Okay, a little OCD over things like icons and colors, so I will add some icons and colors here. We've got instructors. These will be people. Let's see what we've got. Nice friendly guy waving, that's perfect.\u003C\u002Fp>\u003Cp>Let's make that purple as well. Alright, so now if we look at our data model, this is looking pretty good. We will go into the front end and we can see that we've got courses, we've got modules, we've got lessons. Let's just create a course. Let's do published.\u003C\u002Fp>\u003Cp>And we'll call this 100 Apps in 100 hours, great. Watch to follow along as we build an LMS. While building an LMS? I don't know. It's kinda meta.\u003C\u002Fp>\u003Cp>No worries. Alright so we can, let's go to Unsplash and find a nice looking image. One of the nice things about Directus, and you'll hear me say that multiple times throughout this series, is the ability to import from a URL. So I can just copy that URL there, import that image, it will store it in my file library for me. And we'll give this a slug.\u003C\u002Fp>\u003Cp>So you'll see if I press space bar it will automatically format this for me so that it becomes URL safe. And let's go in and create a module. So we'll call this 1st module. We'll create a second module. Great.\u003C\u002Fp>\u003Cp>And we'll add an instructor. The dude building stuff and videos. Cool. I've got let's upload a photo. Great.\u003C\u002Fp>\u003Cp>Good. Solid. Okay. So now we've got our first course. Since we've set up these collections I could start querying this on the front end using the Directus APIs.\u003C\u002Fp>\u003Cp>So So if I just open this up in a new tab, I'm going to change the URL a little bit. We'll go to items\u002Fcourses and boom, we get this permission access error. So I haven't enabled permissions. We've created this actual, collection. We've created several of these collections, but for the general public all these collections are access is forbidden by default.\u003C\u002Fp>\u003Cp>So I could open this up and you know, just allow public access for now to all of these. Once we went to production, obviously we want to restrict this per user. But if I just refresh you can see over here on the left, excuse me, we've got our data coming from the LMS, or the back end of our LMS, our direct us instance. Great. So we are about 15 minutes in.\u003C\u002Fp>\u003Cp>We've got our first bit of data modeling done. Let's dive into kind of the front end and start building, right? We will, I'm just gonna move this out of the way for now, and we'll get to a blank slate inside our code editor. We've got our direct assistance up here, but let's pull this up. Now within the pages directory of my Nuxt application, maybe we create a new folder for courses.\u003C\u002Fp>\u003Cp>This will give us a route slash courses on the front end. And we'll have an index route. That's great. We'll set up a default view component here and now we can start querying that data from Directus. One of the things that I do have in my little Directus module is a composable to actually query the front end of or query the Directus back end.\u003C\u002Fp>\u003Cp>So we can do something like this where we say const courses equals useDirectus. And this is using the Directus SDK on the the back end. We're gonna read items, courses, And I could go in and add some options here as well. You know, Copilot is showing me like a filter for published. But for now, because we haven't published anything, let's just take a look at this.\u003C\u002Fp>\u003Cp>We'll, log this out to the front end. And if we navigate to \u002Fcourses, it looks like we have an object promise, so we wanna do await use directus, and boom, now we've got our data on the front end. Cool. So now we probably want to do a little bit of formatting, you know, add this, add maybe a card for this. One of the things that I am using or I've got set up in this Nuxt instance or this Nuxt template app is the Nuxt UI library, which is just a UI library put out by the Nuxt team.\u003C\u002Fp>\u003Cp>It helps you build apps that look great faster. It works perfectly for stuff like this. On a lot of other projects, I may use, you know, my own custom coded components. But for now let's do a card. We'll do v4 courses and courses because that is an array, course ID.\u003C\u002Fp>\u003Cp>Let's actually just do something like this where we've got v text. I do also have a text component, we'll call it course. Name. And we can use the Nuxt image component, course. Image.\u003C\u002Fp>\u003Cp>See how far that gets us. Okay. So we've got a card. We probably want to give a little bit of styling to the container. What is the I guess the muxu container It constrains the width.\u003C\u002Fp>\u003Cp>Okay. And then maybe we've got let's just go back. We'll do the keep this really simple. I also, use tailwind a lot. We'll do like a max width of 4 XL for now.\u003C\u002Fp>\u003Cp>Make this in x auto. And maybe we set these up in a grid with 3 columns on, like a large size. Okay. So we've got this actual course card. We're not rendering the actual image, which, is concerning me.\u003C\u002Fp>\u003Cp>You don't have permission to access this. So we've hit our first kind of snag here. Somehow I've managed to log myself out of Directus as well. So let's log back in. Directus.\u003C\u002Fp>\u003Cp>Alright. And now we will go in and we forgot to set permissions for our system collection. So all the Directus files, we're just going to make those public for now so we can take a look at that file. I could now see that and it should show up within our card as well. Great.\u003C\u002Fp>\u003Cp>You know, maybe we make this font bold, 2 x l. Make it a bit bigger. Let's make it giant, right, 4 x l. I need to take a look at my Vtex component actually. I think I've already got sizes on here, so we could just use the size prop instead.\u003C\u002Fp>\u003Cp>2XL. Great. Okay. Alright. So now we've got kind of an index page.\u003C\u002Fp>\u003Cp>This is a course listing. Let's wrap this with, another div. I'll go in and paste that. So now we've got a course listing, we've got a course card, How do we get to this actual course detail? So I will go in and, now we'll go into our courses folder within the pages directory And let's create actually let's create a new folder.\u003C\u002Fp>\u003Cp>And we'll use the brackets for this. This will give us a dynamic route. And we will use like slug or I could call this course or ID. We're using slug as the property for the course on the directive side, if I remember correctly. So we've got a slug for each of the courses.\u003C\u002Fp>\u003Cp>That's what we'll query by. Sounds great. And we will go in and now we will add like an index page for this course. So again, we'll build a detailed page here, just a simple index page, and let's do course, wait, use direct us, read item. We wanna read a single item.\u003C\u002Fp>\u003Cp>This is using our SDK context. And we'll use route params slug. That's not gonna cut it. Actually, we want to read multiple items. And the reason why is because we're giving each course an ID, a a UUID.\u003C\u002Fp>\u003Cp>And the Directus SDK, when you read a single item, you can look up by that primary key, in this case the ID field. But where we want to look up by the slug, we're going to use read items and then we're going to construct a filter. So it'll be like this where slug is equal to route dot params.slug. We're gonna use route. So we'll pick up the route from view.\u003C\u002Fp>\u003Cp>We use route dot params.slug. And again, this is why I love building this way with the back end first because I can use actual data to flesh this out. So now on our course listing page we want to add a link to that. So if we go back to the index page, we can wrap, both of these in a Nuxt link component. And for the to prop, let's just add, courses slash course ID or no, course dot slug.\u003C\u002Fp>\u003Cp>Alright, so now we should get a clickable link. We can see at the bottom of the screen it says 100 Apps, 100 Hours. And when I click on it it will give me, an array of the data in this case. But I would rather have just a, I'd rather have the actual image. So let's do something like this where constant course equals courses dot I or core the first item of the array.\u003C\u002Fp>\u003Cp>Maybe this could be a computer prop as well just in case that changes. Courses dot value cannot read properties of undefined. What is going on here? Let's back up and let's just keep it simple. Right.\u003C\u002Fp>\u003Cp>Course equals courses. Okay, there we go. So we've got our course, and now we can start fleshing this out. So if I go in and I look at the Nuxt UI Library, we've got some components here, we've got like a skeleton. We've got a container.\u003C\u002Fp>\u003Cp>We've got a card. What do some of these other apps look like? How do we actually get to their courses? Not a lot of great examples on the website. If we look at Udemy, we could see we've got like a course name, we've got a preview of the course, we've got, some of the lessons here.\u003C\u002Fp>\u003Cp>And then when you get into the actual, enrollment of that specific course, they've got like a kind of a sidebar layout. So let's create a new section. We'll do the course title, course description, follow along as we build. Okay. 100 apps, 100 hours.\u003C\u002Fp>\u003Cp>So the course description, we're gonna use v html for that. Course dot description. Okay. And now we've got that. We could apply some nice styling for this.\u003C\u002Fp>\u003Cp>Let's do the font. Black text 4 x l. We'll do a, let's try that U container again. And this is coming from the Nuxt UI library. Alright.\u003C\u002Fp>\u003Cp>So we got that. We've got, the description. Maybe we wanna add like a small bit of text to label that. This description class text extra small, text gray 500, something like that. And we can space these out a bit.\u003C\u002Fp>\u003Cp>So again, this is a bit crude. We're going for speed over beauty in this case. But we want to flesh out this functionality. Where did you go? Okay, so we got our course name, we got a description.\u003C\u002Fp>\u003Cp>Let's show our instructors. Alright. So this is gonna be our pclasstext. Let's do instructors. Okay.\u003C\u002Fp>\u003Cp>So we've got that. We'll add a bit of margin to it. Space it apart. We'll pull up the instructors. How are we doing on time?\u003C\u002Fp>\u003Cp>Quick time check. Got 30 minutes remaining to flesh this out. Gonna be a definite challenge here. So we've got the instructors. We'll do, should be course dot instructors, right?\u003C\u002Fp>\u003Cp>Let's just log that out and see what we get. Course dot instructors. So you can see Directus is giving me back an array of the different ID's for those instructors. I've said this multiple times, but this is one of the reasons why I really love Directus. Because I can go in and for, this is using the REST API.\u003C\u002Fp>\u003Cp>I can actually go in and do something like this where I say, hey, I want all the root level fields. And then for instructors, I want, I want a list of all the root level fields for the instructor. So I could get all the related fields in a single API call. Now where this is a many to many relationship, you can see I need to drill in one more level. So I have to go in and do one more level where I have instructors underscore ID and grab all those fields.\u003C\u002Fp>\u003Cp>So now I can see the instructor and we can set up like a, just a v four loop here. V four instructor in course dot instructors. And it looks like Copilot has added a lot of code that I did not mean to add here. So let's just pull that back. Be careful how many times you tab, right?\u003C\u002Fp>\u003Cp>So now let's take a look at instructor within that loop and see what we have. Are we even getting anything? Alright. We've got instructors. We've got the instructor ID.\u003C\u002Fp>\u003Cp>K. So we could do something like this. We could actually destructure that. Or can we? Instructor instructor's ID.\u003C\u002Fp>\u003Cp>And let's see what we get back with that. We're breaking stuff. Okay. Yeah. So we'll just run with this for now.\u003C\u002Fp>\u003Cp>Alright. So we've got our instructor, we've got an instructor ID. So we'll have instructor dot instructorsid.name. Let's wrap that P TECH. We'll do, Nuxt Image.\u003C\u002Fp>\u003Cp>And let's do, yeah. Maybe width 24 sounds good. 24 height, rounded full to make this a circle. And then the source is gonna be instructor dot instructorsid.avatar. And then we can do a divvhtml for the bio equals instructor dot instructorsid.bio.\u003C\u002Fp>\u003Cp>Okay. Cool. So now we've got a few things going on here. Let's actually look at the lessons. Right?\u003C\u002Fp>\u003Cp>We'll go into our Directus instance. If we go back to our specific course here, we didn't add any lessons to this specific module. So I'm just gonna pull up the Directus YouTube account. Our YouTube studio here will get to the Directus channel and take a look at our content. So I'm just gonna copy a few of these down.\u003C\u002Fp>\u003Cp>We're gonna add a couple of lessons within each one of these. Just gonna actually take these wholesale from the YouTube channel. Here's some content. Great. Alright.\u003C\u002Fp>\u003Cp>How do we do, we'll copy some of this. How to manage different versions. Blah blah blah, copy that video URL. Oh, that's actually a short. Not sure what that'll do.\u003C\u002Fp>\u003Cp>Let's, let's take an actual video. Great. So we've got our first module. Let's go into the second module and we will add one of my videos. Getting started with Agency OS.\u003C\u002Fp>\u003Cp>My wife tends to make fun of, my sausage fingers all the time. So lots of typos here. So we'll go in. Let's just copy one more of these so we've got something to look at on the front end. Project templates.\u003C\u002Fp>\u003Cp>Great. Okay. So So save this. Now we've got our course, we go back to our course detail page in this instance and, you know, let's add a new section over here where we've got our section. Looks like it get hub copilot for the win.\u003C\u002Fp>\u003Cp>We've got our lessons. Give that some margin. Give some space. And, so if we log each lesson, let's just take a look, we'll probably get the same, let's just close this out real quick. We'll do course dot lessons just to see what we're getting back from the API, which it doesn't look like anything, right?\u003C\u002Fp>\u003Cp>So if I open up the dev tools, we take a look at the network request, we dive into the data, the lessons are actually going to come through the different modules. Alright, so again, we can go back up to our fields section and we could do something like this where we go to modules, then we, drill down again into, our lessons. And we want to grab the module name. And within our lessons, again, we could just grab all the root level fields within that. And now we can see that second API call that was made.\u003C\u002Fp>\u003Cp>We've got our modules. We've got our lessons within that. And again, I only have to make a single API call. You know, we can certainly cache this if this data doesn't change, but it keeps the entire app very snappy and prevents me from having to make 35 different calls to get all the related data. Also kind of it follows a very GraphQL like structure as well where I can request just the data that I need.\u003C\u002Fp>\u003Cp>I don't necessarily have to go in and use these wildcards to grab those root level fields. I could go in and say something like name, description, that way I can prevent over fetching and larger network requests than I actually need. So we've got, now we've got our data coming in correctly. Let's go through and we're going to loop through the modules. So we'll do v 4 module in course dot modules, and then inside that we'll have another loop for the lessons.\u003C\u002Fp>\u003Cp>So we've got our second module. Let's style these a bit. So maybe we wrap this, do something like divide y and divide y, divide gray 300 maybe? Okay. So now we've got a divider between our different modules.\u003C\u002Fp>\u003Cp>And maybe we give each module some padding. That's probably a bit so we'll do p y 4. Let's style the module a bit And font, let's just give it a little fancier font. I think I've got this set up as maybe Poppins inside my specific account. Font display.\u003C\u002Fp>\u003Cp>Great. Div 4. Okay. We got the we don't really need the lesson content. And then let's style each one of the lessons.\u003C\u002Fp>\u003Cp>You know, we'll likely change these to a NUCs link and then the 2. Now we want to dive into each individual lesson. So our URL structure is going to be courses, we've got course, so we use the dollar sign, so so we get the template literals here. We've got course dot ID and then we've got lessons and then maybe lesson dot ID or it we could use slugs on the lessons as well if we wanted to. Let me fix this.\u003C\u002Fp>\u003Cp>We've got slugdot ID. Okay. Unterminated template literal. Okay. Alright.\u003C\u002Fp>\u003Cp>So now we've got a URL that should take us to this specific lesson. I'm gonna go back into our Directus instance, which keeps logging me out, probably a cookie or some type of setup issue on my end when I configured Docker, but we'll work around it for now. Let's go in and give a slug for the individual lessons as well. So, just to maintain parity. We'll go into the interface, we'll make slugify checked, and now we've got a slug for the lessons.\u003C\u002Fp>\u003Cp>Let's go into each one of those lessons and set a slug. Project templates, managing versions. Alright. Getting started. And custom operations.\u003C\u002Fp>\u003Cp>Okay. So now each one of those have a slug. We can change this from ID to slug. We'll still use that ID as the the key for looping over these. But now we're starting to get something that kind of looks like, some courses.\u003C\u002Fp>\u003Cp>You know, maybe we wrap these in a go back. Maybe we wrap these in a card component. And a lot of times as I go along, I would definitely be refactoring these as we went along into components that made sense. So we've got the padding, p y 4. Let's do some space between each card.\u003C\u002Fp>\u003Cp>So we've got a little bit of space between each card. That's great. There's our different modules. I don't know why the first module is showing, below the second module. That could be the way that we've got our sorting on the course.\u003C\u002Fp>\u003Cp>So if we go in, we don't have sorting enabled. So what I can do is go into our Courses section, our Courses Data Model, we'll go to the relationship field, or the relationship tab, and for our sort field we're just going to add that sort property, or that sort field on that. And that should take care of the sorting for us. So now we've got our first module, that's great. We've got our second module, starting to look like something.\u003C\u002Fp>\u003Cp>Let's just take one moment and make a, like a nice little header for this. We've got image course dot image, object cover, And we'll give this a little bit of padding as well. MT 8. Maybe we want to round the corners. Again, very crude but, definitely paints the picture.\u003C\u002Fp>\u003Cp>So we've got about 18 minutes left. Let's dive into the actual lessons, Right? So a pretty common set up when we go to the lessons, and right now we're getting a Page Error Not Found, is to have a list of the other lessons over here on the left hand side, and then on the right hand side we've got the actual course content. So let's figure out what that would actually look like inside here. So one of the nice things about Nuxt is like the nested routes.\u003C\u002Fp>\u003Cp>So we go to their documentation, we go to, it's like child routes. Child route keys, nested routes. So I can set up, like parent and child collections, or pages, within these nested routes so that, it's a nice way to do routing, where I can have a child page show up in the parent view. So let's go in and we've got our slug here. So this is our actual course.\u003C\u002Fp>\u003Cp>Within the course, maybe we want to show let's do a slug. I don't know if this will be it or not, slug dot view, So we'll create a new folder for lessons and then we're going to create a dynamic route for the lesson. So we'll call that lesson dot view v comp ts. We're going to use the route, so route equals use route. And here I'm just gonna log the params to make sure we're on the correct spot.\u003C\u002Fp>\u003Cp>Alright. So we can't find the slug dot view. That's great. Let's add a component for that. Courses, 100 apps, lessons, managing versions, slug.\u003C\u002Fp>\u003Cp>Let's just say this is the course parent. What does that get us? Alright. So we've got managing versions. We don't see the course parent part of it here.\u003C\u002Fp>\u003Cp>Okay. So let's call this lessons. And now all we should see is course parents. But if I go in, and I can't remember the exact syntax, so let's go back to nuxt.com. We'll do the child routes, nested routes.\u003C\u002Fp>\u003Cp>It is what? NUXT page. So we just throw this NUXT page in here And now we see this where we have Course Parent, and then we have our NUCs page which gets rendered inside that. So why are we doing it this way? Because we can actually fetch the lessons within this parent component and then, that will not re render, but then we can navigate within these individual lessons and render those over here on the right.\u003C\u002Fp>\u003Cp>So if we do something like this where, let's flesh this out, we've got a, let's do a div. Alright. We could do, like, an aside maybe. This is the list of lessons. Then we've got another div.\u003C\u002Fp>\u003Cp>We'll render that, and we'll just flex these. Flex. We'll add, let's just maybe give this a fixed width of like 56 or 64 wide. Alright. And just to illustrate this, we'll give it like a background, a bggray100.\u003C\u002Fp>\u003Cp>So we've got our list of lessons there. You Gonna add some padding for that, p 4. Then our next page, we probably got a new container. I need to actually look up order the the settings for that. Let's take a look at it.\u003C\u002Fp>\u003Cp>You container, constrain the width of your content. Max width 7 x l. Okay. Yeah. Works fine for me.\u003C\u002Fp>\u003Cp>Alright. And this could be probably height full. We want it to be the full height or height, screen. That'll get us. Cool.\u003C\u002Fp>\u003Cp>Alright. So now we can go in and render the list of lessons here. So we will grab our lessons. So let's do const lessons equals await. You got GitHub Copilot for the win.\u003C\u002Fp>\u003Cp>We're gonna read the items from the lessons. The filter is gonna be where the course dot slug equals route dot params.slug. So we're gonna pick up the we can pick up the course from the course slug. And let's actually see what we get here. I refresh the page.\u003C\u002Fp>\u003Cp>We're breaking some stuff. Course, slug, route. Oh, we actually have to grab the route, don't we? Forbidden. So I can't get this information.\u003C\u002Fp>\u003Cp>Wonder why that is. Let's take a look. We'll just erase that filter, see what data we're getting back, in this list of lessons. So I'm gonna quickly wrap that and, just output the lessons. Okay.\u003C\u002Fp>\u003Cp>So there's our individual lessons. Looks like those are actually coming through. Okay. But if we go to course dot slug is equal to, Let's just take a look at our network request. Alright?\u003C\u002Fp>\u003Cp>This is being fetched on the server side. You don't have a permission to access this, which makes me think something is wrong. Alright. So let's just look through this really quickly to see if we can find our module. Duh.\u003C\u002Fp>\u003Cp>That's because our modules are not, our lessons are not actually linked to our course, they are with our modules. So the the courses belong or the lessons belong to the individual modules. So, fun debugging issue here, but let's just go into our original index where we get the course, we get our lessons, and we can actually copy this code. And within the lesson route, her lessons where's our parent component? We could do this.\u003C\u002Fp>\u003Cp>Right. And here we could probably adjust it where the slug or the course, slug. So we get all the modules instead of the lessons, which is a little bit counterintuitive. But we still wanna display all those modules within that bar anyway. Alright.\u003C\u002Fp>\u003Cp>So we get our modules. We'll change this to modules. And we'll make sure that we grab the lessons as well. Let's just clean this up and refresh. Something is breaking.\u003C\u002Fp>\u003Cp>We don't get our modules. Route is not defined. Again, silly me, silly rabbit, you have to use the route. Alright. So now we can loop through these modules.\u003C\u002Fp>\u003Cp>And what does GitHub Copilot got for us? Alright. So we've got our different modules here, how to manage different versions, how operations can be completed. Okay, great. Nuxt link courses, route planarams.\u003C\u002Fp>\u003Cp>Slug. And now if I am navigating between these over here on the right you can see that this is actually changing. You can see that we're logging the slug for that specific lesson. So if we go into the lesson, we've got route params. Lesson.\u003C\u002Fp>\u003Cp>Now within this specific component we will call the lesson that we want. But what we'll do is make sure that we update this a little bit. And we're gonna use the lesson equals lessons if we just log lesson over here. Alright. Boom.\u003C\u002Fp>\u003Cp>We can see that. If I go back, I wanted to make sure this doesn't shrink. Select shrink. 0. Alright.\u003C\u002Fp>\u003Cp>And now on our individual lesson, let's actually set this up. So we got the lesson name. We got the lesson content. It's great. And then we will have a where it'll be an iframe?\u003C\u002Fp>\u003Cp>For the YouTube Embed. And I actually think do I have do I have Do I have a video string for this? I don't. Generate lesson and video. What is the YouTube embed?\u003C\u002Fp>\u003Cp>Alright. So we're getting the URL from YouTube. So if I log in to the lessons here, how do we transform this? Closing in on what? We're gonna cut this one down to the wire.\u003C\u002Fp>\u003Cp>Right? We've got 6 minutes and 55 seconds here. How do we actually render out this lesson content? We are going to do, let's rely on chat GPT or get up copilot. Right?\u003C\u002Fp>\u003Cp>Function to generate YouTube embed URL. Okay. So now we're gonna get youtubeurllesson.video. Let's see if this actually works. Cannot read properties of undefined split.\u003C\u002Fp>\u003Cp>So we'll do if, oh, I know what it is, we've used video URL instead of video. A lot of these issues can be avoided by using TypeScript, but lesson let's do a v f. Cannot read properties index of undefined. YouTube video ID. So we need to take a look at Directus.\u003C\u002Fp>\u003Cp>It's what's fun about doing these things live. You never know exactly what's gonna go wrong. And when you're under the gun, the pressure is mounting. Don't necessarily know. Can't even get the password right now.\u003C\u002Fp>\u003Cp>Hallelujah. There we go. Alright. So in this case, we want to, accounting for YouTube URL variations. Let's see what this thing comes up with.\u003C\u002Fp>\u003Cp>And you to that be Will this actually work? Hey. There we go. Alright. We probably want to do, like, an aspect ratio, aspect video.\u003C\u002Fp>\u003Cp>Okay. And so now we have our content. We could do a v if, so there is no lesson content here. And boom. So now if I click through these, we should be loading our different courses.\u003C\u002Fp>\u003Cp>So at this point we are at 3 minutes and 44 seconds. So we did not get as far along as I thought we would get on the front end of this. But, going in and extending this further, one of the things that I would do next is probably taking a look at building this out and adding enrollments to this. So we could set up a like authentication for this so that, let's just say on the access control side of it, instead of allowing all the courses to be seen we could certainly let people view all the courses, see the instructors. But as far as the actual lessons, maybe we didn't want to give them access to that until they were enrolled.\u003C\u002Fp>\u003Cp>But for anybody that was enrolled, they could see all of those courses. So now if I refresh the page, we're gonna see like an error that says, hey, this is forbidden because we cannot see this actual course. But if I were to go into my authentication page, let's just say auth\u002Flogin and use my Directus URL or my Directus username and password. Go back a couple pages. Where are you?\u003C\u002Fp>\u003Cp>Courses. Alright. We go into that and we can actually see those. And that's because this user is that user is now authenticated as the administrator and they have full rights. But we could set up a public and then a user specific role where we've got courses, we've got instructors, you can see all of that, where we want to see only the, like, courses that that person has access to.\u003C\u002Fp>\u003Cp>So, let's just go with no access, like modules, that's fine. But as far as the lessons, you would have to set up like a a role here or, like a custom permission for this specific user. Taking that a step further, you know, we could get into like, making it work with Stripe, setting up some of the different, payment options. Like if we took a look at Udemy, there are, you know, trial options. You could pay individually for courses.\u003C\u002Fp>\u003Cp>You could set up subscriptions for this. But, clearly we we built the back end of our LMS, but, really a little disappointed on how far we made it through the front end, considering I've actually built several of these systems myself. But we've definitely learned a lot. So if we take a look at our list of functionality, how do we do? We got to view a list of courses, we got to the individual courses, we got the individual lessons, We did have the authentication and login, but as far as tracking progress, and enrolling for a course, I give myself, what, 4 out of 6 or 4 out of 7.\u003C\u002Fp>\u003Cp>Not a stellar effort here. So that is this lesson of 100 Apps, 100 Hours. I hope it's been a great example of how quickly you can build functionality like an LMS inside direct us and with front end tools. But there is a reason these other products exist like Podia, Kajabe, Udemy, Teachable, all of those. Rome wasn't built in 1 hour.\u003C\u002Fp>\u003Cp>So hope to catch you on the next episode. That's all I've got for you on this one.\u003C\u002Fp>","Welcome back to the next episode of 100 apps, 100 hours, where we try to recreate and build your favorite apps in 1 hour or less, or die trying. Hopefully not dying. I'm your host Brian Gillespie, developer advocate at Directus. Super nice to have you. Today we've got a app that is near and dear to my heart. I've created a lot of courses in the past, so we are going to be building a learning management system, an LMS. There's a few examples that I took a look at in the the research for this prior to. I've got my proper dad attire on and we are going to dive in and build some apps. So let's just add a little bit of context for you guys before we start the clock. If you take a look at sites like Kajabe or Podia, these are online learning management systems. Most of these are software as a service where you can sign up for, $159 a month, it looks like, or $199 a month and sell your courses. The interface for these, if we can maybe navigate to one of, Podia's websites here. We've got like a main navigation on the left. We've got, you know, the courses that show up on the right. If we take a look at Udemy, that's a kind of a different case where you're on their platform, you're not white labeling, but you can create your own courses and load those up and sell those to their specific audience. So I think we'll land somewhere in between all of these different tools today. Should be very fun, very interesting. So with that, let's, let's dive into this. I'm gonna start my little mouse pose thing so I can highlight things on screen as we go through and build. So, I want to kind of show you what I am working with just so there is no, you're aware, there's no cheating behind the scenes here. I do have a Directus instance already started. I haven't logged in yet, but that is a blank Instance ready to go. That's what we'll use for the back end of the app. We also have a Nuxt 3 starter application. I do have like a Directus SDK already preconfigured and a couple of utilities just to make building a little bit easier. But aside from that we can see this is just a blank slate. There's a login form, text component and an upload component, just to make this a little easier. A lot of this is boilerplate functionality, I don't want to bore you guys with that. So that's the setup. Alright. Let's dive into building the app and we will start the clock on this. So we now have 60 minutes to develop this out, But before we actually do any code, I want to like sketch this out. And I I love using Figma for this. So the first thing we want to flesh out is our data model. What's our schema? We're going to have courses. So within each course, we have some lessons. We probably have some modules. Alright. Maybe these are not huge per se. We'll just make them smaller. What else are we gonna have? We're gonna have users. We'll probably have some instructors. Right? We want to be able to track the instructors on the course. This is probably a a pretty good starting point. Now as far as the functionality that we need, so let's just call this our data model. Cool. We'll go over here. Inspect this out. Functionality. Alright. Let's go through and flesh this out a little bit. We want to view a list of courses, view an individual course, and this is gonna be on the front end. View individual lessons, we probably need to sign up for our course. We just walk it through the entire process. View individual lessons. We want some authentication, so we wanna be able to log in. And, what else? We wanna be able to track progress. Right? And, to that end we probably need like a collection to track enrollments. So who are our users, what courses are they enrolled in, what is the completion percentage for those. Alright. So this seems like a pretty good start. We'll continually come back and refer to this and refine it as we go through. But the first thing that I wanna do is start into our back end. So I'm gonna pull up Directus in this case, and let's see if we can zoom in just a little bit to make it easier for everybody. Now I've got this running on Docker locally, but the easiest way to run Directus is using Directus Cloud. And I need to make sure I've got the correct password here. Alright. So we've got a blank Directus instance. We're going to flesh out this data model in no time at all. So let's create our 1st collection, we'll call it Courses, and we will use the generated UUID as the primary key field. We'll just go in and for now we'll just select all of these defaults for tracking date created, user created, etcetera, handy to have. And for our courses we probably want a name for the course, that's great. We want to have a description for the course, so that will be a WYSIWYG editor. That way we could support HTML and rich text formatting. What else do we need as as far as our course data? We've probably got an image for the course as well. So we could call this image. We could have multiple images, but let's just stick with a single featured image, and we'll use that. Great. So now if we back up, we've got the start to our courses data model. Let's go in and flesh out the other parts of our course. So each course will be made up of several different modules. So the modules, again, we can add all these fields. We may not need those. I typically just add these because it has some preset functionality. Obviously the user created it will store the UUID for that particular user, updated date and times, those are all handy to have. So the modules, we will have a name for the module, and, you know, we could have like a short description for the module. Let's just stick with name, we'll keep it really simple. The other thing that we may want to have on the courses, this just reminds me, is maybe something like a slug. So just a pretty URL for that specific course. So we'll go in and set that up. One of the other things I could do really quickly in Directus is make sure this is URL safe. So there's just a, a field to set that up for us to automatically format that. Next, let's go in and add our lessons table, or lessons collection. We'll do the same, so what's the status, is this published or is it draft? And now in the lessons we'll have a name for the lesson. A lot of names here. We'll have the, let's call it Lesson Content, and that could be rich text. And because these are primarily videos, let's do a video URL. We can host those on Vimeo, YouTube, Bloom, however you wanted to do that. You could also just actually upload these into Directus as well. Alright, so now we've got kind of this side of the equation figured out. Directus already gives us users out of the box, which is great. We can go in and add these other collections. We'll have an instructor's collection. Instructor's, I hope that's how you spell it. So our course instructors, that will be we'll have a name for them. Let's have a bio, so that'll be a WYSIWYG field. And one of the things that I love most about Directus is just how easy it is to map out this data model and get a complete back end with an AI or API ready to query for my front end. So instead of working with dummy data, I can actually prototype live with real data. So we got our name, we got our bio, let's do an image, or we could call it avatar. Great. Cool. And last but not least, we've got enrollments. So we'll just, create all these separate tables, all these different collections within Directus. And now we can go back and flesh out the relationships between these, because that's where the tricky part comes in. Or not necessarily tricky with Directus, but, we wanna make sure that data model works correctly on the front end. So the when we start mapping these out we have our courses. Each course could have multiple modules, and each module could have multiple lessons. So those are one too many relationships inside Directus. So if we start fleshing this out, we'll go into the Courses collection and we'll use the relational fields inside Directus. So behind the scenes Directus will set up these relationships inside your SQL database and make sure everything plays nice with each other. So we're going to call this modules. So that's going to be our key. The related collection here is going to be modules. And then the foreign key, so our key inside the modules collection is going to be Course. We could choose whether we want a list or table layout, that's fine, and we'll just use the name of the module and we'll show a link to that module. So now our modules are set up and if I were to go to the actual module section we can see we've got the reverse relationship back to the course. And maybe we drag and drop this, give these some icons to make them look nice. So we've got a Course, maybe that is a folder looking object. It's great. Let's use black as the color. So we'll customize this a bit and make it look nice. The module, you know, let's look for a list, maybe. Looks great. And now let's add a lesson, we'll just use like a video icon for that. Perfect. Okay. So now if we go back to our modules, again we're going to create a one to many relationship, but this time we're going to do that on our lessons. So the key here will be called lessons and the related collection will be lessons. Our foreign key will be module. So just the singular because each lesson can only be in a single module. And we'll use the name of that lesson. Great. We'll show a link to the item. And now we've got that structure set up. Great. How do the other items relate? Right? We've got instructors. Instructors could have many different courses inside our platform, and a course could have multiple instructors. So that's a good use case for the many to many relationships inside Directus. And what we'll do is go to either courses or instructors, doesn't really matter which one. And instead of a one to many or a many to 1, we're going to use the many to many relationship. So here we'll call this instructors, that's gonna be our key. For our related collection, we're gonna use instructors. And we don't want to allow duplicates, we want to show a link to the item. If I wanted more control over what Directus creates, or uses for the name of the junction tables, I can go into the advanced field mode. So we can see in our junction collection Directus is going to create this inside our SQL database. Courses underscore instructors, and there's gonna be a Courses ID and an Instructor's ID. I can also add this relationship to the instructors field and we'll give this a sort just in case we want to have a head instructor and, you know, prioritize who is the primary instructor on the course. So we'll hit save, Directus will do some magic behind the scenes, and if I go in now I will see a hidden collection in our data model for courses underscore instructors. Savvy? Everything's looking good so far? Great. Alright, now we also want to flesh out our enrollments. So if we think of enrollments, enrollments are going to be the users that are enrolled in the individual courses and this setup will be a little bit different in that we go to Enrollments, there's gonna be a many to one relationship. So each enrollment can only have one course. So what are the course that this person enrolled in? Great. And then we're also going to have a many to one relationship with the Directus user. In this case we could just call it user as well. But the related collection here is going to be the Directus underscore users. That's our system collection for users within directus that, gives us all the authentication and user access and permissions. We could also keep track of progress or lessons completed, but for now let's just roll with this. Okay, a little OCD over things like icons and colors, so I will add some icons and colors here. We've got instructors. These will be people. Let's see what we've got. Nice friendly guy waving, that's perfect. Let's make that purple as well. Alright, so now if we look at our data model, this is looking pretty good. We will go into the front end and we can see that we've got courses, we've got modules, we've got lessons. Let's just create a course. Let's do published. And we'll call this 100 Apps in 100 hours, great. Watch to follow along as we build an LMS. While building an LMS? I don't know. It's kinda meta. No worries. Alright so we can, let's go to Unsplash and find a nice looking image. One of the nice things about Directus, and you'll hear me say that multiple times throughout this series, is the ability to import from a URL. So I can just copy that URL there, import that image, it will store it in my file library for me. And we'll give this a slug. So you'll see if I press space bar it will automatically format this for me so that it becomes URL safe. And let's go in and create a module. So we'll call this 1st module. We'll create a second module. Great. And we'll add an instructor. The dude building stuff and videos. Cool. I've got let's upload a photo. Great. Good. Solid. Okay. So now we've got our first course. Since we've set up these collections I could start querying this on the front end using the Directus APIs. So So if I just open this up in a new tab, I'm going to change the URL a little bit. We'll go to items\u002Fcourses and boom, we get this permission access error. So I haven't enabled permissions. We've created this actual, collection. We've created several of these collections, but for the general public all these collections are access is forbidden by default. So I could open this up and you know, just allow public access for now to all of these. Once we went to production, obviously we want to restrict this per user. But if I just refresh you can see over here on the left, excuse me, we've got our data coming from the LMS, or the back end of our LMS, our direct us instance. Great. So we are about 15 minutes in. We've got our first bit of data modeling done. Let's dive into kind of the front end and start building, right? We will, I'm just gonna move this out of the way for now, and we'll get to a blank slate inside our code editor. We've got our direct assistance up here, but let's pull this up. Now within the pages directory of my Nuxt application, maybe we create a new folder for courses. This will give us a route slash courses on the front end. And we'll have an index route. That's great. We'll set up a default view component here and now we can start querying that data from Directus. One of the things that I do have in my little Directus module is a composable to actually query the front end of or query the Directus back end. So we can do something like this where we say const courses equals useDirectus. And this is using the Directus SDK on the the back end. We're gonna read items, courses, And I could go in and add some options here as well. You know, Copilot is showing me like a filter for published. But for now, because we haven't published anything, let's just take a look at this. We'll, log this out to the front end. And if we navigate to \u002Fcourses, it looks like we have an object promise, so we wanna do await use directus, and boom, now we've got our data on the front end. Cool. So now we probably want to do a little bit of formatting, you know, add this, add maybe a card for this. One of the things that I am using or I've got set up in this Nuxt instance or this Nuxt template app is the Nuxt UI library, which is just a UI library put out by the Nuxt team. It helps you build apps that look great faster. It works perfectly for stuff like this. On a lot of other projects, I may use, you know, my own custom coded components. But for now let's do a card. We'll do v4 courses and courses because that is an array, course ID. Let's actually just do something like this where we've got v text. I do also have a text component, we'll call it course. Name. And we can use the Nuxt image component, course. Image. See how far that gets us. Okay. So we've got a card. We probably want to give a little bit of styling to the container. What is the I guess the muxu container It constrains the width. Okay. And then maybe we've got let's just go back. We'll do the keep this really simple. I also, use tailwind a lot. We'll do like a max width of 4 XL for now. Make this in x auto. And maybe we set these up in a grid with 3 columns on, like a large size. Okay. So we've got this actual course card. We're not rendering the actual image, which, is concerning me. You don't have permission to access this. So we've hit our first kind of snag here. Somehow I've managed to log myself out of Directus as well. So let's log back in. Directus. Alright. And now we will go in and we forgot to set permissions for our system collection. So all the Directus files, we're just going to make those public for now so we can take a look at that file. I could now see that and it should show up within our card as well. Great. You know, maybe we make this font bold, 2 x l. Make it a bit bigger. Let's make it giant, right, 4 x l. I need to take a look at my Vtex component actually. I think I've already got sizes on here, so we could just use the size prop instead. 2XL. Great. Okay. Alright. So now we've got kind of an index page. This is a course listing. Let's wrap this with, another div. I'll go in and paste that. So now we've got a course listing, we've got a course card, How do we get to this actual course detail? So I will go in and, now we'll go into our courses folder within the pages directory And let's create actually let's create a new folder. And we'll use the brackets for this. This will give us a dynamic route. And we will use like slug or I could call this course or ID. We're using slug as the property for the course on the directive side, if I remember correctly. So we've got a slug for each of the courses. That's what we'll query by. Sounds great. And we will go in and now we will add like an index page for this course. So again, we'll build a detailed page here, just a simple index page, and let's do course, wait, use direct us, read item. We wanna read a single item. This is using our SDK context. And we'll use route params slug. That's not gonna cut it. Actually, we want to read multiple items. And the reason why is because we're giving each course an ID, a a UUID. And the Directus SDK, when you read a single item, you can look up by that primary key, in this case the ID field. But where we want to look up by the slug, we're going to use read items and then we're going to construct a filter. So it'll be like this where slug is equal to route dot params.slug. We're gonna use route. So we'll pick up the route from view. We use route dot params.slug. And again, this is why I love building this way with the back end first because I can use actual data to flesh this out. So now on our course listing page we want to add a link to that. So if we go back to the index page, we can wrap, both of these in a Nuxt link component. And for the to prop, let's just add, courses slash course ID or no, course dot slug. Alright, so now we should get a clickable link. We can see at the bottom of the screen it says 100 Apps, 100 Hours. And when I click on it it will give me, an array of the data in this case. But I would rather have just a, I'd rather have the actual image. So let's do something like this where constant course equals courses dot I or core the first item of the array. Maybe this could be a computer prop as well just in case that changes. Courses dot value cannot read properties of undefined. What is going on here? Let's back up and let's just keep it simple. Right. Course equals courses. Okay, there we go. So we've got our course, and now we can start fleshing this out. So if I go in and I look at the Nuxt UI Library, we've got some components here, we've got like a skeleton. We've got a container. We've got a card. What do some of these other apps look like? How do we actually get to their courses? Not a lot of great examples on the website. If we look at Udemy, we could see we've got like a course name, we've got a preview of the course, we've got, some of the lessons here. And then when you get into the actual, enrollment of that specific course, they've got like a kind of a sidebar layout. So let's create a new section. We'll do the course title, course description, follow along as we build. Okay. 100 apps, 100 hours. So the course description, we're gonna use v html for that. Course dot description. Okay. And now we've got that. We could apply some nice styling for this. Let's do the font. Black text 4 x l. We'll do a, let's try that U container again. And this is coming from the Nuxt UI library. Alright. So we got that. We've got, the description. Maybe we wanna add like a small bit of text to label that. This description class text extra small, text gray 500, something like that. And we can space these out a bit. So again, this is a bit crude. We're going for speed over beauty in this case. But we want to flesh out this functionality. Where did you go? Okay, so we got our course name, we got a description. Let's show our instructors. Alright. So this is gonna be our pclasstext. Let's do instructors. Okay. So we've got that. We'll add a bit of margin to it. Space it apart. We'll pull up the instructors. How are we doing on time? Quick time check. Got 30 minutes remaining to flesh this out. Gonna be a definite challenge here. So we've got the instructors. We'll do, should be course dot instructors, right? Let's just log that out and see what we get. Course dot instructors. So you can see Directus is giving me back an array of the different ID's for those instructors. I've said this multiple times, but this is one of the reasons why I really love Directus. Because I can go in and for, this is using the REST API. I can actually go in and do something like this where I say, hey, I want all the root level fields. And then for instructors, I want, I want a list of all the root level fields for the instructor. So I could get all the related fields in a single API call. Now where this is a many to many relationship, you can see I need to drill in one more level. So I have to go in and do one more level where I have instructors underscore ID and grab all those fields. So now I can see the instructor and we can set up like a, just a v four loop here. V four instructor in course dot instructors. And it looks like Copilot has added a lot of code that I did not mean to add here. So let's just pull that back. Be careful how many times you tab, right? So now let's take a look at instructor within that loop and see what we have. Are we even getting anything? Alright. We've got instructors. We've got the instructor ID. K. So we could do something like this. We could actually destructure that. Or can we? Instructor instructor's ID. And let's see what we get back with that. We're breaking stuff. Okay. Yeah. So we'll just run with this for now. Alright. So we've got our instructor, we've got an instructor ID. So we'll have instructor dot instructorsid.name. Let's wrap that P TECH. We'll do, Nuxt Image. And let's do, yeah. Maybe width 24 sounds good. 24 height, rounded full to make this a circle. And then the source is gonna be instructor dot instructorsid.avatar. And then we can do a divvhtml for the bio equals instructor dot instructorsid.bio. Okay. Cool. So now we've got a few things going on here. Let's actually look at the lessons. Right? We'll go into our Directus instance. If we go back to our specific course here, we didn't add any lessons to this specific module. So I'm just gonna pull up the Directus YouTube account. Our YouTube studio here will get to the Directus channel and take a look at our content. So I'm just gonna copy a few of these down. We're gonna add a couple of lessons within each one of these. Just gonna actually take these wholesale from the YouTube channel. Here's some content. Great. Alright. How do we do, we'll copy some of this. How to manage different versions. Blah blah blah, copy that video URL. Oh, that's actually a short. Not sure what that'll do. Let's, let's take an actual video. Great. So we've got our first module. Let's go into the second module and we will add one of my videos. Getting started with Agency OS. My wife tends to make fun of, my sausage fingers all the time. So lots of typos here. So we'll go in. Let's just copy one more of these so we've got something to look at on the front end. Project templates. Great. Okay. So So save this. Now we've got our course, we go back to our course detail page in this instance and, you know, let's add a new section over here where we've got our section. Looks like it get hub copilot for the win. We've got our lessons. Give that some margin. Give some space. And, so if we log each lesson, let's just take a look, we'll probably get the same, let's just close this out real quick. We'll do course dot lessons just to see what we're getting back from the API, which it doesn't look like anything, right? So if I open up the dev tools, we take a look at the network request, we dive into the data, the lessons are actually going to come through the different modules. Alright, so again, we can go back up to our fields section and we could do something like this where we go to modules, then we, drill down again into, our lessons. And we want to grab the module name. And within our lessons, again, we could just grab all the root level fields within that. And now we can see that second API call that was made. We've got our modules. We've got our lessons within that. And again, I only have to make a single API call. You know, we can certainly cache this if this data doesn't change, but it keeps the entire app very snappy and prevents me from having to make 35 different calls to get all the related data. Also kind of it follows a very GraphQL like structure as well where I can request just the data that I need. I don't necessarily have to go in and use these wildcards to grab those root level fields. I could go in and say something like name, description, that way I can prevent over fetching and larger network requests than I actually need. So we've got, now we've got our data coming in correctly. Let's go through and we're going to loop through the modules. So we'll do v 4 module in course dot modules, and then inside that we'll have another loop for the lessons. So we've got our second module. Let's style these a bit. So maybe we wrap this, do something like divide y and divide y, divide gray 300 maybe? Okay. So now we've got a divider between our different modules. And maybe we give each module some padding. That's probably a bit so we'll do p y 4. Let's style the module a bit And font, let's just give it a little fancier font. I think I've got this set up as maybe Poppins inside my specific account. Font display. Great. Div 4. Okay. We got the we don't really need the lesson content. And then let's style each one of the lessons. You know, we'll likely change these to a NUCs link and then the 2. Now we want to dive into each individual lesson. So our URL structure is going to be courses, we've got course, so we use the dollar sign, so so we get the template literals here. We've got course dot ID and then we've got lessons and then maybe lesson dot ID or it we could use slugs on the lessons as well if we wanted to. Let me fix this. We've got slugdot ID. Okay. Unterminated template literal. Okay. Alright. So now we've got a URL that should take us to this specific lesson. I'm gonna go back into our Directus instance, which keeps logging me out, probably a cookie or some type of setup issue on my end when I configured Docker, but we'll work around it for now. Let's go in and give a slug for the individual lessons as well. So, just to maintain parity. We'll go into the interface, we'll make slugify checked, and now we've got a slug for the lessons. Let's go into each one of those lessons and set a slug. Project templates, managing versions. Alright. Getting started. And custom operations. Okay. So now each one of those have a slug. We can change this from ID to slug. We'll still use that ID as the the key for looping over these. But now we're starting to get something that kind of looks like, some courses. You know, maybe we wrap these in a go back. Maybe we wrap these in a card component. And a lot of times as I go along, I would definitely be refactoring these as we went along into components that made sense. So we've got the padding, p y 4. Let's do some space between each card. So we've got a little bit of space between each card. That's great. There's our different modules. I don't know why the first module is showing, below the second module. That could be the way that we've got our sorting on the course. So if we go in, we don't have sorting enabled. So what I can do is go into our Courses section, our Courses Data Model, we'll go to the relationship field, or the relationship tab, and for our sort field we're just going to add that sort property, or that sort field on that. And that should take care of the sorting for us. So now we've got our first module, that's great. We've got our second module, starting to look like something. Let's just take one moment and make a, like a nice little header for this. We've got image course dot image, object cover, And we'll give this a little bit of padding as well. MT 8. Maybe we want to round the corners. Again, very crude but, definitely paints the picture. So we've got about 18 minutes left. Let's dive into the actual lessons, Right? So a pretty common set up when we go to the lessons, and right now we're getting a Page Error Not Found, is to have a list of the other lessons over here on the left hand side, and then on the right hand side we've got the actual course content. So let's figure out what that would actually look like inside here. So one of the nice things about Nuxt is like the nested routes. So we go to their documentation, we go to, it's like child routes. Child route keys, nested routes. So I can set up, like parent and child collections, or pages, within these nested routes so that, it's a nice way to do routing, where I can have a child page show up in the parent view. So let's go in and we've got our slug here. So this is our actual course. Within the course, maybe we want to show let's do a slug. I don't know if this will be it or not, slug dot view, So we'll create a new folder for lessons and then we're going to create a dynamic route for the lesson. So we'll call that lesson dot view v comp ts. We're going to use the route, so route equals use route. And here I'm just gonna log the params to make sure we're on the correct spot. Alright. So we can't find the slug dot view. That's great. Let's add a component for that. Courses, 100 apps, lessons, managing versions, slug. Let's just say this is the course parent. What does that get us? Alright. So we've got managing versions. We don't see the course parent part of it here. Okay. So let's call this lessons. And now all we should see is course parents. But if I go in, and I can't remember the exact syntax, so let's go back to nuxt.com. We'll do the child routes, nested routes. It is what? NUXT page. So we just throw this NUXT page in here And now we see this where we have Course Parent, and then we have our NUCs page which gets rendered inside that. So why are we doing it this way? Because we can actually fetch the lessons within this parent component and then, that will not re render, but then we can navigate within these individual lessons and render those over here on the right. So if we do something like this where, let's flesh this out, we've got a, let's do a div. Alright. We could do, like, an aside maybe. This is the list of lessons. Then we've got another div. We'll render that, and we'll just flex these. Flex. We'll add, let's just maybe give this a fixed width of like 56 or 64 wide. Alright. And just to illustrate this, we'll give it like a background, a bggray100. So we've got our list of lessons there. You Gonna add some padding for that, p 4. Then our next page, we probably got a new container. I need to actually look up order the the settings for that. Let's take a look at it. You container, constrain the width of your content. Max width 7 x l. Okay. Yeah. Works fine for me. Alright. And this could be probably height full. We want it to be the full height or height, screen. That'll get us. Cool. Alright. So now we can go in and render the list of lessons here. So we will grab our lessons. So let's do const lessons equals await. You got GitHub Copilot for the win. We're gonna read the items from the lessons. The filter is gonna be where the course dot slug equals route dot params.slug. So we're gonna pick up the we can pick up the course from the course slug. And let's actually see what we get here. I refresh the page. We're breaking some stuff. Course, slug, route. Oh, we actually have to grab the route, don't we? Forbidden. So I can't get this information. Wonder why that is. Let's take a look. We'll just erase that filter, see what data we're getting back, in this list of lessons. So I'm gonna quickly wrap that and, just output the lessons. Okay. So there's our individual lessons. Looks like those are actually coming through. Okay. But if we go to course dot slug is equal to, Let's just take a look at our network request. Alright? This is being fetched on the server side. You don't have a permission to access this, which makes me think something is wrong. Alright. So let's just look through this really quickly to see if we can find our module. Duh. That's because our modules are not, our lessons are not actually linked to our course, they are with our modules. So the the courses belong or the lessons belong to the individual modules. So, fun debugging issue here, but let's just go into our original index where we get the course, we get our lessons, and we can actually copy this code. And within the lesson route, her lessons where's our parent component? We could do this. Right. And here we could probably adjust it where the slug or the course, slug. So we get all the modules instead of the lessons, which is a little bit counterintuitive. But we still wanna display all those modules within that bar anyway. Alright. So we get our modules. We'll change this to modules. And we'll make sure that we grab the lessons as well. Let's just clean this up and refresh. Something is breaking. We don't get our modules. Route is not defined. Again, silly me, silly rabbit, you have to use the route. Alright. So now we can loop through these modules. And what does GitHub Copilot got for us? Alright. So we've got our different modules here, how to manage different versions, how operations can be completed. Okay, great. Nuxt link courses, route planarams. Slug. And now if I am navigating between these over here on the right you can see that this is actually changing. You can see that we're logging the slug for that specific lesson. So if we go into the lesson, we've got route params. Lesson. Now within this specific component we will call the lesson that we want. But what we'll do is make sure that we update this a little bit. And we're gonna use the lesson equals lessons if we just log lesson over here. Alright. Boom. We can see that. If I go back, I wanted to make sure this doesn't shrink. Select shrink. 0. Alright. And now on our individual lesson, let's actually set this up. So we got the lesson name. We got the lesson content. It's great. And then we will have a where it'll be an iframe? For the YouTube Embed. And I actually think do I have do I have Do I have a video string for this? I don't. Generate lesson and video. What is the YouTube embed? Alright. So we're getting the URL from YouTube. So if I log in to the lessons here, how do we transform this? Closing in on what? We're gonna cut this one down to the wire. Right? We've got 6 minutes and 55 seconds here. How do we actually render out this lesson content? We are going to do, let's rely on chat GPT or get up copilot. Right? Function to generate YouTube embed URL. Okay. So now we're gonna get youtubeurllesson.video. Let's see if this actually works. Cannot read properties of undefined split. So we'll do if, oh, I know what it is, we've used video URL instead of video. A lot of these issues can be avoided by using TypeScript, but lesson let's do a v f. Cannot read properties index of undefined. YouTube video ID. So we need to take a look at Directus. It's what's fun about doing these things live. You never know exactly what's gonna go wrong. And when you're under the gun, the pressure is mounting. Don't necessarily know. Can't even get the password right now. Hallelujah. There we go. Alright. So in this case, we want to, accounting for YouTube URL variations. Let's see what this thing comes up with. And you to that be Will this actually work? Hey. There we go. Alright. We probably want to do, like, an aspect ratio, aspect video. Okay. And so now we have our content. We could do a v if, so there is no lesson content here. And boom. So now if I click through these, we should be loading our different courses. So at this point we are at 3 minutes and 44 seconds. So we did not get as far along as I thought we would get on the front end of this. But, going in and extending this further, one of the things that I would do next is probably taking a look at building this out and adding enrollments to this. So we could set up a like authentication for this so that, let's just say on the access control side of it, instead of allowing all the courses to be seen we could certainly let people view all the courses, see the instructors. But as far as the actual lessons, maybe we didn't want to give them access to that until they were enrolled. But for anybody that was enrolled, they could see all of those courses. So now if I refresh the page, we're gonna see like an error that says, hey, this is forbidden because we cannot see this actual course. But if I were to go into my authentication page, let's just say auth\u002Flogin and use my Directus URL or my Directus username and password. Go back a couple pages. Where are you? Courses. Alright. We go into that and we can actually see those. And that's because this user is that user is now authenticated as the administrator and they have full rights. But we could set up a public and then a user specific role where we've got courses, we've got instructors, you can see all of that, where we want to see only the, like, courses that that person has access to. So, let's just go with no access, like modules, that's fine. But as far as the lessons, you would have to set up like a a role here or, like a custom permission for this specific user. Taking that a step further, you know, we could get into like, making it work with Stripe, setting up some of the different, payment options. Like if we took a look at Udemy, there are, you know, trial options. You could pay individually for courses. You could set up subscriptions for this. But, clearly we we built the back end of our LMS, but, really a little disappointed on how far we made it through the front end, considering I've actually built several of these systems myself. But we've definitely learned a lot. So if we take a look at our list of functionality, how do we do? We got to view a list of courses, we got to the individual courses, we got the individual lessons, We did have the authentication and login, but as far as tracking progress, and enrolling for a course, I give myself, what, 4 out of 6 or 4 out of 7. Not a stellar effort here. So that is this lesson of 100 Apps, 100 Hours. I hope it's been a great example of how quickly you can build functionality like an LMS inside direct us and with front end tools. But there is a reason these other products exist like Podia, Kajabe, Udemy, Teachable, all of those. Rome wasn't built in 1 hour. So hope to catch you on the next episode. That's all I've got for you on this one.",[5502],"0ea8d80f-1177-49fd-b106-ff30b3efb903",[],{"year":2380,"number":177,"id":4162,"episodes":5505,"show":5506},[4164,4165,4166,4167,4168,4169,4170,4171,4172,4173,4137],{"title":322,"slug":323},{"id":5487,"slug":5508,"vimeo_id":5509,"description":5510,"tile":5511,"length":3992,"resources":12,"people":12,"episode_number":13,"published":5422,"title":5512,"video_transcript_html":12,"video_transcript_text":12,"content":12,"seo":12,"status":19,"episode_people":5513,"recommendations":5514,"season":5515},"downtown-nashville","894198753","Filmed by John, a beautiful day in Nashville. Enjoy the sun rise and set with some chill beats.","ad8fcba8-aa80-4439-a056-901212cec875","Downtown Nashville",[],[],{"year":2380,"number":128,"id":5485,"episodes":5516,"show":5517},[5487,5476],{"title":5489,"slug":5490},{"id":2436,"slug":5519,"vimeo_id":5520,"description":5521,"tile":5522,"length":5523,"resources":12,"people":5524,"episode_number":13,"published":5422,"title":5533,"video_transcript_html":5534,"video_transcript_text":5535,"content":12,"seo":12,"status":19,"episode_people":5536,"recommendations":5540,"season":5541},"creativity-in-code","893678493","A discussion on the evolution and future of online experiences, including practical advice on innovation, technology trends, and the role of AI and AR\u002FVR.","dd93d010-869d-4513-a9a8-76f2daba48dd",68,[5525,5527,5530],{"name":4012,"url":5526},"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fcontactbenhaynes\u002F",{"name":5528,"url":5529},"Stewart Smith","https:\u002F\u002Fstewartsmith.io\u002F",{"name":5531,"url":5532},"David Somers","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fjalada\u002F","Creativity in Code","\u003Cp>Speaker 0: Awesome. Well, super excited, to be diving into this. I think it's gonna be a really exciting conversation. Obviously, this is I've built my entire life around, like, the idea that we're talking about. I think we all kinda have, so it'll be hopefully some some interesting stuff to cover.\u003C\u002Fp>\u003Cp>Why don't we kick it off with some intros so everybody kinda knows who's who's chatting, and then we can get into the theme. Stu, do you want to kick it off with with an intro?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Sure. Let's see. What's a good, succinct way to put this? I have both a graphic design background and a coding background, so design and engineering together, which is awesome.\u003C\u002Fp>\u003Cp>So I speak both both vocabularies. It didn't use to be awesome, I feel like, then when we were young. It made us kind of an outcast in both worlds. Maybe I'm speaking too much for myself. So I've worked at Google and Amazon and various other places, and I'm currently at Unity Technologies.\u003C\u002Fp>\u003Cp>Speaker 0: For the for the second time around. Right?\u003C\u002Fp>\u003Cp>Speaker 1: For the second time around. Yeah. I was pretty torn about leaving the first time around. I left to pursue something pretty interesting, and very happy to be back in the family with a lot of folks that I really enjoy solving tough problems with. And I should probably mention that spans like creative technologist stuff, augmented reality, virtual reality, a little bit of quantum computing thrown in there.\u003C\u002Fp>\u003Cp>But, yeah, it bounced around between product design, marketing, research work. It's all fun.\u003C\u002Fp>\u003Cp>Speaker 0: Definitely. Yeah. I've probably known for 20 20 years, over 20 years maybe. Going all the way back to the beginning, you know, talking about, like, getting trained, whatever, learning, you know, design and development. You definitely kinda get this sense of, like, not jack of old trades master, jack of 2, master of, like, hopefully both, like, freedom and technology.\u003C\u002Fp>\u003Cp>They're just no different. But, yeah, all the way back in the the Yukon days. David, how about how about you? Maybe back from where you are now.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Great to be here. Thanks for having me. So I'm a head of engineering at a digital product agency, here in the UK called Pixielabs. We're London based.\u003C\u002Fp>\u003Cp>We design and build digital products, mostly web based or mobile based. We've been going for 10 years now. We had our 10 year party actually a couple of months ago, which was a lot of fun. And I've been with PixieLab since its inception, helping to initially writing all the code and increasingly my team not letting me write the code. And prior to that, I was, helping to kinda lead developments, at an innovation lab at News UK, or at the time it was News International, which is the publisher of The Times of London and The Sun, a big newspaper publisher here.\u003C\u002Fp>\u003Cp>And prior to that, my my background is very, traditional. I did computer science. I was I was a very traditional nerd and always have been, always will be. But, yeah, worked on a whole bunch of stuff under under the kind of pixielabs umbrella, everything from large companies, you know, people like Twitter and HomeServe, but also start ups and and smaller brands and everything, yeah, from, like, big platforms for Fitzy 100 down to cat food subscription websites. And we once made a thinking about, sort of like things that are the most creative, we once made a typewriter type by itself when you tweeted at it, for an art installation with in partnership with Twitter, which I get so much mileage out of in conversation forevermore.\u003C\u002Fp>\u003Cp>Like, that's like the one. It's like the one project where you're like, I can talk about this a lot forever.\u003C\u002Fp>\u003Cp>Speaker 0: Was that at a museum perchance? Like, was that a museum over on your\u003C\u002Fp>\u003Cp>Speaker 2: side? Yeah. It was like it was initially a there was like an art exhibition. They ran Twitter ran like a competition for ideas that were powered by tweets in some way, like any idea. Right?\u003C\u002Fp>\u003Cp>And people submitted everything from, like, pigeons flying around measuring and tweeting the air quality, all the way to the typewriter that was, like, typing out a a novel by through Twitter. And we had to, like, come up with some representation of all of these ideas that people had submitted or, like, 6 of the winning ideas. And then I think the typewriter ended up at the Globe Theatre as well, where it was typing the complete works of Shakespeare, but it would wait for the someone to someone in the world to tweet the next word that it wanted to type. It took months and I think really annoyed all of the staff like, in the corner, like, clacking away. This was a really loud old school typewriter.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So just randomly for someone to, like, say the word betwixt, and then it could keep on Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: Exactly. Exactly. And, you know, like it wasn't the I will admit it did have a time limit before it would just type the word and move on. Otherwise, I don't think it would have ever finished. I mean, you can\u003C\u002Fp>\u003Cp>Speaker 0: just sit there and, like, be the one to keep it moving along. But Right.\u003C\u002Fp>\u003Cp>Speaker 2: And that's the thing. You would stand in front of it. Right? And you could tweet the next word, and it would, like, tweet back at you saying that you'd you'd powered it. It\u003C\u002Fp>\u003Cp>Speaker 1: was wild. The idea of an impatient typewriter.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. It was. It was impatient. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: You should just start tweeting out, like, prompts. Like, okay. Listen. We gotta get this thing rolling.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Please someone tweet this word.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. I did. So at my agency, which is called Ranger, we did something similar. It was with Google, and Teller, I believe, was, like, the creative side, that was pushing it.\u003C\u002Fp>\u003Cp>It was a museum, and they had oh, I don't even remember the one of those things you look through, the, periscopes, telescopes. Mhmm. But it was, like, all digital, and they had all sorts of, like, art museum installation stuff that was, connected to like, real world stuff that was connected to technology. So similar to what we're talking about today, you know, bridging the gap between the real world and digital, I think we're kinda getting more into the the creative and code or design and development depending on which alliteration you choose. I love it.\u003C\u002Fp>\u003Cp>Well, thank you both for for being here. This is, super exciting. So and then just kinda tap it off. So my I'm Ben Haines. I started, an agency maybe 15, 20 years ago, called Ranger out of Brooklyn, which is where Stu is right now, I think.\u003C\u002Fp>\u003Cp>So David's in the UK. Stu, you're in in Brooklyn. So I started in Brooklyn, really, with exactly what we're talking about. Like, I had seen a lot of development firms, and I had seen a lot of design agencies, and they were trying to communicate. And you just get a lot of the, the design hands off.\u003C\u002Fp>\u003Cp>It's like pixel perfect, you know, mock, or here's what we're here's what we're looking for. And, you know, the the development's like, oh, we built it. Here you go. But it's not really following the the design principles. They're not giving feedback on what's possible.\u003C\u002Fp>\u003Cp>Like, designers are really great at making things that look great, but not functional. So I tried to bridge the gap hiring designers and developers to build for, you know, Google, Snapchat, AT and T, Prada, US government, etcetera. But that's where this software Directus kinda stemmed from was, wow. Okay. So we're building these really innovative projects for those sort of companies, and, you know, smaller companies, culture institutions, etcetera.\u003C\u002Fp>\u003Cp>But we're building the same thing over and over and over again. How do you take maybe one of the more I mean, quantum computing, Stu, so you you got me there. But, like, the database is pretty technical. How do you make the database, you know, in the work of a a DBA, a database administrator, you know, how can you democratize that so the business user can do it and you you do it through design? You know, really strong intuitive UX and UI, giving you know, I I was inspired by PHP MyAdmin, which I think is maybe lacking design.\u003C\u002Fp>\u003Cp>I you know, Not not the best not the best system I've ever used. So I was like, oh, just take that and make it friendly and intuitive and safe. So that's where Directus, you know, our our software sort of stemmed from. But so today, we're we're kinda really covering the basis of, where all 3 of us live, which is are we gonna say design and development or creativity and code? I like I like both, I guess.\u003C\u002Fp>\u003Cp>Speaker 1: I think I'm leaning towards creativity and code. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I would say design development, but creativity and code, I like it's not just design, I guess. So, yeah, that is probably better. So the intersection of those two things or the overlap even, I think we've all sort of built the whatever we've been doing for the last 10, 20 years has been has been right there. So, without further ado, why don't we jump in?\u003C\u002Fp>\u003Cp>So I guess to start off, Stew, you mentioned sort of, your history going through different large orgs, you know, AWS, Google, Yahoo, relatively structured companies or very large companies. I'm curious when we're talking about creativity and code going at the c's, how do you, like, encourage a culture of innovation at a company that's a little bit more rigid or, a little bit more red tape?\u003C\u002Fp>\u003Cp>Speaker 1: Wow. I guess, the answer varies quite a bit depending on which context. So for instance, Google is a huge company. Right? And there's a lot of rigid structure in place necessarily.\u003C\u002Fp>\u003Cp>But what was really cool about Creative Lab and also, the data arts team is they were little corners of the company that, folks like Andy Barrett, who runs Creative Lab, he had won the confidence of his fellow executives to, say, we're gonna go off and do something crazy over here. Here's why it's justified. Here are the business reasons and marketing reasons and whatever, but we're going to go be a little nuts and you're going to sit back and chill. And so I'm so glad that like in that context, there's a very strong leader standing between you and a lot of that process, and it allowed Creative Lab to be a little crazy. And I I haven't been there in, quite some time.\u003C\u002Fp>\u003Cp>I feel like there's a little bit of a trajectory where it started to become a little more formalized, probably also necessarily. But I also remember the more Wild West days, literally, with a sword and champagne bottles and stuff, just impromptu on like a Tuesday evening because everyone's working late and major projects are due. So there are a lot of good it was a place where there could be some irreverence and the space and time for some creativity. Of course, things have to get done and they have to get shipped and delivered, so it wasn't without pressure for sure. Whereas I would say Amazon was a very different case and ultimately, I don't think AWS is just so not geared towards a creative process.\u003C\u002Fp>\u003Cp>So I think I lost the battle a bit there within AWS, which is sad. Yes. There's some great people there. But,\u003C\u002Fp>\u003Cp>Speaker 0: I can imagine, like, just in the in the 2 you just described, you know, one has creative in the in the title, you know, in the team name and servicing. So, like, they kinda have maybe different focuses, maybe. But\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. For sure.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And that's that's pretty amazing. And I I feel also feel like, you know, the more emergent like, the newer the idea is for a team or for a company, regardless of how big it is, it's easier to be in that, like, divergent mindset and just be like, oh, we're gonna break things. We're gonna innovate. You know, that's one of the same.\u003C\u002Fp>\u003Cp>We're gonna be, like, super creative and just kind of push the envelope. And then over time, it's like, oh, we have real world things. We have to answer the investors, or we have to kind of rein it in a little bit and switch to convergent mode. Both of those are pretty established companies, but creative I don't remember when Creative Lab was formed, you versus\u003C\u002Fp>\u003Cp>Speaker 1: I don't know. I wanna say maybe there were seeds of it in 2006 or something, which is well before I joined. That would have been I\u003C\u002Fp>\u003Cp>Speaker 0: mean, it held on to that idea.\u003C\u002Fp>\u003Cp>Speaker 1: Very fuzzy about the yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I mean, held on to the the ethos of, like, create I mean, yeah, that's the purpose of the team. Interesting. And would you say the same thing for I think we we were talking recently. I think maybe there was some overlap at Yahoo between when you were there and when I was there, maybe not.\u003C\u002Fp>\u003Cp>But would you say the same thing for for Yahoo? Exclamation.\u003C\u002Fp>\u003Cp>Speaker 1: Well, I think, I, speaking of returning to places, so I had been at Lab, went to Yahoo for a few years, then went back to Creative Lab and Dana Hartstein, which is a whole other story. So I think I entered Yahoo with a different mindset. The context was that Marissa Meyer had left Google, and she had taken over as CEO, and it was still the early days of that, so there was a lot of this optimism that Yahoo had built this giant, I'm gonna use metaphors here, had built this giant ship, and it was a brilliant ship. It was a big battleship out on the sea, but over time, its facilities have been squandered and things were not going so well. So Marissa climbs aboard and says, I'm the captain now, and you could go do a startup or you could go do something else and you'd have to build your entire ship, and that is a difficult lift.\u003C\u002Fp>\u003Cp>Or you could jump on this ghost ship and take all the good parts and rebuild them, and that seemed to be the mentality. We're all here. It's a new era. We already have all of these products and services and things that generate revenue and just let's use it. Let's build anew, from this kit of parts.\u003C\u002Fp>\u003Cp>And, so it was a pretty exciting time. And there, maybe I'm speaking too much for myself, but it felt like there's a bit of this sort of pirate mentality of like, here we are. Let's blow things up and let's reinvent. And because of that, I started out in marketing, had some success there. My biggest success there already incorporated cutting edge research, early days of machine learning, Marketing, obviously, design is what I was bringing to the table.\u003C\u002Fp>\u003Cp>So it was all like engineering, marketing, and design altogether. And because of that, I was in marketing, then I was in product, and then I was in research. So I literally, like, bounced around the company kind of doing similar stuff at every turn but with a different hierarchy and sort of a different set of rules being bent in in a new direction.\u003C\u002Fp>\u003Cp>Speaker 0: I like that. I like that. So anybody on my team knows I'm a big fan of metaphors. And I I thought for sure, when you talk about breaking the ship apart, you're gonna go down the, like, the Theseus, paradox. Oh, right.\u003C\u002Fp>\u003Cp>Yeah. And I was like, oh, no. We're going pirates. We're doing pirates. I like it.\u003C\u002Fp>\u003Cp>That's that's amazing. Yeah. I think that that kinda makes I I I have always sort of worked at, my like, an agency, like self self employed with the exception of maybe a a year, year and a half at we were both, AOL, Verizon, Yahoo, which may or may not have had overlap with you. But, yeah, it was for me, they there was, like, creative freedom, but it was definitely, like, you can be creative until like, toward the end of the process, and then it's like, I hope you had fun because we're gonna shut that down, and we're gonna work on something, you know, something else that maybe is a little bit less fun. And which is almost more stressful because you get to work like, you get to take a project to 90%, and then they kinda scrap it.\u003C\u002Fp>\u003Cp>I just wasn't used to that way of working. It's like, oh, well, I guess that's that's how it's gonna be. So I I wanted a little bit more control and kinda stepped away. I was like, alright. I just need to be able to sail my own pirate ship, because even pirates, sometimes they're they're not hiring the way you wanna pirate.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. If you if you wanna lament some of those, Yahoo things, I got it to a 100%. We had a a very successful marketing campaign that resulted in a patent, which is weird for marketing, and some other interesting developments. And I thought, wow. Okay.\u003C\u002Fp>\u003Cp>Based on the back of this, now it's time to start my own team. And, with the right group of folks, we could bang out a few of these every quarter, and wouldn't that be a good thing? And, it was tentatively called the Marketing Innovation Group, and I was so excited to take this remit that I thought I had earned and like run full speed with it, and that turned out not to be the case. For some reason, it was like, well, that was successful, but the analysis was for none of the reasons you listed. And after that, they never had a project like that again.\u003C\u002Fp>\u003Cp>It was like, all right. So I moved to product for a while with some other very crazy folks.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. You get to meet different meet different teams. I was I was moving toward, like, the VR teams. We probably would have intersected at a certain point.\u003C\u002Fp>\u003Cp>Speaker 1: Oh, goodness. That would have been a lot of fun.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Oh, yeah. Absolutely. But then I was where I was like, how far can I get down this road before whatever cool thing, you know, we dream up gets scrapped? So I I kinda stepped stepped away, but, I'm sorry.\u003C\u002Fp>\u003Cp>Well, so and, David, obviously so I shared some time, you know, with with the big companies like like Stu. You've been over a decade at your agency at Pixie Labs. I ran mine for a similar amount of time, and what I found or just in general, you know, agencies really have to stay at the bleeding edge of, you know, what's going on in terms of the tech stack, in terms of just everything from the the small, like, what's going on within the front end, you know, CSS and frameworks all the way up through larger trends, and things that you can pull into your process. Because you're not always like, the spec that you're giving from a given from a a customer or a client or whatever it is isn't. It's like the the goal, but how you get there and how you really push it is on the agency.\u003C\u002Fp>\u003Cp>And I'm curious. How are you making sure that your team stays on top of, you know, the creative and tech, staying ahead of the game and just leading, you know, leading that side of things. Yeah. Any any\u003C\u002Fp>\u003Cp>Speaker 2: specific I mean, like, as an agency, right, you're optimizing for being billable and kinda remaining ahead of the curve and doing experimentation and things like that can really kinda run counsel to that. It's funny, like, you actually mentioned, Stu, about sort of, like, you know, having a corner of the company that had won the the confidence of execs and and things like that. Like, you almost kinda have to do a similar thing with time, right, and enshrine a a a portion of time where you, you know, for us at Pixielabs, it's like a day a month where we've sort of set aside as non client time, which is not a lot of time to really get a lot done. And, naturally, I'd much rather it would it was a bit more. But, like, having some time that is a little bit enshrined that you're like, this is the day when everyone feels okay with putting down the client work that they're on, you know, and which can be tough with deadlines and things like that and and try and, you know, use that time for a bit of experimentation.\u003C\u002Fp>\u003Cp>But, also, I think, like, there are some areas you can be flexible in an agency on projects. So I kinda try and look for opportunities project by project. Right? And sometimes it can be a bit of an exercise of bargaining with a client as well. So if you've got something where the budget's a little bit low, or maybe it's a for good thing and you're being asked to kind of, like, help, you know, help out something that is more charitable or something like that, maybe maybe that client might be a bit more receptive to you actually doing a bit of experiment you know, experimenting on that project.\u003C\u002Fp>\u003Cp>Right? Trying something that's a bit unfamiliar and just being open with that and saying like, hey. Maybe we're gonna try something a bit new here or work with something that's a bit more unfamiliar to us or, like, we have this idea that we don't know if it's gonna work, but we wanna try it and, you know, depending on the parameters of the project. I think that that there's an opportunity then. Also having, like, good relationships with clients, especially long standing relationships can help with that, especially if you're you know, one of the things that you do in an agency is, like, how can we get more out of a client?\u003C\u002Fp>\u003Cp>Right? Like, how could an existing client, how can we unlock more more spend, more investment in us? And having some ideas, having some things that are experiments, and I'm kinda trying to sell that as an as a kind of joint experiment. Right? And being like, well, let's experiment with this thing together that we see there's a potential for your business, and, like, we can help that make that happen, I think is also is also a way to do it.\u003C\u002Fp>\u003Cp>And I think thinking about the engineering team as well, just in general, like, you do have to make sure that the experiments kinda in some way, I think, align with, like, our goals and vision. Right? Like, if we're, like, we're primarily doing stuff sort of web and mobile. And so, you know, I don't know if I would be comfortable with it with one of my team, like, going wild and doing some experimentation with something hardware based or electronics based or or something like that. Like like, maybe, but I think that that feels a little bit too off piste, and a little bit of a challenge to justify.\u003C\u002Fp>\u003Cp>And instead, it's like, okay. Like, if there's something that's slightly out of our lane, but not massively out of our lane necessarily, unless that was something that we were like, as a business, we also wanted to move into. Right? There are definitely things, you know, that we're seeing on the horizon, like, that you you're sort of alluding to, that are like things that we're like, we do wanna try and move into that area. So those opportunities then maybe are a little bit more left field that then the team can kind of like explore a bit more as well.\u003C\u002Fp>\u003Cp>But, like, having a little bit of alignment, I think, is useful, especially in a small team and especially where you don't have a lot of time to do it. You don't wanna go too off piste. What do you think\u003C\u002Fp>\u003Cp>Speaker 0: like, in terms of some of those things that might be on that horizon? I'm just curious.\u003C\u002Fp>\u003Cp>Speaker 2: Trends. Yeah. Like, one of the one of the things that I'm increasingly thinking about is about Apple moving into the AR and VR space with VisionPRO and thinking about how big of a deal that is for that kind of side of things compared to even I remember when I was at news and we were doing stuff to do with augmented reality and scanning QR codes and putting 3 d models on bus on the sides of bus stops and things like bus shelters and things like that. And, like, nothing has really moved along for a very long time in my opinion. Like, it's sort of you know, there's not sort of been like a huge like, it was novel then.\u003C\u002Fp>\u003Cp>It feels a bit novel now still. But I wonder about, I pay a lot of attention to the fact that Apple are really moving into that space. And so that is something that as a business, we're like, that feels quite out of our lane compared to web and mobile, like, even though it is sort of adjacent. And so that is something that strategically I'm like, there's some there's some thinking to do here.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Oh, for sure. Yeah. I like it. I like that.\u003C\u002Fp>\u003Cp>And so it's interesting. So you're saying almost like you're you've got this I think, like, Google also had a lot of companies have had this, like, idea of, like, white space where 20% of your time or whatever they can afford, is given to their their teams to say, like, hey. Just build. Like, come up with the cool stuff. It doesn't it should be generally on task, but, you know, more proof of concept.\u003C\u002Fp>\u003Cp>But you kinda scope that to the projects, and you kinda budget it in and said, hopefully, if if there a client is looking for something a little bit more innovative, it's like, well, we're we're gonna kinda go off and do some like, a little r and d sprint as part of the budgeted time, or how did you structure that?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. That's yeah. That's we're kinda talking about 2 different 2 different approaches there. Right? On on one hand, you've got the the, like, time that is for us specifically that we're kind of enshrining as non client time.\u003C\u002Fp>\u003Cp>But then on client projects, it's really about, trying sort of keeping keeping things in the back of your mind of, like, things that you'd like to do or things that feel interesting or worth exploring and trying to find opportunities to to kind of worm them into a project or trying to, like, sell them into to an idea that a client has or a problem that a client has and being like, oh, like, we actually think that that this, like, new thing that we've that we've seen that we wanna do a bit of experimenting with, you know, it's you've got a you know, finding something small that we can that we can, like, experiment with something on. And I'm thinking specifically there about new tools or new frameworks or things like that rather than anything too major, just sort of like really about kind of staying at the edge of, you know, the tools using the right tools and using the best tools, to to get the job done. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I like I like that. So I guess that kind of is is pretty interesting. That kinda segues in. I would almost throw it back to to either of you, I guess, at this point.\u003C\u002Fp>\u003Cp>But, on that the tech side, you know, in terms of the stack, are there specific tools or platforms, that have been, like, pretty pretty crucial in building out the different projects that, either of your you know, you or your teams have been have been pushing? Specifically through the lens of, like, this intersection of creative and technology. Obviously, there's plenty to to go through in the tech stack, but I think this is a more nuanced space. Well, of course, Unity. I\u003C\u002Fp>\u003Cp>Speaker 1: mean, I I do agree with Unity. Just, you know yeah. I'll leave it there. I won't pitch for my own company too much or the company I'm a part of. But so other than that, I would say, 3.\u003C\u002Fp>\u003Cp>Js. My goodness, Ricardo and team over the years, that was really the real entryway for me because I started out as a web guy. I started to mess around with 3 d, didn't understand it so well. And then after banging my head against 3. Js and relearning matrix multiplication and all that good stuff, it's it's paid dividends as, the web has become more powerful, WebGL and now WebGPU, and WebAssembly, you can do a whole lot of stuff.\u003C\u002Fp>\u003Cp>David, you had mentioned the there being a gulf between AR, VR, and, and the web and mobile. But I found with things like like the quest, I could do it so much through the web. It was really exciting. And that was one of the things that I worked on at Amazon was, their Sumerian product, which was pretty interesting. To me, the most powerful thing, it was all in browser, sort of like Unity lite in browser.\u003C\u002Fp>\u003Cp>The most powerful thing was the blue publish button on the top. You can make any experiment you want. And then the moment that you were ready to share it with the world, you hit that button, and it takes a few seconds, maybe like 10 seconds. And in that 10 seconds, it is taking your project and replicating it on all of Amazon's servers with all of its 9.9 whatever uptime, and you can immediately share that URL out to anyone. And I thought that was so\u003C\u002Fp>\u003Cp>Speaker 0: cool. 9.9 uptime is not that great, Stu.\u003C\u002Fp>\u003Cp>Speaker 1: Well, 99 whatever. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: They do the they do the best replacement. Yeah. Yeah. But I mean, 3 JS, I mean, it's crazy. I mean, I guess similar to Directus, you know, what I've been working on, it's been around for a long time, but its relevance kind of can still be in the future.\u003C\u002Fp>\u003Cp>Like, it can become more relevant over time depending on I mean, 3 JS has just been, like, just such an amazing thing to not just follow along, but just to use, like, just browsing through the demo pages and all that and just being like, oh, I could kind of fork this idea and just run with it. It's such an amazing platform. I think there's all and then obviously being it's open source. I think it's MIT, obviously. Oh, yeah.\u003C\u002Fp>\u003Cp>Yeah. Yeah. Like, I think that's a big any open source tooling, is is a big win in my book.\u003C\u002Fp>\u003Cp>Speaker 1: I've contributed tiny like, a tiny, tiny bit.\u003C\u002Fp>\u003Cp>Speaker 0: For 3. Js? Yeah. Oh, wow.\u003C\u002Fp>\u003Cp>Speaker 1: It's it's not really worth mentioning, because it's so small in the scheme of things. There are real contributors.\u003C\u002Fp>\u003Cp>Speaker 2: You're doing it anyway.\u003C\u002Fp>\u003Cp>Speaker 1: It's like, okay. Yes. I gave back a tiny bit to this tool that has helped me.\u003C\u002Fp>\u003Cp>Speaker 0: We'll we'll call it a micro maintainer.\u003C\u002Fp>\u003Cp>Speaker 1: There we there we go. Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: I\u003C\u002Fp>\u003Cp>Speaker 0: like it. How about how about you, David?\u003C\u002Fp>\u003Cp>Speaker 2: I think, like yeah. It's interesting. I I had some notes about thinking about tooling of things like 3 JS and and stuff in kind of Webexa land. Like, I think for for things that are around, like creative and bringing creative ideas to life, I try and think of, like, what are the tools that are like one person tools, right, where you can go from nothing to done. Like, you were just describing right where you're talking about, like, building, you know, working on an experiment and then publishing it, right.\u003C\u002Fp>\u003Cp>And you've got all of that tooling in place, that means that you as one person can go from idea to execution to deployment and, like, live and and actually in in the real world. Right? Like, it's boring, but we use, Ruby on Rails, right, as a as a web framework for for most of the stuff that we do, all of the web apps that we're doing. And that has the same kind of idea. Like, in fact, the the founder, David Heinemann Hansen, was saying this at the conference the other week of, like, it's like a one person framework.\u003C\u002Fp>\u003Cp>Right? It's like one person can do the front end, the back end, the deployment, the maintenance. Like, the tooling is designed to be used by one person and actually make, like, make a lift as one person rather than having something that is inherently complex or has multiple moving parts or you need to add more moving parts to it to bring it to life. Right? So that if you're sort of building something yourself from scratch in, I don't know, some some JavaScript framework where like, some back end JavaScript framework where you've gotta also work out the hosting and, you know, you've gotta decide, is it is it is it microservices based?\u003C\u002Fp>\u003Cp>Is it am I gonna put it in a cluster somewhere? Am I gonna you know, how do I how do I get this live? What do I need to think about? Like, those things start to turn into, like, things that you naturally split up into multiple people or you need, you need a person just keeping an eye on it every day and that kind of thing. And and so I really like, like, that's the kind of thing that I that I think about, of, like, what are what are the tools that are, you know, that are that are good for, like, one person that that cover everything from from from start to\u003C\u002Fp>\u003Cp>Speaker 1: finish. Well, I feel like here's where we could also segue into direct us, which is\u003C\u002Fp>\u003Cp>Speaker 2: I did have that I did have that written down that, like, you know, directors is kind of a bit of a one person tool, right, in the sense that it there we go. In the sense that it wait. Where can I get one of them? I haven't I don't have one of those on my desk. What is it?\u003C\u002Fp>\u003Cp>Hang on.\u003C\u002Fp>\u003Cp>Speaker 0: I always have, like, a 1,000 of them printed, like, today, so we'll we'll touch base. Great.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Like, in the same way. Right? In in in the you know, it's like database. You've got APIs.\u003C\u002Fp>\u003Cp>Like, it's giving you all of the it's giving you the front end layer to work in. It's giving you the API layer to work in. There's a director's cloud hosted environment. Right? Again, like it is.\u003C\u002Fp>\u003Cp>It's it does feel like that kind of thing of, like, tool tool of tool of 1 team you know, 1 person teams kind of thing. Yeah. And I think, like and then, otherwise, I was thinking a little bit about, like, what tools do I use just to, like on the very creative end when I'm thinking about just, like, coming up with ideas? And I have to plug I have a special place in my heart for Excalidraw. Do either of you use Excalidraw for, like, for, like, putting together little diagrams of stuff?\u003C\u002Fp>\u003Cp>Speaker 1: No. The comics\u003C\u002Fp>\u003Cp>Speaker 0: at the end of, of Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: But it's it's I don't know what it is. It's something it's so aesthetically pleasing to use. You you open up Excalidraw, and it's like a blank it's like the classic, like, infinite whiteboard, infinite canvas type tool, but, like, everything looks like you've scribbled it. And there's and it has just the right amount of shapes and colors and lines and sizes that everything you make just looks I don't know. I look over my old Excalidraw drawings, and I'm like, these are all so nice.\u003C\u002Fp>\u003Cp>They all just it just makes everything look it's like just it's like just loose enough. It gets kind of reminds me of, like, balsamic mock ups, but I always felt like balsamic mock ups, the aesthetic of that never really kind of vibed with me. I think that is too Comic Sans of of mock ups, but something about Excalidraw, I just\u003C\u002Fp>\u003Cp>Speaker 0: might Yeah. We we use that a lot just for, like, quick, like, oh, let's just go in there. I think, hilariously because I I believe, if I'm remembering the tool the tool correctly, it gives you, like, a one of those random names, whenever you join until you, like, register and say this is your actual name. Right. And the first time I logged in, I'm in with, you know, 20, 30 people, on the direct score team, and it it assigned me Tremendous Beaver, was my my written name.\u003C\u002Fp>\u003Cp>And I I think that still floats around in our Discord server. So, but, yeah, I think it's that balance. I mean, right there is a balance of, like, looseness and, like, get things done. Right. Striking that balance is really difficult.\u003C\u002Fp>\u003Cp>It seems easy, like, just with with design. Like, design becomes transparent when it's done really well. I feel like a tool like that, just makes it seem effortless. Hopefully, that's what we're trying to do with Directus as well. And you you mentioned sort of it's a good tool for 1 engineer, and I think another aspect or facet of that is when you can make that one engineer, that one creative technologist, whatever it is, seem like 10.\u003C\u002Fp>\u003Cp>You know, it's not just effective. You can do it yourself, but giving them the abilities of, like, oh, I'm just, you know, just front end. It's like, oh, this kinda allows you to be full stack and kind of build, at a full stack level. There's some marketing stuff in there somewhere. Yeah.\u003C\u002Fp>\u003Cp>You're right about that. Is there, like, a good way to assess? I mean, you have emergent tech. You've got, you know, AI. You've got all these amazing things that are kind of flowing out.\u003C\u002Fp>\u003Cp>And how can you use them, incorporate them into your workflow, be more efficient. But, also, sometimes when you're building it into the stack, especially the stack of a client, that might you're gonna build it and sort of deliver it, and it's gonna exist and maybe you have to maintain it for months here, how do you ensure that the tech that might be a little newer is actually stable enough? You know, it's, obviously, it's out of beta. It's all that. But, like, how do you know that it's the right decision?\u003C\u002Fp>\u003Cp>Or do you just say, like, well, we'll we'll learn from this, and maybe we won't use it for the next project. Maybe we'll have to rip it out, you know, 6, 12 months later. But anything in terms of that evaluation process?\u003C\u002Fp>\u003Cp>Speaker 2: I think you've kinda talked you kinda started to touch on it, really. I I think that it's all about experimentation, and it's about time boxing and making sure that you have some time to evaluate at the end or or to have some kind of retrospective either as an individual or as a team of, like, how do we feel it when using that new technology or experimenting with that new paradigm or new approach? Like, did we like it? Did it did it and, you know, upfront deciding, like, is this gonna speed us up? Is the plan that is gonna you know, it's gonna make us happier to do whatever it is we're doing?\u003C\u002Fp>\u003Cp>You know, and and really making sure to be critical of the of the the work that you've done and the process you've gone on and be like and be willing to discard it. Right? And be willing to go, no. That was shit. Let's never use that again.\u003C\u002Fp>\u003Cp>Right? It it it really, like, it really slowed us down, or it was really error prone. Or, you know, we learned things like kinds of things. I was trying to think of examples of that, and I was like, we tried to experiment building something largely using serverless functions, right, on on AWS with with Lambda functions. And we were like, let's never do that again.\u003C\u002Fp>\u003Cp>Like, that was horrendous. Like, it just slowed us down. It was like that we didn't see the benefits of the you know, that we that we were expecting to see. Let's let's let's not do that again. Like, making sure to do that, right, and have that process.\u003C\u002Fp>\u003Cp>And, yeah, I think there is that concern of, like, if you've baked something in and now, like, do you need to rip it out if if it's not great? But I have an element of pragmatism of, like, if something works, then it's prob like, you maybe don't need to worry too much about the thing that you've built if it's not working, like, especially on the engineering side as engineers. Engineers love things to be, like, perfect and and technically correct and engineeringly, like like, neat and tidy up. And I strongly push against that all the time. Like, as much as I can, I try and push against that because I think that it's too easy to fall down that hole versus just being like, well, what you know, is it really valuable for it to be perfect?\u003C\u002Fp>\u003Cp>Or, sure, it might be a little bit unmaintainable, but, you know, how long is it gonna last anyway? Like and is that thing gonna completely change in in 6 months or a year because the business has moved on, the idea has moved on, the product has moved on? Maybe you're gonna get an opportunity in 6 months to just throw it away anyway because the value the direction has shifted. So I wouldn't worry preemptively about kind of refactoring something just because you're worried that it's gonna be a bit less maintainable.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. I guess it depends if you're building whatever the tech stack is or the project at, like, the foundational level or more the facade, you know, that's gonna just like, a ephemeral, microsite or something like that, or product that's gonna be I I think it was also a piece of it is, like, even if you like it, you know, David or Stu or your teams are like, oh, this is amazing. You know, 3 JS, not a good example here, but, you know, the kind of the rug being pulled out from under you if it's like it stops being so popular, and something else kinda comes in. Like, oh, Sketch is the right meme.\u003C\u002Fp>\u003Cp>Sketch is the right tool, and then you go over to Figma. I was on I love Sketch, and then suddenly I was like, no Figma's Yeah. Absolutely where where the future is. And then there's been lots of projects where, like, an open source maintainer just disappears and then Mhmm. Love it, but it just isn't available or it's just not the right tool.\u003C\u002Fp>\u003Cp>Or you've got the the niece of the the client, kinda recommend some some tech tech, and they're like, oh, this is what we have to use. This is a requirement. I'm like, oh, well, why? That's not Mhmm. The right job.\u003C\u002Fp>\u003Cp>Speaker 2: I mean, the thing with the thing with, like, stuff being abandoned or whatever, like and the and the risk of that is, like, well, like, how much were you sped up by using that thing in the first place, and how much are you now being slowed down by it being abandoned? Right? Like, you gotta you gotta look at the whole picture. And, like, you know, you you can't necessarily tell what's gonna what's gonna, like, be less supported or is not gonna be the the favorite going forwards. And it's like, you know, if it helps you at the time and at the time you evaluated it and you had a good evaluation process that you can kinda stand behind and you're like, yeah.\u003C\u002Fp>\u003Cp>We decided that that was the right thing at the time. And now in hindsight, it was a terrible choice. But also, like, we were sped up at the time, and we got the job done, and we delivered the project. Like, what, like, where do you wanna be? Like, do you want the thing or not?\u003C\u002Fp>\u003Cp>We could also stay at decision paralysis at the start of the project forever and just wait to see which thing comes out as the best. That is true.\u003C\u002Fp>\u003Cp>Speaker 1: This is part of the reason I like to, comment my code so heavily to document those decisions\u003C\u002Fp>\u003Cp>Speaker 0: poor,\u003C\u002Fp>\u003Cp>Speaker 1: you know, whether they turn out to be better in the future. Also, because it may seem altruistic at first, but it's mostly for me,\u003C\u002Fp>\u003Cp>Speaker 0: you know,\u003C\u002Fp>\u003Cp>Speaker 1: 6 months down the road looking at your own code being like, why? Yeah. Why did I do this?\u003C\u002Fp>\u003Cp>Speaker 0: And then to read like,\u003C\u002Fp>\u003Cp>Speaker 1: oh, this, you know, this very minor edge case is gonna be\u003C\u002Fp>\u003Cp>Speaker 0: There's nothing worse than, like, you go into your old code, and you're like, that's not the way that should be.\u003C\u002Fp>\u003Cp>Speaker 1: Why did I\u003C\u002Fp>\u003Cp>Speaker 0: do that? And then you, like, try to fix it. And then, like, 2 days later, you're like, oh, that's why I did that.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I can't oh, yeah.\u003C\u002Fp>\u003Cp>Speaker 0: That makes sense.\u003C\u002Fp>\u003Cp>Speaker 2: I always I'm always writing comments and encouraging the team to write a comment. You know, if you're just, like, hacking a fix in, but it's because you've got no time, add a comment being like, hey. Hey. I know this isn't great, but, like, I'm I'm under time pressure right now. Right?\u003C\u002Fp>\u003Cp>And just and just, like, being being nice to your past self and and your past team selves when you're looking at code and you're like, what the hell are they thinking? And it's like, well, maybe they were, like, under super client pressure at the time or, like, you know, didn't have the full picture of what was going on, and and now you do. And it's like like, that's the like, software is the the clue is in the start, but it's soft. Right? You can change it.\u003C\u002Fp>\u003Cp>It's Okay. If it's bad today, you can someday, if it's so bad, you can fix it. You can do something about it.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And you're not, I mean, you're not really intimating unless you're breaking things. Yeah. Right. You gotta just document those breaks, why those breaks are there intentionally and with nice fun emojis right there in the comments and the GIF.\u003C\u002Fp>\u003Cp>Yeah. So I'm curious, you know, when starting the creative process, you know, Stu, I'll I'll throw this over to you, and, you know, there's obviously Jed's other point. You have lots of things that, you know, back when we were, like, I like, living together and you were working through these really exciting projects, and now there's probably more enterprise y, large scale projects. But regardless, when you're starting a project like that from scratch, you're sort of in the initial ideation, you know, r and d\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Phase? Like, how what does that look like for you, like, moving through that process from, you know, 0 plus?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Well, in some ways, I feel like such a dinosaur that I'm almost embarrassed to to talk about it. But, I'm I'm really into physicalizing ideas. And, obviously, I'm a tech guy. Right?\u003C\u002Fp>\u003Cp>I don't think we'd be having any of this discussion were it otherwise. But, and yet, pen on paper, Sharpie, just, you know, paper's recyclable. It's cheap. You can burn through ideas. The worse the scribble, maybe the better.\u003C\u002Fp>\u003Cp>And then being able to physically tape up or pin up that stuff so that your your memories are anchored physically around you, and you can refer to a cluster of ideas. And so I know it feels like most people don't work that way in 2023, and it feels like a very old, mammalian kind of, you know, I need to physically represent things in a certain time and place, but, yeah, that's that's how I'll start to formalize the very cloudy ideas in my head. And then from there, I'll start, you know, sketching in, maybe in Figma, if we're really honest, sometimes illustrator still, or directly in code. And, you know, code's so wonderfully flexible that, yeah. So, yeah, for me, it still begins with a a physical process.\u003C\u002Fp>\u003Cp>Speaker 0: Interesting. Yes. I like, I've seen a lot of that, like, working with companies like IDEO. They're big on, like, sticky notes. I remember back in, like, school so are you is it, like, printout paper, like, no lines, line paper, grid paper, field notes?\u003C\u002Fp>\u003Cp>I'm so curious.\u003C\u002Fp>\u003Cp>Speaker 2: Like,\u003C\u002Fp>\u003Cp>Speaker 0: paper is pretty tense about a space stationery. Sticking up to your walls.\u003C\u002Fp>\u003Cp>Speaker 1: You know, back back when we lived together, ancient times and, and Yukon, really, I mean, I was all about those, Rhodia grid pads. You know? I needed the grid. Yeah. I'm sure you have some within reach.\u003C\u002Fp>\u003Cp>Probably.\u003C\u002Fp>\u003Cp>Speaker 0: There's a lot. I have, like, literally just from, like, from those times and just I'm like, in my head, like, yes. That's exactly what I need. It's just pads, field notes, grid paper, like, just every sort of paper. Like, yes.\u003C\u002Fp>\u003Cp>Right tool for the right jobs. Like, sometimes you need a grid, sometimes you don't. It's like an home and joy.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I I started to migrate, around I guess when I when I went back to school, I realized I started getting all of these notebooks that were just, blank paper, just blank, no grid. Yeah. Just no rules. No no constraints.\u003C\u002Fp>\u003Cp>Oh, there must be speaking of, like,\u003C\u002Fp>\u003Cp>Speaker 0: the design the intersection of design development, sorry, Code and creative. I feel like there's the intersection of, like, sticky notes in a in a notepad, like, in a, like, spiral notebook. There has to be I mean, I guess maybe it is just a sticky pad, but something a little bit more formalized. So it's like a book, but you can just pull it out and it's got the sticky Mhmm. Right there on it.\u003C\u002Fp>\u003Cp>We'll we'll get there. I like that. So, David, any thoughts on that? Like, when you're starting starting your process, anything specific beyond sort of taking into the world with with stickiness?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I was thinking actually about that that thing where you said, like, the worse the sketch, the better. It reminded me of, you familiar with the process shape up from, from base camp and 37 signals. They have like a whole dot. There's a, if you Google it, you'll find it.\u003C\u002Fp>\u003Cp>And they, they talk about thick marker wireframes, Right? So using, like, a really thick pen to draw a wireframe so that no one can get bogged down in the detail of, like, how you know, is this thing this size or that size? Is it adjacent to that thing or above it or or to the left of it? Like because it's like the you've used such a thick marker that you can't tell the detail. You can just kinda get an idea of the rough shape, is like a that's what it made me think of when you were like, the worse the sketch, the better.\u003C\u002Fp>\u003Cp>I'm like, yeah. I definitely see the value in this.\u003C\u002Fp>\u003Cp>Speaker 1: Engineer that. So working on some very early was yeah. Wow. This was web based VR stuff in, Creative Lab around, like, 2015, I guess, maybe 2016. So, we were kicking around some ideas, potential product tie ins, things like well, I won't mention what we were working on exactly, but it involved us making some sketches of things in VR and these were early prototypal things that we were going to use to excite the people with the money in the company and then get the budget so that we could outsource and bring in you know, modelers and and, you know, hardcore VR engineers, and then we could start to build the thing.\u003C\u002Fp>\u003Cp>But the problem was making these prototypes. They looked terrible. You know, they're mostly, you know, low poly things that we could make quickly And that was lost on a lot of our higher ups and executives. They'd put on the headset and they'd be, why is it so terrible? And we kept trying to say, this is a sketch.\u003C\u002Fp>\u003Cp>Don't you understand that that looseness and terrible, you know, the slow it looks like, you know, lawnmower man because we're not spending money or too much time on this. And no matter how many words we tried to use to verbalize it, it didn't come across as a sketch. And this was, shortly after Google had acquired, Tilt Brush. And Tilt Brush did not have a scaling feature in it yet. It was early days, and we were making this cityscape and it looked terrible.\u003C\u002Fp>\u003Cp>So I had this idea, we need to do the napkin sketch for VR. So what if, just in my living room at home, I had my own VR rig. What if I could use Tilt Brush to hand draw the cityscape so it looks like a sketch? But you needed to be inside of it, and you couldn't do that yet. You could only draw as tall as you could reach.\u003C\u002Fp>\u003Cp>That was the, you know, the limitation of Tilt Brush. But now I knew these folks. They were part of the Google family, so I was able to just email them and say, hey, I need to scale up these drawings. How could I do this? And they were able to send me, you know, some Python code and and walk me through how their, Tilt Brush files were organized, how I could break them apart, you know, increase the size of all the vectors, which is pretty easy operation.\u003C\u002Fp>\u003Cp>It just wasn't in the interface yet. Put it back together and then open it. And so ultimately, that's what we did. The next walkthrough was literally a sketch of what we were trying to make. So it was really like the reverse pretending that it's still on the napkin.\u003C\u002Fp>\u003Cp>Yeah. I think that communicated a little better that this is a stand in for the real idea. And if you like this, give us the money, and we'll go do the real idea.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. I love that. First of all, Tilt Brush is amazing. I remember the same thing just in there just building.\u003C\u002Fp>\u003Cp>I think I feel like they had a scale feature or maybe not, actually. Yeah. Probably not.\u003C\u002Fp>\u003Cp>Speaker 1: I think they know. They didn't or before\u003C\u002Fp>\u003Cp>Speaker 0: They chose the app. It's never happened to you.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Oh, yeah. Now, I mean, by the time that app was deprecated, it was trivial to scale things up and down. Yeah. But in the moment, we had to\u003C\u002Fp>\u003Cp>Speaker 0: I think it's important. Like, the idea of, like, a rough knock or, like, those prototypes where it's like you have to enforce the rough because if you go too far, I was guilty as, like, a perfectionist. I'm like, oh, I'm making these these mock ups or whatever it might look like in the really early just pitching things. And if you, polish it too much, it's it just seemed it comes off as like a design that they start nitpicking like, oh, how do we change that? Like, we're not even looking at that.\u003C\u002Fp>\u003Cp>We're looking at, like, broad strokes here. But the more detailed and accurate you make it, then they start focusing on different elements of it. So it almost helps to have, like, Tilt Brush. I'm just gonna swap this out there and just, like, see you know, just only comment on what you can see. And when you start adding and predifying it, becomes a very different conversation is is what I found.\u003C\u002Fp>\u003Cp>Speaker 2: I also think it reminds me, like, as a as an engineer, like, I think about these things maybe with like, I draw a lot of technical comparisons. And I think about, like, when you're coming up with ideas and trying to, like, come up with a solution to a problem or or come up with an idea, I think about it as, like, not locally optimizing down one path. Right? And it's, like, going to if you go too detailed too early, I sort of think about it as, like, I don't know if you're familiar with this sort of, like, approaches for for for sort of, like, machine learning or finding finding optimum solutions to problems of, like, the difference between, like, a hill climb algorithm versus simulated annealing. Right?\u003C\u002Fp>\u003Cp>You may be familiar with these. Like, the idea of a hill climb is like if you're sort of, like, searching through a problem space, you just go to the next best problem the next best, like, answer and then the next best answer and the next best answer. And eventually, you get to the top of where of the answer, and you're like, that's the best one. But what you didn't know was that the other side of that hill, there was another much bigger hill that was like an even better solution that was just totally different. But you couldn't get there because you were just, like, going down the better and better path and you weren't, like, exploring other options.\u003C\u002Fp>\u003Cp>And then simulated annealing is the idea where of, like, when you start trying to find the solution, you have an element of randomness where you're always just like, you're sort of you know what the best you found so far is, but you then also randomly just kinda, like, go wildly off piste and try something completely different and go, is that better? And then you're like, no, no, never mind. This is actually the better thing. And then sometimes you find those completely different solutions, and you go and explore. So yeah, right.\u003C\u002Fp>\u003Cp>Very similar. Very similar.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. You're also describing I'm not going to do this justice, but the difference between gate based quantum computing and annealing Oh, really? D Wave does. Yeah. And their, gate based is winning for sure, but, there's some pretty good arguments, I think, for quantum annealing still.\u003C\u002Fp>\u003Cp>Right. But it's the same, I won't derail this further, but it's the same sort of math.\u003C\u002Fp>\u003Cp>Speaker 0: So like,\u003C\u002Fp>\u003Cp>Speaker 1: you know, I mentioned, like, having to learn to multiply matrices again and, like, you know, reteaching myself linear algebra for 3 d stuff. And, you know, all that computation is best handled by a GPU, you know, massively parallel small computations. And then you start to realize it's actually pretty similar similar for, artificial intelligence and machine learning, you know, model training. And then it turns out, again, it's the same old story for quantum simulation. So, yeah, that's how that's how I bounce through those things.\u003C\u002Fp>\u003Cp>But I'll\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: I'm derailing. So What's more\u003C\u002Fp>\u003Cp>Speaker 0: what's more exciting than quantum I mean, I don't I know nothing of quantum computing, but what could be more exciting than than going off the rails there?\u003C\u002Fp>\u003Cp>Speaker 2: I once, I once met someone who was at a at a wedding who's, like, doing research in quantum computing, and I was like, oh, this will be interesting to talk about. Like, I I'm a you know, I'm an engineer. I know a little bit about this kind of stuff maybe. And And I was like, so tell me a little bit about what you're working on. And I think I got a best one in 10 words that he said.\u003C\u002Fp>\u003Cp>I was like, I have not like, you have just immediately lost me. Because he was in some kind of, like, world to do with, like it was something to do with about how you measure the output, and there was some very specific detail of what he was looking at. And I obviously cannot remember it. I did not understand it at the time. It was fascinating to just hear someone talk about it and realize that I'm like, I I'm getting 1% of this.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. But if if you think it's an exciting topic, I have a recommendation, which is, this guy who was at Microsoft at the time, Andrew Hellwer, I think is his name. I think it's h e l w e r. He gives this lecture that you can find on YouTube, and it's named after clearly his favorite book on the subject, which is a great book. It's called Quantum Computing for Computer Scientists, And, I've forced myself through the book.\u003C\u002Fp>\u003Cp>His lecture is so much more to the point and beautiful in its simplicity. He throws away all of the bad metaphors about, you know, cats in boxes and, you know, pizza bagels. Is it a pizza? Is it a bagel? It throws that away, and it gives you the very basic math, from a computer scientist perspective.\u003C\u002Fp>\u003Cp>And from there, that's that was my entryway because I was trying to figure out, like, what is the super cool quantum, quantum computing, quantum physics, whatever superposition? And because of him, I can now give some pretty concrete answers about what's a cubit and how does it work, at least from a mathematical perspective. The actual physics of this stuff is\u003C\u002Fp>\u003Cp>Speaker 2: Of how that stuff works is, yeah. It's I feel like\u003C\u002Fp>\u003Cp>Speaker 0: I'm in much easier to get the Schrodinger side than the math side. I'm a visual No. No. No.\u003C\u002Fp>\u003Cp>Speaker 1: A a cubit is just two values. That's it. It's just a pair of numbers, and then you can build from there. That's it. Yeah.\u003C\u002Fp>\u003Cp>It's just a pair of numbers.\u003C\u002Fp>\u003Cp>Speaker 0: It's only that easy. We're just period and then end of end of the whole lecture. Yeah. So to kinda, like, bridge quantum computing, which is I'm not gonna say inaccessible until you listen to that lecture and you really understand all about it. So, like, AI and something that is, for a lot of people, you know, very inaccessible in terms of really understanding how it's all happening behind the scenes.\u003C\u002Fp>\u003Cp>But there is sort of a resurgence or not resurgence, but a continued resurgence of, you know, AR and VR and AI, all these things kinda, like, coming to a to a front. I'm curious, what do you think so specifically on the on the ARVR side because I think there's probably more experience there. Stu or David, any thoughts on how that's gonna change the landscape in terms of, building online experiences?\u003C\u002Fp>\u003Cp>Speaker 2: I mean, I just I That's definitely one for handle, you know, like, can't\u003C\u002Fp>\u003Cp>Speaker 0: stand those people with community on its name. You know, from from professional to prosumer to consumer and then just keep working its way down the tree. I'm curious what your thoughts are on, like, what do what do think the the issues might be, the challenges, the opportunities, the cool stuff that might start flowing and maybe when, in the ARVR space?\u003C\u002Fp>\u003Cp>Speaker 1: Well, I I think it'll take me a moment to process\u003C\u002Fp>\u003Cp>Speaker 0: what I\u003C\u002Fp>\u003Cp>Speaker 1: can talk about and what I can't talk about. So I'll I'll start with a story from the the pandemic, or, well, let's say lockdown. It's still a pandemic, right? We're still anyway, in fact, I'm almost officially, one line again on the COVID test Just\u003C\u002Fp>\u003Cp>Speaker 0: a\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That was my past week. So we were we were working on something at Amazon, a side project that had to do with warehouse spaces and developing a sort of kiosk that would be for internal use for, you know, a specific task. And we're all working from home. We're all trying to figure out how to do that and also work on projects together.\u003C\u002Fp>\u003Cp>And, to develop this kiosk, I really what I really needed was to have, like, a bunch of different TV screens and things delivered to my home and to have someone prototype these things together. And then I could say, like, you know, this is too big, too small, in the wrong place, whatever, and that just wasn't happening. We just weren't at that point as a society to make that possible, nor do I really have warehouse space in my small Brooklyn apartment. But what I could do was very quickly mock up all of these things, using Amazon Sumerian, put on the headset, and walk around in that space and and get a sense, you know, style, in relation to the size of my own body and feel like, oh, okay. So if this is proper scale, this one feels right.\u003C\u002Fp>\u003Cp>This one doesn't. You know, this is somewhere in between. And it's an immediate gut reaction, and it cuts out all of the guesswork just being able to be virtually in that space, without having to pay for any of the hardware, have it shipped, have it set up. So that that may be a safe way to start to tiptoe into answers here to that question, hopefully.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. No. I mean, a 100%. I mean, I've it's it's interesting just the spatial side of it because you're you know, obviously, it's immersive, but that means, you know, we are directional as a as a animal. You know, we see this way, but it's you know, there's things going on behind you, and so it's just a whole different landscape.\u003C\u002Fp>\u003Cp>You know, what you what you kinda mentioned, I think, kinda ties into the fact that, you know, it's it's just a completely different ballgame. It'll be a different mindset for how you develop and build in that space. How do you hint to somebody that, like, you're supposed to be looking behind you? So, like, maybe maybe turn around and go look at that call to action or whatever whatever that might look like.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Oh, for that, I mean, shout out to, former colleague, Jess Brillhart, who's a big name at Creative Lab and now continues to be a big name on the West Coast. She gave the keynote at South by Southwest a while ago for immersive experiences. She's a filmmaker by education, and has a lot to say about directing attention and focus in a 3 60 degree medium, and I say 360 because at the time, it was really 3 60 film. So it really it really was like rails in a circle around you, and, of course, immersive is, you know, that on all axes.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. The 360 day, that was the project that I was working on at, that large company that got 90% of the way there and then got scrapped because of video content being lacking. So ARVR is pretty exciting because the content doesn't lack. You just create it. It's a little bit more generative, so that's, a different different space.\u003C\u002Fp>\u003Cp>But, so we're coming up on an hour. There's a a bunch of questions that came in, but I don't know if we're gonna have time for those. But, yeah, any any what's that?\u003C\u002Fp>\u003Cp>Speaker 1: Oh, no. I feel I feel bad. We should get to Well,\u003C\u002Fp>\u003Cp>Speaker 0: I some of them. Let's throw we could throw one in because I think it kind of brings us back to that sort of the intersection space, which the question was, do you have any strategies around fostering collaboration culture between designers and developers? And I'll just quickly like, I this is a big thing for me at Ranger, my my old agency, because, I I was both. So I was, you know, I was the intersection, and I just tried to hire people that were the same, you know, that had the same sort of, like, training or or background skills, that I had. And And so I was able to avoid that altogether.\u003C\u002Fp>\u003Cp>You know? And I kinda even pitched that in meetings. I was like, oh, there's the Harvard, like, oh, we're gonna have the seating chart. You know, and we're gonna have our designers here and developers there, and here's how they work in pods and all that. And I was like or you could just avoid the communication altogether and just make them all, you know, it's kind of pre creative technologies, you know, as you turn.\u003C\u002Fp>\u003Cp>Any thoughts on how you can foster that? Like, if you have teams of, like, strictly developers and strictly designers, how you can, marry the 2 of those for either of you?\u003C\u002Fp>\u003Cp>Speaker 2: I've been thinking about this a lot at the moment. Because as an as an agency, we don't have any designers at the moment on us on staff. So we work a lot with third party designers or design teams or freelancers. And so I've been thinking about this a lot because we've it's always been a challenge. We've never quite hit on the right process, and we still haven't hit on the right process.\u003C\u002Fp>\u003Cp>But some of the things that I think about is, like, I think the best devs are the ones that know a little bit of design or, like, good UX and goods, like, like, understand what, you know, some of the, like, just kind of, like, basic underlying principles about what's good and bad design. And the best designers are the ones that understand a little bit about what are the limitations of the medium that I'm delivering into. Right? My worst experiences of working with designers are people who are print designers by trade and haven't really adapted to the web and that but are just doing some web design stuff as well. And it's like that is just such a different world that, like, it it doesn't align.\u003C\u002Fp>\u003Cp>Similarly, I've worked with devs or or or, like, helped look after devs who don't who just don't intrinsically understand anything about good or bad UX. And so as a result, we'll, like, implement designs badly or, like, not even put the right kind of boilerplate and skeleton in place to kinda unlock further development on top. And then, otherwise, I think, like, just a lot of side by side work. Right? Like, just making sure that things are iterative, making sure that it's about, like, responding to challenges, especially when it comes down to kind of delivery.\u003C\u002Fp>\u003Cp>The worst projects are the ones where delivery of designs is a cutoff point. Okay. Now start the designs. Now start the implementation. Sorry.\u003C\u002Fp>\u003Cp>And the best ones are the ones where, like, every small stumbling block of, like, oh, this is actually really hard to lay out on the page, or this is actually really hard to bring to life. Like, but if you just change would it be like, if we just tweak this, it'll be way easier to do. And then, you know, having that kind of that process of, like, it's not about, like, criticizing the designs being wrong. It's about, like, work working within each other's limitations, the space of delivery, and the space of design. I think that that works best, which is maybe a maybe an obvious answer.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. No. But an important one. I mean, again, like, not to get into methodologies, but, like, the waterfall, you know, especially if the designers are just not aware of what they can or can't do can be really problematic. They have to progressively enhance, and they have to know the limitations.\u003C\u002Fp>\u003Cp>And if you only find that out once things are done and polished, you're you're in a bad bad spot. But Yeah. Stu, any any thoughts on that one?\u003C\u002Fp>\u003Cp>Speaker 1: Well, I feel like I have a similar experience as you. So educationally, I think we're both taught print design, but, of course, with, that's not our trade at all. You know, we're in the code from an early age, so that sorted itself out. Still big love for good print design though.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. For sure.\u003C\u002Fp>\u003Cp>Speaker 1: But like you, I'm that intersection and I kind of, my present team excluded because I love my team, so I don't wanna give the impression that I don't. I think outside of my team, what I've experienced in the past is that, like, it's the 2020s. Why are these things still separate? They should all be me. That sounds very narcissistic or very arrogant.\u003C\u002Fp>\u003Cp>I mean, like, I accidentally somehow had these two influences and I'd like to think it was special. That should no longer be special. It's the kids coming up today really ought to have a foot in both worlds and may specialize in 1 or the other, but it's just so weird to me to meet young folks who don't, who seem to be firmly in one camp or the other because it just seems like evolutionarily, that that's just how it should be today.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. And you can actually see\u003C\u002Fp>\u003Cp>Speaker 0: When I would hire people at Ranger, I was I used to talk about, like, this t shaped tire. You know, you've got, a deep dive in their area of specialty, but then a crossbar of understanding so they can communicate with the other teams. You know? I'm a I'm a friend developer, but I can talk to the designers in the back end database. But Yeah.\u003C\u002Fp>\u003Cp>Now I feel like I need to update. Maybe it's like a capital m, and it's like you got a couple deep dives, a little bit more, you know, jack or jill of all trades. You you understand this and that. You're kept but you're a little bit more sprawling with the adjacent sides of you know, print design can be super relevant in how you do digital design, which can be really key in how you do UX and UI. And, you know, I think it all is really just makes a more robust, experience in person, when they're a little bit more multifaceted.\u003C\u002Fp>\u003Cp>But\u003C\u002Fp>\u003Cp>Speaker 1: yeah. I just wanna reiterate again, love my team. It's just it's in the back of my head because we were our team was having this discussion recently. And so, you know, shout\u003C\u002Fp>\u003Cp>Speaker 0: out to Yeah. That's a huge one. I I completely agree. I I watch sci fi movies all the time. I'm like, man, this is I watched the old sci fi movies.\u003C\u002Fp>\u003Cp>Like, they're saying, like, by 2020, we're gonna be in a very different place than where we are actually. I saw one last night with a creator, which was very timely with, you know, the AI stuff, and it was and it had some amazing not print design, but sort of that feeling to it. So I think, yeah, there's lots of ways all this can get pulled together. I but I think it all comes down to that intersection. Whether intersection is inside of one person, you know, they're they're both as, you know, I think the 3 of us are, or you can foster and bring together multiple people across a team and make that team sort of the the whole of of this idea of that intersection.\u003C\u002Fp>\u003Cp>I as I mentioned, I love metaphor, and I'll leave I'll leave us with, a story from when Stu and I used to live together. So we, you know, probably riding around the kitchen with on a unicycle or something. But, at one point, we had a a microwave, and it was, you know, just a throwaway junkie microwave we found. We're like, well, we're not gonna use it. We're just gonna trash it.\u003C\u002Fp>\u003Cp>And so we're like, well, what can we microwaves, you know, as as as younger people do? And so we we put, like, soap in there, and, like, soap was doing cool stuff and all that. And then we figured out, the the, you know, the ultimate test was this grape. And we just cut a grape in half, and you sort of leave the two halves of the grape just slightly connected. And it essentially the sugars or whatever it is, it forms plasma, and this ball of plasma rose to the top of the microwave and literally was melting the metal.\u003C\u002Fp>\u003Cp>But it just came to me while we were talking, and I was thinking back through all those those sort of things that happened. I was like, that's the intersection of design development. You have just, you know, 2 things to these 2 halves. This could be very different, but just you have that connection, and then suddenly you've got this plasma ball that's burning right through the top. You're innovating to the point where you're breaking things, and that's what we're all trying to do.\u003C\u002Fp>\u003Cp>Speaker 2: So And then your microwave explodes.\u003C\u002Fp>\u003Cp>Speaker 0: And then the microwave explodes. And then\u003C\u002Fp>\u003Cp>Speaker 2: it was I\u003C\u002Fp>\u003Cp>Speaker 0: burned up the the I burned up the Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: I had the cord, you know, just ready to pull out anything whenever\u003C\u002Fp>\u003Cp>Speaker 0: At least we had yeah. We had the the fire extinguisher and the cord ready to get pulled, but, no, it was awesome. And I'm just trying\u003C\u002Fp>\u003Cp>Speaker 2: to work out where that was going as you were saying that. I was like, where is this where where is this coming back\u003C\u002Fp>\u003Cp>Speaker 0: You just We made it again. Awesome. Well, a little over time, but I this was super like, just I love just meandering through these these different topics, and and, I I think that's what it's what it's all about. How can you be creative if you had everything super structured? So, yeah, David, Stu, thank you so much for for hopping on and chatting through this.\u003C\u002Fp>\u003Cp>Hopefully, we can we can do it again sometime. This was this was awesome. Yeah. Make sure it happens.\u003C\u002Fp>\u003Cp>Speaker 2: Cool.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Thanks, guys.\u003C\u002Fp>","Awesome. Well, super excited, to be diving into this. I think it's gonna be a really exciting conversation. Obviously, this is I've built my entire life around, like, the idea that we're talking about. I think we all kinda have, so it'll be hopefully some some interesting stuff to cover. Why don't we kick it off with some intros so everybody kinda knows who's who's chatting, and then we can get into the theme. Stu, do you want to kick it off with with an intro? Yeah. Sure. Let's see. What's a good, succinct way to put this? I have both a graphic design background and a coding background, so design and engineering together, which is awesome. So I speak both both vocabularies. It didn't use to be awesome, I feel like, then when we were young. It made us kind of an outcast in both worlds. Maybe I'm speaking too much for myself. So I've worked at Google and Amazon and various other places, and I'm currently at Unity Technologies. For the for the second time around. Right? For the second time around. Yeah. I was pretty torn about leaving the first time around. I left to pursue something pretty interesting, and very happy to be back in the family with a lot of folks that I really enjoy solving tough problems with. And I should probably mention that spans like creative technologist stuff, augmented reality, virtual reality, a little bit of quantum computing thrown in there. But, yeah, it bounced around between product design, marketing, research work. It's all fun. Definitely. Yeah. I've probably known for 20 20 years, over 20 years maybe. Going all the way back to the beginning, you know, talking about, like, getting trained, whatever, learning, you know, design and development. You definitely kinda get this sense of, like, not jack of old trades master, jack of 2, master of, like, hopefully both, like, freedom and technology. They're just no different. But, yeah, all the way back in the the Yukon days. David, how about how about you? Maybe back from where you are now. Yeah. Great to be here. Thanks for having me. So I'm a head of engineering at a digital product agency, here in the UK called Pixielabs. We're London based. We design and build digital products, mostly web based or mobile based. We've been going for 10 years now. We had our 10 year party actually a couple of months ago, which was a lot of fun. And I've been with PixieLab since its inception, helping to initially writing all the code and increasingly my team not letting me write the code. And prior to that, I was, helping to kinda lead developments, at an innovation lab at News UK, or at the time it was News International, which is the publisher of The Times of London and The Sun, a big newspaper publisher here. And prior to that, my my background is very, traditional. I did computer science. I was I was a very traditional nerd and always have been, always will be. But, yeah, worked on a whole bunch of stuff under under the kind of pixielabs umbrella, everything from large companies, you know, people like Twitter and HomeServe, but also start ups and and smaller brands and everything, yeah, from, like, big platforms for Fitzy 100 down to cat food subscription websites. And we once made a thinking about, sort of like things that are the most creative, we once made a typewriter type by itself when you tweeted at it, for an art installation with in partnership with Twitter, which I get so much mileage out of in conversation forevermore. Like, that's like the one. It's like the one project where you're like, I can talk about this a lot forever. Was that at a museum perchance? Like, was that a museum over on your side? Yeah. It was like it was initially a there was like an art exhibition. They ran Twitter ran like a competition for ideas that were powered by tweets in some way, like any idea. Right? And people submitted everything from, like, pigeons flying around measuring and tweeting the air quality, all the way to the typewriter that was, like, typing out a a novel by through Twitter. And we had to, like, come up with some representation of all of these ideas that people had submitted or, like, 6 of the winning ideas. And then I think the typewriter ended up at the Globe Theatre as well, where it was typing the complete works of Shakespeare, but it would wait for the someone to someone in the world to tweet the next word that it wanted to type. It took months and I think really annoyed all of the staff like, in the corner, like, clacking away. This was a really loud old school typewriter. Yeah. So just randomly for someone to, like, say the word betwixt, and then it could keep on Yeah. Exactly. Exactly. And, you know, like it wasn't the I will admit it did have a time limit before it would just type the word and move on. Otherwise, I don't think it would have ever finished. I mean, you can just sit there and, like, be the one to keep it moving along. But Right. And that's the thing. You would stand in front of it. Right? And you could tweet the next word, and it would, like, tweet back at you saying that you'd you'd powered it. It was wild. The idea of an impatient typewriter. Yeah. It was. It was impatient. Yeah. You should just start tweeting out, like, prompts. Like, okay. Listen. We gotta get this thing rolling. Yeah. Please someone tweet this word. Yeah. Yeah. I did. So at my agency, which is called Ranger, we did something similar. It was with Google, and Teller, I believe, was, like, the creative side, that was pushing it. It was a museum, and they had oh, I don't even remember the one of those things you look through, the, periscopes, telescopes. Mhmm. But it was, like, all digital, and they had all sorts of, like, art museum installation stuff that was, connected to like, real world stuff that was connected to technology. So similar to what we're talking about today, you know, bridging the gap between the real world and digital, I think we're kinda getting more into the the creative and code or design and development depending on which alliteration you choose. I love it. Well, thank you both for for being here. This is, super exciting. So and then just kinda tap it off. So my I'm Ben Haines. I started, an agency maybe 15, 20 years ago, called Ranger out of Brooklyn, which is where Stu is right now, I think. So David's in the UK. Stu, you're in in Brooklyn. So I started in Brooklyn, really, with exactly what we're talking about. Like, I had seen a lot of development firms, and I had seen a lot of design agencies, and they were trying to communicate. And you just get a lot of the, the design hands off. It's like pixel perfect, you know, mock, or here's what we're here's what we're looking for. And, you know, the the development's like, oh, we built it. Here you go. But it's not really following the the design principles. They're not giving feedback on what's possible. Like, designers are really great at making things that look great, but not functional. So I tried to bridge the gap hiring designers and developers to build for, you know, Google, Snapchat, AT and T, Prada, US government, etcetera. But that's where this software Directus kinda stemmed from was, wow. Okay. So we're building these really innovative projects for those sort of companies, and, you know, smaller companies, culture institutions, etcetera. But we're building the same thing over and over and over again. How do you take maybe one of the more I mean, quantum computing, Stu, so you you got me there. But, like, the database is pretty technical. How do you make the database, you know, in the work of a a DBA, a database administrator, you know, how can you democratize that so the business user can do it and you you do it through design? You know, really strong intuitive UX and UI, giving you know, I I was inspired by PHP MyAdmin, which I think is maybe lacking design. I you know, Not not the best not the best system I've ever used. So I was like, oh, just take that and make it friendly and intuitive and safe. So that's where Directus, you know, our our software sort of stemmed from. But so today, we're we're kinda really covering the basis of, where all 3 of us live, which is are we gonna say design and development or creativity and code? I like I like both, I guess. I think I'm leaning towards creativity and code. Okay. Yeah. I would say design development, but creativity and code, I like it's not just design, I guess. So, yeah, that is probably better. So the intersection of those two things or the overlap even, I think we've all sort of built the whatever we've been doing for the last 10, 20 years has been has been right there. So, without further ado, why don't we jump in? So I guess to start off, Stew, you mentioned sort of, your history going through different large orgs, you know, AWS, Google, Yahoo, relatively structured companies or very large companies. I'm curious when we're talking about creativity and code going at the c's, how do you, like, encourage a culture of innovation at a company that's a little bit more rigid or, a little bit more red tape? Wow. I guess, the answer varies quite a bit depending on which context. So for instance, Google is a huge company. Right? And there's a lot of rigid structure in place necessarily. But what was really cool about Creative Lab and also, the data arts team is they were little corners of the company that, folks like Andy Barrett, who runs Creative Lab, he had won the confidence of his fellow executives to, say, we're gonna go off and do something crazy over here. Here's why it's justified. Here are the business reasons and marketing reasons and whatever, but we're going to go be a little nuts and you're going to sit back and chill. And so I'm so glad that like in that context, there's a very strong leader standing between you and a lot of that process, and it allowed Creative Lab to be a little crazy. And I I haven't been there in, quite some time. I feel like there's a little bit of a trajectory where it started to become a little more formalized, probably also necessarily. But I also remember the more Wild West days, literally, with a sword and champagne bottles and stuff, just impromptu on like a Tuesday evening because everyone's working late and major projects are due. So there are a lot of good it was a place where there could be some irreverence and the space and time for some creativity. Of course, things have to get done and they have to get shipped and delivered, so it wasn't without pressure for sure. Whereas I would say Amazon was a very different case and ultimately, I don't think AWS is just so not geared towards a creative process. So I think I lost the battle a bit there within AWS, which is sad. Yes. There's some great people there. But, I can imagine, like, just in the in the 2 you just described, you know, one has creative in the in the title, you know, in the team name and servicing. So, like, they kinda have maybe different focuses, maybe. But Yeah. For sure. Yeah. And that's that's pretty amazing. And I I feel also feel like, you know, the more emergent like, the newer the idea is for a team or for a company, regardless of how big it is, it's easier to be in that, like, divergent mindset and just be like, oh, we're gonna break things. We're gonna innovate. You know, that's one of the same. We're gonna be, like, super creative and just kind of push the envelope. And then over time, it's like, oh, we have real world things. We have to answer the investors, or we have to kind of rein it in a little bit and switch to convergent mode. Both of those are pretty established companies, but creative I don't remember when Creative Lab was formed, you versus I don't know. I wanna say maybe there were seeds of it in 2006 or something, which is well before I joined. That would have been I mean, it held on to that idea. Very fuzzy about the yeah. Yeah. I mean, held on to the the ethos of, like, create I mean, yeah, that's the purpose of the team. Interesting. And would you say the same thing for I think we we were talking recently. I think maybe there was some overlap at Yahoo between when you were there and when I was there, maybe not. But would you say the same thing for for Yahoo? Exclamation. Well, I think, I, speaking of returning to places, so I had been at Lab, went to Yahoo for a few years, then went back to Creative Lab and Dana Hartstein, which is a whole other story. So I think I entered Yahoo with a different mindset. The context was that Marissa Meyer had left Google, and she had taken over as CEO, and it was still the early days of that, so there was a lot of this optimism that Yahoo had built this giant, I'm gonna use metaphors here, had built this giant ship, and it was a brilliant ship. It was a big battleship out on the sea, but over time, its facilities have been squandered and things were not going so well. So Marissa climbs aboard and says, I'm the captain now, and you could go do a startup or you could go do something else and you'd have to build your entire ship, and that is a difficult lift. Or you could jump on this ghost ship and take all the good parts and rebuild them, and that seemed to be the mentality. We're all here. It's a new era. We already have all of these products and services and things that generate revenue and just let's use it. Let's build anew, from this kit of parts. And, so it was a pretty exciting time. And there, maybe I'm speaking too much for myself, but it felt like there's a bit of this sort of pirate mentality of like, here we are. Let's blow things up and let's reinvent. And because of that, I started out in marketing, had some success there. My biggest success there already incorporated cutting edge research, early days of machine learning, Marketing, obviously, design is what I was bringing to the table. So it was all like engineering, marketing, and design altogether. And because of that, I was in marketing, then I was in product, and then I was in research. So I literally, like, bounced around the company kind of doing similar stuff at every turn but with a different hierarchy and sort of a different set of rules being bent in in a new direction. I like that. I like that. So anybody on my team knows I'm a big fan of metaphors. And I I thought for sure, when you talk about breaking the ship apart, you're gonna go down the, like, the Theseus, paradox. Oh, right. Yeah. And I was like, oh, no. We're going pirates. We're doing pirates. I like it. That's that's amazing. Yeah. I think that that kinda makes I I I have always sort of worked at, my like, an agency, like self self employed with the exception of maybe a a year, year and a half at we were both, AOL, Verizon, Yahoo, which may or may not have had overlap with you. But, yeah, it was for me, they there was, like, creative freedom, but it was definitely, like, you can be creative until like, toward the end of the process, and then it's like, I hope you had fun because we're gonna shut that down, and we're gonna work on something, you know, something else that maybe is a little bit less fun. And which is almost more stressful because you get to work like, you get to take a project to 90%, and then they kinda scrap it. I just wasn't used to that way of working. It's like, oh, well, I guess that's that's how it's gonna be. So I I wanted a little bit more control and kinda stepped away. I was like, alright. I just need to be able to sail my own pirate ship, because even pirates, sometimes they're they're not hiring the way you wanna pirate. Yeah. If you if you wanna lament some of those, Yahoo things, I got it to a 100%. We had a a very successful marketing campaign that resulted in a patent, which is weird for marketing, and some other interesting developments. And I thought, wow. Okay. Based on the back of this, now it's time to start my own team. And, with the right group of folks, we could bang out a few of these every quarter, and wouldn't that be a good thing? And, it was tentatively called the Marketing Innovation Group, and I was so excited to take this remit that I thought I had earned and like run full speed with it, and that turned out not to be the case. For some reason, it was like, well, that was successful, but the analysis was for none of the reasons you listed. And after that, they never had a project like that again. It was like, all right. So I moved to product for a while with some other very crazy folks. Yeah. You get to meet different meet different teams. I was I was moving toward, like, the VR teams. We probably would have intersected at a certain point. Oh, goodness. That would have been a lot of fun. Yeah. Oh, yeah. Absolutely. But then I was where I was like, how far can I get down this road before whatever cool thing, you know, we dream up gets scrapped? So I I kinda stepped stepped away, but, I'm sorry. Well, so and, David, obviously so I shared some time, you know, with with the big companies like like Stu. You've been over a decade at your agency at Pixie Labs. I ran mine for a similar amount of time, and what I found or just in general, you know, agencies really have to stay at the bleeding edge of, you know, what's going on in terms of the tech stack, in terms of just everything from the the small, like, what's going on within the front end, you know, CSS and frameworks all the way up through larger trends, and things that you can pull into your process. Because you're not always like, the spec that you're giving from a given from a a customer or a client or whatever it is isn't. It's like the the goal, but how you get there and how you really push it is on the agency. And I'm curious. How are you making sure that your team stays on top of, you know, the creative and tech, staying ahead of the game and just leading, you know, leading that side of things. Yeah. Any any specific I mean, like, as an agency, right, you're optimizing for being billable and kinda remaining ahead of the curve and doing experimentation and things like that can really kinda run counsel to that. It's funny, like, you actually mentioned, Stu, about sort of, like, you know, having a corner of the company that had won the the confidence of execs and and things like that. Like, you almost kinda have to do a similar thing with time, right, and enshrine a a a portion of time where you, you know, for us at Pixielabs, it's like a day a month where we've sort of set aside as non client time, which is not a lot of time to really get a lot done. And, naturally, I'd much rather it would it was a bit more. But, like, having some time that is a little bit enshrined that you're like, this is the day when everyone feels okay with putting down the client work that they're on, you know, and which can be tough with deadlines and things like that and and try and, you know, use that time for a bit of experimentation. But, also, I think, like, there are some areas you can be flexible in an agency on projects. So I kinda try and look for opportunities project by project. Right? And sometimes it can be a bit of an exercise of bargaining with a client as well. So if you've got something where the budget's a little bit low, or maybe it's a for good thing and you're being asked to kind of, like, help, you know, help out something that is more charitable or something like that, maybe maybe that client might be a bit more receptive to you actually doing a bit of experiment you know, experimenting on that project. Right? Trying something that's a bit unfamiliar and just being open with that and saying like, hey. Maybe we're gonna try something a bit new here or work with something that's a bit more unfamiliar to us or, like, we have this idea that we don't know if it's gonna work, but we wanna try it and, you know, depending on the parameters of the project. I think that that there's an opportunity then. Also having, like, good relationships with clients, especially long standing relationships can help with that, especially if you're you know, one of the things that you do in an agency is, like, how can we get more out of a client? Right? Like, how could an existing client, how can we unlock more more spend, more investment in us? And having some ideas, having some things that are experiments, and I'm kinda trying to sell that as an as a kind of joint experiment. Right? And being like, well, let's experiment with this thing together that we see there's a potential for your business, and, like, we can help that make that happen, I think is also is also a way to do it. And I think thinking about the engineering team as well, just in general, like, you do have to make sure that the experiments kinda in some way, I think, align with, like, our goals and vision. Right? Like, if we're, like, we're primarily doing stuff sort of web and mobile. And so, you know, I don't know if I would be comfortable with it with one of my team, like, going wild and doing some experimentation with something hardware based or electronics based or or something like that. Like like, maybe, but I think that that feels a little bit too off piste, and a little bit of a challenge to justify. And instead, it's like, okay. Like, if there's something that's slightly out of our lane, but not massively out of our lane necessarily, unless that was something that we were like, as a business, we also wanted to move into. Right? There are definitely things, you know, that we're seeing on the horizon, like, that you you're sort of alluding to, that are like things that we're like, we do wanna try and move into that area. So those opportunities then maybe are a little bit more left field that then the team can kind of like explore a bit more as well. But, like, having a little bit of alignment, I think, is useful, especially in a small team and especially where you don't have a lot of time to do it. You don't wanna go too off piste. What do you think like, in terms of some of those things that might be on that horizon? I'm just curious. Trends. Yeah. Like, one of the one of the things that I'm increasingly thinking about is about Apple moving into the AR and VR space with VisionPRO and thinking about how big of a deal that is for that kind of side of things compared to even I remember when I was at news and we were doing stuff to do with augmented reality and scanning QR codes and putting 3 d models on bus on the sides of bus stops and things like bus shelters and things like that. And, like, nothing has really moved along for a very long time in my opinion. Like, it's sort of you know, there's not sort of been like a huge like, it was novel then. It feels a bit novel now still. But I wonder about, I pay a lot of attention to the fact that Apple are really moving into that space. And so that is something that as a business, we're like, that feels quite out of our lane compared to web and mobile, like, even though it is sort of adjacent. And so that is something that strategically I'm like, there's some there's some thinking to do here. Yeah. Oh, for sure. Yeah. I like it. I like that. And so it's interesting. So you're saying almost like you're you've got this I think, like, Google also had a lot of companies have had this, like, idea of, like, white space where 20% of your time or whatever they can afford, is given to their their teams to say, like, hey. Just build. Like, come up with the cool stuff. It doesn't it should be generally on task, but, you know, more proof of concept. But you kinda scope that to the projects, and you kinda budget it in and said, hopefully, if if there a client is looking for something a little bit more innovative, it's like, well, we're we're gonna kinda go off and do some like, a little r and d sprint as part of the budgeted time, or how did you structure that? Yeah. That's yeah. That's we're kinda talking about 2 different 2 different approaches there. Right? On on one hand, you've got the the, like, time that is for us specifically that we're kind of enshrining as non client time. But then on client projects, it's really about, trying sort of keeping keeping things in the back of your mind of, like, things that you'd like to do or things that feel interesting or worth exploring and trying to find opportunities to to kind of worm them into a project or trying to, like, sell them into to an idea that a client has or a problem that a client has and being like, oh, like, we actually think that that this, like, new thing that we've that we've seen that we wanna do a bit of experimenting with, you know, it's you've got a you know, finding something small that we can that we can, like, experiment with something on. And I'm thinking specifically there about new tools or new frameworks or things like that rather than anything too major, just sort of like really about kind of staying at the edge of, you know, the tools using the right tools and using the best tools, to to get the job done. Right? Yeah. I like I like that. So I guess that kind of is is pretty interesting. That kinda segues in. I would almost throw it back to to either of you, I guess, at this point. But, on that the tech side, you know, in terms of the stack, are there specific tools or platforms, that have been, like, pretty pretty crucial in building out the different projects that, either of your you know, you or your teams have been have been pushing? Specifically through the lens of, like, this intersection of creative and technology. Obviously, there's plenty to to go through in the tech stack, but I think this is a more nuanced space. Well, of course, Unity. I mean, I I do agree with Unity. Just, you know yeah. I'll leave it there. I won't pitch for my own company too much or the company I'm a part of. But so other than that, I would say, 3. Js. My goodness, Ricardo and team over the years, that was really the real entryway for me because I started out as a web guy. I started to mess around with 3 d, didn't understand it so well. And then after banging my head against 3. Js and relearning matrix multiplication and all that good stuff, it's it's paid dividends as, the web has become more powerful, WebGL and now WebGPU, and WebAssembly, you can do a whole lot of stuff. David, you had mentioned the there being a gulf between AR, VR, and, and the web and mobile. But I found with things like like the quest, I could do it so much through the web. It was really exciting. And that was one of the things that I worked on at Amazon was, their Sumerian product, which was pretty interesting. To me, the most powerful thing, it was all in browser, sort of like Unity lite in browser. The most powerful thing was the blue publish button on the top. You can make any experiment you want. And then the moment that you were ready to share it with the world, you hit that button, and it takes a few seconds, maybe like 10 seconds. And in that 10 seconds, it is taking your project and replicating it on all of Amazon's servers with all of its 9.9 whatever uptime, and you can immediately share that URL out to anyone. And I thought that was so cool. 9.9 uptime is not that great, Stu. Well, 99 whatever. Yeah. They do the they do the best replacement. Yeah. Yeah. But I mean, 3 JS, I mean, it's crazy. I mean, I guess similar to Directus, you know, what I've been working on, it's been around for a long time, but its relevance kind of can still be in the future. Like, it can become more relevant over time depending on I mean, 3 JS has just been, like, just such an amazing thing to not just follow along, but just to use, like, just browsing through the demo pages and all that and just being like, oh, I could kind of fork this idea and just run with it. It's such an amazing platform. I think there's all and then obviously being it's open source. I think it's MIT, obviously. Oh, yeah. Yeah. Yeah. Like, I think that's a big any open source tooling, is is a big win in my book. I've contributed tiny like, a tiny, tiny bit. For 3. Js? Yeah. Oh, wow. It's it's not really worth mentioning, because it's so small in the scheme of things. There are real contributors. You're doing it anyway. It's like, okay. Yes. I gave back a tiny bit to this tool that has helped me. We'll we'll call it a micro maintainer. There we there we go. Yeah. I like it. How about how about you, David? I think, like yeah. It's interesting. I I had some notes about thinking about tooling of things like 3 JS and and stuff in kind of Webexa land. Like, I think for for things that are around, like creative and bringing creative ideas to life, I try and think of, like, what are the tools that are like one person tools, right, where you can go from nothing to done. Like, you were just describing right where you're talking about, like, building, you know, working on an experiment and then publishing it, right. And you've got all of that tooling in place, that means that you as one person can go from idea to execution to deployment and, like, live and and actually in in the real world. Right? Like, it's boring, but we use, Ruby on Rails, right, as a as a web framework for for most of the stuff that we do, all of the web apps that we're doing. And that has the same kind of idea. Like, in fact, the the founder, David Heinemann Hansen, was saying this at the conference the other week of, like, it's like a one person framework. Right? It's like one person can do the front end, the back end, the deployment, the maintenance. Like, the tooling is designed to be used by one person and actually make, like, make a lift as one person rather than having something that is inherently complex or has multiple moving parts or you need to add more moving parts to it to bring it to life. Right? So that if you're sort of building something yourself from scratch in, I don't know, some some JavaScript framework where like, some back end JavaScript framework where you've gotta also work out the hosting and, you know, you've gotta decide, is it is it is it microservices based? Is it am I gonna put it in a cluster somewhere? Am I gonna you know, how do I how do I get this live? What do I need to think about? Like, those things start to turn into, like, things that you naturally split up into multiple people or you need, you need a person just keeping an eye on it every day and that kind of thing. And and so I really like, like, that's the kind of thing that I that I think about, of, like, what are what are the tools that are, you know, that are that are good for, like, one person that that cover everything from from from start to finish. Well, I feel like here's where we could also segue into direct us, which is I did have that I did have that written down that, like, you know, directors is kind of a bit of a one person tool, right, in the sense that it there we go. In the sense that it wait. Where can I get one of them? I haven't I don't have one of those on my desk. What is it? Hang on. I always have, like, a 1,000 of them printed, like, today, so we'll we'll touch base. Great. Yeah. Like, in the same way. Right? In in in the you know, it's like database. You've got APIs. Like, it's giving you all of the it's giving you the front end layer to work in. It's giving you the API layer to work in. There's a director's cloud hosted environment. Right? Again, like it is. It's it does feel like that kind of thing of, like, tool tool of tool of 1 team you know, 1 person teams kind of thing. Yeah. And I think, like and then, otherwise, I was thinking a little bit about, like, what tools do I use just to, like on the very creative end when I'm thinking about just, like, coming up with ideas? And I have to plug I have a special place in my heart for Excalidraw. Do either of you use Excalidraw for, like, for, like, putting together little diagrams of stuff? No. The comics at the end of, of Yeah. But it's it's I don't know what it is. It's something it's so aesthetically pleasing to use. You you open up Excalidraw, and it's like a blank it's like the classic, like, infinite whiteboard, infinite canvas type tool, but, like, everything looks like you've scribbled it. And there's and it has just the right amount of shapes and colors and lines and sizes that everything you make just looks I don't know. I look over my old Excalidraw drawings, and I'm like, these are all so nice. They all just it just makes everything look it's like just it's like just loose enough. It gets kind of reminds me of, like, balsamic mock ups, but I always felt like balsamic mock ups, the aesthetic of that never really kind of vibed with me. I think that is too Comic Sans of of mock ups, but something about Excalidraw, I just might Yeah. We we use that a lot just for, like, quick, like, oh, let's just go in there. I think, hilariously because I I believe, if I'm remembering the tool the tool correctly, it gives you, like, a one of those random names, whenever you join until you, like, register and say this is your actual name. Right. And the first time I logged in, I'm in with, you know, 20, 30 people, on the direct score team, and it it assigned me Tremendous Beaver, was my my written name. And I I think that still floats around in our Discord server. So, but, yeah, I think it's that balance. I mean, right there is a balance of, like, looseness and, like, get things done. Right. Striking that balance is really difficult. It seems easy, like, just with with design. Like, design becomes transparent when it's done really well. I feel like a tool like that, just makes it seem effortless. Hopefully, that's what we're trying to do with Directus as well. And you you mentioned sort of it's a good tool for 1 engineer, and I think another aspect or facet of that is when you can make that one engineer, that one creative technologist, whatever it is, seem like 10. You know, it's not just effective. You can do it yourself, but giving them the abilities of, like, oh, I'm just, you know, just front end. It's like, oh, this kinda allows you to be full stack and kind of build, at a full stack level. There's some marketing stuff in there somewhere. Yeah. You're right about that. Is there, like, a good way to assess? I mean, you have emergent tech. You've got, you know, AI. You've got all these amazing things that are kind of flowing out. And how can you use them, incorporate them into your workflow, be more efficient. But, also, sometimes when you're building it into the stack, especially the stack of a client, that might you're gonna build it and sort of deliver it, and it's gonna exist and maybe you have to maintain it for months here, how do you ensure that the tech that might be a little newer is actually stable enough? You know, it's, obviously, it's out of beta. It's all that. But, like, how do you know that it's the right decision? Or do you just say, like, well, we'll we'll learn from this, and maybe we won't use it for the next project. Maybe we'll have to rip it out, you know, 6, 12 months later. But anything in terms of that evaluation process? I think you've kinda talked you kinda started to touch on it, really. I I think that it's all about experimentation, and it's about time boxing and making sure that you have some time to evaluate at the end or or to have some kind of retrospective either as an individual or as a team of, like, how do we feel it when using that new technology or experimenting with that new paradigm or new approach? Like, did we like it? Did it did it and, you know, upfront deciding, like, is this gonna speed us up? Is the plan that is gonna you know, it's gonna make us happier to do whatever it is we're doing? You know, and and really making sure to be critical of the of the the work that you've done and the process you've gone on and be like and be willing to discard it. Right? And be willing to go, no. That was shit. Let's never use that again. Right? It it it really, like, it really slowed us down, or it was really error prone. Or, you know, we learned things like kinds of things. I was trying to think of examples of that, and I was like, we tried to experiment building something largely using serverless functions, right, on on AWS with with Lambda functions. And we were like, let's never do that again. Like, that was horrendous. Like, it just slowed us down. It was like that we didn't see the benefits of the you know, that we that we were expecting to see. Let's let's let's not do that again. Like, making sure to do that, right, and have that process. And, yeah, I think there is that concern of, like, if you've baked something in and now, like, do you need to rip it out if if it's not great? But I have an element of pragmatism of, like, if something works, then it's prob like, you maybe don't need to worry too much about the thing that you've built if it's not working, like, especially on the engineering side as engineers. Engineers love things to be, like, perfect and and technically correct and engineeringly, like like, neat and tidy up. And I strongly push against that all the time. Like, as much as I can, I try and push against that because I think that it's too easy to fall down that hole versus just being like, well, what you know, is it really valuable for it to be perfect? Or, sure, it might be a little bit unmaintainable, but, you know, how long is it gonna last anyway? Like and is that thing gonna completely change in in 6 months or a year because the business has moved on, the idea has moved on, the product has moved on? Maybe you're gonna get an opportunity in 6 months to just throw it away anyway because the value the direction has shifted. So I wouldn't worry preemptively about kind of refactoring something just because you're worried that it's gonna be a bit less maintainable. Yeah. Yeah. I guess it depends if you're building whatever the tech stack is or the project at, like, the foundational level or more the facade, you know, that's gonna just like, a ephemeral, microsite or something like that, or product that's gonna be I I think it was also a piece of it is, like, even if you like it, you know, David or Stu or your teams are like, oh, this is amazing. You know, 3 JS, not a good example here, but, you know, the kind of the rug being pulled out from under you if it's like it stops being so popular, and something else kinda comes in. Like, oh, Sketch is the right meme. Sketch is the right tool, and then you go over to Figma. I was on I love Sketch, and then suddenly I was like, no Figma's Yeah. Absolutely where where the future is. And then there's been lots of projects where, like, an open source maintainer just disappears and then Mhmm. Love it, but it just isn't available or it's just not the right tool. Or you've got the the niece of the the client, kinda recommend some some tech tech, and they're like, oh, this is what we have to use. This is a requirement. I'm like, oh, well, why? That's not Mhmm. The right job. I mean, the thing with the thing with, like, stuff being abandoned or whatever, like and the and the risk of that is, like, well, like, how much were you sped up by using that thing in the first place, and how much are you now being slowed down by it being abandoned? Right? Like, you gotta you gotta look at the whole picture. And, like, you know, you you can't necessarily tell what's gonna what's gonna, like, be less supported or is not gonna be the the favorite going forwards. And it's like, you know, if it helps you at the time and at the time you evaluated it and you had a good evaluation process that you can kinda stand behind and you're like, yeah. We decided that that was the right thing at the time. And now in hindsight, it was a terrible choice. But also, like, we were sped up at the time, and we got the job done, and we delivered the project. Like, what, like, where do you wanna be? Like, do you want the thing or not? We could also stay at decision paralysis at the start of the project forever and just wait to see which thing comes out as the best. That is true. This is part of the reason I like to, comment my code so heavily to document those decisions poor, you know, whether they turn out to be better in the future. Also, because it may seem altruistic at first, but it's mostly for me, you know, 6 months down the road looking at your own code being like, why? Yeah. Why did I do this? And then to read like, oh, this, you know, this very minor edge case is gonna be There's nothing worse than, like, you go into your old code, and you're like, that's not the way that should be. Why did I do that? And then you, like, try to fix it. And then, like, 2 days later, you're like, oh, that's why I did that. Yeah. I can't oh, yeah. That makes sense. I always I'm always writing comments and encouraging the team to write a comment. You know, if you're just, like, hacking a fix in, but it's because you've got no time, add a comment being like, hey. Hey. I know this isn't great, but, like, I'm I'm under time pressure right now. Right? And just and just, like, being being nice to your past self and and your past team selves when you're looking at code and you're like, what the hell are they thinking? And it's like, well, maybe they were, like, under super client pressure at the time or, like, you know, didn't have the full picture of what was going on, and and now you do. And it's like like, that's the like, software is the the clue is in the start, but it's soft. Right? You can change it. It's Okay. If it's bad today, you can someday, if it's so bad, you can fix it. You can do something about it. Yeah. And you're not, I mean, you're not really intimating unless you're breaking things. Yeah. Right. You gotta just document those breaks, why those breaks are there intentionally and with nice fun emojis right there in the comments and the GIF. Yeah. So I'm curious, you know, when starting the creative process, you know, Stu, I'll I'll throw this over to you, and, you know, there's obviously Jed's other point. You have lots of things that, you know, back when we were, like, I like, living together and you were working through these really exciting projects, and now there's probably more enterprise y, large scale projects. But regardless, when you're starting a project like that from scratch, you're sort of in the initial ideation, you know, r and d Yeah. Phase? Like, how what does that look like for you, like, moving through that process from, you know, 0 plus? Yeah. Well, in some ways, I feel like such a dinosaur that I'm almost embarrassed to to talk about it. But, I'm I'm really into physicalizing ideas. And, obviously, I'm a tech guy. Right? I don't think we'd be having any of this discussion were it otherwise. But, and yet, pen on paper, Sharpie, just, you know, paper's recyclable. It's cheap. You can burn through ideas. The worse the scribble, maybe the better. And then being able to physically tape up or pin up that stuff so that your your memories are anchored physically around you, and you can refer to a cluster of ideas. And so I know it feels like most people don't work that way in 2023, and it feels like a very old, mammalian kind of, you know, I need to physically represent things in a certain time and place, but, yeah, that's that's how I'll start to formalize the very cloudy ideas in my head. And then from there, I'll start, you know, sketching in, maybe in Figma, if we're really honest, sometimes illustrator still, or directly in code. And, you know, code's so wonderfully flexible that, yeah. So, yeah, for me, it still begins with a a physical process. Interesting. Yes. I like, I've seen a lot of that, like, working with companies like IDEO. They're big on, like, sticky notes. I remember back in, like, school so are you is it, like, printout paper, like, no lines, line paper, grid paper, field notes? I'm so curious. Like, paper is pretty tense about a space stationery. Sticking up to your walls. You know, back back when we lived together, ancient times and, and Yukon, really, I mean, I was all about those, Rhodia grid pads. You know? I needed the grid. Yeah. I'm sure you have some within reach. Probably. There's a lot. I have, like, literally just from, like, from those times and just I'm like, in my head, like, yes. That's exactly what I need. It's just pads, field notes, grid paper, like, just every sort of paper. Like, yes. Right tool for the right jobs. Like, sometimes you need a grid, sometimes you don't. It's like an home and joy. Yeah. I I started to migrate, around I guess when I when I went back to school, I realized I started getting all of these notebooks that were just, blank paper, just blank, no grid. Yeah. Just no rules. No no constraints. Oh, there must be speaking of, like, the design the intersection of design development, sorry, Code and creative. I feel like there's the intersection of, like, sticky notes in a in a notepad, like, in a, like, spiral notebook. There has to be I mean, I guess maybe it is just a sticky pad, but something a little bit more formalized. So it's like a book, but you can just pull it out and it's got the sticky Mhmm. Right there on it. We'll we'll get there. I like that. So, David, any thoughts on that? Like, when you're starting starting your process, anything specific beyond sort of taking into the world with with stickiness? Yeah. I was thinking actually about that that thing where you said, like, the worse the sketch, the better. It reminded me of, you familiar with the process shape up from, from base camp and 37 signals. They have like a whole dot. There's a, if you Google it, you'll find it. And they, they talk about thick marker wireframes, Right? So using, like, a really thick pen to draw a wireframe so that no one can get bogged down in the detail of, like, how you know, is this thing this size or that size? Is it adjacent to that thing or above it or or to the left of it? Like because it's like the you've used such a thick marker that you can't tell the detail. You can just kinda get an idea of the rough shape, is like a that's what it made me think of when you were like, the worse the sketch, the better. I'm like, yeah. I definitely see the value in this. Engineer that. So working on some very early was yeah. Wow. This was web based VR stuff in, Creative Lab around, like, 2015, I guess, maybe 2016. So, we were kicking around some ideas, potential product tie ins, things like well, I won't mention what we were working on exactly, but it involved us making some sketches of things in VR and these were early prototypal things that we were going to use to excite the people with the money in the company and then get the budget so that we could outsource and bring in you know, modelers and and, you know, hardcore VR engineers, and then we could start to build the thing. But the problem was making these prototypes. They looked terrible. You know, they're mostly, you know, low poly things that we could make quickly And that was lost on a lot of our higher ups and executives. They'd put on the headset and they'd be, why is it so terrible? And we kept trying to say, this is a sketch. Don't you understand that that looseness and terrible, you know, the slow it looks like, you know, lawnmower man because we're not spending money or too much time on this. And no matter how many words we tried to use to verbalize it, it didn't come across as a sketch. And this was, shortly after Google had acquired, Tilt Brush. And Tilt Brush did not have a scaling feature in it yet. It was early days, and we were making this cityscape and it looked terrible. So I had this idea, we need to do the napkin sketch for VR. So what if, just in my living room at home, I had my own VR rig. What if I could use Tilt Brush to hand draw the cityscape so it looks like a sketch? But you needed to be inside of it, and you couldn't do that yet. You could only draw as tall as you could reach. That was the, you know, the limitation of Tilt Brush. But now I knew these folks. They were part of the Google family, so I was able to just email them and say, hey, I need to scale up these drawings. How could I do this? And they were able to send me, you know, some Python code and and walk me through how their, Tilt Brush files were organized, how I could break them apart, you know, increase the size of all the vectors, which is pretty easy operation. It just wasn't in the interface yet. Put it back together and then open it. And so ultimately, that's what we did. The next walkthrough was literally a sketch of what we were trying to make. So it was really like the reverse pretending that it's still on the napkin. Yeah. I think that communicated a little better that this is a stand in for the real idea. And if you like this, give us the money, and we'll go do the real idea. Yeah. Yeah. I love that. First of all, Tilt Brush is amazing. I remember the same thing just in there just building. I think I feel like they had a scale feature or maybe not, actually. Yeah. Probably not. I think they know. They didn't or before They chose the app. It's never happened to you. Yeah. Oh, yeah. Now, I mean, by the time that app was deprecated, it was trivial to scale things up and down. Yeah. But in the moment, we had to I think it's important. Like, the idea of, like, a rough knock or, like, those prototypes where it's like you have to enforce the rough because if you go too far, I was guilty as, like, a perfectionist. I'm like, oh, I'm making these these mock ups or whatever it might look like in the really early just pitching things. And if you, polish it too much, it's it just seemed it comes off as like a design that they start nitpicking like, oh, how do we change that? Like, we're not even looking at that. We're looking at, like, broad strokes here. But the more detailed and accurate you make it, then they start focusing on different elements of it. So it almost helps to have, like, Tilt Brush. I'm just gonna swap this out there and just, like, see you know, just only comment on what you can see. And when you start adding and predifying it, becomes a very different conversation is is what I found. I also think it reminds me, like, as a as an engineer, like, I think about these things maybe with like, I draw a lot of technical comparisons. And I think about, like, when you're coming up with ideas and trying to, like, come up with a solution to a problem or or come up with an idea, I think about it as, like, not locally optimizing down one path. Right? And it's, like, going to if you go too detailed too early, I sort of think about it as, like, I don't know if you're familiar with this sort of, like, approaches for for for sort of, like, machine learning or finding finding optimum solutions to problems of, like, the difference between, like, a hill climb algorithm versus simulated annealing. Right? You may be familiar with these. Like, the idea of a hill climb is like if you're sort of, like, searching through a problem space, you just go to the next best problem the next best, like, answer and then the next best answer and the next best answer. And eventually, you get to the top of where of the answer, and you're like, that's the best one. But what you didn't know was that the other side of that hill, there was another much bigger hill that was like an even better solution that was just totally different. But you couldn't get there because you were just, like, going down the better and better path and you weren't, like, exploring other options. And then simulated annealing is the idea where of, like, when you start trying to find the solution, you have an element of randomness where you're always just like, you're sort of you know what the best you found so far is, but you then also randomly just kinda, like, go wildly off piste and try something completely different and go, is that better? And then you're like, no, no, never mind. This is actually the better thing. And then sometimes you find those completely different solutions, and you go and explore. So yeah, right. Very similar. Very similar. Yeah. You're also describing I'm not going to do this justice, but the difference between gate based quantum computing and annealing Oh, really? D Wave does. Yeah. And their, gate based is winning for sure, but, there's some pretty good arguments, I think, for quantum annealing still. Right. But it's the same, I won't derail this further, but it's the same sort of math. So like, you know, I mentioned, like, having to learn to multiply matrices again and, like, you know, reteaching myself linear algebra for 3 d stuff. And, you know, all that computation is best handled by a GPU, you know, massively parallel small computations. And then you start to realize it's actually pretty similar similar for, artificial intelligence and machine learning, you know, model training. And then it turns out, again, it's the same old story for quantum simulation. So, yeah, that's how that's how I bounce through those things. But I'll Yeah. Yeah. I'm derailing. So What's more what's more exciting than quantum I mean, I don't I know nothing of quantum computing, but what could be more exciting than than going off the rails there? I once, I once met someone who was at a at a wedding who's, like, doing research in quantum computing, and I was like, oh, this will be interesting to talk about. Like, I I'm a you know, I'm an engineer. I know a little bit about this kind of stuff maybe. And And I was like, so tell me a little bit about what you're working on. And I think I got a best one in 10 words that he said. I was like, I have not like, you have just immediately lost me. Because he was in some kind of, like, world to do with, like it was something to do with about how you measure the output, and there was some very specific detail of what he was looking at. And I obviously cannot remember it. I did not understand it at the time. It was fascinating to just hear someone talk about it and realize that I'm like, I I'm getting 1% of this. Yeah. But if if you think it's an exciting topic, I have a recommendation, which is, this guy who was at Microsoft at the time, Andrew Hellwer, I think is his name. I think it's h e l w e r. He gives this lecture that you can find on YouTube, and it's named after clearly his favorite book on the subject, which is a great book. It's called Quantum Computing for Computer Scientists, And, I've forced myself through the book. His lecture is so much more to the point and beautiful in its simplicity. He throws away all of the bad metaphors about, you know, cats in boxes and, you know, pizza bagels. Is it a pizza? Is it a bagel? It throws that away, and it gives you the very basic math, from a computer scientist perspective. And from there, that's that was my entryway because I was trying to figure out, like, what is the super cool quantum, quantum computing, quantum physics, whatever superposition? And because of him, I can now give some pretty concrete answers about what's a cubit and how does it work, at least from a mathematical perspective. The actual physics of this stuff is Of how that stuff works is, yeah. It's I feel like I'm in much easier to get the Schrodinger side than the math side. I'm a visual No. No. No. A a cubit is just two values. That's it. It's just a pair of numbers, and then you can build from there. That's it. Yeah. It's just a pair of numbers. It's only that easy. We're just period and then end of end of the whole lecture. Yeah. So to kinda, like, bridge quantum computing, which is I'm not gonna say inaccessible until you listen to that lecture and you really understand all about it. So, like, AI and something that is, for a lot of people, you know, very inaccessible in terms of really understanding how it's all happening behind the scenes. But there is sort of a resurgence or not resurgence, but a continued resurgence of, you know, AR and VR and AI, all these things kinda, like, coming to a to a front. I'm curious, what do you think so specifically on the on the ARVR side because I think there's probably more experience there. Stu or David, any thoughts on how that's gonna change the landscape in terms of, building online experiences? I mean, I just I That's definitely one for handle, you know, like, can't stand those people with community on its name. You know, from from professional to prosumer to consumer and then just keep working its way down the tree. I'm curious what your thoughts are on, like, what do what do think the the issues might be, the challenges, the opportunities, the cool stuff that might start flowing and maybe when, in the ARVR space? Well, I I think it'll take me a moment to process what I can talk about and what I can't talk about. So I'll I'll start with a story from the the pandemic, or, well, let's say lockdown. It's still a pandemic, right? We're still anyway, in fact, I'm almost officially, one line again on the COVID test Just a Yeah. That was my past week. So we were we were working on something at Amazon, a side project that had to do with warehouse spaces and developing a sort of kiosk that would be for internal use for, you know, a specific task. And we're all working from home. We're all trying to figure out how to do that and also work on projects together. And, to develop this kiosk, I really what I really needed was to have, like, a bunch of different TV screens and things delivered to my home and to have someone prototype these things together. And then I could say, like, you know, this is too big, too small, in the wrong place, whatever, and that just wasn't happening. We just weren't at that point as a society to make that possible, nor do I really have warehouse space in my small Brooklyn apartment. But what I could do was very quickly mock up all of these things, using Amazon Sumerian, put on the headset, and walk around in that space and and get a sense, you know, style, in relation to the size of my own body and feel like, oh, okay. So if this is proper scale, this one feels right. This one doesn't. You know, this is somewhere in between. And it's an immediate gut reaction, and it cuts out all of the guesswork just being able to be virtually in that space, without having to pay for any of the hardware, have it shipped, have it set up. So that that may be a safe way to start to tiptoe into answers here to that question, hopefully. Yeah. No. I mean, a 100%. I mean, I've it's it's interesting just the spatial side of it because you're you know, obviously, it's immersive, but that means, you know, we are directional as a as a animal. You know, we see this way, but it's you know, there's things going on behind you, and so it's just a whole different landscape. You know, what you what you kinda mentioned, I think, kinda ties into the fact that, you know, it's it's just a completely different ballgame. It'll be a different mindset for how you develop and build in that space. How do you hint to somebody that, like, you're supposed to be looking behind you? So, like, maybe maybe turn around and go look at that call to action or whatever whatever that might look like. Yeah. Oh, for that, I mean, shout out to, former colleague, Jess Brillhart, who's a big name at Creative Lab and now continues to be a big name on the West Coast. She gave the keynote at South by Southwest a while ago for immersive experiences. She's a filmmaker by education, and has a lot to say about directing attention and focus in a 3 60 degree medium, and I say 360 because at the time, it was really 3 60 film. So it really it really was like rails in a circle around you, and, of course, immersive is, you know, that on all axes. Yeah. The 360 day, that was the project that I was working on at, that large company that got 90% of the way there and then got scrapped because of video content being lacking. So ARVR is pretty exciting because the content doesn't lack. You just create it. It's a little bit more generative, so that's, a different different space. But, so we're coming up on an hour. There's a a bunch of questions that came in, but I don't know if we're gonna have time for those. But, yeah, any any what's that? Oh, no. I feel I feel bad. We should get to Well, I some of them. Let's throw we could throw one in because I think it kind of brings us back to that sort of the intersection space, which the question was, do you have any strategies around fostering collaboration culture between designers and developers? And I'll just quickly like, I this is a big thing for me at Ranger, my my old agency, because, I I was both. So I was, you know, I was the intersection, and I just tried to hire people that were the same, you know, that had the same sort of, like, training or or background skills, that I had. And And so I was able to avoid that altogether. You know? And I kinda even pitched that in meetings. I was like, oh, there's the Harvard, like, oh, we're gonna have the seating chart. You know, and we're gonna have our designers here and developers there, and here's how they work in pods and all that. And I was like or you could just avoid the communication altogether and just make them all, you know, it's kind of pre creative technologies, you know, as you turn. Any thoughts on how you can foster that? Like, if you have teams of, like, strictly developers and strictly designers, how you can, marry the 2 of those for either of you? I've been thinking about this a lot at the moment. Because as an as an agency, we don't have any designers at the moment on us on staff. So we work a lot with third party designers or design teams or freelancers. And so I've been thinking about this a lot because we've it's always been a challenge. We've never quite hit on the right process, and we still haven't hit on the right process. But some of the things that I think about is, like, I think the best devs are the ones that know a little bit of design or, like, good UX and goods, like, like, understand what, you know, some of the, like, just kind of, like, basic underlying principles about what's good and bad design. And the best designers are the ones that understand a little bit about what are the limitations of the medium that I'm delivering into. Right? My worst experiences of working with designers are people who are print designers by trade and haven't really adapted to the web and that but are just doing some web design stuff as well. And it's like that is just such a different world that, like, it it doesn't align. Similarly, I've worked with devs or or or, like, helped look after devs who don't who just don't intrinsically understand anything about good or bad UX. And so as a result, we'll, like, implement designs badly or, like, not even put the right kind of boilerplate and skeleton in place to kinda unlock further development on top. And then, otherwise, I think, like, just a lot of side by side work. Right? Like, just making sure that things are iterative, making sure that it's about, like, responding to challenges, especially when it comes down to kind of delivery. The worst projects are the ones where delivery of designs is a cutoff point. Okay. Now start the designs. Now start the implementation. Sorry. And the best ones are the ones where, like, every small stumbling block of, like, oh, this is actually really hard to lay out on the page, or this is actually really hard to bring to life. Like, but if you just change would it be like, if we just tweak this, it'll be way easier to do. And then, you know, having that kind of that process of, like, it's not about, like, criticizing the designs being wrong. It's about, like, work working within each other's limitations, the space of delivery, and the space of design. I think that that works best, which is maybe a maybe an obvious answer. Yeah. No. But an important one. I mean, again, like, not to get into methodologies, but, like, the waterfall, you know, especially if the designers are just not aware of what they can or can't do can be really problematic. They have to progressively enhance, and they have to know the limitations. And if you only find that out once things are done and polished, you're you're in a bad bad spot. But Yeah. Stu, any any thoughts on that one? Well, I feel like I have a similar experience as you. So educationally, I think we're both taught print design, but, of course, with, that's not our trade at all. You know, we're in the code from an early age, so that sorted itself out. Still big love for good print design though. Yeah. For sure. But like you, I'm that intersection and I kind of, my present team excluded because I love my team, so I don't wanna give the impression that I don't. I think outside of my team, what I've experienced in the past is that, like, it's the 2020s. Why are these things still separate? They should all be me. That sounds very narcissistic or very arrogant. I mean, like, I accidentally somehow had these two influences and I'd like to think it was special. That should no longer be special. It's the kids coming up today really ought to have a foot in both worlds and may specialize in 1 or the other, but it's just so weird to me to meet young folks who don't, who seem to be firmly in one camp or the other because it just seems like evolutionarily, that that's just how it should be today. Yeah. And you can actually see When I would hire people at Ranger, I was I used to talk about, like, this t shaped tire. You know, you've got, a deep dive in their area of specialty, but then a crossbar of understanding so they can communicate with the other teams. You know? I'm a I'm a friend developer, but I can talk to the designers in the back end database. But Yeah. Now I feel like I need to update. Maybe it's like a capital m, and it's like you got a couple deep dives, a little bit more, you know, jack or jill of all trades. You you understand this and that. You're kept but you're a little bit more sprawling with the adjacent sides of you know, print design can be super relevant in how you do digital design, which can be really key in how you do UX and UI. And, you know, I think it all is really just makes a more robust, experience in person, when they're a little bit more multifaceted. But yeah. I just wanna reiterate again, love my team. It's just it's in the back of my head because we were our team was having this discussion recently. And so, you know, shout out to Yeah. That's a huge one. I I completely agree. I I watch sci fi movies all the time. I'm like, man, this is I watched the old sci fi movies. Like, they're saying, like, by 2020, we're gonna be in a very different place than where we are actually. I saw one last night with a creator, which was very timely with, you know, the AI stuff, and it was and it had some amazing not print design, but sort of that feeling to it. So I think, yeah, there's lots of ways all this can get pulled together. I but I think it all comes down to that intersection. Whether intersection is inside of one person, you know, they're they're both as, you know, I think the 3 of us are, or you can foster and bring together multiple people across a team and make that team sort of the the whole of of this idea of that intersection. I as I mentioned, I love metaphor, and I'll leave I'll leave us with, a story from when Stu and I used to live together. So we, you know, probably riding around the kitchen with on a unicycle or something. But, at one point, we had a a microwave, and it was, you know, just a throwaway junkie microwave we found. We're like, well, we're not gonna use it. We're just gonna trash it. And so we're like, well, what can we microwaves, you know, as as as younger people do? And so we we put, like, soap in there, and, like, soap was doing cool stuff and all that. And then we figured out, the the, you know, the ultimate test was this grape. And we just cut a grape in half, and you sort of leave the two halves of the grape just slightly connected. And it essentially the sugars or whatever it is, it forms plasma, and this ball of plasma rose to the top of the microwave and literally was melting the metal. But it just came to me while we were talking, and I was thinking back through all those those sort of things that happened. I was like, that's the intersection of design development. You have just, you know, 2 things to these 2 halves. This could be very different, but just you have that connection, and then suddenly you've got this plasma ball that's burning right through the top. You're innovating to the point where you're breaking things, and that's what we're all trying to do. So And then your microwave explodes. And then the microwave explodes. And then it was I burned up the the I burned up the Yeah. I had the cord, you know, just ready to pull out anything whenever At least we had yeah. We had the the fire extinguisher and the cord ready to get pulled, but, no, it was awesome. And I'm just trying to work out where that was going as you were saying that. I was like, where is this where where is this coming back You just We made it again. Awesome. Well, a little over time, but I this was super like, just I love just meandering through these these different topics, and and, I I think that's what it's what it's all about. How can you be creative if you had everything super structured? So, yeah, David, Stu, thank you so much for for hopping on and chatting through this. Hopefully, we can we can do it again sometime. This was this was awesome. Yeah. Make sure it happens. Cool. Alright. Thanks, guys.",[5537,5538,5539],"3a344ee7-f2f6-44d7-9cc2-4e5e9d78bf3d","3658ff72-9402-4151-9ba5-fa9025000027","e4a54717-cd97-427f-ac7a-4aab1adb0f1a",[],{"year":2380,"number":212,"id":2434,"episodes":5542,"show":5543},[2436,2437,2417],{"title":2439,"slug":2440},{"id":4793,"slug":5545,"vimeo_id":5546,"description":5547,"tile":5548,"length":13,"resources":12,"people":12,"episode_number":177,"published":5422,"title":5549,"video_transcript_html":5550,"video_transcript_text":5551,"content":12,"seo":12,"status":19,"episode_people":5552,"recommendations":5553,"season":5554},"linear","891433794","Development is hard. Estimating how long it takes based on shirt size is harder-er.","b10634c6-4ab8-4a44-9e0a-25c1fd5ec8ee","Linear","\u003Cp>Speaker 0: And now, DevThoughts by Jack Rabbit. Our team tried using Linear to manage our development tasks. Between cycles, I underestimated the number of points for a task I was assigned for the next Sprint. I forgot that 2 points was equal to one day, but then I was confused because 4 points equals 3 days. I tried to cool down by staring at the road map for a couple hours, but I came to realize that I just had a lot of issues.\u003C\u002Fp>","And now, DevThoughts by Jack Rabbit. Our team tried using Linear to manage our development tasks. Between cycles, I underestimated the number of points for a task I was assigned for the next Sprint. I forgot that 2 points was equal to one day, but then I was confused because 4 points equals 3 days. I tried to cool down by staring at the road map for a couple hours, but I came to realize that I just had a lot of issues.",[],[],{"year":2380,"number":159,"id":4790,"episodes":5555,"show":5556},[4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803,4804,4805,4779],{"title":3380,"slug":3381},{"id":4792,"slug":5558,"vimeo_id":5559,"description":5560,"tile":5561,"length":13,"resources":12,"people":12,"episode_number":13,"published":5422,"title":5562,"video_transcript_html":5563,"video_transcript_text":5564,"content":12,"seo":12,"status":19,"episode_people":5565,"recommendations":5566,"season":5567},"lazy-programmer","891432818","Some developers are efficient. Maybe a little too efficient.","61f390a2-9ce5-4a96-843e-2148f577697e","Lazy Programmer","\u003Cp>Speaker 0: And now Dev Thoughts by Jack Rabbit. They say a good programmer is a lazy one, but I'd like to think of us as efficient. Why spend 2 hours building your own portfolio website that's updated once per year in plain HTML and CSS when you can spend 3 weeks rewriting it in Next. Js in order to use incremental static regeneration at the edge to render all 3 of your blog posts. Now, that's efficiency.\u003C\u002Fp>","And now Dev Thoughts by Jack Rabbit. They say a good programmer is a lazy one, but I'd like to think of us as efficient. Why spend 2 hours building your own portfolio website that's updated once per year in plain HTML and CSS when you can spend 3 weeks rewriting it in Next. Js in order to use incremental static regeneration at the edge to render all 3 of your blog posts. Now, that's efficiency.",[],[],{"year":2380,"number":159,"id":4790,"episodes":5568,"show":5569},[4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803,4804,4805,4779],{"title":3380,"slug":3381},{"id":4682,"slug":5571,"vimeo_id":5572,"description":5573,"tile":5574,"length":2767,"resources":5575,"people":5579,"episode_number":13,"published":5422,"title":5584,"video_transcript_html":5585,"video_transcript_text":5586,"content":12,"seo":12,"status":19,"episode_people":5587,"recommendations":5590,"season":5591},"web-3-blockchain","893747977","Kevin is joined by Ben Greenberg to tackle his learning on topics around Web3, blockchain, and decentralized apps. ","0a994d04-3d4a-46bf-b181-7b34cf50ff00",[5576],{"name":5577,"url":5578},"Fuel","https:\u002F\u002Fwww.fuel.network\u002F",[5580,5583],{"name":5581,"url":5582},"Ben Greenberg","https:\u002F\u002Ftwitter.com\u002Fhummusonrails",{"name":2820,"url":2821},"Web3, Blockchain, and dApps with Ben","\u003Cp>Speaker 0: Hello. My name's Kevin, and welcome to Learning Things I Love to Hate. The goal of this show is to go into situations with an open mind and learn more about the tech behind the hype. And in this episode, I'm joined by my dear friend, Ben Greenberg. Hello, Ben.\u003C\u002Fp>\u003Cp>Speaker 1: Hello, Kevin.\u003C\u002Fp>\u003Cp>Speaker 0: How are you doing today?\u003C\u002Fp>\u003Cp>Speaker 1: I am doing really well. How are you?\u003C\u002Fp>\u003Cp>Speaker 0: I'm fantastic for speaking to you. The topic of today is about web 3. There are so many realms of web 3, but because I don't know much about it at all, I thought let's start with a pretty generalist view. But before we jump into that, could you tell us a little bit about yourself, where you work, and just why why we're speaking about this topic.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Absolutely. So, Kevin, it is so great to be joining you on this. We've known each other for quite a while, and it's such a pleasure to be always in conversation with you. So I am the head of DevRel at a company called Fuel, Fuel Labs.\u003C\u002Fp>\u003Cp>We are building the fastest execution layer on Ethereum, and we offer a really developer centric, platform with a set of developer tools focused primarily on the ability to build decentralized applications, dApps, as one might wanna call them, in the blockchain space and to be offering, really performant and intuitive and developer facing, developer forward, Outlook for developers to build, whatever they're looking to build and to get going in in the space.\u003C\u002Fp>\u003Cp>Speaker 0: So I don't understand some of those words. I don't know what an execution layer is. I'm sure we'll talk about that in a bit. But given that I don't know much about web 3, and I imagine quite a lot of the people watching this might be joining me, I thought we should start with this primer. And I thought a good way to start this is just a a real, like, candid point of just sharing some of my skepticisms that maybe we can address throughout this period we're chatting.\u003C\u002Fp>\u003Cp>If If we don't get to all of them, that's cool. But just at kind of a high level, the idea, the promise of decentralization, I think, is promising. And I think many people viewing will think, yes. Of course, that's promising in a world where large companies control large swaths of our data. But it always seems rooted in some kind of cryptocurrency, and I can't, in my mind, kind of sever and understand the difference between web 3 and decentralization and cryptocurrencies or tokens or something rooted in an area of tech that often is associated with scams.\u003C\u002Fp>\u003Cp>So maybe we can talk a bit about that because I I imagine it isn't all just one big, you know, interchangeable blob. And, also, I just don't get it. I don't know how how better way to say it. Like, if we just zoom in and look at the tech and forget all of the, like, idealistic, you know, backing of of why they exist, I don't really understand the characteristics of a web 3 application or a a dApp, and why they're unique and interesting. So I figured we might start by talking a bit about that, and then, we can get into some codes and maybe you can show me a little bit around.\u003C\u002Fp>\u003Cp>Speaker 1: That sounds lovely. So where should we start, Kevin? You you you lead us on this journey.\u003C\u002Fp>\u003Cp>Speaker 0: I think I kinda have these 2 main skepticisms. 1 around, decentralization and blockchain and cryptocurrency and kind of maybe just not even addressing the skepticism necessarily directly first at least, but what are the differences between these? That would be good. And then maybe talking about then we can jump into more more of the, like, technical, talking about what are the characteristics, like, how does it work at a high level, and then maybe we can jump into a demo. Does that work?\u003C\u002Fp>\u003Cp>Speaker 1: That sounds perfectly fine. So first of all, just a bit of background on my, involvement in this space. So I only came into web 3 blockchain, working in it professionally about a year and a half ago or so, and I too was incredibly skeptical for a very long period of time. And I actually think I remain still a bit of a skeptic on a lot of it, and I think that perspective is actually incredibly valuable when you're working in any space, regardless of whether it's blockchain or microservice architecture or, you know, you're working on Kubernetes clusters. It doesn't really matter what it is as long as you kind of have and retain that element of healthy, constructive criticism and skepticism, I think that just benefits your work and sort of everything you're going forward on.\u003C\u002Fp>\u003Cp>And so I empathize with you and I share a lot of that same sense of unsureness or like, what the what the heck is this? So having said that, I think for me, one of the ways that I've synthesized understanding what we're doing here is my involvement back in, like, connected networking, pre web kind of goes back to the early nineties, and I used to run something back when I was a kid. I'm not that old, but I used to run something back when I was a kid, and I begged my parents back then to get me a few phone lines to make this possible, but it was called the bulletin board system at BBS. And I don't know if you remember or even heard of BBS's. Kevin, you're so young.\u003C\u002Fp>\u003Cp>You don't know these things.\u003C\u002Fp>\u003Cp>Speaker 0: I don't wanna age you. I don't wanna necessarily make\u003C\u002Fp>\u003Cp>Speaker 1: you feel too. Let's let's talk about BBS's for a minute. So BBS is where these, like, pre web places online where people could dial in and there were shared discussion forums. There were file servers. You could share data.\u003C\u002Fp>\u003Cp>It might take you on a, you know, 24 ks modem, you know, an entire day to download something. There were, you know, text based role playing games where you had 2 or 3 turns a day. And that was my introduction to, I guess, what one might call the online community was through BBS. I used to run run-in San Diego where I'm from, born and raised, called the Freethinker BBS. That was my BBS.\u003C\u002Fp>\u003Cp>I was that kind of kid back in the days. And, and then the web came. Right? And, and the web began in the kind of the same modality where it was kind of run by people in their homes or, you know, local. You would you would have a web server on your running on your 48680 megahertz PC, in your basement somewhere, and you'd be serving websites on an Apache web server, you know, on your 486.\u003C\u002Fp>\u003Cp>And then, of course, things dramatically shift. We don't go through the whole history of the web. But, we got to a point where we are now where everything is essentially running on AWS. AWS is running AWS in every other service you're probably ever going to access is somehow connected to AWS. And I think part of what the premise of what we're what we're working on in this space is how can we get back to a place where people own more of their data, where people own more of the, access their data and you have more familiarity and a sense of confidence and where things live.\u003C\u002Fp>\u003Cp>And there's more of a communal aspect to everything we're trying to do. And so I think that is like at writ large from a balcony perspective, kind of like the idea. That's the idea. And the way that it's it's run-in a blockchain context is so one of the first questions people always ask, and I think it's a really good question, is that so I'm accessing some blockchain application. Where the where is the data?\u003C\u002Fp>\u003Cp>Like, where is that actual data living? You know? So if I know if I go to, you know, x slash Twitter or I go to, you know, Instagram or I go to any of these places, I have a general idea from a web developer where the data is. But where is the data on any kind of decentralized application? And the honest answer is that data is everywhere.\u003C\u002Fp>\u003Cp>The data is replicated and duplicated on people's computers and on organizations around the world, at all times. So that spread of the data is, I think, the first thing that we want to tackle and address, and then we can get to other parts as well. But I think understanding that is a fundamental premise to understanding everything else that the data is not owned in any central server or any central node. It's actually spread across, multiple, potentially countless nodes. Obviously, not countless.\u003C\u002Fp>\u003Cp>Nothing is infinite. But yes. Sorry?\u003C\u002Fp>\u003Cp>Speaker 0: No. Could I pause you for a moment and go back a couple of sec? Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Definitely not. Sorry.\u003C\u002Fp>\u003Cp>Speaker 0: The end. It's a it's a it's a train. We ain't stopping. So okay.\u003C\u002Fp>\u003Cp>Speaker 1: So Okay. We'll take a break. We're at the trade station. Let me get it done.\u003C\u002Fp>\u003Cp>Speaker 0: So on that train of thought, and I wanna come back to this train of thought, I wanna talk about the how does it work, right, and what are these nodes, how does the decentralization work, how can you trust it and so on and so forth? Can we take one step back? And I'm kinda curious again. What is the relationship between decent and maybe maybe you're like, no. Actually, we need to tackle them this way around for it to make sense.\u003C\u002Fp>\u003Cp>But I'm curious. What is the relationship between decentralized apps, cryptocurrency slash token slash some kind of fiscal value item and blockchain. And you you might just be like, of course, it's obvious, but, like, I really need\u003C\u002Fp>\u003Cp>Speaker 1: to start thinking about it. Rule of anything is to never assume that anything is obvious. And I think that's like the first lesson we help teach people writing documentation, technical documentation. Right. Don't ever assume what is obvious to you is obvious to others.\u003C\u002Fp>\u003Cp>So to answer that question, I think you actually need to unpack a little bit more. And you need to you need to kind of further accentuate the issues. So considering that the the entire enterprise rests on the notion that people will choose to run these nodes, these data warehouses that replicate the data across the world. So assume people are going to do that. That's the assumption.\u003C\u002Fp>\u003Cp>Then you baked into that assumption has to be some kind of incentivization model for how one does that. And unless you operate on a fully altruistic model of being where I will voluntarily give my, I will voluntarily give my my electricity costs and my Internet costs and and my hardware costs, and I'm just gonna do this for the betterment of humanity, which I think you you and I, of course, operate in that level. Everyone else, I mean, we don't really know. Yeah. But, you know, obviously, we work for our companies entirely altruistically.\u003C\u002Fp>\u003Cp>Then you have to do some kind of incentivization model baked into it. So I think that's the first layer. And then you may say to yourself, okay, that sounds all well and good, Ben, But why can't that incentivization model be based on euros or pounds or dollars or Canadian dollars or pesos or any other form of currency? Why why does it have to be a digital kind of currency? And I think to answer that question, you have to then make that question larger.\u003C\u002Fp>\u003Cp>What is the value of any digital collectible or digital good that exists in any way, shape, and form from the gaming enterprises and beyond. So for example, I have a couple sons. One of them, he's, you know, fast becoming a teenager, is really into this game called Fortnite. I didn't know Fortnite existed until he got into it. That's how much, you know, you know, how how much of a dad I am.\u003C\u002Fp>\u003Cp>But he got he's really into Fortnite. And there's these, like, digital collectibles and things you can do in Fortnite that are really valuable to him. Why is that valuable? It itself is not, doesn't hold intrinsic value. I mean, we could also argue that a US dollar doesn't hold intrinsic value by itself.\u003C\u002Fp>\u003Cp>It's representation of value, but he finds value in it. So then I think the question becomes, if you're building this system and you want it to be digital first, so maybe you ought to create the incentivization model also grounded in a digital value and a digital collectible, a digital good. So then that's the second the second layer. And the third layer is, okay, Ben, what about cryptocurrencies? And to that layer, because then you're now extrapolating to, okay, now it's value transcends the incentivization model of of hardware storage and, you know, incentivizing me to run a node, etc.\u003C\u002Fp>\u003Cp>Now it's suddenly a competing currency in the in the world competing against, you know, global government backed currencies. And to that question, Kevin, I'm gonna say I don't want to get into that because I am not a cryptocurrency person. Like that's not what interests me, I would say, in this space. It's not what motivates me. And I do think and I will end this little bit with a little bit of a caveat, there is a bit of a privilege to not be a cryptocurrency enthusiast in this space.\u003C\u002Fp>\u003Cp>For example, when I travel to parts of the world where the governments are less stable and the currency is less reliable, there is a, there is a deference, to cryptocurrency that I am unfamiliar with as someone who has only lived in first world global north countries. And I am and I was actually a bit taken by surprise by that, and that just reveals sort of the the the places that I've spent most of my life living in. But, you know, the the there are places I've been where the the first question is asked to pay for something, what's your wallet address?\u003C\u002Fp>\u003Cp>Speaker 0: So\u003C\u002Fp>\u003Cp>Speaker 1: At a store.\u003C\u002Fp>\u003Cp>Speaker 0: Interesting. The I don't I don't necessarily wanna go down the, like, and this is why cryptocurrency is a valuable track, which is, like, where this this area is going. But alright. So there's one question I have that I can't quite unpack yet, which is okay. To have people participate and participate, my understanding is there's, like, various degrees to participation, in a decentralized network, I suppose.\u003C\u002Fp>\u003Cp>You might correct me on my terminology by decentralized network or whatever. There's some kind of incentive. And, okay, your son gets Fortnite.\u003C\u002Fp>\u003Cp>Speaker 1: Whatever it whatever he gets.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Sure. They don't have real world value. I mean, they possibly do. I think that is a whole marketplace, but whatever.\u003C\u002Fp>\u003Cp>Speaker 1: You know, I think in digital games in general, those things have quite a lot of value.\u003C\u002Fp>\u003Cp>Speaker 0: But let's assume let's assume they don't, and it's just I want I I want to give you something to participate. Actually, I'm going too far off the the end. Let me just ask the direct question. To to be involved in these networks, does it have to have be backed by some degree of, like, cryptocurrency token or something that converts into real money? For example, you were talking about I have sir a server in the basement, and it's running something, and that's great.\u003C\u002Fp>\u003Cp>And, you know, the web is weird, and we all host our own stuff. Now it's all hosted on, like, 20 data centers around the world, and now we'd like to go back to that. But, perhaps at that point in time, people were just hosting stuff altruistically. If I want people to participate in a decentralized thing that I build, why why is there a financial backing to or is there? Can can I just I'm gonna use some words perhaps ignorantly, stake some token for that has no real that isn't backed by anything that has real money.\u003C\u002Fp>\u003Cp>It's just a number of a thing that just exists. It just bites in the in the world, and that's that or does it always eventually get backed by one of these primitives that get traded?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think well, to there's a couple of different questions in that. But I think, first of all so there's, you know, obviously, I began this our conversation with a bit of nostalgia back to the BBS world. And the thing that I don't have nostalgia about in the BBS world is how far we've come and what one can accomplish with the modern web and the modern Internet infrastructure. And I don't think any of us I'm actually going to take that back.\u003C\u002Fp>\u003Cp>I don't. There are probably people out in the world who do, but I don't want to go back to a world of, you know, 48 ks, 24 ks modems. And I like the conveniences that are wrapped up into my phone into the ability to do the things that we do on on a daily basis. I happen to enjoy that. But that kind and I rely upon it.\u003C\u002Fp>\u003Cp>I more than enjoy upon I rely upon it. But that kind of sophisticated infrastructure requires a lot more than, you know, the the 486 80 megahertz p Intel PCs that, you know, that we used to run things on back in the day.\u003C\u002Fp>\u003Cp>Speaker 0: Oh, we. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. The we. The we. The we. And so I think the infrastructure needs are a lot greater than they were back then.\u003C\u002Fp>\u003Cp>And so even if it was the case that there was a that you could assume a level of altruism, today you have to you have to compensate people for their, for their costs. And then the second layer in that question is, well, what about a translation at some point to a real world value of the incentivization token and tokenization? Like, does it ever have to does it always have to translate back to a real world value? You know, I don't really know. And I'm going to defer to other people who probably will comment in the comments on this or offer their own perspectives.\u003C\u002Fp>\u003Cp>I don't really know if a this is a philosophical question. Right? Do if something is valuable inside a system, does it also have to be able to translate to value external from that is external to the system? And and what are the bridges that one needs to create to create that kind of synchronicity of value from inside a system to outside a system? You know, for I mean, I don't know about you, but I and I but I know for myself, I remember I'm a member of communities where there are value things there are things that are valuable that we do in those communities that are not valuable outside those communities, levels of, communal participation, levels of voluntary service, actions we do as part of, you know, social and ethnic and sub communities in the world that don't translate to value outside of themselves.\u003C\u002Fp>\u003Cp>But do I need to have bridges to show that value, externalize that value, and and translate the value to a larger the larger community, you know, that we live in, whether it's in in the United States or in Germany or anywhere. And, you know, that's, like, that's a philosophical question. Right? That's a larger question. I think in the case of of blockchain tokenization, the answer has been demonstratively yes because they have built those bridges to to convert the token that is used as an incentivization model to US dollar or to euro or to I\u003C\u002Fp>\u003Cp>Speaker 0: think what I'm just thinking is if I wanted to host it and once again, I I wanna, I want us to move on a little bit in a moment, but maybe just for one more, like, quick quick bit here. But, like, if I just wanted to host a thing for me and my mates and this idea of it's decentralized just among us just among us, this is all cool. You know, there's, like, you know, 20 of us. Maybe it's like a school hacker club, whatever. And money doesn't need to be moving hands here.\u003C\u002Fp>\u003Cp>We're happy to spend some resources and not be reimbursed for them. It feels like just doing that is intrinsically wrapped up in a cryptocurrency of some kind. Even if it's small value, even if it's, like, not very much at all, it always from what I'm understanding, you can tell me no, actually. I'm not sure that's true, and it just happens to be true in most cases. It seems like it always ends up back in the world of Ethereum or Bitcoin or insert other I don't only know those 2.\u003C\u002Fp>\u003Cp>I'm sure there are.\u003C\u002Fp>\u003Cp>Speaker 1: I think that I don't think that's true. I think that, if you don't require an incentivization model to to help, inspire people, motivate people, get them involved, then I think you can build your socialist utopia and and everyone can donate their resources as they as they choose and donate their time and their hardware and their costs. And that's entire and that works to some scale. It doesn't work. I don't think I don't think it's been proven to to work at a scale that can power something that is trans transnational that\u003C\u002Fp>\u003Cp>Speaker 0: Sure, sure, sure. But that's much bigger. I'm just talking about, like, in theory, the concepts I'm about to we're about\u003C\u002Fp>\u003Cp>Speaker 1: to talk about. Can create tokenless blockchain, of course. Yeah. A 100%.\u003C\u002Fp>\u003Cp>Speaker 0: I mean, there there was no of course. I had no idea. I always thought there is no world in which that would, like, technically works, but it is. It just why would you slash? It's not very common, you know, or whatever.\u003C\u002Fp>\u003Cp>It only works as\u003C\u002Fp>\u003Cp>Speaker 1: And and and, actually caveat. Fascinatingly, there are private companies that have you and we'll maybe we'll get to this in a little bit, but there are private companies that have built blockchain networks internally that are not, we would call, decentralized, but they're using the primitives of blockchain. They're using blockchain in order for things like provenance, tracking, inventory tracking, things like that. And they're not using it with token because it's all within their system. It's employees using the thing that the incentivization in the company is the is the salary and the stock options.\u003C\u002Fp>\u003Cp>Yeah. And they're using blockchain for the for the utility of inventory tracking, let's say.\u003C\u002Fp>\u003Cp>Speaker 0: So the utility is what I the utility and the kind of these primitives and the structure, I'd love to talk about it. We're we're bumping up on you know, we're starting we've done about\u003C\u002Fp>\u003Cp>Speaker 1: 20 minutes. We're bumping. We're talking. We're getting everything.\u003C\u002Fp>\u003Cp>Speaker 0: Lots, that of already some of my assumptions have been dismantled, which is good. You know, that's that's why I invited you here. I wanted to genuinely learn more. There is I'd love to move on to a demo soon. But before we do that, maybe in, like, 5\u003C\u002Fp>\u003Cp>Speaker 1: ish You like my big Stanley Cup, by the way? This is the new rage in the United States. Everyone has these huge\u003C\u002Fp>\u003Cp>Speaker 0: Stanley Cup now. Some of my friends got them. You know? Some people would call them quite basic.\u003C\u002Fp>\u003Cp>Speaker 1: 40 ounces.\u003C\u002Fp>\u003Cp>Speaker 0: But you know what they're gonna\u003C\u002Fp>\u003Cp>Speaker 1: do, though? This is 40 ounces of water.\u003C\u002Fp>\u003Cp>Speaker 0: I've seen Starbucks have just come out with, like, a Christmassy one. I'm gonna date this video and the the time we're recording.\u003C\u002Fp>\u003Cp>Speaker 1: Yes. I know. You just did that.\u003C\u002Fp>\u003Cp>Speaker 0: That's a bit of a problem. It's this this lovely red, like, you you know, matte red one.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's nice. Yeah. But they\u003C\u002Fp>\u003Cp>Speaker 0: only sell it in the US\u003C\u002Fp>\u003Cp>Speaker 1: as well. Because we Americans, we really hydrate quite a lot, I guess, opposed to the Europeans. What was that type\u003C\u002Fp>\u003Cp>Speaker 0: of trash? Ice.\u003C\u002Fp>\u003Cp>Speaker 1: Right? The Europeans don't like to drink water as much as Americans do, apparently. There was a whole social media trend there.\u003C\u002Fp>\u003Cp>Speaker 0: Looking at my soda here. So let's so let's move on. I would love to know just, like, at a high level, in about 5 minutes ish, I would love to, like I I wanna absorb as much as I can in the 5. I'd love you to, like, show me stuff. Show\u003C\u002Fp>\u003Cp>Speaker 1: me stuff.\u003C\u002Fp>\u003Cp>Speaker 0: Could you maybe talk about some of these primitives and the structure? And I understand that there's this idea of, like, not everyone has the same role within a decentralized network, and I'd love to talk about that how it works. We when we briefly prepped for this, we spoke very briefly about this thing called, like, a consensus mechanism. Don't know what it is. If you could share that, that would be awesome.\u003C\u002Fp>\u003Cp>Speaker 1: Absolutely. So cut me off at any point because, you know, as you know me very well, I can be long winded in my in\u003C\u002Fp>\u003Cp>Speaker 0: my life. The timer?\u003C\u002Fp>\u003Cp>Speaker 1: I do see the timer, but you're not you're not gonna pay attention to the timer. I'm actually\u003C\u002Fp>\u003Cp>Speaker 0: You'll ignore it. That's my job. Fine.\u003C\u002Fp>\u003Cp>Speaker 1: A timer like Kevin. Yes. So, essentially, we had already mentioned briefly that the way this, works at a basic level is that data is replicated across all these individual servers and, you know, people's computers around the world, and those are called nodes. Right. And the question, of course, has to arise.\u003C\u002Fp>\u003Cp>What happens when there is a dispute amongst the nodes? What happens when node 1 and node 33 say, no, the data is actually node 1 says one way and node 33 says the other way? And before we even get to there, what is the data? So we talk about blockchains. Right?\u003C\u002Fp>\u003Cp>What is a blockchain? It's a chain of blocks. What is a block?\u003C\u002Fp>\u003Cp>Speaker 0: Wow. Thanks.\u003C\u002Fp>\u003Cp>Speaker 1: I I know. Right? Was that was that definitionally circular? What is a hand? It is something that displays the characteristics of a hand.\u003C\u002Fp>\u003Cp>What is a blockchain? It is a chain that has blocks in it. I know. I'm like it's we're mind blowingly like astute and wise in this in this episode. But essentially what is a block?\u003C\u002Fp>\u003Cp>A block is a defined discrete piece of data that contains different elements of of of things important to the chain. So it's going to contain a cryptographic hash of the transaction used oftentimes, using Merkle Tree cryptography. So a Merkle ized hash, whatever that means, that's gonna have the previous block, the hash of the previous block, meaning the previous defined set of data and its current data. So that's and then that becomes like a hash that again can get proven cryptographically between the nodes that this hash is valid. It's gonna have some metadata at the top of the block sort of on the version of the chain, you know, versioning, you know, like we're all familiar with semantic versioning, versioning of the chain, other kind of pieces of metadata, and some other bits and bobs is going to be in the block.\u003C\u002Fp>\u003Cp>And so that block gets then disseminated across the nodes for consensus, and that's where we get to consensus mechanism. And consensus has to happen fast, efficiently, and also has to happen securely. And that's a complicated, dilemma. How do you affect fast oh, and don't forget, obviously, it goes without saying decentralized. Right?\u003C\u002Fp>\u003Cp>So how do you affect a decentralized and crazily fast and secure consensus across nodes around the world? Right. Because you you need to ensure these things. So that's where the mechanisms come into place. And there's different consensus mechanisms, and they you can kind of imagine them on a spectrum of decentralization and and and centralization, right, to try and to try and grapple with these things.\u003C\u002Fp>\u003Cp>The the most famous one that often people are aware of is the Bitcoin model, which is proof of work, a proof of work consensus. Proof of work consensus basically requires the servers to solve very complex mathematical operations in order to validate the transaction and then create that new block. That is the least centralized. It is the most decentralized because it doesn't designate or delegate any any node. It's just whoever finishes the mathematical operation first gets to be the node that offers the validation proof that again again gets disseminated across all the other nodes Oh.\u003C\u002Fp>\u003Cp>For validation. But\u003C\u002Fp>\u003Cp>Speaker 0: Sorry. And by doing it first and providing the validation proof, you get more of the insight.\u003C\u002Fp>\u003Cp>Speaker 1: You get the reward. You get the reward.\u003C\u002Fp>\u003Cp>Speaker 0: So you're trying to get the in that model,\u003C\u002Fp>\u003Cp>Speaker 1: you're basically There you understand the incentivization model there.\u003C\u002Fp>\u003Cp>Speaker 0: And you burn the planet in that way because it's really computationally expensive, but you get like, and that's that model. But that isn't that's not universal. Every No.\u003C\u002Fp>\u003Cp>Speaker 1: No. No. That's not universal. And I I by the way, I think to understand a lot of this, one also needs to understand game theory and, the whole pursuit of game theory of, like, how do you incentivize players in a game to complete compete? Where do you find the equilibrium of players?\u003C\u002Fp>\u003Cp>Like, what in game theory, we call that the Nash equilibrium. Like, what is the perfect moment in a game where all players are playing their optimal moves, that's called the Nash equilibrium game theory. And there's a there's really interesting like economics and game theory and cryptography, like, a lot of stuff that goes into this, but that's not for here.\u003C\u002Fp>\u003Cp>Speaker 0: Beyond the scope. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: That's for beyond the scope of this conversation. So that's called proof of work. The problem with proof of work is it's incredibly energy intensive, and it's destroying the world. Okay. So let's let's put that in a category.\u003C\u002Fp>\u003Cp>We don't wanna destroy the world. We kinda like this world. You know? It's got its problems, but it's a nice place to live. It's maybe our only place to live for the foreseeable future.\u003C\u002Fp>\u003Cp>We kinda wanna keep it going. I know Kevin, you got a couple kids. I have a couple kids who kinda want them to have a world to live on. It's a nice\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. You\u003C\u002Fp>\u003Cp>Speaker 1: know, it's fine. Maybe we wanted to go. Okay. Yeah. So then the the the other model that is very popular, is called proof of stake.\u003C\u002Fp>\u003Cp>And so as opposed to proof of stake, proof as opposed to proof of work, proof of stake is essentially that, the node operators stake their token to validate. And the ones that stake more, as collateral tend to get chosen more to be the validator, and then they get more stake and they get more back as a reward. There's a variation of this called delegated proof of stake where also delegators can participate in this consensus mechanism and can choose. So I'll tell you that people, other people who are playing in this game can choose a node operator and say, I'm going to stake as well on that node operator and I'm gonna get a portion of the reward and the operator will get a portion of the reward. And so people get to choose operators.\u003C\u002Fp>\u003Cp>So the benefit of this is it's less energy intensive by far, dramatically less energy intensive by scale, extraordinary scale. It is clearly a little less decentralized, because right? For obvious reasons. But the energy intensive nature of it, the the fact that it doesn't really destroy the planet any more than any other web application destroys the planet, is is a good thing. Like, let's say that's an unreserved good.\u003C\u002Fp>\u003Cp>It's a good. Right. And there's other models as well that are even less decentralized. There's something called proof of authority where basically the chain chooses, through its however the chain chooses things, and that's a whole another conversation. But the chain chooses approved approved nodes to be the validators.\u003C\u002Fp>\u003Cp>And those are unapproved like an approved list of node operators that can validate. That is clearly not super decentralized, but it is obviously also much more fast. And and you could argue more secure because you've you have a defined list of vetted\u003C\u002Fp>\u003Cp>Speaker 0: As as long as those nodes are kept secure. But, sure, you're right.\u003C\u002Fp>\u003Cp>Speaker 1: As long as nodes are kept secure.\u003C\u002Fp>\u003Cp>Speaker 0: I'm kept. In this in this world of it, it has to be energy efficient, decentralized, secure, fast. It's like the all of these various models\u003C\u002Fp>\u003Cp>Speaker 1: It's like a trifecta of a dilemma. Yes. Exactly.\u003C\u002Fp>\u003Cp>Speaker 0: So so all of these all of these, you know, do it different ways, and I'm sure you build your applications on different\u003C\u002Fp>\u003Cp>Speaker 1: And you also need to have an element of fault tolerance for bad actors as well. Sure. And we call that the Byzantine general's dilemma. And if you ever heard of the Byzantine general's lemma. It doesn't it's not from blockchain, but it applies to blockchain.\u003C\u002Fp>\u003Cp>It's the have you heard of this, Kevin? No. No. So very briefly because this is actually quite fascinating. Let's say you are that it comes from, army met army terminology.\u003C\u002Fp>\u003Cp>You are the Byzantine army, right, and you are surrounding a city and you're, you are proceeding to invade the city. The only way to tell all the generals that are surrounding this quite large metropolis that it's time to go, like lunchtime, is by sending messengers to all the generals around the place. And you don't get feedback from the messengers whether the generals have accepted or not. You just have to assume that all the generals will then activate their whatever they call them in armies, brigades. I don't know what whatever the thing is.\u003C\u002Fp>\u003Cp>So you have to assume that a certain percentage of your generals, either by malintent or by incompetence, will not activate their brigades. And maybe they're traitors. Maybe they're just bad at their jobs. We don't know. So you have to have an element of tolerance for ineptitude or for maliciousness.\u003C\u002Fp>\u003Cp>And all of these systems have to have what we call technically the BFT, the Byzantine Fault Tolerance.\u003C\u002Fp>\u003Cp>Speaker 0: The twins' exhausting, but I get it.\u003C\u002Fp>\u003Cp>Speaker 1: It's really really interesting. It's really interesting from, like, a nerd perspective.\u003C\u002Fp>\u003Cp>Speaker 0: So so just to help me understand, because I really wanna jump into, like, if you could if we can maybe build a little dApp, decentralized app together if you're open to that. But do that. But but just to help me understand, just at the core, there are a few players here. There are the developers of a chain, but they they will inevitably participate in it. But those two things are not are not a given.\u003C\u002Fp>\u003Cp>Then you're in the participation of a chain. You have validators, and and they are they might be on an approved list. It might be whatever.\u003C\u002Fp>\u003Cp>Speaker 1: You have to Depending on the mechanism the chain is.\u003C\u002Fp>\u003Cp>Speaker 0: Depending on the mechanism, they will validate and and drive consensus on what the\u003C\u002Fp>\u003Cp>Speaker 1: And send and send those validated blocks and they'll send those validated blocks to all the other nodes\u003C\u002Fp>\u003Cp>Speaker 0: to That was my last question. And then nodes are generic whether you validate this or not, and then you have what I may just call users, standard nodes, not\u003C\u002Fp>\u003Cp>Speaker 1: There are differentiated nodes, and, yeah, that's what you're, I think, intimating at right now.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. But but but there is there's\u003C\u002Fp>\u003Cp>Speaker 1: there's what we call user. There's what we call full nodes, which are the ones that can validate and affirm and do all the operations of a chain. Sure. Then there's archival nodes, which as the name sounds, are there to preserve the history of the chain. And then there's things that we call light nodes or light clients, which participate in some of the operations of the chain but don't validate.\u003C\u002Fp>\u003Cp>And those are really helpful when you have low low infrastructure, like if you wanna have a mobile app participate or you wanna have a wallet a mobile wallet participate. It would\u003C\u002Fp>\u003Cp>Speaker 0: just be like users. I'm a consumer.\u003C\u002Fp>\u003Cp>Speaker 1: Basically users, and they would query full nodes for more data, but they themselves don't possess the entire history of the chain, and they don't participate in the full validation of a chain. And there's other nodes as well, but those are some of the big ones.\u003C\u002Fp>\u003Cp>Speaker 0: That's really interesting. And then just my final question, just to make sure I got it. When I I'm a user. I have a I run a light node, which could be a client, right, a web app, whatever, and I want to gain data. I'm not going to a specific node for that data.\u003C\u002Fp>\u003Cp>No. I'm asking the the chain. There is a mechanism for me to just go out and say, I would like the latest version of a thing. No. Because all the nodes should have the latest version.\u003C\u002Fp>\u003Cp>Speaker 1: So there's different ways there's different ways of doing that from the, application Velma perspective as well. Some chains can expose things like GraphQL API endpoints that allow you to query the chain through like a GraphQL API query. So from the web app developer, it doesn't feel any different than you're building any other app and you have to unfortunately use GraphQL and create very long verbose queries that you could just do with the REST API. But that's for another episode of yours. But so, so it doesn't feel any different from that perspective of a web developer.\u003C\u002Fp>\u003Cp>And then you would there's also the the archive nodes that could be used just to query for data. And so you're querying specifically to the archive node asking it for, you know, current state. That's another example, but often it's through, like, API endpoints that are exposed.\u003C\u002Fp>\u003Cp>Speaker 0: But then if I want to participate, that's the difference between a read only application and one that I can post a status to or something like that. And at that point, I'm no longer a light node. No. I I could be a light node.\u003C\u002Fp>\u003Cp>Speaker 1: Light node. Even when you're sending a transaction, you can still be a light node. It's not\u003C\u002Fp>\u003Cp>Speaker 0: gonna be validating it\u003C\u002Fp>\u003Cp>Speaker 1: right now. You won't be validating and, and unauthoring and participating in the authoring of that block. You'll be spending the transition transaction to the chain to be included in the next block, I. E. The next discrete data bit, but you won't be part of the affirmation process of that block.\u003C\u002Fp>\u003Cp>Speaker 0: So we have to summarize, and I know this is still a a a light version and a bridged version of reality. We have light nodes, which you can think of as just users of an application. They can query. They can send data in. Then you have, then you have full nodes, which are part of the they are they are basically the keepers of the chain.\u003C\u002Fp>\u003Cp>They will, based on some mechanism, decide what is what is true amongst themselves with some degree of fault tolerance call, and they generally will stake some kind of resource on their ability to do that accurately fast accurately and and correctly. And then you have archival nodes, which just store state, but they won't be part of that consensus mechanism. And there are various other kinds. These are these kind of big you I don't think primitives is the right way, but categories of these nodes. That makes lots of sense.\u003C\u002Fp>\u003Cp>I would love to see this in action, and I'm sure I will have loads more questions.\u003C\u002Fp>\u003Cp>Speaker 1: I love that we had this conversation, by the way, Kevin, just to reflect on it for a moment. I think what you're doing is so valuable because we all come to things with a lot of opinions, but to be able to gain just and most of us who are listening to this or watching this later, not all of us, but a lot of but many of us will come at it from some set of technical background and we approach things from a technical perspective. And so to be empowered more with the technical implementation details, it's not that it's to change one's opinion either which way, but to empower one in their opinion, to give them actually and maybe it does change your opinion. But, you know, we approach things through technicalities and through understanding technical specs. And so this conversation doesn't happen enough.\u003C\u002Fp>\u003Cp>So, really, I think it's a really, really good thing that we're doing this.\u003C\u002Fp>\u003Cp>Speaker 0: Thank you. And that's really the point of this series is I'm trying to put away the, you know, any negative, bias that I come with, and you're one of the people I trust most to come in and kinda just cut the idealistic vision piece, which a lot of folks\u003C\u002Fp>\u003Cp>Speaker 1: because I'm such a I'm such a pessimist realist.\u003C\u002Fp>\u003Cp>Speaker 0: Such a pessimist, but but I I knew I know that I can just pause you and go hang on. We're going on an idealistic train here. I and we haven't Good faith. Too much here, but, like, I just wanna know. I just have questions.\u003C\u002Fp>\u003Cp>Now I've already learned stuff. There was also some assumptions I had. I don't wanna kind of ruin where where I land with this, but, like, not every blockchain has to be tokenized. That is really interesting to me. So there is a technical nature of this where if characteristics are are correct for your use case, You can use this, and it doesn't have to be backed by by cryptocurrencies, which many see as scammy.\u003C\u002Fp>\u003Cp>And I will not pass comment on that in this moment. But those 2 those 2 worlds, they are not they are quite linked, but they are not inextricably linked. Like, they can exist.\u003C\u002Fp>\u003Cp>Speaker 1: One one could say I have evidence. I think this is an actually an accurate thing to say that cryptocurrencies are an articulation of a use case of blockchain, but in itself is not the totality of blockchain. It is by far one of the most and maybe the most prominent use case that's been developed to blockchain. And for obvious reasons, human nature, right? We as people tend to want to seek things that will improve our wealth standing and improve our, That's just how we are.\u003C\u002Fp>\u003Cp>And if we're being honest with ourselves as people, that is that is like baked in to to humanity. So that's it. It's not a surprise that new technologies often get articulated early on by those seeking profit first and foremost and maximizing profit. That is true with all new technologies that have ever been developed, including decentralized ledgers like blockchain.\u003C\u002Fp>\u003Cp>Speaker 0: But that isn't but I think what you've said in the reality are not the same thing there. Like, if crypto I I would love to get into the demo in a moment, but just to kind of, articulate my response to that, like, sure. I've heard loads of people in web 3 say cryptocurrency is but one use case. It is but one very common use case for it. However, it's until this conversation, I believed every other use case was built on top of that on top of cryptocurrency.\u003C\u002Fp>\u003Cp>Therefore, they are 1 in the same. It's not a use case that it is the fundamental piece that without this whole thing doesn't work. Well, that isn't the case. You\u003C\u002Fp>\u003Cp>Speaker 1: just have to flesh out the distinction between tokenization of incentive and cryptocurrency. Those are not exactly the same thing. That an internal system has a way to incentivize participation and using your resources and time and hardware is not the exact same thing as cryptocurrencies as a competitor to, you know, a government backed fiat currency. Right? Those are those are 2 interweaved, interwoven, whatever the correct grammar is there, but they but they're they are slightly different.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Cool. Good to know. And then the other thing was just I think I just didn't understand enough about the tech, the characteristics. I'm starting to get that a little bit now.\u003C\u002Fp>\u003Cp>If you're able to, if you're willing to\u003C\u002Fp>\u003Cp>Speaker 1: Can we look at something together?\u003C\u002Fp>\u003Cp>Speaker 0: I would I would love that. I would love\u003C\u002Fp>\u003Cp>Speaker 1: that. Let me just see if, let's do this.\u003C\u002Fp>\u003Cp>Speaker 0: So I did ask you before this to maybe, like, prepare a little something. So I'd kinda be curious just to set the groundwork for for what we're gonna do. I'd love to know what we're gonna look at together and maybe just the background of, you know, there is gonna be some level of infrastructure already in place. We're not gonna go and set up, maybe we will, but we you know, this may be be built on top of some existing chain infrastruct the the wording, I'm not quite finding No.\u003C\u002Fp>\u003Cp>Speaker 1: I appreciate that because I think that'd be given the time constraints, we're not gonna build together on this on this, moment at this moment. But what we will do is walk through some of the code because just like what we did, now by demystifying some of the terminology of the technical specs and how it works from a technical layer, I think it's also equally important, and I think you agree on this, to also demystify some of the technical specs. And when we talk about decentralized applications or dApps, how is that actually different from any other kind of app that one may build? And, you know, the TLDR, by the way, is just not that different. It's quite similar.\u003C\u002Fp>\u003Cp>And, and honestly, from a design perspective, that was intentional because if we want to help people build, developers to build in a new space, do you create an entirely new way of doing things, or do you just build on top of the familiar patterns anyone has when they're building, let's just say a web app or a mobile app, right? So you're gonna do the exact same things and in many cases use either the exact same language stack or slightly modified domain specific versions of that language stack, And and, and this case is what we're gonna look at, and the latter is what we're gonna look at. So what we're gonna look at is very briefly a very small sample dApp, to essentially create your own personal micro journal or microblog that you can store your thoughts and only you can access your thoughts, and it's it's your kind of, like, personal remember they used to be these web apps. I forgot what they were called even. Maybe LiveJournal was one of them where you could publish your thoughts and remember that.\u003C\u002Fp>\u003Cp>Was it LiveJournal? It was LiveJournal. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It\u003C\u002Fp>\u003Cp>Speaker 1: was. And was LiveJournal gated, I. E, like, I can only see my own thoughts if I wanted to. Was that a setting? I think you could share it or you could you could make the setting that you don't.\u003C\u002Fp>\u003Cp>Right. Yeah. So kind of a similar idea, a scaled down version of that. And so if we look in the folder structure, what we see is actually 2 different folders. We see what's called here a blog contract as one folder and then the front end as the other folder.\u003C\u002Fp>\u003Cp>And, that's obvious that in our case of a dApp, the back end is called the contract. The back end is a smart contract, and I think it's not an exact one for one synonymous comparison, but it is a great simplification of it for our purposes that a smart contract is essentially a piece of executable code that lives on the chain, that lives on the blockchain, lives on a block that can be called to do things. So it's code that lives on chain. And the 1st blockchain to make programmable blockchain possible with smart contracts is Ethereum. Right?\u003C\u002Fp>\u003Cp>Before Ethereum was Bitcoin, Bitcoin was not a programmable blockchain. You cannot put smart contracts on it, but Ethereum was the first one to make that possible.\u003C\u002Fp>\u003Cp>Speaker 0: You put code in a block. It goes in the chain. All the validator nodes go, yeah. Cool. This is part of the chain now.\u003C\u002Fp>\u003Cp>And at any point, I can call the code that lives on the chain.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So, actually, let me show you very briefly. Let me open up a code editor here. So if I No. I don't\u003C\u002Fp>\u003Cp>Speaker 0: want to bump up that font size.\u003C\u002Fp>\u003Cp>Speaker 1: No. But this is I will bump it up. I know. I just opened it. But, let's see.\u003C\u002Fp>\u003Cp>Where is it? Why is it not I'm not seeing it. Oh, it's in here maybe. Wait. Where am I missing it?\u003C\u002Fp>\u003Cp>My dot env. I don't know why I can't find it right now. Should be in there. Anyhoo. So Maybe it's oh, I know where it is.\u003C\u002Fp>\u003Cp>Sorry. See, this is real time. Right? There it is. So in the front end side, so in fact, the only thing I have in my dot end file is my contract ID.\u003C\u002Fp>\u003Cp>And what is my contract? I'm using a React. My front end revealed. It's a React app on the front end. Oh my god.\u003C\u002Fp>\u003Cp>The great reveal has been shared. But, what is and, you know, you preface, you know, your dot end variables in React with React app to make them available. Right? That's kind of the part of the architecture of React. So what's the contract ID?\u003C\u002Fp>\u003Cp>That is the address of my code on the chain. That's the that's the, the place, the location identifier of my code on the chain,\u003C\u002Fp>\u003Cp>Speaker 0: Which you don't\u003C\u002Fp>\u003Cp>Speaker 1: need to do\u003C\u002Fp>\u003Cp>Speaker 0: on the chain.\u003C\u002Fp>\u003Cp>Speaker 1: That's not private. Right? So I'm just using dotendvariables just for convenience, but this is my contract address for this contract. Right? So this is the the location on on this particular chain, whatever chain I'm\u003C\u002Fp>\u003Cp>Speaker 0: putting in. This is similar this is similar to, like, writing some code that has to be hosted on AWS. You have to deploy it to get the URL in order to call a serverless\u003C\u002Fp>\u003Cp>Speaker 1: serverless serverless serverless serverless\u003C\u002Fp>\u003Cp>Speaker 0: serverless serverless serverless serverless. This is the\u003C\u002Fp>\u003Cp>Speaker 1: equivalent of the URL. Exactly. And anyone could query this chain and see the contracts that are on this chain, anyone. And and look at this address and see the code that I've uploaded at this address through a query for a a block explorer.\u003C\u002Fp>\u003Cp>Speaker 0: Which is how you increase trust because anyone can Exactly. That's fine.\u003C\u002Fp>\u003Cp>Speaker 1: It's how you eliminate the need to have trust.\u003C\u002Fp>\u003Cp>Speaker 0: So, I mean, I think this is that might be idealistic semantics, but it's how you build trust. It's I can go look at it. Right?\u003C\u002Fp>\u003Cp>Speaker 1: It's how you label people to verify for themselves, basically. Right?\u003C\u002Fp>\u003Cp>Speaker 0: Sure.\u003C\u002Fp>\u003Cp>Speaker 1: So, okay, so we have these 2 folders. We're going to close the front end just for a minute just so we could walk through the back end, the contract, because I think that's a bit more interesting. So just by full disclosure, what you're going to see is one implementation of smart contract architecture. This is coming from the company I work at because why not? I work at Fuel.\u003C\u002Fp>\u003Cp>It's building an execution layer for Ethereum. So when I talk about an execution layer, I'm talking about and I'm gonna give you a 60 second synopsis of this because if I don't, it doesn't make any sense. When we talk about execution layer, we're talking about in the context of a modular architecture where you separated out the concerns like a separation of concerns, kind of like a models, views, controllers, MVC model, web framework, where you have the consensus layer and the execution layer as and there's another layer as well. I can't remember on top of my head, but someone will tell me in the comments, I'm sure, once this is out, and I'll look it up as soon as we're done, but the you separate out those concerns and there are different, parts of the different parts of the chain functioning in different ways and sharing the data between them to enable, basically higher throughput, right, higher speed. You you you have dedicated parts of the chain that do dedicated things in order to just, well, it's it's good.\u003C\u002Fp>\u003Cp>It's good architectural design, first of all, if you believe in like microservices, and it's also it just makes it more easier to debug when there's problems, and it increases increases throughput. Right? Some of the benefits. So we're building out an execution layer. Right?\u003C\u002Fp>\u003Cp>So as part of that developer centered kind of experience, we built out a DSL, a domain specific language for smart contracts called Sway. It's based on Rust, so it's it's Rust based DSL. So it looks a lot like Rust, but incorporates a lot of features that make it a lot more intuitive for smart contract design or more tailored to smart contract design. And so in that context, everything starts at a main file. And the main file, you can have 3 types of files, contract, which is the executable state full piece of code on the chain.\u003C\u002Fp>\u003Cp>You can also have a script, which is, can access state but is not stateful. Right. It's only state oh, actually, I take that back. It's stateful in the moment of when it's executed, but it doesn't hold state. It doesn't keep state.\u003C\u002Fp>\u003Cp>Speaker 0: Like a serverless function? Sure.\u003C\u002Fp>\u003Cp>Speaker 1: It's like a it is a serverless function. That's exactly what it is. Amazing. And the other, the other type of thing you could define here is a predicate, and predicates evaluate to true or false. They they don't hold state and they don't access state.\u003C\u002Fp>\u003Cp>They evaluate a a, a, they basically create a a Boolean evaluation. And if the thing evaluates a true or false, that can then call a transaction on a chain or it can call a thing to do something on the contract. So you could incorporate that kind of interface. You can incorporate that kind of functionality, I should say, in a smart contract. But to further round the separation of concerns, removing out that true or false, like, did I give you 50 tokens or not?\u003C\u002Fp>\u003Cp>If I gave you 50 tokens, do this. If I didn't, don't do that. So instead of having that as part of a contract interface, I take that out and I just make that its own little snippet of code that just checks whether this is true or it's false. And then if it's false, do y. If it's true, do x, and that's essentially it.\u003C\u002Fp>\u003Cp>So that's the 3rd type, of of code you can have, contract script or predicate.\u003C\u002Fp>\u003Cp>Speaker 0: Can I ask\u003C\u002Fp>\u003Cp>Speaker 1: one quick question quickly? Of course.\u003C\u002Fp>\u003Cp>Speaker 0: So we've got 2 parts. We've got front end, which will just run-in a browser that can be hosted on AWS or Netlify\u003C\u002Fp>\u003Cp>Speaker 1: or Or Vercel or Netlify.\u003C\u002Fp>\u003Cp>Speaker 0: And we have the back end. I understand it's not a a straight one for 1, but the executable code that will touch state and the rest of the chain, the database in the it's not quite that, but whatever. And that is that lives on the chain. I'm one thing I'm not really getting is when does the user of the React app, like, have to provide token to participate? Like, what part what part of this does this touch a a store of token?\u003C\u002Fp>\u003Cp>Speaker 1: They may or may not ever need to touch token. It just will depend on the design and the decisions of the particular application developer and what they want to build. So if they wanna build something that requires the user to pay for things or then they would need to provide some token. If you don't want to build something like this small sample application, which I'll run-in a moment, you can see it, it doesn't require any any token because there's nothing it requires an account. It requires a wallet address because that wallet address becomes your identity, but it doesn't require any token.\u003C\u002Fp>\u003Cp>If you want to build something that is you know, a marketplace, I think you would probably wanna have token. If you don't wanna build a marketplace, you don't you're not building a trading platform or a lending platform or any of those kind of things, then you don't you don't need to have token. If you want to pay for the cost of querying the chain because, you know, that has cost to it because you're using people's infrastructure, then you as the application developer can cover that cost. And maybe you have other revenue models for your application. Maybe it's through advertising.\u003C\u002Fp>\u003Cp>Maybe it's through, all the other ways app developers in any in any web space get revenue. Right? Like, it doesn't have to be through the user.\u003C\u002Fp>\u003Cp>Speaker 0: I think you answered it. I might just dig what a little more you said. If the application owner wants to pay for the I forgot the exact wording you said. I'm a little bit confused. This is being hosted on a block on an Ethereum blockchain.\u003C\u002Fp>\u003Cp>Speaker 1: It's being hosted on a fuel on the fuel testnet.\u003C\u002Fp>\u003Cp>Speaker 0: Right. And that is\u003C\u002Fp>\u003Cp>Speaker 1: We're not yet to, like, production main status on\u003C\u002Fp>\u003Cp>Speaker 0: deployment of this block, the submitting this this executable this smart contract, that would cost some money. And then to query it would cost some\u003C\u002Fp>\u003Cp>Speaker 1: So when I right. So when I would publish a new journal entry, that's putting data into the next block that will be finalized, which costs money, infrastructure. It's infrastructure costs infrastructure cost. That is equivalent of AWS cost or the equivalent of AWS\u003C\u002Fp>\u003Cp>Speaker 0: I'm not I'm not against it. I'm not trying to be like, oh, you have have to pay for it. I'm for pay people for for infrastructure. I was just trying to understand what, where, how\u003C\u002Fp>\u003Cp>Speaker 1: And so you as an application developer can choose how you, you know, build your cost models and your revenue.\u003C\u002Fp>\u003Cp>Speaker 0: Wallet addresses their ID, so that is kinda how this all hooks into it's like log in with PayPal. You know? Yes.\u003C\u002Fp>\u003Cp>Speaker 1: So exactly. So There's\u003C\u002Fp>\u003Cp>Speaker 0: a wallet there with money in it.\u003C\u002Fp>\u003Cp>Speaker 1: In the other in the more, you know, in the prevalent kind of web 2 models, login is done with login and password or login with username password or login with social accounts. In the web 3 model, login is done with your account, your wallet account, your ID. And that's an immutable address that links you across, you know, the network, and, and you can use it in any way you want. Many people have many different wallet accounts for different things. Right?\u003C\u002Fp>\u003Cp>So I may have a wallet account that I keep in a place that's very hard to access online and I where I may keep more of my funds. I may have a wallet account I use for debugging and, like, testing things when I'm building applications and many of those. I may have one I use for, like, common, you know, web apps where I'm using, you know, the very popular Chrome extension called MetaMask or a browser extension to enter, and I don't want to, like, put a lot of sensitive data with it. So I may just use that for, like, you know, social apps where it's requiring me to log on. And there's all different sorts of addresses you use, but the address becomes your login.\u003C\u002Fp>\u003Cp>Speaker 0: Got it. So they're inextricably linked in this setup. Again, these tokenless tokenless blockchains are completely separate from this. It's a different implementation of the same concept. So I'm sorry for interrupting you.\u003C\u002Fp>\u003Cp>I just felt that was really important.\u003C\u002Fp>\u003Cp>Speaker 1: No. It's an important thing.\u003C\u002Fp>\u003Cp>Speaker 0: And so should run us through. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So just to very very briefly run through. So this is essentially a lot of Rust syntax, so it looks a bit like Rust, but if, but with any Rust developer would see differences. But here I'm including some modules and because I separate out the concerns, I have a module I created for my data structure. I have a module I created for my interface, meaning, you know, so I have down here an implementation block and this sounds familiar to a lot of different languages as well.\u003C\u002Fp>\u003Cp>So I have an interface that defines what my code will look like and what each thing returns. And then I have an impl block for the actual implementation of that interface. Right? So if I go to the interface and it's in a file called interface, look at that. If I go to my interface, I see this is a library.\u003C\u002Fp>\u003Cp>Right? This is not a contract. It's the library. And I'm using my data structure of a post, which we can look at in just a second, and I'm also using from the standard, Rust library, vector, storage vector, a data type, right, kind of like an array, and here is my interface, my where I define what a blog is, what is inside a blog. It has a post function.\u003C\u002Fp>\u003Cp>It has a get post by author function, initialize user function, a get number of posts function, and a get post, and each one of them returns a different thing. This returns a vector of posts. This returns an identity of a user, which is a type. Right? That's a class.\u003C\u002Fp>\u003Cp>This returns a unsigned 64 bit integer of getting number of posts and this returns a post. When I get a post, it returns a post. What is a post? A post is this. And where I define posts?\u003C\u002Fp>\u003Cp>In the post file. I love it's simple. From okay. You know, my background, by the way, Kevin, is I was a for a long time a Ruby on Rails developer. And we have to get back to that, right?\u003C\u002Fp>\u003Cp>Because one of the things I love about Ruby on Rails is the convention over configuration and that things live in certain defined places. And one of the things I find most frustrating for myself personally, just speaking personally, in the JavaScript framework landscape is the lack of convention. And I love frameworks like Vue, for example, that start imposing a bit of convention. And so I can go into a Vue a lot of Vue web apps and I start understanding you introduced me to Vue, and I'm grateful for you to you for that because I can go into Vue and I can at most web apps and get a sense of the land fairly quickly. With rails, that happens instantaneously because of conventional reconfiguration.\u003C\u002Fp>\u003Cp>So this kind of convention makes me very happy. So I go into the post data structure, and this is another library. Right? And I have my struct, which is kind of like, a class in Rust. I have my struct for a post, which has 3 types, 3 fields, an ID, an unsigned, 64 integer, a content type, which equals a string of 280 characters, and an author, which is an identity.\u003C\u002Fp>\u003Cp>And then I have an implementation of my post where I can create a new post. This is like a class method. Right? Create a new post. And it returns back the self containing an object of ID, the content, and the author.\u003C\u002Fp>\u003Cp>So that's now getting back to main. So I have I've included these two things in my main, which is the main interface for the applications, the call point. It's like the app dot JS or what have you. And then I'm saying I want to use all these different libraries. I want to use a hash.\u003C\u002Fp>\u003Cp>I want to use a vector, etc. I want to use my post data structure. I want to use my blog interface that I define. And then I have a storage. This is what it will be go into my block.\u003C\u002Fp>\u003Cp>This is what gets published in the block. What gets published in the block are the posts, the number of posts, and the user. And so the post is a map of key value, the key being a number and the post. The number of posts is just a number, and the user is an as an identity. And using option, which is, you know, oh, helpful, helpful,\u003C\u002Fp>\u003Cp>Speaker 0: Type that represents an optional value.\u003C\u002Fp>\u003Cp>Speaker 1: Right? It's an at an enum that represents an optional value, either some or none. So I default it to none, and then I provide it with it. And then basically the rest of it is kind of standard Rust code. So, this is meta programming in Rust.\u003C\u002Fp>\u003Cp>This actually goes if you would dive into the Sway code base, you'll find, like, hundreds of lines of code defining what this is. But this is Rust uses a lot of meta programming or you can use a lot of meta programming in Rust to define macros. So these macros, like, abstract away a lot of the things, so I just have to write my actual logic. And then this is, like, create a post. This is get a post, and this is initialize a user and etcetera.\u003C\u002Fp>\u003Cp>And then that's basically the back end. Does that feel dramatically different? Obviously, it's not JavaScript or TypeScript. It's Rust, but does it feel dramatically different to you?\u003C\u002Fp>\u003Cp>Speaker 0: No. Not particularly. I'd be interested in the intricacy where it starts to involve token, but, like, no. Not on the surface. And this these are, as you say, the primitives.\u003C\u002Fp>\u003Cp>Can we see the front end briefly? We're we're starting to we're starting to run on both of our times.\u003C\u002Fp>\u003Cp>Speaker 1: Very, very briefly. So the front end,\u003C\u002Fp>\u003Cp>Speaker 0: essentially Even just the API calls would be interesting.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So let's go to, the so first of all, it's TypeScript. It's a React app with TypeScript, and we then generated a lot of different type generate a lot of types for the front end to interact with the back end. Right? And so if we look at the front end, well, that's the index file.\u003C\u002Fp>\u003Cp>That's not so interesting. But if we look if we look here, we're using we're basically using the the, we have a, a wallet SDK for TypeScript, and React libraries within it, and that allows us to basically, you know, abstract away a lot of that concern as well. So we're importing useAccount, use connect, use wallet, use is connected. These are different, like, what's the fancy word for it in React? But different, like, state, verifiers.\u003C\u002Fp>\u003Cp>Right? So, like, if I go\u003C\u002Fp>\u003Cp>Speaker 0: sounds plausible.\u003C\u002Fp>\u003Cp>Speaker 1: That is but it's not it's not the term in React. We know that. But I'm not a React developer. I barely can write React, essentially.\u003C\u002Fp>\u003Cp>Speaker 0: Neither am I. You told me I taught you Vue, and then immediately, here's the React app.\u003C\u002Fp>\u003Cp>Speaker 1: I know. What can I say? I what can you do? So, you know, we're basically defining the different state of the app. Right?\u003C\u002Fp>\u003Cp>So, is connected, uses that, connect, yada yada yada yada. And then we create posts. We use state. We create a post as an empty array. New post content is a string, etcetera.\u003C\u002Fp>\u003Cp>We define a type for post output, with different, you know, parameters and their types, and a type for a post, and then we set the wallet address. So once we set the wallet address, if I was building an application with tokenization as part of it, then I could also do things like request payment. I could request to pay. I could send token, and I could do all that sort of stuff. I'm I'm not doing any of that here.\u003C\u002Fp>\u003Cp>I'm just getting the wallet address for the contract from the person. And then the rest of it will kind of look similar to another any other React app. I define some some functions like get posts, and then, you know, like, the checks, like, if there's a value, if there's not a value, right, I wanna catch that and put a console in the log. Like, it's not that's kind of standard JavaScript React programming or TypeScript in this case. And then down here is the actual front end\u003C\u002Fp>\u003Cp>Speaker 0: Sure.\u003C\u002Fp>\u003Cp>Speaker 1: Kind of standard. So if we run this, so let me go into the front end. Now if I can type. Thank you.\u003C\u002Fp>\u003Cp>Speaker 0: It's fair. It's what I write most times.\u003C\u002Fp>\u003Cp>Speaker 1: I so appreciate that.\u003C\u002Fp>\u003Cp>Speaker 0: Set up an alias.\u003C\u002Fp>\u003Cp>Speaker 1: I should alias it as well because it's even in there. Look at that. Nom, was there for a second. Alright. So npm start, and it's gonna\u003C\u002Fp>\u003Cp>Speaker 0: pop up. Look it doesn't look that different if you were just using any kind of authentication service plus a payment service, I guess.\u003C\u002Fp>\u003Cp>Speaker 1: So this is, like, my this is my my little blog. Right? Like, this is exactly what it looks like, and there's no post in there currently, but I could write a post. Let's see if this works. It may not work, but let's see because I haven't played with it in a while.\u003C\u002Fp>\u003Cp>But we're live. This is fun. This is a post of mine. And, of course, it's not working right now. Because that's the that's the law.\u003C\u002Fp>\u003Cp>That's the law of things.\u003C\u002Fp>\u003Cp>Speaker 0: We don't need to work it out. In theory, that then gets submitted as a block on the chain.\u003C\u002Fp>\u003Cp>Speaker 1: In theory, it gets submitted as a block on the chain. I do not know why it did not work, but it did not work. Oh, there we go. Thank you. Now it's working.\u003C\u002Fp>\u003Cp>I don't know why it didn't work before. I think I was making changes. I didn't save it, the file.\u003C\u002Fp>\u003Cp>Speaker 0: That was\u003C\u002Fp>\u003Cp>Speaker 1: the that was very basic. So here you see it prompted already because I have 2 wallet extensions loaded. So it's asked me which wallet I want to connect with. Right? So initially, I've these are just 2 different wallets in our ecosystem, and I play with both of them.\u003C\u002Fp>\u003Cp>So I'm going to connect with this wallet. Why isn't it\u003C\u002Fp>\u003Cp>Speaker 0: Is it the fact that you're in this little mini arc thing? Oh, no. Popped up another one.\u003C\u002Fp>\u003Cp>Speaker 1: So I'm connecting. Right? And it's saying, okay. You wanna connect to this application on local host. It wants it can view your balance and activity and then and can request approval for transaction.\u003C\u002Fp>\u003Cp>Speaker 0: Like an OAuth flow.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Right. So I'm gonna connect.\u003C\u002Fp>\u003Cp>Speaker 0: I'm sure that isn't the mechanism of that. That's Oh,\u003C\u002Fp>\u003Cp>Speaker 1: why is it not working, Kevin? Nothing ever works when you want it to work in real time. Oh, now it's working.\u003C\u002Fp>\u003Cp>Speaker 0: Yay. It's all good. It's all good. I mean, a lot of this is about illustration rather than, like, oh, I click So\u003C\u002Fp>\u003Cp>Speaker 1: here you see my post, and let's see if I create a new post. This is a new post. Beautiful. Okay. Approve.\u003C\u002Fp>\u003Cp>Speaker 0: Approve transaction because that's what's happening here. You're providing token even if it's is a text And\u003C\u002Fp>\u003Cp>Speaker 1: there it is. This is a new post.\u003C\u002Fp>\u003Cp>Speaker 0: And there it is. Interesting. Yeah. I mean, that's the case on the front end. It doesn't feel terribly different to This\u003C\u002Fp>\u003Cp>Speaker 1: is why, by the way, I tell all my DevRel colleagues to always record their live coding for presentations and not do live coding from the state.\u003C\u002Fp>\u003Cp>Speaker 0: Not. I think the wealth of knowledge that I have gained as a result of talking to you today transcends the fact that you pressed the button and and something\u003C\u002Fp>\u003Cp>Speaker 1: didn't change. And by the way, the way this would start is actually quite easy. So, you know, you have, create react app, right, in the react land. If I wanna create a new contract, so we have a tool called fork, which is the fuel orchestrator, and I would just do fork new and the name of the contract and instantiate an entirely new contract for me. So fork new blog contract.\u003C\u002Fp>\u003Cp>Speaker 0: Which which, just to be clear, is the local code required to write it. That would still need deploying. That would still need submitting Right.\u003C\u002Fp>\u003Cp>Speaker 1: So let's let's get out of this for a second and see what that looks like. So fork new sample contract. And then if I go to sample contract and I take a look at it, it's very small. There's nothing in there.\u003C\u002Fp>\u003Cp>Speaker 0: So so\u003C\u002Fp>\u003Cp>Speaker 1: It just here's my AB. And you see it's not even separated out. So that can be\u003C\u002Fp>\u003Cp>Speaker 0: that can be deployed. That could be\u003C\u002Fp>\u003Cp>Speaker 1: That could be deployed. It's just a contract that returns a boolean of true, and it'll always be true. Right? There's not there's no operation happening. It's always gonna be true.\u003C\u002Fp>\u003Cp>Speaker 0: It's really interesting. So that's I suppose I suppose a lot of the complexity here is abstracted away by helper libraries It provided by fuel my guests, which is really interesting, all the ecosystem writ large. But, thank you. That was that was a great episode of learning things I love to hate. I hope that that was that was good for you.\u003C\u002Fp>\u003Cp>I feel like I hopefully didn't ask too many really difficult questions, but the questions I did ask allowed\u003C\u002Fp>\u003Cp>Speaker 1: Kevin, anyone who knows you knows you are a wonderful person, and anything you ask is fantastic. I think this is a very good conversation. Thank you for inviting me.\u003C\u002Fp>\u003Cp>Speaker 0: Thank you for being part of it, and thank you everyone for watching this. Until next time. This has been learning things I love to hate. Bye for now.\u003C\u002Fp>","Hello. My name's Kevin, and welcome to Learning Things I Love to Hate. The goal of this show is to go into situations with an open mind and learn more about the tech behind the hype. And in this episode, I'm joined by my dear friend, Ben Greenberg. Hello, Ben. Hello, Kevin. How are you doing today? I am doing really well. How are you? I'm fantastic for speaking to you. The topic of today is about web 3. There are so many realms of web 3, but because I don't know much about it at all, I thought let's start with a pretty generalist view. But before we jump into that, could you tell us a little bit about yourself, where you work, and just why why we're speaking about this topic. Yeah. Absolutely. So, Kevin, it is so great to be joining you on this. We've known each other for quite a while, and it's such a pleasure to be always in conversation with you. So I am the head of DevRel at a company called Fuel, Fuel Labs. We are building the fastest execution layer on Ethereum, and we offer a really developer centric, platform with a set of developer tools focused primarily on the ability to build decentralized applications, dApps, as one might wanna call them, in the blockchain space and to be offering, really performant and intuitive and developer facing, developer forward, Outlook for developers to build, whatever they're looking to build and to get going in in the space. So I don't understand some of those words. I don't know what an execution layer is. I'm sure we'll talk about that in a bit. But given that I don't know much about web 3, and I imagine quite a lot of the people watching this might be joining me, I thought we should start with this primer. And I thought a good way to start this is just a a real, like, candid point of just sharing some of my skepticisms that maybe we can address throughout this period we're chatting. If If we don't get to all of them, that's cool. But just at kind of a high level, the idea, the promise of decentralization, I think, is promising. And I think many people viewing will think, yes. Of course, that's promising in a world where large companies control large swaths of our data. But it always seems rooted in some kind of cryptocurrency, and I can't, in my mind, kind of sever and understand the difference between web 3 and decentralization and cryptocurrencies or tokens or something rooted in an area of tech that often is associated with scams. So maybe we can talk a bit about that because I I imagine it isn't all just one big, you know, interchangeable blob. And, also, I just don't get it. I don't know how how better way to say it. Like, if we just zoom in and look at the tech and forget all of the, like, idealistic, you know, backing of of why they exist, I don't really understand the characteristics of a web 3 application or a a dApp, and why they're unique and interesting. So I figured we might start by talking a bit about that, and then, we can get into some codes and maybe you can show me a little bit around. That sounds lovely. So where should we start, Kevin? You you you lead us on this journey. I think I kinda have these 2 main skepticisms. 1 around, decentralization and blockchain and cryptocurrency and kind of maybe just not even addressing the skepticism necessarily directly first at least, but what are the differences between these? That would be good. And then maybe talking about then we can jump into more more of the, like, technical, talking about what are the characteristics, like, how does it work at a high level, and then maybe we can jump into a demo. Does that work? That sounds perfectly fine. So first of all, just a bit of background on my, involvement in this space. So I only came into web 3 blockchain, working in it professionally about a year and a half ago or so, and I too was incredibly skeptical for a very long period of time. And I actually think I remain still a bit of a skeptic on a lot of it, and I think that perspective is actually incredibly valuable when you're working in any space, regardless of whether it's blockchain or microservice architecture or, you know, you're working on Kubernetes clusters. It doesn't really matter what it is as long as you kind of have and retain that element of healthy, constructive criticism and skepticism, I think that just benefits your work and sort of everything you're going forward on. And so I empathize with you and I share a lot of that same sense of unsureness or like, what the what the heck is this? So having said that, I think for me, one of the ways that I've synthesized understanding what we're doing here is my involvement back in, like, connected networking, pre web kind of goes back to the early nineties, and I used to run something back when I was a kid. I'm not that old, but I used to run something back when I was a kid, and I begged my parents back then to get me a few phone lines to make this possible, but it was called the bulletin board system at BBS. And I don't know if you remember or even heard of BBS's. Kevin, you're so young. You don't know these things. I don't wanna age you. I don't wanna necessarily make you feel too. Let's let's talk about BBS's for a minute. So BBS is where these, like, pre web places online where people could dial in and there were shared discussion forums. There were file servers. You could share data. It might take you on a, you know, 24 ks modem, you know, an entire day to download something. There were, you know, text based role playing games where you had 2 or 3 turns a day. And that was my introduction to, I guess, what one might call the online community was through BBS. I used to run run-in San Diego where I'm from, born and raised, called the Freethinker BBS. That was my BBS. I was that kind of kid back in the days. And, and then the web came. Right? And, and the web began in the kind of the same modality where it was kind of run by people in their homes or, you know, local. You would you would have a web server on your running on your 48680 megahertz PC, in your basement somewhere, and you'd be serving websites on an Apache web server, you know, on your 486. And then, of course, things dramatically shift. We don't go through the whole history of the web. But, we got to a point where we are now where everything is essentially running on AWS. AWS is running AWS in every other service you're probably ever going to access is somehow connected to AWS. And I think part of what the premise of what we're what we're working on in this space is how can we get back to a place where people own more of their data, where people own more of the, access their data and you have more familiarity and a sense of confidence and where things live. And there's more of a communal aspect to everything we're trying to do. And so I think that is like at writ large from a balcony perspective, kind of like the idea. That's the idea. And the way that it's it's run-in a blockchain context is so one of the first questions people always ask, and I think it's a really good question, is that so I'm accessing some blockchain application. Where the where is the data? Like, where is that actual data living? You know? So if I know if I go to, you know, x slash Twitter or I go to, you know, Instagram or I go to any of these places, I have a general idea from a web developer where the data is. But where is the data on any kind of decentralized application? And the honest answer is that data is everywhere. The data is replicated and duplicated on people's computers and on organizations around the world, at all times. So that spread of the data is, I think, the first thing that we want to tackle and address, and then we can get to other parts as well. But I think understanding that is a fundamental premise to understanding everything else that the data is not owned in any central server or any central node. It's actually spread across, multiple, potentially countless nodes. Obviously, not countless. Nothing is infinite. But yes. Sorry? No. Could I pause you for a moment and go back a couple of sec? Yeah. Definitely not. Sorry. The end. It's a it's a it's a train. We ain't stopping. So okay. So Okay. We'll take a break. We're at the trade station. Let me get it done. So on that train of thought, and I wanna come back to this train of thought, I wanna talk about the how does it work, right, and what are these nodes, how does the decentralization work, how can you trust it and so on and so forth? Can we take one step back? And I'm kinda curious again. What is the relationship between decent and maybe maybe you're like, no. Actually, we need to tackle them this way around for it to make sense. But I'm curious. What is the relationship between decentralized apps, cryptocurrency slash token slash some kind of fiscal value item and blockchain. And you you might just be like, of course, it's obvious, but, like, I really need to start thinking about it. Rule of anything is to never assume that anything is obvious. And I think that's like the first lesson we help teach people writing documentation, technical documentation. Right. Don't ever assume what is obvious to you is obvious to others. So to answer that question, I think you actually need to unpack a little bit more. And you need to you need to kind of further accentuate the issues. So considering that the the entire enterprise rests on the notion that people will choose to run these nodes, these data warehouses that replicate the data across the world. So assume people are going to do that. That's the assumption. Then you baked into that assumption has to be some kind of incentivization model for how one does that. And unless you operate on a fully altruistic model of being where I will voluntarily give my, I will voluntarily give my my electricity costs and my Internet costs and and my hardware costs, and I'm just gonna do this for the betterment of humanity, which I think you you and I, of course, operate in that level. Everyone else, I mean, we don't really know. Yeah. But, you know, obviously, we work for our companies entirely altruistically. Then you have to do some kind of incentivization model baked into it. So I think that's the first layer. And then you may say to yourself, okay, that sounds all well and good, Ben, But why can't that incentivization model be based on euros or pounds or dollars or Canadian dollars or pesos or any other form of currency? Why why does it have to be a digital kind of currency? And I think to answer that question, you have to then make that question larger. What is the value of any digital collectible or digital good that exists in any way, shape, and form from the gaming enterprises and beyond. So for example, I have a couple sons. One of them, he's, you know, fast becoming a teenager, is really into this game called Fortnite. I didn't know Fortnite existed until he got into it. That's how much, you know, you know, how how much of a dad I am. But he got he's really into Fortnite. And there's these, like, digital collectibles and things you can do in Fortnite that are really valuable to him. Why is that valuable? It itself is not, doesn't hold intrinsic value. I mean, we could also argue that a US dollar doesn't hold intrinsic value by itself. It's representation of value, but he finds value in it. So then I think the question becomes, if you're building this system and you want it to be digital first, so maybe you ought to create the incentivization model also grounded in a digital value and a digital collectible, a digital good. So then that's the second the second layer. And the third layer is, okay, Ben, what about cryptocurrencies? And to that layer, because then you're now extrapolating to, okay, now it's value transcends the incentivization model of of hardware storage and, you know, incentivizing me to run a node, etc. Now it's suddenly a competing currency in the in the world competing against, you know, global government backed currencies. And to that question, Kevin, I'm gonna say I don't want to get into that because I am not a cryptocurrency person. Like that's not what interests me, I would say, in this space. It's not what motivates me. And I do think and I will end this little bit with a little bit of a caveat, there is a bit of a privilege to not be a cryptocurrency enthusiast in this space. For example, when I travel to parts of the world where the governments are less stable and the currency is less reliable, there is a, there is a deference, to cryptocurrency that I am unfamiliar with as someone who has only lived in first world global north countries. And I am and I was actually a bit taken by surprise by that, and that just reveals sort of the the the places that I've spent most of my life living in. But, you know, the the there are places I've been where the the first question is asked to pay for something, what's your wallet address? So At a store. Interesting. The I don't I don't necessarily wanna go down the, like, and this is why cryptocurrency is a valuable track, which is, like, where this this area is going. But alright. So there's one question I have that I can't quite unpack yet, which is okay. To have people participate and participate, my understanding is there's, like, various degrees to participation, in a decentralized network, I suppose. You might correct me on my terminology by decentralized network or whatever. There's some kind of incentive. And, okay, your son gets Fortnite. Whatever it whatever he gets. Yeah. Sure. They don't have real world value. I mean, they possibly do. I think that is a whole marketplace, but whatever. You know, I think in digital games in general, those things have quite a lot of value. But let's assume let's assume they don't, and it's just I want I I want to give you something to participate. Actually, I'm going too far off the the end. Let me just ask the direct question. To to be involved in these networks, does it have to have be backed by some degree of, like, cryptocurrency token or something that converts into real money? For example, you were talking about I have sir a server in the basement, and it's running something, and that's great. And, you know, the web is weird, and we all host our own stuff. Now it's all hosted on, like, 20 data centers around the world, and now we'd like to go back to that. But, perhaps at that point in time, people were just hosting stuff altruistically. If I want people to participate in a decentralized thing that I build, why why is there a financial backing to or is there? Can can I just I'm gonna use some words perhaps ignorantly, stake some token for that has no real that isn't backed by anything that has real money. It's just a number of a thing that just exists. It just bites in the in the world, and that's that or does it always eventually get backed by one of these primitives that get traded? Yeah. I think well, to there's a couple of different questions in that. But I think, first of all so there's, you know, obviously, I began this our conversation with a bit of nostalgia back to the BBS world. And the thing that I don't have nostalgia about in the BBS world is how far we've come and what one can accomplish with the modern web and the modern Internet infrastructure. And I don't think any of us I'm actually going to take that back. I don't. There are probably people out in the world who do, but I don't want to go back to a world of, you know, 48 ks, 24 ks modems. And I like the conveniences that are wrapped up into my phone into the ability to do the things that we do on on a daily basis. I happen to enjoy that. But that kind and I rely upon it. I more than enjoy upon I rely upon it. But that kind of sophisticated infrastructure requires a lot more than, you know, the the 486 80 megahertz p Intel PCs that, you know, that we used to run things on back in the day. Oh, we. Yeah. Yeah. The we. The we. The we. And so I think the infrastructure needs are a lot greater than they were back then. And so even if it was the case that there was a that you could assume a level of altruism, today you have to you have to compensate people for their, for their costs. And then the second layer in that question is, well, what about a translation at some point to a real world value of the incentivization token and tokenization? Like, does it ever have to does it always have to translate back to a real world value? You know, I don't really know. And I'm going to defer to other people who probably will comment in the comments on this or offer their own perspectives. I don't really know if a this is a philosophical question. Right? Do if something is valuable inside a system, does it also have to be able to translate to value external from that is external to the system? And and what are the bridges that one needs to create to create that kind of synchronicity of value from inside a system to outside a system? You know, for I mean, I don't know about you, but I and I but I know for myself, I remember I'm a member of communities where there are value things there are things that are valuable that we do in those communities that are not valuable outside those communities, levels of, communal participation, levels of voluntary service, actions we do as part of, you know, social and ethnic and sub communities in the world that don't translate to value outside of themselves. But do I need to have bridges to show that value, externalize that value, and and translate the value to a larger the larger community, you know, that we live in, whether it's in in the United States or in Germany or anywhere. And, you know, that's, like, that's a philosophical question. Right? That's a larger question. I think in the case of of blockchain tokenization, the answer has been demonstratively yes because they have built those bridges to to convert the token that is used as an incentivization model to US dollar or to euro or to I think what I'm just thinking is if I wanted to host it and once again, I I wanna, I want us to move on a little bit in a moment, but maybe just for one more, like, quick quick bit here. But, like, if I just wanted to host a thing for me and my mates and this idea of it's decentralized just among us just among us, this is all cool. You know, there's, like, you know, 20 of us. Maybe it's like a school hacker club, whatever. And money doesn't need to be moving hands here. We're happy to spend some resources and not be reimbursed for them. It feels like just doing that is intrinsically wrapped up in a cryptocurrency of some kind. Even if it's small value, even if it's, like, not very much at all, it always from what I'm understanding, you can tell me no, actually. I'm not sure that's true, and it just happens to be true in most cases. It seems like it always ends up back in the world of Ethereum or Bitcoin or insert other I don't only know those 2. I'm sure there are. I think that I don't think that's true. I think that, if you don't require an incentivization model to to help, inspire people, motivate people, get them involved, then I think you can build your socialist utopia and and everyone can donate their resources as they as they choose and donate their time and their hardware and their costs. And that's entire and that works to some scale. It doesn't work. I don't think I don't think it's been proven to to work at a scale that can power something that is trans transnational that Sure, sure, sure. But that's much bigger. I'm just talking about, like, in theory, the concepts I'm about to we're about to talk about. Can create tokenless blockchain, of course. Yeah. A 100%. I mean, there there was no of course. I had no idea. I always thought there is no world in which that would, like, technically works, but it is. It just why would you slash? It's not very common, you know, or whatever. It only works as And and and, actually caveat. Fascinatingly, there are private companies that have you and we'll maybe we'll get to this in a little bit, but there are private companies that have built blockchain networks internally that are not, we would call, decentralized, but they're using the primitives of blockchain. They're using blockchain in order for things like provenance, tracking, inventory tracking, things like that. And they're not using it with token because it's all within their system. It's employees using the thing that the incentivization in the company is the is the salary and the stock options. Yeah. And they're using blockchain for the for the utility of inventory tracking, let's say. So the utility is what I the utility and the kind of these primitives and the structure, I'd love to talk about it. We're we're bumping up on you know, we're starting we've done about 20 minutes. We're bumping. We're talking. We're getting everything. Lots, that of already some of my assumptions have been dismantled, which is good. You know, that's that's why I invited you here. I wanted to genuinely learn more. There is I'd love to move on to a demo soon. But before we do that, maybe in, like, 5 ish You like my big Stanley Cup, by the way? This is the new rage in the United States. Everyone has these huge Stanley Cup now. Some of my friends got them. You know? Some people would call them quite basic. 40 ounces. But you know what they're gonna do, though? This is 40 ounces of water. I've seen Starbucks have just come out with, like, a Christmassy one. I'm gonna date this video and the the time we're recording. Yes. I know. You just did that. That's a bit of a problem. It's this this lovely red, like, you you know, matte red one. Yeah. That's nice. Yeah. But they only sell it in the US as well. Because we Americans, we really hydrate quite a lot, I guess, opposed to the Europeans. What was that type of trash? Ice. Right? The Europeans don't like to drink water as much as Americans do, apparently. There was a whole social media trend there. Looking at my soda here. So let's so let's move on. I would love to know just, like, at a high level, in about 5 minutes ish, I would love to, like I I wanna absorb as much as I can in the 5. I'd love you to, like, show me stuff. Show me stuff. Could you maybe talk about some of these primitives and the structure? And I understand that there's this idea of, like, not everyone has the same role within a decentralized network, and I'd love to talk about that how it works. We when we briefly prepped for this, we spoke very briefly about this thing called, like, a consensus mechanism. Don't know what it is. If you could share that, that would be awesome. Absolutely. So cut me off at any point because, you know, as you know me very well, I can be long winded in my in my life. The timer? I do see the timer, but you're not you're not gonna pay attention to the timer. I'm actually You'll ignore it. That's my job. Fine. A timer like Kevin. Yes. So, essentially, we had already mentioned briefly that the way this, works at a basic level is that data is replicated across all these individual servers and, you know, people's computers around the world, and those are called nodes. Right. And the question, of course, has to arise. What happens when there is a dispute amongst the nodes? What happens when node 1 and node 33 say, no, the data is actually node 1 says one way and node 33 says the other way? And before we even get to there, what is the data? So we talk about blockchains. Right? What is a blockchain? It's a chain of blocks. What is a block? Wow. Thanks. I I know. Right? Was that was that definitionally circular? What is a hand? It is something that displays the characteristics of a hand. What is a blockchain? It is a chain that has blocks in it. I know. I'm like it's we're mind blowingly like astute and wise in this in this episode. But essentially what is a block? A block is a defined discrete piece of data that contains different elements of of of things important to the chain. So it's going to contain a cryptographic hash of the transaction used oftentimes, using Merkle Tree cryptography. So a Merkle ized hash, whatever that means, that's gonna have the previous block, the hash of the previous block, meaning the previous defined set of data and its current data. So that's and then that becomes like a hash that again can get proven cryptographically between the nodes that this hash is valid. It's gonna have some metadata at the top of the block sort of on the version of the chain, you know, versioning, you know, like we're all familiar with semantic versioning, versioning of the chain, other kind of pieces of metadata, and some other bits and bobs is going to be in the block. And so that block gets then disseminated across the nodes for consensus, and that's where we get to consensus mechanism. And consensus has to happen fast, efficiently, and also has to happen securely. And that's a complicated, dilemma. How do you affect fast oh, and don't forget, obviously, it goes without saying decentralized. Right? So how do you affect a decentralized and crazily fast and secure consensus across nodes around the world? Right. Because you you need to ensure these things. So that's where the mechanisms come into place. And there's different consensus mechanisms, and they you can kind of imagine them on a spectrum of decentralization and and and centralization, right, to try and to try and grapple with these things. The the most famous one that often people are aware of is the Bitcoin model, which is proof of work, a proof of work consensus. Proof of work consensus basically requires the servers to solve very complex mathematical operations in order to validate the transaction and then create that new block. That is the least centralized. It is the most decentralized because it doesn't designate or delegate any any node. It's just whoever finishes the mathematical operation first gets to be the node that offers the validation proof that again again gets disseminated across all the other nodes Oh. For validation. But Sorry. And by doing it first and providing the validation proof, you get more of the insight. You get the reward. You get the reward. So you're trying to get the in that model, you're basically There you understand the incentivization model there. And you burn the planet in that way because it's really computationally expensive, but you get like, and that's that model. But that isn't that's not universal. Every No. No. No. That's not universal. And I I by the way, I think to understand a lot of this, one also needs to understand game theory and, the whole pursuit of game theory of, like, how do you incentivize players in a game to complete compete? Where do you find the equilibrium of players? Like, what in game theory, we call that the Nash equilibrium. Like, what is the perfect moment in a game where all players are playing their optimal moves, that's called the Nash equilibrium game theory. And there's a there's really interesting like economics and game theory and cryptography, like, a lot of stuff that goes into this, but that's not for here. Beyond the scope. Yeah. Yeah. That's for beyond the scope of this conversation. So that's called proof of work. The problem with proof of work is it's incredibly energy intensive, and it's destroying the world. Okay. So let's let's put that in a category. We don't wanna destroy the world. We kinda like this world. You know? It's got its problems, but it's a nice place to live. It's maybe our only place to live for the foreseeable future. We kinda wanna keep it going. I know Kevin, you got a couple kids. I have a couple kids who kinda want them to have a world to live on. It's a nice Yeah. Yeah. You know, it's fine. Maybe we wanted to go. Okay. Yeah. So then the the the other model that is very popular, is called proof of stake. And so as opposed to proof of stake, proof as opposed to proof of work, proof of stake is essentially that, the node operators stake their token to validate. And the ones that stake more, as collateral tend to get chosen more to be the validator, and then they get more stake and they get more back as a reward. There's a variation of this called delegated proof of stake where also delegators can participate in this consensus mechanism and can choose. So I'll tell you that people, other people who are playing in this game can choose a node operator and say, I'm going to stake as well on that node operator and I'm gonna get a portion of the reward and the operator will get a portion of the reward. And so people get to choose operators. So the benefit of this is it's less energy intensive by far, dramatically less energy intensive by scale, extraordinary scale. It is clearly a little less decentralized, because right? For obvious reasons. But the energy intensive nature of it, the the fact that it doesn't really destroy the planet any more than any other web application destroys the planet, is is a good thing. Like, let's say that's an unreserved good. It's a good. Right. And there's other models as well that are even less decentralized. There's something called proof of authority where basically the chain chooses, through its however the chain chooses things, and that's a whole another conversation. But the chain chooses approved approved nodes to be the validators. And those are unapproved like an approved list of node operators that can validate. That is clearly not super decentralized, but it is obviously also much more fast. And and you could argue more secure because you've you have a defined list of vetted As as long as those nodes are kept secure. But, sure, you're right. As long as nodes are kept secure. I'm kept. In this in this world of it, it has to be energy efficient, decentralized, secure, fast. It's like the all of these various models It's like a trifecta of a dilemma. Yes. Exactly. So so all of these all of these, you know, do it different ways, and I'm sure you build your applications on different And you also need to have an element of fault tolerance for bad actors as well. Sure. And we call that the Byzantine general's dilemma. And if you ever heard of the Byzantine general's lemma. It doesn't it's not from blockchain, but it applies to blockchain. It's the have you heard of this, Kevin? No. No. So very briefly because this is actually quite fascinating. Let's say you are that it comes from, army met army terminology. You are the Byzantine army, right, and you are surrounding a city and you're, you are proceeding to invade the city. The only way to tell all the generals that are surrounding this quite large metropolis that it's time to go, like lunchtime, is by sending messengers to all the generals around the place. And you don't get feedback from the messengers whether the generals have accepted or not. You just have to assume that all the generals will then activate their whatever they call them in armies, brigades. I don't know what whatever the thing is. So you have to assume that a certain percentage of your generals, either by malintent or by incompetence, will not activate their brigades. And maybe they're traitors. Maybe they're just bad at their jobs. We don't know. So you have to have an element of tolerance for ineptitude or for maliciousness. And all of these systems have to have what we call technically the BFT, the Byzantine Fault Tolerance. The twins' exhausting, but I get it. It's really really interesting. It's really interesting from, like, a nerd perspective. So so just to help me understand, because I really wanna jump into, like, if you could if we can maybe build a little dApp, decentralized app together if you're open to that. But do that. But but just to help me understand, just at the core, there are a few players here. There are the developers of a chain, but they they will inevitably participate in it. But those two things are not are not a given. Then you're in the participation of a chain. You have validators, and and they are they might be on an approved list. It might be whatever. You have to Depending on the mechanism the chain is. Depending on the mechanism, they will validate and and drive consensus on what the And send and send those validated blocks and they'll send those validated blocks to all the other nodes to That was my last question. And then nodes are generic whether you validate this or not, and then you have what I may just call users, standard nodes, not There are differentiated nodes, and, yeah, that's what you're, I think, intimating at right now. Yeah. But but but there is there's there's what we call user. There's what we call full nodes, which are the ones that can validate and affirm and do all the operations of a chain. Sure. Then there's archival nodes, which as the name sounds, are there to preserve the history of the chain. And then there's things that we call light nodes or light clients, which participate in some of the operations of the chain but don't validate. And those are really helpful when you have low low infrastructure, like if you wanna have a mobile app participate or you wanna have a wallet a mobile wallet participate. It would just be like users. I'm a consumer. Basically users, and they would query full nodes for more data, but they themselves don't possess the entire history of the chain, and they don't participate in the full validation of a chain. And there's other nodes as well, but those are some of the big ones. That's really interesting. And then just my final question, just to make sure I got it. When I I'm a user. I have a I run a light node, which could be a client, right, a web app, whatever, and I want to gain data. I'm not going to a specific node for that data. No. I'm asking the the chain. There is a mechanism for me to just go out and say, I would like the latest version of a thing. No. Because all the nodes should have the latest version. So there's different ways there's different ways of doing that from the, application Velma perspective as well. Some chains can expose things like GraphQL API endpoints that allow you to query the chain through like a GraphQL API query. So from the web app developer, it doesn't feel any different than you're building any other app and you have to unfortunately use GraphQL and create very long verbose queries that you could just do with the REST API. But that's for another episode of yours. But so, so it doesn't feel any different from that perspective of a web developer. And then you would there's also the the archive nodes that could be used just to query for data. And so you're querying specifically to the archive node asking it for, you know, current state. That's another example, but often it's through, like, API endpoints that are exposed. But then if I want to participate, that's the difference between a read only application and one that I can post a status to or something like that. And at that point, I'm no longer a light node. No. I I could be a light node. Light node. Even when you're sending a transaction, you can still be a light node. It's not gonna be validating it right now. You won't be validating and, and unauthoring and participating in the authoring of that block. You'll be spending the transition transaction to the chain to be included in the next block, I. E. The next discrete data bit, but you won't be part of the affirmation process of that block. So we have to summarize, and I know this is still a a a light version and a bridged version of reality. We have light nodes, which you can think of as just users of an application. They can query. They can send data in. Then you have, then you have full nodes, which are part of the they are they are basically the keepers of the chain. They will, based on some mechanism, decide what is what is true amongst themselves with some degree of fault tolerance call, and they generally will stake some kind of resource on their ability to do that accurately fast accurately and and correctly. And then you have archival nodes, which just store state, but they won't be part of that consensus mechanism. And there are various other kinds. These are these kind of big you I don't think primitives is the right way, but categories of these nodes. That makes lots of sense. I would love to see this in action, and I'm sure I will have loads more questions. I love that we had this conversation, by the way, Kevin, just to reflect on it for a moment. I think what you're doing is so valuable because we all come to things with a lot of opinions, but to be able to gain just and most of us who are listening to this or watching this later, not all of us, but a lot of but many of us will come at it from some set of technical background and we approach things from a technical perspective. And so to be empowered more with the technical implementation details, it's not that it's to change one's opinion either which way, but to empower one in their opinion, to give them actually and maybe it does change your opinion. But, you know, we approach things through technicalities and through understanding technical specs. And so this conversation doesn't happen enough. So, really, I think it's a really, really good thing that we're doing this. Thank you. And that's really the point of this series is I'm trying to put away the, you know, any negative, bias that I come with, and you're one of the people I trust most to come in and kinda just cut the idealistic vision piece, which a lot of folks because I'm such a I'm such a pessimist realist. Such a pessimist, but but I I knew I know that I can just pause you and go hang on. We're going on an idealistic train here. I and we haven't Good faith. Too much here, but, like, I just wanna know. I just have questions. Now I've already learned stuff. There was also some assumptions I had. I don't wanna kind of ruin where where I land with this, but, like, not every blockchain has to be tokenized. That is really interesting to me. So there is a technical nature of this where if characteristics are are correct for your use case, You can use this, and it doesn't have to be backed by by cryptocurrencies, which many see as scammy. And I will not pass comment on that in this moment. But those 2 those 2 worlds, they are not they are quite linked, but they are not inextricably linked. Like, they can exist. One one could say I have evidence. I think this is an actually an accurate thing to say that cryptocurrencies are an articulation of a use case of blockchain, but in itself is not the totality of blockchain. It is by far one of the most and maybe the most prominent use case that's been developed to blockchain. And for obvious reasons, human nature, right? We as people tend to want to seek things that will improve our wealth standing and improve our, That's just how we are. And if we're being honest with ourselves as people, that is that is like baked in to to humanity. So that's it. It's not a surprise that new technologies often get articulated early on by those seeking profit first and foremost and maximizing profit. That is true with all new technologies that have ever been developed, including decentralized ledgers like blockchain. But that isn't but I think what you've said in the reality are not the same thing there. Like, if crypto I I would love to get into the demo in a moment, but just to kind of, articulate my response to that, like, sure. I've heard loads of people in web 3 say cryptocurrency is but one use case. It is but one very common use case for it. However, it's until this conversation, I believed every other use case was built on top of that on top of cryptocurrency. Therefore, they are 1 in the same. It's not a use case that it is the fundamental piece that without this whole thing doesn't work. Well, that isn't the case. You just have to flesh out the distinction between tokenization of incentive and cryptocurrency. Those are not exactly the same thing. That an internal system has a way to incentivize participation and using your resources and time and hardware is not the exact same thing as cryptocurrencies as a competitor to, you know, a government backed fiat currency. Right? Those are those are 2 interweaved, interwoven, whatever the correct grammar is there, but they but they're they are slightly different. Yeah. Cool. Good to know. And then the other thing was just I think I just didn't understand enough about the tech, the characteristics. I'm starting to get that a little bit now. If you're able to, if you're willing to Can we look at something together? I would I would love that. I would love that. Let me just see if, let's do this. So I did ask you before this to maybe, like, prepare a little something. So I'd kinda be curious just to set the groundwork for for what we're gonna do. I'd love to know what we're gonna look at together and maybe just the background of, you know, there is gonna be some level of infrastructure already in place. We're not gonna go and set up, maybe we will, but we you know, this may be be built on top of some existing chain infrastruct the the wording, I'm not quite finding No. I appreciate that because I think that'd be given the time constraints, we're not gonna build together on this on this, moment at this moment. But what we will do is walk through some of the code because just like what we did, now by demystifying some of the terminology of the technical specs and how it works from a technical layer, I think it's also equally important, and I think you agree on this, to also demystify some of the technical specs. And when we talk about decentralized applications or dApps, how is that actually different from any other kind of app that one may build? And, you know, the TLDR, by the way, is just not that different. It's quite similar. And, and honestly, from a design perspective, that was intentional because if we want to help people build, developers to build in a new space, do you create an entirely new way of doing things, or do you just build on top of the familiar patterns anyone has when they're building, let's just say a web app or a mobile app, right? So you're gonna do the exact same things and in many cases use either the exact same language stack or slightly modified domain specific versions of that language stack, And and, and this case is what we're gonna look at, and the latter is what we're gonna look at. So what we're gonna look at is very briefly a very small sample dApp, to essentially create your own personal micro journal or microblog that you can store your thoughts and only you can access your thoughts, and it's it's your kind of, like, personal remember they used to be these web apps. I forgot what they were called even. Maybe LiveJournal was one of them where you could publish your thoughts and remember that. Was it LiveJournal? It was LiveJournal. Right? Yeah. It was. And was LiveJournal gated, I. E, like, I can only see my own thoughts if I wanted to. Was that a setting? I think you could share it or you could you could make the setting that you don't. Right. Yeah. So kind of a similar idea, a scaled down version of that. And so if we look in the folder structure, what we see is actually 2 different folders. We see what's called here a blog contract as one folder and then the front end as the other folder. And, that's obvious that in our case of a dApp, the back end is called the contract. The back end is a smart contract, and I think it's not an exact one for one synonymous comparison, but it is a great simplification of it for our purposes that a smart contract is essentially a piece of executable code that lives on the chain, that lives on the blockchain, lives on a block that can be called to do things. So it's code that lives on chain. And the 1st blockchain to make programmable blockchain possible with smart contracts is Ethereum. Right? Before Ethereum was Bitcoin, Bitcoin was not a programmable blockchain. You cannot put smart contracts on it, but Ethereum was the first one to make that possible. You put code in a block. It goes in the chain. All the validator nodes go, yeah. Cool. This is part of the chain now. And at any point, I can call the code that lives on the chain. Yeah. So, actually, let me show you very briefly. Let me open up a code editor here. So if I No. I don't want to bump up that font size. No. But this is I will bump it up. I know. I just opened it. But, let's see. Where is it? Why is it not I'm not seeing it. Oh, it's in here maybe. Wait. Where am I missing it? My dot env. I don't know why I can't find it right now. Should be in there. Anyhoo. So Maybe it's oh, I know where it is. Sorry. See, this is real time. Right? There it is. So in the front end side, so in fact, the only thing I have in my dot end file is my contract ID. And what is my contract? I'm using a React. My front end revealed. It's a React app on the front end. Oh my god. The great reveal has been shared. But, what is and, you know, you preface, you know, your dot end variables in React with React app to make them available. Right? That's kind of the part of the architecture of React. So what's the contract ID? That is the address of my code on the chain. That's the that's the, the place, the location identifier of my code on the chain, Which you don't need to do on the chain. That's not private. Right? So I'm just using dotendvariables just for convenience, but this is my contract address for this contract. Right? So this is the the location on on this particular chain, whatever chain I'm putting in. This is similar this is similar to, like, writing some code that has to be hosted on AWS. You have to deploy it to get the URL in order to call a serverless serverless serverless serverless serverless serverless serverless serverless serverless. This is the equivalent of the URL. Exactly. And anyone could query this chain and see the contracts that are on this chain, anyone. And and look at this address and see the code that I've uploaded at this address through a query for a a block explorer. Which is how you increase trust because anyone can Exactly. That's fine. It's how you eliminate the need to have trust. So, I mean, I think this is that might be idealistic semantics, but it's how you build trust. It's I can go look at it. Right? It's how you label people to verify for themselves, basically. Right? Sure. So, okay, so we have these 2 folders. We're going to close the front end just for a minute just so we could walk through the back end, the contract, because I think that's a bit more interesting. So just by full disclosure, what you're going to see is one implementation of smart contract architecture. This is coming from the company I work at because why not? I work at Fuel. It's building an execution layer for Ethereum. So when I talk about an execution layer, I'm talking about and I'm gonna give you a 60 second synopsis of this because if I don't, it doesn't make any sense. When we talk about execution layer, we're talking about in the context of a modular architecture where you separated out the concerns like a separation of concerns, kind of like a models, views, controllers, MVC model, web framework, where you have the consensus layer and the execution layer as and there's another layer as well. I can't remember on top of my head, but someone will tell me in the comments, I'm sure, once this is out, and I'll look it up as soon as we're done, but the you separate out those concerns and there are different, parts of the different parts of the chain functioning in different ways and sharing the data between them to enable, basically higher throughput, right, higher speed. You you you have dedicated parts of the chain that do dedicated things in order to just, well, it's it's good. It's good architectural design, first of all, if you believe in like microservices, and it's also it just makes it more easier to debug when there's problems, and it increases increases throughput. Right? Some of the benefits. So we're building out an execution layer. Right? So as part of that developer centered kind of experience, we built out a DSL, a domain specific language for smart contracts called Sway. It's based on Rust, so it's it's Rust based DSL. So it looks a lot like Rust, but incorporates a lot of features that make it a lot more intuitive for smart contract design or more tailored to smart contract design. And so in that context, everything starts at a main file. And the main file, you can have 3 types of files, contract, which is the executable state full piece of code on the chain. You can also have a script, which is, can access state but is not stateful. Right. It's only state oh, actually, I take that back. It's stateful in the moment of when it's executed, but it doesn't hold state. It doesn't keep state. Like a serverless function? Sure. It's like a it is a serverless function. That's exactly what it is. Amazing. And the other, the other type of thing you could define here is a predicate, and predicates evaluate to true or false. They they don't hold state and they don't access state. They evaluate a a, a, they basically create a a Boolean evaluation. And if the thing evaluates a true or false, that can then call a transaction on a chain or it can call a thing to do something on the contract. So you could incorporate that kind of interface. You can incorporate that kind of functionality, I should say, in a smart contract. But to further round the separation of concerns, removing out that true or false, like, did I give you 50 tokens or not? If I gave you 50 tokens, do this. If I didn't, don't do that. So instead of having that as part of a contract interface, I take that out and I just make that its own little snippet of code that just checks whether this is true or it's false. And then if it's false, do y. If it's true, do x, and that's essentially it. So that's the 3rd type, of of code you can have, contract script or predicate. Can I ask one quick question quickly? Of course. So we've got 2 parts. We've got front end, which will just run-in a browser that can be hosted on AWS or Netlify or Or Vercel or Netlify. And we have the back end. I understand it's not a a straight one for 1, but the executable code that will touch state and the rest of the chain, the database in the it's not quite that, but whatever. And that is that lives on the chain. I'm one thing I'm not really getting is when does the user of the React app, like, have to provide token to participate? Like, what part what part of this does this touch a a store of token? They may or may not ever need to touch token. It just will depend on the design and the decisions of the particular application developer and what they want to build. So if they wanna build something that requires the user to pay for things or then they would need to provide some token. If you don't want to build something like this small sample application, which I'll run-in a moment, you can see it, it doesn't require any any token because there's nothing it requires an account. It requires a wallet address because that wallet address becomes your identity, but it doesn't require any token. If you want to build something that is you know, a marketplace, I think you would probably wanna have token. If you don't wanna build a marketplace, you don't you're not building a trading platform or a lending platform or any of those kind of things, then you don't you don't need to have token. If you want to pay for the cost of querying the chain because, you know, that has cost to it because you're using people's infrastructure, then you as the application developer can cover that cost. And maybe you have other revenue models for your application. Maybe it's through advertising. Maybe it's through, all the other ways app developers in any in any web space get revenue. Right? Like, it doesn't have to be through the user. I think you answered it. I might just dig what a little more you said. If the application owner wants to pay for the I forgot the exact wording you said. I'm a little bit confused. This is being hosted on a block on an Ethereum blockchain. It's being hosted on a fuel on the fuel testnet. Right. And that is We're not yet to, like, production main status on deployment of this block, the submitting this this executable this smart contract, that would cost some money. And then to query it would cost some So when I right. So when I would publish a new journal entry, that's putting data into the next block that will be finalized, which costs money, infrastructure. It's infrastructure costs infrastructure cost. That is equivalent of AWS cost or the equivalent of AWS I'm not I'm not against it. I'm not trying to be like, oh, you have have to pay for it. I'm for pay people for for infrastructure. I was just trying to understand what, where, how And so you as an application developer can choose how you, you know, build your cost models and your revenue. Wallet addresses their ID, so that is kinda how this all hooks into it's like log in with PayPal. You know? Yes. So exactly. So There's a wallet there with money in it. In the other in the more, you know, in the prevalent kind of web 2 models, login is done with login and password or login with username password or login with social accounts. In the web 3 model, login is done with your account, your wallet account, your ID. And that's an immutable address that links you across, you know, the network, and, and you can use it in any way you want. Many people have many different wallet accounts for different things. Right? So I may have a wallet account that I keep in a place that's very hard to access online and I where I may keep more of my funds. I may have a wallet account I use for debugging and, like, testing things when I'm building applications and many of those. I may have one I use for, like, common, you know, web apps where I'm using, you know, the very popular Chrome extension called MetaMask or a browser extension to enter, and I don't want to, like, put a lot of sensitive data with it. So I may just use that for, like, you know, social apps where it's requiring me to log on. And there's all different sorts of addresses you use, but the address becomes your login. Got it. So they're inextricably linked in this setup. Again, these tokenless tokenless blockchains are completely separate from this. It's a different implementation of the same concept. So I'm sorry for interrupting you. I just felt that was really important. No. It's an important thing. And so should run us through. Yeah. Yeah. So just to very very briefly run through. So this is essentially a lot of Rust syntax, so it looks a bit like Rust, but if, but with any Rust developer would see differences. But here I'm including some modules and because I separate out the concerns, I have a module I created for my data structure. I have a module I created for my interface, meaning, you know, so I have down here an implementation block and this sounds familiar to a lot of different languages as well. So I have an interface that defines what my code will look like and what each thing returns. And then I have an impl block for the actual implementation of that interface. Right? So if I go to the interface and it's in a file called interface, look at that. If I go to my interface, I see this is a library. Right? This is not a contract. It's the library. And I'm using my data structure of a post, which we can look at in just a second, and I'm also using from the standard, Rust library, vector, storage vector, a data type, right, kind of like an array, and here is my interface, my where I define what a blog is, what is inside a blog. It has a post function. It has a get post by author function, initialize user function, a get number of posts function, and a get post, and each one of them returns a different thing. This returns a vector of posts. This returns an identity of a user, which is a type. Right? That's a class. This returns a unsigned 64 bit integer of getting number of posts and this returns a post. When I get a post, it returns a post. What is a post? A post is this. And where I define posts? In the post file. I love it's simple. From okay. You know, my background, by the way, Kevin, is I was a for a long time a Ruby on Rails developer. And we have to get back to that, right? Because one of the things I love about Ruby on Rails is the convention over configuration and that things live in certain defined places. And one of the things I find most frustrating for myself personally, just speaking personally, in the JavaScript framework landscape is the lack of convention. And I love frameworks like Vue, for example, that start imposing a bit of convention. And so I can go into a Vue a lot of Vue web apps and I start understanding you introduced me to Vue, and I'm grateful for you to you for that because I can go into Vue and I can at most web apps and get a sense of the land fairly quickly. With rails, that happens instantaneously because of conventional reconfiguration. So this kind of convention makes me very happy. So I go into the post data structure, and this is another library. Right? And I have my struct, which is kind of like, a class in Rust. I have my struct for a post, which has 3 types, 3 fields, an ID, an unsigned, 64 integer, a content type, which equals a string of 280 characters, and an author, which is an identity. And then I have an implementation of my post where I can create a new post. This is like a class method. Right? Create a new post. And it returns back the self containing an object of ID, the content, and the author. So that's now getting back to main. So I have I've included these two things in my main, which is the main interface for the applications, the call point. It's like the app dot JS or what have you. And then I'm saying I want to use all these different libraries. I want to use a hash. I want to use a vector, etc. I want to use my post data structure. I want to use my blog interface that I define. And then I have a storage. This is what it will be go into my block. This is what gets published in the block. What gets published in the block are the posts, the number of posts, and the user. And so the post is a map of key value, the key being a number and the post. The number of posts is just a number, and the user is an as an identity. And using option, which is, you know, oh, helpful, helpful, Type that represents an optional value. Right? It's an at an enum that represents an optional value, either some or none. So I default it to none, and then I provide it with it. And then basically the rest of it is kind of standard Rust code. So, this is meta programming in Rust. This actually goes if you would dive into the Sway code base, you'll find, like, hundreds of lines of code defining what this is. But this is Rust uses a lot of meta programming or you can use a lot of meta programming in Rust to define macros. So these macros, like, abstract away a lot of the things, so I just have to write my actual logic. And then this is, like, create a post. This is get a post, and this is initialize a user and etcetera. And then that's basically the back end. Does that feel dramatically different? Obviously, it's not JavaScript or TypeScript. It's Rust, but does it feel dramatically different to you? No. Not particularly. I'd be interested in the intricacy where it starts to involve token, but, like, no. Not on the surface. And this these are, as you say, the primitives. Can we see the front end briefly? We're we're starting to we're starting to run on both of our times. Very, very briefly. So the front end, essentially Even just the API calls would be interesting. Yeah. So let's go to, the so first of all, it's TypeScript. It's a React app with TypeScript, and we then generated a lot of different type generate a lot of types for the front end to interact with the back end. Right? And so if we look at the front end, well, that's the index file. That's not so interesting. But if we look if we look here, we're using we're basically using the the, we have a, a wallet SDK for TypeScript, and React libraries within it, and that allows us to basically, you know, abstract away a lot of that concern as well. So we're importing useAccount, use connect, use wallet, use is connected. These are different, like, what's the fancy word for it in React? But different, like, state, verifiers. Right? So, like, if I go sounds plausible. That is but it's not it's not the term in React. We know that. But I'm not a React developer. I barely can write React, essentially. Neither am I. You told me I taught you Vue, and then immediately, here's the React app. I know. What can I say? I what can you do? So, you know, we're basically defining the different state of the app. Right? So, is connected, uses that, connect, yada yada yada yada. And then we create posts. We use state. We create a post as an empty array. New post content is a string, etcetera. We define a type for post output, with different, you know, parameters and their types, and a type for a post, and then we set the wallet address. So once we set the wallet address, if I was building an application with tokenization as part of it, then I could also do things like request payment. I could request to pay. I could send token, and I could do all that sort of stuff. I'm I'm not doing any of that here. I'm just getting the wallet address for the contract from the person. And then the rest of it will kind of look similar to another any other React app. I define some some functions like get posts, and then, you know, like, the checks, like, if there's a value, if there's not a value, right, I wanna catch that and put a console in the log. Like, it's not that's kind of standard JavaScript React programming or TypeScript in this case. And then down here is the actual front end Sure. Kind of standard. So if we run this, so let me go into the front end. Now if I can type. Thank you. It's fair. It's what I write most times. I so appreciate that. Set up an alias. I should alias it as well because it's even in there. Look at that. Nom, was there for a second. Alright. So npm start, and it's gonna pop up. Look it doesn't look that different if you were just using any kind of authentication service plus a payment service, I guess. So this is, like, my this is my my little blog. Right? Like, this is exactly what it looks like, and there's no post in there currently, but I could write a post. Let's see if this works. It may not work, but let's see because I haven't played with it in a while. But we're live. This is fun. This is a post of mine. And, of course, it's not working right now. Because that's the that's the law. That's the law of things. We don't need to work it out. In theory, that then gets submitted as a block on the chain. In theory, it gets submitted as a block on the chain. I do not know why it did not work, but it did not work. Oh, there we go. Thank you. Now it's working. I don't know why it didn't work before. I think I was making changes. I didn't save it, the file. That was the that was very basic. So here you see it prompted already because I have 2 wallet extensions loaded. So it's asked me which wallet I want to connect with. Right? So initially, I've these are just 2 different wallets in our ecosystem, and I play with both of them. So I'm going to connect with this wallet. Why isn't it Is it the fact that you're in this little mini arc thing? Oh, no. Popped up another one. So I'm connecting. Right? And it's saying, okay. You wanna connect to this application on local host. It wants it can view your balance and activity and then and can request approval for transaction. Like an OAuth flow. Yeah. Right. So I'm gonna connect. I'm sure that isn't the mechanism of that. That's Oh, why is it not working, Kevin? Nothing ever works when you want it to work in real time. Oh, now it's working. Yay. It's all good. It's all good. I mean, a lot of this is about illustration rather than, like, oh, I click So here you see my post, and let's see if I create a new post. This is a new post. Beautiful. Okay. Approve. Approve transaction because that's what's happening here. You're providing token even if it's is a text And there it is. This is a new post. And there it is. Interesting. Yeah. I mean, that's the case on the front end. It doesn't feel terribly different to This is why, by the way, I tell all my DevRel colleagues to always record their live coding for presentations and not do live coding from the state. Not. I think the wealth of knowledge that I have gained as a result of talking to you today transcends the fact that you pressed the button and and something didn't change. And by the way, the way this would start is actually quite easy. So, you know, you have, create react app, right, in the react land. If I wanna create a new contract, so we have a tool called fork, which is the fuel orchestrator, and I would just do fork new and the name of the contract and instantiate an entirely new contract for me. So fork new blog contract. Which which, just to be clear, is the local code required to write it. That would still need deploying. That would still need submitting Right. So let's let's get out of this for a second and see what that looks like. So fork new sample contract. And then if I go to sample contract and I take a look at it, it's very small. There's nothing in there. So so It just here's my AB. And you see it's not even separated out. So that can be that can be deployed. That could be That could be deployed. It's just a contract that returns a boolean of true, and it'll always be true. Right? There's not there's no operation happening. It's always gonna be true. It's really interesting. So that's I suppose I suppose a lot of the complexity here is abstracted away by helper libraries It provided by fuel my guests, which is really interesting, all the ecosystem writ large. But, thank you. That was that was a great episode of learning things I love to hate. I hope that that was that was good for you. I feel like I hopefully didn't ask too many really difficult questions, but the questions I did ask allowed Kevin, anyone who knows you knows you are a wonderful person, and anything you ask is fantastic. I think this is a very good conversation. Thank you for inviting me. Thank you for being part of it, and thank you everyone for watching this. Until next time. This has been learning things I love to hate. Bye for now.",[5588,5589],"8cb48936-bef9-478e-a1d1-6adade03fcf8","38c7bef7-1e5c-4dd5-b5b1-9e2bf15ad610",[],{"year":2380,"number":213,"id":4680,"episodes":5592,"show":5593},[4682,4683,4684,4657],{"title":4686,"slug":4687},{"id":4607,"slug":5595,"vimeo_id":5596,"description":5597,"tile":5598,"length":129,"resources":5599,"people":5603,"episode_number":13,"published":5422,"title":5605,"video_transcript_html":5606,"video_transcript_text":5607,"content":12,"seo":12,"status":19,"episode_people":5608,"recommendations":5610,"season":5611},"vonage","895915734","Build a prompt which allows customer notifications via text message using the Vonage SMS API","a73e5c66-b217-469a-b76b-a5e13d72f8d6",[5600],{"name":5601,"url":5602},"Vonage SMS API Docs","https:\u002F\u002Fdeveloper.vonage.com\u002Fen\u002Fmessaging\u002Fsms\u002Foverview",[5604],{"name":2820,"url":2821},"Sending SMS Messages with Vonage","\u003Cp>Speaker 0: Vonage is a cloud communication platform that lets you integrate messaging, voice, and video into your applications. And today on Quick Connect, we're going to set up a system that will allow us to send messages directly to users from an item page. So let's get started. On the Vonage side, you're going to need a couple of things. Firstly, you're going to need a Vonage API account.\u003C\u002Fp>\u003Cp>And with that account, you will get an API key and an API secret, which we're going to need in a moment. You're also going to need a phone number from the Vonage programmable numbers product line. So you can go ahead, grab a new number straight here in the dashboard, and make sure that that number has the SMS feature enabled. Now that's everything we need over here, so take note of your API key, secret, and the phone number, and then head over to your director's project. Before we build out this connection to Vonage, I wanna just take a note that I've created this orders collection.\u003C\u002Fp>\u003Cp>And in that orders collection, there is a phone field. That will be the user that that will be the phone number that receives the our text message. Let's go and create a new flow here, and we will call this send SMS. And we'll go ahead and set this based on a manual trigger. We'll do it on just the orders collection, and it will require a confirmation dialogue.\u003C\u002Fp>\u003Cp>So this is like a pop up box that can collect more information before triggering the flow so we'll call this one message text text we'll make it a text box and hit save. So we'll get to see what that confirmation dialogue does. Let's go back to our order, and we see here in the sidebar, there's now a send SMS button. This is the confirmation dialogue. So let's write a message, hello, world, bedtime lucky world, and we'll run flow on current item.\u003C\u002Fp>\u003Cp>Let's go back to the flow and make sure that's worked. We see over here, there's a one on the log, So let's drop that down, and we get to see here in the payload is, inside of the body of the payload, the text that we entered in the box and the ID of the order. So now we're gonna go ahead and do something with this. Now you'll notice we have the key for the order, but what we don't have is the actual phone number, the actual contents itself. And this is actually all that's passed when you do a manual, manual triggered flow.\u003C\u002Fp>\u003Cp>So let's go ahead and grab the whole record to make sure we have the phone number. So let's call this get order get order, and we are going to set that to read data. So let's go ahead and set this to full access, get the orders collection. And what we'll do here is we will get a dynamic value from the trigger. So trigger trigger dot body dot keys, and we just want the first item there.\u003C\u002Fp>\u003Cp>So let's hit save. Save again. And with every step, we're gonna make sure this this works. So let's go back to our order. Let's run the flow again, and we'll head back to the flow.\u003C\u002Fp>\u003Cp>And we see now there's a second log. So in get order, we now have the full record, including the phone number. Take note again that this operation has a key of get underscore order. Next, we're gonna go and actually send a text message. So let's call this one send message.\u003C\u002Fp>\u003Cp>Send message. And we're going to do this by requesting a URL. Now I've taken a look at the Vonage API documentation ahead of time, so I can shortcut us a little bit here, which is that we, in order to send an SMS message, need to send a post request to this URL, res.nextmo.com\u002Fsms \u002Fjson. Now there are a set of query parameters we require in order to make this successful. Firstly, we need to specify who's going to receive the text message.\u003C\u002Fp>\u003Cp>So we have that and get underscore order because that's this operation here dot phone. Next, we need to say who are we sending this from. And so I've actually just copied and pasted my Vonage phone number that I showed you at the beginning. So to and from. Great.\u003C\u002Fp>\u003Cp>Next is the actual message contents itself. So trigger dot body dot text. So that's what we inserted in the box. And then finally, we need to provide our API key and API secret here, which I'm just copying and pasting. API key and API secret.\u003C\u002Fp>\u003Cp>So let's hit save now, save again, and let's go and retrigger this. Oh, hello to your phone. Run flow on current item. And, hopefully, now if I go over there you go. So this is just, this is just the messages app on on macOS piping the text message right here.\u003C\u002Fp>\u003Cp>So that is a little example of how you can connect, Vonage and Directus in order to send SMS messages on demand. Now there are loads of ways you can achieve this. Here, we've used directors flows, and we've kind of hard coded some values inside of this request here. And you can also build custom operations in order to kind of abstract this away and make it a little bit easier so you don't have to provide all of these query parameters every time. And, of course, this doesn't have to be indirect as automate and flows.\u003C\u002Fp>\u003Cp>It can be over in our insights dashboards, or it can be in a hook and so many other ways. But this is a little starting point to see how you can send SMS, messages, text messages using Vonage and Directus together. Hope you've enjoyed this episode of Quick Connect, and I'll see you in the next episode.\u003C\u002Fp>","Vonage is a cloud communication platform that lets you integrate messaging, voice, and video into your applications. And today on Quick Connect, we're going to set up a system that will allow us to send messages directly to users from an item page. So let's get started. On the Vonage side, you're going to need a couple of things. Firstly, you're going to need a Vonage API account. And with that account, you will get an API key and an API secret, which we're going to need in a moment. You're also going to need a phone number from the Vonage programmable numbers product line. So you can go ahead, grab a new number straight here in the dashboard, and make sure that that number has the SMS feature enabled. Now that's everything we need over here, so take note of your API key, secret, and the phone number, and then head over to your director's project. Before we build out this connection to Vonage, I wanna just take a note that I've created this orders collection. And in that orders collection, there is a phone field. That will be the user that that will be the phone number that receives the our text message. Let's go and create a new flow here, and we will call this send SMS. And we'll go ahead and set this based on a manual trigger. We'll do it on just the orders collection, and it will require a confirmation dialogue. So this is like a pop up box that can collect more information before triggering the flow so we'll call this one message text text we'll make it a text box and hit save. So we'll get to see what that confirmation dialogue does. Let's go back to our order, and we see here in the sidebar, there's now a send SMS button. This is the confirmation dialogue. So let's write a message, hello, world, bedtime lucky world, and we'll run flow on current item. Let's go back to the flow and make sure that's worked. We see over here, there's a one on the log, So let's drop that down, and we get to see here in the payload is, inside of the body of the payload, the text that we entered in the box and the ID of the order. So now we're gonna go ahead and do something with this. Now you'll notice we have the key for the order, but what we don't have is the actual phone number, the actual contents itself. And this is actually all that's passed when you do a manual, manual triggered flow. So let's go ahead and grab the whole record to make sure we have the phone number. So let's call this get order get order, and we are going to set that to read data. So let's go ahead and set this to full access, get the orders collection. And what we'll do here is we will get a dynamic value from the trigger. So trigger trigger dot body dot keys, and we just want the first item there. So let's hit save. Save again. And with every step, we're gonna make sure this this works. So let's go back to our order. Let's run the flow again, and we'll head back to the flow. And we see now there's a second log. So in get order, we now have the full record, including the phone number. Take note again that this operation has a key of get underscore order. Next, we're gonna go and actually send a text message. So let's call this one send message. Send message. And we're going to do this by requesting a URL. Now I've taken a look at the Vonage API documentation ahead of time, so I can shortcut us a little bit here, which is that we, in order to send an SMS message, need to send a post request to this URL, res.nextmo.com\u002Fsms \u002Fjson. Now there are a set of query parameters we require in order to make this successful. Firstly, we need to specify who's going to receive the text message. So we have that and get underscore order because that's this operation here dot phone. Next, we need to say who are we sending this from. And so I've actually just copied and pasted my Vonage phone number that I showed you at the beginning. So to and from. Great. Next is the actual message contents itself. So trigger dot body dot text. So that's what we inserted in the box. And then finally, we need to provide our API key and API secret here, which I'm just copying and pasting. API key and API secret. So let's hit save now, save again, and let's go and retrigger this. Oh, hello to your phone. Run flow on current item. And, hopefully, now if I go over there you go. So this is just, this is just the messages app on on macOS piping the text message right here. So that is a little example of how you can connect, Vonage and Directus in order to send SMS messages on demand. Now there are loads of ways you can achieve this. Here, we've used directors flows, and we've kind of hard coded some values inside of this request here. And you can also build custom operations in order to kind of abstract this away and make it a little bit easier so you don't have to provide all of these query parameters every time. And, of course, this doesn't have to be indirect as automate and flows. It can be over in our insights dashboards, or it can be in a hook and so many other ways. But this is a little starting point to see how you can send SMS, messages, text messages using Vonage and Directus together. Hope you've enjoyed this episode of Quick Connect, and I'll see you in the next episode.",[5609],"b477a853-5181-4816-9ef9-be50b2258204",[],{"year":2380,"number":194,"id":4605,"episodes":5612,"show":5613},[4607,4608,4609,4610,4611,4612,4613,4590],{"title":1746,"slug":1747},{"id":4794,"slug":5615,"vimeo_id":5616,"description":5617,"tile":5618,"length":13,"resources":12,"people":12,"episode_number":264,"published":5422,"title":5619,"video_transcript_html":5620,"video_transcript_text":5621,"content":12,"seo":12,"status":19,"episode_people":5622,"recommendations":5623,"season":5624},"passive-aggressive-bug","891433424","The phrase \"to be a fly on the wall\" takes on new meaning.","ed6a5d0b-e190-4157-a582-22ebcc0bfcae","Passive Aggressive Bug","\u003Cp>Speaker 0: And now, Dev Thoughts by Jackrabbit. If bugs could talk, I bet they'd be the most passive aggressive coworkers. Oh, you changed the method name and didn't tell me? No. It's fine.\u003C\u002Fp>\u003Cp>I'll just crash the program, again.\u003C\u002Fp>","And now, Dev Thoughts by Jackrabbit. If bugs could talk, I bet they'd be the most passive aggressive coworkers. Oh, you changed the method name and didn't tell me? No. It's fine. I'll just crash the program, again.",[],[],{"year":2380,"number":159,"id":4790,"episodes":5625,"show":5626},[4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803,4804,4805,4779],{"title":3380,"slug":3381},{"id":2390,"slug":5628,"vimeo_id":5629,"description":5630,"tile":5631,"length":177,"resources":12,"people":5632,"episode_number":11,"published":5634,"title":5635,"video_transcript_html":5636,"video_transcript_text":5637,"content":12,"seo":12,"status":19,"episode_people":5638,"recommendations":5640,"season":5641},"10-8","895934298","Directus 10.8 has huge new updates for our theming engine, including a new extension type and awesome tools to build themes visually.","7b7ebb64-ea8a-4614-b967-b097367bcd1c",[5633],{"name":2820,"url":2821},"2023-12-20","What's New In Directus Version 10.8","\u003Cp>Speaker 0: Hello. My name is Kevin and today I get to tell you what's new in Directus version 10.8. New custom theming options. In Directus 10.7, we released a new theming engine where you could set variables for a bunch of different theming options inside of Directus, and we would take care of the mapping between those values and the data studio markup. Now in directors 10.8, we're adding new options to this section in settings, including the ability to style forms, borders, more colors, public pages and popovers.\u003C\u002Fp>\u003Cp>Introducing custom themes as an extension type. Instead of just altering the existing light and dark themes inside of Directus, you can now create and distribute custom themes. And the cool thing is that you can use the theming default settings in the Directus Data Studio to make a theme that kind of feels like what you want. And then copy the value out of that interface and use it as the theme extension rules, which basically means you can develop custom themes kind of inside of the Director's Data Studio. A really cool experience to knowing exactly how it's going to look and feel.\u003C\u002Fp>\u003Cp>Community contributions. Like always, we have to thank the community for their contributions towards the release of Directus 10.8. Thank you to JD for adding the ability to ignore specific paths from the HTTP logger and thank you to Benjamin for allowing the use of custom collection icons in the draw header. Just a short release note today, but we hope you love the changes we've made to Directus 10.8. It's now available on both Docker Hub and on NPM.\u003C\u002Fp>\u003Cp>And we'll see you in the next release. Bye for now.\u003C\u002Fp>","Hello. My name is Kevin and today I get to tell you what's new in Directus version 10.8. New custom theming options. In Directus 10.7, we released a new theming engine where you could set variables for a bunch of different theming options inside of Directus, and we would take care of the mapping between those values and the data studio markup. Now in directors 10.8, we're adding new options to this section in settings, including the ability to style forms, borders, more colors, public pages and popovers. Introducing custom themes as an extension type. Instead of just altering the existing light and dark themes inside of Directus, you can now create and distribute custom themes. And the cool thing is that you can use the theming default settings in the Directus Data Studio to make a theme that kind of feels like what you want. And then copy the value out of that interface and use it as the theme extension rules, which basically means you can develop custom themes kind of inside of the Director's Data Studio. A really cool experience to knowing exactly how it's going to look and feel. Community contributions. Like always, we have to thank the community for their contributions towards the release of Directus 10.8. Thank you to JD for adding the ability to ignore specific paths from the HTTP logger and thank you to Benjamin for allowing the use of custom collection icons in the draw header. Just a short release note today, but we hope you love the changes we've made to Directus 10.8. It's now available on both Docker Hub and on NPM. And we'll see you in the next release. Bye for now.",[5639],"8566cd06-215b-4da6-a09a-e5a1249c2c93",[],{"year":2380,"number":588,"id":2381,"episodes":5642,"show":5643},[2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394,2366],{"title":2396,"slug":2397},{"id":2389,"slug":5645,"vimeo_id":5646,"description":5647,"tile":5648,"length":129,"resources":12,"people":5649,"episode_number":129,"published":5651,"title":5652,"video_transcript_html":5653,"video_transcript_text":5654,"content":12,"seo":12,"status":19,"episode_people":5655,"recommendations":5657,"season":5658},"10-7","894077828","Kevin runs through all of the changes in Directus 107 - including content versioning, a new theming engine, improvements to Directus Insights, and more.","7af5f31f-26ef-495a-a990-6ed002409151",[5650],{"name":2820,"url":2821},"2023-11-15","What's New In Directus Version 10.7","\u003Cp>Speaker 0: Hello. My name is Kevin. I run developer relations here at Directus and, today, I have the pleasure of telling you what's new in the recently released director's version 10.7. Content versioning. Content versioning is now available in the director's editor and this feature was actually built with you, our community.\u003C\u002Fp>\u003Cp>We opened it up to preview a few months ago and then based on all your feedback, we were able to adjust it ahead of release. Now, content versioning unlocks some really powerful authoring workflows. Firstly, the ability to prepare content and updates to content before publishing it and, secondly, the ability to collaborate with others without accidentally overwriting content. Now, you enable content versioning on a collection and then inside of the item you can go ahead and create new content versions. Every item has its own content versions.\u003C\u002Fp>\u003Cp>You can go ahead and make the changes you want and when you're ready, you can promote some or all fields into the main version which generally in most use cases will then go ahead and publish the content. Now there's an API for managing content versions and new events which are emitted, which you can then use inside of directors Automate. So that's content versioning. Next is the theming engine. Before Director's 10.7, we allowed you to add custom CSS to the director's data studio to make it feel more your own.\u003C\u002Fp>\u003Cp>Well, in directors 10.7, we're not getting rid of that but we are additionally adding a new theming engine. This allows you to configure a bunch of variables to make Directus more themed appropriate to your organisation or project and then you can leave the mapping of those values to the actual markup to us and that means if we ever change the markup, we will also make sure that these variables are respected so you can be more confident in your custom theming. What we have today are just a few variables, but we are constantly adding more as we release new patch and minor releases of Direct Updates to Directus Insights. There have been loads of updates to Directus Insights. Firstly, new style notation and unit options in quite a few of our panels so you can customise the way they look and feel.\u003C\u002Fp>\u003Cp>We have multi line series charts so you can plot data from multiple collections inside of one panel which will allow you to hopefully understand a little more the relationship between your various datasets. There is a new metric list panel available which is well suited for things like seeing the top selling products perhaps in the last quarter. There's some new administration options like duplicating, bulk actions and exporting and importing dashboards And finally, a new panel selection UI which allow you to better illustrate what each of the panels do, and this extends to custom panels as well. There's a ton more inside of directors 10.7 but do check out the updates to directors insights. Secure extensions.\u003C\u002Fp>\u003Cp>Okay. Okay. We've been talking about the director's marketplace for years but what's really important is that we set the correct foundations that we can be confident on for years to come. Part of that is the secure extensions framework and this is a way of building highly sandboxed extensions which have to request additional scopes, additional permissions in order to do things like external web requests and talking to data in your collections. So as part of directors 10.7, we're shipping the secure extensions framework which we'll continue to build upon and this will be really important.\u003C\u002Fp>\u003Cp>You'll be hearing a lot more about it as we march towards working on and releasing a marketplace. Community contributions. This is the section where we thank all of the lovely community members who have contributed since the director's 10.6.0 release. Let's start with the new features and improvements. Thank you to Dominic for making the v button component a little more flexible by adding a target prop for when you're adding external links.\u003C\u002Fp>\u003Cp>Thank you to Gerard for adding auto detection of delimiters for CSV imports and, in turn, allowing for the use of other delimiters instead of just a comma. Thank you to Rob for adding cache clear as a method to the utils service and thank you to Bram for making API errors available through the directives errors package. Now let's talk about the bug fixes and optimizations. Thank you to the team at Gitstart for enhancing the functionality of our checkboxes tree interface with accent sensitive search. Thank you to Christian for contributing an optimization which otherwise was creating the app the director's data studio to sometimes throw errors when the user had a slow internet connection changing the order of operations during the startup.\u003C\u002Fp>\u003Cp>Thank you to Diego for exposing the auth create and auth update filter events to flows. Thank you to Stanislaw for 2 contributions. 1 which fixed the rendering of the user pop over for users without a role and the other which fixed updating the file name and file extension when you replace an asset in director's files. Thank you to Jean Baptiste for fixing the loading of JSON and YAML configuration files and Donald for adding the ability to replace files from within the item details drawer. Thank you to MSC BPI for fixing range requests when using the Azure storage driver and Leo for fixing an issue that would prevent the filter object from functioning in certain WebSocket subscriptions.\u003C\u002Fp>\u003Cp>And, finally, thanks to Zahir for making the transformation argument of the asset service get asset method optional. Before now, it wasn't optional so thank you for that. And finally, to documentation. Firstly, thank you to Chet and Tom for fixing broken links and typos in our documentation and thank you to Andre for adding a Google SAML example to our documentation. Everything we've spoken about today in terms of new features in Directus 10.7 were released as part of our very first leap week which was a week of announcements.\u003C\u002Fp>\u003Cp>That means that every single one of these features already has videos, documentation, and announcement posts. So if you want to learn more, head to the director's blog. Directors 10.7 is now available on NPM and Docker Hub so please do try it out and let us know what you think. Have a wonderful day and we'll see you in the next release.\u003C\u002Fp>","Hello. My name is Kevin. I run developer relations here at Directus and, today, I have the pleasure of telling you what's new in the recently released director's version 10.7. Content versioning. Content versioning is now available in the director's editor and this feature was actually built with you, our community. We opened it up to preview a few months ago and then based on all your feedback, we were able to adjust it ahead of release. Now, content versioning unlocks some really powerful authoring workflows. Firstly, the ability to prepare content and updates to content before publishing it and, secondly, the ability to collaborate with others without accidentally overwriting content. Now, you enable content versioning on a collection and then inside of the item you can go ahead and create new content versions. Every item has its own content versions. You can go ahead and make the changes you want and when you're ready, you can promote some or all fields into the main version which generally in most use cases will then go ahead and publish the content. Now there's an API for managing content versions and new events which are emitted, which you can then use inside of directors Automate. So that's content versioning. Next is the theming engine. Before Director's 10.7, we allowed you to add custom CSS to the director's data studio to make it feel more your own. Well, in directors 10.7, we're not getting rid of that but we are additionally adding a new theming engine. This allows you to configure a bunch of variables to make Directus more themed appropriate to your organisation or project and then you can leave the mapping of those values to the actual markup to us and that means if we ever change the markup, we will also make sure that these variables are respected so you can be more confident in your custom theming. What we have today are just a few variables, but we are constantly adding more as we release new patch and minor releases of Direct Updates to Directus Insights. There have been loads of updates to Directus Insights. Firstly, new style notation and unit options in quite a few of our panels so you can customise the way they look and feel. We have multi line series charts so you can plot data from multiple collections inside of one panel which will allow you to hopefully understand a little more the relationship between your various datasets. There is a new metric list panel available which is well suited for things like seeing the top selling products perhaps in the last quarter. There's some new administration options like duplicating, bulk actions and exporting and importing dashboards And finally, a new panel selection UI which allow you to better illustrate what each of the panels do, and this extends to custom panels as well. There's a ton more inside of directors 10.7 but do check out the updates to directors insights. Secure extensions. Okay. Okay. We've been talking about the director's marketplace for years but what's really important is that we set the correct foundations that we can be confident on for years to come. Part of that is the secure extensions framework and this is a way of building highly sandboxed extensions which have to request additional scopes, additional permissions in order to do things like external web requests and talking to data in your collections. So as part of directors 10.7, we're shipping the secure extensions framework which we'll continue to build upon and this will be really important. You'll be hearing a lot more about it as we march towards working on and releasing a marketplace. Community contributions. This is the section where we thank all of the lovely community members who have contributed since the director's 10.6.0 release. Let's start with the new features and improvements. Thank you to Dominic for making the v button component a little more flexible by adding a target prop for when you're adding external links. Thank you to Gerard for adding auto detection of delimiters for CSV imports and, in turn, allowing for the use of other delimiters instead of just a comma. Thank you to Rob for adding cache clear as a method to the utils service and thank you to Bram for making API errors available through the directives errors package. Now let's talk about the bug fixes and optimizations. Thank you to the team at Gitstart for enhancing the functionality of our checkboxes tree interface with accent sensitive search. Thank you to Christian for contributing an optimization which otherwise was creating the app the director's data studio to sometimes throw errors when the user had a slow internet connection changing the order of operations during the startup. Thank you to Diego for exposing the auth create and auth update filter events to flows. Thank you to Stanislaw for 2 contributions. 1 which fixed the rendering of the user pop over for users without a role and the other which fixed updating the file name and file extension when you replace an asset in director's files. Thank you to Jean Baptiste for fixing the loading of JSON and YAML configuration files and Donald for adding the ability to replace files from within the item details drawer. Thank you to MSC BPI for fixing range requests when using the Azure storage driver and Leo for fixing an issue that would prevent the filter object from functioning in certain WebSocket subscriptions. And, finally, thanks to Zahir for making the transformation argument of the asset service get asset method optional. Before now, it wasn't optional so thank you for that. And finally, to documentation. Firstly, thank you to Chet and Tom for fixing broken links and typos in our documentation and thank you to Andre for adding a Google SAML example to our documentation. Everything we've spoken about today in terms of new features in Directus 10.7 were released as part of our very first leap week which was a week of announcements. That means that every single one of these features already has videos, documentation, and announcement posts. So if you want to learn more, head to the director's blog. Directors 10.7 is now available on NPM and Docker Hub so please do try it out and let us know what you think. Have a wonderful day and we'll see you in the next release.",[5656],"05b21211-f64c-43a8-b963-397de1757cb3",[],{"year":2380,"number":588,"id":2381,"episodes":5659,"show":5660},[2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394,2366],{"title":2396,"slug":2397},{"id":5662,"slug":5663,"vimeo_id":5664,"description":5665,"tile":5666,"length":3277,"resources":5667,"people":5671,"episode_number":159,"published":5675,"title":5676,"video_transcript_html":5677,"video_transcript_text":5678,"content":12,"seo":12,"status":19,"episode_people":5679,"recommendations":5681,"season":5682},"a25a20b8-6c18-49fb-b0a1-84a57025aebb","custom-operations-take-flows-next-level","894204479","Directus Flows allow you to automate tasks with event-based triggers. In this talk, we show an advanced use case generating reports for medical purposes.","4cdd6dbc-d13b-400d-ab17-7c9c09742450",[5668],{"name":5669,"url":5670},"SUNZINET","https:\u002F\u002Fwww.sunzinet.com\u002Fdirectus-agentur",[5672],{"name":5673,"url":5674},"Stephan Schmitz","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fstephan-schmitz-31391a256\u002F","2023-11-13","Building Advanced Custom Operations for Directus Flows - Berlin Meetup","\u003Cp>Speaker 0: Hello, everyone. I'm Stefan. Sure. I'm gonna do that. Well\u003C\u002Fp>\u003Cp>Speaker 1: Oh, there's a little panel. You can either kill the whole table. There's a little panel on the podium.\u003C\u002Fp>\u003Cp>Speaker 0: On the podium.\u003C\u002Fp>\u003Cp>Speaker 1: And I can mutate that.\u003C\u002Fp>\u003Cp>Speaker 0: Hey. Okay. So, again, my name is Stefan. Hello. Thanks for having me.\u003C\u002Fp>\u003Cp>I haven't prayed to the demo gods as well, so maybe we will have 2 comedy shows today. I don't know. I wanna talk to you today about how custom operations can take your flows to the next level. First of all, a quick introduction. I'm Stefan.\u003C\u002Fp>\u003Cp>I'm a product owner since the beginning of this year, and before that, I was working for 20 years now as a software developer. Yeah. I do have a specific love for JavaScript and open source, but, actually, I'm into every web related technology. And I'm a proud father of 2 boys. I'm cheering for FC Koln, which is not so funny at the moment.\u003C\u002Fp>\u003Cp>Well, at least we are playing in the Bundesliga for now, and also for the San Francisco 40 niners, which is more fun at the moment. I work for SunCinet. We are an award winning full service agency. Won the German brand award design awards, Red Dot Awards. You name it, we won it, several times.\u003C\u002Fp>\u003Cp>Full service means, we do strategy consulting, digital marketing, but, of course, also development of all kinds of enterprise level solutions, which is, ecommerce, platform development, mobile application. You name it, we do it. And, also I wanna highlight, we are the 1st official directors partner agency in Germany, actually, and I think the only one right now. So, if you have any questions about implementing something in directors, there's a short link you can follow, or you can go to our website, or just shout out after the talk, and I'm glad to talk to you about that. We do have 170 colleagues working for us in 8 offices around 3 countries.\u003C\u002Fp>\u003Cp>Our headquarters based in Cologne, but we have offices in Austria and Poland as well. Yeah. That's it. Now, before I want to, talk about flows, I want to show you a few more projects we've built upon, directors just because I thought if I would be here, I want to hear what people actually have built upon directors. So, I want to give you a wide range of what we've built.\u003C\u002Fp>\u003Cp>Just a few slides. First thing, I can actually not drop any names here because of NDA reasons, but the project was pretty cool. We've built a complete live streaming and virtual event platform for a huge car manufacturer, based on directors. They, contacted us in the beginning of the corona crisis where all the live events were actually canceled, and wanted something digital to replace that, but wanted to have their own platform. So we built that based on directors, which was great fun.\u003C\u002Fp>\u003Cp>Directors is actually used here to serve all the content and site configuration. We implemented Keycloak. We have a Go middleware in between, and the front end was built on Nuxt. The most cool thing we've built here was actually a page builder, which wasn't available at the time we built the project. Kind of is available now or will be available soon, is available already.\u003C\u002Fp>\u003Cp>Great. So, yeah, just to give editors a more systems with a page preview, editor preview, and stuff like this. Another recent project, is a mobile application we've built for, the German Art and Exhibition Hall, the Bundeskonsthaler in Bonn, where we use directors to actually serve all the content app configuration. Also, user feedback that is provided through the app is posted into directors, so editors when they create surveys they get the responses just in directors, and they can also send out push notifications to mobile users. We actually misuse direct here.\u003C\u002Fp>\u003Cp>We created a collection where they can just enter a title, a description of the push notification, a target group, and a few other things, and hit save. And we have a hook running afterwards to actually connect to the Firebase cloud messaging API to send the data, send out the push notification, and afterwards it just deletes the record. So, yeah. But, it's nice for the editor. They can use one system to do all they need within the app.\u003C\u002Fp>\u003Cp>That's it. Now, let's go to the hot shit, directors flows. Any one of you have ever worked with directors flows, except for you guys working for directors? 1. Okay.\u003C\u002Fp>\u003Cp>That's great because, otherwise, you can tell me something about flows. Now, for all those who haven't worked with flows, let me give you a quick introduction. Directus flows is pretty cool. You have a fancy low code drag and drop editor right in the data center, where you can create workflows to of several, yeah, of several types. Common use cases are data workflows, where you, for example, want to deploy as a website whenever content changes or gets created, or content workflows, where you might want to send an email to a chief editor, as soon as you want to publish an article, or scheduled workflows, like when you want to create automated reports on a nightly basis or stuff like this.\u003C\u002Fp>\u003Cp>And, the cool thing is you don't need to code anymore. You can just go to this fancy drag and drop editor and create your flows right in the data center. Flows are being defined by 3 elements, A trigger, then you have operations, 1 or more operations, and a data chain. Now, Let's quickly forget about the data chain and talk about trigger and operations. When you create a new flow in the data center, you can choose from several predefined, triggers, which can be an event when data changes or something like this.\u003C\u002Fp>\u003Cp>You can create a trigger when an incoming webhook, hits, the director's instance, scheduled scheduled triggers like what I said before, or manual triggers, or even another flow can trigger your flow. So the first thing is you create the trigger for your flow. You choose those from the predefined ones, and this actually defines what starts the flow. And then, we have 1 or more operations which follow the trigger, and if you chose the flow in directors in the data center, you also have multiple operations predefined you can choose from. Like you can implement conditional checks, or you can do all of the CRUD operations, create, read, update data, or whatever.\u003C\u002Fp>\u003Cp>You can send out emails. Like what I said before, you want to send out emails to a chief editor, you can just use this predefined operation and don't need to configure anything. Directus takes care of it. You can, ping other web services, and stuff like this, or transform your payload. So, a lot of predefined operations you can choose from, very powerful.\u003C\u002Fp>\u003Cp>You can connect them and create all your data flows. Now, there's one specific operation, predefined operation, which is the run script operation. So if those predefined operations doesn't do what you want, you can extend on this by choose run script operation, and then you get this code editor where you can put in your custom JavaScript code to be executed. So to give you an idea, I took this from a great video series about directors flows by Bryant, actually, from the directors team. I will share the link with you in the end of the talk.\u003C\u002Fp>\u003Cp>And in this example, this is a flow to generate testing data, seed data. The flow is a manual flow. It is defined to show a button in the collection, and when you click on it, it will run this run script operation, and I I condensed this of course for this, talk here, but what it does is actually it will run a custom JavaScript code to generate the seed data and returns this data actually to the next step in the flow to the next operation, which is again using one of the predefined operations to then create those, data that you received from your run script operation. So this is super powerful. However, it has a few limitations, because this code is executed in a sandbox.\u003C\u002Fp>\u003Cp>It has no network access. It has no file system access, and at least there's no easy way to include third party dependencies into this. So if you want to build more, it's kind of hard to put all your code into this. If you want to use the file system for whatever reason you cannot even do this, so this is where custom operations comes into play. Custom operations allow you to create new types of actions for your flows.\u003C\u002Fp>\u003Cp>So, for example, you can expose an MPN package to, as a custom operation to extend the flow. In this case, we use the camel case function of lodash, to just, actually return, the camel case text, but we can also, let's say for example, taking the example from before, you can include Faker JS to generate your seed data instead of writing all your custom JavaScript codes to generate the seed data. And there's another cool thing actually about it. You see here, that we have the in our handler function, we get the data passed in from our flow, but we also have a second argument, a context argument object, which exposes actually all internal service APIs to us, which is super useful and which is not available in the run script operation. Now, that was the 101 on Calarectus flows and custom operations.\u003C\u002Fp>\u003Cp>Now I want to talk to you about the wall arstvertreteliste. I'm sorry. I have no clue how to translate that into English. Maybe chief physician surrogate list. If you understand what I mean?\u003C\u002Fp>\u003Cp>No. Okay. To give you a quick overview on the project, the client is the University Hospital of Cologne and they asked us to, digitize the Wallas Watriteliste. What is this? So let me quickly try to explain this in English.\u003C\u002Fp>\u003Cp>If you have a private health insurance or if you go to the hospital and just choose to do so and pay for it on your own, you can choose to be seen by the chief physician. And for billing reasons hospitals are required to actually have a persistent documentation on which doctor was the chief physician or its surrogate at any given date? At least in Germany. At least in Germany. So the insurance company might call the hospital or they don't call them, they have access to the documentation, but they if they check the bills and they want to check if it's legit, they go to the system and check.\u003C\u002Fp>\u003Cp>At the 1st May, was doctor Scholz really the the the chief physician for this specific department. So they they are required to have this persisted documentation. Now, the university hospital in Cologne, which is one of the biggest in Germany, has 33 clinics, and they have 235 departments within this 33 clinics. And in the past, this Wallas Watretelister was created manually. So each of the departments, they have a lead.\u003C\u002Fp>\u003Cp>And whenever there's a surrogate for this lead, they were contacting by email the main administration office and say, hey, next week it will be doctor Schmidt's. And they had to create those from huge excel sheets, a PDF file, and put it in the documentation, in the persistent documentation storage. Now let me quickly show you I'll try to show you. Using PowerPoint. Yeah.\u003C\u002Fp>\u003Cp>But So it stays\u003C\u002Fp>\u003Cp>Speaker 1: on top of everything else. You'll probably need to just\u003C\u002Fp>\u003Cp>Speaker 0: out. No. It's because YouTube is still in full screen. So this is the Waldorf Vaterlisten. So we have a huge list of, all the clinics which are on top here.\u003C\u002Fp>\u003Cp>This is the chief physician of of the clinic, and these are the departments listed within the clinics. And if there is no surrogate for the departments then the chief physician for the department is also the chief physician of the clinic, and so on, but they can differ. And now this is the huge list of all the clinics and all the departments, and this was created manually. So and we were asked to actually digitize this. Now when we spoke to the client, we learned that we need a web based interface to do so, and we actually need this to do some data management and for asset downloads.\u003C\u002Fp>\u003Cp>We were required to have a user lock in. We needed a system with fine granular roles and permissions, and we needed server side asset generation and email sent out because, I'll tell you later. So our solution to this was to build something on directors. Server side asset gen asset generation data management fits good. We chose a slightly different approach because, the external companies like the insurance companies, they shouldn't access the director's instance.\u003C\u002Fp>\u003Cp>That was a requirement, so we had to build a custom view. It was built built on view now. We had to build a custom front end for those insurance companies anyway to download this assets. And then we decided to also let the people who work in the OniClinic and set those surrogates to also let them do everything indirect, in the this front end, so all the user groups they all interact with this front end. Directus is actually just running in the background and no one knows about it.\u003C\u002Fp>\u003Cp>Now, fingers crossed, live demo time. So, let me show you how we implemented this using flows and custom operations. So first of all, this is the front end, and let's lock in with as if we were someone working for an insurance company. So this is the interface. They they just can enter, access the archive where they can either, select an individual, date range.\u003C\u002Fp>\u003Cp>Let's say they want to have all the documentation from July August, and they see, okay, there are only 2 lists generated, and they can download it here. Or there's also yearly archives, where they can download a zip file of all the documentation, which were created within a year. Now, let's log out and log in with a user who is working in one of the clinics and want to request a change on the list. Now the user here is working for a specific clinic, and he can only see the departments which are listed in the clinics. Let me quickly show you this in direct us.\u003C\u002Fp>\u003Cp>So we have all the clinics here, and we have the departments here, which are 235, but we can see that we only see 11, which are the departments connected to the specific clinic. And now, I want to change the surrogate for next week. So, if I open one of the items here, one of the departments, I get this calendar view and I can set a surrogate for the next week. So let's say instead of professor doctor Max Musserman, next week it will be me. And if we save this, they they they will also see which, in this calendar view, the ones which are actually requested and which are confirmed.\u003C\u002Fp>\u003Cp>So Yeah. Now, they requested a change. The workflow now requires someone in the administration, and they get an email now. So this is why we required email sent out. They get an email now and say, hey, someone wanted to change a surrogate.\u003C\u002Fp>\u003Cp>Please confirm. So, if they log in, they see all the departments. They can also enter the archive, and they have this new, section here where they see the change requests, and actually, product color for the changes. And now they can go here and see, okay, for this clinic, for this department, from date, start date, end date, he wants. So we can then say, okay, we want to accept this, or we can say we want to decline this, give it a reason.\u003C\u002Fp>\u003Cp>If we decline this, it will also automatically send an email to the email address connected to the director's user who requested the change, and say, well, your change was declined. Please change something or whatever. But now, we want to accept this change. Okay. Now, before we continue with the real workflow, I need to change something.\u003C\u002Fp>\u003Cp>Today is the 18th, so let's say 17th. Okay. Now, let's go to the super cool drag and drop editor of, directors flows. So we have our custom operation here, and this is the generate PDF operation, and we created a flow which actually has a scheduled trigger. It's running once a night, and it's reading the data of the requested changes, of the confirmed changes, in fact.\u003C\u002Fp>\u003Cp>So, we have a filter here. Status equals 1 means so all confirmed changes within the last 24 hours, and then it will pass it to our custom operation. The custom operation actually just gets the data, last, which is an, quick access to get the latest data from the previous, operation, and we have some, yeah, extension paths we need for static asset generation, which are configured here. Now we do have 1956. Let's change this to oh no.\u003C\u002Fp>\u003Cp>Damn. It just changed and now we need to wait 60 seconds. So let's change the crunch up here to run at 58. And while we are waiting, we can actually go back here. You can leave it in.\u003C\u002Fp>\u003Cp>Speaker 1: When I'm testing, I generally change the trigger to just a a webhook, and then I grab the URL, and open it in a new tab, and then it immediately triggers.\u003C\u002Fp>\u003Cp>Speaker 0: It was bad that it just switched to 57. Actually, in all the preparation, it was always like like maximum 10 to, now we got it, like 10 to 15 seconds we needed to wait. I think the lock should be available immediately. Okay. So, in the logs, we saw that our operation was running, and it actually found one change that is great, and it passed this to our custom operation.\u003C\u002Fp>\u003Cp>Now what happened, it actually created it should have created a new PDF, So let's go back to the front end, and here we go. And now if we Demo guts. Let's access the file from here. Yeah, that's right. And have a look if it worked.\u003C\u002Fp>\u003Cp>Great. So, we got the surrogates now here, and the cool thing is actually that we can now set start and end dates on those surrogates, and that not only it doesn't need to be created when on start date, but the main administration doesn't also don't need to care about changing it again when a surrogate expires. So yeah, that's actually it, and now, let's have a quick look at the code, which is probably more helpful if I go to Versus Code later. So this is how custom operations actually look. You can use, the SDK to create 1, and you will get this boilerplate here, which has a actually, this is the API JS file.\u003C\u002Fp>\u003Cp>You have another entry file where you can configure the con, the user interface indirectors, which we saw before where you had this last and the paths, and this is the API JS file where you have your handler function. And as I said earlier, we have the second context argument where we can access all the internal services, like we get the services, we get the database, and all kind of stuff even more. There's the environment variables, the p no longer instance, our accountability information about the current user, so there's a lot of stuff we can access here. And, now, if you remember from the beginning of the run script operation the few limitations, we actually were not able to access the file system, and we were not able to include third party dependencies. I think it's a bit too too small, so I'm gonna switch to here and hope you can all read.\u003C\u002Fp>\u003Cp>So what we do, you see, we import codes from third parties. So, not only will we enclose some note libraries, but we also include archive PDF, which is a wrapper just around JS PDF, which is defined as a dependency in our package JSON. So, we, implement third party codes. We also access the file system later on. You will see that.\u003C\u002Fp>\u003Cp>And from the services we use the file services, and then we actually the steps are to get the data from the database that we need to create, this list, and then there's one keyword I actually need to point out. We cannot specify the encoding actually, in the director's file service, and the default is utf8, which in fact breaks for our custom PDFs. If we would just upload them to the file service, it breaks custom fonts and images. So what we need to do here is actually to save the PDF that we generate with JS PDF to the local file system as a binary file, and then create a read stream on that binary file, and then we can send this to the file service and upload it. And then there's a few more things going on, we actually save it to the archive of the database again, then we check if there's a yearly archive already existent.\u003C\u002Fp>\u003Cp>If so, we delete the zip file that exists, otherwise we create, yeah, and create a new one, otherwise we create a fresh one, and so, yeah. It it's about 500 lines of codes, and yeah. I don't want to show everything here. I think it's enough to get you an idea what what else whatever what you can do with custom operations. You have no such limits that you have with a run script operation, so you can actually use all of the JavaScript power and do whatever you want.\u003C\u002Fp>\u003Cp>So, really, custom operations take your flows to the next level, and I think that's it. Yeah. Quick recap. FlowOS lets you automate data tasks right from within the data center. Really powerful, especially the run script operation, but this one is limited, and we don't have such limits for custom operations.\u003C\u002Fp>\u003Cp>That's basically it. If you want to learn more about directors flows or custom operations, I created, QR codes. I can created a short link which I can share with you to a YouTube playlist with all flows tutorials I found on YouTube. And there's also a quite new section with some cool guides, like how to send bulk emails with SendGrid, how to add item components, how to send SMS notifications with custom operations. Thanks.\u003C\u002Fp>","Hello, everyone. I'm Stefan. Sure. I'm gonna do that. Well Oh, there's a little panel. You can either kill the whole table. There's a little panel on the podium. On the podium. And I can mutate that. Hey. Okay. So, again, my name is Stefan. Hello. Thanks for having me. I haven't prayed to the demo gods as well, so maybe we will have 2 comedy shows today. I don't know. I wanna talk to you today about how custom operations can take your flows to the next level. First of all, a quick introduction. I'm Stefan. I'm a product owner since the beginning of this year, and before that, I was working for 20 years now as a software developer. Yeah. I do have a specific love for JavaScript and open source, but, actually, I'm into every web related technology. And I'm a proud father of 2 boys. I'm cheering for FC Koln, which is not so funny at the moment. Well, at least we are playing in the Bundesliga for now, and also for the San Francisco 40 niners, which is more fun at the moment. I work for SunCinet. We are an award winning full service agency. Won the German brand award design awards, Red Dot Awards. You name it, we won it, several times. Full service means, we do strategy consulting, digital marketing, but, of course, also development of all kinds of enterprise level solutions, which is, ecommerce, platform development, mobile application. You name it, we do it. And, also I wanna highlight, we are the 1st official directors partner agency in Germany, actually, and I think the only one right now. So, if you have any questions about implementing something in directors, there's a short link you can follow, or you can go to our website, or just shout out after the talk, and I'm glad to talk to you about that. We do have 170 colleagues working for us in 8 offices around 3 countries. Our headquarters based in Cologne, but we have offices in Austria and Poland as well. Yeah. That's it. Now, before I want to, talk about flows, I want to show you a few more projects we've built upon, directors just because I thought if I would be here, I want to hear what people actually have built upon directors. So, I want to give you a wide range of what we've built. Just a few slides. First thing, I can actually not drop any names here because of NDA reasons, but the project was pretty cool. We've built a complete live streaming and virtual event platform for a huge car manufacturer, based on directors. They, contacted us in the beginning of the corona crisis where all the live events were actually canceled, and wanted something digital to replace that, but wanted to have their own platform. So we built that based on directors, which was great fun. Directors is actually used here to serve all the content and site configuration. We implemented Keycloak. We have a Go middleware in between, and the front end was built on Nuxt. The most cool thing we've built here was actually a page builder, which wasn't available at the time we built the project. Kind of is available now or will be available soon, is available already. Great. So, yeah, just to give editors a more systems with a page preview, editor preview, and stuff like this. Another recent project, is a mobile application we've built for, the German Art and Exhibition Hall, the Bundeskonsthaler in Bonn, where we use directors to actually serve all the content app configuration. Also, user feedback that is provided through the app is posted into directors, so editors when they create surveys they get the responses just in directors, and they can also send out push notifications to mobile users. We actually misuse direct here. We created a collection where they can just enter a title, a description of the push notification, a target group, and a few other things, and hit save. And we have a hook running afterwards to actually connect to the Firebase cloud messaging API to send the data, send out the push notification, and afterwards it just deletes the record. So, yeah. But, it's nice for the editor. They can use one system to do all they need within the app. That's it. Now, let's go to the hot shit, directors flows. Any one of you have ever worked with directors flows, except for you guys working for directors? 1. Okay. That's great because, otherwise, you can tell me something about flows. Now, for all those who haven't worked with flows, let me give you a quick introduction. Directus flows is pretty cool. You have a fancy low code drag and drop editor right in the data center, where you can create workflows to of several, yeah, of several types. Common use cases are data workflows, where you, for example, want to deploy as a website whenever content changes or gets created, or content workflows, where you might want to send an email to a chief editor, as soon as you want to publish an article, or scheduled workflows, like when you want to create automated reports on a nightly basis or stuff like this. And, the cool thing is you don't need to code anymore. You can just go to this fancy drag and drop editor and create your flows right in the data center. Flows are being defined by 3 elements, A trigger, then you have operations, 1 or more operations, and a data chain. Now, Let's quickly forget about the data chain and talk about trigger and operations. When you create a new flow in the data center, you can choose from several predefined, triggers, which can be an event when data changes or something like this. You can create a trigger when an incoming webhook, hits, the director's instance, scheduled scheduled triggers like what I said before, or manual triggers, or even another flow can trigger your flow. So the first thing is you create the trigger for your flow. You choose those from the predefined ones, and this actually defines what starts the flow. And then, we have 1 or more operations which follow the trigger, and if you chose the flow in directors in the data center, you also have multiple operations predefined you can choose from. Like you can implement conditional checks, or you can do all of the CRUD operations, create, read, update data, or whatever. You can send out emails. Like what I said before, you want to send out emails to a chief editor, you can just use this predefined operation and don't need to configure anything. Directus takes care of it. You can, ping other web services, and stuff like this, or transform your payload. So, a lot of predefined operations you can choose from, very powerful. You can connect them and create all your data flows. Now, there's one specific operation, predefined operation, which is the run script operation. So if those predefined operations doesn't do what you want, you can extend on this by choose run script operation, and then you get this code editor where you can put in your custom JavaScript code to be executed. So to give you an idea, I took this from a great video series about directors flows by Bryant, actually, from the directors team. I will share the link with you in the end of the talk. And in this example, this is a flow to generate testing data, seed data. The flow is a manual flow. It is defined to show a button in the collection, and when you click on it, it will run this run script operation, and I I condensed this of course for this, talk here, but what it does is actually it will run a custom JavaScript code to generate the seed data and returns this data actually to the next step in the flow to the next operation, which is again using one of the predefined operations to then create those, data that you received from your run script operation. So this is super powerful. However, it has a few limitations, because this code is executed in a sandbox. It has no network access. It has no file system access, and at least there's no easy way to include third party dependencies into this. So if you want to build more, it's kind of hard to put all your code into this. If you want to use the file system for whatever reason you cannot even do this, so this is where custom operations comes into play. Custom operations allow you to create new types of actions for your flows. So, for example, you can expose an MPN package to, as a custom operation to extend the flow. In this case, we use the camel case function of lodash, to just, actually return, the camel case text, but we can also, let's say for example, taking the example from before, you can include Faker JS to generate your seed data instead of writing all your custom JavaScript codes to generate the seed data. And there's another cool thing actually about it. You see here, that we have the in our handler function, we get the data passed in from our flow, but we also have a second argument, a context argument object, which exposes actually all internal service APIs to us, which is super useful and which is not available in the run script operation. Now, that was the 101 on Calarectus flows and custom operations. Now I want to talk to you about the wall arstvertreteliste. I'm sorry. I have no clue how to translate that into English. Maybe chief physician surrogate list. If you understand what I mean? No. Okay. To give you a quick overview on the project, the client is the University Hospital of Cologne and they asked us to, digitize the Wallas Watriteliste. What is this? So let me quickly try to explain this in English. If you have a private health insurance or if you go to the hospital and just choose to do so and pay for it on your own, you can choose to be seen by the chief physician. And for billing reasons hospitals are required to actually have a persistent documentation on which doctor was the chief physician or its surrogate at any given date? At least in Germany. At least in Germany. So the insurance company might call the hospital or they don't call them, they have access to the documentation, but they if they check the bills and they want to check if it's legit, they go to the system and check. At the 1st May, was doctor Scholz really the the the chief physician for this specific department. So they they are required to have this persisted documentation. Now, the university hospital in Cologne, which is one of the biggest in Germany, has 33 clinics, and they have 235 departments within this 33 clinics. And in the past, this Wallas Watretelister was created manually. So each of the departments, they have a lead. And whenever there's a surrogate for this lead, they were contacting by email the main administration office and say, hey, next week it will be doctor Schmidt's. And they had to create those from huge excel sheets, a PDF file, and put it in the documentation, in the persistent documentation storage. Now let me quickly show you I'll try to show you. Using PowerPoint. Yeah. But So it stays on top of everything else. You'll probably need to just out. No. It's because YouTube is still in full screen. So this is the Waldorf Vaterlisten. So we have a huge list of, all the clinics which are on top here. This is the chief physician of of the clinic, and these are the departments listed within the clinics. And if there is no surrogate for the departments then the chief physician for the department is also the chief physician of the clinic, and so on, but they can differ. And now this is the huge list of all the clinics and all the departments, and this was created manually. So and we were asked to actually digitize this. Now when we spoke to the client, we learned that we need a web based interface to do so, and we actually need this to do some data management and for asset downloads. We were required to have a user lock in. We needed a system with fine granular roles and permissions, and we needed server side asset generation and email sent out because, I'll tell you later. So our solution to this was to build something on directors. Server side asset gen asset generation data management fits good. We chose a slightly different approach because, the external companies like the insurance companies, they shouldn't access the director's instance. That was a requirement, so we had to build a custom view. It was built built on view now. We had to build a custom front end for those insurance companies anyway to download this assets. And then we decided to also let the people who work in the OniClinic and set those surrogates to also let them do everything indirect, in the this front end, so all the user groups they all interact with this front end. Directus is actually just running in the background and no one knows about it. Now, fingers crossed, live demo time. So, let me show you how we implemented this using flows and custom operations. So first of all, this is the front end, and let's lock in with as if we were someone working for an insurance company. So this is the interface. They they just can enter, access the archive where they can either, select an individual, date range. Let's say they want to have all the documentation from July August, and they see, okay, there are only 2 lists generated, and they can download it here. Or there's also yearly archives, where they can download a zip file of all the documentation, which were created within a year. Now, let's log out and log in with a user who is working in one of the clinics and want to request a change on the list. Now the user here is working for a specific clinic, and he can only see the departments which are listed in the clinics. Let me quickly show you this in direct us. So we have all the clinics here, and we have the departments here, which are 235, but we can see that we only see 11, which are the departments connected to the specific clinic. And now, I want to change the surrogate for next week. So, if I open one of the items here, one of the departments, I get this calendar view and I can set a surrogate for the next week. So let's say instead of professor doctor Max Musserman, next week it will be me. And if we save this, they they they will also see which, in this calendar view, the ones which are actually requested and which are confirmed. So Yeah. Now, they requested a change. The workflow now requires someone in the administration, and they get an email now. So this is why we required email sent out. They get an email now and say, hey, someone wanted to change a surrogate. Please confirm. So, if they log in, they see all the departments. They can also enter the archive, and they have this new, section here where they see the change requests, and actually, product color for the changes. And now they can go here and see, okay, for this clinic, for this department, from date, start date, end date, he wants. So we can then say, okay, we want to accept this, or we can say we want to decline this, give it a reason. If we decline this, it will also automatically send an email to the email address connected to the director's user who requested the change, and say, well, your change was declined. Please change something or whatever. But now, we want to accept this change. Okay. Now, before we continue with the real workflow, I need to change something. Today is the 18th, so let's say 17th. Okay. Now, let's go to the super cool drag and drop editor of, directors flows. So we have our custom operation here, and this is the generate PDF operation, and we created a flow which actually has a scheduled trigger. It's running once a night, and it's reading the data of the requested changes, of the confirmed changes, in fact. So, we have a filter here. Status equals 1 means so all confirmed changes within the last 24 hours, and then it will pass it to our custom operation. The custom operation actually just gets the data, last, which is an, quick access to get the latest data from the previous, operation, and we have some, yeah, extension paths we need for static asset generation, which are configured here. Now we do have 1956. Let's change this to oh no. Damn. It just changed and now we need to wait 60 seconds. So let's change the crunch up here to run at 58. And while we are waiting, we can actually go back here. You can leave it in. When I'm testing, I generally change the trigger to just a a webhook, and then I grab the URL, and open it in a new tab, and then it immediately triggers. It was bad that it just switched to 57. Actually, in all the preparation, it was always like like maximum 10 to, now we got it, like 10 to 15 seconds we needed to wait. I think the lock should be available immediately. Okay. So, in the logs, we saw that our operation was running, and it actually found one change that is great, and it passed this to our custom operation. Now what happened, it actually created it should have created a new PDF, So let's go back to the front end, and here we go. And now if we Demo guts. Let's access the file from here. Yeah, that's right. And have a look if it worked. Great. So, we got the surrogates now here, and the cool thing is actually that we can now set start and end dates on those surrogates, and that not only it doesn't need to be created when on start date, but the main administration doesn't also don't need to care about changing it again when a surrogate expires. So yeah, that's actually it, and now, let's have a quick look at the code, which is probably more helpful if I go to Versus Code later. So this is how custom operations actually look. You can use, the SDK to create 1, and you will get this boilerplate here, which has a actually, this is the API JS file. You have another entry file where you can configure the con, the user interface indirectors, which we saw before where you had this last and the paths, and this is the API JS file where you have your handler function. And as I said earlier, we have the second context argument where we can access all the internal services, like we get the services, we get the database, and all kind of stuff even more. There's the environment variables, the p no longer instance, our accountability information about the current user, so there's a lot of stuff we can access here. And, now, if you remember from the beginning of the run script operation the few limitations, we actually were not able to access the file system, and we were not able to include third party dependencies. I think it's a bit too too small, so I'm gonna switch to here and hope you can all read. So what we do, you see, we import codes from third parties. So, not only will we enclose some note libraries, but we also include archive PDF, which is a wrapper just around JS PDF, which is defined as a dependency in our package JSON. So, we, implement third party codes. We also access the file system later on. You will see that. And from the services we use the file services, and then we actually the steps are to get the data from the database that we need to create, this list, and then there's one keyword I actually need to point out. We cannot specify the encoding actually, in the director's file service, and the default is utf8, which in fact breaks for our custom PDFs. If we would just upload them to the file service, it breaks custom fonts and images. So what we need to do here is actually to save the PDF that we generate with JS PDF to the local file system as a binary file, and then create a read stream on that binary file, and then we can send this to the file service and upload it. And then there's a few more things going on, we actually save it to the archive of the database again, then we check if there's a yearly archive already existent. If so, we delete the zip file that exists, otherwise we create, yeah, and create a new one, otherwise we create a fresh one, and so, yeah. It it's about 500 lines of codes, and yeah. I don't want to show everything here. I think it's enough to get you an idea what what else whatever what you can do with custom operations. You have no such limits that you have with a run script operation, so you can actually use all of the JavaScript power and do whatever you want. So, really, custom operations take your flows to the next level, and I think that's it. Yeah. Quick recap. FlowOS lets you automate data tasks right from within the data center. Really powerful, especially the run script operation, but this one is limited, and we don't have such limits for custom operations. That's basically it. If you want to learn more about directors flows or custom operations, I created, QR codes. I can created a short link which I can share with you to a YouTube playlist with all flows tutorials I found on YouTube. And there's also a quite new section with some cool guides, like how to send bulk emails with SendGrid, how to add item components, how to send SMS notifications with custom operations. Thanks.",[5680],"0538945c-5eac-44c9-b756-0a82d2738018",[],{"year":2380,"number":5683,"id":5684,"episodes":5685,"show":5689},25,"dac1d26f-8071-4acc-a556-3fc30d03496a",[5686,5687,5688,5662],"fa893567-1d64-43e6-9cfd-a79380b49d8e","c1bf3bba-c29b-4d08-91b2-9c53b86d21cf","0edf83f5-87d4-4ea1-8030-0dbf472d228f",{"title":4829,"slug":4830},{"id":5691,"slug":5692,"vimeo_id":5693,"description":5694,"tile":5695,"length":195,"resources":12,"people":5696,"episode_number":213,"published":5700,"title":5701,"video_transcript_html":5702,"video_transcript_text":5703,"content":12,"seo":12,"status":19,"episode_people":5704,"recommendations":5707,"season":5708},"bc63bae0-dec3-4f1b-b825-0da86ebb481c","extend","894066101","At Leap Week 1, we introduced the Secure Extension Framework as a way to gain and have trust with third-party extensions.","cee6bc32-160c-41c5-b981-d92d96b38ecb",[5697,5698],{"name":2820,"url":2821},{"name":3978,"url":5699},"https:\u002F\u002Fdirectus.io\u002Fteam\u002Festher-agbaje","2023-10-27","Extend","\u003Cp>Speaker 0: This has been a huge week of announcements and thank you so much for joining us and being a part of it but we're not quite at the end yet. The idea of a directus marketplace has been around for years. It's the natural next step from having a robust and flexible extensions framework. Now today's announcement isn't the marketplace, but instead the final piece of foundational work required to allow us to work on the marketplace with full force. So let's talk about that foundational work to date.\u003C\u002Fp>\u003Cp>The Director's Extensions SDK provides a toolkit to help you build extensions all the way from scaffolding to working with data and building a polished user interface. You can install extensions via NPM or external storage locations like S3. We've created a detailed yet readable metadata structure for your extensions package JSON file and a host of smaller things, including a standardized way to present errors and more ways that we expose the directus internals for you to take advantage of. But underpinning everything is our need to have confidence in the way that we ask you to build extensions and that that should be more or less the same for many directors versions to come. The secure extensions framework shipping as part of Directus 10.7 is the final part of those foundations, providing an explicit permissions model the extensions will need to adopt to be distributed and adopted in the future.\u003C\u002Fp>\u003Cp>To tell you more, here's Esther.\u003C\u002Fp>\u003Cp>Speaker 1: As developers, we know security is one of the most crucial aspects of building software. Whether you're creating products for users or creating internal tools, you want to be able to innovate freely while also protecting your data from vulnerabilities. Extensions in directors provide a way to build, modify, or expand directors functionality beyond the default for your specific needs. That's why today, we are very excited to announce secure extensions. Secure extensions are a powerful way to maintain strict control over interactions with the external environment when developing extensions.\u003C\u002Fp>\u003Cp>It allows you to configure restrictions on how extensions access your information and communicate externally. In addition to the security they provide, secure extensions also serve as a foundation for the director's marketplace, ensuring that all extensions available for users are developed with security in mind. At the heart of secure extensions is the concept of isolates. An isolate is a secure sandbox where extensions are evaluated and executed. Let's check out an example of how to develop a secure extension.\u003C\u002Fp>\u003Cp>Start by creating an extension like you normally do using the director's extension CLI. Next, open the package dot JSON of the extension and add a sandbox property, which is an object. Within that object, add 2 properties enabled with the value of true and requested scopes, which is an array of function scopes the extension needs access to. By using scopes and the sandbox functions exposed by directors, isolates are granted new and specific capabilities. For example, the request function allows you to make requests to external services.\u003C\u002Fp>\u003Cp>To use the request function, you need to add permissions for the request scope in your extension's metadata, including which external URLs can be accessed by it. Be sure to check out the full list of available sandbox functions in our documentation. We'll continuously be shipping more functions as part of the secure extensions framework, So keep an eye on future releases. With secure extensions laying the groundwork for the marketplace, you can create reliable extensions that enhance the functionality of directors while maintaining the highest standard of data protection. We can't wait to see your contributions and the extensions that you come up with.\u003C\u002Fp>\u003Cp>Over to you, Kev.\u003C\u002Fp>\u003Cp>Speaker 0: And that is the 1st director sleep week wrapped. We covered loads. The release of directors 10.7 comes with content versioning, huge improvements to our insights module, our secure extensions framework, and new app theming options. We also spoke about the evolution of Directus and what it means to treat it as a composable data platform as well as sharing agent c OS with you. And finally, there were some teasers of what comes after secure extensions.\u003C\u002Fp>\u003Cp>Thank you so much for being a part of this. I've had a blast. And on behalf of the whole directors core team, thank you and bye for now.\u003C\u002Fp>","This has been a huge week of announcements and thank you so much for joining us and being a part of it but we're not quite at the end yet. The idea of a directus marketplace has been around for years. It's the natural next step from having a robust and flexible extensions framework. Now today's announcement isn't the marketplace, but instead the final piece of foundational work required to allow us to work on the marketplace with full force. So let's talk about that foundational work to date. The Director's Extensions SDK provides a toolkit to help you build extensions all the way from scaffolding to working with data and building a polished user interface. You can install extensions via NPM or external storage locations like S3. We've created a detailed yet readable metadata structure for your extensions package JSON file and a host of smaller things, including a standardized way to present errors and more ways that we expose the directus internals for you to take advantage of. But underpinning everything is our need to have confidence in the way that we ask you to build extensions and that that should be more or less the same for many directors versions to come. The secure extensions framework shipping as part of Directus 10.7 is the final part of those foundations, providing an explicit permissions model the extensions will need to adopt to be distributed and adopted in the future. To tell you more, here's Esther. As developers, we know security is one of the most crucial aspects of building software. Whether you're creating products for users or creating internal tools, you want to be able to innovate freely while also protecting your data from vulnerabilities. Extensions in directors provide a way to build, modify, or expand directors functionality beyond the default for your specific needs. That's why today, we are very excited to announce secure extensions. Secure extensions are a powerful way to maintain strict control over interactions with the external environment when developing extensions. It allows you to configure restrictions on how extensions access your information and communicate externally. In addition to the security they provide, secure extensions also serve as a foundation for the director's marketplace, ensuring that all extensions available for users are developed with security in mind. At the heart of secure extensions is the concept of isolates. An isolate is a secure sandbox where extensions are evaluated and executed. Let's check out an example of how to develop a secure extension. Start by creating an extension like you normally do using the director's extension CLI. Next, open the package dot JSON of the extension and add a sandbox property, which is an object. Within that object, add 2 properties enabled with the value of true and requested scopes, which is an array of function scopes the extension needs access to. By using scopes and the sandbox functions exposed by directors, isolates are granted new and specific capabilities. For example, the request function allows you to make requests to external services. To use the request function, you need to add permissions for the request scope in your extension's metadata, including which external URLs can be accessed by it. Be sure to check out the full list of available sandbox functions in our documentation. We'll continuously be shipping more functions as part of the secure extensions framework, So keep an eye on future releases. With secure extensions laying the groundwork for the marketplace, you can create reliable extensions that enhance the functionality of directors while maintaining the highest standard of data protection. We can't wait to see your contributions and the extensions that you come up with. Over to you, Kev. And that is the 1st director sleep week wrapped. We covered loads. The release of directors 10.7 comes with content versioning, huge improvements to our insights module, our secure extensions framework, and new app theming options. We also spoke about the evolution of Directus and what it means to treat it as a composable data platform as well as sharing agent c OS with you. And finally, there were some teasers of what comes after secure extensions. Thank you so much for being a part of this. I've had a blast. And on behalf of the whole directors core team, thank you and bye for now.",[5705,5706],"8ebc3999-6d73-4651-ba8b-7ce67861d73e","ffae9b7f-29c7-4f68-ab37-be89f6ddfbc2",[],{"year":2380,"number":4568,"id":5709,"episodes":5710,"show":5715},"3578239b-6b81-4bf3-a0da-8eb437ae76ba",[5711,5712,5713,5714,5691],"3c4afa24-021a-4897-a016-ca0a71808bb0","fb8742d0-e721-49e2-bc90-10bfe538498b","c99fc51a-5f0e-4be0-bad0-6c03de3afc60","052840f3-99fe-451c-acd2-4caa7b454402",{"title":151,"slug":152},{"id":5714,"slug":5717,"vimeo_id":5718,"description":5719,"tile":5720,"length":939,"resources":12,"people":5721,"episode_number":159,"published":5725,"title":3530,"video_transcript_html":5726,"video_transcript_text":5727,"content":12,"seo":12,"status":19,"episode_people":5728,"recommendations":5732,"season":5733},"visualize","894066172","At Leap Week 1, we introduced loads of improvements to Directus Insights and had a Directus community update from our CEO Ben.","425d1a26-015a-4255-8360-5f7d960c1451",[5722,5723,5724],{"name":2820,"url":2821},{"name":4272,"url":4273},{"name":4012,"url":4013},"2023-10-26","\u003Cp>Speaker 0: Directus plays a pivotal role in making your data accessible to everyone in your organization. It's really our core mission to democratize data. Now providing access to your database collections and items through the data studio goes a long way but this is really where Directus Insights shines. If you're not familiar, Directus Insights is our dashboard builder and panels don't have to stop at just displaying data, They can also include forms and other interactive elements providing ways to then act on that data, making dashboards an excellent canvas to build internal and back office applications. We've released lots of great educational content in our docs, on our developer blog, and our YouTube channel to help you build custom panels.\u003C\u002Fp>\u003Cp>And in Directus 10.7, we've given a lot of love to the insights module. I wanna hand over to Connor to tell you more.\u003C\u002Fp>\u003Cp>Speaker 1: Hey, y'all. There have been several dozen improvements to Directus Insights in 10.7. Here they are. Firstly, we've added some useful tools to help you manage your insights dashboards. You can now duplicate entire dashboards, export and import, and apply bulk actions.\u003C\u002Fp>\u003Cp>There's a brand new panel selection user interface, and if you build custom panels, you can provide your own graphic here. It doesn't only look nicer, it does a better job of illustrating what every panel does. You can now comment on dashboards, making them a good canvas for collaboration. Naturally, we've also made enhancements to existing panels, including a bunch of styling options for label and metric panels, number formatting for metric panels, including style, notation, unit, and granular decimal settings, And my favorite, the ability to add multiple series to line charts, which is really useful when understanding your datasets and how they impact each other. Finally, we've released a new metric list panel, which lets you display a list of metrics, such as the top 5 selling products for the quarter, a global leaderboard, or the most viewed pages on your website.\u003C\u002Fp>\u003Cp>There are loads of quality of life improvements in Directus Insights, but you'll have to download Directus 10.7 to discover them. Thank you for having me. I hope\u003C\u002Fp>\u003Cp>Speaker 0: you enjoy all the new features. Bye y'all. Thank you so much for that, Connor. Directus really is the sum of its parts, and that includes you, our wonderful community. So it would give me great pleasure to introduce our CEO and cofounder, Ben, to take the mic and say a few words.\u003C\u002Fp>\u003Cp>Speaker 2: Thank you so much, Kevin. Lots to cover today. This is a really exciting event for us. We have a long history at Directus going back to 2004, when I first created the the platform, as part of my agency in New York City. As some of you might know we based this platform on phpMyAdmin.\u003C\u002Fp>\u003Cp>So that kinda goes to show how, you know, legacy the roots of the system are. We've subsequently open sourced this, you know, years later, Rich joined the team, and we continue to grow throughout 20 years of product development and growing our team. So today, what I'd like to really dive into is the community side of our of our organization, which really is the most important extension of our core team. And we're gonna focus on 5 key aspects of this community that I think really help understand how we got here 20 years later. And we're gonna start off with GitHub.\u003C\u002Fp>\u003Cp>GitHub is inarguably in my opinion one of the most valuable metrics how for how we measure success. I remember, you know, back in 2017 and this is on our our Twitter feed. You can actually see there's a piece of hardware, that just showed this metric, you know, GitHub stars. And I think it was 1400 or thereabouts, back then. Now I have a digital version of the same thing where we are at 23,496.\u003C\u002Fp>\u003Cp>And it would be amazing if we could just push it over you know 24,000 and beyond. But this metric has been so amazing at quantifying, how developers feel about our software. Watching the curve of seeing those GitHub stars go up. We used to be blown away by every single individual star and now it seems like a month doesn't go by where we don't go through a 1,000 stars. So, you know, that has been our guiding light GitHub stars.\u003C\u002Fp>\u003Cp>Those star stargazers. Thank you all so much. The next on that GitHub side of things would really be just contributors. There's the overall contributor network, but who's actually submitting those poll requests to our, our repository. I think we have 350 plus contributors going in and doing pull requests, contributing to our discussions, fixes, and of course taking part in our license change.\u003C\u002Fp>\u003Cp>You know just a few months ago we made a big paradigm shift moving from GPL to BSL and we did this with our community. We did this through GitHub. And so thank you to everybody who's been involved in everything from PRs to figuring out exactly how we make this into a sustainable organization. The third piece of GitHub, would be issues. This is just a really interesting metric.\u003C\u002Fp>\u003Cp>It kinda is the sum of our discussions and pull requests and fixes all of these different components of GitHub. But we actually just a few weeks ago passed 20,000 of these issues which is just an insane number to me. And so, you know, across stars contributors and issues, that's the GitHub side of our community. It's really just the annex of all of our, our work. You know both the code and community, comes together there.\u003C\u002Fp>\u003Cp>Next up, diving into Discord. So if you look at the authoritative, you know, GitHub as where our our source code and we're tracking the project, Discord is more of an ephemeral community building. We have our events. We have, both our core and community located in in Discord. We started off on Slack a little more business oriented, and we shifted over to to Discord and I I believe we passed maybe a month or 2 ago 10,000 members, of this platform which again on the community side is just so meaningful to have people engaging and interacting, asking questions, giving answers, you know, talking about their collaborations and things they've built on our platform.\u003C\u002Fp>\u003Cp>It's just amazing to see that, it's it's, you know, being a little, obsessive about checking all of my channels and seeing all these unread channels that, you know, minutes later, they just there's more people chatting. It's it's just it's just so exciting to see. So beyond sort of our core team, which is in some private channels, our community, which is in all the public channels, we also host a lot of events in our Discord, server. So we have everything from office hours, and request review where we cover you know the process of how we look at our feature requests through GitHub and then we form we go through you know what's how do we decide what comes next. How do we you know evaluate these these requests alongside the business needs and growing the business.\u003C\u002Fp>\u003Cp>So lots to cover. If you want to join those events. You know there's always something going on even in this leap week. We're adding in like ad hoc events you know after these pre recording pre recording things we're actually circling back and you know spending some time on discord chatting about these these releases that we're we're announcing. So join us on those maybe even after this, in Discord.\u003C\u002Fp>\u003Cp>You know, don't mention the core team, but you can certainly come in and join those events and just start those dialogues. Next we have user groups. So user groups is sort of the transition from maybe digital to, you know, in real life. We have, you know, this amazing piece of software. We have this telepresence and videos and all these great events and everything through Discord.\u003C\u002Fp>\u003Cp>But how do you actually bridge the gap into the real world? And we're doing that through regional global events. So our team is global. Our community is global. Our platform is global.\u003C\u002Fp>\u003Cp>And so our events are also global. We've done New York City, and London and Berlin, so far. And really excited to announce right now that we have several other regions that we're that we're pushing imminently. So San Francisco, shout out to our amazing investors, with True Ventures. We have Amsterdam, the home of Rijk Van Zanten, our our CTO and co founder.\u003C\u002Fp>\u003Cp>And we have Paris, coming up where if you've been following along, we have Alex, our new, director of engineering who joined us from the Nuxt team. So he's he's, out of out of Paris or out of, Bordeaux actually. So really exciting to have these these moments to go meet up with our community, have some pizza, hang out and talk shop. And if you want more information, if you're looking for notifications on these events, we actually have an events page on the website. You can go and sign up for those per region.\u003C\u002Fp>\u003Cp>So that's, that's where you can go, for next steps there. 4th is guest authors. So contributing to the platform can come in a lot of different ways. Obviously, you can contribute via code. We talked about events, but content, you know, documentation and articles.\u003C\u002Fp>\u003Cp>How do you use these amazing capabilities? When you have a a toolkit, and there's infinite ways you can piece it together to build different things. And so we love having this guest author program we've just started, which gives us the ability to bring in contributors, to actually have them share the tooling that they're building for Directus. You know, whether it's on Directus or beside Directus extensions, etcetera. Different deployment options.\u003C\u002Fp>\u003Cp>We have, you know, everything under the sun from DigitalOcean or Platform SH. There's so many ways you can deploy it. We can't cover all those. So having guests come in with their domain expertise and talk about you know these different methods is is an amazing avenue to creating that content. I think we even had an like an AI based game that was created using our flow system which is our our automation tool.\u003C\u002Fp>\u003Cp>So really amazing. You can again go to our website and learn more about the guest author program and that's run through GitHub. So same as a pull request or anything else, you just pitch your idea. You'll work with our team and we'll get that formalized and and give you some cash. Next and last is our hackathon.\u003C\u002Fp>\u003Cp>Our hackathons. This is probably, you know, the most exciting thing that we're doing right now. When you talk about a toolkit, you talk about how many options there are. You talk about the modularity and extensibility of Disk. It goes hand in hand with a marketplace with, the extension system.\u003C\u002Fp>\u003Cp>We've built Direct Disk with modularity and extensibility from the ground up. So this is not something we came up with years later. We've truly integrated this into how we thought of the product. You know, if you've heard of our 80 20 rule, we strive to have the core software provide 80% of what you're looking for out of the box. And that other 20% should really be accommodated by not just forking and customizing the open source side, but building extensions and having a really strong developer experience.\u003C\u002Fp>\u003Cp>And so part of that is us working with the community, to look at, you know, what should the theme be for for a certain month and how can we, reward or help incentivize people to go out and build these amazing extensions, that will end up hopefully in this, in a marketplace. So a few months ago we did a AI themed hackathon which was amazing. I'll I'll talk about a few of those, here in a moment. But, it's worth mentioning that we're in the final week of the current hackathon which is around panels. So our dashboarding system called Insights, is again another modular system.\u003C\u002Fp>\u003Cp>You can build anything from a time series to a meter to forecasting or anything else. Submit that into this, hackathon and, you know, several $1,000, you know, donations. Just it's a really great program. But going back to our AI, hackathon of, 2 months ago, we have two ways that we sort of, you know, reward the the winners of those of of each hackathon. 1 is a community award, and 1 is our core team, sort of voting on on what we feel is most meaningful.\u003C\u002Fp>\u003Cp>The community was a really, really amazing one. So this is a AI powered, image processing flow. And what it actually does is it allows you to actually extract text from images or media that gets uploaded to the system in that hook, and also describe, you know, enrich the data with, with metadata from anything that you might upload. So very valuable if you're dealing with a lot of assets, you know, digital asset management system. Just one of the use cases you can power with Directus.\u003C\u002Fp>\u003Cp>That was the community award. So they got, you know, $1,000 cash and we actually donated $250 to the donation of their choice. And on the core side, our core team actually elected Directus Copilot, which is probably familiar with what Copilot is. That was an insights panel where you could actually use NLP, your natural language processing, to ask questions about your data and get meaningful insights. So just really really cool to see what the community comes up with, you know, off the wall, whatever it might be, submit those.\u003C\u002Fp>\u003Cp>And, yeah. We'll be taking a look at the the panel submissions, in just about a week. So those are the 5 key aspects of our community. Obviously the guiding light of this entire organization has been since the beginning. I I couldn't be more proud of our core team.\u003C\u002Fp>\u003Cp>The extension of that being our, you know, additional maintainers and contributors, the global community network that we've built on that has advocated for us and engaged with us across Discord and all these different platforms, not just for Directus, but also for open source, which of course that's what it's what it's all about. So my sincere thank you to everybody in this community, for being a part of this journey. And with that, I'm gonna throw it back to Kevin so we can keep on keeping on. Thanks. Thank you\u003C\u002Fp>\u003Cp>Speaker 0: so much for that, Ben. I couldn't have said it better myself. All of the care and attention we've made to the insights module that we spoke about earlier today is now available in Directus 10.7 which is available now. Now we're starting to get towards the end of Leap Week, but but don't leave us. We have an amazing further announcement for tomorrow, and I'll see you then.\u003C\u002Fp>","Directus plays a pivotal role in making your data accessible to everyone in your organization. It's really our core mission to democratize data. Now providing access to your database collections and items through the data studio goes a long way but this is really where Directus Insights shines. If you're not familiar, Directus Insights is our dashboard builder and panels don't have to stop at just displaying data, They can also include forms and other interactive elements providing ways to then act on that data, making dashboards an excellent canvas to build internal and back office applications. We've released lots of great educational content in our docs, on our developer blog, and our YouTube channel to help you build custom panels. And in Directus 10.7, we've given a lot of love to the insights module. I wanna hand over to Connor to tell you more. Hey, y'all. There have been several dozen improvements to Directus Insights in 10.7. Here they are. Firstly, we've added some useful tools to help you manage your insights dashboards. You can now duplicate entire dashboards, export and import, and apply bulk actions. There's a brand new panel selection user interface, and if you build custom panels, you can provide your own graphic here. It doesn't only look nicer, it does a better job of illustrating what every panel does. You can now comment on dashboards, making them a good canvas for collaboration. Naturally, we've also made enhancements to existing panels, including a bunch of styling options for label and metric panels, number formatting for metric panels, including style, notation, unit, and granular decimal settings, And my favorite, the ability to add multiple series to line charts, which is really useful when understanding your datasets and how they impact each other. Finally, we've released a new metric list panel, which lets you display a list of metrics, such as the top 5 selling products for the quarter, a global leaderboard, or the most viewed pages on your website. There are loads of quality of life improvements in Directus Insights, but you'll have to download Directus 10.7 to discover them. Thank you for having me. I hope you enjoy all the new features. Bye y'all. Thank you so much for that, Connor. Directus really is the sum of its parts, and that includes you, our wonderful community. So it would give me great pleasure to introduce our CEO and cofounder, Ben, to take the mic and say a few words. Thank you so much, Kevin. Lots to cover today. This is a really exciting event for us. We have a long history at Directus going back to 2004, when I first created the the platform, as part of my agency in New York City. As some of you might know we based this platform on phpMyAdmin. So that kinda goes to show how, you know, legacy the roots of the system are. We've subsequently open sourced this, you know, years later, Rich joined the team, and we continue to grow throughout 20 years of product development and growing our team. So today, what I'd like to really dive into is the community side of our of our organization, which really is the most important extension of our core team. And we're gonna focus on 5 key aspects of this community that I think really help understand how we got here 20 years later. And we're gonna start off with GitHub. GitHub is inarguably in my opinion one of the most valuable metrics how for how we measure success. I remember, you know, back in 2017 and this is on our our Twitter feed. You can actually see there's a piece of hardware, that just showed this metric, you know, GitHub stars. And I think it was 1400 or thereabouts, back then. Now I have a digital version of the same thing where we are at 23,496. And it would be amazing if we could just push it over you know 24,000 and beyond. But this metric has been so amazing at quantifying, how developers feel about our software. Watching the curve of seeing those GitHub stars go up. We used to be blown away by every single individual star and now it seems like a month doesn't go by where we don't go through a 1,000 stars. So, you know, that has been our guiding light GitHub stars. Those star stargazers. Thank you all so much. The next on that GitHub side of things would really be just contributors. There's the overall contributor network, but who's actually submitting those poll requests to our, our repository. I think we have 350 plus contributors going in and doing pull requests, contributing to our discussions, fixes, and of course taking part in our license change. You know just a few months ago we made a big paradigm shift moving from GPL to BSL and we did this with our community. We did this through GitHub. And so thank you to everybody who's been involved in everything from PRs to figuring out exactly how we make this into a sustainable organization. The third piece of GitHub, would be issues. This is just a really interesting metric. It kinda is the sum of our discussions and pull requests and fixes all of these different components of GitHub. But we actually just a few weeks ago passed 20,000 of these issues which is just an insane number to me. And so, you know, across stars contributors and issues, that's the GitHub side of our community. It's really just the annex of all of our, our work. You know both the code and community, comes together there. Next up, diving into Discord. So if you look at the authoritative, you know, GitHub as where our our source code and we're tracking the project, Discord is more of an ephemeral community building. We have our events. We have, both our core and community located in in Discord. We started off on Slack a little more business oriented, and we shifted over to to Discord and I I believe we passed maybe a month or 2 ago 10,000 members, of this platform which again on the community side is just so meaningful to have people engaging and interacting, asking questions, giving answers, you know, talking about their collaborations and things they've built on our platform. It's just amazing to see that, it's it's, you know, being a little, obsessive about checking all of my channels and seeing all these unread channels that, you know, minutes later, they just there's more people chatting. It's it's just it's just so exciting to see. So beyond sort of our core team, which is in some private channels, our community, which is in all the public channels, we also host a lot of events in our Discord, server. So we have everything from office hours, and request review where we cover you know the process of how we look at our feature requests through GitHub and then we form we go through you know what's how do we decide what comes next. How do we you know evaluate these these requests alongside the business needs and growing the business. So lots to cover. If you want to join those events. You know there's always something going on even in this leap week. We're adding in like ad hoc events you know after these pre recording pre recording things we're actually circling back and you know spending some time on discord chatting about these these releases that we're we're announcing. So join us on those maybe even after this, in Discord. You know, don't mention the core team, but you can certainly come in and join those events and just start those dialogues. Next we have user groups. So user groups is sort of the transition from maybe digital to, you know, in real life. We have, you know, this amazing piece of software. We have this telepresence and videos and all these great events and everything through Discord. But how do you actually bridge the gap into the real world? And we're doing that through regional global events. So our team is global. Our community is global. Our platform is global. And so our events are also global. We've done New York City, and London and Berlin, so far. And really excited to announce right now that we have several other regions that we're that we're pushing imminently. So San Francisco, shout out to our amazing investors, with True Ventures. We have Amsterdam, the home of Rijk Van Zanten, our our CTO and co founder. And we have Paris, coming up where if you've been following along, we have Alex, our new, director of engineering who joined us from the Nuxt team. So he's he's, out of out of Paris or out of, Bordeaux actually. So really exciting to have these these moments to go meet up with our community, have some pizza, hang out and talk shop. And if you want more information, if you're looking for notifications on these events, we actually have an events page on the website. You can go and sign up for those per region. So that's, that's where you can go, for next steps there. 4th is guest authors. So contributing to the platform can come in a lot of different ways. Obviously, you can contribute via code. We talked about events, but content, you know, documentation and articles. How do you use these amazing capabilities? When you have a a toolkit, and there's infinite ways you can piece it together to build different things. And so we love having this guest author program we've just started, which gives us the ability to bring in contributors, to actually have them share the tooling that they're building for Directus. You know, whether it's on Directus or beside Directus extensions, etcetera. Different deployment options. We have, you know, everything under the sun from DigitalOcean or Platform SH. There's so many ways you can deploy it. We can't cover all those. So having guests come in with their domain expertise and talk about you know these different methods is is an amazing avenue to creating that content. I think we even had an like an AI based game that was created using our flow system which is our our automation tool. So really amazing. You can again go to our website and learn more about the guest author program and that's run through GitHub. So same as a pull request or anything else, you just pitch your idea. You'll work with our team and we'll get that formalized and and give you some cash. Next and last is our hackathon. Our hackathons. This is probably, you know, the most exciting thing that we're doing right now. When you talk about a toolkit, you talk about how many options there are. You talk about the modularity and extensibility of Disk. It goes hand in hand with a marketplace with, the extension system. We've built Direct Disk with modularity and extensibility from the ground up. So this is not something we came up with years later. We've truly integrated this into how we thought of the product. You know, if you've heard of our 80 20 rule, we strive to have the core software provide 80% of what you're looking for out of the box. And that other 20% should really be accommodated by not just forking and customizing the open source side, but building extensions and having a really strong developer experience. And so part of that is us working with the community, to look at, you know, what should the theme be for for a certain month and how can we, reward or help incentivize people to go out and build these amazing extensions, that will end up hopefully in this, in a marketplace. So a few months ago we did a AI themed hackathon which was amazing. I'll I'll talk about a few of those, here in a moment. But, it's worth mentioning that we're in the final week of the current hackathon which is around panels. So our dashboarding system called Insights, is again another modular system. You can build anything from a time series to a meter to forecasting or anything else. Submit that into this, hackathon and, you know, several $1,000, you know, donations. Just it's a really great program. But going back to our AI, hackathon of, 2 months ago, we have two ways that we sort of, you know, reward the the winners of those of of each hackathon. 1 is a community award, and 1 is our core team, sort of voting on on what we feel is most meaningful. The community was a really, really amazing one. So this is a AI powered, image processing flow. And what it actually does is it allows you to actually extract text from images or media that gets uploaded to the system in that hook, and also describe, you know, enrich the data with, with metadata from anything that you might upload. So very valuable if you're dealing with a lot of assets, you know, digital asset management system. Just one of the use cases you can power with Directus. That was the community award. So they got, you know, $1,000 cash and we actually donated $250 to the donation of their choice. And on the core side, our core team actually elected Directus Copilot, which is probably familiar with what Copilot is. That was an insights panel where you could actually use NLP, your natural language processing, to ask questions about your data and get meaningful insights. So just really really cool to see what the community comes up with, you know, off the wall, whatever it might be, submit those. And, yeah. We'll be taking a look at the the panel submissions, in just about a week. So those are the 5 key aspects of our community. Obviously the guiding light of this entire organization has been since the beginning. I I couldn't be more proud of our core team. The extension of that being our, you know, additional maintainers and contributors, the global community network that we've built on that has advocated for us and engaged with us across Discord and all these different platforms, not just for Directus, but also for open source, which of course that's what it's what it's all about. So my sincere thank you to everybody in this community, for being a part of this journey. And with that, I'm gonna throw it back to Kevin so we can keep on keeping on. Thanks. Thank you so much for that, Ben. I couldn't have said it better myself. All of the care and attention we've made to the insights module that we spoke about earlier today is now available in Directus 10.7 which is available now. Now we're starting to get towards the end of Leap Week, but but don't leave us. We have an amazing further announcement for tomorrow, and I'll see you then.",[5729,5730,5731],"3698ff11-c36a-4776-b99d-90e10ab5c8fa","12020eaa-187f-4f64-bd1d-7472e947be61","0bbd3dc8-6276-4235-9980-96500ebbad94",[],{"year":2380,"number":4568,"id":5709,"episodes":5734,"show":5735},[5711,5712,5713,5714,5691],{"title":151,"slug":152},{"id":5713,"slug":5737,"vimeo_id":5738,"description":5739,"tile":5740,"length":159,"resources":12,"people":5741,"episode_number":264,"published":5744,"title":5745,"video_transcript_html":5746,"video_transcript_text":5747,"content":12,"seo":12,"status":19,"episode_people":5748,"recommendations":5751,"season":5752},"customize","894066337","At Leap Week 1, we introduced our brand new theming engine for the Directus Data Studio - a reliable way to set theming options inside of Directus.","2f6a785b-db3c-4952-af82-dd17f598ce3a",[5742,5743],{"name":2820,"url":2821},{"name":3795,"url":3796},"2023-10-25","Customize","\u003Cp>Speaker 0: Welcome back to Leap Week. It's always been important to us that you can make Directus your own not just in terms of what it can do but in terms of how it looks and feels. We already have quite a few custom theming options like setting colors and logos from the very first page that users see right through the data studio. And with custom endpoint extensions, this flexibility runs right through the APIs that directors provides as well. But today, we're gonna expand on what you can do in the Data Studio.\u003C\u002Fp>\u003Cp>Over to Like for more.\u003C\u002Fp>\u003Cp>Speaker 1: We've always allowed admins to add custom CSS to their direct this project, but it can be a little bit hacky and might just break whenever we change the markup in new releases to improve direct this or fix other bugs. Now we're not getting rid of any of these custom styles, but we have a very exciting new way to enhance that ability to make Directus your own. In Directus 10.7, we're launching a completely new theming engine that allows you to define custom themes and customize existing ones. You can do this through a new section in the settings panel. Let me show you how that works.\u003C\u002Fp>\u003Cp>In settings, there is a new theming interface with a set of themable options. Any CSS color, font weight, border ADI, etcetera will be accepted in there and you can save those changes to see them applied immediately. You can treat those new theming options as a sort of promise between the core team and developers that use Directus to make sure that we handle the mapping between the different options and the markup in the app. So that means if we ever change that markup when we implement new features, fix bugs, etcetera, we'll make sure that all of those callers and rules will still work as expected, giving you way more confidence in your custom team within upgrades between versions. This is really, really exciting because of what is happening in the background.\u003C\u002Fp>\u003Cp>What that settings interface is actually doing is really just creating a new Theme file. So anything that you can do within a Theme, you can overwrite within these settings. What's also cool about that is once you're happy with your overrides, you can copy paste that output and save it as a completely new theme. These themes will also be available as custom extensions in the very new future. Today, we're just exposing that light and dark theme you've seen in Directus before, but the intention is to turn themes into its own proper extension type, which also means you can make your own and distribute them to other profits.\u003C\u002Fp>\u003Cp>Gonna be adding more options for the rules in future releases, but we definitely hope you have a lot of fun making Directus feel more new.\u003C\u002Fp>\u003Cp>Speaker 0: We're always working to improve on the customizability of directors so it can really feel like it belongs to your company. This work represents a positive step forward, and we expect to introduce more configuration in this section in future releases. In the meantime, we hope you have fun with it and we'll see you tomorrow.\u003C\u002Fp>","Welcome back to Leap Week. It's always been important to us that you can make Directus your own not just in terms of what it can do but in terms of how it looks and feels. We already have quite a few custom theming options like setting colors and logos from the very first page that users see right through the data studio. And with custom endpoint extensions, this flexibility runs right through the APIs that directors provides as well. But today, we're gonna expand on what you can do in the Data Studio. Over to Like for more. We've always allowed admins to add custom CSS to their direct this project, but it can be a little bit hacky and might just break whenever we change the markup in new releases to improve direct this or fix other bugs. Now we're not getting rid of any of these custom styles, but we have a very exciting new way to enhance that ability to make Directus your own. In Directus 10.7, we're launching a completely new theming engine that allows you to define custom themes and customize existing ones. You can do this through a new section in the settings panel. Let me show you how that works. In settings, there is a new theming interface with a set of themable options. Any CSS color, font weight, border ADI, etcetera will be accepted in there and you can save those changes to see them applied immediately. You can treat those new theming options as a sort of promise between the core team and developers that use Directus to make sure that we handle the mapping between the different options and the markup in the app. So that means if we ever change that markup when we implement new features, fix bugs, etcetera, we'll make sure that all of those callers and rules will still work as expected, giving you way more confidence in your custom team within upgrades between versions. This is really, really exciting because of what is happening in the background. What that settings interface is actually doing is really just creating a new Theme file. So anything that you can do within a Theme, you can overwrite within these settings. What's also cool about that is once you're happy with your overrides, you can copy paste that output and save it as a completely new theme. These themes will also be available as custom extensions in the very new future. Today, we're just exposing that light and dark theme you've seen in Directus before, but the intention is to turn themes into its own proper extension type, which also means you can make your own and distribute them to other profits. Gonna be adding more options for the rules in future releases, but we definitely hope you have a lot of fun making Directus feel more new. We're always working to improve on the customizability of directors so it can really feel like it belongs to your company. This work represents a positive step forward, and we expect to introduce more configuration in this section in future releases. In the meantime, we hope you have fun with it and we'll see you tomorrow.",[5749,5750],"d3171c87-b074-459c-9fcb-4c5dec37a194","6d66dee1-8bca-453f-a81d-401a237b489a",[],{"year":2380,"number":4568,"id":5709,"episodes":5753,"show":5754},[5711,5712,5713,5714,5691],{"title":151,"slug":152},{"id":5756,"slug":5757,"vimeo_id":5758,"description":5759,"tile":5760,"length":159,"resources":12,"people":5761,"episode_number":177,"published":5763,"title":5764,"video_transcript_html":5765,"video_transcript_text":5766,"content":12,"seo":12,"status":19,"episode_people":5767,"recommendations":5769,"season":5770},"1155c102-42e1-4cb8-8f35-3b2d2dbb0376","project-templates-agencyos","894494638","In this video, you'll learn more about project templates and how they work within AgencyOS.","2eec4c80-4a3e-4135-a3dc-8ffef12051ba",[5762],{"name":3590,"url":3591},"2023-10-24","Project Templates In AgencyOS","\u003Cp>Speaker 0: Hi guys. Brian here for Directus. Welcome back to the Agency OS series. In this video, we're going to be talking about project templates. So, imagine that you've been working this deal for this cool organization called Directus and they are ready to move into an actual project.\u003C\u002Fp>\u003Cp>This is where things get a little bit fuzzy before Agency OS. You may have been quoting inside your CRM or just via email and now you're gonna action this project inside Asana or Basecamp or some other tool. But now, with AgentC OS, it's combined into a single platform. Project Templates are gonna help you operationalize your actual Projects. So, we'll click on Create a Project on the right information sidebar.\u003C\u002Fp>\u003Cp>We'll give this project a quick name, New Website Project, select an organization, Directus in this case, select our primary contact, and then we're gonna pick the project template. So these are already preset in Agency OS for you. We'll show you how to customize them in just a few. Then we'll pick a start date for the project. We'll choose a completion date or a target completion date because projects, especially web projects, don't always go according to plan.\u003C\u002Fp>\u003Cp>But that's what we're trying to fix. So now that the project is created, let's just pop in and take a look. One of the nice things about AJC OS and Directus is how flexible it is. So, we'll open up this new website project and I can see I've got all my information prefilled. But the beauty of this, as I scroll down, I've got all of my tasks, my entire project already planned out.\u003C\u002Fp>\u003Cp>That's huge. So we can actually operationalize this. And one of the core features here is the ability to collaborate with clients on Tasks. So you can see this task is marked visible to the client. And if I were to open up the AgencyOS client portal, that task will show up.\u003C\u002Fp>\u003Cp>So we'll just log in to the portal and I could see in the client's task list is that kickoff call, where we would have that detailed information on what that looks like. So that's how project templates work. How do we actually configure these? If you go to the settings tab within the left hand sidebar, you will find your project templates. Now we've gone through and spec'd out several of these project templates for you already.\u003C\u002Fp>\u003Cp>But if we inspect the website design and development one, let's just take a look. You can see the preset task where you can determine the type of task. You can add names, descriptions. You can assign team members. You can make this visible to the client.\u003C\u002Fp>\u003Cp>You can even set dates relevant to the start of the project or when the project is due to start and end this specific task. So you can really systemize each one of your projects and hopefully gain more efficiency than creating this project plan one off every single time. So we've covered how to actually create these project templates. But let's take a peek behind the scenes into the actual flows that create this project. So there is a Create Project Flow inside your settings and it is triggered manually whenever you click the button.\u003C\u002Fp>\u003Cp>It will read the task template or the project template that you set up. It will run some JavaScript, nothing fancy, to format all the tasks within that project. And then create a new project and all the individual tasks for everyone on your team automatically for you just with a couple of clicks. That's it for project templates. Make sure you stay tuned for the other videos in this series for more information on Agent COS.\u003C\u002Fp>\u003Cp>See you.\u003C\u002Fp>","Hi guys. Brian here for Directus. Welcome back to the Agency OS series. In this video, we're going to be talking about project templates. So, imagine that you've been working this deal for this cool organization called Directus and they are ready to move into an actual project. This is where things get a little bit fuzzy before Agency OS. You may have been quoting inside your CRM or just via email and now you're gonna action this project inside Asana or Basecamp or some other tool. But now, with AgentC OS, it's combined into a single platform. Project Templates are gonna help you operationalize your actual Projects. So, we'll click on Create a Project on the right information sidebar. We'll give this project a quick name, New Website Project, select an organization, Directus in this case, select our primary contact, and then we're gonna pick the project template. So these are already preset in Agency OS for you. We'll show you how to customize them in just a few. Then we'll pick a start date for the project. We'll choose a completion date or a target completion date because projects, especially web projects, don't always go according to plan. But that's what we're trying to fix. So now that the project is created, let's just pop in and take a look. One of the nice things about AJC OS and Directus is how flexible it is. So, we'll open up this new website project and I can see I've got all my information prefilled. But the beauty of this, as I scroll down, I've got all of my tasks, my entire project already planned out. That's huge. So we can actually operationalize this. And one of the core features here is the ability to collaborate with clients on Tasks. So you can see this task is marked visible to the client. And if I were to open up the AgencyOS client portal, that task will show up. So we'll just log in to the portal and I could see in the client's task list is that kickoff call, where we would have that detailed information on what that looks like. So that's how project templates work. How do we actually configure these? If you go to the settings tab within the left hand sidebar, you will find your project templates. Now we've gone through and spec'd out several of these project templates for you already. But if we inspect the website design and development one, let's just take a look. You can see the preset task where you can determine the type of task. You can add names, descriptions. You can assign team members. You can make this visible to the client. You can even set dates relevant to the start of the project or when the project is due to start and end this specific task. So you can really systemize each one of your projects and hopefully gain more efficiency than creating this project plan one off every single time. So we've covered how to actually create these project templates. But let's take a peek behind the scenes into the actual flows that create this project. So there is a Create Project Flow inside your settings and it is triggered manually whenever you click the button. It will read the task template or the project template that you set up. It will run some JavaScript, nothing fancy, to format all the tasks within that project. And then create a new project and all the individual tasks for everyone on your team automatically for you just with a couple of clicks. That's it for project templates. Make sure you stay tuned for the other videos in this series for more information on Agent COS. See you.",[5768],"2b4b2ca4-08f4-4b12-bd3c-0d1331f3bea9",[],{"year":2380,"number":722,"id":5771,"episodes":5772,"show":5776},"237e17bc-6e41-460f-8fe7-1d21fd865801",[5773,5756,5774,5775],"6ed0cf93-0868-497e-a21a-97df216f9fa7","cf8b1be3-2bed-45e0-b158-8dacd5afa1f7","4298bc72-ad12-4c76-b57d-506e3d9a0c45",{"title":5777,"slug":5778},"Mastering AgencyOS","mastering-agencyos",{"id":5712,"slug":5780,"vimeo_id":5781,"description":5782,"tile":5783,"length":329,"resources":12,"people":5784,"episode_number":177,"published":5763,"title":5788,"video_transcript_html":5789,"video_transcript_text":5790,"content":12,"seo":12,"status":19,"episode_people":5791,"recommendations":5795,"season":5796},"compose","894066365","At Leap Week 1, we introduced and explained our approach on composability, along with announcing AgencyOS - an all-in-one operating system for digital agencies.","7e6ffdbe-cf3c-409e-b5b8-ba1863b2b04e",[5785,5786,5787],{"name":2820,"url":2821},{"name":4149,"url":4150},{"name":3590,"url":3591},"Compose","\u003Cp>Speaker 0: Welcome back to Leap Week. And today, we want to talk to you about composability and the evolution of Directus into a flexible backend toolkit that can solve for so many use cases. It's fair to say that Directus very much started out life as a headless CMS, an API driven solution for providing data to front end applications. The Directus Data Studio is great for authoring content. And with roles and permissions, it is well suited to authoring and review workflows.\u003C\u002Fp>\u003Cp>But this really just scratches the surface of what Directus is capable of.\u003C\u002Fp>\u003Cp>Speaker 1: Hey, everybody. I'm Matt Miner, director of demand gen at Directus. That's a lot of words to say. I do marketing stuff. Today, I wanna talk about a narrative that we're seeing pop up more and more in the software development world.\u003C\u002Fp>\u003Cp>And before our big announcement, right after this, I just wanna be able to give you a little context so you understand, really, the the full lay of the land of of why we think this is important, and that's this concept of composable architecture. Yes. It's another buzzword that we, as the tech industry, love to make up seemingly every year, for no reason. But this one's actually gaining ground, particularly in larger organizations and for good reason. I wanna preface this with the fact that I've been doing a deep dive for what seems like the last year, but it's actually been the last 6 months, reading reports, talking to customers, and more really just trying to understand what this term means.\u003C\u002Fp>\u003Cp>Spoiler alert. I don't think anybody knows what it means. Everybody is just kinda making stuff up, when it comes to it. So some think it's a mentality in the way you run your organization. I've even seen some of our competitors who will not be named, try to stick claim for it.\u003C\u002Fp>\u003Cp>Call it composable x. But I just wanna, tell you today what we think of it. And if it fits your organization in the way you think about things, then that's great. Really to kick this off, I wanna talk about, one of the most interesting things I came across when I was reading all of these mindless reports and, having great conversations with customers, which is this, concept of Conway's Law. So Conway's Law in of itself, if you think about software development, the traditional sense of it is if you have a team that's distributed globally, they're more likely to create modular architecture.\u003C\u002Fp>\u003Cp>If you have a team that is, located pretty much in the same place, co located, they're more likely to create monolithic architectures. And it makes sense because, you know, global teams usually have their own tech stacks. You think of, like, teams of 7 that are, you know, multiple teams at organizations, and then teams that are together typically are on the same wavelength using the same framework, same tools, things like that. So when you apply this to the concept of composable, there's two sides of the spectrum, and you're probably familiar with this, which is there's the buy, and there's the build. Now as a nontechnical marketer, I typically lean towards the buy side, which is I see a problem, I just go find a software or SaaS that fixes that problem, and then I'm able to quickly get over it, really fast.\u003C\u002Fp>\u003Cp>But the problem is is I have to adapt my the way I work around that. You can think of this getting to a point of where these teams have their own siloed data tech stacks, like, just absolutely crazy. A lot of security concerns there, and we have to adapt to the way that the software works. On the other side of the spectrum is this concept of building, and I think most of the audience here, which is, you know, technical folks, tend to, you know, lean this way, which is, I see a problem. I wanna build something to solve it, and I wanna build it specifically to what our team needs to solve it.\u003C\u002Fp>\u003Cp>So you get plenty of, flexibility on that front, but you're not able to move as fast or as quickly. We think there is value in that side in being as close to being able to build something as flexible as possible for your team. But there is that overhead of maintenance and resources that could be going into something that's, you know, nonessential for for your products or nonessential contributing back to revenue and growing the business. That's why we believe composable to us is, you know, being able to build those apps from elements that everybody understands, the same foundation, that is flexible enough to work with, any framework, Nuxt, Next, React, Vue, and from the same kind of I've already mentioned it, but foundation. So let's talk about where we have been as an industry and where we're going as an industry.\u003C\u002Fp>\u003Cp>Now we're at this crossroads of composable and this concept of it. Previously, everyone's been forced into this buy or build mentality. Like I was saying, nontechnical teams really prefer to buy things. Technical teams tend to wanna build them. Nothing wrong with either choice.\u003C\u002Fp>\u003Cp>There are a few drawbacks. I wanna think about this in the terms of, like let's say you've got a wedding at the end of the week that you just find out about, and you need a suit. You can go to the suit store, buy something off the shelf, and have it, and go to that wedding, and probably get made fun of because you look like a kid in a giant suit. It's not tailored specifically to what you need, but you get it fast, and you're there. The other side of that is going to the suit store, getting fitted for your exact needs, specifications, and then not getting that suit on time, on the timeline you were promised.\u003C\u002Fp>\u003Cp>And then that's even a worse scenario outcome because you're just wearing a T shirt to the wedding. That's really what this old world, new world difference is, is because, you know, let's fast forward to today and what composable enables, and that's you getting custom tailored at the store. And then just like an Amazon delivery, getting it on your doorstep within 1 to 2 days. It's the speed and the flexibility of having both. And we think it is possible.\u003C\u002Fp>\u003Cp>You just need 3 things in place. 1 is you need a hub for all of your teams to be able to work from. That's technical and nontechnical. No code for the nontechnical teams. Low code or heavy code for the technical teams, that everybody can interact on and be able to create the things they need.\u003C\u002Fp>\u003Cp>2nd, you need to have this powered by APIs. So all of the things that you're buying mix with the things you're building, and everything is in beautiful harmony like an orchestra. And then, really, the third thing is it needs to be flexible enough to work with your business. You can be able to cloud host it and not worry about the infrastructure costs and the setup and the maintenance, or you can self host it if security is a large concern of yours and, you need something on prem. We think the flexibility between all three things is really the key to enabling this composable architecture that everybody seems to be talking about.\u003C\u002Fp>\u003Cp>I wanna just talk a little bit about an actual application of this. So we work with Copa Airlines, which is a, you know, $2,700,000,000 airline based in Panama. Over 5,000 employees, 16% increase in engineering headcount last year. Now you're probably aware of the issues that airlines faced earlier this year. A lot of passengers were stranded, because of the legacy tech debt that a lot of them had accrued.\u003C\u002Fp>\u003Cp>And we love Copa because not even just with us as a customer, but they're taking a proactive approach, and, honestly, probably the most proactive we've seen in the industry, in making sure that there is no tech debt and there is technology efficient as possible. We started working with them last year. They came to us for an internal content management system. They wanted to share, like, news with their employees and have, notification systems, so they built that. Within 2 months, they had it up and running.\u003C\u002Fp>\u003Cp>This year, they wanted to move their entire marketing website over to it because they found that their nontechnical and technical teams were able to get a lot of usage out of it. And everybody really enjoyed the experience. So their entire marketing website, which is huge for a company like this because 70% of their sales come from their website, and it has to be reliable. So they launched that on Directus, incredible website. They actually reduced their load speeds from, like, 6 seconds to, I think, like, 1 to 2.\u003C\u002Fp>\u003Cp>So it was a huge boost for them in that arena. And now we're talking to them, about using Directus as, like, kind of an internal tool builder, app builder, things that they can provide to their customers that aren't necessarily just CMS based. So it's really cool to work with them and see this trajectory that they're taking with it. They're finding a lot of usage out of it outside of just that, like, classic CMS, which is where they started. It was the proving ground, and now there's a lot of, flexibility and things that they can do with it.\u003C\u002Fp>\u003Cp>So just to reiterate what I said at the beginning and wrap this up, there's no clear definition of composable architecture. But what brings us confidence in the way that we talk about it and we see the path forward is we see 100 of users doing exactly this every single day. And just to wrap it up with a case in point, we asked our community of 10,000 developers and engineers, like, how would you describe Directus? And the funny thing is we didn't get a single response that was the same. That's awesome for folks that have discovered us and are using us for multiple use cases, but that's not awesome for new potential users that could be overwhelmed by the capabilities and what to do with it.\u003C\u002Fp>\u003Cp>But as we start to refine the product, grow with the awesome support from the community, we feel like there's so much more than it could be used for other than just this content management, use case, which it's really strong for. But there is a lot of other things you can be doing to get value out of it. So today, I wanna introduce my friend, Bryant. He's gonna show you our first step at being a truly all encompassing toolkit for teams that would allow you to move fast, yet scratch that builder itch that I feel like we all have. We call them operating systems because you can truly build full comprehensive software that ties together multiple applications, to help you become more efficient.\u003C\u002Fp>\u003Cp>We're releasing AgencyOS. That's what we've dubbed it, as the first example of an all in one system, that's purpose built to show off some of the incredible functionality that you can build with Directus and truly take a composable approach to your architecture. In the future, expect us to, you know, release and build more, Events OS for managing events, Deals OS from sales teams managing deals, and and so on and so forth. But, again, I'm getting ahead of myself. That's the marketer in me.\u003C\u002Fp>\u003Cp>That's down the road. So, yeah, let me go ahead and kick it over to Bryant to show off, AgencyOS. I think you're really gonna like it, and thanks for listening to my rambling.\u003C\u002Fp>\u003Cp>Speaker 2: Thanks, Matt. Running a digital agency is challenging. There's a load of us in the Directus core team who have done it in a past life. There's so much to it beyond actually delivering projects. And it's those other bits that often make or break client relationships.\u003C\u002Fp>\u003Cp>Yet, to get your operating system set up often means you're stringing together many different rigid off the shelf tools are burning hours and hours of potentially billable time developing your own solutions and practices. Agency OS is everything you need to get your agency off the ground or improve tooling for your existing company. It's based on familiar, extensible open source tools. Directus for the back end and Nuxt for the front end. Here's what it offers.\u003C\u002Fp>\u003Cp>A great looking website to promote your work and convert visitors to leads and booked meetings. Look on brand in minutes. Swap your colors. Add your logos, and set up your fonts using a simple theme config file. It's backed by a super easy to use headless CMS with live preview and content versioning.\u003C\u002Fp>\u003Cp>A back end CRM slash project tracker to manage your entire project workflow. From first contact, creating proposals and managing pipeline, to managing the actual day to day work with projects and tasks. Leverage the project templates to make planning and managing larger complex projects easy peasy. A private client portal to provide superior communication with your clients. Agencies that make it super easy for clients to do business with them will be more successful than those who leave client experience up to chance.\u003C\u002Fp>\u003Cp>Clients can self serve within their portal, get updates on projects, pay their invoices painlessly, and get gently reminded to deliver the files, content, and other info you need to complete their project. Agency OS is the perfect base to compose your own solution. Instead of hoping and praying those off the shelf tools, will finally work together exactly the way you need them. We've just published a bunch of videos to show you how to get the most out of each module. To get started, just follow the setup instructions on our website and you'll be on your way to a better agency in no time.\u003C\u002Fp>\u003Cp>Speaker 0: Directus has come a very long way from where we started, and it really can be the operating system for your entire organization. Today we used agencies as an example but we see real estate leaders, airlines, online stores, and educational institutions base their whole organizational backbone on Directus as a composable data platform. Tomorrow we're heading back to directors 10.7 to announce and highlight our latest release, but for now you can check out agency OS using the links accompanying this video. So until tomorrow, bye for now.\u003C\u002Fp>","Welcome back to Leap Week. And today, we want to talk to you about composability and the evolution of Directus into a flexible backend toolkit that can solve for so many use cases. It's fair to say that Directus very much started out life as a headless CMS, an API driven solution for providing data to front end applications. The Directus Data Studio is great for authoring content. And with roles and permissions, it is well suited to authoring and review workflows. But this really just scratches the surface of what Directus is capable of. Hey, everybody. I'm Matt Miner, director of demand gen at Directus. That's a lot of words to say. I do marketing stuff. Today, I wanna talk about a narrative that we're seeing pop up more and more in the software development world. And before our big announcement, right after this, I just wanna be able to give you a little context so you understand, really, the the full lay of the land of of why we think this is important, and that's this concept of composable architecture. Yes. It's another buzzword that we, as the tech industry, love to make up seemingly every year, for no reason. But this one's actually gaining ground, particularly in larger organizations and for good reason. I wanna preface this with the fact that I've been doing a deep dive for what seems like the last year, but it's actually been the last 6 months, reading reports, talking to customers, and more really just trying to understand what this term means. Spoiler alert. I don't think anybody knows what it means. Everybody is just kinda making stuff up, when it comes to it. So some think it's a mentality in the way you run your organization. I've even seen some of our competitors who will not be named, try to stick claim for it. Call it composable x. But I just wanna, tell you today what we think of it. And if it fits your organization in the way you think about things, then that's great. Really to kick this off, I wanna talk about, one of the most interesting things I came across when I was reading all of these mindless reports and, having great conversations with customers, which is this, concept of Conway's Law. So Conway's Law in of itself, if you think about software development, the traditional sense of it is if you have a team that's distributed globally, they're more likely to create modular architecture. If you have a team that is, located pretty much in the same place, co located, they're more likely to create monolithic architectures. And it makes sense because, you know, global teams usually have their own tech stacks. You think of, like, teams of 7 that are, you know, multiple teams at organizations, and then teams that are together typically are on the same wavelength using the same framework, same tools, things like that. So when you apply this to the concept of composable, there's two sides of the spectrum, and you're probably familiar with this, which is there's the buy, and there's the build. Now as a nontechnical marketer, I typically lean towards the buy side, which is I see a problem, I just go find a software or SaaS that fixes that problem, and then I'm able to quickly get over it, really fast. But the problem is is I have to adapt my the way I work around that. You can think of this getting to a point of where these teams have their own siloed data tech stacks, like, just absolutely crazy. A lot of security concerns there, and we have to adapt to the way that the software works. On the other side of the spectrum is this concept of building, and I think most of the audience here, which is, you know, technical folks, tend to, you know, lean this way, which is, I see a problem. I wanna build something to solve it, and I wanna build it specifically to what our team needs to solve it. So you get plenty of, flexibility on that front, but you're not able to move as fast or as quickly. We think there is value in that side in being as close to being able to build something as flexible as possible for your team. But there is that overhead of maintenance and resources that could be going into something that's, you know, nonessential for for your products or nonessential contributing back to revenue and growing the business. That's why we believe composable to us is, you know, being able to build those apps from elements that everybody understands, the same foundation, that is flexible enough to work with, any framework, Nuxt, Next, React, Vue, and from the same kind of I've already mentioned it, but foundation. So let's talk about where we have been as an industry and where we're going as an industry. Now we're at this crossroads of composable and this concept of it. Previously, everyone's been forced into this buy or build mentality. Like I was saying, nontechnical teams really prefer to buy things. Technical teams tend to wanna build them. Nothing wrong with either choice. There are a few drawbacks. I wanna think about this in the terms of, like let's say you've got a wedding at the end of the week that you just find out about, and you need a suit. You can go to the suit store, buy something off the shelf, and have it, and go to that wedding, and probably get made fun of because you look like a kid in a giant suit. It's not tailored specifically to what you need, but you get it fast, and you're there. The other side of that is going to the suit store, getting fitted for your exact needs, specifications, and then not getting that suit on time, on the timeline you were promised. And then that's even a worse scenario outcome because you're just wearing a T shirt to the wedding. That's really what this old world, new world difference is, is because, you know, let's fast forward to today and what composable enables, and that's you getting custom tailored at the store. And then just like an Amazon delivery, getting it on your doorstep within 1 to 2 days. It's the speed and the flexibility of having both. And we think it is possible. You just need 3 things in place. 1 is you need a hub for all of your teams to be able to work from. That's technical and nontechnical. No code for the nontechnical teams. Low code or heavy code for the technical teams, that everybody can interact on and be able to create the things they need. 2nd, you need to have this powered by APIs. So all of the things that you're buying mix with the things you're building, and everything is in beautiful harmony like an orchestra. And then, really, the third thing is it needs to be flexible enough to work with your business. You can be able to cloud host it and not worry about the infrastructure costs and the setup and the maintenance, or you can self host it if security is a large concern of yours and, you need something on prem. We think the flexibility between all three things is really the key to enabling this composable architecture that everybody seems to be talking about. I wanna just talk a little bit about an actual application of this. So we work with Copa Airlines, which is a, you know, $2,700,000,000 airline based in Panama. Over 5,000 employees, 16% increase in engineering headcount last year. Now you're probably aware of the issues that airlines faced earlier this year. A lot of passengers were stranded, because of the legacy tech debt that a lot of them had accrued. And we love Copa because not even just with us as a customer, but they're taking a proactive approach, and, honestly, probably the most proactive we've seen in the industry, in making sure that there is no tech debt and there is technology efficient as possible. We started working with them last year. They came to us for an internal content management system. They wanted to share, like, news with their employees and have, notification systems, so they built that. Within 2 months, they had it up and running. This year, they wanted to move their entire marketing website over to it because they found that their nontechnical and technical teams were able to get a lot of usage out of it. And everybody really enjoyed the experience. So their entire marketing website, which is huge for a company like this because 70% of their sales come from their website, and it has to be reliable. So they launched that on Directus, incredible website. They actually reduced their load speeds from, like, 6 seconds to, I think, like, 1 to 2. So it was a huge boost for them in that arena. And now we're talking to them, about using Directus as, like, kind of an internal tool builder, app builder, things that they can provide to their customers that aren't necessarily just CMS based. So it's really cool to work with them and see this trajectory that they're taking with it. They're finding a lot of usage out of it outside of just that, like, classic CMS, which is where they started. It was the proving ground, and now there's a lot of, flexibility and things that they can do with it. So just to reiterate what I said at the beginning and wrap this up, there's no clear definition of composable architecture. But what brings us confidence in the way that we talk about it and we see the path forward is we see 100 of users doing exactly this every single day. And just to wrap it up with a case in point, we asked our community of 10,000 developers and engineers, like, how would you describe Directus? And the funny thing is we didn't get a single response that was the same. That's awesome for folks that have discovered us and are using us for multiple use cases, but that's not awesome for new potential users that could be overwhelmed by the capabilities and what to do with it. But as we start to refine the product, grow with the awesome support from the community, we feel like there's so much more than it could be used for other than just this content management, use case, which it's really strong for. But there is a lot of other things you can be doing to get value out of it. So today, I wanna introduce my friend, Bryant. He's gonna show you our first step at being a truly all encompassing toolkit for teams that would allow you to move fast, yet scratch that builder itch that I feel like we all have. We call them operating systems because you can truly build full comprehensive software that ties together multiple applications, to help you become more efficient. We're releasing AgencyOS. That's what we've dubbed it, as the first example of an all in one system, that's purpose built to show off some of the incredible functionality that you can build with Directus and truly take a composable approach to your architecture. In the future, expect us to, you know, release and build more, Events OS for managing events, Deals OS from sales teams managing deals, and and so on and so forth. But, again, I'm getting ahead of myself. That's the marketer in me. That's down the road. So, yeah, let me go ahead and kick it over to Bryant to show off, AgencyOS. I think you're really gonna like it, and thanks for listening to my rambling. Thanks, Matt. Running a digital agency is challenging. There's a load of us in the Directus core team who have done it in a past life. There's so much to it beyond actually delivering projects. And it's those other bits that often make or break client relationships. Yet, to get your operating system set up often means you're stringing together many different rigid off the shelf tools are burning hours and hours of potentially billable time developing your own solutions and practices. Agency OS is everything you need to get your agency off the ground or improve tooling for your existing company. It's based on familiar, extensible open source tools. Directus for the back end and Nuxt for the front end. Here's what it offers. A great looking website to promote your work and convert visitors to leads and booked meetings. Look on brand in minutes. Swap your colors. Add your logos, and set up your fonts using a simple theme config file. It's backed by a super easy to use headless CMS with live preview and content versioning. A back end CRM slash project tracker to manage your entire project workflow. From first contact, creating proposals and managing pipeline, to managing the actual day to day work with projects and tasks. Leverage the project templates to make planning and managing larger complex projects easy peasy. A private client portal to provide superior communication with your clients. Agencies that make it super easy for clients to do business with them will be more successful than those who leave client experience up to chance. Clients can self serve within their portal, get updates on projects, pay their invoices painlessly, and get gently reminded to deliver the files, content, and other info you need to complete their project. Agency OS is the perfect base to compose your own solution. Instead of hoping and praying those off the shelf tools, will finally work together exactly the way you need them. We've just published a bunch of videos to show you how to get the most out of each module. To get started, just follow the setup instructions on our website and you'll be on your way to a better agency in no time. Directus has come a very long way from where we started, and it really can be the operating system for your entire organization. Today we used agencies as an example but we see real estate leaders, airlines, online stores, and educational institutions base their whole organizational backbone on Directus as a composable data platform. Tomorrow we're heading back to directors 10.7 to announce and highlight our latest release, but for now you can check out agency OS using the links accompanying this video. So until tomorrow, bye for now.",[5792,5793,5794],"68b45f53-94fd-49d4-bacf-e2ffd26a3355","96a873a2-932e-4711-86f7-60d0e5ce983e","81818707-c839-46d3-97ce-51ea16766019",[],{"year":2380,"number":4568,"id":5709,"episodes":5797,"show":5798},[5711,5712,5713,5714,5691],{"title":151,"slug":152},{"id":5774,"slug":5800,"vimeo_id":5801,"description":5802,"tile":5803,"length":129,"resources":12,"people":5804,"episode_number":264,"published":5763,"title":5806,"video_transcript_html":5807,"video_transcript_text":5808,"content":12,"seo":12,"status":19,"episode_people":5809,"recommendations":5811,"season":5812},"page-builder-agencyos","894493860","In this video, you'll learn how to create dynamic pages that are on brand and look great with the page builder inside AgencyOS.","a882540d-611e-428a-9379-20cbc1d1a986",[5805],{"name":3590,"url":3591},"Page Builder In AgencyOS","\u003Cp>Speaker 0: Hi there. Bryant here for Directus. Welcome back to the next video in our Agency OS series where we're talking about the Page Builder. When you make the move to Headless, one of the things that your team may miss most is the Page Builder functionality from WordPress or other visual CMS's where you can move things around the screen and control margins and paddings and all of that. While I would argue that's a great thing because I want my content team focused on building the best content possible, not worrying about design or how things look on the page, That can be a blocker.\u003C\u002Fp>\u003Cp>But fear not, Agency OS and Directus make this easy to build dynamic pages and still maintain a consistent design system. Let's take a look at how it works and how to build new pages. So, on the left hand you can see we've got our front end And then on the right hand side, we'll just pull up the home page. You'll notice that the home page is made up of different content blocks. I can move these blocks around on the page and save and refresh our front end to see those changes being made.\u003C\u002Fp>\u003Cp>Great. I can move stuff around on the screen. But how about adding new blocks? Very simple. I'll just go down to the create new option and you've got all the blocks that are available that you've added to your Page Builder collection inside Agent c OS.\u003C\u002Fp>\u003Cp>So we will add a title for this Rich Text block, give it a headline, add some content and save it and we can see the changes happening on the front end. One of the other great features is that you can even reuse existing blocks from other pages so that you don't have to recreate the same stuff on each individual page over and over again. So I just select a logo cloud, refresh, and boom, there it is. Sometimes you may want to hide a block without removing it from the page. It's very easy to do within Aegis CoS.\u003C\u002Fp>\u003Cp>I just pull up the block that I wanna hide, check the box, refresh on the front end, and now that block is hidden from view ready to be activated whenever I need it. With that out of the way, let's take a look at how we add blocks. We're gonna navigate to the data model within our settings. And if we just search for block, we could see that all of these blocks are just different collections within the Directus project. Now adding new ones is simple.\u003C\u002Fp>\u003Cp>We can go into our Page Builder under the Blocks section. And this is where we control all of the blocks that are available for our content editors to add and reuse on different pages. Now for adding a new block, what we're gonna do is go back and add a new collection for this block. We'll call it block new. So we'll save this block, and then we'll add some fields to control our content.\u003C\u002Fp>\u003Cp>So let's add a WYSIWYG editor. We'll just give it a key of content. That's how we're going to access it on the front end. And let's add an image as well. All right, so we've created our collection for our block data.\u003C\u002Fp>\u003Cp>Let's go back into our pages to the Blocks field and add that block to the related collections so that we can add it when we're building a Page. So I'll just check the box for it, save this, and go back to our front end, to the Home page. And now I will be able to use that block within our page content. So we just add that, we will enter some content, select an image, and add it to the page. Now that we've added it to the page, we should be able to see this on the front end.\u003C\u002Fp>\u003Cp>So we'll just drag and drop this up to the top so that it displays in the first position on the page. But when I refresh, lo and behold, I don't see that. That's because our front end isn't aware of this block. So let's open up Versus Code and actually create this block so we can render that content on the front end. I'll just, do some window management here and we'll dive into the project.\u003C\u002Fp>\u003Cp>So we're looking for the components blocks directory. And the quickest way to do this is just cloning one of the existing blocks within the project. We've got a WYSIWYG component here, so I'm just gonna duplicate rich text dot view. Now, I'm just going to do some simple clean up on our types once we've created the file because this is no longer a RichText component, it is our Block New component. Alright, so now we'll just swap out the definition for our props with the new interface and we'll remove the default props because we don't have any properties like alignment that we want to control for this block.\u003C\u002Fp>\u003Cp>Alright, so now we'll just adjust the template for this. We've got our typography pros for our content. This will render any HTML content in a nice format. And then we are also going to just use the Nuxt Image component, because it has some built in helpers for handling images from Directus. So now we've got our template.\u003C\u002Fp>\u003Cp>When I refresh the page, I don't see this block being displayed. And that is because we need to make our Page Builder component aware of this particular block. So the page builder component loops through all of the different types of blocks and renders them on the page. We'll just add a new component within the component map using the Resolve component but we don't have any images or content displaying. This is a permissions problem because we created that new collection within Directus but we forgot to set public permissions for it.\u003C\u002Fp>\u003Cp>So any collections by default have 0 permissions because we want to keep your data secure. So we'll go into the roles and permissions for the public role and we'll look for that block new collection and give read access so anyone in the general public can see our content. Great. We refresh and we're still not there yet. So we have to actually fetch that data from the API, which is happening inside the permalink page within Nuxt.\u003C\u002Fp>\u003Cp>So we'll go into our fields collection, We'll make sure that we are fetching block new. And much like GraphQL, we want to fetch only the content that we needed so our site stays fast. So we'll do id, content, and image as the keys and then we reload to see that content being rendered on our front end. Now, and that's it. So you are now an expert in all things Page Builder.\u003C\u002Fp>\u003Cp>Go forth and build your own pages, add your blocks, customize this as you see fit. Stay tuned for more videos in this series where we'll deep dive into other features of Agency OS.\u003C\u002Fp>","Hi there. Bryant here for Directus. Welcome back to the next video in our Agency OS series where we're talking about the Page Builder. When you make the move to Headless, one of the things that your team may miss most is the Page Builder functionality from WordPress or other visual CMS's where you can move things around the screen and control margins and paddings and all of that. While I would argue that's a great thing because I want my content team focused on building the best content possible, not worrying about design or how things look on the page, That can be a blocker. But fear not, Agency OS and Directus make this easy to build dynamic pages and still maintain a consistent design system. Let's take a look at how it works and how to build new pages. So, on the left hand you can see we've got our front end And then on the right hand side, we'll just pull up the home page. You'll notice that the home page is made up of different content blocks. I can move these blocks around on the page and save and refresh our front end to see those changes being made. Great. I can move stuff around on the screen. But how about adding new blocks? Very simple. I'll just go down to the create new option and you've got all the blocks that are available that you've added to your Page Builder collection inside Agent c OS. So we will add a title for this Rich Text block, give it a headline, add some content and save it and we can see the changes happening on the front end. One of the other great features is that you can even reuse existing blocks from other pages so that you don't have to recreate the same stuff on each individual page over and over again. So I just select a logo cloud, refresh, and boom, there it is. Sometimes you may want to hide a block without removing it from the page. It's very easy to do within Aegis CoS. I just pull up the block that I wanna hide, check the box, refresh on the front end, and now that block is hidden from view ready to be activated whenever I need it. With that out of the way, let's take a look at how we add blocks. We're gonna navigate to the data model within our settings. And if we just search for block, we could see that all of these blocks are just different collections within the Directus project. Now adding new ones is simple. We can go into our Page Builder under the Blocks section. And this is where we control all of the blocks that are available for our content editors to add and reuse on different pages. Now for adding a new block, what we're gonna do is go back and add a new collection for this block. We'll call it block new. So we'll save this block, and then we'll add some fields to control our content. So let's add a WYSIWYG editor. We'll just give it a key of content. That's how we're going to access it on the front end. And let's add an image as well. All right, so we've created our collection for our block data. Let's go back into our pages to the Blocks field and add that block to the related collections so that we can add it when we're building a Page. So I'll just check the box for it, save this, and go back to our front end, to the Home page. And now I will be able to use that block within our page content. So we just add that, we will enter some content, select an image, and add it to the page. Now that we've added it to the page, we should be able to see this on the front end. So we'll just drag and drop this up to the top so that it displays in the first position on the page. But when I refresh, lo and behold, I don't see that. That's because our front end isn't aware of this block. So let's open up Versus Code and actually create this block so we can render that content on the front end. I'll just, do some window management here and we'll dive into the project. So we're looking for the components blocks directory. And the quickest way to do this is just cloning one of the existing blocks within the project. We've got a WYSIWYG component here, so I'm just gonna duplicate rich text dot view. Now, I'm just going to do some simple clean up on our types once we've created the file because this is no longer a RichText component, it is our Block New component. Alright, so now we'll just swap out the definition for our props with the new interface and we'll remove the default props because we don't have any properties like alignment that we want to control for this block. Alright, so now we'll just adjust the template for this. We've got our typography pros for our content. This will render any HTML content in a nice format. And then we are also going to just use the Nuxt Image component, because it has some built in helpers for handling images from Directus. So now we've got our template. When I refresh the page, I don't see this block being displayed. And that is because we need to make our Page Builder component aware of this particular block. So the page builder component loops through all of the different types of blocks and renders them on the page. We'll just add a new component within the component map using the Resolve component but we don't have any images or content displaying. This is a permissions problem because we created that new collection within Directus but we forgot to set public permissions for it. So any collections by default have 0 permissions because we want to keep your data secure. So we'll go into the roles and permissions for the public role and we'll look for that block new collection and give read access so anyone in the general public can see our content. Great. We refresh and we're still not there yet. So we have to actually fetch that data from the API, which is happening inside the permalink page within Nuxt. So we'll go into our fields collection, We'll make sure that we are fetching block new. And much like GraphQL, we want to fetch only the content that we needed so our site stays fast. So we'll do id, content, and image as the keys and then we reload to see that content being rendered on our front end. Now, and that's it. So you are now an expert in all things Page Builder. Go forth and build your own pages, add your blocks, customize this as you see fit. Stay tuned for more videos in this series where we'll deep dive into other features of Agency OS.",[5810],"89e36749-ee99-4ca7-baef-d3b7500a5e13",[],{"year":2380,"number":722,"id":5771,"episodes":5813,"show":5814},[5773,5756,5774,5775],{"title":5777,"slug":5778},{"id":5773,"slug":5816,"vimeo_id":5817,"description":5818,"tile":5819,"length":129,"resources":12,"people":5820,"episode_number":13,"published":5763,"title":5822,"video_transcript_html":5823,"video_transcript_text":5824,"content":12,"seo":12,"status":19,"episode_people":5825,"recommendations":5827,"season":5828},"getting-started-with-agencyos","894493339","In this video, we'll cover how to quickly spin up and deploy an instance of AgencyOS.","ef2db2f0-903e-49cb-ad7a-2d6b9a52b6e3",[5821],{"name":3590,"url":3591},"Getting Started with AgencyOS","\u003Cp>Speaker 0: Hi there. Bryant here for Directus. Welcome to Agency OS. I'll be your guide along the way. Let's dive right in and get you spun up on the platform.\u003C\u002Fp>\u003Cp>Directus Cloud dashboard using GitHub or your email address and then we'll create a new project. So I just give this a project name, my agency operating system, URL, which is gonna be where we access the project, and then we will spin this up. Any new accounts get a 14 day free trial, so you can give it a test run. Why Directus Cloud? Well, for starters, you do not have to mess with any DevOps.\u003C\u002Fp>\u003Cp>You don't have to worry about deploying your platform. You spin up the cloud instance and it is ready to go, ready to scale right out of the box. Your instance should spin up in under 2 minutes and then you'll receive login credentials via your email. So just pull up that email, hit the URL, and log in to your Project. So, when you log in your project will be blank.\u003C\u002Fp>\u003Cp>But don't fear, we're gonna fix that. The first thing that we're gonna do is go to your admin user and scroll down until you see the token field. So we're gonna generate a static token to use with our template CLI command. Please don't forget to save the user after you generate the token because it will not automatically save the token. The next thing we're gonna do is pull up the terminal and we're gonna run a command together.\u003C\u002Fp>\u003Cp>Npxdirectus template cli Apply. This will ask you to install a package. You just need to confirm to install this, and then you will receive a prompt. So you can choose to apply our official templates, which is what we're gonna do here. We're gonna choose the agency OS template, and then we get asked for our directus URL.\u003C\u002Fp>\u003Cp>So this is your cloud instance that you just spun up. In my case it's bryants agency.directus dot app. And then the next thing will be that token. And you copied it and pasted it somewhere so you've got it. You just knock that in there and then the template CLI will start doing its thing.\u003C\u002Fp>\u003Cp>Now, this process does take several minutes because we are populating everything into your instance. All the collections, all the tables, all of the individual sample data and assets. Now once the template is done, you can just do a refresh on your Directus instance and boom. Now you will have access to the Agency OS backend. So if we just browse through here, you can see all the different collections of content.\u003C\u002Fp>\u003Cp>On the left, we've got, your website. You've got different projects. You've got all of your organizations and contacts. We'll dive into that into other videos. But for now, let's show you how to spin up the front end of Agency OS, the website and the portal.\u003C\u002Fp>\u003Cp>So we're going to go back to the GitHub repository. And you can see the installation instructions if you want to spin this up locally first. But for the purpose of this video, we want to deploy something now. We are going to use the one click button for deploying to Vercel. So you will link your GitHub account and create a new repository.\u003C\u002Fp>\u003Cp>We'll just call it bryant's agency OS. And then it will prompt you for the environment variables. So these are the secret values that are used to communicate with Directus. The Directus URL is just going to be your cloud instance that we spun up. The server token can be that admin token that you just spun up, or you can create another user to use as the server token.\u003C\u002Fp>\u003Cp>And then we have things like public site URL, Stripe's secret key, and publishable key if you want to use billing. We'll just leave those blank for now or enter NA so we can spin this project up. Vercel will go through and build the project and within, several minutes we will have a live front end that works perfectly with the back end setup by Directus. So we check the deployment summary. Everything looks good.\u003C\u002Fp>\u003Cp>Let's go to the dashboard. So we'll click Continue to Dashboard. And I'm gonna log in to my own personal account, which is where this particular project was spun up. And then we'll just visit the front end. So, let's take a look.\u003C\u002Fp>\u003Cp>Boom. There it is. There is our website. Looks great. Now, how do we get started developing on this?\u003C\u002Fp>\u003Cp>So we'll go back to GitHub, we'll look for our repositories. And you can see we've got Bryant's Agency OS here. This is that repository that, Vercel created during that process. I'll just go to the code section and you can use the GitHub CLI or the regular terminal commands to fire this up. We're just gonna copy and paste that command inside our local directory.\u003C\u002Fp>\u003Cp>So GitHub is gonna clone that. We will navigate to that directory. So, OCD Bryant's Agency OS. And then I'm going to do npm I or pnpmi to install all the dependencies for the project. So I'm gonna get an error here because I haven't set up our environment variables.\u003C\u002Fp>\u003Cp>So let's just open this up in Versus Code and start developing. I'm gonna go and the first thing you should do is duplicate your env file, the example file, and make sure that you set the Directus URL, the Directus server token, and your Stripe keys if you want to test the Stripe Billing. Make sure you do not commit any of these keys to the repo, and the git ignore file should already take care of that for you. But once you have this set up, we will just p npm dev inside the terminal and Nuxt will spin up for us on the front end. Now while that initial dev server build is happening, I am just gonna do some rearranging on my Windows so I can look at our code and the website side by side.\u003C\u002Fp>\u003Cp>Alright, so there you have it. You've got the back end of Agency OS setup on Direct cloud. You've got your front end served up on Vercel and ready to develop locally. Stay tuned for the next videos in this series where we dive deeper into some of the advanced functionality available within AgentC OS.\u003C\u002Fp>","Hi there. Bryant here for Directus. Welcome to Agency OS. I'll be your guide along the way. Let's dive right in and get you spun up on the platform. Directus Cloud dashboard using GitHub or your email address and then we'll create a new project. So I just give this a project name, my agency operating system, URL, which is gonna be where we access the project, and then we will spin this up. Any new accounts get a 14 day free trial, so you can give it a test run. Why Directus Cloud? Well, for starters, you do not have to mess with any DevOps. You don't have to worry about deploying your platform. You spin up the cloud instance and it is ready to go, ready to scale right out of the box. Your instance should spin up in under 2 minutes and then you'll receive login credentials via your email. So just pull up that email, hit the URL, and log in to your Project. So, when you log in your project will be blank. But don't fear, we're gonna fix that. The first thing that we're gonna do is go to your admin user and scroll down until you see the token field. So we're gonna generate a static token to use with our template CLI command. Please don't forget to save the user after you generate the token because it will not automatically save the token. The next thing we're gonna do is pull up the terminal and we're gonna run a command together. Npxdirectus template cli Apply. This will ask you to install a package. You just need to confirm to install this, and then you will receive a prompt. So you can choose to apply our official templates, which is what we're gonna do here. We're gonna choose the agency OS template, and then we get asked for our directus URL. So this is your cloud instance that you just spun up. In my case it's bryants agency.directus dot app. And then the next thing will be that token. And you copied it and pasted it somewhere so you've got it. You just knock that in there and then the template CLI will start doing its thing. Now, this process does take several minutes because we are populating everything into your instance. All the collections, all the tables, all of the individual sample data and assets. Now once the template is done, you can just do a refresh on your Directus instance and boom. Now you will have access to the Agency OS backend. So if we just browse through here, you can see all the different collections of content. On the left, we've got, your website. You've got different projects. You've got all of your organizations and contacts. We'll dive into that into other videos. But for now, let's show you how to spin up the front end of Agency OS, the website and the portal. So we're going to go back to the GitHub repository. And you can see the installation instructions if you want to spin this up locally first. But for the purpose of this video, we want to deploy something now. We are going to use the one click button for deploying to Vercel. So you will link your GitHub account and create a new repository. We'll just call it bryant's agency OS. And then it will prompt you for the environment variables. So these are the secret values that are used to communicate with Directus. The Directus URL is just going to be your cloud instance that we spun up. The server token can be that admin token that you just spun up, or you can create another user to use as the server token. And then we have things like public site URL, Stripe's secret key, and publishable key if you want to use billing. We'll just leave those blank for now or enter NA so we can spin this project up. Vercel will go through and build the project and within, several minutes we will have a live front end that works perfectly with the back end setup by Directus. So we check the deployment summary. Everything looks good. Let's go to the dashboard. So we'll click Continue to Dashboard. And I'm gonna log in to my own personal account, which is where this particular project was spun up. And then we'll just visit the front end. So, let's take a look. Boom. There it is. There is our website. Looks great. Now, how do we get started developing on this? So we'll go back to GitHub, we'll look for our repositories. And you can see we've got Bryant's Agency OS here. This is that repository that, Vercel created during that process. I'll just go to the code section and you can use the GitHub CLI or the regular terminal commands to fire this up. We're just gonna copy and paste that command inside our local directory. So GitHub is gonna clone that. We will navigate to that directory. So, OCD Bryant's Agency OS. And then I'm going to do npm I or pnpmi to install all the dependencies for the project. So I'm gonna get an error here because I haven't set up our environment variables. So let's just open this up in Versus Code and start developing. I'm gonna go and the first thing you should do is duplicate your env file, the example file, and make sure that you set the Directus URL, the Directus server token, and your Stripe keys if you want to test the Stripe Billing. Make sure you do not commit any of these keys to the repo, and the git ignore file should already take care of that for you. But once you have this set up, we will just p npm dev inside the terminal and Nuxt will spin up for us on the front end. Now while that initial dev server build is happening, I am just gonna do some rearranging on my Windows so I can look at our code and the website side by side. Alright, so there you have it. You've got the back end of Agency OS setup on Direct cloud. You've got your front end served up on Vercel and ready to develop locally. Stay tuned for the next videos in this series where we dive deeper into some of the advanced functionality available within AgentC OS.",[5826],"61cf3fd6-a6e1-45ad-a501-9cd4e27eab4d",[],{"year":2380,"number":722,"id":5771,"episodes":5829,"show":5830},[5773,5756,5774,5775],{"title":5777,"slug":5778},{"id":5775,"slug":5832,"vimeo_id":5833,"description":5834,"tile":5835,"length":195,"resources":12,"people":5836,"episode_number":159,"published":5763,"title":5838,"video_transcript_html":5839,"video_transcript_text":5840,"content":12,"seo":12,"status":19,"episode_people":5841,"recommendations":5843,"season":5844},"stripe-agencyos","894494318","In this video you'll learn how your clients can quickly and easily pay within their portal. ","a28d2560-5990-4cfb-8441-b9942b828594",[5837],{"name":3590,"url":3591},"Client Payments with Stripe and AgencyOS","\u003Cp>Speaker 0: Hi. Brian here for Directus. Welcome back to the next video in our Agency OS series where we're talking about the Stripe checkout or the ability to accept payments from your clients within the portal. This is a very important piece of functionality. You've got to be able to get paid on time to run a successful agency.\u003C\u002Fp>\u003Cp>So let's dive in. Alright. So we're logged into the client portal and we'll just bring up one of the invoices and you can see there's a link to pay this invoice. When we click it, nothing happens because we have the wrong Stripe API keys or we haven't set those up yet. So what we're gonna do is go into our Stripe account and go to the Developers tab and look for API Keys.\u003C\u002Fp>\u003Cp>There are several different keys that you're gonna copy here. The publishable key, which is safe to be used on the front end, and then the secret key, which is only exposed on the server. So we are going to go into our env file and paste these keys. And I'm certainly going to have to roll these keys after this video so don't try to steal my keys. The last piece of the puzzle here is our Stripe webhook secret.\u003C\u002Fp>\u003Cp>Whenever a checkout event occurs, we need to be able to catch or receive those webhooks that Stripe sends out so we can update Directus with those. So I will just follow the instructions here to test in a local environment. We'll make sure that I am logged into the Stripe CLI and then we will forward all Stripe webhook events to localhost3000\u002Fapi\u002Fstripe slash webhooks. So this is the API route within the Nuxt project where we are listening for those webhooks. And once I log in, I listen to that event, it will give me my webhook secret, which we use to verify that Stripe is sending those webhooks.\u003C\u002Fp>\u003Cp>Now, once we test that out, we should be able to pay invoices. But, oops, we need to restart the dev server so those changes can take effect. Alright, so let's wait for our dev server to restart. And once it's back, then we can test out this functionality. So we just hit Pay Invoice.\u003C\u002Fp>\u003Cp>It will throw a Stripe checkout session where the user can pay and complete via card or whatever payments you've got set up in your Stripe account. So I'm just going to enter some test card information and try this payment out to verify this will work. Alright. So once the checkout is complete, Stripe should send us back to the page from whence we came and we can see that this invoice is now marked as paid because a payment was applied. So if I open up our AgencyOS back end inside Directus, we should be able to find that payment within the Billing section.\u003C\u002Fp>\u003Cp>And just like the song, whoomp, there it is. Now, one of the other pieces of functionality that's built into the platform is the Stripe customer portal. They have a great UI, this is set up on the billing page where your customer or your client can go in and update their payment methods within the Stripe customer portal. So that's it for the client functionality within the portal. Let's take a look inside our codebase to see how this operates behind the scenes.\u003C\u002Fp>\u003Cp>Behind the curtain, if you will. So we're going to open up Versus Code and look for our layers section. Within layers we will look for our portal and then you can see under the server API routes we have a Stripe folder where we have a create checkout session. Post. Ts.\u003C\u002Fp>\u003Cp>So, this will accept an invoice ID as a, inside the body of this and then this will create a checkout session and return it to the Stripe JavaScript client to, redirect us to checkout. Then we have a composable which uses the Stripe JavaScript client to actually redirect to checkout. So we've got use Stripe which just basically loads Stripe and then we have a handle checkout function and a get portal link function to send the clients to that portal link. You can also see the API route for creating that portal link. And last but not least, we have our webhooks route that receives those webhooks from Stripe whenever these checkout events occur.\u003C\u002Fp>\u003Cp>This will verify that data coming from Stripe and create those payments within our Agency OS instance. So when you get ready to go to production with this, make sure that you go in and actually create the Stripe endpoints for those webhooks. So whatever your final deployment URL is, you'll go in and set that up inside Stripe using, the API Stripe Webhooks and then you will pick the checkout events just to make sure that all those events get sent to that URL. And make sure that you toggle off Test mode and grab the live API keys on your Production Server. So that's it for this video on how to connect Stripe with AgentC OS.\u003C\u002Fp>\u003Cp>Stay tuned for other videos in the series.\u003C\u002Fp>","Hi. Brian here for Directus. Welcome back to the next video in our Agency OS series where we're talking about the Stripe checkout or the ability to accept payments from your clients within the portal. This is a very important piece of functionality. You've got to be able to get paid on time to run a successful agency. So let's dive in. Alright. So we're logged into the client portal and we'll just bring up one of the invoices and you can see there's a link to pay this invoice. When we click it, nothing happens because we have the wrong Stripe API keys or we haven't set those up yet. So what we're gonna do is go into our Stripe account and go to the Developers tab and look for API Keys. There are several different keys that you're gonna copy here. The publishable key, which is safe to be used on the front end, and then the secret key, which is only exposed on the server. So we are going to go into our env file and paste these keys. And I'm certainly going to have to roll these keys after this video so don't try to steal my keys. The last piece of the puzzle here is our Stripe webhook secret. Whenever a checkout event occurs, we need to be able to catch or receive those webhooks that Stripe sends out so we can update Directus with those. So I will just follow the instructions here to test in a local environment. We'll make sure that I am logged into the Stripe CLI and then we will forward all Stripe webhook events to localhost3000\u002Fapi\u002Fstripe slash webhooks. So this is the API route within the Nuxt project where we are listening for those webhooks. And once I log in, I listen to that event, it will give me my webhook secret, which we use to verify that Stripe is sending those webhooks. Now, once we test that out, we should be able to pay invoices. But, oops, we need to restart the dev server so those changes can take effect. Alright, so let's wait for our dev server to restart. And once it's back, then we can test out this functionality. So we just hit Pay Invoice. It will throw a Stripe checkout session where the user can pay and complete via card or whatever payments you've got set up in your Stripe account. So I'm just going to enter some test card information and try this payment out to verify this will work. Alright. So once the checkout is complete, Stripe should send us back to the page from whence we came and we can see that this invoice is now marked as paid because a payment was applied. So if I open up our AgencyOS back end inside Directus, we should be able to find that payment within the Billing section. And just like the song, whoomp, there it is. Now, one of the other pieces of functionality that's built into the platform is the Stripe customer portal. They have a great UI, this is set up on the billing page where your customer or your client can go in and update their payment methods within the Stripe customer portal. So that's it for the client functionality within the portal. Let's take a look inside our codebase to see how this operates behind the scenes. Behind the curtain, if you will. So we're going to open up Versus Code and look for our layers section. Within layers we will look for our portal and then you can see under the server API routes we have a Stripe folder where we have a create checkout session. Post. Ts. So, this will accept an invoice ID as a, inside the body of this and then this will create a checkout session and return it to the Stripe JavaScript client to, redirect us to checkout. Then we have a composable which uses the Stripe JavaScript client to actually redirect to checkout. So we've got use Stripe which just basically loads Stripe and then we have a handle checkout function and a get portal link function to send the clients to that portal link. You can also see the API route for creating that portal link. And last but not least, we have our webhooks route that receives those webhooks from Stripe whenever these checkout events occur. This will verify that data coming from Stripe and create those payments within our Agency OS instance. So when you get ready to go to production with this, make sure that you go in and actually create the Stripe endpoints for those webhooks. So whatever your final deployment URL is, you'll go in and set that up inside Stripe using, the API Stripe Webhooks and then you will pick the checkout events just to make sure that all those events get sent to that URL. And make sure that you toggle off Test mode and grab the live API keys on your Production Server. So that's it for this video on how to connect Stripe with AgentC OS. Stay tuned for other videos in the series.",[5842],"50893908-338e-41c7-bc9b-c2bf01bdd378",[],{"year":2380,"number":722,"id":5771,"episodes":5845,"show":5846},[5773,5756,5774,5775],{"title":5777,"slug":5778},{"id":5711,"slug":5848,"vimeo_id":5849,"description":5850,"tile":5851,"length":195,"resources":12,"people":5852,"episode_number":13,"published":5855,"title":5856,"video_transcript_html":5857,"video_transcript_text":5858,"content":12,"seo":12,"status":19,"episode_people":5859,"recommendations":5862,"season":5863},"version","894066448","At Leap Week 1, we announced the availability of Content Versioning for the Directus Editor and the new Content Versions API, along with new events for Directus Automate.","d728180a-1a58-463d-9ec3-e3d8b4bc1617",[5853,5854],{"name":2820,"url":2821},{"name":4276,"url":4277},"2023-10-23","Version","\u003Cp>Speaker 0: Hello. We are so excited to welcome you to the very first Director Sleep Week. We have a truly global community, so wherever and whenever you are, hello. We have a fantastic week lined up. Over the next 5 days, we'll be sharing what's new and upcoming for Directus and possibly even some teasers of work that's been years in the making.\u003C\u002Fp>\u003Cp>Now Directus isn't a new project with the first version built by our CEO and co founder Ben almost 20 years ago. Directus 9 then completely overhauled the project into what we use today And earlier this year, we released Directus 10 with a commitment to sustainable open source. There have been over 26,000,000 downloads of Directus and that's because of the hard work and enthusiasm of our core team, contributors, and community members. In fact, there's now over 10,000 of you in our Discord server and 23,000 stars on our GitHub repository. The last few months have seen us bring live preview to the Directus editor, release Directus real time, and create a brand new SDK with much improved developer experience.\u003C\u002Fp>\u003Cp>Today we're releasing directors version 10.7 and to kick it off, we want to introduce content versioning. Content versioning is a huge update to how you and your team author and collaborate in the Directus editor, and I would love to hand it off to Azeri to tell you more.\u003C\u002Fp>\u003Cp>Speaker 1: Until now, collaborating on content using Directus hasn't always been the easiest. We have comments and revision history with accountability. But actually collaborating has largely happened off platform. There was a small risk. You would hit save the changes after someone else and override work, and there was no good way to work on draft updates and saving without publishing.\u003C\u002Fp>\u003Cp>We have taken great care to build content versioning in the director's way, being both super powerful while being unoppressive to your workflow. Rather than talk to you about it, let me show you. Here, I have an articles collection where I've enabled content versioning, and this is an item within that particular collection. And here, we have a pill or menu, which is the main entry point for us to go into content versioning. Here, we can create a new version.\u003C\u002Fp>\u003Cp>I'll call it graph as the key, and mine graph as the name. Now I'll change the title, add a new cover image. And for the content, I will just follow it up with an enemies. Here, I can save the version, and we'll go back here. We can see the main icon is not updated at all.\u003C\u002Fp>\u003Cp>But when I view my draft, I can see my latest changes with the new cover image and content. Over here, I can promote the version. When I see the changes by default, we'll select all the changes from your new draft. And when I click on the preview tab, we can see what would be changes that I will be promoting to. And if I would like to keep the same cover image over here, you can see I will go back to the old image.\u003C\u002Fp>\u003Cp>But for the demo purposes, I'll choose true cover image and content to be updated with the title remaining as the same. And while promoting it, we can choose to delete, keep it, and now choose to delete it. Now once promoting, we can see the title remains the same with the cover image and content updated to the new one that I've just added. You can also use our new content versions API to do what we just did in the data studio. If you have content versioning enabled on the collection, accessing the item like normal will always get the main version.\u003C\u002Fp>\u003Cp>To get a specific version, use the new version parameter with the key provided when you created the version. And over here, it will be the test. And this data is from the test content version for this particular item. To promote a content version, you have to first create a div, which will return a hash And provide that hash to the promo endpoint. Content versioning also exposes new events which can be used in Director's Ultimate.\u003C\u002Fp>\u003Cp>That's both in Hook's extensions and in Flows. A cool tip is that once you combine content versioning and live preview, you can view non published changes in your pages before you sign it off. And that's content versioning. It's been\u003C\u002Fp>\u003Cp>Speaker 0: a pleasure to work on this for you and back to Kevin. Content versioning was a feature built with our our amazing community. It's been in preview for a couple of months and we've loved having your feedback implementing many of the suggestions that you've made. You can get your hands on content versioning now by downloading the just released Directus version 10.7 which is now available on NPM and Docker Hub. That wraps up day 1 of Leap Week.\u003C\u002Fp>\u003Cp>We hope you love where we've landed for content versioning, and there's plenty more to come this week. See you tomorrow.\u003C\u002Fp>","Hello. We are so excited to welcome you to the very first Director Sleep Week. We have a truly global community, so wherever and whenever you are, hello. We have a fantastic week lined up. Over the next 5 days, we'll be sharing what's new and upcoming for Directus and possibly even some teasers of work that's been years in the making. Now Directus isn't a new project with the first version built by our CEO and co founder Ben almost 20 years ago. Directus 9 then completely overhauled the project into what we use today And earlier this year, we released Directus 10 with a commitment to sustainable open source. There have been over 26,000,000 downloads of Directus and that's because of the hard work and enthusiasm of our core team, contributors, and community members. In fact, there's now over 10,000 of you in our Discord server and 23,000 stars on our GitHub repository. The last few months have seen us bring live preview to the Directus editor, release Directus real time, and create a brand new SDK with much improved developer experience. Today we're releasing directors version 10.7 and to kick it off, we want to introduce content versioning. Content versioning is a huge update to how you and your team author and collaborate in the Directus editor, and I would love to hand it off to Azeri to tell you more. Until now, collaborating on content using Directus hasn't always been the easiest. We have comments and revision history with accountability. But actually collaborating has largely happened off platform. There was a small risk. You would hit save the changes after someone else and override work, and there was no good way to work on draft updates and saving without publishing. We have taken great care to build content versioning in the director's way, being both super powerful while being unoppressive to your workflow. Rather than talk to you about it, let me show you. Here, I have an articles collection where I've enabled content versioning, and this is an item within that particular collection. And here, we have a pill or menu, which is the main entry point for us to go into content versioning. Here, we can create a new version. I'll call it graph as the key, and mine graph as the name. Now I'll change the title, add a new cover image. And for the content, I will just follow it up with an enemies. Here, I can save the version, and we'll go back here. We can see the main icon is not updated at all. But when I view my draft, I can see my latest changes with the new cover image and content. Over here, I can promote the version. When I see the changes by default, we'll select all the changes from your new draft. And when I click on the preview tab, we can see what would be changes that I will be promoting to. And if I would like to keep the same cover image over here, you can see I will go back to the old image. But for the demo purposes, I'll choose true cover image and content to be updated with the title remaining as the same. And while promoting it, we can choose to delete, keep it, and now choose to delete it. Now once promoting, we can see the title remains the same with the cover image and content updated to the new one that I've just added. You can also use our new content versions API to do what we just did in the data studio. If you have content versioning enabled on the collection, accessing the item like normal will always get the main version. To get a specific version, use the new version parameter with the key provided when you created the version. And over here, it will be the test. And this data is from the test content version for this particular item. To promote a content version, you have to first create a div, which will return a hash And provide that hash to the promo endpoint. Content versioning also exposes new events which can be used in Director's Ultimate. That's both in Hook's extensions and in Flows. A cool tip is that once you combine content versioning and live preview, you can view non published changes in your pages before you sign it off. And that's content versioning. It's been a pleasure to work on this for you and back to Kevin. Content versioning was a feature built with our our amazing community. It's been in preview for a couple of months and we've loved having your feedback implementing many of the suggestions that you've made. You can get your hands on content versioning now by downloading the just released Directus version 10.7 which is now available on NPM and Docker Hub. That wraps up day 1 of Leap Week. We hope you love where we've landed for content versioning, and there's plenty more to come this week. See you tomorrow.",[5860,5861],"12418f46-bec1-464d-af7a-013ee0008750","b2fa5286-79dd-455f-8a4d-c261a2974d41",[],{"year":2380,"number":4568,"id":5709,"episodes":5864,"show":5865},[5711,5712,5713,5714,5691],{"title":151,"slug":152},{"id":2388,"slug":5867,"vimeo_id":5868,"description":5869,"tile":5870,"length":213,"resources":12,"people":5871,"episode_number":195,"published":5873,"title":5874,"video_transcript_html":5875,"video_transcript_text":5876,"content":12,"seo":12,"status":19,"episode_people":5877,"recommendations":5879,"season":5880},"10-6","894077892","Join Kevin as he shows you what's new in Directus 10.6 - take note because there's a breaking change in here!","b99b4455-e931-40ee-ba10-02618a829437",[5872],{"name":2820,"url":2821},"2023-08-25","What's New In Directus Version 10.6","\u003Cp>Speaker 0: Hello there, my name's Kevin and I run developer relations here at Directus and today I'm gonna show you what's new in Directus version 10.6. Extension building guides. We heard from you that you didn't quite know what guides we had available in our documentation because of the way they were organized. So we've now changed that and created a set of index pages for various areas of our guides so you can see what's available to make the most of Directus. Today, we're also releasing a new set of guides to help you build extensions from scratch, right through from initializing and boiler plating, step by step code instructions, and finally how to actually install extensions into your Director's project.\u003C\u002Fp>\u003Cp>There are 15 available today in our documentation with more coming soon. Replacing VM 2 with isolated VM. Prior to this release, directors relied on VM 2 to run code in the run script operations of flows, our automation feature. The m two is now unmaintained with some serious security issues that could potentially allow code to escape the sandbox and access the machine on which your director's project is running. So in directors 10.6, we've replaced it with isolated VM.\u003C\u002Fp>\u003Cp>Now, at the same time, the run script operation was really only ever intended for simple data manipulation, And as we haven't yet found an adequate way to run arbitrary packages and code, inside of flows without VM 2, we've removed this functionality. What this means in practice is if you are, you know, requiring or importing npm packages inside of the run script operation, that will no longer be supported, and instead you will need to expose those npm packages as custom operation extensions. And fortunately, we have a guide in our new extension building set that shows you exactly how to do this. You should consider this a required upgrade for the security of your Director's project. Redact environment variables in logs for flows.\u003C\u002Fp>\u003Cp>This update is pretty straightforward, you can log values when using flows for debugging and accountability And when you are logging environment variables, which should always be treated as sensitive, directors 10.6 will now the values of those in your logs, keeping the values safe. A new developer blog and guest author program. Inside of our documentation is a brand new developer blog with regular tutorials, tips and tricks, and best practices to help you get the most of Directus. To accompany the blog is our brand new guest author program, where we will guide you through creating an awesome technical blog post and pay you for your time. One place to find breaking changes.\u003C\u002Fp>\u003Cp>A small but important update to our documentation is a new section called releases, which links out to our full set of releases on GitHub, but also contains a page with all breaking changes across various versions of directors. So before you upgrade, you can take a look here to understand what's changing and what steps you may need to take for ongoing success with your project. Our community is wicked cool. There have been a number of patch releases since 10.5. So in this section, we're gonna thank all of the amazing community members who have contributed in all of those patches and 10.6.\u003C\u002Fp>\u003Cp>Thank you to Matthew, Ilya, and Maurice for fixing some broken links in our documentation. Thank you to Nebosia and Mingyu for fixing some small typos in our documentation. Thank you to Bevis for further improving the SDK examples across our API reference in our documentation, and thank you to Gerard for their contribution, which reuses already calculated values in Directus. Directus 10.6 is now available in Docker Hub and on npm. Thank you ever so much for joining me, and we'll see you in the next release.\u003C\u002Fp>","Hello there, my name's Kevin and I run developer relations here at Directus and today I'm gonna show you what's new in Directus version 10.6. Extension building guides. We heard from you that you didn't quite know what guides we had available in our documentation because of the way they were organized. So we've now changed that and created a set of index pages for various areas of our guides so you can see what's available to make the most of Directus. Today, we're also releasing a new set of guides to help you build extensions from scratch, right through from initializing and boiler plating, step by step code instructions, and finally how to actually install extensions into your Director's project. There are 15 available today in our documentation with more coming soon. Replacing VM 2 with isolated VM. Prior to this release, directors relied on VM 2 to run code in the run script operations of flows, our automation feature. The m two is now unmaintained with some serious security issues that could potentially allow code to escape the sandbox and access the machine on which your director's project is running. So in directors 10.6, we've replaced it with isolated VM. Now, at the same time, the run script operation was really only ever intended for simple data manipulation, And as we haven't yet found an adequate way to run arbitrary packages and code, inside of flows without VM 2, we've removed this functionality. What this means in practice is if you are, you know, requiring or importing npm packages inside of the run script operation, that will no longer be supported, and instead you will need to expose those npm packages as custom operation extensions. And fortunately, we have a guide in our new extension building set that shows you exactly how to do this. You should consider this a required upgrade for the security of your Director's project. Redact environment variables in logs for flows. This update is pretty straightforward, you can log values when using flows for debugging and accountability And when you are logging environment variables, which should always be treated as sensitive, directors 10.6 will now the values of those in your logs, keeping the values safe. A new developer blog and guest author program. Inside of our documentation is a brand new developer blog with regular tutorials, tips and tricks, and best practices to help you get the most of Directus. To accompany the blog is our brand new guest author program, where we will guide you through creating an awesome technical blog post and pay you for your time. One place to find breaking changes. A small but important update to our documentation is a new section called releases, which links out to our full set of releases on GitHub, but also contains a page with all breaking changes across various versions of directors. So before you upgrade, you can take a look here to understand what's changing and what steps you may need to take for ongoing success with your project. Our community is wicked cool. There have been a number of patch releases since 10.5. So in this section, we're gonna thank all of the amazing community members who have contributed in all of those patches and 10.6. Thank you to Matthew, Ilya, and Maurice for fixing some broken links in our documentation. Thank you to Nebosia and Mingyu for fixing some small typos in our documentation. Thank you to Bevis for further improving the SDK examples across our API reference in our documentation, and thank you to Gerard for their contribution, which reuses already calculated values in Directus. Directus 10.6 is now available in Docker Hub and on npm. Thank you ever so much for joining me, and we'll see you in the next release.",[5878],"89d4f128-3ab3-472b-91c0-69d2b2cdd16e",[],{"year":2380,"number":588,"id":2381,"episodes":5881,"show":5882},[2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394,2366],{"title":2396,"slug":2397},{"id":5688,"slug":5884,"vimeo_id":5885,"description":5886,"tile":5887,"length":630,"resources":5888,"people":5892,"episode_number":264,"published":5895,"title":5896,"video_transcript_html":5897,"video_transcript_text":5898,"content":12,"seo":12,"status":19,"episode_people":5899,"recommendations":5901,"season":5902},"extending-online-art-gallery","894204814","Using Directus extensions, we augmented Directus to add functionality to generate blurhashes for the smooth loading of high-quality full-screen images.","5a9d3778-c4df-46a1-b60f-c05a32784930",[5889],{"name":5890,"url":5891},"Pixie Labs","https:\u002F\u002Fwww.pixielabs.io\u002F",[5893],{"name":5531,"url":5894},"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fjalada","2023-08-23","Extending Directus for an Online Art Gallery - London Meetup","\u003Cp>Speaker 0: Alright. Hi. My name's David. I'm gonna tell you a little bit about, extending directors to build a beautiful online art gallery. Can everyone hear me okay?\u003C\u002Fp>\u003Cp>If you can't, please, like, wave at me, shout at me, be like, Can't hear you. And then I'll speak up, we'll move this. A quick show of hands just so I can gauge, who here has used Directus on a project? Great. Good start.\u003C\u002Fp>\u003Cp>Who has used it for more than one project? Just because I'm curious. Nice. Who has, when they've been working on a director's project, you've used an extension as part of that? Alright.\u003C\u002Fp>\u003Cp>Starting to shrink. Great. Who has written a custom extension? Okay. Great.\u003C\u002Fp>\u003Cp>Phew. Otherwise, I was worried that I was going to be like, hey, you guys tell me how to write an extension. All right. Before I start, a little bit about me. I'm David.\u003C\u002Fp>\u003Cp>I'm head of engineering at Pixilabs. We are a digital product development agency. That means that we design and develop digital products, everything from internal platforms for FTSE 100 companies to manage smart home device installations, helping funded startups launch their product, all the way down to we once made a typewriter type by itself when you tweeted at it. And that was part of a digital art installation for Twitter, which was really fun. Basically, if we can code it, we will do our best to do that.\u003C\u002Fp>\u003Cp>And, actually, most of the time we build, like, full stack web applications in Ruby on Rails. But sometimes, we need a headless CMS and sometimes, we often turn to directors. So that's what I wanna talk to you about, not the Ruby on Rails apps. Just because I thought it would be interesting, like, if I was here, I would wanna know what are people what are other people making with Directus. Right?\u003C\u002Fp>\u003Cp>So I thought I'd show you a couple of things that we've made at Pixielabs. The first one, is a company called Blackwing 7. They make bespoke cinematography lenses for film, TV, music videos, things like that. This is kinda what you maybe a site that you would traditionally expect for a headless CMS. Right?\u003C\u002Fp>\u003Cp>So, we actually took this over from another agency. It was all completely static, and the client wanted to be able to edit little bits of it, make some of it interactive, and things like that. And so that was something that we kind of turned to Directus to build. Something maybe a bit more unusual, thinking about that kind of, like, not just a headless CMS kind of vibe. This was a for good project called Red Flags.\u003C\u002Fp>\u003Cp>You can kind of think of it as like hazard perception but for consent. If any of you have taken your driving test and you've done the hazard perception, you know, you're watching a video of someone driving, usually through Wales, and you click a button every time there's a sheep on the road. Basically, that, but, but, yeah, for for consent. So there's, like, a video that you watch of someone going on a first date and you click a button every time you see, a, you know, an issue to do with consent. This was a campaign around that.\u003C\u002Fp>\u003Cp>And at the end, you get a score of how many you spotted or how many you missed, and you get to submit some details about you to sort of help their campaign, help understand who is engaging with this. And that's particularly interesting because that was where we were kind of using that ability. You know, Directus is ultimately just a wrapper around a database. Right? And so we could use that to store those results that were being submitted alongside the content that was part of the website as well.\u003C\u002Fp>\u003Cp>So really acting as more like a basic kind of app data store there than just a headless CMS. You kind of maybe something you might use Firebase for as well maybe is kind of like a sort of point of comparison. Kind of interesting. I'm gonna talk about a different project, for a client called Depuri that we worked on. I'm gonna tell you a little bit about that project, why we picked Directus, the extension that we had to build.\u003C\u002Fp>\u003Cp>That's the meat of it. And some tips as well for building your own extension if you, you know, are sort of wondering about how you'd go about doing that. Hopefully, by the end, you're gonna know some pitfalls to avoid if you do go about building an extension, how to get started as well, and, also, you're gonna hopefully know the best, in my opinion, way to, progressively load images on a website if you wanna do it in, like, a really, kind of optimum way because that's the extension that we built. So the project was, to build this kind of beautiful online art gallery, for a new brand, of online art curation and auctions called De Pury. We did a whole kind of tech stack evaluation and architecture process for this, and we recognized a couple of things.\u003C\u002Fp>\u003Cp>We knew that we wanted a bespoke front end so that we could wield all of our front end power, and we wanted a layer there that could, you know, we could kind of do whatever we wanted to do. So, we knew that the designs were gonna be you know, they wanted it to feel luxurious and feel sophisticated and expensive, and so we knew that we needed to be able to kind of tweak it a lot. And we recognized early on we needed a CMS because we did not want to have to edit all the content by hand. We wanted the client to do that. So we recognized pretty quickly that we needed a headless CMS.\u003C\u002Fp>\u003Cp>So Directus ended up being a good fit for a few reasons, actually. First of all, the image handling in Directus, if you've not explored that a bit, is really cool. We knew we were gonna need to be able to do resizing for different devices. We potentially thought we might have to do sort of interesting transformations of images. Maybe we wanted to make them grayscale or blur them or anything like that as part of the designs.\u003C\u002Fp>\u003Cp>And, Directus exposes the whole of the Sharp API. I don't know if anyone's a JavaScript dev. You might be familiar with Sharp. It's like ImageMagick or something like that. It's a really great package for manipulating images.\u003C\u002Fp>\u003Cp>Directus just gives you that whole thing. You can just drive it via the API which is super cool. You can also configure it to cache forever and have no expiry on caching. Right? Which was useful for us.\u003C\u002Fp>\u003Cp>We didn't want images randomly loading slow because Contentful had decided to delete some of our images from their cache or something like that. We really like the API. A lot of headless CMSs at GraphQL. I'm not really a huge fan of GraphQL. Really like that there's a REST API with Directus.\u003C\u002Fp>\u003Cp>It's just quicker and easier to get up and running for a small project. Right? It's easy to deploy. Tapuri had some existing infrastructure on Amazon Web Services, and so it's just a Node. Js app.\u003C\u002Fp>\u003Cp>You can deploy it somewhere. It'll align nicely with that. And lastly, we knew that we could extend it. Right? We felt comfort as we went into this project that we were like, well, if if there's something that directors can't do, we can at least write some JavaScript to make it do what we needed.\u003C\u002Fp>\u003Cp>It turns out, we did need it. So direct us wasn't quite enough. Once we got going, and we were iterating, we identified a problem with the way that the images were loading. The website was full of these large, high quality images. They wanted to sort of full bleed, you know, images of the artwork, that was being curated.\u003C\u002Fp>\u003Cp>And there were also just, like, galleries and it was just images. Right? Just thumbnails of images. And we wanted these to feel great, especially on mobile devices. The default approach, usually, is to do something like, low quality image placeholders.\u003C\u002Fp>\u003Cp>Is anyone is anyone familiar with the concept of low quality image placeholders? If you're not, it's like where you basically request a really tiny image that's, like, 6 by 6 pixels, then you make it as big as you want it, and you add some kind of blur to it. Right? And then as the real image loads, you cross fade from one to the other. You get kind of a nice fade as the image loads rather than just, like, a gray box.\u003C\u002Fp>\u003Cp>The problem is that that still takes time. Right? The page loads. We've got, like, 50 images that we wanna load. Now we're asking someone's web browser to load 50 images.\u003C\u002Fp>\u003Cp>Still lots of requests and so we ended up in this situation, the client wasn't happy with it, where you've got a bunch of gray boxes that quickly change to these blurry versions of images and then they change again to the real image and it just didn't look great. Right? Fortunately, we had a solution in our back pocket that we'd used before which is called blur hash. Is anyone familiar with blur hash as a thing? Alright.\u003C\u002Fp>\u003Cp>Great. Mostly shaking of heads, which is good. So Blahash, is a solution to this. It was made by a company called WALT, which is like a European Deliveroo competitor, which I hadn't heard of, I guess because we have Deliveroo. And they built a thing to to solve this problem.\u003C\u002Fp>\u003Cp>I'll just show you it quickly. So it's kind of like a representation of a placeholder of an image. Right? So, imagine they had the same problem. Right?\u003C\u002Fp>\u003Cp>They had this nice app and they just had these gray boxes and they were annoyed about it. What you basically do is you feed your image into the blur hash algorithm and you get, like, a very short representation of a blurry version of that image. Really, really short, like 10 to 20 characters, which you can then pass back to the blur hash library and it will generate a kind of blurry, smooshed version of the image. And what's cool about this over low quality image placeholders is that there's no extra request. You can include this, so when you're getting the data for your page, you're getting the data about your images, you can have the blur hash ready to go alongside the, like, URL, width, and height.\u003C\u002Fp>\u003Cp>So there's no extra request. One request for everything and you've already got those placeholder images. Super useful for us. Alright. So we knew we wanted blur hash.\u003C\u002Fp>\u003Cp>Directus doesn't have blur hash support by default, so so we needed to build an extension. Specifically, we built a hook extension, and I'll talk a little bit about different types of extensions but that, basically, is, like, custom JavaScript code that you can write and it will run at certain points defined by directors. Right? So, if you've ever written WordPress plugins, maybe, where you've got hooks, that'll feel really familiar or any kind of event based programming in JavaScript, you know, where you're like on this event, do this code. Basically, that.\u003C\u002Fp>\u003Cp>So, in theory, we were like, well, this is pretty straightforward. We want to add a new field against all our images to store the blur hash. Then, when an image is created or modified using that hook, we want to use the blur hash library to calculate that string that you saw for the image and then save it into that new field. In reality, there were a few interesting gotchas which I'll tell you about, but in principle it was pretty straightforward. I'll just show you it quickly, what it looks like in reality.\u003C\u002Fp>\u003Cp>So this was the site. Ignore my dev tools on the side here because I've got network throttling on to try and show you what it looks like. Please find the right button eventually. Hopefully, I've refreshed and cleared cache. Yeah.\u003C\u002Fp>\u003Cp>So, you can see the effect where the blur hash is already there and then it crossfades to the image. As I scroll down, some of these are still even on a pretend 3 gs they're still loading in time. But, you can see the effect. There's no flicker from one to another. There's no gray, then color, then image.\u003C\u002Fp>\u003Cp>It's just straight in. Some of the of course, they're now loading. You know, there we go. There's 1. We can go and look at this in Directus as well.\u003C\u002Fp>\u003Cp>So if I come in here, this is the Directus instance for this site, this is for the home page. If I create a new block here, I'm gonna pretend like they're curating my own photos. So if I create this home page block, if I go back and look at that now and go and look and edit that image, we can see that the blur hash has been added. Right? So the extension's got that field in there.\u003C\u002Fp>\u003Cp>It's generated it on save, and it's put it against the image. So now if I go and have a look, hopefully Imagine if it doesn't work out. Live demos was the worst. I tried to keep this one really simple. Yeah.\u003C\u002Fp>\u003Cp>So you can see the blur hash loaded straightaway and then, on my simulated 3 gs, eventually, my image loads. Look at that. Oh, there was one other thing as well. We were talking about that nice REST API. So you can see here, right, this is the this is all the JSON for the homepage.\u003C\u002Fp>\u003Cp>Single request to get the home page and get all of the related fields. Yeah? Sorry, can you carry on the flow? I'll ask when you're done. Okay.\u003C\u002Fp>\u003Cp>Yeah. I've got space for questions at the end. Hold it hold it in your head if you can. Yeah. So you can see, you can see the blur hashes here being included in with the JSON.\u003C\u002Fp>\u003Cp>Right? So they're all ready to go. Just drop it on. Cool. Alright.\u003C\u002Fp>\u003Cp>The fun stuff. What did we learn? Quite a bit. First of all, there's a Director's extension SDK, which is super useful for getting started. It has a nice kind of, like, command line interface for getting set up.\u003C\u002Fp>\u003Cp>It generates a kind of, like, directory depending on It asks you what kind of extension you wanna make, puts it all together for you, and kinda gets you started. There are lots of types of extensions, and that can be confusing. These are all the different types. Also, they're all kinda subtly different, in the way that they're, like, put together. At least when we were doing ours, hooks are are JavaScript and they're compiled using a thing called roll up, which I'll talk about in a sec.\u003C\u002Fp>\u003Cp>But then app extensions are like ECMAScript modules, and they're loaded in a little bit differently and it's very confusing and it's moved around a little bit and, yeah, there's definitely some funkiness there to watch out for. When you build an extension, especially a hook extension, all of the internal API services are made available to you. They're, like, handed to your code. Like, Here you go. You can use all of this stuff.\u003C\u002Fp>\u003Cp>There's no documentation for what any of that stuff is, Not even in the source code, there's not really comments for it. So you do have to kind of poke around to try and find what you need. I can actually show you an example where we've got our extension source code here. It's a little bit small, I know. But like this here, we're, like, calling the this asset service is coming from Directus.\u003C\u002Fp>\u003Cp>Directus is giving it to us. Presumably, they want us to use it. But this get asset method, you've just gotta guess, what it's doing. Right? And it changes because it's an internal API and I did feel a little bit nervous putting it in here, I will admit.\u003C\u002Fp>\u003Cp>But, hey, it works. So look over the source code. They are there to use. There's also no agreed way to embed a database migration in an extension. So you can write a migration as part of your as part of a director's extension.\u003C\u002Fp>\u003Cp>But, like, if you wanna release an extension that does something to the database, there's no kind of agreed way of doing that. That made us a little bit nervous. I think there's a few different approaches. You can ask the user to put it in themselves. You can copy it in sneak.\u003C\u002Fp>\u003Cp>Ly, which is what we do, or you can just do it at run time, which feels a little bit scary. Rollup, which is a tool that, at least for hook extensions is being used to kinda compile the extension. So you write your extension code and then Rollup builds it. Not webpack or anything like that. It's Rollup.\u003C\u002Fp>\u003Cp>Kind of funky. It does some weird stuff if you want some dependencies. If you're including some dependencies for your extension, roll up's gonna do its best to try and push those all to your extension, and it can make a bit of a mess. And that kinda relates to this. Testing locally can be painful.\u003C\u002Fp>\u003Cp>There is some stuff to help. The Director's, extension SDK has, like, a link tool that you can use to link your extension code into, like, a local Director. So while you're devbing and you're playing around, you can try stuff out That can mess with nodes, like dependency resolution, which can cause you trouble. So watch out for that. But, all that said, reading other extension code is super useful and there's 2 really great ways of doing that.\u003C\u002Fp>\u003Cp>1 is that there's this awesome Directus on GitHub. Everyone loves an awesome something on GitHub, which has got a list of extensions. Super useful to go through and look and see how are other people doing this, how are people solving this problem. And, also, you can just go on to npm and search keywords directors extension because that SDK adds that by default to your package JSON. So if anyone who does that and then publishes it, it gets added as a keyword, which makes it super easy to search.\u003C\u002Fp>\u003Cp>I think that was probably intentional so that they can find all the director's extensions, But useful as well if you're building your own. And hopefully, this doesn't I know there's a lot of negatives there, but it was kind of a, like, here's some stuff to watch out for. In general, it was actually pretty straightforward. The extension's only a 100 lines of code. There's not a lot to it.\u003C\u002Fp>\u003Cp>Right? And it was very easy to get started. And I think, like, there's lots of opportunities to build stuff here. Right? Like, this is a community, it's open source, which actually Kevin is gonna talk about.\u003C\u002Fp>\u003Cp>And it's hard to sell open source to clients, but it does sell well to us and to the team. Like, we believe quite strongly in open source as well. So being able to, like, do this and give back, is great. Yeah. Hopefully hopefully, that was useful and interesting.\u003C\u002Fp>\u003Cp>Thanks for listening, if it wasn't.\u003C\u002Fp>","Alright. Hi. My name's David. I'm gonna tell you a little bit about, extending directors to build a beautiful online art gallery. Can everyone hear me okay? If you can't, please, like, wave at me, shout at me, be like, Can't hear you. And then I'll speak up, we'll move this. A quick show of hands just so I can gauge, who here has used Directus on a project? Great. Good start. Who has used it for more than one project? Just because I'm curious. Nice. Who has, when they've been working on a director's project, you've used an extension as part of that? Alright. Starting to shrink. Great. Who has written a custom extension? Okay. Great. Phew. Otherwise, I was worried that I was going to be like, hey, you guys tell me how to write an extension. All right. Before I start, a little bit about me. I'm David. I'm head of engineering at Pixilabs. We are a digital product development agency. That means that we design and develop digital products, everything from internal platforms for FTSE 100 companies to manage smart home device installations, helping funded startups launch their product, all the way down to we once made a typewriter type by itself when you tweeted at it. And that was part of a digital art installation for Twitter, which was really fun. Basically, if we can code it, we will do our best to do that. And, actually, most of the time we build, like, full stack web applications in Ruby on Rails. But sometimes, we need a headless CMS and sometimes, we often turn to directors. So that's what I wanna talk to you about, not the Ruby on Rails apps. Just because I thought it would be interesting, like, if I was here, I would wanna know what are people what are other people making with Directus. Right? So I thought I'd show you a couple of things that we've made at Pixielabs. The first one, is a company called Blackwing 7. They make bespoke cinematography lenses for film, TV, music videos, things like that. This is kinda what you maybe a site that you would traditionally expect for a headless CMS. Right? So, we actually took this over from another agency. It was all completely static, and the client wanted to be able to edit little bits of it, make some of it interactive, and things like that. And so that was something that we kind of turned to Directus to build. Something maybe a bit more unusual, thinking about that kind of, like, not just a headless CMS kind of vibe. This was a for good project called Red Flags. You can kind of think of it as like hazard perception but for consent. If any of you have taken your driving test and you've done the hazard perception, you know, you're watching a video of someone driving, usually through Wales, and you click a button every time there's a sheep on the road. Basically, that, but, but, yeah, for for consent. So there's, like, a video that you watch of someone going on a first date and you click a button every time you see, a, you know, an issue to do with consent. This was a campaign around that. And at the end, you get a score of how many you spotted or how many you missed, and you get to submit some details about you to sort of help their campaign, help understand who is engaging with this. And that's particularly interesting because that was where we were kind of using that ability. You know, Directus is ultimately just a wrapper around a database. Right? And so we could use that to store those results that were being submitted alongside the content that was part of the website as well. So really acting as more like a basic kind of app data store there than just a headless CMS. You kind of maybe something you might use Firebase for as well maybe is kind of like a sort of point of comparison. Kind of interesting. I'm gonna talk about a different project, for a client called Depuri that we worked on. I'm gonna tell you a little bit about that project, why we picked Directus, the extension that we had to build. That's the meat of it. And some tips as well for building your own extension if you, you know, are sort of wondering about how you'd go about doing that. Hopefully, by the end, you're gonna know some pitfalls to avoid if you do go about building an extension, how to get started as well, and, also, you're gonna hopefully know the best, in my opinion, way to, progressively load images on a website if you wanna do it in, like, a really, kind of optimum way because that's the extension that we built. So the project was, to build this kind of beautiful online art gallery, for a new brand, of online art curation and auctions called De Pury. We did a whole kind of tech stack evaluation and architecture process for this, and we recognized a couple of things. We knew that we wanted a bespoke front end so that we could wield all of our front end power, and we wanted a layer there that could, you know, we could kind of do whatever we wanted to do. So, we knew that the designs were gonna be you know, they wanted it to feel luxurious and feel sophisticated and expensive, and so we knew that we needed to be able to kind of tweak it a lot. And we recognized early on we needed a CMS because we did not want to have to edit all the content by hand. We wanted the client to do that. So we recognized pretty quickly that we needed a headless CMS. So Directus ended up being a good fit for a few reasons, actually. First of all, the image handling in Directus, if you've not explored that a bit, is really cool. We knew we were gonna need to be able to do resizing for different devices. We potentially thought we might have to do sort of interesting transformations of images. Maybe we wanted to make them grayscale or blur them or anything like that as part of the designs. And, Directus exposes the whole of the Sharp API. I don't know if anyone's a JavaScript dev. You might be familiar with Sharp. It's like ImageMagick or something like that. It's a really great package for manipulating images. Directus just gives you that whole thing. You can just drive it via the API which is super cool. You can also configure it to cache forever and have no expiry on caching. Right? Which was useful for us. We didn't want images randomly loading slow because Contentful had decided to delete some of our images from their cache or something like that. We really like the API. A lot of headless CMSs at GraphQL. I'm not really a huge fan of GraphQL. Really like that there's a REST API with Directus. It's just quicker and easier to get up and running for a small project. Right? It's easy to deploy. Tapuri had some existing infrastructure on Amazon Web Services, and so it's just a Node. Js app. You can deploy it somewhere. It'll align nicely with that. And lastly, we knew that we could extend it. Right? We felt comfort as we went into this project that we were like, well, if if there's something that directors can't do, we can at least write some JavaScript to make it do what we needed. It turns out, we did need it. So direct us wasn't quite enough. Once we got going, and we were iterating, we identified a problem with the way that the images were loading. The website was full of these large, high quality images. They wanted to sort of full bleed, you know, images of the artwork, that was being curated. And there were also just, like, galleries and it was just images. Right? Just thumbnails of images. And we wanted these to feel great, especially on mobile devices. The default approach, usually, is to do something like, low quality image placeholders. Is anyone is anyone familiar with the concept of low quality image placeholders? If you're not, it's like where you basically request a really tiny image that's, like, 6 by 6 pixels, then you make it as big as you want it, and you add some kind of blur to it. Right? And then as the real image loads, you cross fade from one to the other. You get kind of a nice fade as the image loads rather than just, like, a gray box. The problem is that that still takes time. Right? The page loads. We've got, like, 50 images that we wanna load. Now we're asking someone's web browser to load 50 images. Still lots of requests and so we ended up in this situation, the client wasn't happy with it, where you've got a bunch of gray boxes that quickly change to these blurry versions of images and then they change again to the real image and it just didn't look great. Right? Fortunately, we had a solution in our back pocket that we'd used before which is called blur hash. Is anyone familiar with blur hash as a thing? Alright. Great. Mostly shaking of heads, which is good. So Blahash, is a solution to this. It was made by a company called WALT, which is like a European Deliveroo competitor, which I hadn't heard of, I guess because we have Deliveroo. And they built a thing to to solve this problem. I'll just show you it quickly. So it's kind of like a representation of a placeholder of an image. Right? So, imagine they had the same problem. Right? They had this nice app and they just had these gray boxes and they were annoyed about it. What you basically do is you feed your image into the blur hash algorithm and you get, like, a very short representation of a blurry version of that image. Really, really short, like 10 to 20 characters, which you can then pass back to the blur hash library and it will generate a kind of blurry, smooshed version of the image. And what's cool about this over low quality image placeholders is that there's no extra request. You can include this, so when you're getting the data for your page, you're getting the data about your images, you can have the blur hash ready to go alongside the, like, URL, width, and height. So there's no extra request. One request for everything and you've already got those placeholder images. Super useful for us. Alright. So we knew we wanted blur hash. Directus doesn't have blur hash support by default, so so we needed to build an extension. Specifically, we built a hook extension, and I'll talk a little bit about different types of extensions but that, basically, is, like, custom JavaScript code that you can write and it will run at certain points defined by directors. Right? So, if you've ever written WordPress plugins, maybe, where you've got hooks, that'll feel really familiar or any kind of event based programming in JavaScript, you know, where you're like on this event, do this code. Basically, that. So, in theory, we were like, well, this is pretty straightforward. We want to add a new field against all our images to store the blur hash. Then, when an image is created or modified using that hook, we want to use the blur hash library to calculate that string that you saw for the image and then save it into that new field. In reality, there were a few interesting gotchas which I'll tell you about, but in principle it was pretty straightforward. I'll just show you it quickly, what it looks like in reality. So this was the site. Ignore my dev tools on the side here because I've got network throttling on to try and show you what it looks like. Please find the right button eventually. Hopefully, I've refreshed and cleared cache. Yeah. So, you can see the effect where the blur hash is already there and then it crossfades to the image. As I scroll down, some of these are still even on a pretend 3 gs they're still loading in time. But, you can see the effect. There's no flicker from one to another. There's no gray, then color, then image. It's just straight in. Some of the of course, they're now loading. You know, there we go. There's 1. We can go and look at this in Directus as well. So if I come in here, this is the Directus instance for this site, this is for the home page. If I create a new block here, I'm gonna pretend like they're curating my own photos. So if I create this home page block, if I go back and look at that now and go and look and edit that image, we can see that the blur hash has been added. Right? So the extension's got that field in there. It's generated it on save, and it's put it against the image. So now if I go and have a look, hopefully Imagine if it doesn't work out. Live demos was the worst. I tried to keep this one really simple. Yeah. So you can see the blur hash loaded straightaway and then, on my simulated 3 gs, eventually, my image loads. Look at that. Oh, there was one other thing as well. We were talking about that nice REST API. So you can see here, right, this is the this is all the JSON for the homepage. Single request to get the home page and get all of the related fields. Yeah? Sorry, can you carry on the flow? I'll ask when you're done. Okay. Yeah. I've got space for questions at the end. Hold it hold it in your head if you can. Yeah. So you can see, you can see the blur hashes here being included in with the JSON. Right? So they're all ready to go. Just drop it on. Cool. Alright. The fun stuff. What did we learn? Quite a bit. First of all, there's a Director's extension SDK, which is super useful for getting started. It has a nice kind of, like, command line interface for getting set up. It generates a kind of, like, directory depending on It asks you what kind of extension you wanna make, puts it all together for you, and kinda gets you started. There are lots of types of extensions, and that can be confusing. These are all the different types. Also, they're all kinda subtly different, in the way that they're, like, put together. At least when we were doing ours, hooks are are JavaScript and they're compiled using a thing called roll up, which I'll talk about in a sec. But then app extensions are like ECMAScript modules, and they're loaded in a little bit differently and it's very confusing and it's moved around a little bit and, yeah, there's definitely some funkiness there to watch out for. When you build an extension, especially a hook extension, all of the internal API services are made available to you. They're, like, handed to your code. Like, Here you go. You can use all of this stuff. There's no documentation for what any of that stuff is, Not even in the source code, there's not really comments for it. So you do have to kind of poke around to try and find what you need. I can actually show you an example where we've got our extension source code here. It's a little bit small, I know. But like this here, we're, like, calling the this asset service is coming from Directus. Directus is giving it to us. Presumably, they want us to use it. But this get asset method, you've just gotta guess, what it's doing. Right? And it changes because it's an internal API and I did feel a little bit nervous putting it in here, I will admit. But, hey, it works. So look over the source code. They are there to use. There's also no agreed way to embed a database migration in an extension. So you can write a migration as part of your as part of a director's extension. But, like, if you wanna release an extension that does something to the database, there's no kind of agreed way of doing that. That made us a little bit nervous. I think there's a few different approaches. You can ask the user to put it in themselves. You can copy it in sneak. Ly, which is what we do, or you can just do it at run time, which feels a little bit scary. Rollup, which is a tool that, at least for hook extensions is being used to kinda compile the extension. So you write your extension code and then Rollup builds it. Not webpack or anything like that. It's Rollup. Kind of funky. It does some weird stuff if you want some dependencies. If you're including some dependencies for your extension, roll up's gonna do its best to try and push those all to your extension, and it can make a bit of a mess. And that kinda relates to this. Testing locally can be painful. There is some stuff to help. The Director's, extension SDK has, like, a link tool that you can use to link your extension code into, like, a local Director. So while you're devbing and you're playing around, you can try stuff out That can mess with nodes, like dependency resolution, which can cause you trouble. So watch out for that. But, all that said, reading other extension code is super useful and there's 2 really great ways of doing that. 1 is that there's this awesome Directus on GitHub. Everyone loves an awesome something on GitHub, which has got a list of extensions. Super useful to go through and look and see how are other people doing this, how are people solving this problem. And, also, you can just go on to npm and search keywords directors extension because that SDK adds that by default to your package JSON. So if anyone who does that and then publishes it, it gets added as a keyword, which makes it super easy to search. I think that was probably intentional so that they can find all the director's extensions, But useful as well if you're building your own. And hopefully, this doesn't I know there's a lot of negatives there, but it was kind of a, like, here's some stuff to watch out for. In general, it was actually pretty straightforward. The extension's only a 100 lines of code. There's not a lot to it. Right? And it was very easy to get started. And I think, like, there's lots of opportunities to build stuff here. Right? Like, this is a community, it's open source, which actually Kevin is gonna talk about. And it's hard to sell open source to clients, but it does sell well to us and to the team. Like, we believe quite strongly in open source as well. So being able to, like, do this and give back, is great. Yeah. Hopefully hopefully, that was useful and interesting. Thanks for listening, if it wasn't.",[5900],"6608dfaa-4ae5-4923-a28e-59b5a35f8aee",[],{"year":2380,"number":5683,"id":5684,"episodes":5903,"show":5904},[5686,5687,5688,5662],{"title":4829,"slug":4830},{"id":2387,"slug":5906,"vimeo_id":5907,"description":5908,"tile":5909,"length":264,"resources":12,"people":5910,"episode_number":213,"published":5912,"title":5913,"video_transcript_html":5914,"video_transcript_text":5915,"content":12,"seo":12,"status":19,"episode_people":5916,"recommendations":5918,"season":5919},"10-5","894083223","Join Alex as he gives you a whirlwind tour of notable new features in Directus 10.5 - including huge updates to our API reference.","27bfa0a8-4801-42b7-8fc0-ba4210dba43e",[5911],{"name":4145,"url":4146},"2023-07-26","What's New In Directus Version 10.5","\u003Cp>Speaker 0: Hey there. My name is Alex, and I'm a solutions engineer here at Directus. Today, I'm gonna take you through a couple of new features in version 10.5. First off, there's been a full release of our new SDK. We've been hard at work building our new SDK, and today marks its full release.\u003C\u002Fp>\u003Cp>You can expect proper type safety, modular, importable functions, that means you can reduce your overall build size, and support for Directus real time. Of course, if you don't use TypeScript, then the new SDK works great for you as well. We've built the new SDK in the open, and thank you to all of the community members who have helped and provided feedback and participated in the process. Every endpoint has code snippets in our API reference, and all guides have been updated in the docs to use the new SDK. Speaking of API reference, an improved Directus API reference.\u003C\u002Fp>\u003Cp>Directus has almost 200 endpoints for working with your data, your files, and projects. We have given our API reference some love and introduced a new component that allows you to see the rest, the GraphQL, or the SDK examples easily. It remembers your preference so you can always see what's relevant to you. Supabase file storage adapter. Another awesome community contribution, this time from Matt, adds a Supabase storage driver, and this means that you can use Supabase storage while still managing your files through Directus.\u003C\u002Fp>\u003Cp>Configuration details can be found in the docs. Awesome community contributions. We also want to thank our community for their contributions towards this release. Thanks to Bevis for working with the team to improve our API reference with the new REST GraphQL toggler, as well as the new SDK snippets. Thanks to Matt for building the SuperBase file storage adapter that we spoke about earlier.\u003C\u002Fp>\u003Cp>Thank you to Magnus for fixing a small visual bug on our Kanban layout when displaying formatted values. And thanks to Ashish for fixing a couple of display issues with our insight panels. Finally, thanks to Gerard for a trio of contributions, One that adds support for deep end use items, one that fixes permissions not honoring dynamic values, and one that simplifies some code around flows returning status codes. Director's 10.5 is now available on NPM and Docker Hub and will be available on Directus Cloud early next week. Thanks for joining me, and see you in the next release.\u003C\u002Fp>","Hey there. My name is Alex, and I'm a solutions engineer here at Directus. Today, I'm gonna take you through a couple of new features in version 10.5. First off, there's been a full release of our new SDK. We've been hard at work building our new SDK, and today marks its full release. You can expect proper type safety, modular, importable functions, that means you can reduce your overall build size, and support for Directus real time. Of course, if you don't use TypeScript, then the new SDK works great for you as well. We've built the new SDK in the open, and thank you to all of the community members who have helped and provided feedback and participated in the process. Every endpoint has code snippets in our API reference, and all guides have been updated in the docs to use the new SDK. Speaking of API reference, an improved Directus API reference. Directus has almost 200 endpoints for working with your data, your files, and projects. We have given our API reference some love and introduced a new component that allows you to see the rest, the GraphQL, or the SDK examples easily. It remembers your preference so you can always see what's relevant to you. Supabase file storage adapter. Another awesome community contribution, this time from Matt, adds a Supabase storage driver, and this means that you can use Supabase storage while still managing your files through Directus. Configuration details can be found in the docs. Awesome community contributions. We also want to thank our community for their contributions towards this release. Thanks to Bevis for working with the team to improve our API reference with the new REST GraphQL toggler, as well as the new SDK snippets. Thanks to Matt for building the SuperBase file storage adapter that we spoke about earlier. Thank you to Magnus for fixing a small visual bug on our Kanban layout when displaying formatted values. And thanks to Ashish for fixing a couple of display issues with our insight panels. Finally, thanks to Gerard for a trio of contributions, One that adds support for deep end use items, one that fixes permissions not honoring dynamic values, and one that simplifies some code around flows returning status codes. Director's 10.5 is now available on NPM and Docker Hub and will be available on Directus Cloud early next week. Thanks for joining me, and see you in the next release.",[5917],"e3703be4-6709-4039-934f-aa25d0d10689",[],{"year":2380,"number":588,"id":2381,"episodes":5920,"show":5921},[2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394,2366],{"title":2396,"slug":2397},{"id":5686,"slug":5923,"vimeo_id":5924,"description":5925,"tile":5926,"length":128,"resources":12,"people":5927,"episode_number":13,"published":5931,"title":5932,"video_transcript_html":5933,"video_transcript_text":5934,"content":12,"seo":12,"status":19,"episode_people":5935,"recommendations":5937,"season":5938},"keep-the-money-flow","894205126","Andreas uses Directus in lots of personal projects to improve the efficiency of his household. In this talk, we hear about his pocket money tracker used with his kids.","2985311c-f9d6-414c-b2ea-394109e4b0ea",[5928],{"name":5929,"url":5930},"Andreas Morgner","https:\u002F\u002Fgithub.com\u002Famo42","2023-07-14","Keep the Money Flow - Berlin Meetup","\u003Cp>Speaker 0: I will talk to you, about how I, how I use, directors within, my family for this talk. So basically, I would start with who I am. So my name is Andreas. As already mentioned, I'm father of 3. Actually, I'm working as a Atlassian consultant in a technical way, so, mostly for digitalization, optimization, and so on.\u003C\u002Fp>\u003Cp>So at work, I do not have any contacts with directors at all, so it's more on a hobby side and, a private interest. Yeah. I'm living in a countryside outside of Berlin, so, quite interesting journey today, and I like directors a lot. At some point, yeah, Kevin asked me, to no. I don't the other way around.\u003C\u002Fp>\u003Cp>I was, helping out in the Discord and ask questions, answering questions, and so on, and then he just promoted me from, a regular member to a moderator, and just overnight I was not informed about this. Thank you. Yeah. Today, I will talk about one project I'm using directives for. So what where's the problem?\u003C\u002Fp>\u003Cp>My children, I get 2 children or 3 2 of them get pocket money already. So they get, they get them from different people, from the grandparents, from us, from aunts, auntles, and so and, yeah, they just play around with it. It's dirty. They stick it all over the the the the rooms and so on, and also they try to stole it from each other. So in the end, they lose it.\u003C\u002Fp>\u003Cp>So yeah. Then we started to write some paper notes with the amount of money, gave it to them, and collect the money for them, but then they lose the paper. And in the end, they don't have the pocket money anymore, and they were sad. So my idea was to, yeah, use just as I do it on work, some digital, assistance, and I came up with directors. In the past, I've already worked with directors several times, so, I've wrote my master thesis with directors as a back end.\u003C\u002Fp>\u003Cp>There was, a testing environment for automated UI tests, so I know the tool already, and, yeah, started with directors implementing, a pocket money system. So it's very, very easy, but I guess the use case could be interesting because it's not a website or asset store or something like this. Basically, it's just containing, a couple of collections, as you can see. So, these were the actually, yeah, collections where I store the information about the accounts of the children so each each child get one account. Then they have transactions and also, there's the third one which is core transfer.\u003C\u002Fp>\u003Cp>This is currently not implemented completely, but the idea is there to store all the information, and have an overview about the money, how much, what is the income, what goes out, and so on. So, basically, it's just a couple of selections. Currently, a single flow that is updating all this stuff in the background once a new transaction is placed. And, yeah, in the end, no more struggle. If they want to pay something, they ask us, we pay for it with our money because we have all the money on our personal, bank accounts, and they just have some virtual money they can spend.\u003C\u002Fp>\u003Cp>So, I guess yesterday, my son came up, want to have a new game on the Switch. I paid for it, reduced his amount on the account, and they can have a look at any time what is the current amount. So yeah basically that's it and I'd like to show you how I was implementing this just indirect this, like this. Yes. I am.\u003C\u002Fp>\u003Cp>At home, it was working. I swear. So that's the pain of self rusting it, so I really should consider using the cloud. Now it's working. Yeah.\u003C\u002Fp>\u003Cp>As you can see, I've got a lot of collections just for all the private stuff I did. And today, we will look at the PocketMiner sections. So as mentioned, you see the accounts. This is real data by, mentioned. So my son is currently saving for a personal computer, and my daughter just bought an iPhone.\u003C\u002Fp>\u003Cp>So that's the result. So the current pocket money goes into this account. And, yeah, there are also 2 testing accounts. I currently using this for, setting up the new flow, because right now, I get only the, transaction implemented. So, currently, you have to, select the transaction, and this will just bring up a new dialogue for you where you can enter all the details about which account, which amount, Is it income?\u003C\u002Fp>\u003Cp>Is it outgoing? So, yeah, you can just select the account. You can also specify, some date just for the record where you, would like to look this up later on. So you can just use the interface here, from the Data Studio and then you can decide if you would like to have it an in as an income or an outgoing transaction. I used both fields and not a negative value for outgoing just to have some differences because sometimes they gave us, €10 and want to have 20, and I don't want to calculate this by my own, so I just put the numbers in and fine with it.\u003C\u002Fp>\u003Cp>So as you can see, I can just enter amount, optional, some comments to have the information later on what was this about. And just as I enter it, it will automatically, update the account. So right now, the account was just increased by 10 as expected. And if I go to a new, transaction, I can also reduce it, of course. So let's say we want to buy something for today of the same account, of course.\u003C\u002Fp>\u003Cp>It's very basic. Yeah. In the end, it's currently just, a single flow that, just uses, very basic information about what is going on. So every time a new transaction was placed, I will look up the account, get the current, information about this account, read out the values, do some calculations. So get the current account at the income at the outgoing and then save the new information within this account.\u003C\u002Fp>\u003Cp>If something goes wrong, it's locked. And yeah. The next step would be to implement this for the transactions as well. But as mentioned, this is currently not implemented. Just starting about it.\u003C\u002Fp>\u003Cp>So it goes from one account to another directly. So right now I have to do 2 transactions. 1 was outgoing, 1 was incoming. This should be, much easier so they can transfer sometimes money from one account to another account. Currently, they're not, having the needs for it, but I guess this could be much easier if they have to use this.\u003C\u002Fp>\u003Cp>Yeah. Basically that's it. Are there any questions or should I talk about some other fancy things I do with my hobby projects? Sorry? Do you charge interest\u003C\u002Fp>\u003Cp>Speaker 1: to Isabelle and overdraft fee?\u003C\u002Fp>\u003Cp>Speaker 0: I was thinking about it. So there there is there is actually a flow about about since as we call it in German. So yes, I were thinking about it, but currently it's disabled as you can see. But yeah.\u003C\u002Fp>\u003Cp>Speaker 1: It's okay. There's a little threat there.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: So how do they interact with it or do they always interact with it through you?\u003C\u002Fp>\u003Cp>Speaker 0: Currently just with with me directly, as they now have an iPhone of course. I was also starting about, building up an app for this. I've started couple of months ago, but have actually no time to yeah, but I want to keep it simple for a moment. Yeah. Of course, I have some, some insights as well.\u003C\u002Fp>\u003Cp>For for instance, this this is very basic. So just yes. I'm not the only one using it. I have no benefits, right now. But yeah.\u003C\u002Fp>\u003Cp>Could be as well used for this. So are you are you using direct as as, like, something for for a lot of stuff I saw there? Yeah. So for instance, another one is, about our newborn. So she has a tracking, fitness tracker, about the the, temperature, the heart rate, the oxygen level, and so on.\u003C\u002Fp>\u003Cp>And, actually, it's tracking this also by using a flow. So I've kind of injected the app that comes with the tracker, which is not really handy because it sometimes turns off or it could get only connection to one phone and not to my wife, only to my and so on. So I've started to track the data on the network and get the, token they're using to transmit the data. And as you can see, this is now going to bed for 70 minutes since he's sleeping yay and yeah this is all the information I get from from the, from the tracker. And with this, I can just have, some more information using insights for instance.\u003C\u002Fp>\u003Cp>So it's tracking this every minute as mentioned so you can see there are 30 minutes here. So usually the heart rate should go down further and further as she's sleeping, and the last 12 hours are not working because she's not wearing it over the day. And here's the data with just 2 hours. So we can also see if she gets awake because then the heart rate starts to increase. And for instance, if she's, ill or got fever, we also could see the information that the heart rate, for instance, is much higher.\u003C\u002Fp>\u003Cp>And also this is, currently implemented that once, some thresholds are broken or overwritten an alert is created so I will not have to use the the app that comes from the vendor. But instead, an Opsgenie is, alert is created. So it fires my Apple Watch, my phone. 10 minutes later, I get a a phone from Opsgenie that are calling and shouting to me that I should have look up what is going on here. Currently also working for the battery level, for instance.\u003C\u002Fp>\u003Cp>So, yeah, also very special use case I would say. I actually is just, reading the data from the application they offer. So, the the tracker sends the information to website and I just read the website similar to the app that is on the phone. So it's not synchronizing the data actually it's just reading out and saving it indirect. Like periodically?\u003C\u002Fp>\u003Cp>Yes. Like\u003C\u002Fp>\u003Cp>Speaker 1: scraping a web page knowing that\u003C\u002Fp>\u003Cp>Speaker 0: actually, it's an API that I'm calling with the flow. So the flow runs every minute, asking for the data, transform this, into actual fields. So there's coming some JSON payload from the API, and I using a flow to transform this all into actual fields I can then work with. As you can see, the Opsgenie's Opsgenie alerts are here as well so every time, a new record is created it's checking about some variables and if one of them is not okay then, and the node is created with an oxygen, And then all the rest is done by oxygen. But I have to notice, you have to turn off the tracking and logging all of the flow and all the create events within those databases because it started to get really slow on my instance, because every minute so much data is collected and stored.\u003C\u002Fp>\u003Cp>I have 2,200 30,000 items in this collection, and it's just a single core instance with 2 gigabyte of RAM or so. It's coming to the end, but it's working fine. What else? I've already told, I'm using it for laser cutter. So we have laser cutter for for handmade stuff.\u003C\u002Fp>\u003Cp>So, my wife is kneeling, and every time she has an idea, I save the material and the settings. And I also have, I guess, some pictures. No. I don't I have one, so I can just see the result or not here it is. So this is the data from my newborn.\u003C\u002Fp>\u003Cp>So I can see which, sets I using, which material, was it cutting, was it engraving. Yeah. So I store all this information within directors as well. Yeah. Also some invoicing that comes to our, mailbox, but I don't want to show you this.\u003C\u002Fp>\u003Cp>Also, the the days of sickness of my child are tracked. So I can just have a look and compare this to the school. So I track each day a child is ill and also started to use this as a PDF creation. So it just comes with one click. Currently, it's generating a PDF or it doesn't.\u003C\u002Fp>\u003Cp>It's We\u003C\u002Fp>\u003Cp>Speaker 1: will start.\u003C\u002Fp>\u003Cp>Speaker 0: No. No. No. It's still referring to a wrong URL. I see.\u003C\u002Fp>\u003Cp>So it's the the old one. That's the problem. It's generating a PDF, but not with the actual data yet. So that's still on my to do list. Anything else?\u003C\u002Fp>\u003Cp>No. Oh, yeah. One one funny thing. I've tracked the state of the TV power of my son because because it's in HomeKit, and there's an automation that sends the information to directors because he have a smart switch on the wallet and thinks he can trick me and turn it on after I turn it off. So every time he turns it on again, I will see it and can show him he turned it on again.\u003C\u002Fp>\u003Cp>So Yeah. Yeah. It's just about communicating to each other. It's also possible to call back to HomeKit, but I guess I have to implement this on some Raspberry Pi solution that is able to receive those requests. Yeah.\u003C\u002Fp>\u003Cp>So basically, it's a problem that I think of and try to find out a solution for this as often, And, yeah, I'm very familiar with with databases in general and with issue issue tracking systems. So I always think of how can I automate things and which information are necessary? And the cool thing is you can just add new fields, remove them, change them, add add a new collection that just combines some information or features. So it's just more iterative. So starting with, the accounts and at the the current, just overall value and do the math manually.\u003C\u002Fp>\u003Cp>Afterwards, I store the transactions, and then flows come in because in the middle, I was just about to develop an own extension that do this, what flows now offers. But as not a full time developer, I'm not so happy with the solution. It was not working, I guess. So with flows, it's super easy. Yeah.\u003C\u002Fp>\u003Cp>So it was just, starting with a simple one and then add some more features, change something, add new fields, remove them. Does it answer the question? Or For me, is it it's easy. Yes. To be honest, it's much easier to have some simple automations compared to Jira, for instance.\u003C\u002Fp>\u003Cp>So I've also had the the the automation to have the, cat litter cleaned by children with an automation in Jira, but it was too complex. It it is you can set it up, but it direct us. It's more easy.\u003C\u002Fp>","I will talk to you, about how I, how I use, directors within, my family for this talk. So basically, I would start with who I am. So my name is Andreas. As already mentioned, I'm father of 3. Actually, I'm working as a Atlassian consultant in a technical way, so, mostly for digitalization, optimization, and so on. So at work, I do not have any contacts with directors at all, so it's more on a hobby side and, a private interest. Yeah. I'm living in a countryside outside of Berlin, so, quite interesting journey today, and I like directors a lot. At some point, yeah, Kevin asked me, to no. I don't the other way around. I was, helping out in the Discord and ask questions, answering questions, and so on, and then he just promoted me from, a regular member to a moderator, and just overnight I was not informed about this. Thank you. Yeah. Today, I will talk about one project I'm using directives for. So what where's the problem? My children, I get 2 children or 3 2 of them get pocket money already. So they get, they get them from different people, from the grandparents, from us, from aunts, auntles, and so and, yeah, they just play around with it. It's dirty. They stick it all over the the the the rooms and so on, and also they try to stole it from each other. So in the end, they lose it. So yeah. Then we started to write some paper notes with the amount of money, gave it to them, and collect the money for them, but then they lose the paper. And in the end, they don't have the pocket money anymore, and they were sad. So my idea was to, yeah, use just as I do it on work, some digital, assistance, and I came up with directors. In the past, I've already worked with directors several times, so, I've wrote my master thesis with directors as a back end. There was, a testing environment for automated UI tests, so I know the tool already, and, yeah, started with directors implementing, a pocket money system. So it's very, very easy, but I guess the use case could be interesting because it's not a website or asset store or something like this. Basically, it's just containing, a couple of collections, as you can see. So, these were the actually, yeah, collections where I store the information about the accounts of the children so each each child get one account. Then they have transactions and also, there's the third one which is core transfer. This is currently not implemented completely, but the idea is there to store all the information, and have an overview about the money, how much, what is the income, what goes out, and so on. So, basically, it's just a couple of selections. Currently, a single flow that is updating all this stuff in the background once a new transaction is placed. And, yeah, in the end, no more struggle. If they want to pay something, they ask us, we pay for it with our money because we have all the money on our personal, bank accounts, and they just have some virtual money they can spend. So, I guess yesterday, my son came up, want to have a new game on the Switch. I paid for it, reduced his amount on the account, and they can have a look at any time what is the current amount. So yeah basically that's it and I'd like to show you how I was implementing this just indirect this, like this. Yes. I am. At home, it was working. I swear. So that's the pain of self rusting it, so I really should consider using the cloud. Now it's working. Yeah. As you can see, I've got a lot of collections just for all the private stuff I did. And today, we will look at the PocketMiner sections. So as mentioned, you see the accounts. This is real data by, mentioned. So my son is currently saving for a personal computer, and my daughter just bought an iPhone. So that's the result. So the current pocket money goes into this account. And, yeah, there are also 2 testing accounts. I currently using this for, setting up the new flow, because right now, I get only the, transaction implemented. So, currently, you have to, select the transaction, and this will just bring up a new dialogue for you where you can enter all the details about which account, which amount, Is it income? Is it outgoing? So, yeah, you can just select the account. You can also specify, some date just for the record where you, would like to look this up later on. So you can just use the interface here, from the Data Studio and then you can decide if you would like to have it an in as an income or an outgoing transaction. I used both fields and not a negative value for outgoing just to have some differences because sometimes they gave us, €10 and want to have 20, and I don't want to calculate this by my own, so I just put the numbers in and fine with it. So as you can see, I can just enter amount, optional, some comments to have the information later on what was this about. And just as I enter it, it will automatically, update the account. So right now, the account was just increased by 10 as expected. And if I go to a new, transaction, I can also reduce it, of course. So let's say we want to buy something for today of the same account, of course. It's very basic. Yeah. In the end, it's currently just, a single flow that, just uses, very basic information about what is going on. So every time a new transaction was placed, I will look up the account, get the current, information about this account, read out the values, do some calculations. So get the current account at the income at the outgoing and then save the new information within this account. If something goes wrong, it's locked. And yeah. The next step would be to implement this for the transactions as well. But as mentioned, this is currently not implemented. Just starting about it. So it goes from one account to another directly. So right now I have to do 2 transactions. 1 was outgoing, 1 was incoming. This should be, much easier so they can transfer sometimes money from one account to another account. Currently, they're not, having the needs for it, but I guess this could be much easier if they have to use this. Yeah. Basically that's it. Are there any questions or should I talk about some other fancy things I do with my hobby projects? Sorry? Do you charge interest to Isabelle and overdraft fee? I was thinking about it. So there there is there is actually a flow about about since as we call it in German. So yes, I were thinking about it, but currently it's disabled as you can see. But yeah. It's okay. There's a little threat there. Yeah. Yeah. So how do they interact with it or do they always interact with it through you? Currently just with with me directly, as they now have an iPhone of course. I was also starting about, building up an app for this. I've started couple of months ago, but have actually no time to yeah, but I want to keep it simple for a moment. Yeah. Of course, I have some, some insights as well. For for instance, this this is very basic. So just yes. I'm not the only one using it. I have no benefits, right now. But yeah. Could be as well used for this. So are you are you using direct as as, like, something for for a lot of stuff I saw there? Yeah. So for instance, another one is, about our newborn. So she has a tracking, fitness tracker, about the the, temperature, the heart rate, the oxygen level, and so on. And, actually, it's tracking this also by using a flow. So I've kind of injected the app that comes with the tracker, which is not really handy because it sometimes turns off or it could get only connection to one phone and not to my wife, only to my and so on. So I've started to track the data on the network and get the, token they're using to transmit the data. And as you can see, this is now going to bed for 70 minutes since he's sleeping yay and yeah this is all the information I get from from the, from the tracker. And with this, I can just have, some more information using insights for instance. So it's tracking this every minute as mentioned so you can see there are 30 minutes here. So usually the heart rate should go down further and further as she's sleeping, and the last 12 hours are not working because she's not wearing it over the day. And here's the data with just 2 hours. So we can also see if she gets awake because then the heart rate starts to increase. And for instance, if she's, ill or got fever, we also could see the information that the heart rate, for instance, is much higher. And also this is, currently implemented that once, some thresholds are broken or overwritten an alert is created so I will not have to use the the app that comes from the vendor. But instead, an Opsgenie is, alert is created. So it fires my Apple Watch, my phone. 10 minutes later, I get a a phone from Opsgenie that are calling and shouting to me that I should have look up what is going on here. Currently also working for the battery level, for instance. So, yeah, also very special use case I would say. I actually is just, reading the data from the application they offer. So, the the tracker sends the information to website and I just read the website similar to the app that is on the phone. So it's not synchronizing the data actually it's just reading out and saving it indirect. Like periodically? Yes. Like scraping a web page knowing that actually, it's an API that I'm calling with the flow. So the flow runs every minute, asking for the data, transform this, into actual fields. So there's coming some JSON payload from the API, and I using a flow to transform this all into actual fields I can then work with. As you can see, the Opsgenie's Opsgenie alerts are here as well so every time, a new record is created it's checking about some variables and if one of them is not okay then, and the node is created with an oxygen, And then all the rest is done by oxygen. But I have to notice, you have to turn off the tracking and logging all of the flow and all the create events within those databases because it started to get really slow on my instance, because every minute so much data is collected and stored. I have 2,200 30,000 items in this collection, and it's just a single core instance with 2 gigabyte of RAM or so. It's coming to the end, but it's working fine. What else? I've already told, I'm using it for laser cutter. So we have laser cutter for for handmade stuff. So, my wife is kneeling, and every time she has an idea, I save the material and the settings. And I also have, I guess, some pictures. No. I don't I have one, so I can just see the result or not here it is. So this is the data from my newborn. So I can see which, sets I using, which material, was it cutting, was it engraving. Yeah. So I store all this information within directors as well. Yeah. Also some invoicing that comes to our, mailbox, but I don't want to show you this. Also, the the days of sickness of my child are tracked. So I can just have a look and compare this to the school. So I track each day a child is ill and also started to use this as a PDF creation. So it just comes with one click. Currently, it's generating a PDF or it doesn't. It's We will start. No. No. No. It's still referring to a wrong URL. I see. So it's the the old one. That's the problem. It's generating a PDF, but not with the actual data yet. So that's still on my to do list. Anything else? No. Oh, yeah. One one funny thing. I've tracked the state of the TV power of my son because because it's in HomeKit, and there's an automation that sends the information to directors because he have a smart switch on the wallet and thinks he can trick me and turn it on after I turn it off. So every time he turns it on again, I will see it and can show him he turned it on again. So Yeah. Yeah. It's just about communicating to each other. It's also possible to call back to HomeKit, but I guess I have to implement this on some Raspberry Pi solution that is able to receive those requests. Yeah. So basically, it's a problem that I think of and try to find out a solution for this as often, And, yeah, I'm very familiar with with databases in general and with issue issue tracking systems. So I always think of how can I automate things and which information are necessary? And the cool thing is you can just add new fields, remove them, change them, add add a new collection that just combines some information or features. So it's just more iterative. So starting with, the accounts and at the the current, just overall value and do the math manually. Afterwards, I store the transactions, and then flows come in because in the middle, I was just about to develop an own extension that do this, what flows now offers. But as not a full time developer, I'm not so happy with the solution. It was not working, I guess. So with flows, it's super easy. Yeah. So it was just, starting with a simple one and then add some more features, change something, add new fields, remove them. Does it answer the question? Or For me, is it it's easy. Yes. To be honest, it's much easier to have some simple automations compared to Jira, for instance. So I've also had the the the automation to have the, cat litter cleaned by children with an automation in Jira, but it was too complex. It it is you can set it up, but it direct us. It's more easy.",[5936],"d8cca303-6d0c-4675-a89f-ad09c5265774",[],{"year":2380,"number":5683,"id":5684,"episodes":5939,"show":5940},[5686,5687,5688,5662],{"title":4829,"slug":4830},{"id":5687,"slug":5942,"vimeo_id":5943,"description":5944,"tile":5945,"length":128,"resources":12,"people":5946,"episode_number":177,"published":5931,"title":5950,"video_transcript_html":5951,"video_transcript_text":5952,"content":12,"seo":12,"status":19,"episode_people":5953,"recommendations":5955,"season":5956},"multitenancy-at-hybrid-heroes","894205000","We will walk you through the steps to implement segregation of client data using Directus fields and scripts to create the appropriate roles and permissions. We will also showcase a real-world use case and how our client benefit.","fbaa2f85-6d9b-4f92-9b1b-c07cda31e3d5",[5947],{"name":5948,"url":5949},"Mariana Costa","https:\u002F\u002Fmarianacosta.xyz\u002F","Multitenancy in Directus - Berlin Meetup","\u003Cp>Speaker 0: Okay then. So welcome everyone. So I'm Mariana. And today, I'm going to be talking a little bit about, we handle multi tenancy in one of our projects. So basically, like a startup guide for if you need to set up multitenancy in your project, just from the director's point of view, but hopefully it'll help you, like, make the process a little less, painful.\u003C\u002Fp>\u003Cp>Let's let's put it that way. So, yeah, I'm Mariana. I'm a software developer at Ivory Heroes. I've been at the company for, okay, a year 8 months, I think. And this is Joliel who is not here.\u003C\u002Fp>\u003Cp>Unfortunately, he was going to be co presenting with me, but something came up. So it's easier in spirit. So, and without without any further delays, let's just get right get right into it. So basically, the structure of the talk will be like, defining multi tenancy. So what it is, then the advantages, how why you might need it, why it may make sense for your own projects.\u003C\u002Fp>\u003Cp>Then basically, we'll do a small case study of 1 of our own projects where we had to implement multi tenancy. How we approached it, how we got started, and also the downsides of our approach and some other, like, general topics that also came up while we were doing this implementation. So, yeah, let's let's get right into it. So, basically, multi tenancy is an architecture where you have essentially one single instance to serve multiple clients. So let's say you want to set up a CMS to, for schools to set up their own block content.\u003C\u002Fp>\u003Cp>With multitenancy, you can have you can also only one instance of, of directors and serve all of all of the schools you want, while having the data collocated but separated from each other. So each school would only see the information that pertains to their own school. But, like, why would you want to do that? Why not just set up individual instances for for each school that would like to to use your services? So maybe maybe you want to lower infrastructure costs.\u003C\u002Fp>\u003Cp>So in some situations, and now we'll stress the some parts of the sentence because it's it's very dependent on the context. It may be cheaper to just have one single instance, if you don't, like, expect any crazy loads. So, the problem here is that sometimes scaling up just one instance may be more cumbersome than just having several single instances separated and deployed individually. So, yeah, as I said, this is very much it very much depends on on the context. For our case, it makes sense, but it may not make sense for for yours.\u003C\u002Fp>\u003Cp>So another possible benefit is that it can lower the the, entry barrier for your your potential clients. So and this is actually one of the reasons why we also choose this. So let's say we have a client that wants to use your product, but they don't really want to worry about the infrastructure. So they don't want to want to deploy anything. They don't want to run any infrastructure.\u003C\u002Fp>\u003Cp>They just want you to take care take care of all of it. And let's say you also don't really have the resources. We have multiple instances. Multi tenancy can help it can help with this. Like, a good compromise could be to just bear the costs of one single instance that can support multiple, tenants.\u003C\u002Fp>\u003Cp>And so you can also support multiple clients and a lower cost. And finally, this isn't really a reason that we, that resonated with us for our context, but it may resonate with you. It can make it easier to kind of aggregate data. So let's say you want to see which features of your, CMS are being used the most often by the most clients. It's easier when you just have, your data collocated.\u003C\u002Fp>\u003Cp>And, again, we don't really have a lot of experience with it, but it may be something that you are interested in. So let's let's say these these benefits, did convince you to to try out multi tenancy. I thought it would be the best to kinda look at a real life, example, And that's why we're going to use, our project that my team is currently working on as as an inspiration. So direct, which is kind of similar to directors and sometimes gets confusing when we talk about the project, in a company, is a platform that we're developing for Freunde visited Berlin. And it's basically focused on the management of, psychological studies.\u003C\u002Fp>\u003Cp>So it's basically a one stop shop for, coordinating a study, creating content for a study, distributing that content to potential study participants. Like one example of of, possible psychological study could be, like, to analyze the impact of immigration of an immigration process in an asylum seeker, for example. So where does multitenancy come into play in in in our product? So the main goal of our clients of AIUN VISITED was to be able to offer the platform to other partners. So think other universities, without having to ask them to set up their own infrastructure.\u003C\u002Fp>\u003Cp>So I feel would bear the cost of of the infrastructure and would offer the platform to other, to to other clients. But, Fine Infrastructure Debt also didn't really want to, push their own infrastructure cost too much. So the goal was to lower their own infrastructure costs and make it easier for other clients to adopt the platform. And so if, the these these requirements kinda match up nicely with with the advantages that I listed, above. So we kinda went ahead and implemented multi tenancy for this.\u003C\u002Fp>\u003Cp>So before we get started actually looking at the implementation, a quick side note. So today we'll be focusing on multitenancy just from the CMS point of view. However, your project and as our own did, probably has more moving parts. So there are more places where you have to implement multitenancy and there are more places where you have to set up systems that guarantee that multitenancy is insured on all of, on your entire platform. So direct direct itself is composed of several different systems.\u003C\u002Fp>\u003Cp>So we have a CMS to create the content. We have an app that is used by the study, participants. We have an admin panel that's used by the coordinators and then an API to kind of tie it all together. So we had to do a lot of changes in all of these, on all of these parts of the app. So today, we're gonna look at a small subset of, of multitenancy, obviously, focused on directors.\u003C\u002Fp>\u003Cp>And, hopefully, it'll be at least, like, a starting point for you to to kinda go through with with a pullbacking multi tenancy on at least one part of your of your product. So, yeah, with that said, like, let's jump right into it. So when you Google direct us multi tenancy, there are not a lot of results. Like there's not like one major guy that pops up to to, as as being, like, super relevant for your use case. We have a couple of, like, GitHub, issues, a glossary entry in the Directus website.\u003C\u002Fp>\u003Cp>We also have, like, more discussions, more GitHub discussions, the down further. So let's maybe look at the glossary entry for for multi tenancy. So, this entry provides a very similar definition to, the one that we looked at previously, but it adds some director specific details. So, basically, that's the two main ways that you can achieve multi tenancy, indirect. So either through project scoping or through wall scoping.\u003C\u002Fp>\u003Cp>So we, for our product opted for wall scoping because project scoping was just not a viable option for us. We self host, and we weren't really, it wasn't really viable to deploy multiple instances of directors as a project scoping approach does. But maybe if if you happen to use Directed Cloud, then, yeah, this definitely could be an option. It's just a disclaimer that we opted for rollscoping because it's all flows and at the moment, at the time, it's was the most reasonable option. So in this presentation, we're going to I'm going to showcase like a slightly modified approach of the roll scoping.\u003C\u002Fp>\u003Cp>So basically, instead of using, one word per tenant, we're gonna assign tenants to users. We found this approach to be a bit simpler, and it is also one that is, kind of promoted by direct. This is one of in one of their more or less recent videos, that talks about multitenancy. But both options are obviously, good, and it's just the context dependent. There's not much to one over the other.\u003C\u002Fp>\u003Cp>So regardless of which approach you choose, either assigning tenants to roles or to users, the main concept to keep in mind is permissions. So, in in our approach, the tenant based permissions are the core of the logic. So, essentially, it's these permissions that allow us to filter which items a user has access to based on the, on the tenant of that item and that user. So let's maybe to to kind of make it easier to visualize this. Let's take a look at all the data structure.\u003C\u002Fp>\u003Cp>So in in this approach, we have a tenant's collection, as a matter of guess, which allows you to to find the tenant. So in a real life scenario scenario, which is what we do, this creation, the credit permissions for the tenants are limited to the super admin role, and which is something that you also might consider because the tenants are a very, very privileged is a very privileged privileged collection. So after we create the tenants collection, we also need a way to actually assign the tenants to to things, to tenants and to users and other collections. So we create one to many, relations between the whichever collections you want to be, multi tenant and to the users, collection as well. So now we can assign tenants to things and to users, but we don't have data segregation at this point.\u003C\u002Fp>\u003Cp>So a a user from one tenant can see data from another tenant. So, but first, let's maybe look at at permissions. So, users belongs to roles and roles can have many permissions. And these permissions basically dictate how the roles can interact with the collections. So let's say we have a customer role and we, we have an admin role.\u003C\u002Fp>\u003Cp>We may want to prevent the customer role from creating in tenants while a site while allowing the admin role for correct permissions to to the tenant collection. So you may have noticed that in these two diagrams, the permissions, collection doesn't really have, a connection with the tenants. So how do we actually create the tenant based permissions that our system depends on? So to put it all together, we kind of need the last piece of the puzzle, which are rules, permission rules. Rules allow you to make this permission the permissions more specific.\u003C\u002Fp>\u003Cp>So in this case, we're adding a rule that states that the user can only access, an item in a collection if their tenant matches a tenant of that item. So let's say, this is a permission for the customer role to to read from the product collection. So this is basically the gist of how the multi tenancy solution with, with user permissions, goes. So you may be aware that also, besides the data structure, there are there's also the question of actually creating sorry. I have a question about that.\u003C\u002Fp>\u003Cp>Yes. Isn't it pretty tedious Exactly. We're gonna we're gonna we're gonna cover this right in the right in the next slide. So as I like like you said, it's it's very tedious to set up roles and permissions automatically, especially if you have like multiple deployments of, of the application. So and it's Stephenson is also error prone.\u003C\u002Fp>\u003Cp>So, if you forget to add a permission to a credit collection, then you're gonna be using our app and then you're gonna be, like, scratching your head, trying to understand why isn't is this not returning the items correctly? Or when I when I quit because I know that I created a collection. Why are they not showing up? So, you know, it's a very, very prominent. It's very tedious.\u003C\u002Fp>\u003Cp>So because we have a lot of collections and because we have several deployments, like in review apps, in staging and production environments, we kinda set up, automated permissions through, scripts. So we don't use it for this. We use external scripts, but the concept concept is is the same. So using the direct SDK, we fetch all of the collections that should support multi tenancy. So, in this case, we check the collect the collections that have a tenant field.\u003C\u002Fp>\u003Cp>And then we update the director's permission, system table to to include all of the permissions, that we want to support, and ensuring that the permissions field specifies that the rule that we we looked at a few slides ago, is is enforced, which ensures that users can only access, can only do specified actions, either reading or writing or sharing on the items that belong to their own tenant. And in addition to our own collections, you can obviously do this also to system collections. We also do it with files and folders because they, say it should be tenant specific as well. And the the logic is fairly similar. You just have to change the the target of of that permission.\u003C\u002Fp>\u003Cp>And so with this basic data structure and with these, automated, permissions, we can have a simple multitenancy to to start with. And as useful as we found this setup to be, we, this this approach kind of does come with some downsides, and I would highlight 2. So the first one would be complexity. So setting up a multi tenant system is inherently more difficult, especially if you have more than just a CMS. And in the multi tenancies system as is our our own case, we also have to add additional features like our clients, also wanted a multi tenant kind of work for a multi tenancy approach because they wanted customization.\u003C\u002Fp>\u003Cp>They wanted their tenants to be able to customize the UI. They want the to customize the way that the study flows. So it adds it's more complexity to to wrap your head around. It's just overall a are the solution to it to implement. And secondly, because the data is is co located, at least in this approach, it's we pay need to pay even more attention to security, especially if you're handling, very sensitive data like medical data.\u003C\u002Fp>\u003Cp>Like, we're placing a lot of trust in this in this multi tenancy system. So this comes at a cost. We need to implement more systems, more checks to make sure that the data release, segregated and, that we we can ensure our clients that, you know, no tenants can see sensitive information from other tenants. As with everything, basically, multi tenant is yes as trade offs. And there really isn't one clear cut rule that can tell you whether you should or should not implement a multitenancy for us.\u003C\u002Fp>\u003Cp>At the time it makes sense and it still continues to make sense. We're still working on it. We're still integrating on it, and it may or may not make sense for your project. And but, hopefully, this this brief intro will kind of at least, settle some, give you some stronger ground to to to thread when you start in, when you start exploring this topic. And we're almost done.\u003C\u002Fp>\u003Cp>But before before you go, I'd like to circle back on on what I said a few slides ago. So this this brief guide, let's put it that way, is meant as a starting point to multitenancy. So there are a lot of topics which I definitely did not cover today. Some things we have come across in our own product and we have solved them. Others which, we have encountered and the decided that we don't really have the resources or it doesn't really make sense to to tackle them right now.\u003C\u002Fp>\u003Cp>And surely, like many other many other topics we are not even aware of. So I just I gather a few here, like, a few topics of discussions for for examples. So how do you handle uniqueness across tenants, for example? So let's say you have a books collection and in the scheme of that collection you have an ISBN. So the, unique code that is attributed to 2 books, field that is set to unique.\u003C\u002Fp>\u003Cp>And then 2 of your tenants have the same book, and the first tenant tries to add a book to a collection and it's offline. And then the second the second tenant second user in another tenant tries to add a book and throw an arrow stone because the field is supposed to be unique. So how do you handle the situation? So you can maybe set up a composite index, so that the unique constraint applies only to the ISBN tenant pair and not just the ISBN. But as far as I know, this is not a natively supported indirect.\u003C\u002Fp>\u003Cp>So you would have to do some workarounds. You'd have to implement some custom interface to show that. But it is a possibility. Or you can even remove the unique constraint if if you think that it doesn't really compromise the integrity of your data schema, it can be it can be easier to just remove the constraints if you can ensure that the system is still compliant with with your with your architecture. And what about tenancy outside of directors?\u003C\u002Fp>\u003Cp>I did mention before that we have several other systems in our platform. So how how do you sync up the tenants collection with with external services? So we can you can set up scripts that run periodically and sync the necessary information, which is what we do, or you can also make use of direct to Saks, which is also something that we are considering, and something that possibly we will start adopting. And what about permissions? Like, how do you keep permissions up to date?\u003C\u002Fp>\u003Cp>If a new collection is added, it's just as come or correctly. So in our use case, we, the automation for this, for this, that it wasn't really worth the effort, but you can also adopt a similar approach to the tenancy that I mentioned in the point above. And, finally, like I said previously, we are following a user based tenancy as opposed to a world based one. But as I said, both options are are viable. I've linked some discussions, in the appendices of of the, of this presentation that do go further into into this topic if you like, if you're interested.\u003C\u002Fp>\u003Cp>But, yeah, I'm sure there are many, many more topics that we haven't really had the chance to to cover, but hopefully this is a good for the, for the discussion. And I do hope that this presentation kind of provided you with a solid, solid introduction to, to multitenancy and that it can make its implementation a little bit less, less painful. And, yeah, I think I think that's it. Thank you all for listening. I left my email there if you're interested in just asking whatever about the presentation, about the product or whatever you you can think of.\u003C\u002Fp>\u003Cp>And, yeah, if you have any questions, then let me know. If you don't, then that's fine. I'm not, I won't get mad, but yeah. Thank you.\u003C\u002Fp>","Okay then. So welcome everyone. So I'm Mariana. And today, I'm going to be talking a little bit about, we handle multi tenancy in one of our projects. So basically, like a startup guide for if you need to set up multitenancy in your project, just from the director's point of view, but hopefully it'll help you, like, make the process a little less, painful. Let's let's put it that way. So, yeah, I'm Mariana. I'm a software developer at Ivory Heroes. I've been at the company for, okay, a year 8 months, I think. And this is Joliel who is not here. Unfortunately, he was going to be co presenting with me, but something came up. So it's easier in spirit. So, and without without any further delays, let's just get right get right into it. So basically, the structure of the talk will be like, defining multi tenancy. So what it is, then the advantages, how why you might need it, why it may make sense for your own projects. Then basically, we'll do a small case study of 1 of our own projects where we had to implement multi tenancy. How we approached it, how we got started, and also the downsides of our approach and some other, like, general topics that also came up while we were doing this implementation. So, yeah, let's let's get right into it. So, basically, multi tenancy is an architecture where you have essentially one single instance to serve multiple clients. So let's say you want to set up a CMS to, for schools to set up their own block content. With multitenancy, you can have you can also only one instance of, of directors and serve all of all of the schools you want, while having the data collocated but separated from each other. So each school would only see the information that pertains to their own school. But, like, why would you want to do that? Why not just set up individual instances for for each school that would like to to use your services? So maybe maybe you want to lower infrastructure costs. So in some situations, and now we'll stress the some parts of the sentence because it's it's very dependent on the context. It may be cheaper to just have one single instance, if you don't, like, expect any crazy loads. So, the problem here is that sometimes scaling up just one instance may be more cumbersome than just having several single instances separated and deployed individually. So, yeah, as I said, this is very much it very much depends on on the context. For our case, it makes sense, but it may not make sense for for yours. So another possible benefit is that it can lower the the, entry barrier for your your potential clients. So and this is actually one of the reasons why we also choose this. So let's say we have a client that wants to use your product, but they don't really want to worry about the infrastructure. So they don't want to want to deploy anything. They don't want to run any infrastructure. They just want you to take care take care of all of it. And let's say you also don't really have the resources. We have multiple instances. Multi tenancy can help it can help with this. Like, a good compromise could be to just bear the costs of one single instance that can support multiple, tenants. And so you can also support multiple clients and a lower cost. And finally, this isn't really a reason that we, that resonated with us for our context, but it may resonate with you. It can make it easier to kind of aggregate data. So let's say you want to see which features of your, CMS are being used the most often by the most clients. It's easier when you just have, your data collocated. And, again, we don't really have a lot of experience with it, but it may be something that you are interested in. So let's let's say these these benefits, did convince you to to try out multi tenancy. I thought it would be the best to kinda look at a real life, example, And that's why we're going to use, our project that my team is currently working on as as an inspiration. So direct, which is kind of similar to directors and sometimes gets confusing when we talk about the project, in a company, is a platform that we're developing for Freunde visited Berlin. And it's basically focused on the management of, psychological studies. So it's basically a one stop shop for, coordinating a study, creating content for a study, distributing that content to potential study participants. Like one example of of, possible psychological study could be, like, to analyze the impact of immigration of an immigration process in an asylum seeker, for example. So where does multitenancy come into play in in in our product? So the main goal of our clients of AIUN VISITED was to be able to offer the platform to other partners. So think other universities, without having to ask them to set up their own infrastructure. So I feel would bear the cost of of the infrastructure and would offer the platform to other, to to other clients. But, Fine Infrastructure Debt also didn't really want to, push their own infrastructure cost too much. So the goal was to lower their own infrastructure costs and make it easier for other clients to adopt the platform. And so if, the these these requirements kinda match up nicely with with the advantages that I listed, above. So we kinda went ahead and implemented multi tenancy for this. So before we get started actually looking at the implementation, a quick side note. So today we'll be focusing on multitenancy just from the CMS point of view. However, your project and as our own did, probably has more moving parts. So there are more places where you have to implement multitenancy and there are more places where you have to set up systems that guarantee that multitenancy is insured on all of, on your entire platform. So direct direct itself is composed of several different systems. So we have a CMS to create the content. We have an app that is used by the study, participants. We have an admin panel that's used by the coordinators and then an API to kind of tie it all together. So we had to do a lot of changes in all of these, on all of these parts of the app. So today, we're gonna look at a small subset of, of multitenancy, obviously, focused on directors. And, hopefully, it'll be at least, like, a starting point for you to to kinda go through with with a pullbacking multi tenancy on at least one part of your of your product. So, yeah, with that said, like, let's jump right into it. So when you Google direct us multi tenancy, there are not a lot of results. Like there's not like one major guy that pops up to to, as as being, like, super relevant for your use case. We have a couple of, like, GitHub, issues, a glossary entry in the Directus website. We also have, like, more discussions, more GitHub discussions, the down further. So let's maybe look at the glossary entry for for multi tenancy. So, this entry provides a very similar definition to, the one that we looked at previously, but it adds some director specific details. So, basically, that's the two main ways that you can achieve multi tenancy, indirect. So either through project scoping or through wall scoping. So we, for our product opted for wall scoping because project scoping was just not a viable option for us. We self host, and we weren't really, it wasn't really viable to deploy multiple instances of directors as a project scoping approach does. But maybe if if you happen to use Directed Cloud, then, yeah, this definitely could be an option. It's just a disclaimer that we opted for rollscoping because it's all flows and at the moment, at the time, it's was the most reasonable option. So in this presentation, we're going to I'm going to showcase like a slightly modified approach of the roll scoping. So basically, instead of using, one word per tenant, we're gonna assign tenants to users. We found this approach to be a bit simpler, and it is also one that is, kind of promoted by direct. This is one of in one of their more or less recent videos, that talks about multitenancy. But both options are obviously, good, and it's just the context dependent. There's not much to one over the other. So regardless of which approach you choose, either assigning tenants to roles or to users, the main concept to keep in mind is permissions. So, in in our approach, the tenant based permissions are the core of the logic. So, essentially, it's these permissions that allow us to filter which items a user has access to based on the, on the tenant of that item and that user. So let's maybe to to kind of make it easier to visualize this. Let's take a look at all the data structure. So in in this approach, we have a tenant's collection, as a matter of guess, which allows you to to find the tenant. So in a real life scenario scenario, which is what we do, this creation, the credit permissions for the tenants are limited to the super admin role, and which is something that you also might consider because the tenants are a very, very privileged is a very privileged privileged collection. So after we create the tenants collection, we also need a way to actually assign the tenants to to things, to tenants and to users and other collections. So we create one to many, relations between the whichever collections you want to be, multi tenant and to the users, collection as well. So now we can assign tenants to things and to users, but we don't have data segregation at this point. So a a user from one tenant can see data from another tenant. So, but first, let's maybe look at at permissions. So, users belongs to roles and roles can have many permissions. And these permissions basically dictate how the roles can interact with the collections. So let's say we have a customer role and we, we have an admin role. We may want to prevent the customer role from creating in tenants while a site while allowing the admin role for correct permissions to to the tenant collection. So you may have noticed that in these two diagrams, the permissions, collection doesn't really have, a connection with the tenants. So how do we actually create the tenant based permissions that our system depends on? So to put it all together, we kind of need the last piece of the puzzle, which are rules, permission rules. Rules allow you to make this permission the permissions more specific. So in this case, we're adding a rule that states that the user can only access, an item in a collection if their tenant matches a tenant of that item. So let's say, this is a permission for the customer role to to read from the product collection. So this is basically the gist of how the multi tenancy solution with, with user permissions, goes. So you may be aware that also, besides the data structure, there are there's also the question of actually creating sorry. I have a question about that. Yes. Isn't it pretty tedious Exactly. We're gonna we're gonna we're gonna cover this right in the right in the next slide. So as I like like you said, it's it's very tedious to set up roles and permissions automatically, especially if you have like multiple deployments of, of the application. So and it's Stephenson is also error prone. So, if you forget to add a permission to a credit collection, then you're gonna be using our app and then you're gonna be, like, scratching your head, trying to understand why isn't is this not returning the items correctly? Or when I when I quit because I know that I created a collection. Why are they not showing up? So, you know, it's a very, very prominent. It's very tedious. So because we have a lot of collections and because we have several deployments, like in review apps, in staging and production environments, we kinda set up, automated permissions through, scripts. So we don't use it for this. We use external scripts, but the concept concept is is the same. So using the direct SDK, we fetch all of the collections that should support multi tenancy. So, in this case, we check the collect the collections that have a tenant field. And then we update the director's permission, system table to to include all of the permissions, that we want to support, and ensuring that the permissions field specifies that the rule that we we looked at a few slides ago, is is enforced, which ensures that users can only access, can only do specified actions, either reading or writing or sharing on the items that belong to their own tenant. And in addition to our own collections, you can obviously do this also to system collections. We also do it with files and folders because they, say it should be tenant specific as well. And the the logic is fairly similar. You just have to change the the target of of that permission. And so with this basic data structure and with these, automated, permissions, we can have a simple multitenancy to to start with. And as useful as we found this setup to be, we, this this approach kind of does come with some downsides, and I would highlight 2. So the first one would be complexity. So setting up a multi tenant system is inherently more difficult, especially if you have more than just a CMS. And in the multi tenancies system as is our our own case, we also have to add additional features like our clients, also wanted a multi tenant kind of work for a multi tenancy approach because they wanted customization. They wanted their tenants to be able to customize the UI. They want the to customize the way that the study flows. So it adds it's more complexity to to wrap your head around. It's just overall a are the solution to it to implement. And secondly, because the data is is co located, at least in this approach, it's we pay need to pay even more attention to security, especially if you're handling, very sensitive data like medical data. Like, we're placing a lot of trust in this in this multi tenancy system. So this comes at a cost. We need to implement more systems, more checks to make sure that the data release, segregated and, that we we can ensure our clients that, you know, no tenants can see sensitive information from other tenants. As with everything, basically, multi tenant is yes as trade offs. And there really isn't one clear cut rule that can tell you whether you should or should not implement a multitenancy for us. At the time it makes sense and it still continues to make sense. We're still working on it. We're still integrating on it, and it may or may not make sense for your project. And but, hopefully, this this brief intro will kind of at least, settle some, give you some stronger ground to to to thread when you start in, when you start exploring this topic. And we're almost done. But before before you go, I'd like to circle back on on what I said a few slides ago. So this this brief guide, let's put it that way, is meant as a starting point to multitenancy. So there are a lot of topics which I definitely did not cover today. Some things we have come across in our own product and we have solved them. Others which, we have encountered and the decided that we don't really have the resources or it doesn't really make sense to to tackle them right now. And surely, like many other many other topics we are not even aware of. So I just I gather a few here, like, a few topics of discussions for for examples. So how do you handle uniqueness across tenants, for example? So let's say you have a books collection and in the scheme of that collection you have an ISBN. So the, unique code that is attributed to 2 books, field that is set to unique. And then 2 of your tenants have the same book, and the first tenant tries to add a book to a collection and it's offline. And then the second the second tenant second user in another tenant tries to add a book and throw an arrow stone because the field is supposed to be unique. So how do you handle the situation? So you can maybe set up a composite index, so that the unique constraint applies only to the ISBN tenant pair and not just the ISBN. But as far as I know, this is not a natively supported indirect. So you would have to do some workarounds. You'd have to implement some custom interface to show that. But it is a possibility. Or you can even remove the unique constraint if if you think that it doesn't really compromise the integrity of your data schema, it can be it can be easier to just remove the constraints if you can ensure that the system is still compliant with with your with your architecture. And what about tenancy outside of directors? I did mention before that we have several other systems in our platform. So how how do you sync up the tenants collection with with external services? So we can you can set up scripts that run periodically and sync the necessary information, which is what we do, or you can also make use of direct to Saks, which is also something that we are considering, and something that possibly we will start adopting. And what about permissions? Like, how do you keep permissions up to date? If a new collection is added, it's just as come or correctly. So in our use case, we, the automation for this, for this, that it wasn't really worth the effort, but you can also adopt a similar approach to the tenancy that I mentioned in the point above. And, finally, like I said previously, we are following a user based tenancy as opposed to a world based one. But as I said, both options are are viable. I've linked some discussions, in the appendices of of the, of this presentation that do go further into into this topic if you like, if you're interested. But, yeah, I'm sure there are many, many more topics that we haven't really had the chance to to cover, but hopefully this is a good for the, for the discussion. And I do hope that this presentation kind of provided you with a solid, solid introduction to, to multitenancy and that it can make its implementation a little bit less, less painful. And, yeah, I think I think that's it. Thank you all for listening. I left my email there if you're interested in just asking whatever about the presentation, about the product or whatever you you can think of. And, yeah, if you have any questions, then let me know. If you don't, then that's fine. I'm not, I won't get mad, but yeah. Thank you.",[5954],"36bad2c5-6a4d-4f20-99ed-674d6e86821e",[],{"year":2380,"number":5683,"id":5684,"episodes":5957,"show":5958},[5686,5687,5688,5662],{"title":4829,"slug":4830},{"id":5960,"slug":5961,"vimeo_id":5962,"description":5963,"tile":5964,"length":889,"resources":12,"people":5965,"episode_number":177,"published":5967,"title":5968,"video_transcript_html":5969,"video_transcript_text":5970,"content":12,"seo":12,"status":19,"episode_people":5971,"recommendations":5973,"season":5974},"bd6e31b6-5155-4324-8f5d-b1b5521bcb43","realtime-chat-app-websockets","893884117","In this tutorial, you'll learn how to build a simple multi-user chat application powered by Directus Realtime and WebSockets in less than 10 minutes.","da8a6606-b34b-46c7-8193-40cf4c68f542",[5966],{"name":3590,"url":3591},"2023-06-29","Build a Realtime Chat App","\u003Cp>Speaker 0: Hi. Brian here for Directus. In this video tutorial, we are going to build a multi user chat application together in under 10 minutes. So stay tuned. So before we touch any code in this project, you need to have a Directus instance running, whether that's on Directus Cloud or using the self hosted version of Directus.\u003C\u002Fp>\u003Cp>So the next thing we're gonna do is create a new collection. We're just gonna call this messages. For the primary key field, I'm gonna choose the type of generated UUID, and we'll add 2 of the system fields, date created and user created. Now we're going to add one more field to this, a text field for the text of the message. Surprise, surprise.\u003C\u002Fp>\u003Cp>So we'll save that. And with that, our data model for this app is done. Next, we're gonna move into our roles and permissions. We're gonna create a new role for our users of the chat application. And in this case, I'm gonna disable app access because it's not really needed.\u003C\u002Fp>\u003Cp>Now once we've created our role, the first thing we're gonna do is add create and read permissions to the messages collection, so that our users will be able to send messages and receive messages. Pretty simple. The last thing that we're gonna do inside this role is create read access for Directus users collection. So this is a system collection sitting underneath the drop down, and then we will move on to adding a couple of users. So let's just quickly add a couple of users here, Kevin the Great and Matt the Awesome.\u003C\u002Fp>\u003Cp>And with that, we should be ready to dive into some actual code. Let's switch over to our editor. Alright. So as you could probably already see, I've just got some boilerplate set up for us. I've got a form for the login.\u003C\u002Fp>\u003Cp>We just take an email address, a password, and a submit button. Then we have an ordered list for our actual messages. And below that we have a new message submission form, so just the text. And then in our script tag, we have a URL to connect to our WebSocket and just a connection variable that we are initializing. So let's talk about the steps that we're gonna go through here.\u003C\u002Fp>\u003Cp>The first things first, we're gonna establish the connection. Next, we'll authenticate. We'll subscribe to our messages, and then we wanna add new message functionality, and then display those messages as well as historical messages. So that's the plan. Let's dive into action.\u003C\u002Fp>\u003Cp>Alright. So let's start with steps 12, establish a connection and authenticate. So we only want to authenticate and connect whenever we actually log in. So we're gonna add this event listener here on the submit button of the login form, and we're gonna prevent the default action so that we don't get a redirect or reload on submit, and then we are going to actually work with our form. So we're gonna get the email and password from our form elements, And then the next thing, we'll actually open that WebSocket connection.\u003C\u002Fp>\u003Cp>So now we've got everything we need to authenticate. Let's add an event listener whenever that connection is opened to actually send that authentication request. Alright. So let's recap where we're at. We're preventing the default action on the form.\u003C\u002Fp>\u003Cp>We're getting the email and password. We're connecting to the Directus real time via WebSocket, and then we are sending authentication request whenever that connection is opened. So as far as our list, 1 and 2 here, if I can actually stop messing with the mouse, 1 and 2 are done. Great. Let's move on to the next thing.\u003C\u002Fp>\u003Cp>Alright. So our next step is just actually getting the messages functionality into our app. So we're gonna subscribe to those messages. But how do we do that? So the first thing that we're gonna do is add an event listener for those messages.\u003C\u002Fp>\u003Cp>So whenever we receive that message from the, WebSocket connection, we are going to run a function called receive message. Next, let's go in and add that function. The function, we'll just call it receive message. It is going to receive the message. Love the jokes here, but for now let's just console.\u003C\u002Fp>\u003Cp>Log this and we'll test this out in the browser. We'll just save this as real time chat and fire this up inside the browser. I still struggle with Arc a little bit, so bear with me. And I can immediately see that I've got a little gotcha here because we're requiring email and password to log in, but I forgot to create emails for Matt and Kevin. So let me do that really quick, and then we'll test this out.\u003C\u002Fp>\u003Cp>Alright. So let's log in, and I'm also gonna open up our console just so we can see those messages as they are logged. So we'll do kevin@example, real secure password here of password. And once I submit, we can see the message being logged here. It is the type auth, the status is okay.\u003C\u002Fp>\u003Cp>So that means we've got some connections actually working here. So let's move on and subscribe to that messages collection. Alright. Because the data that's being sent back and forth is JSON, inside our receive message function, I'm gonna go in and actually parse that JSON message that we're receiving. My next step is gonna be subscribing to the messages collection, but we only wanna do that if we're actually authenticated.\u003C\u002Fp>\u003Cp>The big question is, how do we do that? Well, let's look at it and let's break it down. So if the message type that we receive is off and the status is okay, then we are going to send a subscription request which has the type of subscribe, a collection of messages, and then we have a query for the different fields and sort order that we want. Let's add one more condition to catch whenever that subscription is initialized, and we'll just log this output to the console, subscription started. So I'll log in as Kevin one more time.\u003C\u002Fp>\u003Cp>We'll click submit, and we should see subscription started in the console. And boom, right there it is, subscription started, plain as day. So step number 3, subscribe to messages. Done. Piece of cake.\u003C\u002Fp>\u003Cp>Let's move on. So we're going to add an event listener to our new message form. Whenever that form is submitted, we are going to prevent the default action. We're gonna get the text from that form, and then we are going to create that message by using the connection dot send function, and we will make sure that we use type of items, collection messages, and action create. Our data is simply gonna be the text that we're passing.\u003C\u002Fp>\u003Cp>So as I log in, we can see the subscription has started. Let's hit test message and click submit. And the big finale here, nothing. Right? But if I go inside Directus, I can see that that message is being created.\u003C\u002Fp>\u003Cp>It's just up to us to update the front end whenever that message is actually submitted to Directus. So back inside our code, step number 4 is done. We've added new messages. Next, we are going to display those messages. So let's add a new function, addMessageToList, That just creates a new list item and appends it to our ordered list that we have inside our HTML.\u003C\u002Fp>\u003Cp>Tools like React and Vue make this a little easier to update on the front end, but, hey, vanilla JavaScript is also great. Alright. So now inside our receive message function, let's add a new condition if the type is subscription and the event is create. We're going to add that message to our list. That way, it actually shows up on the front end.\u003C\u002Fp>\u003Cp>And then inside our previous condition, we want to display historical messages whenever that subscription is initialized. So instead of just simply console logging subscription started, we are actually going to do a quick little for loop of all the previous messages and add those to the list as well. So that's all we need. We are going to save, log in, and let's have a chat conversation. So, if I go into the network tab, I can see this WebSocket connection and I'll just send a simple message.\u003C\u002Fp>\u003Cp>Boom. I can see our messages updated there in real time. Let's add another one. Great. These are all being logged.\u003C\u002Fp>\u003Cp>I could see all the messages being sent via WebSockets, but this is a multi user chat application. So let's spin up 2 copies of this that are different users. So I'll log in as Matt and let's have a quick little back and forth with Kevin. So I could see those historical messages. Now I'm going to fire one back at Kevin.\u003C\u002Fp>\u003Cp>Hey, Kevin. How are you? And now I could see that those messages are being updated in both browsers, different clients, different users logged in totally real time. And we have reached the end of this tutorial on creating a multi user chat application with Directus Real Time. I hope this serves as, inspiration for you, a helpful tutorial on how to use WebSockets in Directus Real Time and share what you're building with our team.\u003C\u002Fp>\u003Cp>Hop inside our Discord community. We can't wait to see what you build with this. And don't forget to subscribe to our YouTube channel for more goodness on Directus Real Time and other great features coming your way. Thanks for watching.\u003C\u002Fp>","Hi. Brian here for Directus. In this video tutorial, we are going to build a multi user chat application together in under 10 minutes. So stay tuned. So before we touch any code in this project, you need to have a Directus instance running, whether that's on Directus Cloud or using the self hosted version of Directus. So the next thing we're gonna do is create a new collection. We're just gonna call this messages. For the primary key field, I'm gonna choose the type of generated UUID, and we'll add 2 of the system fields, date created and user created. Now we're going to add one more field to this, a text field for the text of the message. Surprise, surprise. So we'll save that. And with that, our data model for this app is done. Next, we're gonna move into our roles and permissions. We're gonna create a new role for our users of the chat application. And in this case, I'm gonna disable app access because it's not really needed. Now once we've created our role, the first thing we're gonna do is add create and read permissions to the messages collection, so that our users will be able to send messages and receive messages. Pretty simple. The last thing that we're gonna do inside this role is create read access for Directus users collection. So this is a system collection sitting underneath the drop down, and then we will move on to adding a couple of users. So let's just quickly add a couple of users here, Kevin the Great and Matt the Awesome. And with that, we should be ready to dive into some actual code. Let's switch over to our editor. Alright. So as you could probably already see, I've just got some boilerplate set up for us. I've got a form for the login. We just take an email address, a password, and a submit button. Then we have an ordered list for our actual messages. And below that we have a new message submission form, so just the text. And then in our script tag, we have a URL to connect to our WebSocket and just a connection variable that we are initializing. So let's talk about the steps that we're gonna go through here. The first things first, we're gonna establish the connection. Next, we'll authenticate. We'll subscribe to our messages, and then we wanna add new message functionality, and then display those messages as well as historical messages. So that's the plan. Let's dive into action. Alright. So let's start with steps 12, establish a connection and authenticate. So we only want to authenticate and connect whenever we actually log in. So we're gonna add this event listener here on the submit button of the login form, and we're gonna prevent the default action so that we don't get a redirect or reload on submit, and then we are going to actually work with our form. So we're gonna get the email and password from our form elements, And then the next thing, we'll actually open that WebSocket connection. So now we've got everything we need to authenticate. Let's add an event listener whenever that connection is opened to actually send that authentication request. Alright. So let's recap where we're at. We're preventing the default action on the form. We're getting the email and password. We're connecting to the Directus real time via WebSocket, and then we are sending authentication request whenever that connection is opened. So as far as our list, 1 and 2 here, if I can actually stop messing with the mouse, 1 and 2 are done. Great. Let's move on to the next thing. Alright. So our next step is just actually getting the messages functionality into our app. So we're gonna subscribe to those messages. But how do we do that? So the first thing that we're gonna do is add an event listener for those messages. So whenever we receive that message from the, WebSocket connection, we are going to run a function called receive message. Next, let's go in and add that function. The function, we'll just call it receive message. It is going to receive the message. Love the jokes here, but for now let's just console. Log this and we'll test this out in the browser. We'll just save this as real time chat and fire this up inside the browser. I still struggle with Arc a little bit, so bear with me. And I can immediately see that I've got a little gotcha here because we're requiring email and password to log in, but I forgot to create emails for Matt and Kevin. So let me do that really quick, and then we'll test this out. Alright. So let's log in, and I'm also gonna open up our console just so we can see those messages as they are logged. So we'll do kevin@example, real secure password here of password. And once I submit, we can see the message being logged here. It is the type auth, the status is okay. So that means we've got some connections actually working here. So let's move on and subscribe to that messages collection. Alright. Because the data that's being sent back and forth is JSON, inside our receive message function, I'm gonna go in and actually parse that JSON message that we're receiving. My next step is gonna be subscribing to the messages collection, but we only wanna do that if we're actually authenticated. The big question is, how do we do that? Well, let's look at it and let's break it down. So if the message type that we receive is off and the status is okay, then we are going to send a subscription request which has the type of subscribe, a collection of messages, and then we have a query for the different fields and sort order that we want. Let's add one more condition to catch whenever that subscription is initialized, and we'll just log this output to the console, subscription started. So I'll log in as Kevin one more time. We'll click submit, and we should see subscription started in the console. And boom, right there it is, subscription started, plain as day. So step number 3, subscribe to messages. Done. Piece of cake. Let's move on. So we're going to add an event listener to our new message form. Whenever that form is submitted, we are going to prevent the default action. We're gonna get the text from that form, and then we are going to create that message by using the connection dot send function, and we will make sure that we use type of items, collection messages, and action create. Our data is simply gonna be the text that we're passing. So as I log in, we can see the subscription has started. Let's hit test message and click submit. And the big finale here, nothing. Right? But if I go inside Directus, I can see that that message is being created. It's just up to us to update the front end whenever that message is actually submitted to Directus. So back inside our code, step number 4 is done. We've added new messages. Next, we are going to display those messages. So let's add a new function, addMessageToList, That just creates a new list item and appends it to our ordered list that we have inside our HTML. Tools like React and Vue make this a little easier to update on the front end, but, hey, vanilla JavaScript is also great. Alright. So now inside our receive message function, let's add a new condition if the type is subscription and the event is create. We're going to add that message to our list. That way, it actually shows up on the front end. And then inside our previous condition, we want to display historical messages whenever that subscription is initialized. So instead of just simply console logging subscription started, we are actually going to do a quick little for loop of all the previous messages and add those to the list as well. So that's all we need. We are going to save, log in, and let's have a chat conversation. So, if I go into the network tab, I can see this WebSocket connection and I'll just send a simple message. Boom. I can see our messages updated there in real time. Let's add another one. Great. These are all being logged. I could see all the messages being sent via WebSockets, but this is a multi user chat application. So let's spin up 2 copies of this that are different users. So I'll log in as Matt and let's have a quick little back and forth with Kevin. So I could see those historical messages. Now I'm going to fire one back at Kevin. Hey, Kevin. How are you? And now I could see that those messages are being updated in both browsers, different clients, different users logged in totally real time. And we have reached the end of this tutorial on creating a multi user chat application with Directus Real Time. I hope this serves as, inspiration for you, a helpful tutorial on how to use WebSockets in Directus Real Time and share what you're building with our team. Hop inside our Discord community. We can't wait to see what you build with this. And don't forget to subscribe to our YouTube channel for more goodness on Directus Real Time and other great features coming your way. Thanks for watching.",[5972],"9c897eca-729b-4b8b-b0ee-1cf7807fdec2",[],{"year":2380,"number":13,"id":5975,"episodes":5976,"show":5978},"48c4b2f4-6e41-47b1-9d60-e05bc2cd6339",[5977,5960],"6e517a27-efa6-4b76-8fb4-9f6fc43f199d",{"title":5979,"slug":5980},"Make It Realtime","make-it-realtime",{"id":2386,"slug":5982,"vimeo_id":5983,"description":5984,"tile":5985,"length":159,"resources":12,"people":5986,"episode_number":159,"published":5988,"title":5989,"video_transcript_html":5990,"video_transcript_text":5991,"content":12,"seo":12,"status":19,"episode_people":5992,"recommendations":5994,"season":5995},"10-4","894077942","Directus 10.4 has landed with a new beta SDK and some small but important breaking changes. In this video, Colton gives you the roundup of what's notable in our latest release. ","9cee7938-50c2-40be-abb4-43c7d7ae020c",[5987],{"name":4446,"url":4447},"2023-06-28","What's New In Directus Version 10.4","\u003Cp>Speaker 0: Hi, my name is Colton and I work with customers at Directus. Today, I'm going to tell you about what's new in version 10.4, and do stick with me because after some exciting new stuff there's some small but important breaking changes to take note of. A sneak peek of our new SDK. Our JavaScript SDK is the recommended way to work with Directus and we've been building a brand new one from the ground up and today releasing it in beta. Our new SDK is written in typescript and provides full proper type safety, something many of you have been asking for.\u003C\u002Fp>\u003Cp>We've also split up the SDK to various functions you can import separately. By composing a custom client with just the bits you need, you can decrease your overall build size by tree shaking the ones you don't use. Finally, this new SDK will support directus real time taking away a lot of the work needed to send and receive real time data in your applications. New and improved documentation. We've been putting a lot of energy into our doc to make learning directus both easier and more comprehensive.\u003C\u002Fp>\u003Cp>You can find a whole host of guides on using Directus features like live preview in real time, guides on building projects with your favorite frameworks such as Next. Js or Nuxt, and improved guides for self hosting Directus. If you build a project with Directus and need to hand it off, we've moved a bunch of our docs to a new zone called user guides. These are less developer focused and should mean both you and your end users can find what they need. There's a lot more to come, especially around extensions, but this represents a great milestone in our journey, and we hope you learn lots.\u003C\u002Fp>\u003Cp>We have the best community. We want to also thank our community for their contributions towards this release. Thanks to Phil for fixing a bug on our table component which allowed multiple radio buttons to be selected when it should only have allowed 1 thanks to Ashish for fixing a bug which would sometimes show incorrect data based on an archive filter Thanks to Phil for improving how we fetch permissions and settings to avoid an infinite loop, and thanks to the team at Gitstart for their contributions towards improving our filter support. Finally, some small breaking changes that may require your attention. We always try and keep changes that require action to a minimum, but there are a handful this release that should only need a few lines of code to adapt for.\u003C\u002Fp>\u003Cp>Moving extensions to our new error handling package. This change is due to improvements to how we handle errors, especially in hook and endpoint extensions. We only exposed a small number of error types to extension builders and if you threw any other errors, they'd be wrapped in unhelpful generic internal server error to the end user. We have moved all of our exception handling to our new direct as errors package, meaning you can throw more standardized errors that we will surface appropriately and namespace to your extension. This means you'll need to potentially update your error handling to use our library.\u003C\u002Fp>\u003Cp>We have dropped memcached support. Directus 10.4 drops support for memcached as a shared cache. You'll need to move over to Redis, which is generally better suited for this purpose, the change to Redis environment variables. We are consolidating and simplifying environment variables Redis, so if you use it, you'll have to update the names of some variables and hopefully be able to remove commonly duplicated ones. Directus 10.4 is now available on npm and Docker Hub and will be available on Directus Cloud next week.\u003C\u002Fp>\u003Cp>Thanks for joining me, and see you in the next release.\u003C\u002Fp>","Hi, my name is Colton and I work with customers at Directus. Today, I'm going to tell you about what's new in version 10.4, and do stick with me because after some exciting new stuff there's some small but important breaking changes to take note of. A sneak peek of our new SDK. Our JavaScript SDK is the recommended way to work with Directus and we've been building a brand new one from the ground up and today releasing it in beta. Our new SDK is written in typescript and provides full proper type safety, something many of you have been asking for. We've also split up the SDK to various functions you can import separately. By composing a custom client with just the bits you need, you can decrease your overall build size by tree shaking the ones you don't use. Finally, this new SDK will support directus real time taking away a lot of the work needed to send and receive real time data in your applications. New and improved documentation. We've been putting a lot of energy into our doc to make learning directus both easier and more comprehensive. You can find a whole host of guides on using Directus features like live preview in real time, guides on building projects with your favorite frameworks such as Next. Js or Nuxt, and improved guides for self hosting Directus. If you build a project with Directus and need to hand it off, we've moved a bunch of our docs to a new zone called user guides. These are less developer focused and should mean both you and your end users can find what they need. There's a lot more to come, especially around extensions, but this represents a great milestone in our journey, and we hope you learn lots. We have the best community. We want to also thank our community for their contributions towards this release. Thanks to Phil for fixing a bug on our table component which allowed multiple radio buttons to be selected when it should only have allowed 1 thanks to Ashish for fixing a bug which would sometimes show incorrect data based on an archive filter Thanks to Phil for improving how we fetch permissions and settings to avoid an infinite loop, and thanks to the team at Gitstart for their contributions towards improving our filter support. Finally, some small breaking changes that may require your attention. We always try and keep changes that require action to a minimum, but there are a handful this release that should only need a few lines of code to adapt for. Moving extensions to our new error handling package. This change is due to improvements to how we handle errors, especially in hook and endpoint extensions. We only exposed a small number of error types to extension builders and if you threw any other errors, they'd be wrapped in unhelpful generic internal server error to the end user. We have moved all of our exception handling to our new direct as errors package, meaning you can throw more standardized errors that we will surface appropriately and namespace to your extension. This means you'll need to potentially update your error handling to use our library. We have dropped memcached support. Directus 10.4 drops support for memcached as a shared cache. You'll need to move over to Redis, which is generally better suited for this purpose, the change to Redis environment variables. We are consolidating and simplifying environment variables Redis, so if you use it, you'll have to update the names of some variables and hopefully be able to remove commonly duplicated ones. Directus 10.4 is now available on npm and Docker Hub and will be available on Directus Cloud next week. Thanks for joining me, and see you in the next release.",[5993],"0a78c742-1299-412f-a720-c5557fdf5b5a",[],{"year":2380,"number":588,"id":2381,"episodes":5996,"show":5997},[2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394,2366],{"title":2396,"slug":2397},{"id":5977,"slug":5999,"vimeo_id":6000,"description":6001,"tile":6002,"length":264,"resources":12,"people":6003,"episode_number":13,"published":6005,"title":6006,"video_transcript_html":6007,"video_transcript_text":6008,"content":12,"seo":12,"status":19,"episode_people":6009,"recommendations":6011,"season":6012},"getting-started-with-websockets","893884088","In this short tutorial, you'll learn how to quickly add real time data to your application or project using WebSockets and Directus Realtime. Follow along with Bryant as he covers the three easy steps to get started with WebSockets.","7a9fc694-e419-4c8a-836f-40c4530af13d",[6004],{"name":3590,"url":3591},"2023-06-13","Getting Started With WebSockets","\u003Cp>Speaker 0: Hi, Bryant here for Directus. In this quick tutorial, I'm gonna show you just how easy it is to add real time data to your project with Directus Real Time using WebSockets. With Directus, adding real time data to your project takes just 3 easy steps. Number 1, connect. Number 2, authenticate.\u003C\u002Fp>\u003Cp>And number 3, subscribe. Let's work through each of these 3 steps together in a sample project. Okay. So in our sample project, you can see I've already spec'd out a couple different variables. Our URL for our WebSocket connection, our Directus instance, we've got an access token for authentication, And we have a collection for messages.\u003C\u002Fp>\u003Cp>You'll also need a Directus project up and running to make this work. So if we open this and go to the data model settings, you can see I've got a single messages collection with a user field for our user, a date created timestamp, and a text area field called text for the actual text of the message. Step number 1 is as simple as opening a connection using the WebSocket protocol. So we'll define a new connection and then we're also going to drop in a couple of event handlers just so we can actually get some feedback through the console whenever we are opening a connection or whenever we receive a message through this connection. Step 2 is authentication.\u003C\u002Fp>\u003Cp>So whenever you open a new connection, you'll have to send an authentication request before the connection times out. To do that, we are going to send a new message within the open event listener. So connection dot send, we're gonna stringify the JSON body with a type of auth. We're passing that access token that we already previously generated. The last step is actually subscribing to updates from a collection.\u003C\u002Fp>\u003Cp>So here, we're just gonna drop in a new subscribe method that subscribes to the messages collection. So if we open up our file now, we can test this out. We can see that the auth message is being received, that the connection is open and authenticated, but the next step is to actually subscribe to the messages collection. So just gonna call that right here within the console. We can see that subscription is working.\u003C\u002Fp>\u003Cp>And next, let's just actually test this by creating new messages inside Direct Us. Whenever I save this, you can see the messages show up inside our console with all of the data that we created. No refresh. No latency. Just smooth real time data inside your project in 3 minutes or less.\u003C\u002Fp>\u003Cp>Stay tuned for more content on direct us real time, and we can't wait to see what you build. Thanks for watching.\u003C\u002Fp>","Hi, Bryant here for Directus. In this quick tutorial, I'm gonna show you just how easy it is to add real time data to your project with Directus Real Time using WebSockets. With Directus, adding real time data to your project takes just 3 easy steps. Number 1, connect. Number 2, authenticate. And number 3, subscribe. Let's work through each of these 3 steps together in a sample project. Okay. So in our sample project, you can see I've already spec'd out a couple different variables. Our URL for our WebSocket connection, our Directus instance, we've got an access token for authentication, And we have a collection for messages. You'll also need a Directus project up and running to make this work. So if we open this and go to the data model settings, you can see I've got a single messages collection with a user field for our user, a date created timestamp, and a text area field called text for the actual text of the message. Step number 1 is as simple as opening a connection using the WebSocket protocol. So we'll define a new connection and then we're also going to drop in a couple of event handlers just so we can actually get some feedback through the console whenever we are opening a connection or whenever we receive a message through this connection. Step 2 is authentication. So whenever you open a new connection, you'll have to send an authentication request before the connection times out. To do that, we are going to send a new message within the open event listener. So connection dot send, we're gonna stringify the JSON body with a type of auth. We're passing that access token that we already previously generated. The last step is actually subscribing to updates from a collection. So here, we're just gonna drop in a new subscribe method that subscribes to the messages collection. So if we open up our file now, we can test this out. We can see that the auth message is being received, that the connection is open and authenticated, but the next step is to actually subscribe to the messages collection. So just gonna call that right here within the console. We can see that subscription is working. And next, let's just actually test this by creating new messages inside Direct Us. Whenever I save this, you can see the messages show up inside our console with all of the data that we created. No refresh. No latency. Just smooth real time data inside your project in 3 minutes or less. Stay tuned for more content on direct us real time, and we can't wait to see what you build. Thanks for watching.",[6010],"d1d4e6c4-030e-4c94-90de-9d0be8b2af37",[],{"year":2380,"number":13,"id":5975,"episodes":6013,"show":6014},[5977,5960],{"title":5979,"slug":5980},{"id":2385,"slug":6016,"vimeo_id":6017,"description":6018,"tile":6019,"length":264,"resources":12,"people":6020,"episode_number":264,"published":6005,"title":6024,"video_transcript_html":6025,"video_transcript_text":6026,"content":12,"seo":12,"status":19,"episode_people":6027,"recommendations":6029,"season":6030},"10-3","894077969","Directus 10.3 has landed with a huge new feature - WebSockets support! In this video, Jan gives you the roundup of what's notable in our latest release. ","b9aa92a1-8e32-4192-a480-f15511ba9843",[6021],{"name":6022,"url":6023},"Jan Arends","https:\u002F\u002Fdirectus.io\u002Fteam\u002Fjan-arends","What's New In Directus Version 10.3","\u003Cp>Speaker 0: Hi. My name is Jan. I'm a software engineer at Directus, and I'm going to tell you about what's new in version 10.3. WebSocket support in Directus. Directus 10.3 includes new functionality to enable real time features in your by subscribing to changes in the collection.\u003C\u002Fp>\u003Cp>What makes it even cooler is that our WebSockets implementation isn't just read only, but also allows you to send create, update, and delete operations over the same WebSocket connection. Now you can build things like live chats, data dashboards, and notification systems, and be confident that the data you have is up to date. Because our implementation works by emitting events that happen in your database, you don't have to worry about data persistence. What's in your director's project is always the one true source, as well as a standard WebSocket interface. We also allow you to get real time data using GraphQL subscriptions.\u003C\u002Fp>\u003Cp>We have a whole heap of guides in our docs to help you get started with real time and hope you find this exciting as we do. Internal package type docs for extension builders. The directives you use is made of many smaller packages we maintain. And if you build extensions, there's a good chance that you have tried to figure out how they work. To help, we have published auto generated documentation for these packages to take away guesswork and help you understand how you can leverage them in your extensions.\u003C\u002Fp>\u003Cp>There's still some key parts in directives that aren't split into separate packages, but watch the space. Albanian language support in directors data studio. All of the text in directors data studio supports multiple languages. As of Director's 10.3, we now have Albanian as a new language coming in hard and with full support. Translations are provided by the community, and you can find out more in the contributing sections of our docs.\u003C\u002Fp>\u003Cp>We also wanna thank our community for their contributions towards this release. Speaking about Albanian support, thanks to Nino for taking this language from 0 to 100%. Thanks to Augustine for fixing a small bug with some of our operators, such as null and empty. Thanks to Craig, add, and enter for fixing typos in our docs. Directus 10.3 is now available on NPM and DocHub and will be available in Directus cloud within the next few days.\u003C\u002Fp>\u003Cp>Thanks for joining me, and see you in the next release.\u003C\u002Fp>","Hi. My name is Jan. I'm a software engineer at Directus, and I'm going to tell you about what's new in version 10.3. WebSocket support in Directus. Directus 10.3 includes new functionality to enable real time features in your by subscribing to changes in the collection. What makes it even cooler is that our WebSockets implementation isn't just read only, but also allows you to send create, update, and delete operations over the same WebSocket connection. Now you can build things like live chats, data dashboards, and notification systems, and be confident that the data you have is up to date. Because our implementation works by emitting events that happen in your database, you don't have to worry about data persistence. What's in your director's project is always the one true source, as well as a standard WebSocket interface. We also allow you to get real time data using GraphQL subscriptions. We have a whole heap of guides in our docs to help you get started with real time and hope you find this exciting as we do. Internal package type docs for extension builders. The directives you use is made of many smaller packages we maintain. And if you build extensions, there's a good chance that you have tried to figure out how they work. To help, we have published auto generated documentation for these packages to take away guesswork and help you understand how you can leverage them in your extensions. There's still some key parts in directives that aren't split into separate packages, but watch the space. Albanian language support in directors data studio. All of the text in directors data studio supports multiple languages. As of Director's 10.3, we now have Albanian as a new language coming in hard and with full support. Translations are provided by the community, and you can find out more in the contributing sections of our docs. We also wanna thank our community for their contributions towards this release. Speaking about Albanian support, thanks to Nino for taking this language from 0 to 100%. Thanks to Augustine for fixing a small bug with some of our operators, such as null and empty. Thanks to Craig, add, and enter for fixing typos in our docs. Directus 10.3 is now available on NPM and DocHub and will be available in Directus cloud within the next few days. Thanks for joining me, and see you in the next release.",[6028],"81e914dd-fa92-434c-95a8-fa2f3ae52642",[],{"year":2380,"number":588,"id":2381,"episodes":6031,"show":6032},[2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394,2366],{"title":2396,"slug":2397},{"id":5106,"slug":6034,"vimeo_id":6035,"description":6036,"tile":6037,"length":1022,"resources":6038,"people":6042,"episode_number":264,"published":6044,"title":6045,"video_transcript_html":6046,"video_transcript_text":6047,"content":12,"seo":12,"status":19,"episode_people":6048,"recommendations":6050,"season":6051},"talent-recruitment-platform","894101800","In this episode of I Made This, Erwin and Uwe walk Bryant through one of their recent Directus projects - a engineering job listing and talent recruitment site for their client - Rockstar Recruiting.\n","17109185-a938-45b3-a552-a167c18a56fc",[6039],{"name":6040,"url":6041},"Heinrichs & Heinrichs","https:\u002F\u002Fheinrichsundheinrichs.de\u002F",[6043],{"name":3590,"url":3591},"2023-06-01","Building a Talent Platform with H&H","\u003Cp>Speaker 0: Hi, guys. Brian here for Directus. Welcome back to another episode of I Made This. Today, I've got very special guests with me, my friends from Germany, Heinrichs and Heinrichs. I've got Erwin and Yueh.\u003C\u002Fp>\u003Cp>Guys, welcome to I Made This. I this is gonna be probably one of my favorite episodes. How are you guys?\u003C\u002Fp>\u003Cp>Speaker 1: Thank you so much for us. We are fine. Thank you.\u003C\u002Fp>\u003Cp>Speaker 0: Excellent. Excellent. I I I would just preface this to the audience and say that these guys build amazing things with Directus and Nuxt and all kinds of other technologies, and I'm really excited to showcase this project that they are going to demonstrate for us. But before we dive into that, guys, give me the story of Heinrichs and Heinrichs and yourselves as well. Yes.\u003C\u002Fp>\u003Cp>Sure.\u003C\u002Fp>\u003Cp>Speaker 1: Our story is, I think, not not a normal story for front end developer, but a very normal story for designers. So we both did study communication design, and that's a very graphic design related course. Yeah. You can compare it with graphic design, to be honest. And and we did do during our studies a lot with with user interface designs and things like that, and we did love design all the time.\u003C\u002Fp>\u003Cp>And we tried to, bring new ideas for the web to life. And at the beginning, we just were the dinos, and so we had freaking ideas, but, had had the opportunity to realize it. And, yeah. So so we thought about, at at first, we we started with, cooperating with programmers, and they had to bring it to life. And after after a a bit of time, it gets more and more frustrated because we hear heard things like, no.\u003C\u002Fp>\u003Cp>It's not possible in this way. So we learn about our by ourself, and that's the reason why we are designers and front end developers right now.\u003C\u002Fp>\u003Cp>Speaker 0: That's great. So, hey, you guys were working with programmers before, and they just like, they would take your designs and say, hey. This is not gonna work out. And you're like, no. It is.\u003C\u002Fp>\u003Cp>It would Yeah. It should be. If we have to do it ourselves, we'll get it done. Yeah. Right.\u003C\u002Fp>\u003Cp>I love that. I I I love it. So when did you guys start your agency?\u003C\u002Fp>\u003Cp>Speaker 1: We we don't have a agency right now. So we're both working as freelancers, but\u003C\u002Fp>\u003Cp>Speaker 0: Oh, okay.\u003C\u002Fp>\u003Cp>Speaker 1: We are collaborating the Okay. Of the time. So all all the projects we're doing together. And, we started for, for two and a half years ago, I think. Yeah.\u003C\u002Fp>\u003Cp>With full time just doing our stuff, it's, like, two and a half years.\u003C\u002Fp>\u003Cp>Speaker 0: So, like, how many in that 2 years, like, how many projects have you guys done?\u003C\u002Fp>\u003Cp>Speaker 1: We did a lot of projects, to be honest. So, for the most of the time, we're working as, like, ghost designers or ghost front end developers because we, signed a lot of NDAs and worked for global players and, did very niche things where we, design unique, user interfaces, and then we, more more, started to do things for for our own clients. So not just participating in in a team, and I think for our own projects, we did in the last two and a half year. Didn't count it, to be honest. And so there must be more than 20.\u003C\u002Fp>\u003Cp>Speaker 0: That's awesome. That's awesome. And I like, before the the show, I was browsing around on your website, and I love the the interactions that you guys have programmed into it. Thank you so much. It's like do you do you get to like, do clients come to you specifically for that style of, like, hey.\u003C\u002Fp>\u003Cp>We want something very interactive, or is that just like you guys flexing your own creative muscles for your own site?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It's a bit flexing, to be honest, because at the moment, we're just, we just get, our clients not because of our huge, our huge, digital performance. And the digital performance, it's more like, we don't we don't have any we don't have an Instagram account. We, we barely happen on LinkedIn. We I think our website is currently, not, so the robots 16 blocks right now Google and things like that.\u003C\u002Fp>\u003Cp>Speaker 0: So it\u003C\u002Fp>\u003Cp>Speaker 1: barely happen out there in the in the world wide web, but we're trying to to, yeah, to to reach out more and more. And we also have a plan doing a block and things like that to get more so that we can show more the Heinrichs and Heinrichs approach and not be, we're just ghost designers, and you can place us into a team, and then we will figure something out.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. I I mean, I I love it. Like, I think your approach is awesome. And, talk to me about, like, the the tooling that you guys are using.\u003C\u002Fp>\u003Cp>Like, first and foremost, like, what are you what are you designing it? Like, how are you how do you come up with this stuff? Does it like, does everything start in, like, a sketchpad, or you, like, go straight to Figma? Like, talk to me through that.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It depends, to be honest. So sometimes we we, go the the very classic way and start with a mood board and think about so if if it's a if it's a topic where we have to deep dive, then we have to then we start googling a lot. So which are which which what does our client do? So what what does he want how does he want to present himself in the in the Internet or with this idea of his, of his brand?\u003C\u002Fp>\u003Cp>And then we start, for example, with the mood board, and then we go straight into Figma or Adobe XD and start there, with some ideas. They can roughly be just some UI components, more more like a rough scratch, not not just so we don't do a pixel perfect design at the beginning. It's more of the direction process where we, where we try to generate look and feel and, to to bring a brand to life. Yeah. And after that, then we go for Yeah.\u003C\u002Fp>\u003Cp>For a more precise approach. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Cool. Cool. What are what's your your tech stack that you guys are using for, like, most of your clients?\u003C\u002Fp>\u003Cp>Speaker 1: Our tech stack is for us. So Nuxt 3 is one of our most used front end frameworks. Yeah. We we also did some things with pew 3, but when it comes to a website for our clients, then Next 3 is our way to go. And for the back end\u003C\u002Fp>\u003Cp>Speaker 0: the go to.\u003C\u002Fp>\u003Cp>Speaker 2: Use\u003C\u002Fp>\u003Cp>Speaker 0: I love Nuxtree.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. We we we do also. And for the back end, now our main way to go is directors. So we all we so whenever there is a a chance to place directive, then we're going to use it. I yeah.\u003C\u002Fp>\u003Cp>Yeah. We\u003C\u002Fp>\u003Cp>Speaker 0: would. And, of course, that's why we're here. But but yeah. Yeah. Like, what did you guys like, what tools did you use for back ends before Directus?\u003C\u002Fp>\u003Cp>Or Like our well, let me preface that. Like, are most of the sites that you're doing, like, very content heavy? Or, like, are you doing, like, apps for clients as well? Or We did both.\u003C\u002Fp>\u003Cp>Speaker 1: So we we started with\u003C\u002Fp>\u003Cp>Speaker 0: Okay.\u003C\u002Fp>\u003Cp>Speaker 1: So when when we when we talk about the the typical client who just needs a website, then we came from, WordPress. So, like, almost everybody who started with just the front end things, and there there we started in the in the very early stages. We just, bought the theme, And then we customized it, changed the colors, had to look out, had to look of which which plugins could we install and things like that. And after that, we started doing our own themes, then we just back introduced Elementor, things like that. Yeah.\u003C\u002Fp>\u003Cp>But yeah.\u003C\u002Fp>\u003Cp>Speaker 2: Competitive. Yes.\u003C\u002Fp>\u003Cp>Speaker 1: Everybody everybody knows WordPress. Yeah. And we don't like PHP, to be honest.\u003C\u002Fp>\u003Cp>Speaker 0: It's a it's a solid platform. Yeah. It is. I'm right there with you. Like, WordPress is, it's a solid platform.\u003C\u002Fp>\u003Cp>I I my experience has been much the same, though. So I'll I'll echo exactly what you guys said.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It become becomes very difficult when you start installing plug ins and another plugin, and then you have to to update it, and then you have to figure out if it's still compatible with the new WordPress version and things like that. And then your whole page keeps breaking. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: That's that's great when you log in and see there's, like, 75 update notifications for the plugins. Right? You know it's gonna be a good day. It's so true. So I do did you guys jump directly from WordPress into Directus, or was there, like, a\u003C\u002Fp>\u003Cp>Speaker 1: No. No. No.\u003C\u002Fp>\u003Cp>Speaker 0: We some other tool that you try.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. We we did a lot of traditional CMS systems. So we also worked with Joomla. I don't know if this says to anyone out there. We also used KontaO, and that's a very small traditional CMS system.\u003C\u002Fp>\u003Cp>Then we also used, for ecommerce systems, the WooCommerce plugin. That is WordPress as well, but we also did something with Magento. And, yeah, we we we tried a lot, to be honest. And then there comes the point where we heard about, the headless CMS system at first, and then we also tried things like the Tamek. We also tried, Strapi Strapi and\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Pimcore.\u003C\u002Fp>\u003Cp>Speaker 1: Pimcore, and yeah. And then Of course. Also.\u003C\u002Fp>\u003Cp>Speaker 0: Gosh. So I know you guys have been through, like, dozens of different tools.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. We tried everything,\u003C\u002Fp>\u003Cp>Speaker 0: to be honest. How did you guys originally come across Directus, or how did you find Directus?\u003C\u002Fp>\u003Cp>Speaker 1: I was more or less a Google search.\u003C\u002Fp>\u003Cp>Speaker 2: Yes. I'm\u003C\u002Fp>\u003Cp>Speaker 1: sorry. So we we just Googled, what is the best headless team f you could use, and then you get, yeah, then you get directors, you get, you get\u003C\u002Fp>\u003Cp>Speaker 2: Story block, I think, is\u003C\u002Fp>\u003Cp>Speaker 1: Story block contentful, things like that. And Yeah. That that we we did try every day.\u003C\u002Fp>\u003Cp>Speaker 0: Marketing guys are definitely gonna love that. And and\u003C\u002Fp>\u003Cp>Speaker 2: we also love the the open source approach.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's awesome.\u003C\u002Fp>\u003Cp>Speaker 2: Because, where we we did, we didn't have much experience with headless CMS systems, so, the open source approach was was the best one for us to to give it a a try. So we started out with a Strapi, I think, and then tried directors. And directors, us had much more flexibility in building these relations between between the data we we want.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I would say, we, as a designer, directors was usable, was very, very was was was was very easy to use in comparison with Strapi. Because in Strapi, you had to the server has time to reload if you fetch anything in the data object, things like that. So it was more or less in the in the daily task. Directus was way easier to use.\u003C\u002Fp>\u003Cp>Speaker 2: You change something. You have a real time API, and, this combined with the with the hot module reload, and Nuxt 3 was perfectly efficient workflow for us.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Awesome. Yeah. So I like, my one of my other questions to you guys, obviously, you've got clients that are using Directus. We kinda internally, we consider it like a a Swiss army knife.\u003C\u002Fp>\u003Cp>Like, you could use it for an app back end. You can use it for a headless CMS. There's, like, too many different use cases. So I'm curious, like, how do you guys present Directus to your clients when you're, either, like, in, like, the initial stages of the project or, yeah, later when you're actually, like, building the CMS and and, you know, showing it to them?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It depends. Sometimes, we we have, clients who does have a a little bit of technical expertise. And when we drop, when we just, make some backward dropping like Hetto CMS, they are, yeah, go for it. We love Hetto CMS.\u003C\u002Fp>\u003Cp>But but and sometimes we have clients who came or who does have WordPress sites at the moment, for example, and they are like, Yeah. Okay. It could be faster, but, I think it's already fast. And then you have to deep dive into what what are the, what what are the the opportunities while using a headless CMS and why it's so much better than using a WordPress, for example, in this scenario where you have to, maybe in our example, what we're going to show, that we have, that we have 2 fraud end sites and one director's back end. This is so much more practicable than than a WordPress back end.\u003C\u002Fp>\u003Cp>And and then sometimes where where we do a back end for for a web application, for example, then, there's no other possibility than using Swiss Army knife, as you said, as directors, for example.\u003C\u002Fp>\u003Cp>Speaker 0: I see.\u003C\u002Fp>\u003Cp>Speaker 1: Or you even could use it for the digitalization of portfolio. That's also things we've done with, product information management systems. But, as we deep dive in in directors, we learned that this also could be totally possible doing with directors, and that's also a thing.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Like, the spoken like a true, champion, I guess. Like, that is he like, you guys are very good. Very good. I also love Let's get into it.\u003C\u002Fp>\u003Cp>Alright. Like, are you guys ready to dive in? Oh, go ahead. I'm sorry, Yue. Sorry.\u003C\u002Fp>\u003Cp>I also love the the the\u003C\u002Fp>\u003Cp>Speaker 2: the the fact that we can, like, outsource the the content task to the to the to a specific person or to a specific team and say, go for it.\u003C\u002Fp>\u003Cp>Speaker 0: That's true.\u003C\u002Fp>\u003Cp>Speaker 2: Fill it out, play with it, create some content, and we can focus on building, the front end or the actual use case for the date, the the other ones are creating.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's so true.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So you can hey. Like, being able to, like, decouple the content and the the front end the business logic. Useful in that scenario.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's true.\u003C\u002Fp>\u003Cp>Speaker 0: Great. Cool. So do you guys wanna dive in and and show this project? I can remember, like, seeing it when we had a a quick call before we scheduled the episode, and I was blown away. And I I think the audience is too.\u003C\u002Fp>\u003Cp>But\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Sure. I'm going to share my screen.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Alright. So while we're sorting out the details there, Yueh, why don't you kind of give me the backstory of the project?\u003C\u002Fp>\u003Cp>Speaker 1: So the backstory was, we had so it it all started, I think, 7 or 8 years ago. I was still a student, when I got the connection to, to Rockstar Recruiting who just, who just, raised their who who just build their company, and, they asked me back then for a logo. And that's still the logo we can see over here. And we did a a small so I I did a small brand design back then. And now after, yeah, 7 years later, they reached out back and said, maybe you could help us, doing a new homepage, a new website, and that's what we did.\u003C\u002Fp>\u003Cp>And, yeah, that's what you can see here right now. That's awesome.\u003C\u002Fp>\u003Cp>Speaker 0: So 8 years ago, you did the website or the logo for them, and then they like, 8 years later, they're like, hey. This guy is awesome. Let's come back and get the website done.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It's yeah. That's how it happens.\u003C\u002Fp>\u003Cp>Speaker 0: So they are a so they're, basically, a recruiting firm for developers and engineering talent. Is that that's what I gather from the website.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's that's exactly what they are. So they are a recruiting agency who, are looking for developers and data engineers and things like that. And, the first thing we did here was, we asked them, how do the users of your website reach to your website right now? And they said, yeah.\u003C\u002Fp>\u003Cp>The most of them are not, some some Google search hits. They are more like they go to a lot of showcases and things like that, and they're and they're Mhmm. From there, they go to the website. So we thought about when do most of the people who go to this website already know who Rockstar is? Why don't we start with a huge call to action where you can reach out to the to the point, what you're going to do right here?\u003C\u002Fp>\u003Cp>So if you're if you're a company and want to hire a tech talent or, if you're a candidate, meaning a developer or a data engineer or something like that, you could also go right to the to the candidate page. So we just made the animation where you can switch to left and right for, the for the candidates and also for the companies.\u003C\u002Fp>\u003Cp>Speaker 0: Nice. Yeah. Nice. And I I love the, like, the black and white aesthetic and and just yeah. It, like, it it feels very much, like, you're specifically targeting developers in it.\u003C\u002Fp>\u003Cp>Are those, are those keyboard shortcuts that I see down there as well?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. The the these are keyboard shortcuts inspired by the terminal, meaning you have, for example, here, you can see the control and h button, then you will get the would get to home or you could press control and e to go to the imprint and, for the events or the recruiter or everything like that. And you could can hit it and go straightly to the recruiter page, for example, like I did here. Or, you also could, if you're not, technical, more, more a company, for example, who want who's who's searching for technical developers, then you also could, can also just press the button. So the idea is to have a a user experience for both, for the developers or the tech experience people, but also it should be, able to navigate if you don't have much experience with with the terminal in this scenario.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. And to keep it here, we we used a lot of, look and feel, you know, from from from the one point zero time. So, like, these ASCII borders, and this typing animations and everything. It's feels like like like a bit a long time ago.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's so true. And, when we beautiful.\u003C\u002Fp>\u003Cp>Speaker 0: How long did you guys spend in, like, the design phase for this?\u003C\u002Fp>\u003Cp>Speaker 1: I think the design\u003C\u002Fp>\u003Cp>Speaker 0: phase Just curious.\u003C\u002Fp>\u003Cp>Speaker 1: In in 8 days, something like that.\u003C\u002Fp>\u003Cp>Speaker 2: I think the the whole phase with with finding the right idea and deciding which one to go, it went about, like, a month.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. When you go for the whole process with with thinking about the boot and yeah. Then it did take something like a month. But the the the the designing process where we just made the UI components where the idea was set, then, I think we we wait. We went forward pretty, pretty fast.\u003C\u002Fp>\u003Cp>Yeah. And and here we have things like, for example, the ASCII border. In the screen design process, it was pretty simple, and we thought about, yeah, the this this would be something like you would do it in the web 1.0 version. And, we didn't think one second about how it could be used, in a responsive scenario because in the one point zero world, there was no responsive. So we had we had problems that that we never thought about because when you when you want to, make this ASCII border responsive, it gets, it it gets it was quite complicated, to be honest.\u003C\u002Fp>\u003Cp>So we made the SVG grid.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I bet. So yeah.\u003C\u002Fp>\u003Cp>Speaker 1: We we just started with just using some CSS and changing the content. Then we learned, okay. It's not possible. Then we used the Busia then we tried to boost JavaScript. Also pretty hard, and then we came to the SVG grid.\u003C\u002Fp>\u003Cp>Speaker 0: It there you go. Yeah. That's you guys definitely made it challenging on yourself. Like, the end result, it it looks great.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Thank you. Yeah. But yeah. The these these are some small things which you didn't think when you're designing that that you never would think about.\u003C\u002Fp>\u003Cp>Yeah, it could can could be a problem doing a border like this. So\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. These are the things, which which, which which come back with a comment, like, this is not possible.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's so true. And I can totally understand that.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I can totally understand that\u003C\u002Fp>\u003Cp>Speaker 1: the the this would be something which can come at yeah. Possible. Yeah. But we made it possible with with a lot of effort, but, yeah, it's it's there. And, yeah, we also did hear the Odenowski animations.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Those are great. Can you guys or does your client, Rockstar, mind if you show us around, like, the the direct us instance that's powering this?\u003C\u002Fp>\u003Cp>Speaker 1: No. Not at all. We asked before, and it's not a problem at all. So we can deep dive into the directest. So and as we told before, they have 2 front end pages.\u003C\u002Fp>\u003Cp>So they have 2 websites, And Groxo, the one we just saw, and text executive can also show that. That that's the same thing for, for executive roles in the development process. And, this is\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Gotcha.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. The the this is right now, that's not an own firm. It's just by Rockstar Recruiting right now. They will change that, I think. But for the moment, they share a lot of global components.\u003C\u002Fp>\u003Cp>For example, the people. There are a lot of people who are working for Rockstar right now and also for the executive, That's the point where directors made so much sense because we could, do some global components and some components for Rockstar or for TechSec in this scenario.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Like, the locations, the blog post, people, and so on.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. The blog post, for example, goes for both. People goes for both. And also the locations because they, decided not to translate their content. They decided to, just show the different different locations with different content.\u003C\u002Fp>\u003Cp>So you can choose between Berlin and Zurich, and there you have different people for, for the team, and you have have some some more Berlin related infos and things like that. So if you we go for the whole Got it. Here you here you can choose Cool. Between Berlin and Zurich, for example. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Nice. So this is using the Directus translations feature, but not necessarily for translations. It looks like it's all in English.\u003C\u002Fp>\u003Cp>Speaker 1: That's true. All is all is in English, but they, we just use the translation feature as a location feature, and it works pretty good.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That's great. So it looks like I see over in the, like, the nav bar, you've got your globals, and then you've got, like, your different stacks of rock star versus tech executive. And, like, how like, your what do your clients think of of this? Like, was this, like, an iterative process when you built, like, the back end of Directus with them, or you just said, like, hey.\u003C\u002Fp>\u003Cp>We've gone through and mapped all this, and then just, like, hey. Go take a look and tell us what you thought.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's that's one of the coolest thing in that. Coolest things in directors because we we do draft with, how we want to organize our data or how we want to be more specific when we have a UI component like, this one at the beginning, how we want to, to make it possible to change these these words which are typed over here. And then there are things which just make sense. For example, here, you can add the text for it, and then it will just, be changed in the in the front end.\u003C\u002Fp>\u003Cp>And it's, you can extend it with other, with other developer variants, how we called it, or you can erase one just with the tech feature.\u003C\u002Fp>\u003Cp>Speaker 0: But Gotcha.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And and\u003C\u002Fp>\u003Cp>Speaker 0: And you guys have it looks like every bit is dynamic on here. Like, the\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Almost.\u003C\u002Fp>\u003Cp>Speaker 2: That's so true.\u003C\u002Fp>\u003Cp>Speaker 0: Almost. Yeah. Yeah. That's great.\u003C\u002Fp>\u003Cp>Speaker 1: And and yeah. Because we we, as designers, we love to give the users a good experience on the front end. So on the website, when they are entering it, we we love a good user experience when they know how to navigate, when they know how to scroll, when they know where all the, most important informations are. But, with Directus, we had the opportunity to also make a good user experience for the back end, and that's the thing we really love because, we have the possibility to say, okay. We can we can, make almost every all yeah.\u003C\u002Fp>\u003Cp>Almost, everything dynamic, and then they have the possibility to change some things. But, in this scenario, it's almost not not only almost, it's impossible to break the design, because, all all things are as limitless as they they will always look good. It doesn't matter how much they enter here. And that these are the things when you for example, when our clients had Elementor in WordPress or things like that, they they just started entering a headline, which was, I don't know, 100 and 20 words or something like that. And the whole design breaks, and everything looks awkward.\u003C\u002Fp>\u003Cp>And, yeah, with directors, we have\u003C\u002Fp>\u003Cp>Speaker 0: I've I've been there a bunch of times.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And we have the possibility to limit everything that's that's almost impossible to break the design. And that's a huge thing we love about.\u003C\u002Fp>\u003Cp>Speaker 0: What are, like, 2 or 3 of your, like, your favorite pieces that you guys have implemented for this?\u003C\u002Fp>\u003Cp>Speaker 2: I think the the contact form for tech executive was quite a quite a thing.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's true. This one's one of our most loved features. It's a contact form for the tech executive page on, tech executive. And there you have the differentiation.\u003C\u002Fp>\u003Cp>So how do you want to start? You can choose between I'm a candidate or I represent a company. And if you choose 1, then you get, to the to the type to to the form wizard where you can enter your name and, your last name, then your email, and, your phone number. Okay.\u003C\u002Fp>\u003Cp>Speaker 2: It checks it's a real phone number. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: And, all this is completely dynamic, and has has is completely editable. So you have the question groups. You can see over here with data, then the professional track record, technical skills, additional informations, and you can see all these things for the candidate form here. So you have the personal data, and there you have the questions we just go through. So first name and, last name, email, phone, things like that.\u003C\u002Fp>\u003Cp>And you always have the possibility to create a new question, and there you can choose between a question type. So we have a question with answers or a question with input or a question with answer autofill. So if you go for the answer, for example, I will show the the front.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. This this looks really amazing. I I can't imagine how many hours you guys have spent just on this one component.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It was quite hard. But I I think it's very cool because now it's it's a pretty fast wizard for very complex and for a very complex, for a very complex topic because entering all data in from a CV, for example, the technical knowledge in this scenario is a question with answers autofill in short over here. And this is a component where we made, a question with answer autofill. The question is, what's your technical knowledge?\u003C\u002Fp>\u003Cp>Then enter your program image you're familiar with and write your experience from 1 to 6. And then you get all the autofill words. These are the, most used programming languages. And then you have the rating enabled, and you get, you can enter a a thumb a hint for each star. So it's a Novitz beginner intermediate advanced professional.\u003C\u002Fp>\u003Cp>Wow. This is And when\u003C\u002Fp>\u003Cp>Speaker 0: mind blowing, guys.\u003C\u002Fp>\u003Cp>Speaker 1: And when you, for example, add JavaScript, you can choose, you're a beginner or a new leads. And this this all comes from directors. And you have the possibility, for example, to do this with technical knowledge, but you could also use this component for, for languages. So are you a fluent speaker of, English, Spanish, or whatever? And then you also could rate it, and you could also, change the the the, the states here with intermediate bonds and things like that.\u003C\u002Fp>\u003Cp>And you could also, also, fill in all the autofills, like we did here. This is\u003C\u002Fp>\u003Cp>Speaker 0: Wow.\u003C\u002Fp>\u003Cp>Speaker 1: Pretty cool thing where we first used the edit draw value because when we when we we we got we were provided the list with, I don't know how many there. I think something like 250 programming languages. That's And then and then we yeah. That's that's that's true. And then we want to add it.\u003C\u002Fp>\u003Cp>And the first thing we tried was, like, okay. Yavas, and then we did some commas, but it didn't work because when you enter a comma that makes sense, then it just enters the this, this, this tag. But placing all 250 tags with with just copy paste them, it would be a lot of work. And then we just found out, ah, you you have the possibility to edit the to edit the raw value. And then we just, take all of them, export it as a JSON, and place copy paste it in here, and voila.\u003C\u002Fp>\u003Cp>There they are. Yeah. That's pretty cool that that you also want.\u003C\u002Fp>\u003Cp>Speaker 0: Is that something that you teach your clients as well? Like, the edit raw value? Do your clients use that? Or just kind of you guys\u003C\u002Fp>\u003Cp>Speaker 1: For for this specific use, we didn't have to because it's more like a initial fill. Because after after reporting all the 250 tags, the the only things which can happen in the future is that there will be 1 or 2 more, and you just have to enter them in here. So you you you will never have the the the possibility the use case again where you have to import 250, auto fill words because yeah. Which programming language? I don't know.\u003C\u002Fp>\u003Cp>They they just added, I think, OpenAI or something like that, which is pretty new. But, it's not like there are 3 or 4 program languages a month.\u003C\u002Fp>\u003Cp>Speaker 0: Right. Right. What's, I'm I'm curious. Like, what do the relationships look like just for, like, this one, like, question and answer, like, form component?\u003C\u002Fp>\u003Cp>Speaker 2: Which in the data mode?\u003C\u002Fp>\u003Cp>Speaker 1: Uh-huh. Yeah. We we have the question groups with the name, description, and questions. Here, I see many to many relation question. And Look\u003C\u002Fp>\u003Cp>Speaker 0: for the questions. Okay.\u003C\u002Fp>\u003Cp>Speaker 1: We have the question. No. No. That's the group Where are we? Here the quest.\u003C\u002Fp>\u003Cp>Ah, yeah. Okay. So it's it's a credit it's a huge it's a huge question, data model. But it's it's still very easy to use because we made we would did work a lot of conditions in this scenario. So this is what I oops.\u003C\u002Fp>\u003Cp>This is what I thought, would which I have shown here. When you go for a new question, we have so many conditions that the first thing you would see is the question type. And after filling that, you will get the, question, question description, author words, and then you have also the condition for, do you want a rating for the answers? And if you enable that, you also, could, can change the, the rating description.\u003C\u002Fp>\u003Cp>Speaker 0: So So it's very much yeah. It's a it's a lot of information, but, like, with the conditional fields inside Directus, you don't overwhelm the end user at all. It's very clean. Yeah. Yeah.\u003C\u002Fp>\u003Cp>I love it, man. Thank you. What like, are there any other, like, favorite little features that you guys have have built into this thing?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think one of the the things we already have shown was, the the thing was the developer variants over here. And, one thing we also done is additional pages, And this is where we didn't use, right now, the many to any relation where you just can, place some components in. But, they had the idea that they want for SEO landing pages for for, different keywords which which they are going to pay for for the, Google Ads. And, that that's when we introduce the the component additional pages, and this is, just the ID in the next, and they can add it over here.\u003C\u002Fp>\u003Cp>So they have the page name contracting, for example, then they got a huge where they can implement everything. They also have a script field because they are, they have a good technical expertise. So and they ask for it and we just let them do that. But, yeah. Normally normally, this would be a much too too much for a customer with no technical expertise.\u003C\u002Fp>\u003Cp>Right. Yeah. And Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. It's really easy to blow up the site with those script tags. That's so true.\u003C\u002Fp>\u003Cp>Speaker 1: But in this scenario, it works, and they have the possibility to enter a page name, and then they are live, the pages. So they you can reach them by entering the slash. And if you want to, if you want to, if you want to place them in the menu, you also have the possibility to go into the menu field, and there you can add the menu and also have the possibility to add the letter for the quick access, for example.\u003C\u002Fp>\u003Cp>Speaker 2: So the menu we showed. I mean,\u003C\u002Fp>\u003Cp>Speaker 0: like, you guys have literally thought of everything with this.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. It it was very fun because, we we we it was the first time we we we recognized, how many possibilities there are with, not just making it dynamic, and also to make something like conditions and to limit things and to give, things like soft quotas in the in the blog posts for the descriptions, for example. And, yeah, these are a lot of things which which just made so much fun. That's the reason because why we, yeah, why we try to to And not everything dynamic.\u003C\u002Fp>\u003Cp>Speaker 2: And also have the the whole control over the the the map. This was also pretty pretty impressive where each page had the type description in which field, which the the the client can use if if he if he wants to. And this Mhmm. Was was pretty neat and pretty pretty, pretty good because he he has the the whole control over over the SEO thing and, over his Google Ads campaigns. This was also, like, a game changer for us.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's true. The metadata field was Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: And it looks like that is using the the translations feature as well.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's true.\u003C\u002Fp>\u003Cp>Speaker 0: So you can have different metadata for the different locations.\u003C\u002Fp>\u003Cp>Speaker 1: Nice. And we also make\u003C\u002Fp>\u003Cp>Speaker 0: And, is it, like, are both of these sites are built on Nuxt on the front end?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Both both are Nuxt front end pages.\u003C\u002Fp>\u003Cp>Speaker 2: Gotcha. And,\u003C\u002Fp>\u003Cp>Speaker 0: hey, like, like, obviously, like, you guys are you you mentioned you had a lot of you experience, but, you know, what were your like, as you were building this, like, what would you say are, like, your favorite bits or or, like, why why would you reach for Nuxt versus just, like, regular Vue or, you know, one of the other I think there's several other frameworks on Vue as well.\u003C\u002Fp>\u003Cp>Speaker 2: I think Nuxt works best for us because it we test the different render modes, for for example, the hybrid rendering and the the SSG thing, and and the new, we we use I think it was the first time we were using, like, the server side rendering because before we to. We also we we we just did a project with aesthetic Yeah. Just aesthetic aesthetic size, aesthetic stack.\u003C\u002Fp>\u003Cp>Speaker 1: That that was two times, but, yeah, we we also did the the the Nuxt generate and, reached out their static sites. But, yeah, the server side rendering thing is way too cool, to be honest.\u003C\u002Fp>\u003Cp>Speaker 2: For me, I like the the file based routing because in in in the classical view environment, it's, it's it's it's it's in the ass to to configure the route and to say the these are the children of this route and and to register all the components. And if you want to start a a website with Nuxt, you just hit the CLI, go for it, and, directly start, developing for I think it's it's the perfect way to go. It it it and it keeps the boundaries for for for for get for making progress very low.\u003C\u002Fp>\u003Cp>Speaker 0: Love it. Love it. What, it'll, like, did you guys have any trouble integrating Directus with Nuxt?\u003C\u002Fp>\u003Cp>Speaker 1: Not at all. We we did we did see, there there was a example online already, I think, with with the block and next, thing, and that's that's something we set up, and then we just get from there. We we are we had everything we we needed, to be honest. So it was very easy.\u003C\u002Fp>\u003Cp>Speaker 2: Yes. The beginning I saw, I think it was your guitar repo, Brian, the the one with the nice director's actually\u003C\u002Fp>\u003Cp>Speaker 1: True. True. It was his.\u003C\u002Fp>\u003Cp>Speaker 2: We built a little wrapper around because, we had to to implement the, our localized functionality. So we had a little wrapper around and around the wrapper, where we check, okay, which local is present and which local should be used. So,\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Great, guys. So I I do wanna be respectful of your time. I know we're I think we've gone a little over what we had planned, but this is, it's so interesting to me to, like, see how you guys have constructed this and put it all together. You know, let's talk about, like, the the future.\u003C\u002Fp>\u003Cp>Like, what's, what's the future look like for you guys? I know that's pretty vague, but, like, what are you working on? Like, what do you wanna where do you wanna go with this? Like, what else do you wanna do? One\u003C\u002Fp>\u003Cp>Speaker 1: thing we we are going to do for the page we just have shown, I think quite funny because I've seen you you just released, I don't know, 2 weeks ago or something like that, a video with, ChetchyPT and directors. And Yeah. That's the thing we we, did also in the same time, to be honest. And we That's great. We we love it because the idea is to you still have a a a long journey while filling out the whole form for a candidate for, because you have to do, you have to enter your name, your email, your telephone number, and also some some text, like, what are experiences in your technical industries and things like that.\u003C\u002Fp>\u003Cp>And our idea is to, just drop down your CV, and then we will pass all the data set it to chat gpt, and chat gpt will sort all the data into the fields we have in our form. Just, we'll prefill the whole form, and in the best case scenario, you just have to press next next next next and send.\u003C\u002Fp>\u003Cp>Speaker 0: Wow. That would be amazing. That would be a great dude. Like, we're calling that new series the direct to top line, but that would be that would be a great episode as well. True.\u003C\u002Fp>\u003Cp>Oh, and what about the the future for you guys? Like, do you have a a bunch more projects in the works aside from this one? Or\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. We we we do have some daily tasks, but, for us as as a Calabrio, we, our next step would be to we we're the moment refactoring our own home page. So our Heinrichs on Heinrichs.ke. And, we we just set up a directors for this site also, and we're trying to to, reach out to the world with more, with more public relation. So we we're just looking forward to, to, release a blog and to do some things on LinkedIn to have the possibility to get more cool projects like Rockstar, for example, and not just to be in our daily task and as a ghost designer and ghost developer.\u003C\u002Fp>\u003Cp>Speaker 2: Yes. And also, the website is getting a new a new look and feel, a new, a new design update, a new technical update. We're moving to moving into next 3.\u003C\u002Fp>\u003Cp>Speaker 1: So at the moment, Fuse. Right?\u003C\u002Fp>\u003Cp>Speaker 2: Yes. And we're also doing trying trying a new thing with directors, where we, we are rendering blocks. And maybe we can show\u003C\u002Fp>\u003Cp>Speaker 1: many to any\u003C\u002Fp>\u003Cp>Speaker 2: where we where we are where we're trying to like like a home page. The home page contains different blocks. And in the front end, we will have, like like a like a render skeleton where Mhmm. The the API call determines which component gets rendered. So we have, like, like, a front end block and a back end block, which will be which can be used, like, in in in in many ways or for many projects.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. The the main idea is to have,\u003C\u002Fp>\u003Cp>Speaker 2: Like like reusable components over and over and over. Like like like a like a like an own component framework.\u003C\u002Fp>\u003Cp>Speaker 1: Small library where we just make, can make some, front of UI components and directors and reuse in our Nuxt framework, to to, yeah, to just get things passed down If you have the reusable components\u003C\u002Fp>\u003Cp>Speaker 0: that you\u003C\u002Fp>\u003Cp>Speaker 1: use all the time I've\u003C\u002Fp>\u003Cp>Speaker 0: got some to share with you guys on that front then. Yeah. Yeah. I I I've got a project that is in the works, teasing the audience a little bit, but I'll share it with you guys for sure. I think it'll be helpful for you on that side.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. We can.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Guys, so if anybody wants to learn more about you guys and, like, look at some of the projects that you've done or or even work with you guys, like, how how's the best way to go about contacting you?\u003C\u002Fp>\u003Cp>Speaker 1: You? Reach us our our, to our website. So it's it's a very complicated URL. Many for the English speakers because it's Heinrichs. De, but, we just bought a new, URL, Heinrichs.cool.\u003C\u002Fp>\u003Cp>And then you cool.\u003C\u002Fp>\u003Cp>Speaker 0: Very good. That's a little easier.\u003C\u002Fp>\u003Cp>Speaker 1: That's very easy. There you can reach us via email every time.\u003C\u002Fp>\u003Cp>Speaker 2: Yes. I think when this episode will go live, and the new date will also be live. This is this is not me calling you out.\u003C\u002Fp>\u003Cp>Speaker 0: Don't put yourself on a timeline. We're gonna I think we're gonna turn this one around pretty quickly. So, yeah, I I I I look forward to seeing the the new site. I could tell you all the the guys on our team played around with your existing site for, like, 20 minutes, and they were like, oh, this is so cool. So I can't wait to see what the new one looks like.\u003C\u002Fp>\u003Cp>Guys, been a pleasure. I I really appreciate you joining us, and I I can't thank you enough. And I I look forward to seeing, like, the next project that you guys come out with.\u003C\u002Fp>\u003Cp>Speaker 1: Thank you so much for having us.\u003C\u002Fp>\u003Cp>Speaker 0: Alright, guys. Thank you. Thanks.\u003C\u002Fp>","Hi, guys. Brian here for Directus. Welcome back to another episode of I Made This. Today, I've got very special guests with me, my friends from Germany, Heinrichs and Heinrichs. I've got Erwin and Yueh. Guys, welcome to I Made This. I this is gonna be probably one of my favorite episodes. How are you guys? Thank you so much for us. We are fine. Thank you. Excellent. Excellent. I I I would just preface this to the audience and say that these guys build amazing things with Directus and Nuxt and all kinds of other technologies, and I'm really excited to showcase this project that they are going to demonstrate for us. But before we dive into that, guys, give me the story of Heinrichs and Heinrichs and yourselves as well. Yes. Sure. Our story is, I think, not not a normal story for front end developer, but a very normal story for designers. So we both did study communication design, and that's a very graphic design related course. Yeah. You can compare it with graphic design, to be honest. And and we did do during our studies a lot with with user interface designs and things like that, and we did love design all the time. And we tried to, bring new ideas for the web to life. And at the beginning, we just were the dinos, and so we had freaking ideas, but, had had the opportunity to realize it. And, yeah. So so we thought about, at at first, we we started with, cooperating with programmers, and they had to bring it to life. And after after a a bit of time, it gets more and more frustrated because we hear heard things like, no. It's not possible in this way. So we learn about our by ourself, and that's the reason why we are designers and front end developers right now. That's great. So, hey, you guys were working with programmers before, and they just like, they would take your designs and say, hey. This is not gonna work out. And you're like, no. It is. It would Yeah. It should be. If we have to do it ourselves, we'll get it done. Yeah. Right. I love that. I I I love it. So when did you guys start your agency? We we don't have a agency right now. So we're both working as freelancers, but Oh, okay. We are collaborating the Okay. Of the time. So all all the projects we're doing together. And, we started for, for two and a half years ago, I think. Yeah. With full time just doing our stuff, it's, like, two and a half years. So, like, how many in that 2 years, like, how many projects have you guys done? We did a lot of projects, to be honest. So, for the most of the time, we're working as, like, ghost designers or ghost front end developers because we, signed a lot of NDAs and worked for global players and, did very niche things where we, design unique, user interfaces, and then we, more more, started to do things for for our own clients. So not just participating in in a team, and I think for our own projects, we did in the last two and a half year. Didn't count it, to be honest. And so there must be more than 20. That's awesome. That's awesome. And I like, before the the show, I was browsing around on your website, and I love the the interactions that you guys have programmed into it. Thank you so much. It's like do you do you get to like, do clients come to you specifically for that style of, like, hey. We want something very interactive, or is that just like you guys flexing your own creative muscles for your own site? Yeah. It's a bit flexing, to be honest, because at the moment, we're just, we just get, our clients not because of our huge, our huge, digital performance. And the digital performance, it's more like, we don't we don't have any we don't have an Instagram account. We, we barely happen on LinkedIn. We I think our website is currently, not, so the robots 16 blocks right now Google and things like that. So it barely happen out there in the in the world wide web, but we're trying to to, yeah, to to reach out more and more. And we also have a plan doing a block and things like that to get more so that we can show more the Heinrichs and Heinrichs approach and not be, we're just ghost designers, and you can place us into a team, and then we will figure something out. Yeah. Yeah. I I mean, I I love it. Like, I think your approach is awesome. And, talk to me about, like, the the tooling that you guys are using. Like, first and foremost, like, what are you what are you designing it? Like, how are you how do you come up with this stuff? Does it like, does everything start in, like, a sketchpad, or you, like, go straight to Figma? Like, talk to me through that. Yeah. It depends, to be honest. So sometimes we we, go the the very classic way and start with a mood board and think about so if if it's a if it's a topic where we have to deep dive, then we have to then we start googling a lot. So which are which which what does our client do? So what what does he want how does he want to present himself in the in the Internet or with this idea of his, of his brand? And then we start, for example, with the mood board, and then we go straight into Figma or Adobe XD and start there, with some ideas. They can roughly be just some UI components, more more like a rough scratch, not not just so we don't do a pixel perfect design at the beginning. It's more of the direction process where we, where we try to generate look and feel and, to to bring a brand to life. Yeah. And after that, then we go for Yeah. For a more precise approach. Yeah. Alright. Cool. Cool. What are what's your your tech stack that you guys are using for, like, most of your clients? Our tech stack is for us. So Nuxt 3 is one of our most used front end frameworks. Yeah. We we also did some things with pew 3, but when it comes to a website for our clients, then Next 3 is our way to go. And for the back end the go to. Use I love Nuxtree. Yeah. We we we do also. And for the back end, now our main way to go is directors. So we all we so whenever there is a a chance to place directive, then we're going to use it. I yeah. Yeah. We would. And, of course, that's why we're here. But but yeah. Yeah. Like, what did you guys like, what tools did you use for back ends before Directus? Or Like our well, let me preface that. Like, are most of the sites that you're doing, like, very content heavy? Or, like, are you doing, like, apps for clients as well? Or We did both. So we we started with Okay. So when when we when we talk about the the typical client who just needs a website, then we came from, WordPress. So, like, almost everybody who started with just the front end things, and there there we started in the in the very early stages. We just, bought the theme, And then we customized it, changed the colors, had to look out, had to look of which which plugins could we install and things like that. And after that, we started doing our own themes, then we just back introduced Elementor, things like that. Yeah. But yeah. Competitive. Yes. Everybody everybody knows WordPress. Yeah. And we don't like PHP, to be honest. It's a it's a solid platform. Yeah. It is. I'm right there with you. Like, WordPress is, it's a solid platform. I I my experience has been much the same, though. So I'll I'll echo exactly what you guys said. Yeah. It become becomes very difficult when you start installing plug ins and another plugin, and then you have to to update it, and then you have to figure out if it's still compatible with the new WordPress version and things like that. And then your whole page keeps breaking. Yeah. That's that's great when you log in and see there's, like, 75 update notifications for the plugins. Right? You know it's gonna be a good day. It's so true. So I do did you guys jump directly from WordPress into Directus, or was there, like, a No. No. No. We some other tool that you try. Yeah. We we did a lot of traditional CMS systems. So we also worked with Joomla. I don't know if this says to anyone out there. We also used KontaO, and that's a very small traditional CMS system. Then we also used, for ecommerce systems, the WooCommerce plugin. That is WordPress as well, but we also did something with Magento. And, yeah, we we we tried a lot, to be honest. And then there comes the point where we heard about, the headless CMS system at first, and then we also tried things like the Tamek. We also tried, Strapi Strapi and Yeah. Pimcore. Pimcore, and yeah. And then Of course. Also. Gosh. So I know you guys have been through, like, dozens of different tools. Yeah. We tried everything, to be honest. How did you guys originally come across Directus, or how did you find Directus? I was more or less a Google search. Yes. I'm sorry. So we we just Googled, what is the best headless team f you could use, and then you get, yeah, then you get directors, you get, you get Story block, I think, is Story block contentful, things like that. And Yeah. That that we we did try every day. Marketing guys are definitely gonna love that. And and we also love the the open source approach. Yeah. That's awesome. Because, where we we did, we didn't have much experience with headless CMS systems, so, the open source approach was was the best one for us to to give it a a try. So we started out with a Strapi, I think, and then tried directors. And directors, us had much more flexibility in building these relations between between the data we we want. Yeah. I would say, we, as a designer, directors was usable, was very, very was was was was very easy to use in comparison with Strapi. Because in Strapi, you had to the server has time to reload if you fetch anything in the data object, things like that. So it was more or less in the in the daily task. Directus was way easier to use. You change something. You have a real time API, and, this combined with the with the hot module reload, and Nuxt 3 was perfectly efficient workflow for us. Yeah. Awesome. Yeah. So I like, my one of my other questions to you guys, obviously, you've got clients that are using Directus. We kinda internally, we consider it like a a Swiss army knife. Like, you could use it for an app back end. You can use it for a headless CMS. There's, like, too many different use cases. So I'm curious, like, how do you guys present Directus to your clients when you're, either, like, in, like, the initial stages of the project or, yeah, later when you're actually, like, building the CMS and and, you know, showing it to them? Yeah. It depends. Sometimes, we we have, clients who does have a a little bit of technical expertise. And when we drop, when we just, make some backward dropping like Hetto CMS, they are, yeah, go for it. We love Hetto CMS. But but and sometimes we have clients who came or who does have WordPress sites at the moment, for example, and they are like, Yeah. Okay. It could be faster, but, I think it's already fast. And then you have to deep dive into what what are the, what what are the the opportunities while using a headless CMS and why it's so much better than using a WordPress, for example, in this scenario where you have to, maybe in our example, what we're going to show, that we have, that we have 2 fraud end sites and one director's back end. This is so much more practicable than than a WordPress back end. And and then sometimes where where we do a back end for for a web application, for example, then, there's no other possibility than using Swiss Army knife, as you said, as directors, for example. I see. Or you even could use it for the digitalization of portfolio. That's also things we've done with, product information management systems. But, as we deep dive in in directors, we learned that this also could be totally possible doing with directors, and that's also a thing. Yeah. Like, the spoken like a true, champion, I guess. Like, that is he like, you guys are very good. Very good. I also love Let's get into it. Alright. Like, are you guys ready to dive in? Oh, go ahead. I'm sorry, Yue. Sorry. I also love the the the the the fact that we can, like, outsource the the content task to the to the to a specific person or to a specific team and say, go for it. That's true. Fill it out, play with it, create some content, and we can focus on building, the front end or the actual use case for the date, the the other ones are creating. Yeah. That's so true. Yeah. So you can hey. Like, being able to, like, decouple the content and the the front end the business logic. Useful in that scenario. Yeah. That's true. Great. Cool. So do you guys wanna dive in and and show this project? I can remember, like, seeing it when we had a a quick call before we scheduled the episode, and I was blown away. And I I think the audience is too. But Yeah. Sure. I'm going to share my screen. Okay. Alright. So while we're sorting out the details there, Yueh, why don't you kind of give me the backstory of the project? So the backstory was, we had so it it all started, I think, 7 or 8 years ago. I was still a student, when I got the connection to, to Rockstar Recruiting who just, who just, raised their who who just build their company, and, they asked me back then for a logo. And that's still the logo we can see over here. And we did a a small so I I did a small brand design back then. And now after, yeah, 7 years later, they reached out back and said, maybe you could help us, doing a new homepage, a new website, and that's what we did. And, yeah, that's what you can see here right now. That's awesome. So 8 years ago, you did the website or the logo for them, and then they like, 8 years later, they're like, hey. This guy is awesome. Let's come back and get the website done. Yeah. It's yeah. That's how it happens. So they are a so they're, basically, a recruiting firm for developers and engineering talent. Is that that's what I gather from the website. Yeah. That's that's exactly what they are. So they are a recruiting agency who, are looking for developers and data engineers and things like that. And, the first thing we did here was, we asked them, how do the users of your website reach to your website right now? And they said, yeah. The most of them are not, some some Google search hits. They are more like they go to a lot of showcases and things like that, and they're and they're Mhmm. From there, they go to the website. So we thought about when do most of the people who go to this website already know who Rockstar is? Why don't we start with a huge call to action where you can reach out to the to the point, what you're going to do right here? So if you're if you're a company and want to hire a tech talent or, if you're a candidate, meaning a developer or a data engineer or something like that, you could also go right to the to the candidate page. So we just made the animation where you can switch to left and right for, the for the candidates and also for the companies. Nice. Yeah. Nice. And I I love the, like, the black and white aesthetic and and just yeah. It, like, it it feels very much, like, you're specifically targeting developers in it. Are those, are those keyboard shortcuts that I see down there as well? Yeah. The the these are keyboard shortcuts inspired by the terminal, meaning you have, for example, here, you can see the control and h button, then you will get the would get to home or you could press control and e to go to the imprint and, for the events or the recruiter or everything like that. And you could can hit it and go straightly to the recruiter page, for example, like I did here. Or, you also could, if you're not, technical, more, more a company, for example, who want who's who's searching for technical developers, then you also could, can also just press the button. So the idea is to have a a user experience for both, for the developers or the tech experience people, but also it should be, able to navigate if you don't have much experience with with the terminal in this scenario. Yeah. And to keep it here, we we used a lot of, look and feel, you know, from from from the one point zero time. So, like, these ASCII borders, and this typing animations and everything. It's feels like like like a bit a long time ago. Yeah. That's so true. And, when we beautiful. How long did you guys spend in, like, the design phase for this? I think the design phase Just curious. In in 8 days, something like that. I think the the whole phase with with finding the right idea and deciding which one to go, it went about, like, a month. Yeah. When you go for the whole process with with thinking about the boot and yeah. Then it did take something like a month. But the the the the designing process where we just made the UI components where the idea was set, then, I think we we wait. We went forward pretty, pretty fast. Yeah. And and here we have things like, for example, the ASCII border. In the screen design process, it was pretty simple, and we thought about, yeah, the this this would be something like you would do it in the web 1.0 version. And, we didn't think one second about how it could be used, in a responsive scenario because in the one point zero world, there was no responsive. So we had we had problems that that we never thought about because when you when you want to, make this ASCII border responsive, it gets, it it gets it was quite complicated, to be honest. So we made the SVG grid. Yeah. I bet. So yeah. We we just started with just using some CSS and changing the content. Then we learned, okay. It's not possible. Then we used the Busia then we tried to boost JavaScript. Also pretty hard, and then we came to the SVG grid. It there you go. Yeah. That's you guys definitely made it challenging on yourself. Like, the end result, it it looks great. Yeah. Thank you. Yeah. But yeah. The these these are some small things which you didn't think when you're designing that that you never would think about. Yeah, it could can could be a problem doing a border like this. So Yeah. These are the things, which which, which which come back with a comment, like, this is not possible. Yeah. That's so true. And I can totally understand that. Yeah. I can totally understand that the the this would be something which can come at yeah. Possible. Yeah. But we made it possible with with a lot of effort, but, yeah, it's it's there. And, yeah, we also did hear the Odenowski animations. Yeah. Those are great. Can you guys or does your client, Rockstar, mind if you show us around, like, the the direct us instance that's powering this? No. Not at all. We asked before, and it's not a problem at all. So we can deep dive into the directest. So and as we told before, they have 2 front end pages. So they have 2 websites, And Groxo, the one we just saw, and text executive can also show that. That that's the same thing for, for executive roles in the development process. And, this is Okay. Gotcha. Yeah. The the this is right now, that's not an own firm. It's just by Rockstar Recruiting right now. They will change that, I think. But for the moment, they share a lot of global components. For example, the people. There are a lot of people who are working for Rockstar right now and also for the executive, That's the point where directors made so much sense because we could, do some global components and some components for Rockstar or for TechSec in this scenario. Yeah. Like, the locations, the blog post, people, and so on. Yeah. The blog post, for example, goes for both. People goes for both. And also the locations because they, decided not to translate their content. They decided to, just show the different different locations with different content. So you can choose between Berlin and Zurich, and there you have different people for, for the team, and you have have some some more Berlin related infos and things like that. So if you we go for the whole Got it. Here you here you can choose Cool. Between Berlin and Zurich, for example. Yeah. Nice. So this is using the Directus translations feature, but not necessarily for translations. It looks like it's all in English. That's true. All is all is in English, but they, we just use the translation feature as a location feature, and it works pretty good. Yeah. That's great. So it looks like I see over in the, like, the nav bar, you've got your globals, and then you've got, like, your different stacks of rock star versus tech executive. And, like, how like, your what do your clients think of of this? Like, was this, like, an iterative process when you built, like, the back end of Directus with them, or you just said, like, hey. We've gone through and mapped all this, and then just, like, hey. Go take a look and tell us what you thought. Yeah. That's that's one of the coolest thing in that. Coolest things in directors because we we do draft with, how we want to organize our data or how we want to be more specific when we have a UI component like, this one at the beginning, how we want to, to make it possible to change these these words which are typed over here. And then there are things which just make sense. For example, here, you can add the text for it, and then it will just, be changed in the in the front end. And it's, you can extend it with other, with other developer variants, how we called it, or you can erase one just with the tech feature. But Gotcha. Yeah. And and And you guys have it looks like every bit is dynamic on here. Like, the Yeah. Almost. That's so true. Almost. Yeah. Yeah. That's great. And and yeah. Because we we, as designers, we love to give the users a good experience on the front end. So on the website, when they are entering it, we we love a good user experience when they know how to navigate, when they know how to scroll, when they know where all the, most important informations are. But, with Directus, we had the opportunity to also make a good user experience for the back end, and that's the thing we really love because, we have the possibility to say, okay. We can we can, make almost every all yeah. Almost, everything dynamic, and then they have the possibility to change some things. But, in this scenario, it's almost not not only almost, it's impossible to break the design, because, all all things are as limitless as they they will always look good. It doesn't matter how much they enter here. And that these are the things when you for example, when our clients had Elementor in WordPress or things like that, they they just started entering a headline, which was, I don't know, 100 and 20 words or something like that. And the whole design breaks, and everything looks awkward. And, yeah, with directors, we have I've I've been there a bunch of times. Yeah. And we have the possibility to limit everything that's that's almost impossible to break the design. And that's a huge thing we love about. What are, like, 2 or 3 of your, like, your favorite pieces that you guys have implemented for this? I think the the contact form for tech executive was quite a quite a thing. Yeah. That's true. This one's one of our most loved features. It's a contact form for the tech executive page on, tech executive. And there you have the differentiation. So how do you want to start? You can choose between I'm a candidate or I represent a company. And if you choose 1, then you get, to the to the type to to the form wizard where you can enter your name and, your last name, then your email, and, your phone number. Okay. It checks it's a real phone number. Okay. And, all this is completely dynamic, and has has is completely editable. So you have the question groups. You can see over here with data, then the professional track record, technical skills, additional informations, and you can see all these things for the candidate form here. So you have the personal data, and there you have the questions we just go through. So first name and, last name, email, phone, things like that. And you always have the possibility to create a new question, and there you can choose between a question type. So we have a question with answers or a question with input or a question with answer autofill. So if you go for the answer, for example, I will show the the front. Yeah. This this looks really amazing. I I can't imagine how many hours you guys have spent just on this one component. Yeah. It was quite hard. But I I think it's very cool because now it's it's a pretty fast wizard for very complex and for a very complex, for a very complex topic because entering all data in from a CV, for example, the technical knowledge in this scenario is a question with answers autofill in short over here. And this is a component where we made, a question with answer autofill. The question is, what's your technical knowledge? Then enter your program image you're familiar with and write your experience from 1 to 6. And then you get all the autofill words. These are the, most used programming languages. And then you have the rating enabled, and you get, you can enter a a thumb a hint for each star. So it's a Novitz beginner intermediate advanced professional. Wow. This is And when mind blowing, guys. And when you, for example, add JavaScript, you can choose, you're a beginner or a new leads. And this this all comes from directors. And you have the possibility, for example, to do this with technical knowledge, but you could also use this component for, for languages. So are you a fluent speaker of, English, Spanish, or whatever? And then you also could rate it, and you could also, change the the the, the states here with intermediate bonds and things like that. And you could also, also, fill in all the autofills, like we did here. This is Wow. Pretty cool thing where we first used the edit draw value because when we when we we we got we were provided the list with, I don't know how many there. I think something like 250 programming languages. That's And then and then we yeah. That's that's that's true. And then we want to add it. And the first thing we tried was, like, okay. Yavas, and then we did some commas, but it didn't work because when you enter a comma that makes sense, then it just enters the this, this, this tag. But placing all 250 tags with with just copy paste them, it would be a lot of work. And then we just found out, ah, you you have the possibility to edit the to edit the raw value. And then we just, take all of them, export it as a JSON, and place copy paste it in here, and voila. There they are. Yeah. That's pretty cool that that you also want. Is that something that you teach your clients as well? Like, the edit raw value? Do your clients use that? Or just kind of you guys For for this specific use, we didn't have to because it's more like a initial fill. Because after after reporting all the 250 tags, the the only things which can happen in the future is that there will be 1 or 2 more, and you just have to enter them in here. So you you you will never have the the the possibility the use case again where you have to import 250, auto fill words because yeah. Which programming language? I don't know. They they just added, I think, OpenAI or something like that, which is pretty new. But, it's not like there are 3 or 4 program languages a month. Right. Right. What's, I'm I'm curious. Like, what do the relationships look like just for, like, this one, like, question and answer, like, form component? Which in the data mode? Uh-huh. Yeah. We we have the question groups with the name, description, and questions. Here, I see many to many relation question. And Look for the questions. Okay. We have the question. No. No. That's the group Where are we? Here the quest. Ah, yeah. Okay. So it's it's a credit it's a huge it's a huge question, data model. But it's it's still very easy to use because we made we would did work a lot of conditions in this scenario. So this is what I oops. This is what I thought, would which I have shown here. When you go for a new question, we have so many conditions that the first thing you would see is the question type. And after filling that, you will get the, question, question description, author words, and then you have also the condition for, do you want a rating for the answers? And if you enable that, you also, could, can change the, the rating description. So So it's very much yeah. It's a it's a lot of information, but, like, with the conditional fields inside Directus, you don't overwhelm the end user at all. It's very clean. Yeah. Yeah. I love it, man. Thank you. What like, are there any other, like, favorite little features that you guys have have built into this thing? Yeah. I think one of the the things we already have shown was, the the thing was the developer variants over here. And, one thing we also done is additional pages, And this is where we didn't use, right now, the many to any relation where you just can, place some components in. But, they had the idea that they want for SEO landing pages for for, different keywords which which they are going to pay for for the, Google Ads. And, that that's when we introduce the the component additional pages, and this is, just the ID in the next, and they can add it over here. So they have the page name contracting, for example, then they got a huge where they can implement everything. They also have a script field because they are, they have a good technical expertise. So and they ask for it and we just let them do that. But, yeah. Normally normally, this would be a much too too much for a customer with no technical expertise. Right. Yeah. And Yeah. Yeah. Yeah. It's really easy to blow up the site with those script tags. That's so true. But in this scenario, it works, and they have the possibility to enter a page name, and then they are live, the pages. So they you can reach them by entering the slash. And if you want to, if you want to, if you want to place them in the menu, you also have the possibility to go into the menu field, and there you can add the menu and also have the possibility to add the letter for the quick access, for example. So the menu we showed. I mean, like, you guys have literally thought of everything with this. Yeah. It it was very fun because, we we we it was the first time we we we recognized, how many possibilities there are with, not just making it dynamic, and also to make something like conditions and to limit things and to give, things like soft quotas in the in the blog posts for the descriptions, for example. And, yeah, these are a lot of things which which just made so much fun. That's the reason because why we, yeah, why we try to to And not everything dynamic. And also have the the whole control over the the the map. This was also pretty pretty impressive where each page had the type description in which field, which the the the client can use if if he if he wants to. And this Mhmm. Was was pretty neat and pretty pretty, pretty good because he he has the the whole control over over the SEO thing and, over his Google Ads campaigns. This was also, like, a game changer for us. Yeah. That's true. The metadata field was Yeah. And it looks like that is using the the translations feature as well. Yeah. That's true. So you can have different metadata for the different locations. Nice. And we also make And, is it, like, are both of these sites are built on Nuxt on the front end? Yeah. Both both are Nuxt front end pages. Gotcha. And, hey, like, like, obviously, like, you guys are you you mentioned you had a lot of you experience, but, you know, what were your like, as you were building this, like, what would you say are, like, your favorite bits or or, like, why why would you reach for Nuxt versus just, like, regular Vue or, you know, one of the other I think there's several other frameworks on Vue as well. I think Nuxt works best for us because it we test the different render modes, for for example, the hybrid rendering and the the SSG thing, and and the new, we we use I think it was the first time we were using, like, the server side rendering because before we to. We also we we we just did a project with aesthetic Yeah. Just aesthetic aesthetic size, aesthetic stack. That that was two times, but, yeah, we we also did the the the Nuxt generate and, reached out their static sites. But, yeah, the server side rendering thing is way too cool, to be honest. For me, I like the the file based routing because in in in the classical view environment, it's, it's it's it's it's in the ass to to configure the route and to say the these are the children of this route and and to register all the components. And if you want to start a a website with Nuxt, you just hit the CLI, go for it, and, directly start, developing for I think it's it's the perfect way to go. It it it and it keeps the boundaries for for for for get for making progress very low. Love it. Love it. What, it'll, like, did you guys have any trouble integrating Directus with Nuxt? Not at all. We we did we did see, there there was a example online already, I think, with with the block and next, thing, and that's that's something we set up, and then we just get from there. We we are we had everything we we needed, to be honest. So it was very easy. Yes. The beginning I saw, I think it was your guitar repo, Brian, the the one with the nice director's actually True. True. It was his. We built a little wrapper around because, we had to to implement the, our localized functionality. So we had a little wrapper around and around the wrapper, where we check, okay, which local is present and which local should be used. So, Yeah. Great, guys. So I I do wanna be respectful of your time. I know we're I think we've gone a little over what we had planned, but this is, it's so interesting to me to, like, see how you guys have constructed this and put it all together. You know, let's talk about, like, the the future. Like, what's, what's the future look like for you guys? I know that's pretty vague, but, like, what are you working on? Like, what do you wanna where do you wanna go with this? Like, what else do you wanna do? One thing we we are going to do for the page we just have shown, I think quite funny because I've seen you you just released, I don't know, 2 weeks ago or something like that, a video with, ChetchyPT and directors. And Yeah. That's the thing we we, did also in the same time, to be honest. And we That's great. We we love it because the idea is to you still have a a a long journey while filling out the whole form for a candidate for, because you have to do, you have to enter your name, your email, your telephone number, and also some some text, like, what are experiences in your technical industries and things like that. And our idea is to, just drop down your CV, and then we will pass all the data set it to chat gpt, and chat gpt will sort all the data into the fields we have in our form. Just, we'll prefill the whole form, and in the best case scenario, you just have to press next next next next and send. Wow. That would be amazing. That would be a great dude. Like, we're calling that new series the direct to top line, but that would be that would be a great episode as well. True. Oh, and what about the the future for you guys? Like, do you have a a bunch more projects in the works aside from this one? Or Yeah. We we we do have some daily tasks, but, for us as as a Calabrio, we, our next step would be to we we're the moment refactoring our own home page. So our Heinrichs on Heinrichs.ke. And, we we just set up a directors for this site also, and we're trying to to, reach out to the world with more, with more public relation. So we we're just looking forward to, to, release a blog and to do some things on LinkedIn to have the possibility to get more cool projects like Rockstar, for example, and not just to be in our daily task and as a ghost designer and ghost developer. Yes. And also, the website is getting a new a new look and feel, a new, a new design update, a new technical update. We're moving to moving into next 3. So at the moment, Fuse. Right? Yes. And we're also doing trying trying a new thing with directors, where we, we are rendering blocks. And maybe we can show many to any where we where we are where we're trying to like like a home page. The home page contains different blocks. And in the front end, we will have, like like a like a render skeleton where Mhmm. The the API call determines which component gets rendered. So we have, like, like, a front end block and a back end block, which will be which can be used, like, in in in in many ways or for many projects. Yeah. The the main idea is to have, Like like reusable components over and over and over. Like like like a like a like an own component framework. Small library where we just make, can make some, front of UI components and directors and reuse in our Nuxt framework, to to, yeah, to just get things passed down If you have the reusable components that you use all the time I've got some to share with you guys on that front then. Yeah. Yeah. I I I've got a project that is in the works, teasing the audience a little bit, but I'll share it with you guys for sure. I think it'll be helpful for you on that side. Yeah. We can. Yeah. Guys, so if anybody wants to learn more about you guys and, like, look at some of the projects that you've done or or even work with you guys, like, how how's the best way to go about contacting you? You? Reach us our our, to our website. So it's it's a very complicated URL. Many for the English speakers because it's Heinrichs. De, but, we just bought a new, URL, Heinrichs.cool. And then you cool. Very good. That's a little easier. That's very easy. There you can reach us via email every time. Yes. I think when this episode will go live, and the new date will also be live. This is this is not me calling you out. Don't put yourself on a timeline. We're gonna I think we're gonna turn this one around pretty quickly. So, yeah, I I I I look forward to seeing the the new site. I could tell you all the the guys on our team played around with your existing site for, like, 20 minutes, and they were like, oh, this is so cool. So I can't wait to see what the new one looks like. Guys, been a pleasure. I I really appreciate you joining us, and I I can't thank you enough. And I I look forward to seeing, like, the next project that you guys come out with. Thank you so much for having us. Alright, guys. Thank you. Thanks.",[6049],"162e7b09-df83-4b34-ae4b-a88eddb69e52",[],{"year":2380,"number":5101,"id":5102,"episodes":6052,"show":6053},[5104,5105,5106,5078],{"title":5108,"slug":5109},{"id":2384,"slug":6055,"vimeo_id":6056,"description":6057,"tile":6058,"length":264,"resources":12,"people":6059,"episode_number":177,"published":6061,"title":6062,"video_transcript_html":6063,"video_transcript_text":6064,"content":12,"seo":12,"status":19,"episode_people":6065,"recommendations":6067,"season":6068},"10-2","894077992","In this video, Esther unveils a multitude of enhancements, bug fixes, and novel additions, all designed to make your data management experience smoother and more efficient.","fbe6ba3a-68e0-4724-9810-5b32f599bfe9",[6060],{"name":3978,"url":5699},"2023-05-26","What's New In Directus Version 10.2","\u003Cp>Speaker 0: Hi. This is Esther, developer advocate at directors. And today, we are releasing directors 10.2 with a host of new features, improvements, bug fixes, and optimizations. Let's take a closer look at what's new in this release. Introducing live preview.\u003C\u002Fp>\u003Cp>One of the most exciting new features in directors 10.2 is live preview. This feature allows users to see how changes to their data would affect the look on their sites from within the director's data studio. Users can configure a URL pattern for each collection and preview from an item page. From the live preview, users can also test their content at various device widths. We've added a number of guides to our docs to help you set up a website for live preview.\u003C\u002Fp>\u003Cp>This feature is a game changer for those who want to see how their content will look like before publishing it. Improved translation storage. We've overhauled how we store and retrieve translation. Prior to this release, directors stored all languages together when translations were present. This meant that every time a translation was requested, all of the data was retrieved even if only one language was needed.\u003C\u002Fp>\u003Cp>Now, directors toss translations separately which means that it doesn't waste time retrieving data that isn't needed. This is a significant improvement that will make directors faster and more efficient for users who work with multiple languages. New environment variables for file upload safety. Version 10.2 also includes new environment variables for file upload safety settings. These variables will allow users to set file type and size limits which will help prevent security issues and ensure that files are uploaded correctly.\u003C\u002Fp>\u003Cp>One significant bug that was fixed in this release caused self hosted horizontally scaled direct instances to trigger a phone based flow more than once. This was due to the fact that the various instances weren't aware of each other. Directus now utilizes Redis to synchronize scheduled flows and scheduled hooks between multiple instances connected to the same various instance. This fix ensures that directors works as expected even when multiple instances are running. Our community is awesome.\u003C\u002Fp>\u003Cp>Speaker 1: We want to also thank our community for their contributions accordion, and Matt for fixing the block editor in accordion and maths for fixing the block editor interface popover styles.\u003C\u002Fp>\u003Cp>Speaker 0: Directus 10.2 is now available on NPM and Docker Hub and will also be available on Directors Cloud in a few days. Thanks and see you in the next release.\u003C\u002Fp>","Hi. This is Esther, developer advocate at directors. And today, we are releasing directors 10.2 with a host of new features, improvements, bug fixes, and optimizations. Let's take a closer look at what's new in this release. Introducing live preview. One of the most exciting new features in directors 10.2 is live preview. This feature allows users to see how changes to their data would affect the look on their sites from within the director's data studio. Users can configure a URL pattern for each collection and preview from an item page. From the live preview, users can also test their content at various device widths. We've added a number of guides to our docs to help you set up a website for live preview. This feature is a game changer for those who want to see how their content will look like before publishing it. Improved translation storage. We've overhauled how we store and retrieve translation. Prior to this release, directors stored all languages together when translations were present. This meant that every time a translation was requested, all of the data was retrieved even if only one language was needed. Now, directors toss translations separately which means that it doesn't waste time retrieving data that isn't needed. This is a significant improvement that will make directors faster and more efficient for users who work with multiple languages. New environment variables for file upload safety. Version 10.2 also includes new environment variables for file upload safety settings. These variables will allow users to set file type and size limits which will help prevent security issues and ensure that files are uploaded correctly. One significant bug that was fixed in this release caused self hosted horizontally scaled direct instances to trigger a phone based flow more than once. This was due to the fact that the various instances weren't aware of each other. Directus now utilizes Redis to synchronize scheduled flows and scheduled hooks between multiple instances connected to the same various instance. This fix ensures that directors works as expected even when multiple instances are running. Our community is awesome. We want to also thank our community for their contributions accordion, and Matt for fixing the block editor in accordion and maths for fixing the block editor interface popover styles. Directus 10.2 is now available on NPM and Docker Hub and will also be available on Directors Cloud in a few days. Thanks and see you in the next release.",[6066],"188ce690-fa5d-4d69-9caf-5e401cd73467",[],{"year":2380,"number":588,"id":2381,"episodes":6069,"show":6070},[2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394,2366],{"title":2396,"slug":2397},{"id":2383,"slug":6072,"vimeo_id":6073,"description":6074,"tile":6075,"length":264,"resources":12,"people":6076,"episode_number":13,"published":6078,"title":6079,"video_transcript_html":6080,"video_transcript_text":6081,"content":12,"seo":12,"status":19,"episode_people":6082,"recommendations":6084,"season":6085},"10-1","894078027","With over 70 improvements, bug fixes, and new additions, Rijk, our lead maintainer, guides you through the significant updates we've released.\n","e6689d8e-ee7d-4af9-adac-96028478b162",[6077],{"name":3795,"url":3796},"2023-05-12","What's New In Directus Version 10.1","\u003Cp>Speaker 0: Hey. My name is Eric. I'm the lead maintainer of Directus. Here to tell you a little something about everything that's new in version 10.1. This release has over 70 new features, improvements, bug fixes, other optimizations.\u003C\u002Fp>\u003Cp>So let's dive straight in. The first big part of this release is the release of the extensions that were previously exclusive to Directus Cloud. These include a panel bundle for insights that contains a bar chart, a line chart, a pie chart, which is also a donor chart, and a meter to add way more different ways for you to visualize your data within Insights. The second big extension that was released to core is a Kanban view. By pointing it to a drop down field or any sort of other select interface, it'll render out individual columns for you to drag and drop items between.\u003C\u002Fp>\u003Cp>This is great for project management and also other organizational purposes. The final extension is a new block editor interface. This interface allows you to edit your content in like a Notion style interface, and the data is stored in a clean JSON format. The file picker drawer has had a seemingly small but pretty big quality of life improvement in this release as well. When you're now selecting an image in one of the image interfaces, the file picker drawer now has the same sidebar you would find in the file library.\u003C\u002Fp>\u003Cp>This allows you to find the file that you're looking for by the folder that it's stored in. There's a new rate limiter type in the API based on server pressure. By the way, before, when your API was being overloaded, the node process eventually will slow down and the problem only gets worse. With this new rate limiter, you're able to configure the API to drop requests a little early when it notices that the server is heavily under pressure. In 10.1, we now default to e s m for API extension bundles as well.\u003C\u002Fp>\u003Cp>This means that there's a greater compatibility with other libraries in the node ecosystem including the ones that are provided by the Directus Core team. We want to also thank our community for their contributions towards this release, including Tim for adding support for relational fields and field presets, Hannes for numerous contributions in this release, including an improvement to how related collections are displayed in the data model settings, including a bunch of other bug fixes as well, Arthur, who added a graceful shutdown of database connections in the bootstrap CLI command, and Matthew and Maurice for helping with the documentation. Directus 10.1 is now available on npm and Docker Hub, and will also be live on Directus Cloud in the next couple days. That'll be all for release notes, and I'll see you next time.\u003C\u002Fp>","Hey. My name is Eric. I'm the lead maintainer of Directus. Here to tell you a little something about everything that's new in version 10.1. This release has over 70 new features, improvements, bug fixes, other optimizations. So let's dive straight in. The first big part of this release is the release of the extensions that were previously exclusive to Directus Cloud. These include a panel bundle for insights that contains a bar chart, a line chart, a pie chart, which is also a donor chart, and a meter to add way more different ways for you to visualize your data within Insights. The second big extension that was released to core is a Kanban view. By pointing it to a drop down field or any sort of other select interface, it'll render out individual columns for you to drag and drop items between. This is great for project management and also other organizational purposes. The final extension is a new block editor interface. This interface allows you to edit your content in like a Notion style interface, and the data is stored in a clean JSON format. The file picker drawer has had a seemingly small but pretty big quality of life improvement in this release as well. When you're now selecting an image in one of the image interfaces, the file picker drawer now has the same sidebar you would find in the file library. This allows you to find the file that you're looking for by the folder that it's stored in. There's a new rate limiter type in the API based on server pressure. By the way, before, when your API was being overloaded, the node process eventually will slow down and the problem only gets worse. With this new rate limiter, you're able to configure the API to drop requests a little early when it notices that the server is heavily under pressure. In 10.1, we now default to e s m for API extension bundles as well. This means that there's a greater compatibility with other libraries in the node ecosystem including the ones that are provided by the Directus Core team. We want to also thank our community for their contributions towards this release, including Tim for adding support for relational fields and field presets, Hannes for numerous contributions in this release, including an improvement to how related collections are displayed in the data model settings, including a bunch of other bug fixes as well, Arthur, who added a graceful shutdown of database connections in the bootstrap CLI command, and Matthew and Maurice for helping with the documentation. Directus 10.1 is now available on npm and Docker Hub, and will also be live on Directus Cloud in the next couple days. That'll be all for release notes, and I'll see you next time.",[6083],"59cadab1-e34e-4fba-a5ca-1a2fdbc7ef30",[],{"year":2380,"number":588,"id":2381,"episodes":6086,"show":6087},[2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394,2366],{"title":2396,"slug":2397},{"id":5105,"slug":6089,"vimeo_id":6090,"description":6091,"tile":6092,"length":212,"resources":12,"people":6093,"episode_number":177,"published":6098,"title":6099,"video_transcript_html":6100,"video_transcript_text":6101,"content":12,"seo":12,"status":19,"episode_people":6102,"recommendations":6106,"season":6107},"platform-to-monetize-communities","894102457","In this episode of I Made This, Pedro and John chat with Bryant to learn why he chose Directus and walk-through how he built a community monetization platform.","51ab7dd2-9eef-487f-84e3-b4469285d530",[6094,6095,6096],{"name":3590,"url":3591},{"name":4361,"url":4362},{"name":4524,"url":6097},"https:\u002F\u002Fdirectus.io\u002Fteam\u002Fpedro-pizarro","2023-03-17","Building a Platform to Monetize Communities","\u003Cp>Speaker 0: Hello, everyone. Welcome to this episode of I Made This. My name is Pedro.\u003C\u002Fp>\u003Cp>Speaker 1: And I'm John. Thanks for joining us today. We also have Bryant here. Bryant, why don't you give us a brief introduction into who you are?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Absolutely, guys. Thanks for having me. My name is Bryant Gillespie. I am a jack of all trades, I guess you could say.\u003C\u002Fp>\u003Cp>I do a ton of different things. I've got a design background. Been in the sign and print industry for probably too many years at this point. I would say, like, 15, 16 years. Yeah.\u003C\u002Fp>\u003Cp>I've worked in kinda all facets of that industry. Like, I previously ran and helped manage a sign and print shop for probably 5, 6 years. We grew that business, and I burnt myself out and was looking for the next thing. And then I got hooked up with a software company that made software for the sign and print industry. So it was a great fit there, but, yeah, I've got a ton of experience in just a little bit of everything from business management to design, marketing, even a little bit of sales at my former company there for a while.\u003C\u002Fp>\u003Cp>And I've also got this development streak that I'm on at this point. I'm not a professional developer by any means, so let me preface everything that we talk about here. With that, Last 2 or 3 years, I've picked up JavaScript development. Mostly, like, working at a software company, I always wondered, k. How hard is it to do what we're talking about on the product side of things?\u003C\u002Fp>\u003Cp>How challenging is it to build this certain feature? And, yeah, I've got a nasty habit of scratching my own itch that sometimes gets me bogged down into crazy projects like the one we're gonna talk about today.\u003C\u002Fp>\u003Cp>Speaker 0: Awesome. And through that software development streak and kinda learning software development, is that how you found Directus, or what was your first touch with Directus?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I can't remember exactly how I came across Directus. I know hey. Like, the background story there is I used to be really heavy into Airtable, and I think it's a really great tool, great platform, great company. But I had a client that we built a really nice Airtable base.\u003C\u002Fp>\u003Cp>And over the course of 2 or 3 years, this thing just kept getting more and more complex as any project does. And so when we first started, it was just simply, hey. Let's keep track of our orders. And then we started bolting on things like, hey. We wanna be able to send an online proof to our clients and let them approve the artwork for this order, and then, hey.\u003C\u002Fp>\u003Cp>Let's let them pay for the order. And then it graduated into a full blown website where a the client could come on sight unseen and place their own order for decorated apparel. We as we kept iterating on that, things got more and more complex, and there were more moving pieces. And we rapidly started hitting the limits of Airtable, not just, like, what it was built to do. There's some API rate limits there that kind of affect how you call their API and what you can actually do with it.\u003C\u002Fp>\u003Cp>But there's also, I think we were on one of the we weren't on the enterprise plan, and the enterprise plan at the time was probably, like, 3 dollars 1,000 a month, which is kind of out of reach for yeah. We did. Definitely chump change. Yeah. So it's kinda out of reach for a small company that has 15, 20 employees.\u003C\u002Fp>\u003Cp>So that's a lot of money for them to spend on a platform where they were at maybe several $100 a month with all their employees on there and all the platforms and things that we had built around AirDance. That makes sense.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think you're on a similar journey, or I guess I'm on a similar journey to you. And if for anyone who's out there watching, Brian's face probably does look familiar because Brian also does videos for Directus. And so as I've come along on this Directus journey and as Pedro and I have started doing some videos, I also have an entrepreneurial spirit inside me. I've started several companies on the side and kind of have ideas that are floating around inside of my head too.\u003C\u002Fp>\u003Cp>And so I've been on this similar sounding journey to you of exploring these other platforms out there. A lot of these platforms are great for what they do, and they all have their limitations as any software does. But, you know, the more I've been creating these videos for Directus and working for Directus and seeing what it can do, one of the nice things as I'm building out these platforms and some of them actually make their way to actual companies, whether they're small or medium sized, some of them fizzle out. One of the great things that I've kinda come to discover about Directus is there there is no limitation, especially if you're starting off in self hosted. You're not gonna hit that $3,000 a month mark right right away.\u003C\u002Fp>\u003Cp>And so it it's so great for a beginner developer begin I have some experience in web development and websites and things like that. But as you're starting to explore, you're not gonna hit any wall in regards to pricing. You can kind of build it out and get a full fledged platform without having to worry about, oh, I can't get the full traffic tested. I can't gonna have to pay $500 a month starting off. I think that's kind of a similar journey that I've come to uncover and have more creative ideas flowing as I've been creating these videos for Directus.\u003C\u002Fp>\u003Cp>So it sounds very similar to kind of the journey that you are ahead of. You've been making these videos longer than we have, but sounds like a very similar journey.\u003C\u002Fp>\u003Cp>Speaker 2: Kind of\u003C\u002Fp>\u003Cp>Speaker 1: creating you found Directus. You started using it. You started creating videos for it. More ideas come to light. Does that kinda sound right?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. A 100%. And so like I was saying, we were hitting the limits of Airtable, and then it was like, well, what's the next jump? Like, where do we go from here? Hey.\u003C\u002Fp>\u003Cp>This could be a stand alone app that we could have somebody program or develop the back end. And at that stage of the journey, I wasn't comfortable enough to say, hey. This is something I could take on myself. So I started looking at, okay. Are we gonna have a developer or an agency or somebody, like, build this into a full blown app?\u003C\u002Fp>\u003Cp>Because we're running out of runway here on this no code tool like Airtable. And and I'm not knocking those tools at all, but, you know, like you mentioned, there's certain limitations that you run into that you you don't know at the outset. You don't know that you're gonna run into those things or we probably had I known where we were gonna end up with the tool, it probably would not have started where we did with Airtable. Yeah. But Fortunate\u003C\u002Fp>\u003Cp>Speaker 0: enough that you grew at that pace. Right? Like, people underestimate their abilities and they grow a company or grow a software or an organization much faster than anticipated. So that's a good thing.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Absolutely. So we were looking for the next thing and I don't know if it was through Product Hunt or Hacker News or some other group that I like, a random Discord or Slack group or something that I'm a part of. I had stumbled onto the Directus website and I saw I think it it was really close to when version 9 launched, I think. And I was, like, like, what is it?\u003C\u002Fp>\u003Cp>This open data platform. And I saw the screenshots, and I was, like, hey. This kinda looks similar to the other tools that I'm used to, but it looks way more powerful because they're talking about a full SQL database that I own and immediate generated APIs and all the things that I could do through the UI, through the no code app that I'm used to doing in other platforms and more. So Directus did not work out for that particular client. We kind of made a few changes that extended the runway on their existing platform, but I kinda took up the mantle, and I was like, hey.\u003C\u002Fp>\u003Cp>This is I could use this for all my other things, like my own personal projects and get a leg up because now this is actually going to scale. This thing is, like, I'm driving the Ferrari on day 1 instead of effectively driving the Pinto for a little bit and then trying to figure out, hey. Do we slap a turbo on this or get some new wheels or what do we do with it?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. As someone who owns a Ford Fiesta vehicle, I feel I feel slighted that, you know, my car\u003C\u002Fp>\u003Cp>Speaker 0: is great.\u003C\u002Fp>\u003Cp>Speaker 1: It's a great car, but I would take a Ferrari over it. Yeah. And so I think that's a that's a good lead in because a lot of people you know, I I come I came into Directus just thinking you were somebody who created videos, but you've created a whole platform on top of Directus. So why don't you give us a look into the most recent project that you built on Directus because I think it's really cool. I think it kind of ties into a lot of what Pedro and I have heard on calls with prospects and customers as well.\u003C\u002Fp>\u003Cp>So why don't you give us a little insight into the most recent project that you have?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Yeah. Absolutely. Give me a second here, guys, and I'll share my screen. BetterSignShop kinda started when I left my previous company.\u003C\u002Fp>\u003Cp>I was the head of customer success there. I managed a team of customer success managers that would implement and train people on how to use our software that was designed for sign and print. So I've got this this bug, shop manager, being the 2nd in command for 5 years and experiencing every facet of the business through that, and then working on the tech side as a software provider, I probably worked with 350, 400 shops directly over the course of 7 years at my previous company. It was one of those things. It's like, hey.\u003C\u002Fp>\u003Cp>I'm it's time for me to move on to the next thing, and I immediately jumped into consulting with sign and print shops. Just that was the background that I had. That's where I've got the most leverage. Why not explore that? So that morphed into a lot of random projects.\u003C\u002Fp>\u003Cp>Like, the best thing to come out of it was probably this platform that I'm gonna show you guys. But we started a podcast, we started a mastermind group where we meet once a month, we bring in like an outside expert that talks about a specific topic. Maybe that is online marketing for sign and print shops or how to hire sales reps because it's traditionally not a it's a it's a small business type of industry. There are some very large printers and very large sign companies in the world, but traditionally it's it's a small mom and pop type industry. Most shops have fewer than 20 employees.\u003C\u002Fp>\u003Cp>So we had all these resources that we're putting together, and we said, hey. Let's build a platform to contain all those. So we had just a bunch of random stuff that we were building, and we were like, hey. How do we connect all the dots? Right?\u003C\u002Fp>\u003Cp>Like, we were doing interviews with industry experts. We had 100 of we had a 150 members inside a private Facebook group. We've got some calculators on, hey, should I buy that piece of equipment, or should we keep outsourcing this service? So we had all these things, and the challenge was, like, what do we do with it? Like, how do we bring it all together?\u003C\u002Fp>\u003Cp>And that's when we decided to create this platform, My Better Sign Shop, to bring it all together.\u003C\u002Fp>\u003Cp>Speaker 1: I like that you chose a color scheme that fits with Directus, the purples and pinks and\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Yeah. So how I got into this industry was as a graphic designer. So everything I do, I've got that designer OCD type of thing going on where I always hate working on my own stuff because it's so hard for me to pick something. And when we started this, I was like, like, I'm just gonna go wild with this.\u003C\u002Fp>\u003Cp>Like, we gotta go over the top. So my kids, my 3 daughters are into pink and purple, and I said, let's leverage that, and we'll just own those colors. Hey. If you see pink and purple in this space, like hot pink, that's gonna be us. I like it.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Why don't you why don't you give us a quick walk through kind of how you built it out in Directus and then maybe even an insight into what did you build the front end? What did you build the actual website with? You know, what what technology Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Absolutely. So I the thing about Directus, it was so nice building out the data model. And I've you know, I since I graduated from Airtable, like, I I was kinda used to already, like, how do I map out this data relationally? How do I make sure that I'm not duplicating data that that I could just link using a relationship?\u003C\u002Fp>\u003Cp>So the first piece that we started with was probably like the LMS, our learning management system, because we kind of had this course called Profitable Sign Pricing that I was working on prior to this platform, and we just kind of rolled it into the platform. Platform. So inside Directus, I'm not sure if you guys can see this, I could probably zoom in just a little bit. We started, that was the piece that we started with first. So and maybe while we're See\u003C\u002Fp>\u003Cp>Speaker 0: your nice custom icons as well.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Again, that's the design OCD part where everything has to be nightly nicely organized. Yeah. The system, we did all module based because, like I said, it was originally just a bunch of stuff that we had built. And we were like, hey.\u003C\u002Fp>\u003Cp>Let's give people one simple way to access it. So I'll just throw some of these out. And then we have our so we've got our library here, where the library has some resources that are, like, downloads for you guys. Hey. What questions should we be asking to qualify customers?\u003C\u002Fp>\u003Cp>So building this out, it was a really super simple indirectus. If I hop into, like, our library data model here, this is just a folder where we put all of our other collections. So we've got our resources, which are just kind of one time offerings, and then we have our courses. So the data structure here, we have a course that sits at the top level. Then each course has several modules, and each module has lessons.\u003C\u002Fp>\u003Cp>So if we open up the course, we can see we've got, hey, is this a free course? What's the status of this? Is it published? Is it draft? What's the title of the course?\u003C\u002Fp>\u003Cp>What are we gonna use for the slug, which is the URL that we're gonna access that at? The description, we've got a featured image. Then we have the modules for that course. That is a one to many relationship that links to the modules. And inside the modules very similar, title, status, what's the course that this module belongs to, what's the description of the module, and the lessons for that module.\u003C\u002Fp>\u003Cp>So the meat and potatoes is probably in the actual lessons. So we've got the module it belongs to, stylus, title, slug, description. And then we have some interesting things like content, which is the WYSIWYG editor in Directus, which is super nice. Other platforms that I've used, Airtable specifically, like, it does not have a WYSIWYG editor. So doing long form content there does not it's just not helpful.\u003C\u002Fp>\u003Cp>We've got a video input. So this is just a string. On the front end, we've got, a function that will clean this up. So you can paste Vimeo, you could do YouTube, you could do Loom videos. Whatever that is, it'll clean that up and generate, like, an embed code.\u003C\u002Fp>\u003Cp>We also have, like, a form schema here. So some of the courses that we are going to do, we've got let's actually just flip over to one of the lessons maybe here.\u003C\u002Fp>\u003Cp>Speaker 1: So you're saying a school or a university could build an entire learning platform modernized indirect us. So, you know, that's kind of what you've built here in one one module of your platform.\u003C\u002Fp>\u003Cp>Speaker 2: Absolutely. Yeah. A 100%. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: We actually have a university.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Could be yeah. School, university, anything, really.\u003C\u002Fp>\u003Cp>Speaker 0: We actually have a an LMS use case for one of our one of the clients that I work with. I don't know if I'm allowed to say their name or not, so I just won't I'll avoid that. But they've built essentially in a very similar schema with the modules, the courses linked. Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. And eventually, I think we'll add, like, some completion percentages and some niceties onto this. But, yeah, you can see here, this is one of our lessons here. And I on the front end, we've also got, like, this profitable sign pricing course has it's almost like a calculator, like a worksheet type of offering. It's not like a quiz per se.\u003C\u002Fp>\u003Cp>Let me just, show it to you guys, and I'll go back to the side by side here. So this is the front end of the application. We've got our main navigation over here, and it's divided into the different modules that we have. This my shop is a new one that has not released yet. So I'm giving you guys the local flavor here that's got the latest updates also so I don't break anything.\u003C\u002Fp>\u003Cp>And then we have, like, a community module, some other things. But if we enroll in our course here. Okay. Alright. So this is what the initial view looks like.\u003C\u002Fp>\u003Cp>You can expand these modules over here. But I'm going to show you the same calculating your shop rate lesson. It looks like I've got the wrong video here, but I'll just go ahead and hide this menu so we can go full screen. So the format here, we've got a nice video that they'll follow along with. We've got a short description.\u003C\u002Fp>\u003Cp>And then this particular one is all about calculating their shop rate. So in custom sign and print you're building a custom order every single time. Your unit of work there is your time. So how much should you charge for your time? Big question for everybody.\u003C\u002Fp>\u003Cp>So here, basically, we give them the ability to go in and enter in their monthly expenses, how many days a week they're working, how many team members they've got, how often those team members are working, and\u003C\u002Fp>\u003Cp>Speaker 1: I don't work 7 hours\u003C\u002Fp>\u003Cp>Speaker 2: a day. And what target profit they want and then it will spit out what their hourly cost is to operate. We got 2 guys working 6 hours a day. We wanna make a 100,000 or 30,000 in profit at the end of it. Shows us their hourly cost, so this is what that what it costs to keep the lights on, and then what they have to charge per hour or per day to actually make those profit numbers.\u003C\u002Fp>\u003Cp>So those are the things that we're putting together inside this platform, and it's all dedicated to helping sign and print shop owners grow and improve their business.\u003C\u002Fp>\u003Cp>Speaker 1: That's awesome.\u003C\u002Fp>\u003Cp>Speaker 0: That that's sweet. And I noticed that the front end takes a lot of visual cues from the back end. Is that intentional? And what technology do you use for the for the front end of the site?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Yeah. I'm glad you picked up on that because Yeah. Yeah. I like, when we started down the road of modules, I was that's a kind of a design challenge.\u003C\u002Fp>\u003Cp>It really is. And Directus has done such a great job with it of, like, how do I show hierarchy? How do I make sure that people know where everything is at? And I kinda took cues from that. Yeah.\u003C\u002Fp>\u003Cp>We used intercom a lot at my previous employer. They've got, like, a similar, hey. Our modules are on the left hand side kind of set up. And I thought that was a great design pattern. But, yeah, a lot of this has been taken cues directly from the back end inside Directus.\u003C\u002Fp>\u003Cp>That's awesome.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. We've even seen people, because of the way Directus is designed on the back end, using the back end as if it is an internal tool for a business, like, using the back end as an actual front end as well. It's just like this is the tool that we log into as employees.\u003C\u002Fp>\u003Cp>Speaker 2: And that's it's easily doable. Like, you can log in and you can white label this. And if you wanted to be a little more stingy about it, you could even turn off the documentation where, like, it didn't have any, like, references to direct us at all. But Right. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: From And then using roles and permissions to just hide everything that wouldn't be relevant to that user or that group of users.\u003C\u002Fp>\u003Cp>Speaker 2: Absolutely. Yeah. And that that was, like, an option that I considered. That would've dramatically cut the time to get something launched, but I like, being design oriented,\u003C\u002Fp>\u003Cp>Speaker 0: I Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: That OCD kicked in. Okay. We're gonna build a custom front end on this.\u003C\u002Fp>\u003Cp>Speaker 0: That looks great.\u003C\u002Fp>\u003Cp>Speaker 1: So And I think that's one of the I think that's one of the beautiful things about Directus is if for any reason in the future you decided to move away from Directus, if your front end was also the back end, you're kinda stuck there. But with your front end being built separately, you can pull that SQL database because as you mentioned, it's just your it's your SQL database. You could pull that information out. All the tables are there. Directus had injected nothing into it, and you could move to another back end.\u003C\u002Fp>\u003Cp>So I I think that some people find the benefit. We we do think Directus can fit long term, but it is nice to sometimes separate those front end things out because if for any reason you needed the SQL database for somewhere else, you could put it somewhere else as well.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I mean, this is my actual SQL database here. Right? Everything Directus is namespace there. Again, like you said, that's this is just the kind of metadata that enables Directus to do its thing.\u003C\u002Fp>\u003Cp>So, yeah, we've got books. We've got all the lessons. Like, they're all there. Directus, none of this is intertwined with the Directus system data, which it which is very nice. Yeah.\u003C\u002Fp>\u003Cp>I like that aspect of it. I don't see moving this from Directus because, like, the speed at which I can put something together is that's the craziest part of this whole thing for me. I'm not being a professional developer, I'm, like, self taught developer. I've mostly played, like, the get it done guy role, is what I like to call it. Every company that I've been in or even on my own stuff, I'm the guy that if I don't know or I if I don't have somebody that'll do it for me or if, you know, there's a problem, like, I'll figure it out however I need to.\u003C\u002Fp>\u003Cp>And that's what led me into development in the first place. But Directus gives me that tool, like, you know, almost almost too easy for me to go down a rabbit hole and build something.\u003C\u002Fp>\u003Cp>Speaker 1: Did you say go down a rabbit hole on purpose?\u003C\u002Fp>\u003Cp>Speaker 2: I didn't. No. But that's the bunny puns could come easily, though. So, you know, this new module that we've been working on is basically like a knowledge base of Oh, awesome. SOPs and standard procedures.\u003C\u002Fp>\u003Cp>Yeah. Like I mentioned, a lot of these small shops are they don't have things like this in place. So we're gonna give them a template for this. K. Here's your employee handbook.\u003C\u002Fp>\u003Cp>Go in and customize the pieces that you need, but we'll get you 80% of the way there. And using Directus, like, building something like this out, is super on the back end, I probably spent the most of the time here to build this feature of like cards and collections. So if I open up one of these cards, here's what the back end of this card looks like. Probably the most time spent was just trying to map out how I wanted the data to look. So here we've got collections that hold the cards, collections could be nested into each other, so coming up with that schema, like on paper was probably the hardest part about it, but as far as actually getting an API that I can use on the front end it probably took me 10, 15 minutes to put together these 2 collections inside Directus and actually start building something on the front end, which is huge.\u003C\u002Fp>\u003Cp>Speaker 0: This is really cool.\u003C\u002Fp>\u003Cp>Speaker 2: If I would have done something previously, I don't have the back end knowledge or skill set. So I would have either been mutts trying to learn that and put that together and maybe something like Laravel or Rails or something that gives you a leg up or been paying somebody 1,000 of dollars a week probably to build something like this. And just like the speed and flexibility that Directus gives me. K, if I've got an idea and my girls are off shopping for the weekend, which is exactly how this feature came about, You know, it's it's easy enough for to build something that's gonna deliver value in in a weekend. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: So so people just need to take more trips away from their family and you could do more projects.\u003C\u002Fp>\u003Cp>Speaker 2: Is that\u003C\u002Fp>\u003Cp>Speaker 0: what you're saying?\u003C\u002Fp>\u003Cp>Speaker 2: I'm not saying that at all. No. If you get a if you get a random weekend where and you have 3 kids and your wife takes them shopping, use that for project time. 100%. Yeah.\u003C\u002Fp>\u003Cp>So seeing\u003C\u002Fp>\u003Cp>Speaker 0: this seeing this knowledge base really gets my gears turning on a lot of different ideas that you could start implementing. And I'm not gonna impose any of those on you, but I'm sure you have a lot of ideas and future plans around this project. Could you maybe Oh, yeah. Fail some of those or other things you plan to add on to the this project with Directus?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I I mean, even yeah. Like, we've got onboarding checklists that are\u003C\u002Fp>\u003Cp>Speaker 0: Oh, wow.\u003C\u002Fp>\u003Cp>Speaker 2: Like a thing that we're putting into this. So one of the big things in any business really, is how do you onboard new employees? And this is specifically generic enough that it doesn't have to be used for onboarding. But, you know, since we're focused on the team aspect, that's the primary use case here. But, you know, we're working on this onboarding checklist structure where, hey, Whenever we hire somebody, we want them to be able to go in and have just a, hey.\u003C\u002Fp>\u003Cp>Here's how your next 6 weeks or your first 6 weeks are gonna go with the company. And not only does that improve morale, but, you know, it it's easy. It improves retention as well. Like, if you start a new company and you've got the 1st 6 weeks laid out for you, here's everything you need to do to be successful in this new role, that's gonna skyrocket retention. So we've got this set up where you can assign the specific user to a task within the checklist in case, like, a manager needs to be assigned to a certain task instead of the new employee.\u003C\u002Fp>\u003Cp>You could set up due dates, all of that, And then you just run the checklist, and we'll have a couple dashboards in here. This is still very much a work in progress, but that's, that's probably the newest thing. We've got tons of different ideas of what we're gonna implement in the future, but, you know, that's kinda what we're working on at the moment.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I can see this specific feature being really useful for a lot of smaller startups or companies who just don't have the time to build this. Do you have any plans to list the setup or at least some sort of instructions in the upcoming marketplace. You could be able to to list this up there for other companies to be able to use this as well. Have you thought about that?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Yeah. I'm totally open to it. I need to learn more about the plans there and how we could make that work.\u003C\u002Fp>\u003Cp>Speaker 1: Or you could just send people use the your platform too.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. No. I for me, I don't see this I don't wanna get this thing outside of sign and print. So this is probably gonna be, like, a really niche thing, but, totally, what we built here, it's like, there would be nothing stopping you from taking this and either generalizing it to any company or, for me, I mean, like, you could take this and and focus on your if you had a specific intersection, and that's kind of my philosophy of where I bring value anyway is at the intersection of sign and print and tech, automation, integration. Hey.\u003C\u002Fp>\u003Cp>Bringing those 2 together is like a space that that I have, bring the most value. So if you're out there and you're listening to this and that's that describes you, you got a passion on the tech side, you've got some industry knowledge, you could take something like Directus and build a tool that will help bring in recurring revenue for you or, honestly, if I were to focus on this full time, it could probably be a full time business that that we could grow and scale. But, yeah, I've got a few other priorities, and I quite like the lifestyle aspect of\u003C\u002Fp>\u003Cp>Speaker 1: it at this point. That's a perfect segue because I was gonna ask you guys as we kinda start to wrap this up, advice for those people who are building platforms or similar platforms. You obviously have access to the Directus team directly as you kind of work with us week by week. But did you research things on GitHub, or did you reach out to people on Discord? Or did was it kind of just playing around?\u003C\u002Fp>\u003Cp>What advice would you give to those, either new or relatively well versed in direct us? What did you run into that you would advise people to sidestep or hop over if we use a budget plan?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I've always been, like, action oriented, and I don't know if that's sometimes that's a fault. A lot of times, I think that's a strength because and I know, like you said, you were getting into development, John. There's a lot of, like, courses out there on here's how to become a full stack developer, here's how to become a Vue JS developer, and it's just module after module after module after module. And I've tried those, and I discovered that it didn't work for me because it it's either a project that we're building that I don't care about or it's just too abstract in the moment.\u003C\u002Fp>\u003Cp>The it's like, I don't know when I'm gonna use this. That's the same reason that I couldn't give you if you gave me a calculus test right now, I couldn't tell you the first answer on any of it because I haven't used any of that stuff in so long even though I learned it. So the first thing that I would say is, hey. If you got a decent idea or one that you think could be a good side project or a business side. Get into Directus.\u003C\u002Fp>\u003Cp>It's simple enough to build out your data model. I've even got if you're looking for a starter on the front end, where is this thing? So our platform is built on Nuxt, which is based on Vue. Js, but if you need a good starter that already has the plumbing connected with Directus, we've got a starter that you can use as well. So you can pull this down, it's just Brian Gillespie slash nuxt3 dashdirectus dash starter on GitHub.\u003C\u002Fp>\u003Cp>It's getting a little popular, I think. Not that 55 stars is anything compared to the 18 or 20,000 at Directus, but this is a good starting point. Like, just set up a couple collections inside Directus, start building something. And then as you run into challenges, the direct to stocks will be your best friend. Shout out to Aaron on that side of things.\u003C\u002Fp>\u003Cp>I've seen a lot of great improvements on the documentation recently. I really love this cookbook section, like some of these things, how to do a content hierarchy. This was kind of one of the things that probably tripped me up when I first started with Directus, some of these more specific things, which I'm sure that's a challenge for you guys as well because Directus us can could do so many different things.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: Like, how do we tell people how to set these things up? And then the community has been great as well. Even even before I was doing the videos on YouTube for DirectUs, if I would run into any gotchas, k, I I don't know how to do this. I think Brain Slug was one of the guys that reached out and helped me before he was working with the team as well. Yeah.\u003C\u002Fp>\u003Cp>Like, initially, I was trying to set this up and, like, tenancy is a big question that came up as well, and it was one of the things that I had trouble navigating of, like, how do I set up not just, like, an account inside here, but each account has many different users. So, how do I manage all of that inside Directus? I know Directus will do it, but how do I figure it out? And the guys in the community, guys and gals, I should say, were very helpful in that regard of just hopping into the discord, which I like a lot more so than, like, a support forum or some of those other it's nice just to hop in and just kinda post a question and see who's already had experience doing this thing.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Shout out to BrainSlug as well. I'll second that. I think his name is the one I see the most in Discord, and I believe he was on vacation a couple months ago, and it got a little quiet in Discord. I'm like, we need brain slug back.\u003C\u002Fp>\u003Cp>So shout out to him.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Yeah. And there's quite a few names that I see popping up time and time again. So one of the other kinda nice things that I really like about Directus here is the insights module, where I've just been able to set up, like, a quick dashboard that shows me our accounts. So this was back in October.\u003C\u002Fp>\u003Cp>That was when we initially launched, and I could see how many accounts are being created each day, the total number of accounts. So we've got a free plan, and we've got a pay plan. I could see how many people have published their shop listing within our community, what our recommended book numbers come from, and this is all community driven stuff. Hey. This is one of my favorite vendors to work with, or this is a book that I recommend reading.\u003C\u002Fp>\u003Cp>But Direct has made this really easy, whereas, again, like, if I wanted to do this, I would either be exporting the data from Directus and doing a pivot table and some charts inside Google Sheets, which would be a\u003C\u002Fp>\u003Cp>Speaker 1: blast. Pivot tables.\u003C\u002Fp>\u003Cp>Speaker 2: Or I would have to build I would have to build the same functionality somewhere into my own app, which would be kind of a pain to do. So Directus makes this super simple for me. And this has this has been helpful to share with the other guys that are working on the podcast and this platform with me just to give them motivation. K. We signed up 3 accounts today, and we've done very little promotion on this thing at all.\u003C\u002Fp>\u003Cp>So\u003C\u002Fp>\u003Cp>Speaker 1: Have you played around with flows at all? I know for me, flows can get pretty technical pretty fast. Yes.\u003C\u002Fp>\u003Cp>Speaker 2: Yes. So we do have several flows going on. One of the cooler features that I really like about the, like, the community module that we have, in addition to like the list view of here's all the member shops that we have where you can browse what their capabilities are. Hey, if I are you UL certified? Which means, hey, can you do electrical work?\u003C\u002Fp>\u003Cp>Or do you have a wide format printer? One of the really nice things here is this map view. Sign and print is locally, it's mostly a regional, local type market. You're not gonna, with the exception of really large signage, if you want a banner or something simple, you're probably going to be dealing with a local person. So this is another thing that I stole from Directus.\u003C\u002Fp>\u003Cp>You guys were using Mapbox on the admin side for geolocation. Previously I'd used Google Maps and I found Mapbox to be a lot easier, but I wouldn't have known about that had it not been through Directus. But being able to, like, filter these based on a map, tremendously helpful. Okay, I'm located in West Virginia, who around me has a bucket truck that can go do an install, so here's a couple different options that I can find. So it's all about collaboration.\u003C\u002Fp>\u003Cp>But on the Directus side, when somebody edits their listing, and it looks like I've managed to break something there, when they edit their listing they add their address information. So on the flow side of things we've got a Flow set up that will send that address to Mapbox and pick up their latitude and longitude geocode data anytime that listing is updated. So if they go in and change the address, that will store their location correctly. See I've got so many different modules I can't even find them. Directory.\u003C\u002Fp>\u003Cp>You need\u003C\u002Fp>\u003Cp>Speaker 1: a little map icon. You're all iconized, you just need a little\u003C\u002Fp>\u003Cp>Speaker 2: map. Yeah. That's what I need. Yeah, so the geolocation data here, it will automatically generate this for us. So that was been a that was a huge, like, one up for using flows, so I didn't have to, like, custom code any of that or do it manually.\u003C\u002Fp>\u003Cp>Some of the other flows that we've got on our Mastermind calls, We've got, let's open that up. So we've got these thumbnail images that show when you go through and browse through these previous calls. That is a flow that is set up whenever the recording is posted for that previous call it will go out to Vimeo and capture the thumbnail image so we could show it here. And I've also got just a nice little anytime a new user account is added, it will ping us inside Slack. So let me see if I can open this up for you guys just to give you a taste.\u003C\u002Fp>\u003Cp>Yeah. So we've got an inbox inside Slack that whenever a new user and a new account is created, that happens through flows as well.\u003C\u002Fp>\u003Cp>Speaker 1: Very cool. I assume you used a little bit of just toying around till it worked, which is what I do. I just kind of change one thing until it works. Probably a little bit of the dots. Is that kinda how you figured some of those out?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. So my new method for flows is basically taking it step by step. Like, I will create the initial flow or, like, the trigger setup, and then I will trigger that flow to make sure to see what data I've got coming out of it, and then I will just kind of march step by step after that. That kinda makes it easier for me to stay focused instead of building, like, 30 steps at once and then trying to figure out why I'm not getting what I want at the end of it. I've also my wife makes fun of me.\u003C\u002Fp>\u003Cp>I've got sausage fingers. So a lot of my trouble with flows is usually just just because of typos that I'm not paying attention to.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I think the I think I was working on a flow on one thing that I'm kind of toying around with here yesterday or the day before. I think I had about 70 iterations where I was just changing 1 step by step like you kinda do, but also why isn't this working? Okay. Let's change this one thing.\u003C\u002Fp>\u003Cp>Oh, let's change this let's change it to another thing. That's my method of figuring out how it works. Because you you kinda mentioned just starting Yeah. I yeah. I think you get\u003C\u002Fp>\u003Cp>Speaker 2: you learn how to\u003C\u002Fp>\u003Cp>Speaker 1: do it.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I yeah. I think you get you learn how to learn at that point. That's probably the biggest skill is, like, you could go through and you could certainly read all the docs or watch all of the YouTube videos that you guys have done or the ones that I have done. But until, like, you actually need that knowledge, like, you may forget some of that.\u003C\u002Fp>\u003Cp>So, like, I really prefer, like, an action oriented approach where you just dive in and try to figure it out, and then there's plenty of resources available when you get stuck.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Correct me if I'm wrong, Pedro. I think even as sometimes when we're filming these from the field episodes that are also on our YouTube, I think sometimes people ask questions and on those phone calls where sometimes those questions are asked, our engineer, Jonathan, will answer them. And so when we have to kind of go back and reanswer them on from the field, we kinda have to play around with it until we figure out how to answer the question. Is it the same for you, Pedro, or is it just me who's, we'll figure it out?\u003C\u002Fp>\u003Cp>Speaker 0: No. Absolutely. I mean, that there's a little bit of decoding there and going back and reviewing things as they're asked on calls and from the community trying to clarify those things. So definitely feel the pain there.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. There's some of that for me as well where, like, I'm trying to distill a feature into a video. So I've I've got to for me, like, the nuts and bolts of it. Like, you've gotta get into it and do it yourself to actually be able to translate it. So I've it's certainly the it has to be the same way for developers using Directus as well.\u003C\u002Fp>\u003Cp>One of the strengths here is obviously this is no code app that anybody can use, not just, like, the technical members of the team, but, you know, I've got my guys on the podcast. They've got access to this. So if they wanna go in and upload a resource or they wanna upload set up a new meeting for the mastermind calls, they can. And it's simple enough for them to do that.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Awesome. Well, Brian, we thank you very much for being a part of the I made this podcast or video series, whatever you wanna call it. And thank you for giving us a deeper look into better sign shop and everything you're doing there and all the work you've done within Directus. I guess maybe to to leave this off, what, the last question would be maybe what would you like to see from Directus that you haven't seen yet?\u003C\u002Fp>\u003Cp>Or is there a feature that you've just been dying to see come to Directus?\u003C\u002Fp>\u003Cp>Speaker 2: That's a loaded question there. You're getting you're getting me into trouble, Pedro. No. Yeah. I've been consistently impressed with everything that Directus has done.\u003C\u002Fp>\u003Cp>And I had this conversation with Wrike, I think, on one of the calls that I was looking for some guidance on. Hey. How do I do this one feature? But I was talking to Wrike, and I was like, man, you're doing the right abstractions. There's that balance that you've gotta strike of, like, hey, flexibility and and just, like, not taking not putting too much magic into it.\u003C\u002Fp>\u003Cp>I as far as what I would like to see, I think Flows could probably use a little more work on the developer experience side, like, making it easier to figure out what's gonna come out the other end of it. It. That that would be a tremendous help as I get more into flows. What else? I and I I think I saw this on maybe one of the roadmap pages, like an Airtable like spreadsheet view layout where I could edit a lot of data at once would be very helpful.\u003C\u002Fp>\u003Cp>I think there is probably what, like real time, like websockets maybe in the future. I don't know that we have a ton of application for that. Maybe in the future, there'll be, like, a BetterSign Shop chat module where these guys could talk to each other through the app.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Create your own little discord.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Yeah. I mean, I'm excited to to play around with that, certainly. I a lot of times now, I feel like a kid with just, like, a a nice set of Legos. Like, I've got the $100 set of LEGOs, not like the cheap $10 set.\u003C\u002Fp>\u003Cp>Like, Directus gives you that nice LEGO set that's got all the pieces where you just build whatever you want out of it.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. Well, again, I'll thank you for your time as well. I will second that flows. All of that you the things that you mentioned, I think, are in the works.\u003C\u002Fp>\u003Cp>But as someone who has already said in this video that flows can get pretty technical pretty quickly, I will second that. We need some one one click easier options for us nontechnical people, but I do agree that it's very powerful in a good and dangerous way for someone like me who can break things. But thank you again for your time, and thanks to everyone who's been watching this. We'll see you on the next one. And if you have any questions, feel free to reach out to us either on YouTube, Discord, via our website.\u003C\u002Fp>\u003Cp>Follow along and subscribe. Also, go and follow Bryant on his social media accounts as well. We'll put those in the link and description. Thanks again for your time and we'll see you on the next one.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Thank you, guys. It's been a pleasure.\u003C\u002Fp>\u003Cp>Speaker 1: And scene.\u003C\u002Fp>","Hello, everyone. Welcome to this episode of I Made This. My name is Pedro. And I'm John. Thanks for joining us today. We also have Bryant here. Bryant, why don't you give us a brief introduction into who you are? Yeah. Absolutely, guys. Thanks for having me. My name is Bryant Gillespie. I am a jack of all trades, I guess you could say. I do a ton of different things. I've got a design background. Been in the sign and print industry for probably too many years at this point. I would say, like, 15, 16 years. Yeah. I've worked in kinda all facets of that industry. Like, I previously ran and helped manage a sign and print shop for probably 5, 6 years. We grew that business, and I burnt myself out and was looking for the next thing. And then I got hooked up with a software company that made software for the sign and print industry. So it was a great fit there, but, yeah, I've got a ton of experience in just a little bit of everything from business management to design, marketing, even a little bit of sales at my former company there for a while. And I've also got this development streak that I'm on at this point. I'm not a professional developer by any means, so let me preface everything that we talk about here. With that, Last 2 or 3 years, I've picked up JavaScript development. Mostly, like, working at a software company, I always wondered, k. How hard is it to do what we're talking about on the product side of things? How challenging is it to build this certain feature? And, yeah, I've got a nasty habit of scratching my own itch that sometimes gets me bogged down into crazy projects like the one we're gonna talk about today. Awesome. And through that software development streak and kinda learning software development, is that how you found Directus, or what was your first touch with Directus? Yeah. I can't remember exactly how I came across Directus. I know hey. Like, the background story there is I used to be really heavy into Airtable, and I think it's a really great tool, great platform, great company. But I had a client that we built a really nice Airtable base. And over the course of 2 or 3 years, this thing just kept getting more and more complex as any project does. And so when we first started, it was just simply, hey. Let's keep track of our orders. And then we started bolting on things like, hey. We wanna be able to send an online proof to our clients and let them approve the artwork for this order, and then, hey. Let's let them pay for the order. And then it graduated into a full blown website where a the client could come on sight unseen and place their own order for decorated apparel. We as we kept iterating on that, things got more and more complex, and there were more moving pieces. And we rapidly started hitting the limits of Airtable, not just, like, what it was built to do. There's some API rate limits there that kind of affect how you call their API and what you can actually do with it. But there's also, I think we were on one of the we weren't on the enterprise plan, and the enterprise plan at the time was probably, like, 3 dollars 1,000 a month, which is kind of out of reach for yeah. We did. Definitely chump change. Yeah. So it's kinda out of reach for a small company that has 15, 20 employees. So that's a lot of money for them to spend on a platform where they were at maybe several $100 a month with all their employees on there and all the platforms and things that we had built around AirDance. That makes sense. Yeah. I think you're on a similar journey, or I guess I'm on a similar journey to you. And if for anyone who's out there watching, Brian's face probably does look familiar because Brian also does videos for Directus. And so as I've come along on this Directus journey and as Pedro and I have started doing some videos, I also have an entrepreneurial spirit inside me. I've started several companies on the side and kind of have ideas that are floating around inside of my head too. And so I've been on this similar sounding journey to you of exploring these other platforms out there. A lot of these platforms are great for what they do, and they all have their limitations as any software does. But, you know, the more I've been creating these videos for Directus and working for Directus and seeing what it can do, one of the nice things as I'm building out these platforms and some of them actually make their way to actual companies, whether they're small or medium sized, some of them fizzle out. One of the great things that I've kinda come to discover about Directus is there there is no limitation, especially if you're starting off in self hosted. You're not gonna hit that $3,000 a month mark right right away. And so it it's so great for a beginner developer begin I have some experience in web development and websites and things like that. But as you're starting to explore, you're not gonna hit any wall in regards to pricing. You can kind of build it out and get a full fledged platform without having to worry about, oh, I can't get the full traffic tested. I can't gonna have to pay $500 a month starting off. I think that's kind of a similar journey that I've come to uncover and have more creative ideas flowing as I've been creating these videos for Directus. So it sounds very similar to kind of the journey that you are ahead of. You've been making these videos longer than we have, but sounds like a very similar journey. Kind of creating you found Directus. You started using it. You started creating videos for it. More ideas come to light. Does that kinda sound right? Yeah. A 100%. And so like I was saying, we were hitting the limits of Airtable, and then it was like, well, what's the next jump? Like, where do we go from here? Hey. This could be a stand alone app that we could have somebody program or develop the back end. And at that stage of the journey, I wasn't comfortable enough to say, hey. This is something I could take on myself. So I started looking at, okay. Are we gonna have a developer or an agency or somebody, like, build this into a full blown app? Because we're running out of runway here on this no code tool like Airtable. And and I'm not knocking those tools at all, but, you know, like you mentioned, there's certain limitations that you run into that you you don't know at the outset. You don't know that you're gonna run into those things or we probably had I known where we were gonna end up with the tool, it probably would not have started where we did with Airtable. Yeah. But Fortunate enough that you grew at that pace. Right? Like, people underestimate their abilities and they grow a company or grow a software or an organization much faster than anticipated. So that's a good thing. Yeah. Absolutely. So we were looking for the next thing and I don't know if it was through Product Hunt or Hacker News or some other group that I like, a random Discord or Slack group or something that I'm a part of. I had stumbled onto the Directus website and I saw I think it it was really close to when version 9 launched, I think. And I was, like, like, what is it? This open data platform. And I saw the screenshots, and I was, like, hey. This kinda looks similar to the other tools that I'm used to, but it looks way more powerful because they're talking about a full SQL database that I own and immediate generated APIs and all the things that I could do through the UI, through the no code app that I'm used to doing in other platforms and more. So Directus did not work out for that particular client. We kind of made a few changes that extended the runway on their existing platform, but I kinda took up the mantle, and I was like, hey. This is I could use this for all my other things, like my own personal projects and get a leg up because now this is actually going to scale. This thing is, like, I'm driving the Ferrari on day 1 instead of effectively driving the Pinto for a little bit and then trying to figure out, hey. Do we slap a turbo on this or get some new wheels or what do we do with it? Yeah. As someone who owns a Ford Fiesta vehicle, I feel I feel slighted that, you know, my car is great. It's a great car, but I would take a Ferrari over it. Yeah. And so I think that's a that's a good lead in because a lot of people you know, I I come I came into Directus just thinking you were somebody who created videos, but you've created a whole platform on top of Directus. So why don't you give us a look into the most recent project that you built on Directus because I think it's really cool. I think it kind of ties into a lot of what Pedro and I have heard on calls with prospects and customers as well. So why don't you give us a little insight into the most recent project that you have? Yeah. Yeah. Absolutely. Give me a second here, guys, and I'll share my screen. BetterSignShop kinda started when I left my previous company. I was the head of customer success there. I managed a team of customer success managers that would implement and train people on how to use our software that was designed for sign and print. So I've got this this bug, shop manager, being the 2nd in command for 5 years and experiencing every facet of the business through that, and then working on the tech side as a software provider, I probably worked with 350, 400 shops directly over the course of 7 years at my previous company. It was one of those things. It's like, hey. I'm it's time for me to move on to the next thing, and I immediately jumped into consulting with sign and print shops. Just that was the background that I had. That's where I've got the most leverage. Why not explore that? So that morphed into a lot of random projects. Like, the best thing to come out of it was probably this platform that I'm gonna show you guys. But we started a podcast, we started a mastermind group where we meet once a month, we bring in like an outside expert that talks about a specific topic. Maybe that is online marketing for sign and print shops or how to hire sales reps because it's traditionally not a it's a it's a small business type of industry. There are some very large printers and very large sign companies in the world, but traditionally it's it's a small mom and pop type industry. Most shops have fewer than 20 employees. So we had all these resources that we're putting together, and we said, hey. Let's build a platform to contain all those. So we had just a bunch of random stuff that we were building, and we were like, hey. How do we connect all the dots? Right? Like, we were doing interviews with industry experts. We had 100 of we had a 150 members inside a private Facebook group. We've got some calculators on, hey, should I buy that piece of equipment, or should we keep outsourcing this service? So we had all these things, and the challenge was, like, what do we do with it? Like, how do we bring it all together? And that's when we decided to create this platform, My Better Sign Shop, to bring it all together. I like that you chose a color scheme that fits with Directus, the purples and pinks and Yeah. Yeah. So how I got into this industry was as a graphic designer. So everything I do, I've got that designer OCD type of thing going on where I always hate working on my own stuff because it's so hard for me to pick something. And when we started this, I was like, like, I'm just gonna go wild with this. Like, we gotta go over the top. So my kids, my 3 daughters are into pink and purple, and I said, let's leverage that, and we'll just own those colors. Hey. If you see pink and purple in this space, like hot pink, that's gonna be us. I like it. Yeah. Why don't you why don't you give us a quick walk through kind of how you built it out in Directus and then maybe even an insight into what did you build the front end? What did you build the actual website with? You know, what what technology Yeah. Yeah. Absolutely. So I the thing about Directus, it was so nice building out the data model. And I've you know, I since I graduated from Airtable, like, I I was kinda used to already, like, how do I map out this data relationally? How do I make sure that I'm not duplicating data that that I could just link using a relationship? So the first piece that we started with was probably like the LMS, our learning management system, because we kind of had this course called Profitable Sign Pricing that I was working on prior to this platform, and we just kind of rolled it into the platform. Platform. So inside Directus, I'm not sure if you guys can see this, I could probably zoom in just a little bit. We started, that was the piece that we started with first. So and maybe while we're See your nice custom icons as well. Yeah. Again, that's the design OCD part where everything has to be nightly nicely organized. Yeah. The system, we did all module based because, like I said, it was originally just a bunch of stuff that we had built. And we were like, hey. Let's give people one simple way to access it. So I'll just throw some of these out. And then we have our so we've got our library here, where the library has some resources that are, like, downloads for you guys. Hey. What questions should we be asking to qualify customers? So building this out, it was a really super simple indirectus. If I hop into, like, our library data model here, this is just a folder where we put all of our other collections. So we've got our resources, which are just kind of one time offerings, and then we have our courses. So the data structure here, we have a course that sits at the top level. Then each course has several modules, and each module has lessons. So if we open up the course, we can see we've got, hey, is this a free course? What's the status of this? Is it published? Is it draft? What's the title of the course? What are we gonna use for the slug, which is the URL that we're gonna access that at? The description, we've got a featured image. Then we have the modules for that course. That is a one to many relationship that links to the modules. And inside the modules very similar, title, status, what's the course that this module belongs to, what's the description of the module, and the lessons for that module. So the meat and potatoes is probably in the actual lessons. So we've got the module it belongs to, stylus, title, slug, description. And then we have some interesting things like content, which is the WYSIWYG editor in Directus, which is super nice. Other platforms that I've used, Airtable specifically, like, it does not have a WYSIWYG editor. So doing long form content there does not it's just not helpful. We've got a video input. So this is just a string. On the front end, we've got, a function that will clean this up. So you can paste Vimeo, you could do YouTube, you could do Loom videos. Whatever that is, it'll clean that up and generate, like, an embed code. We also have, like, a form schema here. So some of the courses that we are going to do, we've got let's actually just flip over to one of the lessons maybe here. So you're saying a school or a university could build an entire learning platform modernized indirect us. So, you know, that's kind of what you've built here in one one module of your platform. Absolutely. Yeah. A 100%. Yeah. Yeah. We actually have a university. Yeah. Could be yeah. School, university, anything, really. We actually have a an LMS use case for one of our one of the clients that I work with. I don't know if I'm allowed to say their name or not, so I just won't I'll avoid that. But they've built essentially in a very similar schema with the modules, the courses linked. Yeah. Yeah. And eventually, I think we'll add, like, some completion percentages and some niceties onto this. But, yeah, you can see here, this is one of our lessons here. And I on the front end, we've also got, like, this profitable sign pricing course has it's almost like a calculator, like a worksheet type of offering. It's not like a quiz per se. Let me just, show it to you guys, and I'll go back to the side by side here. So this is the front end of the application. We've got our main navigation over here, and it's divided into the different modules that we have. This my shop is a new one that has not released yet. So I'm giving you guys the local flavor here that's got the latest updates also so I don't break anything. And then we have, like, a community module, some other things. But if we enroll in our course here. Okay. Alright. So this is what the initial view looks like. You can expand these modules over here. But I'm going to show you the same calculating your shop rate lesson. It looks like I've got the wrong video here, but I'll just go ahead and hide this menu so we can go full screen. So the format here, we've got a nice video that they'll follow along with. We've got a short description. And then this particular one is all about calculating their shop rate. So in custom sign and print you're building a custom order every single time. Your unit of work there is your time. So how much should you charge for your time? Big question for everybody. So here, basically, we give them the ability to go in and enter in their monthly expenses, how many days a week they're working, how many team members they've got, how often those team members are working, and I don't work 7 hours a day. And what target profit they want and then it will spit out what their hourly cost is to operate. We got 2 guys working 6 hours a day. We wanna make a 100,000 or 30,000 in profit at the end of it. Shows us their hourly cost, so this is what that what it costs to keep the lights on, and then what they have to charge per hour or per day to actually make those profit numbers. So those are the things that we're putting together inside this platform, and it's all dedicated to helping sign and print shop owners grow and improve their business. That's awesome. That that's sweet. And I noticed that the front end takes a lot of visual cues from the back end. Is that intentional? And what technology do you use for the for the front end of the site? Yeah. Yeah. I'm glad you picked up on that because Yeah. Yeah. I like, when we started down the road of modules, I was that's a kind of a design challenge. It really is. And Directus has done such a great job with it of, like, how do I show hierarchy? How do I make sure that people know where everything is at? And I kinda took cues from that. Yeah. We used intercom a lot at my previous employer. They've got, like, a similar, hey. Our modules are on the left hand side kind of set up. And I thought that was a great design pattern. But, yeah, a lot of this has been taken cues directly from the back end inside Directus. That's awesome. Yeah. We've even seen people, because of the way Directus is designed on the back end, using the back end as if it is an internal tool for a business, like, using the back end as an actual front end as well. It's just like this is the tool that we log into as employees. And that's it's easily doable. Like, you can log in and you can white label this. And if you wanted to be a little more stingy about it, you could even turn off the documentation where, like, it didn't have any, like, references to direct us at all. But Right. Yeah. From And then using roles and permissions to just hide everything that wouldn't be relevant to that user or that group of users. Absolutely. Yeah. And that that was, like, an option that I considered. That would've dramatically cut the time to get something launched, but I like, being design oriented, I Yeah. That OCD kicked in. Okay. We're gonna build a custom front end on this. That looks great. So And I think that's one of the I think that's one of the beautiful things about Directus is if for any reason in the future you decided to move away from Directus, if your front end was also the back end, you're kinda stuck there. But with your front end being built separately, you can pull that SQL database because as you mentioned, it's just your it's your SQL database. You could pull that information out. All the tables are there. Directus had injected nothing into it, and you could move to another back end. So I I think that some people find the benefit. We we do think Directus can fit long term, but it is nice to sometimes separate those front end things out because if for any reason you needed the SQL database for somewhere else, you could put it somewhere else as well. Yeah. I mean, this is my actual SQL database here. Right? Everything Directus is namespace there. Again, like you said, that's this is just the kind of metadata that enables Directus to do its thing. So, yeah, we've got books. We've got all the lessons. Like, they're all there. Directus, none of this is intertwined with the Directus system data, which it which is very nice. Yeah. I like that aspect of it. I don't see moving this from Directus because, like, the speed at which I can put something together is that's the craziest part of this whole thing for me. I'm not being a professional developer, I'm, like, self taught developer. I've mostly played, like, the get it done guy role, is what I like to call it. Every company that I've been in or even on my own stuff, I'm the guy that if I don't know or I if I don't have somebody that'll do it for me or if, you know, there's a problem, like, I'll figure it out however I need to. And that's what led me into development in the first place. But Directus gives me that tool, like, you know, almost almost too easy for me to go down a rabbit hole and build something. Did you say go down a rabbit hole on purpose? I didn't. No. But that's the bunny puns could come easily, though. So, you know, this new module that we've been working on is basically like a knowledge base of Oh, awesome. SOPs and standard procedures. Yeah. Like I mentioned, a lot of these small shops are they don't have things like this in place. So we're gonna give them a template for this. K. Here's your employee handbook. Go in and customize the pieces that you need, but we'll get you 80% of the way there. And using Directus, like, building something like this out, is super on the back end, I probably spent the most of the time here to build this feature of like cards and collections. So if I open up one of these cards, here's what the back end of this card looks like. Probably the most time spent was just trying to map out how I wanted the data to look. So here we've got collections that hold the cards, collections could be nested into each other, so coming up with that schema, like on paper was probably the hardest part about it, but as far as actually getting an API that I can use on the front end it probably took me 10, 15 minutes to put together these 2 collections inside Directus and actually start building something on the front end, which is huge. This is really cool. If I would have done something previously, I don't have the back end knowledge or skill set. So I would have either been mutts trying to learn that and put that together and maybe something like Laravel or Rails or something that gives you a leg up or been paying somebody 1,000 of dollars a week probably to build something like this. And just like the speed and flexibility that Directus gives me. K, if I've got an idea and my girls are off shopping for the weekend, which is exactly how this feature came about, You know, it's it's easy enough for to build something that's gonna deliver value in in a weekend. Yeah. So so people just need to take more trips away from their family and you could do more projects. Is that what you're saying? I'm not saying that at all. No. If you get a if you get a random weekend where and you have 3 kids and your wife takes them shopping, use that for project time. 100%. Yeah. So seeing this seeing this knowledge base really gets my gears turning on a lot of different ideas that you could start implementing. And I'm not gonna impose any of those on you, but I'm sure you have a lot of ideas and future plans around this project. Could you maybe Oh, yeah. Fail some of those or other things you plan to add on to the this project with Directus? Yeah. I I mean, even yeah. Like, we've got onboarding checklists that are Oh, wow. Like a thing that we're putting into this. So one of the big things in any business really, is how do you onboard new employees? And this is specifically generic enough that it doesn't have to be used for onboarding. But, you know, since we're focused on the team aspect, that's the primary use case here. But, you know, we're working on this onboarding checklist structure where, hey, Whenever we hire somebody, we want them to be able to go in and have just a, hey. Here's how your next 6 weeks or your first 6 weeks are gonna go with the company. And not only does that improve morale, but, you know, it it's easy. It improves retention as well. Like, if you start a new company and you've got the 1st 6 weeks laid out for you, here's everything you need to do to be successful in this new role, that's gonna skyrocket retention. So we've got this set up where you can assign the specific user to a task within the checklist in case, like, a manager needs to be assigned to a certain task instead of the new employee. You could set up due dates, all of that, And then you just run the checklist, and we'll have a couple dashboards in here. This is still very much a work in progress, but that's, that's probably the newest thing. We've got tons of different ideas of what we're gonna implement in the future, but, you know, that's kinda what we're working on at the moment. Yeah. I can see this specific feature being really useful for a lot of smaller startups or companies who just don't have the time to build this. Do you have any plans to list the setup or at least some sort of instructions in the upcoming marketplace. You could be able to to list this up there for other companies to be able to use this as well. Have you thought about that? Yeah. Yeah. I'm totally open to it. I need to learn more about the plans there and how we could make that work. Or you could just send people use the your platform too. Yeah. No. I for me, I don't see this I don't wanna get this thing outside of sign and print. So this is probably gonna be, like, a really niche thing, but, totally, what we built here, it's like, there would be nothing stopping you from taking this and either generalizing it to any company or, for me, I mean, like, you could take this and and focus on your if you had a specific intersection, and that's kind of my philosophy of where I bring value anyway is at the intersection of sign and print and tech, automation, integration. Hey. Bringing those 2 together is like a space that that I have, bring the most value. So if you're out there and you're listening to this and that's that describes you, you got a passion on the tech side, you've got some industry knowledge, you could take something like Directus and build a tool that will help bring in recurring revenue for you or, honestly, if I were to focus on this full time, it could probably be a full time business that that we could grow and scale. But, yeah, I've got a few other priorities, and I quite like the lifestyle aspect of it at this point. That's a perfect segue because I was gonna ask you guys as we kinda start to wrap this up, advice for those people who are building platforms or similar platforms. You obviously have access to the Directus team directly as you kind of work with us week by week. But did you research things on GitHub, or did you reach out to people on Discord? Or did was it kind of just playing around? What advice would you give to those, either new or relatively well versed in direct us? What did you run into that you would advise people to sidestep or hop over if we use a budget plan? Yeah. I've always been, like, action oriented, and I don't know if that's sometimes that's a fault. A lot of times, I think that's a strength because and I know, like you said, you were getting into development, John. There's a lot of, like, courses out there on here's how to become a full stack developer, here's how to become a Vue JS developer, and it's just module after module after module after module. And I've tried those, and I discovered that it didn't work for me because it it's either a project that we're building that I don't care about or it's just too abstract in the moment. The it's like, I don't know when I'm gonna use this. That's the same reason that I couldn't give you if you gave me a calculus test right now, I couldn't tell you the first answer on any of it because I haven't used any of that stuff in so long even though I learned it. So the first thing that I would say is, hey. If you got a decent idea or one that you think could be a good side project or a business side. Get into Directus. It's simple enough to build out your data model. I've even got if you're looking for a starter on the front end, where is this thing? So our platform is built on Nuxt, which is based on Vue. Js, but if you need a good starter that already has the plumbing connected with Directus, we've got a starter that you can use as well. So you can pull this down, it's just Brian Gillespie slash nuxt3 dashdirectus dash starter on GitHub. It's getting a little popular, I think. Not that 55 stars is anything compared to the 18 or 20,000 at Directus, but this is a good starting point. Like, just set up a couple collections inside Directus, start building something. And then as you run into challenges, the direct to stocks will be your best friend. Shout out to Aaron on that side of things. I've seen a lot of great improvements on the documentation recently. I really love this cookbook section, like some of these things, how to do a content hierarchy. This was kind of one of the things that probably tripped me up when I first started with Directus, some of these more specific things, which I'm sure that's a challenge for you guys as well because Directus us can could do so many different things. Yeah. Like, how do we tell people how to set these things up? And then the community has been great as well. Even even before I was doing the videos on YouTube for DirectUs, if I would run into any gotchas, k, I I don't know how to do this. I think Brain Slug was one of the guys that reached out and helped me before he was working with the team as well. Yeah. Like, initially, I was trying to set this up and, like, tenancy is a big question that came up as well, and it was one of the things that I had trouble navigating of, like, how do I set up not just, like, an account inside here, but each account has many different users. So, how do I manage all of that inside Directus? I know Directus will do it, but how do I figure it out? And the guys in the community, guys and gals, I should say, were very helpful in that regard of just hopping into the discord, which I like a lot more so than, like, a support forum or some of those other it's nice just to hop in and just kinda post a question and see who's already had experience doing this thing. Yeah. Shout out to BrainSlug as well. I'll second that. I think his name is the one I see the most in Discord, and I believe he was on vacation a couple months ago, and it got a little quiet in Discord. I'm like, we need brain slug back. So shout out to him. Yeah. Yeah. And there's quite a few names that I see popping up time and time again. So one of the other kinda nice things that I really like about Directus here is the insights module, where I've just been able to set up, like, a quick dashboard that shows me our accounts. So this was back in October. That was when we initially launched, and I could see how many accounts are being created each day, the total number of accounts. So we've got a free plan, and we've got a pay plan. I could see how many people have published their shop listing within our community, what our recommended book numbers come from, and this is all community driven stuff. Hey. This is one of my favorite vendors to work with, or this is a book that I recommend reading. But Direct has made this really easy, whereas, again, like, if I wanted to do this, I would either be exporting the data from Directus and doing a pivot table and some charts inside Google Sheets, which would be a blast. Pivot tables. Or I would have to build I would have to build the same functionality somewhere into my own app, which would be kind of a pain to do. So Directus makes this super simple for me. And this has this has been helpful to share with the other guys that are working on the podcast and this platform with me just to give them motivation. K. We signed up 3 accounts today, and we've done very little promotion on this thing at all. So Have you played around with flows at all? I know for me, flows can get pretty technical pretty fast. Yes. Yes. So we do have several flows going on. One of the cooler features that I really like about the, like, the community module that we have, in addition to like the list view of here's all the member shops that we have where you can browse what their capabilities are. Hey, if I are you UL certified? Which means, hey, can you do electrical work? Or do you have a wide format printer? One of the really nice things here is this map view. Sign and print is locally, it's mostly a regional, local type market. You're not gonna, with the exception of really large signage, if you want a banner or something simple, you're probably going to be dealing with a local person. So this is another thing that I stole from Directus. You guys were using Mapbox on the admin side for geolocation. Previously I'd used Google Maps and I found Mapbox to be a lot easier, but I wouldn't have known about that had it not been through Directus. But being able to, like, filter these based on a map, tremendously helpful. Okay, I'm located in West Virginia, who around me has a bucket truck that can go do an install, so here's a couple different options that I can find. So it's all about collaboration. But on the Directus side, when somebody edits their listing, and it looks like I've managed to break something there, when they edit their listing they add their address information. So on the flow side of things we've got a Flow set up that will send that address to Mapbox and pick up their latitude and longitude geocode data anytime that listing is updated. So if they go in and change the address, that will store their location correctly. See I've got so many different modules I can't even find them. Directory. You need a little map icon. You're all iconized, you just need a little map. Yeah. That's what I need. Yeah, so the geolocation data here, it will automatically generate this for us. So that was been a that was a huge, like, one up for using flows, so I didn't have to, like, custom code any of that or do it manually. Some of the other flows that we've got on our Mastermind calls, We've got, let's open that up. So we've got these thumbnail images that show when you go through and browse through these previous calls. That is a flow that is set up whenever the recording is posted for that previous call it will go out to Vimeo and capture the thumbnail image so we could show it here. And I've also got just a nice little anytime a new user account is added, it will ping us inside Slack. So let me see if I can open this up for you guys just to give you a taste. Yeah. So we've got an inbox inside Slack that whenever a new user and a new account is created, that happens through flows as well. Very cool. I assume you used a little bit of just toying around till it worked, which is what I do. I just kind of change one thing until it works. Probably a little bit of the dots. Is that kinda how you figured some of those out? Yeah. So my new method for flows is basically taking it step by step. Like, I will create the initial flow or, like, the trigger setup, and then I will trigger that flow to make sure to see what data I've got coming out of it, and then I will just kind of march step by step after that. That kinda makes it easier for me to stay focused instead of building, like, 30 steps at once and then trying to figure out why I'm not getting what I want at the end of it. I've also my wife makes fun of me. I've got sausage fingers. So a lot of my trouble with flows is usually just just because of typos that I'm not paying attention to. Yeah. I think the I think I was working on a flow on one thing that I'm kind of toying around with here yesterday or the day before. I think I had about 70 iterations where I was just changing 1 step by step like you kinda do, but also why isn't this working? Okay. Let's change this one thing. Oh, let's change this let's change it to another thing. That's my method of figuring out how it works. Because you you kinda mentioned just starting Yeah. I yeah. I think you get you learn how to do it. Yeah. I yeah. I think you get you learn how to learn at that point. That's probably the biggest skill is, like, you could go through and you could certainly read all the docs or watch all of the YouTube videos that you guys have done or the ones that I have done. But until, like, you actually need that knowledge, like, you may forget some of that. So, like, I really prefer, like, an action oriented approach where you just dive in and try to figure it out, and then there's plenty of resources available when you get stuck. Yeah. Correct me if I'm wrong, Pedro. I think even as sometimes when we're filming these from the field episodes that are also on our YouTube, I think sometimes people ask questions and on those phone calls where sometimes those questions are asked, our engineer, Jonathan, will answer them. And so when we have to kind of go back and reanswer them on from the field, we kinda have to play around with it until we figure out how to answer the question. Is it the same for you, Pedro, or is it just me who's, we'll figure it out? No. Absolutely. I mean, that there's a little bit of decoding there and going back and reviewing things as they're asked on calls and from the community trying to clarify those things. So definitely feel the pain there. Yeah. There's some of that for me as well where, like, I'm trying to distill a feature into a video. So I've I've got to for me, like, the nuts and bolts of it. Like, you've gotta get into it and do it yourself to actually be able to translate it. So I've it's certainly the it has to be the same way for developers using Directus as well. One of the strengths here is obviously this is no code app that anybody can use, not just, like, the technical members of the team, but, you know, I've got my guys on the podcast. They've got access to this. So if they wanna go in and upload a resource or they wanna upload set up a new meeting for the mastermind calls, they can. And it's simple enough for them to do that. Alright. Awesome. Well, Brian, we thank you very much for being a part of the I made this podcast or video series, whatever you wanna call it. And thank you for giving us a deeper look into better sign shop and everything you're doing there and all the work you've done within Directus. I guess maybe to to leave this off, what, the last question would be maybe what would you like to see from Directus that you haven't seen yet? Or is there a feature that you've just been dying to see come to Directus? That's a loaded question there. You're getting you're getting me into trouble, Pedro. No. Yeah. I've been consistently impressed with everything that Directus has done. And I had this conversation with Wrike, I think, on one of the calls that I was looking for some guidance on. Hey. How do I do this one feature? But I was talking to Wrike, and I was like, man, you're doing the right abstractions. There's that balance that you've gotta strike of, like, hey, flexibility and and just, like, not taking not putting too much magic into it. I as far as what I would like to see, I think Flows could probably use a little more work on the developer experience side, like, making it easier to figure out what's gonna come out the other end of it. It. That that would be a tremendous help as I get more into flows. What else? I and I I think I saw this on maybe one of the roadmap pages, like an Airtable like spreadsheet view layout where I could edit a lot of data at once would be very helpful. I think there is probably what, like real time, like websockets maybe in the future. I don't know that we have a ton of application for that. Maybe in the future, there'll be, like, a BetterSign Shop chat module where these guys could talk to each other through the app. Yeah. Create your own little discord. Yeah. Yeah. I mean, I'm excited to to play around with that, certainly. I a lot of times now, I feel like a kid with just, like, a a nice set of Legos. Like, I've got the $100 set of LEGOs, not like the cheap $10 set. Like, Directus gives you that nice LEGO set that's got all the pieces where you just build whatever you want out of it. Yeah. Yeah. Well, again, I'll thank you for your time as well. I will second that flows. All of that you the things that you mentioned, I think, are in the works. But as someone who has already said in this video that flows can get pretty technical pretty quickly, I will second that. We need some one one click easier options for us nontechnical people, but I do agree that it's very powerful in a good and dangerous way for someone like me who can break things. But thank you again for your time, and thanks to everyone who's been watching this. We'll see you on the next one. And if you have any questions, feel free to reach out to us either on YouTube, Discord, via our website. Follow along and subscribe. Also, go and follow Bryant on his social media accounts as well. We'll put those in the link and description. Thanks again for your time and we'll see you on the next one. Yeah. Thank you, guys. It's been a pleasure. And scene.",[6103,6104,6105],"963ba582-ba10-4576-9b97-a18be8678315","1505448d-c07d-481e-93b9-3695461ce338","02810029-b6f5-4803-a85f-cd995d04d139",[],{"year":2380,"number":5101,"id":5102,"episodes":6108,"show":6109},[5104,5105,5106,5078],{"title":5108,"slug":5109},{"id":5104,"slug":6111,"vimeo_id":6112,"description":6113,"tile":6114,"length":588,"resources":6115,"people":6119,"episode_number":13,"published":6125,"title":6126,"video_transcript_html":6127,"video_transcript_text":6128,"content":12,"seo":12,"status":19,"episode_people":6129,"recommendations":6133,"season":6134},"sports-betting-platform-tdsoft","894102741","John and Pedro are joined by guest Dariusz Tarczyński (CEO and Founder of TDSOFT). He and his team are building some innovative applications in the sports betting space.","cc9c59d3-9503-4366-8dab-d03e7e481e8e",[6116],{"name":6117,"url":6118},"TDSoft","https:\u002F\u002Fwww.tdsoft.com\u002F",[6120,6123,6124],{"name":6121,"url":6122},"Dariusz Tarczyński","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fdariusztarczynski\u002F",{"name":4361,"url":4362},{"name":4524,"url":6097},"2023-03-01","Building a Sports Betting Platform with TDSOFT","\u003Cp>Speaker 0: Alright. Thanks, everyone. We have Darius here from TD Soft. Darius, thanks so much for your time today. Why don't you tell the audience a little bit about yourself and a little bit about TD Soft?\u003C\u002Fp>\u003Cp>Speaker 1: Hey, hey guys. Thanks for having me here. My name is Darius and I run a company in Central Europe and Poland called TDSoft. We built innovative software products for the customers, but also for ourselves. One of the projects we can discuss today is the one that we did for ourselves, which is the better signals.com.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So why don't you, why don't you give a little bit of background into Better Signals? And, also, in the same time, why don't you let us know, as you were starting to build these products, what led you to Directus? What kind of features in Directus kind of made you choose that? And then go into what BetterSignals is in your experience kind of building that out.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So actually starting from the, the Directus as I think this one came first in the company. Actually, we we started to look at this, product, back then it was in PHP, I think. I believe it was a couple of years back. Mhmm.\u003C\u002Fp>\u003Cp>So it was on our radar. You know, the migration to JavaScript based, you know, engine, was definitely the thing that allowed us to move move forward with it. In addition, some of the developers, actually, it was, you know, It started from a couple of points at the same time. Well, first was obviously me, but then I was looking for a technologies that would allow me to start up projects quicker, you know, go to business logic as quickly as possible. You know, we want to be innovative, deliver innovative solutions, and there is no time to waste.\u003C\u002Fp>\u003Cp>The, well, also, some of our developers started noticing directives from, articles, related to Jamstack. So it's kind of polar motive in here. Directors fits in quite nicely, and we were able to leverage, the features of directors in the better signals, project. It allowed us to cut the development time quite a lot. I I I was surprised how quickly we were able to deliver that.\u003C\u002Fp>\u003Cp>Not only this, but we were able to deliver it in a way that was, very, very scalable. So, I mean, we just started a couple months ago. The traffic is still growing, but we're ready for 100 100 times more traffic. And we are not afraid of the costs, as how we set this up. Directors allowed us to really speed up the process.\u003C\u002Fp>\u003Cp>Maybe, I can tell you a little bit about how we structured the project of Better Signals with Directus. Yeah.\u003C\u002Fp>\u003Cp>Speaker 2: I'd love to learn more a little bit more about Better Signals, and that use case, you know, what you use direct us for. Exactly. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Okay. So just, and make the photo for better signals. We have a lot experience in the sports industry and the sports betting industry and in the talent industry. We work with sports betting companies, also affiliate companies, therefore, you know, natural idea of better signals, and, the idea of better signals, started. Better signals with, a new take on how we, provide sports tips, because we leverage the new media a lot.\u003C\u002Fp>\u003Cp>I mean, the deep directors is the content hub that, you know, we push content through, not only on the front end, but also we leverage the, and the flows to and the automations, to automatically publish posts on different media platforms, like Instagram, Twitter, Instagram, and so forth. So, the, the, the whole concept is about delivering quality tips to people in a new ways that are more, you know, attainable with modern market, modern user basis. Got it.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yeah. And you and you mentioned the front end, you know, gathering content from the front end and the back end as well. What did you integrate with Directus? Because some people do use Directus as their actual interface for customers to come on to whether they're building an app or whatnot.\u003C\u002Fp>\u003Cp>But most people use, a different software for the front end. So what did you use to integrate Directus with for the front end, and how was that interaction between Directus and the front end? Was it easy? Was it easy to set up and easy to update? Give us a little bit of information about that.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yes. So in line with, with ReactOS, we we used, a RemX technology that we were really looking at right now because, RemX has this approach of edge computing that, that we really need here. That allows us also to reduce the potential cost of the, you know, upkeep of the project. So we we manage the API of the Lexus.\u003C\u002Fp>\u003Cp>We use the JavaScript SDK with with Remix, which means that we use it mostly for the back end, but also, you know, we have professional tipsters that provide the content to to our platform, to CMS they get. So there is a kind of customer who is using the the platform, which is our, content providers. So so it's, at the end of the day, Directus is a content hub, a content pipeline that runs everything from the backend. Got it. Got it.\u003C\u002Fp>\u003Cp>Speaker 2: And I heard that, you know, while you were building this out, you get a little bit of help or assistance, at least in the project from, from one of our own employees, Roger. Is that how did you find him and, how did that discussion go and what'd you learn here?\u003C\u002Fp>\u003Cp>Speaker 1: It's actually quite funny because, I I didn't knew that, he works for you guys. Thank you for that. But, you know, he was the author of the, the, The, the repository, the starter repository that uses, dynamics, and direct card same time. And, had a couple of, issues, regarding the setup. You know, I'm also tinkering with this software a little bit on myself because I have to have the knowledge of how to build stuff quickly.\u003C\u002Fp>\u003Cp>And so I have reached out to Kim, over Discord channel, and he replied to me quite quickly. Oh, amazing. Only only later, I found out he actually works for it guys.\u003C\u002Fp>\u003Cp>Speaker 2: Did he work for us at the time? Or was that I\u003C\u002Fp>\u003Cp>Speaker 0: don't I don't think he worked for us at the time. I think He should. I think we saw how awesome this yeah. He's been putting out stuff around Dorekas for quite a while, and I think we we snagged him because his stuff is so it helps people, including himself, build tools quickly. He I think he's released and we can put this in the description, by the way, of the video.\u003C\u002Fp>\u003Cp>I think he's released several games that he said he built in 20 or 30 minutes. You know? So they're pretty simple games, but they're kinda fun to play around with. So, for those of you out there who do build things and kinda use Directus, maybe we'll snag you one day. But yeah.\u003C\u002Fp>\u003Cp>So I've I've I've followed along with you, Darius, because obviously we've had discussions before this and you and I have built up a little bit of a working relationship. What other additional product projects do you kind of have in mind to use Directus with or, or, you know, do you see Directus accelerating future projects in general for you guys?\u003C\u002Fp>\u003Cp>Speaker 1: Yes. Definitely. Actually, we are in the middle of building another project, like, this is the project made for the customer. So as a service industry this time, but also sports related. We are building, a really innovative content pipeline, that allow that will allow us to provide, high quality content for multiple websites from one back end only, and actually really, really cuts the costs of, you know, proprietary content, you know, the editorial, before it's getting lost all over the world.\u003C\u002Fp>\u003Cp>And Directus allows us to actually deliver that within the times, times, and the costs, that was that was not before. Basically, also, what's really interesting about how we can, build stuff with directors is that we can prototype a lot because the cost of prototype is very, very small. With that, can provide the model and structure to the customer, suggest how we will do this feed get the gather the feedback on the live, product from him, and actually modify things, ease without leveraging, you know, tons of documentation, beforehand or multiple hours, long calls. Just, just to try to agree on something that we can't build yet. This time with directors, we can actually first build, a quick prototype showcase to the customer, you know, see his feedback, which is really helpful.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. That's awesome. You know, throughout this conversation, obviously, we've, we've talked a lot about how amazing Directus is and and everything we can do. But I'm thinking, like, you know, in the future, obviously, we don't do everything today. So what are, are there some features or, you know, capabilities that you'd like to see out of direct us maybe in future versions or, you something you'd like to see us build within the platform?\u003C\u002Fp>\u003Cp>Obviously it's also custom, extensible to, to any, to some degree, but, what would you like to see coming in the future as as we progress?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Obviously, we we could come up with a Snyder list. I say now with every product. And I actually got, before this call, I got feedback from developers. Maybe the, this podcast doesn't have enough space to, to, to talk about very, very deep technical details.\u003C\u002Fp>\u003Cp>There's a couple of points, that, maybe I'll tell you. My story where I seem to get stopped, in one or two, things that would be really, really amazing to have solved. First thing is, If I would want to create a product, with custom front end using JavaScript SDK with single sign on, I have to implement that by myself, the single sign on, and then find a way to authenticate the user. So, single sign on way to authenticate instead of only login password for, you know, SDK JavaScript SDK would be something that, I would devote for. Got it.\u003C\u002Fp>\u003Cp>Okay. That that wasn't on the list from my developers. So,\u003C\u002Fp>\u003Cp>Speaker 2: Is that already on on GitHub as as a request?\u003C\u002Fp>\u003Cp>Speaker 1: Yes. And there's some discussion pending because it's not that easy.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Yeah. We'll start to upload it now then. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: We'll we'll link it as well so everyone can upload it for you.\u003C\u002Fp>\u003Cp>Speaker 1: There you go. Well, yeah. So if you want to create a very, very quick, application that allows you to quickly single sign login, do something, you know, showcase your work and log out, you know, that kind of simple stuff that might be a base of future SaaS product, you know, MVP. This is a little bit harder. We have to register properly and have login in the password or create SSO to something else, for example, you know, create accounts somewhere else to have single sign on capabilities and then only use the API.\u003C\u002Fp>\u003Cp>So that's one thing. There is a couple things in the, CMS, but I have to say that, the speed of, development in Directus is really encouraging. I mean, we are seeing a lot, a lot of post merged. So for example, I don't feel like I have to push forward and ask for more because I know that there is a lot of stuff happening right now. So, I can be patient enough, in this way for a couple of things to to be resolved, but on the general perspective, you know, the, the benefits, drastically outweigh the issues.\u003C\u002Fp>\u003Cp>I mean, as I mentioned to you, we can build a model very, very quickly, have an API ready, instantly create a quick front type of front end prototype, and also give the CMS to the customer within days. Awesome.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. Well, we'll definitely take that, that feedback to the team as well. See how fast we can get that implemented for you. I know it's difficult though. I've been following that one for a bit.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. No expectations. So I I I I I I I'm sorry. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Well, hopefully our developers are watching and encouraged. We are moving towards you know, we do put a lot of releases out there, a lot of pools or PRs that are done quickly, and we're kinda moving towards this continual release motion where we're just continually releasing new features, bug fixes, extensions, whatever that may be. I think part of your last answer kind of, answered some an additional question I was gonna have, some advice for other users, which also, you know, it's easy to develop. And is is there any sort of other hints that you would give users who are looking to either start using Directus or are using Directus and building a similar application? Any, you know, one or two really good tips that you've learned either the hard or easy way that you would give to other users?\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm. Yeah, for sure. I mean, first of all, try to leverage the, the cloud version, the free cloud version that you have. It's it's good enough. You you can you can start with that.\u003C\u002Fp>\u003Cp>You don't have to, you know, try to host things by yourself from the day 1. Secondly, we had an, ability to work in a team of, 5 developers, on better signals.com. That we noticed that it's really good to give them some space to actually track things out and see their platform capabilities before they're not jumping into work, because there are some trial cuts they they could take. So actually in, in terms of the recommendation, just, you know, explore the platform by yourself, you know, see the breadth of this before you go dive into some, you know, specifics. That that's something that I can, I can really, advise here?\u003C\u002Fp>\u003Cp>And yes, leverage the pre cloud version. It it has enough capabilities.\u003C\u002Fp>\u003Cp>Speaker 2: So Darius, before the call, you were mentioning, really cool flow that you built inside of Directus. I'd love to to see that if we can show that off.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. For sure. Let me just show my screen for you, and then cool. And the cool thing about Slows is that in this product, they allow us to really streamline their process of content delivered to different platforms.\u003C\u002Fp>\u003Cp>We use additional automation software to to continue deliver the, the content. But as you can see, it allows us to, show the the processes in a very pleased way. For example, when we when someone from the editors, editorial team creates a new signal, then we get notified. So we can, for example, quality test that and approve this, without having to check the platform every time. We can also, for example, publish every scheduled signal.\u003C\u002Fp>\u003Cp>So, you know, scheduling is also part of this, flow.\u003C\u002Fp>\u003Cp>Speaker 0: I also appreciate how it's in dark mode. I myself am a dark mode person, so\u003C\u002Fp>\u003Cp>Speaker 1: I'm a fan\u003C\u002Fp>\u003Cp>Speaker 0: of it. I love the contrast.\u003C\u002Fp>\u003Cp>Speaker 2: Love the icons too.\u003C\u002Fp>\u003Cp>Speaker 1: You know, it's all about developer experience, right? We invest a lot in developer experience, further experience. So as you can, I can show that you guys, the, the really cool thing is that I show you the one that will be more complex? Yeah. So I could it's really clear.\u003C\u002Fp>\u003Cp>I mean, even there is a a blog that says hack away, tag or something. It's too clear as we are doing something here for Yeah. Everyone who is not part of the project yet, it's very, very easy to actually understand what's happening there. So the the concept of flows and the visual presentation of flows in this platform is, really, probably best in class.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. Wow. We're gonna we're gonna sniff that and and quote that in the future as well. Yeah. So I do know in the flows, you are running, running one with JavaScript in line.\u003C\u002Fp>\u003Cp>That's a new feature. What's your experience been? Was it hard to learn flows? We're putting out more documentation around that all the time. I think we recently announced or released a flows cookbook.\u003C\u002Fp>\u003Cp>So what's your experience been building Flows? Because this is a feature that as Pedro and I talk to other prospects and users, they really start to understand the power of it. You know, obviously, visually, it's easy, but building it out once you learned it, is it extremely easy to build new flows?\u003C\u002Fp>\u003Cp>Speaker 1: Yes. So it it needs a minute or 2 to, you know, get accustomed with us, and fully understand their power. So I I don't have much advice here except that maybe, just look at the documentation. I know that documentation is still being built for that. I don't think if I have anything, you know, interesting to say.\u003C\u002Fp>\u003Cp>Speaker 0: No, it's good. It's it's, it's great to get a look at it.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And, also I can show you our log screen, you know. Oh, yeah. Nice. One of one of the things that are really cool about Directus is, you know, the theming option and being able to, also showcase to the customer the c CMS that he ordered that has his own brand and colors and style.\u003C\u002Fp>\u003Cp>Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I think that's that's what really surprises me. It's just people, you know, don't take advantage as much as they could with the the theming. You know, we're we're building out a theming option where it just makes it even easier to change colors and whatnot. But even putting your logo up there, the whole the whole platform itself is white labelable, so that's awesome to see that you've done that.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. So brand, consistency and brand awareness is from the 1st day in the Battery Signals comp projects. So it was in a conscious decision to ensure that everything we do is, you know, following the the brand guidelines here, and it looks really nice. Yeah.\u003C\u002Fp>\u003Cp>I can also show you while you're on the call. I could also show you that we have just created quite simple, you know, data model that allows us to, basically deliver content tips, but not only. For example, creating, you know, a static page. It's just that, that simple model here.\u003C\u002Fp>\u003Cp>Speaker 2: I like that. I like\u003C\u002Fp>\u003Cp>Speaker 0: that WYSIWYG editor with a 1,000\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. And there are these are our tips as you can see. And those are being managed by, by professional, content providers, you know, sports enthusiasts, people who have a lot of knowledge in sports who deliver those insights to us. And then we streamline those into our various media platforms. Got it.\u003C\u002Fp>\u003Cp>Speaker 0: Awesome. And then this this is where, you know, people put in these tips and whatnot, and then the flows kind of, you know, work it through your process. Correct?\u003C\u002Fp>\u003Cp>Speaker 1: Exactly. And then they end up here. Got it.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Perfect. Well, since I've been following along and we've been interacting again for a little while, I'm excited to hear what the plan is for, you know, the next 6 to 12 months, maybe even further out if you wanna discuss it. But what are some things that you guys at TD Soft are planning to build and release?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah, sometimes are huge, obviously. In our company, we bought at least 20% of our income into r and d, which means internal product development because we have to level on ourselves, you know, on our own issues before we actually propose to customers on solution. We have full, planning for the whole next year for the better signals dotcom. We are just launching. I I think it'll be it might be even live once the podcast is live, though, or gamify.com platform, which is a business gamification platform for our companies.\u003C\u002Fp>\u003Cp>We use that on ourselves, and our whole team is working together towards achieving their own personal goals, personal development, and, watching their position on either boats and also buying some stuff from the, company store. I hope that other people will find it best in those. So, maybe I can suggest, interested visitor store visit our website once in a while to see the news as we always have something news in our pipeline.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I know that I'm Pedro and I are a little competitive amongst each other as well as with other employees here. So we will definitely be checking that out because any way that we can officially brag over the other person, I think we're, we're, we're all for that. So\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. The most important thing about, you know, gamification is to be over that other person. It is.\u003C\u002Fp>\u003Cp>Speaker 2: It's helped you competition is great for work.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. I mean, we know I can show you our library. It's small, but, you know, we treat self development seriously here, which means that it's not only about tech, but also about personal development and also, you know, Healthy lifestyle, all the things that come together to actually productive life and fulfilling life and not only man, just work. And I hope the same for others.\u003C\u002Fp>\u003Cp>That's why we published this stuff.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I think that's where, you know, you guys developing, putting 20% of your time to in in revenue towards research and development. I think that's kind of the model that a lot of technology companies use. And, for example, I mean, Google itself has developed so many products just from the free time or the time that they develop or they designate towards developing new fun projects, whether it be a side project, somebody just wanted to mess around with, or actual time that they just wanted to try and build something, that's that's where new and great platforms come about. So I think that's a a great amount of time and a great, option that your employees have.\u003C\u002Fp>\u003Cp>Speaker 2: Awesome. Cool. Well, I think that's that's it from for our questions. Well, thank you, Darius, so much for your time. And, you know, we'll we'll obviously be following along with TDSoft.\u003C\u002Fp>\u003Cp>Please let everyone know how they can contact you to perhaps contract out some work or build something with,\u003C\u002Fp>\u003Cp>Speaker 1: obviously you can spell my name in Google search, right? Yeah. There you go. Easy. You know, I hope there'll be some link in the description or something.\u003C\u002Fp>\u003Cp>Yeah. We can do that. Yeah. And then I will also, you know, advertise or maybe comment under the, you know, social media appearances. So there will be a way to find me.\u003C\u002Fp>\u003Cp>Absolutely.\u003C\u002Fp>\u003Cp>Speaker 2: We'll list all your information here in the description and, and be on the lookout for future projects. But again, thank you for being a part of this. And, Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. It was a pleasure, guys. Thanks for having me here and, let's continue building stuff.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Absolutely. Thanks so much. And\u003C\u002Fp>\u003Cp>Speaker 1: scene.\u003C\u002Fp>","Alright. Thanks, everyone. We have Darius here from TD Soft. Darius, thanks so much for your time today. Why don't you tell the audience a little bit about yourself and a little bit about TD Soft? Hey, hey guys. Thanks for having me here. My name is Darius and I run a company in Central Europe and Poland called TDSoft. We built innovative software products for the customers, but also for ourselves. One of the projects we can discuss today is the one that we did for ourselves, which is the better signals.com. Yeah. So why don't you, why don't you give a little bit of background into Better Signals? And, also, in the same time, why don't you let us know, as you were starting to build these products, what led you to Directus? What kind of features in Directus kind of made you choose that? And then go into what BetterSignals is in your experience kind of building that out. Yeah. So actually starting from the, the Directus as I think this one came first in the company. Actually, we we started to look at this, product, back then it was in PHP, I think. I believe it was a couple of years back. Mhmm. So it was on our radar. You know, the migration to JavaScript based, you know, engine, was definitely the thing that allowed us to move move forward with it. In addition, some of the developers, actually, it was, you know, It started from a couple of points at the same time. Well, first was obviously me, but then I was looking for a technologies that would allow me to start up projects quicker, you know, go to business logic as quickly as possible. You know, we want to be innovative, deliver innovative solutions, and there is no time to waste. The, well, also, some of our developers started noticing directives from, articles, related to Jamstack. So it's kind of polar motive in here. Directors fits in quite nicely, and we were able to leverage, the features of directors in the better signals, project. It allowed us to cut the development time quite a lot. I I I was surprised how quickly we were able to deliver that. Not only this, but we were able to deliver it in a way that was, very, very scalable. So, I mean, we just started a couple months ago. The traffic is still growing, but we're ready for 100 100 times more traffic. And we are not afraid of the costs, as how we set this up. Directors allowed us to really speed up the process. Maybe, I can tell you a little bit about how we structured the project of Better Signals with Directus. Yeah. I'd love to learn more a little bit more about Better Signals, and that use case, you know, what you use direct us for. Exactly. Yeah. Okay. So just, and make the photo for better signals. We have a lot experience in the sports industry and the sports betting industry and in the talent industry. We work with sports betting companies, also affiliate companies, therefore, you know, natural idea of better signals, and, the idea of better signals, started. Better signals with, a new take on how we, provide sports tips, because we leverage the new media a lot. I mean, the deep directors is the content hub that, you know, we push content through, not only on the front end, but also we leverage the, and the flows to and the automations, to automatically publish posts on different media platforms, like Instagram, Twitter, Instagram, and so forth. So, the, the, the whole concept is about delivering quality tips to people in a new ways that are more, you know, attainable with modern market, modern user basis. Got it. Yeah. Yeah. And you and you mentioned the front end, you know, gathering content from the front end and the back end as well. What did you integrate with Directus? Because some people do use Directus as their actual interface for customers to come on to whether they're building an app or whatnot. But most people use, a different software for the front end. So what did you use to integrate Directus with for the front end, and how was that interaction between Directus and the front end? Was it easy? Was it easy to set up and easy to update? Give us a little bit of information about that. Yeah. Yes. So in line with, with ReactOS, we we used, a RemX technology that we were really looking at right now because, RemX has this approach of edge computing that, that we really need here. That allows us also to reduce the potential cost of the, you know, upkeep of the project. So we we manage the API of the Lexus. We use the JavaScript SDK with with Remix, which means that we use it mostly for the back end, but also, you know, we have professional tipsters that provide the content to to our platform, to CMS they get. So there is a kind of customer who is using the the platform, which is our, content providers. So so it's, at the end of the day, Directus is a content hub, a content pipeline that runs everything from the backend. Got it. Got it. And I heard that, you know, while you were building this out, you get a little bit of help or assistance, at least in the project from, from one of our own employees, Roger. Is that how did you find him and, how did that discussion go and what'd you learn here? It's actually quite funny because, I I didn't knew that, he works for you guys. Thank you for that. But, you know, he was the author of the, the, The, the repository, the starter repository that uses, dynamics, and direct card same time. And, had a couple of, issues, regarding the setup. You know, I'm also tinkering with this software a little bit on myself because I have to have the knowledge of how to build stuff quickly. And so I have reached out to Kim, over Discord channel, and he replied to me quite quickly. Oh, amazing. Only only later, I found out he actually works for it guys. Did he work for us at the time? Or was that I don't I don't think he worked for us at the time. I think He should. I think we saw how awesome this yeah. He's been putting out stuff around Dorekas for quite a while, and I think we we snagged him because his stuff is so it helps people, including himself, build tools quickly. He I think he's released and we can put this in the description, by the way, of the video. I think he's released several games that he said he built in 20 or 30 minutes. You know? So they're pretty simple games, but they're kinda fun to play around with. So, for those of you out there who do build things and kinda use Directus, maybe we'll snag you one day. But yeah. So I've I've I've followed along with you, Darius, because obviously we've had discussions before this and you and I have built up a little bit of a working relationship. What other additional product projects do you kind of have in mind to use Directus with or, or, you know, do you see Directus accelerating future projects in general for you guys? Yes. Definitely. Actually, we are in the middle of building another project, like, this is the project made for the customer. So as a service industry this time, but also sports related. We are building, a really innovative content pipeline, that allow that will allow us to provide, high quality content for multiple websites from one back end only, and actually really, really cuts the costs of, you know, proprietary content, you know, the editorial, before it's getting lost all over the world. And Directus allows us to actually deliver that within the times, times, and the costs, that was that was not before. Basically, also, what's really interesting about how we can, build stuff with directors is that we can prototype a lot because the cost of prototype is very, very small. With that, can provide the model and structure to the customer, suggest how we will do this feed get the gather the feedback on the live, product from him, and actually modify things, ease without leveraging, you know, tons of documentation, beforehand or multiple hours, long calls. Just, just to try to agree on something that we can't build yet. This time with directors, we can actually first build, a quick prototype showcase to the customer, you know, see his feedback, which is really helpful. Yeah. That's awesome. You know, throughout this conversation, obviously, we've, we've talked a lot about how amazing Directus is and and everything we can do. But I'm thinking, like, you know, in the future, obviously, we don't do everything today. So what are, are there some features or, you know, capabilities that you'd like to see out of direct us maybe in future versions or, you something you'd like to see us build within the platform? Obviously it's also custom, extensible to, to any, to some degree, but, what would you like to see coming in the future as as we progress? Yeah. Obviously, we we could come up with a Snyder list. I say now with every product. And I actually got, before this call, I got feedback from developers. Maybe the, this podcast doesn't have enough space to, to, to talk about very, very deep technical details. There's a couple of points, that, maybe I'll tell you. My story where I seem to get stopped, in one or two, things that would be really, really amazing to have solved. First thing is, If I would want to create a product, with custom front end using JavaScript SDK with single sign on, I have to implement that by myself, the single sign on, and then find a way to authenticate the user. So, single sign on way to authenticate instead of only login password for, you know, SDK JavaScript SDK would be something that, I would devote for. Got it. Okay. That that wasn't on the list from my developers. So, Is that already on on GitHub as as a request? Yes. And there's some discussion pending because it's not that easy. Yeah. Yeah. We'll start to upload it now then. Yeah. We'll we'll link it as well so everyone can upload it for you. There you go. Well, yeah. So if you want to create a very, very quick, application that allows you to quickly single sign login, do something, you know, showcase your work and log out, you know, that kind of simple stuff that might be a base of future SaaS product, you know, MVP. This is a little bit harder. We have to register properly and have login in the password or create SSO to something else, for example, you know, create accounts somewhere else to have single sign on capabilities and then only use the API. So that's one thing. There is a couple things in the, CMS, but I have to say that, the speed of, development in Directus is really encouraging. I mean, we are seeing a lot, a lot of post merged. So for example, I don't feel like I have to push forward and ask for more because I know that there is a lot of stuff happening right now. So, I can be patient enough, in this way for a couple of things to to be resolved, but on the general perspective, you know, the, the benefits, drastically outweigh the issues. I mean, as I mentioned to you, we can build a model very, very quickly, have an API ready, instantly create a quick front type of front end prototype, and also give the CMS to the customer within days. Awesome. Yeah. Well, we'll definitely take that, that feedback to the team as well. See how fast we can get that implemented for you. I know it's difficult though. I've been following that one for a bit. Yeah. No expectations. So I I I I I I I'm sorry. Yeah. Well, hopefully our developers are watching and encouraged. We are moving towards you know, we do put a lot of releases out there, a lot of pools or PRs that are done quickly, and we're kinda moving towards this continual release motion where we're just continually releasing new features, bug fixes, extensions, whatever that may be. I think part of your last answer kind of, answered some an additional question I was gonna have, some advice for other users, which also, you know, it's easy to develop. And is is there any sort of other hints that you would give users who are looking to either start using Directus or are using Directus and building a similar application? Any, you know, one or two really good tips that you've learned either the hard or easy way that you would give to other users? Mhmm. Yeah, for sure. I mean, first of all, try to leverage the, the cloud version, the free cloud version that you have. It's it's good enough. You you can you can start with that. You don't have to, you know, try to host things by yourself from the day 1. Secondly, we had an, ability to work in a team of, 5 developers, on better signals.com. That we noticed that it's really good to give them some space to actually track things out and see their platform capabilities before they're not jumping into work, because there are some trial cuts they they could take. So actually in, in terms of the recommendation, just, you know, explore the platform by yourself, you know, see the breadth of this before you go dive into some, you know, specifics. That that's something that I can, I can really, advise here? And yes, leverage the pre cloud version. It it has enough capabilities. So Darius, before the call, you were mentioning, really cool flow that you built inside of Directus. I'd love to to see that if we can show that off. Yeah. Yeah. For sure. Let me just show my screen for you, and then cool. And the cool thing about Slows is that in this product, they allow us to really streamline their process of content delivered to different platforms. We use additional automation software to to continue deliver the, the content. But as you can see, it allows us to, show the the processes in a very pleased way. For example, when we when someone from the editors, editorial team creates a new signal, then we get notified. So we can, for example, quality test that and approve this, without having to check the platform every time. We can also, for example, publish every scheduled signal. So, you know, scheduling is also part of this, flow. I also appreciate how it's in dark mode. I myself am a dark mode person, so I'm a fan of it. I love the contrast. Love the icons too. You know, it's all about developer experience, right? We invest a lot in developer experience, further experience. So as you can, I can show that you guys, the, the really cool thing is that I show you the one that will be more complex? Yeah. So I could it's really clear. I mean, even there is a a blog that says hack away, tag or something. It's too clear as we are doing something here for Yeah. Everyone who is not part of the project yet, it's very, very easy to actually understand what's happening there. So the the concept of flows and the visual presentation of flows in this platform is, really, probably best in class. Okay. Wow. We're gonna we're gonna sniff that and and quote that in the future as well. Yeah. So I do know in the flows, you are running, running one with JavaScript in line. That's a new feature. What's your experience been? Was it hard to learn flows? We're putting out more documentation around that all the time. I think we recently announced or released a flows cookbook. So what's your experience been building Flows? Because this is a feature that as Pedro and I talk to other prospects and users, they really start to understand the power of it. You know, obviously, visually, it's easy, but building it out once you learned it, is it extremely easy to build new flows? Yes. So it it needs a minute or 2 to, you know, get accustomed with us, and fully understand their power. So I I don't have much advice here except that maybe, just look at the documentation. I know that documentation is still being built for that. I don't think if I have anything, you know, interesting to say. No, it's good. It's it's, it's great to get a look at it. Yeah. And, also I can show you our log screen, you know. Oh, yeah. Nice. One of one of the things that are really cool about Directus is, you know, the theming option and being able to, also showcase to the customer the c CMS that he ordered that has his own brand and colors and style. Mhmm. Yeah. I think that's that's what really surprises me. It's just people, you know, don't take advantage as much as they could with the the theming. You know, we're we're building out a theming option where it just makes it even easier to change colors and whatnot. But even putting your logo up there, the whole the whole platform itself is white labelable, so that's awesome to see that you've done that. Yeah. Yeah. So brand, consistency and brand awareness is from the 1st day in the Battery Signals comp projects. So it was in a conscious decision to ensure that everything we do is, you know, following the the brand guidelines here, and it looks really nice. Yeah. I can also show you while you're on the call. I could also show you that we have just created quite simple, you know, data model that allows us to, basically deliver content tips, but not only. For example, creating, you know, a static page. It's just that, that simple model here. I like that. I like that WYSIWYG editor with a 1,000 Yeah. And there are these are our tips as you can see. And those are being managed by, by professional, content providers, you know, sports enthusiasts, people who have a lot of knowledge in sports who deliver those insights to us. And then we streamline those into our various media platforms. Got it. Awesome. And then this this is where, you know, people put in these tips and whatnot, and then the flows kind of, you know, work it through your process. Correct? Exactly. And then they end up here. Got it. Alright. Perfect. Well, since I've been following along and we've been interacting again for a little while, I'm excited to hear what the plan is for, you know, the next 6 to 12 months, maybe even further out if you wanna discuss it. But what are some things that you guys at TD Soft are planning to build and release? Yeah, sometimes are huge, obviously. In our company, we bought at least 20% of our income into r and d, which means internal product development because we have to level on ourselves, you know, on our own issues before we actually propose to customers on solution. We have full, planning for the whole next year for the better signals dotcom. We are just launching. I I think it'll be it might be even live once the podcast is live, though, or gamify.com platform, which is a business gamification platform for our companies. We use that on ourselves, and our whole team is working together towards achieving their own personal goals, personal development, and, watching their position on either boats and also buying some stuff from the, company store. I hope that other people will find it best in those. So, maybe I can suggest, interested visitor store visit our website once in a while to see the news as we always have something news in our pipeline. Yeah. I know that I'm Pedro and I are a little competitive amongst each other as well as with other employees here. So we will definitely be checking that out because any way that we can officially brag over the other person, I think we're, we're, we're all for that. So Yeah. Yeah. The most important thing about, you know, gamification is to be over that other person. It is. It's helped you competition is great for work. Yeah. Yeah. I mean, we know I can show you our library. It's small, but, you know, we treat self development seriously here, which means that it's not only about tech, but also about personal development and also, you know, Healthy lifestyle, all the things that come together to actually productive life and fulfilling life and not only man, just work. And I hope the same for others. That's why we published this stuff. Yeah. I think that's where, you know, you guys developing, putting 20% of your time to in in revenue towards research and development. I think that's kind of the model that a lot of technology companies use. And, for example, I mean, Google itself has developed so many products just from the free time or the time that they develop or they designate towards developing new fun projects, whether it be a side project, somebody just wanted to mess around with, or actual time that they just wanted to try and build something, that's that's where new and great platforms come about. So I think that's a a great amount of time and a great, option that your employees have. Awesome. Cool. Well, I think that's that's it from for our questions. Well, thank you, Darius, so much for your time. And, you know, we'll we'll obviously be following along with TDSoft. Please let everyone know how they can contact you to perhaps contract out some work or build something with, obviously you can spell my name in Google search, right? Yeah. There you go. Easy. You know, I hope there'll be some link in the description or something. Yeah. We can do that. Yeah. And then I will also, you know, advertise or maybe comment under the, you know, social media appearances. So there will be a way to find me. Absolutely. We'll list all your information here in the description and, and be on the lookout for future projects. But again, thank you for being a part of this. And, Yeah. Yeah. Yeah. It was a pleasure, guys. Thanks for having me here and, let's continue building stuff. Yeah. Absolutely. Thanks so much. And scene.",[6130,6131,6132],"8ba660da-46db-410e-8adc-dcb604affefb","9e1fb4c6-fec0-4a39-b543-44a1632545bb","14b84c3f-a4ca-400f-a814-9957cd465620",[],{"year":2380,"number":5101,"id":5102,"episodes":6135,"show":6136},[5104,5105,5106,5078],{"title":5108,"slug":5109},{"id":4028,"slug":6138,"vimeo_id":6139,"description":6140,"tile":6141,"length":4568,"resources":6142,"people":6147,"episode_number":13,"published":6151,"title":6152,"video_transcript_html":6153,"video_transcript_text":6154,"content":12,"seo":12,"status":19,"episode_people":6155,"recommendations":6159,"season":6160},"mistakes","921050862","How do we balance business and community? What have been some of our mistakes? What vision did you sell to VCs? How else might we make money? ","a7422934-3964-4dec-8d6b-5241a4a1ee24",[6143,6146],{"name":6144,"url":6145},"Blog Post","https:\u002F\u002Fdirectus.io\u002Fblog\u002Fcommunity-question-time-0\u002F",{"name":4008,"url":4009},[6148,6149,6150],{"name":4012,"url":4013},{"name":3795,"url":3796},{"name":2820,"url":2821},"2023-01-31","Mistakes","\u003Cp>Speaker 0: Hello there. My name is Kevin. I run developer relations at Directus, and I'm in New York City for the first time since the start of the pandemic, to meet the team. So that's really exciting. And so I thought I would take this opportunity to meet up with our cofounders, Ben and Reich, and ask them a bunch of questions that you've posed.\u003C\u002Fp>\u003Cp>That's me making sure I don't get knocked down by a car. Ask a ton of your questions, and this is just the start. We'll be doing this every few months. But I'm gonna go meet with Ryke, and then we are gonna ask him some questions. So one of the first questions we had in is just what is the value community at\u003C\u002Fp>\u003Cp>Speaker 1: the end of the day, it's the the foundation where everything is built on at the moment, honestly. Feedback loop is what what makes the project what it is, really.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Because without the feedback, like, we're just flying blind to a degree. We're building stuff for\u003C\u002Fp>\u003Cp>Speaker 1: us. And And and at the end of day, the goal is to help as many people as we can. So we we need that you know, it's a symbiotic thing, really.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Absolutely. So I suppose one of the slightly more tricky questions perhaps is you're gracing yourself. Is, what are some of the, I don't know, mistakes or missteps that you think we have made as directors? Oh, boy.\u003C\u002Fp>\u003Cp>I suppose scope to engineering, but maybe more broadly\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: To date.\u003C\u002Fp>\u003Cp>Speaker 1: That's a great question. I I I think one of the one of the bigger things that comes to mind is that we've launched a lot of stuff out of sort of enthusiasm, out of proof of concepts. Right? Where it's just like, we were noodling around with a new idea and it turns out that the proof of concept was already, you know, kinda there. And then you launch the proof of concept to to a GitHub branch or something and then people got excited and then you end up just pushing it out because it's like, oh, everybody already likes it.\u003C\u002Fp>\u003Cp>Let's just do it. Right? But then after the fact you realize like There's a maintenance\u003C\u002Fp>\u003Cp>Speaker 0: button or it needs\u003C\u002Fp>\u003Cp>Speaker 1: to be built. Built. Right. Yeah. We hadn't quite figured out the full spec yet, so now it's like, oh, but it should have done\u003C\u002Fp>\u003Cp>Speaker 0: this. Yeah. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: Or it should have worked like that. So like taking that slower, slower, has has been a big\u003C\u002Fp>\u003Cp>Speaker 0: I've seen you do that we wanna push out, maintain\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. For sure.\u003C\u002Fp>\u003Cp>Speaker 0: Properly. So I suppose there's definitely, growth\u003C\u002Fp>\u003Cp>Speaker 1: because there's out of that. There's so many fun things that we can do and that we have done and that we're trying out and whatnot. And then it's like, oh, but how would that work for real though? Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: Or at scale.\u003C\u002Fp>\u003Cp>Speaker 1: Or at scale Yeah. Or how does that Yeah. Exactly. So even now with like some of the new big features that we're already working on with like web sockets and stuff, it's like if you really just sit down and just think about what that means, it's like, oh, okay. Yeah.\u003C\u002Fp>\u003Cp>Yeah. There's just so much more that that that shows up and it's it's sometimes easy to miss, you know, the bigger picture if you just get excited about about a new feature like that. Reason with that Jet GPT stuff as well, wherever Yeah. Everybody's like, oh, what if\u003C\u002Fp>\u003Cp>Speaker 0: we just have AI?\u003C\u002Fp>\u003Cp>Speaker 1: It's like, woah, woah, woah.\u003C\u002Fp>\u003Cp>Speaker 0: When you build projects for yourself, you really just have to have like this this implementation that doesn't have to work at scale. Yeah. But we're having to think about what it means to build features that not only all of our all of our community can use Yeah. In open source in their own deployment, but also that we're gonna host and have Yep. Have tens of thousands of users have access to right this minute and\u003C\u002Fp>\u003Cp>Speaker 1: the implications\u003C\u002Fp>\u003Cp>Speaker 0: of that. Thanks for joining me in my office. Very rude for you to be eating on the job, if I may say so myself. Mark, I've got some questions for you. Mhmm.\u003C\u002Fp>\u003Cp>The first one, which is a little tricky, but I think very fair and very in the forefront of many of our community members' minds, which is there is a risk when building a business that we are then going to struggle to maintain and grow the community that's got us to where we are today. And I'm kinda wondering what you think about that.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. No. Like I said, it is it is a delicate delicate balancing act really, where it's like, you know, the community side of things, it it grows the project and user base to what it is right now, but at the same time, to make that sustainable over time, we gotta make enough revenue or enough profit to really get that, you know, make that work long time. Because the last thing we wanna do now is, you know, be sort of flesh in the pen and die out immediately.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. We want people to, like, rely on this project, not just for a year or 2 years, No.\u003C\u002Fp>\u003Cp>Speaker 1: Exactly. We we wanna be there reliable for as long as you need us. Right? And in order to do that, we have to make revenue. That's that's just the way it is, you know.\u003C\u002Fp>\u003Cp>Everybody gotta pay their rent. We gotta pay The one's\u003C\u002Fp>\u003Cp>Speaker 0: up right now.\u003C\u002Fp>\u003Cp>Speaker 1: There's there's loads of cost when it comes to to building software at this scale, and yet, you know, if you give everything away in open source, you you don't make revenue out of it. So it's a very delicate balance I guess. But I'm I'm I'm hopeful that in this dual sort of open source slash SaaS model, we can we can still help out with the parts that are still tricky. Even though the software you already can use, the the tricky bit is still, you know, hosting and doing it at scale for production use, backups and everything. So I do really believe that sort of that SaaS model next to having the open source code base is a sustainable thing.\u003C\u002Fp>\u003Cp>Yeah. I mean, not turning our back from this community either\u003C\u002Fp>\u003Cp>Speaker 0: like Oh, absolutely. Critical because without them, like, we we don't have the adoption. We don't have Exactly. The the proof, the social proof that this makes sense.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: That makes sense. Before I got here, the core team was really hot at answering people's questions when they came in on Discord. And you know community members have quite rightly noticed that starting to trail off a bit and I'm wondering if you have, any thoughts or or reasons for this? I know we've started to talk about it a bit, but given this context of us sitting here and it being a question that we got from the community, I thought this would be a great arena to talk about it again. Yeah.\u003C\u002Fp>\u003Cp>Absolutely.\u003C\u002Fp>\u003Cp>Speaker 1: And I complicated question. Right? Where it's like, early days, you know, smaller open source community. Therefore, the amount of questions that you get, I could just step into discord, you know, in the morning and just answer basically all of the open ones and then start moving on with my day. Whereas now, we're at a point where it would basically be a 3 x full time job to to really, you know, give it the attention and love that it deserves.\u003C\u002Fp>\u003Cp>And then the other thing is we did notice that at certain points, like, the the more duplicated or similar questions you get, it becomes, more of a challenge to sort of aggregate the similar ones together first, and then really just focus on explaining it well and sort of permanently when it comes to, like, docs and videos and stuff\u003C\u002Fp>\u003Cp>Speaker 2: like that.\u003C\u002Fp>\u003Cp>Speaker 1: So I think the focus shifted a little bit from trying to help everybody out individually to trying to help out Everyone. The community as a whole at the same time. So that means aggregating all the questions together first and then figuring out the priority of like, okay, what topics are often asked about? That's also why flows came up just now. Because we we know we get a lot of questions around you.\u003C\u002Fp>\u003Cp>How or how can you debug that better and that sort of thing. So really just taking the time to to first aggregating it and then focusing on the more quality content around that rather than trying to do one off, answer answer.\u003C\u002Fp>\u003Cp>Speaker 0: We're also just, what, like 30 ish people? Oh, that too. Yeah. Yeah. I mean, I think that's a huge thing.\u003C\u002Fp>\u003Cp>It's like we're 30 people. Yeah. And those 30 people are, you know, engineers who are working on pushing the platform forward and fixing bugs that occur. It includes salespeople who are there to, you know, take on people who want to pay us for the service, and that's great because they make the project sustainable, marketing to gather interest, docs, writers. But we are just 30 people and we all have stations that are, you know, pushing the project forward.\u003C\u002Fp>\u003Cp>Absolutely.\u003C\u002Fp>\u003Cp>Speaker 1: And then\u003C\u002Fp>\u003Cp>Speaker 0: answering community questions, is something that we absolutely are committed to doing, but within the bounds of the time we have. Bear in mind we also are doing other bits. And yeah that's really tricky because also like quite crudely, you know, we have a support offering that, you know, enterprises can pay for and those contexts enable us to hire people to help and support. Right. But still that is just a very small amount of, like, you know, hours that can be spent overall on what is appropriate for the people paying.\u003C\u002Fp>\u003Cp>But then when you have the community there as well, the the the the exponential growth of that community and user and users isn't reflected in the stuff that we have For sure. And that's fine, but then but then there's this really come to us one time?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Exactly.\u003C\u002Fp>\u003Cp>Speaker 0: Down in docs and education 100%. And then we can help anyone else who Yeah. Who ask questions who we have time to.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. That's because there's there's also a lot of great questions that sort of toy on the difference between how do I do x versus it would be cool if your ex could do y. You know what I mean? And but yeah, I think right now just to answer the the the actual questions that are coming in, we're we're just focusing on basically tackling it at the source. Just making sure that, like, people don't have to come in to ask the question in the first place.\u003C\u002Fp>\u003Cp>Right? Because there's just angles where you said education content.\u003C\u002Fp>\u003Cp>Speaker 0: And then just trying our best for the rest. Oh, yeah. Absolutely. The last question I have for you actually is, a little bit more high level, I suppose, which is just how do we decide what to work on in engineering? That's a\u003C\u002Fp>\u003Cp>Speaker 1: great one.\u003C\u002Fp>\u003Cp>Speaker 0: Like, how do we decide on that?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. No. That is a great question, which is also one that that changed with the growth of the community and the project, obviously. Because when it started out, I mean, especially especially early early days, it was just like whatever the wind blew and it was just like, oh, it feels like feels like insights this week. You know what I mean?\u003C\u002Fp>\u003Cp>But but nowadays, no, there's there's way more of a process now. Especially seeing that we have a lot of of feature requests coming in as well. It's it's it's it's a pretty big challenge to figure out, like, which one is the important one to do next, basically. Because like you mentioned, the same goes for engineering. Like, there's only so many people and so many hours in\u003C\u002Fp>\u003Cp>Speaker 2: a day.\u003C\u002Fp>\u003Cp>Speaker 1: So we have to figure out what's important.\u003C\u002Fp>\u003Cp>Speaker 0: What goes into that process?\u003C\u002Fp>\u003Cp>Speaker 1: So it is it is mostly community interest honestly, where it's just we have GitHub discussions which is a great aggregator of new ideas. Like, how would it work?\u003C\u002Fp>\u003Cp>Speaker 2: How could it work?\u003C\u002Fp>\u003Cp>Speaker 1: How should it work? Like, how would it work? How could it work? How should it work? And how can we make it work like that?\u003C\u002Fp>\u003Cp>Right? And then the other thing is with up votes, we can basically just have a ranked list of like, here's the top ten things that we would like to see next from that community angle. And at the same time, there's obviously also parts. It's it's A big focus is also stability and performance.\u003C\u002Fp>\u003Cp>Speaker 0: Mhmm. Right? Where it's\u003C\u002Fp>\u003Cp>Speaker 1: like we can't forget that, you know, most of the engineering work is to make sure that what we have remains the work and gets better, rather than it's not it's not just taking on more stuff, more stuff, more stuff. Right? So that's another tricky balance where it's like we gotta be careful in picking our battles because every time we push out something new now, we gotta make sure that it's good. Yeah. It needs to work.\u003C\u002Fp>\u003Cp>Speaker 0: And it's easy to take.\u003C\u002Fp>\u003Cp>Speaker 1: And it's easy to maintain, and it works everywhere.\u003C\u002Fp>\u003Cp>Speaker 0: Another question, I guess, which was explicitly asked, and it's kind of a really nice segue into it. I said it was the last question. I lied. It's ripping off of it, I guess Oh, good. Is how do we balance kind of feature requests from customers, from people who pay us for directors cloud at an enterprise level or, you know, whatever that might look like.\u003C\u002Fp>\u003Cp>Mhmm. Companies who we're who we're wanting to get on board perhaps and community features. I imagine a lot of the time they converge and they are actually similar and it just adds like, you know, oh, well, we can treat that as like 10 upvotes for the sake\u003C\u002Fp>\u003Cp>Speaker 1: of impact.\u003C\u002Fp>\u003Cp>Speaker 0: But sometimes I imagine there's feature requests that come in from this more like enterprise land Sure. That are just not what the community Not at odds with it, just not and we still only have so many hours to spend. So how how do you make that decision as to\u003C\u002Fp>\u003Cp>Speaker 2: That's that's\u003C\u002Fp>\u003Cp>Speaker 0: a great point.\u003C\u002Fp>\u003Cp>Speaker 1: So so we do still look at\u003C\u002Fp>\u003Cp>Speaker 0: it with the\u003C\u002Fp>\u003Cp>Speaker 1: same sort of 80 20 lens that we do for basically everything. Where it's like if somebody shows up and they they want us to they wanna do a sponsored feature and they're like, here's what we needed to do. There's still that first layer that we need to look at it. It's like, does that make sense for the bigger picture? Yeah.\u003C\u002Fp>\u003Cp>Right? Where it's like, we want this to go out to as many people as possible, but this feels like something that is so hyper specific to one use case, it might not be as appropriate as a core feature. Right? And then it becomes a question of like, okay, are we gonna do it as an extension? Are we just gonna not do it?\u003C\u002Fp>\u003Cp>Right? But luckily, so far, what what most of the evidence is basically what you hinted on earlier, where it's like, the needs of the community are inherently the needs of everybody else. Mhmm. Because and it's it's all the same pool. Mhmm.\u003C\u002Fp>\u003Cp>Right? So luckily, a lot of the sponsor features and stuff, they come in and they sponsor something that is already on the feature list. So it's basically what you just mentioned where we treat it as, oh, that just, you know, gives it a virtual 10 extra uploads.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Or however we see how that build that. Yeah.\u003C\u002Fp>\u003Cp>Speaker 1: See how that bumps it to, to the Tejulis, basically. Yeah. But it it is a great way to to to sort of tie it back into your question around how do you make it revenue work. Right? I do think it's a great additional revenue stream where it's like we're we get to build the features that we do wanna build already.\u003C\u002Fp>\u003Cp>Right? But we we can offer to sort of prioritize them for a fee, and that helps, you know, that helps the revenue side of things. It helps us to be able to fund the building of the feature.\u003C\u002Fp>\u003Cp>Speaker 0: Which then everyone can use.\u003C\u002Fp>\u003Cp>Speaker 1: And everybody can use\u003C\u002Fp>\u003Cp>Speaker 0: it still. And I also like the because of how extensible Director says, if it isn't something that's gonna be useful to everyone but for whatever reason, money or whatever, then we deem that no really we do need to build this to, you know, have this company on board or whatever it may be, that there is a way to do it in a way that doesn't just bloat into Corus\u003C\u002Fp>\u003Cp>Speaker 1: as well. Right. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: That is. Just filled extensions. Exactly.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Really nice. Yep.\u003C\u002Fp>\u003Cp>Speaker 0: Well, thanks for taking the time to speak to me.\u003C\u002Fp>\u003Cp>Speaker 1: This has\u003C\u002Fp>\u003Cp>Speaker 0: been really fun. I've enjoyed hanging out in Brooklyn and ending up at Joe's Pizza. Shout out. Yeah. Shout out.\u003C\u002Fp>\u003Cp>It it was a good good slice of\u003C\u002Fp>\u003Cp>Speaker 1: pizza. Yeah.\u003C\u002Fp>\u003Cp>Speaker 0: But, yeah, thank you very much. I'm I'm gonna try and have a chat with Ben over the next few days Yeah. As we are gathered here in New York City.\u003C\u002Fp>\u003Cp>Speaker 1: Absolutely. Sick. Thank you.\u003C\u002Fp>\u003Cp>Speaker 0: Hey, Dan. Good to see you.\u003C\u002Fp>\u003Cp>Speaker 2: Good to see\u003C\u002Fp>\u003Cp>Speaker 0: you, Kevin. Thanks for making the time. Yeah. Even though I totally grabbed you off the side of what we were doing, which is, we've been in New York for the last couple of days. How have you found it?\u003C\u002Fp>\u003Cp>Speaker 2: It's amazing. It's always good to get together with the team in person. We had only 1 or 2 before this, so getting to meet new people on the growing team. Excellent.\u003C\u002Fp>\u003Cp>Speaker 0: So as you know, I've got some questions from the community that I've gathered from a few different places. I'm gonna start with one for me just to set the scene, which is that we have our 1st board meeting in a couple of weeks and I'm kind of curious how you're feeling about it.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I mean, there's the normal nerves that come with the first of anything, but really excited. We're hitting our goals and just, excited to see what the board thinks and have that, communication in both directions.\u003C\u002Fp>\u003Cp>Speaker 0: Nice. Well, the first question from the community I'm gonna start I'm gonna start with one of the the tricky ones. Sorry. But I've set the scene for it, which is that we, you know, we have some VCs now and, they they wanna make their money back, and I'm kinda curious what vision you sold them on and then kind of how that vision interplays with our community and open source work.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. I mean, so it's obviously super important for us to make revenue, to have the income that supports our staff, to support our our servers and everything that we need to deliver our product and services. And we also have to keep the investors happy. That's just a big part of what we're trying to do, building a sustainable company. My mentality for that has been, you know, we have this enormous breadth of open source users, you know, 21,000,000, Docker downloads right now.\u003C\u002Fp>\u003Cp>You don't need to monetize them all. The goal is a rising tide lifts all ships. If we push, the product, if we push the open source offering and a percentage of those need to host with us, they need our professional services, whatever it might be that we commercialized, then they can come take that off their plate, and and we can drive revenue and continue to keep all those investors happy.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. That makes sense. I, kind of on that same trend, I know something in the forefront of community members' minds, knowing full well that, like, we are, you know, we are we are marching towards sustainability through making money. At the moment we have Directus Cloud where, you know, we people can pay us to host Directus for them and handle infrastructure, you know, and hosting costs and rolling updates. But I wanna know, as do members of the community, what else is in your mind in the kind of short, mid term for how we might, you know, make make money?\u003C\u002Fp>\u003Cp>Yeah. So I mean the the\u003C\u002Fp>\u003Cp>Speaker 2: we have the 3 legged stool as I call it, the milkmaid stool that never wobbles. We've got the core platform which is that, that open source cake that we deliver. But it's not just the infrastructure, you know, the table that cake sits on that we're providing. We're also giving those enterprise exclusives. So the cloud comes with exclusive extensions and features and capabilities, that are just icing on that cake.\u003C\u002Fp>\u003Cp>They're not a core part of the software, but those, of course, add a value, to those to the to the cloud service. There's also professional services, support offerings, consulting. There's this whole slew of, things that we go beyond the product, to deliver to those customers as well. And then of course, that third leg of the stool is the marketplace. The extensions is essentially a whole app store, that can have free extensions from the community, from our authoritative engineers, and some of those can be monetized as well.\u003C\u002Fp>\u003Cp>Speaker 0: So one thing you mentioned was about having, you know, extensions that are exclusive to cloud and how they're the icing on the cake. And I'm kinda wondering how you at what point can we no longer really call ourselves, an open source product and instead something more like open core or something like that. How do you grapple with that, How do you grapple with that definition?\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. So basically, you have a number of metaphors. I'm a big fan of metaphors, but the cake one I think works really well. You have this open source core. You have this idea of the cake, and the cake is, you know, exactly that, but the scale matters.\u003C\u002Fp>\u003Cp>You know, if you have this ratio of very little cake and a lot of paid icing on top of it, that's not so tasty. You basically have the whole thing as the cake and then you give it a facade, whatever is the important customized bit for you. You can design it, you lay it out, the color, everything, but that ratio is important. You keep a full cake, that's exactly what it needs to be, you don't need to maintain it, you just kinda buy buy it as is, and then you decorate it, and you make it yours.\u003C\u002Fp>\u003Cp>Speaker 0: Yes. Because you definitely maintain cakes.\u003C\u002Fp>\u003Cp>Speaker 2: Yeah. That's a full on bakery here.\u003C\u002Fp>\u003Cp>Speaker 1: Mhmm. Mhmm.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. I'm on to the last couple now, and I think these are possibly the questions you expected, which is, at the time of recording, we are, you know, in the middle of January 2023. What are you excited about in the next year?\u003C\u002Fp>\u003Cp>Speaker 2: I think one of the biggest things from a tech I'm a product person, so on the technical side, I think real time data of WebSockets is just enormous. The way that we've built the platform is these this toolkit and all those tools can be used together. So when you add something like real time data with web sockets, you compound things like real time collaborative editing, real time dashboard for insights and flows. I think from a, a product standpoint, that's certainly the biggest. And then that that third leg of the stool, the marketplace, the extensions, having that be have have the perfect DX, you know, developer experience, having a marketplace in the app where you can very seamlessly pull in that icing, those extensions, that's gonna be a huge part of this year.\u003C\u002Fp>\u003Cp>Speaker 0: Nice. And I suppose the inverse of that is, you know, you're the CEO of a company that's employing like 30 people ish. What are you worried about?\u003C\u002Fp>\u003Cp>Speaker 2: What's in\u003C\u002Fp>\u003Cp>Speaker 0: your mind?\u003C\u002Fp>\u003Cp>Speaker 2: I I think just that. I mean, 18 months ago, we were it was, Rich and I, you know, a 2 person open source organization, and now we've grown to 30:30 folks and we just hired somebody yesterday. You know, this is it's very challenging to hold on to your ethos, to your vision if you scale very quickly. So growing at the right pace, finding the right team members, which we've been very successful at doing, and just making sure that we don't lose track of our vision, the open source nature, the respectful communication, the collaborative nature of of growing a distributed team across the globe. Those are challenging things to do, but I think so far we're doing it really well.\u003C\u002Fp>\u003Cp>Speaker 0: Cool. Thank you so much. And so that was our first community question time. I'm back in Berlin now editing that video. There's the plant I can't keep alive.\u003C\u002Fp>\u003Cp>But hopefully, you found that interesting. It's really important to us to just be as transparent open as possible with you, the community that has got us to where we are today and will, you know, take us to where we'll be in the future. We'll run this every few months, so keep the questions coming wherever really we'll see them, and we will bring those into future community question times. I also thought this would be a really good chance to just kinda say that with Wrike at least We'll we'll do this monthly, you know, and we might dig into some more technical topics around technical decisions that are being made and technical progress and features that are being built. But yeah, hopefully you found this good and I will see you next time.\u003C\u002Fp>","Hello there. My name is Kevin. I run developer relations at Directus, and I'm in New York City for the first time since the start of the pandemic, to meet the team. So that's really exciting. And so I thought I would take this opportunity to meet up with our cofounders, Ben and Reich, and ask them a bunch of questions that you've posed. That's me making sure I don't get knocked down by a car. Ask a ton of your questions, and this is just the start. We'll be doing this every few months. But I'm gonna go meet with Ryke, and then we are gonna ask him some questions. So one of the first questions we had in is just what is the value community at the end of the day, it's the the foundation where everything is built on at the moment, honestly. Feedback loop is what what makes the project what it is, really. Yeah. Because without the feedback, like, we're just flying blind to a degree. We're building stuff for us. And And and at the end of day, the goal is to help as many people as we can. So we we need that you know, it's a symbiotic thing, really. Yeah. Absolutely. So I suppose one of the slightly more tricky questions perhaps is you're gracing yourself. Is, what are some of the, I don't know, mistakes or missteps that you think we have made as directors? Oh, boy. I suppose scope to engineering, but maybe more broadly Yeah. To date. That's a great question. I I I think one of the one of the bigger things that comes to mind is that we've launched a lot of stuff out of sort of enthusiasm, out of proof of concepts. Right? Where it's just like, we were noodling around with a new idea and it turns out that the proof of concept was already, you know, kinda there. And then you launch the proof of concept to to a GitHub branch or something and then people got excited and then you end up just pushing it out because it's like, oh, everybody already likes it. Let's just do it. Right? But then after the fact you realize like There's a maintenance button or it needs to be built. Built. Right. Yeah. We hadn't quite figured out the full spec yet, so now it's like, oh, but it should have done this. Yeah. Yeah. Or it should have worked like that. So like taking that slower, slower, has has been a big I've seen you do that we wanna push out, maintain Yeah. For sure. Properly. So I suppose there's definitely, growth because there's out of that. There's so many fun things that we can do and that we have done and that we're trying out and whatnot. And then it's like, oh, but how would that work for real though? Yeah. Or at scale. Or at scale Yeah. Or how does that Yeah. Exactly. So even now with like some of the new big features that we're already working on with like web sockets and stuff, it's like if you really just sit down and just think about what that means, it's like, oh, okay. Yeah. Yeah. There's just so much more that that that shows up and it's it's sometimes easy to miss, you know, the bigger picture if you just get excited about about a new feature like that. Reason with that Jet GPT stuff as well, wherever Yeah. Everybody's like, oh, what if we just have AI? It's like, woah, woah, woah. When you build projects for yourself, you really just have to have like this this implementation that doesn't have to work at scale. Yeah. But we're having to think about what it means to build features that not only all of our all of our community can use Yeah. In open source in their own deployment, but also that we're gonna host and have Yep. Have tens of thousands of users have access to right this minute and the implications of that. Thanks for joining me in my office. Very rude for you to be eating on the job, if I may say so myself. Mark, I've got some questions for you. Mhmm. The first one, which is a little tricky, but I think very fair and very in the forefront of many of our community members' minds, which is there is a risk when building a business that we are then going to struggle to maintain and grow the community that's got us to where we are today. And I'm kinda wondering what you think about that. Yeah. No. Like I said, it is it is a delicate delicate balancing act really, where it's like, you know, the community side of things, it it grows the project and user base to what it is right now, but at the same time, to make that sustainable over time, we gotta make enough revenue or enough profit to really get that, you know, make that work long time. Because the last thing we wanna do now is, you know, be sort of flesh in the pen and die out immediately. Yeah. We want people to, like, rely on this project, not just for a year or 2 years, No. Exactly. We we wanna be there reliable for as long as you need us. Right? And in order to do that, we have to make revenue. That's that's just the way it is, you know. Everybody gotta pay their rent. We gotta pay The one's up right now. There's there's loads of cost when it comes to to building software at this scale, and yet, you know, if you give everything away in open source, you you don't make revenue out of it. So it's a very delicate balance I guess. But I'm I'm I'm hopeful that in this dual sort of open source slash SaaS model, we can we can still help out with the parts that are still tricky. Even though the software you already can use, the the tricky bit is still, you know, hosting and doing it at scale for production use, backups and everything. So I do really believe that sort of that SaaS model next to having the open source code base is a sustainable thing. Yeah. I mean, not turning our back from this community either like Oh, absolutely. Critical because without them, like, we we don't have the adoption. We don't have Exactly. The the proof, the social proof that this makes sense. Yeah. That makes sense. Before I got here, the core team was really hot at answering people's questions when they came in on Discord. And you know community members have quite rightly noticed that starting to trail off a bit and I'm wondering if you have, any thoughts or or reasons for this? I know we've started to talk about it a bit, but given this context of us sitting here and it being a question that we got from the community, I thought this would be a great arena to talk about it again. Yeah. Absolutely. And I complicated question. Right? Where it's like, early days, you know, smaller open source community. Therefore, the amount of questions that you get, I could just step into discord, you know, in the morning and just answer basically all of the open ones and then start moving on with my day. Whereas now, we're at a point where it would basically be a 3 x full time job to to really, you know, give it the attention and love that it deserves. And then the other thing is we did notice that at certain points, like, the the more duplicated or similar questions you get, it becomes, more of a challenge to sort of aggregate the similar ones together first, and then really just focus on explaining it well and sort of permanently when it comes to, like, docs and videos and stuff like that. So I think the focus shifted a little bit from trying to help everybody out individually to trying to help out Everyone. The community as a whole at the same time. So that means aggregating all the questions together first and then figuring out the priority of like, okay, what topics are often asked about? That's also why flows came up just now. Because we we know we get a lot of questions around you. How or how can you debug that better and that sort of thing. So really just taking the time to to first aggregating it and then focusing on the more quality content around that rather than trying to do one off, answer answer. We're also just, what, like 30 ish people? Oh, that too. Yeah. Yeah. I mean, I think that's a huge thing. It's like we're 30 people. Yeah. And those 30 people are, you know, engineers who are working on pushing the platform forward and fixing bugs that occur. It includes salespeople who are there to, you know, take on people who want to pay us for the service, and that's great because they make the project sustainable, marketing to gather interest, docs, writers. But we are just 30 people and we all have stations that are, you know, pushing the project forward. Absolutely. And then answering community questions, is something that we absolutely are committed to doing, but within the bounds of the time we have. Bear in mind we also are doing other bits. And yeah that's really tricky because also like quite crudely, you know, we have a support offering that, you know, enterprises can pay for and those contexts enable us to hire people to help and support. Right. But still that is just a very small amount of, like, you know, hours that can be spent overall on what is appropriate for the people paying. But then when you have the community there as well, the the the the exponential growth of that community and user and users isn't reflected in the stuff that we have For sure. And that's fine, but then but then there's this really come to us one time? Yeah. Exactly. Down in docs and education 100%. And then we can help anyone else who Yeah. Who ask questions who we have time to. Yeah. That's because there's there's also a lot of great questions that sort of toy on the difference between how do I do x versus it would be cool if your ex could do y. You know what I mean? And but yeah, I think right now just to answer the the the actual questions that are coming in, we're we're just focusing on basically tackling it at the source. Just making sure that, like, people don't have to come in to ask the question in the first place. Right? Because there's just angles where you said education content. And then just trying our best for the rest. Oh, yeah. Absolutely. The last question I have for you actually is, a little bit more high level, I suppose, which is just how do we decide what to work on in engineering? That's a great one. Like, how do we decide on that? Yeah. No. That is a great question, which is also one that that changed with the growth of the community and the project, obviously. Because when it started out, I mean, especially especially early early days, it was just like whatever the wind blew and it was just like, oh, it feels like feels like insights this week. You know what I mean? But but nowadays, no, there's there's way more of a process now. Especially seeing that we have a lot of of feature requests coming in as well. It's it's it's it's a pretty big challenge to figure out, like, which one is the important one to do next, basically. Because like you mentioned, the same goes for engineering. Like, there's only so many people and so many hours in a day. So we have to figure out what's important. What goes into that process? So it is it is mostly community interest honestly, where it's just we have GitHub discussions which is a great aggregator of new ideas. Like, how would it work? How could it work? How should it work? Like, how would it work? How could it work? How should it work? And how can we make it work like that? Right? And then the other thing is with up votes, we can basically just have a ranked list of like, here's the top ten things that we would like to see next from that community angle. And at the same time, there's obviously also parts. It's it's A big focus is also stability and performance. Mhmm. Right? Where it's like we can't forget that, you know, most of the engineering work is to make sure that what we have remains the work and gets better, rather than it's not it's not just taking on more stuff, more stuff, more stuff. Right? So that's another tricky balance where it's like we gotta be careful in picking our battles because every time we push out something new now, we gotta make sure that it's good. Yeah. It needs to work. And it's easy to take. And it's easy to maintain, and it works everywhere. Another question, I guess, which was explicitly asked, and it's kind of a really nice segue into it. I said it was the last question. I lied. It's ripping off of it, I guess Oh, good. Is how do we balance kind of feature requests from customers, from people who pay us for directors cloud at an enterprise level or, you know, whatever that might look like. Mhmm. Companies who we're who we're wanting to get on board perhaps and community features. I imagine a lot of the time they converge and they are actually similar and it just adds like, you know, oh, well, we can treat that as like 10 upvotes for the sake of impact. But sometimes I imagine there's feature requests that come in from this more like enterprise land Sure. That are just not what the community Not at odds with it, just not and we still only have so many hours to spend. So how how do you make that decision as to That's that's a great point. So so we do still look at it with the same sort of 80 20 lens that we do for basically everything. Where it's like if somebody shows up and they they want us to they wanna do a sponsored feature and they're like, here's what we needed to do. There's still that first layer that we need to look at it. It's like, does that make sense for the bigger picture? Yeah. Right? Where it's like, we want this to go out to as many people as possible, but this feels like something that is so hyper specific to one use case, it might not be as appropriate as a core feature. Right? And then it becomes a question of like, okay, are we gonna do it as an extension? Are we just gonna not do it? Right? But luckily, so far, what what most of the evidence is basically what you hinted on earlier, where it's like, the needs of the community are inherently the needs of everybody else. Mhmm. Because and it's it's all the same pool. Mhmm. Right? So luckily, a lot of the sponsor features and stuff, they come in and they sponsor something that is already on the feature list. So it's basically what you just mentioned where we treat it as, oh, that just, you know, gives it a virtual 10 extra uploads. Yeah. Or however we see how that build that. Yeah. See how that bumps it to, to the Tejulis, basically. Yeah. But it it is a great way to to to sort of tie it back into your question around how do you make it revenue work. Right? I do think it's a great additional revenue stream where it's like we're we get to build the features that we do wanna build already. Right? But we we can offer to sort of prioritize them for a fee, and that helps, you know, that helps the revenue side of things. It helps us to be able to fund the building of the feature. Which then everyone can use. And everybody can use it still. And I also like the because of how extensible Director says, if it isn't something that's gonna be useful to everyone but for whatever reason, money or whatever, then we deem that no really we do need to build this to, you know, have this company on board or whatever it may be, that there is a way to do it in a way that doesn't just bloat into Corus as well. Right. Yeah. That is. Just filled extensions. Exactly. Yeah. Really nice. Yep. Well, thanks for taking the time to speak to me. This has been really fun. I've enjoyed hanging out in Brooklyn and ending up at Joe's Pizza. Shout out. Yeah. Shout out. It it was a good good slice of pizza. Yeah. But, yeah, thank you very much. I'm I'm gonna try and have a chat with Ben over the next few days Yeah. As we are gathered here in New York City. Absolutely. Sick. Thank you. Hey, Dan. Good to see you. Good to see you, Kevin. Thanks for making the time. Yeah. Even though I totally grabbed you off the side of what we were doing, which is, we've been in New York for the last couple of days. How have you found it? It's amazing. It's always good to get together with the team in person. We had only 1 or 2 before this, so getting to meet new people on the growing team. Excellent. So as you know, I've got some questions from the community that I've gathered from a few different places. I'm gonna start with one for me just to set the scene, which is that we have our 1st board meeting in a couple of weeks and I'm kind of curious how you're feeling about it. Yeah. I mean, there's the normal nerves that come with the first of anything, but really excited. We're hitting our goals and just, excited to see what the board thinks and have that, communication in both directions. Nice. Well, the first question from the community I'm gonna start I'm gonna start with one of the the tricky ones. Sorry. But I've set the scene for it, which is that we, you know, we have some VCs now and, they they wanna make their money back, and I'm kinda curious what vision you sold them on and then kind of how that vision interplays with our community and open source work. Yeah. I mean, so it's obviously super important for us to make revenue, to have the income that supports our staff, to support our our servers and everything that we need to deliver our product and services. And we also have to keep the investors happy. That's just a big part of what we're trying to do, building a sustainable company. My mentality for that has been, you know, we have this enormous breadth of open source users, you know, 21,000,000, Docker downloads right now. You don't need to monetize them all. The goal is a rising tide lifts all ships. If we push, the product, if we push the open source offering and a percentage of those need to host with us, they need our professional services, whatever it might be that we commercialized, then they can come take that off their plate, and and we can drive revenue and continue to keep all those investors happy. Yeah. That makes sense. I, kind of on that same trend, I know something in the forefront of community members' minds, knowing full well that, like, we are, you know, we are we are marching towards sustainability through making money. At the moment we have Directus Cloud where, you know, we people can pay us to host Directus for them and handle infrastructure, you know, and hosting costs and rolling updates. But I wanna know, as do members of the community, what else is in your mind in the kind of short, mid term for how we might, you know, make make money? Yeah. So I mean the the we have the 3 legged stool as I call it, the milkmaid stool that never wobbles. We've got the core platform which is that, that open source cake that we deliver. But it's not just the infrastructure, you know, the table that cake sits on that we're providing. We're also giving those enterprise exclusives. So the cloud comes with exclusive extensions and features and capabilities, that are just icing on that cake. They're not a core part of the software, but those, of course, add a value, to those to the to the cloud service. There's also professional services, support offerings, consulting. There's this whole slew of, things that we go beyond the product, to deliver to those customers as well. And then of course, that third leg of the stool is the marketplace. The extensions is essentially a whole app store, that can have free extensions from the community, from our authoritative engineers, and some of those can be monetized as well. So one thing you mentioned was about having, you know, extensions that are exclusive to cloud and how they're the icing on the cake. And I'm kinda wondering how you at what point can we no longer really call ourselves, an open source product and instead something more like open core or something like that. How do you grapple with that, How do you grapple with that definition? Yeah. So basically, you have a number of metaphors. I'm a big fan of metaphors, but the cake one I think works really well. You have this open source core. You have this idea of the cake, and the cake is, you know, exactly that, but the scale matters. You know, if you have this ratio of very little cake and a lot of paid icing on top of it, that's not so tasty. You basically have the whole thing as the cake and then you give it a facade, whatever is the important customized bit for you. You can design it, you lay it out, the color, everything, but that ratio is important. You keep a full cake, that's exactly what it needs to be, you don't need to maintain it, you just kinda buy buy it as is, and then you decorate it, and you make it yours. Yes. Because you definitely maintain cakes. Yeah. That's a full on bakery here. Mhmm. Mhmm. Alright. I'm on to the last couple now, and I think these are possibly the questions you expected, which is, at the time of recording, we are, you know, in the middle of January 2023. What are you excited about in the next year? I think one of the biggest things from a tech I'm a product person, so on the technical side, I think real time data of WebSockets is just enormous. The way that we've built the platform is these this toolkit and all those tools can be used together. So when you add something like real time data with web sockets, you compound things like real time collaborative editing, real time dashboard for insights and flows. I think from a, a product standpoint, that's certainly the biggest. And then that that third leg of the stool, the marketplace, the extensions, having that be have have the perfect DX, you know, developer experience, having a marketplace in the app where you can very seamlessly pull in that icing, those extensions, that's gonna be a huge part of this year. Nice. And I suppose the inverse of that is, you know, you're the CEO of a company that's employing like 30 people ish. What are you worried about? What's in your mind? I I think just that. I mean, 18 months ago, we were it was, Rich and I, you know, a 2 person open source organization, and now we've grown to 30:30 folks and we just hired somebody yesterday. You know, this is it's very challenging to hold on to your ethos, to your vision if you scale very quickly. So growing at the right pace, finding the right team members, which we've been very successful at doing, and just making sure that we don't lose track of our vision, the open source nature, the respectful communication, the collaborative nature of of growing a distributed team across the globe. Those are challenging things to do, but I think so far we're doing it really well. Cool. Thank you so much. And so that was our first community question time. I'm back in Berlin now editing that video. There's the plant I can't keep alive. But hopefully, you found that interesting. It's really important to us to just be as transparent open as possible with you, the community that has got us to where we are today and will, you know, take us to where we'll be in the future. We'll run this every few months, so keep the questions coming wherever really we'll see them, and we will bring those into future community question times. I also thought this would be a really good chance to just kinda say that with Wrike at least We'll we'll do this monthly, you know, and we might dig into some more technical topics around technical decisions that are being made and technical progress and features that are being built. But yeah, hopefully you found this good and I will see you next time.",[6156,6157,6158],"a96cc94f-9d69-42fb-8ca4-826ea9f12c36","b6e2f298-dfab-4e48-9811-bba285a16053","2d8c17b3-6be6-4bce-913b-01c5c2ca556b",[],{"year":2380,"number":909,"id":4026,"episodes":6161,"show":6162},[4028,4001],{"title":4030,"slug":4031},{"id":6164,"slug":6165,"vimeo_id":6166,"description":6167,"tile":6168,"length":159,"resources":12,"people":6169,"episode_number":527,"published":6172,"title":6173,"video_transcript_html":6174,"video_transcript_text":6175,"content":12,"seo":12,"status":19,"episode_people":6176,"recommendations":6179,"season":6180},"7ff793ee-3232-4ece-8a11-25d13812cda0","season-1-bloopers","894041419","What's a season without a load of bloopers? Have fun! ","f082c9f6-9b2b-4dec-982e-b17802b3a3f0",[6170,6171],{"name":4361,"url":4362},{"name":4524,"url":6097},"2023-01-03","Bloopers From The Field","\u003Cp>Speaker 0: Alright. I'm recording there. I'm recording there. I'm good. Are you good?\u003C\u002Fp>\u003Cp>Speaker 1: Cool.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Hey, everyone. Welcome back to another episode of From the Field, q and a with P and J. I am the J in it. I am John.\u003C\u002Fp>\u003Cp>Speaker 1: I am I'm not the p. And that would make me the p. That would be good. For specific user language, we'll default to the project's default language. So that's that's You\u003C\u002Fp>\u003Cp>Speaker 0: wanna re That'll just redo it. Hey, everybody. Welcome back to another episode of from the field p and q and j and a and q r\u003C\u002Fp>\u003Cp>Speaker 1: and d. And x y z. Now q r s t m s t?\u003C\u002Fp>\u003Cp>Speaker 0: Q and a\u003C\u002Fp>\u003Cp>Speaker 1: with p and w x y z z zed for you international folks. So that makes it really easy, so you're never missing any oh, whatever. This is a simple answer. I'm not no need to make it complicated.\u003C\u002Fp>\u003Cp>Speaker 0: Hey, everyone. Welcome back to another episode of From the Field, q and a with p and j. I am John.\u003C\u002Fp>\u003Cp>Speaker 1: And I am Pedro. Let's get into some questions.\u003C\u002Fp>\u003Cp>Speaker 0: Recording in packs.\u003C\u002Fp>\u003Cp>Speaker 1: I'm wearing shorts. Alright. Cool. Alright. Thanks for watching.\u003C\u002Fp>\u003Cp>Oh. Alright. Thanks for watching. Please hit that like and subscribe button and feel free to hop in.\u003C\u002Fp>\u003Cp>Speaker 0: I had it the first time and then I added, added like and subscribe. I know. Right? Up to you.\u003C\u002Fp>\u003Cp>Speaker 1: I was gonna make a dumb joke about the screen share. Hello?\u003C\u002Fp>\u003Cp>Speaker 0: Sure thing, Pedro. The first question this week is, do you have maintenance windows and if so, how often?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So thanks to the facts that yeah. So thanks to the fact that we have, rolling up that we hope you contribute to the community. And until next time, good night. Good good night.\u003C\u002Fp>\u003Cp>Good good to\u003C\u002Fp>\u003Cp>Speaker 0: good night. Good day, sir.\u003C\u002Fp>\u003Cp>Speaker 1: How do yeah. Good night. Good morning. Good evening. Good night wherever you are.\u003C\u002Fp>\u003Cp>Until next time. Goodbye.\u003C\u002Fp>\u003Cp>Speaker 0: Bye.\u003C\u002Fp>\u003Cp>Speaker 1: Cheers. Have a good weekend. Cool. Toodle loo.\u003C\u002Fp>\u003Cp>Speaker 0: There was the Pedro wave.\u003C\u002Fp>","Alright. I'm recording there. I'm recording there. I'm good. Are you good? Cool. Alright. Hey, everyone. Welcome back to another episode of From the Field, q and a with P and J. I am the J in it. I am John. I am I'm not the p. And that would make me the p. That would be good. For specific user language, we'll default to the project's default language. So that's that's You wanna re That'll just redo it. Hey, everybody. Welcome back to another episode of from the field p and q and j and a and q r and d. And x y z. Now q r s t m s t? Q and a with p and w x y z z zed for you international folks. So that makes it really easy, so you're never missing any oh, whatever. This is a simple answer. I'm not no need to make it complicated. Hey, everyone. Welcome back to another episode of From the Field, q and a with p and j. I am John. And I am Pedro. Let's get into some questions. Recording in packs. I'm wearing shorts. Alright. Cool. Alright. Thanks for watching. Oh. Alright. Thanks for watching. Please hit that like and subscribe button and feel free to hop in. I had it the first time and then I added, added like and subscribe. I know. Right? Up to you. I was gonna make a dumb joke about the screen share. Hello? Sure thing, Pedro. The first question this week is, do you have maintenance windows and if so, how often? Yeah. So thanks to the facts that yeah. So thanks to the fact that we have, rolling up that we hope you contribute to the community. And until next time, good night. Good good night. Good good to good night. Good day, sir. How do yeah. Good night. Good morning. Good evening. Good night wherever you are. Until next time. Goodbye. Bye. Cheers. Have a good weekend. Cool. Toodle loo. There was the Pedro wave.",[6177,6178],"ce4d9037-d175-4aac-86b2-aa1d90bcdb7f","6f156e6f-c378-4880-8788-97aedc96f308",[],{"year":6181,"number":264,"id":6182,"episodes":6183,"show":6192},"2022","6885ffd4-fbd0-4807-a1af-f70dab59020e",[6184,6185,6186,6187,6188,6189,6190,6191,6164],"6ac57bbb-b28a-42c0-ad2b-33b5957fb48a","cc2795cc-4153-4f8f-8c08-0d74eb2e1cea","9b905dc7-f650-4a1d-b9af-f3ff02a61ca1","286f3cf5-d603-423d-9319-7f6b7cace71c","3b509ca3-24e4-4c04-8ec3-8338d4195e91","15cf3e94-adbe-47ab-86b2-5da9056c0565","da38c88f-2e8f-4c57-ad24-c5ef074188eb","9e3e30b4-3f1b-4567-9509-60d42b3c1666",{"title":6193,"slug":6194},"From the Field","from-the-field",{"id":6191,"slug":6196,"vimeo_id":6197,"description":6198,"tile":6199,"length":527,"resources":12,"people":6200,"episode_number":11,"published":6203,"title":6204,"video_transcript_html":6205,"video_transcript_text":6206,"content":12,"seo":12,"status":19,"episode_people":6207,"recommendations":6210,"season":6211},"roles-permissions-cron-insights-panel-bulk-edits","894041480","In this episode, John and Pedro answer questions about CRON jobs, Directus Insights, and bulk editing.","1a113e84-f7a3-470d-8f7f-3e66287dcc1e",[6201,6202],{"name":4361,"url":4362},{"name":4524,"url":6097},"2022-12-09","Roles and Permissions | CRON | Insights Panel | Bulk Edits","\u003Cp>Speaker 0: Hey, everyone. Welcome to a holiday special of from the field q and a with p and j. This will be the last one for the year. We're all taking a holiday break so we can focus on some actual work, but this has been fun. But, again, welcome to this holiday episode of from the field with p and j.\u003C\u002Fp>\u003Cp>I am John.\u003C\u002Fp>\u003Cp>Speaker 1: And I'm Pedro. Thanks so much for joining us this year and submitting all your questions. We we definitely appreciate all of that and all the comments to continue to improve this video series. So let's dive in for the last episode of the year into some questions. Alright.\u003C\u002Fp>\u003Cp>So first question of the episode, a lot of the users and customers we've talked to recently have asked about roles and permissions, and we've gone over this a little bit. But, John, could you could you walk us through maybe a video on creating one from scratch?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. I I think it'd just be beneficial to start one of these from scratch. So we'll pull up a screen share here and and show you guys how to create something from scratch, say, for a content editor. So alright, everyone. Let's create a new user.\u003C\u002Fp>\u003Cp>We'll go into settings and you we're do new role and permission just off from scratch. So we're gonna call them a content editor. Not admin access. We'll save this. And here's where you can enable roles.\u003C\u002Fp>\u003Cp>As you can see, everything is default deny. So what we first wanna do, we just want them to see articles and be able to create articles. So up here, you've got your full CRUD. We'll go into articles and we will give them all access to create, all access to see, and edit. But we don't want them to delete.\u003C\u002Fp>\u003Cp>So we'll not let them delete. We might as well also let them have translations access. Alright. So now we've given them access to that. Alright.\u003C\u002Fp>\u003Cp>So what I'm gonna do is I'm gonna log out and log back in as that user. Actually, first, let's assign it. So now when we log in as this new user role, we'll log in and we'll only be able to see articles. And you can see here articles. I could see article translations if I wanted.\u003C\u002Fp>\u003Cp>We'll go in and see I did not create this article as this user, so I cannot go in and I cannot edit things. Don't have permission. But we could go back into articles and we could create new. Alright. So hopefully, that was a great look into creating a a role and a permission from scratch.\u003C\u002Fp>\u003Cp>Hopefully, it was a deep enough dive for some of you. The next question is, is it possible to put a cron job in place to collect data from external API or external sources and then update something in the database on another cron another cron job, you know. Every day, for example, can we update something with new new information?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Absolutely. And let me show you here in this video. Alright. So we're going to do this using flows.\u003C\u002Fp>\u003Cp>I've already created one for the Stripe API. We're gonna be putting a CronJob in place to collect data from that Stripe API and update something in the database. And it's already set up, so we'll go ahead and go to trigger setup. And we'll have a few options here when we first create event hook, webhook, and schedule cron so we're gonna go ahead and do that and let's run this daily at 8 in the morning go ahead and save those changes and now this is an active API integration pulling in data to the database. So we've been asked a lot of questions over the last, you know, few weeks, few months about our insights panel, and we've got some new options coming for the insights module, in in the coming weeks months, so it might be beneficial to do a quick overview, John, of the panel in general and and maybe create a new chart and show people what's possible today and maybe hint at what's coming in the future.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Well, I I think we have some new visual layouts, like some some pie charts and multiline charts coming here in the near future. So I think, again, kind of how we did it with the first example, it might be best and most of you seem to like when we screen share anyway. So let's pull up another example of creating an insights dashboard from some information that is in the database. So alright.\u003C\u002Fp>\u003Cp>So here again, we are in my demo example. And anything that you're keeping track of in the database, you can actually put into our insights panel. So we do have a table here for metrics. We'll go and let's, let's add GitHub stars. So we'll go into here.\u003C\u002Fp>\u003Cp>We'll go into our insights panel. Let's do social media. That's what I consider GitHub, although it's not. But, we'll edit panels and we'll create a new panel. This is where there's a bunch of options here, but I just wanna see how my star growth has been.\u003C\u002Fp>\u003Cp>So, we'll click time series. Again, that collection was in metrics. We'll go with black since that's the GitHub color that I see. Then when we're looking at group aggregation, we'll do maximum. That's the maximum stars for a precision, which I will choose day.\u003C\u002Fp>\u003Cp>I wanna see per day, you know, how my growth is happening. The date field, we'll do a time stamp. Let's I don't know how far back my data goes, but let's just do 1 month. The value field will choose value because we wanna know the value. You can change the decimals here.\u003C\u002Fp>\u003Cp>We'll just keep it as we'll keep it at 0. We'll do automatic there.\u003C\u002Fp>\u003Cp>Speaker 1: I like the smooth. I like the gradient.\u003C\u002Fp>\u003Cp>Speaker 0: And then here in the filter is what we're gonna is this service, and we will do, GitHub. And then we will do one more because, what do we wanna find? We wanna find the key. And the key will equal stars. Because that is what if we go back in a second, you can see that stars is the metric in that table.\u003C\u002Fp>\u003Cp>So we'll do that. Let's add a panel so we know what it is. And icon, I don't really care, but let's pick 1. And color. Let's, let's just go with that color again.\u003C\u002Fp>\u003Cp>So we'll hit save and you can see it popped up and we'll find it and we'll make it bigger and there's our chart. And again, all of this, I'm still in edit mode, so we'll bring it down here. And all of this is being fed from our metrics table. Again, you see GitHub, stars, there's the value itself, timestamp is there. There's additional things as well, but we have chose not to show them.\u003C\u002Fp>\u003Cp>But that's how you create an insights panel. Alright. So hopefully, that was deep enough. I know I do a lot of high level overview. So, hopefully, you got a good view of how to create an insights panel and are excited about some of the upcoming visualizations that we're putting into the platform.\u003C\u002Fp>\u003Cp>Let's do a little bit something simpler, Pedro. How about are you able to bulk edit items?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Of course. Let me show you how that works here. Okay. So bulk editing records within Directus is probably easier than you might even assume.\u003C\u002Fp>\u003Cp>So, of course, we have the search and we can add filters and dive through each product individually, but you know what? Today, let's just select all. And because I'm feeling spicy, I'm gonna batch edit all 26 products in my table and turn this into a 99¢ store. And there you have it. All of the records have been updated.\u003C\u002Fp>\u003Cp>99¢ as the price. And that's bulk editing.\u003C\u002Fp>\u003Cp>Speaker 0: I think that's all the questions that we had. Again, we wanna thank you for everything. Anything else from your end, Pedro?\u003C\u002Fp>\u003Cp>Speaker 1: No. Just, you know, appreciate everything and all the attention that we've gotten on these videos so far. We're definitely looking to grow this channel and grow this series. But, again, thanks for watching. If you like these videos, please like and subscribe, comment on them, and don't forget to hop into one of our communities, whether it's Discord or GitHub, and post your questions there as well and follow along.\u003C\u002Fp>\u003Cp>Happy holidays to everyone, and we'll see you next year.\u003C\u002Fp>","Hey, everyone. Welcome to a holiday special of from the field q and a with p and j. This will be the last one for the year. We're all taking a holiday break so we can focus on some actual work, but this has been fun. But, again, welcome to this holiday episode of from the field with p and j. I am John. And I'm Pedro. Thanks so much for joining us this year and submitting all your questions. We we definitely appreciate all of that and all the comments to continue to improve this video series. So let's dive in for the last episode of the year into some questions. Alright. So first question of the episode, a lot of the users and customers we've talked to recently have asked about roles and permissions, and we've gone over this a little bit. But, John, could you could you walk us through maybe a video on creating one from scratch? Yeah. I I think it'd just be beneficial to start one of these from scratch. So we'll pull up a screen share here and and show you guys how to create something from scratch, say, for a content editor. So alright, everyone. Let's create a new user. We'll go into settings and you we're do new role and permission just off from scratch. So we're gonna call them a content editor. Not admin access. We'll save this. And here's where you can enable roles. As you can see, everything is default deny. So what we first wanna do, we just want them to see articles and be able to create articles. So up here, you've got your full CRUD. We'll go into articles and we will give them all access to create, all access to see, and edit. But we don't want them to delete. So we'll not let them delete. We might as well also let them have translations access. Alright. So now we've given them access to that. Alright. So what I'm gonna do is I'm gonna log out and log back in as that user. Actually, first, let's assign it. So now when we log in as this new user role, we'll log in and we'll only be able to see articles. And you can see here articles. I could see article translations if I wanted. We'll go in and see I did not create this article as this user, so I cannot go in and I cannot edit things. Don't have permission. But we could go back into articles and we could create new. Alright. So hopefully, that was a great look into creating a a role and a permission from scratch. Hopefully, it was a deep enough dive for some of you. The next question is, is it possible to put a cron job in place to collect data from external API or external sources and then update something in the database on another cron another cron job, you know. Every day, for example, can we update something with new new information? Yeah. Absolutely. And let me show you here in this video. Alright. So we're going to do this using flows. I've already created one for the Stripe API. We're gonna be putting a CronJob in place to collect data from that Stripe API and update something in the database. And it's already set up, so we'll go ahead and go to trigger setup. And we'll have a few options here when we first create event hook, webhook, and schedule cron so we're gonna go ahead and do that and let's run this daily at 8 in the morning go ahead and save those changes and now this is an active API integration pulling in data to the database. So we've been asked a lot of questions over the last, you know, few weeks, few months about our insights panel, and we've got some new options coming for the insights module, in in the coming weeks months, so it might be beneficial to do a quick overview, John, of the panel in general and and maybe create a new chart and show people what's possible today and maybe hint at what's coming in the future. Yeah. Well, I I think we have some new visual layouts, like some some pie charts and multiline charts coming here in the near future. So I think, again, kind of how we did it with the first example, it might be best and most of you seem to like when we screen share anyway. So let's pull up another example of creating an insights dashboard from some information that is in the database. So alright. So here again, we are in my demo example. And anything that you're keeping track of in the database, you can actually put into our insights panel. So we do have a table here for metrics. We'll go and let's, let's add GitHub stars. So we'll go into here. We'll go into our insights panel. Let's do social media. That's what I consider GitHub, although it's not. But, we'll edit panels and we'll create a new panel. This is where there's a bunch of options here, but I just wanna see how my star growth has been. So, we'll click time series. Again, that collection was in metrics. We'll go with black since that's the GitHub color that I see. Then when we're looking at group aggregation, we'll do maximum. That's the maximum stars for a precision, which I will choose day. I wanna see per day, you know, how my growth is happening. The date field, we'll do a time stamp. Let's I don't know how far back my data goes, but let's just do 1 month. The value field will choose value because we wanna know the value. You can change the decimals here. We'll just keep it as we'll keep it at 0. We'll do automatic there. I like the smooth. I like the gradient. And then here in the filter is what we're gonna is this service, and we will do, GitHub. And then we will do one more because, what do we wanna find? We wanna find the key. And the key will equal stars. Because that is what if we go back in a second, you can see that stars is the metric in that table. So we'll do that. Let's add a panel so we know what it is. And icon, I don't really care, but let's pick 1. And color. Let's, let's just go with that color again. So we'll hit save and you can see it popped up and we'll find it and we'll make it bigger and there's our chart. And again, all of this, I'm still in edit mode, so we'll bring it down here. And all of this is being fed from our metrics table. Again, you see GitHub, stars, there's the value itself, timestamp is there. There's additional things as well, but we have chose not to show them. But that's how you create an insights panel. Alright. So hopefully, that was deep enough. I know I do a lot of high level overview. So, hopefully, you got a good view of how to create an insights panel and are excited about some of the upcoming visualizations that we're putting into the platform. Let's do a little bit something simpler, Pedro. How about are you able to bulk edit items? Yeah. Of course. Let me show you how that works here. Okay. So bulk editing records within Directus is probably easier than you might even assume. So, of course, we have the search and we can add filters and dive through each product individually, but you know what? Today, let's just select all. And because I'm feeling spicy, I'm gonna batch edit all 26 products in my table and turn this into a 99¢ store. And there you have it. All of the records have been updated. 99¢ as the price. And that's bulk editing. I think that's all the questions that we had. Again, we wanna thank you for everything. Anything else from your end, Pedro? No. Just, you know, appreciate everything and all the attention that we've gotten on these videos so far. We're definitely looking to grow this channel and grow this series. But, again, thanks for watching. If you like these videos, please like and subscribe, comment on them, and don't forget to hop into one of our communities, whether it's Discord or GitHub, and post your questions there as well and follow along. Happy holidays to everyone, and we'll see you next year.",[6208,6209],"d4fe75dd-63b5-4932-8ba7-52b297eabe6c","50464a4b-cd04-483b-8aa5-05cc94aefa9b",[],{"year":6181,"number":264,"id":6182,"episodes":6212,"show":6213},[6184,6185,6186,6187,6188,6189,6190,6191,6164],{"title":6193,"slug":6194},{"id":6190,"slug":6215,"vimeo_id":6216,"description":6217,"tile":6199,"length":213,"resources":12,"people":6218,"episode_number":129,"published":6221,"title":6222,"video_transcript_html":6223,"video_transcript_text":6224,"content":12,"seo":12,"status":19,"episode_people":6225,"recommendations":6228,"season":6229},"graphql-nested-queries-mach-compliance-apis","894041559","In this episode, John and Pedro answer questions about fetching data, MACH, and the APIs offered by Directus.",[6219,6220],{"name":4361,"url":4362},{"name":4524,"url":6097},"2022-12-02","GraphQL Nested Queries | MACH Compliance | APIs","\u003Cp>Speaker 0: Hello, everyone. Welcome to another episode of From the Field q and a with p and j. My name is Pedro,\u003C\u002Fp>\u003Cp>Speaker 1: And I am John. Let's dive into some questions that we've had this past week. I will start off because I want to. So first question is, is there a feedback mechanism for content?\u003C\u002Fp>\u003Cp>Speaker 0: So yeah. Yes. You are able to add comments and even at tag users on specific records as you can see in this video. Cool. Mhmm.\u003C\u002Fp>\u003Cp>Mhmm. Next question. User, I'm not gonna try to pronounce that, on GitHub asks, is it possible to query data from multiple databases? Oh, this is a good one. And we hear this a lot, actually.\u003C\u002Fp>\u003Cp>So\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So it was it was answered by Wrike, but we do get this question a lot. By the way, Wrike is always watching. So if you ever ask a question in GitHub or Discord, he's probably already on top of it. But the answer is\u003C\u002Fp>\u003Cp>Speaker 0: CTO and cofounder. True. If you don't know.\u003C\u002Fp>\u003Cp>Speaker 1: Also guitar player, extraordinary. So he does everything. This is true. But yeah. So currently, the way that Directus is set up, it does connect to a single database.\u003C\u002Fp>\u003Cp>That is the way that it is currently done, but stay tuned. So a little teaser there for you. Alright, Pedro. I think we have a I think we have a list of a few questions that might have some simple answers. So let's kinda hit a couple of them real quickly.\u003C\u002Fp>\u003Cp>Okay. Great. So the the first question is, do you support GraphQL nested queries?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Yes. We do. Alright. Up on that one.\u003C\u002Fp>\u003Cp>Speaker 1: Does your platform allow for reading, query, and content via API?\u003C\u002Fp>\u003Cp>Speaker 0: Yes. Let's start the yes counter. That's 2 yeses.\u003C\u002Fp>\u003Cp>Speaker 1: Does your platform allow for writing mutating content via API? Oh, do we\u003C\u002Fp>\u003Cp>Speaker 0: have a 3? Do we have a 3? Yes. We have 3 yeses. That is a yes for that one.\u003C\u002Fp>\u003Cp>Speaker 1: And since we're on the API train, are your APIs and platform mock compliant? So microservices, API first, composable, and headless.\u003C\u002Fp>\u003Cp>Speaker 0: Yep. That is that is the ding ding ding. You have a winner. All yes. Yes on there.\u003C\u002Fp>\u003Cp>Speaker 1: Kinda kinda convenient how all of those were yeses. I wonder how that was set up.\u003C\u002Fp>\u003Cp>Speaker 0: I wonder.\u003C\u002Fp>\u003Cp>Speaker 1: It's how we do it.\u003C\u002Fp>\u003Cp>Speaker 0: So next question, and this is our final question, I think. So what percentage of the functionality of your platform or of the app is available through the APIs? Is there any functionality only available through the UI that is not available for from, UI that is not available for from, the API? And this is a question we get all the time and one of my favorites, but I'll let you answer it,\u003C\u002Fp>\u003Cp>Speaker 1: John. Yeah. I just like answering it because I'd rather have Jonathan, our sales engineer, come on and answer it. He gets very excited when he's talking about this. Yeah.\u003C\u002Fp>\u003Cp>Whenever you're interacting with the app, the inter the data studio itself, all of that is driven by the API. So every single click, every data change, everything that you do in Directus is done via API. So Mhmm. It's very exciting. He he gets very giddy when he's telling that.\u003C\u002Fp>\u003Cp>But, yeah, everything that you can do in direct us is a 100% API compatible. So you can use those things, you know, some aggregate, whatever you wanna do via the APIs externally as well. So we are 100% API friendly.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And if you think about the architecture behind directus and what it is, it's like you have your your database, Directus installed on top, and then on top of that, there's the no code app. So Directus being the API, so it's database, APIs, app built on top of those APIs. Very cool.\u003C\u002Fp>\u003Cp>Speaker 1: It's like a API sandwich, kinda.\u003C\u002Fp>\u003Cp>Speaker 0: A API sandwich. Yeah. I like that. Sure. Alrighty.\u003C\u002Fp>\u003Cp>Thanks for watching. We hope you're keeping warm. Please like and subscribe to this YouTube channel and to this, phenomenal q and a with p and j that we do, and feel free to hop into one of our communities, GitHub, Discord. We live inside of Discord as a company, So if you wanna come chat with John or myself, we are there probably 247 because we do not sleep. Yeah.\u003C\u002Fp>\u003Cp>So always there to help answer your questions, and, we hope you contribute to the community. And until next time, good night. Good good night. Good good\u003C\u002Fp>\u003Cp>Speaker 1: to good night. Good day, sir.\u003C\u002Fp>\u003Cp>Speaker 0: I do. Yeah. Good night. Good morning. Good evening.\u003C\u002Fp>\u003Cp>Good night wherever you are. Until next time. Goodbye. Bye\u003C\u002Fp>","Hello, everyone. Welcome to another episode of From the Field q and a with p and j. My name is Pedro, And I am John. Let's dive into some questions that we've had this past week. I will start off because I want to. So first question is, is there a feedback mechanism for content? So yeah. Yes. You are able to add comments and even at tag users on specific records as you can see in this video. Cool. Mhmm. Mhmm. Next question. User, I'm not gonna try to pronounce that, on GitHub asks, is it possible to query data from multiple databases? Oh, this is a good one. And we hear this a lot, actually. So Yeah. So it was it was answered by Wrike, but we do get this question a lot. By the way, Wrike is always watching. So if you ever ask a question in GitHub or Discord, he's probably already on top of it. But the answer is CTO and cofounder. True. If you don't know. Also guitar player, extraordinary. So he does everything. This is true. But yeah. So currently, the way that Directus is set up, it does connect to a single database. That is the way that it is currently done, but stay tuned. So a little teaser there for you. Alright, Pedro. I think we have a I think we have a list of a few questions that might have some simple answers. So let's kinda hit a couple of them real quickly. Okay. Great. So the the first question is, do you support GraphQL nested queries? Yeah. Yes. We do. Alright. Up on that one. Does your platform allow for reading, query, and content via API? Yes. Let's start the yes counter. That's 2 yeses. Does your platform allow for writing mutating content via API? Oh, do we have a 3? Do we have a 3? Yes. We have 3 yeses. That is a yes for that one. And since we're on the API train, are your APIs and platform mock compliant? So microservices, API first, composable, and headless. Yep. That is that is the ding ding ding. You have a winner. All yes. Yes on there. Kinda kinda convenient how all of those were yeses. I wonder how that was set up. I wonder. It's how we do it. So next question, and this is our final question, I think. So what percentage of the functionality of your platform or of the app is available through the APIs? Is there any functionality only available through the UI that is not available for from, UI that is not available for from, the API? And this is a question we get all the time and one of my favorites, but I'll let you answer it, John. Yeah. I just like answering it because I'd rather have Jonathan, our sales engineer, come on and answer it. He gets very excited when he's talking about this. Yeah. Whenever you're interacting with the app, the inter the data studio itself, all of that is driven by the API. So every single click, every data change, everything that you do in Directus is done via API. So Mhmm. It's very exciting. He he gets very giddy when he's telling that. But, yeah, everything that you can do in direct us is a 100% API compatible. So you can use those things, you know, some aggregate, whatever you wanna do via the APIs externally as well. So we are 100% API friendly. Yeah. And if you think about the architecture behind directus and what it is, it's like you have your your database, Directus installed on top, and then on top of that, there's the no code app. So Directus being the API, so it's database, APIs, app built on top of those APIs. Very cool. It's like a API sandwich, kinda. A API sandwich. Yeah. I like that. Sure. Alrighty. Thanks for watching. We hope you're keeping warm. Please like and subscribe to this YouTube channel and to this, phenomenal q and a with p and j that we do, and feel free to hop into one of our communities, GitHub, Discord. We live inside of Discord as a company, So if you wanna come chat with John or myself, we are there probably 247 because we do not sleep. Yeah. So always there to help answer your questions, and, we hope you contribute to the community. And until next time, good night. Good good night. Good good to good night. Good day, sir. I do. Yeah. Good night. Good morning. Good evening. Good night wherever you are. Until next time. Goodbye. Bye",[6226,6227],"1cd72465-94dc-4b86-8f4e-fe7906feeda7","f79cb8c9-d764-41ac-afe1-7607a79ec5aa",[],{"year":6181,"number":264,"id":6182,"episodes":6230,"show":6231},[6184,6185,6186,6187,6188,6189,6190,6191,6164],{"title":6193,"slug":6194},{"id":6189,"slug":6233,"vimeo_id":6234,"description":6235,"tile":6199,"length":159,"resources":12,"people":6236,"episode_number":195,"published":6239,"title":6240,"video_transcript_html":6241,"video_transcript_text":6242,"content":12,"seo":12,"status":19,"episode_people":6243,"recommendations":6246,"season":6247},"track-changes-grammar-checks-version-history","894041601","In this episode, John and Pedro answer questions about tracking changes, expiration dates, and checking content for correctness.",[6237,6238],{"name":4361,"url":4362},{"name":4524,"url":6097},"2022-11-25","Track Changes | Grammar Checks | Version History","\u003Cp>Speaker 0: Hey, everyone. Welcome back to another episode of From the Field q and a with p and j. I am John.\u003C\u002Fp>\u003Cp>Speaker 1: And I am Pedro. So let's dive into some questions from this week. I will start. Okay. Okay.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. First question is, as a CMS author, am I able to track any changes made to pages I own or am watching? Yes.\u003C\u002Fp>\u003Cp>Speaker 1: You can. Directus activities and revisions provide full accountability tracking with, flows used to trigger notifications of changes. So just create a simple flow, and then when specific pages you own are edited, you can get a notification of your choosing. So next question. Can I add an expiration date to my records?\u003C\u002Fp>\u003Cp>Speaker 0: Yes. We support date fields. We actually have a pick option when you're creating a new field. You can pick a date field option and, you know, set it to a date that you would want it to expire and you can have a flow automatically clean that up. You can have it archive it, hard delete it.\u003C\u002Fp>\u003Cp>You can move it to a hidden table. You can do any sort of data that you want. You can see here in the screen where you can add that date record, that date field. So it's very simple to set up.\u003C\u002Fp>\u003Cp>Speaker 1: Awesome.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Can I check content for proper grammar and flag issues?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Of course. So within the Direct to Studio app, you know, it's obviously a web based application, so any browser or third party plugins or tools or utilities may be used by the authors or implemented as as workflow validations during their save operations. So I I personally use Grammarly a lot actually, within Chrome. So, you know, just one use case there.\u003C\u002Fp>\u003Cp>Speaker 0: And if anybody can figure out how to have us have proper grammar as you've probably heard through some of our our videos, if we can have a video version of that, that would be greatly appreciated.\u003C\u002Fp>\u003Cp>Speaker 1: But Grammarly video edition. This video is not sponsored.\u003C\u002Fp>\u003Cp>Speaker 0: No. Not sponsored hashtag.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. Moving along. Can I view version history of content? So is there some sort of version history of all the updates going on?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So this is role based and permissions based. So if you allow someone to have, revision access and revision version history, you know, usually admins have it, but you can enable other people to have it as well. You can go in and see who has made changes, what the changes were done. You can also have it be reverted back to a previous version as well.\u003C\u002Fp>\u003Cp>So if you don't like the content that they changed, you can go back as an admin and change it back to whatever previous version was as well. But, very thorough revision tracking, activity tracking, that is one of the things, you know, we want admins and people who are in charge to be able to see that content that's changing. And if something just didn't work, you know, maybe further your audience, they didn't like a picture better than a different picture, you can go back and change it to a previous version. So very simple to do with revision tracking. Alright.\u003C\u002Fp>\u003Cp>Alright. Alright. Thanks y'all for following along this week as we answered some of your questions. As always, don't forget to like and subscribe here on YouTube. If you hop along into some of our communities like GitHub and Discord, these are where we source some of these questions from.\u003C\u002Fp>\u003Cp>We also answer them live throughout the week as well. Our whole team is in there communicating with you. So thanks again. Tune in next time for another episode of from the field, and we'll see you then. Bye, y'all.\u003C\u002Fp>\u003Cp>Speaker 1: Bye.\u003C\u002Fp>","Hey, everyone. Welcome back to another episode of From the Field q and a with p and j. I am John. And I am Pedro. So let's dive into some questions from this week. I will start. Okay. Okay. Alright. First question is, as a CMS author, am I able to track any changes made to pages I own or am watching? Yes. You can. Directus activities and revisions provide full accountability tracking with, flows used to trigger notifications of changes. So just create a simple flow, and then when specific pages you own are edited, you can get a notification of your choosing. So next question. Can I add an expiration date to my records? Yes. We support date fields. We actually have a pick option when you're creating a new field. You can pick a date field option and, you know, set it to a date that you would want it to expire and you can have a flow automatically clean that up. You can have it archive it, hard delete it. You can move it to a hidden table. You can do any sort of data that you want. You can see here in the screen where you can add that date record, that date field. So it's very simple to set up. Awesome. Alright. Can I check content for proper grammar and flag issues? Yeah. Of course. So within the Direct to Studio app, you know, it's obviously a web based application, so any browser or third party plugins or tools or utilities may be used by the authors or implemented as as workflow validations during their save operations. So I I personally use Grammarly a lot actually, within Chrome. So, you know, just one use case there. And if anybody can figure out how to have us have proper grammar as you've probably heard through some of our our videos, if we can have a video version of that, that would be greatly appreciated. But Grammarly video edition. This video is not sponsored. No. Not sponsored hashtag. Alright. Moving along. Can I view version history of content? So is there some sort of version history of all the updates going on? Yeah. So this is role based and permissions based. So if you allow someone to have, revision access and revision version history, you know, usually admins have it, but you can enable other people to have it as well. You can go in and see who has made changes, what the changes were done. You can also have it be reverted back to a previous version as well. So if you don't like the content that they changed, you can go back as an admin and change it back to whatever previous version was as well. But, very thorough revision tracking, activity tracking, that is one of the things, you know, we want admins and people who are in charge to be able to see that content that's changing. And if something just didn't work, you know, maybe further your audience, they didn't like a picture better than a different picture, you can go back and change it to a previous version. So very simple to do with revision tracking. Alright. Alright. Alright. Thanks y'all for following along this week as we answered some of your questions. As always, don't forget to like and subscribe here on YouTube. If you hop along into some of our communities like GitHub and Discord, these are where we source some of these questions from. We also answer them live throughout the week as well. Our whole team is in there communicating with you. So thanks again. Tune in next time for another episode of from the field, and we'll see you then. Bye, y'all. Bye.",[6244,6245],"05c80fe3-8d56-4148-8aa9-e342f4408c7a","3051f7de-420d-4891-ac56-1533e8459af0",[],{"year":6181,"number":264,"id":6182,"episodes":6248,"show":6249},[6184,6185,6186,6187,6188,6189,6190,6191,6164],{"title":6193,"slug":6194},{"id":6188,"slug":6251,"vimeo_id":6252,"description":6253,"tile":6199,"length":129,"resources":12,"people":6254,"episode_number":213,"published":6257,"title":6258,"video_transcript_html":6259,"video_transcript_text":6260,"content":12,"seo":12,"status":19,"episode_people":6261,"recommendations":6264,"season":6265},"languages-translations-roles","894041635","In this episode, John and Pedro answer questions about multi-language support, locale-based permissions, and the editing workflow with languages.",[6255,6256],{"name":4361,"url":4362},{"name":4524,"url":6097},"2022-11-18","Languages | Translations | Roles","\u003Cp>Speaker 0: Hello everyone. Welcome to another episode of From the Field Q and A with P and J. My name is Pedro.\u003C\u002Fp>\u003Cp>Speaker 1: And I am John. Let's get into some questions that we've received this past week. Pedro, I know that you love starting\u003C\u002Fp>\u003Cp>Speaker 0: and regions, so I think most of the questions we've put together for this week are around that, centered around that. The app translations, the content translations, everything. So let's dive into it. The first question is, can you have different default languages for different users in the system? Oh, that's very cool.\u003C\u002Fp>\u003Cp>Speaker 1: Yes. This is a simple setting in the user's account. You can see on the screen share that I recorded earlier today that if you go into a specific user down near the bottom of the page, you can set the default language for that specific user. Again, this is just for the app interface itself. It's not for the content.\u003C\u002Fp>\u003Cp>But when that user logs in, they will see whatever language is their default.\u003C\u002Fp>\u003Cp>Speaker 0: And I believe you can do that per user or per role. Right? Even if it's a role level, You can go in\u003C\u002Fp>\u003Cp>Speaker 1: and set that role. You know, if you have a 100 users, you could have well, I mean, we have 58 languages in the system right now. So, theoretically, you could have 58. But we can add more as we discussed, I think, in a previous video. So\u003C\u002Fp>\u003Cp>Speaker 0: Sweet.\u003C\u002Fp>\u003Cp>Speaker 1: Yes. As as many users as you have, they can all have different default languages.\u003C\u002Fp>\u003Cp>Speaker 0: So Cool. Good.\u003C\u002Fp>\u003Cp>Speaker 1: We'll go on to the next one, another language question.\u003C\u002Fp>\u003Cp>Speaker 0: Mhmm.\u003C\u002Fp>\u003Cp>Speaker 1: Do you support the ability to fall back to a secondary language? For example, if French isn't updated, then fall back to the default for that user.\u003C\u002Fp>\u003Cp>Speaker 0: So, yes, any non localized or missing translations for a specific user language will default to the project's default language.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. Next one.\u003C\u002Fp>\u003Cp>Speaker 0: Next one. So does your platform allow locale based permissions? So for example, a French translator role with access only to French and the source language.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So so if any of you out there watching have ever seen a demo from our sales engineer, Jonathan, this is his favorite thing to show off. I've actually gone back and recreated it, because I've seen it 500 times. But, yeah, you can have specific users. So let's say in the screen recording, for example, we're setting up a France French translator.\u003C\u002Fp>\u003Cp>You can go in and you can have them see the base language, which in this scenario, we've set it to English, and then the language that they're translating to, which is French. So you can have them see those languages. And on the editing side, on the content updating side, you can have them only able to update French. So they can see the English, they can translate it, but they can't touch the English. So you can set up this for any language, any sort of content translator that you may have on hand or on your team.\u003C\u002Fp>\u003Cp>But it is easily set up set up able set up able\u003C\u002Fp>\u003Cp>Speaker 0: Set up able. I like this one. That's a\u003C\u002Fp>\u003Cp>Speaker 1: new one. It works. Whatever. Alright. Next question.\u003C\u002Fp>\u003Cp>We'll we'll we'll follow-up here. How do editors trigger translations? So that kind of follows into this question. How do editors trigger translations?\u003C\u002Fp>\u003Cp>Speaker 0: Right. So there are a lot of ways to do this. Administrators and developers could use the the Directus flows, the SDK, the APIs to integrate with external translation services. That's actually really cool to automate that. Flows support manual trigger options for for single or multiple items.\u003C\u002Fp>\u003Cp>So, for example, you could implement a flow that, you know, when a base language is filled in and saved, you send an API call to an AI translation service somewhere out there of your choosing, there's plenty of those services available, have it translated, and then send back via the API to input into the translation field. So it's like automatic AI powered translations. And then you could have that flow send a notification or email to an actual translator. That's another option, that and and that record is is ready for translation at that point. So the short answer is, yeah, lots of, lots of ways to do it.\u003C\u002Fp>\u003Cp>But I think the flows is is kind of the coolest way. Automate it.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Yeah. I think flows in general, a lot of you in in the comments in some of these videos have been asking about flows. I know that we're putting out videos some at a time. We've got this flows cookbook coming.\u003C\u002Fp>\u003Cp>So Flow's is something that I love doing. I love workflow automations. I love taking things off of my plate. So, you know, not that we suggest it, but I think Flow's is the most fun way to do it.\u003C\u002Fp>\u003Cp>Speaker 0: So And for our last question, is there a way to see if content has been translated? So I'm imagining the question is probably pointing to, you know, if we have a piece of content, how many languages has it been translated to and maybe the status of if that's in progress to be translated. I think that's what we're getting at. Right, John?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. I would assume, let's say, either if you're using an AI translator or a manual person translating, have they filled in all the information to have that content translated? So one of the nice things about Directus is that out of the box, it comes with this content translation view, this layout, where you can see those side by side languages. But in those layouts, you also have a drop down that you can see here on the screen recording. You can see how much of each language has been translated.\u003C\u002Fp>\u003Cp>So however many boxes you're translating, there's, you know, gray for nothing. Red needs more translation. Green all the way to the end means that that language has been fully translated. So there is a nice overview of that and a way to see if all of your content has been edited. Nice.\u003C\u002Fp>\u003Cp>So\u003C\u002Fp>\u003Cp>Speaker 0: I'm glad we have a I'm glad we have a screen share for that one because explaining it verbally is a little bit more difficult than just show showing makes it very simple.\u003C\u002Fp>\u003Cp>Speaker 1: Right. I mean, plus then, you can put that screen over our faces, which it's great to hear us all all the time. But people wanna see it in action. And it's great to actually see it in action when we're showing these things off every week. People are amazed by this.\u003C\u002Fp>\u003Cp>You know, there's a lot of people who use it who use different languages and translations. We have multinational companies using this. So the interface is really useful, and it's really useful way to see what has been translated. So\u003C\u002Fp>\u003Cp>Speaker 0: Awesome.\u003C\u002Fp>\u003Cp>Speaker 1: We've gotten that question a lot. But\u003C\u002Fp>\u003Cp>Speaker 0: thank you for watching this week. We've actually heard a lot of feedback from you on YouTube, from our Discord, from our GitHub, so please continue that feedback as we continue to create these videos, and, we'll continue to improve them based on your questions. So, you know, hop into our Discord, hop into our GitHub, contribute to the community, and we will see you next time. And like and subscribe. Oh, always like and subscribe.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Alright. Bye y'all.\u003C\u002Fp>","Hello everyone. Welcome to another episode of From the Field Q and A with P and J. My name is Pedro. And I am John. Let's get into some questions that we've received this past week. Pedro, I know that you love starting and regions, so I think most of the questions we've put together for this week are around that, centered around that. The app translations, the content translations, everything. So let's dive into it. The first question is, can you have different default languages for different users in the system? Oh, that's very cool. Yes. This is a simple setting in the user's account. You can see on the screen share that I recorded earlier today that if you go into a specific user down near the bottom of the page, you can set the default language for that specific user. Again, this is just for the app interface itself. It's not for the content. But when that user logs in, they will see whatever language is their default. And I believe you can do that per user or per role. Right? Even if it's a role level, You can go in and set that role. You know, if you have a 100 users, you could have well, I mean, we have 58 languages in the system right now. So, theoretically, you could have 58. But we can add more as we discussed, I think, in a previous video. So Sweet. Yes. As as many users as you have, they can all have different default languages. So Cool. Good. We'll go on to the next one, another language question. Mhmm. Do you support the ability to fall back to a secondary language? For example, if French isn't updated, then fall back to the default for that user. So, yes, any non localized or missing translations for a specific user language will default to the project's default language. Alright. Next one. Next one. So does your platform allow locale based permissions? So for example, a French translator role with access only to French and the source language. Yeah. So so if any of you out there watching have ever seen a demo from our sales engineer, Jonathan, this is his favorite thing to show off. I've actually gone back and recreated it, because I've seen it 500 times. But, yeah, you can have specific users. So let's say in the screen recording, for example, we're setting up a France French translator. You can go in and you can have them see the base language, which in this scenario, we've set it to English, and then the language that they're translating to, which is French. So you can have them see those languages. And on the editing side, on the content updating side, you can have them only able to update French. So they can see the English, they can translate it, but they can't touch the English. So you can set up this for any language, any sort of content translator that you may have on hand or on your team. But it is easily set up set up able set up able Set up able. I like this one. That's a new one. It works. Whatever. Alright. Next question. We'll we'll we'll follow-up here. How do editors trigger translations? So that kind of follows into this question. How do editors trigger translations? Right. So there are a lot of ways to do this. Administrators and developers could use the the Directus flows, the SDK, the APIs to integrate with external translation services. That's actually really cool to automate that. Flows support manual trigger options for for single or multiple items. So, for example, you could implement a flow that, you know, when a base language is filled in and saved, you send an API call to an AI translation service somewhere out there of your choosing, there's plenty of those services available, have it translated, and then send back via the API to input into the translation field. So it's like automatic AI powered translations. And then you could have that flow send a notification or email to an actual translator. That's another option, that and and that record is is ready for translation at that point. So the short answer is, yeah, lots of, lots of ways to do it. But I think the flows is is kind of the coolest way. Automate it. Yeah. Yeah. I think flows in general, a lot of you in in the comments in some of these videos have been asking about flows. I know that we're putting out videos some at a time. We've got this flows cookbook coming. So Flow's is something that I love doing. I love workflow automations. I love taking things off of my plate. So, you know, not that we suggest it, but I think Flow's is the most fun way to do it. So And for our last question, is there a way to see if content has been translated? So I'm imagining the question is probably pointing to, you know, if we have a piece of content, how many languages has it been translated to and maybe the status of if that's in progress to be translated. I think that's what we're getting at. Right, John? Yeah. I would assume, let's say, either if you're using an AI translator or a manual person translating, have they filled in all the information to have that content translated? So one of the nice things about Directus is that out of the box, it comes with this content translation view, this layout, where you can see those side by side languages. But in those layouts, you also have a drop down that you can see here on the screen recording. You can see how much of each language has been translated. So however many boxes you're translating, there's, you know, gray for nothing. Red needs more translation. Green all the way to the end means that that language has been fully translated. So there is a nice overview of that and a way to see if all of your content has been edited. Nice. So I'm glad we have a I'm glad we have a screen share for that one because explaining it verbally is a little bit more difficult than just show showing makes it very simple. Right. I mean, plus then, you can put that screen over our faces, which it's great to hear us all all the time. But people wanna see it in action. And it's great to actually see it in action when we're showing these things off every week. People are amazed by this. You know, there's a lot of people who use it who use different languages and translations. We have multinational companies using this. So the interface is really useful, and it's really useful way to see what has been translated. So Awesome. We've gotten that question a lot. But thank you for watching this week. We've actually heard a lot of feedback from you on YouTube, from our Discord, from our GitHub, so please continue that feedback as we continue to create these videos, and, we'll continue to improve them based on your questions. So, you know, hop into our Discord, hop into our GitHub, contribute to the community, and we will see you next time. And like and subscribe. Oh, always like and subscribe. Yeah. Alright. Bye y'all.",[6262,6263],"62ad5daf-7ea8-4e24-b23d-ae2e0e4e4d5a","d35898a8-a425-4db6-8ae2-b34c46614dfc",[],{"year":6181,"number":264,"id":6182,"episodes":6266,"show":6267},[6184,6185,6186,6187,6188,6189,6190,6191,6164],{"title":6193,"slug":6194},{"id":6187,"slug":6269,"vimeo_id":6270,"description":6271,"tile":6199,"length":264,"resources":12,"people":6272,"episode_number":159,"published":6275,"title":6276,"video_transcript_html":6277,"video_transcript_text":6278,"content":12,"seo":12,"status":19,"episode_people":6279,"recommendations":6282,"season":6283},"notifications-bulk-publishing-localizations-sso","894041704","In this episode, John and Pedro answer questions about internationalization, alerting to actions needed, and SSO support.",[6273,6274],{"name":4361,"url":4362},{"name":4524,"url":6097},"2022-11-11","Notifications | Bulk Publishing | Localizations | SSO","\u003Cp>Speaker 0: Hey, everyone. Welcome to another episode of from the field q and a with P&J. I'm John.\u003C\u002Fp>\u003Cp>Speaker 1: And I'm Pedro. Let's hop into some questions we've recently received from the community. John, kick it\u003C\u002Fp>\u003Cp>Speaker 0: off. You bet, Pedro. Alright. First question. As a CMS user, can I receive notifications when content that I own or my team owns and it requires my attention?\u003C\u002Fp>\u003Cp>Speaker 1: Oh, yeah. Absolutely. So Directus Flows supports automatic workflows with access to email, in app notifications, you know, you can also at tag users in Directus, which links them to specific pieces of content as well.\u003C\u002Fp>\u003Cp>Speaker 0: Great question though. So let's go to the next one. Also, Pedro, why don't you ask the next one?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Let's see here. Do you support bulk publishing across languages?\u003C\u002Fp>\u003Cp>Speaker 0: Yes. We do. With the the right data model configuration and with integrations to real time APIs, we do enable that. You can also do individual node or individual pushes as well. It's kinda just up to you.\u003C\u002Fp>\u003Cp>We're very flexible in that manner. So we do support that. Yes. So Cool. Great question.\u003C\u002Fp>\u003Cp>I had to look that one up myself, but it is a great question. So we'll go on to the next one, Pedro. And it kind of is around languages as well. So the next question is, do you support granular localizations in languages? Yep.\u003C\u002Fp>\u003Cp>We do. Alright.\u003C\u002Fp>\u003Cp>Speaker 1: We do. We sure do.\u003C\u002Fp>\u003Cp>Speaker 0: Very easy one, but great question, but very easy answer. Yes. We do.\u003C\u002Fp>\u003Cp>Speaker 1: I love yes or no questions.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. They're they're always good. Alright. Alright. So I think, we had one more good one this week that a lot of people ask.\u003C\u002Fp>\u003Cp>So the last one, Pedro, why don't you ask the last one?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So does Directus Cloud support SSO or single sign on?\u003C\u002Fp>\u003Cp>Speaker 0: Yes. Directus Enterprise Cloud does offer SSO capabilities. We support OAuth, LDAP, OpenID, and SAML as well. So we do have several SSO options. I know I probably had that question 5 or 6 times this week talking to people.\u003C\u002Fp>\u003Cp>So that is a very common question. But And if you have the\u003C\u002Fp>\u003Cp>Speaker 1: need for it, feel free to reach out to John or myself.\u003C\u002Fp>\u003Cp>Speaker 0: That's right. We're great to talk to. Obviously, we were really fun as well. So that's it for this week though, guys. Thanks again for watching.\u003C\u002Fp>\u003Cp>Don't forget to like and subscribe to follow along. Add any questions in the comments as well. And don't forget to hop into one of our other communities like GitHub or Discord. We're having these questions again all week, and it's a great place to see this weekly keeping up to with all these questions as well. So thanks again for tuning in, and we'll see you next time.\u003C\u002Fp>\u003Cp>Cheers.\u003C\u002Fp>","Hey, everyone. Welcome to another episode of from the field q and a with P&J. I'm John. And I'm Pedro. Let's hop into some questions we've recently received from the community. John, kick it off. You bet, Pedro. Alright. First question. As a CMS user, can I receive notifications when content that I own or my team owns and it requires my attention? Oh, yeah. Absolutely. So Directus Flows supports automatic workflows with access to email, in app notifications, you know, you can also at tag users in Directus, which links them to specific pieces of content as well. Great question though. So let's go to the next one. Also, Pedro, why don't you ask the next one? Yeah. Let's see here. Do you support bulk publishing across languages? Yes. We do. With the the right data model configuration and with integrations to real time APIs, we do enable that. You can also do individual node or individual pushes as well. It's kinda just up to you. We're very flexible in that manner. So we do support that. Yes. So Cool. Great question. I had to look that one up myself, but it is a great question. So we'll go on to the next one, Pedro. And it kind of is around languages as well. So the next question is, do you support granular localizations in languages? Yep. We do. Alright. We do. We sure do. Very easy one, but great question, but very easy answer. Yes. We do. I love yes or no questions. Yeah. They're they're always good. Alright. Alright. So I think, we had one more good one this week that a lot of people ask. So the last one, Pedro, why don't you ask the last one? Yeah. So does Directus Cloud support SSO or single sign on? Yes. Directus Enterprise Cloud does offer SSO capabilities. We support OAuth, LDAP, OpenID, and SAML as well. So we do have several SSO options. I know I probably had that question 5 or 6 times this week talking to people. So that is a very common question. But And if you have the need for it, feel free to reach out to John or myself. That's right. We're great to talk to. Obviously, we were really fun as well. So that's it for this week though, guys. Thanks again for watching. Don't forget to like and subscribe to follow along. Add any questions in the comments as well. And don't forget to hop into one of our other communities like GitHub or Discord. We're having these questions again all week, and it's a great place to see this weekly keeping up to with all these questions as well. So thanks again for tuning in, and we'll see you next time. Cheers.",[6280,6281],"14d55461-0ac8-449b-83c6-7180c950f47d","c54d1c21-b0e2-4e02-8aba-0f6e2fcd217f",[],{"year":6181,"number":264,"id":6182,"episodes":6284,"show":6285},[6184,6185,6186,6187,6188,6189,6190,6191,6164],{"title":6193,"slug":6194},{"id":6186,"slug":6287,"vimeo_id":6288,"description":6289,"tile":6199,"length":264,"resources":12,"people":6290,"episode_number":264,"published":6293,"title":6294,"video_transcript_html":6295,"video_transcript_text":6296,"content":12,"seo":12,"status":19,"episode_people":6297,"recommendations":6300,"season":6301},"renaming-collections-network-vulnerability-scans-roles-permissions","894041744","In this episode, John and Pedro answer questions about renaming collections, how we detect vulnerabilities, and our access control settings.",[6291,6292],{"name":4361,"url":4362},{"name":4524,"url":6097},"2022-11-04","Renaming Collections | Network Vulnerability Scans | Roles\u002FPermissions","\u003Cp>Speaker 0: Hey everyone. Welcome to another episode of From the Field Q and A with P&J. I'm Pedro.\u003C\u002Fp>\u003Cp>Speaker 1: And I'm John. Let's dive into some questions we've received this week from the community. Pedro, how about you start with the first one this week?\u003C\u002Fp>\u003Cp>Speaker 0: All right. So first question, is there a way to rename a collection?\u003C\u002Fp>\u003Cp>Speaker 1: There is a way to rename a collection. There's going to be some other things you need to update, some tables here shown on the screen. But you could also try exporting the collection, renaming it, and then reimporting it. That might be a little bit easier if you're technical like that. So there are a couple of ways to do it.\u003C\u002Fp>\u003Cp>Yes. All right. So the next question is, are network vulnerability scans performed against the network and systems?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. All communication happens over HTTPS currently using TLS 1.3 and all communication between internal services within Directus cloud infrastructure, happens in a private subnetwork, with a need to know access control policy.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So we're constantly monitoring for vulnerability scans and making sure that it's a very secure network. We're definitely security first here at, Directus.\u003C\u002Fp>\u003Cp>Speaker 0: Huge priority. Yep. Okay. Next question. Is there a way to have a user friendly display name for specific content type?\u003C\u002Fp>\u003Cp>Speaker 1: Yes. Actually, in the back end controls, as you can see here in this screen recording that I did earlier today, if you navigate to the table and item, just click in field, and then you can add a field name translation, which will display it how you would like to display it so it looks the way that you want it to look and not a\u003C\u002Fp>\u003Cp>Speaker 0: technical name.\u003C\u002Fp>\u003Cp>Speaker 1: Right. But very good question. Hopefully, that screen share helped. And then on to, I think, the last question this week, Pedro, how much control do I have over roles and permissions? Can I configure what certain users see versus what the public sees?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah, actually, there's a big reason. A lot of people come to direct us. We have a lot of really great and granular control. We actually just released a great YouTube video with a really high level explanation, on this. But you can create as many users as you want, and they start off as default deny, and then you can enable what you want them to have access to essentially all the way down to the current, you know, field and row level with the ability to do user variables like dollar sign, current underscore user.\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. Very granular. You can, you can obviously allow full controls for certain users and roles. But you can get very granular, which people do. So great question.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. Thanks for watching. Please like and subscribe the video, and don't forget to hop into one of our communities, whether it's Discord or GitHub, to follow along and ask questions. We'll see you next time.\u003C\u002Fp>\u003Cp>Speaker 1: Bye all.\u003C\u002Fp>","Hey everyone. Welcome to another episode of From the Field Q and A with P&J. I'm Pedro. And I'm John. Let's dive into some questions we've received this week from the community. Pedro, how about you start with the first one this week? All right. So first question, is there a way to rename a collection? There is a way to rename a collection. There's going to be some other things you need to update, some tables here shown on the screen. But you could also try exporting the collection, renaming it, and then reimporting it. That might be a little bit easier if you're technical like that. So there are a couple of ways to do it. Yes. All right. So the next question is, are network vulnerability scans performed against the network and systems? Yeah. All communication happens over HTTPS currently using TLS 1.3 and all communication between internal services within Directus cloud infrastructure, happens in a private subnetwork, with a need to know access control policy. Yeah. So we're constantly monitoring for vulnerability scans and making sure that it's a very secure network. We're definitely security first here at, Directus. Huge priority. Yep. Okay. Next question. Is there a way to have a user friendly display name for specific content type? Yes. Actually, in the back end controls, as you can see here in this screen recording that I did earlier today, if you navigate to the table and item, just click in field, and then you can add a field name translation, which will display it how you would like to display it so it looks the way that you want it to look and not a technical name. Right. But very good question. Hopefully, that screen share helped. And then on to, I think, the last question this week, Pedro, how much control do I have over roles and permissions? Can I configure what certain users see versus what the public sees? Yeah, actually, there's a big reason. A lot of people come to direct us. We have a lot of really great and granular control. We actually just released a great YouTube video with a really high level explanation, on this. But you can create as many users as you want, and they start off as default deny, and then you can enable what you want them to have access to essentially all the way down to the current, you know, field and row level with the ability to do user variables like dollar sign, current underscore user. Yeah. Very granular. You can, you can obviously allow full controls for certain users and roles. But you can get very granular, which people do. So great question. Yeah. Thanks for watching. Please like and subscribe the video, and don't forget to hop into one of our communities, whether it's Discord or GitHub, to follow along and ask questions. We'll see you next time. Bye all.",[6298,6299],"14f43c05-7e22-4ca1-af75-688252cf867b","64d7a63f-625c-426c-b70c-cb4a4df97c03",[],{"year":6181,"number":264,"id":6182,"episodes":6302,"show":6303},[6184,6185,6186,6187,6188,6189,6190,6191,6164],{"title":6193,"slug":6194},{"id":6185,"slug":6305,"vimeo_id":6306,"description":6307,"tile":6199,"length":159,"resources":12,"people":6308,"episode_number":177,"published":6311,"title":6312,"video_transcript_html":6313,"video_transcript_text":6314,"content":12,"seo":12,"status":19,"episode_people":6315,"recommendations":6318,"season":6319},"maintenance-windows-adding-languages-realtime-updates","894041788","In this episode, John and Pedro answer questions about how we manage state inside of Directus, adding languages, and realtime support.",[6309,6310],{"name":4361,"url":4362},{"name":4524,"url":6097},"2022-10-28","Maintenance Windows | Adding Languages | Realtime Updates","\u003Cp>Speaker 0: Hey, everyone. Welcome to another episode of Q and A with P&J. I'm John.\u003C\u002Fp>\u003Cp>Speaker 1: And I'm Pedro. Now let's dive into some questions we've received from the community. John, why don't you kick it off?\u003C\u002Fp>\u003Cp>Speaker 0: Sure thing. Alright. The first question that we have this week is, do you have maintenance windows? And if so, how often?\u003C\u002Fp>\u003Cp>Speaker 1: So thanks to the fact that we do rolling upgrades during our normal updates, you know, we don't experience any kind of downtime. During major updates or infrastructure changes, there may be a small maintenance window, but those are always communicated with you ahead of time and, it usually it's very brief and it won't take down your project. It will only impact your ability to log in for a brief time.\u003C\u002Fp>\u003Cp>Speaker 0: Alright. Let's move on to the next question, Pedro.\u003C\u002Fp>\u003Cp>Speaker 1: Alright. Does Directus use server side state management or stateless for custom extensions?\u003C\u002Fp>\u003Cp>Speaker 0: Great question. There's no built in state management, so it is stateless by default. That being said, your custom extensions do have access to the database, so that's a great place to store stateful information. Awesome. That's a good question.\u003C\u002Fp>\u003Cp>Speaker 1: Cool.\u003C\u002Fp>\u003Cp>Speaker 0: Onto the next. So I guess that'll be me. Yes. One of our users on GitHub, j a a d s, I'm gonna call you Jad because that's kinda rad. Jad's rad.\u003C\u002Fp>\u003Cp>I see 7 languages enabled in my direct us right now. Is there a way to add more languages?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. So, currently, we support 58 languages, but we can add any language via Crowdin. So the full list can be found, at the link on the screen. And as you can see in this video, it's very easy to add to your environment.\u003C\u002Fp>\u003Cp>Speaker 0: Awesome. Hey, everyone. John here with a quick interruption. When we record these things originally, we don't have a script. So sometimes we have to come back with a little bit of a clarification.\u003C\u002Fp>\u003Cp>But, I wanted to be clear around translations. There's the app translations, and that's when we were discussing 58 languages used to get CrowdIn. That's one way that the app handles translations, but then there's also the translations of the content. So, that also you can add any number of translations to that as well, but I wanted to make sure what we were discussing was the app being translated itself. And then what we were showing in the video was actually translating the content.\u003C\u002Fp>\u003Cp>So just a quick little clarification there. We'll get right back to the video. Thanks. Alright, Pedro. What is the next question?\u003C\u002Fp>\u003Cp>Speaker 1: Alright. So the next question, a member of our GitHub community asked, is there a way to currently get real time subscription updates in Directus, whether it's via the JavaScript SDK or GraphQL? And if there isn't, are there any other external tools I can use?\u003C\u002Fp>\u003Cp>Speaker 0: Well, our CTO, Wrike, himself answered this in GitHub, but the the answer is not currently, not to get those real time subscriptions, but we do have WebSockets launching very soon. You will be able to get those real time subscriptions. As you can see here, one of our engineers, BrainSlug, has actually been testing it out internally. Here's a really cool small feature, a chat feature that he's been testing around, but you can see here the the live real time subscriptions in action. Like Discord or GitHub or leave a comment here in YouTube.\u003C\u002Fp>\u003Cp>Again, we'll be taking questions from these comments to answer in further episodes. Thanks again. Have a great day.\u003C\u002Fp>","Hey, everyone. Welcome to another episode of Q and A with P&J. I'm John. And I'm Pedro. Now let's dive into some questions we've received from the community. John, why don't you kick it off? Sure thing. Alright. The first question that we have this week is, do you have maintenance windows? And if so, how often? So thanks to the fact that we do rolling upgrades during our normal updates, you know, we don't experience any kind of downtime. During major updates or infrastructure changes, there may be a small maintenance window, but those are always communicated with you ahead of time and, it usually it's very brief and it won't take down your project. It will only impact your ability to log in for a brief time. Alright. Let's move on to the next question, Pedro. Alright. Does Directus use server side state management or stateless for custom extensions? Great question. There's no built in state management, so it is stateless by default. That being said, your custom extensions do have access to the database, so that's a great place to store stateful information. Awesome. That's a good question. Cool. Onto the next. So I guess that'll be me. Yes. One of our users on GitHub, j a a d s, I'm gonna call you Jad because that's kinda rad. Jad's rad. I see 7 languages enabled in my direct us right now. Is there a way to add more languages? Yeah. So, currently, we support 58 languages, but we can add any language via Crowdin. So the full list can be found, at the link on the screen. And as you can see in this video, it's very easy to add to your environment. Awesome. Hey, everyone. John here with a quick interruption. When we record these things originally, we don't have a script. So sometimes we have to come back with a little bit of a clarification. But, I wanted to be clear around translations. There's the app translations, and that's when we were discussing 58 languages used to get CrowdIn. That's one way that the app handles translations, but then there's also the translations of the content. So, that also you can add any number of translations to that as well, but I wanted to make sure what we were discussing was the app being translated itself. And then what we were showing in the video was actually translating the content. So just a quick little clarification there. We'll get right back to the video. Thanks. Alright, Pedro. What is the next question? Alright. So the next question, a member of our GitHub community asked, is there a way to currently get real time subscription updates in Directus, whether it's via the JavaScript SDK or GraphQL? And if there isn't, are there any other external tools I can use? Well, our CTO, Wrike, himself answered this in GitHub, but the the answer is not currently, not to get those real time subscriptions, but we do have WebSockets launching very soon. You will be able to get those real time subscriptions. As you can see here, one of our engineers, BrainSlug, has actually been testing it out internally. Here's a really cool small feature, a chat feature that he's been testing around, but you can see here the the live real time subscriptions in action. Like Discord or GitHub or leave a comment here in YouTube. Again, we'll be taking questions from these comments to answer in further episodes. Thanks again. Have a great day.",[6316,6317],"9e4b1be7-ab46-4924-9b6c-f369705b07e8","32cda52c-5409-4ff9-9223-fdbc249e7d8b",[],{"year":6181,"number":264,"id":6182,"episodes":6320,"show":6321},[6184,6185,6186,6187,6188,6189,6190,6191,6164],{"title":6193,"slug":6194},{"id":6184,"slug":6323,"vimeo_id":6324,"description":6325,"tile":6199,"length":159,"resources":12,"people":6326,"episode_number":13,"published":6329,"title":6330,"video_transcript_html":6331,"video_transcript_text":6332,"content":12,"seo":12,"status":19,"episode_people":6333,"recommendations":6336,"season":6337},"db-vendor-migration-server-location-gdpr","894041841","In this episode, John and Pedro answer questions about Directus' own database choice and migration between projects.",[6327,6328],{"name":4361,"url":4362},{"name":4524,"url":6097},"2022-10-21","Database Vendor | Migration | Server Location and GDPR Compliance","\u003Cp>Speaker 0: Hey everyone. Welcome to the first episode of From the Field Q and A with P and J. My name is Pedro.\u003C\u002Fp>\u003Cp>Speaker 1: And I'm John. Let's dive into some questions that we've recently received. Pedro, how about you start off with the first one?\u003C\u002Fp>\u003Cp>Speaker 0: So our first question, user Ryan Skye, aka Tony on GitHub, asked which DB vendor is used by Directus Cloud itself?\u003C\u002Fp>\u003Cp>Speaker 1: Great question, Tony. And Ben actually answered this in the thread itself. Ben is our CEO. In the cloud, we use Postgres, unless you're talking about community cloud, which is currently SQLite that may change in the future, in regards to community cloud, but in enterprise and standard, we do use Postgres. Alright.\u003C\u002Fp>\u003Cp>Great question, Tony. Let's look on to the next one.\u003C\u002Fp>\u003Cp>Speaker 0: Okay. So onto the next question. Is there a way I can migrate everything from 1 Directus cloud instance into another Directus cloud instance?\u003C\u002Fp>\u003Cp>Speaker 1: Yeah. There's actually a schema sync utility. We also give you that capability in command line interface, CLI. Those are both enabled on enterprise cloud, so that is something that you can do as well. Also, if you're migrating from one project to another, whether it be community to standard, standard to enterprise, we will also do that migration for you.\u003C\u002Fp>\u003Cp>Speaker 0: Mhmm. And you can provide for or you can you can find information on the schema sync utility in our direct to stocks online.\u003C\u002Fp>\u003Cp>Speaker 1: That's actually a great question that ties into our next question, Pedro. How do I migrate a dev environment to a production environment?\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. So same thing. We have the schema migration tool, which is a CLI integration, and we've got an API wrapper actually coming pretty soon here.\u003C\u002Fp>\u003Cp>Speaker 1: That's a good point too. We also allow you to have dev environments, nonproduction environments, as well as production environments, and seamlessly move information, schemas in between the 2. That's kind of where that version locking comes into play, where Pedro mentioned last question.\u003C\u002Fp>\u003Cp>Speaker 0: So for the final question, we have a lot of customers in Europe, who ask to be compliant with GDPR regulations and ask where our servers are actually hosted? Yeah. That's a\u003C\u002Fp>\u003Cp>Speaker 1: great question. We do have several servers in GDPR compliant areas. For our standard cloud, we have an east US, location. We have Asia location, and we also have servers in Frankfurt, Germany. So for standard cloud, you're covered under GDPR in Frankfurt, Germany.\u003C\u002Fp>\u003Cp>When you move up to enterprise cloud, we have a a bunch more locations. Some of those include London, Stockholm, Paris, and I believe there's even one in Ireland. All of those which are enabled for GDPR as well. So plenty of locations available for those who are GDPR aware.\u003C\u002Fp>\u003Cp>Speaker 0: Yeah. And in addition to that, it's all based on Amazon AWS infrastructure, so highly performant and secure as well. Thanks so much for tuning in to from the field q and a with p and j. Make sure to smash that like button and subscribe for notifications on our future videos.\u003C\u002Fp>\u003Cp>Speaker 1: And feel free to ask questions in the comments. We're definitely gonna take some of those questions for future episodes. We love answering community questions. And don't forget to join the communities on Discord and GitHub as well. That's where we also source questions from.\u003C\u002Fp>\u003Cp>Thanks again. Everyone have a great day.\u003C\u002Fp>","Hey everyone. Welcome to the first episode of From the Field Q and A with P and J. My name is Pedro. And I'm John. Let's dive into some questions that we've recently received. Pedro, how about you start off with the first one? So our first question, user Ryan Skye, aka Tony on GitHub, asked which DB vendor is used by Directus Cloud itself? Great question, Tony. And Ben actually answered this in the thread itself. Ben is our CEO. In the cloud, we use Postgres, unless you're talking about community cloud, which is currently SQLite that may change in the future, in regards to community cloud, but in enterprise and standard, we do use Postgres. Alright. Great question, Tony. Let's look on to the next one. Okay. So onto the next question. Is there a way I can migrate everything from 1 Directus cloud instance into another Directus cloud instance? Yeah. There's actually a schema sync utility. We also give you that capability in command line interface, CLI. Those are both enabled on enterprise cloud, so that is something that you can do as well. Also, if you're migrating from one project to another, whether it be community to standard, standard to enterprise, we will also do that migration for you. Mhmm. And you can provide for or you can you can find information on the schema sync utility in our direct to stocks online. That's actually a great question that ties into our next question, Pedro. How do I migrate a dev environment to a production environment? Yeah. So same thing. We have the schema migration tool, which is a CLI integration, and we've got an API wrapper actually coming pretty soon here. That's a good point too. We also allow you to have dev environments, nonproduction environments, as well as production environments, and seamlessly move information, schemas in between the 2. That's kind of where that version locking comes into play, where Pedro mentioned last question. So for the final question, we have a lot of customers in Europe, who ask to be compliant with GDPR regulations and ask where our servers are actually hosted? Yeah. That's a great question. We do have several servers in GDPR compliant areas. For our standard cloud, we have an east US, location. We have Asia location, and we also have servers in Frankfurt, Germany. So for standard cloud, you're covered under GDPR in Frankfurt, Germany. When you move up to enterprise cloud, we have a a bunch more locations. Some of those include London, Stockholm, Paris, and I believe there's even one in Ireland. All of those which are enabled for GDPR as well. So plenty of locations available for those who are GDPR aware. Yeah. And in addition to that, it's all based on Amazon AWS infrastructure, so highly performant and secure as well. Thanks so much for tuning in to from the field q and a with p and j. Make sure to smash that like button and subscribe for notifications on our future videos. And feel free to ask questions in the comments. We're definitely gonna take some of those questions for future episodes. We love answering community questions. And don't forget to join the communities on Discord and GitHub as well. That's where we also source questions from. Thanks again. Everyone have a great day.",[6334,6335],"2349d813-50d1-4474-b4e8-c42f65bf3d59","107b99ad-aa78-444e-93e6-4094dcf4b3bf",[],{"year":6181,"number":264,"id":6182,"episodes":6338,"show":6339},[6184,6185,6186,6187,6188,6189,6190,6191,6164],{"title":6193,"slug":6194},1784728874568]